gaoluyang
3 天以前 92230c9a97dc9ce9df3313d11d26999c04bb6b26
src/components/u-city-select/u-city-select.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,265 @@
<template>
   <u-popup :show="modelValue" mode="bottom" :popup="false" :mask="true" :closeable="true"
      :safe-area-inset-bottom="true" close-icon-color="#ffffff" :z-index="uZIndex" :maskCloseAble="maskCloseAble"
      @close="close">
      <u-tabs v-if="modelValue" :list="genTabsList" :scrollable="true" :current="tabsIndex" @change="tabsChange"
         ref="tabs" />
      <view class="area-box">
         <view class="u-flex" :class="{ 'change': isChange }">
            <view class="area-item">
               <view class="u-padding-10 u-bg-gray" style="height: 100%;">
                  <scroll-view :scroll-y="true" style="height: 100%">
                     <u-cell-group>
                        <u-cell v-for="(item, index) in provinces" :title="item.label" :arrow="false"
                           :index="index" :key="index" @click="provinceChange(index)">
                           <template v-slot:right-icon>
                              <u-icon v-if="isChooseP && province === index" size="17"
                                 name="checkbox-mark"></u-icon>
                           </template>
                        </u-cell>
                     </u-cell-group>
                  </scroll-view>
               </view>
            </view>
            <view class="area-item">
               <view class="u-padding-10 u-bg-gray" style="height: 100%;">
                  <scroll-view :scroll-y="true" style="height: 100%">
                     <u-cell-group v-if="isChooseP">
                        <u-cell v-for="(item, index) in citys" :title="item.label" :arrow="false" :index="index"
                           :key="index" @click="cityChange(index)">
                           <template v-slot:right-icon>
                              <u-icon v-if="isChooseC && city === index" size="17"
                                 name="checkbox-mark"></u-icon>
                           </template>
                        </u-cell>
                     </u-cell-group>
                  </scroll-view>
               </view>
            </view>
            <view class="area-item">
               <view class="u-padding-10 u-bg-gray" style="height: 100%;">
                  <scroll-view :scroll-y="true" style="height: 100%">
                     <u-cell-group v-if="isChooseC">
                        <u-cell v-for="(item, index) in areas" :title="item.label" :arrow="false" :index="index"
                           :key="index" @click="areaChange(index)">
                           <template v-slot:right-icon>
                              <u-icon v-if="isChooseA && area === index" size="17"
                                 name="checkbox-mark"></u-icon>
                           </template>
                        </u-cell>
                     </u-cell-group>
                  </scroll-view>
               </view>
            </view>
         </view>
      </view>
   </u-popup>
