提交
This commit is contained in:
1 parent
c00f19f8bd
commit
272c68af4b
134 files changed
+930389
-38617
No files matched your search
@@ -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: '人员' },
|
||||
|
||||
@@ -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: '单据类型' },
|
||||
|
||||
@@ -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: '人员' },
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in new issue
Block a user