feat: Table组件重构

This commit is contained in:
陈凯龙
2021-10-21 17:24:04 +08:00
parent f64842462e
commit 07adefb89b
21 changed files with 509 additions and 420 deletions

View File

@@ -37,7 +37,7 @@
</div>
</template>
<script setup lang="ts" name="Detail">
<script setup lang="ts" name="ComDetail">
import { PropType, ref, computed } from 'vue'
import { SchemaConfig } from './types'
@@ -65,11 +65,15 @@ const props = defineProps({
//
data: {
type: Object as PropType<IObj>,
default: () => {
return {}
},
required: true
},
//
schema: {
type: Array as PropType<SchemaConfig[]>,
default: () => [],
required: true
},
//

View File

@@ -41,7 +41,7 @@
</el-dialog>
</template>
<script setup lang="ts" name="Dialog">
<script setup lang="ts" name="ComDialog">
import { ref, computed, PropType, nextTick, unref, useAttrs, useSlots } from 'vue'
import SvgIcon from '@/components/SvgIcon/index.vue'

View File

@@ -187,7 +187,7 @@
</div>
</template>
<script setup lang="ts" name="Search">
<script setup lang="ts" name="ComSearch">
import { PropType, watch, ref, unref } from 'vue'
import { deepClone } from '@/utils'

View File

@@ -4,7 +4,7 @@ export default defineComponent({
name: 'Slot',
props: {
row: {
type: Object as PropType<object>,
type: Object as PropType<IObj>,
default: () => null
},
index: {
@@ -12,7 +12,7 @@ export default defineComponent({
default: null
},
column: {
type: Object as PropType<object>,
type: Object as PropType<IObj>,
default: () => null
},
slotName: {
@@ -20,7 +20,7 @@ export default defineComponent({
default: ''
}
},
render(props: any) {
render(props) {
const _this: any = inject('tableRoot')
return h(
'span',

View File

@@ -0,0 +1,159 @@
<template>
<div>
<el-table ref="tableRef" :border="true" v-bind="getBindValue" @header-dragend="headerDragend">
<!-- 多选 -->
<el-table-column
v-if="selection"
type="selection"
:reserve-selection="reserveSelection"
width="40"
/>
<template v-for="item in columns">
<!-- 自定义索引 -->
<template v-if="item.type === 'index'">
<el-table-column
:key="item[item.field]"
v-bind="{ ...getItemBindValue(item) }"
type="index"
:index="item.index"
/>
</template>
<!-- 树型数据 -->
<template v-else-if="item.children && item.children.length">
<table-column :key="item[item.field]" :child="item" />
</template>
<template v-else>
<el-table-column
:key="item[item.field]"
v-bind="{ ...getItemBindValue(item) }"
:prop="item.field"
>
<!-- 表头插槽 -->
<template v-if="item.slots && item.slots.header" #header="scope">
<table-slot
v-if="item.slots && item.slots.header"
:slot-name="item.slots.header"
:column="item"
:index="scope.$index"
/>
</template>
<!-- 表格内容插槽自定义 -->
<template v-if="item.slots && item.slots.default" #default="scope">
<table-slot
v-if="item.slots && item.slots.default"
:slot-name="item.slots.default"
:row="scope.row"
:column="item"
:index="scope.$index"
/>
</template>
</el-table-column>
</template>
</template>
</el-table>
<div v-if="pagination" class="pagination__wrap">
<el-pagination
:style="paginationStyle"
:page-sizes="[10, 20, 30, 40, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
v-bind="getPaginationBindValue"
/>
</div>
</div>
</template>
<script setup lang="ts" name="ComTable">
import { PropType, computed, useAttrs, ref, getCurrentInstance, provide } from 'vue'
import { deepClone } from '@/utils'
import { isObject } from '@/utils/validate'
import TableColumn from './components/TableColumn.vue'
import TableSlot from './components/Slot.vue'
const props = defineProps({
// 表头
columns: {
type: Array as PropType<IObj[]>,
default: () => []
},
// 是否多选
selection: {
type: Boolean as PropType<boolean>,
default: false
},
// 是否展示分页
pagination: {
type: [Boolean, Object] as PropType<boolean | IObj>,
default: false
},
// 仅对 type=selection 的列有效,类型为 Boolean为 true 则会在数据更新之后保留之前选中的数据(需指定 row-key
reserveSelection: {
type: Boolean as PropType<boolean>,
default: false
}
})
const attrs = useAttrs()
const tableRef = ref<HTMLElement | null>(null)
function getTableRef() {
return tableRef.value as any
}
const _this = getCurrentInstance()
provide('tableRoot', _this)
const getBindValue = computed((): IObj => {
const bindValue = { ...attrs, ...props } as IObj
delete bindValue.columns
return bindValue
})
function getItemBindValue(item: IObj) {
const delArr: string[] = []
const obj = deepClone(item)
for (const key in obj) {
if (delArr.indexOf(key) !== -1) {
delete obj[key]
}
}
return obj
}
const getPaginationBindValue = computed((): IObj => {
const PaginationBindValue =
props.pagination && isObject(props.pagination) ? { ...props.pagination } : {}
return PaginationBindValue
})
const paginationStyle = computed(() => {
return {
textAlign: (props.pagination && (props.pagination as IObj).position) || 'right'
}
})
function headerDragend(newWidth: number, _: number, column: IObj) {
// 不懂为啥无法自动计算宽度只能手动去计算了。。失望ing到时候看看能不能优化吧。
const htmlArr = document.getElementsByClassName(column.id)
for (const v of htmlArr as any) {
if (v.firstElementChild) {
;(v.firstElementChild as any).style.width = newWidth + 'px'
}
}
}
defineExpose({
getTableRef
})
</script>
<style lang="less" scoped>
.pagination__wrap {
padding: 10px;
margin-top: 15px;
background: #fff;
}
</style>

View File

@@ -65,7 +65,7 @@ import { ref, reactive, computed, watch, nextTick, unref } from 'vue'
import { previewProps } from './props'
import { isFirefox } from '@/utils/validate'
import { on, off } from '@/utils/dom-utils'
import throttle from 'lodash-es/throttle'
import { throttle } from 'lodash-es'
import SvgIcon from '_c/SvgIcon/index.vue'
const mousewheelEventName = isFirefox() ? 'DOMMouseScroll' : 'mousewheel'

View File

@@ -1,170 +0,0 @@
<template>
<el-table ref="elTable" :border="true" v-bind="getBindValue" @header-dragend="headerDragend">
<!-- 多选 -->
<el-table-column
v-if="selection"
type="selection"
:reserve-selection="reserveSelection"
width="40"
/>
<template v-for="item in columns">
<!-- 自定义索引 -->
<template v-if="item.type === 'index'">
<el-table-column
:key="item[item.field]"
v-bind="{ ...getItemBindValue(item) }"
type="index"
:index="item.index"
/>
</template>
<!-- 树型数据 -->
<template v-else-if="item.children && item.children.length">
<table-column :key="item[item.field]" :child="item" />
</template>
<template v-else>
<el-table-column
:key="item[item.field]"
v-bind="{ ...getItemBindValue(item) }"
:prop="item.field"
>
<!-- 表头插槽 -->
<template v-if="item.slots && item.slots.header" #header="scope">
<table-slot
v-if="item.slots && item.slots.header"
:slot-name="item.slots.header"
:column="item"
:index="scope.$index"
/>
</template>
<!-- 表格内容插槽自定义 -->
<template v-if="item.slots && item.slots.default" #default="scope">
<table-slot
v-if="item.slots && item.slots.default"
:slot-name="item.slots.default"
:row="scope.row"
:column="item"
:index="scope.$index"
/>
</template>
</el-table-column>
</template>
</template>
</el-table>
<div v-if="pagination" class="pagination__wrap">
<el-pagination
:style="paginationStyle"
:page-sizes="[10, 20, 30, 40, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
v-bind="getPaginationBindValue"
@size-change="sizeChange"
@current-change="currentChange"
/>
</div>
</template>
<script setup lang="ts" name="ComTable">
import { PropType, computed, ref, unref, useAttrs } from 'vue'
import { deepClone } from '@/utils'
import { isObject } from '@/utils/validate'
import TableColumn from './components/TableColumn.vue'
import TableSlot from './components/Slot.vue'
const props = defineProps({
// 表头
columns: {
type: Array as PropType<IObj[]>,
default: () => []
},
// 是否多选
selection: {
type: Boolean as PropType<boolean>,
default: false
},
// 是否展示分页
pagination: {
type: [Boolean, Object] as PropType<boolean | IObj>,
default: false
},
// 仅对 type=selection 的列有效,类型为 Boolean为 true 则会在数据更新之后保留之前选中的数据(需指定 row-key
reserveSelection: {
type: Boolean as PropType<boolean>,
default: false
}
})
const attrs = useAttrs()
const elTable = ref<HTMLElement | null>(null)
function getTableRef() {
return unref(elTable as any)
}
// const _this = getCurrentInstance()
// provide('tableRoot', _this)
const getBindValue = computed((): IObj => {
const bindValue = { ...attrs, ...props } as IObj
delete bindValue.columns
return bindValue
})
function getItemBindValue(item: IObj) {
const delArr: string[] = []
const obj = deepClone(item)
for (const key in obj) {
if (delArr.indexOf(key) !== -1) {
delete obj[key]
}
}
return obj
}
const getPaginationBindValue = computed((): IObj => {
const PaginationBindValue =
props.pagination && isObject(props.pagination) ? { ...props.pagination } : {}
return PaginationBindValue
})
const paginationStyle = computed(() => {
return {
textAlign: (props.pagination && (props.pagination as IObj).position) || 'right'
}
})
function headerDragend(newWidth: number, _: number, column: IObj) {
// 不懂为啥无法自动计算宽度只能手动去计算了。。失望ing到时候看看能不能优化吧。
const htmlArr = document.getElementsByClassName(column.id)
for (const v of htmlArr as any) {
if (v.firstElementChild) {
;(v.firstElementChild as any).style.width = newWidth + 'px'
}
}
}
function sizeChange(val: number) {
if (props.pagination && (props.pagination as IObj).onSizeChange) {
;(props.pagination as IObj).onSizeChange(val)
}
}
function currentChange(val: number) {
if (props.pagination && (props.pagination as IObj).onCurrentChange) {
;(props.pagination as IObj).onCurrentChange(val)
}
}
defineExpose({
getTableRef
})
</script>
<style lang="less" scoped>
.pagination__wrap {
padding: 10px;
margin-top: 15px;
background: #fff;
}
</style>

View File

@@ -1,9 +1,9 @@
import type { App } from 'vue'
import SvgIcon from './SvgIcon/index.vue' // svg组件
import ComSearch from './Search/index.vue' // search组件
import ComDialog from './Dialog/index.vue' // dialog组件
import ComDetail from './Detail/index.vue' // detail组件
import ComTable from './Table/index.vue' // table组件
import ComSearch from './ComSearch/index.vue' // search组件
import ComDialog from './ComDialog/index.vue' // dialog组件
import ComDetail from './ComDetail/index.vue' // detail组件
import ComTable from './ComTable/index.vue' // table组件
export function setupGlobCom(app: App<Element>): void {
app.component('SvgIcon', SvgIcon)