优化
This commit is contained in:
@@ -6,7 +6,7 @@ import { Building2, CheckCircle2, Clock3, FileCheck2, FileText, Pencil, Trash2,
|
||||
import DataTable, { type DataTableColumn } from '@/components/common/DataTable.vue'
|
||||
import { useTenantAdministrationStore, type TenantCompanyProfile, type TenantQualification } from '@/modules/tenant-management/store/tenantAdministration'
|
||||
import { useAuthStore } from '@/modules/auth/public'
|
||||
import { formatQualificationFileSize, validateQualificationFile } from '@/modules/tenant-management/utils/qualificationFile'
|
||||
import { formatQualificationFileSize, qualificationUploadErrorMessage, validateQualificationFile } from '@/modules/tenant-management/utils/qualificationFile'
|
||||
import { qualificationStatusMeta } from '@/modules/tenant-management/utils/qualificationStatus'
|
||||
import {
|
||||
downloadQualificationImage,
|
||||
@@ -19,10 +19,10 @@ const store = useTenantAdministrationStore()
|
||||
const auth = useAuthStore()
|
||||
const companyApproved = computed(() => store.company.auditStatus === 'ACTIVE')
|
||||
const auditStatusCopy = computed(() => ({
|
||||
PROFILE_REQUIRED: { title: '请完善企业资质', detail: '四类必填资质提交审核前不能提交头程订单。' },
|
||||
PROFILE_REQUIRED: { title: '请完善企业资质', detail: '上传营业执照和法人身份证并提交审核后,才能提交头程订单。' },
|
||||
PENDING: { title: '企业资料正在等待平台审核', detail: '审核期间仍可维护资料和保存订单草稿;审核通过前不能提交订单。' },
|
||||
ACTIVE: { title: '企业资料已审核,下单资格已开通', detail: '您可以创建、保存并提交头程订单。' },
|
||||
REJECTED: { title: '企业资料审核未通过', detail: '请根据平台反馈替换对应资质,四类文件齐全后可重新提交审核。' },
|
||||
REJECTED: { title: '企业资料审核未通过', detail: '请根据平台反馈替换对应资质,必填文件齐全后可重新提交审核。' },
|
||||
DISABLED: { title: '企业账号已被平台停用', detail: '当前不能提交订单,请联系平台客服核实。' },
|
||||
})[store.company.auditStatus])
|
||||
const activeTab = ref('basic')
|
||||
@@ -44,7 +44,7 @@ const loading = ref(false)
|
||||
const loadError = ref('')
|
||||
const draft = reactive<TenantCompanyProfile>({ ...store.company })
|
||||
type QualificationUploadState = 'idle' | 'uploading' | 'uploaded' | 'error'
|
||||
type QualificationTypeDefinition = { value: MerchantQualificationType; label: string; description: string }
|
||||
type QualificationTypeDefinition = { value: MerchantQualificationType; label: string; description: string; required: boolean }
|
||||
type QualificationDraftItem = QualificationTypeDefinition & {
|
||||
file: File | null
|
||||
fileName: string
|
||||
@@ -57,11 +57,12 @@ type QualificationDraftItem = QualificationTypeDefinition & {
|
||||
}
|
||||
|
||||
const qualificationTypes: QualificationTypeDefinition[] = [
|
||||
{ value: 'BUSINESS_LICENSE', label: '营业执照', description: '上传清晰完整的营业执照文件。' },
|
||||
{ value: 'LEGAL_PERSON_ID', label: '法人身份证', description: '正反面请合并为一个 PDF、JPG 或 PNG 文件。' },
|
||||
{ value: 'FREIGHT_FORWARDER_CERTIFICATE', label: '货代资质证书', description: '上传当前有效的货代资质证明。' },
|
||||
{ value: 'BANK_ACCOUNT_LICENSE', label: '银行开户许可证', description: '上传企业对公账户开户证明。' },
|
||||
{ value: 'BUSINESS_LICENSE', label: '营业执照', description: '上传清晰完整的营业执照文件。', required: true },
|
||||
{ value: 'LEGAL_PERSON_ID', label: '法人身份证', description: '正反面请合并为一个 PDF、JPG 或 PNG 文件。', required: true },
|
||||
{ value: 'FREIGHT_FORWARDER_CERTIFICATE', label: '货代资质证书', description: '如有,可上传当前有效的货代资质证明。', required: false },
|
||||
{ value: 'BANK_ACCOUNT_LICENSE', label: '银行开户许可证', description: '如有,可上传企业对公账户开户证明。', required: false },
|
||||
]
|
||||
const requiredQualificationTypes = qualificationTypes.filter((type) => type.required)
|
||||
const qualificationDrafts = reactive<QualificationDraftItem[]>(qualificationTypes.map((type) => ({
|
||||
...type,
|
||||
file: null,
|
||||
@@ -80,11 +81,11 @@ const normalizedQualificationCertificateNo = computed(() => qualificationCertifi
|
||||
const businessLicenseFileSelected = computed(() => Boolean(
|
||||
qualificationDrafts.find((item) => item.value === 'BUSINESS_LICENSE')?.file,
|
||||
))
|
||||
const qualificationCreditCodeReady = computed(() => companyCreditCodeValid.value
|
||||
|| (businessLicenseFileSelected.value && unifiedSocialCreditCodePattern.test(normalizedQualificationCertificateNo.value)))
|
||||
const qualificationCreditCodeReady = computed(() => !businessLicenseFileSelected.value
|
||||
|| companyCreditCodeValid.value
|
||||
|| unifiedSocialCreditCodePattern.test(normalizedQualificationCertificateNo.value))
|
||||
const qualificationCreditCodeError = computed(() => {
|
||||
if (qualificationCreditCodeReady.value) return ''
|
||||
if (!businessLicenseFileSelected.value) return '请重新选择营业执照,并填写18位统一社会信用代码'
|
||||
return '请输入18位字母或数字组成的统一社会信用代码'
|
||||
})
|
||||
const canUploadQualification = computed(() => ['PROFILE_REQUIRED', 'REJECTED'].includes(store.company.auditStatus))
|
||||
@@ -98,22 +99,12 @@ const qualificationUploadDisabledReason = computed(() => {
|
||||
if (store.company.auditStatus === 'DISABLED') return '企业账号已停用,暂不能提交资质文件。'
|
||||
return ''
|
||||
})
|
||||
const missingQualificationTypes = computed(() => qualificationTypes
|
||||
.filter((type) => {
|
||||
const draftItem = qualificationDrafts.find((item) => item.value === type.value)
|
||||
return !uploadedQualificationTypes.value.has(type.value)
|
||||
&& !draftItem?.file
|
||||
&& draftItem?.uploadState !== 'uploaded'
|
||||
})
|
||||
.map((type) => type.label))
|
||||
const selectedQualificationCount = computed(() => qualificationDrafts.filter((item) => Boolean(item.file)).length)
|
||||
const canSaveQualificationBatch = computed(() => canUploadQualification.value
|
||||
&& selectedQualificationCount.value > 0
|
||||
&& missingQualificationTypes.value.length === 0
|
||||
&& qualificationCreditCodeReady.value)
|
||||
const qualificationSaveDisabledReason = computed(() => {
|
||||
if (!canUploadQualification.value) return qualificationUploadDisabledReason.value
|
||||
if (missingQualificationTypes.value.length) return `还需选择:${missingQualificationTypes.value.join('、')}`
|
||||
if (!qualificationCreditCodeReady.value) return qualificationCreditCodeError.value
|
||||
if (!selectedQualificationCount.value) return '请选择需要新增或替换的资质文件'
|
||||
return ''
|
||||
@@ -123,15 +114,15 @@ const qualificationDialogHint = computed(() => qualificationSaveDisabledReason.v
|
||||
: qualificationSaveDisabledReason.value)
|
||||
const canSubmitQualificationReview = computed(() => canUploadQualification.value
|
||||
&& companyCreditCodeValid.value
|
||||
&& qualificationTypes.every((type) => uploadedQualificationTypes.value.has(type.value)))
|
||||
&& requiredQualificationTypes.every((type) => uploadedQualificationTypes.value.has(type.value)))
|
||||
const qualificationReviewDisabledReason = computed(() => {
|
||||
if (!canUploadQualification.value) return qualificationUploadDisabledReason.value
|
||||
if (!companyCreditCodeValid.value) return '请先通过营业执照填写18位统一社会信用代码'
|
||||
const missing = qualificationTypes.filter((type) => !uploadedQualificationTypes.value.has(type.value)).map((type) => type.label)
|
||||
const missing = requiredQualificationTypes.filter((type) => !uploadedQualificationTypes.value.has(type.value)).map((type) => type.label)
|
||||
return missing.length ? `请先上传:${missing.join('、')}` : ''
|
||||
})
|
||||
const qualificationPanelHint = computed(() => canUploadQualification.value
|
||||
? '一次选择四类必填资质;文件保存不会自动送审,全部齐全后再提交企业审核。'
|
||||
? '营业执照、法人身份证为必填;货代资质证书、银行开户许可证可选填。文件保存后需另行提交审核。'
|
||||
: qualificationUploadDisabledReason.value)
|
||||
const profileFormRules: FormRules<TenantCompanyProfile> = {
|
||||
companyName: [{ required: true, whitespace: true, message: '请输入公司名称', trigger: 'blur' }],
|
||||
@@ -243,14 +234,14 @@ function qualificationSlotLabel(item: QualificationDraftItem) {
|
||||
const record = qualificationRecord(item.value)
|
||||
if (record?.status === 'PENDING') return '已上传'
|
||||
if (record?.status === 'REJECTED') return '需重新上传'
|
||||
return '待上传'
|
||||
return item.required ? '待上传' : '选填'
|
||||
}
|
||||
|
||||
function qualificationSlotTagType(item: QualificationDraftItem) {
|
||||
if (item.file) return 'warning'
|
||||
if (item.uploadState === 'uploaded' || qualificationRecord(item.value)?.status === 'PENDING') return 'success'
|
||||
if (item.uploadState === 'error' || qualificationRecord(item.value)?.status === 'REJECTED') return 'danger'
|
||||
return 'warning'
|
||||
return item.required ? 'warning' : 'info'
|
||||
}
|
||||
|
||||
function handleQualificationFileChange(type: MerchantQualificationType, file: UploadFile) {
|
||||
@@ -323,13 +314,6 @@ async function submitQualification() {
|
||||
ElMessage.warning(qualificationUploadDisabledReason.value)
|
||||
return
|
||||
}
|
||||
if (missingQualificationTypes.value.length) {
|
||||
qualificationDrafts.forEach((item) => {
|
||||
if (!uploadedQualificationTypes.value.has(item.value) && !item.file) item.error = '请选择该项资质文件'
|
||||
})
|
||||
ElMessage.warning(`请补齐:${missingQualificationTypes.value.join('、')}`)
|
||||
return
|
||||
}
|
||||
if (!qualificationCreditCodeReady.value) {
|
||||
const businessLicense = qualificationDraftItem('BUSINESS_LICENSE')
|
||||
if (businessLicense) businessLicense.error = qualificationCreditCodeError.value
|
||||
@@ -366,7 +350,7 @@ async function submitQualification() {
|
||||
item.uploadState = 'uploaded'
|
||||
uploadedCount += 1
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : '资质文件上传失败'
|
||||
const message = qualificationUploadErrorMessage(error)
|
||||
item.error = message
|
||||
item.uploadState = 'error'
|
||||
failures.push(`${item.label}:${message}`)
|
||||
@@ -542,14 +526,14 @@ function closeQualificationPreview() {
|
||||
@closed="resetQualificationDraft"
|
||||
>
|
||||
<div class="qualification-dialog-intro">
|
||||
<strong>一次补齐四类必填资质</strong>
|
||||
<span>每类选择一个文件后统一保存;文件保存不会自动提交企业审核。</span>
|
||||
<strong>必填资质与选填资料</strong>
|
||||
<span>营业执照、法人身份证必填;其余两项可按需上传。文件保存不会自动提交企业审核。</span>
|
||||
</div>
|
||||
<div class="qualification-upload-grid">
|
||||
<article v-for="item in qualificationDrafts" :key="item.value" class="qualification-upload-item">
|
||||
<header>
|
||||
<div>
|
||||
<strong>{{ item.label }}</strong>
|
||||
<strong>{{ item.label }}({{ item.required ? '必填' : '选填' }})</strong>
|
||||
<p>{{ item.description }}</p>
|
||||
</div>
|
||||
<el-tag :type="qualificationSlotTagType(item)" effect="light">{{ qualificationSlotLabel(item) }}</el-tag>
|
||||
@@ -579,7 +563,7 @@ function closeQualificationPreview() {
|
||||
<p v-if="item.error" class="qualification-upload-error" role="alert">{{ item.error }}</p>
|
||||
</article>
|
||||
</div>
|
||||
<el-form-item label="统一社会信用代码" class="qualification-credit-code" :required="!companyCreditCodeValid" :error="qualificationCreditCodeReady ? '' : qualificationCreditCodeError">
|
||||
<el-form-item label="统一社会信用代码" class="qualification-credit-code" :required="businessLicenseFileSelected && !companyCreditCodeValid" :error="qualificationCreditCodeReady ? '' : qualificationCreditCodeError">
|
||||
<el-input v-model="qualificationCertificateNo" maxlength="18" placeholder="请输入18位统一社会信用代码" @input="qualificationCertificateNo = String($event).toUpperCase()" />
|
||||
</el-form-item>
|
||||
<p v-if="qualificationDialogHint" class="qualification-dialog-hint">{{ qualificationDialogHint }}</p>
|
||||
|
||||
@@ -47,7 +47,7 @@ test('implements company details, contact information and qualification workflow
|
||||
assert.match(profileSource, /:close-on-click-modal="!qualificationSubmitting"/)
|
||||
assert.match(profileSource, /:close-on-press-escape="!qualificationSubmitting"/)
|
||||
assert.match(profileSource, /:show-close="!qualificationSubmitting"/)
|
||||
assert.match(profileSource, /请重新选择营业执照,并填写18位统一社会信用代码/)
|
||||
assert.match(profileSource, /const qualificationCreditCodeReady = computed\(\(\) => !businessLicenseFileSelected\.value/)
|
||||
assert.match(profileSource, /companyCreditCodeValid\.value/)
|
||||
assert.doesNotMatch(profileSource, /<el-select v-model="qualificationDraft\.type"/)
|
||||
assert.match(profileSource, /class="mobile-qualification-list"/)
|
||||
@@ -61,7 +61,12 @@ test('implements company details, contact information and qualification workflow
|
||||
assert.match(profileSource, /ref="profileFormRef" :model="draft" :rules="profileFormRules"/)
|
||||
assert.match(profileSource, /prop="companyName" required><el-input v-model="draft\.companyName" placeholder="请输入公司名称"/)
|
||||
assert.match(profileSource, /const valid = await profileFormRef\.value\?\.validate\(\)/)
|
||||
assert.match(profileSource, /请选择该项资质文件/)
|
||||
assert.match(profileSource, /const requiredQualificationTypes = qualificationTypes\.filter\(\(type\) => type\.required\)/)
|
||||
assert.match(profileSource, /货代资质证书'.*required: false/)
|
||||
assert.match(profileSource, /银行开户许可证'.*required: false/)
|
||||
assert.match(profileSource, /requiredQualificationTypes\.every/)
|
||||
assert.doesNotMatch(profileSource, /四类必填资质/)
|
||||
assert.match(profileSource, /qualificationUploadErrorMessage\(error\)/)
|
||||
assert.match(profileSource, /submitTenantQualificationReview/)
|
||||
assert.match(profileSource, /提交企业审核/)
|
||||
assert.match(profileSource, /已保存.*项资质文件,尚未提交审核/)
|
||||
|
||||
Reference in New Issue
Block a user