This commit is contained in:
zlwu committed 2026-08-02 17:18:33 +08:00
1 parent c00f19f8bd
commit 272c68af4b
134 files changed
+930389 -38617

No files matched your search

+46 -3
View File
@@ -189,9 +189,49 @@
min-width="180"
show-overflow-tooltip
/>
<el-table-column v-if="isColumnVisible('products')" label="产品信息" min-width="240">
<el-table-column
v-if="isColumnVisible('productName')"
label="产品名称"
min-width="200"
align="left"
header-align="left"
>
<template #default="{ row }">
<PlPlanListProductCell :products="row.products" />
<PlPlanListProductCell :products="row.products" field="name" />
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('productSpec')"
label="型号/规格"
min-width="120"
align="center"
header-align="center"
show-overflow-tooltip
>
<template #default="{ row }">
<PlPlanListProductCell :products="row.products" field="spec" />
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('productQty')"
label="数量"
min-width="90"
align="right"
header-align="right"
>
<template #default="{ row }">
<PlPlanListProductCell :products="row.products" field="qty" />
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('productUnit')"
label="单位"
min-width="80"
align="center"
header-align="center"
>
<template #default="{ row }">
<PlPlanListProductCell :products="row.products" field="unit" />
</template>
</el-table-column>
<el-table-column v-if="isColumnVisible('planAttachments')" label="计划附件" min-width="200">
@@ -328,7 +368,10 @@ const listColumnDefs = [
{ prop: 'followStatus', label: '跟进状态' },
{ prop: 'planType', label: '单据类型' },
{ prop: 'customerName', label: '客户名称' },
{ prop: 'products', label: '产品信息' },
{ prop: 'productName', label: '产品名称' },
{ prop: 'productSpec', label: '型号/规格' },
{ prop: 'productQty', label: '数量' },
{ prop: 'productUnit', label: '单位' },
{ prop: 'planAttachments', label: '计划附件' },
{ prop: 'licensePlate', label: '车牌号' },
{ prop: 'personnelSummary', label: '人员' },
+46 -3
View File
@@ -179,9 +179,49 @@
min-width="120"
align="center"
/>
<el-table-column v-if="isColumnVisible('products')" label="产品信息" min-width="240">
<el-table-column
v-if="isColumnVisible('productName')"
label="产品名称"
min-width="200"
align="left"
header-align="left"
>
<template #default="{ row }">
<PlPlanListProductCell :products="row.products" />
<PlPlanListProductCell :products="row.products" field="name" />
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('productSpec')"
label="型号/规格"
min-width="120"
align="center"
header-align="center"
show-overflow-tooltip
>
<template #default="{ row }">
<PlPlanListProductCell :products="row.products" field="spec" />
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('productQty')"
label="数量"
min-width="90"
align="right"
header-align="right"
>
<template #default="{ row }">
<PlPlanListProductCell :products="row.products" field="qty" />
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('productUnit')"
label="单位"
min-width="80"
align="center"
header-align="center"
>
<template #default="{ row }">
<PlPlanListProductCell :products="row.products" field="unit" />
</template>
</el-table-column>
<el-table-column v-if="isColumnVisible('planAttachments')" label="计划附件" min-width="280">
@@ -382,7 +422,10 @@ const planList = ref<Plan[]>([])
const listColumnDefs = [
{ prop: 'planDate', label: '计划日期' },
{ prop: 'products', label: '产品信息' },
{ prop: 'productName', label: '产品名称' },
{ prop: 'productSpec', label: '型号/规格' },
{ prop: 'productQty', label: '数量' },
{ prop: 'productUnit', label: '单位' },
{ prop: 'planAttachments', label: '计划附件' },
{ prop: 'customerName', label: '客户名称' },
{ prop: 'planType', label: '单据类型' },
+42 -5
View File
@@ -246,14 +246,48 @@
show-overflow-tooltip
/>
<el-table-column
v-if="isColumnVisible('products')"
label="产品信息"
min-width="240"
v-if="isColumnVisible('productName')"
label="产品名称"
min-width="200"
align="left"
header-align="left"
>
<template #default="{ row }">
<PlPlanListProductCell :products="row.products" />
<PlPlanListProductCell :products="row.products" field="name" />
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('productSpec')"
label="型号/规格"
min-width="120"
align="center"
header-align="center"
show-overflow-tooltip
>
<template #default="{ row }">
<PlPlanListProductCell :products="row.products" field="spec" />
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('productQty')"
label="数量"
min-width="90"
align="right"
header-align="right"
>
<template #default="{ row }">
<PlPlanListProductCell :products="row.products" field="qty" />
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('productUnit')"
label="单位"
min-width="80"
align="center"
header-align="center"
>
<template #default="{ row }">
<PlPlanListProductCell :products="row.products" field="unit" />
</template>
</el-table-column>
<el-table-column
@@ -490,7 +524,10 @@ const listColumnDefs = [
{ prop: 'planDate', label: '计划日期' },
{ prop: 'planType', label: '单据类型' },
{ prop: 'customerName', label: '客户名称' },
{ prop: 'products', label: '产品信息' },
{ prop: 'productName', label: '产品名称' },
{ prop: 'productSpec', label: '型号/规格' },
{ prop: 'productQty', label: '数量' },
{ prop: 'productUnit', label: '单位' },
{ prop: 'planAttachments', label: '计划附件' },
{ prop: 'licensePlate', label: '车牌号' },
{ prop: 'personnelSummary', label: '人员' },
+26 -13
View File
@@ -441,6 +441,7 @@ import {
type VehicleDetail,
type VehiclePersonnelRow,
type CertRow,
type CertRead,
type Personnel,
type Carrier
} from '@/api/pl'
@@ -454,7 +455,7 @@ import SystemViewDialog, {
} from '@/components/business/system-view-dialog/index.vue'
import { loadPersonnelWithCerts } from '@/composables/usePlVehicleViewDetail'
import { useDbInstantSearch } from '@/composables/useDbInstantSearch'
import { pickBxList, pickBxPageResult } from '@/utils/http/bx'
import { pickBxData, pickBxList, pickBxPageResult } from '@/utils/http/bx'
import { DEFAULT_LIST_PAGE_SIZE } from '@/utils/table/tableConfig'
import { formatDateTime } from '@/utils/format'
import {
@@ -488,6 +489,7 @@ type CertFormListRef = InstanceType<typeof PlVehicleCertPartTabs>
const loading = ref(false)
const quickSearchKeyword = ref('')
const submitLoading = ref(false)
const dialogLoading = ref(false)
const vehicleList = ref<Vehicle[]>([])
const carrierList = ref<Carrier[]>([])
@@ -554,7 +556,6 @@ const formData = reactive({
const formRules = {
licensePlate: [{ required: true, message: '请输入车牌号', trigger: 'blur' }],
trailerNo: [{ required: true, message: '请输入挂车号', trigger: 'blur' }],
vehicleStatus: [{ required: true, message: '请选择车辆状态', trigger: 'change' }],
carrierId: [{ required: true, message: '请选择承运商', trigger: 'change' }]
}
@@ -923,14 +924,28 @@ async function handleView(row: Vehicle) {
}
}
function mapCertRowsForForm(certs: CertRead[] | undefined | null) {
return (certs || []).map(c => ({
_key: c.id || c.certId || `cert-${Math.random().toString(36).slice(2, 10)}`,
certId: c.id || c.certId,
vehiclePart: c.vehiclePart,
certName: c.certName,
certNo: c.certNo,
expireDate: c.expireDate ? String(c.expireDate).slice(0, 10) : '',
remindDays: c.remindDays ?? 30,
attachmentUrls: c.attachmentUrls || ''
}))
}
async function handleEdit(row: Vehicle) {
dialogTitle.value = '编辑自有车辆'
isEdit.value = true
dialogLoading.value = true
dialogVisible.value = true
try {
await loadPersonnelOptions()
const res = await vehicleApi.get(row.id)
const detail = ((res.data ?? res) || {}) as VehicleDetail
const detail = pickBxData<VehicleDetail>(res) || ({} as VehicleDetail)
if (detail.vehicleType !== VEHICLE_TYPE_SELF) {
ElMessage.warning('仅可编辑自有车辆')
dialogVisible.value = false
@@ -953,23 +968,21 @@ async function handleEdit(row: Vehicle) {
age: p.age ?? null
}))
)
// 等 trailerNo 相关 watch 先消化,再回填证书,避免被 hasTrailer 逻辑误清空
await nextTick()
const sourceCerts =
detail.certList?.length ? detail.certList : row.certList?.length ? row.certList : []
certList.value = ensureSelfVehicleCertSlots(
(detail.certList || []).map(c => ({
_key: c.id || c.certId || `cert-${Math.random().toString(36).slice(2, 10)}`,
certId: c.id || c.certId,
vehiclePart: c.vehiclePart,
certName: c.certName,
certNo: c.certNo,
expireDate: c.expireDate,
remindDays: c.remindDays ?? 30,
attachmentUrls: c.attachmentUrls || ''
})),
mapCertRowsForForm(sourceCerts),
formData.trailerNo
)
await nextTick()
certFormListRef.value?.refreshRequiredSlots?.()
} catch {
dialogVisible.value = false
ElMessage.error('加载车辆详情失败')
} finally {
dialogLoading.value = false
}
}
+31 -15
View File
@@ -459,6 +459,7 @@ import {
type VehicleDetail,
type VehiclePersonnelRow,
type CertRow,
type CertRead,
type Carrier,
type Personnel
} from '@/api/pl'
@@ -472,7 +473,7 @@ import SystemViewDialog, {
type ViewSection
} from '@/components/business/system-view-dialog/index.vue'
import { useDbInstantSearch } from '@/composables/useDbInstantSearch'
import { pickBxList, pickBxPageResult } from '@/utils/http/bx'
import { pickBxData, pickBxList, pickBxPageResult } from '@/utils/http/bx'
import { DEFAULT_LIST_PAGE_SIZE } from '@/utils/table/tableConfig'
import { formatDateTime } from '@/utils/format'
import { formatPlVehiclePlateDisplay, normalizePlPersonnelSummaryDisplay } from '@/utils/pl-display'
@@ -498,6 +499,8 @@ const loading = ref(false)
const quickSearchKeyword = ref('')
const submitLoading = ref(false)
const dialogLoading = ref(false)
/** 编辑回填中:跳过 vehicleType watch,避免证书被 ensure 覆盖清空 */
const hydratingEdit = ref(false)
const vehicleList = ref<Vehicle[]>([])
const carrierList = ref<Carrier[]>([])
@@ -567,7 +570,6 @@ const formData = reactive({
const formRules = {
licensePlate: [{ required: true, message: '请输入车牌号', trigger: 'blur' }],
trailerNo: [{ required: true, message: '请输入挂车号', trigger: 'blur' }],
carrierId: [{ required: true, message: '请选择承运商', trigger: 'change' }]
}
@@ -974,15 +976,29 @@ async function handleView(row: Vehicle) {
}
}
function mapCertRowsForForm(certs: CertRead[] | undefined | null) {
return (certs || []).map(c => ({
_key: c.id || c.certId || `cert-${Math.random().toString(36).slice(2, 10)}`,
certId: c.id || c.certId,
vehiclePart: c.vehiclePart,
certName: c.certName,
certNo: c.certNo,
expireDate: c.expireDate ? String(c.expireDate).slice(0, 10) : '',
remindDays: c.remindDays ?? 30,
attachmentUrls: c.attachmentUrls || ''
}))
}
async function handleEdit(row: Vehicle) {
dialogTitle.value = '编辑车辆'
isEdit.value = true
dialogLoading.value = true
hydratingEdit.value = true
dialogVisible.value = true
try {
await loadPersonnelOptions()
const res = await vehicleApi.get(row.id)
const detail = ((res.data ?? res) || {}) as VehicleDetail
const detail = pickBxData<VehicleDetail>(res) || ({} as VehicleDetail)
Object.assign(formData, {
id: detail.id,
licensePlate: detail.licensePlate,
@@ -1001,19 +1017,19 @@ async function handleEdit(row: Vehicle) {
age: p.age ?? null
}))
)
const mappedCerts = (detail.certList || []).map(c => ({
_key: c.id || c.certId || `cert-${Math.random().toString(36).slice(2, 10)}`,
certId: c.id || c.certId,
vehiclePart: c.vehiclePart,
certName: c.certName,
certNo: c.certNo,
expireDate: c.expireDate,
remindDays: c.remindDays ?? 30,
attachmentUrls: c.attachmentUrls || ''
}))
certList.value = ensureVehicleCertSlots(mappedCerts, formData.trailerNo, VEHICLE_TYPE_LOCK)
// 等 trailerNo 相关 watch 先消化,再回填证书,避免被 hasTrailer 逻辑误清空
await nextTick()
const sourceCerts =
detail.certList?.length ? detail.certList : row.certList?.length ? row.certList : []
certList.value = ensureVehicleCertSlots(
mapCertRowsForForm(sourceCerts),
formData.trailerNo,
VEHICLE_TYPE_LOCK
)
await nextTick()
certFormListRef.value?.refreshRequiredSlots?.()
} finally {
hydratingEdit.value = false
dialogLoading.value = false
}
}
@@ -1105,7 +1121,7 @@ async function handleDelete(row: Vehicle) {
watch(
() => formData.vehicleType,
async type => {
if (!dialogVisible.value) return
if (!dialogVisible.value || hydratingEdit.value) return
await loadPersonnelOptions()
if (type === 'self' || type === 'carrier') {
certList.value = ensureVehicleCertSlots(