提交
This commit is contained in:
1 parent
c00f19f8bd
commit
272c68af4b
134 files changed
+930389
-38617
No files matched your search
@@ -3,7 +3,7 @@
|
||||
<el-dialog
|
||||
v-model="innerVisible"
|
||||
:title="dialogTitle"
|
||||
width="820px"
|
||||
width="1100px"
|
||||
align-center
|
||||
destroy-on-close
|
||||
append-to-body
|
||||
@@ -13,83 +13,84 @@
|
||||
style="backdrop-filter: none"
|
||||
@closed="handleClosed"
|
||||
>
|
||||
<el-form label-position="top" class="form-grid-2">
|
||||
<el-form-item label="客户原文" class="form-col-span-2">
|
||||
<el-form label-position="top">
|
||||
<el-form-item label="客户原文">
|
||||
<el-input
|
||||
v-model="rawText"
|
||||
type="textarea"
|
||||
:rows="8"
|
||||
placeholder="粘贴客户提供的车辆与驾押信息后点击识别"
|
||||
:rows="10"
|
||||
placeholder="请按示例格式填写或粘贴客户原文后点击识别"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item class="form-col-span-2">
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleParse">
|
||||
<el-icon><MagicStick /></el-icon>识别
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
|
||||
<template v-if="parsed">
|
||||
<el-form-item v-if="showVehicleFields" label="承运商" class="form-col-span-2" required>
|
||||
<el-select
|
||||
v-model="carrierId"
|
||||
placeholder="请选择承运商"
|
||||
filterable
|
||||
clearable
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="c in carrierOptions"
|
||||
:key="c.id"
|
||||
:label="c.carrierName"
|
||||
:value="c.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item
|
||||
v-if="mode === 'personnel-first' && hasPlate"
|
||||
label="建车选项"
|
||||
class="form-col-span-2"
|
||||
>
|
||||
<el-form-item v-if="mode === 'personnel-first' && hasPlate" label="建车选项">
|
||||
<el-checkbox v-model="createVehicle">一并创建三方车辆并关联</el-checkbox>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="showVehicleFields" label="主车号">
|
||||
<el-input v-model="vehicleForm.licensePlate" maxlength="20" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="showVehicleFields" label="挂车号">
|
||||
<el-input v-model="vehicleForm.trailerNo" maxlength="20" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="showVehicleFields" label="核载(吨)">
|
||||
<el-input-number
|
||||
v-model="vehicleForm.loadCapacity"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<div v-if="showVehicleFields" class="form-grid-4">
|
||||
<el-form-item label="承运商" required>
|
||||
<el-select
|
||||
v-model="carrierId"
|
||||
placeholder="请选择承运商"
|
||||
filterable
|
||||
clearable
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="c in carrierOptions"
|
||||
:key="c.id"
|
||||
:label="c.carrierName"
|
||||
:value="c.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="主车号">
|
||||
<el-input v-model="vehicleForm.licensePlate" maxlength="20" />
|
||||
</el-form-item>
|
||||
<el-form-item label="挂车号">
|
||||
<el-input v-model="vehicleForm.trailerNo" maxlength="20" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="核载(吨)">
|
||||
<el-input-number
|
||||
v-model="vehicleForm.loadCapacity"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<el-form-item label="驾驶员">
|
||||
<el-input v-model="driverForm.personnelName" maxlength="100" />
|
||||
</el-form-item>
|
||||
<el-form-item label="驾驶员手机">
|
||||
<el-input v-model="driverForm.phoneNo" maxlength="32" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="驾驶员身份证" class="form-col-span-2">
|
||||
<el-input v-model="driverForm.idCardNo" maxlength="32" clearable />
|
||||
</el-form-item>
|
||||
<div class="form-grid-3">
|
||||
<el-form-item label="驾驶员">
|
||||
<el-input v-model="driverForm.personnelName" maxlength="100" />
|
||||
</el-form-item>
|
||||
<el-form-item label="驾驶员手机">
|
||||
<el-input v-model="driverForm.phoneNo" maxlength="32" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="驾驶员身份证">
|
||||
<el-input v-model="driverForm.idCardNo" maxlength="32" clearable />
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<el-form-item label="押运员">
|
||||
<el-input v-model="escortForm.personnelName" maxlength="100" />
|
||||
</el-form-item>
|
||||
<el-form-item label="押运员电话">
|
||||
<el-input v-model="escortForm.phoneNo" maxlength="32" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="押运员身份证" class="form-col-span-2">
|
||||
<el-input v-model="escortForm.idCardNo" maxlength="32" clearable />
|
||||
</el-form-item>
|
||||
<div class="form-grid-3">
|
||||
<el-form-item label="押运员">
|
||||
<el-input v-model="escortForm.personnelName" maxlength="100" />
|
||||
</el-form-item>
|
||||
<el-form-item label="押运员电话">
|
||||
<el-input v-model="escortForm.phoneNo" maxlength="32" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="押运员身份证">
|
||||
<el-input v-model="escortForm.idCardNo" maxlength="32" clearable />
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<div v-if="conflictRows.length" class="form-col-span-2 paste-conflict-block">
|
||||
<div v-if="conflictRows.length" class="paste-conflict-block">
|
||||
<h4 class="paste-conflict-title">冲突处理</h4>
|
||||
<div v-for="row in conflictRows" :key="row.key" class="paste-conflict-row">
|
||||
<div class="paste-conflict-label">
|
||||
@@ -105,7 +106,6 @@
|
||||
|
||||
<el-alert
|
||||
v-if="parsed.warnings.length"
|
||||
class="form-col-span-2"
|
||||
type="warning"
|
||||
:closable="false"
|
||||
:title="`有 ${parsed.warnings.length} 行未识别`"
|
||||
@@ -140,6 +140,7 @@
|
||||
} from '@/api/pl'
|
||||
import { pickBxPageResult } from '@/utils/http/bx'
|
||||
import {
|
||||
PL_PASTE_SAMPLE_TEXT,
|
||||
parsePlCustomerVehicleText,
|
||||
type PlPasteRecognizeResult
|
||||
} from '@/utils/pl-paste-recognize'
|
||||
@@ -169,7 +170,7 @@
|
||||
props.mode === 'vehicle-bundle' ? '粘贴识别(整车套装)' : '粘贴识别(以人为主)'
|
||||
)
|
||||
|
||||
const rawText = ref('')
|
||||
const rawText = ref(PL_PASTE_SAMPLE_TEXT)
|
||||
const parsed = ref<PlPasteRecognizeResult | null>(null)
|
||||
const carrierId = ref('')
|
||||
const createVehicle = ref(true)
|
||||
@@ -376,7 +377,7 @@
|
||||
}
|
||||
|
||||
function handleClosed() {
|
||||
rawText.value = ''
|
||||
rawText.value = PL_PASTE_SAMPLE_TEXT
|
||||
parsed.value = null
|
||||
carrierId.value = ''
|
||||
createVehicle.value = true
|
||||
@@ -395,12 +396,20 @@
|
||||
watch(
|
||||
() => props.visible,
|
||||
async v => {
|
||||
if (v) await ensureCarriers()
|
||||
if (v) {
|
||||
rawText.value = PL_PASTE_SAMPLE_TEXT
|
||||
await ensureCarriers()
|
||||
}
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.paste-conflict-block,
|
||||
:deep(.el-alert) {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.paste-conflict-block {
|
||||
border: 1px solid var(--el-border-color-lighter);
|
||||
border-radius: 8px;
|
||||
|
||||
@@ -5,33 +5,72 @@
|
||||
:key="product.id || `${product.productNo}-${index}`"
|
||||
class="pl-plan-list-product-cell__row"
|
||||
>
|
||||
<span class="pl-plan-list-product-cell__line">
|
||||
<span class="pl-plan-list-product-cell__text">{{ formatProductLine(product) }}</span>
|
||||
<el-tag v-if="getHazardFlags(product).isToxic" type="warning" size="small" class="pl-plan-list-product-cell__mark">
|
||||
毒
|
||||
</el-tag>
|
||||
<el-tag
|
||||
v-if="getHazardFlags(product).isExplosive"
|
||||
type="danger"
|
||||
size="small"
|
||||
class="pl-plan-list-product-cell__mark"
|
||||
>
|
||||
爆
|
||||
</el-tag>
|
||||
<span
|
||||
v-if="showCertStatus && isHazardProduct(product)"
|
||||
class="pl-plan-list-product-cell__certs"
|
||||
>
|
||||
<span
|
||||
v-for="slot in getRequiredCertSlots(product)"
|
||||
:key="slot.key"
|
||||
class="pl-plan-list-product-cell__cert-text"
|
||||
<span
|
||||
class="pl-plan-list-product-cell__line"
|
||||
:class="{
|
||||
'pl-plan-list-product-cell__line--qty': field === 'qty',
|
||||
'pl-plan-list-product-cell__line--center': field === 'spec' || field === 'unit'
|
||||
}"
|
||||
>
|
||||
<template v-if="!field">
|
||||
<span class="pl-plan-list-product-cell__text">{{ formatProductLine(product) }}</span>
|
||||
<el-tag
|
||||
v-if="getHazardFlags(product).isToxic"
|
||||
type="warning"
|
||||
size="small"
|
||||
class="pl-plan-list-product-cell__mark"
|
||||
>
|
||||
{{ slot.label }}(<span
|
||||
:class="{ 'pl-plan-list-product-cell__cert-status--missing': !isProductCertSlotFilled(product, slot.key) }"
|
||||
>{{ isProductCertSlotFilled(product, slot.key) ? '有' : '无' }}</span>)
|
||||
毒
|
||||
</el-tag>
|
||||
<el-tag
|
||||
v-if="getHazardFlags(product).isExplosive"
|
||||
type="danger"
|
||||
size="small"
|
||||
class="pl-plan-list-product-cell__mark"
|
||||
>
|
||||
爆
|
||||
</el-tag>
|
||||
<span
|
||||
v-if="showCertStatus && isHazardProduct(product)"
|
||||
class="pl-plan-list-product-cell__certs"
|
||||
>
|
||||
<span
|
||||
v-for="slot in getRequiredCertSlots(product)"
|
||||
:key="slot.key"
|
||||
class="pl-plan-list-product-cell__cert-text"
|
||||
>
|
||||
{{ slot.label }}(<span
|
||||
:class="{
|
||||
'pl-plan-list-product-cell__cert-status--missing': !isProductCertSlotFilled(
|
||||
product,
|
||||
slot.key
|
||||
)
|
||||
}"
|
||||
>{{ isProductCertSlotFilled(product, slot.key) ? '有' : '无' }}</span
|
||||
>)
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
||||
<template v-else-if="field === 'name'">
|
||||
<span class="pl-plan-list-product-cell__text">{{ product.productName || '—' }}</span>
|
||||
<el-tag
|
||||
v-if="getHazardFlags(product).isToxic"
|
||||
type="warning"
|
||||
size="small"
|
||||
class="pl-plan-list-product-cell__mark"
|
||||
>
|
||||
毒
|
||||
</el-tag>
|
||||
<el-tag
|
||||
v-if="getHazardFlags(product).isExplosive"
|
||||
type="danger"
|
||||
size="small"
|
||||
class="pl-plan-list-product-cell__mark"
|
||||
>
|
||||
爆
|
||||
</el-tag>
|
||||
</template>
|
||||
<span v-else class="pl-plan-list-product-cell__text">{{ formatFieldValue(product) }}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -47,12 +86,20 @@ import {
|
||||
resolveProductHazardFlags
|
||||
} from '@/composables/usePlToxicProductCertCheck'
|
||||
import { useDictStore } from '@/store/modules/dict'
|
||||
import { formatPlanProductListText } from '@/utils/pl-display'
|
||||
import {
|
||||
formatPlanProductListText,
|
||||
getPlanProductQuantity,
|
||||
getPlanProductSpec
|
||||
} from '@/utils/pl-display'
|
||||
|
||||
withDefaults(
|
||||
export type PlPlanListProductCellField = 'name' | 'spec' | 'qty' | 'unit'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
products?: PlanProduct[]
|
||||
/** 危化品行下方展示证书有无(按产品类型) */
|
||||
/** 按字段拆列展示;未传时保持整行拼接 */
|
||||
field?: PlPlanListProductCellField
|
||||
/** 危化品行下方展示证书有无(按产品类型;仅整行模式) */
|
||||
showCertStatus?: boolean
|
||||
}>(),
|
||||
{
|
||||
@@ -71,6 +118,21 @@ function formatProductLine(product: PlanProduct) {
|
||||
return formatPlanProductListText(product, getUnitLabel)
|
||||
}
|
||||
|
||||
function formatFieldValue(product: PlanProduct) {
|
||||
if (props.field === 'spec') {
|
||||
return getPlanProductSpec(product)
|
||||
}
|
||||
if (props.field === 'qty') {
|
||||
return getPlanProductQuantity(product)
|
||||
}
|
||||
if (props.field === 'unit') {
|
||||
const unitRaw = product.unit || ''
|
||||
if (!unitRaw) return '—'
|
||||
return getUnitLabel(unitRaw) || unitRaw
|
||||
}
|
||||
return '—'
|
||||
}
|
||||
|
||||
function getHazardFlags(product: PlanProduct) {
|
||||
return resolveProductHazardFlags(product)
|
||||
}
|
||||
@@ -105,6 +167,18 @@ function getHazardFlags(product: PlanProduct) {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.pl-plan-list-product-cell__line--qty {
|
||||
justify-content: flex-end;
|
||||
text-align: right;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.pl-plan-list-product-cell__line--center {
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.pl-plan-list-product-cell__text {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
@@ -1224,7 +1224,17 @@
|
||||
mapContractProducts(contractSnapshot.value?.products)
|
||||
)
|
||||
|
||||
const hazardProducts = computed(() => products.value.filter((item) => isHazardProduct(item)))
|
||||
/** 危化证区域展示 CRM 标准名(productRows 已回填 standardProductName;库内 productName 可能是别名) */
|
||||
const hazardProducts = computed(() =>
|
||||
products.value
|
||||
.filter((item) => isHazardProduct(item))
|
||||
.map((item) => {
|
||||
const formRow = item.id ? planProductFormById.value.get(String(item.id)) : undefined
|
||||
const standardName = String(formRow?.standardProductName || '').trim()
|
||||
if (!standardName || standardName === item.productName) return item
|
||||
return { ...item, productName: standardName }
|
||||
})
|
||||
)
|
||||
|
||||
async function handleViewContract() {
|
||||
const contractId = contractSnapshot.value?.id || plan.value?.contractId
|
||||
|
||||
@@ -125,8 +125,10 @@ function syncCertList(headCerts: CertRowWithKey[], trailerCerts: CertRowWithKey[
|
||||
certList.value = mergeVehicleCertsByPart(headCerts, trailerCerts, hasTrailer.value)
|
||||
}
|
||||
|
||||
watch(hasTrailer, value => {
|
||||
if (!value) {
|
||||
watch(hasTrailer, (value, oldValue) => {
|
||||
// 仅在「有挂车 → 无挂车」时剥离挂车证书,避免编辑回填时
|
||||
// trailerNo 与 certList 同 tick 赋值触发误清空。
|
||||
if (!value && oldValue) {
|
||||
activePart.value = PL_VEHICLE_CERT_PART_HEAD
|
||||
certList.value = mergeVehicleCertsByPart(
|
||||
splitVehicleCertsByPart(certList.value).headCerts,
|
||||
@@ -137,6 +139,10 @@ watch(hasTrailer, value => {
|
||||
trailerCertFormRef.value?.clearUploadRefs()
|
||||
return
|
||||
}
|
||||
if (!value) {
|
||||
activePart.value = PL_VEHICLE_CERT_PART_HEAD
|
||||
return
|
||||
}
|
||||
if (activePart.value !== PL_VEHICLE_CERT_PART_TRAILER) {
|
||||
activePart.value = PL_VEHICLE_CERT_PART_HEAD
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user