bug修改
This commit is contained in:
parent
58ace303d2
commit
2ff53cae29
|
|
@ -75,3 +75,12 @@ export function getSampleImageListAPI(params) {
|
||||||
params
|
params
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 数据管理->样本库管理->获取样本图片列表
|
||||||
|
export function getSampleListByVersionIdAPI(params) {
|
||||||
|
return request({
|
||||||
|
url: '/smartPlatform/data/sample/getSampleListByVersionId',
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -447,6 +447,7 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
// 核心:提交审核结果,适配分类标注的审批参数
|
// 核心:提交审核结果,适配分类标注的审批参数
|
||||||
|
// 核心:提交审核结果,适配分类标注的审批参数
|
||||||
async submitAuditResult(auditStatus) {
|
async submitAuditResult(auditStatus) {
|
||||||
if (this.isAuditing) return
|
if (this.isAuditing) return
|
||||||
if (!this.currentImage.id) {
|
if (!this.currentImage.id) {
|
||||||
|
|
@ -474,9 +475,17 @@ export default {
|
||||||
|
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
this.$message.success(auditStatus === 1 ? '审核通过提交成功!' : '审核不通过提交成功!')
|
this.$message.success(auditStatus === 1 ? '审核通过提交成功!' : '审核不通过提交成功!')
|
||||||
|
// ===== 新增核心代码开始 =====
|
||||||
|
await this.loadAuditImages(); // 刷新审批图片列表(等待加载完成)
|
||||||
|
// 刷新后重置索引:取原索引和新列表最大索引的最小值,防止越界
|
||||||
|
this.currentImageIndex = Math.min(this.currentImageIndex, this.imageList.length - 1);
|
||||||
|
// 若刷新后还有图片,重新赋值当前图片并重置视图;无图片则置空
|
||||||
|
if (this.imageList.length === 0) {
|
||||||
|
this.currentImage = {};
|
||||||
|
this.$message.info('暂无剩余审批图片数据');
|
||||||
|
}
|
||||||
// 自动切换到下一张(如果有),提升审批效率
|
// 自动切换到下一张(如果有),提升审批效率
|
||||||
if (this.currentImageIndex < this.imageList.length - 1) {
|
if (this.imageList.length > 0 && this.currentImageIndex < this.imageList.length - 1) {
|
||||||
this.nextImage()
|
this.nextImage()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -498,7 +507,7 @@ export default {
|
||||||
// 全局容器:适配审批页面的布局
|
// 全局容器:适配审批页面的布局
|
||||||
.labeling-page-container {
|
.labeling-page-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 92vh;
|
height: 90.5vh;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@ export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
labelingTaskId: this.convertToNumber(decryptWithSM4(this.$route.query.labelingTaskId || '')),
|
labelingTaskId: this.convertToNumber(decryptWithSM4(this.$route.query.labelingTaskId || '')),
|
||||||
|
labelingTemplate: decryptWithSM4(this.$route.query.labelingTemplate || ''),
|
||||||
// 表格配置
|
// 表格配置
|
||||||
formLabel,
|
formLabel,
|
||||||
columnsList,
|
columnsList,
|
||||||
|
|
@ -107,12 +108,29 @@ export default {
|
||||||
// 查看按钮点击事件(支持选中行/批量查看)
|
// 查看按钮点击事件(支持选中行/批量查看)
|
||||||
handleView() {
|
handleView() {
|
||||||
const labelingTaskIdStr = String(this.labelingTaskId)
|
const labelingTaskIdStr = String(this.labelingTaskId)
|
||||||
|
console.log('123123123131',this.labelingTemplate)
|
||||||
|
if (this.labelingTemplate === '1' || this.labelingTemplate === '2') {
|
||||||
|
this.$router.push({
|
||||||
|
name: 'ClassificationDetail',
|
||||||
|
query: {
|
||||||
|
labelingTaskId: encryptWithSM4(labelingTaskIdStr),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (this.labelingTemplate === '3' || this.labelingTemplate === '4' || this.labelingTemplate === '5') {
|
||||||
this.$router.push({
|
this.$router.push({
|
||||||
name: 'LabelingDetail',
|
name: 'LabelingDetail',
|
||||||
query: {
|
query: {
|
||||||
labelingTaskId : encryptWithSM4(labelingTaskIdStr)
|
labelingTaskId: encryptWithSM4(labelingTaskIdStr),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} else if (this.labelingTemplate === '8' || this.labelingTemplate === '9') {
|
||||||
|
this.$router.push({
|
||||||
|
name: 'OcrLabelingDetail',
|
||||||
|
query: {
|
||||||
|
labelingTaskId: encryptWithSM4(labelingTaskIdStr),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -139,12 +139,13 @@ export default {
|
||||||
methods: {
|
methods: {
|
||||||
// 进度列点击跳转处理
|
// 进度列点击跳转处理
|
||||||
handleProgressClick(data) {
|
handleProgressClick(data) {
|
||||||
|
console.log('data',data)
|
||||||
const labelingTaskIdStr = String(data.labelingTaskId)
|
const labelingTaskIdStr = String(data.labelingTaskId)
|
||||||
this.$router.push({
|
this.$router.push({
|
||||||
name: 'auditList',
|
name: 'auditList',
|
||||||
query: {
|
query: {
|
||||||
labelingTaskId : encryptWithSM4(labelingTaskIdStr)
|
labelingTaskId : encryptWithSM4(labelingTaskIdStr),
|
||||||
|
labelingTemplate: encryptWithSM4(data.labelingTemplateValue)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
width="1200px"
|
width="1200px"
|
||||||
:close-on-click-modal="false"
|
:close-on-click-modal="false"
|
||||||
@close="handleClose"
|
@close="handleClose"
|
||||||
|
class="data-select-dialog"
|
||||||
>
|
>
|
||||||
<div class="data-select-container">
|
<div class="data-select-container">
|
||||||
<!-- 左侧三层样本树 -->
|
<!-- 左侧三层样本树 -->
|
||||||
|
|
@ -738,6 +739,7 @@ export default {
|
||||||
this.fullScreenImageName = item.fileName || item.label || '样本图片';
|
this.fullScreenImageName = item.fileName || item.label || '样本图片';
|
||||||
this.fullScreenImageVisible = true;
|
this.fullScreenImageVisible = true;
|
||||||
document.body.style.overflow = 'hidden';
|
document.body.style.overflow = 'hidden';
|
||||||
|
document.body.style.paddingRight = '17px';
|
||||||
},
|
},
|
||||||
|
|
||||||
/** 关闭全屏预览 */
|
/** 关闭全屏预览 */
|
||||||
|
|
@ -746,6 +748,7 @@ export default {
|
||||||
this.fullScreenImageUrl = "";
|
this.fullScreenImageUrl = "";
|
||||||
this.fullScreenImageName = "";
|
this.fullScreenImageName = "";
|
||||||
document.body.style.overflow = 'auto';
|
document.body.style.overflow = 'auto';
|
||||||
|
document.body.style.paddingRight = '0';
|
||||||
},
|
},
|
||||||
|
|
||||||
/** 页码大小变化 */
|
/** 页码大小变化 */
|
||||||
|
|
@ -799,21 +802,41 @@ export default {
|
||||||
.data-select-root {
|
.data-select-root {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
overflow: hidden; /* 兜底:禁用根容器滚动 */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 主容器 */
|
// 关键:穿透控制el-dialog的所有默认滚动,全局唯一不会污染其他dialog
|
||||||
|
::v-deep .data-select-dialog {
|
||||||
|
.el-dialog__body {
|
||||||
|
padding: 15px 20px !important; /* 微调内边距,减少高度浪费 */
|
||||||
|
height: 700px !important; /* 固定dialog内容区高度,包含标题/内容/按钮 */
|
||||||
|
overflow: hidden !important; /* 彻底禁用dialog默认的内容区滚动,核心修复 */
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 主容器 - 核心:移除外层滚动,弹性占满dialog内容区 */
|
||||||
.data-select-container {
|
.data-select-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
height: 600px;
|
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding-bottom: 10px;
|
padding-bottom: 0; /* 修复1:删掉主容器底部内边距,避免间距放大双横线 */
|
||||||
|
overflow: hidden; /* 禁用自身滚动 */
|
||||||
|
flex: 1; /* 弹性占满dialog body的剩余高度,抵消footer和内边距 */
|
||||||
|
height: 100%; /* 兜底高度 */
|
||||||
|
}
|
||||||
|
|
||||||
.sample-tree-panel {
|
// 左侧树面板 - 独立滚动,高度100%
|
||||||
|
.sample-tree-panel {
|
||||||
width: 250px;
|
width: 250px;
|
||||||
border-right: 1px solid #ebeef5;
|
border-right: 1px solid #ebeef5;
|
||||||
padding-right: 10px;
|
padding-right: 10px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
height: 100%; /* 占满主容器高度 */
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column; /* 弹性列布局,标题固定+树滚动 */
|
||||||
|
overflow: hidden; /* 禁用面板自身滚动 */
|
||||||
|
|
||||||
.panel-title {
|
.panel-title {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
|
@ -822,6 +845,7 @@ export default {
|
||||||
margin: 0 0 10px 0;
|
margin: 0 0 10px 0;
|
||||||
padding-bottom: 8px;
|
padding-bottom: 8px;
|
||||||
border-bottom: 1px solid #ebeef5;
|
border-bottom: 1px solid #ebeef5;
|
||||||
|
flex-shrink: 0; /* 标题不收缩,固定高度 */
|
||||||
}
|
}
|
||||||
|
|
||||||
.tree-empty-tip {
|
.tree-empty-tip {
|
||||||
|
|
@ -829,11 +853,17 @@ export default {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
color: #999;
|
color: #999;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
flex: 1; /* 占满剩余高度,居中显示 */
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
::v-deep .el-tree {
|
::v-deep .el-tree {
|
||||||
height: calc(100% - 30px);
|
flex: 1; /* 弹性占满剩余高度 */
|
||||||
overflow-y: auto;
|
height: 100%;
|
||||||
|
overflow-y: auto; /* 仅树组件自身滚动,唯一滚动点 */
|
||||||
|
overflow-x: hidden; /* 禁用横向滚动 */
|
||||||
.tree-tooltip {
|
.tree-tooltip {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
@ -841,7 +871,7 @@ export default {
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
// 禁用节点样式(关键:覆盖Element UI默认样式)
|
// 禁用节点样式
|
||||||
.el-tree-node.is-disabled {
|
.el-tree-node.is-disabled {
|
||||||
color: #ccc !important;
|
color: #ccc !important;
|
||||||
.el-checkbox {
|
.el-checkbox {
|
||||||
|
|
@ -858,19 +888,22 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 确保禁用节点的复选框不可点击
|
|
||||||
.el-tree-node.is-disabled .el-checkbox {
|
.el-tree-node.is-disabled .el-checkbox {
|
||||||
display: inline-block !important;
|
display: inline-block !important;
|
||||||
cursor: not-allowed !important;
|
cursor: not-allowed !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.sample-image-panel {
|
// 右侧图片面板 - 独立滚动,高度100%
|
||||||
flex: 1;
|
.sample-image-panel {
|
||||||
overflow-y: auto;
|
flex: 1; /* 占满剩余宽度 */
|
||||||
|
height: 100%; /* 占满主容器高度 */
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding-bottom: 10px;
|
padding-bottom: 0; /* 修复2:删掉面板底部内边距,避免冗余间距 */
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column; /* 弹性列布局:头部+图片滚动+分页固定 */
|
||||||
|
overflow: hidden; /* 禁用面板自身滚动,核心 */
|
||||||
|
|
||||||
.select-header {
|
.select-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -879,18 +912,23 @@ export default {
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
padding-bottom: 8px;
|
padding-bottom: 8px;
|
||||||
border-bottom: 1px solid #ebeef5;
|
border-bottom: 1px solid #ebeef5;
|
||||||
|
flex-shrink: 0; /* 头部固定,不收缩 */
|
||||||
|
}
|
||||||
|
|
||||||
.select-count {
|
.select-count {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: #666;
|
color: #666;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.image-empty-tip {
|
.image-empty-tip {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 50px;
|
padding: 50px;
|
||||||
color: #999;
|
color: #999;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
flex: 1; /* 占满剩余高度,居中 */
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.image-grid {
|
.image-grid {
|
||||||
|
|
@ -898,6 +936,12 @@ export default {
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 15px;
|
gap: 15px;
|
||||||
min-height: 100px;
|
min-height: 100px;
|
||||||
|
flex: 1; /* 弹性占满头部和分页之间的所有高度 */
|
||||||
|
overflow-y: auto; /* 仅图片网格自身滚动,唯一滚动点 */
|
||||||
|
overflow-x: hidden; /* 禁用横向滚动 */
|
||||||
|
padding-right: 6px; /* 为滚动条预留空间,避免内容遮挡 */
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
.image-item {
|
.image-item {
|
||||||
width: calc(33.33% - 10px);
|
width: calc(33.33% - 10px);
|
||||||
|
|
@ -960,13 +1004,25 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
// 分页组件固定在面板底部,不滚动
|
||||||
|
::v-deep .el-pagination {
|
||||||
|
flex-shrink: 0; /* 分页固定,不收缩 */
|
||||||
|
margin-top: 15px !important;
|
||||||
|
text-align: right;
|
||||||
|
// 移除分页自身的任何边框,避免冗余
|
||||||
|
border: none !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 弹窗底部按钮区 - 核心:只保留这一条顶部边框
|
||||||
.dialog-footer {
|
.dialog-footer {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
|
flex-shrink: 0; /* 按钮区固定,不收缩 */
|
||||||
|
border-top: 1px solid #ebeef5; /* 唯一保留的分隔线 */
|
||||||
|
margin-top: 10px; /* 微调间距,让分隔线和分页的距离更美观 */
|
||||||
|
border-bottom: none !important; /* 兜底:确保无底部边框 */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 全屏预览样式 */
|
/* 全屏预览样式 */
|
||||||
|
|
@ -983,6 +1039,7 @@ export default {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
overflow: hidden; /* 禁用预览弹窗滚动 */
|
||||||
}
|
}
|
||||||
|
|
||||||
.full-screen-image-content {
|
.full-screen-image-content {
|
||||||
|
|
|
||||||
|
|
@ -37,9 +37,9 @@
|
||||||
type="text"
|
type="text"
|
||||||
v-hasPermi="['data:dataset:version:add']"
|
v-hasPermi="['data:dataset:version:add']"
|
||||||
class="action-btn version-add-btn"
|
class="action-btn version-add-btn"
|
||||||
@click="handleVersionAdd(data)"
|
@click="handleExport(data)"
|
||||||
>
|
>
|
||||||
新增版本
|
导出
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
type="text"
|
type="text"
|
||||||
|
|
@ -60,6 +60,7 @@ import TableModel from '@/components/TableModel2'
|
||||||
import { columnsList, formLabel } from './config'
|
import { columnsList, formLabel } from './config'
|
||||||
import { datasetVersionListAPI, delDatasetVersionDataAPI } from '@/api/data/dataset'
|
import { datasetVersionListAPI, delDatasetVersionDataAPI } from '@/api/data/dataset'
|
||||||
import { decryptWithSM4, encryptWithSM4 } from '@/utils/sm'
|
import { decryptWithSM4, encryptWithSM4 } from '@/utils/sm'
|
||||||
|
import {exportImagesAPI} from "@/api/data/labeling";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'DatasetVersionList', // 修正组件名,避免冲突
|
name: 'DatasetVersionList', // 修正组件名,避免冲突
|
||||||
|
|
@ -181,8 +182,41 @@ export default {
|
||||||
this.queryVersionList()
|
this.queryVersionList()
|
||||||
},
|
},
|
||||||
|
|
||||||
handleExport(row) {
|
/** 导出操作 */
|
||||||
this.$message.info(`正在导出数据集:${row.datasetName}`)
|
async handleExport(row) {
|
||||||
|
const datasetVersionId = row.datasetVersionId
|
||||||
|
// 根据选择的格式处理导出
|
||||||
|
try {
|
||||||
|
let blob, fileName;
|
||||||
|
blob = await exportImagesAPI({ datasetVersionId });
|
||||||
|
fileName = `数据集图片.zip`;
|
||||||
|
// 处理文件下载
|
||||||
|
this.downloadFile(blob, fileName);
|
||||||
|
this.$message.success('导出成功,正在下载...');
|
||||||
|
} catch (error) {
|
||||||
|
// 异常处理(兼容blob格式的错误信息)
|
||||||
|
console.error('导出失败:', error);
|
||||||
|
if (error.response?.data instanceof Blob) {
|
||||||
|
const text = await new Response(error.response.data).text();
|
||||||
|
const res = JSON.parse(text);
|
||||||
|
this.$message.error(res.msg || '导出失败,无可用数据');
|
||||||
|
} else {
|
||||||
|
this.$message.error(error.msg || '导出失败,请稍后重试');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
downloadFile(blob, fileName) {
|
||||||
|
// 创建下载链接
|
||||||
|
const url = window.URL.createObjectURL(new Blob([blob]));
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = url;
|
||||||
|
a.download = fileName;
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
// 释放URL对象,清理临时标签
|
||||||
|
window.URL.revokeObjectURL(url);
|
||||||
|
document.body.removeChild(a);
|
||||||
},
|
},
|
||||||
|
|
||||||
handleQuery() {
|
handleQuery() {
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,11 @@ export default {
|
||||||
watch: {
|
watch: {
|
||||||
isAdd: {
|
isAdd: {
|
||||||
handler(newVal) {
|
handler(newVal) {
|
||||||
|
console.log('newVal',newVal)
|
||||||
|
console.log('123',CONSTANT_PARAMS.type)
|
||||||
|
|
||||||
if (newVal === CONSTANT_PARAMS.type) {
|
if (newVal === CONSTANT_PARAMS.type) {
|
||||||
|
console.log('1231')
|
||||||
this.initFormData();
|
this.initFormData();
|
||||||
}else{
|
}else{
|
||||||
this.form.labelGroupName = this.rowData.labelGroupName
|
this.form.labelGroupName = this.rowData.labelGroupName
|
||||||
|
|
@ -75,6 +79,7 @@ export default {
|
||||||
methods: {
|
methods: {
|
||||||
/** 初始化表单数据 */
|
/** 初始化表单数据 */
|
||||||
initFormData() {
|
initFormData() {
|
||||||
|
console.log('初始化表单数据:', this.rowData);
|
||||||
if (this.rowData) {
|
if (this.rowData) {
|
||||||
// 编辑模式:填充表单数据
|
// 编辑模式:填充表单数据
|
||||||
this.form = {
|
this.form = {
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@
|
||||||
<script>
|
<script>
|
||||||
import TableModel from '@/components/TableModel2'
|
import TableModel from '@/components/TableModel2'
|
||||||
import { columnsList, formLabel } from './config'
|
import { columnsList, formLabel } from './config'
|
||||||
import { labelListAPI, delLabelAPI } from '@/api/data/label'
|
import { labelListAPI, delDataAPI } from '@/api/data/label'
|
||||||
import LabelForm from './LabelForm.vue'
|
import LabelForm from './LabelForm.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
@ -101,8 +101,9 @@ export default {
|
||||||
/** 修改操作 */
|
/** 修改操作 */
|
||||||
handleUpdate(row) {
|
handleUpdate(row) {
|
||||||
this.rowData = {
|
this.rowData = {
|
||||||
labelGroupName: this.activeCategoryName,
|
...row,
|
||||||
...row
|
labelGroupName: this.activeCategoryName
|
||||||
|
|
||||||
}
|
}
|
||||||
this.title = '编辑标签'
|
this.title = '编辑标签'
|
||||||
this.isAdd = 'edit'
|
this.isAdd = 'edit'
|
||||||
|
|
@ -126,7 +127,7 @@ export default {
|
||||||
dangerouslyUseHTMLString: true,
|
dangerouslyUseHTMLString: true,
|
||||||
customClass: 'delete-confirm-dialog'
|
customClass: 'delete-confirm-dialog'
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
delLabelAPI(
|
delDataAPI(
|
||||||
{
|
{
|
||||||
labelId: raw.labelId
|
labelId: raw.labelId
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,8 @@
|
||||||
<!-- 保留缩放/全屏/重置 -->
|
<!-- 保留缩放/全屏/重置 -->
|
||||||
<el-button icon="el-icon-zoom-in" @click="zoomIn" title="放大"></el-button>
|
<el-button icon="el-icon-zoom-in" @click="zoomIn" title="放大"></el-button>
|
||||||
<el-button icon="el-icon-zoom-out" @click="zoomOut" title="缩小"></el-button>
|
<el-button icon="el-icon-zoom-out" @click="zoomOut" title="缩小"></el-button>
|
||||||
<el-button icon="el-icon-full-screen" @click="toggleFullScreen" title="页面内全屏" :class="{ active: isFullScreen }"></el-button>
|
<!-- <el-button icon="el-icon-full-screen" @click="toggleFullScreen" title="页面内全屏" :class="{ active: isFullScreen }"></el-button>-->
|
||||||
<el-button icon="el-icon-refresh" @click="resetZoom" title="重置视图"></el-button>
|
<!-- <el-button icon="el-icon-refresh" @click="resetZoom" title="重置视图"></el-button>-->
|
||||||
</el-button-group>
|
</el-button-group>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -460,16 +460,16 @@ export default {
|
||||||
// 切换图片:核心改造,清空所有残留数据,移除标注相关
|
// 切换图片:核心改造,清空所有残留数据,移除标注相关
|
||||||
async switchImage(index) {
|
async switchImage(index) {
|
||||||
// 检查是否有未保存的分类
|
// 检查是否有未保存的分类
|
||||||
if (this.selectedLabelIds.length > 0 || this.isInvalidData) {
|
// if (this.selectedLabelIds.length > 0 || this.isInvalidData) {
|
||||||
try {
|
// try {
|
||||||
await this.saveLabel() // 静默保存当前分类
|
// await this.saveLabel() // 静默保存当前分类
|
||||||
} catch (err) {
|
// } catch (err) {
|
||||||
const confirm = await this.$confirm('当前图片分类未保存,是否放弃修改并切换?', '提示', {
|
// const confirm = await this.$confirm('当前图片分类未保存,是否放弃修改并切换?', '提示', {
|
||||||
type: 'warning'
|
// type: 'warning'
|
||||||
})
|
// })
|
||||||
if (!confirm) return
|
// if (!confirm) return
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
// 切换图片并**清空所有残留数据**
|
// 切换图片并**清空所有残留数据**
|
||||||
this.currentImageIndex = index
|
this.currentImageIndex = index
|
||||||
|
|
@ -536,6 +536,16 @@ export default {
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
this.$message.success('分类保存成功!');
|
this.$message.success('分类保存成功!');
|
||||||
this.saveCurrentLabelData(); // 更新本地数据
|
this.saveCurrentLabelData(); // 更新本地数据
|
||||||
|
// ===== 新增核心代码开始 =====
|
||||||
|
await this.loadTreeData(); // 刷新图片列表(等待加载完成)
|
||||||
|
// 刷新后恢复当前索引,防止越界(比如图片被移走后列表变短)
|
||||||
|
this.currentImageIndex = Math.min(this.currentImageIndex, this.imageList.length - 1);
|
||||||
|
// 重新赋值当前图片
|
||||||
|
this.currentImage = this.imageList[this.currentImageIndex] || {};
|
||||||
|
// 恢复当前图片的选中标签和无效标记,保证页面显示一致
|
||||||
|
this.selectedLabelIds = this.currentImage.selectedLabelIds || [];
|
||||||
|
this.isInvalidData = this.currentImage.isInvalid || false;
|
||||||
|
// ===== 新增核心代码结束 =====
|
||||||
return Promise.resolve(res);
|
return Promise.resolve(res);
|
||||||
} else {
|
} else {
|
||||||
const errorMsg = '保存失败:' + (res.msg || '接口返回异常');
|
const errorMsg = '保存失败:' + (res.msg || '接口返回异常');
|
||||||
|
|
|
||||||
|
|
@ -74,9 +74,9 @@
|
||||||
<div
|
<div
|
||||||
class="label-item"
|
class="label-item"
|
||||||
v-for="(label, labelIndex) in group.labels"
|
v-for="(label, labelIndex) in group.labels"
|
||||||
:key="label.id || labelIndex"
|
:key="label.labelId || labelIndex"
|
||||||
>
|
>
|
||||||
<span class="label-name">#{{ labelIndex + 1 }} {{ label.name }} </span>
|
<span class="label-name">#{{ labelIndex + 1 }} {{ label.labelName }} </span>
|
||||||
<span class="shape-count" v-if="label.shapeIds && label.shapeIds.length">
|
<span class="shape-count" v-if="label.shapeIds && label.shapeIds.length">
|
||||||
({{ label.shapeIds.length }}个标注)
|
({{ label.shapeIds.length }}个标注)
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -109,6 +109,23 @@ export default {
|
||||||
name: 'LabelingDetail',
|
name: 'LabelingDetail',
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
// 标签绘制配置:复用原标注页的核心配置
|
||||||
|
labelDrawConfig: {
|
||||||
|
baseLineWidth: 3,
|
||||||
|
minLineWidth: 1,
|
||||||
|
maxLineWidth: 6,
|
||||||
|
textFontSize: 16,
|
||||||
|
textPadding: 3,
|
||||||
|
shapeMainColor: {
|
||||||
|
rect: '#00ff00', // 矩形填充主色
|
||||||
|
polygon: '#0099ff', // 多边形填充主色
|
||||||
|
circle: '#ff9900' // 圆形填充主色
|
||||||
|
},
|
||||||
|
selectedColor: '#ff0000', // 选中时所有形状的填充/描边主色
|
||||||
|
bgOpacity: 0.4, // 文字背景透明度
|
||||||
|
shapeFillOpacity: 0.4, // 图形自身填充透明度
|
||||||
|
textColor: "#ffffff"
|
||||||
|
},
|
||||||
shapeColorMap: {
|
shapeColorMap: {
|
||||||
rect: '#00ff00', // 矩形 - 绿色
|
rect: '#00ff00', // 矩形 - 绿色
|
||||||
polygon: '#0099ff', // 多边形 - 蓝色
|
polygon: '#0099ff', // 多边形 - 蓝色
|
||||||
|
|
@ -157,6 +174,58 @@ export default {
|
||||||
return isNaN(num) ? '' : num;
|
return isNaN(num) ? '' : num;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 【核心工具方法1】16进制颜色转RGBA(支持#fff/#ffffff格式,添加透明度)
|
||||||
|
hexToRgba(hex, opacity) {
|
||||||
|
const cleanHex = hex.replace('#', '');
|
||||||
|
const fullHex = cleanHex.length === 3
|
||||||
|
? cleanHex.split('').map(c => c + c).join('')
|
||||||
|
: cleanHex;
|
||||||
|
const r = parseInt(fullHex.substring(0, 2), 16);
|
||||||
|
const g = parseInt(fullHex.substring(2, 4), 16);
|
||||||
|
const b = parseInt(fullHex.substring(4, 6), 16);
|
||||||
|
return `rgba(${r}, ${g}, ${b}, ${opacity})`;
|
||||||
|
},
|
||||||
|
|
||||||
|
// 【核心工具方法2】判断16进制颜色是否为浅色(基于亮度公式,亮度>0.5为浅色)
|
||||||
|
isLightColor(hex) {
|
||||||
|
const cleanHex = hex.replace('#', '');
|
||||||
|
const fullHex = cleanHex.length === 3
|
||||||
|
? cleanHex.split('').map(c => c + c).join('')
|
||||||
|
: cleanHex;
|
||||||
|
const r = parseInt(fullHex.substring(0, 2), 16) / 255;
|
||||||
|
const g = parseInt(fullHex.substring(2, 4), 16) / 255;
|
||||||
|
const b = parseInt(fullHex.substring(4, 6), 16) / 255;
|
||||||
|
// 国际标准相对亮度公式,判断颜色深浅最准确
|
||||||
|
const luminance = (0.299 * r + 0.587 * g + 0.114 * b);
|
||||||
|
return luminance > 0.5;
|
||||||
|
},
|
||||||
|
|
||||||
|
// 【核心工具方法3】动态计算线条宽度(适配缩放)
|
||||||
|
getDynamicLineWidth() {
|
||||||
|
const { baseLineWidth, minLineWidth, maxLineWidth } = this.labelDrawConfig
|
||||||
|
// 缩放比例越小,线条越粗(反比例),并限制最大/最小值
|
||||||
|
const dynamicWidth = baseLineWidth / this.zoomScale
|
||||||
|
return Math.max(minLineWidth, Math.min(maxLineWidth, dynamicWidth))
|
||||||
|
},
|
||||||
|
|
||||||
|
// 【核心工具方法4】动态计算标签文字字号(适配缩放)
|
||||||
|
getDynamicFontSize() {
|
||||||
|
const { textFontSize } = this.labelDrawConfig
|
||||||
|
// 文字字号反比例适配,保证缩放后文字大小视觉一致
|
||||||
|
const dynamicSize = textFontSize / this.zoomScale
|
||||||
|
return Math.max(10, Math.min(24, dynamicSize)) // 限制字号范围,避免过大/过小
|
||||||
|
},
|
||||||
|
|
||||||
|
// 【工具方法】根据标签ID查找标签详情
|
||||||
|
findLabelById(labelId) {
|
||||||
|
for (const group of this.labelGroups) {
|
||||||
|
for (const label of group.labels) {
|
||||||
|
if (label.labelId === labelId) return label
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
},
|
||||||
|
|
||||||
// 加载标签树数据(仅展示)
|
// 加载标签树数据(仅展示)
|
||||||
async loadLabelTreeData() {
|
async loadLabelTreeData() {
|
||||||
try {
|
try {
|
||||||
|
|
@ -226,7 +295,7 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 生成标注ID
|
// 生成标注ID
|
||||||
const shapeId = `shape_${item.labelingSampleId}_${item.labelId}_${Date.now()}`
|
const shapeId = `shape_${item.labelingSampleId}_${item.id || Date.now()}`
|
||||||
|
|
||||||
// 构造标注形状数据
|
// 构造标注形状数据
|
||||||
const shape = {
|
const shape = {
|
||||||
|
|
@ -357,7 +426,8 @@ export default {
|
||||||
} else if (window.AILabel) {
|
} else if (window.AILabel) {
|
||||||
instance = window.AILabel({ el: container, image: image })
|
instance = window.AILabel({ el: container, image: image })
|
||||||
} else {
|
} else {
|
||||||
instance = this.createSimpleLabelTool(container, image)
|
// 自研工具传入当前实例,用于调用工具方法
|
||||||
|
instance = this.createSimpleLabelTool(container, image, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (instance) {
|
if (instance) {
|
||||||
|
|
@ -383,21 +453,21 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// 自研简易标注工具(仅展示,移除所有编辑功能)
|
// 自研简易标注工具(仅展示,集成标签名称绘制逻辑)
|
||||||
createSimpleLabelTool(container, image) {
|
createSimpleLabelTool(container, image, parentInstance) {
|
||||||
const tool = {
|
const tool = {
|
||||||
el: container,
|
el: container,
|
||||||
image,
|
image,
|
||||||
shapes: [],
|
shapes: [],
|
||||||
selectedShape: null,
|
selectedShape: null,
|
||||||
mainCanvas: null,
|
mainCanvas: null,
|
||||||
tempCanvas: null,
|
|
||||||
mainCtx: null,
|
mainCtx: null,
|
||||||
tempCtx: null,
|
|
||||||
zoomScale: 1.0,
|
zoomScale: 1.0,
|
||||||
|
// 挂载父组件实例,用于调用工具方法和获取配置
|
||||||
|
parent: parentInstance,
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
// 主画布(仅展示标注)
|
// 主画布(仅展示标注+标签文字)
|
||||||
const mainCanvas = document.createElement('canvas')
|
const mainCanvas = document.createElement('canvas')
|
||||||
mainCanvas.style.cssText = 'position:absolute;top:0;left:0;pointer-events:none;z-index:10;width:100%;height:100%;'
|
mainCanvas.style.cssText = 'position:absolute;top:0;left:0;pointer-events:none;z-index:10;width:100%;height:100%;'
|
||||||
mainCanvas.width = container.offsetWidth
|
mainCanvas.width = container.offsetWidth
|
||||||
|
|
@ -422,15 +492,6 @@ export default {
|
||||||
this.redrawMainCanvas()
|
this.redrawMainCanvas()
|
||||||
},
|
},
|
||||||
|
|
||||||
// 绘制临时形状(空实现,移除编辑功能)
|
|
||||||
drawTempShape() {},
|
|
||||||
|
|
||||||
// 取消绘制(空实现)
|
|
||||||
cancelDrawing() {},
|
|
||||||
|
|
||||||
// 设置绘制类型(空实现)
|
|
||||||
setDrawType() {},
|
|
||||||
|
|
||||||
// 设置缩放
|
// 设置缩放
|
||||||
setScale(scale) {
|
setScale(scale) {
|
||||||
this.zoomScale = scale
|
this.zoomScale = scale
|
||||||
|
|
@ -470,12 +531,12 @@ export default {
|
||||||
return [...this.shapes]
|
return [...this.shapes]
|
||||||
},
|
},
|
||||||
|
|
||||||
// 重绘画布(仅展示)
|
// 【核心修改】重绘画布(展示标注+居中绘制标签名称)
|
||||||
redrawMainCanvas() {
|
redrawMainCanvas() {
|
||||||
this.mainCtx.clearRect(0, 0, this.mainCanvas.width, this.mainCanvas.height)
|
this.mainCtx.clearRect(0, 0, this.mainCanvas.width, this.mainCanvas.height)
|
||||||
this.mainCtx.save()
|
this.mainCtx.save()
|
||||||
|
|
||||||
// 居中逻辑
|
// 画布居中+缩放逻辑(原有保留)
|
||||||
this.mainCtx.translate(
|
this.mainCtx.translate(
|
||||||
this.el.offsetWidth / 2,
|
this.el.offsetWidth / 2,
|
||||||
this.el.offsetHeight / 2
|
this.el.offsetHeight / 2
|
||||||
|
|
@ -486,7 +547,7 @@ export default {
|
||||||
-this.image.naturalWidth / 2,
|
-this.image.naturalWidth / 2,
|
||||||
-this.image.naturalHeight / 2
|
-this.image.naturalHeight / 2
|
||||||
)
|
)
|
||||||
// 绘制图片
|
// 绘制底图
|
||||||
this.mainCtx.drawImage(
|
this.mainCtx.drawImage(
|
||||||
this.image,
|
this.image,
|
||||||
0, 0,
|
0, 0,
|
||||||
|
|
@ -495,24 +556,34 @@ export default {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 绘制所有标注(仅展示)
|
// 获取动态配置(适配缩放)
|
||||||
|
const dynamicLineWidth = this.parent.getDynamicLineWidth()
|
||||||
|
const dynamicFontSize = this.parent.getDynamicFontSize()
|
||||||
|
const { textPadding, bgOpacity, shapeFillOpacity, shapeMainColor } = this.parent.labelDrawConfig
|
||||||
|
|
||||||
|
// 绘制所有标注+标签文字
|
||||||
this.shapes.forEach(s => {
|
this.shapes.forEach(s => {
|
||||||
|
// 1. 获取标注基础信息
|
||||||
|
const shapeColor = s.color || shapeMainColor[s.type] || '#000000'
|
||||||
|
const labelId = s.labelId || ''
|
||||||
|
const label = this.parent.findLabelById(labelId)
|
||||||
|
|
||||||
|
// 2. 设置标注绘制样式
|
||||||
|
this.mainCtx.lineWidth = dynamicLineWidth
|
||||||
|
this.mainCtx.strokeStyle = shapeColor
|
||||||
|
this.mainCtx.fillStyle = this.parent.hexToRgba(shapeColor, shapeFillOpacity)
|
||||||
|
|
||||||
|
// 3. 绘制标注图形(原有逻辑保留,样式统一)
|
||||||
if (s.type === 'rect') {
|
if (s.type === 'rect') {
|
||||||
this.mainCtx.strokeStyle = s.color || '#00ff00'
|
|
||||||
this.mainCtx.fillStyle = 'rgba(0,255,0,0.1)'
|
|
||||||
this.mainCtx.strokeRect(s.x, s.y, s.width, s.height)
|
this.mainCtx.strokeRect(s.x, s.y, s.width, s.height)
|
||||||
this.mainCtx.fillRect(s.x, s.y, s.width, s.height)
|
this.mainCtx.fillRect(s.x, s.y, s.width, s.height)
|
||||||
} else if (s.type === 'circle') {
|
} else if (s.type === 'circle') {
|
||||||
this.mainCtx.strokeStyle = s.color || '#ff9900'
|
|
||||||
this.mainCtx.fillStyle = 'rgba(255,153,0,0.1)'
|
|
||||||
this.mainCtx.beginPath()
|
this.mainCtx.beginPath()
|
||||||
this.mainCtx.arc(s.x, s.y, s.radius, 0, Math.PI * 2)
|
this.mainCtx.arc(s.x, s.y, s.radius, 0, Math.PI * 2)
|
||||||
this.mainCtx.closePath()
|
this.mainCtx.closePath()
|
||||||
this.mainCtx.fill()
|
this.mainCtx.fill()
|
||||||
this.mainCtx.stroke()
|
this.mainCtx.stroke()
|
||||||
} else if (s.type === 'polygon') {
|
} else if (s.type === 'polygon') {
|
||||||
this.mainCtx.strokeStyle = s.color || '#0099ff'
|
|
||||||
this.mainCtx.fillStyle = 'rgba(0,153,255,0.1)'
|
|
||||||
this.mainCtx.beginPath()
|
this.mainCtx.beginPath()
|
||||||
s.points.forEach((point, idx) => {
|
s.points.forEach((point, idx) => {
|
||||||
idx === 0 ? this.mainCtx.moveTo(point.x, point.y) : this.mainCtx.lineTo(point.x, point.y)
|
idx === 0 ? this.mainCtx.moveTo(point.x, point.y) : this.mainCtx.lineTo(point.x, point.y)
|
||||||
|
|
@ -521,15 +592,53 @@ export default {
|
||||||
this.mainCtx.fill()
|
this.mainCtx.fill()
|
||||||
this.mainCtx.stroke()
|
this.mainCtx.stroke()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 4. 绘制标签名称(核心逻辑:无标签则不绘制)
|
||||||
|
if (!label) return
|
||||||
|
// 计算标注图形的正中心坐标(不同形状通用)
|
||||||
|
let textX, textY
|
||||||
|
if (s.type === 'rect') {
|
||||||
|
textX = s.x + s.width / 2
|
||||||
|
textY = s.y + s.height / 2
|
||||||
|
} else if (s.type === 'circle') {
|
||||||
|
textX = s.x
|
||||||
|
textY = s.y
|
||||||
|
} else if (s.type === 'polygon') {
|
||||||
|
const totalX = s.points.reduce((sum, p) => sum + p.x, 0)
|
||||||
|
const totalY = s.points.reduce((sum, p) => sum + p.y, 0)
|
||||||
|
textX = totalX / s.points.length
|
||||||
|
textY = totalY / s.points.length
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 设置文字基础样式
|
||||||
|
this.mainCtx.font = `${dynamicFontSize}px Microsoft YaHei`
|
||||||
|
this.mainCtx.textAlign = 'center' // 水平居中
|
||||||
|
this.mainCtx.textBaseline = 'middle' // 垂直居中
|
||||||
|
|
||||||
|
// 6. 智能计算文字背景和文字颜色(适配不同标注颜色)
|
||||||
|
const bgRgba = this.parent.hexToRgba(shapeColor, bgOpacity)
|
||||||
|
const textColor = this.parent.isLightColor(shapeColor) ? '#000000' : '#ffffff'
|
||||||
|
|
||||||
|
// 7. 绘制文字背景框(适配文字宽度,居中展示)
|
||||||
|
const textWidth = this.mainCtx.measureText(label.labelName).width
|
||||||
|
const textHeight = dynamicFontSize
|
||||||
|
this.mainCtx.fillStyle = bgRgba
|
||||||
|
this.mainCtx.fillRect(
|
||||||
|
textX - (textWidth + 2 * textPadding) / 2,
|
||||||
|
textY - (textHeight + 2 * textPadding) / 2,
|
||||||
|
textWidth + 2 * textPadding,
|
||||||
|
textHeight + 2 * textPadding
|
||||||
|
)
|
||||||
|
|
||||||
|
// 8. 绘制标签文字
|
||||||
|
this.mainCtx.fillStyle = textColor
|
||||||
|
this.mainCtx.fillText(label.labelName, textX, textY)
|
||||||
})
|
})
|
||||||
|
|
||||||
this.mainCtx.restore()
|
this.mainCtx.restore()
|
||||||
},
|
},
|
||||||
|
|
||||||
// 事件绑定(空实现)
|
// 形状数据操作(绑定labelId/color等信息)
|
||||||
on() {},
|
|
||||||
|
|
||||||
// 形状数据操作(仅读取)
|
|
||||||
setShapeData(id, data) {
|
setShapeData(id, data) {
|
||||||
const shape = this.shapes.find(s => s.id === id)
|
const shape = this.shapes.find(s => s.id === id)
|
||||||
if (shape) Object.assign(shape, data)
|
if (shape) Object.assign(shape, data)
|
||||||
|
|
@ -559,7 +668,7 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// 加载标注数据(仅展示)
|
// 加载标注数据(仅展示,绑定labelId用于绘制标签)
|
||||||
loadLabelData() {
|
loadLabelData() {
|
||||||
if (!this.ailabelInstance || !this.currentImage) return
|
if (!this.ailabelInstance || !this.currentImage) return
|
||||||
|
|
||||||
|
|
@ -579,6 +688,7 @@ export default {
|
||||||
shapes.forEach(shapeData => {
|
shapes.forEach(shapeData => {
|
||||||
try {
|
try {
|
||||||
let shapeId = ''
|
let shapeId = ''
|
||||||
|
// 根据标注类型绘制图形
|
||||||
if (shapeData.type === 'rect' && shapeData.x !== undefined) {
|
if (shapeData.type === 'rect' && shapeData.x !== undefined) {
|
||||||
shapeId = this.ailabelInstance.drawRect?.(shapeData.x, shapeData.y, shapeData.width, shapeData.height) || shapeData.id
|
shapeId = this.ailabelInstance.drawRect?.(shapeData.x, shapeData.y, shapeData.width, shapeData.height) || shapeData.id
|
||||||
} else if (shapeData.type === 'polygon' && shapeData.points && shapeData.points.length >= 3) {
|
} else if (shapeData.type === 'polygon' && shapeData.points && shapeData.points.length >= 3) {
|
||||||
|
|
@ -587,10 +697,11 @@ export default {
|
||||||
shapeId = this.ailabelInstance.drawCircle?.(shapeData.x, shapeData.y, shapeData.radius) || shapeData.id
|
shapeId = this.ailabelInstance.drawCircle?.(shapeData.x, shapeData.y, shapeData.radius) || shapeData.id
|
||||||
}
|
}
|
||||||
|
|
||||||
// 绑定标注样式和数据
|
// 【核心】绑定标注的labelId和color(用于绘制标签名称)
|
||||||
if (shapeId) {
|
if (shapeId) {
|
||||||
this.ailabelInstance.setShapeData?.(shapeId, {
|
this.ailabelInstance.setShapeData?.(shapeId, {
|
||||||
color: shapeData.color || this.shapeColorMap[shapeData.type]
|
labelId: shapeData.labelId, // 绑定标签ID,用于查找标签名称
|
||||||
|
color: shapeData.color || this.shapeColorMap[shapeData.type] // 绑定标注颜色
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -663,7 +774,6 @@ export default {
|
||||||
handleShapeDeleted() {},
|
handleShapeDeleted() {},
|
||||||
selectLabel() {},
|
selectLabel() {},
|
||||||
findShapeIdsByLabelId() { return [] },
|
findShapeIdsByLabelId() { return [] },
|
||||||
findLabelById() { return null },
|
|
||||||
removeLabelById() {},
|
removeLabelById() {},
|
||||||
confirmAddLabel() {},
|
confirmAddLabel() {},
|
||||||
deleteSelected() {},
|
deleteSelected() {},
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
<el-button plain size="mini" type="success" icon="el-icon-download" v-hasPermi="['system:user:import']"
|
<el-button plain size="mini" type="success" icon="el-icon-download" v-hasPermi="['system:user:import']"
|
||||||
@click="handleModelExport">
|
@click="handleModelExport">
|
||||||
导入模板
|
模板下载
|
||||||
</el-button>
|
</el-button>
|
||||||
|
|
||||||
<el-button plain size="mini" type="warning" icon="el-icon-upload2" v-hasPermi="['system:user:import']"
|
<el-button plain size="mini" type="warning" icon="el-icon-upload2" v-hasPermi="['system:user:import']"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue