element-ui 表单模态表格封装

<template>
  <div ref="cTableMain" class="cTable" :style="{'height':_height}">
    <div :class="_tableHeight" class="is-null-data">
      <el-table
        :key="toggleIndex"
        ref="scTable"
        v-loading="_loading"
        v-bind="$attrs"
        :data="tableData"
        :row-key="rowKey"
        :tree-props="treeProps"
        :height="height==='auto'?null:'100%'"
        :size="config.size"
        :border="config.border"
        :stripe="config.stripe"
        :summary-method="remoteSummary?remoteSummaryMethod:summaryMethod"
        @sort-change="sortChange"
        @select-all="selectionAll"
        @selection-change="selectionChange"
        @filter-change="filterChange"
      >
        <slot />
        <div v-for="(item, index) in userColumn" :key="index">
          <el-table-column
            v-if="!item.hide && (item.action ? checkAction(item.action) : true)"
            :column-key="item.prop"
            :label="item.label"
            :prop="item.prop"
            :width="item.width"
            :sortable="item.sortable"
            :fixed="item.fixed"
            :align="item.align"
            :header-align="item.headerAlign"
            :filters="item.filters"
            :filter-method="remoteFilter||!item.filters?null:filterHandler"
            :show-overflow-tooltip="item.showOverflowTooltip"
          >
            <template #default="scope">
              <slot :name="item.prop" v-bind="scope">
                {{ scope.row[item.prop] ? scope.row[item.prop] : '-' }}
              </slot>
            </template>
          </el-table-column>
        </div>
        <el-table-column min-width="1" />
        <template #empty>
          <el-empty :description="emptyText" :image-size="100" />
        </template>
      </el-table>
    </div>
    <div v-if="!hidePagination" class="cTable-pagination"><slot name="foot" /></div>
  </div>
</template>

