From 72b025d6b43271ae88541ea23c92070b3b2acc96 Mon Sep 17 00:00:00 2001 From: sd <shidong@jhsoft.cc> Date: 星期二, 05 八月 2025 16:15:44 +0800 Subject: [PATCH] 模型训练-批量批注、批量删除以及批量导入功能实现 --- src/pages/modelTuning/components/rightCardList.vue | 403 +++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 files changed, 359 insertions(+), 44 deletions(-) diff --git a/src/pages/modelTuning/components/rightCardList.vue b/src/pages/modelTuning/components/rightCardList.vue index b07c05d..0b83799 100644 --- a/src/pages/modelTuning/components/rightCardList.vue +++ b/src/pages/modelTuning/components/rightCardList.vue @@ -1,5 +1,39 @@ <template> <div class="image-gallery"> + <!-- 娣诲姞瀵煎叆缁勪欢 --> + <BatchImport ref="batchImport" :show-type-selector="false" @import="handleImportFiles" /> + <!-- 妯″瀷璁粌寮圭獥 --> + <el-dialog title="妯″瀷璁粌" :visible.sync="trainDialogVisible" width="372px" top="10vh"> + <div class="sample-info"> + <div class="info-label">鏍锋湰淇℃伅</div> + <div class="sample-count">姝f牱鏈暟閲忥細{{ positiveCount }}</div> + <div class="sample-count">璐熸牱鏈暟閲忥細{{ negativeCount }}</div> + </div> + <div slot="footer" class="dialog-footer"> + <el-button @click="trainDialogVisible = false">鍙栨秷</el-button> + <el-button type="primary" @click="startTraining">寮�濮嬭缁�</el-button> + </div> + </el-dialog> + + <!-- 鎵归噺鏍囨敞寮圭獥 --> + <el-dialog title="鎵归噺鏍囨敞" :visible.sync="batchLabelDialogVisible" width="472px" top="10vh"> + <div class="label-options"> + <div class="label-option" :class="{ active: batchLabelStatus === 1 }" @click="batchLabelStatus = 1"> + 姝g‘ + </div> + <div class="label-option" :class="{ active: batchLabelStatus === 2 }" @click="batchLabelStatus = 2"> + 閿欒 + </div> + <div class="label-option" :class="{ active: batchLabelStatus === 0 }" @click="batchLabelStatus = 0"> + 涓嶇‘瀹� + </div> + </div> + <div slot="footer" class="dialog-footer"> + <el-button type="danger" @click="handleBatchDelete">鎵归噺鍒犻櫎</el-button> + <el-button @click="batchLabelDialogVisible = false">鍙栨秷</el-button> + <el-button type="primary" @click="confirmBatchLabeling">纭畾</el-button> + </div> + </el-dialog> <!-- 椤堕儴绛涢�夊尯鍩� --> <div class="filter-section"> <el-form :inline="true" class="filter-form"> @@ -38,13 +72,13 @@ <el-button type="primary" class="action-btn"> <i class="el-icon-download"></i> 瀵煎叆 </el-button> - <el-dropdown-menu slot="dropdown"> - <el-dropdown-item>姝f牱鏈�</el-dropdown-item> - <el-dropdown-item>璐熸牱鏈�</el-dropdown-item> - <el-dropdown-item>寰呮爣璁版牱鏈�</el-dropdown-item> + <el-dropdown-menu slot="dropdown" v-if="!this.trainId == 0"> + <el-dropdown-item @click.native="openImportDialog(1)">姝f牱鏈�</el-dropdown-item> + <el-dropdown-item @click.native="openImportDialog(2)">璐熸牱鏈�</el-dropdown-item> + <el-dropdown-item @click.native="openImportDialog(3)">寰呮爣璁版牱鏈�</el-dropdown-item> </el-dropdown-menu> </el-dropdown> - <el-button type="primary" class="action-btn"> + <el-button type="primary" @click="openTrainDialog" class="action-btn"> 妯″瀷璁粌 </el-button> </div> @@ -54,7 +88,8 @@ <!-- 鎵归噺鎿嶄綔鎺у埗鏍� --> <div class="batch-controls" v-if="isBatchMode"> <div class="select-all"> - <el-checkbox v-model="selectAll" @change="toggleSelectAll">鍏ㄩ��</el-checkbox> + <el-checkbox :indeterminate="isIndeterminate" v-model="selectAll" + @change="toggleSelectAll">鍏ㄩ��</el-checkbox> </div> <!-- <div>宸查�夋嫨 {{ selectedCount }} 涓」鐩�</div> --> <div class="batch-actions"> @@ -79,20 +114,31 @@ </template> <script> +import BatchImport from './batchImport'; import imageCard from './imageCard'; import Pagination from '@/components/rightPagination'; -import { getTrains, updateTrainStatus, deleteTrains } from "@/api/modelTuning"; +import { getTrains, updateTrainStatus, deleteTrains, batchUpdateTrainStatus, batchDeleteTrains, uploadDataTrainTags } from "@/api/modelTuning"; export default { components: { imageCard, Pagination, + BatchImport }, name: 'ImageGallery', data() { return { + trainDialogVisible: false, // 妯″瀷璁粌寮圭獥鍙鎬� + batchLabelDialogVisible: false, // 鎵归噺鏍囨敞寮圭獥鍙鎬� + // 妯″瀷璁粌寮圭獥鏁版嵁 + positiveCount: 0, // 姝f牱鏈暟閲� + negativeCount: 0, // 璐熸牱鏈暟閲� + // 鎵归噺鏍囨敞鐘舵�� + batchLabelStatus: 0, // 榛樿閫夋嫨"涓嶇‘瀹�" + isIndeterminate: false, + selectAll: false, pagination: {}, - trainId: null, + trainId: 0, totalCount: 0, // 鎬绘暟鎹噺 currentPage: 1, // 褰撳墠椤电爜 pageSize: 12, // 姣忛〉鏁伴噺 @@ -123,6 +169,67 @@ mounted() { }, methods: { + // 鎵撳紑瀵煎叆瀵硅瘽妗� + openImportDialog(type) { + this.$refs.batchImport.presetType = type; + this.$refs.batchImport.open(); + }, + // 澶勭悊瀵煎叆鐨勬枃浠� + async handleImportFiles({ type, files }) { + try { + this.$loading({ text: `瀵煎叆${this.getTypeName(type)}涓�...` }); + // 1. 鍒涘缓FormData鐢ㄤ簬鏂囦欢涓婁紶 + let formData = new FormData(); + // 2. 娣诲姞瀹為檯鏂囦欢鍐呭鍒癋ormData + files.forEach(item => { + formData.append('file', item); + }); + formData.append('tagId', this.trainId); + formData.append('status', type === 3 ? 0 : type); + + // console.log(formData) + // // 妯℃嫙涓婁紶璇锋眰锛堝疄闄呭簲璋冪敤API锛� + let rspc = await uploadDataTrainTags(formData) + if (rspc && rspc.status === 200) { + this.$message({ + type: 'success', + message: '鎴愬姛' + }); + this.fetchTableData() + } else { + this.$message({ + type: 'error', + message: rspc.msg + }); + + } + } catch (error) { + this.$message.error(`瀵煎叆澶辫触: ${error.message}`); + } finally { + this.$loading().close(); + } + }, + getTypeName(type) { + return { + 1: '姝f牱鏈�', + 2: '璐熸牱鏈�', + 3: '寰呮爣璁版牱鏈�' + }[type]; + }, + // 妯℃嫙鏂囦欢涓婁紶鍑芥暟 + async uploadFile(file, type) { + // 鍦ㄥ疄闄呭簲鐢ㄤ腑锛岃繖閲屽簲璇ユ槸涓�涓狝PI璋冪敤 + return new Promise((resolve) => { + const formData = new FormData(); + formData.append('file', file); + formData.append('type', type); + formData.append('trainId', this.trainId); + + // 璋冪敤涓婁紶API锛屼緥濡�: + // await uploadSample(formData); + resolve(); + }); + }, async paginationChange(params) { this.currentPage = params.page this.pageSize = params.pageSize @@ -131,11 +238,12 @@ async changeTrainId(trainId) { // console.info(trainId) this.trainId = trainId + this.isBatchMode = false await this.fetchTableData() }, // 鑾峰彇琛ㄦ牸鏁版嵁鏂规硶 async fetchTableData(params) { - console.info(this.currentPage) + // console.info(this.currentPage) this.galleryItems = [] // params.tagId = this.trainId let rspc = await getTrains({ @@ -144,15 +252,20 @@ pageSize: this.pageSize, }); if (rspc && rspc.status === 200) { - this.galleryItems = rspc.data.list; + if (rspc.data.list) { + this.galleryItems = rspc.data.list.map(item => ({ + ...item, + selected: false // 纭繚姣忎釜鍗$墖閮芥湁鍒濆鍊� + })); + } } // console.log('trainId:', this.trainId); this.totalCount = rspc.data.pagination.total // 鏇存柊鍒嗛〉鏁版嵁鍓嶅厛鏍¢獙褰撳墠椤电爜 - const totalPage = res.data.pagination.totalPage + const totalPage = rspc.data.pagination.totalPage const currentPage = this.currentPage > totalPage ? totalPage - : res.data.pagination.page + : rspc.data.pagination.page this.currentPage = currentPage, this.totalPage = totalPage }, @@ -190,13 +303,18 @@ }, //淇敼鍥剧墖鐘舵�� async handleStatusChange(parm) { - console.log('淇敼鐘舵��', parm); + // console.log('淇敼鐘舵��', parm); let rspc = await updateTrainStatus(parm); if (rspc && rspc.status === 200) { this.$message({ type: 'success', message: '鎴愬姛' }); + // for (let i = 0; i < this.galleryItems.length; i++) { + // if (parm.trainId === this.galleryItems[i].trainId) { + // this.galleryItems[i].status = parm.status + // } + // } this.fetchTableData() } else { this.$message({ @@ -230,20 +348,9 @@ }, // 杩涘叆鎵归噺妯″紡 enterBatchMode() { - // this.isBatchMode = true; - // this.selectAll = false; - // this.batchSelected = []; - - // // 娓呴櫎宸查�夌姸鎬� - // this.galleryItems.forEach(item => { - // item.selected = false; - // }); - }, - - // 閫�鍑烘壒閲忔ā寮� - exitBatchMode() { - this.isBatchMode = false; + this.isBatchMode = true; this.selectAll = false; + this.isIndeterminate = false; this.batchSelected = []; // 娓呴櫎宸查�夌姸鎬� @@ -251,37 +358,174 @@ item.selected = false; }); }, + + // 閫�鍑烘壒閲忔ā寮� + exitBatchMode() { + this.isBatchMode = false; + this.selectAll = false; + this.isIndeterminate = false; + this.batchSelected = []; + + // 娓呴櫎宸查�夌姸鎬� + this.galleryItems.forEach(item => { + if (item.hasOwnProperty('selected')) { + item.selected = false; + } + }); + }, toggleSelect(index) { if (!this.isBatchMode) return; - const position = this.batchSelected.indexOf(index); - if (position === -1) { + const isCurrentlySelected = this.galleryItems[index].selected; + + // 鏇存柊閫変腑鐘舵�� + this.$set(this.galleryItems[index], 'selected', !isCurrentlySelected); + + // 鏇存柊batchSelected鏁扮粍 + if (!isCurrentlySelected) { + // 娣诲姞閫夋嫨 this.batchSelected.push(index); } else { - this.batchSelected.splice(position, 1); + // 绉婚櫎閫夋嫨 + const position = this.batchSelected.indexOf(index); + if (position !== -1) { + this.batchSelected.splice(position, 1); + } } this.checkAllSelected(); }, checkAllSelected() { - this.isAllSelected = this.batchSelected.length === this.galleryItems.length; + this.selectAll = this.batchSelected.length === this.galleryItems.length; + this.isIndeterminate = this.batchSelected.length > 0 && this.batchSelected.length < this.galleryItems.length; + // console.info("batchSelected.length="+this.batchSelected.length) + // console.info("galleryItems.length="+this.galleryItems.length) }, // 鍏ㄩ��/鍙栨秷鍏ㄩ�� toggleSelectAll() { - this.galleryItems.forEach(item => { - item.selected = this.selectAll; + const allSelected = this.selectAll; + this.batchSelected = []; + + this.galleryItems.forEach((_, index) => { + this.$set(this.galleryItems[index], 'selected', allSelected); + if (allSelected) { + this.batchSelected.push(index); + } }); + this.isIndeterminate = this.batchSelected.length > 0 && this.batchSelected.length < this.galleryItems.length; }, // 纭鎵归噺鎿嶄綔 confirmBatch() { - const selectedItems = this.galleryItems.filter(item => item.selected); + this.openBatchLabelDialog() + }, + // 鎵撳紑妯″瀷璁粌寮圭獥 + openTrainDialog() { + // 姝ゅ搴旇皟鐢ˋPI鑾峰彇瀹為檯鐨勬牱鏈暟閲� + // 杩欓噷浣跨敤绀轰緥鏁版嵁 + this.positiveCount = 100; + this.negativeCount = 10; + // this.trainDialogVisible = true; + }, + // 寮�濮嬭缁� + async startTraining() { + try { + this.$loading({ text: '妯″瀷璁粌涓�...' }); + // 璋冪敤瀹為檯鐨勮缁傾PI + // await startModelTraining({ + // positive: this.positiveCount, + // negative: this.negativeCount + // }); - this.$message({ - message: `宸插${selectedItems.length}涓」鎵ц鎵归噺鎿嶄綔`, - type: 'success' + // 妯℃嫙API寤惰繜 + await new Promise(resolve => setTimeout(resolve, 2000)); + + this.$message.success('妯″瀷璁粌宸插紑濮�'); + this.trainDialogVisible = false; + } catch (error) { + this.$message.error(`璁粌澶辫触: ${error.message}`); + } finally { + this.$loading().close(); + } + }, + // 鎵撳紑鎵归噺鏍囨敞寮圭獥 + openBatchLabelDialog() { + if (this.batchSelected.length === 0) { + this.$message.warning('璇峰厛閫夋嫨鍥剧墖杩涜鏍囨敞'); + return; + } + this.batchLabelStatus = 0; // 閲嶇疆涓轰笉纭畾鐘舵�� + this.batchLabelDialogVisible = true; + }, + // 纭鎵归噺鏍囨敞 + async confirmBatchLabeling() { + if (this.batchSelected.length === 0) { + this.$message.warning('璇峰厛閫夋嫨鍥剧墖'); + return; + } + + try { + // this.$loading({ text: '鎵归噺鏍囨敞涓�...' }); + + const selectedItems = this.galleryItems.filter(item => item.selected); + let ids = [] + for (let i = 0; i < selectedItems.length; i++) { + ids.push(selectedItems[i].trainId) + } + + // 璋冪敤鎵归噺鏇存柊鐘舵�佺殑API + let rspc = await batchUpdateTrainStatus({ + ids: ids, + status: this.batchLabelStatus + }) + if (rspc && rspc.status === 200) { + this.$message.success(`宸叉垚鍔熸爣娉�${selectedItems.length}涓暟鎹甡); + this.batchLabelDialogVisible = false; + this.exitBatchMode(); // 閫�鍑烘壒閲忔ā寮� + this.fetchTableData(); // 鍒锋柊鏁版嵁 + } else { + this.$message.error(`鏍囨敞澶辫触: ${rspc.msg}`); + } + } catch (error) { + this.$message.error(`鏍囨敞澶辫触: ${error.message}`); + } finally { + this.$loading().close(); + } + }, + // 鎵归噺鍒犻櫎 + async handleBatchDelete() { + if (this.batchSelected.length === 0) { + this.$message.warning('璇峰厛閫夋嫨鍥剧墖'); + return; + } + + this.$confirm(`纭畾瑕佸垹闄ら�変腑鐨�${this.batchSelected.length}涓暟鎹悧锛焋, '璀﹀憡', { + confirmButtonText: '纭畾', + cancelButtonText: '鍙栨秷', + type: 'warning' + }).then(async () => { + try { + // this.$loading({ text: '鍒犻櫎涓�...' }); + + // 璋冪敤鎵归噺鍒犻櫎API + let rspc = await batchDeleteTrains({ + ids: ids + }) + if (rspc && rspc.status === 200) { + this.$message.success(`宸叉垚鍔熷垹闄�${this.batchSelected.length}涓暟鎹甡); + this.batchLabelDialogVisible = false; + this.exitBatchMode(); // 閫�鍑烘壒閲忔ā寮� + this.fetchTableData(); // 鍒锋柊鏁版嵁 + } else { + this.$message.error(`鍒犻櫎澶辫触: ${rspc.msg}`); + } + } catch (error) { + this.$message.error(`鍒犻櫎澶辫触: ${error.message}`); + } finally { + this.$loading().close(); + } + }).catch(() => { + // 鐢ㄦ埛鍙栨秷鍒犻櫎 }); - - this.exitBatchMode(); }, } } @@ -298,8 +542,8 @@ /* 绛涢�夊尯鍩熸牱寮� */ .filter-section { - padding: 20px; - background-color: #f5f7fa; + /* padding: 20px; */ + /* background-color: #f5f7fa; */ border-radius: 4px; margin-bottom: 20px; } @@ -349,11 +593,12 @@ display: flex; align-items: center; background: #fff; - border-bottom: 1px solid #e6ebf5; - padding: 10px 20px; + /* border-bottom: 1px solid #e6ebf5; */ + padding: 5px; margin-bottom: 20px; - border-radius: 4px; - box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05); + margin-top: -10px; + /* border-radius: 4px; */ + /* box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05); */ } @@ -366,4 +611,74 @@ display: flex; gap: 10px; } + +/* 寮圭獥鏍峰紡 */ +/* 鏍囬宸﹀榻� */ +::v-deep .el-dialog__header { + text-align: left; +} + +::v-deep .el-dialog__title { + font-weight: bold; + font-size: 16px; +} + +::v-deep .el-dialog__body { + padding-top: 20px; + padding-bottom: 15px; +} + +/* 妯″瀷璁粌寮圭獥鍐呭 */ +.sample-info { + padding: 20px; +} + +.info-label { + font-weight: bold; + margin-bottom: 12px; + color: #606266; +} + +.sample-count { + padding: 8px 0; + border-bottom: 1px solid #f0f2f5; +} + +/* 鎵归噺鏍囨敞寮圭獥鍐呭 */ +.label-options { + display: flex; + flex-direction: column; +} + +.label-option { + width: 100%; + padding: 15px 20px; + margin: 8px 0; + border: 1px solid #dcdfe6; + border-radius: 4px; + cursor: pointer; + transition: all 0.3s; + text-align: center; + box-sizing: border-box; +} + +.label-option:hover { + border-color: #409eff; + color: #409eff; +} + +.label-option.active { + border-color: #409eff; + background-color: #ecf5ff; + color: #409eff; +} + +/* 寮圭獥搴曢儴鎸夐挳 */ +.dialog-footer { + /* display: flex; */ + text-align: center; + /* justify-content: space-between; */ + /* padding: 10px 20px; */ + /* border-top: 1px solid #e6ebf5; */ +} </style> \ No newline at end of file -- Gitblit v1.8.0