基于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>
  • 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
    };
  },
});

二、使用步骤

<TableConfiguration
   ref="supplierListRef"
   v-model:data="tableEntity.table"
   :hasSelection="true"
   :selectionFixed="true"
   :property="tableProperty"
   align="center"
   />
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
    }
  },
})


  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我们来一步步实现基于Vue3、TypeScript和Element Plus的表格分页查询配置。 1. 首先我们需要安装Vue3、Vue Router、Element Plus和Axios等依赖: ```bash npm install vue@next vue-router@4.0.0-beta.8 element-plus@next axios ``` 2. 在`main.ts`中引入Vue、VueRouter、Element Plus和Axios: ```typescript import { createApp } from 'vue' import App from './App.vue' import router from './router' import ElementPlus from 'element-plus' import 'element-plus/lib/theme-chalk/index.css' import axios from 'axios' const app = createApp(App) app.use(router) app.use(ElementPlus) app.config.globalProperties.$axios = axios app.mount('#app') ``` 3. 在`src/components`目录下创建一个`Table.vue`组件,用来展示表格数据: ```html <template> <el-table :data="tableData"> <el-table-column v-for="column in columns" :key="column.prop" :prop="column.prop" :label="column.label"></el-table-column> </el-table> </template> <script lang="ts"> import { defineComponent } from 'vue' export default defineComponent({ props: { columns: { type: Array, default: () => [] }, data: { type: Array, default: () => [] } }, computed: { tableData() { return this.data.map(item => { const row = {} this.columns.forEach(column => { row[column.prop] = item[column.prop] }) return row }) } } }) </script> ``` 4. 在`src/components`目录下创建一个`Pagination.vue`组件,用来展示分页控件: ```html <template> <el-pagination :current-page="currentPage" :page-size="pageSize" :total="total" @current-change="handleCurrentChange" @size-change="handleSizeChange" ></el-pagination> </template> <script lang="ts"> import { defineComponent } from 'vue' export default defineComponent({ props: { currentPage: { type: Number, default: 1 }, pageSize: { type: Number, default: 10 }, total: { type: Number, default: 0 } }, methods: { handleCurrentChange(currentPage: number) { this.$emit('update:currentPage', currentPage) }, handleSizeChange(pageSize: number) { this.$emit('update:pageSize', pageSize) } } }) </script> ``` 5. 在`src/views`目录下创建一个`TableDemo.vue`视图组件,用来展示表格和分页控件: ```html <template> <div> <el-form :inline="true" :model="query" class="table-demo__form"> <el-form-item v-for="field in fields" :key="field.prop" :label="field.label"> <el-input v-model="query[field.prop]" placeholder="请输入"></el-input> </el-form-item> <el-button type="primary" icon="el-icon-search" @click="handleSearch">查询</el-button> </el-form> <Table :columns="columns" :data="data"></Table> <Pagination :currentPage.sync="currentPage" :pageSize.sync="pageSize" :total="total"></Pagination> </div> </template> <script lang="ts"> import { defineComponent, ref } from 'vue' import axios from 'axios' import Table from '@/components/Table.vue' import Pagination from '@/components/Pagination.vue' export default defineComponent({ components: { Table, Pagination }, setup() { const fields = [ { prop: 'name', label: '姓名' }, { prop: 'age', label: '年龄' }, { prop: 'gender', label: '性别' } ] const columns = [ { prop: 'name', label: '姓名' }, { prop: 'age', label: '年龄' }, { prop: 'gender', label: '性别' } ] const currentPage = ref(1) const pageSize = ref(10) const total = ref(0) const data = ref([]) const query = ref({ name: '', age: '', gender: '' }) const fetchData = async () => { const res = await axios.get('/api/users', { params: { ...query.value, page: currentPage.value, size: pageSize.value } }) data.value = res.data.items total.value = res.data.total } const handleSearch = () => { currentPage.value = 1 fetchData() } fetchData() return { fields, columns, currentPage, pageSize, total, data, query, handleSearch } } }) </script> <style> .table-demo__form { margin-bottom: 20px; } </style> ``` 6. 在`src/router/index.ts`中配置路由: ```typescript import { createRouter, createWebHashHistory } from 'vue-router' import TableDemo from '@/views/TableDemo.vue' const routes = [ { path: '/', name: 'TableDemo', component: TableDemo } ] const router = createRouter({ history: createWebHashHistory(), routes }) export default router ``` 7. 在`public`目录下创建一个`api`目录,用来模拟后端接口: ```json // api/users.json { "total": 100, "items": [ { "name": "张三", "age": 18, "gender": "男" }, { "name": "李四", "age": 20, "gender": "女" }, { "name": "王五", "age": 22, "gender": "男" }, { "name": "赵六", "age": 24, "gender": "女" }, { "name": "钱七", "age": 26, "gender": "男" } ] } ``` 8. 在`package.json`中添加一个`dev`命令,用来启动开发服务器: ```json { "scripts": { "dev": "vite" } } ``` 9. 启动开发服务器并访问`http://localhost:3000/`,就可以看到我们的表格和分页控件了。 以上就是基于Vue3、TypeScript和Element Plus的表格分页查询配置的实现步骤。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值