This commit is contained in:
2026-08-04 10:31:53 +08:00
parent 198101c42c
commit 5c25806081
10 changed files with 100 additions and 31 deletions
@@ -34,6 +34,12 @@ 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/)
})
test('only blocks submission when a known company audit status is not active', () => {
assert.match(storeSource, /companyAuditStatus\?: MerchantTenantStatus/)
assert.match(storeSource, /if \(companyAuditStatus && companyAuditStatus !== 'ACTIVE'\)/)
})
test('does not expose the out-of-scope cancellation write', () => {
@@ -66,7 +66,7 @@ function savePayload(draft: FirstLegOrderDraft, warehouse: PlatformReceivingWare
// 后端把 customerId 作为订单主往来单位校验并以服务端名称覆盖 customerName;
// 当前商户下单以所选发货单位作为主往来单位,收发货详情仍分别保存订单快照。
customerId: draft.shipperPartyId,
customerName,
customerName: customerName.trim() || draft.shipperPartySnapshot.name,
transportMode: apiTransport(draft),
tradeTerm: draft.tradeTerm || undefined,
destinationCountryCode: draft.destinationCountryCode.trim().toUpperCase(),
@@ -246,8 +246,8 @@ export const useFirstLegOrdersStore = defineStore('client-first-leg-orders', ()
return loadDetail(String(createdId))
}
async function submit(draft: FirstLegOrderDraft, customerName: string, id?: string, companyAuditStatus: MerchantTenantStatus = 'ACTIVE') {
if (companyAuditStatus !== 'ACTIVE') throw new Error('企业资料尚未审核通过,请先完善企业资料并等待平台审核')
async function submit(draft: FirstLegOrderDraft, customerName: string, id?: string, companyAuditStatus?: MerchantTenantStatus) {
if (companyAuditStatus && companyAuditStatus !== 'ACTIVE') throw new Error('企业资料尚未审核通过,请先完善企业资料并等待平台审核')
const order = await saveDraft(draft, customerName, id)
await submitMerchantFirstLegOrder(order.id)
return loadDetail(order.id)
@@ -33,13 +33,21 @@ test('exposes real list, draft, submit, and detail workflow entries without canc
})
test('blocks order submission before company approval while preserving draft actions', () => {
assert.match(source, /companyApproved/)
assert.match(source, /companySubmissionBlocked/)
assert.match(source, /companyAuditLoaded\.value = true/)
assert.match(source, /企业资料待审核,当前仅可保存订单草稿/)
assert.match(source, /暂时无法提交订单/)
assert.match(source, /router\.push\('\/company\/profile'\)/)
assert.match(source, /auth\.hasPermission\('order:first-leg:submit'\)/)
})
test('defers approval checks to the server when the member cannot read the company profile', () => {
assert.match(source, /const canViewCompanyProfile = computed/)
assert.match(source, /const companySubmissionBlocked = computed\(\(\) => companyAuditLoaded\.value/)
assert.match(source, /const knownCompanyAuditStatus = companyAuditLoaded\.value \? tenantStore\.company\.auditStatus : undefined/)
assert.match(source, /store\.submit\([^\n]*knownCompanyAuditStatus\)/)
})
test('renders dynamic cargo lines and responsive drawers', () => {
assert.match(source, /v-for="\(item, index\) in form\.items"/)
assert.match(source, /function addCargoItem\(\)/)
@@ -77,7 +85,7 @@ test('keeps desktop query actions on the first row and only shows the qualificat
assert.match(source, /\.first-leg-query-toolbar \{ flex-wrap: nowrap; \}/)
assert.match(source, /query-toolbar__filters\) \{ display: grid; min-width: 0; flex: 1 1 auto;/)
assert.match(source, /query-toolbar__actions\) \{ align-self: flex-start; flex-wrap: nowrap; margin-left: auto;/)
assert.match(source, /v-if="!companyApproved && !supportingDataErrors\.some/)
assert.match(source, /v-if="companySubmissionBlocked && !supportingDataErrors\.some/)
assert.doesNotMatch(source, /企业资料已审核,可正常提交订单/)
assert.doesNotMatch(source, /order-eligibility\.is-approved/)
})
@@ -29,7 +29,9 @@ const auth = useAuthStore()
const route = useRoute()
const router = useRouter()
const isCreatePage = computed(() => route.name === 'first-leg-order-create')
const companyApproved = computed(() => tenantStore.company.auditStatus === 'ACTIVE')
const canViewCompanyProfile = computed(() => auth.hasPermission('company:profile:view'))
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)
@@ -195,10 +197,11 @@ const emptyDescription = computed(() => hasActiveFilters.value ? '没有符合
async function loadPageData() {
supportingDataLoading.value = true
supportingDataErrors.value = []
companyAuditLoaded.value = false
availableShippers.value = []
availableConsignees.value = []
const companyRequest = auth.hasPermission('company:profile:view')
const companyRequest = canViewCompanyProfile.value
? tenantStore.loadCompanyContext()
: Promise.resolve()
const [ordersResult, partiesResult, companyResult] = await Promise.allSettled([
@@ -213,7 +216,9 @@ async function loadPageData() {
} else {
supportingDataErrors.value.push(`往来单位加载失败:${errorMessage(partiesResult.reason)}`)
}
if (companyResult.status === 'rejected') {
if (canViewCompanyProfile.value && companyResult.status === 'fulfilled') {
companyAuditLoaded.value = true
} else if (companyResult.status === 'rejected') {
supportingDataErrors.value.push(`企业资料加载失败:${errorMessage(companyResult.reason)}`)
}
if (ordersResult.status === 'rejected' && !store.loadError) {
@@ -510,7 +515,7 @@ async function saveDraft() {
async function submitOrder() {
if (saving.value) return
if (!companyApproved.value) {
if (companySubmissionBlocked.value) {
try {
await ElMessageBox.confirm('企业资料审核通过后才能提交订单。您可以继续保存草稿,或前往企业资料核对并补充信息。', '暂时无法提交订单', {
type: 'warning',
@@ -530,7 +535,8 @@ async function submitOrder() {
try {
saving.value = true
const order = await store.submit(cloneDraft(form), tenantStore.company.companyName, editingId.value, tenantStore.company.auditStatus)
const knownCompanyAuditStatus = companyAuditLoaded.value ? tenantStore.company.auditStatus : undefined
const order = await store.submit(cloneDraft(form), tenantStore.company.companyName, editingId.value, knownCompanyAuditStatus)
markEditorClean()
editorOpen.value = false
if (isCreatePage.value) await router.push({ name: 'first-leg-orders' })
@@ -569,10 +575,10 @@ async function searchOrders(model: QueryModel) {
<div><strong>订单列表</strong><span>共 {{ filteredOrders.length }} 条</span></div>
<el-button v-if="auth.hasPermission('order:first-leg:create')" type="primary" :disabled="availableWarehouses.length === 0" @click="openCreate"><FilePlus2 :size="17" />创建订单</el-button>
</div>
<div v-if="!companyApproved && !supportingDataErrors.some((message) => message.startsWith('企业资料'))" class="order-eligibility is-pending" role="status">
<div v-if="companySubmissionBlocked && !supportingDataErrors.some((message) => message.startsWith('企业资料'))" class="order-eligibility is-pending" role="status">
<AlertTriangle :size="20" />
<div><strong>{{ canCreateOrder ? '企业资料待审核,当前仅可保存订单草稿' : '企业资料待审核,当前订单为只读' }}</strong><span>{{ canCreateOrder ? '平台审核通过后即可提交;您仍可创建、编辑和保存草稿。' : '您当前拥有只读权限,可以继续查看订单进展。' }}</span></div>
<el-button v-if="!companyApproved && auth.hasPermission('company:profile:view')" link type="primary" @click="router.push('/company/profile')">查看企业资料</el-button>
<el-button v-if="canViewCompanyProfile" link type="primary" @click="router.push('/company/profile')">查看企业资料</el-button>
</div>
<div v-if="supportingDataErrors.length" class="supporting-data-error" role="alert">
<AlertTriangle :size="20" />
@@ -711,7 +717,7 @@ async function searchOrders(model: QueryModel) {
<p class="cargo-tip">FBA 货物必须填写 SKU、UPC、货件编号和跟踪编号;提交前请核对重量与体积。</p>
</section>
</el-form>
<template #footer><div class="editor-actions"><el-button :disabled="saving" @click="closeEditor">返回</el-button><el-button v-if="editingId ? canEditOrder : canCreateOrder" :loading="saving" @click="saveDraft">保存草稿</el-button><el-button v-if="auth.hasPermission('order:first-leg:submit')" type="primary" :loading="saving" @click="submitOrder"><Send :size="16" />{{ companyApproved ? '提交订单' : '提交前需完成资料审核' }}</el-button></div></template>
<template #footer><div class="editor-actions"><el-button :disabled="saving" @click="closeEditor">返回</el-button><el-button v-if="editingId ? canEditOrder : canCreateOrder" :loading="saving" @click="saveDraft">保存草稿</el-button><el-button v-if="auth.hasPermission('order:first-leg:submit')" type="primary" :loading="saving" @click="submitOrder"><Send :size="16" />{{ companySubmissionBlocked ? '提交前需完成资料审核' : '提交订单' }}</el-button></div></template>
</FirstLegOrderEditorShell>
<el-drawer v-model="detailOpen" class="first-leg-detail" title="订单详情" size="680px">