增加标注

This commit is contained in:
BianLzhaoMin 2025-11-25 18:06:26 +08:00
parent c7ac05a8ac
commit 53b8a63b38
2 changed files with 1161 additions and 273 deletions

View File

@ -2,14 +2,26 @@
<div class="image-results-container"> <div class="image-results-container">
<!-- 按下标分组显示 --> <!-- 按下标分组显示 -->
<template v-if="groupedImageResults && groupedImageResults.length > 0"> <template v-if="groupedImageResults && groupedImageResults.length > 0">
<div v-for="(group, groupIndex) in groupedImageResults" :key="group.index" class="group-section"> <div
v-for="(group, groupIndex) in groupedImageResults"
:key="group.index"
class="group-section"
>
<!-- 每组的上传信息提示 --> <!-- 每组的上传信息提示 -->
<div class="upload-info" v-if="isEvaluate == '0'"> <div class="upload-info" v-if="isEvaluate == '0'">
<img src="@/assets/images/imageCaptioning/photography.png" alt="" class="icon-photography"> <img
src="@/assets/images/imageCaptioning/photography.png"
alt=""
class="icon-photography"
/>
<span> <span>
{{ group.index + 1 }} - {{ group.operaName }}: {{ group.index + 1 }} - {{ group.operaName }}:
上传{{ group.totalCount }}张图片可识别标注{{ group.recognizedCount }} 上传{{ group.totalCount }}张图片可识别标注{{
{{ group.unrecognizedCount }}张无法识别标注已识别标注图片如下请确认 group.recognizedCount
}}
{{
group.unrecognizedCount
}}张无法识别标注已识别标注图片如下请确认
</span> </span>
</div> </div>
@ -18,46 +30,96 @@
<div <div
v-for="(result, imageIndex) in group.images" v-for="(result, imageIndex) in group.images"
:key="imageIndex" :key="imageIndex"
:class="isEvaluate == '1' ? 'image-item-evaluate' : 'image-item'" :class="
isEvaluate == '1'
? 'image-item-evaluate'
: 'image-item'
"
> >
<el-image
<ImagePreview :class="
:class="isEvaluate == '1' ? 'result-image-evaluate' : 'result-image'" isEvaluate == '1'
? 'result-image-evaluate'
: 'result-image'
"
:src="result.url" :src="result.url"
/> fit="contain"
@click="onClickImage(result)"
>
<div slot="error" class="image-slot">
<i class="el-icon-picture-outline"></i>
</div>
</el-image>
<!-- 右上角的hand图标 --> <!-- 右上角的hand图标 -->
<img <img
v-if="isGroupImageSelected(groupIndex, imageIndex) && isEvaluate == '0'" v-if="
isGroupImageSelected(groupIndex, imageIndex) &&
isEvaluate == '0'
"
src="@/assets/images/imageCaptioning/hand-yellow.png" src="@/assets/images/imageCaptioning/hand-yellow.png"
alt="selected" alt="selected"
class="icon-hand" class="icon-hand"
@click="handleGroupImageClick(groupIndex, imageIndex, group.isSure)" @click="
> handleGroupImageClick(
groupIndex,
imageIndex,
group.isSure,
)
"
/>
<img <img
v-else-if="!isGroupImageSelected(groupIndex, imageIndex) && isEvaluate == '0'" v-else-if="
!isGroupImageSelected(groupIndex, imageIndex) &&
isEvaluate == '0'
"
src="@/assets/images/imageCaptioning/hand.png" src="@/assets/images/imageCaptioning/hand.png"
alt="hand" alt="hand"
class="icon-hand" class="icon-hand"
@click="handleGroupImageClick(groupIndex, imageIndex, group.isSure)" @click="
> handleGroupImageClick(
groupIndex,
imageIndex,
group.isSure,
)
"
/>
<div class="card-content" v-if="isEvaluate == '1'"> <div class="card-content" v-if="isEvaluate == '1'">
<div class="card-footer"> <div class="card-footer">
<div class="person-info"> <div class="person-info">
<span class="name">综合得分:{{ overallScore }}</span> <span class="name"
>综合得分:{{ overallScore }}</span
>
<span class="name">{{ dictValue }}</span> <span class="name">{{ dictValue }}</span>
<span class="date">{{ contentImage }}</span> <span class="date">{{ contentImage }}</span>
</div> </div>
<div class="tags"> <div class="tags">
<div class="tag-row"> <div class="tag-row">
<span class="tag-item" style="padding-left: 16px;">清晰度: {{ clarity }}</span> <span
<span class="tag-item" style="padding-left: 16px;">干净度: {{ cleanliness }}</span> class="tag-item"
<span class="tag-item">压缩痕迹: {{ compressMarks }}</span> style="padding-left: 16px"
>清晰度: {{ clarity }}</span
>
<span
class="tag-item"
style="padding-left: 16px"
>干净度: {{ cleanliness }}</span
>
<span class="tag-item"
>压缩痕迹:
{{ compressMarks }}</span
>
</div> </div>
<div class="tag-row"> <div class="tag-row">
<span class="tag-item">明暗均衡: {{ balance }}</span> <span class="tag-item"
<span class="tag-item">整体观感: {{ impression }}</span> >明暗均衡: {{ balance }}</span
<span class="tag-item">细节体验: {{ detail }}</span> >
<span class="tag-item"
>整体观感: {{ impression }}</span
>
<span class="tag-item"
>细节体验: {{ detail }}</span
>
</div> </div>
</div> </div>
</div> </div>
@ -81,8 +143,12 @@
<!-- 保持原有的单一展示方式 --> <!-- 保持原有的单一展示方式 -->
<template v-else> <template v-else>
<!-- 上传信息提示 --> <!-- 上传信息提示 -->
<div class="upload-info" v-if ="isEvaluate == '0'"> <div class="upload-info" v-if="isEvaluate == '0'">
<img src="@/assets/images/imageCaptioning/photography.png" alt="" class="icon-photography"> <img
src="@/assets/images/imageCaptioning/photography.png"
alt=""
class="icon-photography"
/>
<span>{{ uploadInfo }}</span> <span>{{ uploadInfo }}</span>
</div> </div>
@ -91,12 +157,18 @@
<div <div
v-for="(result, index) in imageResults" v-for="(result, index) in imageResults"
:key="index" :key="index"
:class="isEvaluate == '1' ? 'image-item-evaluate' : 'image-item'" :class="
isEvaluate == '1' ? 'image-item-evaluate' : 'image-item'
"
> >
<ImagePreview <ImagePreview
:class="isEvaluate == '1' ? 'result-image-evaluate' : 'result-image'" :class="
isEvaluate == '1'
? 'result-image-evaluate'
: 'result-image'
"
:src="result.url" :src="result.url"
@click.native="onClickImage(result)"
/> />
<!-- 右上角的hand图标 --> <!-- 右上角的hand图标 -->
@ -106,32 +178,50 @@
alt="selected" alt="selected"
class="icon-hand" class="icon-hand"
@click="handleImageClick(index, isSure)" @click="handleImageClick(index, isSure)"
> />
<img <img
v-else-if="!selectedImages[index] && isEvaluate == '0'" v-else-if="!selectedImages[index] && isEvaluate == '0'"
src="@/assets/images/imageCaptioning/hand.png" src="@/assets/images/imageCaptioning/hand.png"
alt="hand" alt="hand"
class="icon-hand" class="icon-hand"
@click="handleImageClick(index, isSure)" @click="handleImageClick(index, isSure)"
> />
<div class="card-content" v-if="isEvaluate == '1'"> <div class="card-content" v-if="isEvaluate == '1'">
<div class="card-footer"> <div class="card-footer">
<div class="person-info"> <div class="person-info">
<span class="name">综合得分:{{ overallScore }}</span> <span class="name"
>综合得分:{{ overallScore }}</span
>
<span class="name">{{ dictValue }}</span> <span class="name">{{ dictValue }}</span>
<span class="date">{{ contentImage }}</span> <span class="date">{{ contentImage }}</span>
</div> </div>
<div class="tags"> <div class="tags">
<div class="tag-row"> <div class="tag-row">
<span class="tag-item" style="padding-left: 16px;">清晰度: {{ clarity }}</span> <span
<span class="tag-item" style="padding-left: 16px;">干净度: {{ cleanliness }}</span> class="tag-item"
<span class="tag-item">压缩痕迹: {{ compressMarks }}</span> style="padding-left: 16px"
>清晰度: {{ clarity }}</span
>
<span
class="tag-item"
style="padding-left: 16px"
>干净度: {{ cleanliness }}</span
>
<span class="tag-item"
>压缩痕迹: {{ compressMarks }}</span
>
</div> </div>
<div class="tag-row"> <div class="tag-row">
<span class="tag-item">明暗均衡: {{ balance }}</span> <span class="tag-item"
<span class="tag-item">整体观感: {{ impression }}</span> >明暗均衡: {{ balance }}</span
<span class="tag-item">细节体验: {{ detail }}</span> >
<span class="tag-item"
>整体观感: {{ impression }}</span
>
<span class="tag-item"
>细节体验: {{ detail }}</span
>
</div> </div>
</div> </div>
</div> </div>
@ -141,87 +231,433 @@
<!-- 操作按钮 --> <!-- 操作按钮 -->
<div class="action-buttons" v-if="isEvaluate == '0'"> <div class="action-buttons" v-if="isEvaluate == '0'">
<el-button type="primary" @click="$emit('confirm-results')" :disabled="isSure === '1'"> <el-button
type="primary"
@click="$emit('confirm-results')"
:disabled="isSure === '1'"
>
{{ isSure === '1' ? '已确认' : '确认' }} {{ isSure === '1' ? '已确认' : '确认' }}
</el-button> </el-button>
</div> </div>
</template> </template>
<!-- 图片显示弹框 -->
<el-dialog
width="70%"
:visible.sync="imageDialogVisible"
:before-close="handleCloseImageDialog"
>
<template #title>
<span>图片标注</span>
</template>
<div v-if="currentImage" class="annotation-dialog">
<div class="annotation-preview">
<div
v-if="isReAnnotating"
class="annotation-canvas-wrapper"
:style="{
width: canvasWidth + 'px',
height: canvasHeight + 'px',
}"
>
<canvas ref="imageCanvas" class="image-canvas"></canvas>
<canvas
ref="drawCanvas"
class="draw-canvas"
@mousedown="handleMouseDown"
@mousemove="handleMouseMove"
@mouseup="handleMouseUp"
@mouseleave="handleMouseLeave"
></canvas>
</div>
<img
v-else
:src="currentImage.url"
alt="preview"
class="annotation-preview-image"
/>
</div>
<div v-if="isReAnnotating" class="annotation-type">
<span class="label">标注类型</span>
<el-select
v-model="annotationType"
placeholder="请选择"
size="small"
class="annotation-type-select"
>
<el-option
v-for="option in annotationTypeOptions"
:key="option.value"
:label="option.label"
:value="option.value"
/>
</el-select>
</div>
<div class="annotation-info">
<div class="annotation-meta">
<p v-if="isReAnnotating">
图片尺寸{{ originalSize.width }} x
{{ originalSize.height }}
</p>
<p v-if="annotationCoords">
左上({{ annotationCoords.x1 }},
{{ annotationCoords.y1 }}) 右下({{
annotationCoords.x2
}}, {{ annotationCoords.y2 }})
</p>
<!-- <p v-else
>{{
isReAnnotating
? '拖拽绘制标注框'
: '点击重新标注'
}}</p
> -->
</div>
<div class="annotation-buttons">
<el-button
v-if="!isReAnnotating"
type="primary"
size="small"
@click="startReAnnotation"
>
重新标注
</el-button>
<template v-else>
<el-button size="small" @click="cancelReAnnotation">
取消标注
</el-button>
<el-button
type="primary"
size="small"
:disabled="!annotationCoords || !annotationType"
@click="emitAnnotation"
>
提交
</el-button>
</template>
</div>
</div>
</div>
<div v-else class="annotation-empty">请先选择图片</div>
</el-dialog>
</div> </div>
</template> </template>
<script> <script>
export default { export default {
name: "ImageResults", name: 'ImageResults',
props: { props: {
uploadInfo: { uploadInfo: {
type: String, type: String,
default: '' default: '',
}, },
imageResults: { imageResults: {
type: Array, type: Array,
default: () => [] default: () => [],
}, },
groupedImageResults: { groupedImageResults: {
type: Array, type: Array,
default: () => [] default: () => [],
}, },
selectedImages: { selectedImages: {
type: Object, type: Object,
default: () => ({}) default: () => ({}),
}, },
isEvaluate: { isEvaluate: {
type: String, type: String,
default: '0' default: '0',
}, },
isSure: { isSure: {
type: String, type: String,
default: '' default: '',
},
},
data() {
return {
imageDialogVisible: false,
currentImage: null,
canvasWidth: 720,
canvasHeight: 480,
isReAnnotating: false,
originalSize: {
width: 0,
height: 0,
},
scaleInfo: {
scale: 1,
offsetX: 0,
offsetY: 0,
},
isDrawing: false,
startPoint: null,
currentRect: null,
annotationCoords: null,
annotationType: '',
annotationTypeOptions: [
{ label: '人物', value: 'person' },
{ label: '物体', value: 'object' },
{ label: '场景', value: 'scene' },
],
} }
}, },
methods: { methods: {
isGroupImageSelected(groupIndex, imageIndex) { isGroupImageSelected(groupIndex, imageIndex) {
// //
const key = `${groupIndex}-${imageIndex}`; const key = `${groupIndex}-${imageIndex}`
return !!this.selectedImages[key]; return !!this.selectedImages[key]
}, },
handleGroupImageClick(groupIndex, imageIndex, groupIsSure) { handleGroupImageClick(groupIndex, imageIndex, groupIsSure) {
// //
if (groupIsSure === '1') { if (groupIsSure === '1') {
this.$message.warning('该组已确认,无法更改选择'); this.$message.warning('该组已确认,无法更改选择')
return; return
} }
const key = `${groupIndex}-${imageIndex}`; const key = `${groupIndex}-${imageIndex}`
this.$emit('hand-click', {groupIndex, imageIndex, key}); this.$emit('hand-click', { groupIndex, imageIndex, key })
}, },
handleImageClick(index, isSure) { handleImageClick(index, isSure) {
// //
if (isSure === '1') { if (isSure === '1') {
this.$message.warning('已确认,无法更改选择'); this.$message.warning('已确认,无法更改选择')
return; return
} }
this.$emit('hand-click', {index, isSingle: true}); this.$emit('hand-click', { index, isSingle: true })
}, },
confirmGroup(groupIndex,id) { confirmGroup(groupIndex, id) {
console.log('confirmGroup', id) console.log('confirmGroup', id)
// //
this.$emit('confirm-results', id); this.$emit('confirm-results', id)
},
//
onClickImage(image) {
if (!image || !image.url) {
return
} }
this.currentImage = image
this.imageDialogVisible = true
this.annotationCoords = null
this.annotationType = ''
this.isReAnnotating = false
this.$nextTick(() => {
this.renderBaseImage()
})
},
//
handleCloseImageDialog() {
this.imageDialogVisible = false
this.resetAnnotation()
},
startReAnnotation() {
this.isReAnnotating = true
this.annotationCoords = null
this.annotationType = ''
this.$nextTick(() => {
this.renderBaseImage()
})
},
cancelReAnnotation() {
this.isReAnnotating = false
this.clearDrawing()
this.annotationCoords = null
this.annotationType = ''
},
renderBaseImage() {
const imageCanvas = this.$refs.imageCanvas
const drawCanvas = this.$refs.drawCanvas
if (
!this.isReAnnotating ||
!imageCanvas ||
!drawCanvas ||
!this.currentImage
) {
return
} }
imageCanvas.width = this.canvasWidth
imageCanvas.height = this.canvasHeight
drawCanvas.width = this.canvasWidth
drawCanvas.height = this.canvasHeight
const ctx = imageCanvas.getContext('2d')
ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight)
const img = new Image()
img.crossOrigin = 'Anonymous'
img.onload = () => {
const { naturalWidth, naturalHeight } = img
this.originalSize = {
width: naturalWidth,
height: naturalHeight,
}
const scale = Math.min(
this.canvasWidth / naturalWidth,
this.canvasHeight / naturalHeight,
)
const drawWidth = naturalWidth * scale
const drawHeight = naturalHeight * scale
const offsetX = (this.canvasWidth - drawWidth) / 2
const offsetY = (this.canvasHeight - drawHeight) / 2
ctx.drawImage(img, offsetX, offsetY, drawWidth, drawHeight)
this.scaleInfo = { scale, offsetX, offsetY }
this.clearDrawing()
}
img.src = this.currentImage.url
},
clearDrawing() {
const drawCanvas = this.$refs.drawCanvas
if (!drawCanvas) return
const drawCtx = drawCanvas.getContext('2d')
drawCtx.clearRect(0, 0, drawCanvas.width, drawCanvas.height)
},
handleMouseDown(event) {
if (!this.currentImage) return
const position = this.getCanvasPosition(event)
this.isDrawing = true
this.startPoint = position
this.currentRect = { ...position, width: 0, height: 0 }
},
handleMouseMove(event) {
if (!this.isDrawing || !this.currentRect) return
const position = this.getCanvasPosition(event)
this.currentRect.width = position.x - this.startPoint.x
this.currentRect.height = position.y - this.startPoint.y
this.drawCurrentRect()
},
handleMouseUp() {
if (!this.isDrawing || !this.currentRect) return
this.isDrawing = false
this.annotationCoords = this.calculateOriginalCoords(
this.currentRect,
)
},
handleMouseLeave() {
if (!this.isDrawing) return
this.isDrawing = false
this.drawCurrentRect()
},
getCanvasPosition(event) {
const canvas = this.$refs.drawCanvas
const rect = canvas.getBoundingClientRect()
const x = event.clientX - rect.left
const y = event.clientY - rect.top
return { x, y }
},
drawCurrentRect() {
const drawCanvas = this.$refs.drawCanvas
if (!drawCanvas || !this.currentRect) return
const ctx = drawCanvas.getContext('2d')
ctx.clearRect(0, 0, drawCanvas.width, drawCanvas.height)
ctx.strokeStyle = '#ff8800'
ctx.lineWidth = 2
ctx.setLineDash([6, 4])
ctx.strokeRect(
this.currentRect.x,
this.currentRect.y,
this.currentRect.width,
this.currentRect.height,
)
ctx.setLineDash([])
},
calculateOriginalCoords(rect) {
const { scale, offsetX, offsetY } = this.scaleInfo
if (!scale || !this.originalSize.width) return null
const normalize = (value, offset) => (value - offset) / scale
let x1 = Math.min(rect.x, rect.x + rect.width)
let y1 = Math.min(rect.y, rect.y + rect.height)
let x2 = Math.max(rect.x, rect.x + rect.width)
let y2 = Math.max(rect.y, rect.y + rect.height)
x1 = this.clampCoordinate(
normalize(x1, offsetX),
0,
this.originalSize.width,
)
y1 = this.clampCoordinate(
normalize(y1, offsetY),
0,
this.originalSize.height,
)
x2 = this.clampCoordinate(
normalize(x2, offsetX),
0,
this.originalSize.width,
)
y2 = this.clampCoordinate(
normalize(y2, offsetY),
0,
this.originalSize.height,
)
return {
x1: Math.round(x1),
y1: Math.round(y1),
x2: Math.round(x2),
y2: Math.round(y2),
}
},
clampCoordinate(value, min, max) {
if (Number.isNaN(value)) return min
return Math.max(min, Math.min(max, value))
},
emitAnnotation() {
if (
!this.annotationCoords ||
!this.currentImage ||
!this.annotationType
)
return
this.$emit('manual-annotation', {
image: this.currentImage,
coords: this.annotationCoords,
type: this.annotationType,
})
this.$message.success('坐标已生成')
},
resetAnnotation() {
this.currentImage = null
this.annotationCoords = null
this.annotationType = ''
this.isReAnnotating = false
this.originalSize = { width: 0, height: 0 }
this.scaleInfo = { scale: 1, offsetX: 0, offsetY: 0 }
this.isDrawing = false
this.startPoint = null
this.currentRect = null
},
},
} }
</script> </script>
<style scoped> <style scoped lang="scss">
.upload-info { .upload-info {
display: flex; display: flex;
align-items: center; align-items: center;
margin-bottom: 20px; margin-bottom: 20px;
font-size: 16px; font-size: 16px;
color: #54646C; color: #54646c;
font-weight: 600; font-weight: 600;
} }
@ -269,6 +705,83 @@ export default {
object-fit: contain; object-fit: contain;
} }
.annotation-dialog {
display: flex;
flex-direction: column;
gap: 16px;
align-items: center;
}
.annotation-preview {
width: 100%;
display: flex;
justify-content: center;
}
.annotation-canvas-wrapper {
position: relative;
border: 1px dashed #dcdfe6;
border-radius: 8px;
background: #000;
}
.image-canvas,
.draw-canvas {
position: absolute;
top: 0;
left: 0;
}
.image-canvas {
z-index: 1;
}
.draw-canvas {
z-index: 2;
cursor: crosshair;
}
.annotation-preview-image {
max-width: 100%;
max-height: 520px;
border-radius: 8px;
}
.annotation-type {
width: 100%;
display: flex;
align-items: center;
gap: 8px;
}
.annotation-type-select {
flex: 1;
}
.annotation-info {
width: 100%;
display: flex;
align-items: flex-start;
justify-content: space-between;
color: #333;
gap: 16px;
}
.annotation-meta p {
margin: 0 0 6px 0;
}
.annotation-buttons {
display: flex;
gap: 8px;
}
.annotation-empty {
text-align: center;
color: #999;
padding: 30px 0;
}
.icon-hand { .icon-hand {
position: absolute; position: absolute;
top: 25px; top: 25px;
@ -289,7 +802,6 @@ export default {
margin-bottom: 20px; margin-bottom: 20px;
} }
.card-content { .card-content {
width: 100%; width: 100%;
display: flex; display: flex;
@ -335,7 +847,7 @@ export default {
display: table; display: table;
width: 100%; width: 100%;
table-layout: fixed; table-layout: fixed;
margin:3px 0 0 0; margin: 3px 0 0 0;
} }
.tag-item { .tag-item {
@ -353,4 +865,24 @@ export default {
justify-content: flex-end; justify-content: flex-end;
margin: 10px 18px 10px 18px; margin: 10px 18px 10px 18px;
} }
el-image {
::v-deep .el-image__inner {
object-fit: contain;
transition: all 0.3s;
cursor: pointer;
&:hover {
transform: scale(1.2);
}
}
::v-deep .image-slot {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
color: #909399;
font-size: 30px;
}
}
</style> </style>

View File

@ -0,0 +1,356 @@
<template>
<div class="image-results-container">
<!-- 按下标分组显示 -->
<template v-if="groupedImageResults && groupedImageResults.length > 0">
<div v-for="(group, groupIndex) in groupedImageResults" :key="group.index" class="group-section">
<!-- 每组的上传信息提示 -->
<div class="upload-info" v-if="isEvaluate == '0'">
<img src="@/assets/images/imageCaptioning/photography.png" alt="" class="icon-photography">
<span>
{{ group.index + 1 }} - {{ group.operaName }}:
上传{{ group.totalCount }}张图片可识别标注{{ group.recognizedCount }}
{{ group.unrecognizedCount }}张无法识别标注已识别标注图片如下请确认
</span>
</div>
<!-- 每组的图片展示 -->
<div class="image-results-grid">
<div
v-for="(result, imageIndex) in group.images"
:key="imageIndex"
:class="isEvaluate == '1' ? 'image-item-evaluate' : 'image-item'"
>
<ImagePreview
:class="isEvaluate == '1' ? 'result-image-evaluate' : 'result-image'"
:src="result.url"
/>
<!-- 右上角的hand图标 -->
<img
v-if="isGroupImageSelected(groupIndex, imageIndex) && isEvaluate == '0'"
src="@/assets/images/imageCaptioning/hand-yellow.png"
alt="selected"
class="icon-hand"
@click="handleGroupImageClick(groupIndex, imageIndex, group.isSure)"
>
<img
v-else-if="!isGroupImageSelected(groupIndex, imageIndex) && isEvaluate == '0'"
src="@/assets/images/imageCaptioning/hand.png"
alt="hand"
class="icon-hand"
@click="handleGroupImageClick(groupIndex, imageIndex, group.isSure)"
>
<div class="card-content" v-if="isEvaluate == '1'">
<div class="card-footer">
<div class="person-info">
<span class="name">综合得分:{{ overallScore }}</span>
<span class="name">{{ dictValue }}</span>
<span class="date">{{ contentImage }}</span>
</div>
<div class="tags">
<div class="tag-row">
<span class="tag-item" style="padding-left: 16px;">清晰度: {{ clarity }}</span>
<span class="tag-item" style="padding-left: 16px;">干净度: {{ cleanliness }}</span>
<span class="tag-item">压缩痕迹: {{ compressMarks }}</span>
</div>
<div class="tag-row">
<span class="tag-item">明暗均衡: {{ balance }}</span>
<span class="tag-item">整体观感: {{ impression }}</span>
<span class="tag-item">细节体验: {{ detail }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 每组的操作按钮 -->
<div class="action-buttons" v-if="isEvaluate == '0'">
<el-button
type="primary"
@click="confirmGroup(groupIndex, group.id)"
:disabled="group.isSure === '1'"
>
{{ group.isSure === '1' ? '已确认' : '确认' }}
</el-button>
</div>
</div>
</template>
<!-- 保持原有的单一展示方式 -->
<template v-else>
<!-- 上传信息提示 -->
<div class="upload-info" v-if ="isEvaluate == '0'">
<img src="@/assets/images/imageCaptioning/photography.png" alt="" class="icon-photography">
<span>{{ uploadInfo }}</span>
</div>
<!-- 图片识别结果展示 -->
<div class="image-results-grid">
<div
v-for="(result, index) in imageResults"
:key="index"
:class="isEvaluate == '1' ? 'image-item-evaluate' : 'image-item'"
>
<ImagePreview
:class="isEvaluate == '1' ? 'result-image-evaluate' : 'result-image'"
:src="result.url"
/>
<!-- 右上角的hand图标 -->
<img
v-if="selectedImages[index] && isEvaluate == '0'"
src="@/assets/images/imageCaptioning/hand-yellow.png"
alt="selected"
class="icon-hand"
@click="handleImageClick(index, isSure)"
>
<img
v-else-if="!selectedImages[index] && isEvaluate == '0'"
src="@/assets/images/imageCaptioning/hand.png"
alt="hand"
class="icon-hand"
@click="handleImageClick(index, isSure)"
>
<div class="card-content" v-if="isEvaluate == '1'">
<div class="card-footer">
<div class="person-info">
<span class="name">综合得分:{{ overallScore }}</span>
<span class="name">{{ dictValue }}</span>
<span class="date">{{ contentImage }}</span>
</div>
<div class="tags">
<div class="tag-row">
<span class="tag-item" style="padding-left: 16px;">清晰度: {{ clarity }}</span>
<span class="tag-item" style="padding-left: 16px;">干净度: {{ cleanliness }}</span>
<span class="tag-item">压缩痕迹: {{ compressMarks }}</span>
</div>
<div class="tag-row">
<span class="tag-item">明暗均衡: {{ balance }}</span>
<span class="tag-item">整体观感: {{ impression }}</span>
<span class="tag-item">细节体验: {{ detail }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 操作按钮 -->
<div class="action-buttons" v-if="isEvaluate == '0'">
<el-button type="primary" @click="$emit('confirm-results')" :disabled="isSure === '1'">
{{ isSure === '1' ? '已确认' : '确认' }}
</el-button>
</div>
</template>
</div>
</template>
<script>
export default {
name: "ImageResults",
props: {
uploadInfo: {
type: String,
default: ''
},
imageResults: {
type: Array,
default: () => []
},
groupedImageResults: {
type: Array,
default: () => []
},
selectedImages: {
type: Object,
default: () => ({})
},
isEvaluate: {
type: String,
default: '0'
},
isSure: {
type: String,
default: ''
}
},
methods: {
isGroupImageSelected(groupIndex, imageIndex) {
//
const key = `${groupIndex}-${imageIndex}`;
return !!this.selectedImages[key];
},
handleGroupImageClick(groupIndex, imageIndex, groupIsSure) {
//
if (groupIsSure === '1') {
this.$message.warning('该组已确认,无法更改选择');
return;
}
const key = `${groupIndex}-${imageIndex}`;
this.$emit('hand-click', {groupIndex, imageIndex, key});
},
handleImageClick(index, isSure) {
//
if (isSure === '1') {
this.$message.warning('已确认,无法更改选择');
return;
}
this.$emit('hand-click', {index, isSingle: true});
},
confirmGroup(groupIndex,id) {
console.log('confirmGroup', id)
//
this.$emit('confirm-results', id);
}
}
}
</script>
<style scoped>
.upload-info {
display: flex;
align-items: center;
margin-bottom: 20px;
font-size: 16px;
color: #54646C;
font-weight: 600;
}
.icon-photography {
width: 47px;
height: 47px;
margin-right: 8px;
vertical-align: middle;
}
.image-results-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 15px;
margin-bottom: 30px;
}
.image-item {
position: relative;
width: 100%;
height: 260px;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.image-item-evaluate {
position: relative;
width: 100%;
height: 360px;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.result-image {
width: 100%;
height: 100%;
object-fit: contain;
}
.result-image-evaluate {
width: 100%;
height: 260px;
object-fit: contain;
}
.icon-hand {
position: absolute;
top: 25px;
right: 25px;
width: 30px;
height: 30px;
cursor: pointer;
border-radius: 50%;
z-index: 1;
}
.icon-hand:hover {
opacity: 0.8;
}
.action-buttons {
text-align: right;
margin-bottom: 20px;
}
.card-content {
width: 100%;
display: flex;
flex-direction: column;
font-size: 14px;
}
.card-footer {
display: flex;
flex-direction: column;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 100%;
min-height: 24px;
}
.person-info {
display: flex;
justify-content: space-between;
margin: 10px 18px 0 18px;
align-items: center;
}
.name {
font-weight: bold;
color: #333;
}
.date {
color: #666;
/*font-size: 12px;*/
}
.tags {
display: flex;
flex-direction: column;
margin: 10px 18px 0 18px;
gap: 6px;
}
.tag-row {
display: table;
width: 100%;
table-layout: fixed;
margin:3px 0 0 0;
}
.tag-item {
display: table-cell;
width: 33.33%;
text-align: left;
font-size: 12px;
color: #333;
white-space: nowrap;
padding: 0 4px;
}
.action-buttons {
display: flex;
justify-content: flex-end;
margin: 10px 18px 10px 18px;
}
</style>