You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
287 lines
6.5 KiB
Vue
287 lines
6.5 KiB
Vue
<template>
|
|
<view :class="`wd-table ${border ? 'is-border' : ''}`" :style="tableStyle">
|
|
<scroll-view
|
|
:enable-flex="true"
|
|
:throttle="false"
|
|
:scrollLeft="reactiveState.scrollLeft"
|
|
:scroll-x="true"
|
|
class="wd-table__header"
|
|
@scroll="scroll"
|
|
v-if="showHeader"
|
|
>
|
|
<view id="table-header" class="wd-table__content" :style="realWidthStyle" style="position: sticky; top: 0; z-index: 2">
|
|
<view
|
|
:class="`wd-table__cell ${border ? 'is-border' : ''} ${column.fixed ? 'is-fixed' : ''} ${stripe ? 'is-stripe' : ''} is-${column.align} ${
|
|
isLastFixed(column) && reactiveState.scrollLeft ? 'is-shadow' : ''
|
|
}`"
|
|
:style="headerCellStyle(index)"
|
|
v-for="(column, index) in reactiveState.columns"
|
|
:key="index"
|
|
>
|
|
<wd-sort-button
|
|
v-model="column.sortDirection"
|
|
allow-reset
|
|
:line="false"
|
|
:title="column.label"
|
|
@change="({ value }) => handleSortChange(value, index)"
|
|
v-if="column.sortable"
|
|
/>
|
|
<text v-else :class="`wd-table__value ${ellipsis ? 'is-ellipsis' : ''}`">{{ column.label }}</text>
|
|
</view>
|
|
</view>
|
|
</scroll-view>
|
|
<scroll-view
|
|
class="wd-table__body"
|
|
:style="bodyStyle"
|
|
:enable-flex="true"
|
|
:throttle="false"
|
|
:scroll-x="true"
|
|
@scroll="scroll"
|
|
:scrollLeft="reactiveState.scrollLeft"
|
|
>
|
|
<view id="table-body" class="wd-table__content" :style="realWidthStyle">
|
|
<slot></slot>
|
|
</view>
|
|
</scroll-view>
|
|
</view>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
export default {
|
|
name: 'wd-table',
|
|
options: {
|
|
addGlobalClass: true,
|
|
virtualHost: true,
|
|
styleIsolation: 'shared'
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<script lang="ts" setup>
|
|
import { type CSSProperties, computed, provide, watch, reactive } from 'vue'
|
|
import { addUnit, debounce, deepClone, isDef, objToStyle } from '../common/util'
|
|
import type { SortDirection, TableColumn } from '../wd-table-col/types'
|
|
|
|
interface Props {
|
|
// 显示的数据
|
|
data: Array<any>
|
|
// 是否带有边框
|
|
border?: boolean
|
|
// 是否为斑马纹 table
|
|
stripe?: boolean
|
|
// Table 的高度
|
|
height?: string
|
|
// 行高
|
|
rowHeight?: number | string
|
|
// 是否显示表头
|
|
showHeader?: boolean
|
|
// 是否超出2行隐藏
|
|
ellipsis?: boolean
|
|
}
|
|
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
// 显示的数据
|
|
data: () => [],
|
|
// table行是否为斑马纹
|
|
stripe: true,
|
|
// 是否显示边框
|
|
border: true,
|
|
// table高度
|
|
height: '80vh',
|
|
// 行高
|
|
rowHeight: 50,
|
|
// 是否显示表头
|
|
showHeader: true,
|
|
// 是否超出2行隐藏
|
|
ellipsis: true
|
|
})
|
|
|
|
watch(
|
|
() => props.data,
|
|
(newValue) => {
|
|
reactiveState.data = newValue
|
|
},
|
|
{ deep: true }
|
|
)
|
|
|
|
watch(
|
|
() => props.stripe,
|
|
(newValue) => {
|
|
reactiveState.stripe = newValue
|
|
},
|
|
{ deep: true }
|
|
)
|
|
|
|
watch(
|
|
() => props.border,
|
|
(newValue) => {
|
|
reactiveState.border = newValue
|
|
},
|
|
{ deep: true }
|
|
)
|
|
|
|
watch(
|
|
() => props.height,
|
|
(newValue) => {
|
|
reactiveState.height = newValue
|
|
},
|
|
{ deep: true }
|
|
)
|
|
|
|
watch(
|
|
() => props.rowHeight,
|
|
(newValue) => {
|
|
reactiveState.rowHeight = newValue
|
|
},
|
|
{ deep: true }
|
|
)
|
|
watch(
|
|
() => props.showHeader,
|
|
(newValue) => {
|
|
reactiveState.showHeader = newValue
|
|
},
|
|
{ deep: true }
|
|
)
|
|
|
|
watch(
|
|
() => props.ellipsis,
|
|
(newValue) => {
|
|
reactiveState.ellipsis = newValue
|
|
},
|
|
{ deep: true }
|
|
)
|
|
|
|
const reactiveState = reactive({
|
|
data: props.data,
|
|
stripe: props.stripe,
|
|
border: props.border,
|
|
height: props.height,
|
|
rowHeight: props.rowHeight,
|
|
showHeader: props.showHeader,
|
|
ellipsis: props.ellipsis,
|
|
scrollLeft: 0,
|
|
columns: [] as TableColumn[],
|
|
setRowClick,
|
|
setColumns
|
|
})
|
|
|
|
const scroll = debounce(handleScroll, 100, false) // 滚动事件
|
|
|
|
provide('wdTable', reactiveState)
|
|
|
|
const emit = defineEmits(['click', 'sort-method', 'row-click'])
|
|
|
|
/**
|
|
* 容器样式
|
|
*/
|
|
const tableStyle = computed(() => {
|
|
const style: CSSProperties = {}
|
|
if (isDef(props.height)) {
|
|
style['max-height'] = addUnit(props.height)
|
|
}
|
|
return objToStyle(style)
|
|
})
|
|
|
|
const realWidthStyle = computed(() => {
|
|
const style: CSSProperties = {
|
|
display: 'flex'
|
|
}
|
|
let width: string | number = ''
|
|
reactiveState.columns.forEach((column) => {
|
|
width = width ? `${width} + ${addUnit(column.width)}` : addUnit(column.width)
|
|
})
|
|
style['width'] = `calc(${width})`
|
|
return objToStyle(style)
|
|
})
|
|
|
|
const bodyStyle = computed(() => {
|
|
const style: CSSProperties = {}
|
|
if (isDef(props.height)) {
|
|
style['height'] = isDef(props.rowHeight) ? `calc(${props.data.length} * ${addUnit(props.rowHeight)})` : `calc(${props.data.length} * 50px)`
|
|
}
|
|
return `${objToStyle(style)};`
|
|
})
|
|
|
|
/**
|
|
* 是否最后一个固定元素
|
|
* @param column 列数据
|
|
*/
|
|
function isLastFixed(column: TableColumn) {
|
|
let isLastFixed: boolean = false
|
|
if (column.fixed && isDef(reactiveState.columns)) {
|
|
const columns = reactiveState.columns.filter((column) => {
|
|
return column.fixed
|
|
})
|
|
if (columns.length && columns[columns.length - 1].prop === column.prop) {
|
|
isLastFixed = true
|
|
}
|
|
}
|
|
return isLastFixed
|
|
}
|
|
|
|
/**
|
|
* 设置列
|
|
* @param column 列
|
|
*/
|
|
function setColumns(column: TableColumn) {
|
|
reactiveState.columns = deepClone([...reactiveState.columns, column])
|
|
}
|
|
|
|
/**
|
|
* 表头单元格样式
|
|
*/
|
|
function headerCellStyle(columnIndex: number) {
|
|
const style: CSSProperties = {}
|
|
if (isDef(reactiveState.columns[columnIndex].width)) {
|
|
style['width'] = addUnit(reactiveState.columns[columnIndex].width)
|
|
}
|
|
if (reactiveState.columns[columnIndex].fixed) {
|
|
if (columnIndex > 0) {
|
|
let left: string | number = ''
|
|
reactiveState.columns.forEach((column, index) => {
|
|
if (index < columnIndex) {
|
|
left = left ? `${left} + ${addUnit(column.width)}` : addUnit(column.width)
|
|
}
|
|
})
|
|
style['left'] = `calc(${left})`
|
|
} else {
|
|
style['left'] = 0
|
|
}
|
|
}
|
|
return objToStyle(style)
|
|
}
|
|
|
|
/**
|
|
* 排序
|
|
* @param value
|
|
* @param index
|
|
*/
|
|
function handleSortChange(value: SortDirection, index: number) {
|
|
reactiveState.columns[index].sortDirection = value
|
|
reactiveState.columns.forEach((col, i) => {
|
|
if (index != i) {
|
|
col.sortDirection = 0
|
|
}
|
|
})
|
|
emit('sort-method', reactiveState.columns[index])
|
|
}
|
|
|
|
/**
|
|
* 滚动事件
|
|
*/
|
|
function handleScroll(event) {
|
|
if (!props.showHeader) {
|
|
return
|
|
}
|
|
reactiveState.scrollLeft = event.detail.scrollLeft
|
|
}
|
|
|
|
function setRowClick(index: number) {
|
|
emit('row-click', { rowIndex: index })
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
@import './index.scss';
|
|
</style>
|