<template>
|
<view class="page">
|
<!-- 自定义导航栏:状态栏留白由行内 paddingTop 撑开 -->
|
<view class="nav" :style="{ paddingTop: statusBarHeight + 'px' }">
|
<view class="nav-bar">
|
<!-- 返回 -->
|
<view class="nav-back" @tap="handleBack">
|
<view class="icon-back"></view>
|
</view>
|
<text class="nav-title">企业/个体户认证</text>
|
</view>
|
</view>
|
|
<!-- 四段式步骤条:已完成打勾、当前高亮(提交后锁定不可点) -->
|
<view class="steps">
|
<view v-for="(label, idx) in STEP_LABELS" :key="idx" class="step" @tap="handleStepTap(idx)">
|
<view class="step-dot" :class="{ 'step-dot--done': idx < step, 'step-dot--active': idx === step }">
|
<text v-if="idx < step" class="step-dot-check">✓</text>
|
<text v-else class="step-dot-num">{{ idx + 1 }}</text>
|
</view>
|
<text class="step-label" :class="{ 'step-label--done': idx < step, 'step-label--active': idx === step }">{{ label }}</text>
|
<view v-if="idx < STEP_LABELS.length - 1" class="step-line" :class="{ 'step-line--done': idx < step }"></view>
|
</view>
|
</view>
|
|
<scroll-view class="body" scroll-y>
|
<!-- 第 4 步:提交审核 / 结果占位 -->
|
<view v-if="step === 3" class="result">
|
<view class="result-icon" :class="resultState === 'passed' ? 'result-icon--done' : 'result-icon--audit'">
|
<text class="result-icon-text">{{ resultState === 'passed' ? '✓' : '···' }}</text>
|
</view>
|
<text class="result-title">{{ resultInfo.title }}</text>
|
<text class="result-tip">{{ resultInfo.tip }}</text>
|
</view>
|
|
<!-- 第 1 步:企业信息 -->
|
<view v-else-if="step === 0" class="form">
|
<!-- 认证失败:展示驳回原因,可修改后重提 -->
|
<view v-if="showRejectReason" class="reject-tip">
|
<text class="reject-tip-text">驳回原因:{{ rejectReason }}</text>
|
</view>
|
|
<!-- 主体类型 -->
|
<view class="field">
|
<text class="field-label">主体类型</text>
|
<picker :range="SUBJECT_TYPE_LABELS" @change="(e) => handleSelectChange('subjectType', SUBJECT_TYPE_VALUES, e)">
|
<view class="field-select">
|
<text :class="form.subjectType ? 'field-select-text' : 'field-select-placeholder'">{{ subjectTypeLabel || '请选择主体类型' }}</text>
|
<view class="icon-chevron"></view>
|
</view>
|
</picker>
|
</view>
|
|
<!-- 企业/个体名称 -->
|
<view class="field">
|
<text class="field-label">企业/个体名称</text>
|
<input class="field-input" v-model="form.companyName" type="text" placeholder="请输入企业/个体名称" :placeholder-style="PLACEHOLDER_STYLE" maxlength="128" />
|
</view>
|
|
<!-- 营业执照 -->
|
<view class="field">
|
<text class="field-label">营业执照</text>
|
<view class="upload" @tap="handleChooseImage('licenseImage', '营业执照')">
|
<image v-if="form.licenseImage" class="upload-img" :src="form.licenseImage" mode="aspectFill" />
|
<view v-else class="upload-empty">
|
<view class="icon-camera"></view>
|
<text class="upload-empty-text">上传营业执照</text>
|
</view>
|
</view>
|
</view>
|
|
<!-- 统一社会信用代码 -->
|
<view class="field">
|
<text class="field-label">统一社会信用代码</text>
|
<input class="field-input" v-model="form.creditCode" type="text" placeholder="请输入统一社会信用代码" :placeholder-style="PLACEHOLDER_STYLE" maxlength="32" />
|
</view>
|
|
<!-- 营业执照有效期类型:长期时隐藏起止日期 -->
|
<view class="field">
|
<text class="field-label">营业执照有效期类型</text>
|
<picker :range="VALID_TYPE_LABELS" @change="(e) => handleSelectChange('licenseValidType', VALID_TYPE_VALUES, e)">
|
<view class="field-select">
|
<text :class="form.licenseValidType ? 'field-select-text' : 'field-select-placeholder'">{{ labelOf(VALID_TYPE_VALUES, VALID_TYPE_LABELS, form.licenseValidType) || '请选择有效期类型' }}</text>
|
<view class="icon-chevron"></view>
|
</view>
|
</picker>
|
</view>
|
|
<!-- 营业执照有效期起止:仅短期展示且必填 -->
|
<view v-if="form.licenseValidType === '2'" class="field">
|
<text class="field-label">营业执照有效期</text>
|
<view class="date-range">
|
<picker mode="date" :value="form.licenseValidStart" @change="(e) => handleDateChange('licenseValidStart', e)">
|
<view class="date-value" :class="{ 'date-value--placeholder': !form.licenseValidStart }">{{ form.licenseValidStart || '开始日期' }}</view>
|
</picker>
|
<text class="date-sep">~</text>
|
<picker mode="date" :value="form.licenseValidEnd" @change="(e) => handleDateChange('licenseValidEnd', e)">
|
<view class="date-value" :class="{ 'date-value--placeholder': !form.licenseValidEnd }">{{ form.licenseValidEnd || '截止日期' }}</view>
|
</picker>
|
</view>
|
</view>
|
|
<!-- 营业执照注册地区 -->
|
<view class="field">
|
<text class="field-label">营业执照注册地区</text>
|
<picker mode="region" :value="companyRegionValue" @change="(e) => handleRegionChange('registeredRegion', 'company', e)">
|
<view class="field-select">
|
<text :class="form.registeredRegion ? 'field-select-text' : 'field-select-placeholder'">{{ form.registeredRegion || '请选择注册地区' }}</text>
|
<view class="icon-chevron"></view>
|
</view>
|
</picker>
|
</view>
|
|
<!-- 营业执照详细地址 -->
|
<view class="field">
|
<text class="field-label">营业执照详细地址</text>
|
<input class="field-input" v-model="form.companyAddress" type="text" placeholder="请输入营业执照详细地址" :placeholder-style="PLACEHOLDER_STYLE" maxlength="255" />
|
</view>
|
</view>
|
|
<!-- 第 2 步:法人信息 -->
|
<view v-else-if="step === 1" class="form">
|
<!-- 法人姓名 -->
|
<view class="field">
|
<text class="field-label">法人姓名</text>
|
<input class="field-input" v-model="form.legalPerson" type="text" placeholder="请输入法人姓名" :placeholder-style="PLACEHOLDER_STYLE" maxlength="64" />
|
</view>
|
|
<!-- 法人身份证人像面 / 国徽面 -->
|
<view class="field">
|
<text class="field-label">法人身份证</text>
|
<view class="upload-row">
|
<view class="upload upload--half" @tap="handleChooseImage('legalIdCardFront', '法人身份证人像面')">
|
<image v-if="form.legalIdCardFront" class="upload-img" :src="form.legalIdCardFront" mode="aspectFill" />
|
<view v-else class="upload-empty">
|
<view class="icon-camera"></view>
|
<text class="upload-empty-text">人像面</text>
|
</view>
|
</view>
|
<view class="upload upload--half" @tap="handleChooseImage('legalIdCardBack', '法人身份证国徽面')">
|
<image v-if="form.legalIdCardBack" class="upload-img" :src="form.legalIdCardBack" mode="aspectFill" />
|
<view v-else class="upload-empty">
|
<view class="icon-camera"></view>
|
<text class="upload-empty-text">国徽面</text>
|
</view>
|
</view>
|
</view>
|
</view>
|
|
<!-- 法人身份证有效期类型 -->
|
<view class="field">
|
<text class="field-label">法人身份证有效期类型</text>
|
<picker :range="VALID_TYPE_LABELS" @change="(e) => handleSelectChange('legalIdValidType', VALID_TYPE_VALUES, e)">
|
<view class="field-select">
|
<text :class="form.legalIdValidType ? 'field-select-text' : 'field-select-placeholder'">{{ labelOf(VALID_TYPE_VALUES, VALID_TYPE_LABELS, form.legalIdValidType) || '请选择有效期类型' }}</text>
|
<view class="icon-chevron"></view>
|
</view>
|
</picker>
|
</view>
|
|
<!-- 法人身份证有效期起止:仅短期展示且必填 -->
|
<view v-if="form.legalIdValidType === '2'" class="field">
|
<text class="field-label">法人身份证有效期</text>
|
<view class="date-range">
|
<picker mode="date" :value="form.legalIdValidStart" @change="(e) => handleDateChange('legalIdValidStart', e)">
|
<view class="date-value" :class="{ 'date-value--placeholder': !form.legalIdValidStart }">{{ form.legalIdValidStart || '开始日期' }}</view>
|
</picker>
|
<text class="date-sep">~</text>
|
<picker mode="date" :value="form.legalIdValidEnd" @change="(e) => handleDateChange('legalIdValidEnd', e)">
|
<view class="date-value" :class="{ 'date-value--placeholder': !form.legalIdValidEnd }">{{ form.legalIdValidEnd || '截止日期' }}</view>
|
</picker>
|
</view>
|
</view>
|
|
<!-- 法人身份证详细地址 -->
|
<view class="field">
|
<text class="field-label">法人身份证详细地址</text>
|
<input class="field-input" v-model="form.legalIdAddress" type="text" placeholder="请输入法人身份证详细地址" :placeholder-style="PLACEHOLDER_STYLE" maxlength="255" />
|
</view>
|
|
<!-- 负责人姓名 -->
|
<view class="field">
|
<text class="field-label">负责人姓名</text>
|
<input class="field-input" v-model="form.contactName" type="text" placeholder="请输入负责人姓名" :placeholder-style="PLACEHOLDER_STYLE" maxlength="64" />
|
</view>
|
|
<!-- 负责人联系电话 -->
|
<view class="field">
|
<text class="field-label">负责人联系电话</text>
|
<input class="field-input" v-model="form.contactPhone" type="number" placeholder="请输入负责人联系电话" :placeholder-style="PLACEHOLDER_STYLE" maxlength="20" />
|
</view>
|
|
<!-- 负责人邮箱 -->
|
<view class="field">
|
<text class="field-label">负责人邮箱</text>
|
<input class="field-input" v-model="form.contactEmail" type="text" placeholder="请输入负责人邮箱" :placeholder-style="PLACEHOLDER_STYLE" maxlength="128" />
|
</view>
|
</view>
|
|
<!-- 第 3 步:收款信息 -->
|
<view v-else class="form">
|
<!-- 收款类型 -->
|
<view class="field">
|
<text class="field-label">收款类型</text>
|
<picker :range="PAYEE_TYPE_OPTIONS" @change="(e) => handlePayeeTypeChange(e)">
|
<view class="field-select">
|
<text :class="form.payeeType ? 'field-select-text' : 'field-select-placeholder'">{{ form.payeeType || '请选择收款类型' }}</text>
|
<view class="icon-chevron"></view>
|
</view>
|
</picker>
|
</view>
|
|
<!-- 银行卡号码面照片 -->
|
<view class="field">
|
<text class="field-label">银行卡号码面照片</text>
|
<view class="upload" @tap="handleChooseImage('bankCardImage', '银行卡号码面照片')">
|
<image v-if="form.bankCardImage" class="upload-img" :src="form.bankCardImage" mode="aspectFill" />
|
<view v-else class="upload-empty">
|
<view class="icon-camera"></view>
|
<text class="upload-empty-text">上传银行卡照片</text>
|
</view>
|
</view>
|
</view>
|
|
<!-- 银行卡号 -->
|
<view class="field">
|
<text class="field-label">银行卡号</text>
|
<input class="field-input" v-model="form.bankCardNo" type="number" placeholder="请输入银行卡号" :placeholder-style="PLACEHOLDER_STYLE" maxlength="32" />
|
</view>
|
|
<!-- 开户城市 -->
|
<view class="field">
|
<text class="field-label">开户城市</text>
|
<picker mode="region" :value="bankRegionValue" @change="(e) => handleRegionChange('bankCity', 'bank', e)">
|
<view class="field-select">
|
<text :class="form.bankCity ? 'field-select-text' : 'field-select-placeholder'">{{ form.bankCity || '请选择开户城市' }}</text>
|
<view class="icon-chevron"></view>
|
</view>
|
</picker>
|
</view>
|
</view>
|
</scroll-view>
|
|
<!-- 底部按钮:前两步「下一步」,第 3 步「提交资料」;结果页隐藏 -->
|
<view v-if="step < 3" class="footer">
|
<view class="btn-submit" :class="{ 'btn-submit--disabled': submitting }" @tap="handlePrimary">
|
<text class="btn-submit-text">{{ step === 2 ? '提交资料' : '下一步' }}</text>
|
</view>
|
</view>
|
</view>
|
</template>
|
|
<script setup>
|
import { computed, reactive, ref } from 'vue'
|
import { onShow } from '@dcloudio/uni-app'
|
import { ensureLogin } from '@/utils/request.js'
|
import { getCompanyCertificationStatus, submitCompanyCertification, uploadAvatarFile } from '@/api/index.js'
|
|
/** 状态栏高度:自定义导航栏下用于顶部留白 */
|
const statusBarHeight = uni.getSystemInfoSync().statusBarHeight || 0
|
|
/** 图片大小上限:5MB(选图后前置校验,超限直接拒绝,避免无效上传) */
|
const IMAGE_MAX_SIZE = 5 * 1024 * 1024
|
|
/** 输入框占位色:与 picker/日期占位色统一(小程序 scoped 下 placeholder-class 不生效,改用行内样式) */
|
const PLACEHOLDER_STYLE = 'color:#94a3b8;font-size:30rpx'
|
|
/** 步骤标题:与原型四步一一对应 */
|
const STEP_LABELS = ['企业信息', '法人信息', '收款信息', '提交审核']
|
|
/** 主体类型:值 2个体工商户 / 3企业(与后端 subject_type 语义一致) */
|
const SUBJECT_TYPE_VALUES = ['2', '3']
|
const SUBJECT_TYPE_LABELS = ['个体工商户', '企业']
|
|
/** 有效期类型:值 1长期 / 2短期;长期隐藏起止日期且不校验 */
|
const VALID_TYPE_VALUES = ['1', '2']
|
const VALID_TYPE_LABELS = ['长期', '短期']
|
|
/** 收款类型选项:本期仅基础必填,直接存中文文本 */
|
const PAYEE_TYPE_OPTIONS = ['对公账户', '对私账户']
|
|
/** 结果页文案:审核中 / 已通过 */
|
const RESULT_MAP = {
|
auditing: { title: '资料审核中', tip: '您的资料正在审核中,请耐心等待' },
|
passed: { title: '认证已通过', tip: '您的企业/个体户认证已通过审核' }
|
}
|
|
/** 当前步骤:0~2 为表单步骤,3 为提交审核结果页 */
|
const step = ref(0)
|
|
/** 结果页类型:'auditing' 审核中 / 'passed' 已通过 */
|
const resultState = ref('')
|
|
/** 提交中标记:防止重复点击造成重复提交 */
|
const submitting = ref(false)
|
|
/** 审核状态(0待审核 1已通过 2已驳回;空表示从未提交) */
|
const auditStatus = ref('')
|
|
/** 驳回原因:仅审核状态为「已驳回」时有值 */
|
const rejectReason = ref('')
|
|
/** 全部表单字段(有效期起止拆为起/止两个值,提交时再拼接为文本) */
|
const form = reactive({
|
subjectType: '',
|
companyName: '',
|
licenseImage: '',
|
creditCode: '',
|
licenseValidType: '',
|
licenseValidStart: '',
|
licenseValidEnd: '',
|
registeredRegion: '',
|
companyAddress: '',
|
legalPerson: '',
|
legalIdCardFront: '',
|
legalIdCardBack: '',
|
legalIdValidType: '',
|
legalIdValidStart: '',
|
legalIdValidEnd: '',
|
legalIdAddress: '',
|
contactName: '',
|
contactPhone: '',
|
contactEmail: '',
|
payeeType: '',
|
bankCardImage: '',
|
bankCardNo: '',
|
bankCity: ''
|
})
|
|
/** 地区选择器回显值:picker 的 value 需与三级数组保持一致 */
|
const companyRegionValue = ref([])
|
const bankRegionValue = ref([])
|
|
/** 值 → 文案:按同下标映射,未命中返回空串 */
|
const labelOf = (values, labels, value) => {
|
const index = values.indexOf(value)
|
return index > -1 ? labels[index] : ''
|
}
|
|
/** 解析「起~止」文本为起止两个日期;长期或未填时均返回空 */
|
const parseValidPeriod = (period) => {
|
if (!period) return { start: '', end: '' }
|
const [start = '', end = ''] = period.split('~')
|
return { start, end }
|
}
|
|
/** 拼接「起~止」文本:任一项缺失则返回空(长期提交空值) */
|
const buildValidPeriod = (start, end) => (start && end ? `${start}~${end}` : '')
|
|
/** 是否展示驳回原因 */
|
const showRejectReason = computed(() => auditStatus.value === '2' && !!rejectReason.value)
|
|
/** 是否已提交(结果页):锁定时不可回退修改 */
|
const locked = computed(() => step.value === 3)
|
|
/** 结果页文案对象 */
|
const resultInfo = computed(() => RESULT_MAP[resultState.value] || RESULT_MAP.auditing)
|
|
/** 主体类型文案(用于选择项回显) */
|
const subjectTypeLabel = computed(() => labelOf(SUBJECT_TYPE_VALUES, SUBJECT_TYPE_LABELS, form.subjectType))
|
|
/** 返回上一页:无上级页面(如直接进入)时回到个人中心 */
|
const handleBack = () => {
|
const pages = getCurrentPages()
|
if (pages.length > 1) {
|
uni.navigateBack()
|
return
|
}
|
uni.switchTab({ url: '/pages/profile/index' })
|
}
|
|
/** 点击步骤条:已提交(结果页)不可回退,仅允许回退到已完成的步骤 */
|
const handleStepTap = (idx) => {
|
if (locked.value) return
|
if (idx < step.value) step.value = idx
|
}
|
|
/** 通用下拉选择回写(主体类型 / 有效期类型) */
|
const handleSelectChange = (field, values, e) => {
|
form[field] = values[e.detail.value] || ''
|
}
|
|
/** 收款类型选择(直接存中文文本) */
|
const handlePayeeTypeChange = (e) => {
|
form.payeeType = PAYEE_TYPE_OPTIONS[e.detail.value] || ''
|
}
|
|
/** 地区选择:回写文本并按空格拆分为数组用于 picker 回显 */
|
const handleRegionChange = (field, target, e) => {
|
const region = e.detail.value || []
|
form[field] = region.join(' ')
|
if (target === 'company') {
|
companyRegionValue.value = region
|
} else {
|
bankRegionValue.value = region
|
}
|
}
|
|
/** 日期选择回写(有效期起 / 止) */
|
const handleDateChange = (field, e) => {
|
form[field] = e.detail.value
|
}
|
|
/** 图片来源选项:相册上传 / 拍照上传,供用户按场景选择 */
|
const IMAGE_SOURCE_OPTIONS = [
|
{ label: '拍照', sourceType: ['camera'] },
|
{ label: '从相册选择', sourceType: ['album'] }
|
]
|
|
/**
|
* 选图并上传:先选来源(相册/拍照)-> 选图 -> 大小校验 -> 上传文件服务 -> 写回表单字段
|
* @param {String} field 表单字段名
|
* @param {String} label 字段中文名(用于失败提示)
|
*/
|
const handleChooseImage = (field, label) => {
|
uni.showActionSheet({
|
itemList: IMAGE_SOURCE_OPTIONS.map((item) => item.label),
|
success: (sheetRes) => {
|
const option = IMAGE_SOURCE_OPTIONS[sheetRes.tapIndex]
|
if (option) chooseAndUploadImage(field, label, option.sourceType)
|
}
|
})
|
}
|
|
/**
|
* 按指定来源选图并上传(相册 / 拍照)
|
* @param {String} field 表单字段名
|
* @param {String} label 字段中文名(用于失败提示)
|
* @param {Array} sourceType 图片来源:['camera'] 拍照 / ['album'] 相册
|
*/
|
const chooseAndUploadImage = (field, label, sourceType) => {
|
uni.chooseImage({
|
count: 1,
|
sizeType: ['compressed'],
|
sourceType,
|
success: async (res) => {
|
const filePath = (res.tempFilePaths || [])[0]
|
const file = (res.tempFiles || [])[0]
|
if (!filePath) return
|
// 前置大小校验:超限直接提示,避免无效上传
|
if (file && file.size > IMAGE_MAX_SIZE) {
|
uni.showToast({ title: '图片不能超过5MB', icon: 'none' })
|
return
|
}
|
uni.showLoading({ title: '上传中', mask: true })
|
try {
|
const uploadRes = await uploadAvatarFile(filePath)
|
const url = uploadRes.data && uploadRes.data.url
|
uni.hideLoading()
|
if (!url) {
|
uni.showToast({ title: '上传失败,请重试', icon: 'none' })
|
return
|
}
|
form[field] = url
|
} catch (error) {
|
uni.hideLoading()
|
console.error(`[COMPANY_CERT] 上传${label}失败`, error)
|
uni.showToast({ title: '上传失败,请重试', icon: 'none' })
|
}
|
}
|
})
|
}
|
|
/** 单步必填校验:返回失败提示(通过返回空串) */
|
const validateStep = (idx) => {
|
// 第 1 步:企业信息
|
if (idx === 0) {
|
if (!form.subjectType) return '请选择主体类型'
|
if (!form.companyName.trim()) return '请输入企业/个体名称'
|
if (!form.licenseImage) return '请上传营业执照'
|
if (!form.creditCode.trim()) return '请输入统一社会信用代码'
|
if (!form.licenseValidType) return '请选择营业执照有效期类型'
|
// 短期必须填写起止日期;长期隐藏且不校验
|
if (form.licenseValidType === '2' && (!form.licenseValidStart || !form.licenseValidEnd)) {
|
return '请选择营业执照有效期起止日期'
|
}
|
if (!form.registeredRegion) return '请选择营业执照注册地区'
|
if (!form.companyAddress.trim()) return '请输入营业执照详细地址'
|
return ''
|
}
|
// 第 2 步:法人信息
|
if (idx === 1) {
|
if (!form.legalPerson.trim()) return '请输入法人姓名'
|
if (!form.legalIdCardFront) return '请上传法人身份证人像面'
|
if (!form.legalIdCardBack) return '请上传法人身份证国徽面'
|
if (!form.legalIdValidType) return '请选择法人身份证有效期类型'
|
if (form.legalIdValidType === '2' && (!form.legalIdValidStart || !form.legalIdValidEnd)) {
|
return '请选择法人身份证有效期起止日期'
|
}
|
if (!form.legalIdAddress.trim()) return '请输入法人身份证详细地址'
|
if (!form.contactName.trim()) return '请输入负责人姓名'
|
if (!form.contactPhone.trim()) return '请输入负责人联系电话'
|
if (!form.contactEmail.trim()) return '请输入负责人邮箱'
|
return ''
|
}
|
// 第 3 步:收款信息
|
if (idx === 2) {
|
if (!form.payeeType) return '请选择收款类型'
|
if (!form.bankCardImage) return '请上传银行卡号码面照片'
|
if (!form.bankCardNo.trim()) return '请输入银行卡号'
|
if (!form.bankCity) return '请选择开户城市'
|
return ''
|
}
|
return ''
|
}
|
|
/** 整表单校验:按步骤顺序返回首个缺失项提示 */
|
const validateAll = () => validateStep(0) || validateStep(1) || validateStep(2)
|
|
/** 组装提交入参(字段名与后端 CompanyCertificationApplyDto 一致) */
|
const buildPayload = () => ({
|
subjectType: form.subjectType,
|
companyName: form.companyName.trim(),
|
licenseImage: form.licenseImage,
|
creditCode: form.creditCode.trim(),
|
licenseValidType: form.licenseValidType,
|
licenseValidPeriod: form.licenseValidType === '2' ? buildValidPeriod(form.licenseValidStart, form.licenseValidEnd) : '',
|
registeredRegion: form.registeredRegion,
|
companyAddress: form.companyAddress.trim(),
|
legalPerson: form.legalPerson.trim(),
|
legalIdCardFront: form.legalIdCardFront,
|
legalIdCardBack: form.legalIdCardBack,
|
legalIdValidType: form.legalIdValidType,
|
legalIdValidPeriod: form.legalIdValidType === '2' ? buildValidPeriod(form.legalIdValidStart, form.legalIdValidEnd) : '',
|
legalIdAddress: form.legalIdAddress.trim(),
|
contactName: form.contactName.trim(),
|
contactPhone: form.contactPhone.trim(),
|
contactEmail: form.contactEmail.trim(),
|
payeeType: form.payeeType,
|
bankCardImage: form.bankCardImage,
|
bankCardNo: form.bankCardNo.trim(),
|
bankCity: form.bankCity
|
})
|
|
/**
|
* 拉取企业认证状态并回填表单
|
* 从未提交 → 停在第 1 步空表单;已驳回 → 停在第 1 步并展示驳回原因;
|
* 审核中 / 已通过 → 直接进入第 4 步结果页(提交后不可再编辑)。
|
* 查询失败静默降级为「未认证」,不阻断用户填报。
|
*/
|
const loadCompanyCertificationStatus = async () => {
|
try {
|
const res = await getCompanyCertificationStatus()
|
const data = res.data || {}
|
auditStatus.value = data.auditStatus || ''
|
rejectReason.value = data.auditRemark || ''
|
// 回填企业信息
|
form.subjectType = data.subjectType || ''
|
form.companyName = data.companyName || ''
|
form.licenseImage = data.licenseImage || ''
|
form.creditCode = data.creditCode || ''
|
form.licenseValidType = data.licenseValidType || ''
|
form.registeredRegion = data.registeredRegion || ''
|
form.companyAddress = data.companyAddress || ''
|
// 回填法人信息
|
form.legalPerson = data.legalPerson || ''
|
form.legalIdCardFront = data.legalIdCardFront || ''
|
form.legalIdCardBack = data.legalIdCardBack || ''
|
form.legalIdValidType = data.legalIdValidType || ''
|
form.legalIdAddress = data.legalIdAddress || ''
|
form.contactName = data.contactName || ''
|
form.contactPhone = data.contactPhone || ''
|
form.contactEmail = data.contactEmail || ''
|
// 回填收款信息
|
form.payeeType = data.payeeType || ''
|
form.bankCardImage = data.bankCardImage || ''
|
form.bankCardNo = data.bankCardNo || ''
|
form.bankCity = data.bankCity || ''
|
// 有效期文本拆回起止日期
|
const licensePeriod = parseValidPeriod(data.licenseValidPeriod)
|
form.licenseValidStart = licensePeriod.start
|
form.licenseValidEnd = licensePeriod.end
|
const legalPeriod = parseValidPeriod(data.legalIdValidPeriod)
|
form.legalIdValidStart = legalPeriod.start
|
form.legalIdValidEnd = legalPeriod.end
|
// 地区文本按空格拆回三级数组,供 picker 回显
|
if (form.registeredRegion) companyRegionValue.value = form.registeredRegion.split(' ')
|
if (form.bankCity) bankRegionValue.value = form.bankCity.split(' ')
|
// 决定首屏:审核中 / 已通过锁定在结果页
|
if (auditStatus.value === '0') {
|
resultState.value = 'auditing'
|
step.value = 3
|
} else if (auditStatus.value === '1') {
|
resultState.value = 'passed'
|
step.value = 3
|
} else {
|
step.value = 0
|
}
|
} catch (error) {
|
console.error('[COMPANY_CERT] 查询企业认证状态失败', error)
|
auditStatus.value = ''
|
rejectReason.value = ''
|
step.value = 0
|
}
|
}
|
|
/** 主按钮:第 3 步提交,前两步逐级校验后进入下一步 */
|
const handlePrimary = () => {
|
if (submitting.value) return
|
if (step.value === 2) {
|
handleSubmit()
|
return
|
}
|
const errorMsg = validateStep(step.value)
|
if (errorMsg) {
|
uni.showToast({ title: errorMsg, icon: 'none' })
|
return
|
}
|
step.value += 1
|
}
|
|
/** 提交企业/个体认证:整表单校验通过后调接口,成功后进入结果页 */
|
const handleSubmit = async () => {
|
const errorMsg = validateAll()
|
if (errorMsg) {
|
uni.showToast({ title: errorMsg, icon: 'none' })
|
return
|
}
|
submitting.value = true
|
uni.showLoading({ title: '提交中', mask: true })
|
try {
|
await submitCompanyCertification(buildPayload())
|
uni.hideLoading()
|
auditStatus.value = '0'
|
resultState.value = 'auditing'
|
step.value = 3
|
uni.showToast({ title: '提交成功,等待审核', icon: 'none' })
|
} catch (error) {
|
uni.hideLoading()
|
console.error('[COMPANY_CERT] 提交企业认证失败', error)
|
uni.showToast({ title: (error && error.message) || '提交失败,请重试', icon: 'none' })
|
} finally {
|
submitting.value = false
|
}
|
}
|
|
// 进入页面校验登录态并拉取认证状态回填(未登录跳登录页)
|
// 注意:选图/拍照(chooseImage)返回时会再次触发 onShow,若重复拉取会覆盖用户已填内容,
|
// 故用 hasLoadedStatus 保证状态回填仅在首次显示时执行一次
|
let hasLoadedStatus = false
|
onShow(() => {
|
if (!ensureLogin()) return
|
if (hasLoadedStatus) return
|
hasLoadedStatus = true
|
loadCompanyCertificationStatus()
|
})
|
</script>
|
|
<style scoped lang="scss">
|
.page {
|
height: 100vh;
|
display: flex;
|
flex-direction: column;
|
box-sizing: border-box;
|
background-color: #ffffff;
|
}
|
|
/* ==================== 自定义导航栏 ==================== */
|
|
.nav {
|
flex-shrink: 0;
|
background-color: #ffffff;
|
}
|
|
.nav-bar {
|
position: relative;
|
height: 88rpx;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
}
|
|
.nav-back {
|
position: absolute;
|
left: 16rpx;
|
top: 50%;
|
transform: translateY(-50%);
|
width: 64rpx;
|
height: 64rpx;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
}
|
|
/* 纯 CSS 左向箭头:左 + 下边框旋转 45° */
|
.icon-back {
|
width: 22rpx;
|
height: 22rpx;
|
border-left: 4rpx solid #1f2937;
|
border-bottom: 4rpx solid #1f2937;
|
transform: rotate(45deg);
|
}
|
|
.nav-title {
|
font-size: 34rpx;
|
font-weight: 700;
|
line-height: 1;
|
letter-spacing: 1rpx;
|
color: #0f172a;
|
}
|
|
/* ==================== 四段式步骤条 ==================== */
|
|
.steps {
|
flex-shrink: 0;
|
display: flex;
|
align-items: flex-start;
|
padding: 24rpx 32rpx 28rpx;
|
border-bottom: 2rpx solid #f1f5f9;
|
}
|
|
.step {
|
position: relative;
|
flex: 1;
|
display: flex;
|
flex-direction: column;
|
align-items: center;
|
}
|
|
.step-dot {
|
position: relative;
|
z-index: 1;
|
width: 44rpx;
|
height: 44rpx;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
border-radius: 50%;
|
background: #e2e8f0;
|
}
|
|
/* 已完成:草绿实心 + 打勾 */
|
.step-dot--done {
|
background: #82c236;
|
}
|
|
/* 当前:草绿渐变高亮 */
|
.step-dot--active {
|
background: linear-gradient(90deg, #82c236 0%, #639c1f 100%);
|
}
|
|
.step-dot-check {
|
font-size: 24rpx;
|
font-weight: 700;
|
line-height: 1;
|
color: #ffffff;
|
}
|
|
.step-dot-num {
|
font-size: 24rpx;
|
font-weight: 600;
|
line-height: 1;
|
color: #94a3b8;
|
}
|
|
.step-dot--active .step-dot-num {
|
color: #ffffff;
|
}
|
|
/* 连接线:从当前步骤圆心延伸到下一步骤圆心 */
|
.step-line {
|
position: absolute;
|
top: 21rpx;
|
left: 50%;
|
width: 100%;
|
height: 4rpx;
|
background: #e2e8f0;
|
}
|
|
.step-line--done {
|
background: #82c236;
|
}
|
|
.step-label {
|
margin-top: 12rpx;
|
font-size: 24rpx;
|
line-height: 1;
|
color: #94a3b8;
|
}
|
|
.step-label--active {
|
font-weight: 600;
|
color: #639c1f;
|
}
|
|
.step-label--done {
|
color: #334155;
|
}
|
|
/* ==================== 内容区 ==================== */
|
|
.body {
|
flex: 1;
|
min-height: 0;
|
}
|
|
.form {
|
padding: 32rpx 32rpx 40rpx;
|
}
|
|
/* ==================== 表单字段 ==================== */
|
|
.field {
|
margin-bottom: 40rpx;
|
}
|
|
.field-label {
|
display: block;
|
font-size: 28rpx;
|
font-weight: 600;
|
line-height: 1;
|
color: #334155;
|
}
|
|
.field-input {
|
height: 88rpx;
|
margin-top: 8rpx;
|
font-size: 30rpx;
|
color: #1f2937;
|
border-bottom: 2rpx solid #e2e8f0;
|
}
|
|
/* 下拉选择行:文本 + 右箭头 */
|
.field-select {
|
height: 88rpx;
|
margin-top: 8rpx;
|
display: flex;
|
align-items: center;
|
justify-content: space-between;
|
border-bottom: 2rpx solid #e2e8f0;
|
}
|
|
.field-select-text {
|
font-size: 30rpx;
|
color: #1f2937;
|
}
|
|
.field-select-placeholder {
|
font-size: 30rpx;
|
color: #94a3b8;
|
}
|
|
.icon-chevron {
|
width: 32rpx;
|
height: 32rpx;
|
flex-shrink: 0;
|
background-repeat: no-repeat;
|
background-position: center center;
|
background-size: 100% 100%;
|
background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjOTRBM0I4IiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI+PHBhdGggZD0ibTkgMTggNi02LTYtNiIvPjwvc3ZnPg==');
|
}
|
|
/* ==================== 有效期起止日期 ==================== */
|
|
.date-range {
|
height: 88rpx;
|
margin-top: 8rpx;
|
display: flex;
|
align-items: center;
|
border-bottom: 2rpx solid #e2e8f0;
|
}
|
|
.date-range picker {
|
flex: 1;
|
}
|
|
.date-value {
|
font-size: 30rpx;
|
color: #1f2937;
|
}
|
|
.date-value--placeholder {
|
color: #94a3b8;
|
}
|
|
.date-sep {
|
flex-shrink: 0;
|
padding: 0 20rpx;
|
font-size: 30rpx;
|
color: #94a3b8;
|
}
|
|
/* ==================== 图片上传 ==================== */
|
|
.upload {
|
width: 100%;
|
height: 260rpx;
|
margin-top: 16rpx;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
box-sizing: border-box;
|
border: 2rpx dashed #cbd5e1;
|
border-radius: 12rpx;
|
background: #f8fafc;
|
overflow: hidden;
|
}
|
|
/* 法人身份证正反面并排 */
|
.upload-row {
|
display: flex;
|
gap: 24rpx;
|
margin-top: 16rpx;
|
}
|
|
.upload--half {
|
flex: 1;
|
width: auto;
|
height: 220rpx;
|
margin-top: 0;
|
}
|
|
.upload-img {
|
width: 100%;
|
height: 100%;
|
}
|
|
.upload-empty {
|
display: flex;
|
flex-direction: column;
|
align-items: center;
|
gap: 16rpx;
|
}
|
|
.upload-empty-text {
|
font-size: 26rpx;
|
color: #94a3b8;
|
}
|
|
.icon-camera {
|
width: 48rpx;
|
height: 48rpx;
|
background-repeat: no-repeat;
|
background-position: center center;
|
background-size: 100% 100%;
|
background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjOTRBM0I4IiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI+PHBhdGggZD0iTTE0LjUgNGgtNUw3IDdINGEyIDIgMCAwIDAtMiAydjlhMiAyIDAgMCAwIDIgMmgxNmEyIDIgMCAwIDAgMi0yVjlhMiAyIDAgMCAwLTItMmgtM2wtMi41LTN6Ii8+PGNpcmNsZSBjeD0iMTIiIGN5PSIxMyIgcj0iMyIvPjwvc3ZnPg==');
|
}
|
|
/* ==================== 驳回原因 ==================== */
|
|
.reject-tip {
|
margin-bottom: 32rpx;
|
padding: 20rpx 24rpx;
|
border-radius: 12rpx;
|
background: #fef2f2;
|
}
|
|
.reject-tip-text {
|
font-size: 26rpx;
|
line-height: 1.5;
|
color: #ef4444;
|
}
|
|
/* ==================== 结果页 ==================== */
|
|
.result {
|
padding: 140rpx 64rpx 0;
|
display: flex;
|
flex-direction: column;
|
align-items: center;
|
}
|
|
.result-icon {
|
width: 140rpx;
|
height: 140rpx;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
border-radius: 50%;
|
}
|
|
.result-icon--audit {
|
background: #eff6ff;
|
}
|
|
.result-icon--done {
|
background: #f7fee7;
|
}
|
|
.result-icon-text {
|
font-size: 40rpx;
|
font-weight: 700;
|
line-height: 1;
|
}
|
|
.result-icon--audit .result-icon-text {
|
color: #1e5ebc;
|
}
|
|
.result-icon--done .result-icon-text {
|
color: #4d7c0f;
|
}
|
|
.result-title {
|
margin-top: 40rpx;
|
font-size: 34rpx;
|
font-weight: 700;
|
line-height: 1;
|
color: #0f172a;
|
}
|
|
.result-tip {
|
margin-top: 20rpx;
|
font-size: 28rpx;
|
line-height: 1.5;
|
text-align: center;
|
color: #94a3b8;
|
}
|
|
/* ==================== 底部提交 ==================== */
|
|
.footer {
|
flex-shrink: 0;
|
padding: 24rpx 48rpx calc(44rpx + env(safe-area-inset-bottom));
|
}
|
|
.btn-submit {
|
width: 100%;
|
height: 96rpx;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
border-radius: 12rpx;
|
background: linear-gradient(90deg, #82c236 0%, #639c1f 100%);
|
box-shadow: 0 16rpx 40rpx rgba(99, 156, 31, 0.28);
|
}
|
|
.btn-submit--disabled {
|
opacity: 0.6;
|
}
|
|
.btn-submit-text {
|
font-size: 32rpx;
|
font-weight: 600;
|
line-height: 1;
|
color: #ffffff;
|
}
|
</style>
|