univer实现字段拖拽

好久不写文章了,这篇是写给 Univer 群里的友友们看的。

1_1711617301 -big-original.gif

1. 获取canvas滚动偏移量

监听命令

通过监听命令api 可以知道canvas内滚动 会触发 命令sheet.operation.set-scroll

单独输出 命令sheet.operation.set-scroll 的params 可以获取到数据

代码:

      this.univerAPI.onBeforeCommandExecute((command) => {
        const { id, type, params } = command
          if (id === 'sheet.operation.set-scroll') {
            console.log('params', params)
            const { offsetX, offsetY, sheetId, sheetViewStartColumn, sheetViewStartRow, unitId } =
              params
            this.$emit('setScroll', {
              offsetX,
              offsetY,
              sheetId,
              sheetViewStartColumn,
              sheetViewStartRow,
              unitId
            })
          }
      })

image.png

{ offsetX, offsetY, sheetId, sheetViewStartColumn, sheetViewStartRow, unitId }

通过多次滚动,对数据的分析可以得出

这些数据为滚动后,左上角第一个格子的相关参数。

sheetViewStartColumn:左上角可视行的行数

sheetViewStartRow:左上角可视列的列数

offsetX:左上角可视第一个格被滚动的距离X

offsetY:左上角可视第一个格被滚动的距离Y

image.png

例如上图滚动后,则上面数据都是相对于B3格子。sheetViewStartColumn为B3的行,sheetViewStartRow为B3的列,offsetX为B3的x被遮住的部分,offsetY为B3的y被遮住的部分

同时,知道了sheetViewStartColumn,和sheetViewStartRow,就可以知道前面还有多少行多少列。就可以计算出前面所有行和列的距离。

2.获取鼠标位置

image.png

        <button @dragend="inserDataWithDrag" draggable="true">
          客户名称(可拖拽)
        </button>

下面就是全部核心代码 写满注释了。

前面几个常数在我的demo页面内,是上面图片的这样一个关系,可以根据自己的调整

/**
 * @description 拖拽字段到单元格内
 * @param {*} event
 */
