Commit 240756ce by pangchong

refactor(editor): 优化编辑器节点选中逻辑支持diff模式

- 修改selectedNode相关getter以正确解析带-txt-后缀的节点ID
- 替换所有直接调用editorStore.setSelectedNodeId为selectNode函数调用
- selectNode函数新增diff模式判定,diff模式下禁止设置节点选中
- 在DocNodeRenderer和TableEditor组件中新增selectNode封装节点选中逻辑
- useDocNodeRenderer新增diffMode属性和diffMode注入,diff模式禁用节点选中高亮及事件处理
- 调整点击事件绑定,统一使用selectNode函数以兼容diff模式限制
- 修复部分节点ID解析错误,确保父节点正确查找
- 增强编辑界面点击响应一致性及diff模式交互体验
parent 93b2fe08
......@@ -379,11 +379,13 @@ export const useEditorStore = defineStore('editor', {
getters: {
selectedNode(state): XmlNode | null {
if (!state.selectedNodeId) return null
return state.nodeMap.get(state.selectedNodeId)?.node ?? null
const realId = state.selectedNodeId.includes('-txt-') ? state.selectedNodeId.split('-txt-')[0] : state.selectedNodeId
return state.nodeMap.get(realId)?.node ?? null
},
selectedNodeParent(state): XmlNode | null {
if (!state.selectedNodeId) return null
return state.nodeMap.get(state.selectedNodeId)?.parent ?? null
const realId = state.selectedNodeId.includes('-txt-') ? state.selectedNodeId.split('-txt-')[0] : state.selectedNodeId
return state.nodeMap.get(realId)?.parent ?? null
}
},
......@@ -486,7 +488,8 @@ export const useEditorStore = defineStore('editor', {
this.saveSnapshot()
setParentRecursive(newNode, parent.id)
const index = parent.children.findIndex((c) => c.id === this.selectedNodeId)
const realId = this.selectedNodeId.includes('-txt-') ? this.selectedNodeId.split('-txt-')[0] : this.selectedNodeId
const index = parent.children.findIndex((c) => c.id === realId)
if (index !== -1) {
parent.children.splice(index + 1, 0, newNode)
} else {
......
......@@ -9,13 +9,21 @@ import { SHOW_SINGLE_CHILD_INDEX } from '@/configs'
const ALERT_ROOT_TAGS = new Set(ALERT_BLOCK_TAGS)
const INLINE_ELEMENTS_SET = new Set(INLINE_ELEMENTS)
export function useDocNodeRenderer(props: { node: XmlNode; parent?: XmlNode | null; isInline?: boolean; insideRefBlock?: boolean }) {
export function useDocNodeRenderer(props: { node: XmlNode; parent?: XmlNode | null; isInline?: boolean; insideRefBlock?: boolean; diffMode?: boolean }) {
const editorStore = useEditorStore()
const { locale } = useI18n()
const isSelected = computed(() => editorStore.renderedSelectedNodeId === props.node.id)
const injectedDiffMode = inject<any>('diffMode', false)
const isDiffMode = computed(() => {
if (props.diffMode) return true
if (typeof injectedDiffMode === 'boolean') return injectedDiffMode
return injectedDiffMode?.value ?? false
})
const isSelected = computed(() => !isDiffMode.value && editorStore.renderedSelectedNodeId === props.node.id)
onMounted(() => {
if (isDiffMode.value) return
nodeSelectedRefs.add(props.node.id)
// 自动溯源刷新选中:若发现当前选中节点的祖先链中包含当前节点,则触发状态重算,使新挂载的组件正确取得高亮
if (editorStore.selectedNodeId) {
......@@ -35,6 +43,7 @@ export function useDocNodeRenderer(props: { node: XmlNode; parent?: XmlNode | nu
})
onBeforeUnmount(() => {
if (isDiffMode.value) return
nodeSelectedRefs.delete(props.node.id)
})
......
......@@ -210,7 +210,7 @@
:class="[
editorStore.selectedNodeId === `${node.id}-txt-${index}` ? 'ring-2 ring-primary ring-offset-1 bg-primary/10' : ''
]"
@click.stop="editorStore.setSelectedNodeId(`${node.id}-txt-${index}`)"
@click.stop="selectNode(`${node.id}-txt-${index}`)"
@blur="handleMixedTextBlur(index, $event)"
@keydown.enter="handleEnterKey"
v-text="item.text"
......@@ -224,7 +224,7 @@
:class="[
editorStore.selectedNodeId === `${node.id}-txt-${index}` ? 'ring-2 ring-primary ring-offset-1 bg-primary/10' : ''
]"
@click.stop="editorStore.setSelectedNodeId(`${node.id}-txt-${index}`)"
@click.stop="selectNode(`${node.id}-txt-${index}`)"
>
<template v-if="item.diffWords && item.diffWords.length > 0">
<span v-for="(w, idx) in item.diffWords" :key="idx" :class="getDiffWordClass(w)">{{ w.value }}</span>
......@@ -298,7 +298,7 @@
? 'ring-2 ring-primary ring-offset-1 bg-primary/10'
: 'focus:bg-fill-3'
"
@click.stop="editorStore.setSelectedNodeId(`${node.id}-txt-${index}`)"
@click.stop="selectNode(`${node.id}-txt-${index}`)"
@blur="handleMixedTextBlur(index, $event)"
@keydown.enter.prevent
v-text="item.text"
......@@ -428,7 +428,7 @@
<span
class="inline-ref font-mono font-medium hover:underline cursor-pointer select-all"
style="color: blue"
@click.stop="editorStore.setSelectedNodeId(node.id)"
@click.stop="selectNode(node.id)"
>
<template v-if="!insideRefBlock">
{{ shouldShowPrefix(node.textContent || node.attributes.REFLOC) ? (isChineseContext ? '(参考: ' : '(Ref: ') : '(' }}
......@@ -552,7 +552,7 @@
v-if="getRefTargetId(node)"
class="inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-sans font-medium bg-primary/10 text-primary border border-primary/20 hover:bg-primary/20 transition-all select-none ml-1 cursor-pointer align-middle print-hide"
title="点击直接定位至目标插图"
@click.stop="editorStore.setSelectedNodeId(getRefTargetId(node)!, true)"
@click.stop="selectNode(getRefTargetId(node)!, true)"
>
<n-icon size="11" class="mr-0.5"><NavigateOutline /></n-icon>
定位插图
......@@ -565,7 +565,7 @@
<span
class="font-mono cursor-pointer underline inline-flex items-baseline"
style="color: blue"
@click.stop="editorStore.setSelectedNodeId(node.id)"
@click.stop="selectNode(node.id)"
>
<span class="mr-1 select-none">FIN</span>
<span
......@@ -617,7 +617,7 @@
class="inline text-color1 cursor-pointer select-none"
:data-node-id="node.id"
:class="[editorStore.selectedNodeId === node.id ? 'ring-2 ring-primary ring-offset-1 rounded-sm bg-primary/20 px-0.5' : '']"
@click.stop="editorStore.setSelectedNodeId(node.id)"
@click.stop="selectNode(node.id)"
>
<template v-for="(child, idx) in node.children.filter((c) => c.tagName === 'TORVALUE')" :key="child.id">
<span v-if="idx > 0" class="inline text-color3 mx-0.5 select-none">(</span>
......@@ -633,7 +633,7 @@
class="inline text-color1 cursor-pointer select-none font-mono underline hover:text-primary transition-all"
:data-node-id="node.id"
:class="[editorStore.selectedNodeId === node.id ? 'ring-2 ring-primary ring-offset-1 rounded-sm bg-primary/20 px-0.5' : '']"
@click.stop="editorStore.setSelectedNodeId(node.id)"
@click.stop="selectNode(node.id)"
>
{{ formatTorvalue(node.attributes.MIN, node.attributes.MAX, node.attributes.UNIT) }}
</span>
......@@ -643,7 +643,7 @@
<template v-else-if="node.tagName === 'TED' || node.tagName === 'CON'">
<span
class="text-color1 cursor-pointer select-none inline-flex items-center space-x-1"
@click.stop="editorStore.setSelectedNodeId(node.id)"
@click.stop="selectNode(node.id)"
>
<template v-for="childTag in COMPOSITE_CONTAINER_MAP[node.tagName]" :key="childTag">
<template v-if="node.children.find((c) => c.tagName === childTag)">
......@@ -661,7 +661,7 @@
<template v-else-if="node.tagName === 'EXPD'">
<span
class="text-color1 cursor-pointer select-none inline-flex items-center space-x-1"
@click.stop="editorStore.setSelectedNodeId(node.id)"
@click.stop="selectNode(node.id)"
>
<template v-if="node.children && node.children.length > 0">
<template v-for="childTag in COMPOSITE_CONTAINER_MAP[node.tagName]" :key="childTag">
......@@ -742,7 +742,7 @@
? 'bg-primary/5 font-bold'
: ''
]"
@click.stop="editorStore.setSelectedNodeId(cbData.id)"
@click.stop="selectNode(cbData.id)"
>
<td
v-for="colTag in COMPOSITE_CONTAINER_MAP['CBDATA']"
......@@ -848,7 +848,7 @@
size="tiny"
type="primary"
secondary
@click.stop="editorStore.setSelectedNodeId(getReferencingNodes(node)[0].id, true)"
@click.stop="selectNode(getReferencingNodes(node)[0].id, true)"
title="点击直接定位至引用当前插图的位置"
>
<template #icon>
......@@ -875,7 +875,7 @@
v-for="refNode in getReferencingNodes(node)"
:key="refNode.id"
class="px-2 py-1.5 text-xs hover:bg-primary/10 hover:text-primary rounded cursor-pointer flex items-center justify-between transition-colors border border-transparent hover:border-primary/20"
@click="editorStore.setSelectedNodeId(refNode.id, true)"
@click="selectNode(refNode.id, true)"
>
<div class="flex items-center space-x-1 truncate mr-2">
<span class="font-mono font-bold">&lt;{{ refNode.tagName }}&gt;</span>
......@@ -977,7 +977,7 @@
<div
class="my-2 font-mono text-sm leading-relaxed overflow-x-auto flex flex-col space-y-0.5"
style="font-family: 'NSimSun', '新宋体', 'SimSun', '宋体', monospace !important; white-space: pre !important"
@click.stop="editorStore.setSelectedNodeId(node.id)"
@click.stop="selectNode(node.id)"
>
<DocNodeRenderer v-for="child in node.children" :key="child.id" :node="child" :parent="node" />
</div>
......@@ -997,7 +997,7 @@
<!-- 19.7 GDESC (图形描述) 处理 -->
<template v-else-if="node.tagName === 'GDESC'">
<div class="my-1.5 pl-[14pt] pr-[6pt] text-xs text-color3 leading-relaxed" @click.stop="editorStore.setSelectedNodeId(node.id)">
<div class="my-1.5 pl-[14pt] pr-[6pt] text-xs text-color3 leading-relaxed" @click.stop="selectNode(node.id)">
<DocNodeRenderer v-for="child in node.children" :key="child.id" :node="child" :parent="node" />
</div>
</template>
......@@ -1007,14 +1007,14 @@
<div
class="text-color1 font-bold my-2"
:class="[parent && (parent.tagName === 'SHEET' || parent.tagName === 'GRAPHIC') ? 'text-sm text-center' : 'text-base']"
@click.stop="editorStore.setSelectedNodeId(node.id)"
@click.stop="selectNode(node.id)"
>
<template v-if="node.mixedContent && node.mixedContent.length > 0">
<span
v-for="(mixed, idx) in node.mixedContent"
:key="idx"
:data-node-id="mixed.type === 'text' ? `${node.id}-txt-${idx}` : undefined"
@click.stop="mixed.type === 'text' ? editorStore.setSelectedNodeId(`${node.id}-txt-${idx}`) : undefined"
@click.stop="mixed.type === 'text' ? selectNode(`${node.id}-txt-${idx}`) : undefined"
>
<template v-if="mixed.type === 'text'">{{ mixed.text }}</template>
<template v-else-if="mixed.type === 'element'">
......@@ -1274,7 +1274,7 @@
: 'underline'
]"
style="color: blue"
@click.stop="editorStore.setSelectedNodeId(child.id)"
@click.stop="selectNode(child.id)"
>
{{ child.textContent }}
</span>
......@@ -1307,7 +1307,7 @@
: 'underline'
]"
style="color: blue"
@click.stop="editorStore.setSelectedNodeId(child.id)"
@click.stop="selectNode(child.id)"
>
{{ child.textContent }}
</span>
......@@ -1640,13 +1640,18 @@ const {
getChildNode,
handleTextBlur,
handleEnterKey,
handleChildTextBlur,
handleMixedTextBlur,
hasNonCbDataChildren,
getNonCbDataChildren
} = useDocNodeRenderer(props)
const selectNode = (id: string, forceScroll = false) => {
if (isDiffMode.value) return
editorStore.setSelectedNodeId(id, forceScroll)
}
const handleNodeClick = (e: MouseEvent) => {
if (isDiffMode.value) return
if (e.ctrlKey || e.metaKey) {
let refId =
props.node.attributes?.REFID || props.node.attributes?.STRUCTID || props.node.attributes?.GRAPHICKEY || props.node.attributes?.GNBR
......@@ -1660,7 +1665,7 @@ const handleNodeClick = (e: MouseEvent) => {
if (refId) {
const targetNode = editorStore.findNodeByRef(refId, 'GRAPHIC')
if (targetNode) {
editorStore.setSelectedNodeId(targetNode.id, true)
selectNode(targetNode.id, true)
window.$message?.success?.(`已按 Ctrl 跳转到关联插图 <${targetNode.tagName}> (KEY/ID: ${refId})`)
return
} else {
......@@ -1668,7 +1673,7 @@ const handleNodeClick = (e: MouseEvent) => {
}
}
}
editorStore.setSelectedNodeId(props.node.id)
selectNode(props.node.id)
}
</script>
......
......@@ -1292,6 +1292,7 @@ export function useTableEditor(props: { node: XmlNode }, options?: { mergeModalR
})
const selectCellLogic = (cell: TableCellModel, e: MouseEvent) => {
if (isDiffMode.value) return
const isCtrl = e.ctrlKey || e.metaKey
if (isCtrl) {
if (selectedCellIds.value.includes(cell.id)) {
......
......@@ -39,7 +39,7 @@
v-if="structure.theadRows.length > 0"
:data-node-id="structure.theadId"
data-tag-name="THEAD"
@click.stop="editorStore.setSelectedNodeId(structure.theadId)"
@click.stop="selectNode(structure.theadId)"
class="transition-all"
:class="[isNodeSelected(structure.theadId) ? 'outline outline-2 outline-primary outline-offset-[-2px] bg-primary/5' : '']"
:style="editorStore.nodeMap.get(structure.theadId)?.node.attributes?.MERGED === 'TRUE' ? { backgroundColor: 'yellow' } : {}"
......@@ -70,7 +70,7 @@
insertedRowIds.has(row.id) ? 'inserted-row-highlight' : ''
]"
:style="editorStore.nodeMap.get(row.id)?.node.attributes?.MERGED === 'TRUE' ? { backgroundColor: 'yellow' } : {}"
@click.stop="editorStore.setSelectedNodeId(row.id)"
@click.stop="selectNode(row.id)"
>
<!-- 表头行选择号 -->
<th
......@@ -80,7 +80,7 @@
isNodeSelected(row.id) ? 'bg-primary text-white' : '',
!isDiffMode && !isNodeSelected(row.id) ? 'hover:bg-fill-3' : ''
]"
@click.stop="editorStore.setSelectedNodeId(row.id)"
@click.stop="selectNode(row.id)"
@contextmenu.prevent="isDiffMode ? null : handleRowContextMenu(row.id, $event)"
>
H{{ structure.theadRows.length > 1 ? rIdx + 1 : '' }}
......@@ -185,7 +185,7 @@
<tbody
:data-node-id="structure.tbodyId"
data-tag-name="TBODY"
@click.stop="editorStore.setSelectedNodeId(structure.tbodyId)"
@click.stop="selectNode(structure.tbodyId)"
class="transition-all"
:class="[isNodeSelected(structure.tbodyId) ? 'outline outline-2 outline-primary outline-offset-[-2px] bg-primary/5' : '']"
:style="editorStore.nodeMap.get(structure.tbodyId)?.node.attributes?.MERGED === 'TRUE' ? { backgroundColor: 'yellow' } : {}"
......@@ -216,7 +216,7 @@
insertedRowIds.has(row.id) ? 'inserted-row-highlight' : ''
]"
:style="editorStore.nodeMap.get(row.id)?.node.attributes?.MERGED === 'TRUE' ? { backgroundColor: 'yellow' } : {}"
@click.stop="editorStore.setSelectedNodeId(row.id)"
@click.stop="selectNode(row.id)"
>
<!-- 行号选择单元格 -->
<td
......@@ -226,7 +226,7 @@
isNodeSelected(row.id) ? 'bg-primary text-white' : 'bg-fill-4 text-color3',
!isDiffMode && !isNodeSelected(row.id) ? 'hover:bg-fill-3' : ''
]"
@click.stop="editorStore.setSelectedNodeId(row.id)"
@click.stop="selectNode(row.id)"
@contextmenu.prevent="isDiffMode ? null : handleRowContextMenu(row.id, $event)"
>
{{ rIdx + 1 }}
......@@ -425,6 +425,11 @@ const {
executeMergeAction
} = useTableEditor(props, { mergeModalRef })
const selectNode = (id: string, forceScroll = false) => {
if (isDiffMode.value) return
editorStore.setSelectedNodeId(id, forceScroll)
}
const isNodeSelected = (id: string) => {
if (isDiffMode.value) return false
return rawIsNodeSelected(id)
......
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