vue-fantable
Version:
Vue table component for huge data
633 lines (562 loc) • 16.2 kB
JSX
import HeaderCheckboxContent from './header-checkbox-content.jsx'
import HeaderFilterContent from './header-filter-content.jsx'
import HeaderFilterCustomContent from './header-filter-custom-content.jsx'
import {
getFixedTotalWidthByColumnKey,
clsName,
getColKeysByRangeColKeys,
getColKeysByHeaderColumn,
} from '../util/index.js'
import { getValByUnit, isEmptyValue } from '../../../src/utils/index.js'
import { COMPS_NAME, COLUMN_TYPES, EMIT_EVENTS } from '../util/constant'
import VeIcon from '@P/ve-icon/ve-icon.js'
import { ICON_NAMES } from '@P/ve-icon/src/util/constant'
import { GLOBAL_EVENT } from '@P/events/global-events.js'
// import eventCenter from '@P/events/event-center.js'
export default {
name: COMPS_NAME.FAN_TABLE_THADER_Th,
inject: ['eventCenter'],
props: {
// group columns item
groupColumn: {
type: Array,
required: true,
},
// group column item
groupColumnItem: {
type: Object,
required: true,
},
colgroups: {
type: Array,
required: true,
},
headerRows: {
type: Array,
default: function () {
return []
},
},
fixedHeader: {
type: Boolean,
},
isGroupHeader: {
type: Boolean,
required: true,
},
rowIndex: {
type: Number,
required: true,
},
cellSelectionData: {
type: Object,
default: function () {
return null
},
},
// cell selection range data
cellSelectionRangeData: {
type: Object,
default: function () {
return null
},
},
headerIndicatorColKeys: {
type: Object,
default: function () {
return null
},
},
// checkbox option
checkboxOption: {
type: Object,
default: function () {
return null
},
},
// sort option
sortOption: {
type: Object,
default: function () {
return null
},
},
// sort columns
sortColumns: {
type: Object,
default: function () {
return null
},
},
// cell style option
cellStyleOption: {
type: Object,
default: function () {
return null
},
},
// event custom option
eventCustomOption: {
type: Object,
default: function () {
return null
},
},
},
computed: {
// is last left fixed column
isLastLeftFixedColumn() {
let result = false
const { groupColumn, groupColumnItem: column } = this
const { fixed, _keys } = column
if (fixed === 'left') {
const leftFixedColumns = groupColumn.filter(
(x) => x.fixed === 'left',
)
const index = leftFixedColumns.findIndex(
(x) => x._keys === _keys,
)
if (index === leftFixedColumns.length - 1) {
result = true
}
}
return result
},
// is first right fixed column
isfirstRightFixedColumn() {
let result = false
const { groupColumn, groupColumnItem: column } = this
const { fixed, _keys } = column
if (fixed === 'right') {
const leftFixedColumns = groupColumn.filter(
(x) => x.fixed === 'right',
)
if (leftFixedColumns[0]._keys === _keys) {
result = true
}
}
return result
},
// is last column
isLastCloumn() {
let result = false
const { colgroups, groupColumnItem: column } = this
const lastColumnKey = colgroups[colgroups.length - 1].key
const keys = column._keys.split('|')
if (keys.length) {
if (keys.length === 1) {
if (keys[0] === lastColumnKey) {
result = true
}
} else if (keys[keys.length - 2] === lastColumnKey) {
result = true
}
}
return result
},
// is sortable column
isSortableCloumn() {
let result = false
const { sortColumns, groupColumnItem } = this
const currentField = groupColumnItem.field
if (Object.keys(sortColumns).includes(currentField)) {
result = true
}
return result
},
},
methods: {
/*
* @getTheadThClass
* @desc get thead th class
* @param {string} fixed - 固定方式
*/
getTheadThClass({ fixed }) {
const result = {
[clsName('header-th')]: true,
[clsName('fixed-left')]: fixed === 'left',
[clsName('fixed-right')]: fixed === 'right',
[clsName('last-left-fixed-column')]: this.isLastLeftFixedColumn,
[clsName('first-right-fixed-column')]:
this.isfirstRightFixedColumn,
[clsName('last-column')]: this.isLastCloumn,
[clsName('sortable-column')]: this.isSortableCloumn,
}
const {
cellStyleOption,
rowIndex,
groupColumnItem: column,
cellSelectionRangeData,
colgroups,
isGroupHeader,
headerIndicatorColKeys,
} = this
if (cellSelectionRangeData) {
const { leftColKey, rightColKey } = cellSelectionRangeData
const { startColKeyIndex } = headerIndicatorColKeys
const isIndicatorActive = startColKeyIndex > -1
if (!isEmptyValue(leftColKey)) {
let indicatorColKeys = []
if (leftColKey === rightColKey) {
indicatorColKeys = [leftColKey]
} else {
indicatorColKeys =
getColKeysByRangeColKeys({
colKey1: leftColKey,
colKey2: rightColKey,
colgroups,
}) ?? []
}
let showIndicator = false
if (!isGroupHeader) {
if (indicatorColKeys.indexOf(column.key) > -1) {
showIndicator = true
}
} else {
const colKeys = getColKeysByHeaderColumn({
headerColumnItem: column,
})
showIndicator = colKeys.every((colKey) => {
return indicatorColKeys.indexOf(colKey) > -1
})
}
if (showIndicator) {
if (isIndicatorActive) {
result[clsName('cell-indicator-active')] = true
} else {
result[clsName('cell-indicator')] = true
}
}
}
}
if (
cellStyleOption &&
typeof cellStyleOption.headerCellClass === 'function'
) {
const customClass = cellStyleOption.headerCellClass({
column,
rowIndex,
})
if (customClass) {
result[customClass] = true
}
}
return result
},
/*
* @getTheadThStyle
* @desc get body td style
* @param {string} _keys - 当前列包含的key(单元格合并时有多个key值)
* @param {string} align - 居中方式
* @param {bool} fixed - 固定方式
* @param {number} rowIndex - 当前行号
*/
getTheadThStyle({ _keys, align, fixed }, rowIndex) {
const result = {}
const colgroups = this.colgroups
const headerRows = this.headerRows
// text align
result['text-align'] = align || 'center'
// fixed left total width or right total width
if (fixed) {
let key = ''
let totalWidth = 0
const keys = _keys.split('|')
if (fixed === 'left') {
key = keys[0]
} else if (fixed === 'right') {
key = keys.length === 1 ? keys[0] : keys[keys.length - 2]
}
if (key) {
// column index
const columnIndex = colgroups.findIndex(
(x) => x.key === key,
)
if (
(fixed === 'left' && columnIndex > 0) ||
(fixed === 'right' &&
columnIndex < colgroups.length - 1)
) {
totalWidth = getFixedTotalWidthByColumnKey({
colgroups,
colKey: key,
fixed,
})
totalWidth = getValByUnit(totalWidth)
}
}
result.left = fixed === 'left' ? totalWidth : ''
result.right = fixed === 'right' ? totalWidth : ''
}
// header row th fixed top
if (this.fixedHeader) {
let rowHeight = 0
if (rowIndex > 0) {
rowHeight = headerRows.reduce(
(total, currentVal, index) => {
return index < rowIndex
? currentVal.rowHeight + total
: total
},
0,
)
}
rowHeight = getValByUnit(rowHeight)
result.top = rowHeight
}
return result
},
// get chcekbox content
getCheckboxContent() {
let result = null
const { checkboxOption } = this
if (this.groupColumnItem.type === COLUMN_TYPES.CHECKBOX) {
if (!checkboxOption.hideSelectAll) {
// checkbox content props
const checkboxProps = {
column: this.groupColumnItem,
checkboxOption: this.checkboxOption,
}
result = <HeaderCheckboxContent {...checkboxProps} />
}
}
return result
},
// sort change
sortChange() {
let sortResult = ''
const { sortColumns, groupColumnItem, sortOption } = this
const { sortAlways } = sortOption
const currentField = groupColumnItem.field
const sortBy = sortColumns[currentField]
if (sortAlways) {
sortResult = sortBy === 'asc' ? 'desc' : 'asc'
} else {
sortResult = sortBy === 'asc' ? 'desc' : sortBy === 'desc' ? '' : 'asc'
}
this.eventCenter.emit(GLOBAL_EVENT.SORT_CHANGE, {
currentField,
sortResult,
})
},
// get sort content
getSortContent() {
let result = null
const { sortColumns, groupColumnItem } = this
const currentField = groupColumnItem.field
if (Object.keys(sortColumns).includes(currentField)) {
const sortBy = sortColumns[currentField]
const props = {
class: clsName('sort'),
}
result = (
<span {...props}>
<VeIcon
class={[
clsName('sort-icon'),
clsName('sort-icon-top'),
sortBy === 'asc' ? 'active' : '',
]}
name={ICON_NAMES.SORT_TOP_ARROW}
/>
<VeIcon
class={[
clsName('sort-icon'),
clsName('sort-icon-bottom'),
sortBy === 'desc' ? 'active' : '',
]}
name={ICON_NAMES.SORT_BOTTOM_ARROW}
/>
</span>
)
}
return result
},
// get filter content
getFilterContent() {
let result = null
const { groupColumnItem } = this
if (groupColumnItem.filter) {
// filter content props
const filterProps = {
column: this.groupColumnItem,
}
result = <HeaderFilterContent {...filterProps} />
}
return result
},
// get filter custom content
getFilterCustomContent() {
let result = null
const { groupColumnItem } = this
if (groupColumnItem.filterCustom) {
// filter content props
const filterProps = {
column: this.groupColumnItem,
}
result = <HeaderFilterCustomContent {...filterProps} />
}
return result
},
// cell click
cellClick(e, fn) {
fn && fn(e)
const { groupColumnItem } = this
this.eventCenter.emit(GLOBAL_EVENT.HEADER_CELL_CLICK, {
event: e,
column: groupColumnItem,
})
},
// dblclick
cellDblclick(e, fn) {
fn && fn(e)
},
// contextmenu
cellContextmenu(e, fn) {
fn && fn(e)
const { groupColumnItem } = this
this.eventCenter.emit(GLOBAL_EVENT.HEADER_CELL_CONTEXTMENU, {
event: e,
column: groupColumnItem,
},
)
},
// mouseenter
cellMouseenter(e, fn) {
fn && fn(e)
},
// mouseleave
cellMouseleave(e, fn) {
fn && fn(e)
const { groupColumnItem } = this
this.eventCenter.emit(GLOBAL_EVENT.HEADER_CELL_MOUSELEAVE, {
event: e,
column: groupColumnItem,
},
)
},
// mousemove
cellMousemove(e, fn) {
fn && fn(e)
const { groupColumnItem } = this
this.eventCenter.emit(GLOBAL_EVENT.HEADER_CELL_MOUSEMOVE, {
event: e,
column: groupColumnItem,
})
},
// mouseover
cellMouseover(e, fn) {
fn && fn(e)
const { groupColumnItem } = this
this.eventCenter.emit(GLOBAL_EVENT.HEADER_CELL_MOUSEOVER, {
event: e,
column: groupColumnItem,
})
},
// mousedown
cellMousedown(e, fn) {
fn && fn(e)
const { groupColumnItem } = this
this.eventCenter.emit(GLOBAL_EVENT.HEADER_CELL_MOUSEDOWN, {
event: e,
column: groupColumnItem,
})
},
// mouseup
cellMouseup(e, fn) {
fn && fn(e)
},
},
render(h) {
const {
groupColumnItem,
getTheadThClass,
getTheadThStyle,
rowIndex,
eventCustomOption,
} = this
let content
if (typeof groupColumnItem.renderHeaderCell === 'function') {
const { ...column } = groupColumnItem
const renderResult = groupColumnItem.renderHeaderCell(
{
column,
},
h,
)
content = renderResult
} else {
content = groupColumnItem.title
}
// custom on cell event
let customEvents = {}
if (eventCustomOption) {
const { headerCellEvents } = eventCustomOption
customEvents = headerCellEvents
? headerCellEvents({ column: groupColumnItem, rowIndex })
: {}
}
const {
click,
dblclick,
contextmenu,
mouseenter,
mouseleave,
mousemove,
mouseover,
mousedown,
mouseup,
} = customEvents
const thProps = {
style: getTheadThStyle(groupColumnItem, rowIndex),
class: getTheadThClass(groupColumnItem),
rowspan: groupColumnItem._rowspan,
colspan: groupColumnItem._colspan,
onClick: (e) => {
this.cellClick(e, click)
if (
this.isSortableCloumn && e.target instanceof HTMLTableCellElement
) {
this.sortChange()
}
},
onDblclick: (e) => {
this.cellDblclick(e, dblclick)
},
onContextmenu: (e) => {
this.cellContextmenu(e, contextmenu)
},
onMouseenter: (e) => {
this.cellMouseenter(e, mouseenter)
},
onMouseleave: (e) => {
this.cellMouseleave(e, mouseleave)
},
onMousemove: (e) => {
this.cellMousemove(e, mousemove)
},
onMouseover: (e) => {
this.cellMouseover(e, mouseover)
},
onMousedown: (e) => {
this.cellMousedown(e, mousedown)
},
onMouseup: (e) => {
this.cellMouseup(e, mouseup)
},
}
return (
<th {...thProps}>
{/* checkbox content */}
{this.getCheckboxContent()}
{content}
{/* sort content */}
{this.getSortContent()}
{/* filter content */}
{this.getFilterContent()}
{/* filter custom content */}
{this.getFilterCustomContent()}
</th>
)
},
}