This commit is contained in:
BianLzhaoMin 2025-11-26 16:11:54 +08:00
parent 651342597b
commit 88c29b9dae
5 changed files with 487 additions and 341 deletions

View File

@ -23,67 +23,93 @@
@toggle-all-tags="$emit('toggle-all-tags')" @toggle-all-tags="$emit('toggle-all-tags')"
/> />
<!-- 输入备注 -->
<el-input
:rows="1"
placeholder="请输入备注"
type="textarea"
v-model="remarkInfo"
/>
<!-- 图片上传区域 --> <!-- 图片上传区域 -->
<FileUploader <FileUploader
:file-list="fileList" :file-list="fileList"
:selected-tag="selectedTag" :selected-tag="selectedTag"
@file-change="(file, fileList) => $emit('file-change', file, fileList)" @file-change="
(file, fileList) => $emit('file-change', file, fileList)
"
@remove-image="(index) => $emit('remove-image', index)" @remove-image="(index) => $emit('remove-image', index)"
@start-upload="$emit('start-upload')" @start-upload="$emit('start-upload', remarkInfo)"
/> />
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import ImageResults from './ImageResultsUn.vue'; import ImageResults from './ImageResultsUn.vue'
import TagSelector from './TagSelector.vue'; import TagSelector from './TagSelector.vue'
import FileUploader from './FileUploaderUn.vue'; import FileUploader from './FileUploaderUn.vue'
import axios from 'axios' import axios from 'axios'
import { getToken } from '@/utils/auth' import { getToken } from '@/utils/auth'
export default { export default {
name: "HistoryView", name: 'HistoryView',
components: { components: {
ImageResults, ImageResults,
TagSelector, TagSelector,
FileUploader FileUploader,
},
data() {
return {
remarkInfo: '',
}
}, },
props: { props: {
uploadInfo: { uploadInfo: {
type: String, type: String,
default: '' default: '',
}, },
imageResults: { imageResults: {
type: Array, type: Array,
default: () => [] default: () => [],
}, },
selectedImages: { selectedImages: {
type: Object, type: Object,
default: () => ({}) default: () => ({}),
}, },
tags: { tags: {
type: Array, type: Array,
default: () => [] default: () => [],
}, },
visibleTags: { visibleTags: {
type: Array, type: Array,
default: () => [] default: () => [],
}, },
selectedTag: { selectedTag: {
type: Array, type: Array,
default: () => [] default: () => [],
}, },
fileList: { fileList: {
type: Array, type: Array,
default: () => [] default: () => [],
} },
}, },
methods: { methods: {
handleHandClickFromChild(result, index) { handleHandClickFromChild(result, index) {
this.$emit('hand-click', result, index); this.$emit('hand-click', result, index)
} },
},
watch: {
imageResults: {
handler(newVal) {
if (newVal.length > 0) {
this.remarkInfo = newVal[0].remark
} }
},
immediate: true,
},
},
} }
</script> </script>

View File

