乐刷进件页面搭建

This commit is contained in:
ren
2025-07-08 22:35:20 +08:00
parent 5b177269a3
commit 3020f7bae3
10 changed files with 1411 additions and 3 deletions

View File

@@ -7,8 +7,8 @@ VITE_PUBLIC_PATH=/
# 跨域代理,可以配置多个,请注意不要换行
# VITE_PROXY=[["/server","http://daxpay-api.test.yibeiguangnian.cn/"]]
# VITE_PROXY=[["/server","http://192.168.11.229:19999/"]]
VITE_PROXY=[["/server","https://pay1.bootx.cn/"]]
# VITE_PROXY=[["/server","https://dev.server.daxpay.cn/"]]
# VITE_PROXY=[["/server","https://pay1.bootx.cn/"]]
VITE_PROXY=[["/server","https://dev.server.daxpay.cn/"]]
# VITE_PROXY=[["/server","http://127.0.0.1:19999/"]]

View File

@@ -26,8 +26,8 @@
"dependencies": {
"@types/lodash-es": "^4.17.12",
"@unocss/reset": "^0.58.9",
"@vant/area-data": "^2.0.0",
"@vueuse/core": "^10.11.1",
"vue-qr": "^4.0.9",
"axios": "^1.7.9",
"date-fns": "^3.6.0",
"lodash-es": "^4.17.21",
@@ -37,6 +37,7 @@
"qs": "^6.13.1",
"vant": "^4.9.10",
"vue": "^3.5.13",
"vue-qr": "^4.0.9",
"vue-router": "4.2.5"
},
"devDependencies": {

8
pnpm-lock.yaml generated
View File

@@ -14,6 +14,9 @@ importers:
'@unocss/reset':
specifier: ^0.58.9
version: 0.58.9
'@vant/area-data':
specifier: ^2.0.0
version: 2.0.0
'@vueuse/core':
specifier: ^10.11.1
version: 10.11.1(vue@3.5.13(typescript@5.7.2))
@@ -1194,6 +1197,9 @@ packages:
peerDependencies:
vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0
'@vant/area-data@2.0.0':
resolution: {integrity: sha512-zgP4AA8z09S9QTNgVCCHo9cHjcybrv22RJDYPjuCkecn4SB98T5EoPQh2TwqbQXmUhbaOGgiZGy3OUaUxnY7qg==}
'@vant/popperjs@1.3.0':
resolution: {integrity: sha512-hB+czUG+aHtjhaEmCJDuXOep0YTZjdlRR+4MSmIFnkCQIxJaXLQdSsR90XWvAI2yvKUI7TCGqR8pQg2RtvkMHw==}
@@ -5497,6 +5503,8 @@ snapshots:
transitivePeerDependencies:
- rollup
'@vant/area-data@2.0.0': {}
'@vant/popperjs@1.3.0': {}
'@vant/use@1.6.0(vue@3.5.13(typescript@5.7.2))':

View File

@@ -98,6 +98,14 @@ export const DaxPayH5Route: RouteRecordRaw = {
title: '支付失败页面',
},
},
{
path: '/onboarded/leshua',
name: 'leshua',
component: () => import('@/views/daxpay/h5/onboarded/leshua/index.vue'),
meta: {
title: '乐刷进件',
},
},
],
}
/**

View File

@@ -0,0 +1,71 @@
export type ErrorMessageMode = 'none' | 'modal' | 'message' | undefined
export type SuccessMessageMode = ErrorMessageMode
export interface RequestOptions {
// Splicing request parameters to url
joinParamsToUrl?: boolean
// Format request parameter time
formatDate?: boolean
// Whether to process the request result
isTransformResponse?: boolean
// Whether to return native response headers
// For example: use this attribute when you need to get the response headers
isReturnNativeResponse?: boolean
// 是否加入网址
joinPrefix?: boolean
// 接口地址,如果将其留空,请使用默认 apiUrl
apiUrl?: string
// 请求拼接路径
urlPrefix?: string
// 错误消息提示类型
errorMessageMode?: ErrorMessageMode
// Success message prompt type
successMessageMode?: SuccessMessageMode
// Whether to add a timestamp
joinTime?: boolean
ignoreCancelToken?: boolean
// Whether to send token in header
withToken?: boolean
// 请求重试机制
retryRequest?: RetryRequest
}
export interface RetryRequest {
isOpenRetry: boolean
count: number
waitTime: number
}
/**
* 通用响应类
*/
export interface Result<T = any> {
code: number
type: 'success' | 'error' | 'warning'
msg: string
traceId: string | null | undefined
data: T
}
/**
* 分页响应类
*/
export interface PageResult<T = any> {
current: number
records: Array<T>
size: number
total: number
}
// multipart/form-data: upload file
export interface UploadFileParams {
// Other parameters
data?: Recordable
// File parameter interface field name
name?: string
// file name
file: File | Blob
// file name
filename?: string
[key: string]: any
}

