- 在API模型中新增证件类型字段(idType) - 修改API请求路径,移除SERVER_API_URL前缀 - 新增根据入驻申请创建商户的接口方法 - 在菜单配置中添加商城管理和商户入驻申请菜单项 - 新增商家入驻申请和申请成功页面路由 - 创建商家入驻申请表单页面,包含三步流程:基本信息、资质信息、确认提交 - 实现图片上传和预览功能,支持营业执照、身份证等资质文件上传 - 添加表单验证规则,确保必填信息完整 - 创建申请提交成功页面,提供返回首页和查看申请按钮 - 优化CMS网站搜索组件代码结构和格式
351 lines
10 KiB
Vue
351 lines
10 KiB
Vue
<template>
|
|
<ele-modal
|
|
:width="800"
|
|
:visible="visible"
|
|
:maskClosable="false"
|
|
:maxable="maxable"
|
|
title="审核商户入驻申请"
|
|
:body-style="{ paddingBottom: '28px' }"
|
|
@update:visible="updateVisible"
|
|
@ok="submitReview"
|
|
>
|
|
<a-tabs v-model:activeKey="activeKey">
|
|
<a-tab-pane key="1" tab="申请信息">
|
|
<a-descriptions bordered :column="{ xxl: 1, xl: 1, lg: 1, md: 1, sm: 1, xs: 1 }">
|
|
<a-descriptions-item label="商户名称">{{ data?.merchantName }}</a-descriptions-item>
|
|
<a-descriptions-item label="证件号码">{{ data?.merchantCode }}</a-descriptions-item>
|
|
<a-descriptions-item label="联系人手机号">{{ data?.phone }}</a-descriptions-item>
|
|
<a-descriptions-item label="联系人姓名">{{ data?.realName }}</a-descriptions-item>
|
|
<a-descriptions-item label="身份证号码">{{ data?.idCard }}</a-descriptions-item>
|
|
<a-descriptions-item label="店铺类型">{{ data?.shopType }}</a-descriptions-item>
|
|
<a-descriptions-item label="商户分类">{{ data?.category }}</a-descriptions-item>
|
|
<a-descriptions-item label="手续费">{{ data?.commission }}%</a-descriptions-item>
|
|
<a-descriptions-item label="创建时间">{{ data?.createTime }}</a-descriptions-item>
|
|
</a-descriptions>
|
|
</a-tab-pane>
|
|
|
|
<a-tab-pane key="2" tab="资质文件">
|
|
<a-row :gutter="24">
|
|
<a-col :span="8">
|
|
<div class="file-preview">
|
|
<h4>营业执照</h4>
|
|
<a-image
|
|
v-if="data?.yyzz"
|
|
:src="data?.yyzz"
|
|
:width="150"
|
|
:height="150"
|
|
/>
|
|
<a-empty v-else description="未上传" />
|
|
</div>
|
|
</a-col>
|
|
|
|
<a-col :span="8">
|
|
<div class="file-preview">
|
|
<h4>身份证正面</h4>
|
|
<a-image
|
|
v-if="data?.sfz1"
|
|
:src="data?.sfz1"
|
|
:width="150"
|
|
:height="150"
|
|
/>
|
|
<a-empty v-else description="未上传" />
|
|
</div>
|
|
</a-col>
|
|
|
|
<a-col :span="8">
|
|
<div class="file-preview">
|
|
<h4>身份证反面</h4>
|
|
<a-image
|
|
v-if="data?.sfz2"
|
|
:src="data?.sfz2"
|
|
:width="150"
|
|
:height="150"
|
|
/>
|
|
<a-empty v-else description="未上传" />
|
|
</div>
|
|
</a-col>
|
|
|
|
<a-col :span="24" style="margin-top: 20px;">
|
|
<div class="file-preview">
|
|
<h4>其他资质文件</h4>
|
|
<div v-if="otherFiles.length > 0" class="files-grid">
|
|
<a-image
|
|
v-for="(file, index) in otherFiles"
|
|
:key="index"
|
|
:src="file"
|
|
:width="100"
|
|
:height="100"
|
|
style="margin-right: 10px; margin-bottom: 10px;"
|
|
/>
|
|
</div>
|
|
<a-empty v-else description="未上传其他资质文件" />
|
|
</div>
|
|
</a-col>
|
|
</a-row>
|
|
</a-tab-pane>
|
|
|
|
<a-tab-pane key="3" tab="审核处理">
|
|
<a-form
|
|
ref="formRef"
|
|
:model="reviewForm"
|
|
:rules="reviewRules"
|
|
:label-col="{ span: 6 }"
|
|
:wrapper-col="{ span: 18 }"
|
|
>
|
|
<a-form-item label="审核结果" name="status" required>
|
|
<a-radio-group v-model:value="reviewForm.status">
|
|
<a-radio :value="1">通过</a-radio>
|
|
<a-radio :value="2">驳回</a-radio>
|
|
</a-radio-group>
|
|
</a-form-item>
|
|
|
|
<a-form-item
|
|
v-if="reviewForm.status === 2"
|
|
label="驳回原因"
|
|
name="reason"
|
|
required
|
|
>
|
|
<a-textarea
|
|
v-model:value="reviewForm.reason"
|
|
:rows="4"
|
|
:maxlength="200"
|
|
placeholder="请输入驳回原因"
|
|
/>
|
|
</a-form-item>
|
|
|
|
<a-form-item
|
|
v-if="reviewForm.status === 1"
|
|
label="手续费(%)"
|
|
name="commission"
|
|
>
|
|
<a-input-number
|
|
v-model:value="reviewForm.commission"
|
|
:step="0.1"
|
|
:max="100"
|
|
:min="0"
|
|
:precision="2"
|
|
placeholder="请输入手续费"
|
|
/>
|
|
</a-form-item>
|
|
|
|
<a-form-item
|
|
v-if="reviewForm.status === 1"
|
|
label="是否自营"
|
|
name="ownStore"
|
|
>
|
|
<a-switch
|
|
checked-children="是"
|
|
un-checked-children="否"
|
|
:checked="reviewForm.ownStore === 1"
|
|
@update:checked="val => reviewForm.ownStore = val ? 1 : 0"
|
|
/>
|
|
</a-form-item>
|
|
|
|
<a-form-item
|
|
v-if="reviewForm.status === 1"
|
|
label="是否推荐"
|
|
name="recommend"
|
|
>
|
|
<a-switch
|
|
checked-children="是"
|
|
un-checked-children="否"
|
|
:checked="reviewForm.recommend === 1"
|
|
@update:checked="val => reviewForm.recommend = val ? 1 : 0"
|
|
/>
|
|
</a-form-item>
|
|
|
|
<a-form-item
|
|
v-if="reviewForm.status === 1"
|
|
label="是否需要审核"
|
|
name="goodsReview"
|
|
>
|
|
<a-switch
|
|
checked-children="是"
|
|
un-checked-children="否"
|
|
:checked="reviewForm.goodsReview === 1"
|
|
@update:checked="val => reviewForm.goodsReview = val ? 1 : 0"
|
|
/>
|
|
</a-form-item>
|
|
|
|
<a-form-item
|
|
v-if="reviewForm.status === 1"
|
|
label="创建商户"
|
|
name="createMerchant"
|
|
>
|
|
<a-switch
|
|
checked-children="是"
|
|
un-checked-children="否"
|
|
:checked="reviewForm.createMerchant"
|
|
@update:checked="val => reviewForm.createMerchant = val"
|
|
/>
|
|
<div class="tips">审核通过后是否立即创建商户</div>
|
|
</a-form-item>
|
|
|
|
<a-form-item label="备注" name="comments">
|
|
<a-textarea
|
|
v-model:value="reviewForm.comments"
|
|
:rows="4"
|
|
:maxlength="200"
|
|
placeholder="请输入备注信息"
|
|
/>
|
|
</a-form-item>
|
|
</a-form>
|
|
</a-tab-pane>
|
|
</a-tabs>
|
|
</ele-modal>
|
|
</template>
|
|
|
|
<script lang="ts" setup>
|
|
import { ref, reactive, watch } from 'vue';
|
|
import { Form, message } from 'ant-design-vue';
|
|
import { checkShopMerchantApply, createMerchantFromApply } from '@/api/shop/shopMerchantApply';
|
|
import { ShopMerchantApply } from '@/api/shop/shopMerchantApply/model';
|
|
|
|
const useForm = Form.useForm;
|
|
|
|
const props = defineProps<{
|
|
visible: boolean;
|
|
data?: ShopMerchantApply | null;
|
|
}>();
|
|
|
|
const emit = defineEmits<{
|
|
(e: 'done'): void;
|
|
(e: 'update:visible', visible: boolean): void;
|
|
}>();
|
|
|
|
// 提交状态
|
|
const loading = ref(false);
|
|
// 是否显示最大化切换按钮
|
|
const maxable = ref(true);
|
|
// 表单引用
|
|
const formRef = ref<any>(null);
|
|
// 当前标签页
|
|
const activeKey = ref('1');
|
|
|
|
// 其他资质文件
|
|
const otherFiles = ref<string[]>([]);
|
|
|
|
// 审核表单
|
|
const reviewForm = reactive({
|
|
status: 1, // 默认通过
|
|
reason: '',
|
|
commission: 0,
|
|
ownStore: 0,
|
|
recommend: 0,
|
|
goodsReview: 1,
|
|
createMerchant: true, // 默认创建商户
|
|
comments: ''
|
|
});
|
|
|
|
// 审核表单验证规则
|
|
const reviewRules = reactive({
|
|
status: [
|
|
{ required: true, message: '请选择审核结果', trigger: 'change' }
|
|
],
|
|
reason: [
|
|
{ required: true, message: '请输入驳回原因', trigger: 'blur' }
|
|
]
|
|
});
|
|
|
|
/* 更新visible */
|
|
const updateVisible = (value: boolean) => {
|
|
emit('update:visible', value);
|
|
};
|
|
|
|
// 提交审核
|
|
const submitReview = () => {
|
|
if (!formRef.value) return;
|
|
|
|
formRef.value
|
|
.validate()
|
|
.then(() => {
|
|
loading.value = true;
|
|
|
|
const formData = {
|
|
applyId: props.data?.applyId,
|
|
status: reviewForm.status,
|
|
reason: reviewForm.reason,
|
|
commission: reviewForm.commission,
|
|
ownStore: reviewForm.ownStore,
|
|
recommend: reviewForm.recommend,
|
|
goodsReview: reviewForm.goodsReview,
|
|
comments: reviewForm.comments
|
|
};
|
|
|
|
checkShopMerchantApply(formData)
|
|
.then(async (msg) => {
|
|
message.success(msg);
|
|
|
|
// 如果审核通过且需要创建商户
|
|
if (reviewForm.status === 1 && reviewForm.createMerchant && props.data?.applyId) {
|
|
try {
|
|
await createMerchantFromApply(props.data.applyId);
|
|
message.success('商户创建成功');
|
|
} catch (e) {
|
|
message.error('商户创建失败: ' + (e as Error).message);
|
|
}
|
|
}
|
|
|
|
loading.value = false;
|
|
updateVisible(false);
|
|
emit('done');
|
|
})
|
|
.catch((e) => {
|
|
loading.value = false;
|
|
message.error(e.message);
|
|
});
|
|
})
|
|
.catch(() => {});
|
|
};
|
|
|
|
const { resetFields } = useForm(reviewForm, reviewRules);
|
|
|
|
watch(
|
|
() => props.visible,
|
|
(visible) => {
|
|
if (visible) {
|
|
resetFields();
|
|
reviewForm.status = 1;
|
|
reviewForm.createMerchant = true;
|
|
|
|
// 解析其他资质文件
|
|
otherFiles.value = [];
|
|
if (props.data?.files) {
|
|
try {
|
|
const files = JSON.parse(props.data.files);
|
|
if (Array.isArray(files)) {
|
|
otherFiles.value = files;
|
|
} else {
|
|
otherFiles.value = [props.data.files];
|
|
}
|
|
} catch {
|
|
otherFiles.value = [props.data.files];
|
|
}
|
|
}
|
|
}
|
|
},
|
|
{ immediate: true }
|
|
);
|
|
</script>
|
|
|
|
<style lang="less" scoped>
|
|
.file-preview {
|
|
text-align: center;
|
|
|
|
h4 {
|
|
margin-bottom: 10px;
|
|
font-weight: normal;
|
|
}
|
|
}
|
|
|
|
.files-grid {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.tips {
|
|
font-size: 12px;
|
|
color: #999;
|
|
margin-top: 5px;
|
|
}
|
|
</style> |