Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
C
center-book
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
EzijingWeb
center-book
Commits
dc071b55
提交
dc071b55
authored
7月 22, 2026
作者:
王鹏飞
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
refactor(admin): colocate editor menu events
上级
6712281f
显示空白字符变更
内嵌
并排
正在显示
15 个修改的文件
包含
98 行增加
和
135 行删除
+98
-135
create-feature-registry.js
...ommon/wangeditor-customer/core/create-feature-registry.js
+3
-0
define-editor-feature.js
.../common/wangeditor-customer/core/define-editor-feature.js
+2
-1
useEditorFeatureEvents.js
...common/wangeditor-customer/core/useEditorFeatureEvents.js
+18
-0
feature.js
.../src/common/wangeditor-customer/features/audio/feature.js
+1
-0
feature.js
...wangeditor-customer/features/chapter-structure/feature.js
+4
-0
feature.js
...ommon/wangeditor-customer/features/expand-read/feature.js
+1
-0
feature.js
...rc/common/wangeditor-customer/features/formula/feature.js
+1
-0
feature.js
...rc/common/wangeditor-customer/features/gallery/feature.js
+4
-0
feature.js
.../src/common/wangeditor-customer/features/image/feature.js
+22
-0
feature.js
...n/src/common/wangeditor-customer/features/link/feature.js
+1
-0
feature.js
...c/common/wangeditor-customer/features/practice/feature.js
+1
-0
feature.js
...rc/common/wangeditor-customer/features/tooltip/feature.js
+1
-0
feature.js
.../src/common/wangeditor-customer/features/video/feature.js
+1
-0
WangEditorCustomer.jsx
...c/common/wangeditor-customer/shell/WangEditorCustomer.jsx
+36
-134
feature-registry.test.ts
...min/src/test/wangeditor-customer/feature-registry.test.ts
+2
-0
没有找到文件。
apps/admin/src/common/wangeditor-customer/core/create-feature-registry.js
浏览文件 @
dc071b55
...
...
@@ -15,15 +15,18 @@ export const createFeatureRegistry = features => {
const
menus
=
features
.
flatMap
(
feature
=>
feature
.
menus
).
sort
(
byOrder
)
const
nodeModules
=
features
.
flatMap
(
feature
=>
feature
.
nodeModules
).
sort
(
byOrder
)
const
dialogEntries
=
features
.
flatMap
(
feature
=>
feature
.
dialogs
)
const
events
=
features
.
flatMap
(
feature
=>
feature
.
events
)
assertUnique
(
menus
.
map
(
entry
=>
entry
.
config
.
key
),
'menu key'
)
assertUnique
(
nodeModules
.
flatMap
(
entry
=>
entry
.
types
),
'node type'
)
assertUnique
(
dialogEntries
.
map
(
entry
=>
entry
.
id
),
'dialog id'
)
assertUnique
(
events
.
map
(
entry
=>
entry
.
name
),
'editor event'
)
return
{
features
:
[...
features
],
menus
,
nodeModules
,
events
,
dialogs
:
new
Map
(
dialogEntries
.
map
(
entry
=>
[
entry
.
id
,
entry
.
component
])),
}
}
apps/admin/src/common/wangeditor-customer/core/define-editor-feature.js
浏览文件 @
dc071b55
...
...
@@ -3,4 +3,5 @@ export const defineEditorFeature = ({
menus
=
[],
nodeModules
=
[],
dialogs
=
[],
})
=>
({
id
,
menus
,
nodeModules
,
dialogs
})
events
=
[],
})
=>
({
id
,
menus
,
nodeModules
,
dialogs
,
events
})
apps/admin/src/common/wangeditor-customer/core/useEditorFeatureEvents.js
0 → 100644
浏览文件 @
dc071b55
import
{
useEffect
,
useRef
}
from
'react'
export
const
useEditorFeatureEvents
=
({
editor
,
registry
,
runtime
})
=>
{
const
runtimeRef
=
useRef
(
runtime
)
runtimeRef
.
current
=
runtime
useEffect
(()
=>
{
if
(
!
editor
)
return
const
listeners
=
registry
.
events
.
map
(({
name
,
handler
})
=>
({
name
,
listener
:
payload
=>
handler
({
editor
,
payload
,
runtime
:
runtimeRef
.
current
}),
}))
listeners
.
forEach
(({
name
,
listener
})
=>
editor
.
on
(
name
,
listener
))
return
()
=>
listeners
.
forEach
(({
name
,
listener
})
=>
editor
.
off
(
name
,
listener
))
},
[
editor
,
registry
])
}
apps/admin/src/common/wangeditor-customer/features/audio/feature.js
浏览文件 @
dc071b55
...
...
@@ -6,4 +6,5 @@ export default defineEditorFeature({
id
:
'audio'
,
menus
:
[{
order
:
50
,
config
:
menu
}],
nodeModules
:
[{
order
:
50
,
types
:
[
'chapterAudio'
],
module
:
nodeModule
}],
events
:
[{
name
:
'AudioMenuClick'
,
handler
:
({
runtime
})
=>
runtime
.
openAudio
()
}],
})
apps/admin/src/common/wangeditor-customer/features/chapter-structure/feature.js
浏览文件 @
dc071b55
...
...
@@ -11,4 +11,8 @@ export default defineEditorFeature({
{
order
:
10
,
types
:
[
'chapterSection'
],
module
:
sectionNodeModule
},
{
order
:
20
,
types
:
[
'chapterHeader'
],
module
:
headerNodeModule
},
],
events
:
[
{
name
:
'ChapterItemMenuClick'
,
handler
:
({
runtime
})
=>
runtime
.
openSection
()
},
{
name
:
'ChapterTitleClick'
,
handler
:
({
runtime
})
=>
runtime
.
openTitle
()
},
],
})
apps/admin/src/common/wangeditor-customer/features/expand-read/feature.js
浏览文件 @
dc071b55
...
...
@@ -10,4 +10,5 @@ export default defineEditorFeature({
{
order
:
120
,
types
:
[
'chapterExpandRead'
],
module
:
nodeModule
},
{
order
:
130
,
types
:
[
'chapterExpandReadSimple'
],
module
:
simpleNodeModule
},
],
events
:
[{
name
:
'ExpandReadClick'
,
handler
:
({
runtime
})
=>
{
runtime
.
captureSelectionFontSize
();
runtime
.
openExpandRead
()
}
}],
})
apps/admin/src/common/wangeditor-customer/features/formula/feature.js
浏览文件 @
dc071b55
...
...
@@ -6,4 +6,5 @@ export default defineEditorFeature({
id
:
'formula'
,
menus
:
[{
order
:
60
,
config
:
menu
}],
nodeModules
:
[{
order
:
80
,
types
:
[
'chapterFormula'
],
module
:
nodeModule
}],
events
:
[{
name
:
'FormulaMenuClick'
,
handler
:
({
runtime
})
=>
runtime
.
openFormula
()
}],
})
apps/admin/src/common/wangeditor-customer/features/gallery/feature.js
浏览文件 @
dc071b55
...
...
@@ -11,4 +11,8 @@ export default defineEditorFeature({
{
order
:
60
,
types
:
[
'chapterGallery'
],
module
:
nodeModule
},
{
order
:
70
,
types
:
[
'chapterGalleryInline'
],
module
:
inlineNodeModule
},
],
events
:
[
{
name
:
'GalleryMenuClick'
,
handler
:
({
runtime
})
=>
{
runtime
.
captureSelectionFontSize
();
runtime
.
openGallery
({
isOnline
:
false
})
}
},
{
name
:
'GalleryOnlineMenuClick'
,
handler
:
({
runtime
})
=>
{
runtime
.
captureSelectionFontSize
();
runtime
.
openGallery
({
isOnline
:
true
})
}
},
],
})
apps/admin/src/common/wangeditor-customer/features/image/feature.js
浏览文件 @
dc071b55
...
...
@@ -2,9 +2,31 @@ import { defineEditorFeature } from '../../core/define-editor-feature'
import
editMenu
from
'./edit-menu'
import
onlineMenu
from
'./online-menu'
import
nodeModule
from
'./node'
import
{
SlateEditor
,
SlateElement
}
from
'@wangeditor/editor'
const
getSelectedImageInfo
=
editor
=>
{
const
entries
=
SlateEditor
.
nodes
(
editor
,
{
match
:
node
=>
SlateElement
.
isElement
(
node
)
&&
node
.
type
===
'paragraph'
,
universal
:
true
,
})
const
info
=
{}
for
(
const
[
node
,
path
]
of
entries
)
{
node
.
children
.
forEach
(
item
=>
{
if
(
item
.
type
===
'image'
)
info
.
image
=
item
})
info
.
node
=
node
info
.
path
=
path
}
return
info
}
export
default
defineEditorFeature
({
id
:
'image'
,
menus
:
[{
order
:
10
,
config
:
onlineMenu
},
{
order
:
110
,
config
:
editMenu
}],
nodeModules
:
[{
order
:
30
,
types
:
[
'chapterImage'
],
module
:
nodeModule
}],
events
:
[
{
name
:
'ImageMenuClick'
,
handler
:
({
runtime
})
=>
runtime
.
openImage
({
isOnline
:
false
})
},
{
name
:
'ImageOnlineMenuClick'
,
handler
:
({
runtime
})
=>
runtime
.
openImage
({
isOnline
:
true
})
},
{
name
:
'ImageEditorClick'
,
handler
:
({
editor
,
runtime
})
=>
runtime
.
openImage
({
isOnline
:
false
,
imageInfo
:
getSelectedImageInfo
(
editor
)
})
},
],
})
apps/admin/src/common/wangeditor-customer/features/link/feature.js
浏览文件 @
dc071b55
...
...
@@ -6,4 +6,5 @@ export default defineEditorFeature({
id
:
'link'
,
menus
:
[{
order
:
120
,
config
:
menu
}],
nodeModules
:
[{
order
:
110
,
types
:
[
'chapterLink'
],
module
:
nodeModule
}],
events
:
[{
name
:
'CustomerLinkClick'
,
handler
:
({
runtime
})
=>
{
runtime
.
captureSelectionFontSize
();
runtime
.
openLink
()
}
}],
})
apps/admin/src/common/wangeditor-customer/features/practice/feature.js
浏览文件 @
dc071b55
...
...
@@ -6,4 +6,5 @@ export default defineEditorFeature({
id
:
'practice'
,
menus
:
[{
order
:
100
,
config
:
menu
}],
nodeModules
:
[{
order
:
90
,
types
:
[
'chapterPractice'
],
module
:
nodeModule
}],
events
:
[{
name
:
'PracticeMenuClick'
,
handler
:
({
runtime
})
=>
runtime
.
openPractice
()
}],
})
apps/admin/src/common/wangeditor-customer/features/tooltip/feature.js
浏览文件 @
dc071b55
...
...
@@ -6,4 +6,5 @@ export default defineEditorFeature({
id
:
'tooltip'
,
menus
:
[{
order
:
70
,
config
:
menu
}],
nodeModules
:
[{
order
:
100
,
types
:
[
'chapterTooltip'
],
module
:
nodeModule
}],
events
:
[{
name
:
'TooltipMenuClick'
,
handler
:
({
runtime
})
=>
{
runtime
.
captureSelectionFontSize
();
runtime
.
openTooltip
()
}
}],
})
apps/admin/src/common/wangeditor-customer/features/video/feature.js
浏览文件 @
dc071b55
...
...
@@ -6,4 +6,5 @@ export default defineEditorFeature({
id
:
'video'
,
menus
:
[{
order
:
40
,
config
:
menu
}],
nodeModules
:
[{
order
:
40
,
types
:
[
'chapterVideo'
],
module
:
nodeModule
}],
events
:
[{
name
:
'VideoMenuClick'
,
handler
:
({
runtime
})
=>
runtime
.
openVideo
()
}],
})
apps/admin/src/common/wangeditor-customer/shell/WangEditorCustomer.jsx
浏览文件 @
dc071b55
...
...
@@ -13,8 +13,8 @@ import { registerEditorFeatures } from "../core/register-editor-features";
import
{
EditorDialogHost
}
from
"../core/EditorDialogHost"
;
import
{
editorFeatureRegistry
}
from
"../features"
;
import
{
SlateEditor
,
DomEditor
,
SlateElement
,
SlateTransforms
}
from
"@wangeditor/editor"
;
import
{
Editor
,
Toolbar
}
from
"@wangeditor/editor-for-react"
;
import
{
DomEditor
,
SlateTransforms
}
from
"@wangeditor/editor"
;
import
{
Editor
}
from
"@wangeditor/editor-for-react"
;
import
{
fontFamilyList
,
lineHeightList
}
from
"../utils/setting"
;
import
"@wangeditor/editor/dist/css/style.css"
;
// 引入 css
import
timesave
from
"@/assets/images/timesave.png"
;
...
...
@@ -43,6 +43,7 @@ import { EditorHeader } from './EditorHeader'
import
{
EditorSidebar
}
from
'./EditorSidebar'
import
{
useEditorLifecycle
}
from
'./useEditorLifecycle'
import
{
useToolbarPresentation
}
from
'./useToolbarPresentation'
import
{
useEditorFeatureEvents
}
from
'../core/useEditorFeatureEvents'
import
"../index.less"
;
...
...
@@ -341,143 +342,44 @@ const WangEditorCustomer = (props, ref) => {
setContent
(
editor
.
getHtml
());
};
// 及时销毁 editor ,重要!
useEffect
(()
=>
{
if
(
editor
)
{
// 图片上传
editor
.
on
(
"ImageMenuClick"
,
()
=>
{
console
.
log
(
"ImageMenuClick"
,
"----"
);
setIsOnline
(
false
);
setImageVisible
(
true
);
});
// 在线图片
editor
.
on
(
"ImageOnlineMenuClick"
,
()
=>
{
console
.
log
(
"ImageOnlineMenuClick"
,
"----"
);
setIsOnline
(
true
);
setImageVisible
(
true
);
});
// 画廊上传
editor
.
on
(
"GalleryMenuClick"
,
()
=>
{
if
(
editor
.
selection
)
{
listenNodeStyle
(
editor
.
selection
.
anchor
.
path
);
}
console
.
log
(
"GalleryMenuClick"
,
"----"
);
// galleryRef.current.setVisible(true);
setIsOnline
(
false
);
setGalleryVisible
(
true
);
});
// 在线画廊
editor
.
on
(
"GalleryOnlineMenuClick"
,
()
=>
{
if
(
editor
.
selection
)
{
listenNodeStyle
(
editor
.
selection
.
anchor
.
path
);
}
console
.
log
(
"GalleryOnlineMenuClick"
,
"----"
);
// galleryRef.current.setVisible(true);
setIsOnline
(
true
);
setGalleryVisible
(
true
);
});
// 视频上传
editor
.
on
(
"VideoMenuClick"
,
()
=>
{
console
.
log
(
"VideoMenuClick"
,
"----"
);
videoRef
.
current
.
setVisible
(
true
);
});
// 音频上传
editor
.
on
(
"AudioMenuClick"
,
()
=>
{
console
.
log
(
"AudioMenuClick"
,
"----"
);
audioRef
.
current
.
setVisible
(
true
);
});
// 章节
editor
.
on
(
"ChapterItemMenuClick"
,
()
=>
{
console
.
log
(
"ChapterItemMenuClick"
,
"----"
);
setSectionVisible
(
true
);
});
// 章头
editor
.
on
(
"ChapterTitleClick"
,
()
=>
{
console
.
log
(
"ChapterTitleClick"
,
"----"
);
setTitleVisible
(
true
);
});
// 交互练习
editor
.
on
(
"PracticeMenuClick"
,
()
=>
{
console
.
log
(
"PracticeMenuClick"
,
"----"
);
setPracticeOpenVisible
(
true
);
});
// 公式
editor
.
on
(
"FormulaMenuClick"
,
()
=>
{
console
.
log
(
"FormulaMenuClick"
,
"----"
);
formulaRef
.
current
.
setVisible
(
true
);
});
// 插入图片
editor
.
on
(
"ImageEditorClick"
,
()
=>
{
console
.
log
(
"ImageEditorClick"
,
"----"
);
const
nodeEntries
=
SlateEditor
.
nodes
(
editor
,
{
match
:
(
node
)
=>
{
// JS syntax
if
(
SlateElement
.
isElement
(
node
))
{
if
(
node
.
type
===
"paragraph"
)
{
return
true
;
// 匹配 paragraph
}
}
return
false
;
const
featureRuntime
=
{
openImage
:
({
isOnline
,
imageInfo
})
=>
{
if
(
imageInfo
)
setImageInfo
(
imageInfo
)
setIsOnline
(
isOnline
)
setImageVisible
(
true
)
},
openGallery
:
({
isOnline
})
=>
{
setIsOnline
(
isOnline
)
setGalleryVisible
(
true
)
},
openVideo
:
()
=>
videoRef
.
current
?.
setVisible
(
true
),
openAudio
:
()
=>
audioRef
.
current
?.
setVisible
(
true
),
openTitle
:
()
=>
setTitleVisible
(
true
),
openSection
:
()
=>
setSectionVisible
(
true
),
openPractice
:
()
=>
setPracticeOpenVisible
(
true
),
openFormula
:
()
=>
formulaRef
.
current
?.
setVisible
(
true
),
openTooltip
:
()
=>
setTooltipVisible
(
true
),
openLink
:
()
=>
setLinkVisible
(
true
),
openExpandRead
:
()
=>
setExpandVisible
(
true
),
captureSelectionFontSize
:
()
=>
{
if
(
editor
?.
selection
)
listenNodeStyle
(
editor
.
selection
.
anchor
.
path
)
},
universal
:
true
,
});
let
info
=
{};
for
(
let
nodeEntry
of
nodeEntries
)
{
const
[
node
,
path
]
=
nodeEntry
;
node
.
children
.
forEach
((
item
)
=>
{
if
(
item
.
type
===
"image"
)
{
info
.
image
=
item
;
}
});
info
.
node
=
node
;
info
.
path
=
path
;
}
setImageInfo
(
info
);
setIsOnline
(
false
);
setImageVisible
(
true
);
});
// 气泡
editor
.
on
(
"TooltipMenuClick"
,
()
=>
{
if
(
editor
.
selection
)
{
listenNodeStyle
(
editor
.
selection
.
anchor
.
path
);
}
setTooltipVisible
(
true
);
});
// 链接
editor
.
on
(
"CustomerLinkClick"
,
()
=>
{
if
(
editor
.
selection
)
{
listenNodeStyle
(
editor
.
selection
.
anchor
.
path
);
}
setLinkVisible
(
true
);
});
// 扩展
editor
.
on
(
"ExpandReadClick"
,
()
=>
{
if
(
editor
.
selection
)
{
listenNodeStyle
(
editor
.
selection
.
anchor
.
path
);
}
setExpandVisible
(
true
);
});
useEditorFeatureEvents
({
editor
,
registry
:
editorFeatureRegistry
,
runtime
:
featureRuntime
})
editor
.
addMark
(
"fontSize"
,
"18px"
);
editor
.
addMark
(
"fontFamily"
,
"黑体"
);
editor
.
addMark
(
"lineHeight"
,
1.5
);
setContent
(
html
);
// 及时销毁 editor ,重要!
useEffect
(()
=>
{
if
(
!
editor
)
return
editor
.
addMark
(
"fontSize"
,
"18px"
)
editor
.
addMark
(
"fontFamily"
,
"黑体"
)
editor
.
addMark
(
"lineHeight"
,
1.5
)
setContent
(
html
)
if
(
gData
.
length
>
0
)
{
editor
.
enable
();
editor
.
focus
();
}
else
{
editor
.
disable
();
}
}
return
()
=>
{
if
(
editor
===
null
)
return
;
editor
.
destroy
();
setEditor
(
null
);
};
},
[
editor
]);
editor
.
destroy
()
setEditor
(
null
)
}
},
[
editor
])
useEditorLifecycle
({
editor
,
gData
,
disabled
});
useToolbarPresentation
({
toolbarRef
,
active
:
!
isView
&&
tabKey
===
"text"
});
...
...
apps/admin/src/test/wangeditor-customer/feature-registry.test.ts
浏览文件 @
dc071b55
...
...
@@ -49,6 +49,7 @@ describe('createFeatureRegistry', () => {
menus
:
[{
order
:
10
,
config
:
menu
(
'menu-a'
)
}],
nodeModules
:
[{
order
:
10
,
types
:
[
'node-a'
],
module
:
{}
}],
dialogs
:
[{
id
:
'dialog-a'
,
component
:
dialog
}],
events
:
[{
name
:
'event-a'
,
handler
:
()
=>
{}
}],
...
overrides
,
})
...
...
@@ -56,6 +57,7 @@ describe('createFeatureRegistry', () => {
expect
(()
=>
createFeatureRegistry
([
make
(),
make
({
id
:
'feature-b'
})])).
toThrow
(
'Duplicate menu key: menu-a'
)
expect
(()
=>
createFeatureRegistry
([
make
(),
make
({
id
:
'feature-b'
,
menus
:
[]
})])).
toThrow
(
'Duplicate node type: node-a'
)
expect
(()
=>
createFeatureRegistry
([
make
(),
make
({
id
:
'feature-b'
,
menus
:
[],
nodeModules
:
[]
})])).
toThrow
(
'Duplicate dialog id: dialog-a'
)
expect
(()
=>
createFeatureRegistry
([
make
(),
make
({
id
:
'feature-b'
,
menus
:
[],
nodeModules
:
[],
dialogs
:
[]
})])).
toThrow
(
'Duplicate editor event: event-a'
)
})
it
(
'keeps the legacy menu and node registration orders'
,
()
=>
{
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论