Element Plus encapsulates el-dialog, el-table, and el-select form dialog boxes to realize single-selection and multiple-selection scenarios.

1. Usage scenarios

First of all, we need to know why a component with el-dialog, el-table, and is packaged . You will definitely encounter such a situation during development.el-select
后台管理项目

用户管理For example: there are 角色管理two functional modules in our project.
We need to bind roles when adding users, and the bound roles can be selected multiple times, as shown in the
figure below:

Will most of us write like this
insert image description here
? So is there a problem with writing like this? Of course, there is no problem.

But if when choosing a role, I want to see the description of the role, or the avatar, or the permissions of the role, what should I do at this time.
I can't, cancel this editing, go to the function module of the role and then add users, isn't it very troublesome.

Next, the components we encapsulate perfectly solve this problem.
Let's first show the renderings
insert image description here
, including query and pagination.

insert image description here
Next let's look at the code

2. Code implementation

1. Main file

The main page has nothing to say, just a formform, as in normal usage.

<!-- 页面主文件 -->
<template>
    <div>
        <el-form :model="form">
            <el-form-item label="角色">
                <!-- LTableDialog 就是我们这次需要封装的组件 -->
                <LTableDialog v-model="form.menuId" multiple method="roleList" checkLabel="roleName" title="角色选择"
                    :tableColumns="roleColumnsData" :tableQuery="roleQueryData" @change="roleChange">
                </LTableDialog>
            </el-form-item>
            <el-form-item label="菜单">
                <LTableDialog v-model="form.roleId" method="menuList" checkLabel="menuName" title="菜单选择"
                    :tableColumns="menuColumnsData" :tableQuery="menuQueryData">
                </LTableDialog>
            </el-form-item>
        </el-form>
    </div>
</template>

<script setup>
import {
      
       reactive, toRefs, onMounted } from 'vue';

// 这里需要配置 表格字段和搜索条件
import {
      
       roleColumnsData, roleQueryData, menuColumnsData, menuQueryData } from "./tableDialog"

const state = reactive({
      
      
    // 提交表单
    form: {
      
      
        menuId: []
    }
})

const {
      
       form } = toRefs(state)

onMounted(() => {
      
      

})

// LTableDialog Change事件,可选可不选
function roleChange(e) {
      
      
    console.log(e)
}

</script>

<style scoped></style>

2. Tabular data and search criteria configuration files

const roleColumnsData: any = [
    {
    
    
        prop: 'roleName', // 字段名称
        label: '角色名称', // 表格表头名称
        min_width: 120,	// 表格最小长度
        fixed: true // 表格是否固定
    },
    {
    
    
        prop: 'roleRemark',
        label: '角色备注',
        align: 'center', // 表格对齐方式
        width: 200, // 表格长度
    },
    {
    
    
        prop: 'createDate',
        label: ' 创建时间',
        align: 'center',
        type: 'time', // 表格类型
        width: 180,
    },
]

const roleQueryData: any = [
	// label:查询条件名称 、 prop:查询字段(传给后端的) 、 text:查询类型
    {
    
     label: "角色名称", prop: 'roleName', type: 'text' }, 
    {
    
     label: "创建时间", prop: 'dateTime', type: 'date' },
]

const menuColumnsData: any = [
    {
    
    
        prop: 'menuName',
        label: '菜单名称',
        min_width: 120,
        fixed: true
    },
    {
    
    
        prop: 'menuPath',
        label: '菜单路径',
        align: 'center',
        width: 200,
    },
    {
    
    
    	// 后端返回类型是 1、2 的情况,这里是做转换的
        prop: 'menuType',
        label: '菜单类型',
        align: 'center',
        // type 也是要传的
        type: 'status',
        // 不同类型对应的名称
        option: {
    
    
            '1': '目录',
            '2': '页面'
        },
        // 不同类型对应的颜色
        color: {
    
    
            '1': ' #a0cfff',
            '2': ' #b3e19d',
        },
        width: 100
    },
    {
    
    
        prop: 'parentName',
        label: '父级菜单名称',
        align: 'center',
        width: 200,
    },
    {
    
    
        prop: 'createDate',
        label: ' 创建时间',
        align: 'center',
        type: 'time',
        width: 180,
    },
]

const menuQueryData: any = [
    {
    
     label: "菜单名称", prop: 'menuName', type: 'text' },
]

export {
    
    
    roleColumnsData, // 角色表格数据
    roleQueryData, // 角色搜索条件
    menuColumnsData, // 菜单表格数据
    menuQueryData // 菜单搜索条件
}

3. LTableDialog component encapsulation

