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
233926ec
Commit
233926ec
authored
Jul 30, 2026
by
pangchong
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 节点操作新增顺序调整
parent
158ac826
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
218 additions
and
42 deletions
+218
-42
src/utils/dtdManager.ts
+0
-0
src/views/editor/components/EditorPanel/components/EditAreaContextMenuModal/functionals/index.ts
+83
-0
src/views/editor/components/EditorPanel/components/EditAreaContextMenuModal/index.vue
+15
-1
src/views/editor/components/NodeTree/functionals/index.ts
+120
-41
No files found.
src/utils/dtdManager.ts
View file @
233926ec
This diff is collapsed.
Click to expand it.
src/views/editor/components/EditorPanel/components/EditAreaContextMenuModal/functionals/index.ts
View file @
233926ec
...
...
@@ -269,6 +269,31 @@ export function useEditAreaContextMenu(props: EditAreaContextMenuProps, emit: (e
return
canDeleteChild
(
mapped
.
parent
.
tagName
,
mapped
.
node
.
tagName
,
existingTags
)
})
// 是否允许上移(DTD 顺序约束)
const
canMoveUpActive
=
computed
(()
=>
{
const
nodeId
=
activeNodeId
.
value
if
(
!
nodeId
)
return
false
const
isVirtual
=
nodeId
.
includes
(
'-txt-'
)
const
realId
=
isVirtual
?
nodeId
.
split
(
'-txt-'
)[
0
]
:
nodeId
const
mapped
=
editorStore
.
nodeMap
.
get
(
realId
)
if
(
!
mapped
||
!
mapped
.
parent
)
return
false
const
parent
=
isVirtual
?
mapped
.
node
:
mapped
.
parent
return
checkCanMoveNode
(
parent
,
nodeId
,
isVirtual
).
canMoveUp
})
// 是否允许下移(DTD 顺序约束)
const
canMoveDownActive
=
computed
(()
=>
{
const
nodeId
=
activeNodeId
.
value
if
(
!
nodeId
)
return
false
const
isVirtual
=
nodeId
.
includes
(
'-txt-'
)
const
realId
=
isVirtual
?
nodeId
.
split
(
'-txt-'
)[
0
]
:
nodeId
const
mapped
=
editorStore
.
nodeMap
.
get
(
realId
)
if
(
!
mapped
||
!
mapped
.
parent
)
return
false
const
parent
=
isVirtual
?
mapped
.
node
:
mapped
.
parent
return
checkCanMoveNode
(
parent
,
nodeId
,
isVirtual
).
canMoveDown
})
// 根据 DTD 获取允许添加的子标签列表
const
allowedChildrenList
=
computed
<
string
[]
>
(()
=>
{
const
node
=
activeNode
.
value
...
...
@@ -509,6 +534,56 @@ export function useEditAreaContextMenu(props: EditAreaContextMenuProps, emit: (e
addNodeAllowedTags
.
value
=
insertableParentList
.
value
addNodeVisible
.
value
=
true
}
// 11. 上移节点
else
if
(
actionName
===
'moveUp'
)
{
const
mapped2
=
editorStore
.
nodeMap
.
get
(
realId
)
if
(
!
mapped2
||
!
mapped2
.
parent
)
return
const
parent
=
isVirtual
?
mapped2
.
node
:
mapped2
.
parent
const
nodeIdToMove
=
activeNodeId
.
value
||
realId
const
{
canMoveUp
}
=
checkCanMoveNode
(
parent
,
nodeIdToMove
,
isVirtual
)
if
(
!
canMoveUp
)
{
window
.
$message
.
warning
(
'已在最顶部或 DTD 顺序不支持上移'
)
return
}
editorStore
.
saveSnapshot
()
const
success
=
moveNodeInParent
(
parent
,
nodeIdToMove
,
'up'
,
isVirtual
)
if
(
success
)
{
editorStore
.
rebuildNodeMap
()
if
(
!
isVirtual
)
{
editorStore
.
setSelectedNodeId
(
realId
)
}
window
.
$message
.
success
(
'上移成功'
)
}
else
{
window
.
$message
.
warning
(
'上移失败'
)
}
}
// 12. 下移节点
else
if
(
actionName
===
'moveDown'
)
{
const
mapped2
=
editorStore
.
nodeMap
.
get
(
realId
)
if
(
!
mapped2
||
!
mapped2
.
parent
)
return
const
parent
=
isVirtual
?
mapped2
.
node
:
mapped2
.
parent
const
nodeIdToMove
=
activeNodeId
.
value
||
realId
const
{
canMoveDown
}
=
checkCanMoveNode
(
parent
,
nodeIdToMove
,
isVirtual
)
if
(
!
canMoveDown
)
{
window
.
$message
.
warning
(
'已在最底部或 DTD 顺序不支持下移'
)
return
}
editorStore
.
saveSnapshot
()
const
success
=
moveNodeInParent
(
parent
,
nodeIdToMove
,
'down'
,
isVirtual
)
if
(
success
)
{
editorStore
.
rebuildNodeMap
()
if
(
!
isVirtual
)
{
editorStore
.
setSelectedNodeId
(
realId
)
}
window
.
$message
.
success
(
'下移成功'
)
}
else
{
window
.
$message
.
warning
(
'下移失败'
)
}
}
}
const
pasteXmlOptions
=
computed
(()
=>
[
...
...
@@ -523,6 +598,11 @@ export function useEditAreaContextMenu(props: EditAreaContextMenuProps, emit: (e
{
label
:
'插入到下方'
,
key
:
'insertAfter'
,
disabled
:
!
insertableParentList
.
value
.
length
}
])
const
editOrderOptions
=
computed
(()
=>
[
{
label
:
'上移'
,
key
:
'moveUp'
,
disabled
:
!
canMoveUpActive
.
value
},
{
label
:
'下移'
,
key
:
'moveDown'
,
disabled
:
!
canMoveDownActive
.
value
}
])
const
pasteNodeOptions
=
computed
(()
=>
[
{
label
:
'粘贴到上方'
,
key
:
'pasteNodeAbove'
,
disabled
:
!
insertableParentList
.
value
.
length
},
{
label
:
'粘贴到下方'
,
key
:
'pasteNodeBelow'
,
disabled
:
!
insertableParentList
.
value
.
length
},
...
...
@@ -538,6 +618,8 @@ export function useEditAreaContextMenu(props: EditAreaContextMenuProps, emit: (e
isVirtualLayoutNode
,
canTranslateActive
,
canDeleteActive
,
canMoveUpActive
,
canMoveDownActive
,
allowedChildrenList
,
insertableParentList
,
hasCopyCache
,
...
...
@@ -545,6 +627,7 @@ export function useEditAreaContextMenu(props: EditAreaContextMenuProps, emit: (e
runAction
,
pasteXmlOptions
,
editStructureOptions
,
editOrderOptions
,
pasteNodeOptions
}
}
src/views/editor/components/EditorPanel/components/EditAreaContextMenuModal/index.vue
View file @
233926ec
...
...
@@ -74,7 +74,7 @@
</n-dropdown>
</div>
<div
class=
"grid grid-cols-
3
gap-2"
>
<div
class=
"grid grid-cols-
4
gap-2"
>
<!-- 4. 删除节点 -->
<CommonButton
type=
"error"
secondary
block
:disabled=
"!canDeleteActive"
@
click=
"runAction('deleteNode')"
>
<
template
#
icon
>
...
...
@@ -93,6 +93,16 @@
</CommonButton>
</n-dropdown>
<!-- 5b. 编辑顺序 -->
<n-dropdown
trigger=
"click"
:options=
"editOrderOptions"
:disabled=
"isTextNode || (!canMoveUpActive && !canMoveDownActive)"
@
select=
"runAction"
>
<CommonButton
type=
"warning"
secondary
block
:disabled=
"isTextNode || (!canMoveUpActive && !canMoveDownActive)"
>
<
template
#
icon
>
<n-icon><swap-vertical-outline
/></n-icon>
</
template
>
编辑顺序
</CommonButton>
</n-dropdown>
<!-- 6. 粘贴节点 (DTD) -->
<n-dropdown
trigger=
"click"
:options=
"pasteNodeOptions"
:disabled=
"!hasCopyCache"
@
select=
"runAction"
>
<CommonButton
type=
"warning"
secondary
block
:disabled=
"!hasCopyCache"
>
...
...
@@ -160,6 +170,7 @@ import {
ClipboardOutline
,
TrashOutline
,
BuildOutline
,
SwapVerticalOutline
,
LanguageOutline
,
CodeWorkingOutline
,
EyeOutline
,
...
...
@@ -187,11 +198,14 @@ const {
isVirtualLayoutNode
,
canTranslateActive
,
canDeleteActive
,
canMoveUpActive
,
canMoveDownActive
,
hasCopyCache
,
isTranslating
,
runAction
,
pasteXmlOptions
,
editStructureOptions
,
editOrderOptions
,
pasteNodeOptions
}
=
useEditAreaContextMenu
(
props
,
(
event
,
val
)
=>
{
if
(
event
===
'update:visible'
)
{
...
...
src/views/editor/components/NodeTree/functionals/index.ts
View file @
233926ec
...
...
@@ -874,14 +874,15 @@ export function useNodeTree(
return
options
}
// 查看XML
// ================= 组 1:查看与辅助 =================
// 1. 查看XML
options
.
push
({
label
:
'查看XML'
,
key
:
'viewXml'
,
icon
:
icon
(
CodeWorkingOutline
)
})
// 查看规则(虚拟文本节点不需要)
//
2.
查看规则(虚拟文本节点不需要)
if
(
!
isVirtual
)
{
options
.
push
({
label
:
'查看规则'
,
...
...
@@ -890,14 +891,7 @@ export function useNodeTree(
})
}
// 编辑节点(属性/内容)
options
.
push
({
label
:
isVirtual
?
'编辑文本'
:
'编辑节点'
,
key
:
'editNode'
,
icon
:
icon
(
CreateOutline
)
})
// 智能翻译
// 3. 智能翻译
const
enSourceNode
=
getEnglishSourceNode
(
node
,
parent
)
if
(
enSourceNode
&&
!
isVirtual
&&
hasTranslatableText
(
enSourceNode
))
{
options
.
push
({
...
...
@@ -907,21 +901,17 @@ export function useNodeTree(
})
}
// 复制节点
// ================= 组 2:复制 / 粘贴 / 暂存 =================
options
.
push
({
type
:
'divider'
,
key
:
'd1'
})
// 4. 复制节点
options
.
push
({
label
:
'复制节点'
,
key
:
'copyNode'
,
icon
:
icon
(
CopyOutline
)
})
// 本地暂存
options
.
push
({
label
:
'本地暂存'
,
key
:
'localStash'
,
icon
:
icon
(
ArchiveOutline
)
})
// 粘贴节点(有缓存时显示)
// 5. 粘贴节点(有缓存时显示)
if
(
copyNodeCache
.
value
)
{
const
pasteChildren
:
DropdownOption
[]
=
isVirtual
?
[
...
...
@@ -941,7 +931,7 @@ export function useNodeTree(
})
}
// 粘贴XML
//
6.
粘贴XML
const
pasteFragmentChildren
:
DropdownOption
[]
=
isVirtual
?
[
{
label
:
'粘贴到上方'
,
key
:
'pasteFragmentAbove'
,
icon
:
icon
(
ClipboardOutline
),
disabled
:
!
parent
},
...
...
@@ -959,28 +949,24 @@ export function useNodeTree(
children
:
pasteFragmentChildren
})
// 删除节点(受 DTD 约束,文本节点和分页符节点永远允许删除)
if
(
parent
)
{
const
deletable
=
isVirtual
||
VIRTUAL_LAYOUT_TAGS
.
includes
(
node
.
tagName
)
?
true
:
(()
=>
{
const
existingTags
=
parent
.
children
.
filter
((
c
)
=>
!
VIRTUAL_LAYOUT_TAGS
.
includes
(
c
.
tagName
)).
map
((
c
)
=>
c
.
tagName
)
// 位置特许规则:父级中最后一个子节点(按位置)允许删除
if
(
existingTags
.
length
>
0
&&
existingTags
[
existingTags
.
length
-
1
]
===
node
.
tagName
)
{
return
true
}
return
canDeleteChild
(
parent
.
tagName
,
node
.
tagName
,
existingTags
)
})()
options
.
push
({
label
:
'删除节点'
,
key
:
'deleteNode'
,
icon
:
icon
(
TrashOutline
),
disabled
:
!
deletable
})
}
// 7. 本地暂存
options
.
push
({
label
:
'本地暂存'
,
key
:
'localStash'
,
icon
:
icon
(
ArchiveOutline
)
})
// ================= 组 3:节点编辑与结构调整 =================
options
.
push
({
type
:
'divider'
,
key
:
'd2'
})
// 编辑结构子菜单
// 8. 编辑节点(属性/内容)
options
.
push
({
label
:
isVirtual
?
'编辑文本'
:
'编辑节点'
,
key
:
'editNode'
,
icon
:
icon
(
CreateOutline
)
})
// 9. 编辑结构
const
structureChildren
:
DropdownOption
[]
=
[]
// 添加子节点(文本节点不能拥有子节点)
...
...
@@ -1024,6 +1010,33 @@ export function useNodeTree(
})
}
// 10. 编辑顺序(上移 / 下移)
if
(
parent
)
{
const
{
canMoveUp
,
canMoveDown
}
=
checkCanMoveNode
(
parent
,
nodeId
,
isVirtual
)
options
.
push
({
label
:
'编辑顺序'
,
key
:
'editOrder'
,
icon
:
icon
(
ArrowUpOutline
),
children
:
[
{
label
:
'上移'
,
key
:
'moveUp'
,
icon
:
icon
(
ArrowUpOutline
),
disabled
:
!
canMoveUp
},
{
label
:
'下移'
,
key
:
'moveDown'
,
icon
:
icon
(
ArrowDownOutline
),
disabled
:
!
canMoveDown
}
]
})
}
// ================= 组 4:更多与删除 =================
options
.
push
({
type
:
'divider'
,
key
:
'd3'
})
// 保存为模板
options
.
push
({
label
:
'保存为模板'
,
...
...
@@ -1031,6 +1044,27 @@ export function useNodeTree(
icon
:
icon
(
SaveOutline
)
})
// 删除节点(受 DTD 约束,文本节点和分页符节点永远允许删除)
if
(
parent
)
{
const
deletable
=
isVirtual
||
VIRTUAL_LAYOUT_TAGS
.
includes
(
node
.
tagName
)
?
true
:
(()
=>
{
const
existingTags
=
parent
.
children
.
filter
((
c
)
=>
!
VIRTUAL_LAYOUT_TAGS
.
includes
(
c
.
tagName
)).
map
((
c
)
=>
c
.
tagName
)
// 位置特许规则:父级中最后一个子节点(按位置)允许删除
if
(
existingTags
.
length
>
0
&&
existingTags
[
existingTags
.
length
-
1
]
===
node
.
tagName
)
{
return
true
}
return
canDeleteChild
(
parent
.
tagName
,
node
.
tagName
,
existingTags
)
})()
options
.
push
({
label
:
'删除节点'
,
key
:
'deleteNode'
,
icon
:
icon
(
TrashOutline
),
disabled
:
!
deletable
})
}
return
options
}
...
...
@@ -1406,6 +1440,51 @@ export function useNodeTree(
}
break
}
// ── 上移节点 ──
case
'moveUp'
:
{
if
(
!
parent
)
break
const
{
canMoveUp
}
=
checkCanMoveNode
(
parent
,
nodeId
,
isVirtual
)
if
(
!
canMoveUp
)
{
window
.
$message
?.
warning
(
'已在最顶部或 DTD 顺序不支持上移'
)
break
}
editorStore
.
saveSnapshot
()
const
success
=
moveNodeInParent
(
parent
,
nodeId
,
'up'
,
isVirtual
)
if
(
success
)
{
editorStore
.
rebuildNodeMap
()
if
(
!
isVirtual
)
{
editorStore
.
setSelectedNodeId
(
realNodeId
)
}
window
.
$message
?.
success
(
'上移成功'
)
}
else
{
window
.
$message
?.
warning
(
'上移失败'
)
}
break
}
// ── 下移节点 ──
case
'moveDown'
:
{
if
(
!
parent
)
break
const
{
canMoveDown
}
=
checkCanMoveNode
(
parent
,
nodeId
,
isVirtual
)
if
(
!
canMoveDown
)
{
window
.
$message
?.
warning
(
'已在最底部或 DTD 顺序不支持下移'
)
break
}
editorStore
.
saveSnapshot
()
const
success
=
moveNodeInParent
(
parent
,
nodeId
,
'down'
,
isVirtual
)
if
(
success
)
{
editorStore
.
rebuildNodeMap
()
if
(
!
isVirtual
)
{
editorStore
.
setSelectedNodeId
(
realNodeId
)
}
window
.
$message
?.
success
(
'下移成功'
)
}
else
{
window
.
$message
?.
warning
(
'下移失败'
)
}
break
}
}
}
...
...
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