Files
art-design/src/views/crmpc/stocking/Add.vue
T
2025-11-19 09:31:16 +08:00

1477 lines
56 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="stocking-add-page">
<div class="header boxshadow">
<div class="title">合塑实业·汽车事业部-备货单(客户下定)</div>
<div class="subtitle">业务员:{{ getUserInfo.user.name }}</div>
</div>
<div class="boxshadow">
<div class="form-title">
<span class="border"></span>
<span class="title">基础信息</span>
</div>
<ElForm :model="baseForm" :rules="baseFormRules" ref="baseFormRef" class="base-form" label-position="right">
<ElRow :gutter="12">
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
<ElFormItem label="阿里订单号/PI号" label-width="124px" prop="pi_no" required>
<ElInput v-model="baseForm.pi_no" placeholder="请输入阿里订单号/PI号" />
</ElFormItem>
</ElCol>
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
<ElFormItem label="日期" label-width="114px" prop="biz_date" required>
<ElDatePicker
v-model="baseForm.biz_date"
type="date"
value-format="YYYY-MM-DD"
format="YYYY-MM-DD"
placeholder="请选择日期"
style="width: 100%"
/>
</ElFormItem>
</ElCol>
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
<ElFormItem label="清关目的国" label-width="114px" prop="clearance_country_id" required>
<el-select
v-model="baseForm.clearance_country_id"
filterable
placeholder="请选择国家"
@change="(val) => (baseForm.clearance_country_snapshot = (countryOptions || []).find((x: any) => x.id === val)?.cn_addrs || '')"
>
<el-option
v-for="item in countryOptions"
:key="item.id"
:label="item.cn_addrs"
:value="item.id"
/>
</el-select>
<ElInput v-model="baseForm.clearance_country_snapshot" type="hidden" />
</ElFormItem>
</ElCol>
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
<ElFormItem label="客户名称" label-width="114px" prop="customer_id" required>
<ElInput
v-model="baseForm.customer_snapshot"
placeholder="请选择客户"
readonly
@click="openCustomerDialog"
>
<template #suffix>
<i class="iconfont-sys" @click.stop="openCustomerDialog">&#xe867;</i>
</template>
</ElInput>
<ElInput v-model="baseForm.customer_id" type="hidden" />
</ElFormItem>
</ElCol>
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
<ElFormItem label="客户来源" label-width="124px">
<el-tooltip class="box-item" content="如需修改客户来源,请先编辑修改客户资料">
<el-select
v-model="baseForm.customer_source_id"
filterable
disabled
placeholder="请选择客户渠道"
@change="(val) => (baseForm.customer_source_snapshot = (channelOptions || []).find((x: any) => x.id === val)?.name || '')"
>
<el-option
v-for="item in channelOptions"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-tooltip>
<ElInput v-model="baseForm.customer_source_snapshot" type="hidden" />
</ElFormItem>
</ElCol>
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
<ElFormItem label="客户所在国" label-width="114px">
<el-tooltip class="box-item" content="如需修改客户所在国,请先编辑修改客户资料">
<el-select
v-model="baseForm.customer_country_id"
filterable
placeholder="请选择国家"
disabled
@change="(val) => (baseForm.customer_country_snapshot = (countryOptions || []).find((x: any) => x.id === val)?.cn_addrs || '')"
>
<el-option
v-for="item in countryOptions"
:key="item.id"
:label="item.cn_addrs"
:value="item.id"
/>
</el-select>
</el-tooltip>
<ElInput v-model="baseForm.customer_country_snapshot" type="hidden" />
</ElFormItem>
</ElCol>
</ElRow>
</ElForm>
</div>
<!-- 明细行 -->
<div class="boxshadow">
<div class="form-title">
<span class="border"></span>
<span class="title">订单详情</span>
</div>
<ElForm :model="formModel" :rules="itemRules" ref="itemsFormRef" class="items-wrap" label-position="right">
<div class="item-row form-box" v-for="(item, idx) in items" :key="idx">
<ElRow :gutter="12" class="item-row-wrap">
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
<ElFormItem label="车型" label-width="85px" :prop="`items.${idx}.sku_id`" required>
<ElInput
v-model="item.model_snapshot"
placeholder="请选择车型"
readonly
@click="openVehicleDialog(idx)"
>
<template #suffix>
<i class="iconfont-sys" @click.stop="openVehicleDialog(idx)">&#xe867;</i>
</template>
</ElInput>
<ElInput v-model="item.sku_id" type="hidden" />
</ElFormItem>
</ElCol>
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
<ElFormItem label="颜色/外饰" label-width="85px" :prop="`items.${idx}.color_exterior_snapshot`" required>
<el-select
v-model="item.color_exterior_snapshot"
filterable
placeholder="请选择内容"
@change="(val) => (item.color_exterior_code = (wcolorOptions || []).find((x) => x.color_text === val)?.color_code || '')"
>
<ElOption
v-for="item in wcolorOptions"
:key="item.color_code"
:label="item.color_text"
:value="item.color_text"
/>
</el-select>
<ElInput v-model="item.color_exterior_code" type="hidden" />
</ElFormItem>
</ElCol>
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
<ElFormItem label="颜色/内饰" label-width="85px" :prop="`items.${idx}.color_interior_snapshot`" required>
<el-select
v-model="item.color_interior_snapshot"
filterable
placeholder="请选择内容"
@change="
(val) =>
(item.color_interior_code =
(ncolorOptions || []).find((x) => x.color_text === val)?.color_code ||
'')
"
>
<ElOption
v-for="item in ncolorOptions"
:key="item.color_code"
:label="item.color_text"
:value="item.color_text"
/>
</el-select>
<ElInput v-model="item.color_interior_code" type="hidden" />
</ElFormItem>
</ElCol>
<!-- <ElCol :sm="12" :md="12" :lg="6" :xl="4">
<ElFormItem label="改装" label-width="85px">
<el-select
v-model="item.modify_ids"
filterable
multiple
placeholder="请选择内容"
@change="(vals) => (item.modify_list = (modificationOptions || []).filter((x: any) => (vals || []).includes(x.id)).map((x: any) => ({ option_id: x.id, option_name_snapshot: x.name })))"
>
<el-option
v-for="item in modificationOptions"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</ElFormItem>
</ElCol> -->
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
<ElFormItem label="里程" label-width="85px">
<ElInput v-model="item.mileage" placeholder="请输入里程" />
</ElFormItem>
</ElCol>
</ElRow>
<ElRow :gutter="12" class="item-row-wrap">
<ElCol :sm="12" :md="12" :lg="6" :xl="4" v-if="(item.mileage ?? 0) <= 0">
<ElFormItem label="官方指导价" label-width="95px" :prop="`items.${idx}.official_price`" required>
<ElInput v-model="item.official_price" placeholder="请输入官方指导价">
<template #suffix>
<ElSelect v-model="item.guide_price_currency" style="width: 74px; border: none">
<ElOption
v-for="c in currencyOptions"
:key="c.currency_code"
:label="c.currency_name"
:value="c.currency_code"
/>
</ElSelect>
</template>
</ElInput>
<ElInput v-model="item.guide_price_currency_snapshot" type="hidden" />
</ElFormItem>
</ElCol>
<ElCol :sm="12" :md="12" :lg="6" :xl="4" v-else>
<ElFormItem label="二手挂牌价" label-width="95px" :prop="`items.${idx}.listing_price`" required>
<ElInput v-model="item.listing_price" placeholder="请输入二手挂牌价">
<template #suffix>
<ElSelect v-model="item.listing_price_currency" style="width: 74px">
<ElOption
v-for="c in currencyOptions"
:key="c.currency_code"
:label="c.currency_name"
:value="c.currency_code"
/>
</ElSelect>
</template>
</ElInput>
<ElInput v-model="item.listing_price_currency_snapshot" type="hidden" />
</ElFormItem>
</ElCol>
<ElCol :sm="12" :md="12" :lg="6" :xl="4" v-if="(item.mileage ?? 0) <= 0">
<ElFormItem label="优惠" label-width="85px">
<ElInput v-model="item.discount_price" placeholder="请输入优惠额">
<template #suffix>
<ElSelect v-model="item.discount_currency" style="width: 74px">
<ElOption
v-for="c in currencyOptions"
:key="c.currency_code"
:label="c.currency_name"
:value="c.currency_code"
/>
</ElSelect>
</template>
</ElInput>
<ElInput v-model="item.discount_currency_snapshot" type="hidden" />
</ElFormItem>
</ElCol>
<ElCol :sm="12" :md="12" :lg="6" :xl="4" v-else>
<ElFormItem label="优惠" label-width="85px">
<ElInput v-model="item.reduction_price" placeholder="请输入优惠">
<template #suffix>
<ElSelect v-model="item.reduction_price_currency" style="width: 74px">
<ElOption
v-for="c in currencyOptions"
:key="c.currency_code"
:label="c.currency_name"
:value="c.currency_code"
/>
</ElSelect>
</template>
</ElInput>
<ElInput v-model="item.reduction_price_currency_snapshot" type="hidden" />
</ElFormItem>
</ElCol>
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
<ElFormItem label="加价" label-width="85px">
<ElInput v-model="item.increase_price" placeholder="请输入加价">
<template #suffix>
<ElSelect v-model="item.increase_price_currency" style="width: 74px">
<ElOption
v-for="c in currencyOptions"
:key="c.currency_code"
:label="c.currency_name"
:value="c.currency_code"
/>
</ElSelect>
</template>
</ElInput>
<ElInput v-model="item.increase_price_currency_snapshot" type="hidden" />
</ElFormItem>
</ElCol>
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
<ElFormItem label="收款价" label-width="85px" :prop="`items.${idx}.unit_price`" required>
<ElInput v-model="item.unit_price" placeholder="请输入收款价">
<template #suffix>
<ElSelect v-model="item.unit_price_currency" style="width: 74px">
<ElOption
v-for="c in currencyOptions"
:key="c.currency_code"
:label="c.currency_name"
:value="c.currency_code"
/>
</ElSelect>
</template>
</ElInput>
<ElInput v-model="item.unit_price_currency_snapshot" type="hidden" />
</ElFormItem>
</ElCol>
</ElRow>
<ElRow :gutter="12" class="item-row-wrap">
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
<ElFormItem label="手续费" label-width="85px">
<ElInput v-model="item.handling_fee" placeholder="请输入手续费">
<template #suffix>
<ElSelect v-model="item.helper_fee_currency" style="width: 74px">
<ElOption
v-for="c in currencyOptions"
:key="c.currency_code"
:label="c.currency_name"
:value="c.currency_code"
/>
</ElSelect>
</template>
</ElInput>
<ElInput v-model="item.helper_fee_currency_snapshot" type="hidden" />
</ElFormItem>
</ElCol>
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
<ElFormItem label="改装费" label-width="85px">
<ElInput v-model="item.retrofit_fee" placeholder="请输入改装费">
<template #suffix>
<ElSelect v-model="item.modify_fee_currency" style="width: 74px">
<ElOption
v-for="c in currencyOptions"
:key="c.currency_code"
:label="c.currency_name"
:value="c.currency_code"
/>
</ElSelect>
</template>
</ElInput>
<ElInput v-model="item.modify_fee_currency_snapshot" type="hidden" />
</ElFormItem>
</ElCol>
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
<ElFormItem label="配件费" label-width="85px">
<ElInput v-model="item.accessory_price" placeholder="请输入配件费">
<template #suffix>
<ElSelect v-model="item.accessory_price_currency" style="width: 74px">
<ElOption
v-for="c in currencyOptions"
:key="c.currency_code"
:label="c.currency_name"
:value="c.currency_code"
/>
</ElSelect>
</template>
</ElInput>
<ElInput v-model="item.accessory_price_currency_snapshot" type="hidden" />
</ElFormItem>
</ElCol>
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
<ElFormItem label="FOB段运费" label-width="85px">
<ElInput v-model="item.fob_transport_fee" placeholder="请输入FOB段运费">
<template #suffix>
<ElSelect v-model="item.fob_fee_currency" style="width: 74px">
<ElOption
v-for="c in currencyOptions"
:key="c.currency_code"
:label="c.currency_name"
:value="c.currency_code"
/>
</ElSelect>
</template>
</ElInput>
<ElInput v-model="item.fob_fee_currency_snapshot" type="hidden" />
</ElFormItem>
</ElCol>
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
<ElFormItem label="港杂费" label-width="85px">
<ElInput v-model="item.port_misc_fee" placeholder="请输入港杂费">
<template #suffix>
<ElSelect v-model="item.port_fee_currency" style="width: 74px">
<ElOption
v-for="c in currencyOptions"
:key="c.currency_code"
:label="c.currency_name"
:value="c.currency_code"
/>
</ElSelect>
</template>
</ElInput>
<ElInput v-model="item.port_fee_currency_snapshot" type="hidden" />
</ElFormItem>
</ElCol>
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
<ElFormItem label="海运费" label-width="85px">
<ElInput v-model="item.ocean_freight" placeholder="请输入海运费">
<template #suffix>
<ElSelect v-model="item.ocean_freight_currency" style="width: 74px">
<ElOption
v-for="c in currencyOptions"
:key="c.currency_code"
:label="c.currency_name"
:value="c.currency_code"
/>
</ElSelect>
</template>
</ElInput>
<ElInput v-model="item.ocean_freight_currency_snapshot" type="hidden" />
</ElFormItem>
</ElCol>
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
<ElFormItem label="佣金" label-width="85px">
<ElInput v-model="item.commission" placeholder="请输入佣金">
<template #suffix>
<ElSelect v-model="item.commission_currency" style="width: 74px">
<ElOption
v-for="c in currencyOptions"
:key="c.currency_code"
:label="c.currency_name"
:value="c.currency_code"
/>
</ElSelect>
</template>
</ElInput>
<ElInput v-model="item.commission_currency_snapshot" type="hidden" />
</ElFormItem>
</ElCol>
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
<ElFormItem label="挂账" label-width="85px">
<ElInput v-model="item.retention_price" placeholder="请输入挂账">
<template #suffix>
<ElSelect v-model="item.retention_price_currency" style="width: 74px">
<ElOption
v-for="c in currencyOptions"
:key="c.currency_code"
:label="c.currency_name"
:value="c.currency_code"
/>
</ElSelect>
</template>
</ElInput>
<ElInput v-model="item.retention_price_currency_snapshot" type="hidden" />
</ElFormItem>
</ElCol>
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
<ElFormItem label="购置税" label-width="85px">
<ElInput v-model="item.purchase_tax" placeholder="请输入购置税">
<template #suffix>
<ElSelect v-model="item.purchase_tax_currency" style="width: 74px">
<ElOption
v-for="c in currencyOptions"
:key="c.currency_code"
:label="c.currency_name"
:value="c.currency_code"
/>
</ElSelect>
</template>
</ElInput>
<ElInput v-model="item.purchase_tax_currency_snapshot" type="hidden" />
</ElFormItem>
</ElCol>
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
<ElFormItem label="预估退税额" label-width="85px">
<ElInput
v-model="item.tax_refund"
placeholder="正在计算预估退税额"
@input="handleUserEdit(idx)"
>
<template #suffix>
<span style="font-size: 11px">人民币</span>
</template>
</ElInput>
<ElInput v-model="item.tax_refund_currency_snapshot" type="hidden" />
</ElFormItem>
</ElCol>
</ElRow>
<ElRow :gutter="12" class="item-row-wrap">
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
<ElFormItem label="车价" label-width="85px">
<ElInput
v-model="item.car_price"
placeholder="正在计算车价"
@input="handleUserEdit(idx)"
>
<template #suffix>
<ElSelect v-model="item.car_price_currency" style="width: 74px">
<ElOption
v-for="c in currencyOptions"
:key="c.currency_code"
:label="c.currency_name"
:value="c.currency_code"
/>
</ElSelect>
</template>
</ElInput>
<ElInput v-model="item.car_price_currency_snapshot" type="hidden" />
</ElFormItem>
</ElCol>
<!-- <ElCol :sm="12" :md="12" :lg="6" :xl="4">
<ElFormItem label="总价" label-width="85px">
<ElInput v-model="item.total_price" placeholder="请输入总价">
<template #suffix>
<ElSelect v-model="item.total_price_currency" style="width: 74px">
<ElOption
v-for="c in currencyOptions"
:key="c.currency_code"
:label="c.currency_name"
:value="c.currency_code"
/>
</ElSelect>
</template>
</ElInput>
<ElInput v-model="item.total_price_currency_snapshot" type="hidden" />
</ElFormItem>
</ElCol> -->
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
<ElFormItem label="利润" label-width="85px">
<ElInput
v-model="item.profit"
placeholder="正在计算利润"
:class="Number(item.profit) < 0 ? 'profit-negative' : ''"
disabled
>
<template #suffix>
<span style="font-size: 11px">人民币</span>
</template>
</ElInput>
</ElFormItem>
</ElCol>
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
<ElFormItem label="报关价" label-width="85px">
<ElInput
v-model="item.clearance_price"
placeholder="正在计算报关价"
@input="handleUserEdit(idx)"
>
<template #suffix>
<ElSelect v-model="item.clearance_price_currency" style="width: 74px">
<ElOption
v-for="c in currencyOptions"
:key="c.currency_code"
:label="c.currency_name"
:value="c.currency_code"
/>
</ElSelect>
</template>
</ElInput>
<ElInput v-model="item.clearance_price_currency_snapshot" type="hidden" />
</ElFormItem>
</ElCol>
<ElCol :sm="12" :md="12" :lg="6" :xl="4" v-if="(item.mileage ?? 0) > 0">
<ElFormItem label="车架号" label-width="85px">
<ElInput v-model="item.vin" placeholder="请输入车架号"></ElInput>
</ElFormItem>
</ElCol>
</ElRow>
<ElRow :gutter="12" class="item-row-wrap">
<ElCol :sm="24" :md="24" :lg="24" :xl="24">
<ElFormItem label="备注" label-width="85px">
<ElInput type="textarea" :rows="2" v-model="item.remark" placeholder="请输入备注" />
</ElFormItem>
</ElCol>
</ElRow>
<ElRow :gutter="12">
<ElCol :span="24" style="text-align: right">
<el-tag effect="light" type="success" @click="copyRow(idx)" class="row-actions">
复制
</el-tag>
<el-tag
effect="light"
@click="addRowAfter(idx)"
v-if="idx == items.length - 1"
class="row-actions"
>
新增
</el-tag>
<el-tag
type="danger"
effect="light"
class="row-actions"
@click="removeRow(idx)"
v-if="items.length > 1"
>
删除
</el-tag>
</ElCol>
</ElRow>
</div>
<ElRow :gutter="12">
<ElCol :sm="12" :md="12" :lg="6" :xl="6">
<ElFormItem label="合计到岸价" label-width="105px">
<ElInput v-model="baseForm.cif_total" placeholder="请输入合计到岸价">
<template #suffix>
<ElSelect v-model="baseForm.cif_total_currency" style="width: 74px">
<ElOption
v-for="c in currencyOptions"
:key="c.currency_code"
:label="c.currency_name"
:value="c.currency_code"
/>
</ElSelect>
</template>
</ElInput>
<ElInput v-model="baseForm.cif_total_currency_snapshot" type="hidden" />
</ElFormItem>
</ElCol>
<ElCol :sm="12" :md="12" :lg="6" :xl="6">
<ElFormItem label="合同汇率" label-width="105px">
<ElInput v-model="baseForm.contract_exchange_rate" placeholder="请输入">
<template #suffix>
<ElSelect v-model="baseForm.settlement_currency" style="width: 74px">
<ElOption
@change="(val) => (baseForm.contract_exchange_rate = (currencyOptions || []).find((x: any) => x.currency_code === val)?.exchange_rate || '')"
v-for="c in currencyOptions"
:key="c.currency_code"
:label="c.currency_name"
:value="c.currency_code"
/>
</ElSelect>
</template>
</ElInput>
</ElFormItem>
</ElCol>
</ElRow>
</ElForm>
</div>
<div class="boxshadow">
<div class="form-title">
<span class="border"></span>
<span class="title">运输详情</span>
</div>
<ElForm :model="baseForm" :rules="transportFormRules" ref="transportFormRef" class="foot-form" label-position="right">
<ElRow :gutter="12">
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
<ElFormItem label="清关人" label-width="98px" prop="declarant_name" required>
<ElInput v-model="baseForm.declarant_name" placeholder="请输入清关人" />
</ElFormItem>
</ElCol>
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
<ElFormItem label="预计发送日" label-width="98px" prop="eta_ship_date" required>
<ElDatePicker
v-model="baseForm.eta_ship_date"
value-format="YYYY-MM-DD"
format="YYYY-MM-DD"
type="date"
placeholder="请选择日期"
style="width: 100%"
/>
</ElFormItem>
</ElCol>
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
<ElFormItem label="国内收货港" label-width="98px" prop="domestic_port_id" required>
<ElSelect
v-model="baseForm.domestic_port_id"
placeholder="请选择国内收货商"
filterable
@change="
(val) =>
(baseForm.domestic_port_snapshot =
(portOptions || []).find((x: any) => x.id === val)?.port_name || '')
"
>
<ElOption
v-for="p in portOptions"
:key="p.id"
:label="p.port_names"
:value="p.id"
/>
</ElSelect>
<ElInput v-model="baseForm.domestic_port_snapshot" type="hidden" />
</ElFormItem>
</ElCol>
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
<ElFormItem label="运输方式" label-width="98px" prop="shipping_method_id" required>
<ElSelect
v-model="baseForm.shipping_method_id"
placeholder="请选择运输方式"
@change="
(val) =>
(baseForm.shipping_method_snapshot =
(transportOptions || []).find((x) => x.item_value === val)?.item_label ||
'')
"
>
<ElOption
v-for="t in transportOptions"
:key="t.item_value"
:label="t.item_label"
:value="t.item_value"
/>
</ElSelect>
<ElInput v-model="baseForm.shipping_method_snapshot" type="hidden" />
</ElFormItem>
</ElCol>
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
<ElFormItem label="贸易条款" label-width="98px" prop="trade_term_id" required>
<ElSelect
v-model="baseForm.trade_term_id"
placeholder="请选择"
filterable
@change="
(val) =>
(baseForm.trade_term_snapshot =
(trade_termsOptions || []).find((x) => x.id === val)?.item_label || '')
"
>
<ElOption
v-for="t in trade_termsOptions"
:key="t.id"
:label="t.item_label"
:value="t.id"
/>
</ElSelect>
<ElInput v-model="baseForm.trade_term_snapshot" type="hidden" />
</ElFormItem>
</ElCol>
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
<ElFormItem label="清关目的港" label-width="98px" prop="clearance_port_id" required>
<ElSelect
v-model="baseForm.clearance_port_id"
filterable
placeholder="请选择"
@change="
(val) =>
(baseForm.clearance_port_snapshot =
(portOptionsall || []).find((x: any) => x.id === val)?.port_name || '')
"
>
<ElOption
v-for="p in portOptionsall"
:key="p.id"
:label="p.port_names"
:value="p.id"
/>
</ElSelect>
<ElInput v-model="baseForm.clearance_port_snapshot" type="hidden" />
</ElFormItem>
</ElCol>
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
<ElFormItem label="最迟到港时间" label-width="108px" prop="latest_arrival_date" required>
<ElDatePicker
v-model="baseForm.latest_arrival_date"
type="date"
value-format="YYYY-MM-DD"
format="YYYY-MM-DD"
placeholder="请选择日期"
style="width: 100%"
/>
</ElFormItem>
</ElCol>
</ElRow>
</ElForm>
</div>
<div class="boxshadow">
<div class="form-title">
<span class="border"></span>
<span class="title">计划收款信息</span>
</div>
<div class="payment-section">
<div class="payment-items">
<div v-for="(payment, idx) in paymentItems" :key="idx" class="payment-item">
<ElForm :model="payment" :rules="paymentFormRules" :ref="(el) => setPaymentFormRef(el, idx)" label-width="65px" class="payment-form" label-position="right">
<ElRow :gutter="12">
<ElCol :sm="12" :md="12" :lg="6" :xl="6">
<ElFormItem label="汇款人" label-width="65px" prop="remitter_name" required>
<ElInput v-model="payment.remitter_name" placeholder="请选择汇款人" />
</ElFormItem>
</ElCol>
<ElCol :sm="12" :md="12" :lg="4" :xl="4">
<ElFormItem label="汇款国" label-width="65px" prop="remit_country_id" required>
<el-select
v-model="payment.remit_country_id"
filterable
placeholder="请选择汇款国"
@change="
(val) =>
(payment.remit_country_name_snapshot =
(countryOptions || []).find((x: any) => x.id === val)?.cn_addrs || '')
"
>
<el-option
v-for="item in countryOptions"
:key="item.id"
:label="item.cn_addrs"
:value="item.id"
/>
</el-select>
<ElInput v-model="payment.remit_country_name_snapshot" type="hidden" />
</ElFormItem>
</ElCol>
<ElCol :sm="12" :md="12" :lg="4" :xl="4">
<ElFormItem label="日期" label-width="65px" prop="remit_date" required>
<ElDatePicker
v-model="payment.remit_date"
type="date"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
placeholder="请选择日期"
style="width: 100%"
/>
</ElFormItem>
</ElCol>
<ElCol :sm="12" :md="12" :lg="5" :xl="5">
<ElFormItem label="账户" label-width="65px" prop="account_id" required>
<ElSelect
v-model="payment.account_id"
filterable
placeholder="请选择账户"
@change="
(val) =>
(payment.account_name_snapshot =
(accountOptions || []).find((x: any) => x.id === val)?.short_desc || '')
"
>
<ElOption
v-for="item in accountOptions"
:key="item.id"
:label="item.short_desc"
:value="item.id"
/>
</ElSelect>
<ElInput v-model="payment.account_name_snapshot" type="hidden" />
</ElFormItem>
</ElCol>
<ElCol :sm="12" :md="12" :lg="5" :xl="5">
<ElFormItem label="金额" label-width="65px" prop="amount" required>
<ElInput v-model="payment.amount" placeholder="请输入金额">
<template #suffix>
<ElSelect
v-model="payment.currency_code"
style="width: 74px"
@change="
(val) => {
const found = (currencyOptions || []).find(
(x: any) => x.currency_code === val
)
payment.currency_name_snapshot = found?.currency_name || ''
}
"
>
<ElOption
v-for="c in currencyOptions"
:key="c.currency_code"
:label="c.currency_name"
:value="c.currency_code"
/>
</ElSelect>
</template>
</ElInput>
<ElInput v-model="payment.currency_name_snapshot" type="hidden" />
</ElFormItem>
</ElCol>
</ElRow>
<ElRow :gutter="12">
<ElCol :span="24" style="text-align: right">
<ElButton type="success" size="small" @click="copyPaymentItem(idx)">复制</ElButton>
<ElButton
type="primary"
size="small"
@click="addPaymentItem"
v-if="idx === paymentItems.length - 1"
>添加</ElButton
>
<ElButton
type="danger"
size="small"
@click="removePaymentItem(idx)"
:disabled="paymentItems.length === 1"
>删除</ElButton
>
</ElCol>
</ElRow>
</ElForm>
</div>
</div>
</div>
</div>
<!-- 上传合同 -->
<div class="boxshadow">
<div class="form-title">
<span class="border"></span>
<span class="title">上传合同</span>
</div>
<MultiFileUpload
v-model="contractFiles"
upload-text="点击上传"
hint-text="支持多文件上传"
:icon-src="contractIcon"
accept=".jpg,.jpeg,.png,.gif,.pdf,.doc,.docx,.xls,.xlsx,.txt"
@change="
(files) =>
(baseForm.contract = files.map((f) => ({
url: f.file_name,
original_name: f.name
})))
"
/>
</div>
<!-- 上传PI -->
<div class="boxshadow">
<div class="form-title">
<span class="border"></span>
<span class="title">上传PI</span>
</div>
<MultiFileUpload
v-model="piFiles"
upload-text="点击上传"
hint-text="支持多文件上传"
:icon-src="contractIcon"
accept=".jpg,.jpeg,.png,.gif,.pdf,.doc,.docx,.xls,.xlsx,.txt"
@change="
(files) =>
(baseForm.pi_doc = files.map((f) => ({
url: f.file_name,
original_name: f.name
})))
"
/>
</div>
<!-- 上传水单 -->
<div class="boxshadow">
<div class="form-title">
<span class="border"></span>
<span class="title">上传水单</span>
</div>
<MultiFileUpload
v-model="waterBillFiles"
upload-text="点击上传"
hint-text="支持多文件上传"
:icon-src="contractIcon"
accept=".jpg,.jpeg,.png,.gif,.pdf,.doc,.docx,.xls,.xlsx,.txt"
@change="
(files) =>
(baseForm.transaction_slip = files.map((f) => ({
url: f.file_name,
original_name: f.name
})))
"
/>
</div>
<div class="form-card">
<!-- 底部信息 -->
<div class="submit-wrap">
<ElButton
type="primary"
plain
size="large"
class="submit-btn"
@click="handleSubmitWithValidation(0)"
>保存</ElButton
>
<ElButton
type="primary"
size="large"
class="submit-btn"
@click="handleSubmitWithValidation(1)"
>提交</ElButton
>
</div>
</div>
<!-- 选择车型 弹框 -->
<ElDialog
v-model="vehicleDialog.visible"
title="选择车辆"
width="900px"
:close-on-click-modal="false"
top="40px"
>
<div
class="search-toolbar"
style="margin-bottom: 12px; display: flex; justify-content: flex-end; gap: 8px"
>
<el-input
v-model="vehicleDialog.search.keyword"
placeholder="请输入关键字"
clearable
style="width: 300px"
@keyup.enter="getVehicleList"
>
<template #suffix>
<el-icon class="clickable" @click="getVehicleList"><Search /></el-icon>
</template>
</el-input>
</div>
<ArtTable
:loading="vehicleDialog.loading"
:data="vehicleDialog.tableData"
:currentPage="vehicleDialog.pagination.currentPage"
:pageSize="vehicleDialog.pagination.pageSize"
:total="vehicleDialog.pagination.total"
@size-change="handleVehicleSizeChange"
@current-change="handleVehicleCurrentChange"
>
<template #default>
<ElTableColumn label="品牌" prop="brand_name" align="center" />
<ElTableColumn label="车系" prop="series_name" align="center" />
<ElTableColumn label="车型/配置" prop="model_name" min-width="160">
<template #default="scope">
<span class="primary">{{ scope.row.model_name }}</span>
</template>
</ElTableColumn>
<ElTableColumn label="年款" prop="model_year" width="80" align="center" />
<ElTableColumn label="指导价" prop="msrp" width="100" align="center" />
<ElTableColumn label="操作" width="100" align="center" fixed="right">
<template #default="scope">
<ElButton link type="primary" @click="chooseVehicle(scope.row)">选择</ElButton>
</template>
</ElTableColumn>
</template>
</ArtTable>
</ElDialog>
<!-- 选择客户 弹框 -->
<ElDialog
v-model="customerDialog.visible"
title="选择客户"
width="900px"
:close-on-click-modal="false"
top="40px"
>
<div
class="search-toolbar"
style="margin-bottom: 12px; display: flex; justify-content: flex-end; gap: 8px"
>
<el-input
v-model="customerDialog.search.keyword"
placeholder="请输入客户名称/电话"
clearable
style="width: 300px"
@keyup.enter="getCustomerList"
>
<template #suffix>
<el-icon class="clickable" @click="getCustomerList"><Search /></el-icon>
</template>
</el-input>
</div>
<ArtTable
:loading="customerDialog.loading"
:data="customerDialog.tableData"
:currentPage="customerDialog.pagination.currentPage"
:pageSize="customerDialog.pagination.pageSize"
:total="customerDialog.pagination.total"
@size-change="handleCustomerSizeChange"
@current-change="handleCustomerCurrentChange"
>
<template #default>
<ElTableColumn label="客户名称" prop="contacts" />
<ElTableColumn label="客户编码" prop="code" />
<ElTableColumn label="电话" prop="phone" />
<ElTableColumn label="公司" prop="company">
<template #default="scope">
<div>{{
scope.row.company == '' || scope.row.company == '0' ? ' --' : scope.row.company
}}</div>
</template>
</ElTableColumn>
<ElTableColumn label="操作" width="80" align="center" fixed="right">
<template #default="scope">
<ElButton link type="primary" @click="chooseCustomer(scope.row)">选择</ElButton>
</template>
</ElTableColumn>
</template>
</ArtTable>
</ElDialog>
</div>
</template>
<script setup lang="ts">
import { Search } from '@element-plus/icons-vue'
import { useUserStore } from '@/store/modules/user'
import { CrmService } from '@/api/crmApi'
import { useStockingForm } from '@/composables/useStockingForm'
import { onMounted, ref, computed, toRaw, watch, onBeforeUnmount } from 'vue'
import MultiFileUpload from '@/components/core/forms/MultiFileUpload.vue'
import contractIcon from '@/assets/img/crm/contract.png'
// 本地草稿缓存(用于切换页面回显,关闭页面时清空)
const STORAGE_VERSION = 'v1'
const userStoreForDraft = useUserStore()
const storageUserId = userStoreForDraft?.info?.userId || 'anonymous'
const draftKeyBase = `stocking:add:draft:${STORAGE_VERSION}:${storageUserId}`
const routeForDraft = useRoute()
const draftKey = computed(() => {
// 若存在复制/编辑 id,区分不同草稿;否则使用路径
const id = routeForDraft.query?.id
return id ? `${draftKeyBase}:${id}` : `${draftKeyBase}:${routeForDraft.fullPath}`
})
defineOptions({ name: 'CrmStockingAdd' }) // 定义组件名称,用于 KeepAlive 缓存控制
const { getUserInfo } = useUserStore()
const route = useRoute()
const stocking_id = route.query.id
// 表单引用
const baseFormRef = ref()
const itemsFormRef = ref()
const transportFormRef = ref()
const paymentFormRefs = ref<any[]>([])
// 表单模型
const formModel = computed(() => ({ items: items.value }))
// 设置收款表单引用
const setPaymentFormRef = (el: any, idx: number) => {
if (el) {
paymentFormRefs.value[idx] = el
}
}
// 基础表单校验规则
const baseFormRules = {
pi_no: [{ required: true, message: '请输入阿里订单号/PI号', trigger: 'blur' }],
biz_date: [{ required: true, message: '请选择日期', trigger: 'change' }],
clearance_country_id: [{ required: true, message: '请选择清关目的国', trigger: 'change' }],
customer_id: [{ required: true, message: '请选择客户', trigger: 'change' }]
}
// 订单项校验规则
const itemRules = computed(() => {
const rules: any = {}
items.value.forEach((_, idx) => {
rules[`items.${idx}.sku_id`] = [{ required: true, message: '请选择车型', trigger: 'change' }]
rules[`items.${idx}.color_exterior_snapshot`] = [
{ required: true, message: '请选择颜色/外饰', trigger: 'change' }
]
rules[`items.${idx}.color_interior_snapshot`] = [
{ required: true, message: '请选择颜色/内饰', trigger: 'change' }
]
rules[`items.${idx}.official_price`] = [
{ required: true, message: '请输入官方指导价', trigger: 'blur' }
]
rules[`items.${idx}.listing_price`] = [
{ required: true, message: '请输入二手挂牌价', trigger: 'blur' }
]
rules[`items.${idx}.unit_price`] = [
{ required: true, message: '请输入收款价', trigger: 'blur' }
]
})
return rules
})
// 运输详情校验规则
const transportFormRules = {
declarant_name: [{ required: true, message: '请输入清关人', trigger: 'blur' }],
eta_ship_date: [{ required: true, message: '请选择预计发送日', trigger: 'change' }],
domestic_port_id: [{ required: true, message: '请选择国内收货港', trigger: 'change' }],
shipping_method_id: [{ required: true, message: '请选择运输方式', trigger: 'change' }],
trade_term_id: [{ required: true, message: '请选择贸易条款', trigger: 'change' }],
clearance_port_id: [{ required: true, message: '请选择清关目的港', trigger: 'change' }],
latest_arrival_date: [{ required: true, message: '请选择最迟到港时间', trigger: 'change' }]
}
// 收款信息校验规则
const paymentFormRules = {
remitter_name: [{ required: true, message: '请输入汇款人', trigger: 'blur' }],
remit_country_id: [{ required: true, message: '请选择汇款国', trigger: 'change' }],
remit_date: [{ required: true, message: '请选择汇款日期', trigger: 'change' }],
account_id: [{ required: true, message: '请选择账户', trigger: 'change' }],
amount: [{ required: true, message: '请输入金额', trigger: 'blur' }]
}
// 使用公共的备货单表单逻辑
const {
// 数据
baseForm,
items,
paymentItems,
contractFiles,
piFiles,
waterBillFiles,
customerDialog,
vehicleDialog,
// 选项数据
channelOptions,
countryOptions,
modificationOptions,
currencyOptions,
accountOptions,
customerOptions,
portOptions,
portOptionsall,
wcolorOptions,
ncolorOptions,
transportOptions,
trade_termsOptions,
// 上传相关
// 方法
initializeForm,
populateFromDetail,
handleSubmit,
getCurrencyRate,
validateContractRate,
wrapMoney,
calcProfit,
handleUserEdit,
// 订单项方法
copyRow,
addRowAfter,
removeRow,
// 收款项方法
addPaymentItem,
removePaymentItem,
copyPaymentItem,
// 客户选择方法
openCustomerDialog,
getCustomerList,
handleCustomerSizeChange,
handleCustomerCurrentChange,
chooseCustomer,
// 车辆选择方法
openVehicleDialog,
getVehicleList,
handleVehicleSizeChange,
handleVehicleCurrentChange,
chooseVehicle
} = useStockingForm(false) // isFinal = false
// ---------- 草稿缓存:保存/恢复/清理 ----------
const DRAFT_DEBOUNCE_MS = 500
let draftTimer: number | any = null
const pickDraftPayload = () => ({
baseForm: toRaw(baseForm),
items: toRaw(items.value),
paymentItems: toRaw(paymentItems.value),
contractFiles: toRaw(contractFiles.value),
piFiles: toRaw(piFiles.value),
waterBillFiles: toRaw(waterBillFiles.value)
})
const saveDraft = () => {
try {
const payload = pickDraftPayload()
localStorage.setItem(draftKey.value, JSON.stringify(payload))
} catch {}
}
const scheduleSaveDraft = () => {
if (draftTimer) clearTimeout(draftTimer)
draftTimer = setTimeout(() => {
saveDraft()
}, DRAFT_DEBOUNCE_MS)
}
const loadDraft = () => {
try {
const raw = localStorage.getItem(draftKey.value)
if (!raw) return
const parsed = JSON.parse(raw)
// 合并到现有表单数据
if (parsed?.baseForm && typeof parsed.baseForm === 'object') {
Object.assign(baseForm, parsed.baseForm)
}
if (Array.isArray(parsed?.items)) {
items.value = parsed.items
}
if (Array.isArray(parsed?.paymentItems)) {
paymentItems.value = parsed.paymentItems
}
if (Array.isArray(parsed?.contractFiles)) {
contractFiles.value = parsed.contractFiles
baseForm.contract = (contractFiles.value || []).map((f: any) => f.file_name)
}
if (Array.isArray(parsed?.piFiles)) {
piFiles.value = parsed.piFiles
baseForm.pi_doc = (piFiles.value || []).map((f: any) => f.file_name)
}
if (Array.isArray(parsed?.waterBillFiles)) {
waterBillFiles.value = parsed.waterBillFiles
baseForm.transaction_slip = (waterBillFiles.value || []).map((f: any) => f.file_name)
}
} catch {}
}
const clearDraft = () => {
try {
localStorage.removeItem(draftKey.value)
} catch {}
}
// 获取详情数据
const getdetilfn = async () => {
const res = await CrmService.getStockDetail({ id: stocking_id })
populateFromDetail(res)
}
// 自定义提交方法,添加表单校验
const handleSubmitWithValidation = async (isFinal: number) => {
try {
// 校验基础表单
await baseFormRef.value?.validate()
// 校验订单项表单
await itemsFormRef.value?.validate()
// 校验运输详情表单
await transportFormRef.value?.validate()
// 校验收款信息表单
for (let i = 0; i < paymentItems.value.length; i++) {
const paymentFormRef = paymentFormRefs.value[i]
if (paymentFormRef) {
await paymentFormRef.validate()
}
}
// 如果校验通过,调用原始的handleSubmit方法
await handleSubmit(isFinal)
// 提交成功后清理草稿
clearDraft()
} catch (error) {
console.log('表单校验失败:', error)
}
}
// 初始化表单
onMounted(async () => {
await initializeForm()
// 如果有ID,代表复制 加载详情数据
if (stocking_id) {
await getdetilfn()
}
// 尝试从草稿恢复
loadDraft()
// 监听表单变化,自动保存草稿
watch(
() => [baseForm, items.value, paymentItems.value],
() => scheduleSaveDraft(),
{ deep: true }
)
watch(contractFiles, scheduleSaveDraft, { deep: true })
watch(piFiles, scheduleSaveDraft, { deep: true })
watch(waterBillFiles, scheduleSaveDraft, { deep: true })
// 浏览器标签关闭时清理草稿
const beforeUnload = () => {
clearDraft()
}
window.addEventListener('beforeunload', beforeUnload)
onBeforeUnmount(() => {
window.removeEventListener('beforeunload', beforeUnload)
// 当页面真正被卸载(如关闭当前路由标签)时清理草稿
clearDraft()
})
})
</script>
<style scoped lang="scss">
:deep(.el-input-group--append .el-input-group__append .el-select .el-select__wrapper) {
padding: 4px;
}
:deep(.el-select__selected-item span) {
font-size: 11px;
}
.form-title {
font-weight: bold;
color: #222;
margin-bottom: 15px;
}
.form-title .title {
margin-left: 6px;
}
.stocking-add-page {
min-height: 100vh;
}
.header {
display: flex;
flex-direction: column;
align-items: center;
padding: 16px 20px;
margin-bottom: 12px;
.title {
font-size: 18px;
font-weight: 600;
margin-bottom: 15px;
}
.subtitle {
color: var(--el-text-color-secondary);
}
}
.items-wrap {
margin-top: 6px;
}
.item-row {
padding: 8px 0;
}
.row-actions {
margin-left: 8px;
cursor: pointer;
}
/* 利润为负数时,输入文字变红(兼容 Element Plus disabled 样式) */
:deep(.profit-negative .el-input__wrapper) {
color: #ff4d4f !important;
}
:deep(.profit-negative .el-input__inner) {
color: #ff4d4f !important;
-webkit-text-fill-color: #ff4d4f !important; /* 修复 WebKit 在 disabled 下的文本颜色 */
}
:deep(.profit-negative.is-disabled .el-input__wrapper) {
background-color: var(--el-fill-color-blank) !important;
box-shadow: 0 0 0 1px var(--el-input-border-color, #dcdfe6) inset !important;
}
.submit-wrap {
display: flex;
justify-content: space-around;
padding: 24px 0 8px;
width: 500px;
margin: auto;
}
.submit-btn {
width: 100px;
height: 44px;
font-size: 16px;
}
.payment-item {
margin-bottom: 12px;
}
.upload-box {
width: 200px;
}
.file-preview {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.preview-image {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 4px;
}
.file-icon {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
background: #f5f7fa;
border-radius: 4px;
border: 1px dashed #d9d9d9;
}
.file-icon i {
font-size: 32px;
color: #409eff;
margin-bottom: 8px;
}
.file-name {
font-size: 12px;
color: #606266;
text-align: center;
word-break: break-all;
padding: 0 8px;
}
:deep(.el-input__suffix .el-select .el-select__wrapper) {
box-shadow: none;
padding: 0px;
text-align: right;
}
.form-box {
border-bottom: 1px solid #f2f4f6;
margin-bottom: 20px;
padding: 8px 0px 20px;
}
/* 右对齐所有表单项的标签文字 */
// :deep(.el-form-item__label) {
// text-align: right;
// }
</style>