vue-fantable
Version:
Vue table component for huge data
656 lines (586 loc) • 15.2 kB
JSX
import { clsName, getRowKeysByRangeRowKeys } from '../util/index.js'
import { isNumber, isBoolean, isEmptyValue } from '../../../src/utils/index.js'
import {
COMPS_NAME,
COLUMN_TYPES,
EXPAND_TRIGGER_TYPES,
EMIT_EVENTS,
COMPS_CUSTOM_ATTRS,
} from '../util/constant'
import ExpandTrIcon from './expand-tr-icon.jsx'
import BodyCheckboxContent from './body-checkbox-content.jsx'
import BodyRadioContent from './body-radio-content.jsx'
import { GLOBAL_EVENT } from '@P/events/global-events.js'
// import eventCenter from '@P/events/event-center.js'
export default {
name: COMPS_NAME.FAN_TABLE_BODY_TD,
components: {
BodyCheckboxContent, ExpandTrIcon
},
inject: ['eventCenter'],
props: {
rowData: {
type: Object,
required: true,
},
column: {
type: Object,
required: true,
},
columnCollection: {
type: Array,
required: true,
},
rowIndex: {
type: Number,
required: true,
},
colgroups: {
type: Array,
required: true,
},
rowKeyFieldName: {
type: String,
default: null,
},
allRowKeys: {
type: Array,
required: true,
},
/*
expand
*/
// expand row option
expandOption: {
type: Object,
default: function () {
return null
},
},
// is expand row
isExpandRow: {
type: Boolean,
required: true,
},
// expanded row keys
expandedRowkeys: {
type: Array,
default: function () {
return []
},
},
/*
checkbox
*/
// checkbox option
checkboxOption: {
type: Object,
default: function () {
return null
},
},
internalCheckboxSelectedRowKeys: {
type: Array,
default: function () {
return null
},
},
/*
radio
*/
radioOption: {
type: Object,
default: function () {
return null
},
},
internalRadioSelectedRowKey: {
type: [String, Number],
default: null,
},
// cell style option
cellStyleOption: {
type: Object,
default: function () {
return null
},
},
// event custom option
eventCustomOption: {
type: Object,
default: function () {
return null
},
},
// cell selection data
cellSelectionData: {
type: Object,
default: function () {
return null
},
},
// cell selection range data
cellSelectionRangeData: {
type: Object,
default: function () {
return null
},
},
bodyIndicatorRowKeys: {
type: Object,
default: function () {
return null
},
},
// cell span option
cellSpanOption: {
type: Object,
default: function () {
return null
},
},
// edit opttion
editOption: {
type: Object,
default: function () {
return null
},
},
},
emits: ['expand-row-change'],
data() {
return {
// 原始单元格数据
rawCellValue: '',
}
},
computed: {
/*
current column collection item
1、Cache the style、class of each column
*/
currentColumnCollectionItem() {
const { columnCollection, column } = this
return columnCollection.find((x) => x.colKey === column.key)
},
// current row key
currentRowKey() {
const { rowData, rowKeyFieldName } = this
return rowData[rowKeyFieldName]
},
},
watch: {
// watch row data
rowData: {
handler(rowData) {
const column = this.column
if (column) {
this.rawCellValue = rowData[column.field]
}
},
deep: true,
immediate: true,
},
},
methods: {
/*
* @bodyTdStyle
* @desc body td style
*/
bodyTdStyle() {
const { currentColumnCollectionItem } = this
let result = {}
if (currentColumnCollectionItem) {
result = Object.assign(
result,
currentColumnCollectionItem.style,
)
}
return result
},
/*
* @bodyTdClass
* @desc body td class
*/
bodyTdClass() {
const { currentColumnCollectionItem } = this
const { fixed, operationColumn } = this.column
let result = {
[clsName('body-td')]: true,
}
const {
cellStyleOption,
rowData,
column,
rowIndex,
allRowKeys,
cellSelectionData,
cellSelectionRangeData,
bodyIndicatorRowKeys,
currentRowKey,
} = this
// column fixed
if (fixed) {
result[clsName('fixed-left')] = fixed === 'left'
result[clsName('fixed-right')] = fixed === 'right'
}
// operation column
if (operationColumn) {
result[clsName('operation-col')] = true
}
// cell style option
if (
cellStyleOption &&
typeof cellStyleOption.bodyCellClass === 'function'
) {
const customClass = cellStyleOption.bodyCellClass({
row: rowData,
column,
rowIndex,
})
if (customClass) {
result[customClass] = true
}
}
if (cellSelectionData) {
const { rowKey, colKey } = cellSelectionData.currentCell
if (!isEmptyValue(rowKey) && !isEmptyValue(colKey)) {
if (currentRowKey === rowKey) {
// cell selection
if (column.key === colKey) {
result[clsName('cell-selection')] = true
}
}
if (operationColumn) {
const { topRowKey, bottomRowKey } =
cellSelectionRangeData
const { startRowKeyIndex } = bodyIndicatorRowKeys
const isIndicatorActive = startRowKeyIndex > -1
let indicatorRowKeys = []
if (topRowKey === bottomRowKey) {
indicatorRowKeys = [topRowKey]
} else {
indicatorRowKeys = getRowKeysByRangeRowKeys({
topRowKey,
bottomRowKey,
allRowKeys,
})
}
// cell indicator (operation column)
if (indicatorRowKeys.includes(currentRowKey)) {
if (isIndicatorActive) {
result[clsName('cell-indicator-active')] = true
} else {
result[clsName('cell-indicator')] = true
}
}
}
}
}
if (currentColumnCollectionItem) {
result = Object.assign(
result,
currentColumnCollectionItem.class,
)
}
return result
},
// get ellipsis content style
getEllipsisContentStyle() {
const result = {}
const { ellipsis } = this.column
if (ellipsis) {
const { lineClamp } = ellipsis
const _lineClamp = isNumber(lineClamp) ? lineClamp : 1
result['-webkit-line-clamp'] = _lineClamp
}
return result
},
// get render content
getRenderContent(h) {
let content = null
const { column, rowData, rowIndex, rawCellValue } = this
// has render function
if (typeof column.renderBodyCell === 'function') {
const renderResult = column.renderBodyCell(
{
row: rowData,
column,
rowIndex,
},
h,
)
content = renderResult
} else {
content = rawCellValue
}
// ellipisis
if (column.ellipsis) {
const { showTitle } = column.ellipsis
// default true
const isShowTitle = isBoolean(showTitle) ? showTitle : true
content = (
<span
title={isShowTitle ? content : ''}
style={this.getEllipsisContentStyle()}
class={clsName('body-td-span-ellipsis')}
>
{content}
</span>
)
}
return content
},
// get chcekbox content
getCheckboxContent() {
if (this.column.type === COLUMN_TYPES.CHECKBOX) {
// checkbox content props
const checkboxProps = {
column: this.column,
checkboxOption: this.checkboxOption,
rowKey: this.rowData[this.rowKeyFieldName],
internalCheckboxSelectedRowKeys:
this.internalCheckboxSelectedRowKeys,
}
return <BodyCheckboxContent {...checkboxProps} />
}
return null
},
// get radio content
getRadioContent() {
if (this.column.type === COLUMN_TYPES.RADIO) {
// radio props
const radioProps = {
column: this.column,
radioOption: this.radioOption,
rowKey: this.rowData[this.rowKeyFieldName],
internalRadioSelectedRowKey:
this.internalRadioSelectedRowKey,
}
return <BodyRadioContent {...radioProps} />
}
return null
},
// get cell span
getCellSpan() {
const { cellSpanOption, rowData, column, rowIndex } = this
let rowspan = 1
let colspan = 1
if (cellSpanOption) {
const { bodyCellSpan } = cellSpanOption
if (typeof bodyCellSpan === 'function') {
const result = bodyCellSpan({
row: rowData,
column,
rowIndex,
})
if (typeof result === 'object') {
rowspan = result.rowspan
colspan = result.colspan
}
}
}
return { rowspan, colspan }
},
// cell click
cellClick(e, fn) {
fn && fn(e)
const { column, expandOption, rowData } = this
this.eventCenter.emit(GLOBAL_EVENT.BODY_CELL_CLICK, {
event: e,
rowData,
column,
})
if (column.type !== COLUMN_TYPES.EXPAND) {
return false
}
if (expandOption) {
const eventTargetName = e.target.nodeName
const trigger = expandOption.trigger
// expand row by click icon
if (!trigger || trigger === EXPAND_TRIGGER_TYPES.ICON) {
if (eventTargetName !== 'TD') {
e.stopPropagation()
// this.$emit(EMIT_EVENTS.EXPAND_ROW_CHANGE)
this.$emit('expand-row-change')
}
} else if (trigger === EXPAND_TRIGGER_TYPES.CELL) {
// expand row by click cell(td)
e.stopPropagation()
// this.$emit(EMIT_EVENTS.EXPAND_ROW_CHANGE)
this.$emit('expand-row-change')
}
}
},
// dblclick
cellDblclick(e, fn) {
fn && fn(e)
const { column, rowData } = this
this.eventCenter.emit(GLOBAL_EVENT.BODY_CELL_DOUBLE_CLICK,
{
event: e,
rowData,
column,
},
)
},
// contextmenu
cellContextmenu(e, fn) {
fn && fn(e)
const { column, rowData } = this
this.eventCenter.emit(GLOBAL_EVENT.BODY_CELL_CONTEXTMENU,
{
event: e,
rowData,
column,
},
)
},
// mouseenter
cellMouseenter(e, fn) {
fn && fn(e)
},
// mouseleave
cellMouseleave(e, fn) {
fn && fn(e)
},
// mousemove
cellMousemove(e, fn) {
fn && fn(e)
const { column, rowData } = this
this.eventCenter.emit(GLOBAL_EVENT.BODY_CELL_MOUSEMOVE,
{
event: e,
rowData,
column,
},
)
},
// mouseover
cellMouseover(e, fn) {
fn && fn(e)
const { column, rowData } = this
this.eventCenter.emit(GLOBAL_EVENT.BODY_CELL_MOUSEOVER,
{
event: e,
rowData,
column,
},
)
},
// mousedown
cellMousedown(e, fn) {
fn && fn(e)
const { column, rowData } = this
this.eventCenter.emit(GLOBAL_EVENT.BODY_CELL_MOUSEDOWN,
{
event: e,
rowData,
column,
},
)
},
// mouseup
cellMouseup(e, fn) {
fn && fn(e)
const { column, rowData } = this
this.eventCenter.emit(GLOBAL_EVENT.BODY_CELL_MOUSEUP, {
event: e,
rowData,
column,
})
},
},
render(h) {
const {
column,
cellClick,
rowData,
isExpandRow,
expandOption,
expandedRowkeys,
rowKeyFieldName,
eventCustomOption,
rowIndex,
} = this
// expand icon props
const expandIconProps = {
rowData,
column,
expandOption,
expandedRowkeys,
rowKeyFieldName,
cellClick,
}
const { rowspan, colspan } = this.getCellSpan()
if (!rowspan || !colspan) {
return null
}
// custom on cell event
let customEvents = {}
if (eventCustomOption) {
const { bodyCellEvents } = eventCustomOption
customEvents = bodyCellEvents
? bodyCellEvents({ row: rowData, column, rowIndex })
: {}
}
const {
click,
dblclick,
contextmenu,
mouseenter,
mouseleave,
mousemove,
mouseover,
mousedown,
mouseup,
} = customEvents
// td props
const tdProps = {
class: this.bodyTdClass(),
style: this.bodyTdStyle(),
rowspan,
colspan,
[COMPS_CUSTOM_ATTRS.BODY_COLUMN_KEY]: column.key,
onClick: (e) => {
this.cellClick(e, click)
},
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 (
<td {...tdProps}>
{/* expadn tr icon */}
{isExpandRow && <ExpandTrIcon {...expandIconProps} />}
{/* checkbox content */}
{this.getCheckboxContent()}
{/* radio content */}
{this.getRadioContent()}
{/* other cell content */}
{this.getRenderContent(h)}
</td>
)
},
}