UNPKG

vxe-table-select-area

Version:

一个基于 vxe-table 的可区域选中复制、粘贴的组件

541 lines (499 loc) 14.7 kB
import { clsName, getFixedTotalWidthByColumnKey, getCaretPosition, setCaretPosition } from '../../select-area/common' import { isEmptyValue } from '../../select-area/utils/index' import { autoResize } from '../../select-area/utils/auto-resize' import { EMIT_EVENTS, INSTANCE_METHODS, HOOKS_NAME } from '../../select-area/common/constant' import { focus } from '../../select-area/directives/focus' import { debounce } from 'lodash' export default { name: 'VxeSelectAreaEditor', directives: { focus: focus }, props: { parentRendered: { type: Boolean, required: true }, hooks: { type: Object, required: true }, // start input value every time // inputStartValue: { // type: [String, Number], // required: true, // }, rowKeyFieldName: { type: String, default: null }, // table data tableData: { type: Array, required: true }, colgroups: { type: Array, required: true }, // cell selection option cellSelectionData: { type: Object, required: true }, // editing cell // editingCell: { // type: Object, // required: true, // }, // is editing cell // isCellEditing: { // type: Boolean, // required: true, // }, // has horizontal scroll bar // hasXScrollBar: { // type: Boolean, // required: true, // }, // has vertical scroll bar // hasYScrollBar: { // type: Boolean, // required: true, // }, // has right fixed column // hasRightFixedColumn: { // type: Boolean, // required: true, // }, scrollBarWidth: { type: Number, required: true } }, data () { return { textareaInputRef: 'textareaInputRef', // raw cell value rawCellValue: '', // display textarea displayTextarea: false, // virtual scroll overflowViewport overflowViewport: false, // textarea element rect textareaRect: { left: 0, top: 0 }, // table element tableEl: null, // cell element cellEl: null, // auto resize autoResize: null, // is edit cell focus isEditCellFocus: false } }, computed: { // current column currentColumn () { let result = null const { colgroups, cellSelectionData } = this const { currentCell } = cellSelectionData if ( !isEmptyValue(currentCell.rowKey) && !isEmptyValue(currentCell.colKey) ) { result = colgroups.find((x) => x.key === currentCell.colKey) } return result }, // container class containerClass () { let result = null const { displayTextarea, overflowViewport } = this // console.log(1300000, this) result = { [clsName('edit-input-container')]: true, [clsName('edit-input-container-show')]: displayTextarea && !overflowViewport } return result }, // container style containerStyle () { let result = {} const { displayTextarea, overflowViewport, textareaRect, currentColumn: column } = this const { top, left } = textareaRect // console.log(152, displayTextarea, overflowViewport, textareaRect) if (displayTextarea && !overflowViewport) { result = { top: top + 'px', left: left + 'px', height: null, // because @ve-fixed-body-cell-index: 10; 'z-index': column.fixed ? 10 : 0, opacity: 1 } } else { result = { top: top + 'px', left: left + 'px', height: '1px', 'z-index': -1, opacity: 0 } } return result }, // textarea class textareaClass () { let result = null result = { [clsName('edit-input')]: true } return result } }, watch: { parentRendered: { handler: function (val) { if (val) { // fixed #471 this.setTableEl() // console.log('hook>>>', this.hooks) // add table container scroll hook this.hooks.addHook( HOOKS_NAME.TABLE_CONTAINER_SCROLL, () => { if (this.displayTextarea) { if (!this.cellEl) { this.setCellEl() } } this.debounceSetCellEl() this.setTextareaPosition() // console.log('1111111111111111111') this.debounceSetTextareaPosition() } ) // add table size change hook this.hooks.addHook(HOOKS_NAME.TABLE_SIZE_CHANGE, () => { // console.log('22222222222222222') this.setTextareaPosition() }) } }, immediate: true }, // cell selection key data 'cellSelectionData.currentCell': { handler: function (val) { this.isEditCellFocus = false const { rowKey, colKey } = val if (!isEmptyValue(rowKey) && !isEmptyValue(colKey)) { this.setCellEl() // wait for selection cell rendered this.$nextTick(() => { // console.log('333333333333333') this.setTextareaPosition() setTimeout(() => { this.isEditCellFocus = true }) }) } }, deep: true, immediate: true }, // watch normal end cell 'cellSelectionData.normalEndCell': { handler: function (val) { /* trigger editor(textarea) element select 解决通过点击的区域选择,无法复制的问题 */ if (!isEmptyValue(val.colKey)) { this[INSTANCE_METHODS.TEXTAREA_SELECT]() } }, deep: true, immediate: true } // is editing cell // isCellEditing: { // handler: function (val) { // if (val) { // this.showTextarea(); // } else { // this.hideTextarea(); // } // }, // deep: true, // immediate: true, // }, // inputStartValue: { // handler: function () { // this.setRawCellValue(); // }, // immediate: true, // }, }, mounted () { this.autoResize = autoResize() // this.$nextTick(() => { // var _this = this.$parent.$parent // // console.log(2755555,_this) // _this.$on([EMIT_EVENTS.EDIT_INPUT_COPY], (e) => { // _this.editorCopy(e); // },) // }) }, methods: { // set table element setTableEl () { this.$nextTick(() => { const tableEl = this.$parent.$el this.tableEl = tableEl }) }, // set cell element setCellEl () { const { cellSelectionData, tableEl } = this // console.log(28777777, this) const { rowKey, colKey } = cellSelectionData.currentCell if (tableEl) { const cellEl = tableEl.querySelector( `tbody tr[rowid="${rowKey}"] td[colid="${colKey}"]` ) if (cellEl) { this.cellEl = cellEl this.overflowViewport = false } } }, // set textarea position setTextareaPosition () { const { hasXScrollBar, hasYScrollBar, scrollBarWidth, colgroups, hasRightFixedColumn, currentColumn: column, cellEl, tableEl } = this // console.log(31444, cellEl, tableEl) if (cellEl && tableEl) { const { left: tableLeft, top: tableTop, right: tableRight, bottom: tableBottom } = tableEl.getBoundingClientRect() const { left: cellLeft, top: cellTop, height: cellHeight, width: cellWidth, right: cellRight, bottom: cellBottom } = cellEl.getBoundingClientRect() // console.log('444444444') if (cellHeight && cellWidth) { // console.log('55555555', cellHeight, cellWidth) let maxHeight = cellHeight + tableBottom - cellBottom let maxWidth = cellWidth + tableRight - cellRight // has horizontal scroll bar if (hasXScrollBar) { maxHeight -= scrollBarWidth } // has vertical scroll bar if (hasYScrollBar) { maxWidth -= scrollBarWidth } /* If the right fixed column is included, the max width of the textarea needs to be subtracted from the sum of the right fixed columns 如果包含右固定列,编辑框最大宽度需要去减去右固定列之和的宽度 */ if (hasRightFixedColumn) { if (column && !column.fixed) { const rightFixedTotalWidth = getFixedTotalWidthByColumnKey({ colgroups, colKey: column.key, fixed: 'right' }) if (rightFixedTotalWidth) { maxWidth -= rightFixedTotalWidth } } } this.autoResize.init( this.$refs[this.textareaInputRef], { minHeight: Math.min(cellHeight, maxHeight), maxHeight: maxHeight, // TEXTAREA should never be higher than visible part of the viewport (should not cover the scrollbar) minWidth: Math.min(cellWidth, maxWidth), maxWidth: maxWidth // TEXTAREA should never be wider than visible part of the viewport (should not cover the scrollbar) }, true // observe textarea change\cut\paste etc. ) this.textareaRect = { left: cellLeft - tableLeft, top: cellTop - tableTop } } else { /* 存在以下可能: 1、虚拟滚动超出viewport 2、单元格被删除(通过右键菜单等方式) */ // fixed #477 this.textareaRect = { left: 0, top: 0 } this.cellEl = null this.overflowViewport = true } } }, // show textarea showTextarea () { this.setRawCellValue() this.displayTextarea = true }, // hide textarea hideTextarea () { this.displayTextarea = false this.textareaUnObserve() }, // textarea unObserve textareaUnObserve () { if (this.autoResize) { this.autoResize.unObserve() } }, // set raw cell value setRawCellValue () { this.rawCellValue = this.inputStartValue }, // textarea value change textareaValueChange (val) { this.$emit(EMIT_EVENTS.EDIT_INPUT_VALUE_CHANGE, val) }, // textarea select [INSTANCE_METHODS.TEXTAREA_SELECT] () { const textareaInputEl = this.$refs[this.textareaInputRef] if (textareaInputEl) { textareaInputEl.select() } }, // textarea add new line [INSTANCE_METHODS.TEXTAREA_ADD_NEW_LINE] () { const { isCellEditing, editingCell } = this if (isCellEditing) { const textareaInputEl = this.$refs[this.textareaInputRef] const caretPosition = getCaretPosition(textareaInputEl) let value = editingCell.row[editingCell.colKey] // solve error of number slice method value += '' const newValue = `${value.slice( 0, caretPosition )}\n${value.slice(caretPosition)}` // 直接更新 textarea 值 textareaInputEl.value = newValue // 手动赋值不会触发textarea 文本变化事件,需要手动更新 editingCell 值 this.textareaValueChange(newValue) setCaretPosition(textareaInputEl, caretPosition + 1) } } }, created () { // debounce set textarea position this.debounceSetTextareaPosition = debounce( this.setTextareaPosition, 210 ) // debounce set cell el this.debounceSetCellEl = debounce(() => { if (this.displayTextarea) { if (!this.cellEl) { this.setCellEl() } } }, 200) }, render: function render (h) { const _e = this._e const { containerClass, containerStyle, textareaClass, rawCellValue, isCellEditing, isEditCellFocus } = this const containerProps = { style: containerStyle, class: containerClass } // console.log(5011111, this, containerProps) const textareaProps = { ref: this.textareaInputRef, class: textareaClass, directives: [ { name: 'focus', value: { focus: isEditCellFocus } } ], domProps: { value: rawCellValue }, attrs: { tabindex: -1 }, on: { // input: (e) => { // if (isCellEditing) { // this.textareaValueChange(e.target.value); // this.rawCellValue = e.target.value; // } // }, // click: () => { // // console.log(525) // this.$emit(EMIT_EVENTS.EDIT_INPUT_CLICK); // }, // copy: function copy(evnt) { // // console.log(52999999, evnt) // }, copy: (e) => { // console.log(52999999, e) this.$emit(EMIT_EVENTS.EDIT_INPUT_COPY, e) }, paste: (e) => { // console.log(53333) this.$emit(EMIT_EVENTS.EDIT_INPUT_PASTE, e) }, cut: (e) => { this.$emit(EMIT_EVENTS.EDIT_INPUT_CUT, e) } } } // console.log('544textareaProps',textareaProps) return h('div', containerProps, [h('textarea', { ...textareaProps })] ) } }