优化
This commit is contained in:
@@ -11,7 +11,6 @@ import {
|
||||
downloadTenantQualification,
|
||||
createPlatformTenantImpersonation,
|
||||
getPlatformTenant,
|
||||
getPlatformTenantDimensionInitializationStatus,
|
||||
listEnterpriseReviews,
|
||||
listPlatformTenants,
|
||||
listTenantQualifications,
|
||||
@@ -24,8 +23,6 @@ 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'
|
||||
@@ -64,7 +61,6 @@ 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(),
|
||||
@@ -79,12 +75,6 @@ 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 {
|
||||
@@ -155,26 +145,6 @@ 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({
|
||||
@@ -215,7 +185,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(1120px, calc(100vw - 28px))'")
|
||||
expect(customersSource).toContain("'min(680px, 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"')
|
||||
@@ -314,46 +284,6 @@ 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 })
|
||||
|
||||
@@ -13,7 +13,6 @@ import {
|
||||
createPlatformTenantImpersonation,
|
||||
downloadTenantQualification,
|
||||
getPlatformTenant,
|
||||
getPlatformTenantDimensionInitializationStatus,
|
||||
listEnterpriseReviews,
|
||||
listPlatformTenantOrderCounts,
|
||||
listPlatformTenants,
|
||||
@@ -33,14 +32,6 @@ import {
|
||||
type PlatformTenantResource,
|
||||
type PlatformTenantStatus,
|
||||
} from '@/modules/iam'
|
||||
import {
|
||||
getCustomerDimensionConfig,
|
||||
updateCustomerDimensionConfig,
|
||||
updateCustomerDimensionDefaultCalculationMode,
|
||||
type CustomerDimensionBoxType,
|
||||
type CustomerDimensionCalculationMode,
|
||||
} from '@/modules/dimension-config'
|
||||
import { createCustomerChargeRule, setCustomerChargeRuleDefault } from '@/modules/fba-pricing'
|
||||
import ApiErrorMeta from '@/shared/components/ApiErrorMeta.vue'
|
||||
import OperationErrorToast from '@/shared/components/OperationErrorToast.vue'
|
||||
import CopyButton from '@/shared/components/CopyButton.vue'
|
||||
@@ -145,25 +136,6 @@ const queryFields: QueryField[] = [
|
||||
]
|
||||
|
||||
type CustomerForm = CreatePlatformTenantRequest & { unifiedSocialCreditCode: string }
|
||||
type CreationBillingRow = {
|
||||
boxType: CustomerDimensionBoxType
|
||||
boxTypeName: string
|
||||
innerLengthCm: number
|
||||
innerWidthCm: number
|
||||
innerHeightCm: number
|
||||
edgeAdditionCm: number
|
||||
coefficient: number
|
||||
weight: number
|
||||
remark: string
|
||||
}
|
||||
|
||||
const newCreationBillingRows = (): CreationBillingRow[] => [
|
||||
{ boxType: 'SMALL', boxTypeName: '小箱', innerLengthCm: 30, innerWidthCm: 20, innerHeightCm: 20, edgeAdditionCm: 2, coefficient: 1.1, weight: 5, remark: '标准小箱' },
|
||||
{ boxType: 'MEDIUM', boxTypeName: '中箱', innerLengthCm: 40, innerWidthCm: 30, innerHeightCm: 30, edgeAdditionCm: 2, coefficient: 1.1, weight: 8, remark: '标准中箱' },
|
||||
{ boxType: 'LARGE', boxTypeName: '大箱', innerLengthCm: 50, innerWidthCm: 40, innerHeightCm: 40, edgeAdditionCm: 2, coefficient: 1.1, weight: 12, remark: '标准大箱' },
|
||||
{ boxType: 'EXTRA_LARGE', boxTypeName: '特大箱', innerLengthCm: 60, innerWidthCm: 50, innerHeightCm: 50, edgeAdditionCm: 2, coefficient: 1.1, weight: 15, remark: '特大箱' },
|
||||
]
|
||||
|
||||
const emptyForm = (): CustomerForm => ({
|
||||
tenantName: '', legalName: '', legalRepresentative: '', unifiedSocialCreditCode: '', contactName: '', contactPhone: '',
|
||||
contactEmail: '', contactWechat: '', servicePackage: '基础版', salesOwnerName: '', paymentTermDays: 30,
|
||||
@@ -171,11 +143,6 @@ const emptyForm = (): CustomerForm => ({
|
||||
registeredAddress: '', internalRemark: '',
|
||||
})
|
||||
const form = ref(emptyForm())
|
||||
const creationBillingEnabled = ref(false)
|
||||
const creationBillingDefaultMode = ref<Extract<CustomerDimensionCalculationMode, 'ADD' | 'MULTIPLY'>>('ADD')
|
||||
const creationBillingRows = ref<CreationBillingRow[]>(newCreationBillingRows())
|
||||
const creationBillingSession = ref(false)
|
||||
const savedCreationBillingSnapshot = ref('')
|
||||
const rules: FormRules = {
|
||||
tenantName: [{ required: true, message: '请输入公司名称', trigger: 'blur' }],
|
||||
legalName: [{ required: true, message: '请输入营业执照上的企业名称', trigger: 'blur' }],
|
||||
@@ -247,13 +214,9 @@ let loadSequence = 0
|
||||
let reviewLoadSequence = 0
|
||||
let editorLoadSequence = 0
|
||||
const editorTitle = computed(() => editingId.value ? '编辑客户' : '新增客户')
|
||||
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 editorDialogWidth = computed(() => editingId.value
|
||||
? 'min(640px, calc(100vw - 28px))'
|
||||
: 'min(680px, calc(100vw - 28px))')
|
||||
const canUploadQualifications = computed(() => !editorStatus.value || ['PROFILE_REQUIRED', 'REJECTED'].includes(editorStatus.value))
|
||||
const canSubmitReview = computed(() => Boolean(editingId.value) && ['PROFILE_REQUIRED', 'REJECTED'].includes(editorStatus.value || ''))
|
||||
const uploadingQualifications = computed(() => qualifications.value.some((item) => item.uploading))
|
||||
@@ -423,110 +386,12 @@ function clearQualificationPreviews() {
|
||||
function resetEditor() {
|
||||
clearQualificationPreviews()
|
||||
form.value = emptyForm(); qualifications.value = newQualificationDrafts(); creditCodeEstablished.value = false; qualificationLoadError.value = ''; savedFormSnapshot.value = ''
|
||||
creationBillingEnabled.value = false
|
||||
creationBillingDefaultMode.value = 'ADD'
|
||||
creationBillingRows.value = newCreationBillingRows()
|
||||
creationBillingSession.value = false
|
||||
savedCreationBillingSnapshot.value = ''
|
||||
}
|
||||
function openCreate() {
|
||||
editorLoadSequence += 1
|
||||
editingId.value = null; editorStatus.value = null; resetEditor(); creationBillingSession.value = true; editorOpen.value = true
|
||||
editingId.value = null; editorStatus.value = null; resetEditor(); editorOpen.value = true
|
||||
nextTick(() => formRef.value?.clearValidate())
|
||||
}
|
||||
|
||||
function creationBillingSnapshot() {
|
||||
return JSON.stringify({
|
||||
enabled: creationBillingEnabled.value,
|
||||
defaultMode: creationBillingDefaultMode.value,
|
||||
rows: creationBillingRows.value,
|
||||
})
|
||||
}
|
||||
|
||||
function validateCreationBillingRules() {
|
||||
if (!creationBillingEnabled.value) return true
|
||||
if (!canConfigureCreationBilling.value) {
|
||||
ElMessage.error('当前账号缺少箱规尺寸或计费规则编辑权限,无法随客户一并保存')
|
||||
return false
|
||||
}
|
||||
if (creationBillingRows.value.some((row) => ![
|
||||
row.innerLengthCm, row.innerWidthCm, row.innerHeightCm,
|
||||
].every((value) => Number.isFinite(value) && value >= 0.001))) {
|
||||
ElMessage.error('四档箱规的内径长、宽、高必须大于 0')
|
||||
return false
|
||||
}
|
||||
if (creationBillingRows.value.some((row) => !Number.isFinite(row.edgeAdditionCm)
|
||||
|| row.edgeAdditionCm < 0.001 || row.edgeAdditionCm > 100)) {
|
||||
ElMessage.error('四档箱规的单边加宽必须在 0.001 至 100cm 之间')
|
||||
return false
|
||||
}
|
||||
if (creationBillingRows.value.some((row) => !Number.isFinite(row.coefficient) || row.coefficient < 0.001)) {
|
||||
ElMessage.error('四档箱规的权重系数必须大于 0')
|
||||
return false
|
||||
}
|
||||
if (creationBillingRows.value.some((row) => !Number.isFinite(row.weight) || row.weight < 0.001)) {
|
||||
ElMessage.error('四档箱规的重量必须大于 0')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
function wait(milliseconds: number) {
|
||||
return new Promise((resolve) => window.setTimeout(resolve, milliseconds))
|
||||
}
|
||||
|
||||
async function waitForDimensionInitialization(tenantId: string) {
|
||||
for (let attempt = 0; attempt < 20; attempt += 1) {
|
||||
const status = await getPlatformTenantDimensionInitializationStatus(tenantId)
|
||||
if (status.status === 'READY') return
|
||||
if (status.status === 'FAILED') {
|
||||
throw new Error(status.lastErrorMessage || '客户默认箱规初始化失败')
|
||||
}
|
||||
await wait(500)
|
||||
}
|
||||
throw new Error('客户默认箱规仍在初始化,请稍后再次点击保存')
|
||||
}
|
||||
|
||||
async function saveCreationBillingRules(tenantId: string, customerName: string) {
|
||||
if (!creationBillingEnabled.value || creationBillingSnapshot() === savedCreationBillingSnapshot.value) return
|
||||
await waitForDimensionInitialization(tenantId)
|
||||
const current = await getCustomerDimensionConfig(tenantId)
|
||||
const versions = new Map(current.rules.map((rule) => [rule.boxType, rule.version]))
|
||||
await updateCustomerDimensionConfig(tenantId, {
|
||||
rules: creationBillingRows.value.map((row) => ({
|
||||
boxType: row.boxType,
|
||||
innerLengthCm: row.innerLengthCm,
|
||||
innerWidthCm: row.innerWidthCm,
|
||||
innerHeightCm: row.innerHeightCm,
|
||||
edgeAdditionCm: row.edgeAdditionCm,
|
||||
remark: row.remark || null,
|
||||
version: versions.get(row.boxType) ?? 0,
|
||||
})),
|
||||
})
|
||||
const firstCoefficient = creationBillingRows.value[0].coefficient
|
||||
const created = await createCustomerChargeRule(tenantId, {
|
||||
ruleCode: `RULE-${Date.now()}`,
|
||||
customerId: tenantId,
|
||||
customerName,
|
||||
businessType: 'FBA',
|
||||
chargeMode: 'CBM',
|
||||
taxMode: 'TAX_INCLUDED',
|
||||
conversionFactor: 167,
|
||||
lengthFactor: firstCoefficient,
|
||||
widthFactor: firstCoefficient,
|
||||
heightFactor: firstCoefficient,
|
||||
defaultRule: false,
|
||||
roundingScale: 2,
|
||||
roundingMode: 'HALF_UP',
|
||||
currency: 'CNY',
|
||||
effectiveFrom: new Date().toISOString().slice(0, 19),
|
||||
effectiveTo: null,
|
||||
remark: creationBillingRows.value.map((row) => `${row.boxType}:${row.innerLengthCm},${row.innerWidthCm},${row.innerHeightCm};系数:${row.coefficient};重量:${row.weight};${row.boxTypeName}:${row.remark || '—'}`).join(';'),
|
||||
})
|
||||
await setCustomerChargeRuleDefault(tenantId, created.id)
|
||||
await updateCustomerDimensionDefaultCalculationMode(tenantId, creationBillingDefaultMode.value)
|
||||
savedCreationBillingSnapshot.value = creationBillingSnapshot()
|
||||
}
|
||||
function mapTenantToForm(row: PlatformTenantResource): CustomerForm {
|
||||
return {
|
||||
tenantName: row.tenantName, legalName: row.legalName || '', legalRepresentative: row.legalRepresentative || '', unifiedSocialCreditCode: row.unifiedSocialCreditCode || '',
|
||||
@@ -697,7 +562,6 @@ async function uploadSelectedQualifications(tenantId: string) {
|
||||
|
||||
async function saveCustomer() {
|
||||
if (!formRef.value || !(await formRef.value.validate().catch(() => false))) return
|
||||
if (!validateCreationBillingRules()) return
|
||||
if (!requiredQualificationPackageComplete.value) {
|
||||
ElMessage.warning(`请完整保存必填业务项:${missingQualificationPackageItems.value.join('、')}`)
|
||||
return
|
||||
@@ -728,15 +592,6 @@ async function saveCustomer() {
|
||||
editorStatus.value = saved.status
|
||||
savedFormSnapshot.value = JSON.stringify(form.value)
|
||||
const failures = hadSelectedQualifications ? await uploadSelectedQualifications(String(saved.id)) : []
|
||||
if (creationBillingSession.value && creationBillingEnabled.value) {
|
||||
try {
|
||||
await saveCreationBillingRules(String(saved.id), saved.tenantName)
|
||||
} catch (error) {
|
||||
ElMessage.error(`客户资料已保存,但箱规尺寸和权重系数未完整保存:${getApiErrorMessage(error, '请稍后再次点击保存')}`)
|
||||
await loadCustomers()
|
||||
return
|
||||
}
|
||||
}
|
||||
if (failures.length) {
|
||||
ElMessage.warning(`客户资料已保存,但${failures.join('、')}上传失败;失败文件已保留,请修正后再次保存`)
|
||||
} else if (hadBusinessLicense && hadLegalPersonId) {
|
||||
@@ -1235,26 +1090,6 @@ onMounted(() => { void loadCustomers() })
|
||||
<el-form-item label="账期(天)" prop="paymentTermDays" required><el-input-number v-model="form.paymentTermDays" :min="0" :max="365" controls-position="right" placeholder="请输入账期天数" /></el-form-item>
|
||||
</div><el-form-item label="内部备注" prop="internalRemark"><el-input v-model="form.internalRemark" type="textarea" :rows="3" placeholder="请输入仅平台人员可见的客户备注" maxlength="1000" show-word-limit /></el-form-item></FormSection>
|
||||
|
||||
<FormSection v-if="creationBillingSession" class="creation-billing-section" title="客户计费规则" description="创建客户时一次配置箱规尺寸和权重系数,保存后无需再进入客户详情补录。">
|
||||
<div class="creation-billing-heading">
|
||||
<el-checkbox v-model="creationBillingEnabled" :disabled="!canConfigureCreationBilling">按箱规尺寸计算</el-checkbox>
|
||||
<el-radio-group v-if="creationBillingEnabled" v-model="creationBillingDefaultMode" aria-label="默认箱规计算方式">
|
||||
<el-radio-button value="ADD">箱规尺寸</el-radio-button>
|
||||
<el-radio-button value="MULTIPLY">权重系数</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<p v-if="!canConfigureCreationBilling" class="creation-billing-permission-hint">当前账号需同时具备箱规尺寸和客户计费规则编辑权限,才能在创建时配置。</p>
|
||||
<template v-if="creationBillingEnabled">
|
||||
<p class="creation-billing-hint">四档尺寸与权重系数会随客户资料一并保存;所选计算方式将设为客户默认规则。</p>
|
||||
<div class="creation-billing-table-wrap">
|
||||
<table class="creation-billing-table">
|
||||
<thead><tr><th>箱规类型</th><th>内径长(cm)</th><th>内径宽(cm)</th><th>内径高(cm)</th><th>单边加宽(cm)</th><th>权重系数</th><th>重量(kg)</th><th>备注</th></tr></thead>
|
||||
<tbody><tr v-for="row in creationBillingRows" :key="row.boxType"><th scope="row">{{ row.boxTypeName }}</th><td><el-input-number v-model="row.innerLengthCm" :min="0.001" :precision="3" :controls="false" /></td><td><el-input-number v-model="row.innerWidthCm" :min="0.001" :precision="3" :controls="false" /></td><td><el-input-number v-model="row.innerHeightCm" :min="0.001" :precision="3" :controls="false" /></td><td><el-input-number v-model="row.edgeAdditionCm" :min="0.001" :max="100" :precision="3" :controls="false" /></td><td><el-input-number v-model="row.coefficient" :min="0.001" :precision="3" :controls="false" /></td><td><el-input-number v-model="row.weight" :min="0.001" :precision="3" :controls="false" /></td><td><el-input v-model="row.remark" maxlength="500" placeholder="选填" /></td></tr></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
||||
</FormSection>
|
||||
|
||||
<FormSection class="qualification-section" title="企业认证资料" description="营业执照和18位统一社会信用代码为必填业务项;法人身份证、货代资质证书和银行开户许可证均为选填。"><div v-if="editorStatus" class="qualification-heading"><el-tag :type="statusMeta(editorStatus).type" effect="light">{{ statusMeta(editorStatus).label }}</el-tag></div>
|
||||
<el-form-item label="统一社会信用代码" prop="unifiedSocialCreditCode" required class="qualification-credit-code"><el-input v-model="form.unifiedSocialCreditCode" :disabled="creditCodeEstablished" placeholder="请输入18位大写字母或数字" maxlength="18" @input="form.unifiedSocialCreditCode = String($event).toUpperCase()" /><small>{{ creditCodeEstablished ? '已登记的统一社会信用代码不可直接修改。' : '必须与营业执照一致,并随营业执照共同保存;法人身份证为选填。' }}</small></el-form-item>
|
||||
<div v-if="qualificationLoadError" class="qualification-load-error" role="alert">{{ qualificationLoadError }}<el-button v-if="editingId" link type="primary" @click="loadEditorQualifications(editingId)">重试</el-button></div>
|
||||
@@ -1400,7 +1235,6 @@ onMounted(() => { void loadCustomers() })
|
||||
:deep(.customer-list-table .el-tag--danger) { background:linear-gradient(135deg,rgba(239,68,68,.15),rgba(220,38,38,.1)); color:#dc2626; }
|
||||
:deep(.customer-list-table .el-tag--info) { background:linear-gradient(135deg,rgba(102,126,234,.15),rgba(118,75,162,.1)); color:#667eea; }
|
||||
.capital-input { display:grid; grid-template-columns:minmax(0,1fr) 130px; width:100%; gap:8px; } .capital-input :deep(.el-input-number),.capital-input :deep(.el-select) { width:100%; }
|
||||
.creation-billing-heading { display:flex; min-height:36px; align-items:center; justify-content:space-between; flex-wrap:wrap; gap:12px; } .creation-billing-heading :deep(.el-checkbox) { margin-right:0; font-weight:650; } .creation-billing-hint,.creation-billing-permission-hint { margin:8px 0 0; color:var(--cc-muted); font-size:12px; line-height:1.6; } .creation-billing-permission-hint { color:var(--el-color-warning); } .creation-billing-table-wrap { overflow-x:auto; margin-top:12px; border:1px solid var(--cc-border); border-radius:8px; } .creation-billing-table { width:100%; min-width:1040px; border-collapse:collapse; } .creation-billing-table th,.creation-billing-table td { padding:9px 10px; border-bottom:1px solid var(--cc-border); text-align:left; white-space:nowrap; } .creation-billing-table thead th { background:#f7f8fc; color:var(--cc-muted); font-size:12px; } .creation-billing-table tbody tr:last-child th,.creation-billing-table tbody tr:last-child td { border-bottom:0; } .creation-billing-table tbody th { color:var(--cc-text); font-size:13px; } .creation-billing-table :deep(.el-input-number) { width:112px; } .creation-billing-table :deep(.el-input) { width:130px; }
|
||||
.qualification-heading { display:flex; justify-content:flex-end; margin-bottom:12px; } .qualification-hint { margin:12px 0 0; color:var(--cc-muted); font-size:12px; line-height:1.6; } .qualification-hint span,.qualification-label > span { color:var(--el-color-danger); }
|
||||
.qualification-credit-code { max-width:420px; margin-top:16px; } .qualification-credit-code small,.field-hint { display:block; margin-top:6px; color:var(--cc-muted); font-size:11px; line-height:1.5; }
|
||||
.qualification-table { overflow:hidden; margin-top:14px; border:1px solid var(--cc-border); border-radius:8px; } .qualification-row { display:grid; grid-template-columns:170px minmax(0,1fr) 220px; min-width:0; border-top:1px solid var(--cc-border); } .qualification-row:first-child { border-top:0; } .qualification-row > * { display:flex; min-width:0; min-height:68px; align-items:center; padding:12px 14px; } .qualification-header { min-height:44px; background:#f7f8fc; color:var(--cc-muted); font-size:12px; font-weight:600; } .qualification-header > * { min-height:44px; }
|
||||
|
||||
@@ -147,16 +147,6 @@ export interface PlatformTenantCreationResponse {
|
||||
temporaryPassword: string
|
||||
}
|
||||
|
||||
export interface CustomerDimensionInitializationStatus {
|
||||
tenantId: number | string
|
||||
status: 'PENDING' | 'READY' | 'FAILED'
|
||||
attemptCount: number
|
||||
nextRetryAt: string | null
|
||||
lastErrorCode: string | null
|
||||
lastErrorMessage: string | null
|
||||
updatedAt: string | null
|
||||
}
|
||||
|
||||
export interface ResetPlatformTenantOwnerPasswordRequest {
|
||||
newPassword: string
|
||||
forceChangePassword: boolean
|
||||
@@ -373,10 +363,6 @@ export function getPlatformTenant(tenantId: number | string) {
|
||||
return getData<PlatformTenantResource>(`/platform/v1/iam/tenants/${tenantId}`)
|
||||
}
|
||||
|
||||
export function getPlatformTenantDimensionInitializationStatus(tenantId: number | string) {
|
||||
return getData<CustomerDimensionInitializationStatus>(`/platform/v1/iam/tenants/${tenantId}/dimension-initialization`)
|
||||
}
|
||||
|
||||
export async function checkPlatformTenantContactPhone(contactPhone: string, excludeTenantId?: number | string) {
|
||||
const response = await http.get<ApiEnvelope<boolean>, ApiEnvelope<boolean>>(
|
||||
'/platform/v1/iam/tenants/contact-phone-availability',
|
||||
|
||||
Reference in New Issue
Block a user