This commit is contained in:
zlwu committed 2026-08-03 12:48:52 +08:00
1 parent 594e4ce38e
commit e233269abf
144 files changed
+228178 -111974

No files matched your search

+1 -1
View File
@@ -1 +1 @@
49357
19114
+1 -6
View File
@@ -153,17 +153,13 @@
"151": "Community 151",
"152": "MinIOStorage",
"153": "Community 153",
"154": "web_login_rsa_public_key",
"155": "Community 155",
"156": "get_client_ip",
"157": "Community 157",
"158": "Community 158",
"159": "context.py",
"160": "register_local_callback",
"161": "backfill_crm_contract_attachment_urls.py",
"162": "monitor.py",
"163": "login_crypto.py",
"164": "redis_client.py",
"165": "Community 165",
"166": "code:block1 (/iflytek/shanxia2.0/backend)",
"167": "Community 167",
@@ -177,10 +173,9 @@
"175": "vehicle_maintenance.py",
"176": "_forbid_legacy_invoice_fields",
"177": "vehicle_maintenance.py",
"178": "_build_user_info_payload",
"179": "_ensure_subpack_slip_no",
"180": "menu.py",
"181": "web_login_rsa_public_key",
"181": "create_driver",
"185": "vehicle_urea.py",
"186": "transport_address.py",
"189": "create",
@@ -0,0 +1,533 @@
{
"0": "Community 0",
"1": "Community 1",
"2": "Community 2",
"3": "Community 3",
"4": "Community 4",
"5": "Community 5",
"6": "Community 6",
"7": "Community 7",
"8": "Community 8",
"9": "Community 9",
"10": "Community 10",
"11": "Community 11",
"12": "Community 12",
"13": "Community 13",
"14": "Community 14",
"15": "Community 15",
"16": "Community 16",
"17": "Community 17",
"18": "Community 18",
"19": "Community 19",
"20": "Community 20",
"21": "Community 21",
"22": "Community 22",
"23": "Community 23",
"24": "Community 24",
"25": "Community 25",
"26": "Community 26",
"27": "Community 27",
"28": "Community 28",
"29": "Community 29",
"30": "Community 30",
"31": "Community 31",
"32": "Community 32",
"33": "Community 33",
"34": "Community 34",
"35": "Community 35",
"36": "Community 36",
"37": "Community 37",
"38": "Community 38",
"39": "Community 39",
"40": "Community 40",
"41": "Community 41",
"42": "Community 42",
"43": "Community 43",
"44": "Community 44",
"45": "Community 45",
"46": "Community 46",
"47": "Community 47",
"48": "Community 48",
"49": "Community 49",
"50": "Community 50",
"51": "Community 51",
"52": "Community 52",
"53": "Community 53",
"54": "Community 54",
"55": "Community 55",
"56": "Community 56",
"57": "Community 57",
"58": "Community 58",
"59": "Community 59",
"60": "Community 60",
"61": "Community 61",
"62": "Community 62",
"63": "Community 63",
"64": "Community 64",
"65": "Community 65",
"66": "Community 66",
"67": "Community 67",
"68": "Community 68",
"69": "Community 69",
"70": "Community 70",
"71": "Community 71",
"72": "Community 72",
"73": "Community 73",
"74": "Community 74",
"75": "Community 75",
"76": "Community 76",
"77": "Community 77",
"78": "Community 78",
"79": "Community 79",
"80": "Community 80",
"81": "Community 81",
"82": "Community 82",
"83": "Community 83",
"84": "Community 84",
"85": "Community 85",
"86": "Community 86",
"87": "Community 87",
"88": "Any",
"89": "pl_ocr_service.py",
"90": "Community 90",
"91": "pl_vehicle_recharge_service.py",
"92": "Community 92",
"93": "_invoice_info_by_customer_impl",
"94": "response.py",
"95": "Community 95",
"96": "Community 96",
"97": "_ensure_subpack_slip_no",
"98": "Community 98",
"99": "Community 99",
"100": "price_letter_detail",
"101": ".to_tenant_user_dict",
"102": "dept.py",
"103": "Community 103",
"104": "apply_crm_create_by_scope",
"105": "list_customers",
"106": "Community 106",
"107": "apply_pl_scope",
"108": "Community 108",
"109": "Community 109",
"110": "Base",
"111": "Community 111",
"112": "storage.py",
"113": "quote_share_log.py",
"114": "db_models.py",
"115": "site.py",
"116": "Community 116",
"117": "Community 117",
"118": "Community 118",
"119": "Community 119",
"120": "Community 120",
"121": "Community 121",
"122": "Community 122",
"123": "Community 123",
"124": "Community 124",
"125": "Community 125",
"126": "customer_pool.py",
"127": "Community 127",
"128": "plan.py",
"129": "BaseStorage",
"130": "datetime_parse.py",
"131": "LocalStorage",
"132": "crm_supplier_hazard.py",
"133": "Community 133",
"134": "Community 134",
"135": "post.py",
"136": "Community 136",
"137": "statistics.py",
"138": "Community 138",
"139": "Community 139",
"140": "Community 140",
"141": "clue_statistics.py",
"142": "Community 142",
"143": "SysEthnic",
"144": "SysApiRequestLog",
"145": "minio_url.py",
"146": "__init__.py",
"147": "Community 147",
"148": "storage.py",
"149": "Community 149",
"150": "ContractListQuery",
"151": "Community 151",
"152": "MinIOStorage",
"153": "Community 153",
"154": "web_login_rsa_public_key",
"155": "Community 155",
"156": "get_client_ip",
"157": "Community 157",
"158": "Community 158",
"159": "context.py",
"160": "register_local_callback",
"161": "backfill_crm_contract_attachment_urls.py",
"163": "login_crypto.py",
"165": "Community 165",
"166": "code:block1 (/iflytek/shanxia2.0/backend)",
"167": "Community 167",
"168": "vehicle_urea.py",
"169": "product.py",
"170": "test_quote_approver_setting.py",
"171": "BaseStorage",
"172": "LocalStorage",
"173": "resolve_create_time_bounds",
"174": "datetime_parse.py",
"175": "vehicle_maintenance.py",
"176": "_forbid_legacy_invoice_fields",
"177": "vehicle_maintenance.py",
"178": "_build_user_info_payload",
"179": "_ensure_subpack_slip_no",
"180": "menu.py",
"181": "create_driver",
"185": "vehicle_urea.py",
"186": "transport_address.py",
"189": "create",
"190": "menu.py",
"192": "pool_rule.py",
"197": "_coerce_resource_snapshot",
"198": "user.py",
"202": "statistics.py",
"206": "crm_quote_pdf.py",
"207": "shanxia2.0 后端骨架(FastAPI)",
"211": "leave.py",
"216": "io_order.py",
"217": "auth.py",
"218": "pool_rule.py",
"220": "api_crypto_middleware.py",
"223": "workflow.py",
"224": "supplier_info.py",
"226": "get_active",
"228": "advance.py",
"230": "attachment.py",
"232": "transport_address.py",
"233": "customer_pool.py",
"235": "invoice_info.py",
"236": "price_letter.py",
"237": "order_status_compat.py",
"239": "shanxia2.0 Backend 生产部署说明",
"240": "delete",
"242": "leave.py",
"244": "expense_item.py",
"245": "__init__.py",
"246": "db_collation.py",
"247": "expense_item.py",
"249": "__init__.py",
"250": "invoice.py",
"251": "menu.py",
"252": "public_inventory.py",
"254": "Activate.ps1",
"256": "statistics.py",
"259": "一次上传一条发票:多文件挂同一档案,金额为总额,只写一条额度流水。",
"262": "reimburse.py",
"263": "__init__.py",
"264": "__init__.py",
"265": "__init__.py",
"266": "workflow.py",
"269": "__init__.py",
"270": "leave.py",
"271": "menu.py",
"272": "__init__.py",
"273": "__init__.py",
"274": "__init__.py",
"275": "__init__.py",
"276": "__init__.py",
"278": "__init__.py",
"281": "__init__.py",
"286": "send_delivery_notice",
"287": "__init__.py",
"288": "__init__.py",
"289": "send_delivery_warning",
"292": "send_order_status",
"293": "__init__.py",
"294": "__init__.py",
"295": "__init__.py",
"296": "__init__.py",
"297": "__init__.py",
"298": "__init__.py",
"299": "__init__.py",
"300": "__init__.py",
"301": "__init__.py",
"302": "send_payment_success",
"303": "send_refund_result",
"304": "__init__.py",
"305": "__init__.py",
"306": "__init__.py",
"307": "__init__.py",
"308": "__init__.py",
"309": "__init__.py",
"310": "__init__.py",
"311": "__init__.py",
"312": "__init__.py",
"313": "send_urge_notification",
"314": "get",
"315": "get_multi_with_filters",
"317": "base.py",
"318": "clue.py",
"320": "customer_pool.py",
"321": "customer.py",
"323": "invoice_info.py",
"326": "price_letter.py",
"327": "product.py",
"328": "quote.py",
"329": "user.py",
"330": "base.py",
"331": "__tablename__",
"332": "__init__.py",
"333": "advance.py",
"334": "validate_expense_item_id",
"335": "attachment.py",
"336": "common.py",
"337": "validate_parent_id",
"338": "__init__.py",
"339": "invoice.py",
"340": "coerce_applicant_id",
"341": "details_must_not_be_empty",
"342": "reimburse.py",
"343": "validate_advance_id",
"344": "validate_expense_item_id",
"345": "validate_approver_id",
"346": "common.py",
"347": "contact.py",
"348": "customer_follow.py",
"349": "customer.py",
"350": "dashboard.py",
"351": "__init__.py",
"352": "inventory.py",
"353": "plans.py",
"354": "weighing.py",
"355": "__init__.py",
"356": "freight.py",
"357": "__init__.py",
"358": "plan.py",
"360": "validate_coordinates",
"361": "transport.py",
"362": "validate_amount",
"363": "validate_amount",
"364": "validate_recharge_type",
"365": "validate_amount",
"366": "validate_apply_amount",
"367": "validate_quantity",
"368": "approve",
"369": "create",
"370": "finance_issue_stats",
"371": "get_by_id",
"372": "get_paged",
"373": "global_status_counts",
"374": "issue",
"375": "_latest_approval_per_node",
"376": "_my_apply_base",
"377": "my_apply_stats",
"378": "offset_amount_map",
"379": "pending_approval_stats",
"380": "advance.py",
"381": "_query_approvals",
"382": "reject",
"383": "soft_delete",
"384": "submit",
"385": "_tenant_no",
"386": "update",
"387": "_workflow_nodes",
"389": "create",
"390": "get_by_biz",
"391": "get_by_id",
"392": "attachment.py",
"393": "soft_delete",
"394": "_tenant_no",
"395": "get_by_id",
"396": "get_paged",
"397": "base.py",
"398": "soft_delete",
"399": "update",
"401": "_build_tree",
"402": "create",
"403": "get_by_id",
"404": "get_item_tree",
"405": "expense_item.py",
"406": "soft_delete",
"407": "_tenant_no",
"408": "update",
"409": "_count_invoices",
"410": "create",
"411": "delete",
"412": "get_mine_summary",
"413": "is_wallet_admin",
"414": "add_entry",
"415": "assert_can_delete_invoice",
"416": "calc_summary",
"417": "debit_reimburse",
"418": "list_entries",
"419": "_lock_summary",
"420": "refresh_summary",
"421": "release_reimburse",
"422": "list_ledgers",
"423": "list_wallets",
"424": "_lookup_user_name",
"425": "out_rows_with_attachments",
"426": "invoice.py",
"427": "_require_wallet_admin",
"428": "_resolve_owner",
"429": "_resolve_owner_for_query",
"430": "_summary_to_dict",
"431": "_tenant_no",
"432": "approve",
"433": "batch_offset",
"434": "build_apply_out_with_details",
"435": "build_expense_name_condition",
"436": "can_change_applicant",
"437": "create_with_details",
"438": "finance_payment_stats",
"439": "get_by_id",
"440": "get_details",
"441": "get_details_map_by_reimburse_ids",
"442": "get_linked_invoice_ids",
"443": "get_linked_invoice_ids_map",
"444": "get_linked_invoices_brief",
"445": "get_paged",
"446": "global_status_counts",
"447": "_has_debit_reimburse",
"448": "_latest_approval_per_node",
"449": "_my_apply_base",
"450": "my_apply_stats",
"451": "offset",
"452": "out_one_with_attachment_counts",
"453": "out_rows_with_attachment_counts",
"454": "pay",
"455": "reimburse.py",
"456": "_query_approvals",
"457": "reject",
"458": "resolve_applicant",
"459": "soft_delete",
"460": "submit",
"461": "sync_reimburse_invoices",
"462": "_tenant_no",
"463": "update_with_details",
"464": "withdraw",
"465": "_workflow_nodes",
"466": "ensure_by_biz_type",
"467": "get_by_biz_type",
"468": "get_by_id",
"469": "get_paged",
"470": "workflow.py",
"471": "save_nodes",
"472": "soft_delete",
"473": "_tenant_no",
"474": "update",
"487": "_get_variable",
"488": "_parse_node_config",
"489": "_parse_variables",
"490": "customer_pool.py",
"491": "__init__.py",
"495": "code:bash (cd /path/to/shanxia2.0)",
"496": "code:bash (./start-prod.sh status # 查看状态)",
"497": "code:bash (curl -s http://127.0.0.1:9111/health)",
"498": "code:bash (cd /iflytek/shanxia2.0/backend)",
"499": "code:bash (python -m venv venv)",
"500": "code:bash (pip install -r requirements.txt)",
"501": "code:env (# 数据库配置)",
"502": "code:bash (uvicorn app.main:app --reload --port 8000)",
"503": "BaseModel",
"504": "PlanResourceSnapshotMixin",
"510": "advance.py",
"511": "attachment.py",
"512": "expense_item.py",
"514": "workflow.py",
"517": "dashboard.py",
"518": "__init__.py",
"519": "inventory.py",
"520": "plans.py",
"521": "public_inventory.py",
"522": "weighing.py",
"523": "__init__.py",
"524": "leave.py",
"525": "menu.py",
"526": "base_data.py",
"527": "freight.py",
"528": "__init__.py",
"529": "inventory.py",
"530": "io_order.py",
"531": "plan.py",
"533": "transport_address.py",
"534": "transport.py",
"535": "vehicle_maintenance.py",
"536": "vehicle_recharge.py",
"537": "vehicle_urea.py",
"538": "workflow.py",
"539": "auth.py",
"545": "__init__.py",
"548": "storage.py",
"551": "base.py",
"553": "clue.py",
"554": "contact.py",
"555": "contract.py",
"556": "customer_follow.py",
"557": "customer_pool.py",
"558": "customer.py",
"559": "invoice_info.py",
"560": "opportunity.py",
"561": "pool_rule.py",
"562": "price_letter.py",
"563": "product.py",
"564": "quote.py",
"565": "statistics.py",
"566": "supplier_info.py",
"569": "__init__.py",
"570": "leave.py",
"571": "menu.py",
"573": "user.py",
"574": "workflow.py",
"576": "base.py",
"577": "__init__.py",
"578": "attachment.py",
"579": "auth.py",
"580": "advance.py",
"581": "attachment.py",
"582": "common.py",
"583": "expense_item.py",
"584": "__init__.py",
"585": "workflow.py",
"586": "common.py",
"587": "clue.py",
"588": "contact.py",
"589": "contract.py",
"590": "customer_follow.py",
"591": "customer_pool.py",
"592": "customer.py",
"593": "invoice_info.py",
"594": "opportunity.py",
"595": "pool_rule.py",
"596": "price_letter.py",
"597": "product.py",
"598": "quote.py",
"599": "statistics.py",
"600": "supplier_info.py",
"601": "dashboard.py",
"602": "__init__.py",
"603": "inventory.py",
"604": "plans.py",
"605": "public_inventory.py",
"606": "weighing.py",
"607": "__init__.py",
"608": "leave.py",
"609": "menu.py",
"610": "base_data.py",
"611": "freight.py",
"612": "__init__.py",
"613": "inventory.py",
"614": "io_order.py",
"615": "plan.py",
"616": "transport_address.py",
"617": "transport.py",
"618": "vehicle_maintenance.py",
"619": "vehicle_recharge.py",
"620": "vehicle_urea.py",
"621": "user.py",
"622": "workflow.py",
"623": "advance.py",
"624": "attachment.py",
"625": "base.py",
"626": "expense_item.py",
"627": "storage.py",
"628": "workflow.py",
"636": "customer_pool.py",
"638": "__init__.py"
}
@@ -0,0 +1,759 @@
# Graph Report - backend (2026-08-03)
## Corpus Check
- 303 files · ~206,532 words
- Verdict: corpus is large enough that graph structure adds value.
## Summary
- 4551 nodes · 15459 edges · 531 communities (511 shown, 20 thin omitted)
- Extraction: 97% EXTRACTED · 3% INFERRED · 0% AMBIGUOUS · INFERRED: 410 edges (avg confidence: 0.53)
- Token cost: 0 input · 0 output
## Graph Freshness
- Built from commit: `40fc5494`
- Run `git rev-parse HEAD` and compare to check if the graph is stale.
- Run `graphify update .` after code changes (no API cost).
## Community Hubs (Navigation)
- [[_COMMUNITY_Community 0|Community 0]]
- [[_COMMUNITY_Community 1|Community 1]]
- [[_COMMUNITY_Community 2|Community 2]]
- [[_COMMUNITY_Community 3|Community 3]]
- [[_COMMUNITY_Community 4|Community 4]]
- [[_COMMUNITY_Community 5|Community 5]]
- [[_COMMUNITY_Community 6|Community 6]]
- [[_COMMUNITY_Community 7|Community 7]]
- [[_COMMUNITY_Community 8|Community 8]]
- [[_COMMUNITY_Community 11|Community 11]]
- [[_COMMUNITY_Community 12|Community 12]]
- [[_COMMUNITY_Community 13|Community 13]]
- [[_COMMUNITY_Community 14|Community 14]]
- [[_COMMUNITY_Community 15|Community 15]]
- [[_COMMUNITY_Community 16|Community 16]]
- [[_COMMUNITY_Community 18|Community 18]]
- [[_COMMUNITY_Community 19|Community 19]]
- [[_COMMUNITY_Community 21|Community 21]]
- [[_COMMUNITY_Community 22|Community 22]]
- [[_COMMUNITY_Community 23|Community 23]]
- [[_COMMUNITY_Community 24|Community 24]]
- [[_COMMUNITY_Community 25|Community 25]]
- [[_COMMUNITY_Community 26|Community 26]]
- [[_COMMUNITY_Community 27|Community 27]]
- [[_COMMUNITY_Community 28|Community 28]]
- [[_COMMUNITY_Community 29|Community 29]]
- [[_COMMUNITY_Community 30|Community 30]]
- [[_COMMUNITY_Community 33|Community 33]]
- [[_COMMUNITY_Community 34|Community 34]]
- [[_COMMUNITY_Community 35|Community 35]]
- [[_COMMUNITY_Community 36|Community 36]]
- [[_COMMUNITY_Community 37|Community 37]]
- [[_COMMUNITY_Community 38|Community 38]]
- [[_COMMUNITY_Community 39|Community 39]]
- [[_COMMUNITY_Community 42|Community 42]]
- [[_COMMUNITY_Community 43|Community 43]]
- [[_COMMUNITY_Community 44|Community 44]]
- [[_COMMUNITY_Community 45|Community 45]]
- [[_COMMUNITY_Community 46|Community 46]]
- [[_COMMUNITY_Community 48|Community 48]]
- [[_COMMUNITY_Community 49|Community 49]]
- [[_COMMUNITY_Community 50|Community 50]]
- [[_COMMUNITY_Community 51|Community 51]]
- [[_COMMUNITY_Community 52|Community 52]]
- [[_COMMUNITY_Community 53|Community 53]]
- [[_COMMUNITY_Community 54|Community 54]]
- [[_COMMUNITY_Community 58|Community 58]]
- [[_COMMUNITY_Community 59|Community 59]]
- [[_COMMUNITY_Community 60|Community 60]]
- [[_COMMUNITY_Community 61|Community 61]]
- [[_COMMUNITY_Community 62|Community 62]]
- [[_COMMUNITY_Community 63|Community 63]]
- [[_COMMUNITY_Community 64|Community 64]]
- [[_COMMUNITY_Community 65|Community 65]]
- [[_COMMUNITY_Community 66|Community 66]]
- [[_COMMUNITY_Community 67|Community 67]]
- [[_COMMUNITY_Community 68|Community 68]]
- [[_COMMUNITY_Community 69|Community 69]]
- [[_COMMUNITY_Community 70|Community 70]]
- [[_COMMUNITY_Community 71|Community 71]]
- [[_COMMUNITY_Community 72|Community 72]]
- [[_COMMUNITY_Community 73|Community 73]]
- [[_COMMUNITY_Community 74|Community 74]]
- [[_COMMUNITY_Community 77|Community 77]]
- [[_COMMUNITY_Community 78|Community 78]]
- [[_COMMUNITY_Community 81|Community 81]]
- [[_COMMUNITY_Community 83|Community 83]]
- [[_COMMUNITY_Community 84|Community 84]]
- [[_COMMUNITY_Community 86|Community 86]]
- [[_COMMUNITY_Community 87|Community 87]]
- [[_COMMUNITY_Any|Any]]
- [[_COMMUNITY_pl_ocr_service.py|pl_ocr_service.py]]
- [[_COMMUNITY_pl_vehicle_recharge_service.py|pl_vehicle_recharge_service.py]]
- [[_COMMUNITY_Community 92|Community 92]]
- [[_COMMUNITY__invoice_info_by_customer_impl|_invoice_info_by_customer_impl]]
- [[_COMMUNITY_response.py|response.py]]
- [[_COMMUNITY_Community 95|Community 95]]
- [[_COMMUNITY__ensure_subpack_slip_no|_ensure_subpack_slip_no]]
- [[_COMMUNITY_price_letter_detail|price_letter_detail]]
- [[_COMMUNITY_.to_tenant_user_dict|.to_tenant_user_dict]]
- [[_COMMUNITY_dept.py|dept.py]]
- [[_COMMUNITY_apply_crm_create_by_scope|apply_crm_create_by_scope]]
- [[_COMMUNITY_list_customers|list_customers]]
- [[_COMMUNITY_Community 106|Community 106]]
- [[_COMMUNITY_apply_pl_scope|apply_pl_scope]]
- [[_COMMUNITY_Community 109|Community 109]]
- [[_COMMUNITY_Base|Base]]
- [[_COMMUNITY_Community 111|Community 111]]
- [[_COMMUNITY_storage.py|storage.py]]
- [[_COMMUNITY_quote_share_log.py|quote_share_log.py]]
- [[_COMMUNITY_db_models.py|db_models.py]]
- [[_COMMUNITY_site.py|site.py]]
- [[_COMMUNITY_Community 116|Community 116]]
- [[_COMMUNITY_Community 118|Community 118]]
- [[_COMMUNITY_Community 119|Community 119]]
- [[_COMMUNITY_Community 122|Community 122]]
- [[_COMMUNITY_Community 125|Community 125]]
- [[_COMMUNITY_customer_pool.py|customer_pool.py]]
- [[_COMMUNITY_Community 127|Community 127]]
- [[_COMMUNITY_plan.py|plan.py]]
- [[_COMMUNITY_BaseStorage|BaseStorage]]
- [[_COMMUNITY_datetime_parse.py|datetime_parse.py]]
- [[_COMMUNITY_LocalStorage|LocalStorage]]
- [[_COMMUNITY_crm_supplier_hazard.py|crm_supplier_hazard.py]]
- [[_COMMUNITY_Community 134|Community 134]]
- [[_COMMUNITY_post.py|post.py]]
- [[_COMMUNITY_Community 136|Community 136]]
- [[_COMMUNITY_statistics.py|statistics.py]]
- [[_COMMUNITY_Community 138|Community 138]]
- [[_COMMUNITY_Community 139|Community 139]]
- [[_COMMUNITY_clue_statistics.py|clue_statistics.py]]
- [[_COMMUNITY_SysEthnic|SysEthnic]]
- [[_COMMUNITY_SysApiRequestLog|SysApiRequestLog]]
- [[_COMMUNITY_minio_url.py|minio_url.py]]
- [[_COMMUNITY___init__.py|__init__.py]]
- [[_COMMUNITY_Community 147|Community 147]]
- [[_COMMUNITY_ContractListQuery|ContractListQuery]]
- [[_COMMUNITY_Community 151|Community 151]]
- [[_COMMUNITY_MinIOStorage|MinIOStorage]]
- [[_COMMUNITY_web_login_rsa_public_key|web_login_rsa_public_key]]
- [[_COMMUNITY_get_client_ip|get_client_ip]]
- [[_COMMUNITY_context.py|context.py]]
- [[_COMMUNITY_register_local_callback|register_local_callback]]
- [[_COMMUNITY_backfill_crm_contract_attachment_urls.py|backfill_crm_contract_attachment_urls.py]]
- [[_COMMUNITY_login_crypto.py|login_crypto.py]]
- [[_COMMUNITY_codeblock1 (iflytekshanxia2.0backend)|code:block1 (/iflytek/shanxia2.0/backend)]]
- [[_COMMUNITY_test_quote_approver_setting.py|test_quote_approver_setting.py]]
- [[_COMMUNITY_BaseStorage|BaseStorage]]
- [[_COMMUNITY_LocalStorage|LocalStorage]]
- [[_COMMUNITY_resolve_create_time_bounds|resolve_create_time_bounds]]
- [[_COMMUNITY_datetime_parse.py|datetime_parse.py]]
- [[_COMMUNITY__build_user_info_payload|_build_user_info_payload]]
- [[_COMMUNITY__ensure_subpack_slip_no|_ensure_subpack_slip_no]]
- [[_COMMUNITY_menu.py|menu.py]]
- [[_COMMUNITY_create_driver|create_driver]]
- [[_COMMUNITY_crm_quote_pdf.py|crm_quote_pdf.py]]
- [[_COMMUNITY_shanxia2.0 后端骨架(FastAPI)|shanxia2.0 后端骨架(FastAPI)]]
- [[_COMMUNITY_api_crypto_middleware.py|api_crypto_middleware.py]]
- [[_COMMUNITY_order_status_compat.py|order_status_compat.py]]
- [[_COMMUNITY_shanxia2.0 Backend 生产部署说明|shanxia2.0 Backend 生产部署说明]]
- [[_COMMUNITY_db_collation.py|db_collation.py]]
- [[_COMMUNITY_一次上传一条发票:多文件挂同一档案,金额为总额,只写一条额度流水。|一次上传一条发票:多文件挂同一档案,金额为总额,只写一条额度流水。]]
- [[_COMMUNITY___init__.py|__init__.py]]
- [[_COMMUNITY___init__.py|__init__.py]]
- [[_COMMUNITY_codebash (cd pathtoshanxia2.0)|code:bash (cd /path/to/shanxia2.0)]]
- [[_COMMUNITY_codebash (.start-prod.sh status 查看状态)|code:bash (./start-prod.sh status # 查看状态)]]
- [[_COMMUNITY_codebash (curl -s http127.0.0.19111health)|code:bash (curl -s http://127.0.0.1:9111/health)]]
- [[_COMMUNITY_codebash (cd iflytekshanxia2.0backend)|code:bash (cd /iflytek/shanxia2.0/backend)]]
- [[_COMMUNITY_codebash (python -m venv venv)|code:bash (python -m venv venv)]]
- [[_COMMUNITY_codebash (pip install -r requirements.txt)|code:bash (pip install -r requirements.txt)]]
- [[_COMMUNITY_codeenv ( 数据库配置)|code:env (# 数据库配置)]]
- [[_COMMUNITY_codebash (uvicorn app.mainapp --reload --port 8000)|code:bash (uvicorn app.main:app --reload --port 8000)]]
- [[_COMMUNITY_BaseModel|BaseModel]]
- [[_COMMUNITY_PlanResourceSnapshotMixin|PlanResourceSnapshotMixin]]
## God Nodes (most connected - your core abstractions)
1. `BaseModel` - 442 edges
2. `get_tenant_no()` - 349 edges
3. `DbUserContext` - 254 edges
4. `success_response()` - 253 edges
5. `error_response()` - 235 edges
6. `generate_uuid()` - 195 edges
7. `BxUserContext` - 183 edges
8. `crm_user_info()` - 137 edges
9. `assert_row_deletable_by_creator()` - 83 edges
10. `ApiResponse` - 79 edges
## Surprising Connections (you probably didn't know these)
- `BackfillRule` --uses--> `Settings` [INFERRED]
scripts/migrate_crm_sys_oss.py → app/core/config.py
- `MigrationStats` --uses--> `Settings` [INFERRED]
scripts/migrate_bx_data.py → app/core/config.py
- `StepStats` --uses--> `Settings` [INFERRED]
scripts/migrate_bx_data.py → app/core/config.py
- `MigrationStats` --uses--> `Settings` [INFERRED]
scripts/migrate_crm_clue.py → app/core/config.py
- `StepStats` --uses--> `Settings` [INFERRED]
scripts/migrate_crm_clue.py → app/core/config.py
## Import Cycles
- None detected.
## Communities (531 total, 20 thin omitted)
### Community 0 - "Community 0"
Cohesion: 0.11
Nodes (38): approve_settlement_legacy(), cancel_settlement(), create_settlement(), delete_settlement(), disburse_settlement(), _generate_settlement_no(), get_settlement(), get_settlement_approval_flow() (+30 more)
### Community 1 - "Community 1"
Cohesion: 0.08
Nodes (46): CRM 合同子模块路由 — 变更/模板/审批独立页, register_contract_sub_routes(), execute_change(), export_contract_templates(), _get_change_row(), list_contract_templates(), Java Controller TODO — 保持 pending,记录备注, submit_change_approval() (+38 more)
### Community 2 - "Community 2"
Cohesion: 0.19
Nodes (24): register_quote_routes(), accept_quote(), create_new_version(), _ensure_quote_in_scope(), generate_pdf(), generate_share(), get_quote(), get_quote_detail() (+16 more)
### Community 3 - "Community 3"
Cohesion: 0.07
Nodes (89): attachment_download_url(), attachment_preview_url(), AttachmentDownloadUrlBody, create_dept(), create_dict_data(), create_dict_type(), create_menu(), create_post() (+81 more)
### Community 4 - "Community 4"
Cohesion: 0.09
Nodes (75): _apply_list_filters(), assign_opportunities(), batch_update_expect_date(), batch_update_stage(), change_stage(), create_opportunity(), _decimal_value(), _enrich_user_names() (+67 more)
### Community 5 - "Community 5"
Cohesion: 0.19
Nodes (26): Settings, BaseSettings, count_source_rows(), derive_source_url(), fetch_portal_admin_id(), fetch_source_rows(), legacy_id_to_str(), load_runtime_settings() (+18 more)
### Community 6 - "Community 6"
Cohesion: 0.05
Nodes (100): create_contact(), get_contact(), list_contacts_by_customer(), _parse_optional_date(), Any, date, Session, _row_to_dict() (+92 more)
### Community 7 - "Community 7"
Cohesion: 0.12
Nodes (36): get_password_hash(), Namespace, build_merged_users(), bx_role_labels(), check_minio(), check_mysql(), check_redis(), _clip() (+28 more)
### Community 8 - "Community 8"
Cohesion: 0.13
Nodes (40): _build_quote_filters(), calculate_quote_amount(), create_quote(), _decimal_value(), _enrich_contact_fields(), _enrich_supplier_name(), _fill_contract_linked_count(), generate_quote_no() (+32 more)
### Community 11 - "Community 11"
Cohesion: 0.11
Nodes (54): cancel_apply(), create_apply(), create_disburse(), delete_applies(), delete_disburses(), get_apply(), get_disburse(), list_applies() (+46 more)
### Community 12 - "Community 12"
Cohesion: 0.06
Nodes (51): create_leave(), delete_leave(), get_leave(), Session, submit_leave(), update_leave(), AttachmentUploadResponse, ChangePasswordRequest (+43 more)
### Community 13 - "Community 13"
Cohesion: 0.15
Nodes (32): create_instance(), create_process_def(), create_task(), get_all_tasks_for_business(), get_instance(), get_latest_version(), get_nodes_by_version(), get_pending_tasks_by_node() (+24 more)
### Community 14 - "Community 14"
Cohesion: 0.16
Nodes (32): check_document_expiry(), document_reminder_stats(), get_inventory_ledger(), get_summary(), list_document_reminders(), list_transactions(), list_warnings(), date (+24 more)
### Community 15 - "Community 15"
Cohesion: 0.11
Nodes (14): BxReimburseApply, BxReimburseApproval, BxReimburseDetail, 报销单勾选的发票附件(同一发票可勾选多个附件,各占一条)。, ReimburseApplyOut, ReimburseInvoiceBriefOut, ReimburseInvoiceLinkIn, Any (+6 more)
### Community 16 - "Community 16"
Cohesion: 0.09
Nodes (60): cancel_apply(), create_apply(), create_record(), delete_applies(), delete_records(), get_apply(), get_record(), list_applies() (+52 more)
### Community 18 - "Community 18"
Cohesion: 0.09
Nodes (38): advance_pending_approval_stats(), advance_status_counts(), approve_advance(), approved_by_me(), create_advance(), delete_advance(), _fill_offset_amounts(), get_advance() (+30 more)
### Community 19 - "Community 19"
Cohesion: 0.09
Nodes (48): _avatar_url_field(), _build_user_info_payload(), change_current_user_password(), get_current_user_profile(), get_web_auth_menu_codes(), _jwt_user_payload(), logout(), public_quote_share() (+40 more)
### Community 21 - "Community 21"
Cohesion: 0.13
Nodes (36): _assert_brand_deletable(), barcode_exists(), category_code_exists(), _category_row_to_dict(), create_category(), create_product(), generate_auto_product_no(), generate_barcode() (+28 more)
### Community 22 - "Community 22"
Cohesion: 0.08
Nodes (28): parse_admin_request_json(), Any, Request, 运营 Web(/web/admin)JSON 请求体解析。 与 ApiCryptoMiddleware 配合:中间件未改写 body 时,在路由内解密 `{, 读取 JSON body;dict 时走 resolve_request_payload(明文 / 密文 / data 包装)。 Returns:, ApiCryptoError, _b64url_decode(), _b64url_encode() (+20 more)
### Community 23 - "Community 23"
Cohesion: 0.18
Nodes (8): convert_heic_to_jpeg(), MinIOStorage, process_image_content(), Any, 从 MINIO_BASE_URL 解析公网 endpoint;未配置或与内网相同时复用 self.client。, 将 HEIC/HEIF 格式转换为 JPEG 格式, 处理图片内容,如果是 HEIC 格式则转换为 JPEG, BytesIO
### Community 24 - "Community 24"
Cohesion: 0.10
Nodes (26): API 路由包 — 山峡化工生产一体化门户, _leave_local_callback(), leave_workflow_callback(), list_leaves(), get_menu_list(), 运营端菜单接口(与 web-admin 前端静态路由 sqxp.ts 对齐,14 个一级菜单), menu_badge_stats(), Session (+18 more)
### Community 25 - "Community 25"
Cohesion: 0.10
Nodes (30): _build_public_page(), _format_file_size(), _format_plan_date(), get_public_transport(), _get_transport_by_token(), _parse_feedback_images(), Session, UploadFile (+22 more)
### Community 26 - "Community 26"
Cohesion: 0.28
Nodes (17): _address_to_read(), _build_address_query(), create_transport_address(), delete_transport_address(), _get_address(), get_transport_address(), list_all_transport_addresses(), list_transport_addresses() (+9 more)
### Community 27 - "Community 27"
Cohesion: 0.15
Nodes (33): _apply_plan_product_update(), _apply_plan_update(), AutoGenerateMonthlyResult, _base_query(), create_plan(), _generate_plan_no(), get_plan(), _get_plan_by_id_only() (+25 more)
### Community 28 - "Community 28"
Cohesion: 0.11
Nodes (55): PlFreightSettlement, PlFreightSettlementApproval, 运费结算表 pl_freight_settlement, 运费结算审批 pl_freight_settlement_approval, FreightSettlementDisburseBody, FreightSettlementSubmitApprovalBody, FreightSettlementUpdate, _advance_after_approval() (+47 more)
### Community 29 - "Community 29"
Cohesion: 0.12
Nodes (31): _build_public_page(), _build_site_product_read(), _format_file_size(), get_public_site(), _get_site_link_by_token(), _parse_feedback_images(), Session, UploadFile (+23 more)
### Community 30 - "Community 30"
Cohesion: 0.16
Nodes (26): clear_target_categories(), compute_level(), count_target_rows(), derive_source_url(), fetch_portal_admin_id(), fetch_source_rows(), legacy_id_to_str(), load_runtime_settings() (+18 more)
### Community 33 - "Community 33"
Cohesion: 0.12
Nodes (29): bind_biz(), delete_attachment(), download_file(), list_by_biz(), _parse_id_list(), preview_attachment(), Session, UploadFile (+21 more)
### Community 34 - "Community 34"
Cohesion: 0.24
Nodes (7): camel_to_snake(), CRUDBase, get_model_attr(), HistoryCRUD, Any, Session, ModelType
### Community 35 - "Community 35"
Cohesion: 0.18
Nodes (36): clear_portal_workflow(), _dec(), derive_source_url(), fetch_portal_admin_id(), fetch_source_rows(), legacy_id_to_str(), load_bx_user_mapping(), load_runtime_settings() (+28 more)
### Community 36 - "Community 36"
Cohesion: 0.11
Nodes (36): approve_task(), _assert_wf_instance_in_scope(), _business_type_label(), create_process_def(), _enrich_task_list(), get_business_all_tasks(), _get_business_form_fields(), get_designer_data() (+28 more)
### Community 37 - "Community 37"
Cohesion: 0.18
Nodes (24): delete_plan(), delete_carrier(), unbind_vehicle_driver(), delete_plan(), _actor_ids(), _actor_names(), assert_row_deletable_by_creator(), assert_rows_deletable_by_creator() (+16 more)
### Community 38 - "Community 38"
Cohesion: 0.14
Nodes (25): load_brand_qtys_by_plan_product_ids(), persist_plan_product_brand_qtys(), Any, Decimal, Session, 按计划产品明细 ID 批量加载归档品牌数量(brandId/brandName/qty)。, 软删旧品牌量行后按归档结果插入;无品牌约定则只软删不插入。, validate_brand_qty_rows() (+17 more)
### Community 39 - "Community 39"
Cohesion: 0.11
Nodes (51): assign_clues(), check_convert(), check_duplicate_clue(), convert_clue(), create_clue(), _enrich_user_names(), _ensure_clue_in_scope(), create_clue_follow() (+43 more)
### Community 42 - "Community 42"
Cohesion: 0.19
Nodes (32): _dec(), derive_source_url(), fetch_portal_admin_id(), fetch_source_rows(), legacy_id_to_str(), load_oss_id_to_url(), load_runtime_settings(), load_settings_env() (+24 more)
### Community 43 - "Community 43"
Cohesion: 0.10
Nodes (30): create_invoice(), delete_invoice(), get_mine_wallet_summary(), list_invoice_ledgers(), list_invoice_wallets(), list_invoices(), Request, Session (+22 more)
### Community 44 - "Community 44"
Cohesion: 0.13
Nodes (17): ApiCryptoMiddleware, ApiRequestLogMiddleware, _db_pool_keepalive_loop(), _error_json(), lifespan(), Exception, Request, 数据库异常:返回 JSON 并保留 CORS 头,避免浏览器只报跨域错误 (+9 more)
### Community 45 - "Community 45"
Cohesion: 0.11
Nodes (60): _apply_plan_logistics_from_payload(), _apply_transport_arrange_fields(), arrange_transport(), _assert_vehicle_carrier_eligible(), _build_arrange_result(), _build_driver_url(), create_transport(), _ensure_driver_access_token() (+52 more)
### Community 46 - "Community 46"
Cohesion: 0.12
Nodes (49): _apply_contract_entity_fields(), _build_contract_filters(), _calc_amount_with_tax(), _change_row_to_dict(), contract_no_exists(), _contract_period_key(), create_contract(), create_contract_change() (+41 more)
### Community 48 - "Community 48"
Cohesion: 0.20
Nodes (26): _apply_inventory_product_update(), _apply_inventory_update(), _base_query(), create_inventory(), create_link_for_plan(), delete_inventory(), _generate_batch_no(), _get_inventory() (+18 more)
### Community 49 - "Community 49"
Cohesion: 0.17
Nodes (14): _enrich_change_rows(), export_contract_changes(), list_contract_changes(), soft_delete_contract_changes(), soft_delete_contract_templates(), apply_crm_create_by_scope(), ensure_crm_create_by_row_in_scope(), ensure_crm_owner_row_in_scope() (+6 more)
### Community 50 - "Community 50"
Cohesion: 0.15
Nodes (9): 微信消息通知服务 支持微信小程序模板消息推送 使用方式: from app.core.wechat_message import WechatMes, 发送订单状态通知 Args: openid: 用户openid order_n, 发送支付成功通知 Args: openid: 用户openid order_n, 发送配送通知 Args: openid: 配送员openid task_no:, 发送配送时效预警 Args: openid: 配送员openid task_n, 发送退款结果通知 Args: openid: 用户openid order_n, 发送催单通知给配送员 Args: deliver_openid: 配送员openid, 发送模板消息 Args: openid: 用户openid template_ (+1 more)
### Community 51 - "Community 51"
Cohesion: 0.18
Nodes (20): _dict_field(), format_brand_qty_summary(), load_transport_driver_product_feedbacks(), persist_io_item_brand_qtys(), persist_transport_product_feedback(), Any, Decimal, Session (+12 more)
### Community 52 - "Community 52"
Cohesion: 0.18
Nodes (29): _dec(), derive_source_url(), fetch_portal_admin_id(), fetch_source_rows(), legacy_id_to_str(), load_oss_id_to_url(), load_runtime_settings(), load_settings_env() (+21 more)
### Community 53 - "Community 53"
Cohesion: 0.12
Nodes (31): BxReimburseInvoice, BxReimburseOffset, 报销(bx)模块数据模型 — 对齐 docs/sql/20260606_bx_tables.sql, filter_current_cycle_approvals(), 审批轮次过滤 — 驳回后重新提交时,进度接口仅展示当前轮次。, 审批进度仅展示当前轮次;完整历史由 get_approval_history 返回。, allocate_reimburse_title_seq(), format_reimburse_title() (+23 more)
### Community 54 - "Community 54"
Cohesion: 0.25
Nodes (24): _get_by_plan(), get_inbound(), get_inbound_by_plan(), _get_order(), get_outbound(), get_outbound_by_plan(), inbound_site_url(), list_inbound() (+16 more)
### Community 58 - "Community 58"
Cohesion: 0.18
Nodes (17): get_oper_log_meta(), OperLogMeta, 请求级操作日志元数据(ContextVar), 供异步落库线程使用,避免 ContextVar 跨线程丢失。, set_oper_log_meta(), snapshot_oper_log_meta(), _format_summary(), oper_log() (+9 more)
### Community 59 - "Community 59"
Cohesion: 0.20
Nodes (28): _dec(), derive_source_url(), fetch_portal_admin_id(), fetch_source_rows(), legacy_id_to_str(), load_oss_id_to_url(), load_runtime_settings(), load_settings_env() (+20 more)
### Community 60 - "Community 60"
Cohesion: 0.18
Nodes (17): _alias_row_to_dict(), _build_category_tree(), _collect_category_with_ancestors(), list_category_tree(), list_oss_by_biz(), list_product_options(), list_products(), _load_brands_aliases_maps() (+9 more)
### Community 61 - "Community 61"
Cohesion: 0.16
Nodes (33): DbInboundOutbound, PlTransportDbLink, 运输-地磅出入库关联 pl_transport_db_link, _apply_plan_quantity_delta(), _create_db_inventory(), _create_db_plan(), DibangInventoryLinkResult, find_monthly_db_plan() (+25 more)
### Community 62 - "Community 62"
Cohesion: 0.21
Nodes (22): clear_target_products(), count_target_rows(), _dec(), derive_source_url(), fetch_portal_admin_id(), fetch_source_rows(), legacy_id_to_str(), load_runtime_settings() (+14 more)
### Community 63 - "Community 63"
Cohesion: 0.14
Nodes (17): create_expense_item(), delete_expense_item(), get_expense_item(), get_expense_item_tree(), Session, update_expense_item(), BxExpenseItem, _coerce_id() (+9 more)
### Community 64 - "Community 64"
Cohesion: 0.06
Nodes (167): category_add(), category_delete(), category_tree(), category_update(), clue_assign(), clue_check_convert(), clue_check_duplicate(), clue_convert() (+159 more)
### Community 65 - "Community 65"
Cohesion: 0.10
Nodes (58): cancel_apply(), create_apply(), create_process(), delete_applies(), delete_processes(), get_apply(), get_process(), list_applies() (+50 more)
### Community 66 - "Community 66"
Cohesion: 0.18
Nodes (18): create_workflow(), delete_workflow(), get_workflow(), get_workflow_by_biz_type(), list_workflows(), Session, save_workflow_nodes(), update_workflow() (+10 more)
### Community 67 - "Community 67"
Cohesion: 0.22
Nodes (13): 运输计划 - 安排运输时车辆/人员/证件资源快照 Schema, SnapshotCertRead, SnapshotPersonnelRow, TransportResourceSnapshot, build_personnel_summary(), links: [(personnel_name, role, is_default)] → (summary, default_driver_name)., apply_resource_snapshot_json(), build_transport_resource_snapshot() (+5 more)
### Community 68 - "Community 68"
Cohesion: 0.16
Nodes (28): bind_vehicle_driver(), delete_vehicle(), paste_recognize_commit(), generate_uuid(), PlVehiclePersonnel, 车辆-人员关联表 pl_vehicle_personnel, VehicleDriverCreate, VehiclePersonnelRow (+20 more)
### Community 69 - "Community 69"
Cohesion: 0.14
Nodes (26): _append_user_scope_conditions(), apply_data_scope(), assert_row_in_data_scope(), _collect_scope_ids(), _get_child_dept_ids(), get_user_dept_id(), Session, 数据权限过滤(V0.3:基于角色 data_scope 的查询级行级过滤) data_scope 值: 1 = 全部数据 2 = 本部门数据 3 (+18 more)
### Community 70 - "Community 70"
Cohesion: 0.39
Nodes (10): create_post(), get_post(), list_posts(), post_code_exists(), Any, Session, _row_to_dict(), soft_delete_post() (+2 more)
### Community 71 - "Community 71"
Cohesion: 0.03
Nodes (134): CRM 报价单 API 路由 — 挂接到 crm.router, BaseModel, PagedQuery, PagedResponse, WorkflowNodeOut, ClueAssignBody, ClueConvertBody, ClueCreateBody (+126 more)
### Community 72 - "Community 72"
Cohesion: 0.19
Nodes (28): apply_biz_backfill(), BackfillRule, BizRef, build_biz_index(), count_target_rows(), derive_source_url(), fetch_portal_admin_id(), fetch_source_rows() (+20 more)
### Community 73 - "Community 73"
Cohesion: 0.20
Nodes (25): derive_source_url(), fetch_portal_admin_id(), fetch_source_rows(), legacy_id_to_str(), load_oss_id_to_url(), load_runtime_settings(), load_settings_env(), load_user_mapping() (+17 more)
### Community 74 - "Community 74"
Cohesion: 0.35
Nodes (11): create_dict_type(), dict_type_code_exists(), get_dict_type(), list_dict_types(), Any, Session, 字典类型 CRUD(sys_dict_type), _row_to_dict() (+3 more)
### Community 77 - "Community 77"
Cohesion: 0.11
Nodes (12): get_wechat_pay_client(), get_wechat_pay_v2(), Any, 微信支付工具模块 支持微信支付 V3 API 文档参考: https://pay.weixin.qq.com/docs/v3/apis/index.html, 统一下单接口 (V3) Args: out_trade_no: 商户订单号 total_amo, 申请退款 Args: out_trade_no: 商户订单号 total_amount: 订单, 小程序调起支付的二次签名参数(与公众号 JSAPI 规则一致), 统一下单 (V2 API) Args: out_trade_no: 商户订单号 total_f (+4 more)
### Community 78 - "Community 78"
Cohesion: 0.20
Nodes (16): _claims_from_auth(), _format_body_bytes(), _persist_log(), Any, Request, 统一记录 API 请求/响应出入参的中间件, _redact_obj(), _schedule_persist_log() (+8 more)
### Community 81 - "Community 81"
Cohesion: 0.17
Nodes (20): operlog_detail(), operlog_list(), Session, 操作日志分页列表(基于 API 请求日志适配 RuoYi OperLog 结构), _user_info(), 获取用户所有角色的数据权限配置。 返回: [{"data_scope": str, "details": [SysRoleDataScopeDetai, _resolve_user_role_scopes(), _apply_oper_log_scope() (+12 more)
### Community 83 - "Community 83"
Cohesion: 0.15
Nodes (30): _advance_contract_after_approval(), approve_contract(), approve_contract_by_contract_id(), approve_pass(), approve_reject(), approve_transfer(), _build_approval_list_filters(), can_user_approve() (+22 more)
### Community 84 - "Community 84"
Cohesion: 0.23
Nodes (23): build_id_maps(), clip_text(), count_rows(), export_mapping(), fetch_rows(), legacy_id_to_str(), main(), map_id() (+15 more)
### Community 86 - "Community 86"
Cohesion: 0.19
Nodes (26): _apply_weighing_product_update(), _apply_weighing_update(), _base_query(), create_weighing(), delete_weighing(), _get_weighing(), get_weighing_related(), list_weighing() (+18 more)
### Community 87 - "Community 87"
Cohesion: 0.20
Nodes (19): approve_quote(), can_user_approve(), create_approval_records(), delete_pending_approvals(), get_quote_approval(), list_quote_approvals(), Any, Session (+11 more)
### Community 88 - "Any"
Cohesion: 0.18
Nodes (30): _dec(), derive_source_url(), fetch_portal_admin_id(), fetch_source_rows(), legacy_id_to_str(), legacy_parent_id(), load_oss_id_to_url(), load_runtime_settings() (+22 more)
### Community 89 - "pl_ocr_service.py"
Cohesion: 0.19
Nodes (22): PlOcrUsageMonth, 阿里云 OCR 月度调用计数 pl_ocr_usage_month(按自然月全局统计), assert_quota_available(), _build_ocr_client(), compress_image_bytes(), current_usage_month(), _first_attachment_url(), get_or_create_usage_row() (+14 more)
### Community 91 - "pl_vehicle_recharge_service.py"
Cohesion: 0.10
Nodes (50): CRM 合同 API 路由 — GET/PUT/DELETE/POST 对齐原 Java CRM, register_contract_routes(), 接口菜单权限守卫。super_admin 直接放行。, require_menu_code(), batch_assign_owner(), batch_submit_contract_approval(), contract_row_to_dict(), download_contract_pdf() (+42 more)
### Community 92 - "Community 92"
Cohesion: 0.16
Nodes (25): 验证密码(哈希非 bcrypt 或损坏时返回 False,避免抛错), verify_password(), change_password(), create_user(), get_user(), get_user_with_relations(), _hash_password(), list_post_ids_for_user() (+17 more)
### Community 93 - "_invoice_info_by_customer_impl"
Cohesion: 0.28
Nodes (11): assert_products_allowed_for_supplier(), enrich_products_with_hazard_flags(), get_supplier_hazard_flags(), product_allowed(), Any, Session, 批量回填明细行 isToxic/isExplosive(来自产品主数据,供前端切换主体剔除)。, test_dual_requires_both() (+3 more)
### Community 94 - "response.py"
Cohesion: 0.19
Nodes (16): get_storage(), 文件存储工具模块 支持多种存储方式: 1. 本地存储 (local) - 默认 2. 七牛云存储 (qiniu) - 推荐生产环境使用 3. 阿里云OSS (, download_file_bytes(), file_stream(), get_access_url(), get_download_url(), get_preview_url(), normalize_storage_path() (+8 more)
### Community 95 - "Community 95"
Cohesion: 0.25
Nodes (12): Config, create_tenant(), get_tenant(), list_tenants(), Any, Session, _row_to_dict(), soft_delete_tenant() (+4 more)
### Community 97 - "_ensure_subpack_slip_no"
Cohesion: 0.13
Nodes (29): create_carrier(), get_carrier(), list_carriers(), reject_carrier(), update_carrier(), PlCarrier, carrier_to_read(), CarrierAuditReject (+21 more)
### Community 100 - "price_letter_detail"
Cohesion: 0.31
Nodes (13): apply_renumber(), build_mapping(), fetch_products(), format_new_product_no(), load_settings_env(), main(), product_id_sort_key(), Engine (+5 more)
### Community 101 - ".to_tenant_user_dict"
Cohesion: 0.21
Nodes (11): CRM 模块 Excel 导出列定义(字段与字典类型对齐 Java *Vo @ExcelDictFormat)。, _build_workbook(), DictLabelResolver, ExportColumn, load_dict_label_maps(), Any, Session, Excel 导出工具:集中处理字典 code → label(对齐 Java ExcelDictConvert / ExcelDictPreloadConver (+3 more)
### Community 102 - "dept.py"
Cohesion: 0.33
Nodes (13): _build_tree(), _cascade_soft_delete(), _collect_with_ancestors(), create_dept(), get_dept(), list_depts(), list_depts_tree(), Any (+5 more)
### Community 104 - "apply_crm_create_by_scope"
Cohesion: 0.22
Nodes (9): _dispatch_local(), Any, 工作流回调服务 —— 流程状态变更时通知业务系统, 注册本地回调函数。当 callback_url 以 path 开头时直接调用 fn。, 尝试本地直调。返回 True 表示已处理,False 表示无匹配的本地回调。, 流程实例状态变更后,向业务系统发送回调通知。 优先级: 1. 如果 callback_url 是内部路径(以 / 开头),优先尝试本地直调, register_local_callback(), trigger_callback() (+1 more)
### Community 105 - "list_customers"
Cohesion: 0.14
Nodes (34): create_vehicle(), get_vehicle(), list_personnel(), list_vehicles(), 运输计划管理 - 基础数据 API(承运商、车辆、人员), update_vehicle(), _vehicle_to_detail(), _vehicle_to_read() (+26 more)
### Community 106 - "Community 106"
Cohesion: 0.20
Nodes (14): clue_import_template(), customer_import_template(), _opp_list_kwargs(), opportunity_export(), opportunity_kanban(), opportunity_list(), opportunity_mine_list(), product_import_template() (+6 more)
### Community 107 - "apply_pl_scope"
Cohesion: 0.33
Nodes (8): allocate_next_biz_no(), _format_biz_no(), _prefix_for(), datetime, Session, 业务协议号自动生成:XBCC / JZCC + YYYYMMDD + 6 位序号, 原子分配下一个业务协议号。 须在 create 事务内调用;使用 MySQL LAST_INSERT_ID 保证并发安全。, BizNoKind
### Community 109 - "Community 109"
Cohesion: 0.09
Nodes (43): bx_health(), 报销(bx)API 路由聚合 — REST 风格,协议对齐 bx 原系统。, P0 健康检查:验证 bx 路由已挂载且响应协议为 code=0。, approve_reimburse(), approved_by_me(), _build_details_data(), create_reimburse(), delete_reimburse() (+35 more)
### Community 110 - "Base"
Cohesion: 0.20
Nodes (14): get_user_menu_codes(), Session, 读取用户授权的菜单权限标识集合(与前端 meta.menuCode / perms 对齐)。, _expand_with_ancestor_menu_ids(), _granted_menu_ids_for_user(), iter_user_menu_perm_codes(), Session, 角色仅勾选按钮(F)时,补齐祖先 C/M 菜单 id,便于前端匹配 :list 路由。 (+6 more)
### Community 111 - "Community 111"
Cohesion: 0.21
Nodes (17): _assert_editable(), _build_public_page(), _format_file_size(), _get_inventory_by_token(), get_public_inventory(), _parse_feedback_images(), Session, UploadFile (+9 more)
### Community 112 - "storage.py"
Cohesion: 0.09
Nodes (27): 供方全量列表(下拉)— Java: CrmSupplierInfoController.listAll, 修改供方 — Java: CrmSupplierInfoController.edit, 删除供方 — Java: CrmSupplierInfoController.remove, 导出供方列表 — Java: CrmSupplierInfoController.export, supplier_info_delete(), supplier_info_export(), supplier_info_list_all(), supplier_info_update() (+19 more)
### Community 113 - "quote_share_log.py"
Cohesion: 0.26
Nodes (12): list_share_logs(), _parse_user_agent(), Any, Session, CRM 报价单分享访问日志 CRUD — 对照 CrmQuoteShareLogServiceImpl.java, Java: statisticsVisits, 简易 User-Agent 解析(替代 Hutool UserAgentUtil), Java: queryLogsByQuoteId (+4 more)
### Community 114 - "db_models.py"
Cohesion: 0.24
Nodes (12): DbSequence, 地磅(db)模块数据模型 — 对齐 docs/sql/db_tables.sql, _allocate_inbound_outbound_id(), _ensure_sequence_row(), max_numeric_inbound_outbound_id(), next_inbound_outbound_id(), Session, 出入库单 id 生成 — 租户内纯数字自增,并发安全。 (+4 more)
### Community 115 - "site.py"
Cohesion: 0.39
Nodes (10): create_site(), get_site(), list_sites(), Any, Session, _row_to_dict(), site_code_exists(), soft_delete_site() (+2 more)
### Community 116 - "Community 116"
Cohesion: 0.19
Nodes (25): calculate_subtotal(), _decimal_value(), delete_products_by_quote_id(), _ensure_quote_parent_scope(), _get_field(), get_quote_product(), insert_quote_product(), list_quote_products() (+17 more)
### Community 118 - "Community 118"
Cohesion: 0.22
Nodes (22): batch_insert_contract_products(), calculate_subtotal(), _decimal_value(), delete_price_tiers_by_contract_id(), delete_products_by_contract_id(), _get_field(), insert_contract_product(), _letter_oss_ids_to_json() (+14 more)
### Community 119 - "Community 119"
Cohesion: 0.16
Nodes (28): CertRow, _cert_row_complete(), collect_required_cert_errors(), _find_cert_for_required(), get_required_personnel_cert_names(), match_required_cert_name(), _missing_field_labels(), Session (+20 more)
### Community 122 - "Community 122"
Cohesion: 0.39
Nodes (8): _brand_row_to_dict(), CrmProductBrand, apply_updates(), collect_candidates(), main(), parse_name(), Path, write_csv()
### Community 125 - "Community 125"
Cohesion: 0.07
Nodes (51): _build_page(), _get_by_token(), _make_handlers(), _models(), Session, 运输计划 · 入库/出库单现场免登录公共 API, SoftDeleteModel, BizAttachment (+43 more)
### Community 126 - "customer_pool.py"
Cohesion: 0.45
Nodes (11): _customer_base_query(), get_follow_statistics(), get_industry_statistics(), get_level_statistics(), get_overview_statistics(), get_owner_statistics(), get_source_statistics(), get_trend_statistics() (+3 more)
### Community 127 - "Community 127"
Cohesion: 0.21
Nodes (19): _cargo_stats(), get_dashboard_stats(), get_inbound_by_cargo(), get_inbound_by_partner_cargo(), get_outbound_by_cargo(), get_outbound_by_partner_cargo(), _partner_cargo_stats(), Session (+11 more)
### Community 128 - "plan.py"
Cohesion: 0.11
Nodes (60): create_plan_full(), 创建运输计划(完整表单:合同产品 + 可选预选车辆,不自动安排运输), update_plan(), QrCodeResponse, PlPlan, PlPlanProduct, PlPlanTransport, 计划产品明细表 pl_plan_product (+52 more)
### Community 129 - "BaseStorage"
Cohesion: 0.22
Nodes (4): PersonnelBase, PersonnelCreate, PersonnelUpdate, 车辆/人员 Schema 接受 photoUrls。
### Community 130 - "datetime_parse.py"
Cohesion: 0.40
Nodes (9): create_oss(), create_oss(), get_oss_by_id(), get_oss_by_url(), Session, 按主键 id 查询附件(兼容旧 CRM 雪花 oss_id 迁入后的记录), 将 fileUrl 或旧 CRM 数字 ossId 解析为 MinIO 相对路径。 门户新上传把相对路径写入业务字段;迁入合同附件仍存数字 ossId, resolve_storage_path() (+1 more)
### Community 131 - "LocalStorage"
Cohesion: 0.13
Nodes (18): ApiRequestLogCRUD, Session, list_ethnics(), Any, Session, 民族码表 CRUD(全局 sys_ethnic), _row_to_dict(), 数据模型(与 docs/sql/shanxia2.0.sql 全库导出基线对齐的核心系统表) (+10 more)
### Community 132 - "crm_supplier_hazard.py"
Cohesion: 0.29
Nodes (12): build_updates(), derive_source_url(), fetch_source_customers(), legacy_id_to_str(), load_oss_map(), load_runtime_settings(), load_settings_env(), main() (+4 more)
### Community 134 - "Community 134"
Cohesion: 0.25
Nodes (18): batch_generate_sample_codes(), batch_issue_sample_codes(), batch_revoke_sample_codes(), create_sample_code(), get_sample_code(), list_sample_codes(), _parse_issued_time(), Any (+10 more)
### Community 135 - "post.py"
Cohesion: 0.24
Nodes (12): get_last_month_range(), month_range(), date, Session, 地磅计划按月自动生成 — 迁移自 simple-dibang-xiaoshou/auto-create-plan/auto_create_monthly_pla, 按月自动生成采购计划。 - 默认仅在每月 1 日执行(force=True 时跳过该限制,供手动触发验收) - 遍历所有租户,按上月计划模板生, run_auto_generate_monthly_plans(), default_execution_status() (+4 more)
### Community 136 - "Community 136"
Cohesion: 0.19
Nodes (18): create_role(), get_role(), get_role_data_scope(), get_role_menu_ids(), list_roles(), Any, Session, 角色 CRUD(sys_role / sys_role_menu / sys_role_data_scope_detail) (+10 more)
### Community 137 - "statistics.py"
Cohesion: 0.22
Nodes (22): advance_issue_stats(), batch_offset(), export_advance_issues(), issue_advance(), list_advance_issues(), list_advance_offset_records(), list_pending_payments(), list_processed_payments() (+14 more)
### Community 138 - "Community 138"
Cohesion: 0.33
Nodes (19): check_python(), create_venv(), ensure_dirs(), ensure_valid_venv(), ensure_venv_and_runtime(), install_dependencies(), is_running(), log_error() (+11 more)
### Community 139 - "Community 139"
Cohesion: 0.33
Nodes (7): create_from_quote(), _quote_products_to_contract_products(), Java: createFromQuote — 返回合同 id, _parse_optional_date(), date, Java: toContract — 返回 contractId, to_contract()
### Community 141 - "clue_statistics.py"
Cohesion: 0.24
Nodes (12): normalize_attachment_csv(), extract_minio_object_key(), join_relative_urls(), normalize_oss_storage_url(), MinIO 附件 URL 规范化(数据迁移共用)。 背景 ---- CRM / 地磅 / 报销与 shanxia2.0 共用同一 MinIO(bucket 默, 业务表 OSS ID 串 → 相对 URL 串(用于客户证照、跟进附件等)。, 去掉预签名 query(``?X-Amz-...``)。, 将绝对/相对 URL 规范为 MinIO object key(不含 bucket)。 (+4 more)
### Community 143 - "SysEthnic"
Cohesion: 0.33
Nodes (11): create_leave(), get_leave(), list_leaves(), Any, Session, _row_to_dict(), soft_delete_leave(), update_leave() (+3 more)
### Community 144 - "SysApiRequestLog"
Cohesion: 0.11
Nodes (34): apply_site_feedback(), create_io_order_for_transport(), _dibang_qty_by_plan_product(), enrich_io_items_with_brand_feedback(), enrich_io_order_dict(), _format_io_product_summary_line(), get_active_io_order_for_plan(), _io_item_brand_qty_model() (+26 more)
### Community 146 - "__init__.py"
Cohesion: 0.28
Nodes (16): create_price_letter(), get_price_letter(), list_price_letters(), _parse_date(), Any, date, Session, _row_to_dict() (+8 more)
### Community 147 - "Community 147"
Cohesion: 0.37
Nodes (17): check_env(), check_python(), create_venv(), init_env(), install_dependencies(), log_error(), log_info(), log_success() (+9 more)
### Community 154 - "web_login_rsa_public_key"
Cohesion: 0.22
Nodes (10): invoice_info_by_customer(), _invoice_info_by_customer_impl(), invoice_info_list(), 按客户ID查询开票信息(与 by-customer 等价), InvoiceInfoByCustomerBody, InvoiceInfoCreateBody, InvoiceInfoDeleteBody, InvoiceInfoSetDefaultBody (+2 more)
### Community 156 - "get_client_ip"
Cohesion: 0.27
Nodes (11): create_dict_data(), get_dict_data(), list_dict_data(), list_dict_data_options(), Any, Session, 字典数据 CRUD(sys_dict_data), 字典下拉选项:租户主数据,忽略数据范围(user_info 保留兼容调用方,不参与过滤)。 (+3 more)
### Community 160 - "register_local_callback"
Cohesion: 0.29
Nodes (6): Config, format_api_error_message(), PageResult, Exception, 将 HTTP/SQLAlchemy 等异常转为可展示的错误文案(避免 str([]) 空列表)。, ResponseModel
### Community 161 - "backfill_crm_contract_attachment_urls.py"
Cohesion: 0.33
Nodes (10): load_oss_map(), load_runtime_settings(), load_settings_env(), main(), Any, Engine, 返回 (新值, 是否发生了 id→url 替换)。, resolve_token() (+2 more)
### Community 163 - "login_crypto.py"
Cohesion: 0.31
Nodes (9): decrypt_login_payload(), ensure_initialized(), extract_login_credentials(), get_public_pem(), Any, 登录凭据 RSA 解密(与前端 JSEncrypt PKCS#1 v1.5 兼容)。, Base64 密文 -> JSON 对象(须含 username、password、ts 毫秒时间戳)。, 从请求体解析用户名、密码。 返回 (username, password, error_message);error_message 非空时表示失败。 (+1 more)
### Community 170 - "test_quote_approver_setting.py"
Cohesion: 0.18
Nodes (22): _assert_users_exist(), get_setting(), Any, Session, _row_to_dict(), save_setting(), _assert_users_exist(), get_setting() (+14 more)
### Community 171 - "BaseStorage"
Cohesion: 0.13
Nodes (4): BaseStorage, OSSStorage, 带下载文件名的访问 URL;默认实现为普通访问地址。, 浏览器内联预览 URL;默认实现为普通访问地址。
### Community 173 - "resolve_create_time_bounds"
Cohesion: 0.21
Nodes (13): format_datetime(), get_current_time(), parse_datetime(), datetime, datetime_end_of_day(), fmt_date(), fmt_datetime(), inventory_date_to_api() (+5 more)
### Community 174 - "datetime_parse.py"
Cohesion: 0.67
Nodes (3): parse_optional_plan_datetime(), datetime, 运输计划相关日期时间解析:前端 date-picker 仅传 YYYY-MM-DD。
### Community 178 - "_build_user_info_payload"
Cohesion: 0.49
Nodes (10): apply_pl_scope(), Session, build_menu_badge_counts(), count_document_reminder_expired_pending(), count_freight_approval_pending(), count_freight_disburse_pending(), _count_pending_applies(), count_plan_archive_pending() (+2 more)
### Community 179 - "_ensure_subpack_slip_no"
Cohesion: 0.06
Nodes (70): archive_plan(), _build_driver_url(), cancel_plan(), cancel_plan_wait_notice(), count_plans_by_contract(), create_from_contract(), create_plan(), _determine_plan_type() (+62 more)
### Community 180 - "menu.py"
Cohesion: 0.36
Nodes (13): _build_tree(), _cascade_soft_delete(), create_menu(), get_menu(), list_menus(), list_menus_tree(), menu_name_exists(), Any (+5 more)
### Community 181 - "create_driver"
Cohesion: 0.19
Nodes (23): approve_carrier(), create_driver(), create_personnel(), delete_driver(), delete_personnel(), get_driver(), get_personnel(), list_drivers() (+15 more)
### Community 206 - "crm_quote_pdf.py"
Cohesion: 0.36
Nodes (10): build_quote_html(), _default_value(), _format_date(), _format_discount(), _format_money(), generate_quote_pdf(), _get_pdf_styles(), Any (+2 more)
### Community 207 - "shanxia2.0 后端骨架(FastAPI)"
Cohesion: 0.18
Nodes (10): 1. 创建虚拟环境, 2. 安装依赖, 3. 配置环境变量, 4. 启动服务, API 路由分组, shanxia2.0 后端骨架(FastAPI), 开发规范, 快速开始 (+2 more)
### Community 220 - "api_crypto_middleware.py"
Cohesion: 0.15
Nodes (21): _claims_from_auth(), _coerce_post_method_override_to_get(), _get_redis_client(), _is_crypto_api(), _json_error_response(), _mark_nonce_once(), _nonce_key(), Redis (+13 more)
### Community 237 - "order_status_compat.py"
Cohesion: 0.36
Nodes (7): derive_legacy_status_from_three(), derive_three_status_from_legacy(), Decimal, 阶段A兼容:从旧 status 推导三字段。 约束:旧 status 本身混合了支付/配送/订单生命周期,因此只能尽力推断; 若业务侧已有更精确, 阶段A兼容:从三字段反推旧 status(用于旧前端/旧筛选参数兼容)。, ThreeStatus, _to_decimal()
### Community 239 - "shanxia2.0 Backend 生产部署说明"
Cohesion: 0.25
Nodes (7): 1. 上传到服务器, 2. 服务器首次启动, 3. 日常运维, 4. 配置文件, 5. 与 Nginx 配合, 6. 常见问题, shanxia2.0 Backend 生产部署说明
### Community 246 - "db_collation.py"
Cohesion: 0.38
Nodes (6): eq_unicode(), like_unicode(), MySQL 字符串比较/模糊匹配时统一排序规则,避免 Illegal mix of collations。, 跨表字符串等值比较(JOIN / WHERE)时统一为 utf8mb4_unicode_ci。, 将列显式设为 utf8mb4_unicode_ci。, unicode_ci()
## Knowledge Gaps
- **30 isolated node(s):** `Config`, `Config`, `Config`, `Config`, `Config` (+25 more)
These have ≤1 connection - possible missing edges or undocumented components.
- **20 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes.
## Suggested Questions
_Questions this graph is uniquely positioned to answer:_
- **Why does `generate_uuid()` connect `Community 68` to `plan.py`, `datetime_parse.py`, `Community 2`, `Community 4`, `Community 6`, `Community 134`, `Community 8`, `Community 136`, `post.py`, `Community 11`, `Community 7`, `Community 13`, `Community 14`, `SysEthnic`, `Community 15`, `SysApiRequestLog`, `__init__.py`, `Community 19`, `Community 18`, `Community 21`, `Community 16`, `Community 26`, `Community 27`, `get_client_ip`, `Community 28`, `Community 29`, `Community 33`, `Community 38`, `Community 39`, `test_quote_approver_setting.py`, `Community 43`, `Community 45`, `resolve_create_time_bounds`, `Community 46`, `Community 49`, `_ensure_subpack_slip_no`, `menu.py`, `create_driver`, `Community 53`, `Community 51`, `Community 61`, `Community 63`, `Community 65`, `Community 66`, `Community 70`, `Community 74`, `Community 83`, `Community 86`, `Community 87`, `pl_ocr_service.py`, `Community 92`, `Community 95`, `_ensure_subpack_slip_no`, `dept.py`, `list_customers`, `apply_pl_scope`, `storage.py`, `quote_share_log.py`, `db_models.py`, `site.py`, `Community 116`, `Community 118`, `Community 122`, `Community 125`?**
_High betweenness centrality (0.154) - this node is a cross-community bridge._
- **Why does `Settings` connect `Community 5` to `backfill_crm_contract_attachment_urls.py`, `Community 35`, `crm_supplier_hazard.py`, `price_letter_detail`, `Community 7`, `Community 72`, `Community 73`, `Community 42`, `Community 52`, `Community 30`, `Any`, `Community 59`, `Community 62`, `Community 95`?**
_High betweenness centrality (0.110) - this node is a cross-community bridge._
- **Why does `BaseModel` connect `Community 71` to `plan.py`, `Community 1`, `BaseStorage`, `Community 3`, `Community 0`, `statistics.py`, `Community 11`, `Community 12`, `Community 14`, `Community 15`, `Community 16`, `Community 18`, `Community 24`, `Community 25`, `web_login_rsa_public_key`, `Community 27`, `Community 28`, `Community 26`, `register_local_callback`, `Community 33`, `Community 36`, `Community 43`, `Community 45`, `Community 48`, `_ensure_subpack_slip_no`, `create_driver`, `Community 54`, `Community 63`, `Community 64`, `Community 65`, `Community 66`, `Community 67`, `Community 68`, `Community 81`, `Community 86`, `pl_vehicle_recharge_service.py`, `_ensure_subpack_slip_no`, `list_customers`, `Community 106`, `Community 109`, `Community 111`, `storage.py`, `Community 119`, `Community 125`, `Community 127`?**
_High betweenness centrality (0.110) - this node is a cross-community bridge._
- **Are the 8 inferred relationships involving `DbUserContext` (e.g. with `InventoryRelatedData` and `AutoGenerateMonthlyResult`) actually correct?**
_`DbUserContext` has 8 INFERRED edges - model-reasoned connections that need verification._
- **What connects `API 路由包 — 山峡化工生产一体化门户`, `返回运营端角色下拉选项(label=库中角色名,value=Web 角色码)。`, `返回用户下拉选项(label=昵称,value=用户ID,附带 nickName/userNo)。 供申请人/审批人等选择场景使用,返回本租户全部有效` to the rest of the system?**
_498 weakly-connected nodes found - possible documentation gaps or missing edges._
- **Should `Community 0` be split into smaller, more focused modules?**
_Cohesion score 0.11406423034330011 - nodes in this community are weakly interconnected._
- **Should `Community 1` be split into smaller, more focused modules?**
_Cohesion score 0.07624113475177305 - nodes in this community are weakly interconnected._
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+177 -198
View File
@@ -1,16 +1,16 @@
# Graph Report - backend (2026-08-02)
# Graph Report - backend (2026-08-03)
## Corpus Check
- 303 files · ~206,910 words
- 304 files · ~207,289 words
- Verdict: corpus is large enough that graph structure adds value.
## Summary
- 4553 nodes · 15476 edges · 533 communities (516 shown, 17 thin omitted)
- 4566 nodes · 15507 edges · 528 communities (512 shown, 16 thin omitted)
- Extraction: 97% EXTRACTED · 3% INFERRED · 0% AMBIGUOUS · INFERRED: 410 edges (avg confidence: 0.53)
- Token cost: 0 input · 0 output
## Graph Freshness
- Built from commit: `c00f19f8`
- Built from commit: `594e4ce3`
- Run `git rev-parse HEAD` and compare to check if the graph is stale.
- Run `graphify update .` after code changes (no API cost).
@@ -134,24 +134,19 @@
- [[_COMMUNITY_ContractListQuery|ContractListQuery]]
- [[_COMMUNITY_Community 151|Community 151]]
- [[_COMMUNITY_MinIOStorage|MinIOStorage]]
- [[_COMMUNITY_web_login_rsa_public_key|web_login_rsa_public_key]]
- [[_COMMUNITY_get_client_ip|get_client_ip]]
- [[_COMMUNITY_context.py|context.py]]
- [[_COMMUNITY_register_local_callback|register_local_callback]]
- [[_COMMUNITY_backfill_crm_contract_attachment_urls.py|backfill_crm_contract_attachment_urls.py]]
- [[_COMMUNITY_monitor.py|monitor.py]]
- [[_COMMUNITY_login_crypto.py|login_crypto.py]]
- [[_COMMUNITY_redis_client.py|redis_client.py]]
- [[_COMMUNITY_codeblock1 (iflytekshanxia2.0backend)|code:block1 (/iflytek/shanxia2.0/backend)]]
- [[_COMMUNITY_test_quote_approver_setting.py|test_quote_approver_setting.py]]
- [[_COMMUNITY_BaseStorage|BaseStorage]]
- [[_COMMUNITY_LocalStorage|LocalStorage]]
- [[_COMMUNITY_resolve_create_time_bounds|resolve_create_time_bounds]]
- [[_COMMUNITY_datetime_parse.py|datetime_parse.py]]
- [[_COMMUNITY__build_user_info_payload|_build_user_info_payload]]
- [[_COMMUNITY__ensure_subpack_slip_no|_ensure_subpack_slip_no]]
- [[_COMMUNITY_menu.py|menu.py]]
- [[_COMMUNITY_web_login_rsa_public_key|web_login_rsa_public_key]]
- [[_COMMUNITY_create_driver|create_driver]]
- [[_COMMUNITY_crm_quote_pdf.py|crm_quote_pdf.py]]
- [[_COMMUNITY_shanxia2.0 后端骨架(FastAPI)|shanxia2.0 后端骨架(FastAPI)]]
- [[_COMMUNITY_api_crypto_middleware.py|api_crypto_middleware.py]]
@@ -199,35 +194,35 @@
## Import Cycles
- None detected.
## Communities (533 total, 17 thin omitted)
## Communities (528 total, 16 thin omitted)
### Community 0 - "Community 0"
Cohesion: 0.11
Nodes (37): approve_settlement_legacy(), cancel_settlement(), create_settlement(), delete_settlement(), disburse_settlement(), _generate_settlement_no(), get_settlement(), get_settlement_approval_flow() (+29 more)
### Community 1 - "Community 1"
Cohesion: 0.18
Nodes (24): CRM 合同子模块路由 — 变更/模板/审批独立页, register_contract_sub_routes(), execute_change(), export_contract_changes(), export_contract_templates(), _get_change_row(), get_contract_change_detail(), list_contract_changes() (+16 more)
Cohesion: 0.09
Nodes (34): CRM 合同 API 路由 — GET/PUT/DELETE/POST 对齐原 Java CRM, 接口菜单权限守卫。super_admin 直接放行。, require_menu_code(), ContractApproverSettingSaveBody, ContractApprovalActionBody, ContractBatchAssignBody, ContractBatchSubmitBody, ContractChangeCreateBody (+26 more)
### Community 2 - "Community 2"
Cohesion: 0.27
Nodes (11): assert_carrier_eligible_for_transport(), carrier_agreement_expired(), carrier_audit_approved(), carrier_eligible_for_transport(), carrier_qualification_complete(), date, 资质附件均为选填,保留函数签名供兼容调用。, _urls_nonempty() (+3 more)
Cohesion: 0.24
Nodes (20): register_quote_routes(), accept_quote(), _ensure_quote_in_scope(), generate_pdf(), generate_share(), get_quote(), get_quote_detail(), _get_quote_in_scope() (+12 more)
### Community 3 - "Community 3"
Cohesion: 0.07
Nodes (87): attachment_download_url(), attachment_preview_url(), AttachmentDownloadUrlBody, create_dept(), create_dict_data(), create_dict_type(), create_menu(), create_post() (+79 more)
Cohesion: 0.08
Nodes (97): attachment_download_url(), attachment_preview_url(), AttachmentDownloadUrlBody, create_dept(), create_dict_data(), create_dict_type(), create_menu(), create_post() (+89 more)
### Community 4 - "Community 4"
Cohesion: 0.12
Nodes (61): _apply_list_filters(), assign_opportunities(), batch_update_expect_date(), batch_update_stage(), change_stage(), create_opportunity(), _decimal_value(), _enrich_user_names() (+53 more)
Cohesion: 0.09
Nodes (76): _apply_list_filters(), assign_opportunities(), batch_update_expect_date(), batch_update_stage(), change_stage(), create_opportunity(), _decimal_value(), _enrich_user_names() (+68 more)
### Community 5 - "Community 5"
Cohesion: 0.20
Nodes (24): count_source_rows(), derive_source_url(), fetch_portal_admin_id(), fetch_source_rows(), legacy_id_to_str(), load_runtime_settings(), load_settings_env(), load_user_mapping() (+16 more)
### Community 6 - "Community 6"
Cohesion: 0.11
Nodes (49): customer_create(), customer_update(), follow_by_customer(), follow_detail(), assign_customer(), batch_claim_customers(), check_duplicate_customer(), claim_customer() (+41 more)
Cohesion: 0.14
Nodes (42): assign_customer(), batch_claim_customers(), check_duplicate_customer(), claim_customer(), create_customer(), deal_customer(), _enrich_user_names(), _ensure_customer_in_scope() (+34 more)
### Community 7 - "Community 7"
Cohesion: 0.12
@@ -235,43 +230,43 @@ Nodes (36): get_password_hash(), Namespace, build_merged_users(), bx_role_labels
### Community 8 - "Community 8"
Cohesion: 0.11
Nodes (47): _build_quote_filters(), calculate_quote_amount(), create_new_version(), create_quote(), _decimal_value(), _enrich_contact_fields(), _enrich_supplier_name(), _fill_contract_linked_count() (+39 more)
Nodes (46): _build_quote_filters(), calculate_quote_amount(), create_new_version(), create_quote(), _decimal_value(), _enrich_contact_fields(), _enrich_supplier_name(), _fill_contract_linked_count() (+38 more)
### Community 11 - "Community 11"
Cohesion: 0.10
Nodes (55): cancel_apply(), create_apply(), create_disburse(), delete_applies(), delete_disburses(), get_apply(), get_disburse(), list_applies() (+47 more)
Cohesion: 0.15
Nodes (40): PlVehicleUreaApply, PlVehicleUreaDisburse, 车辆尿素申请 pl_vehicle_urea_apply, 车辆尿素发放登记 pl_vehicle_urea_disburse, Decimal, VehicleUreaApplyCreate, VehicleUreaApplyRead, VehicleUreaApplyUpdate (+32 more)
### Community 12 - "Community 12"
Cohesion: 0.07
Nodes (43): AttachmentUploadResponse, ChangePasswordRequest, Config, LoginRequest, LoginResponse, Pydantic schemas - 用户认证, ResetPasswordRequest, UserCreate (+35 more)
Cohesion: 0.04
Nodes (89): contact_add(), contact_delete(), contact_update(), customer_create(), customer_delete(), customer_update(), pool_customer_assign(), pool_customer_claim() (+81 more)
### Community 13 - "Community 13"
Cohesion: 0.11
Nodes (37): create_instance(), create_process_def(), create_task(), get_all_tasks_for_business(), get_instance(), get_latest_version(), get_nodes_by_version(), get_pending_tasks_by_node() (+29 more)
Cohesion: 0.16
Nodes (31): create_instance(), create_process_def(), create_task(), get_all_tasks_for_business(), get_instance(), get_latest_version(), get_nodes_by_version(), get_pending_tasks_by_node() (+23 more)
### Community 14 - "Community 14"
Cohesion: 0.10
Nodes (48): check_document_expiry(), document_reminder_stats(), get_inventory_ledger(), get_summary(), list_document_reminders(), list_transactions(), list_warnings(), date (+40 more)
Cohesion: 0.16
Nodes (32): check_document_expiry(), document_reminder_stats(), get_inventory_ledger(), get_summary(), list_document_reminders(), list_transactions(), list_warnings(), date (+24 more)
### Community 15 - "Community 15"
Cohesion: 0.12
Nodes (14): BxReimburseApply, BxReimburseApproval, BxReimburseDetail, ReimburseApplyOut, ReimburseInvoiceBriefOut, Any, Session, 归一化勾选链接;返回 None 表示未传(不改关联),[] 表示清空。 (+6 more)
Cohesion: 0.11
Nodes (16): BxReimburseApply, BxReimburseApproval, BxReimburseDetail, BxReimburseInvoice, BxReimburseOffset, 报销单勾选的发票附件(同一发票可勾选多个附件,各占一条)。, ReimburseApplyOut, ReimburseInvoiceBriefOut (+8 more)
### Community 16 - "Community 16"
Cohesion: 0.09
Nodes (64): cancel_apply(), create_apply(), create_record(), delete_applies(), delete_records(), get_apply(), get_record(), list_applies() (+56 more)
Nodes (60): cancel_apply(), create_apply(), create_record(), delete_applies(), delete_records(), get_apply(), get_record(), list_applies() (+52 more)
### Community 18 - "Community 18"
Cohesion: 0.09
Nodes (37): advance_pending_approval_stats(), advance_status_counts(), approve_advance(), approved_by_me(), create_advance(), delete_advance(), _fill_offset_amounts(), get_advance() (+29 more)
Nodes (38): advance_pending_approval_stats(), advance_status_counts(), approve_advance(), approved_by_me(), create_advance(), delete_advance(), _fill_offset_amounts(), get_advance() (+30 more)
### Community 19 - "Community 19"
Cohesion: 0.23
Nodes (12): change_current_user_password(), get_current_user_profile(), public_quote_share(), Request, Session, 获取当前用户信息(与 sys_user 字段对应)。, 公开分享查看报价单 — Java: GET /share/{shareCode}(无 JWT), get_current_user() (+4 more)
Cohesion: 0.09
Nodes (50): _avatar_url_field(), _build_user_info_payload(), change_current_user_password(), get_current_user_profile(), get_web_auth_menu_codes(), _jwt_user_payload(), logout(), public_quote_share() (+42 more)
### Community 21 - "Community 21"
Cohesion: 0.09
Nodes (56): _alias_row_to_dict(), _assert_brand_deletable(), barcode_exists(), _brand_row_to_dict(), _build_category_tree(), category_code_exists(), _category_row_to_dict(), _collect_category_with_ancestors() (+48 more)
Cohesion: 0.06
Nodes (86): create_price_letter(), get_price_letter(), list_price_letters(), _parse_date(), Any, date, Session, _row_to_dict() (+78 more)
### Community 22 - "Community 22"
Cohesion: 0.08
@@ -282,108 +277,108 @@ Cohesion: 0.11
Nodes (8): convert_heic_to_jpeg(), OSSStorage, process_image_content(), QiniuStorage, 文件存储工具模块 支持多种存储方式: 1. 本地存储 (local) - 默认 2. 七牛云存储 (qiniu) - 推荐生产环境使用 3. 阿里云OSS (, 将 HEIC/HEIF 格式转换为 JPEG 格式, 处理图片内容,如果是 HEIC 格式则转换为 JPEG, BytesIO
### Community 24 - "Community 24"
Cohesion: 0.12
Nodes (18): 地磅 API 路由聚合 — REST 风格,协议对齐原 simple-dibang-xiaoshou。, menu_badge_stats(), Session, Session, recognize_cert(), RecognizeCertBody, RecognizeCertResult, _auto_fill_create_dept_id() (+10 more)
Cohesion: 0.11
Nodes (14): API 路由包 — 山峡化工生产一体化门户, get_menu_list(), 运营端菜单接口(与 web-admin 前端静态路由 sqxp.ts 对齐,14 个一级菜单), _auto_fill_create_dept_id(), get_db(), ORM before_insert 钩子:当 create_dept_id 为空时,自动从请求上下文填充当前用户部门 ID, get_db_current_user_optional(), Session (+6 more)
### Community 25 - "Community 25"
Cohesion: 0.07
Nodes (80): _build_public_page(), _format_file_size(), _format_plan_date(), get_public_transport(), _get_transport_by_token(), _parse_feedback_images(), Session, UploadFile (+72 more)
Cohesion: 0.15
Nodes (24): Session, recognize_cert(), RecognizeCertBody, RecognizeCertResult, cancel_apply(), create_apply(), create_disburse(), delete_applies() (+16 more)
### Community 26 - "Community 26"
Cohesion: 0.28
Nodes (17): _address_to_read(), _build_address_query(), create_transport_address(), delete_transport_address(), _get_address(), get_transport_address(), list_all_transport_addresses(), list_transport_addresses() (+9 more)
### Community 27 - "Community 27"
Cohesion: 0.14
Nodes (35): _apply_plan_product_update(), _apply_plan_update(), AutoGenerateMonthlyResult, _base_query(), create_plan(), delete_plan(), _generate_plan_no(), get_plan() (+27 more)
Cohesion: 0.18
Nodes (29): _apply_plan_product_update(), _apply_plan_update(), AutoGenerateMonthlyResult, _base_query(), create_plan(), delete_plan(), _generate_plan_no(), get_plan() (+21 more)
### Community 28 - "Community 28"
Cohesion: 0.12
Nodes (53): PlFreightSettlement, PlFreightSettlementApproval, 运费结算表 pl_freight_settlement, 运费结算审批 pl_freight_settlement_approval, _advance_after_approval(), _approval_to_dict(), can_user_approve_settlement(), cancel_draft_settlement() (+45 more)
Cohesion: 0.08
Nodes (69): menu_badge_stats(), Session, PlFreightSettlement, PlFreightSettlementApproval, PlRemittanceVoucher, 运费结算表 pl_freight_settlement, 运费结算审批 pl_freight_settlement_approval, 汇款凭证表 pl_remittance_voucher (+61 more)
### Community 29 - "Community 29"
Cohesion: 0.09
Nodes (36): _build_public_page(), _build_site_product_read(), _format_file_size(), get_public_site(), _get_site_link_by_token(), _parse_feedback_images(), Session, UploadFile (+28 more)
Cohesion: 0.11
Nodes (33): _build_public_page(), _build_site_product_read(), _format_file_size(), get_public_site(), _get_site_link_by_token(), _parse_feedback_images(), Session, UploadFile (+25 more)
### Community 30 - "Community 30"
Cohesion: 0.16
Nodes (26): clear_target_categories(), compute_level(), count_target_rows(), derive_source_url(), fetch_portal_admin_id(), fetch_source_rows(), legacy_id_to_str(), load_runtime_settings() (+18 more)
### Community 33 - "Community 33"
Cohesion: 0.14
Nodes (20): bind_biz(), delete_attachment(), download_file(), list_by_biz(), _parse_id_list(), preview_attachment(), Session, UploadFile (+12 more)
Cohesion: 0.20
Nodes (15): bind_biz(), delete_attachment(), download_file(), list_by_biz(), _parse_id_list(), preview_attachment(), Session, UploadFile (+7 more)
### Community 34 - "Community 34"
Cohesion: 0.13
Nodes (18): get_current_time(), parse_datetime(), datetime, get_pool_record(), list_pool_records(), Any, Session, _row_to_dict() (+10 more)
Cohesion: 0.24
Nodes (7): camel_to_snake(), CRUDBase, get_model_attr(), HistoryCRUD, Any, Session, ModelType
### Community 35 - "Community 35"
Cohesion: 0.18
Nodes (36): clear_portal_workflow(), _dec(), derive_source_url(), fetch_portal_admin_id(), fetch_source_rows(), legacy_id_to_str(), load_bx_user_mapping(), load_runtime_settings() (+28 more)
### Community 36 - "Community 36"
Cohesion: 0.09
Nodes (42): approve_task(), _assert_wf_instance_in_scope(), _business_type_label(), create_process_def(), delete_process_def(), _enrich_task_list(), get_business_all_tasks(), _get_business_form_fields() (+34 more)
Cohesion: 0.15
Nodes (31): approve_task(), _assert_wf_instance_in_scope(), _business_type_label(), create_process_def(), _enrich_task_list(), get_business_all_tasks(), _get_business_form_fields(), get_designer_data() (+23 more)
### Community 37 - "Community 37"
Cohesion: 0.24
Nodes (18): _actor_ids(), _actor_names(), assert_row_deletable_by_creator(), assert_rows_deletable_by_creator(), can_delete_by_creator(), is_super_admin(), normalize_user_info(), Any (+10 more)
### Community 38 - "Community 38"
Cohesion: 0.14
Nodes (25): load_brand_qtys_by_plan_product_ids(), persist_plan_product_brand_qtys(), Any, Decimal, Session, 按计划产品明细 ID 批量加载归档品牌数量(brandId/brandName/qty)。, 软删旧品牌量行后按归档结果插入;无品牌约定则只软删不插入。, validate_brand_qty_rows() (+17 more)
Cohesion: 0.06
Nodes (57): generate_uuid(), get_current_time(), parse_datetime(), datetime, allocate_next_biz_no(), _format_biz_no(), _prefix_for(), datetime (+49 more)
### Community 39 - "Community 39"
Cohesion: 0.21
Nodes (29): assign_clues(), check_convert(), check_duplicate_clue(), convert_clue(), create_clue(), _enrich_user_names(), _ensure_clue_in_scope(), get_follow_statistics() (+21 more)
Cohesion: 0.22
Nodes (27): assign_clues(), check_convert(), check_duplicate_clue(), convert_clue(), create_clue(), _enrich_user_names(), _ensure_clue_in_scope(), get_clue() (+19 more)
### Community 42 - "Community 42"
Cohesion: 0.19
Nodes (32): _dec(), derive_source_url(), fetch_portal_admin_id(), fetch_source_rows(), legacy_id_to_str(), load_oss_id_to_url(), load_runtime_settings(), load_settings_env() (+24 more)
### Community 43 - "Community 43"
Cohesion: 0.12
Nodes (26): create_invoice(), delete_invoice(), get_mine_wallet_summary(), list_invoice_ledgers(), list_invoice_wallets(), list_invoices(), Request, Session (+18 more)
Cohesion: 0.10
Nodes (32): create_invoice(), delete_invoice(), get_mine_wallet_summary(), list_invoice_ledgers(), list_invoice_wallets(), list_invoices(), Request, Session (+24 more)
### Community 44 - "Community 44"
Cohesion: 0.13
Nodes (16): ApiCryptoMiddleware, _db_pool_keepalive_loop(), _error_json(), lifespan(), Exception, Request, 数据库异常:返回 JSON 并保留 CORS 头,避免浏览器只报跨域错误, 未捕获异常:统一 JSON 500,确保跨域场景下仍有 Access-Control-Allow-Origin (+8 more)
Nodes (17): ApiCryptoMiddleware, ApiRequestLogMiddleware, _db_pool_keepalive_loop(), _error_json(), lifespan(), Exception, Request, 数据库异常:返回 JSON 并保留 CORS 头,避免浏览器只报跨域错误 (+9 more)
### Community 45 - "Community 45"
Cohesion: 0.50
Nodes (4): ApiRequestLogCRUD, Session, SysApiRequestLog, SysApiRequestLogHis
Cohesion: 0.08
Nodes (76): _build_public_page(), _format_file_size(), _format_plan_date(), get_public_transport(), _get_transport_by_token(), _parse_feedback_images(), Session, UploadFile (+68 more)
### Community 46 - "Community 46"
Cohesion: 0.24
Nodes (20): register_quote_routes(), accept_quote(), _ensure_quote_in_scope(), generate_pdf(), generate_share(), get_quote(), get_quote_detail(), _get_quote_in_scope() (+12 more)
Cohesion: 0.10
Nodes (60): _apply_contract_entity_fields(), _build_contract_filters(), _build_customer_address(), _calc_amount_with_tax(), _change_row_to_dict(), contract_no_exists(), _contract_period_key(), create_contract() (+52 more)
### Community 48 - "Community 48"
Cohesion: 0.20
Nodes (28): _apply_inventory_product_update(), _apply_inventory_update(), _base_query(), create_inventory(), create_link_for_plan(), delete_inventory(), _generate_batch_no(), _get_inventory() (+20 more)
Cohesion: 0.21
Nodes (27): _apply_inventory_product_update(), _apply_inventory_update(), _base_query(), create_inventory(), create_link_for_plan(), delete_inventory(), _generate_batch_no(), _get_inventory() (+19 more)
### Community 49 - "Community 49"
Cohesion: 0.18
Nodes (18): apply_crm_create_by_scope(), _apply_list_filters(), create_supplier_info(), list_all_supplier_info(), list_supplier_info(), Any, Session, CRM 供方信息 CRUD — 对照 CrmSupplierInfoServiceImpl.java (+10 more)
Cohesion: 0.21
Nodes (17): apply_crm_create_by_scope(), CRM 数据权限辅助(负责人 owner_user_id + 标准 create_by/create_dept_id)。, _apply_list_filters(), create_supplier_info(), get_supplier_info(), list_all_supplier_info(), list_supplier_info(), Any (+9 more)
### Community 50 - "Community 50"
Cohesion: 0.11
Nodes (33): contact_add(), contact_birthday(), contact_by_customer(), contact_decision(), contact_delete(), contact_detail(), contact_list(), contact_primary() (+25 more)
Cohesion: 0.15
Nodes (9): 微信消息通知服务 支持微信小程序模板消息推送 使用方式: from app.core.wechat_message import WechatMes, 发送订单状态通知 Args: openid: 用户openid order_n, 发送支付成功通知 Args: openid: 用户openid order_n, 发送配送通知 Args: openid: 配送员openid task_no:, 发送配送时效预警 Args: openid: 配送员openid task_n, 发送退款结果通知 Args: openid: 用户openid order_n, 发送催单通知给配送员 Args: deliver_openid: 配送员openid, 发送模板消息 Args: openid: 用户openid template_ (+1 more)
### Community 51 - "Community 51"
Cohesion: 0.16
Nodes (22): PlTransportProductFeedback, 运输单按产品回传 pl_transport_product_feedback, _dict_field(), format_brand_qty_summary(), load_transport_driver_product_feedbacks(), persist_io_item_brand_qtys(), persist_transport_product_feedback(), Any (+14 more)
Cohesion: 0.08
Nodes (25): category_add(), category_delete(), category_tree(), category_update(), product_add(), product_delete(), product_detail(), product_options() (+17 more)
### Community 52 - "Community 52"
Cohesion: 0.18
Nodes (29): _dec(), derive_source_url(), fetch_portal_admin_id(), fetch_source_rows(), legacy_id_to_str(), load_oss_id_to_url(), load_runtime_settings(), load_settings_env() (+21 more)
### Community 53 - "Community 53"
Cohesion: 0.11
Nodes (34): 报销模块认证适配:门户 JWT 用户 → bx 业务上下文。, BxReimburseInvoice, BxReimburseOffset, BxWorkflowNode, 报销(bx)模块数据模型 — 对齐 docs/sql/20260606_bx_tables.sql, filter_current_cycle_approvals(), 审批轮次过滤 — 驳回后重新提交时,进度接口仅展示当前轮次。, 审批进度仅展示当前轮次;完整历史由 get_approval_history 返回。 (+26 more)
Cohesion: 0.10
Nodes (36): 报销模块认证适配:门户 JWT 用户 → bx 业务上下文。, filter_current_cycle_approvals(), 审批轮次过滤 — 驳回后重新提交时,进度接口仅展示当前轮次。, 审批进度仅展示当前轮次;完整历史由 get_approval_history 返回。, 报销模块 Service 基类 — 租户隔离 + UUID + 软删除。, assert_bx_attachment_biz_in_scope(), assert_bx_biz_in_scope(), assert_bx_row_in_scope() (+28 more)
### Community 54 - "Community 54"
Cohesion: 0.19
Nodes (28): _get_by_plan(), get_inbound(), get_inbound_by_plan(), _get_order(), get_outbound(), get_outbound_by_plan(), inbound_site_url(), list_inbound() (+20 more)
Cohesion: 0.26
Nodes (23): _get_by_plan(), get_inbound(), get_inbound_by_plan(), _get_order(), get_outbound(), get_outbound_by_plan(), inbound_site_url(), list_inbound() (+15 more)
### Community 58 - "Community 58"
Cohesion: 0.18
@@ -394,40 +389,40 @@ Cohesion: 0.20
Nodes (28): _dec(), derive_source_url(), fetch_portal_admin_id(), fetch_source_rows(), legacy_id_to_str(), load_oss_id_to_url(), load_runtime_settings(), load_settings_env() (+20 more)
### Community 60 - "Community 60"
Cohesion: 0.13
Nodes (19): clue_export(), clue_import_template(), contact_export(), customer_import_template(), _opp_list_kwargs(), opportunity_export(), opportunity_kanban(), opportunity_list() (+11 more)
Cohesion: 0.16
Nodes (24): CRM 合同子模块路由 — 变更/模板/审批独立页, register_contract_sub_routes(), execute_change(), export_contract_templates(), _get_change_row(), list_contract_templates(), Java Controller TODO — 保持 pending,记录备注, soft_delete_contract_changes() (+16 more)
### Community 61 - "Community 61"
Cohesion: 0.11
Nodes (44): generate_uuid(), PlTransport, PlTransportDbLink, 运输-地磅出入库关联 pl_transport_db_link, _apply_plan_quantity_delta(), _create_db_inventory(), _create_db_plan(), DibangInventoryLinkResult (+36 more)
Nodes (43): PlTransportDbLink, 运输-地磅出入库关联 pl_transport_db_link, _apply_plan_quantity_delta(), _create_db_inventory(), _create_db_plan(), DibangInventoryLinkResult, find_monthly_db_plan(), _generate_batch_no() (+35 more)
### Community 62 - "Community 62"
Cohesion: 0.21
Nodes (22): clear_target_products(), count_target_rows(), _dec(), derive_source_url(), fetch_portal_admin_id(), fetch_source_rows(), legacy_id_to_str(), load_runtime_settings() (+14 more)
### Community 63 - "Community 63"
Cohesion: 0.27
Nodes (6): BxExpenseItem, Any, Session, ExpenseItemService, Session, M
Cohesion: 0.15
Nodes (17): create_expense_item(), delete_expense_item(), get_expense_item(), get_expense_item_tree(), Session, update_expense_item(), BxExpenseItem, _coerce_id() (+9 more)
### Community 64 - "Community 64"
Cohesion: 0.05
Nodes (137): category_add(), category_delete(), category_tree(), category_update(), clue_check_convert(), clue_check_duplicate(), clue_follow_detail(), clue_follow_statistics() (+129 more)
Cohesion: 0.04
Nodes (170): clue_assign(), clue_convert(), clue_create(), clue_delete(), clue_detail(), clue_export(), clue_follow_create(), clue_follow_delete() (+162 more)
### Community 65 - "Community 65"
Cohesion: 0.11
Nodes (57): cancel_apply(), create_apply(), create_process(), delete_applies(), delete_processes(), get_apply(), get_process(), list_applies() (+49 more)
Cohesion: 0.10
Nodes (58): cancel_apply(), create_apply(), create_process(), delete_applies(), delete_processes(), get_apply(), get_process(), list_applies() (+50 more)
### Community 66 - "Community 66"
Cohesion: 0.19
Nodes (17): create_workflow(), delete_workflow(), get_workflow(), get_workflow_by_biz_type(), list_workflows(), Session, save_workflow_nodes(), update_workflow() (+9 more)
Cohesion: 0.14
Nodes (25): create_workflow(), delete_workflow(), get_workflow(), get_workflow_by_biz_type(), list_workflows(), Session, save_workflow_nodes(), update_workflow() (+17 more)
### Community 67 - "Community 67"
Cohesion: 0.12
Nodes (31): clue_detail(), customer_detail(), invoice_info_add(), invoice_info_by_customer(), _invoice_info_by_customer_impl(), invoice_info_delete(), invoice_info_list(), invoice_info_set_default() (+23 more)
Cohesion: 0.19
Nodes (14): parse_resource_snapshot(), Any, 运输计划 - 安排运输时车辆/人员/证件资源快照 Schema, 从 ORM 文本列或 dict 解析快照。, SnapshotCertRead, SnapshotPersonnelRow, TransportResourceSnapshot, apply_resource_snapshot_json() (+6 more)
### Community 68 - "Community 68"
Cohesion: 0.32
Nodes (14): paste_recognize_commit(), ConflictAction, PasteCommitBody, PasteCommitResult, PastePersonnelIn, PasteVehicleIn, 运输计划 - 三方粘贴识别提交 Schema, commit_paste_recognize() (+6 more)
Cohesion: 0.30
Nodes (15): paste_recognize_commit(), ConflictAction, PasteCommitBody, PasteCommitResult, PastePersonnelIn, PasteVehicleIn, 运输计划 - 三方粘贴识别提交 Schema, commit_paste_recognize() (+7 more)
### Community 69 - "Community 69"
Cohesion: 0.15
@@ -439,7 +434,7 @@ Nodes (10): create_post(), get_post(), list_posts(), post_code_exists(), Any, Se
### Community 71 - "Community 71"
Cohesion: 0.10
Nodes (35): CRM 报价单 API 路由 — 挂接到 crm.router, 接口菜单权限守卫。super_admin 直接放行。, require_menu_code(), total_pages(), QuoteApproverSettingSaveBody, QuoteAcceptBody, QuoteApprovalApproveBody, QuoteApprovalListBody (+27 more)
Nodes (33): CRM 报价单 API 路由 — 挂接到 crm.router, total_pages(), QuoteApproverSettingSaveBody, QuoteAcceptBody, QuoteApprovalApproveBody, QuoteApprovalListBody, QuoteApprovalRefreshNamesBody, QuoteApprovalWithdrawBody (+25 more)
### Community 72 - "Community 72"
Cohesion: 0.19
@@ -450,20 +445,20 @@ Cohesion: 0.20
Nodes (25): derive_source_url(), fetch_portal_admin_id(), fetch_source_rows(), legacy_id_to_str(), load_oss_id_to_url(), load_runtime_settings(), load_settings_env(), load_user_mapping() (+17 more)
### Community 74 - "Community 74"
Cohesion: 0.35
Nodes (11): create_dict_type(), dict_type_code_exists(), get_dict_type(), list_dict_types(), Any, Session, 字典类型 CRUD(sys_dict_type), _row_to_dict() (+3 more)
Cohesion: 0.15
Nodes (17): get_pool_record(), list_pool_records(), Any, Session, _row_to_dict(), total_pages(), CrmCustomerPool, check_customer_protect_time() (+9 more)
### Community 77 - "Community 77"
Cohesion: 0.11
Nodes (12): get_wechat_pay_client(), get_wechat_pay_v2(), Any, 微信支付工具模块 支持微信支付 V3 API 文档参考: https://pay.weixin.qq.com/docs/v3/apis/index.html, 统一下单接口 (V3) Args: out_trade_no: 商户订单号 total_amo, 申请退款 Args: out_trade_no: 商户订单号 total_amount: 订单, 小程序调起支付的二次签名参数(与公众号 JSAPI 规则一致), 统一下单 (V2 API) Args: out_trade_no: 商户订单号 total_f (+4 more)
### Community 78 - "Community 78"
Cohesion: 0.22
Nodes (15): ApiRequestLogMiddleware, _claims_from_auth(), _format_body_bytes(), _persist_log(), Any, Request, 统一记录 API 请求/响应出入参的中间件, _redact_obj() (+7 more)
Cohesion: 0.16
Nodes (18): _claims_from_auth(), _format_body_bytes(), _persist_log(), Any, Request, 统一记录 API 请求/响应出入参的中间件, _redact_obj(), _schedule_persist_log() (+10 more)
### Community 81 - "Community 81"
Cohesion: 0.25
Nodes (14): 获取用户所有角色的数据权限配置。 返回: [{"data_scope": str, "details": [SysRoleDataScopeDetai, _resolve_user_role_scopes(), business_type_from_path(), title_from_path(), _apply_oper_log_scope(), get_oper_log(), list_oper_logs(), _oper_log_row_in_scope() (+6 more)
Cohesion: 0.16
Nodes (22): operlog_detail(), operlog_list(), Session, 操作日志分页列表(基于 API 请求日志适配 RuoYi OperLog 结构), _user_info(), 获取用户所有角色的数据权限配置。 返回: [{"data_scope": str, "details": [SysRoleDataScopeDetai, _resolve_user_role_scopes(), business_type_from_path() (+14 more)
### Community 83 - "Community 83"
Cohesion: 0.15
@@ -475,7 +470,7 @@ Nodes (23): build_id_maps(), clip_text(), count_rows(), export_mapping(), fetch_
### Community 86 - "Community 86"
Cohesion: 0.15
Nodes (35): _apply_weighing_product_update(), _apply_weighing_update(), _base_query(), create_weighing(), delete_weighing(), _get_weighing(), get_weighing_related(), list_weighing() (+27 more)
Nodes (31): _apply_weighing_product_update(), _apply_weighing_update(), _base_query(), create_weighing(), delete_weighing(), _get_weighing(), get_weighing_related(), list_weighing() (+23 more)
### Community 87 - "Community 87"
Cohesion: 0.20
@@ -490,16 +485,16 @@ Cohesion: 0.19
Nodes (22): PlOcrUsageMonth, 阿里云 OCR 月度调用计数 pl_ocr_usage_month(按自然月全局统计), assert_quota_available(), _build_ocr_client(), compress_image_bytes(), current_usage_month(), _first_attachment_url(), get_or_create_usage_row() (+14 more)
### Community 91 - "pl_vehicle_recharge_service.py"
Cohesion: 0.07
Nodes (93): register_contract_routes(), _apply_contract_entity_fields(), batch_assign_owner(), batch_submit_contract_approval(), _build_contract_filters(), _build_customer_address(), _calc_amount_with_tax(), _change_row_to_dict() (+85 more)
Cohesion: 0.13
Nodes (36): register_contract_routes(), batch_assign_owner(), batch_submit_contract_approval(), contract_row_to_dict(), download_contract_pdf(), _ensure_contract_in_scope(), export_contracts(), finish_contract() (+28 more)
### Community 92 - "Community 92"
Cohesion: 0.15
Nodes (27): change_password(), create_user(), get_user(), get_user_with_relations(), _hash_password(), list_post_ids_for_user(), list_user_ids_bound_to_post(), list_users() (+19 more)
Cohesion: 0.14
Nodes (29): 验证密码(哈希非 bcrypt 或损坏时返回 False,避免抛错), verify_password(), change_password(), create_user(), get_user(), get_user_with_relations(), _hash_password(), list_post_ids_for_user() (+21 more)
### Community 93 - "_invoice_info_by_customer_impl"
Cohesion: 0.28
Nodes (11): assert_products_allowed_for_supplier(), enrich_products_with_hazard_flags(), get_supplier_hazard_flags(), product_allowed(), Any, Session, 批量回填明细行 isToxic/isExplosive(来自产品主数据,供前端切换主体剔除)。, test_dual_requires_both() (+3 more)
Cohesion: 0.25
Nodes (17): ensure_crm_create_by_row_in_scope(), create_pool_rule(), delete_pool_rules(), execute_pool_rules(), _execute_single_rule(), get_pool_rule(), list_pool_rules(), Any (+9 more)
### Community 94 - "response.py"
Cohesion: 0.22
@@ -510,44 +505,44 @@ Cohesion: 0.35
Nodes (11): create_tenant(), get_tenant(), list_tenants(), Any, Session, _row_to_dict(), soft_delete_tenant(), tenant_no_exists() (+3 more)
### Community 97 - "_ensure_subpack_slip_no"
Cohesion: 0.06
Nodes (91): approve_carrier(), bind_vehicle_driver(), create_carrier(), create_driver(), create_personnel(), create_vehicle(), delete_carrier(), delete_driver() (+83 more)
Cohesion: 0.11
Nodes (24): create_carrier(), get_carrier(), update_carrier(), carrier_to_read(), CarrierBase, CarrierCreate, CarrierQualificationBase, CarrierRead (+16 more)
### Community 100 - "price_letter_detail"
Cohesion: 0.27
Nodes (15): Settings, BaseSettings, apply_renumber(), build_mapping(), fetch_products(), format_new_product_no(), load_settings_env(), main() (+7 more)
### Community 101 - ".to_tenant_user_dict"
Cohesion: 0.27
Nodes (8): CRM 模块 Excel 导出列定义(字段与字典类型对齐 Java *Vo @ExcelDictFormat)。, _build_workbook(), DictLabelResolver, ExportColumn, Any, Excel 导出工具:集中处理字典 code → label(对齐 Java ExcelDictConvert / ExcelDictPreloadConver, 导出列定义,dict_type 非空时自动将字典 value 转为 label。, 导出单元格解析:按列配置做字典翻译或自定义格式化。
Cohesion: 0.19
Nodes (15): CRM 模块 Excel 导出列定义(字段与字典类型对齐 Java *Vo @ExcelDictFormat)。, _build_workbook(), DictLabelResolver, export_rows_to_xlsx_response(), ExportColumn, load_dict_label_maps(), Any, Session (+7 more)
### Community 102 - "dept.py"
Cohesion: 0.33
Nodes (13): _build_tree(), _cascade_soft_delete(), _collect_with_ancestors(), create_dept(), get_dept(), list_depts(), list_depts_tree(), Any (+5 more)
Cohesion: 0.11
Nodes (34): _build_tree(), _cascade_soft_delete(), _collect_with_ancestors(), create_dept(), get_dept(), list_depts(), list_depts_tree(), Any (+26 more)
### Community 104 - "apply_crm_create_by_scope"
Cohesion: 0.24
Nodes (7): camel_to_snake(), CRUDBase, get_model_attr(), HistoryCRUD, Any, Session, ModelType
Cohesion: 0.15
Nodes (16): 数据模型(与 docs/sql/shanxia2.0.sql 全库导出基线对齐的核心系统表), Web 管理端约定角色(由 sys_role.role_name 映射而来,非数据库枚举), 流程实例 wf_process_instance, UserRole, WfProcessInstance, _dispatch_local(), Any, 工作流回调服务 —— 流程状态变更时通知业务系统 (+8 more)
### Community 105 - "list_customers"
Cohesion: 0.14
Nodes (29): _jwt_user_payload(), logout(), 公共接口(Web 登录、刷新 Token、登出;用户模型对齐 docs/sql/shanxia2.0.sql 基线), 登出:吊销 Redis 会话(若已配置 REDIS_HOST)。, Web 端登录:账号为 sys_user.user_no,密码为 sys_user.password(bcrypt)。, refresh_token(), web_login(), create_access_token() (+21 more)
Cohesion: 0.18
Nodes (20): list_personnel(), list_vehicles(), _personnel_to_read(), _vehicle_to_read(), PlCert, CertRead, certs_to_read(), list_owner_certs() (+12 more)
### Community 106 - "Community 106"
Cohesion: 0.36
Nodes (15): create_opportunity_follow(), _ensure_parent_opportunity(), get_opportunity_follow(), list_follows_by_opportunity(), list_opportunity_follows(), _parse_optional_datetime(), Any, datetime (+7 more)
Cohesion: 0.23
Nodes (16): create_vehicle(), get_vehicle(), update_vehicle(), _vehicle_to_detail(), VehicleDetailRead, VehiclePersonnelRow, build_personnel_summary(), load_vehicle_personnel_rows() (+8 more)
### Community 107 - "apply_pl_scope"
Cohesion: 0.30
Nodes (14): create_clue_follow(), delete_clue_follow(), get_clue_follow(), list_clue_follows(), Any, Session, 更新线索跟进记录,必要时同步线索 next_time, 软删除跟进记录,并回写线索 follow_count / last_time (+6 more)
Cohesion: 0.29
Nodes (16): create_contact(), get_contact(), list_contacts_by_customer(), _parse_optional_date(), Any, date, Session, _row_to_dict() (+8 more)
### Community 109 - "Community 109"
Cohesion: 0.12
Nodes (45): create_expense_item(), delete_expense_item(), get_expense_item(), get_expense_item_tree(), Session, update_expense_item(), bx_health(), 报销(bx)API 路由聚合 — REST 风格,协议对齐 bx 原系统。 (+37 more)
Cohesion: 0.17
Nodes (30): bx_health(), 报销(bx)API 路由聚合 — REST 风格,协议对齐 bx 原系统。, P0 健康检查:验证 bx 路由已挂载且响应协议为 code=0。, approve_reimburse(), approved_by_me(), _build_details_data(), create_reimburse(), delete_reimburse() (+22 more)
### Community 110 - "Base"
Cohesion: 0.16
Nodes (19): get_web_auth_menu_codes(), 获取当前用户菜单权限标识(sys_menu.perms,经角色继承)。, get_user_menu_codes(), Session, 读取用户授权的菜单权限标识集合(与前端 meta.menuCode / perms 对齐)。, _expand_with_ancestor_menu_ids(), _granted_menu_ids_for_user(), iter_user_menu_perm_codes() (+11 more)
Cohesion: 0.19
Nodes (15): get_user_menu_codes(), Session, 读取用户授权的菜单权限标识集合(与前端 meta.menuCode / perms 对齐)。, _expand_with_ancestor_menu_ids(), _granted_menu_ids_for_user(), iter_user_menu_perm_codes(), _map_role_name_to_web(), Session (+7 more)
### Community 111 - "Community 111"
Cohesion: 0.28
@@ -566,144 +561,128 @@ Cohesion: 0.24
Nodes (12): DbSequence, 地磅(db)模块数据模型 — 对齐 docs/sql/db_tables.sql, _allocate_inbound_outbound_id(), _ensure_sequence_row(), max_numeric_inbound_outbound_id(), next_inbound_outbound_id(), Session, 出入库单 id 生成 — 租户内纯数字自增,并发安全。 (+4 more)
### Community 115 - "site.py"
Cohesion: 0.39
Nodes (10): create_site(), get_site(), list_sites(), Any, Session, _row_to_dict(), site_code_exists(), soft_delete_site() (+2 more)
Cohesion: 0.12
Nodes (15): pool_rule_add(), pool_rule_delete(), pool_rule_detail(), pool_rule_execute(), pool_rule_list(), pool_rule_update(), pool_rule_update_status(), PoolRuleAddBody (+7 more)
### Community 116 - "Community 116"
Cohesion: 0.19
Nodes (25): calculate_subtotal(), _decimal_value(), delete_products_by_quote_id(), _ensure_quote_parent_scope(), _get_field(), get_quote_product(), insert_quote_product(), list_quote_products() (+17 more)
### Community 118 - "Community 118"
Cohesion: 0.22
Nodes (22): batch_insert_contract_products(), calculate_subtotal(), _decimal_value(), delete_price_tiers_by_contract_id(), delete_products_by_contract_id(), _get_field(), insert_contract_product(), _letter_oss_ids_to_json() (+14 more)
Cohesion: 0.12
Nodes (34): batch_insert_contract_products(), calculate_subtotal(), _decimal_value(), delete_price_tiers_by_contract_id(), delete_products_by_contract_id(), _get_field(), insert_contract_product(), _letter_oss_ids_to_json() (+26 more)
### Community 119 - "Community 119"
Cohesion: 0.16
Nodes (29): CertRow, VehiclePersonnelRow, _cert_row_complete(), collect_required_cert_errors(), _find_cert_for_required(), get_required_personnel_cert_names(), match_required_cert_name(), _missing_field_labels() (+21 more)
Nodes (28): CertRow, _cert_row_complete(), collect_required_cert_errors(), _find_cert_for_required(), get_required_personnel_cert_names(), match_required_cert_name(), _missing_field_labels(), Session (+20 more)
### Community 122 - "Community 122"
Cohesion: 0.05
Nodes (71): clue_assign(), clue_convert(), clue_create(), clue_delete(), clue_follow_create(), clue_follow_delete(), clue_follow_list(), clue_follow_update() (+63 more)
Cohesion: 0.33
Nodes (15): ensure_crm_customer_linked_row_in_scope(), ensure_crm_owner_row_in_scope(), Session, 客户子表(开票信息等):先校验关联客户 owner scope,否则校验 create_by。, _clear_default_for_customer(), create_invoice_info(), get_invoice_info(), list_invoice_info_by_customer() (+7 more)
### Community 125 - "Community 125"
Cohesion: 0.05
Nodes (86): _build_driver_url(), get_plan_arrange_info(), 获取计划已安排运输后的司机链接与地磅出入库关联(供查看弹窗复制简述/二维码)。, _build_page(), _get_by_token(), _make_handlers(), _models(), Session (+78 more)
Nodes (85): _build_driver_url(), get_plan_arrange_info(), 获取计划已安排运输后的司机链接与地磅出入库关联(供查看弹窗复制简述/二维码)。, _build_page(), _get_by_token(), _make_handlers(), _models(), Session (+77 more)
### Community 126 - "customer_pool.py"
Cohesion: 0.45
Nodes (11): _customer_base_query(), get_follow_statistics(), get_industry_statistics(), get_level_statistics(), get_overview_statistics(), get_owner_statistics(), get_source_statistics(), get_trend_statistics() (+3 more)
### Community 127 - "Community 127"
Cohesion: 0.26
Nodes (18): _cargo_stats(), get_dashboard_stats(), get_inbound_by_cargo(), get_inbound_by_partner_cargo(), get_outbound_by_cargo(), get_outbound_by_partner_cargo(), _partner_cargo_stats(), Session (+10 more)
Cohesion: 0.21
Nodes (19): _cargo_stats(), get_dashboard_stats(), get_inbound_by_cargo(), get_inbound_by_partner_cargo(), get_outbound_by_cargo(), get_outbound_by_partner_cargo(), _partner_cargo_stats(), Session (+11 more)
### Community 128 - "plan.py"
Cohesion: 0.13
Nodes (49): PlPlan, PlPlanProduct, 计划产品明细表 pl_plan_product, PlanArchiveFreightBlock, PlanArchiveProductItem, PlanProductCreate, PlanTransportCreateBlock, all_hazard_products_cert_complete() (+41 more)
Nodes (51): _generate_notify_message(), update_plan(), PlPlan, PlPlanProduct, 计划产品明细表 pl_plan_product, PlanArchiveFreightBlock, PlanArchiveProductItem, PlanTransportCreateBlock (+43 more)
### Community 129 - "BaseStorage"
Cohesion: 0.31
Nodes (14): create_pool_rule(), delete_pool_rules(), execute_pool_rules(), _execute_single_rule(), get_pool_rule(), list_pool_rules(), Any, Session (+6 more)
Cohesion: 0.29
Nodes (14): create_clue_follow(), delete_clue_follow(), get_clue_follow(), get_follow_statistics(), list_clue_follows(), Any, Session, 更新线索跟进记录,必要时同步线索 next_time (+6 more)
### Community 130 - "datetime_parse.py"
Cohesion: 0.47
Nodes (8): create_oss(), get_oss_by_id(), get_oss_by_url(), Session, 按主键 id 查询附件(兼容旧 CRM 雪花 oss_id 迁入后的记录), 将 fileUrl 或旧 CRM 数字 ossId 解析为 MinIO 相对路径。 门户新上传把相对路径写入业务字段;迁入合同附件仍存数字 ossId, resolve_storage_path(), SysOss
Cohesion: 0.31
Nodes (3): Any, Session, WorkflowEngine
### Community 131 - "LocalStorage"
Cohesion: 0.47
Nodes (5): list_ethnics(), Any, Session, 民族码表 CRUD(全局 sys_ethnic), _row_to_dict()
Cohesion: 0.36
Nodes (7): list_ethnics(), Any, Session, 民族码表 CRUD(全局 sys_ethnic), _row_to_dict(), 民族码表 sys_ethnic(国标 GB/T 3304,全局), SysEthnic
### Community 132 - "crm_supplier_hazard.py"
Cohesion: 0.29
Nodes (12): build_updates(), derive_source_url(), fetch_source_customers(), legacy_id_to_str(), load_oss_map(), load_runtime_settings(), load_settings_env(), main() (+4 more)
Cohesion: 0.14
Nodes (23): build_updates(), derive_source_url(), fetch_source_customers(), legacy_id_to_str(), load_oss_map(), load_runtime_settings(), load_settings_env(), main() (+15 more)
### Community 134 - "Community 134"
Cohesion: 0.25
Nodes (18): batch_generate_sample_codes(), batch_issue_sample_codes(), batch_revoke_sample_codes(), create_sample_code(), get_sample_code(), list_sample_codes(), _parse_issued_time(), Any (+10 more)
### Community 135 - "post.py"
Cohesion: 0.24
Nodes (12): get_last_month_range(), month_range(), date, Session, 地磅计划按月自动生成 — 迁移自 simple-dibang-xiaoshou/auto-create-plan/auto_create_monthly_pla, 按月自动生成采购计划。 - 默认仅在每月 1 日执行(force=True 时跳过该限制,供手动触发验收) - 遍历所有租户,按上月计划模板生, run_auto_generate_monthly_plans(), default_execution_status() (+4 more)
Cohesion: 0.30
Nodes (10): get_last_month_range(), month_range(), date, Session, 地磅计划按月自动生成 — 迁移自 simple-dibang-xiaoshou/auto-create-plan/auto_create_monthly_pla, 按月自动生成采购计划。 - 默认仅在每月 1 日执行(force=True 时跳过该限制,供手动触发验收) - 遍历所有租户,按上月计划模板生, run_auto_generate_monthly_plans(), default_execution_status() (+2 more)
### Community 136 - "Community 136"
Cohesion: 0.19
Nodes (18): create_role(), get_role(), get_role_data_scope(), get_role_menu_ids(), list_roles(), Any, Session, 角色 CRUD(sys_role / sys_role_menu / sys_role_data_scope_detail) (+10 more)
### Community 137 - "statistics.py"
Cohesion: 0.10
Nodes (33): advance_issue_stats(), batch_offset(), export_advance_issues(), issue_advance(), list_advance_issues(), list_advance_offset_records(), list_pending_payments(), list_processed_payments() (+25 more)
Cohesion: 0.09
Nodes (37): advance_issue_stats(), batch_offset(), export_advance_issues(), issue_advance(), list_advance_issues(), list_advance_offset_records(), list_pending_payments(), list_processed_payments() (+29 more)
### Community 138 - "Community 138"
Cohesion: 0.33
Nodes (19): check_python(), create_venv(), ensure_dirs(), ensure_valid_venv(), ensure_venv_and_runtime(), install_dependencies(), is_running(), log_error() (+11 more)
### Community 139 - "Community 139"
Cohesion: 0.17
Nodes (8): 发送订单状态通知 Args: openid: 用户openid order_n, 发送支付成功通知 Args: openid: 用户openid order_n, 发送配送通知 Args: openid: 配送员openid task_no:, 发送配送时效预警 Args: openid: 配送员openid task_n, 发送退款结果通知 Args: openid: 用户openid order_n, 发送催单通知给配送员 Args: deliver_openid: 配送员openid, 发送模板消息 Args: openid: 用户openid template_, WechatMessageService
Cohesion: 0.23
Nodes (13): align_contract_no_to_sign_date(), generate_contract_no(), date, Java: generateContractNo — {供方编码}/X-JT{yyyyMMdd}{3位序号}, 标准编号替换日期段,保留序号;非标准返回 None。, 将标准合同编号日期段对齐签订日;撞号则序号顺延(含软删查重)。 非标准编号原样返回。无签订日则原样返回。, replace_contract_no_date(), test_align_bumps_seq_when_occupied() (+5 more)
### Community 141 - "clue_statistics.py"
Cohesion: 0.24
Nodes (11): extract_minio_object_key(), join_relative_urls(), normalize_oss_storage_url(), MinIO 附件 URL 规范化(数据迁移共用)。 背景 ---- CRM / 地磅 / 报销与 shanxia2.0 共用同一 MinIO(bucket 默, 业务表 OSS ID 串 → 相对 URL 串(用于客户证照、跟进附件等)。, 去掉预签名 query(``?X-Amz-...``)。, 将绝对/相对 URL 规范为 MinIO object key(不含 bucket)。, 迁移 CRM ``sys_oss`` 行:``url`` + ``file_name`` → 门户相对路径。 (+3 more)
Cohesion: 0.17
Nodes (11): price_letter_add(), price_letter_detail(), price_letter_list(), price_letter_update(), price_letter_void(), PriceLetterCreateBody, PriceLetterDetailBody, PriceLetterListBody (+3 more)
### Community 143 - "SysEthnic"
Cohesion: 0.33
Nodes (11): create_leave(), get_leave(), list_leaves(), Any, Session, _row_to_dict(), soft_delete_leave(), update_leave() (+3 more)
Cohesion: 0.29
Nodes (12): create_leave(), get_leave(), list_leaves(), Any, Session, _row_to_dict(), soft_delete_leave(), total_pages() (+4 more)
### Community 144 - "SysApiRequestLog"
Cohesion: 0.60
Nodes (9): apply_site_feedback(), _header(), _item(), test_apply_site_feedback_does_not_reset_first_submitted_at(), test_apply_site_feedback_keeps_in_progress_when_requested(), test_apply_site_feedback_rejects_already_voided(), test_apply_site_feedback_rejects_invalid_status(), test_apply_site_feedback_sets_status_completed() (+1 more)
### Community 145 - "minio_url.py"
Cohesion: 0.49
Nodes (10): clue_statistics_level(), _apply_create_range(), _base_query(), get_clue_level_statistics(), get_clue_overview_statistics(), get_clue_owner_statistics(), get_clue_source_statistics(), get_clue_trend_statistics() (+2 more)
Cohesion: 0.27
Nodes (10): carrier_agreement_expired(), carrier_audit_approved(), carrier_eligible_for_transport(), carrier_qualification_complete(), date, 资质附件均为选填,保留函数签名供兼容调用。, _urls_nonempty(), validate_carrier_qualification_fields() (+2 more)
### Community 146 - "__init__.py"
Cohesion: 0.29
Nodes (15): create_price_letter(), get_price_letter(), list_price_letters(), _parse_date(), Any, date, Session, _row_to_dict() (+7 more)
Cohesion: 0.58
Nodes (9): _apply_create_range(), _base_query(), get_clue_level_statistics(), get_clue_overview_statistics(), get_clue_owner_statistics(), get_clue_source_statistics(), get_clue_trend_statistics(), Any (+1 more)
### Community 147 - "Community 147"
Cohesion: 0.37
Nodes (17): check_env(), check_python(), create_venv(), init_env(), install_dependencies(), log_error(), log_info(), log_success() (+9 more)
### Community 150 - "ContractListQuery"
Cohesion: 0.04
Nodes (71): CRM 合同 API 路由 — GET/PUT/DELETE/POST 对齐原 Java CRM, PageResult, ResponseModel, BaseModel, SoftDeleteModel, PagedQuery, PagedResponse, InvoiceAttachmentOut (+63 more)
Cohesion: 0.27
Nodes (11): customer_dealed(), customer_export(), customer_key(), customer_list(), customer_long_time_no_follow(), customer_mine(), customer_to_follow(), customer_week_new() (+3 more)
### Community 152 - "MinIOStorage"
Cohesion: 0.24
Nodes (5): MinIOStorage, Any, 从 MINIO_BASE_URL 解析公网 endpoint;未配置或与内网相同时复用 self.client。, MinIO 预签名下载 URL,携带原始文件名。, MinIO 预签名内联预览 URL(PDF 等 iframe 场景)。
### Community 154 - "web_login_rsa_public_key"
Cohesion: 0.33
Nodes (8): allocate_next_biz_no(), _format_biz_no(), _prefix_for(), datetime, Session, 业务协议号自动生成:XBCC / JZCC + YYYYMMDD + 6 位序号, 原子分配下一个业务协议号。 须在 create 事务内调用;使用 MySQL LAST_INSERT_ID 保证并发安全。, BizNoKind
### Community 156 - "get_client_ip"
Cohesion: 0.27
Nodes (11): create_dict_data(), get_dict_data(), list_dict_data(), list_dict_data_options(), Any, Session, 字典数据 CRUD(sys_dict_data), 字典下拉选项:租户主数据,忽略数据范围(user_info 保留兼容调用方,不参与过滤)。 (+3 more)
### Community 159 - "context.py"
Cohesion: 0.46
Nodes (7): CrmProductBrand, apply_updates(), collect_candidates(), main(), parse_name(), Path, write_csv()
### Community 160 - "register_local_callback"
Cohesion: 0.11
Nodes (24): API 路由包 — 山峡化工生产一体化门户, create_leave(), delete_leave(), get_leave(), _leave_local_callback(), leave_workflow_callback(), list_leaves(), Session (+16 more)
Cohesion: 0.19
Nodes (15): create_leave(), delete_leave(), get_leave(), _leave_local_callback(), leave_workflow_callback(), list_leaves(), Session, submit_leave() (+7 more)
### Community 161 - "backfill_crm_contract_attachment_urls.py"
Cohesion: 0.33
Nodes (10): load_oss_map(), load_runtime_settings(), load_settings_env(), main(), Any, Engine, 返回 (新值, 是否发生了 id→url 替换)。, resolve_token() (+2 more)
### Community 162 - "monitor.py"
Cohesion: 0.36
Nodes (8): operlog_detail(), operlog_list(), Session, 操作日志分页列表(基于 API 请求日志适配 RuoYi OperLog 结构), _user_info(), OperLogDetailBody, OperLogListBody, 操作日志(兼容 RuoYi OperLog 视图)
### Community 163 - "login_crypto.py"
Cohesion: 0.18
Nodes (11): Config, decrypt_login_payload(), ensure_initialized(), extract_login_credentials(), get_public_pem(), Any, 登录凭据 RSA 解密(与前端 JSEncrypt PKCS#1 v1.5 兼容)。, Base64 密文 -> JSON 对象(须含 username、password、ts 毫秒时间戳)。 (+3 more)
### Community 164 - "redis_client.py"
Cohesion: 0.39
Nodes (8): get_redis_client(), get_sync_redis_client(), Redis, 共享 Redis 客户端(同步:会话鉴权;异步:API 加密 nonce)。, 同步 Redis(auth 会话等,在线程池中调用)。, 异步 Redis(API 加密 nonce 等中间件)。, _redis_common_kwargs(), redis_enabled()
Cohesion: 0.31
Nodes (9): decrypt_login_payload(), ensure_initialized(), extract_login_credentials(), get_public_pem(), Any, 登录凭据 RSA 解密(与前端 JSEncrypt PKCS#1 v1.5 兼容)。, Base64 密文 -> JSON 对象(须含 username、password、ts 毫秒时间戳)。, 从请求体解析用户名、密码。 返回 (username, password, error_message);error_message 非空时表示失败。 (+1 more)
### Community 170 - "test_quote_approver_setting.py"
Cohesion: 0.20
@@ -714,25 +693,25 @@ Cohesion: 0.22
Nodes (3): BaseStorage, 带下载文件名的访问 URL;默认实现为普通访问地址。, 浏览器内联预览 URL;默认实现为普通访问地址。
### Community 173 - "resolve_create_time_bounds"
Cohesion: 0.52
Nodes (6): parse_create_time_day_bounds(), _parse_day_start(), datetime, 报销模块日期范围工具(从 bx-backend 迁移)。, 解析申请时间筛选边界,返回 (含起, 不含止)。, resolve_create_time_bounds()
Cohesion: 0.35
Nodes (10): format_datetime(), datetime_end_of_day(), fmt_date(), fmt_datetime(), inventory_date_to_api(), is_empty_inventory_date(), _parse_site_feedback_images(), date (+2 more)
### Community 174 - "datetime_parse.py"
Cohesion: 0.67
Nodes (3): parse_optional_plan_datetime(), datetime, 运输计划相关日期时间解析:前端 date-picker 仅传 YYYY-MM-DD。
### Community 178 - "_build_user_info_payload"
Cohesion: 0.40
Nodes (5): _avatar_url_field(), _build_user_info_payload(), 与 web-admin 静态路由 meta.roles(admin/super_admin)对齐的单元素数组。, 与 web-admin `userInfo` 字段约定一致。, _web_role_list()
### Community 179 - "_ensure_subpack_slip_no"
Cohesion: 0.07
Nodes (72): archive_plan(), cancel_plan(), cancel_plan_wait_notice(), count_plans_by_contract(), create_from_contract(), create_plan(), create_plan_full(), delete_plan() (+64 more)
Nodes (73): archive_plan(), cancel_plan(), cancel_plan_wait_notice(), count_plans_by_contract(), create_from_contract(), create_plan(), create_plan_full(), delete_plan() (+65 more)
### Community 180 - "menu.py"
Cohesion: 0.36
Nodes (13): _build_tree(), _cascade_soft_delete(), create_menu(), get_menu(), list_menus(), list_menus_tree(), menu_name_exists(), Any (+5 more)
### Community 181 - "create_driver"
Cohesion: 0.16
Nodes (32): approve_carrier(), bind_vehicle_driver(), create_driver(), create_personnel(), delete_carrier(), delete_driver(), delete_personnel(), delete_vehicle() (+24 more)
### Community 206 - "crm_quote_pdf.py"
Cohesion: 0.36
Nodes (10): build_quote_html(), _default_value(), _format_date(), _format_discount(), _format_money(), generate_quote_pdf(), _get_pdf_styles(), Any (+2 more)
@@ -742,8 +721,8 @@ Cohesion: 0.18
Nodes (10): 1. 创建虚拟环境, 2. 安装依赖, 3. 配置环境变量, 4. 启动服务, API 路由分组, shanxia2.0 后端骨架(FastAPI), 开发规范, 快速开始 (+2 more)
### Community 220 - "api_crypto_middleware.py"
Cohesion: 0.24
Nodes (13): _claims_from_auth(), _coerce_post_method_override_to_get(), _get_redis_client(), _is_crypto_api(), _json_error_response(), _mark_nonce_once(), _nonce_key(), Redis (+5 more)
Cohesion: 0.13
Nodes (22): _claims_from_auth(), _coerce_post_method_override_to_get(), _get_redis_client(), _is_crypto_api(), _json_error_response(), _mark_nonce_once(), _nonce_key(), Redis (+14 more)
### Community 237 - "order_status_compat.py"
Cohesion: 0.36
@@ -760,22 +739,22 @@ Nodes (6): eq_unicode(), like_unicode(), MySQL 字符串比较/模糊匹配时
## Knowledge Gaps
- **30 isolated node(s):** `Config`, `Config`, `Config`, `Config`, `Config` (+25 more)
These have ≤1 connection - possible missing edges or undocumented components.
- **17 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes.
- **16 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes.
## Suggested Questions
_Questions this graph is uniquely positioned to answer:_
- **Why does `generate_uuid()` connect `Community 61` to `plan.py`, `BaseStorage`, `datetime_parse.py`, `Community 4`, `Community 6`, `Community 134`, `Community 8`, `Community 136`, `post.py`, `Community 11`, `Community 7`, `Community 13`, `Community 14`, `SysEthnic`, `Community 15`, `Community 16`, `__init__.py`, `Community 18`, `Community 21`, `Community 25`, `web_login_rsa_public_key`, `Community 26`, `Community 27`, `get_client_ip`, `Community 28`, `context.py`, `Community 34`, `Community 38`, `Community 39`, `test_quote_approver_setting.py`, `Community 43`, `Community 49`, `Community 50`, `_ensure_subpack_slip_no`, `menu.py`, `Community 53`, `Community 51`, `Community 63`, `Community 65`, `Community 66`, `Community 67`, `Community 68`, `Community 70`, `Community 74`, `Community 83`, `Community 86`, `Community 87`, `pl_ocr_service.py`, `pl_vehicle_recharge_service.py`, `Community 92`, `Community 95`, `_ensure_subpack_slip_no`, `dept.py`, `list_customers`, `Community 106`, `apply_pl_scope`, `quote_share_log.py`, `db_models.py`, `site.py`, `Community 116`, `Community 118`, `Community 125`?**
_High betweenness centrality (0.123) - this node is a cross-community bridge._
- **Why does `Settings` connect `price_letter_detail` to `backfill_crm_contract_attachment_urls.py`, `login_crypto.py`, `crm_supplier_hazard.py`, `Community 35`, `Community 5`, `Community 7`, `Community 72`, `Community 73`, `Community 42`, `Community 52`, `Community 30`, `Any`, `Community 59`, `Community 62`?**
_High betweenness centrality (0.111) - this node is a cross-community bridge._
- **Why does `BaseModel` connect `ContractListQuery` to `Community 0`, `Community 1`, `plan.py`, `Community 3`, `Community 6`, `statistics.py`, `Community 11`, `Community 12`, `Community 14`, `Community 15`, `Community 16`, `Community 18`, `Community 24`, `Community 25`, `Community 26`, `Community 27`, `register_local_callback`, `Community 33`, `monitor.py`, `Community 36`, `Community 48`, `Community 50`, `_ensure_subpack_slip_no`, `Community 54`, `Community 60`, `Community 64`, `Community 65`, `Community 66`, `Community 67`, `Community 68`, `Community 71`, `Community 86`, `_ensure_subpack_slip_no`, `Community 109`, `Community 111`, `storage.py`, `Community 119`, `Community 122`, `Community 125`, `Community 127`?**
_High betweenness centrality (0.093) - this node is a cross-community bridge._
- **Why does `generate_uuid()` connect `Community 38` to `plan.py`, `BaseStorage`, `Community 4`, `Community 6`, `Community 134`, `Community 8`, `Community 136`, `post.py`, `Community 11`, `Community 7`, `Community 13`, `Community 14`, `SysEthnic`, `Community 15`, `Community 16`, `Community 18`, `Community 19`, `Community 21`, `Community 26`, `Community 27`, `get_client_ip`, `Community 28`, `Community 39`, `test_quote_approver_setting.py`, `Community 43`, `Community 45`, `Community 46`, `Community 49`, `_ensure_subpack_slip_no`, `menu.py`, `create_driver`, `Community 53`, `Community 61`, `Community 63`, `Community 65`, `Community 66`, `Community 68`, `Community 70`, `Community 74`, `Community 83`, `Community 86`, `Community 87`, `pl_ocr_service.py`, `Community 92`, `_invoice_info_by_customer_impl`, `Community 95`, `_ensure_subpack_slip_no`, `dept.py`, `list_customers`, `Community 106`, `apply_pl_scope`, `quote_share_log.py`, `db_models.py`, `Community 116`, `Community 118`, `Community 122`, `Community 125`?**
_High betweenness centrality (0.138) - this node is a cross-community bridge._
- **Why does `Settings` connect `price_letter_detail` to `backfill_crm_contract_attachment_urls.py`, `Community 35`, `crm_supplier_hazard.py`, `Community 5`, `Community 7`, `Community 72`, `Community 73`, `Community 42`, `Community 52`, `Community 30`, `Any`, `Community 59`, `api_crypto_middleware.py`, `Community 62`?**
_High betweenness centrality (0.108) - this node is a cross-community bridge._
- **Why does `BaseModel` connect `Community 12` to `Community 0`, `Community 1`, `plan.py`, `Community 3`, `statistics.py`, `Community 11`, `clue_statistics.py`, `Community 14`, `Community 15`, `Community 16`, `Community 18`, `ContractListQuery`, `Community 24`, `Community 25`, `Community 26`, `Community 27`, `Community 28`, `register_local_callback`, `Community 33`, `Community 36`, `Community 43`, `Community 45`, `Community 48`, `Community 51`, `_ensure_subpack_slip_no`, `create_driver`, `Community 54`, `Community 60`, `Community 63`, `Community 64`, `Community 65`, `Community 66`, `Community 67`, `Community 68`, `Community 71`, `Community 81`, `Community 86`, `_ensure_subpack_slip_no`, `Community 106`, `Community 109`, `Community 111`, `storage.py`, `site.py`, `Community 119`, `Community 125`, `Community 127`?**
_High betweenness centrality (0.092) - this node is a cross-community bridge._
- **Are the 8 inferred relationships involving `DbUserContext` (e.g. with `InventoryRelatedData` and `AutoGenerateMonthlyResult`) actually correct?**
_`DbUserContext` has 8 INFERRED edges - model-reasoned connections that need verification._
- **What connects `API 路由包 — 山峡化工生产一体化门户`, `返回运营端角色下拉选项(label=库中角色名,value=Web 角色码)。`, `返回用户下拉选项(label=昵称,value=用户ID,附带 nickName/userNo)。 供申请人/审批人等选择场景使用,返回本租户全部有效` to the rest of the system?**
_497 weakly-connected nodes found - possible documentation gaps or missing edges._
_500 weakly-connected nodes found - possible documentation gaps or missing edges._
- **Should `Community 0` be split into smaller, more focused modules?**
_Cohesion score 0.1141025641025641 - nodes in this community are weakly interconnected._
- **Should `Community 3` be split into smaller, more focused modules?**
_Cohesion score 0.07314524555903866 - nodes in this community are weakly interconnected._
- **Should `Community 1` be split into smaller, more focused modules?**
_Cohesion score 0.09009009009009009 - nodes in this community are weakly interconnected._
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_crm_contract_no_align_py", "label": "test_crm_contract_no_align.py", "file_type": "code", "source_file": "tests/test_crm_contract_no_align.py", "source_location": "L1"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_crm_contract_no_align_test_replace_keeps_seq_and_swaps_date", "label": "test_replace_keeps_seq_and_swaps_date()", "file_type": "code", "source_file": "tests/test_crm_contract_no_align.py", "source_location": "L7"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_crm_contract_no_align_test_replace_noop_when_same_date", "label": "test_replace_noop_when_same_date()", "file_type": "code", "source_file": "tests/test_crm_contract_no_align.py", "source_location": "L14"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_crm_contract_no_align_test_replace_returns_none_for_nonstandard", "label": "test_replace_returns_none_for_nonstandard()", "file_type": "code", "source_file": "tests/test_crm_contract_no_align.py", "source_location": "L18"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_crm_contract_no_align_test_align_returns_original_when_nonstandard", "label": "test_align_returns_original_when_nonstandard()", "file_type": "code", "source_file": "tests/test_crm_contract_no_align.py", "source_location": "L24"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_crm_contract_no_align_test_align_bumps_seq_when_occupied", "label": "test_align_bumps_seq_when_occupied()", "file_type": "code", "source_file": "tests/test_crm_contract_no_align.py", "source_location": "L34"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_crm_contract_no_align_test_align_exclude_id_skips_self", "label": "test_align_exclude_id_skips_self()", "file_type": "code", "source_file": "tests/test_crm_contract_no_align.py", "source_location": "L47"}], "edges": [{"source": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_crm_contract_no_align_py", "target": "datetime", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/test_crm_contract_no_align.py", "source_location": "L1", "weight": 1.0}, {"source": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_crm_contract_no_align_py", "target": "unittest_mock", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/test_crm_contract_no_align.py", "source_location": "L2", "weight": 1.0}, {"source": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_crm_contract_no_align_py", "target": "app_crud_crm_contract", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/test_crm_contract_no_align.py", "source_location": "L4", "weight": 1.0}, {"source": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_crm_contract_no_align_py", "target": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_crm_contract_no_align_test_replace_keeps_seq_and_swaps_date", "relation": "contains", "confidence": "EXTRACTED", "source_file": "tests/test_crm_contract_no_align.py", "source_location": "L7", "weight": 1.0}, {"source": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_crm_contract_no_align_py", "target": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_crm_contract_no_align_test_replace_noop_when_same_date", "relation": "contains", "confidence": "EXTRACTED", "source_file": "tests/test_crm_contract_no_align.py", "source_location": "L14", "weight": 1.0}, {"source": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_crm_contract_no_align_py", "target": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_crm_contract_no_align_test_replace_returns_none_for_nonstandard", "relation": "contains", "confidence": "EXTRACTED", "source_file": "tests/test_crm_contract_no_align.py", "source_location": "L18", "weight": 1.0}, {"source": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_crm_contract_no_align_py", "target": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_crm_contract_no_align_test_align_returns_original_when_nonstandard", "relation": "contains", "confidence": "EXTRACTED", "source_file": "tests/test_crm_contract_no_align.py", "source_location": "L24", "weight": 1.0}, {"source": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_crm_contract_no_align_py", "target": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_crm_contract_no_align_test_align_bumps_seq_when_occupied", "relation": "contains", "confidence": "EXTRACTED", "source_file": "tests/test_crm_contract_no_align.py", "source_location": "L34", "weight": 1.0}, {"source": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_crm_contract_no_align_py", "target": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_crm_contract_no_align_test_align_exclude_id_skips_self", "relation": "contains", "confidence": "EXTRACTED", "source_file": "tests/test_crm_contraLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_py", "label": "contract.py", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L1"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_ensure_contract_in_scope", "label": "_ensure_contract_in_scope()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L47"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/crud/crm/contract.py"}, {"id": "crmcontract", "label": "CrmContract", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/crud/crm/contract.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_get_contract_row_in_scope", "label": "_get_contract_row_in_scope()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L51"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_total_pages", "label": "total_pages()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L68"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_decimal_value", "label": "_decimal_value()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L74"}, {"id": "any", "label": "Any", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/crud/crm/contract.py"}, {"id": "decimal", "label": "Decimal", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/crud/crm/contract.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_parse_optional_date", "label": "_parse_optional_date()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L82"}, {"id": "date", "label": "date", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/crud/crm/contract.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_parse_optional_datetime", "label": "_parse_optional_datetime()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L100"}, {"id": "datetime", "label": "datetime", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/crud/crm/contract.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_get_field", "label": "_get_field()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L116"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_resolve_user_display_name", "label": "_resolve_user_display_name()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L123"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_row_to_dict", "label": "_row_to_dict()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L133"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_contract_row_to_dict", "label": "contract_row_to_dict()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L210"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_build_contract_filters", "label": "_build_contract_filters()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L214"}, {"id": "query", "label": "Query", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/crud/crm/contract.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_list_contracts", "label": "list_contracts()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L355"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_get_contract", "label": "get_contract()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L389"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_get_contract_detail", "label": "get_contract_detail()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L439"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_get_contract_row", "label": "get_contract_row()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L459"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_get_supplier_short_name", "label": "_get_supplier_short_name(Line truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_py", "label": "contract.py", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L1"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_ensure_contract_in_scope", "label": "_ensure_contract_in_scope()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L48"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/crud/crm/contract.py"}, {"id": "crmcontract", "label": "CrmContract", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/crud/crm/contract.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_get_contract_row_in_scope", "label": "_get_contract_row_in_scope()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L52"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_total_pages", "label": "total_pages()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L69"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_decimal_value", "label": "_decimal_value()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L75"}, {"id": "any", "label": "Any", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/crud/crm/contract.py"}, {"id": "decimal", "label": "Decimal", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/crud/crm/contract.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_parse_optional_date", "label": "_parse_optional_date()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L83"}, {"id": "date", "label": "date", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/crud/crm/contract.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_parse_optional_datetime", "label": "_parse_optional_datetime()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L101"}, {"id": "datetime", "label": "datetime", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/crud/crm/contract.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_get_field", "label": "_get_field()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L117"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_resolve_user_display_name", "label": "_resolve_user_display_name()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L124"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_is_blank", "label": "_is_blank()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L134"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_first_non_blank", "label": "_first_non_blank()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L138"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_get_default_invoice_info", "label": "_get_default_invoice_info()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L146"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_build_customer_address", "label": "_build_customer_address()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L156"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_row_to_dict", "label": "_row_to_dict()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L168"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_contract_row_to_dict", "label": "contract_row_to_dict()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L245"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_build_contract_filters", "label": "_build_contract_filters()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L249"}, {"id": "query", "label": "Query", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/crud/crm/contract.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_list_contracts", "label": "list_contractsLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_py", "label": "contract.py", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L1"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_ensure_contract_in_scope", "label": "_ensure_contract_in_scope()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L47"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/crud/crm/contract.py"}, {"id": "crmcontract", "label": "CrmContract", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/crud/crm/contract.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_get_contract_row_in_scope", "label": "_get_contract_row_in_scope()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L51"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_total_pages", "label": "total_pages()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L68"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_decimal_value", "label": "_decimal_value()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L74"}, {"id": "any", "label": "Any", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/crud/crm/contract.py"}, {"id": "decimal", "label": "Decimal", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/crud/crm/contract.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_parse_optional_date", "label": "_parse_optional_date()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L82"}, {"id": "date", "label": "date", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/crud/crm/contract.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_parse_optional_datetime", "label": "_parse_optional_datetime()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L100"}, {"id": "datetime", "label": "datetime", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/crud/crm/contract.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_get_field", "label": "_get_field()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L116"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_resolve_user_display_name", "label": "_resolve_user_display_name()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L123"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_row_to_dict", "label": "_row_to_dict()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L133"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_contract_row_to_dict", "label": "contract_row_to_dict()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L210"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_build_contract_filters", "label": "_build_contract_filters()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L214"}, {"id": "query", "label": "Query", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/crud/crm/contract.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_list_contracts", "label": "list_contracts()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L355"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_get_contract", "label": "get_contract()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L389"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_get_contract_detail", "label": "get_contract_detail()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L439"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_get_contract_row", "label": "get_contract_row()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L459"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_get_supplier_short_name", "label": "_get_supplier_short_name(Line truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_py", "label": "contract.py", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L1"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_ensure_contract_in_scope", "label": "_ensure_contract_in_scope()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L48"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/crud/crm/contract.py"}, {"id": "crmcontract", "label": "CrmContract", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/crud/crm/contract.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_get_contract_row_in_scope", "label": "_get_contract_row_in_scope()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L52"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_total_pages", "label": "total_pages()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L69"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_decimal_value", "label": "_decimal_value()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L75"}, {"id": "any", "label": "Any", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/crud/crm/contract.py"}, {"id": "decimal", "label": "Decimal", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/crud/crm/contract.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_parse_optional_date", "label": "_parse_optional_date()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L83"}, {"id": "date", "label": "date", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/crud/crm/contract.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_parse_optional_datetime", "label": "_parse_optional_datetime()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L101"}, {"id": "datetime", "label": "datetime", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/crud/crm/contract.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_get_field", "label": "_get_field()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L117"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_resolve_user_display_name", "label": "_resolve_user_display_name()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L124"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_is_blank", "label": "_is_blank()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L134"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_first_non_blank", "label": "_first_non_blank()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L138"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_get_default_invoice_info", "label": "_get_default_invoice_info()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L146"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_build_customer_address", "label": "_build_customer_address()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L156"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_row_to_dict", "label": "_row_to_dict()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L168"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_contract_row_to_dict", "label": "contract_row_to_dict()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L245"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_build_contract_filters", "label": "_build_contract_filters()", "file_type": "code", "source_file": "app/crud/crm/contract.py", "source_location": "L249"}, {"id": "query", "label": "Query", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/crud/crm/contract.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_crud_crm_contract_list_contracts", "label": "list_contractsLine truncated
+1 -1
View File
@@ -1 +1 @@
{"/Users/zlwu/work/00code/shanxia2.0/backend/start-prod.sh":{"size":7357,"mtime_ns":1782639355476060453,"hash":"ca89869446be74a2bf2367b3b28f448580220859a39a84ad9613ac5fe35a8433"},"/Users/zlwu/work/00code/shanxia2.0/backend/start.sh":{"size":9548,"mtime_ns":1780114661446983244,"hash":"4d632863e8a0cd81194b491d83a0218a4157435ae812dec76a6f6197b4d48e57"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/__init__.py":{"size":21,"mtime_ns":1780113982281973984,"hash":"60a04ff08fd97084e83ea809fd13feda6d2848be20ebf309e31fc1dad7523230"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/main.py":{"size":6280,"mtime_ns":1782790852770448643,"hash":"8f9da799977bee7134d87a26ff8434ac5ef8eabb0b7368dd537eee49ad7fd00b"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/tasks/__init__.py":{"size":2207,"mtime_ns":1783208067881286091,"hash":"313e6da0e91d743ce891dd283458c79b50b24c4d909dc96ca7b3fd9d2bfa5013"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/auth.py":{"size":7457,"mtime_ns":1782790841500516545,"hash":"34bac56e1c2cb6215f4b5fa2dc32d77d4b4abca51428819eebf4afd269cc51cf"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/api_crypto.py":{"size":1870,"mtime_ns":1780108930724444498,"hash":"c04b748aea86890a5971f749523c7b17dbad1ddd6f9f7f07a5679d159c984e33"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/login_crypto.py":{"size":3858,"mtime_ns":1780108930725155710,"hash":"b82d51afabf2293a9cb1fb2e5f994c383948e7f47ea95fe674686b4f3f3342b1"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/config.py":{"size":6958,"mtime_ns":1784032424448146538,"hash":"c2fb44aedb880733de28afa7f92d2c1c681eb600f821bbeba2bcda3933e5bc37"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/wechat_pay.py":{"size":23585,"mtime_ns":1780108930725768130,"hash":"3d52265ce65d21e7103f94afa1e8547aee0823b5ea6d65c50317436ac81fb4fc"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/api_request_log_middleware.py":{"size":11971,"mtime_ns":1782701669198208175,"hash":"0b84c32828939e6102bb3647691fa8f04b1f68e0e0e9f28127374cc44e19a11c"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/database.py":{"size":1510,"mtime_ns":1782790847265601586,"hash":"8bc9c8ae62e2be2c6920036582ba448dfcd1711c41cebb62274ff6bc6ee14b0b"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/security.py":{"size":728,"mtime_ns":1780108930725470004,"hash":"df0f01115064f7b8de1c81dbe9a6db39360c236610b68c33b3234a51d7f0025b"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/request_payload.py":{"size":2842,"mtime_ns":1780108930725350836,"hash":"e9937151530ab1417ef297aef59111977725cf90be2a8fa76c00a0618789aba0"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/__init__.py":{"size":61,"mtime_ns":1780108930724312414,"hash":"3dd1842ee59b0c804176b03b4b8a323bd4a252b7feb1bbc32db64f3a8928570e"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/response.py":{"size":1824,"mtime_ns":1780108930725412462,"hash":"f1739352d65ea8287d76e282a59553aa8d63c97e35764c64ac378aa5bd9c347e"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/menu_guard.py":{"size":1611,"mtime_ns":1780108930725223002,"hash":"3e26077288c3620a4780038b891a326206b45bffe00aca545bdae30172487235"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/db_collation.py":{"size":548,"mtime_ns":1780108930725025043,"hash":"0c680e25a18244ca839a7de6ed1f4313b33bc8da118d89c463aee1b6af00c6f9"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/context.py":{"size":421,"mtime_ns":1780108930724824458,"hash":"83a10aee67162374993c10a30bf8bb8bd9c4b35cf48f42c832415ccebd91660f"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/utils.py":{"size":1498,"mtime_ns":1780108930725607296,"hash":"09323693661c1d4560458b86a954128ccff82bbaf6522da0b44f24da99ddf9ea"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/storage.py":{"size":14141,"mtime_ns":1782855136810943216,"hash":"3ae2c413c10898a8adb3d6773870bafbfbc18c739c10e0e8b4f765b4b5ca6c08"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/order_status_compat.py":{"size":2828,"mtime_ns":1780108930725284669,"hash":"5349e5a0745ee6734c4a995337779307adefc46f453906171988a77de2081540"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/exceptions.py":{"size":4946,"mtime_ns":1780108930725084335,"hash":"79c997d964bca1a4f12f2b92fb1f181fdb9e2b3cf72eae39f4cc164d23794fd7"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/data_scope.py":{"size":8043,"mtime_ns":1782648925694296304,"hash":"626dff86937e5cacec634ecbc61cd53d3763d760223a1d4ec7cd11f64e59a220"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/admin_request_json.py":{"size":2669,"mtime_ns":1780108930724378914,"hash":"4f68b3cf84291e7bddad6ecf4bb4d29ca5e1ec246a9f7f9c4cf5909d0ec1174a"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/api_crypto_middleware.py":{"size":10876,"mtime_ns":1782790001926627098,"hash":"b39f128b6612589ea6ac5babd30752e9585b3af07b82bb1ad53c9e3c0d90816f"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/wechat_message.py":{"size":11194,"mtime_ns":1780108930725684255,"hash":"5086070deb14d99d1a69e419b2299ec4Line truncated
{"/Users/zlwu/work/00code/shanxia2.0/backend/start-prod.sh":{"size":7357,"mtime_ns":1782639355476060453,"hash":"ca89869446be74a2bf2367b3b28f448580220859a39a84ad9613ac5fe35a8433"},"/Users/zlwu/work/00code/shanxia2.0/backend/start.sh":{"size":9548,"mtime_ns":1780114661446983244,"hash":"4d632863e8a0cd81194b491d83a0218a4157435ae812dec76a6f6197b4d48e57"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/__init__.py":{"size":21,"mtime_ns":1780113982281973984,"hash":"60a04ff08fd97084e83ea809fd13feda6d2848be20ebf309e31fc1dad7523230"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/main.py":{"size":6280,"mtime_ns":1782790852770448643,"hash":"8f9da799977bee7134d87a26ff8434ac5ef8eabb0b7368dd537eee49ad7fd00b"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/tasks/__init__.py":{"size":2207,"mtime_ns":1783208067881286091,"hash":"313e6da0e91d743ce891dd283458c79b50b24c4d909dc96ca7b3fd9d2bfa5013"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/auth.py":{"size":7457,"mtime_ns":1782790841500516545,"hash":"34bac56e1c2cb6215f4b5fa2dc32d77d4b4abca51428819eebf4afd269cc51cf"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/api_crypto.py":{"size":1870,"mtime_ns":1780108930724444498,"hash":"c04b748aea86890a5971f749523c7b17dbad1ddd6f9f7f07a5679d159c984e33"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/login_crypto.py":{"size":3858,"mtime_ns":1780108930725155710,"hash":"b82d51afabf2293a9cb1fb2e5f994c383948e7f47ea95fe674686b4f3f3342b1"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/config.py":{"size":6958,"mtime_ns":1784032424448146538,"hash":"c2fb44aedb880733de28afa7f92d2c1c681eb600f821bbeba2bcda3933e5bc37"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/wechat_pay.py":{"size":23585,"mtime_ns":1780108930725768130,"hash":"3d52265ce65d21e7103f94afa1e8547aee0823b5ea6d65c50317436ac81fb4fc"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/api_request_log_middleware.py":{"size":11971,"mtime_ns":1782701669198208175,"hash":"0b84c32828939e6102bb3647691fa8f04b1f68e0e0e9f28127374cc44e19a11c"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/database.py":{"size":1510,"mtime_ns":1782790847265601586,"hash":"8bc9c8ae62e2be2c6920036582ba448dfcd1711c41cebb62274ff6bc6ee14b0b"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/security.py":{"size":728,"mtime_ns":1780108930725470004,"hash":"df0f01115064f7b8de1c81dbe9a6db39360c236610b68c33b3234a51d7f0025b"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/request_payload.py":{"size":2842,"mtime_ns":1780108930725350836,"hash":"e9937151530ab1417ef297aef59111977725cf90be2a8fa76c00a0618789aba0"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/__init__.py":{"size":61,"mtime_ns":1780108930724312414,"hash":"3dd1842ee59b0c804176b03b4b8a323bd4a252b7feb1bbc32db64f3a8928570e"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/response.py":{"size":1824,"mtime_ns":1780108930725412462,"hash":"f1739352d65ea8287d76e282a59553aa8d63c97e35764c64ac378aa5bd9c347e"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/menu_guard.py":{"size":1611,"mtime_ns":1780108930725223002,"hash":"3e26077288c3620a4780038b891a326206b45bffe00aca545bdae30172487235"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/db_collation.py":{"size":548,"mtime_ns":1780108930725025043,"hash":"0c680e25a18244ca839a7de6ed1f4313b33bc8da118d89c463aee1b6af00c6f9"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/context.py":{"size":421,"mtime_ns":1780108930724824458,"hash":"83a10aee67162374993c10a30bf8bb8bd9c4b35cf48f42c832415ccebd91660f"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/utils.py":{"size":1498,"mtime_ns":1780108930725607296,"hash":"09323693661c1d4560458b86a954128ccff82bbaf6522da0b44f24da99ddf9ea"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/storage.py":{"size":14141,"mtime_ns":1782855136810943216,"hash":"3ae2c413c10898a8adb3d6773870bafbfbc18c739c10e0e8b4f765b4b5ca6c08"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/order_status_compat.py":{"size":2828,"mtime_ns":1780108930725284669,"hash":"5349e5a0745ee6734c4a995337779307adefc46f453906171988a77de2081540"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/exceptions.py":{"size":4946,"mtime_ns":1780108930725084335,"hash":"79c997d964bca1a4f12f2b92fb1f181fdb9e2b3cf72eae39f4cc164d23794fd7"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/data_scope.py":{"size":8043,"mtime_ns":1782648925694296304,"hash":"626dff86937e5cacec634ecbc61cd53d3763d760223a1d4ec7cd11f64e59a220"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/admin_request_json.py":{"size":2669,"mtime_ns":1780108930724378914,"hash":"4f68b3cf84291e7bddad6ecf4bb4d29ca5e1ec246a9f7f9c4cf5909d0ec1174a"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/api_crypto_middleware.py":{"size":10876,"mtime_ns":1782790001926627098,"hash":"b39f128b6612589ea6ac5babd30752e9585b3af07b82bb1ad53c9e3c0d90816f"},"/Users/zlwu/work/00code/shanxia2.0/backend/app/core/wechat_message.py":{"size":11194,"mtime_ns":1780108930725684255,"hash":"5086070deb14d99d1a69e419b2299ec4Line truncated
+4 -4
View File
@@ -63,12 +63,12 @@
</div>
<div id="legend"></div>
</div>
<div id="stats">4553 nodes &middot; 15476 edges &middot; 533 communities</div>
<div id="stats">4566 nodes &middot; 15507 edges &middot; 528 communities</div>
</div>
<script>
const RAW_NODES = [{"id": "app_init", "label": "__init__.py", "color": {"background": "#59A14F", "border": "#59A14F", "highlight": {"background": "#ffffff", "border": "#59A14F"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "__init__.py", "community": 304, "community_name": "__init__.py", "source_file": "app/__init__.py", "file_type": "code", "degree": 0}, {"id": "app_api_init", "label": "__init__.py", "color": {"background": "#4E79A7", "border": "#4E79A7", "highlight": {"background": "#ffffff", "border": "#4E79A7"}}, "size": 10.7, "font": {"size": 0, "color": "#ffffff"}, "title": "__init__.py", "community": 160, "community_name": "register_local_callback", "source_file": "app/api/__init__.py", "file_type": "code", "degree": 10}, {"id": "app_api_init_rationale_1", "label": "API \u8def\u7531\u5305 \u2014 \u5c71\u5ce1\u5316\u5de5\u751f\u4ea7\u4e00\u4f53\u5316\u95e8\u6237", "color": {"background": "#4E79A7", "border": "#4E79A7", "highlight": {"background": "#ffffff", "border": "#4E79A7"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "API \u8def\u7531\u5305 \u2014 \u5c71\u5ce1\u5316\u5de5\u751f\u4ea7\u4e00\u4f53\u5316\u95e8\u6237", "community": 160, "community_name": "register_local_callback", "source_file": "app/api/__init__.py", "file_type": "rationale", "degree": 1}, {"id": "app_api_admin", "label": "admin.py", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 17.2, "font": {"size": 12, "color": "#ffffff"}, "title": "admin.py", "community": 3, "community_name": "Community 3", "source_file": "app/api/admin.py", "file_type": "code", "degree": 110}, {"id": "app_api_admin_list_roles_for_select", "label": "list_roles_for_select()", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "list_roles_for_select()", "community": 3, "community_name": "Community 3", "source_file": "app/api/admin.py", "file_type": "code", "degree": 5}, {"id": "app_api_admin_py_session", "label": "Session", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 13.5, "font": {"size": 0, "color": "#ffffff"}, "title": "Session", "community": 3, "community_name": "Community 3", "source_file": "", "file_type": "code", "degree": 53}, {"id": "app_api_admin_list_users_for_select", "label": "list_users_for_select()", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "list_users_for_select()", "community": 3, "community_name": "Community 3", "source_file": "app/api/admin.py", "file_type": "code", "degree": 5}, {"id": "app_api_admin_list_roles_for_designer", "label": "list_roles_for_designer()", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "list_roles_for_designer()", "community": 3, "community_name": "Community 3", "source_file": "app/api/admin.py", "file_type": "code", "degree": 5}, {"id": "app_api_admin_tenantcreatebody", "label": "TenantCreateBody", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 10.5, "font": {"size": 0, "color": "#ffffff"}, "title": "TenantCreateBody", "community": 3, "community_name": "Community 3", "source_file": "app/api/admin.py", "file_type": "code", "degree": 7}, {"id": "app_api_admin_tenantupdatebody", "label": "TenantUpdateBody", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 10.5, "font": {"size": 0, "color": "#ffffff"}, "title": "TenantUpdateBody", "community": 3, "community_name": "Community 3", "source_file": "app/api/admin.py", "file_type": "code", "degree": 7}, {"id": "app_api_admin_list_tenants", "label": "list_tenants()", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "list_tenants()", "community": 3, "community_name": "Community 3", "source_file": "app/api/admin.py", "file_type": "code", "degree": 3}, {"id": "app_api_admin_create_tenant", "label": "create_tenant()", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "create_tenant()", "community": 3, "community_name": "Community 3", "source_file": "app/api/admin.py", "file_type": "code", "degree": 5}, {"id": "app_api_admin_update_tenant", "label": "update_tenant()", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "Line truncated
const RAW_EDGES = [{"from": "app_api_init", "to": "app_api_admin", "label": "re_exports", "title": "re_exports [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_init", "to": "app_api_bx_init", "label": "re_exports", "title": "re_exports [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_init", "to": "app_api_crm", "label": "re_exports", "title": "re_exports [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_init", "to": "app_api_db_init", "label": "re_exports", "title": "re_exports [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_init", "to": "app_api_leave", "label": "re_exports", "title": "re_exports [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_init", "to": "app_api_menu", "label": "re_exports", "title": "re_exports [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_init", "to": "app_api_monitor", "label": "re_exports", "title": "re_exports [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_init", "to": "app_api_public", "label": "re_exports", "title": "re_exports [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_init", "to": "app_api_workflow", "label": "re_exports", "title": "re_exports [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_init_rationale_1", "to": "app_api_init", "label": "rationale_for", "title": "rationale_for [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_admin", "to": "app_api_admin_attachment_download_url", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_admin", "to": "app_api_admin_attachment_preview_url", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_admin", "to": "app_api_admin_attachmentdownloadurlbody", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_admin", "to": "app_api_admin_create_dept", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_admin", "to": "app_api_admin_create_dict_data", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_admin", "to": "app_api_admin_create_dict_type", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_admin", "to": "app_api_admin_create_menu", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_admin", "to": "app_api_admin_create_post", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_admin", "to": "app_api_admin_create_role_management", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_admin", "to": "app_api_admin_create_site", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_admin", "to": "app_api_admin_create_tenant", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_admin", "to": "app_api_admin_create_user", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_admin", "to": "app_api_admin_delete_dept", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_admin", "to": "app_api_admin_delete_dict_data", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_admin", "to": "app_api_admin_delete_dict_type", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {Line truncated
const LEGEND = [{"cid": 0, "color": "#4E79A7", "label": "Community 0", "count": 40}, {"cid": 1, "color": "#F28E2B", "label": "Community 1", "count": 25}, {"cid": 2, "color": "#E15759", "label": "Community 2", "count": 13}, {"cid": 3, "color": "#76B7B2", "label": "Community 3", "count": 88}, {"cid": 4, "color": "#59A14F", "label": "Community 4", "count": 63}, {"cid": 5, "color": "#EDC948", "label": "Community 5", "count": 26}, {"cid": 6, "color": "#B07AA1", "label": "Community 6", "count": 53}, {"cid": 7, "color": "#FF9DA7", "label": "Community 7", "count": 37}, {"cid": 8, "color": "#9C755F", "label": "Community 8", "count": 48}, {"cid": 9, "color": "#BAB0AC", "label": "Community 9", "count": 1}, {"cid": 10, "color": "#4E79A7", "label": "Community 10", "count": 1}, {"cid": 11, "color": "#F28E2B", "label": "Community 11", "count": 61}, {"cid": 12, "color": "#E15759", "label": "Community 12", "count": 51}, {"cid": 13, "color": "#76B7B2", "label": "Community 13", "count": 42}, {"cid": 14, "color": "#59A14F", "label": "Community 14", "count": 54}, {"cid": 15, "color": "#EDC948", "label": "Community 15", "count": 53}, {"cid": 16, "color": "#B07AA1", "label": "Community 16", "count": 72}, {"cid": 17, "color": "#FF9DA7", "label": "Community 17", "count": 1}, {"cid": 18, "color": "#9C755F", "label": "Community 18", "count": 64}, {"cid": 19, "color": "#BAB0AC", "label": "Community 19", "count": 12}, {"cid": 20, "color": "#4E79A7", "label": "Community 20", "count": 1}, {"cid": 21, "color": "#F28E2B", "label": "Community 21", "count": 57}, {"cid": 22, "color": "#E15759", "label": "Community 22", "count": 43}, {"cid": 23, "color": "#76B7B2", "label": "Community 23", "count": 25}, {"cid": 24, "color": "#59A14F", "label": "Community 24", "count": 27}, {"cid": 25, "color": "#EDC948", "label": "Community 25", "count": 86}, {"cid": 26, "color": "#B07AA1", "label": "Community 26", "count": 20}, {"cid": 27, "color": "#FF9DA7", "label": "Community 27", "count": 38}, {"cid": 28, "color": "#9C755F", "label": "Community 28", "count": 54}, {"cid": 29, "color": "#BAB0AC", "label": "Community 29", "count": 41}, {"cid": 30, "color": "#4E79A7", "label": "Community 30", "count": 27}, {"cid": 31, "color": "#F28E2B", "label": "Community 31", "count": 1}, {"cid": 32, "color": "#E15759", "label": "Community 32", "count": 1}, {"cid": 33, "color": "#76B7B2", "label": "Community 33", "count": 30}, {"cid": 34, "color": "#59A14F", "label": "Community 34", "count": 24}, {"cid": 35, "color": "#EDC948", "label": "Community 35", "count": 38}, {"cid": 36, "color": "#B07AA1", "label": "Community 36", "count": 56}, {"cid": 37, "color": "#FF9DA7", "label": "Community 37", "count": 20}, {"cid": 38, "color": "#9C755F", "label": "Community 38", "count": 28}, {"cid": 39, "color": "#BAB0AC", "label": "Community 39", "count": 30}, {"cid": 40, "color": "#4E79A7", "label": "Community 40", "count": 1}, {"cid": 41, "color": "#F28E2B", "label": "Community 41", "count": 1}, {"cid": 42, "color": "#E15759", "label": "Community 42", "count": 34}, {"cid": 43, "color": "#76B7B2", "label": "Community 43", "count": 53}, {"cid": 44, "color": "#59A14F", "label": "Community 44", "count": 20}, {"cid": 45, "color": "#EDC948", "label": "Community 45", "count": 8}, {"cid": 46, "color": "#B07AA1", "label": "Community 46", "count": 20}, {"cid": 47, "color": "#FF9DA7", "label": "Community 47", "count": 1}, {"cid": 48, "color": "#9C755F", "label": "Community 48", "count": 30}, {"cid": 49, "color": "#BAB0AC", "label": "Community 49", "count": 20}, {"cid": 50, "color": "#4E79A7", "label": "Community 50", "count": 36}, {"cid": 51, "color": "#F28E2B", "label": "Community 51", "count": 24}, {"cid": 52, "color": "#E15759", "label": "Community 52", "count": 31}, {"cid": 53, "color": "#76B7B2", "label": "Community 53", "count": 47}, {"cid": 54, "color": "#59A14F", "label": "Community 54", "count": 30}, {"cid": 55, "color": "#EDC948", "label": "Community 55", "count": 1}, {"cid": 56, "color": "#B07AA1", "label": "Community 56", "count": 1}, {"cid": 57, "color": "#FF9DA7", "label": "Community 57", "count": 1}, {"cid": 58, "color": "#9C755F", "label": "Community 58", "count": 19}, {"cid": 59, "color": "#BAB0AC", "label": "Community 59", "count": 30}, {"cid": 60, "color": "#4E79A7", "label": "Community 60", "count": 19}, {"cid": 61, "color": "#F28E2B", "label": "Community 61", "count": 47}, {"cid": 62, "color": "#E15759", "label": "Community 62", "count": 23}, {"cid": 63, "color": "#76B7B2", "label": "Community 63", "count": 18}, {"cid": 64, "color": "#59A14F", "label": "Community 64", "count": 141}, {"cid": 65, "color": "#EDC948", "label": "Community 65", "count": 61}, {"cid": 66, "color": "#B07AA1", "label": "Community 66", "count": 29}, {"cid": 67, "color": "#FF9DA7", "label": "Community 67", "count": 33}, {"cid": 68, "color": "#9C755F", "label": "Community 68", "count": 16}, {"cid": 69, "color": "#BAB0AC", "label": "Community 69", "count": 27}, {"cid": 70, "color": "#4E79A7", "label": "ComLine truncated
const RAW_NODES = [{"id": "app_init", "label": "__init__.py", "color": {"background": "#59A14F", "border": "#59A14F", "highlight": {"background": "#ffffff", "border": "#59A14F"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "__init__.py", "community": 304, "community_name": "__init__.py", "source_file": "app/__init__.py", "file_type": "code", "degree": 0}, {"id": "app_api_init", "label": "__init__.py", "color": {"background": "#59A14F", "border": "#59A14F", "highlight": {"background": "#ffffff", "border": "#59A14F"}}, "size": 10.7, "font": {"size": 0, "color": "#ffffff"}, "title": "__init__.py", "community": 24, "community_name": "Community 24", "source_file": "app/api/__init__.py", "file_type": "code", "degree": 10}, {"id": "app_api_init_rationale_1", "label": "API \u8def\u7531\u5305 \u2014 \u5c71\u5ce1\u5316\u5de5\u751f\u4ea7\u4e00\u4f53\u5316\u95e8\u6237", "color": {"background": "#59A14F", "border": "#59A14F", "highlight": {"background": "#ffffff", "border": "#59A14F"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "API \u8def\u7531\u5305 \u2014 \u5c71\u5ce1\u5316\u5de5\u751f\u4ea7\u4e00\u4f53\u5316\u95e8\u6237", "community": 24, "community_name": "Community 24", "source_file": "app/api/__init__.py", "file_type": "rationale", "degree": 1}, {"id": "app_api_admin", "label": "admin.py", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 17.2, "font": {"size": 12, "color": "#ffffff"}, "title": "admin.py", "community": 3, "community_name": "Community 3", "source_file": "app/api/admin.py", "file_type": "code", "degree": 110}, {"id": "app_api_admin_list_roles_for_select", "label": "list_roles_for_select()", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "list_roles_for_select()", "community": 3, "community_name": "Community 3", "source_file": "app/api/admin.py", "file_type": "code", "degree": 5}, {"id": "app_api_admin_py_session", "label": "Session", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 13.5, "font": {"size": 0, "color": "#ffffff"}, "title": "Session", "community": 3, "community_name": "Community 3", "source_file": "", "file_type": "code", "degree": 53}, {"id": "app_api_admin_list_users_for_select", "label": "list_users_for_select()", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "list_users_for_select()", "community": 3, "community_name": "Community 3", "source_file": "app/api/admin.py", "file_type": "code", "degree": 5}, {"id": "app_api_admin_list_roles_for_designer", "label": "list_roles_for_designer()", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "list_roles_for_designer()", "community": 3, "community_name": "Community 3", "source_file": "app/api/admin.py", "file_type": "code", "degree": 5}, {"id": "app_api_admin_tenantcreatebody", "label": "TenantCreateBody", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 10.5, "font": {"size": 0, "color": "#ffffff"}, "title": "TenantCreateBody", "community": 3, "community_name": "Community 3", "source_file": "app/api/admin.py", "file_type": "code", "degree": 7}, {"id": "app_api_admin_tenantupdatebody", "label": "TenantUpdateBody", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 10.5, "font": {"size": 0, "color": "#ffffff"}, "title": "TenantUpdateBody", "community": 3, "community_name": "Community 3", "source_file": "app/api/admin.py", "file_type": "code", "degree": 7}, {"id": "app_api_admin_list_tenants", "label": "list_tenants()", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "list_tenants()", "community": 3, "community_name": "Community 3", "source_file": "app/api/admin.py", "file_type": "code", "degree": 3}, {"id": "app_api_admin_create_tenant", "label": "create_tenant()", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "create_tenant()", "community": 3, "community_name": "Community 3", "source_file": "app/api/admin.py", "file_type": "code", "degree": 5}, {"id": "app_api_admin_update_tenant", "label": "update_tenant()", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 10.3Line truncated
const RAW_EDGES = [{"from": "app_api_init", "to": "app_api_admin", "label": "re_exports", "title": "re_exports [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_init", "to": "app_api_bx_init", "label": "re_exports", "title": "re_exports [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_init", "to": "app_api_crm", "label": "re_exports", "title": "re_exports [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_init", "to": "app_api_db_init", "label": "re_exports", "title": "re_exports [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_init", "to": "app_api_leave", "label": "re_exports", "title": "re_exports [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_init", "to": "app_api_menu", "label": "re_exports", "title": "re_exports [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_init", "to": "app_api_monitor", "label": "re_exports", "title": "re_exports [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_init", "to": "app_api_public", "label": "re_exports", "title": "re_exports [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_init", "to": "app_api_workflow", "label": "re_exports", "title": "re_exports [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_init_rationale_1", "to": "app_api_init", "label": "rationale_for", "title": "rationale_for [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_admin", "to": "app_api_admin_attachment_download_url", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_admin", "to": "app_api_admin_attachment_preview_url", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_admin", "to": "app_api_admin_attachmentdownloadurlbody", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_admin", "to": "app_api_admin_create_dept", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_admin", "to": "app_api_admin_create_dict_data", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_admin", "to": "app_api_admin_create_dict_type", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_admin", "to": "app_api_admin_create_menu", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_admin", "to": "app_api_admin_create_post", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_admin", "to": "app_api_admin_create_role_management", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_admin", "to": "app_api_admin_create_site", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_admin", "to": "app_api_admin_create_tenant", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_admin", "to": "app_api_admin_create_user", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_admin", "to": "app_api_admin_delete_dept", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_admin", "to": "app_api_admin_delete_dict_data", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_api_admin", "to": "app_api_admin_delete_dict_type", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {Line truncated
const LEGEND = [{"cid": 0, "color": "#4E79A7", "label": "Community 0", "count": 40}, {"cid": 1, "color": "#F28E2B", "label": "Community 1", "count": 37}, {"cid": 2, "color": "#E15759", "label": "Community 2", "count": 20}, {"cid": 3, "color": "#76B7B2", "label": "Community 3", "count": 98}, {"cid": 4, "color": "#59A14F", "label": "Community 4", "count": 79}, {"cid": 5, "color": "#EDC948", "label": "Community 5", "count": 26}, {"cid": 6, "color": "#B07AA1", "label": "Community 6", "count": 44}, {"cid": 7, "color": "#FF9DA7", "label": "Community 7", "count": 38}, {"cid": 8, "color": "#9C755F", "label": "Community 8", "count": 47}, {"cid": 9, "color": "#BAB0AC", "label": "Community 9", "count": 1}, {"cid": 10, "color": "#4E79A7", "label": "Community 10", "count": 1}, {"cid": 11, "color": "#F28E2B", "label": "Community 11", "count": 45}, {"cid": 12, "color": "#E15759", "label": "Community 12", "count": 105}, {"cid": 13, "color": "#76B7B2", "label": "Community 13", "count": 33}, {"cid": 14, "color": "#59A14F", "label": "Community 14", "count": 35}, {"cid": 15, "color": "#EDC948", "label": "Community 15", "count": 55}, {"cid": 16, "color": "#B07AA1", "label": "Community 16", "count": 66}, {"cid": 17, "color": "#FF9DA7", "label": "Community 17", "count": 1}, {"cid": 18, "color": "#9C755F", "label": "Community 18", "count": 65}, {"cid": 19, "color": "#BAB0AC", "label": "Community 19", "count": 53}, {"cid": 20, "color": "#4E79A7", "label": "Community 20", "count": 1}, {"cid": 21, "color": "#F28E2B", "label": "Community 21", "count": 90}, {"cid": 22, "color": "#E15759", "label": "Community 22", "count": 43}, {"cid": 23, "color": "#76B7B2", "label": "Community 23", "count": 25}, {"cid": 24, "color": "#59A14F", "label": "Community 24", "count": 23}, {"cid": 25, "color": "#EDC948", "label": "Community 25", "count": 29}, {"cid": 26, "color": "#B07AA1", "label": "Community 26", "count": 20}, {"cid": 27, "color": "#FF9DA7", "label": "Community 27", "count": 31}, {"cid": 28, "color": "#9C755F", "label": "Community 28", "count": 74}, {"cid": 29, "color": "#BAB0AC", "label": "Community 29", "count": 37}, {"cid": 30, "color": "#4E79A7", "label": "Community 30", "count": 27}, {"cid": 31, "color": "#F28E2B", "label": "Community 31", "count": 1}, {"cid": 32, "color": "#E15759", "label": "Community 32", "count": 1}, {"cid": 33, "color": "#76B7B2", "label": "Community 33", "count": 24}, {"cid": 34, "color": "#59A14F", "label": "Community 34", "count": 20}, {"cid": 35, "color": "#EDC948", "label": "Community 35", "count": 38}, {"cid": 36, "color": "#B07AA1", "label": "Community 36", "count": 32}, {"cid": 37, "color": "#FF9DA7", "label": "Community 37", "count": 20}, {"cid": 38, "color": "#9C755F", "label": "Community 38", "count": 66}, {"cid": 39, "color": "#BAB0AC", "label": "Community 39", "count": 28}, {"cid": 40, "color": "#4E79A7", "label": "Community 40", "count": 1}, {"cid": 41, "color": "#F28E2B", "label": "Community 41", "count": 1}, {"cid": 42, "color": "#E15759", "label": "Community 42", "count": 34}, {"cid": 43, "color": "#76B7B2", "label": "Community 43", "count": 62}, {"cid": 44, "color": "#59A14F", "label": "Community 44", "count": 21}, {"cid": 45, "color": "#EDC948", "label": "Community 45", "count": 81}, {"cid": 46, "color": "#B07AA1", "label": "Community 46", "count": 61}, {"cid": 47, "color": "#FF9DA7", "label": "Community 47", "count": 1}, {"cid": 48, "color": "#9C755F", "label": "Community 48", "count": 29}, {"cid": 49, "color": "#BAB0AC", "label": "Community 49", "count": 19}, {"cid": 50, "color": "#4E79A7", "label": "Community 50", "count": 19}, {"cid": 51, "color": "#F28E2B", "label": "Community 51", "count": 26}, {"cid": 52, "color": "#E15759", "label": "Community 52", "count": 31}, {"cid": 53, "color": "#76B7B2", "label": "Community 53", "count": 47}, {"cid": 54, "color": "#59A14F", "label": "Community 54", "count": 24}, {"cid": 55, "color": "#EDC948", "label": "Community 55", "count": 1}, {"cid": 56, "color": "#B07AA1", "label": "Community 56", "count": 1}, {"cid": 57, "color": "#FF9DA7", "label": "Community 57", "count": 1}, {"cid": 58, "color": "#9C755F", "label": "Community 58", "count": 19}, {"cid": 59, "color": "#BAB0AC", "label": "Community 59", "count": 30}, {"cid": 60, "color": "#4E79A7", "label": "Community 60", "count": 25}, {"cid": 61, "color": "#F28E2B", "label": "Community 61", "count": 46}, {"cid": 62, "color": "#E15759", "label": "Community 62", "count": 23}, {"cid": 63, "color": "#76B7B2", "label": "Community 63", "count": 33}, {"cid": 64, "color": "#59A14F", "label": "Community 64", "count": 175}, {"cid": 65, "color": "#EDC948", "label": "Community 65", "count": 62}, {"cid": 66, "color": "#B07AA1", "label": "Community 66", "count": 40}, {"cid": 67, "color": "#FF9DA7", "label": "Community 67", "count": 17}, {"cid": 68, "color": "#9C755F", "label": "Community 68", "count": 17}, {"cid": 69, "color": "#BAB0AC", "label": "Community 69", "count": 27}, {"cid": 70, "color": "#4E79A7", "label": "CLine truncated
// HTML-escape helper — prevents XSS when injecting graph data into innerHTML
function esc(s) {
File diff suppressed because it is too large. Load diff
@@ -134,7 +134,7 @@ Java `validateFrameworkContractForSubmitApproval` 校验 **letter_oss_ids**(
|------|------|
| S1 | `CREATE TABLE crm_contract_product_price_tier`(门户字段规范) |
| S2 | `crm_contract` 补索引:`idx_status`、`idx_owner_user_id`、`idx_sign_date`、`idx_end_date`、`idx_create_time` |
| S3 | `crm_contract` 唯一约束:`uk_contract_tenant_no_del (contract_no, tenant_no, del_flag)` — 若已存在则跳过 |
| S3 | `crm_contract` 唯一约束:`uk_contract_tenant_no (contract_no, tenant_no)` — 租户内全局唯一(含软删不复用);历史曾为 `uk_contract_tenant_no_del` |
**执行顺序**:测试库手动执行 → 验证 → 步骤 15 更新 ORM → 后端实现 tier CRUD。
@@ -217,6 +217,8 @@ ENV_FILE=.env.production python scripts/migrate_crm_product_category.py \
| 当日晚 | **报价/合同审批批量办结** | 报价 `submitted`→`accepted` **726**;报价审批 pending→approved **824**;合同 `approving`→`approved` **673**;合同审批 pending→approved **682**;见 `logs/2026-08-02-force-approve-quote-contract.md` |
| 当日晚 | **报销 P2 全量重迁** | DELETE 业务表后 `--phase all`:item 48 / wf 2+4 / advance 13+27 / reimburse 127+170+195+4 / att **155**;未动发票夹;见 `phase-2-bx/logs/2026-08-02-bx-all-remigrate.md` |
| 当日晚 | **地磅 P3 全量重迁** | 源 `dibang-test`,`--truncate-target --include-deleted`:plan **118** / IO **2392** / slip **1852**;孤儿 weighing#84→IO94;见 `phase-3-dibang/logs/2026-08-02-dibang-migrate.md` |
| 2026-08-03 | **增量同步 IO#2398** | 出入库 `2398`(UPDATE)+ 地磅单 `1895`(INSERT);车皖H38397 / CRKDH0002048 / 净重 34330;见 `phase-3-dibang/logs/2026-08-03-sync-io-2398.md` |
| 2026-08-03 | **同步当日全部单据** | 源 `dibang-test` 2026-08-03:IO **6**(INSERT 2 UPDATE 4)/ slip **2**(INSERT 1 UPDATE 1);计划 0;见 `phase-3-dibang/logs/2026-08-03-sync-today.md` |
**续跑建议**
@@ -0,0 +1,56 @@
-- 合同编号重复排查(只读,不改数据)
-- 库:shanxia2.0 / crm_contract
-- 用途:检查租户内 contract_no 是否仍有重复(含软删)
SET NAMES utf8mb4;
-- 1) 按 (contract_no, tenant_no) 分组:任何重复(含软删)
SELECT
contract_no,
tenant_no,
COUNT(*) AS cnt,
SUM(CASE WHEN del_flag = 0 THEN 1 ELSE 0 END) AS alive_cnt,
SUM(CASE WHEN del_flag = 1 THEN 1 ELSE 0 END) AS deleted_cnt,
GROUP_CONCAT(
CONCAT(id, '/del', del_flag, '/', IFNULL(customer_name, ''))
ORDER BY del_flag, create_time
SEPARATOR ' | '
) AS detail
FROM crm_contract
GROUP BY contract_no, tenant_no
HAVING COUNT(*) > 1
ORDER BY cnt DESC, contract_no;
-- 2) 仅有效单据 (del_flag=0) 是否撞号
SELECT
contract_no,
tenant_no,
COUNT(*) AS alive_cnt,
GROUP_CONCAT(id ORDER BY create_time SEPARATOR ' | ') AS ids,
GROUP_CONCAT(IFNULL(customer_name, '') ORDER BY create_time SEPARATOR ' | ') AS customers
FROM crm_contract
WHERE del_flag = 0
GROUP BY contract_no, tenant_no
HAVING COUNT(*) > 1
ORDER BY alive_cnt DESC, contract_no;
-- 3) 指定编号示例:SX/X-JT20260803001(含改号后的 ~DEL~ 变体)
SELECT
id,
contract_no,
contract_name,
customer_name,
status,
del_flag,
create_time,
update_time
FROM crm_contract
WHERE tenant_no = 'shanxia2.0'
AND (
contract_no = 'SX/X-JT20260803001'
OR contract_no LIKE 'SX/X-JT20260803001~%'
)
ORDER BY del_flag, create_time;
-- 4) 当前唯一索引是否已是全局唯一版
SHOW INDEX FROM crm_contract WHERE Key_name LIKE 'uk_contract%';
@@ -0,0 +1,77 @@
-- 合同编号租户内全局唯一(含软删不复用)— 可重复执行
-- 1) 消解已有 (contract_no, tenant_no) 撞号:保留 del_flag=0,软删行改号
-- 2) 唯一索引改为 (contract_no, tenant_no)
--
-- 说明:若生产已执行过,旧索引 uk_contract_tenant_no_del 可能已不存在,
-- 本脚本会先检查再 DROP/ADD,避免 1091。
SET NAMES utf8mb4;
-- 软删撞号改号:{原编号}~DEL~{id前8位}
UPDATE crm_contract c
INNER JOIN (
SELECT contract_no, tenant_no
FROM crm_contract
GROUP BY contract_no, tenant_no
HAVING COUNT(*) > 1
) d ON c.contract_no = d.contract_no AND c.tenant_no = d.tenant_no
SET c.contract_no = CONCAT(c.contract_no, '~DEL~', LEFT(c.id, 8)),
c.update_time = NOW()
WHERE c.del_flag = 1
AND c.contract_no NOT LIKE '%~DEL~%'
AND c.contract_no NOT LIKE '%~DUP~%';
-- 若仍有撞号(极端:两条都 del_flag=0),再改非 keep 的一条
UPDATE crm_contract c
INNER JOIN (
SELECT contract_no, tenant_no, MIN(id) AS keep_id
FROM crm_contract
GROUP BY contract_no, tenant_no
HAVING COUNT(*) > 1
) d ON c.contract_no = d.contract_no AND c.tenant_no = d.tenant_no AND c.id <> d.keep_id
SET c.contract_no = CONCAT(c.contract_no, '~DUP~', LEFT(c.id, 8)),
c.update_time = NOW()
WHERE c.contract_no NOT LIKE '%~DUP~%';
-- 有旧索引则删除
SET @db := DATABASE();
SET @sql := (
SELECT IF(
EXISTS(
SELECT 1 FROM information_schema.statistics
WHERE table_schema = @db
AND table_name = 'crm_contract'
AND index_name = 'uk_contract_tenant_no_del'
),
'ALTER TABLE crm_contract DROP INDEX `uk_contract_tenant_no_del`',
'SELECT ''skip DROP uk_contract_tenant_no_del'' AS info'
)
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 无新索引则添加
SET @sql := (
SELECT IF(
EXISTS(
SELECT 1 FROM information_schema.statistics
WHERE table_schema = @db
AND table_name = 'crm_contract'
AND index_name = 'uk_contract_tenant_no'
),
'SELECT ''skip ADD uk_contract_tenant_no (already exists)'' AS info',
'ALTER TABLE crm_contract ADD UNIQUE KEY `uk_contract_tenant_no` (`contract_no`, `tenant_no`)'
)
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 验收:应为空
SELECT contract_no, tenant_no, COUNT(*) AS cnt
FROM crm_contract
GROUP BY contract_no, tenant_no
HAVING COUNT(*) > 1;
SHOW INDEX FROM crm_contract WHERE Key_name LIKE 'uk_contract%';
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -895,7 +895,7 @@ CREATE TABLE `crm_contract` (
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`del_flag` smallint(6) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_contract_tenant_no_del` (`contract_no`,`tenant_no`,`del_flag`),
UNIQUE KEY `uk_contract_tenant_no` (`contract_no`,`tenant_no`),
KEY `idx_customer_id` (`customer_id`),
KEY `idx_tenant` (`tenant_no`),
KEY `idx_opportunity_id` (`opportunity_id`),
@@ -0,0 +1,25 @@
# ListTable 列宽持久化 Implementation Plan
> **For agentic workers:** Implement task-by-task. Steps use checkbox syntax.
**Goal:** 全站主列表支持拖拽列宽,并用 localStorage 按用户+tableKey 持久化。
**Architecture:** `usePersistedListColumnWidths` + `ListTable` 薄封装 `el-table`;页面把主列表 `el-table.list-table` 换成 `ListTable` 并补 `table-key`。
**Tech Stack:** Vue 3、Element Plus、localStorage、现有 `list-table` 样式
---
### Task 1: composable + ListTable + 样式
- [x] `usePersistedListColumnWidths.ts`
- [x] `components/business/list-table/index.vue`
- [x] `el-ui.scss` 兼容 `list-table` + `el-table--border`
### Task 2: 批量接入主列表
- [x] 各业务/系统/Demo 主列表替换为 `ListTable` 并设置唯一 `table-key`(约 133 处)
### Task 3: 验证
- [ ] 拖列宽 → 刷新仍保持;固定操作列不错位;`clearSelection` 等 ref 方法可用
@@ -0,0 +1,36 @@
# 列表列宽拖拽 + localStorage 持久化(方案 C)
## 目标
所有主列表(`list-table`)支持拖拽调整列宽,并按用户 + 表格标识写入本机 localStorage,刷新后恢复。
## 方案
薄封装组件 `ListTable`(内部仍是 `el-table`):
- 默认开启 `border`(Element Plus 列宽拖拽前置条件)
- `@header-dragend` 写入宽度
- 挂载 / 数据与列变化后,把已存宽度写回表格内部列并 `doLayout()`
- 页面侧保留现有 `el-table-column` 模板,一般无需给每列绑 `:width`
## 存储
- Key:`list_col_widths:{userId}:{tableKey}`
- Value:`Record<columnKey, number>`,`columnKey` 优先 `prop`,否则 `__selection__` / `__index__` 等
- 未登录时 `userId` 用 `anonymous`
## 接入约定
- 主列表:`<el-table class="list-table" …>` → `<ListTable table-key="模块-页面" class="list-table" …>`
- `table-key` 全局唯一;同页多表必须不同 key
- 弹窗内临时明细表可暂不接入;若接入须单独 `table-key`
- `defineExpose` 代理底层 `el-table` 实例方法(如 `clearSelection`)
## 样式
沿用现有 `list-table` 单元格边框;开启 `el-table--border` 后避免双线 / 顶底外框回潮(与 `el-ui.scss` 已有 border 覆盖对齐)。
## 非目标
- 不落库、不跨设备同步
- 本阶段不持久化列顺序(列显隐仍由 `usePersistedListColumns` 负责)
+1 -1
View File
@@ -1 +1 @@
{"/Users/wuzilong/work/00code/shanxia2.0/.agents/skills/brainstorming/scripts/start-server.sh":{"size":4606,"mtime_ns":1780108930000000000,"hash":"7c3c6c5cce454f755159758334ffb434c970a29960bbff6669efe0cb22d1f2fb"},"/Users/wuzilong/work/00code/shanxia2.0/.agents/skills/brainstorming/scripts/stop-server.sh":{"size":1314,"mtime_ns":1780108930000000000,"hash":"9581ff879388aaa10463aea1fc1e9f9c00d69f2106f055ac976f97513ab84aba"},"/Users/wuzilong/work/00code/shanxia2.0/.agents/skills/systematic-debugging/find-polluter.sh":{"size":1528,"mtime_ns":1780108930000000000,"hash":"8340aac47e1279874e46d7ecb4954b5faae531a883abf05b118951687d8ca044"},"/Users/wuzilong/work/00code/shanxia2.0/.cursor/mcp.json":{"size":468,"mtime_ns":1780120483000000000,"hash":"ce4b438767a095c6a830e4612c221303ea16ab46af23f438bfb033eace904c36"},"/Users/wuzilong/work/00code/shanxia2.0/.opencode/opencode.json":{"size":105,"mtime_ns":1780120095000000000,"hash":"936122822138b12f363fb9b7a8567edaa1f834c835137a4c72d81c17116ce11e"},"/Users/wuzilong/work/00code/shanxia2.0/.opencode/package.json":{"size":65,"mtime_ns":1780109751000000000,"hash":"cf97d417f1ebce0ba81ad4629a5345e13cc95cb799c6eb2632caead5364880de"},"/Users/wuzilong/work/00code/shanxia2.0/.sisyphus/run-continuation/ses_17f66cf13ffeo6JpSbBDM7Jx4t.json":{"size":214,"mtime_ns":1780274139000000000,"hash":"26182a523926188400188b8cc668585f8e81586416fa2399fd3585b0c07ff86c"},"/Users/wuzilong/work/00code/shanxia2.0/.sisyphus/run-continuation/ses_1818ffbf9ffezXCeRFVV3Jj01O.json":{"size":263,"mtime_ns":1780238536000000000,"hash":"bf2e860eaa6fa5051d4cb0f70a53cf419b558c57759154e559712efef636dcb5"},"/Users/wuzilong/work/00code/shanxia2.0/.sisyphus/run-continuation/ses_181f43997ffeO8mkoWfZl8v2Ri.json":{"size":214,"mtime_ns":1780236423000000000,"hash":"8252a79ead22a73fe0e229ef4eb6e1aac159547cdc04607d4102c75b077501a2"},"/Users/wuzilong/work/00code/shanxia2.0/.sisyphus/run-continuation/ses_1823f3f66ffesCD5qohVVcWtbf.json":{"size":214,"mtime_ns":1780230242000000000,"hash":"27ba5fb0606f8ddda44c4f01ce5fbce77d7429f890317c6a1a6f9456058b1f70"},"/Users/wuzilong/work/00code/shanxia2.0/.sisyphus/run-continuation/ses_183d2d323ffeBMoLOLwwPmAXym.json":{"size":214,"mtime_ns":1780210339000000000,"hash":"26814a22a2f0ee80b65a7306c4f52becca5edb09358cc12b588efa896546f571"},"/Users/wuzilong/work/00code/shanxia2.0/.sisyphus/run-continuation/ses_183f72fc4ffeCZI7jMs3P9gB5n.json":{"size":214,"mtime_ns":1780201410000000000,"hash":"cd6c278503c190ec5dae59675957e9a963cbd94ce09cf974b838a0e717b42817"},"/Users/wuzilong/work/00code/shanxia2.0/.sisyphus/run-continuation/ses_184f894aeffej3cvErXgdYFhY8.json":{"size":263,"mtime_ns":1780201410000000000,"hash":"d3bb6c88a4c332c09d2684c3a34f10e311620956a8777a894cb1492ab15cce86"},"/Users/wuzilong/work/00code/shanxia2.0/.sisyphus/run-continuation/ses_186a13679ffenULQIK102x2NfS.json":{"size":214,"mtime_ns":1780201410000000000,"hash":"eb2fe454abea5ef36854bc2858ba6e9ac5e8f4d18e0121901f007816e0e40ebe"},"/Users/wuzilong/work/00code/shanxia2.0/.sisyphus/run-continuation/ses_187ac729affeBH1jDFb5X7rWz0.json":{"size":214,"mtime_ns":1780201410000000000,"hash":"b571d2be1c871201f1d572c536b57c89a277b0b3eb3f3a3e4125db5008b37a3f"},"/Users/wuzilong/work/00code/shanxia2.0/.sisyphus/run-continuation/ses_18880f06fffeJSijqM7oWZ6JYI.json":{"size":214,"mtime_ns":1780201410000000000,"hash":"efb86e46a8d0c2af314450407bfacd3f422ec8f68025291a440760cffe4ca092"},"/Users/wuzilong/work/00code/shanxia2.0/.sisyphus/run-continuation/ses_1892f6be2ffe1K80MWGl0oc2Fo.json":{"size":214,"mtime_ns":1780114611000000000,"hash":"9b109fac4a24678ea27b4afe2840bcd06c339b490508d6aa531e051fe3f034b7"},"/Users/wuzilong/work/00code/shanxia2.0/backend/app/__init__.py":{"size":21,"mtime_ns":1780113982000000000,"hash":"5d7c80dd077475acb7eb12227dc0e7cf38bbb5fd9e84c4eab9eb2be8c5406357"},"/Users/wuzilong/work/00code/shanxia2.0/backend/app/api/__init__.py":{"size":665,"mtime_ns":1780804649000000000,"hash":"349c40e55e8aae86d0913a17da2f9d338d842243e5fdf807bad52382bc2876d9"},"/Users/wuzilong/work/00code/shanxia2.0/backend/app/api/admin.py":{"size":50332,"mtime_ns":1785469147058813215,"hash":"0f8d1eccf751fcd3da60a5f4a593a4a21f614a55de02dcfb56a700f44aa6b7cb"},"/Users/wuzilong/work/00code/shanxia2.0/backend/app/api/bx/__init__.py":{"size":827,"mtime_ns":1784980394000000000,"hash":"7605bb52b5361261c5e04d52bec9a1e704b49dd4ffe5c561da079831e8d8db9e"},"/Users/wuzilong/work/00code/shanxia2.0/backend/app/api/bx/advance.py":{"size":13404,"mtime_ns":1784992736000000000,"hash":"7e4bacb178dde4fddc158713abe0eb9e8ab6fedf57e571701e45a5af3189f858"},"/Users/wuzilong/work/00code/shanxia2.0/backend/app/api/bx/attachment.py":{"size":5758,"mtime_ns":1782790898000000000,"hash":"f2d20fc5b2a98748835ae1710666f11eae1aa323d80a4678189bb2d032559b4b"},"/Users/wuzilong/work/00code/shanxia2.0/backend/app/api/bx/expense_item.py":{"size":3362,"mtime_ns":1782650913000000000,"hash":"142f0f6a3e45cb5d7b8153ee3048c5fb82258167264f325dd4fa8801b2e56c35"},"/Users/wuzilong/work/00code/shanxia2.0/backend/app/apLine truncated
{"/Users/wuzilong/work/00code/shanxia2.0/.agents/skills/brainstorming/scripts/start-server.sh":{"size":4606,"mtime_ns":1780108930000000000,"hash":"7c3c6c5cce454f755159758334ffb434c970a29960bbff6669efe0cb22d1f2fb"},"/Users/wuzilong/work/00code/shanxia2.0/.agents/skills/brainstorming/scripts/stop-server.sh":{"size":1314,"mtime_ns":1780108930000000000,"hash":"9581ff879388aaa10463aea1fc1e9f9c00d69f2106f055ac976f97513ab84aba"},"/Users/wuzilong/work/00code/shanxia2.0/.agents/skills/systematic-debugging/find-polluter.sh":{"size":1528,"mtime_ns":1780108930000000000,"hash":"8340aac47e1279874e46d7ecb4954b5faae531a883abf05b118951687d8ca044"},"/Users/wuzilong/work/00code/shanxia2.0/.cursor/mcp.json":{"size":468,"mtime_ns":1780120483000000000,"hash":"ce4b438767a095c6a830e4612c221303ea16ab46af23f438bfb033eace904c36"},"/Users/wuzilong/work/00code/shanxia2.0/.opencode/opencode.json":{"size":105,"mtime_ns":1780120095000000000,"hash":"936122822138b12f363fb9b7a8567edaa1f834c835137a4c72d81c17116ce11e"},"/Users/wuzilong/work/00code/shanxia2.0/.opencode/package.json":{"size":65,"mtime_ns":1780109751000000000,"hash":"cf97d417f1ebce0ba81ad4629a5345e13cc95cb799c6eb2632caead5364880de"},"/Users/wuzilong/work/00code/shanxia2.0/.sisyphus/run-continuation/ses_17f66cf13ffeo6JpSbBDM7Jx4t.json":{"size":214,"mtime_ns":1780274139000000000,"hash":"26182a523926188400188b8cc668585f8e81586416fa2399fd3585b0c07ff86c"},"/Users/wuzilong/work/00code/shanxia2.0/.sisyphus/run-continuation/ses_1818ffbf9ffezXCeRFVV3Jj01O.json":{"size":263,"mtime_ns":1780238536000000000,"hash":"bf2e860eaa6fa5051d4cb0f70a53cf419b558c57759154e559712efef636dcb5"},"/Users/wuzilong/work/00code/shanxia2.0/.sisyphus/run-continuation/ses_181f43997ffeO8mkoWfZl8v2Ri.json":{"size":214,"mtime_ns":1780236423000000000,"hash":"8252a79ead22a73fe0e229ef4eb6e1aac159547cdc04607d4102c75b077501a2"},"/Users/wuzilong/work/00code/shanxia2.0/.sisyphus/run-continuation/ses_1823f3f66ffesCD5qohVVcWtbf.json":{"size":214,"mtime_ns":1780230242000000000,"hash":"27ba5fb0606f8ddda44c4f01ce5fbce77d7429f890317c6a1a6f9456058b1f70"},"/Users/wuzilong/work/00code/shanxia2.0/.sisyphus/run-continuation/ses_183d2d323ffeBMoLOLwwPmAXym.json":{"size":214,"mtime_ns":1780210339000000000,"hash":"26814a22a2f0ee80b65a7306c4f52becca5edb09358cc12b588efa896546f571"},"/Users/wuzilong/work/00code/shanxia2.0/.sisyphus/run-continuation/ses_183f72fc4ffeCZI7jMs3P9gB5n.json":{"size":214,"mtime_ns":1780201410000000000,"hash":"cd6c278503c190ec5dae59675957e9a963cbd94ce09cf974b838a0e717b42817"},"/Users/wuzilong/work/00code/shanxia2.0/.sisyphus/run-continuation/ses_184f894aeffej3cvErXgdYFhY8.json":{"size":263,"mtime_ns":1780201410000000000,"hash":"d3bb6c88a4c332c09d2684c3a34f10e311620956a8777a894cb1492ab15cce86"},"/Users/wuzilong/work/00code/shanxia2.0/.sisyphus/run-continuation/ses_186a13679ffenULQIK102x2NfS.json":{"size":214,"mtime_ns":1780201410000000000,"hash":"eb2fe454abea5ef36854bc2858ba6e9ac5e8f4d18e0121901f007816e0e40ebe"},"/Users/wuzilong/work/00code/shanxia2.0/.sisyphus/run-continuation/ses_187ac729affeBH1jDFb5X7rWz0.json":{"size":214,"mtime_ns":1780201410000000000,"hash":"b571d2be1c871201f1d572c536b57c89a277b0b3eb3f3a3e4125db5008b37a3f"},"/Users/wuzilong/work/00code/shanxia2.0/.sisyphus/run-continuation/ses_18880f06fffeJSijqM7oWZ6JYI.json":{"size":214,"mtime_ns":1780201410000000000,"hash":"efb86e46a8d0c2af314450407bfacd3f422ec8f68025291a440760cffe4ca092"},"/Users/wuzilong/work/00code/shanxia2.0/.sisyphus/run-continuation/ses_1892f6be2ffe1K80MWGl0oc2Fo.json":{"size":214,"mtime_ns":1780114611000000000,"hash":"9b109fac4a24678ea27b4afe2840bcd06c339b490508d6aa531e051fe3f034b7"},"/Users/wuzilong/work/00code/shanxia2.0/backend/app/__init__.py":{"size":21,"mtime_ns":1780113982000000000,"hash":"5d7c80dd077475acb7eb12227dc0e7cf38bbb5fd9e84c4eab9eb2be8c5406357"},"/Users/wuzilong/work/00code/shanxia2.0/backend/app/api/__init__.py":{"size":665,"mtime_ns":1780804649000000000,"hash":"349c40e55e8aae86d0913a17da2f9d338d842243e5fdf807bad52382bc2876d9"},"/Users/wuzilong/work/00code/shanxia2.0/backend/app/api/admin.py":{"size":50332,"mtime_ns":1785469147058813215,"hash":"0f8d1eccf751fcd3da60a5f4a593a4a21f614a55de02dcfb56a700f44aa6b7cb"},"/Users/wuzilong/work/00code/shanxia2.0/backend/app/api/bx/__init__.py":{"size":827,"mtime_ns":1784980394000000000,"hash":"7605bb52b5361261c5e04d52bec9a1e704b49dd4ffe5c561da079831e8d8db9e"},"/Users/wuzilong/work/00code/shanxia2.0/backend/app/api/bx/advance.py":{"size":13404,"mtime_ns":1784992736000000000,"hash":"7e4bacb178dde4fddc158713abe0eb9e8ab6fedf57e571701e45a5af3189f858"},"/Users/wuzilong/work/00code/shanxia2.0/backend/app/api/bx/attachment.py":{"size":5758,"mtime_ns":1782790898000000000,"hash":"f2d20fc5b2a98748835ae1710666f11eae1aa323d80a4678189bb2d032559b4b"},"/Users/wuzilong/work/00code/shanxia2.0/backend/app/api/bx/expense_item.py":{"size":3362,"mtime_ns":1782650913000000000,"hash":"142f0f6a3e45cb5d7b8153ee3048c5fb82258167264f325dd4fa8801b2e56c35"},"/Users/wuzilong/work/00code/shanxia2.0/backend/app/apLine truncated
+2320 -2300
View File
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -1 +1 @@
.
web-admin
@@ -385,7 +385,7 @@
"383": "ApproverSelector.vue",
"384": "Community 384",
"385": "Community 385",
"386": "navigation.ts",
"386": "storage.ts",
"387": "Community 387",
"388": "index.vue",
"389": "Community 389",
@@ -417,7 +417,7 @@
"415": "Community 415",
"416": "Community 416",
"417": "Community 417",
"418": "loading.ts",
"418": "SettledPanel.vue",
"419": "Community 419",
"420": "Community 420",
"421": "Community 421",
@@ -451,7 +451,7 @@
"449": "Community 449",
"450": "Community 450",
"451": "Community 451",
"452": "crm-product-display.ts",
"452": "error.ts",
"453": "ViewKvTableCell.vue",
"454": "zh_CN.js",
"455": "zh_CN.js",
@@ -465,7 +465,7 @@
"463": "index.vue",
"464": "zh_CN.js",
"465": "index.vue",
"466": "v2.4.2.1 - 2025-06-16",
"466": "pl-batch-print.ts",
"467": "Community 467",
"468": "Community 468",
"469": "Community 469",
@@ -477,12 +477,12 @@
"475": "Community 475",
"476": "Community 476",
"477": "Community 477",
"478": "v2.4.2.3 - 2025-06-18",
"478": "SettingActions.vue",
"479": "Community 479",
"480": "Community 480",
"481": "Community 481",
"482": "detail.vue",
"483": "detail.vue",
"483": "crm-product-display.ts",
"484": "Community 484",
"485": "zh_CN.js",
"486": "Community 486",
@@ -494,16 +494,20 @@
"492": "Community 492",
"493": "SettingActions.vue",
"494": "Community 494",
"495": "useTheme.ts",
"495": "colors.ts",
"496": "Community 496",
"497": "handleEdit",
"498": "Community 498",
"499": "Community 499",
"500": "Community 500",
"501": "theme.min.js",
"502": "index.vue",
"501": "price-letter-template.ts",
"502": "highlight.ts",
"503": "Community 503",
"504": "fastEnter.ts",
"504": "index.vue",
"505": "navigation.ts",
"506": "loading.ts",
"507": "Community 507",
"508": "detail.vue",
"509": "Community 509",
"510": "Community 510",
"511": "Community 511",
@@ -511,10 +515,13 @@
"513": "Community 513",
"514": "Community 514",
"515": "Community 515",
"516": "loadUserOptions",
"517": "zh_CN.js",
"521": "Community 521",
"522": "Community 522",
"523": "Community 523",
"524": "Community 524",
"525": "index.vue",
"528": "Community 528",
"529": "Community 529",
"530": "Community 530",
@@ -1072,7 +1079,6 @@
"1273": "index.js",
"1274": "index.js",
"1275": "index.js",
"1276": "index.js",
"1277": "plugin.js",
"1278": "plugin.min.js",
"1279": "index.js",
@@ -8459,7 +8465,6 @@
"9855": "index.ts",
"9859": "index.ts",
"9860": "worktab.ts",
"9870": "index.ts",
"9871": "index.ts",
"9875": "index.ts",
"9880": "index.ts",
@@ -8503,7 +8508,6 @@
"9942": "index.vue",
"9943": "index.vue",
"9944": "index.vue",
"9945": "index.vue",
"9946": "index.vue",
"9947": "index.vue",
"9948": "index.vue",
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+186 -146
View File
@@ -15,8 +15,8 @@
"semantic_hash": ""
},
"package.json": {
"mtime": 1781960299.0,
"ast_hash": "917d064ff039bbff3cd5f58ca84905cc",
"mtime": 1785657009.9324844,
"ast_hash": "f84943886ed35640c67e7d9d11ad9857",
"semantic_hash": ""
},
"public/tinymce/icons/default/icons.js": {
@@ -925,8 +925,8 @@
"semantic_hash": ""
},
"src/api/crm/contract/index.ts": {
"mtime": 1784432876.0,
"ast_hash": "96ea22fdf780e1dbb45ef4ddca1ac752",
"mtime": 1785562213.6032443,
"ast_hash": "6552d015ed1ceece5a692541a15d267e",
"semantic_hash": ""
},
"src/api/crm/contract/template.ts": {
@@ -935,8 +935,8 @@
"semantic_hash": ""
},
"src/api/crm/contract/types.ts": {
"mtime": 1785418058.3253186,
"ast_hash": "2bdd57895c96899c016a5381cc3aadf6",
"mtime": 1785562198.7176075,
"ast_hash": "96af4081ba44c4dfcf7290bfd0e96c2f",
"semantic_hash": ""
},
"src/api/crm/customer.ts": {
@@ -990,13 +990,13 @@
"semantic_hash": ""
},
"src/api/crm/quote/index.ts": {
"mtime": 1782858537.0,
"ast_hash": "64a4aeed132b7e5f91e77500cdc72078",
"mtime": 1785561129.8765674,
"ast_hash": "db60c5fb2bf7b095b34d9c59ce1b23e6",
"semantic_hash": ""
},
"src/api/crm/quote/types.ts": {
"mtime": 1785418057.2499275,
"ast_hash": "79c22f0c0778870e8d63cd810235025f",
"mtime": 1785560664.0574367,
"ast_hash": "e2075c731aecd18d9109499499e0e460",
"semantic_hash": ""
},
"src/api/crm/supplierInfo/index.ts": {
@@ -1060,8 +1060,8 @@
"semantic_hash": ""
},
"src/api/pl/index.ts": {
"mtime": 1785484576.5006232,
"ast_hash": "618f676f635932cb16ea78b4b316aca9",
"mtime": 1785637875.0674777,
"ast_hash": "c2f8fec9acb4519de443caffa9651ebb",
"semantic_hash": ""
},
"src/api/pl/public-io-order.ts": {
@@ -1165,8 +1165,8 @@
"semantic_hash": ""
},
"src/components/business/attachment-upload-field/index.vue": {
"mtime": 1784957614.0,
"ast_hash": "d30d48a518416c3db06e3d119fd186b3",
"mtime": 1785687409.4106731,
"ast_hash": "21852610c686090dddb4e0c38de05a2d",
"semantic_hash": ""
},
"src/components/business/brand-qty-chips-field/index.vue": {
@@ -1265,8 +1265,8 @@
"semantic_hash": ""
},
"src/components/business/pl-freight-approval-dialog/index.vue": {
"mtime": 1783753616.0,
"ast_hash": "3fe1efff0d2c38b03c8c54ea019d5178",
"mtime": 1785554123.0353835,
"ast_hash": "8fd128b63e2a27ac51d1e181844d31c4",
"semantic_hash": ""
},
"src/components/business/pl-freight-disburse-dialog/index.vue": {
@@ -1290,8 +1290,8 @@
"semantic_hash": ""
},
"src/components/business/pl-paste-recognize-dialog/index.vue": {
"mtime": 1784955172.0,
"ast_hash": "cf096e29e0019c6463251c4496fe0402",
"mtime": 1785656610.2477045,
"ast_hash": "c2642bb0a71196a4a81111e645160ec7",
"semantic_hash": ""
},
"src/components/business/pl-personnel-cert-badges/index.vue": {
@@ -1320,28 +1320,28 @@
"semantic_hash": ""
},
"src/components/business/pl-plan-form-dialog/PlPlanFormProductTable.vue": {
"mtime": 1785453604.2758663,
"ast_hash": "9f357e39f48d5abe5d293c7a222889c8",
"mtime": 1785678456.0486379,
"ast_hash": "d2e486725cf5f13b3915d6dc3577c02a",
"semantic_hash": ""
},
"src/components/business/pl-plan-form-dialog/index.vue": {
"mtime": 1785443779.1846144,
"ast_hash": "ef602541306d28d17b8281815e120048",
"mtime": 1785687974.0307984,
"ast_hash": "528f3c36e3cd50bd4093a8750591d218",
"semantic_hash": ""
},
"src/components/business/pl-plan-hazard-cert-view-table/index.vue": {
"mtime": 1783752373.0,
"ast_hash": "6beb539dcc91dd6bb78417f683123cf4",
"mtime": 1785595630.9553328,
"ast_hash": "ce3f3795a19f64675f1a00e609307aff",
"semantic_hash": ""
},
"src/components/business/pl-plan-list-cert-cell/index.vue": {
"mtime": 1783267434.0,
"ast_hash": "471178c9428705f3db122576ab901508",
"mtime": 1785687834.3991888,
"ast_hash": "38ee200507b191947b3f9d769c12f084",
"semantic_hash": ""
},
"src/components/business/pl-plan-list-product-cell/index.vue": {
"mtime": 1783266934.0,
"ast_hash": "285474632192f66603ea802fb56b008f",
"mtime": 1785688271.8190951,
"ast_hash": "845bc2c5df1a226b31f39df8d68608de",
"semantic_hash": ""
},
"src/components/business/pl-plan-site-feedback-dialog/index.vue": {
@@ -1350,8 +1350,8 @@
"semantic_hash": ""
},
"src/components/business/pl-plan-transport-feedback-blocks/index.vue": {
"mtime": 1785453009.7039275,
"ast_hash": "cac7364dd44d943d1d5a19b69ab41d93",
"mtime": 1785595651.913746,
"ast_hash": "6da4aa736e1c04bc28d3d28ab9f66cd7",
"semantic_hash": ""
},
"src/components/business/pl-plan-type-tabs/index.vue": {
@@ -1365,8 +1365,8 @@
"semantic_hash": ""
},
"src/components/business/pl-plan-view-tabs/index.vue": {
"mtime": 1785453605.050906,
"ast_hash": "d652166022ea4cebcd815a64e8504720",
"mtime": 1785661200.027831,
"ast_hash": "87cb5245f8f6af374ef404143a59e9fb",
"semantic_hash": ""
},
"src/components/business/pl-product-name-hazard-cell/index.vue": {
@@ -1390,8 +1390,8 @@
"semantic_hash": ""
},
"src/components/business/pl-vehicle-cert-part-tabs/index.vue": {
"mtime": 1783844759.0,
"ast_hash": "a8237cabea3fef3788d758e43fb360ec",
"mtime": 1785644489.2764986,
"ast_hash": "bae6a74433f52d5e502620a827a5fca0",
"semantic_hash": ""
},
"src/components/business/pl-vehicle-cert-part-view/index.vue": {
@@ -1775,8 +1775,8 @@
"semantic_hash": ""
},
"src/components/core/layouts/art-header-bar/widget/ArtUserMenu.vue": {
"mtime": 1783851637.0,
"ast_hash": "834fcdec04745719d022e2b19804122f",
"mtime": 1785687261.0229707,
"ast_hash": "72b4fc9127f9bebfe02276d92ae2f8d6",
"semantic_hash": ""
},
"src/components/core/layouts/art-header-bar/widget/UserPasswordDialog.vue": {
@@ -1815,13 +1815,13 @@
"semantic_hash": ""
},
"src/components/core/layouts/art-page-content/index.vue": {
"mtime": 1782858532.0,
"ast_hash": "2fa976632599fa2c6c7dd05533098acf",
"mtime": 1785574166.2589664,
"ast_hash": "acb2c52fb67a465de38da3cccf949018",
"semantic_hash": ""
},
"src/components/core/layouts/art-portal-header/index.vue": {
"mtime": 1784980421.0,
"ast_hash": "8e5e59eacc383443e87def622e246c08",
"mtime": 1785573262.1831734,
"ast_hash": "6ad6a77d5e884aa5e8526aaf7c68a70c",
"semantic_hash": ""
},
"src/components/core/layouts/art-portal-mobile-menu/index.vue": {
@@ -2220,8 +2220,8 @@
"semantic_hash": ""
},
"src/hooks/core/useLayoutHeight.ts": {
"mtime": 1782858536.0,
"ast_hash": "77f4c895199ca2a026716644183506ee",
"mtime": 1785574161.3632877,
"ast_hash": "9d07063081ed7955a0c225dc90d8c3e2",
"semantic_hash": ""
},
"src/hooks/core/useTable.ts": {
@@ -2370,8 +2370,8 @@
"semantic_hash": ""
},
"src/router/modules/sqxp.ts": {
"mtime": 1785038548.642912,
"ast_hash": "982e56f3715b56249e52c58b92de1089",
"mtime": 1785562198.7176168,
"ast_hash": "5f10df50a13a96cdbb8f82214845e327",
"semantic_hash": ""
},
"src/router/routes/asyncRoutes.ts": {
@@ -2555,8 +2555,8 @@
"semantic_hash": ""
},
"src/utils/crm-attachment.ts": {
"mtime": 1782858530.0,
"ast_hash": "fa7f83ab53c4684d0a92f7a0cead04b6",
"mtime": 1785595389.799997,
"ast_hash": "8afe6d3149147b3bcbcbb377479545b5",
"semantic_hash": ""
},
"src/utils/crm-opportunity-labels.ts": {
@@ -2730,8 +2730,8 @@
"semantic_hash": ""
},
"src/utils/patch-el-tag-empty.ts": {
"mtime": 1782858528.0,
"ast_hash": "c0df54dff6daad2a091db842b3e0c803",
"mtime": 1785660037.7600873,
"ast_hash": "98e5c9e83b36e38e74a13ddda7fc8414",
"semantic_hash": ""
},
"src/utils/pl-batch-print.ts": {
@@ -2740,8 +2740,8 @@
"semantic_hash": ""
},
"src/utils/pl-cert-slots.ts": {
"mtime": 1783947180.0,
"ast_hash": "8af97a57279e0223c77be962f9478e87",
"mtime": 1785643488.9656408,
"ast_hash": "9248600d21502299389bfee571e40a91",
"semantic_hash": ""
},
"src/utils/pl-delivery-slip-template.ts": {
@@ -2765,13 +2765,13 @@
"semantic_hash": ""
},
"src/utils/pl-paste-recognize.ts": {
"mtime": 1784948327.0,
"ast_hash": "c2212130783b18cde1c41dd57bec99d0",
"mtime": 1785656689.388857,
"ast_hash": "10746a1e7a735dca458ae9bbf50ed2b3",
"semantic_hash": ""
},
"src/utils/pl-personnel-cert.ts": {
"mtime": 1783946960.0,
"ast_hash": "ce4a23486763cdd36e54734d609f65b6",
"mtime": 1785643495.256588,
"ast_hash": "504cdbf04ef4f6cbd002589e5aab15d5",
"semantic_hash": ""
},
"src/utils/pl-slip-pdf-render.ts": {
@@ -2795,8 +2795,8 @@
"semantic_hash": ""
},
"src/utils/pl-vehicle-cert.ts": {
"mtime": 1783946962.0,
"ast_hash": "ce856feef2f4d3f514f2d13e889f2fcc",
"mtime": 1785643499.7194405,
"ast_hash": "2897093af801798ce064c7dd8f6b1b67",
"semantic_hash": ""
},
"src/utils/protocol-payment-installment.ts": {
@@ -2815,8 +2815,8 @@
"semantic_hash": ""
},
"src/utils/router/portal-menu.ts": {
"mtime": 1782858530.0,
"ast_hash": "cb43a7a4d46a12f342b672d5fcbbed55",
"mtime": 1785573820.0049753,
"ast_hash": "a757f0d7421b493ad5b7f6a8e30e233e",
"semantic_hash": ""
},
"src/utils/router/sidebar-menu-filter.ts": {
@@ -2960,8 +2960,8 @@
"semantic_hash": ""
},
"src/utils/user-avatar.ts": {
"mtime": 1782858528.0,
"ast_hash": "b6c258b7536370e0a3d0f0a8533c8c14",
"mtime": 1785686684.0798013,
"ast_hash": "ad6de6bff49e3bce89b7f43f6c1bd418",
"semantic_hash": ""
},
"src/utils/view-dialog-refresh.ts": {
@@ -2980,13 +2980,13 @@
"semantic_hash": ""
},
"src/views/bx/advance/apply/index.vue": {
"mtime": 1785484060.3820906,
"ast_hash": "acf364846d739fb28a7e960aebc2e8eb",
"mtime": 1785576463.2360728,
"ast_hash": "462508d9a772f17b3d8cb456695a12fb",
"semantic_hash": ""
},
"src/views/bx/advance/approve/index.vue": {
"mtime": 1785484060.3825192,
"ast_hash": "c929a25ad68456bbba89bc06eef79449",
"mtime": 1785576478.511863,
"ast_hash": "c0b92572a594f8de091a1336e7023c1a",
"semantic_hash": ""
},
"src/views/bx/advance/issue/index.vue": {
@@ -3015,28 +3015,28 @@
"semantic_hash": ""
},
"src/views/bx/reimburse/apply/index.vue": {
"mtime": 1785484060.3838677,
"ast_hash": "f89bb42e26d8827d58555afb46a34351",
"mtime": 1785576281.8421597,
"ast_hash": "34959f4d18846d87de0c82822638d267",
"semantic_hash": ""
},
"src/views/bx/reimburse/approve/index.vue": {
"mtime": 1785484060.384278,
"ast_hash": "76d5c9d039afc04c4de7b3d5ecfabb8a",
"mtime": 1785576338.6838882,
"ast_hash": "5071e36c9219c711fb58dec19bf790c2",
"semantic_hash": ""
},
"src/views/bx/view/all/index.vue": {
"mtime": 1785474039.2156944,
"ast_hash": "2124c808673ccb5fc9d5ff881fe8360d",
"mtime": 1785594098.6765788,
"ast_hash": "fa74b766e8af5331f04b1d81d18a2c3a",
"semantic_hash": ""
},
"src/views/bx/view/mine/index.vue": {
"mtime": 1785474037.9511244,
"ast_hash": "d0d3eac9d9fff73b3be8becbd5d54c1c",
"mtime": 1785594082.8353248,
"ast_hash": "3026c76eff16aefcf513feaecc90b055",
"semantic_hash": ""
},
"src/views/bx/view/todo/index.vue": {
"mtime": 1785484060.3889232,
"ast_hash": "67d5d3e4bc5391ce8f9e11da8991f3f2",
"mtime": 1785554184.3029728,
"ast_hash": "90adfcb301ac359daecd4d4bbd03115a",
"semantic_hash": ""
},
"src/views/bx/workflow/advance/index.vue": {
@@ -3080,8 +3080,8 @@
"semantic_hash": ""
},
"src/views/crm/clue/index.vue": {
"mtime": 1785484060.3903744,
"ast_hash": "c49f5740e60dbda53241bd0ce8c23670",
"mtime": 1785688454.0806177,
"ast_hash": "157b4d1fee5eb6460786b3c60fdbbf4f",
"semantic_hash": ""
},
"src/views/crm/clue/invalidForm.vue": {
@@ -3095,18 +3095,18 @@
"semantic_hash": ""
},
"src/views/crm/contract/approval/index.vue": {
"mtime": 1785476270.7298734,
"ast_hash": "82474b45a3eff5d76a72357901c098f2",
"mtime": 1785688366.9445198,
"ast_hash": "01ccea7d752b4f1cb7bb949bc4c29717",
"semantic_hash": ""
},
"src/views/crm/contract/change/index.vue": {
"mtime": 1785484060.39825,
"ast_hash": "50fa69013017bd40cbdacad1f1a27b21",
"mtime": 1785688366.9445238,
"ast_hash": "342454d1b91c58829626a322d5c4a1b2",
"semantic_hash": ""
},
"src/views/crm/contract/components/ApprovalDialog.vue": {
"mtime": 1784432883.0,
"ast_hash": "9e7271444d197c71909b1a32c29ca9b7",
"mtime": 1785575851.7262146,
"ast_hash": "03dd18359ab3c2838950bb8455c0f183",
"semantic_hash": ""
},
"src/views/crm/contract/components/ContractAttachmentPanel.vue": {
@@ -3120,8 +3120,8 @@
"semantic_hash": ""
},
"src/views/crm/contract/components/ContractFormDialog.vue": {
"mtime": 1785479714.324576,
"ast_hash": "ca1360a240ff06acb5691c058415335a",
"mtime": 1785633696.9241896,
"ast_hash": "5327a57a90552e3041923e8f06ef5f1c",
"semantic_hash": ""
},
"src/views/crm/contract/components/ExecuteDialog.vue": {
@@ -3135,8 +3135,8 @@
"semantic_hash": ""
},
"src/views/crm/contract/components/ProductSelector.vue": {
"mtime": 1784997424.0,
"ast_hash": "003e4de5b02b87ad24e4db9a3eb36d14",
"mtime": 1785688447.4651124,
"ast_hash": "021205c084c3f68042d66a17864c818a",
"semantic_hash": ""
},
"src/views/crm/contract/components/RenewDialog.vue": {
@@ -3160,8 +3160,8 @@
"semantic_hash": ""
},
"src/views/crm/contract/index.vue": {
"mtime": 1785483590.9993153,
"ast_hash": "89a68c57dbfaf8d1d408ab2f70584b3f",
"mtime": 1785688299.7803407,
"ast_hash": "86bf1f9773d3f6ca8dbdc8bf1cabf82b",
"semantic_hash": ""
},
"src/views/crm/contract/template/index.vue": {
@@ -3170,8 +3170,8 @@
"semantic_hash": ""
},
"src/views/crm/customer/CustomerDetailDialog.vue": {
"mtime": 1783823911.0,
"ast_hash": "291b100b9b5ec6f6bd8c6602434265b7",
"mtime": 1785688467.7160695,
"ast_hash": "ad7a7a5ba907abe78f65c0e440419622",
"semantic_hash": ""
},
"src/views/crm/customer/CustomerFormDialog.vue": {
@@ -3215,8 +3215,8 @@
"semantic_hash": ""
},
"src/views/crm/customer/index.vue": {
"mtime": 1785484060.4056373,
"ast_hash": "2d059e1b1fa1b8eb1d662a211b12b51a",
"mtime": 1785688276.8571663,
"ast_hash": "71eb27dccf74c3118f254d616ffbf0b6",
"semantic_hash": ""
},
"src/views/crm/customer/pool/RuleIndex.vue": {
@@ -3240,8 +3240,8 @@
"semantic_hash": ""
},
"src/views/crm/customer/pool/index.vue": {
"mtime": 1785484060.4091,
"ast_hash": "ff5ab04aeda3a2369b8c9b1cf28337ee",
"mtime": 1785688278.4846113,
"ast_hash": "e92c6b1a5377a50c5e1c38b31b4e13b8",
"semantic_hash": ""
},
"src/views/crm/opportunity/components/BatchUpdateDateDialog.vue": {
@@ -3295,8 +3295,8 @@
"semantic_hash": ""
},
"src/views/crm/opportunity/index.vue": {
"mtime": 1785483535.921578,
"ast_hash": "1405f0b40374a04d78d97950531fa8a8",
"mtime": 1785688365.6783743,
"ast_hash": "58c4816c443495cfc93dad24e785cb7a",
"semantic_hash": ""
},
"src/views/crm/opportunity/kanban.vue": {
@@ -3305,8 +3305,8 @@
"semantic_hash": ""
},
"src/views/crm/opportunity/statistics.vue": {
"mtime": 1783749525.0,
"ast_hash": "12cd5228f44374c1a0b7ad364315f7d1",
"mtime": 1785688417.6763241,
"ast_hash": "ebe4b09c055571af7497386424d1991d",
"semantic_hash": ""
},
"src/views/crm/price-letter/components/VoidDialog.vue": {
@@ -3315,8 +3315,8 @@
"semantic_hash": ""
},
"src/views/crm/price-letter/index.vue": {
"mtime": 1785483594.8454294,
"ast_hash": "0b47ecfffdd2a4361551974f4a51230b",
"mtime": 1785688366.9397857,
"ast_hash": "fab3bba9aa0b037ee3e0bf578d03c763",
"semantic_hash": ""
},
"src/views/crm/product-category/index.vue": {
@@ -3325,23 +3325,23 @@
"semantic_hash": ""
},
"src/views/crm/product/index.vue": {
"mtime": 1785484060.4052067,
"ast_hash": "0feaafc2cce101e3beece19e0ff57671",
"mtime": 1785688299.1079094,
"ast_hash": "2bc525683a961a8367af5455a4928777",
"semantic_hash": ""
},
"src/views/crm/quote/components/ApprovalDialog.vue": {
"mtime": 1783753615.0,
"ast_hash": "2663d2ded83d77600246f8dd4c939b93",
"mtime": 1785554132.7209337,
"ast_hash": "8651b3d4814aa74e1dbf387605192722",
"semantic_hash": ""
},
"src/views/crm/quote/components/ApproverSelector.vue": {
"mtime": 1784513207.0,
"ast_hash": "e966a94b79ed4774511b2e8fa091cb52",
"mtime": 1785561155.732058,
"ast_hash": "2f1f262d7f5bb76d5f0b392f1652b86d",
"semantic_hash": ""
},
"src/views/crm/quote/components/ProductSelector.vue": {
"mtime": 1784997422.0,
"ast_hash": "7a52dbef9481ca01f8254b640978ade5",
"mtime": 1785688447.7888398,
"ast_hash": "d33f78a4d6a872ee202723a781e2525f",
"semantic_hash": ""
},
"src/views/crm/quote/components/QuoteDetailDialog.vue": {
@@ -3355,8 +3355,8 @@
"semantic_hash": ""
},
"src/views/crm/quote/components/QuoteFormPanel.vue": {
"mtime": 1785418107.430554,
"ast_hash": "b2680f0756f645c4acd2602beaa110bd",
"mtime": 1785633695.5956545,
"ast_hash": "5edd7b3bdd31330234d384ddc3bc5276",
"semantic_hash": ""
},
"src/views/crm/quote/components/ShareDialog.vue": {
@@ -3390,8 +3390,8 @@
"semantic_hash": ""
},
"src/views/crm/quote/index.vue": {
"mtime": 1785483592.9234543,
"ast_hash": "01067387bb862a72548237babfcc0dd9",
"mtime": 1785688315.153787,
"ast_hash": "6859c621455f7ce8a269e32e1264f633",
"semantic_hash": ""
},
"src/views/crm/quote/statistics.vue": {
@@ -3415,13 +3415,13 @@
"semantic_hash": ""
},
"src/views/db/inventory/index.vue": {
"mtime": 1785483597.0377345,
"ast_hash": "7cdcbe48f8f547e610f290c34680ff9f",
"mtime": 1785688379.8658595,
"ast_hash": "ef6f2f3653fadbef1405a2c6539e296d",
"semantic_hash": ""
},
"src/views/db/plans/index.vue": {
"mtime": 1785483600.0356514,
"ast_hash": "2fab328cca0f03d8e07fd5c0b27c042f",
"mtime": 1785688371.4975572,
"ast_hash": "d29e15137292e2d5fafc50d6a0e3495a",
"semantic_hash": ""
},
"src/views/db/weighing/index.vue": {
@@ -3430,18 +3430,18 @@
"semantic_hash": ""
},
"src/views/demo/category/index.vue": {
"mtime": 1785483535.9377656,
"ast_hash": "aa31285f9df879ecf4930ea1f476b52a",
"mtime": 1785687766.4212794,
"ast_hash": "c419b61ffa5e361a60ae8a4234e94541",
"semantic_hash": ""
},
"src/views/demo/order/index.vue": {
"mtime": 1783753626.0,
"ast_hash": "db0c8e5011a45243206b04283b5cfc9a",
"mtime": 1785688424.0064135,
"ast_hash": "edbca188e778296efaca1b8ece3d6f2f",
"semantic_hash": ""
},
"src/views/demo/product/index.vue": {
"mtime": 1785483535.9407132,
"ast_hash": "3868a0f2a998e4656ca1b9b99fab5b0e",
"mtime": 1785687790.647131,
"ast_hash": "b10eb985c467b5382620ab2e6c5ed9d2",
"semantic_hash": ""
},
"src/views/demo/unit/index.vue": {
@@ -3465,8 +3465,8 @@
"semantic_hash": ""
},
"src/views/index/index.vue": {
"mtime": 1784974043.0,
"ast_hash": "c0ac5f6b4e6e8ae8679e2415bba44faa",
"mtime": 1785550322.081815,
"ast_hash": "daac0b5ab188a0a361fa4615baaf1229",
"semantic_hash": ""
},
"src/views/index/route-parent.vue": {
@@ -3480,13 +3480,13 @@
"semantic_hash": ""
},
"src/views/pl/_shared/IoOrderListPage.vue": {
"mtime": 1785484848.206626,
"ast_hash": "c1596dbaa7e56aa6c2132b095c18205f",
"mtime": 1785688447.254805,
"ast_hash": "5a9207e867263f0a9e336661791cd421",
"semantic_hash": ""
},
"src/views/pl/carrier/index.vue": {
"mtime": 1785484060.378389,
"ast_hash": "048d4ee0a23b918d5f3f76cf68619cd4",
"mtime": 1785687647.189011,
"ast_hash": "a388dab9bcba16a7ea80cd394a6d4117",
"semantic_hash": ""
},
"src/views/pl/driver/index.vue": {
@@ -3555,8 +3555,8 @@
"semantic_hash": ""
},
"src/views/pl/inventory/ledger.vue": {
"mtime": 1785484060.376128,
"ast_hash": "12123788d6253c5917f43e9fc653d5c0",
"mtime": 1785688368.7035508,
"ast_hash": "c007e8bc143e3b6680b0eba6eb0fe8f2",
"semantic_hash": ""
},
"src/views/pl/outbound/index.vue": {
@@ -3565,18 +3565,18 @@
"semantic_hash": ""
},
"src/views/pl/personnel/index.vue": {
"mtime": 1785484060.3787086,
"ast_hash": "4147f6500602567ff0304731d7212080",
"mtime": 1785687647.3870685,
"ast_hash": "e09a07b46657f809428b2043c97efaa1",
"semantic_hash": ""
},
"src/views/pl/plan-archive/index.vue": {
"mtime": 1785484060.3771799,
"ast_hash": "9affbba86635dadf3b88d3675d059807",
"mtime": 1785688369.0286162,
"ast_hash": "3faf5a22e1779430de4430e7ee626435",
"semantic_hash": ""
},
"src/views/pl/plan-cert/index.vue": {
"mtime": 1785484025.1107006,
"ast_hash": "24553244c8b667db6d12dd0f7adced42",
"mtime": 1785688378.7003713,
"ast_hash": "28e040218bb32782b5a6cd6dd011bd18",
"semantic_hash": ""
},
"src/views/pl/plan/arrange.vue": {
@@ -3595,13 +3595,13 @@
"semantic_hash": ""
},
"src/views/pl/plan/index.vue": {
"mtime": 1785483278.3425868,
"ast_hash": "29b38860eb65cfa27f8822d6aea4db9c",
"mtime": 1785688368.1447096,
"ast_hash": "d4badeb92d01a5916b3890585dade015",
"semantic_hash": ""
},
"src/views/pl/self-vehicle/index.vue": {
"mtime": 1785484060.3775532,
"ast_hash": "0fae9d03efc6d5c5eeea1182f5b3ba03",
"mtime": 1785687648.187472,
"ast_hash": "e8c6c96122dd4b288a9feb1e36ee81cc",
"semantic_hash": ""
},
"src/views/pl/transport-address/index.vue": {
@@ -3645,8 +3645,8 @@
"semantic_hash": ""
},
"src/views/pl/vehicle/index.vue": {
"mtime": 1785484060.3667428,
"ast_hash": "d2fde8d1efcfe3f281404c6788a83133",
"mtime": 1785687648.545521,
"ast_hash": "8f97e2381c5af2f7af84e3002ca75cfa",
"semantic_hash": ""
},
"src/views/public/db/inventory/index.vue": {
@@ -3745,8 +3745,8 @@
"semantic_hash": ""
},
"src/views/system/workflow/components/ApprovalDialog.vue": {
"mtime": 1782858542.0,
"ast_hash": "c9add9d76a1766f53a55a841f6a10d26",
"mtime": 1785554129.2843409,
"ast_hash": "30bf5da8a4a8f1e00d9ee7baa8f2c8b8",
"semantic_hash": ""
},
"src/views/system/workflow/components/ProcessStatusTag.vue": {
@@ -3785,8 +3785,8 @@
"semantic_hash": ""
},
"vite.config.ts": {
"mtime": 1781960302.0,
"ast_hash": "612f53ff8840a0a9efce97b05723b717",
"mtime": 1785657006.2875016,
"ast_hash": "3396c99ac3851533d6261f7209236057",
"semantic_hash": ""
},
"CHANGELOG.md": {
@@ -5163,5 +5163,45 @@
"mtime": 1780108931.0,
"ast_hash": "839d61e0376bb89e1c2bd458ecb2051b",
"semantic_hash": ""
},
"src/components/business/pl-carrier-list-attachment-cell/index.vue": {
"mtime": 1785687619.1675503,
"ast_hash": "071b3cfadbb1f387e27f8162e67d210f",
"semantic_hash": ""
},
"src/components/business/pl-cert-list-attachment-cell/index.vue": {
"mtime": 1785687487.253446,
"ast_hash": "1376d4dc0abb1a11c3e39d36f2bf5420",
"semantic_hash": ""
},
"src/components/core/layouts/art-portal-mega-menu/index.vue": {
"mtime": 1785573680.8338625,
"ast_hash": "555646bf9b64c4a7ff733e3050fbaa2c",
"semantic_hash": ""
},
"src/composables/useListMediaActive.ts": {
"mtime": 1785687608.3699732,
"ast_hash": "1f70855fbe442733e27e8824ccf01bea",
"semantic_hash": ""
},
"src/utils/pl-photo.ts": {
"mtime": 1785637871.6080778,
"ast_hash": "6c38aee291a78f98b2fdc35e69de2038",
"semantic_hash": ""
},
"src/views/crm/contract/approver-setting/index.vue": {
"mtime": 1785562205.1300278,
"ast_hash": "5029bcf7da48be7e164662d243b160c1",
"semantic_hash": ""
},
"src/views/crm/quote/approver-setting/index.vue": {
"mtime": 1785561133.7066674,
"ast_hash": "721699f8c9f13cfe9e11e4a040dd7030",
"semantic_hash": ""
},
"public/avatar.svg": {
"mtime": 1785686983.0506332,
"ast_hash": "40bdcac74cbf715934839a61b455a519",
"semantic_hash": ""
}
}
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+188
View File
@@ -0,0 +1,188 @@
/**
* 将带 class="list-table" 的 <el-table> 批量替换为 <ListTable table-key="...">。
* 同文件多表自动追加 -2/-3;不会误匹配 el-table-column。
*
* Usage: node scripts/migrate-list-table.mjs
*/
import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const srcRoot = path.resolve(__dirname, '../src')
const viewsRoot = path.resolve(srcRoot, 'views')
const componentsRoot = path.resolve(srcRoot, 'components')
/** 匹配真正的 el-table 开标签,排除 el-table-column / el-table-column-group 等 */
const OPEN_RE = /<el-table(?=[\s/>])/g
const CLOSE_TAG = '</el-table>'
function walk(dir, out = []) {
if (!fs.existsSync(dir)) return out
for (const name of fs.readdirSync(dir)) {
const full = path.join(dir, name)
const st = fs.statSync(full)
if (st.isDirectory()) {
// 跳过 ListTable 自身,避免把内部 el-table 替换掉
if (full.endsWith(`${path.sep}list-table`)) continue
walk(full, out)
} else if (name.endsWith('.vue')) out.push(full)
}
return out
}
function toKebab(s) {
return String(s)
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
.replace(/[_\s]+/g, '-')
.replace(/-+/g, '-')
.toLowerCase()
}
function baseKeyFromFile(file) {
let rel
if (file.startsWith(viewsRoot + path.sep) || file.startsWith(viewsRoot + '/')) {
rel = path.relative(viewsRoot, file)
} else {
rel = path.relative(componentsRoot, file)
}
const parts = rel.split(path.sep)
const stem = path.basename(file, '.vue')
const dirs = parts.slice(0, -1).map(toKebab)
if (!['index', 'Index'].includes(stem)) dirs.push(toKebab(stem))
return dirs.filter(Boolean).join('-') || toKebab(stem)
}
function hasListTableClass(attrText) {
return /class\s*=\s*(['"`])([^'"`]*\blist-table\b[^'"`]*)\1/.test(attrText)
}
function findOpenTagEnd(content, nameEnd) {
let j = nameEnd
let quote = null
while (j < content.length) {
const ch = content[j]
if (quote) {
if (ch === quote) quote = null
} else if (ch === '"' || ch === "'" || ch === '`') {
quote = ch
} else if (ch === '>') {
return j
}
j++
}
return -1
}
function transform(content, file) {
const baseKey = baseKeyFromFile(file)
let tableIndex = 0
let i = 0
let out = ''
while (i < content.length) {
OPEN_RE.lastIndex = i
const m = OPEN_RE.exec(content)
if (!m) {
out += content.slice(i)
break
}
out += content.slice(i, m.index)
const start = m.index
const nameEnd = start + '<el-table'.length
const openEnd = findOpenTagEnd(content, nameEnd)
if (openEnd < 0) {
out += content.slice(start)
break
}
const attrText = content.slice(nameEnd, openEnd)
const selfClosing = content[openEnd - 1] === '/'
const isList = hasListTableClass(attrText)
if (!isList) {
out += content.slice(start, openEnd + 1)
i = openEnd + 1
continue
}
tableIndex += 1
const key = tableIndex === 1 ? baseKey : `${baseKey}-${tableIndex}`
let newAttrs = attrText
if (!/\btable-key\s*=/.test(newAttrs) && !/\btableKey\s*=/.test(newAttrs)) {
newAttrs = ` table-key="${key}"` + newAttrs
}
if (selfClosing) {
out += `<ListTable${newAttrs.slice(0, -1)} />`
i = openEnd + 1
continue
}
out += `<ListTable${newAttrs}>`
i = openEnd + 1
let depth = 1
while (i < content.length && depth > 0) {
OPEN_RE.lastIndex = i
const nextOpen = OPEN_RE.exec(content)
const nextCloseIdx = content.indexOf(CLOSE_TAG, i)
if (nextCloseIdx < 0) {
out += content.slice(i)
i = content.length
depth = 0
break
}
const openIdx = nextOpen ? nextOpen.index : -1
if (openIdx >= 0 && openIdx < nextCloseIdx) {
// 嵌套 el-table:原样拷贝开标签,depth++
out += content.slice(i, openIdx)
const nestedNameEnd = openIdx + '<el-table'.length
const nestedOpenEnd = findOpenTagEnd(content, nestedNameEnd)
if (nestedOpenEnd < 0) {
out += content.slice(openIdx)
i = content.length
depth = 0
break
}
out += content.slice(openIdx, nestedOpenEnd + 1)
i = nestedOpenEnd + 1
if (content[nestedOpenEnd - 1] !== '/') depth += 1
continue
}
// 关闭标签
out += content.slice(i, nextCloseIdx)
depth -= 1
if (depth === 0) {
out += '</ListTable>'
} else {
out += CLOSE_TAG
}
i = nextCloseIdx + CLOSE_TAG.length
}
}
return { content: out, count: tableIndex }
}
const files = [...walk(viewsRoot), ...walk(path.join(componentsRoot, 'business'))]
let total = 0
const changed = []
for (const file of files) {
const raw = fs.readFileSync(file, 'utf8')
if (!raw.includes('list-table') || !raw.includes('<el-table')) continue
const { content, count } = transform(raw, file)
if (count > 0 && content !== raw) {
fs.writeFileSync(file, content)
total += count
changed.push(`${path.relative(path.resolve(__dirname, '..'), file)} (${count})`)
}
}
console.log(`Migrated ${total} tables in ${changed.length} files:`)
for (const line of changed) console.log(' -', line)
+4 -10
View File
@@ -867,6 +867,7 @@ body .el-table thead {
}
// 主列表:无顶/底外框;左右外缘 + 列间竖线 + 行间横线
// 含 el-table--border(ListTable 为列宽拖拽开启):覆盖上方「border 模式去竖线」规则,保持 list-table 网格
.page-container .list-table.el-table,
.console-container .list-table.el-table,
.el-dialog .list-table.el-table,
@@ -909,16 +910,9 @@ body .el-table thead {
}
}
/*
* 横向滚到最右侧时末列完整可见:
* 给表头/表体 table 右侧留出空隙,增大可滚动宽度,避免末列被 overflow 或
* 叠加滚动条裁切。不要给末列 .cell 加 padding,否则会挤占「创建时间」等定宽文案。
*/
&.el-table--scrollable-x {
.el-table__header-wrapper table,
.el-table__body-wrapper table {
margin-right: 12px;
}
// 列宽拖拽代理线(Element Plus border 模式)
.el-table__column-resize-proxy {
border-left: 1px dashed var(--mint-primary, #10b981) !important;
}
}
@@ -59,7 +59,7 @@
<div class="panel dialog-list-panel">
<div class="list-body list-wrap dialog-list-body-only">
<el-table
<ListTable table-key="business-contract-selector"
ref="tableRef"
v-loading="loading"
:data="contractList"
@@ -138,7 +138,7 @@
{{ planCountMap[String(row.id)] ?? 0 }}
</template>
</el-table-column>
</el-table>
</ListTable>
<ListPagination
v-show="total > 0"
@@ -7,13 +7,13 @@
:remote-method="searchRemote"
:loading="loading"
:allow-create="allowCreate"
default-first-option
:default-first-option="allowCreate"
clearable
style="width: 100%"
@focus="handleFocus"
>
<el-option
v-for="item in options"
v-for="item in displayOptions"
:key="item.id"
:label="item.customerName"
:value="item.customerName"
@@ -98,6 +98,22 @@
loadDefault()
}
}
/** 编辑回显:历史往来单位若不在客户库,仍展示只读选项(禁止 allow-create 时) */
const displayOptions = computed(() => {
const current = model.value?.trim()
if (!current) return options.value
if (options.value.some((item) => item.customerName === current)) {
return options.value
}
return [
{
id: `legacy-${current}`,
customerName: current
},
...options.value
]
})
</script>
<style scoped lang="scss">
@@ -53,6 +53,7 @@
productName: string
model?: string
unit?: string
status?: string
}
const dictStore = useDictStore()
@@ -60,11 +61,14 @@
const options = ref<ProductOption[]>([])
const formatProductMeta = (item: ProductOption): string => {
const parts: string[] = []
const model = item.model?.trim() || ''
const unitRaw = item.unit?.trim() || ''
const unit = unitRaw ? dictStore.getLabel('crm_product_unit', unitRaw) : ''
if (model && unit) return `${model} / ${unit}`
return model || unit || ''
if (model && unit) parts.push(`${model} / ${unit}`)
else if (model || unit) parts.push(model || unit)
if (item.status === 'off') parts.push('已下架')
return parts.join(' · ')
}
const extractList = (res: unknown): ProductOption[] => {
@@ -84,6 +88,7 @@
specification.value = item.model?.trim() || ''
}
/** 编辑回显:当前产品即使已下架也保留在选项中,新建/重选仅上架产品 */
const ensureCurrentOption = async () => {
const id = productId.value?.trim()
if (!id) return
@@ -103,7 +108,7 @@
const loadDefault = async () => {
loading.value = true
try {
const res = await fetchProductList({ page: 1, pageSize: 20 })
const res = await fetchProductList({ page: 1, pageSize: 20, status: 'on' })
options.value = extractList(res)
await ensureCurrentOption()
} catch (error) {
@@ -124,7 +129,8 @@
const res = await fetchProductList({
keyword: query.trim(),
page: 1,
pageSize: 50
pageSize: 50,
status: 'on'
})
options.value = extractList(res)
await ensureCurrentOption()
@@ -0,0 +1,156 @@
<!--
主列表薄封装:在 el-table 上启用列宽拖拽,并按 table-key 持久化到 localStorage。
用法:将 <el-table class="list-table" …> 换成 <ListTable table-key="crm-customer" class="list-table" …>
-->
<template>
<el-table
ref="tableRef"
v-bind="tableAttrs"
:border="resolvedBorder"
:allow-drag-last-column="true"
:class="tableClass"
@header-dragend="handleHeaderDragEnd"
>
<slot />
</el-table>
</template>
<script setup lang="ts">
import { computed, nextTick, onMounted, ref, unref, useAttrs, watch } from 'vue'
import {
resolveListColumnKey,
usePersistedListColumnWidths
} from '@/composables/usePersistedListColumnWidths'
defineOptions({ name: 'ListTable', inheritAttrs: false })
const props = withDefaults(
defineProps<{
/** 表格唯一标识,用于 localStorage 分区(同页多表必须不同) */
tableKey: string
/** 是否持久化列宽,默认 true */
persistColumnWidth?: boolean
/** 是否开启 border(列宽拖拽依赖);默认 true */
border?: boolean
}>(),
{
persistColumnWidth: true,
border: true
}
)
const attrs = useAttrs()
const tableRef = ref<any>(null)
const { setWidth, applyToTable, resetWidths, getWidths } = usePersistedListColumnWidths(
computed(() => props.tableKey)
)
const resolvedBorder = computed(() => props.border !== false)
const tableClass = computed(() => {
const extra = attrs.class
return ['list-table', extra]
})
const tableAttrs = computed(() => {
const raw = { ...attrs } as Record<string, unknown>
delete raw.class
delete raw.border
// 避免与内部 handler 重复绑定(父级若仍监听,下面会手动转发)
delete raw.onHeaderDragend
delete raw.onHeaderDragEnd
return raw
})
const parentHeaderDragEnd = computed(() => {
const a = attrs as Record<string, unknown>
return (a.onHeaderDragend || a.onHeaderDragEnd) as
| ((...args: any[]) => void)
| Array<(...args: any[]) => void>
| undefined
})
let applyTimer: ReturnType<typeof setTimeout> | null = null
const scheduleApply = () => {
if (!props.persistColumnWidth) return
if (!Object.keys(getWidths()).length) return
if (applyTimer) clearTimeout(applyTimer)
applyTimer = setTimeout(() => {
nextTick(() => {
applyToTable(tableRef.value)
})
}, 16)
}
const handleHeaderDragEnd = (
newWidth: number,
oldWidth: number,
column: any,
event: MouseEvent
) => {
if (props.persistColumnWidth) {
const key = resolveListColumnKey(column)
if (key) setWidth(key, newWidth)
}
const parent = parentHeaderDragEnd.value
if (typeof parent === 'function') {
parent(newWidth, oldWidth, column, event)
} else if (Array.isArray(parent)) {
parent.forEach((fn) => fn?.(newWidth, oldWidth, column, event))
}
}
onMounted(() => {
scheduleApply()
// 首屏列尚未完全注册时再补一次
setTimeout(() => scheduleApply(), 100)
})
watch(
() => (attrs as any).data,
() => scheduleApply(),
{ deep: false }
)
watch(
() => props.tableKey,
() => scheduleApply()
)
// 列显隐导致列数量变化时写回(避免在 drag 过程中用 onUpdated 反复 apply)
watch(
() => {
const cols = tableRef.value?.store?.states?.columns
return unref(cols)?.length ?? 0
},
() => scheduleApply()
)
/**
* 代理底层 el-table,保证父组件 ref 上的 clearSelection / doLayout 等仍可用
*/
const exposed = new Proxy(
{},
{
get(_target, prop) {
if (prop === 'elTableRef') return tableRef
if (prop === 'applyColumnWidths') {
return () => applyToTable(tableRef.value)
}
if (prop === 'resetColumnWidths') {
return () => {
resetWidths()
}
}
const table = tableRef.value
if (!table) return undefined
const value = table[prop as keyof typeof table]
return typeof value === 'function' ? value.bind(table) : value
}
}
)
defineExpose(exposed)
</script>
@@ -9,7 +9,7 @@
<div v-if="!certList.length" class="cert-empty">{{ emptyHint }}</div>
<el-table
<ListTable table-key="business-pl-cert-form-list"
v-else-if="layout === 'table'"
:data="certList"
stripe
@@ -113,7 +113,7 @@
</el-button>
</template>
</el-table-column>
</el-table>
</ListTable>
<div v-else class="cert-card-list">
<div v-for="(cert, index) in certList" :key="cert._key" class="cert-card">
@@ -4,7 +4,7 @@
<div v-else-if="layout === 'table'" class="panel dialog-list-panel">
<div class="list-body list-wrap dialog-list-body-only">
<el-table
<ListTable table-key="business-pl-cert-view-list"
:data="certList"
class="list-table"
stripe
@@ -77,7 +77,7 @@
<span v-else>—</span>
</template>
</el-table-column>
</el-table>
</ListTable>
</div>
</div>
@@ -113,7 +113,7 @@
<h3 class="dialog-section-title">关联运输信息</h3>
<div class="panel dialog-list-panel">
<div class="list-body list-wrap dialog-list-body-only">
<el-table
<ListTable table-key="business-pl-freight-disburse-dialog"
:data="settlement.transports || []"
class="list-table"
stripe
@@ -159,7 +159,7 @@
<el-table-column prop="freightAmount" label="运费(元)" min-width="110" align="right">
<template #default="{ row }">{{ formatAmount(row.freightAmount) }}</template>
</el-table-column>
</el-table>
</ListTable>
</div>
</div>
</section>
@@ -87,7 +87,7 @@
</div>
<div class="panel dialog-list-panel">
<div class="list-body list-wrap dialog-list-body-only">
<el-table
<ListTable table-key="business-pl-freight-settlement-form-dialog"
:data="editableTransports"
class="list-table"
stripe
@@ -152,7 +152,7 @@
</el-button>
</template>
</el-table-column>
</el-table>
</ListTable>
</div>
</div>
<p v-if="canEditTransports" class="dialog-section-tip">至少保留一条关联运输;删除后可在「添加运输」中重新加入</p>
@@ -222,7 +222,7 @@
</div>
</div>
<div class="list-body list-wrap dialog-list-body-only">
<el-table
<ListTable table-key="business-pl-freight-settlement-form-dialog-2"
:data="removedTransports"
class="list-table"
stripe
@@ -309,7 +309,7 @@
>
<template #default="{ row }">{{ formatAmount(row.freightAmount) }}</template>
</el-table-column>
</el-table>
</ListTable>
</div>
</div>
@@ -322,7 +322,7 @@
</div>
</div>
<div class="list-body list-wrap dialog-list-body-only">
<el-table
<ListTable table-key="business-pl-freight-settlement-form-dialog-3"
v-loading="addLoading"
:data="availablePlans"
class="list-table"
@@ -412,7 +412,7 @@
row.archiveTime ? formatDateTime(row.archiveTime) : '—'
}}</template>
</el-table-column>
</el-table>
</ListTable>
<div class="pagination list-pager-bar">
<div class="list-pager-selected">
@@ -32,7 +32,7 @@
<h4 class="view-section-title">发票信息</h4>
<div class="panel dialog-list-panel">
<div class="list-body list-wrap dialog-list-body-only">
<el-table
<ListTable table-key="business-pl-freight-settlement-view-dialog"
:data="invoiceRows"
class="list-table"
stripe
@@ -61,7 +61,7 @@
<span v-else class="view-kv-value">—</span>
</template>
</el-table-column>
</el-table>
</ListTable>
</div>
</div>
<div class="view-kv-grid view-kv-grid--cols-2 view-kv-grid--mt">
@@ -80,7 +80,7 @@
<h4 class="view-section-title">关联运输信息</h4>
<div class="panel dialog-list-panel">
<div class="list-body list-wrap dialog-list-body-only">
<el-table
<ListTable table-key="business-pl-freight-settlement-view-dialog-2"
:data="settlement.transports || []"
class="list-table"
stripe
@@ -126,7 +126,7 @@
<el-table-column prop="freightAmount" label="运费(元)" min-width="110" align="right">
<template #default="{ row }">{{ formatAmount(row.freightAmount) }}</template>
</el-table-column>
</el-table>
</ListTable>
</div>
</div>
</section>
@@ -1,7 +1,7 @@
<template>
<div class="panel dialog-list-panel pl-plan-product-table-wrap">
<div class="list-body list-wrap dialog-list-body-only">
<el-table :data="products" stripe class="list-table pl-plan-product-table" empty-text="暂无产品">
<ListTable table-key="business-pl-plan-form-dialog-pl-plan-form-product-table" :data="products" stripe class="list-table pl-plan-product-table" empty-text="暂无产品">
<el-table-column label="序号" type="index" min-width="60" align="center" header-align="center" />
<el-table-column label="产品名称" min-width="140" show-overflow-tooltip>
<template #default="{ row }">
@@ -140,7 +140,7 @@
</el-button>
</template>
</el-table-column>
</el-table>
</ListTable>
</div>
</div>
</template>
@@ -23,7 +23,7 @@
<p v-if="!hasDriverFeedback" class="pl-plan-form-empty-hint">暂无司机回传</p>
<template v-else>
<div class="view-subtable-wrap">
<el-table :data="driverFeedbackTableRows" class="view-subtable list-table" stripe>
<ListTable table-key="business-pl-plan-transport-feedback-blocks" :data="driverFeedbackTableRows" class="view-subtable list-table" stripe>
<el-table-column type="index" label="序号" min-width="68" align="center" />
<el-table-column
prop="productNo"
@@ -98,7 +98,7 @@
{{ row.remark || '—' }}
</template>
</el-table-column>
</el-table>
</ListTable>
</div>
<div class="pl-plan-io-site-meta">
<div class="pl-plan-io-site-meta__item">
@@ -148,7 +148,7 @@
<template v-if="ioOrder">
<ViewKvTable :fields="ioOrderViewFields" mode="quint" />
<div class="view-subtable-wrap" style="margin-top: 12px">
<el-table :data="resolvedIoOrderItems" class="view-subtable list-table" stripe>
<ListTable table-key="business-pl-plan-transport-feedback-blocks-2" :data="resolvedIoOrderItems" class="view-subtable list-table" stripe>
<el-table-column type="index" label="序号" min-width="68" align="center" />
<el-table-column
prop="productNo"
@@ -215,7 +215,7 @@
{{ row.remark || '—' }}
</template>
</el-table-column>
</el-table>
</ListTable>
</div>
<div class="pl-plan-io-site-meta">
<div class="pl-plan-io-site-meta__item">
@@ -238,7 +238,7 @@
暂无现场链接
</p>
<div v-else class="view-subtable-wrap">
<el-table :data="siteFeedbackTableRows" class="view-subtable list-table" stripe>
<ListTable table-key="business-pl-plan-transport-feedback-blocks-3" :data="siteFeedbackTableRows" class="view-subtable list-table" stripe>
<el-table-column type="index" label="序号" min-width="68" align="center" />
<el-table-column
prop="productName"
@@ -294,7 +294,7 @@
</el-button>
</template>
</el-table-column>
</el-table>
</ListTable>
</div>
</template>
</section>
@@ -329,7 +329,7 @@
<template v-if="row.weighing">
<h5 class="pl-plan-dibang-weighing-title">地磅单</h5>
<div class="view-subtable-wrap">
<el-table :data="[row.weighing]" class="view-subtable list-table" stripe>
<ListTable table-key="business-pl-plan-transport-feedback-blocks-4" :data="[row.weighing]" class="view-subtable list-table" stripe>
<el-table-column
type="index"
label="序号"
@@ -402,7 +402,7 @@
align="center"
show-overflow-tooltip
/>
</el-table>
</ListTable>
</div>
</template>
<p v-else class="pl-plan-form-empty-hint pl-plan-dibang-pending">尚未关联地磅单</p>
@@ -48,7 +48,7 @@
</div>
<div class="list-body list-wrap dialog-list-body-only">
<el-table
<ListTable table-key="business-pl-transport-address-select-dialog"
class="list-table pl-transport-address-select-table"
:data="rows"
v-loading="loading"
@@ -107,7 +107,7 @@
</el-button>
</template>
</el-table-column>
</el-table>
</ListTable>
<ListPagination
v-show="pagination.total > 0"
@@ -15,7 +15,14 @@
:key="item.id"
:label="item.licensePlate"
:value="item.licensePlate"
/>
>
<div class="pl-vehicle-option">
<span>{{ item.licensePlate }}</span>
<span v-if="vehicleTypeLabel(item)" class="pl-vehicle-option__meta">{{
vehicleTypeLabel(item)
}}</span>
</div>
</el-option>
</el-select>
</template>
@@ -32,17 +39,29 @@
placeholder?: string
}>(),
{
placeholder: '请选择车牌号'
placeholder: '请选择车牌号(三方/自有车辆)'
}
)
const loading = ref(false)
const options = ref<Vehicle[]>([])
const vehicleTypeLabel = (item: Vehicle): string => {
if (item.id?.startsWith('legacy-')) return '历史'
if (item.vehicleType === 'self') return '自有'
if (item.vehicleType === 'carrier') return '三方'
return ''
}
/** 三方车辆 + 山峡自有车辆,禁止手填新建 */
const loadOptions = async (keyword: string) => {
loading.value = true
try {
const res = await vehicleApi.list(keyword.trim() ? { q: keyword.trim() } : undefined)
const res = await vehicleApi.list({
...(keyword.trim() ? { q: keyword.trim() } : {}),
page: 1,
pageSize: 100
})
options.value = pickBxList<Vehicle>(res)
} catch (error) {
console.error('加载运输车辆列表失败', error)
@@ -58,7 +77,7 @@
}
}
/** 编辑态回显:历史出入库单中的车牌若不在运输车辆库,仍展示只读选项 */
/** 编辑态回显:历史出入库单中的车牌若不在车辆库,仍展示只读选项 */
const displayOptions = computed(() => {
const current = model.value?.trim()
if (!current) return options.value
@@ -77,3 +96,19 @@
]
})
</script>
<style scoped lang="scss">
.pl-vehicle-option {
display: flex;
justify-content: space-between;
align-items: center;
gap: 12px;
min-width: 0;
}
.pl-vehicle-option__meta {
flex-shrink: 0;
font-size: 12px;
color: var(--text-muted);
}
</style>
@@ -0,0 +1,138 @@
import { unref, type Ref } from 'vue'
import { useUserStore } from '@/store/modules/user'
export type ListColumnWidthMap = Record<string, number>
/** 与 usePersistedListColumns 的特殊列命名保持一致,便于同一套 key 习惯 */
export function resolveListColumnKey(column: {
property?: string
prop?: string
type?: string
id?: string
label?: string
}): string | null {
const prop = column.property || column.prop
if (prop) return String(prop)
if (column.type === 'selection') return '__selection__'
if (column.type === 'index') return '__index__'
if (column.type === 'expand') return '__expand__'
if (column.id) return String(column.id)
if (column.label) return `__label__:${column.label}`
return null
}
function buildStorageKey(tableKey: string, userId?: string | number | null): string {
const uid = userId != null && String(userId).trim() !== '' ? String(userId) : 'anonymous'
return `list_col_widths:${uid}:${tableKey}`
}
function loadWidths(storageKey: string): ListColumnWidthMap {
if (typeof localStorage === 'undefined') return {}
const raw = localStorage.getItem(storageKey)
if (!raw) return {}
try {
const parsed = JSON.parse(raw)
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return {}
const next: ListColumnWidthMap = {}
for (const [key, value] of Object.entries(parsed)) {
const n = Number(value)
if (key && Number.isFinite(n) && n > 0) {
next[key] = Math.round(n)
}
}
return next
} catch {
return {}
}
}
function persistWidths(storageKey: string, widths: ListColumnWidthMap) {
if (typeof localStorage === 'undefined') return
try {
localStorage.setItem(storageKey, JSON.stringify(widths))
} catch {
// ignore quota / private mode
}
}
function getTableColumns(table: any): any[] {
if (!table) return []
const cols = table.store?.states?.columns
const list = unref(cols)
return Array.isArray(list) ? list : []
}
/**
* 列表列宽 localStorage 持久化(按用户 + tableKey)。
* 配合 ListTable:拖拽结束写入,挂载后写回 Element Plus 内部列宽。
*/
export function usePersistedListColumnWidths(tableKey: string | Ref<string>) {
const userStore = useUserStore()
const resolveKey = () => {
const key = String(unref(tableKey) || '').trim()
const userId = userStore.getUserInfo?.userId
return key ? buildStorageKey(key, userId) : ''
}
const getWidths = (): ListColumnWidthMap => {
const storageKey = resolveKey()
return storageKey ? loadWidths(storageKey) : {}
}
const setWidth = (columnKey: string, width: number) => {
const storageKey = resolveKey()
if (!storageKey || !columnKey) return
const n = Math.round(Number(width))
if (!Number.isFinite(n) || n <= 0) return
const next = { ...loadWidths(storageKey), [columnKey]: n }
persistWidths(storageKey, next)
}
const resetWidths = () => {
const storageKey = resolveKey()
if (!storageKey || typeof localStorage === 'undefined') return
try {
localStorage.removeItem(storageKey)
} catch {
// ignore
}
}
/**
* 将已存宽度写回表格列并 relayout。
* @returns 是否应用了至少一列
*/
const applyToTable = (table: any): boolean => {
const widths = getWidths()
const keys = Object.keys(widths)
if (!keys.length || !table) return false
const columns = getTableColumns(table)
if (!columns.length) return false
let applied = false
for (const col of columns) {
const colKey = resolveListColumnKey(col)
if (!colKey) continue
const w = widths[colKey]
if (w == null) continue
col.width = w
col.realWidth = w
applied = true
}
if (applied && typeof table.doLayout === 'function') {
table.doLayout()
}
return applied
}
return {
getWidths,
setWidth,
resetWidths,
applyToTable,
resolveListColumnKey
}
}
+3
View File
@@ -3,6 +3,7 @@ import App from './App.vue'
import { createApp } from 'vue'
import { ElTable } from 'element-plus'
import EmptyAwareElTag from './utils/patch-el-tag-empty'
import ListTable from '@/components/business/list-table/index.vue'
ElTable.props.border = { type: Boolean, default: true }
@@ -52,6 +53,8 @@ logApiCryptoBootInfo()
const app = createApp(App)
app.component('ElTag', EmptyAwareElTag)
// 列表列宽拖拽持久化:全局注册,避免按需解析失败导致部分页仍是裸标签
app.component('ListTable', ListTable)
initStore(app)
initRouter(app)
setupGlobDirectives(app)
@@ -112,7 +112,7 @@
<div class="list-body list-wrap">
<div class="table-scroll table-page-summary">
<el-table
<ListTable table-key="bx-advance-apply"
class="list-table"
:data="rows"
v-loading="loading"
@@ -236,7 +236,7 @@
<span v-else>-</span>
</template>
</el-table-column>
</el-table>
</ListTable>
</div>
<div class="pagination list-pager-bar">
@@ -347,7 +347,7 @@
</div>
<BaseDetailTableSection title="冲抵记录" :data="viewOffsetRecords" empty-text="暂无冲抵记录">
<el-table class="list-table" :data="viewOffsetRecords" stripe size="small">
<ListTable table-key="bx-advance-apply-2" class="list-table" :data="viewOffsetRecords" stripe size="small">
<el-table-column type="index" label="序号" min-width="68" align="center" header-align="center" />
<el-table-column
prop="reimburseApplyCode"
@@ -390,7 +390,7 @@
header-align="center"
/>
<el-table-column prop="operator" label="操作人" min-width="100" align="center" header-align="center" />
</el-table>
</ListTable>
</BaseDetailTableSection>
<BxApprovalFlowTabs
@@ -86,7 +86,7 @@
<div class="list-body list-wrap">
<div class="table-scroll table-page-summary">
<el-table
<ListTable table-key="bx-advance-approve"
class="list-table"
:data="pendingRows"
v-loading="pendingLoading"
@@ -166,7 +166,7 @@
<span v-else>-</span>
</template>
</el-table-column>
</el-table>
</ListTable>
</div>
<div class="pagination list-pager-bar">
@@ -254,7 +254,7 @@
<div class="list-body list-wrap">
<div class="table-scroll table-page-summary">
<el-table
<ListTable table-key="bx-advance-approve-2"
class="list-table"
:data="approvedRows"
v-loading="approvedLoading"
@@ -346,7 +346,7 @@
<span v-else>-</span>
</template>
</el-table-column>
</el-table>
</ListTable>
</div>
<div class="pagination list-pager-bar">
@@ -85,7 +85,7 @@
<div class="list-body list-wrap">
<div class="table-scroll table-page-summary">
<el-table
<ListTable table-key="bx-advance-issue"
class="list-table"
:data="rows"
v-loading="loading"
@@ -185,7 +185,7 @@
header-align="center"
show-overflow-tooltip
/>
</el-table>
</ListTable>
</div>
<div class="pagination list-pager-bar">
@@ -276,7 +276,7 @@
<div class="list-body list-wrap">
<div class="table-scroll table-page-summary">
<el-table
<ListTable table-key="bx-advance-issue-2"
class="list-table"
:data="rows"
v-loading="loading"
@@ -381,7 +381,7 @@
align="center"
header-align="center"
/>
</el-table>
</ListTable>
</div>
<div class="pagination list-pager-bar">
@@ -488,7 +488,7 @@
</div>
<BaseDetailTableSection title="冲抵流水" :data="offsetRecords" empty-text="暂无冲抵记录">
<el-table class="list-table" :data="offsetRecords" stripe size="small">
<ListTable table-key="bx-advance-issue-3" class="list-table" :data="offsetRecords" stripe size="small">
<el-table-column type="index" label="序号" min-width="68" align="center" header-align="center" />
<el-table-column prop="reimburseApplyCode" label="报销单号" min-width="130" align="center" header-align="center" />
<el-table-column prop="reimburseApplicantName" label="报销申请人" min-width="100" align="center" header-align="center" />
@@ -500,7 +500,7 @@
<el-table-column prop="createTime" label="冲抵时间" min-width="160" align="center" header-align="center" />
<el-table-column prop="operator" label="操作人" min-width="100" align="center" header-align="center" />
<el-table-column prop="remark" label="备注" min-width="140" align="left" header-align="center" show-overflow-tooltip />
</el-table>
</ListTable>
</BaseDetailTableSection>
<div v-if="viewAttachments.length" class="dlg-view-section">
@@ -100,7 +100,7 @@
</div>
<div class="list-body list-wrap">
<el-table
<ListTable table-key="bx-expense-item"
ref="tableRef"
class="list-table"
:data="filteredTree"
@@ -175,7 +175,7 @@
</el-tag>
</template>
</el-table-column>
</el-table>
</ListTable>
</div>
<div class="pagination list-pager-bar">
@@ -48,7 +48,7 @@
</div>
<div class="list-body list-wrap">
<el-table
<ListTable table-key="bx-invoice-wallet-admin"
v-loading="loading"
class="list-table"
:data="rows"
@@ -81,7 +81,7 @@
</el-button>
</template>
</el-table-column>
</el-table>
</ListTable>
<div class="pagination list-pager-bar">
<el-pagination
v-model:current-page="page"
@@ -132,7 +132,7 @@
<el-tabs v-model="detailTab" class="dlg-view-flow-tabs wallet-detail-tabs">
<el-tab-pane label="发票列表" name="invoices">
<p class="detail-tab-tip">删除将冲减总额与余额</p>
<el-table
<ListTable table-key="bx-invoice-wallet-admin-2"
class="list-table"
:data="invoiceRows"
stripe
@@ -168,13 +168,13 @@
</el-button>
</template>
</el-table-column>
</el-table>
</ListTable>
<p v-if="!invoiceRows.length" class="detail-empty-hint">
暂无发票;可通过「代传发票」录入。
</p>
</el-tab-pane>
<el-tab-pane label="额度流水" name="ledgers">
<el-table
<ListTable table-key="bx-invoice-wallet-admin-3"
class="list-table"
:data="ledgerRows"
stripe
@@ -194,7 +194,7 @@
<el-table-column prop="remark" label="备注" min-width="160" show-overflow-tooltip>
<template #default="{ row }">{{ row.remark || '-' }}</template>
</el-table-column>
</el-table>
</ListTable>
<p v-if="!ledgerRows.length" class="detail-empty-hint">暂无额度流水。</p>
</el-tab-pane>
</el-tabs>
@@ -73,7 +73,7 @@
<el-tabs v-model="activeTab" class="wallet-tabs">
<el-tab-pane label="发票列表" name="invoices">
<div class="list-body list-wrap">
<el-table
<ListTable table-key="bx-invoice-wallet-mine"
v-loading="loading"
class="list-table"
:data="filteredInvoiceRows"
@@ -105,12 +105,12 @@
<template #default="{ row }">{{ row.uploadByName || '-' }}</template>
</el-table-column>
<el-table-column prop="createTime" label="上传时间" min-width="140" />
</el-table>
</ListTable>
</div>
</el-tab-pane>
<el-tab-pane label="额度流水" name="ledgers">
<div class="list-body list-wrap">
<el-table v-loading="ledgerLoading" class="list-table" :data="filteredLedgerRows" stripe>
<ListTable table-key="bx-invoice-wallet-mine-2" v-loading="ledgerLoading" class="list-table" :data="filteredLedgerRows" stripe>
<el-table-column type="index" label="序号" min-width="68" align="center" />
<el-table-column prop="createTime" label="时间" min-width="170" />
<el-table-column prop="entryType" label="类型" min-width="140">
@@ -126,7 +126,7 @@
<el-table-column prop="remark" label="备注" min-width="180" show-overflow-tooltip>
<template #default="{ row }">{{ row.remark || '-' }}</template>
</el-table-column>
</el-table>
</ListTable>
</div>
</el-tab-pane>
</el-tabs>
+8 -8
View File
@@ -86,7 +86,7 @@
<div class="list-body list-wrap">
<div class="table-scroll table-page-summary">
<el-table
<ListTable table-key="bx-payment"
class="list-table"
:data="rows"
v-loading="loading"
@@ -177,7 +177,7 @@
}}
</template>
</el-table-column>
</el-table>
</ListTable>
</div>
<div class="pagination list-pager-bar">
@@ -275,7 +275,7 @@
<div class="list-body list-wrap">
<div class="table-scroll table-page-summary">
<el-table
<ListTable table-key="bx-payment-2"
class="list-table"
:data="processedRows"
v-loading="processedLoading"
@@ -389,7 +389,7 @@
{{ row.payTime || '-' }}
</template>
</el-table-column>
</el-table>
</ListTable>
</div>
<div class="pagination list-pager-bar">
@@ -536,7 +536,7 @@
<el-icon><RefreshLeft /></el-icon>重置
</el-button>
</div>
<el-table
<ListTable table-key="bx-payment-3"
class="list-table"
:data="offsetAdvances"
v-loading="offsetAdvanceLoading"
@@ -564,7 +564,7 @@
{{ formatMoney(Number(row.actualAmount || row.amount || 0) - Number(row.offsetAmount || 0)) }}
</template>
</el-table-column>
</el-table>
</ListTable>
<div class="offset-pager">
<el-pagination
v-model:current-page="offsetAdvancePage"
@@ -653,7 +653,7 @@
<h4 class="dlg-view-section-title section-gap-tight">冲抵明细</h4>
<BaseDetailTableSection embedded title="冲抵明细" :data="processedOffsetRecords" empty-text="暂无冲抵明细">
<el-table class="list-table" :data="processedOffsetRecords" stripe size="small">
<ListTable table-key="bx-payment-4" class="list-table" :data="processedOffsetRecords" stripe size="small">
<el-table-column type="index" label="序号" min-width="68" align="center" header-align="center" />
<el-table-column prop="advanceApplyCode" label="备用金单号" min-width="130" align="center" header-align="center" />
<el-table-column prop="advanceApplicantName" label="备用金申请人" min-width="110" align="center" header-align="center" />
@@ -665,7 +665,7 @@
</el-table-column>
<el-table-column prop="createTime" label="冲抵时间" min-width="160" align="center" header-align="center" />
<el-table-column prop="operator" label="操作人" min-width="100" align="center" header-align="center" />
</el-table>
</ListTable>
</BaseDetailTableSection>
</div>
@@ -102,7 +102,7 @@
<div class="list-body list-wrap">
<div class="table-scroll table-page-summary">
<el-table
<ListTable table-key="bx-reimburse-apply"
class="list-table"
:data="rows"
v-loading="loading"
@@ -209,7 +209,7 @@
<span v-else>-</span>
</template>
</el-table-column>
</el-table>
</ListTable>
</div>
<div class="pagination list-pager-bar">
@@ -482,7 +482,7 @@
<div class="dlg-view-section">
<h4 class="dlg-view-section-title">报销明细</h4>
<el-table
<ListTable table-key="bx-reimburse-apply-2"
class="list-table view-detail-table"
:data="viewData.details || []"
stripe
@@ -505,7 +505,7 @@
<el-table-column prop="remark" label="报销事由" min-width="140" align="left" header-align="center">
<template #default="{ row }">{{ row.remark || '-' }}</template>
</el-table-column>
</el-table>
</ListTable>
</div>
<div v-if="viewLinkedInvoiceFiles.length" class="dlg-view-section">
@@ -113,7 +113,7 @@
<div class="list-body list-wrap">
<div class="table-scroll table-page-summary">
<el-table
<ListTable table-key="bx-reimburse-approve"
class="list-table"
:data="pendingRows"
v-loading="pendingLoading"
@@ -214,7 +214,7 @@
<span v-else>-</span>
</template>
</el-table-column>
</el-table>
</ListTable>
</div>
<div class="pagination list-pager-bar">
@@ -329,7 +329,7 @@
<div class="list-body list-wrap">
<div class="table-scroll table-page-summary">
<el-table
<ListTable table-key="bx-reimburse-approve-2"
class="list-table"
:data="approvedRows"
v-loading="approvedLoading"
@@ -442,7 +442,7 @@
<span v-else>-</span>
</template>
</el-table-column>
</el-table>
</ListTable>
</div>
<div class="pagination list-pager-bar">
+2 -2
View File
@@ -89,7 +89,7 @@
</div>
<div class="list-body list-wrap table-page-summary table-page-summary-biz-split">
<el-table
<ListTable table-key="bx-view-all"
class="list-table"
:data="pager.pagedRows.value"
v-loading="loading"
@@ -197,7 +197,7 @@
<span v-else>-</span>
</template>
</el-table-column>
</el-table>
</ListTable>
<div class="pagination list-pager-bar">
<span class="list-range">{{ pager.rangeText.value }}</span>
+2 -2
View File
@@ -82,7 +82,7 @@
<div class="list-body list-wrap">
<div class="table-scroll table-page-summary table-page-summary-biz-split">
<el-table
<ListTable table-key="bx-view-mine"
class="list-table"
:data="pagedRows"
v-loading="tableLoading"
@@ -181,7 +181,7 @@
<span v-else>-</span>
</template>
</el-table-column>
</el-table>
</ListTable>
</div>
<div class="pagination list-pager-bar">
+2 -2
View File
@@ -88,7 +88,7 @@
<div class="list-body list-wrap">
<div class="table-scroll table-page-summary">
<el-table
<ListTable table-key="bx-view-todo"
class="list-table"
:data="pagedRows"
v-loading="tableLoading"
@@ -182,7 +182,7 @@
<span v-else>-</span>
</template>
</el-table-column>
</el-table>
</ListTable>
</div>
<div class="pagination list-pager-bar">
@@ -68,7 +68,7 @@
</div>
<div class="list-body list-wrap">
<el-table
<ListTable table-key="bx-workflow-advance"
class="list-table"
:data="filteredNodes"
v-loading="loading"
@@ -116,7 +116,7 @@
</el-tag>
</template>
</el-table-column>
</el-table>
</ListTable>
</div>
<div class="pagination list-pager-bar">
@@ -68,7 +68,7 @@
</div>
<div class="list-body list-wrap">
<el-table
<ListTable table-key="bx-workflow-reimburse"
class="list-table"
:data="filteredNodes"
v-loading="loading"
@@ -116,7 +116,7 @@
</el-tag>
</template>
</el-table-column>
</el-table>
</ListTable>
</div>
<div class="pagination list-pager-bar">
+2 -2
View File
@@ -202,7 +202,7 @@
</div>
<div class="list-body list-wrap">
<el-table
<ListTable table-key="crm-clue"
class="list-table"
:data="clueList"
v-loading="loading"
@@ -395,7 +395,7 @@
header-align="center"
show-overflow-tooltip
/>
</el-table>
</ListTable>
<div class="pagination list-pager-bar">
<div class="list-pager-selected">
+2 -2
View File
@@ -76,7 +76,7 @@
<section v-else class="invalid-dialog-section">
<h3 class="invalid-section-title">待标记线索({{ batchClues.length }} 条)</h3>
<el-table :data="batchClues" class="list-table invalid-batch-table" stripe max-height="240">
<ListTable table-key="crm-clue-invalid-form" :data="batchClues" class="list-table invalid-batch-table" stripe max-height="240">
<el-table-column type="index" label="序号" min-width="68" align="center" header-align="center" />
<el-table-column prop="clueName" label="线索姓名" min-width="120" show-overflow-tooltip />
<el-table-column prop="company" label="公司名称" min-width="140" show-overflow-tooltip />
@@ -93,7 +93,7 @@
<span v-else>—</span>
</template>
</el-table-column>
</el-table>
</ListTable>
</section>
<section class="invalid-dialog-section">
@@ -69,7 +69,7 @@
<span class="chart-title">销售跟进排行榜</span>
</div>
<div class="list-body list-wrap">
<el-table class="list-table" :data="ownerData" stripe max-height="400">
<ListTable table-key="crm-clue-statistics" class="list-table" :data="ownerData" stripe max-height="400">
<el-table-column type="index" label="排名" min-width="70" align="center">
<template #default="scope">
<el-tag v-if="scope.$index === 0" type="danger" size="small">1</el-tag>
@@ -94,7 +94,7 @@
{{ Number(scope.row.avgFollowCount || 0).toFixed(1) }}次
</template>
</el-table-column>
</el-table>
</ListTable>
</div>
</div>
<div class="chart-panel">
@@ -95,7 +95,7 @@
</div>
<div class="list-body list-wrap">
<el-table
<ListTable table-key="crm-contract-approval"
class="list-table"
v-loading="loading"
:data="approvalList"
@@ -171,7 +171,7 @@
<el-table-column label="审批时间" v-if="isColumnVisible('approvalTime')" min-width="160" align="center" header-align="center">
<template #default="{ row }">{{ parseTime(row.approvalTime) }}</template>
</el-table-column>
</el-table>
</ListTable>
<div class="pagination list-pager-bar">
<el-pagination
@@ -144,7 +144,7 @@
</div>
<div class="list-body list-wrap">
<el-table
<ListTable table-key="crm-contract-change"
class="list-table"
v-loading="loading"
:data="changeList"
@@ -235,7 +235,7 @@
<el-table-column label="创建时间" v-if="isColumnVisible('createTime')" min-width="160" align="center" header-align="center">
<template #default="{ row }">{{ parseTime(row.createTime) }}</template>
</el-table-column>
</el-table>
</ListTable>
<div class="pagination list-pager-bar">
<div class="list-pager-selected">
@@ -176,7 +176,7 @@
<el-divider content-position="left">产品明细</el-divider>
<div class="panel dialog-list-panel mb-4">
<div class="list-body list-wrap dialog-list-body-only">
<el-table :data="detailContract.products" class="list-table" stripe empty-text="暂无产品明细">
<ListTable table-key="crm-contract-components-contract-detail-dialog" :data="detailContract.products" class="list-table" stripe empty-text="暂无产品明细">
<el-table-column label="序号" type="index" min-width="60" align="center" header-align="center" />
<el-table-column label="产品名称" min-width="120" :show-overflow-tooltip="true">
<template #default="{ row }">
@@ -261,7 +261,7 @@
<span>{{ scope.row.remark || '-' }}</span>
</template>
</el-table-column>
</el-table>
</ListTable>
</div>
</div>
@@ -281,7 +281,7 @@
</div>
<div class="panel dialog-list-panel mb-3">
<div class="list-body list-wrap dialog-list-body-only">
<el-table :data="prod.priceTiers || []" class="list-table" stripe empty-text="暂无调价记录">
<ListTable table-key="crm-contract-components-contract-detail-dialog-2" :data="prod.priceTiers || []" class="list-table" stripe empty-text="暂无调价记录">
<el-table-column type="index" label="#" min-width="52" align="center" header-align="center" />
<el-table-column label="开始日期" prop="startDate" min-width="120" align="center" />
<el-table-column label="结束日期" prop="endDate" min-width="120" align="center" />
@@ -307,7 +307,7 @@
<span v-else class="text-muted">-</span>
</template>
</el-table-column>
</el-table>
</ListTable>
</div>
</div>
</div>
@@ -406,7 +406,7 @@
</div>
</div>
<div class="list-body list-wrap">
<el-table
<ListTable table-key="crm-contract-components-contract-detail-dialog-3"
:data="detailPaymentList"
class="list-table"
stripe
@@ -436,7 +436,7 @@
<el-table-column label="创建时间" prop="createTime" width="160" align="center">
<template #default="scope">{{ parseTime(scope.row.createTime) }}</template>
</el-table-column>
</el-table>
</ListTable>
</div>
</div>
</el-tab-pane>
@@ -454,7 +454,7 @@
</div>
</div>
<div class="list-body list-wrap">
<el-table
<ListTable table-key="crm-contract-components-contract-detail-dialog-4"
:data="detailInvoiceList"
class="list-table"
stripe
@@ -484,7 +484,7 @@
<el-table-column label="创建时间" prop="createTime" width="160" align="center">
<template #default="scope">{{ parseTime(scope.row.createTime) }}</template>
</el-table-column>
</el-table>
</ListTable>
</div>
</div>
</el-tab-pane>
@@ -1670,6 +1670,10 @@ const fetchQuoteFreight = async (quoteId?: number): Promise<number> => {
}
};
/** 产品明细中是否含「自提」送货方式(字典 crm_shfs) */
const hasSelfPickupProduct = (products?: Array<{ deliveryMethod?: string }> | null): boolean =>
!!products?.some((p) => String(p.deliveryMethod || '').trim() === '自提');
/** 替换模板中的占位符为数据库最新数据 */
const replacePlaceholders = (htmlContent: string, contractData: ContractVO | undefined, quoteFreight = 0): string => {
if (!htmlContent || !contractData) return htmlContent;
@@ -1929,6 +1933,9 @@ const replacePlaceholders = (htmlContent: string, contractData: ContractVO | und
const totalWithFreightChinese = numberToChinese(totalWithFreight);
const freightText = freightAmount > 0 ? `¥${formatNumber(freightAmount)}` : '-';
const totalWithFreightText = isFrameworkContract ? '随行就市' : `${totalWithFreightChinese}(¥${formatNumber(totalWithFreight)})`;
const tipRowText = hasSelfPickupProduct(contractData.products)
? '含税、客户自提结算以实际到货吨位为准'
: '含税、含运费、装卸费(一票制)&nbsp;&nbsp;&nbsp;&nbsp;结算以实际到货吨位为准';
const productTotalRow = `
<tr style="background-color: #fff; font-weight: bold;">
<td colspan="2" style="padding: 5px 6px; border: 0.3px solid #000; text-align: center; font-size: 12px;">运费</td>
@@ -1938,7 +1945,7 @@ const replacePlaceholders = (htmlContent: string, contractData: ContractVO | und
</tr>
<tr>
<td colspan="11" style="padding: 5px 6px; border: 0.3px solid #000; text-align: center; font-size: 11px; background-color: #fff;">
含税、含运费、装卸费(一票制)&nbsp;&nbsp;&nbsp;&nbsp;结算以实际到货吨位为准
${tipRowText}
</td>
</tr>
`;
@@ -2068,6 +2075,14 @@ const generateContractTemplate = (contractData: ContractVO | undefined): string
const 第五条Content = '{{SETTLEMENT_METHOD_CONTENT}}'; // 占位符
// 注意:实际内容在打印/生成PDF时通过 replacePlaceholders 动态替换
// 第三条:任一产品送货方式为「自提」时使用客户自提条款(仅新生成模板写入)
const isSelfPickup = hasSelfPickupProduct(contractData.products);
const article3Content = isSelfPickup
? `<div style="margin: 0;">1、交货方式:需方自提,供方需提供需方要求产品。</div>
<div style="margin: 0;">2、运输:在运输过程中发生的一切费用由需方承担,到达本公司卸货过程中所发生的一些费用由需方承担。</div>`
: `<div style="margin: 0;">1、交货方式:供方送货,供方需提供需方要求产品,送达至需方要求地点。</div>
<div style="margin: 0;">2、运输:在运输过程中发生的一切费用由供方承担,到达本公司卸货过程中所发生的一些费用由供方承担。</div>`;
// 数字转中文大写
const numberToChinese = (num: number): string => {
const digits = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'];
@@ -2314,8 +2329,7 @@ const generateContractTemplate = (contractData: ContractVO | undefined): string
<div style="margin-bottom: 5px; line-height: 1.5; font-size: 12px;">
<div style="font-weight: bold; margin-bottom: 0; color: #000;">第三条、交货方式及运输方式</div>
<div style="line-height: 1.8;">
<div style="margin: 0;">1、交货方式:供方送货,供方需提供需方要求产品,送达至需方要求地点。</div>
<div style="margin: 0;">2、运输:在运输过程中发生的一切费用由供方承担,到达本公司卸货过程中所发生的一些费用由供方承担。</div>
${article3Content}
</div>
</div>
@@ -2441,8 +2455,10 @@ const handleSaveTemplate = async () => {
const totalRowMatch = templateWithPlaceholders.match(/<tr style="background-color: #fff; font-weight: bold;">[\s\S]*?<\/tr>/);
const totalRowHtml = totalRowMatch ? totalRowMatch[0] : '';
// 3. 提取提示行
const tipRowMatch = templateWithPlaceholders.match(/<tr>[\s\S]*?<td colspan="10"[\s\S]*?结算以实际到货吨位为准[\s\S]*?<\/td>[\s\S]*?<\/tr>/);
// 3. 提取提示行(兼容一票制 / 客户自提文案,以及 colspan 10/11)
const tipRowMatch = templateWithPlaceholders.match(
/<tr>[\s\S]*?<td colspan="1[01]"[\s\S]*?结算以实际到货吨位为准[\s\S]*?<\/td>[\s\S]*?<\/tr>/
);
const tipRowHtml = tipRowMatch ? tipRowMatch[0] : '';
// 替换产品表格部分
@@ -2462,9 +2478,10 @@ const handleSaveTemplate = async () => {
}
}
// 替换提示行
// 替换提示行(兼容旧一票制与客户自提提示)
if (tipRowHtml) {
const tipRowPattern = /<tr>[\s\S]*?<td colspan="10"[\s\S]*?>含税、含运费[\s\S]*?<\/tr>/;
const tipRowPattern =
/<tr>[\s\S]*?<td colspan="1[01]"[\s\S]*?>含税、(?:含运费|客户自提)[\s\S]*?<\/tr>/;
if (tipRowPattern.test(finalTemplate)) {
finalTemplate = finalTemplate.replace(tipRowPattern, tipRowHtml);
}
@@ -1178,11 +1178,6 @@ const handleQuoteConfirm = (quote: QuoteVO) => {
form.value.quoteId = quote.id
form.value.quoteNo = quote.quoteNo
if (quote.quoteDate) {
form.value.signDate = quote.quoteDate
handleSignDateChange(quote.quoteDate)
}
if (quote.customerId && !form.value.customerId) {
form.value.customerId = quote.customerId
form.value.customerName = quote.customerName || ''
@@ -58,7 +58,7 @@
<!-- 产品列表 -->
<div class="panel dialog-list-panel">
<div class="list-body list-wrap dialog-list-body-only">
<el-table
<ListTable table-key="crm-contract-components-product-selector"
ref="tableRef"
v-loading="loading"
:data="productList"
@@ -98,7 +98,7 @@
</el-tag>
</template>
</el-table-column>
</el-table>
</ListTable>
<pagination
v-show="total > 0"
+21 -4
View File
@@ -1400,6 +1400,10 @@ const extractSettlementClauseContent = (htmlContent: string): string => {
return content.replace(/\u00a0/g, ' ').trim();
};
/** 产品明细中是否含「自提」送货方式(字典 crm_shfs) */
const hasSelfPickupProduct = (products?: Array<{ deliveryMethod?: string }> | null): boolean =>
!!products?.some((p) => String(p.deliveryMethod || '').trim() === '自提');
/**
* 替换模板中的占位符为数据库最新数据
* 用于每次打开富文本时动态更新数据
@@ -1618,6 +1622,9 @@ const replacePlaceholders = (htmlContent: string, contractData: ContractVO | und
}
// 替换产品合计行
const tipRowText = hasSelfPickupProduct(contractData.products)
? '含税、客户自提结算以实际到货吨位为准'
: '含税、含运费、装卸费(一票制)&nbsp;&nbsp;&nbsp;&nbsp;结算以实际到货吨位为准';
const productTotalRow = `
<tr style="background-color: #fff; font-weight: bold;">
<td colspan="4" style="padding: 5px 6px; border: 1px solid #000; text-align: center; font-size: 12px;">合计(人民币)</td>
@@ -1625,7 +1632,7 @@ const replacePlaceholders = (htmlContent: string, contractData: ContractVO | und
</tr>
<tr>
<td colspan="11" style="padding: 5px 6px; border: 1px solid #000; text-align: center; font-size: 11px; background-color: #fff;">
含税、含运费、装卸费(一票制)&nbsp;&nbsp;&nbsp;&nbsp;结算以实际到货吨位为准
${tipRowText}
</td>
</tr>
`;
@@ -1729,6 +1736,17 @@ const generateContractTemplate = (contractData: ContractVO | undefined): string
const 第五条Content = getSettlementClauseContent(contractData);
// 第三条 / 表格提示行:任一产品送货方式为「自提」时使用客户自提文案(仅新生成模板写入)
const isSelfPickup = hasSelfPickupProduct(contractData.products);
const tipRowText = isSelfPickup
? '含税、客户自提结算以实际到货吨位为准'
: '含税、含运费、装卸费(一票制)&nbsp;&nbsp;&nbsp;&nbsp;结算以实际到货吨位为准';
const article3Content = isSelfPickup
? `<div style="margin: 0;">1、交货方式:需方自提,供方需提供需方要求产品。</div>
<div style="margin: 0;">2、运输:在运输过程中发生的一切费用由需方承担,到达本公司卸货过程中所发生的一些费用由需方承担。</div>`
: `<div style="margin: 0;">1、交货方式:供方送货,供方需提供需方要求产品,送达至需方要求地点。</div>
<div style="margin: 0;">2、运输:在运输过程中发生的一切费用由供方承担,到达本公司卸货过程中所发生的一些费用由供方承担。</div>`;
// 数字转中文大写
const numberToChinese = (num: number): string => {
const digits = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'];
@@ -1915,7 +1933,7 @@ const generateContractTemplate = (contractData: ContractVO | undefined): string
<!-- 提示行 -->
<tr>
<td colspan="10" style="padding: 5px 6px; border: 1px solid #000; text-align: center; font-size: 11px; background-color: #fff;">
含税、含运费、装卸费(一票制)&nbsp;&nbsp;&nbsp;&nbsp;结算以实际到货吨位为准
${tipRowText}
</td>
</tr>
</tbody>
@@ -1935,8 +1953,7 @@ const generateContractTemplate = (contractData: ContractVO | undefined): string
<div style="margin-bottom: 5px; line-height: 1.5; font-size: 12px;">
<div style="font-weight: bold; margin-bottom: 0; color: #000;">第三条、交货方式及运输方式</div>
<div style="padding-left: 20px; line-height: 1.5;">
<div style="margin: 0;">1、交货方式:供方送货,供方需提供需方要求产品,送达至需方要求地点。</div>
<div style="margin: 0;">2、运输:在运输过程中发生的一切费用由供方承担,到达本公司卸货过程中所发生的一些费用由供方承担。</div>
${article3Content}
</div>
</div>
@@ -1149,13 +1149,6 @@ const handleQuoteConfirm = (quote: QuoteVO) => {
form.value.quoteId = quote.id;
form.value.quoteNo = quote.quoteNo;
// 如果报价单有报价日期,自动带出到签订日期
if (quote.quoteDate) {
form.value.signDate = quote.quoteDate;
// 同步更新合同期限
handleSignDateChange(quote.quoteDate);
}
// 自动带出客户信息
if (quote.customerId && !form.value.customerId) {
form.value.customerId = quote.customerId;
+4 -4
View File
@@ -178,7 +178,7 @@
</div>
<div class="list-body list-wrap"> <!-- 操作按钮栏 + 数据表格 -->
<el-table
<ListTable table-key="crm-contract"
v-loading="loading"
class="list-table"
stripe
@@ -191,7 +191,7 @@
<el-table-column type="expand" width="55">
<template #default="scope">
<div class="contract-product-expand" v-loading="expandLoadingMap[String(scope.row.id)]">
<el-table :data="scope.row.products || []" stripe class="list-table">
<ListTable table-key="crm-contract-2" :data="scope.row.products || []" stripe class="list-table">
<el-table-column label="序号" type="index" width="60" align="center" />
<el-table-column
label="产品名称"
@@ -249,7 +249,7 @@
<span>{{ productScope.row.remark || '-' }}</span>
</template>
</el-table-column>
</el-table>
</ListTable>
</div>
</template>
</el-table-column>
@@ -439,7 +439,7 @@
show-overflow-tooltip
/>
<el-table-column v-if="isColumnVisible('remark')" label="备注" align="center" prop="remark" min-width="200" show-overflow-tooltip />
</el-table>
</ListTable>
<div class="pagination list-pager-bar">
<div class="list-pager-selected">
@@ -0,0 +1,36 @@
/** 供方 / 需方字段:空字符串也需提交,以便清空落库(调换后一侧为空时不能被剥掉) */
export const CONTRACT_PARTY_FIELD_KEYS = [
'partyA',
'partyAAddress',
'partyAContact',
'partyAMobile',
'partyALegalPerson',
'partyAAgent',
'partyAPhoneFax',
'partyABank',
'partyAAccount',
'partyB',
'partyBAddress',
'partyBContact',
'partyBMobile',
'partyBLegalPerson',
'partyBAgent',
'partyBPhoneFax',
'partyBBank',
'partyBAccount'
] as const
const partyFieldSet = new Set<string>(CONTRACT_PARTY_FIELD_KEYS)
/** 清理 undefined / 空字符串,但保留供需方空字符串以便后端原样写入 */
export function omitEmptyExceptPartyFields(data: Record<string, any>) {
Object.keys(data).forEach((key) => {
if (data[key] === undefined) {
delete data[key]
return
}
if (data[key] === '' && !partyFieldSet.has(key)) {
delete data[key]
}
})
}
@@ -100,7 +100,7 @@
</div>
<div class="list-body list-wrap">
<el-table
<ListTable table-key="crm-contract-template"
class="list-table"
v-loading="loading"
:data="templateList"
@@ -180,7 +180,7 @@
{{ parseTime(row.createTime) }}
</template>
</el-table-column>
</el-table>
</ListTable>
<div class="pagination list-pager-bar">
<div class="list-pager-selected">
@@ -120,7 +120,7 @@
</div>
</div>
<div class="list-body list-wrap">
<el-table
<ListTable table-key="crm-customer-customer-detail-dialog"
v-loading="contactLoading"
:data="contactList"
class="list-table"
@@ -205,7 +205,7 @@
</el-button>
</template>
</el-table-column>
</el-table>
</ListTable>
</div>
</div>
</el-tab-pane>
@@ -213,7 +213,7 @@
<el-tab-pane label="商机" name="opportunity">
<div class="panel dialog-list-panel">
<div class="list-body list-wrap dialog-list-body-only">
<el-table
<ListTable table-key="crm-customer-customer-detail-dialog-2"
v-loading="opportunityLoading"
:data="opportunityList"
class="list-table"
@@ -267,7 +267,7 @@
align="center"
header-align="center"
/>
</el-table>
</ListTable>
</div>
</div>
</el-tab-pane>
@@ -275,7 +275,7 @@
<el-tab-pane label="合同" name="contract">
<div class="panel dialog-list-panel">
<div class="list-body list-wrap dialog-list-body-only">
<el-table
<ListTable table-key="crm-customer-customer-detail-dialog-3"
v-loading="contractLoading"
:data="contractList"
class="list-table"
@@ -342,7 +342,7 @@
align="center"
header-align="center"
/>
</el-table>
</ListTable>
</div>
</div>
</el-tab-pane>
@@ -354,7 +354,7 @@
<el-tab-pane label="操作日志" name="log">
<div class="panel dialog-list-panel">
<div class="list-body list-wrap dialog-list-body-only">
<el-table
<ListTable table-key="crm-customer-customer-detail-dialog-4"
v-loading="logLoading"
:data="operLogList"
class="list-table"
@@ -424,7 +424,7 @@
</el-button>
</template>
</el-table-column>
</el-table>
</ListTable>
<div v-if="logTotal > 0" class="pagination list-pager-bar">
<el-pagination
v-model:current-page="logQuery.pageNum"
@@ -13,7 +13,7 @@
</div>
</div>
<el-table v-loading="loading" :data="contactList" stripe class="list-table">
<ListTable table-key="crm-customer-components-contact-list" v-loading="loading" :data="contactList" stripe class="list-table">
<el-table-column type="index" label="序号" min-width="68" align="center" header-align="center" />
<el-table-column label="姓名" prop="contactName" min-width="120" align="center">
<template #default="{ row }">
@@ -87,7 +87,7 @@
</el-button>
</template>
</el-table-column>
</el-table>
</ListTable>
<ContactFormDialog ref="contactFormRef" @success="getList" />
</div>
@@ -11,7 +11,7 @@
</div>
<div class="list-body list-wrap">
<el-table
<ListTable table-key="crm-customer-components-invoice-info"
:data="invoiceInfoList"
v-loading="loading"
stripe
@@ -111,7 +111,7 @@
</el-button>
</template>
</el-table-column>
</el-table>
</ListTable>
</div>
<el-dialog :title="dialog.title" v-model="dialog.visible" width="800px" class="dialog-form-layout" align-center append-to-body @close="handleClose">
+2 -2
View File
@@ -179,7 +179,7 @@
<!-- 数据表格 -->
<div class="list-body list-wrap">
<el-table
<ListTable table-key="crm-customer"
class="list-table"
:data="customerList"
v-loading="loading"
@@ -610,7 +610,7 @@
header-align="center"
show-overflow-tooltip
/>
</el-table>
</ListTable>
<div class="pagination list-pager-bar">
<div class="list-pager-selected">
已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
@@ -110,7 +110,7 @@
<!-- 数据表格 -->
<div class="list-body list-wrap">
<el-table
<ListTable table-key="crm-customer-pool-rule-index"
class="list-table"
:data="ruleList"
v-loading="loading"
@@ -205,7 +205,7 @@
header-align="center"
show-overflow-tooltip
/>
</el-table>
</ListTable>
<div class="pagination list-pager-bar">
<div class="list-pager-selected">
已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
@@ -89,7 +89,7 @@
<!-- 数据表格 -->
<div class="list-body list-wrap">
<el-table
<ListTable table-key="crm-customer-pool"
class="list-table"
:data="customerList"
v-loading="loading"
@@ -501,7 +501,7 @@
header-align="center"
show-overflow-tooltip
/>
</el-table>
</ListTable>
<div class="pagination list-pager-bar">
<div class="list-pager-selected">
已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
@@ -91,7 +91,7 @@
<span class="chart-title">阶段转化明细</span>
</div>
<div class="list-body list-wrap">
<el-table class="list-table" :data="funnelData" stripe>
<ListTable table-key="crm-opportunity-funnel" class="list-table" :data="funnelData" stripe>
<el-table-column type="index" label="序号" min-width="68" align="center" header-align="center" />
<el-table-column label="商机阶段" prop="stageName" min-width="120" align="center" />
<el-table-column label="商机数量" prop="count" min-width="100" align="center">
@@ -113,7 +113,7 @@
/>
</template>
</el-table-column>
</el-table>
</ListTable>
</div>
</div>
</section>
@@ -152,7 +152,7 @@
</div>
<div class="list-body list-wrap">
<el-table
<ListTable table-key="crm-opportunity"
v-loading="loading"
class="list-table"
stripe
@@ -447,7 +447,7 @@
min-width="200"
show-overflow-tooltip
/>
</el-table>
</ListTable>
<div class="pagination list-pager-bar">
<div class="list-pager-selected">
@@ -101,7 +101,7 @@
<span class="chart-title">最高预估金额商机 Top 10</span>
</div>
<div class="list-body list-wrap">
<el-table class="list-table" :data="topAmountList" stripe max-height="400">
<ListTable table-key="crm-opportunity-statistics" class="list-table" :data="topAmountList" stripe max-height="400">
<el-table-column type="index" label="排名" min-width="70" align="center" />
<el-table-column
prop="opportunityName"
@@ -125,7 +125,7 @@
<template #default="scope">¥{{ formatMoney(scope.row.amount) }}</template>
</el-table-column>
<el-table-column prop="ownerUserName" label="负责人" min-width="100" align="center" />
</el-table>
</ListTable>
</div>
</div>
<div class="chart-panel">
@@ -133,7 +133,7 @@
<span class="chart-title">最高跟进次数商机 Top 10</span>
</div>
<div class="list-body list-wrap">
<el-table class="list-table" :data="topFollowList" stripe max-height="400">
<ListTable table-key="crm-opportunity-statistics-2" class="list-table" :data="topFollowList" stripe max-height="400">
<el-table-column type="index" label="排名" min-width="70" align="center" />
<el-table-column
prop="opportunityName"
@@ -159,7 +159,7 @@
</template>
</el-table-column>
<el-table-column prop="ownerUserName" label="负责人" min-width="100" align="center" />
</el-table>
</ListTable>
</div>
</div>
</div>
@@ -137,7 +137,7 @@
</div>
<div class="list-body list-wrap">
<el-table
<ListTable table-key="crm-price-letter"
class="list-table"
:data="tableData"
v-loading="loading"
@@ -243,7 +243,7 @@
align="center"
header-align="center"
/>
</el-table>
</ListTable>
<div class="pagination list-pager-bar">
<div class="list-pager-selected">
@@ -100,7 +100,7 @@
<!-- 树形表格 -->
<div class="list-body list-wrap">
<el-table
<ListTable table-key="crm-product-category"
v-if="refreshTable"
ref="tableRef"
class="list-table"
@@ -158,7 +158,7 @@
header-align="center"
show-overflow-tooltip
/>
</el-table>
</ListTable>
</div>
<div class="pagination list-pager-bar">
+2 -2
View File
@@ -124,7 +124,7 @@
<!-- 数据表格 -->
<div class="list-body list-wrap">
<el-table
<ListTable table-key="crm-product"
class="list-table"
:data="productList"
v-loading="loading"
@@ -265,7 +265,7 @@
</el-tag>
</template>
</el-table-column>
</el-table>
</ListTable>
</div>
<!-- 分页 -->
@@ -58,7 +58,7 @@
<!-- 产品列表 -->
<div class="panel dialog-list-panel">
<div class="list-body list-wrap dialog-list-body-only">
<el-table
<ListTable table-key="crm-quote-components-product-selector"
ref="tableRef"
v-loading="loading"
:data="productList"
@@ -98,7 +98,7 @@
</el-tag>
</template>
</el-table-column>
</el-table>
</ListTable>
<ListPagination
v-show="total > 0"
@@ -66,7 +66,7 @@
<template #products>
<div class="panel dialog-list-panel">
<div class="list-body list-wrap dialog-list-body-only">
<el-table
<ListTable table-key="crm-quote-components-quote-detail-dialog"
:data="quoteInfo.productList || []"
class="list-table"
stripe
@@ -148,7 +148,7 @@
<span>{{ row.deliveryMethod || '—' }}</span>
</template>
</el-table-column>
</el-table>
</ListTable>
</div>
</div>
<div v-if="(quoteInfo.productList?.length || 0) > 0" class="quote-product-summary">
@@ -119,7 +119,7 @@
</div>
<div class="list-body list-wrap">
<el-table
<ListTable table-key="crm-quote-components-quote-form-panel"
:data="form.products"
class="list-table"
stripe
@@ -270,7 +270,7 @@
</el-button>
</template>
</el-table-column>
</el-table>
</ListTable>
</div>
</div>
+2 -2
View File
@@ -82,7 +82,7 @@
<!-- 产品明细 -->
<el-divider content-position="left">产品明细</el-divider>
<el-table :data="quote?.productList" class="list-table" stripe>
<ListTable table-key="crm-quote-detail" :data="quote?.productList" class="list-table" stripe>
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column label="产品名称" prop="productName" min-width="120" align="center" header-align="center" :show-overflow-tooltip="true" />
<el-table-column label="型号/规格" prop="model" min-width="130" align="center" header-align="center" :show-overflow-tooltip="true">
@@ -134,7 +134,7 @@
<span>{{ scope.row.deliveryMethod || '-' }}</span>
</template>
</el-table-column>
</el-table>
</ListTable>
<!-- 产品汇总 -->
<div class="product-summary mt-4">
+4 -4
View File
@@ -148,7 +148,7 @@
</div>
<div class="list-body list-wrap">
<el-table
<ListTable table-key="crm-quote"
v-loading="loading"
class="list-table"
stripe
@@ -161,7 +161,7 @@
<el-table-column type="expand" width="55">
<template #default="scope">
<div class="quote-product-expand" v-loading="expandLoadingMap[String(scope.row.id)]">
<el-table :data="scope.row.productList || []" stripe class="list-table">
<ListTable table-key="crm-quote-2" :data="scope.row.productList || []" stripe class="list-table">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column
label="产品名称"
@@ -221,7 +221,7 @@
<span>{{ productScope.row.deliveryMethod || '-' }}</span>
</template>
</el-table-column>
</el-table>
</ListTable>
</div>
</template>
</el-table-column>
@@ -392,7 +392,7 @@
show-overflow-tooltip
/>
<el-table-column v-if="isColumnVisible('remark')" label="备注" align="center" prop="remark" min-width="200" show-overflow-tooltip />
</el-table>
</ListTable>
<div class="pagination list-pager-bar">
<div class="list-pager-selected">
+2 -2
View File
@@ -62,7 +62,7 @@
<section class="stats-section">
<h3 class="section-title">状态详情统计</h3>
<div class="list-body list-wrap">
<el-table :data="statusList" class="list-table" stripe>
<ListTable table-key="crm-quote-statistics" :data="statusList" class="list-table" stripe>
<el-table-column
type="index"
label="序号"
@@ -84,7 +84,7 @@
<el-table-column label="占比" prop="percent" min-width="100" align="right">
<template #default="{ row }">{{ row.percent }}%</template>
</el-table-column>
</el-table>
</ListTable>
</div>
</section>
</div>
@@ -33,7 +33,7 @@
</div>
<div class="list-body list-wrap">
<el-table v-loading="loading" :data="tableData" class="list-table" stripe>
<ListTable table-key="crm-supplier-hazard" v-loading="loading" :data="tableData" class="list-table" stripe>
<el-table-column prop="supplierName" label="主体名称" min-width="200" show-overflow-tooltip />
<el-table-column prop="supplierCode" label="编码" min-width="100" show-overflow-tooltip />
<el-table-column label="允许易制毒" min-width="120" align="center">
@@ -64,7 +64,7 @@
</template>
</el-table-column>
<el-table-column prop="updateTime" label="更新时间" min-width="160" show-overflow-tooltip />
</el-table>
</ListTable>
<div class="pagination list-pager-bar">
<el-pagination
+14 -19
View File
@@ -82,11 +82,7 @@
</el-button>
<el-button
class="btn-ghost"
:disabled="
selectedRows.length !== 1 ||
!selectedRows[0]?.purchasePlanId ||
!!selectedRows[0]?.hasWeighingSlip
"
:disabled="selectedRows.length !== 1 || !!selectedRows[0]?.hasWeighingSlip"
@click="handleGenerateWeighing(selectedRows[0])"
>
<el-icon><Plus /></el-icon>
@@ -98,7 +94,7 @@
</el-button>
<el-button
class="btn-ghost"
:disabled="selectedRows.length !== 1 || !selectedRows[0]?.purchasePlanId"
:disabled="selectedRows.length !== 1"
@click="handleCopyFillLink(selectedRows[0])"
>
<el-icon><Link /></el-icon>
@@ -138,7 +134,7 @@
</div>
<div class="list-body list-wrap">
<el-table
<ListTable table-key="db-inventory"
class="list-table"
:data="tableData"
v-loading="loading"
@@ -350,7 +346,7 @@
sortable="custom"
:sort-order="getSortOrder('remarks')"
/>
</el-table>
</ListTable>
<div class="pagination list-pager-bar">
<div class="list-pager-selected">
@@ -408,14 +404,18 @@
</el-radio-group>
</el-form-item>
<el-form-item label="往来单位" prop="partnerCompany">
<DbCrmCustomerSelect v-model="formData.partnerCompany" placeholder="请输入往来单位搜索" />
<DbCrmCustomerSelect
v-model="formData.partnerCompany"
:allow-create="false"
placeholder="请从客户管理中搜索选择"
/>
</el-form-item>
<el-form-item label="产品" prop="productId">
<DbCrmProductSelect
v-model:product-id="formData.productId"
v-model:product-name="formData.product"
v-model:specification="formData.specification"
placeholder="请搜索并选择 CRM 产品"
placeholder="请搜索并选择上架产品"
/>
</el-form-item>
<el-form-item label="规格">
@@ -434,7 +434,7 @@
/>
</el-form-item>
<el-form-item label="车牌号" prop="vehicleNo">
<PlVehicleSelect v-model="formData.vehicleNo" />
<PlVehicleSelect v-model="formData.vehicleNo" placeholder="请选择三方或自有车辆车牌" />
</el-form-item>
<el-form-item label="计划号" prop="planNo" class="form-col-span-2">
<el-input v-model="formData.planNo" placeholder="请输入计划号" :readonly="isPlanNoReadonly" />
@@ -466,7 +466,6 @@
编辑
</el-button>
<el-button
v-if="viewingRecord.purchasePlanId"
class="btn-ghost"
:disabled="viewingRecord.hasWeighingSlip"
@click="handleGenerateWeighing(viewingRecord)"
@@ -474,11 +473,7 @@
<el-icon><Plus /></el-icon>
{{ viewingRecord.hasWeighingSlip ? '已生成地磅单' : '生成地磅单' }}
</el-button>
<el-button
v-if="viewingRecord.purchasePlanId"
class="btn-ghost"
@click="handleCopyFillLink(viewingRecord)"
>
<el-button class="btn-ghost" @click="handleCopyFillLink(viewingRecord)">
<el-icon><Link /></el-icon>
磅单二维码
</el-button>
@@ -645,8 +640,8 @@
const formRules = {
date: [{ required: true, message: '请选择日期', trigger: 'change' }],
partnerCompany: [{ required: true, message: '请输入往来单位', trigger: 'blur' }],
productId: [{ required: true, message: '请选择 CRM 产品', trigger: 'change' }],
partnerCompany: [{ required: true, message: '请选择往来单位', trigger: 'change' }],
productId: [{ required: true, message: '请选择上架产品', trigger: 'change' }],
quantity: [{ required: true, message: '请输入数量', trigger: 'blur' }],
vehicleNo: [{ required: true, message: '请选择车牌号', trigger: 'change' }]
}
+2 -2
View File
@@ -134,7 +134,7 @@
</div>
<div class="list-body list-wrap">
<el-table
<ListTable table-key="db-plans"
class="list-table"
:data="tableData"
v-loading="loading"
@@ -280,7 +280,7 @@
</div>
</template>
</el-table-column>
</el-table>
</ListTable>
<div class="pagination list-pager-bar">
<div class="list-pager-selected">
+2 -2
View File
@@ -113,7 +113,7 @@
</div>
<div class="list-body list-wrap">
<el-table
<ListTable table-key="db-weighing"
class="list-table"
:data="paginatedData"
v-loading="loading"
@@ -296,7 +296,7 @@
{{ row.deduction || 0 }}
</template>
</el-table-column>
</el-table>
</ListTable>
<div class="pagination list-pager-bar">
<div class="list-pager-selected">
+2 -2
View File
@@ -85,7 +85,7 @@
</div>
<div class="list-body list-wrap">
<el-table
<ListTable table-key="demo-category"
class="list-table"
:data="tableData"
v-loading="loading"
@@ -209,7 +209,7 @@
</div>
</template>
</el-table-column>
</el-table>
</ListTable>
<div class="pagination list-pager-bar">
<div class="list-pager-selected">
Loaded 100 of 144 files, more files were not shown because too many files have changed in this diff. Show more