475 lines
16 KiB
Vue
475 lines
16 KiB
Vue
<template>
|
||
<Dialog v-bind="$attrs" :options="{ title: '上传实名资料', size: '2xl' }" v-model="show">
|
||
<template #body>
|
||
<div class="space-y-6">
|
||
<!-- 步骤指示器 -->
|
||
<div class="bg-gradient-to-r from-blue-50 to-indigo-50 rounded-xl p-6 border border-blue-200">
|
||
<div class="flex items-start">
|
||
<div class="flex-shrink-0">
|
||
<InfoIcon class="h-6 w-6 text-blue-600" />
|
||
</div>
|
||
<div class="ml-4">
|
||
<h3 class="text-lg font-medium text-blue-900 mb-2">实名认证说明</h3>
|
||
<div class="text-sm text-blue-700 space-y-2">
|
||
<p>• 个人用户:请上传身份证正反面照片</p>
|
||
<p>• 企业用户:请上传营业执照和法人身份证</p>
|
||
<p>• 文件格式支持:JPG、PNG、PDF,单个文件不超过5MB</p>
|
||
<p>• 请确保证件信息清晰可见,避免反光或模糊</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<form @submit.prevent="submitUpload" class="space-y-6">
|
||
<!-- 认证类型选择 -->
|
||
<div>
|
||
<label class="text-base font-medium text-gray-900 block mb-4">认证类型</label>
|
||
<div class="grid grid-cols-2 gap-4">
|
||
<label class="relative cursor-pointer">
|
||
<input
|
||
type="radio"
|
||
v-model="formData.certType"
|
||
value="personal"
|
||
class="sr-only peer"
|
||
/>
|
||
<div class="border-2 border-gray-200 rounded-xl p-4 peer-checked:border-blue-500 peer-checked:bg-blue-50 transition-all duration-200 hover:border-gray-300">
|
||
<div class="flex items-center">
|
||
<UserIcon class="h-6 w-6 text-gray-600 mr-3" />
|
||
<div>
|
||
<div class="font-medium text-gray-900">个人认证</div>
|
||
<div class="text-sm text-gray-600">使用个人身份证进行认证</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</label>
|
||
|
||
<label class="relative cursor-pointer">
|
||
<input
|
||
type="radio"
|
||
v-model="formData.certType"
|
||
value="enterprise"
|
||
class="sr-only peer"
|
||
/>
|
||
<div class="border-2 border-gray-200 rounded-xl p-4 peer-checked:border-blue-500 peer-checked:bg-blue-50 transition-all duration-200 hover:border-gray-300">
|
||
<div class="flex items-center">
|
||
<BuildingIcon class="h-6 w-6 text-gray-600 mr-3" />
|
||
<div>
|
||
<div class="font-medium text-gray-900">企业认证</div>
|
||
<div class="text-sm text-gray-600">使用营业执照进行认证</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 个人认证文件上传 -->
|
||
<div v-if="formData.certType === 'personal'" class="space-y-6">
|
||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||
<!-- 身份证正面 -->
|
||
<FileUpload
|
||
label="身份证正面"
|
||
description="请上传身份证正面照片"
|
||
:files="formData.idCardFront"
|
||
@update:files="formData.idCardFront = $event"
|
||
accept="image/*,.pdf"
|
||
:maxSize="5"
|
||
required
|
||
/>
|
||
|
||
<!-- 身份证反面 -->
|
||
<FileUpload
|
||
label="身份证反面"
|
||
description="请上传身份证反面照片"
|
||
:files="formData.idCardBack"
|
||
@update:files="formData.idCardBack = $event"
|
||
accept="image/*,.pdf"
|
||
:maxSize="5"
|
||
required
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 企业认证文件上传 -->
|
||
<div v-if="formData.certType === 'enterprise'" class="space-y-6">
|
||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||
<!-- 营业执照 -->
|
||
<FileUpload
|
||
label="营业执照"
|
||
description="请上传营业执照照片或扫描件"
|
||
:files="formData.businessLicense"
|
||
@update:files="formData.businessLicense = $event"
|
||
accept="image/*,.pdf"
|
||
:maxSize="5"
|
||
required
|
||
/>
|
||
|
||
<!-- 法人身份证 -->
|
||
<FileUpload
|
||
label="法人身份证"
|
||
description="请上传法人身份证正面照片"
|
||
:files="formData.legalIdCard"
|
||
@update:files="formData.legalIdCard = $event"
|
||
accept="image/*,.pdf"
|
||
:maxSize="5"
|
||
required
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 联系信息 -->
|
||
<div class="bg-gray-50 rounded-xl p-6">
|
||
<h3 class="text-lg font-medium text-gray-900 mb-4">联系信息</h3>
|
||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 mb-2">联系人姓名</label>
|
||
<input
|
||
type="text"
|
||
v-model="formData.contactName"
|
||
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||
placeholder="请输入联系人姓名"
|
||
required
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 mb-2">联系电话</label>
|
||
<input
|
||
type="tel"
|
||
v-model="formData.contactPhone"
|
||
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||
placeholder="请输入联系电话"
|
||
required
|
||
/>
|
||
</div>
|
||
|
||
<div class="md:col-span-2">
|
||
<label class="block text-sm font-medium text-gray-700 mb-2">邮箱地址</label>
|
||
<input
|
||
type="email"
|
||
v-model="formData.contactEmail"
|
||
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||
placeholder="请输入邮箱地址"
|
||
required
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 备注 -->
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 mb-2">备注说明(可选)</label>
|
||
<textarea
|
||
v-model="formData.remark"
|
||
rows="3"
|
||
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||
placeholder="如有特殊情况请在此说明..."
|
||
></textarea>
|
||
</div>
|
||
|
||
<!-- 提交按钮 -->
|
||
<div class="flex justify-end space-x-4 pt-6 border-t">
|
||
<Button variant="outline" @click="$emit('close')">
|
||
取消
|
||
</Button>
|
||
<Button
|
||
type="submit"
|
||
:loading="loading"
|
||
class="px-8 bg-blue-600 hover:bg-blue-700 text-white"
|
||
>
|
||
提交认证
|
||
</Button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</template>
|
||
</Dialog>
|
||
</template>
|
||
|
||
<script>
|
||
import { Dialog, Button, createResource } from 'jingrow-ui';
|
||
import { toast } from 'vue-sonner';
|
||
import InfoIcon from '~icons/lucide/info';
|
||
import UserIcon from '~icons/lucide/user';
|
||
import BuildingIcon from '~icons/lucide/building';
|
||
import UploadIcon from '~icons/lucide/upload';
|
||
import FileIcon from '~icons/lucide/file';
|
||
import XIcon from '~icons/lucide/x';
|
||
|
||
// 文件上传组件
|
||
const FileUpload = {
|
||
props: {
|
||
label: String,
|
||
description: String,
|
||
files: Array,
|
||
accept: String,
|
||
maxSize: Number,
|
||
required: Boolean
|
||
},
|
||
emits: ['update:files'],
|
||
components: { UploadIcon, FileIcon, XIcon },
|
||
template: `
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 mb-2">
|
||
{{ label }}
|
||
<span v-if="required" class="text-red-500">*</span>
|
||
</label>
|
||
<p class="text-sm text-gray-600 mb-3">{{ description }}</p>
|
||
|
||
<div class="space-y-3">
|
||
<!-- 文件上传区域 -->
|
||
<div
|
||
@click="triggerFileInput"
|
||
@dragover.prevent
|
||
@drop.prevent="handleDrop"
|
||
class="border-2 border-dashed border-gray-300 rounded-lg p-6 text-center cursor-pointer hover:border-blue-400 hover:bg-blue-50 transition-colors duration-200"
|
||
>
|
||
<UploadIcon class="h-8 w-8 text-gray-400 mx-auto mb-2" />
|
||
<p class="text-sm text-gray-600">点击上传或拖拽文件到此处</p>
|
||
<p class="text-xs text-gray-500 mt-1">
|
||
支持 {{ accept }} 格式,最大 {{ maxSize }}MB
|
||
</p>
|
||
</div>
|
||
|
||
<!-- 已上传文件列表 -->
|
||
<div v-if="files && files.length > 0" class="space-y-2">
|
||
<div
|
||
v-for="(file, index) in files"
|
||
:key="index"
|
||
class="flex items-center justify-between bg-gray-50 rounded-lg p-3"
|
||
>
|
||
<div class="flex items-center">
|
||
<FileIcon class="h-5 w-5 text-blue-600 mr-2" />
|
||
<div>
|
||
<div class="text-sm font-medium text-gray-900">{{ file.name }}</div>
|
||
<div class="text-xs text-gray-500">{{ formatFileSize(file.size) }}</div>
|
||
</div>
|
||
</div>
|
||
<button
|
||
@click="removeFile(index)"
|
||
class="text-gray-400 hover:text-red-500 transition-colors"
|
||
>
|
||
<XIcon class="h-4 w-4" />
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<input
|
||
ref="fileInput"
|
||
type="file"
|
||
:accept="accept"
|
||
multiple
|
||
class="hidden"
|
||
@change="handleFileSelect"
|
||
/>
|
||
</div>
|
||
`,
|
||
methods: {
|
||
triggerFileInput() {
|
||
this.$refs.fileInput.click();
|
||
},
|
||
handleFileSelect(event) {
|
||
const files = Array.from(event.target.files);
|
||
this.addFiles(files);
|
||
},
|
||
handleDrop(event) {
|
||
const files = Array.from(event.dataTransfer.files);
|
||
this.addFiles(files);
|
||
},
|
||
addFiles(newFiles) {
|
||
const validFiles = newFiles.filter(file => {
|
||
// 检查文件大小
|
||
if (file.size > this.maxSize * 1024 * 1024) {
|
||
toast.error(`文件 ${file.name} 超过 ${this.maxSize}MB 限制`);
|
||
return false;
|
||
}
|
||
return true;
|
||
});
|
||
|
||
const currentFiles = this.files || [];
|
||
this.$emit('update:files', [...currentFiles, ...validFiles]);
|
||
},
|
||
removeFile(index) {
|
||
const currentFiles = [...this.files];
|
||
currentFiles.splice(index, 1);
|
||
this.$emit('update:files', currentFiles);
|
||
},
|
||
formatFileSize(bytes) {
|
||
if (bytes === 0) return '0 Bytes';
|
||
const k = 1024;
|
||
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
||
}
|
||
}
|
||
};
|
||
|
||
export default {
|
||
name: 'JsiteDomainUploadRealNameDialog',
|
||
components: {
|
||
Dialog,
|
||
Button,
|
||
InfoIcon,
|
||
UserIcon,
|
||
BuildingIcon,
|
||
FileUpload
|
||
},
|
||
props: {
|
||
domain: String,
|
||
domainDoc: Object
|
||
},
|
||
emits: ['success', 'close'],
|
||
data() {
|
||
return {
|
||
show: true,
|
||
loading: false,
|
||
formData: {
|
||
certType: 'personal',
|
||
idCardFront: [],
|
||
idCardBack: [],
|
||
businessLicense: [],
|
||
legalIdCard: [],
|
||
contactName: '',
|
||
contactPhone: '',
|
||
contactEmail: '',
|
||
remark: ''
|
||
}
|
||
};
|
||
},
|
||
methods: {
|
||
async submitUpload() {
|
||
// 验证必填字段
|
||
if (!this.validateForm()) {
|
||
return;
|
||
}
|
||
|
||
this.loading = true;
|
||
|
||
try {
|
||
// 创建FormData对象
|
||
const formData = new FormData();
|
||
|
||
// 添加基本信息
|
||
formData.append('domain', this.domainDoc.domain);
|
||
formData.append('certType', this.formData.certType);
|
||
formData.append('contactName', this.formData.contactName);
|
||
formData.append('contactPhone', this.formData.contactPhone);
|
||
formData.append('contactEmail', this.formData.contactEmail);
|
||
formData.append('remark', this.formData.remark);
|
||
|
||
// 添加文件
|
||
if (this.formData.certType === 'personal') {
|
||
if (this.formData.idCardFront.length > 0) {
|
||
this.formData.idCardFront.forEach((file, index) => {
|
||
formData.append(`idCardFront_${index}`, file);
|
||
});
|
||
}
|
||
if (this.formData.idCardBack.length > 0) {
|
||
this.formData.idCardBack.forEach((file, index) => {
|
||
formData.append(`idCardBack_${index}`, file);
|
||
});
|
||
}
|
||
} else if (this.formData.certType === 'enterprise') {
|
||
if (this.formData.businessLicense.length > 0) {
|
||
this.formData.businessLicense.forEach((file, index) => {
|
||
formData.append(`businessLicense_${index}`, file);
|
||
});
|
||
}
|
||
if (this.formData.legalIdCard.length > 0) {
|
||
this.formData.legalIdCard.forEach((file, index) => {
|
||
formData.append(`legalIdCard_${index}`, file);
|
||
});
|
||
}
|
||
}
|
||
|
||
// 调用后端上传API
|
||
const uploadRequest = createResource({
|
||
url: 'jcloud.api.domain_west.upload_domain_real_name_files',
|
||
method: 'POST',
|
||
params: formData,
|
||
onSuccess: (response) => {
|
||
// 修复API响应处理逻辑,支持两种响应格式
|
||
if (response && response.message && response.message.status === "success") {
|
||
toast.success('实名资料上传成功,请等待审核');
|
||
this.$emit('success');
|
||
this.$emit('close');
|
||
} else if (response && response.status === "success") {
|
||
toast.success('实名资料上传成功,请等待审核');
|
||
this.$emit('success');
|
||
this.$emit('close');
|
||
} else {
|
||
const errorMessage = response?.message?.message || response?.message || '上传失败,请重试';
|
||
toast.error(errorMessage);
|
||
}
|
||
this.loading = false;
|
||
},
|
||
onError: (error) => {
|
||
console.error('上传实名资料失败:', error);
|
||
toast.error('上传失败,请重试');
|
||
this.loading = false;
|
||
}
|
||
});
|
||
|
||
uploadRequest.submit();
|
||
|
||
} catch (error) {
|
||
console.error('上传实名资料失败:', error);
|
||
toast.error('上传失败,请重试');
|
||
} finally {
|
||
this.loading = false;
|
||
}
|
||
},
|
||
validateForm() {
|
||
if (!this.formData.certType) {
|
||
toast.error('请选择认证类型');
|
||
return false;
|
||
}
|
||
|
||
if (this.formData.certType === 'personal') {
|
||
if (!this.formData.idCardFront.length) {
|
||
toast.error('请上传身份证正面');
|
||
return false;
|
||
}
|
||
if (!this.formData.idCardBack.length) {
|
||
toast.error('请上传身份证反面');
|
||
return false;
|
||
}
|
||
} else if (this.formData.certType === 'enterprise') {
|
||
if (!this.formData.businessLicense.length) {
|
||
toast.error('请上传营业执照');
|
||
return false;
|
||
}
|
||
if (!this.formData.legalIdCard.length) {
|
||
toast.error('请上传法人身份证');
|
||
return false;
|
||
}
|
||
}
|
||
|
||
if (!this.formData.contactName.trim()) {
|
||
toast.error('请填写联系人姓名');
|
||
return false;
|
||
}
|
||
|
||
if (!this.formData.contactPhone.trim()) {
|
||
toast.error('请填写联系电话');
|
||
return false;
|
||
}
|
||
|
||
if (!this.formData.contactEmail.trim()) {
|
||
toast.error('请填写邮箱地址');
|
||
return false;
|
||
}
|
||
|
||
// 验证邮箱格式
|
||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||
if (!emailRegex.test(this.formData.contactEmail)) {
|
||
toast.error('请填写正确的邮箱地址');
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
}
|
||
};
|
||
</script> |