mirror of
https://gitee.com/wot-design-uni/wot-design-uni.git
synced 2025-12-06 09:08:51 +08:00
feat: ✨ table组件支持排序
This commit is contained in:
parent
68affd2f59
commit
9898b01c06
File diff suppressed because it is too large
Load Diff
@ -795,4 +795,7 @@ $-navbar-hover-color :var(--wot-navbar-hover-color, #eee) !default; // navbar ho
|
|||||||
$-navbar-capsule-border-color: var(--wot-navbar-capsule-border-color, #e7e7e7);
|
$-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);
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)) {
|
||||||
// width存在且包含px则转成px
|
// width存在且包含px则转成px
|
||||||
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)) {
|
||||||
// width存在且包含px则转成px
|
// width存在且包含px则转成px
|
||||||
style['width'] = addUnit(props.width)
|
style['width'] = addUnit(props.width)
|
||||||
@ -93,37 +93,40 @@ const rowStyle = computed(() => {
|
|||||||
// width存在且包含px则转成px
|
// width存在且包含px则转成px
|
||||||
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>
|
||||||
|
|
||||||
|
|||||||
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -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-view中sticky失效的问题
|
const scrollWidth = ref<number | string>('auto') // 动态设置滚动宽度,兼容微信scroll-view中sticky失效的问题
|
||||||
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';
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user