• 基于element-plus定义表格行内编辑配置化



    前言

    基于element-plus定义表单配置化 基础上,封装个Element-plus的table表格
    由于表格不同于form组件,需自定义校验器,以下组件配置了单个校验,及提交统一校验方法,且自定义必填校验*显示和校验错误部分边框标红等,实际可根据业务及不同场景优化改造相关定义

    后期抽空新增表格行及删除行等功能,

    在这里插入图片描述


    一、新增table组件

    • table-configuration/index.vue
    <template>
      <el-table
        border
        ref="tableRef"
        :show-header="showHeader"
        :data="tableData"
        style="width: 100%"
        tooltip-effect
        :max-height="tablemaxHeight"
      >
        <el-table-column type="selection" :fixed="selectionFixed" width="55" v-if="hasSelection"/>
        <template v-for="(item, index) in tableProperty" :key="item">
          <el-table-column
            :align="align"
            :sortable="item.sortable"
            :min-width="item.width"
            :show-overflow-tooltip="showOverflowTooltip"
            :label="item.label"
          >
            <template #header>
              <div :class="[getTableHeader(item.property.rules)]" v-html="item.label">div>
            template>
            <template #default="scope">
              <component 
                :class="[scope.$index >=0 && getIsErrorClass(scope.$index, index)]"
                v-model:content="scope.row[item.field]"
                v-model="scope.row[item.field]"
                :property="{...item.property, name: item.field}"
                :is="item.type"
                @fieldBlur="(val) => blur(val, item, scope.$index, index)"
                @fieldChange="(val) => change(val, item, scope.$index, index)" />
            template>
          el-table-column>
        template>
      el-table>
    template>
    <script lang="ts" src="./index.ts"/>
    <style lang="less">
    .is-error .el-select-v2__wrapper,.is-error .el-select-v2__wrapper:focus,.is-error .el-textarea__inner,.is-error .el-textarea__inner:focus {
      box-shadow: 0 0 0 1px var(--el-color-danger) inset
    }
    
    .is-error .el-input__wrapper {
      box-shadow: 0 0 0 1px var(--el-color-danger) inset
    }
    .table {
      &_header:before {
        content: "*";
        color: var(--el-color-danger);
        margin-right: 4px;
      }
    }
    style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • table-configuration/index.ts
    import { tableHooks } from "@/composables/table-hooks";
    import { computed, defineComponent, reactive, ref } from "vue";
    import Input from "@/components/form-configuration/input.vue";
    import Select from "@/components/form-configuration/select.vue";
    import Vhtml from "@/components/form-configuration/v-html.vue";
    import Upload from "@/components/form-configuration/upload.vue";
    import Switch from "@/components/form-configuration/switch.vue";
    import Radio from "@/components/form-configuration/radio.vue";
    import Checkbox from "@/components/form-configuration/checkbox.vue";
    import Date from "@/components/form-configuration/date.vue";
    import Cascader from "@/components/form-configuration/cascader.vue";
    import { isArray } from "lodash-es";
    import { ElMessage } from "element-plus";
    import type { rulesType } from "@/interface";
    
    const ruleType = {
      required: false,
      message: '',
      trigger: '',
      validator: (val: any) =>{return val}
    }
    const fieldProperty = {
      label: 'demo',
      type: 'Input',
      field: 'demo',
      width: '120px',
      err: '',
      property: {
        maxlength: 200,
        rules: [ruleType]
      }
    }
    export default defineComponent({
      components: {
        Input,
        Select,
        Vhtml,
        Upload,
        Switch,
        Radio,
        Checkbox,
        Date,
        Cascader,
      },
      props: {
        align: {
          type: String,
          default: 'left', // left / center / right
        },
        showHeader: {
          type: Boolean,
          default: true,
        },
        selectionFixed: {
          type: Boolean,
          default: false,
        },
        showOverflowTooltip: {
          type: Boolean,
          default: true,
        },
        hasSelection: {
          type: Boolean,
          default: false,
        },
        property: {
          type: Object,
          default() {
            return [ fieldProperty ];
          },
        },
        data: {
          type: Object,
          default() {
            return {};
          },
        },
      },
      setup(props, { emit }) {
        const { tablemaxHeight } = tableHooks();
        const tableRef = ref()
        const tableData = computed({
          get() {
            return props.data;
          },
          set(val) {
            emit("update:data", val);
          },
        });
        const noType = 'noType'
        const tableProperty = computed(() => props.property);
        const blur = (val: any, item: typeof fieldProperty, rowIndex: number, colIndex: number) => {
          let arr: Array<boolean> = []
          if (item.property.rules && isArray(item.property.rules)) {
            arr = validateForField(item, val, 'blur', rowIndex, colIndex)
          }
          if (!arr.length) {
            emit('blur', {
              val, field: item.field, rowIndex, colIndex
            }) // 触发
            clearIsError(rowIndex, colIndex)
          }
        }
        const change = (val: any, item: typeof fieldProperty, rowIndex: number, colIndex: number) => {
          let arr: Array<boolean> = []
          if (item.property.rules && isArray(item.property.rules)) {
            arr = validateForField(item, val, 'change', rowIndex, colIndex)
          }
          if (!arr.length) {
            emit('change', {
              val, field: item.field, rowIndex, colIndex
            }) // 触发
            clearIsError(rowIndex, colIndex)
          }
        }
        const isError = ref<{rowIndex: number, colIndex: number}[]>([])
        const validateForField = (item: typeof fieldProperty, val: any, type: string, rowIndex: number, colIndex: number) => {
          let arr: Array<boolean> = []
          item.property.rules.forEach((valid) => {
            const types = [valid.trigger, noType]
            if (valid.required && !val) {
              ElMessage.error(valid.message)
              arr.push(false)
              isError.value.push({
                rowIndex, colIndex,
              })
              return
            }
            if (!valid.required && !val || !types.includes(type)) return
            if (!valid.validator) return
            const bool = valid.validator(val)
            if (!bool) {
              ElMessage.error(valid.message)
              arr.push(bool)
              isError.value.push({
                rowIndex, colIndex,
              })
            }
          })
          return arr
        }
        const clearIsError = (rowIndex: number, colIndex: number) => {
          if (rowIndex === -1) {
            isError.value = []
          } else {
            isError.value = isError.value.filter((item) => {
              return !((item.rowIndex === rowIndex) && (item.colIndex === colIndex))
            })
          }
        }
        const validate = () => {
          let arr: Array<boolean> = []
          clearIsError(-1, -1)
          tableData.value.forEach((data: object, rowIndex: number) => {
            tableProperty.value.forEach((tabPro: any, colIndex: number) => {
              if (!tabPro.property.rules) return
              const field = tabPro.field as keyof typeof data
              arr.push(...validateForField(tabPro, data[field], noType, rowIndex, colIndex))
            });
          });
          return !arr.length
        }
        const getIsErrorClass = computed(() => {
          return (rowIndex: number, colIndex: number) => {
            let bool = false
            isError.value.forEach((error) => {
              (error.rowIndex === rowIndex) && (error.colIndex === colIndex) && (bool = true)
            })
            return bool ? 'is-error' : ''
          }
        })
        const getTableHeader = (rules: rulesType[]) => {
          if (!rules) return ''
          return !!rules.filter((item) => item.required).length ? 'table_header' : ''
        }
        return {
          tableRef,
          tablemaxHeight,
          tableProperty,
          tableData,
          isError,
          getIsErrorClass,
          getTableHeader,
          change,
          blur,
          validate
        };
      },
    });
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190

    二、使用步骤

    <TableConfiguration
       ref="supplierListRef"
       v-model:data="tableEntity.table"
       :hasSelection="true"
       :selectionFixed="true"
       :property="tableProperty"
       align="center"
       />
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    import { defineComponent, reactive, ref } from 'vue'
    import TableConfiguration from '@/components/table-configuration/index.vue'
    export default defineComponent({
      components: {
        TableConfiguration
      },
      setup() {
        const tableRef = ref()
        const tableProperty = reactive([
          { label: 'Input01', type: 'Input', field: 'Input01',  property: {
            maxlength: 500,
            rules: [{ required: false, message: 'error', trigger: 'blur', validator: (value: string) => {
              return /^\+?[1-9][0-9]*$/.test(value)
            }}]
          }},
          { label: 'Input02', type: 'Input', field: 'Input02', width: '200px', property: {
            maxlength: 500,
            rules: [{ required: true, message: 'error', trigger: 'blur' }]
          }}
        ])
        const tableEntity = reactive({
          table: [{
            Input01: '',
            Input02: '',
          }]
        })
        const validate = () => {
          tableRef.value.validate()
        }
        return {
          tableRef,
          tableProperty,
          tableEntity,
          validate
        }
      },
    })
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

  • 相关阅读:
    [ web基础篇 ] Burp Suite 爆破 Basic 认证密码
    使用TS 封装 自定义hooks,实现不一样的 CRUD
    抖音选品的方法一抓一大把,抖音小店选品的底层逻辑,你知道吗?
    jdk-synchronized源码学习
    c语言-输入输出详解
    ‍ IT行业就业趋势:哪些方向更受青睐?
    一文搞懂漏洞严重程度分析
    Dockerfile 修改文件角色容量变大
    php加密解密的用法(对称加密,非对称加密)
    Hexagon_V65_Programmers_Reference_Manual(41)
  • 原文地址:https://blog.csdn.net/weiCong_Ling/article/details/134326184