jcloud/dashboard/src2/components/JsiteDomainUploadRealNameDialog.vue

475 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>
<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> 文件格式支持JPGPNGPDF单个文件不超过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>