Commit e5785566 by pangchong

fix: 优化工卡对比弹窗滚动条显示、工具栏样式及右键菜单混合文本节点目标选择与撤回定位

parent 6be9711c
......@@ -306,3 +306,14 @@ export const COMPOSITE_CONTAINER_MAP: Record<string, string[]> = {
- **配置位置约束(强制)**:针对不同环境(如 `bx`、`xm` 等)的编辑器渲染或行为参数配置,**必须且仅允许**统一写入各自环境目录下的配置文件中(例如 `src/bx/config/index.ts`、`src/xm/config/index.ts` 等),并在 `src/configs/index.ts` 中进行中转与兜底导出。
- **禁止在 `.env` 中编写渲染配置**:**绝对禁止**将任何与编辑器视图渲染、业务呈现逻辑或默认文件名/文件内容相关的配置项写入项目根目录下的各个 `.env.*`(环境变量)文件中。根目录下的 `.env` 配置文件应当只保留环境区分(如 `VITE_CUSTOM_CODE`)以及基础 API 接口服务地址等系统底层变量。
---
## 23. XML 右键上下文菜单与目标节点选择规范 (单文本与多文本区分规则)
- **区分单文本节点与多文本节点 (混合内容)**:
- **单文本节点**(节点 `mixedContent` 内部仅有 1 段有效纯文本):该文本即为父元素节点本身的内容,**严禁**在右键点击时将其推入 `-txt-` 虚拟 `#text` 节点。操作目标必须直接定位并归属于实体 XML 标签节点(如 `<REFBLOCK>`、`<PARA>` 等),右键菜单 Header 顶部展示 `目标: <REFBLOCK>`,下拉树列表中**不渲染**无意义的 `#text` 选项。
- **多文本节点 / 混合内容**(节点内部包含多个被子 Element 元素分隔的独立文本片段,如 `<REFBLOCK>文本1<REFINT/>文本2</REFBLOCK>`):**必须**保留 `-txt-` 虚拟文本节点,以便用户精确定位到具体的文本片段。右键点击具体文本片段时展示 `目标: #text`,并在下拉层级列表中展示并打勾该具体 `#text` 选项。
- **目标节点名称展示格式**:在右键菜单 Header 顶部及相关面板展示目标节点名称时,实体 XML 节点名称**必须**统一附带尖括号格式(如 `目标: <REFBLOCK>`),保持与大纲树和层级列表一致的视觉识别。
- **层级切换响应式刷新**:右键菜单 Header 选项的 Key 必须包含 `activeNodeId` 动态后缀(如 `key: `headerTarget:${activeNodeId}``),以保证用户在下拉层级列表中手动切换选择父/子/文本节点时,菜单顶部的 `目标: <XXX>` 标题能实时联动更新。
......@@ -773,6 +773,13 @@ export const useEditorStore = defineStore('editor', {
this.redoStack = []
},
hasNode(id: string | null | undefined): boolean {
if (!id) return false
if (this.nodeMap.has(id)) return true
const realId = id.includes('-txt-') ? id.split('-txt-')[0] : id
return this.nodeMap.has(realId)
},
undo() {
if (this.undoStack.length === 0 || !this.xmlTree) return
......@@ -787,7 +794,7 @@ export const useEditorStore = defineStore('editor', {
this.xmlTree = previousSnapshot.tree
this.rebuildNodeMap()
if (previousSnapshot.selectedNodeId && this.nodeMap.has(previousSnapshot.selectedNodeId)) {
if (previousSnapshot.selectedNodeId && this.hasNode(previousSnapshot.selectedNodeId)) {
this.setSelectedNodeId(previousSnapshot.selectedNodeId)
} else {
this.setSelectedNodeId(previousSnapshot.tree.id)
......@@ -812,7 +819,7 @@ export const useEditorStore = defineStore('editor', {
this.xmlTree = nextSnapshot.tree
this.rebuildNodeMap()
if (nextSnapshot.selectedNodeId && this.nodeMap.has(nextSnapshot.selectedNodeId)) {
if (nextSnapshot.selectedNodeId && this.hasNode(nextSnapshot.selectedNodeId)) {
this.setSelectedNodeId(nextSnapshot.selectedNodeId)
} else {
this.setSelectedNodeId(nextSnapshot.tree.id)
......
......@@ -451,7 +451,7 @@ export function useEditAreaContextMenu(props: EditAreaContextMenuProps, emit: (e
editorStore.deleteNode(realId)
window.$message.success('节点删除成功')
if (editorStore.selectedNodeId && editorStore.nodeMap.has(editorStore.selectedNodeId)) {
if (editorStore.selectedNodeId && editorStore.hasNode(editorStore.selectedNodeId)) {
activeNodeId.value = editorStore.selectedNodeId
} else {
emit('update:visible', false) // 若没有存活选中节点,则主动关闭当前弹窗
......
......@@ -168,9 +168,21 @@ const handleContextMenu = (e: MouseEvent) => {
const isVirtualText = rawNodeId.includes('-txt-')
const realId = isVirtualText ? rawNodeId.split('-txt-')[0] : rawNodeId
// 区分单文本节点与多文本节点(混合内容):仅当存在多个文本片段时,才保留 -txt- 虚拟文本节点
let shouldKeepVirtualText = false
if (isVirtualText) {
const mapped = editorStore.nodeMap.get(realId)
if (mapped?.node?.mixedContent) {
const textItems = mapped.node.mixedContent.filter((m) => m.type === 'text' && (m.text || '').trim())
if (textItems.length > 1) {
shouldKeepVirtualText = true
}
}
}
// 向上回溯构建祖先节点 ID 链
const ids: string[] = []
if (isVirtualText) {
if (isVirtualText && shouldKeepVirtualText) {
ids.push(rawNodeId)
}
......
......@@ -168,8 +168,8 @@
</div>
<div class="flex-1 flex gap-2 min-h-0">
<!-- 左:源工卡 XML(屏蔽原生竖向滚动条,用自定义滚动条替代) -->
<div class="flex-1 min-w-0 h-full xml-compare-left flex relative">
<!-- 左:源工卡 XML(对比后屏蔽原生竖向滚动条,用自定义滚动条替代) -->
<div class="flex-1 min-w-0 h-full xml-compare-left flex relative" :class="{ 'is-compared': compared }">
<div class="flex-1 min-w-0 h-full">
<CommonXmlEditor
ref="leftEditorRef"
......@@ -206,7 +206,7 @@
]"
/>
</template>
<!-- 可拖拽视口 thumb(有内容可滚动时才显示) -->
<!-- 可拖拽视口 thumb(对比后有内容可滚动时才显示) -->
<div
v-if="compared && leftScrollerBound && isScrollable"
class="absolute left-0 right-0 rounded-sm custom-scrollbar-thumb-v"
......@@ -233,7 +233,7 @@
</div>
<!-- 右:目标工卡 XML(同上) -->
<div class="flex-1 min-w-0 h-full xml-compare-right flex relative">
<div class="flex-1 min-w-0 h-full xml-compare-right flex relative" :class="{ 'is-compared': compared }">
<div class="flex-1 min-w-0 h-full">
<CommonXmlEditor
ref="rightEditorRef"
......@@ -270,7 +270,7 @@
]"
/>
</template>
<!-- 可拖拽视口 thumb(有内容可滚动时才显示) -->
<!-- 可拖拽视口 thumb(对比后有内容可滚动时才显示) -->
<div
v-if="compared && leftScrollerBound && isScrollable"
class="absolute left-0 right-0 rounded-sm custom-scrollbar-thumb-v"
......@@ -603,9 +603,9 @@ defineExpose({
border-color: var(--n-scrollbar-color-hover, rgba(128, 128, 128, 0.5)) !important;
}
// 隐藏 CodeMirror 原生竖向滚动条(用自定义滚动条替代)
.xml-compare-left,
.xml-compare-right {
// 对比后隐藏 CodeMirror 原生竖向滚动条(用自定义滚动条替代),未对比时保留原生滚动条
.xml-compare-left.is-compared,
.xml-compare-right.is-compared {
:deep(.cm-scroller) {
scrollbar-width: none;
&::-webkit-scrollbar {
......
......@@ -8,16 +8,20 @@
<div class="flex items-center bg-fill-2 p-0.5 rounded border border-divider">
<button
type="button"
class="px-2.5 h-6 text-xs font-semibold rounded transition-all select-none focus:outline-none"
:class="!insertBelow ? 'bg-card text-primary shadow-[0_1px_2px_rgba(0,0,0,0.06)]' : 'text-color2 hover:bg-fill-3'"
class="px-2.5 h-6 text-xs font-semibold rounded transition-[background-color,color] select-none focus:outline-none"
:style="!insertBelow
? 'background: var(--card-color); box-shadow: 0 1px 3px rgba(0,0,0,0.12); color: var(--colorText1)'
: 'background: transparent; color: var(--colorText3)'"
@click="insertBelow = false"
>
内部
</button>
<button
type="button"
class="px-2.5 h-6 text-xs font-semibold rounded transition-all select-none focus:outline-none"
:class="insertBelow ? 'bg-card text-primary shadow-[0_1px_2px_rgba(0,0,0,0.06)]' : 'text-color2 hover:bg-fill-3'"
class="px-2.5 h-6 text-xs font-semibold rounded transition-[background-color,color] select-none focus:outline-none hover:bg-fill-3"
:style="insertBelow
? 'background: var(--card-color); box-shadow: 0 1px 3px rgba(0,0,0,0.12); color: var(--colorText1)'
: 'background: transparent; color: var(--colorText3)'"
@click="insertBelow = true"
>
下方
......
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