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.
366 lines
9.5 KiB
366 lines
9.5 KiB
<!-- @format -->
|
|
|
|
<template>
|
|
<div>
|
|
<a-input
|
|
placeholder="请选择"
|
|
readonly="readonly"
|
|
style="cursor: pointer"
|
|
v-model:value="choiceCityName"
|
|
@click.stop="showCard">
|
|
<template #suffix> <ns-icon name="drow" size="12" @click.stop="showCard" /></template>
|
|
</a-input>
|
|
<div v-show="visible" class="selectCard">
|
|
<header class="card_header">
|
|
<div :style="{ background: regionLevel === 1 ? '#fff' : '' }" @click.stop="check(1)"
|
|
>省</div
|
|
>
|
|
<div :style="{ background: regionLevel === 2 ? '#fff' : '' }" @click.stop="check(2)"
|
|
>市</div
|
|
>
|
|
<div :style="{ background: regionLevel === 3 ? '#fff' : '' }" @click.stop="check(3)"
|
|
>区</div
|
|
>
|
|
</header>
|
|
<ul>
|
|
<li
|
|
:class="choiceCity[regionLevel - 1] === item.name ? 'isChoice' : ''"
|
|
@click.stop="choiceItem(item)"
|
|
v-for="item in data[regionLevel]"
|
|
:key="item.code"
|
|
>{{ item.name }}</li
|
|
>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import { cloneDeep, isArray, isEqual, isObject } from 'lodash-es';
|
|
import { defineComponent, ref, watch, computed, onMounted } from 'vue';
|
|
import { http } from '/nerv-lib/util/http';
|
|
export default defineComponent({
|
|
name: 'NsInputCityV2',
|
|
props: {
|
|
value: {
|
|
type: Array,
|
|
default: () => {
|
|
return [];
|
|
},
|
|
},
|
|
defaultValue: {
|
|
type: Array,
|
|
default: () => {
|
|
return [];
|
|
},
|
|
},
|
|
api: {
|
|
type: String,
|
|
default: '',
|
|
},
|
|
fieldMap: {
|
|
type: Object || Array,
|
|
default: () => {
|
|
return [];
|
|
},
|
|
},
|
|
formModel: {
|
|
type: Object || Array,
|
|
},
|
|
isSeparate: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
},
|
|
emits: ['change'],
|
|
setup(props, { emit }) {
|
|
// console.log(props);
|
|
const visible = ref(false);
|
|
const hasload = ref(false);
|
|
let choiceCity = ref<any[]>([]);
|
|
let choiceCityName = ref('');
|
|
let province = ref([]); //省
|
|
let city = ref([]); //市
|
|
let area = ref([]); //区
|
|
const data = ref([]);
|
|
data.value[1] = province.value;
|
|
const regionLevel = ref(1);
|
|
|
|
const showCard = () => {
|
|
visible.value = true;
|
|
};
|
|
const closeCard = () => {
|
|
visible.value = false;
|
|
};
|
|
const check = (e) => {
|
|
regionLevel.value = e;
|
|
initData(regionLevel.value);
|
|
};
|
|
|
|
const choiceItem = (e: any) => {
|
|
let currObject = [
|
|
{ name: 0, code: 3, enmpitIndex: [1, 2, 4, 5] },
|
|
{ name: 1, code: 4, enmpitIndex: [2, 5] },
|
|
{ name: 2, code: 5, enmpitIndex: [] },
|
|
];
|
|
let enmpitIndex = [[1, 2, 4, 5], [2, 5], []][regionLevel.value - 1];
|
|
let cureleve: { name: number; code: number; enmpitIndex: any[] } =
|
|
currObject[regionLevel.value - 1];
|
|
const update = () => {
|
|
let copecity = cloneDeep(choiceCity.value);
|
|
Object.keys(cureleve).forEach((el: string) => {
|
|
let index = cureleve[el];
|
|
let value = e[el];
|
|
copecity[index] = value;
|
|
});
|
|
enmpitIndex.forEach((el) => {
|
|
copecity[el] = null;
|
|
});
|
|
choiceCity.value = [...copecity];
|
|
};
|
|
switch (regionLevel.value) {
|
|
case 1:
|
|
update();
|
|
regionLevel.value = 2;
|
|
initData(2, e.code);
|
|
break;
|
|
case 2:
|
|
update();
|
|
regionLevel.value = 3;
|
|
initData(3, e.code);
|
|
break;
|
|
case 3:
|
|
update();
|
|
closeCard();
|
|
break;
|
|
}
|
|
};
|
|
const initData = (regionLevel: Number, code?: Number) => {
|
|
let parentcode = '';
|
|
switch (regionLevel) {
|
|
case 1:
|
|
province.value = [];
|
|
data.value[2] = [];
|
|
data.value[3] = [];
|
|
break;
|
|
case 2:
|
|
parentcode = choiceCity.value[3];
|
|
city.value = [];
|
|
data.value[3] = [];
|
|
break;
|
|
case 3:
|
|
parentcode = choiceCity.value[4];
|
|
area.value = [];
|
|
break;
|
|
}
|
|
async function getDate() {
|
|
try {
|
|
let body: Recordable = {};
|
|
body['regionLevel'] = regionLevel;
|
|
if (parentcode || regionLevel == 1) {
|
|
body.parentCode = parentcode;
|
|
const res = await http.get(props.api || '/api/pension/pension/objs/BaseArea', body);
|
|
if (res.success) {
|
|
switch (regionLevel) {
|
|
case 1:
|
|
province.value = res.data;
|
|
break;
|
|
case 2:
|
|
city.value = res.data;
|
|
break;
|
|
case 3:
|
|
area.value = res.data;
|
|
break;
|
|
}
|
|
data.value[regionLevel] = res.data;
|
|
}
|
|
}
|
|
} catch (err) {
|
|
console.log(err);
|
|
}
|
|
}
|
|
getDate();
|
|
};
|
|
const updateValue = (newv) => {
|
|
let leve = 0;
|
|
let code = '';
|
|
let cityMap = newv;
|
|
newv.forEach((el, i) => {
|
|
if (el) {
|
|
leve = i;
|
|
}
|
|
});
|
|
if (!props.isSeparate) {
|
|
regionLevel.value = leve > 0 ? leve : 1;
|
|
} else {
|
|
regionLevel.value = leve > 2 ? leve - 2 : 1;
|
|
}
|
|
regionLevel.value == 1 ? '' : (code = cityMap[leve - 1]);
|
|
if (!props.isSeparate) {
|
|
newv.forEach((el, i) => {
|
|
if (i === 0) {
|
|
let cityNameList = el.split('/');
|
|
cityNameList.forEach((item, index) => {
|
|
cityMap[index] = item;
|
|
});
|
|
} else {
|
|
cityMap[i + 3] = el;
|
|
}
|
|
});
|
|
} else {
|
|
cityMap = newv;
|
|
}
|
|
choiceCity.value = cityMap;
|
|
initData(regionLevel.value, code);
|
|
};
|
|
|
|
onMounted(() => {
|
|
initData(regionLevel.value);
|
|
if (isArray(props.value) && props.value.length) {
|
|
updateValue(props.value);
|
|
}
|
|
if (props.defaultValue.length) {
|
|
updateValue(props.defaultValue);
|
|
}
|
|
});
|
|
|
|
watch(
|
|
() => choiceCity.value,
|
|
(newv, oldv) => {
|
|
if (newv) {
|
|
let list = [];
|
|
newv.forEach((el, i) => {
|
|
if (el && i < 3) {
|
|
list.push(el);
|
|
}
|
|
});
|
|
choiceCityName.value = list.join('/');
|
|
if (!props.isSeparate) {
|
|
emit('change', [choiceCityName, ...newv.slice(1)]);
|
|
} else {
|
|
emit('change', newv);
|
|
}
|
|
}
|
|
},
|
|
);
|
|
|
|
watch(
|
|
() => props.value,
|
|
(newv, oldv) => {
|
|
if (!isEqual(newv, oldv) && !isEqual(newv, choiceCity.value)) {
|
|
updateValue(newv);
|
|
}
|
|
if (props.value?.length > 5 && props.value[4]) {
|
|
regionLevel.value = 3;
|
|
initData(regionLevel.value);
|
|
}
|
|
},
|
|
);
|
|
|
|
watch(
|
|
() => props.formModel,
|
|
(newv, oldv) => {
|
|
if (newv && !props.value.length && props.fieldMap.length && !hasload.value) {
|
|
let cityMap = [];
|
|
let leve = 0;
|
|
let code = '';
|
|
props.fieldMap.forEach((el, i) => {
|
|
cityMap[i] = newv[el];
|
|
if (newv[el]) {
|
|
leve = i;
|
|
}
|
|
});
|
|
if (!props.isSeparate) {
|
|
regionLevel.value = leve > 0 ? leve : 1;
|
|
} else {
|
|
regionLevel.value = leve > 2 ? leve - 2 : 1;
|
|
}
|
|
regionLevel.value == 1 ? '' : (code = cityMap[leve - 1]);
|
|
// choiceCity.value = cityMap;
|
|
updateValue(cityMap, code);
|
|
hasload.value = true;
|
|
}
|
|
},
|
|
);
|
|
watch(
|
|
() => visible.value,
|
|
(val) => {
|
|
val
|
|
? document.body.addEventListener('click', closeCard)
|
|
: document.body.removeEventListener('click', closeCard);
|
|
},
|
|
);
|
|
|
|
return {
|
|
choiceCity,
|
|
showCard,
|
|
closeCard,
|
|
regionLevel,
|
|
check,
|
|
choiceItem,
|
|
visible,
|
|
province,
|
|
city,
|
|
area,
|
|
data,
|
|
choiceCityName,
|
|
};
|
|
},
|
|
});
|
|
</script>
|
|
<style lang="less" scoped>
|
|
.selectCard {
|
|
position: absolute;
|
|
z-index: 4;
|
|
width: 300px;
|
|
height: 180px;
|
|
transform: translateY(5px);
|
|
box-shadow: 0 2px 8px rgb(0 0 0 / 15%);
|
|
animation: move-down 0.5s;
|
|
background: #fff;
|
|
border: 1px solid #ccc;
|
|
.card_header {
|
|
width: 100%;
|
|
height: 26px;
|
|
background: rgb(238, 238, 238);
|
|
display: flex;
|
|
justify-content: space-between;
|
|
border-bottom: 1px solid #ccc;
|
|
div {
|
|
text-align: center;
|
|
width: 34%;
|
|
cursor: pointer;
|
|
}
|
|
}
|
|
ul {
|
|
width: 100%;
|
|
height: 150px;
|
|
overflow: auto;
|
|
list-style: none;
|
|
padding: 10px;
|
|
li {
|
|
line-height: 30px;
|
|
cursor: pointer;
|
|
padding-left: 10px;
|
|
&:hover {
|
|
background-color: rgba(3, 141, 218, 0.1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.isChoice {
|
|
color: rgb(3, 141, 218);
|
|
}
|
|
@keyframes move-down {
|
|
0% {
|
|
transform: scale(0);
|
|
height: 0px;
|
|
opacity: 0;
|
|
}
|
|
100% {
|
|
transform: scale(1);
|
|
height: 180px;
|
|
opacity: 1;
|
|
}
|
|
}
|
|
</style>
|
|
|