<script>
import config from './config'
export default {
  name: 'CTable',
  props: {
    loading: { type: Boolean, default: false },
    tableName: { type: String, default: '' },
    params: { type: Object, default: () => ({}) },
    data: { type: [Object, Array], default: () => [] },
    height: { type: [String, Number], default: '100%' },
    size: { type: String, default: 'default' },
    border: { type: Boolean, default: false },
    stripe: { type: Boolean, default: false },
    rowKey: { type: String, default: '' },
    summaryMethod: { type: Function, default: null },
    column: { type: [Object, Array], default: () => [] },
    treeProps: { type: Object, default: () => ({ hasChildren: 'hasChildren', children: 'children' }) },
    remoteSort: { type: Boolean, default: false },
    remoteFilter: { type: Boolean, default: false },
    remoteSummary: { type: Boolean, default: false },
    hideToolbar: { type: Boolean, default: false }, // 工具栏
    hidePagination: { type: Boolean, default: false }, // 分页
    hideToolbarLeft: { type: Boolean, default: false }, // 工具栏左
    hideToolbarRight: { type: Boolean, default: false }, // 工具栏右
    hideFullscreen: { type: Boolean, default: false }, // 全屏
    hideSetting: { type: Boolean, default: false } // 表格设置

  },
  data() {
    return {
      isActivat: true,
      emptyText: '暂无数据',
      toggleIndex: 0,
      tableData: [],
      tableHeight: '100%',
      tableParams: this.params,
      userColumn: [],
      prop: null,
      customColumnShow: false,
      summary: {},
      config: {
        size: this.size,
        border: this.border,
        stripe: this.stripe
      }
    }
  },
  computed: {
    _height() {
      return Number(this.height) ? Number(this.height) + 'px' : this.height
    },
    _tableHeight() {
      if (!this.hidePagination && !this.hideToolbar) {
        return 'tableHeight-120'
      } else if (!this.hidePagination || !this.hideToolbar) {
        return 'tableHeight-60'
      } else {
        return 'tableHeight'
      }
    },
    _loading: {
      get() {
        return this.loading
      }, set(value) {
        this.$emit('update:loading', value)
      }
    }
  },
  watch: {
    // 监听从props里拿到值了
    data(valArr) {
      this.tableData = this.data
      /* const _this = this
      _this.userColumn = _this.column.map(function(value) {
        const arr = valArr.map(x => x[value.prop]) // 获取每一列的所有数据
        arr.push(value.label) // 把每列的表头也加进去算
        value.width = _this.getMaxLength(arr) + 20 // 每列内容最大的宽度 + 表格的内间距(依据实际情况而定)
        return value
      })*/

      // this.$refs.scTable.$el.querySelector('.el-table__body-wrapper').scrollTop = 0
    }
  },
  mounted() {
    // 判断是否开启自定义列
    if (this.column) {
      this.getCustomColumn()
    } else {
      this.userColumn = this.column
    }
    this.tableData = this.data
  },
  activated() {
    if (!this.isActivat) {
      this.$refs.scTable.doLayout()
    }
  },
  deactivated() {
    this.isActivat = false
  },
  methods: {
    /**
     * 遍历列的所有内容,获取最宽一列的宽度
     * @param arr
     */
    getMaxLength(arr) {
      return arr.reduce((acc, item) => {
        if (item) {
          const calcLen = this.getTextWidth(item)
          console.log('calcLen', calcLen)
          console.log('acc', acc)
          if (acc < calcLen) {
            acc = calcLen
          }
        }
        return acc
      }, 0)
    },
    /**
     * 使用span标签包裹内容,然后计算span的宽度 width: px
     * @param valArr
     */
    getTextWidth(str) {
      let width = 0
      const html = document.createElement('span')
      html.innerText = str
      html.className = 'getTextWidth'
      document.querySelector('body').appendChild(html)
      width = document.querySelector('.getTextWidth').offsetWidth + 50
      document.querySelector('.getTextWidth').remove()
      return width
    },
    // 刷新数据
    refresh() {
      this.$refs.scTable.clearSelection()
    },
    // 更新数据 合并上一次params
    upData(params) {
      this.$refs.scTable.clearSelection()
      Object.assign(this.tableParams, params || {})
      // this.$refs.scTable.$el.querySelector('.el-table__body-wrapper').scrollTop = 0
    },
    // 重载数据 替换params
    reload(params) {
      this.tableParams = params || {}
      this.$refs.scTable.clearSelection()
      this.$refs.scTable.clearSort()
      this.$refs.scTable.clearFilter()
      // this.$refs.scTable.$el.querySelector('.el-table__body-wrapper').scrollTop = 0
    },
    // 自定义变化事件
    columnSettingChange(userColumn) {
      this.userColumn = userColumn
      this.toggleIndex += 1
      this.doLayout()
    },
    // 排序事件
    sortChange(obj) {
      if (!this.remoteSort) {
        return false
      }
      if (obj.column && obj.prop) {
        this.prop = obj.prop
        this.order = obj.order
      } else {
        this.prop = null
        this.order = null
      }
      // this.$refs.scTable.$el.querySelector('.el-table__body-wrapper').scrollTop = 0
    },
    selectionAll(val) {
      this.$emit('select-all', val)
    },
    selectionChange(obj) {
      this.$emit('selection-change', obj)
    },
    // 本地过滤
    filterHandler(value, row, column) {
      const property = column.property
      return row[property] === value
    },
    // 过滤事件
    filterChange(filters) {
      if (!this.remoteFilter) {
        return false
      }
      Object.keys(filters).forEach(key => {
        filters[key] = filters[key].join(',')
      })
      this.upData(filters)
    },
    // 远程合计行处理
    remoteSummaryMethod(param) {
      const { columns } = param
      const sums = []
      columns.forEach((column, index) => {
        if (index === 0) {
          sums[index] = '合计'
          return
        }
        const values = this.summary[column.property]
        if (values) {
          sums[index] = values
        } else {
          sums[index] = ''
        }
      })
      return sums
    },
    configSizeChange() {
      this.$refs.scTable.doLayout()
    },
    // 原生方法转发
    clearSelection() {
      this.$refs.scTable.clearSelection()
    },
    toggleRowSelection(row, selected) {
      this.$refs.scTable.toggleRowSelection(row, selected)
    },
    toggleAllSelection() {
      this.$refs.scTable.toggleAllSelection()
    },
    toggleRowExpansion(row, expanded) {
      this.$refs.scTable.toggleRowExpansion(row, expanded)
    },
    setCurrentRow(row) {
      this.$refs.scTable.setCurrentRow(row)
    },
    clearSort() {
      this.$refs.scTable.clearSort()
    },
    clearFilter(columnKey) {
      this.$refs.scTable.clearFilter(columnKey)
    },
    doLayout() {
      this.$nextTick(() => {
        setTimeout(() => {
          this.$refs.scTable.doLayout()
        }, 200)
      })
    },
    sort(prop, order) {
      this.$refs.scTable.sort(prop, order)
    }
  }
}
</script>

模态

