复杂表格的前端处理

适用场景:element组件(ui及plus都可以,核心思路是一致的)

情况一:element+多选+翻页+勾选

关于已上的情况,网上的资源相对还是比较多的,本人受益于以下文这两篇文章:

需求定义

而本人这次的需求,相较于以往的要更加复杂一些:

具体需求: 用户初选择页面:是table嵌套table的模式,选择的是内部table的row;
仅显示已选择后,需要罗列出已选择的rows;返回查看全部数据的时候,需要将已选择的数据回显进内部的table里。
具体如下图所示,帮助大家理解需求:

  1. 选择第一页数据:
  2. 选择第二页数据
  3. 查看已选择数据
  4. 再次回看全部数据(回显打勾)
    首先,会返回之前的第二页,显示了之前选择的第二页情况;点击进入第一页,一开始每个父table都是收起的状态,点击张开后,需要重新勾选上已选择的项目。

代码实现

由于本人的仅查看已选择和全部的table不一致,则写了两个vue的文件,如下:

其中需要注意以下几点:
1、< ComSubList >和 < ComChooseTask >为组件形式引用,目的一是为了代码清晰;二是< ComSubList >组件在每次点击的时候会根据父组件那行的id传值,生成新的table里的数据,以组件形式书写更易独立代码空间。
可以看出< ComSubList >组件向内传值了scope.row.taskGroupId,就是为了初始化的时候向后台申请数据。
2、使用了v-show而不是v-if
如果使用了v-if在切换已选择和全部的时候会重新加载一遍,增加了一些不必要的后台请求,用户体验也不是很好(翻页的后台请求是无法避免,但是不翻页情况下,且大量用户操作完一般不会再多次翻页查看回显,更可能去已选择那里查看已选择的,因此v-show的体验感会更好)
且v-show是放在了单独的div上的,这是因为v-show会和有flex属性产生冲突,因此尽量放在纯净的div上。

<template>
  <div class="cards">
    <div class="opt">
      <div> </div>
      <div>
        <el-checkbox v-model="formModel.isUsed" @change="onIsUsedChanged">仅显示已选中</el-checkbox
        >   
        <el-button type="primary" @click="onMerge">合并路线</el-button>
        <el-button type="primary" @click="onDelete">删除</el-button>
        <el-button type="primary" @click="onRouteLibrary">导入路线库</el-button>
      </div>
    </div>
    // v-show="!formModel.isUsed"的为全部数据的情况
    <div v-show="!formModel.isUsed">
      <el-table
        ref="multipleTableRef"
        :data="list"
        style="width: 100%"
        border
        @expand-change="expandChange"
      >
        <el-table-column type="expand">
          <template #default="scope">
            <ComSubList ref="rSublist" :taskGroupId="scope.row.taskGroupId"></ComSubList>
          </template>
        </el-table-column>

        <el-table-column label="Operate" width="160">
          <template #default="scope">
            <el-button size="mini" @click="onTestResult(scope.row)">测试结果</el-button>
          </template>
        </el-table-column>
        <el-table-column property="taskGroupId" label="Group ID" width="80" />
        <el-table-column property="taskTypeValue" label="Test Type" />
        <el-table-column property="taskName" label="Test Name" />
        <el-table-column property="countryName" label="Test Nation" />
        <el-table-column property="operatorName" label="carrier" />
        <el-table-column property="projectName" label="Test Project" />
        <el-table-column property="createTime" label="Test Time">
          <template #default="scope">{{ time(scope.row.createTime) }}</template>
        </el-table-column>
        <!-- <el-table-column property="testResult" label="Test Result" /> -->
        <el-table-column property="createOwner" label="owner" />
      </el-table>
      <el-pagination
        v-model:current-page="page"
        v-model:page-size="count"
        :page-sizes="[10, 20, 500]"
        small="small"
        layout="total, sizes, prev, pager, next, jumper"
        :total="totalCount"
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
      />
    </div>
    // v-show="formModel.isUsed"的为已选择数据的情况
    <div v-show="formModel.isUsed">
      <ComChooseTask ref="rChooseTask"></ComChooseTask>
    </div>
  </div>
</template>

<script lang="ts">
import { defineComponent, computed, reactive, nextTick } from 'vue'
import { Delete, Download } from '@element-plus/icons-vue'
import { useStore } from 'vuex'
import ComChooseTask from './chooseList.vue'
import ComSubList from './sublist.vue'

export default defineComponent({
  props: {},
  components: { Delete, Download, ComSubList, ComChooseTask },
  beforeCreate() {},
  setup() {
    const store: any = useStore($key)
    const state: any = reactive({
      formModel: {},
      formOpts: {
        dialog: false
      },
      rChooseTask: null,
      rSublist: null,
      list: computed({
        get() {
          return store.state.imapList.list
        },
        set(val: string) {
          store.commit(`imapList/list`, val)
        }
      }),
      page: computed({
        get() {
          return store.state.imapList.page
        },
        set(val: string) {
          store.commit(`imapList/page`, val)
        }
      }),
      count: computed({
        get() {
          return store.state.imapList.count
        },
        set(val: string) {
          store.commit(`imapList/count`, val)
        }
      }),
      totalCount: computed({
        get() {
          return store.state.imapList.totalCount
        },
        set(val: string) {}
      }),
      time(val: any) {
        return $func.time(val)
      },
      async handleSizeChange(val: any) {
        await $store.dispatch('imapList/loadList')
      },
      async handleCurrentChange(val: any) {
        await $store.dispatch('imapList/loadList')
      },
      onIsUsedChanged() {},
      async expandChange(row: any) {},
      async onTestResult(row: any) {
        store.commit('taskList/detailVisable', false)
        store.commit('taskList/groupId', row.taskGroupId)
        await store.dispatch('taskList/loadSubList')
        $router.push('/taskResult')
      },
      onTestLine() {
        $router.push('/imap')
      },
      onDelete() {},
      onMerge() {},
      onRouteLibrary() {}
    })
    return state
  },
  created() {}
})
</script>

ComSubList组件

<template>
  <div>
    <el-table
      ref="rTable"
      :data="formModel.sublist"
      style="width: 100%"
      border
      @selection-change="selectionChange"
      @select="handleSelectionChange"
    >
      <el-table-column type="selection" width="55" />
      <el-table-column label="Operate" width="160">
        <template #default="scope">
          <el-button size="mini" @click="onTestLine(scope.row)">测试路线</el-button>
        </template>
      </el-table-column>
      <el-table-column property="taskId" label="Task ID" width="80" />
      <el-table-column property="deviceSn" label="Device SN" />
      <el-table-column property="deviceStatus" label="Device Status" />
      <el-table-column property="runState" label="Run State" />
      <el-table-column property="createTime" label="Create Time">
        <template #default="scope">{{ time(scope.row.createTime) }}</template>
      </el-table-column>
      <el-table-column property="assignTime" label="Assign Time">
        <template #default="scope">{{ time(scope.row.assignTime) }}</template>
      </el-table-column>
      <el-table-column property="finishTime" label="Finish Time">
        <template #default="scope">{{ time(scope.row.finishTime) }}</template>
      </el-table-column>
      <el-table-column property="testResult" label="Test Result" />
    </el-table>
    <el-dialog title="地图显示" width="80%" v-model="formOpts.dialog" draggable>
      <ComMap></ComMap>
    </el-dialog>
  </div>
</template>

<script lang="ts">
import { defineComponent, computed, reactive, nextTick } from 'vue'
import { Delete, Download } from '@element-plus/icons-vue'
import ComMap from '../map/index.vue'
import { useStore } from 'vuex'

export default defineComponent({
  props: {
    taskGroupId: {
      type: Number,
      required: true
    }
  },
  components: { Delete, Download, ComMap },
  beforeCreate() {},
  setup(props: any, ctx) {
    const store: any = useStore($key)
    const state: any = reactive({
      formModel: {
        sublist: []
      },
      formOpts: {
        dialog: false
      },
      rTable: null,
      chooseSublist: computed({
        get() {
          return store.state.imapList.chooseSublist
        },
        set(val: string) {}
      }),
      chooseListId: computed({
        get() {
          return store.state.imapList.chooseListId
        },
        set(val: string) {}
      }),
      time(val: any) {
        return $func.time(val)
      },
      async selectionChange(val: any) {
        if (val) {
          // 将获取到的id存入tableAllSelectedId数组(点击某行前面的勾选、选中某行的勾选、全选。三种状态都能触发此功能)
          let len = val.length
          for (let i = 0; i < len; i++) {
            if (val[i] === undefined) {
              continue
            } else {
              if (state.chooseListId.indexOf(val[i].taskId) === -1) {
                store.commit('imapList/addTask', val[i])
              }
            }
          }
          console.log('state.formOpts.tableAllSelectedId', state.chooseListId)
        }
      },
      async handleSelectionChange(rows: any, row: any) {
        // 判断是点击了表格勾选还是取消勾选
        // true就是选中,0或者false是取消选中
        const selected = rows.length && rows.indexOf(row) !== -1
        if (!selected) {
          // 如果点击取消勾选
          store.commit('imapList/delTask', row)
        }
        console.log('selectes -> tableAllSelectedId', state.chooseListId)
      },
      tick(rows?: any[]) {
        if (rows) {
          nextTick(() => {
            rows.forEach((row) => {
              state.rTable.toggleRowSelection(
                state.formModel.sublist.find((item: any) => {
                  return row.taskId == item.taskId // 注意这里寻找的字段要唯一,示例仅参考
                }),
                true
              )
            })
          })
        }
      },
      async load(row: any) {
        let res = await store.dispatch(`imapList/loadSubList`, props.taskGroupId)
        state.formModel.sublist = res
        state.tick(state.chooseSublist)
      },
      onTestLine() {
        state.formOpts.dialog = true
      }
    })
    return state
  },
  created() {
    this.load()
  }
})
</script>

<style lang="less" scoped></style>

ComChooseTask组件

<template>
  <el-table
    ref="multipleTableRef"
    :data="chooseSublist"
    style="width: 100%"
    border
    @selection-change="handleSelectionChange"
  >
    <el-table-column type="selection" width="55" />
    <el-table-column label="Operate" width="160">
      <template #default="scope">
        <el-button size="mini" @click="onTestLine(scope.row)">测试路线</el-button>
      </template>
    </el-table-column>
    <el-table-column property="taskId" label="Task ID" width="80" />
    <el-table-column property="deviceSn" label="Device SN" />
    <el-table-column property="deviceStatus" label="Device Status" />
    <el-table-column property="runState" label="Run State" />
    <el-table-column property="createTime" label="Create Time">
      <template #default="scope">{{ time(scope.row.createTime) }}</template>
    </el-table-column>
    <el-table-column property="assignTime" label="Assign Time">
      <template #default="scope">{{ time(scope.row.assignTime) }}</template>
    </el-table-column>
    <el-table-column property="finishTime" label="Finish Time">
      <template #default="scope">{{ time(scope.row.finishTime) }}</template>
    </el-table-column>
    <el-table-column property="testResult" label="Test Result" />
  </el-table>
  <el-dialog title="地图显示" width="80%" v-model="formOpts.dialog" draggable>
    <ComMap></ComMap>
  </el-dialog>
</template>

<script lang="ts">
import { defineComponent, computed, reactive, nextTick } from 'vue'
import { Delete, Download } from '@element-plus/icons-vue'
import ComMap from '../map/index.vue'
import { useStore } from 'vuex'

export default defineComponent({
  props: {
    taskGroupId: {
      type: Number,
      required: true
    }
  },
  components: { Delete, Download, ComMap },
  beforeCreate() {},
  setup(props: any, ctx) {
    const store: any = useStore($key)
    const state: any = reactive({
      formModel: {},
      formOpts: {
        dialog: false
      },
      chooseSublist: computed({
        get() {
          return store.state.imapList.chooseSublist
        },
        set(val: string) {
          store.commit(`imapList/chooseSublist`, val)
        }
      }),
      chooseListId: computed({
        get() {
          return store.state.imapList.chooseListId
        },
        set(val: string) {
          store.commit(`imapList/chooseListId`, val)
        }
      }),
      time(val: any) {
        return $func.time(val)
      },
      async handleSelectionChange() {},
      onTestLine() {
        state.formOpts.dialog = true
      }
    })
    return state
  },
  created() {}
})
</script>

<style lang="less" scoped>
</style>

本人再附上vuex里的一些逻辑,里面可能有些没用到,你们自己省略就好,我就直接copy上来了。

import { ElMessage } from 'element-plus'

// imap list
const vx = {
  namespaced: true,
  state: {
    // model
    count: 10,
    countryId: null,
    endTime: null,
    keyWord: null,
    operatorId: null,
    page: 1,
    projectId: '',
    startTime: null,
    taskType: null,
    testType: 0,
    list: [],

    //choose list
    chooseSublist: [],
    chooseListId: [],

    // opts
    totalCount: 0,
    countryOperators: [],
    projects: [],
    taskTypes: [
      {
        value: 0,
        label: 'APK'
      },
      {
        value: 1,
        label: 'Web'
      }
    ],
    testTypes: [
      {
        value: 0,
        label: 'Itestplus'
      },
      {
        value: 1,
        label: 'Box'
      }
    ]
  },
  mutations: {
    reset(state: any) {
      ;(state.count = 10),
        (state.countryId = null),
        (state.endTime = null),
        (state.keyWord = null),
        (state.operatorId = null),
        (state.page = 1),
        (state.projectId = ''),
        (state.startTime = null),
        (state.taskType = null),
        (state.testType = 0),
        (state.countryOperators = []),
        (state.projects = []),
        (state.totalCount = 0),
        (state.list = []),
        (state.chooseSublist = []),
        (state.chooseListId = [])
    },
    totalCount(state: any, val: any) {
      state.totalCount = val
    },
    page(state: any, val: any) {
      state.page = val
    },
    count(state: any, val: any) {
      state.count = val
    },
    list(state: any, val: any) {
      state.list = val
    },
    chooseSublist(state: any, val: any) {
      state.chooseSublist = val
    },
    chooseListId(state: any, val: any) {
      state.chooseListId = val
    },
    addTask(state: any, val: any) {
      state.chooseSublist.push(val)
      state.chooseListId.push(val.taskId)
    },
    delTask(state: any, val: any) {
      let id = val.taskId
      let index = state.chooseListId.findIndex((obj: any) => obj === id)
      state.chooseListId.splice(index, 1)

      let indexObj = state.chooseSublist.findIndex((obj: any) => obj.taskId === id)
      state.chooseSublist.splice(indexObj, 1)
    },
    taskType(state: any, val: any) {
      state.taskType = val
    },
    testType(state: any, val: any) {
      state.testType = val
    },
    countryId(state: any, val: any) {
      state.countryId = val
    },
    operatorId(state: any, val: any) {
      state.operatorId = val
    },
    keyWord(state: any, val: any) {
      state.keyWord = val
    },
    projectId(state: any, val: any) {
      state.projectId = val
    },
    startTime(state: any, val: any) {
      if (!val) {
        state.startTime = null
      } else {
        state.startTime = val
      }
    },
    endTime(state: any, val: any) {
      if (!val) {
        state.endTime = null
      } else {
        state.endTime = val
      }
    },
    onlyFail(state: any, val: any) {
      state.onlyFail = val
    }
  },
  actions: {
    async init({ commit, dispatch, state, rootState }: any) {
      dispatch('loadProject')
      dispatch('loadCountryOperator')
      dispatch('loadList')
    },
    async loadCountryOperator({ commit, dispatch, state, rootState }: any) {
      try {
        let params: any = {
          status: 1,
          page: 1,
          count: 99999
        }
        $func.jsonDelNull(params)
        let res = await $ax.get($api.operator.get.searchOperatorRelationByFilter, { params })
        let countryOperator: any = []
        if (res.data.results && res.data.results.length > 0) {
          res.data.results.forEach((item: any) => {
            // 暂不配置运营商
            let operator: any = []
            if (item.operator && item.operator.length > 0) {
              item.operator.forEach((itm: any) => {
                operator.push({
                  value: itm.operatorId,
                  label: itm.operatorName
                })
              })
            }
            let countryOperatorItem = {
              value: item.countryId,
              label: item.countryName,
              children: operator
            }
            countryOperator.push(countryOperatorItem)
          })
        }
        state.countryOperators = countryOperator
      } catch (e: any) {
        console.log('e', e)
      }
    },
    async loadProject({ commit, dispatch, state, rootState }: any) {
      try {
        let params: any = {
          page: 1,
          count: 99999
        }
        $func.jsonDelNull(params)
        let res = await $ax.get($api.project.get.getallprojects, { params })
        state.projects = res.data.results
      } catch (e: any) {
        console.log('e', e)
      }
    },
    async loadList({ commit, dispatch, state, rootState }: any) {
      try {
        let params: any = {
          page: state.page,
          count: state.count,
          countryId: state.countryId,
          endTime: state.endTime,
          keyWord: '',
          operatorId: state.operatorId,
          projectId: state.projectId,
          startTime: state.startTime,
          taskType: state.taskType,
          testType: state.testType
        }

        $func.jsonDelNull(params)
        if (!params.hasOwnProperty('startTime')) {
          params.startTime = null
        }
        if (!params.hasOwnProperty('endTime')) {
          params.endTime = null
        }
        let res = await $ax.post($api.task.post.getAllGroupTask, params)
        if (res.data.results) {
          res.data.results.forEach((item: any) => {
            item.sublist = []
          })
        }
        state.list = res.data.results
        state.totalCount = res.data.total_count
      } catch (e: any) {
        console.log('e', e)
      }
    },
    async loadSubList({ commit, dispatch, state, rootState }: any, groupId: any) {
      try {
        let params: any = {
          page: 1,
          count: 9999,
          groupId: groupId
        }
        $func.jsonDelNull(params)
        let res = await $ax.get($api.task.get.getTaskInfo, { params })
        return res.data.results
      } catch (e: any) {
        console.log('e', e)
      }
    },
    async loadTaskDetail({ commit, dispatch, state, rootState }: any) {
      try {
        let params: any = {
          taskId: state.taskId
        }

        $func.jsonDelNull(params)
        let res = await $ax.get($api.task.get.getTaskDetailInfo, { params })
        state.detailMainInfo = res.data
      } catch (e: any) {
        console.log('e', e)
      }
    },
    async loadTaskCaseDetail({ commit, dispatch, state, rootState }: any) {
      try {
        let params: any = {
          taskId: state.taskId,
          onlyFail: state.onlyFail,
          page: 1,
          count: 9999
        }

        $func.jsonDelNull(params)
        let res = await $ax.get($api.task.get.getTaskCaseInfo, { params })
        state.detailCaseInfo = res.data.results
      } catch (e: any) {
        console.log('e', e)
      }
    }
  },
  getters: {}
}

export { vx }

总结一下

我们整理一下大体思路:
其实核心就是element组件table的选择+翻页+回显的具体实现
主要依托于两个函数:@selection-change=“selectionChange”,@select=“handleSelectionChange”
还有table回显时,对已选择数据的打勾。

async selectionChange(val: any) {
        if (val) {
          // 将获取到的id存入tableAllSelectedId数组(点击某行前面的勾选、选中某行的勾选、全选。三种状态都能触发此功能)
          let len = val.length
          for (let i = 0; i < len; i++) {
            if (val[i] === undefined) {
              continue
            } else {
              if (state.chooseListId.indexOf(val[i].taskId) === -1) {
                store.commit('imapList/addTask', val[i])
              }
            }
          }
          console.log('state.formOpts.tableAllSelectedId', state.chooseListId)
        }
      },
      async handleSelectionChange(rows: any, row: any) {
        // 判断是点击了表格勾选还是取消勾选
        // true就是选中,0或者false是取消选中
        const selected = rows.length && rows.indexOf(row) !== -1
        if (!selected) {
          // 如果点击取消勾选
          store.commit('imapList/delTask', row)
        }
        console.log('selectes -> tableAllSelectedId', state.chooseListId)
      },

情况二; element组件库 + 翻页 + 搜索 + 选择 + 仅查看已选择(最基础的功能,情况一的基础版本)

大概样式如下所示:

复制elementui表单_sed

核心思路
我习惯于用两个table来记录完整数据(t1)和已选择的数据(t2)
t1表中对于选择 + 翻页 + 搜索的实现主要基于@selection-change=“selectionChange”,@select=“handleSelectionChange”
回显时需要将已选择的数据勾选上,我这里使用了watch去监听。
具体代码如下(vue2 elementUI实现):

<template>
  <div class="container" :style="{ maxHeight: formOpts.show ? '1000px' : '30px' }">
    <div class="hline" @click="onExpand">
      <strong class="title-font">日志分析策略:</strong>
      <div class="show"><i class="el-icon-arrow-right" /></div>
    </div>
    <div class="line"></div>
    <div>
      <div class="search-box">
        <div>
          <el-input
            size="small"
            class="input-search"
            v-model="formModel.strategyName"
            placeholder="策略名称"
          />
          <el-input
            size="small"
            class="input-search"
            v-model="formModel.submiiter"
            placeholder="Owner"
          />
          <el-button size="small" type="primary" @click="getList">搜索</el-button>
        </div>
        <el-checkbox v-model="formOpts.selected">仅查看已选中</el-checkbox>
      </div>
      <el-table
        border
        key="t1"
        v-if="!formOpts.selected"
        ref="multipleTable"
        :data="formOpts.strategies"
        style="width: 73%"
        @select="handleSelectionChange"
        @selection-change="selectionChange"
      >
        <el-table-column type="selection" width="55">
        </el-table-column>
        <el-table-column label="ID" width="40">
          <template slot-scope="scope">{{ scope.row.analyStrategyId }}</template>
        </el-table-column>
        <el-table-column prop="analyStrategyName" label="名称" width="150"> </el-table-column>
        <el-table-column prop="remark" label="描述"> </el-table-column>
        <el-table-column prop="submitter" label="Owner" width="140"> </el-table-column>
      </el-table>
      <el-pagination
        v-if="!formOpts.selected"
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
        :current-page="formModel.page"
        :page-sizes="[5, 10, 20]"
        :page-size="formModel.count"
        layout="total, sizes, prev, pager, next, jumper"
        :total="formOpts.total"
      >
      </el-pagination>
      <el-table
        border
        key="t2"
        height="300"
        v-if="formOpts.selected"
        ref="multipleTable_selected"
        :data="formOpts.selectedList"
        style="width: 73%"
      >
        <!-- <el-table-column type="selection" width="55">
          <template slot-scope="scope">
            <el-checkbox
              v-model="formOpts.selected && formOpts.trueFlag"
              :disabled="formOpts.selected"
            ></el-checkbox>
          </template>
        </el-table-column> -->
        <el-table-column label="ID" width="40">
          <template slot-scope="scope">{{ scope.row.analyStrategyId }}</template>
        </el-table-column>
        <el-table-column prop="analyStrategyName" label="名称" width="150"> </el-table-column>
        <el-table-column prop="remark" label="描述"> </el-table-column>
        <el-table-column prop="submitter" label="Owner" width="140"> </el-table-column>
      </el-table>
    </div>
  </div>
</template>

<script>
export default {
  name: 'ulogsys',
  props: {
    value: Object,
    isCopy: {
      type: Boolean,
      default: false,
    },
  },
  data() {
    return {
      formOpts: {
        show: false,
        strategies: [],
        total: 0,
        selected: false,
        selectedId: [],
        selectedList: [],
        trueFlag: true,
      },
      formModel: {
        page: 1,
        count: 5,
        strategyName: '',
        submitter: '',
        ulogsys: {},
      },
      rules: {},
    }
  },
  watch: {
    'formOpts.strategies': {
      async handler(val) {
        val.forEach((item) => {
          this.$nextTick(() => {
            if (this.$refs.multipleTable) {
              if (this.formOpts.selectedId.indexOf(item.analyStrategyId) > -1) {
                this.$refs.multipleTable.toggleRowSelection(item, true)
              } else {
                this.$refs.multipleTable.toggleRowSelection(item, false)
              }
            }
          })
        })
      },
      deep: false,
      immediate: true,
    },
    'formOpts.selected': {
      async handler(val) {
        this.formOpts.strategies.forEach((item) => {
          this.$nextTick(() => {
            if (this.$refs.multipleTable) {
              if (this.formOpts.selectedId.indexOf(item.analyStrategyId) > -1) {
                this.$refs.multipleTable.toggleRowSelection(item, true)
              } else {
                this.$refs.multipleTable.toggleRowSelection(item, false)
              }
            }
          })
        })
      },
      deep: false,
      immediate: true,
    },
  },
  methods: {
    handleSizeChange(val) {
      this.formModel.count = val
      this.getList()
    },
    handleCurrentChange(val) {
      this.formModel.page = val
      this.getList()
    },
    async selectionChange(val) {
      if (val) {
        // 将获取到的id存入tableAllSelectedId数组(点击某行前面的勾选、选中某行的勾选、全选。三种状态都能触发此功能)
        val.forEach((item) => {
          if (this.formOpts.selectedId.indexOf(item.analyStrategyId) === -1) {
            this.formOpts.selectedId.push(item.analyStrategyId)
            this.formOpts.selectedList.push(item)
          }
        })
      }
    },
    async handleSelectionChange(rows, row) {
      // 判断是点击了表格勾选还是取消勾选
      // true就是选中,0或者false是取消选中
      const selected = rows.length && rows.indexOf(row) !== -1
      if (!selected) {
        // 如果点击取消勾选
        let id = row.analyStrategyId
        let index = this.formOpts.selectedId.findIndex((obj) => obj === id)
        this.formOpts.selectedId.splice(index, 1)

        let indexObj = this.formOpts.selectedList.findIndex((obj) => obj.analyStrategyId === id)
        this.formOpts.selectedList.splice(indexObj, 1)
      }
    },

    changeUlogsys() {},
    onExpand() {
      this.formOpts.show = !this.formOpts.show
    },
    async getList() {
      let params = {
        submitter: this.formModel.submitter,
        strategyName: this.formModel.strategyName,
        page: this.formModel.page,
        count: this.formModel.count,
      }
      this.$func.jsonDelNull(params)
      let res = await this.$api.newTest.getULogStrategy(params)

      this.formOpts.strategies = res.data.data.results
      this.formOpts.total = res.data.data.totalCount
    },
    load() {
      this.getList()
    },
  },
  mounted() {
    this.load()
  },
}
</script>

<style scoped lang="less">
.hline {
  width: 73%;
  display: flex;
  justify-content: space-between;
  cursor: pointer;
  .show {
    color: #c0c4cc;
  }
}
.container {
  max-height: 30px;
  transition: max-height 1s;
  overflow: hidden;
}

.title-font {
  color: #35bd9e;
  font-size: 18px;
}
.line {
  width: 73%;
  height: 2px;
  border-top: solid #dcdfe6 1px;
  margin-bottom: 15px;
}
.search-box {
  width: 73%;
  display: flex;
  justify-content: space-between;
  margin-bottom: 10px;
  .input-search {
    width: 200px;
    margin-right: 10px;
  }
}
.el-pagination {
  margin: 10px 0;
}
</style>

情况三: 翻页 + 搜索 + 单选 + 进查看已选择

复制elementui表单_数据_02

这种情况比多选的要容易一些
其中核心思路是把第一列里添加el-radio,再监听el-radio选择的id,将整行row的数据添加到已选择的数据表里

<template>
  <div class="container" :style="{ maxHeight: formOpts.show ? '1000px' : '30px' }">
    <div class="hline" @click="onExpand">
      <strong class="title-font">日志分析策略:</strong>
      <div class="show"><i class="el-icon-arrow-right" /></div>
    </div>
    <div class="line"></div>
    <div>
      <div class="search-box">
        <div>
          <el-input
            size="small"
            class="input-search"
            v-model="formModel.strategyName"
            placeholder="策略名称"
          />
          <el-input
            size="small"
            class="input-search"
            v-model="formModel.submiiter"
            placeholder="Owner"
          />
          <el-button size="small" type="primary" @click="getList">搜索</el-button>
        </div>
        <el-checkbox v-model="formOpts.selected">仅查看已选中</el-checkbox>
      </div>
      <el-table
        border
        key="t1"
        v-if="!formOpts.selected"
        ref="multipleTable"
        :data="formOpts.strategies"
        style="width: 73%"
      >
        <el-table-column width="75">
          <template slot-scope="scope">
            <el-radio v-model="formModel.checkedId" :label="scope.row.analyStrategyId"></el-radio>
          </template>
        </el-table-column>
        <el-table-column prop="analyStrategyName" label="名称" width="150"> </el-table-column>
        <el-table-column prop="remark" label="描述"> </el-table-column>
        <el-table-column prop="submitter" label="Owner" width="140"> </el-table-column>
      </el-table>
      <el-pagination
        v-if="!formOpts.selected"
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
        :current-page="formModel.page"
        :page-sizes="[5, 10, 20]"
        :page-size="formModel.count"
        layout="total, sizes, prev, pager, next, jumper"
        :total="formOpts.total"
      >
      </el-pagination>
      <el-table
        border
        key="t2"
        height="300"
        v-if="formOpts.selected"
        ref="multipleTable_selected"
        :data="formOpts.selectedList"
        style="width: 73%"
      >
        <el-table-column label="ID" width="40">
          <template slot-scope="scope">{{ scope.row.analyStrategyId }}</template>
        </el-table-column>
        <el-table-column prop="analyStrategyName" label="名称" width="150"> </el-table-column>
        <el-table-column prop="remark" label="描述"> </el-table-column>
        <el-table-column prop="submitter" label="Owner" width="140"> </el-table-column>
      </el-table>
    </div>
  </div>
</template>

<script>
export default {
  name: 'ulogsys',
  props: {
    value: Object,
    isCopy: {
      type: Boolean,
      default: false,
    },
  },
  data() {
    return {
      formOpts: {
        show: false,
        strategies: [],
        total: 0,
        selected: false,
        selectedId: [],
        selectedList: [],
        trueFlag: true,
      },
      formModel: {
        page: 1,
        count: 5,
        strategyName: '',
        submitter: '',
        ulogsys: {},
        checkedId: null,
      },
      rules: {},
    }
  },
  watch: {
    'formModel.checkedId': {
      async handler(val) {
        if (val > 0) {
          this.formOpts.selectedList = []
          let temp = this.formOpts.strategies.filter((item) => {
            return item.analyStrategyId == val
          })
          this.formOpts.selectedList = temp
          console.log('this.formOpts.selectedList', this.formOpts.selectedList)
        } else {
          this.formOpts.selectedList = []
        }
      },
      deep: false,
      immediate: true,
    },
  },
  methods: {
    handleSizeChange(val) {
      this.formModel.count = val
      this.getList()
    },
    handleCurrentChange(val) {
      this.formModel.page = val
      this.getList()
    },
    changeUlogsys() {},
    onExpand() {
      this.formOpts.show = !this.formOpts.show
    },
    async getList() {
      if (this.formModel.submitter || this.formModel.strategyName) {
        this.formModel.page = 1
        this.formModel.count = 5
      }
      let params = {
        submitter: this.formModel.submitter,
        strategyName: this.formModel.strategyName,
        page: this.formModel.page,
        count: this.formModel.count,
      }
      this.$func.jsonDelNull(params)
      let res = await this.$api.newTest.getULogStrategy(params)

      this.formOpts.strategies = res.data.data.results
        ? res.data.data.results.map((item) => {
            let temp = {
              checked: false,
            }
            return { ...item, ...temp }
          })
        : []
      this.formOpts.total = res.data.data.totalCount
    },
    load() {
      this.getList()
    },
  },
  mounted() {
    this.load()
  },
}
</script>

<style scoped lang="less">
.hline {
  width: 73%;
  display: flex;
  justify-content: space-between;
  cursor: pointer;
  .show {
    color: #c0c4cc;
  }
}
.container {
  max-height: 30px;
  transition: max-height 1s;
  overflow: hidden;
}

.title-font {
  color: #35bd9e;
  font-size: 18px;
}
.line {
  width: 73%;
  height: 2px;
  border-top: solid #dcdfe6 1px;
  margin-bottom: 15px;
}
.search-box {
  width: 73%;
  display: flex;
  justify-content: space-between;
  margin-bottom: 10px;
  .input-search {
    width: 200px;
    margin-right: 10px;
  }
}
.el-pagination {
  margin: 10px 0;
}
</style>

其他复杂表格后续继续增加。。。(敬请期待)