# 五级联动地址选择器组件

## 组件简介

`area-cascade-picker` 是一个支持省市区县街道社区的五级联动地址选择组件，适用于需要选择详细地址的场景。

## 功能特点

- ✅ 五级联动选择（省/市/区/街道/社区）
- ✅ 自动级联更新
- ✅ 支持取消和确认操作
- ✅ 底部弹出式交互
- ✅ 支持自定义标题
- ✅ 返回完整的地址信息和各级代码

## 使用方法

### 1. 引入组件

```vue
<template>
    <view>
        <button @click="openPicker">选择地址</button>
        <area-cascade-picker ref="areaPicker"></area-cascade-picker>
    </view>
</template>

<script setup>
import { ref } from 'vue'
import AreaCascadePicker from '@/components/area-cascade-picker/area-cascade-picker.vue'

const areaPicker = ref(null)
</script>
```

### 2. 打开选择器

```javascript
const openPicker = () => {
    areaPicker.value?.open({
        title: '选择地址',
        maskClick: true,
        success: (addressData) => {
            console.log('选择的地址：', addressData)
            // 处理选择结果
        },
        cancel: () => {
            console.log('取消选择')
        },
        change: (addressData) => {
            console.log('地址变化：', addressData)
        }
    })
}
```

## API 说明

### open(config)

打开地址选择器

#### 参数 config

| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|--------|------|------|--------|------|
| title | String | 否 | '选择地址' | 选择器标题 |
| maskClick | Boolean | 否 | false | 是否允许点击遮罩关闭 |
| success | Function | 否 | - | 确认选择的回调函数 |
| cancel | Function | 否 | - | 取消选择的回调函数 |
| change | Function | 否 | - | 选择变化的回调函数 |
| defaultValue | Object | 否 | null | 默认选中的地址（暂未实现） |
| forceRefresh | Boolean | 否 | false | 是否强制刷新数据（忽略缓存） |

#### success 回调参数

```javascript
{
    address: "新疆维吾尔自治区/喀什地区/喀什市/学府街道/学府社区居委会",
    province: {
        code: "650000",
        name: "新疆维吾尔自治区"
    },
    city: {
        code: "653100",
        name: "喀什地区"
    },
    district: {
        code: "653101",
        name: "喀什市"
    },
    street: {
        code: "65310101",
        name: "学府街道"
    },
    community: {
        code: "6531010101",
        name: "学府社区居委会"
    }
}
```

### close()

关闭地址选择器

```javascript
areaPicker.value?.close()
```

### clearCache()

清除地址数据缓存

```javascript
areaPicker.value?.clearCache()
```

## 数据格式

组件使用树形结构的地址数据，格式如下：

```javascript
[
    {
        code: '650000',           // 行政区划代码
        name: '新疆维吾尔自治区',  // 名称
        children: [               // 下级行政区
            {
                code: '653100',
                name: '喀什地区',
                children: [
                    {
                        code: '653101',
                        name: '喀什市',
                        children: [
                            {
                                code: '65310101',
                                name: '学府街道',
                                children: [
                                    {
                                        code: '6531010101',
                                        name: '学府社区居委会'
                                    }
                                ]
                            }
                        ]
                    }
                ]
            }
        ]
    }
]
```

## 完整示例

### 企业注册地址选择

```vue
<template>
    <view class="form-item" @click="selectLocation">
        <text class="label">企业注册地点</text>
        <view class="input-content">
            <text :class="{ placeholder: !formData.address }">
                {{ formData.address || '请选择注册地点' }}
            </text>
            <uni-icons type="arrowright" size="16"></uni-icons>
        </view>
    </view>
    
    <area-cascade-picker ref="areaPicker"></area-cascade-picker>
</template>

<script setup>
import { ref, reactive } from 'vue'
import AreaCascadePicker from '@/components/area-cascade-picker/area-cascade-picker.vue'

const areaPicker = ref(null)
const formData = reactive({
    address: '',
    provinceCode: '',
    provinceName: '',
    cityCode: '',
    cityName: '',
    districtCode: '',
    districtName: '',
    streetCode: '',
    streetName: '',
    communityCode: '',
    communityName: ''
})

const selectLocation = () => {
    areaPicker.value?.open({
        title: '选择企业注册地点',
        maskClick: true,
        success: (addressData) => {
            // 保存完整地址
            formData.address = addressData.address
            
            // 保存各级信息
            formData.provinceCode = addressData.province?.code
            formData.provinceName = addressData.province?.name
            formData.cityCode = addressData.city?.code
            formData.cityName = addressData.city?.name
            formData.districtCode = addressData.district?.code
            formData.districtName = addressData.district?.name
            formData.streetCode = addressData.street?.code
            formData.streetName = addressData.street?.name
            formData.communityCode = addressData.community?.code
            formData.communityName = addressData.community?.name
            
            console.log('已选择地址：', formData)
        }
    })
}
</script>
```

