Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
I
internal_regulation_manager_new
Project
Project
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
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
David Yang
internal_regulation_manager_new
Commits
ba3d23a8
Commit
ba3d23a8
authored
Oct 07, 2026
by
drigle
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 支持拓扑节点删除恢复并修正右键菜单定位
parent
e7a7bf9d
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
163 additions
and
1 deletion
+163
-1
graph-removal.test.mjs
scripts/graph-removal.test.mjs
+105
-0
RegulationGraph.jsx
src/pages/internal_regulation_manager/RegulationGraph.jsx
+0
-0
graphRemoval.js
src/pages/internal_regulation_manager/graphRemoval.js
+53
-0
internal-regulation.business-flow.md
...l_regulation_manager/internal-regulation.business-flow.md
+5
-1
No files found.
scripts/graph-removal.test.mjs
0 → 100644
View file @
ba3d23a8
import
assert
from
'node:assert/strict'
;
import
test
from
'node:test'
;
import
{
getGraphBranchIds
,
isGraphDeleteShortcut
,
setGraphBranchRemoved
}
from
'../src/pages/internal_regulation_manager/graphRemoval.js'
;
import
{
getNextGraphRecordNode
}
from
'../src/pages/internal_regulation_manager/graphSelection.js'
;
import
{
serializeGraph
}
from
'../src/services/regulationGraphData.js'
;
function
exampleGraph
()
{
const
nodes
=
[
{
id
:
'root'
,
type
:
'rootNode'
,
parent
:
null
},
{
id
:
'branch'
,
type
:
'groupNode'
,
parent
:
'root'
,
content
:
'流程备注'
},
{
id
:
'child'
,
type
:
'regulationNode'
,
parent
:
'branch'
,
relatedRegulation
:
'same-regulation'
},
{
id
:
'grandchild'
,
type
:
'regulationNode'
,
parent
:
'child'
,
relatedReviseRegulation
:
'new-plan'
},
{
id
:
'sibling'
,
type
:
'regulationNode'
,
parent
:
'root'
,
relatedRegulation
:
'same-regulation'
},
].
map
((
node
)
=>
({
...
node
,
removed
:
false
,
data
:
{
removed
:
false
}
}));
return
{
nodes
,
edges
:
nodes
.
filter
((
node
)
=>
node
.
parent
).
map
((
node
)
=>
({
source
:
node
.
parent
,
target
:
node
.
id
}))
};
}
test
(
'deleting a branch includes descendants but preserves parent and sibling occurrences of the same regulation'
,
()
=>
{
const
original
=
exampleGraph
();
const
removed
=
setGraphBranchRemoved
(
original
,
'branch'
,
true
);
assert
.
deepEqual
(
removed
.
nodes
.
filter
((
node
)
=>
node
.
removed
).
map
((
node
)
=>
node
.
id
),
[
'branch'
,
'child'
,
'grandchild'
]);
assert
.
equal
(
removed
.
nodes
[
0
],
original
.
nodes
[
0
]);
assert
.
equal
(
removed
.
nodes
.
at
(
-
1
),
original
.
nodes
.
at
(
-
1
));
assert
.
equal
(
removed
.
edges
,
original
.
edges
);
assert
.
equal
(
removed
.
nodes
[
1
].
content
,
'流程备注'
);
assert
.
equal
(
removed
.
nodes
[
2
].
relatedRegulation
,
'same-regulation'
);
assert
.
equal
(
removed
.
nodes
[
3
].
relatedReviseRegulation
,
'new-plan'
);
assert
.
equal
(
original
.
nodes
.
some
((
node
)
=>
node
.
removed
||
node
.
data
.
removed
),
false
);
removed
.
nodes
.
forEach
((
node
)
=>
assert
.
equal
(
node
.
data
.
removed
,
node
.
removed
));
});
test
(
'deleting a leaf does not remove the entire regulation or its other occurrence'
,
()
=>
{
const
original
=
exampleGraph
();
const
removed
=
setGraphBranchRemoved
(
original
,
'grandchild'
,
true
);
assert
.
deepEqual
(
removed
.
nodes
.
filter
((
node
)
=>
node
.
removed
).
map
((
node
)
=>
node
.
id
),
[
'grandchild'
]);
const
record
=
{
_id
:
'same-regulation'
};
assert
.
equal
(
getNextGraphRecordNode
(
record
,
removed
.
nodes
,
'child'
,
record
.
_id
).
id
,
'sibling'
);
});
test
(
'record navigation skips removed occurrences while other occurrences remain'
,
()
=>
{
const
removed
=
setGraphBranchRemoved
(
exampleGraph
(),
'child'
,
true
);
const
record
=
{
_id
:
'same-regulation'
};
assert
.
equal
(
getNextGraphRecordNode
(
record
,
removed
.
nodes
,
'child'
,
record
.
_id
).
id
,
'sibling'
);
assert
.
equal
(
getNextGraphRecordNode
(
record
,
removed
.
nodes
,
'sibling'
,
record
.
_id
).
id
,
'sibling'
);
});
test
(
'saving a deleted branch preserves its removal state, hierarchy and links for reload and recovery'
,
()
=>
{
const
original
=
exampleGraph
();
const
saved
=
serializeGraph
(
setGraphBranchRemoved
(
original
,
'branch'
,
true
));
const
reloaded
=
JSON
.
parse
(
JSON
.
stringify
(
saved
));
assert
.
deepEqual
(
reloaded
.
nodes
.
filter
((
node
)
=>
node
.
removed
).
map
((
node
)
=>
node
.
id
),
[
'branch'
,
'child'
,
'grandchild'
]);
assert
.
equal
(
reloaded
.
nodes
[
3
].
parent
,
'child'
);
assert
.
equal
(
reloaded
.
nodes
[
2
].
data
.
related_regulation
,
'same-regulation'
);
assert
.
equal
(
reloaded
.
edges
.
length
,
original
.
edges
.
length
);
const
restored
=
setGraphBranchRemoved
(
reloaded
,
'branch'
,
false
);
assert
.
equal
(
restored
.
nodes
.
some
((
node
)
=>
node
.
removed
||
node
.
data
.
removed
),
false
);
assert
.
deepEqual
(
restored
.
edges
,
reloaded
.
edges
);
});
test
(
'restoring a descendant restores its parent path without restoring deleted sibling branches'
,
()
=>
{
const
removed
=
setGraphBranchRemoved
(
exampleGraph
(),
'root'
,
true
);
const
restored
=
setGraphBranchRemoved
(
removed
,
'grandchild'
,
false
);
assert
.
deepEqual
(
restored
.
nodes
.
filter
((
node
)
=>
!
node
.
removed
).
map
((
node
)
=>
node
.
id
),
[
'root'
,
'branch'
,
'child'
,
'grandchild'
]);
assert
.
equal
(
restored
.
nodes
.
at
(
-
1
).
removed
,
true
);
assert
.
equal
(
removed
.
nodes
.
every
((
node
)
=>
node
.
removed
),
true
);
});
test
(
'numeric IDs and legacy data.parent still identify the correct branch'
,
()
=>
{
const
graph
=
{
nodes
:
[{
id
:
1
},
{
id
:
2
,
data
:
{
parent
:
1
}
},
{
id
:
3
}],
edges
:
[]
};
assert
.
deepEqual
([...
getGraphBranchIds
(
graph
.
nodes
,
'1'
)],
[
'1'
,
'2'
]);
const
removed
=
setGraphBranchRemoved
(
graph
,
1
,
true
);
assert
.
deepEqual
(
removed
.
nodes
.
filter
((
node
)
=>
node
.
removed
).
map
((
node
)
=>
node
.
id
),
[
1
,
2
]);
});
test
(
'missing selections and missing nodes leave the graph intact'
,
()
=>
{
const
graph
=
exampleGraph
();
for
(
const
id
of
[
null
,
undefined
,
'missing'
])
{
assert
.
equal
(
getGraphBranchIds
(
graph
.
nodes
,
id
).
size
,
0
);
assert
.
equal
(
setGraphBranchRemoved
(
graph
,
id
,
true
),
graph
);
}
assert
.
equal
(
getGraphBranchIds
([{
id
:
'null'
}],
null
).
size
,
0
);
});
test
(
'branch traversal and restoring an ancestor path terminate with cyclic parents'
,
()
=>
{
const
graph
=
{
nodes
:
[{
id
:
'a'
,
parent
:
'b'
},
{
id
:
'b'
,
parent
:
'a'
},
{
id
:
'other'
}],
edges
:
[]
};
const
removed
=
setGraphBranchRemoved
(
graph
,
'a'
,
true
);
assert
.
deepEqual
(
removed
.
nodes
.
filter
((
node
)
=>
node
.
removed
).
map
((
node
)
=>
node
.
id
),
[
'a'
,
'b'
]);
const
restored
=
setGraphBranchRemoved
(
removed
,
'b'
,
false
);
assert
.
equal
(
restored
.
nodes
.
some
((
node
)
=>
node
.
removed
),
false
);
});
test
(
'only unmodified Delete or Mac backward-delete presses trigger deletion'
,
()
=>
{
const
event
=
{
key
:
'Delete'
};
assert
.
equal
(
isGraphDeleteShortcut
(
event
),
true
);
assert
.
equal
(
isGraphDeleteShortcut
({
key
:
'Backspace'
}),
true
);
for
(
const
key
of
[
'Enter'
,
'Escape'
])
assert
.
equal
(
isGraphDeleteShortcut
({
key
}),
false
);
for
(
const
flag
of
[
'defaultPrevented'
,
'repeat'
,
'isComposing'
,
'ctrlKey'
,
'metaKey'
,
'altKey'
,
'shiftKey'
])
{
assert
.
equal
(
isGraphDeleteShortcut
({
...
event
,
[
flag
]:
true
}),
false
,
flag
);
}
});
test
(
'editable fields or overlay targets block the shortcut while canvas targets permit it'
,
()
=>
{
assert
.
equal
(
isGraphDeleteShortcut
({
key
:
'Delete'
,
target
:
{
closest
:
()
=>
({})
}
}),
false
);
assert
.
equal
(
isGraphDeleteShortcut
({
key
:
'Delete'
,
target
:
{
closest
:
()
=>
null
}
}),
true
);
});
src/pages/internal_regulation_manager/RegulationGraph.jsx
View file @
ba3d23a8
This diff is collapsed.
Click to expand it.
src/pages/internal_regulation_manager/graphRemoval.js
0 → 100644
View file @
ba3d23a8
export
function
getGraphBranchIds
(
nodes
,
nodeId
)
{
if
(
nodeId
===
null
||
nodeId
===
undefined
)
return
new
Set
();
const
id
=
String
(
nodeId
);
if
(
!
nodes
.
some
((
node
)
=>
String
(
node
.
id
)
===
id
))
return
new
Set
();
const
childrenByParent
=
new
Map
();
nodes
.
forEach
((
node
)
=>
{
const
parent
=
node
.
parent
??
node
.
data
?.
parent
;
if
(
parent
===
null
||
parent
===
undefined
)
return
;
const
parentId
=
String
(
parent
);
const
children
=
childrenByParent
.
get
(
parentId
)
||
[];
children
.
push
(
String
(
node
.
id
));
childrenByParent
.
set
(
parentId
,
children
);
});
const
branchIds
=
new
Set
([
id
]);
const
queue
=
[
id
];
for
(
let
index
=
0
;
index
<
queue
.
length
;
index
+=
1
)
{
(
childrenByParent
.
get
(
queue
[
index
])
||
[]).
forEach
((
childId
)
=>
{
if
(
branchIds
.
has
(
childId
))
return
;
branchIds
.
add
(
childId
);
queue
.
push
(
childId
);
});
}
return
branchIds
;
}
export
function
setGraphBranchRemoved
(
graph
,
nodeId
,
removed
)
{
const
affectedIds
=
getGraphBranchIds
(
graph
.
nodes
,
nodeId
);
if
(
!
affectedIds
.
size
)
return
graph
;
if
(
!
removed
)
{
// A restored descendant must have a visible parent path, including when
// the user restores it directly from the removed-node list.
const
nodesById
=
new
Map
(
graph
.
nodes
.
map
((
node
)
=>
[
String
(
node
.
id
),
node
]));
let
node
=
nodesById
.
get
(
String
(
nodeId
));
while
(
node
)
{
const
parentId
=
node
.
parent
??
node
.
data
?.
parent
;
if
(
parentId
===
null
||
parentId
===
undefined
||
affectedIds
.
has
(
String
(
parentId
)))
break
;
affectedIds
.
add
(
String
(
parentId
));
node
=
nodesById
.
get
(
String
(
parentId
));
}
}
return
{
...
graph
,
nodes
:
graph
.
nodes
.
map
((
node
)
=>
affectedIds
.
has
(
String
(
node
.
id
))
?
{
...
node
,
removed
,
data
:
{
...
node
.
data
,
removed
}
}
:
node
),
};
}
export
function
isGraphDeleteShortcut
(
event
)
{
if
(
!
[
'Delete'
,
'Backspace'
].
includes
(
event
.
key
)
||
event
.
defaultPrevented
||
event
.
repeat
||
event
.
isComposing
||
event
.
ctrlKey
||
event
.
metaKey
||
event
.
altKey
||
event
.
shiftKey
)
return
false
;
return
!
event
.
target
?.
closest
?.(
'input, textarea, select, [contenteditable]:not([contenteditable="false"]), [role="textbox"], [role="searchbox"], [role="combobox"], [role="spinbutton"], [role="dialog"], [role="alertdialog"], [role="menu"], [role="listbox"]'
);
}
src/pages/internal_regulation_manager/internal-regulation.business-flow.md
View file @
ba3d23a8
...
@@ -40,13 +40,17 @@
...
@@ -40,13 +40,17 @@
-
“全部 / 待分类”列表首次点击制度记录时定位第一个关联节点,连续点击同一条记录时按图谱节点顺序循环定位(A → B → C → A);只有一个关联节点时保持定位该节点。记录
`_id/name`
的直接关联节点合并后参与循环,只有没有直接关联时才按
`metadata.origin_regulation`
查找。优先在未移除节点中循环;全部关联节点已移除时才在已移除节点中循环,并自动开启“显示已移除”。点击其他制度、选中画布或“节点 / 删除节点”列表、重新加载或切换拓扑版本后,再点击该制度从第一个关联节点重新开始。循环过程仍只高亮点击的制度记录,自动展开目标祖先、保持缩放并打开详情;快速连续点击时取消尚未执行的旧定位回调,画布最终定位到最新选中的节点。
-
“全部 / 待分类”列表首次点击制度记录时定位第一个关联节点,连续点击同一条记录时按图谱节点顺序循环定位(A → B → C → A);只有一个关联节点时保持定位该节点。记录
`_id/name`
的直接关联节点合并后参与循环,只有没有直接关联时才按
`metadata.origin_regulation`
查找。优先在未移除节点中循环;全部关联节点已移除时才在已移除节点中循环,并自动开启“显示已移除”。点击其他制度、选中画布或“节点 / 删除节点”列表、重新加载或切换拓扑版本后,再点击该制度从第一个关联节点重新开始。循环过程仍只高亮点击的制度记录,自动展开目标祖先、保持缩放并打开详情;快速连续点击时取消尚未执行的旧定位回调,画布最终定位到最新选中的节点。
-
当前所查看的拓扑版本没有关联节点时,点击制度记录显示 HeroUI 提示“该制度在当前拓扑图中暂无关联节点。”,保持画布、筛选和已选节点状态;待分类列表仍支持拖拽归类。
-
当前所查看的拓扑版本没有关联节点时,点击制度记录显示 HeroUI 提示“该制度在当前拓扑图中暂无关联节点。”,保持画布、筛选和已选节点状态;待分类列表仍支持拖拽归类。
-
画布内单击节点只选中节点并打开右侧详情,保持当前缩放和画布位置,不自动放大或居中;左侧列表点击和拖入制度后的定位可平移到目标节点,但保持操作前的缩放比例。“恢复视图”及缩放控件仍由用户主动调整视角。
-
画布内单击节点只选中节点并打开右侧详情,保持当前缩放和画布位置,不自动放大或居中;左侧列表点击和拖入制度后的定位可平移到目标节点,但保持操作前的缩放比例。“恢复视图”及缩放控件仍由用户主动调整视角。
-
当前版本选中节点后,可按
`Delete`
(兼容 Mac 的
`⌫ / Backspace`
)或点击右侧详情“节点操作 → 删除节点”。删除按节点 ID 将该节点及全部下级节点的
`removed`
、
`data.removed`
设为
`true`
,保留节点引用、父子层级及连线;同一制度在其他分支的节点不受影响。删除后清除节点和记录选择、取消旧定位回调与拖入预览、移除被删除的流程筛选路径,并关闭“显示已移除”,让删除分支立即从画布隐藏;节点保留在支持搜索的“删除节点”列表。只有已删除关联的制度重新进入“待分类”,仍有未移除关联节点的制度保持已分类。
-
在“删除节点”列表选择节点后,右侧详情显示“已删除”,提供“恢复节点”或“恢复节点及子节点”。恢复将所选分支及其祖先的
`removed`
、
`data.removed`
设为
`false`
,保留其他分支的删除状态;自动展开目标祖先、保持缩放定位并打开详情。删除和恢复均只修改当前内存图谱,点击“保存”后通过
`POST /record/import`
写入
`internal_regulation_graph.metadata.graph_info`
;不删除制度或修订计划 Record,保存失败保留修改。
-
删除快捷键仅对当前页面已选中且未删除的节点生效;输入框、备注、搜索框、可编辑区域、选择器、菜单和弹窗内的
`Delete / Backspace`
保持原有编辑行为,带修饰键、输入法组合中或长按重复事件不触发节点删除。归档版本、加载/导入/保存期间、节点编辑弹窗打开时禁用删除和恢复;已删除节点不重复删除。点击画布空白处或详情“清除选择”会同时清除节点和记录选择,后续删除快捷键不操作之前的节点。React Flow 默认删除快捷键关闭,统一由上述逻辑处理。
-
拓扑页高度锁定为当前视口可用高度,工具栏和画布保持在当前屏幕内;节点列表和详情区域在容器内部滚动,页面外层不因节点数量增长而整体向下延伸。
-
拓扑页高度锁定为当前视口可用高度,工具栏和画布保持在当前屏幕内;节点列表和详情区域在容器内部滚动,页面外层不因节点数量增长而整体向下延伸。
-
工具栏支持现行制度显示、已移除节点显示、现行/待新增/待修订/待废止筛选、当前版本和归档版本切换;归档版本只读,不能导入或保存。
-
工具栏支持现行制度显示、已移除节点显示、现行/待新增/待修订/待废止筛选、当前版本和归档版本切换;归档版本只读,不能导入或保存。
-
流程筛选使用多层级联路径,不将全部节点平铺到单层下拉;用户从第一层流程开始逐级展开并勾选路径。筛选保留选中路径、路径祖先和最后一个节点的子孙,不会因保留根节点路径而放行同一父级下的其他流程分支。
-
流程筛选使用多层级联路径,不将全部节点平铺到单层下拉;用户从第一层流程开始逐级展开并勾选路径。筛选保留选中路径、路径祖先和最后一个节点的子孙,不会因保留根节点路径而放行同一父级下的其他流程分支。
-
“导入 / 导出 / 保存”是图谱级操作,常驻拓扑容器右上角,与节点详情共用右侧面板;操作栏在顶部,三个按钮等宽横向排列、统一高度与间距,图标和文字保持同一行;导出使用 HeroUI Button 作为下拉触发器,保存保留主操作样式。下方节点详情以分隔线隔开并独立滚动。未选中节点、清除选择或图谱为空时操作栏仍可见;收起右侧详情时仅隐藏下方详情并释放画布宽度,顶部操作栏保留在右上角。导入使用 HeroUI Button 打开
`.xmind/.json`
文件选择,导出菜单使用 HeroUI Dropdown 提供“制度列表(.csv)/ 拓扑图(.xmind)”,保存当前整张图谱;归档版本禁用导入和保存,保留导出;导入或保存期间禁用重复导入、保存。
-
“导入 / 导出 / 保存”是图谱级操作,常驻拓扑容器右上角,与节点详情共用右侧面板;操作栏在顶部,三个按钮等宽横向排列、统一高度与间距,图标和文字保持同一行;导出使用 HeroUI Button 作为下拉触发器,保存保留主操作样式。下方节点详情以分隔线隔开并独立滚动。未选中节点、清除选择或图谱为空时操作栏仍可见;收起右侧详情时仅隐藏下方详情并释放画布宽度,顶部操作栏保留在右上角。导入使用 HeroUI Button 打开
`.xmind/.json`
文件选择,导出菜单使用 HeroUI Dropdown 提供“制度列表(.csv)/ 拓扑图(.xmind)”,保存当前整张图谱;归档版本禁用导入和保存,保留导出;导入或保存期间禁用重复导入、保存。
-
XMind 导出基于当前所查看版本的完整内存图谱,包括尚未保存的修改、折叠/筛选隐藏和已移除节点;导出不触发保存请求,归档版本也可导出。单根图沿用原根节点,多根或缺失父节点的分支归入“制度拓扑图”统一根节点,按
`parent`
保留层级;非父子连线转为 XMind 关联线。制度主题使用
`《制度名称》`
,节点备注写入 XMind Notes,制度状态和“已移除”写入标签;XMind 使用向右的逻辑图布局,不承诺复制画布的手工坐标、缩放或筛选状态。
-
XMind 导出基于当前所查看版本的完整内存图谱,包括尚未保存的修改、折叠/筛选隐藏和已移除节点;导出不触发保存请求,归档版本也可导出。单根图沿用原根节点,多根或缺失父节点的分支归入“制度拓扑图”统一根节点,按
`parent`
保留层级;非父子连线转为 XMind 关联线。制度主题使用
`《制度名称》`
,节点备注写入 XMind Notes,制度状态和“已移除”写入标签;XMind 使用向右的逻辑图布局,不承诺复制画布的手工坐标、缩放或筛选状态。
-
XMind 文件在浏览器内生成,使用现有 JSZip 打包
`content.json/metadata.json/manifest.json`
,下载名称为
`制度拓扑图-版本.xmind`
。生成期间显示“正在导出”并禁用重复导出;无节点、重复节点 ID 或循环父子关系时显示明确错误,失败保留当前图谱。CSV 继续按原规则导出制度列表。
-
XMind 文件在浏览器内生成,使用现有 JSZip 打包
`content.json/metadata.json/manifest.json`
,下载名称为
`制度拓扑图-版本.xmind`
。生成期间显示“正在导出”并禁用重复导出;无节点、重复节点 ID 或循环父子关系时显示明确错误,失败保留当前图谱。CSV 继续按原规则导出制度列表。
-
右侧详情支持编辑标题和备注、查看流程路径/制度路径/历史版本、折叠子节点和打开关联制度;“添加流程节点”位于详情的节点操作区,右键也可添加流程节点或描述。
-
右侧详情支持编辑标题和备注、查看流程路径/制度路径/历史版本、折叠子节点和打开关联制度;“添加流程节点”位于详情的节点操作区,右键也可添加流程节点或备注。
-
右键节点时,在鼠标点击位置显示“添加流程节点 / 添加备注”菜单;右键画布空白处只显示“添加流程节点”。菜单坐标以中间画布的实际定位容器为基准,不计入左侧列表宽度,不使用图谱缩放或平移后的节点坐标;菜单渲染后按实际宽高限制在画布内,靠近右侧或底部时自动向内调整,避免覆盖详情或被裁切。菜单项使用 HeroUI Button;点击外部、按 Esc、开始平移/缩放或拖动节点、窗口尺寸变化时关闭菜单,选择菜单项后按原流程打开节点编辑弹窗。
-
当前版本支持上传
`.xmind`
(兼容
`content.json`
与
`content.xml`
),会将
`《制度名称》`
主题匹配到制度记录,生成流程节点及连线,并同步
`graph_process`
分类。
-
当前版本支持上传
`.xmind`
(兼容
`content.json`
与
`content.xml`
),会将
`《制度名称》`
主题匹配到制度记录,生成流程节点及连线,并同步
`graph_process`
分类。
-
“全部 / 待分类”的制度记录可拖到画布中任意未移除可见节点的右侧连接区域:区域向右延伸 220 个画布单位,上下各扩展 32 个单位;相邻区域重叠时优先选择离鼠标最近的节点。命中后高亮父节点、显示连接区域与横向连线预览,并提示“松开连接到「节点名称」”;缩放、平移及手动移动节点后仍按当前画布坐标判断。
-
“全部 / 待分类”的制度记录可拖到画布中任意未移除可见节点的右侧连接区域:区域向右延伸 220 个画布单位,上下各扩展 32 个单位;相邻区域重叠时优先选择离鼠标最近的节点。命中后高亮父节点、显示连接区域与横向连线预览,并提示“松开连接到「节点名称」”;缩放、平移及手动移动节点后仍按当前画布坐标判断。
-
松手后按落点目标建立父子节点及连线,展开祖先路径和父节点、选中新制度并打开详情;同时清除流程/状态筛选并开启现行显示,让新节点可见。待新增关联修订计划 ID,现行/待修订/待废止制度保留制度 ID,修订计划通过
`origin_regulation`
关联现行制度。
-
松手后按落点目标建立父子节点及连线,展开祖先路径和父节点、选中新制度并打开详情;同时清除流程/状态筛选并开启现行显示,让新节点可见。待新增关联修订计划 ID,现行/待修订/待废止制度保留制度 ID,修订计划通过
`origin_regulation`
关联现行制度。
...
...
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