提交代码

This commit is contained in:
jiang 2024-11-27 09:00:55 +08:00
parent 25aa4a7fe7
commit d7d8272978
21 changed files with 1917 additions and 177 deletions

View File

@ -3,7 +3,7 @@ import request from '@/utils/request'
//查询数据集
export function list(query) {
return request({
url: '/ai/version/list',
url: '/ai/annotationTask/list',
method: 'get',
params: query
})

View File

@ -8,6 +8,14 @@ export function list(query) {
params: query
})
}
export function datasetList(query) {
return request({
url: '/ai/dataset/datasetList',
method: 'get',
params: query
})
}
//创建数据集
export function add(data) {
return request({
@ -31,10 +39,19 @@ export function setPublic(datasetId,isPublic) {
isPublic
}
return request({
url: '/ai/dataset/set-public/',
url: '/ai/dataset/setPublic',
method: 'post',
params: data
})
}
export function importData(data) {
return request({
url: '/ai/dataset/import',
method: 'post',
data: data
})
}

View File

@ -162,6 +162,34 @@ export const dynamicRoutes = [
}
]
},
{
path: '/dataCenter/annotationTask/dataAnnotations',
component: Layout,
hidden: true,
permissions: ['system:dict:list'],
children: [
{
path: 'index/:taskId(\\d+)',
component: () => import('@/views/dataCenter/annotationTask/child/dataAnnotations.vue'),
name: 'Data',
meta: { title: '数据标注', activeMenu: '/dataCenter/annotationTask/dataAnnotations' }
}
]
},
{
path: '/dataCenter/annotationTask/examine',
component: Layout,
hidden: true,
permissions: ['system:dict:list'],
children: [
{
path: 'index/:taskId(\\d+)',
component: () => import('@/views/dataCenter/annotationTask/child/examine.vue'),
name: 'Data',
meta: { title: '数据审核', activeMenu: '/dataCenter/annotationTask/examine' }
}
]
},
{
path: '/monitor/job-log',
component: Layout,

View File

@ -0,0 +1,245 @@
<template>
<div>
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="85px">
<el-form-item label="任务标题" prop="datasetName">
<el-input
v-model="queryParams.taskName"
placeholder="请输入任务标题"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="时间" prop="time">
<el-date-picker
v-model="queryParams.time"
type="datetimerange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期">
</el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
<el-button
style="float: right"
type="primary"
plain
icon="el-icon-plus"
size="mini"
@click="handleAdd"
v-hasPermi="['dataCenter:dataSet:add']"
>新建</el-button>
</el-form>
<el-table style="width: 100%" v-loading="loading" :data="list" :height="tableHeight" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="50" align="center" />
<el-table-column type="index" label="序号" align="center" min-width="50" />
<el-table-column label="开始时间-结束时间" align="center" show-overflow-tooltip prop="datasetName"/>
<el-table-column label="任务标题" align="center" show-overflow-tooltip prop="taskName" />
<el-table-column label="负责人员" align="center" show-overflow-tooltip prop="ownerName" min-width="100"/>
<el-table-column label="任务状态" show-overflow-tooltip align="center" prop="annotateTaskStatus" width="160"/>
<el-table-column label="所属数据集" show-overflow-tooltip align="center" prop="datasetName" />
<el-table-column label="标注进度(已标注个数/总数)" show-overflow-tooltip min-width="140" align="center" prop="createBy" >
<template slot-scope="scope">
<div style="display: flex;align-items: center;justify-content: center;">
<!-- 使用 v-if 确保当 annotatedCount > 0 时渲染进度条 -->
<el-progress
v-if="scope.row.totalCount > 0"
color="#13ce66"
:percentage="getPercentage(scope.row)"
style="width: 150px;"/>
<!-- 显示标注进度 -->
<span>({{ (scope.row.totalCount - scope.row.status0Count) + '/' + scope.row.totalCount }})</span>
</div>
</template>
</el-table-column>
<el-table-column label="待审核个数" show-overflow-tooltip align="center" prop="createBy">
<template slot-scope="scope">
<span>{{ scope.row.totalCount - (scope.row.status2Count+scope.row.status3Count)}}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" min-width="140" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleExamine(scope.row)"
v-hasPermi="['dataCenter:sample:edit']"
>审核</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleDimension(scope.row)"
v-hasPermi="['dataCenter:sample:edit']"
>智能标注</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleAnnotation(scope.row)"
v-hasPermi="['dataCenter:sample:edit']"
>标注</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleRelease(scope.row)"
v-hasPermi="['dataCenter:sample:edit']"
>发布</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<!-- 新建数据集弹窗 -->
<add-task-dialog :open="addOpen" :get-list="getList" @dialog-cancel="handleCancel"></add-task-dialog>
<release-version-dialog :dataset-id="datasetId" :open="releaseOpen" @dialog-cancel="handleCancel"></release-version-dialog>
</div>
</template>
<script>
import { list,del,setPublic} from '@/api/dataCenter/annotationTask'
import addTaskDialog from '../dialog/addTaskDialog'
import releaseVersionDialog from '../dialog/releaseVersionDialog.vue'
export default {
components: { releaseVersionDialog, addTaskDialog},
name: "allTasks",
data() {
return {
datasetId: 0,
//
loading: true,
//
ids: [],
//
single: true,
//
multiple: true,
tableHeight: 0,
//
showSearch: true,
//
total: 0,
//
list: [],
//
addOpen: false,
releaseOpen: false,
//
queryParams: {
pageNum: 1,
pageSize: 10,
datasetName: null,
},
};
},
created() {
this.getList();
//
this.updateTableHeight();
//
window.addEventListener("resize", this.updateTableHeight);
},
methods: {
updateTableHeight() {
//
const headerHeight = 300; //
const footerHeight = 80; //
this.tableHeight = window.innerHeight - headerHeight - footerHeight;
},
/**获取数据 **/
getList(){
this.loading =true
list(this.queryParams).then(response => {
console.log(response);
this.list = response.rows;
this.total = response.total;
this.loading =false
})
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
handleAdd(){
this.addOpen = true;
},
handleRelease(row){
this.releaseOpen = true;
this.datasetId = row.datasetId;
},
handleDimension(row){
},
handleAnnotation(row){
this.$tab.openPage("数据标注", '/dataCenter/annotationTask/dataAnnotations/index/' + row.taskId);
},
handleExamine(row){
this.$tab.openPage("数据审核", '/dataCenter/annotationTask/examine/index/' + row.taskId);
},
getPercentage(row) {
if (row.totalCount > 0 && row.status0Count >= 0) {
let percentage = Math.floor(((row.totalCount - row.status0Count) / row.totalCount) * 100);
// percentage 0 100
return Math.min(Math.max(percentage, 0), 100);
}
return 0; // 0%
},
handleDelete(row){
const ids = row.datasetId || this.ids;
this.$modal.confirm('是否确认删除数据项?').then(function() {
return del(ids);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
},
//
handleSelectionChange(selection) {
this.ids = selection.map(item => item.datasetId)
this.single = selection.length!==1
this.multiple = !selection.length
},
//
handleCancel() {
this.addOpen = false;
this.releaseOpen =false;
},
}
}
</script>
<style scoped>
.demo-table-expand {
font-size: 0;
}
.demo-table-expand label {
width: 90px;
color: #99a9bf;
}
.demo-table-expand .el-form-item {
margin-right: 0;
margin-bottom: 0;
width: 50%;
}
</style>

View File

@ -0,0 +1,53 @@
<template>
<div>
<canvas ref="canvas" @mousedown="startDraw" @mousemove="draw" @mouseup="endDraw"></canvas>
<div v-for="(box, index) in boxes" :key="index" :style="{ position: 'absolute', left: box.x + 'px', top: box.y + 'px', width: box.width + 'px', height: box.height + 'px', border: '2px solid red' }"></div>
</div>
</template>
<script>
export default {
data() {
return {
isDrawing: false,
startX: 0,
startY: 0,
boxes: []
};
},
mounted() {
const canvas = this.$refs.canvas;
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
this.ctx = canvas.getContext('2d');
},
methods: {
startDraw(e) {
this.isDrawing = true;
const rect = this.$refs.canvas.getBoundingClientRect();
this.startX = e.clientX - rect.left;
this.startY = e.clientY - rect.top;
},
draw(e) {
if (this.isDrawing) {
const rect = this.$refs.canvas.getBoundingClientRect();
this.ctx.clearRect(0, 0, this.$refs.canvas.width, this.$refs.canvas.height);
this.ctx.strokeRect(this.startX, this.startY, e.clientX - rect.left - this.startX, e.clientY - rect.top - this.startY);
}
},
endDraw() {
if (this.isDrawing) {
this.isDrawing = false;
const rect = this.$refs.canvas.getBoundingClientRect();
const box = {
x: this.startX,
y: this.startY,
width: Math.abs(e.clientX - rect.left - this.startX),
height: Math.abs(e.clientY - rect.top - this.startY)
};
this.boxes.push(box);
}
}
}
};
</script>

View File

@ -0,0 +1,214 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="85px">
<el-form-item label="文件名称" prop="fileName">
<el-input
v-model="queryParams.fileName"
placeholder="请输入数据集名称"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-plus"
size="mini"
@click="handleAdd"
v-hasPermi="['dataCenter:dataSet:add']"
>导入</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['dataCenter:dataSet:remove']"
>删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-close"
size="mini"
@click="handleClose"
>关闭</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table style="width: 100%" v-loading="loading" :data="list" :height="tableHeight" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="50" align="center" />
<el-table-column type="index" label="序号" min-width="50" />
<el-table-column label="文件名称" align="center" show-overflow-tooltip prop="fileName" min-width="100" />
<el-table-column label="文件大小" align="center" show-overflow-tooltip prop="fileSize" >
<template slot-scope="scope">
<span>{{scope.row.fileSize < 1024 * 1024?(scope.row.fileSize/1024).toFixed(2)+'KB':(scope.row.fileSize/1024/1024).toFixed(2)+'MB'}}</span>
</template>
</el-table-column>
<el-table-column label="格式" align="center" show-overflow-tooltip prop="createBy" min-width="100">
<template slot-scope="scope">
<span>
{{scope.row.fileName.split('.').pop()}}
</span>
</template>
</el-table-column>
<el-table-column label="创建时间" show-overflow-tooltip align="center" prop="createTime" min-width="100">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" min-width="100" class-name="small-padding fixed-width" >
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
v-hasPermi="['dataCenter:sample:remove']"
>删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<!-- 导入 -->
<import-dialog :get-list="getList" :open="importOpen" :data-type="dataType" :dataset-id="datasetId.toString()" @dialog-cancel="handleCancel"/>
</div>
</template>
<script>
import {list} from '@/api/dataCenter/dataSetDetails'
export default {
name: "DataSet",
data() {
return {
dataType:'0',
datasetId: '0',
//
loading: true,
//
ids: [],
//
single: true,
//
multiple: true,
tableHeight: 0,
//
showSearch: true,
//
total: 0,
//
list: [],
//
importOpen: false,
//
queryParams: {
pageNum: 1,
pageSize: 10,
datasetName: null,
},
};
},
created() {
this.dataType = this.$route.query && this.$route.query.dataType;
this.datasetId = this.$route.params && this.$route.params.dataSetId;
this.getList();
//
this.updateTableHeight();
//
window.addEventListener("resize", this.updateTableHeight);
},
methods: {
updateTableHeight() {
//
const headerHeight = 200; //
const footerHeight = 80; //
this.tableHeight = window.innerHeight - headerHeight - footerHeight;
},
/**获取数据 **/
getList(){
this.loading =true
this.queryParams.dataSetId = this.datasetId;
list(this.queryParams).then(response => {
this.list = response.rows;
this.total = response.total;
this.loading =false
})
},
/** 返回按钮操作 */
handleClose() {
const obj = { path: "/dataCenter/dataSet" };
this.$tab.closeOpenPage(obj);
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
handleAdd(){
this.importOpen = true;
},
handleDelete(row){
const datasetId = this.datasetId;
const ids = row.fileId || this.ids.join(",");
this.$modal.confirm('是否确认删除数据项?').then(function() {
return remove(datasetId,ids);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
},
//
handleSelectionChange(selection) {
this.ids = selection.map(item => item.fileId)
this.single = selection.length!==1
this.multiple = !selection.length
},
//
handleCancel() {
this.importOpen= false;
},
}
}
</script>
<style scoped>
.demo-table-expand {
font-size: 0;
}
.demo-table-expand label {
width: 90px;
color: #99a9bf;
}
.demo-table-expand .el-form-item {
margin-right: 0;
margin-bottom: 0;
width: 50%;
}
</style>

View File

@ -0,0 +1,9 @@
<template>
</template>
<script>
</script>
<style scoped lang="scss">
</style>

View File

@ -0,0 +1,228 @@
<template>
<div>
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="85px">
<el-form-item label="任务标题" prop="datasetName">
<el-input
v-model="queryParams.taskName"
placeholder="请输入任务标题"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="时间" prop="time">
<el-date-picker
v-model="queryParams.time"
type="datetimerange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期">
</el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
<el-button
style="float: right"
type="primary"
plain
icon="el-icon-plus"
size="mini"
@click="handleAdd"
v-hasPermi="['dataCenter:dataSet:add']"
>新建</el-button>
</el-form>
<el-table style="width: 100%" v-loading="loading" :data="list" :height="tableHeight" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="50" align="center" />
<el-table-column type="index" label="序号" align="center" min-width="50" />
<el-table-column label="开始时间-结束时间" align="center" show-overflow-tooltip prop="datasetName"/>
<el-table-column label="任务标题" align="center" show-overflow-tooltip prop="taskName" />
<el-table-column label="负责人员" align="center" show-overflow-tooltip prop="createBy" min-width="100"/>
<el-table-column label="任务状态" show-overflow-tooltip align="center" prop="annotateTaskStatus" width="160"/>
<el-table-column label="所属数据集" show-overflow-tooltip align="center" prop="datasetDesc" />
<el-table-column label="标注进度(已标注个数/总数)" show-overflow-tooltip min-width="140" align="center" prop="createBy" />
<el-table-column label="待审核个数" show-overflow-tooltip align="center" prop="createBy" />
<el-table-column label="操作" align="center" min-width="140" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleExamine(scope.row)"
v-hasPermi="['dataCenter:sample:edit']"
>审核</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleDimension(scope.row)"
v-hasPermi="['dataCenter:sample:edit']"
>智能标注</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleAnnotation(scope.row)"
v-hasPermi="['dataCenter:sample:edit']"
>标注</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleRelease(scope.row)"
v-hasPermi="['dataCenter:sample:edit']"
>发布</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<!-- 新建数据集弹窗 -->
<add-task-dialog :open="addOpen" :get-list="getList" @dialog-cancel="handleCancel"></add-task-dialog>
<release-version-dialog :dataset-id="datasetId" :open="releaseOpen" @dialog-cancel="handleCancel"></release-version-dialog>
</div>
</template>
<script>
import { list,del,setPublic} from '@/api/dataCenter/annotationTask'
import addTaskDialog from '../dialog/addTaskDialog'
import releaseVersionDialog from '../dialog/releaseVersionDialog.vue'
export default {
components: { releaseVersionDialog, addTaskDialog},
name: "allTasks",
data() {
return {
datasetId: 0,
//
loading: true,
//
ids: [],
//
single: true,
//
multiple: true,
tableHeight: 0,
//
showSearch: true,
//
total: 0,
//
list: [],
//
addOpen: false,
releaseOpen: false,
//
queryParams: {
pageNum: 1,
pageSize: 10,
datasetName: null,
},
};
},
created() {
this.getList();
//
this.updateTableHeight();
//
window.addEventListener("resize", this.updateTableHeight);
},
methods: {
updateTableHeight() {
//
const headerHeight = 300; //
const footerHeight = 80; //
this.tableHeight = window.innerHeight - headerHeight - footerHeight;
},
/**获取数据 **/
getList(){
this.loading =true
list(this.queryParams).then(response => {
console.log(response);
this.list = response.rows;
this.total = response.total;
this.loading =false
})
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
handleAdd(){
this.addOpen = true;
},
handleRelease(row){
this.releaseOpen = true;
this.datasetId = row.datasetId;
},
handleDimension(row){
},
handleAnnotation(row){
this.$tab.openPage("数据标注", '/dataCenter/annotationTask/dataAnnotations/index/' + row.taskId);
},
handleExamine(row){
this.$tab.openPage("数据审核", '/dataCenter/annotationTask/examine/index/' + row.taskId);
},
getPercentage(row) {
if (row.annotatedCount > 0 && row.notAnnotatedCount >= 0) {
let percentage = ((row.annotatedCount - row.notAnnotatedCount) / row.annotatedCount) * 100;
// percentage 0 100
return Math.min(Math.max(percentage, 0), 100);
}
return 0; // 0%
},
handleDelete(row){
const ids = row.datasetId || this.ids;
this.$modal.confirm('是否确认删除数据项?').then(function() {
return del(ids);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
},
//
handleSelectionChange(selection) {
this.ids = selection.map(item => item.datasetId)
this.single = selection.length!==1
this.multiple = !selection.length
},
//
handleCancel() {
this.addOpen = false;
this.releaseOpen =false;
},
}
}
</script>
<style scoped>
.demo-table-expand {
font-size: 0;
}
.demo-table-expand label {
width: 90px;
color: #99a9bf;
}
.demo-table-expand .el-form-item {
margin-right: 0;
margin-bottom: 0;
width: 50%;
}
</style>

View File

@ -0,0 +1,228 @@
<template>
<div>
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="85px">
<el-form-item label="任务标题" prop="datasetName">
<el-input
v-model="queryParams.taskName"
placeholder="请输入任务标题"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="时间" prop="time">
<el-date-picker
v-model="queryParams.time"
type="datetimerange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期">
</el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
<el-button
style="float: right"
type="primary"
plain
icon="el-icon-plus"
size="mini"
@click="handleAdd"
v-hasPermi="['dataCenter:dataSet:add']"
>新建</el-button>
</el-form>
<el-table style="width: 100%" v-loading="loading" :data="list" :height="tableHeight" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="50" align="center" />
<el-table-column type="index" label="序号" align="center" min-width="50" />
<el-table-column label="开始时间-结束时间" align="center" show-overflow-tooltip prop="datasetName"/>
<el-table-column label="任务标题" align="center" show-overflow-tooltip prop="taskName" />
<el-table-column label="负责人员" align="center" show-overflow-tooltip prop="createBy" min-width="100"/>
<el-table-column label="任务状态" show-overflow-tooltip align="center" prop="annotateTaskStatus" width="160"/>
<el-table-column label="所属数据集" show-overflow-tooltip align="center" prop="datasetDesc" />
<el-table-column label="标注进度(已标注个数/总数)" show-overflow-tooltip min-width="140" align="center" prop="createBy" />
<el-table-column label="待审核个数" show-overflow-tooltip align="center" prop="createBy" />
<el-table-column label="操作" align="center" min-width="140" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleExamine(scope.row)"
v-hasPermi="['dataCenter:sample:edit']"
>审核</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleDimension(scope.row)"
v-hasPermi="['dataCenter:sample:edit']"
>智能标注</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleAnnotation(scope.row)"
v-hasPermi="['dataCenter:sample:edit']"
>标注</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleRelease(scope.row)"
v-hasPermi="['dataCenter:sample:edit']"
>发布</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<!-- 新建数据集弹窗 -->
<add-task-dialog :open="addOpen" :get-list="getList" @dialog-cancel="handleCancel"></add-task-dialog>
<release-version-dialog :dataset-id="datasetId" :open="releaseOpen" @dialog-cancel="handleCancel"></release-version-dialog>
</div>
</template>
<script>
import { list,del,setPublic} from '@/api/dataCenter/annotationTask'
import addTaskDialog from '../dialog/addTaskDialog'
import releaseVersionDialog from '../dialog/releaseVersionDialog.vue'
export default {
components: { releaseVersionDialog, addTaskDialog},
name: "allTasks",
data() {
return {
datasetId: 0,
//
loading: true,
//
ids: [],
//
single: true,
//
multiple: true,
tableHeight: 0,
//
showSearch: true,
//
total: 0,
//
list: [],
//
addOpen: false,
releaseOpen: false,
//
queryParams: {
pageNum: 1,
pageSize: 10,
datasetName: null,
},
};
},
created() {
this.getList();
//
this.updateTableHeight();
//
window.addEventListener("resize", this.updateTableHeight);
},
methods: {
updateTableHeight() {
//
const headerHeight = 300; //
const footerHeight = 80; //
this.tableHeight = window.innerHeight - headerHeight - footerHeight;
},
/**获取数据 **/
getList(){
this.loading =true
list(this.queryParams).then(response => {
console.log(response);
this.list = response.rows;
this.total = response.total;
this.loading =false
})
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
handleAdd(){
this.addOpen = true;
},
handleRelease(row){
this.releaseOpen = true;
this.datasetId = row.datasetId;
},
handleDimension(row){
},
handleAnnotation(row){
this.$tab.openPage("数据标注", '/dataCenter/annotationTask/dataAnnotations/index/' + row.taskId);
},
handleExamine(row){
this.$tab.openPage("数据审核", '/dataCenter/annotationTask/examine/index/' + row.taskId);
},
getPercentage(row) {
if (row.annotatedCount > 0 && row.notAnnotatedCount >= 0) {
let percentage = ((row.annotatedCount - row.notAnnotatedCount) / row.annotatedCount) * 100;
// percentage 0 100
return Math.min(Math.max(percentage, 0), 100);
}
return 0; // 0%
},
handleDelete(row){
const ids = row.datasetId || this.ids;
this.$modal.confirm('是否确认删除数据项?').then(function() {
return del(ids);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
},
//
handleSelectionChange(selection) {
this.ids = selection.map(item => item.datasetId)
this.single = selection.length!==1
this.multiple = !selection.length
},
//
handleCancel() {
this.addOpen = false;
this.releaseOpen =false;
},
}
}
</script>
<style scoped>
.demo-table-expand {
font-size: 0;
}
.demo-table-expand label {
width: 90px;
color: #99a9bf;
}
.demo-table-expand .el-form-item {
margin-right: 0;
margin-bottom: 0;
width: 50%;
}
</style>

View File

@ -0,0 +1,302 @@
<template>
<div>
<el-dialog title="创建标注任务" :visible.sync="isOpen" width="700px" append-to-body @close="cancel" :close-on-click-modal="false">
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="名称" prop="taskName">
<el-input v-model="form.taskName" maxlength="20" placeholder="请输入版本名称"/>
</el-form-item>
<el-form-item label="描述" prop="taskDesc">
<el-input
type="textarea"
placeholder="请输入描述"
v-model="form.taskDesc"
maxlength="200"
show-word-limit
>
</el-input>
</el-form-item>
<el-form-item label="时间" prop="data">
<el-date-picker
style="width: 100%"
v-model="form.data"
type="datetimerange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期">
</el-date-picker>
</el-form-item>
<el-form-item label="标注类型" prop="annotateType" >
<el-select v-model="form.annotateType" placeholder="请选择标注类型" clearable style="width: 100%">
<el-option v-for="dict in dict.type.ai_annotate_type" :key="dict.value" :label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="标签" prop="label" >
<treeselect v-model="form.label" :disable-branch-nodes="true" :multiple="true" :options="labelsListOptions" :normalizer="normalizer" placeholder="请选择标签" />
</el-form-item>
<el-form-item label="数据集" prop="datasetId" >
<el-select v-model="form.datasetId" placeholder="请选择数据集" clearable style="width: 100%">
<el-option v-for="dict in datasetList" :key="dict.datasetId" :label="dict.datasetName"
:value="dict.datasetId"
/>
</el-select>
</el-form-item>
<el-form-item label="是否启用标注团队" prop="isStartTeam" label-width="130px">
<el-switch
v-model="form.isStartTeam"
>
</el-switch>
</el-form-item>
<div v-show="form.isStartTeam">
<el-form-item label="选择标注人员" prop="isStartTeam" label-width="100px">
<el-button type="primary" @click="selectOpen=true">选择标注人员</el-button>
</el-form-item>
<el-form-item label="分配数量" prop="isStartTeam" label-width="100px">
<el-table
size="mini"
:data="tableData"
>
<el-table-column
label="姓名"
prop="name"
align="center"
max-width="180"
>
</el-table-column>
<el-table-column
prop="type"
label="角色"
align="center"
max-width="180"
>
<template slot-scope="scope">
<span>{{ scope.row.type === '0' ? '审核人员' : '标注人员' }}</span>
</template>
</el-table-column>
<el-table-column
prop="num"
align="center"
label="数量"
max-width="50"
>
<template slot-scope="scope">
<el-input-number
v-model="scope.row.num"
:min="1"
:max="scope.row.type === '0' ? getReviewerMax(scope.row) : getLabelingMax(scope.row)"
style="width: 150px"
></el-input-number>
</template>
</el-table-column>
</el-table>
</el-form-item>
</div>
</el-form>
<template #footer>
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</template>
</el-dialog>
<select-personnel-dialog dataset-id="" :open="selectOpen" @dialog-cancel="handleCancel" @update-data="updateData"/>
</div>
</template>
<script>
import selectPersonnelDialog from '../dialog/selectPersonnelDialog.vue'
import { add } from '@/api/dataCenter/annotationTask'
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'
export default {
dicts: ['ai_annotate_type'],
components: { selectPersonnelDialog, Treeselect},
props: {
open: {
type: Boolean,
default: false,
required: true
},
},
computed: {
isOpen: {
get() {
return this.open
},
set(value) {
this.$emit('update:open', value)
}
}
},
watch:{
isOpen(newVal, oldVal){
if (newVal){
this.getTreeselect();
this.getDatasetList();
}
}
},
data() {
return {
datasetNum:0,
datasetList:[],
tableData: [],
//
labelsListOptions: [],
labels:[],
selectOpen: false,
form: {}, //
rules: {
taskName: [
{ required: true, message: '版本名称不能为空', trigger: 'blur' }
],
annotateType: [
{ required: true, message: '标注类型不能为空', trigger: 'blur' }
],
label: [
{ required: true, message: '请选择标签', trigger: 'blur' }
],
datasetId:[
{ required: true, message: '请选择数据集', trigger: 'blur' }
],
data:[
{ required: true, message: '请选择时间', trigger: 'blur' }
]
}
}
},
methods: {
normalizer(node) {
if (node.children && !node.children.length) {
delete node.children;
}
return {
id: node.labelId,
label: node.labelName,
children: node.children
};
},
getDatasetList(){
datasetList({}).then(response => {
this.datasetList = response.data;
})
},
/** 查询样本标签管理下拉树结构 */
getTreeselect() {
listDataSetLabels({}).then(response => {
console.log(response)
this.labelsListOptions = [];
const menu = { labelId: 0, labelName: '主类目', children: [] };
menu.children = this.handleTree(response.data, "labelId");
this.labelsListOptions.push(menu);
});
},
getReviewerMax(row) {
// this.datasetNum tableData
const totalAssigned = this.tableData
.filter(item => item.type === '0' && item.id !== row.id)
.reduce((sum, item) => sum + item.num, 0);
return this.datasetNum - totalAssigned;
},
getLabelingMax(row) {
const totalAssigned = this.tableData
.filter(item => item.type === '1' && item.id !== row.id)
.reduce((sum, item) => sum + item.num, 0);
return this.datasetNum - totalAssigned;
},
//
submitForm() {
if (this.tableData.length === 0 && this.form.isStartTeam) {
this.$modal.msgWarning('请选择人员')
return;
}
this.$refs['form'].validate(valid => {
if (valid) {
this.form.labels = this.form.label.join(",")
if (this.form.isStartTeam) {
let annotators = [];
let reviewers = [];
this.tableData.forEach((item) => {
if (item.type === '0') {
reviewers.push(item);
}
if (item.type === '1') {
annotators.push(item);
}
})
this.form.annotators = annotators;
this.form.reviewers = annotators;
}
add(this.form).then(response => {
this.$modal.msgSuccess('发布成功')
this.cancel()
})
}
})
},
updateData(data) {
this.tableData = []
let num = Math.max(data.selectReviewer.length, data.selectLabeling.length)
let minNum = Math.floor(this.datasetNum / num) //
if (minNum < 1) {
this.$modal.msgWarning('每人最少分配1张');
return;
}
// Reviewer
let reviewerRemainder = this.datasetNum % data.selectReviewer.length //
let reviewerNum = Math.floor(this.datasetNum / data.selectReviewer.length) //
data.selectReviewer.forEach(item => {
item.num = reviewerNum //
if (reviewerRemainder > 0) {
item.num += 1 // 1
reviewerRemainder-- //
}
this.tableData.push(item)
})
// Labeling
let labelingRemainder = this.datasetNum % data.selectLabeling.length //
let labelingNum = Math.floor(this.datasetNum / data.selectLabeling.length) //
data.selectLabeling.forEach(item => {
item.num = labelingNum //
if (labelingRemainder > 0) {
item.num += 1 // 1
labelingRemainder-- //
}
this.tableData.push(item)
})
},
//
cancel() {
this.isOpen = false
this.reset()
this.$emit('dialog-cancel') //
},
//
reset() {
this.tableData = [];
this.form = {} //
this.$refs.form.resetFields() // Element UI
},
//
handleCancel() {
this.selectOpen = false
}
}
}
</script>
<style scoped lang="scss">
/* 样式按需定义 */
/* 提升优先级 */
.vue-treeselect--has-value .vue-treeselect__input {
line-height: inherit !important;
vertical-align: baseline !important;
}
</style>

View File

@ -0,0 +1,169 @@
<template>
<div style="height:calc( 100vh - 130px);display: flex;flex-direction: row;align-items: stretch;">
<div class="left-panel">
</div>
<div class="center-panel">
<div id="label-studio-container" class="label-studio-wrapper">
</div>
</div>
</div>
</template>
<script>
import LabelStudio from "label-studio";
import "label-studio/build/static/css/main.css";
export default {
name: "LabelStudioWrapper",
props: {
taskData: {
type: Object,
required: true, // URL
},
config: {
type: String,
required: true, //
},
},
data() {
return {
labelStudioInstance: null,
scale: 1, //
};
},
mounted() {
this.initializeLabelStudio();
this.setupImageZoom(); //
},
methods: {
initializeLabelStudio() {
this.labelStudioInstance = new LabelStudio("label-studio-container", {
config: this.config, // 使
task: this.taskData, // 使
customButtons: [
{
text: "保存标注",
onClick: this.handleSaveClick, //
},
{
text: "清空标注",
onClick: this.handleClearClick,
},
],
onLabelStudioLoad: (LS) => {
const annotation = LS.annotationStore.addAnnotation({
userGenerate: true,
});
LS.annotationStore.selectAnnotation(annotation.id);
},
onSubmitAnnotation: (annotationData) => {
this.handleAnnotationSubmit(annotationData);
},
});
},
handleAnnotationSubmit(annotationData) {
//
console.log("标注结果:", annotationData);
},
//
handleSaveClick() {
const annotationData = this.labelStudioInstance.annotationStore.serializeAnnotations();
console.log("保存标注数据:", annotationData);
this.sendAnnotationToServer(annotationData);
},
//
handleClearClick() {
console.log("清空标注");
this.labelStudioInstance.annotationStore.clearAnnotations();
},
sendAnnotationToServer(annotationData) {
// POST
this.$http.post("/api/save-annotation", annotationData)
.then(response => {
console.log("标注数据已保存", response.data);
})
.catch(error => {
console.error("保存标注数据失败", error);
});
},
//
setupImageZoom() {
const image = document.getElementById("image");
const imageContainer = document.getElementById("image-container");
imageContainer.addEventListener("wheel", (event) => {
event.preventDefault();
if (event.deltaY > 0) {
this.scale = Math.max(0.5, this.scale - 0.1); //
} else {
this.scale = Math.min(3, this.scale + 0.1); //
}
image.style.transform = `scale(${this.scale})`; //
});
},
},
beforeDestroy() {
if (this.labelStudioInstance && this.labelStudioInstance.destroy) {
this.labelStudioInstance.destroy();
this.labelStudioInstance = null;
}
},
};
</script>
<style>
.left-panel{
width: 30%;
}
.center-panel{
width: 70%;
}
.label-studio-wrapper,.ls-editor{
width: 100%;
height: 100vh;
}
/* 自定义按钮样式 */
.save-button {
background-color: #4CAF50;
color: white;
padding: 10px;
border: none;
border-radius: 5px;
cursor: pointer;
}
.clear-button {
background-color: #f44336;
color: white;
padding: 10px;
border: none;
border-radius: 5px;
cursor: pointer;
}
.save-button:hover,
.clear-button:hover {
opacity: 0.8;
}
/* 图片容器 */
.image-container {
width: 100%;
height: 80vh; /* 设置容器的高度,可以根据需要调整 */
overflow: hidden;
position: relative;
display: flex;
justify-content: center;
align-items: center;
background-color: #f0f0f0;
}
#image {
transition: transform 0.25s ease; /* 缩放时平滑过渡 */
max-width: 100%;
max-height: 100%;
object-fit: contain;
}
</style>

View File

@ -0,0 +1,152 @@
<template>
<div>
<el-dialog
title="选择人员"
:visible.sync="isOpen"
width="500px"
append-to-body
@close="cancel"
:close-on-click-modal="false"
>
<el-form ref="form" :model="form" label-position="top" :rules="rules">
<el-form-item label="审核人员" prop="selectReviewer">
<el-select v-model="form.selectReviewer" multiple placeholder="请选择审核人员" clearable style="width: 100%">
<el-option
v-for="item in reviewer"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="标注人员" prop="selectLabeling">
<el-select v-model="form.selectLabeling" multiple placeholder="请选择标注人员" clearable style="width: 100%">
<el-option
v-for="item in labeling"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</template>
</el-dialog>
</div>
</template>
<script>
import { getAllList } from "@/api/dataCenter/teamMember";
export default {
props: {
open: {
type: Boolean,
default: false,
required: true,
},
},
computed: {
isOpen: {
get() {
return this.open;
},
set(value) {
this.$emit("update:open", value);
},
},
},
watch: {
open(newVal) {
if (newVal) {
this.getList();
}
},
},
data() {
return {
list:[],
reviewer: [], //
labeling: [], //
form:{
selectLabeling: [], //
selectReviewer: [], //
},
rules: {
selectReviewer: [
{ required: true, message: "请选择审核人员", trigger: "blur", type: 'array'},
],
selectLabeling: [
{ required: true, message: "请选择标注人员", trigger: "blur" ,type: 'array'},
],
},
};
},
methods: {
//
getList() {
getAllList().then((res) => {
//
this.reviewer = [];
this.labeling = [];
this.list =[];
this.list =res.data;
//
res.data.forEach((item) => {
const roleIds = item.roleId.split(",");
if (roleIds.includes("0")) {
//
this.labeling.push({ id: item.memberId, name: item.userName });
}
if (roleIds.includes("1")) {
//
this.reviewer.push({ id: item.memberId, name: item.userName });
}
});
});
},
//
submitForm() {
this.$refs["form"].validate((valid) => {
if (valid) {
const data = {
selectReviewer:[],
selectLabeling:[],
};
this.form.selectReviewer.forEach((id) => {
const result = this.reviewer.find(item => item.id === id);
data.selectReviewer.push({id: result.id, name: result.name,num:0,type:'0'});
})
this.form.selectLabeling.forEach((id) => {
const result = this.labeling.find(item => item.id === id);
console.log(result)
data.selectLabeling.push({id: result.id, name: result.name,num:0,type:'1'});
})
this.$emit('update-data', data); //
this.cancel();
}
});
},
//
cancel() {
this.isOpen = false; //
this.reset(); //
this.$emit("dialog-cancel"); //
},
//
reset() {
this.selectReviewer = []; //
this.selectLabeling = []; //
this.$refs.form.resetFields(); //
},
},
};
</script>
<style scoped>
/* 样式按需定义 */
</style>

View File

@ -0,0 +1,64 @@
<template>
<div class="app-container ">
<el-tabs type="border-card" @tab-click="handleClick">
<!-- 我的文件 -->
<el-tab-pane label="全部任务">
<div v-if="activeTab === 'allTasks'">
<all-tasks v-if="activeTab === 'allTasks'"/>
</div>
</el-tab-pane>
<el-tab-pane label="我发起的">
<div v-if="activeTab === 'initiatedTasks'">
<initiatedTasks v-if="activeTab === 'initiatedTasks'" />
</div>
</el-tab-pane>
<el-tab-pane label="我参与的">
<div v-if="activeTab === 'participatedTasks'">
<participatedTasks v-if="activeTab === 'participatedTasks'" />
</div>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
import allTasks from './child/allTasks.vue'
import initiatedTasks from './child/initiatedTasks.vue'
import participatedTasks from './child/participatedTasks.vue'
export default {
components: {allTasks,initiatedTasks,participatedTasks},
data() {
return {
activeTab: 'allTasks' // ""
}
},
methods: {
handleClick(tab, event) {
// activeTab
if (tab.label === '全部任务') {
this.activeTab = 'allTasks';
} else if (tab.label === '我发起的') {
this.activeTab = 'initiatedTasks';
}else if (tab.label === '我参与的') {
this.activeTab = 'participatedTasks';
}
}
}
}
</script>
<style scoped lang="scss">
.app-container {
.el-tabs {
margin-bottom: 10px;
height: calc(100vh - 140px);
}
.el-tab-pane {
padding: 20px;
background-color: #f9f9f9;
border-radius: 4px;
}
}
</style>

View File

@ -43,18 +43,18 @@
<el-form-item label="上传文件" prop="dataSource">
<uploadFiles :disabled="form.inputId===0" :parent-id="form.inputId.toString()" :fileUrl="form.inputPath || ''"/>
</el-form-item>
<el-form-item label="数据来源" prop="isAnnotated">
<el-radio-group v-model="form.isAnnotated">
<el-radio :label="0" border>未标注</el-radio>
<el-radio :label="1" border>已标注</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-show="form.isAnnotated === 1">
<div style="display: flex;flex-direction: column;">
<span>文件存放方式满足YOLO格式</span>
<img style="width: 50%" :src="ObjectDetection_YOLO" alt="Weibo">
</div>
</el-form-item>
<!-- <el-form-item label="数据来源" prop="isAnnotated">-->
<!-- <el-radio-group v-model="form.isAnnotated">-->
<!-- <el-radio :label="0" border>未标注</el-radio>-->
<!-- <el-radio :label="1" border>已标注</el-radio>-->
<!-- </el-radio-group>-->
<!-- </el-form-item>-->
<!-- <el-form-item v-show="form.isAnnotated === 1">-->
<!-- <div style="display: flex;flex-direction: column;">-->
<!-- <span>文件存放方式满足YOLO格式</span>-->
<!-- <img style="width: 50%" :src="ObjectDetection_YOLO" alt="Weibo">-->
<!-- </div>-->
<!-- </el-form-item>-->
</div>
<el-form-item label="输出路径" prop="outputPath">
<el-input placeholder="请选择数据集输出路径" readonly :disabled="true" v-model="form.outputPath" class="input-with-select">
@ -136,6 +136,7 @@ export default {
methods: {
//
submitForm() {
this.$refs["form"].validate(valid => {
if (valid) {
add(this.form).then(response => {

View File

@ -17,6 +17,16 @@
>
</el-input>
</el-form-item>
<el-form-item label="时间" prop="data">
<el-date-picker
style="width: 100%;"
v-model="form.data"
type="datetimerange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期">
</el-date-picker>
</el-form-item>
<el-form-item label="标注类型" prop="annotateType" >
<el-select v-model="form.annotateType" placeholder="请选择标注类型" clearable style="width: 100%">
<el-option v-for="dict in dict.type.ai_annotate_type" :key="dict.value" :label="dict.label"
@ -25,12 +35,8 @@
</el-select>
</el-form-item>
<el-form-item label="选择标签" prop="annotateType" >
<el-select v-model="form.labels" multiple placeholder="请选择标签" clearable style="width: 100%">
<el-option v-for="dict in dict.type.ai_annotate_type" :key="dict.value" :label="dict.label"
:value="dict.value"
/>
</el-select>
<el-form-item label="标签" prop="label" >
<treeselect v-model="form.label" :disable-branch-nodes="true" :multiple="true" :options="labelsListOptions" :normalizer="normalizer" placeholder="请选择父节点" />
</el-form-item>
<el-form-item label="是否启用标注团队" prop="isStartTeam" label-width="130px">
<el-switch
@ -97,10 +103,12 @@
import selectPersonnelDialog from '../child/selectPersonnelDialog.vue'
import { add } from '@/api/dataCenter/annotationTask'
import {listDataSetLabels } from "@/api/dataCenter/labels";
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
export default {
dicts: ['ai_annotate_type'],
components: { selectPersonnelDialog },
components: { selectPersonnelDialog, Treeselect},
props: {
open: {
type: Boolean,
@ -128,9 +136,20 @@ export default {
}
}
},
watch:{
isOpen(newVal, oldVal){
if (newVal){
this.getTreeselect();
}
}
},
data() {
return {
tableData: [],
//
labelsListOptions: [],
labels:[],
selectOpen: false,
form: {}, //
rules: {
@ -138,15 +157,38 @@ export default {
{ required: true, message: '版本名称不能为空', trigger: 'blur' }
],
annotateType: [
{ required: true, message: '版本名称不能为空', trigger: 'blur' }
{ required: true, message: '标注类型不能为空', trigger: 'blur' }
],
labels: [
{ required: true, message: '版本名称不能为空', trigger: 'blur' }
label: [
{ required: true, message: '请选择标签', trigger: 'blur' }
],
data:[
{ required: true, message: '请选择时间', trigger: 'blur' }
]
}
}
},
methods: {
normalizer(node) {
if (node.children && !node.children.length) {
delete node.children;
}
return {
id: node.labelId,
label: node.labelName,
children: node.children
};
},
/** 查询样本标签管理下拉树结构 */
getTreeselect() {
listDataSetLabels({}).then(response => {
console.log(response)
this.labelsListOptions = [];
const menu = { labelId: 0, labelName: '主类目', children: [] };
menu.children = this.handleTree(response.data, "labelId");
this.labelsListOptions.push(menu);
});
},
getReviewerMax(row) {
// this.datasetNum tableData
const totalAssigned = this.tableData
@ -168,6 +210,7 @@ export default {
}
this.$refs['form'].validate(valid => {
if (valid) {
this.form.labels=this.form.label.join(",")
if(this.form.isStartTeam){
let annotators = [];
let reviewers =[];
@ -246,4 +289,9 @@ export default {
<style scoped lang="scss">
/* 样式按需定义 */
/* 提升优先级 */
.vue-treeselect--has-value .vue-treeselect__input {
line-height: inherit !important;
vertical-align: baseline !important;
}
</style>

View File

@ -92,7 +92,7 @@
/>
<!-- 导入 -->
<import-dialog :get-list="getList" :open="importOpen" :dataset-id="datasetId" @dialog-cancel="handleCancel"/>
<import-dialog :get-list="getList" :open="importOpen" :data-type="dataType" :dataset-id="datasetId.toString()" @dialog-cancel="handleCancel"/>
</div>
</template>
<script>
@ -105,7 +105,8 @@ export default {
name: "DataSet",
data() {
return {
datasetId: 0,
dataType:'0',
datasetId: '0',
//
loading: true,
//
@ -132,6 +133,7 @@ export default {
};
},
created() {
this.dataType = this.$route.query && this.$route.query.dataType;
this.datasetId = this.$route.params && this.$route.params.dataSetId;
this.getList();
//
@ -151,7 +153,6 @@ export default {
this.loading =true
this.queryParams.dataSetId = this.datasetId;
list(this.queryParams).then(response => {
console.log(response);
this.list = response.rows;
this.total = response.total;
this.loading =false
@ -173,7 +174,7 @@ export default {
this.handleQuery();
},
handleAdd(){
this.addOpen = true;
this.importOpen = true;
},
handleDelete(row){
const datasetId = this.datasetId;

View File

@ -1,7 +1,7 @@
<template>
<div>
<el-dialog title="导入" :visible.sync="isOpen" width="700px" append-to-body :modal="false" @close="cancel" :close-on-click-modal="false">
<el-form ref="form" :model="form" :rules="rules" label-width="100px">
<el-form ref="form" :model="form" :rules="importRules" label-width="100px">
<el-form-item label="数据来源" prop="dataSource" >
<el-radio-group v-model="form.dataSource">
<el-radio :label="0" border>系统选择</el-radio>
@ -14,10 +14,10 @@
</el-input>
</el-form-item>
<div v-show="form.dataSource ===1">
<el-form-item label="上传文件" prop="dataSource">
<el-form-item label="上传文件">
<uploadFiles :disabled="form.inputId===0" :parent-id="form.inputId.toString()" :fileUrl="form.inputPath || ''"/>
</el-form-item>
<el-form-item label="数据来源" prop="isAnnotated">
<!-- <el-form-item label="数据标注状态" prop="isAnnotated">
<el-radio-group v-model="form.isAnnotated">
<el-radio :label="0" border>未标注</el-radio>
<el-radio :label="1" border>已标注</el-radio>
@ -28,13 +28,8 @@
<span>文件存放方式满足YOLO格式</span>
<img style="width: 50%" :src="ObjectDetection_YOLO" alt="Weibo">
</div>
</el-form-item>
</el-form-item>-->
</div>
<el-form-item label="输出路径" prop="outputPath">
<el-input placeholder="请选择数据集输出路径" readonly :disabled="true" v-model="form.outputPath" class="input-with-select">
<el-button slot="append" icon="el-icon-folder-opened" @click="selectFolder(1)"></el-button>
</el-input>
</el-form-item>
</el-form>
<template #footer>
<el-button type="primary" @click="submitForm"> </el-button>
@ -50,6 +45,7 @@
import ObjectDetection_YOLO from '@/assets/images/ObjectDetection_YOLO.png'
import selectFolderDialog from './selectFolderDialog.vue'
import uploadFiles from '../../library/child/uploadFiles.vue'
import { importData } from '@/api/dataCenter/dataSet'
export default {
components: { uploadFiles, selectFolderDialog},
@ -58,12 +54,17 @@ export default {
type: Boolean,
default: false,
},
dataType:{
type: String,
default: '',
required: true
},
getList: {
type: Function,
required: true,
},
datasetId: {
type: Number,
type: String,
default: 0,
required: true
}
@ -80,41 +81,43 @@ export default {
},
data() {
return {
type: this.dataType, //
ObjectDetection_YOLO:ObjectDetection_YOLO,
folderOpen: false,
folderType:0,
form: {
dataType:0,
dataSource:0,
inputId:0,
outputId:0
inputPath:'',
}, //
rules:{
importRules:{
dataSource:[
{ required: true, message: '数据来源不能为空', trigger: 'blur' },
],
inputPath:[
{ required: true, message: '导入路径不能为空', trigger: 'blur' },
],
outputPath:[
{ required: true, message: '输出路径不能为空', trigger: 'blur' },
],
]
}
};
},
methods: {
//
submitForm() {
this.form.datasetId = this.datasetId
this.$refs["form"].validate(valid => {
if (valid) {
console.log(this.form)
this.form.datasetId = this.datasetId
this.form.dataType = this.dataType
importData(this.form).then(response => {
this.$modal.msgSuccess("导入成功");
this.cancel();
})
}
})
//
},
//
cancel() {
this.getList();
this.isOpen = false;
this.reset();
this.$emit('dialog-cancel'); //
@ -122,10 +125,10 @@ export default {
//
reset() {
this.form = {
dataType:0,
dataSource:0,
inputId:0,
outputId:0}; //
outputId:0
}; //
this.$refs.form.resetFields(); // Element UI
},
//
@ -137,12 +140,10 @@ export default {
this.folderOpen = false;
},
updateData(data){
if (data.folderType === 0) {
this.form.inputPath = data.folder;
this.form.inputId = data.fileId
}else {
this.form.outputPath = data.folder;
this.form.outputId = data.fileId
this.form.inputPath = data.folder || ''; // inputPath
this.form.inputId = data.fileId || 0;
}
}
},

View File

@ -26,17 +26,6 @@
v-hasPermi="['dataCenter:dataSet:add']"
>新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="el-icon-edit"
size="mini"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['dataCenter:dataSet:edit']"
>修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
@ -56,7 +45,10 @@
<el-table-column type="index" label="序号" min-width="50" />
<el-table-column label="名称" align="center" show-overflow-tooltip prop="datasetName">
<template slot-scope="scope">
<router-link :to="'/dataCenter/dataSet-details/index/' + scope.row.datasetId" class="link-type">
<router-link :to="{
path: '/dataCenter/dataSet-details/index/' + scope.row.datasetId,
query: { dataType: scope.row.dataType }
}" class="link-type">
<span>{{ scope.row.datasetName }}</span>
</router-link>
</template>
@ -112,16 +104,9 @@
size="mini"
type="text"
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
@click="downloadFile(scope.row)"
v-hasPermi="['dataCenter:sample:edit']"
>导出</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleRelease(scope.row)"
v-hasPermi="['dataCenter:sample:edit']"
>发布</el-button>
<el-button
size="mini"
type="text"
@ -149,18 +134,15 @@
/>
<!-- 添加对话框 -->
<add-data-set-dialog :get-list="getList" :open="addOpen" @dialog-cancel="handleCancel"/>
<!--版本发布-->
<release-version-dialog :dataset-id="datasetId" :open="releaseOpen" @dialog-cancel="handleCancel"/>
<!--创建标注任务-->
<annotation-task-dialog :datasetNum="datasetNum" :dataset-id="datasetId" :open="annotationOpen" @dialog-cancel="handleCancel"/>
<!-- 导入 -->
<import-dialog :get-list="getList" :open="importOpen" :dataset-id="datasetId" @dialog-cancel="handleCancel"/>
<import-dialog :get-list="getList" :open="importOpen" :data-type="dataType" :dataset-id="datasetId.toString()" @dialog-cancel="handleCancel"/>
</div>
</template>
<script>
import addDataSetDialog from '../child/addDataSetDialog.vue'
import releaseVersionDialog from '../child/releaseVersionDialog.vue'
import annotationTaskDialog from '../child/annotationTaskDialog.vue'
import importDialog from '../child/importDialog.vue'
@ -168,10 +150,11 @@ import { list,del,setPublic} from '@/api/dataCenter/dataSet'
export default {
components: { addDataSetDialog,releaseVersionDialog,annotationTaskDialog,importDialog},
components: { addDataSetDialog,annotationTaskDialog,importDialog},
name: "DataSet",
data() {
return {
dataType:'0',
datasetNum:0,
datasetId: 0,
//
@ -239,10 +222,6 @@ export default {
handleAdd(){
this.addOpen = true;
},
handleRelease(row){
this.releaseOpen = true;
this.datasetId = row.datasetId;
},
handleAnnotation(row){
this.annotationOpen = true;
this.datasetId = row.datasetId;
@ -251,6 +230,7 @@ export default {
handleImport(row){
this.importOpen = true;
this.datasetId = row.datasetId;
this.dataType = row.dataType;
},
handleUpdate(){
@ -276,9 +256,9 @@ export default {
}).catch(() => {});
},
change(row){
let text = row.isPublic === '0' ? '不共享' : '共享'
let text = row.isPublic === '0' ? '不公开' : '公开'
this.$modal.confirm('确认要' + text + '吗?').then(function() {
return setPublic(row.fileId, row.isPublic)
return setPublic(row.datasetId, row.isPublic)
}).then(() => {
this.$modal.msgSuccess('操作成功')
}).catch(function() {
@ -294,10 +274,13 @@ export default {
//
handleCancel() {
this.addOpen = false;
this.releaseOpen= false;
this.annotationOpen = false;
this.importOpen = false;
},
downloadFile(row) {
const datasetId = row.datasetId;
this.download('/ai/dataset/export/'+datasetId,{},`${new Date().getTime()}.zip`,{timeout: 600000})
}
}
}
</script>

View File

@ -15,80 +15,49 @@
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-plus"
size="mini"
@click="handleAdd"
v-hasPermi="['dataCenter:dataSet:add']"
>新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="el-icon-edit"
size="mini"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['dataCenter:dataSet:edit']"
>修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['dataCenter:dataSet:remove']"
>删除</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table style="width: 100%" v-loading="loading" :data="list" :height="tableHeight" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="50" align="center" />
<el-table-column type="index" label="序号" min-width="50" />
<el-table-column label="名称" align="center" show-overflow-tooltip prop="datasetName">
<template slot-scope="scope">
<router-link :to="{
path: '/dataCenter/dataSet-details/index/' + scope.row.datasetId,
query: { dataType: scope.row.dataType }
}" class="link-type">
<span>{{ scope.row.datasetName }}</span>
</router-link>
</template>
</el-table-column>
<el-table-column label="版本名称" align="center" show-overflow-tooltip prop="sampleFormat" />
<el-table-column label="标注进度" align="center" show-overflow-tooltip prop="createBy" min-width="100">
<template slot-scope="scope">
<div style="display: flex;align-items: center;justify-content: center;">
<!-- 使用 v-if 确保当 annotatedCount > 0 时渲染进度条 -->
<el-progress
v-if="scope.row.annotatedCount > 0"
color="#13ce66"
:percentage="getPercentage(scope.row)"
style="width: 150px;"/>
<!-- 显示标注进度 -->
<span>({{ (scope.row.annotatedCount - scope.row.notAnnotatedCount) + '/' + scope.row.annotatedCount }})</span>
</div>
</template>
<el-table v-loading="loading" :data="list" :height="tableHeight" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column type="index" label="序号" width="80" />
<el-table-column label="名称" align="center" prop="sampleName" />
<el-table-column label="版本名称" align="center" prop="sampleFormat" />
<el-table-column label="标注进度" align="center" prop="createBy" />
<el-table-column label="创建时间" align="center" prop="createTime" width="160">
</el-table-column>
<el-table-column label="创建时间" show-overflow-tooltip align="center" prop="createTime" width="160">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="描述" align="center" prop="createBy" />
<el-table-column label="是否公开" align="center" prop="createBy" >
<el-table-column label="描述" show-overflow-tooltip align="center" prop="datasetDesc" />
<el-table-column label="是否公开" show-overflow-tooltip align="center" prop="createBy" >
<template slot-scope="scope">
<el-switch v-model="scope.row.isPublic" active-value="1" inactive-value="0"
@change="change(scope.row)"
></el-switch>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['dataCenter:sample:edit']"
>修改</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
v-hasPermi="['dataCenter:sample:remove']"
>删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination
@ -101,10 +70,17 @@
</div>
</template>
<script>
import { list,setPublic} from '@/api/dataCenter/dataSet'
export default {
name: "Sample",
name: "DataSet",
data() {
return {
dataType:'0',
datasetNum:0,
datasetId: 0,
//
loading: true,
//
@ -120,29 +96,18 @@ export default {
total: 0,
//
list: [],
//
title: "",
//
open: false,
addOpen: false,
releaseOpen: false,
annotationOpen:false,
importOpen:false,
//
queryParams: {
pageNum: 1,
pageSize: 10,
datasetName: null,
isPublic:'1'
},
//
form: {},
//
rules: {
sampleName: [
{ required: true, message: '样本类型名称不能为空', trigger: 'blur' },
{ min: 2, max: 20, message: '样本类型名称长度必须介于 2 和 20 之间', trigger: 'blur' }
],
sampleFormat: [
{ required: true, message: '格式不能为空', trigger: 'blur' },
{ min: 1, max: 20, message: '格式长度必须介于 1 和 20 之间', trigger: 'blur' }
],
}
};
},
created() {
@ -161,7 +126,13 @@ export default {
},
/**获取数据 **/
getList(){
this.loading =false
this.loading =true
list(this.queryParams).then(response => {
console.log(response);
this.list = response.rows;
this.total = response.total;
this.loading =false
})
},
/** 搜索按钮操作 */
handleQuery() {
@ -174,24 +145,49 @@ export default {
this.handleQuery();
},
handleAdd(){
this.addOpen = true;
},
handleUpdate(){
getPercentage(row) {
if (row.annotatedCount > 0 && row.notAnnotatedCount >= 0) {
let percentage = ((row.annotatedCount - row.notAnnotatedCount) / row.annotatedCount) * 100;
// percentage 0 100
return Math.min(Math.max(percentage, 0), 100);
}
return 0; // 0%
},
handleDelete(){
},
change(){
change(row){
let text = row.isPublic === '0' ? '不公开' : '公开'
this.$modal.confirm('确认要' + text + '吗?').then(function() {
return setPublic(row.datasetId, row.isPublic)
}).then(() => {
this.$modal.msgSuccess('操作成功')
this.getList();
}).catch(function() {
row.isPublic = row.isPublic === '0' ? '1' : '0'
})
},
//
handleSelectionChange(selection) {
this.ids = selection.map(item => item.id)
this.ids = selection.map(item => item.datasetId)
this.single = selection.length!==1
this.multiple = !selection.length
},
}
}
}
</script>
<style scoped>
.demo-table-expand {
font-size: 0;
}
.demo-table-expand label {
width: 90px;
color: #99a9bf;
}
.demo-table-expand .el-form-item {
margin-right: 0;
margin-bottom: 0;
width: 50%;
}
</style>

View File

@ -184,6 +184,7 @@ export default {
this.labelsListOptions.push(menu);
});
},
//
cancel() {
this.open = false;