@ -44,7 +44,7 @@
" "
:src="result.url" :src="result.url"
fit="contain" fit="contain"
@click="onClickImage(result)" @click="onClickImage(result, group.isSure)"
> >
<div slot="error" class="image-slot"> <div slot="error" class="image-slot">
<i class="el-icon-picture-outline"></i> <i class="el-icon-picture-outline"></i>
@ -248,7 +248,7 @@
:before-close="handleCloseImageDialog" :before-close="handleCloseImageDialog"
> >
<template #title> <template #title>
<span>图片标注</span> <span>{{ isSure == 1 ? '图片查看' : '图片标注' }}</span>
</template> </template>
<div v-if="currentImage" class="annotation-dialog"> <div v-if="currentImage" class="annotation-dialog">
<div class="annotation-preview"> <div class="annotation-preview">
@ -309,14 +309,14 @@
</div> </div>
<div class="annotation-buttons"> <div class="annotation-buttons">
<el-button <el-button
v-if="!isReAnnotating" v-if="!isReAnnotating && isSure != 1"
type="primary" type="primary"
size="small" size="small"
@click="startReAnnotation" @click="startReAnnotation"
> >
重新标注 重新标注
</el-button> </el-button>
<template v-else> <template v-if="isReAnnotating && isSure != 1">
<el-button size="small" @click="cancelReAnnotation"> <el-button size="small" @click="cancelReAnnotation">
取消标注 取消标注
</el-button> </el-button>
@ -445,6 +445,7 @@ export default {
], ],
imgSrc: '', imgSrc: '',
isSure: null,
} }
}, },
created() { created() {
@ -485,11 +486,12 @@ export default {
}, },
// //
onClickImage(image) { onClickImage(image, isSure = null) {
console.log('image', image) console.log('image', image)
if (!image || !image.url) { if (!image || !image.url) {
return return
} }
this.isSure = isSure
this.currentImage = image this.currentImage = image
this.imgSrc = image.url this.imgSrc = image.url
this.imageDialogVisible = true this.imageDialogVisible = true
@ -506,6 +508,10 @@ export default {
}, },
startReAnnotation() { startReAnnotation() {
if (this.isSure == 1) {
this.$message.warning('该图片已确认,无法重新标注')
return
}
this.isReAnnotating = true this.isReAnnotating = true
this.annotationCoords = null this.annotationCoords = null
this.annotationType = '' this.annotationType = ''
@ -779,6 +785,28 @@ export default {
finalCanvas.height = this.originalSize.height finalCanvas.height = this.originalSize.height
const finalCtx = finalCanvas.getContext('2d') const finalCtx = finalCanvas.getContext('2d')
//
// 使
const avgSize =
(this.originalSize.width + this.originalSize.height) / 2
const baseSize = 1000 // 1000px
// 使
// 0.8 - 3.0
const scaleFactor = Math.max(
0.8,
Math.min(3.0, Math.sqrt(avgSize / baseSize)),
)
// 线
const baseLineWidth = 1.5
const lineWidth = baseLineWidth * scaleFactor
const baseFontSize = 12
const fontSize = baseFontSize * scaleFactor
const baseMargin = 2
const margin = baseMargin * scaleFactor
const basePadding = 2
const padding = basePadding * scaleFactor
// //
const img = new Image() const img = new Image()
img.crossOrigin = 'Anonymous' img.crossOrigin = 'Anonymous'
@ -795,15 +823,50 @@ export default {
// //
this.annotations.forEach((annotation) => { this.annotations.forEach((annotation) => {
const { coords } = annotation const { coords, typeName } = annotation
//
finalCtx.strokeStyle = '#03FB02' finalCtx.strokeStyle = '#03FB02'
finalCtx.lineWidth = 1.5 finalCtx.lineWidth = lineWidth
finalCtx.strokeRect( finalCtx.strokeRect(
coords.x1, coords.x1,
coords.y1, coords.y1,
coords.x2 - coords.x1, coords.x2 - coords.x1,
coords.y2 - coords.y1, coords.y2 - coords.y1,
) )
//
if (typeName) {
//
finalCtx.font = `${fontSize}px Arial`
finalCtx.textBaseline = 'top'
//
const textMetrics =
finalCtx.measureText(typeName)
const textWidth = textMetrics.width
const textHeight = fontSize
// + margin
const labelX = coords.x1 + margin
const labelY = coords.y1 + margin
//
finalCtx.fillStyle = '#01CB04'
finalCtx.fillRect(
labelX,
labelY,
textWidth + padding * 2,
textHeight + padding * 2,
)
//
finalCtx.fillStyle = '#ffffff'
finalCtx.fillText(
typeName,
labelX + padding,
labelY + padding,
)
}
}) })
resolve() resolve()

View File

@ -265,6 +265,8 @@ export default {
isSure: record.isSure, isSure: record.isSure,
operId: record.operaId, operId: record.operaId,
id: record.id, id: record.id,
isSure: record.isSure,
remark: record.remark,
}), }),
) )
// - isActive // - isActive
@ -528,6 +530,7 @@ export default {
operId: record.operaId, operId: record.operaId,
id: record.id, id: record.id,
remark: record.remark, remark: record.remark,
isSure: record.isSure,
}), }),
) )

View File