<!-- 表格对话框组件 -->
<template>
    <div class="l-table-dialog">
        <!-- 这里是el-select选择器 -->
        <el-select v-model="newValue" :multiple="props.multiple" collapse-tags placeholder="点击选择数据">
            <el-option :label="item[props.checkLabel]" :value="item[props.checkValue]" v-for="(item, index) in dataList"
                :key="item.id" />
        </el-select>
        <el-button :icon="Search" @click="openDialog" />
        <el-dialog v-model="tableDialog" :title="props.title" :close-on-click-modal="false">
            <!-- LQuery是之前封装的搜索组件,下面我会粘出文档地址 -->
            <LQuery :queryModule="queryModule"></LQuery>
            <!-- el-table 我们的表格组件 -->
            <el-table :data="dataList" border style="width: 100%; overflow-y: scroll" v-loading="loading"
                :current-row-key="props.checkValue" @select="selectChange" ref="tableRef"
                :class="props.multiple ? '' : 'multipleShow'">
                <!-- :class="props.multiple ? '' : 'multipleShow'" 单选时隐藏表格左上角全选功能-->
                <el-table-column type="selection" width="50" align="center" />
                <!-- 这里就是循环我们上面配置好的表格数据,做渲染 -->
                <el-table-column v-for="(item, index) in props.tableColumns" :key="item.prop" :prop="item.prop"
                    :label="item.label" :align="item.align || 'left'" :width="item.width" :min-width="item.min_width"
                    :fixed="item.fixed">
                    <template slot-scope="scope" #default="scope">
                        <!-- 根据不用的类型对数据做处理 -->
                        <div v-if="item.type == 'status'">
                            <el-tag>{
   
   {
                                fieldChange(scope.row[item.prop], item.option) }}</el-tag>
                        </div>
                        <div v-else-if="item.type == 'image'">
                            <el-image style="width: 60px; height: 60px" :src="scope.row[item.prop]"
                                :preview-src-list="[scope.row[item.prop]]" :preview-teleported="true">
                            </el-image>
                        </div>
                        <div v-else-if="item.type == 'time'">{
   
   { formatDate(scope.row[item.prop]) }}</div>
                        <div v-else>{
   
   { scope.row[item.prop] }}</div>
                    </template>
                </el-table-column>
            </el-table>
            <div class="l-pages">
                <!-- 分页组件 -->
                <el-pagination :current-page="pages.page" :page-size.sync="pages.limit" :page-sizes="pageSizes"
                    :layout="layout" :total="pages.total" @size-change="sizeChange" @current-change="currentChange" />
            </div>
            <template #footer>
                <span class="dialog-footer">
                    <el-button @click="tableDialog = false">取 消</el-button>
                    <el-button type="primary" @click="Confirm">确 定</el-button>
                </span>
            </template>
        </el-dialog>
    </div>
</template>

<script setup>
import {
      
       defineProps, computed, nextTick, onMounted, reactive, ref, toRefs, defineEmits } from 'vue'
import {
      
       Search } from '@element-plus/icons-vue'
import {
      
       ElTable } from 'element-plus';

import {
      
       formatDate } from '@/utils/index'
import {
      
       roleList } from '@/api/role/index'
import {
      
       menuList } from '@/api/menu/index'

const props = defineProps({
      
      
    tableColumns: {
      
       // 表格数据,类型:数组
        type: Array
    },
    tableQuery: {
      
       // 搜索条件,类型:数组
        type: Array
    },
    method: {
      
       // 这个是方法,下面会讲到
        type: String
    },
    modelValue: {
      
       // 选择的值,单选时类型为Number,多选时类型为Array
        type: [Number, Array]
    },
    layout: {
      
       // 分页组件配置
        type: String,
        default: "total, sizes, prev, pager, next, jumper",
    },
    pageSizes: {
      
       // 每页条数
        type: Array,
        default() {
      
      
            return [10, 20, 30, 50];
        },
    },
    // el-select 中的el-option需要两个属性分别是value:选择的值、label值对应的名称
    // 因为我们el-option肯定是循环出来的,每次请求列表拿回来的值也都不是一样的,所以我们需要两个变量来控制
    checkLabel: {
      
      
        type: String,
        default: "name"
    },
    checkValue: {
      
      
        type: String,
        default: "id"
    },
    // dialog 对话框标题
    title: {
      
      
        type: String,
        default: "表格对话框"
    },
    // 是否开启多选,默认不开启
    multiple: {
      
      
        type: Boolean,
        default: false
    }
})

// 子组件调用父组件方法
const emit = defineEmits(['update:modelValue', 'change'])

const tableRef = ref(ElTable);

// 利用计算属性修改父组件绑定的值
const newValue = computed({
      
      
    get() {
      
      
        return props.modelValue
    },
    set(value) {
      
      
        emit('update:modelValue', value)
    }
})

const state = reactive({
      
      
    tableDialog: false,
    loading: false,
    dataList: [], // 请求的数据

    queryForm: {
      
      },
    pages: {
      
      
        total: 0,
        limit: 20,
        page: 1,
    }
})

const {
      
       tableDialog, loading, dataList, pages, queryForm } = toRefs(state)

// 这个是 搜索组件需要用到的
const queryModule = ref({
      
      
    queryForm: queryForm,
    query: props.tableQuery,
    Search: tableSearch,
    Recover: tableRecover
})

