Compare commits

...

2 Commits

Author SHA1 Message Date
halo d2030f1711 增加企业开户信息确认H5页面 2026-08-07 16:04:42 +08:00
halo 2e879255ed 增加企业开户信息确认H5页面 2026-08-07 16:03:56 +08:00
9 changed files with 712 additions and 44 deletions

View File

@ -1,3 +1,4 @@
VITE_API_BASE_URL=http://localhost:8888
npmhttp://www.baidu.com
VITE_H5_QRCODE_BASE_URL=http://www.baidu.com
VITE_H5_QRCODE_PATH=/mobile/personal-open
VITE_H5_QRCODE_PATH_ENTERPRISE=/mobile/enterprise-open

View File

@ -1,3 +1,4 @@
VITE_API_BASE_URL=
VITE_H5_QRCODE_BASE_URL=
VITE_H5_QRCODE_PATH=/mobile/personal-open
VITE_H5_QRCODE_PATH_ENTERPRISE=/mobile/enterprise-open

View File

@ -0,0 +1,43 @@
import request from './request'
// 企业开户移动端H5确认页接口契约(2026-08-07新增,详见《缺失的后端接口.md》登记):
// - draft-detail:真实后端目前没有对应的匿名详情查询接口(与个人开户不同,企业开户后端仅提供
// confirm 这一个"直接提交确认"接口),本次前端参照个人开户 mobile/draft-detail 的契约模式
// 假设新增该接口,详情字段(尤其 id/channelName)均为前端假设,待后端确认。
// - send-verify-code:企业开户没有专属的发码接口,直接复用个人开户H5确认页在用的通用发码接口
// (该接口本身是透传给短信网关的通用参数,技术上可复用,待后端确认是否允许非个人开户场景调用)。
// tradeNo/tradeType 固定值取自个人开户H5当前已修正的正确取值(2026-08-06 修正记录,见
// personalOpen.js 同名字段注释):tradeNo 固定 "221506",tradeType 固定 "0"。
// - confirm:后端已提供的真实接口,语义为"申请人扫码核实信息后点击确认,提交凡荣e链",
// 与个人开户的 mobile/submit 不同,请求体是 {channelNo, verifyCode, confirmBto:{id,
// applicationStatus, confirmTime}},confirmBto.id 取自本次假设的 draft-detail 接口。
export const fetchDraftDetailApi = (applicationNo) =>
request.post('/api/wallet-management/enterprise-open-application/mobile/draft-detail', { applicationNo })
// cardName(银行卡户名):企业场景没有个人开户那种"卡户名与客户姓名一致"的天然对应关系,
// 企业银行账号的户名通常即企业名称,故取 enterpriseName 代替(与个人开户用 customerName
// 的处理思路一致)。
export const sendVerifyCodeApi = ({ channelNo, mobile, cardNo, cardName, idNo }) =>
request.post('/api/wallet-management/personal-opening/mobile/send-verify-code/general', {
channelNo,
tradeNo: '221506',
tradeType: '0',
mobile,
cardNo,
cardName,
idNo
})
// confirm 为真实已有接口,直接对接真实后端(不在 mock-server 实现),applicationStatus 固定传
// 'CONFIRMED'(枚举含义"已确认",对应"申请人扫码确认"这一步),confirmTime 为提交时的当前时间。
export const confirmApplicationApi = ({ channelNo, verifyCode, id, confirmTime }) =>
request.post('/api/wallet-management/enterprise-open-application/confirm', {
channelNo,
verifyCode,
confirmBto: {
id,
applicationStatus: 'CONFIRMED',
confirmTime
}
})

View File

