feat(pl): add Excel export for vehicle maintenance apply list
Support filtered and selected rows via backend export and ListExportDropdown. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
6a89452f1a
commit
441e1fdae1
4 files changed
+117
-2
No files matched your search
@@ -1,13 +1,22 @@
|
||||
"""车辆维保 API"""
|
||||
from datetime import date
|
||||
from typing import Optional
|
||||
from datetime import datetime
|
||||
from typing import List, Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, Query, status
|
||||
from pydantic import BaseModel, Field
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.core.database import get_db
|
||||
from app.core.db_auth import DbUserContext, get_db_current_user
|
||||
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.schemas.pl.vehicle_maintenance import (
|
||||
VehicleMaintenanceApplyCreate,
|
||||
VehicleMaintenanceApplyListResponse,
|
||||
@@ -74,6 +83,76 @@ def list_reminders(
|
||||
)
|
||||
|
||||
|
||||
|
||||
MAINT_APPLY_EXPORT_COLUMNS = [
|
||||
ExportColumn(title="申请单号", field="applyNo"),
|
||||
ExportColumn(title="车牌号", field="licensePlate"),
|
||||
ExportColumn(title="维保类型", field="maintenanceType"),
|
||||
ExportColumn(title="申请日期", field="applyDate"),
|
||||
ExportColumn(title="开始日期", field="startDate"),
|
||||
ExportColumn(title="预计取车", field="expectedPickupDate"),
|
||||
ExportColumn(title="申请人", field="applicantName"),
|
||||
ExportColumn(title="状态", field="status"),
|
||||
ExportColumn(title="创建时间", field="createTime"),
|
||||
]
|
||||
|
||||
|
||||
class MaintenanceApplyExportBody(BaseModel):
|
||||
q: Optional[str] = None
|
||||
maintenance_type: Optional[str] = Field(default=None, alias="maintenanceType")
|
||||
status: Optional[str] = None
|
||||
apply_date_from: Optional[date] = Field(default=None, alias="applyDateFrom")
|
||||
apply_date_to: Optional[date] = Field(default=None, alias="applyDateTo")
|
||||
ids: Optional[List[str]] = None
|
||||
columns: Optional[List[str]] = None
|
||||
|
||||
model_config = {"populate_by_name": True}
|
||||
|
||||
|
||||
@apply_router.post("/export")
|
||||
def export_applies(
|
||||
body: MaintenanceApplyExportBody,
|
||||
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())
|
||||
items, _ = svc.list_applies(
|
||||
db,
|
||||
tenant_no,
|
||||
user,
|
||||
q=body.q,
|
||||
maintenance_type=body.maintenance_type,
|
||||
status=body.status,
|
||||
apply_date_from=body.apply_date_from,
|
||||
apply_date_to=body.apply_date_to,
|
||||
ids=body.ids,
|
||||
page=1,
|
||||
page_size=EXPORT_MAX_ROWS,
|
||||
max_page_size=EXPORT_MAX_ROWS,
|
||||
)
|
||||
rows = []
|
||||
for item in items:
|
||||
data = item.model_dump(by_alias=True)
|
||||
for k in ("applyDate", "startDate", "expectedPickupDate", "createTime"):
|
||||
if data.get(k) is not None:
|
||||
data[k] = str(data[k])[:19]
|
||||
rows.append(data)
|
||||
cols = filter_export_columns(MAINT_APPLY_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=rows,
|
||||
filename_stem=f"维保申请_{date_str}",
|
||||
)
|
||||
except ExportValidationError as e:
|
||||
raise HTTPException(status_code=400, detail=str(e)) from e
|
||||
|
||||
|
||||
@apply_router.get("/{id}", response_model=VehicleMaintenanceApplyRead)
|
||||
def get_apply(
|
||||
id: str,
|
||||
|
||||
@@ -268,8 +268,10 @@ def list_applies(
|
||||
apply_date_from: Optional[date] = None,
|
||||
apply_date_to: Optional[date] = None,
|
||||
applicant_id: Optional[str] = None,
|
||||
ids: Optional[list[str]] = None,
|
||||
page: int = 1,
|
||||
page_size: int = 20,
|
||||
max_page_size: Optional[int] = None,
|
||||
) -> tuple[list[VehicleMaintenanceApplyRead], int]:
|
||||
query = db.query(PlVehicleMaintenanceApply).filter(
|
||||
PlVehicleMaintenanceApply.tenant_no == tenant_no,
|
||||
@@ -294,9 +296,14 @@ def list_applies(
|
||||
query = query.filter(PlVehicleMaintenanceApply.apply_date <= apply_date_to)
|
||||
if applicant_id:
|
||||
query = query.filter(PlVehicleMaintenanceApply.applicant_id == applicant_id)
|
||||
if ids:
|
||||
query = query.filter(PlVehicleMaintenanceApply.id.in_(ids))
|
||||
|
||||
query = apply_pl_scope(query, PlVehicleMaintenanceApply, user, db, tenant_no)
|
||||
total = query.count()
|
||||
page = max(1, page)
|
||||
cap = int(max_page_size) if max_page_size else 500
|
||||
page_size = min(max(1, page_size), cap)
|
||||
rows = (
|
||||
query.order_by(PlVehicleMaintenanceApply.create_time.desc())
|
||||
.offset((page - 1) * page_size)
|
||||
|
||||
@@ -1530,6 +1530,8 @@ export interface VehicleMaintenanceProcessBoardListResponse {
|
||||
|
||||
export const vehicleMaintenanceApplyApi = {
|
||||
list: (params?: Record<string, unknown>) =>
|
||||
export: (data: Record<string, any>) =>
|
||||
request.post('/transport/vehicle-maintenance-apply/export', data, { responseType: 'blob' }),
|
||||
request.get<VehicleMaintenanceApplyListResponse>('/transport/vehicle-maintenance-apply/', {
|
||||
params
|
||||
}),
|
||||
|
||||
@@ -86,6 +86,15 @@
|
||||
<el-icon><CircleClose /></el-icon>作废
|
||||
</el-button>
|
||||
|
||||
<ListExportDropdown
|
||||
:export-fn="vehicleMaintenanceApplyApi.export"
|
||||
:get-filter-params="getMaintApplyExportFilterParams"
|
||||
:has-selection="true"
|
||||
:selected-ids="selectedRows.map((r) => r.id)"
|
||||
:visible-columns="maintApplyExportColumns"
|
||||
filename="维保申请.xlsx"
|
||||
/>
|
||||
|
||||
<ListToolbarEndActions
|
||||
:loading="loading"
|
||||
:column-defs="listColumnDefs"
|
||||
@@ -370,6 +379,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 { useDbInstantSearch } from '@/composables/useDbInstantSearch'
|
||||
@@ -472,6 +482,23 @@ const submitLoading = ref(false)
|
||||
const cancelLoadingMap = reactive<Record<string, boolean>>({})
|
||||
const tableData = ref<VehicleMaintenanceApply[]>([])
|
||||
const selectedRows = ref<VehicleMaintenanceApply[]>([])
|
||||
|
||||
function getMaintApplyExportFilterParams() {
|
||||
return {
|
||||
q: quickSearchKeyword.value.trim() || searchForm.q || undefined,
|
||||
maintenanceType: searchForm.maintenanceType || undefined,
|
||||
status: searchForm.status || undefined,
|
||||
applyDateFrom: dateRange.value?.[0],
|
||||
applyDateTo: dateRange.value?.[1]
|
||||
}
|
||||
}
|
||||
|
||||
const maintApplyExportColumns = computed(() => {
|
||||
const cols = [...visibleColumns.value]
|
||||
if (!cols.includes('applyNo')) cols.unshift('applyNo')
|
||||
return cols
|
||||
})
|
||||
|
||||
const vehicleOptions = ref<Vehicle[]>([])
|
||||
const vehicleTrailerLookup = computed(() => buildVehicleTrailerLookup(vehicleOptions.value))
|
||||
const dateRange = ref<string[]>([])
|
||||
|
||||
Reference in new issue
Block a user