onMounted(() => {
      
      
    getDataList()
})

// 打开表格对话框
function openDialog() {
      
      
    /*
        打开对话框,如果el-select有选中值的情况下在 dataList 中过滤出来选中的数据存放在 row 变量中
        接着使用 el-table 中的 toggleRowSelection 方法切换某一行的状态
    */
    let row = []
    state.tableDialog = true
    if (props.multiple) {
      
      
        state.dataList.filter(item => {
      
      
            props.modelValue.includes(item[props.checkValue]) && row.push(item);
        });
    } else {
      
      
        row = state.dataList.filter(i => i.id == props.modelValue)
    }
    nextTick(function () {
      
      
        row.forEach(item => {
      
      
            tableRef.value.toggleRowSelection(item, true)
        })
    })
}

// 获取表格数据
async function getDataList() {
      
      
    // eval(`${props.method}({ pages: state.pages, query: state.queryForm })`) 这里是我自己请求后台接口的方式
    // props.method 就是请求方法,后面则是传的参数,如果还看不懂的话,看下面例子
    /*
        例子:
        roleList({ pages: state.pages, query: state.queryForm }).then(res=>{}).catch(res=>{})
    */
    // eval:可以接收一个字符串作为脚本代码运行
    state.loading = true
    const res = await eval(`${ 
        props.method}({ pages: state.pages, query: state.queryForm })`)
    state.loading = false
    // 下面就是我接口返回的结果咯。
    state.dataList = res.data
    state.pages.total = res.total
    /*
        [
            {
                "id": 1,
                "roleName": "开发角色",
                "roleRemark": "用于开发阶段",
                "menus": "1,2,3,4,5,6",
                "menuPowers": "1,2,3,4,5,6,13,7,8,9,10,11,12",
                "roleStatus": 1,
                "createDate": 1682406566687
            },
            {
                "id": 3,
                "roleName": "管理权限角色",
                "roleRemark": "管理菜单权限",
                "menus": "",
                "menuPowers": "",
                "roleStatus": 1,
                "createDate": 1683614324526
            }
        ]
    */

    // 这样方便做假数据测试了吧
}

// 确认数据
function Confirm() {
      
      
    /*
        dialog 确认事件
        确认数据是判断当前组件是多选还是单选,多选返回数组,单选返回数字
        tableRef.value.getSelectionRows() getSelectionRows方法拿到当前表格选中的数据
        select.map(i => i[props.checkValue]) 将所需要的值返回成一个数组
        emit('change', select[0]) 调用父组件Change方法,将选中的整个对象或整个数据返回
    */
    let select = tableRef.value.getSelectionRows()
    if (props.multiple) {
      
      
        let ids = select.map(i => i[props.checkValue])
        emit('update:modelValue', ids)
        emit('change', select)
    } else {
      
      
        emit('update:modelValue', select[0][props.checkValue])
        emit('change', select[0])
    }
    state.tableDialog = false
}

// 处理表格数据
function fieldChange(row, option) {
      
      
    if (option[row]) {
      
      
        return option[row]
    }
}

// 表格
// el-table select事件,手动勾选数据行checkbox时触发
/*
    这里需要判断,组件为单选时
    需要先试用 clearSelection 方法清除用户的选择
    在使用 toggleRowSelection 方法根据select返回的第二个参数 row 去选中用户选择的行
*/
function selectChange(e, row) {
      
      
    if (!props.multiple) {
      
      
        tableRef.value.clearSelection()
        nextTick(function () {
      
      
            tableRef.value.toggleRowSelection(row, true)
        })
    }
}

// 下面就是一些分页和搜索触发的事件了

// 分页 页数事件
function sizeChange(item) {
      
      
    state.pages.limit = item
    getDataList()
}

// 分页条数事件
function currentChange(item) {
      
      
    state.pages.page = item
    getDataList()
}

function tableSearch() {
      
      
    getDataList()
}
function tableRecover() {
      
      
    state.queryForm = {
      
      }
    getDataList()
}
</script>

<style lang="scss" scoped>
.l-table-dialog {
      
      

    .el-table {
      
      
        height: calc(100% - 76px);
    }

    .multipleShow {
      
      
        ::v-deep .el-table__header {
      
      
            .el-table-column--selection {
      
      
                .cell {
      
      
                    display: none;
                }
            }
        }
    }

    .el-select {
      
      
        width: 220px;
    }

    ::v-deep .el-dialog {
      
      
        width: 1000px;
        height: 640px;

        .el-dialog__body {
      
      
            height: calc(100% - 110px);
            padding: 5px 20px;
        }
    }

    .l-pages {
      
      
        margin-top: 10px;
    }
}
</style>

end

LQueryComponent link Element Plus el-table form query encapsulation

If there are any unreasonable components, please correct me.
When using, pay attention to replacing the place where the list data is requested with your own.
If you don't understand, you can leave a message or chat privately.

Thank you for watching~

Guess you like

Origin blog.csdn.net/m0_67584973/article/details/131112954