<template>
<section class="table-header">
<!-- <view
class="fixed-title-mark"
:style="{
width: handleCellSize(firstColumn.width),
height: handleCellSize(props.headerHeight),
textAlign: firstColumn.align || 'center',
}"
>
<h5-table-cell
:key="Math.random()"
:dataValue="firstColumn.title"
:slotKey="firstColumn.slotTitleKey"
:slots="$slots"
/>
</view> -->
<view
v-show="moreMark"
class="fixed-title-more"
:style="{
height: handleCellSize(props.headerHeight),
}"
>
<view class="mark"></view>
</view>
<h5-table-header
ref="tableContainerRef"
:column="props.column"
:class="['title-header']"
@handleHeadSortClick="handleHeadSortClick"
:slots="$slots"
:height="props.headerHeight"
:rootValue="props.rootValue"
></h5-table-header>
</section>
<view
ref="tableRef"
class="table"
:style="{
height: handleCellSize(tableHeight),
}"
>
<!-- <section
class="first-column"
:style="{
width: handleCellSize(firstColumn.width),
}"
>
<view
v-for="(item, index) in props.tableDatas"
:class="['table-row-column']"
:style="{
width: handleCellSize(firstColumn.width),
height: handleCellSize(props.rowHeight),
textAlign: firstColumn.align || 'center',
}"
>
<h5-table-cell
:key="index"
:dataValue="firstColumn.dataIndex ? item[firstColumn.dataIndex] : ''"
:dataItem="item"
:render="firstColumn.render"
:slotKey="firstColumn.slotKey"
:slots="$slots"
/>
</view>
</section> -->
<section id="table-content" class="table-content">
<h5-table-row
v-for="(item, index) in props.tableDatas"
:key="index"
:data-item="item"
:column="props.column"
:height="props.rowHeight"
:slots="$slots"
:rootValue="props.rootValue"
:rowColor="item.rowColor"
@touchend.native="handleClick(item, index)"
></h5-table-row>
</section>
<section class="loading" @click="tryAgain" v-show="props.disable">
{{ loadingText }}
</section>
<view
class="rowMarkContainer"
:style="{
top: rowDownMarkTop + 'px',
}"
v-show="rowDownMarkTop > 0"
>
<slot name="rowDownMark"></slot>
</view>
</view>
</template>
<script lang="ts" setup name="H5Table">
import h5TableCell from './h5-table-cell'
import H5TableRow from './h5-table-row.vue'
import H5TableHeader from './h5-table-header.vue'
import useGetTransformX from '../hooks/useGetTransformX'
import { onMounted, computed, ref, watchEffect, watch } from 'vue'
import type { columnItemType, sortStatusType } from '../types'
import { cellSize, pxtorem } from '../utils'
import useDebounce from '../hooks/useDebounce'
import useResize from '../hooks/useResize'
type propsType = {
minTableHeight?: number //表格最小高度
rowNum?: number // 表格显示几行
headerHeight?: number // 头部默认高度
rowHeight?: number //每行数据的默认高度
column: Array<columnItemType>
tableDatas: Array<any>
fixedHeader?: boolean // 是否固定表头
isClick?: boolean // 是否需要触发行点击事件
disable?: boolean // 是否启用下拉加载
error?: boolean // 数据加载失败
loading?: boolean // 数据处于加载状态
finish?: boolean // 数据 是否完全加载
loadingText?: string // 加载文案
errorText?: string // 失败文案
finishedText?: string // 完成文案
offset?: number //触发加载的底部距离
rootValue?: number //
rowColor?: string | undefined // 行颜色
}
type emitType = {
(e: 'rowClick', item: any, index: number): void
(e: 'handleHeadSortClick', propKey: string, type: sortStatusType): void
(e: 'update:loading', val: boolean): void
(e: 'update:error', val: boolean): void
(e: 'load'): void
}
const tableHeight = ref<number>(600)
const tableWidth = ref<number>(0)
const tableContent = ref<number>(0)
const tableRef = ref<HTMLElement | null>(null)
const tableContainerRef = ref<typeof H5TableHeader | null>(null)
const tableContentEL = ref<HTMLElement | null>(null)
const rowDownMarkTop = ref<number>(0)
const props = withDefaults(defineProps<propsType>(), {
minTableHeight: 600,
rowNum: 6,
headerHeight: 60,
rowHeight: 100,
tableDatas: () => [],
fixedHeader: true,
isClick: true,
disable: false, // 是否启用下拉加载
error: false, // 数据加载失败
loading: false, // 数据处于加载状态
finish: false, // 数据 是否完全加载
loadingText: '加载中...', // 加载文案
errorText: '出错了', // 失败文案
finishedText: '到底了', // 完成文案
offset: 10,
rootValue: 75,
})
const emits = defineEmits<emitType>()
const disable = computed(() => props.disable)
const moreMark = ref<boolean>(false)
const handleCellSize = (num: number) => {
return cellSize(num, props.rootValue)
}
const loading = computed({
get() {
return props.loading
},
set(val: boolean) {
emits('update:loading', val)
},
})
const error = computed({
get() {
return props.error
},
set(val: boolean) {
emits('update:error', val)
},
})
const loadingText = computed(() => {
let str = ''
if (loading.value) {
str = props.loadingText
}
if (props.finish) {
str = props.finishedText
}
if (error.value) {
str = props.errorText
}
return str
})
const bottomEvent = () => {
if (props.finish) return
if (!loading.value) {
loading.value = true
emits('load')
}
}
const tryAgain = () => {
if (error.value) {
error.value = false
emits('load')
}
}
const handleClick = (item: any, index: number) => {
if (!props.isClick) return
//只有 左右 上下 移动 都在 20像素之内 才判定 用户点击
if (Math.abs(distanX.value) < 20 && Math.abs(distanY.value) < 20) {
emits('rowClick', item, index)
}
}
const handleHeadSortClick = (propKey: string, type: sortStatusType) => {
//先恢复 rowMarkContainer 再排序
realHandleDom(0, -1)
emits('handleHeadSortClick', propKey, type)
}
// 根据 指定下标 下 移动元素
const handleDom = () => {
// 使用闭包 记录 上次 被操作的dom 列表
let pre_doms: Array<any> = []
return (height: number, index: number) => {
if (pre_doms.length > 0) {
pre_doms.forEach((item) => {
item.style.marginBottom = 0
})
// 恢复清空
pre_doms = []
}
// index -1 表示 恢复margin 移动
if (index === -1) {
rowDownMarkTop.value = 0
return
}
const tableDom = tableRef.value
//获取第一列
const firstColumn = tableDom?.querySelector('.table .first-column') || null
const targetDom = firstColumn?.children[index] || null
if (targetDom) {
;(targetDom as any).style.marginBottom = pxtorem(height, props.rootValue)
pre_doms.push(targetDom)
}
// 移动 index 所属的行
const rowDom = tableDom?.querySelector('#table-content')?.children[index]
const rowTarget: HTMLCollection | undefined = rowDom?.children
if (rowTarget) {
Array.from(rowTarget).forEach((item) => {
;(item as any).style.marginBottom = pxtorem(height, props.rootValue)
pre_doms.push(item)
})
}
let rem = Number(document.documentElement.style.fontSize.replace('px', ''))
// 计算 点击元素插槽下移距离
const top =
rowDom!.getBoundingClientRect().top -
tableContentEL.value!.getBoundingClientRect().top
rowDownMarkTop.value