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

main
黄伟杰 2 weeks ago
parent 09db2fd3ed
commit a02a7d77a1

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

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

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

@ -129,18 +129,25 @@
<span v-else>-</span> <span v-else>-</span>
</template> </template>
</el-table-column> </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"> <template #default="scope">
<UploadImg <div
v-if="ticketResultEditable" v-if="ticketResultEditable"
v-model="scope.row.images" v-loading="isTicketResultImageUploading(scope.row)"
:drag="false" element-loading-text="上传中"
:show-btn-text="false" class="ticket-result-images"
width="56px" :class="{ 'is-full': getTicketResultImages(scope.row).length >= 3 }"
height="56px" >
/> <UploadImgs
v-model="scope.row.images"
:limit="3"
width="56px"
height="56px"
@uploading-change="setTicketResultImageUploading(scope.row, $event)"
/>
</div>
<el-image <el-image
v-else-if="scope.row.images" v-else-if="parseImages(scope.row.images).length"
:src="parseFirstImage(scope.row.images)" :src="parseFirstImage(scope.row.images)"
:preview-src-list="parseImages(scope.row.images)" :preview-src-list="parseImages(scope.row.images)"
preview-teleported preview-teleported
@ -382,7 +389,7 @@
<div class="mold-maintain-page__form-actions"> <div class="mold-maintain-page__form-actions">
<el-button @click="emit('back')">{{ t('MoldManagement.MoldBrandPage.cancel') }}</el-button> <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> </div>
</div> </div>
@ -573,6 +580,12 @@ const replaceNetFormData = reactive({
remark: undefined as string | undefined 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 subMoldList = ref<any[]>([])
const subMoldLoading = ref(false) const subMoldLoading = ref(false)
@ -652,6 +665,32 @@ const subjectLoadingMap = ref<Record<string, boolean>>({})
const ticketResultList = ref<any[]>([]) const ticketResultList = ref<any[]>([])
const ticketResultEditable = ref(false) const ticketResultEditable = ref(false)
const ticketResultBackup = ref<any[] | null>(null) 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 = () => { const handleTicketResultEditToggle = () => {
if (ticketResultEditable.value) { if (ticketResultEditable.value) {
// //
@ -741,6 +780,7 @@ const ensureSubjectListLoaded = async (planId: number | string) => {
} }
const setTicketResultList = (subjectList: any[]) => { const setTicketResultList = (subjectList: any[]) => {
clearTicketResultImageUploading()
ticketResultList.value = (subjectList ?? []).map((item: any, index: number) => ({ ticketResultList.value = (subjectList ?? []).map((item: any, index: number) => ({
id: item.id, id: item.id,
subjectId: item.id, subjectId: item.id,
@ -750,7 +790,7 @@ const setTicketResultList = (subjectList: any[]) => {
valueType: item.valueType ?? '', valueType: item.valueType ?? '',
textInput: item.textInput ?? '', textInput: item.textInput ?? '',
inspectionResult: item.inspectionResult ?? '0', inspectionResult: item.inspectionResult ?? '0',
images: item.images ?? '', images: parseImages(item.images).slice(0, 3),
remark: item.remark ?? '', remark: item.remark ?? '',
sort: index + 1 sort: index + 1
})) }))
@ -770,6 +810,10 @@ const parseFirstImage = (value: any): string => {
return parseImages(value)[0] || '' return parseImages(value)[0] || ''
} }
const getTicketResultImages = (row: any): string[] => {
return parseImages(row?.images)
}
const handleProjectFormExpandChange = async (row: any, expandedRows: any[]) => { const handleProjectFormExpandChange = async (row: any, expandedRows: any[]) => {
const isExpanded = expandedRows.some((r) => String(r.id) === String(row.id)) const isExpanded = expandedRows.some((r) => String(r.id) === String(row.id))
if (!isExpanded) return if (!isExpanded) return
@ -840,6 +884,9 @@ const selectMaintainType = async (type: number) => {
await initOptions() await initOptions()
// //
if (type === 4) { if (type === 4) {
if (!replaceNetFormData.pressureNetTime) {
replaceNetFormData.pressureNetTime = getCurrentDateTime()
}
await loadSubMoldList() await loadSubMoldList()
} }
} }
@ -860,7 +907,10 @@ const submitForm = async () => {
taskType: String(taskFormData.taskType), taskType: String(taskFormData.taskType),
moldList: props.mold?.id ? String(props.mold.id) : undefined, moldList: props.mold?.id ? String(props.mold.id) : undefined,
projectForm: taskFormData.projectForm, 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')) message.success(t('common.createSuccess'))
// //
@ -870,6 +920,7 @@ const submitForm = async () => {
selectedProjectFormName.value = '' selectedProjectFormName.value = ''
ticketResultList.value = [] ticketResultList.value = []
ticketResultEditable.value = false ticketResultEditable.value = false
clearTicketResultImageUploading()
} else if (isRepair.value) { } else if (isRepair.value) {
// //
const payload: any = { const payload: any = {
@ -926,7 +977,7 @@ const submitForm = async () => {
message.success(t('common.createSuccess')) message.success(t('common.createSuccess'))
// //
replaceNetFormData.moldIds = [] replaceNetFormData.moldIds = []
replaceNetFormData.pressureNetTime = undefined replaceNetFormData.pressureNetTime = getCurrentDateTime()
replaceNetFormData.remark = undefined replaceNetFormData.remark = undefined
await nextTick() await nextTick()
maintainFormRef.value?.clearValidate() maintainFormRef.value?.clearValidate()
@ -942,6 +993,7 @@ const submitForm = async () => {
const open = async () => { const open = async () => {
maintainFormData.maintainType = 1 maintainFormData.maintainType = 1
repairFormData.requireDate = Date.now() repairFormData.requireDate = Date.now()
replaceNetFormData.pressureNetTime = getCurrentDateTime()
await initOptions() await initOptions()
} }
@ -1101,6 +1153,23 @@ defineExpose({ open })
margin: 0 auto; 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) { @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-label">{{ t('MoldManagement.MoldBrandDetail.inspectionTime') }}</span><span
class="device-ledger-history-item-value">{{ formatHistoryTime(item.taskTime) }}</span> class="device-ledger-history-item-value">{{ formatHistoryTime(item.taskTime) }}</span>
</div> </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 <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-label">{{ t('MoldManagement.MoldBrandDetail.remark') }}</span><span
class="device-ledger-history-item-value">{{ item.remark ?? '-' }}</span></div> class="device-ledger-history-item-value">{{ item.remark ?? '-' }}</span></div>
@ -169,6 +185,22 @@
<div class="device-ledger-history-item-row"><span <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-label">{{ t('MoldManagement.MoldBrandDetail.finishDate') }}</span><span
class="device-ledger-history-item-value">{{ formatHistoryTime(row.finishDate) }}</span></div> 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> </div>
</div> </div>
@ -423,6 +455,7 @@ type HistoryStepItem = {
criteria?: any criteria?: any
remark?: any remark?: any
taskTime?: any taskTime?: any
images?: any
} }
type HistoryStepGroup = { key: string; time: string; operator: string; items: HistoryStepItem[] } type HistoryStepGroup = { key: string; time: string; operator: string; items: HistoryStepItem[] }
@ -451,7 +484,8 @@ const buildStepGroups = (
method: row?.inspectionMethod, method: row?.inspectionMethod,
criteria: row?.judgmentCriteria, criteria: row?.judgmentCriteria,
remark: row?.remark, remark: row?.remark,
taskTime: row?.taskTime taskTime: row?.taskTime,
images: row?.images
} }
if (!groups.has(groupKey)) { if (!groups.has(groupKey)) {
@ -906,10 +940,40 @@ onMounted(() => {
gap: 12px; gap: 12px;
} }
.device-ledger-history-item-row--images {
align-items: start;
}
.device-ledger-history-item-label { .device-ledger-history-item-label {
color: var(--el-text-color-secondary); 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) { @media (max-width: 1024px) {
.mold-brand-detail__hero { .mold-brand-detail__hero {
grid-template-columns: 1fr; grid-template-columns: 1fr;
@ -919,4 +983,4 @@ onMounted(() => {
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
} }
</style> </style>

@ -17,15 +17,34 @@
<span v-else>-</span> <span v-else>-</span>
</template> </template>
</el-table-column> </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"> <template #default="scope">
<UploadImg <div
v-if="String(scope.row.inspectionResult) === '0'" v-model="imageMap[String(scope.row.id)]" v-if="String(scope.row.inspectionResult) === '0'"
:drag="false" :show-btn-text="false" width="64px" height="64px" /> v-loading="isImageUploading(String(scope.row.id))"
<el-image element-loading-text="上传中"
v-else-if="scope.row.images" :src="parseFirstImage(scope.row.images)" class="ticket-result-images"
:preview-src-list="parseImages(scope.row.images)" preview-teleported fit="cover" :class="{ 'is-full': (imageMap[String(scope.row.id)] || []).length >= 3 }"
style="width: 64px; height: 64px" /> >
<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> <span v-else>-</span>
</template> </template>
</el-table-column> </el-table-column>
@ -85,7 +104,7 @@ v-else-if="scope.row.images" :src="parseFirstImage(scope.row.images)"
<template #footer> <template #footer>
<el-button @click="dialogVisible = false">{{ t('MoldManagement.TicketResultDialog.cancel') }}</el-button> <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> </template>
</Dialog> </Dialog>
</template> </template>
@ -117,7 +136,22 @@ const managementId = ref<number | undefined>(undefined)
const jobStatus = ref<string | number | undefined>(undefined) const jobStatus = ref<string | number | undefined>(undefined)
const cancelReason = ref<string | undefined>(undefined) const cancelReason = ref<string | undefined>(undefined)
const decisionMap = reactive<Record<string, '1' | '2' | 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) => { const getTagDict = (dictType: string, value: any) => {
if (!dictReady.value) return undefined if (!dictReady.value) return undefined
@ -163,6 +197,7 @@ const open = async (options: { managementId: number; title?: string; jobStatus?:
cancelReason.value = options.cancelReason cancelReason.value = options.cancelReason
for (const key of Object.keys(decisionMap)) delete decisionMap[key] 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(imageMap)) delete imageMap[key]
for (const key of Object.keys(imageUploadingMap)) delete imageUploadingMap[key]
queryParams.pageNo = 1 queryParams.pageNo = 1
await getList() await getList()
} }
@ -189,8 +224,8 @@ const getList = async () => {
for (const row of list.value) { for (const row of list.value) {
const id = row?.id const id = row?.id
if (!id) continue if (!id) continue
if (row.images && !imageMap[String(id)]) { if (!imageMap[String(id)]) {
imageMap[String(id)] = parseFirstImage(row.images) imageMap[String(id)] = parseImages(row.images).slice(0, 3)
} }
} }
} finally { } finally {
@ -214,8 +249,8 @@ const handleSave = async () => {
if (String(row.inspectionResult) !== '0') continue if (String(row.inspectionResult) !== '0') continue
const decision = decisionMap[String(row.id)] const decision = decisionMap[String(row.id)]
if (!decision) continue if (!decision) continue
const img = imageMap[String(row.id)] const images = imageMap[String(row.id)]
payload.push({ ...(row as any), inspectionResult: decision, images: img || row.images }) payload.push({ ...(row as any), inspectionResult: decision, images: Array.isArray(images) ? images.join(',') : row.images })
} }
if (!payload.length) { if (!payload.length) {
message.error(t('MoldManagement.TicketResultDialog.selectDecisionTip')) message.error(t('MoldManagement.TicketResultDialog.selectDecisionTip'))
@ -247,13 +282,34 @@ const parseImages = (value: any): string[] => {
.filter(Boolean) .filter(Boolean)
} }
const parseFirstImage = (value: any): string => {
return parseImages(value)[0] || ''
}
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
:deep(.el-upload) { :deep(.el-upload) {
margin: 0 auto; 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> </style>

Loading…
Cancel
Save