Commit fc1aca98 by pangchong

feat: 富文本渲染

parent 58321083
......@@ -7,6 +7,7 @@ export {}
declare module 'vue' {
export interface GlobalComponents {
NButton: typeof import('naive-ui')['NButton']
NCard: typeof import('naive-ui')['NCard']
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
NInput: typeof import('naive-ui')['NInput']
......@@ -15,6 +16,8 @@ declare module 'vue' {
NLayoutFooter: typeof import('naive-ui')['NLayoutFooter']
NLayoutHeader: typeof import('naive-ui')['NLayoutHeader']
NMessageProvider: typeof import('naive-ui')['NMessageProvider']
NResult: typeof import('naive-ui')['NResult']
NSpace: typeof import('naive-ui')['NSpace']
NTree: typeof import('naive-ui')['NTree']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
......
......@@ -25,14 +25,14 @@ import { IDomEditor, IEditorConfig } from '@wangeditor/editor'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import { isNumber } from 'lodash'
import { Boot, IModuleConf } from '@wangeditor/editor'
import { renderElemConf } from '../functions/render-elem'
import { elemToHtmlConf } from '../functions/elem-to-html'
import { parseHtmlConf } from '../functions/parse-elem-html'
import renderElemConf from '../functions/render-elem'
import elemToHtmlConf from '../functions/elem-to-html'
import parseHtmlConf from '../functions/parse-elem-html'
const module: Partial<IModuleConf> = {
renderElems: [renderElemConf],
elemsToHtml: [elemToHtmlConf],
parseElemsHtml: [parseHtmlConf]
renderElems: renderElemConf,
elemsToHtml: elemToHtmlConf,
parseElemsHtml: parseHtmlConf
}
Boot.registerModule(module)
......
......@@ -4,58 +4,70 @@
<n-input v-model:value="searchKey" placeholder="搜索" />
</div>
<div class="flex-auto overflow-auto">
<n-tree v-model:expanded-keys="expandedKeys" ref="treeRef" :pattern="searchKey" :data="realComposableData" block-line />
<n-tree
v-model:expanded-keys="expandedKeys"
ref="treeRef"
:pattern="searchKey"
:data="realComposableData"
block-line
:node-props="nodeProps"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { searchKey, treeData, realComposableData, xmlDOM } from '../constants'
import FileXML from "@/assets/file/CES-QEC-V250-A.xml?raw"
import FileXML from '@/assets/file/CES-QEC-V250-A.xml?raw'
import type { TreeOption } from 'naive-ui'
import { nodeProps } from '../functions'
const xmlProcessing = useXMLProcessing()
const nodeSet = [
"JOBCARD",
"CEP",
"TITLE",
"WARNING",
"TFMATR",
"PRETOPIC",
"PARA",
"LIST1",
"L1ITEM",
"TABLE",
"TOPIC",
"SUBTASK",
"NOTE",
"LIST2",
"L2ITEM",
"LIST3",
"L3ITEM",
"LIST4",
"L4ITEM",
"STEP",
"RECORD-LINE"
'JOBCARD',
'CEP',
'TITLE',
'WARNING',
'TFMATR',
'PRETOPIC',
'PARA',
'LIST1',
'L1ITEM',
'TABLE',
'TOPIC',
'SUBTASK',
'NOTE',
'LIST2',
'L2ITEM',
'LIST3',
'L3ITEM',
'LIST4',
'L4ITEM',
'STEP',
'RECORD-LINE'
]
const treeRef = ref()
const expandedKeys = ref<string[]>([])
onMounted(function() {
onMounted(function () {
const res = xmlProcessing.processXML(FileXML, nodeSet)
treeData.value = res.treeData
xmlDOM.value = res.xmlDOM
console.log('all-node', xmlDOM.value)
})
function getAllKeys(item: TreeOption[]) {
return item.reduce(function(q, w) {
return item.reduce(function (q, w) {
q.push(w.key as string)
if (w.children) {
q.push(...getAllKeys(w.children))
}
return q;
return q
}, [] as string[])
}
watch(realComposableData, function() {
watch(
realComposableData,
function () {
expandedKeys.value = getAllKeys(realComposableData.value)
}, {flush: 'post', deep: true})
},
{ flush: 'post', deep: true }
)
</script>
import { SlateElement } from '@wangeditor/editor'
import { SlateElement, IModuleConf } from '@wangeditor/editor'
function JOBCARDToHtml(elem: SlateElement, childrenHtml: string): string {
return `<JOBCARD
data-w-e-type="JOBCARD"
const elemToHtml = (type: string) => {
return (elem: SlateElement, childrenHtml: string): string => {
const dataKey = (elem as any).dataKey || ''
return `<${type}
data-w-e-type="${type}"
data-w-e-is-void
data-w-e-is-inline
>${childrenHtml}</JOBCARD>`
data-key="${dataKey}"
>${childrenHtml}</${type}>`
}
}
export const elemToHtmlConf = {
type: 'JOBCARD',
elemToHtml: JOBCARDToHtml
const createElemConfig = (types: string | string[]) => {
const configs: IModuleConf['elemsToHtml'] = []
const typeArray = Array.isArray(types) ? types : [types]
typeArray.forEach((type) => {
configs.push({
type,
elemToHtml: elemToHtml(type)
})
})
return configs
}
export default createElemConfig([
'EOTK-HEADER',
'JOBCARD',
'CEP',
'TITLE',
'WARNING',
'TFMATR',
'PRETOPIC',
'PARA',
'LIST1',
'L1ITEM',
'TABLE',
'TOPIC',
'SUBTASK',
'NOTE',
'LIST2',
'L2ITEM',
'LIST3',
'L3ITEM',
'LIST4',
'L4ITEM',
'STEP',
'RECORD-LINE'
])
import { IDomEditor } from '@wangeditor/editor'
import { editorRef } from '../constants'
import { TreeOption } from 'naive-ui'
export const handleEditor = (editor: IDomEditor) => {
console.log(editor)
console.log(editor.getHtml())
// console.log(editor.getElemsByTypePrefix('JOBCARD'))
// console.log(editor)
// const headers = editor.getElemsByTypePrefix('header')
// const menu = headers.map((header: any) => {
// const title = SlateNode.string(header)
......@@ -13,3 +17,11 @@ export const handleEditor = (editor: IDomEditor) => {
// })
// treeData.value = buildTree(menu)
}
export const nodeProps = ({ option }: { option: TreeOption }) => {
return {
onClick() {
console.log(editorRef.value?.editorRef.getEditableContainer())
editorRef.value?.editorRef.scrollToElem(['w-e-element-124'])
}
}
}
import { IDomEditor, SlateDescendant, SlateElement } from '@wangeditor/editor'
import { IDomEditor, SlateDescendant, SlateElement, IModuleConf } from '@wangeditor/editor'
function parseJOBCARDHtml(domElem: Element, children: SlateDescendant[], editor: IDomEditor): SlateElement {
const parseElemHtml = (type: string) => {
return (domElem: Element, children: SlateDescendant[], editor: IDomEditor): SlateElement => {
const dataKey = domElem.getAttribute('data-key') || ''
const myResume = {
type: 'JOBCARD',
children: [{ text: (children[0] as any).text }] // void node 必须有 children ,其中有一个空字符串,重要!!!
type,
dataKey,
children
}
return myResume
}
}
export const parseHtmlConf = {
selector: 'JOBCARD[data-w-e-type="JOBCARD"]', // CSS 选择器,匹配特定的 HTML 标签
parseElemHtml: parseJOBCARDHtml
const createParseConfig = (types: string | string[]) => {
const configs: IModuleConf['parseElemsHtml'] = []
const typeArray = Array.isArray(types) ? types : [types]
typeArray.forEach((type) => {
configs.push({
selector: `${type}[data-w-e-type="${type}"]`,
parseElemHtml: parseElemHtml(type)
})
})
return configs
}
export default createParseConfig([
'EOTK-HEADER',
'JOBCARD',
'CEP',
'TITLE',
'WARNING',
'TFMATR',
'PRETOPIC',
'PARA',
'LIST1',
'L1ITEM',
'TABLE',
'TOPIC',
'SUBTASK',
'NOTE',
'LIST2',
'L2ITEM',
'LIST3',
'L3ITEM',
'LIST4',
'L4ITEM',
'STEP',
'RECORD-LINE'
])
import { h, VNode } from 'snabbdom'
import { IDomEditor, SlateElement } from '@wangeditor/editor'
import { IDomEditor, IModuleConf, SlateElement } from '@wangeditor/editor'
function renderJOBCARD(elem: SlateElement, children: VNode[] | null, editor: IDomEditor): VNode {
return h('JOBCARD', { style: { display: 'block' } }, children)
const renderElem = (type: string, style = { display: 'block' }) => {
return (elem: SlateElement, children: VNode[] | null, editor: IDomEditor): VNode => {
const dataKey = (elem as any).dataKey
return h(type, { style, props: { id: dataKey } }, children)
}
}
export const renderElemConf = {
type: 'JOBCARD',
renderElem: renderJOBCARD
const createRenderConfig = (types: string | string[]) => {
const configs: IModuleConf['renderElems'] = []
const typeArray = Array.isArray(types) ? types : [types]
typeArray.forEach((type) => {
configs.push({
type,
renderElem: renderElem(type)
})
})
return configs
}
export default createRenderConfig([
'EOTK-HEADER',
'JOBCARD',
'CEP',
'TITLE',
'WARNING',
'TFMATR',
'PRETOPIC',
'PARA',
'LIST1',
'L1ITEM',
'TABLE',
'TOPIC',
'SUBTASK',
'NOTE',
'LIST2',
'L2ITEM',
'LIST3',
'L3ITEM',
'LIST4',
'L4ITEM',
'STEP',
'RECORD-LINE'
])
......@@ -11,15 +11,15 @@
<script setup lang="ts">
import ContentEditor from './components/ContentEditor.vue'
import ContentTree from './components/ContentTree.vue'
import { editorRef, formData } from './constants'
import { editorRef, formData, xmlDOM } from './constants'
import { handleEditor } from './functions'
onMounted(() => {
nextTick(() => {
editorRef.value?.editorRef?.setHtml('<JOBCARD data-w-e-type="JOBCARD">Custom Content</JOBCARD>')
const node = { type: 'JOBCARD', children: [{ text: 'simple text' }] }
editorRef.value?.editorRef?.insertNode(node)
console.log(editorRef.value?.editorRef.getHtml())
xmlDOM.value.documentElement.querySelectorAll(':not([data-key])').forEach((node) => node.remove())
const serializer = new XMLSerializer()
const xmlString = serializer.serializeToString(xmlDOM.value.documentElement)
editorRef.value?.editorRef?.setHtml(xmlString)
})
})
</script>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment