From 792f8d6abe783bb8bbd897deb9bd1581e20bce17 Mon Sep 17 00:00:00 2001 From: Mrx <18278715334@163.com> Date: Tue, 17 Mar 2026 17:19:00 +0800 Subject: [PATCH 01/11] add --- .eslintrc-auto-import.json | 1 + auto-imports.d.ts | 1 + src/api/index.js | 19 +- src/constants/menu.js | 1 + .../admin/certification-reviews/index.vue | 441 +++++++++++++ .../components/EnterpriseInfo.vue | 603 ++++++++++++++++-- .../components/ManualReviewPending.vue | 100 +++ src/pages/certification/index.vue | 66 +- src/router/index.js | 6 + 9 files changed, 1180 insertions(+), 58 deletions(-) create mode 100644 src/pages/admin/certification-reviews/index.vue create mode 100644 src/pages/certification/components/ManualReviewPending.vue diff --git a/.eslintrc-auto-import.json b/.eslintrc-auto-import.json index 666770a..55a19f9 100644 --- a/.eslintrc-auto-import.json +++ b/.eslintrc-auto-import.json @@ -366,6 +366,7 @@ "useThrottleFn": true, "useThrottledRefHistory": true, "useTimeAgo": true, + "useTimeAgoIntl": true, "useTimeout": true, "useTimeoutFn": true, "useTimeoutPoll": true, diff --git a/auto-imports.d.ts b/auto-imports.d.ts index 1af4034..516d1ce 100644 --- a/auto-imports.d.ts +++ b/auto-imports.d.ts @@ -817,6 +817,7 @@ declare module 'vue' { readonly useThrottleFn: UnwrapRef readonly useThrottledRefHistory: UnwrapRef readonly useTimeAgo: UnwrapRef + readonly useTimeAgoIntl: UnwrapRef readonly useTimeout: UnwrapRef readonly useTimeoutFn: UnwrapRef readonly useTimeoutPoll: UnwrapRef diff --git a/src/api/index.js b/src/api/index.js index 0930975..2abe03b 100644 --- a/src/api/index.js +++ b/src/api/index.js @@ -194,8 +194,25 @@ export const certificationApi = { } }), + // 上传认证图片到七牛云(企业信息中的营业执照、办公场地、场景附件、授权代表身份证等) + uploadFile: (file) => { + const formData = new FormData() + formData.append('file', file) + return request.post('/certifications/upload', formData, { + headers: { + 'Content-Type': 'multipart/form-data' + } + }) + }, + // 管理员代用户完成认证(暂不关联合同) - adminCompleteWithoutContract: (data) => request.post('/certifications/admin/complete-without-contract', data) + adminCompleteWithoutContract: (data) => request.post('/certifications/admin/complete-without-contract', data), + + // 管理端企业审核:列表、详情、通过、拒绝 + adminListSubmitRecords: (params) => request.get('/certifications/admin/submit-records', { params }), + adminGetSubmitRecord: (id) => request.get(`/certifications/admin/submit-records/${id}`), + adminApproveSubmitRecord: (id, data) => request.post(`/certifications/admin/submit-records/${id}/approve`, data || {}), + adminRejectSubmitRecord: (id, data) => request.post(`/certifications/admin/submit-records/${id}/reject`, data) } // API相关接口 diff --git a/src/constants/menu.js b/src/constants/menu.js index 1c8ba00..0401563 100644 --- a/src/constants/menu.js +++ b/src/constants/menu.js @@ -116,6 +116,7 @@ export const getUserAccessibleMenuItems = (userType = 'user') => { icon: Setting, children: [ { name: '系统统计', path: '/admin/statistics', icon: ChartBar }, + { name: '企业审核', path: '/admin/certification-reviews', icon: ShieldCheck }, { name: '产品管理', path: '/admin/products', icon: Cube }, { name: '用户管理', path: '/admin/users', icon: Users }, { name: '分类管理', path: '/admin/categories', icon: Tag }, diff --git a/src/pages/admin/certification-reviews/index.vue b/src/pages/admin/certification-reviews/index.vue new file mode 100644 index 0000000..4d949ed --- /dev/null +++ b/src/pages/admin/certification-reviews/index.vue @@ -0,0 +1,441 @@ + + + + + diff --git a/src/pages/certification/components/EnterpriseInfo.vue b/src/pages/certification/components/EnterpriseInfo.vue index 3f7dcff..7b876ce 100644 --- a/src/pages/certification/components/EnterpriseInfo.vue +++ b/src/pages/certification/components/EnterpriseInfo.vue @@ -22,7 +22,7 @@ label-width="10em" class="enterprise-form-content" > -
+

基本信息

@@ -42,21 +42,7 @@
- OCR识别 - - - - 上传营业执照 - - + 可进行OCR识别,请在下方上传营业执照
@@ -84,6 +70,7 @@ + @@ -96,21 +83,9 @@ /> - - - - - - - - + + + + + + + + +
上传清晰可辨的营业执照图片
+
+
+
+
+ + + + +
+ 请在非 IE 浏览器下上传大小不超过 3M 的图片,最多 10 张,需体现门楣 LOGO、办公设备与工作人员。 +
+
+ +
+ +
上传办公场地环境照片
+
+
+
+
+
+
+ + + + + + + + +

授权代表信息

+

授权代表信息用于证明该人员已获得企业授权,请确保姓名、身份证号、手机号及身份证正反面照片真实有效。

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
上传授权代表身份证人像面
+
+
+
+ + + + +
上传授权代表身份证国徽面
+
+
+
+
+ +

应用场景填写

+

请描述您调用接口的具体业务场景

