vue3+vxe-table实现可编辑、展开行(展开行内容为动态表格)

4,462 阅读2分钟

需求:为了支持运维的高效率工作,需要新增,编辑,归档事项,事项中又包括了task1,task2,task3......在任务项中又包括了要做的各种编辑项,新增项,先几个图来介绍下:

1.新增事项:通过弹窗填写新增的事项,其中包括了提交时做的各种校验

image.png

2.事项列表,包括编辑事项和新增的任务的操作项,(不要问我为什么没有删除项,这里的设计是在我们点击编辑时。有一列“事项状态”,可以更改里面的事项状态来达到删除的作用)

image.png

其中还,每个展开行中都会有一个表格,通过点击事项名称或者 “新增任务”按钮 可展开行

image.png

3.新增任务:这里给出的需求是在原来有的事项中去新增子事项,我们这里称新任务,那么就是需要新增一行表格,并且表格内要有对应的填写项,而且是可以指定是在任务的前面还是末尾添加一行表格;

image.png

4.编辑事项/任务:这两个项时比较相似的,也就是点击编辑可改当前事项的内容

image.png

分析: 用到vxe-table,里面比较相像的是tree结构和展开行,但是对于这两种,我们还是选择展开行内嵌入表格。找个主要是考虑展开行的数据是一个有自己表头的单独表格。然后后端返回的数据中也不好定义parentId和自己的id。同时我们这边来看一下后端的数据输出格式:

image.png

那就是集展开行、编辑、树形表格于一体,下面我们来直接上代码

结构这块我们可以这样实现: `

            <vxe-table ref="xTable" border show-overflow show-header-overflow :column-config="{resizable: true}"
                :loading="editData.loading" :data="editData.tableData" class="event-style" keep-source
                :row-class-name="eventRowClass" :expand-config="{labelField: 'qe_event_name'}"
                :edit-config="{trigger: 'manual', mode: 'row'}" :tooltip-config="editData.tableTooltipConfig">
                <!-- @edit-closed="cancelRowEvent" -->

                <vxe-column field="qe_event_name" title="事项" type="expand">
                    <!-- 展开行 -->
                    <template #content="{row:eventRow,rowIndex:eventRowIndex}">

                        <vxe-table border :ref="(el)=>setItemRef(el, 'tree'+eventRow.qe_event_index)" show-overflow
                            keep-source show-header-overflow :data="eventRow.qe_task_list"
                            :column-config="{resizable: true}" :edit-config="{trigger: 'manual', mode: 'row'}"
                            style="padding: 20px;" :tooltip-config="editData.taskTooltipConfig">
                            <!-- @edit-closed="cancelRowTask(eventRow.qe_event_index)" -->
                            <vxe-column field="qt_task_name" title="任务" :edit-render="{}">
                                <template #edit="{ row }">
                                    <vxe-input v-model="row.qt_task_name" type="text" placeholder="请输入"></vxe-input>
                                </template>
                            </vxe-column>
                            <vxe-column field="qt_task_oper" title="跟进人" :edit-render="{}" width="100">
                                <template #edit="{ row }">
                                    <vxe-input v-model="row.qt_task_oper" type="text" placeholder="请输入"></vxe-input>
                                </template>
                            </vxe-column>

                            <vxe-column field="qt_task_progress" title="进度描述" :edit-render="{}">
                                <template #edit="{ row }">
                                    <vxe-input v-model="row.qt_task_progress" type="text"
                                        placeholder="请输入"></vxe-input>
                                </template>
                            </vxe-column>
                            <vxe-column field="qt_task_status" title="任务状态" :edit-render="{}" width="100">
                                <template #default="{ row }">

                                    <el-tag v-if="row.qt_task_status" :type="tagType(row.qt_task_status)">
                                        {{row.qt_task_status}}
                                    </el-tag>
                                </template>
                                <template #edit="{ row }">
                                    <vxe-select v-model="row.qt_task_status " transfer>
                                        <vxe-option v-for="(item,idx) in editData.taskOption" :key="idx+100000"
                                            :value="item" :label="item"></vxe-option>
                                    </vxe-select>
                                </template>
                            </vxe-column>

                            <vxe-column field="qt_task_estimate" title="预计完成日期" :edit-render="{}" width="125">
                                <template #edit="{ row }">
                                    <vxe-input v-model="row.qt_task_estimate " type="date" placeholder="请选择日期"
                                        transfer></vxe-input>
                                </template>
                            </vxe-column>
                            <vxe-column field="qt_task_link" title="相关链接" :edit-render="{}">
                                <template #edit="{ row }">
                                    <vxe-input v-model="row.qt_task_link" type="text"></vxe-input>
                                </template>
                            </vxe-column>
                            <vxe-column field="qt_task_create" title="任务创建日期" width="125">
                            </vxe-column>
                            <vxe-column title="操作" align="center" width="160" fixed="right">
                                <template #default="{ row }">
                                    <template v-if="isEditTask(eventRow.qe_event_index,row)">

                                        <el-button size="small" type="primary"
                                            @click="saveRowTask(eventRow,row,eventRowIndex)">保存</el-button>
                                        <el-button size="small"
                                            @click="cancelRowTask(eventRow.qe_event_index)">取消</el-button>
                                    </template>
                                    <template v-else>
                                        <el-button @click="editRowTask(eventRow,row)">编辑任务</el-button>
                                    </template>

                                </template>
                            </vxe-column>
                        </vxe-table>
                        <!-- 展开行终止 -->
                    </template>
                    <template #edit="{ row}">
                        <vxe-input v-model="row.qe_event_name " type="text"></vxe-input>
                    </template>
                </vxe-column>

                <vxe-column field="qe_event_creator" title="创建人" :edit-render="{}" width="100">
                    <template #edit="{ row }">
                        <vxe-input v-model="row.qe_event_creator " type="text" placeholder="创建人"></vxe-input>
                    </template>
                </vxe-column>
                <vxe-column field="qe_event_detail" title="事项描述" :edit-render="{}">
                    <template #edit="{ row }">
                        <vxe-input v-model="row.qe_event_detail " type="text" placeholder="事项描述"></vxe-input>
                    </template>
                </vxe-column>
                <vxe-column field="qe_event_progress" title="进度描述" :edit-render="{}">
                    <template #edit="{ row }">
                        <vxe-input v-model="row.qe_event_progress" type="text" placeholder="进度描述"></vxe-input>
                    </template>
                </vxe-column>
                <vxe-column field="qe_event_status" title="事项状态" :edit-render="{}" width="100">
                    <template #default="{ row }">
                        <!-- <span>{{row.qe_event_status}}</span> -->
                        <el-tag v-if="row.qe_event_status" :type="tagType(row.qe_event_status)">
                            {{row.qe_event_status}}
                        </el-tag>
                    </template>
                    <template #edit="{ row }">
                        <vxe-select v-model="row.qe_event_status" transfer placeholder="事项状态">
                            <vxe-option v-for="(item,idx) in editData.eventOption" :key="idx+1000" :value="item"
                                :label="item"></vxe-option>
                        </vxe-select>
                    </template>
                </vxe-column>

                <vxe-column field="qe_event_estimate" title="预计完成日期" :edit-render="{}" width="125">
                    <template #edit="{ row }">
                        <vxe-input v-model="row.qe_event_estimate " type="date" placeholder="日期"
                            transfer></vxe-input>
                    </template>
                </vxe-column>
                <vxe-column field="qe_event_link" title="相关链接" :edit-render="{}">
                    <template #edit="{ row }">
                        <vxe-input v-model="row.qe_event_link " type="text" placeholder="多个链接用分号分隔"></vxe-input>
                    </template>
                </vxe-column>
                <vxe-column field="qe_event_create" title="任务创建日期" width="120">

                </vxe-column>
                <vxe-column title="操作" align="center" width="200" fixed="right">
                    <template #default="{ row }">
                        <template v-if="$refs.xTable.isEditByRow(row)">
                            <el-button size="small" type="primary" @click="saveRowEvent(row)">保存</el-button>
                            <el-button size="small" @click="cancelRowEvent()">取消</el-button>
                        </template>
                        <template v-else>
                            <el-button type="info" size="small" plain @click="editRowEvent(row)">编辑事项</el-button>
                            <el-button type="success" size="small" plain @click="addTaskEvent(row)">新增任务</el-button>
                        </template>

                    </template>

                </vxe-column>
            </vxe-table>`

初始化定义:

const editData = reactive({
    loading: false,
    tableData: [

    ],
    taskOption: ["未开始", "进行中", "已完成", "暂停"],
    eventOption: ["未开始", "进行中", "已完成", "暂停", "归档"],
    tableTooltipConfig: {
        howAll: true,
        enterable: true,
        theme: 'light',
        // contentMethod: ({ type, column, row, items, _columnIndex }) => {
        //     const { field } = column
        //     if (!field) {
        //         return
        //     }
        //     if (field == 'qe_event_link') {
        //         let arrLink = row[field].split(';')
        //         return arrLink.join('\n')
        //     }

        //     //return row[field]
        // }
    },
    taskTooltipConfig: {
        howAll: true,
        enterable: true,
        theme: 'light',
    }
})
    const xTable = ref()

在结构中我们可以观察到有动态树的存在,我们是这么解决的:

        if (el) {
            iframeRefs[key] = el
        }
    };
     const iframeRefs = {}`

对于事件的操作:

//编辑事项
const editRowEvent = (row) => {
        const $table = xTable.value
        $table.setEditRow(row)

    }
//保存事项
const saveRowEvent = (row) => {
        const $table = xTable.value
        $table.clearEdit().then(() => {
            editData.loading = true
            setTimeout(() => {
                editData.loading = false
                let obj = {
                    qe_event_index: row.qe_event_index,
                    qe_event_name: row.qe_event_name,
                    qe_event_creator: row.qe_event_creator,
                    qe_event_detail: row.qe_event_detail,
                    qe_event_estimate: row.qe_event_estimate,
                    qe_event_link: row.qe_event_link,
                    qe_event_status: row.qe_event_status,
                    qe_event_progress: row.qe_event_progress
                    //task_list: row.task_list

                }

                create_event(obj).then(res => {
                    if (res.code == 200) {
                        ElMessage.success(res.message)
                        getTable();

                    } else {
                        ElMessage.error(res.message)
                    }

                })

            }, 300)
        })
    }
//取消保存事项
const cancelRowEvent = () => {
        const $table = xTable.value
        $table.clearEdit().then(() => {
            // 还原行数据
            $table.revertData()
        })
    }



//新增任务
    const addTaskEvent = async (row) => {
        const $fatable = xTable.value
        if (!$fatable.isExpandByRow(row)) {
            await $fatable.toggleRowExpand(row)
        }

        const $table = iframeRefs['tree' + row.qe_event_index]
        const record = {
            qt_task_name: '',
            qt_task_oper: '',
            qt_task_progress: '',
            qt_task_status: '',
            qt_task_create: '',
            qt_task_estimate: '',
            qt_task_link: ''
        }

        const { row: newRow } = await $table.insertAt(record, row.qe_task_list[0])
        await $table.setEditCell(newRow,)
        await $table.setEditRow(newRow);
    }

//编辑任务
const editRowTask = (frow, row) => {
        const $table = iframeRefs['tree' + frow.qe_event_index]
        currentTaskRowKey.value = row.qt_task_index;
        $table.setEditRow(row)
    }

//保存任务
    const saveRowTask = (frow, row, eventRowIndex) => {

        fatherRow.value = frow;
        let task_list_name = frow.qe_task_list.map(item => {
            return item.qt_task_name
        })

        if (row.qt_task_name == '') {
            ElMessage.error('任务名称不能为空')
            return;
        }
        //新增下的任务
        if (!row.qt_task_index) {
            if (task_list_name.indexOf(row.qt_task_name) != -1) {
                ElMessage.warning('新增的任务有重新啦!')
                return;
            }
        }
        //编辑任务
        // if (row.qt_task_index == currentTaskRowKey.value) {
        //     let flagFtype = task_list_others.value.every((item) => !!item.qt_task_name);
        //     if (flagFtype) {
        //         ElMessage.warning('修改后的任务名已经存在啦!')
        //         return;
        //     }
        // }

        const $table = iframeRefs['tree' + frow.qe_event_index]

        $table.clearEdit().then(() => {

            setTimeout(() => {


                let obj = {
                    findex: row.qt_task_index ? row.qt_task_index : '',
                    ops_event: frow.qe_event_name,
                    ops_task: row.qt_task_name,
                    ops_person: row.qt_task_oper,
                    fprogress: row.qt_task_progress,
                    ftask_status: row.qt_task_status,
                    festimate_time: row.qt_task_estimate,
                    flink: row.qt_task_link,
                }
                edit_save(obj).then(res => {
                    if (res.code == 200) {
                        ElMessage.success(res.message)
                        getTable();
                        setTimeout(() => {
                            // xTable.value.setAllRowExpand(true)
                            //console.log(fatherRow.value, 'fatherRow.valuefatherRow.valuefatherRow.value');
                            xTable.value.setRowExpand(editData.tableData[eventRowIndex], true)
                        }, 300)


                    } else {
                        ElMessage.error(res.message)
                    }

                })

            }, 100)
        })
    }
//取消保存任务
 const cancelRowTask = async (key) => {
        const $table = iframeRefs['tree' + key]
        await $table.clearEdit().then(() => {
            // 还原行数据
            $table.revertData()
        })

    }