feat(pl): add Excel export for plan and transport lists
Support filtered/selected export on plan, archive, cert and transport pages. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
b06fb6ac56
commit
6a89452f1a
7 files changed
+339
-5
No files matched your search
+126
-2
@@ -1,9 +1,10 @@
|
||||
"""运输计划管理 - 计划 API"""
|
||||
import re
|
||||
from datetime import date
|
||||
from typing import Optional
|
||||
from datetime import date, datetime
|
||||
from typing import List, Optional
|
||||
|
||||
from fastapi import APIRouter, Body, Depends, HTTPException, Query, status
|
||||
from pydantic import BaseModel, Field
|
||||
from sqlalchemy import func, or_
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
@@ -14,6 +15,13 @@ from app.core.delete_guard import assert_row_deletable_by_creator, user_info_fro
|
||||
from app.core.menu_guard import SUPER_ADMIN_ROLE, get_user_menu_codes
|
||||
from app.services.pl_data_scope import apply_pl_scope, assert_pl_row_in_scope
|
||||
from app.core.tenant_context import get_tenant_no
|
||||
from app.utils.excel_export import (
|
||||
EXPORT_MAX_ROWS,
|
||||
ExportColumn,
|
||||
ExportValidationError,
|
||||
export_rows_to_xlsx_response,
|
||||
filter_export_columns,
|
||||
)
|
||||
from app.core.utils import generate_uuid
|
||||
from app.models.crm_models import CrmContract, CrmContractProduct, CrmProduct
|
||||
from app.models.db_models import DbInboundOutbound
|
||||
@@ -465,6 +473,122 @@ def list_plans(
|
||||
return PlanListResponse(total=total, list=result_list)
|
||||
|
||||
|
||||
|
||||
PLAN_EXPORT_COLUMNS = [
|
||||
ExportColumn(title="计划编号", field="planNo"),
|
||||
ExportColumn(title="跟进状态", field="followStatus"),
|
||||
ExportColumn(title="计划日期", field="planDate"),
|
||||
ExportColumn(title="单据类型", field="planType"),
|
||||
ExportColumn(title="供方", field="supplierName"),
|
||||
ExportColumn(title="需方", field="customerName"),
|
||||
ExportColumn(title="车牌号", field="licensePlate"),
|
||||
ExportColumn(title="司机", field="driverName"),
|
||||
ExportColumn(title="运输状态", field="transportStatus"),
|
||||
ExportColumn(title="归档状态", field="archiveStatus"),
|
||||
ExportColumn(title="备注", field="remark"),
|
||||
]
|
||||
|
||||
|
||||
class PlanExportBody(BaseModel):
|
||||
q: Optional[str] = None
|
||||
plan_no: Optional[str] = Field(default=None, alias="planNo")
|
||||
plan_type: Optional[str] = Field(default=None, alias="planType")
|
||||
follow_status: Optional[str] = Field(default=None, alias="followStatus")
|
||||
transport_status: Optional[str] = Field(default=None, alias="transportStatus")
|
||||
archive_status: Optional[str] = Field(default=None, alias="archiveStatus")
|
||||
customer_name: Optional[str] = Field(default=None, alias="customerName")
|
||||
product_name: Optional[str] = Field(default=None, alias="productName")
|
||||
license_plate: Optional[str] = Field(default=None, alias="licensePlate")
|
||||
plan_date_from: Optional[date] = Field(default=None, alias="planDateFrom")
|
||||
plan_date_to: Optional[date] = Field(default=None, alias="planDateTo")
|
||||
has_hazard_product: Optional[int] = Field(default=None, alias="hasHazardProduct")
|
||||
cert_upload_status: Optional[str] = Field(default=None, alias="certUploadStatus")
|
||||
ids: Optional[List[str]] = None
|
||||
columns: Optional[List[str]] = None
|
||||
|
||||
model_config = {"populate_by_name": True}
|
||||
|
||||
|
||||
@router.post("/export")
|
||||
def export_plans(
|
||||
body: PlanExportBody,
|
||||
db: Session = Depends(get_db),
|
||||
user: DbUserContext = Depends(get_db_current_user),
|
||||
):
|
||||
"""导出计划列表(当前筛选全量,或 ids 已选)。"""
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
|
||||
query = db.query(PlPlan).filter(
|
||||
PlPlan.tenant_no == tenant_no,
|
||||
PlPlan.del_flag == 0,
|
||||
)
|
||||
if body.q:
|
||||
q_kw = body.q.strip()
|
||||
if q_kw:
|
||||
q_lower = q_kw.lower()
|
||||
query = query.filter(
|
||||
or_(
|
||||
PlPlan.plan_no.contains(q_kw),
|
||||
func.lower(PlPlan.supplier_name).contains(q_lower),
|
||||
func.lower(PlPlan.customer_name).contains(q_lower),
|
||||
func.lower(PlPlan.destination_name).contains(q_lower),
|
||||
_plan_product_name_exists(db, tenant_no, q_kw),
|
||||
_plan_plate_match(q_kw),
|
||||
)
|
||||
)
|
||||
if body.plan_no:
|
||||
query = query.filter(PlPlan.plan_no.contains(body.plan_no))
|
||||
if body.plan_type:
|
||||
query = query.filter(PlPlan.plan_type == body.plan_type)
|
||||
if body.follow_status:
|
||||
query = query.filter(PlPlan.follow_status == body.follow_status)
|
||||
if body.transport_status:
|
||||
query = query.filter(PlPlan.transport_status == body.transport_status)
|
||||
if body.archive_status:
|
||||
query = query.filter(PlPlan.archive_status == body.archive_status)
|
||||
if body.customer_name:
|
||||
query = query.filter(func.lower(PlPlan.customer_name).contains(body.customer_name.lower()))
|
||||
if body.product_name:
|
||||
product_name_kw = body.product_name.strip()
|
||||
if product_name_kw:
|
||||
query = query.filter(_plan_product_name_exists(db, tenant_no, product_name_kw))
|
||||
if body.license_plate:
|
||||
plate_kw = body.license_plate.strip()
|
||||
if plate_kw:
|
||||
query = query.filter(_plan_plate_match(plate_kw))
|
||||
if body.plan_date_from:
|
||||
query = query.filter(PlPlan.plan_date >= body.plan_date_from)
|
||||
if body.plan_date_to:
|
||||
query = query.filter(PlPlan.plan_date <= body.plan_date_to)
|
||||
if body.has_hazard_product == 1:
|
||||
query = query.filter(plan_has_hazard_product_exists(tenant_no))
|
||||
if (body.cert_upload_status or "").strip().lower() == "incomplete":
|
||||
query = query.filter(plan_has_incomplete_hazard_cert_exists(tenant_no))
|
||||
if body.ids:
|
||||
query = query.filter(PlPlan.id.in_(body.ids))
|
||||
query = apply_pl_scope(query, PlPlan, user, db, tenant_no)
|
||||
plans = query.order_by(PlPlan.create_time.desc()).limit(EXPORT_MAX_ROWS + 1).all()
|
||||
items = []
|
||||
for plan in plans:
|
||||
data = PlanWithProducts.model_validate(plan).model_dump(by_alias=True)
|
||||
# flatten date
|
||||
if data.get("planDate") is not None:
|
||||
data["planDate"] = str(data["planDate"])[:10]
|
||||
items.append(data)
|
||||
cols = filter_export_columns(PLAN_EXPORT_COLUMNS, body.columns)
|
||||
date_str = datetime.now().strftime("%Y%m%d")
|
||||
return export_rows_to_xlsx_response(
|
||||
db,
|
||||
tenant_no=tenant_no,
|
||||
sheet_title="计划列表",
|
||||
columns=cols,
|
||||
rows=items,
|
||||
filename_stem=f"计划列表_{date_str}",
|
||||
)
|
||||
except ExportValidationError as e:
|
||||
raise HTTPException(status_code=400, detail=str(e)) from e
|
||||
|
||||
|
||||
@router.get("/count-by-contract")
|
||||
def count_plans_by_contract(
|
||||
contract_ids: str = Query(..., alias="contractIds", description="合同ID,逗号分隔"),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""运输计划管理 - 运输 API"""
|
||||
from datetime import date, datetime
|
||||
from typing import Optional
|
||||
from typing import List, Optional
|
||||
import json
|
||||
import secrets
|
||||
|
||||
@@ -13,6 +13,13 @@ from app.core.database import get_db
|
||||
from app.core.db_auth import DbUserContext, get_db_current_user
|
||||
from app.services.pl_data_scope import apply_pl_scope, assert_pl_row_in_scope
|
||||
from app.core.tenant_context import get_tenant_no
|
||||
from app.utils.excel_export import (
|
||||
EXPORT_MAX_ROWS,
|
||||
ExportColumn,
|
||||
ExportValidationError,
|
||||
export_rows_to_xlsx_response,
|
||||
filter_export_columns,
|
||||
)
|
||||
from app.core.utils import generate_uuid
|
||||
from app.core.config import settings
|
||||
from app.models.pl_models import (
|
||||
@@ -432,6 +439,85 @@ def list_transports(
|
||||
# =============================================================================
|
||||
|
||||
|
||||
|
||||
TRANSPORT_EXPORT_COLUMNS = [
|
||||
ExportColumn(title="运输编号", field="transportNo"),
|
||||
ExportColumn(title="车牌号", field="licensePlate"),
|
||||
ExportColumn(title="司机", field="driverName"),
|
||||
ExportColumn(title="承运商", field="carrierName"),
|
||||
ExportColumn(title="运输介质", field="transportMedium"),
|
||||
ExportColumn(title="起点", field="startPoint"),
|
||||
ExportColumn(title="终点", field="endPoint"),
|
||||
ExportColumn(title="运输状态", field="transportStatus"),
|
||||
ExportColumn(title="审批状态", field="approvalStatus"),
|
||||
ExportColumn(title="备注", field="remark"),
|
||||
]
|
||||
|
||||
|
||||
class TransportExportBody(BaseModel):
|
||||
q: Optional[str] = None
|
||||
transport_no: Optional[str] = Field(default=None, alias="transportNo")
|
||||
transport_status: Optional[str] = Field(default=None, alias="transportStatus")
|
||||
approval_status: Optional[str] = Field(default=None, alias="approvalStatus")
|
||||
vehicle_id: Optional[str] = Field(default=None, alias="vehicleId")
|
||||
driver_id: Optional[str] = Field(default=None, alias="driverId")
|
||||
ids: Optional[List[str]] = None
|
||||
columns: Optional[List[str]] = None
|
||||
|
||||
model_config = {"populate_by_name": True}
|
||||
|
||||
|
||||
@router.post("/export")
|
||||
def export_transports(
|
||||
body: TransportExportBody,
|
||||
db: Session = Depends(get_db),
|
||||
user: DbUserContext = Depends(get_db_current_user),
|
||||
):
|
||||
"""导出运输列表(当前筛选全量,或 ids 已选)。"""
|
||||
try:
|
||||
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
|
||||
query = db.query(PlTransport).filter(
|
||||
PlTransport.tenant_no == tenant_no,
|
||||
PlTransport.del_flag == 0,
|
||||
)
|
||||
if body.q:
|
||||
query = query.filter(
|
||||
or_(
|
||||
PlTransport.transport_no.contains(body.q),
|
||||
PlTransport.license_plate.contains(body.q),
|
||||
PlTransport.driver_name.contains(body.q),
|
||||
)
|
||||
)
|
||||
if body.transport_no:
|
||||
query = query.filter(PlTransport.transport_no.contains(body.transport_no))
|
||||
if body.transport_status:
|
||||
query = query.filter(PlTransport.transport_status == body.transport_status)
|
||||
if body.approval_status:
|
||||
query = query.filter(PlTransport.approval_status == body.approval_status)
|
||||
if body.vehicle_id:
|
||||
query = query.filter(PlTransport.vehicle_id == body.vehicle_id)
|
||||
if body.driver_id:
|
||||
query = query.filter(PlTransport.driver_id == body.driver_id)
|
||||
if body.ids:
|
||||
query = query.filter(PlTransport.id.in_(body.ids))
|
||||
query = apply_pl_scope(query, PlTransport, user, db, tenant_no)
|
||||
rows = query.order_by(PlTransport.create_time.desc()).limit(EXPORT_MAX_ROWS + 1).all()
|
||||
items = [TransportRead.model_validate(r).model_dump(by_alias=True) for r in rows]
|
||||
cols = filter_export_columns(TRANSPORT_EXPORT_COLUMNS, body.columns)
|
||||
date_str = datetime.now().strftime("%Y%m%d")
|
||||
return export_rows_to_xlsx_response(
|
||||
db,
|
||||
tenant_no=tenant_no,
|
||||
sheet_title="运输列表",
|
||||
columns=cols,
|
||||
rows=items,
|
||||
filename_stem=f"运输列表_{date_str}",
|
||||
)
|
||||
except ExportValidationError as e:
|
||||
raise HTTPException(status_code=400, detail=str(e)) from e
|
||||
|
||||
|
||||
|
||||
@router.post("/", response_model=TransportRead, status_code=status.HTTP_201_CREATED)
|
||||
def create_transport(
|
||||
payload: TransportCreate,
|
||||
|
||||
@@ -138,9 +138,9 @@ export interface VehicleQuery {
|
||||
|
||||
export const vehicleApi = {
|
||||
list: (params?: VehicleQuery) =>
|
||||
request.get<PlListResponse<Vehicle>>('/transport/vehicle', { params }),
|
||||
export: (data: Record<string, any>) =>
|
||||
request.post('/transport/vehicle/export', data, { responseType: 'blob' }),
|
||||
request.get<PlListResponse<Vehicle>>('/transport/vehicle', { params }),
|
||||
get: (id: string) => request.get<VehicleDetail>(`/transport/vehicle/${id}`),
|
||||
create: (data: VehiclePayload) => request.post<VehicleDetail>('/transport/vehicle', data),
|
||||
update: (id: string, data: Partial<VehiclePayload>) =>
|
||||
@@ -194,9 +194,9 @@ export interface PersonnelQuery {
|
||||
|
||||
export const personnelApi = {
|
||||
list: (params?: PersonnelQuery) =>
|
||||
request.get<PlListResponse<Personnel>>('/transport/personnel', { params }),
|
||||
export: (data: Record<string, any>) =>
|
||||
request.post('/transport/personnel/export', data, { responseType: 'blob' }),
|
||||
request.get<PlListResponse<Personnel>>('/transport/personnel', { params }),
|
||||
get: (id: string) => request.get<PersonnelDetail>(`/transport/personnel/${id}`),
|
||||
create: (data: PersonnelPayload) => request.post<PersonnelDetail>('/transport/personnel', data),
|
||||
update: (id: string, data: Partial<PersonnelPayload>) =>
|
||||
@@ -696,6 +696,8 @@ export interface PlanContractGapQuery {
|
||||
|
||||
export const planApi = {
|
||||
list: (params?: PlanQuery) => request.get<PlanListResponse>('/transport/plan/', { params }),
|
||||
export: (data: Record<string, any>) =>
|
||||
request.post('/transport/plan/export', data, { responseType: 'blob' }),
|
||||
get: (id: string) => request.get<Plan>(`/transport/plan/${id}`),
|
||||
countByContract: (contractIds: string[]) =>
|
||||
request.get<{ counts: Record<string, number> }>('/transport/plan/count-by-contract', {
|
||||
@@ -1002,6 +1004,8 @@ export interface TransportListResponse {
|
||||
|
||||
export const transportApi = {
|
||||
list: (params?: TransportQuery) => request.get<TransportListResponse>('/transport/order/', { params }),
|
||||
export: (data: Record<string, any>) =>
|
||||
request.post('/transport/order/export', data, { responseType: 'blob' }),
|
||||
get: (id: string) => request.get<Transport>(`/transport/order/${id}`),
|
||||
create: (data: Partial<Transport> & { planIds?: string[] }) =>
|
||||
request.post<Transport>('/transport/order/', data),
|
||||
|
||||
@@ -95,6 +95,15 @@
|
||||
<el-icon><FolderChecked /></el-icon>
|
||||
{{ selectedPlan ? archiveButtonLabel(selectedPlan) : '归档' }}
|
||||
</el-button>
|
||||
<ListExportDropdown
|
||||
:export-fn="planApi.export"
|
||||
:get-filter-params="getPlanArchiveExportFilterParams"
|
||||
:has-selection="true"
|
||||
:selected-ids="selectedRows.map((r) => r.id)"
|
||||
:visible-columns="planArchiveExportColumns"
|
||||
filename="计划归档.xlsx"
|
||||
/>
|
||||
|
||||
<ListToolbarEndActions
|
||||
:loading="loading"
|
||||
:column-defs="listColumnDefs"
|
||||
@@ -351,6 +360,7 @@
|
||||
<script setup lang="ts">
|
||||
import ListQuickSearchInput from '@/components/business/list-quick-search-input/index.vue'
|
||||
import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue'
|
||||
import ListExportDropdown from '@/components/business/list-export-dropdown/index.vue'
|
||||
import ListColumnStatsRow from '@/components/business/list-column-stats-row/index.vue'
|
||||
import type { StatsRowColumn } from '@/utils/list-column-stats'
|
||||
import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
|
||||
@@ -478,6 +488,28 @@ function getArchiveStatCellValue(row: Plan, prop: string): unknown {
|
||||
}
|
||||
|
||||
const selectedRows = ref<Plan[]>([])
|
||||
function getPlanArchiveExportFilterParams() {
|
||||
const keyword = quickSearch.value.trim()
|
||||
return {
|
||||
q: keyword || undefined,
|
||||
planNo: keyword ? undefined : searchForm.planNo || undefined,
|
||||
planType: searchForm.planType || undefined,
|
||||
followStatus: searchForm.followStatus || undefined,
|
||||
transportStatus: 'arranged',
|
||||
customerName: keyword ? undefined : searchForm.customerName || undefined,
|
||||
productName: keyword ? undefined : searchForm.productName || undefined,
|
||||
archiveStatus: searchForm.archiveStatus || undefined,
|
||||
planDateFrom: dateRange.value?.[0] || undefined,
|
||||
planDateTo: dateRange.value?.[1] || undefined
|
||||
}
|
||||
}
|
||||
|
||||
const planArchiveExportColumns = computed(() => {
|
||||
const cols = [...visibleColumns.value]
|
||||
if (!cols.includes('planNo')) cols.unshift('planNo')
|
||||
return cols
|
||||
})
|
||||
|
||||
const viewingPlan = ref<Plan | null>(null)
|
||||
|
||||
function syncViewingPlanFromList() {
|
||||
|
||||
@@ -135,6 +135,15 @@
|
||||
<el-icon><CircleClose /></el-icon>取消齐备
|
||||
</el-button>
|
||||
|
||||
<ListExportDropdown
|
||||
:export-fn="planApi.export"
|
||||
:get-filter-params="getPlanCertExportFilterParams"
|
||||
:has-selection="true"
|
||||
:selected-ids="selectedRows.map((r) => r.id)"
|
||||
:visible-columns="planCertExportColumns"
|
||||
filename="计划证件.xlsx"
|
||||
/>
|
||||
|
||||
<ListToolbarEndActions
|
||||
:loading="loading"
|
||||
:column-defs="listColumnDefs"
|
||||
@@ -413,6 +422,7 @@ import PlPlanListCertCell from '@/components/business/pl-plan-list-cert-cell/ind
|
||||
import PlPlanListProductCell from '@/components/business/pl-plan-list-product-cell/index.vue'
|
||||
import ListQuickSearchInput from '@/components/business/list-quick-search-input/index.vue'
|
||||
import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue'
|
||||
import ListExportDropdown from '@/components/business/list-export-dropdown/index.vue'
|
||||
import {
|
||||
buildHazardCertReadyConfirmMessage,
|
||||
isHazardCertReady,
|
||||
@@ -477,6 +487,27 @@ const {
|
||||
} = usePersistedListColumns('pl-plan-cert-list-columns', listColumnDefs)
|
||||
|
||||
const selectedRows = ref<Plan[]>([])
|
||||
function getPlanCertExportFilterParams() {
|
||||
const keyword = quickSearch.value.trim()
|
||||
return {
|
||||
q: keyword || undefined,
|
||||
planNo: keyword ? undefined : searchForm.planNo || undefined,
|
||||
planType: searchForm.planType || undefined,
|
||||
followStatus: searchForm.followStatus || undefined,
|
||||
customerName: keyword ? undefined : searchForm.customerName || undefined,
|
||||
planDateFrom: dateRange.value?.[0] || undefined,
|
||||
planDateTo: dateRange.value?.[1] || undefined,
|
||||
hasHazardProduct: 1,
|
||||
certUploadStatus: searchForm.certUploadStatus || undefined
|
||||
}
|
||||
}
|
||||
|
||||
const planCertExportColumns = computed(() => {
|
||||
const cols = [...visibleColumns.value]
|
||||
if (!cols.includes('planNo')) cols.unshift('planNo')
|
||||
return cols
|
||||
})
|
||||
|
||||
const viewingPlan = ref<Plan | null>(null)
|
||||
const selectedPlan = computed(() => (selectedRows.value.length === 1 ? selectedRows.value[0] : null))
|
||||
const dateRange = ref<string[]>([])
|
||||
|
||||
@@ -183,6 +183,15 @@
|
||||
<el-icon><EditPen /></el-icon>强制改状态
|
||||
</el-button>
|
||||
|
||||
<ListExportDropdown
|
||||
:export-fn="planApi.export"
|
||||
:get-filter-params="getPlanExportFilterParams"
|
||||
:has-selection="true"
|
||||
:selected-ids="selectedRows.map((r) => r.id)"
|
||||
:visible-columns="planExportColumns"
|
||||
filename="计划列表.xlsx"
|
||||
/>
|
||||
|
||||
<ListToolbarEndActions
|
||||
:loading="loading"
|
||||
:column-defs="listColumnDefs"
|
||||
@@ -626,6 +635,7 @@ import PlPasteRecognizeDialog from '@/components/business/pl-paste-recognize-dia
|
||||
import PlPlanFormDialog from '@/components/business/pl-plan-form-dialog/index.vue'
|
||||
import ListQuickSearchInput from '@/components/business/list-quick-search-input/index.vue'
|
||||
import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue'
|
||||
import ListExportDropdown from '@/components/business/list-export-dropdown/index.vue'
|
||||
import ListColumnStatsRow from '@/components/business/list-column-stats-row/index.vue'
|
||||
import type { StatsRowColumn } from '@/utils/list-column-stats'
|
||||
import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
|
||||
@@ -761,6 +771,27 @@ function getPlanStatCellValue(row: Plan, prop: string): unknown {
|
||||
}
|
||||
|
||||
const selectedRows = ref<Plan[]>([])
|
||||
function getPlanExportFilterParams() {
|
||||
const keyword = quickSearch.value.trim()
|
||||
return {
|
||||
q: keyword || undefined,
|
||||
planNo: keyword ? undefined : searchForm.planNo || undefined,
|
||||
planType: tabFilters.planType || undefined,
|
||||
followStatus: tabFilters.followStatus || searchForm.followStatus || undefined,
|
||||
customerName: keyword ? undefined : searchForm.customerName || undefined,
|
||||
productName: keyword ? undefined : searchForm.productName || undefined,
|
||||
licensePlate: keyword ? undefined : searchForm.licensePlate || undefined,
|
||||
planDateFrom: dateRange.value?.[0] || undefined,
|
||||
planDateTo: dateRange.value?.[1] || undefined
|
||||
}
|
||||
}
|
||||
|
||||
const planExportColumns = computed(() => {
|
||||
const cols = [...visibleColumns.value]
|
||||
if (!cols.includes('planNo')) cols.unshift('planNo')
|
||||
return cols
|
||||
})
|
||||
|
||||
const viewingPlan = ref<Plan | null>(null)
|
||||
|
||||
function syncViewingPlanFromList() {
|
||||
|
||||
@@ -62,6 +62,15 @@
|
||||
<el-icon><Share /></el-icon>二维码
|
||||
</el-button>
|
||||
|
||||
<ListExportDropdown
|
||||
:export-fn="transportApi.export"
|
||||
:get-filter-params="getTransportExportFilterParams"
|
||||
:has-selection="true"
|
||||
:selected-ids="selectedRows.map((r) => r.id)"
|
||||
:visible-columns="transportExportColumns"
|
||||
filename="运输列表.xlsx"
|
||||
/>
|
||||
|
||||
<ListToolbarEndActions
|
||||
:loading="loading"
|
||||
:column-defs="listColumnDefs"
|
||||
@@ -250,6 +259,7 @@ import SystemViewDialog, {
|
||||
import { transportApi, type Transport } from '@/api/pl'
|
||||
import ListQuickSearchInput from '@/components/business/list-quick-search-input/index.vue'
|
||||
import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue'
|
||||
import ListExportDropdown from '@/components/business/list-export-dropdown/index.vue'
|
||||
import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
|
||||
import { useDbInstantSearch } from '@/composables/useDbInstantSearch'
|
||||
import { formatPlVehiclePlateDisplay } from '@/utils/pl-display'
|
||||
@@ -283,6 +293,22 @@ const detailLoading = ref(false)
|
||||
const qrcodeLoading = ref(false)
|
||||
const transportList = ref<Transport[]>([])
|
||||
const selectedRows = ref<Transport[]>([])
|
||||
function getTransportExportFilterParams() {
|
||||
const keyword = quickSearchKeyword.value.trim()
|
||||
return {
|
||||
q: keyword || undefined,
|
||||
transportNo: keyword ? undefined : searchForm.transportNo || undefined,
|
||||
transportStatus: searchForm.transportStatus || undefined,
|
||||
approvalStatus: searchForm.approvalStatus || undefined
|
||||
}
|
||||
}
|
||||
|
||||
const transportExportColumns = computed(() => {
|
||||
const cols = [...visibleColumns.value]
|
||||
if (!cols.includes('transportNo')) cols.unshift('transportNo')
|
||||
return cols
|
||||
})
|
||||
|
||||
const detailVisible = ref(false)
|
||||
const currentTransport = ref<Transport | null>(null)
|
||||
|
||||
|
||||
Reference in new issue
Block a user