@ -1,7 +1,8 @@
// 与主项目 src/router/index.js 的定位不同:本工程无需登录鉴权/动态菜单路由,
// 仅一个业务路由,直接静态声明即可。
// 业务路由静态声明即可。
import { createRouter, createWebHistory } from 'vue-router'
import PersonalOpenMobile from '@/views/PersonalOpenMobile.vue'
import EnterpriseOpenMobile from '@/views/EnterpriseOpenMobile.vue'
const router = createRouter({
history: createWebHistory(),
@ -11,7 +12,12 @@ const router = createRouter({
name: 'PersonalOpenMobile',
component: PersonalOpenMobile
},
// 兜底:任何其它路径都重定向到唯一业务页面(本工程不做多页面导航)
{
path: '/mobile/enterprise-open',
name: 'EnterpriseOpenMobile',
component: EnterpriseOpenMobile
},
// 兜底:未知路径重定向到个人开户页面(本工程默认业务场景,与既有行为保持一致)
{ path: '/:pathMatch(.*)*', redirect: '/mobile/personal-open' }
]
})

View File

@ -0,0 +1,246 @@
<!-- src/views/EnterpriseOpenMobile.vue -->
<!-- 企业开户移动端H5确认页(2026-08-07新增),扫码入口来自管理后台企业开户表单"邀请扫码办理"
生成的二维码与个人开户H5(PersonalOpenMobile.vue)"整屏替换单步"视觉不同,按产品提供的
原型截图实现为"手风琴式竖直步骤列表":顶部蓝色横幅(业务确认+银行品牌),下方1-4步骤单页内
纵向排列,已完成步骤折叠为"✓ 步骤名"一行,当前步骤展开显示内容,未开始步骤折叠为灰色
"圆圈数字 步骤名"一行截图里没有任何"上一步"按钮,本页面同样不支持步骤回退(与企业开户
confirm 接口 DRAFTCONFIRMED 的单向状态迁移语义一致)
后端契约缺口(详见缺失的后端接口.md src/api/enterpriseOpen.js 顶部注释):
- mobile/draft-detail 是本次前端假设新增的接口,后端目前未提供
- 发验证码复用个人开户H5在用的通用接口
- 提交确认(confirm)是真实已有接口,本地 mock-server 未实现,只能连真实后端环境验证 -->
<template>
<div class="page">
<div v-if="loading" class="center-box">
<van-loading type="spinner" size="24px">加载中...</van-loading>
</div>
<van-empty v-else-if="loadError" image="error" :description="loadError" class="center-box" />
<template v-else>
<div class="banner">
<span class="banner-title">业务确认</span>
<div class="banner-brand">
<span class="brand-badge">+</span>
<span class="brand-name">浙江稠州商业银行</span>
</div>
</div>
<div class="step-list">
<div class="step-row" :class="rowClass('INFO')">
<div class="step-row-header">
<span class="step-icon">
<van-icon v-if="isDone('INFO')" name="success" class="icon-done" />
<span v-else class="step-number">1</span>
</span>
<span class="step-label">信息核对</span>
</div>
<EnterpriseInfoConfirmStep
v-if="currentStep === 'INFO'"
:detail="draftDetail"
@next="currentStep = 'AGREEMENT'"
/>
</div>
<div class="step-row" :class="rowClass('AGREEMENT')">
<div class="step-row-header">
<span class="step-icon">
<van-icon v-if="isDone('AGREEMENT')" name="success" class="icon-done" />
<span v-else class="step-number">2</span>
</span>
<span class="step-label">相关协议</span>
</div>
<EnterpriseAgreementStep v-if="currentStep === 'AGREEMENT'" @next="currentStep = 'SMS'" />
</div>
<div class="step-row" :class="rowClass('SMS')">
<div class="step-row-header">
<span class="step-icon">
<van-icon v-if="isDone('SMS')" name="success" class="icon-done" />
<span v-else class="step-number">3</span>
</span>
<span class="step-label">手机验证</span>
</div>
<EnterpriseSmsVerifyStep
v-if="currentStep === 'SMS'"
:id="draftDetail?.id"
:channel-no="draftDetail?.channelNo"
:mobile="draftDetail?.mobilePhone"
:mobile-phone-mask="draftDetail?.mobilePhoneMask"
:card-no="draftDetail?.bankCardNumber"
:card-name="draftDetail?.enterpriseName"
:id-no="draftDetail?.certificateNumber"
@submitted="handleSubmitted"
/>
</div>
<div class="step-row" :class="rowClass('RESULT')">
<div class="step-row-header">
<span class="step-icon">
<van-icon v-if="isDone('RESULT')" name="success" class="icon-done" />
<span v-else class="step-number">4</span>
</span>
<span class="step-label">完成</span>
</div>
<ResultStep v-if="currentStep === 'RESULT'" :result="resultData" />
</div>
</div>
</template>
</div>
</template>
<script setup>
import { onMounted, ref } from 'vue'
import { useRoute } from 'vue-router'
import { fetchDraftDetailApi } from '@/api/enterpriseOpen'
import EnterpriseInfoConfirmStep from './steps/enterprise/EnterpriseInfoConfirmStep.vue'
import EnterpriseAgreementStep from './steps/enterprise/EnterpriseAgreementStep.vue'
import EnterpriseSmsVerifyStep from './steps/enterprise/EnterpriseSmsVerifyStep.vue'
import ResultStep from './steps/ResultStep.vue'
const route = useRoute()
const applicationNo = route.query.applicationNo || ''
const loading = ref(true)
const loadError = ref('')
const draftDetail = ref(null)
// 4:INFO() -> AGREEMENT() -> SMS(+) -> RESULT(,)
// "",退
const STEP_ORDER = ['INFO', 'AGREEMENT', 'SMS', 'RESULT']
const currentStep = ref('INFO')
const resultData = ref(null)
function isDone(key) {
return STEP_ORDER.indexOf(key) < STEP_ORDER.indexOf(currentStep.value)
}
function rowClass(key) {
if (isDone(key)) return 'is-done'
if (key === currentStep.value) return 'is-current'
return 'is-pending'
}
function handleSubmitted(data) {
resultData.value = data
currentStep.value = 'RESULT'
}
onMounted(async () => {
if (!applicationNo) {
loading.value = false
loadError.value = '链接无效,缺少申请编号,请重新扫描二维码'
return
}
try {
const res = await fetchDraftDetailApi(applicationNo)
if (res.data?.code === 200) {
draftDetail.value = res.data.data
} else {
loadError.value = res.data?.message || '获取开户信息失败,请联系柜员重新生成二维码'
}
} catch {
// request.js ,
loadError.value = '获取开户信息失败,请检查网络后重新扫描二维码'
} finally {
loading.value = false
}
})
</script>
<style scoped>
.page {
min-height: 100vh;
background: #f5f6f8;
}
.center-box {
display: flex;
align-items: center;
justify-content: center;
min-height: 60vh;
}
.banner {
display: flex;
align-items: center;
justify-content: space-between;
padding: 14px 16px;
background: linear-gradient(90deg, #1677ff, #4096ff);
color: #fff;
}
.banner-title {
font-size: 16px;
font-weight: 600;
}
.banner-brand {
display: flex;
align-items: center;
gap: 6px;
}
.brand-badge {
display: inline-flex;
align-items: center;
justify-content: center;
width: 18px;
height: 18px;
border-radius: 4px;
background: #fff;
color: #ee0a24;
font-weight: 700;
font-size: 13px;
}
.brand-name {
font-size: 12px;
}
.step-list {
padding: 12px 16px 24px;
}
.step-row {
background: #fff;
border-radius: 8px;
margin-bottom: 12px;
overflow: hidden;
}
.step-row-header {
display: flex;
align-items: center;
gap: 10px;
padding: 14px 16px;
}
.step-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 22px;
height: 22px;
flex-shrink: 0;
}
.step-number {
display: inline-flex;
align-items: center;
justify-content: center;
width: 22px;
height: 22px;
border-radius: 50%;
background: #e8e8e8;
color: #969799;
font-size: 13px;
}
.icon-done {
color: #07c160;
font-size: 20px;
}
.step-label {
font-size: 15px;
color: #969799;
}
.is-current .step-number {
background: #1677ff;
color: #fff;
}
.is-current .step-label {
color: #323233;
font-weight: 600;
}
.is-done .step-label {
color: #323233;
}
</style>

