Files
mp-vue/src/views/passport/merchant/apply.vue
赵忠林 a485faa0e4 feat(shop): 添加商户入驻申请功能
- 在API模型中新增证件类型字段(idType)
- 修改API请求路径,移除SERVER_API_URL前缀
- 新增根据入驻申请创建商户的接口方法
- 在菜单配置中添加商城管理和商户入驻申请菜单项
- 新增商家入驻申请和申请成功页面路由
- 创建商家入驻申请表单页面,包含三步流程:基本信息、资质信息、确认提交
- 实现图片上传和预览功能,支持营业执照、身份证等资质文件上传
- 添加表单验证规则,确保必填信息完整
- 创建申请提交成功页面,提供返回首页和查看申请按钮
- 优化CMS网站搜索组件代码结构和格式
2025-12-05 23:48:16 +08:00

582 lines
16 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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