Files
dvcp_v2_webapp/ui/packages/common/AiArea.vue
aixianling 0dda0201ba BUG 139
2023-03-24 15:24:18 +08:00

380 lines
10 KiB
Vue

<template>
<section class="ai-area">
<div v-if="inputClicker" @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" class="area-btn" type="primary" size="mini" @click="chooseArea">
{{ btnShowArea ? selectedName : "切换地区" }}
</el-button>
<a class="custom-clicker" v-else @click="chooseArea">
<slot :areaname="selectedName" :fullname="fullName" :id="selected"/>
</a>
<ai-dialog :visible.sync="dialog" title="选择地区" width="60%" @onConfirm="confirmArea" @open="selected=(value||'')">
<ai-highlight content="您当前选择&nbsp;@v" :value="selectedName" 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 AiHighlight from "../layout/AiHighlight";
import instance from "../../lib/js/request";
import Area from "../../lib/js/area";
export default {
name: 'AiArea',
components: {AiHighlight},
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
},
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),
selectedName: v => v.selectedArea.name || "无",
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.selectedName)
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>