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 |  505 +++++++++++++++++++++++++++++++++++++++++++------------
 1 files changed, 392 insertions(+), 113 deletions(-)

diff --git a/src/pages/modelTuning/components/rightCardList.vue b/src/pages/modelTuning/components/rightCardList.vue
index f0d4db4..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">
@@ -74,31 +109,39 @@
         </div>
         <!-- 鍒嗛〉缁勪欢 -->
         <Pagination :total="totalCount" :current-page.sync="currentPage" :page-size.sync="pageSize"
-            @pagination-change="fetchTableData" />
+            @pagination-change="paginationChange" />
     </div>
 </template>
 
 <script>
+import BatchImport from './batchImport';
 import imageCard from './imageCard';
 import Pagination from '@/components/rightPagination';
-import { getTrainTags, getTrains, updateTrainStatus, deleteTrains } from "@/api/modelTuning";
+import { getTrains, updateTrainStatus, deleteTrains, batchUpdateTrainStatus, batchDeleteTrains, uploadDataTrainTags } from "@/api/modelTuning";
 export default {
     components: {
         imageCard,
         Pagination,
+        BatchImport
 
     },
     name: 'ImageGallery',
-    props: {
-        trainId: {
-            type: Number
-        },
-    },
     data() {
         return {
+            trainDialogVisible: false,       // 妯″瀷璁粌寮圭獥鍙鎬�
+            batchLabelDialogVisible: false,  // 鎵归噺鏍囨敞寮圭獥鍙鎬�
+            // 妯″瀷璁粌寮圭獥鏁版嵁
+            positiveCount: 0,       // 姝f牱鏈暟閲�
+            negativeCount: 0,       // 璐熸牱鏈暟閲�
+            // 鎵归噺鏍囨敞鐘舵��
+            batchLabelStatus: 0,    // 榛樿閫夋嫨"涓嶇‘瀹�"
+            isIndeterminate: false,
+            selectAll: false,
+            pagination: {},
+            trainId: 0,
             totalCount: 0,     // 鎬绘暟鎹噺
             currentPage: 1,    // 褰撳墠椤电爜
-            pageSize: 10,     // 姣忛〉鏁伴噺
+            pageSize: 12,     // 姣忛〉鏁伴噺
             tableData: [],    // 琛ㄦ牸鏁版嵁
             // 鏄惁鎵归噺妯″紡
             isBatchMode: false,
@@ -106,7 +149,7 @@
             // 绛涢�夋潯浠�
             filter: {
                 cameraName: '',
-                timeRange: ['2025-06-22', '2025-06-28'],
+                timeRange: ['', ''],
                 category: 'all'
             },
 
@@ -120,78 +163,111 @@
 
             // 鍥剧墖鏁版嵁
             galleryItems: [
-                {
-                    id: 1,
-                    image: "@/assets/img/鏍锋湰鍥�.png",
-                    date: '2025-07-01 10:00:09',
-                    camera: '澶ч棬鍙f娴嬫憚鍍忓ご',
-                    status: 'correct'
-                },
-                {
-                    id: 2,
-                    image: "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='300' height='200' viewBox='0 0 300 200'%3E%3Crect width='300' height='200' fill='%23f0f7ff'/%3E%3Crect x='50' y='30' width='200' height='140' rx='5' ry='5' fill='%23d5e8ff' stroke='%23409EFF' stroke-width='1'/%3E%3Cpath d='M100,60 L200,60 M100,80 L200,80 M100,100 L200,100 M100,120 L200,120 M100,140 L200,140' stroke='%23409EFF' stroke-width='1'/%3E%3Cpath d='M80,80 L120,120 M80,120 L120,80' stroke='%23FF4D4F' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E",
-                    date: '2025-07-01 10:00:09',
-                    camera: '澶ч棬鍙f娴嬫憚鍍忓ご',
-                    status: 'incorrect'
-                },
-                {
-                    id: 3,
-                    image: "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='300' height='200' viewBox='0 0 300 200'%3E%3Crect width='300' height='200' fill='%23f0f7ff'/%3E%3Crect x='50' y='30' width='200' height='140' rx='5' ry='5' fill='%23d5e8ff' stroke='%23409EFF' stroke-width='1'/%3E%3Cellipse cx='150' cy='100' rx='70' ry='40' stroke='%23409EFF' stroke-width='1' fill='none'/%3E%3Ccircle cx='130' cy='80' r='5' fill='%23409EFF'/%3E%3Ccircle cx='170' cy='80' r='5' fill='%23409EFF'/%3E%3Cpath d='M150,100 Q160,120 140,120' stroke='%23409EFF' stroke-width='1' fill='none'/%3E%3C/svg%3E",
-                    date: '2025-07-01 10:00:09',
-                    camera: '澶ч棬鍙f娴嬫憚鍍忓ご',
-                    status: 'correct'
-                },
-                {
-                    id: 4,
-                    image: "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='300' height='200' viewBox='0 0 300 200'%3E%3Crect width='300' height='200' fill='%23f0f7ff'/%3E%3Crect x='50' y='30' width='200' height='140' rx='5' ry='5' fill='%23d5e8ff' stroke='%23409EFF' stroke-width='1'/%3E%3Cpath d='M80,80 L220,80 M150,50 L150,110' stroke='%23409EFF' stroke-width='1'/%3E%3Ccircle cx='150' cy='80' r='40' stroke='%23409EFF' stroke-width='1' fill='none'/%3E%3C/svg%3E",
-                    date: '2025-07-01 10:00:09',
-                    camera: '澶ч棬鍙f娴嬫憚鍍忓ご',
-                    status: 'unknown'
-                },
-                {
-                    id: 5,
-                    image: "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='300' height='200' viewBox='0 0 300 200'%3E%3Crect width='300' height='200' fill='%23f0f7ff'/%3E%3Crect x='50' y='30' width='200' height='140' rx='5' ry='5' fill='%23d5e8ff' stroke='%23409EFF' stroke-width='1'/%3E%3Cpath d='M100,60 L200,60 M100,80 L200,80 M100,100 L200,100 M100,120 L200,120' stroke='%23409EFF' stroke-width='1'/%3E%3Cpath d='M120,100 A20,20 0 1,1 180,100 A20,20 0 1,1 120,100' stroke='%23409EFF' stroke-width='1' fill='none'/%3E%3C/svg%3E",
-                    date: '2025-07-01 10:00:09',
-                    camera: '澶ч棬鍙f娴嬫憚鍍忓ご',
-                    status: 'correct'
-                },
-                {
-                    id: 6,
-                    image: "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='300' height='200' viewBox='0 0 300 200'%3E%3Crect width='300' height='200' fill='%23f0f7ff'/%3E%3Crect x='50' y='30' width='200' height='140' rx='5' ry='5' fill='%23d5e8ff' stroke='%23409EFF' stroke-width='1'/%3E%3Crect x='70' y='60' width='60' height='40' rx='3' ry='3' fill='%23a0c8ff' stroke='%23409EFF' stroke-width='1'/%3E%3Crect x='170' y='60' width='60' height='40' rx='3' ry='3' fill='%23a0c8ff' stroke='%23409EFF' stroke-width='1'/%3E%3Crect x='120' y='120' width='60' height='20' rx='2' ry='2' fill='%23a0c8ff' stroke='%23409EFF' stroke-width='1'/%3E%3C/svg%3E",
-                    date: '2025-07-01 10:00:09',
-                    camera: '澶ч棬鍙f娴嬫憚鍍忓ご',
-                    status: 'incorrect'
-                }
             ]
         }
     },
     mounted() {
-        this.fastFetchTableData()
     },
     methods: {
-        async fastFetchTableData() {
-            let rspc = await getTrainTags();
-            if (rspc && rspc.status === 200) {
-                this.fetchTableData({
-                    tagId: rspc.data.list[0].id,
-                    page: this.currentPage,
-                    limit: this.pageSize
-                })
+        // 鎵撳紑瀵煎叆瀵硅瘽妗�
+        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
+            await this.fetchTableData()
+        },
+        async changeTrainId(trainId) {
+            // console.info(trainId)
+            this.trainId = trainId
+            this.isBatchMode = false
+            await this.fetchTableData()
         },
         // 鑾峰彇琛ㄦ牸鏁版嵁鏂规硶
         async fetchTableData(params) {
-            // yourApi.getData(params).then(res => {
-            //     this.tableData = res.data.list
-            //     this.totalCount = res.data.total
-            // })
+            // console.info(this.currentPage)
             this.galleryItems = []
-            let rspc = await getTrains(params);
+            // params.tagId = this.trainId
+            let rspc = await getTrains({
+                tagId: this.trainId,
+                page: this.currentPage,
+                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 = 20
+            this.totalCount = rspc.data.pagination.total
+            // 鏇存柊鍒嗛〉鏁版嵁鍓嶅厛鏍¢獙褰撳墠椤电爜
+            const totalPage = rspc.data.pagination.totalPage
+            const currentPage = this.currentPage > totalPage
+                ? totalPage
+                : rspc.data.pagination.page
+            this.currentPage = currentPage,
+                this.totalPage = totalPage
         },
         //鍒犻櫎
         async handleDeleteDetails(item) {
@@ -209,7 +285,11 @@
                         type: 'success',
                         message: '鎴愬姛'
                     });
-                    this.fetchSelectData()
+                    // 鍒犻櫎鎴愬姛鍚庤嚜鍔ㄤ慨姝i〉鐮�
+                    if (this.galleryItems && this.galleryItems.length === 1 && this.currentPage > 1) {
+                        this.currentPage -= 1
+                    }
+                    this.fetchTableData()
                 } else {
                     this.$message({
                         type: 'error',
@@ -223,18 +303,19 @@
         },
         //淇敼鍥剧墖鐘舵��
         async handleStatusChange(parm) {
-            console.log('淇敼鐘舵��', parm);
+            // console.log('淇敼鐘舵��', parm);
             let rspc = await updateTrainStatus(parm);
             if (rspc && rspc.status === 200) {
                 this.$message({
                     type: 'success',
                     message: '鎴愬姛'
                 });
-                this.fetchTableData({
-                    tagid: this.trainId,
-                    page: this.currentPage,
-                    limit: this.pageSize
-                })
+                // 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({
                     type: 'error',
@@ -251,8 +332,9 @@
         handleSearch() {
             // console.log('鎵ц鎼滅储:', this.filter);
             // console.log('trainId:', this.trainId);
-            // 杩欓噷鍙互娣诲姞瀹為檯鐨勬悳绱㈤�昏緫
-            this.fetchTableData({ tagId: this.trainId })
+            this.currentPage = 1,    // 褰撳墠椤电爜
+                // 杩欓噷鍙互娣诲姞瀹為檯鐨勬悳绱㈤�昏緫
+                this.fetchTableData({ tagId: this.trainId })
         },
 
         // 閲嶇疆绛涢�夋潯浠�
@@ -266,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 = [];
 
             // 娓呴櫎宸查�夌姸鎬�
@@ -287,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();
         },
     }
 }
@@ -334,8 +542,8 @@
 
 /* 绛涢�夊尯鍩熸牱寮� */
 .filter-section {
-    padding: 20px;
-    background-color: #f5f7fa;
+    /* padding: 20px; */
+    /* background-color: #f5f7fa; */
     border-radius: 4px;
     margin-bottom: 20px;
 }
@@ -385,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); */
 
 }
 
@@ -402,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