个人基于vue3+TS+ant 实现城市三字码选择小组件
页面效果图
核心代码
<template>
<a-popover
v-model:open="visible"
class="city-popover"
title="支持城市检索"
trigger="click"
:destroyTooltipOnHide="true"
placement="bottomLeft"
>
<template #content>
<div class="city-main-box">
<!-- 输入检索 -->
<a-input v-model:value="searchValue" placeholder="输入城市名称检索" />
<!-- 输入检索列表 -->
<div class="search-value-list" v-if="searchList?.length > 0">
<a-list :data-source="searchList">
<template #renderItem="{ item }">
<a-list-item class="pointer" @click="choseCity(item)">
<div>{{ item.name }}</div>
<div>{{ item.provinceName }}</div>
</a-list-item>
</template>
</a-list>
<a-pagination size="small" @change="listDistrictBySyRun" v-model:current="current" :total="total" show-less-items />
</div>
<!-- 默认城市三字码 -->
<a-tabs v-model:activeKey="activeKey" v-else>
<a-tab-pane v-for="tab in cityOptions" :key="tab.key" :tab="tab.label">
<div class="city-list">
<div class="city-box" v-for="(city, index) in tab?.children" :key="index">
<div class="box-left">{{ city.label }}</div>
<div class="box-right">
<a-button v-for="(item, index) in city?.children" :key="index" @click="choseCity(item)" type="text">{{
item.name
}}</a-button>
</div>
</div>
</div>
</a-tab-pane>
</a-tabs>
</div>
</template>
<a-input class="city-chose-input" readonly :bordered="props.bordered" v-model:value="props.value" placeholder="选择城市" />
</a-popover>
</template>
<script setup lang="ts">
import { computed, onMounted, reactive, ref, watch, watchEffect, toRefs, createVNode } from 'vue';
import { DataType, usePagination, useRequest } from 'vue-request';
const emit = defineEmits(['chosedCity']);
interface Props {
value: string
placeholder?: string
bordered?: boolean
index?: number
i?: number
}
const props = withDefaults(defineProps<Props>(), {
value: '',
bordered: true,
placeholder: '请选择',
});
const searchValue = ref<string>('');
const visible = ref<boolean>(false);
const activeKey = ref<number>(0);
const current = ref(1);
const total = ref<number>(0);
// const searchList = ref<Array<object>>([]);
const {
data: cityOptions,
run: getInitialGroupingCityRun,
loading: getInitialGroupingCityLoading,
} = useRequest(cityApi.getInitialGroupingCity, {
defaultParams: [],
onSuccess: () => {
// detailsApiRun({ todoId: todoId.value, code: code.value })
},
manual: false,
});
const searchList = ref<Array<CityResponse>>([])
const listDistrictBySyRun = () => {
cityApi.listDistrictBySy(searchValue.value, 8, current.value).then(res => {
total.value = res.total
searchList.value = res.records
})
}
const choseCity = (city: CityItem) => {
debugger
searchList.value = [];
visible.value = false;
searchValue.value = '';
emit('chosedCity', city);
};
watch(
searchValue,
(newVal) => {
if (!newVal) {
searchList.value = [];
return;
}
current.value = 1
listDistrictBySyRun();
},
{
deep: true,
},
);
</script>
<style scoped lang="less">
.city-main-box {
width: 600px;
.pointer{
cursor: pointer;
}
}
.city-list {
.city-box {
display: flex;
border-bottom: 1px solid #eee;
.box-left {
display: flex;
align-items: center;
justify-content: center;
width: 10%;
color: orange;
font-size: 20px;
}
.box-right {
display: flex;
flex-wrap: wrap;
flex: 1;
}
}
}
</style>