| 385 | const [grouppedMenus, setGrouppedMenus] = useState<TGrouppedMenus>({}); |
| 386 | |
| 387 | const updateScriptMenuList = (scriptMenuList: ScriptMenuEntry[]) => { |
| 388 | setScriptMenuList(scriptMenuList); |
| 389 | // 因为 scriptMenuList 的修改只在这处。 |
| 390 | // 直接在这里呼叫 setGrouppedMenus, 不需要 useEffect |
| 391 | setGrouppedMenus((prev) => { |
| 392 | // 依 groupKey 进行聚合:将同语义(mainframe/subframe)命令合并为单一分组以供 UI 呈现。 |
| 393 | const ret = {} as TGrouppedMenus; |
| 394 | let changed = false; |
| 395 | let retLen = 0; |
| 396 | for (const { uuid, menus, menuUpdated: m } of scriptMenuList) { |
| 397 | retLen++; |
| 398 | const menuUpdated = m || 0; |
| 399 | if (prev[uuid]?.menuUpdated === menuUpdated) { |
| 400 | ret[uuid] = prev[uuid]; |
| 401 | continue; // Skip if unchanged |
| 402 | } |
| 403 | |
| 404 | const resultMap = new Map<string, ScriptMenuItem[]>(); |
| 405 | for (const menu of menus) { |
| 406 | if (menu.options?.mSeparator) continue; // popup 不显示分隔线 |
| 407 | const groupKey = menu.groupKey.split(",")[0]; // popup 显示不区分二级菜单或三级菜单 |
| 408 | let m = resultMap.get(groupKey); |
| 409 | if (!m) resultMap.set(groupKey, (m = [])); |
| 410 | m.push(menu); |
| 411 | } |
| 412 | |
| 413 | const result = []; |
| 414 | for (const [groupKey, arr] of resultMap) { |
| 415 | result.push({ uuid, groupKey, menus: arr } as GroupScriptMenuItem); |
| 416 | } |
| 417 | |
| 418 | // 输出以 uuid 分组存放;不依赖 list 的迭代顺序以避免不稳定渲染。 |
| 419 | ret[uuid] = { group: result, menuUpdated }; |
| 420 | changed = true; |
| 421 | } |
| 422 | ret.__length__ = retLen; |
| 423 | if (!changed && ret.__length__ !== prev.__length__) changed = true; |
| 424 | |
| 425 | // 若无引用变更则维持原物件以降低重渲染 |
| 426 | return changed ? ret : prev; |
| 427 | }); |
| 428 | }; |
| 429 | |
| 430 | const url = useMemo(() => { |
| 431 | let url: URL; |