vue3 tableselect 指令

<template>
  <div>
    <table border="1" v-table-select="state">
      <tr v-for="(rowData,rowIndex) of state.tableData" :key="rowIndex" >
        <th>{{rowData.row}}</th>
        <td v-for="(columnData,columnIndex) of rowData.data"
         :key="columnData.id" :class="{'selected':columnData.selected}"
         :data-row="rowIndex"
         :data-column="columnIndex"
         >
        {{columnData.content}}
        </td>

      </tr>

    </table>
  </div>
</template>

<script setup>
import {reactive,watch} from 'vue'
import vTableSelect from './directives/tableSelect.js'
const data = [
  {
    row: "X",
    data: [
      { id: 1, content: 1,selected:false },
      { id: 2, content: 2 },
      { id: 3, content: 3 },
      { id: 4, content: 4 },
    ],
  },
  {
    row: "Y",
    data: [
      { id: 5, content: 5 },
      { id: 6, content: 6 },
      { id: 7, content: 7 },
      { id: 8, content: 8 },
    ],
  },
  {
    row: "Z",
    data: [
      { id: 9, content: 9 },
      { id: 10, content: 10 },
      { id: 11, content: 11 },
      { id: 12, content: 12 },
    ],
  },
];
const state=reactive({
  tableData:data,
  selectedData:null, //选择的数据
  selectedAreaData:[]  //拖动的数据
})
watch(()=>state.selectedData,(newValue)=>{
  console.log('selectedData',newValue);
} );

watch(()=>state.selectedAreaData,(newValue)=>{
  console.log('selectedAreaData',newValue);
} );
</script>

<style>
body {
  user-select: none;
  margin: 0;
  padding: 0;
}
table {
  width: 800px;
  border-collapse: collapse;
  table-layout: fixed;
  text-align: center;
}
td {
  height: 33px;
  cursor: pointer;
  box-sizing: border-box;
  position: relative;
}
td.selected {
  background-color: orange;
}
</style>
let isMouseOver = false;
const vTableSelect = {
    mounted(el, bindings) {
        // console.log(el,bindings.value);
        vTableSelect.el = el
        bindEvent(bindings.value)
    }
}

function bindEvent(state) {
    const { el } = vTableSelect
    el.addEventListener('click', handleTDClick.bind(el, state), false) //
    el.addEventListener('dblclick', handleTDDblClick.bind(el, state), false) //双击
    el.addEventListener('mousedown', handleTDMouseDown.bind(el, state), false)
    window.addEventListener('click', handleWindowClick.bind(el, state), false)//取消
}

function handleTDClick(...[state, e]) {
    if (!isMouseOver) {
        e.stopPropagation()
        const { target } = e
        restoreSelectedData(state)
        if (target.tagName === 'TD') {
            const { row, column } = getRowAndColumn(target)
            const selectedTD = getTargetData(state.tableData, row, column)
            if (state.selectedData !== selectedTD) {
                state.selectedData = selectedTD
                state.selectedData.selected = true


            }
        }
    }
}

function handleTDDblClick(...[state, e]) {
    e.stopPropagation()
    const { target } = e
    restoreSelectedData(state)
    if (target.tagName === 'TD') {
        const { row, column } = getRowAndColumn(target)
        state.selectedData = getTargetData(state.tableData, row, column)
        this.oInput = createInput(target, state.tableData, row, column)
    }

}
//选中区域
function handleTDMouseDown(...[state, e]) {
    e.stopPropagation()
    isMouseOver = false
    const { target } = e
    // const {el}=vTableSelect
    const _handleTDMouseOver = handleTDMouseOver.bind(this, state)
    restoreSelectedData(state)


    document.addEventListener('mouseover', _handleTDMouseOver, false)
    document.addEventListener('mouseup', handleTDMouseUp, false)
    if (target.tagName === 'TD') {
        const { row, column } = getRowAndColumn(target)
        this.startRow = row;
        this.startColumn = column
    }
    function handleTDMouseUp() {
        document.removeEventListener('mouseover', _handleTDMouseOver, false)
        document.removeEventListener('mouseup', handleTDMouseUp, false)
        setTimeout(() => {
            isMouseOver = false
        })
    }
}

function handleTDMouseOver(...[state, e]) {
    e.stopPropagation()
    isMouseOver = true
    const { target } = e
    if (target.tagName === 'TD') {
        const { row, column } = getRowAndColumn(target)
        this.endRow = row;
        this.endColumn = column
        state.selectedAreaData = getSelectedAreaData(
            state.tableData,
            this.startRow,
            this.startColumn,
            this.endRow,
            this.endColumn

        )
    }
}

function handleWindowClick(...[state, e]) {
    this.oInput && (state.selectedData.content = this.oInput.value)
    !isMouseOver && restoreSelectedData(state)
}

function createInput(target, data, row, column) {
    const { content } = getTargetData(data, row, column)
    const oInput = document.createElement('input')
    oInput.type = 'text'
    oInput.value = content
    oInput.onfocus = oInput.select;
    target.appendChild(oInput)
    oInput.focus()
    oInput.style.cssText = `
    position:absolute;
    top:0;
    left:0;
    width:100%;
    height:100%;
    box-sizing:border-box;
    text-align:center;
    font-size:16px;
    `
    return oInput
}

function getSelectedAreaData(data, startRow, startColumn, endRow, endColumn) {
    const selectedAreaData = []
    console.log(data, startRow, startColumn, endRow, endColumn);
    if (startRow <= endRow) {
        for (let i = startRow; i <= endRow; i++) {
            setSelectedAreaData(data[i].data, startColumn, endColumn)
        }
    } else {
        for (let i = startRow; i >= endRow; i--) {
            setSelectedAreaData(data[i].data, startColumn, endColumn)
        }
    }
    function setSelectedAreaData(rowData, startColumn, endColumn) {
        if (startColumn <= endColumn) {
            for (let j = startColumn; j <= endColumn; j++) {
                pushColumnData(rowData[j])
            }
        } else {
            for (let j = startColumn; j >= endColumn; j--) {
                pushColumnData(rowData[j])
            }
        }
        function pushColumnData(columnData) {
            columnData.selected = true;
            selectedAreaData.push(columnData)
        }

    }
    return selectedAreaData
}


//恢复
function restoreSelectedData(state) {
    const { el } = vTableSelect
    if (state.selectedData) {
        state.selectedData.selected = false
    }
    if (state.selectedAreaData.length) {
        state.selectedAreaData.forEach(data => {
            data.selected = false
        })
        state.selectedAreaData = []
    }
    if (el.oInput) {
        el.oInput.remove()
        el.oInput = null
    }
}

//获取行列
function getRowAndColumn(target) {
    const { dataset } = target
    return {
        row: Number(dataset.row),
        column: Number(dataset.column),
    }
}
function getTargetData(data, row, column) {

    let target = null;
    data.forEach((_row, _rowIndex) => {
        if (row === _rowIndex) {
            _row.data.forEach((_column, _columnIndex) => {
                if (column === _columnIndex) {
                    target = _column
                }
            })
        }
    })
    return target
}
export default vTableSelect
posted @   7c89  阅读(191)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
历史上的今天:
2021-11-27 千分符分隔金额 number.toLocaleString("en-US")
点击右上角即可分享
微信分享提示