zlwu před 6 měsíci
rodič
revize
a14a6c18e7

+ 167 - 166
graphify-out/GRAPH_REPORT.md

@@ -1,11 +1,11 @@
 # Graph Report - sqxp_副本2  (2026-05-02)
 
 ## Corpus Check
-- 703 files · ~644,114 words
+- 704 files · ~644,277 words
 - Verdict: corpus is large enough that graph structure adds value.
 
 ## Summary
-- 5010 nodes · 27155 edges · 82 communities detected
+- 5014 nodes · 27159 edges · 81 communities detected
 - Extraction: 19% EXTRACTED · 81% INFERRED · 0% AMBIGUOUS · INFERRED: 21960 edges (avg confidence: 0.54)
 - Token cost: 0 input · 0 output
 
@@ -43,47 +43,47 @@
 - [[_COMMUNITY_Community 30|Community 30]]
 - [[_COMMUNITY_Community 31|Community 31]]
 - [[_COMMUNITY_Community 32|Community 32]]
-- [[_COMMUNITY_Community 33|Community 33]]
-- [[_COMMUNITY_Community 34|Community 34]]
 - [[_COMMUNITY_Community 35|Community 35]]
+- [[_COMMUNITY_Community 36|Community 36]]
+- [[_COMMUNITY_Community 37|Community 37]]
 - [[_COMMUNITY_Community 38|Community 38]]
-- [[_COMMUNITY_Community 39|Community 39]]
 - [[_COMMUNITY_Community 40|Community 40]]
-- [[_COMMUNITY_Community 41|Community 41]]
+- [[_COMMUNITY_Community 42|Community 42]]
 - [[_COMMUNITY_Community 43|Community 43]]
+- [[_COMMUNITY_Community 44|Community 44]]
 - [[_COMMUNITY_Community 45|Community 45]]
 - [[_COMMUNITY_Community 46|Community 46]]
-- [[_COMMUNITY_Community 47|Community 47]]
 - [[_COMMUNITY_Community 48|Community 48]]
 - [[_COMMUNITY_Community 49|Community 49]]
+- [[_COMMUNITY_Community 50|Community 50]]
 - [[_COMMUNITY_Community 51|Community 51]]
 - [[_COMMUNITY_Community 52|Community 52]]
-- [[_COMMUNITY_Community 53|Community 53]]
-- [[_COMMUNITY_Community 54|Community 54]]
-- [[_COMMUNITY_Community 55|Community 55]]
+- [[_COMMUNITY_Community 56|Community 56]]
+- [[_COMMUNITY_Community 59|Community 59]]
 - [[_COMMUNITY_Community 61|Community 61]]
+- [[_COMMUNITY_Community 62|Community 62]]
 - [[_COMMUNITY_Community 63|Community 63]]
 - [[_COMMUNITY_Community 64|Community 64]]
 - [[_COMMUNITY_Community 65|Community 65]]
 - [[_COMMUNITY_Community 66|Community 66]]
-- [[_COMMUNITY_Community 67|Community 67]]
 - [[_COMMUNITY_Community 68|Community 68]]
 - [[_COMMUNITY_Community 69|Community 69]]
+- [[_COMMUNITY_Community 70|Community 70]]
 - [[_COMMUNITY_Community 71|Community 71]]
-- [[_COMMUNITY_Community 72|Community 72]]
-- [[_COMMUNITY_Community 76|Community 76]]
+- [[_COMMUNITY_Community 75|Community 75]]
+- [[_COMMUNITY_Community 78|Community 78]]
 - [[_COMMUNITY_Community 79|Community 79]]
-- [[_COMMUNITY_Community 80|Community 80]]
-- [[_COMMUNITY_Community 82|Community 82]]
-- [[_COMMUNITY_Community 94|Community 94]]
-- [[_COMMUNITY_Community 104|Community 104]]
-- [[_COMMUNITY_Community 106|Community 106]]
+- [[_COMMUNITY_Community 81|Community 81]]
+- [[_COMMUNITY_Community 93|Community 93]]
+- [[_COMMUNITY_Community 103|Community 103]]
+- [[_COMMUNITY_Community 105|Community 105]]
+- [[_COMMUNITY_Community 157|Community 157]]
 - [[_COMMUNITY_Community 158|Community 158]]
 - [[_COMMUNITY_Community 159|Community 159]]
 - [[_COMMUNITY_Community 160|Community 160]]
 - [[_COMMUNITY_Community 161|Community 161]]
 - [[_COMMUNITY_Community 162|Community 162]]
-- [[_COMMUNITY_Community 163|Community 163]]
+- [[_COMMUNITY_Community 430|Community 430]]
 - [[_COMMUNITY_Community 431|Community 431]]
 - [[_COMMUNITY_Community 432|Community 432]]
 - [[_COMMUNITY_Community 433|Community 433]]
@@ -91,7 +91,6 @@
 - [[_COMMUNITY_Community 435|Community 435]]
 - [[_COMMUNITY_Community 436|Community 436]]
 - [[_COMMUNITY_Community 437|Community 437]]
-- [[_COMMUNITY_Community 438|Community 438]]
 
 ## God Nodes (most connected - your core abstractions)
 1. `BizPriceListItem` - 814 edges
@@ -112,10 +111,10 @@
   web-admin/src/views/auth/register/index.vue → sqxp-wx/pages/client/address/edit.vue
 - `number()` --calls--> `inputMaxlength()`  [INFERRED]
   web-admin/src/views/system/inquiry-quotation/index.vue → sqxp-wx/uni_modules/uni-easyinput/components/uni-easyinput/uni-easyinput.vue
-- `number()` --calls--> `mounted()`  [INFERRED]
-  web-admin/src/views/system/inquiry-quotation/index.vue → sqxp-wx/uni_modules/uni-table/components/uni-tr/uni-tr.vue
-- `number()` --calls--> `handler()`  [INFERRED]
-  web-admin/src/views/system/inquiry-quotation/index.vue → sqxp-wx/uni_modules/uni-pagination/components/uni-pagination/uni-pagination.vue
+- `number()` --calls--> `onPickerChange()`  [INFERRED]
+  web-admin/src/views/system/inquiry-quotation/index.vue → sqxp-wx/pages/client/invoice/invoice.vue
+- `showMenu()` --calls--> `nextTick()`  [INFERRED]
+  web-admin/src/views/widgets/context-menu/index.vue → sqxp-wx/wxcomponents/vant/common/utils.js
 
 ## Communities
 
@@ -125,402 +124,404 @@ Nodes (724): 获取管理员的区域过滤条件      对于区域管理员,
 
 ### Community 1 - "Community 1"
 Cohesion: 0.01
-Nodes (186): fullRegion(), if(), if(), FireworkSystem, mapKeys(), VantComponent(), HTMLParser(), parseAttrs() (+178 more)
+Nodes (171): calendar(), memo(), record(), handleSelect(), onMenuShow(), showMenu(), onClear(), onInput() (+163 more)
 
 ### Community 2 - "Community 2"
 Cohesion: 0.01