<template>
  <el-dialog
    v-bind="$attrs"
    :top="top"
    :close-on-click-modal="closeOnClickModal"
    :show-close="showClose"
    append-to-body
    :close-on-press-escape="closeOnPressEscape"
    :width="width"
  >
    <div slot="title">
      <slot name="title">
        <div class="title" :style="`background:${titleBg}`">
          {{ title }}
        </div>
      </slot>
    </div>
    <div v-if="maxHeight" :style="style">
      <el-scrollbar :vertical="true" :max-height="maxHeight" style="height:100%">
        <div>
          <slot />
        </div>
      </el-scrollbar>
    </div>
    <div v-else :style="style">
      <slot />
    </div>
    <div slot="footer" :class="[footerClass[footerPosition], 'dialog-footer', dialogFooter]">
      <slot name="footer" />
    </div>
  </el-dialog>
</template>
<script>
export default {
  name: 'CustomDialog',
  props: {
    closeOnPressEscape: {
      type: Boolean,
      default: true
    },
    showClose: {
      type: Boolean,
      default: true
    },
    closeOnClickModal: {
      type: Boolean,
      default: false
    },
    maxHeight: {
      type: String,
      default: undefined
    },
    top: {
      type: String,
      default: '7vh'
    },
    title: {
      type: String,
      default: ''
    },
    footerPosition: {
      type: String,
      default: 'center'
    },
    dialogFooter: {
      type: String,
      default: 'center'
    },
    width: {
      type: String,
      default: '585px'
    },
    titleBg: {
      type: String,
      default: '#F1F6FF'
    }
  },
  data() {
    return {
      footerClass: {
        'center': 'footer-center',
        'left': 'footer-left',
        'right': 'footer-right'
      }
    }
  },
  computed: {
    style() {
      const style = {}
      if (!this.fullscreen) {
        if (this.maxHeight) {
          style.height = this.maxHeight
        }
      }
      return style
    }
  }
}
</script>
<style lang="scss">
.footer-center {
  display: flex;
  justify-content: center;
}
.footer-left{
  display: flex;
  justify-content: left;
}
.footer-right{
  display: flex;
  justify-content: right;
}
</style>

表单

 

<template>
  <div>
    <el-form ref="ruleForm" v-loading="formLoading" :model="form" :rules="rules" :label-width="labelWidth" :class="direction==='vertical'?'normal_form':'flex_form'">
      <el-form-item
        v-for="item in options"
        :key="item.propName"
        :prop="item.propName"
        :label="item.labelName"
        :label-width="item.labelWidth || labelWidth"
        class="form_item"
      >
        <!--提供一个插槽,提高扩展性-->
        <slot v-if="item.component === 'slot'" :name="item.slotName" />
        <el-input v-else-if="item.component === 'textarea'" v-model="form[item.propName]" :class="item.componentClass ? item.componentClass : 'g-input-m'" :autosize="{ minRows:3, maxRows: 3}" type="textarea" placeholder="请输入" />
        <template v-else>
          <component :is="item.component" v-model="form[item.propName]" :class="item.componentClass ? item.componentClass : 'g-input-m'" v-bind="item.props" :style="{width: item.width || '200px'}" v-on="item.listeners">
            <template v-if="item.component === 'el-select'">
              <el-option v-for="(subitem,index) in item.data" :key="index" :label="subitem.label" :value="subitem.value" />
            </template>
            <template v-else-if="item.component === 'el-select-city'">
              <el-radio
                v-for="(subitem,index) in item.optionsArr"
                :key="index"
                :label="subitem.value"
              >
                {{ subitem.label }}
              </el-radio>
            </template>
            <template v-else-if="item.component === 'el-checkbox-group'">
              <el-checkbox
                v-for="(subitem,index) in item.optionsArr"
                :key="index"
                :label="subitem.value"
              >
                {{ subitem.label }}
              </el-checkbox>
            </template>
            <template v-else-if="item.component === 'el-radio-group'">
              <el-radio
                v-for="(subitem,index) in item.optionsArr"
                :key="index"
                :label="subitem.value"
              >
                {{ subitem.label }}
              </el-radio>
            </template>
          </component>
        </template>
      </el-form-item>
    </el-form>
  </div>
</template>

<script>
export default {
  props: {
    form: { // 表单绑定数据
      type: Object,
      default: () => {}
    },
    options: { // 表单项配置数据
      type: Array,
      default: () => []
    },
    rules: { // 表单验证规则
      type: Object,
      default: () => {}
    },
    labelWidth: { // label宽度
      type: String,
      default: '100px'
    },
    direction: { // 表单方向
      type: String,
      default: 'vertical'
    },
    formLoading: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
    }
  },
  methods: {
    validate() {
      this.$refs.ruleForm.validate(valid => {
        this.$emit('validate-form', valid)
      })
    },
    resetFields() {
      this.$refs.ruleForm.resetFields()
    }
  }
}
</script>

 表格

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值