文章目录
前言
在 基于element-plus定义表单配置化 基础上,封装个Element-plus的table表格
由于表格不同于form组件,需自定义校验器,以下组件配置了单个校验,及提交统一校验方法,且自定义必填校验*
显示和校验错误部分边框标红等,实际可根据业务及不同场景优化改造相关定义
后期抽空新增表格行及删除行等功能,
一、新增table组件
- table-configuration/index.vue
html
<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
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
};
},
});
二、使用步骤
html
<TableConfiguration
ref="supplierListRef"
v-model:data="supplierListEntity.product"
:hasSelection="true"
:selectionFixed="true"
:property="tableProperty"
align="center"
/>
js
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
}
},
})