-Nodes (205): handleSubmit(), loadDetail(), onRegionMultiConfirm(), regionLabel(), syncMultiFromForm(), validate(), fetchActiveRegions(), fetchAssignableMenuCodes() (+197 more)
+Nodes (166): fullRegion(), if(), if(), FireworkSystem, mapKeys(), VantComponent(), HTMLParser(), parseAttrs() (+158 more)
 
 ### Community 3 - "Community 3"
 Cohesion: 0.01
-Nodes (172): calendar(), memo(), record(), handleSelect(), onMenuShow(), showMenu(), onClear(), onInput() (+164 more)
+Nodes (168): fetchActiveRegions(), fetchAssignableMenuCodes(), fetchCategoryList(), fetchCategoryParameters(), fetchCategoryTree(), fetchCreateUnit(), fetchNotDeletedRegions(), fetchProductBannerList() (+160 more)
 
 ### Community 4 - "Community 4"
 Cohesion: 0.01
-Nodes (244): InvoiceCreateRequest, MessageCreateRequest, 获取商品的评价列表(仅显示已审核通过的评价), AdjustResponse, Config, CreateAdjustRequest, 批量添加轮播图(请求体为 JSON 数组;兼容密文信封与 ApiCryptoMiddleware 未改写 body 的场景), 批量添加轮播图(请求体为 JSON 数组;兼容密文信封与 ApiCryptoMiddleware 未改写 body 的场景) (+236 more)
+Nodes (255): InvoiceCreateRequest, MessageCreateRequest, 获取商品的评价列表(仅显示已审核通过的评价), AdjustResponse, Config, CreateAdjustRequest, 批量添加轮播图(请求体为 JSON 数组;兼容密文信封与 ApiCryptoMiddleware 未改写 body 的场景), 批量添加轮播图(请求体为 JSON 数组;兼容密文信封与 ApiCryptoMiddleware 未改写 body 的场景) (+247 more)
 
 ### Community 5 - "Community 5"
 Cohesion: 0.03
-Nodes (242): _aggregate_client_settlement_history(), _aggregate_supplier_settlement_history(), approve_quotation(), assign_deliverer_to_order(), assign_delivery_alias(), batch_create_settlements(), _batch_scope_region_ids_by_user(), _batch_supplier_totals_for_orders() (+234 more)
+Nodes (243): _aggregate_client_settlement_history(), _aggregate_supplier_settlement_history(), _apply_region_minutes_to_data(), approve_quotation(), assign_deliverer_to_order(), assign_delivery_alias(), batch_create_settlements(), _batch_scope_region_ids_by_user() (+235 more)
 
 ### Community 6 - "Community 6"
-Cohesion: 0.02
-Nodes (72): fetchGetMenuCodes(), fetchGetUserInfo(), fetchLogin(), fetchGetMenuList(), fetchGetRoleList(), fetchGetUserList(), reload(), nextTick() (+64 more)
+Cohesion: 0.01
+Nodes (146): handleSubmit(), loadDetail(), onRegionMultiConfirm(), regionLabel(), syncMultiFromForm(), validate(), 小程序客户端扩展接口:购物车、商品分类、仪表盘、发票、下单、支付, 从叶子分类 id 向上解析(含缓存):返回 (根分类, 叶子分类)。 (+138 more)
 
 ### Community 7 - "Community 7"
-Cohesion: 0.03
-Nodes (75): BizMessage, update_banner(), create_comment(), 获取中国地图GeoJSON数据     返回保存在系统配置中的地图数据, Base, ApiRequestLogCRUD, InquiryCRUD, 取消发布询价单(published -> draft) (+67 more)
+Cohesion: 0.02
+Nodes (64): fetchGetMenuCodes(), fetchGetUserInfo(), fetchLogin(), fetchGetMenuList(), fetchGetRoleList(), fetchGetUserList(), reload(), addUnit() (+56 more)
 
 ### Community 8 - "Community 8"
-Cohesion: 0.02
-Nodes (63): askAbortDownload(), checkLocalStoragePackage(), closeUpdate(), compare(), deleteSavedFile(), downLoadComplete(), installPackage(), onLoad() (+55 more)
+Cohesion: 0.03
+Nodes (84): get_category_parameters(), update_category_parameters(), 获取中国地图GeoJSON数据     返回保存在系统配置中的地图数据, Base, ApiRequestLogCRUD, HistoryCRUD, CategoryParameterCRUD, CategoryParameterHisCRUD (+76 more)
 
 ### Community 9 - "Community 9"
-Cohesion: 0.03
-Nodes (64): create_price_version(), get_dashboard_statistics(), get_today_orders_by_region(), list_units(), delivery_complete(), get_deliver_profile(), get_delivery_task(), get_expiring_tasks() (+56 more)
+Cohesion: 0.02
+Nodes (98): get_today_orders_by_client(), require_admin(), _supplier_pickup_address_block(), fetchAdjustableOrders(), fetchClientSettlementHistorySummary(), fetchClientSettlementOrderList(), fetchClientSettlementSheetDetail(), fetchClientSettlementSheetList() (+90 more)
 
 ### Community 10 - "Community 10"
-Cohesion: 0.02
-Nodes (84): _build_order_delivery_timeline(), _delivery_timeline_type(), get_today_orders_by_client(), _log_entry_in_delivery_timeline(), require_admin(), _status_log_timeline_description(), fetchAdjustableOrders(), fetchClientSettlementHistorySummary() (+76 more)
+Cohesion: 0.03
+Nodes (64): create_price_version(), get_dashboard_statistics(), get_password_history_list(), get_today_orders_by_region(), list_categories(), list_price_versions(), list_regions(), list_units() (+56 more)
 
 ### Community 11 - "Community 11"
 Cohesion: 0.03
 Nodes (54): handleFormClose(), handleClose(), initForm(), resetFormModel(), _Blur(), created(), init(), inputContentStyle() (+46 more)
 
 ### Community 12 - "Community 12"
-Cohesion: 0.03
-Nodes (93): 小程序客户端扩展接口:购物车、商品分类、仪表盘、发票、下单、支付, 从叶子分类 id 向上解析(含缓存):返回 (根分类, 叶子分类)。, 可申请开票的已完成订单(简化:已完成且尚无发票), 可申请开票的已完成订单(简化:已完成且尚无发票), 可申请开票的已完成订单(简化:已完成且尚无发票), 商品对应的一级(根)分类,供购物车行展示与序列化。, 从叶子分类 id 向上解析(含缓存):返回 (根分类, 叶子分类)。, 购物车:先一级分类 sort_order,再商品名、商品 id(稳定)。 (+85 more)
+Cohesion: 0.04
+Nodes (79): 处理预警请求体(与前端 JSON / axios 自动转 body 对齐), 处理预警(兼容明文 JSON、密文信封 enc/edata,以及 { data: {...} } 包裹), 处理预警(与 ApiCryptoMiddleware / 运营端加密协议一致:Request.json + parse_admin_request_json), 处理预警(与 ApiCryptoMiddleware / 运营端加密协议一致:Request.json + parse_admin_request_json), # TODO: 发送微信模板消息提醒配送员, # TODO: 发送微信模板消息提醒配送员, # TODO: 发送微信模板消息提醒配送员, # TODO: 发送微信模板消息提醒配送员 (+71 more)
 
 ### Community 13 - "Community 13"
