diff --git a/src/modules/first-leg/store/firstLegOrders.test.mjs b/src/modules/first-leg/store/firstLegOrders.test.mjs index 13105a9..3869d02 100644 --- a/src/modules/first-leg/store/firstLegOrders.test.mjs +++ b/src/modules/first-leg/store/firstLegOrders.test.mjs @@ -24,7 +24,7 @@ test('maps transport and status fields instead of treating container mode as tra test('uses real API data without falling back to demo warehouses or orders', () => { assert.match(storeSource, /orders\.value = \[\]/) assert.match(storeSource, /warehouses\.value = \[\]/) - assert.match(storeSource, /Promise\.all\(\[getMerchantFirstLegOrders\(\), getMerchantWarehouses\(\)\]\)/) + assert.match(storeSource, /Promise\.allSettled\(\[getMerchantFirstLegOrders\(\), getMerchantWarehouses\(\)\]\)/) assert.doesNotMatch(storeSource, /DEMO_PLATFORM_RECEIVING_WAREHOUSES|initialOrders/) }) @@ -35,6 +35,13 @@ test('sends backend-required channel, customs, destination and cargo fields', () assert.match(storeSource, /containerMode: draft\.transportMode === 'FCL' \? 'FULL_CONTAINER'/) assert.match(storeSource, /customerId: draft\.shipperPartyId/) assert.match(storeSource, /customerName: customerName\.trim\(\) \|\| draft\.shipperPartySnapshot\.name/) + assert.match(storeSource, /destinationWarehouseId: draft\.destinationType === 'PRIVATE' \? undefined : draft\.destinationWarehouseId/) +}) + +test('separates domestic receiving warehouses from public destinations', () => { + assert.match(storeSource, /warehouse\.warehouseType !== 'DOMESTIC'/) + assert.match(storeSource, /item\.warehouseType === draft\.destinationType/) + assert.match(storeSource, /draft\.amazonWarehouseCode = draft\.destinationType === 'FBA' \? destinationWarehouse\.code : ''/) }) test('only blocks submission when a known company audit status is not active', () => { @@ -67,6 +74,7 @@ test('maps backend warehouse snapshots and fulfillment courier fields', () => { test('keeps failed order loads distinct from empty results', () => { assert.match(storeSource, /const loadError = ref\(''\)/) - assert.match(storeSource, /loadError\.value = error instanceof Error/) - assert.match(storeSource, /return \{ orders, warehouses, loading, loadedFromApi, loadError/) + assert.match(storeSource, /orderResult\.reason instanceof Error/) + assert.match(storeSource, /warehouseLoadError/) + assert.match(storeSource, /return \{ orders, warehouses, loading, loadedFromApi, loadError, warehouseLoadError/) }) diff --git a/src/modules/first-leg/store/firstLegOrders.ts b/src/modules/first-leg/store/firstLegOrders.ts index 6418cb4..edcb366 100644 --- a/src/modules/first-leg/store/firstLegOrders.ts +++ b/src/modules/first-leg/store/firstLegOrders.ts @@ -21,6 +21,12 @@ import type { FirstLegPartySnapshot } from '@/modules/first-leg/data/firstLegPar import type { FirstLegOrder, FirstLegOrderDraft, FirstLegOrderTimelineItem } from '@/modules/first-leg/types/firstLegOrder' import { canClientEditFirstLegOrder } from '@/modules/first-leg/utils/firstLegOrder' +export type ClientWarehouseType = 'DOMESTIC' | 'FBA' | 'OVERSEAS' +export type ClientWarehouse = PlatformReceivingWarehouse & { + warehouseType: ClientWarehouseType + countryCode: string +} + const countryCodes: Readonly> = { 中国: 'CN', 美国: 'US', 德国: 'DE', 英国: 'GB', 加拿大: 'CA', 日本: 'JP', 澳大利亚: 'AU', } @@ -60,7 +66,7 @@ function partyPayload(snapshot: FirstLegPartySnapshot): FirstLegOrderPartyPayloa } } -function savePayload(draft: FirstLegOrderDraft, warehouse: PlatformReceivingWarehouse, customerName: string): FirstLegOrderSaveRequest { +function savePayload(draft: FirstLegOrderDraft, warehouse: ClientWarehouse, customerName: string): FirstLegOrderSaveRequest { if (!draft.shipperPartySnapshot || !draft.consigneePartySnapshot) throw new Error('请完整填写收发货信息') return { // 后端把 customerId 作为订单主往来单位校验并以服务端名称覆盖 customerName; @@ -70,6 +76,7 @@ function savePayload(draft: FirstLegOrderDraft, warehouse: PlatformReceivingWare transportMode: apiTransport(draft), tradeTerm: draft.tradeTerm || undefined, destinationCountryCode: draft.destinationCountryCode.trim().toUpperCase(), + destinationWarehouseId: draft.destinationType === 'PRIVATE' ? undefined : draft.destinationWarehouseId, destinationWarehouseName: draft.destinationWarehouseName, expectedShipDate: draft.expectedDepartureDate || undefined, customerRemark: draft.customerRemark || undefined, @@ -167,26 +174,29 @@ function mapOrder(order: MerchantFirstLegOrder, warehouses: PlatformReceivingWar })), timeline: detail ? timeline(detail) : [], createdAt: order.createdAt, updatedAt: order.updatedAt, tradeTerm: order.tradeTerm ?? '', destinationCountryCode: order.destinationCountryCode, + destinationWarehouseId: order.destinationWarehouseId ? String(order.destinationWarehouseId) : '', destinationWarehouseName: order.destinationWarehouseName, pickupMethod: order.pickupMethod, channelCode: order.channelCode, channelName: order.channelName, taxMode: order.taxMode, customsInspection: order.customsInspection, destinationType: order.destinationType, amazonWarehouseCode: order.amazonWarehouseCode ?? '', shipmentId: order.shipmentId ?? '', } } -function mapWarehouse(item: MerchantWarehouse): PlatformReceivingWarehouse { +function mapWarehouse(item: MerchantWarehouse): ClientWarehouse { return { id: String(item.id), code: item.warehouseCode, name: item.warehouseName, province: '', city: '', address: '', contactName: item.contactName ?? '', contactPhone: item.contactPhone ?? '', businessHours: '', status: item.status, remark: item.remark ?? '', updatedAt: item.updatedAt ?? '', + warehouseType: item.warehouseType as ClientWarehouseType, countryCode: item.countryCode, } } export const useFirstLegOrdersStore = defineStore('client-first-leg-orders', () => { const orders = ref([]) - const warehouses = ref([]) + const warehouses = ref([]) const loading = ref(false) const loadedFromApi = ref(false) const loadError = ref('') + const warehouseLoadError = ref('') const activeCount = computed(() => orders.value.filter((item) => ['PENDING_REVIEW', 'ACCEPTED', 'BOOKED', 'IN_TRANSIT'].includes(item.status)).length) const pendingCount = computed(() => orders.value.filter((item) => item.status === 'PENDING_REVIEW').length) @@ -196,16 +206,23 @@ export const useFirstLegOrdersStore = defineStore('client-first-leg-orders', () loading.value = true loadedFromApi.value = true loadError.value = '' + warehouseLoadError.value = '' orders.value = [] warehouses.value = [] - try { - const [orderPage, warehousePage] = await Promise.all([getMerchantFirstLegOrders(), getMerchantWarehouses()]) - warehouses.value = warehousePage.records.filter((item) => item.status === 'ACTIVE').map(mapWarehouse) - orders.value = orderPage.records.map((item) => mapOrder(item, warehouses.value)) - } catch (error) { - loadError.value = error instanceof Error ? error.message : '订单与仓库数据加载失败' - throw error - } finally { loading.value = false } + const [orderResult, warehouseResult] = await Promise.allSettled([getMerchantFirstLegOrders(), getMerchantWarehouses()]) + if (warehouseResult.status === 'fulfilled') { + warehouses.value = warehouseResult.value.records + .filter((item) => item.status === 'ACTIVE' && ['DOMESTIC', 'FBA', 'OVERSEAS'].includes(item.warehouseType)) + .map(mapWarehouse) + } else { + warehouseLoadError.value = warehouseResult.reason instanceof Error ? warehouseResult.reason.message : '公共仓库数据加载失败' + } + if (orderResult.status === 'fulfilled') { + orders.value = orderResult.value.records.map((item) => mapOrder(item, warehouses.value)) + } else { + loadError.value = orderResult.reason instanceof Error ? orderResult.reason.message : '订单数据加载失败' + } + loading.value = false } async function loadDetail(id: string) { @@ -235,7 +252,16 @@ export const useFirstLegOrdersStore = defineStore('client-first-leg-orders', () async function saveDraft(draft: FirstLegOrderDraft, customerName: string, id?: string) { const warehouse = warehouses.value.find((item) => item.id === draft.receivingWarehouseId) - if (!warehouse) throw new Error('所选收货仓当前不可用') + if (!warehouse || warehouse.warehouseType !== 'DOMESTIC') throw new Error('所选国内收货仓当前不可用') + if (draft.destinationType !== 'PRIVATE') { + const destinationWarehouse = warehouses.value.find((item) => item.id === draft.destinationWarehouseId && item.warehouseType === draft.destinationType) + if (!destinationWarehouse) throw new Error('所选目的仓当前不可用,请重新选择') + draft.destinationWarehouseName = destinationWarehouse.name + draft.destinationCountryCode = destinationWarehouse.countryCode + draft.amazonWarehouseCode = draft.destinationType === 'FBA' ? destinationWarehouse.code : '' + } else { + draft.destinationWarehouseId = '' + } if (id) { const order = getById(id) if (!order || !canClientEditFirstLegOrder(order.status)) throw new Error('当前订单状态不允许编辑') @@ -253,5 +279,5 @@ export const useFirstLegOrdersStore = defineStore('client-first-leg-orders', () return loadDetail(order.id) } - return { orders, warehouses, loading, loadedFromApi, loadError, activeCount, pendingCount, getById, load, loadDetail, loadWarehouseDetail, saveDraft, submit } + return { orders, warehouses, loading, loadedFromApi, loadError, warehouseLoadError, activeCount, pendingCount, getById, load, loadDetail, loadWarehouseDetail, saveDraft, submit } }) diff --git a/src/modules/first-leg/types/firstLegOrder.ts b/src/modules/first-leg/types/firstLegOrder.ts index 3018a30..029ed34 100644 --- a/src/modules/first-leg/types/firstLegOrder.ts +++ b/src/modules/first-leg/types/firstLegOrder.ts @@ -70,6 +70,7 @@ export interface FirstLegOrder { updatedAt: string tradeTerm: string destinationCountryCode: string + destinationWarehouseId: string destinationWarehouseName: string pickupMethod: 'SELF_EXPRESS' | 'PICKUP' | 'SELF_TRUCK' channelCode: string @@ -101,6 +102,7 @@ export type FirstLegOrderDraft = Pick< | 'items' | 'tradeTerm' | 'destinationCountryCode' + | 'destinationWarehouseId' | 'destinationWarehouseName' | 'pickupMethod' | 'channelCode' diff --git a/src/modules/first-leg/utils/firstLegOrder.test.mjs b/src/modules/first-leg/utils/firstLegOrder.test.mjs index 7c46610..1a318bc 100644 --- a/src/modules/first-leg/utils/firstLegOrder.test.mjs +++ b/src/modules/first-leg/utils/firstLegOrder.test.mjs @@ -41,6 +41,7 @@ function validDraft() { customerRemark: '', tradeTerm: 'FOB', destinationCountryCode: 'US', + destinationWarehouseId: 'WH-FBA-ONT8', destinationWarehouseName: 'ONT8', pickupMethod: 'SELF_EXPRESS', channelCode: 'SEA-US-FBA', @@ -113,6 +114,20 @@ test('requires FBA reference, warehouse, shipment, SKU and UPC fields', () => { assert.ok(errors.includes('第 1 条货物缺少 UPC')) }) +test('requires public destination ids while keeping private addresses detached from public warehouses', () => { + const publicDraft = validDraft() + publicDraft.destinationWarehouseId = '' + assert.ok(validateFirstLegOrderDraft(publicDraft).includes('请选择平台维护的目的仓')) + + const privateDraft = validDraft() + privateDraft.destinationType = 'PRIVATE' + privateDraft.destinationWarehouseId = 'WH-FBA-ONT8' + privateDraft.amazonWarehouseCode = '' + privateDraft.shipmentId = '' + privateDraft.referenceNo = '' + assert.ok(validateFirstLegOrderDraft(privateDraft).includes('私人地址不能引用平台公共仓库')) +}) + test('does not require container details for non-FCL cargo', () => { const draft = validDraft() draft.transportMode = 'AIR' diff --git a/src/modules/first-leg/utils/firstLegOrder.ts b/src/modules/first-leg/utils/firstLegOrder.ts index 63e88b9..5a6f874 100644 --- a/src/modules/first-leg/utils/firstLegOrder.ts +++ b/src/modules/first-leg/utils/firstLegOrder.ts @@ -66,6 +66,8 @@ export function validateFirstLegOrderDraft(draft: FirstLegOrderDraft) { if (!draft.destination.trim()) errors.push('请填写目的地') if (!/^[A-Za-z]{2}$/.test(draft.destinationCountryCode.trim())) errors.push('请输入两位英文字母的目的国代码') if (!draft.destinationWarehouseName.trim()) errors.push('请填写目的仓名称') + if (draft.destinationType !== 'PRIVATE' && !draft.destinationWarehouseId) errors.push('请选择平台维护的目的仓') + if (draft.destinationType === 'PRIVATE' && draft.destinationWarehouseId) errors.push('私人地址不能引用平台公共仓库') if (!draft.channelCode.trim() || !draft.channelName.trim()) errors.push('请填写渠道编码和渠道名称') if (!draft.receivingWarehouseId) errors.push('请选择平台收货仓') if (!draft.shipperPartyId || !draft.shipperPartySnapshot || !draft.shipper.trim()) errors.push('请选择发货单位') diff --git a/src/modules/first-leg/views/FirstLegOrdersView.test.mjs b/src/modules/first-leg/views/FirstLegOrdersView.test.mjs index b91d6df..cb5c776 100644 --- a/src/modules/first-leg/views/FirstLegOrdersView.test.mjs +++ b/src/modules/first-leg/views/FirstLegOrdersView.test.mjs @@ -96,7 +96,7 @@ test('does not expose arbitrary status mutation controls to customers', () => { }) test('requires an enabled platform receiving warehouse in the order form', () => { - assert.match(source, /computed\(\(\) => store\.warehouses\)/) + assert.match(source, /store\.warehouses\.filter\(\(warehouse\) => warehouse\.warehouseType === 'DOMESTIC'\)/) assert.match(source, /availableWarehouses\.value\.length === 0/) assert.match(source, /label="平台收货仓"[^>]*required/) assert.match(source, /v-model="form\.receivingWarehouseId"/) @@ -111,6 +111,15 @@ test('requires an enabled platform receiving warehouse in the order form', () => assert.match(source, /loadSelectedWarehouse\(detailedOrder\.receivingWarehouseId\)/) }) +test('selects platform destinations by type and caches private addresses per user', () => { + assert.match(source, /warehouse\.warehouseType === form\.destinationType/) + assert.match(source, /v-model="form\.destinationWarehouseId"/) + assert.match(source, /form\.amazonWarehouseCode = form\.destinationType === 'FBA' \? warehouse\.code : ''/) + assert.match(source, /cc:first-leg:private-addresses:\$\{auth\.user\?\.id/) + assert.match(source, /localStorage\.setItem\(privateAddressStorageKey\(\)/) + assert.match(source, /私人地址不能引用平台公共仓库|form\.destinationWarehouseId = ''/) +}) + test('collects the backend-required first-leg fields', () => { for (const model of ['destinationCountryCode', 'destinationWarehouseName', 'channelCode', 'pickupMethod', 'taxMode', 'destinationType', 'packageType', 'upc']) { assert.match(source, new RegExp(`v-model="(?:form|item)\\.${model}"`)) @@ -191,4 +200,5 @@ test('loads complete order detail before editing and rejects an unavailable hist assert.match(source, /draftFromOrder\(detailedOrder\)/) assert.match(source, /draft\.receivingWarehouseId = ''/) assert.match(source, /原平台收货仓当前不可用,请重新选择可用收货仓/) + assert.match(source, /原目的仓当前不可用,请重新选择平台维护的目的仓/) }) diff --git a/src/modules/first-leg/views/FirstLegOrdersView.vue b/src/modules/first-leg/views/FirstLegOrdersView.vue index 32076ec..6833d29 100644 --- a/src/modules/first-leg/views/FirstLegOrdersView.vue +++ b/src/modules/first-leg/views/FirstLegOrdersView.vue @@ -34,7 +34,8 @@ const companyAuditLoaded = ref(false) const companySubmissionBlocked = computed(() => companyAuditLoaded.value && tenantStore.company.auditStatus !== 'ACTIVE') const canCreateOrder = computed(() => auth.hasPermission('order:first-leg:create')) const canEditOrder = computed(() => auth.hasPermission('order:first-leg:edit')) -const availableWarehouses = computed(() => store.warehouses) +const availableWarehouses = computed(() => store.warehouses.filter((warehouse) => warehouse.warehouseType === 'DOMESTIC')) +const availableDestinationWarehouses = computed(() => store.warehouses.filter((warehouse) => warehouse.warehouseType === form.destinationType)) const availableShippers = ref(firstLegPartySource.listAvailable('SHIPPER')) const availableConsignees = ref(firstLegPartySource.listAvailable('CONSIGNEE')) const filters = ref({ keyword: '', status: '', transportMode: '', createdRange: [] }) @@ -77,6 +78,37 @@ const supportingDataErrors = ref([]) const editorBaseline = ref('') let cargoSequence = 0 +interface CachedPrivateAddress { + id: string + label: string + consigneePartyId: string + consigneePartySnapshot: NonNullable + consignee: string + destination: string + destinationCountryCode: string + destinationWarehouseName: string +} + +const privateAddresses = ref([]) +const selectedPrivateAddressId = ref('') + +function privateAddressStorageKey() { + return `cc:first-leg:private-addresses:${auth.user?.id ?? 'anonymous'}` +} + +function loadPrivateAddresses() { + try { + const value = JSON.parse(localStorage.getItem(privateAddressStorageKey()) ?? '[]') as unknown + privateAddresses.value = Array.isArray(value) ? value.filter((item): item is CachedPrivateAddress => Boolean(item && typeof item === 'object' && 'id' in item && 'consigneePartySnapshot' in item)) : [] + } catch { + privateAddresses.value = [] + } +} + +function persistPrivateAddresses() { + localStorage.setItem(privateAddressStorageKey(), JSON.stringify(privateAddresses.value)) +} + const orderFormRules: FormRules = { transportMode: [{ required: true, message: '请选择运输方式', trigger: 'change' }], businessType: [{ required: true, message: '请选择业务类型', trigger: 'change' }], @@ -141,6 +173,7 @@ function emptyDraft(): FirstLegOrderDraft { customerRemark: '', tradeTerm: 'FOB', destinationCountryCode: 'US', + destinationWarehouseId: '', destinationWarehouseName: '', pickupMethod: 'SELF_EXPRESS', channelCode: 'SEA-US-FBA', @@ -221,9 +254,8 @@ async function loadPageData() { } else if (companyResult.status === 'rejected') { supportingDataErrors.value.push(`企业资料加载失败:${errorMessage(companyResult.reason)}`) } - if (ordersResult.status === 'rejected' && !store.loadError) { - supportingDataErrors.value.push(`订单与仓库加载失败:${errorMessage(ordersResult.reason)}`) - } + if (ordersResult.status === 'rejected' && !store.loadError) supportingDataErrors.value.push(`订单加载失败:${errorMessage(ordersResult.reason)}`) + if (store.warehouseLoadError) supportingDataErrors.value.push(`公共仓库加载失败:${store.warehouseLoadError}`) supportingDataLoading.value = false } @@ -239,6 +271,7 @@ function handleBeforeUnload(event: BeforeUnloadEvent) { onMounted(() => { window.addEventListener('beforeunload', handleBeforeUnload) + loadPrivateAddresses() void loadPageData() }) @@ -297,6 +330,7 @@ function draftFromOrder(order: FirstLegOrder): FirstLegOrderDraft { customerRemark: order.customerRemark, tradeTerm: order.tradeTerm, destinationCountryCode: order.destinationCountryCode, + destinationWarehouseId: order.destinationWarehouseId, destinationWarehouseName: order.destinationWarehouseName, pickupMethod: order.pickupMethod, channelCode: order.channelCode, @@ -347,8 +381,67 @@ function selectChannel(code: string) { form.channelName = channel?.name ?? '' } -function syncAmazonWarehouse(value: string) { - if (form.destinationType === 'FBA') form.destinationWarehouseName = value.trim() +function selectDestinationWarehouse(id: string) { + const warehouse = availableDestinationWarehouses.value.find((item) => item.id === id) + if (!warehouse) return + form.destinationWarehouseId = warehouse.id + form.destinationWarehouseName = warehouse.name + form.destinationCountryCode = warehouse.countryCode.toUpperCase() + form.destination = warehouse.name + form.amazonWarehouseCode = form.destinationType === 'FBA' ? warehouse.code : '' +} + +function changeDestinationType() { + form.destinationWarehouseId = '' + form.destinationWarehouseName = '' + form.amazonWarehouseCode = '' + selectedPrivateAddressId.value = '' + resetEditorValidation() +} + +function selectPrivateAddress(id: string) { + const address = privateAddresses.value.find((item) => item.id === id) + if (!address) return + form.consigneePartyId = address.consigneePartyId + form.consigneePartySnapshot = { ...address.consigneePartySnapshot } + form.consignee = address.consignee + form.destination = address.destination + form.destinationCountryCode = address.destinationCountryCode + form.destinationWarehouseId = '' + form.destinationWarehouseName = address.destinationWarehouseName +} + +function savePrivateAddress() { + if (!form.consigneePartySnapshot || !form.consigneePartyId || !form.destinationWarehouseName.trim() || !form.destinationCountryCode.trim()) { + ElMessage.warning('请先完整填写收货单位、私人地址名称和目的国代码') + return + } + const snapshot = { ...form.consigneePartySnapshot } + const existing = privateAddresses.value.find((item) => item.consigneePartyId === form.consigneePartyId && item.consigneePartySnapshot.address === snapshot.address) + const saved: CachedPrivateAddress = { + id: existing?.id ?? globalThis.crypto?.randomUUID?.() ?? `private-${Date.now()}`, + label: `${form.destinationWarehouseName.trim()} · ${snapshot.address}`, + consigneePartyId: form.consigneePartyId, + consigneePartySnapshot: snapshot, + consignee: form.consignee, + destination: form.destination, + destinationCountryCode: form.destinationCountryCode.trim().toUpperCase(), + destinationWarehouseName: form.destinationWarehouseName.trim(), + } + privateAddresses.value = existing + ? privateAddresses.value.map((item) => item.id === existing.id ? saved : item) + : [saved, ...privateAddresses.value] + selectedPrivateAddressId.value = saved.id + persistPrivateAddresses() + ElMessage.success(existing ? '私人地址已更新' : '私人地址已保存到当前账号') +} + +function removePrivateAddress() { + if (!selectedPrivateAddressId.value) return + privateAddresses.value = privateAddresses.value.filter((item) => item.id !== selectedPrivateAddressId.value) + selectedPrivateAddressId.value = '' + persistPrivateAddresses() + ElMessage.success('已从当前账号的私人地址中移除') } function selectShipper(id: string) { @@ -399,6 +492,15 @@ async function openEdit(order: FirstLegOrder) { draft.receivingWarehouseId = '' ElMessage.warning('原平台收货仓当前不可用,请重新选择可用收货仓') } + if (draft.destinationType !== 'PRIVATE') { + const destinationAvailable = store.warehouses.some((item) => item.id === draft.destinationWarehouseId && item.warehouseType === draft.destinationType) + if (!destinationAvailable) { + draft.destinationWarehouseId = '' + draft.destinationWarehouseName = '' + draft.amazonWarehouseCode = '' + ElMessage.warning('原目的仓当前不可用,请重新选择平台维护的目的仓') + } + } editingId.value = detailedOrder.id replaceForm(draft) if (warehouseAvailable) void loadSelectedWarehouse(detailedOrder.receivingWarehouseId) @@ -658,7 +760,14 @@ async function searchOrders(model: QueryModel) {

发货与目的地

单位信息会保存为本订单快照,后续主数据变更不会影响本单。

- FBA 仓海外仓私人仓 + FBA 仓海外仓私人地址 +
+ + + + 保存当前地址 + 移除缓存 +

发货信息

@@ -682,9 +791,11 @@ async function searchOrders(model: QueryModel) {
- - - + 平台暂未维护可用的{{ form.destinationType === 'FBA' ? ' FBA 仓' : '海外仓' }},请联系平台管理员。 + + + +
@@ -820,6 +931,8 @@ async function searchOrders(model: QueryModel) { .party-panel :deep(.el-alert) { margin-bottom: 14px; } .party-contact-grid, .destination-fields { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 12px; } .destination-fields { margin-top: 16px; } +.private-address-toolbar { display: grid; grid-template-columns: minmax(220px, 1fr) auto auto; gap: 10px; align-items: center; margin-bottom: 16px; } +.field-guidance { display: block; margin-top: 5px; color: var(--muted); line-height: 1.45; } .cargo-entry { padding: 16px; border: 1px solid var(--border); background: #fbfcfc; } .cargo-entry + .cargo-entry { margin-top: 12px; } .cargo-entry > header { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 12px; } @@ -887,7 +1000,7 @@ async function searchOrders(model: QueryModel) { .order-table-toolbar .el-button { width: 100%; min-height: 40px; } .order-eligibility { flex-wrap: wrap; } .order-eligibility .el-button { width: 100%; min-height: 44px; } - .order-form-grid, .order-detail-grid, .party-select-grid, .party-contact-grid, .destination-fields, .cargo-primary-grid, .cargo-secondary-grid { grid-template-columns: 1fr; } + .order-form-grid, .order-detail-grid, .party-select-grid, .party-contact-grid, .destination-fields, .private-address-toolbar, .cargo-primary-grid, .cargo-secondary-grid { grid-template-columns: 1fr; } .segmented-options { width: 100%; align-items: stretch; flex-direction: column; } .segmented-options :deep(.el-radio-button__inner) { border: 1px solid var(--border); border-radius: 6px !important; box-shadow: none !important; } .warehouse-options { display: grid; width: 100%; grid-template-columns: 1fr; }