Commit fb7284af by pangchong

refactor: 代码优化

parent 8a5c5306
import { IModuleConf, SlateElement } from '@wangeditor/editor'
import { nodeSet } from '@/views/editor/constants/nodeParsed.ts'
import { nodeSet } from '@/configs/node.config'
const elemToHtml = (type: string) => {
return (elem: SlateElement, childrenHtml: string): string => {
......
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) => {
return (domElem: Element, children: SlateDescendant[], editor: IDomEditor): SlateElement => {
......
import { h, VNode } from 'snabbdom'
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' }) => {
return (elem: SlateElement, children: VNode[] | null, editor: IDomEditor): VNode => {
......
// 引入 TreeRenderResult 类型,该类型定义在 @/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 { getUUID } from '@/utils'
// 定义 Processing 类,用于处理 XML 数据
export class Processing {
......@@ -185,7 +184,7 @@ export class Processing {
// 生成唯一的 key
let targetKey = domNode.getAttribute('data-key')
if (!targetKey) {
targetKey = 'g-' + UUID.generate()
targetKey = getUUID()
}
// 创建树形数据项
const treeItem: TreeRenderResult = {
......@@ -227,7 +226,7 @@ export class Processing {
// 生成唯一的 key
let targetKey = node.getAttribute('data-key')
if (!targetKey) {
targetKey = 'g-' + UUID.generate()
targetKey = getUUID()
}
// 创建树形数据项
const treeItem: TreeRenderResult = {
......
import {Processing} from '@/lib/XMLProcessor/src/core/Processing.ts'
import {Plugin, getCurrentInstance} from 'vue'
import TextA from "@/assets/file/CES-QEC-V250-A.xml?raw"
import TextB from "@/assets/file/Trans-Convert.xml?raw"
import {nodeSet} from '@/views/editor/constants/nodeParsed.ts'
import { Processing } from '@/lib/XMLProcessor/src/core/Processing.ts'
import { Plugin, getCurrentInstance } from 'vue'
import TextA from '@/assets/file/CES-QEC-V250-A.xml?raw'
import TextB from '@/assets/file/Trans-Convert.xml?raw'
import { nodeSet } from '@/configs/node.config'
const p = new Processing()
// @ts-ignore
window.$p = function() {
let nodeV = p.dualCompareFromString(
TextA,
TextB,
nodeSet
)
window.$p = function () {
let nodeV = p.dualCompareFromString(TextA, TextB, nodeSet)
console.log('v-h', nodeV)
}
export const XMLProcessing: Plugin = function(app) {
app.provide('xmlProcessing', p);
export const XMLProcessing: Plugin = function (app) {
app.provide('xmlProcessing', p)
}
export function useXMLProcessing(): Processing {
......@@ -23,4 +19,4 @@ export function useXMLProcessing(): Processing {
return <Processing>inject('xmlProcessing')
}
return p
}
\ No newline at end of file
}
import { UUID } from 'uuidjs'
export const getUUID = () => {
return 'g-' + UUID.generate()
}
......@@ -19,22 +19,20 @@
</template>
<script lang="ts" setup>
import { realComposableData, searchKey, treeSelectedKeys, treeRef } from '../constants'
import type { TreeOption } from 'naive-ui'
import { nodeProps } from '../functions'
const expandedKeys = ref<string[]>([])
function 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[])
}
import { treeSelectedKeys, treeRef, searchKey, expandedKeys, treeData } from '../constants/tree'
import { buildFilteredTree, getAllKeys, nodeProps } from '../functions/tree'
const realComposableData = computed(function () {
if (!searchKey.value) {
return treeData.value
}
// @ts-ignore
const result = buildFilteredTree(treeData.value[0], searchKey.value)
if (!result) {
return []
}
return [result]
})
watch(
realComposableData,
function () {
......
// @ts-nocheck
import type { TreeOption } from 'naive-ui'
import { TreeRenderResult } from '@/lib/XMLProcessor/src/typing'
import { ArrowLeft24Filled, ArrowRight24Filled, Book20Filled, TableInsertColumn24Filled } from '@vicons/fluent'
import { IDomEditor } from '@wangeditor/editor'
export const showLoading = ref(false)
// 菜单相关
export const searchKey = ref('')
export const treeData: Ref<TreeOption[]> = ref([])
export const xmlDOM: Ref<Document> = ref()
export const treeSelectedKeys = ref<string[]>([])
export const xmlContent: Ref<string> = ref('')
//编辑器相关
export const editorRef = ref()
export const formData = reactive({
html: ''
})
export const treeRef = ref()
// 递归函数:检查树中是否存在满足条件的节点
// 递归函数:检查树中是否存在满足条件的节点,并构建新的树结构
function 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) // 如果子节点符合条件,加入到当前节点的子节点列表
//编辑器相关配置
export const editorRef = shallowRef<IDomEditor>()
export const editorHtml = ref('')
//菜单配置
export const toolbarConfig = {
toolbarKeys: [
'headerSelect',
'bold',
'italic',
'underline',
'through',
'color',
'fontSize',
'lineHeight',
'bulletedList',
'numberedList',
'justifyLeft',
'justifyCenter',
'justifyRight',
'undo',
'redo',
'uploadImage',
'insertLink',
'fullScreen',
'clearStyle'
]
}
//右键菜单
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)
})
}
},
{
label: '插入',
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)
})
}
}
]
}
// 如果有符合条件的子节点,返回当前节点,并更新子节点列表
if (filteredChildren.length > 0) {
return { ...tree, children: filteredChildren }
}
}
// 如果当前节点及其子节点都不满足条件,返回 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 { editorRef, formData, showLoading, treeData, treeRef, treeSelectedKeys, xmlContent, xmlDOM } from '../constants'
import { TreeOption } from 'naive-ui'
import { nodeSet } from '../constants/nodeParsed'
import { treeData, treeRef, treeSelectedKeys } from '../constants/tree'
import { dropdownConfig, editorRef, editorHtml } from '../constants'
import { UUID } from 'uuidjs'
import { getUUID } from '@/utils'
export const handleEditor = (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
export const handleCreated = (editor: IDomEditor) => {
editorRef.value = editor
handleChange(editor)
}
export const handleClick = (key: string) => {
export const handleClick = (event: any) => {
const key = getKey(event.target)
treeRef.value?.scrollTo({ key })
treeSelectedKeys.value = [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 = ''
export const setEditorActive = (key: string, callBack?: Function) => {
const container = editorRef.value?.editorRef.getEditableContainer()
const id = container.querySelector(`[data-key="${key}"]`)?.getAttribute('id')
const container = editorRef.value?.getEditableContainer()
const id = container!.querySelector(`[data-key="${key}"]`)?.getAttribute('id') as string
if (container) {
if (lastFocusedId) {
container.querySelector(`#${lastFocusedId}`)?.classList.remove('bg-primaryColorHover')
......@@ -51,6 +31,49 @@ export const setEditorActive = (key: string, callBack?: Function) => {
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 () => {
const input = document.createElement('input')
input.type = 'file'
......@@ -65,7 +88,7 @@ export const uploadXml = async () => {
showLoading.value = true
const res = await xmlProcessing.processFile(file, nodeSet)
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>
<n-spin class="h-full w-full" content-class="h-full w-full" :show="showLoading" description="加载中...">
<ContentEditor
ref="editorRef"
v-model="formData.html"
@change="handleEditor"
@created="handleEditor"
@handleClick="handleClick"
@handleContextSelect="handleContextSelect"
>
<template #left>
<n-card class="h-full min-w-[300px] max-w-[300px] mr-[15px]" content-class="bg-baseColor !p-0 overflow-hidden">
<ContentTree></ContentTree>
</n-card>
</template>
</ContentEditor>
</n-spin>
<div class="z-10 h-full flex flex-col">
<!-- 工具栏 -->
<div class="flex">
<div class="flex-1">
<Toolbar :editor="editorRef" editorId="wangeEditor-1" class="border-b border-solid border-borderColor" />
</div>
<n-button @click="uploadXml" class="h-full">上传XML</n-button>
</div>
<!-- 编辑器 -->
<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">
<Tree />
</n-card>
<n-card class="flex-1" content-class="h-full !p-0">
<Editor
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>
<script setup lang="ts">
import ContentEditor from './components/ContentEditor.vue'
import ContentTree from './components/ContentTree.vue'
import { editorRef, formData, showLoading, xmlContent } from './constants'
import { handleClick, handleEditor, handleContextSelect } from './functions'
// @ts-ignore
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import { Boot, IModuleConf } from '@wangeditor/editor'
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(() => {
// nextTick(() => {
// editorRef.value?.editorRef?.setHtml(xmlContent.value)
// })
const module: Partial<IModuleConf> = {
renderElems: renderElemConf,
elemsToHtml: elemToHtmlConf,
parseElemsHtml: parseHtmlConf
}
Boot.registerModule(module)
// 组件销毁时,及时销毁编辑器
onBeforeUnmount(() => {
const editor = unref(editorRef.value)
// 销毁,并移除 editor
editor?.destroy()
})
</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