This commit is contained in:
2026-09-10 11:55:58 +08:00
parent ae7d8fb22e
commit e3b2552d8d
2 changed files with 77 additions and 5 deletions
@@ -11,6 +11,7 @@ import {
downloadTenantQualification,
createPlatformTenantImpersonation,
getPlatformTenant,
getPlatformTenantDimensionInitializationStatus,
listEnterpriseReviews,
listPlatformTenants,
listTenantQualifications,
@@ -23,6 +24,8 @@ import {
type EnterpriseReview,
type PlatformTenantResource,
} from '@/modules/iam'
import { getCustomerDimensionConfig, updateCustomerDimensionConfig, updateCustomerDimensionDefaultCalculationMode } from '@/modules/dimension-config'
import { createCustomerChargeRule, setCustomerChargeRuleDefault } from '@/modules/fba-pricing'
import { createPlatformExportTask } from '@/modules/downloads'
import { useAccessPreviewStore } from '@/core/access/accessPreview'
import { useAuthStore } from '@/core/auth/auth'
@@ -61,6 +64,7 @@ vi.mock('@/modules/iam', () => ({
createPlatformTenantImpersonation: vi.fn(),
downloadTenantQualification: vi.fn(),
getPlatformTenant: vi.fn(),
getPlatformTenantDimensionInitializationStatus: vi.fn(),
listEnterpriseReviews: vi.fn(),
listPlatformTenantOrderCounts: vi.fn(),
listPlatformTenants: vi.fn(),
@@ -75,6 +79,12 @@ vi.mock('@/modules/iam', () => ({
}))
vi.mock('@/modules/downloads', () => ({ createPlatformExportTask: vi.fn() }))
vi.mock('@/modules/dimension-config', () => ({
getCustomerDimensionConfig: vi.fn(),
updateCustomerDimensionConfig: vi.fn(),
updateCustomerDimensionDefaultCalculationMode: vi.fn(),
}))
vi.mock('@/modules/fba-pricing', () => ({ createCustomerChargeRule: vi.fn(), setCustomerChargeRuleDefault: vi.fn() }))
vi.mock('@/shared/components/exportTaskCreatedDialog', () => ({ showExportTaskCreatedDialog: vi.fn() }))
class ResizeObserverStub implements ResizeObserver {
@@ -145,6 +155,26 @@ describe('CustomersView', () => {
page: 1, size: 10, total: 1, pages: 1,
})
vi.mocked(getPlatformTenant).mockResolvedValue(fullTenant)
vi.mocked(getPlatformTenantDimensionInitializationStatus).mockResolvedValue({
tenantId: fullTenant.id, status: 'READY', attemptCount: 0, nextRetryAt: null,
lastErrorCode: null, lastErrorMessage: null, updatedAt: '2026-08-01T10:00:00',
})
const dimensionRules = [
['SMALL', '小箱', 30, 20, 20],
['MEDIUM', '中箱', 40, 30, 30],
['LARGE', '大箱', 50, 40, 40],
['EXTRA_LARGE', '特大箱', 60, 50, 50],
].map(([boxType, boxTypeName, length, width, height], index) => ({
id: `dimension-${index}`, boxType, boxTypeName, innerLengthCm: length, innerWidthCm: width,
innerHeightCm: height, edgeAdditionCm: 2, outerLengthCm: Number(length) + 4,
outerWidthCm: Number(width) + 4, outerHeightCm: Number(height) + 4, volumeCbm: 0.01,
remark: String(boxTypeName), version: 0, createdAt: null, updatedAt: null,
}))
vi.mocked(getCustomerDimensionConfig).mockResolvedValue({ tenantId: fullTenant.id, defaultCalculationMode: 'ACTUAL', rules: dimensionRules } as never)
vi.mocked(updateCustomerDimensionConfig).mockResolvedValue({ tenantId: fullTenant.id, defaultCalculationMode: 'ACTUAL', rules: dimensionRules } as never)
vi.mocked(updateCustomerDimensionDefaultCalculationMode).mockResolvedValue({ tenantId: fullTenant.id, defaultCalculationMode: 'ADD', rules: dimensionRules } as never)
vi.mocked(createCustomerChargeRule).mockResolvedValue({ id: 'charge-rule-1' } as never)
vi.mocked(setCustomerChargeRuleDefault).mockResolvedValue(undefined)
vi.mocked(listTenantQualifications).mockResolvedValue([])
vi.mocked(listEnterpriseReviews).mockResolvedValue([])
vi.mocked(createPlatformTenant).mockResolvedValue({
@@ -185,7 +215,7 @@ describe('CustomersView', () => {
it('matches the frozen prototype containers for customer operations', () => {
expect(customersSource).toContain("router.push({ name: 'customer-detail'")
expect(customersSource).toContain("'min(640px, calc(100vw - 28px))'")
expect(customersSource).toContain("'min(680px, calc(100vw - 28px))'")
expect(customersSource).toContain("'min(1120px, calc(100vw - 28px))'")
expect(customersSource).toContain('width="min(520px, calc(100vw - 28px))" class="customer-password-dialog"')
expect(customersSource).toContain('width="min(1040px, calc(100vw - 28px))" class="review-decision-dialog"')
expect(customersSource).not.toContain('<AppDrawer v-model="editorOpen"')
@@ -284,6 +314,46 @@ describe('CustomersView', () => {
)
})
it('saves carton dimensions and all four weight coefficients during customer creation', async () => {
const auth = useAuthStore()
auth.permissions = [...auth.permissions, 'platform:customer:create', 'firstleg:dimension-config:view', 'firstleg:dimension-config:edit', 'firstleg:fba-pricing:rule:edit']
useAccessPreviewStore().initializeAfterLogin()
const wrapper = mount(CustomersView, { attachTo: document.body, global: { plugins: [pinia, ElementPlus] } })
await flushPromises()
;[...document.querySelectorAll<HTMLButtonElement>('button')]
.find((button) => button.textContent?.trim() === '新增客户')?.click()
await flushPromises()
await wrapper.get('input[placeholder="请输入工商登记公司名称"]').setValue(fullTenant.tenantName)
await wrapper.get('input[placeholder="请输入营业执照上的企业名称"]').setValue(fullTenant.legalName)
await wrapper.get('input[placeholder="请输入法定代表人姓名"]').setValue(fullTenant.legalRepresentative)
await wrapper.get('input[placeholder="请输入主要联系人姓名"]').setValue(fullTenant.contactName)
await wrapper.get('input[placeholder="请输入公司联系电话"]').setValue(fullTenant.contactPhone)
await wrapper.get('input[placeholder="请输入18位大写字母或数字"]').setValue(fullTenant.unifiedSocialCreditCode)
const licenseFile = new File(['license'], 'license.png', { type: 'image/png' })
selectQualificationFile(0, licenseFile)
const billingCheckbox = [...document.querySelectorAll<HTMLElement>('.el-checkbox')]
.find((checkbox) => checkbox.textContent?.includes('按箱规尺寸计算'))
billingCheckbox?.click()
await flushPromises()
expect(document.body.textContent).toContain('权重系数')
expect(document.querySelectorAll('.creation-billing-table tbody tr')).toHaveLength(4)
;[...document.querySelectorAll<HTMLButtonElement>('button')]
.find((button) => button.textContent?.includes('保存资料和资质'))?.click()
await flushPromises()
expect(updateCustomerDimensionConfig).toHaveBeenCalledWith(fullTenant.id, expect.objectContaining({
rules: expect.arrayContaining([expect.objectContaining({ boxType: 'SMALL', innerLengthCm: 30, edgeAdditionCm: 2 })]),
}))
expect(createCustomerChargeRule).toHaveBeenCalledWith(fullTenant.id, expect.objectContaining({
customerId: fullTenant.id,
remark: expect.stringMatching(/SMALL:30,20,20;系数:1\.1;重量:5/),
}))
expect(setCustomerChargeRuleDefault).toHaveBeenCalledWith(fullTenant.id, 'charge-rule-1')
expect(updateCustomerDimensionDefaultCalculationMode).toHaveBeenCalledWith(fullTenant.id, 'ADD')
})
it('keeps the company contact phone editable without changing a member login phone', async () => {
const activeTenant = { ...fullTenant, status: 'ACTIVE' as const }
vi.mocked(listPlatformTenants).mockResolvedValue({ records: [activeTenant], page: 1, size: 10, total: 1, pages: 1 })
@@ -247,10 +247,12 @@ let loadSequence = 0
let reviewLoadSequence = 0
let editorLoadSequence = 0
const editorTitle = computed(() => editingId.value ? '编辑客户' : '新增客户')
const editorDialogWidth = computed(() => editingId.value
? 'min(640px, calc(100vw - 28px))'
: 'min(1120px, calc(100vw - 28px))')
const canConfigureCreationBilling = computed(() => access.can('firstleg:dimension-config:edit')
const editorDialogWidth = computed(() => creationBillingSession.value
? 'min(1120px, calc(100vw - 28px))'
: 'min(640px, calc(100vw - 28px))')
const canConfigureCreationBilling = computed(() => access.can('platform:customer:list')
&& access.can('firstleg:dimension-config:view')
&& access.can('firstleg:dimension-config:edit')
&& access.can('firstleg:fba-pricing:rule:edit'))
const canUploadQualifications = computed(() => !editorStatus.value || ['PROFILE_REQUIRED', 'REJECTED'].includes(editorStatus.value))
const canSubmitReview = computed(() => Boolean(editingId.value) && ['PROFILE_REQUIRED', 'REJECTED'].includes(editorStatus.value || ''))