This commit is contained in:
Mrx
2026-03-18 11:00:35 +08:00
parent 00374f285b
commit ee4b6c4de7

View File

@@ -125,33 +125,25 @@
<div class="text-xs mb-1 text-blue-500">
请在非 IE 浏览器下上传大小不超过 1M 的图片最多 10 需体现门楣 LOGO办公设备与工作人员
</div>
<div
class="upload-drop-zone"
:class="{ 'is-dragover': officePlaceDragover }"
@dragover.prevent="officePlaceDragover = true"
@dragleave.prevent="officePlaceDragover = false"
@drop.prevent="onOfficePlaceDrop"
<el-upload
ref="officePlaceUploadRef"
class="upload-area"
action="#"
list-type="picture-card"
:auto-upload="false"
v-model:file-list="officePlaceFileList"
accept="image/jpeg,image/jpg,image/png,image/webp"
multiple
:limit="10"
:on-change="handleOfficePlaceChange"
:on-remove="handleOfficePlaceRemove"
:before-upload="beforeUpload"
>
<el-upload
ref="officePlaceUploadRef"
class="upload-area"
action="#"
list-type="picture-card"
:auto-upload="false"
v-model:file-list="officePlaceFileList"
accept="image/jpeg,image/jpg,image/png,image/webp"
multiple
:limit="10"
:on-change="handleOfficePlaceChange"
:on-remove="handleOfficePlaceRemove"
:before-upload="beforeUpload"
>
<div class="upload-trigger-inner">
<el-icon class="upload-icon"><ArrowUpTrayIcon /></el-icon>
<div class="el-upload__text">上传办公场地环境照片</div>
</div>
</el-upload>
</div>
<div class="upload-trigger-inner">
<el-icon class="upload-icon"><ArrowUpTrayIcon /></el-icon>
<div class="el-upload__text">上传办公场地环境照片</div>
</div>
</el-upload>
</el-form-item>
</el-col>
</el-row>
@@ -319,33 +311,25 @@
<div class="text-xs mb-1 text-blue-500">
请在非 IE 浏览器下上传大小不超过 1M 的图片最多 10 张后台应用截图
</div>
<div
class="upload-drop-zone"
:class="{ 'is-dragover': scenarioDragover }"
@dragover.prevent="scenarioDragover = true"
@dragleave.prevent="scenarioDragover = false"
@drop.prevent="onScenarioDrop"
<el-upload
ref="scenarioUploadRef"
class="upload-area"
action="#"
list-type="picture-card"
:auto-upload="false"
v-model:file-list="scenarioFileList"
accept="image/jpeg,image/jpg,image/png,image/webp"
multiple
:limit="10"
:on-change="handleScenarioChange"
:on-remove="handleScenarioRemove"
:before-upload="beforeUpload"
>
<el-upload
ref="scenarioUploadRef"
class="upload-area"
action="#"
list-type="picture-card"
:auto-upload="false"
v-model:file-list="scenarioFileList"
accept="image/jpeg,image/jpg,image/png,image/webp"
multiple
:limit="10"
:on-change="handleScenarioChange"
:on-remove="handleScenarioRemove"
:before-upload="beforeUpload"
>
<div class="upload-trigger-inner">
<el-icon class="upload-icon"><ArrowUpTrayIcon /></el-icon>
<div class="el-upload__text">上传业务场景相关截图或证明材料</div>
</div>
</el-upload>
</div>
<div class="upload-trigger-inner">
<el-icon class="upload-icon"><ArrowUpTrayIcon /></el-icon>
<div class="el-upload__text">上传业务场景相关截图或证明材料</div>
</div>
</el-upload>
</el-form-item>
</el-col>
</el-row>
@@ -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 数组,优先用服务器 URLresponse.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%;