提交代码

This commit is contained in:
jiang 2024-11-28 12:27:07 +08:00
parent 959ed2c2c9
commit 049a415936
7 changed files with 482 additions and 63 deletions

View File

@ -49,6 +49,7 @@
"js-beautify": "1.13.0",
"js-cookie": "3.0.1",
"jsencrypt": "3.0.0-rc.1",
"label-studio": "^1.0.1",
"nprogress": "0.2.0",
"quill": "1.3.7",
"screenfull": "5.0.2",

View File

@ -41,12 +41,19 @@ export function getAnnotationDetails(data) {
})
}
export function listSelection(data) {
export function getMyNoAnnotatedTask(data) {
return request({
url: '/ai/annotationTask/list/listSelection',
url: '/ai/annotationTask/getMyNoAnnotatedTask',
method: 'get',
params: data
})
}
export function getMyAnnotationFiles(annotationStatus,taskId) {
return request({
url: '/ai/annotationTask/getMyAnnotationFiles/'+annotationStatus+'/'+taskId,
method: 'get',
})
}

View File

@ -112,7 +112,7 @@
<!-- 新建数据集弹窗 -->
<add-task-dialog :open="addOpen" :get-list="getList" @dialog-cancel="handleCancel"></add-task-dialog>
<release-version-dialog :dataset-id="datasetId" :open="releaseOpen" :task-id="taskId" @dialog-cancel="handleCancel"></release-version-dialog>
<release-version-dialog :get-list="getList" :dataset-id="datasetId" :open="releaseOpen" :task-id="taskId" :last-version-name="lastVersionName" @dialog-cancel="handleCancel"></release-version-dialog>
</div>
</template>
@ -137,6 +137,7 @@ export default {
{label:'正在审核',value:'3'},
{label:'已审核',value:'4'},
],
lastVersionName:'',
taskId:0,
datasetId: 0,
//
@ -204,9 +205,11 @@ export default {
this.addOpen = true;
},
handleRelease(row){
this.releaseOpen = true;
this.datasetId = row.datasetId;
this.taskId = row.taskId;
this.lastVersionName = row.lastVersionName || '';
console.log(this.lastVersionName);
this.releaseOpen = true;
},
handleDimension(row){

View File

@ -0,0 +1,168 @@
<template>
<div>
<div ref="labelStudio" id="label-studio-container"></div>
</div>
</template>
<script>
import LabelStudio from 'label-studio';
import 'label-studio/build/static/css/main.css';
export default {
name: 'CustomLabelStudio',
props: {
task: {
type: String,
required: true
}
},
data() {
return {
labelStudio: null,
customButtons: [
{
id: "custom-button",
name: "自定义按钮",
icon: "🔍",
action: () => {
console.log("自定义按钮被点击了!");
}
}
],
chineseLocalization: {
"DONE": "完成",
"SKIP": "跳过",
"SUBMIT": "提交",
"ANNOTATION": "标注",
"ANNOTATIONS": "标注",
"LABEL": "标签",
"LABELS": "标签",
"RELATIONS": "关系",
"REGIONS": "区域",
"RESULTS": "结果",
},
labelConfig: `
<View>
<Image name="image" value="$image"/>
<RectangleLabels name="label" toName="image">
<Label value="人" background="#ff0000"/>
<Label value="车" background="#00ff00"/>
</RectangleLabels>
</View>
`,
previousAnnotations: [] //
};
},
mounted() {
this.initLabelStudio();
},
beforeDestroy() {
if (this.labelStudio) {
this.labelStudio.destroy();
}
},
methods: {
initLabelStudio() {
this.labelStudio = new LabelStudio('label-studio-container', {
config: this.labelConfig,
interfaces: [
"panel",
"update",
"controls",
],
user: {
pk: 1,
firstName: "James",
lastName: "Dean"
},
task: JSON.parse(this.task),
locale: 'zh_CN',
messages: this.chineseLocalization,
onLabelStudioLoad: (LS) => {
console.log("Label Studio 已加载", LS);
var c = LS.annotationStore.addAnnotation({
userGenerate: true
});
LS.annotationStore.selectAnnotation(c.id);
//
LS.annotationStore.events.on('addAnnotation', this.handleAddAnnotation);
LS.annotationStore.events.on('updateAnnotation', this.handleUpdateAnnotation);
//
this.startAnnotationCheck(LS);
}
});
},
//
handleAddAnnotation(annotation) {
console.log("新增标注框:", annotation);
},
//
handleUpdateAnnotation(annotation) {
console.log("更新标注框:", annotation);
},
//
startAnnotationCheck(LS) {
this.checkAnnotationsInterval = setInterval(() => {
this.checkAnnotations(LS);
}, 1000); //
},
//
stopAnnotationCheck() {
if (this.checkAnnotationsInterval) {
clearInterval(this.checkAnnotationsInterval);
}
},
//
checkAnnotations(LS) {
const currentAnnotations = LS.annotationStore.data; //
//
if (this.hasAnnotationsChanged(currentAnnotations)) {
console.log('标注数据发生变化:', currentAnnotations);
this.previousAnnotations = currentAnnotations; //
}
},
//
hasAnnotationsChanged(currentAnnotations) {
//
if (currentAnnotations.length !== this.previousAnnotations.length) {
return true; //
}
// ID
for (let i = 0; i < currentAnnotations.length; i++) {
const current = currentAnnotations[i];
const previous = this.previousAnnotations[i];
if (current.id !== previous.id) {
return true; // ID
}
// true
if (JSON.stringify(current.geometry) !== JSON.stringify(previous.geometry)) {
return true;
}
}
return false;
}
}
};
</script>
<style scoped>
.label-studio-container {
height: 600px;
width: 100%;
border: 1px solid #e0e0e0;
border-radius: 8px;
overflow: hidden;
}
</style>

View File

@ -8,7 +8,8 @@
<el-button v-for="(icon, index) in leftIcons"
:key="index"
:icon="icon"
circle>
circle
>
</el-button>
</div>
<!-- 右侧选择框和设置按钮 -->
@ -18,12 +19,14 @@
placeholder="请选择标注任务"
clearable
class="select-task"
@change="selectTask"
>
<el-option
v-for="dict in dict.type.ai_annotate_type"
:key="dict.value"
:label="dict.label"
:value="dict.value"
v-for="dict in taskList"
:key="dict.taskId"
:label="dict.taskName"
:value="dict.taskId"
/>
</el-select>
<span class="settings-icon">
@ -40,41 +43,91 @@
<div>
<el-button
type="primary"
:class="{'is-selected': annotationType===0}"
plain
size="mini"
>全部</el-button>
@click="toggleSelected(0)"
>全部
</el-button>
<el-button
type="primary"
:class="{'is-selected': annotationType===1}"
plain
size="mini"
>已标注</el-button>
@click="toggleSelected(1)"
>已标注
</el-button>
<el-button
type="primary"
:class="{'is-selected': annotationType===2}"
plain
size="mini"
>未标注</el-button>
@click="toggleSelected(2)"
>未标注
</el-button>
<el-button
type="primary"
:class="{'is-selected': annotationType===3}"
plain
size="mini"
>审核驳回</el-button>
@click="toggleSelected(3)"
>审核驳回
</el-button>
</div>
</div>
<div class="bottom-content-left-bottom">
<div>
<ul>
<li v-for="i in count" class="list-item">
<el-checkbox v-model="checked"> 文件名称{{ i }}</el-checkbox>
<li v-for="item in images" class="list-item">
<div @click="setItem(item)">
<input type="checkbox" :checked="false" disabled>
<span style="font-size: 14px;margin-left: 5px">{{ item.fileName }}</span>
</div>
</li>
</ul>
</div>
</div>
</div>
<div class="bottom-content-center">
<div></div>
<div>
<custom-label-studio :task="JSON.stringify(task)"/>
</div>
</div>
<div class="bottom-content-right">
<div></div>
<div class="bottom-content-right-center">
<div class="bottom-content-right-top">
<div>
</div>
</div>
<div class="bottom-content-right-middle">
<div>
</div>
</div>
<div class="bottom-content-right-bottom">
<div>
<el-button
type="primary"
plain
size="mini"
>上一张
</el-button>
<el-button
type="primary"
plain
size="mini"
>下一张
</el-button>
<el-button
type="primary"
plain
size="mini"
>保存
</el-button>
</div>
</div>
</div>
</div>
</div>
</div>
@ -82,10 +135,21 @@
</template>
<script>
import customLabelStudio from './customLabelStudio.vue'
import { getMyNoAnnotatedTask,getMyAnnotationFiles } from '../../../../api/dataCenter/annotationTask'
export default {
dicts: ['ai_annotate_type'],
components: { customLabelStudio },
data() {
return {
taskList:[],
task: {
id: 1,
data: {
image: 'http://192.168.0.14:9090/bonus/1/测试/xigu_wangwang_3787075_37.jpg'
}
},
taskId: undefined, // ID
leftIcons: [
'el-icon-circle-plus-outline',
@ -93,21 +157,66 @@ export default {
'el-icon-search',
'el-icon-search',
'el-icon-search',
'el-icon-search',
'el-icon-search'
], //
count: 600
};
count: 600,
annotationType: 0,
images: []
}
},
methods:{
load () {
this.count += 2
created() {
this.listSelection()
},
methods: {
listSelection() {
getMyNoAnnotatedTask({}).then(res => {
this.taskList = res.data;
})
},
selectTask(id){
this.images = [];
this.annotationType = 0;
this.taskId = id;
getMyAnnotationFiles(this.annotationType,id).then(response => {
this.images =response.data;
})
},
load() {
},
toggleSelected(type) {
this.images = [];
if (this.annotationType !== type) {
this.annotationType = type
}
if (!this.taskId){
return
}
getMyAnnotationFiles(this.annotationType,this.taskId).then(response => {
this.images =response.data;
})
},
setItem(item) {
console.log(item)
}
}
};
}
</script>
<style scoped lang="scss">
/* 默认按钮样式 */
.el-button {
transition: all 0.3s ease;
}
/* 选中时的样式 */
.el-button.is-selected {
background-color: #007bff !important; /* 设置选中背景色 */
border-color: #007bff !important; /* 设置选中边框色 */
color: white !important; /* 设置字体颜色 */
}
.div-main {
width: 100%;
height: calc(100vh - 84px);
@ -157,7 +266,9 @@ export default {
.bottom-part {
width: 100%;
height: calc(100% - 60px);
padding: 10px;
padding-bottom: 10px;
padding-left: 10px;
padding-right: 10px;
.bottom-content {
display: flex;
@ -177,26 +288,29 @@ export default {
width: 20%;
.bottom-content-left-bottom,
.bottom-content-left-top{
.bottom-content-left-top {
width: 100%;
padding: 5px;
}
.bottom-content-left-top{
.bottom-content-left-top {
height: 50px;
div{
div {
width: 100%;
height: 100%;
background-color: #f5f7fa;
border: 1px solid #dfe4ed;
display: flex;
align-items: center;
justify-content: center
justify-content: space-evenly;
}
}
.bottom-content-left-bottom{
.bottom-content-left-bottom {
height: calc(100% - 50px);
div{
> div {
width: 100%;
height: 100%;
background-color: #f5f7fa;
@ -204,7 +318,8 @@ export default {
display: flex;
align-items: center;
justify-content: center;
ul{
ul {
width: 100%;
height: 100%;
overflow: auto;
@ -212,18 +327,54 @@ export default {
list-style: none; /* 移除默认的点样式 */
li {
width: 100%;
padding: 10px;
padding: 1px;
background-color: #FFFFFF;
div {
height: 40px;
padding-left: 2px;
display: flex;
cursor: pointer; /* 手势效果 */
align-items: center;
background-color: #f5f7fa;
}
input[type="checkbox"] {
appearance: none; /* 隐藏默认复选框样式 */
width: 15px;
height: 15px;
border: 1px solid #4d4e51;
border-radius: 4px; /* 圆角方框 */
cursor: pointer;
}
input[type="checkbox"]:checked::after {
content: '✔'; /* 显示勾符号 */
color: white;
font-size: 10px;
display: flex;
align-items: center;
justify-content: center;
}
input[type="checkbox"]:checked {
background-color: rgb(64, 158, 255); /* 改变选中颜色 */
border-color: rgb(64, 158, 255);
}
}
}
/* 针对自定义滚动条样式 */
ul::-webkit-scrollbar {
cursor: default; /* 对滚动条自定义光标 */
width: 5px;
}
ul::-webkit-scrollbar-thumb {
background-color: #888;
cursor: pointer; /* 手势效果 */
}
ul::-webkit-scrollbar-thumb:hover {
background-color: #555;
}
@ -234,7 +385,8 @@ export default {
.bottom-content-center {
width: 60%;
padding: 5px;
div{
div {
width: 100%;
height: 100%;
background-color: #f5f7fa;
@ -245,15 +397,57 @@ export default {
.bottom-content-right {
width: 20%;
padding: 5px;
div{
.bottom-content-right-center {
width: 100%;
height: 100%;
background-color: #f5f7fa;
border: 1px solid #dfe4ed;
display: flex;
align-items: center;
overflow:auto;
flex-direction: column;
.bottom-content-right-top {
width: 100%;
height: calc(50% - 25px);
padding: 5px;
div {
width: 100%;
height: 100%;
background-color: #f5f7fa;
border: 1px solid #dfe4ed;
}
}
.bottom-content-right-middle {
width: 100%;
height: calc(50% - 25px);
padding: 5px;
div {
width: 100%;
height: 100%;
background-color: #f5f7fa;
border: 1px solid #dfe4ed;
}
}
.bottom-content-right-bottom {
width: 100%;
height: 50px;
padding: 5px;
div {
width: 100%;
height: 100%;
background-color: #f5f7fa;
border: 1px solid #dfe4ed;
display: flex;
align-items: center;
justify-content: space-evenly;
}
}
}
}
}

View File

@ -111,6 +111,7 @@ import {listDataSetLabels } from "@/api/dataCenter/labels";
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
import { datasetList } from '@/api/dataCenter/dataSet'
import { parseTime } from '../../../../utils/bonus'
export default {
dicts: ['ai_annotate_type'],
components: { selectPersonnelDialog, Treeselect},
@ -226,8 +227,11 @@ export default {
}
this.$refs['form'].validate(valid => {
if (valid) {
this.form.startTime = parseTime(this.form.data[0])
this.form.endTime = parseTime(this.form.data[1])
this.form.isStartTeam = this.form.startTeam?'1':'0';
this.form.labels = this.form.label.join(",")
console.log(this.form)
if (this.form.isStartTeam) {
let annotators = [];
let reviewers = [];

View File

@ -1,9 +1,11 @@
<template>
<div>
<el-dialog title="发布版本" :visible.sync="isOpen" width="500px" append-to-body @close="cancel" :close-on-click-modal="false">
<el-form ref="form" label-position="top" :model="form" :rules="rules" >
<el-dialog title="发布版本" :visible.sync="isOpen" width="500px" append-to-body @close="cancel"
:close-on-click-modal="false"
>
<el-form ref="form" label-position="top" :model="form" :rules="rules">
<el-form-item label="版本名称" prop="versionName">
<el-input v-model="form.versionName" maxlength="20" placeholder="请输入版本名称" />
<el-input v-model="form.versionName" maxlength="20" placeholder="请输入版本名称"/>
</el-form-item>
<el-form-item label="描述" prop="versionDesc">
<el-input
@ -35,66 +37,106 @@ export default {
open: {
type: Boolean,
default: false,
required: true
},
getList: {
type: Function,
required: true,
},
datasetId:{
datasetId: {
type: Number,
default: 0,
required: true,
required: true
},
taskId:{
taskId: {
type: Number,
default: 0,
required: true,
required: true
},
lastVersionName: {
type: String,
default: '',
required: true
}
},
computed: {
isOpen: {
get() {
return this.open;
return this.open
},
set(value) {
this.$emit('update:open', value);
},
this.$emit('update:open', value)
}
},
},
data() {
return {
form: {}, //
rules:{
versionName:[
form: {
versionName: this.lastVersionName || '' // lastVersionName
}, //
rules: {
versionName: [
{ required: true, message: '版本名称不能为空', trigger: 'blur' },
{ pattern: /^V\d{1,3}(\.\d{1,3}){2}$/, message: '版本名称式不正确,应为 vX.Y.Z 格式', trigger: 'blur' }
]
}
};
}
},
watch: {
isOpen(newVal){
if (newVal){
if (this.isValidVersion(this.lastVersionName)) {
this.form.versionName = this.incrementMajorVersion(this.lastVersionName);
} else {
this.form.versionName = 'V1.0.0'; //
}
}
},
},
methods: {
//
isValidVersion(version) {
return /^V\d{1,3}(\.\d{1,3}){2}$/.test(version);
},
//
incrementMajorVersion(version) {
const match = version.match(/^V(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/);
if (match) {
let major = parseInt(match[1], 10);
//
major += 1;
return `V${major}.0.0`; // 0
}
return version; //
},
//
submitForm() {
this.$refs["form"].validate(valid => {
this.$refs['form'].validate(valid => {
if (valid) {
this.form.datasetId = this.datasetId;
this.form.taskId = this.taskId;
this.form.datasetId = this.datasetId
this.form.taskId = this.taskId
add(this.form).then(response => {
this.$modal.msgSuccess("发布成功");
this.cancel();
this.$modal.msgSuccess('发布成功')
this.cancel()
})
}
})
},
//
cancel() {
this.isOpen = false;
this.reset();
this.$emit('dialog-cancel'); //
this.isOpen = false
this.reset()
this.$emit('dialog-cancel') //
this.getList();
},
//
reset() {
this.form = {}; //
this.$refs.form.resetFields(); // Element UI
},
},
};
this.form = {versionName:''} //
this.$refs.form.resetFields() // Element UI
}
}
}
</script>
<style scoped lang="scss">