232 lines
6.3 KiB
Markdown
232 lines
6.3 KiB
Markdown
# 网格管理页面设计文档
|
||
|
||
## 1. 页面概述
|
||
|
||
网格管理页面允许系统管理员创建、查看、编辑和删除地理网格。每个网格代表一个责任区域,可以关联到特定的主管或网格员。此功能是任务分配和地理数据可视化的基础。
|
||
|
||
## 2. 页面布局
|
||
|
||

|
||
|
||
### 2.1 布局结构
|
||
|
||
页面由两部分组成:
|
||
- **左侧**: 网格列表,以表格形式展示所有已定义的网格及其基本信息(名称、负责人等)。
|
||
- **右侧**: 交互式地图,用于可视化展示所选网格的地理边界。当创建或编辑网格时,地图将进入绘图模式。
|
||
|
||
## 3. 组件结构
|
||
|
||
```vue
|
||
<template>
|
||
<div class="grid-management-page">
|
||
<el-page-header title="网格管理" content="定义地理责任区" />
|
||
|
||
<el-row :gutter="20" class="page-container">
|
||
<!-- 左侧网格列表与操作 -->
|
||
<el-col :span="10" :xs="24">
|
||
<el-card>
|
||
<template #header>
|
||
<div class="table-toolbar">
|
||
<span>网格列表</span>
|
||
<el-button type="primary" icon="Plus" @click="handleCreate">新建网格</el-button>
|
||
</div>
|
||
</template>
|
||
<el-table :data="grids" v-loading="loading" @row-click="handleRowClick" highlight-current-row>
|
||
<el-table-column prop="name" label="网格名称" />
|
||
<el-table-column prop="manager" label="负责人" />
|
||
<el-table-column label="操作" width="120">
|
||
<template #default="{ row }">
|
||
<el-button link type="primary" @click.stop="handleEdit(row)">编辑</el-button>
|
||
<el-popconfirm title="确定删除此网格?" @confirm="handleDelete(row)">
|
||
<template #reference>
|
||
<el-button link type="danger" @click.stop>删除</el-button>
|
||
</template>
|
||
</el-popconfirm>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
</el-card>
|
||
</el-col>
|
||
|
||
<!-- 右侧地图显示 -->
|
||
<el-col :span="14" :xs="24">
|
||
<el-card>
|
||
<template #header>地图预览</template>
|
||
<div ref="mapContainer" class="map-container"></div>
|
||
</el-card>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<!-- 网格编辑/创建对话框 -->
|
||
<GridFormDialog v-model="dialogVisible" :grid-id="selectedGridId" @success="onFormSuccess" />
|
||
</div>
|
||
</template>
|
||
```
|
||
|
||
## 4. 数据结构
|
||
|
||
```typescript
|
||
// 网格列表项
|
||
interface GridListItem {
|
||
id: number;
|
||
name: string;
|
||
manager?: string;
|
||
// GeoJSON 字符串
|
||
geometry: string;
|
||
}
|
||
|
||
// 网格表单数据 (用于对话框)
|
||
interface GridFormData {
|
||
id?: number;
|
||
name: string;
|
||
managerId?: number;
|
||
// GeoJSON 字符串
|
||
geometry: string;
|
||
}
|
||
```
|
||
|
||
## 5. 状态管理
|
||
|
||
```typescript
|
||
// 地图实例
|
||
let mapInstance = null;
|
||
let currentGridLayer = null;
|
||
|
||
// 组件内状态
|
||
const mapContainer = ref<HTMLElement>();
|
||
const dialogVisible = ref(false);
|
||
const selectedGridId = ref<number | null>(null);
|
||
|
||
// 全局状态 (Pinia Store)
|
||
const gridStore = useGridStore();
|
||
const { grids, loading } = storeToRefs(gridStore);
|
||
```
|
||
|
||
## 6. 交互逻辑
|
||
|
||
### 6.1 地图与列表交互
|
||
|
||
```typescript
|
||
onMounted(() => {
|
||
gridStore.fetchGrids();
|
||
initializeMap();
|
||
});
|
||
|
||
const initializeMap = () => {
|
||
// ... 地图初始化逻辑
|
||
};
|
||
|
||
// 点击表格行,在地图上高亮显示对应网格
|
||
const handleRowClick = (row: GridListItem) => {
|
||
if (currentGridLayer) {
|
||
mapInstance.removeLayer(currentGridLayer);
|
||
}
|
||
if (row.geometry) {
|
||
const geoJson = JSON.parse(row.geometry);
|
||
currentGridLayer = L.geoJSON(geoJson).addTo(mapInstance);
|
||
mapInstance.fitBounds(currentGridLayer.getBounds());
|
||
}
|
||
};
|
||
```
|
||
|
||
### 6.2 CRUD 操作
|
||
|
||
```typescript
|
||
const handleCreate = () => {
|
||
selectedGridId.value = null;
|
||
dialogVisible.value = true;
|
||
};
|
||
|
||
const handleEdit = (grid: GridListItem) => {
|
||
selectedGridId.value = grid.id;
|
||
dialogVisible.value = true;
|
||
};
|
||
|
||
const handleDelete = async (grid: GridListItem) => {
|
||
await gridStore.deleteGrid(grid.id);
|
||
ElMessage.success('网格删除成功');
|
||
gridStore.fetchGrids();
|
||
};
|
||
|
||
const onFormSuccess = () => {
|
||
dialogVisible.value = false;
|
||
gridStore.fetchGrids();
|
||
};
|
||
```
|
||
|
||
## 7. API 调用
|
||
|
||
```typescript
|
||
// api/grid.ts
|
||
export const gridApi = {
|
||
getGrids: () => apiClient.get('/grid'),
|
||
getGridById: (id: number) => apiClient.get(`/grid/${id}`),
|
||
createGrid: (data: GridFormData) => apiClient.post('/grid', data),
|
||
updateGrid: (id: number, data: GridFormData) => apiClient.put(`/grid/${id}`, data),
|
||
deleteGrid: (id: number) => apiClient.delete(`/grid/${id}`),
|
||
};
|
||
|
||
// stores/grid.ts
|
||
export const useGridStore = defineStore('grid', {
|
||
state: () => ({
|
||
grids: [] as GridListItem[],
|
||
loading: false,
|
||
}),
|
||
actions: {
|
||
async fetchGrids() {
|
||
this.loading = true;
|
||
try {
|
||
const { data } = await gridApi.getGrids();
|
||
this.grids = data;
|
||
} finally {
|
||
this.loading = false;
|
||
}
|
||
},
|
||
// ... create, update, delete actions
|
||
}
|
||
});
|
||
```
|
||
|
||
## 8. 样式设计
|
||
|
||
```scss
|
||
.grid-management-page {
|
||
padding: 24px;
|
||
|
||
.page-container {
|
||
margin-top: 24px;
|
||
}
|
||
|
||
.table-toolbar {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.map-container {
|
||
height: 600px;
|
||
}
|
||
}
|
||
```
|
||
|
||
## 9. 关联组件
|
||
|
||
### `GridFormDialog.vue`
|
||
|
||
这是本页面的核心复杂组件,用于创建和编辑网格。
|
||
- **Props**: `modelValue`, `gridId`
|
||
- **内部组件**:
|
||
- 一个表单,包含网格名称、负责人(下拉选择)等字段。
|
||
- 一个内嵌的 Leaflet 地图,用于绘制和编辑网格边界。
|
||
- **功能**:
|
||
- **地图绘制**: 提供多边形(Polygon)绘制工具。用户可以在地图上绘制闭合区域来定义网格边界。
|
||
- **数据加载**: 在编辑模式下,根据 `gridId` 加载网格数据,并在表单和内嵌地图上显示。
|
||
- **数据保存**: 用户完成绘制和表单填写后,点击保存。组件将绘制的多边形转换为 GeoJSON 字符串,连同表单数据一起,调用 store 的 action 进行保存。
|
||
|
||
## 10. 测试用例
|
||
|
||
- **集成测试**:
|
||
- 测试能否成功创建一个新网格,包括在地图上绘制边界和填写信息。
|
||
- 测试点击列表中的网格,地图上是否正确显示其边界。
|
||
- 测试编辑功能,包括修改网格信息和在地图上重新编辑边界。
|
||
- 测试删除网格功能。 |