style:模具-点检保养图片上传回显(3张)

main
黄伟杰 1 week ago
parent 09db2fd3ed
commit a02a7d77a1

@ -90,6 +90,45 @@ const fileList = ref<UploadUserFile[]>([])
const uploadNumber = ref<number>(0)
const uploadList = ref<UploadUserFile[]>([])
const nameMap = ref<Record<string, string>>({})
const uploading = ref(false)
interface UploadEmits {
(e: 'update:modelValue', value: string[]): void
(e: 'uploading-change', value: boolean): void
}
const emit = defineEmits<UploadEmits>()
const setUploading = (value: boolean) => {
if (uploading.value === value) return
uploading.value = value
emit('uploading-change', value)
}
const removeUploadingFile = (uploadFile?: UploadFile | UploadUserFile) => {
if (!uploadFile?.uid) return
const index = fileList.value.findIndex((item) => item.uid === uploadFile.uid)
if (index > -1) {
fileList.value.splice(index, 1)
}
}
const finishUploadQueue = () => {
if (uploadNumber.value > 0 && uploadList.value.length !== uploadNumber.value) return
if (uploadList.value.length) {
fileList.value.push(...uploadList.value)
uploadList.value = []
emitUpdateModelValue()
}
uploadNumber.value = 0
setUploading(false)
}
const finishOneUploadAsFailed = (uploadFile?: UploadFile | UploadUserFile) => {
removeUploadingFile(uploadFile)
uploadNumber.value = Math.max(uploadNumber.value - 1, 0)
finishUploadQueue()
}
/**
* @description 文件上传之前判断
* @param rawFile 上传的文件
@ -109,16 +148,15 @@ const beforeUpload: UploadProps['beforeUpload'] = (rawFile) => {
message: `上传图片大小不能超过 ${props.fileSize}M`,
type: 'warning'
})
uploadNumber.value++
return imgType.includes(rawFile.type as FileTypes) && imgSize
const valid = imgType.includes(rawFile.type as FileTypes) && imgSize
if (valid) {
uploadNumber.value++
setUploading(true)
}
return valid
}
//
interface UploadEmits {
(e: 'update:modelValue', value: string[]): void
}
const emit = defineEmits<UploadEmits>()
const unwrapUploadPayload = (res: any) => {
if (!res) return undefined
const payload = res.data ?? res
@ -150,6 +188,7 @@ const uploadSuccess: UploadProps['onSuccess'] = (res: any, uploadFile): void =>
const url = getUrlFromRes(res)
if (!url) {
message.error('上传返回数据异常')
finishOneUploadAsFailed(uploadFile)
return
}
const name = getNameFromRes(res, url)
@ -157,17 +196,9 @@ const uploadSuccess: UploadProps['onSuccess'] = (res: any, uploadFile): void =>
nameMap.value[url] = name
}
message.success('上传成功')
const index = fileList.value.findIndex((item) => item.uid === uploadFile.uid)
if (index > -1) {
fileList.value.splice(index, 1)
}
removeUploadingFile(uploadFile)
uploadList.value.push({ name, url })
if (uploadList.value.length === uploadNumber.value) {
fileList.value.push(...uploadList.value)
uploadList.value = []
uploadNumber.value = 0
emitUpdateModelValue()
}
finishUploadQueue()
}
const getDisplayNameByUrl = (url: string): string => {
@ -215,12 +246,13 @@ const handleRemove = (uploadFile: UploadFile) => {
}
//
const uploadError = () => {
const uploadError: UploadProps['onError'] = (_error, uploadFile) => {
ElNotification({
title: '温馨提示',
message: '图片上传失败,请您重新上传!',
type: 'error'
})
finishOneUploadAsFailed(uploadFile)
}
//

@ -2723,6 +2723,7 @@ export default {
inspectionMethod: 'Inspection Method',
criteria: 'Criteria',
inspectionTime: 'Inspection Time',
images: 'Images',
remark: 'Remark',
operator: 'Operator',
projectContent: 'Project Content',

@ -2204,6 +2204,7 @@ export default {
inspectionMethod: '点检方式',
criteria: '判定标准',
inspectionTime: '点检时间',
images: '图片',
remark: '备注',
operator: '操作人',
// 维修

@ -129,18 +129,25 @@
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column :label="t('MoldManagement.MoldBrandPage.image')" align="center" prop="images" width="110">
<el-table-column :label="t('MoldManagement.MoldBrandPage.image')" align="center" prop="images" min-width="260">
<template #default="scope">
<UploadImg
<div
v-if="ticketResultEditable"
v-model="scope.row.images"
:drag="false"
:show-btn-text="false"
width="56px"
height="56px"
/>
v-loading="isTicketResultImageUploading(scope.row)"
element-loading-text="上传中"
class="ticket-result-images"
:class="{ 'is-full': getTicketResultImages(scope.row).length >= 3 }"
>
<UploadImgs
v-model="scope.row.images"
:limit="3"
width="56px"
height="56px"
@uploading-change="setTicketResultImageUploading(scope.row, $event)"
/>
</div>
<el-image
v-else-if="scope.row.images"
v-else-if="parseImages(scope.row.images).length"
:src="parseFirstImage(scope.row.images)"
:preview-src-list="parseImages(scope.row.images)"
preview-teleported
@ -382,7 +389,7 @@
<div class="mold-maintain-page__form-actions">
<el-button @click="emit('back')">{{ t('MoldManagement.MoldBrandPage.cancel') }}</el-button>
<el-button type="primary" @click="submitForm" :loading="submitLoading">{{ t('MoldManagement.MoldBrandPage.submit') }}</el-button>
<el-button type="primary" @click="submitForm" :loading="submitLoading" :disabled="isInspectOrMaintain && hasTicketResultImageUploading">{{ t('MoldManagement.MoldBrandPage.submit') }}</el-button>
</div>
</div>
</div>
@ -573,6 +580,12 @@ const replaceNetFormData = reactive({
remark: undefined as string | undefined
})
const getCurrentDateTime = () => {
const date = new Date()
const pad = (value: number) => String(value).padStart(2, '0')
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`
}
//
const subMoldList = ref<any[]>([])
const subMoldLoading = ref(false)
@ -652,6 +665,32 @@ const subjectLoadingMap = ref<Record<string, boolean>>({})
const ticketResultList = ref<any[]>([])
const ticketResultEditable = ref(false)
const ticketResultBackup = ref<any[] | null>(null)
const ticketResultImageUploadingMap = reactive<Record<string, boolean>>({})
const hasTicketResultImageUploading = computed(() => Object.values(ticketResultImageUploadingMap).some(Boolean))
const getTicketResultImageKey = (row: any) => String(row?.id ?? row?.subjectId ?? row?.sort ?? '')
const setTicketResultImageUploading = (row: any, value: boolean) => {
const key = getTicketResultImageKey(row)
if (!key) return
if (value) {
ticketResultImageUploadingMap[key] = true
} else {
delete ticketResultImageUploadingMap[key]
}
}
const isTicketResultImageUploading = (row: any) => {
const key = getTicketResultImageKey(row)
return !!ticketResultImageUploadingMap[key]
}
const clearTicketResultImageUploading = () => {
for (const key of Object.keys(ticketResultImageUploadingMap)) {
delete ticketResultImageUploadingMap[key]
}
}
const handleTicketResultEditToggle = () => {
if (ticketResultEditable.value) {
//
@ -741,6 +780,7 @@ const ensureSubjectListLoaded = async (planId: number | string) => {
}
const setTicketResultList = (subjectList: any[]) => {
clearTicketResultImageUploading()
ticketResultList.value = (subjectList ?? []).map((item: any, index: number) => ({
id: item.id,
subjectId: item.id,
@ -750,7 +790,7 @@ const setTicketResultList = (subjectList: any[]) => {
valueType: item.valueType ?? '',
textInput: item.textInput ?? '',
inspectionResult: item.inspectionResult ?? '0',
images: item.images ?? '',
images: parseImages(item.images).slice(0, 3),
remark: item.remark ?? '',
sort: index + 1
}))
@ -770,6 +810,10 @@ const parseFirstImage = (value: any): string => {
return parseImages(value)[0] || ''
}
const getTicketResultImages = (row: any): string[] => {
return parseImages(row?.images)
}
const handleProjectFormExpandChange = async (row: any, expandedRows: any[]) => {
const isExpanded = expandedRows.some((r) => String(r.id) === String(row.id))
if (!isExpanded) return
@ -840,6 +884,9 @@ const selectMaintainType = async (type: number) => {
await initOptions()
//
if (type === 4) {
if (!replaceNetFormData.pressureNetTime) {
replaceNetFormData.pressureNetTime = getCurrentDateTime()
}
await loadSubMoldList()
}
}
@ -860,7 +907,10 @@ const submitForm = async () => {
taskType: String(taskFormData.taskType),
moldList: props.mold?.id ? String(props.mold.id) : undefined,
projectForm: taskFormData.projectForm,
ticketResultsList: ticketResultList.value.map((item: any) => ({ ...item }))
ticketResultsList: ticketResultList.value.map((item: any) => ({
...item,
images: normalizeImageString(item.images)
}))
})
message.success(t('common.createSuccess'))
//
@ -870,6 +920,7 @@ const submitForm = async () => {
selectedProjectFormName.value = ''
ticketResultList.value = []
ticketResultEditable.value = false
clearTicketResultImageUploading()
} else if (isRepair.value) {
//
const payload: any = {
@ -926,7 +977,7 @@ const submitForm = async () => {
message.success(t('common.createSuccess'))
//
replaceNetFormData.moldIds = []
replaceNetFormData.pressureNetTime = undefined
replaceNetFormData.pressureNetTime = getCurrentDateTime()
replaceNetFormData.remark = undefined
await nextTick()
maintainFormRef.value?.clearValidate()
@ -942,6 +993,7 @@ const submitForm = async () => {
const open = async () => {
maintainFormData.maintainType = 1
repairFormData.requireDate = Date.now()
replaceNetFormData.pressureNetTime = getCurrentDateTime()
await initOptions()
}
@ -1101,6 +1153,23 @@ defineExpose({ open })
margin: 0 auto;
}
.ticket-result-images {
display: flex;
justify-content: center;
min-height: 56px;
}
.ticket-result-images :deep(.el-upload-list--picture-card) {
display: flex;
flex-wrap: nowrap;
justify-content: center;
gap: 8px;
}
.ticket-result-images.is-full :deep(.el-upload--picture-card) {
display: none;
}
@media (max-width: 1400px) {
}

@ -102,6 +102,22 @@
class="device-ledger-history-item-label">{{ t('MoldManagement.MoldBrandDetail.inspectionTime') }}</span><span
class="device-ledger-history-item-value">{{ formatHistoryTime(item.taskTime) }}</span>
</div>
<div class="device-ledger-history-item-row device-ledger-history-item-row--images"><span
class="device-ledger-history-item-label">{{ t('MoldManagement.MoldBrandDetail.images') }}</span><span
class="device-ledger-history-item-value">
<div v-if="parseImages(item.images).length" class="device-ledger-repair-images">
<el-image
v-for="image in parseImages(item.images)"
:key="image"
:src="image"
:preview-src-list="parseImages(item.images)"
preview-teleported
fit="cover"
class="device-ledger-repair-image"
/>
</div>
<div v-else class="device-ledger-repair-image-placeholder">-</div>
</span></div>
<div class="device-ledger-history-item-row"><span
class="device-ledger-history-item-label">{{ t('MoldManagement.MoldBrandDetail.remark') }}</span><span
class="device-ledger-history-item-value">{{ item.remark ?? '-' }}</span></div>
@ -169,6 +185,22 @@
<div class="device-ledger-history-item-row"><span
class="device-ledger-history-item-label">{{ t('MoldManagement.MoldBrandDetail.finishDate') }}</span><span
class="device-ledger-history-item-value">{{ formatHistoryTime(row.finishDate) }}</span></div>
<div class="device-ledger-history-item-row device-ledger-history-item-row--images"><span
class="device-ledger-history-item-label">{{ t('MoldManagement.MoldRepair.faultImages') }}</span><span
class="device-ledger-history-item-value">
<div v-if="parseImages(row.faultImages).length" class="device-ledger-repair-images">
<el-image
v-for="image in parseImages(row.faultImages)"
:key="image"
:src="image"
:preview-src-list="parseImages(row.faultImages)"
preview-teleported
fit="cover"
class="device-ledger-repair-image"
/>
</div>
<div v-else class="device-ledger-repair-image-placeholder">-</div>
</span></div>
</div>
</div>
</div>
@ -423,6 +455,7 @@ type HistoryStepItem = {
criteria?: any
remark?: any
taskTime?: any
images?: any
}
type HistoryStepGroup = { key: string; time: string; operator: string; items: HistoryStepItem[] }
@ -451,7 +484,8 @@ const buildStepGroups = (
method: row?.inspectionMethod,
criteria: row?.judgmentCriteria,
remark: row?.remark,
taskTime: row?.taskTime
taskTime: row?.taskTime,
images: row?.images
}
if (!groups.has(groupKey)) {
@ -906,10 +940,40 @@ onMounted(() => {
gap: 12px;
}
.device-ledger-history-item-row--images {
align-items: start;
}
.device-ledger-history-item-label {
color: var(--el-text-color-secondary);
}
.device-ledger-repair-images {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.device-ledger-repair-image,
.device-ledger-repair-image-placeholder {
width: 64px;
height: 64px;
border-radius: 6px;
}
.device-ledger-repair-image {
border: 1px solid var(--el-border-color-lighter);
}
.device-ledger-repair-image-placeholder {
display: inline-flex;
align-items: center;
justify-content: center;
border: 1px dashed var(--el-border-color);
color: var(--el-text-color-placeholder);
background: var(--el-fill-color-light);
}
@media (max-width: 1024px) {
.mold-brand-detail__hero {
grid-template-columns: 1fr;
@ -919,4 +983,4 @@ onMounted(() => {
grid-template-columns: 1fr;
}
}
</style>
</style>

@ -17,15 +17,34 @@
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column :label="t('MoldManagement.TicketResultDialog.images')" align="center" prop="images" min-width="180">
<el-table-column :label="t('MoldManagement.TicketResultDialog.images')" align="center" prop="images" min-width="260">
<template #default="scope">
<UploadImg
v-if="String(scope.row.inspectionResult) === '0'" v-model="imageMap[String(scope.row.id)]"
:drag="false" :show-btn-text="false" width="64px" height="64px" />
<el-image
v-else-if="scope.row.images" :src="parseFirstImage(scope.row.images)"
:preview-src-list="parseImages(scope.row.images)" preview-teleported fit="cover"
style="width: 64px; height: 64px" />
<div
v-if="String(scope.row.inspectionResult) === '0'"
v-loading="isImageUploading(String(scope.row.id))"
element-loading-text="上传中"
class="ticket-result-images"
:class="{ 'is-full': (imageMap[String(scope.row.id)] || []).length >= 3 }"
>
<UploadImgs
v-model="imageMap[String(scope.row.id)]"
:limit="3"
width="64px"
height="64px"
@uploading-change="setImageUploading(String(scope.row.id), $event)"
/>
</div>
<div v-else-if="parseImages(scope.row.images).length" class="ticket-result-images">
<el-image
v-for="image in parseImages(scope.row.images)"
:key="image"
:src="image"
:preview-src-list="parseImages(scope.row.images)"
preview-teleported
fit="cover"
class="ticket-result-image"
/>
</div>
<span v-else>-</span>
</template>
</el-table-column>
@ -85,7 +104,7 @@ v-else-if="scope.row.images" :src="parseFirstImage(scope.row.images)"
<template #footer>
<el-button @click="dialogVisible = false">{{ t('MoldManagement.TicketResultDialog.cancel') }}</el-button>
<el-button v-if="String(jobStatus) === '0'" type="primary" @click="handleSave" :loading="submitLoading">{{ t('MoldManagement.TicketResultDialog.save') }}</el-button>
<el-button v-if="String(jobStatus) === '0'" type="primary" @click="handleSave" :loading="submitLoading" :disabled="hasImageUploading">{{ t('MoldManagement.TicketResultDialog.save') }}</el-button>
</template>
</Dialog>
</template>
@ -117,7 +136,22 @@ const managementId = ref<number | undefined>(undefined)
const jobStatus = ref<string | number | undefined>(undefined)
const cancelReason = ref<string | undefined>(undefined)
const decisionMap = reactive<Record<string, '1' | '2' | undefined>>({})
const imageMap = reactive<Record<string, string>>({})
const imageMap = reactive<Record<string, string[]>>({})
const imageUploadingMap = reactive<Record<string, boolean>>({})
const hasImageUploading = computed(() => Object.values(imageUploadingMap).some(Boolean))
const setImageUploading = (key: string, value: boolean) => {
if (!key) return
if (value) {
imageUploadingMap[key] = true
} else {
delete imageUploadingMap[key]
}
}
const isImageUploading = (key: string) => {
return !!imageUploadingMap[key]
}
const getTagDict = (dictType: string, value: any) => {
if (!dictReady.value) return undefined
@ -163,6 +197,7 @@ const open = async (options: { managementId: number; title?: string; jobStatus?:
cancelReason.value = options.cancelReason
for (const key of Object.keys(decisionMap)) delete decisionMap[key]
for (const key of Object.keys(imageMap)) delete imageMap[key]
for (const key of Object.keys(imageUploadingMap)) delete imageUploadingMap[key]
queryParams.pageNo = 1
await getList()
}
@ -189,8 +224,8 @@ const getList = async () => {
for (const row of list.value) {
const id = row?.id
if (!id) continue
if (row.images && !imageMap[String(id)]) {
imageMap[String(id)] = parseFirstImage(row.images)
if (!imageMap[String(id)]) {
imageMap[String(id)] = parseImages(row.images).slice(0, 3)
}
}
} finally {
@ -214,8 +249,8 @@ const handleSave = async () => {
if (String(row.inspectionResult) !== '0') continue
const decision = decisionMap[String(row.id)]
if (!decision) continue
const img = imageMap[String(row.id)]
payload.push({ ...(row as any), inspectionResult: decision, images: img || row.images })
const images = imageMap[String(row.id)]
payload.push({ ...(row as any), inspectionResult: decision, images: Array.isArray(images) ? images.join(',') : row.images })
}
if (!payload.length) {
message.error(t('MoldManagement.TicketResultDialog.selectDecisionTip'))
@ -247,13 +282,34 @@ const parseImages = (value: any): string[] => {
.filter(Boolean)
}
const parseFirstImage = (value: any): string => {
return parseImages(value)[0] || ''
}
</script>
<style scoped lang="scss">
:deep(.el-upload) {
margin: 0 auto;
}
.ticket-result-images {
display: flex;
justify-content: center;
min-height: 64px;
}
.ticket-result-images :deep(.el-upload-list--picture-card) {
display: flex;
flex-wrap: nowrap;
justify-content: center;
gap: 8px;
}
.ticket-result-images.is-full :deep(.el-upload--picture-card) {
display: none;
}
.ticket-result-image {
width: 64px;
height: 64px;
border: 1px solid var(--el-border-color-lighter);
border-radius: 6px;
}
</style>

Loading…
Cancel
Save