提交
This commit is contained in:
1 parent
8e70047f4c
commit
b4a49183db
33 files changed
+11558
-5889
No files matched your search
+1
-1
@@ -1 +1 @@
|
||||
8708
|
||||
8001
|
||||
@@ -2,13 +2,16 @@
|
||||
from fastapi import APIRouter
|
||||
|
||||
from app.api.pl import plan, transport, freight, inventory
|
||||
from app.api.pl import base_data, vehicle_recharge, vehicle_maintenance, vehicle_urea
|
||||
from app.api.pl import base_data, vehicle_recharge, vehicle_maintenance, vehicle_urea, transport_address
|
||||
|
||||
pl_router = APIRouter(prefix="/transport", tags=["运输计划管理"])
|
||||
|
||||
# 基础数据(承运商、车辆、司机)
|
||||
pl_router.include_router(base_data.router)
|
||||
|
||||
# 运输通用地址库
|
||||
pl_router.include_router(transport_address.router)
|
||||
|
||||
# 计划管理
|
||||
pl_router.include_router(plan.router)
|
||||
|
||||
|
||||
@@ -303,9 +303,13 @@ def create_plan(
|
||||
settlement_method=payload.settlement_method,
|
||||
has_freight=payload.has_freight,
|
||||
pickup_address=payload.pickup_address,
|
||||
pickup_lng=payload.pickup_lng,
|
||||
pickup_lat=payload.pickup_lat,
|
||||
pickup_time=payload.pickup_time,
|
||||
pickup_notes=payload.pickup_notes,
|
||||
destination_address=payload.destination_address,
|
||||
destination_lng=payload.destination_lng,
|
||||
destination_lat=payload.destination_lat,
|
||||
delivery_time=payload.delivery_time,
|
||||
destination_notes=payload.destination_notes,
|
||||
follow_user_id=payload.follow_user_id or user.id,
|
||||
@@ -380,9 +384,13 @@ def create_plan_full(
|
||||
settlement_method=payload.settlement_method,
|
||||
has_freight=payload.has_freight,
|
||||
pickup_address=payload.pickup_address,
|
||||
pickup_lng=payload.pickup_lng,
|
||||
pickup_lat=payload.pickup_lat,
|
||||
pickup_time=payload.pickup_time,
|
||||
pickup_notes=payload.pickup_notes,
|
||||
destination_address=payload.destination_address,
|
||||
destination_lng=payload.destination_lng,
|
||||
destination_lat=payload.destination_lat,
|
||||
delivery_time=payload.delivery_time,
|
||||
destination_notes=payload.destination_notes,
|
||||
total_amount=calc_plan_total_amount(payload.products or []),
|
||||
|
||||
@@ -113,9 +113,13 @@ def _build_public_page(db: Session, transport: PlTransport) -> PublicTransportPa
|
||||
plan_date=_format_plan_date(plan.plan_date if plan else None),
|
||||
plan_type=plan.plan_type if plan else "",
|
||||
pickup_address=plan.pickup_address if plan else transport.start_point,
|
||||
pickup_lng=plan.pickup_lng if plan else None,
|
||||
pickup_lat=plan.pickup_lat if plan else None,
|
||||
destination_address=(
|
||||
(plan.destination_address or plan.destination_name) if plan else transport.end_point
|
||||
),
|
||||
destination_lng=plan.destination_lng if plan else None,
|
||||
destination_lat=plan.destination_lat if plan else None,
|
||||
license_plate=transport.license_plate,
|
||||
driver_name=transport.driver_name,
|
||||
driver_phone=transport.driver_phone,
|
||||
|
||||
@@ -246,6 +246,9 @@ def _apply_plan_logistics_from_payload(plan: PlPlan, payload: TransportArrange)
|
||||
plan.pickup_address = payload.pickup_address
|
||||
elif payload.start_point is not None:
|
||||
plan.pickup_address = payload.start_point
|
||||
if "pickup_lng" in payload.model_fields_set or "pickup_lat" in payload.model_fields_set:
|
||||
plan.pickup_lng = payload.pickup_lng
|
||||
plan.pickup_lat = payload.pickup_lat
|
||||
if payload.pickup_time is not None:
|
||||
plan.pickup_time = payload.pickup_time
|
||||
if payload.pickup_notes is not None:
|
||||
@@ -254,6 +257,9 @@ def _apply_plan_logistics_from_payload(plan: PlPlan, payload: TransportArrange)
|
||||
plan.destination_address = payload.destination_address
|
||||
elif payload.end_point is not None:
|
||||
plan.destination_address = payload.end_point
|
||||
if "destination_lng" in payload.model_fields_set or "destination_lat" in payload.model_fields_set:
|
||||
plan.destination_lng = payload.destination_lng
|
||||
plan.destination_lat = payload.destination_lat
|
||||
if payload.delivery_time is not None:
|
||||
plan.delivery_time = payload.delivery_time
|
||||
if payload.destination_notes is not None:
|
||||
|
||||
@@ -102,6 +102,30 @@ class PlPersonnel(Base):
|
||||
del_flag = Column(SmallInteger, nullable=False, default=0, comment="删除标记:0-正常 1-已删除")
|
||||
|
||||
|
||||
class PlTransportAddress(Base):
|
||||
"""运输通用地址库 pl_transport_address"""
|
||||
|
||||
__tablename__ = "pl_transport_address"
|
||||
|
||||
id = Column(String(64), primary_key=True, comment="主键ID")
|
||||
address_name = Column(String(200), nullable=False, comment="地址名称")
|
||||
full_address = Column(String(500), nullable=False, comment="详细地址")
|
||||
lng = Column(DECIMAL(10, 6), nullable=False, comment="经度")
|
||||
lat = Column(DECIMAL(10, 6), nullable=False, comment="纬度")
|
||||
contact_name = Column(String(100), nullable=True, comment="联系人")
|
||||
contact_phone = Column(String(32), nullable=True, comment="联系电话")
|
||||
status = Column(CHAR(1), nullable=False, default="1", comment="状态 1-启用 0-停用")
|
||||
tenant_no = Column(String(64), nullable=False, default="shanxia2.0", comment="租户编号")
|
||||
order_num = Column(Integer, nullable=True, comment="排序号")
|
||||
remark = Column(String(512), nullable=True, comment="备注")
|
||||
create_by = Column(String(64), nullable=True, comment="创建人id")
|
||||
create_dept_id = Column(String(64), nullable=False, default="", comment="创建人部门ID")
|
||||
create_time = Column(DateTime, nullable=False, default=datetime.now, comment="创建时间")
|
||||
update_by = Column(String(64), nullable=True, comment="更新人id")
|
||||
update_time = Column(DateTime, nullable=False, default=datetime.now, onupdate=datetime.now, comment="更新时间")
|
||||
del_flag = Column(SmallInteger, nullable=False, default=0, comment="删除标记:0-正常 1-已删除")
|
||||
|
||||
|
||||
class PlCert(Base):
|
||||
"""证书表 pl_cert"""
|
||||
|
||||
@@ -248,9 +272,13 @@ class PlPlan(Base):
|
||||
# 运输信息
|
||||
has_freight = Column(SmallInteger, nullable=False, default=0, comment="是否有运输费用 0-否 1-是")
|
||||
pickup_address = Column(String(500), nullable=True, comment="取货地址")
|
||||
pickup_lng = Column(DECIMAL(10, 6), nullable=True, comment="取货经度")
|
||||
pickup_lat = Column(DECIMAL(10, 6), nullable=True, comment="取货纬度")
|
||||
pickup_time = Column(DateTime, nullable=True, comment="取货时间")
|
||||
pickup_notes = Column(String(500), nullable=True, comment="取货注意事项")
|
||||
destination_address = Column(String(500), nullable=True, comment="目的地地址")
|
||||
destination_lng = Column(DECIMAL(10, 6), nullable=True, comment="送货经度")
|
||||
destination_lat = Column(DECIMAL(10, 6), nullable=True, comment="送货纬度")
|
||||
delivery_time = Column(DateTime, nullable=True, comment="送达时间")
|
||||
destination_notes = Column(String(500), nullable=True, comment="目的地注意事项")
|
||||
transport_id = Column(String(64), nullable=True, comment="运输ID")
|
||||
|
||||
@@ -5,6 +5,7 @@ from typing import Optional
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from app.schemas.pl.datetime_parse import OptionalPlanDateTime
|
||||
from app.schemas.pl.resource_snapshot import PlanResourceSnapshotMixin
|
||||
|
||||
# =============================================================================
|
||||
@@ -105,10 +106,14 @@ class PlanBase(BaseModel):
|
||||
settlement_method: Optional[str] = Field(default=None, max_length=50, alias="settlementMethod", description="结算方式")
|
||||
has_freight: int = Field(default=0, alias="hasFreight", description="是否有运输费用")
|
||||
pickup_address: Optional[str] = Field(default=None, max_length=500, alias="pickupAddress", description="取货地址")
|
||||
pickup_time: Optional[datetime] = Field(default=None, alias="pickupTime", description="取货时间")
|
||||
pickup_lng: Optional[Decimal] = Field(default=None, alias="pickupLng", description="取货经度")
|
||||
pickup_lat: Optional[Decimal] = Field(default=None, alias="pickupLat", description="取货纬度")
|
||||
pickup_time: OptionalPlanDateTime = Field(default=None, alias="pickupTime", description="取货时间")
|
||||
pickup_notes: Optional[str] = Field(default=None, max_length=500, alias="pickupNotes", description="取货注意事项")
|
||||
destination_address: Optional[str] = Field(default=None, max_length=500, alias="destinationAddress", description="目的地地址")
|
||||
delivery_time: Optional[datetime] = Field(default=None, alias="deliveryTime", description="送达时间")
|
||||
destination_lng: Optional[Decimal] = Field(default=None, alias="destinationLng", description="送货经度")
|
||||
destination_lat: Optional[Decimal] = Field(default=None, alias="destinationLat", description="送货纬度")
|
||||
delivery_time: OptionalPlanDateTime = Field(default=None, alias="deliveryTime", description="送达时间")
|
||||
destination_notes: Optional[str] = Field(default=None, max_length=500, alias="destinationNotes", description="目的地注意事项")
|
||||
notify_message: Optional[str] = Field(default=None, alias="notifyMessage", description="通知信息")
|
||||
follow_user_id: Optional[str] = Field(default=None, alias="followUserId", description="跟进人ID")
|
||||
@@ -158,10 +163,14 @@ class PlanUpdate(BaseModel):
|
||||
settlement_method: Optional[str] = Field(default=None, max_length=50, alias="settlementMethod")
|
||||
has_freight: Optional[int] = Field(default=None, alias="hasFreight")
|
||||
pickup_address: Optional[str] = Field(default=None, max_length=500, alias="pickupAddress")
|
||||
pickup_time: Optional[datetime] = Field(default=None, alias="pickupTime")
|
||||
pickup_lng: Optional[Decimal] = Field(default=None, alias="pickupLng")
|
||||
pickup_lat: Optional[Decimal] = Field(default=None, alias="pickupLat")
|
||||
pickup_time: OptionalPlanDateTime = Field(default=None, alias="pickupTime")
|
||||
pickup_notes: Optional[str] = Field(default=None, max_length=500, alias="pickupNotes")
|
||||
destination_address: Optional[str] = Field(default=None, max_length=500, alias="destinationAddress")
|
||||
delivery_time: Optional[datetime] = Field(default=None, alias="deliveryTime")
|
||||
destination_lng: Optional[Decimal] = Field(default=None, alias="destinationLng")
|
||||
destination_lat: Optional[Decimal] = Field(default=None, alias="destinationLat")
|
||||
delivery_time: OptionalPlanDateTime = Field(default=None, alias="deliveryTime")
|
||||
destination_notes: Optional[str] = Field(default=None, max_length=500, alias="destinationNotes")
|
||||
notify_message: Optional[str] = Field(default=None, alias="notifyMessage")
|
||||
follow_user_id: Optional[str] = Field(default=None, alias="followUserId")
|
||||
|
||||
@@ -5,6 +5,7 @@ from typing import Optional
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from app.schemas.pl.datetime_parse import OptionalPlanDateTime
|
||||
from app.schemas.pl.resource_snapshot import PlanResourceSnapshotMixin
|
||||
|
||||
|
||||
@@ -48,10 +49,14 @@ class TransportArrange(BaseModel):
|
||||
carrier_id: Optional[str] = Field(default=None, alias="carrierId", description="承运商ID")
|
||||
transport_medium: Optional[str] = Field(default=None, alias="transportMedium", description="运输介质")
|
||||
pickup_address: Optional[str] = Field(default=None, alias="pickupAddress", description="取货地址")
|
||||
pickup_time: Optional[datetime] = Field(default=None, alias="pickupTime", description="取货时间")
|
||||
pickup_lng: Optional[Decimal] = Field(default=None, alias="pickupLng", description="取货经度")
|
||||
pickup_lat: Optional[Decimal] = Field(default=None, alias="pickupLat", description="取货纬度")
|
||||
pickup_time: OptionalPlanDateTime = Field(default=None, alias="pickupTime", description="取货时间")
|
||||
pickup_notes: Optional[str] = Field(default=None, alias="pickupNotes", description="取货注意事项")
|
||||
destination_address: Optional[str] = Field(default=None, alias="destinationAddress", description="送货地址")
|
||||
delivery_time: Optional[datetime] = Field(default=None, alias="deliveryTime", description="送达时间")
|
||||
destination_lng: Optional[Decimal] = Field(default=None, alias="destinationLng", description="送货经度")
|
||||
destination_lat: Optional[Decimal] = Field(default=None, alias="destinationLat", description="送货纬度")
|
||||
delivery_time: OptionalPlanDateTime = Field(default=None, alias="deliveryTime", description="送达时间")
|
||||
destination_notes: Optional[str] = Field(default=None, alias="destinationNotes", description="送货注意事项")
|
||||
start_point: Optional[str] = Field(default=None, alias="startPoint", description="运输起点(兼容,等同取货地址)")
|
||||
end_point: Optional[str] = Field(default=None, alias="endPoint", description="运输终点(兼容,等同送货地址)")
|
||||
@@ -191,7 +196,11 @@ class PublicTransportPageRead(BaseModel):
|
||||
plan_date: Optional[str] = Field(default=None, alias="planDate")
|
||||
plan_type: str = Field(alias="planType")
|
||||
pickup_address: Optional[str] = Field(default=None, alias="pickupAddress")
|
||||
pickup_lng: Optional[Decimal] = Field(default=None, alias="pickupLng")
|
||||
pickup_lat: Optional[Decimal] = Field(default=None, alias="pickupLat")
|
||||
destination_address: Optional[str] = Field(default=None, alias="destinationAddress")
|
||||
destination_lng: Optional[Decimal] = Field(default=None, alias="destinationLng")
|
||||
destination_lat: Optional[Decimal] = Field(default=None, alias="destinationLat")
|
||||
license_plate: Optional[str] = Field(default=None, alias="licensePlate")
|
||||
driver_name: Optional[str] = Field(default=None, alias="driverName")
|
||||
driver_phone: Optional[str] = Field(default=None, alias="driverPhone")
|
||||
|
||||
@@ -108,10 +108,8 @@
|
||||
"106": "Community 106",
|
||||
"107": "Community 107",
|
||||
"108": "Community 108",
|
||||
"109": "Community 109",
|
||||
"110": "Community 110",
|
||||
"111": "Community 111",
|
||||
"112": "Community 112",
|
||||
"113": "Community 113",
|
||||
"114": "Community 114",
|
||||
"115": "Community 115",
|
||||
@@ -121,32 +119,24 @@
|
||||
"119": "Community 119",
|
||||
"120": "Community 120",
|
||||
"121": "Community 121",
|
||||
"122": "Community 122",
|
||||
"123": "Community 123",
|
||||
"124": "Community 124",
|
||||
"125": "Community 125",
|
||||
"126": "Community 126",
|
||||
"127": "Community 127",
|
||||
"128": "Community 128",
|
||||
"129": "Community 129",
|
||||
"130": "Community 130",
|
||||
"131": "Community 131",
|
||||
"132": "Community 132",
|
||||
"133": "Community 133",
|
||||
"134": "Community 134",
|
||||
"135": "Community 135",
|
||||
"136": "Community 136",
|
||||
"137": "Community 137",
|
||||
"138": "Community 138",
|
||||
"139": "Community 139",
|
||||
"140": "Community 140",
|
||||
"141": "Community 141",
|
||||
"142": "Community 142",
|
||||
"143": "Community 143",
|
||||
"144": "Community 144",
|
||||
"145": "Community 145",
|
||||
"146": "Community 146",
|
||||
"147": "Community 147",
|
||||
"148": "Community 148",
|
||||
"150": "Community 150"
|
||||
"149": "Community 149",
|
||||
"150": "Community 150",
|
||||
"151": "Community 151"
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
# Graph Report - backend (2026-07-01)
|
||||
|
||||
## Corpus Check
|
||||
- 226 files · ~158,852 words
|
||||
- 229 files · ~159,883 words
|
||||
- Verdict: corpus is large enough that graph structure adds value.
|
||||
|
||||
## Summary
|
||||
- 5033 nodes · 34891 edges · 150 communities (148 shown, 2 thin omitted)
|
||||
- Extraction: 51% EXTRACTED · 49% INFERRED · 0% AMBIGUOUS · INFERRED: 17194 edges (avg confidence: 0.5)
|
||||
- 5097 nodes · 35085 edges · 140 communities (138 shown, 2 thin omitted)
|
||||
- Extraction: 51% EXTRACTED · 49% INFERRED · 0% AMBIGUOUS · INFERRED: 17242 edges (avg confidence: 0.5)
|
||||
- Token cost: 0 input · 0 output
|
||||
|
||||
## Graph Freshness
|
||||
- Built from commit: `7c838cfb`
|
||||
- Built from commit: `8e70047f`
|
||||
- Run `git rev-parse HEAD` and compare to check if the graph is stale.
|
||||
- Run `graphify update .` after code changes (no API cost).
|
||||
|
||||
@@ -124,46 +124,36 @@
|
||||
- [[_COMMUNITY_Community 106|Community 106]]
|
||||
- [[_COMMUNITY_Community 107|Community 107]]
|
||||
- [[_COMMUNITY_Community 108|Community 108]]
|
||||
- [[_COMMUNITY_Community 109|Community 109]]
|
||||
- [[_COMMUNITY_Community 110|Community 110]]
|
||||
- [[_COMMUNITY_Community 111|Community 111]]
|
||||
- [[_COMMUNITY_Community 112|Community 112]]
|
||||
- [[_COMMUNITY_Community 122|Community 122]]
|
||||
- [[_COMMUNITY_Community 123|Community 123]]
|
||||
- [[_COMMUNITY_Community 124|Community 124]]
|
||||
- [[_COMMUNITY_Community 125|Community 125]]
|
||||
- [[_COMMUNITY_Community 126|Community 126]]
|
||||
- [[_COMMUNITY_Community 127|Community 127]]
|
||||
- [[_COMMUNITY_Community 128|Community 128]]
|
||||
- [[_COMMUNITY_Community 129|Community 129]]
|
||||
- [[_COMMUNITY_Community 130|Community 130]]
|
||||
- [[_COMMUNITY_Community 131|Community 131]]
|
||||
- [[_COMMUNITY_Community 132|Community 132]]
|
||||
- [[_COMMUNITY_Community 133|Community 133]]
|
||||
- [[_COMMUNITY_Community 134|Community 134]]
|
||||
- [[_COMMUNITY_Community 135|Community 135]]
|
||||
- [[_COMMUNITY_Community 136|Community 136]]
|
||||
- [[_COMMUNITY_Community 137|Community 137]]
|
||||
- [[_COMMUNITY_Community 138|Community 138]]
|
||||
- [[_COMMUNITY_Community 139|Community 139]]
|
||||
- [[_COMMUNITY_Community 140|Community 140]]
|
||||
- [[_COMMUNITY_Community 141|Community 141]]
|
||||
- [[_COMMUNITY_Community 143|Community 143]]
|
||||
- [[_COMMUNITY_Community 144|Community 144]]
|
||||
- [[_COMMUNITY_Community 145|Community 145]]
|
||||
- [[_COMMUNITY_Community 146|Community 146]]
|
||||
- [[_COMMUNITY_Community 147|Community 147]]
|
||||
- [[_COMMUNITY_Community 148|Community 148]]
|
||||
- [[_COMMUNITY_Community 149|Community 149]]
|
||||
- [[_COMMUNITY_Community 150|Community 150]]
|
||||
- [[_COMMUNITY_Community 151|Community 151]]
|
||||
|
||||
## God Nodes (most connected - your core abstractions)
|
||||
1. `Session` - 1123 edges
|
||||
2. `BaseModel` - 420 edges
|
||||
2. `BaseModel` - 423 edges
|
||||
3. `int` - 336 edges
|
||||
4. `Any` - 331 edges
|
||||
5. `get_tenant_no()` - 327 edges
|
||||
6. `SysUser` - 282 edges
|
||||
7. `DbUserContext` - 272 edges
|
||||
4. `get_tenant_no()` - 334 edges
|
||||
5. `Any` - 331 edges
|
||||
6. `DbUserContext` - 282 edges
|
||||
7. `SysUser` - 282 edges
|
||||
8. `success_response()` - 259 edges
|
||||
9. `Session` - 257 edges
|
||||
10. `bool` - 255 edges
|
||||
@@ -180,135 +170,135 @@
|
||||
- `int` --uses--> `LocalStorage` [INFERRED]
|
||||
scripts/run_db_auto_generate_plans.py → app/core/storage.py
|
||||
|
||||
## Communities (150 total, 2 thin omitted)
|
||||
## Communities (140 total, 2 thin omitted)
|
||||
|
||||
### Community 0 - "Community 0"
|
||||
Cohesion: 0.14
|
||||
Nodes (103): date, DbUserContext, int, Session, str, DocumentReminderRead, DocumentReminderUpdate, FreightSettlementUpdate (+95 more)
|
||||
Nodes (105): date, DbUserContext, int, Session, str, DocumentReminderRead, DocumentReminderUpdate, FreightSettlementCreate (+97 more)
|
||||
|
||||
### Community 1 - "Community 1"
|
||||
Cohesion: 0.04
|
||||
Nodes (46): invoice_info_list(), 按客户ID查询开票信息(与 by-customer 等价), 按客户ID查询开票信息(与 by-customer 等价), 按客户ID查询开票信息(与 by-customer 等价), 按客户ID查询开票信息(与 by-customer 等价), 按客户ID查询开票信息(与 by-customer 等价), 供方全量列表(下拉)— Java: CrmSupplierInfoController.listAll, 供方全量列表(下拉)— Java: CrmSupplierInfoController.listAll (+38 more)
|
||||
Cohesion: 0.08
|
||||
Nodes (26): 供方详情 — Java: CrmSupplierInfoController.getInfo, 供方详情 — Java: CrmSupplierInfoController.getInfo, 供方详情 — Java: CrmSupplierInfoController.getInfo, 供方详情 — Java: CrmSupplierInfoController.getInfo, 供方详情 — Java: CrmSupplierInfoController.getInfo, 供方详情 — Java: CrmSupplierInfoController.getInfo, 供方详情 — Java: CrmSupplierInfoController.getInfo, 供方详情 — Java: CrmSupplierInfoController.getInfo (+18 more)
|
||||
|
||||
### Community 2 - "Community 2"
|
||||
Cohesion: 0.35
|
||||
Nodes (219): Any, bool, str, UploadFile, CRM 线索管理 Pydantic Schemas, CRM 客户管理 Pydantic Schemas, CRM 商机管理 Pydantic Schemas, CRM 客户公海池规则 Pydantic Schemas (+211 more)
|
||||
Nodes (220): Any, bool, str, UploadFile, CRM 线索管理 Pydantic Schemas, CRM 客户管理 Pydantic Schemas, CRM 商机管理 Pydantic Schemas, CRM 客户公海池规则 Pydantic Schemas (+212 more)
|
||||
|
||||
### Community 3 - "Community 3"
|
||||
Cohesion: 0.07
|
||||
Nodes (171): _apply_default_invoice_info_to_customers(), category_add(), category_delete(), category_tree(), category_update(), clue_assign(), clue_check_convert(), clue_check_duplicate() (+163 more)
|
||||
Nodes (170): _apply_default_invoice_info_to_customers(), category_add(), category_delete(), category_tree(), category_update(), clue_assign(), clue_check_convert(), clue_check_duplicate() (+162 more)
|
||||
|
||||
### Community 4 - "Community 4"
|
||||
Cohesion: 0.13
|
||||
Nodes (29): Any, bool, CRM 供方信息 CRUD — 对照 CrmSupplierInfoServiceImpl.java, int, Session, str, _apply_list_filters(), create_supplier_info() (+21 more)
|
||||
Cohesion: 0.08
|
||||
Nodes (41): Any, bool, CRM 供方信息 CRUD — 对照 CrmSupplierInfoServiceImpl.java, int, Session, str, generate_contract_no(), Java: generateContractNo — {供方编码}/X-JT + yyyyMMdd + 3位流水 (+33 more)
|
||||
|
||||
### Community 5 - "Community 5"
|
||||
Cohesion: 0.08
|
||||
Nodes (39): _db_pool_keepalive_loop(), _error_json(), lifespan(), _mask_secret(), Exception, int, Request, str (+31 more)
|
||||
Cohesion: 0.07
|
||||
Nodes (41): _db_pool_keepalive_loop(), _error_json(), lifespan(), _mask_secret(), Exception, int, Request, str (+33 more)
|
||||
|
||||
### Community 6 - "Community 6"
|
||||
Cohesion: 0.14
|
||||
Nodes (39): Any, bool, int, Session, str, SysUser, create_user(), get_user() (+31 more)
|
||||
|
||||
### Community 7 - "Community 7"
|
||||
Cohesion: 0.06
|
||||
Nodes (33): BaseModel, AttachmentOut, ReimburseApplyCreate, ReimburseApplyUpdate, ReimburseApprovalCreate, _coerce_optional_id(), WorkflowDefCreate, WorkflowDefUpdate (+25 more)
|
||||
Cohesion: 0.33
|
||||
Nodes (5): _coerce_optional_id(), WorkflowDefCreate, WorkflowDefUpdate, WorkflowNodeCreate, WorkflowNodeOut
|
||||
|
||||
### Community 8 - "Community 8"
|
||||
Cohesion: 0.35
|
||||
Nodes (5): Any, str, WfProcessInstance, WorkflowEngine, WfProcessInstance
|
||||
|
||||
### Community 9 - "Community 9"
|
||||
Cohesion: 0.19
|
||||
Nodes (38): ProcessDefCreateBody, ProcessDefUpdateBody, PublishVersionBody, StartInstanceBody, Any, bool, WfProcessInstance, bool (+30 more)
|
||||
Cohesion: 0.14
|
||||
Nodes (34): Session, str, Session, str, user_info_from_context(), parse_date_str(), plan_to_read(), plan_to_read_with_fill_link() (+26 more)
|
||||
|
||||
### Community 10 - "Community 10"
|
||||
Cohesion: 0.03
|
||||
Nodes (151): register_quote_routes(), Any, bool, CRM 报价单 CRUD — 对照 CrmQuoteServiceImpl.java(P2 全量), datetime, Decimal, int, Session (+143 more)
|
||||
Nodes (185): register_quote_routes(), CrmContractProductPriceTier, Query, Any, bool, int, Session, str (+177 more)
|
||||
|
||||
### Community 11 - "Community 11"
|
||||
Cohesion: 0.15
|
||||
Nodes (38): Any, bool, BxUserContext, BxWorkflowNode, float, int, ReimburseApplyOut, Session (+30 more)
|
||||
Cohesion: 0.16
|
||||
Nodes (26): Any, bool, BxUserContext, BxWorkflowNode, float, int, ReimburseApplyOut, Session (+18 more)
|
||||
|
||||
### Community 12 - "Community 12"
|
||||
Cohesion: 0.12
|
||||
Nodes (84): date, DbUserContext, int, Session, str, VehicleUreaApplyCreate, VehicleUreaApplyRead, VehicleUreaApplyUpdate (+76 more)
|
||||
Cohesion: 0.10
|
||||
Nodes (92): date, DbUserContext, int, Session, str, VehicleUreaApplyCreate, VehicleUreaApplyRead, VehicleUreaApplyUpdate (+84 more)
|
||||
|
||||
### Community 13 - "Community 13"
|
||||
Cohesion: 0.05
|
||||
Nodes (72): BxUserContext, Session, str, UploadFile, Any, bool, bytes, int (+64 more)
|
||||
Cohesion: 0.13
|
||||
Nodes (8): bool, Path, str, BaseStorage, LocalStorage, OSSStorage, QiniuStorage, Path
|
||||
|
||||
### Community 14 - "Community 14"
|
||||
Cohesion: 0.09
|
||||
Nodes (55): Any, bool, datetime, int, Session, str, _advance_contract_after_approval(), approve_contract() (+47 more)
|
||||
Nodes (58): Any, bool, CrmContract, datetime, int, Query, Session, str (+50 more)
|
||||
|
||||
### Community 15 - "Community 15"
|
||||
Cohesion: 0.14
|
||||
Nodes (28): str, T, bool, date, datetime, int, Session, str (+20 more)
|
||||
Nodes (29): str, T, bool, date, datetime, int, Session, str (+21 more)
|
||||
|
||||
### Community 16 - "Community 16"
|
||||
Cohesion: 0.07
|
||||
Nodes (103): Any, CRM 合同子模块路由 — 变更/模板/审批独立页, register_contract_sub_routes(), Any, CRM 合同 CRUD — 对照 CrmContractServiceImpl.java(列表/详情 P1), Decimal, int, Session (+95 more)
|
||||
Cohesion: 0.03
|
||||
Nodes (201): register_contract_routes(), CRM 合同子模块路由 — 变更/模板/审批独立页, register_contract_sub_routes(), Any, bool, CRM 合同 CRUD — 对照 CrmContractServiceImpl.java(列表/详情 P1), CrmContract, date (+193 more)
|
||||
|
||||
### Community 17 - "Community 17"
|
||||
Cohesion: 0.18
|
||||
Nodes (50): Any, CRM 商机 CRUD(主表 + 阶段/赢输单/统计/看板/漏斗), CrmOpportunity, date, datetime, Decimal, int, Session (+42 more)
|
||||
Nodes (55): Any, date, Any, CRM 商机 CRUD(主表 + 阶段/赢输单/统计/看板/漏斗), CrmOpportunity, date, datetime, Decimal (+47 more)
|
||||
|
||||
### Community 18 - "Community 18"
|
||||
Cohesion: 0.18
|
||||
Nodes (19): Any, bool, BxUserContext, BxWorkflowNode, float, int, Session, str (+11 more)
|
||||
Cohesion: 0.14
|
||||
Nodes (31): Any, bool, BxUserContext, BxWorkflowNode, float, int, Session, str (+23 more)
|
||||
|
||||
### Community 19 - "Community 19"
|
||||
Cohesion: 0.21
|
||||
Nodes (27): 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() (+19 more)
|
||||
Cohesion: 0.23
|
||||
Nodes (25): approve_task(), _assert_wf_instance_in_scope(), _business_type_label(), delete_process_def(), _enrich_task_list(), get_business_all_tasks(), _get_business_form_fields(), get_designer_data() (+17 more)
|
||||
|
||||
### Community 20 - "Community 20"
|
||||
Cohesion: 0.22
|
||||
Nodes (47): BxUserContext, int, Session, str, BxUserContext, int, Session, str (+39 more)
|
||||
Cohesion: 0.28
|
||||
Nodes (34): AdvanceApplyCreate, AdvanceApplyOut, AdvanceApplyUpdate, AdvanceApprovalCreate, BxUserContext, int, Session, str (+26 more)
|
||||
|
||||
### Community 21 - "Community 21"
|
||||
Cohesion: 0.05
|
||||
Nodes (162): bool, DbUserContext, int, 运输计划管理 - 基础数据 API(承运商、车辆、人员), PlCarrier, PlVehicle, Session, str (+154 more)
|
||||
Nodes (114): DbUserContext, int, 运输计划管理 - 基础数据 API(承运商、车辆、人员), Session, str, date, 运输计划管理 - 基础数据 Schema(承运商、车辆、人员、证书), bool (+106 more)
|
||||
|
||||
### Community 22 - "Community 22"
|
||||
Cohesion: 0.22
|
||||
Nodes (29): Any, int, Session, str, create_instance(), create_process_def(), create_task(), get_all_tasks_for_business() (+21 more)
|
||||
|
||||
### Community 23 - "Community 23"
|
||||
Cohesion: 0.23
|
||||
Nodes (37): AdvanceIssueRequest, BxUserContext, int, ReimburseApplyOut, Session, str, datetime, str (+29 more)
|
||||
Cohesion: 0.24
|
||||
Nodes (44): AdvanceIssueRequest, BxUserContext, int, ReimburseApplyOut, Session, str, int, str (+36 more)
|
||||
|
||||
### Community 24 - "Community 24"
|
||||
Cohesion: 0.12
|
||||
Nodes (89): date, DbUserContext, int, Session, str, VehicleMaintenanceApplyCreate, VehicleMaintenanceApplyRead, VehicleMaintenanceApplyReminder (+81 more)
|
||||
Nodes (92): date, DbUserContext, int, Session, str, VehicleMaintenanceApplyCreate, VehicleMaintenanceApplyRead, VehicleMaintenanceApplyReminder (+84 more)
|
||||
|
||||
### Community 25 - "Community 25"
|
||||
Cohesion: 0.09
|
||||
Nodes (29): 运输计划管理 - 基础数据 Schema(承运商、车辆、人员、证书), CarrierAuditReject, CarrierBase, CarrierCreate, CarrierListResponse, CarrierQualificationBase, CarrierRead, CarrierUpdate (+21 more)
|
||||
Cohesion: 0.05
|
||||
Nodes (126): date, DbUserContext, int, PlanCreateFull, PlPlan, Session, str, PlTransportSiteLink (+118 more)
|
||||
|
||||
### Community 26 - "Community 26"
|
||||
Cohesion: 0.15
|
||||
Nodes (23): logout(), 登出:吊销 Redis 会话(若已配置 REDIS_HOST)。, 登出:吊销 Redis 会话(若已配置 REDIS_HOST)。, 登出:吊销 Redis 会话(若已配置 REDIS_HOST)。, bool, int, Redis, Request (+15 more)
|
||||
Cohesion: 0.20
|
||||
Nodes (18): bool, int, Redis, Request, str, _claims_from_auth(), _coerce_post_method_override_to_get(), _get_redis_client() (+10 more)
|
||||
|
||||
### Community 27 - "Community 27"
|
||||
Cohesion: 0.11
|
||||
Nodes (57): Any, bool, int, Session, str, Any, bool, CRM 线索 CRUD(线索主表 + 转化/分配/无效/等级操作) (+49 more)
|
||||
Nodes (58): Any, bool, int, Session, str, Any, bool, CRM 线索 CRUD(线索主表 + 转化/分配/无效/等级操作) (+50 more)
|
||||
|
||||
### Community 28 - "Community 28"
|
||||
Cohesion: 0.33
|
||||
Nodes (29): AdvanceApplyCreate, AdvanceApplyOut, AdvanceApplyUpdate, AdvanceApprovalCreate, BxUserContext, int, Session, str (+21 more)
|
||||
Cohesion: 0.14
|
||||
Nodes (25): bool, BxUserContext, int, Session, str, Any, bool, int (+17 more)
|
||||
|
||||
### Community 29 - "Community 29"
|
||||
Cohesion: 0.08
|
||||
Nodes (26): 供方详情 — Java: CrmSupplierInfoController.getInfo, 供方详情 — Java: CrmSupplierInfoController.getInfo, 供方详情 — Java: CrmSupplierInfoController.getInfo, 供方详情 — Java: CrmSupplierInfoController.getInfo, 供方详情 — Java: CrmSupplierInfoController.getInfo, 供方详情 — Java: CrmSupplierInfoController.getInfo, 供方详情 — Java: CrmSupplierInfoController.getInfo, 供方详情 — Java: CrmSupplierInfoController.getInfo (+18 more)
|
||||
Cohesion: 0.28
|
||||
Nodes (25): DbUserContext, int, Session, str, PlTransportAddress, 运输通用地址库 pl_transport_address, _address_to_read(), _build_address_query() (+17 more)
|
||||
|
||||
### Community 30 - "Community 30"
|
||||
Cohesion: 0.07
|
||||
Nodes (43): AttachmentUploadResponse, ChangePasswordRequest, Config, LoginRequest, LoginResponse, Pydantic schemas - 用户认证, ResetPasswordRequest, UserCreate (+35 more)
|
||||
Nodes (41): AttachmentUploadResponse, ChangePasswordRequest, Config, LoginRequest, LoginResponse, Pydantic schemas - 用户认证, ResetPasswordRequest, UserCreate (+33 more)
|
||||
|
||||
### Community 31 - "Community 31"
|
||||
Cohesion: 0.15
|
||||
Nodes (30): Any, bool, int, Session, str, approve_quote(), can_user_approve(), create_approval_records() (+22 more)
|
||||
Cohesion: 0.08
|
||||
Nodes (26): 分页查询供方列表 — Java: CrmSupplierInfoController.list, 分页查询供方列表 — Java: CrmSupplierInfoController.list, 分页查询供方列表 — Java: CrmSupplierInfoController.list, 分页查询供方列表 — Java: CrmSupplierInfoController.list, 分页查询供方列表 — Java: CrmSupplierInfoController.list, 分页查询供方列表 — Java: CrmSupplierInfoController.list, 分页查询供方列表 — Java: CrmSupplierInfoController.list, 分页查询供方列表 — Java: CrmSupplierInfoController.list (+18 more)
|
||||
|
||||
### Community 32 - "Community 32"
|
||||
Cohesion: 0.10
|
||||
@@ -316,15 +306,15 @@ Nodes (99): date, DbUserContext, int, Session, str, VehicleRechargeApplyCreate,
|
||||
|
||||
### Community 33 - "Community 33"
|
||||
Cohesion: 0.13
|
||||
Nodes (56): Any, int, Session, str, Any, bool, CRM 客户 CRUD(客户主表 + 转移/成交/公海池操作), CrmCustomer (+48 more)
|
||||
Nodes (54): Any, int, Session, str, Any, bool, CRM 客户 CRUD(客户主表 + 转移/成交/公海池操作), CrmCustomer (+46 more)
|
||||
|
||||
### Community 34 - "Community 34"
|
||||
Cohesion: 0.19
|
||||
Nodes (34): int, str, generate_random_password(), get_file_size_display(), Engine, int, Path, build_id_maps() (+26 more)
|
||||
Cohesion: 0.23
|
||||
Nodes (28): Engine, build_id_maps(), clip_text(), count_rows(), export_mapping(), fetch_rows(), legacy_id_to_str(), main() (+20 more)
|
||||
|
||||
### Community 35 - "Community 35"
|
||||
Cohesion: 0.04
|
||||
Nodes (182): DbUserContext, FreightSettlementDisburseBody, FreightSettlementSubmitApprovalBody, FreightSettlementUpdate, int, Session, str, Any (+174 more)
|
||||
Cohesion: 0.03
|
||||
Nodes (196): DbUserContext, FreightSettlementDisburseBody, FreightSettlementSubmitApprovalBody, FreightSettlementUpdate, int, Session, str, Any (+188 more)
|
||||
|
||||
### Community 36 - "Community 36"
|
||||
Cohesion: 0.19
|
||||
@@ -339,60 +329,60 @@ Cohesion: 0.13
|
||||
Nodes (15): Any, bool, int, str, get_wechat_pay_client(), get_wechat_pay_v2(), 微信支付工具模块 支持微信支付 V3 API 文档参考: https://pay.weixin.qq.com/docs/v3/apis/index.html, 统一下单接口 (V3) Args: out_trade_no: 商户订单号 total_amo (+7 more)
|
||||
|
||||
### Community 39 - "Community 39"
|
||||
Cohesion: 0.18
|
||||
Nodes (22): datetime, date, datetime, str, 审批轮次过滤 — 驳回后重新提交时,进度接口仅展示当前轮次。, format_datetime(), get_current_time(), parse_datetime() (+14 more)
|
||||
Cohesion: 0.16
|
||||
Nodes (23): datetime, int, str, datetime, str, 审批轮次过滤 — 驳回后重新提交时,进度接口仅展示当前轮次。, format_datetime(), generate_random_password() (+15 more)
|
||||
|
||||
### Community 40 - "Community 40"
|
||||
Cohesion: 0.13
|
||||
Nodes (37): Any, bool, Decimal, int, Session, str, batch_insert_quote_products(), calculate_subtotal() (+29 more)
|
||||
|
||||
### Community 41 - "Community 41"
|
||||
Cohesion: 0.12
|
||||
Nodes (41): Any, bool, date, int, Session, str, Session, str (+33 more)
|
||||
Cohesion: 0.13
|
||||
Nodes (40): Any, bool, date, int, Session, str, Session, str (+32 more)
|
||||
|
||||
### Community 42 - "Community 42"
|
||||
Cohesion: 0.08
|
||||
Nodes (25): 技术栈, 1. 创建虚拟环境, 项目结构, 1. 创建虚拟环境, 2. 安装依赖, 2. 安装依赖, 3. 配置环境变量, 快速开始 (+17 more)
|
||||
|
||||
### Community 43 - "Community 43"
|
||||
Cohesion: 0.21
|
||||
Nodes (16): Any, int, str, BusinessException, InvalidStatusTransitionException, log_api_request(), log_delivery_operation(), log_operation() (+8 more)
|
||||
Cohesion: 0.08
|
||||
Nodes (48): Any, int, str, float, int, str, Any, int (+40 more)
|
||||
|
||||
### Community 44 - "Community 44"
|
||||
Cohesion: 0.21
|
||||
Nodes (36): DbInboundOutbound, DbUserContext, Session, str, CreateLinkForPlanPayload, _apply_inventory_product_update(), _apply_inventory_update(), _base_query() (+28 more)
|
||||
Nodes (37): DbInboundOutbound, DbUserContext, Session, str, CreateLinkForPlanPayload, _apply_inventory_product_update(), _apply_inventory_update(), _base_query() (+29 more)
|
||||
|
||||
### Community 45 - "Community 45"
|
||||
Cohesion: 0.36
|
||||
Nodes (15): create_leave(), delete_leave(), get_leave(), _leave_local_callback(), leave_workflow_callback(), list_leaves(), submit_leave(), update_leave() (+7 more)
|
||||
Cohesion: 0.27
|
||||
Nodes (17): create_leave(), delete_leave(), get_leave(), _leave_local_callback(), leave_workflow_callback(), list_leaves(), submit_leave(), update_leave() (+9 more)
|
||||
|
||||
### Community 46 - "Community 46"
|
||||
Cohesion: 0.15
|
||||
Nodes (22): clue_import_template(), customer_import_template(), product_import_template(), str, Any, bool, Session, str (+14 more)
|
||||
|
||||
### Community 47 - "Community 47"
|
||||
Cohesion: 0.05
|
||||
Nodes (82): CRM 合同 API 路由 — GET/PUT/DELETE/POST 对齐原 Java CRM, register_contract_routes(), bool, bool, 运营端角色守卫:admin / super_admin 可访问。, require_admin_or_super_admin(), batch_assign_owner(), batch_submit_approval() (+74 more)
|
||||
Cohesion: 0.24
|
||||
Nodes (10): Exception, bytes, bytes, BytesIO, convert_heic_to_jpeg(), MinIOStorage, process_image_content(), 文件存储工具模块 支持多种存储方式: 1. 本地存储 (local) - 默认 2. 七牛云存储 (qiniu) - 推荐生产环境使用 3. 阿里云OSS ( (+2 more)
|
||||
|
||||
### Community 48 - "Community 48"
|
||||
Cohesion: 0.39
|
||||
Nodes (8): bool, BxUserContext, Session, str, 按业务类型获取工作流;已知类型不存在时自动创建空定义(无节点)。, 按业务类型获取工作流;已知类型不存在时自动创建空定义(无节点)。, WorkflowService, BxWorkflowDef
|
||||
Cohesion: 0.18
|
||||
Nodes (23): BxUserContext, int, Session, str, Any, str, create_workflow(), delete_workflow() (+15 more)
|
||||
|
||||
### Community 49 - "Community 49"
|
||||
Cohesion: 0.30
|
||||
Nodes (21): Any, bool, CrmOpportunity, datetime, int, Session, str, ensure_crm_create_by_row_in_scope() (+13 more)
|
||||
Cohesion: 0.31
|
||||
Nodes (20): Any, bool, CrmOpportunity, datetime, int, Session, str, create_opportunity_follow() (+12 more)
|
||||
|
||||
### Community 50 - "Community 50"
|
||||
Cohesion: 0.12
|
||||
Nodes (31): CRM 报价单 API 路由 — 挂接到 crm.router, CRM 报价单 Pydantic Schemas, QuoteAcceptBody, QuoteApprovalApproveBody, QuoteApprovalListBody, QuoteApprovalRefreshNamesBody, QuoteApprovalWithdrawBody, QuoteCreateBody (+23 more)
|
||||
Cohesion: 0.11
|
||||
Nodes (39): CRM 报价单 API 路由 — 挂接到 crm.router, str, CRM 报价单 Pydantic Schemas, AdvanceApprovalCreate, ContractApprovalPassBody, CustomerCheckDuplicateBody, CustomerPoolDetailBody, CustomerPoolListBody (+31 more)
|
||||
|
||||
### Community 51 - "Community 51"
|
||||
Cohesion: 0.07
|
||||
Nodes (55): int, PlTransport, Session, str, UploadFile, Any, Session, str (+47 more)
|
||||
Cohesion: 0.22
|
||||
Nodes (18): bytes, int, str, download_file_bytes(), file_stream(), get_access_url(), get_download_url(), get_preview_url() (+10 more)
|
||||
|
||||
### Community 52 - "Community 52"
|
||||
Cohesion: 0.10
|
||||
Nodes (63): CrmContract, Query, Any, bool, CrmContractProductPriceTier, date, Decimal, int (+55 more)
|
||||
Cohesion: 0.16
|
||||
Nodes (33): Any, bool, CrmContractProductPriceTier, date, Decimal, int, Session, str (+25 more)
|
||||
|
||||
### Community 53 - "Community 53"
|
||||
Cohesion: 0.24
|
||||
@@ -403,8 +393,8 @@ Cohesion: 0.22
|
||||
Nodes (17): Any, bool, Session, str, list_share_logs(), _parse_user_agent(), CRM 报价单分享访问日志 CRUD — 对照 CrmQuoteShareLogServiceImpl.java, Java: statisticsVisits (+9 more)
|
||||
|
||||
### Community 55 - "Community 55"
|
||||
Cohesion: 0.20
|
||||
Nodes (30): Any, bool, CRM 产品模块 CRUD(产品分类 + 产品 + 附件), int, Session, str, barcode_exists(), category_code_exists() (+22 more)
|
||||
Cohesion: 0.04
|
||||
Nodes (114): Any, bool, CRM 产品模块 CRUD(产品分类 + 产品 + 附件), int, Session, str, SysOss, int (+106 more)
|
||||
|
||||
### Community 56 - "Community 56"
|
||||
Cohesion: 0.26
|
||||
@@ -423,28 +413,28 @@ Cohesion: 0.25
|
||||
Nodes (23): Any, bool, int, Session, str, create_role(), get_role(), get_role_data_scope() (+15 more)
|
||||
|
||||
### Community 60 - "Community 60"
|
||||
Cohesion: 0.28
|
||||
Nodes (18): Any, int, Session, str, _build_tree(), _cascade_soft_delete(), _collect_with_ancestors(), create_dept() (+10 more)
|
||||
Cohesion: 0.35
|
||||
Nodes (17): Any, bool, int, Session, str, _build_tree(), _cascade_soft_delete(), create_menu() (+9 more)
|
||||
|
||||
### Community 61 - "Community 61"
|
||||
Cohesion: 0.12
|
||||
Nodes (46): Session, str, bool, date, DbInboundOutbound, DbPurchasePlan, float, PlPlan (+38 more)
|
||||
Cohesion: 0.08
|
||||
Nodes (85): int, PlTransport, Session, str, UploadFile, bool, date, DbInboundOutbound (+77 more)
|
||||
|
||||
### Community 62 - "Community 62"
|
||||
Cohesion: 0.12
|
||||
Nodes (45): bool, DbInboundOutbound, int, PublicInventoryFeedback, PublicInventoryPageRead, Session, str, UploadFile (+37 more)
|
||||
Cohesion: 0.10
|
||||
Nodes (56): bool, DbInboundOutbound, int, PublicInventoryFeedback, PublicInventoryPageRead, Session, str, UploadFile (+48 more)
|
||||
|
||||
### Community 63 - "Community 63"
|
||||
Cohesion: 0.09
|
||||
Nodes (22): generate_auto_product_no(), Java: generateAutoProductNo — SXWL + 8位随机大写字符, Java: generateAutoProductNo — SXWL + 8位随机大写字符, Java: resolveProductIdForQuoteItem(CrmProduct 无 tax_rate,跳过税率匹配), Java: resolveProductIdForQuoteItem(CrmProduct 无 tax_rate,跳过税率匹配), Java: generateAutoProductNo — SXWL + 8位随机大写字符, Java: generateAutoProductNo — SXWL + 8位随机大写字符, Java: generateAutoProductNo — SXWL + 8位随机大写字符 (+14 more)
|
||||
Cohesion: 0.14
|
||||
Nodes (19): Session, str, Session, str, get_user_menu_codes(), 菜单 code 权限守卫(V0.3:通过 sys_user_role → sys_role_menu → sys_menu.perms), 读取用户授权的菜单权限标识集合(与前端 meta.menuCode / perms 对齐)。, 接口菜单权限守卫。super_admin 直接放行。 (+11 more)
|
||||
|
||||
### Community 64 - "Community 64"
|
||||
Cohesion: 0.27
|
||||
Nodes (11): Any, bool, str, decrypt_login_payload(), ensure_initialized(), extract_login_credentials(), get_public_pem(), 登录凭据 RSA 解密(与前端 JSEncrypt PKCS#1 v1.5 兼容)。 (+3 more)
|
||||
|
||||
### Community 65 - "Community 65"
|
||||
Cohesion: 0.33
|
||||
Nodes (10): Any, int, Session, str, list_ethnics(), 民族码表 CRUD(全局 sys_ethnic), _row_to_dict(), 民族码表 sys_ethnic(国标 GB/T 3304,全局) (+2 more)
|
||||
Cohesion: 0.39
|
||||
Nodes (8): bool, BxUserContext, Session, str, 按业务类型获取工作流;已知类型不存在时自动创建空定义(无节点)。, 按业务类型获取工作流;已知类型不存在时自动创建空定义(无节点)。, WorkflowService, BxWorkflowDef
|
||||
|
||||
### Community 66 - "Community 66"
|
||||
Cohesion: 0.14
|
||||
@@ -455,20 +445,20 @@ Cohesion: 0.16
|
||||
Nodes (33): Any, bool, int, Session, str, Any, Session, str (+25 more)
|
||||
|
||||
### Community 68 - "Community 68"
|
||||
Cohesion: 0.25
|
||||
Nodes (11): check_customer_protect_time(), _execute_single_rule(), _get_all_tenant_nos(), pool_recovery_job(), 公海池自动回收任务(由 APScheduler 调度) 遍历所有租户的启用规则,执行自动回收。, 每天凌晨1点:检查保护期到期客户,跟进次数不足则退回公海, 每天早上9点:保护期到期提醒(3天内到期且跟进不足), remind_customer_recycle() (+3 more)
|
||||
Cohesion: 0.07
|
||||
Nodes (61): Any, CertRead, date, int, Session, str, CertRead, Session (+53 more)
|
||||
|
||||
### Community 69 - "Community 69"
|
||||
Cohesion: 0.28
|
||||
Nodes (25): DbUserContext, float, Session, str, Session, str, DashboardCargoStat, DashboardPartnerCargoStat (+17 more)
|
||||
Cohesion: 0.33
|
||||
Nodes (22): DbUserContext, float, Session, str, get_db_current_user(), DashboardCargoStat, DashboardPartnerCargoStat, DashboardStats (+14 more)
|
||||
|
||||
### Community 70 - "Community 70"
|
||||
Cohesion: 0.29
|
||||
Nodes (14): is_valid_dict_type(), load_portal_baseline(), load_sx(), load_zkxw(), main(), map_tag_type(), normalize_row(), parse_insert() (+6 more)
|
||||
|
||||
### Community 71 - "Community 71"
|
||||
Cohesion: 0.08
|
||||
Nodes (51): BxUserContext, Session, str, Any, str, bool, BxUserContext, int (+43 more)
|
||||
Cohesion: 0.25
|
||||
Nodes (18): BxUserContext, Session, str, bool, BxUserContext, Session, str, create_expense_item() (+10 more)
|
||||
|
||||
### Community 72 - "Community 72"
|
||||
Cohesion: 0.34
|
||||
@@ -479,36 +469,36 @@ Cohesion: 0.07
|
||||
Nodes (100): attachment_download_url(), attachment_preview_url(), AttachmentDownloadUrlBody, create_dept(), create_dict_data(), create_dict_type(), create_menu(), create_post() (+92 more)
|
||||
|
||||
### Community 74 - "Community 74"
|
||||
Cohesion: 0.08
|
||||
Nodes (26): 分页查询供方列表 — Java: CrmSupplierInfoController.list, 分页查询供方列表 — Java: CrmSupplierInfoController.list, 分页查询供方列表 — Java: CrmSupplierInfoController.list, 分页查询供方列表 — Java: CrmSupplierInfoController.list, 分页查询供方列表 — Java: CrmSupplierInfoController.list, 分页查询供方列表 — Java: CrmSupplierInfoController.list, 分页查询供方列表 — Java: CrmSupplierInfoController.list, 分页查询供方列表 — Java: CrmSupplierInfoController.list (+18 more)
|
||||
Cohesion: 0.04
|
||||
Nodes (46): invoice_info_list(), 按客户ID查询开票信息(与 by-customer 等价), 按客户ID查询开票信息(与 by-customer 等价), 按客户ID查询开票信息(与 by-customer 等价), 按客户ID查询开票信息(与 by-customer 等价), 按客户ID查询开票信息(与 by-customer 等价), 供方全量列表(下拉)— Java: CrmSupplierInfoController.listAll, 供方全量列表(下拉)— Java: CrmSupplierInfoController.listAll (+38 more)
|
||||
|
||||
### Community 75 - "Community 75"
|
||||
Cohesion: 0.05
|
||||
Nodes (124): bool, Decimal, int, PlanCreateFull, PlPlan, PlPlanProduct, PlTransport, PlVehicle (+116 more)
|
||||
Cohesion: 0.07
|
||||
Nodes (95): bool, Decimal, int, PlanCreateFull, PlPlan, PlPlanProduct, PlTransport, PlVehicle (+87 more)
|
||||
|
||||
### Community 76 - "Community 76"
|
||||
Cohesion: 0.18
|
||||
Nodes (10): Any, Request, str, str, parse_admin_request_json(), 运营 Web(/web/admin)JSON 请求体解析。 与 ApiCryptoMiddleware 配合:中间件未改写 body 时,在路由内解密 `{, 读取 JSON body;dict 时走 resolve_request_payload(明文 / 密文 / data 包装)。 Returns:, 统一解析接口请求载荷(明文 / 密文信封)。 (+2 more)
|
||||
|
||||
### Community 77 - "Community 77"
|
||||
Cohesion: 0.34
|
||||
Nodes (23): DbUserContext, DbWeighingSlip, Session, str, weighing_to_read(), apply_product_snapshot_to_weighing(), _apply_weighing_product_update(), _apply_weighing_update() (+15 more)
|
||||
Cohesion: 0.23
|
||||
Nodes (29): DbUserContext, DbWeighingSlip, Session, str, Session, str, weighing_to_read(), apply_product_snapshot_to_weighing() (+21 more)
|
||||
|
||||
### Community 78 - "Community 78"
|
||||
Cohesion: 0.13
|
||||
Nodes (20): bool, date, Session, str, get_last_month_range(), month_range(), 地磅计划按月自动生成 — 迁移自 simple-dibang-xiaoshou/auto-create-plan/auto_create_monthly_pla, 按月自动生成采购计划。 - 默认仅在每月 1 日执行(force=True 时跳过该限制,供手动触发验收) - 遍历所有租户,按上月计划模板生 (+12 more)
|
||||
Cohesion: 0.19
|
||||
Nodes (14): bool, date, Session, str, get_last_month_range(), month_range(), 地磅计划按月自动生成 — 迁移自 simple-dibang-xiaoshou/auto-create-plan/auto_create_monthly_pla, 按月自动生成采购计划。 - 默认仅在每月 1 日执行(force=True 时跳过该限制,供手动触发验收) - 遍历所有租户,按上月计划模板生 (+6 more)
|
||||
|
||||
### Community 79 - "Community 79"
|
||||
Cohesion: 0.36
|
||||
Nodes (10): add_scope_before_order(), ensure_import(), patch_base_data(), patch_get_helper(), patch_inventory(), patch_plan(), patch_transport(), str (+2 more)
|
||||
|
||||
### Community 80 - "Community 80"
|
||||
Cohesion: 0.05
|
||||
Nodes (62): str, CRM 合同 Pydantic Schemas — 对照 CrmContractBo / CrmContractVo, ContractApprovalActionBody, ContractApprovalApproveBody, ContractApprovalByContractBody, ContractApprovalExportBody, ContractApprovalListBody, ContractApprovalListQuery (+54 more)
|
||||
Cohesion: 0.07
|
||||
Nodes (46): CRM 合同 API 路由 — GET/PUT/DELETE/POST 对齐原 Java CRM, CRM 合同 Pydantic Schemas — 对照 CrmContractBo / CrmContractVo, ContractApprovalActionBody, ContractApprovalApproveBody, ContractApprovalByContractBody, ContractApprovalExportBody, ContractApprovalListBody, ContractApprovalListQuery (+38 more)
|
||||
|
||||
### Community 81 - "Community 81"
|
||||
Cohesion: 0.25
|
||||
Nodes (8): generate_template_no(), Java: generateTemplateNo — TMPL{yyyyMMdd}{4位序号}, Java: generateTemplateNo — TMPL{yyyyMMdd}{4位序号}, Java: generateTemplateNo — TMPL{yyyyMMdd}{4位序号}, Java: generateTemplateNo — TMPL{yyyyMMdd}{4位序号}, Java: generateTemplateNo — TMPL{yyyyMMdd}{4位序号}, Java: generateTemplateNo — TMPL{yyyyMMdd}{4位序号}, Java: generateTemplateNo — TMPL{yyyyMMdd}{4位序号}
|
||||
Cohesion: 0.41
|
||||
Nodes (13): BxUserContext, Session, str, UploadFile, bind_biz(), delete_attachment(), download_file(), list_by_biz() (+5 more)
|
||||
|
||||
### Community 82 - "Community 82"
|
||||
Cohesion: 0.29
|
||||
@@ -523,28 +513,28 @@ Cohesion: 0.41
|
||||
Nodes (11): Any, str, build_quote_html(), _default_value(), _format_date(), _format_discount(), _format_money(), generate_quote_pdf() (+3 more)
|
||||
|
||||
### Community 85 - "Community 85"
|
||||
Cohesion: 0.26
|
||||
Nodes (49): InboundOutboundRead, bool, DbPurchasePlan, DbUserContext, PurchasePlanRead, PurchasePlanWithFillLink, Session, str (+41 more)
|
||||
Cohesion: 0.23
|
||||
Nodes (47): InboundOutboundRead, bool, DbPurchasePlan, DbUserContext, PurchasePlanRead, PurchasePlanWithFillLink, WeighingSlipRead, bool (+39 more)
|
||||
|
||||
### Community 86 - "Community 86"
|
||||
Cohesion: 0.18
|
||||
Nodes (11): float, int, str, 发送订单状态通知 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 (+3 more)
|
||||
Cohesion: 0.30
|
||||
Nodes (13): Any, bool, bytes, int, str, ApiCryptoError, _b64url_decode(), _b64url_encode() (+5 more)
|
||||
|
||||
### Community 87 - "Community 87"
|
||||
Cohesion: 0.22
|
||||
Nodes (12): Any, bool, str, WfProcessInstance, _LocalCallbackFn, _dispatch_local(), 工作流回调服务 —— 流程状态变更时通知业务系统, 注册本地回调函数。当 callback_url 以 path 开头时直接调用 fn。 (+4 more)
|
||||
Cohesion: 0.19
|
||||
Nodes (38): ProcessDefCreateBody, ProcessDefUpdateBody, PublishVersionBody, StartInstanceBody, Any, bool, WfProcessInstance, bool (+30 more)
|
||||
|
||||
### Community 88 - "Community 88"
|
||||
Cohesion: 0.18
|
||||
Nodes (11): generate_contract_no(), Java: generateContractNo — {供方编码}/X-JT + yyyyMMdd + 3位流水, Java: generateContractNo — {供方编码}/X-JT + yyyyMMdd + 3位流水, Java: generateContractNo — {供方编码}/X-JT{yyyyMMdd}{3位序号}, Java: generateContractNo — {供方编码}/X-JT{yyyyMMdd}{3位序号}, Java: generateContractNo — {供方编码}/X-JT{yyyyMMdd}{3位序号}, Java: generateContractNo — {供方编码}/X-JT{yyyyMMdd}{3位序号}, Java: generateContractNo — {供方编码}/X-JT{yyyyMMdd}{3位序号} (+3 more)
|
||||
Cohesion: 0.25
|
||||
Nodes (11): check_customer_protect_time(), _execute_single_rule(), _get_all_tenant_nos(), pool_recovery_job(), 公海池自动回收任务(由 APScheduler 调度) 遍历所有租户的启用规则,执行自动回收。, 每天凌晨1点:检查保护期到期客户,跟进次数不足则退回公海, 每天早上9点:保护期到期提醒(3天内到期且跟进不足), remind_customer_recycle() (+3 more)
|
||||
|
||||
### Community 89 - "Community 89"
|
||||
Cohesion: 0.14
|
||||
Nodes (14): _build_category_tree(), _collect_category_with_ancestors(), list_category_tree(), 获取产品分类树(嵌套 children,供树形表格/树选择器使用), 获取产品分类树(嵌套 children,供树形表格/树选择器使用), 获取产品分类树(嵌套 children,供树形表格/树选择器使用), 按 parentId 将扁平分类列表组装为嵌套树, 获取产品分类树(嵌套 children,供树形表格/树选择器使用) (+6 more)
|
||||
Cohesion: 0.20
|
||||
Nodes (6): int, MinIO 预签名下载 URL,携带原始文件名。, MinIO 预签名下载 URL,携带原始文件名。, MinIO 预签名内联预览 URL(PDF 等 iframe 场景)。, 带下载文件名的访问 URL;默认实现为普通访问地址。, 浏览器内联预览 URL;默认实现为普通访问地址。
|
||||
|
||||
### Community 90 - "Community 90"
|
||||
Cohesion: 0.22
|
||||
Nodes (18): Any, bool, bytes, int, Request, str, _claims_from_auth(), _format_body_bytes() (+10 more)
|
||||
Cohesion: 0.16
|
||||
Nodes (26): Any, bool, bytes, int, Request, str, bool, int (+18 more)
|
||||
|
||||
### Community 91 - "Community 91"
|
||||
Cohesion: 0.32
|
||||
@@ -555,8 +545,8 @@ Cohesion: 0.34
|
||||
Nodes (16): Any, bool, int, Session, str, create_dict_type(), dict_type_code_exists(), get_dict_type() (+8 more)
|
||||
|
||||
### Community 93 - "Community 93"
|
||||
Cohesion: 0.15
|
||||
Nodes (22): int, Session, str, bool, datetime, DbWeighingSlip, Session, str (+14 more)
|
||||
Cohesion: 0.23
|
||||
Nodes (14): int, Session, str, _allocate_inbound_outbound_id(), _ensure_sequence_row(), max_numeric_inbound_outbound_id(), next_inbound_outbound_id(), 出入库单 id 生成 — 租户内纯数字自增,并发安全。 (+6 more)
|
||||
|
||||
### Community 94 - "Community 94"
|
||||
Cohesion: 0.18
|
||||
@@ -571,112 +561,80 @@ Cohesion: 0.34
|
||||
Nodes (16): Any, bool, int, Session, str, create_tenant(), get_tenant(), list_tenants() (+8 more)
|
||||
|
||||
### Community 97 - "Community 97"
|
||||
Cohesion: 0.24
|
||||
Nodes (27): Any, bool, CRM 公海池规则 CRUD + 规则执行逻辑, int, Session, str, create_pool_rule(), create_rule() (+19 more)
|
||||
Cohesion: 0.21
|
||||
Nodes (30): pool_rule_toggle(), Any, bool, CRM 公海池规则 CRUD + 规则执行逻辑, int, Session, str, create_pool_rule() (+22 more)
|
||||
|
||||
### Community 98 - "Community 98"
|
||||
Cohesion: 0.38
|
||||
Nodes (15): Any, bool, int, Session, str, create_post(), get_post(), list_posts() (+7 more)
|
||||
|
||||
### Community 99 - "Community 99"
|
||||
Cohesion: 0.21
|
||||
Nodes (9): API 路由包 — 山峡化工生产一体化门户, get_menu_list(), 运营端菜单接口(与 web-admin 前端静态路由 sqxp.ts 对齐,14 个一级菜单), operlog_detail(), operlog_list(), 操作日志分页列表(基于 API 请求日志适配 RuoYi OperLog 结构), 操作日志分页列表(基于 API 请求日志适配 RuoYi OperLog 结构), _user_info() (+1 more)
|
||||
Cohesion: 0.07
|
||||
Nodes (29): BaseModel, AdvanceApplyOut, AttachmentOut, ReimburseApplyCreate, Config, PageResult, ResponseModel, ContactByCustomerBody (+21 more)
|
||||
|
||||
### Community 100 - "Community 100"
|
||||
Cohesion: 0.45
|
||||
Nodes (12): bool, date, PlCarrier, str, assert_carrier_eligible_for_transport(), carrier_agreement_expired(), carrier_audit_approved(), carrier_eligible_for_transport() (+4 more)
|
||||
Cohesion: 0.20
|
||||
Nodes (10): create_from_quote(), _quote_products_to_contract_products(), Java: createFromQuote — 返回合同 id, Java: createFromQuote — 返回合同 id, Java: createFromQuote — 返回合同 id, Java: createFromQuote — 返回合同 id, Java: createFromQuote — 返回合同 id, Java: createFromQuote — 返回合同 id (+2 more)
|
||||
|
||||
### Community 101 - "Community 101"
|
||||
Cohesion: 0.40
|
||||
Nodes (3): BaseSettings, Config, Settings
|
||||
|
||||
### Community 102 - "Community 102"
|
||||
Cohesion: 0.60
|
||||
Nodes (10): Any, Session, str, _apply_create_range(), _base_query(), get_clue_level_statistics(), get_clue_overview_statistics(), get_clue_owner_statistics() (+2 more)
|
||||
Cohesion: 0.53
|
||||
Nodes (11): Any, Session, str, _apply_create_range(), _base_query(), get_clue_level_statistics(), get_clue_overview_statistics(), get_clue_owner_statistics() (+3 more)
|
||||
|
||||
### Community 103 - "Community 103"
|
||||
Cohesion: 0.08
|
||||
Nodes (158): date, DbUserContext, int, PlanCreateFull, PlPlan, Session, str, bool (+150 more)
|
||||
Cohesion: 0.07
|
||||
Nodes (141): bool, PlCarrier, PlVehicle, VehicleCreate, VehicleRead, VehicleUpdate, bool, DbUserContext (+133 more)
|
||||
|
||||
### Community 104 - "Community 104"
|
||||
Cohesion: 0.13
|
||||
Nodes (38): bool, Redis, Request, str, _T, bool, Redis, decode_token() (+30 more)
|
||||
Cohesion: 0.11
|
||||
Nodes (49): 公共接口(Web 登录、刷新 Token、登出;用户模型对齐 docs/sql/shanxia2.0.sql 基线), refresh_token(), bool, Redis, Request, Session, str, _T (+41 more)
|
||||
|
||||
### Community 105 - "Community 105"
|
||||
Cohesion: 0.38
|
||||
Nodes (15): Any, bool, int, Session, str, create_site(), get_site(), list_sites() (+7 more)
|
||||
|
||||
### Community 106 - "Community 106"
|
||||
Cohesion: 0.13
|
||||
Nodes (15): patch_plan_product_certs(), 更新计划危化品产品购买证/运输证(不触发安排运输), 更新计划危化品产品购买证/运输证(不触发安排运输), 更新计划危化品产品购买证/运输证(不触发安排运输), 更新计划危化品产品购买证/运输证(不触发安排运输), 更新计划危化品产品购买证/运输证(不触发安排运输), 更新计划危化品产品购买证/运输证(不触发安排运输), 更新计划危化品产品购买证/运输证(不触发安排运输) (+7 more)
|
||||
|
||||
### Community 107 - "Community 107"
|
||||
Cohesion: 0.22
|
||||
Nodes (9): create_from_opportunity(), _opportunity_products_to_contract_products(), Java: createFromOpportunity — 返回合同 id, Java: createFromOpportunity — 返回合同 id, Java: createFromOpportunity — 返回合同 id, Java: createFromOpportunity — 返回合同 id, Java: createFromOpportunity — 返回合同 id, Java: createFromOpportunity — 返回合同 id (+1 more)
|
||||
|
||||
### Community 108 - "Community 108"
|
||||
Cohesion: 0.44
|
||||
Nodes (8): bool, int, Session, str, ApiRequestLogCRUD, SysApiRequestLog, SysApiRequestLogHis, SysApiRequestLog
|
||||
|
||||
### Community 109 - "Community 109"
|
||||
Cohesion: 0.22
|
||||
Nodes (9): Java: renewContract — 基础续签, Java: renewContract — 基础续签, Java: renewContract — 基础续签, Java: renewContract — 基础续签, Java: renewContract — 基础续签, Java: renewContract — 基础续签, Java: renewContract — 基础续签, Java: renewContract — 基础续签 (+1 more)
|
||||
|
||||
### Community 111 - "Community 111"
|
||||
Cohesion: 0.07
|
||||
Nodes (27): Session, Any, Session, str, Session, str, get_current_user(), get_current_user_by_token() (+19 more)
|
||||
|
||||
### Community 112 - "Community 112"
|
||||
Cohesion: 0.17
|
||||
Nodes (12): generate_product_no(), 生成产品编号(格式:CP + yyyyMMdd + 3位序号,如 CP20260530001), 生成产品编号(格式:CP + yyyyMMdd + 3位序号,如 CP20260530001), 生成产品编号(格式:CP + yyyyMMdd + 3位序号,如 CP20260530001), 生成产品编号(格式:CP + yyyyMMdd + 3位序号,如 CP20260530001), 生成产品编号(格式:CP + yyyyMMdd + 3位序号,如 CP20260530001), 生成产品编号(格式:CP + yyyyMMdd + 3位序号,如 CP20260530001), 生成产品编号(格式:CP + yyyyMMdd + 3位序号,如 CP20260530001) (+4 more)
|
||||
|
||||
### Community 122 - "Community 122"
|
||||
Cohesion: 0.40
|
||||
Nodes (10): SysOss, int, Session, str, SysOss, create_oss(), create_oss(), get_oss_by_url() (+2 more)
|
||||
|
||||
### Community 123 - "Community 123"
|
||||
Cohesion: 0.33
|
||||
Nodes (8): Decimal, str, derive_legacy_status_from_three(), derive_three_status_from_legacy(), 阶段A兼容:从旧 status 推导三字段。 约束:旧 status 本身混合了支付/配送/订单生命周期,因此只能尽力推断; 若业务侧已有更精确, 阶段A兼容:从三字段反推旧 status(用于旧前端/旧筛选参数兼容)。, ThreeStatus, _to_decimal()
|
||||
|
||||
### Community 124 - "Community 124"
|
||||
Cohesion: 0.28
|
||||
Nodes (8): date, datetime, str, _date_to_str(), _dt_to_str(), PagedQuery, PagedResponse, bx 原系统 API 响应包装(code=0 / msg / data.rows),与门户 success_response 分离。
|
||||
### Community 107 - "Community 107"
|
||||
Cohesion: 0.34
|
||||
Nodes (25): BxUserContext, Session, str, ApiResponse, approve_reimburse(), approved_by_me(), _build_details_data(), create_reimburse() (+17 more)
|
||||
|
||||
### Community 108 - "Community 108"
|
||||
Cohesion: 0.40
|
||||
Nodes (3): API 路由包 — 山峡化工生产一体化门户, get_menu_list(), 运营端菜单接口(与 web-admin 前端静态路由 sqxp.ts 对齐,14 个一级菜单)
|
||||
|
||||
### Community 111 - "Community 111"
|
||||
Cohesion: 0.25
|
||||
Nodes (7): Any, Session, str, get_db_current_user_optional(), 与门户 JWT / apply_data_scope 所需的 user_info 字段对齐。, 有 Bearer 且为 admin/super_admin 时返回上下文,否则 None(供三方 H5 无登录调用)。, 有 Bearer 且为 admin/super_admin 时返回上下文,否则 None(供三方 H5 无登录调用)。
|
||||
|
||||
### Community 125 - "Community 125"
|
||||
Cohesion: 0.23
|
||||
Nodes (10): Any, bool, int, Session, str, camel_to_snake(), CRUDBase, get_model_attr() (+2 more)
|
||||
|
||||
### Community 126 - "Community 126"
|
||||
Cohesion: 0.22
|
||||
Nodes (9): _category_row_to_dict(), list_categories(), 分类 ORM 对象转 dict(camelCase 键名), 分类 ORM 对象转 dict(camelCase 键名), 分类 ORM 对象转 dict(camelCase 键名), 分类 ORM 对象转 dict(camelCase 键名), 获取分类(扁平列表,支持名称模糊与状态筛选), 获取分类(扁平列表,支持名称模糊与状态筛选) (+1 more)
|
||||
Nodes (11): Any, bool, int, Session, str, generate_id(), camel_to_snake(), CRUDBase (+3 more)
|
||||
|
||||
### Community 127 - "Community 127"
|
||||
Cohesion: 0.29
|
||||
Nodes (10): datetime, int, Session, str, BizNoKind, allocate_next_biz_no(), _format_biz_no(), _prefix_for() (+2 more)
|
||||
|
||||
### Community 128 - "Community 128"
|
||||
Cohesion: 0.20
|
||||
Nodes (10): create_from_quote(), _quote_products_to_contract_products(), Java: createFromQuote — 返回合同 id, Java: createFromQuote — 返回合同 id, Java: createFromQuote — 返回合同 id, Java: createFromQuote — 返回合同 id, Java: createFromQuote — 返回合同 id, Java: createFromQuote — 返回合同 id (+2 more)
|
||||
|
||||
### Community 129 - "Community 129"
|
||||
Cohesion: 0.22
|
||||
Nodes (9): generate_barcode(), 生成唯一条码(纯数字:yyyyMMdd + 6位序号,如 20260606000001), 生成唯一条码(纯数字:yyyyMMdd + 6位序号,如 20260606000001), 生成唯一条码(纯数字:yyyyMMdd + 6位序号,如 20260606000001), 生成唯一条码(纯数字:yyyyMMdd + 6位序号,如 20260606000001), 生成唯一条码(纯数字:yyyyMMdd + 6位序号,如 20260606000001), 生成唯一条码(纯数字:yyyyMMdd + 6位序号,如 20260606000001), 生成唯一条码(纯数字:yyyyMMdd + 6位序号,如 20260606000001) (+1 more)
|
||||
Nodes (12): Any, bool, str, WfProcessInstance, _LocalCallbackFn, _dispatch_local(), 工作流回调服务 —— 流程状态变更时通知业务系统, 注册本地回调函数。当 callback_url 以 path 开头时直接调用 fn。 (+4 more)
|
||||
|
||||
### Community 130 - "Community 130"
|
||||
Cohesion: 0.10
|
||||
Nodes (32): _avatar_url_field(), _build_user_info_payload(), get_current_user_profile(), get_web_auth_menu_codes(), _jwt_user_payload(), 公共接口(Web 登录、刷新 Token、登出;用户模型对齐 docs/sql/shanxia2.0.sql 基线), 获取当前用户信息(与 sys_user 字段对应)。, 获取当前用户信息(与 sys_user 字段对应)。 (+24 more)
|
||||
Cohesion: 0.08
|
||||
Nodes (30): _avatar_url_field(), _build_user_info_payload(), get_current_user_profile(), get_web_auth_menu_codes(), _jwt_user_payload(), logout(), 登出:吊销 Redis 会话(若已配置 REDIS_HOST)。, 登出:吊销 Redis 会话(若已配置 REDIS_HOST)。 (+22 more)
|
||||
|
||||
### Community 131 - "Community 131"
|
||||
Cohesion: 0.11
|
||||
Nodes (33): bool, Session, str, _append_user_scope_conditions(), apply_data_scope(), assert_row_in_data_scope(), _collect_scope_ids(), _get_child_dept_ids() (+25 more)
|
||||
Cohesion: 0.08
|
||||
Nodes (43): bool, Session, str, Any, int, Session, str, _append_user_scope_conditions() (+35 more)
|
||||
|
||||
### Community 132 - "Community 132"
|
||||
Cohesion: 0.40
|
||||
Nodes (5): bool, str, get_password_hash(), 验证密码(哈希非 bcrypt 或损坏时返回 False,避免抛错), verify_password()
|
||||
|
||||
### Community 133 - "Community 133"
|
||||
Cohesion: 0.32
|
||||
Nodes (5): AdvanceApplyCreate, AdvanceApplyOut, AdvanceApplyUpdate, AdvanceApprovalCreate, _coerce_optional_id()
|
||||
Cohesion: 0.28
|
||||
Nodes (8): date, datetime, str, _date_to_str(), _dt_to_str(), PagedQuery, PagedResponse, bx 原系统 API 响应包装(code=0 / msg / data.rows),与门户 success_response 分离。
|
||||
|
||||
### Community 134 - "Community 134"
|
||||
Cohesion: 0.38
|
||||
@@ -687,32 +645,24 @@ Cohesion: 0.25
|
||||
Nodes (7): 1. 上传到服务器, 2. 服务器首次启动, 3. 日常运维, 4. 配置文件, 5. 与 Nginx 配合, 6. 常见问题, shanxia2.0 Backend 生产部署说明
|
||||
|
||||
### Community 136 - "Community 136"
|
||||
Cohesion: 0.29
|
||||
Nodes (7): _product_row_to_dict(), 产品 ORM 对象转 dict(camelCase 键名), 产品 ORM 对象转 dict(camelCase 键名), 产品 ORM 对象转 dict(camelCase 键名), 产品 ORM 对象转 dict(camelCase 键名), 产品 ORM 对象转 dict(camelCase 键名), 产品 ORM 对象转 dict(camelCase 键名)
|
||||
|
||||
### Community 137 - "Community 137"
|
||||
Cohesion: 0.13
|
||||
Nodes (15): _generate_plan_no(), 生成计划编号 SXDDHYYYYMMDD######, 生成计划编号 SXDDHYYYYMMDD######, 生成计划编号 SXDDHYYYYMMDD######, 生成计划编号 SXDDHYYYYMMDD######, 生成计划编号 SXDDHYYYYMMDD######, 生成计划编号 SXDDHYYYYMMDD######, 生成计划编号 SXDDHYYYYMMDD###### (+7 more)
|
||||
Cohesion: 0.50
|
||||
Nodes (4): Web 端登录用 RSA 公钥(PKCS#1 v1.5,与 JSEncrypt 一致), Web 端登录用 RSA 公钥(PKCS#1 v1.5,与 JSEncrypt 一致), Web 端登录用 RSA 公钥(PKCS#1 v1.5,与 JSEncrypt 一致), web_login_rsa_public_key()
|
||||
|
||||
### Community 138 - "Community 138"
|
||||
Cohesion: 0.39
|
||||
Nodes (6): _inject_user_info_after_tenant(), patch_contract_routes(), patch_quote_routes(), patch_route_file(), Path, str
|
||||
|
||||
### Community 139 - "Community 139"
|
||||
Cohesion: 0.20
|
||||
Nodes (10): 解析司机信息:优先 pl_personnel,兼容旧 pl_driver。, 解析司机信息:优先 pl_personnel,兼容旧 pl_driver。, 解析司机信息:优先 pl_personnel,兼容旧 pl_driver。, 解析司机信息:优先 pl_personnel,兼容旧 pl_driver。, 解析司机信息:优先 pl_personnel,兼容旧 pl_driver。, 解析司机信息:优先 pl_personnel,兼容旧 pl_driver。, 解析司机信息:优先 pl_personnel,兼容旧 pl_driver。, 解析司机信息:优先 pl_personnel,兼容旧 pl_driver。 (+2 more)
|
||||
|
||||
### Community 140 - "Community 140"
|
||||
Cohesion: 0.83
|
||||
Nodes (3): _add_user_info_kwarg(), patch_file(), str
|
||||
|
||||
### Community 141 - "Community 141"
|
||||
Cohesion: 0.29
|
||||
Nodes (7): get_third_party_plan_page(), 三方 H5 填写页初始化数据(无需门户登录,与原系统一致)。, 三方 H5 填写页初始化数据(无需门户登录,与原系统一致)。, 三方 H5 填写页初始化数据(无需门户登录,与原系统一致)。, 三方 H5 填写页初始化数据(无需门户登录,与原系统一致)。, 三方 H5 填写页初始化数据(无需门户登录,与原系统一致)。, 三方 H5 填写页初始化数据(无需门户登录,与原系统一致)。
|
||||
Cohesion: 0.25
|
||||
Nodes (8): generate_template_no(), Java: generateTemplateNo — TMPL{yyyyMMdd}{4位序号}, Java: generateTemplateNo — TMPL{yyyyMMdd}{4位序号}, Java: generateTemplateNo — TMPL{yyyyMMdd}{4位序号}, Java: generateTemplateNo — TMPL{yyyyMMdd}{4位序号}, Java: generateTemplateNo — TMPL{yyyyMMdd}{4位序号}, Java: generateTemplateNo — TMPL{yyyyMMdd}{4位序号}, Java: generateTemplateNo — TMPL{yyyyMMdd}{4位序号}
|
||||
|
||||
### Community 143 - "Community 143"
|
||||
Cohesion: 0.32
|
||||
Nodes (22): Any, CrmOpportunity, Decimal, int, Session, str, batch_add_opportunity_products(), _calc_subtotal() (+14 more)
|
||||
Cohesion: 0.18
|
||||
Nodes (31): Any, CrmOpportunity, Decimal, int, Session, str, create_from_opportunity(), _opportunity_products_to_contract_products() (+23 more)
|
||||
|
||||
### Community 144 - "Community 144"
|
||||
Cohesion: 0.17
|
||||
@@ -722,37 +672,37 @@ Nodes (20): Any, int, str, get_oper_log_meta(), OperLogMeta, 请求级操作日
|
||||
Cohesion: 0.33
|
||||
Nodes (3): 地磅 API 路由聚合 — REST 风格,协议对齐原 simple-dibang-xiaoshou。, 地磅(db)Pydantic schemas — 对齐原 simple-dibang-xiaoshou 契约。, 地磅 API 路由聚合 — REST 风格,协议对齐原 simple-dibang-xiaoshou。
|
||||
|
||||
### Community 146 - "Community 146"
|
||||
Cohesion: 0.47
|
||||
Nodes (5): OperLogDetailBody, OperLogListBody, 操作日志(兼容 RuoYi OperLog 视图), OperLogDetailBody, OperLogListBody
|
||||
|
||||
### Community 147 - "Community 147"
|
||||
Cohesion: 0.18
|
||||
Nodes (26): Any, bool, int, Session, str, Session, str, _build_tree() (+18 more)
|
||||
### Community 149 - "Community 149"
|
||||
Cohesion: 0.13
|
||||
Nodes (19): operlog_detail(), operlog_list(), 操作日志分页列表(基于 API 请求日志适配 RuoYi OperLog 结构), 操作日志分页列表(基于 API 请求日志适配 RuoYi OperLog 结构), _user_info(), Session, _auto_fill_create_dept_id(), get_db() (+11 more)
|
||||
|
||||
### Community 150 - "Community 150"
|
||||
Cohesion: 0.33
|
||||
Nodes (3): _coerce_id(), OffsetItem, ReimburseDetailCreate
|
||||
|
||||
### Community 151 - "Community 151"
|
||||
Cohesion: 0.50
|
||||
Nodes (4): datetime, object, parse_optional_plan_datetime(), 运输计划相关日期时间解析:前端 date-picker 仅传 YYYY-MM-DD。
|
||||
|
||||
## Knowledge Gaps
|
||||
- **69 isolated node(s):** `Session`, `Request`, `bool`, `bool`, `Config` (+64 more)
|
||||
- **70 isolated node(s):** `Session`, `Request`, `bool`, `bool`, `Config` (+65 more)
|
||||
These have ≤1 connection - possible missing edges or undocumented components.
|
||||
- **2 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 `Session` connect `Community 3` to `Community 0`, `Community 1`, `Community 2`, `Community 4`, `Community 6`, `Community 7`, `Community 8`, `Community 9`, `Community 10`, `Community 11`, `Community 13`, `Community 14`, `Community 15`, `Community 16`, `Community 17`, `Community 18`, `Community 19`, `Community 20`, `Community 21`, `Community 22`, `Community 23`, `Community 27`, `Community 28`, `Community 29`, `Community 31`, `Community 33`, `Community 35`, `Community 36`, `Community 37`, `Community 40`, `Community 41`, `Community 44`, `Community 45`, `Community 46`, `Community 47`, `Community 48`, `Community 49`, `Community 52`, `Community 54`, `Community 55`, `Community 56`, `Community 57`, `Community 59`, `Community 60`, `Community 61`, `Community 63`, `Community 65`, `Community 66`, `Community 67`, `Community 69`, `Community 71`, `Community 73`, `Community 74`, `Community 75`, `Community 77`, `Community 78`, `Community 81`, `Community 85`, `Community 88`, `Community 89`, `Community 92`, `Community 93`, `Community 95`, `Community 96`, `Community 97`, `Community 98`, `Community 99`, `Community 102`, `Community 103`, `Community 105`, `Community 107`, `Community 108`, `Community 109`, `Community 111`, `Community 112`, `Community 122`, `Community 125`, `Community 126`, `Community 127`, `Community 128`, `Community 129`, `Community 130`, `Community 131`, `Community 137`, `Community 141`, `Community 143`, `Community 146`, `Community 147`?**
|
||||
_High betweenness centrality (0.204) - this node is a cross-community bridge._
|
||||
- **Why does `BaseModel` connect `Community 80` to `Community 0`, `Community 2`, `Community 133`, `Community 134`, `Community 7`, `Community 9`, `Community 11`, `Community 12`, `Community 146`, `Community 19`, `Community 148`, `Community 21`, `Community 20`, `Community 23`, `Community 150`, `Community 25`, `Community 24`, `Community 28`, `Community 30`, `Community 32`, `Community 44`, `Community 45`, `Community 47`, `Community 50`, `Community 51`, `Community 62`, `Community 69`, `Community 71`, `Community 73`, `Community 75`, `Community 77`, `Community 85`, `Community 103`, `Community 124`?**
|
||||
_High betweenness centrality (0.067) - this node is a cross-community bridge._
|
||||
- **Why does `datetime` connect `Community 39` to `Community 0`, `Community 2`, `Community 3`, `Community 4`, `Community 5`, `Community 6`, `Community 9`, `Community 10`, `Community 11`, `Community 12`, `Community 13`, `Community 14`, `Community 143`, `Community 16`, `Community 17`, `Community 15`, `Community 147`, `Community 148`, `Community 21`, `Community 22`, `Community 23`, `Community 24`, `Community 25`, `Community 27`, `Community 30`, `Community 31`, `Community 32`, `Community 33`, `Community 34`, `Community 35`, `Community 36`, `Community 37`, `Community 38`, `Community 40`, `Community 41`, `Community 43`, `Community 44`, `Community 47`, `Community 49`, `Community 50`, `Community 51`, `Community 52`, `Community 54`, `Community 55`, `Community 56`, `Community 59`, `Community 60`, `Community 61`, `Community 62`, `Community 67`, `Community 68`, `Community 69`, `Community 75`, `Community 77`, `Community 78`, `Community 80`, `Community 84`, `Community 85`, `Community 92`, `Community 93`, `Community 95`, `Community 96`, `Community 97`, `Community 98`, `Community 100`, `Community 102`, `Community 103`, `Community 104`, `Community 105`, `Community 108`, `Community 124`, `Community 127`?**
|
||||
_High betweenness centrality (0.058) - this node is a cross-community bridge._
|
||||
- **Why does `Session` connect `Community 3` to `Community 0`, `Community 1`, `Community 2`, `Community 4`, `Community 6`, `Community 8`, `Community 9`, `Community 10`, `Community 11`, `Community 14`, `Community 15`, `Community 16`, `Community 17`, `Community 18`, `Community 19`, `Community 20`, `Community 22`, `Community 23`, `Community 25`, `Community 27`, `Community 28`, `Community 31`, `Community 33`, `Community 35`, `Community 36`, `Community 37`, `Community 40`, `Community 41`, `Community 43`, `Community 44`, `Community 45`, `Community 46`, `Community 48`, `Community 49`, `Community 52`, `Community 54`, `Community 55`, `Community 56`, `Community 57`, `Community 59`, `Community 60`, `Community 61`, `Community 63`, `Community 65`, `Community 66`, `Community 67`, `Community 69`, `Community 71`, `Community 73`, `Community 74`, `Community 77`, `Community 78`, `Community 81`, `Community 85`, `Community 87`, `Community 90`, `Community 92`, `Community 93`, `Community 95`, `Community 96`, `Community 97`, `Community 98`, `Community 99`, `Community 100`, `Community 102`, `Community 103`, `Community 104`, `Community 105`, `Community 107`, `Community 111`, `Community 125`, `Community 127`, `Community 130`, `Community 131`, `Community 141`, `Community 143`, `Community 149`?**
|
||||
_High betweenness centrality (0.217) - this node is a cross-community bridge._
|
||||
- **Why does `BaseModel` connect `Community 50` to `Community 0`, `Community 2`, `Community 132`, `Community 134`, `Community 7`, `Community 11`, `Community 12`, `Community 15`, `Community 19`, `Community 148`, `Community 20`, `Community 150`, `Community 23`, `Community 149`, `Community 21`, `Community 25`, `Community 24`, `Community 29`, `Community 30`, `Community 32`, `Community 35`, `Community 44`, `Community 45`, `Community 48`, `Community 61`, `Community 62`, `Community 68`, `Community 69`, `Community 71`, `Community 73`, `Community 75`, `Community 77`, `Community 80`, `Community 85`, `Community 87`, `Community 99`, `Community 103`, `Community 107`?**
|
||||
_High betweenness centrality (0.074) - this node is a cross-community bridge._
|
||||
- **Why does `SysUser` connect `Community 87` to `Community 130`, `Community 131`, `Community 2`, `Community 3`, `Community 6`, `Community 8`, `Community 10`, `Community 12`, `Community 14`, `Community 16`, `Community 17`, `Community 18`, `Community 19`, `Community 22`, `Community 23`, `Community 24`, `Community 27`, `Community 32`, `Community 33`, `Community 35`, `Community 36`, `Community 39`, `Community 43`, `Community 52`, `Community 56`, `Community 69`, `Community 73`, `Community 90`, `Community 103`, `Community 104`?**
|
||||
_High betweenness centrality (0.057) - this node is a cross-community bridge._
|
||||
- **Are the 251 inferred relationships involving `str` (e.g. with `AdvanceApplyCreate` and `AdvanceApplyOut`) actually correct?**
|
||||
_`str` has 251 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **Are the 248 inferred relationships involving `Session` (e.g. with `AdvanceApplyCreate` and `AdvanceApplyOut`) actually correct?**
|
||||
_`Session` has 248 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **Are the 140 inferred relationships involving `int` (e.g. with `AdvanceApplyCreate` and `AdvanceApplyOut`) actually correct?**
|
||||
_`int` has 140 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **What connects `定期 ping MySQL,避免远程库/NAT 回收空闲 TCP 后首请求长时间卡住。`, `数据库异常:返回 JSON 并保留 CORS 头,避免浏览器只报跨域错误`, `未捕获异常:统一 JSON 500,确保跨域场景下仍有 Access-Control-Allow-Origin` to the rest of the system?**
|
||||
_1298 weakly-connected nodes found - possible documentation gaps or missing edges._
|
||||
- **Are the 28 inferred relationships involving `get_tenant_no()` (e.g. with `category_tree()` and `contact_create()`) actually correct?**
|
||||
_`get_tenant_no()` has 28 INFERRED edges - model-reasoned connections that need verification._
|
||||
+1
-1
@@ -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":1953,"mtime_ns":1780805101147405418,"hash":"d2a416ff9daedad88780cbe3f14ea75e8299044f8787afb60e731f43963dc405"},"/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":6698,"mtime_ns":1782790876685112837,"hash":"9d955a6baa70f78ddfcf29407d9711915a04e59a7f616254d06d25d25e8d0327"},"/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":1953,"mtime_ns":1780805101147405418,"hash":"d2a416ff9daedad88780cbe3f14ea75e8299044f8787afb60e731f43963dc405"},"/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":6698,"mtime_ns":1782790876685112837,"hash":"9d955a6baa70f78ddfcf29407d9711915a04e59a7f616254d06d25d25e8d0327"},"/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
|
||||
+4280
-1688
File diff suppressed because it is too large.
Load diff
@@ -2766,9 +2766,13 @@ CREATE TABLE `pl_plan` (
|
||||
`settlement_method` varchar(50) DEFAULT NULL COMMENT '结算方式',
|
||||
`has_freight` smallint(6) NOT NULL DEFAULT '0' COMMENT '是否有运输费用 0-否 1-是',
|
||||
`pickup_address` varchar(500) DEFAULT NULL COMMENT '取货地址',
|
||||
`pickup_lng` decimal(10,6) DEFAULT NULL COMMENT '取货经度',
|
||||
`pickup_lat` decimal(10,6) DEFAULT NULL COMMENT '取货纬度',
|
||||
`pickup_time` datetime DEFAULT NULL COMMENT '取货时间',
|
||||
`pickup_notes` varchar(500) DEFAULT NULL COMMENT '取货注意事项',
|
||||
`destination_address` varchar(500) DEFAULT NULL COMMENT '目的地地址',
|
||||
`destination_lng` decimal(10,6) DEFAULT NULL COMMENT '送货经度',
|
||||
`destination_lat` decimal(10,6) DEFAULT NULL COMMENT '送货纬度',
|
||||
`delivery_time` datetime DEFAULT NULL COMMENT '送达时间',
|
||||
`destination_notes` varchar(500) DEFAULT NULL COMMENT '目的地注意事项',
|
||||
`transport_id` varchar(64) DEFAULT NULL COMMENT '运输ID',
|
||||
@@ -3041,6 +3045,38 @@ INSERT INTO `pl_remittance_voucher` (`id`, `settlement_id`, `voucher_no`, `remit
|
||||
INSERT INTO `pl_remittance_voucher` (`id`, `settlement_id`, `voucher_no`, `remittance_date`, `remittance_amount`, `voucher_urls`, `tenant_no`, `order_num`, `remark`, `create_by`, `create_dept_id`, `create_time`, `update_by`, `update_time`, `del_flag`) VALUES ('8BCB0652B9414F7EAD6EA2A4E752ECF3', '73854CC0B7B64AFE8C983120551420BB', NULL, '2026-06-28', 306.00, '[\"pl/freight-voucher/202606/28/58a088fd1641474c856a3dc71c0397ae.jpeg\"]', 'shanxia2.0', NULL, '123', 'd3000000000000000000000000000001', 'b13c2be2-4e71-11f1-9f47-00163e3bdd96', '2026-06-28 17:04:52', NULL, '2026-06-28 17:04:52', 0);
|
||||
COMMIT;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for pl_transport_address
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `pl_transport_address`;
|
||||
CREATE TABLE `pl_transport_address` (
|
||||
`id` varchar(64) NOT NULL COMMENT '主键ID',
|
||||
`address_name` varchar(200) NOT NULL COMMENT '地址名称',
|
||||
`full_address` varchar(500) NOT NULL COMMENT '详细地址',
|
||||
`lng` decimal(10,6) NOT NULL COMMENT '经度',
|
||||
`lat` decimal(10,6) NOT NULL COMMENT '纬度',
|
||||
`contact_name` varchar(100) DEFAULT NULL COMMENT '联系人',
|
||||
`contact_phone` varchar(32) DEFAULT NULL COMMENT '联系电话',
|
||||
`status` char(1) NOT NULL DEFAULT '1' COMMENT '状态 1-启用 0-停用',
|
||||
`tenant_no` varchar(64) NOT NULL DEFAULT 'shanxia2.0' COMMENT '租户编号',
|
||||
`order_num` int(11) DEFAULT NULL COMMENT '排序号',
|
||||
`remark` varchar(512) DEFAULT NULL COMMENT '备注',
|
||||
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人id',
|
||||
`create_dept_id` varchar(64) NOT NULL DEFAULT '' COMMENT '创建人部门ID',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_by` varchar(64) DEFAULT NULL COMMENT '更新人id',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`del_flag` tinyint(4) NOT NULL DEFAULT '0' COMMENT '删除标记:0-正常 1-已删除',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_pl_transport_address_tenant` (`tenant_no`,`del_flag`,`status`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='运输通用地址库';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of pl_transport_address
|
||||
-- ----------------------------
|
||||
BEGIN;
|
||||
COMMIT;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for pl_transport
|
||||
-- ----------------------------
|
||||
|
||||
+80
-35
@@ -280,7 +280,7 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/crm/contract.py": {
|
||||
"mtime": 1782857094.9578602,
|
||||
"mtime": 1782906332.877103,
|
||||
"ast_hash": "6553f54ca1fd19086c170095f402a628",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
@@ -1725,8 +1725,8 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/env.d.ts": {
|
||||
"mtime": 1782858528.2320914,
|
||||
"ast_hash": "c9f9ecf49f80dcac1e408db4c1892ef8",
|
||||
"mtime": 1782903782.0621061,
|
||||
"ast_hash": "7490c67ebd4a28d850f0085e15e4eb26",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/shims-loose.d.ts": {
|
||||
@@ -5835,18 +5835,18 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/backend/app/models/pl_models.py": {
|
||||
"mtime": 1782793455.702941,
|
||||
"ast_hash": "6b8c4660d7028d7186267699d5ca3aa1",
|
||||
"mtime": 1782906535.7235444,
|
||||
"ast_hash": "e78c2b5826952efd2979f1a5d1fd93bb",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/plan.py": {
|
||||
"mtime": 1782793472.1020458,
|
||||
"ast_hash": "84fc6e5bd61edae63a7e23577d32c20b",
|
||||
"mtime": 1782905365.6902194,
|
||||
"ast_hash": "0f6ab4b0bf6aae653e35c93be4a9bb1c",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/transport.py": {
|
||||
"mtime": 1782793487.671617,
|
||||
"ast_hash": "4cf6d81e08cca8d6e92c43856ecf52e2",
|
||||
"mtime": 1782905367.5290751,
|
||||
"ast_hash": "e9b1480cdd11a2adb78247bd3d5b3b80",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/__init__.py": {
|
||||
@@ -5865,18 +5865,18 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/plan.py": {
|
||||
"mtime": 1782793517.0180697,
|
||||
"ast_hash": "9167954b7475bdb07dad255a9348e6b1",
|
||||
"mtime": 1782904185.1767316,
|
||||
"ast_hash": "0d840b3017a9357fa63428b838b9c5b5",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/transport.py": {
|
||||
"mtime": 1782793483.046418,
|
||||
"ast_hash": "229a992f46ae31178bb20aa0834b60d5",
|
||||
"mtime": 1782904202.7464836,
|
||||
"ast_hash": "e75ce6bfce6df56f49e2a0cb4b678217",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/__init__.py": {
|
||||
"mtime": 1782696220.8612864,
|
||||
"ast_hash": "b5a4c54d5c2a73740d5dc29a59c62a89",
|
||||
"mtime": 1782906547.2093558,
|
||||
"ast_hash": "28dc4010ec275301fcb4a013b6855a50",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/freight.py": {
|
||||
@@ -5895,8 +5895,8 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/api/pl/index.ts": {
|
||||
"mtime": 1782858537.3636208,
|
||||
"ast_hash": "c3c649ae2c94a034a0c57ee85bf8e4ec",
|
||||
"mtime": 1782906561.7571106,
|
||||
"ast_hash": "0f2965eae3756fb9045da589c832df80",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/views/pl/transport/index.vue": {
|
||||
@@ -5930,8 +5930,8 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/router/modules/pl.ts": {
|
||||
"mtime": 1782858544.556459,
|
||||
"ast_hash": "7d1ddf0f841328644a2c3734e9ff50fe",
|
||||
"mtime": 1782906603.1698864,
|
||||
"ast_hash": "1c1a94c7fdd3e4b1f93bbd77fdcd39c8",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/views/pl/plan/arrange.vue": {
|
||||
@@ -6030,8 +6030,8 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/business/pl-plan-form-dialog/index.vue": {
|
||||
"mtime": 1782902572.0918643,
|
||||
"ast_hash": "591a913bc32fa5d6d35e938609158451",
|
||||
"mtime": 1782907228.400472,
|
||||
"ast_hash": "ffa41946bfeaf3cd8dff3abbf585078c",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_plan_service.py": {
|
||||
@@ -6100,13 +6100,13 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/business/pl-plan-arrange-dialog/index.vue": {
|
||||
"mtime": 1782902592.068708,
|
||||
"ast_hash": "3818b8e76ca8c7227fa79f0bbcbaa011",
|
||||
"mtime": 1782906602.6501355,
|
||||
"ast_hash": "e26aedc11d6ef73dae6ea8251ff78cce",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/public_transport.py": {
|
||||
"mtime": 1782790898.570381,
|
||||
"ast_hash": "fbbba3979eed1c05b47d9acc45c0ecb1",
|
||||
"mtime": 1782904189.9898672,
|
||||
"ast_hash": "39ff399ebeb91882700686c192780bb7",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/backend/app/services/pl_dibang_bridge_service.py": {
|
||||
@@ -6125,8 +6125,8 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/business/pl-plan-view-tabs/index.vue": {
|
||||
"mtime": 1782902595.1814783,
|
||||
"ast_hash": "0d2b0fb0b941d63b1ec42c70b4f77483",
|
||||
"mtime": 1783001126.5947132,
|
||||
"ast_hash": "c940f2b9f6c1b2a1678083b65ae38bbc",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/business/db/DbPoundSlipQrcodeDialog.vue": {
|
||||
@@ -6135,13 +6135,13 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/api/pl/public-transport.ts": {
|
||||
"mtime": 1782858537.3784862,
|
||||
"ast_hash": "677f5eaaa05adb17442119694cd3aefc",
|
||||
"mtime": 1782904310.288007,
|
||||
"ast_hash": "42a82e7cc5bf345b537d35f6095c5b34",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/views/public/pl/transport/index.vue": {
|
||||
"mtime": 1782858542.845052,
|
||||
"ast_hash": "e8f8114d2972c7b1a388c8dd1d7f56b1",
|
||||
"mtime": 1782904322.2179258,
|
||||
"ast_hash": "de73ab2289eba99e7ccaadbbb5ed9390",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/layouts/PublicMobileShell.vue": {
|
||||
@@ -6150,8 +6150,8 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/composables/usePlPlanArrangeActions.ts": {
|
||||
"mtime": 1782902595.352905,
|
||||
"ast_hash": "7bc4755be0a912766098ae7bebbc0c8f",
|
||||
"mtime": 1782904305.6774886,
|
||||
"ast_hash": "a640aac6bd737470877b0449125f5812",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/utils/router/portal-menu.ts": {
|
||||
@@ -6290,8 +6290,8 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/business/pl-plan-archive-dialog/index.vue": {
|
||||
"mtime": 1782858535.4483042,
|
||||
"ast_hash": "b1015cda352bbe0dea3fa29faac65561",
|
||||
"mtime": 1783001120.0693388,
|
||||
"ast_hash": "b7fa59a57d7261011227b147c250a84a",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/views/pl/plan-archive/index.vue": {
|
||||
@@ -6673,5 +6673,50 @@
|
||||
"mtime": 1782865843.497152,
|
||||
"ast_hash": "f33b59225e189655c09853759400b048",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/utils/amap/loader.ts": {
|
||||
"mtime": 1782915097.1272,
|
||||
"ast_hash": "2a88741c1b8e29efe4049d40d44db119",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/business/amap-address-picker/index.vue": {
|
||||
"mtime": 1782904217.7621434,
|
||||
"ast_hash": "06a6495e30b80e357a6a11c7b2b7cca6",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/utils/amap/navigation.ts": {
|
||||
"mtime": 1782904191.971898,
|
||||
"ast_hash": "a1beb46fb9a717580c5bccec73dde5d9",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/datetime_parse.py": {
|
||||
"mtime": 1782905360.7460208,
|
||||
"ast_hash": "999484f6738d95159b3771ac05d57b4f",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/transport_address.py": {
|
||||
"mtime": 1782906539.7314954,
|
||||
"ast_hash": "9e795527053f9ac5d2f4c4161782000c",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/backend/app/api/pl/transport_address.py": {
|
||||
"mtime": 1782906547.114098,
|
||||
"ast_hash": "465480e4c04d089602bd09ee8618bcf3",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/business/pl-transport-address-select-dialog/index.vue": {
|
||||
"mtime": 1782907507.8495402,
|
||||
"ast_hash": "0f140b53c99c240dc8665e249bba168a",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/business/pl-address-with-library/index.vue": {
|
||||
"mtime": 1782907231.5989144,
|
||||
"ast_hash": "e3c51808508324c011fe3e069edfc646",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"/Users/zlwu/work/00code/shanxia2.0/web-admin/src/views/pl/transport-address/index.vue": {
|
||||
"mtime": 1782906655.9155624,
|
||||
"ast_hash": "4ba8d6395ad9952db3868ff6f6a5df10",
|
||||
"semantic_hash": ""
|
||||
}
|
||||
}
|
||||
@@ -703,33 +703,28 @@
|
||||
"701": "Community 701",
|
||||
"702": "Community 702",
|
||||
"703": "Community 703",
|
||||
"704": "Community 704",
|
||||
"705": "Community 705",
|
||||
"706": "Community 706",
|
||||
"707": "Community 707",
|
||||
"708": "Community 708",
|
||||
"709": "Community 709",
|
||||
"710": "Community 710",
|
||||
"711": "Community 711",
|
||||
"712": "Community 712",
|
||||
"713": "Community 713",
|
||||
"714": "Community 714",
|
||||
"715": "Community 715",
|
||||
"717": "Community 717",
|
||||
"718": "Community 718",
|
||||
"719": "Community 719",
|
||||
"720": "Community 720",
|
||||
"722": "Community 722",
|
||||
"723": "Community 723",
|
||||
"724": "Community 724",
|
||||
"727": "Community 727",
|
||||
"728": "Community 728",
|
||||
"729": "Community 729",
|
||||
"730": "Community 730",
|
||||
"731": "Community 731",
|
||||
"733": "Community 733",
|
||||
"734": "Community 734",
|
||||
"735": "Community 735",
|
||||
"736": "Community 736",
|
||||
"737": "Community 737",
|
||||
"738": "Community 738",
|
||||
"739": "Community 739",
|
||||
"741": "Community 741"
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
+4703
-3382
File diff suppressed because it is too large.
Load diff
@@ -184,6 +184,52 @@ export const personnelApi = {
|
||||
delete: (id: string) => request.delete(`/transport/personnel/${id}`)
|
||||
}
|
||||
|
||||
// 运输通用地址库
|
||||
export interface TransportAddress {
|
||||
id: string
|
||||
addressName: string
|
||||
fullAddress: string
|
||||
lng: number
|
||||
lat: number
|
||||
contactName?: string
|
||||
contactPhone?: string
|
||||
status: string
|
||||
remark?: string
|
||||
createTime: string
|
||||
updateTime: string
|
||||
}
|
||||
|
||||
export interface TransportAddressQuery {
|
||||
q?: string
|
||||
status?: string
|
||||
page?: number
|
||||
pageSize?: number
|
||||
}
|
||||
|
||||
export interface TransportAddressPayload {
|
||||
addressName: string
|
||||
fullAddress: string
|
||||
lng: number
|
||||
lat: number
|
||||
contactName?: string
|
||||
contactPhone?: string
|
||||
status?: string
|
||||
remark?: string
|
||||
}
|
||||
|
||||
export const transportAddressApi = {
|
||||
list: (params?: TransportAddressQuery) =>
|
||||
request.get<PlListResponse<TransportAddress>>('/transport/address', { params }),
|
||||
listAll: (params?: { q?: string }) =>
|
||||
request.get<PlListResponse<TransportAddress>>('/transport/address/all', { params }),
|
||||
get: (id: string) => request.get<TransportAddress>(`/transport/address/${id}`),
|
||||
create: (data: TransportAddressPayload) =>
|
||||
request.post<TransportAddress>('/transport/address', data),
|
||||
update: (id: string, data: Partial<TransportAddressPayload>) =>
|
||||
request.put<TransportAddress>(`/transport/address/${id}`, data),
|
||||
delete: (id: string) => request.delete(`/transport/address/${id}`)
|
||||
}
|
||||
|
||||
// 司机(兼容旧 API,映射至 personnel)
|
||||
export interface Driver {
|
||||
id: string
|
||||
@@ -325,9 +371,13 @@ export interface Plan {
|
||||
settlementMethod?: string
|
||||
hasFreight: number
|
||||
pickupAddress?: string
|
||||
pickupLng?: number | null
|
||||
pickupLat?: number | null
|
||||
pickupTime?: string
|
||||
pickupNotes?: string
|
||||
destinationAddress?: string
|
||||
destinationLng?: number | null
|
||||
destinationLat?: number | null
|
||||
deliveryTime?: string
|
||||
destinationNotes?: string
|
||||
vehicleId?: string
|
||||
@@ -403,9 +453,13 @@ export interface PlanCreateFullPayload {
|
||||
settlementMethod?: string
|
||||
hasFreight: number
|
||||
pickupAddress?: string
|
||||
pickupLng?: number | null
|
||||
pickupLat?: number | null
|
||||
pickupTime?: string
|
||||
pickupNotes?: string
|
||||
destinationAddress?: string
|
||||
destinationLng?: number | null
|
||||
destinationLat?: number | null
|
||||
deliveryTime?: string
|
||||
destinationNotes?: string
|
||||
followUserId?: string
|
||||
@@ -521,9 +575,13 @@ export interface TransportArrange {
|
||||
carrierId?: string
|
||||
transportMedium?: string
|
||||
pickupAddress?: string
|
||||
pickupLng?: number | null
|
||||
pickupLat?: number | null
|
||||
pickupTime?: string
|
||||
pickupNotes?: string
|
||||
destinationAddress?: string
|
||||
destinationLng?: number | null
|
||||
destinationLat?: number | null
|
||||
deliveryTime?: string
|
||||
destinationNotes?: string
|
||||
distanceKm?: number
|
||||
|
||||
@@ -13,7 +13,11 @@ export interface PublicTransportPage {
|
||||
planDate?: string
|
||||
planType: string
|
||||
pickupAddress?: string
|
||||
pickupLng?: number | null
|
||||
pickupLat?: number | null
|
||||
destinationAddress?: string
|
||||
destinationLng?: number | null
|
||||
destinationLat?: number | null
|
||||
licensePlate?: string
|
||||
driverName?: string
|
||||
driverPhone?: string
|
||||
|
||||
@@ -0,0 +1,427 @@
|
||||
<template>
|
||||
<div class="amap-address-picker">
|
||||
<el-input
|
||||
:id="inputId"
|
||||
v-model="innerAddress"
|
||||
:placeholder="placeholder"
|
||||
:disabled="disabled"
|
||||
clearable
|
||||
@input="handleAddressInput"
|
||||
>
|
||||
<template #append>
|
||||
<el-button :disabled="disabled" :loading="mapOpening" @click="openMapDialog">
|
||||
<el-icon><Location /></el-icon>地图选点
|
||||
</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
|
||||
<el-dialog
|
||||
v-model="mapVisible"
|
||||
title="地图选点"
|
||||
width="760px"
|
||||
align-center
|
||||
destroy-on-close
|
||||
append-to-body
|
||||
class="dialog-form-layout amap-address-picker-dialog"
|
||||
modal-class="order-module-dialog-mask"
|
||||
:close-on-click-modal="false"
|
||||
style="backdrop-filter: none"
|
||||
@opened="handleMapDialogOpened"
|
||||
@closed="handleMapDialogClosed"
|
||||
>
|
||||
<div class="amap-address-picker-body">
|
||||
<div class="amap-address-picker-search">
|
||||
<el-input
|
||||
:id="searchInputId"
|
||||
v-model="searchKeyword"
|
||||
placeholder="搜索地点、POI"
|
||||
clearable
|
||||
@keyup.enter="handleSearch"
|
||||
>
|
||||
<template #append>
|
||||
<el-button :loading="searchLoading" @click="handleSearch">
|
||||
<el-icon><Search /></el-icon>搜索
|
||||
</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
|
||||
<div ref="mapContainerRef" class="amap-address-picker-map" />
|
||||
|
||||
<p v-if="pickedAddress" class="amap-address-picker-result">
|
||||
已选:<span>{{ pickedAddress }}</span>
|
||||
</p>
|
||||
<p v-else class="amap-address-picker-hint">点击地图选点,或搜索后选择结果;可拖拽标记微调位置</p>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="mapVisible = false">
|
||||
<el-icon><Close /></el-icon>取消
|
||||
</el-button>
|
||||
<el-button type="primary" :disabled="!pickedAddress" @click="confirmPick">
|
||||
<el-icon><Check /></el-icon>确定
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Check, Close, Location, Search } from '@element-plus/icons-vue'
|
||||
import {
|
||||
formatPoiAddress,
|
||||
isAmapConfigured,
|
||||
loadAmap,
|
||||
loadAmapPlugins,
|
||||
resolveLngLat,
|
||||
type AMapAutoComplete,
|
||||
type AMapGeocoder,
|
||||
type AMapGlobal,
|
||||
type AMapLngLat,
|
||||
type AMapMap,
|
||||
type AMapMarker,
|
||||
type AMapPlaceSearch,
|
||||
type AMapPoi
|
||||
} from '@/utils/amap/loader'
|
||||
|
||||
export interface AmapAddressPickResult {
|
||||
address: string
|
||||
lng?: number
|
||||
lat?: number
|
||||
}
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
modelValue?: string
|
||||
lng?: number | null
|
||||
lat?: number | null
|
||||
placeholder?: string
|
||||
disabled?: boolean
|
||||
city?: string
|
||||
}>(),
|
||||
{
|
||||
modelValue: '',
|
||||
lng: null,
|
||||
lat: null,
|
||||
placeholder: '请输入或地图选点',
|
||||
disabled: false,
|
||||
city: ''
|
||||
}
|
||||
)
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [value: string]
|
||||
'update:lng': [value: number | null]
|
||||
'update:lat': [value: number | null]
|
||||
change: [payload: AmapAddressPickResult]
|
||||
}>()
|
||||
|
||||
const inputId = `amap-address-input-${useId()}`
|
||||
const searchInputId = `amap-address-search-${useId()}`
|
||||
|
||||
const innerAddress = ref(props.modelValue || '')
|
||||
const mapVisible = ref(false)
|
||||
const mapOpening = ref(false)
|
||||
const searchKeyword = ref('')
|
||||
const searchLoading = ref(false)
|
||||
const pickedAddress = ref('')
|
||||
const pickedLngLat = ref<[number, number] | null>(null)
|
||||
|
||||
const mapContainerRef = ref<HTMLDivElement>()
|
||||
let amapRef: AMapGlobal | null = null
|
||||
let mapInstance: AMapMap | null = null
|
||||
let markerInstance: AMapMarker | null = null
|
||||
let geocoder: AMapGeocoder | null = null
|
||||
let placeSearch: AMapPlaceSearch | null = null
|
||||
let autoComplete: AMapAutoComplete | null = null
|
||||
let mainAutoComplete: AMapAutoComplete | null = null
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
value => {
|
||||
if (value !== innerAddress.value) {
|
||||
innerAddress.value = value || ''
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
initMainAutoComplete()
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
destroyMainAutoComplete()
|
||||
destroyMapResources()
|
||||
})
|
||||
|
||||
function setCoordinates(lng?: number | null, lat?: number | null) {
|
||||
const nextLng = lng ?? null
|
||||
const nextLat = lat ?? null
|
||||
emit('update:lng', nextLng)
|
||||
emit('update:lat', nextLat)
|
||||
}
|
||||
|
||||
function handleAddressInput(value: string) {
|
||||
emit('update:modelValue', value)
|
||||
setCoordinates(null, null)
|
||||
emit('change', { address: value })
|
||||
}
|
||||
|
||||
async function ensureAmapReady() {
|
||||
if (!isAmapConfigured()) {
|
||||
ElMessage.warning('未配置高德地图 Key,请在 .env.development.local 中设置 VITE_AMAP_KEY')
|
||||
return false
|
||||
}
|
||||
try {
|
||||
amapRef = await loadAmap()
|
||||
await loadAmapPlugins()
|
||||
return true
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : '高德地图加载失败'
|
||||
ElMessage.error(message)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
function bindAutoComplete(
|
||||
target: AMapAutoComplete | null,
|
||||
onSelect: (poi?: AMapPoi) => void
|
||||
) {
|
||||
if (!target) return
|
||||
const handler = (event: { poi?: AMapPoi }) => onSelect(event.poi)
|
||||
target.on('select', handler)
|
||||
}
|
||||
|
||||
async function initMainAutoComplete() {
|
||||
const ready = await ensureAmapReady()
|
||||
if (!ready || !amapRef) return
|
||||
|
||||
amapRef.plugin(['AMap.AutoComplete'], () => {
|
||||
if (mainAutoComplete) return
|
||||
mainAutoComplete = new amapRef!.AutoComplete({
|
||||
input: inputId,
|
||||
city: props.city || '全国',
|
||||
citylimit: !!props.city
|
||||
})
|
||||
bindAutoComplete(mainAutoComplete, poi => {
|
||||
const address = formatPoiAddress(poi)
|
||||
if (!address) return
|
||||
innerAddress.value = address
|
||||
emit('update:modelValue', address)
|
||||
if (poi?.location) {
|
||||
const [lng, lat] = resolveLngLat(poi.location)
|
||||
setCoordinates(lng, lat)
|
||||
emit('change', { address, lng, lat })
|
||||
} else {
|
||||
setCoordinates(null, null)
|
||||
emit('change', { address })
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function destroyMainAutoComplete() {
|
||||
mainAutoComplete = null
|
||||
}
|
||||
|
||||
async function openMapDialog() {
|
||||
if (props.disabled) return
|
||||
mapOpening.value = true
|
||||
try {
|
||||
const ready = await ensureAmapReady()
|
||||
if (!ready) return
|
||||
pickedAddress.value = innerAddress.value || ''
|
||||
pickedLngLat.value =
|
||||
props.lng != null && props.lat != null ? [Number(props.lng), Number(props.lat)] : null
|
||||
searchKeyword.value = innerAddress.value || ''
|
||||
mapVisible.value = true
|
||||
} finally {
|
||||
mapOpening.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function handleMapDialogOpened() {
|
||||
await nextTick()
|
||||
await initMapPicker()
|
||||
if (pickedLngLat.value) {
|
||||
applyLngLat({ lng: pickedLngLat.value[0], lat: pickedLngLat.value[1] }, pickedAddress.value)
|
||||
} else if (searchKeyword.value.trim()) {
|
||||
handleSearch()
|
||||
}
|
||||
}
|
||||
|
||||
function handleMapDialogClosed() {
|
||||
destroyMapResources()
|
||||
}
|
||||
|
||||
async function initMapPicker() {
|
||||
if (!amapRef || !mapContainerRef.value) return
|
||||
|
||||
destroyMapResources()
|
||||
|
||||
mapInstance = new amapRef.Map(mapContainerRef.value, {
|
||||
zoom: 12,
|
||||
viewMode: '2D'
|
||||
})
|
||||
|
||||
geocoder = new amapRef.Geocoder()
|
||||
placeSearch = new amapRef.PlaceSearch({
|
||||
map: mapInstance,
|
||||
pageSize: 8,
|
||||
city: props.city || '全国',
|
||||
citylimit: !!props.city
|
||||
})
|
||||
|
||||
autoComplete = new amapRef.AutoComplete({
|
||||
input: searchInputId,
|
||||
city: props.city || '全国',
|
||||
citylimit: !!props.city
|
||||
})
|
||||
bindAutoComplete(autoComplete, poi => {
|
||||
if (!poi?.location) return
|
||||
applyPoiSelection(poi)
|
||||
})
|
||||
|
||||
mapInstance.on('click', event => {
|
||||
applyLngLat(event.lnglat)
|
||||
})
|
||||
|
||||
window.setTimeout(() => {
|
||||
mapInstance?.resize()
|
||||
}, 120)
|
||||
}
|
||||
|
||||
function applyPoiSelection(poi: AMapPoi) {
|
||||
if (!poi.location) return
|
||||
applyLngLat(poi.location, formatPoiAddress(poi))
|
||||
}
|
||||
|
||||
function applyLngLat(lnglat: AMapLngLat, presetAddress?: string) {
|
||||
if (!amapRef || !mapInstance) return
|
||||
const position = resolveLngLat(lnglat)
|
||||
pickedLngLat.value = position
|
||||
|
||||
if (!markerInstance) {
|
||||
markerInstance = new amapRef.Marker({
|
||||
position,
|
||||
draggable: true
|
||||
})
|
||||
markerInstance.on('dragend', () => {
|
||||
const pos = markerInstance?.getPosition()
|
||||
if (pos) reverseGeocode(pos)
|
||||
})
|
||||
mapInstance.add(markerInstance)
|
||||
} else {
|
||||
markerInstance.setPosition(position)
|
||||
}
|
||||
|
||||
mapInstance.setCenter(position)
|
||||
mapInstance.setZoom(16)
|
||||
|
||||
if (presetAddress) {
|
||||
pickedAddress.value = presetAddress
|
||||
return
|
||||
}
|
||||
reverseGeocode(lnglat)
|
||||
}
|
||||
|
||||
function reverseGeocode(lnglat: AMapLngLat) {
|
||||
if (!geocoder) return
|
||||
geocoder.getAddress(lnglat, (status, result) => {
|
||||
if (status !== 'complete') return
|
||||
const address = result.regeocode?.formattedAddress?.trim()
|
||||
if (address) pickedAddress.value = address
|
||||
})
|
||||
}
|
||||
|
||||
function handleSearch() {
|
||||
const keyword = searchKeyword.value.trim()
|
||||
if (!keyword) {
|
||||
ElMessage.warning('请输入搜索关键词')
|
||||
return
|
||||
}
|
||||
if (!placeSearch) return
|
||||
|
||||
searchLoading.value = true
|
||||
placeSearch.search(keyword, (status, result) => {
|
||||
searchLoading.value = false
|
||||
if (status !== 'complete') {
|
||||
ElMessage.warning('未找到相关地点')
|
||||
return
|
||||
}
|
||||
const poi = result.poiList?.pois?.[0]
|
||||
if (!poi) {
|
||||
ElMessage.warning('未找到相关地点')
|
||||
return
|
||||
}
|
||||
applyPoiSelection(poi)
|
||||
})
|
||||
}
|
||||
|
||||
function confirmPick() {
|
||||
if (!pickedAddress.value) return
|
||||
innerAddress.value = pickedAddress.value
|
||||
emit('update:modelValue', pickedAddress.value)
|
||||
const payload: AmapAddressPickResult = { address: pickedAddress.value }
|
||||
if (pickedLngLat.value) {
|
||||
payload.lng = pickedLngLat.value[0]
|
||||
payload.lat = pickedLngLat.value[1]
|
||||
setCoordinates(payload.lng, payload.lat)
|
||||
} else {
|
||||
setCoordinates(null, null)
|
||||
}
|
||||
emit('change', payload)
|
||||
mapVisible.value = false
|
||||
}
|
||||
|
||||
function destroyMapResources() {
|
||||
autoComplete = null
|
||||
placeSearch = null
|
||||
geocoder = null
|
||||
markerInstance = null
|
||||
if (mapInstance) {
|
||||
mapInstance.destroy()
|
||||
mapInstance = null
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.amap-address-picker {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.amap-address-picker-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.amap-address-picker-map {
|
||||
width: 100%;
|
||||
height: 420px;
|
||||
border: 1px solid var(--border, #d9e8e2);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.amap-address-picker-result,
|
||||
.amap-address-picker-hint {
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
color: var(--text-muted, #64748b);
|
||||
line-height: 1.5;
|
||||
|
||||
span {
|
||||
color: var(--text, #1e293b);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
.el-dialog.amap-address-picker-dialog.dialog-form-layout {
|
||||
width: min(760px, calc(100vw - 80px)) !important;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,89 @@
|
||||
<template>
|
||||
<div class="pl-address-with-library">
|
||||
<div class="pl-address-with-library__row">
|
||||
<AmapAddressPicker
|
||||
v-model="innerAddress"
|
||||
v-model:lng="innerLng"
|
||||
v-model:lat="innerLat"
|
||||
class="pl-address-with-library__picker"
|
||||
:placeholder="placeholder"
|
||||
:disabled="disabled"
|
||||
/>
|
||||
<el-button
|
||||
class="pl-address-with-library__library-btn btn-ghost"
|
||||
:disabled="disabled"
|
||||
@click="libraryVisible = true"
|
||||
>
|
||||
<el-icon><FolderOpened /></el-icon>从地址库选择
|
||||
</el-button>
|
||||
</div>
|
||||
<PlTransportAddressSelectDialog v-model="libraryVisible" @select="handleLibrarySelect" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { FolderOpened } from '@element-plus/icons-vue'
|
||||
import AmapAddressPicker from '@/components/business/amap-address-picker/index.vue'
|
||||
import PlTransportAddressSelectDialog from '@/components/business/pl-transport-address-select-dialog/index.vue'
|
||||
import type { TransportAddress } from '@/api/pl/index'
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: string
|
||||
lng?: number | null
|
||||
lat?: number | null
|
||||
placeholder?: string
|
||||
disabled?: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [value: string]
|
||||
'update:lng': [value: number | null]
|
||||
'update:lat': [value: number | null]
|
||||
}>()
|
||||
|
||||
const libraryVisible = ref(false)
|
||||
|
||||
const innerAddress = computed({
|
||||
get: () => props.modelValue,
|
||||
set: (value: string) => emit('update:modelValue', value)
|
||||
})
|
||||
|
||||
const innerLng = computed({
|
||||
get: () => props.lng ?? null,
|
||||
set: (value: number | null) => emit('update:lng', value)
|
||||
})
|
||||
|
||||
const innerLat = computed({
|
||||
get: () => props.lat ?? null,
|
||||
set: (value: number | null) => emit('update:lat', value)
|
||||
})
|
||||
|
||||
function handleLibrarySelect(row: TransportAddress) {
|
||||
innerAddress.value = row.fullAddress
|
||||
innerLng.value = row.lng != null ? Number(row.lng) : null
|
||||
innerLat.value = row.lat != null ? Number(row.lat) : null
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.pl-address-with-library {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.pl-address-with-library__row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.pl-address-with-library__picker {
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.pl-address-with-library__library-btn {
|
||||
flex: 0 0 auto;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
@@ -25,61 +25,64 @@
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section class="pl-plan-form-section">
|
||||
<section class="pl-plan-form-section pl-plan-freight-section">
|
||||
<h4 class="pl-plan-subtitle">运费信息</h4>
|
||||
<div class="form-grid-4">
|
||||
<el-form-item label="是否有运输费用">
|
||||
<el-switch v-model="formData.hasFreight" :active-value="1" :inactive-value="0" />
|
||||
</el-form-item>
|
||||
<template v-if="formData.hasFreight === 1">
|
||||
<el-form-item label="运输公里数">
|
||||
<el-input-number
|
||||
v-model="formData.distanceKm"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
style="width: 100%"
|
||||
/>
|
||||
<el-form
|
||||
:model="formData"
|
||||
label-width="96px"
|
||||
label-position="right"
|
||||
class="pl-plan-freight-form"
|
||||
>
|
||||
<div class="form-grid-4">
|
||||
<el-form-item label="是否有运输费用">
|
||||
<el-switch v-model="formData.hasFreight" :active-value="1" :inactive-value="0" />
|
||||
</el-form-item>
|
||||
<el-form-item label="运费计算方式">
|
||||
<el-select v-model="formData.freightType">
|
||||
<el-option label="按重量" value="by_weight" />
|
||||
<el-option label="按趟" value="by_trip" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="formData.freightType === 'by_weight'"
|
||||
:label="`运输量(${freightUnitLabel})`"
|
||||
>
|
||||
<el-input-number
|
||||
v-model="freightCalc.transportQty.value"
|
||||
:min="0"
|
||||
:precision="3"
|
||||
style="width: 100%"
|
||||
@change="freightCalc.markTransportQtyTouched()"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="formData.freightType === 'by_weight'"
|
||||
:label="`运输单价(元/${freightUnitLabel})`"
|
||||
>
|
||||
<el-input-number
|
||||
v-model="freightCalc.freightPrice.value"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="运费金额(元)">
|
||||
<el-input-number
|
||||
v-model="freightCalc.freightAmount.value"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
style="width: 100%"
|
||||
@change="freightCalc.markFreightAmountTouched()"
|
||||
/>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</div>
|
||||
<template v-if="formData.hasFreight === 1">
|
||||
<el-form-item label="运输公里数">
|
||||
<el-input-number
|
||||
v-model="formData.distanceKm"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="运费计算方式">
|
||||
<el-select v-model="formData.freightType">
|
||||
<el-option label="按重量" value="by_weight" />
|
||||
<el-option label="按趟" value="by_trip" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="formData.freightType === 'by_weight'"
|
||||
:label="`运输量(${freightUnitLabel})`"
|
||||
>
|
||||
<el-input-number
|
||||
v-model="freightCalc.transportQty.value"
|
||||
:min="0"
|
||||
:precision="3"
|
||||
@change="freightCalc.markTransportQtyTouched()"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="formData.freightType === 'by_weight'"
|
||||
:label="`运输单价(元/${freightUnitLabel})`"
|
||||
>
|
||||
<el-input-number
|
||||
v-model="freightCalc.freightPrice.value"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="运费金额(元)">
|
||||
<el-input-number
|
||||
v-model="freightCalc.freightAmount.value"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
@change="freightCalc.markFreightAmountTouched()"
|
||||
/>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</div>
|
||||
</el-form>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
@@ -384,10 +387,32 @@ async function handleSubmit() {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.pl-arrange-freight-muted {
|
||||
margin-bottom: 12px;
|
||||
font-size: 13px;
|
||||
color: #64748b;
|
||||
.pl-plan-freight-form {
|
||||
:deep(.el-form-item) {
|
||||
margin-bottom: 0;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
:deep(.el-form-item__label) {
|
||||
height: auto !important;
|
||||
line-height: 1.4 !important;
|
||||
padding: 0 8px 0 0 !important;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
:deep(.el-form-item__content) {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.form-grid-4 {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
:deep(.el-select),
|
||||
:deep(.el-input-number) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.pl-arrange-footer {
|
||||
@@ -396,6 +421,7 @@ async function handleSubmit() {
|
||||
gap: 8px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
@@ -132,7 +132,12 @@
|
||||
<h4 class="pl-plan-subtitle">取货信息</h4>
|
||||
<div class="form-grid-3">
|
||||
<el-form-item label="取货地址">
|
||||
<el-input v-model="formData.pickupAddress" placeholder="取货地址" />
|
||||
<PlAddressWithLibrary
|
||||
v-model="formData.pickupAddress"
|
||||
v-model:lng="formData.pickupLng"
|
||||
v-model:lat="formData.pickupLat"
|
||||
placeholder="取货地址"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="取货时间">
|
||||
<el-date-picker
|
||||
@@ -174,7 +179,12 @@
|
||||
<h4 class="pl-plan-subtitle">送货信息</h4>
|
||||
<div class="form-grid-3">
|
||||
<el-form-item label="送货地址">
|
||||
<el-input v-model="formData.destinationAddress" placeholder="送货地址" />
|
||||
<PlAddressWithLibrary
|
||||
v-model="formData.destinationAddress"
|
||||
v-model:lng="formData.destinationLng"
|
||||
v-model:lat="formData.destinationLat"
|
||||
placeholder="送货地址"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="送达时间">
|
||||
<el-date-picker
|
||||
@@ -315,7 +325,11 @@
|
||||
@click="
|
||||
copyPlanSummary({
|
||||
pickupAddress: formData.pickupAddress,
|
||||
pickupLng: formData.pickupLng,
|
||||
pickupLat: formData.pickupLat,
|
||||
destinationAddress: formData.destinationAddress,
|
||||
destinationLng: formData.destinationLng,
|
||||
destinationLat: formData.destinationLat,
|
||||
pickupNotes: formData.pickupNotes,
|
||||
destinationNotes: formData.destinationNotes,
|
||||
licensePlate: vehicleList.find(v => v.id === formData.vehicleId)?.licensePlate,
|
||||
@@ -374,6 +388,7 @@ import {
|
||||
type VehiclePersonnelRow
|
||||
} from '@/api/pl'
|
||||
import DbPoundSlipQrcodeDialog from '@/components/business/db/DbPoundSlipQrcodeDialog.vue'
|
||||
import PlAddressWithLibrary from '@/components/business/pl-address-with-library/index.vue'
|
||||
import PlVehicleSummaryStrip from '@/components/business/pl-vehicle-summary-strip/index.vue'
|
||||
import PlVehicleViewDialog from '@/components/business/pl-vehicle-view-dialog/index.vue'
|
||||
import PlPlanViewTabs from '@/components/business/pl-plan-view-tabs/index.vue'
|
||||
@@ -449,9 +464,13 @@ const formData = reactive({
|
||||
driverId: '',
|
||||
carrierId: '',
|
||||
pickupAddress: '',
|
||||
pickupLng: null as number | null,
|
||||
pickupLat: null as number | null,
|
||||
pickupTime: '',
|
||||
pickupNotes: '',
|
||||
destinationAddress: '',
|
||||
destinationLng: null as number | null,
|
||||
destinationLat: null as number | null,
|
||||
deliveryTime: '',
|
||||
destinationNotes: '',
|
||||
hasFreight: 0,
|
||||
@@ -581,9 +600,13 @@ function applyVehiclePersonnel(personnelList: VehiclePersonnelRow[], preferredDr
|
||||
|
||||
async function applyPrefillFromPlan(data: Plan) {
|
||||
formData.pickupAddress = data.pickupAddress || ''
|
||||
formData.pickupLng = data.pickupLng != null ? Number(data.pickupLng) : null
|
||||
formData.pickupLat = data.pickupLat != null ? Number(data.pickupLat) : null
|
||||
formData.pickupTime = formatDate(data.pickupTime) || ''
|
||||
formData.pickupNotes = data.pickupNotes || ''
|
||||
formData.destinationAddress = data.destinationAddress || ''
|
||||
formData.destinationLng = data.destinationLng != null ? Number(data.destinationLng) : null
|
||||
formData.destinationLat = data.destinationLat != null ? Number(data.destinationLat) : null
|
||||
formData.deliveryTime = formatDate(data.deliveryTime) || ''
|
||||
formData.destinationNotes = data.destinationNotes || ''
|
||||
formData.hasFreight = data.hasFreight ?? 0
|
||||
@@ -657,9 +680,13 @@ function resetState() {
|
||||
driverId: '',
|
||||
carrierId: '',
|
||||
pickupAddress: '',
|
||||
pickupLng: null,
|
||||
pickupLat: null,
|
||||
pickupTime: '',
|
||||
pickupNotes: '',
|
||||
destinationAddress: '',
|
||||
destinationLng: null,
|
||||
destinationLat: null,
|
||||
deliveryTime: '',
|
||||
destinationNotes: '',
|
||||
hasFreight: 0,
|
||||
@@ -829,9 +856,13 @@ async function handleSubmit() {
|
||||
driverId: formData.driverId,
|
||||
carrierId: formData.carrierId || undefined,
|
||||
pickupAddress: formData.pickupAddress || undefined,
|
||||
pickupLng: formData.pickupLng,
|
||||
pickupLat: formData.pickupLat,
|
||||
pickupTime: formData.pickupTime || undefined,
|
||||
pickupNotes: formData.pickupNotes || undefined,
|
||||
destinationAddress: formData.destinationAddress || undefined,
|
||||
destinationLng: formData.destinationLng,
|
||||
destinationLat: formData.destinationLat,
|
||||
deliveryTime: formData.deliveryTime || undefined,
|
||||
destinationNotes: formData.destinationNotes || undefined,
|
||||
transportMedium: transportMediumText.value || undefined,
|
||||
|
||||
@@ -122,9 +122,14 @@
|
||||
<div class="pl-plan-logistics-row">
|
||||
<div class="pl-plan-logistics-block">
|
||||
<h4 class="pl-plan-subtitle">取货信息</h4>
|
||||
<div class="form-grid-3">
|
||||
<el-form-item label="取货地址">
|
||||
<el-input v-model="formData.pickupAddress" placeholder="取货地址" />
|
||||
<div class="form-grid-2">
|
||||
<el-form-item label="取货地址" class="form-col-span-2">
|
||||
<PlAddressWithLibrary
|
||||
v-model="formData.pickupAddress"
|
||||
v-model:lng="formData.pickupLng"
|
||||
v-model:lat="formData.pickupLat"
|
||||
placeholder="取货地址"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="取货时间">
|
||||
<el-date-picker
|
||||
@@ -164,9 +169,14 @@
|
||||
|
||||
<div class="pl-plan-logistics-block">
|
||||
<h4 class="pl-plan-subtitle">送货信息</h4>
|
||||
<div class="form-grid-3">
|
||||
<el-form-item label="送货地址">
|
||||
<el-input v-model="formData.destinationAddress" placeholder="送货地址" />
|
||||
<div class="form-grid-2">
|
||||
<el-form-item label="送货地址" class="form-col-span-2">
|
||||
<PlAddressWithLibrary
|
||||
v-model="formData.destinationAddress"
|
||||
v-model:lng="formData.destinationLng"
|
||||
v-model:lat="formData.destinationLat"
|
||||
placeholder="送货地址"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="送达时间">
|
||||
<el-date-picker
|
||||
@@ -273,6 +283,7 @@ import ViewKvTable from '@/components/business/system-view-dialog/ViewKvTable.vu
|
||||
import type { ViewField } from '@/components/business/system-view-dialog/types'
|
||||
import PlVehicleSummaryStrip from '@/components/business/pl-vehicle-summary-strip/index.vue'
|
||||
import PlVehicleViewDialog from '@/components/business/pl-vehicle-view-dialog/index.vue'
|
||||
import PlAddressWithLibrary from '@/components/business/pl-address-with-library/index.vue'
|
||||
import PlPlanFormProductTable from './PlPlanFormProductTable.vue'
|
||||
import {
|
||||
applyContractToPlanForm,
|
||||
@@ -373,9 +384,13 @@ const formData = reactive({
|
||||
settlementMethod: '',
|
||||
hasFreight: 0,
|
||||
pickupAddress: '',
|
||||
pickupLng: null as number | null,
|
||||
pickupLat: null as number | null,
|
||||
pickupTime: '',
|
||||
pickupNotes: '',
|
||||
destinationAddress: '',
|
||||
destinationLng: null as number | null,
|
||||
destinationLat: null as number | null,
|
||||
deliveryTime: '',
|
||||
destinationNotes: '',
|
||||
vehicleId: '',
|
||||
@@ -571,9 +586,13 @@ function buildPlanCommonPayload(options?: { allowClearTransport?: boolean }): Pl
|
||||
settlementMethod: formData.settlementMethod || undefined,
|
||||
hasFreight: formData.hasFreight,
|
||||
pickupAddress: formData.pickupAddress || undefined,
|
||||
pickupLng: formData.pickupLng,
|
||||
pickupLat: formData.pickupLat,
|
||||
pickupTime: formData.pickupTime || undefined,
|
||||
pickupNotes: formData.pickupNotes || undefined,
|
||||
destinationAddress: formData.destinationAddress || undefined,
|
||||
destinationLng: formData.destinationLng,
|
||||
destinationLat: formData.destinationLat,
|
||||
deliveryTime: formData.deliveryTime || undefined,
|
||||
destinationNotes: formData.destinationNotes || undefined,
|
||||
followUserId: formData.followUserId || undefined,
|
||||
@@ -650,9 +669,13 @@ function resetForm() {
|
||||
settlementMethod: '',
|
||||
hasFreight: 0,
|
||||
pickupAddress: '',
|
||||
pickupLng: null as number | null,
|
||||
pickupLat: null as number | null,
|
||||
pickupTime: '',
|
||||
pickupNotes: '',
|
||||
destinationAddress: '',
|
||||
destinationLng: null as number | null,
|
||||
destinationLat: null as number | null,
|
||||
deliveryTime: '',
|
||||
destinationNotes: '',
|
||||
vehicleId: '',
|
||||
@@ -705,9 +728,13 @@ async function loadPlanForEdit(planId: string) {
|
||||
settlementMethod: data.settlementMethod || '',
|
||||
hasFreight: data.hasFreight ?? 0,
|
||||
pickupAddress: data.pickupAddress || '',
|
||||
pickupLng: data.pickupLng != null ? Number(data.pickupLng) : null,
|
||||
pickupLat: data.pickupLat != null ? Number(data.pickupLat) : null,
|
||||
pickupTime: formatDate(data.pickupTime) || '',
|
||||
pickupNotes: data.pickupNotes || '',
|
||||
destinationAddress: data.destinationAddress || '',
|
||||
destinationLng: data.destinationLng != null ? Number(data.destinationLng) : null,
|
||||
destinationLat: data.destinationLat != null ? Number(data.destinationLat) : null,
|
||||
deliveryTime: formatDate(data.deliveryTime) || '',
|
||||
destinationNotes: data.destinationNotes || '',
|
||||
vehicleId: data.vehicleId || '',
|
||||
@@ -1084,7 +1111,7 @@ async function handleSubmit() {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.form-grid-3 {
|
||||
.form-grid-2 {
|
||||
gap: 8px 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
<ViewKvTable :fields="pickupViewFields" mode="quad" />
|
||||
</div>
|
||||
|
||||
<div class="party-flow-bridge" aria-hidden="true">
|
||||
<div class="party-flow-bridge">
|
||||
<span class="party-flow-bridge__label">运输</span>
|
||||
<span class="party-flow-bridge__arrow">
|
||||
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
||||
|
||||
@@ -0,0 +1,238 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="innerVisible"
|
||||
title="从地址库选择"
|
||||
width="1000px"
|
||||
align-center
|
||||
destroy-on-close
|
||||
append-to-body
|
||||
class="dialog-form-layout pl-transport-address-select-dialog"
|
||||
modal-class="order-module-dialog-mask"
|
||||
:close-on-click-modal="false"
|
||||
style="backdrop-filter: none"
|
||||
@open="handleOpen"
|
||||
>
|
||||
<div class="filter-block pl-transport-address-select-filter">
|
||||
<el-form class="filter-form" @submit.prevent>
|
||||
<div class="filter-grid">
|
||||
<el-form-item label="名称/地址">
|
||||
<el-input
|
||||
v-model="keyword"
|
||||
placeholder="搜索地址名称、详细地址、联系人或电话"
|
||||
clearable
|
||||
@keyup.enter="handleSearch"
|
||||
/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div class="filter-actions">
|
||||
<div class="filter-actions-start" />
|
||||
<div class="filter-actions-end">
|
||||
<el-button class="btn-ghost" :loading="loading" @click="handleReset">
|
||||
<el-icon><RefreshLeft /></el-icon>重置
|
||||
</el-button>
|
||||
<el-button class="btn-main" type="primary" :loading="loading" @click="handleSearch">
|
||||
<el-icon><Search /></el-icon>搜索
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<div class="panel dialog-list-panel">
|
||||
<div class="list-body list-wrap dialog-list-body-only">
|
||||
<el-table
|
||||
class="list-table pl-transport-address-select-table"
|
||||
:data="rows"
|
||||
v-loading="loading"
|
||||
stripe
|
||||
row-key="id"
|
||||
highlight-current-row
|
||||
:max-height="480"
|
||||
empty-text="暂无可用地址"
|
||||
@row-dblclick="handleConfirm"
|
||||
>
|
||||
<el-table-column
|
||||
type="index"
|
||||
label="序号"
|
||||
min-width="68"
|
||||
align="center"
|
||||
header-align="center"
|
||||
:index="index => (pagination.page - 1) * pagination.pageSize + index + 1"
|
||||
/>
|
||||
<el-table-column prop="addressName" label="地址名称" min-width="140" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-link type="primary" @click="handleConfirm(row)">{{ row.addressName }}</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="fullAddress" label="详细地址" min-width="260" show-overflow-tooltip />
|
||||
<el-table-column label="坐标" min-width="150" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.lng != null && row.lat != null">{{ row.lng }}, {{ row.lat }}</span>
|
||||
<span v-else>—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="contactName"
|
||||
label="联系人"
|
||||
min-width="100"
|
||||
align="center"
|
||||
header-align="center"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="contactPhone"
|
||||
label="联系电话"
|
||||
min-width="120"
|
||||
align="center"
|
||||
header-align="center"
|
||||
/>
|
||||
<el-table-column
|
||||
label="操作"
|
||||
min-width="100"
|
||||
align="center"
|
||||
header-align="center"
|
||||
fixed="right"
|
||||
class-name="col-actions"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" @click="handleConfirm(row)">
|
||||
<el-icon><Select /></el-icon>选择
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<ListPagination
|
||||
v-show="pagination.total > 0"
|
||||
:total="pagination.total"
|
||||
v-model:page="pagination.page"
|
||||
v-model:limit="pagination.pageSize"
|
||||
@pagination="fetchList"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="innerVisible = false">
|
||||
<el-icon><Close /></el-icon>关闭
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Close, RefreshLeft, Search, Select } from '@element-plus/icons-vue'
|
||||
import { transportAddressApi, type TransportAddress } from '@/api/pl/index'
|
||||
import ListPagination from '@/components/ListPagination/index.vue'
|
||||
import { pickBxPageResult } from '@/utils/http/bx'
|
||||
import { DEFAULT_LIST_PAGE_SIZE } from '@/utils/table/tableConfig'
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [value: boolean]
|
||||
select: [row: TransportAddress]
|
||||
}>()
|
||||
|
||||
const innerVisible = computed({
|
||||
get: () => props.modelValue,
|
||||
set: (value: boolean) => emit('update:modelValue', value)
|
||||
})
|
||||
|
||||
const loading = ref(false)
|
||||
const keyword = ref('')
|
||||
const rows = ref<TransportAddress[]>([])
|
||||
|
||||
const pagination = reactive({
|
||||
page: 1,
|
||||
pageSize: DEFAULT_LIST_PAGE_SIZE,
|
||||
total: 0
|
||||
})
|
||||
|
||||
async function fetchList() {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await transportAddressApi.list({
|
||||
q: keyword.value.trim() || undefined,
|
||||
status: '1',
|
||||
page: pagination.page,
|
||||
pageSize: pagination.pageSize
|
||||
})
|
||||
const { list, total } = pickBxPageResult<TransportAddress>(res)
|
||||
rows.value = list
|
||||
pagination.total = total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function handleSearch() {
|
||||
pagination.page = 1
|
||||
fetchList()
|
||||
}
|
||||
|
||||
function handleReset() {
|
||||
keyword.value = ''
|
||||
pagination.page = 1
|
||||
fetchList()
|
||||
}
|
||||
|
||||
function handleOpen() {
|
||||
keyword.value = ''
|
||||
pagination.page = 1
|
||||
pagination.pageSize = DEFAULT_LIST_PAGE_SIZE
|
||||
pagination.total = 0
|
||||
rows.value = []
|
||||
fetchList()
|
||||
}
|
||||
|
||||
function handleConfirm(row: TransportAddress) {
|
||||
emit('select', row)
|
||||
innerVisible.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.pl-transport-address-select-filter {
|
||||
padding: 12px 16px 10px;
|
||||
background: var(--mint-surface, #fff);
|
||||
border: 1px solid var(--mint-border, #d9e8e2);
|
||||
border-radius: var(--mint-radius, 10px);
|
||||
margin-bottom: 12px;
|
||||
|
||||
.filter-grid {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
gap: 10px 14px;
|
||||
max-width: 480px;
|
||||
}
|
||||
|
||||
.filter-actions {
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.filter-actions-end {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.filter-actions-end .el-button + .el-button {
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.pl-transport-address-select-dialog {
|
||||
:deep(.el-dialog__body) {
|
||||
min-height: 620px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -9,6 +9,7 @@ import { planApi } from '@/api/pl'
|
||||
import DbPoundSlipQrcodeDialog from '@/components/business/db/DbPoundSlipQrcodeDialog.vue'
|
||||
import { useDictStore } from '@/store/modules/dict'
|
||||
import { formatDate } from '@/utils/format'
|
||||
import { appendAmapNavLine, buildAmapNavigationUrl } from '@/utils/amap/navigation'
|
||||
import { pickBxData } from '@/utils/http/bx'
|
||||
import { formatPlanProductListText } from '@/utils/pl-display'
|
||||
import { buildSiteSummaryFromInventoryLinks } from '@/utils/db-site-summary'
|
||||
@@ -109,7 +110,11 @@ export function usePlPlanArrangeActions() {
|
||||
startPoint?: string
|
||||
endPoint?: string
|
||||
pickupAddress?: string
|
||||
pickupLng?: number | null
|
||||
pickupLat?: number | null
|
||||
destinationAddress?: string
|
||||
destinationLng?: number | null
|
||||
destinationLat?: number | null
|
||||
pickupNotes?: string
|
||||
destinationNotes?: string
|
||||
licensePlate?: string
|
||||
@@ -124,15 +129,37 @@ export function usePlPlanArrangeActions() {
|
||||
.map(p => formatPlanProductListText(p, getUnitLabel, { usePlannedQuantity: true }))
|
||||
.join(';')
|
||||
lines.push(`产品:${productText || '—'}`)
|
||||
lines.push(
|
||||
`路线:${options?.pickupAddress || plan.value?.pickupAddress || options?.startPoint || '—'} → ${
|
||||
options?.destinationAddress ||
|
||||
plan.value?.destinationAddress ||
|
||||
plan.value?.destinationName ||
|
||||
options?.endPoint ||
|
||||
'—'
|
||||
}`
|
||||
|
||||
const pickupAddress =
|
||||
options?.pickupAddress || plan.value?.pickupAddress || options?.startPoint || '—'
|
||||
const destinationAddress =
|
||||
options?.destinationAddress ||
|
||||
plan.value?.destinationAddress ||
|
||||
plan.value?.destinationName ||
|
||||
options?.endPoint ||
|
||||
'—'
|
||||
lines.push(`取货地址:${pickupAddress}`)
|
||||
lines.push(`送货地址:${destinationAddress}`)
|
||||
|
||||
const pickupLng = options?.pickupLng ?? plan.value?.pickupLng
|
||||
const pickupLat = options?.pickupLat ?? plan.value?.pickupLat
|
||||
const destinationLng = options?.destinationLng ?? plan.value?.destinationLng
|
||||
const destinationLat = options?.destinationLat ?? plan.value?.destinationLat
|
||||
appendAmapNavLine(
|
||||
lines,
|
||||
'取货导航',
|
||||
buildAmapNavigationUrl(pickupLng, pickupLat, pickupAddress !== '—' ? pickupAddress : '取货点')
|
||||
)
|
||||
appendAmapNavLine(
|
||||
lines,
|
||||
'送货导航',
|
||||
buildAmapNavigationUrl(
|
||||
destinationLng,
|
||||
destinationLat,
|
||||
destinationAddress !== '—' ? destinationAddress : '送货点'
|
||||
)
|
||||
)
|
||||
|
||||
lines.push(`取货注意事项:${options?.pickupNotes || plan.value?.pickupNotes || '—'}`)
|
||||
lines.push(`送货注意事项:${options?.destinationNotes || plan.value?.destinationNotes || '—'}`)
|
||||
lines.push(`司机链接:${options?.driverUrl || driverUrl.value || '—'}`)
|
||||
@@ -208,7 +235,11 @@ export function usePlPlanArrangeActions() {
|
||||
startPoint?: string
|
||||
endPoint?: string
|
||||
pickupAddress?: string
|
||||
pickupLng?: number | null
|
||||
pickupLat?: number | null
|
||||
destinationAddress?: string
|
||||
destinationLng?: number | null
|
||||
destinationLat?: number | null
|
||||
pickupNotes?: string
|
||||
destinationNotes?: string
|
||||
licensePlate?: string
|
||||
|
||||
Vendored
+9
@@ -1,5 +1,14 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
interface ImportMetaEnv {
|
||||
readonly VITE_AMAP_KEY?: string
|
||||
readonly VITE_AMAP_SECURITY_CODE?: string
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
readonly env: ImportMetaEnv
|
||||
}
|
||||
|
||||
declare module 'nprogress'
|
||||
|
||||
declare module 'crypto-js'
|
||||
|
||||
@@ -32,6 +32,13 @@ export const plRoutes: AppRouteRecord[] = [
|
||||
component: '/pl/vehicle/index',
|
||||
meta: { title: '车辆管理', keepAlive: true, menuCode: 'pl:vehicle:list' }
|
||||
},
|
||||
{
|
||||
id: 'PL00000000000000000000000000004A',
|
||||
name: 'PlTransportAddress',
|
||||
path: 'transport-address',
|
||||
component: '/pl/transport-address/index',
|
||||
meta: { title: '地址库', keepAlive: true, menuCode: 'pl:transport-address:list' }
|
||||
},
|
||||
{
|
||||
id: 'PL00000000000000000000000000004',
|
||||
name: 'PlPersonnel',
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
declare global {
|
||||
interface Window {
|
||||
AMap?: AMapGlobal
|
||||
_AMapSecurityConfig?: { securityJsCode: string }
|
||||
}
|
||||
}
|
||||
|
||||
export interface AMapLngLat {
|
||||
lng: number
|
||||
lat: number
|
||||
getLng?: () => number
|
||||
getLat?: () => number
|
||||
}
|
||||
|
||||
export interface AMapPoi {
|
||||
name?: string
|
||||
address?: string
|
||||
location?: AMapLngLat
|
||||
district?: string
|
||||
}
|
||||
|
||||
export interface AMapGlobal {
|
||||
plugin: (names: string | string[], callback: () => void) => void
|
||||
Map: new (container: HTMLElement | string, opts: Record<string, unknown>) => AMapMap
|
||||
Marker: new (opts: Record<string, unknown>) => AMapMarker
|
||||
AutoComplete: new (opts: Record<string, unknown>) => AMapAutoComplete
|
||||
PlaceSearch: new (opts: Record<string, unknown>) => AMapPlaceSearch
|
||||
Geocoder: new (opts?: Record<string, unknown>) => AMapGeocoder
|
||||
}
|
||||
|
||||
export interface AMapMap {
|
||||
on: (event: string, handler: (event: { lnglat: AMapLngLat }) => void) => void
|
||||
destroy: () => void
|
||||
resize: () => void
|
||||
setCenter: (center: [number, number]) => void
|
||||
setZoom: (zoom: number) => void
|
||||
add: (overlay: unknown) => void
|
||||
remove: (overlay: unknown) => void
|
||||
}
|
||||
|
||||
export interface AMapMarker {
|
||||
setPosition: (lnglat: [number, number] | AMapLngLat) => void
|
||||
getPosition: () => AMapLngLat
|
||||
on: (event: string, handler: () => void) => void
|
||||
}
|
||||
|
||||
export interface AMapAutoComplete {
|
||||
on: (event: string, handler: (event: { poi?: AMapPoi }) => void) => void
|
||||
off: (event: string, handler: (event: { poi?: AMapPoi }) => void) => void
|
||||
}
|
||||
|
||||
export interface AMapPlaceSearch {
|
||||
search: (
|
||||
keyword: string,
|
||||
callback: (status: string, result: { poiList?: { pois?: AMapPoi[] } }) => void
|
||||
) => void
|
||||
}
|
||||
|
||||
export interface AMapGeocoder {
|
||||
getAddress: (
|
||||
lnglat: [number, number] | AMapLngLat,
|
||||
callback: (status: string, result: { regeocode?: { formattedAddress?: string } }) => void
|
||||
) => void
|
||||
getLocation: (
|
||||
address: string,
|
||||
callback: (
|
||||
status: string,
|
||||
result: { geocodes?: Array<{ location?: { lng: number; lat: number } }> }
|
||||
) => void
|
||||
) => void
|
||||
}
|
||||
|
||||
const AMAP_SCRIPT_ID = 'amap-js-sdk-v2'
|
||||
const DEFAULT_PLUGINS = ['AMap.AutoComplete', 'AMap.PlaceSearch', 'AMap.Geocoder']
|
||||
|
||||
let loadPromise: Promise<AMapGlobal> | null = null
|
||||
let pluginsReadyPromise: Promise<void> | null = null
|
||||
|
||||
export function getAmapKey() {
|
||||
return String(import.meta.env.VITE_AMAP_KEY || '').trim()
|
||||
}
|
||||
|
||||
export function getAmapSecurityCode() {
|
||||
return String(import.meta.env.VITE_AMAP_SECURITY_CODE || '').trim()
|
||||
}
|
||||
|
||||
export function isAmapConfigured() {
|
||||
return !!getAmapKey()
|
||||
}
|
||||
|
||||
function injectSecurityConfig() {
|
||||
const securityJsCode = getAmapSecurityCode()
|
||||
if (!securityJsCode) return
|
||||
window._AMapSecurityConfig = { securityJsCode }
|
||||
}
|
||||
|
||||
export function loadAmap(): Promise<AMapGlobal> {
|
||||
const key = getAmapKey()
|
||||
if (!key) {
|
||||
return Promise.reject(new Error('未配置 VITE_AMAP_KEY'))
|
||||
}
|
||||
|
||||
if (window.AMap) {
|
||||
return Promise.resolve(window.AMap)
|
||||
}
|
||||
|
||||
if (loadPromise) return loadPromise
|
||||
|
||||
injectSecurityConfig()
|
||||
|
||||
loadPromise = new Promise((resolve, reject) => {
|
||||
const existing = document.getElementById(AMAP_SCRIPT_ID) as HTMLScriptElement | null
|
||||
if (existing) {
|
||||
existing.addEventListener('load', () => {
|
||||
if (window.AMap) resolve(window.AMap)
|
||||
else reject(new Error('高德地图 SDK 加载失败'))
|
||||
})
|
||||
existing.addEventListener('error', () => reject(new Error('高德地图 SDK 加载失败')))
|
||||
return
|
||||
}
|
||||
|
||||
const script = document.createElement('script')
|
||||
script.id = AMAP_SCRIPT_ID
|
||||
script.type = 'text/javascript'
|
||||
script.async = true
|
||||
script.src = `https://webapi.amap.com/maps?v=2.0&key=${encodeURIComponent(key)}`
|
||||
script.onload = () => {
|
||||
if (window.AMap) resolve(window.AMap)
|
||||
else reject(new Error('高德地图 SDK 加载失败'))
|
||||
}
|
||||
script.onerror = () => reject(new Error('高德地图 SDK 加载失败'))
|
||||
document.head.appendChild(script)
|
||||
})
|
||||
|
||||
return loadPromise
|
||||
}
|
||||
|
||||
export function loadAmapPlugins(plugins: string[] = DEFAULT_PLUGINS): Promise<void> {
|
||||
if (pluginsReadyPromise) return pluginsReadyPromise
|
||||
|
||||
pluginsReadyPromise = loadAmap().then(
|
||||
AMap =>
|
||||
new Promise((resolve, reject) => {
|
||||
const timer = window.setTimeout(() => {
|
||||
pluginsReadyPromise = null
|
||||
reject(new Error('高德地图插件加载超时'))
|
||||
}, 15000)
|
||||
AMap.plugin(plugins, () => {
|
||||
window.clearTimeout(timer)
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
)
|
||||
|
||||
return pluginsReadyPromise
|
||||
}
|
||||
|
||||
export function resolveLngLat(lnglat: AMapLngLat): [number, number] {
|
||||
const lng = typeof lnglat.getLng === 'function' ? lnglat.getLng() : lnglat.lng
|
||||
const lat = typeof lnglat.getLat === 'function' ? lnglat.getLat() : lnglat.lat
|
||||
return [lng, lat]
|
||||
}
|
||||
|
||||
export function formatPoiAddress(poi?: AMapPoi) {
|
||||
if (!poi) return ''
|
||||
const parts = [poi.district, poi.address, poi.name].filter(Boolean)
|
||||
return parts.join('').trim() || poi.name || poi.address || ''
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/** 构建高德 URI 导航链接(手机点击可唤起高德 App) */
|
||||
export function buildAmapNavigationUrl(
|
||||
lng?: number | null,
|
||||
lat?: number | null,
|
||||
name?: string
|
||||
): string | null {
|
||||
if (lng == null || lat == null || Number.isNaN(Number(lng)) || Number.isNaN(Number(lat))) {
|
||||
return null
|
||||
}
|
||||
const lngText = Number(lng).toFixed(6)
|
||||
const latText = Number(lat).toFixed(6)
|
||||
const toName = encodeURIComponent((name || '目的地').trim() || '目的地')
|
||||
return `https://uri.amap.com/navigation?to=${lngText},${latText},${toName}&mode=car&coordinate=gaode&callnative=1`
|
||||
}
|
||||
|
||||
export function appendAmapNavLine(lines: string[], label: string, url: string | null) {
|
||||
if (url) {
|
||||
lines.push(`${label}:${url}`)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,497 @@
|
||||
<!-- 运输地址库 -->
|
||||
<template>
|
||||
<div class="page-container">
|
||||
<section class="panel">
|
||||
<div class="panel-top">
|
||||
<h2 class="title-bar-heading">地址库</h2>
|
||||
</div>
|
||||
|
||||
<div class="filter-block">
|
||||
<el-form :model="searchForm" class="filter-form" @submit.prevent>
|
||||
<div class="filter-grid">
|
||||
<el-form-item label="名称/地址">
|
||||
<el-input
|
||||
v-model="searchForm.q"
|
||||
placeholder="搜索名称或详细地址"
|
||||
clearable
|
||||
@keyup.enter="handleSearch"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="searchForm.status" placeholder="全部" clearable>
|
||||
<el-option label="启用" value="1" />
|
||||
<el-option label="停用" value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div class="filter-actions">
|
||||
<div class="filter-actions-end">
|
||||
<el-button class="btn-ghost" :loading="loading" @click="handleReset">
|
||||
<el-icon><RefreshLeft /></el-icon>重置
|
||||
</el-button>
|
||||
<el-button class="btn-main" type="primary" :loading="loading" @click="handleSearch">
|
||||
<el-icon><Search /></el-icon>搜索
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<div class="list-toolbar">
|
||||
<div class="list-toolbar-row">
|
||||
<div class="list-toolbar-selected">
|
||||
已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
|
||||
</div>
|
||||
<el-button class="btn-main" type="primary" @click="handleAdd">
|
||||
<el-icon><Plus /></el-icon>新增地址
|
||||
</el-button>
|
||||
<el-button
|
||||
class="btn-ghost"
|
||||
:disabled="selectedRows.length !== 1"
|
||||
@click="handleToolbarEdit"
|
||||
>
|
||||
<el-icon><Edit /></el-icon>编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
class="btn-ghost"
|
||||
type="danger"
|
||||
:disabled="selectedRows.length !== 1 || !canDeleteByCreator(selectedRows[0])"
|
||||
:title="selectedRows.length === 1 && !canDeleteByCreator(selectedRows[0]) ? CREATOR_DELETE_TIP : ''"
|
||||
@click="handleToolbarDelete"
|
||||
>
|
||||
<el-icon><Delete /></el-icon>删除
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="list-body list-wrap">
|
||||
<el-table
|
||||
class="list-table"
|
||||
:data="addressRows"
|
||||
v-loading="loading"
|
||||
stripe
|
||||
row-key="id"
|
||||
@selection-change="handleSelectionChange"
|
||||
@row-dblclick="handleView"
|
||||
>
|
||||
<el-table-column type="selection" min-width="55" align="center" header-align="center" />
|
||||
<el-table-column
|
||||
type="index"
|
||||
label="序号"
|
||||
min-width="68"
|
||||
align="center"
|
||||
header-align="center"
|
||||
:index="index => (pagination.page - 1) * pagination.pageSize + index + 1"
|
||||
/>
|
||||
<el-table-column prop="addressName" label="地址名称" min-width="140" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-link type="primary" @click="handleView(row)">{{ row.addressName }}</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="fullAddress" label="详细地址" min-width="240" show-overflow-tooltip />
|
||||
<el-table-column label="坐标" min-width="160" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.lng != null && row.lat != null">{{ row.lng }}, {{ row.lat }}</span>
|
||||
<span v-else>—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="contactName" label="联系人" min-width="100" align="center" />
|
||||
<el-table-column prop="contactPhone" label="联系电话" min-width="120" align="center" />
|
||||
<el-table-column prop="status" label="状态" min-width="90" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.status === '1' ? 'success' : 'info'" size="small">
|
||||
{{ row.status === '1' ? '启用' : '停用' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="remark" label="备注" min-width="140" show-overflow-tooltip />
|
||||
</el-table>
|
||||
|
||||
<div class="pagination list-pager-bar">
|
||||
<el-pagination
|
||||
v-model:current-page="pagination.page"
|
||||
v-model:page-size="pagination.pageSize"
|
||||
:total="pagination.total"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@current-change="handlePageChange"
|
||||
@size-change="handleSizeChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
:title="dialogTitle"
|
||||
width="640px"
|
||||
align-center
|
||||
destroy-on-close
|
||||
class="dialog-form-layout pl-transport-address-form-dialog"
|
||||
modal-class="order-module-dialog-mask"
|
||||
:close-on-click-modal="false"
|
||||
style="backdrop-filter: none"
|
||||
@close="handleDialogClose"
|
||||
>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-position="top"
|
||||
scroll-to-error
|
||||
:scroll-into-view-options="{ behavior: 'smooth', block: 'center' }"
|
||||
>
|
||||
<div class="form-grid-2">
|
||||
<el-form-item label="地址名称" prop="addressName">
|
||||
<el-input v-model="formData.addressName" placeholder="如:安庆仓库、某某化工厂" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="formData.status" placeholder="请选择状态">
|
||||
<el-option label="启用" value="1" />
|
||||
<el-option label="停用" value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="详细地址" prop="fullAddress" class="form-col-span-2">
|
||||
<AmapAddressPicker
|
||||
v-model="formData.fullAddress"
|
||||
v-model:lng="formData.lng"
|
||||
v-model:lat="formData.lat"
|
||||
placeholder="请地图选点或搜索 POI"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="联系人">
|
||||
<el-input v-model="formData.contactName" placeholder="联系人(可选)" />
|
||||
</el-form-item>
|
||||
<el-form-item label="联系电话">
|
||||
<el-input v-model="formData.contactPhone" placeholder="联系电话(可选)" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" class="form-col-span-2">
|
||||
<el-input v-model="formData.remark" type="textarea" :rows="2" placeholder="备注(可选)" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">
|
||||
<el-icon><Close /></el-icon>取消
|
||||
</el-button>
|
||||
<el-button type="primary" :loading="submitLoading" @click="handleSubmit">
|
||||
<el-icon><Check /></el-icon>确定
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<SystemViewDialog
|
||||
v-model="viewVisible"
|
||||
title="地址详情"
|
||||
:loading="viewLoading"
|
||||
:hero-title="viewDetail?.addressName || '—'"
|
||||
:hero-meta="viewHeroMeta"
|
||||
:sections="viewSections"
|
||||
>
|
||||
<template #hero-actions>
|
||||
<el-button @click="handleEditFromView">
|
||||
<el-icon><Edit /></el-icon>编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
:disabled="!viewDetail || !canDeleteByCreator(viewDetail)"
|
||||
@click="handleDeleteFromView"
|
||||
>
|
||||
<el-icon><Delete /></el-icon>删除
|
||||
</el-button>
|
||||
</template>
|
||||
</SystemViewDialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Check, Close, Delete, Edit, Plus, RefreshLeft, Search } from '@element-plus/icons-vue'
|
||||
import {
|
||||
transportAddressApi,
|
||||
type TransportAddress,
|
||||
type TransportAddressPayload
|
||||
} from '@/api/pl/index'
|
||||
import AmapAddressPicker from '@/components/business/amap-address-picker/index.vue'
|
||||
import SystemViewDialog, { type ViewSection } from '@/components/business/system-view-dialog/index.vue'
|
||||
import { useDbInstantSearch } from '@/composables/useDbInstantSearch'
|
||||
import { pickBxPageResult } from '@/utils/http/bx'
|
||||
import { DEFAULT_LIST_PAGE_SIZE } from '@/utils/table/tableConfig'
|
||||
import { formatDateTime } from '@/utils/format'
|
||||
import { canDeleteByCreator, CREATOR_DELETE_TIP } from '@/utils/creator-delete'
|
||||
|
||||
const loading = ref(false)
|
||||
const submitLoading = ref(false)
|
||||
const addressRows = ref<TransportAddress[]>([])
|
||||
const selectedRows = ref<TransportAddress[]>([])
|
||||
const dialogVisible = ref(false)
|
||||
const dialogTitle = ref('')
|
||||
const formRef = ref()
|
||||
const isEdit = ref(false)
|
||||
const viewVisible = ref(false)
|
||||
const viewLoading = ref(false)
|
||||
const viewDetail = ref<TransportAddress | null>(null)
|
||||
|
||||
const searchForm = reactive({
|
||||
q: '',
|
||||
status: ''
|
||||
})
|
||||
|
||||
const pagination = reactive({
|
||||
page: 1,
|
||||
pageSize: DEFAULT_LIST_PAGE_SIZE,
|
||||
total: 0
|
||||
})
|
||||
|
||||
const formData = reactive({
|
||||
id: '',
|
||||
addressName: '',
|
||||
fullAddress: '',
|
||||
lng: null as number | null,
|
||||
lat: null as number | null,
|
||||
contactName: '',
|
||||
contactPhone: '',
|
||||
status: '1',
|
||||
remark: ''
|
||||
})
|
||||
|
||||
const formRules = {
|
||||
addressName: [{ required: true, message: '请输入地址名称', trigger: 'blur' }],
|
||||
fullAddress: [{ required: true, message: '请填写详细地址', trigger: 'blur' }],
|
||||
status: [{ required: true, message: '请选择状态', trigger: 'change' }],
|
||||
lng: [{ required: true, message: '请通过地图选点获取经度', trigger: 'change' }],
|
||||
lat: [{ required: true, message: '请通过地图选点获取纬度', trigger: 'change' }]
|
||||
}
|
||||
|
||||
const viewHeroMeta = computed(() => {
|
||||
if (!viewDetail.value) return ''
|
||||
return viewDetail.value.fullAddress || ''
|
||||
})
|
||||
|
||||
const viewSections = computed<ViewSection[]>(() => {
|
||||
const d = viewDetail.value
|
||||
if (!d) return []
|
||||
return [
|
||||
{
|
||||
title: '基本信息',
|
||||
fields: [
|
||||
{ label: '地址名称', value: d.addressName },
|
||||
{ label: '详细地址', value: d.fullAddress, span: 'full' },
|
||||
{
|
||||
label: '坐标',
|
||||
value: d.lng != null && d.lat != null ? `${d.lng}, ${d.lat}` : '—'
|
||||
},
|
||||
{ label: '联系人', value: d.contactName || '—' },
|
||||
{ label: '联系电话', value: d.contactPhone || '—' },
|
||||
{
|
||||
label: '状态',
|
||||
value: d.status === '1' ? '启用' : '停用',
|
||||
tagType: d.status === '1' ? 'success' : 'info'
|
||||
},
|
||||
{ label: '创建时间', value: formatDateTime(d.createTime) },
|
||||
{ label: '更新时间', value: formatDateTime(d.updateTime) },
|
||||
{ label: '备注', value: d.remark || '—', span: 'full' }
|
||||
]
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
async function fetchData() {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await transportAddressApi.list({
|
||||
q: searchForm.q || undefined,
|
||||
status: searchForm.status || undefined,
|
||||
page: pagination.page,
|
||||
pageSize: pagination.pageSize
|
||||
})
|
||||
const { list, total } = pickBxPageResult<TransportAddress>(res)
|
||||
addressRows.value = list
|
||||
pagination.total = total
|
||||
const idSet = new Set(addressRows.value.map(item => item.id))
|
||||
selectedRows.value = selectedRows.value.filter(item => idSet.has(item.id))
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function handleSearch() {
|
||||
pagination.page = 1
|
||||
fetchData()
|
||||
}
|
||||
|
||||
function handleReset() {
|
||||
searchForm.q = ''
|
||||
searchForm.status = ''
|
||||
selectedRows.value = []
|
||||
pagination.page = 1
|
||||
fetchData()
|
||||
}
|
||||
|
||||
function handlePageChange() {
|
||||
fetchData()
|
||||
}
|
||||
|
||||
function handleSizeChange() {
|
||||
pagination.page = 1
|
||||
fetchData()
|
||||
}
|
||||
|
||||
function handleSelectionChange(rows: TransportAddress[]) {
|
||||
selectedRows.value = rows
|
||||
}
|
||||
|
||||
function handleToolbarEdit() {
|
||||
if (selectedRows.value.length !== 1) {
|
||||
ElMessage.warning('请选择一条地址记录')
|
||||
return
|
||||
}
|
||||
handleEdit(selectedRows.value[0])
|
||||
}
|
||||
|
||||
function handleToolbarDelete() {
|
||||
if (selectedRows.value.length !== 1) {
|
||||
ElMessage.warning('请选择一条地址记录')
|
||||
return
|
||||
}
|
||||
handleDelete(selectedRows.value[0])
|
||||
}
|
||||
|
||||
function handleAdd() {
|
||||
dialogTitle.value = '新增地址'
|
||||
isEdit.value = false
|
||||
Object.assign(formData, {
|
||||
id: '',
|
||||
addressName: '',
|
||||
fullAddress: '',
|
||||
lng: null,
|
||||
lat: null,
|
||||
contactName: '',
|
||||
contactPhone: '',
|
||||
status: '1',
|
||||
remark: ''
|
||||
})
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
async function handleView(row: TransportAddress) {
|
||||
viewVisible.value = true
|
||||
viewLoading.value = true
|
||||
viewDetail.value = null
|
||||
try {
|
||||
const res = await transportAddressApi.get(row.id)
|
||||
viewDetail.value = (res.data ?? res) as TransportAddress
|
||||
} catch {
|
||||
viewVisible.value = false
|
||||
ElMessage.error('加载地址详情失败')
|
||||
} finally {
|
||||
viewLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function handleEdit(row: TransportAddress) {
|
||||
dialogTitle.value = '编辑地址'
|
||||
isEdit.value = true
|
||||
dialogVisible.value = true
|
||||
try {
|
||||
const res = await transportAddressApi.get(row.id)
|
||||
const detail = (res.data ?? res) as TransportAddress
|
||||
Object.assign(formData, {
|
||||
id: detail.id,
|
||||
addressName: detail.addressName,
|
||||
fullAddress: detail.fullAddress,
|
||||
lng: detail.lng != null ? Number(detail.lng) : null,
|
||||
lat: detail.lat != null ? Number(detail.lat) : null,
|
||||
contactName: detail.contactName || '',
|
||||
contactPhone: detail.contactPhone || '',
|
||||
status: detail.status,
|
||||
remark: detail.remark || ''
|
||||
})
|
||||
} catch {
|
||||
ElMessage.error('加载地址详情失败')
|
||||
}
|
||||
}
|
||||
|
||||
function handleEditFromView() {
|
||||
if (!viewDetail.value) return
|
||||
viewVisible.value = false
|
||||
handleEdit(viewDetail.value)
|
||||
}
|
||||
|
||||
async function handleDeleteFromView() {
|
||||
if (!viewDetail.value) return
|
||||
await handleDelete(viewDetail.value)
|
||||
viewVisible.value = false
|
||||
viewDetail.value = null
|
||||
}
|
||||
|
||||
function validateCoordinates(): boolean {
|
||||
if (formData.lng == null || formData.lat == null) {
|
||||
ElMessage.warning('请通过地图选点填写经纬度')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
const valid = await formRef.value?.validate().catch(() => false)
|
||||
if (!valid || !validateCoordinates()) return
|
||||
|
||||
submitLoading.value = true
|
||||
try {
|
||||
const payload: TransportAddressPayload = {
|
||||
addressName: formData.addressName.trim(),
|
||||
fullAddress: formData.fullAddress.trim(),
|
||||
lng: formData.lng as number,
|
||||
lat: formData.lat as number,
|
||||
contactName: formData.contactName.trim() || undefined,
|
||||
contactPhone: formData.contactPhone.trim() || undefined,
|
||||
status: formData.status,
|
||||
remark: formData.remark.trim() || undefined
|
||||
}
|
||||
|
||||
if (isEdit.value) {
|
||||
await transportAddressApi.update(formData.id, payload)
|
||||
} else {
|
||||
await transportAddressApi.create(payload)
|
||||
}
|
||||
ElMessage.success(isEdit.value ? '编辑成功' : '新增成功')
|
||||
dialogVisible.value = false
|
||||
fetchData()
|
||||
} finally {
|
||||
submitLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function handleDialogClose() {
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
|
||||
async function handleDelete(row: TransportAddress) {
|
||||
if (!canDeleteByCreator(row)) {
|
||||
ElMessage.warning(CREATOR_DELETE_TIP)
|
||||
return
|
||||
}
|
||||
await ElMessageBox.confirm('确定删除该地址吗?', '提示', { type: 'warning' })
|
||||
await transportAddressApi.delete(row.id)
|
||||
ElMessage.success('删除成功')
|
||||
if (viewDetail.value?.id === row.id) {
|
||||
viewVisible.value = false
|
||||
viewDetail.value = null
|
||||
}
|
||||
fetchData()
|
||||
}
|
||||
|
||||
useDbInstantSearch(
|
||||
() => ({
|
||||
q: searchForm.q,
|
||||
status: searchForm.status
|
||||
}),
|
||||
handleSearch
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
fetchData()
|
||||
})
|
||||
</script>
|
||||
@@ -15,6 +15,35 @@
|
||||
<van-cell title="路线" :value="routeDisplay" class="route-cell" />
|
||||
</van-cell-group>
|
||||
|
||||
<van-cell-group
|
||||
v-if="pickupNavUrl || destinationNavUrl"
|
||||
inset
|
||||
class="section-card nav-card"
|
||||
title="导航"
|
||||
>
|
||||
<div class="nav-actions">
|
||||
<van-button
|
||||
v-if="pickupNavUrl"
|
||||
type="primary"
|
||||
block
|
||||
round
|
||||
@click="openNavigation(pickupNavUrl)"
|
||||
>
|
||||
导航到取货点
|
||||
</van-button>
|
||||
<van-button
|
||||
v-if="destinationNavUrl"
|
||||
type="primary"
|
||||
plain
|
||||
block
|
||||
round
|
||||
@click="openNavigation(destinationNavUrl)"
|
||||
>
|
||||
导航到送货点
|
||||
</van-button>
|
||||
</div>
|
||||
</van-cell-group>
|
||||
|
||||
<van-cell-group v-if="page.products.length" inset class="section-card" title="产品明细">
|
||||
<van-cell
|
||||
v-for="(item, idx) in page.products"
|
||||
@@ -72,6 +101,7 @@
|
||||
type PublicTransportProduct
|
||||
} from '@/api/pl/public-transport'
|
||||
import PublicMobileShell from '@/layouts/PublicMobileShell.vue'
|
||||
import { buildAmapNavigationUrl } from '@/utils/amap/navigation'
|
||||
|
||||
import 'vant/es/toast/style'
|
||||
import '@/styles/public-mobile-vant.scss'
|
||||
@@ -99,6 +129,28 @@
|
||||
return `${page.value.pickupAddress || '—'} → ${page.value.destinationAddress || '—'}`
|
||||
})
|
||||
|
||||
const pickupNavUrl = computed(() => {
|
||||
if (!page.value) return null
|
||||
return buildAmapNavigationUrl(
|
||||
page.value.pickupLng,
|
||||
page.value.pickupLat,
|
||||
page.value.pickupAddress || '取货点'
|
||||
)
|
||||
})
|
||||
|
||||
const destinationNavUrl = computed(() => {
|
||||
if (!page.value) return null
|
||||
return buildAmapNavigationUrl(
|
||||
page.value.destinationLng,
|
||||
page.value.destinationLat,
|
||||
page.value.destinationAddress || '送货点'
|
||||
)
|
||||
})
|
||||
|
||||
function openNavigation(url: string) {
|
||||
window.location.href = url
|
||||
}
|
||||
|
||||
function formatProductQuantity(value?: number) {
|
||||
if (value == null || Number.isNaN(Number(value))) return '—'
|
||||
const num = Number(value)
|
||||
@@ -207,4 +259,11 @@
|
||||
.uploader-wrap {
|
||||
padding: 12px 16px 16px;
|
||||
}
|
||||
|
||||
.nav-actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
padding: 12px 16px 16px;
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user