View File

@ -0,0 +1,76 @@
<template>
<div class="step-body">
<div class="agreement-row">
<van-checkbox v-model="serviceAgreed">
我已阅读并同意
<span class="link" @click.stop="confirmAgreement('service')">浙江稠州商业银行用户支付服务协议</span>
</van-checkbox>
</div>
<div class="agreement-row">
<van-checkbox v-model="privacyAgreed">
我已阅读并同意
<span class="link" @click.stop="confirmAgreement('privacy')">浙江稠州商业银行用户支付服务隐私政策</span>
</van-checkbox>
</div>
<div class="footer">
<van-button type="primary" block round :disabled="!bothAgreed" @click="$emit('next')"></van-button>
</div>
</div>
</template>
<script setup>
import { computed, ref } from 'vue'
import { showConfirmDialog } from 'vant'
defineEmits(['next'])
const serviceAgreed = ref(false)
const privacyAgreed = ref(false)
const bothAgreed = computed(() => serviceAgreed.value && privacyAgreed.value)
// / EnterpriseOpenForm.vue/PersonalOpenForm.vue 使
// (H5 AgreementStep.vue 使"PDF+",
// H5+,PDF/)
const AGREEMENT_CONTENT = {
service: {
title: '是否同意《浙江稠州商业银行用户支付服务协议》',
field: 'serviceAgreed'
},
privacy: {
title: '是否同意《浙江稠州商业银行用户支付服务隐私政策》',
field: 'privacyAgreed'
}
}
// :( window.confirm() ,
// Vant showConfirmDialog,,
// ),
async function confirmAgreement(type) {
const info = AGREEMENT_CONTENT[type]
try {
await showConfirmDialog({ title: info.title, confirmButtonText: '确定', cancelButtonText: '取消' })
if (info.field === 'serviceAgreed') serviceAgreed.value = true
else privacyAgreed.value = true
} catch {
// "",
}
}
</script>
<style scoped>
.step-body {
padding: 4px 16px 4px;
}
.agreement-row {
padding: 8px 0;
font-size: 14px;
color: #646566;
}
.link {
color: #1989fa;
}
.footer {
padding: 12px 0 4px;
}
</style>

