zlwu hai 5 meses
pai
achega
51ca87c9ce
Modificáronse 47 ficheiros con 917 adicións e 373 borrados
  1. 21 0
      backend/app/api/admin.py
  2. 20 0
      backend/app/api/mini_client.py
  3. 10 1
      backend/app/api/mini_supplier.py
  4. 18 0
      backend/app/api/payment.py
  5. 4 0
      backend/app/crud/order.py
  6. 162 231
      graphify-out/GRAPH_REPORT.md
  7. 0 0
      graphify-out/cache/ast/10dbfd47ecb0e56f88384b3b0e59c581cba4066615030eb0ec40eafd09075485.json
  8. 1 0
      graphify-out/cache/ast/173452b4899392ef5e6f7fc0e53285cd794c3218530be32f3315e1ef3128c3bf.json
  9. 1 0
      graphify-out/cache/ast/1cfb0ceb01ac1cc4e9a87f7bfa4906a75ff95efe54e53739092bc456f64c05b9.json
  10. 0 0
      graphify-out/cache/ast/1d4c756b4ce6b10538782de7d7e6aa5f4522eac3683cc5a8c82e4163180aeccb.json
  11. 1 0
      graphify-out/cache/ast/2e19f87fa0ab3e7830ac0c3ac31a56757e7999e17e09348e536a01726c6b0b85.json
  12. 1 0
      graphify-out/cache/ast/2e2124dc934e5aede1317e4c37ba8b409420e037534dd9c029d4cbfadee6813d.json
  13. 1 0
      graphify-out/cache/ast/2e36acf56dbf9d7367a4329879eed32144f8c01ee67714767c3cb6d21ec4d2e0.json
  14. 1 0
      graphify-out/cache/ast/383bf27dde5185b3ad231e45bf5829b0a7dceedc3be71d07a68f42dfe3af3591.json
  15. 1 0
      graphify-out/cache/ast/3caa63bb9d9ffedf3d3277fb06448fe29b1d358aefc0df8b11ae61ef55bd37c3.json
  16. 0 0
      graphify-out/cache/ast/3d58a9b17f992b6b8aa103e2ece893b104505ef55d5a41a38f84e50e9b084fdb.json
  17. 1 0
      graphify-out/cache/ast/3d818994ba4d427cf72bff30e77a1ea43f655d097205038da0cf08d61d08d8c2.json
  18. 1 0
      graphify-out/cache/ast/6dd4f62c8b9fc25d98061011ab759599c89c196795092af492502f182e076053.json
  19. 0 0
      graphify-out/cache/ast/6dfa5cc9f348fc8c9156a1b0900620fe46311b411529bf17be50536a3d39f918.json
  20. 1 0
      graphify-out/cache/ast/746050a3b039f33ce2ad858022c98b4215c3a2fda41511b84e904689bbe219d5.json
  21. 0 0
      graphify-out/cache/ast/79a0a2a62358e13cff78923f1aad958a8c502663194c4e828ff91d106f20e198.json
  22. 1 0
      graphify-out/cache/ast/7ebbf37b77249c5b2271c91f29882ba866f9d2692a0e3626680c4833da15f9a1.json
  23. 1 0
      graphify-out/cache/ast/842d34b5f9ad2eff8de131a95016a2651097d37933bf9e88928d383983a8f626.json
  24. 1 0
      graphify-out/cache/ast/8a5645a29bca286d306c82c2613ed7022a471a34b30d00da7309db09ccfeb114.json
  25. 1 0
      graphify-out/cache/ast/8f4009b1594e216df75e70fae938771f28a78c41f12102c3f2b876363785ac8e.json
  26. 0 0
      graphify-out/cache/ast/9942cfc6c92972f494365040668823c08047c62399a2be477307b5a6b6575678.json
  27. 1 0
      graphify-out/cache/ast/9b2f1cc65da9568bf7de0d0d63625791d1f725bcc3f08799b16f7f20be0d49a8.json
  28. 1 0
      graphify-out/cache/ast/9f3db9609335f4191e5a718f6d353f555d02bb14ec31ca9c2ebbf7beea1a75ba.json
  29. 1 0
      graphify-out/cache/ast/a0adda399d88afa16ff77f4f4316c07c56679109ebb92b2b83b80af7cf017601.json
  30. 0 0
      graphify-out/cache/ast/a599323ff9899e0db8863ac706afae8e3e020d21718047aa7dfcf74b6a008ede.json
  31. 1 0
      graphify-out/cache/ast/b06e745eed3af2cdadebba9c5fa0f7e33c5efe52fa7ba3a6ed012fb5db1ad850.json
  32. 0 0
      graphify-out/cache/ast/c3b7c63e4685826666b0c4a42f678b0600a92254e215da258395a5b8497a7629.json
  33. 0 0
      graphify-out/cache/ast/c43c500b2cdeb6f0666425bd772e8e9645d06ed6b31fc3ff4f7e8fa52cc77530.json
  34. 1 0
      graphify-out/cache/ast/d6a0cc807f49cab6b57227f88f17629f1667bc6f31aa7bb1bc0258a672924de0.json
  35. 0 0
      graphify-out/cache/ast/e1ebb4c35582b456832c8d8652abca17d0d985edb4e2dfc90e2fdf49456c372b.json
  36. 0 0
      graphify-out/cache/ast/ea7a210b6b302ca7214c42a7b3e1c15a3ee11e58346a149b020fb60b1c6ca410.json
  37. 1 0
      graphify-out/cache/ast/eed6bc9c4fdf6cfdcc9c3fc8558562439f539e95527bab7a93d243ec7c32534c.json
  38. 112 112
      graphify-out/graph.json
  39. 122 2
      sqxp-wx/pages/client/order/detail/detail.vue
  40. 54 0
      sqxp-wx/pages/client/order/order.vue
  41. 59 8
      sqxp-wx/pages/supplier/delivery/delivery.vue
  42. 90 0
      sqxp-wx/pages/supplier/delivery/detail/detail.vue
  43. 58 8
      sqxp-wx/pages/supplier/home/home.vue
  44. 54 0
      sqxp-wx/utils/order-payment-display.js
  45. 10 3
      web-admin/src/views/system/client-settlement/index.vue
  46. 93 5
      web-admin/src/views/system/order/index.vue
  47. 10 3
      web-admin/src/views/system/supplier-settlement/index.vue

+ 21 - 0
backend/app/api/admin.py