View File

@@ -0,0 +1,61 @@
import type { PageResult } from './axios'
/**
* 分页参数
*/
export interface PageParam {
// 每页数量
size: number
// 当前页数
current: number
}
/**
* 分页表格列表对象
*/
export interface TablePageModel<T = any> {
// 分页参数
pages: PageParam
// 查询参数
queryParam: any
// 结果
pagination: PageResult<T>
}
/**
* 基础实体对象
*/
export interface BaseEntity {
id?: number | string | null
createTime?: string | null
}
/**
* 商户应用基础实体对象
*/
export interface MchEntity extends BaseEntity {
// 服务商号
isvNo?: string
// 服务商名称
isvName?: string
// 代理商号
agentNo?: string
// 代理商名称
agentName?: string
// 商户号
mchNo?: string
// 商户名称
mchName?: string
// 应用号
appId?: string
// 应用名称
appName?: string
}
/**
* 键值对对象
*/
export interface KeyValue {
key: string
value: string
}

View File

@@ -0,0 +1,270 @@
import { unref } from 'vue'
import type { MchEntity } from './base'
import { http } from '@/utils/http/axios'
import type { PageResult, Result } from '#/axios'
/**
* 获取单条
*/
export function findById(id) {
return http.request<Result<IsvMchApply>>({
url: '/isv/mch/apply/findById',
method: 'get',
params: { id: unref(id) },
})
}
/**
* 分页
*/
export function page(params) {
return http.request<Result<PageResult<IsvMchApply>>>({
url: '/isv/mch/apply/page',
method: 'get',
params,
})
}
/**
* 保存
*/
export function save(obj: IsvMchApply) {
return http.request<Result<string>>({
url: '/isv/mch/apply/create',
method: 'post',
data: obj,
})
}
/**
* 更新
*/
export function update(obj: IsvMchApply) {
return http.request({
url: '/isv/mch/apply/update',
method: 'post',
data: obj,
})
}
/**
* 提交
*/
export function submit(id) {
return http.request({
url: '/isv/mch/apply/submit',
method: 'post',
params: { id },
})
}
/**
* 根据通道查询进件申请类型下拉列表
*/
export function dropdownByChannel(channel) {
return http.request({
url: '/isv/mch/apply/type/dropdownByChannel',
method: 'get',
params: { channel },
})
}
/**
* 删除
*/
export function del(id) {
return http.request({
url: '/isv/mch/apply/delete',
method: 'post',
params: { id },
})
}
/**
* 同步
*/
export function syncInfo(id) {
return http.request({
url: '/isv/mch/apply/sync',
method: 'post',
params: { id },
})
}
/**
* 生成进件商户
*/
export function genMchInfo(param) {
return http.request({
url: '/isv/mch/apply/genMchInfo',
method: 'post',
data: param,
})
}
/**
* 商户进件申请单
*/
export interface IsvMchApply extends MchEntity {
// 进件通道
channel?: string
// 进件类型
applyType?: string
// 服务商
isvNo?: string
// 进件的商户类型
applyMchType?: string
// 商户号
mchNo?: string
// 单据名称
name?: string
// 表单数据
formData?: string
// 外部状态
outStatus?: string
// 状态
status?: string
// 错误提示
errorMsg?: string
}
/**
* 进件商户申请信息
* @author xxm
* @since 2025/6/9
*/
export interface OnbMerchantApply {
/** 商户类型 @see OnbMerchantTypeEnum */
merchantType?: string
/** 商户全称 */
merchantName?: string
/** 商户简称 */
merchantShortName?: string
}
/**
* 进件申请法人信息
* @author xxm
* @since 2025/6/9
*/
export interface OnbLegalApply {
/** 法人姓名 */
legalName?: string
/** 身份证号 */
certNo?: string
/** 身份证有效期类型 */
certPeriodLong?: boolean
/** 身份证开始时间 */
certStartDate?: string
/** 身份证结束时间 */
certEndDate?: string
/** 身份证正面照片 */
certFrontPic?: string
/** 身份证正面照片路径 */
certFrontPicUrl?: string
/** 身份证反面照片 */
certBackPic?: string
/** 身份证反面照片路径 */
certBackPicUrl?: string
}
/**
* 进件营业执照信息
* @author xxm
* @since 2025/6/9
*/
export interface OnbLicenseApply {
/** 营业执照号 */
licenseNo?: string
/** 营业执照名称 */
licenseName?: string
/** 执照地址-省市区编码 */
licenseRegionCode?: string[]
/** 营业执照详细地址 */
licenseAddress?: string
/** 营业执照有效期类型 */
licensePeriodLong?: boolean
/** 营业执照开始日期 */
licenseStartDate?: string
/** 营业执照结束日期 */
licenseEndDate?: string
/** 营业执照照片 */
licensePic?: string
/** 营业执照照片路径 */
licensePicUrl?: string
}
/**
* 经营场所信息
* @author xxm
* @since 2025/6/9
*/
export interface OnbShopApply {
/** 经营场所名称 */
shopName?: string
/** 省市区编码 */
shopRegionCode?: string[]
/** 经营场所详细地址 */
shopAddress?: string
/** 门头照 */
shopDoorPic?: string
/** 门头照路径 */
shopDoorPicUrl?: string
/** 室内照 */
shopInsidePic?: string
/** 室内照路径 */
shopInsidePicUrl?: string
/** 收银台照片 */
shopCashierPic?: string
/** 收银台照片路径 */
shopCashierPicUrl?: string
}
/**
* 进件结算卡信息
* @author xxm
* @since 2025/6/9
*/
export interface OnbBankAccountApply {
/** 账户类型 */
bankAccountType?: string
/** 银行卡账户名 */
bankAccountName?: string
/** 银行卡号 */
bankCardNo?: string
/** 银行卡开户行联行号 */
bankBranchNo?: string
/** 银行预留手机号 */
bankPhone?: string
/** 银行卡正面照片 */
bankCardPic?: string
/** 银行卡正面照片路径 */
bankCardPicUrl?: string
}
/**
* 持卡人信息
* @author xxm
* @since 2025/6/9
*/
export interface OnbCardHolderApply {
/** 持卡人姓名 */
holderName?: string
/** 身份证号 */
certNo?: string
/** 身份证有效期类型 */
certPeriodLong?: boolean
/** 身份证开始时间 */
certStartDate?: string
/** 身份证结束时间 */
certEndDate?: string
/** 身份证正面照片 */
certFrontPic?: string
/** 身份证正面照片路径 */
certFrontPicUrl?: string
/** 身份证反面照片 */
certBackPic?: string
/** 身份证反面照片路径 */
certBackPicUrl?: string
/** 非法人结算授权函图片 */
letterOfAuthPic?: string
/** 非法人结算授权函图片路径 */
letterOfAuthPicUrl?: string
}

