wangEditor icon indicating copy to clipboard operation
wangEditor copied to clipboard

自定义table表格插件回显到编辑器时执行了编辑器自身的表格parseElemHtml代码

Open leidao opened this issue 2 years ago • 7 comments

问题描述

根据html回显到编辑器时没有执行我的parseElemHtml代码而是走了编辑器自身的表格parseElemHtml代码,该怎么处理? 源码中的parse-elem-html.ts代码中并没有对table、tr、th、td等元素进行精确匹配。

wangEditor 版本

"@wangeditor/editor": "^5.0.1", "@wangeditor/editor-for-react": "^1.0.2",

是否查阅了文档 ?

最小成本的复现步骤

(请告诉我们,如何最快的复现该问题?)

  • 步骤一
render-elem.tsx 
/*
 * @Description:
 * @Author: ldx
 * @Date: 2022-05-13 15:22:43
 * @LastEditors: ldx
 * @LastEditTime: 2022-05-17 17:40:31
 */
import { DomEditor, IDomEditor, SlateElement } from '@wangeditor/editor'
import { Editor, Path, Point, Range } from 'slate'
import { h, VNode } from 'snabbdom'

import { editorData, genContainerId, getContainerElem } from '../utils'
import { TableCellElement, TableElement, TableRowElement } from './custom-types'
const columns = [
  {
    title: 'Name',
    dataIndex: 'name',
    key: 'name'
  },
  {
    title: 'Age',
    dataIndex: 'age',
    key: 'age'
  },
  {
    title: 'Address',
    dataIndex: 'address',
    key: 'address'
  }
]
const dataSource = [
  {
    key: '1',
    name: 'John Brown',
    age: 32,
    address: 'New York No. 1 Lake Park'
  },
  {
    key: '2',
    name: 'Jim Green',
    age: 42,
    address: 'London No. 1 Lake Park'
  },
  {
    key: '3',
    name: 'Joe Black',
    age: 32,
    address: 'Sidney No. 1 Lake Park'
  }
]

function renderTableHead(columns: any[] = []): VNode {
  return h(
    'tr',
    {},
    columns.map((column) => h('th', {}, column.title))
  )
}
function renderTableEmpty(columns: any[] = []): VNode {
  return h('tr', {}, [
    h(
      'td',
      {
        colSpan: columns.length
      },
      [
        h(
          'div',
          {
            style: {
              display: 'flex',
              flexDirection: 'column',
              justifyContent: 'center',
              alignItems: 'center',
              color: '#0f0f0f4d'
            }
          },
          [
            h('i', {
              className: 'iconfont icon-zanwushuju1',
              style: { fontSize: '42px' }
            }),
            h('span', {}, '暂无数据')
          ]
        )
      ]
    )
  ])
}
function renderTableCell(dataSource: any[] = [], columns: any[] = []): VNode[] {
  if (dataSource.length === 0) {
    return [renderTableEmpty(columns)]
  } else {
    return dataSource.map((data) =>
      h(
        'tr',
        {},
        columns.map((column) =>
          h('td', {}, [h('span', {}, data[column.dataIndex])])
        )
      )
    )
  }
}
function renderTable(
  elemNode: SlateElement,
  children: VNode[] | null,
  editor: IDomEditor
): VNode {
  const { fullWidth = false, id = '' } = elemNode as TableElement
  const { editable = false } = elemNode as TableElement
  // 当前节点是否选中
  const selected = DomEditor.isNodeSelected(editor, elemNode)
  const containerId = genContainerId(editor, elemNode)
  if (selected && getContainerElem(containerId)) {
    editor.emit('select:customElement', id)
  }
  console.log('editorData', editorData.get(id))

  let data = editorData.get(id) as any
  if (!data) {
    // 请求接口
    data = {
      columns,
      dataSource
    }
    editorData.set(id, data)
  }
  // 是否可编辑
  // editable = editable || getContentEditable(editor, elemNode)
  // 构建 table vnode
  const tableVnode = h(
    'table',
    {
      className: 'custom-table',
      contentEditable: editable,
      style: {
        width: fullWidth ? '100%' : '',
        margin: 'auto'
      }
    },
    [
      renderTableHead(data.columns),
      ...renderTableCell(data.dataSource, data.columns)
    ]
  )

  // 构建容器 vnode
  const containerVnode = h(
    'div',
    {
      props: {
        id: containerId,
        contentEditable: false // 不可编辑
      },
      style: {
        display: 'flex', // inline
        alignItems: 'center',
        justifyContent: 'center'
      }
    },
    [
      h(
        'span',
        {
          style: {
            width: fullWidth ? '100%' : '',
            display: 'block', // inline
            marginLeft: '3px',
            marginRight: '3px',
            border: selected // 选中/不选中,样式不一样
              ? '2px solid var(--w-e-textarea-selected-border-color)' // wangEditor 提供了 css var https://www.wangeditor.com/v5/theme.html
              : '2px solid transparent',
            borderRadius: '3px',
            padding: '3px 3px'
          }
        },
        [tableVnode]
      )
    ]
  )

  return containerVnode
}
const conf = {
  type: 'customTable', // 节点 type ,重要!!!
  renderElem: renderTable
}
export default conf

  • 步骤二