const inserDataWithDrag = (event) => {
  const canvasLeft = 45 // 行数字宽度
  const pageLeft = 250 // 左侧字段区域宽带
  const canvasTop = 20 // 列字母高度
  const canvasTool = 32 // 工具条高度
  const canvasInput = 28 // 输入框高度
  const canvasCellLeft = canvasLeft + pageLeft
  const canvasCellTop = canvasTop + canvasTool + canvasInput
  const mouseX = event.clientX - canvasCellLeft // 鼠标在Excel表格内的相对位置
  const mouseY = event.clientY - canvasCellTop // 鼠标在Excel表格内的相对位置
  if (mouseX < 0 || mouseY < 0) return

  const allData = univerRef.value.getData() // 获取所有的sheet表 -
  const nowSheet = allData.sheets['sheet-01'] // 我暂时手动直接拿第一个表的内容了,需要的自己手动更换
  // Object.entries(allSheets).forEach(([key, value])
  const defaultColumnWidth = nowSheet.defaultColumnWidth // 默认行宽度
  const defaultRowHeight = nowSheet.defaultRowHeight // 默认列高度
  const rowData = nowSheet.rowData // 手动设置过列宽度的行数据
  const columnData = nowSheet.columnData // 手动设置过行高度的列数据
  let canvasScrollXHide = offsetX.value // 操作区间滚动后的偏移量x 滚动后 但滚动距离不超过1列 
  let canvasScrollYHide = offsetY.value // 操作区间滚动后的偏移量y 滚动后 但滚动距离不超过1行
  if (sheetViewStartColumn.value) { // 当页面滚动超过一行 
    const columnLen = new Array(sheetViewStartColumn.value).fill(null)
    const canvasScrollX = columnLen.reduce((acc, currentValue, currentIndex) => {
      if (columnData[currentIndex] && columnData[currentIndex].w) {
        // 如果手动设置过列宽
        return acc + columnData[currentIndex].w
      } else {
        return acc + defaultColumnWidth
      }
    }, 0)
    canvasScrollXHide += canvasScrollX // 拿到最终操作区间滚动后的全部偏移量x
  }
  if (sheetViewStartRow.value) { // 当页面滚动超过一列
    const rowLen = new Array(sheetViewStartRow.value).fill(null)
    const canvasScrollY = rowLen.reduce((acc, currentValue, currentIndex) => {
      if (rowData[currentIndex] && rowData[currentIndex].h) {
        // 如果手动设置过行高
        return acc + rowData[currentIndex].h
      } else {
        return acc + defaultRowHeight
      }
    }, 0)
    canvasScrollYHide += canvasScrollY // 拿到最终操作区间滚动后的全部偏移量y
  }

  // 拿到偏移量 就可以知道当前鼠标对于canvas的起点,0,0的位置
  const moseInCanvasX = canvasScrollXHide + mouseX // 得到鼠标相对于0,0位置的偏移量
  const moseInCanvasY = canvasScrollYHide + mouseY // 得到鼠标相对于0,0位置的偏移量

  console.log('鼠标位置:', mouseX, mouseY)
  console.log('鼠标在canvas内相对于起始0,0的位置:', moseInCanvasX, moseInCanvasY)

  // 计算鼠标在canvas内相对于起始0,0的位置在哪个单元格 获取行
  const getDragColumn = (moseInCanvasX, defaultColumnWidth) => {
    let count = 0
    let currentValue = 0

    while (currentValue <= moseInCanvasX) {
      if (columnData[count] && columnData[count].w) {
        // 如果下一列有设置过宽度 需要加上设置过的宽度
        currentValue += columnData[count].w
      } else {
        currentValue += defaultColumnWidth
      }
      count++
    }
    return count - 1 // 减去最后一次操作
  }

  // 计算鼠标在canvas内相对于起始0,0的位置在哪个单元格 获取列
  const getDragRow = (moseInCanvasY, defaultRowHeight) => {
    let count = 0
    let currentValue = 0

    while (currentValue <= moseInCanvasY) {
      if (rowData[count] && rowData[count].h) {
        // 如果下一行有设置过高度 需要加上设置过的高度
        currentValue += rowData[count].h
      } else {
        currentValue += defaultRowHeight
      }
      count++
    }
    return count - 1 // 减去最后一次操作
  }

  const column = getDragColumn(+moseInCanvasX, +defaultColumnWidth)
  const row = getDragRow(+moseInCanvasY, +defaultRowHeight)
  console.log('鼠标所在的位置列:', column)
  console.log('鼠标所在的位置行:', row)
}

已得到鼠标所在行列,赋值即可

在上面代码最后添加如下代码

  ...
  ...
  ...
  ...
  const column = getDragColumn(+moseInCanvasX, +defaultColumnWidth)
  const row = getDragRow(+moseInCanvasY, +defaultRowHeight)
  console.log('鼠标所在的位置列:', column)
  console.log('鼠标所在的位置行:', row)

  univerRef.value.setData({
    pos: [row, column, 1, 1],
    value: '{客户名称}' // 根据自身业务去赋值即可
  })

设置范围数据

上面univerRef.value.setData 是我封装过的赋值方法,本体也是官方api提供的

    setData({ pos, value }) {
      const [col, row, colNum, rowNum] = pos

      const activeSheet = this.univerAPI.getActiveWorkbook().getActiveSheet()

      const range = activeSheet.getRange(col, row, colNum, rowNum)
      range?.setValue(value)
    }

彩蛋:

最后如果想设置拖拽数据后单元格显示高亮:

image.png

监听命令

同样,通过监听命令api 可以知道canvas内被点击,会触发 命令sheet.operation.set-selections

参照最开的的调试输出params,最终结果是可以通过调用该命令高亮格子

      this.univerAPI.executeCommand('sheet.operation.set-selections', {
        selections: [{ range: { startRow: row, startColumn: column, endRow: row, endColumn: column } }]
      })

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,277评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,689评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,624评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,356评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,402评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,292评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,135评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,992评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,429评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,636评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,785评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,492评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,092评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,723评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,858评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,891评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,713评论 2 354

推荐阅读更多精彩内容