• vue3 + element Plus实现表格根据关键字合并行,并实现行的增删改操作


    首先看最终实现的效果

    请添加图片描述

    1.实现初始化表格

    这里主要用到的是表格的span-method这个方法

    <template>
        <div class="main-page">
            <div class="flex-end">
                <div class="public-search">添加班级</div>
            </div>
            <el-table border :data="tableData" stripe style="width: 100%;" :span-method="objectSpanMethod">
                <el-table-column label="班级" align="center" prop="class" />
                <el-table-column label="姓名" align="center" prop="name" />
                <el-table-column label="课程" align="center" prop="course" />
                <el-table-column label="备注" align="center" prop="remark" />
                <el-table-column fixed="right" label="删除学生" align="center">
                    <template #default="scope">
                        <span class="del-btn">删除</span>
                    </template>
                </el-table-column>
                <el-table-column fixed="right" label="操作" align="center">
                    <template #default="scope">
                        <span class="add-btn">添加学生</span>
                        <span class="del-btn">删除班级</span>
                    </template>
                </el-table-column>
            </el-table>
        </div>
    </template>
    
    <script setup>
    import { ref } from "vue";
    
    const tableData = ref()  //表格数据
    
    // 第一列的的索引
    const firstIndex = ref([])
    // 相同行数名的索引的值
    const mergerRowIndex = ref([])
    //合并表格列
    const objectSpanMethod = ({ row, column, rowIndex, columnIndex }) => {
        if (![1, 2, 3, 4].includes(columnIndex)) {  // 从0开始数,第1,2, 3, 4列是需要合并的
            if (rowIndex === 0) {
                return {
                    rowspan: firstIndex.value[0],
                    colspan: 1
                }
            }
            if (firstIndex.value[0] > rowIndex && rowIndex > 0) {
                return {
                    rowspan: 0,
                    colspan: 0
                }
            }
            for (let i = 0; i < firstIndex.value.length; i++) {
                if (rowIndex === firstIndex.value[i]) {
                    return {
                        rowspan: firstIndex.value[i + 1] - firstIndex.value[i],
                        colspan: 1
                    }
                }
                if (firstIndex.value[i + 1] > rowIndex && rowIndex > firstIndex.value[i]) {
                    return {
                        rowspan: 0,
                        colspan: 0
                    }
                }
            }
        }
    }
    // 查验有无相同行数名的对象,如果知道不同行数名的位置,则记住其位置并且压入
    const getMergerRowIndex = () => {
        // 每次调用这个函数,就需要把之前压入到函数内的值给清空
        mergerRowIndex.value = []
        firstIndex.value = []
        for (let i = 1; i < tableData.value.length; i++) {
            if (tableData.value[i].class != tableData.value[i - 1].class) {
                mergerRowIndex.value.push(i);
                firstIndex.value.push(i);
            }
        }
        //  并且还要压入表格的的长度
        mergerRowIndex.value.push(tableData.value.length);
        firstIndex.value.push(tableData.value.length);
    }
    
    //请求接口获取数据
    const getData = () => {
        let data = [
            { id: '1', class: '一班', name: '张三', course: '数学', remark: '数学年级第二', },
            { id: '2', class: '一班', name: '李四', course: '语文', remark: '语文还行', },
            { id: '3', class: '一班', name: '王五', course: '英语', remark: '英语一般般', },
            { id: '4', class: '二班', name: '向小小', course: '语文', remark: '语文年级第一', },
            { id: '5', class: '二班', name: '杨六六', course: '数学', remark: '数学年级第一', },
        ]
        tableData.value = data
        getMergerRowIndex()
    }
    getData()
    </script>
    
    <style lang="scss"  scoped>
    .main-page {
        .flex-end {
            display: flex;
            justify-content: end;
            margin-bottom: 10px;
        }
    
        .add-btn {
            color: #0077ef;
            margin-right: 4px;
            cursor: pointer;
        }
    
        .del-btn {
            color: #f56c6c;
            cursor: pointer;
        }
    
        .public-search {
            background: #0077ef;
            border-radius: 4px;
            height: 32px;
            padding: 0 14px;
            text-align: center;
            color: #ffffff;
            cursor: pointer;
            align-items: center;
            display: flex;
            user-select: none;
        }
    }
    </style>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130

    初始化后的页面
    在这里插入图片描述

    2.实现添加班级与学生的功能

    关键代码

    //给添加班级与添加学生绑定事件
    <div class="public-search" @click="addStudents(null, null)">添加班级</div>
    <span class="add-btn" @click="addStudents(scope.$index, scope.row)">添加学生</span>
    //为了方便管理数据给新增弹窗重新定义了一个组件AddClass
    <AddClass v-model:show="showClass" :row="rowList" :tableData="tableData" @addClass="addClass" />
    
    <script>
    import AddClass from "./AddClass.vue"; //引入组件
    
    const showClass = ref(false)
    const rowList = ref()  //行数据
    const rowIndex = ref(0)  //点击的多少行
    
    //添加学生和班级
    const addStudents = (index, row) => {
        rowIndex.value = index || 0
        rowList.value = row || {}
        showClass.value = true
    }
    
    //添加班级或学生
    const addClass = (form) => {
        //这里全局都是根据class来判断唯一值的,视具体情况而修改
        let len = tableData.value.filter(res => res.class === form.class).length;
        if (len === 0) {
            len = tableData.value.length;
        }
        //新增学生的随机id
        form.id = Math.random()
        //使用splice(xx,0,{}) 这个方法在指定位置加数据
        tableData.value.splice(len + rowIndex.value, 0, {
            ...form
        })
        getMergerRowIndex()
    }
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    最后实现的效果
    在这里插入图片描述

    3.添加的弹窗

    <template>
        <div class="add-class">
            <el-dialog v-model="dialogVisible" title="新增" width="480px" :before-close="closeDialog">
                <el-form ref="ruleFormRef" :model="ruleForm" label-width="120px" label-position="top" class="demo-ruleForm">
                    <el-form-item label="班级:" prop="class" v-if="!hasClass">
                        <el-select v-model="ruleForm.class" filterable allow-create default-first-option placeholder="请输入或者选择班级">
                            <el-option label="一班" value="一班" />
                            <el-option label="二班" value="二班" />
                            <el-option label="三班" value="三班" />
                            <el-option label="四班" value="四班" />
                            <el-option label="五班" value="五班" />
                        </el-select>
                    </el-form-item>
                    <el-form-item label="姓名:" prop="name">
                        <el-input v-model="ruleForm.name" />
                    </el-form-item>
                    <el-form-item label="课程:" prop="course">
                        <el-input v-model="ruleForm.course" />
                    </el-form-item>
                    <el-form-item label="备注:" prop="remark">
                        <el-input v-model="ruleForm.remark" :rows="2" type="textarea" />
                    </el-form-item>
                </el-form>
                <template #footer>
                    <span class="dialog-footer">
                        <el-button type="primary" @click="onSubmit">
                            确定
                        </el-button>
                        <el-button @click="closeDialog">取消</el-button>
                    </span>
                </template>
            </el-dialog>
        </div>
    </template>
    
    <script setup >
    import { ref, reactive, defineEmits, defineProps, computed, watch } from "vue"
    import { ElMessage } from 'element-plus'
    const emits = defineEmits(['update:show']);
    const props = defineProps({
        show: Boolean,
        row: Object || {},
        tableData: Array
    })
    watch(props, () => {
        if (props.row && props.row.class) {
            ruleForm.class = props.row.class
            hasClass.value = true
        } else {
            ruleForm.class = ''
            hasClass.value = false
        }
    })
    const hasClass = ref(false) //是否有class
    const dialogVisible = computed({
        get: () => { return props.show },
        set: newVal => emits('update:show', newVal)
    });
    const ruleForm = reactive({ 
        class: '',
        name: '',
        course: '',
        remark: '',
    })
    const ruleFormRef = ref()
    const closeDialog = () => {
        dialogVisible.value = false
        ruleFormRef.value && ruleFormRef.value.resetFields()
    }
    const onSubmit = async () => {
        //如果存在班级则不能继续添加班级
        if (!hasClass.value) {
            if (props.tableData.some(res => res.class === ruleForm.class)) {
                ElMessage.error('已存在该班级');
                return;
            }
        }
    
        emits('addClass', ruleForm)
        closeDialog()
    }
    </script>
    
    <style lang="scss" scoped>
    .add-class {
    }
    </style>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88

    新增班级弹窗效果
    在这里插入图片描述
    新增学生的效果
    在这里插入图片描述

    4.删除班级

    关键代码

       //绑定事件
       <span class="del-btn" @click="delClass(scope.$index, scope.row)">删除班级</span>
    
    import { ElMessage, ElMessageBox } from 'element-plus'
    //删除班级
    const delClass = (index, row) => {
        ElMessageBox.confirm(
            `是否删除班级${row.class}?`,
            {
                confirmButtonText: '确定',
                cancelButtonText: '取消',
                type: 'warning',
            }
        ).then(() => {
            let len = tableData.value.filter(res => res.class === row.class).length
            tableData.value.splice(index, len)
            getMergerRowIndex()
            ElMessage({
                type: 'success',
                message: '删除成功',
            })
        }).catch(() => {
            ElMessage({
                type: 'info',
                message: '取消操作',
            })
        })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    实现效果
    请添加图片描述

    5.删除学生

    关键代码

    //绑定事件
    <span class="del-btn" @click="delStudents(scope.row)">删除</span>
    
    //删除学生
    const delStudents = (row) => {
        ElMessageBox.confirm(
            `是否删除学生${row.name}?`,
            {
                confirmButtonText: '确定',
                cancelButtonText: '取消',
                type: 'warning',
            }
        ).then(() => {
            tableData.value = tableData.value.filter(item => {
                return item.id != row.id
            })
            getMergerRowIndex()
            ElMessage({
                type: 'success',
                message: '删除成功',
            })
        }).catch(() => {
            ElMessage({
                type: 'info',
                message: '取消操作',
            })
        })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    效果图
    请添加图片描述

    end: 关于修改,其实和新增差不多这里就没有写了,合并表格并实现增删改查的功能就是这些。

    全部代码可以关注这里哦

  • 相关阅读:
    3D视觉 之 线激光3D相机
    Win10/Win11系统bitlocker正在等待激活如何解决?
    索引失效的几种情况
    用Python实现队列---循环队列
    【Paper】2022_离散时间多智能体系统编队-包围控制研究_李博凡
    Collections工具类
    awk从入门到入土(16)awk变量类型探讨--关于数字和string两种类型
    项目管理之分析项目特点的方法
    动物主题网页设计(学生期末作业必看)
    C语言中的虚拟地址
  • 原文地址:https://blog.csdn.net/Jet_Lover/article/details/133781005