sd
2025-08-08 54572b3c9db78d4374281087a8c66e5c3b3b6200
src/pages/modelTuning/components/rightCardList.vue
@@ -1,5 +1,41 @@
<template>
    <div class="image-gallery">
        <!-- 添加导入组件 -->
        <BatchImport ref="batchImport" :show-type-selector="false" @import="handleImportFiles" />
        <!-- 模型训练弹窗 -->
        <el-dialog class="dialog1" title="模型训练" :visible.sync="trainDialogVisible" width="372px" top="10vh">
            <div class="info-label">样本信息</div>
            <div class="sample-info">
                <div class="sample-count">正样本数量:{{ 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">
                    <i class="el-icon-time"></i>
                    开始训练</el-button>
            </div>
        </el-dialog>
        <!-- 批量标注弹窗 -->
        <el-dialog class="dialog2" title="批量标注" :visible.sync="batchLabelDialogVisible" width="472px" top="10vh">
            <div class="label-options">
                <div class="label-option" :class="{ active: batchLabelStatus === 1 }" @click="batchLabelStatus = 1">
                    正确
                </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">
@@ -11,7 +47,8 @@
                <!-- 选择时段 -->
                <el-form-item label="选择时段">
                    <el-date-picker style="width: 256px;" v-model="filter.timeRange" type="daterange"
                        value-format="yyyy-MM-dd" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" />
                        value-format="yyyy-MM-dd" range-separator="至" start-placeholder="开始日期"
                        end-placeholder="结束日期" />
                </el-form-item>
                <!-- 分类 -->
@@ -38,13 +75,13 @@
                        <el-button type="primary" class="action-btn">
                            <i class="el-icon-download"></i> 导入
                        </el-button>
                        <el-dropdown-menu slot="dropdown">
                            <el-dropdown-item>正样本</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)">正样本</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 +91,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">
@@ -66,39 +104,54 @@
                    @status-change="handleStatusChange" @show-details="showImageDetails"
                    @edit-annotation="editAnnotation" @download="downloadImage"-->
        <div class="gallery-section">
            <el-row :gutter="20" class="image-grid">
                <image-card v-for="(item, index) in galleryItems" :key="index" :item="item" :is-batch-mode="isBatchMode"
                    @delete-details="handleDeleteDetails" @status-change="handleStatusChange"
                    @card-click="handleCardClick1" @toggle-select="toggleSelect(index)" />
            </el-row>
            <div class="image-grid" ref="imageGrid">
                <div v-for="(item, index) in galleryItems" :key="index" class="image-card-wrapper"
                    :style="{ width: cardWidth}">
                    <image-card :item="item" :is-batch-mode="isBatchMode" @delete-details="handleDeleteDetails"
                        @status-change="handleStatusChange" @card-click="handleCardClick1"
                        @toggle-select="toggleSelect(index)" />
                </div>
            </div>
        </div>
        <!-- 分页组件 -->
        <Pagination :total="totalCount" :current-page.sync="currentPage" :page-size.sync="pageSize"
            @pagination-change="fetchTableData" />
            @pagination-change="paginationChange" />
    </div>
</template>
<script>
import _ from 'lodash'; // 用于防抖
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 {
            cardWidth: 'calc(25% - 20px)', // 默认4列布局
            minCardWidth: 300, // 卡片最小宽度
            margin: 20, // 卡片间距
            trainDialogVisible: false,       // 模型训练弹窗可见性
            batchLabelDialogVisible: false,  // 批量标注弹窗可见性
            // 模型训练弹窗数据
            positiveCount: 0,       // 正样本数量
            negativeCount: 0,       // 负样本数量
            // 批量标注状态
            batchLabelStatus: 0,    // 默认选择"不确定"
            isIndeterminate: false,
            selectAll: false,
            pagination: {},
            trainId: 0,
            totalCount: 0,     // 总数据量
            currentPage: 1,    // 当前页码
            pageSize: 12,     // 每页数量
            pageSize: 15,     // 每页数量
            tableData: [],    // 表格数据
            // 是否批量模式
            isBatchMode: false,
@@ -106,92 +159,146 @@
            // 筛选条件
            filter: {
                cameraName: '',
                timeRange: ['2025-06-22', '2025-06-28'],
                category: 'all'
                timeRange: ['', ''],
                category: -1
            },
            // 分类选项
            categories: [
                { label: '全部', value: 'all' },
                { label: '正确', value: 'correct' },
                { label: '错误', value: 'incorrect' },
                { label: '不确定', value: 'unknown' }
                { label: '全部', value: -1 },
                { label: '正确', value: 1 },
                { label: '错误', value: 2 },
                { label: '不确定', value: 0 }
            ],
            // 图片数据
            galleryItems: [
                {
                    id: 1,
                    image: "@/assets/img/样本图.png",
                    date: '2025-07-01 10:00:09',
                    camera: '大门口检测摄像头',
                    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: '大门口检测摄像头',
                    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: '大门口检测摄像头',
                    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: '大门口检测摄像头',
                    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: '大门口检测摄像头',
                    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: '大门口检测摄像头',
                    status: 'incorrect'
                }
            ]
        }
    },
    mounted() {
        this.fastFetchTableData()
        this.calculateCardWidth();
        // 添加防抖的resize监听
        window.addEventListener('resize', _.debounce(this.calculateCardWidth, 100));
    },
    beforeDestroy() {
        window.removeEventListener('resize', this.calculateCardWidth);
    },
    methods: {
        async fastFetchTableData() {
            let rspc = await getTrainTags();
            if (rspc && rspc.status === 200) {
                this.fetchTableData({
                    tagId: rspc.data.list[0].id
                })
        // 新增卡片宽度计算方法
        calculateCardWidth() {
            if (this.$refs.imageGrid) {
                const containerWidth = this.$refs.imageGrid.clientWidth;
                // 计算每行可以放置的卡片数量
                const cardsPerRow = Math.floor(containerWidth / (this.minCardWidth + this.margin));
                const n = Math.max(3, cardsPerRow); // 至少1列
                // 设置卡片宽度公式
                this.cardWidth = `calc(${100 / n}% - ${this.margin}px)`;
            }
        },
        // 打开导入对话框
        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. 添加实际文件内容到FormData
                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: '正样本',
                2: '负样本',
                3: '待标记样本'
            }[type];
        },
        // 模拟文件上传函数
        async uploadFile(file, type) {
            // 在实际应用中,这里应该是一个API调用
            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 = []
            params.page = this.currentPage
            params.pageSize = this.pageSize
            let rspc = await getTrains(params);
            // params.tagId = this.trainId
            let rspc = await getTrains({
                tagId: this.trainId,
                page: this.currentPage,
                pageSize: this.pageSize,
                startTime: this.filter.timeRange[0] ? this.filter.timeRange[0] + " 00:00:00" : "",
                endTime: this.filter.timeRange[1] ? this.filter.timeRange[1] + " 23:23:59" : "",
                searchName: this.filter.cameraName,
                status: this.filter.category
            });
            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 +316,11 @@
                        type: 'success',
                        message: '成功'
                    });
                    this.fetchSelectData()
                    // 删除成功后自动修正页码
                    if (this.galleryItems && this.galleryItems.length === 1 && this.currentPage > 1) {
                        this.currentPage -= 1
                    }
                    this.fetchTableData()
                } else {
                    this.$message({
                        type: 'error',
@@ -223,18 +334,20 @@
        },
        //修改图片状态
        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',
@@ -250,9 +363,10 @@
        // 处理搜索
        handleSearch() {
            // console.log('执行搜索:', this.filter);
            // console.log('trainId:', this.trainId);
            // 这里可以添加实际的搜索逻辑
            this.fetchTableData({ tagId: this.trainId })
            // console.log('filter:', this.filter.cameraName);
            this.currentPage = 1,    // 当前页码
                // 这里可以添加实际的搜索逻辑
                this.fetchTableData()
        },
        // 重置筛选条件
@@ -260,26 +374,15 @@
            this.filter = {
                cameraName: '',
                timeRange: ['', ''],
                category: 'all'
                category: -1
            };
            console.log('已重置筛选条件');
        },
        // 进入批量模式
        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,44 +390,201 @@
                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() {
            // 此处应调用API获取实际的样本数量
            // 这里使用示例数据
            this.positiveCount = this.galleryItems.filter(item => item.status === 1).length;
            this.negativeCount = this.galleryItems.filter(item => item.status === 2).length;
            this.trainDialogVisible = true;
        },
        // 开始训练
        async startTraining() {
            try {
                // this.$loading({ text: '模型训练中...' });
                // 调用实际的训练API
                // 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;
                this.$message.error(`功能暂未实现`);
            } 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();
        },
    }
}
</script>
<style scoped>
/* 新增图片网格布局样式 */
.image-grid {
    display: flex;
    flex-wrap: wrap;
    margin: -10px;
    min-width: 1000px;
    /* 负边距抵消包裹元素的边距 */
    /* width: 100%; */
}
.image-card-wrapper {
    margin: 10px;
    /* 设置卡片间距 */
    box-sizing: border-box;
    /* transition: width 0.3s ease; */
    /* 添加平滑过渡效果 */
}
.image-gallery {
    min-width: 1265px;
    background-color: #ffffff;
    padding: 20px;
    border-radius: 4px;
@@ -334,8 +594,8 @@
/* 筛选区域样式 */
.filter-section {
    padding: 20px;
    background-color: #f5f7fa;
    /* padding: 20px; */
    /* background-color: #f5f7fa; */
    border-radius: 4px;
    margin-bottom: 20px;
}
@@ -385,11 +645,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 +663,91 @@
    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;
}
/* 模型训练弹窗样式 */
.dialog1 {
    .info-label {
        font-weight: bold;
        font-size: 20px;
        margin-bottom: 15px;
        color: black;
        text-align: left;
    }
    .sample-info {
        display: flex;
        justify-content: space-between;
        /* padding: 0 10px; */
        margin-bottom: 20px;
    }
    .sample-count {
        /* font-weight: 600; */
        font-size: 15px;
        color: #333;
    }
    .dialog-footer {
        display: flex;
        justify-content: flex-end;
        padding: 10px 0;
    }
}
/* 批量惭怍弹窗样式 */
.dialog2 {
    /* 批量标注弹窗内容 */
    .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>