feat: 创建运单/代发运单弹窗补齐基本信息与下单协议
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:
zzh 2026-10-20 11:23:15 +08:00
parent 6df8d8976d
commit a917216c3b
3 changed files with 474 additions and 55 deletions

View File

@ -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',

View File

@ -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': '商品明细',

View File

@ -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;