View File

@@ -0,0 +1,20 @@
<template>
<van-uploader v-model="fileList" :max-count="1" :preview-image="true" :after-read="afterRead" :before-read="beforeRead()" />
</template>
<script setup>
// 上传文件列表
const fileList = ref([
{ url: 'https://fastly.jsdelivr.net/npm/@vant/assets/sand.jpeg' },
])
// 上传前回调
function beforeRead() {
console.log('上传前')
}
// 上传后回调
function afterRead(file) {
console.log(file)
}
</script>

View File

@@ -0,0 +1,844 @@
<!-- eslint-disable vue/valid-attribute-name -->
<!-- eslint-disable format/prettier -->
<template>
<div class="leshuaOnBoarded">
<!-- 步骤页 -->
<div class="stepsBox">
<span class="current">{{ currentObj.currentIndex }} </span>/{{ currentObj.date.length }}
<span class="stepName"> {{ currentObj.currentTitle }}</span>
<!-- 经营信息 结算账户 -->
</div>
<div class="formBox">
<van-form ref="formRef" @failed="onFailed">
<!-- 第一模块 -->
<template v-if="currentObj.currentIndex === 1">
<div class="commonTitle">
联系人信息
</div>
<van-cell-group inset>
<!-- 商户类型 -->
<van-field name="merchantType" label="商户类型:" label-align="top" required>
<template #input>
<van-radio-group
v-model="formData.mchApply.merchant.merchantType"
direction="horizontal"
>
<van-radio name="micro">
小微商户
</van-radio>
<van-radio name="individual">
个体工商户
</van-radio>
<van-radio name="enterprise">
企业
</van-radio>
</van-radio-group>
</template>
</van-field>
<!-- 商户简称 -->
<van-field
v-model="formData.mchApply.merchant.merchantShortName"
label-align="top"
name="merchantShortName"
placeholder="请输入"
label="商户简称:"
required
clearable
:rules="rulesOne.merchantShortName"
/>
</van-cell-group>
<div class="commonTitle">
法人信息
</div>
<van-cell-group inset>
<!-- 身份证正面 -->
<van-field name="checkboxGroup" label="身份证正面:" label-align="top" required>
<template #input>
<upLoadView />
</template>
</van-field>
<!-- 身份证反面 -->
<van-field name="checkboxGroup" label="身份证反面:" label-align="top" required>
<template #input>
<upLoadView />
</template>
</van-field>
<!-- 身份证姓名 -->
<van-field
v-model="formData.mchApply.legal.legalName"
label-align="top"
name="legalName"
placeholder="请输入"
label="身份证姓名:"
required
clearable
:rules="rulesOne.legalName"
/>
<!-- 身份证号码 -->
<van-field
v-model="formData.mchApply.legal.certNo"
label-align="top"
name="certNo"
placeholder="请输入"
label="身份证号码:"
required
clearable
:rules="rulesOne.certNo"
/>
<!-- 身份证有效期 -->
<van-field label="身份证有效期:" label-align="top">
<template #input>
<van-switch v-model="formData.mchApply.legal.certPeriodLong">
<template #node>
<div class="vSwitch">
{{ formData.mchApply.legal.certPeriodLong ? "长期" : "" }}
</div>
</template>
</van-switch>
</template>
</van-field>
<!-- 证件有效期开始日期 -->
<van-field
v-model="formData.mchApply.legal.certStartDate"
name="certStartDate"
label="证件有效期开始日期:"
label-align="top"
readonly
placeholder="请选择"
required
:clearable="true"
:rules="rulesOne.certStartDate"
@click="datePickerObj.openPickObj('certStartDate')"
/>
<!-- 证件有效期结束日期 -->
<van-field
v-if="!formData.mchApply.legal.certPeriodLong"
v-model="formData.mchApply.legal.certEndDate"
readonly
name="certEndDate"
label="证件有效期结束日期:"
placeholder="请选择"
label-align="top"
required
clearable
:rules="rulesOne.certEndDate"
@click="datePickerObj.openPickObj('certEndDate')"
/>
<!-- 联系人手机号 -->
<van-field
v-model="formData.mchApply.other.contactPhone"
label-align="top"
name="contactPhone"
placeholder="请输入"
label="联系人手机号:"
required
clearable
:rules="rulesOne.contactPhone"
/>
</van-cell-group>
<template
v-if="['individual', 'enterprise'].includes(formData.mchApply.merchant.merchantType)"
>
<div class="commonTitle">
营业执照信息
</div>
<van-cell-group inset>
<!-- 营业执照照片 -->
<van-field name="licensePicUrl" label="营业执照照片:" label-align="top" required>
<template #input>
<upLoadView />
</template>
</van-field>
<!-- 营业执照号 -->
<van-field
v-model="formData.mchApply.license.licenseNo"
label-align="top"
name="licenseNo"
placeholder="请输入"
label="营业执照号:"
required
clearable
:rules="rulesOne.licenseNo"
/>
<!-- 营业执照名称 -->
<van-field
v-model="formData.mchApply.license.licenseName"
label-align="top"
name="licenseName"
placeholder="请输入"
label="营业执照名称:"
required
clearable
:rules="rulesOne.licenseName"
/>
<!-- 营业执照详细地址 -->
<van-field
v-model="formData.mchApply.license.licenseAddress"
label-align="top"
name="licenseAddress"
placeholder="请输入"
label="营业执照详细地址:"
required
clearable
:rules="rulesOne.licenseAddress"
/>
<!-- 注册有效期 -->
<van-field label="注册有效期:" label-align="top">
<template #input>
<van-switch v-model="formData.mchApply.license.licensePeriodLong">
<template #node>
<div class="vSwitch">
{{ formData.mchApply.license.licensePeriodLong ? "长期" : "" }}
</div>
</template>
</van-switch>
</template>
</van-field>
<!-- 开始日期 -->
<van-field
v-model="formData.mchApply.license.licenseStartDate"
name="licenseStartDate"
label="开始日期:"
label-align="top"
readonly
placeholder="请选择"
required
clearable
:rules="rulesOne.licenseStartDate"
@click="datePickerObj.openPickObj('licenseStartDate')"
/>
<!-- 结束日期 -->
<van-field
v-if="!formData.mchApply.license.licensePeriodLong"
v-model="formData.mchApply.license.licenseEndDate"
readonly
name="licenseEndDate"
label="结束日期:"
placeholder="请选择"
label-align="top"
required
clearable
:rules="rulesOne.licenseEndDate"
@click="datePickerObj.openPickObj('licenseEndDate')"
/>
</van-cell-group>
</template>
</template>
<!-- 第二模块 -->
<template v-if="currentObj.currentIndex === 2">
<div class="commonTitle">
经营场所信息
</div>
<van-cell-group inset>
<!-- 门店名称 -->
<van-field
v-model="formData.mchApply.shop.shopName"
label-align="top"
name="shopName"
placeholder="请输入"
label="门店名称:"
required
:rules="rulesTwo.shopName"
clearable
/>
<!-- 经营类目 -->
<van-field name="mccCodes" label="经营类目:" label-align="top" required>
<template #input>
<!-- formData.mchApply.other.mccCodes -->
<van-field
v-model="categoryStr"
is-link
readonly
name="mccCodes"
placeholder="请选择经营类目"
:rules="rulesTwo.mccCodes"
@click="cascaderObj.openCategotyBtn"
/>
</template>
</van-field>
<!-- 经营场所所在区县 -->
<van-field name="shopRegionCode" label="经营场所所在区县:" label-align="top" required>
<template #input>
<!-- formData.mchApply.shop.shopRegionCode -->
<van-field
v-model="areaStr"
is-link
readonly
name="shopRegionCode"
:rules="rulesTwo.shopRegionCode"
placeholder="请选择所在地区"
@click="cascaderObj.openAreaBtn"
/>
</template>
</van-field>
<!-- 经营场所详细地址 -->
<van-field
v-model="formData.mchApply.shop.shopAddress"
label-align="top"
name="shopAddress"
placeholder="请输入"
label="经营场所详细地址:"
required
:rules="rulesTwo.shopAddress"
clearable
/>
<van-field name="checkboxGroup" label="商户门头图片:" label-align="top" required>
<template #input>
<upLoadView />
</template>
</van-field>
<van-field name="checkboxGroup" label="营业场所室内照片:" label-align="top" required>
<template #input>
<upLoadView />
</template>
</van-field>
<van-field
name="checkboxGroup"
label="营业场所室内照片2"
label-align="top"
required
>
<template #input>
<upLoadView />
</template>
</van-field>
</van-cell-group>
</template>
<!-- 第二模块 -->
<template v-if="currentObj.currentIndex === 3">
<div class="commonTitle">
结算卡信息
</div>
<van-cell-group inset>
<!-- 银行卡正面 -->
<van-field name="checkboxGroup" label="银行卡正面:" label-align="top" required>
<template #input>
<upLoadView />
</template>
</van-field>
<!-- 银行卡开户名 -->
<van-field
v-model="formData.mchApply.bankAccount.bankAccountName"
label-align="top"
name="bankAccountName"
placeholder="请输入"
label="银行卡开户名:"
required
:rules="rulesThree.bankAccountName"
clearable
/>
<!-- 银行卡号 -->
<van-field
v-model="formData.mchApply.shop.bankCardNo"
label-align="top"
name="bankCardNo"
placeholder="请输入"
label="银行卡号:"
required
clearable
:rules="rulesThree.bankCardNo"
/>
<!-- 开户银行联行号 -->
<van-field
v-model="formData.mchApply.bankAccount.bankBranchNo"
label-align="top"
name="bankBranchNo"
placeholder="请输入"
label="开户银行联行号:"
required
clearable
:rules="rulesThree.bankBranchNo"
/>
</van-cell-group>
</template>
</van-form>
</div>
<div class="btnContain">
<div class="btnBox">
<van-button v-if="currentObj.currentIndex > 1" type="primary" block @click="prevClick">
上一步
</van-button>
<van-button v-if="currentObj.currentIndex < currentObj.date.length" type="primary" block @click="nextClick">
下一步
</van-button>
<van-button v-if="currentObj.currentIndex === currentObj.date.length" type="primary" block @click="submitClick">
提交
</van-button>
</div>
<div class="btnBox">
<van-button type="primary" plain block @click="saveClick">
暂存
</van-button>
</div>
</div>
<!-- 公共时间弹窗 -->
<van-popup v-model:show="datePickerObj.showDatePicker" destroy-on-close position="bottom">
<van-date-picker
v-model="datePickerObj.datePickerValue"
:columns-type="datePickerObj.columType"
:min-date="new Date(1950, 0, 1)"
:max-date="new Date(2099, 0, 1)"
@cancel="datePickerObj.closePickObj"
@confirm="datePickerObj.onConfirm"
/>
</van-popup>
<!-- 级联选择器 -->
<van-popup v-model:show="cascaderObj.showDialog" round position="bottom">
<!-- 区域 -->
<van-cascader
v-if="cascaderObj.status === 'area'"
v-model="cascaderObj.areaValue"
title="请选择"
:field-names="cascaderObj.fieldNames"
:options="cascaderObj.areaOptions"
@close="cascaderObj.closeDialog"
@finish="cascaderObj.onFinish"
/>
<!-- 类目 -->
<van-cascader
v-if="cascaderObj.status === 'category'"
v-model="cascaderObj.cateoryValue"
title="请选择"
:field-names="cascaderObj.fieldNames"
:options="cascaderObj.cateoryOptions"
@close="cascaderObj.closeDialog"
@finish="cascaderObj.onFinish"
/>
</van-popup>
</div>
</template>
<script setup>
// import type { MerchantApply } from '../common/onBoarded.api.ts'
import { useCascaderAreaData } from '@vant/area-data'
import { showNotify } from 'vant'
import upLoadView from '../components/upLoadView.vue'
import { findAllProvinceAndCityAndArea, mccTree } from './leshua.api'
// 控制当前页面数据对象
const currentObj = reactive({
currentIndex: 3, // 当前页面的值
// 数据
date: [
{
index: 1,
title: '主体信息',
},
{
index: 2,
title: '经营信息',
},
{
index: 3,
title: '结算账户',
},
],
currentTitle: computed(() => {
const title = currentObj.date.find(item => item.index === currentObj.currentIndex).title
return title
}),
})
// 表单ref对象
const formRef = ref(null)
// 表单数据对象
// MerchantApply
const formData = ref({
mchApply: {
merchant: {},
legal: {},
license: {},
shop: {},
bankAccount: {},
cardHolder: {},
other: {},
},
})
// 格式化树装数据
function formatDate(data) {
if (!Array.isArray(data)) {
return []
}
return data.map((item) => {
const { children, ...rest } = item
if (children && children.length > 0 && item.level < 3) {
rest.children = formatDate(children)
}
return rest
})
}
const categoryStr = ref('') // 类目接收字段名
const areaStr = ref('') // 地区接收字段名
// 级联选择框对象
const cascaderObj = reactive({
showDialog: false, // 弹窗
status: '', // 标识
fieldNames: {
text: 'name',
value: 'code',
children: 'children',
}, // 自定义名
areaOptions: [], // 区域数据
cateoryOptions: [], // 类目数据
areaValue: '', // 区域选择器的值
cateoryValue: '', // 类目选择器的值
openCategotyBtn: () => {
// 打开类目弹窗
cascaderObj.status = 'category'
cascaderObj.showDialog = true
},
openAreaBtn: () => {
// 打开区域弹窗
cascaderObj.status = 'area'
cascaderObj.showDialog = true
},
closeDialog: () => {
// 关闭弹窗
cascaderObj.showDialog = false
cascaderObj.status = ''
},
onFinish: (finish) => {
// 选择完成事件
if (cascaderObj.status === 'category') {
formData.value.mchApply.other.mccCodes = finish.selectedOptions.map(item => item.code) // 赋值需要传参的数据
categoryStr.value = finish.selectedOptions.map(item => item.name).join('/') // 用于表单显示
}
if (cascaderObj.status === 'area') {
formData.value.mchApply.shop.shopRegionCode = finish.selectedOptions.map(item => item.code) // 赋值需要传参的数据
areaStr.value = finish.selectedOptions.map(item => item.name).join('/')
}
cascaderObj.showDialog = false
},
// 获取类目数据
getCateoryData: () => {
mccTree().then(({ data }) => {
cascaderObj.cateoryOptions = formatDate(data)
})
},
// 获取地区树据
getAreaDate: () => {
findAllProvinceAndCityAndArea().then(({ data }) => {
cascaderObj.areaOptions = formatDate(data)
})
},
})
// 控制时间选择弹窗对象
const datePickerObj = reactive({
showDatePicker: false, // 控制选择弹窗是否显示
columType: ['year', 'month', 'day'], // 类型
statusType: '', // 标识 储存点击的是哪个时间框
datePickerValue: [new Date().getFullYear(), 0, 1], // 绑定的值(默认当前年月一号)
openPickObj: (type) => {
datePickerObj.statusType = type // 存储标识
datePickerObj.showDatePicker = true
},
onConfirm: () => {
// 确定选择
switch (datePickerObj.statusType) {
case 'certStartDate': // 证件有效期开始日期
formData.value.mchApply.legal.certStartDate = datePickerObj.datePickerValue.join('-')
break
case 'certEndDate': // 证件有效期结束日期
formData.value.mchApply.legal.certEndDate = datePickerObj.datePickerValue.join('-')
break
case 'licenseStartDate': // 营业执照开始日期
formData.value.mchApply.license.licenseStartDate = datePickerObj.datePickerValue.join('-')
break
case 'licenseEndDate': // 营业执照结束日期
formData.value.mchApply.license.licenseEndDate = datePickerObj.datePickerValue.join('-')
break
}
datePickerObj.showDatePicker = false
},
// 关闭弹窗
closePickObj: () => {
datePickerObj.statusType = '' // 存储标识
datePickerObj.showDatePicker = false
},
})
// 校验规则第一步
const rulesOne = reactive({
/* 联系人信息 */
// 商户简称
merchantShortName: [{ required: true, message: '请输入商户简称' }],
/* 法人信息 */
// 身份证姓名
legalName: [{ required: true, message: '请输入身份证姓名' }],
// 身份证号码
certNo: [
{ required: true, message: '请输入身份证号码' },
{
pattern: /^[1-9]\d{16}[0-9X]$/i,
message: '身份证号码格式错误',
},
],
// 开始时间
certStartDate: [{ required: true, message: '请选择证件开始时间' }],
// 结束时间
certEndDate: [{ required: true, message: '请选择证件结束时间' }],
// 手机号码
contactPhone: [
{ required: true, message: '请输入手机号' },
{
pattern: /^1[3-9]\d{9}$/,
message: '手机号格式错误',
},
],
/* 营业执照信息 */
// 营业执照号
licenseNo: [
{ required: true, message: '请输入营业执照号' },
{
pattern: /^\d{15}$|^\d{18}$/,
message: '营业执照号应为15位或18位数字',
},
],
// 营业执照名称
licenseName: [
{ required: true, message: '请输入营业执照名称' },
{
max: 100,
message: '营业执照名称不能超过100个字符',
},
],
// 营业执照详细地址
licenseAddress: [
{ required: true, message: '请输入营业执照详细地址' },
{
max: 200,
message: '地址不能超过200个字符',
},
],
// 注册开始日期
licenseStartDate: [{ required: true, message: '请选择注册开始日期' }],
// 注册结束日期(非长期时必填)
licenseEndDate: [{ required: true, message: '请选择注册结束日期' }],
})
// 校验规则第二步
const rulesTwo = reactive({
// 门店名称
shopName: [
{ required: true, message: '请输入门店名称' },
{
max: 200,
message: '名称不能超过32个字符',
},
],
// 经营类目
mccCodes: [{ required: true, message: '请选择经营类目' }],
// 所在区县
shopRegionCode: [{ required: true, message: '请选择所在区县' }],
// 详细地址
shopAddress: [
{ required: true, message: '请输入经营场所详细地址' },
{
max: 200,
message: '详细地址超过32个字符',
},
],
})
// 校验规则第三步
const rulesThree = reactive({
// 银行卡开户名
bankAccountName: [{ required: true, message: '请输入银行卡开户名' }],
// 银行卡号
bankCardNo: [{ required: true, message: '请输入银行卡号' }],
// 开户银行联行号
bankBranchNo: [
{ required: true, message: '请输入开户银行联行号' },
],
})
// 筛选出验证规则
function getFieldsByStep(index) {
switch (index) {
case 1:
return Object.keys(rulesOne)
case 2:
return Object.keys(rulesTwo)
case 3:
return Object.keys(rulesThree)
default:
return []
}
}
// 点击上一步
function prevClick() {
currentObj.currentIndex--
}
// 点击下一步进行校验
function nextClick() {
const fieldsToValidate = getFieldsByStep(currentObj.currentIndex)
formRef.value
.validate(fieldsToValidate)
.then(() => {
// 执行下一步操作
currentObj.currentIndex++
})
.catch((error) => {
console.log(error)
showNotify({ type: 'danger', message: '还有必填项未填!请仔细检查' })
})
}
// 提交
function submitClick() {
const fieldsToValidate = getFieldsByStep(currentObj.date.length) // 只验证最后一页
formRef.value
.validate(fieldsToValidate)
.then(() => {
// 执行下一步操作
})
.catch((error) => {
console.log(error)
showNotify({ type: 'danger', message: '还有必填项未填!请仔细检查' })
})
}
// 暂存
function saveClick() {
console.log('11')
}
onMounted(() => {
formData.value.mchApply.merchant.merchantType = 'micro' // 默认选择小微商户
cascaderObj.getCateoryData() // 获取类目
cascaderObj.getAreaDate() // 获取区域名
})
</script>
<style lang="less" scoped>
.leshuaOnBoarded {
width: 100%;
height: 100%;
.stepsBox {
width: 100%;
height: 5%;
display: flex;
align-items: center;
background-color: #f7f7f7;
padding: 0px 1.25rem;
font-size: 1rem;
font-weight: 600;
letter-spacing: 2px;
.current {
color: #448ef7;
}
}
.formBox {
width: 100%;
height: 80%;
overflow: scroll;
padding: 1.25rem 0rem;
// 公共头部
.commonTitle {
height: 3.125rem;
width: 100%;
display: flex;
align-items: center;
padding: 0rem 1.25rem;
background-color: #f7f7f7;
color: #448ef7;
letter-spacing: 1px;
position: relative;
font-weight: 600;
&::before {
position: absolute;
top: 50%;
left: 0.3125rem;
transform: translateY(-50%);
content: '';
width: 0.3125rem;
height: 1.25rem;
background-color: #448ef7;
}
}
}
.btnContain {
width: 100%;
height: 10%;
.btnBox {
display: flex;
gap: 1.25rem;
padding: 0px 1.25rem;
margin: 0.625rem 0rem;
}
}
}
</style>
<style lang="less">
.leshuaOnBoarded {
.van-form {
.van-cell-group {
&.van-cell-group--inset {
margin: 0 !important;
}
.van-cell {
.van-cell__title {
&.van-field__label--top {
margin-bottom: 0.625rem !important;
}
}
.van-cell__value {
// 单选
.van-radio-group {
.van-radio {
.van-radio__icon {
height: 1rem;
width: 1rem;
font-size: 1.125rem;
.van-icon {
width: 1rem;
height: 1rem;
line-height: 1;
}
}
.van-badge__wrapper {
&.van-icon {
width: 1rem;
height: 1rem;
}
}
}
}
.van-field__body {
input {
border: 0.0625rem solid #f5f5f5;
height: 3.125rem;
padding-left: 0.625rem;
padding-right: 2.5rem;
}
.van-field__clear {
position: absolute;
right: 0.625rem;
}
.van-switch {
.van-switch__node {
display: flex;
justify-content: center;
.vSwitch {
font-size: 0.75rem;
}
}
}
}
}
&.van-cell--clickable {
padding: 0rem 0rem !important;
display: flex;
align-items: center;
}
}
}
}
}
</style>

