Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
I
Ifar-Xml-Edtor
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
pangchong
Ifar-Xml-Edtor
Commits
e5785566
Commit
e5785566
authored
Aug 06, 2026
by
pangchong
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: 优化工卡对比弹窗滚动条显示、工具栏样式及右键菜单混合文本节点目标选择与撤回定位
parent
6be9711c
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
50 additions
and
16 deletions
+50
-16
.gemini/.rules.md
+11
-0
src/store/editor/index.ts
+9
-2
src/views/editor/components/EditorPanel/components/EditAreaContextMenuModal/functionals/index.ts
+1
-1
src/views/editor/components/EditorPanel/index.vue
+13
-1
src/views/editor/components/EditorToolbar/components/CompareModal/index.vue
+8
-8
src/views/editor/components/EditorToolbar/index.vue
+8
-4
No files found.
.gemini/.rules.md
View file @
e5785566
...
@@ -306,3 +306,14 @@ export const COMPOSITE_CONTAINER_MAP: Record<string, string[]> = {
...
@@ -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` 中进行中转与兜底导出。
- **配置位置约束(强制)**:针对不同环境(如 `bx`、`xm` 等)的编辑器渲染或行为参数配置,**必须且仅允许**统一写入各自环境目录下的配置文件中(例如 `src/bx/config/index.ts`、`src/xm/config/index.ts` 等),并在 `src/configs/index.ts` 中进行中转与兜底导出。
- **禁止在 `.env` 中编写渲染配置**:**绝对禁止**将任何与编辑器视图渲染、业务呈现逻辑或默认文件名/文件内容相关的配置项写入项目根目录下的各个 `.env.*`(环境变量)文件中。根目录下的 `.env` 配置文件应当只保留环境区分(如 `VITE_CUSTOM_CODE`)以及基础 API 接口服务地址等系统底层变量。
- **禁止在 `.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>`
标题能实时联动更新。
src/store/editor/index.ts
View file @
e5785566
...
@@ -773,6 +773,13 @@ export const useEditorStore = defineStore('editor', {
...
@@ -773,6 +773,13 @@ export const useEditorStore = defineStore('editor', {
this
.
redoStack
=
[]
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
()
{
undo
()
{
if
(
this
.
undoStack
.
length
===
0
||
!
this
.
xmlTree
)
return
if
(
this
.
undoStack
.
length
===
0
||
!
this
.
xmlTree
)
return
...
@@ -787,7 +794,7 @@ export const useEditorStore = defineStore('editor', {
...
@@ -787,7 +794,7 @@ export const useEditorStore = defineStore('editor', {
this
.
xmlTree
=
previousSnapshot
.
tree
this
.
xmlTree
=
previousSnapshot
.
tree
this
.
rebuildNodeMap
()
this
.
rebuildNodeMap
()
if
(
previousSnapshot
.
selectedNodeId
&&
this
.
nodeMap
.
has
(
previousSnapshot
.
selectedNodeId
))
{
if
(
previousSnapshot
.
selectedNodeId
&&
this
.
hasNode
(
previousSnapshot
.
selectedNodeId
))
{
this
.
setSelectedNodeId
(
previousSnapshot
.
selectedNodeId
)
this
.
setSelectedNodeId
(
previousSnapshot
.
selectedNodeId
)
}
else
{
}
else
{
this
.
setSelectedNodeId
(
previousSnapshot
.
tree
.
id
)
this
.
setSelectedNodeId
(
previousSnapshot
.
tree
.
id
)
...
@@ -812,7 +819,7 @@ export const useEditorStore = defineStore('editor', {
...
@@ -812,7 +819,7 @@ export const useEditorStore = defineStore('editor', {
this
.
xmlTree
=
nextSnapshot
.
tree
this
.
xmlTree
=
nextSnapshot
.
tree
this
.
rebuildNodeMap
()
this
.
rebuildNodeMap
()
if
(
nextSnapshot
.
selectedNodeId
&&
this
.
nodeMap
.
has
(
nextSnapshot
.
selectedNodeId
))
{
if
(
nextSnapshot
.
selectedNodeId
&&
this
.
hasNode
(
nextSnapshot
.
selectedNodeId
))
{
this
.
setSelectedNodeId
(
nextSnapshot
.
selectedNodeId
)
this
.
setSelectedNodeId
(
nextSnapshot
.
selectedNodeId
)
}
else
{
}
else
{
this
.
setSelectedNodeId
(
nextSnapshot
.
tree
.
id
)
this
.
setSelectedNodeId
(
nextSnapshot
.
tree
.
id
)
...
...
src/views/editor/components/EditorPanel/components/EditAreaContextMenuModal/functionals/index.ts
View file @
e5785566
...
@@ -451,7 +451,7 @@ export function useEditAreaContextMenu(props: EditAreaContextMenuProps, emit: (e
...
@@ -451,7 +451,7 @@ export function useEditAreaContextMenu(props: EditAreaContextMenuProps, emit: (e
editorStore.deleteNode(realId)
editorStore.deleteNode(realId)
window.$message.success('节点删除成功')
window.$message.success('节点删除成功')
if (editorStore.selectedNodeId && editorStore.
nodeMap.has
(editorStore.selectedNodeId)) {
if (editorStore.selectedNodeId && editorStore.
hasNode
(editorStore.selectedNodeId)) {
activeNodeId.value = editorStore.selectedNodeId
activeNodeId.value = editorStore.selectedNodeId
} else {
} else {
emit('update:visible', false) // 若没有存活选中节点,则主动关闭当前弹窗
emit('update:visible', false) // 若没有存活选中节点,则主动关闭当前弹窗
...
...
src/views/editor/components/EditorPanel/index.vue
View file @
e5785566
...
@@ -168,9 +168,21 @@ const handleContextMenu = (e: MouseEvent) => {
...
@@ -168,9 +168,21 @@ const handleContextMenu = (e: MouseEvent) => {
const
isVirtualText
=
rawNodeId
.
includes
(
'-txt-'
)
const
isVirtualText
=
rawNodeId
.
includes
(
'-txt-'
)
const
realId
=
isVirtualText
?
rawNodeId
.
split
(
'-txt-'
)[
0
]
:
rawNodeId
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 链
// 向上回溯构建祖先节点 ID 链
const
ids
:
string
[]
=
[]
const
ids
:
string
[]
=
[]
if
(
isVirtualText
)
{
if
(
isVirtualText
&&
shouldKeepVirtualText
)
{
ids
.
push
(
rawNodeId
)
ids
.
push
(
rawNodeId
)
}
}
...
...
src/views/editor/components/EditorToolbar/components/CompareModal/index.vue
View file @
e5785566
...
@@ -168,8 +168,8 @@
...
@@ -168,8 +168,8 @@
</div>
</div>
<div
class=
"flex-1 flex gap-2 min-h-0"
>
<div
class=
"flex-1 flex gap-2 min-h-0"
>
<!-- 左:源工卡 XML(
将
屏蔽原生竖向滚动条,用自定义滚动条替代) -->
<!-- 左:源工卡 XML(
对比后
屏蔽原生竖向滚动条,用自定义滚动条替代) -->
<div
class=
"flex-1 min-w-0 h-full xml-compare-left flex relative"
>
<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"
>
<div
class=
"flex-1 min-w-0 h-full"
>
<CommonXmlEditor
<CommonXmlEditor
ref=
"leftEditorRef"
ref=
"leftEditorRef"
...
@@ -206,7 +206,7 @@
...
@@ -206,7 +206,7 @@
]"
]"
/>
/>
</
template
>
</
template
>
<!-- 可拖拽视口 thumb(有内容可滚动时才显示) -->
<!-- 可拖拽视口 thumb(
对比后
有内容可滚动时才显示) -->
<div
<div
v-if=
"compared && leftScrollerBound && isScrollable"
v-if=
"compared && leftScrollerBound && isScrollable"
class=
"absolute left-0 right-0 rounded-sm custom-scrollbar-thumb-v"
class=
"absolute left-0 right-0 rounded-sm custom-scrollbar-thumb-v"
...
@@ -233,7 +233,7 @@
...
@@ -233,7 +233,7 @@
</div>
</div>
<!-- 右:目标工卡 XML(同上) -->
<!-- 右:目标工卡 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"
>
<div
class=
"flex-1 min-w-0 h-full"
>
<CommonXmlEditor
<CommonXmlEditor
ref=
"rightEditorRef"
ref=
"rightEditorRef"
...
@@ -270,7 +270,7 @@
...
@@ -270,7 +270,7 @@
]"
]"
/>
/>
</
template
>
</
template
>
<!-- 可拖拽视口 thumb(有内容可滚动时才显示) -->
<!-- 可拖拽视口 thumb(
对比后
有内容可滚动时才显示) -->
<div
<div
v-if=
"compared && leftScrollerBound && isScrollable"
v-if=
"compared && leftScrollerBound && isScrollable"
class=
"absolute left-0 right-0 rounded-sm custom-scrollbar-thumb-v"
class=
"absolute left-0 right-0 rounded-sm custom-scrollbar-thumb-v"
...
@@ -603,9 +603,9 @@ defineExpose({
...
@@ -603,9 +603,9 @@ defineExpose({
border-color
:
var
(
--n-scrollbar-color-hover
,
rgba
(
128
,
128
,
128
,
0.5
))
!important
;
border-color
:
var
(
--n-scrollbar-color-hover
,
rgba
(
128
,
128
,
128
,
0.5
))
!important
;
}
}
//
隐藏
CodeMirror
原生竖向滚动条(用自定义滚动条替代)
//
对比后隐藏
CodeMirror
原生竖向滚动条(用自定义滚动条替代),未对比时保留原生滚动条
.xml-compare-left
,
.xml-compare-left
.is-compared
,
.xml-compare-right
{
.xml-compare-right
.is-compared
{
:deep(.cm-scroller)
{
:deep(.cm-scroller)
{
scrollbar-width
:
none
;
scrollbar-width
:
none
;
&::-webkit-scrollbar
{
&::-webkit-scrollbar
{
...
...
src/views/editor/components/EditorToolbar/index.vue
View file @
e5785566
...
@@ -8,16 +8,20 @@
...
@@ -8,16 +8,20 @@
<div
class=
"flex items-center bg-fill-2 p-0.5 rounded border border-divider"
>
<div
class=
"flex items-center bg-fill-2 p-0.5 rounded border border-divider"
>
<button
<button
type=
"button"
type=
"button"
class=
"px-2.5 h-6 text-xs font-semibold rounded transition-all select-none focus:outline-none"
class=
"px-2.5 h-6 text-xs font-semibold rounded transition-[background-color,color] 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'"
: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"
@
click=
"insertBelow = false"
>
>
内部
内部
</button>
</button>
<button
<button
type=
"button"
type=
"button"
class=
"px-2.5 h-6 text-xs font-semibold rounded transition-all select-none focus:outline-none"
class=
"px-2.5 h-6 text-xs font-semibold rounded transition-[background-color,color] select-none focus:outline-none hover:bg-fill-3"
:class=
"insertBelow ? 'bg-card text-primary shadow-[0_1px_2px_rgba(0,0,0,0.06)]' : 'text-color2 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"
@
click=
"insertBelow = true"
>
>
下方
下方
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment