账号界面
This commit is contained in:
379
ui/packages/common/area/AiArea.vue
Normal file
379
ui/packages/common/area/AiArea.vue
Normal file
@@ -0,0 +1,379 @@
|
||||
<template>
|
||||
<section class="ai-area">
|
||||
<div v-if="inputClicker&&!$scopedSlots.default" @click="chooseArea" class="input-clicker">
|
||||
<el-row type="flex" justify="space-between">
|
||||
<div class="prepend">
|
||||
<i style="font-size: 16px" class="iconfont iconLocation"/>
|
||||
切换地区
|
||||
</div>
|
||||
<div class="content nowrap-text fill" v-text="fullName"/>
|
||||
<i class="iconfont iconChange pad-r10"/>
|
||||
</el-row>
|
||||
</div>
|
||||
<el-button v-else-if="!customClicker&&!$scopedSlots.default" class="area-btn" type="primary" size="mini"
|
||||
@click="chooseArea">
|
||||
{{ btnShowArea ? selectedArea.name : "切换地区" }}
|
||||
</el-button>
|
||||
<a class="custom-clicker" v-else @click="chooseArea">
|
||||
<slot :areaname="selectedArea.name" :fullname="fullName" :id="selected"/>
|
||||
</a>
|
||||
<ai-dialog :visible.sync="dialog" title="选择地区" width="60%" @onConfirm="confirmArea" :modal="mask"
|
||||
@open="selected=(value||'')">
|
||||
<ai-highlight content="您当前选择 @v" :value="selectedArea.name" color="#333" bold/>
|
||||
<div class="area_edge">
|
||||
<div class="area-box" v-for="ops in showOps">
|
||||
<h2 v-text="ops.header"/>
|
||||
<div class="area-item" :class="{selected: selectedMap.includes(area.id)}" v-for="area in ops.list"
|
||||
@click="getChildrenAreas(area)">
|
||||
<ai-badge>
|
||||
<span>{{ area.name }}</span>
|
||||
<div slot="badge" v-if="showBadge&&area.tipName" :class="getLabelClassByLabelType(area.labelType)">
|
||||
{{ area.tipName }}
|
||||
</div>
|
||||
</ai-badge>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ai-dialog>
|
||||
</section>
|
||||
</template>
|
||||
<script>
|
||||
import instance from "../../../lib/js/request";
|
||||
import Area from "../../../lib/js/area";
|
||||
|
||||
export default {
|
||||
name: 'AiArea',
|
||||
inject: {
|
||||
elFormItem: {default: ""},
|
||||
elForm: {default: ''},
|
||||
},
|
||||
model: {
|
||||
prop: 'value',
|
||||
event: 'change'
|
||||
},
|
||||
props: {
|
||||
instance: {default: () => instance},
|
||||
action: String,
|
||||
areaLevel: {type: [Number, String], default: 5},
|
||||
btnShowArea: {type: Boolean, default: false},
|
||||
customClicker: {type: Boolean, default: false},
|
||||
disabled: {type: Boolean, default: false},
|
||||
hideLevel: {type: [Number, String], default: 0},
|
||||
inputClicker: {type: Boolean, default: true},
|
||||
provinceAction: String,
|
||||
separator: {type: String, default: ""},
|
||||
showBadge: {type: Boolean, default: true},
|
||||
value: String,
|
||||
valueLevel: {type: [Number, String], default: -1},
|
||||
root: String,
|
||||
mask: {type: Boolean, default: true}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selected: null,
|
||||
areaOps: [],
|
||||
fullName: '',
|
||||
dialog: false,
|
||||
ProvinceCityCounty: [],
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
rootArea: v => new Area(v.root),
|
||||
currentArea: v => v.selected || v.value,
|
||||
startLevel: v => Math.max(Number(v.hideLevel), 0, v.rootArea.level),//地区最高可选行政地区等级
|
||||
endLevel: v => Number(v.areaLevel) || 0,//地区最低可选行政地区等级
|
||||
selectedArea: v => new Area(v.currentArea, v.hashMap),
|
||||
selectedMap: v => v.selectedArea.areaMap,
|
||||
validateState: v => ['', 'success'].includes(v.elFormItem?.validateState),
|
||||
hashMap() {
|
||||
//地区数据缓存器,用于快速获取数据
|
||||
const hash = {}
|
||||
this.areaOps.flat().map(e => hash[e.id] = e)
|
||||
return hash
|
||||
},
|
||||
showOps() {
|
||||
const levelLabels = {
|
||||
0: "省/直辖市",
|
||||
1: "市",
|
||||
2: "县/区",
|
||||
3: "乡/镇/街道",
|
||||
4: "村/社区"
|
||||
}
|
||||
let ops = this.areaOps.map((list, i) => ({
|
||||
header: levelLabels[i], i, list
|
||||
})).slice(Math.max(0, this.startLevel), this.endLevel)
|
||||
if (this.startLevel > 0 && ops.length > 0) {
|
||||
const tmp = this.$copy(ops[0]?.list?.[0] || {})
|
||||
if (this.startLevel >= ops[0].i) {
|
||||
const opsMap = this.selectedMap.length > 0 ? this.selectedMap : this.rootArea.areaMap
|
||||
ops[0].list = [this.hashMap[opsMap[ops[0].i]]].filter(Boolean) || []
|
||||
} else {
|
||||
const prev = +tmp.type - 1
|
||||
const prevId = tmp.parentId
|
||||
prev > -1 && ops.unshift({header: levelLabels[prev], list: [this.hashMap[prevId]]})
|
||||
}
|
||||
}
|
||||
return ops
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value: {
|
||||
immediate: true,
|
||||
handler(v) {
|
||||
this.dispatch('ElFormItem', 'el.form.change', [v]);
|
||||
this.initAreaName()
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
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));
|
||||
}
|
||||
},
|
||||
chooseArea() {
|
||||
if (this.disabled) return
|
||||
this.selected = this.$copy(this.value)
|
||||
this.initOptions().then(() => this.dialog = true)
|
||||
},
|
||||
confirmArea() {
|
||||
if (this.valueLevel > -1) {
|
||||
this.$emit("change", this.selectedMap[this.valueLevel])
|
||||
} else {
|
||||
this.$emit("change", this.selected)
|
||||
}
|
||||
this.$emit("area", this.selected, this.selectedArea);
|
||||
this.dialog = false
|
||||
},
|
||||
getChildrenAreas(area) {
|
||||
this.selected = area.id;
|
||||
const level = Area.getLevelByAreaId(area.id);
|
||||
if (level < 4) {
|
||||
this.getAreasByParentId(area.id).then(list => {
|
||||
this.areaOps.splice(level + 1, 5, list)
|
||||
})
|
||||
}
|
||||
},
|
||||
getAreasByParentId(id) {
|
||||
const level = Area.getLevelByAreaId(id)
|
||||
return new Promise(resolve => {
|
||||
if (level < 2) {
|
||||
this.getProvinceCityCounty().then(() => {
|
||||
resolve(this.ProvinceCityCounty.filter(e => e.parentId == id))
|
||||
})
|
||||
} else {
|
||||
this.instance.post(this.action || "/admin/area/queryAreaByParentId", null, {
|
||||
withoutToken: true,
|
||||
params: {id}
|
||||
}).then(res => {
|
||||
if (res?.data) {
|
||||
resolve(res.data)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
getLabelClassByLabelType(type) {
|
||||
let cls = "badge-label"
|
||||
switch (type) {
|
||||
case '1':
|
||||
cls += ' label-town'
|
||||
break;
|
||||
case '3':
|
||||
cls += ' label-village'
|
||||
break;
|
||||
default:
|
||||
cls += ' label-poor'
|
||||
break
|
||||
}
|
||||
return cls
|
||||
},
|
||||
getProvinceCityCounty() {
|
||||
return new Promise(resolve => {
|
||||
if (localStorage.getItem("ProvinceCityCounty")) {
|
||||
resolve(JSON.parse(localStorage.getItem("ProvinceCityCounty")))
|
||||
} else {
|
||||
this.instance.post(this.provinceAction || "/admin/area/queryProvinceListContainCity", null, {
|
||||
withoutToken: true
|
||||
}).then(res => {
|
||||
if (res && res.data) {
|
||||
localStorage.setItem("ProvinceCityCounty", JSON.stringify(res.data))
|
||||
resolve(res.data)
|
||||
}
|
||||
})
|
||||
}
|
||||
}).then(list => this.ProvinceCityCounty = list)
|
||||
},
|
||||
initOptions() {
|
||||
this.areaOps = []
|
||||
const opsMap = this.selectedMap.length > 0 ? this.selectedMap : this.rootArea.areaMap
|
||||
let map = {};
|
||||
return Promise.all([null, ...opsMap].map((id, i) => this.getAreasByParentId(id).then(list => map[i] = list))).then(() => {
|
||||
this.areaOps = Object.values(map)
|
||||
})
|
||||
},
|
||||
initAreaName() {
|
||||
if (this.value) {
|
||||
Area.createByAction(this.currentArea, this.instance).then(names => {
|
||||
this.selectedArea.getName(names)
|
||||
this.fullName = this.selectedArea.nameMap.join(this.separator)
|
||||
this.$emit("update:name", this.selectedArea.name)
|
||||
this.$emit("fullname", this.fullName)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.ai-area {
|
||||
.area-btn {
|
||||
box-shadow: 0 2px 8px 0 rgba(76, 132, 255, 0.6);
|
||||
}
|
||||
|
||||
.input-clicker {
|
||||
width: 320px;
|
||||
cursor: pointer;
|
||||
border: 1px solid #D0D4DC;
|
||||
line-height: 32px;
|
||||
border-radius: 2px;
|
||||
font-size: 14px;
|
||||
|
||||
.prepend {
|
||||
background: rgba(245, 245, 245, 1);
|
||||
width: auto;
|
||||
border-right: 1px solid #D0D4DC;
|
||||
padding: 0 8px;
|
||||
white-space: nowrap;
|
||||
|
||||
}
|
||||
|
||||
.content {
|
||||
text-align: left;
|
||||
padding-left: 14px;
|
||||
padding-right: 8px;
|
||||
direction: rtl;
|
||||
}
|
||||
|
||||
.suffix {
|
||||
width: auto;
|
||||
padding: 0 12px
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: $primaryColor;
|
||||
}
|
||||
}
|
||||
|
||||
.custom-clicker {
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
.area_edge {
|
||||
max-height: 350px;
|
||||
overflow-y: auto;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.area-box {
|
||||
box-shadow: 0px -1px 0px 0px rgba(238, 238, 238, 1);
|
||||
padding: 16px 0 8px 0;
|
||||
|
||||
& > section {
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
& > h2 {
|
||||
color: rgba(51, 51, 51, 1);
|
||||
line-height: 22px;
|
||||
margin-bottom: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.area-item {
|
||||
display: inline-block;
|
||||
border-radius: 2px;
|
||||
border: 1px solid #D0D4DC;
|
||||
margin: 8px 8px 8px 0;
|
||||
padding: 3px 10px;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
line-height: normal;
|
||||
font-size: 14px;
|
||||
|
||||
&:hover {
|
||||
color: rgba($primaryColor, .8);
|
||||
border-color: rgba($primaryColor, .8);
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #ddd;
|
||||
line-height: normal;
|
||||
margin: 5px;
|
||||
padding: 3px;
|
||||
cursor: pointer;
|
||||
|
||||
span {
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: rgba($primaryColor, .8);
|
||||
border-color: rgba($primaryColor, .8);
|
||||
}
|
||||
}
|
||||
|
||||
.selected {
|
||||
color: rgba($primaryColor, .8);
|
||||
border-color: rgba($primaryColor, .8);
|
||||
}
|
||||
}
|
||||
|
||||
.badge-label {
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
border-radius: 15px;
|
||||
color: #fff;
|
||||
width: 12px;
|
||||
letter-spacing: 10px;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
padding: 3px 5px;
|
||||
white-space: nowrap;
|
||||
transition: width 1s, letter-spacing 0.05s;
|
||||
|
||||
&.label-town {
|
||||
background: rgba($primaryColor, .8);
|
||||
}
|
||||
|
||||
&.label-village {
|
||||
background: rgba($primaryColor, .8);
|
||||
}
|
||||
|
||||
&.label-poor {
|
||||
background: #ffb14c;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
width: initial;
|
||||
letter-spacing: unset;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
295
ui/packages/common/area/AiAreaGet.vue
Normal file
295
ui/packages/common/area/AiAreaGet.vue
Normal file
@@ -0,0 +1,295 @@
|
||||
<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>
|
||||
273
ui/packages/common/area/AiAreaSelect.vue
Normal file
273
ui/packages/common/area/AiAreaSelect.vue
Normal file
@@ -0,0 +1,273 @@
|
||||
<template>
|
||||
<section class="AiAreaSelect">
|
||||
<el-row type="flex" :gutter="5">
|
||||
<el-col v-for="(item,i) in areaInfo" :key="i" v-show="isShowSelector(i)" style="width: auto;">
|
||||
<el-select :size="selectClass" v-if="areaLists[i].length>0||alwaysShow" @focus="$emit('focus')"
|
||||
@blur="$emit('blur')" :placeholder="placeVal[i]" clearable
|
||||
:disabled="isDisabledSelector(i)" :value="areaInfo[i]" @change="v=>handleSelectorChange(v,i)">
|
||||
<template v-if="lockFirstOption&&i==(hideNum||0)">
|
||||
<el-option v-if="!areaInfo[i]||areaInfo[i]===area.id" v-for="(area,j) in areaLists[i]" :key="j"
|
||||
:value="area.id" :label="area.name"/>
|
||||
</template>
|
||||
<el-option v-else v-for="(area,j) in areaLists[i]" :key="j" :value="area.id" :label="area.name"/>
|
||||
</el-select>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "AiAreaSelect",
|
||||
model: {
|
||||
prop: 'value',
|
||||
event: 'change',
|
||||
},
|
||||
inject: {
|
||||
elFormItem: {default: ""},
|
||||
},
|
||||
props: {
|
||||
value: String,
|
||||
areaLevel: {type: [Number, String], default: 5},
|
||||
disabledLevel: {type: [Number, String], default: 0},
|
||||
disabled: {type: Boolean, default: false},
|
||||
hideLevel: {type: [Number, String], default: 0},
|
||||
valueLevel: {type: [Number, String], default: -1},
|
||||
alwaysShow: {type: Boolean, default: false},
|
||||
selectClass: {type: String, default: 'small'},
|
||||
lockFirstOption: {type: Boolean, default: false},
|
||||
instance: Function,
|
||||
action: String,
|
||||
provinceAction: String
|
||||
},
|
||||
computed: {
|
||||
hideNum() {
|
||||
return Number(this.hideLevel)
|
||||
},
|
||||
disabledNum() {
|
||||
return Number(this.disabledLevel)
|
||||
},
|
||||
showNum() {
|
||||
return Number(this.areaLevel)
|
||||
},
|
||||
valueIndex() {
|
||||
return Number(this.valueLevel)
|
||||
},
|
||||
current() {
|
||||
return this.selected || this.value
|
||||
},
|
||||
areaInfo() {
|
||||
let info = {}
|
||||
const currentLevel = this.getLevelByAreaId(this.current)
|
||||
for (let i = 0; i < this.showNum; i++) {
|
||||
//防止地区代码出现,在获取到选项后再赋值
|
||||
if (i <= currentLevel) info[i] = this.areaLists[i]?.length ? this.getAreaByAreaType(i) : ""
|
||||
else info[i] = null
|
||||
}
|
||||
return info
|
||||
},
|
||||
areaObj() {
|
||||
let info = {}
|
||||
for (let key in this.areaInfo) {
|
||||
if (this.areaInfo[key]) {
|
||||
info[key] = this.areaLists[key].find(e => e.id === this.areaInfo[key])
|
||||
}
|
||||
}
|
||||
return info
|
||||
},
|
||||
selectedName() {
|
||||
const index = this.getLevelByAreaId(this.current)
|
||||
if (index > -1) {
|
||||
const area = this.areaLists[index].find(e => e.id === this.current)
|
||||
return area ? area.name : ""
|
||||
} else return ""
|
||||
},
|
||||
selectedFullName() {
|
||||
let name = ""
|
||||
for (let i in this.areaInfo) {
|
||||
if (this.areaInfo[i]) {
|
||||
const area = this.areaLists[i].find(e => e.id === this.areaInfo[i])
|
||||
name += area ? area.name : ""
|
||||
}
|
||||
}
|
||||
return name
|
||||
},
|
||||
areaLelve() {
|
||||
return this.getLevelByAreaId(this.current);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
selected() {
|
||||
if (this.valueIndex > -1) {
|
||||
this.$emit("change", this.areaInfo[this.valueIndex] || null)
|
||||
} else {
|
||||
this.$emit("change", this.current)
|
||||
}
|
||||
this.$emit("area", this.areaInfo);
|
||||
this.$emit('areaLelve', this.areaLelve)
|
||||
},
|
||||
areaObj() {
|
||||
this.$emit("areaObj", this.areaObj)
|
||||
},
|
||||
selectedName() {
|
||||
this.$emit("name", this.selectedName)
|
||||
},
|
||||
selectedFullName() {
|
||||
this.$emit("fullname", this.selectedFullName)
|
||||
},
|
||||
valueLevel() {
|
||||
this.refreshAreaList();
|
||||
},
|
||||
value(v) {
|
||||
//特殊处置结果 后台传值赋值改变需要重新加载数据
|
||||
this.dispatch('ElFormItem', 'el.form.change', [v]);
|
||||
if (!this.selected) {
|
||||
this.refreshAreaList();
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selected: null,
|
||||
areaLists: {
|
||||
0: [],
|
||||
1: [],
|
||||
2: [],
|
||||
3: [],
|
||||
4: [],
|
||||
},
|
||||
placeVal: {
|
||||
0: '请选择省',
|
||||
1: '请选择市',
|
||||
2: '请选择区',
|
||||
3: '请选择镇',
|
||||
4: '请选择村'
|
||||
},
|
||||
ProvinceCityCounty: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
isShowSelector(i) {
|
||||
let index = Number(i)
|
||||
return index >= this.hideNum
|
||||
},
|
||||
isDisabledSelector(i) {
|
||||
let index = Number(i)
|
||||
return this.disabled || (index < this.disabledNum)
|
||||
},
|
||||
handleSelectorChange(area, index) {
|
||||
if (area) {
|
||||
this.selected = area
|
||||
if (index < 4 && this.areaInfo[index]) {
|
||||
this.getAreasByParentId(index).then(() => {
|
||||
for (let i = index; i < this.showNum; i++) {
|
||||
if (this.areaLists[i + 2]) this.areaLists[i + 2] = []
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {//清空操作
|
||||
let i = Number(index), pre = i - 1, origin = JSON.parse(JSON.stringify(this.areaInfo))
|
||||
if (pre > 0) {
|
||||
this.selected = origin[pre]
|
||||
for (let j = i + 1; j < this.showNum; j++) {
|
||||
this.areaLists[j] = []
|
||||
}
|
||||
} else {
|
||||
this.selected = origin[0]
|
||||
for (let j = i + 2; j < this.showNum; j++) {
|
||||
this.areaLists[j] = []
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
getAreasByParentId(index) {
|
||||
index = Number(index)
|
||||
return new Promise((resolve) => {
|
||||
this.areaLists[index + 1] = []
|
||||
if (index < 2) {
|
||||
index = Number(index)
|
||||
let data = this.ProvinceCityCounty.filter(e => e.parentId == this.areaInfo[index])
|
||||
if (data && (index + 1 < this.showNum)) this.areaLists[index + 1] = data
|
||||
this.$forceUpdate();
|
||||
resolve(index)
|
||||
} else {
|
||||
this.instance.post(this.action || "/admin/area/queryAreaByParentId", null, {
|
||||
withoutToken: true,
|
||||
params: {id: this.areaInfo[index]}
|
||||
}).then(res => {
|
||||
if (res?.data) {
|
||||
index = Number(index)
|
||||
if (index + 1 < this.showNum) this.areaLists[index + 1] = res.data
|
||||
this.$forceUpdate()
|
||||
}
|
||||
resolve(index)
|
||||
})
|
||||
}
|
||||
|
||||
})
|
||||
},
|
||||
getProvinceCityCounty() {
|
||||
return this.instance.post(this.provinceAction || "/admin/area/queryProvinceListContainCity", null, {withoutToken: true})
|
||||
},
|
||||
getAreaByAreaType(areaType) {
|
||||
let lvCount = [2, 4, 6, 9, 12]
|
||||
return this.current?.split("").map((e, i) => i < lvCount[areaType] ? e : 0).join("")
|
||||
},
|
||||
getLevelByAreaId(code) {
|
||||
if (code) {
|
||||
if (code.length == 2 || code.endsWith('0000000000')) return 0
|
||||
else if (code.endsWith('00000000')) return 1
|
||||
else if (code.endsWith('000000')) return 2
|
||||
else if (code.endsWith('000')) return 3
|
||||
else return 4
|
||||
} else return -1
|
||||
},
|
||||
refreshAreaList() {
|
||||
for (let i = 0; i < this.showNum; i++) {
|
||||
this.areaLists[i] = []
|
||||
}
|
||||
this.getProvinceCityCounty().then(res => {
|
||||
this.ProvinceCityCounty = res.data
|
||||
this.areaLists[0] = res.data.filter(d => !d.parentId)
|
||||
const getAreaList = i => {
|
||||
if (this.areaInfo?.[i])
|
||||
this.getAreasByParentId(i).then(next => getAreaList(next + 1))
|
||||
}
|
||||
getAreaList(0)
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 表单验证
|
||||
* @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));
|
||||
}
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.refreshAreaList()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.AiAreaSelect {
|
||||
.el-col {
|
||||
min-width: 90px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
67
ui/packages/common/area/AiAreaTree.vue
Normal file
67
ui/packages/common/area/AiAreaTree.vue
Normal file
@@ -0,0 +1,67 @@
|
||||
<script>
|
||||
import instance from "dui/lib/js/request";
|
||||
|
||||
export default {
|
||||
name: "AiAreaTree",
|
||||
props: {
|
||||
instance: {default: () => instance},
|
||||
action: {default: "/admin/area/queryAreaByParentId"},
|
||||
rootId: String,
|
||||
value: String,
|
||||
range: {default: 5} //可选最小级别地区
|
||||
},
|
||||
model: {
|
||||
prop: "value",
|
||||
event: "input"
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
areaProps: {
|
||||
label: "name",
|
||||
isLeaf: "isLeaf"
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getAreas(node, resolve) {
|
||||
let id = node.data?.id
|
||||
if (node.level == 0) {
|
||||
id = this.rootId
|
||||
}
|
||||
this.getAreasByParent(id).then(resolve)
|
||||
},
|
||||
handleClick(data) {
|
||||
this.$emit("input", data.id)
|
||||
},
|
||||
getAreasByParent(id) {
|
||||
return this.instance.post(this.action, null, {
|
||||
withoutToken: !0,
|
||||
params: {id}
|
||||
}).then(res => {
|
||||
return res.data?.map(e => ({...e, isLeaf: e.type == this.range})) || []
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-scrollbar class="AiAreaTree">
|
||||
<el-tree :load="getAreas" @node-click="handleClick" lazy :props="areaProps"/>
|
||||
</el-scrollbar>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.AiAreaTree {
|
||||
min-width: 200px;
|
||||
height: 100%;
|
||||
|
||||
:deep(.el-scrollbar__wrap) {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
:deep(.el-tree) {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user