/*
 * @Description:
 * @Author: ldx
 * @Date: 2022-05-13 15:22:43
 * @LastEditors: ldx
 * @LastEditTime: 2022-05-17 18:16:42
 */
parse-elem-html.ts
import { IDomEditor, SlateDescendant, SlateElement } from '@wangeditor/editor'

import { TableElement } from './custom-types'

function parseTableHtml(
  elem: Element,
  children: SlateDescendant[],
  editor: IDomEditor
): SlateElement {
  const id = elem.getAttribute('data-id') || ''
  const fullWidth = elem.getAttribute('data-fullWidth') || false
  const editable = elem.getAttribute('data-editable') || false
  console.log('================================================')

  return {
    type: 'customTable',
    id,
    fullWidth,
    editable,
    children: [{ text: '' }]
  } as TableElement
}

const parseTableHtmlConf = {
  selector: 'table[data-w-e-type="customTable"]',
  parseElemHtml: parseTableHtml
}

export default parseTableHtmlConf

  • 步骤三
index.ts
/*
 * @Description:
 * @Author: ldx
 * @Date: 2022-05-17 11:17:36
 * @LastEditors: ldx
 * @LastEditTime: 2022-05-17 18:15:37
 */

import { IModuleConf } from '@wangeditor/core'

import { tableToHtmlConf } from './elem-to-html'
import parseTableHtmlConf from './parse-elem-html'
import withTable from './plugin'
import { preParseTableHtmlConf } from './pre-parse-html'
import renderElemConf from './render-elem'

export const module: Partial<IModuleConf> = {
  renderElems: [renderElemConf],
  elemsToHtml: [tableToHtmlConf],
}