-Cohesion: 0.04
-Nodes (73): DeliveryWarningCRUD, 处理预警请求体(与前端 JSON / axios 自动转 body 对齐), 处理预警(兼容明文 JSON、密文信封 enc/edata,以及 { data: {...} } 包裹), 处理预警(与 ApiCryptoMiddleware / 运营端加密协议一致:Request.json + parse_admin_request_json), 处理预警(与 ApiCryptoMiddleware / 运营端加密协议一致:Request.json + parse_admin_request_json), # TODO: 发送微信模板消息提醒配送员, # TODO: 发送微信模板消息提醒配送员, # TODO: 发送微信模板消息提醒配送员 (+65 more)
+Cohesion: 0.05
+Nodes (32): adjust_order(), cancel_order_admin(), confirm_quotation_supplier(), InvoiceCRUD, client_urge_order(), apply_invoice(), _upsert_wechat_pay_pending(), _upsert_wechat_pay_record() (+24 more)
 
 ### Community 14 - "Community 14"
-Cohesion: 0.05
-Nodes (29): adjust_order(), cancel_order_admin(), confirm_quotation_supplier(), _replace_user_region_scopes(), InvoiceCRUD, client_urge_order(), apply_invoice(), _upsert_wechat_pay_pending() (+21 more)
+Cohesion: 0.04
+Nodes (55): delete_attachment(), get_business_attachments(), get_business_types(), upload_attachment(), assign_delivery_task(), assign_delivery_task_batch(), cancel_delivery_task(), _category_path_display() (+47 more)
 
 ### Community 15 - "Community 15"
 Cohesion: 0.05
 Nodes (77): 批量获取供应商取货地址,避免列表接口逐条查询。, 一级/二级分类用 / 连接(与订单详情一致)。, 价格清单行 price 为面向客户的最终单价;pricing_coefficient 为调价系数(%),供货单价 = price / (1 + 系数/100)。, (price_list_id, product_id, unit_id) -> BizPriceListItem, 供应商默认取货地址;无默认时取第一条 pickup 类型地址。, 获取供应商配送任务详情(订单仅返回客户订单号;商品行含主图与规格;单价由价格清单行 price(客户最终单价)与 pricing_coefficient 反推供, 获取供应商配送任务详情(订单仅返回客户订单号;商品行含主图与规格;单价由价格清单行 price(客户最终单价)与 pricing_coefficient 反推供, 获取供应商配送任务详情(订单仅返回客户订单号;商品行含主图与规格;单价由价格清单行 price(客户最终单价)与 pricing_coefficient 反推供 (+69 more)
 
 ### Community 16 - "Community 16"
-Cohesion: 0.06
-Nodes (61): change_password(), get_client_ip(), _avatar_url_field(), get_current_user(), get_mini_current_user(), logout(), mini_bind_wechat(), mini_change_password() (+53 more)
+Cohesion: 0.05
+Nodes (64): _avatar_url_field(), get_current_user(), get_mini_current_user(), get_web_auth_menu_codes(), logout(), mini_bind_wechat(), mini_change_password(), mini_code_openid() (+56 more)
 
 ### Community 17 - "Community 17"
-Cohesion: 0.05
-Nodes (15): fetchGetLatestUpgradeLog(), if(), getDefaultLanguage(), emitUrls(), removeItem(), revokeLocalPreview(), checkStorageCompatibility(), StorageConfig (+7 more)
+Cohesion: 0.03
+Nodes (43): askAbortDownload(), checkLocalStoragePackage(), closeUpdate(), compare(), deleteSavedFile(), downLoadComplete(), installPackage(), onLoad() (+35 more)
 
 ### Community 18 - "Community 18"
 Cohesion: 0.06
-Nodes (37): _apply_region_minutes_to_data(), create_inquiry(), create_region(), create_unit(), _minutes_value_from_request(), _resolve_admin_payload_with_crypto_fallback(), update_client_settlement_sheet(), update_inquiry() (+29 more)
+Nodes (55): _address_to_client_dict(), apply_client_refund(), client_cancel_order(), client_confirm_receive(), client_direct_cancel_order(), create_address(), create_order(), _deliverer_contact_phone() (+47 more)
 
 ### Community 19 - "Community 19"
 Cohesion: 0.05
-Nodes (26): get_settlement_statistics(), get_product_review_stats(), like_review(), list_product_reviews(), require_client_or_supplier(), approve_review(), get_review_statistics(), list_reviews() (+18 more)
+Nodes (15): fetchGetLatestUpgradeLog(), if(), getDefaultLanguage(), emitUrls(), removeItem(), revokeLocalPreview(), checkStorageCompatibility(), StorageConfig (+7 more)
 
 ### Community 20 - "Community 20"
 Cohesion: 0.05
-Nodes (35): delete_attachment(), get_business_attachments(), get_business_types(), list_attachments(), upload_attachment(), confirm_deliver(), confirm_pickup(), get_deliverer_efficiency_statistics() (+27 more)
+Nodes (48): _resolve_request_payload(), create_parameter(), delete_parameter(), list_parameters(), require_admin(), _resolve_payload_with_crypto_fallback(), update_parameter(), 兼容明文/密文请求,返回业务 payload。失败时返回 error_response。 (+40 more)
 
 ### Community 21 - "Community 21"
-Cohesion: 0.1
-Nodes (33): create_order(), add_cart(), _cart_item_to_dict(), _cart_list_dicts(), _cart_resolve_root_category(), _cart_row_sort_key(), _cart_rows_after_sync(), cart_summary() (+25 more)
+Cohesion: 0.05
+Nodes (22): change(), created(), getDataList(), getSelectedValue(), handler(), loadData(), mixinDatacomResData(), modelValue() (+14 more)
 
 ### Community 22 - "Community 22"
-Cohesion: 0.09
-Nodes (32): post_user_menu_scopes(), _update_user_menu_scopes(), 兼容明文/密文请求,返回业务 payload。失败时返回 error_response。, _resolve_payload_with_crypto_fallback(), BaseHTTPMiddleware, ApiCryptoError, _b64url_decode(), _b64url_encode() (+24 more)
-
-### Community 23 - "Community 23"
-Cohesion: 0.08
-Nodes (27): handle_warning(), _preview_text(), get_client_product_detail(), get_client_product_detail_readonly(), get_deliverer_product_detail(), 配送员查看商品详情:轮播、基础信息、参数、富文本、详情媒体(与供应商端一致,不依赖客户价目表/区域绑定), get_supplier_product_detail(), create_banners() (+19 more)
-
-### Community 24 - "Community 24"
 Cohesion: 0.07
-Nodes (16): addUnit(), getSystemInfoSync(), isDef(), isNumber(), setSizeWithUnit(), setSizeWithUnit(), setTextSizeWithUnit(), mounted() (+8 more)
+Nodes (30): DeliveryWarningCRUD, get_warning_detail(), get_warning_list(), handle_warning(), _preview_text(), _to_response(), create_review(), get_product_review_stats() (+22 more)
 
-### Community 25 - "Community 25"
+### Community 23 - "Community 23"
 Cohesion: 0.09
-Nodes (19): get_category_parameters(), update_category_parameters(), 兼容明文/密文请求,返回业务 payload。失败时返回 error_response。, CategoryParameterCRUD, CategoryParameterHisCRUD, ProductParameterCRUD, ProductParameterHisCRUD, BasCategoryParameter (+11 more)
+Nodes (27): base64ToBytes(), bytesToBase64(), decryptJson(), deriveKey(), encryptJson(), fromBase64Url(), hexToBytes(), sha256Hex() (+19 more)
 
-### Community 26 - "Community 26"
-Cohesion: 0.1
-Nodes (20): _supplier_pickup_address_block(), _batch_supplier_pickup_address_map(), cancel_delivery_task(), _category_path_display(), get_delivery_task(), list_delivery_tasks(), _numeric_to_float_optional(), _price_list_item_map() (+12 more)
-
-### Community 27 - "Community 27"
+### Community 24 - "Community 24"
 Cohesion: 0.11
 Nodes (12): activated(), bottom(), center(), close(), deactivated(), destroyed(), left(), onTap() (+4 more)
 
-### Community 28 - "Community 28"
+### Community 25 - "Community 25"
 Cohesion: 0.18
 Nodes (21): destroyed(), getDirection(), getDom(), isPC(), mousedown(), mouseleave(), mousemove(), mouseup() (+13 more)
 
-### Community 29 - "Community 29"
+### Community 26 - "Community 26"
 Cohesion: 0.13
 Nodes (13): change(), clearVal(), created(), debounce(), emit(), formatItemName(), getCache(), handler() (+5 more)
 
-### Community 30 - "Community 30"
+### Community 27 - "Community 27"
 Cohesion: 0.12
 Nodes (15): addCopyButton(), highlightCode(), insertLineNumbers(), isBlockProcessed(), markBlockAsProcessed(), processBlock(), setupHighlightDirective(), setupRippleDirective() (+7 more)
 
-### Community 31 - "Community 31"
+### Community 28 - "Community 28"
 Cohesion: 0.17
 Nodes (12): BusinessException, InvalidStatusTransitionException, log_delivery_operation(), log_operation(), log_order_operation(), log_settlement_operation(), log_user_operation(), OrderLockedException (+4 more)
 
-### Community 32 - "Community 32"
+### Community 29 - "Community 29"
+Cohesion: 0.16
+Nodes (6): submit_quotation(), InquiryCRUD, InquiryItemCRUD, 取消发布询价单(published -> draft), 获取区域当前发布中的询价单(最多1条,业务上应保证互斥), BizInquiryHis
+
+### Community 30 - "Community 30"
 Cohesion: 0.16
 Nodes (10): useTable(), useTableImpl(), getColumnChecks(), useTableColumns(), createErrorHandler(), createSmartDebounce(), defaultResponseAdapter(), extractPagination() (+2 more)
 
-### Community 33 - "Community 33"
+### Community 31 - "Community 31"
 Cohesion: 0.2
 Nodes (14): defaultAvatarPathByRole(), normalizeAvatarUrlForMini(), resolveUserAvatarUrl(), appendQueryParam(), fallbackVersionFromManifest(), getStaticAssetsVersion(), hasSignedQuery(), hasVersionParam() (+6 more)
 
-### Community 34 - "Community 34"
-Cohesion: 0.14
-Nodes (11): Exception, notify_price_list_updated(), WebSocket:区域价格清单更新推送(客户端实时刷新), 小程序端 WebSocket 入口:/ws/mini, 在 FastAPI app 上注册 websocket 路由。, 按 region_id 管理 websocket 连接并广播。, 鉴权并接入房间。返回 current_user dict。, 向 region 房间广播消息。返回尝试发送的连接数。 (+3 more)
-
-### Community 35 - "Community 35"
+### Community 32 - "Community 32"
 Cohesion: 0.28
 Nodes (14): getDirection(), getDisabledType(), getDom(), move(), moveDirection(), openState(), range(), resetTouchStatus() (+6 more)
 
-### Community 38 - "Community 38"
+### Community 35 - "Community 35"
 Cohesion: 0.17
 Nodes (2): clean(), onInput()
 
-### Community 39 - "Community 39"
+### Community 36 - "Community 36"
 Cohesion: 0.24
 Nodes (4): getVal(), leftIconWidth(), navbarHeight(), rightIconWidth()
 
-### Community 40 - "Community 40"
+### Community 37 - "Community 37"
 Cohesion: 0.4
 Nodes (9): backup_table(), count_affected(), get_text_columns(), main(), make_backup_table_name(), parse_args(), quote_ident(), 生成备份表名,控制在 MySQL 64 字符限制内。 (+1 more)
 
-### Community 41 - "Community 41"
+### Community 38 - "Community 38"
 Cohesion: 0.24
 Nodes (3): _calcValue(), _getDecimalScale(), _onBlur()
 
-### Community 43 - "Community 43"
+### Community 40 - "Community 40"
 Cohesion: 0.27
 Nodes (4): dataValue(), emit(), onChange(), setOpen()
 
-### Community 45 - "Community 45"
+### Community 42 - "Community 42"
 Cohesion: 0.25
 Nodes (2): borderStyle(), value2px()
 
-### Community 46 - "Community 46"
+### Community 43 - "Community 43"
 Cohesion: 0.29
 Nodes (3): BaseModel, get_active(), SoftDeleteModel
 
-### Community 47 - "Community 47"
+### Community 44 - "Community 44"
 Cohesion: 0.39
 Nodes (6): isIgnorableScriptError(), normalizeErrorMessage(), registerPromiseErrorHandler(), registerResourceErrorHandler(), scriptErrorHandler(), setupErrorHandle()
 
-### Community 48 - "Community 48"
+### Community 45 - "Community 45"
 Cohesion: 0.29
 Nodes (2): handleTouchEnd(), setTransition()
 
-### Community 49 - "Community 49"
+### Community 46 - "Community 46"
 Cohesion: 0.36
 Nodes (1): TableHeightCalculator
 
-### Community 51 - "Community 51"
+### Community 48 - "Community 48"
 Cohesion: 0.36
 Nodes (1): RouteValidator
 
-### Community 52 - "Community 52"
+### Community 49 - "Community 49"
 Cohesion: 0.39
 Nodes (5): chooseAll(), chooseAndUploadFile(), chooseImage(), chooseVideo(), uploadFiles()
 
-### Community 53 - "Community 53"
+### Community 50 - "Community 50"
 Cohesion: 0.33
 Nodes (2): logApiCryptoBootInfo(), maskSecret()
 
-### Community 54 - "Community 54"
+### Community 51 - "Community 51"
 Cohesion: 0.38
 Nodes (4): fetchApiRequestLogList(), handleReset(), handleSearch(), loadData()
 
-### Community 55 - "Community 55"
+### Community 52 - "Community 52"
 Cohesion: 0.38
 Nodes (3): _change(), close(), open()
 
-### Community 61 - "Community 61"
+### Community 56 - "Community 56"
+Cohesion: 0.4
+Nodes (1): CategoryCRUD
+
+### Community 59 - "Community 59"
 Cohesion: 0.33
 Nodes (1): BridgeError
 
-### Community 63 - "Community 63"
+### Community 61 - "Community 61"
 Cohesion: 0.4
 Nodes (2): created(), getTable()
 
-### Community 64 - "Community 64"
+### Community 62 - "Community 62"
 Cohesion: 0.47
 Nodes (3): get_file_data(), get_file_ext(), get_file_info()
 
-### Community 65 - "Community 65"
+### Community 63 - "Community 63"
 Cohesion: 0.4
 Nodes (2): created(), getForm()
 
-### Community 66 - "Community 66"
+### Community 64 - "Community 64"
 Cohesion: 0.47
 Nodes (4): onDial(), onTextClick(), dialPhone(), normalizeDialNumber()
 
-### Community 67 - "Community 67"
+### Community 65 - "Community 65"
 Cohesion: 0.4
 Nodes (4): 运营端角色守卫:admin / super_admin 可访问。, 接口菜单权限守卫。super_admin 直接放行。, require_admin_or_super_admin(), require_menu_code()
 
-### Community 68 - "Community 68"
-Cohesion: 0.6
-Nodes (4): derive_three_status_from_legacy(), 阶段A兼容:从旧 status 推导三字段。     约束:旧 status 本身混合了支付/配送/订单生命周期,因此只能尽力推断;     若业务侧已有更精确, ThreeStatus, _to_decimal()
-
-### Community 69 - "Community 69"
+### Community 66 - "Community 66"
 Cohesion: 0.5
 Nodes (2): focusInput(), handleKeydown()
 
-### Community 71 - "Community 71"
+### Community 68 - "Community 68"
 Cohesion: 0.5
 Nodes (3): BaseSettings, Config, Settings
 
-### Community 72 - "Community 72"
+### Community 69 - "Community 69"
+Cohesion: 0.5
+Nodes (2): 申请退款          Args:             out_trade_no: 商户订单号             total_amount, 生成签名 V3 (SHA256 with RSA)
+
+### Community 70 - "Community 70"
+Cohesion: 0.67
+Nodes (2): if(), logSidebarMenu()
+
+### Community 71 - "Community 71"
 Cohesion: 0.5
 Nodes (1): collectIds()
 
-### Community 76 - "Community 76"
+### Community 75 - "Community 75"
 Cohesion: 0.83
 Nodes (3): classes(), isTrue(), onClick()
 
-### Community 79 - "Community 79"
+### Community 78 - "Community 78"
 Cohesion: 0.67
 Nodes (2): created(), getParent()
 
-### Community 80 - "Community 80"
+### Community 79 - "Community 79"
 Cohesion: 1.0
 Nodes (2): getSettingDefaults(), resetToDefaults()
 
-### Community 82 - "Community 82"
+### Community 81 - "Community 81"
 Cohesion: 1.0
 Nodes (2): defaultAvatarPathByRole(), resolveUserAvatarUrl()
 
-### Community 94 - "Community 94"
+### Community 93 - "Community 93"
 Cohesion: 0.67
 Nodes (1): updateUseModal()
 
-### Community 104 - "Community 104"
+### Community 103 - "Community 103"
 Cohesion: 1.0
 Nodes (2): buildMiniEncryptedLoginBody(), fetchLoginPublicKey()
 
-### Community 106 - "Community 106"
+### Community 105 - "Community 105"
 Cohesion: 0.67
 Nodes (1): return()
 
-### Community 158 - "Community 158"
+### Community 157 - "Community 157"
 Cohesion: 1.0
 Nodes (1): 发送订单状态通知                  Args:             openid: 用户openid             ord
 
-### Community 159 - "Community 159"
+### Community 158 - "Community 158"
 Cohesion: 1.0
 Nodes (1): 发送支付成功通知                  Args:             openid: 用户openid             ord
 
-### Community 160 - "Community 160"
+### Community 159 - "Community 159"
 Cohesion: 1.0
 Nodes (1): 发送配送通知                  Args:             openid: 配送员openid             task
 
-### Community 161 - "Community 161"
+### Community 160 - "Community 160"
 Cohesion: 1.0
 Nodes (1): 发送配送时效预警                  Args:             openid: 配送员openid             ta
 
-### Community 162 - "Community 162"
+### Community 161 - "Community 161"
 Cohesion: 1.0
 Nodes (1): 发送退款结果通知                  Args:             openid: 用户openid             ord
 
-### Community 163 - "Community 163"
+### Community 162 - "Community 162"
 Cohesion: 1.0
 Nodes (1): 发送催单通知给配送员                  Args:             deliver_openid: 配送员openid
 
-### Community 431 - "Community 431"
+### Community 430 - "Community 430"
 Cohesion: 1.0
 Nodes (1): 读取 JSON body;dict 时走 resolve_request_payload(明文 / 密文 / data 包装)。      Returns:
 
-### Community 432 - "Community 432"
+### Community 431 - "Community 431"
 Cohesion: 1.0
 Nodes (1): 按 region_id 管理 websocket 连接并广播。
 
-### Community 433 - "Community 433"
+### Community 432 - "Community 432"
 Cohesion: 1.0
 Nodes (1): 鉴权并接入房间。返回 current_user dict。
 
-### Community 434 - "Community 434"
+### Community 433 - "Community 433"
 Cohesion: 1.0
 Nodes (1): 向 region 房间广播消息。返回尝试发送的连接数。
 
-### Community 435 - "Community 435"
+### Community 434 - "Community 434"
 Cohesion: 1.0
 Nodes (1): 小程序端 WebSocket 入口:/ws/mini
 
-### Community 436 - "Community 436"
+### Community 435 - "Community 435"
 Cohesion: 1.0
 Nodes (1): 在 FastAPI app 上注册 websocket 路由。
 
-### Community 437 - "Community 437"
+### Community 436 - "Community 436"
 Cohesion: 1.0
 Nodes (1): Base64 密文 -> JSON 对象(须含 username、password、ts 毫秒时间戳)。
 
-### Community 438 - "Community 438"
+### Community 437 - "Community 437"
 Cohesion: 1.0
 Nodes (1): 从请求体解析用户名、密码。     返回 (username, password, error_message);error_message 非空时表示失败。
 
 ## Knowledge Gaps
 - **84 isolated node(s):** `关闭超时未支付的线上订单(超时分钟数来自系统设置 auto_cancel_minutes)。`, `初始化定时任务      Args:         get_db_session: 数据库会话获取函数(从 app.core.database 导入)`, `登录凭据 RSA 解密(与前端 JSEncrypt PKCS#1 v1.5 兼容)。`, `Base64 密文 -> JSON 对象(须含 username、password、ts 毫秒时间戳)。`, `从请求体解析用户名、密码。     返回 (username, password, error_message);error_message 非空时表示失败。` (+79 more)
   These have ≤1 connection - possible missing edges or undocumented components.
-- **Thin community `Community 38`** (13 nodes): `uni-combox.vue`, `clean()`, `data()`, `default()`, `filterCandidates()`, `filterCandidatesLength()`, `handler()`, `labelStyle()`, `onBlur()`, `onFocus()`, `onInput()`, `onSelectorClick()`, `toggleSelector()`
+- **Thin community `Community 35`** (13 nodes): `uni-combox.vue`, `clean()`, `data()`, `default()`, `filterCandidates()`, `filterCandidatesLength()`, `handler()`, `labelStyle()`, `onBlur()`, `onFocus()`, `onInput()`, `onSelectorClick()`, `toggleSelector()`
+  Too small to be a meaningful cluster - may be noise or needs more connections extracted.
+- **Thin community `Community 42`** (9 nodes): `upload-file.vue`, `borderLineStyle()`, `borderStyle()`, `choose()`, `default()`, `delFile()`, `styles()`, `uploadFiles()`, `value2px()`
+  Too small to be a meaningful cluster - may be noise or needs more connections extracted.
+- **Thin community `Community 45`** (8 nodes): `cleanupEventListeners()`, `handleTouchEnd()`, `handleTouchMove()`, `handleTouchStart()`, `handleWheelScroll()`, `setTransition()`, `setupEventListeners()`, `index.vue`
+  Too small to be a meaningful cluster - may be noise or needs more connections extracted.
+- **Thin community `Community 46`** (8 nodes): `TableHeightCalculator`, `.calculate()`, `.calculateOffset()`, `.calculatePaginationOffset()`, `.constructor()`, `.getHeaderHeight()`, `useTableHeight()`, `useTableHeight.ts`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 45`** (9 nodes): `upload-file.vue`, `borderLineStyle()`, `borderStyle()`, `choose()`, `default()`, `delFile()`, `styles()`, `uploadFiles()`, `value2px()`
+- **Thin community `Community 48`** (8 nodes): `RouteValidator`, `.checkComponents()`, `.checkDuplicates()`, `.checkNestedIndexComponent()`, `.logLayoutError()`, `.resolvePath()`, `.validate()`, `RouteValidator.ts`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 48`** (8 nodes): `cleanupEventListeners()`, `handleTouchEnd()`, `handleTouchMove()`, `handleTouchStart()`, `handleWheelScroll()`, `setTransition()`, `setupEventListeners()`, `index.vue`
+- **Thin community `Community 50`** (7 nodes): `refreshButtonAutoIcons()`, `refreshPanelTitleIcons()`, `logApiCryptoBootInfo()`, `maskSecret()`, `App.vue`, `main.ts`, `console.ts`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 49`** (8 nodes): `TableHeightCalculator`, `.calculate()`, `.calculateOffset()`, `.calculatePaginationOffset()`, `.constructor()`, `.getHeaderHeight()`, `useTableHeight()`, `useTableHeight.ts`
+- **Thin community `Community 56`** (6 nodes): `category.py`, `CategoryCRUD`, `._build_tree()`, `.get_by_name()`, `.get_children()`, `.get_tree()`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 51`** (8 nodes): `RouteValidator`, `.checkComponents()`, `.checkDuplicates()`, `.checkNestedIndexComponent()`, `.logLayoutError()`, `.resolvePath()`, `.validate()`, `RouteValidator.ts`
+- **Thin community `Community 59`** (6 nodes): `bridge-error.js`, `BridgeError`, `.code()`, `.constructor()`, `.errCode()`, `.errMsg()`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 53`** (7 nodes): `refreshButtonAutoIcons()`, `refreshPanelTitleIcons()`, `logApiCryptoBootInfo()`, `maskSecret()`, `App.vue`, `main.ts`, `console.ts`
+- **Thin community `Community 61`** (6 nodes): `uni-thead.vue`, `checkboxSelected()`, `created()`, `data()`, `getTable()`, `init()`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 61`** (6 nodes): `bridge-error.js`, `BridgeError`, `.code()`, `.constructor()`, `.errCode()`, `.errMsg()`
+- **Thin community `Community 63`** (6 nodes): `uni-group.vue`, `created()`, `data()`, `getForm()`, `onClick()`, `title()`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 63`** (6 nodes): `uni-thead.vue`, `checkboxSelected()`, `created()`, `data()`, `getTable()`, `init()`
+- **Thin community `Community 66`** (5 nodes): `focusInput()`, `handleKeydown()`, `if()`, `search()`, `index.vue`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 65`** (6 nodes): `uni-group.vue`, `created()`, `data()`, `getForm()`, `onClick()`, `title()`
+- **Thin community `Community 69`** (4 nodes): `申请退款          Args:             out_trade_no: 商户订单号             total_amount`, `生成签名 V3 (SHA256 with RSA)`, `._generate_sign_v3()`, `.refund()`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 69`** (5 nodes): `focusInput()`, `handleKeydown()`, `if()`, `search()`, `index.vue`
+- **Thin community `Community 70`** (4 nodes): `if()`, `logSidebarMenu()`, `index.vue`, `SidebarSubmenu.vue`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 72`** (4 nodes): `catch()`, `collectIds()`, `handleCategoryTableSelectionChange()`, `index.vue`
+- **Thin community `Community 71`** (4 nodes): `catch()`, `collectIds()`, `handleCategoryTableSelectionChange()`, `index.vue`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 79`** (4 nodes): `popup.js`, `created()`, `data()`, `getParent()`
+- **Thin community `Community 78`** (4 nodes): `popup.js`, `created()`, `data()`, `getParent()`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 80`** (3 nodes): `getSettingDefaults()`, `resetToDefaults()`, `setting.ts`
+- **Thin community `Community 79`** (3 nodes): `getSettingDefaults()`, `resetToDefaults()`, `setting.ts`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 82`** (3 nodes): `defaultAvatarPathByRole()`, `resolveUserAvatarUrl()`, `user-avatar.ts`
+- **Thin community `Community 81`** (3 nodes): `defaultAvatarPathByRole()`, `resolveUserAvatarUrl()`, `user-avatar.ts`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 94`** (3 nodes): `check-update.js`, `check-update.ts`, `updateUseModal()`
+- **Thin community `Community 93`** (3 nodes): `check-update.js`, `check-update.ts`, `updateUseModal()`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 104`** (3 nodes): `loginRsa.js`, `buildMiniEncryptedLoginBody()`, `fetchLoginPublicKey()`
+- **Thin community `Community 103`** (3 nodes): `loginRsa.js`, `buildMiniEncryptedLoginBody()`, `fetchLoginPublicKey()`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 106`** (3 nodes): `return()`, `detail.vue`, `detail.vue`
+- **Thin community `Community 105`** (3 nodes): `return()`, `detail.vue`, `detail.vue`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 158`** (1 nodes): `发送订单状态通知                  Args:             openid: 用户openid             ord`
+- **Thin community `Community 157`** (1 nodes): `发送订单状态通知                  Args:             openid: 用户openid             ord`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 159`** (1 nodes): `发送支付成功通知                  Args:             openid: 用户openid             ord`
+- **Thin community `Community 158`** (1 nodes): `发送支付成功通知                  Args:             openid: 用户openid             ord`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 160`** (1 nodes): `发送配送通知                  Args:             openid: 配送员openid             task`
+- **Thin community `Community 159`** (1 nodes): `发送配送通知                  Args:             openid: 配送员openid             task`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 161`** (1 nodes): `发送配送时效预警                  Args:             openid: 配送员openid             ta`
+- **Thin community `Community 160`** (1 nodes): `发送配送时效预警                  Args:             openid: 配送员openid             ta`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 162`** (1 nodes): `发送退款结果通知                  Args:             openid: 用户openid             ord`
+- **Thin community `Community 161`** (1 nodes): `发送退款结果通知                  Args:             openid: 用户openid             ord`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 163`** (1 nodes): `发送催单通知给配送员                  Args:             deliver_openid: 配送员openid`
+- **Thin community `Community 162`** (1 nodes): `发送催单通知给配送员                  Args:             deliver_openid: 配送员openid`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 431`** (1 nodes): `读取 JSON body;dict 时走 resolve_request_payload(明文 / 密文 / data 包装)。      Returns:`
+- **Thin community `Community 430`** (1 nodes): `读取 JSON body;dict 时走 resolve_request_payload(明文 / 密文 / data 包装)。      Returns:`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 432`** (1 nodes): `按 region_id 管理 websocket 连接并广播。`
+- **Thin community `Community 431`** (1 nodes): `按 region_id 管理 websocket 连接并广播。`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 433`** (1 nodes): `鉴权并接入房间。返回 current_user dict。`
+- **Thin community `Community 432`** (1 nodes): `鉴权并接入房间。返回 current_user dict。`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 434`** (1 nodes): `向 region 房间广播消息。返回尝试发送的连接数。`
+- **Thin community `Community 433`** (1 nodes): `向 region 房间广播消息。返回尝试发送的连接数。`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 435`** (1 nodes): `小程序端 WebSocket 入口:/ws/mini`
+- **Thin community `Community 434`** (1 nodes): `小程序端 WebSocket 入口:/ws/mini`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 436`** (1 nodes): `在 FastAPI app 上注册 websocket 路由。`
+- **Thin community `Community 435`** (1 nodes): `在 FastAPI app 上注册 websocket 路由。`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 437`** (1 nodes): `Base64 密文 -> JSON 对象(须含 username、password、ts 毫秒时间戳)。`
+- **Thin community `Community 436`** (1 nodes): `Base64 密文 -> JSON 对象(须含 username、password、ts 毫秒时间戳)。`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 438`** (1 nodes): `从请求体解析用户名、密码。     返回 (username, password, error_message);error_message 非空时表示失败。`
+- **Thin community `Community 437`** (1 nodes): `从请求体解析用户名、密码。     返回 (username, password, error_message);error_message 非空时表示失败。`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
 
 ## Suggested Questions
 _Questions this graph is uniquely positioned to answer:_
 
-- **Why does `push()` connect `Community 1` to `Community 2`, `Community 3`, `Community 8`, `Community 11`, `Community 14`, `Community 17`, `Community 18`?**
-  _High betweenness centrality (0.114) - this node is a cross-community bridge._
-- **Why does `number()` connect `Community 2` to `Community 1`, `Community 3`, `Community 8`, `Community 11`, `Community 18`, `Community 24`?**
-  _High betweenness centrality (0.062) - this node is a cross-community bridge._
-- **Why does `BizPriceListItem` connect `Community 0` to `Community 7`, `Community 9`, `Community 12`, `Community 14`, `Community 15`?**
+- **Why does `push()` connect `Community 2` to `Community 1`, `Community 3`, `Community 11`, `Community 13`, `Community 17`, `Community 19`, `Community 21`?**
+  _High betweenness centrality (0.120) - this node is a cross-community bridge._
+- **Why does `number()` connect `Community 3` to `Community 1`, `Community 2`, `Community 5`, `Community 6`, `Community 9`, `Community 11`, `Community 17`, `Community 21`, `Community 23`?**
+  _High betweenness centrality (0.049) - this node is a cross-community bridge._
+- **Why does `CRUDBase` connect `Community 12` to `Community 4`, `Community 8`, `Community 9`, `Community 10`, `Community 13`, `Community 14`, `Community 16`, `Community 20`, `Community 22`, `Community 56`, `Community 29`?**
   _High betweenness centrality (0.039) - this node is a cross-community bridge._
 - **Are the 812 inferred relationships involving `BizPriceListItem` (e.g. with `PriceListCRUD` and `PriceListItemCRUD`) actually correct?**
   _`BizPriceListItem` has 812 INFERRED edges - model-reasoned connections that need verification._

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 0
graphify-out/cache/ast/05449d8fd5e8b059dbc68b84244458b756af842ae1cbdcf2ec145983417f570f.json


+ 1 - 0
graphify-out/cache/ast/bcc7c4fca15ccc618545b48dde81d357d62806e6c44e2e91fffd534126595678.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_components_core_layouts_art_menus_art_sidebar_menu_widget_sidebarsubmenu_vue", "label": "SidebarSubmenu.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/components/core/layouts/art-menus/art-sidebar-menu/widget/SidebarSubmenu.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 0
graphify-out/cache/ast/d3ec4d58d667a97cddbb872955b90fa6d6424e74709c03a4dcf69c415c0dba68.json


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 112 - 112
graphify-out/graph.json


+ 54 - 7
web-admin/src/components/core/layouts/art-menus/art-sidebar-menu/index.vue

@@ -143,6 +143,7 @@
   import { useMenuStore } from '@/store/modules/menu'
   import { isIframe } from '@/utils/navigation'
   import { handleMenuJump } from '@/utils/navigation'
+  import { filterSidebarRoutes } from '@/utils/router/sidebar-menu-filter'
   import SidebarSubmenu from './widget/SidebarSubmenu.vue'
   import { useCommon } from '@/hooks/core/useCommon'
   import { useWindowSize, useTimeoutFn } from '@vueuse/core'
@@ -153,6 +154,16 @@
   const ANIMATION_DELAY = 350
   const MENU_CLOSE_WIDTH = MenuWidth.CLOSE
 
+  /** 开发环境或 window.__SQXP_DEBUG_SIDEBAR__ = true 时打印侧栏展开同步日志 */
+  const sidebarMenuDebug =
+    import.meta.env.DEV ||
+    (typeof window !== 'undefined' &&
+      !!(window as Window & { __SQXP_DEBUG_SIDEBAR__?: boolean }).__SQXP_DEBUG_SIDEBAR__)
+
+  const logSidebarMenu = (...args: unknown[]) => {
+    if (sidebarMenuDebug) console.debug('[ArtSidebarMenu]', ...args)
+  }
+
   const route = useRoute()
   const router = useRouter()
   const settingStore = useSettingStore()
@@ -218,11 +229,13 @@
   })
 
   /**
-   * 递归收集所有可展开菜单 index,保证左侧菜单默认全展开(桌面端)
+   * 递归收集所有可展开菜单 index(与 SidebarSubmenu 使用同一套过滤后的树,
+   * 避免对未渲染的 ElSubMenu 调用 open 导致展开状态不稳定)
    */
   const collectOpenMenuIndexes = (items: any[]): string[] => {
+    const filtered = filterSidebarRoutes(items || [])
     const openIndexes: string[] = []
-    items.forEach((item) => {
+    filtered.forEach((item) => {
       if (item?.children && item.children.length > 0 && !item?.meta?.isHide) {
         const idx = String(item.path || item.meta?.title || '')
         if (idx) openIndexes.push(idx)
@@ -235,16 +248,40 @@
   /**
    * Element Plus 的 default-openeds 仅在初始化时生效,
    * 这里在菜单数据变更后主动展开,避免刷新后展开状态不一致。
+   * 使用多帧等待,确保 ElSubMenu 已挂载后再 open(单次 nextTick 易竞态)。
    */
   const syncExpandedMenus = async (): Promise<void> => {
-    if (isMobileScreen.value || !menuOpen.value) return
+    if (isMobileScreen.value || !menuOpen.value) {
+      logSidebarMenu('syncExpandedMenus skip', {
+        reason: isMobileScreen.value ? 'mobile' : 'menuCollapsed',
+        isMobileScreen: isMobileScreen.value,
+        menuOpen: menuOpen.value
+      })
+      return
+    }
 
     await nextTick()
-    const menuInstance = menuRef.value
-    if (!menuInstance?.open) return
+    await nextTick()
+    await new Promise<void>((resolve) => requestAnimationFrame(() => resolve()))
+
+    const menuInstance = menuRef.value as { open?: (index: string) => void } | null
+    if (!menuInstance?.open) {
+      logSidebarMenu('syncExpandedMenus abort: menuRef.open 不可用', {
+        hasMenuRef: !!menuRef.value,
+        menuListLength: menuList.value.length
+      })
+      return
+    }
+
+    const indexes = defaultOpenedMenus.value
+    logSidebarMenu('syncExpandedMenus apply', {
+      indexCount: indexes.length,
+      sample: indexes.slice(0, 5),
+      routerPath: routerPath.value
+    })
 
-    defaultOpenedMenus.value.forEach((index) => {
-      menuInstance.open(index)
+    indexes.forEach((index) => {
+      menuInstance.open!(index)
     })
   }
 
@@ -375,13 +412,23 @@
   watch(
     menuList,
     (list) => {
+      const rawLen = (list || []).length
       defaultOpenedMenus.value = collectOpenMenuIndexes(list || [])
+      logSidebarMenu('menuList watch', {
+        rawTopLevelCount: rawLen,
+        filteredOpenIndexCount: defaultOpenedMenus.value.length,
+        pathSample: defaultOpenedMenus.value.slice(0, 8)
+      })
       syncExpandedMenus()
     },
     { immediate: true, deep: true }
   )
 
   watch([menuOpen, isMobileScreen], () => {
+    logSidebarMenu('menuOpen / isMobileScreen watch', {
+      menuOpen: menuOpen.value,
+      isMobileScreen: isMobileScreen.value
+    })
     syncExpandedMenus()
   })
 </script>

+ 3 - 44
web-admin/src/components/core/layouts/art-menus/art-sidebar-menu/widget/SidebarSubmenu.vue

@@ -60,6 +60,7 @@
   import { computed } from 'vue'
   import type { AppRouteRecord } from '@/types/router'
   import { formatMenuTitle } from '@/utils/router'
+  import { filterSidebarRoutes } from '@/utils/router/sidebar-menu-filter'
   import { handleMenuJump } from '@/utils/navigation'
   import { useSettingStore } from '@/store/modules/setting'
 
@@ -103,7 +104,7 @@
    * 过滤后的菜单项列表
    * 只显示未隐藏的菜单项
    */
-  const filteredMenuItems = computed(() => filterRoutes(props.list))
+  const filteredMenuItems = computed(() => filterSidebarRoutes(props.list || []))
 
   /**
    * 跳转到指定页面
@@ -122,47 +123,6 @@
     emit('close')
   }
 
-  /**
-   * 判断菜单项本身是否可以作为可点击页面保留在菜单中
-   */
-  const isNavigableRoute = (item: AppRouteRecord): boolean => {
-    return !!(
-      !item.meta.isHide &&
-      ((item.path && item.path.trim()) || item.meta.link || item.meta.isIframe === true) &&
-      (item.component || item.meta.link || item.meta.isIframe === true)
-    )
-  }
-
-  /**
-   * 递归过滤菜单路由,移除隐藏的菜单项
-   * 但如果父菜单本身就是可访问页面,则即使子菜单都被隐藏也应该保留
-   * @param items 菜单项数组
-   * @returns 过滤后的菜单项数组
-   */
-  const filterRoutes = (items: AppRouteRecord[]): AppRouteRecord[] => {
-    return items
-      .filter((item) => {
-        // 如果当前项被隐藏,直接过滤掉
-        if (item.meta.isHide) {
-          return false
-        }
-
-        // 如果有子菜单,递归过滤子菜单
-        if (item.children && item.children.length > 0) {
-          const filteredChildren = filterRoutes(item.children)
-          // 目录菜单要求有可见子菜单;页面菜单则允许仅保留自身
-          return filteredChildren.length > 0 || isNavigableRoute(item)
-        }
-
-        // 叶子节点且未被隐藏,保留
-        return isNavigableRoute(item)
-      })
-      .map((item) => ({
-        ...item,
-        children: item.children ? filterRoutes(item.children) : undefined
-      }))
-  }
-
   /**
    * 判断菜单项是否包含可见的子菜单
    * @param item 菜单项数据
@@ -172,8 +132,7 @@
     if (!item.children || item.children.length === 0) {
       return false
     }
-    // 递归检查是否有可见的子菜单
-    const filteredChildren = filterRoutes(item.children)
+    const filteredChildren = filterSidebarRoutes(item.children)
     return filteredChildren.length > 0
   }
 

+ 36 - 0
web-admin/src/utils/router/sidebar-menu-filter.ts

@@ -0,0 +1,36 @@
+import type { AppRouteRecord } from '@/types/router'
+
+/**
+ * 与侧栏渲染(SidebarSubmenu)一致的菜单过滤,用于收集展开 index 等逻辑,
+ * 避免「原始树」与「实际渲染的 ElSubMenu」不一致导致 open(index) 无效。
+ */
+export function isSidebarNavigableRoute(item: AppRouteRecord): boolean {
+  return !!(
+    !item.meta.isHide &&
+    ((item.path && item.path.trim()) || item.meta.link || item.meta.isIframe === true) &&
+    (item.component || item.meta.link || item.meta.isIframe === true)
+  )
+}
+
+/**
+ * 递归过滤侧栏菜单路由(与 SidebarSubmenu.filterRoutes 行为一致)
+ */
+export function filterSidebarRoutes(items: AppRouteRecord[]): AppRouteRecord[] {
+  return items
+    .filter((item) => {
+      if (item.meta.isHide) {
+        return false
+      }
+
+      if (item.children && item.children.length > 0) {
+        const filteredChildren = filterSidebarRoutes(item.children)
+        return filteredChildren.length > 0 || isSidebarNavigableRoute(item)
+      }
+
+      return isSidebarNavigableRoute(item)
+    })
+    .map((item) => ({
+      ...item,
+      children: item.children ? filterSidebarRoutes(item.children) : undefined
+    }))
+}

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů