vue多功能表格封装

vue多功能表格封装___动态展示表格列、多条件查询

以下代码有部分是看开源eladmin-web项目改写的,不足之处请大家多多指教,共同学习。
eladmin-web项目地址:https://github.com/elunez/eladmin
1、 index.vue

<template>
  <div class="app-container">
    <!-- 多条件查询组件 -->
    <queryForm ref="queryForm" />
    <!-- 筛选组件 -->
    <buttonGroup :table-columns="tableColumns" @initCol="initCol" @toQuery="toQuery" @refresh="refresh"/>
    <!--表格组件-->
    <tables ref="table" :loading="loading" :data="data" :table-columns="tableColumns" :height="height" @clickRow="clickRow" @selectData="selectData" @edit="edit" />
    <!--分页组件-->
    <el-pagination
      :total="total"
      :current-page="page + 1"
      :page-sizes="[20, 40, 80, 100]"
      :page-size="size"
      style="margin-top: 8px;"
      layout="total, prev, pager, next, sizes"
      @size-change="sizeChange"
      @current-change="pageChange"/>
  </div>
</template>
<script>
import tables from '@/components/ViewComponents/table'// 引入组件
import buttonGroup from '@/components/ViewComponents/buttonGroup' // 筛选组件
import queryForm from './multiQuery' // 多条件查询组件
import initData from '@/mixins/requestInit'
export default {
  // 注册组件
  components: { tables, buttonGroup, queryForm },
  mixins: [initData],
  data() {
    return {
      height: 625,
      columns: this.obColumns(),
      tableColumns: [{
        hasSort: false,
        isShow: true,
        prop: 'number',
        label: '数量',
        align: 'center',
        mwidth: 100
      }, {
        hasSort: false,
        isShow: true,
        prop: 'number1',
        label: '数量',
        align: 'center',
        mwidth: 100
      }, {
        hasSort: false,
        isShow: true,
        prop: 'failureCause',
        label: '原因',
        align: 'center',
        mwidth: 100
      }, {
        hasSort: false,
        isShow: true,
        prop: 'number2',
        label: '数量',
        align: 'center',
        mwidth: 100
      }]
    }
  },
  created() {
    this.$nextTick(() => {
      this.height = document.documentElement.clientHeight - 240
    })
    this.init()
  },
  methods: {
    beforeInit() {
      this.url = 'api/repairRequest'
      const sort = 'id,desc'
      this.params = { page: this.page, size: this.size, sort: sort }
      return true
    },
    // 双击编辑
    edit() {
      console.log(arguments[0], '表格编辑数据')
    },
    // 表格多选
    selectData(arr) {
      console.log(arr, '表格组件返回值')
    },
    // 表格单选
    clickRow(row, event, column) {
      console.log('单击表格行数据')
    },
    // 条件查询
    toQuery() {
      this.$refs.queryForm.dialog = true
    },
    // 刷新
    refresh() {
      this.init()
    },
    initCol() {
      const that = this.$refs.table
      that.initCol(arguments[0])
    },
    obColumns(columns) {
      return {
        visible(col) {
          return !columns || !columns[col] ? true : columns[col].visible
        }
      }
    }
  }
}
</script>

2、表格组件(table.vue)
在这里插入图片描述

<template>
  <div style="padding-top:30px;">
    <el-table
      v-loading="loading"
      ref="table"
      :data="data"
      :height="height"
      :span-method="arraySpanMethod"
      :row-class-name="tableRowClassName"
      resizable
      border
      style="width: 100%"
      @filter-change="handleFilterChange"
      @row-click="clickRow"
      @row-dblclick="edit"
      @selection-change = "selectData">
      <!-- 明细 -->
      <el-table-column
        type="selection"
        width="40"/>
      <!--数据源-->
      <el-table-column
        v-for="(column, index) in tableColumns"
        v-if="columns.visible(`${column.prop}`)"
        :sortable="column.hasSort"
        :key="index"
        :prop="column.prop"
        :label="column.label"
        :align="column.align"
        :width="column.width"
        :min-width="column.mwidth"
        header-align="center">
        <template slot-scope="scope">
          <span v-if="column.prop === 'returnDate'" >{{ parseTime1(scope.row.returnDate) }}</span>
          <span v-else-if="column.prop === 'demandTime'" >{{ parseTime1(scope.row.demandTime) }}</span>
          <template v-else>
            <span>{{ scope.row[column.prop] }}</span>
          </template>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>
<script>
import { parseTime1 } from '@/utils/index'
import Vue from 'vue'
export default {
//过滤器
  filters: {
    getStatus(val) {
      switch (val) {
        case 0:
          return '不知道'
        case 1:
          return '了解'
        case 2:
          return '熟悉'
      }
    }
  },
  props: {
    loading: {
      type: Boolean,
      default: () => false
    },
    data: {// 表格数据源 默认为空数组
      type: Array,
      default: () => []
    },
    tableColumns: {// 表格的字段展示 默认为空数组
      type: Array,
      default: () => []
    },
    height: {// 表格高度
      type: Number,
      default: () => 625
    },
    formatter: {// 单据信息行数据显示
      type: Function,
      default: () => {
        return
      }
    }
  },
  data() {
    return {
      columns: this.obColumns(),
      listLoading: false,
      visible: false,
      delLoading: false,
      rowIds: [],
      deleteIds: [],
      detailIds: [],
      isAdd: true,
      multipleSelection: []
    }
  },
  created() {
    this.$nextTick(() => {
      this.initColData()
    })
  },
  methods: {
    parseTime1,
    initColData() {
      const columns = {}
      this.$refs.table.columns.forEach(e => {
        if (!e.property || e.type !== 'default') {
          return
        }
        columns[e.property] = {
          label: e.label,
          visible: true
        }
      })
      this.columns = this.obColumns(columns)
      this.updateProp('tableColumns', columns)
    },
    initCol(columns) {
      this.columns = this.obColumns(columns)
      this.updateProp('tableColumns', columns)
    },
    handleCurrentChange(row) {
      this.currentRow = JSON.parse(JSON.stringify(row))
    },
    // 选择改变
    selectionChangeHandler(val) {
      this.selections = val
    },
    obColumns(columns) {
      return {
        visible(col) {
          return !columns || !columns[col] ? true : columns[col].visible
        }
      }
    },
    // Vue.set( target, key, value )
    // 在对象上设置一个属性。如果属性还不存在,则添加新属性并触发更改通知
    // target:要更改的数据源(可以是对象或者数组)
    // key:要更改的具体数据
    // value :重新赋的值
    updateProp(name, value) {
      console.log(name === 'tableColumns', 'name名字', value)
      // const table = this.$refs.table
      Vue.set(this.tableColumns, name, value)
    },
    // 表头点击
    handleFilterChange() {
      console.log(arguments, '表头点击')
    },
    // 双击编辑
    edit() {
      this.$emit('edit', arguments[0])
    },
    // 单击行时触发方法
    clickRow(row, event, column) {
      // this.$emit('clickRow', row, event, column)
      this.$refs.table.clearSelection()
      const index = this.rowIds.indexOf(row.id)
      if (index === -1) {
        this.$refs.table.toggleRowSelection(row, true)
      } else {
        this.rowIds = []
        this.$refs.table.toggleRowSelection(row, false)
      }
    },
    // 多选改变时触发方法
    selectData(arr) {
      this.multipleSelection = arr
      this.$emit('selectData', arr)
    },
    // 合并表格行
    arraySpanMethod({ row, column, rowIndex, columnIndex }) {
      const length = this.tableColumns.length + 2
      if (columnIndex === 0 && this.dataSource[rowIndex].isShow) {
        return [1, length]
      }
      for (let i = 1; i < length; i++) {
        if (columnIndex === i && this.dataSource[rowIndex].isShow) {
          return [1, 0]
        }
      }
    },
    // 斑马纹表格样式
    tableRowClassName({ row, rowIndex }) {
      let color = ''
      if (rowIndex % 2 === 0) {
        color = 'warning-row'
      } else {
        color = 'success-row'
      }
      if (row.isShow) {
        color = 'table-row'
      }
      for (let i = 0; i < this.multipleSelection.length; i++) {
        if (row === this.multipleSelection[i]) {
          color = 'select-row'
        }
      }
      return color
    }
  }
}
</script>
<style lang="scss">
@import "@/styles/table.scss";
</style>

3、筛选列、刷新、查询按钮组件(multiQuery.vue)
在这里插入图片描述

<template>
  <div class="buttonGroup-box">
    <el-button-group class="crud-opts-right">
      <el-button size="mini" icon="el-icon-search" @click="toQuery"/>
      <el-button size="mini" icon="el-icon-refresh" @click="refresh"/>
      <el-popover placement="bottom-end" width="150" trigger="click">
        <el-button slot="reference" size="mini" icon="el-icon-s-grid">
          <i class="fa fa-caret-down" aria-hidden="true"/>
        </el-button>
        <el-checkbox v-model="allColumnsSelected" :indeterminate="allColumnsSelectedIndeterminate" @change="handleCheckAllChange">全选</el-checkbox>
        <el-checkbox v-for="(item,index) in tableColumns" :key="index" v-model="item.visible" @change="handleCheckedTableColumnsChange(item)"> {{ item.label }}</el-checkbox>
      </el-popover>
    </el-button-group>
  </div>
</template>
<script>
export default {
  props: {
    tableColumns: {// 表格数据源 默认为空数组
      type: Array,
      default: () => []
    }
  },
  data() {
    return {
      allColumnsSelected: true,
      allColumnsSelectedIndeterminate: false
    }
  },
  methods: {
    handleCheckAllChange(val) {
      if (val === false) {
        this.allColumnsSelected = true
        return
      }
      for (const key in this.tableColumns) {
        this.tableColumns[key].visible = val
      }
      this.allColumnsSelected = val
      this.allColumnsSelectedIndeterminate = false
    },
    handleCheckedTableColumnsChange(item) {
      let totalCount = 0
      let selectedCount = 0
      for (const key in this.tableColumns) {
        ++totalCount
        selectedCount += this.tableColumns[key].visible ? 1 : 0
      }
      if (selectedCount === 0) {
        this.$notify({
          title: '请至少选择一列',
          type: 'warning',
          duration: 2500
        })
        this.$nextTick(function() {
          item.visible = true
        })
        return
      }
      this.allColumnsSelected = selectedCount === totalCount
      this.allColumnsSelectedIndeterminate = selectedCount !== totalCount && selectedCount !== 0
      const columns = {}
      this.tableColumns.forEach(e => {
        columns[e.prop] = {
          label: e.label,
          visible: e.visible === true
        }
      })
      this.$emit('initCol', columns)
    },
    toQuery() {
      this.$emit('toQuery')
    },
    refresh() {
      this.$emit('refresh')
    }
  }
}
</script>
<style scoped>
.el-button--mini {
    padding: 7px 15px;
    font-size: 14px;
    border-radius: 3px;
}
.buttonGroup-box{
  float: right;
}
</style>

4、多条件查询组件(queryForm.vue)

<template>
  <!-- <div class="app-container" style="height:800px"> -->
  <el-dialog :append-to-body="true" :close-on-click-modal="false" :before-close="cancel" :visible.sync="dialog" :title="form.table" center width="800px" @open="getCondition">
    <div sclass="button-row" style="margin-top:10px">
      <template>
        <el-button size="mini" type="primary" icon="el-icon-plus" @click="insertEvent">添加查询条件</el-button>
        <el-button size="mini" type="success" icon="el-icon-search" @click="doSubmit" >查询</el-button>
        <el-button size="mini" type="primary" @click="reset" >重置</el-button>
        <el-button type="text" @click="cancel">关闭</el-button>
      </template>
    </div>
    <!-- 表格 -->
    <vxe-table
      ref="queryTable"
      :data="form.conditionVoList"
      :edit-config="{trigger: 'click', mode: 'cell'}"
      show-overflow
      resizable
      max-height="400"
      @edit-actived="editActivedEvent"
      @cell-click="increase">
      <vxe-table-column name="icon" fixed="left" width="60" title="删除" >
        <template slot-scope="scope" >
          <svg-icon icon-class="delete" @click.native="deleteRow(scope.row)" />
        </template>
      </vxe-table-column>
      <vxe-table-column :edit-render="{name: 'input', attrs: {disabled: false}}" width="140" field="associate" title="条件间联系" >
        <template v-if="form.conditionVoList.indexOf(scope.row)!==0 " slot-scope="scope" >
          <el-select v-model="scope.row.associate" placeholder="请选择" @change="autoAdd(scope.row)">
            <el-option
              v-for="item in associates"
              :disabled="form.conditionVoList.indexOf(scope.row)===0"
              :key="item.value"
              :label="item.label"
              :value="item.value"/>
          </el-select>
        </template>
      </vxe-table-column>
      <vxe-table-column :edit-render="{name: 'input', attrs: {disabled: false}}" min-width="140" field="column" title="查询条件"/>
      <vxe-table-column :edit-render="{name: 'input', attrs: {disabled: false}}" width="140" field="type" title="条件值关系" >
        <template slot-scope="scope">
          <el-select v-model="scope.row.type" placeholder="请选择">
            <el-option
              v-for="item in types"
              :key="item.value"
              :label="item.label"
              :value="item.value"/>
          </el-select>
        </template>
      </vxe-table-column>
      <vxe-table-column :edit-render="{name: 'input', attrs: {disabled: false}}" min-width="140" field="value" title="查询值" />
    </vxe-table>

    <template>
      <!-- 查询返回列表 -->
      <el-table
        v-loading="listLoading"
        v-if="query"
        ref="refTable"
        :data="columns"
        height="200px"
        resizable
        border
        style="width: 100%">
        <!-- 单据 -->
        <el-table-column prop="name" width="350px" label="姓名" />
        <el-table-column prop="age" width="350px" label="年龄" />
      </el-table>
    </template>
  </el-dialog>
  <!-- </div> -->
</template>

<script>
import pSelect from '@/components/ViewComponents/select'
import personSelect from '@/components/ViewComponents/select'
import request from '@/utils/request'
export default {
  components: { pSelect, personSelect },
  props: {
  },
  data() {
    return {
      query: false,
      loading: false,
      dialog: false,
      columns: [],
      form: {
        table: 'test',
        conditionVoList: []
      },
      types: [
        {
          label: '大于',
          value: 'gt'
        },
        {
          label: '等于',
          value: 'eq'
        },
        {
          label: '小于',
          value: 'lt'
        }, {
          label: '包含',
          value: 'like'
        }
      ],
      associates: [
        {
          label: '并且',
          value: 'and'
        },
        {
          label: '或者',
          value: 'or'
        }
      ]
    }
  },
  // created() {
  //   this.insertEvent()
  // },
  mounted() {
    this.insertEvent()
    this.insertEvent()
  },
  methods: {
    // 打开dialog的回调方法
    getCondition() {
      this.form.conditionVoList = [{
        column: 'name',
        type: 'like',
        value: '小',
        associate: ''
      }]
    },
    // 增加行
    insertEvent() {
      this.form.conditionVoList.push({
        column: '',
        type: '',
        value: '',
        associate: ''
      })
    },
    // vxe表格   单元格点击事件
    increase(data) {
      const index = this.form.conditionVoList.indexOf(data.row)
      const length = this.form.conditionVoList.length
      if ((index === length - 1) && data.row.value !== '') {
        this.insertEvent()
      }
    },
    // 条件联系选择后就自动增加行
    autoAdd() {
      const index = this.form.conditionVoList.indexOf(arguments[0])
      const length = this.form.conditionVoList.length
      if (index === length - 1) {
        this.insertEvent()
      }
    },
    // vxe表格  控制单元格禁用状态
    editActivedEvent({ row }) {
      const qTable = this.$refs.queryTable
      const aColumn = qTable.getColumnByField('associate') // 条件之间的联系
      if (this.form.conditionVoList.indexOf(row) === 0) {
        aColumn.editRender.attrs.disabled = true
      }
    },
    // 点击查询按钮
    doSubmit() {
      console.log(this.form)
      var arr = []
      for (let i = 0; i < this.form.conditionVoList.length; i++) {
        if (!this.form.conditionVoList[i].value) {
          this.form.conditionVoList.splice(i, 1)
        }
      }
      request({
        method: 'post',
        url: 'api/testQuery',
        data: this.form
      }).then(res => {
        this.query = true
        this.columns = res
        console.log(res, '数据')
      })
      // this.columns = arr
    },
    // 删除行
    deleteRow() {
      const index = this.form.conditionVoList.indexOf(arguments[0])
      this.form.conditionVoList.splice(index, 1)
    },
    // 点击重置按钮 Multiconditional
    reset() {
      this.query = false
      this.form.conditionVoList = [
        {
          column: '',
          type: '',
          value: '',
          associate: ''
        },
        {
          column: '',
          type: '',
          value: '',
          associate: ''
        }
      ]
    },
    // 关闭查询表单
    cancel() {
      this.resetForm()
    },
    resetForm() {
      this.dialog = false //
      this.reset()
    }
  }
}
</script>
<style lang="scss">
  @import "@/styles/form.scss";
</style>

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Vue 3 提供了更快的数据响应和更好的 TypeScript 支持,我们可以使用 Composition API 来封装一个行编辑表格组件。下面是一个简单的例子: ```html <template> <table> <thead> <tr> <th v-for="(header, index) in headers" :key="index">{{ header }}</th> <th></th> </tr> </thead> <tbody> <tr v-for="(row, rowIndex) in rows" :key="rowIndex"> <td v-for="(column, columnIndex) in row" :key="columnIndex"> <template v-if="!editing[rowIndex][columnIndex]"> {{ column }} </template> <template v-else> <input v-model="editData[rowIndex][columnIndex]" /> </template> </td> <td> <button @click="startEdit(rowIndex)">Edit</button> <button @click="saveEdit(rowIndex)">Save</button> <button @click="cancelEdit(rowIndex)">Cancel</button> </td> </tr> </tbody> </table> </template> <script> import { ref } from 'vue'; export default { name: 'EditableTable', props: { headers: Array, data: Array, }, setup(props) { const editing = ref( Array.from({ length: props.data.length }, () => Array(props.headers.length).fill(false)) ); const editData = ref( Array.from({ length: props.data.length }, () => Array(props.headers.length).fill(null)) ); const rows = ref(props.data); function startEdit(rowIndex) { editing.value[rowIndex] = Array(props.headers.length).fill(false); editing.value[rowIndex][0] = true; editData.value[rowIndex] = [...rows.value[rowIndex]]; } function saveEdit(rowIndex) { rows.value[rowIndex] = [...editData.value[rowIndex]]; editing.value[rowIndex] = Array(props.headers.length).fill(false); } function cancelEdit(rowIndex) { editing.value[rowIndex] = Array(props.headers.length).fill(false); } return { rows, editing, editData, startEdit, saveEdit, cancelEdit, }; }, }; </script> ``` 在这个例子中,我们使用了 `ref` 来定义了 `editing` 和 `editData` 两个响应式变量,分别表示当前每行是否在编辑状态和编辑后的数据。我们使用 `startEdit` 函数来开始编辑某一行,它会将 `editing` 和 `editData` 置为相应的状态。`saveEdit` 函数用于保存编辑后的数据,`cancelEdit` 用于取消编辑。 在模板中,我们使用 `v-if` 来根据当前是否处于编辑状态来显示不同的内容。同时,我们使用 `v-model` 来实现数据的双向绑定。`<button>` 元素用于触发相应的编辑操作。 注意:这只是一个简单的例子,实际应用中可能需要更多的功能和处理。同时,这里只是演示了如何使用 Composition API 来封装一个行编辑表格组件,具体的实现可能会因应用场景而有所不同。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值