@ -1,6 +1,11 @@
<template> <template>
<div class="app-container"> <div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" > <el-form
:model="queryParams"
ref="queryForm"
size="small"
:inline="true"
>
<el-form-item prop="date"> <el-form-item prop="date">
<el-date-picker <el-date-picker
v-model="queryParams.operaTime" v-model="queryParams.operaTime"
@ -10,10 +15,37 @@
</el-date-picker> </el-date-picker>
</el-form-item> </el-form-item>
<el-form-item prop="remark">
<el-input
v-model="queryParams.remark"
placeholder="请输入备注"
clearable
style="width: 240px"
/>
</el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> <el-button
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery" style="background:transparent;border-color: #B1C0CB;">重置</el-button> type="primary"
<el-button icon="el-icon-download" size="mini" @click="handleExport" style="background:transparent;border-color: #B1C0CB;">下载</el-button> icon="el-icon-search"
size="mini"
@click="handleQuery"
>搜索</el-button
>
<el-button
icon="el-icon-refresh"
size="mini"
@click="resetQuery"
style="background: transparent; border-color: #b1c0cb"
>重置</el-button
>
<el-button
icon="el-icon-download"
size="mini"
@click="handleExport"
style="background: transparent; border-color: #b1c0cb"
>下载</el-button
>
</el-form-item> </el-form-item>
</el-form> </el-form>
@ -38,10 +70,16 @@
<div class="card-content"> <div class="card-content">
<div class="card-footer"> <div class="card-footer">
<div class="person-info"> <div class="person-info">
<span class="name">综合得分:{{ item.overallScore }}</span> <span class="name"
>综合得分:{{ item.overallScore }}</span
>
<span class="name">{{ item.dictValue }}</span> <span class="name">{{ item.dictValue }}</span>
<span class="date">{{ item.operaName }}</span> <span class="date">{{ item.operaName }}</span>
</div> </div>
<div class="person-info">
<span class="name">备注:{{ item.remark }}</span>
</div>
<!-- <div class="tags"> <!-- <div class="tags">
<div class="tag-row"> <div class="tag-row">
<span class="tag-item" style="padding-left: 16px;">清晰度: {{ item.clarity }}</span> <span class="tag-item" style="padding-left: 16px;">清晰度: {{ item.clarity }}</span>
@ -54,7 +92,12 @@
<span class="tag-item">细节体验: {{ item.detail }}</span> <span class="tag-item">细节体验: {{ item.detail }}</span>
</div> </div>
</div>--> </div>-->
<div style="border-top: 1px solid #CFD4D7;margin-top: 10px"></div> <div
style="
border-top: 1px solid #cfd4d7;
margin-top: 10px;
"
></div>
<div class="action-buttons"> <div class="action-buttons">
<el-button <el-button
type="danger" type="danger"
@ -79,15 +122,18 @@
:page-sizes="[8, 16, 32, 64]" :page-sizes="[8, 16, 32, 64]"
@pagination="getList" @pagination="getList"
/> />
</div> </div>
</template> </template>
<script> <script>
import {deleteFile, exportImages, getAllImagelist} from "@/api/imageCaptioning/imageLibrary"; import {
deleteFile,
exportImages,
getAllImagelist,
} from '@/api/imageCaptioning/imageLibrary'
export default { export default {
name: "imageCaptioningLibrary", name: 'imageCaptioningLibrary',
data() { data() {
return { return {
// //
@ -98,7 +144,8 @@ export default {
pageSize: 8, pageSize: 8,
date: '', date: '',
operaType: 2, operaType: 2,
operaTime:'' operaTime: '',
remark: '',
}, },
// //
form: {}, form: {},
@ -108,13 +155,12 @@ export default {
} }
}, },
created() { created() {
this.getList(); this.getList()
}, },
mounted() { mounted() {
this.getItemWidth() this.getItemWidth()
}, },
methods: { methods: {
// //
getItemWidth() { getItemWidth() {
@ -122,11 +168,11 @@ export default {
}, },
/** 查询角色列表 */ /** 查询角色列表 */
getList() { getList() {
getAllImagelist(this.queryParams).then(response => { getAllImagelist(this.queryParams).then((response) => {
this.proMaterialsListAll = response.rows; this.proMaterialsListAll = response.rows
this.total = response.total || 0; this.total = response.total || 0
console.log(this.proMaterialsListAll) console.log(this.proMaterialsListAll)
this.showProMaterialsDuctList = this.proMaterialsListAll; this.showProMaterialsDuctList = this.proMaterialsListAll
}) })
}, },
/** 搜索按钮操作 */ /** 搜索按钮操作 */
@ -145,7 +191,7 @@ export default {
}, },
/** 重置按钮操作 */ /** 重置按钮操作 */
resetQuery() { resetQuery() {
this.resetForm("queryForm") this.resetForm('queryForm')
// null // null
this.queryParams.operaTime = null this.queryParams.operaTime = null
this.handleQuery() this.handleQuery()
@ -153,22 +199,26 @@ export default {
/** 导出按钮操作 */ /** 导出按钮操作 */
handleExport() { handleExport() {
const imageIds = this.showProMaterialsDuctList.map(item => item.imageId); const imageIds = this.showProMaterialsDuctList.map(
(item) => item.imageId,
)
exportImages({ exportImages({
imageId: imageIds imageId: imageIds,
}).then(response => { })
.then((response) => {
// //
const blob = new Blob([response]); const blob = new Blob([response])
const link = document.createElement('a'); const link = document.createElement('a')
const fileName = `图片压缩包_${new Date().getTime()}.zip`; const fileName = `图片压缩包_${new Date().getTime()}.zip`
link.href = URL.createObjectURL(blob); link.href = URL.createObjectURL(blob)
link.download = fileName; link.download = fileName
link.click(); link.click()
URL.revokeObjectURL(link.href); URL.revokeObjectURL(link.href)
}).catch(error => { })
console.error('下载失败:', error); .catch((error) => {
this.$message.error('下载失败'); console.error('下载失败:', error)
}); this.$message.error('下载失败')
})
}, },
// //
@ -176,10 +226,11 @@ export default {
this.$confirm('确认要删除该记录吗?', '提示', { this.$confirm('确认要删除该记录吗?', '提示', {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning' type: 'warning',
}).then(() => { })
.then(() => {
// //
deleteFile({"imageId":item.imageId}).then(response => { deleteFile({ imageId: item.imageId }).then((response) => {
if (response.code === 200) { if (response.code === 200) {
this.$message.success('删除成功') this.$message.success('删除成功')
this.getList() this.getList()
@ -187,25 +238,25 @@ export default {
this.$message.error('删除失败') this.$message.error('删除失败')
} }
}) })
}).catch(() => { })
.catch(() => {
this.$message.info('已取消删除') this.$message.info('已取消删除')
}) })
} },
} },
} }
</script> </script>
<style scoped> <style scoped>
.app-container { .app-container {
overflow: hidden; overflow: hidden;
height: calc(100vh - 84px); height: calc(100vh - 84px);
width: 100%; width: 100%;
background: url('../../../assets/images/imageCaptioning/login-bg-background.png') no-repeat center center; background: url('../../../assets/images/imageCaptioning/login-bg-background.png')
no-repeat center center;
background-size: 100% 100%; background-size: 100% 100%;
} }
.content-area { .content-area {
flex: 1; flex: 1;
height: calc(100vh - 218px); height: calc(100vh - 218px);
@ -310,10 +361,10 @@ export default {
margin: 10px 18px 0px 18px; margin: 10px 18px 0px 18px;
} }
>>>.el-input__inner{ .el-input__inner {
background: transparent !important; background: transparent !important;
color: #333333 !important; color: #333333 !important;
border-color: #B5C4CC; border-color: #b5c4cc;
} }
.pagination-container { .pagination-container {

View File

@ -87,6 +87,7 @@ export default {
selectedImages: {}, // selectedImages: {}, //
isSidebarVisible: true, // / isSidebarVisible: true, // /
addId: '', addId: '',
remarkInfo: '',
} }
}, },
@ -123,7 +124,7 @@ export default {
this.addId = '' this.addId = ''
}, },
startUpload() { startUpload(remarkInfo = '') {
console.log('开始上传') console.log('开始上传')
if (this.fileList.length === 0) { if (this.fileList.length === 0) {
@ -156,7 +157,7 @@ export default {
}) })
const params = { const params = {
id: this.addId, id: this.addId,
remark: this.remarkInfo, remark: remarkInfo || this.remarkInfo,
} }
formData.append('params', JSON.stringify(params)) formData.append('params', JSON.stringify(params))
// formData.append("id", this.addId) // formData.append("id", this.addId)
@ -190,6 +191,7 @@ export default {
balance: item.balance, balance: item.balance,
detail: item.detail, detail: item.detail,
dictValue: item.dictValue, dictValue: item.dictValue,
remark: record.remark,
}), }),
) )
allImageResults = allImageResults =
@ -316,6 +318,7 @@ export default {
balance: item.balance, balance: item.balance,
detail: item.detail, detail: item.detail,
dictValue: item.dictValue, dictValue: item.dictValue,
remark: record.remark,
}), }),
) )
allImageResults = allImageResults.concat(fileResults) allImageResults = allImageResults.concat(fileResults)