export default module

  • 步骤四 const node = { type: 'customTable', id, api: '', editable: false, children: [{ text: '' }] // children: rows } editor?.insertNode(node)

leidao avatar May 17 '22 10:05 leidao

你一次性写的代码太多了。

你就把 renderTable 写的足够简单(先不管功能,先做测试),最好 5 行代码搞定,你看是否执行这个函数?

只管 render table ,其他的都不管,调试通这个再说。

二次开发嘛,要一步一步来,调通一步就往前走一步。

wangfupeng1988 avatar May 18 '22 00:05 wangfupeng1988

我进行了一个足够简单的demo测试,还是复现了这个问题。我认为是源码中表格的pre-parse-html文件中对table、tr、th、td等元素没有进行精确匹配导致的。

leidao avatar May 18 '22 01:05 leidao

render-elem.tsx
import { h, VNode } from 'snabbdom'

function renderTable(): VNode {
  // 构建 table vnode
  const tableVnode = h(
    'table',
    {
      className: 'custom-table'
    },
    [h('tr', {}, [h('th', {}, 'aaaa')]), h('tr', {}, [h('td', {}, 'bbbb')])]
  )

  return tableVnode
}
const conf = {
  type: 'customTable', // 节点 type ,重要!!!
  renderElem: renderTable
}
export default conf
parse-elem-html.ts
/*
 * @Description:
 * @Author: ldx
 * @Date: 2022-05-13 15:22:43
 * @LastEditors: ldx
 * @LastEditTime: 2022-05-18 09:09:02
 */

import { SlateElement } from '@wangeditor/editor'

import { TableElement } from './custom-types'

function parseTableHtml(): SlateElement {
  return {
    type: 'customTable',
    children: [{ text: '' }]
  } as TableElement
}

const parseTableHtmlConf = {
  selector: 'table[data-w-e-type="customTable"]',
  parseElemHtml: parseTableHtml
}

export default parseTableHtmlConf

index.ts
/*
 * @Description:
 * @Author: ldx
 * @Date: 2022-05-17 11:17:36
 * @LastEditors: ldx
 * @LastEditTime: 2022-05-18 09:12:38
 */

import { IModuleConf } from '@wangeditor/core'

import { tableToHtmlConf } from './elem-to-html'
import parseTableHtmlConf from './parse-elem-html'

export const module: Partial<IModuleConf> = {
  elemsToHtml: [tableToHtmlConf],
  parseElemsHtml: [parseTableHtmlConf]
}

export default module

 // 使用
  const node = {
            type: 'customTable',
            children: [{ text: '' }]
          }
          editor?.insertNode(node)

leidao avatar May 18 '22 01:05 leidao

自定义table表格插件回显到编辑器时执行了编辑器自身的表格parseElemHtml代码

我认为是源码中表格的pre-parse-html文件中对table、tr、th、td等元素没有进行精确匹配导致的。

看当前现象,应该是你说的原因。我近期去排查一下。

wangfupeng1988 avatar May 18 '22 02:05 wangfupeng1988

近期我的项目需要上线,请问您什么时候修复这个问题。如果近期没有修复计划,我这边需要调整该功能。

---- 回复的原邮件 ---- | 发件人 | @.> | | 日期 | 2022年05月18日 10:20 | | 收件人 | @.> | | 抄送至 | @.@.> | | 主题 | Re: [wangeditor-team/wangEditor] 自定义table表格插件回显到编辑器时执行了编辑器自身的表格parseElemHtml代码 (Issue #4220) |

自定义table表格插件回显到编辑器时执行了编辑器自身的表格parseElemHtml代码

我认为是源码中表格的pre-parse-html文件中对table、tr、th、td等元素没有进行精确匹配导致的。

看当前现象,应该是你说的原因。我近期去排查一下。

— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you authored the thread.Message ID: @.***>

leidao avatar Jun 06 '22 13:06 leidao

近期不行,没时间改。

wangfupeng1988 avatar Jun 07 '22 00:06 wangfupeng1988

这个问题修复了么

rainbow520lxr avatar Aug 06 '22 08:08 rainbow520lxr

这个问题是否可以安排时间修复下,5月17号提出已经三个半月了,理解作者的开源不易,也经常刷到作者的抖音,明白开源是无私奉献,但确实这个问题影响到了项目开发,只能请求作者解决下了。

leidao avatar Sep 05 '22 06:09 leidao

已修复。更新最新版 @wangeditor/editor 即可。

只要你的 HTML 中带有 data-w-e-type 属性(如 <table data-w-e-type="xxx">...</table> ),就不会走默认的 parseElemHtml

你需要自定义自己的 toHtml 和 parseHtml 逻辑(匹配好 data-w-e-type 的值),具体可参考“下载附件”插件的源码 https://github.com/wangeditor-team/wangEditor-plugin-upload-attachment

wangfupeng1988 avatar Sep 14 '22 03:09 wangfupeng1988