zlwu 6 ماه پیش
والد
کامیت
6d5b17a5f5
44فایلهای تغییر یافته به همراه823 افزوده شده و 371 حذف شده
  1. 20 7
      backend/app/api/delivery.py
  2. 110 19
      backend/app/api/delivery_warning.py
  3. 3 3
      backend/app/api/mini_deliverer.py
  4. 44 1
      backend/app/core/admin_request_json.py
  5. 20 0
      backend/app/core/request_payload.py
  6. 214 140
      graphify-out/GRAPH_REPORT.md
  7. 1 0
      graphify-out/cache/ast/0308f994d1e5e79cbe403d29c8e951a2a5eae4c410aaa736afe85919ceba57bf.json
  8. 0 0
      graphify-out/cache/ast/0bd20fb7eaa6cafb01fdd4702c51c5b9341b3c7383bd76e368668ea291240c37.json
  9. 1 0
      graphify-out/cache/ast/12dcc54c389a918bf403faff3bae02af77fa2feb234e131e90dbaffd70cc334c.json
  10. 0 0
      graphify-out/cache/ast/18a7cdb888f27bac76f18f9a11b6ae3d4565ec8c6382fbc92fc43c689bad0fd6.json
  11. 1 0
      graphify-out/cache/ast/30043a6fc0caefdeacfbab6015909e7f97124949e01fe6edb33ed381ce1d6bef.json
  12. 0 0
      graphify-out/cache/ast/354de9733b9d558c5f7ed14cd720b41098aee405cec032ad5cf617a6a72e3efd.json
  13. 1 0
      graphify-out/cache/ast/42e3f473dd595bfa41ea1cd3417946b7a0e88741d191664b81dbf8c5fdd127bc.json
  14. 1 0
      graphify-out/cache/ast/4ff310c290a6a5c3b7be214a2fdefc048dea4326326ec82d429f69d20349f7cd.json
  15. 1 0
      graphify-out/cache/ast/529bfd1a891dbbf4dbbf22a7f96ec9e6be8a3dca19046fb85ca9a4fccf036a34.json
  16. 1 0
      graphify-out/cache/ast/6764472febe56f7d6c307c4fa621c807503477e3434dd665f4f1d8426b0d6e4f.json
  17. 0 0
      graphify-out/cache/ast/6b46e6202b9e5567511f3b95841061eeb97a3289b53d9be0b9a07f9ef3dc4b41.json
  18. 0 0
      graphify-out/cache/ast/7064c4e1e95675c05cb0e1bfbb73a2b2fb1d286263014c8bffe179f0358ce029.json
  19. 0 0
      graphify-out/cache/ast/75b9e42b737ca49259cf29edd3daa5cf2a1a02b42d97cda966d294406081dc1c.json
  20. 0 0
      graphify-out/cache/ast/7b4c1443d76360709c5ca443f0a3e2dc422ea9000f633c11f9d99f12c09f2e69.json
  21. 0 0
      graphify-out/cache/ast/7bfab3e460af42bcf443964dfd647288365ffa1ada38059d42f5859df1599cbd.json
  22. 0 0
      graphify-out/cache/ast/820a4d14abf1b6a21ffab4556a423e133431055722c90253df231e146e8fb8f8.json
  23. 1 0
      graphify-out/cache/ast/96475bfae66beb394e54534bf816595c2a89112a7e9cdc16e95d3a9af11c670d.json
  24. 1 0
      graphify-out/cache/ast/9752ab276b2c4693ef1215014d39050db0ae5157fd8591334fa245c41f39fbed.json
  25. 1 0
      graphify-out/cache/ast/9ae03375e514388185e1dd3287920f13a860004b3782bcca57a6e9c5c997d20f.json
  26. 0 0
      graphify-out/cache/ast/a190a9801ac68b7776643725fc10d4b7b49b25c59b8adb4383448b64b0d9780e.json
  27. 1 0
      graphify-out/cache/ast/a7d98328f830506e704730269daf88c87cc8b1afb1992d1876f1c6834c98c580.json
  28. 0 0
      graphify-out/cache/ast/a93b9166f381e9185f91710b64f3cd1e4cfa245991c8de20f34f7091b41ed9d3.json
  29. 0 0
      graphify-out/cache/ast/b77225378ebd8fd347ef71327f45eb3420a348d524dd745a6ecc56469794c09d.json
  30. 1 0
      graphify-out/cache/ast/cfdd874603a05cb73bfa92f1fea8f336bd23e747342a56dd382c3290e8378196.json
  31. 1 0
      graphify-out/cache/ast/e43cd25678dfd2ccae21e2ae65d32eb9fd81fab579c377bf0418261d72273d87.json
  32. 0 0
      graphify-out/cache/ast/ed2a84b6f96fd196ddea2eaff614e80d4a028d4ed1bc19cc8c3db8dbe9c6f914.json
  33. 1 0
      graphify-out/cache/ast/ee98212a550c9865b1f6d970ae5a626758c1f3019ddd900638994b33d29b53f4.json
  34. 1 1
      graphify-out/graph.html
  35. 112 112
      graphify-out/graph.json
  36. 22 10
      sqxp-wx/pages/login/login.vue
  37. 6 20
      sqxp-wx/pages/supplier/inquiry/inquiry.vue
  38. 4 12
      sqxp-wx/pages/supplier/product/detail.vue
  39. 90 0
      sqxp-wx/utils/supplierInquiryTempPrice.js
  40. 4 2
      web-admin/src/api/business.ts
  41. 1 1
      web-admin/src/views/system/client-settlement/index.vue
  42. 78 7
      web-admin/src/views/system/delivery-warning/index.vue
  43. 79 35
      web-admin/src/views/system/delivery/index.vue
  44. 1 1
      web-admin/src/views/system/supplier-settlement/index.vue

+ 20 - 7
backend/app/api/delivery.py

@@ -419,14 +419,26 @@ def get_delivery_task(
             "status": item.status
             "status": item.status
         })
         })
 
 
