From 08982b87ffaacfe06105f0f42acb1ffc32aacca1 Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Thu, 6 Aug 2026 18:02:04 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/platformCarrierRates.test.ts | 34 ++--- .../carrier-rates/api/platformCarrierRates.ts | 15 +-- .../pages/CarrierRatesView.test.ts | 38 +++--- .../carrier-rates/pages/CarrierRatesView.vue | 89 ++----------- .../unified-orders/api/unifiedOrders.test.ts | 26 +++- .../unified-orders/api/unifiedOrders.ts | 18 +++ .../pages/UnifiedOrdersView.test.ts | 11 ++ .../pages/UnifiedOrdersView.vue | 120 +++++++++++++++++- 8 files changed, 213 insertions(+), 138 deletions(-) diff --git a/src/modules/fba-pricing/carrier-rates/api/platformCarrierRates.test.ts b/src/modules/fba-pricing/carrier-rates/api/platformCarrierRates.test.ts index 311925ee..3afba370 100644 --- a/src/modules/fba-pricing/carrier-rates/api/platformCarrierRates.test.ts +++ b/src/modules/fba-pricing/carrier-rates/api/platformCarrierRates.test.ts @@ -20,7 +20,7 @@ import { const payload = { id: '9007199254740995', - tenantId: '10001', + tenantId: '0', rateCode: 'RATE-US-WEST', rateVersion: 2, originWarehouseId: '20001', @@ -48,38 +48,36 @@ const payload = { describe('platformCarrierRates api', () => { beforeEach(() => vi.clearAllMocks()) - it('queries the real carrier-rate path and keeps every long id as a string', async () => { + it('queries the platform-wide carrier-rate path and keeps every long id as a string', async () => { mocks.get.mockResolvedValue({ data: [payload] }) - const result = await listPlatformCarrierRates({ customerId: ' 10001 ', rateCode: ' RATE-US ' }) + const result = await listPlatformCarrierRates({ rateCode: ' RATE-US ' }) expect(mocks.get).toHaveBeenCalledWith('/platform/v1/fba-pricing/carrier-rates', { - params: { tenantId: '10001', rateCode: 'RATE-US' }, + params: { rateCode: 'RATE-US' }, }) expect(result[0]).toMatchObject({ id: '9007199254740995', - tenantId: '10001', + tenantId: '0', originWarehouseId: '20001', carrierId: '30001', }) }) - it('omits the compatibility tenant parameter for an all-customer list query', async () => { + it('omits the tenant parameter for the platform-wide list query', async () => { mocks.get.mockResolvedValue({ data: [payload] }) await listPlatformCarrierRates() - await listPlatformCarrierRates({ customerId: '', rateCode: ' ' }) + await listPlatformCarrierRates({ rateCode: ' ' }) expect(mocks.get).toHaveBeenNthCalledWith(1, '/platform/v1/fba-pricing/carrier-rates', { params: {} }) expect(mocks.get).toHaveBeenNthCalledWith(2, '/platform/v1/fba-pricing/carrier-rates', { params: {} }) }) - it('loads a version detail with tenant scope', async () => { + it('loads a platform-wide version detail without tenant scope', async () => { mocks.get.mockResolvedValue({ data: payload }) - await getPlatformCarrierRate('10001', '9001') - expect(mocks.get).toHaveBeenCalledWith('/platform/v1/fba-pricing/carrier-rates/9001', { - params: { tenantId: '10001' }, - }) + await getPlatformCarrierRate('9001') + expect(mocks.get).toHaveBeenCalledWith('/platform/v1/fba-pricing/carrier-rates/9001') }) it('creates immutable versions and retires an active version', async () => { @@ -103,14 +101,10 @@ describe('platformCarrierRates api', () => { remark: null, } - await createPlatformCarrierRateVersion('10001', request) - await retirePlatformCarrierRate('10001', '9001') + await createPlatformCarrierRateVersion(request) + await retirePlatformCarrierRate('9001') - expect(mocks.post).toHaveBeenCalledWith('/platform/v1/fba-pricing/carrier-rates/versions', request, { - params: { tenantId: '10001' }, - }) - expect(mocks.delete).toHaveBeenCalledWith('/platform/v1/fba-pricing/carrier-rates/9001', { - params: { tenantId: '10001' }, - }) + expect(mocks.post).toHaveBeenCalledWith('/platform/v1/fba-pricing/carrier-rates/versions', request) + expect(mocks.delete).toHaveBeenCalledWith('/platform/v1/fba-pricing/carrier-rates/9001') }) }) diff --git a/src/modules/fba-pricing/carrier-rates/api/platformCarrierRates.ts b/src/modules/fba-pricing/carrier-rates/api/platformCarrierRates.ts index d0f6d387..8e861c01 100644 --- a/src/modules/fba-pricing/carrier-rates/api/platformCarrierRates.ts +++ b/src/modules/fba-pricing/carrier-rates/api/platformCarrierRates.ts @@ -59,7 +59,6 @@ export interface CreatePlatformCarrierRateVersionRequest { } export interface PlatformCarrierRateQuery { - customerId?: string rateCode?: string } @@ -87,38 +86,32 @@ function normalizeCarrierRate(value: PlatformCarrierRatePayload): PlatformCarrie } export async function listPlatformCarrierRates(query: PlatformCarrierRateQuery = {}) { - const customerId = query.customerId?.trim() const rateCode = query.rateCode?.trim() const response = await http.get, ApiEnvelope>( basePath, - { params: { ...(customerId ? { tenantId: customerId } : {}), ...(rateCode ? { rateCode } : {}) } }, + { params: rateCode ? { rateCode } : {} }, ) return unwrapApiResponse(response).map(normalizeCarrierRate) } -export async function getPlatformCarrierRate(tenantId: string, id: string) { +export async function getPlatformCarrierRate(id: string) { const response = await http.get, ApiEnvelope>( `${basePath}/${id}`, - { params: { tenantId } }, ) return normalizeCarrierRate(unwrapApiResponse(response)) } export async function createPlatformCarrierRateVersion( - tenantId: string, payload: CreatePlatformCarrierRateVersionRequest, ) { const response = await http.post, ApiEnvelope>( `${basePath}/versions`, payload, - { params: { tenantId } }, ) return normalizeCarrierRate(unwrapApiResponse(response)) } -export async function retirePlatformCarrierRate(tenantId: string, id: string) { - const response = await http.delete, ApiEnvelope>(`${basePath}/${id}`, { - params: { tenantId }, - }) +export async function retirePlatformCarrierRate(id: string) { + const response = await http.delete, ApiEnvelope>(`${basePath}/${id}`) return unwrapApiResponse(response) } diff --git a/src/modules/fba-pricing/carrier-rates/pages/CarrierRatesView.test.ts b/src/modules/fba-pricing/carrier-rates/pages/CarrierRatesView.test.ts index 3103ddf1..257bf5a8 100644 --- a/src/modules/fba-pricing/carrier-rates/pages/CarrierRatesView.test.ts +++ b/src/modules/fba-pricing/carrier-rates/pages/CarrierRatesView.test.ts @@ -7,10 +7,10 @@ const queryToolbarSource = readFileSync(join(process.cwd(), 'src/shared/componen describe('CarrierRatesView source contract', () => { it('keeps operation column first and covers every required business field', () => { - const customerColumn = "key: 'customer', label: '所属企业'" + const scopeColumn = "key: 'scope', label: '适用范围'" const destinationColumn = "key: 'destinationCode', prop: 'destinationCode'" - expect(source.indexOf("key: 'actions'")).toBeLessThan(source.indexOf(customerColumn)) - expect(source.indexOf(customerColumn)).toBeLessThan(source.indexOf(destinationColumn)) + expect(source.indexOf("key: 'actions'")).toBeLessThan(source.indexOf(scopeColumn)) + expect(source.indexOf(scopeColumn)).toBeLessThan(source.indexOf(destinationColumn)) expect(source.indexOf(destinationColumn)).toBeLessThan(source.indexOf("key: 'rate'")) for (const label of ['起运仓', '业务类型', '渠道编码', '船司', 'FBA 目的仓', '计费单位', '单价', '币种', '固定附加费', '有效期', '备注', '生效时间', '结束时间']) { expect(source).toContain(label) @@ -27,41 +27,35 @@ describe('CarrierRatesView source contract', () => { expect(source).not.toContain('编辑价格') }) - it('keeps the customer company scope in the query toolbar without exposing tenant concepts', () => { - expect(source).toContain("key: 'customerId'") - expect(source.indexOf("key: 'customerId'")).toBeLessThan(source.indexOf("key: 'rateCode'")) - expect(source).toContain("'aria-label': '所属企业'") - expect(source).toContain('listPlatformTenants') - expect(source).toContain('masterDataLoaded') + it('treats carrier rates as platform-wide data without a customer selector', () => { + expect(source).toContain("key: 'scope', label: '适用范围'") + expect(source).toContain('全部客户') + expect(source).not.toContain("key: 'customerId'") + expect(source).not.toContain('listPlatformTenants') expect(source).not.toContain('PlatformTenantSelector') expect(source).not.toContain('selectedTenantId') expect(source).not.toContain('租户') }) - it('uses semantic compact filter widths and keeps the customer selection empty by default', () => { + it('uses semantic compact filter widths without a customer field', () => { for (const field of [ - "key: 'customerId',\n type: 'select',\n width: 280", "key: 'rateCode', type: 'input', width: 220", "key: 'destinationCode', type: 'input', width: 220", "key: 'channelCode', type: 'input', width: 220", "key: 'chargeUnit', type: 'select', width: 180", "key: 'status', type: 'select', width: 160", ]) expect(source).toContain(field) - expect(source).toContain("customerId: ''") - expect(source).toContain("placeholder: customerLoadError.value ? '企业列表加载失败' : '全部客户'") expect(source).not.toContain(':search-on-reset="false"') - expect(source).not.toContain('请先选择所属企业') expect(source).not.toContain('请选择所属企业并查询价格数据') expect(source).not.toContain('customers.value[0]') }) - it('loads all customers initially and after reset while preserving row scope for record actions', () => { + it('loads the platform-wide list and keeps row actions independent of customer scope', () => { expect(source).toContain('void loadRateData()') - expect(source).toContain("listPlatformCarrierRates(customerId ? { customerId } : {})") - expect(source).toContain('loadRateData(customerId)') - expect(source).toContain('getPlatformCarrierRate(String(row.tenantId), row.id)') - expect(source).toContain('retirePlatformCarrierRate(String(row.tenantId), row.id)') - expect(source).toContain('customerLabel(detail.tenantId)') + expect(source).toContain('listPlatformCarrierRates({ rateCode: filters.value.rateCode })') + expect(source).toContain('getPlatformCarrierRate(row.id)') + expect(source).toContain('retirePlatformCarrierRate(row.id)') + expect(source).toContain('
适用范围
全部客户
') }) it('loads shared carrier master data under the platform public scope', () => { @@ -73,8 +67,8 @@ describe('CarrierRatesView source contract', () => { expect(source).not.toContain('loadCustomerMasterData') }) - it('keeps customer rate queries independent from edit-only master data', () => { - expect(source).toContain('await loadRateData(customerId)') + it('keeps public rate queries independent from edit-only master data', () => { + expect(source).toContain('await loadRateData()') expect(source).not.toContain('loadPlatformMasterData(customerId)') }) diff --git a/src/modules/fba-pricing/carrier-rates/pages/CarrierRatesView.vue b/src/modules/fba-pricing/carrier-rates/pages/CarrierRatesView.vue index f9c8b79f..76bad4e4 100644 --- a/src/modules/fba-pricing/carrier-rates/pages/CarrierRatesView.vue +++ b/src/modules/fba-pricing/carrier-rates/pages/CarrierRatesView.vue @@ -4,7 +4,6 @@ import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from 'elem import { CalendarClock, Eye, Plus, Power, Ship, Warehouse } from '@lucide/vue' import { useAccessPreviewStore } from '@/core/access/accessPreview' import { useUnsavedWorkRegistration } from '@/core/unsaved-work/useUnsavedWorkRegistration' -import { listPlatformTenants, type PlatformTenantResource as PlatformCustomerResource } from '@/modules/iam' import { listPlatformWarehouses, type PlatformWarehouseResource } from '@/modules/warehouses' import { listPlatformPartners, type PlatformPartnerResource } from '@/modules/master-data' import { getApiErrorDetails, type ApiErrorDetails } from '@/shared/api/http' @@ -26,7 +25,6 @@ import { } from '../api/platformCarrierRates' type RateFilters = QueryModel & { - customerId: string rateCode: string destinationCode: string channelCode: string @@ -51,9 +49,6 @@ interface RateForm { } const access = useAccessPreviewStore() -const customers = ref([]) -const customersLoading = ref(false) -const customerLoadError = ref(null) const masterDataLoaded = ref(false) const rows = ref([]) const warehouses = ref([]) @@ -69,7 +64,6 @@ const detailLoading = ref(false) const saving = ref(false) const detail = ref(null) const retiringIds = ref(new Set()) -const editorCustomerId = ref('') const formRef = ref() let loadSequence = 0 let masterDataSequence = 0 @@ -100,18 +94,9 @@ const { captureBaseline } = useUnsavedWorkRegistration({ requiredPermission, }) -const filters = ref({ customerId: '', rateCode: '', destinationCode: '', channelCode: '', chargeUnit: '', status: '' }) +const filters = ref({ rateCode: '', destinationCode: '', channelCode: '', chargeUnit: '', status: '' }) const appliedFilters = ref({ ...filters.value }) -const customerSwitchDisabled = computed(() => editorOpen.value || detailOpen.value || saving.value || detailLoading.value || retiringIds.value.size > 0) const queryFields = computed(() => [ - { - key: 'customerId', - type: 'select', - width: 280, - placeholder: customerLoadError.value ? '企业列表加载失败' : '全部客户', - props: { 'aria-label': '所属企业', filterable: true, disabled: customersLoading.value || Boolean(customerLoadError.value) || customerSwitchDisabled.value }, - options: customers.value.map((customer) => ({ value: String(customer.id), label: `${customer.tenantName}(${customer.tenantCode || customer.id})` })), - }, { key: 'rateCode', type: 'input', width: 220, placeholder: '价格编码', props: { 'aria-label': '价格编码' } }, { key: 'destinationCode', type: 'input', width: 220, placeholder: 'FBA 目的仓编码', props: { 'aria-label': 'FBA 目的仓编码' } }, { key: 'channelCode', type: 'input', width: 220, placeholder: '渠道编码', props: { 'aria-label': '渠道编码' } }, @@ -131,7 +116,7 @@ const queryFields = computed(() => [ const columns: DataTableColumn[] = [ { key: 'actions', label: '操作', width: 230, fixed: 'left', slotName: 'actions' }, - { key: 'customer', label: '所属企业', minWidth: 200, slotName: 'customer' }, + { key: 'scope', label: '适用范围', minWidth: 140, slotName: 'scope' }, { key: 'destinationCode', prop: 'destinationCode', label: 'FBA 目的仓', minWidth: 135 }, { key: 'rate', label: '价格版本', minWidth: 180, slotName: 'rate' }, { key: 'originWarehouseName', prop: 'originWarehouseName', label: '起运仓', minWidth: 170, showOverflowTooltip: true }, @@ -188,10 +173,6 @@ const rules: FormRules = { effectiveTo: [{ validator: validateEffectiveTo, trigger: 'change' }], } -watch(() => filters.value.customerId, () => { - page.value = 1 -}) - watch( () => access.can('firstleg:fba-pricing:rate:edit'), (canEdit) => { @@ -214,43 +195,16 @@ function statusLabel(status: CarrierRateStatus) { return status === 'ACTIVE' ? '启用' : '停用' } -function customerLabel(customerId: string) { - const customer = customers.value.find((item) => String(item.id) === String(customerId)) - return customer ? `${customer.tenantName}(${customer.tenantCode || '未设置编码'})` : '客户信息待加载' -} - function nullable(value: string) { return value.trim() || null } -async function loadCustomers() { - customersLoading.value = true - customerLoadError.value = null - try { - const records: PlatformCustomerResource[] = [] - let currentPage = 1 - let pages = 1 - do { - const result = await listPlatformTenants({ page: currentPage, size: 200, status: 'ACTIVE' }) - records.push(...result.records) - pages = Math.max(1, result.pages) - currentPage += 1 - } while (currentPage <= pages) - customers.value = records - } catch (error) { - customers.value = [] - customerLoadError.value = getApiErrorDetails(error, '企业列表加载失败,暂时无法按所属企业筛选或显示企业名称;价格列表仍可查询') - } finally { - customersLoading.value = false - } -} - -async function loadRateData(customerId = filters.value.customerId) { +async function loadRateData() { const sequence = ++loadSequence loading.value = true loadError.value = null try { - const rateResult = await listPlatformCarrierRates(customerId ? { customerId } : {}) + const rateResult = await listPlatformCarrierRates({ rateCode: filters.value.rateCode }) if (sequence !== loadSequence) return rows.value = rateResult } catch (error) { @@ -292,9 +246,7 @@ async function loadPlatformMasterData() { } async function applyQuery(model: QueryModel) { - const customerId = String(model.customerId || '') appliedFilters.value = { - customerId, rateCode: String(model.rateCode || ''), destinationCode: String(model.destinationCode || ''), channelCode: String(model.channelCode || ''), @@ -302,12 +254,11 @@ async function applyQuery(model: QueryModel) { status: (model.status || '') as CarrierRateStatus | '', } page.value = 1 - await loadRateData(customerId) + await loadRateData() } function resetQuery(model: QueryModel) { appliedFilters.value = { - customerId: String(model.customerId || ''), rateCode: String(model.rateCode || ''), destinationCode: String(model.destinationCode || ''), channelCode: String(model.channelCode || ''), @@ -336,16 +287,10 @@ function resetForm(rate?: PlatformCarrierRateResource) { } async function openCreate(rate?: PlatformCarrierRateResource) { - const customerId = String(rate?.tenantId || filters.value.customerId || '') - if (!customerId) { - ElMessage.warning('新增价格版本需要先选择所属企业') - return - } if (!masterDataLoaded.value) { const loaded = await loadPlatformMasterData() if (!loaded) return } - editorCustomerId.value = customerId resetForm(rate) captureBaseline() editorOpen.value = true @@ -357,7 +302,7 @@ async function openDetail(row: PlatformCarrierRateResource) { detailLoading.value = true detail.value = null try { - detail.value = await getPlatformCarrierRate(String(row.tenantId), row.id) + detail.value = await getPlatformCarrierRate(row.id) } catch (error) { const meta = getApiErrorDetails(error, '价格版本详情加载失败') ElMessage.error(meta.requestId ? `${meta.message}(requestId: ${meta.requestId})` : meta.message) @@ -399,8 +344,6 @@ function buildPayload(): CreatePlatformCarrierRateVersionRequest | null { } async function saveVersion() { - const customerId = editorCustomerId.value - if (!customerId) return const valid = await formRef.value?.validate().catch(() => false) if (!valid) return const payload = buildPayload() @@ -410,10 +353,10 @@ async function saveVersion() { } saving.value = true try { - await createPlatformCarrierRateVersion(customerId, payload) + await createPlatformCarrierRateVersion(payload) editorOpen.value = false ElMessage.success('船司 FBA 价格新版本已创建') - await loadRateData(filters.value.customerId) + await loadRateData() } catch (error) { const meta = getApiErrorDetails(error, '价格版本创建失败,请检查有效期和匹配维度后重试') ElMessage.error(meta.requestId ? `${meta.message}(requestId: ${meta.requestId})` : meta.message) @@ -430,9 +373,9 @@ async function retireRate(row: PlatformCarrierRateResource) { { type: 'warning', confirmButtonText: '确认停用', cancelButtonText: '取消' }, ) retiringIds.value = new Set(retiringIds.value).add(row.id) - await retirePlatformCarrierRate(String(row.tenantId), row.id) + await retirePlatformCarrierRate(row.id) ElMessage.success('价格版本已停用') - await loadRateData(filters.value.customerId) + await loadRateData() } catch (error) { if (error === 'cancel' || error === 'close') return const meta = getApiErrorDetails(error, '价格版本停用失败,请重试') @@ -450,7 +393,6 @@ function closeEditor() { } onMounted(() => { - void loadCustomers() void loadRateData() }) @@ -465,11 +407,6 @@ onMounted(() => { @reset="resetQuery" /> - - - 重新加载企业列表 - - { 新增价格版本 @@ -524,7 +461,7 @@ onMounted(() => { >停用 - + @@ -601,7 +538,7 @@ onMounted(() => {
{{ detail.rateCode }}版本 V{{ detail.rateVersion }}
-

运输匹配

所属企业
{{ customerLabel(detail.tenantId) }}
起运仓
{{ detail.originWarehouseName }}
FBA 目的仓
{{ detail.destinationCode }}
业务类型
{{ detail.businessType }}
渠道编码
{{ detail.channelCode }}
船司
{{ detail.carrierName }}
船司 ID
{{ detail.carrierId }}
+

运输匹配

适用范围
全部客户
起运仓
{{ detail.originWarehouseName }}
FBA 目的仓
{{ detail.destinationCode }}
业务类型
{{ detail.businessType }}
渠道编码
{{ detail.channelCode }}
船司
{{ detail.carrierName }}
船司 ID
{{ detail.carrierId }}

计费口径

计费单位
{{ detail.chargeUnit }}
币种
{{ detail.currency }}
单价
{{ formatMoney(detail.unitPrice, detail.currency) }}
固定附加费
{{ formatMoney(detail.additionalFee, detail.currency) }}

版本有效期

生效时间
{{ formatDateTime(detail.effectiveFrom) }}
结束时间
{{ formatDateTime(detail.effectiveTo) }}
更新时间
{{ formatDateTime(detail.updatedAt) }}
记录版本
{{ detail.version ?? '-' }}

{{ detail.remark }}

diff --git a/src/modules/unified-orders/api/unifiedOrders.test.ts b/src/modules/unified-orders/api/unifiedOrders.test.ts index 7025a2b4..be401571 100644 --- a/src/modules/unified-orders/api/unifiedOrders.test.ts +++ b/src/modules/unified-orders/api/unifiedOrders.test.ts @@ -1,14 +1,23 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' import { http } from '@/shared/api/http' -import { getPlatformUnifiedOrder, listPlatformUnifiedOrders } from './unifiedOrders' +import { + approvePlatformUnifiedOrder, + assignPlatformUnifiedOrderCarrier, + getPlatformUnifiedOrder, + listPlatformUnifiedOrders, +} from './unifiedOrders' vi.mock('@/shared/api/http', () => ({ - http: { get: vi.fn() }, + http: { get: vi.fn(), post: vi.fn(), put: vi.fn() }, unwrapApiResponse: (response: { data: T }) => response.data, })) describe('unified orders API contract', () => { - beforeEach(() => vi.mocked(http.get).mockReset()) + beforeEach(() => { + vi.mocked(http.get).mockReset() + vi.mocked(http.post).mockReset() + vi.mocked(http.put).mockReset() + }) it('uses the C02 platform orders endpoint and contract filters', async () => { vi.mocked(http.get).mockResolvedValue({ data: { records: [], page: '2', size: '20', total: '0', pages: '0' } }) @@ -62,7 +71,14 @@ describe('unified orders API contract', () => { expect(http.get).toHaveBeenCalledWith('/platform/v1/orders/90001', { params: { tenantId: '208' } }) }) - it('does not expose a guessed generic action endpoint', async () => { - expect(http).not.toHaveProperty('post') + it('uses the explicit first-leg carrier and approval boundaries for pending review', async () => { + vi.mocked(http.put).mockResolvedValue({ data: null }) + vi.mocked(http.post).mockResolvedValue({ data: null }) + + await assignPlatformUnifiedOrderCarrier('208', '90001', '30001') + await approvePlatformUnifiedOrder('208', '90001') + + expect(http.put).toHaveBeenCalledWith('/platform/v1/orders/first-leg/90001/carrier', { carrierId: '30001' }, { params: { tenantId: '208' } }) + expect(http.post).toHaveBeenCalledWith('/platform/v1/orders/first-leg/90001/approve', undefined, { params: { tenantId: '208' } }) }) }) diff --git a/src/modules/unified-orders/api/unifiedOrders.ts b/src/modules/unified-orders/api/unifiedOrders.ts index 2aaf6801..315888d1 100644 --- a/src/modules/unified-orders/api/unifiedOrders.ts +++ b/src/modules/unified-orders/api/unifiedOrders.ts @@ -116,6 +116,8 @@ export interface UnifiedOrderDetailOrderSnapshot { receivingWarehouseId: number | string | null receivingWarehouseName: string | null channelName: string | null + carrierId: number | string | null + carrierName: string | null createdAt: string | null updatedAt: string | null } @@ -271,6 +273,22 @@ export async function getPlatformUnifiedOrder(tenantId: string, orderId: string) return { ...result, summary: normalizeRow(result.summary) } } +export function assignPlatformUnifiedOrderCarrier(tenantId: string, orderId: string, carrierId: string) { + return http.put, ApiEnvelope>( + `/platform/v1/orders/first-leg/${orderId}/carrier`, + { carrierId }, + { params: { tenantId } }, + ).then(unwrapApiResponse) +} + +export function approvePlatformUnifiedOrder(tenantId: string, orderId: string) { + return http.post, ApiEnvelope>( + `/platform/v1/orders/first-leg/${orderId}/approve`, + undefined, + { params: { tenantId } }, + ).then(unwrapApiResponse) +} + export function unifiedOrderStatusLabel(status: UnifiedOrderMainStatus) { return unifiedOrderStatusOptions.find((item) => item.value === status)?.label || status } diff --git a/src/modules/unified-orders/pages/UnifiedOrdersView.test.ts b/src/modules/unified-orders/pages/UnifiedOrdersView.test.ts index c8424391..b163db50 100644 --- a/src/modules/unified-orders/pages/UnifiedOrdersView.test.ts +++ b/src/modules/unified-orders/pages/UnifiedOrdersView.test.ts @@ -66,6 +66,17 @@ describe('UnifiedOrdersView contract', () => { expect(source).not.toContain('/actions/') }) + it('implements pending review with an active public carrier assignment', () => { + expect(source).toContain("row.nextAction === 'APPROVE_ORDER'") + expect(source).toContain("can('platform:order:approve')") + expect(source).toContain("listPlatformPartners({ page: currentPage, size: 200, type: 'CARRIER', status: 'ACTIVE' })") + expect(source).toContain('await assignPlatformUnifiedOrderCarrier(tenantId, orderId, String(carrier.id))') + expect(source).toContain('await approvePlatformUnifiedOrder(tenantId, orderId)') + expect(source.indexOf('await assignPlatformUnifiedOrderCarrier')).toBeLessThan(source.indexOf('await approvePlatformUnifiedOrder')) + expect(source).toContain('暂无启用的承运商,请先到往来单位维护承运商主数据。') + expect(source).toContain("detail.order.order?.carrierName || '待平台指派'") + }) + it('provides unified detail, history and internal note states', () => { expect(source).toContain('getPlatformUnifiedOrder') expect(source).toContain('aria-label="状态历史"') diff --git a/src/modules/unified-orders/pages/UnifiedOrdersView.vue b/src/modules/unified-orders/pages/UnifiedOrdersView.vue index 794adf80..1d4aa847 100644 --- a/src/modules/unified-orders/pages/UnifiedOrdersView.vue +++ b/src/modules/unified-orders/pages/UnifiedOrdersView.vue @@ -1,9 +1,12 @@