vue3+vite+uniapp 封装一个省市区组件

一、预览图

请添加图片描述

二、使用前的一些注意事项

  1. 只支持在 uniapp vue3 项目中使用
  2. 支持微信小程序和h5 (app端没有测试过)
  3. ui库用的 uview-plus
  4. 省市区数据用的是 vant-ui 提供的一个赖库 @vant/area-data

三、组件代码

<template>
    <u-popup :show="show" type="bottom" @close="handlePopupClose" round="44rpx">
        <view class="area-picker">
            <view class="title">
                请选择收货地址
                <view class="close-icon" @click="handlePopupClose">
                    <u-icon name="close" size="44rpx" color="#666666"></u-icon>
                </view>
            </view>
            <view class="header">
                <view @click="doChange('province')"
                    :class="['header-item', activeType === 'province' ? 'header-item--active' : '']"
                    v-if="activeType === 'province' || activeType === 'city' || activeType === 'district' || innerProvince">
                    {{ innerProvince ? innerProvince.name : '请选择省' }}
                </view>
                <view @click="doChange('city')" :class="['header-item', activeType === 'city' ? 'header-item--active' : '']"
                    v-if="activeType === 'city' || activeType === 'district' || innerProvince">
                    {{ innerProvince && innerCity ? innerCity.name : '请选择市' }}
                </view>
                <view @click="doChange('district')"
                    :class="['header-item', activeType === 'district' ? 'header-item--active' : '']"
                    v-if="activeType === 'district' || innerCity">
                    {{ innerProvince && innerCity && innerCounty ? innerCounty.name : '请选择区' }}
                </view>
            </view>
            <scroll-view scroll-y class="main" :scroll-with-animation="true">
                <view :id="`tag-${item.id}`" :class="['main-item', select(item.name) ? 'main-item--active' : '']"
                    @click="doSelect(item)" v-for="item in showList" :key="item.id">
                    <u-icon v-if="select(item.name)" name="checkbox-mark" size="44rpx" color="#3c9cff"></u-icon>
                    {{ item.name }}
                </view>
            </scroll-view>
        </view>
    </u-popup>
</template>
<script setup>
import { computed, nextTick, ref } from 'vue'
import { areaList } from "@vant/area-data";


const props = defineProps({
    show: {
        type: Boolean,
        default: false
    },
    area: {
        type: Array,
        default: () => []
    },
    id: {
        type: String,
        default: ''
    },
})

const emits = defineEmits(['close', 'confirm']) // 事件

const areaData = ref(areaList)

let innerProvince = ref(null) // 选择的省
let innerCity = ref(null) // 选择的市
let innerCounty = ref(null) // 选择的区
let activeType = ref('province') // 当前所选的area类型
const viewId = ref(null) //  应当展示在视图中的节点id

// 是否被选中
const select = computed(() => {
    return (item) => {
        switch (activeType.value) {
            case 'province':
                return innerProvince.value ? item === innerProvince.value.name : false
            case 'city':
                return innerCity.value ? item === innerCity.value.name : false
            case 'district':
                return innerCounty.value ? item === innerCounty.value.name : false
            default:
                return innerProvince.value ? item === innerProvince.value.name : false
        }
    }
})

// 展示的列表
const showList = computed(() => {
    switch (activeType.value) {
        case 'province':
            return provinceList.value
        case 'city':
            return cityList.value
        case 'district':
            return countyList.value
        default:
            return provinceList.value
    }
})

// 省列表
const provinceList = computed(() => {
    const provinceList = []
    if (areaData.value && areaData.value.province_list) {
        for (const key in areaData.value.province_list) {
            if (areaData.value.province_list[key]) {
                provinceList.push({
                    id: key,
                    name: areaData.value.province_list[key]
                })
            }
        }
    }
    return provinceList
})

// 市列表
const cityList = computed(() => {
    const cityList = []
    if (areaData.value && areaData.value.city_list) {
        for (const key in areaData.value.city_list) {
            if (areaData.value.city_list[key] && innerProvince.value && innerProvince.value.id.slice(0, 2) === key.slice(0, 2)) {
                cityList.push({
                    id: key,
                    name: areaData.value.city_list[key]
                })
            }
        }
    }
    return cityList
})

// 区列表
const countyList = computed(() => {
    const countyList = []
    if (areaData.value && areaData.value.county_list) {
        for (const key in areaData.value.county_list) {
            if (areaData.value.county_list[key] && (!innerProvince.value || (innerCity.value && innerCity.value.id.slice(0, 4) === key.slice(0, 4)))) {
                countyList.push({
                    id: key,
                    name: areaData.value.county_list[key]
                })
            }
        }
    }
    return countyList
})
// 关闭 popup 
function handlePopupClose() {
    emits('close')
}
// 地址选择完成
function doConfirm() {
    const list = [innerProvince.value, innerCity.value, innerCounty.value]
    const obj = {}
    list.forEach((v, i) => {
        i === 0 ? obj.province = v.name : ''
        i === 1 ? obj.city = v.name : ''
        i === 2 ? obj.county = v.name : ''
    });
    emits('confirm', obj, [innerProvince.value, innerCity.value, innerCounty.value])
}

// 切换当前选择的省市区类型
function doChange(type) {
    activeType.value = type
}

