城市选择组件
架构
bash
├── src
│ ├── components
│ │ ├── chooseCity
│ │ │ ├── lib (引入的第三方库)
│ │ │ │ ├── city.ts
│ │ │ │ └── province.json
│ │ │ ├── src (源码)
│ │ │ │ ├── index.vue
│ │ │ ├── index.ts
index.ts
ts
import { type App } from 'vue'
import chooseCity from './src/index.vue'
// 让这个组件可以通过use的形式使用
export default {
install(app: App) {
app.component('YJ-choose-city', chooseCity)
}
}
hooks/chooseCity/useCity.ts
ts
import { ElMessage } from 'element-plus'
import { ref, watch, onMounted } from 'vue'
import cityList from '@/components/chooseCity/lib/city.ts'
import provinceList from '@/components/chooseCity/lib/province.json'
export const useCity = () => {
// 是否显示
const visible = ref(false)
// 最后的结果
const result = ref([])
// 选择的值
const selectvalue = ref([])
// 筛选的列表
const options = ref([])
// 选择字母还是城市 1 字母 2 省份
const choosetype = ref('1')
// 字母城市数据
const cities = ref(cityList)
// 省份城市数据
const provinceinit = ref(provinceList)
const province = ref(provinceList)
// 选择的字母
const selectLetters = ref('A')
// 省份选择的字母
const selectProvinceLetters = ref('A')
// 观察 选择的类型是字母还是省份
watch(
() => {
return choosetype.value
},
(newVal, oldVal) => {
if (newVal != oldVal) {
selectLetters.value = 'A'
selectProvinceLetters.value = 'A'
}
}
)
// 观察结果的变化
watch(
() => {
return result.value
},
(newVal, oldVal) => {
const cityall: any = cities.value
if (choosetype.value == '1') {
// 一上来先都清空
for (let attr in cityall.cities) {
for (let i = 0; i < cityall.cities[attr].length; i++) {
cityall.cities[attr][i].active = false
}
}
// 获取到结果
newVal.forEach((item, index) => {
for (let attr in cityall.cities) {
for (let i = 0; i < cityall.cities[attr].length; i++) {
// 如果匹配上,则赋值
if (cityall.cities[attr][i].name == item) {
cityall.cities[attr][i].active = true
}
}
}
})
} else {
const provinceall: any = province.value
for (let attr in provinceall) {
for (let i = 0; i < provinceall[attr].length; i++) {
for (let m = 0; m < provinceall[attr][i].data.length; m++) {
provinceall[attr][i].data[m].active = false
}
}
newVal.forEach((item, index) => {
for (let attr in provinceall) {
for (let i = 0; i < provinceall[attr].length; i++) {
// 如果匹配上,则赋值
for (let m = 0; m < provinceall[attr][i].data.length; m++) {
if (provinceall[attr][i].data[m].name == item) {
provinceall[attr][i].data[m].active = true
}
}
}
}
})
}
}
},
{ deep: true }
)
// 方法开始
// 获取 最开始的
const getoptions = () => {
const provinceall: any = province.value
let result: any = []
Object.values(provinceall).forEach((item: any, index) => {
item.forEach((value: any, index2: any) => {
value.data.forEach((content: any, index3: any) => {
result.push(content)
})
})
})
options.value = result
}
// 过滤省份
const filterprovince = () => {
let result = JSON.parse(JSON.stringify(provinceinit.value))
Object.values(result).forEach((item: any, indx) => {
item.forEach((value: any, index: any) => {
value.data.forEach((content: any, index2: any) => {
let obj = {
active: false,
name: ''
}
obj.name = content
obj.active = false
value.data[index2] = obj
})
})
})
province.value = result
}
// 改变省份
const changeprovincecity = (content: string) => {
const resultAll = JSON.parse(JSON.stringify(result.value))
if (resultAll.length == 5) {
ElMessage.warning('最多只能选择五个城市')
return false
}
if (resultAll.includes(content)) {
ElMessage.warning('该城市已经选择过了')
return false
} else {
resultAll.push(content)
}
result.value = resultAll
}
// 改变省份字母
const changeprovinceletters = (content: string) => {
selectProvinceLetters.value = content
let el = document.getElementById('province' + content)
if (el) {
el.scrollIntoView()
}
}
// 改变字母
const changeletters = (content: string) => {
selectLetters.value = content
let el = document.getElementById(content)
if (el) {
el.scrollIntoView()
}
}
// 清除所有
const cleanAll = () => {
result.value = []
}
// 选择城市
const changecity = (content: any) => {
const resultAll = JSON.parse(JSON.stringify(result.value))
if (resultAll.length == 5) {
ElMessage.warning('最多只能选择五个城市')
return false
}
if (resultAll.includes(content.name)) {
ElMessage.warning('该城市已经选择过了')
return false
} else {
resultAll.push(content.name)
}
result.value = resultAll
}
// 挂载
onMounted(() => {
filterprovince()
getoptions()
})
return {
visible,
result,
selectvalue,
options,
choosetype,
selectLetters,
selectProvinceLetters,
getoptions,
filterprovince,
changeprovincecity,
changeprovinceletters,
changeletters,
cleanAll,
changecity,
cities: cities,
province: province
}
}
components/chooseCity/index.vue
vue
<template>
<el-popover placement="bottom-start" width="600" :visible="visible">
<template #reference>
<!--最外边显示-->
<div class="result" @click="visible = !visible">
<div v-if="result.length == 0">请选择城市</div>
<div v-else>{{ result.join(',') }}</div>
</div>
<!--最外边结束-->
</template>
<template #default>
<!--内容开始-->
<div class="container">
<el-row>
<el-col :span="8">
<el-radio-group v-model="choosetype">
<el-radio-button value="1">按照城市</el-radio-button>
<el-radio-button value="2">按照省份</el-radio-button>
</el-radio-group>
</el-col>
<el-col :span="13">
<el-select v-model="result" multiple placeholder="请选择" filterable style="width: 320px">
<el-option v-for="item in options" :key="item.name" :label="item.name" :value="item.name">
</el-option>
</el-select>
</el-col>
<el-col :span="2" style="margin-right: 10px">
<el-button type="primary" @click="cleanAll">清空</el-button>
</el-col>
</el-row>
<!--循环拼音字母-->
<div class="letters" v-if="choosetype == '1'">
<div
v-for="(content, index) in Object.keys(cities.cities)"
:key="index"
:class="[selectLetters == content ? 'letteritems active' : 'letteritems']"
@click="changeletters(content)"
>
{{ content }}
</div>
</div>
<!--循环拼音字母-->
<!--按照拼音首字母循环城市-->
<div class="cityAll" v-if="choosetype == '1'">
<div v-for="(content, attr) in cities.cities" :key="attr">
<el-row :id="attr">
<!--左侧字母-->
<el-col :span="2" class="cityAll_letters"> {{ attr }}</el-col>
<!--左侧字母-->
<!--右侧数据-->
<el-col :span="22">
<div
v-for="(item, index) in content"
:key="index"
:class="[item.active ? 'cityall_items active' : 'cityall_items']"
@click="changecity(item)"
>
{{ item.name }}
</div>
</el-col>
<!--右侧数据-->
</el-row>
</div>
</div>
<!--按照拼音首字母循环城市-->
<!--循环省市字母-->
<div class="letters" v-if="choosetype == '2'">
<div
v-for="(content, index) in Object.keys(province)"
:key="index"
:class="[
selectProvinceLetters == content
? 'letteritems provinceletters active'
: 'letteritems provinceletters'
]"
@click="changeprovinceletters(content)"
>
{{ content }}
</div>
</div>
<!--循环省市字母-->
<!--按照省份循环城市-->
<div class="cityAll" v-if="choosetype == '2'">
<div v-for="(content, index) in Object.values(province)" :key="index">
<div v-for="(item, index2) in content" :key="index2">
<el-row :id="'province' + item.id">
<!--左侧字母-->
<el-col :span="3" class="cityAll_letters"> {{ item.name }}</el-col>
<!--左侧字母-->
<!--右侧数据-->
<el-col :span="21">
<div
v-for="(value, index3) in item.data"
:key="index3"
:class="[value.active ? 'cityall_items active' : 'cityall_items']"
@click="changeprovincecity(value.name)"
>
{{ value.name }}
</div>
</el-col>
<!--右侧数据-->
</el-row>
</div>
</div>
</div>
<!--按照省份循环城市-->
</div>
<!--内容结束-->
</template>
</el-popover>
</template>
<script setup lang="ts">
import { useCity } from '@/hooks/chooseCity/useCity'
const {
visible,
result,
selectvalue,
options,
choosetype,
selectLetters,
selectProvinceLetters,
getoptions,
filterprovince,
changeprovincecity,
changeprovinceletters,
changeletters,
cleanAll,
changecity,
cities,
province
} = useCity()
</script>
<style scoped lang="scss">
.result {
width: 220px;
height: 40px;
border: 1px solid #ccc;
cursor: pointer;
text-align: center;
line-height: 35px;
margin: 0 auto;
margin-top: 30px;
}
.container {
.letters {
margin-bottom: 20px;
overflow: hidden;
.letteritems {
font-size: 14px;
width: 30px;
height: 30px;
text-align: center;
line-height: 30px;
border: 1px solid #ccc;
float: left;
margin-top: 20px;
margin-bottom: 0px;
margin-right: 10px;
cursor: pointer;
&.provinceletters {
width: 50px;
height: 30px;
}
&.active {
background: #409eff;
color: white;
}
}
}
.cityAll {
width: 580px;
height: 300px;
overflow-y: auto;
.cityAll_letters {
font-weight: 700;
font-size: 16px;
}
.cityall_items {
font-size: 15px;
float: left;
margin-right: 10px;
margin-bottom: 10px;
cursor: pointer;
&.active {
color: red;
}
}
}
}
</style>