View File

@ -0,0 +1,37 @@
<template>
<div class="step-body">
<van-cell-group inset>
<van-cell title="业务类型" value="稠州企业开户" />
<van-cell title="办理渠道" :value="dealChannelText" />
<van-cell title="客户" :value="detail?.enterpriseName || '-'" />
<van-cell title="手机号" :value="detail?.mobilePhoneMask || '-'" />
</van-cell-group>
<div class="footer">
<van-button type="primary" block round @click="$emit('next')">,</van-button>
</div>
</div>
</template>
<script setup>
import { computed } from 'vue'
const props = defineProps({
detail: { type: Object, default: null }
})
defineEmits(['next'])
// "",();""
// channelName (, src/api/enterpriseOpen.js ),
// "(线)",线H5
const dealChannelText = computed(() => `${props.detail?.channelName || '-'}(线上)`)
</script>
<style scoped>
.step-body {
padding: 0 0 4px;
}
.footer {
padding: 16px 16px 4px;
}
</style>

View File

@ -0,0 +1,155 @@
<template>
<div class="step-body">
<van-cell-group inset>
<van-cell title="手机号" :value="mobilePhoneMask || '-'" />
</van-cell-group>
<div class="verify-row">
<van-field v-model="verifyCode" type="digit" maxlength="6" placeholder="请输入短信验证码" class="verify-input" />
<van-button
size="small"
type="primary"
plain
class="send-btn"
:disabled="countdown > 0 || sending"
:loading="sending"
@click="handleSendCode"
>
{{ countdown > 0 ? `${countdown}s后重新获取` : '发送' }}
</van-button>
</div>
<div class="footer">
<van-button
type="primary"
block
round
:disabled="!verifyCode || submitting"
:loading="submitting"
loading-text="提交中..."
@click="handleSubmit"
>
提交
</van-button>
</div>
<!-- 耗时操作统一等待态:提交期间整页遮罩,禁止用户进行其它操作 -->
<van-overlay :show="submitting" class="submit-overlay">
<div class="overlay-center">
<van-loading type="spinner" color="#fff" size="28px">提交中,请稍候...</van-loading>
</div>
</van-overlay>
</div>
</template>
<script setup>
import { ref } from 'vue'
import dayjs from 'dayjs'
import { showToast } from 'vant'
import { sendVerifyCodeApi, confirmApplicationApi } from '@/api/enterpriseOpen'
const props = defineProps({
id: { type: [Number, String], default: undefined },
channelNo: { type: String, default: '' },
mobile: { type: String, default: '' },
mobilePhoneMask: { type: String, default: '' },
cardNo: { type: String, default: '' },
cardName: { type: String, default: '' },
idNo: { type: String, default: '' }
})
const emit = defineEmits(['submitted'])
const verifyCode = ref('')
const sending = ref(false)
const submitting = ref(false)
const countdown = ref(0)
let timer = null
function startCountdown() {
countdown.value = 60
timer = setInterval(() => {
countdown.value -= 1
if (countdown.value <= 0) {
clearInterval(timer)
timer = null
}
}, 1000)
}
async function handleSendCode() {
sending.value = true
try {
const res = await sendVerifyCodeApi({
channelNo: props.channelNo,
mobile: props.mobile,
cardNo: props.cardNo,
cardName: props.cardName,
idNo: props.idNo
})
if (res.data?.code === 200) {
showToast('验证码已发送')
startCountdown()
}
// 200,
} finally {
sending.value = false
}
}
// : enterprise-open-application/confirm ,confirmTime
async function handleSubmit() {
submitting.value = true
try {
const res = await confirmApplicationApi({
channelNo: props.channelNo,
verifyCode: verifyCode.value,
id: props.id,
confirmTime: dayjs().format('YYYY-MM-DD HH:mm:ss')
})
if (res.data?.code === 200) {
emit('submitted', res.data.data)
} else {
// :,,便
verifyCode.value = ''
}
} finally {
submitting.value = false
}
}
</script>
<style scoped>
.step-body {
padding: 0 0 4px;
}
.verify-row {
display: flex;
align-items: center;
gap: 8px;
margin: 16px;
background: #f7f8fa;
border-radius: 8px;
padding: 4px 12px 4px 0;
}
.verify-input {
flex: 1;
background: transparent;
}
.send-btn {
flex-shrink: 0;
white-space: nowrap;
}
.footer {
padding: 4px 16px 4px;
}
.submit-overlay {
display: flex;
align-items: center;
justify-content: center;
}
.overlay-center {
display: flex;
flex-direction: column;
align-items: center;
}
</style>