@@ -4017,6 +4017,8 @@ def list_orders(
             "payStatus": pay_status,
             "cancelStatus": getattr(order, "cancel_status", None),
             "paymentMethod": order_crud.normalize_payment_method(order.payment_method),
+            "clientSettlementStatus": getattr(order, "client_settlement_status", None) or "pending",
+            "supplierSettlementStatus": getattr(order, "supplier_settlement_status", None) or "pending",
             "paymentStatus": "paid" if order.paid_amount and order.paid_amount > 0 else "unpaid",
             "itemCount": item_count,
             "createAt": str(order.create_at) if order.create_at else None,
@@ -4286,6 +4288,10 @@ def get_order(
         "deliveryStatus": delivery_status,
         "payStatus": pay_status,
         "paymentMethod": order_crud.normalize_payment_method(order.payment_method),
+        "clientSettlementStatus": getattr(order, "client_settlement_status", None) or "pending",
+        "supplierSettlementStatus": getattr(order, "supplier_settlement_status", None) or "pending",
+        "clientSettlementAt": str(order.client_settlement_at) if getattr(order, "client_settlement_at", None) else None,
+        "supplierSettlementAt": str(order.supplier_settlement_at) if getattr(order, "supplier_settlement_at", None) else None,
         "totalAmount": float(order.total_amount) if order.total_amount else 0,
         "sellingTotalAmount": round(selling_total_amount, 2),
         "supplierTotalAmount": round(supplier_total_amount, 2),
@@ -5912,6 +5918,8 @@ def create_client_settlement(
     db.add(settlement)
     db.flush()
 
+    from app.crud.order import order_crud
+
     for order in orders:
         db.add(OrdClientSettlementItem(
             id=generate_id(),
@@ -5928,6 +5936,19 @@ def create_client_settlement(
         order.client_settlement_at = now
         order.update_by = creator["user_id"]
         order.update_at = now
+        # 客户结算即视为线下收款对账完成:同步支付状态(不覆盖退款中)
+        ps = (getattr(order, "pay_status", None) or "").strip()
+        if ps != "refunding":
+            ra = order.receivable_amount or Decimal("0")
+            ta = order.total_amount or Decimal("0")
+            pa = order.paid_amount or Decimal("0")
+            if ra > Decimal("0") and pa < ra:
+                order.paid_amount = ra
+            elif ta > Decimal("0") and pa < ta:
+                order.paid_amount = ta
+            if not order.payment_time:
+                order.payment_time = now
+            order_crud._apply_three_status(order, pay_status="paid")
 
     db.commit()
     return success_response({

+ 20 - 0
backend/app/api/mini_client.py

@@ -746,6 +746,7 @@ def list_client_orders(
             "deliveryStatus": delivery_status,
             "payStatus": pay_status,
             "paymentMethod": order_crud.normalize_payment_method(order.payment_method),
+            "clientSettlementStatus": getattr(order, "client_settlement_status", None) or "pending",
             "itemCount": item_count,
             "totalQuantity": total_quantity if total_quantity is not None else item_count,
             "items": item_details,
@@ -844,11 +845,27 @@ def get_client_order(
         })
 
     from app.core.order_status_compat import derive_three_status_from_legacy
+    from app.models.models import BizWechatPay
+
     ts = derive_three_status_from_legacy(order.status, paid_amount=order.paid_amount)
     order_status = getattr(order, "order_status", None) or ts.order_status
     delivery_status = getattr(order, "delivery_status", None) or ts.delivery_status
     pay_status = getattr(order, "pay_status", None) or ts.pay_status
 
+    wp = (
+        db.query(BizWechatPay)
+        .filter(BizWechatPay.order_id == order_id, BizWechatPay.is_del == 0)
+        .order_by(BizWechatPay.create_at.desc())
+        .first()
+    )
+    wechat_pay_payload = None
+    if wp:
+        wechat_pay_payload = {
+            "transactionId": ((wp.transaction_id or "").strip() or None),
+            "outTradeNo": ((wp.out_trade_no or "").strip() or None),
+            "payTime": str(wp.pay_time) if wp.pay_time else None,
+        }
+
     return success_response({
         "id": order.id,
         "orderNo": order.order_no,
@@ -861,6 +878,9 @@ def get_client_order(
         "deliveryStatus": delivery_status,
         "payStatus": pay_status,
         "paymentMethod": order_crud.normalize_payment_method(order.payment_method),
+        "clientSettlementStatus": getattr(order, "client_settlement_status", None) or "pending",
+        "clientSettlementAt": str(order.client_settlement_at) if getattr(order, "client_settlement_at", None) else None,
+        "wechatPay": wechat_pay_payload,
         "totalAmount": float(order.total_amount) if order.total_amount else 0,
         "paidAmount": float(order.paid_amount) if order.paid_amount else 0,
         "receivableAmount": float(order.receivable_amount) if order.receivable_amount else 0,

+ 10 - 1
backend/app/api/mini_supplier.py

@@ -1004,7 +1004,12 @@ def list_supplier_tasks(
             "totalQuantity": round(total_quantity, 3),
             "supplierQuoteTotal": round(supplier_quote_total, 2),
             "items": line_items,
-            "createAt": str(task.create_at) if task.create_at else None
+            "createAt": str(task.create_at) if task.create_at else None,
+            "supplierSettlementStatus": (
+                (getattr(order, "supplier_settlement_status", None) or "pending")
+                if order
+                else "pending"
+            ),
         })
 
     return success_response({
@@ -1050,6 +1055,10 @@ def get_supplier_task_detail(
             # 供应商备货详情仅展示客户订单号,不下发客户联系人、地址等
             order_info = {
                 "orderNo": order.order_no,
+                "supplierSettlementStatus": getattr(order, "supplier_settlement_status", None) or "pending",
+                "supplierSettlementAt": str(order.supplier_settlement_at)
+                if getattr(order, "supplier_settlement_at", None)
+                else None,
             }
 
     deliver_info = None

+ 18 - 0
backend/app/api/payment.py

@@ -304,6 +304,24 @@ async def wechat_pay_notify(request: Request):
                 order.delivery_status = "pending_dispatch"
             order_crud.sync_pay_fields_from_amounts(order)
             order.update_at = datetime.now()
+            if getattr(order, "pay_status", None) == "paid":
+                note_parts = []
+                if transaction_id:
+                    note_parts.append(f"微信交易号:{transaction_id}")
+                if pay_time:
+                    note_parts.append(f"支付完成时间:{pay_time.strftime('%Y-%m-%d %H:%M:%S')}")
+                order_crud._create_status_log(
+                    db,
+                    str(order.id),
+                    "pay_pending_payment",
+                    "pay_paid",
+                    "system",
+                    "system",
+                    reason="微信支付成功",
+                    note=";".join(note_parts) if note_parts else None,
+                    create_name="微信支付",
+                    log_create_at=pay_time,
+                )
             _upsert_wechat_pay_record(
                 db,
                 order_id=str(order.id),

+ 4 - 0
backend/app/crud/order.py

@@ -530,8 +530,10 @@ class OrderCRUD(CRUDBase[BizOrder]):
         reason: str = None,
         note: str = None,
         create_name: str = None,
+        log_create_at: Optional[datetime] = None,
     ):
         """创建订单状态变更日志"""
+        ts = log_create_at or datetime.now()
         log = BizOrderStatusLog(
             id=generate_id(),
             order_id=order_id,
@@ -543,6 +545,8 @@ class OrderCRUD(CRUDBase[BizOrder]):
             note=note,
             create_by=operator_id,
             create_name=create_name or None,
+            create_at=ts,
+            update_at=ts,
         )
         db.add(log)
 

+ 162 - 231
graphify-out/GRAPH_REPORT.md

@@ -1,12 +1,12 @@
-# Graph Report - sqxp_副本2  (2026-05-02)
+# Graph Report - sqxp_副本2  (2026-05-06)
 
 ## Corpus Check
-- 704 files · ~644,277 words
+- 705 files · ~645,782 words
 - Verdict: corpus is large enough that graph structure adds value.
 
 ## Summary
-- 5014 nodes · 27159 edges · 81 communities detected
-- Extraction: 19% EXTRACTED · 81% INFERRED · 0% AMBIGUOUS · INFERRED: 21960 edges (avg confidence: 0.54)
+- 5070 nodes · 28190 edges · 68 communities detected
+- Extraction: 18% EXTRACTED · 82% INFERRED · 0% AMBIGUOUS · INFERRED: 22987 edges (avg confidence: 0.54)
 - Token cost: 0 input · 0 output
 
 ## Community Hubs (Navigation)
@@ -31,78 +31,65 @@
 - [[_COMMUNITY_Community 18|Community 18]]
 - [[_COMMUNITY_Community 19|Community 19]]
 - [[_COMMUNITY_Community 20|Community 20]]
-- [[_COMMUNITY_Community 21|Community 21]]
-- [[_COMMUNITY_Community 22|Community 22]]
 - [[_COMMUNITY_Community 23|Community 23]]
 - [[_COMMUNITY_Community 24|Community 24]]
 - [[_COMMUNITY_Community 25|Community 25]]
 - [[_COMMUNITY_Community 26|Community 26]]
 - [[_COMMUNITY_Community 27|Community 27]]
-- [[_COMMUNITY_Community 28|Community 28]]
 - [[_COMMUNITY_Community 29|Community 29]]
-- [[_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 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 48|Community 48]]
+- [[_COMMUNITY_Community 41|Community 41]]
+- [[_COMMUNITY_Community 47|Community 47]]
 - [[_COMMUNITY_Community 49|Community 49]]
 - [[_COMMUNITY_Community 50|Community 50]]
 - [[_COMMUNITY_Community 51|Community 51]]
 - [[_COMMUNITY_Community 52|Community 52]]
-- [[_COMMUNITY_Community 56|Community 56]]
+- [[_COMMUNITY_Community 53|Community 53]]
+- [[_COMMUNITY_Community 54|Community 54]]
+- [[_COMMUNITY_Community 57|Community 57]]
+- [[_COMMUNITY_Community 58|Community 58]]
 - [[_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 68|Community 68]]
+- [[_COMMUNITY_Community 67|Community 67]]
 - [[_COMMUNITY_Community 69|Community 69]]
-- [[_COMMUNITY_Community 70|Community 70]]
-- [[_COMMUNITY_Community 71|Community 71]]
-- [[_COMMUNITY_Community 75|Community 75]]
-- [[_COMMUNITY_Community 78|Community 78]]
-- [[_COMMUNITY_Community 79|Community 79]]
 - [[_COMMUNITY_Community 81|Community 81]]
+- [[_COMMUNITY_Community 91|Community 91]]
 - [[_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 430|Community 430]]
-- [[_COMMUNITY_Community 431|Community 431]]
-- [[_COMMUNITY_Community 432|Community 432]]
-- [[_COMMUNITY_Community 433|Community 433]]
-- [[_COMMUNITY_Community 434|Community 434]]
-- [[_COMMUNITY_Community 435|Community 435]]
-- [[_COMMUNITY_Community 436|Community 436]]
-- [[_COMMUNITY_Community 437|Community 437]]
+- [[_COMMUNITY_Community 145|Community 145]]
+- [[_COMMUNITY_Community 146|Community 146]]
+- [[_COMMUNITY_Community 147|Community 147]]
+- [[_COMMUNITY_Community 148|Community 148]]
+- [[_COMMUNITY_Community 149|Community 149]]
+- [[_COMMUNITY_Community 150|Community 150]]
+- [[_COMMUNITY_Community 418|Community 418]]
+- [[_COMMUNITY_Community 419|Community 419]]
+- [[_COMMUNITY_Community 420|Community 420]]
+- [[_COMMUNITY_Community 421|Community 421]]
+- [[_COMMUNITY_Community 422|Community 422]]
+- [[_COMMUNITY_Community 423|Community 423]]
+- [[_COMMUNITY_Community 424|Community 424]]
+- [[_COMMUNITY_Community 425|Community 425]]
 
 ## God Nodes (most connected - your core abstractions)
-1. `BizPriceListItem` - 814 edges
-2. `BizInquiry` - 743 edges
-3. `BizQuotation` - 741 edges
-4. `BizAttachment` - 731 edges
-5. `SysUser` - 730 edges
-6. `BasProduct` - 730 edges
-7. `BizDeliveryTask` - 722 edges
-8. `BasProductCategory` - 720 edges
-9. `BasProductUnit` - 720 edges
-10. `BizOrder` - 711 edges
+1. `BizPriceListItem` - 862 edges
+2. `BizInquiry` - 787 edges
+3. `BizQuotation` - 785 edges
+4. `BizAttachment` - 775 edges
+5. `SysUser` - 768 edges
+6. `BasProduct` - 768 edges
+7. `BizDeliveryTask` - 760 edges
+8. `BasProductCategory` - 758 edges
+9. `BasProductUnit` - 758 edges
+10. `BizOrder` - 749 edges
 
 ## Surprising Connections (you probably didn't know these)
 - `get_active()` --calls--> `query()`  [INFERRED]
@@ -111,423 +98,367 @@
   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--> `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
+- `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
+- `_run_delivery_warning_check()` --calls--> `close()`  [INFERRED]
+  backend/app/tasks/__init__.py → sqxp-wx/uni_modules/uni-transition/components/uni-transition/uni-transition.vue
 
 ## Communities
 
 ### Community 0 - "Community 0"
-Cohesion: 0.07
-Nodes (724): 获取管理员的区域过滤条件      对于区域管理员,返回其所属区域ID(用于过滤数据)     对于超级管理员(无区域绑定),返回None(不过滤), 更新用户菜单授权(按 code 全量覆盖;兼容明文/密文载荷)。, 更新用户菜单授权(按 code 全量覆盖;兼容明文/密文载荷)。, 与 PUT 同逻辑:前端按 POST 模式调用时复用。, 兼容旧逻辑:返回单区域过滤ID。多区域场景返回排序后首个区域。, 更新用户菜单授权(按 code 全量覆盖;兼容明文/密文载荷)。, 兼容旧逻辑:返回单区域过滤ID。多区域场景返回排序后首个区域。, 更新用户菜单授权(按 code 全量覆盖;兼容明文/密文载荷)。 (+716 more)
+Cohesion: 0.01
+Nodes (815): adjust_order(), _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() (+807 more)
 
 ### Community 1 - "Community 1"
-Cohesion: 0.01
-Nodes (171): calendar(), memo(), record(), handleSelect(), onMenuShow(), showMenu(), onClear(), onInput() (+163 more)
+Cohesion: 0.06
+Nodes (750): 获取管理员的区域过滤条件      对于区域管理员,返回其所属区域ID(用于过滤数据)     对于超级管理员(无区域绑定),返回None(不过滤), 更新用户菜单授权(按 code 全量覆盖;兼容明文/密文载荷)。, 更新用户菜单授权(按 code 全量覆盖;兼容明文/密文载荷)。, 与 PUT 同逻辑:前端按 POST 模式调用时复用。, 兼容旧逻辑:返回单区域过滤ID。多区域场景返回排序后首个区域。, 更新用户菜单授权(按 code 全量覆盖;兼容明文/密文载荷)。, 兼容旧逻辑:返回单区域过滤ID。多区域场景返回排序后首个区域。, 更新用户菜单授权(按 code 全量覆盖;兼容明文/密文载荷)。 (+742 more)
 
 ### Community 2 - "Community 2"
 Cohesion: 0.01
-Nodes (166): fullRegion(), if(), if(), FireworkSystem, mapKeys(), VantComponent(), HTMLParser(), parseAttrs() (+158 more)
+Nodes (211): handleSubmit(), loadDetail(), onRegionMultiConfirm(), regionLabel(), syncMultiFromForm(), validate(), fetchActiveRegions(), fetchAssignableMenuCodes() (+203 more)
 
 ### Community 3 - "Community 3"
 Cohesion: 0.01
-Nodes (168): fetchActiveRegions(), fetchAssignableMenuCodes(), fetchCategoryList(), fetchCategoryParameters(), fetchCategoryTree(), fetchCreateUnit(), fetchNotDeletedRegions(), fetchProductBannerList() (+160 more)
+Nodes (194): fullRegion(), if(), if(), FireworkSystem, mapKeys(), VantComponent(), HTMLParser(), parseAttrs() (+186 more)
 
 ### Community 4 - "Community 4"
 Cohesion: 0.01
-Nodes (255): InvoiceCreateRequest, MessageCreateRequest, 获取商品的评价列表(仅显示已审核通过的评价), AdjustResponse, Config, CreateAdjustRequest, 批量添加轮播图(请求体为 JSON 数组;兼容密文信封与 ApiCryptoMiddleware 未改写 body 的场景), 批量添加轮播图(请求体为 JSON 数组;兼容密文信封与 ApiCryptoMiddleware 未改写 body 的场景) (+247 more)
+Nodes (166): reload(), calendar(), memo(), record(), addUnit(), isDef(), isNumber(), nextTick() (+158 more)
 
 ### Community 5 - "Community 5"
-Cohesion: 0.03
-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)
+Cohesion: 0.01
+Nodes (253): InvoiceCreateRequest, MessageCreateRequest, 获取商品的评价列表(仅显示已审核通过的评价), AdjustResponse, Config, CreateAdjustRequest, 批量添加轮播图(请求体为 JSON 数组;兼容密文信封与 ApiCryptoMiddleware 未改写 body 的场景), 批量添加轮播图(请求体为 JSON 数组;兼容密文信封与 ApiCryptoMiddleware 未改写 body 的场景) (+245 more)
 
 ### Community 6 - "Community 6"
-Cohesion: 0.01
-Nodes (146): handleSubmit(), loadDetail(), onRegionMultiConfirm(), regionLabel(), syncMultiFromForm(), validate(), 小程序客户端扩展接口:购物车、商品分类、仪表盘、发票、下单、支付, 从叶子分类 id 向上解析(含缓存):返回 (根分类, 叶子分类)。 (+138 more)
+Cohesion: 0.02
+Nodes (152): 处理预警请求体(与前端 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: 发送微信模板消息提醒配送员 (+144 more)
 
 ### Community 7 - "Community 7"
 Cohesion: 0.02
-Nodes (64): fetchGetMenuCodes(), fetchGetUserInfo(), fetchLogin(), fetchGetMenuList(), fetchGetRoleList(), fetchGetUserList(), reload(), addUnit() (+56 more)
+Nodes (56): fetchGetMenuCodes(), fetchGetUserInfo(), fetchLogin(), fetchGetMenuList(), fetchGetRoleList(), fetchGetUserList(), useSettingsHandlers(), useSettingsPanel() (+48 more)
 
 ### Community 8 - "Community 8"
 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.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.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.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.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"
+### Community 9 - "Community 9"
 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)
+Nodes (87): 批量获取供应商取货地址,避免列表接口逐条查询。, 一级/二级分类用 / 连接(与订单详情一致)。, 价格清单行 price 为面向客户的最终单价;pricing_coefficient 为调价系数(%),供货单价 = price / (1 + 系数/100)。, (price_list_id, product_id, unit_id) -> BizPriceListItem, 供应商默认取货地址;无默认时取第一条 pickup 类型地址。, 获取供应商配送任务详情(订单仅返回客户订单号;商品行含主图与规格;单价由价格清单行 price(客户最终单价)与 pricing_coefficient 反推供, 获取供应商配送任务详情(订单仅返回客户订单号;商品行含主图与规格;单价由价格清单行 price(客户最终单价)与 pricing_coefficient 反推供, 获取供应商配送任务详情(订单仅返回客户订单号;商品行含主图与规格;单价由价格清单行 price(客户最终单价)与 pricing_coefficient 反推供 (+79 more)
 
-### Community 16 - "Community 16"
-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"
+### Community 10 - "Community 10"
 Cohesion: 0.03
 Nodes (43): askAbortDownload(), checkLocalStoragePackage(), closeUpdate(), compare(), deleteSavedFile(), downLoadComplete(), installPackage(), onLoad() (+35 more)
 
-### Community 18 - "Community 18"
-Cohesion: 0.06
-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"
+### Community 11 - "Community 11"
 Cohesion: 0.05
 Nodes (15): fetchGetLatestUpgradeLog(), if(), getDefaultLanguage(), emitUrls(), removeItem(), revokeLocalPreview(), checkStorageCompatibility(), StorageConfig (+7 more)
 
-### Community 20 - "Community 20"
-Cohesion: 0.05
-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"
+### Community 12 - "Community 12"
 Cohesion: 0.05
 Nodes (22): change(), created(), getDataList(), getSelectedValue(), handler(), loadData(), mixinDatacomResData(), modelValue() (+14 more)
 
-### Community 22 - "Community 22"
-Cohesion: 0.07
-Nodes (30): DeliveryWarningCRUD, get_warning_detail(), get_warning_list(), handle_warning(), _preview_text(), _to_response(), create_review(), get_product_review_stats() (+22 more)
-
-### Community 23 - "Community 23"
+### Community 13 - "Community 13"
 Cohesion: 0.09
 Nodes (27): base64ToBytes(), bytesToBase64(), decryptJson(), deriveKey(), encryptJson(), fromBase64Url(), hexToBytes(), sha256Hex() (+19 more)
 
-### Community 24 - "Community 24"
-Cohesion: 0.11
-Nodes (12): activated(), bottom(), center(), close(), deactivated(), destroyed(), left(), onTap() (+4 more)
-
-### Community 25 - "Community 25"
+### Community 14 - "Community 14"
 Cohesion: 0.18
 Nodes (21): destroyed(), getDirection(), getDom(), isPC(), mousedown(), mouseleave(), mousemove(), mouseup() (+13 more)
 
-### Community 26 - "Community 26"
+### Community 15 - "Community 15"
 Cohesion: 0.13
 Nodes (13): change(), clearVal(), created(), debounce(), emit(), formatItemName(), getCache(), handler() (+5 more)
 
-### Community 27 - "Community 27"
+### Community 16 - "Community 16"
 Cohesion: 0.12
 Nodes (15): addCopyButton(), highlightCode(), insertLineNumbers(), isBlockProcessed(), markBlockAsProcessed(), processBlock(), setupHighlightDirective(), setupRippleDirective() (+7 more)
 
-### Community 28 - "Community 28"
+### Community 17 - "Community 17"
 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 29 - "Community 29"
-Cohesion: 0.16
-Nodes (6): submit_quotation(), InquiryCRUD, InquiryItemCRUD, 取消发布询价单(published -> draft), 获取区域当前发布中的询价单(最多1条,业务上应保证互斥), BizInquiryHis
-
-### Community 30 - "Community 30"
+### Community 18 - "Community 18"
 Cohesion: 0.16
 Nodes (10): useTable(), useTableImpl(), getColumnChecks(), useTableColumns(), createErrorHandler(), createSmartDebounce(), defaultResponseAdapter(), extractPagination() (+2 more)
 
-### Community 31 - "Community 31"
+### Community 19 - "Community 19"
 Cohesion: 0.2
 Nodes (14): defaultAvatarPathByRole(), normalizeAvatarUrlForMini(), resolveUserAvatarUrl(), appendQueryParam(), fallbackVersionFromManifest(), getStaticAssetsVersion(), hasSignedQuery(), hasVersionParam() (+6 more)
 
-### Community 32 - "Community 32"
+### Community 20 - "Community 20"
 Cohesion: 0.28
 Nodes (14): getDirection(), getDisabledType(), getDom(), move(), moveDirection(), openState(), range(), resetTouchStatus() (+6 more)
 
-### Community 35 - "Community 35"
+### Community 23 - "Community 23"
 Cohesion: 0.17
 Nodes (2): clean(), onInput()
 
-### Community 36 - "Community 36"
+### Community 24 - "Community 24"
+Cohesion: 0.33
+Nodes (11): changeFlag(), countDown(), day(), handler(), hour(), minute(), second(), startData() (+3 more)
+
+### Community 25 - "Community 25"
 Cohesion: 0.24
 Nodes (4): getVal(), leftIconWidth(), navbarHeight(), rightIconWidth()
 
-### Community 37 - "Community 37"
+### Community 26 - "Community 26"
 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 38 - "Community 38"
+### Community 27 - "Community 27"
 Cohesion: 0.24
 Nodes (3): _calcValue(), _getDecimalScale(), _onBlur()
 
-### Community 40 - "Community 40"
+### Community 29 - "Community 29"
 Cohesion: 0.27
 Nodes (4): dataValue(), emit(), onChange(), setOpen()
 
-### Community 42 - "Community 42"
+### Community 31 - "Community 31"
 Cohesion: 0.25
 Nodes (2): borderStyle(), value2px()
 
-### Community 43 - "Community 43"
+### Community 32 - "Community 32"
 Cohesion: 0.29
 Nodes (3): BaseModel, get_active(), SoftDeleteModel
 
-### Community 44 - "Community 44"
+### Community 33 - "Community 33"
 Cohesion: 0.39
 Nodes (6): isIgnorableScriptError(), normalizeErrorMessage(), registerPromiseErrorHandler(), registerResourceErrorHandler(), scriptErrorHandler(), setupErrorHandle()
 
-### Community 45 - "Community 45"
+### Community 34 - "Community 34"
 Cohesion: 0.29
 Nodes (2): handleTouchEnd(), setTransition()
 
-### Community 46 - "Community 46"
+### Community 35 - "Community 35"
 Cohesion: 0.36
 Nodes (1): TableHeightCalculator
 
-### Community 48 - "Community 48"
+### Community 37 - "Community 37"
 Cohesion: 0.36
 Nodes (1): RouteValidator
 
-### Community 49 - "Community 49"
+### Community 38 - "Community 38"
 Cohesion: 0.39
 Nodes (5): chooseAll(), chooseAndUploadFile(), chooseImage(), chooseVideo(), uploadFiles()
 
-### Community 50 - "Community 50"
+### Community 39 - "Community 39"
 Cohesion: 0.33
 Nodes (2): logApiCryptoBootInfo(), maskSecret()
 
-### Community 51 - "Community 51"
+### Community 40 - "Community 40"
 Cohesion: 0.38
 Nodes (4): fetchApiRequestLogList(), handleReset(), handleSearch(), loadData()
 
-### Community 52 - "Community 52"
+### Community 41 - "Community 41"
 Cohesion: 0.38
 Nodes (3): _change(), close(), open()
 
-### Community 56 - "Community 56"
-Cohesion: 0.4
-Nodes (1): CategoryCRUD
-
-### Community 59 - "Community 59"
+### Community 47 - "Community 47"
 Cohesion: 0.33
 Nodes (1): BridgeError
 
-### Community 61 - "Community 61"
+### Community 49 - "Community 49"
 Cohesion: 0.4
 Nodes (2): created(), getTable()
 
-### Community 62 - "Community 62"
+### Community 50 - "Community 50"
 Cohesion: 0.47
 Nodes (3): get_file_data(), get_file_ext(), get_file_info()
 
-### Community 63 - "Community 63"
+### Community 51 - "Community 51"
 Cohesion: 0.4
 Nodes (2): created(), getForm()
 
-### Community 64 - "Community 64"
+### Community 52 - "Community 52"
 Cohesion: 0.47
 Nodes (4): onDial(), onTextClick(), dialPhone(), normalizeDialNumber()
 
-### Community 65 - "Community 65"
+### Community 53 - "Community 53"
 Cohesion: 0.4
 Nodes (4): 运营端角色守卫:admin / super_admin 可访问。, 接口菜单权限守卫。super_admin 直接放行。, require_admin_or_super_admin(), require_menu_code()
 
-### Community 66 - "Community 66"
+### Community 54 - "Community 54"
 Cohesion: 0.5
 Nodes (2): focusInput(), handleKeydown()
 
-### Community 68 - "Community 68"
+### Community 57 - "Community 57"
 Cohesion: 0.5
 Nodes (3): BaseSettings, Config, Settings
 
-### Community 69 - "Community 69"
-Cohesion: 0.5
-Nodes (2): 申请退款          Args:             out_trade_no: 商户订单号             total_amount, 生成签名 V3 (SHA256 with RSA)
-
-### Community 70 - "Community 70"
+### Community 58 - "Community 58"
 Cohesion: 0.67
 Nodes (2): if(), logSidebarMenu()
 
-### Community 71 - "Community 71"
+### Community 59 - "Community 59"
 Cohesion: 0.5
 Nodes (1): collectIds()
 
-### Community 75 - "Community 75"
+### Community 63 - "Community 63"
 Cohesion: 0.83
 Nodes (3): classes(), isTrue(), onClick()
 
-### Community 78 - "Community 78"
+### Community 66 - "Community 66"
 Cohesion: 0.67
 Nodes (2): created(), getParent()
 
-### Community 79 - "Community 79"
+### Community 67 - "Community 67"
 Cohesion: 1.0
 Nodes (2): getSettingDefaults(), resetToDefaults()
 
-### Community 81 - "Community 81"
+### Community 69 - "Community 69"
 Cohesion: 1.0
 Nodes (2): defaultAvatarPathByRole(), resolveUserAvatarUrl()
 
-### Community 93 - "Community 93"
+### Community 81 - "Community 81"
 Cohesion: 0.67
 Nodes (1): updateUseModal()
 
-### Community 103 - "Community 103"
+### Community 91 - "Community 91"
 Cohesion: 1.0
 Nodes (2): buildMiniEncryptedLoginBody(), fetchLoginPublicKey()
 
-### Community 105 - "Community 105"
+### Community 93 - "Community 93"
 Cohesion: 0.67
 Nodes (1): return()
 
-### Community 157 - "Community 157"
+### Community 145 - "Community 145"
 Cohesion: 1.0
 Nodes (1): 发送订单状态通知                  Args:             openid: 用户openid             ord
 
-### Community 158 - "Community 158"
+### Community 146 - "Community 146"
 Cohesion: 1.0
 Nodes (1): 发送支付成功通知                  Args:             openid: 用户openid             ord
 
-### Community 159 - "Community 159"
+### Community 147 - "Community 147"
 Cohesion: 1.0
 Nodes (1): 发送配送通知                  Args:             openid: 配送员openid             task
 
-### Community 160 - "Community 160"
+### Community 148 - "Community 148"
 Cohesion: 1.0
 Nodes (1): 发送配送时效预警                  Args:             openid: 配送员openid             ta
 
-### Community 161 - "Community 161"
+### Community 149 - "Community 149"
 Cohesion: 1.0
 Nodes (1): 发送退款结果通知                  Args:             openid: 用户openid             ord
 
-### Community 162 - "Community 162"
+### Community 150 - "Community 150"
 Cohesion: 1.0
 Nodes (1): 发送催单通知给配送员                  Args:             deliver_openid: 配送员openid
 
-### Community 430 - "Community 430"
+### Community 418 - "Community 418"
 Cohesion: 1.0
 Nodes (1): 读取 JSON body;dict 时走 resolve_request_payload(明文 / 密文 / data 包装)。      Returns:
 
-### Community 431 - "Community 431"
+### Community 419 - "Community 419"
 Cohesion: 1.0
 Nodes (1): 按 region_id 管理 websocket 连接并广播。
 
-### Community 432 - "Community 432"
+### Community 420 - "Community 420"
 Cohesion: 1.0
 Nodes (1): 鉴权并接入房间。返回 current_user dict。
 
-### Community 433 - "Community 433"
+### Community 421 - "Community 421"
 Cohesion: 1.0
 Nodes (1): 向 region 房间广播消息。返回尝试发送的连接数。
 
-### Community 434 - "Community 434"
+### Community 422 - "Community 422"
 Cohesion: 1.0
 Nodes (1): 小程序端 WebSocket 入口:/ws/mini
 
-### Community 435 - "Community 435"
+### Community 423 - "Community 423"
 Cohesion: 1.0
 Nodes (1): 在 FastAPI app 上注册 websocket 路由。
 
-### Community 436 - "Community 436"
+### Community 424 - "Community 424"
 Cohesion: 1.0
 Nodes (1): Base64 密文 -> JSON 对象(须含 username、password、ts 毫秒时间戳)。
 
-### Community 437 - "Community 437"
+### Community 425 - "Community 425"
 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)
+- **87 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 非空时表示失败。` (+82 more)
   These have ≤1 connection - possible missing edges or undocumented components.
-- **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`
+- **Thin community `Community 23`** (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 46`** (8 nodes): `TableHeightCalculator`, `.calculate()`, `.calculateOffset()`, `.calculatePaginationOffset()`, `.constructor()`, `.getHeaderHeight()`, `useTableHeight()`, `useTableHeight.ts`
+- **Thin community `Community 31`** (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 48`** (8 nodes): `RouteValidator`, `.checkComponents()`, `.checkDuplicates()`, `.checkNestedIndexComponent()`, `.logLayoutError()`, `.resolvePath()`, `.validate()`, `RouteValidator.ts`
+- **Thin community `Community 34`** (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 50`** (7 nodes): `refreshButtonAutoIcons()`, `refreshPanelTitleIcons()`, `logApiCryptoBootInfo()`, `maskSecret()`, `App.vue`, `main.ts`, `console.ts`
+- **Thin community `Community 35`** (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 56`** (6 nodes): `category.py`, `CategoryCRUD`, `._build_tree()`, `.get_by_name()`, `.get_children()`, `.get_tree()`
+- **Thin community `Community 37`** (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 59`** (6 nodes): `bridge-error.js`, `BridgeError`, `.code()`, `.constructor()`, `.errCode()`, `.errMsg()`
+- **Thin community `Community 39`** (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 61`** (6 nodes): `uni-thead.vue`, `checkboxSelected()`, `created()`, `data()`, `getTable()`, `init()`
+- **Thin community `Community 47`** (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 63`** (6 nodes): `uni-group.vue`, `created()`, `data()`, `getForm()`, `onClick()`, `title()`
+- **Thin community `Community 49`** (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 66`** (5 nodes): `focusInput()`, `handleKeydown()`, `if()`, `search()`, `index.vue`
+- **Thin community `Community 51`** (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 69`** (4 nodes): `申请退款          Args:             out_trade_no: 商户订单号             total_amount`, `生成签名 V3 (SHA256 with RSA)`, `._generate_sign_v3()`, `.refund()`
+- **Thin community `Community 54`** (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 70`** (4 nodes): `if()`, `logSidebarMenu()`, `index.vue`, `SidebarSubmenu.vue`
+- **Thin community `Community 58`** (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 71`** (4 nodes): `catch()`, `collectIds()`, `handleCategoryTableSelectionChange()`, `index.vue`
+- **Thin community `Community 59`** (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 78`** (4 nodes): `popup.js`, `created()`, `data()`, `getParent()`
+- **Thin community `Community 66`** (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 79`** (3 nodes): `getSettingDefaults()`, `resetToDefaults()`, `setting.ts`
+- **Thin community `Community 67`** (3 nodes): `getSettingDefaults()`, `resetToDefaults()`, `setting.ts`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 81`** (3 nodes): `defaultAvatarPathByRole()`, `resolveUserAvatarUrl()`, `user-avatar.ts`
+- **Thin community `Community 69`** (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 93`** (3 nodes): `check-update.js`, `check-update.ts`, `updateUseModal()`
+- **Thin community `Community 81`** (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 103`** (3 nodes): `loginRsa.js`, `buildMiniEncryptedLoginBody()`, `fetchLoginPublicKey()`
+- **Thin community `Community 91`** (3 nodes): `loginRsa.js`, `buildMiniEncryptedLoginBody()`, `fetchLoginPublicKey()`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 105`** (3 nodes): `return()`, `detail.vue`, `detail.vue`
+- **Thin community `Community 93`** (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 157`** (1 nodes): `发送订单状态通知                  Args:             openid: 用户openid             ord`
+- **Thin community `Community 145`** (1 nodes): `发送订单状态通知                  Args:             openid: 用户openid             ord`
   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 146`** (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             task`
+- **Thin community `Community 147`** (1 nodes): `发送配送通知                  Args:             openid: 配送员openid             task`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 160`** (1 nodes): `发送配送时效预警                  Args:             openid: 配送员openid             ta`
+- **Thin community `Community 148`** (1 nodes): `发送配送时效预警                  Args:             openid: 配送员openid             ta`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 161`** (1 nodes): `发送退款结果通知                  Args:             openid: 用户openid             ord`
+- **Thin community `Community 149`** (1 nodes): `发送退款结果通知                  Args:             openid: 用户openid             ord`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 162`** (1 nodes): `发送催单通知给配送员                  Args:             deliver_openid: 配送员openid`
+- **Thin community `Community 150`** (1 nodes): `发送催单通知给配送员                  Args:             deliver_openid: 配送员openid`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 430`** (1 nodes): `读取 JSON body;dict 时走 resolve_request_payload(明文 / 密文 / data 包装)。      Returns:`
+- **Thin community `Community 418`** (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 431`** (1 nodes): `按 region_id 管理 websocket 连接并广播。`
+- **Thin community `Community 419`** (1 nodes): `按 region_id 管理 websocket 连接并广播。`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 432`** (1 nodes): `鉴权并接入房间。返回 current_user dict。`
+- **Thin community `Community 420`** (1 nodes): `鉴权并接入房间。返回 current_user dict。`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 433`** (1 nodes): `向 region 房间广播消息。返回尝试发送的连接数。`
+- **Thin community `Community 421`** (1 nodes): `向 region 房间广播消息。返回尝试发送的连接数。`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 434`** (1 nodes): `小程序端 WebSocket 入口:/ws/mini`
+- **Thin community `Community 422`** (1 nodes): `小程序端 WebSocket 入口:/ws/mini`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 435`** (1 nodes): `在 FastAPI app 上注册 websocket 路由。`
+- **Thin community `Community 423`** (1 nodes): `在 FastAPI app 上注册 websocket 路由。`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 436`** (1 nodes): `Base64 密文 -> JSON 对象(须含 username、password、ts 毫秒时间戳)。`
+- **Thin community `Community 424`** (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 437`** (1 nodes): `从请求体解析用户名、密码。     返回 (username, password, error_message);error_message 非空时表示失败。`
+- **Thin community `Community 425`** (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 2` to `Community 1`, `Community 3`, `Community 11`, `Community 13`, `Community 17`, `Community 19`, `Community 21`?**
+- **Why does `push()` connect `Community 3` to `Community 0`, `Community 2`, `Community 4`, `Community 6`, `Community 8`, `Community 10`, `Community 11`, `Community 12`?**
   _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`?**
+- **Why does `number()` connect `Community 2` to `Community 0`, `Community 3`, `Community 8`, `Community 10`, `Community 12`, `Community 13`?**
   _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`?**
+- **Why does `BizPriceListItem` connect `Community 1` to `Community 0`, `Community 9`, `Community 6`?**
   _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._
-- **Are the 741 inferred relationships involving `BizInquiry` (e.g. with `InquiryCRUD` and `InquiryItemCRUD`) actually correct?**
-  _`BizInquiry` has 741 INFERRED edges - model-reasoned connections that need verification._
-- **Are the 739 inferred relationships involving `BizQuotation` (e.g. with `QuotationCRUD` and `QuotationItemCRUD`) actually correct?**
-  _`BizQuotation` has 739 INFERRED edges - model-reasoned connections that need verification._
-- **Are the 729 inferred relationships involving `BizAttachment` (e.g. with `AttachmentCRUD` and `价格清单行 price 为面向客户的最终单价;pricing_coefficient 为调价系数(%),供货单价 = price / (1 + 系数/100)。`) actually correct?**
-  _`BizAttachment` has 729 INFERRED edges - model-reasoned connections that need verification._
+- **Are the 860 inferred relationships involving `BizPriceListItem` (e.g. with `PriceListCRUD` and `PriceListItemCRUD`) actually correct?**
+  _`BizPriceListItem` has 860 INFERRED edges - model-reasoned connections that need verification._
+- **Are the 785 inferred relationships involving `BizInquiry` (e.g. with `InquiryCRUD` and `InquiryItemCRUD`) actually correct?**
+  _`BizInquiry` has 785 INFERRED edges - model-reasoned connections that need verification._
+- **Are the 783 inferred relationships involving `BizQuotation` (e.g. with `QuotationCRUD` and `QuotationItemCRUD`) actually correct?**
+  _`BizQuotation` has 783 INFERRED edges - model-reasoned connections that need verification._
+- **Are the 773 inferred relationships involving `BizAttachment` (e.g. with `AttachmentCRUD` and `价格清单行 price 为面向客户的最终单价;pricing_coefficient 为调价系数(%),供货单价 = price / (1 + 系数/100)。`) actually correct?**
+  _`BizAttachment` has 773 INFERRED edges - model-reasoned connections that need verification._

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
graphify-out/cache/ast/10dbfd47ecb0e56f88384b3b0e59c581cba4066615030eb0ec40eafd09075485.json


+ 1 - 0
graphify-out/cache/ast/173452b4899392ef5e6f7fc0e53285cd794c3218530be32f3315e1ef3128c3bf.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_sqxp_wx_pages_supplier_home_home_vue", "label": "home.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/supplier/home/home.vue", "source_location": "L1"}, {"id": "home_home_if", "label": "if()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/supplier/home/home.vue", "source_location": "L221"}], "edges": [{"source": "users_wuzilong_work_00code_sqxp_2_sqxp_wx_pages_supplier_home_home_vue", "target": "home_home_if", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/supplier/home/home.vue", "source_location": "L221", "weight": 1.0}], "raw_calls": [{"caller_nid": "home_home_if", "callee": "push", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/supplier/home/home.vue", "source_location": "L222"}, {"caller_nid": "home_home_if", "callee": "add", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/supplier/home/home.vue", "source_location": "L223"}]}

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

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_system_order_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/order/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
graphify-out/cache/ast/1d4c756b4ce6b10538782de7d7e6aa5f4522eac3683cc5a8c82e4163180aeccb.json


+ 1 - 0
graphify-out/cache/ast/2e19f87fa0ab3e7830ac0c3ac31a56757e7999e17e09348e536a01726c6b0b85.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_sqxp_wx_pages_client_order_order_vue", "label": "order.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/client/order/order.vue", "source_location": "L1"}, {"id": "order_order_if", "label": "if()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/client/order/order.vue", "source_location": "L251"}], "edges": [{"source": "users_wuzilong_work_00code_sqxp_2_sqxp_wx_pages_client_order_order_vue", "target": "order_order_if", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/client/order/order.vue", "source_location": "L251", "weight": 1.0}], "raw_calls": [{"caller_nid": "order_order_if", "callee": "push", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/client/order/order.vue", "source_location": "L252"}, {"caller_nid": "order_order_if", "callee": "add", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/client/order/order.vue", "source_location": "L253"}]}

+ 1 - 0
graphify-out/cache/ast/2e2124dc934e5aede1317e4c37ba8b409420e037534dd9c029d4cbfadee6813d.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_system_client_settlement_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/client-settlement/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}

+ 1 - 0
graphify-out/cache/ast/2e36acf56dbf9d7367a4329879eed32144f8c01ee67714767c3cb6d21ec4d2e0.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_sqxp_wx_pages_supplier_delivery_detail_detail_vue", "label": "detail.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/supplier/delivery/detail/detail.vue", "source_location": "L1"}, {"id": "detail_detail_return", "label": "return()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/supplier/delivery/detail/detail.vue", "source_location": "L202"}], "edges": [{"source": "users_wuzilong_work_00code_sqxp_2_sqxp_wx_pages_supplier_delivery_detail_detail_vue", "target": "detail_detail_return", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/supplier/delivery/detail/detail.vue", "source_location": "L202", "weight": 1.0}], "raw_calls": []}

+ 1 - 0
graphify-out/cache/ast/383bf27dde5185b3ad231e45bf5829b0a7dceedc3be71d07a68f42dfe3af3591.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_sqxp_wx_pages_supplier_delivery_delivery_vue", "label": "delivery.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/supplier/delivery/delivery.vue", "source_location": "L1"}, {"id": "delivery_delivery_if", "label": "if()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/supplier/delivery/delivery.vue", "source_location": "L203"}], "edges": [{"source": "users_wuzilong_work_00code_sqxp_2_sqxp_wx_pages_supplier_delivery_delivery_vue", "target": "delivery_delivery_if", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/supplier/delivery/delivery.vue", "source_location": "L203", "weight": 1.0}], "raw_calls": [{"caller_nid": "delivery_delivery_if", "callee": "push", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/supplier/delivery/delivery.vue", "source_location": "L204"}, {"caller_nid": "delivery_delivery_if", "callee": "add", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/supplier/delivery/delivery.vue", "source_location": "L205"}]}

+ 1 - 0
graphify-out/cache/ast/3caa63bb9d9ffedf3d3277fb06448fe29b1d358aefc0df8b11ae61ef55bd37c3.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_sqxp_wx_pages_supplier_delivery_delivery_vue", "label": "delivery.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/supplier/delivery/delivery.vue", "source_location": "L1"}, {"id": "delivery_delivery_if", "label": "if()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/supplier/delivery/delivery.vue", "source_location": "L203"}], "edges": [{"source": "users_wuzilong_work_00code_sqxp_2_sqxp_wx_pages_supplier_delivery_delivery_vue", "target": "delivery_delivery_if", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/supplier/delivery/delivery.vue", "source_location": "L203", "weight": 1.0}], "raw_calls": [{"caller_nid": "delivery_delivery_if", "callee": "push", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/supplier/delivery/delivery.vue", "source_location": "L204"}, {"caller_nid": "delivery_delivery_if", "callee": "add", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/supplier/delivery/delivery.vue", "source_location": "L205"}]}

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
graphify-out/cache/ast/3d58a9b17f992b6b8aa103e2ece893b104505ef55d5a41a38f84e50e9b084fdb.json


+ 1 - 0
graphify-out/cache/ast/3d818994ba4d427cf72bff30e77a1ea43f655d097205038da0cf08d61d08d8c2.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_sqxp_wx_pages_client_order_order_vue", "label": "order.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/client/order/order.vue", "source_location": "L1"}, {"id": "order_order_if", "label": "if()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/client/order/order.vue", "source_location": "L251"}], "edges": [{"source": "users_wuzilong_work_00code_sqxp_2_sqxp_wx_pages_client_order_order_vue", "target": "order_order_if", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/client/order/order.vue", "source_location": "L251", "weight": 1.0}], "raw_calls": [{"caller_nid": "order_order_if", "callee": "push", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/client/order/order.vue", "source_location": "L252"}, {"caller_nid": "order_order_if", "callee": "add", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/client/order/order.vue", "source_location": "L253"}]}

+ 1 - 0
graphify-out/cache/ast/6dd4f62c8b9fc25d98061011ab759599c89c196795092af492502f182e076053.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_system_order_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/order/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
graphify-out/cache/ast/6dfa5cc9f348fc8c9156a1b0900620fe46311b411529bf17be50536a3d39f918.json


+ 1 - 0
graphify-out/cache/ast/746050a3b039f33ce2ad858022c98b4215c3a2fda41511b84e904689bbe219d5.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_sqxp_wx_pages_supplier_delivery_delivery_vue", "label": "delivery.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/supplier/delivery/delivery.vue", "source_location": "L1"}, {"id": "delivery_delivery_if", "label": "if()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/supplier/delivery/delivery.vue", "source_location": "L203"}], "edges": [{"source": "users_wuzilong_work_00code_sqxp_2_sqxp_wx_pages_supplier_delivery_delivery_vue", "target": "delivery_delivery_if", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/supplier/delivery/delivery.vue", "source_location": "L203", "weight": 1.0}], "raw_calls": [{"caller_nid": "delivery_delivery_if", "callee": "push", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/supplier/delivery/delivery.vue", "source_location": "L204"}, {"caller_nid": "delivery_delivery_if", "callee": "add", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/supplier/delivery/delivery.vue", "source_location": "L205"}]}

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
graphify-out/cache/ast/79a0a2a62358e13cff78923f1aad958a8c502663194c4e828ff91d106f20e198.json


+ 1 - 0
graphify-out/cache/ast/7ebbf37b77249c5b2271c91f29882ba866f9d2692a0e3626680c4833da15f9a1.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_sqxp_wx_pages_supplier_delivery_detail_detail_vue", "label": "detail.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/supplier/delivery/detail/detail.vue", "source_location": "L1"}, {"id": "detail_detail_return", "label": "return()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/supplier/delivery/detail/detail.vue", "source_location": "L202"}], "edges": [{"source": "users_wuzilong_work_00code_sqxp_2_sqxp_wx_pages_supplier_delivery_detail_detail_vue", "target": "detail_detail_return", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/supplier/delivery/detail/detail.vue", "source_location": "L202", "weight": 1.0}], "raw_calls": []}

