diff --git a/.trae/documents/空间拓扑与用电架构-实现计划.md b/.trae/documents/空间拓扑与用电架构-实现计划.md
new file mode 100644
index 0000000..a81c867
--- /dev/null
+++ b/.trae/documents/空间拓扑与用电架构-实现计划.md
@@ -0,0 +1,121 @@
+# 空间拓扑与用电架构 · 实现计划
+
+## Context(为什么做)
+
+现有「空间拓扑与用电架构」页面(`client/src/views/customer/topology/index.vue`)只实现了「客户 → 建筑 → 房间」三层树 + 建筑/房间增删改,**"用电架构"部分完全缺失**:数据库与后端没有任何承载"用电回路"的表或接口,页面上也没有配电拓扑可视化。
+
+设计稿 `系统设计/UI/空间拓扑与用电架构/code.html` 定义了三栏工业级布局(左空间树 / 中配电单线拓扑流 / 右节点属性面板)。本次目标:在新表 + 新后端接口的基础上,把该页面补齐为三栏结构,让"空间"与"用电回路"打通。
+
+## 已确认的边界(用户决策)
+
+| 项 | 结论 |
+| ------ | ---------------------------------------- |
+| 目标形态 | 按设计稿做完整三栏(左树 / 中单线图 / 右属性面板) |
+| 左树层级 | 客户 → 建筑 → **楼层** → 房间 → **回路**(不要"配电箱"层) |
+| 楼层 | 由房间/回路按 `floor_no` 分组生成的**虚拟层**,不建表 |
+| 中间图上层 | 进线柜 / 变压器:**只读展示**,不落库、不做 CRUD |
+| 中间图末端 | **回路卡片**(一个回路一张卡) |
+| 实时电气量 | 电压/电流/功率/温度等**一律占位**(显示 `—`),不造假数据、不接采集 |
+| ICC 映射 | **不做**(设计稿里的网关层、ICC 设备码、告警阈值联动均不实现) |
+| 覆盖端 | 仅 Web 管理端 `client/` |
+| 数据来源 | 新建表 + 后端 CRUD 接口 |
+
+## 数据模型(新增 1 张表)
+
+`customer_circuit`(客户用电回路)—— 参照 `customer_room` 的建表风格(`database/db_init.sql#L364-L387`):
+
+| 字段 | 类型 | 说明 |
+| -------------- | --------------------- | ----------------------------------- |
+| id | BIGSERIAL PK |
|
+| customer\_id | BIGINT NOT NULL | → `customer(id)` |
+| building\_id | BIGINT NOT NULL | → `customer_building(id)` |
+| room\_id | BIGINT NULL | → `customer_room(id)`,允许挂建筑/楼层而不挂房间 |
+| floor\_no | VARCHAR(16) | 楼层(冗余,便于按楼层分组) |
+| circuit\_code | VARCHAR(64) NOT NULL | 回路编号,全局唯一 |
+| circuit\_name | VARCHAR(128) NOT NULL | 回路名称 |
+| loop\_spec | VARCHAR(128) | 回路规格(如 `三相380V / 铜芯4×25+1×16`) |
+| load\_type | VARCHAR(32) | 负荷类型(照明/动力/空调/插座…) |
+| rated\_voltage | VARCHAR(32) | 额定电压(配置值) |
+| rated\_current | VARCHAR(32) | 额定电流(配置值) |
+| rated\_power | NUMERIC(10,2) | 额定功率 kW(配置值) |
+| status | SMALLINT DEFAULT 1 | 1 正常 / 0 停用 |
+| remark | TEXT | 备注 |
+| sort\_order | INT DEFAULT 0 | 排序 |
+| created\_at | TIMESTAMP |
|
+
+索引:`customer_id`、`building_id`、`room_id`、唯一键 `circuit_code`。
+
+> 命名跟随现有习惯:表 `customer_circuit`、entity `CustomerCircuit`、路由 `/api/customer-circuits`、权限码 `customer:circuit`(与 `customer:building` / `customer:room` 同级)。
+
+## 后端改动(Midway.js + sequelize-typescript)
+
+参照样板:`customer_room` 全链路。
+
+1. **迁移**:`database/migrations/20261011_customer_circuit.sql`(幂等 `CREATE TABLE IF NOT EXISTS`),并在 `database/db_init.sql` 同步该表定义。
+2. **Entity**:`server/src/entity/customer_circuit.entity.ts`(照抄 `customer_building.entity.ts` 的 `@Table/@Column/@BelongsTo/@CreatedAt` 写法)。
+3. **DTO**:`server/src/dto/customer_circuit.dto.ts`(Query/Create/Update,class-validator 风格照抄 `customer_room.dto.ts`)。
+4. **Service**:`server/src/service/customer_circuit.service.ts`
+
+ * `list` / `detail` / `create` / `update` / `delete`(照抄 `customer_room.service.ts`,统一返回 `{ code, message, data }`)。
+
+ * `topology(customerId)`:**一次请求**返回整棵「建筑 → 楼层 → 房间 → 回路」结构,避免前端 N+1(现有页面是逐建筑拉房间)。返回形如:
+
+ ```json
+ { "buildings": [ { "id":1, "buildingName":"…", "floors":[ { "floorNo":"2", "rooms":[ { "id":9, "roomName":"…", "circuits":[…] } ], "circuits":[…] } ] } ] }
+ ```
+5. **Controller**:`server/src/controller/customer_circuit.controller.ts`(`@Controller("/api/customer-circuits")`,含 `GET /topology`)。
+6. **权限注册**:`server/src/middleware/rbac.middleware.ts` 的 `PERMISSION_MAP` 新增 `"/api/customer-circuits": "customer:circuit"`。
+7. **菜单/权限种子**:在 `database/db_seed.sql` 复用现有「空间拓扑与用电架构」菜单(id=32, `/customer/topology`, 权限码 `customer:topology`),并**参照** **`customer:room`** **的登记方式**新增按钮级权限 `customer:circuit`。
+
+## 前端改动(Vue3 + Ant Design Vue)
+
+1. **新增 API 封装** `client/src/api/customerCircuit.ts`:照抄 `customerRoom.ts` 风格(`getList/getDetail/create/update/delete/getTopology` + 类型定义 + 常量:负荷类型选项)。
+2. **重写** `client/src/views/customer/topology/index.vue` 为三栏:
+
+ * **顶部**:面包屑 + 客户切换器 + 指标条 + 「新增回路」按钮。指标只展示**可由现有数据算出**的项(已接入建筑数、监测用电回路数、空间建模覆盖率 = 有回路的房间数/房间总数);无数据源的项(变电所/配电室、ICC 边缘网关)**不展示**。
+
+ * **左栏(≈280px)**:`a-tree` 渲染「客户 → 建筑 → 楼层 → 房间 → 回路」;节点徽章显示回路数;节点悬停操作:建筑/房间沿用现有增删改(`customerBuildingApi` / `customerRoomApi`),回路可新增/编辑/删除。
+
+ * **中栏**:单线图。上层为**只读示意**(10kV 进线柜 → 变压器 → 低压母线,按客户/建筑信息填充文案)、下层为**回路卡片网格**(编号/名称/所属房间/规格/额定参数,点击选中;实时电气量显示 `—`)。用 div 网格 + 内联 SVG 母线渲染(与设计稿一致),**不引入新依赖**(echarts 已在依赖中但不必要)。
+
+ * **右栏(≈320px)**:选中节点的属性面板(回路基本信息可编辑并保存;房间/建筑节点展示只读属性)。
+
+ * 样式沿用页面既有基线:背景 `#eef3f8`、主色 `#0054cd`、卡片圆角 `12px`、等宽字体用 `Consolas/Menlo`。
+3. **路由/权限**:沿用现有 `customer/topology` 路由与 `customer:topology` 菜单,**不新增页面路由**。
+
+## 不做(明确排除)
+
+* ICC 网关节点、ICC 设备码映射、告警阈值联动、下发同步
+
+* 实时电气量采集与展示(一律 `—`)
+
+* 「从 ICC 同步拓扑」「CAD/BIM 导入」「导出高清拓扑图」按钮 → 先不实现(可留占位,若留则点击提示"暂未开放")
+
+* 「空间三维映射」视图切换
+
+* 配电箱/配电柜层
+
+* Flutter 管理端
+
+## 假设(如不认可请指出)
+
+1. 中间图的上层(进线柜/变压器)**纯前端静态示意**,文案按所选客户/建筑动态填充,不落库。
+2. `circuit_code` **全局唯一**(与设备台账 `equip_code` 一致);若你希望"客户内唯一",需改唯一约束。
+3. 回路可以**不挂房间**(只挂建筑+楼层),以适应公共区域回路。
+4. 顶部指标只做能算出来的 3 项,其余留白而非显示假数据。
+
+## 验证方式
+
+1. 执行迁移:`database/migrations/20261011_customer_circuit.sql`(以及 db\_seed 的权限登记),确认表与权限数据落库。
+2. 后端:启动 `server/`,用带 token 的请求验证 `GET /api/customer-circuits/topology?customerId=1`、以及 create/update/delete;无 token 应 401、无权限应 403。
+3. 前端:`client/` 执行 `npm run check`(vue-tsc)通过;`npm run dev` 后进入「客户管理 → 空间拓扑与用电架构」:
+
+ * 选客户 → 左树按「建筑 → 楼层 → 房间 → 回路」正确展开;
+
+ * 新增/编辑/删除回路后树与中间卡片同步刷新;
+
+ * 点击回路卡片 → 右栏属性正确回填、可保存;
+
+ * 中间图上层的进线柜/变压器为只读示意。
+4. 回归:确认原有建筑/房间增删改仍可用(房间功能未被破坏)。
+
diff --git a/client/src/api/customerCircuit.ts b/client/src/api/customerCircuit.ts
new file mode 100644
index 0000000..96851d7
--- /dev/null
+++ b/client/src/api/customerCircuit.ts
@@ -0,0 +1,132 @@
+import request from '@/utils/request';
+
+export interface CustomerCircuitInfo {
+ id: number;
+ customerId: number;
+ buildingId: number;
+ roomId?: number | null;
+ floorNo?: string;
+ circuitCode: string;
+ circuitName: string;
+ loopSpec?: string;
+ loadType?: string;
+ ratedVoltage?: string;
+ ratedCurrent?: string;
+ ratedPower?: number | string | null;
+ status: number;
+ remark?: string;
+ sortOrder?: number;
+ createdAt?: string;
+}
+
+export interface CustomerCircuitQuery {
+ page?: number;
+ pageSize?: number;
+ keyword?: string;
+ customerId?: number;
+ buildingId?: number;
+ roomId?: number;
+ floorNo?: string;
+}
+
+export interface CustomerCircuitListResult {
+ list: CustomerCircuitInfo[];
+ total: number;
+ page: number;
+ pageSize: number;
+}
+
+export interface CustomerCircuitCreateParams {
+ circuitCode: string;
+ customerId: number;
+ buildingId: number;
+ circuitName: string;
+ roomId?: number;
+ floorNo?: string;
+ loopSpec?: string;
+ loadType?: string;
+ ratedVoltage?: string;
+ ratedCurrent?: string;
+ ratedPower?: number;
+ status?: number;
+ remark?: string;
+ sortOrder?: number;
+}
+
+export interface CustomerCircuitUpdateParams {
+ circuitCode?: string;
+ customerId?: number;
+ buildingId?: number;
+ circuitName?: string;
+ roomId?: number;
+ floorNo?: string;
+ loopSpec?: string;
+ loadType?: string;
+ ratedVoltage?: string;
+ ratedCurrent?: string;
+ ratedPower?: number;
+ status?: number;
+ remark?: string;
+ sortOrder?: number;
+}
+
+// ==================== 空间拓扑(一次返回整棵树) ====================
+export interface TopologyRoom {
+ id: number;
+ roomCode: string;
+ roomName: string;
+ buildingId: number;
+ floorNo?: string;
+ roomPurpose?: string;
+ deptId?: number;
+ circuits: CustomerCircuitInfo[];
+}
+
+export interface TopologyFloor {
+ floorNo: string;
+ rooms: TopologyRoom[];
+ circuits: CustomerCircuitInfo[];
+}
+
+export interface TopologyBuilding {
+ id: number;
+ customerId: number;
+ buildingName: string;
+ purpose?: string;
+ basicInfo?: string;
+ floorCount?: number;
+ floorDownCount?: number;
+ floors: TopologyFloor[];
+}
+
+export interface CustomerTopologyResult {
+ buildings: TopologyBuilding[];
+}
+
+// ==================== 常量 ====================
+export const CIRCUIT_LOAD_TYPES = ['照明', '动力', '空调', '插座', '电梯', '消防', '其他'];
+
+export const CIRCUIT_STATUS_OPTIONS = [
+ { label: '正常', value: 1 },
+ { label: '停用', value: 0 },
+];
+
+export const customerCircuitApi = {
+ getList: (params: CustomerCircuitQuery) =>
+ request.get('/customer-circuits', { params }),
+
+ getTopology: (customerId: number) =>
+ request.get('/customer-circuits/topology', { params: { customerId } }),
+
+ getDetail: (id: number) =>
+ request.get(`/customer-circuits/${id}`),
+
+ create: (data: CustomerCircuitCreateParams) =>
+ request.post('/customer-circuits', data),
+
+ update: (id: number, data: CustomerCircuitUpdateParams) =>
+ request.put(`/customer-circuits/${id}`, data),
+
+ delete: (id: number) =>
+ request.delete(`/customer-circuits/${id}`),
+};
\ No newline at end of file
diff --git a/client/src/views/customer/topology/index.vue b/client/src/views/customer/topology/index.vue
index 6d96d1d..4d7605a 100644
--- a/client/src/views/customer/topology/index.vue
+++ b/client/src/views/customer/topology/index.vue
@@ -1,6 +1,6 @@
-
+
-
-
-
-