-    # 处理图片
-    pickup_images = []
-    if task.pickup_image_url:
-        pickup_images = task.pickup_image_url.split(",")
+    # 凭证图片:与运营端订单详情一致(附件表优先,任务字段兜底)
+    from app.crud.attachment import attachment_crud as _attachment_crud
 
 
-    delivery_images = []
-    if task.delivery_image_url:
-        delivery_images = task.delivery_image_url.split(",")
+    prepare_images = [
+        a.file_url for a in _attachment_crud.get_by_business(db, "delivery_prepare", task.id) if a.file_url
+    ]
+    pickup_images = [
+        a.file_url for a in _attachment_crud.get_by_business(db, "delivery_pickup", task.id) if a.file_url
+    ]
+    delivery_images = [
+        a.file_url for a in _attachment_crud.get_by_business(db, "delivery_deliver", task.id) if a.file_url
+    ]
+    if not pickup_images and getattr(task, "pickup_image_url", None):
+        pickup_images = [
+            s.strip() for s in str(task.pickup_image_url).split(",") if s and str(s).strip()
+        ]
+    if not delivery_images and getattr(task, "delivery_image_url", None):
+        delivery_images = [
+            s.strip() for s in str(task.delivery_image_url).split(",") if s and str(s).strip()
+        ]
 
 
     return success_response({
     return success_response({
         "id": task.id,
         "id": task.id,
@@ -458,6 +470,7 @@ def get_delivery_task(
         "assignTime": str(task.create_at) if task.deliver_id else None,
         "assignTime": str(task.create_at) if task.deliver_id else None,
         "pickupTime": str(task.pickup_time) if task.pickup_time else None,
         "pickupTime": str(task.pickup_time) if task.pickup_time else None,
         "pickupRemark": task.pickup_remark,
         "pickupRemark": task.pickup_remark,
+        "prepareImages": prepare_images,
         "pickupImages": pickup_images,
         "pickupImages": pickup_images,
         "deliveryTime": str(task.delivery_time) if task.delivery_time else None,
         "deliveryTime": str(task.delivery_time) if task.delivery_time else None,
         "deliveryRemark": task.delivery_remark,
         "deliveryRemark": task.delivery_remark,

+ 110 - 19
backend/app/api/delivery_warning.py

@@ -1,12 +1,13 @@
 """配送时效预警 API"""
 """配送时效预警 API"""
+import logging
 from typing import Optional, List, Dict
 from typing import Optional, List, Dict
-from fastapi import APIRouter, Depends, Query
+from fastapi import APIRouter, Depends, Query, Request
 from sqlalchemy.orm import Session
 from sqlalchemy.orm import Session
 from sqlalchemy import and_
 from sqlalchemy import and_
-from pydantic import BaseModel, Field
 
 
 from app.core.database import get_db
 from app.core.database import get_db
 from app.core.auth import get_current_user
 from app.core.auth import get_current_user
+from app.core.admin_request_json import parse_admin_request_json
 from app.core.response import success_response, error_response
 from app.core.response import success_response, error_response
 from app.core.utils import generate_id
 from app.core.utils import generate_id
 from app.models.models import BizDeliveryTask, BizDeliveryWarning
 from app.models.models import BizDeliveryTask, BizDeliveryWarning
@@ -23,6 +24,15 @@ main_router = APIRouter(prefix="/web/admin/delivery-warning", tags=["配送时
 legacy_router = APIRouter(prefix="/api", tags=["配送时效预警(兼容)"])
 legacy_router = APIRouter(prefix="/api", tags=["配送时效预警(兼容)"])
 router = APIRouter()
 router = APIRouter()
 
 
+_delivery_warning_log = logging.getLogger("sqxp")
+
+
+def _preview_text(val: Optional[str], limit: int = 160) -> str:
+    if val is None:
+        return ""
+    s = str(val).replace("\r", " ").replace("\n", " ")
+    return s if len(s) <= limit else s[: limit - 3] + "..."
+
 
 
 class DeliveryWarningCRUD(CRUDBase[BizDeliveryWarning]):
 class DeliveryWarningCRUD(CRUDBase[BizDeliveryWarning]):
     """配送时效预警CRUD"""
     """配送时效预警CRUD"""
@@ -82,9 +92,19 @@ class DeliveryWarningCRUD(CRUDBase[BizDeliveryWarning]):
         now = datetime.now()
         now = datetime.now()
         operator_display = operator_name or operator_id or "未知操作人"
         operator_display = operator_name or operator_id or "未知操作人"
         handle_display = handle_type or "manual"
         handle_display = handle_type or "manual"
-        remark_display = remark or "已处理"
+        remark_display = " ".join((remark or "已处理").split())
+        if not remark_display:
+            remark_display = "已处理"
         process_line = f"{now.strftime('%Y-%m-%d %H:%M:%S')}|{operator_display}|{handle_display}|{remark_display}"
         process_line = f"{now.strftime('%Y-%m-%d %H:%M:%S')}|{operator_display}|{handle_display}|{remark_display}"
 
 
+        _delivery_warning_log.info(
+            "delivery_warning.mark_handled warning_id=%s remark_len=%s remark_preview=%r process_line_preview=%r",
+            warning_id,
+            len(remark_display),
+            _preview_text(remark_display, 120),
+            _preview_text(process_line, 200),
+        )
+
         # 仅记录过程,不变更是否已解决
         # 仅记录过程,不变更是否已解决
         old_note = (warning.resolved_note or "").strip()
         old_note = (warning.resolved_note or "").strip()
         warning.resolved_note = f"{old_note}\n{process_line}".strip() if old_note else process_line
         warning.resolved_note = f"{old_note}\n{process_line}".strip() if old_note else process_line
@@ -297,31 +317,91 @@ def get_warning_detail(
 
 
 
 
 @main_router.post("/handle/{warning_id}", response_model=dict)
 @main_router.post("/handle/{warning_id}", response_model=dict)
-def handle_warning(
+async def handle_warning(
     warning_id: str,
     warning_id: str,
-    body: dict = None,
-    handle_type: str = None,
-    remark: str = None,
-    note: str = None,
+    request: Request,
     db: Session = Depends(get_db),
     db: Session = Depends(get_db),
-    current_user: dict = Depends(get_current_user)
+    current_user: dict = Depends(get_current_user),
+    note: Optional[str] = Query(None, description="处理备注(兼容 query,优先请求体)"),
+    remark: Optional[str] = Query(None, description="同 note"),
 ):
 ):
-    """处理预警"""
+    """处理预警(与 ApiCryptoMiddleware / 运营端加密协议一致:Request.json + parse_admin_request_json)"""
     try:
     try:
-        # 兼容前端 JSON body 与 query 参数多种传参方式
-        payload = body or {}
-        handle_type = payload.get("handleType") or payload.get("handle_type") or handle_type
-        remark = payload.get("remark") or payload.get("note") or remark or note
-        if not handle_type:
-            handle_type = "manual"  # 默认手动处理
-        if not remark:
-            remark = "手动处理"
+        uid = current_user.get("user_id")
+        qn = (note or "").strip()
+        qr = (remark or "").strip()
+        client_host = request.client.host if request.client else ""
+        _delivery_warning_log.info(
+            "delivery_warning.handle enter warning_id=%s operator_user_id=%s path=%s query_note_len=%s query_remark_len=%s client=%s",
+            warning_id,
+            uid,
+            request.url.path,
+            len(qn),
+            len(qr),
+            client_host,
+        )
+
+        data, err = await parse_admin_request_json(
+            request, current_user, source="delivery_warning.handle"
+        )
+        _delivery_warning_log.info(
+            "delivery_warning.handle after_parse err=%r data_type=%s",
+            err,
+            type(data).__name__,
+        )
+        if err:
+            _delivery_warning_log.warning(
+                "delivery_warning.handle parse error warning_id=%s err=%s operator_user_id=%s",
+                warning_id,
+                err,
+                uid,
+            )
+            return error_response(400, err)
+        payload = data if isinstance(data, dict) else {}
+        if isinstance(payload, dict):
+            nk = payload.get("note")
+            rk = payload.get("remark")
+            _delivery_warning_log.info(
+                "delivery_warning.handle payload keys=%s note_len=%s remark_len=%s note_preview=%r remark_preview=%r",
+                sorted(payload.keys()),
+                len(str(nk) if nk is not None else ""),
+                len(str(rk) if rk is not None else ""),
+                _preview_text(str(nk) if nk is not None else "", 100),
+                _preview_text(str(rk) if rk is not None else "", 100),
+            )
+        else:
+            _delivery_warning_log.warning(
+                "delivery_warning.handle payload_not_dict warning_id=%s type=%s repr_preview=%r",
+                warning_id,
+                type(payload).__name__,
+                _preview_text(repr(data), 200),
+            )
+
+        handle_type = (payload.get("handleType") or payload.get("handle_type") or "").strip() or "manual"
+        body_note = str(payload.get("remark") or payload.get("note") or "").strip()
+        raw_note = body_note or qn or qr
+        if not raw_note:
+            _delivery_warning_log.warning(
+                "delivery_warning.handle empty remark warning_id=%s operator_user_id=%s payload_keys=%s body_note_len=%s",
+                warning_id,
+                uid,
+                sorted(payload.keys()) if isinstance(payload, dict) else [],
+                len(body_note),
+            )
+            return error_response(400, "请填写处理备注")
+        remark_val = raw_note
+        _delivery_warning_log.info(
+            "delivery_warning.handle resolved handle_type=%s remark_len=%s remark_preview=%r",
+            handle_type,
+            len(remark_val),
+            _preview_text(remark_val, 160),
+        )
 
 
         warning = delivery_warning_crud.mark_handled(
         warning = delivery_warning_crud.mark_handled(
             db=db,
             db=db,
             warning_id=warning_id,
             warning_id=warning_id,
             handle_type=handle_type,
             handle_type=handle_type,
-            remark=remark,
+            remark=remark_val,
             operator_id=current_user.get("user_id"),
             operator_id=current_user.get("user_id"),
             operator_name=current_user.get("real_name")
             operator_name=current_user.get("real_name")
         )
         )
@@ -329,8 +409,19 @@ def handle_warning(
         if not warning:
         if not warning:
             return error_response(404, "预警不存在")
             return error_response(404, "预警不存在")
 
 
+        _delivery_warning_log.info(
+            "delivery_warning.handle success warning_id=%s resolved_note_total_len=%s",
+            warning_id,
+            len((warning.resolved_note or "")),
+        )
         return success_response({"message": "处理记录已保存"})
         return success_response({"message": "处理记录已保存"})
     except Exception as e:
     except Exception as e:
+        _delivery_warning_log.exception(
+            "delivery_warning.handle failed warning_id=%s operator_user_id=%s err=%s",
+            warning_id,
+            current_user.get("user_id"),
+            str(e),
+        )
         return error_response(500, f"处理预警失败: {str(e)}")
         return error_response(500, f"处理预警失败: {str(e)}")
 
 
 
 

+ 3 - 3
backend/app/api/mini_deliverer.py

@@ -1,6 +1,6 @@
 """配送员端接口"""
 """配送员端接口"""
 from typing import Optional
 from typing import Optional
-from datetime import datetime
+from datetime import datetime, timedelta
 from fastapi import APIRouter, Depends, Query
 from fastapi import APIRouter, Depends, Query
 from sqlalchemy.orm import Session
 from sqlalchemy.orm import Session
 
 
@@ -310,7 +310,7 @@ def get_deliverer_statistics(
     today_completed = [t for t in completed_tasks if t.delivery_time and t.delivery_time >= today]
     today_completed = [t for t in completed_tasks if t.delivery_time and t.delivery_time >= today]
 
 
     # 本周已完成
     # 本周已完成
-    week_start = today.replace(day=today.day - today.weekday())
+    week_start = today - timedelta(days=today.weekday())
     week_completed = [t for t in completed_tasks if t.delivery_time and t.delivery_time >= week_start]
     week_completed = [t for t in completed_tasks if t.delivery_time and t.delivery_time >= week_start]
 
 
     # 本月已完成
     # 本月已完成
@@ -762,7 +762,7 @@ def get_deliverer_efficiency_statistics(
     # 时间范围
     # 时间范围
     now = datetime.now()
     now = datetime.now()
     today = now.replace(hour=0, minute=0, second=0, microsecond=0)
     today = now.replace(hour=0, minute=0, second=0, microsecond=0)
-    week_start = today.replace(day=today.day - today.weekday())
+    week_start = today - timedelta(days=today.weekday())
     month_start = today.replace(day=1)
     month_start = today.replace(day=1)
 
 
     # 获取已完成任务(按配送员状态)
     # 获取已完成任务(按配送员状态)

+ 44 - 1
backend/app/core/admin_request_json.py

@@ -6,12 +6,15 @@
 
 
 from __future__ import annotations
 from __future__ import annotations
 
 
+import logging
 from typing import Any, Optional, Tuple
 from typing import Any, Optional, Tuple
 
 
 from fastapi import Request
 from fastapi import Request
 
 
 from app.core.request_payload import resolve_request_payload
 from app.core.request_payload import resolve_request_payload
 
 
+_log = logging.getLogger("sqxp")
+
 
 
 async def parse_admin_request_json(
 async def parse_admin_request_json(
     request: Request,
     request: Request,
@@ -26,10 +29,50 @@ async def parse_admin_request_json(
         (payload, None) 成功;
         (payload, None) 成功;
         (_, err_msg) 密文解密失败(此时 payload 为原始 raw,调用方可忽略)。
         (_, err_msg) 密文解密失败(此时 payload 为原始 raw,调用方可忽略)。
     """
     """
-    raw: Any = await request.json()
+    dw = source.startswith("delivery_warning")
+    try:
+        raw: Any = await request.json()
+    except Exception as ex:
+        if dw:
+            _log.warning("parse_admin_request_json source=%s request.json() failed: %s", source, ex)
+        raw = {}
     if isinstance(raw, dict):
     if isinstance(raw, dict):
+        if dw:
+            ed = raw.get("edata")
+            _log.info(
+                "parse_admin_request_json source=%s raw_keys=%s enc=%s edata_len=%s user_id=%s",
+                source,
+                sorted(raw.keys()),
+                int(raw.get("enc", 0) or 0),
+                len(ed) if isinstance(ed, str) else (len(str(ed)) if ed is not None else 0),
+                current_user.get("user_id"),
+            )
         resolved = resolve_request_payload(raw, current_user, source=source)
         resolved = resolve_request_payload(raw, current_user, source=source)
+        if dw:
+            if isinstance(resolved, dict):
+                _log.info(
+                    "parse_admin_request_json source=%s resolved_keys=%s",
+                    source,
+                    sorted(resolved.keys()),
+                )
+            else:
+                _log.info(
+                    "parse_admin_request_json source=%s resolved_type=%s",
+                    source,
+                    type(resolved).__name__,
+                )
         if resolved == {} and int(raw.get("enc", 0) or 0) == 1:
         if resolved == {} and int(raw.get("enc", 0) or 0) == 1:
+            if dw:
+                _log.warning(
+                    "parse_admin_request_json source=%s treating as decrypt failure (resolved empty, enc=1)",
+                    source,
+                )
             return raw, "密文解密失败"
             return raw, "密文解密失败"
         return resolved, None
         return resolved, None
+    if dw:
+        _log.warning(
+            "parse_admin_request_json source=%s raw_not_dict type=%s",
+            source,
+            type(raw).__name__,
+        )
     return raw, None
     return raw, None

+ 20 - 0
backend/app/core/request_payload.py

@@ -43,9 +43,29 @@ def resolve_request_payload(
         except Exception as exc:
         except Exception as exc:
             logger.warning("%s resolve_request_payload decrypt failed: err=%s", source, exc)
             logger.warning("%s resolve_request_payload decrypt failed: err=%s", source, exc)
             return {}
             return {}
+    dw = source.startswith("delivery_warning")
     if isinstance(payload, dict):
     if isinstance(payload, dict):
         inner = payload.get("data")
         inner = payload.get("data")
         if isinstance(inner, (dict, list)):
         if isinstance(inner, (dict, list)):
+            if dw:
+                if isinstance(inner, dict):
+                    logger.info(
+                        "%s unwrap: using inner dict keys=%s note_len=%s remark_len=%s",
+                        source,
+                        sorted(inner.keys()),
+                        len(str(inner.get("note") or "")),
+                        len(str(inner.get("remark") or "")),
+                    )
+                else:
+                    logger.info("%s unwrap: inner is list len=%s", source, len(inner))
             return inner
             return inner
+        if dw:
+            logger.info(
+                "%s no_inner_data: payload_keys=%s (no dict/list data wrapper)",
+                source,
+                sorted(payload.keys()),
+            )
+    elif dw:
+        logger.info("%s payload_not_dict type=%s", source, type(payload).__name__)
     return payload if isinstance(payload, dict) else {}
     return payload if isinstance(payload, dict) else {}
 
 

+ 214 - 140
graphify-out/GRAPH_REPORT.md

@@ -1,12 +1,12 @@
 # Graph Report - sqxp_副本2  (2026-05-02)
 # Graph Report - sqxp_副本2  (2026-05-02)
 
 
 ## Corpus Check
 ## Corpus Check
-- 702 files · ~642,948 words
+- 703 files · ~644,109 words
 - Verdict: corpus is large enough that graph structure adds value.
 - Verdict: corpus is large enough that graph structure adds value.
 
 
 ## Summary
 ## Summary
-- 4980 nodes · 26716 edges · 65 communities detected
-- Extraction: 19% EXTRACTED · 81% INFERRED · 0% AMBIGUOUS · INFERRED: 21528 edges (avg confidence: 0.54)
+- 4995 nodes · 26760 edges · 79 communities detected
+- Extraction: 19% EXTRACTED · 81% INFERRED · 0% AMBIGUOUS · INFERRED: 21565 edges (avg confidence: 0.54)
 - Token cost: 0 input · 0 output
 - Token cost: 0 input · 0 output
 
 
 ## Community Hubs (Navigation)
 ## Community Hubs (Navigation)
@@ -31,50 +31,64 @@
 - [[_COMMUNITY_Community 18|Community 18]]
 - [[_COMMUNITY_Community 18|Community 18]]
 - [[_COMMUNITY_Community 19|Community 19]]
 - [[_COMMUNITY_Community 19|Community 19]]
 - [[_COMMUNITY_Community 20|Community 20]]
 - [[_COMMUNITY_Community 20|Community 20]]
+- [[_COMMUNITY_Community 21|Community 21]]
+- [[_COMMUNITY_Community 22|Community 22]]
 - [[_COMMUNITY_Community 23|Community 23]]
 - [[_COMMUNITY_Community 23|Community 23]]
 - [[_COMMUNITY_Community 24|Community 24]]
 - [[_COMMUNITY_Community 24|Community 24]]
 - [[_COMMUNITY_Community 25|Community 25]]
 - [[_COMMUNITY_Community 25|Community 25]]
 - [[_COMMUNITY_Community 26|Community 26]]
 - [[_COMMUNITY_Community 26|Community 26]]
+- [[_COMMUNITY_Community 27|Community 27]]
 - [[_COMMUNITY_Community 28|Community 28]]
 - [[_COMMUNITY_Community 28|Community 28]]
+- [[_COMMUNITY_Community 29|Community 29]]
 - [[_COMMUNITY_Community 30|Community 30]]
 - [[_COMMUNITY_Community 30|Community 30]]
 - [[_COMMUNITY_Community 31|Community 31]]
 - [[_COMMUNITY_Community 31|Community 31]]
 - [[_COMMUNITY_Community 32|Community 32]]
 - [[_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 36|Community 36]]
 - [[_COMMUNITY_Community 37|Community 37]]
 - [[_COMMUNITY_Community 37|Community 37]]
 - [[_COMMUNITY_Community 38|Community 38]]
 - [[_COMMUNITY_Community 38|Community 38]]
-- [[_COMMUNITY_Community 39|Community 39]]
 - [[_COMMUNITY_Community 40|Community 40]]
 - [[_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 46|Community 46]]
 - [[_COMMUNITY_Community 48|Community 48]]
 - [[_COMMUNITY_Community 48|Community 48]]
 - [[_COMMUNITY_Community 49|Community 49]]
 - [[_COMMUNITY_Community 49|Community 49]]
 - [[_COMMUNITY_Community 50|Community 50]]
 - [[_COMMUNITY_Community 50|Community 50]]
 - [[_COMMUNITY_Community 51|Community 51]]
 - [[_COMMUNITY_Community 51|Community 51]]
 - [[_COMMUNITY_Community 52|Community 52]]
 - [[_COMMUNITY_Community 52|Community 52]]
-- [[_COMMUNITY_Community 53|Community 53]]
-- [[_COMMUNITY_Community 55|Community 55]]
 - [[_COMMUNITY_Community 56|Community 56]]
 - [[_COMMUNITY_Community 56|Community 56]]
-- [[_COMMUNITY_Community 60|Community 60]]
+- [[_COMMUNITY_Community 59|Community 59]]
+- [[_COMMUNITY_Community 61|Community 61]]
+- [[_COMMUNITY_Community 62|Community 62]]
 - [[_COMMUNITY_Community 63|Community 63]]
 - [[_COMMUNITY_Community 63|Community 63]]
 - [[_COMMUNITY_Community 64|Community 64]]
 - [[_COMMUNITY_Community 64|Community 64]]
+- [[_COMMUNITY_Community 65|Community 65]]
 - [[_COMMUNITY_Community 66|Community 66]]
 - [[_COMMUNITY_Community 66|Community 66]]
-- [[_COMMUNITY_Community 78|Community 78]]
-- [[_COMMUNITY_Community 88|Community 88]]
-- [[_COMMUNITY_Community 90|Community 90]]
-- [[_COMMUNITY_Community 142|Community 142]]
-- [[_COMMUNITY_Community 143|Community 143]]
-- [[_COMMUNITY_Community 144|Community 144]]
-- [[_COMMUNITY_Community 145|Community 145]]
-- [[_COMMUNITY_Community 146|Community 146]]
-- [[_COMMUNITY_Community 147|Community 147]]
-- [[_COMMUNITY_Community 415|Community 415]]
-- [[_COMMUNITY_Community 416|Community 416]]
-- [[_COMMUNITY_Community 417|Community 417]]
-- [[_COMMUNITY_Community 418|Community 418]]
-- [[_COMMUNITY_Community 419|Community 419]]
-- [[_COMMUNITY_Community 420|Community 420]]
-- [[_COMMUNITY_Community 421|Community 421]]
+- [[_COMMUNITY_Community 68|Community 68]]
+- [[_COMMUNITY_Community 69|Community 69]]
+- [[_COMMUNITY_Community 73|Community 73]]
+- [[_COMMUNITY_Community 76|Community 76]]
+- [[_COMMUNITY_Community 77|Community 77]]
+- [[_COMMUNITY_Community 79|Community 79]]
+- [[_COMMUNITY_Community 91|Community 91]]
+- [[_COMMUNITY_Community 101|Community 101]]
+- [[_COMMUNITY_Community 103|Community 103]]
+- [[_COMMUNITY_Community 155|Community 155]]
+- [[_COMMUNITY_Community 156|Community 156]]
+- [[_COMMUNITY_Community 157|Community 157]]
+- [[_COMMUNITY_Community 158|Community 158]]
+- [[_COMMUNITY_Community 159|Community 159]]
+- [[_COMMUNITY_Community 160|Community 160]]
+- [[_COMMUNITY_Community 428|Community 428]]
+- [[_COMMUNITY_Community 429|Community 429]]
+- [[_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]]
 
 
 ## God Nodes (most connected - your core abstractions)
 ## God Nodes (most connected - your core abstractions)
 1. `BizPriceListItem` - 800 edges
 1. `BizPriceListItem` - 800 edges
@@ -82,10 +96,10 @@
 3. `BizQuotation` - 727 edges
 3. `BizQuotation` - 727 edges
 4. `BizAttachment` - 717 edges
 4. `BizAttachment` - 717 edges
 5. `BasProduct` - 716 edges
 5. `BasProduct` - 716 edges
-6. `SysUser` - 707 edges
-7. `BasProductCategory` - 706 edges
-8. `BasProductUnit` - 706 edges
-9. `BizDeliveryTask` - 699 edges
+6. `BizDeliveryTask` - 708 edges
+7. `SysUser` - 707 edges
+8. `BasProductCategory` - 706 edges
+9. `BasProductUnit` - 706 edges
 10. `BizOrder` - 696 edges
 10. `BizOrder` - 696 edges
 
 
 ## Surprising Connections (you probably didn't know these)
 ## Surprising Connections (you probably didn't know these)
@@ -97,344 +111,404 @@
   web-admin/src/views/system/inquiry-quotation/index.vue → sqxp-wx/uni_modules/uni-easyinput/components/uni-easyinput/uni-easyinput.vue
   web-admin/src/views/system/inquiry-quotation/index.vue → sqxp-wx/uni_modules/uni-easyinput/components/uni-easyinput/uni-easyinput.vue
 - `number()` --calls--> `mounted()`  [INFERRED]
 - `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
   web-admin/src/views/system/inquiry-quotation/index.vue → sqxp-wx/uni_modules/uni-table/components/uni-tr/uni-tr.vue
-- `showMenu()` --calls--> `nextTick()`  [INFERRED]
-  web-admin/src/views/widgets/context-menu/index.vue → sqxp-wx/wxcomponents/vant/common/utils.js
+- `number()` --calls--> `handler()`  [INFERRED]
+  web-admin/src/views/system/inquiry-quotation/index.vue → sqxp-wx/uni_modules/uni-pagination/components/uni-pagination/uni-pagination.vue
 
 
 ## Communities
 ## Communities
 
 
 ### Community 0 - "Community 0"
 ### Community 0 - "Community 0"
-Cohesion: 0.01
-Nodes (826): 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() (+818 more)
-
-### Community 1 - "Community 1"
 Cohesion: 0.07
 Cohesion: 0.07
 Nodes (710): 获取管理员的区域过滤条件      对于区域管理员,返回其所属区域ID(用于过滤数据)     对于超级管理员(无区域绑定),返回None(不过滤), 更新用户菜单授权(按 code 全量覆盖;兼容明文/密文载荷)。, 更新用户菜单授权(按 code 全量覆盖;兼容明文/密文载荷)。, 与 PUT 同逻辑:前端按 POST 模式调用时复用。, 兼容旧逻辑:返回单区域过滤ID。多区域场景返回排序后首个区域。, 更新用户菜单授权(按 code 全量覆盖;兼容明文/密文载荷)。, 兼容旧逻辑:返回单区域过滤ID。多区域场景返回排序后首个区域。, 更新用户菜单授权(按 code 全量覆盖;兼容明文/密文载荷)。 (+702 more)
 Nodes (710): 获取管理员的区域过滤条件      对于区域管理员,返回其所属区域ID(用于过滤数据)     对于超级管理员(无区域绑定),返回None(不过滤), 更新用户菜单授权(按 code 全量覆盖;兼容明文/密文载荷)。, 更新用户菜单授权(按 code 全量覆盖;兼容明文/密文载荷)。, 与 PUT 同逻辑:前端按 POST 模式调用时复用。, 兼容旧逻辑:返回单区域过滤ID。多区域场景返回排序后首个区域。, 更新用户菜单授权(按 code 全量覆盖;兼容明文/密文载荷)。, 兼容旧逻辑:返回单区域过滤ID。多区域场景返回排序后首个区域。, 更新用户菜单授权(按 code 全量覆盖;兼容明文/密文载荷)。 (+702 more)
 
 
+### Community 1 - "Community 1"
+Cohesion: 0.01
+Nodes (203): fullRegion(), if(), if(), FireworkSystem, mapKeys(), VantComponent(), HTMLParser(), parseAttrs() (+195 more)
+
 ### Community 2 - "Community 2"
 ### Community 2 - "Community 2"
 Cohesion: 0.01
 Cohesion: 0.01
-Nodes (205): fullRegion(), if(), if(), FireworkSystem, mapKeys(), VantComponent(), HTMLParser(), parseAttrs() (+197 more)
+Nodes (202): handleSubmit(), loadDetail(), onRegionMultiConfirm(), regionLabel(), syncMultiFromForm(), validate(), fetchActiveRegions(), fetchAssignableMenuCodes() (+194 more)
 
 
 ### Community 3 - "Community 3"
 ### Community 3 - "Community 3"
 Cohesion: 0.01
 Cohesion: 0.01
-Nodes (205): handleSubmit(), loadDetail(), onRegionMultiConfirm(), regionLabel(), syncMultiFromForm(), validate(), fetchActiveRegions(), fetchAssignableMenuCodes() (+197 more)
+Nodes (157): calendar(), memo(), record(), handleSelect(), onMenuShow(), showMenu(), onClear(), onInput() (+149 more)
 
 
 ### Community 4 - "Community 4"
 ### Community 4 - "Community 4"
-Cohesion: 0.01
-Nodes (158): calendar(), memo(), record(), handleSelect(), onMenuShow(), showMenu(), onClear(), onInput() (+150 more)
+Cohesion: 0.03
+Nodes (259): _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() (+251 more)
 
 
 ### Community 5 - "Community 5"
 ### Community 5 - "Community 5"
 Cohesion: 0.01
 Cohesion: 0.01
-Nodes (274): InvoiceCreateRequest, MessageCreateRequest, 获取商品的评价列表(仅显示已审核通过的评价), AdjustResponse, Config, CreateAdjustRequest, 批量添加轮播图(请求体为 JSON 数组;兼容密文信封与 ApiCryptoMiddleware 未改写 body 的场景), 批量添加轮播图(请求体为 JSON 数组;兼容密文信封与 ApiCryptoMiddleware 未改写 body 的场景) (+266 more)
+Nodes (244): InvoiceCreateRequest, MessageCreateRequest, 获取商品的评价列表(仅显示已审核通过的评价), AdjustResponse, Config, CreateAdjustRequest, 批量添加轮播图(请求体为 JSON 数组;兼容密文信封与 ApiCryptoMiddleware 未改写 body 的场景), 批量添加轮播图(请求体为 JSON 数组;兼容密文信封与 ApiCryptoMiddleware 未改写 body 的场景) (+236 more)
 
 
 ### Community 6 - "Community 6"
 ### Community 6 - "Community 6"
 Cohesion: 0.02
 Cohesion: 0.02
-Nodes (63): fetchGetMenuCodes(), fetchGetUserInfo(), fetchLogin(), fetchGetMenuList(), fetchGetRoleList(), fetchGetUserList(), reload(), addUnit() (+55 more)
+Nodes (70): fetchGetMenuCodes(), fetchGetUserInfo(), fetchLogin(), fetchGetMenuList(), fetchGetRoleList(), fetchGetUserList(), reload(), addUnit() (+62 more)
 
 
 ### Community 7 - "Community 7"
 ### Community 7 - "Community 7"
-Cohesion: 0.03
-Nodes (99): 小程序客户端扩展接口:购物车、商品分类、仪表盘、发票、下单、支付, 从叶子分类 id 向上解析(含缓存):返回 (根分类, 叶子分类)。, 可申请开票的已完成订单(简化:已完成且尚无发票), 可申请开票的已完成订单(简化:已完成且尚无发票), 可申请开票的已完成订单(简化:已完成且尚无发票), 商品对应的一级(根)分类,供购物车行展示与序列化。, 从叶子分类 id 向上解析(含缓存):返回 (根分类, 叶子分类)。, 购物车:先一级分类 sort_order,再商品名、商品 id(稳定)。 (+91 more)
+Cohesion: 0.02
+Nodes (100): _build_order_delivery_timeline(), _delivery_timeline_type(), get_today_orders_by_client(), _log_entry_in_delivery_timeline(), require_admin(), _status_log_timeline_description(), fetchAdjustableOrders(), fetchClientSettlementHistorySummary() (+92 more)
 
 
 ### Community 8 - "Community 8"
 ### Community 8 - "Community 8"
-Cohesion: 0.03
-Nodes (55): handleFormClose(), handleClose(), initForm(), resetFormModel(), _Blur(), created(), init(), inputContentStyle() (+47 more)
+Cohesion: 0.02
+Nodes (103): 小程序客户端扩展接口:购物车、商品分类、仪表盘、发票、下单、支付, 从叶子分类 id 向上解析(含缓存):返回 (根分类, 叶子分类)。, 可申请开票的已完成订单(简化:已完成且尚无发票), 可申请开票的已完成订单(简化:已完成且尚无发票), 可申请开票的已完成订单(简化:已完成且尚无发票), 商品对应的一级(根)分类,供购物车行展示与序列化。, 从叶子分类 id 向上解析(含缓存):返回 (根分类, 叶子分类)。, 购物车:先一级分类 sort_order,再商品名、商品 id(稳定)。 (+95 more)
 
 
 ### Community 9 - "Community 9"
 ### Community 9 - "Community 9"
-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)
+Cohesion: 0.03
+Nodes (76): update_detail_media(), 获取中国地图GeoJSON数据     返回保存在系统配置中的地图数据, Base, ApiRequestLogCRUD, CategoryParameterCRUD, CategoryParameterHisCRUD, 将购物车中仍属于当前生效价格清单的商品行,同步为区域最新生效清单的单价、单位与清单 ID。         同一商品多条行(历史清单 ID 不同)合并数量并保留, InquiryCRUD (+68 more)
 
 
 ### Community 10 - "Community 10"
 ### Community 10 - "Community 10"
 Cohesion: 0.03
 Cohesion: 0.03
-Nodes (43): askAbortDownload(), checkLocalStoragePackage(), closeUpdate(), compare(), deleteSavedFile(), downLoadComplete(), installPackage(), onLoad() (+35 more)
+Nodes (65): adjust_order(), create_price_version(), get_dashboard_statistics(), get_today_orders_by_region(), list_products_simple(), list_units(), delivery_complete(), get_deliver_profile() (+57 more)
 
 
 ### Community 11 - "Community 11"
 ### Community 11 - "Community 11"
-Cohesion: 0.05
-Nodes (15): fetchGetLatestUpgradeLog(), if(), getDefaultLanguage(), emitUrls(), removeItem(), revokeLocalPreview(), checkStorageCompatibility(), StorageConfig (+7 more)
+Cohesion: 0.03
+Nodes (54): handleFormClose(), handleClose(), initForm(), resetFormModel(), _Blur(), created(), init(), inputContentStyle() (+46 more)
 
 
 ### Community 12 - "Community 12"
 ### Community 12 - "Community 12"
+Cohesion: 0.04
+Nodes (78): DeliveryWarningCRUD, 处理预警请求体(与前端 JSON / axios 自动转 body 对齐), 处理预警(兼容明文 JSON、密文信封 enc/edata,以及 { data: {...} } 包裹), 处理预警(与 ApiCryptoMiddleware / 运营端加密协议一致:Request.json + parse_admin_request_json), 处理预警(与 ApiCryptoMiddleware / 运营端加密协议一致:Request.json + parse_admin_request_json), # TODO: 发送微信模板消息提醒配送员, # TODO: 发送微信模板消息提醒配送员, # TODO: 发送微信模板消息提醒配送员 (+70 more)
+
+### Community 13 - "Community 13"
+Cohesion: 0.04
+Nodes (34): post_user_menu_scopes(), put_user_menu_scopes(), _update_user_menu_scopes(), assign_delivery_task(), assign_delivery_task_batch(), InvoiceCRUD, BizMessage, client_urge_order() (+26 more)
+
+### Community 14 - "Community 14"
+Cohesion: 0.03
+Nodes (17): toLogin(), if(), confirmLogout(), getDefaultLanguage(), emitUrls(), removeItem(), revokeLocalPreview(), checkStorageCompatibility() (+9 more)
+
+### Community 15 - "Community 15"
+Cohesion: 0.05
+Nodes (65): change_password(), get_client_ip(), _avatar_url_field(), get_current_user(), logout(), mini_bind_wechat(), mini_change_password(), mini_code_openid() (+57 more)
+
+### Community 16 - "Community 16"
+Cohesion: 0.05
+Nodes (77): 批量获取供应商取货地址,避免列表接口逐条查询。, 一级/二级分类用 / 连接(与订单详情一致)。, 价格清单行 price 为面向客户的最终单价;pricing_coefficient 为调价系数(%),供货单价 = price / (1 + 系数/100)。, (price_list_id, product_id, unit_id) -> BizPriceListItem, 供应商默认取货地址;无默认时取第一条 pickup 类型地址。, 获取供应商配送任务详情(订单仅返回客户订单号;商品行含主图与规格;单价由价格清单行 price(客户最终单价)与 pricing_coefficient 反推供, 获取供应商配送任务详情(订单仅返回客户订单号;商品行含主图与规格;单价由价格清单行 price(客户最终单价)与 pricing_coefficient 反推供, 获取供应商配送任务详情(订单仅返回客户订单号;商品行含主图与规格;单价由价格清单行 price(客户最终单价)与 pricing_coefficient 反推供 (+69 more)
+
+### Community 17 - "Community 17"
+Cohesion: 0.04
+Nodes (33): get_category_parameters(), update_category_parameters(), create_review(), get_product_review_stats(), like_review(), list_product_reviews(), require_client_or_supplier(), approve_review() (+25 more)
+
+### Community 18 - "Community 18"
+Cohesion: 0.03
+Nodes (43): askAbortDownload(), checkLocalStoragePackage(), closeUpdate(), compare(), deleteSavedFile(), downLoadComplete(), installPackage(), onLoad() (+35 more)
+
+### Community 19 - "Community 19"
+Cohesion: 0.04
+Nodes (58): _supplier_pickup_address_block(), delete_attachment(), get_business_attachments(), get_business_types(), list_attachments(), _batch_supplier_pickup_address_map(), _category_path_display(), get_delivery_task() (+50 more)
+
+### Community 20 - "Community 20"
 Cohesion: 0.05
 Cohesion: 0.05
 Nodes (22): change(), created(), getDataList(), getSelectedValue(), handler(), loadData(), mixinDatacomResData(), modelValue() (+14 more)
 Nodes (22): change(), created(), getDataList(), getSelectedValue(), handler(), loadData(), mixinDatacomResData(), modelValue() (+14 more)
 
 
-### Community 13 - "Community 13"
+### Community 21 - "Community 21"
+Cohesion: 0.1
+Nodes (31): create_order(), add_cart(), _cart_item_to_dict(), _cart_list_dicts(), _cart_resolve_root_category(), _cart_row_sort_key(), _cart_rows_after_sync(), cart_summary() (+23 more)
+
+### Community 22 - "Community 22"
+Cohesion: 0.08
+Nodes (30): submit_quotation(), BaseHTTPMiddleware, ApiCryptoError, _b64url_decode(), _b64url_encode(), decrypt_json(), derive_key(), encrypt_json() (+22 more)
+
+### Community 23 - "Community 23"
 Cohesion: 0.09
 Cohesion: 0.09
-Nodes (27): base64ToBytes(), bytesToBase64(), decryptJson(), deriveKey(), encryptJson(), fromBase64Url(), hexToBytes(), sha256Hex() (+19 more)
+Nodes (26): handle_warning(), _preview_text(), get_client_product_detail(), get_client_product_detail_readonly(), get_deliverer_product_detail(), 配送员查看商品详情:轮播、基础信息、参数、富文本、详情媒体(与供应商端一致,不依赖客户价目表/区域绑定), create_banners(), delete_banner() (+18 more)
 
 
-### Community 14 - "Community 14"
+### Community 24 - "Community 24"
 Cohesion: 0.18
 Cohesion: 0.18
 Nodes (21): destroyed(), getDirection(), getDom(), isPC(), mousedown(), mouseleave(), mousemove(), mouseup() (+13 more)
 Nodes (21): destroyed(), getDirection(), getDom(), isPC(), mousedown(), mouseleave(), mousemove(), mouseup() (+13 more)
 
 
-### Community 15 - "Community 15"
+### Community 25 - "Community 25"
 Cohesion: 0.13
 Cohesion: 0.13
 Nodes (13): change(), clearVal(), created(), debounce(), emit(), formatItemName(), getCache(), handler() (+5 more)
 Nodes (13): change(), clearVal(), created(), debounce(), emit(), formatItemName(), getCache(), handler() (+5 more)
 
 
-### Community 16 - "Community 16"
+### Community 26 - "Community 26"
+Cohesion: 0.15
+Nodes (16): create_parameter(), delete_parameter(), 兼容明文/密文请求,返回业务 payload。失败时返回 error_response。, require_admin(), _resolve_payload_with_crypto_fallback(), update_parameter(), ProductParameterCRUD, ProductParameterHisCRUD (+8 more)
+
+### Community 27 - "Community 27"
 Cohesion: 0.12
 Cohesion: 0.12
 Nodes (15): addCopyButton(), highlightCode(), insertLineNumbers(), isBlockProcessed(), markBlockAsProcessed(), processBlock(), setupHighlightDirective(), setupRippleDirective() (+7 more)
 Nodes (15): addCopyButton(), highlightCode(), insertLineNumbers(), isBlockProcessed(), markBlockAsProcessed(), processBlock(), setupHighlightDirective(), setupRippleDirective() (+7 more)
 
 
-### Community 17 - "Community 17"
+### Community 28 - "Community 28"
 Cohesion: 0.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)
 Nodes (12): BusinessException, InvalidStatusTransitionException, log_delivery_operation(), log_operation(), log_order_operation(), log_settlement_operation(), log_user_operation(), OrderLockedException (+4 more)
 
 
-### Community 18 - "Community 18"
+### Community 29 - "Community 29"
+Cohesion: 0.17
+Nodes (16): animation(), bindTiming(), close(), getDom(), getEl(), getSelectorQuery(), init(), leftOptions() (+8 more)
+
+### Community 30 - "Community 30"
 Cohesion: 0.16
 Cohesion: 0.16
 Nodes (10): useTable(), useTableImpl(), getColumnChecks(), useTableColumns(), createErrorHandler(), createSmartDebounce(), defaultResponseAdapter(), extractPagination() (+2 more)
 Nodes (10): useTable(), useTableImpl(), getColumnChecks(), useTableColumns(), createErrorHandler(), createSmartDebounce(), defaultResponseAdapter(), extractPagination() (+2 more)
 
 
-### Community 19 - "Community 19"
+### Community 31 - "Community 31"
 Cohesion: 0.2
 Cohesion: 0.2
 Nodes (14): defaultAvatarPathByRole(), normalizeAvatarUrlForMini(), resolveUserAvatarUrl(), appendQueryParam(), fallbackVersionFromManifest(), getStaticAssetsVersion(), hasSignedQuery(), hasVersionParam() (+6 more)
 Nodes (14): defaultAvatarPathByRole(), normalizeAvatarUrlForMini(), resolveUserAvatarUrl(), appendQueryParam(), fallbackVersionFromManifest(), getStaticAssetsVersion(), hasSignedQuery(), hasVersionParam() (+6 more)
 
 
-### Community 20 - "Community 20"
+### Community 32 - "Community 32"
 Cohesion: 0.28
 Cohesion: 0.28
 Nodes (14): getDirection(), getDisabledType(), getDom(), move(), moveDirection(), openState(), range(), resetTouchStatus() (+6 more)
 Nodes (14): getDirection(), getDisabledType(), getDom(), move(), moveDirection(), openState(), range(), resetTouchStatus() (+6 more)
 
 
-### Community 23 - "Community 23"
+### Community 35 - "Community 35"
 Cohesion: 0.17
 Cohesion: 0.17
 Nodes (2): clean(), onInput()
 Nodes (2): clean(), onInput()
 
 
-### Community 24 - "Community 24"
+### Community 36 - "Community 36"
 Cohesion: 0.24
 Cohesion: 0.24
 Nodes (4): getVal(), leftIconWidth(), navbarHeight(), rightIconWidth()
 Nodes (4): getVal(), leftIconWidth(), navbarHeight(), rightIconWidth()
 
 
-### Community 25 - "Community 25"
+### Community 37 - "Community 37"
 Cohesion: 0.4
 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)
 Nodes (9): backup_table(), count_affected(), get_text_columns(), main(), make_backup_table_name(), parse_args(), quote_ident(), 生成备份表名,控制在 MySQL 64 字符限制内。 (+1 more)
 
 
-### Community 26 - "Community 26"
+### Community 38 - "Community 38"
 Cohesion: 0.24
 Cohesion: 0.24
 Nodes (3): _calcValue(), _getDecimalScale(), _onBlur()
 Nodes (3): _calcValue(), _getDecimalScale(), _onBlur()
 
 
-### Community 28 - "Community 28"
+### Community 40 - "Community 40"
 Cohesion: 0.27
 Cohesion: 0.27
 Nodes (4): dataValue(), emit(), onChange(), setOpen()
 Nodes (4): dataValue(), emit(), onChange(), setOpen()
 
 
-### Community 30 - "Community 30"
+### Community 42 - "Community 42"
 Cohesion: 0.25
 Cohesion: 0.25
 Nodes (2): borderStyle(), value2px()
 Nodes (2): borderStyle(), value2px()
 
 
-### Community 31 - "Community 31"
+### Community 43 - "Community 43"
 Cohesion: 0.29
 Cohesion: 0.29
 Nodes (3): BaseModel, get_active(), SoftDeleteModel
 Nodes (3): BaseModel, get_active(), SoftDeleteModel
 
 
-### Community 32 - "Community 32"
+### Community 44 - "Community 44"
 Cohesion: 0.39
 Cohesion: 0.39
 Nodes (6): isIgnorableScriptError(), normalizeErrorMessage(), registerPromiseErrorHandler(), registerResourceErrorHandler(), scriptErrorHandler(), setupErrorHandle()
 Nodes (6): isIgnorableScriptError(), normalizeErrorMessage(), registerPromiseErrorHandler(), registerResourceErrorHandler(), scriptErrorHandler(), setupErrorHandle()
 
 
-### Community 33 - "Community 33"
+### Community 45 - "Community 45"
 Cohesion: 0.29
 Cohesion: 0.29
 Nodes (2): handleTouchEnd(), setTransition()
 Nodes (2): handleTouchEnd(), setTransition()
 
 
-### Community 34 - "Community 34"
+### Community 46 - "Community 46"
 Cohesion: 0.36
 Cohesion: 0.36
 Nodes (1): TableHeightCalculator
 Nodes (1): TableHeightCalculator
 
 
-### Community 36 - "Community 36"
+### Community 48 - "Community 48"
 Cohesion: 0.36
 Cohesion: 0.36
 Nodes (1): RouteValidator
 Nodes (1): RouteValidator
 
 
-### Community 37 - "Community 37"
+### Community 49 - "Community 49"
 Cohesion: 0.39
 Cohesion: 0.39
 Nodes (5): chooseAll(), chooseAndUploadFile(), chooseImage(), chooseVideo(), uploadFiles()
 Nodes (5): chooseAll(), chooseAndUploadFile(), chooseImage(), chooseVideo(), uploadFiles()
 
 
-### Community 38 - "Community 38"
+### Community 50 - "Community 50"
 Cohesion: 0.33
 Cohesion: 0.33
 Nodes (2): logApiCryptoBootInfo(), maskSecret()
 Nodes (2): logApiCryptoBootInfo(), maskSecret()
 
 
-### Community 39 - "Community 39"
+### Community 51 - "Community 51"
 Cohesion: 0.38
 Cohesion: 0.38
 Nodes (4): fetchApiRequestLogList(), handleReset(), handleSearch(), loadData()
 Nodes (4): fetchApiRequestLogList(), handleReset(), handleSearch(), loadData()
 
 
-### Community 40 - "Community 40"
+### Community 52 - "Community 52"
 Cohesion: 0.38
 Cohesion: 0.38
 Nodes (3): _change(), close(), open()
 Nodes (3): _change(), close(), open()
 
 
-### Community 46 - "Community 46"
+### Community 56 - "Community 56"
+Cohesion: 0.4
+Nodes (1): CategoryCRUD
+
+### Community 59 - "Community 59"
 Cohesion: 0.33
 Cohesion: 0.33
 Nodes (1): BridgeError
 Nodes (1): BridgeError
 
 
-### Community 48 - "Community 48"
+### Community 61 - "Community 61"
 Cohesion: 0.4
 Cohesion: 0.4
 Nodes (2): created(), getTable()
 Nodes (2): created(), getTable()
 
 
-### Community 49 - "Community 49"
+### Community 62 - "Community 62"
 Cohesion: 0.47
 Cohesion: 0.47
 Nodes (3): get_file_data(), get_file_ext(), get_file_info()
 Nodes (3): get_file_data(), get_file_ext(), get_file_info()
 
 
-### Community 50 - "Community 50"
+### Community 63 - "Community 63"
 Cohesion: 0.4
 Cohesion: 0.4
 Nodes (2): created(), getForm()
 Nodes (2): created(), getForm()
 
 
-### Community 51 - "Community 51"
+### Community 64 - "Community 64"
 Cohesion: 0.47
 Cohesion: 0.47
 Nodes (4): onDial(), onTextClick(), dialPhone(), normalizeDialNumber()
 Nodes (4): onDial(), onTextClick(), dialPhone(), normalizeDialNumber()
 
 
-### Community 52 - "Community 52"
+### Community 65 - "Community 65"
 Cohesion: 0.4
 Cohesion: 0.4
 Nodes (4): 运营端角色守卫:admin / super_admin 可访问。, 接口菜单权限守卫。super_admin 直接放行。, require_admin_or_super_admin(), require_menu_code()
 Nodes (4): 运营端角色守卫:admin / super_admin 可访问。, 接口菜单权限守卫。super_admin 直接放行。, require_admin_or_super_admin(), require_menu_code()
 
 
-### Community 53 - "Community 53"
+### Community 66 - "Community 66"
 Cohesion: 0.5
 Cohesion: 0.5
 Nodes (2): focusInput(), handleKeydown()
 Nodes (2): focusInput(), handleKeydown()
 
 
-### Community 55 - "Community 55"
+### Community 68 - "Community 68"
 Cohesion: 0.5
 Cohesion: 0.5
 Nodes (3): BaseSettings, Config, Settings
 Nodes (3): BaseSettings, Config, Settings
 
 
-### Community 56 - "Community 56"
+### Community 69 - "Community 69"
 Cohesion: 0.5
 Cohesion: 0.5
 Nodes (1): collectIds()
 Nodes (1): collectIds()
 
 
-### Community 60 - "Community 60"
+### Community 73 - "Community 73"
 Cohesion: 0.83
 Cohesion: 0.83
 Nodes (3): classes(), isTrue(), onClick()
 Nodes (3): classes(), isTrue(), onClick()
 
 
-### Community 63 - "Community 63"
+### Community 76 - "Community 76"
 Cohesion: 0.67
 Cohesion: 0.67
 Nodes (2): created(), getParent()
 Nodes (2): created(), getParent()
 
 
-### Community 64 - "Community 64"
+### Community 77 - "Community 77"
 Cohesion: 1.0
 Cohesion: 1.0
 Nodes (2): getSettingDefaults(), resetToDefaults()
 Nodes (2): getSettingDefaults(), resetToDefaults()
 
 
-### Community 66 - "Community 66"
+### Community 79 - "Community 79"
 Cohesion: 1.0
 Cohesion: 1.0
 Nodes (2): defaultAvatarPathByRole(), resolveUserAvatarUrl()
 Nodes (2): defaultAvatarPathByRole(), resolveUserAvatarUrl()
 
 
-### Community 78 - "Community 78"
+### Community 91 - "Community 91"
 Cohesion: 0.67
 Cohesion: 0.67
 Nodes (1): updateUseModal()
 Nodes (1): updateUseModal()
 
 
-### Community 88 - "Community 88"
+### Community 101 - "Community 101"
 Cohesion: 1.0
 Cohesion: 1.0
 Nodes (2): buildMiniEncryptedLoginBody(), fetchLoginPublicKey()
 Nodes (2): buildMiniEncryptedLoginBody(), fetchLoginPublicKey()
 
 
-### Community 90 - "Community 90"
+### Community 103 - "Community 103"
 Cohesion: 0.67
 Cohesion: 0.67
 Nodes (1): return()
 Nodes (1): return()
 
 
-### Community 142 - "Community 142"
+### Community 155 - "Community 155"
 Cohesion: 1.0
 Cohesion: 1.0
 Nodes (1): 发送订单状态通知                  Args:             openid: 用户openid             ord
 Nodes (1): 发送订单状态通知                  Args:             openid: 用户openid             ord
 
 
-### Community 143 - "Community 143"
+### Community 156 - "Community 156"
 Cohesion: 1.0
 Cohesion: 1.0
 Nodes (1): 发送支付成功通知                  Args:             openid: 用户openid             ord
 Nodes (1): 发送支付成功通知                  Args:             openid: 用户openid             ord
 
 
-### Community 144 - "Community 144"
+### Community 157 - "Community 157"
 Cohesion: 1.0
 Cohesion: 1.0
 Nodes (1): 发送配送通知                  Args:             openid: 配送员openid             task
 Nodes (1): 发送配送通知                  Args:             openid: 配送员openid             task
 
 
-### Community 145 - "Community 145"
+### Community 158 - "Community 158"
 Cohesion: 1.0
 Cohesion: 1.0
 Nodes (1): 发送配送时效预警                  Args:             openid: 配送员openid             ta
 Nodes (1): 发送配送时效预警                  Args:             openid: 配送员openid             ta
 
 
-### Community 146 - "Community 146"
+### Community 159 - "Community 159"
 Cohesion: 1.0
 Cohesion: 1.0
 Nodes (1): 发送退款结果通知                  Args:             openid: 用户openid             ord
 Nodes (1): 发送退款结果通知                  Args:             openid: 用户openid             ord
 
 
-### Community 147 - "Community 147"
+### Community 160 - "Community 160"
 Cohesion: 1.0
 Cohesion: 1.0
 Nodes (1): 发送催单通知给配送员                  Args:             deliver_openid: 配送员openid
 Nodes (1): 发送催单通知给配送员                  Args:             deliver_openid: 配送员openid
 
 
-### Community 415 - "Community 415"
+### Community 428 - "Community 428"
+Cohesion: 1.0
+Nodes (1): 读取 JSON body;dict 时走 resolve_request_payload(明文 / 密文 / data 包装)。      Returns:
+
+### Community 429 - "Community 429"
 Cohesion: 1.0
 Cohesion: 1.0
 Nodes (1): 按 region_id 管理 websocket 连接并广播。
 Nodes (1): 按 region_id 管理 websocket 连接并广播。
 
 
-### Community 416 - "Community 416"
+### Community 430 - "Community 430"
 Cohesion: 1.0
 Cohesion: 1.0
 Nodes (1): 鉴权并接入房间。返回 current_user dict。
 Nodes (1): 鉴权并接入房间。返回 current_user dict。
 
 
-### Community 417 - "Community 417"
+### Community 431 - "Community 431"
 Cohesion: 1.0
 Cohesion: 1.0
 Nodes (1): 向 region 房间广播消息。返回尝试发送的连接数。
 Nodes (1): 向 region 房间广播消息。返回尝试发送的连接数。
 
 
-### Community 418 - "Community 418"
+### Community 432 - "Community 432"
 Cohesion: 1.0
 Cohesion: 1.0
 Nodes (1): 小程序端 WebSocket 入口:/ws/mini
 Nodes (1): 小程序端 WebSocket 入口:/ws/mini
 
 
-### Community 419 - "Community 419"
+### Community 433 - "Community 433"
 Cohesion: 1.0
 Cohesion: 1.0
 Nodes (1): 在 FastAPI app 上注册 websocket 路由。
 Nodes (1): 在 FastAPI app 上注册 websocket 路由。
 
 
-### Community 420 - "Community 420"
+### Community 434 - "Community 434"
 Cohesion: 1.0
 Cohesion: 1.0
 Nodes (1): Base64 密文 -> JSON 对象(须含 username、password、ts 毫秒时间戳)。
 Nodes (1): Base64 密文 -> JSON 对象(须含 username、password、ts 毫秒时间戳)。
 
 
-### Community 421 - "Community 421"
+### Community 435 - "Community 435"
 Cohesion: 1.0
 Cohesion: 1.0
 Nodes (1): 从请求体解析用户名、密码。     返回 (username, password, error_message);error_message 非空时表示失败。
 Nodes (1): 从请求体解析用户名、密码。     返回 (username, password, error_message);error_message 非空时表示失败。
 
 
 ## Knowledge Gaps
 ## Knowledge Gaps
-- **83 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 非空时表示失败。` (+78 more)
+- **84 isolated node(s):** `关闭超时未支付的线上订单(超时分钟数来自系统设置 auto_cancel_minutes)。`, `初始化定时任务      Args:         get_db_session: 数据库会话获取函数(从 app.core.database 导入)`, `登录凭据 RSA 解密(与前端 JSEncrypt PKCS#1 v1.5 兼容)。`, `Base64 密文 -> JSON 对象(须含 username、password、ts 毫秒时间戳)。`, `从请求体解析用户名、密码。     返回 (username, password, error_message);error_message 非空时表示失败。` (+79 more)
   These have ≤1 connection - possible missing edges or undocumented components.
   These have ≤1 connection - possible missing edges or undocumented components.
-- **Thin community `Community 23`** (13 nodes): `uni-combox.vue`, `clean()`, `data()`, `default()`, `filterCandidates()`, `filterCandidatesLength()`, `handler()`, `labelStyle()`, `onBlur()`, `onFocus()`, `onInput()`, `onSelectorClick()`, `toggleSelector()`
+- **Thin community `Community 35`** (13 nodes): `uni-combox.vue`, `clean()`, `data()`, `default()`, `filterCandidates()`, `filterCandidatesLength()`, `handler()`, `labelStyle()`, `onBlur()`, `onFocus()`, `onInput()`, `onSelectorClick()`, `toggleSelector()`
+  Too small to be a meaningful cluster - may be noise or needs more connections extracted.
+- **Thin community `Community 42`** (9 nodes): `upload-file.vue`, `borderLineStyle()`, `borderStyle()`, `choose()`, `default()`, `delFile()`, `styles()`, `uploadFiles()`, `value2px()`
+  Too small to be a meaningful cluster - may be noise or needs more connections extracted.
+- **Thin community `Community 45`** (8 nodes): `cleanupEventListeners()`, `handleTouchEnd()`, `handleTouchMove()`, `handleTouchStart()`, `handleWheelScroll()`, `setTransition()`, `setupEventListeners()`, `index.vue`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 30`** (9 nodes): `upload-file.vue`, `borderLineStyle()`, `borderStyle()`, `choose()`, `default()`, `delFile()`, `styles()`, `uploadFiles()`, `value2px()`
+- **Thin community `Community 46`** (8 nodes): `TableHeightCalculator`, `.calculate()`, `.calculateOffset()`, `.calculatePaginationOffset()`, `.constructor()`, `.getHeaderHeight()`, `useTableHeight()`, `useTableHeight.ts`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 33`** (8 nodes): `cleanupEventListeners()`, `handleTouchEnd()`, `handleTouchMove()`, `handleTouchStart()`, `handleWheelScroll()`, `setTransition()`, `setupEventListeners()`, `index.vue`
+- **Thin community `Community 48`** (8 nodes): `RouteValidator`, `.checkComponents()`, `.checkDuplicates()`, `.checkNestedIndexComponent()`, `.logLayoutError()`, `.resolvePath()`, `.validate()`, `RouteValidator.ts`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 34`** (8 nodes): `TableHeightCalculator`, `.calculate()`, `.calculateOffset()`, `.calculatePaginationOffset()`, `.constructor()`, `.getHeaderHeight()`, `useTableHeight()`, `useTableHeight.ts`
+- **Thin community `Community 50`** (7 nodes): `refreshButtonAutoIcons()`, `refreshPanelTitleIcons()`, `logApiCryptoBootInfo()`, `maskSecret()`, `App.vue`, `main.ts`, `console.ts`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 36`** (8 nodes): `RouteValidator`, `.checkComponents()`, `.checkDuplicates()`, `.checkNestedIndexComponent()`, `.logLayoutError()`, `.resolvePath()`, `.validate()`, `RouteValidator.ts`
+- **Thin community `Community 56`** (6 nodes): `category.py`, `CategoryCRUD`, `._build_tree()`, `.get_by_name()`, `.get_children()`, `.get_tree()`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 38`** (7 nodes): `refreshButtonAutoIcons()`, `refreshPanelTitleIcons()`, `logApiCryptoBootInfo()`, `maskSecret()`, `App.vue`, `main.ts`, `console.ts`
+- **Thin community `Community 59`** (6 nodes): `bridge-error.js`, `BridgeError`, `.code()`, `.constructor()`, `.errCode()`, `.errMsg()`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 46`** (6 nodes): `bridge-error.js`, `BridgeError`, `.code()`, `.constructor()`, `.errCode()`, `.errMsg()`
+- **Thin community `Community 61`** (6 nodes): `uni-thead.vue`, `checkboxSelected()`, `created()`, `data()`, `getTable()`, `init()`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 48`** (6 nodes): `uni-thead.vue`, `checkboxSelected()`, `created()`, `data()`, `getTable()`, `init()`
+- **Thin community `Community 63`** (6 nodes): `uni-group.vue`, `created()`, `data()`, `getForm()`, `onClick()`, `title()`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 50`** (6 nodes): `uni-group.vue`, `created()`, `data()`, `getForm()`, `onClick()`, `title()`
+- **Thin community `Community 66`** (5 nodes): `focusInput()`, `handleKeydown()`, `if()`, `search()`, `index.vue`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 53`** (5 nodes): `focusInput()`, `handleKeydown()`, `if()`, `search()`, `index.vue`
+- **Thin community `Community 69`** (4 nodes): `catch()`, `collectIds()`, `handleCategoryTableSelectionChange()`, `index.vue`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 56`** (4 nodes): `catch()`, `collectIds()`, `handleCategoryTableSelectionChange()`, `index.vue`
+- **Thin community `Community 76`** (4 nodes): `popup.js`, `created()`, `data()`, `getParent()`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 63`** (4 nodes): `popup.js`, `created()`, `data()`, `getParent()`
+- **Thin community `Community 77`** (3 nodes): `getSettingDefaults()`, `resetToDefaults()`, `setting.ts`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 64`** (3 nodes): `getSettingDefaults()`, `resetToDefaults()`, `setting.ts`
+- **Thin community `Community 79`** (3 nodes): `defaultAvatarPathByRole()`, `resolveUserAvatarUrl()`, `user-avatar.ts`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 66`** (3 nodes): `defaultAvatarPathByRole()`, `resolveUserAvatarUrl()`, `user-avatar.ts`
+- **Thin community `Community 91`** (3 nodes): `check-update.js`, `check-update.ts`, `updateUseModal()`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 78`** (3 nodes): `check-update.js`, `check-update.ts`, `updateUseModal()`
+- **Thin community `Community 101`** (3 nodes): `loginRsa.js`, `buildMiniEncryptedLoginBody()`, `fetchLoginPublicKey()`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 88`** (3 nodes): `loginRsa.js`, `buildMiniEncryptedLoginBody()`, `fetchLoginPublicKey()`
+- **Thin community `Community 103`** (3 nodes): `return()`, `detail.vue`, `detail.vue`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 90`** (3 nodes): `return()`, `detail.vue`, `detail.vue`
+- **Thin community `Community 155`** (1 nodes): `发送订单状态通知                  Args:             openid: 用户openid             ord`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 142`** (1 nodes): `发送订单状态通知                  Args:             openid: 用户openid             ord`
+- **Thin community `Community 156`** (1 nodes): `发送支付成功通知                  Args:             openid: 用户openid             ord`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 143`** (1 nodes): `发送支付成功通知                  Args:             openid: 用户openid             ord`
+- **Thin community `Community 157`** (1 nodes): `发送配送通知                  Args:             openid: 配送员openid             task`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 144`** (1 nodes): `发送配送通知                  Args:             openid: 配送员openid             task`
+- **Thin community `Community 158`** (1 nodes): `发送配送时效预警                  Args:             openid: 配送员openid             ta`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 145`** (1 nodes): `发送配送时效预警                  Args:             openid: 配送员openid             ta`
+- **Thin community `Community 159`** (1 nodes): `发送退款结果通知                  Args:             openid: 用户openid             ord`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 146`** (1 nodes): `发送退款结果通知                  Args:             openid: 用户openid             ord`
+- **Thin community `Community 160`** (1 nodes): `发送催单通知给配送员                  Args:             deliver_openid: 配送员openid`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 147`** (1 nodes): `发送催单通知给配送员                  Args:             deliver_openid: 配送员openid`
+- **Thin community `Community 428`** (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.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 415`** (1 nodes): `按 region_id 管理 websocket 连接并广播。`
+- **Thin community `Community 429`** (1 nodes): `按 region_id 管理 websocket 连接并广播。`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 416`** (1 nodes): `鉴权并接入房间。返回 current_user dict。`
+- **Thin community `Community 430`** (1 nodes): `鉴权并接入房间。返回 current_user dict。`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 417`** (1 nodes): `向 region 房间广播消息。返回尝试发送的连接数。`
+- **Thin community `Community 431`** (1 nodes): `向 region 房间广播消息。返回尝试发送的连接数。`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 418`** (1 nodes): `小程序端 WebSocket 入口:/ws/mini`
+- **Thin community `Community 432`** (1 nodes): `小程序端 WebSocket 入口:/ws/mini`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 419`** (1 nodes): `在 FastAPI app 上注册 websocket 路由。`
+- **Thin community `Community 433`** (1 nodes): `在 FastAPI app 上注册 websocket 路由。`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 420`** (1 nodes): `Base64 密文 -> JSON 对象(须含 username、password、ts 毫秒时间戳)。`
+- **Thin community `Community 434`** (1 nodes): `Base64 密文 -> JSON 对象(须含 username、password、ts 毫秒时间戳)。`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 421`** (1 nodes): `从请求体解析用户名、密码。     返回 (username, password, error_message);error_message 非空时表示失败。`
+- **Thin community `Community 435`** (1 nodes): `从请求体解析用户名、密码。     返回 (username, password, error_message);error_message 非空时表示失败。`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
 
 
 ## Suggested Questions
 ## Suggested Questions
 _Questions this graph is uniquely positioned to answer:_
 _Questions this graph is uniquely positioned to answer:_
 
 
-- **Why does `push()` connect `Community 2` to `Community 0`, `Community 3`, `Community 4`, `Community 7`, `Community 8`, `Community 10`, `Community 11`, `Community 12`?**
-  _High betweenness centrality (0.102) - this node is a cross-community bridge._
-- **Why does `number()` connect `Community 3` to `Community 0`, `Community 2`, `Community 8`, `Community 10`, `Community 12`, `Community 13`?**
-  _High betweenness centrality (0.044) - this node is a cross-community bridge._
-- **Why does `BizPriceListItem` connect `Community 1` to `Community 0`, `Community 9`, `Community 7`?**
-  _High betweenness centrality (0.041) - this node is a cross-community bridge._
+- **Why does `push()` connect `Community 1` to `Community 2`, `Community 3`, `Community 11`, `Community 13`, `Community 14`, `Community 18`, `Community 20`?**
+  _High betweenness centrality (0.115) - this node is a cross-community bridge._
+- **Why does `number()` connect `Community 2` to `Community 1`, `Community 7`, `Community 11`, `Community 18`, `Community 20`?**
+  _High betweenness centrality (0.062) - this node is a cross-community bridge._
+- **Why does `BizPriceListItem` connect `Community 0` to `Community 4`, `Community 8`, `Community 9`, `Community 10`, `Community 13`, `Community 16`?**
+  _High betweenness centrality (0.039) - this node is a cross-community bridge._
 - **Are the 798 inferred relationships involving `BizPriceListItem` (e.g. with `PriceListCRUD` and `PriceListItemCRUD`) actually correct?**
 - **Are the 798 inferred relationships involving `BizPriceListItem` (e.g. with `PriceListCRUD` and `PriceListItemCRUD`) actually correct?**
   _`BizPriceListItem` has 798 INFERRED edges - model-reasoned connections that need verification._
   _`BizPriceListItem` has 798 INFERRED edges - model-reasoned connections that need verification._
 - **Are the 727 inferred relationships involving `BizInquiry` (e.g. with `InquiryCRUD` and `InquiryItemCRUD`) actually correct?**
 - **Are the 727 inferred relationships involving `BizInquiry` (e.g. with `InquiryCRUD` and `InquiryItemCRUD`) actually correct?**

+ 1 - 0
graphify-out/cache/ast/0308f994d1e5e79cbe403d29c8e951a2a5eae4c410aaa736afe85919ceba57bf.json

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

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 0
graphify-out/cache/ast/0bd20fb7eaa6cafb01fdd4702c51c5b9341b3c7383bd76e368668ea291240c37.json


+ 1 - 0
graphify-out/cache/ast/12dcc54c389a918bf403faff3bae02af77fa2feb234e131e90dbaffd70cc334c.json

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

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 0
graphify-out/cache/ast/18a7cdb888f27bac76f18f9a11b6ae3d4565ec8c6382fbc92fc43c689bad0fd6.json


+ 1 - 0
graphify-out/cache/ast/30043a6fc0caefdeacfbab6015909e7f97124949e01fe6edb33ed381ce1d6bef.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": []}

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 0
graphify-out/cache/ast/354de9733b9d558c5f7ed14cd720b41098aee405cec032ad5cf617a6a72e3efd.json


+ 1 - 0
graphify-out/cache/ast/42e3f473dd595bfa41ea1cd3417946b7a0e88741d191664b81dbf8c5fdd127bc.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": []}

+ 1 - 0
graphify-out/cache/ast/4ff310c290a6a5c3b7be214a2fdefc048dea4326326ec82d429f69d20349f7cd.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/529bfd1a891dbbf4dbbf22a7f96ec9e6be8a3dca19046fb85ca9a4fccf036a34.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/6764472febe56f7d6c307c4fa621c807503477e3434dd665f4f1d8426b0d6e4f.json

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

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 0
graphify-out/cache/ast/6b46e6202b9e5567511f3b95841061eeb97a3289b53d9be0b9a07f9ef3dc4b41.json


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 0
graphify-out/cache/ast/7064c4e1e95675c05cb0e1bfbb73a2b2fb1d286263014c8bffe179f0358ce029.json


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 0
graphify-out/cache/ast/75b9e42b737ca49259cf29edd3daa5cf2a1a02b42d97cda966d294406081dc1c.json


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 0
graphify-out/cache/ast/7b4c1443d76360709c5ca443f0a3e2dc422ea9000f633c11f9d99f12c09f2e69.json


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 0
graphify-out/cache/ast/7bfab3e460af42bcf443964dfd647288365ffa1ada38059d42f5859df1599cbd.json


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 0
graphify-out/cache/ast/820a4d14abf1b6a21ffab4556a423e133431055722c90253df231e146e8fb8f8.json


+ 1 - 0
graphify-out/cache/ast/96475bfae66beb394e54534bf816595c2a89112a7e9cdc16e95d3a9af11c670d.json

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

+ 1 - 0
graphify-out/cache/ast/9752ab276b2c4693ef1215014d39050db0ae5157fd8591334fa245c41f39fbed.json

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

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

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_sqxp_wx_pages_supplier_inquiry_inquiry_vue", "label": "inquiry.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/supplier/inquiry/inquiry.vue", "source_location": "L1"}, {"id": "inquiry_inquiry_if", "label": "if()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/supplier/inquiry/inquiry.vue", "source_location": "L124"}], "edges": [{"source": "users_wuzilong_work_00code_sqxp_2_sqxp_wx_pages_supplier_inquiry_inquiry_vue", "target": "inquiry_inquiry_if", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/supplier/inquiry/inquiry.vue", "source_location": "L124", "weight": 1.0}], "raw_calls": [{"caller_nid": "inquiry_inquiry_if", "callee": "filter", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/sqxp-wx/pages/supplier/inquiry/inquiry.vue", "source_location": "L125"}]}

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 0
graphify-out/cache/ast/a190a9801ac68b7776643725fc10d4b7b49b25c59b8adb4383448b64b0d9780e.json


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

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

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 0
graphify-out/cache/ast/a93b9166f381e9185f91710b64f3cd1e4cfa245991c8de20f34f7091b41ed9d3.json


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 0
graphify-out/cache/ast/b77225378ebd8fd347ef71327f45eb3420a348d524dd745a6ecc56469794c09d.json


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

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_sqxp_2_web_admin_src_types_import_components_d_ts", "label": "components.d.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/sqxp_\u526f\u672c2/web-admin/src/types/import/components.d.ts", "source_location": "L1"}], "edges": [], "raw_calls": []}

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

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

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 0
graphify-out/cache/ast/ed2a84b6f96fd196ddea2eaff614e80d4a028d4ed1bc19cc8c3db8dbe9c6f914.json


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

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

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 1
graphify-out/graph.html


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 112 - 112
graphify-out/graph.json


+ 22 - 10
sqxp-wx/pages/login/login.vue

@@ -147,7 +147,9 @@ function iconMaskStyle(maskUrl) {
       <view class="login-footer">
       <view class="login-footer">
         <view class="footer-line">
         <view class="footer-line">
           <view class="agree-check" @tap="toggleProtocolAgreement">
           <view class="agree-check" @tap="toggleProtocolAgreement">
-            <view class="agree-check__icon" :class="{ 'agree-check__icon--checked': agreeProtocol }">✓</view>
+            <view class="agree-check__icon" :class="{ 'agree-check__icon--checked': agreeProtocol }">
+              <text class="agree-check__tick">✓</text>
+            </view>
           </view>
           </view>
           <text class="footer-text">登录即表示同意</text>
           <text class="footer-text">登录即表示同意</text>
           <text class="link" @tap="goUserAgreement">《用户服务协议》</text>
           <text class="link" @tap="goUserAgreement">《用户服务协议》</text>
@@ -370,26 +372,36 @@ function iconMaskStyle(maskUrl) {
   display: inline-flex;
   display: inline-flex;
   align-items: center;
   align-items: center;
   justify-content: center;
   justify-content: center;
-  min-width: 28rpx;
-  min-height: 28rpx;
+  padding: 8rpx;
   margin-right: 4rpx;
   margin-right: 4rpx;
+  flex-shrink: 0;
 }
 }
 
 
 .agree-check__icon {
 .agree-check__icon {
-  width: 24rpx;
-  height: 24rpx;
+  width: 30rpx;
+  height: 30rpx;
   border-radius: 50%;
   border-radius: 50%;
-  border: 1px solid #c0c4cc;
-  color: transparent;
-  font-size: 16rpx;
-  line-height: 24rpx;
-  text-align: center;
+  border: 2rpx solid #c0c4cc;
+  box-sizing: border-box;
+  display: flex;
+  align-items: center;
+  justify-content: center;
   background: #fff;
   background: #fff;
 }
 }
 
 
+.agree-check__tick {
+  font-size: 18rpx;
+  font-weight: 700;
+  line-height: 1;
+  color: transparent;
+}
+
 .agree-check__icon--checked {
 .agree-check__icon--checked {
   border-color: var(--theme-color);
   border-color: var(--theme-color);
   background: var(--theme-color);
   background: var(--theme-color);
+}
+
+.agree-check__icon--checked .agree-check__tick {
   color: #fff;
   color: #fff;
 }
 }
 
 

+ 6 - 20
sqxp-wx/pages/supplier/inquiry/inquiry.vue

@@ -4,6 +4,7 @@ import { onShow } from '@dcloudio/uni-app';
 import { useUserStore } from '@/stores/user';
 import { useUserStore } from '@/stores/user';
 import { request, parseResponseJson } from '@/utils/request';
 import { request, parseResponseJson } from '@/utils/request';
 import { DEFAULT_PLACEHOLDER_IMAGE_URL, IMG_EMPTY, staticAssetUrl } from '@/utils/staticAsset';
 import { DEFAULT_PLACEHOLDER_IMAGE_URL, IMG_EMPTY, staticAssetUrl } from '@/utils/staticAsset';
+import { readInquiryTempMapForUser, writeOneTempPriceForUser } from '@/utils/supplierInquiryTempPrice';
 import ClientSkeleton from '@/components/client/client-skeleton.vue';
 import ClientSkeleton from '@/components/client/client-skeleton.vue';
 import ClientImageLoader from '@/components/client/client-image-loader.vue';
 import ClientImageLoader from '@/components/client/client-image-loader.vue';
 const userStore = useUserStore();
 const userStore = useUserStore();
@@ -28,7 +29,6 @@ const confirmDialogConfirmText = ref('确认');
 const confirmDialogCancelText = ref('取消');
 const confirmDialogCancelText = ref('取消');
 const confirmDialogLoading = ref(false);
 const confirmDialogLoading = ref(false);
 let confirmDialogOnConfirm = null;
 let confirmDialogOnConfirm = null;
-const TEMP_PRICE_STORAGE_KEY = 'supplier_inquiry_temp_prices';
 // H5 上 type="digit" 常无法输入小数点;小程序端保留 digit 以调出带小数点键盘
 // H5 上 type="digit" 常无法输入小数点;小程序端保留 digit 以调出带小数点键盘
 const isH5Env = typeof window !== 'undefined';
 const isH5Env = typeof window !== 'undefined';
 const priceInputType = isH5Env ? 'text' : 'digit';
 const priceInputType = isH5Env ? 'text' : 'digit';
@@ -193,29 +193,17 @@ function formatPriceToFixed2(value) {
 function saveTempPriceToStorage(productId) {
 function saveTempPriceToStorage(productId) {
     if (!productId)
     if (!productId)
         return;
         return;
+    const uid = userStore.userInfo?.id;
     const inquiryKey = currentInquiryId.value || '_default';
     const inquiryKey = currentInquiryId.value || '_default';
     const raw = priceMap.value[productId];
     const raw = priceMap.value[productId];
-    const tempPriceMap = uni.getStorageSync(TEMP_PRICE_STORAGE_KEY) || {};
-    const inquiryTempMap = (tempPriceMap[inquiryKey] && typeof tempPriceMap[inquiryKey] === 'object')
-        ? tempPriceMap[inquiryKey]
-        : {};
-    // 价格为空:移除暂存记录(回到未填状态)
     if (!raw) {
     if (!raw) {
-        if (inquiryTempMap && Object.prototype.hasOwnProperty.call(inquiryTempMap, productId)) {
-            delete inquiryTempMap[productId];
-            tempPriceMap[inquiryKey] = inquiryTempMap;
-            uni.setStorageSync(TEMP_PRICE_STORAGE_KEY, tempPriceMap);
-        }
+        writeOneTempPriceForUser(uid, inquiryKey, productId, null);
         return;
         return;
     }
     }
     const num = Number(raw);
     const num = Number(raw);
     if (Number.isNaN(num) || num < 0)
     if (Number.isNaN(num) || num < 0)
         return;
         return;
-    tempPriceMap[inquiryKey] = {
-        ...(inquiryTempMap || {}),
-        [productId]: Number(num.toFixed(2))
-    };
-    uni.setStorageSync(TEMP_PRICE_STORAGE_KEY, tempPriceMap);
+    writeOneTempPriceForUser(uid, inquiryKey, productId, num);
 }
 }
 async function fetchInquiryData() {
 async function fetchInquiryData() {
     isLoading.value = true;
     isLoading.value = true;
@@ -264,10 +252,8 @@ async function fetchInquiryData() {
     }
     }
 }
 }
 function applyTempPriceFromStorage() {
 function applyTempPriceFromStorage() {
-    const tempPriceMap = uni.getStorageSync(TEMP_PRICE_STORAGE_KEY) || {};
-    const inquiryKey = currentInquiryId.value || '_default';
-    const inquiryTempMap = tempPriceMap[inquiryKey];
-    if (!inquiryTempMap || typeof inquiryTempMap !== 'object')
+    const inquiryTempMap = readInquiryTempMapForUser(userStore.userInfo?.id, currentInquiryId.value || '_default');
+    if (!inquiryTempMap)
         return;
         return;
     Object.keys(inquiryTempMap).forEach((productId) => {
     Object.keys(inquiryTempMap).forEach((productId) => {
         const price = Number(inquiryTempMap[productId]);
         const price = Number(inquiryTempMap[productId]);

+ 4 - 12
sqxp-wx/pages/supplier/product/detail.vue

@@ -118,6 +118,7 @@ import { useUserStore } from '@/stores/user'
 import ClientSkeleton from '@/components/client/client-skeleton.vue'
 import ClientSkeleton from '@/components/client/client-skeleton.vue'
 import ClientImageLoader from '@/components/client/client-image-loader.vue'
 import ClientImageLoader from '@/components/client/client-image-loader.vue'
 import { withOssVersion } from '@/utils/ossCache'
 import { withOssVersion } from '@/utils/ossCache'
+import { mergeTempProductPriceForUser, readInquiryTempMapForUser } from '@/utils/supplierInquiryTempPrice'
 
 
 const userStore = useUserStore()
 const userStore = useUserStore()
 
 
@@ -139,7 +140,6 @@ const inquiryItems = ref<any[]>([])
 const currentProductId = ref('')
 const currentProductId = ref('')
 const bannerActiveIndex = ref(0)
 const bannerActiveIndex = ref(0)
 let openerEventChannel: any = null
 let openerEventChannel: any = null
-const TEMP_PRICE_STORAGE_KEY = 'supplier_inquiry_temp_prices'
 const ossImg = withOssVersion
 const ossImg = withOssVersion
 
 
 function formatMeasureText(measureValue?: number | null, unitName?: string | null) {
 function formatMeasureText(measureValue?: number | null, unitName?: string | null) {
@@ -180,10 +180,8 @@ function onPriceBlur() {
 
 
 function applyTempPriceForCurrentProduct() {
 function applyTempPriceForCurrentProduct() {
   if (!currentProductId.value) return
   if (!currentProductId.value) return
-  const inquiryKey = inquiryId.value || '_default'
-  const tempPriceMap = uni.getStorageSync(TEMP_PRICE_STORAGE_KEY) || {}
-  const inquiryTempMap = tempPriceMap[inquiryKey]
-  if (!inquiryTempMap || typeof inquiryTempMap !== 'object') return
+  const inquiryTempMap = readInquiryTempMapForUser(userStore.userInfo?.id, inquiryId.value || '_default')
+  if (!inquiryTempMap) return
   const temp = inquiryTempMap[currentProductId.value]
   const temp = inquiryTempMap[currentProductId.value]
   if (temp == null || temp === '') return
   if (temp == null || temp === '') return
   const num = Number(temp)
   const num = Number(temp)
@@ -275,13 +273,7 @@ async function saveSingleProductPrice() {
   savingPrice.value = true
   savingPrice.value = true
   try {
   try {
     const tempPrice = Number(currentPrice.toFixed(2))
     const tempPrice = Number(currentPrice.toFixed(2))
-    const tempPriceMap = uni.getStorageSync(TEMP_PRICE_STORAGE_KEY) || {}
-    const inquiryKey = inquiryId.value || '_default'
-    tempPriceMap[inquiryKey] = {
-      ...(tempPriceMap[inquiryKey] || {}),
-      [currentProductId.value]: tempPrice
-    }
-    uni.setStorageSync(TEMP_PRICE_STORAGE_KEY, tempPriceMap)
+    mergeTempProductPriceForUser(userStore.userInfo?.id, inquiryId.value || '_default', currentProductId.value, tempPrice)
 
 
     openerEventChannel?.emit('tempPriceSaved', {
     openerEventChannel?.emit('tempPriceSaved', {
       inquiryId: inquiryId.value,
       inquiryId: inquiryId.value,

+ 90 - 0
sqxp-wx/utils/supplierInquiryTempPrice.js

@@ -0,0 +1,90 @@
+/**
+ * 供应商询价页「暂存价格」:按 userId 分桶,避免多账号同机串数据。
+ * 结构:{ [userId]: { [inquiryId]: { [productId]: number } } }
+ */
+export const SUPPLIER_INQUIRY_TEMP_PRICES_KEY = 'supplier_inquiry_temp_prices';
+
+/**
+ * @param {string | undefined | null} userId
+ * @param {string} inquiryKey
+ * @returns {Record<string, number> | null}
+ */
+export function readInquiryTempMapForUser(userId, inquiryKey) {
+  const key = (inquiryKey && String(inquiryKey).trim()) || '_default';
+  const uid = userId == null || String(userId).trim() === '' ? '' : String(userId).trim();
+  if (!uid) return null;
+  const root = uni.getStorageSync(SUPPLIER_INQUIRY_TEMP_PRICES_KEY) || {};
+  if (!root || typeof root !== 'object') return null;
+  const byUser = root[uid];
+  if (!byUser || typeof byUser !== 'object') return null;
+  const m = byUser[key];
+  if (!m || typeof m !== 'object') return null;
+  return m;
+}
+
+/**
+ * @param {string | undefined | null} userId
+ * @param {string} inquiryKey
+ * @param {string} productId
+ * @param {string|number|undefined|null} rawPrice 空或无效则删除该条暂存
+ */
+export function writeOneTempPriceForUser(userId, inquiryKey, productId, rawPrice) {
+  const key = (inquiryKey && String(inquiryKey).trim()) || '_default';
+  const uid = userId == null || String(userId).trim() === '' ? '' : String(userId).trim();
+  if (!uid || !productId) return;
+  const root = { ...(uni.getStorageSync(SUPPLIER_INQUIRY_TEMP_PRICES_KEY) || {}) };
+  if (!root[uid] || typeof root[uid] !== 'object') {
+    root[uid] = {};
+  }
+  else {
+    root[uid] = { ...root[uid] };
+  }
+  const byUser = root[uid];
+  const block = (byUser[key] && typeof byUser[key] === 'object') ? { ...byUser[key] } : {};
+  if (rawPrice === '' || rawPrice == null) {
+    if (Object.prototype.hasOwnProperty.call(block, productId)) {
+      delete block[productId];
+    }
+  }
+  else {
+    const num = Number(rawPrice);
+    if (Number.isNaN(num) || num < 0) return;
+    block[productId] = Number(num.toFixed(2));
+  }
+  if (Object.keys(block).length === 0) {
+    delete byUser[key];
+  }
+  else {
+    byUser[key] = block;
+  }
+  if (Object.keys(byUser).length === 0) {
+    delete root[uid];
+  }
+  uni.setStorageSync(SUPPLIER_INQUIRY_TEMP_PRICES_KEY, root);
+}
+
+/**
+ * 与「暂存价格」按钮写一整块 map 的语义(商品详情页)
+ * @param {string | undefined | null} userId
+ * @param {string} inquiryKey
+ * @param {string} productId
+ * @param {number} price
+ */
+export function mergeTempProductPriceForUser(userId, inquiryKey, productId, price) {
+  const key = (inquiryKey && String(inquiryKey).trim()) || '_default';
+  const uid = userId == null || String(userId).trim() === '' ? '' : String(userId).trim();
+  if (!uid || !productId) return;
+  const root = { ...(uni.getStorageSync(SUPPLIER_INQUIRY_TEMP_PRICES_KEY) || {}) };
+  if (!root[uid] || typeof root[uid] !== 'object') {
+    root[uid] = {};
+  }
+  else {
+    root[uid] = { ...root[uid] };
+  }
+  const byUser = root[uid];
+  byUser[key] = {
+    ...(byUser[key] && typeof byUser[key] === 'object' ? byUser[key] : {}),
+    [productId]: price
+  };
+  uni.setStorageSync(SUPPLIER_INQUIRY_TEMP_PRICES_KEY, root);
+}

+ 4 - 2
web-admin/src/api/business.ts

@@ -210,11 +210,13 @@ export function fetchDeliveryWarningList(params: {
 
 
 /**
 /**
  * 解决预警
  * 解决预警
+ * 注意:`note` 为 `undefined` 时若写 `{ note }`,JSON.stringify 会省略该键,加密后服务端收到空对象,备注会落成默认「手动处理」。
+ * 因此此处始终序列化出 `note` 字符串字段。
  */
  */
-export function fetchResolveWarning(id: string, note: string) {
+export function fetchResolveWarning(id: string, note: string | undefined | null) {
   return request.post({
   return request.post({
     url: `/web/admin/delivery-warning/handle/${id}`,
     url: `/web/admin/delivery-warning/handle/${id}`,
-    params: { note }
+    data: { note: note == null ? '' : String(note) }
   })
   })
 }
 }
 
 

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

@@ -505,7 +505,7 @@
                         </el-tag>
                         </el-tag>
                       </template>
                       </template>
                     </el-table-column>
                     </el-table-column>
-                    <el-table-column prop="orderCreateAt" label="下单时间" min-width="170" />
+                    <el-table-column prop="orderCreateAt" label="下单时间" min-width="300" />
                     </el-table>
                     </el-table>
                   </div>
                   </div>
                   <el-empty v-else description="暂无明细" :image-size="72" />
                   <el-empty v-else description="暂无明细" :image-size="72" />

+ 78 - 7
web-admin/src/views/system/delivery-warning/index.vue

@@ -366,7 +366,10 @@
               {{ displayValue(log.operator) }}
               {{ displayValue(log.operator) }}
               <span class="process-log-type">· {{ displayValue(log.handleType) }}</span>
               <span class="process-log-type">· {{ displayValue(log.handleType) }}</span>
             </div>
             </div>
-            <div class="view-tl-desc">{{ displayValue(log.note) }}</div>
+            <div v-if="processLogNoteText(log)" class="view-tl-note-wrap">
+              <span class="view-tl-note-label">处理备注</span>
+              <div class="view-tl-note-content">{{ processLogNoteText(log) }}</div>
+            </div>
           </div>
           </div>
           <el-empty v-if="!(detailData.processLogs || []).length" description="暂无处理过程记录" :image-size="72" />
           <el-empty v-if="!(detailData.processLogs || []).length" description="暂无处理过程记录" :image-size="72" />
         </div>
         </div>
@@ -642,6 +645,14 @@
     const text = String(value).trim()
     const text = String(value).trim()
     return text === '' ? '-' : text
     return text === '' ? '-' : text
   }
   }
+  /** 处理过程记录中的备注(兼容 note / remark) */
+  const processLogNoteText = (log: Record<string, unknown> | undefined) => {
+    if (!log) return ''
+    const raw = log.note ?? log.remark
+    if (raw == null || raw === undefined) return ''
+    const text = String(raw).trim()
+    return text === '' || text === '-' ? '' : text
+  }
   const getExpandedItems = (warningId: string) => expandedItemsMap.value[warningId] || []
   const getExpandedItems = (warningId: string) => expandedItemsMap.value[warningId] || []
   const ensureExpandedItems = async (row: any) => {
   const ensureExpandedItems = async (row: any) => {
     if (expandedItemsMap.value[row.id]) return
     if (expandedItemsMap.value[row.id]) return
@@ -716,32 +727,67 @@
     pagination.page = 1
     pagination.page = 1
     loadData()
     loadData()
   }
   }
+  const devLog = (...args: unknown[]) => {
+    if (import.meta.env.DEV) console.info('[时效预警]', ...args)
+  }
+
   const handleView = async (row: any) => {
   const handleView = async (row: any) => {
     detailData.value = { ...row }
     detailData.value = { ...row }
     // 尝试获取更多信息
     // 尝试获取更多信息
     try {
     try {
-      const warningRes = await fetchDeliveryWarningDetail(row.id)
+      const warningRes: any = await fetchDeliveryWarningDetail(row.id)
       if (warningRes) {
       if (warningRes) {
         detailData.value = { ...detailData.value, ...warningRes }
         detailData.value = { ...detailData.value, ...warningRes }
       }
       }
+      devLog('查看 fetchDeliveryWarningDetail 返回(已与列表行合并前)', {
+        warningId: row.id,
+        keys: warningRes && typeof warningRes === 'object' ? Object.keys(warningRes) : [],
+        resolveNote: warningRes?.resolveNote,
+        remark: warningRes?.remark,
+        processLogs: warningRes?.processLogs,
+        processLogsLength: Array.isArray(warningRes?.processLogs) ? warningRes.processLogs.length : null
+      })
       // 获取配送详情
       // 获取配送详情
       if (row.deliveryId) {
       if (row.deliveryId) {
         const deliveryRes = await fetchDeliveryDetail(row.deliveryId)
         const deliveryRes = await fetchDeliveryDetail(row.deliveryId)
         if (deliveryRes) {
         if (deliveryRes) {
           detailData.value.deliveryDetail = deliveryRes
           detailData.value.deliveryDetail = deliveryRes
         }
         }
+        devLog('查看 fetchDeliveryDetail 返回 keys', {
+          deliveryId: row.deliveryId,
+          keys: deliveryRes && typeof deliveryRes === 'object' ? Object.keys(deliveryRes) : []
+        })
       }
       }
     } catch (error) {
     } catch (error) {
       console.error('加载详情失败:', error)
       console.error('加载详情失败:', error)
     }
     }
+    devLog('查看 弹窗将展示的 detailData.keys', Object.keys(detailData.value || {}))
     detailVisible.value = true
     detailVisible.value = true
   }
   }
   const handleResolve = (row: any) => { resolveForm.id = row.id; resolveForm.note = ''; resolveVisible.value = true }
   const handleResolve = (row: any) => { resolveForm.id = row.id; resolveForm.note = ''; resolveVisible.value = true }
   const confirmResolve = async () => {
   const confirmResolve = async () => {
+    const noteTrim = String(resolveForm.note ?? '').trim()
+    if (!noteTrim) {
+      ElMessage.warning('请填写处理备注')
+      return
+    }
     submitLoading.value = true
     submitLoading.value = true
-    try { await fetchResolveWarning(resolveForm.id, resolveForm.note); ElMessage.success('处理成功'); resolveVisible.value = false; loadData() }
-    catch (error) { console.error('处理失败:', error) }
-    finally { submitLoading.value = false }
+    try {
+      devLog('处理 提交 fetchResolveWarning', {
+        warningId: resolveForm.id,
+        noteLength: noteTrim.length,
+        notePreview: noteTrim.slice(0, 80)
+      })
+      const resolveRet: any = await fetchResolveWarning(resolveForm.id, noteTrim)
+      devLog('处理 fetchResolveWarning 返回(request 已解包为 data)', resolveRet)
+      ElMessage.success('处理成功')
+      resolveVisible.value = false
+      loadData()
+    } catch (error) {
+      console.error('处理失败:', error)
+    } finally {
+      submitLoading.value = false
+    }
   }
   }
   onMounted(() => {
   onMounted(() => {
     void ensureCategoryPathDataLoaded()
     void ensureCategoryPathDataLoaded()
@@ -1146,6 +1192,30 @@
   font-weight: 500;
   font-weight: 500;
 }
 }
 
 
+.view-tl-note-wrap {
+  margin-top: 8px;
+  padding: 10px 12px;
+  background: #f8fafc;
+  border-radius: 8px;
+  border: 1px solid var(--mint-border, #d9e8e2);
+}
+
+.view-tl-note-label {
+  display: block;
+  font-size: 11px;
+  font-weight: 600;
+  color: #64748b;
+  margin-bottom: 6px;
+}
+
+.view-tl-note-content {
+  font-size: 13px;
+  color: #1e293b;
+  line-height: 1.55;
+  white-space: pre-wrap;
+  word-break: break-word;
+}
+
 @media (max-width: 760px) {
 @media (max-width: 760px) {
   .view-kv-grid {
   .view-kv-grid {
     grid-template-columns: 1fr;
     grid-template-columns: 1fr;
@@ -1192,7 +1262,7 @@
 }
 }
 
 
 .delivery-warning-resolve-dialog .el-dialog__body {
 .delivery-warning-resolve-dialog .el-dialog__body {
-  padding: 18px 28px 6px;
+  padding: 26px 28px 8px;
 }
 }
 
 
 .delivery-warning-resolve-dialog .el-dialog__footer {
 .delivery-warning-resolve-dialog .el-dialog__footer {
@@ -1201,7 +1271,8 @@
 }
 }
 
 
 .delivery-warning-resolve-dialog .resolve-dialog-tip {
 .delivery-warning-resolve-dialog .resolve-dialog-tip {
-  margin: 0 0 12px;
+  margin: 0 0 14px;
+  padding-top: 4px;
   font-size: 12px;
   font-size: 12px;
   color: #64748b;
   color: #64748b;
   line-height: 1.5;
   line-height: 1.5;

+ 79 - 35
web-admin/src/views/system/delivery/index.vue

@@ -303,18 +303,22 @@
                   <span class="label">取货备注</span>
                   <span class="label">取货备注</span>
                   <span class="value text-wrap">{{ viewData.pickupRemark || '-' }}</span>
                   <span class="value text-wrap">{{ viewData.pickupRemark || '-' }}</span>
                 </div>
                 </div>
-                <div v-if="viewData.pickupImages && viewData.pickupImages.length > 0" class="address-images">
-                  <span class="label">取货凭证</span>
-                  <div class="image-list">
-                    <el-image
-                      v-for="(img, index) in viewData.pickupImages"
-                      :key="index"
-                      :src="img"
-                      fit="cover"
-                      class="image-item"
-                      :preview-src-list="viewData.pickupImages"
-                      preview-teleported
-                    />
+                <div class="od-address-line od-address-line--block">
+                  <span class="od-label">备货凭证</span>
+                  <div class="od-value">
+                    <div v-if="(viewData.prepareImages || []).length" class="evidence-grid">
+                      <el-image
+                        v-for="(img, idx) in viewData.prepareImages"
+                        :key="`prep-${idx}`"
+                        :src="img"
+                        :preview-src-list="viewData.prepareImages"
+                        :initial-index="idx"
+                        fit="cover"
+                        class="evidence-image"
+                        preview-teleported
+                      />
+                    </div>
+                    <span v-else>-</span>
                   </div>
                   </div>
                 </div>
                 </div>
               </div>
               </div>
@@ -344,18 +348,40 @@
                   <span class="label">送达备注</span>
                   <span class="label">送达备注</span>
                   <span class="value text-wrap">{{ viewData.deliveryRemark || '-' }}</span>
                   <span class="value text-wrap">{{ viewData.deliveryRemark || '-' }}</span>
                 </div>
                 </div>
-                <div v-if="viewData.deliveryImages && viewData.deliveryImages.length > 0" class="address-images">
-                  <span class="label">送达凭证</span>
-                  <div class="image-list">
-                    <el-image
-                      v-for="(img, index) in viewData.deliveryImages"
-                      :key="index"
-                      :src="img"
-                      fit="cover"
-                      class="image-item"
-                      :preview-src-list="viewData.deliveryImages"
-                      preview-teleported
-                    />
+                <div class="od-address-line od-address-line--block">
+                  <span class="od-label">取货凭证</span>
+                  <div class="od-value">
+                    <div v-if="(viewData.pickupImages || []).length" class="evidence-grid">
+                      <el-image
+                        v-for="(img, idx) in viewData.pickupImages"
+                        :key="`pickup-${idx}`"
+                        :src="img"
+                        :preview-src-list="viewData.pickupImages"
+                        :initial-index="idx"
+                        fit="cover"
+                        class="evidence-image"
+                        preview-teleported
+                      />
+                    </div>
+                    <span v-else>-</span>
+                  </div>
+                </div>
+                <div class="od-address-line od-address-line--block">
+                  <span class="od-label">送达凭证</span>
+                  <div class="od-value">
+                    <div v-if="(viewData.deliveryImages || []).length" class="evidence-grid">
+                      <el-image
+                        v-for="(img, idx) in viewData.deliveryImages"
+                        :key="`deliver-${idx}`"
+                        :src="img"
+                        :preview-src-list="viewData.deliveryImages"
+                        :initial-index="idx"
+                        fit="cover"
+                        class="evidence-image"
+                        preview-teleported
+                      />
+                    </div>
+                    <span v-else>-</span>
                   </div>
                   </div>
                 </div>
                 </div>
               </div>
               </div>
@@ -531,6 +557,7 @@
   const viewData = ref<any>({
   const viewData = ref<any>({
     items: [],
     items: [],
     timeline: [],
     timeline: [],
+    prepareImages: [],
     pickupImages: [],
     pickupImages: [],
     deliveryImages: [],
     deliveryImages: [],
     supplierPickup: null,
     supplierPickup: null,
@@ -818,6 +845,7 @@
       viewDetailTab.value = 'detail'
       viewDetailTab.value = 'detail'
       viewData.value = {
       viewData.value = {
         ...res,
         ...res,
+        prepareImages: Array.isArray(res.prepareImages) ? res.prepareImages : [],
         pickupImages: Array.isArray(res.pickupImages) ? res.pickupImages : [],
         pickupImages: Array.isArray(res.pickupImages) ? res.pickupImages : [],
         deliveryImages: Array.isArray(res.deliveryImages) ? res.deliveryImages : [],
         deliveryImages: Array.isArray(res.deliveryImages) ? res.deliveryImages : [],
         supplierPickup: res.supplierPickup ?? null,
         supplierPickup: res.supplierPickup ?? null,
@@ -1252,26 +1280,42 @@
   }
   }
 }
 }
 
 
-.address-images {
-  margin-top: 12px;
+/* 与订单列表「查看/运营」配送 Tab 中地址区块的凭证展示一致 */
+.od-address-line {
+  display: flex;
+  gap: 12px;
+  margin-bottom: 10px;
+  align-items: flex-start;
 
 
-  .label {
-    display: block;
+  .od-label {
+    flex-shrink: 0;
+    width: 88px;
     color: #909399;
     color: #909399;
-    margin-bottom: 8px;
   }
   }
+
+  .od-value {
+    flex: 1;
+    min-width: 0;
+    word-break: break-all;
+  }
+}
+
+.od-address-line--block {
+  align-items: flex-start;
 }
 }
 
 
-.image-list {
+.evidence-grid {
   display: flex;
   display: flex;
   flex-wrap: wrap;
   flex-wrap: wrap;
-  gap: 12px;
+  gap: 8px;
 }
 }
 
 
-.image-item {
-  width: 120px;
-  height: 120px;
-  border-radius: 8px;
+.evidence-image {
+  width: 72px;
+  height: 72px;
+  border-radius: 6px;
+  border: 1px solid #ebeef5;
+  overflow: hidden;
   cursor: pointer;
   cursor: pointer;
 }
 }
 
 

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

@@ -492,7 +492,7 @@
                         </el-tag>
                         </el-tag>
                       </template>
                       </template>
                     </el-table-column>
                     </el-table-column>
-                    <el-table-column prop="orderCreateAt" label="下单时间" min-width="170" />
+                    <el-table-column prop="orderCreateAt" label="下单时间" min-width="300" />
                     </el-table>
                     </el-table>
                   </div>
                   </div>
                   <el-empty v-else description="暂无明细" :image-size="72" />
                   <el-empty v-else description="暂无明细" :image-size="72" />

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است