feat: 创建运单/代发运单弹窗补齐基本信息与下单协议
All checks were successful
Build and Deploy Front / deploy (push) Successful in 5m50s
All checks were successful
Build and Deploy Front / deploy (push) Successful in 5m50s
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
6df8d8976d
commit
a917216c3b
@ -1058,6 +1058,8 @@ export default {
|
|||||||
'order.platformStoreOrder.createLogistics.tip': 'Review recipient and line items for each order, then confirm the shipping product before submitting.',
|
'order.platformStoreOrder.createLogistics.tip': 'Review recipient and line items for each order, then confirm the shipping product before submitting.',
|
||||||
'order.platformStoreOrder.createLogistics.product': 'Shipping Product',
|
'order.platformStoreOrder.createLogistics.product': 'Shipping Product',
|
||||||
'order.platformStoreOrder.createLogistics.productRequired': 'Please select a shipping product',
|
'order.platformStoreOrder.createLogistics.productRequired': 'Please select a shipping product',
|
||||||
|
'order.platformStoreOrder.createLogistics.basicRequired':
|
||||||
|
'Please complete waybill basic info (warehouse, cargo type, weight, dimensions, inbound method, etc.)',
|
||||||
'order.platformStoreOrder.createLogistics.orderList': 'Selected Orders',
|
'order.platformStoreOrder.createLogistics.orderList': 'Selected Orders',
|
||||||
'order.platformStoreOrder.createLogistics.receiverTitle': 'Recipient Check',
|
'order.platformStoreOrder.createLogistics.receiverTitle': 'Recipient Check',
|
||||||
'order.platformStoreOrder.createLogistics.linesTitle': 'Line Items',
|
'order.platformStoreOrder.createLogistics.linesTitle': 'Line Items',
|
||||||
|
|||||||
@ -1047,6 +1047,7 @@ export default {
|
|||||||
'order.platformStoreOrder.createLogistics.tip': '请逐单核对收件人与商品明细,确认运输方式后再提交,避免下错单。',
|
'order.platformStoreOrder.createLogistics.tip': '请逐单核对收件人与商品明细,确认运输方式后再提交,避免下错单。',
|
||||||
'order.platformStoreOrder.createLogistics.product': '运输方式',
|
'order.platformStoreOrder.createLogistics.product': '运输方式',
|
||||||
'order.platformStoreOrder.createLogistics.productRequired': '请选择运输方式',
|
'order.platformStoreOrder.createLogistics.productRequired': '请选择运输方式',
|
||||||
|
'order.platformStoreOrder.createLogistics.basicRequired': '请完整填写运单基本信息(仓库、货物属性、实重、长宽高、到仓方式等)',
|
||||||
'order.platformStoreOrder.createLogistics.orderList': '待创建清单',
|
'order.platformStoreOrder.createLogistics.orderList': '待创建清单',
|
||||||
'order.platformStoreOrder.createLogistics.receiverTitle': '收件人校对',
|
'order.platformStoreOrder.createLogistics.receiverTitle': '收件人校对',
|
||||||
'order.platformStoreOrder.createLogistics.linesTitle': '商品明细',
|
'order.platformStoreOrder.createLogistics.linesTitle': '商品明细',
|
||||||
|
|||||||
@ -13,13 +13,33 @@ import {
|
|||||||
postOrderStockBindInventoryCandidates,
|
postOrderStockBindInventoryCandidates,
|
||||||
postOrderStockBindParcelCandidates,
|
postOrderStockBindParcelCandidates,
|
||||||
} from '@/api/order'
|
} from '@/api/order'
|
||||||
|
import { getSenderAddressList } from '@/api/order/senderAddress'
|
||||||
|
import { getExportQualificationList } from '@/api/customer/exportQualification'
|
||||||
|
import { getDictListAll } from '@/api/system/dict'
|
||||||
import { resolveOmsAssetUrl } from '@/api/oms/common'
|
import { resolveOmsAssetUrl } from '@/api/oms/common'
|
||||||
import SlidingTabs from '@/components/SlidingTabs/index.vue'
|
import SlidingTabs from '@/components/SlidingTabs/index.vue'
|
||||||
import { $baseMessage } from '@/utils'
|
import { $baseMessage } from '@/utils'
|
||||||
|
import { normalizeCargoAttributeCode } from '@/utils/cargoAttribute'
|
||||||
import { normalizeListRow, pickField } from '@/utils/warehouseListMapper'
|
import { normalizeListRow, pickField } from '@/utils/warehouseListMapper'
|
||||||
|
|
||||||
import type { CreateLogisticsMode } from './constants'
|
import type { CreateLogisticsMode } from './constants'
|
||||||
|
|
||||||
|
type SelectOption = { label: string; value: string }
|
||||||
|
type QualOption = { id: number; label: string; isDefault: boolean }
|
||||||
|
|
||||||
|
const SAMPLE_EXPORT_CUSTOMS_MODE_CODE = '9610Simple'
|
||||||
|
const EXPORT_CUSTOMS_MODE_9610_SUMMARY = '9610Summary'
|
||||||
|
const AGREEMENT_TEXT = `《物流下单用户协议》及《物流服务赔偿协议》
|
||||||
|
|
||||||
|
一、服务说明
|
||||||
|
您确认通过本系统创建的运单/代发订单,将按所选运输方式、仓库与件重尺计费并履约。
|
||||||
|
|
||||||
|
二、责任与理赔
|
||||||
|
请以正式协议文本为准;勾选即视为已阅读并同意全部条款。
|
||||||
|
|
||||||
|
三、勾选确认
|
||||||
|
本人已仔细阅读并充分理解全部条款,自愿接受本协议所有约定。`
|
||||||
|
|
||||||
export type { CreateLogisticsMode }
|
export type { CreateLogisticsMode }
|
||||||
|
|
||||||
export type BindDraftItem = {
|
export type BindDraftItem = {
|
||||||
@ -43,6 +63,8 @@ type OrderDraft = {
|
|||||||
customerId?: number
|
customerId?: number
|
||||||
warehouseId?: number
|
warehouseId?: number
|
||||||
platformOrderNo: string
|
platformOrderNo: string
|
||||||
|
/** 可编辑客户单号,默认平台订单号 */
|
||||||
|
clientNo: string
|
||||||
orderCountry: string
|
orderCountry: string
|
||||||
recipientName: string
|
recipientName: string
|
||||||
recipientPhone: string
|
recipientPhone: string
|
||||||
@ -103,6 +125,28 @@ const batchMatchDeclaration = ref(false)
|
|||||||
const batchQtyPerOrder = ref(1)
|
const batchQtyPerOrder = ref(1)
|
||||||
let searchTimer: ReturnType<typeof setTimeout> | null = null
|
let searchTimer: ReturnType<typeof setTimeout> | null = null
|
||||||
|
|
||||||
|
/** 运单基本信息(对齐下单页) */
|
||||||
|
const cargoAttribute = ref('general')
|
||||||
|
const actualWeight = ref<number | undefined>(undefined)
|
||||||
|
const quantity = ref<number>(1)
|
||||||
|
const lengthCm = ref<number | undefined>(10)
|
||||||
|
const widthCm = ref<number | undefined>(10)
|
||||||
|
const heightCm = ref<number | undefined>(10)
|
||||||
|
const exportCustomsMode = ref(SAMPLE_EXPORT_CUSTOMS_MODE_CODE)
|
||||||
|
const exportQualificationId = ref<number | undefined>()
|
||||||
|
const platformCode = ref<string>()
|
||||||
|
const inboundMethod = ref<'selfShip' | 'doorPickup'>('selfShip')
|
||||||
|
const senderAddressId = ref<number | undefined>()
|
||||||
|
const expectPickupTime = ref<string>()
|
||||||
|
const agreementChecked = ref(false)
|
||||||
|
const agreementModalVisible = ref(false)
|
||||||
|
const dictLoading = ref(false)
|
||||||
|
const cargoAttributeOptions = ref<SelectOption[]>([])
|
||||||
|
const exportCustomsModeOptions = ref<SelectOption[]>([])
|
||||||
|
const ecommercePlatformOptions = ref<SelectOption[]>([])
|
||||||
|
const exportQualificationOptions = ref<QualOption[]>([])
|
||||||
|
const pickupAddressOptions = ref<Array<{ label: string; value: number }>>([])
|
||||||
|
|
||||||
const modalVisible = computed({
|
const modalVisible = computed({
|
||||||
get: () => props.visible,
|
get: () => props.visible,
|
||||||
set: (v: boolean) => emit('update:visible', v),
|
set: (v: boolean) => emit('update:visible', v),
|
||||||
@ -167,17 +211,172 @@ const warehouseLabel = computed(() => {
|
|||||||
return opt?.label || '-'
|
return opt?.label || '-'
|
||||||
})
|
})
|
||||||
|
|
||||||
const canSubmit = computed(() => {
|
const needExportQualification = computed(
|
||||||
|
() =>
|
||||||
|
String(exportCustomsMode.value ?? '').trim().toLowerCase() ===
|
||||||
|
EXPORT_CUSTOMS_MODE_9610_SUMMARY.toLowerCase(),
|
||||||
|
)
|
||||||
|
|
||||||
|
const basicInfoReady = computed(() => {
|
||||||
if (!productCode.value) return false
|
if (!productCode.value) return false
|
||||||
|
if (!warehouseId.value) return false
|
||||||
|
if (!String(cargoAttribute.value || '').trim()) return false
|
||||||
|
if (!(Number(actualWeight.value) > 0)) return false
|
||||||
|
if (!(Number(quantity.value) > 0)) return false
|
||||||
|
if (!(Number(lengthCm.value) > 0) || !(Number(widthCm.value) > 0) || !(Number(heightCm.value) > 0)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (needExportQualification.value) {
|
||||||
|
if (!(Number(exportQualificationId.value) > 0)) return false
|
||||||
|
if (!String(platformCode.value || '').trim()) return false
|
||||||
|
}
|
||||||
|
if (inboundMethod.value === 'doorPickup') {
|
||||||
|
if (!(Number(senderAddressId.value) > 0)) return false
|
||||||
|
if (!expectPickupTime.value) return false
|
||||||
|
}
|
||||||
|
if (drafts.value.some((d) => !String(d.clientNo || '').trim())) return false
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
const canSubmit = computed(() => {
|
||||||
|
if (!basicInfoReady.value) return false
|
||||||
|
if (!agreementChecked.value) return false
|
||||||
if (drafts.value.length === 0) return false
|
if (drafts.value.length === 0) return false
|
||||||
if (drafts.value.some((d) => d.receiverMissing || d.linesMissing)) return false
|
if (drafts.value.some((d) => d.receiverMissing || d.linesMissing)) return false
|
||||||
if (isFulfill.value) {
|
if (isFulfill.value) {
|
||||||
if (!warehouseId.value) return false
|
|
||||||
if (drafts.value.some((d) => d.bindItems.length === 0)) return false
|
if (drafts.value.some((d) => d.bindItems.length === 0)) return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
function resolveExportCustomsMode9610Simple() {
|
||||||
|
const opts = exportCustomsModeOptions.value
|
||||||
|
const byCode = opts.find(
|
||||||
|
(o) => o.value === SAMPLE_EXPORT_CUSTOMS_MODE_CODE || o.value.toLowerCase() === '9610simple',
|
||||||
|
)
|
||||||
|
if (byCode) return byCode.value
|
||||||
|
const byLabel = opts.find((o) => /9610.*简易|简易.*9610/i.test(o.label))
|
||||||
|
if (byLabel) return byLabel.value
|
||||||
|
return SAMPLE_EXPORT_CUSTOMS_MODE_CODE
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapDictOptions(rows: any[]): SelectOption[] {
|
||||||
|
return (rows || [])
|
||||||
|
.map((row) => {
|
||||||
|
const value = String(row?.dictCode ?? row?.DictCode ?? '').trim()
|
||||||
|
const label = String(row?.dictName ?? row?.DictName ?? value).trim()
|
||||||
|
if (!value) return null
|
||||||
|
return { label: label || value, value }
|
||||||
|
})
|
||||||
|
.filter((x): x is SelectOption => !!x)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadBasicDicts() {
|
||||||
|
dictLoading.value = true
|
||||||
|
try {
|
||||||
|
const [cargoRows, exportRows, platformRows] = await Promise.all([
|
||||||
|
getDictListAll({ dictType: 'cargo_attribute' }),
|
||||||
|
getDictListAll({ dictType: 'export_customs_mode' }),
|
||||||
|
getDictListAll({ dictType: 'e-commerce_platform' }),
|
||||||
|
])
|
||||||
|
cargoAttributeOptions.value = mapDictOptions(cargoRows as any[])
|
||||||
|
exportCustomsModeOptions.value = mapDictOptions(exportRows as any[])
|
||||||
|
ecommercePlatformOptions.value = mapDictOptions(platformRows as any[])
|
||||||
|
if (!cargoAttribute.value && cargoAttributeOptions.value[0]) {
|
||||||
|
cargoAttribute.value = normalizeCargoAttributeCode(cargoAttributeOptions.value[0].value)
|
||||||
|
} else {
|
||||||
|
cargoAttribute.value = normalizeCargoAttributeCode(cargoAttribute.value)
|
||||||
|
}
|
||||||
|
exportCustomsMode.value = resolveExportCustomsMode9610Simple()
|
||||||
|
} catch {
|
||||||
|
cargoAttributeOptions.value = [
|
||||||
|
{ label: '普货', value: 'general' },
|
||||||
|
{ label: '带电', value: 'electronics' },
|
||||||
|
{ label: '敏感', value: 'sensitive' },
|
||||||
|
]
|
||||||
|
exportCustomsModeOptions.value = [
|
||||||
|
{ label: '9610 简易申报', value: SAMPLE_EXPORT_CUSTOMS_MODE_CODE },
|
||||||
|
{ label: '9610 汇总申报', value: EXPORT_CUSTOMS_MODE_9610_SUMMARY },
|
||||||
|
]
|
||||||
|
} finally {
|
||||||
|
dictLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadExportQualifications() {
|
||||||
|
const customerId = resolveCandidatesCustomerId()
|
||||||
|
try {
|
||||||
|
const res = await getExportQualificationList({
|
||||||
|
page: 1,
|
||||||
|
pageSize: 100,
|
||||||
|
customerId,
|
||||||
|
})
|
||||||
|
exportQualificationOptions.value = (res.items || [])
|
||||||
|
.map((item: Record<string, unknown>) => {
|
||||||
|
const id = Number(pickField(item, 'id', 'Id') ?? 0)
|
||||||
|
if (!id) return null
|
||||||
|
const companyName = String(pickField(item, 'companyName', 'CompanyName') ?? '').trim()
|
||||||
|
const creditCode = String(pickField(item, 'socialCreditCode', 'SocialCreditCode') ?? '').trim()
|
||||||
|
const isDefault = Boolean(pickField(item, 'isDefault', 'IsDefault'))
|
||||||
|
const base = companyName || creditCode || `#${id}`
|
||||||
|
return { id, label: isDefault ? `${base}(默认)` : base, isDefault }
|
||||||
|
})
|
||||||
|
.filter((x): x is QualOption => !!x)
|
||||||
|
if (!exportQualificationId.value) {
|
||||||
|
const def =
|
||||||
|
exportQualificationOptions.value.find((x) => x.isDefault) || exportQualificationOptions.value[0]
|
||||||
|
exportQualificationId.value = def?.id
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
exportQualificationOptions.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadPickupAddresses() {
|
||||||
|
const customerId = resolveCandidatesCustomerId()
|
||||||
|
try {
|
||||||
|
const res = await getSenderAddressList({
|
||||||
|
page: 1,
|
||||||
|
pageSize: 200,
|
||||||
|
addressType: 'pickup',
|
||||||
|
customerId,
|
||||||
|
})
|
||||||
|
pickupAddressOptions.value = (res.items || [])
|
||||||
|
.map((item: Record<string, unknown>) => {
|
||||||
|
const id = Number(pickField(item, 'id', 'Id') ?? 0)
|
||||||
|
if (!id) return null
|
||||||
|
const name = String(pickField(item, 'contactName', 'ContactName', 'name', 'Name') ?? '').trim()
|
||||||
|
const phone = String(pickField(item, 'phone', 'Phone') ?? '').trim()
|
||||||
|
const label = [name, phone].filter(Boolean).join(' · ') || `#${id}`
|
||||||
|
return { label, value: id }
|
||||||
|
})
|
||||||
|
.filter((x): x is { label: string; value: number } => !!x)
|
||||||
|
} catch {
|
||||||
|
pickupAddressOptions.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function openAgreementModal() {
|
||||||
|
agreementModalVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildParcelBasicPayload() {
|
||||||
|
return {
|
||||||
|
cargoAttribute: normalizeCargoAttributeCode(cargoAttribute.value),
|
||||||
|
actualWeight: Number(actualWeight.value),
|
||||||
|
quantity: Number(quantity.value),
|
||||||
|
length: Number(lengthCm.value),
|
||||||
|
width: Number(widthCm.value),
|
||||||
|
height: Number(heightCm.value),
|
||||||
|
exportCustomsMode: String(exportCustomsMode.value || '').trim() || resolveExportCustomsMode9610Simple(),
|
||||||
|
exportQualificationId: needExportQualification.value ? exportQualificationId.value : undefined,
|
||||||
|
platformCode: needExportQualification.value ? platformCode.value : undefined,
|
||||||
|
inboundMethod: inboundMethod.value,
|
||||||
|
senderAddressId: inboundMethod.value === 'doorPickup' ? senderAddressId.value : undefined,
|
||||||
|
expectPickupTime: inboundMethod.value === 'doorPickup' ? expectPickupTime.value : undefined,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function close() {
|
function close() {
|
||||||
emit('update:visible', false)
|
emit('update:visible', false)
|
||||||
}
|
}
|
||||||
@ -208,11 +407,13 @@ function mapDetail(raw: Record<string, unknown>): OrderDraft {
|
|||||||
const shipLines = lines
|
const shipLines = lines
|
||||||
const customerIdRaw = Number(pickField(row, 'customerId', 'CustomerId') ?? 0)
|
const customerIdRaw = Number(pickField(row, 'customerId', 'CustomerId') ?? 0)
|
||||||
const warehouseIdRaw = Number(pickField(row, 'warehouseId', 'WarehouseId') ?? 0)
|
const warehouseIdRaw = Number(pickField(row, 'warehouseId', 'WarehouseId') ?? 0)
|
||||||
|
const platformOrderNo = String(pickField(row, 'platformOrderNo', 'PlatformOrderNo') ?? '')
|
||||||
return {
|
return {
|
||||||
storeOrderId: Number(pickField(row, 'id', 'Id')),
|
storeOrderId: Number(pickField(row, 'id', 'Id')),
|
||||||
customerId: Number.isFinite(customerIdRaw) && customerIdRaw > 0 ? customerIdRaw : undefined,
|
customerId: Number.isFinite(customerIdRaw) && customerIdRaw > 0 ? customerIdRaw : undefined,
|
||||||
warehouseId: Number.isFinite(warehouseIdRaw) && warehouseIdRaw > 0 ? warehouseIdRaw : undefined,
|
warehouseId: Number.isFinite(warehouseIdRaw) && warehouseIdRaw > 0 ? warehouseIdRaw : undefined,
|
||||||
platformOrderNo: String(pickField(row, 'platformOrderNo', 'PlatformOrderNo') ?? ''),
|
platformOrderNo,
|
||||||
|
clientNo: platformOrderNo,
|
||||||
orderCountry: String(pickField(row, 'orderCountry', 'OrderCountry') ?? ''),
|
orderCountry: String(pickField(row, 'orderCountry', 'OrderCountry') ?? ''),
|
||||||
recipientName,
|
recipientName,
|
||||||
recipientPhone: String(pickField(row, 'recipientPhone', 'RecipientPhone') ?? ''),
|
recipientPhone: String(pickField(row, 'recipientPhone', 'RecipientPhone') ?? ''),
|
||||||
@ -525,6 +726,10 @@ function buildBindPayload(items: BindDraftItem[]) {
|
|||||||
|
|
||||||
async function handleSubmit(): Promise<boolean> {
|
async function handleSubmit(): Promise<boolean> {
|
||||||
if (!canSubmit.value) {
|
if (!canSubmit.value) {
|
||||||
|
if (!agreementChecked.value) {
|
||||||
|
$baseMessage(t('order.placeOrder.agreement.required'), 'warning')
|
||||||
|
return false
|
||||||
|
}
|
||||||
if (!productCode.value) {
|
if (!productCode.value) {
|
||||||
$baseMessage(
|
$baseMessage(
|
||||||
isFulfillWms.value
|
isFulfillWms.value
|
||||||
@ -534,10 +739,14 @@ async function handleSubmit(): Promise<boolean> {
|
|||||||
)
|
)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (isFulfill.value && !warehouseId.value) {
|
if (!warehouseId.value) {
|
||||||
$baseMessage(t('order.platformStoreOrder.message.warehouseRequired'), 'warning')
|
$baseMessage(t('order.platformStoreOrder.message.warehouseRequired'), 'warning')
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if (!basicInfoReady.value) {
|
||||||
|
$baseMessage(t('order.platformStoreOrder.createLogistics.basicRequired'), 'warning')
|
||||||
|
return false
|
||||||
|
}
|
||||||
const bad = drafts.value.find(
|
const bad = drafts.value.find(
|
||||||
(d) => d.receiverMissing || d.linesMissing || (isFulfill.value && d.bindItems.length === 0),
|
(d) => d.receiverMissing || d.linesMissing || (isFulfill.value && d.bindItems.length === 0),
|
||||||
)
|
)
|
||||||
@ -579,12 +788,14 @@ async function handleSubmit(): Promise<boolean> {
|
|||||||
try {
|
try {
|
||||||
const items = drafts.value.map((d) => ({
|
const items = drafts.value.map((d) => ({
|
||||||
storeOrderId: d.storeOrderId,
|
storeOrderId: d.storeOrderId,
|
||||||
|
clientNo: String(d.clientNo || '').trim() || undefined,
|
||||||
bindItems: isFulfill.value ? buildBindPayload(d.bindItems) : undefined,
|
bindItems: isFulfill.value ? buildBindPayload(d.bindItems) : undefined,
|
||||||
}))
|
}))
|
||||||
const body = {
|
const body = {
|
||||||
productCode: productCode.value,
|
productCode: productCode.value,
|
||||||
warehouseId: warehouseId.value,
|
warehouseId: warehouseId.value,
|
||||||
customerId: props.customerId,
|
customerId: props.customerId,
|
||||||
|
parcelBasic: buildParcelBasicPayload(),
|
||||||
items,
|
items,
|
||||||
}
|
}
|
||||||
const result = isFulfillWms.value
|
const result = isFulfillWms.value
|
||||||
@ -636,7 +847,29 @@ watch(
|
|||||||
batchMode.value = props.storeOrderIds.length > 1
|
batchMode.value = props.storeOrderIds.length > 1
|
||||||
batchMatchDeclaration.value = false
|
batchMatchDeclaration.value = false
|
||||||
batchQtyPerOrder.value = 1
|
batchQtyPerOrder.value = 1
|
||||||
|
cargoAttribute.value = 'general'
|
||||||
|
actualWeight.value = undefined
|
||||||
|
quantity.value = 1
|
||||||
|
lengthCm.value = 10
|
||||||
|
widthCm.value = 10
|
||||||
|
heightCm.value = 10
|
||||||
|
exportCustomsMode.value = SAMPLE_EXPORT_CUSTOMS_MODE_CODE
|
||||||
|
exportQualificationId.value = undefined
|
||||||
|
platformCode.value = undefined
|
||||||
|
inboundMethod.value = 'selfShip'
|
||||||
|
senderAddressId.value = undefined
|
||||||
|
expectPickupTime.value = undefined
|
||||||
|
agreementChecked.value = false
|
||||||
|
await loadBasicDicts()
|
||||||
await loadDrafts()
|
await loadDrafts()
|
||||||
|
// 实重默认按订单申报行估算(取首单)
|
||||||
|
const first = drafts.value[0]
|
||||||
|
if (first && !(Number(actualWeight.value) > 0)) {
|
||||||
|
const est = first.lines.reduce((s, x) => s + Math.max(1, Number(x.quantity) || 0) * 0.01, 0)
|
||||||
|
actualWeight.value = Number(est.toFixed(3)) || 0.01
|
||||||
|
}
|
||||||
|
await loadExportQualifications()
|
||||||
|
await loadPickupAddresses()
|
||||||
if (isFulfill.value) await loadCandidates()
|
if (isFulfill.value) await loadCandidates()
|
||||||
},
|
},
|
||||||
{ immediate: true },
|
{ immediate: true },
|
||||||
@ -652,14 +885,24 @@ watch(leftTab, () => {
|
|||||||
if (!props.visible || !isFulfill.value) return
|
if (!props.visible || !isFulfill.value) return
|
||||||
void loadCandidates()
|
void loadCandidates()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
watch(needExportQualification, (need) => {
|
||||||
|
if (!props.visible || !need) return
|
||||||
|
void loadExportQualifications()
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(inboundMethod, (method) => {
|
||||||
|
if (!props.visible || method !== 'doorPickup') return
|
||||||
|
void loadPickupAddresses()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<a-modal
|
<a-modal
|
||||||
v-model:visible="modalVisible"
|
v-model:visible="modalVisible"
|
||||||
:title="title"
|
:title="title"
|
||||||
:width="isFulfill ? '1360px' : 960"
|
:width="isFulfill ? '1360px' : '1080px'"
|
||||||
:top="isFulfill ? '20px' : '10vh'"
|
:top="isFulfill ? '20px' : '6vh'"
|
||||||
:ok-loading="submitting"
|
:ok-loading="submitting"
|
||||||
:ok-text="
|
:ok-text="
|
||||||
isFulfillWms
|
isFulfillWms
|
||||||
@ -670,7 +913,11 @@ watch(leftTab, () => {
|
|||||||
"
|
"
|
||||||
:ok-button-props="{ disabled: !canSubmit || loading }"
|
:ok-button-props="{ disabled: !canSubmit || loading }"
|
||||||
unmount-on-close
|
unmount-on-close
|
||||||
:body-style="isFulfill ? { padding: '12px 16px', maxHeight: 'calc(100vh - 140px)', overflow: 'hidden' } : undefined"
|
:body-style="{
|
||||||
|
padding: '12px 16px',
|
||||||
|
maxHeight: isFulfill ? 'calc(100vh - 140px)' : 'calc(100vh - 160px)',
|
||||||
|
overflow: isFulfill ? 'hidden' : 'auto',
|
||||||
|
}"
|
||||||
:on-before-ok="handleSubmit"
|
:on-before-ok="handleSubmit"
|
||||||
@cancel="close"
|
@cancel="close"
|
||||||
>
|
>
|
||||||
@ -686,49 +933,146 @@ watch(leftTab, () => {
|
|||||||
}}
|
}}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="psc-config">
|
<section class="psc-basic">
|
||||||
<div class="psc-config__item">
|
<header class="psc-basic__head">
|
||||||
<span class="psc-config__label">
|
<strong>{{ t('order.placeOrder.single.stepBasic') }}</strong>
|
||||||
|
<span class="psc-config__meta">
|
||||||
{{
|
{{
|
||||||
isFulfillWms
|
t('order.platformStoreOrder.createLogistics.readyMeta', {
|
||||||
? t('order.platformStoreOrder.createFulfillWms.product')
|
total: drafts.length,
|
||||||
: t('order.platformStoreOrder.createLogistics.product')
|
ready: readyCount,
|
||||||
|
})
|
||||||
}}
|
}}
|
||||||
</span>
|
</span>
|
||||||
<a-select
|
</header>
|
||||||
v-model="productCode"
|
<div class="psc-basic__grid">
|
||||||
allow-search
|
<div class="psc-basic__item">
|
||||||
:options="productOptions"
|
<span class="psc-basic__label">
|
||||||
:placeholder="
|
{{
|
||||||
isFulfillWms
|
isFulfillWms
|
||||||
? t('order.platformStoreOrder.createFulfillWms.product')
|
? t('order.platformStoreOrder.createFulfillWms.product')
|
||||||
: t('order.platformStoreOrder.form.product')
|
: t('order.platformStoreOrder.createLogistics.product')
|
||||||
"
|
}}
|
||||||
style="width: 260px"
|
<em>*</em>
|
||||||
/>
|
</span>
|
||||||
|
<a-select
|
||||||
|
v-model="productCode"
|
||||||
|
allow-search
|
||||||
|
:options="productOptions"
|
||||||
|
:loading="dictLoading"
|
||||||
|
:placeholder="t('order.platformStoreOrder.form.product')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="psc-basic__item">
|
||||||
|
<span class="psc-basic__label">客户单号 <em>*</em></span>
|
||||||
|
<a-input
|
||||||
|
v-if="activeDraft"
|
||||||
|
v-model="activeDraft.clientNo"
|
||||||
|
:placeholder="activeDraft.platformOrderNo || '客户单号'"
|
||||||
|
/>
|
||||||
|
<a-input v-else disabled placeholder="—" />
|
||||||
|
</div>
|
||||||
|
<div class="psc-basic__item">
|
||||||
|
<span class="psc-basic__label">货物属性 <em>*</em></span>
|
||||||
|
<a-select
|
||||||
|
v-model="cargoAttribute"
|
||||||
|
allow-search
|
||||||
|
:options="cargoAttributeOptions"
|
||||||
|
:loading="dictLoading"
|
||||||
|
placeholder="请选择"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="psc-basic__item">
|
||||||
|
<span class="psc-basic__label">仓库 <em>*</em></span>
|
||||||
|
<a-select
|
||||||
|
v-model="warehouseId"
|
||||||
|
allow-search
|
||||||
|
:options="warehouseOptions"
|
||||||
|
:placeholder="t('warehouse.common.warehouse')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="psc-basic__item">
|
||||||
|
<span class="psc-basic__label">实重(kg) <em>*</em></span>
|
||||||
|
<a-input-number v-model="actualWeight" :min="0.01" :precision="3" hide-button style="width: 100%" />
|
||||||
|
</div>
|
||||||
|
<div class="psc-basic__item">
|
||||||
|
<span class="psc-basic__label">包裹数量 <em>*</em></span>
|
||||||
|
<a-input-number v-model="quantity" :min="1" :precision="0" hide-button style="width: 100%" />
|
||||||
|
</div>
|
||||||
|
<div class="psc-basic__item">
|
||||||
|
<span class="psc-basic__label">出口报关方式</span>
|
||||||
|
<a-select
|
||||||
|
v-model="exportCustomsMode"
|
||||||
|
allow-search
|
||||||
|
:options="exportCustomsModeOptions"
|
||||||
|
:loading="dictLoading"
|
||||||
|
placeholder="请选择"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-if="needExportQualification" class="psc-basic__item">
|
||||||
|
<span class="psc-basic__label">出口资质备案 <em>*</em></span>
|
||||||
|
<a-select
|
||||||
|
v-model="exportQualificationId"
|
||||||
|
allow-search
|
||||||
|
:options="exportQualificationOptions.map((x) => ({ label: x.label, value: x.id }))"
|
||||||
|
placeholder="请选择"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-if="needExportQualification" class="psc-basic__item">
|
||||||
|
<span class="psc-basic__label">电商平台 <em>*</em></span>
|
||||||
|
<a-select
|
||||||
|
v-model="platformCode"
|
||||||
|
allow-search
|
||||||
|
:options="ecommercePlatformOptions"
|
||||||
|
placeholder="请选择"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="psc-basic__item psc-basic__item--dims">
|
||||||
|
<span class="psc-basic__label">长宽高(cm) <em>*</em></span>
|
||||||
|
<div class="psc-dims">
|
||||||
|
<a-input-number v-model="lengthCm" :min="0.1" :precision="1" hide-button placeholder="长" />
|
||||||
|
<span>×</span>
|
||||||
|
<a-input-number v-model="widthCm" :min="0.1" :precision="1" hide-button placeholder="宽" />
|
||||||
|
<span>×</span>
|
||||||
|
<a-input-number v-model="heightCm" :min="0.1" :precision="1" hide-button placeholder="高" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="psc-basic__item psc-basic__item--inbound">
|
||||||
|
<span class="psc-basic__label">到仓方式 <em>*</em></span>
|
||||||
|
<a-radio-group v-model="inboundMethod" type="button" size="small">
|
||||||
|
<a-radio value="selfShip">自寄到仓</a-radio>
|
||||||
|
<a-radio value="doorPickup">上门揽收</a-radio>
|
||||||
|
</a-radio-group>
|
||||||
|
</div>
|
||||||
|
<template v-if="inboundMethod === 'doorPickup'">
|
||||||
|
<div class="psc-basic__item">
|
||||||
|
<span class="psc-basic__label">揽收地址 <em>*</em></span>
|
||||||
|
<a-select
|
||||||
|
v-model="senderAddressId"
|
||||||
|
allow-search
|
||||||
|
:options="pickupAddressOptions"
|
||||||
|
placeholder="请选择揽收地址"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="psc-basic__item">
|
||||||
|
<span class="psc-basic__label">期望揽收时间 <em>*</em></span>
|
||||||
|
<a-date-picker
|
||||||
|
v-model="expectPickupTime"
|
||||||
|
show-time
|
||||||
|
format="YYYY-MM-DD HH:mm"
|
||||||
|
value-format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div class="psc-config__item">
|
<div class="psc-agreement">
|
||||||
<span class="psc-config__label">
|
<a-checkbox v-model="agreementChecked" />
|
||||||
{{ t('warehouse.common.warehouse') }}
|
<span class="psc-agreement__link" @click="openAgreementModal">
|
||||||
<em v-if="isFulfill">*</em>
|
{{ t('order.placeOrder.agreement.label') }}
|
||||||
</span>
|
</span>
|
||||||
<a-select
|
|
||||||
v-model="warehouseId"
|
|
||||||
allow-clear
|
|
||||||
:options="warehouseOptions"
|
|
||||||
:placeholder="t('warehouse.common.warehouse')"
|
|
||||||
style="width: 200px"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="psc-config__meta">
|
</section>
|
||||||
{{
|
|
||||||
t('order.platformStoreOrder.createLogistics.readyMeta', {
|
|
||||||
total: drafts.length,
|
|
||||||
ready: readyCount,
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="showBatchBar" class="psc-batch-bar">
|
<div v-if="showBatchBar" class="psc-batch-bar">
|
||||||
<div class="psc-batch-bar__left">
|
<div class="psc-batch-bar__left">
|
||||||
@ -1067,6 +1411,16 @@ watch(leftTab, () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
|
|
||||||
|
<a-modal
|
||||||
|
v-model:visible="agreementModalVisible"
|
||||||
|
:title="t('order.placeOrder.agreement.label')"
|
||||||
|
:footer="false"
|
||||||
|
:width="560"
|
||||||
|
unmount-on-close
|
||||||
|
>
|
||||||
|
<pre class="psc-agreement-body">{{ AGREEMENT_TEXT }}</pre>
|
||||||
|
</a-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
@ -1094,28 +1448,42 @@ watch(leftTab, () => {
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.psc-config {
|
.psc-basic {
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
align-items: center;
|
|
||||||
gap: 12px 16px;
|
|
||||||
padding: 10px 12px;
|
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
|
padding: 12px;
|
||||||
background: var(--color-fill-2);
|
background: var(--color-fill-2);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.psc-config__item {
|
.psc-basic__head {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 12px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
strong {
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--color-text-1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.psc-config__label {
|
.psc-basic__grid {
|
||||||
font-size: 13px;
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
|
gap: 10px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.psc-basic__item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.psc-basic__label {
|
||||||
|
font-size: 12px;
|
||||||
color: var(--color-text-2);
|
color: var(--color-text-2);
|
||||||
white-space: nowrap;
|
|
||||||
|
|
||||||
em {
|
em {
|
||||||
color: #f53f3f;
|
color: #f53f3f;
|
||||||
@ -1124,6 +1492,54 @@ watch(leftTab, () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.psc-basic__item--dims,
|
||||||
|
.psc-basic__item--inbound {
|
||||||
|
grid-column: span 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.psc-dims {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
|
||||||
|
.arco-input-number {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.psc-agreement {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 12px;
|
||||||
|
padding-top: 10px;
|
||||||
|
border-top: 1px solid var(--color-border-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.psc-agreement__link {
|
||||||
|
color: var(--app-primary-color);
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 13px;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.psc-agreement-body {
|
||||||
|
margin: 0;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-word;
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.7;
|
||||||
|
color: var(--color-text-2);
|
||||||
|
max-height: 50vh;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.psc-config__meta {
|
.psc-config__meta {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user