// 选中省市区项
function doSelect(item) {
    switch (activeType.value) {
        case 'province':
            if (innerProvince.value && innerProvince.value.id === item.id) {
                innerProvince.value = null
            } else {
                innerProvince.value = item
                activeType.value = 'city'
            }
            innerCity.value = null
            innerCounty.value = null
            break
        case 'city':
            if (innerCity.value && innerCity.value.id === item.id) {
                innerCity.value = null
            } else {
                innerCity.value = item
                activeType.value = 'district'
            }
            innerCounty.value = null
            break
        case 'district':
            if (innerCounty.value && innerCounty.value.id === item.id) {
                innerCounty.value = null
            } else {
                innerCounty.value = item
                doConfirm()
            }
            break
        default:
            if (innerProvince.value && innerProvince.value.id === item.id) {
                innerProvince.value = null
            } else {
                innerProvince.value = item
                activeType.value = 'city'
            }
            innerCity.value = null
            innerCounty.value = null
            break
    }
}
</script>
  
<style lang="scss" scoped>
$color-text-secondary: #101010;

.area-picker {
    position: relative;
    height: 846rpx;
    height: calc(846rpx + constant(safe-area-inset-bottom));
    height: calc(846rpx + env(safe-area-inset-bottom));
    width: calc(100vw - 80rpx);
    background: #ffffff;
    padding: 0 40rpx;
    border-radius: 20rpx 20rpx 0px 0px;
    padding-bottom: 0;
    padding-bottom: constant(safe-area-inset-bottom);
    padding-bottom: env(safe-area-inset-bottom);

    .title {
        height: 114rpx;
        width: 100%;
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 36rpx;
        font-family: PingFangSC-Medium, PingFang SC;
        font-weight: 500;
        color: #202124;

        .close-icon {
            position: absolute;
            top: 57rpx;
            right: 0;
            padding: 19rpx;
            transform: translateY(-50%);
        }
    }

    .header {
        display: flex;
        margin-bottom: 24rpx;

        &-item {
            height: 44rpx;
            font-size: 32rpx;
            font-family: PingFangSC-Medium, PingFang SC;
            font-weight: 500;
            color: $color-text-secondary;
            max-width: 186rpx;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;

            &:not(:last-child) {
                margin-right: 56rpx;
            }

            &--active {
                color: $u-primary;
            }
        }
    }

    .main {
        height: calc(100% - 182rpx);
        overflow: auto;

        ::-webkit-scrollbar {
            width: 0;
            height: 0;
            color: transparent;
        }

        &-item {
            display: flex;
            align-items: center;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
            width: 100%;
            height: 84rpx;
            background: #ffffff;
            font-size: 28rpx;
            color: $color-text-secondary;

            image {
                width: 44rpx;
                height: 44rpx;
            }

            &--active {
                font-family: PingFangSC-Medium, PingFang SC;
                font-weight: 500;
                color: $color-text-secondary;
            }
        }
    }
}
</style>

四、组件使用

<template>
    <view class="container">
        <u-button @click="show = true" type="primary" customStyle="width: 90%;margin-top: 60rpx;">选择区域</u-button>
        <view style="text-align: center; margin-top: 60rpx;">所选区域:{{ areaText }}</view>
        <AreaPicker :show="show" @confirm="handleConfirmArea" @close="show = false"></AreaPicker>
    </view>
</template>

<script setup>
import { ref } from "vue";

const show = ref(false);
const areaText = ref("");

function handleConfirmArea(item) {
    console.log("当前选中区域:", item);
    const { province, city, county } = item;
    areaText.value = province + " " + city + " " + county;
    show.value = false;
}
</script>

<style lang="scss" scoped></style>
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
搭建一个使用vue3+ts+vite+uniapp的微信小程序的步骤如下: 1. 首先安装最新版的Node.js和npm。 2. 安装uni-app-cli脚手架工具,命令如下: ``` npm install -g @vue/cli npm install -g @vue/cli-init npm install -g @dcloudio/uni-cli ``` 3. 创建一个uni-app项目,命令如下: ``` vue create -p dcloudio/uni-preset-vue my-project ``` 4. 进入项目目录,安装依赖包,命令如下: ``` cd my-project npm install ``` 5. 安装并配置微信小程序开发者工具,下载地址:https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html 6. 在微信小程序开发者工具中,选择导入uni-app项目,选择项目目录下的dist/dev/mp-weixin文件夹,导入后即可进行开发和调试。 7. 如果需要使用vue3和typescript,在项目中安装相关依赖包,命令如下: ``` npm install --save-dev vue@next @vue/compiler-sfc typescript ts-loader ``` 8. 在项目根目录下创建vue.config.js文件,配置如下: ``` module.exports = { chainWebpack: config => { config.module .rule('ts') .use('ts-loader') .loader('ts-loader') .tap(options => { options.appendTsSuffixTo = [/\.vue$/] return options }) } } ``` 9. 在src目录下创建shims-vue.d.ts文件,内容如下: ``` declare module "*.vue" { import { ComponentOptions } from "vue"; const component: ComponentOptions; export default component; } ``` 10. 现在你就可以使用vue3和typescript进行开发了。同时,如果需要使用vite进行开发,可以参考uni-app官方文档进行配置:https://uniapp.dcloud.io/collocation/vite 以上就是使用vue3+ts+vite+uniapp搭建微信小程序的步骤。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值