+ 1 - 0
graphify-out/cache/ast/842d34b5f9ad2eff8de131a95016a2651097d37933bf9e88928d383983a8f626.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_sqxp_wx_pages_client_order_detail_detail_vue", "label": "detail.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/client/order/detail/detail.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}

+ 1 - 0
graphify-out/cache/ast/8a5645a29bca286d306c82c2613ed7022a471a34b30d00da7309db09ccfeb114.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_system_order_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/order/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}

+ 1 - 0
graphify-out/cache/ast/8f4009b1594e216df75e70fae938771f28a78c41f12102c3f2b876363785ac8e.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_sqxp_wx_pages_supplier_home_home_vue", "label": "home.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/supplier/home/home.vue", "source_location": "L1"}, {"id": "home_home_if", "label": "if()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/supplier/home/home.vue", "source_location": "L221"}], "edges": [{"source": "users_wuzilong_work_00code_sqxp_2_sqxp_wx_pages_supplier_home_home_vue", "target": "home_home_if", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/supplier/home/home.vue", "source_location": "L221", "weight": 1.0}], "raw_calls": [{"caller_nid": "home_home_if", "callee": "push", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/supplier/home/home.vue", "source_location": "L222"}, {"caller_nid": "home_home_if", "callee": "add", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/supplier/home/home.vue", "source_location": "L223"}]}

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
graphify-out/cache/ast/9942cfc6c92972f494365040668823c08047c62399a2be477307b5a6b6575678.json