View File

@ -380,6 +380,34 @@
<PersonalCustomerPicker ref="legalRepPickerRef" @select="handleSelectLegalRep" />
<PersonalCustomerPicker ref="rolePickerRef" @select="handleRolePersonSelect" />
<PersonalCustomerPicker ref="beneficiaryPickerRef" @select="handleAddBeneficiary" />
<!-- "确认"按钮的二次确认弹窗,仿 PersonalOpenForm.vue:confirm=文案+三按钮态,
qrcode=邀请扫码办理成功后的二维码展示态 -->
<a-modal
v-model:open="confirmModalVisible"
title="提示"
:footer="null"
:mask-closable="false"
@cancel="handleModalCancel"
>
<template v-if="confirmModalStep === 'confirm'">
<p>是否确认开户?</p>
<div class="confirm-modal-actions">
<a-space>
<a-button type="primary" :loading="modalSaving" :disabled="modalInviting" @click="handleModalSave"></a-button>
<a-button :disabled="modalSaving || modalInviting" @click="handleModalCancel"></a-button>
<a-button :loading="modalInviting" :disabled="modalSaving" @click="handleModalInvite"></a-button>
</a-space>
</div>
</template>
<template v-else>
<div class="qrcode-panel">
<qrcode-vue :value="qrCodeUrl" :size="200" level="M" />
<p class="qrcode-application-no">申请编号:{{ qrApplicationNo }}</p>
<a-button @click="handleModalCancel"></a-button>
</div>
</template>
</a-modal>
</a-spin>
</div>
</template>
@ -389,6 +417,7 @@ import { ref, reactive, computed, onMounted, onUnmounted } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { message, Modal } from 'ant-design-vue'
import { PlusOutlined } from '@ant-design/icons-vue'
import QrcodeVue from 'qrcode.vue'
import ChannelSelect from '@/components/ChannelSelect.vue'
import DictSelect from '@/components/DictSelect.vue'
import LicenseUpload from '@/components/LicenseUpload.vue'
@ -427,6 +456,15 @@ const channelNo = ref(undefined)
const agreementChecked = ref(false)
const submitting = ref(false)
// ""( create 使):confirm=+,
// qrcode=,仿 PersonalOpenForm.vue
const confirmModalVisible = ref(false)
const confirmModalStep = ref('confirm')
const modalSaving = ref(false)
const modalInviting = ref(false)
const qrCodeUrl = ref('')
const qrApplicationNo = ref('')
const businessLicenseFileNo = ref('')
const legalPersonIdFrontFileNo = ref('')
const legalPersonIdBackFileNo = ref('')
@ -554,6 +592,8 @@ let smsTimer = null
const busy = computed(
() =>
submitting.value ||
modalSaving.value ||
modalInviting.value ||
businessLicenseUploading.value ||
legalPersonIdFrontUploading.value ||
legalPersonIdBackUploading.value ||
@ -565,7 +605,7 @@ const busyTip = computed(() => {
if (legalPersonIdFrontUploading.value || legalPersonIdBackUploading.value) return '身份证照片上传中,请稍候...'
if (beneficiaryProofUploading.value) return '受益人材料证明上传中,请稍候...'
if (smsSending.value) return '验证码发送中,请稍候...'
if (submitting.value) return '提交中,请稍候...'
if (submitting.value || modalSaving.value || modalInviting.value) return '提交中,请稍候...'
return ''
})
@ -1055,50 +1095,52 @@ function buildBeneficiaryBto(bene) {
return bto
}
// create/update payload,create(/) update()
// , update id
function buildSubmitPayload() {
return {
organizationId: form.organizationId,
enterpriseId: form.enterpriseId,
enterpriseName: form.enterpriseName,
businessLicense: form.businessLicense,
certificateType: 'BUSINESS_LICENSE',
certificateNumber: form.certificateNumber,
certificateEffectiveDate: form.certificateEffectiveDate,
certificateExpiryDate: form.certificateExpiryDate,
certificateAddress: form.certificateAddress,
businessScope: form.businessScope,
industry: form.industry,
mobilePhone: smsForm.mobile,
bankCardNumber: form.bankCardNumber,
bankNo: form.bankNo,
bankName: form.bankName,
...buildLegalRepPayload(),
...buildControllerPayload(),
...buildLeaderPayload(),
...buildOperatorPayload(),
channelNo: channelNo.value,
businessLicenseFileNo: businessLicenseFileNo.value,
// create/update verifyCode ,.md
verifyCode: smsForm.verifyCode,
enterpriseAccountOpenApplicationFileBtoList: [
{ fileNo: businessLicenseFileNo.value, fileType: '12' },
{ fileNo: legalPersonIdFrontFileNo.value, fileType: '01' },
{ fileNo: legalPersonIdBackFileNo.value, fileType: '02' },
{ fileNo: beneficiaryProofFileNo.value, fileType: '14' }
],
enterpriseAccountOpenApplicationBeneficiaryBtoList: beneficiaryList.value.map(buildBeneficiaryBto)
}
}
// "": update ,(
// PersonalOpenForm.vue handleEditSubmit )
async function handleSubmit() {
submitting.value = true
try {
const payload = {
organizationId: form.organizationId,
enterpriseId: form.enterpriseId,
enterpriseName: form.enterpriseName,
businessLicense: form.businessLicense,
certificateType: 'BUSINESS_LICENSE',
certificateNumber: form.certificateNumber,
certificateEffectiveDate: form.certificateEffectiveDate,
certificateExpiryDate: form.certificateExpiryDate,
certificateAddress: form.certificateAddress,
businessScope: form.businessScope,
industry: form.industry,
mobilePhone: smsForm.mobile,
bankCardNumber: form.bankCardNumber,
bankNo: form.bankNo,
bankName: form.bankName,
...buildLegalRepPayload(),
...buildControllerPayload(),
...buildLeaderPayload(),
...buildOperatorPayload(),
channelNo: channelNo.value,
businessLicenseFileNo: businessLicenseFileNo.value,
// create/update verifyCode ,.md
verifyCode: smsForm.verifyCode,
enterpriseAccountOpenApplicationFileBtoList: [
{ fileNo: businessLicenseFileNo.value, fileType: '12' },
{ fileNo: legalPersonIdFrontFileNo.value, fileType: '01' },
{ fileNo: legalPersonIdBackFileNo.value, fileType: '02' },
{ fileNo: beneficiaryProofFileNo.value, fileType: '14' }
],
enterpriseAccountOpenApplicationBeneficiaryBtoList: beneficiaryList.value.map(buildBeneficiaryBto)
}
let res
if (isCreate.value) {
res = await createEnterpriseOpenApplicationApi(payload)
} else {
// : list/detail id, applicationNo ,
res = await updateEnterpriseOpenApplicationApi({ id: route.query.applicationNo, ...payload })
}
// : list/detail id, applicationNo ,
const res = await updateEnterpriseOpenApplicationApi({ id: route.query.applicationNo, ...buildSubmitPayload() })
if (res.data.code === 200) {
message.success(isCreate.value ? '开户申请已提交' : '修改成功')
message.success('修改成功')
goBack()
}
} finally {
@ -1106,12 +1148,62 @@ async function handleSubmit() {
}
}
// "":create ();edit 沿
async function handleConfirmButtonClick() {
const valid = await validateBeforeSubmit()
if (!valid) return
if (isCreate.value) {
confirmModalStep.value = 'confirm'
confirmModalVisible.value = true
return
}
await handleSubmit()
}
// "": create 稿,
async function handleModalSave() {
modalSaving.value = true
try {
const res = await createEnterpriseOpenApplicationApi(buildSubmitPayload())
if (res.data.code === 200) {
message.success('开户申请已提交')
confirmModalVisible.value = false
goBack()
}
} finally {
modalSaving.value = false
}
}
// H5 :/,(),
// ,; .env.production,
// VITE_H5_QRCODE_PATH_ENTERPRISE( VITE_H5_QRCODE_PATH ,)
function buildQrCodeUrl(applicationNo) {
const base = import.meta.env.VITE_H5_QRCODE_BASE_URL || 'https://example.com'
const path = import.meta.env.VITE_H5_QRCODE_PATH_ENTERPRISE || '/mobile/enterprise-open'
return `${base}${path}?applicationNo=${applicationNo}`
}
// "":"",,
async function handleModalInvite() {
modalInviting.value = true
try {
const res = await createEnterpriseOpenApplicationApi(buildSubmitPayload())
if (res.data.code === 200) {
qrApplicationNo.value = res.data.data.applicationNo
qrCodeUrl.value = buildQrCodeUrl(res.data.data.applicationNo)
confirmModalStep.value = 'qrcode'
}
} finally {
modalInviting.value = false
}
}
// ""//"":,
function handleModalCancel() {
confirmModalVisible.value = false
}
function goBack() {
router.push('/wallet/enterprise-open')
}
@ -1163,4 +1255,15 @@ function goBack() {
height: auto;
max-height: none;
}
.confirm-modal-actions {
margin-top: 24px;
text-align: right;
}
.qrcode-panel {
text-align: center;
}
.qrcode-application-no {
margin: 12px 0 8px;
color: #333;
}
</style>