feat: table组件支持排序

This commit is contained in:
xuqingkai 2023-10-28 18:15:14 +08:00
parent 68affd2f59
commit 9898b01c06
7 changed files with 220 additions and 1520 deletions

File diff suppressed because it is too large Load Diff

View File

@ -796,3 +796,6 @@ $-navbar-capsule-border-color: var(--wot-navbar-capsule-border-color, #e7e7e7);
$-navbar-capsule-border-radius: var(--wot-navbar-capsule-border-radius, 16px); $-navbar-capsule-border-radius: var(--wot-navbar-capsule-border-radius, 16px);
$-navbar-capsule-width: var(--wot-navbar-capsule-width, 88px); $-navbar-capsule-width: var(--wot-navbar-capsule-width, 88px);
$-navbar-capsule-height: var(--wot-navbar-capsule-height, 32px); $-navbar-capsule-height: var(--wot-navbar-capsule-height, 32px);
// table
$-table-color: var(--wot-table-color, $-font-gray-1);

View File

@ -24,7 +24,7 @@ export default {
} }
</script> </script>
<script lang="ts" setup> <script lang="ts" setup>
import { type CSSProperties, computed, inject, onMounted, ref, watch, getCurrentInstance, reactive } from 'vue' import { type CSSProperties, computed, inject, onMounted, ref, watch, getCurrentInstance } from 'vue'
import { isDef, objToStyle } from '../common/util' import { isDef, objToStyle } from '../common/util'
interface Props { interface Props {

View File

@ -1,6 +1,6 @@
type AlignType = 'left' | 'center' | 'right' // 列的对齐方式 type AlignType = 'left' | 'center' | 'right' // 列的对齐方式
type SortDirection = 'asc' | 'desc' // 列的排序方向 export type SortDirection = 0 | 1 | -1 // 列的排序方向
export interface TableColumn { export interface TableColumn {
// 列对应字段 // 列对应字段
@ -16,5 +16,5 @@ export interface TableColumn {
// 列的对齐方式可选值left,center,right // 列的对齐方式可选值left,center,right
align?: AlignType align?: AlignType
// 列的排序方向 // 列的排序方向
sortDirection?: SortDirection | '' sortDirection: SortDirection
} }

View File

@ -8,7 +8,7 @@
@click="handleRowClick(index)" @click="handleRowClick(index)"
> >
<slot v-if="$slots.default" :row="scope(index)"></slot> <slot v-if="$slots.default" :row="scope(index)"></slot>
<view class="cell" v-else>{{ row }}</view> <text :class="`wd-table__value ${ellipsis ? 'is-ellipsis' : ''}`" v-else>{{ row }}</text>
</view> </view>
</view> </view>
</template> </template>
@ -24,7 +24,7 @@ export default {
} }
</script> </script>
<script lang="ts" setup> <script lang="ts" setup>
import { Ref, computed, inject, onMounted, ref } from 'vue' import { type CSSProperties, computed, inject, onMounted } from 'vue'
import { addUnit, isDef, objToStyle, isOdd } from '../common/util' import { addUnit, isDef, objToStyle, isOdd } from '../common/util'
type AlignType = 'left' | 'center' | 'right' type AlignType = 'left' | 'center' | 'right'
@ -51,29 +51,29 @@ const props = withDefaults(defineProps<Props>(), {
align: 'left' align: 'left'
}) })
// eslint-disable-next-line @typescript-eslint/ban-types const parent = inject<any>('wdTable', { data: [] }) // table
const setColumns: Function = inject('setColumns') as Function // .
// eslint-disable-next-line @typescript-eslint/ban-types
const setRowClick: Function = inject('setRowClick') as Function
const $props = inject<Ref>('$props') || ref({}) // table
// //
const stripe = computed(() => { const stripe = computed(() => {
return $props.value.stripe || false return parent.stripe || false
}) })
/** /**
* 是否有边框 * 是否有边框
*/ */
const border = computed(() => { const border = computed(() => {
return $props.value.border || false return parent.border || false
})
const ellipsis = computed(() => {
return parent.ellipsis || false
}) })
/** /**
* 根节点样式 * 根节点样式
*/ */
const rootStyle = computed(() => { const rootStyle = computed(() => {
const style: Record<string, string | number> = {} const style: CSSProperties = {}
if (isDef(props.width)) { if (isDef(props.width)) {
// widthpxpx // widthpxpx
style['width'] = addUnit(props.width) style['width'] = addUnit(props.width)
@ -83,8 +83,8 @@ const rootStyle = computed(() => {
}) })
const rowStyle = computed(() => { const rowStyle = computed(() => {
const rowHeight: string | number = $props.value.rowHeight || '80rpx' // const rowHeight: string | number = parent.rowHeight || '80rpx' //
const style: Record<string, string | number> = {} const style: CSSProperties = {}
if (isDef(props.width)) { if (isDef(props.width)) {
// widthpxpx // widthpxpx
style['width'] = addUnit(props.width) style['width'] = addUnit(props.width)
@ -93,37 +93,40 @@ const rowStyle = computed(() => {
// widthpxpx // widthpxpx
style['height'] = addUnit(rowHeight) style['height'] = addUnit(rowHeight)
} }
return objToStyle(style) return objToStyle(style)
}) })
// //
const scope = computed(() => { const scope = computed(() => {
return (index: number) => { return (index: number) => {
return $props.value.data[index] || {} return parent.data[index] || {}
} }
}) })
// //
const column = computed(() => { const column = computed(() => {
let column: any[] = $props.value.data.map((item) => { let column: any[] = parent.data.map((item) => {
return item[props.prop] return item[props.prop]
}) })
return column return column
}) })
onMounted(() => { onMounted(() => {
setColumns({ parent.setColumns &&
prop: props.prop, parent.setColumns({
label: props.label, prop: props.prop,
width: props.width, label: props.label,
sortable: props.sortable, width: props.width,
lightHigh: props.lightHigh, sortable: props.sortable,
align: props.align lightHigh: props.lightHigh,
}) sortDirection: 0,
align: props.align
})
}) })
function handleRowClick(index: number) { function handleRowClick(index: number) {
setRowClick(index) parent.setRowClick && parent.setRowClick(index)
} }
</script> </script>

View File

@ -55,7 +55,6 @@
width: 100%; width: 100%;
height: 48px; height: 48px;
display: flex; display: flex;
pointer-events: none;
overflow-x: auto; overflow-x: auto;
white-space: nowrap; white-space: nowrap;
} }
@ -66,13 +65,15 @@
} }
@include e(cell) { @include e(cell) {
flex: 0 0 auto;
width: 220rpx;
display: flex; display: flex;
align-items: center; align-items: center;
box-sizing: border-box; box-sizing: border-box;
padding: 10px; flex: 0 0 auto;
width: 220rpx;
min-height: 50px;
padding: 8px 10px;
font-size: 13px;
color: $-table-color;
@include when(border) { @include when(border) {
border-right: 1px solid #ececec; border-right: 1px solid #ececec;
@ -82,6 +83,24 @@
@include when(stripe) { @include when(stripe) {
background: #f3f3f3; background: #f3f3f3;
} }
@include when(left) {
justify-content: flex-start;
}
@include when(center) {
justify-content: center;
}
@include when(right) {
justify-content: flex-end;
}
} }
@include e(value) {
@include when(ellipsis) {
@include multiEllipsis(2);
}
}
} }

View File

@ -1,32 +1,48 @@
<template> <template>
<view :class="`wd-table-content ${border ? 'is-border' : ''}`"> <view :class="`wd-table-content ${border ? 'is-border' : ''}`">
<view :class="`wd-table--fixed ${isShadow ? 'is-shadow' : ''}`" :style="fixedStyle"> <view :class="`wd-table--fixed ${isShadow ? 'is-shadow' : ''}`" :style="fixedStyle" v-if="$slots.fixed">
<view class="wd-table__header" v-if="showHeader"> <view class="wd-table__header" v-if="showHeader">
<view <view
:class="`wd-table__cell ${border ? 'is-border' : ''} ${stripe ? 'is-stripe' : ''}`" :class="`wd-table__cell ${border ? 'is-border' : ''} ${stripe ? 'is-stripe' : ''} is-${column.align}`"
:style="headerCellStyle(column.width)" :style="headerCellStyle(column.width)"
v-for="(column, index) in columns" v-for="(column, index) in columns"
:key="index" :key="index"
> >
<text>{{ column.label }}</text> <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>
</view> </view>
<scroll-view class="wd-table__body" :style="fixedBodyStyle" :enable-flex="true" :scroll-y="true" :scroll-top="scrollTop"> <scroll-view class="wd-table__body" :style="fixedBodyStyle" :enable-flex="true" :throttle="false" :scroll-y="true" :scroll-top="scrollTop">
<view style="display: inline-flex" id="fixed-body"> <view style="display: inline-flex" id="fixed-body">
<slot name="fixed"></slot> <slot name="fixed"></slot>
</view> </view>
</scroll-view> </scroll-view>
</view> </view>
<scroll-view :class="`wd-table is-border`" :style="wraperStyle" :scroll-x="true" :throttle="false" @scroll="handleWraperScroll"> <scroll-view class="wd-table" :style="wraperStyle" :scroll-x="true" :throttle="false" @scroll="handleWraperScroll">
<view class="wd-table__header" id="table-header" :style="rowStyle" v-if="showHeader"> <view class="wd-table__header" id="table-header" :style="rowStyle" v-if="showHeader">
<view <view
:class="`wd-table__cell ${border ? 'is-border' : ''} ${stripe ? 'is-stripe' : ''}`" :class="`wd-table__cell ${border ? 'is-border' : ''} ${stripe ? 'is-stripe' : ''} is-${column.align}`"
:style="headerCellStyle(column.width)" :style="headerCellStyle(column.width)"
v-for="(column, index) in columns" v-for="(column, index) in columns"
:key="index" :key="index"
> >
<text>{{ column.label }}</text> <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>
</view> </view>
<scroll-view class="wd-table__body" :style="bodyStyle" :enable-flex="true" :throttle="false" :scroll-y="true" @scroll="handleBodyScroll"> <scroll-view class="wd-table__body" :style="bodyStyle" :enable-flex="true" :throttle="false" :scroll-y="true" @scroll="handleBodyScroll">
@ -51,9 +67,11 @@ export default {
</script> </script>
<script lang="ts" setup> <script lang="ts" setup>
import { type CSSProperties, computed, getCurrentInstance, nextTick, onMounted, provide, ref, watch } from 'vue' import { type CSSProperties, computed, getCurrentInstance, nextTick, onMounted, provide, ref, watch, reactive } from 'vue'
import { addUnit, deepClone, getRect, isDef, objToStyle } from '../common/util' import { addUnit, deepClone, getRect, isDef, objToStyle } from '../common/util'
import type { TableColumn } from '../wd-table-col/types' import type { SortDirection, TableColumn } from '../wd-table-col/types'
const a = ref(0)
interface Props { interface Props {
// //
@ -68,6 +86,8 @@ interface Props {
rowHeight?: number | string rowHeight?: number | string
// //
showHeader?: boolean showHeader?: boolean
// 2
ellipsis?: boolean
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
@ -79,16 +99,18 @@ const props = withDefaults(defineProps<Props>(), {
// table // table
height: '80vh', height: '80vh',
// //
rowHeight: '48px', rowHeight: 50,
// //
showHeader: true showHeader: true,
// 2
ellipsis: true
}) })
// privide$dataSource // privide$dataSource
watch( watch(
props, () => props.data,
() => { (newValue) => {
$props.value = props parentData.data = newValue
}, },
{ deep: true } { deep: true }
) )
@ -96,23 +118,25 @@ watch(
const scrollTop = ref<number>(0) // scroll-view const scrollTop = ref<number>(0) // scroll-view
const scrollWidth = ref<number | string>('auto') // scroll-viewsticky const scrollWidth = ref<number | string>('auto') // scroll-viewsticky
const columns = ref<TableColumn[]>([]) // const columns = ref<TableColumn[]>([]) //
const $props = ref<Props>(props)
const fixedWidth = ref<number | string>(0) // const fixedWidth = ref<number | string>(0) //
const isShadow = ref<boolean>(false) // shadow const isShadow = ref<boolean>(false) // shadow
const emit = defineEmits(['click', 'sort-method', 'row-click']) const parentData = reactive({
data: props.data,
/** stripe: props.stripe,
* 根节点样式 border: props.border,
*/ height: props.height,
const rootStyle = computed(() => { rowHeight: props.rowHeight,
const style: CSSProperties = {} showHeader: props.showHeader,
if (isDef(props.height)) { ellipsis: props.ellipsis,
style['height'] = addUnit(props.height) setRowClick,
} setColumns
return objToStyle(style)
}) })
provide('wdTable', parentData)
const emit = defineEmits(['click', 'sort-method', 'row-click'])
/** /**
* 容器样式 * 容器样式
*/ */
@ -145,7 +169,7 @@ const rowStyle = computed(() => {
return objToStyle(style) return objToStyle(style)
}) })
const headerHeight = ref<string | number>(48) // header const headerHeight = ref<string | number>(50) // header
const fixedBodyStyle = computed(() => { const fixedBodyStyle = computed(() => {
const style: CSSProperties = {} const style: CSSProperties = {}
@ -193,31 +217,23 @@ function setColumns(column: TableColumn) {
* 表头单元格样式 * 表头单元格样式
*/ */
function headerCellStyle(width?: string | number) { function headerCellStyle(width?: string | number) {
const style: Record<string, string | number> = {} const style: CSSProperties = {}
if (isDef(width)) { if (isDef(width)) {
style['width'] = addUnit(width) style['width'] = addUnit(width)
} }
return objToStyle(style) return objToStyle(style)
} }
/** function handleSortChange(value: SortDirection, index: number) {
* 排序事件 columns.value[index].sortDirection = value
*/
function doSort(column, index) {
if (column.sortDirection === 'asc') {
columns.value[index].sortDirection = 'desc'
} else if (columns.value[index].sortDirection === 'desc') {
columns.value[index].sortDirection = ''
} else {
columns.value[index].sortDirection = 'asc'
}
columns.value.forEach((col, i) => { columns.value.forEach((col, i) => {
if (index != i) { if (index != i) {
col.sortDirection = '' col.sortDirection = 0
} }
}) })
emit('sort-method', columns.value[index]) emit('sort-method', columns.value[index])
} }
/** /**
* 滚动事件 * 滚动事件
*/ */
@ -245,11 +261,30 @@ function handleWraperScroll(event) {
function setRowClick(index: number) { function setRowClick(index: number) {
emit('row-click', { rowIndex: index }) emit('row-click', { rowIndex: index })
} }
provide('$props', $props) // provide
provide('setRowClick', setRowClick)
provide('setColumns', setColumns)
</script> </script>
<!-- <script module="touch" lang="wxs">
function touchstart(event, ins) {
console.log(event);
console.log(ins);
}
function touchend(event, ins) {
console.log(event);
console.log(ins);
}
function transitionend(event, ins) {
console.log(event);
console.log(ins);
}
module.exports= {
touchstart,
touchend,
transitionend
}
</script> -->
<style lang="scss" scoped> <style lang="scss" scoped>
@import './index.scss'; @import './index.scss';