|
|
@@ -1,172 +1,218 @@
|
|
|
-<template>
|
|
|
- <view class="ssq adffc">
|
|
|
- <view class="box">
|
|
|
- <view class="box-top adfac">
|
|
|
- <view class="box-top-btn qx" @click="handleCancel">取消</view>
|
|
|
- <view class="box-top-title">{{title}}</view>
|
|
|
- <view class="box-top-btn qd" @click="handleConfirm">确定</view>
|
|
|
- </view>
|
|
|
- <picker-view :value="pickerValue" @change="handlePickerChange" class="picker-view">
|
|
|
- <!-- 省份列 -->
|
|
|
- <picker-view-column>
|
|
|
- <view
|
|
|
- class="picker-item"
|
|
|
- :class="{ 'selected-item': pickerValue[0] === index }"
|
|
|
- v-for="(province, index) in provinces"
|
|
|
- :key="province.name"
|
|
|
- >
|
|
|
- {{ province.name }}
|
|
|
- </view>
|
|
|
- </picker-view-column>
|
|
|
- <!-- 城市列 -->
|
|
|
- <picker-view-column>
|
|
|
- <view
|
|
|
- class="picker-item"
|
|
|
- :class="{ 'selected-item': pickerValue[1] === index }"
|
|
|
- v-for="(city, index) in cities"
|
|
|
- :key="city.name"
|
|
|
- >
|
|
|
- {{ city.name }}
|
|
|
- </view>
|
|
|
- </picker-view-column>
|
|
|
- <!-- 区县列 -->
|
|
|
- <picker-view-column>
|
|
|
- <view
|
|
|
- class="picker-item"
|
|
|
- :class="{ 'selected-item': pickerValue[2] === index }"
|
|
|
- v-for="(area, index) in areas"
|
|
|
- :key="area.name"
|
|
|
- >
|
|
|
- {{ area.name }}
|
|
|
- </view>
|
|
|
- </picker-view-column>
|
|
|
- </picker-view>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
-</template>
|
|
|
-
|
|
|
-<script>
|
|
|
- import { cityData, hotCities } from './city-data.js';
|
|
|
- export default {
|
|
|
- props:{
|
|
|
- title:{
|
|
|
- typeof:String,
|
|
|
- default:'所属地区'
|
|
|
- }
|
|
|
- },
|
|
|
- data(){
|
|
|
- return {
|
|
|
- provinces:cityData,
|
|
|
- cities:[],
|
|
|
- areas:[],
|
|
|
- pickerValue:[0,0,0]
|
|
|
- }
|
|
|
- },
|
|
|
- computed:{
|
|
|
- selectedProvince:function(){
|
|
|
- return this.provinces[this.pickerValue[0]] || {}
|
|
|
- },
|
|
|
- selectedCity:function(){
|
|
|
- return this.cities[this.pickerValue[1]] || {}
|
|
|
- },
|
|
|
- selectedArea:function(){
|
|
|
- return this.areas[this.pickerValue[2]] || {}
|
|
|
- }
|
|
|
- },
|
|
|
- mounted() {
|
|
|
- this.initialize()
|
|
|
- },
|
|
|
- methods:{
|
|
|
- handlePickerChange(e){
|
|
|
- const newPickerValue = e.detail.value;
|
|
|
- const [provinceIndex, cityIndex, areaIndex] = newPickerValue;
|
|
|
- const oldProvinceIndex = this.pickerValue[0];
|
|
|
- const oldCityIndex = this.pickerValue[1];
|
|
|
-
|
|
|
- if (provinceIndex !== oldProvinceIndex) {
|
|
|
- this.cities = this.provinces[provinceIndex].children;
|
|
|
- this.areas = this.cities[0]?.children || [];
|
|
|
- this.pickerValue = [provinceIndex, 0, 0];
|
|
|
- } else if (cityIndex !== oldCityIndex) {
|
|
|
- // 更新地区列表,并将地区索引重置为0
|
|
|
- this.areas = this.cities[cityIndex]?.children || [];
|
|
|
- this.pickerValue = [provinceIndex, cityIndex, 0];
|
|
|
- } else {
|
|
|
- this.pickerValue = newPickerValue;
|
|
|
- }
|
|
|
- },
|
|
|
- initialize(){
|
|
|
- this.cities = this.provinces[0]?.children || [];
|
|
|
- this.areas = this.cities[0]?.children || [];
|
|
|
- },
|
|
|
- handleCancel(){
|
|
|
- this.$emit('cancel')
|
|
|
- },
|
|
|
- handleConfirm(){
|
|
|
- this.$emit('confirm',this.pickerValue)
|
|
|
- },
|
|
|
- }
|
|
|
- }
|
|
|
-</script>
|
|
|
-
|
|
|
-<style scoped lang="scss">
|
|
|
- .ssq{
|
|
|
- position: fixed;
|
|
|
- left: 0;
|
|
|
- right: 0;
|
|
|
- top: 0;
|
|
|
- bottom: 0;
|
|
|
- background: rgba(0, 0, 0, .5);
|
|
|
- justify-content: flex-end;
|
|
|
- z-index: 1001;
|
|
|
-
|
|
|
- .box{
|
|
|
- background: #FFFFFF;
|
|
|
- border-radius: 24rpx 24rpx 0 0;
|
|
|
- &-top{
|
|
|
- width: 100%;
|
|
|
- height: 84rpx;
|
|
|
- &-btn{
|
|
|
- width: 120rpx;
|
|
|
- font-size: 30rpx;
|
|
|
- text-align: center;
|
|
|
- line-height: 42rpx;
|
|
|
- &.qx{
|
|
|
- color: #909193;
|
|
|
- }
|
|
|
- &.qd{
|
|
|
- color: #3c9cff;
|
|
|
- }
|
|
|
- }
|
|
|
- &-title{
|
|
|
- flex: 1;
|
|
|
- font-size: 32rpx;
|
|
|
- color: #303133;
|
|
|
- text-align: center;
|
|
|
- line-height: 42rpx;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- .picker-view {
|
|
|
- width: 100%;
|
|
|
- height: 380rpx;
|
|
|
- margin: 40rpx 0 80rpx;
|
|
|
- }
|
|
|
-
|
|
|
- .picker-item {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- font-size: 36rpx;
|
|
|
- color: #303133;
|
|
|
- transition: all 0.2s;
|
|
|
-
|
|
|
- &.selected-item {
|
|
|
- font-size: 36rpx;
|
|
|
- font-weight: bold;
|
|
|
- color: #303133;
|
|
|
- }
|
|
|
- }
|
|
|
-</style>
|
|
|
+<template>
|
|
|
+ <view class="ssq adffc">
|
|
|
+ <view class="box">
|
|
|
+ <view class="box-top adfac">
|
|
|
+ <view class="box-top-btn qx" @click="handleCancel">取消</view>
|
|
|
+ <view class="box-top-title">{{ title }}</view>
|
|
|
+ <view class="box-top-btn qd" @click="handleConfirm">确定</view>
|
|
|
+ </view>
|
|
|
+ <picker-view :value="pickerValue" @change="handlePickerChange" class="picker-view">
|
|
|
+ <!-- 省份列 -->
|
|
|
+ <picker-view-column>
|
|
|
+ <view class="picker-item" :class="{ 'selected-item': pickerValue[0] === index }" v-for="(province, index) in provinces" :key="province.id">
|
|
|
+ {{ province.name }}
|
|
|
+ </view>
|
|
|
+ </picker-view-column>
|
|
|
+ <!-- 城市列 -->
|
|
|
+ <picker-view-column>
|
|
|
+ <view class="picker-item" :class="{ 'selected-item': pickerValue[1] === index }" v-for="(city, index) in cities" :key="city.id">
|
|
|
+ {{ city.name }}
|
|
|
+ </view>
|
|
|
+ </picker-view-column>
|
|
|
+ <!-- 区县列 -->
|
|
|
+ <picker-view-column>
|
|
|
+ <view class="picker-item" :class="{ 'selected-item': pickerValue[2] === index }" v-for="(area, index) in areas" :key="area.id">
|
|
|
+ {{ area.name }}
|
|
|
+ </view>
|
|
|
+ </picker-view-column>
|
|
|
+ </picker-view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+export default {
|
|
|
+ props: {
|
|
|
+ title: {
|
|
|
+ typeof: String,
|
|
|
+ default: '所属地区'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ provinces: [],
|
|
|
+ cities: [],
|
|
|
+ areas: [],
|
|
|
+ pickerValue: [0, 0, 0]
|
|
|
+ };
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ selectedProvince: function () {
|
|
|
+ return this.provinces[this.pickerValue[0]] || {};
|
|
|
+ },
|
|
|
+ selectedCity: function () {
|
|
|
+ return this.cities[this.pickerValue[1]] || {};
|
|
|
+ },
|
|
|
+ selectedArea: function () {
|
|
|
+ return this.areas[this.pickerValue[2]] || {};
|
|
|
+ }
|
|
|
+ },
|
|
|
+ mounted() {
|
|
|
+ this.getProvinceCityAreaData();
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ getProvinceCityAreaData() {
|
|
|
+ this.$api.get('/sys/region/tree').then(({ data: res }) => {
|
|
|
+ if (res.code !== 0) return this.$showToast(res.msg);
|
|
|
+ this.provinces = this.arrayToTreeOptimized(res.data);
|
|
|
+ this.initialize();
|
|
|
+ });
|
|
|
+ },
|
|
|
+ //方法一:递归
|
|
|
+ arrayToTree(list, rootPid = '0') {
|
|
|
+ const tree = [];
|
|
|
+
|
|
|
+ // 1. 首先找到所有根节点
|
|
|
+ list.forEach((item) => {
|
|
|
+ if (item.pid === rootPid) {
|
|
|
+ // 2. 找到根节点后,递归寻找它的子节点
|
|
|
+ const children = this.arrayToTree(list, item.id);
|
|
|
+ if (children.length > 0) {
|
|
|
+ item.children = children;
|
|
|
+ }
|
|
|
+ tree.push(item);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ return tree;
|
|
|
+ },
|
|
|
+ //方法二:Map映射,性能提升
|
|
|
+ arrayToTreeOptimized(list, rootPid = '0') {
|
|
|
+ const map = new Map();
|
|
|
+ const tree = [];
|
|
|
+
|
|
|
+ list.forEach((item) => {
|
|
|
+ map.set(item.id, { ...item, children: [] });
|
|
|
+ });
|
|
|
+
|
|
|
+ // 2. 再次遍历列表,建立父子关系
|
|
|
+ map.forEach((node) => {
|
|
|
+ const parent = map.get(node.pid);
|
|
|
+ if (parent) {
|
|
|
+ // 如果找到父节点,则将当前节点添加到父节点的 children 数组中
|
|
|
+ parent.children.push(node);
|
|
|
+ } else if (node.pid === rootPid) {
|
|
|
+ // 如果没有父节点,并且 pid 是根 pid,则为根节点
|
|
|
+ tree.push(node);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 清理空 children 数组(可选步骤,如果不需要空数组)
|
|
|
+ map.forEach((node) => {
|
|
|
+ if (node.children.length === 0) {
|
|
|
+ delete node.children;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ return tree;
|
|
|
+ },
|
|
|
+ handlePickerChange(e) {
|
|
|
+ const newPickerValue = e.detail.value;
|
|
|
+ const [provinceIndex, cityIndex, areaIndex] = newPickerValue;
|
|
|
+ const oldProvinceIndex = this.pickerValue[0];
|
|
|
+ const oldCityIndex = this.pickerValue[1];
|
|
|
+
|
|
|
+ if (provinceIndex !== oldProvinceIndex) {
|
|
|
+ this.cities = this.provinces[provinceIndex].children;
|
|
|
+ this.areas = this.cities[0]?.children || [];
|
|
|
+ this.pickerValue = [provinceIndex, 0, 0];
|
|
|
+ } else if (cityIndex !== oldCityIndex) {
|
|
|
+ // 更新地区列表,并将地区索引重置为0
|
|
|
+ this.areas = this.cities[cityIndex]?.children || [];
|
|
|
+ this.pickerValue = [provinceIndex, cityIndex, 0];
|
|
|
+ } else {
|
|
|
+ this.pickerValue = newPickerValue;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ initialize() {
|
|
|
+ this.cities = this.provinces[0]?.children || [];
|
|
|
+ this.areas = this.cities[0]?.children || [];
|
|
|
+ },
|
|
|
+ handleCancel() {
|
|
|
+ this.$emit('cancel');
|
|
|
+ },
|
|
|
+ handleConfirm() {
|
|
|
+ this.$emit('confirm', {
|
|
|
+ provinceId:this.provinces[this.pickerValue[0]].id,
|
|
|
+ cityId:this.cities[this.pickerValue[1]].id,
|
|
|
+ districtId:this.areas[this.pickerValue[2]].id,
|
|
|
+ provinceName:this.provinces[this.pickerValue[0]].name,
|
|
|
+ cityName:this.cities[this.pickerValue[1]].name,
|
|
|
+ districtName:this.areas[this.pickerValue[2]].name,
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+};
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped lang="scss">
|
|
|
+.ssq {
|
|
|
+ position: fixed;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ top: 0;
|
|
|
+ bottom: 0;
|
|
|
+ background: rgba(0, 0, 0, 0.5);
|
|
|
+ justify-content: flex-end;
|
|
|
+ z-index: 1001;
|
|
|
+
|
|
|
+ .box {
|
|
|
+ background: #ffffff;
|
|
|
+ border-radius: 24rpx 24rpx 0 0;
|
|
|
+ &-top {
|
|
|
+ width: 100%;
|
|
|
+ height: 84rpx;
|
|
|
+ &-btn {
|
|
|
+ width: 120rpx;
|
|
|
+ font-size: 30rpx;
|
|
|
+ text-align: center;
|
|
|
+ line-height: 42rpx;
|
|
|
+ &.qx {
|
|
|
+ color: #909193;
|
|
|
+ }
|
|
|
+ &.qd {
|
|
|
+ color: #3c9cff;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ &-title {
|
|
|
+ flex: 1;
|
|
|
+ font-size: 32rpx;
|
|
|
+ color: #303133;
|
|
|
+ text-align: center;
|
|
|
+ line-height: 42rpx;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.picker-view {
|
|
|
+ width: 100%;
|
|
|
+ height: 380rpx;
|
|
|
+ margin: 40rpx 0 80rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.picker-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ font-size: 36rpx;
|
|
|
+ color: #303133;
|
|
|
+ transition: all 0.2s;
|
|
|
+
|
|
|
+ &.selected-item {
|
|
|
+ font-size: 36rpx;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #303133;
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|