- 在API模型中新增证件类型字段(idType) - 修改API请求路径,移除SERVER_API_URL前缀 - 新增根据入驻申请创建商户的接口方法 - 在菜单配置中添加商城管理和商户入驻申请菜单项 - 新增商家入驻申请和申请成功页面路由 - 创建商家入驻申请表单页面,包含三步流程:基本信息、资质信息、确认提交 - 实现图片上传和预览功能,支持营业执照、身份证等资质文件上传 - 添加表单验证规则,确保必填信息完整 - 创建申请提交成功页面,提供返回首页和查看申请按钮 - 优化CMS网站搜索组件代码结构和格式
582 lines
16 KiB
Vue
582 lines
16 KiB
Vue
<template>
|
||
<div class="merchant-apply-container">
|
||
<div class="merchant-apply-header">
|
||
<h1>商家入驻申请</h1>
|
||
<p>欢迎申请成为平台商家,请填写以下信息</p>
|
||
</div>
|
||
|
||
<a-steps :current="currentStep" style="margin-bottom: 30px">
|
||
<a-step title="基本信息" />
|
||
<a-step title="资质信息" />
|
||
<a-step title="确认提交" />
|
||
</a-steps>
|
||
|
||
<a-form
|
||
ref="formRef"
|
||
:model="form"
|
||
:rules="rules"
|
||
:label-col="{ span: 6 }"
|
||
:wrapper-col="{ span: 18 }"
|
||
class="merchant-apply-form"
|
||
>
|
||
<!-- 第一步:基本信息 -->
|
||
<div v-show="currentStep === 0">
|
||
<a-card title="基本信息" style="margin-bottom: 20px">
|
||
<a-row :gutter="24">
|
||
<a-col :span="12">
|
||
<a-form-item label="商户名称" name="merchantName">
|
||
<a-input
|
||
v-model:value="form.merchantName"
|
||
placeholder="请输入商户名称"
|
||
:maxlength="100"
|
||
/>
|
||
</a-form-item>
|
||
</a-col>
|
||
|
||
<a-col :span="12">
|
||
<a-form-item label="证件类型" name="idType">
|
||
<a-select
|
||
v-model:value="form.idType"
|
||
placeholder="请选择证件类型"
|
||
>
|
||
<a-select-option value="1">营业执照</a-select-option>
|
||
<a-select-option value="2">统一社会信用代码</a-select-option>
|
||
</a-select>
|
||
</a-form-item>
|
||
</a-col>
|
||
|
||
<a-col :span="12">
|
||
<a-form-item label="证件号码" name="merchantCode">
|
||
<a-input
|
||
v-model:value="form.merchantCode"
|
||
placeholder="请输入证件号码"
|
||
:maxlength="50"
|
||
/>
|
||
</a-form-item>
|
||
</a-col>
|
||
|
||
<a-col :span="12">
|
||
<a-form-item label="商户手机号" name="phone">
|
||
<a-input
|
||
v-model:value="form.phone"
|
||
placeholder="请输入联系人手机号"
|
||
:maxlength="11"
|
||
/>
|
||
</a-form-item>
|
||
</a-col>
|
||
|
||
<a-col :span="12">
|
||
<a-form-item label="商户姓名" name="realName">
|
||
<a-input
|
||
v-model:value="form.realName"
|
||
placeholder="请输入联系人姓名"
|
||
:maxlength="20"
|
||
/>
|
||
</a-form-item>
|
||
</a-col>
|
||
|
||
<a-col :span="12">
|
||
<a-form-item label="身份证号码" name="idCard">
|
||
<a-input
|
||
v-model:value="form.idCard"
|
||
placeholder="请输入法人身份证号码"
|
||
:maxlength="18"
|
||
/>
|
||
</a-form-item>
|
||
</a-col>
|
||
|
||
<a-col :span="12">
|
||
<a-form-item label="店铺类型" name="shopType">
|
||
<a-input
|
||
v-model:value="form.shopType"
|
||
placeholder="请输入店铺类型"
|
||
:maxlength="50"
|
||
/>
|
||
</a-form-item>
|
||
</a-col>
|
||
|
||
<a-col :span="12">
|
||
<a-form-item label="商户分类" name="category">
|
||
<a-input
|
||
v-model:value="form.category"
|
||
placeholder="请输入商户所属分类"
|
||
:maxlength="100"
|
||
/>
|
||
</a-form-item>
|
||
</a-col>
|
||
</a-row>
|
||
</a-card>
|
||
</div>
|
||
|
||
<!-- 第二步:资质信息 -->
|
||
<div v-show="currentStep === 1">
|
||
<a-card title="资质信息" style="margin-bottom: 20px">
|
||
<a-row :gutter="24">
|
||
<a-col :span="12">
|
||
<a-form-item label="营业执照" name="yyzz">
|
||
<div class="upload-container">
|
||
<a-image
|
||
v-if="form.yyzz"
|
||
:src="form.yyzz"
|
||
:width="120"
|
||
:height="120"
|
||
style="margin-bottom: 10px"
|
||
/>
|
||
<SelectFile
|
||
:placeholder="`请上传营业执照`"
|
||
:limit="1"
|
||
:data="yyzzImages"
|
||
@done="chooseYyzzImage"
|
||
@del="onDeleteYyzzImage"
|
||
/>
|
||
<p class="upload-tip">请上传清晰的营业执照照片</p>
|
||
</div>
|
||
</a-form-item>
|
||
</a-col>
|
||
|
||
<a-col :span="12">
|
||
<a-form-item label="身份证正面" name="sfz1">
|
||
<div class="upload-container">
|
||
<a-image
|
||
v-if="form.sfz1"
|
||
:src="form.sfz1"
|
||
:width="120"
|
||
:height="120"
|
||
style="margin-bottom: 10px"
|
||
/>
|
||
<SelectFile
|
||
:placeholder="`请上传身份证正面`"
|
||
:limit="1"
|
||
:data="sfz1Images"
|
||
@done="chooseSfz1Image"
|
||
@del="onDeleteSfz1Image"
|
||
/>
|
||
<p class="upload-tip">请上传清晰的身份证正面照片</p>
|
||
</div>
|
||
</a-form-item>
|
||
</a-col>
|
||
|
||
<a-col :span="12">
|
||
<a-form-item label="身份证反面" name="sfz2">
|
||
<div class="upload-container">
|
||
<a-image
|
||
v-if="form.sfz2"
|
||
:src="form.sfz2"
|
||
:width="120"
|
||
:height="120"
|
||
style="margin-bottom: 10px"
|
||
/>
|
||
<SelectFile
|
||
:placeholder="`请上传身份证反面`"
|
||
:limit="1"
|
||
:data="sfz2Images"
|
||
@done="chooseSfz2Image"
|
||
@del="onDeleteSfz2Image"
|
||
/>
|
||
<p class="upload-tip">请上传清晰的身份证反面照片</p>
|
||
</div>
|
||
</a-form-item>
|
||
</a-col>
|
||
|
||
<a-col :span="24">
|
||
<a-form-item label="资质图片" name="files">
|
||
<SelectFile
|
||
:placeholder="`请上传其他资质证明文件`"
|
||
:limit="9"
|
||
:data="files"
|
||
@done="chooseFiles"
|
||
@del="onDeleteFiles"
|
||
/>
|
||
<p class="upload-tip"
|
||
>可上传产品合格证、授权书等相关资质文件(最多9张)</p
|
||
>
|
||
</a-form-item>
|
||
</a-col>
|
||
</a-row>
|
||
</a-card>
|
||
</div>
|
||
|
||
<!-- 第三步:确认提交 -->
|
||
<div v-show="currentStep === 2">
|
||
<a-card title="申请信息确认" style="margin-bottom: 20px">
|
||
<a-descriptions
|
||
bordered
|
||
:column="{ xxl: 1, xl: 1, lg: 1, md: 1, sm: 1, xs: 1 }"
|
||
>
|
||
<a-descriptions-item label="商户名称">{{
|
||
form.merchantName
|
||
}}</a-descriptions-item>
|
||
<a-descriptions-item label="证件类型">{{
|
||
getIdTypeName(form.idType)
|
||
}}</a-descriptions-item>
|
||
<a-descriptions-item label="证件号码">{{
|
||
form.merchantCode
|
||
}}</a-descriptions-item>
|
||
<a-descriptions-item label="联系人手机号">{{
|
||
form.phone
|
||
}}</a-descriptions-item>
|
||
<a-descriptions-item label="联系人姓名">{{
|
||
form.realName
|
||
}}</a-descriptions-item>
|
||
<a-descriptions-item label="身份证号码">{{
|
||
form.idCard
|
||
}}</a-descriptions-item>
|
||
<a-descriptions-item label="店铺类型">{{
|
||
form.shopType
|
||
}}</a-descriptions-item>
|
||
<a-descriptions-item label="商户分类">{{
|
||
form.category
|
||
}}</a-descriptions-item>
|
||
<a-descriptions-item label="营业执照">
|
||
<a-image
|
||
v-if="form.yyzz"
|
||
:src="form.yyzz"
|
||
:width="120"
|
||
:height="120"
|
||
/>
|
||
</a-descriptions-item>
|
||
<a-descriptions-item label="身份证正面">
|
||
<a-image
|
||
v-if="form.sfz1"
|
||
:src="form.sfz1"
|
||
:width="120"
|
||
:height="120"
|
||
/>
|
||
</a-descriptions-item>
|
||
<a-descriptions-item label="身份证反面">
|
||
<a-image
|
||
v-if="form.sfz2"
|
||
:src="form.sfz2"
|
||
:width="120"
|
||
:height="120"
|
||
/>
|
||
</a-descriptions-item>
|
||
<a-descriptions-item label="其他资质文件">
|
||
<div v-if="files.length > 0" class="files-preview">
|
||
<a-image
|
||
v-for="(file, index) in files"
|
||
:key="index"
|
||
:src="file.url"
|
||
:width="80"
|
||
:height="80"
|
||
style="margin-right: 10px; margin-bottom: 10px"
|
||
/>
|
||
</div>
|
||
<span v-else>无</span>
|
||
</a-descriptions-item>
|
||
</a-descriptions>
|
||
|
||
<a-alert
|
||
message="请仔细核对以上信息,提交后将无法修改。审核结果将在3个工作日内通过短信通知您。"
|
||
type="info"
|
||
show-icon
|
||
style="margin-top: 20px"
|
||
/>
|
||
</a-card>
|
||
</div>
|
||
|
||
<!-- 操作按钮 -->
|
||
<div class="form-actions">
|
||
<a-button
|
||
v-if="currentStep > 0"
|
||
@click="prevStep"
|
||
style="margin-right: 10px"
|
||
>
|
||
上一步
|
||
</a-button>
|
||
|
||
<a-button v-if="currentStep < 2" type="primary" @click="nextStep">
|
||
下一步
|
||
</a-button>
|
||
|
||
<a-button
|
||
v-if="currentStep === 2"
|
||
type="primary"
|
||
@click="submitApply"
|
||
:loading="submitLoading"
|
||
>
|
||
提交申请
|
||
</a-button>
|
||
</div>
|
||
</a-form>
|
||
</div>
|
||
</template>
|
||
|
||
<script lang="ts" setup>
|
||
import { ref, reactive } from 'vue';
|
||
import { Form, message } from 'ant-design-vue';
|
||
import { useRouter } from 'vue-router';
|
||
import { addShopMerchantApply } from '@/api/shop/shopMerchantApply';
|
||
import { ShopMerchantApply } from '@/api/shop/shopMerchantApply/model';
|
||
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
|
||
import { FileRecord } from '@/api/system/file/model';
|
||
|
||
const useForm = Form.useForm;
|
||
const router = useRouter();
|
||
|
||
// 当前步骤
|
||
const currentStep = ref(0);
|
||
|
||
// 提交状态
|
||
const submitLoading = ref(false);
|
||
|
||
// 表单引用
|
||
const formRef = ref<any>(null);
|
||
|
||
// 图片数据
|
||
const yyzzImages = ref<ItemType[]>([]);
|
||
const sfz1Images = ref<ItemType[]>([]);
|
||
const sfz2Images = ref<ItemType[]>([]);
|
||
const files = ref<ItemType[]>([]);
|
||
|
||
// 表单数据
|
||
const form = reactive<ShopMerchantApply>({
|
||
type: 1,
|
||
idType: '1', // 默认营业执照
|
||
merchantName: undefined,
|
||
merchantCode: undefined,
|
||
image: undefined,
|
||
phone: undefined,
|
||
realName: undefined,
|
||
idCard: undefined,
|
||
shopType: undefined,
|
||
category: undefined,
|
||
commission: undefined,
|
||
keywords: undefined,
|
||
yyzz: undefined,
|
||
sfz1: undefined,
|
||
sfz2: undefined,
|
||
files: undefined,
|
||
userId: undefined,
|
||
ownStore: 0,
|
||
recommend: 0,
|
||
goodsReview: 1,
|
||
name2: undefined,
|
||
reason: undefined,
|
||
comments: '商家入驻申请',
|
||
status: 0,
|
||
sortNumber: 100,
|
||
tenantId: undefined
|
||
});
|
||
|
||
// 表单验证规则
|
||
const rules = reactive({
|
||
merchantName: [
|
||
{ required: true, message: '请输入商户名称', trigger: 'blur' }
|
||
],
|
||
idType: [{ required: true, message: '请选择证件类型', trigger: 'change' }],
|
||
merchantCode: [
|
||
{ required: true, message: '请输入证件号码', trigger: 'blur' }
|
||
],
|
||
phone: [{ required: true, message: '请输入联系人手机号', trigger: 'blur' }],
|
||
realName: [
|
||
{ required: true, message: '请输入联系人姓名', trigger: 'blur' }
|
||
],
|
||
idCard: [{ required: true, message: '请输入身份证号码', trigger: 'blur' }],
|
||
shopType: [{ required: true, message: '请输入店铺类型', trigger: 'blur' }],
|
||
category: [{ required: true, message: '请输入商户分类', trigger: 'blur' }],
|
||
yyzz: [{ required: true, message: '请上传营业执照', trigger: 'change' }],
|
||
sfz1: [{ required: true, message: '请上传身份证正面', trigger: 'change' }],
|
||
sfz2: [{ required: true, message: '请上传身份证反面', trigger: 'change' }]
|
||
});
|
||
|
||
// 获取证件类型名称
|
||
const getIdTypeName = (type: string | undefined) => {
|
||
switch (type) {
|
||
case '1':
|
||
return '营业执照';
|
||
case '2':
|
||
return '统一社会信用代码';
|
||
default:
|
||
return '';
|
||
}
|
||
};
|
||
|
||
// 图片选择处理
|
||
const chooseYyzzImage = (data: FileRecord) => {
|
||
yyzzImages.value = [
|
||
{
|
||
uid: data.id,
|
||
url: data.path,
|
||
status: 'done'
|
||
}
|
||
];
|
||
form.yyzz = data.path;
|
||
};
|
||
|
||
const onDeleteYyzzImage = () => {
|
||
yyzzImages.value = [];
|
||
form.yyzz = undefined;
|
||
};
|
||
|
||
const chooseSfz1Image = (data: FileRecord) => {
|
||
sfz1Images.value = [
|
||
{
|
||
uid: data.id,
|
||
url: data.path,
|
||
status: 'done'
|
||
}
|
||
];
|
||
form.sfz1 = data.path;
|
||
};
|
||
|
||
const onDeleteSfz1Image = () => {
|
||
sfz1Images.value = [];
|
||
form.sfz1 = undefined;
|
||
};
|
||
|
||
const chooseSfz2Image = (data: FileRecord) => {
|
||
sfz2Images.value = [
|
||
{
|
||
uid: data.id,
|
||
url: data.path,
|
||
status: 'done'
|
||
}
|
||
];
|
||
form.sfz2 = data.path;
|
||
};
|
||
|
||
const onDeleteSfz2Image = () => {
|
||
sfz2Images.value = [];
|
||
form.sfz2 = undefined;
|
||
};
|
||
|
||
const chooseFiles = (data: FileRecord) => {
|
||
files.value.push({
|
||
uid: data.id,
|
||
url: data.path,
|
||
status: 'done'
|
||
});
|
||
};
|
||
|
||
const onDeleteFiles = (index: number) => {
|
||
files.value.splice(index, 1);
|
||
};
|
||
|
||
// 步骤控制
|
||
const nextStep = () => {
|
||
if (!formRef.value) return;
|
||
|
||
// 验证当前步骤的表单
|
||
const validateFields = getValidateFields();
|
||
if (validateFields.length > 0) {
|
||
formRef.value
|
||
.validateFields(validateFields)
|
||
.then(() => {
|
||
currentStep.value++;
|
||
})
|
||
.catch(() => {
|
||
message.error('请完善必填信息');
|
||
});
|
||
} else {
|
||
currentStep.value++;
|
||
}
|
||
};
|
||
|
||
const prevStep = () => {
|
||
currentStep.value--;
|
||
};
|
||
|
||
// 获取当前步骤需要验证的字段
|
||
const getValidateFields = () => {
|
||
switch (currentStep.value) {
|
||
case 0:
|
||
return [
|
||
'merchantName',
|
||
'idType',
|
||
'merchantCode',
|
||
'phone',
|
||
'realName',
|
||
'idCard',
|
||
'shopType',
|
||
'category'
|
||
];
|
||
case 1:
|
||
return ['yyzz', 'sfz1', 'sfz2'];
|
||
default:
|
||
return [];
|
||
}
|
||
};
|
||
|
||
// 提交申请
|
||
const submitApply = () => {
|
||
if (!formRef.value) return;
|
||
|
||
submitLoading.value = true;
|
||
|
||
// 处理表单数据
|
||
const formData = {
|
||
...form,
|
||
files:
|
||
files.value.length > 0
|
||
? JSON.stringify(files.value.map((item) => item.url))
|
||
: undefined
|
||
};
|
||
|
||
addShopMerchantApply(formData)
|
||
.then((msg) => {
|
||
submitLoading.value = false;
|
||
message.success('申请提交成功,我们将在3个工作日内完成审核!');
|
||
// 跳转到申请成功页面
|
||
router.push('/merchant/success');
|
||
})
|
||
.catch((e) => {
|
||
submitLoading.value = false;
|
||
message.error(e.message || '申请提交失败,请稍后重试');
|
||
});
|
||
};
|
||
|
||
const { resetFields } = useForm(form, rules);
|
||
</script>
|
||
|
||
<style lang="less" scoped>
|
||
.merchant-apply-container {
|
||
max-width: 1000px;
|
||
margin: 20px auto;
|
||
padding: 20px;
|
||
background: #fff;
|
||
border-radius: 4px;
|
||
}
|
||
|
||
.merchant-apply-header {
|
||
text-align: center;
|
||
margin-bottom: 30px;
|
||
|
||
h1 {
|
||
font-size: 24px;
|
||
color: #333;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
p {
|
||
color: #666;
|
||
}
|
||
}
|
||
|
||
.merchant-apply-form {
|
||
margin-top: 20px;
|
||
}
|
||
|
||
.upload-container {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
}
|
||
|
||
.upload-tip {
|
||
color: #999;
|
||
font-size: 12px;
|
||
margin-top: 5px;
|
||
}
|
||
|
||
.files-preview {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.form-actions {
|
||
text-align: center;
|
||
margin-top: 30px;
|
||
}
|
||
</style>
|