feat: Table组件重构
This commit is contained in:
@@ -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
|
||||
},
|
||||
// 是否标题和内容各占一行 垂直布局
|
||||
@@ -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'
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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',
|
||||
159
src/components/ComTable/index.vue
Normal file
159
src/components/ComTable/index.vue
Normal 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>
|
||||
@@ -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'
|
||||
|
||||
|
||||
@@ -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>
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user