feat: PLANNING体系首版(00路线图/02任务总表/03执行协议/I-03/I-04/registry42口径)+M1止血交付
This commit is contained in:
@@ -0,0 +1,204 @@
|
||||
# 地区三级联动表实现计划
|
||||
|
||||
## 目标
|
||||
创建省份/城市/地区三级联动数据库表,在个人中心"所在位置"字段中使用级联选择,替代当前的静态硬编码数据。
|
||||
|
||||
## 当前状态分析
|
||||
|
||||
### 现状
|
||||
- `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`
|
||||
|
||||
```python
|
||||
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`
|
||||
|
||||
```python
|
||||
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`
|
||||
|
||||
```python
|
||||
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`
|
||||
|
||||
添加:
|
||||
```python
|
||||
path('regions/', region_views.RegionListView.as_view(), name='region-list'),
|
||||
```
|
||||
|
||||
### 7. 前端 API 请求
|
||||
|
||||
**文件**: `c:\Users\12914\Desktop\vscode\chunyu_project_react\src\utils\request.ts`
|
||||
|
||||
添加:
|
||||
```typescript
|
||||
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`
|
||||
|
||||
修改内容:
|
||||
1. 移除静态 `locationOptions`
|
||||
2. 添加 `regionOptions` 状态,从API获取省级数据
|
||||
3. 改造 `Cascader` 组件为动态加载模式
|
||||
4. 使用 `loadData` 属性实现按需加载子级数据
|
||||
5. 保存时将地区编码数组转换为 `"省/市/区"` 格式字符串
|
||||
|
||||
```typescript
|
||||
// 新的地区级联选择器
|
||||
<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` | 修改 | 使用动态地区数据 |
|
||||
|
||||
## 验证步骤
|
||||
|
||||
1. 运行 `python manage.py makemigrations` 生成迁移
|
||||
2. 运行 `python manage.py migrate` 执行迁移
|
||||
3. 运行 `python manage.py seed_regions` 导入初始数据
|
||||
4. 访问 `/api/user/regions/` 验证API返回数据
|
||||
5. 打开个人中心,验证地区级联选择器正常工作
|
||||
6. 保存位置后验证数据库存储格式正确
|
||||
|
||||
## 假设与决策
|
||||
|
||||
### 假设
|
||||
- 前端使用 Ant Design Cascader 组件的 `loadData` 属性实现懒加载
|
||||
- 初始数据来源于国家统计局行政区划代码
|
||||
- Region 表数据量约为 3000+ 条(省34个、市300+、区县2800+)
|
||||
|
||||
### 决策
|
||||
- 使用 `parent` 自关联外键实现树形结构
|
||||
- API 只返回当前级别数据,前端按需加载子级
|
||||
- 存储格式保持兼容:`"省/市/区"` 字符串
|
||||
Reference in New Issue
Block a user