| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358 |
- <template>
- <view class="addressPicker">
- <u-popup :show="modelshow" @close="close" round="20rpx" bgColor="#fff" mode="bottom"
- :closeOnClickOverlay="true">
- <view class="">
- <view class="header header-padding dis a-c j-c borderBottom">
- <text>选择地区</text>
- </view>
- <view class="content">
- <view class="quickOptions">
- <picker-view :indicator-style="indicatorStyle" :value="value" @change="monthbindChange"
- class="picker-view">
- <picker-view-column>
- <view class="item dis a-c j-c " v-for="(item,index) in columns[0]" :key="index">
- {{item.name}}
- </view>
- </picker-view-column>
- <picker-view-column>
- <view class="item dis a-c j-c " v-for="(item,index) in columns[1]" :key="index">
- {{item.name}}
- </view>
- </picker-view-column>
- <picker-view-column>
- <view class="item dis a-c j-c " v-for="(item,index) in columns[2]" :key="index">
- {{item.name}}
- </view>
- </picker-view-column>
- </picker-view>
- <view class="custom-footer dis j-s a-c ">
- <view class="dis a-c j-c" @tap="statusConfirm">确定</view>
- </view>
- </view>
- </view>
- </view>
- </u-popup>
- <!-- <u-picker :show="shows" ref="uPicker" :defaultIndex="defaultIndex" :columns="columns" keyName="name"
- @change="changeHandler" @cancel="cancel" @confirm="confirm" @close="close"
- :closeOnClickOverlay="true"></u-picker> -->
- </view>
- </template>
- <script>
- import address from '@/utils/address.json'
- export default {
- props: {
- show: Boolean,
- defaultIndex: {
- type: Array,
- default: () => {
- return [0, 0, 0]
- },
- },
- defaultCodes: { // 改为接收标准6位码数组
- type: Array,
- default: () => ['110000', '110100', '110101'] // 默认北京东城区
- }
- },
- data() {
- return {
- modelshow: this.show,
- columns: [
- [],
- [],
- []
- ], // 省/市/区三级数据
- fullData: [], // 包含完整结构的缓存数据
- value: [0, 0, 0],
- lastValue: [0, 0, 0], // 保存上一次的选中值
- indicatorStyle: `height: 41px;`
- }
- },
- watch: {
- show(val) {
- this.modelshow = val
- if (val) this.initByCodes(this.defaultCodes)
- },
- defaultCodes: {
- immediate: true,
- handler(codes) {
- this.initByCodes(codes)
- }
- }
- },
- async created() {
- await this.initData()
- console.log(this.fullData);
- },
- methods: {
- //月份滚动事件
- monthbindChange: function(e) {
- const val = e.detail.value; // 当前选中下标数组 [省索引, 市索引, 区索引]
- const lastVal = this.lastValue;
- let provinceIndex = val[0];
- let cityIndex = val[1];
- let areaIndex = val[2] || 0;
- // 判断是否是省份变化
- if (provinceIndex !== lastVal[0]) {
- if (this.fullData[provinceIndex]) {
- const cities = this.fullData[provinceIndex].children || [];
- this.columns[1] = cities.map(c => ({
- name: c.name,
- code: c.code
- }));
- // 重置城市索引到第一个
- cityIndex = 0;
- // 更新区级数据
- if (cities[0]) {
- const areas = cities[0].children || [];
- this.columns[2] = areas.map(a => ({
- name: a.name,
- code: a.code
- }));
- areaIndex = 0;
- } else {
- this.columns[2] = [];
- }
- }
- }
- // 判断是否是城市变化
- else if (cityIndex !== lastVal[1]) {
- if (this.fullData[provinceIndex]?.children?.[cityIndex]) {
- const areas = this.fullData[provinceIndex].children[cityIndex].children || [];
- this.columns[2] = areas.map(a => ({
- name: a.name,
- code: a.code
- }));
- areaIndex = 0;
- } else {
- this.columns[2] = [];
- }
- }
- // 保存当前值
- this.lastValue = [provinceIndex, cityIndex, areaIndex];
- // 使用nextTick延迟更新value,避免阻塞用户滑动
- this.$nextTick(() => {
- this.value = [provinceIndex, cityIndex, areaIndex];
- });
- },
- // 初始化数据结构(含6位码处理)
- initData() {
- this.fullData = address.map(province => ({
- ...province,
- code: this.ensure6DigitCode(province.code),
- children: province.children.map(city => ({
- ...city,
- code: this.ensure6DigitCode(city.code),
- children: (city.children || []).map(area => ({
- ...area,
- code: this.ensure6DigitCode(area.code)
- }))
- }))
- }))
- // 初始化省级选项
- this.columns[0] = this.fullData.map(p => ({
- name: p.name,
- code: p.code
- }))
- this.initDefaultSelection();
- },
- initDefaultSelection() {
- if (this.defaultCodes && this.defaultCodes.length >= 3) {
- // 如果传入了defaultCodes,优先使用
- this.initByCodes(this.defaultCodes);
- } else {
- // 否则自动选择第一个省+第一个市+第一个区
- const firstProvince = this.fullData[0];
- const firstCity = firstProvince.children?.[0];
- const firstArea = firstCity?.children?.[0];
- this.columns[1] = firstProvince.children?.map(c => ({
- name: c.name,
- code: c.code
- })) || [];
- this.columns[2] = firstCity?.children?.map(a => ({
- name: a.name,
- code: a.code
- })) || [];
- // 设置Picker选中状态
- this.$nextTick(() => {
- this.$refs.uPicker?.setIndexs([0, 0, 0]);
- });
- }
- },
- // 根据6位码初始化选中状态
- initByCodes(codes) {
- if (!codes || codes.length < 3) return
- // 1. 查找省级索引
- const provIndex = this.fullData.findIndex(
- p => p.code === codes[0]
- )
- if (provIndex === -1) return
- // 2. 设置市级数据
- const cities = this.fullData[provIndex].children
- this.columns[1] = cities.map(c => ({
- name: c.name,
- code: c.code
- }))
- // 3. 查找市级索引
- const cityIndex = cities.findIndex(
- c => c.code === codes[1]
- )
- if (cityIndex === -1) return
- // 4. 设置区级数据
- const areas = cities[cityIndex].children
- this.columns[2] = areas.map(a => ({
- name: a.name,
- code: a.code
- }))
- },
- // 确保6位行政区划码
- ensure6DigitCode(code) {
- if (!code) return ''
- if (code.length === 2) return code + '0000'
- if (code.length === 4) return code + '00'
- return code // 6位或9位码保持不变
- },
- // 选择变化事件(适配6位码)
- changeHandler(e) {
- const {
- columnIndex,
- index
- } = e
- if (columnIndex === 0) {
- // 省份变化
- const cities = this.fullData[index].children
- this.columns[1] = cities.map(c => ({
- name: c.name,
- code: c.code
- }))
- this.columns[2] = cities[0]?.children.map(a => ({
- name: a.name,
- code: a.code
- })) || []
- this.$refs.uPicker.setColumnValues(1, this.columns[1])
- this.$refs.uPicker.setColumnValues(2, this.columns[2])
- } else if (columnIndex === 1) {
- // 城市变化
- const areas = this.fullData[e.indexs[0]].children[index].children
- this.columns[2] = areas.map(a => ({
- name: a.name,
- code: a.code
- }))
- this.$refs.uPicker.setColumnValues(2, this.columns[2])
- }
- },
- statusConfirm() {
- const provinceIndex = this.value[0] || 0;
- const cityIndex = this.value[1] || 0;
- const areaIndex = this.value[2] || 0;
- const province = this.columns[0][provinceIndex];
- const city = this.columns[1][cityIndex];
- const area = this.columns[2][areaIndex];
- this.$emit('confirm', {
- names: [province?.name, city?.name, area?.name].filter(Boolean),
- codes: [province?.code, city?.code, area?.code].filter(Boolean),
- fullAddress: [province?.name, city?.name, area?.name].filter(Boolean).join('-')
- });
- this.close();
- },
- cancel() {
- this.$emit('cancel')
- },
- close() {
- this.$emit('close')
- },
- confirm(e) {
- const selected = e.value
- this.$emit('confirm', {
- names: selected.map(i => i.name),
- codes: selected.map(i => i.code), // 标准6位码
- fullAddress: selected.map(i => i.name).join('-')
- })
- }
- }
- }
- </script>
- <style lang="scss" scoped>
- .header {
- color: #333;
- font-size: 32rpx;
- font-weight: bold;
- }
- .header-padding {
- padding: 27rpx 30rpx;
- }
- .borderBottom {
- border-bottom: 1px solid #EEEEEE;
- }
- .quickOptions {
- padding: 32rpx;
- box-sizing: border-box;
- margin-bottom: 20rpx;
- }
- .custom-footer {
- padding: 40rpx 0 0;
- box-sizing: border-box;
- view {
- font-size: 30rpx;
- padding: 20rpx;
- flex: 1;
- }
- view:first-child {
- color: #333;
- background: transparent;
- border-radius: 50rpx;
- border: 1rpx solid #FDE935;
- margin-right: 28rpx;
- }
- view:last-child {
- background: #FDE935;
- border-radius: 50rpx;
- color: #1F1F1F;
- }
- }
- .picker-view {
- width: 100%;
- height: 400rpx;
- .item {
- font-size: 30rpx;
- color: #333;
- }
- }
- </style>
|