Commit fb7284af by pangchong

refactor: 代码优化

parent 8a5c5306
import { IModuleConf, SlateElement } from '@wangeditor/editor' import { IModuleConf, SlateElement } from '@wangeditor/editor'
import { nodeSet } from '@/views/editor/constants/nodeParsed.ts' import { nodeSet } from '@/configs/node.config'
const elemToHtml = (type: string) => { const elemToHtml = (type: string) => {
return (elem: SlateElement, childrenHtml: string): string => { return (elem: SlateElement, childrenHtml: string): string => {
......
import { IDomEditor, IModuleConf, SlateDescendant, SlateElement } from '@wangeditor/editor' import { IDomEditor, IModuleConf, SlateDescendant, SlateElement } from '@wangeditor/editor'
import { nodeSet } from '@/views/editor/constants/nodeParsed.ts' import { nodeSet } from '@/configs/node.config'
const parseElemHtml = (type: string) => { const parseElemHtml = (type: string) => {
return (domElem: Element, children: SlateDescendant[], editor: IDomEditor): SlateElement => { return (domElem: Element, children: SlateDescendant[], editor: IDomEditor): SlateElement => {
......
import { h, VNode } from 'snabbdom' import { h, VNode } from 'snabbdom'
import { IDomEditor, IModuleConf, SlateElement } from '@wangeditor/editor' import { IDomEditor, IModuleConf, SlateElement } from '@wangeditor/editor'
import { nodeSet } from '@/views/editor/constants/nodeParsed.ts' import { nodeSet } from '@/configs/node.config'
const renderElem = (type: string, style = { display: 'block' }) => { const renderElem = (type: string, style = { display: 'block' }) => {
return (elem: SlateElement, children: VNode[] | null, editor: IDomEditor): VNode => { return (elem: SlateElement, children: VNode[] | null, editor: IDomEditor): VNode => {
......
// 引入 TreeRenderResult 类型,该类型定义在 @/lib/XMLProcessor/src/typing 模块中 // 引入 TreeRenderResult 类型,该类型定义在 @/lib/XMLProcessor/src/typing 模块中
import { NewTreeModification, OldTreeModification, TreeRenderResult, TreeRenderResultFlatted } from '@/lib/XMLProcessor/src/typing' import { NewTreeModification, OldTreeModification, TreeRenderResult, TreeRenderResultFlatted } from '@/lib/XMLProcessor/src/typing'
// 引入 UUID 类用于生成唯一标识符
import { UUID } from 'uuidjs'
import { md5 } from 'js-md5' import { md5 } from 'js-md5'
import { getUUID } from '@/utils'
// 定义 Processing 类,用于处理 XML 数据 // 定义 Processing 类,用于处理 XML 数据
export class Processing { export class Processing {
...@@ -185,7 +184,7 @@ export class Processing { ...@@ -185,7 +184,7 @@ export class Processing {
// 生成唯一的 key // 生成唯一的 key
let targetKey = domNode.getAttribute('data-key') let targetKey = domNode.getAttribute('data-key')
if (!targetKey) { if (!targetKey) {
targetKey = 'g-' + UUID.generate() targetKey = getUUID()
} }
// 创建树形数据项 // 创建树形数据项
const treeItem: TreeRenderResult = { const treeItem: TreeRenderResult = {
...@@ -227,7 +226,7 @@ export class Processing { ...@@ -227,7 +226,7 @@ export class Processing {
// 生成唯一的 key // 生成唯一的 key
let targetKey = node.getAttribute('data-key') let targetKey = node.getAttribute('data-key')
if (!targetKey) { if (!targetKey) {
targetKey = 'g-' + UUID.generate() targetKey = getUUID()
} }
// 创建树形数据项 // 创建树形数据项
const treeItem: TreeRenderResult = { const treeItem: TreeRenderResult = {
......
import {Processing} from '@/lib/XMLProcessor/src/core/Processing.ts' import { Processing } from '@/lib/XMLProcessor/src/core/Processing.ts'
import {Plugin, getCurrentInstance} from 'vue' import { Plugin, getCurrentInstance } from 'vue'
import TextA from "@/assets/file/CES-QEC-V250-A.xml?raw" import TextA from '@/assets/file/CES-QEC-V250-A.xml?raw'
import TextB from "@/assets/file/Trans-Convert.xml?raw" import TextB from '@/assets/file/Trans-Convert.xml?raw'
import {nodeSet} from '@/views/editor/constants/nodeParsed.ts' import { nodeSet } from '@/configs/node.config'
const p = new Processing() const p = new Processing()
// @ts-ignore // @ts-ignore
window.$p = function() { window.$p = function () {
let nodeV = p.dualCompareFromString( let nodeV = p.dualCompareFromString(TextA, TextB, nodeSet)
TextA,
TextB,
nodeSet
)
console.log('v-h', nodeV) console.log('v-h', nodeV)
} }
export const XMLProcessing: Plugin = function(app) { export const XMLProcessing: Plugin = function (app) {
app.provide('xmlProcessing', p); app.provide('xmlProcessing', p)
} }
export function useXMLProcessing(): Processing { export function useXMLProcessing(): Processing {
......
import { UUID } from 'uuidjs'
export const getUUID = () => {
return 'g-' + UUID.generate()
}
...@@ -19,22 +19,20 @@ ...@@ -19,22 +19,20 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { realComposableData, searchKey, treeSelectedKeys, treeRef } from '../constants' import { treeSelectedKeys, treeRef, searchKey, expandedKeys, treeData } from '../constants/tree'
import type { TreeOption } from 'naive-ui' import { buildFilteredTree, getAllKeys, nodeProps } from '../functions/tree'
import { nodeProps } from '../functions'
const expandedKeys = ref<string[]>([]) const realComposableData = computed(function () {
if (!searchKey.value) {
function getAllKeys(item: TreeOption[]) { return treeData.value
return item.reduce(function (q, w) {
q.push(w.key as string)
if (w.children) {
q.push(...getAllKeys(w.children))
} }
return q // @ts-ignore
}, [] as string[]) const result = buildFilteredTree(treeData.value[0], searchKey.value)
} if (!result) {
return []
}
return [result]
})
watch( watch(
realComposableData, realComposableData,
function () { function () {
......
// @ts-nocheck import { ArrowLeft24Filled, ArrowRight24Filled, Book20Filled, TableInsertColumn24Filled } from '@vicons/fluent'
import type { TreeOption } from 'naive-ui' import { IDomEditor } from '@wangeditor/editor'
import { TreeRenderResult } from '@/lib/XMLProcessor/src/typing'
export const showLoading = ref(false) export const showLoading = ref(false)
// 菜单相关 //编辑器相关配置
export const searchKey = ref('') export const editorRef = shallowRef<IDomEditor>()
export const treeData: Ref<TreeOption[]> = ref([]) export const editorHtml = ref('')
export const xmlDOM: Ref<Document> = ref() //菜单配置
export const treeSelectedKeys = ref<string[]>([]) export const toolbarConfig = {
export const xmlContent: Ref<string> = ref('') toolbarKeys: [
//编辑器相关 'headerSelect',
export const editorRef = ref() 'bold',
export const formData = reactive({ 'italic',
html: '' 'underline',
}) 'through',
export const treeRef = ref() 'color',
// 递归函数:检查树中是否存在满足条件的节点 'fontSize',
// 递归函数:检查树中是否存在满足条件的节点,并构建新的树结构 'lineHeight',
function buildFilteredTree(tree: TreeRenderResult, searchString: string): TreeRenderResult | null { 'bulletedList',
// 如果当前节点的 label 包含指定字符串,直接返回当前节点 'numberedList',
if (tree.label.includes(searchString)) { 'justifyLeft',
return { ...tree } // 返回当前节点的副本 'justifyCenter',
} 'justifyRight',
'undo',
// 如果当前节点有子节点,递归检查每个子节点 'redo',
if (tree.children) { 'uploadImage',
const filteredChildren: TreeRenderResult[] = [] 'insertLink',
for (let child of tree.children) { 'fullScreen',
const result = buildFilteredTree(child, searchString) 'clearStyle'
if (result) { ]
filteredChildren.push(result) // 如果子节点符合条件,加入到当前节点的子节点列表 }
//右键菜单
export const dropdownConfig = reactive({
show: false,
x: 0,
y: 0,
options: [
{
label: '左缩进',
key: 'left',
icon() {
return h(NIcon, null, {
default: () => h(ArrowLeft24Filled)
})
} }
},
{
label: '右缩进',
key: 'right',
icon() {
return h(NIcon, null, {
default: () => h(ArrowRight24Filled)
})
} }
},
// 如果有符合条件的子节点,返回当前节点,并更新子节点列表 {
if (filteredChildren.length > 0) { label: '插入',
return { ...tree, children: filteredChildren } key: 'insert',
icon() {
return h(NIcon, null, {
default: () => h(TableInsertColumn24Filled)
})
},
children: [
{
label: 'TaskTitle',
key: 'TaskTitle',
icon() {
return h(NIcon, null, {
default: () => h(Book20Filled)
})
} }
},
{
label: 'TopicTitle',
key: 'TopicTitle',
icon() {
return h(NIcon, null, {
default: () => h(Book20Filled)
})
} }
// 如果当前节点及其子节点都不满足条件,返回 null
return null
}
export const realComposableData = computed(function () {
if (!searchKey.value) {
return treeData.value
} }
// @ts-ignore ]
const result = buildFilteredTree(treeData.value[0], searchKey.value)
if (!result) {
return []
} }
// @ts-ignore ]
return [result]
}) })
import { TreeOption } from 'naive-ui'
export const expandedKeys = ref<string[]>([])
export const treeData: Ref<TreeOption[]> = ref([])
export const treeSelectedKeys = ref<string[]>([])
export const treeRef = ref()
export const searchKey = ref('')
import { showLoading } from '../constants'
import { nodeSet } from '@/configs/node.config'
import { IDomEditor } from '@wangeditor/editor' import { IDomEditor } from '@wangeditor/editor'
import { editorRef, formData, showLoading, treeData, treeRef, treeSelectedKeys, xmlContent, xmlDOM } from '../constants' import { treeData, treeRef, treeSelectedKeys } from '../constants/tree'
import { TreeOption } from 'naive-ui' import { dropdownConfig, editorRef, editorHtml } from '../constants'
import { nodeSet } from '../constants/nodeParsed'
import { UUID } from 'uuidjs' import { UUID } from 'uuidjs'
import { getUUID } from '@/utils'
export const handleEditor = (editor: IDomEditor) => { export const handleCreated = (editor: IDomEditor) => {
if (editor.getHtml() == '<p><br></p>') return editorRef.value = editor
const xmlProcessing = useXMLProcessing() handleChange(editor)
const res = xmlProcessing.processXML(editor.getHtml().replace(/<p><br><\/p>/g, ''), nodeSet)
treeData.value = res.treeData
} }
export const handleClick = (event: any) => {
export const handleClick = (key: string) => { const key = getKey(event.target)
treeRef.value?.scrollTo({ key }) treeRef.value?.scrollTo({ key })
treeSelectedKeys.value = [key] treeSelectedKeys.value = [key]
setEditorActive(key) setEditorActive(key)
} }
export const handleContextSelect = (key: string) => {
if (key == 'TaskTitle') {
const node = { type: 'TaskTitle', dataKey: 'g-' + UUID.generate(), children: [{ text: 'TaskTitle' }] }
editorRef.value?.editorRef?.insertNode(node)
} else if (key == 'TopicTitle') {
const node = { type: 'TopicTitle', dataKey: 'g-' + UUID.generate(), children: [{ text: 'TopicTitle' }] }
editorRef.value?.editorRef?.insertNode(node)
}
}
export const nodeProps = ({ option }: { option: TreeOption }) => {
return {
onClick() {
setEditorActive(option.key as string, (id: string) => {
editorRef.value?.editorRef.scrollToElem([id])
})
}
}
}
let lastFocusedId = '' let lastFocusedId = ''
export const setEditorActive = (key: string, callBack?: Function) => { export const setEditorActive = (key: string, callBack?: Function) => {
const container = editorRef.value?.editorRef.getEditableContainer() const container = editorRef.value?.getEditableContainer()
const id = container.querySelector(`[data-key="${key}"]`)?.getAttribute('id') const id = container!.querySelector(`[data-key="${key}"]`)?.getAttribute('id') as string
if (container) { if (container) {
if (lastFocusedId) { if (lastFocusedId) {
container.querySelector(`#${lastFocusedId}`)?.classList.remove('bg-primaryColorHover') container.querySelector(`#${lastFocusedId}`)?.classList.remove('bg-primaryColorHover')
...@@ -51,6 +31,49 @@ export const setEditorActive = (key: string, callBack?: Function) => { ...@@ -51,6 +31,49 @@ export const setEditorActive = (key: string, callBack?: Function) => {
if (callBack) callBack(id) if (callBack) callBack(id)
} }
//获取节点的key
export const getKey = (elem: any): string => {
if (!elem) return ''
const key = elem.getAttribute('data-key')
if (key && elem != null) {
return key
} else {
elem = elem.parentElement
return getKey(elem)
}
}
// 右键
export const handleContextMenu = (event: MouseEvent) => {
dropdownConfig.show = false
nextTick(() => {
dropdownConfig.show = true
dropdownConfig.x = event.clientX
dropdownConfig.y = event.clientY
})
}
// 右键选择
export const handleSelect = (key: string | number) => {
dropdownConfig.show = false
editorRef.value?.restoreSelection()
if (key == 'TaskTitle') {
const node = { type: 'TaskTitle', dataKey: getUUID(), children: [{ text: 'TaskTitle' }] }
editorRef.value?.insertNode(node)
} else if (key == 'TopicTitle') {
const node = { type: 'TopicTitle', dataKey: getUUID(), children: [{ text: 'TopicTitle' }] }
editorRef.value?.insertNode(node)
}
}
export const onClickoutside = () => {
dropdownConfig.show = false
}
// 编辑器内容变化
export const handleChange = (editor: IDomEditor) => {
if (editor.getHtml() == '<p><br></p>') return
const xmlProcessing = useXMLProcessing()
const res = xmlProcessing.processXML(editor.getHtml().replace(/<p><br><\/p>/g, ''), nodeSet)
treeData.value = res.treeData
}
// 上传xml
export const uploadXml = async () => { export const uploadXml = async () => {
const input = document.createElement('input') const input = document.createElement('input')
input.type = 'file' input.type = 'file'
...@@ -65,7 +88,7 @@ export const uploadXml = async () => { ...@@ -65,7 +88,7 @@ export const uploadXml = async () => {
showLoading.value = true showLoading.value = true
const res = await xmlProcessing.processFile(file, nodeSet) const res = await xmlProcessing.processFile(file, nodeSet)
showLoading.value = false showLoading.value = false
formData.html = res.xmlContent editorHtml.value = res.xmlContent
} }
}) })
} }
import { TreeRenderResult } from '@/lib/XMLProcessor/src/typing'
import { setEditorActive } from '.'
import { editorRef } from '../constants'
import { TreeOption } from 'naive-ui'
//点击节点
export const nodeProps = ({ option }: { option: TreeOption }) => {
return {
onClick() {
setEditorActive(option.key as string, (id: string) => {
editorRef.value?.scrollToElem(id)
})
}
}
}
// 递归函数:检查树中是否存在满足条件的节点,并构建新的树结构
export const buildFilteredTree = (tree: TreeRenderResult, searchString: string): TreeRenderResult | null => {
// 如果当前节点的 label 包含指定字符串,直接返回当前节点
if (tree.label.includes(searchString)) {
return { ...tree } // 返回当前节点的副本
}
// 如果当前节点有子节点,递归检查每个子节点
if (tree.children) {
const filteredChildren: TreeRenderResult[] = []
for (let child of tree.children) {
const result = buildFilteredTree(child, searchString)
if (result) {
filteredChildren.push(result) // 如果子节点符合条件,加入到当前节点的子节点列表
}
}
// 如果有符合条件的子节点,返回当前节点,并更新子节点列表
if (filteredChildren.length > 0) {
return { ...tree, children: filteredChildren }
}
}
// 如果当前节点及其子节点都不满足条件,返回 null
return null
}
export const getAllKeys = (item: TreeOption[]) => {
return item.reduce(function (q, w) {
q.push(w.key as string)
if (w.children) {
q.push(...getAllKeys(w.children))
}
return q
}, [] as string[])
}
<template> <template>
<n-spin class="h-full w-full" content-class="h-full w-full" :show="showLoading" description="加载中..."> <div class="z-10 h-full flex flex-col">
<ContentEditor <!-- 工具栏 -->
ref="editorRef" <div class="flex">
v-model="formData.html" <div class="flex-1">
@change="handleEditor" <Toolbar :editor="editorRef" editorId="wangeEditor-1" class="border-b border-solid border-borderColor" />
@created="handleEditor" </div>
@handleClick="handleClick" <n-button @click="uploadXml" class="h-full">上传XML</n-button>
@handleContextSelect="handleContextSelect" </div>
> <!-- 编辑器 -->
<template #left> <div class="p-[15px] flex flex-1 overflow-hidden">
<n-card class="h-full min-w-[300px] max-w-[300px] mr-[15px]" content-class="bg-baseColor !p-0 overflow-hidden"> <n-card class="h-full min-w-[300px] max-w-[300px] mr-[15px]" content-class="bg-baseColor !p-0 overflow-hidden">
<ContentTree></ContentTree> <Tree />
</n-card> </n-card>
</template> <n-card class="flex-1" content-class="h-full !p-0">
</ContentEditor> <Editor
</n-spin> v-model="editorHtml"
editorId="wangeEditor-1"
:style="{ height: '100%' }"
@on-change="handleChange"
@on-created="handleCreated"
@click="handleClick"
@contextmenu.prevent="handleContextMenu"
/>
<n-dropdown
placement="bottom-start"
trigger="manual"
:x="dropdownConfig.x"
:y="dropdownConfig.y"
:options="dropdownConfig.options"
:show="dropdownConfig.show"
:on-clickoutside="onClickoutside"
@select="handleSelect"
/>
</n-card>
</div>
</div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import ContentEditor from './components/ContentEditor.vue' // @ts-ignore
import ContentTree from './components/ContentTree.vue' import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import { editorRef, formData, showLoading, xmlContent } from './constants' import { Boot, IModuleConf } from '@wangeditor/editor'
import { handleClick, handleEditor, handleContextSelect } from './functions' import renderElemConf from '@/configs/render-elem'
import elemToHtmlConf from '@/configs/elem-to-html'
import parseHtmlConf from '@/configs/parse-elem-html'
import { dropdownConfig, editorRef, editorHtml } from './constants/'
import { handleChange, handleClick, handleContextMenu, handleCreated, handleSelect, onClickoutside, uploadXml } from './functions/'
import Tree from './components/Tree.vue'
onMounted(() => { const module: Partial<IModuleConf> = {
// nextTick(() => { renderElems: renderElemConf,
// editorRef.value?.editorRef?.setHtml(xmlContent.value) elemsToHtml: elemToHtmlConf,
// }) parseElemsHtml: parseHtmlConf
}
Boot.registerModule(module)
// 组件销毁时,及时销毁编辑器
onBeforeUnmount(() => {
const editor = unref(editorRef.value)
// 销毁,并移除 editor
editor?.destroy()
}) })
</script> </script>
<style lang="less" scoped></style> <style src="@wangeditor/editor/dist/css/style.css"></style>
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