@@ -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 >