6.7 KiB
6.7 KiB
地区三级联动表实现计划
目标
创建省份/城市/地区三级联动数据库表,在个人中心"所在位置"字段中使用级联选择,替代当前的静态硬编码数据。
当前状态分析
现状
UserInfoCard.tsx中的locationOptions是静态硬编码的(只包含北京、上海、广东的部分区县)FUser.location字段是CharField(max_length=100),存储格式为"省/市/区"字符串- IP定位API (
GetIPDataView) 返回regionName和city,但没有结构化存储 - 无地区数据库表
问题
- 静态数据覆盖范围有限
- 无法与IP定位数据联动
- 用户体验差(需要手动输入)
实现方案
1. 创建 Region 模型
文件: c:\Users\12914\Desktop\vscode\chunyu_project\user\models.py
class Region(models.Model):
"""地区三级联动表 - 省/市/区"""
REGION_LEVEL_CHOICES = [
(1, '省份'),
(2, '城市'),
(3, '区县'),
]
name = models.CharField(max_length=100, verbose_name='地区名称')
code = models.CharField(max_length=20, unique=True, verbose_name='地区编码')
level = models.IntegerField(choices=REGION_LEVEL_CHOICES, verbose_name='级别')
parent = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='children', verbose_name='上级地区')
pinyin = models.CharField(max_length=200, blank=True, verbose_name='拼音')
sort_order = models.IntegerField(default=0, verbose_name='排序')
class Meta:
verbose_name = '地区'
verbose_name_plural = '地区'
ordering = ['level', 'sort_order', 'code']
indexes = [
models.Index(fields=['parent', 'level']),
models.Index(fields=['code']),
]
def __str__(self):
return self.name
2. 创建数据迁移文件
文件: c:\Users\12914\Desktop\vscode\chunyu_project\user\migrations\0010_region.py
迁移文件将:
- 创建 Region 表
- 包含初始数据种子命令的引用
3. 创建数据种子命令
文件: c:\Users\12914\Desktop\vscode\chunyu_project\user\management\commands\seed_regions.py
功能:
- 从JSON文件或内置数据导入全国省市区数据
- 支持增量更新(已存在则跳过)
- 数据来源:国家统计局行政区划代码
4. 创建 API 序列化器
文件: c:\Users\12914\Desktop\vscode\chunyu_project\user\serializers\region_serializers.py
class RegionSerializer(serializers.ModelSerializer):
children = serializers.SerializerMethodField()
class Meta:
model = Region
fields = ['id', 'name', 'code', 'level', 'parent_id', 'children']
def get_children(self, obj):
children = obj.children.all()
if children.exists():
return RegionSerializer(children, many=True).data
return None
5. 创建 API 视图
文件: c:\Users\12914\Desktop\vscode\chunyu_project\user\views\region.py
class RegionListView(APIView):
"""获取地区级联数据"""
permission_classes = [AllowAny]
def get(self, request):
# 只返回省级数据,前端按需加载子级
level = request.GET.get('level', 1)
parent_code = request.GET.get('parent_code')
if parent_code:
regions = Region.objects.filter(parent__code=parent_code)
else:
regions = Region.objects.filter(level=level)
serializer = RegionSerializer(regions, many=True)
return Response({'code': 200, 'data': serializer.data})
6. 注册路由
文件: c:\Users\12914\Desktop\vscode\chunyu_project\user\urls.py
添加:
path('regions/', region_views.RegionListView.as_view(), name='region-list'),
7. 前端 API 请求
文件: c:\Users\12914\Desktop\vscode\chunyu_project_react\src\utils\request.ts
添加:
const region = {
getList: (params?: { level?: number; parent_code?: string }) =>
request.public.get('/user/regions/', params),
};
8. 前端组件改造
文件: C:\Users\12914\Desktop\vscode\chunyu_project_react\src\pages\Profile\components\UserInfoCard.tsx
修改内容:
- 移除静态
locationOptions - 添加
regionOptions状态,从API获取省级数据 - 改造
Cascader组件为动态加载模式 - 使用
loadData属性实现按需加载子级数据 - 保存时将地区编码数组转换为
"省/市/区"格式字符串
// 新的地区级联选择器
<Cascader
options={regionOptions}
loadData={async (selectedOptions) => {
const targetOption = selectedOptions[selectedOptions.length - 1];
targetOption.loading = true;
const children = await region.getList({ parent_code: targetOption.code });
targetOption.children = children;
setRegionOptions([...regionOptions]);
}}
value={location}
onChange={(v) => setLocation(v as string[])}
placeholder="请选择地区"
expandTrigger="hover"
displayRender={(labels) => labels.join(" / ")}
style={{ width: "100%" }}
/>
9. IP定位联动(可选)
登录时自动填充位置:
- 在
_create_login_record中调用IP API - 将
regionName和city存入LoginRecord.location - 尝试匹配 Region 表中的地区编码
文件清单
| 文件 | 操作 | 说明 |
|---|---|---|
chunyu_project/user/models.py |
修改 | 添加 Region 模型 |
chunyu_project/user/migrations/0010_region.py |
新建 | 创建 Region 表迁移 |
chunyu_project/user/management/commands/seed_regions.py |
新建 | 数据种子命令 |
chunyu_project/user/serializers/region_serializers.py |
新建 | 地区序列化器 |
chunyu_project/user/views/region.py |
新建 | 地区API视图 |
chunyu_project/user/urls.py |
修改 | 注册地区路由 |
chunyu_project_react/src/utils/request.ts |
修改 | 添加 region API |
chunyu_project_react/src/pages/Profile/components/UserInfoCard.tsx |
修改 | 使用动态地区数据 |
验证步骤
- 运行
python manage.py makemigrations生成迁移 - 运行
python manage.py migrate执行迁移 - 运行
python manage.py seed_regions导入初始数据 - 访问
/api/user/regions/验证API返回数据 - 打开个人中心,验证地区级联选择器正常工作
- 保存位置后验证数据库存储格式正确
假设与决策
假设
- 前端使用 Ant Design Cascader 组件的
loadData属性实现懒加载 - 初始数据来源于国家统计局行政区划代码
- Region 表数据量约为 3000+ 条(省34个、市300+、区县2800+)
决策
- 使用
parent自关联外键实现树形结构 - API 只返回当前级别数据,前端按需加载子级
- 存储格式保持兼容:
"省/市/区"字符串