+ + + + + + + + + + + + + + + + + +
+ 请在非IE浏览器下上传大小不超过3M的图片,要求:不超过10张后台应用截图 +
+
+ +
+ +
上传业务场景相关截图或证明材料
+
+
+
+
+
+
@@ -190,7 +378,7 @@ import { CheckIcon, DocumentIcon } from '@heroicons/vue/24/outline' -import { ElMessage, ElMessageBox } from 'element-plus' +import { ElMessage, ElMessageBox, genFileId } from 'element-plus' import { useAliyunCaptcha } from '@/composables/useAliyunCaptcha' const props = defineProps({ @@ -203,7 +391,17 @@ const props = defineProps({ legalPersonID: '', legalPersonPhone: '', enterpriseAddress: '', - legalPersonCode: '' + legalPersonCode: '', + // 扩展:营业执照 & 办公场地 & 场景 + businessLicenseImageURL: '', + officePlaceImageURLs: [], + apiUsage: '', + scenarioAttachmentURLs: [], + // 授权代表信息 + authorizedRepName: '', + authorizedRepID: '', + authorizedRepPhone: '', + authorizedRepIDImageURLs: [] }) } }) @@ -224,7 +422,17 @@ const form = ref({ legalPersonID: '', legalPersonPhone: '', enterpriseAddress: '', - legalPersonCode: '' + legalPersonCode: '', + // 扩展:营业执照 & 办公场地 & 场景 + businessLicenseImageURL: '', + officePlaceImageURLs: [], + apiUsage: '', + scenarioAttachmentURLs: [], + // 授权代表信息 + authorizedRepName: '', + authorizedRepID: '', + authorizedRepPhone: '', + authorizedRepIDImageURLs: [] }) // 验证码相关状态 @@ -239,6 +447,17 @@ const submitting = ref(false) const ocrLoading = ref(false) const ocrResult = ref(false) const uploadRef = ref() +const officePlaceUploadRef = ref() +const scenarioUploadRef = ref() +const officePlaceDragover = ref(false) +const scenarioDragover = ref(false) + +// 上传文件列表(前端展示用) +const officePlaceFileList = ref([]) +const scenarioFileList = ref([]) +const businessLicenseFileList = ref([]) +const authorizedRepIDFrontFileList = ref([]) +const authorizedRepIDBackFileList = ref([]) // 计算属性 const canSendCode = computed(() => { @@ -326,6 +545,30 @@ const enterpriseRules = { legalPersonCode: [ { required: true, message: '请输入验证码', trigger: 'blur' }, { len: 6, message: '验证码应为6位数字', trigger: 'blur' } + ], + // 扩展字段简单校验(可按需加强) + businessLicenseImageURL: [ + { required: true, message: '请上传营业执照图片', trigger: 'change' } + ], + apiUsage: [ + { required: true, message: '请填写接口用途', trigger: 'blur' }, + { min: 5, max: 500, message: '接口用途长度应在5-500个字符之间', trigger: 'blur' } + ], + // 授权代表信息简单校验(可按需加强) + authorizedRepName: [ + { required: true, message: '请输入授权代表姓名', trigger: 'blur' }, + { min: 2, max: 20, message: '授权代表姓名长度应在2-20个字符之间', trigger: 'blur' } + ], + authorizedRepID: [ + { required: true, message: '请输入授权代表身份证号', trigger: 'blur' }, + { validator: validateIDCard, trigger: 'blur' } + ], + authorizedRepPhone: [ + { required: true, message: '请输入授权代表手机号', trigger: 'blur' }, + { validator: validatePhone, trigger: 'blur' } + ], + authorizedRepIDImageURLs: [ + { required: true, message: '请上传授权代表身份证正反面图片', trigger: 'change' } ] } @@ -395,7 +638,7 @@ const beforeUpload = (file) => { return true } -// 处理文件变化 +// 处理文件变化:触发 OCR,并保存营业执照原图 URL(若有上传地址) const handleFileChange = async (file) => { if (!beforeUpload(file.raw)) { return @@ -419,6 +662,11 @@ const handleFileChange = async (file) => { form.value.legalPersonID = ocrData.legal_person_id || '' form.value.enterpriseAddress = ocrData.address || '' + // 如果后端返回了已保存的营业执照图片URL,可以直接写入 + if (ocrData.license_image_url) { + form.value.businessLicenseImageURL = ocrData.license_image_url + } + ocrResult.value = true ElMessage.success('营业执照识别成功,已自动填充表单') } else { @@ -432,8 +680,211 @@ const handleFileChange = async (file) => { } } +// 判断是否为前端 blob 预览地址(需上传到服务器后替换为真实 URL) +const isBlobUrl = (url) => typeof url === 'string' && url.startsWith('blob:') + +// 上传单张图片到七牛云,返回可访问 URL +const uploadFileToServer = async (file) => { + const res = await certificationApi.uploadFile(file) + if (!res?.success || !res?.data?.url) { + throw new Error(res?.error?.message || '图片上传失败') + } + return res.data.url +} + +// 提交前将 blob 图片全部上传到七牛云,并更新表单中的 URL +const uploadAllBlobFilesAndFillForm = async () => { + const tasks = [] + + // 营业执照:若当前是 blob 则上传 + if (isBlobUrl(form.value.businessLicenseImageURL) && businessLicenseFileList.value.length > 0 && businessLicenseFileList.value[0].raw) { + tasks.push( + uploadFileToServer(businessLicenseFileList.value[0].raw).then((url) => { + form.value.businessLicenseImageURL = url + }) + ) + } + + // 办公场地多图:按顺序上传 + if (officePlaceFileList.value.length > 0) { + const files = officePlaceFileList.value.filter((f) => f.raw).map((f) => f.raw) + if (files.length > 0) { + tasks.push( + Promise.all(files.map((f) => uploadFileToServer(f))).then((urls) => { + form.value.officePlaceImageURLs = urls + }) + ) + } + } + + // 应用场景附件多图:按顺序上传 + if (scenarioFileList.value.length > 0) { + const files = scenarioFileList.value.filter((f) => f.raw).map((f) => f.raw) + if (files.length > 0) { + tasks.push( + Promise.all(files.map((f) => uploadFileToServer(f))).then((urls) => { + form.value.scenarioAttachmentURLs = urls + }) + ) + } + } + + // 授权代表身份证正反面:人像面 + 国徽面 + const frontRaw = authorizedRepIDFrontFileList.value[0]?.raw + const backRaw = authorizedRepIDBackFileList.value[0]?.raw + if (frontRaw || backRaw) { + tasks.push( + (async () => { + const urls = [] + if (frontRaw) urls.push(await uploadFileToServer(frontRaw)) + if (backRaw) urls.push(await uploadFileToServer(backRaw)) + form.value.authorizedRepIDImageURLs = urls + })() + ) + } + + await Promise.all(tasks) +} + +// 从 el-upload 的 fileList 中提取 URL 数组(后端接好上传接口后可用 response.url) +const extractUrls = (fileList) => { + return fileList + .map(f => f.url || f.response?.url || f.name) + .filter(Boolean) +} + +// 营业执照图片变更(同时触发 OCR 识别) +const handleBusinessLicenseChange = async (file, fileList) => { + businessLicenseFileList.value = fileList + const urls = extractUrls(fileList) + form.value.businessLicenseImageURL = urls[0] || '' + + // 使用当前选择的营业执照图片触发 OCR 识别 + if (file && file.raw) { + await handleFileChange(file) + } +} + +const handleBusinessLicenseRemove = (file, fileList) => { + businessLicenseFileList.value = fileList + const urls = extractUrls(fileList) + form.value.businessLicenseImageURL = urls[0] || '' +} + +// 手动清除营业执照图片(预览区域中的“删除”按钮) +const clearBusinessLicense = () => { + businessLicenseFileList.value = [] + form.value.businessLicenseImageURL = '' +} + +// 授权代表身份证人像面图片变更 +const handleAuthorizedRepIDFrontChange = (file, fileList) => { + authorizedRepIDFrontFileList.value = fileList + updateAuthorizedRepIDImageURLs() +} + +const handleAuthorizedRepIDFrontRemove = (file, fileList) => { + authorizedRepIDFrontFileList.value = fileList + updateAuthorizedRepIDImageURLs() +} + +// 授权代表身份证国徽面图片变更 +const handleAuthorizedRepIDBackChange = (file, fileList) => { + authorizedRepIDBackFileList.value = fileList + updateAuthorizedRepIDImageURLs() +} + +const handleAuthorizedRepIDBackRemove = (file, fileList) => { + authorizedRepIDBackFileList.value = fileList + updateAuthorizedRepIDImageURLs() +} + +// 手动清除授权代表身份证人像面 +const clearAuthorizedRepFront = () => { + authorizedRepIDFrontFileList.value = [] + updateAuthorizedRepIDImageURLs() +} + +// 手动清除授权代表身份证国徽面 +const clearAuthorizedRepBack = () => { + authorizedRepIDBackFileList.value = [] + updateAuthorizedRepIDImageURLs() +} + +// 汇总授权代表身份证正反面图片URL到一个数组字段 +const updateAuthorizedRepIDImageURLs = () => { + const frontUrl = extractUrls(authorizedRepIDFrontFileList.value)[0] || '' + const backUrl = extractUrls(authorizedRepIDBackFileList.value)[0] || '' + const urls = [] + if (frontUrl) urls.push(frontUrl) + if (backUrl) urls.push(backUrl) + form.value.authorizedRepIDImageURLs = urls +} + +// 办公场地:拖放文件时通过 handleStart 加入列表 +const onOfficePlaceDrop = (e) => { + officePlaceDragover.value = false + const upload = officePlaceUploadRef.value + if (!upload) return + const files = Array.from(e.dataTransfer?.files || []).filter( + (f) => f.type && /^image\/(jpeg|jpg|png|webp)$/i.test(f.type) + ) + const limit = 10 + const remain = Math.max(0, limit - officePlaceFileList.value.length) + const toAdd = files.slice(0, remain) + for (const file of toAdd) { + if (!beforeUpload(file)) continue + if (typeof file.uid === 'undefined') file.uid = genFileId() + upload.handleStart(file) + } + if (files.length > remain && remain > 0) ElMessage.warning(`最多上传 ${limit} 张,已忽略多余文件`) +} + +// 应用场景:拖放文件时通过 handleStart 加入列表 +const onScenarioDrop = (e) => { + scenarioDragover.value = false + const upload = scenarioUploadRef.value + if (!upload) return + const files = Array.from(e.dataTransfer?.files || []).filter( + (f) => f.type && /^image\/(jpeg|jpg|png|webp)$/i.test(f.type) + ) + const limit = 10 + const remain = Math.max(0, limit - scenarioFileList.value.length) + const toAdd = files.slice(0, remain) + for (const file of toAdd) { + if (!beforeUpload(file)) continue + if (typeof file.uid === 'undefined') file.uid = genFileId() + upload.handleStart(file) + } + if (files.length > remain && remain > 0) ElMessage.warning(`最多上传 ${limit} 张,已忽略多余文件`) +} + +// 办公场地图片变更 +const handleOfficePlaceChange = (file, fileList) => { + officePlaceFileList.value = fileList + form.value.officePlaceImageURLs = extractUrls(fileList) +} + +const handleOfficePlaceRemove = (file, fileList) => { + officePlaceFileList.value = fileList + form.value.officePlaceImageURLs = extractUrls(fileList) +} + +// 应用场景附件图片变更 +const handleScenarioChange = (file, fileList) => { + scenarioFileList.value = fileList + form.value.scenarioAttachmentURLs = extractUrls(fileList) +} + +const handleScenarioRemove = (file, fileList) => { + scenarioFileList.value = fileList + form.value.scenarioAttachmentURLs = extractUrls(fileList) +} + // 提交表单 const submitForm = async () => { + if (submitting.value) return + submitting.value = true try { await enterpriseFormRef.value.validate() @@ -450,12 +901,42 @@ const submitForm = async () => { } ) - submitting.value = true + // 先将所有 blob 图片上传到七牛云,再提交企业信息 + try { + await uploadAllBlobFilesAndFillForm() + } catch (err) { + ElMessage.error(err?.message || '图片上传失败,请重试') + submitting.value = false + return + } - // Mock API 调用 - await new Promise(resolve => setTimeout(resolve, 2000)) + // 调用后端提交接口 + const payload = { + company_name: form.value.companyName, + unified_social_code: form.value.unifiedSocialCode, + legal_person_name: form.value.legalPersonName, + legal_person_id: form.value.legalPersonID, + legal_person_phone: form.value.legalPersonPhone, + enterprise_address: form.value.enterpriseAddress, + verification_code: form.value.legalPersonCode, + // 扩展字段 + business_license_image_url: form.value.businessLicenseImageURL, + office_place_image_urls: form.value.officePlaceImageURLs, + api_usage: form.value.apiUsage, + scenario_attachment_urls: form.value.scenarioAttachmentURLs, + // 授权代表信息 + authorized_rep_name: form.value.authorizedRepName, + authorized_rep_id: form.value.authorizedRepID, + authorized_rep_phone: form.value.authorizedRepPhone, + authorized_rep_id_image_urls: form.value.authorizedRepIDImageURLs + } - emit('submit', form.value) + const res = await certificationApi.submitEnterpriseInfo(payload) + if (!res.success) { + throw new Error(res?.error?.message || '提交企业信息失败') + } + + emit('submit', { formData: form.value, response: res }) } catch (error) { // 用户点击取消或关闭对话框,不处理 @@ -609,6 +1090,13 @@ onUnmounted(() => { border-radius: 2px; } +.section-desc { + font-size: 14px; + color: #64748b; + margin: 0 0 20px 0; + line-height: 1.6; +} + /* 表单输入框 */ .form-input :deep(.el-input__wrapper) { border-radius: 8px; @@ -633,6 +1121,45 @@ onUnmounted(() => { padding: 8px 12px; } +/* 拖放区域:包裹 picture-card 上传,支持拖拽图片到整块区域 */ +.upload-drop-zone { + width: 100%; + border-radius: 8px; + transition: background 0.2s, border-color 0.2s; +} +.upload-drop-zone.is-dragover { + background: var(--el-color-primary-light-9, #ecf5ff); + outline: 2px dashed var(--el-color-primary); + outline-offset: -2px; +} + +/* 上传区域基础样式 */ +.upload-area { + width: 100%; +} + +/* 保证 picture-card 触发区域整块可点击、可拖拽 */ +.upload-trigger-inner { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; + min-height: 148px; + cursor: pointer; + gap: 8px; +} + +.upload-icon { + font-size: 28px; +} + +/* 当已有一张图片时,隐藏单图上传的“+ 选择文件”入口 */ +.single-upload-area :deep(.el-upload-list__item + .el-upload--picture-card) { + display: none; +} + /* 验证码按钮 */ .code-btn { min-width: 100px; diff --git a/src/pages/certification/components/ManualReviewPending.vue b/src/pages/certification/components/ManualReviewPending.vue new file mode 100644 index 0000000..24b3e77 --- /dev/null +++ b/src/pages/certification/components/ManualReviewPending.vue @@ -0,0 +1,100 @@ + + + + + diff --git a/src/pages/certification/index.vue b/src/pages/certification/index.vue index 64f2c7f..eca7688 100644 --- a/src/pages/certification/index.vue +++ b/src/pages/certification/index.vue @@ -68,6 +68,14 @@ @submit="handleEnterpriseSubmit" /> + + { // 步骤特定元数据 const stepMeta = ref({}) // 用于存储当前步骤的metadata +// 人工审核步骤的提交时间展示 +const manualReviewSubmitTime = computed(() => { + const at = certificationData.value?.metadata?.enterprise_info?.submit_at ?? certificationData.value?.info_submitted_at + if (!at) return '' + try { + const d = new Date(at) + return Number.isNaN(d.getTime()) ? '' : d.toLocaleString('zh-CN') + } catch { + return '' + } +}) + // 表单数据 const enterpriseForm = ref({ companyName: '', @@ -188,35 +216,32 @@ const enterpriseForm = ref({ enterpriseEmail: '', }) -// 开发模式控制 +// const isDevelopment = ref(false) const devCurrentStep = ref('enterprise_info') // 合同签署加载状态 const contractSignLoading = ref(false) -// 事件处理 -const handleEnterpriseSubmit = async (formData) => { +// 事件处理:优先用提交接口返回的认证数据更新步骤,确保进入「人工审核」页,避免依赖二次请求 +const handleEnterpriseSubmit = async (payload) => { try { loading.value = true - // 字段映射 - const payload = { - company_name: formData.companyName, - unified_social_code: formData.unifiedSocialCode, - legal_person_name: formData.legalPersonName, - legal_person_id: formData.legalPersonID, - legal_person_phone: formData.legalPersonPhone, - enterprise_address: formData.enterpriseAddress, - enterprise_email: formData.enterpriseEmail, - verification_code: formData.legalPersonCode, + const nextAction = payload?.response?.data?.metadata?.next_action + if (nextAction) { + ElMessage.success(nextAction) + } else { + ElMessage.success('企业信息提交成功,请等待管理员审核') + } + if (payload?.response?.data?.status) { + certificationData.value = payload.response.data + stepMeta.value = payload.response.data?.metadata || {} + await setCurrentStepByStatus() + } else { + await getCertificationDetails() } - await certificationApi.submitEnterpriseInfo(payload) - ElMessage.success('企业信息提交成功') - // 提交成功后刷新认证详情 - await getCertificationDetails() } catch (error) { - ElMessage.error(error?.message || '提交失败,请检查表单信息') - // 提交失败时不刷新认证详情,保持用户填写的信息 + ElMessage.error(error?.message || '获取认证状态失败,请刷新页面') } finally { loading.value = false } @@ -355,6 +380,9 @@ const setCurrentStepByStatus = async () => { case 'pending': currentStep.value = 'enterprise_info' break + case 'info_pending_review': + currentStep.value = 'manual_review' + break case 'info_submitted': currentStep.value = 'enterprise_verify' break diff --git a/src/router/index.js b/src/router/index.js index ff20bee..a520d62 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -304,6 +304,12 @@ const routes = [ name: 'AdminPurchaseRecords', component: () => import('@/pages/admin/purchase-records/index.vue'), meta: { title: '购买记录管理' } + }, + { + path: 'certification-reviews', + name: 'AdminCertificationReviews', + component: () => import('@/pages/admin/certification-reviews/index.vue'), + meta: { title: '企业审核' } } ] }, From 00374f285b4fb7396fa171f9d35a93b048e348a2 Mon Sep 17 00:00:00 2001 From: Mrx <18278715334@163.com> Date: Tue, 17 Mar 2026 18:30:23 +0800 Subject: [PATCH 02/11] f --- .../components/EnterpriseInfo.vue | 64 +++++++++++++++---- 1 file changed, 50 insertions(+), 14 deletions(-) diff --git a/src/pages/certification/components/EnterpriseInfo.vue b/src/pages/certification/components/EnterpriseInfo.vue index 7b876ce..c6de9c4 100644 --- a/src/pages/certification/components/EnterpriseInfo.vue +++ b/src/pages/certification/components/EnterpriseInfo.vue @@ -123,7 +123,7 @@
- 请在非 IE 浏览器下上传大小不超过 3M 的图片,最多 10 张,需体现门楣 LOGO、办公设备与工作人员。 + 请在非 IE 浏览器下上传大小不超过 1M 的图片,最多 10 张,需体现门楣 LOGO、办公设备与工作人员。
- 请在非IE浏览器下上传大小不超过3M的图片,要求:不超过10张后台应用截图 + 请在非 IE 浏览器下上传大小不超过 1M 的图片,最多 10 张后台应用截图。
{ const beforeUpload = (file) => { const allowedTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/webp'] const isValidType = allowedTypes.includes(file.type) - const isValidSize = file.size / 1024 / 1024 < 5 + const maxSizeMB = 1 + const isValidSize = file.size / 1024 / 1024 < maxSizeMB if (!isValidType) { ElMessage.error('只支持 JPG、PNG、WEBP 格式的图片') return false } if (!isValidSize) { - ElMessage.error('图片大小不能超过 5MB') + ElMessage.error(`图片大小不能超过 ${maxSizeMB}MB`) return false } return true @@ -692,6 +693,25 @@ const uploadFileToServer = async (file) => { return res.data.url } +// 选择后立即上传:对 el-upload 的 file 对象上传并更新 url/status,不更新表单(由调用方更新) +const uploadFileOnceSelected = async (file) => { + if (!file?.raw) return null + if (file.url && !isBlobUrl(file.url)) return file.url // 已是服务器 URL,不重复上传 + file.status = 'uploading' + try { + const url = await uploadFileToServer(file.raw) + file.url = url + file.status = 'success' + if (file.response === undefined) file.response = {} + file.response.url = url + return url + } catch (err) { + file.status = 'fail' + ElMessage.error(err?.message || '图片上传失败') + return null + } +} + // 提交前将 blob 图片全部上传到七牛云,并更新表单中的 URL const uploadAllBlobFilesAndFillForm = async () => { const tasks = [] @@ -753,15 +773,19 @@ const extractUrls = (fileList) => { .filter(Boolean) } -// 营业执照图片变更(同时触发 OCR 识别) +// 营业执照图片变更:先 OCR 识别,再选择即上传 const handleBusinessLicenseChange = async (file, fileList) => { businessLicenseFileList.value = fileList const urls = extractUrls(fileList) form.value.businessLicenseImageURL = urls[0] || '' - // 使用当前选择的营业执照图片触发 OCR 识别 if (file && file.raw) { await handleFileChange(file) + // OCR 若未返回服务器 URL,则选择后立即上传 + if (isBlobUrl(form.value.businessLicenseImageURL)) { + const url = await uploadFileOnceSelected(file) + if (url) form.value.businessLicenseImageURL = url + } } } @@ -777,9 +801,12 @@ const clearBusinessLicense = () => { form.value.businessLicenseImageURL = '' } -// 授权代表身份证人像面图片变更 -const handleAuthorizedRepIDFrontChange = (file, fileList) => { +// 授权代表身份证人像面图片变更:选择即上传 +const handleAuthorizedRepIDFrontChange = async (file, fileList) => { authorizedRepIDFrontFileList.value = fileList + if (file?.raw && isBlobUrl(file.url)) { + await uploadFileOnceSelected(file) + } updateAuthorizedRepIDImageURLs() } @@ -788,9 +815,12 @@ const handleAuthorizedRepIDFrontRemove = (file, fileList) => { updateAuthorizedRepIDImageURLs() } -// 授权代表身份证国徽面图片变更 -const handleAuthorizedRepIDBackChange = (file, fileList) => { +// 授权代表身份证国徽面图片变更:选择即上传 +const handleAuthorizedRepIDBackChange = async (file, fileList) => { authorizedRepIDBackFileList.value = fileList + if (file?.raw && isBlobUrl(file.url)) { + await uploadFileOnceSelected(file) + } updateAuthorizedRepIDImageURLs() } @@ -859,9 +889,12 @@ const onScenarioDrop = (e) => { if (files.length > remain && remain > 0) ElMessage.warning(`最多上传 ${limit} 张,已忽略多余文件`) } -// 办公场地图片变更 -const handleOfficePlaceChange = (file, fileList) => { +// 办公场地图片变更:选择即上传 +const handleOfficePlaceChange = async (file, fileList) => { officePlaceFileList.value = fileList + if (file?.raw && isBlobUrl(file.url)) { + await uploadFileOnceSelected(file) + } form.value.officePlaceImageURLs = extractUrls(fileList) } @@ -870,9 +903,12 @@ const handleOfficePlaceRemove = (file, fileList) => { form.value.officePlaceImageURLs = extractUrls(fileList) } -// 应用场景附件图片变更 -const handleScenarioChange = (file, fileList) => { +// 应用场景附件图片变更:选择即上传 +const handleScenarioChange = async (file, fileList) => { scenarioFileList.value = fileList + if (file?.raw && isBlobUrl(file.url)) { + await uploadFileOnceSelected(file) + } form.value.scenarioAttachmentURLs = extractUrls(fileList) } From ee4b6c4de7e07b0bd570d9dc58aecbff2f24851c Mon Sep 17 00:00:00 2001 From: Mrx <18278715334@163.com> Date: Wed, 18 Mar 2026 11:00:35 +0800 Subject: [PATCH 03/11] f --- .../components/EnterpriseInfo.vue | 240 +++++------------- 1 file changed, 65 insertions(+), 175 deletions(-) diff --git a/src/pages/certification/components/EnterpriseInfo.vue b/src/pages/certification/components/EnterpriseInfo.vue index c6de9c4..2b235b4 100644 --- a/src/pages/certification/components/EnterpriseInfo.vue +++ b/src/pages/certification/components/EnterpriseInfo.vue @@ -125,33 +125,25 @@
请在非 IE 浏览器下上传大小不超过 1M 的图片,最多 10 张,需体现门楣 LOGO、办公设备与工作人员。
-
- -
- -
上传办公场地环境照片
-
-
-
+
+ +
上传办公场地环境照片
+
+ @@ -319,33 +311,25 @@
请在非 IE 浏览器下上传大小不超过 1M 的图片,最多 10 张后台应用截图。
-
- -
- -
上传业务场景相关截图或证明材料
-
-
-
+
+ +
上传业务场景相关截图或证明材料
+
+ @@ -378,7 +362,7 @@ import { CheckIcon, DocumentIcon } from '@heroicons/vue/24/outline' -import { ElMessage, ElMessageBox, genFileId } from 'element-plus' +import { ElMessage, ElMessageBox } from 'element-plus' import { useAliyunCaptcha } from '@/composables/useAliyunCaptcha' const props = defineProps({ @@ -449,8 +433,6 @@ const ocrResult = ref(false) const uploadRef = ref() const officePlaceUploadRef = ref() const scenarioUploadRef = ref() -const officePlaceDragover = ref(false) -const scenarioDragover = ref(false) // 上传文件列表(前端展示用) const officePlaceFileList = ref([]) @@ -681,9 +663,6 @@ const handleFileChange = async (file) => { } } -// 判断是否为前端 blob 预览地址(需上传到服务器后替换为真实 URL) -const isBlobUrl = (url) => typeof url === 'string' && url.startsWith('blob:') - // 上传单张图片到七牛云,返回可访问 URL const uploadFileToServer = async (file) => { const res = await certificationApi.uploadFile(file) @@ -693,17 +672,17 @@ const uploadFileToServer = async (file) => { return res.data.url } -// 选择后立即上传:对 el-upload 的 file 对象上传并更新 url/status,不更新表单(由调用方更新) +// 选择后立即上传:服务器 URL 存到 response.url,保留 file.url 为 blob 以便预览(避免服务器证书等问题导致预览失败) const uploadFileOnceSelected = async (file) => { if (!file?.raw) return null - if (file.url && !isBlobUrl(file.url)) return file.url // 已是服务器 URL,不重复上传 + if (file.response?.url) return file.response.url // 已上传过,不重复上传 file.status = 'uploading' try { const url = await uploadFileToServer(file.raw) - file.url = url file.status = 'success' if (file.response === undefined) file.response = {} file.response.url = url + // 不覆盖 file.url,保留 blob 预览地址,避免服务器证书无效时预览失败 return url } catch (err) { file.status = 'fail' @@ -712,64 +691,27 @@ const uploadFileOnceSelected = async (file) => { } } -// 提交前将 blob 图片全部上传到七牛云,并更新表单中的 URL -const uploadAllBlobFilesAndFillForm = async () => { - const tasks = [] +// 提交前仅从 fileList 同步 URL 到表单,并检查是否全部已上传(选择即上传,提交时不再批量上传) +const syncFormUrlsAndCheckReady = () => { + form.value.businessLicenseImageURL = extractUrls(businessLicenseFileList.value)[0] || '' + form.value.officePlaceImageURLs = extractUrls(officePlaceFileList.value) + form.value.scenarioAttachmentURLs = extractUrls(scenarioFileList.value) + updateAuthorizedRepIDImageURLs() - // 营业执照:若当前是 blob 则上传 - if (isBlobUrl(form.value.businessLicenseImageURL) && businessLicenseFileList.value.length > 0 && businessLicenseFileList.value[0].raw) { - tasks.push( - uploadFileToServer(businessLicenseFileList.value[0].raw).then((url) => { - form.value.businessLicenseImageURL = url - }) - ) - } - - // 办公场地多图:按顺序上传 - if (officePlaceFileList.value.length > 0) { - const files = officePlaceFileList.value.filter((f) => f.raw).map((f) => f.raw) - if (files.length > 0) { - tasks.push( - Promise.all(files.map((f) => uploadFileToServer(f))).then((urls) => { - form.value.officePlaceImageURLs = urls - }) - ) - } - } - - // 应用场景附件多图:按顺序上传 - if (scenarioFileList.value.length > 0) { - const files = scenarioFileList.value.filter((f) => f.raw).map((f) => f.raw) - if (files.length > 0) { - tasks.push( - Promise.all(files.map((f) => uploadFileToServer(f))).then((urls) => { - form.value.scenarioAttachmentURLs = urls - }) - ) - } - } - - // 授权代表身份证正反面:人像面 + 国徽面 - const frontRaw = authorizedRepIDFrontFileList.value[0]?.raw - const backRaw = authorizedRepIDBackFileList.value[0]?.raw - if (frontRaw || backRaw) { - tasks.push( - (async () => { - const urls = [] - if (frontRaw) urls.push(await uploadFileToServer(frontRaw)) - if (backRaw) urls.push(await uploadFileToServer(backRaw)) - form.value.authorizedRepIDImageURLs = urls - })() - ) - } - - await Promise.all(tasks) + const hasUploading = (list) => list.some((f) => f.status === 'uploading') + const hasUnfinished = (list) => list.some((f) => f.raw && !f.response?.url) + if (hasUploading(businessLicenseFileList.value) || hasUnfinished(businessLicenseFileList.value)) return false + if (hasUploading(officePlaceFileList.value) || hasUnfinished(officePlaceFileList.value)) return false + if (hasUploading(scenarioFileList.value) || hasUnfinished(scenarioFileList.value)) return false + if (hasUploading(authorizedRepIDFrontFileList.value) || hasUnfinished(authorizedRepIDFrontFileList.value)) return false + if (hasUploading(authorizedRepIDBackFileList.value) || hasUnfinished(authorizedRepIDBackFileList.value)) return false + return true } -// 从 el-upload 的 fileList 中提取 URL 数组(后端接好上传接口后可用 response.url) +// 从 el-upload 的 fileList 中提取 URL 数组,优先用服务器 URL(response.url),提交用 const extractUrls = (fileList) => { return fileList - .map(f => f.url || f.response?.url || f.name) + .map(f => f.response?.url || f.url || f.name) .filter(Boolean) } @@ -781,8 +723,8 @@ const handleBusinessLicenseChange = async (file, fileList) => { if (file && file.raw) { await handleFileChange(file) - // OCR 若未返回服务器 URL,则选择后立即上传 - if (isBlobUrl(form.value.businessLicenseImageURL)) { + // OCR 若未返回服务器 URL,则选择后立即上传(未上传过才上传) + if (!file.response?.url) { const url = await uploadFileOnceSelected(file) if (url) form.value.businessLicenseImageURL = url } @@ -804,7 +746,7 @@ const clearBusinessLicense = () => { // 授权代表身份证人像面图片变更:选择即上传 const handleAuthorizedRepIDFrontChange = async (file, fileList) => { authorizedRepIDFrontFileList.value = fileList - if (file?.raw && isBlobUrl(file.url)) { + if (file?.raw && !file.response?.url) { await uploadFileOnceSelected(file) } updateAuthorizedRepIDImageURLs() @@ -818,7 +760,7 @@ const handleAuthorizedRepIDFrontRemove = (file, fileList) => { // 授权代表身份证国徽面图片变更:选择即上传 const handleAuthorizedRepIDBackChange = async (file, fileList) => { authorizedRepIDBackFileList.value = fileList - if (file?.raw && isBlobUrl(file.url)) { + if (file?.raw && !file.response?.url) { await uploadFileOnceSelected(file) } updateAuthorizedRepIDImageURLs() @@ -851,48 +793,10 @@ const updateAuthorizedRepIDImageURLs = () => { form.value.authorizedRepIDImageURLs = urls } -// 办公场地:拖放文件时通过 handleStart 加入列表 -const onOfficePlaceDrop = (e) => { - officePlaceDragover.value = false - const upload = officePlaceUploadRef.value - if (!upload) return - const files = Array.from(e.dataTransfer?.files || []).filter( - (f) => f.type && /^image\/(jpeg|jpg|png|webp)$/i.test(f.type) - ) - const limit = 10 - const remain = Math.max(0, limit - officePlaceFileList.value.length) - const toAdd = files.slice(0, remain) - for (const file of toAdd) { - if (!beforeUpload(file)) continue - if (typeof file.uid === 'undefined') file.uid = genFileId() - upload.handleStart(file) - } - if (files.length > remain && remain > 0) ElMessage.warning(`最多上传 ${limit} 张,已忽略多余文件`) -} - -// 应用场景:拖放文件时通过 handleStart 加入列表 -const onScenarioDrop = (e) => { - scenarioDragover.value = false - const upload = scenarioUploadRef.value - if (!upload) return - const files = Array.from(e.dataTransfer?.files || []).filter( - (f) => f.type && /^image\/(jpeg|jpg|png|webp)$/i.test(f.type) - ) - const limit = 10 - const remain = Math.max(0, limit - scenarioFileList.value.length) - const toAdd = files.slice(0, remain) - for (const file of toAdd) { - if (!beforeUpload(file)) continue - if (typeof file.uid === 'undefined') file.uid = genFileId() - upload.handleStart(file) - } - if (files.length > remain && remain > 0) ElMessage.warning(`最多上传 ${limit} 张,已忽略多余文件`) -} - // 办公场地图片变更:选择即上传 const handleOfficePlaceChange = async (file, fileList) => { officePlaceFileList.value = fileList - if (file?.raw && isBlobUrl(file.url)) { + if (file?.raw && !file.response?.url) { await uploadFileOnceSelected(file) } form.value.officePlaceImageURLs = extractUrls(fileList) @@ -906,7 +810,7 @@ const handleOfficePlaceRemove = (file, fileList) => { // 应用场景附件图片变更:选择即上传 const handleScenarioChange = async (file, fileList) => { scenarioFileList.value = fileList - if (file?.raw && isBlobUrl(file.url)) { + if (file?.raw && !file.response?.url) { await uploadFileOnceSelected(file) } form.value.scenarioAttachmentURLs = extractUrls(fileList) @@ -937,11 +841,9 @@ const submitForm = async () => { } ) - // 先将所有 blob 图片上传到七牛云,再提交企业信息 - try { - await uploadAllBlobFilesAndFillForm() - } catch (err) { - ElMessage.error(err?.message || '图片上传失败,请重试') + // 选择即上传:提交时不再上传,仅同步 URL 并校验是否均已上传完成 + if (!syncFormUrlsAndCheckReady()) { + ElMessage.warning('请等待所有图片上传完成后再提交') submitting.value = false return } @@ -1157,18 +1059,6 @@ onUnmounted(() => { padding: 8px 12px; } -/* 拖放区域:包裹 picture-card 上传,支持拖拽图片到整块区域 */ -.upload-drop-zone { - width: 100%; - border-radius: 8px; - transition: background 0.2s, border-color 0.2s; -} -.upload-drop-zone.is-dragover { - background: var(--el-color-primary-light-9, #ecf5ff); - outline: 2px dashed var(--el-color-primary); - outline-offset: -2px; -} - /* 上传区域基础样式 */ .upload-area { width: 100%; From c90259e3f400b40829c9ad9d26320c05d4901d9f Mon Sep 17 00:00:00 2001 From: Mrx <18278715334@163.com> Date: Wed, 18 Mar 2026 12:04:36 +0800 Subject: [PATCH 04/11] f --- src/pages/admin/certification-reviews/index.vue | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/pages/admin/certification-reviews/index.vue b/src/pages/admin/certification-reviews/index.vue index 4d949ed..744743e 100644 --- a/src/pages/admin/certification-reviews/index.vue +++ b/src/pages/admin/certification-reviews/index.vue @@ -105,6 +105,9 @@ {{ detail.manual_review_remark }} + + {{ detail.failure_reason }} +
From 681845607002f407812817af67b5652733b0d3fb Mon Sep 17 00:00:00 2001 From: Mrx <18278715334@163.com> Date: Wed, 18 Mar 2026 16:04:49 +0800 Subject: [PATCH 05/11] f --- .../certification/components/EnterpriseInfo.vue | 14 ++++++++++++-- .../components/ManualReviewPending.vue | 15 +++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/pages/certification/components/EnterpriseInfo.vue b/src/pages/certification/components/EnterpriseInfo.vue index 2b235b4..dc7cf5f 100644 --- a/src/pages/certification/components/EnterpriseInfo.vue +++ b/src/pages/certification/components/EnterpriseInfo.vue @@ -747,7 +747,12 @@ const clearBusinessLicense = () => { const handleAuthorizedRepIDFrontChange = async (file, fileList) => { authorizedRepIDFrontFileList.value = fileList if (file?.raw && !file.response?.url) { - await uploadFileOnceSelected(file) + const url = await uploadFileOnceSelected(file) + if (url) { + authorizedRepIDFrontFileList.value = authorizedRepIDFrontFileList.value.map((f) => + f.uid === file.uid ? { ...f, status: 'success', response: { url }, url: f.url } : f + ) + } } updateAuthorizedRepIDImageURLs() } @@ -761,7 +766,12 @@ const handleAuthorizedRepIDFrontRemove = (file, fileList) => { const handleAuthorizedRepIDBackChange = async (file, fileList) => { authorizedRepIDBackFileList.value = fileList if (file?.raw && !file.response?.url) { - await uploadFileOnceSelected(file) + const url = await uploadFileOnceSelected(file) + if (url) { + authorizedRepIDBackFileList.value = authorizedRepIDBackFileList.value.map((f) => + f.uid === file.uid ? { ...f, status: 'success', response: { url }, url: f.url } : f + ) + } } updateAuthorizedRepIDImageURLs() } diff --git a/src/pages/certification/components/ManualReviewPending.vue b/src/pages/certification/components/ManualReviewPending.vue index 24b3e77..9ad4cd0 100644 --- a/src/pages/certification/components/ManualReviewPending.vue +++ b/src/pages/certification/components/ManualReviewPending.vue @@ -49,6 +49,7 @@ defineProps({ const emit = defineEmits(['refresh']) const refreshing = ref(false) +const pollTimer = ref(null) const handleRefresh = async () => { refreshing.value = true @@ -58,6 +59,20 @@ const handleRefresh = async () => { refreshing.value = false } } + +onMounted(() => { + // 人工审核阶段:自动轮询状态,审核通过后会在父组件中自动切换步骤 + pollTimer.value = window.setInterval(() => { + emit('refresh') + }, 5000) +}) + +onUnmounted(() => { + if (pollTimer.value) { + window.clearInterval(pollTimer.value) + pollTimer.value = null + } +}) + From 40a4c7fa0802971114f88e87036f75a48039699f Mon Sep 17 00:00:00 2001 From: Mrx <18278715334@163.com> Date: Fri, 20 Mar 2026 16:23:07 +0800 Subject: [PATCH 11/11] f --- src/pages/api/ApiDebugger.vue | 83 +++++++++++++++++++++++------------ 1 file changed, 55 insertions(+), 28 deletions(-) diff --git a/src/pages/api/ApiDebugger.vue b/src/pages/api/ApiDebugger.vue index f30a589..8117f39 100644 --- a/src/pages/api/ApiDebugger.vue +++ b/src/pages/api/ApiDebugger.vue @@ -181,17 +181,17 @@ * - -
+ +
- 上传图片(JPG/BMP/PNG) + {{ getUploadButtonTextByField(field) }} @@ -423,7 +423,7 @@
-
@@ -749,12 +749,12 @@ watch( if (newProductId === oldProductId) { return } - + // 如果正在选择产品,不重复执行 if (isSelectingProduct.value) { return } - + if (newProductId && userProducts.value.length > 0) { await autoSelectProduct(newProductId) } else if (!newProductId && userProducts.value.length > 0 && !selectedProduct.value) { @@ -771,13 +771,13 @@ const autoSelectProduct = async (productId) => { console.log('正在选择产品,跳过重复请求') return } - + // 如果已经选择了相同的产品,不重复选择 if (lastSelectedProductId.value === productId && selectedProduct.value) { console.log('产品已选择,跳过重复选择:', productId) return } - + // 如果用户产品列表为空,等待加载完成 if (!userProducts.value.length) { console.log('等待用户产品列表加载完成...') @@ -883,13 +883,13 @@ const loadApiKeys = async () => { const selectProduct = async (product) => { // 防止重复选择相同产品 const productId = product.product_id || product.id - if (selectedProduct.value && + if (selectedProduct.value && (selectedProduct.value.id === productId || selectedProduct.value.product_id === productId) && !isSelectingProduct.value) { console.log('产品已选择,跳过重复加载:', productId) return } - + // 确保API密钥已经加载 if (!debugForm.accessId || !debugForm.secretKey) { ElMessage.warning('正在加载API密钥,请稍候...') @@ -934,24 +934,51 @@ const loadFormConfig = async (apiCode) => { } } -// 处理图片上传并转换为base64 -const handleImageUpload = (file, fieldName) => { +const getFieldValidationText = (field) => { + return typeof field?.validation === 'string' ? field.validation : '' +} + +const isBase64ImageOnlyField = (field) => { + return getFieldValidationText(field).includes('Base64图片') +} + +const isBase64UploadField = (field) => { + if (field?.type !== 'textarea') return false + const validationText = getFieldValidationText(field) + return validationText.includes('Base64图片') || validationText.includes('Base64编码') || validationText.toLowerCase().includes('base64') +} + +const getUploadAcceptByField = (field) => { + if (isBase64ImageOnlyField(field)) { + return 'image/jpeg,image/jpg,image/png,image/bmp' + } + return 'image/jpeg,image/jpg,image/png,image/bmp,application/pdf,.pdf' +} + +const getUploadButtonTextByField = (field) => { + return isBase64ImageOnlyField(field) ? '上传图片(JPG/BMP/PNG)' : '上传文件(JPG/BMP/PNG/PDF)' +} + +// 处理文件上传并转换为base64(支持按字段规则限制类型) +const handleImageUpload = (file, fieldName, field) => { const fileObj = file.raw || file - + // 验证文件类型 - const allowedTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/bmp'] + const allowedTypes = isBase64ImageOnlyField(field) + ? ['image/jpeg', 'image/jpg', 'image/png', 'image/bmp'] + : ['image/jpeg', 'image/jpg', 'image/png', 'image/bmp', 'application/pdf'] if (!allowedTypes.includes(fileObj.type)) { - ElMessage.error('只支持 JPG、BMP、PNG 格式的图片') + ElMessage.error(isBase64ImageOnlyField(field) ? '只支持 JPG、BMP、PNG 格式的图片' : '只支持 JPG、BMP、PNG、PDF 格式的文件') return false } - + // 验证文件大小(限制为5MB) const maxSize = 5 * 1024 * 1024 // 5MB if (fileObj.size > maxSize) { - ElMessage.error('图片大小不能超过 5MB') + ElMessage.error('文件大小不能超过 5MB') return false } - + // 读取文件并转换为base64 const reader = new FileReader() reader.onload = (e) => { @@ -959,13 +986,13 @@ const handleImageUpload = (file, fieldName) => { // 移除 data:image/xxx;base64, 前缀,只保留纯base64数据 const base64Data = base64String.includes(',') ? base64String.split(',')[1] : base64String formData.value[fieldName] = base64Data - ElMessage.success('图片上传成功,已转换为base64') + ElMessage.success('文件上传成功,已转换为base64') } reader.onerror = () => { - ElMessage.error('图片读取失败,请重试') + ElMessage.error('文件读取失败,请重试') } reader.readAsDataURL(fileObj) - + return false // 阻止自动上传 } @@ -1142,7 +1169,7 @@ const convertFieldTypes = (data) => { if (!formFields.value || formFields.value.length === 0) { return data } - + const processedData = { ...data } formFields.value.forEach(field => { const value = processedData[field.name] @@ -1150,7 +1177,7 @@ const convertFieldTypes = (data) => { if (value === '' || value === null || value === undefined) { return } - + // 根据字段类型进行转换 if (field.type === 'number') { // 将字符串转换为数字(整数) @@ -1160,7 +1187,7 @@ const convertFieldTypes = (data) => { } } }) - + return processedData } @@ -1197,7 +1224,7 @@ const encryptWithAES = async (data, secretKey) => { // 解析JSON字符串(如果是字符串) let parsedData = typeof data === 'string' ? JSON.parse(data) : data - + // 根据字段类型进行类型转换 parsedData = convertFieldTypes(parsedData) @@ -1282,7 +1309,7 @@ const handleDebug = async () => { debugResult.value = null decryptedData.value = null await nextTick() // 确保DOM更新 - + const startTime = new Date() try {