|
|
27f9b06d12
|
[F7] 规则链列表/编辑页(DAG 画布整合,决策 5 双入口)
整合 F1 DagCanvas + F2 DagNodePanel + F3 DagPropertyPanel + F8 dag-converter
为完整的规则链 v2 编辑体验。
新增:
- apps/web-antd/src/views/iot/rule/chain/api/rule-chain.ts
- CRUD + enable/disable/deploy/debug/copy API 封装
- apps/web-antd/src/views/iot/rule/chain/stores/rule-chain.ts
- Pinia store: meta + nodes + edges + dirty guard + save/deploy 区分新建/更新
- apps/web-antd/src/views/iot/rule/chain/list.vue
- 4 维筛选(name/subsystem/type/status)+ 分页 20/50/100
- 启用/禁用乐观 UI + 调试切换 Modal 确认(评审 B8)
- WARNING 状态红 Badge + Tooltip 物模型变更原因(评审 E4)
- 双入口 Banner Alert 链到 v1 scene 页(决策 5)
- hasAccessByCodes iot:rule:{update,delete} 权限
- apps/web-antd/src/views/iot/rule/chain/edit.vue
- 三栏布局:DagNodePanel(220) + DagCanvas + DagPropertyPanel(320)
- drop 处理: handleDragOver preventDefault / screenToFlowCoordinate /
newTempId 新节点临时 ID
- nodeClick → selectedNodeStore.setCurrent; paneClick → clear
- onBeforeRouteLeave dirty 拦截 + Modal 确认
- readonly 模式: status=ENABLED 默认只读,可"解锁编辑"
- 发布: save(若 dirty) → deploy API(Modal 前置确认)
- 创建后 router.replace 含真实 ID
- apps/web-antd/src/views/iot/rule/chain/__tests__/rule-chain-store.spec.ts (10 用例)
修改:
- apps/web-antd/src/router/routes/modules/iot.ts
追加 /iot/rule/chain (顶级) + /iot/rule/chain/edit/:id? (hideInMenu detail)
- locales/langs/{zh-CN,en-US}/page.json 补 iot.ruleChain.* 约 80 键
Known Pitfalls 落地:
- drop 兼容: handleDragOver 调 e.preventDefault + dropEffect='copy'(Firefox)
- 临时 ID: newTempId 生成 new_<uuid>,isTempId 识别 INSERT(F8 约定)
- dirty guard: onBeforeRouteLeave 拦截离开
- readonly: status=ENABLED 已发布默认只读,可解锁重新编辑
- 画布双击 nodeDblclick 已透传(DagPropertyPanel 可选聚焦)
决策 5 双入口落实:
- 现有 /iot/rule/scene/* (v1 表单) 完全保留
- 新增 /iot/rule/chain/* (v2 DAG)
- list.vue 顶部 Alert 给用户两入口引导
质检:
- pnpm test:unit src/views/iot/rule/chain → 58/58 通过(累计 F1-F8 测试)
- pnpm lint F7 files → 0 errors
- pnpm check:type chain → 0 errors
后端 B2 API 契约:
- GET /iot/rule-chain/page / get?id=
- POST /iot/rule-chain/create PUT update DELETE delete?id=
- PUT enable / disable / deploy / debug
- POST copy?id=
- GET /iot/subsystem/simple-list (F9 提供,F7 筛选器复用)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-04-23 23:50:23 +08:00 |
|
|
|
7b1a0132d0
|
[F3] DAG 画布属性面板 + 动态表单机制
主会话(Opus)自做,覆盖任务卡 §2 全部文件。
核心文件:
- apps/web-antd/src/views/iot/rule/chain/api/node-metadata.ts
- GET /iot/rule/provider/metadata API 封装
- ProviderMetadata/FormCreateSchema/FormCreateRuleItem 类型定义
- apps/web-antd/src/views/iot/rule/chain/stores/selected-node.ts
- Pinia store: current 节点 + dirty 标记 + metadata 缓存
- metadataByType O(1) 索引 + getSchemaByType/getMetadataByType 查表
- loadMetadata fetcher 可注入(测试 + 后端未就绪 fallback 到 mock)
- apps/web-antd/src/views/iot/rule/chain/components/node-schema/index.ts
- 7 种节点 mock schema (device_state/device_property/timer/expression/
property_set/alarm_trigger/notify)
- buildMockMetadata() 供 fallback 使用
- apps/web-antd/src/views/iot/rule/chain/components/DynamicNodeForm.vue
- @form-create/ant-design-vue 封装,schema 切换自动 resetFields
- "i18n:" 前缀约定递归解析 title/placeholder/options.label/validate.message
- 过滤 undefined 避免 configuration 写入噪声
- apps/web-antd/src/views/iot/rule/chain/components/DagPropertyPanel.vue
- 面板容器:节点名称输入 + DynamicNodeForm
- onMounted 加载 metadata,后端失败 fallback 到 mock(node-schema)
测试:
- selected-node-store.spec.ts 13 用例(store actions / loadMetadata /
getSchemaByType / reset)
- node-schema.spec.ts 12 用例(mock schema 覆盖 5+ 种 / buildMockMetadata 映射)
i18n: iot.dag.panel.* / iot.dag.field.* / iot.dag.option.* / iot.dag.validate.*
zh-CN 与 en-US 同步新增。
Known Pitfalls 落地:
- 评审 B5/H2: 模板变量用 ${data.x}/${alarm.x}/${trigger.x} 占位(notify template
placeholder 字面展示,禁用旧 $[...] 语法)
- form-create schema 切换时调用 fApi.resetFields 防止字段残留
- configuration 过滤 undefined 防止响应式写入 null
- iot:subsystem:simple-list 权限 403 由 @vben/request 拦截器统一处理
- TS 严格模式零 any,configuration 类型 Record<string, unknown>
决策 5 双入口:
- 本任务仅新建 chain/ 目录的组件 + store + API + mock schema
- 路由追加由后续任务处理(项目采用动态菜单机制,静态路由 modules/iot.ts
仅放 hideInMenu 详情页;chain 的 list/edit 菜单由后端 menu 表配置)
- 现有 iot/rule/scene/* 保持不变 ✓
质检:
- pnpm test:unit src/views/iot/rule/chain → 48/48 通过
- pnpm lint apps/web-antd/src/views/iot/rule/chain/ → 0 errors
- pnpm check:type (web-antd) → chain 目录 0 errors(其他模块预存错误与本任务无关)
note: DagPropertyPanel + DynamicNodeForm 的渲染集成测试留给 F7 edit.vue 真实画布
dogfood;本次 unit test 覆盖纯函数与 store。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
2026-04-23 23:12:11 +08:00 |
|
|
|
887e51eaaa
|
[F11] 设备查询页 subsystemId 筛选 + 未归属标签
- apps/web-antd/src/api/iot/device/device/index.ts
- Device 接口加 subsystemId/subsystemName
- 新增 BindSubsystemReqVO + bindDeviceSubsystem (PUT /iot/device/bindSubsystem)
- apps/web-antd/src/views/iot/device/device/index.vue
- 筛选器:子系统下拉 + 未归属哨兵 -1 (→ queryParams.unassigned=true)
- 单设备/批量绑定弹窗,批量 100 台/批
- 行操作 + TableAction 增加"绑定子系统"按钮 (auth: iot:device:update)
- apps/web-antd/src/views/iot/device/device/data.ts
- useGridColumns 追加"所属子系统"列 (slot: subsystem)
- apps/web-antd/src/views/iot/device/device/modules/card-view.vue
- 卡片视图加子系统信息行 + 未归属红标签
- Props 透传 subsystems / searchParams.subsystemId / unassigned
- apps/web-antd/src/views/iot/device/device/__tests__/device-subsystem-filter.spec.ts
- 14 用例: 筛选参数转换 / 未归属标签条件 / 批量分批逻辑
- locales/langs/{zh-CN,en-US}/page.json: iot.device.filter.subsystem.* 12 键同步
- Known Pitfalls 落地: 评审 A2 NULL 醒目红标签 / 哨兵 -1 → unassigned=true /
批量 100 台分批 / iot:device:update 权限 / simple-list 加载静默降级
后端 B11 API 补充契约:
- GET /iot/device/page?unassigned=true (subsystemId IS NULL 过滤)
- PUT /iot/device/bindSubsystem { deviceId, subsystemId: number | null }
- 分页响应 Device 含 subsystemId?: number | null + subsystemName?: string
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-04-23 23:02:01 +08:00 |
|
|
|
ba459aa1d7
|
[F9/F10] 子系统管理 + 告警正交 5 态列表(UI 骨架 + mock)
两任务并行完成,因同动 router/iot.ts 与 locales/page.json 合并 commit。
F9 子系统管理 + 设备批量绑定:
- apps/web-antd/src/api/iot/subsystem/index.ts
- apps/web-antd/src/views/iot/subsystem/{list,form,devices}.vue
- apps/web-antd/src/views/iot/subsystem/__tests__/subsystem-utils.test.ts (18 用例)
- Known Pitfalls: 评审 A6 Redis stats 降级 / A7 403 拦截器 / 批量 100 台 / code regex snake_case
- 后端 B10/B11 API 契约: /iot/subsystem/{page,get,create,update,delete,simple-list,device-count}
+ /iot/device/{batchBindSubsystem,bindSubsystem} + /iot/device/page 加 subsystemId 过滤
F10 告警记录 (评审 C1 正交 5 态):
- apps/web-antd/src/views/iot/alarm/record/{list,detail}.vue
- apps/web-antd/src/views/iot/alarm/record/{alarm-state,api}.ts
- apps/web-antd/src/views/iot/alarm/record/components/{AlarmStateTag,AlarmOperations}.vue
- apps/web-antd/src/views/iot/alarm/record/__tests__/AlarmStateTag.spec.ts (12 用例)
- 5 态: 活跃·未确认(red) / 活跃·已确认(orange) / 已清除·未确认(gold醒目) / 已清除·已确认(green) / 已归档(default)
- Known Pitfalls: C1 5 态必展示 / archived 优先判断 / 30s 轮询 / 严重度颜色映射
- 后端 B12 API 契约: /iot/alarm-record/{page,get,ack,unack,clear,archive,batch-*,history,remark}
共同:
- apps/web-antd/src/router/routes/modules/iot.ts 追加 subsystem + alarm 路由
- locales/langs/{zh-CN,en-US}/page.json 追加 iot.subsystem.* + iot.alarm.*
note: 路由用顶级路径;项目采用动态菜单机制,菜单注册需后端 menu 表配置(运维侧工作),
前端路由可通过 URL 直接访问。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-04-23 22:39:47 +08:00 |
|
|
|
8613641d1d
|
[F2] DAG 节点面板(左侧拖拽区)
- apps/web-antd/src/components/iot-dag/DagNodePanel.vue
- 3 类分组 collapse + 搜索过滤 + 加载/错误/空态
- apps/web-antd/src/components/iot-dag/NodeTypeCard.vue
- HTML5 dragstart 双 MIME:application/x-iot-dag-node + text/plain
- payload: { type: providerType, category: dagNodeType }
- apps/web-antd/src/components/iot-dag/hooks/useNodeCatalog.ts
- Mock 13 节点类型(5 trigger / 3 condition / 5 action)
- fetcher 可注入、permissionFilter 预留
- TODO(F3/F7): 替换为 @vben/request /iot/rule/provider/metadata
- apps/web-antd/src/components/iot-dag/__tests__/useNodeCatalog.spec.ts 17 用例
- apps/web-antd/src/components/iot-dag/index.ts barrel 增加 DagNodePanel/NodeTypeCard
- i18n: iot.dag.panel.* + iot.dag.node.<type>.{name,desc} zh-CN/en-US 同步
- Known Pitfalls 落地: ⚠️ Firefox dataTransfer 双 MIME / 权限过滤预留 / icon Lucide 名
note: Acceptance #3 drop 由 F3/F7 DagCanvasContainer 实现,F2 只负责发送方。
图标通过 data-icon attribute 传递,F3/F7 集成时接入 lucide-vue-next 渲染。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-04-23 22:07:37 +08:00 |
|
|
|
7dbf41b75f
|
[F1] @vue-flow/core 依赖 + DagCanvas 基础组件
- apps/web-antd/src/components/iot-dag/ 7 文件
- DagCanvas.vue / DagCanvasToolbar.vue
- hooks/useDagState.ts(50 步撤销栈 + structuredClone 深拷贝)
- hooks/useDagShortcuts.ts(Del/Ctrl+Z/Ctrl+Y + input guard)
- types.ts(DagNode/DagEdge/DagCanvasProps + 6 emits)
- index.ts barrel + __tests__/DagCanvas.spec.ts(16 用例全绿)
- pnpm-workspace.yaml: catalog 新增 @vue-flow/{core,background,controls,minimap}
- apps/web-antd/package.json: 4 包全部 'catalog:' 引用
- i18n: zh-CN/en-US iot.dag.toolbar.* + iot.dag.canvas.* 同步
- Known Pitfalls 落地: ⚠️ catalog 约束 / 样式 import / TS 严格零 any
note: Acceptance #7 (Storybook/playground) 项目未集成 Storybook,
由 F7 规则链编辑页自然 dogfood。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-04-23 21:32:11 +08:00 |
|
dylanmay
|
5374e64bcb
|
fix: resolve todo
|
2025-11-27 09:55:24 +08:00 |
|
xingyu4j
|
e44f9a0aca
|
Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev
|
2025-11-24 11:47:31 +08:00 |
|
xingyu4j
|
e311cfb8da
|
feat: route add profile
|
2025-11-10 17:51:10 +08:00 |
|
dylanmay
|
5269d4c387
|
fix: code style
|
2025-11-06 15:25:11 +08:00 |
|
xingyu4j
|
18273c42a6
|
feat: add table-action comp
|
2025-05-15 11:49:44 +08:00 |
|
xingyu4j
|
bd02645e26
|
perf: 优化 租户切换
|
2025-05-06 15:44:31 +08:00 |
|
YunaiV
|
ff4a478e83
|
feat: action locales【6dad2152】
|
2025-03-24 21:30:21 +08:00 |
|
Vben
|
0df8c5c02c
|
refactor: reconstruct language files into multi-file structures (#4683)
* refactor: reconstruct language files into multi-file structures
* chore: typo
|
2024-10-19 14:28:21 +08:00 |
|