+ 1 - 0
graphify-out/cache/ast/9b2f1cc65da9568bf7de0d0d63625791d1f725bcc3f08799b16f7f20be0d49a8.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_sqxp_wx_pages_supplier_delivery_detail_detail_vue", "label": "detail.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/supplier/delivery/detail/detail.vue", "source_location": "L1"}, {"id": "detail_detail_return", "label": "return()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/supplier/delivery/detail/detail.vue", "source_location": "L202"}], "edges": [{"source": "users_wuzilong_work_00code_sqxp_2_sqxp_wx_pages_supplier_delivery_detail_detail_vue", "target": "detail_detail_return", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/supplier/delivery/detail/detail.vue", "source_location": "L202", "weight": 1.0}], "raw_calls": []}

+ 1 - 0
graphify-out/cache/ast/9f3db9609335f4191e5a718f6d353f555d02bb14ec31ca9c2ebbf7beea1a75ba.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_sqxp_wx_pages_client_order_detail_detail_vue", "label": "detail.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/client/order/detail/detail.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}

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

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_sqxp_wx_pages_supplier_home_home_vue", "label": "home.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/supplier/home/home.vue", "source_location": "L1"}, {"id": "home_home_if", "label": "if()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/supplier/home/home.vue", "source_location": "L221"}], "edges": [{"source": "users_wuzilong_work_00code_sqxp_2_sqxp_wx_pages_supplier_home_home_vue", "target": "home_home_if", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/supplier/home/home.vue", "source_location": "L221", "weight": 1.0}], "raw_calls": [{"caller_nid": "home_home_if", "callee": "push", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/supplier/home/home.vue", "source_location": "L222"}, {"caller_nid": "home_home_if", "callee": "add", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/supplier/home/home.vue", "source_location": "L223"}]}

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
graphify-out/cache/ast/a599323ff9899e0db8863ac706afae8e3e020d21718047aa7dfcf74b6a008ede.json


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

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_sqxp_wx_pages_client_order_detail_detail_vue", "label": "detail.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/client/order/detail/detail.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
graphify-out/cache/ast/c3b7c63e4685826666b0c4a42f678b0600a92254e215da258395a5b8497a7629.json


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
graphify-out/cache/ast/c43c500b2cdeb6f0666425bd772e8e9645d06ed6b31fc3ff4f7e8fa52cc77530.json


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

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_system_supplier_settlement_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/supplier-settlement/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
graphify-out/cache/ast/e1ebb4c35582b456832c8d8652abca17d0d985edb4e2dfc90e2fdf49456c372b.json


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
graphify-out/cache/ast/ea7a210b6b302ca7214c42a7b3e1c15a3ee11e58346a149b020fb60b1c6ca410.json


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

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_views_system_order_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/views/system/order/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 112 - 112
graphify-out/graph.json


+ 122 - 2
sqxp-wx/pages/client/order/detail/detail.vue

@@ -6,6 +6,7 @@ import { request, parseResponseJson } from '@/utils/request'
 import { formatYuan } from '@/utils/format'
 import { formatDateTime } from '@/utils/date'
 import { getClientOrderUnifiedStatus } from '@/utils/client-order-status'
+import { paymentMethodLabel, paymentOrSettlementSubLabel } from '@/utils/order-payment-display'
 import PhoneDialInline from '@/components/phone-dial-inline.vue'
 import ProductDetailOverlay from '@/components/client/product-detail-overlay.vue'
 import ClientTabBar from '@/components/client/client-tab-bar.vue'
@@ -83,6 +84,41 @@ function formatDate(dateStr) {
   return formatDateTime(dateStr)
 }
 
+function isOnlinePayment(detail) {
+  const m = String(detail?.paymentMethod || 'online')
+    .trim()
+    .toLowerCase()
+  return m !== 'offline'
+}
+
+function paymentSerialRaw(detail) {
+  const w = detail?.wechatPay
+  if (w?.transactionId) return String(w.transactionId).trim()
+  if (w?.outTradeNo) return String(w.outTradeNo).trim()
+  return ''
+}
+
+function paymentSerialDisplay(detail) {
+  const s = paymentSerialRaw(detail)
+  return s || '—'
+}
+
+function paymentSerialCopyable(detail) {
+  return paymentSerialRaw(detail).length > 0
+}
+
+function onlinePaymentTimeDisplay(detail) {
+  const t = detail?.paymentTime || detail?.wechatPay?.payTime
+  return t ? formatDate(t) : '—'
+}
+
+function clientSettlementTimeDisplay(detail) {
+  const s = String(detail?.clientSettlementStatus || 'pending').toLowerCase()
+  if (s !== 'settled') return '—'
+  const t = detail?.clientSettlementAt
+  return t ? formatDate(t) : '—'
+}
+
 async function fetchOrderDetail() {
   if (!orderId.value) return
 
@@ -393,14 +429,59 @@ onLoad((query) => {
             </view>
           </view>
 
-          <!-- 金额信息 -->
+          <!-- 支付信息 + 合计 -->
           <view class="amount-section">
             <view class="section-title-row">
               <view class="section-title-with-icon">
                 <image class="section-title-icon" :src="ICON_SECTION_AMOUNT" mode="aspectFit" />
-                <text class="section-title">金额汇总</text>
+                <text class="section-title">支付信息</text>
               </view>
             </view>
+
+            <view class="pay-info-block">
+              <template v-if="isOnlinePayment(orderDetail)">
+                <view class="pay-info-row">
+                  <text class="amount-label">支付方式</text>
+                  <text class="amount-value">{{ paymentMethodLabel(orderDetail.paymentMethod) }}</text>
+                </view>
+                <view class="pay-info-row">
+                  <text class="amount-label">支付结果</text>
+                  <text class="amount-value">{{ paymentOrSettlementSubLabel(orderDetail) }}</text>
+                </view>
+                <view class="pay-info-row pay-info-row--multiline">
+                  <text class="amount-label">支付流水号</text>
+                  <view class="pay-info-value-wrap">
+                    <text class="amount-value pay-serial">{{ paymentSerialDisplay(orderDetail) }}</text>
+                    <SqCopyBtn
+                      v-if="paymentSerialCopyable(orderDetail)"
+                      class="pay-serial-copy"
+                      :text="paymentSerialRaw(orderDetail)"
+                      label="支付流水号"
+                      icon-only
+                    />
+                  </view>
+                </view>
+                <view class="pay-info-row">
+                  <text class="amount-label">支付时间</text>
+                  <text class="amount-value">{{ onlinePaymentTimeDisplay(orderDetail) }}</text>
+                </view>
+              </template>
+              <template v-else>
+                <view class="pay-info-row">
+                  <text class="amount-label">支付方式</text>
+                  <text class="amount-value">{{ paymentMethodLabel(orderDetail.paymentMethod) }}</text>
+                </view>
+                <view class="pay-info-row">
+                  <text class="amount-label">结算状态</text>
+                  <text class="amount-value">{{ paymentOrSettlementSubLabel(orderDetail) }}</text>
+                </view>
+                <view class="pay-info-row">
+                  <text class="amount-label">结算时间</text>
+                  <text class="amount-value">{{ clientSettlementTimeDisplay(orderDetail) }}</text>
+                </view>
+              </template>
+            </view>
+
             <view class="amount-row total">
               <text class="amount-label">合计(元)</text>
               <text class="amount-value total">¥{{ formatYuan(orderDetail.totalAmount) }}</text>
@@ -1010,6 +1091,45 @@ onLoad((query) => {
   border-radius: 24rpx;
   padding: 20rpx 30rpx;
 
+  .pay-info-block {
+    padding-bottom: 8rpx;
+    margin-bottom: 8rpx;
+    border-bottom: 1rpx solid rgba(148, 163, 184, 0.25);
+  }
+
+  .pay-info-row {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding: 14rpx 0;
+    gap: 16rpx;
+
+    &--multiline {
+      align-items: flex-start;
+    }
+  }
+
+  .pay-info-value-wrap {
+    flex: 1;
+    min-width: 0;
+    display: flex;
+    align-items: flex-start;
+    justify-content: flex-end;
+    gap: 10rpx;
+  }
+
+  .pay-serial {
+    text-align: right;
+    word-break: break-all;
+    line-height: 1.45;
+    max-width: 420rpx;
+  }
+
+  .pay-serial-copy {
+    flex: 0 0 auto;
+    margin-top: 2rpx;
+  }
+
   .amount-row {
     display: flex;
     justify-content: space-between;

+ 54 - 0
sqxp-wx/pages/client/order/order.vue

@@ -65,6 +65,13 @@
           </view>
         </view>
 
+        <view class="order-pay-meta">
+          <text class="order-pay-method">{{ paymentMethodLabel(order.paymentMethod ?? order.payment_method) }}</text>
+          <text
+            :class="['order-pay-sub', 'order-pay-sub--' + paymentSubLabelClass(order)]"
+          >{{ paymentOrSettlementSubLabel(order) }}</text>
+        </view>
+
         <!-- 不用横向 scroll-view:H5 嵌套「纵 scroll-view + 横 scroll-view」时 uni 会写 scrollLeft,DOM 未就绪会报错 -->
         <view v-if="(order.items || []).length" class="product-scroll">
           <view class="product-scroll-inner">
@@ -136,6 +143,11 @@ import { request, parseResponseJson } from '@/utils/request'
 import { formatYuan } from '@/utils/format'
 import { formatDateTime } from '@/utils/date'
 import { getClientOrderUnifiedStatus } from '@/utils/client-order-status'
+import {
+  paymentMethodLabel,
+  paymentOrSettlementSubLabel,
+  paymentSubLabelClass,
+} from '@/utils/order-payment-display'
 import { IMG_DEFAULT_PRODUCT, IMG_EMPTY, staticAssetUrl } from '@/utils/staticAsset'
 import ClientTabBar from '@/components/client/client-tab-bar.vue'
 import ClientSkeleton from '@/components/client/client-skeleton.vue'
@@ -542,6 +554,48 @@ onShow(() => {
     }
   }
 
+  .order-pay-meta {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    gap: 16rpx;
+    margin-bottom: 12rpx;
+    padding: 0;
+    box-sizing: border-box;
+    width: 100%;
+    max-width: 100%;
+  }
+
+  .order-pay-method {
+    font-size: 24rpx;
+    font-weight: 600;
+    color: var(--c-text);
+    flex-shrink: 0;
+  }
+
+  .order-pay-sub {
+    font-size: 22rpx;
+    flex: 1;
+    min-width: 0;
+    text-align: right;
+    line-height: 1.35;
+
+    &--settled {
+      color: #059669;
+      font-weight: 600;
+    }
+
+    &--pending {
+      color: #d97706;
+      font-weight: 600;
+    }
+
+    &--warn {
+      color: #dc2626;
+      font-weight: 600;
+    }
+  }
+
   .product-scroll {
     margin-top: 0;
     padding-bottom: 16rpx;

+ 59 - 8
sqxp-wx/pages/supplier/delivery/delivery.vue

@@ -43,6 +43,13 @@ function taskFooterTime(row) {
         return formatDateTime(row.createAt);
     return '';
 }
+/** 平台侧「商家/供应商结算」状态(与 biz_order.supplier_settlement_status 一致) */
+function supplierSettlementSettled(row) {
+    return String(row?.supplierSettlementStatus || '').toLowerCase() === 'settled';
+}
+function supplierSettlementFooterText(row) {
+    return supplierSettlementSettled(row) ? '已与平台结算' : '待平台结算';
+}
 const list = ref([]);
 const page = ref(0);
 const total = ref(0);
@@ -406,16 +413,26 @@ onShow(() => {
             已送达行:{{ row.deliveredItems }}
           </view>
 
-          <view v-if="(row.items || []).length" class="product-scroll">
-            <view class="product-scroll-inner">
-              <view v-for="it in row.items || []" :key="it.id" class="product-card">
-                <ClientImageLoader class="product-image" :src="taskItemImage(it)" mode="aspectFill" />
-                <view class="product-meta">
-                  <text class="product-name">{{ it.productName || '-' }}</text>
-                  <text class="product-qty">{{ taskQtyText(it) }}</text>
+          <view class="product-section">
+            <view v-if="(row.items || []).length" class="product-scroll">
+              <view class="product-scroll-inner">
+                <view v-for="it in row.items || []" :key="it.id" class="product-card">
+                  <ClientImageLoader class="product-image" :src="taskItemImage(it)" mode="aspectFill" />
+                  <view class="product-meta">
+                    <text class="product-name">{{ it.productName || '-' }}</text>
+                    <text class="product-qty">{{ taskQtyText(it) }}</text>
+                  </view>
                 </view>
               </view>
             </view>
+            <view class="product-section-foot">
+              <view
+                class="settlement-tag"
+                :class="supplierSettlementSettled(row) ? 'settlement-tag--done' : 'settlement-tag--wait'"
+              >
+                <text class="settlement-tag-text">{{ supplierSettlementFooterText(row) }}</text>
+              </view>
+            </view>
           </view>
 
           <view class="task-card-footer">
@@ -741,15 +758,49 @@ page {
     margin-top: 6rpx;
     color: #07c160;
   }
-  .product-scroll {
+  .product-section {
     margin-top: 12rpx;
     padding-bottom: 16rpx;
     border-bottom: 1rpx solid #f5f5f5;
+    width: 100%;
+    max-width: 100%;
+    box-sizing: border-box;
+  }
+  .product-scroll {
     width: 100%;
     overflow-x: auto;
     overflow-y: hidden;
     -webkit-overflow-scrolling: touch;
   }
+  .product-section-foot {
+    display: flex;
+    justify-content: flex-end;
+    align-items: center;
+    padding-top: 12rpx;
+  }
+  .settlement-tag {
+    padding: 6rpx 18rpx;
+    border-radius: 8rpx;
+    flex-shrink: 0;
+    max-width: 78%;
+  }
+  .settlement-tag-text {
+    font-size: 22rpx;
+    font-weight: 600;
+    line-height: 1.3;
+  }
+  .settlement-tag--done {
+    background: #ecfdf5;
+  }
+  .settlement-tag--done .settlement-tag-text {
+    color: #059669;
+  }
+  .settlement-tag--wait {
+    background: #fff7e8;
+  }
+  .settlement-tag--wait .settlement-tag-text {
+    color: #d97706;
+  }
   .product-scroll-inner {
     display: flex;
     flex-direction: row;

+ 90 - 0
sqxp-wx/pages/supplier/delivery/detail/detail.vue

@@ -3,6 +3,7 @@ import { computed, ref } from 'vue';
 import { onLoad } from '@dcloudio/uni-app';
 import { useUserStore } from '@/stores/user';
 import { request, parseResponseJson } from '@/utils/request';
+import { formatDateTime } from '@/utils/date';
 import { API_BASE, TOKEN_KEY } from '@/config/api';
 import PhoneDialInline from '@/components/phone-dial-inline.vue';
 import { DEFAULT_PLACEHOLDER_IMAGE_URL, staticAssetUrl } from '@/utils/staticAsset';
@@ -292,6 +293,21 @@ function goProductDetail(productId) {
         url: `/pages/supplier/product/detail?productId=${encodeURIComponent(productId)}&viewOnly=1`
     });
 }
+/** 订单侧与平台(商家结算)结算状态,与列表卡片口径一致 */
+function supplierSettlementSettled() {
+    return String(order.value?.supplierSettlementStatus || '').toLowerCase() === 'settled';
+}
+function supplierSettlementStatusText() {
+    return supplierSettlementSettled() ? '已与平台结算' : '待平台结算';
+}
+function supplierSettlementTimeText() {
+    if (!supplierSettlementSettled())
+        return '—';
+    const t = order.value?.supplierSettlementAt;
+    if (t == null || String(t).trim() === '')
+        return '—';
+    return formatDateTime(String(t));
+}
 async function loadDetail() {
     const id = taskId.value;
     if (!id) {
@@ -436,6 +452,26 @@ onLoad((query) => {
             保存凭证
           </button>
         </view>
+
+        <view v-if="order" class="settlement-under-proof">
+          <view class="cell">
+            <text class="k">平台结算状态</text>
+            <view class="v settlement-val">
+              <view
+                class="settlement-tag"
+                :class="supplierSettlementSettled() ? 'settlement-tag--done' : 'settlement-tag--wait'"
+              >
+                <text class="settlement-tag-text">{{ supplierSettlementStatusText() }}</text>
+              </view>
+            </view>
+          </view>
+          <view class="cell">
+            <text class="k">结算时间</text>
+            <view class="v settlement-val">
+              <text class="settlement-time-text">{{ supplierSettlementTimeText() }}</text>
+            </view>
+          </view>
+        </view>
       </view>
 
       <view v-if="(order && order.orderNo) || (deliver && (deliver.deliverName || deliver.phone))" class="card">
@@ -960,6 +996,60 @@ onLoad((query) => {
   margin-bottom: 16rpx;
   line-height: 1.5;
 }
+
+.settlement-under-proof {
+  margin-top: 20rpx;
+  padding-top: 8rpx;
+  border-top: 1rpx solid #eef5f2;
+}
+
+.settlement-under-proof .cell:first-child {
+  padding-top: 8rpx;
+}
+
+.settlement-val {
+  display: flex;
+  justify-content: flex-end;
+  align-items: center;
+  min-width: 0;
+}
+
+.settlement-time-text {
+  font-size: 26rpx;
+  color: #303133;
+  text-align: right;
+  word-break: break-all;
+}
+
+.settlement-tag {
+  padding: 6rpx 18rpx;
+  border-radius: 8rpx;
+  flex-shrink: 0;
+  max-width: 100%;
+}
+
+.settlement-tag-text {
+  font-size: 22rpx;
+  font-weight: 600;
+  line-height: 1.3;
+}
+
+.settlement-tag--done {
+  background: #ecfdf5;
+}
+
+.settlement-tag--done .settlement-tag-text {
+  color: #059669;
+}
+
+.settlement-tag--wait {
+  background: #fff7e8;
+}
+
+.settlement-tag--wait .settlement-tag-text {
+  color: #d97706;
+}
+
 .cell {
   display: flex;
   align-items: flex-start;

+ 58 - 8
sqxp-wx/pages/supplier/home/home.vue

@@ -101,6 +101,12 @@ function taskLineRight(row) {
         return `截止 ${row.effectiveTime}`;
     return '';
 }
+function supplierSettlementSettled(row) {
+    return String(row?.supplierSettlementStatus || '').toLowerCase() === 'settled';
+}
+function supplierSettlementFooterText(row) {
+    return supplierSettlementSettled(row) ? '已与平台结算' : '待平台结算';
+}
 // 统计数据
 const statistics = ref({
     pendingInquiries: 0,
@@ -413,16 +419,26 @@ onShow(() => {
             </view>
             <view v-if="row.effectiveTime" class="meta">送达截止 {{ row.effectiveTime }}</view>
 
-            <view v-if="(row.items || []).length" class="product-scroll">
-              <view class="product-scroll-inner">
-                <view v-for="it in row.items || []" :key="it.id" class="product-card">
-                  <ClientImageLoader class="product-image" :src="taskItemImage(it)" mode="aspectFill" />
-                  <view class="product-meta">
-                    <text class="product-name">{{ it.productName || '-' }}</text>
-                    <text class="product-qty">{{ taskQtyXText(it) }}</text>
+            <view class="product-section">
+              <view v-if="(row.items || []).length" class="product-scroll">
+                <view class="product-scroll-inner">
+                  <view v-for="it in row.items || []" :key="it.id" class="product-card">
+                    <ClientImageLoader class="product-image" :src="taskItemImage(it)" mode="aspectFill" />
+                    <view class="product-meta">
+                      <text class="product-name">{{ it.productName || '-' }}</text>
+                      <text class="product-qty">{{ taskQtyXText(it) }}</text>
+                    </view>
                   </view>
                 </view>
               </view>
+              <view class="product-section-foot">
+                <view
+                  class="settlement-tag"
+                  :class="supplierSettlementSettled(row) ? 'settlement-tag--done' : 'settlement-tag--wait'"
+                >
+                  <text class="settlement-tag-text">{{ supplierSettlementFooterText(row) }}</text>
+                </view>
+              </view>
             </view>
 
             <view class="task-card-footer">
@@ -810,15 +826,49 @@ onShow(() => {
   color: #087f57;
   font-weight: 600;
 }
-.card .product-scroll {
+.card .product-section {
   margin-top: 12rpx;
   padding-bottom: 16rpx;
   border-bottom: 1rpx solid #f5f5f5;
+  width: 100%;
+  max-width: 100%;
+  box-sizing: border-box;
+}
+.card .product-scroll {
   width: 100%;
   overflow-x: auto;
   overflow-y: hidden;
   -webkit-overflow-scrolling: touch;
 }
+.card .product-section-foot {
+  display: flex;
+  justify-content: flex-end;
+  align-items: center;
+  padding-top: 12rpx;
+}
+.card .settlement-tag {
+  padding: 6rpx 18rpx;
+  border-radius: 8rpx;
+  flex-shrink: 0;
+  max-width: 78%;
+}
+.card .settlement-tag-text {
+  font-size: 22rpx;
+  font-weight: 600;
+  line-height: 1.3;
+}
+.card .settlement-tag--done {
+  background: #ecfdf5;
+}
+.card .settlement-tag--done .settlement-tag-text {
+  color: #059669;
+}
+.card .settlement-tag--wait {
+  background: #fff7e8;
+}
+.card .settlement-tag--wait .settlement-tag-text {
+  color: #d97706;
+}
 .card .product-scroll-inner {
   display: flex;
   flex-direction: row;

+ 54 - 0
sqxp-wx/utils/order-payment-display.js

@@ -0,0 +1,54 @@
+/** 订单列表/详情:支付方式与支付或结算说明(与后端 paymentMethod / payStatus / clientSettlementStatus 对齐) */
+
+export function paymentMethodLabel(method) {
+  const raw =
+    method &&
+    typeof method === 'object' &&
+    ('paymentMethod' in method || 'payment_method' in method)
+      ? method.paymentMethod ?? method.payment_method
+      : method
+  const m = String(raw || 'online')
+    .trim()
+    .toLowerCase()
+  return m === 'offline' ? '线下支付' : '线上支付'
+}
+
+/**
+ * @param {{ paymentMethod?: string, payStatus?: string, clientSettlementStatus?: string }} order
+ */
+export function paymentOrSettlementSubLabel(order) {
+  const m = String(order?.paymentMethod || 'online')
+    .trim()
+    .toLowerCase()
+  if (m === 'offline') {
+    return order?.clientSettlementStatus === 'settled' ? '已结算' : '待结算'
+  }
+  const ps = String(order?.payStatus || 'pending_payment').trim()
+  const map = {
+    paid: '支付成功',
+    pending_payment: '待支付',
+    refunding: '退款中',
+    refunded: '已退款',
+  }
+  return map[ps] || ps
+}
+
+export function paymentOrSettlementLabelTitle(order) {
+  const m = String(order?.paymentMethod || 'online')
+    .trim()
+    .toLowerCase()
+  return m === 'offline' ? '结算状态' : '支付状态'
+}
+
+export function paymentSubLabelClass(order) {
+  const m = String(order?.paymentMethod || 'online')
+    .trim()
+    .toLowerCase()
+  if (m === 'offline') {
+    return order?.clientSettlementStatus === 'settled' ? 'settled' : 'pending'
+  }
+  const ps = String(order?.payStatus || 'pending_payment').trim()
+  if (ps === 'paid') return 'settled'
+  if (ps === 'refunding' || ps === 'refunded') return 'warn'
+  return 'pending'
+}

+ 10 - 3
web-admin/src/views/system/client-settlement/index.vue

@@ -229,10 +229,17 @@
               </template>
             </el-table-column>
             <el-table-column prop="createAt" label="下单时间" min-width="170" />
-            <el-table-column prop="clientSettlementStatus" label="客户结算" width="100">
+            <el-table-column prop="clientSettlementStatus" label="客户结算" min-width="120" align="center">
               <template #default="{ row }">
-                <el-tag :type="row.clientSettlementStatus === 'settled' ? 'success' : 'warning'" size="small">
-                  {{ row.clientSettlementStatus === 'settled' ? '已结算' : '待结算' }}
+                <el-tag :type="row.clientSettlementStatus === 'settled' ? 'success' : 'info'" size="small">
+                  {{ row.clientSettlementStatus === 'settled' ? '已与客户结算' : '待客户结算' }}
+                </el-tag>
+              </template>
+            </el-table-column>
+            <el-table-column prop="supplierSettlementStatus" label="供应商结算" min-width="120" align="center">
+              <template #default="{ row }">
+                <el-tag :type="row.supplierSettlementStatus === 'settled' ? 'success' : 'info'" size="small">
+                  {{ row.supplierSettlementStatus === 'settled' ? '已与供应商结算' : '待供应商结算' }}
                 </el-tag>
               </template>
             </el-table-column>

+ 93 - 5
web-admin/src/views/system/order/index.vue

@@ -232,6 +232,20 @@
           </el-tag>
         </template>
       </el-table-column>
+      <el-table-column prop="clientSettlementStatus" label="客户结算" min-width="110" align="center">
+        <template #default="{ row }">
+          <el-tag :type="getSettlementTagType(row.clientSettlementStatus)" size="small">
+            {{ getClientSettlementLabel(row.clientSettlementStatus) }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column prop="supplierSettlementStatus" label="供应商结算" min-width="120" align="center">
+        <template #default="{ row }">
+          <el-tag :type="getSettlementTagType(row.supplierSettlementStatus)" size="small">
+            {{ getSupplierSettlementLabel(row.supplierSettlementStatus) }}
+          </el-tag>
+        </template>
+      </el-table-column>
       <el-table-column prop="createAt" label="下单时间" min-width="180" />
       <el-table-column label="操作" min-width="260" fixed="right" align="center" header-align="center">
         <template #default="{ row }">
@@ -368,6 +382,28 @@
               <dt>支付方式</dt>
               <dd>{{ getPaymentMethod(viewData.paymentMethod) }}</dd>
             </div>
+            <div class="view-kv">
+              <dt>客户结算</dt>
+              <dd class="view-kv-settlement-dd">
+                <el-tag :type="getSettlementTagType(viewData.clientSettlementStatus)" size="small">
+                  {{ getClientSettlementLabel(viewData.clientSettlementStatus) }}
+                </el-tag>
+                <span class="view-kv-settlement-time">{{
+                  settlementAtDisplay(viewData.clientSettlementStatus, viewData.clientSettlementAt)
+                }}</span>
+              </dd>
+            </div>
+            <div class="view-kv">
+              <dt>供应商结算</dt>
+              <dd class="view-kv-settlement-dd">
+                <el-tag :type="getSettlementTagType(viewData.supplierSettlementStatus)" size="small">
+                  {{ getSupplierSettlementLabel(viewData.supplierSettlementStatus) }}
+                </el-tag>
+                <span class="view-kv-settlement-time">{{
+                  settlementAtDisplay(viewData.supplierSettlementStatus, viewData.supplierSettlementAt)
+                }}</span>
+              </dd>
+            </div>
             <div class="view-kv">
               <dt>支付时间</dt>
               <dd>{{ displayValue(viewData.payTime) }}</dd>
@@ -881,6 +917,28 @@
               <dt>支付方式</dt>
               <dd>{{ getPaymentMethod(viewData.paymentMethod) }}</dd>
             </div>
+            <div class="view-kv">
+              <dt>客户结算</dt>
+              <dd class="view-kv-settlement-dd">
+                <el-tag :type="getSettlementTagType(viewData.clientSettlementStatus)" size="small">
+                  {{ getClientSettlementLabel(viewData.clientSettlementStatus) }}
+                </el-tag>
+                <span class="view-kv-settlement-time">{{
+                  settlementAtDisplay(viewData.clientSettlementStatus, viewData.clientSettlementAt)
+                }}</span>
+              </dd>
+            </div>
+            <div class="view-kv">
+              <dt>供应商结算</dt>
+              <dd class="view-kv-settlement-dd">
+                <el-tag :type="getSettlementTagType(viewData.supplierSettlementStatus)" size="small">
+                  {{ getSupplierSettlementLabel(viewData.supplierSettlementStatus) }}
+                </el-tag>
+                <span class="view-kv-settlement-time">{{
+                  settlementAtDisplay(viewData.supplierSettlementStatus, viewData.supplierSettlementAt)
+                }}</span>
+              </dd>
+            </div>
             <div class="view-kv">
               <dt>支付时间</dt>
               <dd>{{ displayValue(viewData.payTime) }}</dd>
@@ -1895,6 +1953,14 @@
     return String(t).replace('T', ' ').slice(0, 19)
   }
 
+  /** 结算时间:待结算或无时间戳时统一显示「—」 */
+  const settlementAtDisplay = (status: string | undefined, at: string | undefined | null) => {
+    const s = (status || '').toLowerCase()
+    if (s !== 'settled') return '—'
+    const formatted = formatOrderDetailTime(at)
+    return formatted || '—'
+  }
+
   const resolveOrderStatus = (row: any) => {
     if (row?.cancelStatus === 'pending_review') return 'pending_review_cancel'
     if (row?.orderStatus) return row.orderStatus
@@ -1977,6 +2043,21 @@
     return map[m] || method || '-'
   }
 
+  const getSettlementTagType = (status: string | undefined) => {
+    const s = (status || 'pending').toLowerCase()
+    return s === 'settled' ? 'success' : 'info'
+  }
+
+  const getClientSettlementLabel = (status: string | undefined) => {
+    const s = (status || 'pending').toLowerCase()
+    return s === 'settled' ? '已与客户结算' : '待客户结算'
+  }
+
+  const getSupplierSettlementLabel = (status: string | undefined) => {
+    const s = (status || 'pending').toLowerCase()
+    return s === 'settled' ? '已与供应商结算' : '待供应商结算'
+  }
+
   const formatAmount = (amount: number | string) => {
     const num = typeof amount === 'string' ? parseFloat(amount) : amount
     return isNaN(num) ? '0.00' : num.toFixed(2)
@@ -3665,11 +3746,6 @@
 
   .order-module-dialog .view-section {
     padding: 18px 22px;
-    border-bottom: 1px solid #d9e8e2;
-  }
-
-  .order-module-dialog .view-section:last-child {
-    border-bottom: 0;
   }
 
   .order-module-dialog .view-section h3 {
@@ -3680,6 +3756,18 @@
     color: #64748b;
   }
 
+  .view-kv-settlement-dd {
+    display: flex;
+    flex-wrap: wrap;
+    align-items: center;
+    gap: 8px;
+  }
+
+  .view-kv-settlement-time {
+    font-size: 12px;
+    color: #64748b;
+  }
+
   .order-module-dialog .view-kv-grid {
     display: grid;
     grid-template-columns: repeat(2, minmax(0, 1fr));

+ 10 - 3
web-admin/src/views/system/supplier-settlement/index.vue

@@ -226,10 +226,17 @@
               </template>
             </el-table-column>
             <el-table-column prop="createAt" label="下单时间" min-width="170" />
-            <el-table-column prop="supplierSettlementStatus" label="商家结算" width="100">
+            <el-table-column prop="clientSettlementStatus" label="客户结算" min-width="120" align="center">
               <template #default="{ row }">
-                <el-tag :type="row.supplierSettlementStatus === 'settled' ? 'success' : 'warning'" size="small">
-                  {{ row.supplierSettlementStatus === 'settled' ? '已结算' : '待结算' }}
+                <el-tag :type="row.clientSettlementStatus === 'settled' ? 'success' : 'info'" size="small">
+                  {{ row.clientSettlementStatus === 'settled' ? '已与客户结算' : '待客户结算' }}
+                </el-tag>
+              </template>
+            </el-table-column>
+            <el-table-column prop="supplierSettlementStatus" label="供应商结算" min-width="120" align="center">
+              <template #default="{ row }">
+                <el-tag :type="row.supplierSettlementStatus === 'settled' ? 'success' : 'info'" size="small">
+                  {{ row.supplierSettlementStatus === 'settled' ? '已与供应商结算' : '待供应商结算' }}
                 </el-tag>
               </template>
             </el-table-column>

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio