Files
art-design/src/composables/useStockingForm.ts
T

1461 lines
50 KiB
TypeScript
Raw Normal View History

import { ref, reactive, watch, nextTick } from 'vue'
import { ElMessage } from 'element-plus'
2025-09-18 09:55:10 +08:00
import { CrmService } from '@/api/crmApi'
import { commonService } from '@/api/commonApi'
import { vehicleService } from '@/api/vehicleApi'
import { useUploadHeaders } from '@/composables/useUploadHeaders'
// 类型定义
export interface BaseForm {
pi_no: string
biz_date: string
clearance_country_id: string
clearance_country_snapshot: string
customer_source_id: string
customer_source_snapshot: string
customer_id: string
customer_snapshot: string
customer_country_id: string
customer_country_snapshot: string
declarant_name: string
eta_ship_date: string
shipping_method_id: string
shipping_method_snapshot: string
domestic_port_id: string
domestic_port_snapshot: string
trade_term_id: string
trade_term_snapshot: string
clearance_port_id: string
clearance_port_snapshot: string
latest_arrival_date: string
pick_up_date: string
collect_all_date: string
contract_exchange_rate: string
settlement_currency: string
remark: string
2025-10-22 11:52:20 +08:00
cif_total: number | undefined
cif_total_currency: string
cif_total_currency_snapshot: string
2025-09-18 09:55:10 +08:00
is_final: string
object_id: string | number
2025-10-22 11:52:20 +08:00
contract: string[]
contract_url?: string[]
pi_doc: string[]
pi_doc_url?: string[]
transaction_slip: string[]
transaction_slip_url?: string[]
2025-09-18 09:55:10 +08:00
}
export interface ItemRow {
sku_id: string
vin?: string
_echo_vin?: boolean
mileage?: number
listing_price?: number | undefined
listing_price_currency: string
listing_price_currency_snapshot: string
reduction_price?: number | undefined
reduction_price_currency: string
reduction_price_currency_snapshot: string
model_snapshot: string
color_interior_code: string
color_interior_snapshot: string
color_exterior_code: string
color_exterior_snapshot: string
unit_price: number | undefined
handling_fee: string
fob_transport_fee: number | undefined
port_misc_fee: string
2025-09-24 19:06:26 +08:00
remark: string
2025-09-18 09:55:10 +08:00
ocean_freight: string
ocean_freight_currency: string
ocean_freight_currency_snapshot: string
2025-09-24 19:06:26 +08:00
commission: string
commission_currency: string
commission_currency_snapshot: string
purchase_tax: string
purchase_tax_currency: string
purchase_tax_currency_snapshot: string
tax_refund: string
tax_refund_currency: string
tax_refund_currency_snapshot: string
clearance_price: number | undefined
clearance_price_currency: string
clearance_price_currency_snapshot: string
2025-09-18 09:55:10 +08:00
retrofit_fee: string
total_price: number | undefined
official_price: number | undefined
discount_price: number | undefined
fob_fee?: number | undefined
fob_fee_currency: string
fob_fee_currency_snapshot: string
helper_fee?: number | undefined
helper_fee_currency: string
helper_fee_currency_snapshot: string
domestic_freight?: number | undefined
domestic_freight_currency: string
domestic_freight_currency_snapshot: string
port_fee?: number | undefined
port_fee_currency: string
port_fee_currency_snapshot: string
unit_price_currency: string
unit_price_currency_snapshot: string
total_price_currency: string
total_price_currency_snapshot: string
guide_price_currency: string
guide_price_currency_snapshot: string
discount?: number | undefined
discount_currency: string
discount_currency_snapshot: string
contract_rate?: number | undefined
profit: number | undefined
2025-10-22 11:52:20 +08:00
profit_currency: string
profit_currency_snapshot: string
2025-09-18 09:55:10 +08:00
profit_type_snapshot?: string
modify_ids: string[]
modify_list: Array<{ option_id: string; option_name_snapshot: string }>
modify_fee_currency: string
modify_fee_currency_snapshot: string
2025-10-22 11:52:20 +08:00
accessory_price: number | undefined
accessory_price_currency: string
accessory_price_currency_snapshot: string
retention_price: number | undefined
retention_price_currency: string
retention_price_currency_snapshot: string
car_price: number | undefined
car_price_currency: string
car_price_currency_snapshot: string
increase_price?: number | undefined
increase_price_currency?: string
increase_price_currency_snapshot?: string
2025-09-18 09:55:10 +08:00
}
export interface PaymentItem {
remit_country_id: string
remit_country_name_snapshot: string
remit_date: string
currency_code: string
currency_name_snapshot: string
account_id: string
account_name_snapshot: string
remitter_name: string
amount: number | undefined
}
export interface DialogState {
visible: boolean
search: { keyword: string }
loading: boolean
tableData: any[]
pagination: { currentPage: number; pageSize: number; total: number }
targetIndex?: number
}
export function useStockingForm(isFinal = false, stockingId?: string | number) {
const { uploadHeaders } = useUploadHeaders()
const actionImageUplode = `${import.meta.env.VITE_API_URL}/ticket/upload?scene=stock_orders`
// 回显填充抑制标记:为 true 时不触发自动计算覆盖接口值
const isPopulating = ref(false)
2025-10-22 11:52:20 +08:00
const userEditing = ref<Set<number>>(new Set()) // 跟踪用户正在编辑的行
2025-09-18 09:55:10 +08:00
// 选项数据
const channelOptions = ref<any>()
const countryOptions = ref<any>()
const modificationOptions = ref<any>()
const currencyOptions = ref<any>()
const accountOptions = ref<any>()
const customerOptions = ref<any>()
const portOptions = ref<any>()
const portOptionsall = ref<any>()
const wcolorOptions = ref<Array<any>>([])
const ncolorOptions = ref<Array<any>>([])
const transportOptions = ref<Array<any>>([])
const trade_termsOptions = ref<Array<any>>([])
// 表单数据
const baseForm = reactive<BaseForm>({
pi_no: '',
biz_date: '',
clearance_country_id: '',
clearance_country_snapshot: '',
customer_source_id: '',
customer_source_snapshot: '',
customer_id: '',
customer_snapshot: '',
customer_country_id: '',
customer_country_snapshot: '',
declarant_name: '',
eta_ship_date: '',
shipping_method_id: '',
shipping_method_snapshot: '',
domestic_port_id: '',
domestic_port_snapshot: '',
trade_term_id: '',
trade_term_snapshot: '',
clearance_port_id: '',
clearance_port_snapshot: '',
latest_arrival_date: '',
contract_exchange_rate: '',
settlement_currency: 'CNY',
2025-09-18 09:55:10 +08:00
remark: '',
2025-10-22 11:52:20 +08:00
cif_total: undefined,
cif_total_currency: 'CNY',
2025-10-22 11:52:20 +08:00
cif_total_currency_snapshot: '',
2025-09-18 09:55:10 +08:00
is_final: isFinal ? '1' : '0',
object_id: stockingId || '0',
2025-10-22 11:52:20 +08:00
contract: [],
contract_url: [],
pi_doc: [],
pi_doc_url: [],
transaction_slip: [],
transaction_slip_url: [],
2025-09-18 09:55:10 +08:00
pick_up_date: '',
collect_all_date: ''
})
// 创建新的订单项
const newItem = (): ItemRow => ({
sku_id: '',
model_snapshot: '',
_echo_vin: false,
mileage: 0,
listing_price: undefined,
listing_price_currency: 'CNY',
listing_price_currency_snapshot: '人民币',
2025-09-18 09:55:10 +08:00
reduction_price: undefined,
reduction_price_currency: 'CNY',
reduction_price_currency_snapshot: '人民币',
2025-09-18 09:55:10 +08:00
color_interior_code: '',
color_interior_snapshot: '',
color_exterior_code: '',
color_exterior_snapshot: '',
unit_price: undefined,
handling_fee: '',
fob_transport_fee: undefined,
port_misc_fee: '',
2025-09-24 19:06:26 +08:00
remark: '',
2025-09-18 09:55:10 +08:00
ocean_freight: '',
ocean_freight_currency: 'CNY',
ocean_freight_currency_snapshot: '人民币',
2025-09-24 19:06:26 +08:00
commission: '',
commission_currency: 'CNY',
commission_currency_snapshot: '人民币',
2025-09-24 19:06:26 +08:00
purchase_tax: '',
purchase_tax_currency: 'CNY',
purchase_tax_currency_snapshot: '人民币',
2025-09-24 19:06:26 +08:00
tax_refund: '',
tax_refund_currency: 'CNY',
tax_refund_currency_snapshot: '人民币',
clearance_price: undefined,
clearance_price_currency: 'CNY',
clearance_price_currency_snapshot: '人民币',
2025-09-18 09:55:10 +08:00
retrofit_fee: '',
total_price: undefined,
official_price: undefined,
discount_price: undefined,
fob_fee: undefined,
fob_fee_currency: 'CNY',
fob_fee_currency_snapshot: '人民币',
2025-09-18 09:55:10 +08:00
helper_fee: undefined,
helper_fee_currency: 'CNY',
helper_fee_currency_snapshot: '人民币',
2025-09-18 09:55:10 +08:00
domestic_freight: undefined,
domestic_freight_currency: 'CNY',
domestic_freight_currency_snapshot: '人民币',
2025-09-18 09:55:10 +08:00
port_fee: undefined,
port_fee_currency: 'CNY',
port_fee_currency_snapshot: '人民币',
unit_price_currency: 'CNY',
unit_price_currency_snapshot: '人民币',
total_price_currency: 'CNY',
total_price_currency_snapshot: '人民币',
guide_price_currency: 'CNY',
guide_price_currency_snapshot: '人民币',
2025-09-18 09:55:10 +08:00
discount: undefined,
discount_currency: 'CNY',
discount_currency_snapshot: '人民币',
2025-09-18 09:55:10 +08:00
contract_rate: 7.16,
profit: undefined,
2025-10-22 11:52:20 +08:00
profit_currency: 'CNY',
profit_currency_snapshot: '人民币',
2025-09-18 09:55:10 +08:00
modify_ids: [],
modify_list: [],
modify_fee_currency: 'CNY',
modify_fee_currency_snapshot: '人民币',
2025-10-22 11:52:20 +08:00
accessory_price: undefined,
accessory_price_currency: 'CNY',
accessory_price_currency_snapshot: '人民币',
2025-10-22 11:52:20 +08:00
retention_price: undefined,
retention_price_currency: 'CNY',
retention_price_currency_snapshot: '人民币',
2025-10-22 11:52:20 +08:00
car_price: undefined,
car_price_currency: 'CNY',
car_price_currency_snapshot: '人民币',
increase_price: undefined,
increase_price_currency: 'CNY',
increase_price_currency_snapshot: '人民币'
2025-09-18 09:55:10 +08:00
})
// 创建新的收款项
const newPaymentItem = (): PaymentItem => ({
remit_country_id: '',
remit_country_name_snapshot: '',
remit_date: '',
currency_code: 'CNY',
currency_name_snapshot: '人民币',
2025-09-18 09:55:10 +08:00
account_id: '',
account_name_snapshot: '',
remitter_name: '',
amount: undefined
})
const items = ref<ItemRow[]>([newItem()])
const paymentItems = ref<PaymentItem[]>([newPaymentItem()])
2025-10-22 11:52:20 +08:00
const contractFiles = ref<{ name: string; url: string; file_name: string }[]>([])
const piFiles = ref<{ name: string; url: string; file_name: string }[]>([])
const waterBillFiles = ref<{ name: string; url: string; file_name: string }[]>([])
2025-09-18 09:55:10 +08:00
// 弹框状态
const customerDialog = reactive<DialogState>({
visible: false,
search: { keyword: '' },
loading: false,
tableData: [],
pagination: { currentPage: 1, pageSize: 10, total: 0 }
})
const vehicleDialog = reactive<DialogState>({
visible: false,
search: { keyword: '' },
loading: false,
tableData: [],
pagination: { currentPage: 1, pageSize: 10, total: 0 },
targetIndex: -1
})
// 数据加载方法
const getgetChannel = async () => {
const res = await CrmService.getChannel()
channelOptions.value = res || []
}
const getcustomer = async () => {
const res = await commonService.getCustomerSelect()
customerOptions.value = res || []
}
const getarea = async () => {
const res = await CrmService.getCountry()
res.forEach((item: any) => {
item.cn_addrs = item.cn_addr + '(' + item.en_addr + ')'
})
// console.log(res)
countryOptions.value = res || []
}
const getmodification = async () => {
const res = await commonService.getModificationSelect()
modificationOptions.value = res || []
}
const loadColorOptions = async () => {
try {
const res = await commonService.getColorSelect({ type: 1 })
wcolorOptions.value = res
const response = await commonService.getColorSelect({ type: 2 })
ncolorOptions.value = response
} catch (error) {
console.error('获取颜色列表失败:', error)
ElMessage.error('获取颜色列表失败')
}
}
const loadAccountOptions = async () => {
try {
const res = await commonService.getAccountSelect()
accountOptions.value = res
} catch (error) {
console.error('获取账户列表失败:', error)
ElMessage.error('获取账户列表失败')
}
}
const loadCurrencyOptions = async () => {
try {
const res = await commonService.getCurrencySelect()
currencyOptions.value = res
} catch (error) {
console.error('获取币种列表失败:', error)
ElMessage.error('获取币种列表失败')
}
}
const loadPortOptions = async () => {
try {
const res = await commonService.getPortSelect({ scene: 'domestic' })
2025-09-24 19:06:26 +08:00
res.forEach((item: any) => {
item.port_names = item.port_name + '(' + item.port_name_en + ')'
})
const resall = await commonService.getPortSelect({ scene: 'overseas' })
resall.forEach((item: any) => {
item.port_names = item.port_name + '(' + item.port_name_en + ')'
})
2025-09-18 09:55:10 +08:00
portOptions.value = res
portOptionsall.value = resall
} catch (error) {
console.error('获取港口列表失败:', error)
ElMessage.error('获取港口列表失败')
}
}
const loadDictOptions = async () => {
const response = await commonService.getDictSelect({ scope: '2,15' })
transportOptions.value = response.transport
trade_termsOptions.value = response.trade_terms
}
// 币种相关方法
const getCurrencyRate = (code: string): string => {
try {
const found = (currencyOptions?.value || []).find((c: any) => c.currency_code === code)
return found?.rate || found?.exchange_rate || ''
} catch {
2025-09-18 09:55:10 +08:00
return ''
}
}
const getCurrencyBounds = (code: string): { min: number; max: number; base: number } => {
const found: any =
(currencyOptions?.value || []).find((c: any) => c.currency_code === code) || {}
2025-09-18 09:55:10 +08:00
const base = Number(found?.rate || found?.exchange_rate || 0) || 0
const up = Number(found?.float_percent_up)
const down = Number(found?.float_percent_down)
if (!Number.isNaN(up) || !Number.isNaN(down)) {
const upPct = (Number.isNaN(up) ? 0 : up) / 100
const downPct = (Number.isNaN(down) ? 0 : down) / 100
return { min: base * (1 - downPct), max: base * (1 + upPct), base }
}
return { min: base * 0.9, max: base * 1.1, base }
}
const validateContractRate = () => {
const code = baseForm.settlement_currency || items.value[0]?.unit_price_currency || 'CNY'
2025-09-18 09:55:10 +08:00
const { min, max } = getCurrencyBounds(code)
const val = Number(baseForm.contract_exchange_rate)
if (Number.isNaN(val)) return
if (val < min) {
baseForm.contract_exchange_rate = String(Number(min.toFixed(6)))
ElMessage?.warning?.('合同汇率已按币种浮动下限自动纠正')
} else if (val > max) {
baseForm.contract_exchange_rate = String(Number(max.toFixed(6)))
ElMessage?.warning?.('合同汇率已按币种浮动上限自动纠正')
}
}
// 金额包装方法
const wrapMoney = (amount: any, currency: string, remark: string) => ({
currency: currency || '',
amount: amount === undefined || amount === null ? 0 : String(amount),
2025-09-18 09:55:10 +08:00
rate: getCurrencyRate(currency),
remark
})
// 利润计算方法
const getRateNumber = (code: string): number => {
const r = Number(getCurrencyRate(code))
return Number.isNaN(r) ? 0 : r
}
const convertCurrency = (amount: any, fromCode?: string, toCode?: string): number => {
const val = amount === undefined || amount === null || amount === '' ? 0 : Number(amount)
if (Number.isNaN(val)) return 0
const from = fromCode || 'CNY'
const to = toCode || 'CNY'
2025-09-18 09:55:10 +08:00
if (from === to) return val
const rateFrom = getRateNumber(from)
const rateTo = getRateNumber(to) || 1
if (!rateFrom) return 0
return (val * rateFrom) / rateTo
}
2025-10-22 11:52:20 +08:00
// 利润=收款价-官方指导价/二手挂牌价+优惠-手续费-改装费-配件费-fob运费-港杂-海运-佣金-挂账-购置税+预估退税
2025-09-18 09:55:10 +08:00
const calcProfit = (it: any): number => {
2025-10-22 11:52:20 +08:00
const targetCurrency = 'CNY' // 固定使用人民币
2025-09-18 09:55:10 +08:00
const unit = convertCurrency(it.unit_price, it.unit_price_currency, targetCurrency)
const retrofit = convertCurrency(
it.retrofit_fee,
(it as any).modify_fee_currency,
targetCurrency
)
2025-09-18 09:55:10 +08:00
const fob = convertCurrency(
it.fob_transport_fee ?? (it as any).fob_fee,
(it as any).fob_fee_currency,
targetCurrency
)
const handling = convertCurrency(
it.handling_fee ?? (it as any).helper_fee,
(it as any).helper_fee_currency,
targetCurrency
)
const port = convertCurrency(
it.port_misc_fee ?? (it as any).port_fee,
(it as any).port_fee_currency,
targetCurrency
)
const ocean = convertCurrency(
it.ocean_freight ?? (it as any).ocean_freight,
(it as any).ocean_freight_currency,
targetCurrency
)
2025-09-24 19:06:26 +08:00
const commission = convertCurrency(
it.commission ?? (it as any).commission,
(it as any).commission_currency,
targetCurrency
)
const purchaseTax = convertCurrency(
it.purchase_tax ?? (it as any).purchase_tax,
(it as any).purchase_tax_currency,
targetCurrency
)
const taxRefund = convertCurrency(
it.tax_refund ?? (it as any).tax_refund,
(it as any).tax_refund_currency,
targetCurrency
)
2025-10-22 11:52:20 +08:00
//挂账
const retention = convertCurrency(
it.retention_price ?? (it as any).retention_price,
(it as any).retention_price_currency,
targetCurrency
)
//配件
const accessory = convertCurrency(
it.accessory_price ?? (it as any).accessory_price,
(it as any).accessory_price_currency,
targetCurrency
)
//加价
const increase = convertCurrency(
it.increase_price ?? (it as any).increase_price,
(it as any).increase_price_currency,
targetCurrency
)
2025-09-18 09:55:10 +08:00
const mileageVal = Number(it.mileage ?? 0)
if (mileageVal > 0) {
// 二手车:用挂牌价与二手减价替代官方指导价与优惠
const listing = convertCurrency(
it.listing_price ?? (it as any).listing_price,
(it as any).listing_price_currency,
targetCurrency
)
const reduction = convertCurrency(
it.reduction_price ?? (it as any).reduction_price,
(it as any).reduction_price_currency,
targetCurrency
)
2025-10-22 13:53:25 +08:00
const profit =
2025-09-24 19:06:26 +08:00
unit -
2025-10-22 13:53:25 +08:00
(retrofit +
fob +
listing +
handling +
port +
ocean +
commission +
purchaseTax +
retention +
increase +
2025-10-22 13:53:25 +08:00
accessory) +
taxRefund +
reduction
2025-10-22 13:53:25 +08:00
return Number(profit.toFixed(2))
2025-09-18 09:55:10 +08:00
}
// 新车/里程<=0:保持原有计算(官方指导价与优惠)
const guide = convertCurrency(
it.official_price ?? (it as any).guide_price,
(it as any).guide_price_currency,
targetCurrency
)
const discount = convertCurrency(
it.discount_price ?? (it as any).discount,
(it as any).discount_currency,
targetCurrency
)
2025-10-22 13:53:25 +08:00
const profit =
2025-09-24 19:06:26 +08:00
unit -
2025-10-22 13:53:25 +08:00
(retrofit +
fob +
guide +
handling +
port +
ocean +
commission +
purchaseTax +
retention +
increase +
2025-10-22 13:53:25 +08:00
accessory) +
taxRefund +
discount
2025-10-22 13:53:25 +08:00
return Number(profit.toFixed(2))
2025-09-18 09:55:10 +08:00
}
2025-10-22 11:52:20 +08:00
// 计算报关价 = 收款价 - 挂账
const calcClearanceFee = (it: any): number => {
const targetCurrency = it.clearance_price_currency || baseForm.settlement_currency || 'CNY'
const unit = convertCurrency(it.unit_price, it.unit_price_currency, targetCurrency)
2025-10-22 11:52:20 +08:00
const retention = convertCurrency(
it.retention_price ?? (it as any).retention_price,
(it as any).retention_price_currency,
targetCurrency
)
2025-10-22 11:52:20 +08:00
return unit - retention
}
2025-10-22 11:52:20 +08:00
// 计算预估退税额 = (官方价 - 折扣价) * 11.5% (固定人民币)
const calcTaxRefund = (it: any): number => {
const mileageVal = Number(it.mileage ?? 0)
if (mileageVal > 0) return 0
2025-10-22 11:52:20 +08:00
const targetCurrency = 'CNY' // 固定使用人民币
const official = convertCurrency(
it.official_price ?? (it as any).guide_price,
(it as any).guide_price_currency,
targetCurrency
)
const discount = convertCurrency(
it.discount_price ?? (it as any).discount,
(it as any).discount_currency,
targetCurrency
)
const base = official - discount
const refund = base * 0.115
2025-10-22 13:53:25 +08:00
return refund < 0 ? 0 : Number(refund.toFixed(2))
}
2025-10-22 11:52:20 +08:00
// 计算车价:车价 = 报关价 - FOB运费 - 港杂费 - 海运费
const calcCarPrice = (it: any): number => {
const targetCurrency = it.car_price_currency || baseForm.settlement_currency || 'CNY'
2025-10-22 11:52:20 +08:00
const clearance = convertCurrency(
it.clearance_price ?? (it as any).clearance_price,
(it as any).clearance_price_currency,
targetCurrency
)
const fob = convertCurrency(
it.fob_transport_fee ?? (it as any).fob_fee,
(it as any).fob_fee_currency,
targetCurrency
)
const port = convertCurrency(
it.port_misc_fee ?? (it as any).port_fee,
(it as any).port_fee_currency,
targetCurrency
)
const ocean = convertCurrency(
it.ocean_freight ?? (it as any).ocean_freight,
(it as any).ocean_freight_currency,
targetCurrency
)
return clearance - fob - port - ocean
}
2025-09-18 09:55:10 +08:00
// 合同文件相关方法
const isImageFile = (file: { name: string; url: string; file_name: string }) => {
return /\.(jpg|jpeg|png|gif)$/i.test(file.name) || file.file_name.startsWith('image/')
}
const beforeContractUpload = (file: File) => {
const isValidType = /\.(jpg|jpeg|png|gif|pdf|doc|docx|xls|xlsx|txt)$/i.test(file.name)
if (!isValidType) {
ElMessage.error('只能上传图片或文档文件!')
return false
}
const isLt10M = file.size / 1024 / 1024 < 10
if (!isLt10M) {
ElMessage.error('文件大小不能超过 10MB!')
return false
}
return true
}
const handleContractUpload = (response: any) => {
if (response.code === 200) {
const fileInfo = {
name: response.data.original_name || '合同文件',
url: response.data.file_url,
file_name: response.data.file_name
}
2025-10-22 11:52:20 +08:00
contractFiles.value.push(fileInfo)
// 更新合同字段为文件名数组
baseForm.contract = contractFiles.value.map((file) => file.file_name)
2025-09-18 09:55:10 +08:00
ElMessage.success('合同上传成功!')
} else {
ElMessage.error(response.message || '合同上传失败!')
}
}
2025-10-22 11:52:20 +08:00
const removeContractFile = (index: number) => {
contractFiles.value.splice(index, 1)
// 更新合同字段为文件名数组
baseForm.contract = contractFiles.value.map((file) => file.file_name)
2025-09-18 09:55:10 +08:00
ElMessage.success('合同文件已删除')
}
2025-10-22 11:52:20 +08:00
const downloadContractFile = (file: { name: string; url: string; file_name: string }) => {
// 创建下载链接
const link = document.createElement('a')
link.href = file.url
link.download = file.name
link.target = '_blank'
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
}
// PI文件处理方法
const removePiFile = (index: number) => {
piFiles.value.splice(index, 1)
// 更新PI字段为文件名数组
baseForm.pi_doc = piFiles.value.map((file) => file.file_name)
ElMessage.success('PI文件已删除')
}
const downloadPiFile = (file: { name: string; url: string; file_name: string }) => {
const link = document.createElement('a')
link.href = file.url
link.download = file.name
link.target = '_blank'
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
}
// 水单文件处理方法
const removeWaterBillFile = (index: number) => {
waterBillFiles.value.splice(index, 1)
// 更新水单字段为文件名数组
baseForm.transaction_slip = waterBillFiles.value.map((file) => file.file_name)
ElMessage.success('水单文件已删除')
}
const downloadWaterBillFile = (file: { name: string; url: string; file_name: string }) => {
const link = document.createElement('a')
link.href = file.url
link.download = file.name
link.target = '_blank'
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
}
2025-09-18 09:55:10 +08:00
// 订单项操作方法
const copyRow = (idx: number) => {
const copied = JSON.parse(JSON.stringify(items.value[idx])) as ItemRow
items.value.splice(idx + 1, 0, copied)
}
const addRowAfter = (idx: number) => {
items.value.splice(idx + 1, 0, newItem())
}
const removeRow = (idx: number) => {
items.value.splice(idx, 1)
}
// 收款项操作方法
const addPaymentItem = () => {
paymentItems.value.push(newPaymentItem())
}
const removePaymentItem = (idx: number) => {
if (paymentItems.value.length > 1) {
paymentItems.value.splice(idx, 1)
}
}
2025-10-22 11:52:20 +08:00
const copyPaymentItem = (idx: number) => {
const originalItem = paymentItems.value[idx]
if (originalItem) {
const copiedItem = { ...originalItem }
// 完全复制所有字段,包括汇款人和汇款日期
paymentItems.value.splice(idx + 1, 0, copiedItem)
}
}
2025-09-18 09:55:10 +08:00
// 客户选择相关方法
const openCustomerDialog = () => {
customerDialog.visible = true
customerDialog.pagination.currentPage = 1
getCustomerList()
}
const getCustomerList = async () => {
customerDialog.loading = true
try {
const { currentPage, pageSize } = customerDialog.pagination
const params = {
current: currentPage,
size: pageSize,
keyword: customerDialog.search.keyword
}
2025-09-18 09:55:10 +08:00
const { data, current, size, total } = await CrmService.getcustomerList(params as any)
customerDialog.tableData = data || []
Object.assign(customerDialog.pagination, { currentPage: current, pageSize: size, total })
} catch {
// 忽略错误
2025-09-18 09:55:10 +08:00
} finally {
customerDialog.loading = false
}
}
const handleCustomerSizeChange = (size: number) => {
customerDialog.pagination.pageSize = size
getCustomerList()
}
const handleCustomerCurrentChange = (page: number) => {
customerDialog.pagination.currentPage = page
getCustomerList()
}
const chooseCustomer = (row: any) => {
baseForm.customer_id = row.id || ''
baseForm.customer_snapshot = row.contacts || row.name || ''
baseForm.customer_country_id = row.country_id || ''
baseForm.customer_country_snapshot = row.country_name || ''
baseForm.customer_source_id = row.channel_id || ''
baseForm.customer_source_snapshot = row.channel_name || ''
customerDialog.visible = false
}
// 车辆选择相关方法
const openVehicleDialog = (index: number) => {
vehicleDialog.targetIndex = index
vehicleDialog.visible = true
vehicleDialog.pagination.currentPage = 1
getVehicleList()
}
const getVehicleList = async () => {
vehicleDialog.loading = true
try {
const { currentPage, pageSize } = vehicleDialog.pagination
const params = { current: currentPage, size: pageSize, keyword: vehicleDialog.search.keyword }
const { records, current, size, total } = await vehicleService.getVehicleList(params)
vehicleDialog.tableData = records || []
Object.assign(vehicleDialog.pagination, { currentPage: current, pageSize: size, total })
} catch {
ElMessage.error('获取车辆列表失败')
} finally {
vehicleDialog.loading = false
}
}
const handleVehicleSizeChange = (size: number) => {
vehicleDialog.pagination.pageSize = size
getVehicleList()
}
const handleVehicleCurrentChange = (page: number) => {
vehicleDialog.pagination.currentPage = page
getVehicleList()
}
const chooseVehicle = (row: any) => {
const idx = vehicleDialog.targetIndex
if (idx !== undefined && idx >= 0 && idx < items.value.length) {
const target = items.value[idx]
target.sku_id = row.id || ''
2025-10-22 11:52:20 +08:00
target.model_snapshot = row.brand_name + ' ' + row.series_name + ' ' + row.model_name
2025-09-18 09:55:10 +08:00
target.color_exterior_code = row.color_exterior_code || ''
target.color_exterior_snapshot = row.color_exterior_text || ''
target.color_interior_code = row.color_interior_code || ''
target.color_interior_snapshot = row.color_interior_text || ''
target.official_price = row.msrp ?? target.official_price
if (row.currency) {
target.guide_price_currency = row.currency
}
}
vehicleDialog.visible = false
}
// 数据回显方法
const populateFromDetail = (res: any) => {
if (!res) return
isPopulating.value = true
2025-09-18 09:55:10 +08:00
// 基础信息
baseForm.pick_up_date = res.pick_up_date || ''
baseForm.collect_all_date = res.collect_all_date || ''
baseForm.pi_no = res.pi_no || ''
2025-10-22 11:52:20 +08:00
// 合同字段兼容字符串或数组
baseForm.contract = Array.isArray(res.contract)
? res.contract
2025-10-22 13:53:25 +08:00
: res.contract
? String(res.contract).split(',')
: []
2025-09-18 09:55:10 +08:00
baseForm.biz_date = res.biz_date || ''
baseForm.clearance_country_id = res.clearance_country_id || ''
baseForm.clearance_country_snapshot = res.clearance_country_snapshot || ''
baseForm.customer_source_id = res.customer_source_id || ''
baseForm.customer_source_snapshot = res.customer_source_snapshot || ''
baseForm.customer_id = res.customer_id || ''
baseForm.customer_snapshot = res.customer_snapshot || ''
baseForm.customer_country_id = res.customer_country_id || ''
baseForm.customer_country_snapshot = res.customer_country_snapshot || ''
baseForm.declarant_name = res.declarant_name || ''
baseForm.eta_ship_date = res.eta_ship_date || ''
baseForm.shipping_method_id = res.shipping_method_id ? String(res.shipping_method_id) : ''
baseForm.shipping_method_snapshot = res.shipping_method_snapshot || ''
baseForm.domestic_port_id = res.domestic_port_id || ''
baseForm.domestic_port_snapshot = res.domestic_port_snapshot || ''
baseForm.trade_term_id = res.trade_term_id || ''
baseForm.trade_term_snapshot = res.trade_term_snapshot || ''
baseForm.clearance_port_id = res.clearance_port_id || ''
baseForm.clearance_port_snapshot = res.clearance_port_snapshot || ''
baseForm.latest_arrival_date = res.latest_arrival_date || ''
baseForm.remark = res.remark || ''
baseForm.settlement_currency = res.settlement_currency || 'CNY'
2025-09-18 09:55:10 +08:00
// 合计与汇率
2025-10-22 11:52:20 +08:00
baseForm.cif_total = res.cif_total.amount ? Number(res.cif_total.amount) : undefined
baseForm.cif_total_currency = res.cif_total.currency || 'CNY'
2025-10-22 11:52:20 +08:00
baseForm.contract_exchange_rate = res.contract_exchange_rate
2025-09-18 09:55:10 +08:00
// 明细
const list = Array.isArray(res.veh_list) ? res.veh_list : []
items.value = list.map((it: any) => ({
sku_id: it.sku_id ? String(it.sku_id) : '',
vin: it.vin || '',
_echo_vin: Boolean(it.vin),
mileage: it.mileage ?? 0,
model_snapshot: it.model_snapshot || '',
listing_price: it.listing_price?.amount ? Number(it.listing_price.amount) : undefined,
listing_price_currency: it.listing_price?.currency || 'CNY',
2025-09-18 09:55:10 +08:00
listing_price_currency_snapshot: '',
reduction_price: it.reduction_price?.amount ? Number(it.reduction_price.amount) : undefined,
reduction_price_currency: it.reduction_price?.currency || 'CNY',
2025-09-18 09:55:10 +08:00
reduction_price_currency_snapshot: '',
color_interior_code: it.color_interior_code || '',
color_interior_snapshot: it.color_interior_snapshot || '',
color_exterior_code: it.color_exterior_code || '',
color_exterior_snapshot: it.color_exterior_snapshot || '',
unit_price: it.unit_price?.amount ? Number(it.unit_price.amount) : undefined,
handling_fee: it.handling_fee?.amount ?? '',
fob_transport_fee: it.fob_transport_fee?.amount
? Number(it.fob_transport_fee.amount)
: undefined,
2025-09-18 09:55:10 +08:00
port_misc_fee: it.port_misc_fee?.amount ?? '',
2025-09-24 19:06:26 +08:00
remark: it.remark || '',
2025-09-18 09:55:10 +08:00
ocean_freight: it.ocean_freight?.amount ?? '',
ocean_freight_currency: it.ocean_freight?.currency || 'CNY',
2025-09-18 09:55:10 +08:00
ocean_freight_currency_snapshot: '',
2025-09-24 19:06:26 +08:00
commission: it.commission?.amount ?? '',
commission_currency: it.commission?.currency || 'CNY',
2025-09-24 19:06:26 +08:00
commission_currency_snapshot: '',
purchase_tax: it.purchase_tax?.amount ?? '',
purchase_tax_currency: it.purchase_tax?.currency || 'CNY',
2025-09-24 19:06:26 +08:00
purchase_tax_currency_snapshot: '',
2025-09-18 09:55:10 +08:00
retrofit_fee: it.retrofit_fee?.amount ?? '',
total_price: it.total_price?.amount ? Number(it.total_price.amount) : undefined,
official_price: it.official_price?.amount ? Number(it.official_price.amount) : undefined,
discount_price: it.discount_price?.amount ? Number(it.discount_price.amount) : undefined,
fob_fee: undefined,
fob_fee_currency: it.fob_transport_fee?.currency || 'CNY',
2025-09-18 09:55:10 +08:00
fob_fee_currency_snapshot: '',
helper_fee: undefined,
helper_fee_currency: it.handling_fee?.currency || 'CNY',
2025-09-18 09:55:10 +08:00
helper_fee_currency_snapshot: '',
domestic_freight: undefined,
domestic_freight_currency: it.domestic_freight?.currency || 'CNY',
2025-09-18 09:55:10 +08:00
domestic_freight_currency_snapshot: '',
port_fee: undefined,
port_fee_currency: it.port_misc_fee?.currency || 'CNY',
2025-09-18 09:55:10 +08:00
port_fee_currency_snapshot: '',
unit_price_currency: it.unit_price?.currency || 'CNY',
2025-09-18 09:55:10 +08:00
unit_price_currency_snapshot: '',
total_price_currency: it.total_price?.currency || 'CNY',
2025-09-18 09:55:10 +08:00
total_price_currency_snapshot: '',
guide_price_currency: it.official_price?.currency || 'CNY',
2025-09-18 09:55:10 +08:00
guide_price_currency_snapshot: '',
discount: undefined,
discount_currency: it.discount_price?.currency || 'CNY',
2025-09-18 09:55:10 +08:00
discount_currency_snapshot: '',
contract_rate: baseForm.contract_exchange_rate
? Number(baseForm.contract_exchange_rate)
: undefined,
profit: it.profit?.amount ? Number(it.profit.amount) : undefined,
2025-09-18 09:55:10 +08:00
profit_type_snapshot: '',
clearance_price: it.clearance_price?.amount ? Number(it.clearance_price.amount) : undefined,
clearance_price_currency: it.clearance_price?.currency || 'CNY',
clearance_price_currency_snapshot: '',
modify_ids: Array.isArray(it.modify_list)
? it.modify_list.map((m: any) => Number(m.option_id))
: [],
2025-09-18 09:55:10 +08:00
modify_list: Array.isArray(it.modify_list)
? it.modify_list.map((m: any) => ({
option_id: String(m.option_id),
option_name_snapshot: m.option_name_snapshot
}))
2025-09-18 09:55:10 +08:00
: [],
modify_fee_currency: it.retrofit_fee?.currency || 'CNY',
modify_fee_currency_snapshot: '',
tax_refund: it.tax_refund?.amount ?? '',
tax_refund_currency: it.tax_refund?.currency || 'CNY',
2025-10-22 11:52:20 +08:00
tax_refund_currency_snapshot: '',
accessory_price: it.accessory_price?.amount ? Number(it.accessory_price.amount) : undefined,
accessory_price_currency: it.accessory_price?.currency || 'CNY',
2025-10-22 11:52:20 +08:00
accessory_price_currency_snapshot: '',
retention_price: it.retention_price?.amount ? Number(it.retention_price.amount) : undefined,
retention_price_currency: it.retention_price?.currency || 'CNY',
2025-10-22 11:52:20 +08:00
retention_price_currency_snapshot: '',
car_price: it.car_price?.amount ? Number(it.car_price.amount) : undefined,
car_price_currency: it.car_price?.currency || 'CNY',
car_price_currency_snapshot: '',
increase_price: it.increase_price?.amount ? Number(it.increase_price.amount) : undefined,
increase_price_currency: it.increase_price?.currency || 'CNY',
increase_price_currency_snapshot: ''
2025-09-18 09:55:10 +08:00
}))
// 计划收款
const pays = Array.isArray(res.account_list) ? res.account_list : []
paymentItems.value = pays.map((p: any) => ({
remit_country_id: p.remit_country_id ? Number(p.remit_country_id) : '',
remit_country_name_snapshot: p.remit_country_name_snapshot || '',
remit_date: p.remit_date || '',
currency_code: p.currency_code || '',
currency_name_snapshot: p.currency_name_snapshot || '',
account_id: p.account_id ? p.account_id : '',
account_name_snapshot: p.account_name_snapshot || '',
remitter_name: p.remitter_name || '',
amount: p.amount ? Number(p.amount) : undefined
}))
2025-10-22 11:52:20 +08:00
// 合同文件回显(兼容数组与逗号字符串)
2025-09-18 09:55:10 +08:00
if (res.contract) {
2025-10-22 11:52:20 +08:00
const contractNamesArr: string[] = Array.isArray(res.contract)
? res.contract
: String(res.contract).split(',')
const contractUrlsArr: string[] = Array.isArray(res.contract_url)
? (res.contract_url as string[])
2025-10-22 13:53:25 +08:00
: res.contract_url
? String(res.contract_url).split(',')
: []
2025-10-22 11:52:20 +08:00
baseForm.contract = contractNamesArr
baseForm.contract_url = contractUrlsArr
contractFiles.value = contractNamesArr.map((fileName: string, index: number) => ({
name: fileName,
url: contractUrlsArr[index] || fileName,
file_name: fileName
}))
}
// PI文件回显(兼容数组与逗号字符串)
if (res.pi_doc) {
const piNamesArr: string[] = Array.isArray(res.pi_doc)
? res.pi_doc
: String(res.pi_doc).split(',')
const piUrlsArr: string[] = Array.isArray(res.pi_doc_url)
? (res.pi_doc_url as string[])
2025-10-22 13:53:25 +08:00
: res.pi_doc_url
? String(res.pi_doc_url).split(',')
: []
2025-10-22 11:52:20 +08:00
baseForm.pi_doc = piNamesArr
baseForm.pi_doc_url = piUrlsArr
piFiles.value = piNamesArr.map((fileName: string, index: number) => ({
name: fileName,
url: piUrlsArr[index] || fileName,
file_name: fileName
}))
}
// 水单文件回显(兼容数组与逗号字符串)
if (res.transaction_slip) {
const slipNamesArr: string[] = Array.isArray(res.transaction_slip)
? res.transaction_slip
: String(res.transaction_slip).split(',')
const slipUrlsArr: string[] = Array.isArray(res.transaction_slip_url)
? (res.transaction_slip_url as string[])
2025-10-22 13:53:25 +08:00
: res.transaction_slip_url
? String(res.transaction_slip_url).split(',')
: []
2025-10-22 11:52:20 +08:00
baseForm.transaction_slip = slipNamesArr
baseForm.transaction_slip_url = slipUrlsArr
waterBillFiles.value = slipNamesArr.map((fileName: string, index: number) => ({
name: fileName,
url: slipUrlsArr[index] || fileName,
file_name: fileName
}))
2025-09-18 09:55:10 +08:00
}
// 等待本轮变更完成后,再恢复自动计算
nextTick(() => {
isPopulating.value = false
})
2025-09-18 09:55:10 +08:00
}
// 提交方法
const handleSubmit = async (type: number, edit?: string, isfinal?: string) => {
// 提交校验:最终提交且为提交动作时(type=1),必须填写 VIN
const isFinalFlag = isfinal === '1' || (isfinal as any) === true
2025-10-22 13:53:25 +08:00
if (type === 1 && baseForm.contract.length == 0) {
2025-10-22 11:52:20 +08:00
ElMessage.error('请上传合同文件')
return
}
2025-10-22 13:53:25 +08:00
if (type === 1 && baseForm.pi_doc.length == 0) {
2025-10-22 11:52:20 +08:00
ElMessage.error('请上传pi文件')
return
}
2025-10-22 13:53:25 +08:00
if (type === 1 && baseForm.transaction_slip.length == 0) {
2025-10-22 11:52:20 +08:00
ElMessage.error('请上传水单文件')
return
}
2025-09-18 09:55:10 +08:00
if (isFinalFlag && type === 1) {
// const hasEmptyVin = (items.value || []).some((it) => !it.vin || String(it.vin).trim() === '')
// if (hasEmptyVin) {
// ElMessage.error('存在未填写车架号(VIN)的明细,请补全后再提交')
// return
// }
2025-09-18 09:55:10 +08:00
}
const mappedItems = items.value.map((it) => ({
sku_id: it.sku_id,
vin: it.vin || '',
mileage: it.mileage,
model_snapshot: it.model_snapshot,
color_interior_code: it.color_interior_code,
color_interior_snapshot: it.color_interior_snapshot,
color_exterior_code: it.color_exterior_code,
color_exterior_snapshot: it.color_exterior_snapshot,
2025-09-24 19:06:26 +08:00
remark: it.remark,
2025-09-18 09:55:10 +08:00
unit_price: wrapMoney(it.unit_price, (it as any).unit_price_currency, '单价'),
handling_fee: wrapMoney(it.handling_fee, (it as any).helper_fee_currency, '手续费'),
fob_transport_fee: wrapMoney(
it.fob_transport_fee ?? (it as any).fob_fee,
(it as any).fob_fee_currency,
'FOB运输费'
),
port_misc_fee: wrapMoney(
it.port_misc_fee ?? (it as any).port_fee,
(it as any).port_fee_currency,
'港杂费'
),
ocean_freight: wrapMoney(it.ocean_freight, (it as any).ocean_freight_currency, '海运费'),
2025-09-24 19:06:26 +08:00
commission: wrapMoney(it.commission, (it as any).commission_currency, '佣金'),
purchase_tax: wrapMoney(it.purchase_tax, (it as any).purchase_tax_currency, '购置税'),
// tax_refund: wrapMoney(it.tax_refund, (it as any).tax_refund_currency, '退税'),
2025-10-22 11:52:20 +08:00
tax_refund: wrapMoney(it.tax_refund, 'CNY', '退税'),
2025-09-18 09:55:10 +08:00
retrofit_fee: wrapMoney((it as any).retrofit_fee, (it as any).modify_fee_currency, '改装费'),
modify_list: it.modify_list,
// total_price: wrapMoney(it.total_price, (it as any).total_price_currency, '总价'),
total_price: wrapMoney(it.unit_price, (it as any).unit_price_currency, '总价'),
official_price: wrapMoney(
(it as any).official_price ?? (it as any).guide_price,
(it as any).guide_price_currency,
'官方指导价'
),
discount_price: wrapMoney(
(it as any).discount_price ?? (it as any).discount,
(it as any).discount_currency,
'优惠额'
),
2025-10-22 11:52:20 +08:00
profit: wrapMoney(it.profit, 'CNY', '利润'),
2025-09-18 09:55:10 +08:00
listing_price: wrapMoney(it.listing_price, (it as any).listing_price_currency, '挂牌价'),
reduction_price: wrapMoney(
it.reduction_price,
(it as any).reduction_price_currency,
'二手减价'
),
2025-10-22 11:52:20 +08:00
clearance_price: wrapMoney(
it.clearance_price,
(it as any).clearance_price_currency,
'报关价'
),
accessory_price: wrapMoney(
it.accessory_price,
(it as any).accessory_price_currency,
'配件价'
),
retention_price: wrapMoney(it.retention_price, (it as any).retention_price_currency, '挂账'),
car_price: wrapMoney(it.car_price, (it as any).car_price_currency, '车价'),
increase_price: wrapMoney(it.increase_price, (it as any).increase_price_currency, '加价')
2025-09-18 09:55:10 +08:00
}))
// 从提交参数中移除 settlement_currency
const baseFormWithoutCur = { ...(baseForm as any) }
2025-10-22 13:53:25 +08:00
baseFormWithoutCur.cif_total = wrapMoney(
baseFormWithoutCur.cif_total,
baseFormWithoutCur.cif_total_currency,
'合计'
)
2025-10-22 11:52:20 +08:00
// delete (baseFormWithoutCur as any).cif_total
delete (baseFormWithoutCur as any).cif_total_currency
delete (baseFormWithoutCur as any).cif_total_currency_snapshot
2025-09-18 09:55:10 +08:00
delete (baseFormWithoutCur as any).contract_url
2025-10-22 11:52:20 +08:00
delete (baseFormWithoutCur as any).pi_doc_url
delete (baseFormWithoutCur as any).transaction_slip_url
2025-09-18 09:55:10 +08:00
const payload = {
...baseFormWithoutCur,
veh_list: mappedItems,
account_list: paymentItems.value,
approval_status: type
}
console.log(payload)
2025-09-18 09:55:10 +08:00
try {
if (edit) {
const { message } = await CrmService.editStock({
...payload,
id: edit
} as any)
ElMessage.success(message || '保存成功')
} else {
const { message } = await CrmService.addStock(payload as any)
ElMessage.success(message || '保存成功')
}
setTimeout(() => {
if (isfinal == '1') {
window.location.href = '/crmpc/stocking/final_list'
} else {
window.location.href = '/crmpc/stocking/stocking_list'
}
}, 1000)
} catch {
// ElMessage.error('保存失败')
2025-09-18 09:55:10 +08:00
}
}
// 监听器
watch(
() => baseForm.settlement_currency,
(newCurrency) => {
if (isPopulating.value) return
2025-09-18 09:55:10 +08:00
if (newCurrency) {
baseForm.contract_exchange_rate = getCurrencyRate(newCurrency)
if (typeof validateContractRate === 'function') {
validateContractRate()
}
2025-10-22 11:52:20 +08:00
// // 结算币种变化时,按新币种重算所有行利润
// ;(items.value || []).forEach((it: any, index: number) => {
// const profit = calcProfit(it)
// if (items.value[index])
// items.value[index].profit = isNaN(profit) ? 0 : Number(profit.toFixed(2))
// const clearance = calcClearanceFee(it)
// if (items.value[index]) {
// items.value[index].clearance_price = isNaN(clearance) ? 0 : Number(clearance.toFixed(2))
// // 确保报关价货币单位与计算结果一致
// if (items.value[index]) {
// ;(items.value[index] as any).clearance_price_currency =
// it.clearance_price_currency || baseForm.settlement_currency || 'CNY'
2025-10-22 11:52:20 +08:00
// }
// }
// const refund = calcTaxRefund(it)
// if (items.value[index]) {
// items.value[index].tax_refund = isNaN(refund) ? 0 : (Number(refund.toFixed(2)) as any)
// ;(items.value[index] as any).tax_refund_currency = baseForm.settlement_currency || 'CNY'
2025-10-22 11:52:20 +08:00
// }
// })
2025-09-18 09:55:10 +08:00
}
}
)
watch(
() => baseForm.contract_exchange_rate,
(newCurrency) => {
if (newCurrency) {
if (typeof validateContractRate === 'function') {
validateContractRate()
}
}
}
)
watch(
items,
(val) => {
if (isPopulating.value) return
2025-09-18 09:55:10 +08:00
val.forEach((it: any, index: number) => {
2025-10-22 11:52:20 +08:00
// 跳过用户正在编辑的行
if (userEditing.value.has(index)) return
2025-09-18 09:55:10 +08:00
const profit = calcProfit(it)
2025-10-22 11:52:20 +08:00
if (items.value[index]) {
items.value[index].profit = isNaN(profit) ? 0 : Number(profit.toFixed(2))
2025-10-22 11:52:20 +08:00
;(items.value[index] as any).profit_currency = 'CNY'
}
// 自动计算报关价
const clearance = calcClearanceFee(it)
if (items.value[index]) {
items.value[index].clearance_price = isNaN(clearance) ? 0 : Number(clearance.toFixed(2))
// 确保报关价货币单位与计算结果一致
if (items.value[index]) {
;(items.value[index] as any).clearance_price_currency =
it.clearance_price_currency || baseForm.settlement_currency || 'CNY'
}
}
2025-10-22 11:52:20 +08:00
// 自动计算预估退税额
const refund = calcTaxRefund(it)
if (items.value[index]) {
2025-10-22 11:52:20 +08:00
items.value[index].tax_refund = isNaN(refund) ? '0' : String(Number(refund.toFixed(2)))
;(items.value[index] as any).tax_refund_currency = 'CNY'
}
// 自动计算车价
const carPrice = calcCarPrice(it)
if (items.value[index]) {
items.value[index].car_price = isNaN(carPrice) ? 0 : Number(carPrice.toFixed(2))
;(items.value[index] as any).car_price_currency =
it.car_price_currency || baseForm.settlement_currency || 'CNY'
}
2025-09-18 09:55:10 +08:00
})
2025-10-22 11:52:20 +08:00
// 合计到岸价 = 所有报关价相加
2025-09-18 09:55:10 +08:00
const sum = (val || []).reduce((acc: number, cur: any) => {
const n =
2025-10-22 11:52:20 +08:00
cur?.clearance_price === undefined ||
cur?.clearance_price === null ||
cur?.clearance_price === ''
? 0
2025-10-22 11:52:20 +08:00
: Number(cur.clearance_price)
2025-09-18 09:55:10 +08:00
return acc + (isNaN(n) ? 0 : n)
}, 0)
2025-10-22 11:52:20 +08:00
baseForm.cif_total = Number(sum.toFixed(2))
2025-10-22 11:52:20 +08:00
// 同步合计币种为第一行报关价币种(若有)
2025-09-18 09:55:10 +08:00
if (items.value.length > 0) {
2025-10-22 11:52:20 +08:00
const c = items.value[0]?.clearance_price_currency
if (c) baseForm.cif_total_currency = c
2025-09-18 09:55:10 +08:00
}
},
{ deep: true }
)
2025-10-22 11:52:20 +08:00
// 监听车价货币单位变化,重新计算车价并更新货币名称快照
watch(
() => items.value.map((item: any) => item.car_price_currency),
(newCurrencies, oldCurrencies) => {
if (isPopulating.value) return
newCurrencies.forEach((currency: string, index: number) => {
if (currency && currency !== oldCurrencies?.[index]) {
const found = (currencyOptions.value || []).find((x: any) => x.currency_code === currency)
if (found && items.value[index]) {
;(items.value[index] as any).car_price_currency_snapshot = found.currency_name
// 重新计算车价
const carPrice = calcCarPrice(items.value[index])
items.value[index].car_price = isNaN(carPrice) ? 0 : Number(carPrice.toFixed(2))
}
}
})
},
{ deep: true }
)
// 处理用户编辑状态的方法
const handleUserEdit = (index: number) => {
// 标记用户正在编辑这一行
userEditing.value.add(index)
// 延迟清除编辑状态,给用户时间完成输入
setTimeout(() => {
userEditing.value.delete(index)
}, 1000)
}
2025-09-18 09:55:10 +08:00
// 初始化方法
const initializeForm = async () => {
await Promise.all([
getcustomer(),
loadCurrencyOptions(),
getgetChannel(),
getarea(),
getmodification(),
loadColorOptions(),
loadAccountOptions(),
loadPortOptions(),
loadDictOptions()
])
2025-09-18 09:55:10 +08:00
// 设置默认汇率
baseForm.contract_exchange_rate = getCurrencyRate('CNY')
2025-09-18 09:55:10 +08:00
// 设置预计发送日默认为当天
const today = new Date()
const year = today.getFullYear()
const month = String(today.getMonth() + 1).padStart(2, '0')
const day = String(today.getDate()).padStart(2, '0')
baseForm.biz_date = `${year}-${month}-${day}`
}
return {
// 数据
baseForm,
items,
paymentItems,
2025-10-22 11:52:20 +08:00
contractFiles,
piFiles,
waterBillFiles,
2025-09-18 09:55:10 +08:00
customerDialog,
vehicleDialog,
2025-09-18 09:55:10 +08:00
// 选项数据
channelOptions,
countryOptions,
modificationOptions,
currencyOptions,
accountOptions,
customerOptions,
portOptions,
portOptionsall,
wcolorOptions,
ncolorOptions,
transportOptions,
trade_termsOptions,
2025-09-18 09:55:10 +08:00
// 上传相关
actionImageUplode,
uploadHeaders,
2025-09-18 09:55:10 +08:00
// 方法
initializeForm,
populateFromDetail,
handleSubmit,
getCurrencyRate,
validateContractRate,
wrapMoney,
calcProfit,
calcClearanceFee,
calcTaxRefund,
2025-10-22 11:52:20 +08:00
handleUserEdit,
2025-09-18 09:55:10 +08:00
// 合同文件方法
isImageFile,
beforeContractUpload,
handleContractUpload,
removeContractFile,
2025-10-22 11:52:20 +08:00
downloadContractFile,
removePiFile,
downloadPiFile,
removeWaterBillFile,
downloadWaterBillFile,
2025-09-18 09:55:10 +08:00
// 订单项方法
copyRow,
addRowAfter,
removeRow,
newItem,
2025-09-18 09:55:10 +08:00
// 收款项方法
addPaymentItem,
removePaymentItem,
2025-10-22 11:52:20 +08:00
copyPaymentItem,
2025-09-18 09:55:10 +08:00
newPaymentItem,
2025-09-18 09:55:10 +08:00
// 客户选择方法
openCustomerDialog,
getCustomerList,
handleCustomerSizeChange,
handleCustomerCurrentChange,
chooseCustomer,
2025-09-18 09:55:10 +08:00
// 车辆选择方法
openVehicleDialog,
getVehicleList,
handleVehicleSizeChange,
handleVehicleCurrentChange,
chooseVehicle
}
}