View File

@@ -0,0 +1,125 @@
import type {
OnbBankAccountApply,
OnbCardHolderApply,
OnbLegalApply,
OnbLicenseApply,
OnbMerchantApply,
OnbShopApply,
} from '../common/onBoarded.api'
import { http } from '@/utils/http/axios'
import type { Result } from '#/axios'
/**
* 查询
*/
export function getInfo(id) {
return http.request<Result<MerchantApply>>({
method: 'get',
url: '/leshua/mch/apply/findById',
params: { id },
})
}
/**
* 保存
*/
export function save(param: MerchantApply) {
return http.request<Result<void>>({
method: 'post',
url: '/leshua/mch/apply/save',
data: param,
})
}
/**
* 乐刷经营类目树
*/
export function mccTree() {
return http.request<Result<MccConst[]>>({
method: 'get',
url: '/leshua/mcc/tree',
})
}
/**
* 地区树
*/
export function findAllProvinceAndCityAndArea() {
return http.request<Result<Region[]>>({
url: '/china/region/findAllProvinceAndCityAndArea',
method: 'get',
})
}
/**
* 乐刷商户申请参数
*/
export interface MerchantApply {
/** 申请单ID */
applyId?: string
/** 商户申请参数 */
mchApply: MchApply
}
/**
* 商户申请信息
*/
export interface MchApply {
/** 商户信息 */
merchant: OnbMerchantApply
/** 法人信息 */
legal: OnbLegalApply
/** 商户资质图片信息 */
license: OnbLicenseApply
/** 经营场所名称 */
shop: OnbShopApply
/** 结算卡信息 */
bankAccount: OnbBankAccountApply
/** 持卡人信息 */
cardHolder: OnbCardHolderApply
/** 其他申请信息 */
other: OtherApply
}
/**
* 其他申请数据
*/
export interface OtherApply {
/** 联系人手机号 */
contactPhone?: string
/** 经营类目代码 */
mccCodes?: string[]
/** 开户银行省市 */
bankRegionCode?: string[]
/** 开户银行网点名称 */
bankName?: string
/** 法人手持结算授权合影 */
legaHandAuthPic?: string
/** 法人手持结算授权合影 */
legaHandAuthPicUrl?: string
}
/**
* 乐刷经营类目
*/
export interface MccConst {
/** 类目 */
code: string
/** 类目名称 */
name: string
/** 父类目 */
parentCode: string
/** 子类目 */
children?: MccConst[]
}
/**
* 区域
*/
export interface Region {
code: string
name: string
/** 省市区街道 */
level: 1 | 2 | 3 | 4
isLeaf?: boolean
children: Region[]
}