</template>
<script setup lang="ts">
import { ref, computed, onMounted, PropType } from 'vue';
import provincesSource from "./province.js";
import citysSource from "./city.js";
import areasSource from "./area.js";
// å®šä¹‰æŽ¥å£
interface Region {
   label: string;
   value: string;
}
interface CitySelectResult {
   province: Region;
   city: Region;
   area: Region;
}
interface TabItem {
   name: string;
}
// Props å®šä¹‰
const props = defineProps({
   // é€šè¿‡åŒå‘绑定控制组件的弹出与收起
   modelValue: {
      type: Boolean,
      default: false
   },
   // é»˜è®¤æ˜¾ç¤ºçš„地区,可传类似["河北省", "秦皇岛市", "北戴河区"]
   defaultRegion: {
      type: Array as PropType<string[]>,
      default: () => []
   },
   // é»˜è®¤æ˜¾ç¤ºåœ°åŒºçš„编码,defaultRegion和areaCode同时存在,areaCode优先,可传类似["13", "1303", "130304"]
   areaCode: {
      type: Array as PropType<string[]>,
      default: () => []
   },
   // æ˜¯å¦å…è®¸é€šè¿‡ç‚¹å‡»é®ç½©å…³é—­Picker
   maskCloseAble: {
      type: Boolean,
      default: true
   },
   // å¼¹å‡ºçš„z-index值
   zIndex: {
      type: [String, Number],
      default: 0
   }
});
// äº‹ä»¶å®šä¹‰
const emit = defineEmits<{
   (e: 'update:modelValue', value: boolean): void;
   (e: 'close'): void;
   (e: 'city-change', result: CitySelectResult): void;
}>();
const cityValue = ref("");
const isChooseP = ref(false); // æ˜¯å¦å·²ç»é€‰æ‹©äº†çœ
const province = ref(0); // çœçº§ä¸‹æ ‡
const provinces = ref<Region[]>(provincesSource);
const isChooseC = ref(false); // æ˜¯å¦å·²ç»é€‰æ‹©äº†å¸‚
const city = ref(0); // å¸‚级下标
const citys = ref<Region[]>(citysSource[0]);
const isChooseA = ref(false); // æ˜¯å¦å·²ç»é€‰æ‹©äº†åŒº
const area = ref(0); // åŒºçº§ä¸‹æ ‡
const areas = ref<Region[]>(areasSource[0][0]);
const tabsIndex = ref(0);
const tabs = ref();
// è®¡ç®—属性
const isChange = computed(() => {
   return tabsIndex.value > 1;
});
const genTabsList = computed((): TabItem[] => {
   let tabsList: TabItem[] = [{
      name: "请选择"
   }];
   if (isChooseP.value) {
      tabsList[0].name = provinces.value[province.value].label;
      tabsList[1] = {
         name: "请选择"
      };
   }
   if (isChooseC.value) {
      tabsList[1].name = citys.value[city.value].label;
      tabsList[2] = {
         name: "请选择"
      };
   }
   if (isChooseA.value) {
      tabsList[2].name = areas.value[area.value].label;
   }
   return tabsList;
});
const uZIndex = computed(() => {
   // å¦‚果用户有传递z-index值,优先使用
   return props.zIndex ? props.zIndex : 1075; // å‡è®¾$u.zIndex.popup为1075
});
// æ–¹æ³•
const setProvince = (label = "", value = "") => {
   provinces.value.map((v, k) => {
      if (value ? v.value == value : v.label == label) {
         provinceChange(k);
      }
   });
};
const setCity = (label = "", value = "") => {
   citys.value.map((v, k) => {
      if (value ? v.value == value : v.label == label) {
         cityChange(k);
      }
   });
};
const setArea = (label = "", value = "") => {
   areas.value.map((v, k) => {
      if (value ? v.value == value : v.label == label) {
         isChooseA.value = true;
         area.value = k;
      }
   });
};
const close = () => {
   emit('update:modelValue', false);
   emit('close');
};
const tabsChange = (value: { index: number }) => {
   tabsIndex.value = value.index;
};
const provinceChange = (index: number) => {
   isChooseP.value = true;
   isChooseC.value = false;
   isChooseA.value = false;
   province.value = index;
   citys.value = citysSource[index];
   tabsIndex.value = 1;
};
const cityChange = (index: number) => {
   isChooseC.value = true;
   isChooseA.value = false;
   city.value = index;
   areas.value = areasSource[province.value][index];
   tabsIndex.value = 2;
};
const areaChange = (index: number) => {
   isChooseA.value = true;
   area.value = index;
   const result: CitySelectResult = {
      province: provinces.value[province.value],
      city: citys.value[city.value],
      area: areas.value[area.value]
   };
   emit('city-change', result);
   close();
};
// ç”Ÿå‘½å‘¨æœŸé’©å­
onMounted(() => {
   if (props.areaCode.length == 3) {
      setProvince("", props.areaCode[0]);
      setCity("", props.areaCode[1]);
      setArea("", props.areaCode[2]);
   } else if (props.defaultRegion.length == 3) {
      setProvince(props.defaultRegion[0], "");
      setCity(props.defaultRegion[1], "");
      setArea(props.defaultRegion[2], "");
   }
});
</script>
<style lang="scss">
.area-box {
   width: 100%;
   overflow: hidden;
   height: 800rpx;
   >view {
      width: 150%;
      transition: transform 0.3s ease-in-out 0s;
      transform: translateX(0);
      &.change {
         transform: translateX(-33.3333333%);
      }
   }
   .area-item {
      width: 33.3333333%;
      height: 800rpx;
   }
}
</style>