## 性能优化

### 懒加载方案（已实现）⭐

组件已实现**懒加载**机制，大幅优化90M+地址数据的加载性能：

#### 核心优化

1. **首次加载**：只加载省份列表（< 1MB，3-5秒完成）
2. **按需加载**：用户选择省份后，再加载该省份的详细数据（2-5MB，5-10秒）
3. **智能缓存**：已加载的数据会缓存，切换省份时秒开
4. **自动降级**：如果服务器不支持分片接口，自动从完整数据中提取

#### 性能对比

| 场景 | 优化前 | 优化后（懒加载） |
|------|--------|------------------|
| 首次打开选择器 | 加载90M+（3-5分钟） | 加载省份列表（< 1MB，3-5秒） |
| 选择省份 | 无需加载 | 加载该省份数据（2-5MB，5-10秒） |
| 切换省份 | 无需加载 | 从缓存读取（< 1秒） |

#### 使用方式

组件已自动使用懒加载模式，无需修改调用代码：

```javascript
// 正常使用，自动懒加载
areaPicker.value?.open({
    success: (addressData) => {
        console.log('选择的地址：', addressData)
    }
})

// 强制刷新数据（忽略缓存）
areaPicker.value?.open({
    forceRefresh: true,  // 强制从服务器重新加载
    success: (addressData) => {
        console.log('选择的地址：', addressData)
    }
})

// 清除缓存
areaPicker.value?.clearCache()
```

### 服务器分片接口（最佳方案）🚀

如果服务器可以提供分片接口，性能会进一步提升。详见：[地址数据懒加载优化方案.md](../../docs/地址数据懒加载优化方案.md)

需要的接口：
- 省份列表接口：`/address_provinces.json`（轻量级，< 1MB）
- 省份详情接口：`/address_province_{code}.json`（按需加载，每个2-5MB）

### 缓存机制

1. **自动缓存**：已加载的数据会自动缓存到 IndexedDB（H5）或 uni.storage（小程序）
2. **缓存有效期**：默认7天，过期后自动重新加载
3. **离线支持**：网络失败时自动使用缓存数据
4. **存储方案**：优先使用 IndexedDB，自动降级到 uni.storage

## 注意事项

1. **数据来源**：当前从远程JSON文件加载，生产环境建议接入后端API
2. **数据更新**：如需接入后端API，修改 `loadAreaData` 方法即可
3. **性能优化**：已集成缓存机制，首次加载后速度大幅提升
4. **兼容性**：支持 H5、微信小程序等多端
5. **存储限制**：小程序环境有存储限制，如遇问题会自动清理旧缓存

## 接入后端API

在组件的 `loadAreaData` 方法中取消注释并配置API：

```javascript
async loadAreaData() {
    try {
        const resp = await uni.request({
            url: '/app/common/area/cascade',
            method: 'GET'
        });
        if (resp.statusCode === 200 && resp.data && resp.data.data) {
            this.areaData = resp.data.data;
            return;
        }
    } catch (error) {
        console.error('加载地区数据失败:', error);
    }
    
    // 失败时使用模拟数据
    this.areaData = this.getMockData();
}
```

## 更新日志

### v1.2.0 (2025-01-XX)
- 🚀 **懒加载优化**：实现按需加载，首次加载从几分钟减少到几秒
- ✅ 首次只加载省份列表（< 1MB，3-5秒）
- ✅ 按需加载省份详情（选择省份时才加载）
- ✅ 智能缓存已加载的数据，切换省份秒开
- ✅ 支持服务器分片接口（最佳性能）
- ✅ 自动降级方案（兼容完整数据）

### v1.1.0 (2025-01-XX)
- 🚀 **性能优化**：集成智能缓存系统，优化90M+地址数据加载
- ✅ 支持 IndexedDB 和 uni.storage 双缓存方案
- ✅ 支持缓存过期管理和自动清理
- ✅ 支持强制刷新数据
- ✅ 优化首次加载体验，后续加载秒开

### v1.0.0 (2025-10-21)
- ✨ 初始版本
- ✅ 实现五级联动选择功能
- ✅ 支持省市区县街道社区选择
- ✅ 提供完整的地址信息返回

