Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
C
center-book
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
EzijingWeb
center-book
Commits
f77be006
提交
f77be006
authored
9月 12, 2024
作者:
王鹏飞
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
chore: 修改编辑器AI相关内容
上级
1cc62424
显示空白字符变更
内嵌
并排
正在显示
31 个修改的文件
包含
1584 行增加
和
2830 行删除
+1584
-2830
package-lock.json
package-lock.json
+1
-0
index.jsx
src/common/wangeditor-customer/ai-drawer/index.jsx
+0
-214
chapter-item.jsx
src/common/wangeditor-customer/components/chapter-item.jsx
+107
-160
chapter-title.jsx
src/common/wangeditor-customer/components/chapter-title.jsx
+165
-226
expand.jsx
src/common/wangeditor-customer/components/expand.jsx
+133
-195
gallery.jsx
src/common/wangeditor-customer/components/gallery.jsx
+9
-9
link.jsx
src/common/wangeditor-customer/components/link.jsx
+116
-164
tooltip.jsx
src/common/wangeditor-customer/components/tooltip.jsx
+104
-144
ChapterItem.js
src/common/wangeditor-customer/customer/ChapterItem.js
+10
-13
index-padd.jsx
src/common/wangeditor-customer/index-padd.jsx
+0
-685
index.jsx
src/common/wangeditor-customer/index.jsx
+104
-354
index.less
src/common/wangeditor-customer/index.less
+22
-154
AIChat.jsx
src/common/wangeditor-customer/menu/AIChat.jsx
+13
-25
AIContentInspect.jsx
src/common/wangeditor-customer/menu/AIContentInspect.jsx
+21
-0
AIDigitalHuman.jsx
src/common/wangeditor-customer/menu/AIDigitalHuman.jsx
+27
-0
AIExpand.jsx
src/common/wangeditor-customer/menu/AIExpand.jsx
+11
-26
AIPolishing.jsx
src/common/wangeditor-customer/menu/AIPolishing.jsx
+14
-27
AIPunctuation.jsx
src/common/wangeditor-customer/menu/AIPunctuation.jsx
+21
-0
AIRewrite.jsx
src/common/wangeditor-customer/menu/AIRewrite.jsx
+14
-25
AISummary.jsx
src/common/wangeditor-customer/menu/AISummary.jsx
+13
-26
AIChatDrawer.jsx
src/common/wangeditor-customer/menu/common/AIChatDrawer.jsx
+108
-0
AIChatDrawer.less
src/common/wangeditor-customer/menu/common/AIChatDrawer.less
+1
-1
AIModal.jsx
src/common/wangeditor-customer/menu/common/AIModal.jsx
+15
-15
BaseModalMenu.jsx
src/common/wangeditor-customer/menu/common/BaseModalMenu.jsx
+26
-0
useAIChat.js
src/common/wangeditor-customer/menu/common/useAIChat.js
+84
-0
setting.js
src/common/wangeditor-customer/utils/setting.js
+27
-24
index.jsx
src/components/editor/index.jsx
+69
-6
styles.less
src/components/editor/styles.less
+55
-0
index.jsx
src/pages/books/section/index.jsx
+287
-337
index.jsx
src/pages/editor/index.jsx
+3
-0
routes.jsx
src/routes/routes.jsx
+4
-0
没有找到文件。
package-lock.json
浏览文件 @
f77be006
...
...
@@ -584,6 +584,7 @@
"version"
:
"3.0.6"
,
"resolved"
:
"https://registry.npmjs.org/@fortaine/fetch-event-source/-/fetch-event-source-3.0.6.tgz"
,
"integrity"
:
"sha512-621GAuLMvKtyZQ3IA6nlDWhV1V/7PGOTNIGLUifxt0KzM+dZIweJ6F3XvQF3QnqeNfS1N7WQ0Kil1Di/lhChEw=="
,
"license"
:
"MIT"
,
"engines"
:
{
"node"
:
">=16.15"
}
...
...
src/common/wangeditor-customer/ai-drawer/index.jsx
deleted
100644 → 0
浏览文件 @
1cc62424
import
{
useState
,
useEffect
}
from
'react'
import
{
SendOutlined
}
from
'@ant-design/icons'
import
{
Input
,
Button
,
message
}
from
'antd'
import
md5
from
'js-md5'
import
dayjs
from
'dayjs'
import
{
sleep
}
from
'@/utils/common'
import
'./index.less'
import
{
useSelector
,
useDispatch
}
from
'react-redux'
import
{
setEditorAi
}
from
'@/store/modules/editor'
import
normalAvatar
from
'@/assets/images/icon-normal-avatar.png'
const
UUID
=
'f3846153ba784b6d86bdcd5533259c88'
const
auth_key
=
'f3846153ba784b6d86bdcd5533259c88'
const
auth_secret
=
'HO4IyLEwEOHpeOXBxaLQUOqWslJRGs1M'
const
AIDrawerComponent
=
(
props
)
=>
{
const
{
selectText
}
=
props
const
dispatch
=
useDispatch
()
const
{
userInfo
}
=
useSelector
((
state
)
=>
state
.
user
)
// 用户状态信息
const
{
editorAi
}
=
useSelector
((
state
)
=>
state
.
editor
)
// ai信息设置
const
[
value
,
setValue
]
=
useState
(
selectText
)
// 输入值
const
[
loading
,
setLoading
]
=
useState
(
false
)
// loading
const
[
chatId
,
setChatId
]
=
useState
(
null
)
// 对话的id
const
[
chatContentList
,
setChatContentList
]
=
useState
([])
// 对话数据
const
element
=
document
.
querySelector
(
'#chat-content'
)
let
str
=
''
useEffect
(()
=>
{
if
(
editorAi
&&
Object
.
entries
(
editorAi
).
length
>
0
)
{
let
tempJSON
=
JSON
.
parse
(
JSON
.
stringify
(
chatContentList
))
let
conversationId
=
editorAi
.
conversationId
const
item
=
tempJSON
.
filter
((
item
)
=>
item
.
conversationId
===
conversationId
)
if
(
item
&&
item
.
length
>
0
)
{
tempJSON
[
tempJSON
.
length
-
1
]
=
editorAi
}
else
{
tempJSON
.
push
(
editorAi
)
}
setChatId
(
editorAi
.
chatId
)
setChatContentList
(
tempJSON
)
element
.
scrollTo
(
0
,
999999
)
}
else
{
setLoading
(
false
)
}
},
[
editorAi
])
useEffect
(()
=>
{
if
(
selectText
)
{
targetChat
()
}
return
()
=>
{
setValue
(
''
)
setChatContentList
([])
setChatId
(
null
)
}
},
[])
let
buffer
=
''
const
readBuffer
=
(
reader
,
decoder
)
=>
{
return
reader
.
read
().
then
(
async
({
done
,
value
})
=>
{
if
(
done
)
{
// 处理最后的缓冲数据
setLoading
(
false
)
setValue
(
''
)
dispatch
(
setEditorAi
({}))
str
=
''
processBuffer
(
buffer
)
return
}
await
sleep
(
80
)
buffer
+=
decoder
.
decode
(
value
,
{
stream
:
true
})
// 检查缓冲区中是否有完整的消息
const
messages
=
buffer
.
split
(
'
\
n
\
n'
)
// 假设每个消息以换行符结束
messages
.
forEach
((
message
,
index
)
=>
{
if
(
index
<
messages
.
length
-
1
)
{
processBuffer
(
message
)
}
})
buffer
=
messages
[
messages
.
length
-
1
]
// 更新缓冲区
readBuffer
(
reader
,
decoder
)
})
}
const
processBuffer
=
(
text
)
=>
{
let
aiContent
=
{}
try
{
const
content
=
text
.
replace
(
/
\n
+/g
,
''
).
split
(
'data:'
)
if
(
content
instanceof
Array
)
{
const
itemContent
=
JSON
.
parse
(
content
[
1
])
if
(
!
(
itemContent
.
finish
&&
itemContent
.
finish
===
true
))
{
str
+=
itemContent
.
content
aiContent
=
{
type
:
'ai'
,
content
:
str
,
chatId
:
itemContent
.
chatId
,
avatar
:
itemContent
.
role
.
avatar
,
conversationId
:
itemContent
.
conversationId
}
dispatch
(
setEditorAi
(
aiContent
))
}
}
}
catch
{}
}
// 提交对话
const
targetChat
=
async
()
=>
{
if
(
value
)
{
setLoading
(
true
)
const
timestamp
=
Date
.
now
()
fetch
(
'/api/tiangong/openapi/agent/chat/stream/v1'
,
{
method
:
'POST'
,
responseType
:
'stream'
,
// 设置响应类型为 'stream'
headers
:
{
'Content-Type'
:
'application/json'
,
authKey
:
auth_key
,
timestamp
:
timestamp
,
sign
:
md5
(
`
${
auth_key
}${
auth_secret
}${
timestamp
}
`
),
stream
:
true
// or change to "false" 不处理流式返回内容
},
body
:
JSON
.
stringify
({
agentId
:
UUID
,
chatId
:
chatId
,
userChatInput
:
value
}),
mode
:
'cors'
})
.
then
((
response
)
=>
{
// response.body 是一个 ReadableStream 对象
const
stream
=
response
.
body
// 使用流式数据
const
reader
=
stream
.
getReader
()
const
decoder
=
new
TextDecoder
()
// 用于解码二进制数据流
let
userContent
=
{
type
:
'user'
,
content
:
value
,
time
:
Date
.
now
()
}
let
tempJSON
=
JSON
.
parse
(
JSON
.
stringify
(
chatContentList
))
tempJSON
.
push
(
userContent
)
setChatContentList
(
tempJSON
)
// 开始读取流数据
readBuffer
(
reader
,
decoder
)
})
.
catch
((
error
)
=>
{
// 处理请求错误
console
.
error
(
'Request failed:'
,
error
)
})
}
else
{
message
.
error
(
'请输入对话内容!'
)
}
}
return
(
<
div
className=
"ai-drawer-content"
>
<
div
className=
"ai-chat-container"
>
<
div
className=
"chat-content"
id=
"chat-content"
>
<
div
className=
"chat-content-padd"
>
{
chatContentList
&&
chatContentList
.
length
>
0
&&
chatContentList
.
map
((
item
,
index
)
=>
{
return
(
<
div
className=
{
`chat-content-item`
}
key=
{
index
}
>
{
item
.
type
===
'user'
&&
<
div
className=
"time"
>
{
dayjs
(
item
.
time
).
format
(
'YYYY-MM-DD HH:mm'
)
}
</
div
>
}
{
item
.
type
===
'ai'
&&
(
<
div
className=
"inside"
>
<
div
className=
"ai-in-content"
>
<
div
className=
"img"
>
<
img
src=
{
item
.
avatar
}
alt=
""
/>
</
div
>
<
div
className=
"ask-content"
>
{
item
.
content
}
</
div
>
</
div
>
</
div
>
)
}
{
item
.
type
===
'user'
&&
(
<
div
className=
"inside inside-user"
>
<
div
className=
"user-in-content"
>
<
div
className=
"ask-content"
>
{
item
.
content
}
</
div
>
<
div
className=
"img"
>
<
img
src=
{
userInfo
.
pic
?
userInfo
.
pic
:
normalAvatar
}
alt=
"用户头像"
/>
</
div
>
</
div
>
</
div
>
)
}
</
div
>
)
})
}
</
div
>
</
div
>
</
div
>
<
div
className=
"ai-chat-ask"
>
<
div
className=
"text"
>
<
Input
.
TextArea
value=
{
value
}
defaultValue=
{
value
}
allowClear
disabled=
{
loading
}
onChange=
{
(
e
)
=>
setValue
(
e
.
target
.
value
)
}
palceholder=
"请输入内容"
style=
{
{
height
:
'80px'
,
resize
:
'none'
}
}
></
Input
.
TextArea
>
</
div
>
<
div
className=
"button"
>
<
Button
type=
"primary"
icon=
{
<
SendOutlined
/>
}
loading=
{
loading
}
size=
"large"
onClick=
{
targetChat
}
></
Button
>
</
div
>
</
div
>
</
div
>
)
}
export
default
AIDrawerComponent
src/common/wangeditor-customer/components/chapter-item.jsx
浏览文件 @
f77be006
import
React
,
{
useState
,
useEffect
,
forwardRef
,
useImperativeHandle
}
from
'react'
;
import
{
Modal
,
Divider
,
Upload
,
Input
,
Space
,
Button
,
Form
,
Spin
,
message
,
ColorPicker
,
Select
,
InputNumber
,
Row
,
Col
,
}
from
'antd'
;
import
{
DomEditor
,
SlateEditor
,
SlateTransforms
,
SlateElement
}
from
'@wangeditor/editor'
;
import
{
fontFamilyList
,
fontFizeList
}
from
'../utils/setting'
;
import
$
from
'jquery'
;
import
'./index.less'
;
import
React
,
{
useState
,
useEffect
,
forwardRef
,
useImperativeHandle
}
from
'react'
import
{
Modal
,
Divider
,
Upload
,
Input
,
Space
,
Button
,
Form
,
Spin
,
message
,
ColorPicker
,
Select
,
InputNumber
,
Row
,
Col
}
from
'antd'
import
{
DomEditor
,
SlateEditor
,
SlateTransforms
,
SlateElement
}
from
'@wangeditor/editor'
import
{
fontFamilyList
,
fontSizeList
}
from
'../utils/setting'
import
$
from
'jquery'
import
'./index.less'
const
ChapterItemModal
=
forwardRef
((
props
,
ref
)
=>
{
const
{
editor
,
setSectionInfo
,
sectionInfo
,
setSectionVisible
}
=
props
;
const
[
form
]
=
Form
.
useForm
()
;
const
[
visible
,
setVisible
]
=
useState
(
false
)
;
const
{
editor
,
setSectionInfo
,
sectionInfo
,
setSectionVisible
}
=
props
const
[
form
]
=
Form
.
useForm
()
const
[
visible
,
setVisible
]
=
useState
(
false
)
// 是否更新的判断
const
[
toolStatus
,
setToolStatus
]
=
useState
(
false
)
;
const
[
bgColorValue
,
setBgColorValue
]
=
useState
(
'#000000'
)
;
const
[
textColor
,
setTextColor
]
=
useState
(
'#ffffff'
)
;
const
[
nowRandom
,
setNowRandom
]
=
useState
(
null
)
;
const
[
toolStatus
,
setToolStatus
]
=
useState
(
false
)
const
[
bgColorValue
,
setBgColorValue
]
=
useState
(
'#000000'
)
const
[
textColor
,
setTextColor
]
=
useState
(
'#ffffff'
)
const
[
nowRandom
,
setNowRandom
]
=
useState
(
null
)
const
[
initValues
,
setInitValues
]
=
useState
({
title
:
''
,
textColor
:
textColor
,
...
...
@@ -37,34 +22,34 @@ const ChapterItemModal = forwardRef((props, ref) => {
size
:
24
,
bgColor
:
bgColorValue
,
height
:
100
,
family
:
'黑体'
,
})
;
family
:
'黑体'
})
useImperativeHandle
(
ref
,
()
=>
{
return
{
setVisible
,
}
;
})
;
setVisible
}
})
useEffect
(()
=>
{
if
(
sectionInfo
&&
Object
.
entries
(
sectionInfo
).
length
>
0
)
{
setInitValues
({
...
sectionInfo
})
;
form
.
setFieldsValue
({
...
sectionInfo
})
;
setNowRandom
(
sectionInfo
.
random
)
;
setTextColor
(
sectionInfo
.
textColor
)
;
setBgColorValue
(
sectionInfo
.
bgColor
)
;
setSectionInfo
({})
;
setInitValues
({
...
sectionInfo
})
form
.
setFieldsValue
({
...
sectionInfo
})
setNowRandom
(
sectionInfo
.
random
)
setTextColor
(
sectionInfo
.
textColor
)
setBgColorValue
(
sectionInfo
.
bgColor
)
setSectionInfo
({})
}
},
[
sectionInfo
])
;
},
[
sectionInfo
])
const
bgColorChange
=
(
value
,
hex
)
=>
{
setBgColorValue
(
hex
)
;
form
.
setFieldsValue
({
bgColor
:
hex
})
;
}
;
setBgColorValue
(
hex
)
form
.
setFieldsValue
({
bgColor
:
hex
})
}
const
textColorChange
=
(
value
,
hex
)
=>
{
setTextColor
(
hex
)
;
form
.
setFieldsValue
({
textColor
:
hex
})
;
}
;
setTextColor
(
hex
)
form
.
setFieldsValue
({
textColor
:
hex
})
}
const
callback
=
({
title
,
random
,
bgColor
,
textColor
,
align
,
height
,
size
})
=>
{
form
.
setFieldsValue
({
...
...
@@ -73,82 +58,82 @@ const ChapterItemModal = forwardRef((props, ref) => {
textColor
:
textColor
,
align
:
align
,
height
:
height
,
size
:
size
,
})
;
setToolStatus
(
true
)
;
setNowRandom
(
random
)
;
}
;
size
:
size
})
setToolStatus
(
true
)
setNowRandom
(
random
)
}
// 获取当前节点的上一个节点
function
getPreviousNode
(
editor
)
{
const
{
selection
}
=
editor
;
if
(
!
selection
)
return
null
;
const
{
selection
}
=
editor
if
(
!
selection
)
return
null
const
[
start
]
=
SlateEditor
.
nodes
(
editor
,
{
at
:
selection
})
;
const
previous
=
SlateEditor
.
previous
(
editor
,
{
at
:
start
[
1
]
})
;
const
[
start
]
=
SlateEditor
.
nodes
(
editor
,
{
at
:
selection
})
const
previous
=
SlateEditor
.
previous
(
editor
,
{
at
:
start
[
1
]
})
if
(
previous
)
{
return
previous
;
return
previous
}
return
null
;
return
null
}
const
onFinish
=
(
values
)
=>
{
editor
.
restoreSelection
()
;
const
onFinish
=
values
=>
{
editor
.
restoreSelection
()
const
nodeEntries
=
SlateEditor
.
nodes
(
editor
,
{
match
:
(
node
)
=>
{
match
:
node
=>
{
// JS syntax
if
(
SlateElement
.
isElement
(
node
))
{
if
(
node
.
type
===
'paragraph'
)
{
return
true
;
// 匹配 paragraph
return
true
// 匹配 paragraph
}
}
return
false
;
return
false
},
universal
:
true
,
})
;
universal
:
true
})
for
(
let
nodeEntry
of
nodeEntries
)
{
const
[
node
,
path
]
=
nodeEntry
;
const
[
node
,
path
]
=
nodeEntry
if
(
node
.
children
[
0
].
text
===
''
)
{
SlateTransforms
.
removeNodes
(
editor
)
;
SlateTransforms
.
removeNodes
(
editor
)
}
}
if
(
nowRandom
)
{
const
nodeEntries
=
SlateEditor
.
nodes
(
editor
,
{
match
:
(
node
)
=>
{
match
:
node
=>
{
// JS syntax
if
(
SlateElement
.
isElement
(
node
))
{
if
(
node
.
type
===
'chapterSection'
)
{
return
true
;
// 匹配 chapterHeader
return
true
// 匹配 chapterHeader
}
}
return
false
;
return
false
},
universal
:
true
,
})
;
universal
:
true
})
if
(
nodeEntries
)
{
for
(
let
nodeEntry
of
nodeEntries
)
{
const
[
node
,
path
]
=
nodeEntry
;
const
[
node
,
path
]
=
nodeEntry
// console.log(node, path);
if
(
parseInt
(
node
.
random
)
===
parseInt
(
nowRandom
))
{
SlateTransforms
.
setNodes
(
editor
,
{
...
node
,
...
values
},
{
at
:
path
})
;
SlateTransforms
.
setNodes
(
editor
,
{
...
node
,
...
values
},
{
at
:
path
})
form
.
resetFields
()
;
setSectionVisible
(
false
)
;
return
false
;
form
.
resetFields
()
setSectionVisible
(
false
)
return
false
}
}
}
return
;
return
}
const
html
=
editor
.
getHtml
()
;
const
parser
=
new
DOMParser
()
;
let
random
=
Math
.
random
().
toString
(
10
).
substring
(
2
,
10
)
;
const
docBody
=
parser
.
parseFromString
(
html
,
'text/html'
)
;
const
section
=
docBody
.
body
.
querySelectorAll
(
'.chapter-item-section'
)
;
// 节头标签
const
html
=
editor
.
getHtml
()
const
parser
=
new
DOMParser
()
let
random
=
Math
.
random
().
toString
(
10
).
substring
(
2
,
10
)
const
docBody
=
parser
.
parseFromString
(
html
,
'text/html'
)
const
section
=
docBody
.
body
.
querySelectorAll
(
'.chapter-item-section'
)
// 节头标签
editor
.
insertNode
({
type
:
'chapterSection'
,
...
...
@@ -161,103 +146,74 @@ const ChapterItemModal = forwardRef((props, ref) => {
size
:
values
.
size
?
values
.
size
:
20
,
family
:
values
.
family
,
callback
:
callback
,
children
:
[{
text
:
''
}]
,
})
;
children
:
[{
text
:
''
}]
})
form
.
resetFields
()
;
setSectionVisible
(
false
)
;
}
;
form
.
resetFields
()
setSectionVisible
(
false
)
}
return
(
<
div
>
<
Divider
/>
<
div
className=
'editor-content-form'
>
<
Form
layout=
'vertical'
name=
'validate_other'
form=
{
form
}
onFinish=
{
onFinish
}
initialValues=
{
initValues
}
>
<
Form
.
Item
label=
'节头标题'
name=
'title'
rules=
{
[{
required
:
true
,
message
:
'请输入节头标题'
}]
}
extra=
'最多输入30字'
>
<
Input
maxLength=
{
30
}
placeholder=
''
allowClear
/>
<
div
className=
"editor-content-form"
>
<
Form
layout=
"vertical"
name=
"validate_other"
form=
{
form
}
onFinish=
{
onFinish
}
initialValues=
{
initValues
}
>
<
Form
.
Item
label=
"节头标题"
name=
"title"
rules=
{
[{
required
:
true
,
message
:
'请输入节头标题'
}]
}
extra=
"最多输入30字"
>
<
Input
maxLength=
{
30
}
placeholder=
""
allowClear
/>
</
Form
.
Item
>
<
Form
.
Item
>
<
Row
gutter=
{
20
}
>
<
Col
span=
{
12
}
>
<
div
className=
'justcontent-color-inline'
>
<
div
className=
"justcontent-color-inline"
>
<
Form
.
Item
label=
'背景颜色'
name=
'bgColor'
className=
'flex-max'
rules=
{
[{
pattern
:
/^#
[
0-9A-Fa-f
]
{6}$/i
,
message
:
'请输入正确的16进制色值'
}]
}
>
<
Input
maxLength=
{
100
}
placeholder=
''
allowClear
/>
label=
"背景颜色"
name=
"bgColor"
className=
"flex-max"
rules=
{
[{
pattern
:
/^#
[
0-9A-Fa-f
]
{6}$/i
,
message
:
'请输入正确的16进制色值'
}]
}
>
<
Input
maxLength=
{
100
}
placeholder=
""
allowClear
/>
</
Form
.
Item
>
<
Form
.
Item
label=
{
` `
}
>
<
ColorPicker
disabledAlpha
format=
'hex'
value=
{
bgColorValue
}
defaultValue=
{
bgColorValue
}
onChange=
{
bgColorChange
}
/>
<
ColorPicker
disabledAlpha
format=
"hex"
value=
{
bgColorValue
}
defaultValue=
{
bgColorValue
}
onChange=
{
bgColorChange
}
/>
</
Form
.
Item
>
</
div
>
</
Col
>
<
Col
span=
{
12
}
>
<
div
className=
'justcontent-color-inline'
>
<
div
className=
"justcontent-color-inline"
>
<
Form
.
Item
label=
'文本颜色'
name=
'textColor'
className=
'flex-max'
rules=
{
[{
pattern
:
/^#
[
0-9A-Fa-f
]
{6}$/i
,
message
:
'请输入正确的16进制色值'
}]
}
>
<
Input
placeholder=
''
allowClear
/>
label=
"文本颜色"
name=
"textColor"
className=
"flex-max"
rules=
{
[{
pattern
:
/^#
[
0-9A-Fa-f
]
{6}$/i
,
message
:
'请输入正确的16进制色值'
}]
}
>
<
Input
placeholder=
""
allowClear
/>
</
Form
.
Item
>
<
Form
.
Item
label=
{
` `
}
>
<
ColorPicker
disabledAlpha
format=
'hex'
value=
{
textColor
}
defaultValue=
{
textColor
}
onChange=
{
textColorChange
}
/>
<
ColorPicker
disabledAlpha
format=
"hex"
value=
{
textColor
}
defaultValue=
{
textColor
}
onChange=
{
textColorChange
}
/>
</
Form
.
Item
>
</
div
>
</
Col
>
<
Col
span=
{
12
}
>
<
Form
.
Item
label=
'字体'
name=
'family'
>
<
Form
.
Item
label=
"字体"
name=
"family"
>
<
Select
>
{
fontFamilyList
.
map
((
item
,
index
)
=>
{
return
(
<
Select
.
Option
value=
{
item
.
value
}
key=
{
index
}
>
{
item
.
name
}
</
Select
.
Option
>
)
;
)
})
}
</
Select
>
</
Form
.
Item
>
</
Col
>
<
Col
span=
{
12
}
>
<
Form
.
Item
label=
'文字大小'
name=
'size'
rules=
{
[{
required
:
true
,
message
:
'请输入文字大小'
}]
}
>
<
Form
.
Item
label=
"文字大小"
name=
"size"
rules=
{
[{
required
:
true
,
message
:
'请输入文字大小'
}]
}
>
<
Select
>
{
font
F
izeList
.
map
((
item
,
index
)
=>
{
{
font
S
izeList
.
map
((
item
,
index
)
=>
{
return
(
<
Select
.
Option
value=
{
item
.
value
}
key=
{
index
}
>
{
item
.
name
}
</
Select
.
Option
>
)
;
)
})
}
</
Select
>
</
Form
.
Item
>
...
...
@@ -271,37 +227,28 @@ const ChapterItemModal = forwardRef((props, ref) => {
/> */
}
</
Col
>
<
Col
span=
{
12
}
>
<
Form
.
Item
label=
'对齐方式'
name=
'align'
rules=
{
[{
required
:
true
,
message
:
'请选择对齐方式'
}]
}
>
<
Form
.
Item
label=
"对齐方式"
name=
"align"
rules=
{
[{
required
:
true
,
message
:
'请选择对齐方式'
}]
}
>
<
Select
>
<
Select
.
Option
value=
'left'
>
左对齐
</
Select
.
Option
>
<
Select
.
Option
value=
'center'
>
居中对齐
</
Select
.
Option
>
<
Select
.
Option
value=
'right'
>
右对齐
</
Select
.
Option
>
<
Select
.
Option
value=
"left"
>
左对齐
</
Select
.
Option
>
<
Select
.
Option
value=
"center"
>
居中对齐
</
Select
.
Option
>
<
Select
.
Option
value=
"right"
>
右对齐
</
Select
.
Option
>
</
Select
>
</
Form
.
Item
>
</
Col
>
<
Col
span=
{
12
}
>
<
Form
.
Item
label=
'高度'
name=
'height'
>
<
InputNumber
controls=
{
false
}
placeholder=
''
style=
{
{
width
:
'100%'
,
textAlign
:
'left'
}
}
allowClear
/>
<
Form
.
Item
label=
"高度"
name=
"height"
>
<
InputNumber
controls=
{
false
}
placeholder=
""
style=
{
{
width
:
'100%'
,
textAlign
:
'left'
}
}
allowClear
/>
</
Form
.
Item
>
</
Col
>
<
Col
span=
{
12
}
></
Col
>
</
Row
>
</
Form
.
Item
>
<
Form
.
Item
className=
'editor-form-buttons'
>
<
Form
.
Item
className=
"editor-form-buttons"
>
<
Space
>
<
Button
type=
'default'
onClick=
{
()
=>
setSectionVisible
(
false
)
}
>
<
Button
type=
"default"
onClick=
{
()
=>
setSectionVisible
(
false
)
}
>
取消
</
Button
>
<
Button
type=
'primary'
htmlType=
'submit'
>
<
Button
type=
"primary"
htmlType=
"submit"
>
{
nowRandom
?
'更新'
:
'插入'
}
</
Button
>
</
Space
>
...
...
@@ -309,7 +256,7 @@ const ChapterItemModal = forwardRef((props, ref) => {
</
Form
>
</
div
>
</
div
>
)
;
})
;
)
})
export
default
ChapterItemModal
;
export
default
ChapterItemModal
src/common/wangeditor-customer/components/chapter-title.jsx
浏览文件 @
f77be006
import
React
,
{
useState
,
useEffect
,
forwardRef
,
useImperativeHandle
}
from
'react'
;
import
{
Modal
,
Divider
,
Upload
,
Input
,
Space
,
Button
,
Form
,
Spin
,
message
,
ColorPicker
,
Select
,
InputNumber
,
Row
,
Col
,
}
from
'antd'
;
import
{
CloudUploadOutlined
}
from
'@ant-design/icons'
;
import
{
DomEditor
,
SlateEditor
,
SlateTransforms
,
SlateElement
}
from
'@wangeditor/editor'
;
import
{
fontFamilyList
,
fontFizeList
}
from
'../utils/setting'
;
import
'./index.less'
;
import
$
from
'jquery'
;
import
React
,
{
useState
,
useEffect
,
forwardRef
,
useImperativeHandle
}
from
'react'
import
{
Modal
,
Divider
,
Upload
,
Input
,
Space
,
Button
,
Form
,
Spin
,
message
,
ColorPicker
,
Select
,
InputNumber
,
Row
,
Col
}
from
'antd'
import
{
CloudUploadOutlined
}
from
'@ant-design/icons'
import
{
DomEditor
,
SlateEditor
,
SlateTransforms
,
SlateElement
}
from
'@wangeditor/editor'
import
{
fontFamilyList
,
fontSizeList
}
from
'../utils/setting'
import
'./index.less'
import
$
from
'jquery'
import
{
partSize
,
normalUploader
,
multipartUploader
}
from
'../utils/upload'
;
import
{
partSize
,
normalUploader
,
multipartUploader
}
from
'../utils/upload'
const
{
Dragger
}
=
Upload
;
const
{
Dragger
}
=
Upload
const
imgUrl2
=
'http://zxts-book-file.zijingebook.com/2024/03/05/image-1709606013683-6k5qmzf75zj.png'
;
const
imgUrl2
=
'http://zxts-book-file.zijingebook.com/2024/03/05/image-1709606013683-6k5qmzf75zj.png'
const
ImageModal
=
forwardRef
((
props
,
ref
)
=>
{
const
{
editor
,
ossClient
,
setTitleVisible
,
titleInfo
,
setTitleInfo
}
=
props
;
const
{
editor
,
ossClient
,
setTitleVisible
,
titleInfo
,
setTitleInfo
}
=
props
const
fileAccept
=
[
'.png'
,
'.jpg'
,
'.jpeg'
,
'.svg'
]
;
const
[
form
]
=
Form
.
useForm
()
;
const
[
bgColorValue
,
setBgColorValue
]
=
useState
(
'#000000'
)
;
const
[
textColor
,
setTextColor
]
=
useState
(
'#ffffff'
)
;
const
[
uploading
,
setUploading
]
=
useState
(
false
)
;
// 文件上传状态
const
[
progress
,
setProgress
]
=
useState
(
0
)
;
const
[
file
,
setFile
]
=
useState
({})
;
const
[
fileList
,
setFileList
]
=
useState
([])
;
const
[
imgUrl
,
setImgUrl
]
=
useState
(
''
)
;
const
fileAccept
=
[
'.png'
,
'.jpg'
,
'.jpeg'
,
'.svg'
]
const
[
form
]
=
Form
.
useForm
()
const
[
bgColorValue
,
setBgColorValue
]
=
useState
(
'#000000'
)
const
[
textColor
,
setTextColor
]
=
useState
(
'#ffffff'
)
const
[
uploading
,
setUploading
]
=
useState
(
false
)
// 文件上传状态
const
[
progress
,
setProgress
]
=
useState
(
0
)
const
[
file
,
setFile
]
=
useState
({})
const
[
fileList
,
setFileList
]
=
useState
([])
const
[
imgUrl
,
setImgUrl
]
=
useState
(
''
)
const
[
initValues
,
setInitValues
]
=
useState
({
title
:
''
,
size
:
32
,
...
...
@@ -48,39 +32,39 @@ const ImageModal = forwardRef((props, ref) => {
align
:
'left'
,
bgColor
:
bgColorValue
,
height
:
200
,
family
:
'黑体'
,
})
;
family
:
'黑体'
})
// 是否更新的判断
const
[
toolStatus
,
setToolStatus
]
=
useState
(
false
)
;
const
[
nowRandom
,
setNowRandom
]
=
useState
(
null
)
;
const
[
toolStatus
,
setToolStatus
]
=
useState
(
false
)
const
[
nowRandom
,
setNowRandom
]
=
useState
(
null
)
useEffect
(()
=>
{
if
(
titleInfo
&&
Object
.
entries
(
titleInfo
).
length
>
0
)
{
setInitValues
({
...
titleInfo
})
;
setImgUrl
(
titleInfo
.
bgImgUrl
)
;
form
.
setFieldsValue
({
...
titleInfo
})
;
setTextColor
(
titleInfo
.
textColor
)
;
setBgColorValue
(
titleInfo
.
bgColor
)
;
setNowRandom
(
titleInfo
.
random
)
;
setTitleInfo
({})
;
setInitValues
({
...
titleInfo
})
setImgUrl
(
titleInfo
.
bgImgUrl
)
form
.
setFieldsValue
({
...
titleInfo
})
setTextColor
(
titleInfo
.
textColor
)
setBgColorValue
(
titleInfo
.
bgColor
)
setNowRandom
(
titleInfo
.
random
)
setTitleInfo
({})
}
},
[
titleInfo
])
;
},
[
titleInfo
])
useImperativeHandle
(
ref
,
()
=>
{
return
{
form
,
setInitValues
,
}
;
})
;
setInitValues
}
})
const
textColorChange
=
(
value
,
hex
)
=>
{
setTextColor
(
hex
)
;
form
.
setFieldsValue
({
textColor
:
hex
})
;
}
;
setTextColor
(
hex
)
form
.
setFieldsValue
({
textColor
:
hex
})
}
const
bgColorChange
=
(
value
,
hex
)
=>
{
setBgColorValue
(
hex
)
;
form
.
setFieldsValue
({
bgColor
:
hex
})
;
}
;
setBgColorValue
(
hex
)
form
.
setFieldsValue
({
bgColor
:
hex
})
}
const
callback
=
({
title
,
random
,
bgImgUrl
,
textColor
,
align
,
height
,
size
,
family
})
=>
{
form
.
setFieldsValue
({
...
...
@@ -90,116 +74,116 @@ const ImageModal = forwardRef((props, ref) => {
align
:
align
,
height
:
height
,
size
:
size
,
family
:
family
,
})
;
setImgUrl
(
bgImgUrl
)
;
setToolStatus
(
true
)
;
setNowRandom
(
random
)
;
}
;
family
:
family
})
setImgUrl
(
bgImgUrl
)
setToolStatus
(
true
)
setNowRandom
(
random
)
}
const
normFile
=
(
e
)
=>
{
const
normFile
=
e
=>
{
if
(
Array
.
isArray
(
e
))
{
return
e
;
return
e
}
return
e
?.
fileList
}
return
e
?.
fileList
;
};
const
uploadProps
=
{
name
:
'file'
,
maxCount
:
1
,
showUploadList
:
false
,
accept
:
fileAccept
.
join
(
','
),
beforeUpload
:
(
file
,
fileList
)
=>
{
const
fileExt
=
file
.
name
.
substring
(
file
.
name
.
lastIndexOf
(
'.'
))
;
const
fileExt
=
file
.
name
.
substring
(
file
.
name
.
lastIndexOf
(
'.'
))
if
(
!
fileAccept
.
includes
(
fileExt
.
toLowerCase
()))
{
message
.
error
(
'请上传正确格式的图片'
)
;
return
false
;
message
.
error
(
'请上传正确格式的图片'
)
return
false
}
setFile
(
file
)
;
setFileList
(
fileList
)
;
setFile
(
file
)
setFileList
(
fileList
)
},
customRequest
:
async
()
=>
{
setUploading
(
true
)
;
let
data
=
null
;
setUploading
(
true
)
let
data
=
null
if
(
file
.
size
>=
partSize
)
{
data
=
await
multipartUploader
(
file
,
'image'
,
ossClient
,
(
progress
,
checkpoint
)
=>
{
console
.
log
(
`上传进度
${
progress
}
`
)
;
setProgress
(
parseInt
(
progress
*
100
))
;
})
;
console
.
log
(
'multipartUploader --> '
,
data
)
;
console
.
log
(
`上传进度
${
progress
}
`
)
setProgress
(
parseInt
(
progress
*
100
))
})
console
.
log
(
'multipartUploader --> '
,
data
)
}
else
{
data
=
await
normalUploader
(
file
,
'image'
,
ossClient
)
;
console
.
log
(
'normalUploader --> '
,
data
)
;
data
=
await
normalUploader
(
file
,
'image'
,
ossClient
)
console
.
log
(
'normalUploader --> '
,
data
)
}
if
(
data
.
status
===
200
&&
data
.
statusCode
===
200
)
{
const
{
url
,
name
}
=
data
;
setImgUrl
(
url
);
form
.
setFieldsValue
({
bgImgUrl
:
url
});
const
{
url
,
name
}
=
data
setImgUrl
(
url
)
form
.
setFieldsValue
({
bgImgUrl
:
url
})
}
setUploading
(
false
)
}
}
setUploading
(
false
);
},
};
const
onFinish
=
(
values
)
=>
{
editor
.
restoreSelection
()
;
const
onFinish
=
values
=>
{
editor
.
restoreSelection
()
const
nodeEntries
=
SlateEditor
.
nodes
(
editor
,
{
match
:
(
node
)
=>
{
match
:
node
=>
{
// JS syntax
if
(
SlateElement
.
isElement
(
node
))
{
if
(
node
.
type
===
'paragraph'
)
{
return
true
;
// 匹配 paragraph
return
true
// 匹配 paragraph
}
}
return
false
;
return
false
},
universal
:
true
,
})
;
universal
:
true
})
for
(
let
nodeEntry
of
nodeEntries
)
{
const
[
node
,
path
]
=
nodeEntry
;
const
[
node
,
path
]
=
nodeEntry
if
(
node
.
children
[
0
].
text
===
''
)
{
SlateTransforms
.
removeNodes
(
editor
)
;
SlateTransforms
.
removeNodes
(
editor
)
}
}
if
(
nowRandom
)
{
const
nodeEntries
=
SlateEditor
.
nodes
(
editor
,
{
match
:
(
node
)
=>
{
match
:
node
=>
{
// JS syntax
if
(
SlateElement
.
isElement
(
node
))
{
if
(
node
.
type
===
'chapterHeader'
)
{
return
true
;
// 匹配 chapterHeader
return
true
// 匹配 chapterHeader
}
}
return
false
;
return
false
},
universal
:
true
,
})
;
universal
:
true
})
if
(
nodeEntries
)
{
for
(
let
nodeEntry
of
nodeEntries
)
{
const
[
node
,
path
]
=
nodeEntry
;
const
[
node
,
path
]
=
nodeEntry
if
(
parseInt
(
node
.
random
)
===
parseInt
(
nowRandom
))
{
SlateTransforms
.
setNodes
(
editor
,
{
...
node
,
...
values
},
{
at
:
path
})
;
setImgUrl
(
''
)
;
setProgress
(
0
)
;
setUploading
(
false
)
;
form
.
resetFields
()
;
setTitleVisible
(
false
)
;
return
false
;
SlateTransforms
.
setNodes
(
editor
,
{
...
node
,
...
values
},
{
at
:
path
})
setImgUrl
(
''
)
setProgress
(
0
)
setUploading
(
false
)
form
.
resetFields
()
setTitleVisible
(
false
)
return
false
}
}
}
return
false
;
return
false
}
const
html
=
editor
.
getHtml
()
;
const
parser
=
new
DOMParser
()
;
let
random
=
Math
.
random
().
toString
(
10
).
substring
(
2
,
10
)
;
const
html
=
editor
.
getHtml
()
const
parser
=
new
DOMParser
()
let
random
=
Math
.
random
().
toString
(
10
).
substring
(
2
,
10
)
if
(
nowRandom
)
{
random
=
nowRandom
;
random
=
nowRandom
}
const
docBody
=
parser
.
parseFromString
(
html
,
'text/html'
)
;
const
section
=
docBody
.
body
.
querySelectorAll
(
'.chapter-item-header'
)
;
// 是否存在章头
const
docBody
=
parser
.
parseFromString
(
html
,
'text/html'
)
const
section
=
docBody
.
body
.
querySelectorAll
(
'.chapter-item-header'
)
// 是否存在章头
editor
.
insertNode
({
type
:
'chapterHeader'
,
...
...
@@ -213,73 +197,60 @@ const ImageModal = forwardRef((props, ref) => {
size
:
values
.
size
?
values
.
size
:
26
,
family
:
values
.
family
,
callback
:
callback
,
children
:
[{
text
:
''
}]
,
})
;
setImgUrl
(
''
)
;
setProgress
(
0
)
;
setUploading
(
false
)
;
form
.
resetFields
()
;
setTitleVisible
(
false
)
;
}
;
children
:
[{
text
:
''
}]
})
setImgUrl
(
''
)
setProgress
(
0
)
setUploading
(
false
)
form
.
resetFields
()
setTitleVisible
(
false
)
}
return
(
<
div
>
<
Divider
/>
<
div
className=
'editor-content-form'
>
<
Form
layout=
'vertical'
name=
'validate_other'
form=
{
form
}
onFinish=
{
onFinish
}
initialValues=
{
initValues
}
>
<
Form
.
Item
label=
'章头背景图片'
valuePropName=
'fileList'
getValueFromEvent=
{
normFile
}
name=
'bgImgUrl'
>
<
div
className=
"editor-content-form"
>
<
Form
layout=
"vertical"
name=
"validate_other"
form=
{
form
}
onFinish=
{
onFinish
}
initialValues=
{
initValues
}
>
<
Form
.
Item
label=
"章头背景图片"
valuePropName=
"fileList"
getValueFromEvent=
{
normFile
}
name=
"bgImgUrl"
>
<
Spin
spinning=
{
uploading
}
tip=
{
`${progress}%`
}
>
<
div
className=
'editor-dragger'
>
<
div
className=
"editor-dragger"
>
<
Dragger
{
...
uploadProps
}
showUploadList=
{
false
}
>
{
!
imgUrl
&&
(
<
div
className=
'editor-uploader-process'
>
<
p
className=
'ant-upload-drag-icon'
>
<
div
className=
"editor-uploader-process"
>
<
p
className=
"ant-upload-drag-icon"
>
<
CloudUploadOutlined
style=
{
{
fontSize
:
40
}
}
/>
</
p
>
<
p
className=
'ant-upload-text'
>
点击上传或拖拽到此处上传
</
p
>
<
p
className=
'ant-upload-hint'
>
支持上传 .png、.jpg、.jpeg、.svg格式的图片
</
p
>
<
p
className=
"ant-upload-text"
>
点击上传或拖拽到此处上传
</
p
>
<
p
className=
"ant-upload-hint"
>
支持上传 .png、.jpg、.jpeg、.svg格式的图片
</
p
>
</
div
>
)
}
{
imgUrl
&&
(
<>
<
div
className=
'editor-uploader-result'
>
<
div
className=
'editor-uploader-result-img'
>
<
img
src=
{
imgUrl
}
alt=
''
/>
<
div
className=
"editor-uploader-result"
>
<
div
className=
"editor-uploader-result-img"
>
<
img
src=
{
imgUrl
}
alt=
""
/>
</
div
>
<
div
className=
'editor-uploader-result-tips'
>
<
div
className=
"editor-uploader-result-tips"
>
<
Space
>
<
Button
size=
'small'
type=
'primary'
size=
"small"
type=
"primary"
ghost
onClick=
{
()
=>
{
setImgUrl
(
null
);
form
.
setFieldsValue
({
bgImgUrl
:
''
});
}
}
>
setImgUrl
(
null
)
form
.
setFieldsValue
({
bgImgUrl
:
''
})
}
}
>
替换
</
Button
>
<
Button
size=
'small'
type=
'default'
onClick=
{
(
ev
)
=>
{
ev
.
stopPropagation
();
ev
.
preventDefault
();
setImgUrl
(
null
);
form
.
setFieldsValue
({
bgImgUrl
:
''
});
}
}
>
size=
"small"
type=
"default"
onClick=
{
ev
=>
{
ev
.
stopPropagation
()
ev
.
preventDefault
()
setImgUrl
(
null
)
form
.
setFieldsValue
({
bgImgUrl
:
''
})
}
}
>
移除
</
Button
>
</
Space
>
...
...
@@ -291,119 +262,87 @@ const ImageModal = forwardRef((props, ref) => {
</
div
>
</
Spin
>
</
Form
.
Item
>
<
Form
.
Item
label=
'标题'
name=
'title'
rules=
{
[{
required
:
true
,
message
:
'请输入标题'
}]
}
extra=
'最多输入30字'
>
<
Input
maxLength=
{
30
}
placeholder=
''
allowClear
/>
<
Form
.
Item
label=
"标题"
name=
"title"
rules=
{
[{
required
:
true
,
message
:
'请输入标题'
}]
}
extra=
"最多输入30字"
>
<
Input
maxLength=
{
30
}
placeholder=
""
allowClear
/>
</
Form
.
Item
>
<
Form
.
Item
>
<
Row
gutter=
{
20
}
>
<
Col
span=
{
12
}
>
<
div
className=
'justcontent-color-inline'
>
<
div
className=
"justcontent-color-inline"
>
<
Form
.
Item
label=
'章头背景色'
name=
'bgColor'
className=
'flex-max'
rules=
{
[{
pattern
:
/^#
[
0-9A-Fa-f
]
{6}$/i
,
message
:
'请输入正确的16进制色值'
}]
}
>
<
Input
placeholder=
''
allowClear
/>
label=
"章头背景色"
name=
"bgColor"
className=
"flex-max"
rules=
{
[{
pattern
:
/^#
[
0-9A-Fa-f
]
{6}$/i
,
message
:
'请输入正确的16进制色值'
}]
}
>
<
Input
placeholder=
""
allowClear
/>
</
Form
.
Item
>
<
Form
.
Item
label=
{
` `
}
>
<
ColorPicker
disabledAlpha
value=
{
bgColorValue
}
defaultValue=
{
bgColorValue
}
format=
'hex'
onChange=
{
bgColorChange
}
/>
<
ColorPicker
disabledAlpha
value=
{
bgColorValue
}
defaultValue=
{
bgColorValue
}
format=
"hex"
onChange=
{
bgColorChange
}
/>
</
Form
.
Item
>
</
div
>
</
Col
>
<
Col
span=
{
12
}
>
<
div
className=
'justcontent-color-inline'
>
<
div
className=
"justcontent-color-inline"
>
<
Form
.
Item
label=
'文本颜色'
name=
'textColor'
className=
'flex-max'
rules=
{
[{
pattern
:
/^#
[
0-9A-Fa-f
]
{6}$/i
,
message
:
'请输入正确的16进制色值'
}]
}
>
<
Input
maxLength=
{
100
}
placeholder=
''
allowClear
/>
label=
"文本颜色"
name=
"textColor"
className=
"flex-max"
rules=
{
[{
pattern
:
/^#
[
0-9A-Fa-f
]
{6}$/i
,
message
:
'请输入正确的16进制色值'
}]
}
>
<
Input
maxLength=
{
100
}
placeholder=
""
allowClear
/>
</
Form
.
Item
>
<
Form
.
Item
label=
{
` `
}
>
<
ColorPicker
value=
{
textColor
}
defaultValue=
{
textColor
}
disabledAlpha
format=
'hex'
onChange=
{
textColorChange
}
/>
<
ColorPicker
value=
{
textColor
}
defaultValue=
{
textColor
}
disabledAlpha
format=
"hex"
onChange=
{
textColorChange
}
/>
</
Form
.
Item
>
</
div
>
</
Col
>
<
Col
span=
{
12
}
>
<
Form
.
Item
label=
'字体'
name=
'family'
>
<
Form
.
Item
label=
"字体"
name=
"family"
>
<
Select
>
{
fontFamilyList
.
map
((
item
,
index
)
=>
{
return
(
<
Select
.
Option
value=
{
item
.
value
}
key=
{
index
}
>
{
item
.
name
}
</
Select
.
Option
>
)
;
)
})
}
</
Select
>
</
Form
.
Item
>
</
Col
>
<
Col
span=
{
12
}
>
<
Form
.
Item
label=
'文字大小'
name=
'size'
rules=
{
[{
required
:
true
,
message
:
'请输入文字大小'
}]
}
>
<
Form
.
Item
label=
"文字大小"
name=
"size"
rules=
{
[{
required
:
true
,
message
:
'请输入文字大小'
}]
}
>
<
Select
>
{
font
F
izeList
.
map
((
item
,
index
)
=>
{
{
font
S
izeList
.
map
((
item
,
index
)
=>
{
return
(
<
Select
.
Option
value=
{
item
.
value
}
key=
{
index
}
>
{
item
.
name
}
</
Select
.
Option
>
)
;
)
})
}
</
Select
>
</
Form
.
Item
>
</
Col
>
<
Col
span=
{
12
}
>
<
Form
.
Item
label=
'对齐方式'
name=
'align'
rules=
{
[{
required
:
true
,
message
:
'请选择对齐方式'
}]
}
>
<
Form
.
Item
label=
"对齐方式"
name=
"align"
rules=
{
[{
required
:
true
,
message
:
'请选择对齐方式'
}]
}
>
<
Select
>
<
Select
.
Option
value=
'left'
>
左对齐
</
Select
.
Option
>
<
Select
.
Option
value=
'center'
>
居中对齐
</
Select
.
Option
>
<
Select
.
Option
value=
'right'
>
右对齐
</
Select
.
Option
>
<
Select
.
Option
value=
"left"
>
左对齐
</
Select
.
Option
>
<
Select
.
Option
value=
"center"
>
居中对齐
</
Select
.
Option
>
<
Select
.
Option
value=
"right"
>
右对齐
</
Select
.
Option
>
</
Select
>
</
Form
.
Item
>
</
Col
>
<
Col
span=
{
12
}
>
<
Form
.
Item
label=
'高度'
name=
'height'
>
<
InputNumber
controls=
{
false
}
placeholder=
''
style=
{
{
width
:
'100%'
,
textAlign
:
'left'
}
}
allowClear
/>
<
Form
.
Item
label=
"高度"
name=
"height"
>
<
InputNumber
controls=
{
false
}
placeholder=
""
style=
{
{
width
:
'100%'
,
textAlign
:
'left'
}
}
allowClear
/>
</
Form
.
Item
>
</
Col
>
</
Row
>
</
Form
.
Item
>
<
Form
.
Item
className=
'editor-form-buttons'
>
<
Form
.
Item
className=
"editor-form-buttons"
>
<
Space
>
<
Button
type=
'default'
disabled=
{
uploading
}
onClick=
{
()
=>
setTitleVisible
(
false
)
}
>
<
Button
type=
"default"
disabled=
{
uploading
}
onClick=
{
()
=>
setTitleVisible
(
false
)
}
>
取消
</
Button
>
<
Button
type=
'primary'
disabled=
{
uploading
}
htmlType=
'submit'
>
<
Button
type=
"primary"
disabled=
{
uploading
}
htmlType=
"submit"
>
{
nowRandom
?
'更新'
:
'插入'
}
</
Button
>
</
Space
>
...
...
@@ -411,7 +350,7 @@ const ImageModal = forwardRef((props, ref) => {
</
Form
>
</
div
>
</
div
>
)
;
})
;
)
})
export
default
ImageModal
;
export
default
ImageModal
src/common/wangeditor-customer/components/expand.jsx
浏览文件 @
f77be006
import
React
,
{
useState
,
useEffect
,
forwardRef
,
useImperativeHandle
}
from
'react'
;
import
{
Modal
,
Divider
,
ColorPicker
,
Input
,
Space
,
Button
,
Form
,
Spin
,
message
,
Select
,
Row
,
Col
,
}
from
'antd'
;
import
{
SlateTransforms
,
SlateEditor
,
DomEditor
,
SlateElement
}
from
'@wangeditor/editor'
;
import
React
,
{
useState
,
useEffect
,
forwardRef
,
useImperativeHandle
}
from
'react'
import
{
Modal
,
Divider
,
ColorPicker
,
Input
,
Space
,
Button
,
Form
,
Spin
,
message
,
Select
,
Row
,
Col
}
from
'antd'
import
{
SlateTransforms
,
SlateEditor
,
DomEditor
,
SlateElement
}
from
'@wangeditor/editor'
import
{
font
FizeList
}
from
'../utils/setting'
;
import
EditorSimple
from
'@/common/editor-simple'
;
import
'./index.less'
;
import
{
addExpandRead
,
expandReadInfo
}
from
'../utils/request'
;
import
{
font
SizeList
}
from
'../utils/setting'
import
EditorSimple
from
'@/common/editor-simple'
import
'./index.less'
import
{
addExpandRead
,
expandReadInfo
}
from
'../utils/request'
const
ExpandModal
=
forwardRef
((
props
,
ref
)
=>
{
const
{
editor
,
ossClient
,
bookId
,
chapterId
,
setExpandVisible
,
setExpandInfo
,
expandInfo
,
selectionSize
=
18
,
}
=
props
;
const
{
editor
,
ossClient
,
bookId
,
chapterId
,
setExpandVisible
,
setExpandInfo
,
expandInfo
,
selectionSize
=
18
}
=
props
const
[
form
]
=
Form
.
useForm
()
;
const
flexValue
=
Form
.
useWatch
(
'flex'
,
form
)
;
const
[
loadLoading
,
setLoadLoading
]
=
useState
(
false
)
;
const
[
content
,
setContent
]
=
useState
(
''
)
;
const
[
themeValue
,
setThemeValue
]
=
useState
(
'#ab1941'
)
;
const
[
oldFlex
,
setOldFlex
]
=
useState
(
null
)
;
const
[
form
]
=
Form
.
useForm
()
const
flexValue
=
Form
.
useWatch
(
'flex'
,
form
)
const
[
loadLoading
,
setLoadLoading
]
=
useState
(
false
)
const
[
content
,
setContent
]
=
useState
(
''
)
const
[
themeValue
,
setThemeValue
]
=
useState
(
'#ab1941'
)
const
[
oldFlex
,
setOldFlex
]
=
useState
(
null
)
const
[
initvalues
,
setInitValue
]
=
useState
({
flex
:
1
,
name
:
''
,
title
:
''
,
content
:
''
,
theme
:
'#ab1941'
,
fontSize
:
selectionSize
||
18
,
})
;
const
[
nowRandom
,
setNowRandom
]
=
useState
(
null
)
;
fontSize
:
selectionSize
||
18
})
const
[
nowRandom
,
setNowRandom
]
=
useState
(
null
)
// const [fontsize, setFontsize] = useState(selectionSize);
const
getExpandInfo
=
async
(
random
)
=>
{
setLoadLoading
(
true
)
;
const
getExpandInfo
=
async
random
=>
{
setLoadLoading
(
true
)
const
data
=
await
expandReadInfo
({
book_id
:
bookId
,
chapter_id
:
chapterId
,
position
:
nowRandom
||
random
,
})
;
position
:
nowRandom
||
random
})
if
(
data
)
{
setContent
(
data
.
content
)
;
setContent
(
data
.
content
)
// setInitValue({ ...data });
// form.setFieldsValue({ ...data });
}
setLoadLoading
(
false
)
;
}
;
setLoadLoading
(
false
)
}
const
textColorChange
=
(
value
,
hex
)
=>
{
setThemeValue
(
hex
)
;
form
.
setFieldsValue
({
theme
:
hex
})
;
}
;
setThemeValue
(
hex
)
form
.
setFieldsValue
({
theme
:
hex
})
}
useEffect
(()
=>
{
if
(
Object
.
entries
(
expandInfo
).
length
>
0
)
{
(
async
()
=>
{
setLoadLoading
(
true
)
;
let
obj
=
{
...
expandInfo
,
flex
:
parseInt
(
expandInfo
.
flex
),
fontSize
:
expandInfo
.
fontsize
}
;
setThemeValue
(
expandInfo
.
theme
)
;
setOldFlex
(
parseInt
(
expandInfo
.
flex
))
;
setInitValue
(
obj
)
;
form
.
setFieldsValue
(
obj
)
;
setNowRandom
(
expandInfo
.
random
)
;
await
getExpandInfo
(
expandInfo
.
random
)
;
setExpandInfo
({})
;
;
(
async
()
=>
{
setLoadLoading
(
true
)
let
obj
=
{
...
expandInfo
,
flex
:
parseInt
(
expandInfo
.
flex
),
fontSize
:
expandInfo
.
fontsize
}
setThemeValue
(
expandInfo
.
theme
)
setOldFlex
(
parseInt
(
expandInfo
.
flex
))
setInitValue
(
obj
)
form
.
setFieldsValue
(
obj
)
setNowRandom
(
expandInfo
.
random
)
await
getExpandInfo
(
expandInfo
.
random
)
setExpandInfo
({})
// setFontsize(expandInfo.fontsize);
setLoadLoading
(
false
)
;
})()
;
setLoadLoading
(
false
)
})()
}
},
[
expandInfo
])
;
},
[
expandInfo
])
const
removeInlineElement
=
(
editor
,
type
)
=>
{
const
{
selection
}
=
editor
;
if
(
!
selection
)
return
;
const
{
selection
}
=
editor
if
(
!
selection
)
return
const
[
inlineNode
]
=
SlateEditor
.
nodes
(
editor
,
{
match
:
(
node
)
=>
SlateElement
.
isElement
(
node
)
&&
node
.
type
===
type
,
})
;
console
.
log
(
'inlineNode'
,
inlineNode
)
;
if
(
!
inlineNode
)
return
;
const
[
parent
]
=
SlateEditor
.
parent
(
editor
,
inlineNode
[
1
])
;
match
:
node
=>
SlateElement
.
isElement
(
node
)
&&
node
.
type
===
type
})
console
.
log
(
'inlineNode'
,
inlineNode
)
if
(
!
inlineNode
)
return
const
[
parent
]
=
SlateEditor
.
parent
(
editor
,
inlineNode
[
1
])
if
(
!
parent
||
SlateElement
.
isElement
(
parent
))
{
Transforms
.
removeNodes
(
editor
,
{
match
:
(
node
)
=>
node
.
type
===
type
});
Transforms
.
removeNodes
(
editor
,
{
match
:
node
=>
node
.
type
===
type
})
}
else
{
Transforms
.
removeNodes
(
editor
,
{
at
:
inlineNode
[
1
]
});
Transforms
.
removeNodes
(
editor
,
{
at
:
inlineNode
[
1
]
})
}
}
};
const
onFinish
=
async
(
values
)
=>
{
editor
.
restoreSelection
()
;
const
onFinish
=
async
values
=>
{
editor
.
restoreSelection
()
if
(
parseInt
(
values
.
flex
)
===
1
)
{
const
nodeEntries
=
SlateEditor
.
nodes
(
editor
,
{
match
:
(
node
)
=>
{
match
:
node
=>
{
// JS syntax
if
(
SlateElement
.
isElement
(
node
))
{
if
(
node
.
type
===
'paragraph'
)
{
return
true
;
// 匹配 paragraph
return
true
// 匹配 paragraph
}
}
return
false
;
return
false
},
universal
:
true
,
})
;
universal
:
true
})
for
(
let
nodeEntry
of
nodeEntries
)
{
const
[
node
,
path
]
=
nodeEntry
;
const
[
node
,
path
]
=
nodeEntry
if
(
node
.
children
[
0
].
text
===
''
)
{
SlateTransforms
.
removeNodes
(
editor
)
;
SlateTransforms
.
removeNodes
(
editor
)
}
}
}
setLoadLoading
(
true
)
;
setLoadLoading
(
true
)
const
{
flex
,
theme
,
fontSize
,
...
other
}
=
values
;
const
{
flex
,
theme
,
fontSize
,
...
other
}
=
values
if
(
nowRandom
)
{
let
props
=
{
flex
:
parseInt
(
values
.
flex
),
...
...
@@ -135,34 +113,34 @@ const ExpandModal = forwardRef((props, ref) => {
title
:
values
.
title
,
random
:
nowRandom
,
theme
:
values
.
theme
,
fontsize
:
values
.
fontSize
,
}
;
fontsize
:
values
.
fontSize
}
if
(
parseInt
(
oldFlex
)
!==
flex
)
{
SlateTransforms
.
removeNodes
(
editor
)
;
SlateTransforms
.
removeNodes
(
editor
)
if
(
parseInt
(
values
.
flex
)
===
1
)
{
editor
.
insertNode
({
type
:
'chapterExpandRead'
,
...
props
,
children
:
[{
text
:
''
}]
,
})
;
children
:
[{
text
:
''
}]
})
}
else
{
editor
.
insertNode
({
type
:
'chapterExpandReadSimple'
,
...
props
,
children
:
[{
text
:
''
}]
,
})
;
children
:
[{
text
:
''
}]
})
}
}
else
{
if
(
parseInt
(
flex
)
===
2
)
{
editor
.
move
(
1
)
;
editor
.
deleteBackward
()
;
editor
.
move
(
1
)
editor
.
deleteBackward
()
editor
.
insertNode
({
type
:
'chapterExpandReadSimple'
,
...
props
,
children
:
[{
text
:
''
}]
,
})
;
children
:
[{
text
:
''
}]
})
// const nodeEntries = SlateEditor.nodes(editor, {
// match: (node) => {
...
...
@@ -187,22 +165,22 @@ const ExpandModal = forwardRef((props, ref) => {
// }
}
else
{
const
nodeEntries
=
SlateEditor
.
nodes
(
editor
,
{
match
:
(
node
)
=>
{
match
:
node
=>
{
// JS syntax
if
(
SlateElement
.
isElement
(
node
))
{
if
(
node
.
type
===
'chapterExpandRead'
)
{
return
true
;
// 匹配 chapterToolTip
return
true
// 匹配 chapterToolTip
}
}
return
false
;
return
false
},
universal
:
true
,
})
;
universal
:
true
})
// 遍历匹配的节点迭代器,获取匹配的节点路径
for
(
let
nodeEntry
of
nodeEntries
)
{
const
[
node
,
path
]
=
nodeEntry
;
const
[
node
,
path
]
=
nodeEntry
if
(
node
.
random
===
nowRandom
)
{
SlateTransforms
.
setNodes
(
editor
,
props
,
{
at
:
path
})
;
SlateTransforms
.
setNodes
(
editor
,
props
,
{
at
:
path
})
}
}
}
...
...
@@ -213,22 +191,22 @@ const ExpandModal = forwardRef((props, ref) => {
chapter_id
:
chapterId
,
position
:
nowRandom
,
type
:
flex
,
...
other
,
})
;
setNowRandom
(
null
)
;
setExpandVisible
(
false
)
;
setLoadLoading
(
false
)
;
return
false
;
...
other
})
setNowRandom
(
null
)
setExpandVisible
(
false
)
setLoadLoading
(
false
)
return
false
}
let
random
=
Math
.
random
().
toString
(
10
).
substring
(
2
,
10
)
;
let
random
=
Math
.
random
().
toString
(
10
).
substring
(
2
,
10
)
const
data
=
await
addExpandRead
({
book_id
:
bookId
,
chapter_id
:
chapterId
,
position
:
random
,
type
:
flex
,
...
other
,
})
;
...
other
})
if
(
parseInt
(
values
.
flex
)
===
1
)
{
editor
.
insertNode
({
...
...
@@ -239,8 +217,8 @@ const ExpandModal = forwardRef((props, ref) => {
random
:
random
,
theme
:
values
.
theme
,
fontsize
:
values
.
fontSize
||
selectionSize
,
children
:
[{
text
:
''
}]
,
})
;
children
:
[{
text
:
''
}]
})
}
else
{
editor
.
insertNode
({
type
:
'chapterExpandReadSimple'
,
...
...
@@ -250,85 +228,57 @@ const ExpandModal = forwardRef((props, ref) => {
random
:
random
,
theme
:
values
.
theme
,
fontsize
:
values
.
fontSize
||
selectionSize
,
children
:
[{
text
:
''
}]
,
})
;
children
:
[{
text
:
''
}]
})
}
setNowRandom
(
null
)
;
setExpandVisible
(
false
)
;
setLoadLoading
(
false
)
;
}
;
setNowRandom
(
null
)
setExpandVisible
(
false
)
setLoadLoading
(
false
)
}
return
(
<
div
>
<
Divider
/>
<
div
className=
'editor-content-form'
>
<
Form
layout=
'vertical'
name=
'validate_other'
form=
{
form
}
onFinish=
{
onFinish
}
initialValues=
{
initvalues
}
>
<
Form
.
Item
label=
'扩展类型'
name=
'flex'
rules=
{
[{
required
:
true
,
message
:
'请选择扩展类型'
}]
}
>
<
div
className=
"editor-content-form"
>
<
Form
layout=
"vertical"
name=
"validate_other"
form=
{
form
}
onFinish=
{
onFinish
}
initialValues=
{
initvalues
}
>
<
Form
.
Item
label=
"扩展类型"
name=
"flex"
rules=
{
[{
required
:
true
,
message
:
'请选择扩展类型'
}]
}
>
<
Select
disabled=
{
oldFlex
>
0
?
true
:
false
}
>
<
Select
.
Option
value=
{
1
}
>
独立扩展
</
Select
.
Option
>
<
Select
.
Option
value=
{
2
}
>
行内扩展
</
Select
.
Option
>
</
Select
>
</
Form
.
Item
>
<
Form
.
Item
label=
'扩展名称'
name=
'name'
rules=
{
[{
required
:
true
,
message
:
'请输入扩展名称'
}]
}
extra=
'最多输入100字'
>
<
Input
maxLength=
{
100
}
placeholder=
''
allowClear
/>
<
Form
.
Item
label=
"扩展名称"
name=
"name"
rules=
{
[{
required
:
true
,
message
:
'请输入扩展名称'
}]
}
extra=
"最多输入100字"
>
<
Input
maxLength=
{
100
}
placeholder=
""
allowClear
/>
</
Form
.
Item
>
{
flexValue
===
1
&&
(
<
Form
.
Item
label=
'扩展标题'
name=
'title'
label=
"扩展标题"
name=
"title"
rules=
{
[
{
required
:
flexValue
===
1
?
true
:
false
,
message
:
'请输入扩展标题'
},
{
max
:
100
,
message
:
'最多输入100个字符!'
}
,
{
max
:
100
,
message
:
'最多输入100个字符!'
}
]
}
extra=
'最多输入100字'
>
<
Input
.
TextArea
maxLength=
{
100
}
autoSize=
{
{
minRows
:
2
,
maxRows
:
3
}
}
placeholder=
''
allowClear
/>
extra=
"最多输入100字"
>
<
Input
.
TextArea
maxLength=
{
100
}
autoSize=
{
{
minRows
:
2
,
maxRows
:
3
}
}
placeholder=
""
allowClear
/>
</
Form
.
Item
>
)
}
{
flexValue
===
1
?
(
<
Form
.
Item
>
<
div
className=
'justcontent-color-inline'
>
<
div
className=
"justcontent-color-inline"
>
<
Form
.
Item
label=
'扩展主题色'
name=
'theme'
className=
'flex-max'
label=
"扩展主题色"
name=
"theme"
className=
"flex-max"
rules=
{
[
{
required
:
true
,
message
:
'请选择颜色'
},
{
pattern
:
/^#
[
0-9A-Fa-f
]
{6}$/i
,
message
:
'请输入正确的16进制色值'
},
]
}
>
<
Input
placeholder=
''
allowClear
/>
{
pattern
:
/^#
[
0-9A-Fa-f
]
{6}$/i
,
message
:
'请输入正确的16进制色值'
}
]
}
>
<
Input
placeholder=
""
allowClear
/>
</
Form
.
Item
>
<
Form
.
Item
label=
{
` `
}
>
<
ColorPicker
disabledAlpha
value=
{
themeValue
}
defaultValue=
{
themeValue
}
format=
'hex'
onChange=
{
textColorChange
}
/>
<
ColorPicker
disabledAlpha
value=
{
themeValue
}
defaultValue=
{
themeValue
}
format=
"hex"
onChange=
{
textColorChange
}
/>
</
Form
.
Item
>
</
div
>
</
Form
.
Item
>
...
...
@@ -336,59 +286,47 @@ const ExpandModal = forwardRef((props, ref) => {
<
Form
.
Item
>
<
Row
gutter=
{
20
}
>
<
Col
span=
{
12
}
>
<
Form
.
Item
label=
'字号'
name=
'fontSize'
>
<
Form
.
Item
label=
"字号"
name=
"fontSize"
>
<
Select
>
{
font
F
izeList
.
map
((
item
,
index
)
=>
{
{
font
S
izeList
.
map
((
item
,
index
)
=>
{
return
(
<
Select
.
Option
value=
{
item
.
value
}
key=
{
index
}
>
{
item
.
name
}
</
Select
.
Option
>
)
;
)
})
}
</
Select
>
</
Form
.
Item
>
</
Col
>
<
Col
span=
{
12
}
>
<
div
className=
'justcontent-color-inline'
>
<
div
className=
"justcontent-color-inline"
>
<
Form
.
Item
label=
'扩展主题色'
name=
'theme'
className=
'flex-max'
label=
"扩展主题色"
name=
"theme"
className=
"flex-max"
rules=
{
[
{
required
:
true
,
message
:
'请选择颜色'
},
{
pattern
:
/^#
[
0-9A-Fa-f
]
{6}$/i
,
message
:
'请输入正确的16进制色值'
},
]
}
>
<
Input
placeholder=
''
allowClear
/>
{
pattern
:
/^#
[
0-9A-Fa-f
]
{6}$/i
,
message
:
'请输入正确的16进制色值'
}
]
}
>
<
Input
placeholder=
""
allowClear
/>
</
Form
.
Item
>
<
Form
.
Item
label=
{
` `
}
>
<
ColorPicker
disabledAlpha
value=
{
themeValue
}
defaultValue=
{
themeValue
}
format=
'hex'
onChange=
{
textColorChange
}
/>
<
ColorPicker
disabledAlpha
value=
{
themeValue
}
defaultValue=
{
themeValue
}
format=
"hex"
onChange=
{
textColorChange
}
/>
</
Form
.
Item
>
</
div
>
</
Col
>
</
Row
>
</
Form
.
Item
>
)
}
<
Form
.
Item
label=
'扩展内容'
name=
'content'
>
<
EditorSimple
form=
{
form
}
fieldName=
{
'content'
}
content=
{
content
}
loadLoading=
{
loadLoading
}
/>
<
Form
.
Item
label=
"扩展内容"
name=
"content"
>
<
EditorSimple
form=
{
form
}
fieldName=
{
'content'
}
content=
{
content
}
loadLoading=
{
loadLoading
}
/>
</
Form
.
Item
>
<
Form
.
Item
className=
'editor-form-buttons'
>
<
Form
.
Item
className=
"editor-form-buttons"
>
<
Space
>
<
Button
type=
'default'
onClick=
{
()
=>
setExpandVisible
(
false
)
}
>
<
Button
type=
"default"
onClick=
{
()
=>
setExpandVisible
(
false
)
}
>
取消
</
Button
>
<
Button
type=
'primary'
loadLoading=
{
loadLoading
}
htmlType=
'submit'
>
<
Button
type=
"primary"
loadLoading=
{
loadLoading
}
htmlType=
"submit"
>
{
nowRandom
?
'更新'
:
'插入'
}
</
Button
>
</
Space
>
...
...
@@ -396,7 +334,7 @@ const ExpandModal = forwardRef((props, ref) => {
</
Form
>
</
div
>
</
div
>
)
;
})
;
)
})
export
default
ExpandModal
;
export
default
ExpandModal
src/common/wangeditor-customer/components/gallery.jsx
浏览文件 @
f77be006
...
...
@@ -4,11 +4,11 @@ import './index.less'
import
{
addGallery
}
from
'../utils/request'
import
{
SlateEditor
,
SlateTransforms
,
SlateElement
}
from
'@wangeditor/editor'
import
{
findNodeWithParent
,
font
F
izeList
}
from
'../utils/setting'
import
{
findNodeWithParent
,
font
S
izeList
}
from
'../utils/setting'
import
GalleryFormItem
from
'./galleryItem'
const
randomOne
=
Math
.
random
().
toString
(
16
).
substring
(
2
,
10
)
const
GalleryModal
=
(
props
)
=>
{
const
GalleryModal
=
props
=>
{
const
{
editor
,
ossClient
,
galleryInfo
,
bookId
,
chapterId
,
setGalleryVisible
,
setGalleryInfo
,
selectionSize
=
18
,
isOnline
=
false
}
=
props
const
[
form
]
=
Form
.
useForm
()
...
...
@@ -57,7 +57,7 @@ const GalleryModal = (props) => {
const
[
activeKey
,
setActiveKey
]
=
useState
(
initialItems
[
0
].
key
)
const
[
items
,
setItems
]
=
useState
(
initialItems
)
const
onChange
=
(
newActiveKey
)
=>
{
const
onChange
=
newActiveKey
=>
{
setActiveKey
(
newActiveKey
)
}
const
add
=
()
=>
{
...
...
@@ -81,8 +81,8 @@ const GalleryModal = (props) => {
setItems
(
newPanes
)
setActiveKey
(
newActiveKey
)
}
const
remove
=
async
(
targetKey
)
=>
{
const
tempGallery
=
picList
.
filter
(
(
item
)
=>
item
.
key
!==
targetKey
)
const
remove
=
async
targetKey
=>
{
const
tempGallery
=
picList
.
filter
(
item
=>
item
.
key
!==
targetKey
)
await
setPicList
(
tempGallery
)
console
.
log
(
tempGallery
)
form
.
setFieldsValue
({
gallery
:
tempGallery
})
...
...
@@ -94,7 +94,7 @@ const GalleryModal = (props) => {
lastIndex
=
i
-
1
}
})
let
newPanes
=
items
.
filter
(
(
item
)
=>
item
.
key
!==
targetKey
)
let
newPanes
=
items
.
filter
(
item
=>
item
.
key
!==
targetKey
)
if
(
newPanes
.
length
&&
newActiveKey
===
targetKey
)
{
if
(
lastIndex
>=
0
)
{
newActiveKey
=
newPanes
[
lastIndex
].
key
...
...
@@ -184,7 +184,7 @@ const GalleryModal = (props) => {
}
},
[
galleryInfo
])
const
onFinish
=
async
(
values
)
=>
{
const
onFinish
=
async
values
=>
{
editor
.
restoreSelection
()
// setLoading(true);
const
{
galleryTitle
,
flex
,
gallery
,
fontSize
,
theme
=
''
}
=
values
...
...
@@ -193,7 +193,7 @@ const GalleryModal = (props) => {
if
(
parseInt
(
flex
)
!==
2
)
{
// 删除空白的p标签
const
nodeEntries
=
SlateEditor
.
nodes
(
editor
,
{
match
:
(
node
)
=>
{
match
:
node
=>
{
// JS syntax
if
(
SlateElement
.
isElement
(
node
))
{
if
(
node
.
type
===
'paragraph'
)
{
...
...
@@ -310,7 +310,7 @@ const GalleryModal = (props) => {
<
Col
span=
{
12
}
>
<
Form
.
Item
label=
"字号"
name=
"fontSize"
>
<
Select
>
{
font
F
izeList
.
map
((
item
,
index
)
=>
{
{
font
S
izeList
.
map
((
item
,
index
)
=>
{
return
(
<
Select
.
Option
value=
{
item
.
value
}
key=
{
index
}
>
{
item
.
name
}
...
...
src/common/wangeditor-customer/components/link.jsx
浏览文件 @
f77be006
import
React
,
{
useState
,
useEffect
,
forwardRef
,
useImperativeHandle
}
from
'react'
;
import
{
Modal
,
Divider
,
Upload
,
Input
,
Space
,
Button
,
Form
,
Spin
,
message
,
Select
,
Cascader
,
ColorPicker
,
Row
,
Col
}
from
'antd'
;
import
{
SlateTransforms
,
SlateEditor
,
DomEditor
,
SlateElement
}
from
'@wangeditor/editor'
;
import
'./index.less'
;
import
{
addChapterTooltip
,
delChapterTooltip
}
from
'../utils/request'
;
import
{
fontFizeList
}
from
'../utils/setting'
;
import
{
findTreeElementByKey
}
from
'@/utils/common'
;
import
React
,
{
useState
,
useEffect
,
forwardRef
,
useImperativeHandle
}
from
'react'
import
{
Modal
,
Divider
,
Upload
,
Input
,
Space
,
Button
,
Form
,
Spin
,
message
,
Select
,
Cascader
,
ColorPicker
,
Row
,
Col
}
from
'antd'
import
{
SlateTransforms
,
SlateEditor
,
DomEditor
,
SlateElement
}
from
'@wangeditor/editor'
import
'./index.less'
import
{
addChapterTooltip
,
delChapterTooltip
}
from
'../utils/request'
import
{
fontSizeList
}
from
'../utils/setting'
import
{
findTreeElementByKey
}
from
'@/utils/common'
const
LinkModal
=
forwardRef
((
props
,
ref
)
=>
{
const
{
editor
,
ossClient
,
bookId
,
chapterId
,
setLinkVisible
,
setLinkInfo
,
linkInfo
,
gData
,
selectionSize
=
18
}
=
props
;
const
{
editor
,
ossClient
,
bookId
,
chapterId
,
setLinkVisible
,
setLinkInfo
,
linkInfo
,
gData
,
selectionSize
=
18
}
=
props
const
[
form
]
=
Form
.
useForm
()
;
const
linktype
=
Form
.
useWatch
(
'linktype'
,
form
)
;
const
[
loading
,
setLoading
]
=
useState
(
false
)
;
const
[
themeValue
,
setThemeValue
]
=
useState
(
'#ab1941'
)
;
const
[
form
]
=
Form
.
useForm
()
const
linktype
=
Form
.
useWatch
(
'linktype'
,
form
)
const
[
loading
,
setLoading
]
=
useState
(
false
)
const
[
themeValue
,
setThemeValue
]
=
useState
(
'#ab1941'
)
const
[
initvalues
,
setInitValue
]
=
useState
({
linktype
:
1
,
title
:
''
,
link
:
''
,
theme
:
'#ab1941'
,
fontSize
:
selectionSize
||
18
})
;
const
[
toolStatus
,
setToolStatus
]
=
useState
(
false
)
;
const
[
nowRandom
,
setNowRandom
]
=
useState
(
null
)
;
const
[
fontsize
,
setFontsize
]
=
useState
(
selectionSize
)
;
})
const
[
toolStatus
,
setToolStatus
]
=
useState
(
false
)
const
[
nowRandom
,
setNowRandom
]
=
useState
(
null
)
const
[
fontsize
,
setFontsize
]
=
useState
(
selectionSize
)
const
linkChange
=
(
value
)
=>
{
const
linkChange
=
value
=>
{
if
(
value
===
2
)
{
form
.
setFieldsValue
({
chapters
:
[]
});
form
.
setFieldsValue
({
chapters
:
[]
})
}
}
useEffect
(()
=>
{
if
(
editor
.
getSelectionText
())
{
setInitValue
({
...
initvalues
,
title
:
editor
.
getSelectionText
()
});
form
.
setFieldsValue
({
title
:
editor
.
getSelectionText
()
})
;
setInitValue
({
...
initvalues
,
title
:
editor
.
getSelectionText
()
})
form
.
setFieldsValue
({
title
:
editor
.
getSelectionText
()
})
}
},
[])
useEffect
(()
=>
{
if
(
Object
.
entries
(
linkInfo
).
length
>
0
)
{
let
chapters
=
[]
;
let
chapters
=
[]
try
{
chapters
=
linkInfo
.
chapters
.
split
(
','
).
map
(
(
item
)
=>
parseInt
(
item
));
chapters
=
linkInfo
.
chapters
.
split
(
','
).
map
(
item
=>
parseInt
(
item
))
}
catch
(
e
)
{
chapters
=
[]
;
chapters
=
[]
}
let
obj
=
{
...
linkInfo
,
linktype
:
parseInt
(
linkInfo
.
linktype
),
chapters
,
fontSize
:
parseInt
(
linkInfo
.
fontsize
)
}
;
setInitValue
(
obj
)
;
setThemeValue
(
linkInfo
.
theme
)
;
form
.
setFieldsValue
(
obj
)
;
setNowRandom
(
linkInfo
.
random
)
;
let
obj
=
{
...
linkInfo
,
linktype
:
parseInt
(
linkInfo
.
linktype
),
chapters
,
fontSize
:
parseInt
(
linkInfo
.
fontsize
)
}
setInitValue
(
obj
)
setThemeValue
(
linkInfo
.
theme
)
form
.
setFieldsValue
(
obj
)
setNowRandom
(
linkInfo
.
random
)
// setFontsize(linkInfo.fontsize);
setLinkInfo
({})
;
setLinkInfo
({})
}
},
[
linkInfo
])
;
},
[
linkInfo
])
const
textColorChange
=
(
value
,
hex
)
=>
{
setThemeValue
(
hex
)
;
form
.
setFieldsValue
({
theme
:
hex
})
;
}
;
setThemeValue
(
hex
)
form
.
setFieldsValue
({
theme
:
hex
})
}
const
onFinish
=
async
(
values
)
=>
{
editor
.
restoreSelection
()
;
const
onFinish
=
async
values
=>
{
editor
.
restoreSelection
()
let
child
=
{}
;
let
child
=
{}
if
(
values
.
linktype
===
2
)
{
const
last
=
values
.
chapters
[
values
.
chapters
.
length
-
1
]
;
child
=
findTreeElementByKey
(
gData
,
'key'
,
last
)
;
const
last
=
values
.
chapters
[
values
.
chapters
.
length
-
1
]
child
=
findTreeElementByKey
(
gData
,
'key'
,
last
)
if
(
child
.
children
&&
child
.
children
.
length
)
{
form
.
setFields
([{
chapters
:
{
errors
:
[
'请选择正确的子节!'
]
}
}])
;
return
false
;
form
.
setFields
([{
chapters
:
{
errors
:
[
'请选择正确的子节!'
]
}
}])
return
false
}
}
setLoading
(
true
)
;
const
{
linktype
,
link
,
...
other
}
=
values
;
let
newLink
=
''
;
setLoading
(
true
)
const
{
linktype
,
link
,
...
other
}
=
values
let
newLink
=
''
if
(
link
)
{
if
(
!
/^https*:
\/\/
/
.
test
(
link
))
{
newLink
=
`http://
${
link
}
`
;
newLink
=
`http://
${
link
}
`
}
else
{
newLink
=
link
;
newLink
=
link
}
}
...
...
@@ -108,40 +92,40 @@ const LinkModal = forwardRef((props, ref) => {
random
:
nowRandom
,
title
:
parseInt
(
values
.
linktype
)
===
1
?
values
.
title
:
child
.
title
,
chapters
:
values
.
chapters
instanceof
Array
?
values
.
chapters
.
join
(
','
)
:
''
,
fontsize
:
values
.
fontSize
,
}
;
fontsize
:
values
.
fontSize
}
const
nodeEntries
=
SlateEditor
.
nodes
(
editor
,
{
match
:
(
node
)
=>
{
match
:
node
=>
{
// JS syntax
if
(
SlateElement
.
isElement
(
node
))
{
if
(
node
.
type
===
'chapterLink'
)
{
return
true
;
return
true
}
}
return
false
;
return
false
},
universal
:
true
,
})
;
universal
:
true
})
// 遍历匹配的节点迭代器,获取匹配的节点路径
if
(
nodeEntries
===
null
)
{
console
.
log
(
'当前未选中的 chapterLink'
)
;
console
.
log
(
'当前未选中的 chapterLink'
)
}
else
{
for
(
let
nodeEntry
of
nodeEntries
)
{
const
[
node
,
path
]
=
nodeEntry
;
console
.
log
(
'linkedit'
,
node
)
;
const
[
node
,
path
]
=
nodeEntry
console
.
log
(
'linkedit'
,
node
)
if
(
node
.
random
===
nowRandom
)
{
SlateTransforms
.
setNodes
(
editor
,
props
,
{
at
:
path
})
;
SlateTransforms
.
setNodes
(
editor
,
props
,
{
at
:
path
})
}
}
}
message
.
success
(
'更新成功!'
)
;
setLinkVisible
(
false
)
;
setNowRandom
(
null
)
;
setLoading
(
false
)
;
return
false
;
message
.
success
(
'更新成功!'
)
setLinkVisible
(
false
)
setNowRandom
(
null
)
setLoading
(
false
)
return
false
}
let
random
=
Math
.
random
().
toString
(
10
).
substring
(
2
,
10
)
;
let
random
=
Math
.
random
().
toString
(
10
).
substring
(
2
,
10
)
editor
.
insertNode
({
type
:
'chapterLink'
,
title
:
parseInt
(
values
.
linktype
)
===
1
?
values
.
title
:
child
.
title
,
...
...
@@ -151,71 +135,61 @@ const LinkModal = forwardRef((props, ref) => {
chapters
:
values
.
chapters
instanceof
Array
?
values
.
chapters
.
join
(
','
)
:
''
,
random
,
fontsize
:
values
.
fontSize
,
children
:
[{
text
:
''
}]
,
})
;
message
.
success
(
'添加成功!'
)
;
setNowRandom
(
null
)
;
setLinkVisible
(
false
)
;
setLoading
(
false
)
;
}
;
children
:
[{
text
:
''
}]
})
message
.
success
(
'添加成功!'
)
setNowRandom
(
null
)
setLinkVisible
(
false
)
setLoading
(
false
)
}
const
deleteNowTooltip
=
async
()
=>
{
const
data
=
await
delChapterTooltip
({
book_id
:
bookId
,
chapter_id
:
chapterId
,
position
:
nowRandom
,
})
;
position
:
nowRandom
})
if
(
data
)
{
editor
.
restoreSelection
()
;
editor
.
restoreSelection
()
const
nodeEntries
=
SlateEditor
.
nodes
(
editor
,
{
match
:
(
node
)
=>
{
match
:
node
=>
{
// JS syntax
if
(
SlateElement
.
isElement
(
node
))
{
if
(
node
.
type
===
'chapterLink'
)
{
return
true
;
// 匹配 chapterToolTip
return
true
// 匹配 chapterToolTip
}
}
return
false
;
return
false
},
universal
:
true
,
})
;
universal
:
true
})
// 遍历匹配的节点迭代器,获取匹配的节点路径
let
nodePaths
=
[]
;
let
nodes
=
[]
;
let
nodePaths
=
[]
let
nodes
=
[]
if
(
nodeEntries
===
null
)
{
console
.
log
(
'当前未选中的 chapterLink'
)
;
console
.
log
(
'当前未选中的 chapterLink'
)
}
else
{
for
(
let
nodeEntry
of
nodeEntries
)
{
const
[
node
,
path
]
=
nodeEntry
;
nodePaths
.
push
(
path
)
;
nodes
.
push
(
node
)
;
const
[
node
,
path
]
=
nodeEntry
nodePaths
.
push
(
path
)
nodes
.
push
(
node
)
}
// 将属性应用到匹配的节点
for
(
const
path
of
nodePaths
)
{
SlateTransforms
.
removeNodes
(
editor
,
{
at
:
path
})
;
SlateTransforms
.
removeNodes
(
editor
,
{
at
:
path
})
}
}
setNowRandom
(
null
);
setLinkVisible
(
false
);
setNowRandom
(
null
)
setLinkVisible
(
false
)
}
}
};
return
(
<
div
>
<
Divider
/>
<
div
className=
'editor-content-form'
>
<
Form
layout=
'vertical'
name=
'validate_other'
form=
{
form
}
onFinish=
{
onFinish
}
initialValues=
{
initvalues
}
>
<
Form
.
Item
label=
'链接类型'
name=
'linktype'
rules=
{
[{
required
:
true
,
message
:
'请选择链接类型'
}]
}
>
<
div
className=
"editor-content-form"
>
<
Form
layout=
"vertical"
name=
"validate_other"
form=
{
form
}
onFinish=
{
onFinish
}
initialValues=
{
initvalues
}
>
<
Form
.
Item
label=
"链接类型"
name=
"linktype"
rules=
{
[{
required
:
true
,
message
:
'请选择链接类型'
}]
}
>
<
Select
onChange=
{
linkChange
}
>
<
Select
.
Option
value=
{
1
}
>
普通链接
</
Select
.
Option
>
<
Select
.
Option
value=
{
2
}
>
章节链接
</
Select
.
Option
>
...
...
@@ -223,40 +197,25 @@ const LinkModal = forwardRef((props, ref) => {
</
Form
.
Item
>
{
linktype
===
1
&&
(
<>
<
Form
.
Item
label=
'链接标题'
name=
'title'
rules=
{
[{
required
:
true
,
message
:
'请输入链接标题'
}]
}
extra=
'最多输入100字'
>
<
Input
maxLength=
{
100
}
placeholder=
''
allowClear
/>
<
Form
.
Item
label=
"链接标题"
name=
"title"
rules=
{
[{
required
:
true
,
message
:
'请输入链接标题'
}]
}
extra=
"最多输入100字"
>
<
Input
maxLength=
{
100
}
placeholder=
""
allowClear
/>
</
Form
.
Item
>
<
Form
.
Item
label=
'链接'
name=
'link'
label=
"链接"
name=
"link"
rules=
{
[
{
required
:
true
,
message
:
'请输入链接地址'
},
{
pattern
:
/^https*:
\/\/
/gi
,
message
:
'链接地址需要http(s)://开头'
},
]
}
>
<
Input
placeholder=
''
allowClear
/>
{
pattern
:
/^https*:
\/\/
/gi
,
message
:
'链接地址需要http(s)://开头'
}
]
}
>
<
Input
placeholder=
""
allowClear
/>
</
Form
.
Item
>
</>
)
}
{
linktype
===
2
&&
(
<>
<
Form
.
Item
label=
'子节'
name=
'chapters'
rules=
{
[{
required
:
true
,
message
:
'请选择子节'
}]
}
>
<
Cascader
options=
{
gData
}
fieldNames=
{
{
label
:
'title'
,
value
:
'key'
,
children
:
'children'
}
}
placeholder=
'请选择子节'
allowClear
/>
<
Form
.
Item
label=
"子节"
name=
"chapters"
rules=
{
[{
required
:
true
,
message
:
'请选择子节'
}]
}
>
<
Cascader
options=
{
gData
}
fieldNames=
{
{
label
:
'title'
,
value
:
'key'
,
children
:
'children'
}
}
placeholder=
"请选择子节"
allowClear
/>
</
Form
.
Item
>
</>
)
}
...
...
@@ -264,52 +223,45 @@ const LinkModal = forwardRef((props, ref) => {
<
Form
.
Item
>
<
Row
gutter=
{
20
}
>
<
Col
span=
{
12
}
>
<
Form
.
Item
label=
'字号'
name=
'fontSize'
>
<
Form
.
Item
label=
"字号"
name=
"fontSize"
>
<
Select
>
{
font
F
izeList
.
map
((
item
,
index
)
=>
{
{
font
S
izeList
.
map
((
item
,
index
)
=>
{
return
(
<
Select
.
Option
value=
{
item
.
value
}
key=
{
index
}
>
{
item
.
name
}
</
Select
.
Option
>
)
;
)
})
}
</
Select
>
</
Form
.
Item
>
</
Col
>
<
Col
span=
{
12
}
>
<
div
className=
'justcontent-color-inline'
>
<
div
className=
"justcontent-color-inline"
>
<
Form
.
Item
label=
'扩展主题色'
name=
'theme'
className=
'flex-max'
label=
"扩展主题色"
name=
"theme"
className=
"flex-max"
rules=
{
[
{
required
:
true
,
message
:
'请选择颜色'
},
{
pattern
:
/^#
[
0-9A-Fa-f
]
{6}$/i
,
message
:
'请输入正确的16进制色值'
},
]
}
>
<
Input
placeholder=
''
allowClear
/>
{
pattern
:
/^#
[
0-9A-Fa-f
]
{6}$/i
,
message
:
'请输入正确的16进制色值'
}
]
}
>
<
Input
placeholder=
""
allowClear
/>
</
Form
.
Item
>
<
Form
.
Item
label=
{
` `
}
>
<
ColorPicker
disabledAlpha
value=
{
themeValue
}
defaultValue=
{
themeValue
}
format=
'hex'
onChange=
{
textColorChange
}
/>
<
ColorPicker
disabledAlpha
value=
{
themeValue
}
defaultValue=
{
themeValue
}
format=
"hex"
onChange=
{
textColorChange
}
/>
</
Form
.
Item
>
</
div
>
</
Col
>
</
Row
>
</
Form
.
Item
>
<
Form
.
Item
className=
'editor-form-buttons'
>
<
Form
.
Item
className=
"editor-form-buttons"
>
<
Space
>
{
toolStatus
&&
(
<
Button
type=
'default'
onClick=
{
deleteNowTooltip
}
>
<
Button
type=
"default"
onClick=
{
deleteNowTooltip
}
>
删除
</
Button
>
)
}
<
Button
type=
'primary'
loading=
{
loading
}
htmlType=
'submit'
>
<
Button
type=
"primary"
loading=
{
loading
}
htmlType=
"submit"
>
{
nowRandom
?
'更新'
:
'插入'
}
</
Button
>
</
Space
>
...
...
@@ -317,7 +269,7 @@ const LinkModal = forwardRef((props, ref) => {
</
Form
>
</
div
>
</
div
>
)
;
})
;
)
})
export
default
LinkModal
;
export
default
LinkModal
src/common/wangeditor-customer/components/tooltip.jsx
浏览文件 @
f77be006
import
React
,
{
useState
,
useEffect
,
forwardRef
,
useImperativeHandle
}
from
'react'
;
import
{
Modal
,
Divider
,
Upload
,
Input
,
Space
,
Button
,
Form
,
Spin
,
message
,
Select
,
ColorPicker
,
Row
,
Col
,
}
from
'antd'
;
import
{
SlateTransforms
,
SlateEditor
,
DomEditor
,
SlateElement
}
from
'@wangeditor/editor'
;
import
'./index.less'
;
import
{
addChapterTooltip
,
delChapterTooltip
}
from
'../utils/request'
;
import
{
findNodeWithParent
,
fontFizeList
}
from
'../utils/setting'
;
import
React
,
{
useState
,
useEffect
,
forwardRef
,
useImperativeHandle
}
from
'react'
import
{
Modal
,
Divider
,
Upload
,
Input
,
Space
,
Button
,
Form
,
Spin
,
message
,
Select
,
ColorPicker
,
Row
,
Col
}
from
'antd'
import
{
SlateTransforms
,
SlateEditor
,
DomEditor
,
SlateElement
}
from
'@wangeditor/editor'
import
'./index.less'
import
{
addChapterTooltip
,
delChapterTooltip
}
from
'../utils/request'
import
{
findNodeWithParent
,
fontSizeList
}
from
'../utils/setting'
const
TooltipModal
=
forwardRef
((
props
,
ref
)
=>
{
const
{
editor
,
ossClient
,
bookId
,
chapterId
,
setTooltipVisible
,
setTooltipInfo
,
tooltipInfo
,
selectionSize
=
18
}
=
props
;
const
{
editor
,
ossClient
,
bookId
,
chapterId
,
setTooltipVisible
,
setTooltipInfo
,
tooltipInfo
,
selectionSize
=
18
}
=
props
const
[
form
]
=
Form
.
useForm
()
;
const
tooltipTypeValue
=
Form
.
useWatch
(
'tooltipType'
,
form
)
;
const
[
loading
,
setLoading
]
=
useState
(
false
)
;
const
[
themeValue
,
setThemeValue
]
=
useState
(
'#ab1941'
)
;
const
[
form
]
=
Form
.
useForm
()
const
tooltipTypeValue
=
Form
.
useWatch
(
'tooltipType'
,
form
)
const
[
loading
,
setLoading
]
=
useState
(
false
)
const
[
themeValue
,
setThemeValue
]
=
useState
(
'#ab1941'
)
const
[
initvalues
,
setInitValue
]
=
useState
({
tooltipType
:
1
,
title
:
''
,
...
...
@@ -34,38 +19,38 @@ const TooltipModal = forwardRef((props, ref) => {
content
:
''
,
theme
:
'#ab1941'
,
fontSize
:
selectionSize
||
18
})
;
const
[
toolStatus
,
setToolStatus
]
=
useState
(
false
)
;
const
[
nowRandom
,
setNowRandom
]
=
useState
(
null
)
;
const
[
fontsize
,
setFontsize
]
=
useState
(
selectionSize
)
;
})
const
[
toolStatus
,
setToolStatus
]
=
useState
(
false
)
const
[
nowRandom
,
setNowRandom
]
=
useState
(
null
)
const
[
fontsize
,
setFontsize
]
=
useState
(
selectionSize
)
useEffect
(()
=>
{
if
(
Object
.
entries
(
tooltipInfo
).
length
>
0
)
{
let
obj
=
{
...
tooltipInfo
,
tooltipType
:
parseInt
(
tooltipInfo
.
tooltipType
),
fontSize
:
parseInt
(
tooltipInfo
.
fontsize
)
}
;
setInitValue
(
obj
)
;
form
.
setFieldsValue
(
obj
)
;
setThemeValue
(
tooltipInfo
.
theme
)
;
setNowRandom
(
tooltipInfo
.
random
)
;
setFontsize
(
parseInt
(
tooltipInfo
.
fontsize
))
;
setTooltipInfo
({})
;
let
obj
=
{
...
tooltipInfo
,
tooltipType
:
parseInt
(
tooltipInfo
.
tooltipType
),
fontSize
:
parseInt
(
tooltipInfo
.
fontsize
)
}
setInitValue
(
obj
)
form
.
setFieldsValue
(
obj
)
setThemeValue
(
tooltipInfo
.
theme
)
setNowRandom
(
tooltipInfo
.
random
)
setFontsize
(
parseInt
(
tooltipInfo
.
fontsize
))
setTooltipInfo
({})
}
},
[
tooltipInfo
])
;
},
[
tooltipInfo
])
const
textColorChange
=
(
value
,
hex
)
=>
{
setThemeValue
(
hex
)
;
form
.
setFieldsValue
({
theme
:
hex
})
;
}
;
setThemeValue
(
hex
)
form
.
setFieldsValue
({
theme
:
hex
})
}
const
onFinish
=
async
(
values
)
=>
{
editor
.
restoreSelection
()
;
setLoading
(
true
)
;
const
{
tooltipType
,
link
,
theme
,
fontSize
,
...
other
}
=
values
;
let
newLink
=
''
;
const
onFinish
=
async
values
=>
{
editor
.
restoreSelection
()
setLoading
(
true
)
const
{
tooltipType
,
link
,
theme
,
fontSize
,
...
other
}
=
values
let
newLink
=
''
if
(
link
)
{
if
(
!
/^https*:
\/\/
/
.
test
(
link
))
{
newLink
=
`http://
${
link
}
`
;
newLink
=
`http://
${
link
}
`
}
else
{
newLink
=
link
;
newLink
=
link
}
}
...
...
@@ -76,33 +61,33 @@ const TooltipModal = forwardRef((props, ref) => {
position
:
nowRandom
,
link
:
newLink
,
...
other
,
type
:
tooltipType
,
})
;
type
:
tooltipType
})
if
(
data
)
{
const
props
=
{
...
values
,
link
:
newLink
,
random
:
nowRandom
,
theme
,
fontsize
:
values
.
fontSize
||
selectionSize
||
fontsize
}
;
const
props
=
{
...
values
,
link
:
newLink
,
random
:
nowRandom
,
theme
,
fontsize
:
values
.
fontSize
||
selectionSize
||
fontsize
}
const
aPath
=
findNodeWithParent
(
editor
.
children
,
'chapterTooltip'
,
'random'
,
nowRandom
)
;
SlateTransforms
.
setNodes
(
editor
,
props
,
{
at
:
aPath
.
reverse
()
})
;
message
.
success
(
'更新成功!'
)
;
setTooltipVisible
(
false
)
;
setNowRandom
(
null
)
;
const
aPath
=
findNodeWithParent
(
editor
.
children
,
'chapterTooltip'
,
'random'
,
nowRandom
)
SlateTransforms
.
setNodes
(
editor
,
props
,
{
at
:
aPath
.
reverse
()
})
message
.
success
(
'更新成功!'
)
setTooltipVisible
(
false
)
setNowRandom
(
null
)
}
setLoading
(
false
)
;
return
;
setLoading
(
false
)
return
}
const
text
=
editor
.
getSelectionText
()
;
let
random
=
Math
.
random
().
toString
(
10
).
substring
(
2
,
10
)
;
const
text
=
editor
.
getSelectionText
()
let
random
=
Math
.
random
().
toString
(
10
).
substring
(
2
,
10
)
const
data
=
await
addChapterTooltip
({
book_id
:
bookId
,
chapter_id
:
chapterId
,
position
:
random
,
link
:
newLink
,
...
other
,
type
:
tooltipType
,
})
;
type
:
tooltipType
})
if
(
data
)
{
editor
.
insertNode
({
...
...
@@ -114,72 +99,62 @@ const TooltipModal = forwardRef((props, ref) => {
random
,
theme
,
fontsize
:
values
.
fontSize
||
selectionSize
||
fontsize
,
children
:
[{
text
:
''
}],
});
message
.
success
(
'添加成功!'
);
children
:
[{
text
:
''
}]
})
message
.
success
(
'添加成功!'
)
}
setNowRandom
(
null
)
setTooltipVisible
(
false
)
setLoading
(
false
)
}
setNowRandom
(
null
);
setTooltipVisible
(
false
);
setLoading
(
false
);
};
const
deleteNowTooltip
=
async
()
=>
{
const
data
=
await
delChapterTooltip
({
book_id
:
bookId
,
chapter_id
:
chapterId
,
position
:
nowRandom
,
})
;
position
:
nowRandom
})
if
(
data
)
{
editor
.
restoreSelection
()
;
editor
.
restoreSelection
()
const
nodeEntries
=
SlateEditor
.
nodes
(
editor
,
{
match
:
(
node
)
=>
{
match
:
node
=>
{
// JS syntax
if
(
SlateElement
.
isElement
(
node
))
{
if
(
node
.
type
===
'chapterTooltip'
)
{
return
true
;
// 匹配 chapterToolTip
return
true
// 匹配 chapterToolTip
}
}
return
false
;
return
false
},
universal
:
true
,
})
;
universal
:
true
})
// 遍历匹配的节点迭代器,获取匹配的节点路径
let
nodePaths
=
[]
;
let
nodes
=
[]
;
let
nodePaths
=
[]
let
nodes
=
[]
if
(
nodeEntries
===
null
)
{
console
.
log
(
'当前未选中的 chapterTooltip'
)
;
console
.
log
(
'当前未选中的 chapterTooltip'
)
}
else
{
for
(
let
nodeEntry
of
nodeEntries
)
{
const
[
node
,
path
]
=
nodeEntry
;
nodePaths
.
push
(
path
)
;
nodes
.
push
(
node
)
;
const
[
node
,
path
]
=
nodeEntry
nodePaths
.
push
(
path
)
nodes
.
push
(
node
)
}
// 将属性应用到匹配的节点
for
(
const
path
of
nodePaths
)
{
SlateTransforms
.
removeNodes
(
editor
,
{
at
:
path
});
SlateTransforms
.
removeNodes
(
editor
,
{
at
:
path
})
}
}
setNowRandom
(
null
)
setTooltipVisible
(
false
)
}
setNowRandom
(
null
);
setTooltipVisible
(
false
);
}
};
return
(
<
div
>
<
Divider
/>
<
div
className=
'editor-content-form'
>
<
Form
layout=
'vertical'
name=
'validate_other'
form=
{
form
}
onFinish=
{
onFinish
}
initialValues=
{
initvalues
}
>
<
Form
.
Item
label=
'气泡类型'
name=
'tooltipType'
rules=
{
[{
required
:
true
,
message
:
'请选择气泡类型'
}]
}
>
<
div
className=
"editor-content-form"
>
<
Form
layout=
"vertical"
name=
"validate_other"
form=
{
form
}
onFinish=
{
onFinish
}
initialValues=
{
initvalues
}
>
<
Form
.
Item
label=
"气泡类型"
name=
"tooltipType"
rules=
{
[{
required
:
true
,
message
:
'请选择气泡类型'
}]
}
>
<
Select
disabled=
{
nowRandom
?
true
:
false
}
>
<
Select
.
Option
value=
{
1
}
>
文字气泡
</
Select
.
Option
>
<
Select
.
Option
value=
{
2
}
>
图标气泡
</
Select
.
Option
>
...
...
@@ -187,82 +162,67 @@ const TooltipModal = forwardRef((props, ref) => {
</
Form
.
Item
>
{
tooltipTypeValue
===
1
&&
(
<
Form
.
Item
label=
'气泡标题'
name=
'title'
rules=
{
[
{
required
:
tooltipTypeValue
===
1
?
true
:
false
,
message
:
'请输入气泡标题'
},
]
}
extra=
'最多输入100字'
>
<
Input
maxLength=
{
100
}
placeholder=
''
allowClear
/>
label=
"气泡标题"
name=
"title"
rules=
{
[{
required
:
tooltipTypeValue
===
1
?
true
:
false
,
message
:
'请输入气泡标题'
}]
}
extra=
"最多输入100字"
>
<
Input
maxLength=
{
100
}
placeholder=
""
allowClear
/>
</
Form
.
Item
>
)
}
<
Form
.
Item
label=
'描述'
name=
'content'
rules=
{
[{
required
:
true
,
message
:
'请输入描述内容'
}]
}
>
<
Input
.
TextArea
autoSize=
{
{
minRows
:
2
,
maxRows
:
4
}
}
placeholder=
''
allowClear
/>
<
Form
.
Item
label=
"描述"
name=
"content"
rules=
{
[{
required
:
true
,
message
:
'请输入描述内容'
}]
}
>
<
Input
.
TextArea
autoSize=
{
{
minRows
:
2
,
maxRows
:
4
}
}
placeholder=
""
allowClear
/>
</
Form
.
Item
>
<
Form
.
Item
>
<
Row
gutter=
{
20
}
>
<
Col
span=
{
12
}
>
<
Form
.
Item
label=
'字号'
name=
'fontSize'
>
<
Form
.
Item
label=
"字号"
name=
"fontSize"
>
<
Select
>
{
font
F
izeList
.
map
((
item
,
index
)
=>
{
{
font
S
izeList
.
map
((
item
,
index
)
=>
{
return
(
<
Select
.
Option
value=
{
item
.
value
}
key=
{
index
}
>
{
item
.
name
}
</
Select
.
Option
>
)
;
)
})
}
</
Select
>
</
Form
.
Item
>
</
Col
>
<
Col
span=
{
12
}
>
<
div
className=
'justcontent-color-inline'
>
<
div
className=
"justcontent-color-inline"
>
<
Form
.
Item
label=
'气泡主题色'
name=
'theme'
className=
'flex-max'
label=
"气泡主题色"
name=
"theme"
className=
"flex-max"
rules=
{
[
{
required
:
true
,
message
:
'请选择颜色'
},
{
pattern
:
/^#
[
0-9A-Fa-f
]
{6}$/i
,
message
:
'请输入正确的16进制色值'
},
]
}
>
<
Input
placeholder=
''
allowClear
/>
{
pattern
:
/^#
[
0-9A-Fa-f
]
{6}$/i
,
message
:
'请输入正确的16进制色值'
}
]
}
>
<
Input
placeholder=
""
allowClear
/>
</
Form
.
Item
>
<
Form
.
Item
label=
{
` `
}
>
<
ColorPicker
disabledAlpha
value=
{
themeValue
}
defaultValue=
{
themeValue
}
format=
'hex'
onChange=
{
textColorChange
}
/>
<
ColorPicker
disabledAlpha
value=
{
themeValue
}
defaultValue=
{
themeValue
}
format=
"hex"
onChange=
{
textColorChange
}
/>
</
Form
.
Item
>
</
div
>
</
Col
>
</
Row
>
</
Form
.
Item
>
<
Form
.
Item
label=
'链接(非必须)'
name=
'link'
label=
"链接(非必须)"
name=
"link"
rules=
{
[
{
required
:
false
,
message
:
'请输入气泡内容'
},
{
pattern
:
/^https*:
\/\/
/gi
,
message
:
'链接地址需要http(s)://开头'
},
]
}
>
<
Input
placeholder=
''
allowClear
/>
{
pattern
:
/^https*:
\/\/
/gi
,
message
:
'链接地址需要http(s)://开头'
}
]
}
>
<
Input
placeholder=
""
allowClear
/>
</
Form
.
Item
>
<
Form
.
Item
className=
'editor-form-buttons'
>
<
Form
.
Item
className=
"editor-form-buttons"
>
<
Space
>
{
toolStatus
&&
(
<
Button
type=
'default'
onClick=
{
deleteNowTooltip
}
>
<
Button
type=
"default"
onClick=
{
deleteNowTooltip
}
>
删除
</
Button
>
)
}
<
Button
type=
'primary'
loading=
{
loading
}
htmlType=
'submit'
>
<
Button
type=
"primary"
loading=
{
loading
}
htmlType=
"submit"
>
{
nowRandom
?
'更新'
:
'插入'
}
</
Button
>
</
Space
>
...
...
@@ -270,7 +230,7 @@ const TooltipModal = forwardRef((props, ref) => {
</
Form
>
</
div
>
</
div
>
)
;
})
;
)
})
export
default
TooltipModal
;
export
default
TooltipModal
src/common/wangeditor-customer/customer/ChapterItem.js
浏览文件 @
f77be006
import
{
DomEditor
,
SlateTransforms
,
SlateRange
}
from
'@wangeditor/editor'
;
import
{
DomEditor
,
SlateTransforms
,
SlateRange
}
from
'@wangeditor/editor'
class
ChapterItem
{
constructor
()
{
this
.
title
=
'
章节
'
this
.
title
=
'
节头
'
this
.
iconSvg
=
`<svg width="22px" height="23px" viewBox="0 0 22 23" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>图标</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
...
...
@@ -14,14 +14,14 @@ class ChapterItem {
</g>
</g>
</g>
</svg>`
;
</svg>`
this
.
tag
=
'button'
}
getValue
(
editor
)
{
return
'hello, 图片, , , 图片'
}
isActive
(
editor
)
{
return
false
;
return
false
}
isDisabled
(
editor
)
{
const
{
selection
}
=
editor
...
...
@@ -35,7 +35,7 @@ class ChapterItem {
// eslint-disable-next-line array-callback-return
const
hasPreElem
=
selectedElems
.
some
(
elem
=>
{
const
type
=
DomEditor
.
getNodeType
(
elem
)
;
const
type
=
DomEditor
.
getNodeType
(
elem
)
// 代码块 引用 表格 禁用
if
(
type
===
'pre'
||
type
===
'blockquote'
||
type
===
'table'
||
type
===
'table-row'
||
type
===
'table-cell'
)
return
true
})
...
...
@@ -45,10 +45,10 @@ class ChapterItem {
}
exec
(
editor
,
value
)
{
// editor.insertText(value) // value 即 this.getValue(editor) 的返回值
if
(
this
.
isDisabled
(
editor
))
{
return
;
if
(
this
.
isDisabled
(
editor
))
{
return
}
editor
.
emit
(
'ChapterItemMenuClick'
)
;
editor
.
emit
(
'ChapterItemMenuClick'
)
}
}
...
...
@@ -56,9 +56,7 @@ export default {
key
:
'ChapterItem'
,
// 定义 menu key :要保证唯一、不重复(重要)
factory
()
{
return
new
ChapterItem
()
// 把 `YourMenuClass` 替换为你菜单的 class
}
,
}
}
export
{
ChapterItem
}
\ No newline at end of file
export
{
ChapterItem
}
src/common/wangeditor-customer/index-padd.jsx
deleted
100644 → 0
浏览文件 @
1cc62424
import
React
,
{
useState
,
useEffect
,
useRef
,
forwardRef
,
useImperativeHandle
}
from
'react'
;
import
{
Input
,
Spin
,
message
,
Button
}
from
'antd'
;
import
{
EyeOutlined
}
from
'@ant-design/icons'
;
import
AliOSS
from
'ali-oss'
;
import
dayjs
from
'dayjs'
;
import
{
Boot
}
from
'@wangeditor/editor'
;
import
{
useSelector
}
from
'react-redux'
;
import
{
SlateEditor
,
SlateNode
,
SlateElement
,
SlateText
}
from
'@wangeditor/editor'
;
import
{
Editor
,
Toolbar
}
from
'@wangeditor/editor-for-react'
;
import
'@wangeditor/editor/dist/css/style.css'
;
// 引入 css
import
'./node/index.less'
;
import
timesave
from
'@/assets/images/timesave.png'
;
import
relativeTime
from
'dayjs/plugin/relativeTime'
;
dayjs
.
extend
(
relativeTime
);
import
{
storageChange
}
from
'@/utils/storage.js'
;
import
linkCardModule
from
'@wangeditor/plugin-link-card'
;
import
PaddingSpace
from
'./customer/padding'
;
import
ImageAutoConf
from
'./customer/Image'
;
import
GalleryAutoConf
from
'./customer/Gallery'
;
import
VideoAutoConf
from
'./customer/Video'
;
import
AudioAutoConf
from
'./customer/Audio'
;
import
ChapterTitleAutoConf
from
'./customer/ChapterTitle'
;
import
ChapterItemAutoConf
from
'./customer/ChapterItem'
;
import
PracticeAutoConf
from
'./customer/Practice'
;
import
FormulaAutoConf
from
'./customer/Formula'
;
import
TooltipAutoConf
from
'./customer/Tooltip'
;
import
ImageModal
from
'./components/image'
;
import
VideoModal
from
'./components/video'
;
import
GalleryModal
from
'./components/gallery'
;
import
AudioModal
from
'./components/audio'
;
import
ChapterTitleModal
from
'./components/chapter-title'
;
import
ChapterItemModal
from
'./components/chapter-item'
;
import
PracticeModal
from
'./components/practice'
;
import
FormulaModal
from
'./components/formula'
;
import
TooltipModal
from
'./components/tooltip'
;
import
chapterSectionModule
from
'./node/chapterItem'
;
import
chapterHeaderModule
from
'./node/chapterTitle'
;
import
chapterImageModule
from
'./node/image'
;
import
chapterVideoModule
from
'./node/video'
;
import
chapterAudioModule
from
'./node/audio'
;
import
chapterGalleryModule
from
'./node/gallery'
;
import
chapterPracticeModule
from
'./node/practice'
;
import
formulaModule
from
'./node/formula'
;
import
tooltipModule
from
'./node/tooltip'
;
import
PracticeSettingModal
from
'./practice/index'
;
import
$
from
'jquery'
;
import
{
getInfoByChapterId
}
from
'@/pages/books/section/request'
;
import
{
getAliOSSSTSToken
}
from
'./utils/request'
;
import
'./index.less'
;
const
menuArr1
=
[
'字体样式'
,
'字号'
];
const
menuArr2
=
[
'图片'
,
'画廊'
,
'视频'
,
'音频'
];
const
menuArr3
=
[
'代码块'
,
'链接'
,
'引用'
,
'公式'
,
'章头'
,
'章节'
,
'交互练习'
,
'气泡'
];
const
colorList
=
[
'#ab1941'
,
'#2970f6'
,
'#2ad882'
,
'#eb3351'
];
const
bookBucketName
=
'zxts-book-file'
;
const
module
=
{
menus
:
[
ImageAutoConf
,
GalleryAutoConf
,
VideoAutoConf
,
AudioAutoConf
,
FormulaAutoConf
,
TooltipAutoConf
,
ChapterTitleAutoConf
,
ChapterItemAutoConf
,
PracticeAutoConf
,
],
};
Boot
.
registerModule
(
module
);
// 注册节头内容
Boot
.
registerModule
(
chapterSectionModule
);
// 注册章头内容
Boot
.
registerModule
(
chapterHeaderModule
);
// 注册图片内容
Boot
.
registerModule
(
chapterImageModule
);
// 注册视频内容
Boot
.
registerModule
(
chapterVideoModule
);
// 注册音频内容
Boot
.
registerModule
(
chapterAudioModule
);
// 注册画廊
Boot
.
registerModule
(
chapterGalleryModule
);
// 注册公式
Boot
.
registerModule
(
formulaModule
);
// 注册练习
Boot
.
registerModule
(
chapterPracticeModule
);
// 注册气泡
Boot
.
registerModule
(
tooltipModule
);
Boot
.
registerModule
(
linkCardModule
);
const
tabsMenu
=
[
{
key
:
'text'
,
title
:
'文本设置'
},
{
key
:
'style'
,
title
:
'样式模版'
},
];
storageChange
();
const
WangEditorCustomer
=
forwardRef
((
props
,
ref
)
=>
{
const
{
chapterId
,
bookId
,
contentId
,
html
,
setHtml
,
saveContent
}
=
props
;
// 自动保存时间
const
{
autosaveTime
}
=
useSelector
((
state
)
=>
state
.
editor
);
const
toolbarRef
=
useRef
();
const
paddingSpaceRef
=
useRef
();
const
[
cId
,
setCId
]
=
useState
(
contentId
);
const
[
ossClient
,
setOssClient
]
=
useState
(
null
);
// oss 操作
const
[
STSToken
,
setSTSToken
]
=
useState
(
null
);
// oss 过期设置
const
[
tabKey
,
setTabKey
]
=
useState
(
'text'
);
const
[
loading
,
setLoading
]
=
useState
(
true
);
// editor 实例
const
[
editor
,
setEditor
]
=
useState
(
null
);
const
[
editorNodes
,
setEditorNodes
]
=
useState
(
null
);
const
[
content
,
setContent
]
=
useState
(
html
);
const
saveRef
=
useRef
();
// 工具栏配置
const
toolbarConfig
=
{
toolbarKeys
:
[
// '|',
// 'redo',
// 'undo',
// 'emotion',
// 'todo',
// 'fullScreen',
// '|',
// 'headerSelect',
// 'lineHeight',
// '|',
'fontFamily'
,
// 'uploadImage',
'fontSize'
,
'|'
,
'justifyLeft'
,
'justifyRight'
,
'justifyCenter'
,
'justifyJustify'
,
'divider'
,
'|'
,
'lineHeight'
,
'|'
,
'bold'
,
'italic'
,
'through'
,
'underline'
,
'sub'
,
'sup'
,
'color'
,
'bgColor'
,
'bulletedList'
,
'numberedList'
,
'indent'
,
'delIndent'
,
'|'
,
// 'insertImage',
// 'uploadImage',
// 'insertVideo',
// 'uploadVideo',
'insertTable'
,
'|'
,
'codeBlock'
,
// 代码块
'insertLink'
,
// 链接
// 'insertFormula', // 公式
'blockquote'
,
// 引用
// 'code',
// 'clearStyle',
],
};
useImperativeHandle
(
ref
,
()
=>
{
return
{
editor
,
};
});
const
imageRef
=
useRef
();
const
galleryRef
=
useRef
();
const
videoRef
=
useRef
();
const
audioRef
=
useRef
();
const
formulaRef
=
useRef
();
const
tooltipRef
=
useRef
();
const
chapterTitleRef
=
useRef
();
const
chapterItemRef
=
useRef
();
const
practiceRef
=
useRef
();
const
practiceSettingRef
=
useRef
();
toolbarConfig
.
insertKeys
=
{
index
:
30
,
keys
:
[
'ImageAuto'
,
'GalleryAuto'
,
'VideoAuto'
,
'AudioAuto'
,
'FormulaAuto'
,
'ChapterTitle'
,
'ChapterItem'
,
'Practice'
,
'TooltipAuto'
,
],
};
// 编辑器配置
const
editorConfig
=
{
placeholder
:
'请输入内容...'
,
// 选中公式时的悬浮菜单
hoverbarKeys
:
{
// formula: {
// menuKeys: ['editFormula'], // “编辑公式”菜单
// },
// 在编辑器中,选中链接文本时,要弹出的菜单
link
:
{
menuKeys
:
[
'editLink'
,
'unLink'
,
'viewLink'
,
// 默认的配置可以通过 `editor.getConfig().hoverbarKeys.link` 获取
'convertToLinkCard'
,
// 增加 '转为链接卡片'菜单
],
},
MENU_CONF
:
{
// '转为链接卡片'菜单的配置
convertToLinkCard
:
{
// 自定义获取 link-card 信息,可选
// 返回 { title, iconImgSrc }
async
getLinkCardInfo
(
linkText
,
linkUrl
)
{
// 1. 可通过 iframe 加载网页,然后获取网页 title 和其中的图片
// 2. 服务端获取(有些网页会设置 `X-Frame-Options` ,无法通过 iframe 加载)
// // 模拟异步返回
// return new Promise(resolve => {
// setTimeout(() => {
// const info = { title: linkText, iconImgSrc: '' }
// resolve(info)
// }, 100)
// })
},
},
// 其他...
},
},
};
// 编辑器按钮重排
const
toolSetttingReplace
=
()
=>
{
setTimeout
(()
=>
{
const
toolbarElement
=
toolbarRef
.
current
&&
toolbarRef
.
current
.
children
[
1
].
children
[
0
];
const
allChildren
=
toolbarElement
.
children
;
const
oH6_1
=
document
.
createElement
(
'h4'
);
oH6_1
.
setAttribute
(
'class'
,
'w-auto type-heading'
);
oH6_1
.
innerHTML
=
'常用格式'
;
toolbarElement
.
insertBefore
(
oH6_1
,
allChildren
[
0
]);
const
oHDiv_3
=
document
.
createElement
(
'div'
);
oHDiv_3
.
setAttribute
(
'class'
,
'custom-bar-box two'
);
toolbarElement
.
insertBefore
(
oHDiv_3
,
allChildren
[
1
]);
$
(
allChildren
[
1
]).
append
(
$
(
allChildren
[
2
]).
detach
());
$
(
allChildren
[
1
]).
append
(
$
(
allChildren
[
2
]).
detach
());
const
oH6_2
=
document
.
createElement
(
'h4'
);
oH6_2
.
setAttribute
(
'class'
,
'w-auto type-heading'
);
oH6_2
.
innerHTML
=
'媒体资源'
;
toolbarElement
.
insertBefore
(
oH6_2
,
allChildren
[
22
]);
const
oHDiv_1
=
document
.
createElement
(
'div'
);
oHDiv_1
.
setAttribute
(
'class'
,
'custom-bar-box'
);
toolbarElement
.
insertBefore
(
oHDiv_1
,
allChildren
[
23
]);
const
itemBox
=
'<div class="w-e-bar-boxitem"></div>'
;
$
(
itemBox
).
insertBefore
(
$
(
allChildren
[
4
]));
const
itemBox2
=
'<div class="w-e-bar-boxitem you"></div>'
;
$
(
itemBox2
).
insertBefore
(
$
(
allChildren
[
10
]));
$
(
allChildren
[
4
]).
append
(
$
(
allChildren
[
3
]).
detach
());
$
(
allChildren
[
3
]).
append
(
$
(
allChildren
[
5
]).
detach
());
$
(
allChildren
[
3
]).
append
(
$
(
allChildren
[
5
]).
detach
());
$
(
allChildren
[
3
]).
append
(
$
(
allChildren
[
5
]).
detach
());
$
(
allChildren
[
3
]).
append
(
$
(
allChildren
[
4
]).
detach
());
const
oHDivInput_1
=
document
.
createElement
(
'div'
);
oHDivInput_1
.
setAttribute
(
'class'
,
'custom-bar-box input'
);
oHDivInput_1
.
setAttribute
(
'id'
,
'custom-bar-box-input'
);
toolbarElement
.
insertBefore
(
oHDivInput_1
,
allChildren
[
4
]);
$
(
allChildren
[
6
]).
append
(
$
(
allChildren
[
7
]).
detach
());
$
(
allChildren
[
6
]).
append
(
$
(
allChildren
[
7
]).
detach
());
$
(
allChildren
[
6
]).
append
(
$
(
allChildren
[
7
]).
detach
());
$
(
allChildren
[
6
]).
append
(
$
(
allChildren
[
7
]).
detach
());
$
(
allChildren
[
6
]).
append
(
$
(
allChildren
[
7
]).
detach
());
$
(
allChildren
[
6
]).
append
(
$
(
allChildren
[
7
]).
detach
());
$
(
allChildren
[
6
]).
append
(
$
(
allChildren
[
7
]).
detach
());
$
(
allChildren
[
6
]).
append
(
$
(
allChildren
[
7
]).
detach
());
$
(
allChildren
[
6
]).
append
(
$
(
allChildren
[
7
]).
detach
());
$
(
allChildren
[
6
]).
append
(
$
(
allChildren
[
7
]).
detach
());
$
(
allChildren
[
6
]).
append
(
$
(
allChildren
[
7
]).
detach
());
$
(
allChildren
[
6
]).
append
(
$
(
allChildren
[
7
]).
detach
());
$
(
allChildren
[
9
]).
append
(
$
(
allChildren
[
15
]).
detach
());
$
(
allChildren
[
9
]).
append
(
$
(
allChildren
[
15
]).
detach
());
$
(
allChildren
[
9
]).
append
(
$
(
allChildren
[
15
]).
detach
());
$
(
allChildren
[
9
]).
append
(
$
(
allChildren
[
15
]).
detach
());
$
(
allChildren
[
9
]).
append
(
$
(
allChildren
[
10
]).
detach
());
const
oH6_3
=
document
.
createElement
(
'h4'
);
oH6_3
.
setAttribute
(
'class'
,
'w-auto type-heading'
);
oH6_3
.
innerHTML
=
'高级模块'
;
toolbarElement
.
insertBefore
(
oH6_3
,
allChildren
[
11
]);
const
oHDiv_2
=
document
.
createElement
(
'div'
);
oHDiv_2
.
setAttribute
(
'class'
,
'custom-bar-box'
);
toolbarElement
.
insertBefore
(
oHDiv_2
,
allChildren
[
12
]);
$
(
allChildren
[
12
]).
append
(
$
(
allChildren
[
13
]).
detach
());
$
(
allChildren
[
12
]).
append
(
$
(
allChildren
[
13
]).
detach
());
$
(
allChildren
[
12
]).
append
(
$
(
allChildren
[
13
]).
detach
());
$
(
allChildren
[
12
]).
append
(
$
(
allChildren
[
13
]).
detach
());
$
(
allChildren
[
12
]).
append
(
$
(
allChildren
[
13
]).
detach
());
$
(
allChildren
[
12
]).
append
(
$
(
allChildren
[
13
]).
detach
());
$
(
allChildren
[
12
]).
append
(
$
(
allChildren
[
13
]).
detach
());
$
(
allChildren
[
12
]).
append
(
$
(
allChildren
[
13
]).
detach
());
$
(
'.custom-bar-box'
).
each
((
index
,
item
)
=>
{
$
(
item
)
.
find
(
'.w-e-bar-item'
)
.
each
((
cIndex
,
cItem
)
=>
{
if
(
index
===
2
&&
cIndex
===
4
)
{
$
(
cItem
).
hide
();
}
else
{
const
oP
=
document
.
createElement
(
'p'
);
if
(
index
===
0
)
{
$
(
oP
).
insertBefore
(
$
(
cItem
).
find
(
'button'
));
oP
.
innerHTML
=
menuArr1
[
cIndex
];
}
else
if
(
index
===
2
)
{
oP
.
innerHTML
=
menuArr2
[
cIndex
];
$
(
cItem
).
append
(
oP
);
}
else
if
(
index
===
3
)
{
oP
.
innerHTML
=
menuArr3
[
cIndex
];
$
(
cItem
).
append
(
oP
);
}
}
});
});
setLoading
(
false
);
},
200
);
};
editorConfig
.
onCreated
=
(
editor
)
=>
{
setLoading
(
true
);
toolSetttingReplace
();
};
editorConfig
.
onFocus
=
(
editor
)
=>
{
clearTimeout
(
saveRef
.
current
);
};
editorConfig
.
onBlur
=
(
editor
)
=>
{
// 失焦保存
// setHtml(editor.getHtml());
saveContent
();
};
editorConfig
.
onChange
=
(
editor
)
=>
{
// setHtml(editor.getHtml());
// clearTimeout(saveRef.current);
// saveRef.current = setTimeout(() => {
// saveContent();
// }, 800);
};
// 及时销毁 editor ,重要!
useEffect
(()
=>
{
if
(
editor
)
{
// 图片上传
editor
.
on
(
'ImageMenuClick'
,
()
=>
{
console
.
log
(
'ImageMenuClick'
,
'----'
);
imageRef
.
current
.
setVisible
(
true
);
});
// 画廊上传
editor
.
on
(
'GalleryMenuClick'
,
()
=>
{
console
.
log
(
'GalleryMenuClick'
,
'----'
);
galleryRef
.
current
.
setVisible
(
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'
,
'----'
);
chapterItemRef
.
current
.
setVisible
(
true
);
});
// 章头
editor
.
on
(
'ChapterTitleClick'
,
()
=>
{
console
.
log
(
'ChapterTitleClick'
,
'----'
);
chapterTitleRef
.
current
.
setVisible
(
true
);
});
// 交互练习
editor
.
on
(
'PracticeMenuClick'
,
()
=>
{
console
.
log
(
'PracticeMenuClick'
,
'----'
);
practiceRef
.
current
.
setVisible
(
true
);
});
// 公式
editor
.
on
(
'FormulaMenuClick'
,
()
=>
{
console
.
log
(
'FormulaMenuClick'
,
'----'
);
formulaRef
.
current
.
setVisible
(
true
);
});
// 气泡
editor
.
on
(
'TooltipMenuClick'
,
()
=>
{
if
(
editor
.
getSelectionText
())
{
const
textSelection
=
editor
.
selection
;
if
(
textSelection
.
anchor
.
path
[
0
]
===
textSelection
.
focus
.
path
[
0
])
{
tooltipRef
.
current
.
setVisible
(
true
);
}
else
{
message
.
error
(
'气泡操作不能跨段落进行!'
);
}
}
else
{
message
.
error
(
'气泡操作需要选中内容!'
);
}
});
}
return
()
=>
{
if
(
editor
===
null
)
return
;
editor
.
destroy
();
setEditor
(
null
);
};
},
[
editor
]);
useEffect
(()
=>
{
if
(
editor
&&
html
)
{
setContent
(
html
);
}
},
[
html
,
editor
]);
const
tabKeyChange
=
(
key
)
=>
{
if
(
key
===
'text'
)
{
toolSetttingReplace
();
}
setTabKey
(
key
);
};
const
getStsAuthToken
=
async
()
=>
{
const
data
=
await
getAliOSSSTSToken
();
if
(
data
)
{
window
.
sessionStorage
.
setItem
(
'sts'
,
JSON
.
stringify
(
data
));
setSTSToken
(
data
);
const
ossClientTemp
=
await
new
AliOSS
({
accessKeyId
:
data
.
AccessKeyId
,
accessKeySecret
:
data
.
AccessKeySecret
,
stsToken
:
data
.
SecurityToken
,
endpoint
:
'zijingebook.com'
,
// region: 'cn-beijing',
// endpoint: data.endpoint,
bucket
:
bookBucketName
,
timeout
:
180000
,
refreshSTSToken
:
async
()
=>
{
const
info
=
await
getAliOSSSTSToken
();
return
{
AccessKeyId
:
info
.
AccessKeyId
,
AccessKeySecret
:
info
.
AccessKeySecret
,
SecurityToken
:
info
.
SecurityToken
,
};
},
refreshSTSTokenInterval
:
14
*
60
*
1000
,
});
setOssClient
(
ossClientTemp
);
}
};
useEffect
(()
=>
{
(
async
()
=>
{
const
tempStsToken
=
window
.
sessionStorage
?
window
.
sessionStorage
.
getItem
(
'sts'
)
:
''
;
try
{
const
stsToken
=
JSON
.
parse
(
tempStsToken
);
// 15 分钟过期
if
(
dayjs
(
stsToken
.
Expiration
).
valueOf
()
-
dayjs
().
valueOf
()
>=
14
*
60
*
1000
)
{
getStsAuthToken
();
}
else
{
const
ossClientTemp
=
await
new
AliOSS
({
accessKeyId
:
data
.
AccessKeyId
,
accessKeySecret
:
data
.
AccessKeySecret
,
stsToken
:
data
.
SecurityToken
,
// endpoint: data.endpoint,
// region: 'cn-beijing',
endpoint
:
'zijingebook.com'
,
bucket
:
bookBucketName
,
timeout
:
180000
,
refreshSTSToken
:
async
()
=>
{
const
info
=
await
getAliOSSSTSToken
();
return
{
AccessKeyId
:
info
.
AccessKeyId
,
AccessKeySecret
:
info
.
AccessKeySecret
,
SecurityToken
:
info
.
SecurityToken
,
};
},
refreshSTSTokenInterval
:
14
*
60
*
1000
,
});
setOssClient
(
ossClientTemp
);
setSTSToken
(
stsToken
);
}
}
catch
(
e
)
{
getStsAuthToken
();
}
})();
},
[]);
const
setColor
=
(
type
)
=>
{
$
(
`#chapter-item-header`
).
css
({
color
:
'#fff'
,
backgroundColor
:
colorList
[
type
-
1
]
});
$
(
`#chapter-item-section`
).
css
({
color
:
'#fff'
,
backgroundColor
:
colorList
[
type
-
1
]
});
};
return
(
<
div
className=
'wangeditor-customer-container'
>
<
div
style=
{
{
height
:
'calc(100vh - 250px)'
,
overflowY
:
'hidden'
}
}
className=
'editor-content-container'
>
<
div
className=
'title-head'
>
<
div
className=
'right'
>
<
div
className=
'save-time time-on'
>
<
Button
danger
type=
'link'
className=
'timepsave'
onClick=
{
saveContent
}
icon=
{
<
img
src=
{
timesave
}
/>
}
>
自动保存
</
Button
>
<
span
className=
'time'
>
{
autosaveTime
>
0
?
dayjs
(
autosaveTime
).
fromNow
()
:
''
}
</
span
>
</
div
>
<
div
className=
'save-time'
>
<
Button
icon=
{
<
EyeOutlined
/>
}
className=
'view'
>
预览
</
Button
>
<
span
className=
'time'
>
</
span
>
</
div
>
</
div
>
</
div
>
<
Spin
spinning=
{
loading
}
className=
'editor-content-container-loading'
>
<
Editor
defaultConfig=
{
editorConfig
}
value=
{
content
}
onCreated=
{
setEditor
}
// onChange={(editor) => setHtml(editor.getHtml())}
mode=
'default'
style=
{
{
height
:
'calc(100vh - 250px)'
,
overflowY
:
'hidden'
}
}
/>
</
Spin
>
</
div
>
{
/* <CustomerMenu editor={editor} /> */
}
<
div
className=
'menu-tabs-key'
>
<
div
className=
'tabs'
>
{
tabsMenu
&&
tabsMenu
.
length
&&
tabsMenu
.
map
((
item
)
=>
{
return
(
<
div
className=
{
`tabs-item ${item.key === tabKey ? 'active' : ''}`
}
key=
{
item
.
key
}
onClick=
{
()
=>
tabKeyChange
(
item
.
key
)
}
>
{
item
.
title
}
<
span
></
span
>
</
div
>
);
})
}
</
div
>
<
div
className=
'menu-tabs-content'
>
{
tabKey
===
'text'
&&
(
<
div
ref=
{
toolbarRef
}
className=
'toolbox-parent'
>
<
PaddingSpace
ref=
{
paddingSpaceRef
}
editor=
{
editor
}
editorNodes=
{
editorNodes
}
/>
<
Toolbar
editor=
{
editor
}
defaultConfig=
{
toolbarConfig
}
mode=
'default'
style=
{
{
borderBottom
:
'1px solid #ccc'
}
}
className=
'editor-toolbar-container'
></
Toolbar
>
</
div
>
)
}
{
tabKey
===
'style'
&&
(
<
div
className=
'styletem'
>
<
p
>
样式模板
</
p
>
<
ul
>
<
li
>
<
div
className=
'left'
>
<
span
className=
'color color1'
></
span
>
<
b
className=
'type'
>
默认
</
b
>
</
div
>
<
Button
type=
'link'
className=
'use'
onClick=
{
()
=>
setColor
(
1
)
}
>
使用
</
Button
>
</
li
>
<
li
>
<
div
className=
'left'
>
<
span
className=
'color color2'
></
span
>
<
b
className=
'type'
>
蓝色
</
b
>
</
div
>
<
Button
type=
'link'
className=
'use'
onClick=
{
()
=>
setColor
(
2
)
}
>
使用
</
Button
>
</
li
>
<
li
>
<
div
className=
'left'
>
<
span
className=
'color color3'
></
span
>
<
b
className=
'type'
>
绿色
</
b
>
</
div
>
<
Button
type=
'link'
className=
'use'
onClick=
{
()
=>
setColor
(
3
)
}
>
使用
</
Button
>
</
li
>
<
li
>
<
div
className=
'left'
>
<
span
className=
'color color4'
></
span
>
<
b
className=
'type'
>
红色
</
b
>
</
div
>
<
Button
type=
'link'
className=
'use'
onClick=
{
()
=>
setColor
(
4
)
}
>
使用
</
Button
>
</
li
>
</
ul
>
</
div
>
)
}
</
div
>
</
div
>
<
ImageModal
ref=
{
imageRef
}
editor=
{
editor
}
ossClient=
{
ossClient
}
STSToken=
{
STSToken
}
/>
<
VideoModal
ref=
{
videoRef
}
editor=
{
editor
}
ossClient=
{
ossClient
}
STSToken=
{
STSToken
}
/>
<
GalleryModal
ref=
{
galleryRef
}
editor=
{
editor
}
ossClient=
{
ossClient
}
STSToken=
{
STSToken
}
/>
<
AudioModal
ref=
{
audioRef
}
editor=
{
editor
}
ossClient=
{
ossClient
}
STSToken=
{
STSToken
}
/>
<
ChapterTitleModal
ref=
{
chapterTitleRef
}
editor=
{
editor
}
ossClient=
{
ossClient
}
STSToken=
{
STSToken
}
/>
<
ChapterItemModal
ref=
{
chapterItemRef
}
editor=
{
editor
}
ossClient=
{
ossClient
}
STSToken=
{
STSToken
}
/>
<
PracticeModal
ref=
{
practiceRef
}
chapterId=
{
chapterId
}
bookId=
{
bookId
}
editor=
{
editor
}
ossClient=
{
ossClient
}
STSToken=
{
STSToken
}
/>
<
FormulaModal
ref=
{
formulaRef
}
editor=
{
editor
}
ossClient=
{
ossClient
}
STSToken=
{
STSToken
}
/>
<
TooltipModal
ref=
{
tooltipRef
}
editor=
{
editor
}
chapterId=
{
chapterId
}
bookId=
{
bookId
}
ossClient=
{
ossClient
}
STSToken=
{
STSToken
}
/>
<
PracticeSettingModal
ref=
{
practiceSettingRef
}
nodes=
{
practiceRef
.
current
&&
practiceRef
.
current
.
nodes
?
practiceRef
.
current
.
nodes
:
null
}
chapterId=
{
chapterId
}
bookId=
{
bookId
}
editor=
{
editor
}
/>
</
div
>
);
});
export
default
WangEditorCustomer
;
src/common/wangeditor-customer/index.jsx
浏览文件 @
f77be006
...
...
@@ -13,7 +13,7 @@ import { Boot } from '@wangeditor/editor'
import
{
SlateEditor
,
DomEditor
,
SlateElement
,
SlateTransforms
}
from
'@wangeditor/editor'
import
{
Editor
,
Toolbar
}
from
'@wangeditor/editor-for-react'
import
{
fontFamilyList
}
from
'./utils/setting'
import
{
fontFamilyList
,
fontSizeList
,
lineHeightList
}
from
'./utils/setting'
import
'@wangeditor/editor/dist/css/style.css'
// 引入 css
import
timesave
from
'@/assets/images/timesave.png'
...
...
@@ -22,8 +22,6 @@ dayjs.extend(relativeTime)
import
{
storageChange
}
from
'@/utils/storage.js'
// import PaddingSpace from './customer/padding';
import
ImageAutoOnlineConf
from
'./customer/ImageOnline'
import
GalleryAutoConf
from
'./customer/Gallery'
import
GalleryAutoOnlineConf
from
'./customer/GalleryOnline'
...
...
@@ -37,11 +35,18 @@ import TooltipAutoConf from './customer/Tooltip'
import
ImageEditorConf
from
'./customer/ImageEditor'
import
CustomerLinkConf
from
'./customer/CustomerLink'
import
ExpandReadConf
from
'./customer/ExpandRead'
import
PolishingConf
from
'./ai/Polishing'
import
ExpandArticleConf
from
'./ai/ExpandArticle'
import
RewriteConf
from
'./ai/Rewrite'
import
SummaryConf
from
'./ai/Summary'
import
AISelectTextRef
from
'./ai/AI'
// AI对话
import
AIChat
from
'./menu/AIChat'
// AI辅助
import
AIRewrite
from
'./menu/AIRewrite'
import
AIExpand
from
'./menu/AIExpand'
import
AISummary
from
'./menu/AISummary'
import
AIPolishing
from
'./menu/AIPolishing'
import
AIPunctuation
from
'./menu/AIPunctuation'
import
AIContentInspect
from
'./menu/AIContentInspect'
import
AIDigitalHuman
from
'./menu/AIDigitalHuman'
import
ImageModal
from
'./components/image'
import
VideoModal
from
'./components/video'
...
...
@@ -54,8 +59,6 @@ import FormulaModal from './components/formula'
import
TooltipModal
from
'./components/tooltip'
import
LinkModal
from
'./components/link'
import
ExpandModal
from
'./components/expand'
import
AIDrawerComponent
from
'./ai-drawer/index'
import
AIWrite
from
'./components/aiWrite'
import
chapterSectionModule
from
'./node/chapterItem'
import
chapterHeaderModule
from
'./node/chapterTitle'
...
...
@@ -79,13 +82,6 @@ import $ from 'jquery'
import
{
getAliOSSSTSToken
}
from
'./utils/request'
import
'./index.less'
const
menuArr0
=
[
'重做'
,
'撤销'
]
const
menuArr1
=
[
'字体样式'
,
'字号'
,
'行高'
]
const
menuArr2
=
[
'离线图片'
,
'在线图片'
,
'离线画廊'
,
'在线画廊'
,
'视频'
,
'音频'
,
'表格'
]
const
menuArr3
=
[
'代码块'
,
'引用'
,
'链接'
,
'公式'
,
'章头'
,
'节头'
,
'交互练习'
,
'气泡'
,
'扩展阅读'
]
const
menuArr4
=
[
'改写'
,
'扩写'
,
'缩写'
,
'总结'
]
const
colorList
=
[
'#ab1941'
,
'#2970f6'
,
'#2ad882'
,
'#eb3351'
]
const
bookBucketName
=
'zxts-book-file'
const
module
=
{
...
...
@@ -104,11 +100,14 @@ const module = {
ImageEditorConf
,
CustomerLinkConf
,
ExpandReadConf
,
PolishingConf
,
ExpandArticleConf
,
RewriteConf
,
SummaryConf
,
AISelectTextRef
AIChat
,
AIRewrite
,
AIExpand
,
AISummary
,
AIPolishing
,
AIPunctuation
,
AIContentInspect
,
AIDigitalHuman
]
}
Boot
.
registerModule
(
module
)
...
...
@@ -149,7 +148,7 @@ const WangEditorCustomer = (props, ref) => {
const
dispatch
=
useDispatch
()
// 自动保存时间
const
{
autosaveTime
}
=
useSelector
(
(
state
)
=>
state
.
editor
)
const
{
autosaveTime
}
=
useSelector
(
state
=>
state
.
editor
)
const
toolbarRef
=
useRef
()
...
...
@@ -157,7 +156,6 @@ const WangEditorCustomer = (props, ref) => {
const
[
STSToken
,
setSTSToken
]
=
useState
(
null
)
// oss 过期设置
const
[
tabKey
,
setTabKey
]
=
useState
(
'text'
)
const
[
loading
,
setLoading
]
=
useState
(
true
)
// editor 实例
const
[
editor
,
setEditor
]
=
useState
(
null
)
const
[
content
,
setContent
]
=
useState
(
html
)
...
...
@@ -183,7 +181,7 @@ const WangEditorCustomer = (props, ref) => {
const
[
aiVisible
,
setAIVisible
]
=
useState
(
false
)
// ai对话弹窗
const
[
priviewVisible
,
setPr
i
viewVisible
]
=
useState
(
false
)
const
[
priviewVisible
,
setPr
e
viewVisible
]
=
useState
(
false
)
const
[
historyVisible
,
setHistoryVisible
]
=
useState
(
false
)
//点击历史
const
[
selectionSize
,
setSelectionSize
]
=
useState
(
16
)
// 当前字号大小
...
...
@@ -248,7 +246,7 @@ const WangEditorCustomer = (props, ref) => {
}
})
const
listenNodeStyle
=
(
path
)
=>
{
const
listenNodeStyle
=
path
=>
{
const
children
=
editor
.
children
let
node
=
null
if
(
path
[
1
]
===
0
)
{
...
...
@@ -303,16 +301,6 @@ const WangEditorCustomer = (props, ref) => {
// 工具栏配置
const
toolbarConfig
=
{
toolbarKeys
:
[
// '|',
// 'redo',
// 'undo',
// 'emotion',
// 'todo',
// 'fullScreen',
// '|',
// 'headerSelect',
// 'lineHeight',
// '|',
'redo'
,
'undo'
,
'|'
,
...
...
@@ -337,18 +325,32 @@ const WangEditorCustomer = (props, ref) => {
'justifyJustify'
,
'divider'
,
'|'
,
// 'insertImage',
// 'uploadImage',
// 'insertVideo',
// 'uploadVideo',
'ImageAuto'
,
'ImageAutoOnline'
,
'GalleryAuto'
,
'GalleryAutoOnline'
,
'VideoAuto'
,
'AudioAuto'
,
'insertTable'
,
'|'
,
'codeBlock'
,
// 代码块
// 'insertLink', // 链接
// 'insertFormula', // 公式
'blockquote'
// 引用
// 'code',
// 'clearStyle',
'blockquote'
,
// 引用
'CustomerLink'
,
'FormulaAuto'
,
'ChapterTitle'
,
'ChapterItem'
,
'Practice'
,
'TooltipAuto'
,
'ExpandRead'
,
'|'
,
'AIRewrite'
,
'AIExpand'
,
'AISummary'
,
'AIPolishing'
,
'AIPunctuation'
,
'AIContentInspect'
,
'|'
,
'AIDigitalHuman'
]
}
...
...
@@ -372,47 +374,12 @@ const WangEditorCustomer = (props, ref) => {
}
]
toolbarConfig
.
insertKeys
=
{
index
:
30
,
keys
:
[
'ImageAuto'
,
'ImageAutoOnline'
,
'GalleryAuto'
,
'GalleryAutoOnline'
,
'VideoAuto'
,
'AudioAuto'
,
'CustomerLink'
,
'FormulaAuto'
,
'ChapterTitle'
,
'ChapterItem'
,
'Practice'
,
'TooltipAuto'
,
'ExpandRead'
,
'RewriteAuto'
,
'ExpandArticleAuto'
,
'PolishingAuto'
,
'SummaryAuto'
]
}
// 编辑器配置
let
editorConfig
=
{
placeholder
:
'请输入内容...'
,
// 选中公式时的悬浮菜单
hoverbarKeys
:
{
// formula: {
// menuKeys: ['editFormula'], // “编辑公式”菜单
// },
// link: {
// menuKeys: [
// 'editLink',
// 'unLink',
// 'viewLink', // 默认的配置可以通过 `editor.getConfig().hoverbarKeys.link` 获取
// ],
// },
text
:
{
menuKeys
:
[
// 'headerSelect',
'CustomerLink'
,
'bulletedList'
,
'numberedList'
,
...
...
@@ -427,196 +394,58 @@ const WangEditorCustomer = (props, ref) => {
'color'
,
'bgColor'
,
'clearStyle'
,
'AI
SelectTextAuto
'
'AI
Chat
'
]
},
image
:
{
menuKeys
:
[
'imageWidth30'
,
'imageWidth50'
,
'imageWidth100'
,
'ImageEditor'
,
// 'viewImageLink',
'deleteImage'
]
menuKeys
:
[
'imageWidth30'
,
'imageWidth50'
,
'imageWidth100'
,
'ImageEditor'
,
'deleteImage'
]
},
ImageAuto
:
{
menuKeys
:
[
'imageWidthChpater100'
,
'imageWidthChpater50'
,
'imageWidthChpater30'
,
'convertToLinkCard'
]
}
},
MENU_CONF
:
{
fontSize
:
{
fontSizeList
:
[
{
name
:
'初号'
,
value
:
'56px'
},
{
name
:
'小初'
,
value
:
'48px'
},
{
name
:
'一号'
,
value
:
'34px'
},
{
name
:
'小一'
,
value
:
'32px'
},
{
name
:
'二号'
,
value
:
'29px'
},
{
name
:
'小二'
,
value
:
'24px'
},
{
name
:
'三号'
,
value
:
'21px'
},
{
name
:
'小三'
,
value
:
'20px'
},
{
name
:
'四号'
,
value
:
'18px'
},
{
name
:
'小四'
,
value
:
'16px'
},
{
name
:
'五号'
,
value
:
'14px'
},
{
name
:
'小五'
,
value
:
'12px'
},
{
name
:
'六号'
,
value
:
'10px'
},
{
name
:
'小六'
,
value
:
'8px'
},
{
name
:
'七号'
,
value
:
'7px'
},
{
name
:
'八号'
,
value
:
'6px'
}
]
},
fontFamily
:
{
fontFamilyList
:
fontFamilyList
},
lineHeight
:
{
lineHeightList
:
[
'1'
,
'1.25'
,
'1.5'
,
'2'
,
'2.5'
,
'3'
]
}
// 其他...
fontSize
:
{
fontSizeList
},
fontFamily
:
{
fontFamilyList
},
lineHeight
:
{
lineHeightList
}
}
}
// 编辑器按钮重排
const
toolSett
t
ingReplace
=
()
=>
{
const
toolSettingReplace
=
()
=>
{
setTimeout
(()
=>
{
const
toolbarElement
=
toolbarRef
.
current
&&
toolbarRef
.
current
.
children
[
0
].
children
[
0
]
const
allChildren
=
toolbarElement
.
children
const
oHDiv_1
=
document
.
createElement
(
'div'
)
oHDiv_1
.
setAttribute
(
'class'
,
'custom-bar-box two'
)
toolbarElement
.
insertBefore
(
oHDiv_1
,
allChildren
[
0
])
$
(
allChildren
[
0
]).
append
(
$
(
allChildren
[
1
]).
detach
())
$
(
allChildren
[
0
]).
append
(
$
(
allChildren
[
1
]).
detach
())
const
oH6_1
=
document
.
createElement
(
'h4'
)
oH6_1
.
setAttribute
(
'class'
,
'w-auto type-heading'
)
oH6_1
.
innerHTML
=
'常用格式'
toolbarElement
.
insertBefore
(
oH6_1
,
allChildren
[
2
])
// 行高字体字号设置
const
itemBox
=
'<div class="custom-bar-box two"></div>'
$
(
itemBox
).
insertBefore
(
$
(
allChildren
[
3
]))
$
(
allChildren
[
3
]).
append
(
$
(
allChildren
[
4
]).
detach
())
$
(
allChildren
[
3
]).
append
(
$
(
allChildren
[
4
]).
detach
())
$
(
allChildren
[
3
]).
append
(
$
(
allChildren
[
4
]).
detach
())
// 加粗之类的
const
itemBox2
=
'<div class="w-e-bar-boxitem you"></div>'
$
(
itemBox2
).
insertBefore
(
$
(
allChildren
[
4
]))
$
(
allChildren
[
4
]).
append
(
$
(
allChildren
[
5
]).
detach
())
$
(
allChildren
[
4
]).
append
(
$
(
allChildren
[
5
]).
detach
())
$
(
allChildren
[
4
]).
append
(
$
(
allChildren
[
5
]).
detach
())
$
(
allChildren
[
4
]).
append
(
$
(
allChildren
[
5
]).
detach
())
$
(
allChildren
[
4
]).
append
(
$
(
allChildren
[
5
]).
detach
())
$
(
allChildren
[
4
]).
append
(
$
(
allChildren
[
5
]).
detach
())
$
(
allChildren
[
4
]).
append
(
$
(
allChildren
[
5
]).
detach
())
$
(
allChildren
[
4
]).
append
(
$
(
allChildren
[
5
]).
detach
())
$
(
allChildren
[
4
]).
append
(
$
(
allChildren
[
5
]).
detach
())
$
(
allChildren
[
4
]).
append
(
$
(
allChildren
[
5
]).
detach
())
$
(
allChildren
[
4
]).
append
(
$
(
allChildren
[
5
]).
detach
())
$
(
allChildren
[
4
]).
append
(
$
(
allChildren
[
5
]).
detach
())
// 对齐
const
itemBox3
=
'<div class="w-e-bar-boxitem you"></div>'
$
(
itemBox3
).
insertBefore
(
$
(
allChildren
[
5
]))
$
(
allChildren
[
5
]).
append
(
$
(
allChildren
[
6
]).
detach
())
$
(
allChildren
[
5
]).
append
(
$
(
allChildren
[
6
]).
detach
())
$
(
allChildren
[
5
]).
append
(
$
(
allChildren
[
6
]).
detach
())
$
(
allChildren
[
5
]).
append
(
$
(
allChildren
[
6
]).
detach
())
$
(
allChildren
[
5
]).
append
(
$
(
allChildren
[
6
]).
detach
())
// 媒体资源
const
oH6_2
=
document
.
createElement
(
'h4'
)
oH6_2
.
setAttribute
(
'class'
,
'w-auto type-heading'
)
oH6_2
.
innerHTML
=
'媒体资源'
toolbarElement
.
insertBefore
(
oH6_2
,
allChildren
[
7
])
const
itemBox4
=
document
.
createElement
(
'div'
)
itemBox4
.
setAttribute
(
'class'
,
'custom-bar-box media'
)
toolbarElement
.
insertBefore
(
itemBox4
,
allChildren
[
8
])
console
.
log
(
$
(
allChildren
[
8
]),
$
(
allChildren
[
13
]),
$
(
allChildren
[
9
]))
$
(
allChildren
[
8
]).
append
(
$
(
allChildren
[
13
]).
detach
())
$
(
allChildren
[
8
]).
append
(
$
(
allChildren
[
13
]).
detach
())
$
(
allChildren
[
8
]).
append
(
$
(
allChildren
[
13
]).
detach
())
$
(
allChildren
[
8
]).
append
(
$
(
allChildren
[
13
]).
detach
())
$
(
allChildren
[
8
]).
append
(
$
(
allChildren
[
13
]).
detach
())
$
(
allChildren
[
8
]).
append
(
$
(
allChildren
[
13
]).
detach
())
$
(
allChildren
[
8
]).
append
(
$
(
allChildren
[
9
]).
detach
())
// 高级模块
const
oH6_3
=
document
.
createElement
(
'h4'
)
oH6_3
.
setAttribute
(
'class'
,
'w-auto type-heading'
)
oH6_3
.
innerHTML
=
'高级模块'
toolbarElement
.
insertBefore
(
oH6_3
,
allChildren
[
10
])
const
itemBox5
=
document
.
createElement
(
'div'
)
itemBox5
.
setAttribute
(
'class'
,
'custom-bar-box hight'
)
toolbarElement
.
insertBefore
(
itemBox5
,
allChildren
[
11
])
$
(
allChildren
[
11
]).
append
(
$
(
allChildren
[
12
]).
detach
())
$
(
allChildren
[
11
]).
append
(
$
(
allChildren
[
12
]).
detach
())
$
(
allChildren
[
11
]).
append
(
$
(
allChildren
[
12
]).
detach
())
$
(
allChildren
[
11
]).
append
(
$
(
allChildren
[
12
]).
detach
())
$
(
allChildren
[
11
]).
append
(
$
(
allChildren
[
12
]).
detach
())
$
(
allChildren
[
11
]).
append
(
$
(
allChildren
[
12
]).
detach
())
$
(
allChildren
[
11
]).
append
(
$
(
allChildren
[
12
]).
detach
())
$
(
allChildren
[
11
]).
append
(
$
(
allChildren
[
12
]).
detach
())
$
(
allChildren
[
11
]).
append
(
$
(
allChildren
[
12
]).
detach
())
// ai
const
dividerline
=
document
.
createElement
(
'div'
)
dividerline
.
setAttribute
(
'class'
,
'w-e-bar-divider'
)
toolbarElement
.
insertBefore
(
dividerline
,
allChildren
[
12
])
const
oH6_4
=
document
.
createElement
(
'h4'
)
oH6_4
.
setAttribute
(
'class'
,
'w-auto type-heading'
)
oH6_4
.
innerHTML
=
'AI辅助'
toolbarElement
.
insertBefore
(
oH6_4
,
allChildren
[
13
])
const
oHDiv_4
=
document
.
createElement
(
'div'
)
oHDiv_4
.
setAttribute
(
'class'
,
'custom-bar-box hight'
)
toolbarElement
.
insertBefore
(
oHDiv_4
,
allChildren
[
14
])
$
(
allChildren
[
14
]).
append
(
$
(
allChildren
[
15
]).
detach
())
$
(
allChildren
[
14
]).
append
(
$
(
allChildren
[
15
]).
detach
())
$
(
allChildren
[
14
]).
append
(
$
(
allChildren
[
15
]).
detach
())
$
(
allChildren
[
14
]).
append
(
$
(
allChildren
[
15
]).
detach
())
$
(
'.custom-bar-box'
).
each
((
index
,
item
)
=>
{
$
(
item
)
.
find
(
'.w-e-bar-item'
)
.
each
((
cIndex
,
cItem
)
=>
{
const
oP
=
document
.
createElement
(
'p'
)
if
(
index
===
0
)
{
oP
.
innerHTML
=
menuArr0
[
cIndex
]
$
(
oP
).
insertBefore
(
$
(
cItem
).
find
(
'button'
))
}
else
if
(
index
===
1
)
{
oP
.
innerHTML
=
menuArr1
[
cIndex
]
$
(
oP
).
insertBefore
(
$
(
cItem
).
find
(
'button'
))
}
else
if
(
index
===
2
)
{
oP
.
innerHTML
=
menuArr2
[
cIndex
]
// $(cItem).append(oP);
$
(
cItem
).
find
(
'button'
).
append
(
oP
)
}
else
if
(
index
===
3
)
{
oP
.
innerHTML
=
menuArr3
[
cIndex
]
// $(cItem).append(oP);
$
(
cItem
).
find
(
'button'
).
append
(
oP
)
}
else
if
(
index
===
4
)
{
oP
.
innerHTML
=
menuArr4
[
cIndex
]
// $(cItem).append(oP);
$
(
cItem
).
find
(
'button'
).
append
(
oP
)
}
const
editorToolbar
=
document
.
querySelector
(
'.editor-toolbar-container'
)
// 设置菜单模块标题
const
dividerElements
=
editorToolbar
.
querySelectorAll
(
'.w-e-bar-divider'
)
const
dividerTitles
=
[
'常用格式'
,
'媒体资源'
,
'高级模块'
,
'AI辅助'
,
'AI数字人'
]
dividerElements
.
forEach
((
element
,
index
)
=>
{
element
.
innerHTML
=
dividerTitles
[
index
]
})
// 设置菜单标题
const
menuButtonElements
=
editorToolbar
.
querySelectorAll
(
'.w-e-bar-item button'
)
menuButtonElements
.
forEach
((
element
,
index
)
=>
{
if
(
index
>
1
&&
index
<
22
)
return
const
width
=
index
===
0
||
index
===
1
?
'50%'
:
'25%'
element
.
parentElement
.
style
.
width
=
width
element
.
classList
.
add
(
'has-title'
)
const
title
=
element
.
getAttribute
(
'data-tooltip'
)
const
span
=
document
.
createElement
(
'span'
)
span
.
innerHTML
=
title
span
.
className
=
'title'
element
.
appendChild
(
span
)
})
setLoading
(
false
)
},
350
)
},
50
)
}
editorConfig
.
onCreated
=
(
editor
)
=>
{
setLoading
(
true
)
}
editorConfig
.
onFocus
=
(
editor
)
=>
{
editorConfig
.
onFocus
=
editor
=>
{
clearTimeout
(
saveRef
.
current
)
}
editorConfig
.
onBlur
=
(
editor
)
=>
{
editorConfig
.
onBlur
=
editor
=>
{
// 失焦保存
setHtml
(
editor
.
getHtml
())
setContent
(
editor
.
getHtml
())
}
editorConfig
.
onChange
=
(
editor
)
=>
{
editorConfig
.
onChange
=
editor
=>
{
setHtml
(
editor
.
getHtml
())
setContent
(
editor
.
getHtml
())
}
...
...
@@ -624,10 +453,7 @@ const WangEditorCustomer = (props, ref) => {
// 及时销毁 editor ,重要!
useEffect
(()
=>
{
if
(
editor
)
{
// console.log(editor.getConfig().hoverbarKeys.image);
// console.log(editor, editorConfig);
toolSetttingReplace
()
toolSettingReplace
()
// 图片上传
editor
.
on
(
'ImageMenuClick'
,
()
=>
{
console
.
log
(
'ImageMenuClick'
,
'----'
)
...
...
@@ -695,7 +521,7 @@ const WangEditorCustomer = (props, ref) => {
console
.
log
(
'ImageEditorClick'
,
'----'
)
const
nodeEntries
=
SlateEditor
.
nodes
(
editor
,
{
match
:
(
node
)
=>
{
match
:
node
=>
{
// JS syntax
if
(
SlateElement
.
isElement
(
node
))
{
if
(
node
.
type
===
'paragraph'
)
{
...
...
@@ -745,45 +571,12 @@ const WangEditorCustomer = (props, ref) => {
}
setExpandVisible
(
true
)
})
// 改写
editor
.
on
(
'RewriteMenuClick'
,
()
=>
{
if
(
editor
.
selection
)
{
listenNodeStyle
(
editor
.
selection
.
anchor
.
path
)
}
setAiWriteOpen
(
true
)
setAiWriteAction
(
'rewrite'
)
})
// 扩写
editor
.
on
(
'ExpandArticleMenuClick'
,
()
=>
{
if
(
editor
.
selection
)
{
listenNodeStyle
(
editor
.
selection
.
anchor
.
path
)
}
setAiWriteOpen
(
true
)
setAiWriteAction
(
'expand'
)
})
// 缩写
editor
.
on
(
'PolishingMenuClick'
,
()
=>
{
if
(
editor
.
selection
)
{
listenNodeStyle
(
editor
.
selection
.
anchor
.
path
)
}
setAiWriteOpen
(
true
)
setAiWriteAction
(
'abbreviate'
)
})
// 总结
editor
.
on
(
'SummaryMenuClick'
,
()
=>
{
if
(
editor
.
selection
)
{
listenNodeStyle
(
editor
.
selection
.
anchor
.
path
)
}
setAiWriteOpen
(
true
)
setAiWriteAction
(
'summary'
)
})
// ai对话
editor
.
on
(
'AISelectTextClick'
,
()
=>
{
setSelectText
(
editor
.
getSelectionText
())
setAIVisible
(
true
)
})
const
oldHtml
=
editor
.
getHtml
()
editor
.
addMark
(
'fontSize'
,
'18px'
)
editor
.
addMark
(
'fontFamily'
,
'黑体'
)
...
...
@@ -814,9 +607,9 @@ const WangEditorCustomer = (props, ref) => {
}
},
[
gData
,
editor
])
const
tabKeyChange
=
(
key
)
=>
{
const
tabKeyChange
=
key
=>
{
if
(
key
===
'text'
)
{
toolSett
t
ingReplace
()
toolSettingReplace
()
}
setTabKey
(
key
)
editor
.
focus
()
...
...
@@ -825,7 +618,7 @@ const WangEditorCustomer = (props, ref) => {
// 预览
const
previewIt
=
async
()
=>
{
await
saveContent
()
setPr
i
viewVisible
(
true
)
setPr
e
viewVisible
(
true
)
}
// 历史
...
...
@@ -838,7 +631,7 @@ const WangEditorCustomer = (props, ref) => {
if
(
data
)
{
window
.
sessionStorage
.
setItem
(
'sts'
,
JSON
.
stringify
(
data
))
setSTSToken
(
data
)
const
ossClientTemp
=
await
new
AliOSS
({
const
ossClientTemp
=
new
AliOSS
({
accessKeyId
:
data
.
AccessKeyId
,
accessKeySecret
:
data
.
AccessKeySecret
,
stsToken
:
data
.
SecurityToken
,
...
...
@@ -898,23 +691,27 @@ const WangEditorCustomer = (props, ref) => {
})()
},
[])
const
setColor
=
(
type
)
=>
{
const
colorList
=
[
{
color
:
'#ab1941'
,
name
:
'默认'
},
{
color
:
'#2970f6'
,
name
:
'蓝色'
},
{
color
:
'#2ad882'
,
name
:
'绿色'
},
{
color
:
'#eb3351'
,
name
:
'红色'
}
]
const
setColor
=
color
=>
{
const
headers
=
document
.
querySelectorAll
(
`.w-e-scroll .chapter-item-header`
)
const
sections
=
document
.
querySelectorAll
(
`.w-e-scroll .chapter-item-section`
)
headers
.
forEach
(
(
item
)
=>
{
headers
.
forEach
(
item
=>
{
const
node
=
DomEditor
.
toSlateNode
(
editor
,
item
)
const
path
=
DomEditor
.
findPath
(
editor
,
node
)
SlateTransforms
.
setNodes
(
editor
,
{
...
node
,
textColor
:
'#ffffff'
,
bgColor
:
color
List
[
type
-
1
]
},
{
at
:
path
})
SlateTransforms
.
setNodes
(
editor
,
{
...
node
,
textColor
:
'#ffffff'
,
bgColor
:
color
},
{
at
:
path
})
})
sections
.
forEach
(
(
item
)
=>
{
sections
.
forEach
(
item
=>
{
const
node
=
DomEditor
.
toSlateNode
(
editor
,
item
)
const
path
=
DomEditor
.
findPath
(
editor
,
node
)
SlateTransforms
.
setNodes
(
editor
,
{
...
node
,
textColor
:
'#ffffff'
,
bgColor
:
color
List
[
type
-
1
]
},
{
at
:
path
})
SlateTransforms
.
setNodes
(
editor
,
{
...
node
,
textColor
:
'#ffffff'
,
bgColor
:
color
},
{
at
:
path
})
})
}
const
[
aiWriteOpen
,
setAiWriteOpen
]
=
useState
(
false
)
const
[
aiWriteAction
,
setAiWriteAction
]
=
useState
(
'rewrite'
)
return
(
<
div
className=
"wangeditor-customer-container"
>
<
div
className=
"editor-content-container"
>
...
...
@@ -963,7 +760,7 @@ const WangEditorCustomer = (props, ref) => {
<
div
className=
"tabs"
>
{
tabsMenu
&&
tabsMenu
.
length
&&
tabsMenu
.
map
(
(
item
)
=>
{
tabsMenu
.
map
(
item
=>
{
return
(
<
div
className=
{
`tabs-item ${item.key === tabKey ? 'active' : ''}`
}
key=
{
item
.
key
}
onClick=
{
()
=>
tabKeyChange
(
item
.
key
)
}
>
{
item
.
title
}
...
...
@@ -975,54 +772,26 @@ const WangEditorCustomer = (props, ref) => {
<
div
className=
"menu-tabs-content"
>
{
tabKey
===
'text'
&&
(
<
div
ref=
{
toolbarRef
}
className=
"toolbox-parent"
>
<
Toolbar
editor=
{
editor
}
defaultConfig=
{
toolbarConfig
}
mode=
"default"
style=
{
{
borderBottom
:
'1px solid #ccc'
}
}
className=
"editor-toolbar-container"
></
Toolbar
>
<
Toolbar
editor=
{
editor
}
defaultConfig=
{
toolbarConfig
}
mode=
"default"
className=
"editor-toolbar-container"
></
Toolbar
>
</
div
>
)
}
{
tabKey
===
'style'
&&
(
<
div
className=
"styletem"
>
<
p
>
样式模板
</
p
>
<
ul
>
<
li
>
<
div
className=
"left"
>
<
span
className=
"color color1"
></
span
>
<
b
className=
"type"
>
默认
</
b
>
</
div
>
<
Button
type=
"link"
className=
"use"
onClick=
{
()
=>
setColor
(
1
)
}
>
使用
</
Button
>
</
li
>
<
li
>
<
div
className=
"left"
>
<
span
className=
"color color2"
></
span
>
<
b
className=
"type"
>
蓝色
</
b
>
</
div
>
<
Button
type=
"link"
className=
"use"
onClick=
{
()
=>
setColor
(
2
)
}
>
使用
</
Button
>
</
li
>
<
li
>
<
div
className=
"left"
>
<
span
className=
"color color3"
></
span
>
<
b
className=
"type"
>
绿色
</
b
>
</
div
>
<
Button
type=
"link"
className=
"use"
onClick=
{
()
=>
setColor
(
3
)
}
>
使用
</
Button
>
</
li
>
<
li
>
{
colorList
.
map
(
item
=>
{
return
(
<
li
key=
{
item
.
color
}
>
<
div
className=
"left"
>
<
span
className=
"color color4"
></
span
>
<
b
className=
"type"
>
红色
</
b
>
<
span
className=
"color"
style=
{
{
backgroundColor
:
item
.
color
}
}
></
span
>
<
b
className=
"type"
>
{
item
.
name
}
</
b
>
</
div
>
<
Button
type=
"link"
className=
"use"
onClick=
{
()
=>
setColor
(
4
)
}
>
<
Button
type=
"link"
className=
"use"
onClick=
{
()
=>
setColor
(
item
.
color
)
}
>
使用
</
Button
>
</
li
>
)
})
}
</
ul
>
</
div
>
)
}
...
...
@@ -1275,7 +1044,7 @@ const WangEditorCustomer = (props, ref) => {
classNames=
{
{
body
:
'phone-body'
,
wrapper
:
'phone-wrapper'
}
}
wrapClassName=
"wrap-phone-privew"
width=
"494px"
onCancel=
{
()
=>
setPr
i
viewVisible
(
false
)
}
>
onCancel=
{
()
=>
setPr
e
viewVisible
(
false
)
}
>
<
PreviewModal
ref=
{
previewRef
}
gData=
{
gData
}
editor=
{
editor
}
chapterId=
{
chapterId
}
bookId=
{
bookId
}
nowTitle=
{
nowTitle
}
/>
</
Modal
>
...
...
@@ -1302,25 +1071,6 @@ const WangEditorCustomer = (props, ref) => {
setContent=
{
setContent
}
/>
</
Modal
>
{
/* ai对话 */
}
<
Drawer
open=
{
aiVisible
}
width=
"600px"
title=
"AI对话"
destroyOnClose
onClose=
{
()
=>
setAIVisible
(
false
)
}
rootClassName=
"ai-drawer-wrapper"
className=
"ai-drawer-container"
>
<
AIDrawerComponent
setAIVisible=
{
setAIVisible
}
selectText=
{
selectText
}
/>
</
Drawer
>
<
AIWrite
open=
{
aiWriteOpen
}
docAction=
{
aiWriteAction
}
onCancel=
{
()
=>
setAiWriteOpen
(
false
)
}
editor=
{
editor
}
chapterId=
{
chapterId
}
bookId=
{
bookId
}
></
AIWrite
>
</
div
>
)
}
...
...
src/common/wangeditor-customer/index.less
浏览文件 @
f77be006
...
...
@@ -239,173 +239,41 @@
}
}
.editor-toolbar-container {
flex: 0 0 306px;
border: none !important;
margin: 10px;
.w-e-bar {
background-color: transparent;
padding: 5px 15px;
}
.w-e-bar-boxitem {
background-color: #fff;
width: 100%;
display: flex;
justify-content: space-around;
&.you {
flex-flow: row wrap;
.w-e-bar-item {
width: 16.6%;
flex: 0 0 16.6%;
}
}
&.line-height {
justify-content: flex-start;
padding: 0;
svg {
width: 18px;
height: 18px;
}
}
.custom-bar-box {
background-color: #fff;
.w-e-bar-divider {
margin: 0;
padding-top: 10px;
width: 100%;
display: flex;
justify-content: flex-start;
flex-flow: row wrap;
.w-e-menu-tooltip-v5 {
height: 50px;
height: auto;
background: #fafafa;
font-size: 14px;
font-weight: 600;
line-height: 40px;
}
&.two {
.w-e-bar-item {
width: 33%;
flex: 0 0 33%;
justify-content: flex-start;
text-align: left;
display: block;
p {
color: #333;
font-weight: bold;
}
height: auto;
button {
padding-left: 0;
}
}
svg:nth-child(2) {
display: none;
}
&.input {
justify-content: space-around;
padding: 10px 0;
height: 62px;
.input-item {
flex: 1;
padding: 0 10px;
text-align: center;
input {
&.has-title {
width: 100%;
color: #333;
text-align: center;
border: 1px solid #d9d9d9;
border-radius: 5px;
height: 30px;
line-height: 30px;
&:hover {
border-color: #b83956;
}
}
.text {
color: #999;
font-size: 12px;
margin-top: 8px;
}
}
}
&.media {
.w-e-bar-item {
width: 25%;
flex: 0 0 25%;
p {
font-size: 10px;
-webkit-transform: scale(1.2);
-moz-transform: scale(1.2);
-o-transform: scale(1.2);
-ms-transform: scale(1.2);
transform: scale(1.2);
}
}
}
&.hight {
.w-e-bar-item {
width: 25%;
flex: 0 0 25%;
p {
font-size: 10px;
-webkit-transform: scale(1.2);
-moz-transform: scale(1.2);
-o-transform: scale(1.2);
-ms-transform: scale(1.2);
transform: scale(1.2);
}
}
}
&.media,
&.hight {
.w-e-bar-item {
button {
display: block;
flex-direction: column;
height: 100%;
padding: 10px;
height: 50px;
svg {
font-size: 22px;
}
}
p {
display: block;
width: 100%;
margin: 0px;
}
}
}
.w-e-bar-item {
width: 25%;
flex: 0 0 25%;
display: flex;
flex-direction: column;
height: 60px;
}
}
.w-e-bar-divider {
display: block;
height: 1px;
width: 100%;
margin: 8px 0;
&:nth-of-type(16) {
height: 0;
}
// &:nth-child(odd) {
// height: 0;
// }
}
.w-auto {
display: block;
width: 100%;
height: 40px;
line-height: 40px;
&.type-heading {
font-weight: 600;
}
}
.w-e-bar {
svg {
width: 18px;
height: 18px;
&:nth-child(2) {
display: none;
}
align-items: center;
.title {
margin: 5px 0 0 0;
}
}
.w-e-select-list {
z-index: 10001;
width: 180px !important;
}
.w-e-menu-tooltip-v5:before {
z-index: 10001;
}
}
.ant-spin-nested-loading,
...
...
@@ -417,7 +285,7 @@
}
.menu-tabs-key {
flex:
1
;
flex:
0 0 306px
;
background: #fafafa;
border: 1px solid #e5e5e5;
min-width: 300px;
...
...
src/common/wangeditor-customer/
ai/AI.js
→
src/common/wangeditor-customer/
menu/AIChat.jsx
浏览文件 @
f77be006
// Extend menu
class
AISelectTextAuto
{
import
BaseModalMenu
from
'./common/BaseModalMenu'
import
AIChatDrawer
from
'./common/AIChatDrawer'
class
AIChat
extends
BaseModalMenu
{
constructor
()
{
this
.
title
=
'AI'
;
this
.
iconSvg
=
`<svg t="1713246032755" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="12352" width="16" height="16"><path d="M826.368 325.632c0-7.168 2.048-10.24 10.24-10.24h123.904c7.168 0 10.24 2.048 10.24 10.24v621.568c0 7.168-2.048 10.24-10.24 10.24h-122.88c-8.192 0-10.24-4.096-10.24-10.24l-1.024-621.568z m-8.192-178.176c0-50.176 35.84-79.872 79.872-79.872 48.128 0 79.872 32.768 79.872 79.872 0 52.224-33.792 79.872-81.92 79.872-46.08 1.024-77.824-27.648-77.824-79.872zM462.848 584.704C441.344 497.664 389.12 307.2 368.64 215.04h-2.048c-16.384 92.16-58.368 247.808-92.16 369.664h188.416zM243.712 712.704l-62.464 236.544c-2.048 7.168-4.096 8.192-12.288 8.192H54.272c-8.192 0-10.24-2.048-8.192-12.288l224.256-783.36c4.096-13.312 7.168-26.624 8.192-65.536 0-6.144 2.048-8.192 7.168-8.192H450.56c6.144 0 8.192 2.048 10.24 8.192l250.88 849.92c2.048 7.168 0 10.24-7.168 10.24H573.44c-7.168 0-10.24-2.048-12.288-7.168l-65.536-236.544c1.024 1.024-251.904 0-251.904 0z" fill="#cdcdcd" p-id="12353"></path></svg>`
;
this
.
tag
=
'button'
;
super
()
this
.
title
=
'AI'
this
.
iconSvg
=
`<svg t="1713246032755" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="12352" width="16" height="16"><path d="M826.368 325.632c0-7.168 2.048-10.24 10.24-10.24h123.904c7.168 0 10.24 2.048 10.24 10.24v621.568c0 7.168-2.048 10.24-10.24 10.24h-122.88c-8.192 0-10.24-4.096-10.24-10.24l-1.024-621.568z m-8.192-178.176c0-50.176 35.84-79.872 79.872-79.872 48.128 0 79.872 32.768 79.872 79.872 0 52.224-33.792 79.872-81.92 79.872-46.08 1.024-77.824-27.648-77.824-79.872zM462.848 584.704C441.344 497.664 389.12 307.2 368.64 215.04h-2.048c-16.384 92.16-58.368 247.808-92.16 369.664h188.416zM243.712 712.704l-62.464 236.544c-2.048 7.168-4.096 8.192-12.288 8.192H54.272c-8.192 0-10.24-2.048-8.192-12.288l224.256-783.36c4.096-13.312 7.168-26.624 8.192-65.536 0-6.144 2.048-8.192 7.168-8.192H450.56c6.144 0 8.192 2.048 10.24 8.192l250.88 849.92c2.048 7.168 0 10.24-7.168 10.24H573.44c-7.168 0-10.24-2.048-12.288-7.168l-65.536-236.544c1.024 1.024-251.904 0-251.904 0z" fill="#cdcdcd" p-id="12353"></path></svg>`
this
.
tag
=
'button'
}
getValue
(
editor
)
{
return
'hello, AI'
;
}
isActive
(
editor
)
{
return
false
;
// or true
}
isDisabled
(
editor
)
{
return
false
;
// or true
}
exec
(
editor
,
value
)
{
// editor.insertText(value) // value 即 this.getValue(editor) 的返回值
if
(
this
.
isDisabled
(
editor
))
{
return
;
}
editor
.
emit
(
'AISelectTextClick'
);
return
<
AIChatDrawer
key=
{
Date
.
now
()
}
editor=
{
editor
}
></
AIChatDrawer
>
}
}
export
default
{
key
:
'AI
SelectTextAuto
'
,
// 定义 menu key :要保证唯一、不重复(重要)
key
:
'AI
Chat
'
,
// 定义 menu key :要保证唯一、不重复(重要)
factory
()
{
return
new
AISelectTextAuto
();
// 把 `YourMenuClass` 替换为你菜单的 class
},
};
export
{
AISelectTextAuto
};
return
new
AIChat
()
// 把 `YourMenuClass` 替换为你菜单的 class
}
}
src/common/wangeditor-customer/menu/AIContentInspect.jsx
0 → 100644
浏览文件 @
f77be006
import
BaseModalMenu
from
'./common/BaseModalMenu'
import
AIModal
from
'./common/AIModal'
class
AIContentInspect
extends
BaseModalMenu
{
constructor
()
{
super
()
this
.
title
=
'内容检查'
this
.
iconSvg
=
`<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><path fill="currentColor" d="M11.5 13.5q.525 0 .988-.137t.912-.413l1.525 1.575q.2.225.513.213t.537-.213q.225-.225.238-.537T16 13.45l-1.55-1.55q.275-.425.413-.9T15 10q0-1.475-1.038-2.488T11.5 6.5T9.037 7.513T8 10t1.038 2.488T11.5 13.5m0-1.5q-.825 0-1.412-.587T9.5 10t.588-1.412T11.5 8q.8 0 1.4.588T13.5 10t-.587 1.413T11.5 12M2 21q-.425 0-.712-.288T1 20t.288-.712T2 19h20q.425 0 .713.288T23 20t-.288.713T22 21zm2-3q-.825 0-1.412-.587T2 16V5q0-.825.588-1.412T4 3h16q.825 0 1.413.588T22 5v11q0 .825-.587 1.413T20 18zm0-2h16V5H4zm0 0V5z"/></svg>`
}
getValue
(
editor
)
{
return
<
AIModal
key=
{
Date
.
now
()
}
editor=
{
editor
}
docAction=
"contentInspect"
></
AIModal
>
}
}
export
default
{
key
:
'AIContentInspect'
,
factory
()
{
return
new
AIContentInspect
()
}
}
src/common/wangeditor-customer/menu/AIDigitalHuman.jsx
0 → 100644
浏览文件 @
f77be006
// Extend menu
class
AIDigitalHuman
{
constructor
()
{
this
.
title
=
'数字人'
this
.
iconSvg
=
`<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 28 28"><path fill="currentColor" d="M12 5.5a2 2 0 0 0 1.491 1.935c.337.053.68.053 1.018 0A2 2 0 1 0 12 5.5m-1.337 1.058a3.5 3.5 0 1 1 6.675 0l4.419-1.436a2.477 2.477 0 1 1 1.53 4.712L18 11.552v3.822c0 .16.03.32.091.468l2.728 6.752a2.477 2.477 0 0 1-4.594 1.856l-2.243-5.553l-2.232 5.56a2.46 2.46 0 0 1-3.21 1.362a2.477 2.477 0 0 1-1.364-3.215l2.734-6.812q.09-.224.09-.466v-3.774L4.712 9.834a2.477 2.477 0 0 1 1.531-4.712zm2.518 2.346a5 5 0 0 1-.649-.162L5.78 6.548a.977.977 0 0 0-.604 1.859l5.46 1.774c.515.168.864.648.864 1.189v3.957c0 .35-.067.698-.198 1.024l-2.734 6.811a.977.977 0 0 0 .538 1.267a.96.96 0 0 0 1.252-.531l2.463-6.136c.42-1.045 1.897-1.047 2.319-.003l2.476 6.129a.977.977 0 1 0 1.812-.732L16.7 16.404a2.8 2.8 0 0 1-.2-1.03V11.37c0-.541.349-1.021.864-1.189l5.46-1.774a.977.977 0 1 0-.604-1.859l-6.752 2.194q-.32.104-.649.162a3.5 3.5 0 0 1-1.639 0"/></svg>`
this
.
tag
=
'button'
}
getValue
()
{
return
'hello, 音频'
}
isActive
()
{
return
false
}
isDisabled
()
{
return
true
}
exec
()
{
return
}
}
export
default
{
key
:
'AIDigitalHuman'
,
factory
()
{
return
new
AIDigitalHuman
()
}
}
src/common/wangeditor-customer/
ai/ExpandArticle.js
→
src/common/wangeditor-customer/
menu/AIExpand.jsx
浏览文件 @
f77be006
// Extend menu
class
ExpandArticleAuto
{
import
BaseModalMenu
from
'./common/BaseModalMenu'
import
AIModal
from
'./common/AIModal'
class
AIExpand
extends
BaseModalMenu
{
constructor
()
{
this
.
title
=
'扩写'
;
super
()
this
.
title
=
'扩写'
this
.
iconSvg
=
`<svg width="48px" height="48px" viewBox="0 0 48 48" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>图标</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="图标" transform="translate(-622, -216)" fill="#333333" fill-rule="nonzero">
<g id="文章扩写" transform="translate(622, 216)">
...
...
@@ -11,32 +12,16 @@ class ExpandArticleAuto {
</g>
</g>
</g>
</svg>`
;
this
.
tag
=
'button'
;
</svg>`
}
getValue
(
editor
)
{
return
'hello, 音频'
;
}
isActive
(
editor
)
{
return
false
;
// or true
}
isDisabled
(
editor
)
{
return
false
;
// or true
}
exec
(
editor
,
value
)
{
// editor.insertText(value) // value 即 this.getValue(editor) 的返回值
if
(
this
.
isDisabled
(
editor
))
{
return
;
}
editor
.
emit
(
'ExpandArticleMenuClick'
);
return
<
AIModal
key=
{
Date
.
now
()
}
editor=
{
editor
}
docAction=
"expand"
></
AIModal
>
}
}
export
default
{
key
:
'
ExpandArticleAuto'
,
// 定义 menu key :要保证唯一、不重复(重要)
key
:
'
AIExpand'
,
factory
()
{
return
new
ExpandArticleAuto
();
// 把 `YourMenuClass` 替换为你菜单的 class
},
};
export
{
ExpandArticleAuto
};
return
new
AIExpand
()
}
}
src/common/wangeditor-customer/
ai/Polishing.js
→
src/common/wangeditor-customer/
menu/AIPolishing.jsx
浏览文件 @
f77be006
// Extend menu
class
PolishingAuto
{
import
BaseModalMenu
from
'./common/BaseModalMenu'
import
AIModal
from
'./common/AIModal'
class
AIPolishing
extends
BaseModalMenu
{
constructor
()
{
this
.
title
=
'缩写'
;
super
()
this
.
title
=
'润色'
this
.
iconSvg
=
`<svg width="48px" height="48px" viewBox="0 0 48 48" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>图标</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="图标" transform="translate(-536, -221)" fill="#333333" fill-rule="nonzero">
<g id="
缩写-(1)" transform="translate(536, 221)">
<g id="
润色-(1)" transform="translate(536, 221)">
<path d="M1.95471575,0 L44.9584623,0 C46.2616061,0 46.913178,0.678713837 46.913178,2.03614151 L46.913178,2.03614151 C46.913178,3.39356919 46.2616061,4.07228302 44.9584623,4.07228302 L1.95471575,4.07228302 C0.651571917,4.07228302 0,3.39356919 0,2.03614151 L0,2.03614151 C0,0.678713837 0.651571917,0 1.95471575,0 Z" id="路径"></path>
<path d="M1.95471575,12.2168491 L29.3207363,12.2168491 C30.6238801,12.2168491 31.275452,12.8955629 31.275452,14.2529906 L31.275452,14.2529906 C31.275452,15.6104183 30.6238801,16.2891321 29.3207363,16.2891321 L1.95471575,16.2891321 C0.651571917,16.2891321 0,15.6104183 0,14.2529906 L0,14.2529906 C0,12.8955629 0.651571917,12.2168491 1.95471575,12.2168491 Z" id="路径"></path>
<path d="M1.95471575,24.4336981 L17.5924418,24.4336981 C18.8955856,24.4336981 19.5471575,25.112412 19.5471575,26.4698397 L19.5471575,26.4698397 C19.5471575,27.8272673 18.8955856,28.5059812 17.5924418,28.5059812 L1.95471575,28.5059812 C0.651571917,28.5059812 0,27.8272673 0,26.4698397 L0,26.4698397 C0,25.112412 0.651571917,24.4336981 1.95471575,24.4336981 Z" id="路径"></path>
...
...
@@ -14,32 +17,16 @@ class PolishingAuto {
</g>
</g>
</g>
</svg>`
;
this
.
tag
=
'button'
;
</svg>`
}
getValue
(
editor
)
{
return
'hello, 缩写'
;
}
isActive
(
editor
)
{
return
false
;
// or true
}
isDisabled
(
editor
)
{
return
false
;
// or true
}
exec
(
editor
,
value
)
{
// editor.insertText(value) // value 即 this.getValue(editor) 的返回值
if
(
this
.
isDisabled
(
editor
))
{
return
;
}
editor
.
emit
(
'PolishingMenuClick'
);
return
<
AIModal
key=
{
Date
.
now
()
}
editor=
{
editor
}
docAction=
"abbreviate"
></
AIModal
>
}
}
export
default
{
key
:
'
PolishingAuto'
,
// 定义 menu key :要保证唯一、不重复(重要)
key
:
'
AIPolishing'
,
// 定义 menu key :要保证唯一、不重复(重要)
factory
()
{
return
new
PolishingAuto
();
// 把 `YourMenuClass` 替换为你菜单的 class
},
};
export
{
PolishingAuto
};
return
new
AIPolishing
()
// 把 `YourMenuClass` 替换为你菜单的 class
}
}
src/common/wangeditor-customer/menu/AIPunctuation.jsx
0 → 100644
浏览文件 @
f77be006
import
BaseModalMenu
from
'./common/BaseModalMenu'
import
AIModal
from
'./common/AIModal'
class
AIPunctuation
extends
BaseModalMenu
{
constructor
()
{
super
()
this
.
title
=
'标点校对'
this
.
iconSvg
=
`<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 1024 1024"><path fill="currentColor" d="M920 416H616c-4.4 0-8 3.6-8 8v112c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8v-56h60v320h-46c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h164c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8h-46V480h60v56c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8V424c0-4.4-3.6-8-8-8M656 296V168c0-4.4-3.6-8-8-8H104c-4.4 0-8 3.6-8 8v128c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-64h168v560h-92c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h264c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-92V232h168v64c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8"/></svg>`
}
getValue
(
editor
)
{
return
<
AIModal
key=
{
Date
.
now
()
}
editor=
{
editor
}
docAction=
"punctuation"
></
AIModal
>
}
}
export
default
{
key
:
'AIPunctuation'
,
factory
()
{
return
new
AIPunctuation
()
}
}
src/common/wangeditor-customer/
ai/Rewrite.js
→
src/common/wangeditor-customer/
menu/AIRewrite.jsx
浏览文件 @
f77be006
// Extend menu
class
RewriteAuto
{
import
BaseModalMenu
from
'./common/BaseModalMenu'
import
AIModal
from
'./common/AIModal'
class
AIRewrite
extends
BaseModalMenu
{
constructor
()
{
this
.
title
=
'改写'
;
super
()
this
.
title
=
'改写'
this
.
iconSvg
=
`<svg width="48px" height="48px" viewBox="0 0 48 48" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="图标" transform="translate(-731, -218)" fill="#333333" fill-rule="nonzero">
...
...
@@ -11,32 +15,17 @@ class RewriteAuto {
</g>
</g>
</g>
</svg>`
;
this
.
tag
=
'button'
;
</svg>`
this
.
tag
=
'button'
}
getValue
(
editor
)
{
return
'hello, 音频'
;
}
isActive
(
editor
)
{
return
false
;
// or true
}
isDisabled
(
editor
)
{
return
false
;
// or true
}
exec
(
editor
,
value
)
{
// editor.insertText(value) // value 即 this.getValue(editor) 的返回值
if
(
this
.
isDisabled
(
editor
))
{
return
;
}
editor
.
emit
(
'RewriteMenuClick'
);
return
<
AIModal
key=
{
Date
.
now
()
}
editor=
{
editor
}
docAction=
"rewrite"
></
AIModal
>
}
}
export
default
{
key
:
'
RewriteAuto'
,
// 定义 menu key :要保证唯一、不重复(重要)
key
:
'
AIRewrite'
,
factory
()
{
return
new
RewriteAuto
();
// 把 `YourMenuClass` 替换为你菜单的 class
},
};
export
{
RewriteAuto
};
return
new
AIRewrite
()
}
}
src/common/wangeditor-customer/
ai/Summary.js
→
src/common/wangeditor-customer/
menu/AISummary.jsx
浏览文件 @
f77be006
// Extend menu
class
SummaryAuto
{
import
BaseModalMenu
from
'./common/BaseModalMenu'
import
AIModal
from
'./common/AIModal'
class
AISummary
extends
BaseModalMenu
{
constructor
()
{
this
.
title
=
'总结'
;
super
()
this
.
title
=
'总结'
this
.
iconSvg
=
`<svg width="48px" height="48px" viewBox="0 0 48 48" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>图标</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="图标" transform="translate(-821, -216)" fill="#333333" fill-rule="nonzero">
<g id="总结" transform="translate(821, 216)">
...
...
@@ -13,32 +16,16 @@ class SummaryAuto {
</g>
</g>
</g>
</svg>`
;
this
.
tag
=
'button'
;
</svg>`
}
getValue
(
editor
)
{
return
'hello, 音频'
;
}
isActive
(
editor
)
{
return
false
;
// or true
}
isDisabled
(
editor
)
{
return
false
;
// or true
}
exec
(
editor
,
value
)
{
// editor.insertText(value) // value 即 this.getValue(editor) 的返回值
if
(
this
.
isDisabled
(
editor
))
{
return
;
}
editor
.
emit
(
'SummaryMenuClick'
);
return
<
AIModal
key=
{
Date
.
now
()
}
editor=
{
editor
}
docAction=
"summary"
></
AIModal
>
}
}
export
default
{
key
:
'
SummaryAuto'
,
// 定义 menu key :要保证唯一、不重复(重要)
key
:
'
AISummary'
,
factory
()
{
return
new
SummaryAuto
();
// 把 `YourMenuClass` 替换为你菜单的 class
},
};
export
{
SummaryAuto
};
return
new
AISummary
()
}
}
src/common/wangeditor-customer/menu/common/AIChatDrawer.jsx
0 → 100644
浏览文件 @
f77be006
import
{
useState
,
useEffect
}
from
'react'
import
{
SendOutlined
}
from
'@ant-design/icons'
import
{
Drawer
,
Input
,
Button
,
message
}
from
'antd'
import
dayjs
from
'dayjs'
import
{
useAIChat
}
from
'./useAIChat'
import
normalAvatar
from
'@/assets/images/icon-normal-avatar.png'
import
'./AIChatDrawer.less'
const
AIChatDrawer
=
props
=>
{
const
selectText
=
props
.
editor
.
getSelectionText
()
const
[
open
,
setOpen
]
=
useState
(
true
)
const
{
messages
,
post
,
isLoading
}
=
useAIChat
()
const
[
value
,
setValue
]
=
useState
(
selectText
)
// 输入值
useEffect
(()
=>
{
if
(
selectText
)
{
targetChat
()
}
return
()
=>
{
setValue
(
''
)
}
},
[
selectText
])
const
element
=
document
.
querySelector
(
'#chat-content'
)
useEffect
(()
=>
{
element
?.
scrollTo
(
0
,
999999
)
},
[
messages
,
element
])
// 提交对话
const
targetChat
=
async
()
=>
{
if
(
value
)
{
post
({
userChatInput
:
value
})
setValue
(
''
)
}
else
{
message
.
error
(
'请输入对话内容!'
)
}
}
return
(
<
Drawer
open=
{
open
}
width=
"600px"
title=
"AI对话"
destroyOnClose
onClose=
{
()
=>
setOpen
(
false
)
}
rootClassName=
"ai-drawer-wrapper"
className=
"ai-drawer-container"
>
<
div
className=
"ai-drawer-content"
>
<
div
className=
"ai-chat-container"
>
<
div
className=
"chat-content"
id=
"chat-content"
>
<
div
className=
"chat-content-padd"
>
{
messages
.
map
((
item
,
index
)
=>
{
return
(
<
div
className=
{
`chat-content-item`
}
key=
{
index
}
>
{
item
.
role_type
===
'user'
&&
<
div
className=
"time"
>
{
dayjs
(
item
.
time
).
format
(
'YYYY-MM-DD HH:mm'
)
}
</
div
>
}
{
item
.
role_type
===
'ai'
&&
(
<
div
className=
"inside"
>
<
div
className=
"ai-in-content"
>
<
div
className=
"img"
>
<
img
src=
{
item
.
role
?.
avatar
}
/>
</
div
>
<
div
className=
"ask-content"
>
{
item
.
content
}
</
div
>
</
div
>
</
div
>
)
}
{
item
.
role_type
===
'user'
&&
(
<
div
className=
"inside inside-user"
>
<
div
className=
"user-in-content"
>
<
div
className=
"ask-content"
>
{
item
.
content
}
</
div
>
<
div
className=
"img"
>
<
img
src=
{
normalAvatar
}
/>
</
div
>
</
div
>
</
div
>
)
}
</
div
>
)
})
}
</
div
>
</
div
>
</
div
>
<
div
className=
"ai-chat-ask"
>
<
div
className=
"text"
>
<
Input
.
TextArea
value=
{
value
}
defaultValue=
{
value
}
allowClear
disabled=
{
isLoading
}
onChange=
{
e
=>
setValue
(
e
.
target
.
value
)
}
placeholder=
"请输入内容"
style=
{
{
height
:
'80px'
,
resize
:
'none'
}
}
></
Input
.
TextArea
>
</
div
>
<
div
className=
"button"
>
<
Button
type=
"primary"
icon=
{
<
SendOutlined
/>
}
loading=
{
isLoading
}
size=
"large"
onClick=
{
targetChat
}
></
Button
>
</
div
>
</
div
>
</
div
>
</
Drawer
>
)
}
export
default
AIChatDrawer
src/common/wangeditor-customer/
ai-drawer/index
.less
→
src/common/wangeditor-customer/
menu/common/AIChatDrawer
.less
浏览文件 @
f77be006
...
...
@@ -102,7 +102,7 @@
flex: 1;
}
.button {
flex: 0 0
6
0px;
flex: 0 0
4
0px;
padding-left: 10px;
.ant-btn {
height: 80px;
...
...
src/common/wangeditor-customer/
components/aiWrite
.jsx
→
src/common/wangeditor-customer/
menu/common/AIModal
.jsx
浏览文件 @
f77be006
...
...
@@ -6,25 +6,27 @@ const { TextArea } = Input
const
actionMap
=
{
rewrite
:
{
name
:
'改写'
,
prompt
:
'帮我改写以下文字内容:'
},
expand
:
{
name
:
'扩写'
,
prompt
:
'帮我在以下文字内容基础上进行扩写:'
},
abbreviate
:
{
name
:
'缩写'
,
prompt
:
'帮我缩写以下文字内容:'
},
summary
:
{
name
:
'总结'
,
prompt
:
'帮我总结以下文字内容:'
}
abbreviate
:
{
name
:
'润色'
,
prompt
:
'帮我润色以下文字内容:'
},
summary
:
{
name
:
'总结'
,
prompt
:
'帮我总结以下文字内容:'
},
punctuation
:
{
name
:
'标点校对'
,
prompt
:
'帮我校对以下文字内容中的标点符号:'
},
contentInspect
:
{
name
:
'内容检查'
,
prompt
:
'帮我检查以下文字内容中的敏感词和错别字:'
}
}
export
default
function
AIWrite
({
editor
,
docAction
,
...
rest
})
{
export
default
function
AIModal
({
editor
,
docAction
})
{
const
[
isModalOpen
,
setIsModalOpen
]
=
useState
(
true
)
const
[
content
,
setContent
]
=
useState
(
''
)
const
{
text
,
fetch
,
isLoading
}
=
useAIEdit
()
const
actionText
=
actionMap
[
docAction
]?.
name
const
[
selectionText
,
setSelectionText
]
=
useState
(
''
)
useEffect
(()
=>
{
if
(
rest
.
open
)
{
const
selection
=
editor
.
getSelectionText
()
if
(
selection
)
{
setSelectionText
(
selection
)
setContent
(
selection
)
fetch
({
messages
:
[{
role
:
'user'
,
content
:
actionMap
[
docAction
].
prompt
+
selection
}]
})
}
}
},
[
rest
.
open
])
},
[
docAction
,
editor
,
fetch
])
useEffect
(()
=>
{
setContent
(
text
)
...
...
@@ -37,21 +39,19 @@ export default function AIWrite({ editor, docAction, ...rest }) {
const
handlePrimary
=
()
=>
{
editor
.
restoreSelection
()
editor
.
insertText
(
text
)
rest
.
onCancel
(
)
setIsModalOpen
(
false
)
}
return
(
<
Modal
title=
{
`以下是AI${actionText}结果:`
}
{
...
rest
}
open=
{
isModalOpen
}
footer=
{
null
}
classNames=
{
{
header
:
'editor-header-customer'
,
body
:
'editor-body-customer'
,
wrapper
:
'editor-wrapper-customer'
}
}
>
classNames=
{
{
header
:
'editor-header-customer'
,
body
:
'editor-body-customer'
,
wrapper
:
'editor-wrapper-customer'
}
}
onOk=
{
handlePrimary
}
onCancel=
{
()
=>
setIsModalOpen
(
false
)
}
>
<
Spin
spinning=
{
isLoading
}
>
<
TextArea
autoSize=
{
{
minRows
:
4
}
}
value=
{
content
}
onChange=
{
(
e
)
=>
setContent
(
e
.
target
.
value
)
}
/>
<
TextArea
autoSize=
{
{
minRows
:
4
}
}
value=
{
content
}
onChange=
{
e
=>
setContent
(
e
.
target
.
value
)
}
/>
</
Spin
>
<
br
/>
<
Flex
gap=
"small"
justify=
"center"
>
...
...
@@ -61,7 +61,7 @@ export default function AIWrite({ editor, docAction, ...rest }) {
<
Button
type=
"primary"
onClick=
{
handleFetch
}
>
重新
{
actionText
}
</
Button
>
<
Button
type=
"primary"
onClick=
{
rest
.
onCancel
}
>
<
Button
type=
"primary"
onClick=
{
()
=>
setIsModalOpen
(
false
)
}
>
取消
</
Button
>
</
Flex
>
...
...
src/common/wangeditor-customer/menu/common/BaseModalMenu.jsx
0 → 100644
浏览文件 @
f77be006
import
ReactDOM
from
'react-dom/client'
import
{
ConfigProvider
,
App
}
from
'antd'
import
zhCN
from
'antd/locale/zh_CN'
export
default
class
BaseModalMenu
{
constructor
()
{
this
.
tag
=
'button'
this
.
$el
=
document
.
createElement
(
'div'
)
document
.
body
.
appendChild
(
this
.
$el
)
this
.
$root
=
ReactDOM
.
createRoot
(
this
.
$el
)
}
isActive
()
{
return
false
// or true
}
isDisabled
()
{
return
false
// or true
}
exec
(
editor
,
value
)
{
this
.
$root
.
render
(
<
ConfigProvider
locale=
{
zhCN
}
theme=
{
{
token
:
{
colorPrimary
:
'#ab1941'
}
}
}
>
<
App
>
{
value
}
</
App
>
</
ConfigProvider
>
)
}
}
src/common/wangeditor-customer/menu/common/useAIChat.js
0 → 100644
浏览文件 @
f77be006
import
md5
from
'js-md5'
import
{
fetchEventSource
}
from
'@fortaine/fetch-event-source'
import
{
useState
,
useCallback
}
from
'react'
export
function
useAIChat
()
{
const
authKey
=
'f3846153ba784b6d86bdcd5533259c88'
const
authSecret
=
'HO4IyLEwEOHpeOXBxaLQUOqWslJRGs1M'
const
[
messages
,
setMessages
]
=
useState
([])
const
[
chatId
,
setChatId
]
=
useState
(
null
)
const
[
isLoading
,
setIsLoading
]
=
useState
(
false
)
const
addMessage
=
useCallback
(
message
=>
{
setMessages
(
prevMessages
=>
[...
prevMessages
,
message
])
},
[])
const
updateMessages
=
useCallback
(
newMessage
=>
{
setMessages
(
prevMessages
=>
{
const
existingMessage
=
prevMessages
.
find
(
msg
=>
msg
.
conversationId
===
newMessage
.
conversationId
)
const
content
=
newMessage
.
content
===
'
\
n'
?
'<br/>'
:
newMessage
.
content
||
''
if
(
existingMessage
)
{
// 更新现有消息
return
prevMessages
.
map
(
msg
=>
(
msg
.
conversationId
===
newMessage
.
conversationId
?
{
...
msg
,
content
:
msg
.
content
+
content
}
:
msg
))
}
else
{
// 新增消息
return
[...
prevMessages
,
{
...
newMessage
,
content
,
role_type
:
'ai'
,
time
:
Date
.
now
()
}]
}
})
},
[])
const
post
=
useCallback
(
async
data
=>
{
const
timestamp
=
Date
.
now
()
const
sign
=
md5
(
`
${
authKey
}${
authSecret
}${
timestamp
}
`
)
// 插入用户消息
addMessage
({
role_type
:
'user'
,
content
:
data
.
userChatInput
,
conversationId
:
`user-
${
timestamp
}
`
,
// 生成一个唯一的 conversationId
time
:
Date
.
now
()
})
setIsLoading
(
true
)
try
{
await
fetchEventSource
(
'/api/tiangong/openapi/agent/chat/stream/v1'
,
{
method
:
'POST'
,
headers
:
{
authKey
,
timestamp
,
sign
,
'Content-Type'
:
'application/json'
},
body
:
JSON
.
stringify
({
...
data
,
chatId
,
agentId
:
authKey
}),
onopen
(
response
)
{
if
(
!
response
.
ok
)
{
throw
new
Error
(
'Network response was not ok'
)
}
},
onmessage
(
event
)
{
const
message
=
JSON
.
parse
(
event
.
data
)
setChatId
(
message
.
chatId
)
updateMessages
(
message
)
},
onerror
(
error
)
{
console
.
error
(
'Fetch error:'
,
error
)
},
onclose
()
{
setIsLoading
(
false
)
}
})
}
catch
(
error
)
{
console
.
error
(
'Post error:'
,
error
)
setIsLoading
(
false
)
}
},
[
authKey
,
authSecret
,
chatId
,
addMessage
,
updateMessages
]
)
return
{
messages
,
isLoading
,
post
}
}
src/common/wangeditor-customer/utils/setting.js
浏览文件 @
f77be006
...
...
@@ -4,10 +4,10 @@ export const fontFamilyList = [
{
name
:
'黑体'
,
value
:
'黑体'
},
{
name
:
'宋体'
,
value
:
'宋体'
},
{
name
:
'楷体'
,
value
:
'楷体'
},
{
name
:
'仿宋'
,
value
:
'仿宋'
}
,
]
;
{
name
:
'仿宋'
,
value
:
'仿宋'
}
]
export
const
font
F
izeList
=
[
export
const
font
S
izeList
=
[
{
name
:
'初号'
,
value
:
56
},
{
name
:
'小初'
,
value
:
48
},
{
name
:
'一号'
,
value
:
34
},
...
...
@@ -23,55 +23,58 @@ export const fontFizeList = [
{
name
:
'六号'
,
value
:
10
},
{
name
:
'小六'
,
value
:
8
},
{
name
:
'七号'
,
value
:
7
},
{
name
:
'八号'
,
value
:
6
},
];
{
name
:
'八号'
,
value
:
6
}
]
export
const
lineHeightList
=
[
'1'
,
'1.25'
,
'1.5'
,
'2'
,
'2.5'
,
'3'
]
export
const
findElementPath
=
(
editor
,
elementType
)
=>
{
const
{
children
}
=
editor
;
const
{
children
}
=
editor
// 递归函数来遍历节点
function
traverseNodes
(
node
)
{
if
(
Array
.
isArray
(
node
))
{
for
(
let
i
=
0
;
i
<
node
.
length
;
i
++
)
{
const
child
=
node
[
i
]
;
traverseNodes
(
child
)
;
const
child
=
node
[
i
]
traverseNodes
(
child
)
if
(
child
.
type
===
elementType
)
{
// 当找到匹配的元素类型时,返回其路径
return
[
i
,
...
traverseNodes
(
child
)]
;
return
[
i
,
...
traverseNodes
(
child
)]
}
}
}
else
if
(
node
.
type
===
elementType
)
{
// 如果当前节点是元素节点且类型匹配,则返回其路径
return
[
traverseNodes
(
node
.
parent
)]
;
return
[
traverseNodes
(
node
.
parent
)]
}
return
null
;
return
null
}
// 从文档的根节点开始遍历
const
path
=
traverseNodes
(
children
)
;
return
path
;
}
;
const
path
=
traverseNodes
(
children
)
return
path
}
export
const
findNodeWithParent
=
(
node
,
type
,
key
=
'random'
,
value
)
=>
{
const
path
=
[]
;
// 用于存储当前节点的路径
const
path
=
[]
// 用于存储当前节点的路径
// 递归遍历节点的辅助函数
function
traverse
(
nodes
,
type
,
key
,
value
)
{
for
(
let
i
=
0
;
i
<
nodes
.
length
;
i
++
)
{
const
current
=
nodes
[
i
]
;
const
current
=
nodes
[
i
]
if
(
current
.
type
===
type
&&
String
(
current
[
key
])
===
String
(
value
))
{
// 找到匹配的节点,将索引添加到路径数组中
path
.
push
(
i
)
;
return
true
;
path
.
push
(
i
)
return
true
}
if
(
Array
.
isArray
(
current
.
children
)
&&
traverse
(
current
.
children
,
type
,
key
,
value
))
{
// 如果子节点中找到匹配的节点,添加当前节点的索引到路径
path
.
push
(
i
)
;
return
true
;
path
.
push
(
i
)
return
true
}
}
return
false
;
// 未找到匹配的节点
return
false
// 未找到匹配的节点
}
// 从根节点开始遍历
traverse
(
node
,
type
,
key
,
value
)
;
return
path
;
// 返回找到的路径,如果没有找到则返回空数组
}
;
traverse
(
node
,
type
,
key
,
value
)
return
path
// 返回找到的路径,如果没有找到则返回空数组
}
src/components/editor/index.jsx
浏览文件 @
f77be006
import
{
useState
,
useEffect
}
from
'react'
import
{
Editor
,
Toolbar
}
from
'@wangeditor/editor-for-react'
import
'@wangeditor/editor/dist/css/style.css'
// 引入 css
import
'./styles.less'
function
MyEdito
r
()
{
function
WangEditorCustome
r
()
{
// editor 实例
const
[
editor
,
setEditor
]
=
useState
(
null
)
// JS 语法
...
...
@@ -17,7 +18,38 @@ function MyEditor() {
},
[])
// 工具栏配置
const
toolbarConfig
=
{}
const
toolbarConfig
=
{
toolbarKeys
:
[
'redo'
,
'undo'
,
'|'
,
'fontFamily'
,
'fontSize'
,
'lineHeight'
,
'color'
,
'bold'
,
'italic'
,
'through'
,
'underline'
,
'bgColor'
,
'bulletedList'
,
'numberedList'
,
'indent'
,
'delIndent'
,
'sub'
,
'sup'
,
'justifyLeft'
,
'justifyCenter'
,
'justifyRight'
,
'justifyJustify'
,
'divider'
,
'|'
,
'insertTable'
,
'|'
,
'codeBlock'
,
// 代码块
'blockquote'
// 引用
]
}
// 编辑器配置
const
editorConfig
=
{
...
...
@@ -32,21 +64,52 @@ function MyEditor() {
}
},
[
editor
])
const
onCreated
=
editor
=>
{
setEditor
(
editor
)
setTimeout
(()
=>
{
const
editorToolbar
=
document
.
querySelector
(
'.editor-toolbar'
)
// 设置菜单模块标题
const
dividerElements
=
editorToolbar
.
querySelectorAll
(
'.w-e-bar-divider'
)
const
dividerTitles
=
[
'常用格式'
,
'媒体资源'
,
'高级模块'
,
'AI辅助'
,
'AI数字人'
]
dividerElements
.
forEach
((
element
,
index
)
=>
{
element
.
innerHTML
=
dividerTitles
[
index
]
})
// 设置菜单标题
const
menuButtonElements
=
editorToolbar
.
querySelectorAll
(
'.w-e-bar-item button'
)
menuButtonElements
.
forEach
((
element
,
index
)
=>
{
if
(
index
>
1
&&
index
<
22
)
return
element
.
classList
.
add
(
'has-title'
)
const
title
=
element
.
getAttribute
(
'data-tooltip'
)
const
span
=
document
.
createElement
(
'span'
)
span
.
innerHTML
=
title
span
.
className
=
'title'
element
.
appendChild
(
span
)
})
},
50
)
}
return
(
<>
<
div
style=
{
{
border
:
'1px solid #ccc'
,
zIndex
:
100
}
}
>
<
Toolbar
editor=
{
editor
}
defaultConfig=
{
toolbarConfig
}
mode=
"default"
style=
{
{
borderBottom
:
'1px solid #ccc'
}
}
/>
<
div
className=
"editor-wrapper"
>
<
div
className=
"editor-left"
>
<
div
className=
"editor-header"
></
div
>
<
div
className=
"editor-main"
>
<
Editor
defaultConfig=
{
editorConfig
}
value=
{
html
}
onCreated=
{
setEditor
}
onCreated=
{
onCreated
}
onChange=
{
editor
=>
setHtml
(
editor
.
getHtml
())
}
mode=
"default"
style=
{
{
height
:
'500px'
,
overflowY
:
'hidden'
}
}
/>
</
div
>
</
div
>
<
div
className=
"editor-right"
>
<
Toolbar
editor=
{
editor
}
defaultConfig=
{
toolbarConfig
}
mode=
"default"
className=
"editor-toolbar"
/>
</
div
>
</
div
>
</>
)
}
export
default
MyEdito
r
export
default
WangEditorCustome
r
src/components/editor/styles.less
0 → 100644
浏览文件 @
f77be006
.editor-wrapper {
display: flex;
}
.editor-left {
flex: 1;
border: 1px solid #ccc;
}
.editor-right {
flex: 0 0 300px;
background: #fafafa;
border: 1px solid #e5e5e5;
min-width: 300px;
margin-left: 10px;
border-radius: 6px;
}
.editor-toolbar {
margin: 10px;
.w-e-bar {
padding: 0;
svg {
width: 18px;
height: 18px;
}
}
.w-e-bar-divider {
margin: 0;
padding-top: 10px;
width: 100%;
height: auto;
background: #fafafa;
font-size: 14px;
font-weight: 600;
line-height: 40px;
}
.w-e-bar-item {
height: auto;
button {
svg:nth-child(2) {
display: none;
}
&.has-title {
width: 100%;
height: auto;
padding: 10px;
flex-direction: column;
align-items: center;
.title {
margin: 5px 0 0 0;
}
}
}
}
}
src/pages/books/section/index.jsx
浏览文件 @
f77be006
import
React
,
{
useState
,
useEffect
,
useRef
}
from
'react'
;
import
{
Divider
,
Button
,
Row
,
Col
,
Descriptions
,
Tree
,
Tooltip
,
Dropdown
,
Space
,
Input
,
Popconfirm
,
Modal
,
Spin
,
}
from
'antd'
;
import
React
,
{
useState
,
useEffect
,
useRef
}
from
'react'
import
{
Divider
,
Button
,
Row
,
Col
,
Descriptions
,
Tree
,
Tooltip
,
Dropdown
,
Space
,
Input
,
Popconfirm
,
Modal
,
Spin
}
from
'antd'
import
{
DiffOutlined
,
MenuFoldOutlined
,
...
...
@@ -22,251 +8,235 @@ import {
DashOutlined
,
CloseOutlined
,
CheckOutlined
,
EllipsisOutlined
,
}
from
'@ant-design/icons'
;
import
WangEditorCustomer
from
'@/common/wangeditor-customer'
;
import
{
useLocation
,
useNavigate
}
from
'react-router-dom'
;
import
{
useDispatch
,
useSelector
}
from
'react-redux'
;
import
{
setAutosaveTime
}
from
'@/store/modules/editor'
;
import
{
setTreeChapter
}
from
'@/store/modules/user'
;
import
EditChapterTitle
from
'./components/form-chapter-title'
;
import
{
get
}
from
'lodash-es'
;
import
md5
from
'js-md5'
;
import
{
convertToAntdTreeData
,
findTreeElementByKey
,
findFirstNotHasChildren
,
findParentLevelOne
,
findTreeToIndex
,
findNodeById
,
}
from
'@/utils/common'
;
import
{
getAllList
,
getInfoByChapterId
,
sectionEdit
,
chapterDel
,
dragOrder
,
getRecordList
,
getUserInfo
,
getPowerByRoleId
,
}
from
'./request'
;
import
'./index.less'
;
EllipsisOutlined
}
from
'@ant-design/icons'
import
WangEditorCustomer
from
'@/common/wangeditor-customer'
import
{
useLocation
,
useNavigate
}
from
'react-router-dom'
import
{
useDispatch
,
useSelector
}
from
'react-redux'
import
{
setAutosaveTime
}
from
'@/store/modules/editor'
import
{
setTreeChapter
}
from
'@/store/modules/user'
import
EditChapterTitle
from
'./components/form-chapter-title'
import
{
get
}
from
'lodash-es'
import
md5
from
'js-md5'
import
{
convertToAntdTreeData
,
findTreeElementByKey
,
findFirstNotHasChildren
,
findParentLevelOne
,
findTreeToIndex
,
findNodeById
}
from
'@/utils/common'
import
{
getAllList
,
getInfoByChapterId
,
sectionEdit
,
chapterDel
,
dragOrder
,
getRecordList
,
getUserInfo
,
getPowerByRoleId
}
from
'./request'
import
'./index.less'
const
Examine
=
()
=>
{
const
location
=
useLocation
()
;
const
id
=
get
(
location
,
'state.id'
,
''
)
;
const
location
=
useLocation
()
const
id
=
get
(
location
,
'state.id'
,
''
)
const
{
treeChapter
}
=
useSelector
(
(
state
)
=>
state
.
user
);
const
navigate
=
useNavigate
()
;
const
dispatch
=
useDispatch
()
;
const
{
treeChapter
}
=
useSelector
(
state
=>
state
.
user
)
const
navigate
=
useNavigate
()
const
dispatch
=
useDispatch
()
const
[
gData
,
setGData
]
=
useState
([])
;
const
[
chapterId
,
setChapterId
]
=
useState
(
0
)
;
const
[
bookId
,
setBookId
]
=
useState
(
0
)
;
const
[
nameList
,
setNameList
]
=
useState
([])
;
const
[
openDel
,
setOpenDel
]
=
useState
(
false
)
;
const
[
delNode
,
setDelNode
]
=
useState
({})
;
const
[
recordList
,
setRecordList
]
=
useState
([])
;
const
[
gData
,
setGData
]
=
useState
([])
const
[
chapterId
,
setChapterId
]
=
useState
(
0
)
const
[
bookId
,
setBookId
]
=
useState
(
0
)
const
[
nameList
,
setNameList
]
=
useState
([])
const
[
openDel
,
setOpenDel
]
=
useState
(
false
)
const
[
delNode
,
setDelNode
]
=
useState
({})
const
[
recordList
,
setRecordList
]
=
useState
([])
// 树节点设置
const
[
expandedKeys
,
setExpandedKeys
]
=
useState
([])
;
const
[
checkedKeys
,
setCheckedKeys
]
=
useState
([])
;
const
[
nowTitle
,
setNowTitle
]
=
useState
(
''
)
;
const
[
expandedKeys
,
setExpandedKeys
]
=
useState
([])
const
[
checkedKeys
,
setCheckedKeys
]
=
useState
([])
const
[
nowTitle
,
setNowTitle
]
=
useState
(
''
)
// 编辑操作
const
[
isCollapse
,
setisCollapse
]
=
useState
(
false
)
;
const
[
editKey
,
setEditKey
]
=
useState
(
null
)
;
const
[
parentId
,
setParentId
]
=
useState
(
null
)
;
const
[
editValue
,
setEditValue
]
=
useState
(
''
)
;
const
[
loading
,
setLoading
]
=
useState
(
false
)
;
const
[
delLoading
,
setDelLoading
]
=
useState
(
false
)
;
const
[
contentMd5
,
setContentMd5
]
=
useState
(
''
)
;
const
[
quanXian
,
setQuanXian
]
=
useState
(
false
)
;
const
[
isCollapse
,
setisCollapse
]
=
useState
(
false
)
const
[
editKey
,
setEditKey
]
=
useState
(
null
)
const
[
parentId
,
setParentId
]
=
useState
(
null
)
const
[
editValue
,
setEditValue
]
=
useState
(
''
)
const
[
loading
,
setLoading
]
=
useState
(
false
)
const
[
delLoading
,
setDelLoading
]
=
useState
(
false
)
const
[
contentMd5
,
setContentMd5
]
=
useState
(
''
)
const
[
quanXian
,
setQuanXian
]
=
useState
(
false
)
// 编辑器内容
const
editorRef
=
useRef
()
;
const
saveInterRef
=
useRef
()
;
const
[
html
,
setHtml
]
=
useState
(
''
)
;
const
[
contentId
,
setContentId
]
=
useState
(
false
)
;
const
editorRef
=
useRef
()
const
saveInterRef
=
useRef
()
const
[
html
,
setHtml
]
=
useState
(
''
)
const
[
contentId
,
setContentId
]
=
useState
(
false
)
// 获取目录结构
const
getChapterTreeList
=
async
()
=>
{
setLoading
(
true
)
;
const
{
data
=
[],
code
}
=
await
getAllList
({
book_id
:
id
})
;
setLoading
(
true
)
const
{
data
=
[],
code
}
=
await
getAllList
({
book_id
:
id
})
if
(
code
!==
200
)
{
setLoading
(
false
)
;
navigate
(
'/books/management/list'
,
{
replace
:
true
})
;
return
;
setLoading
(
false
)
navigate
(
'/books/management/list'
,
{
replace
:
true
})
return
}
if
(
data
.
length
===
0
)
{
setLoading
(
false
)
;
return
;
setLoading
(
false
)
return
}
const
uniqueBookList
=
[...
new
Set
(
data
.
map
(
(
item
)
=>
item
.
book_id
))][
0
];
setBookId
(
uniqueBookList
)
;
const
arr
=
convertToAntdTreeData
(
data
,
'name'
)
;
setGData
(
arr
)
;
const
allKeys
=
getAllNodeKeys
(
arr
)
;
setExpandedKeys
(
allKeys
)
;
const
uniqueBookList
=
[...
new
Set
(
data
.
map
(
item
=>
item
.
book_id
))][
0
]
setBookId
(
uniqueBookList
)
const
arr
=
convertToAntdTreeData
(
data
,
'name'
)
setGData
(
arr
)
const
allKeys
=
getAllNodeKeys
(
arr
)
setExpandedKeys
(
allKeys
)
const
first
=
findFirstNotHasChildren
(
arr
)
;
setNowTitle
(
first
.
title
)
;
const
first
=
findFirstNotHasChildren
(
arr
)
setNowTitle
(
first
.
title
)
if
(
treeChapter
&&
Object
.
entries
(
treeChapter
).
length
>
0
)
{
if
(
parseInt
(
treeChapter
.
saveBookId
)
===
parseInt
(
id
))
{
const
childInKey
=
findTreeElementByKey
(
arr
,
'key'
,
treeChapter
.
saveChapterId
)
;
const
childInKey
=
findTreeElementByKey
(
arr
,
'key'
,
treeChapter
.
saveChapterId
)
if
(
childInKey
)
{
setChapterId
(
childInKey
.
key
)
;
setCheckedKeys
([
childInKey
.
key
])
;
setNowTitle
(
childInKey
.
title
)
;
setChapterId
(
childInKey
.
key
)
setCheckedKeys
([
childInKey
.
key
])
setNowTitle
(
childInKey
.
title
)
}
else
{
setChapterId
(
first
.
key
)
;
setCheckedKeys
([
first
.
key
])
;
setNowTitle
(
first
.
title
)
;
setChapterId
(
first
.
key
)
setCheckedKeys
([
first
.
key
])
setNowTitle
(
first
.
title
)
}
}
else
{
setChapterId
(
first
.
key
)
;
setCheckedKeys
([
first
.
key
])
;
setNowTitle
(
first
.
title
)
;
await
dispatch
(
setTreeChapter
({
saveBookId
:
id
,
saveChapterId
:
first
.
key
}))
;
setChapterId
(
first
.
key
)
setCheckedKeys
([
first
.
key
])
setNowTitle
(
first
.
title
)
await
dispatch
(
setTreeChapter
({
saveBookId
:
id
,
saveChapterId
:
first
.
key
}))
}
}
else
{
setChapterId
(
first
.
key
);
setCheckedKeys
([
first
.
key
]);
setNowTitle
(
first
.
title
);
setChapterId
(
first
.
key
)
setCheckedKeys
([
first
.
key
])
setNowTitle
(
first
.
title
)
}
setLoading
(
false
)
}
setLoading
(
false
);
};
//选中了章节管理才展示编辑
const
getQuanXian
=
async
()
=>
{
const
data
=
await
getUserInfo
()
;
const
data1
=
await
getPowerByRoleId
({
role_id
:
data
.
id
})
;
const
data
=
await
getUserInfo
()
const
data1
=
await
getPowerByRoleId
({
role_id
:
data
.
id
})
if
(
!
data1
.
includes
(
37
))
{
setQuanXian
(
true
);
setQuanXian
(
true
)
}
}
};
const
newChapterChange
=
(
id
,
title
)
=>
{
setChapterId
(
parseInt
(
id
))
;
setCheckedKeys
([
parseInt
(
id
)])
;
setNowTitle
(
title
)
;
}
;
setChapterId
(
parseInt
(
id
))
setCheckedKeys
([
parseInt
(
id
)])
setNowTitle
(
title
)
}
// 递归函数,获取所有节点的key
const
getAllNodeKeys
=
(
nodes
)
=>
{
let
keys
=
[]
;
nodes
.
forEach
(
(
node
)
=>
{
keys
.
push
(
node
.
key
)
;
const
getAllNodeKeys
=
nodes
=>
{
let
keys
=
[]
nodes
.
forEach
(
node
=>
{
keys
.
push
(
node
.
key
)
if
(
node
.
children
&&
node
.
children
.
length
>
0
)
{
keys
=
keys
.
concat
(
getAllNodeKeys
(
node
.
children
));
keys
=
keys
.
concat
(
getAllNodeKeys
(
node
.
children
))
}
})
return
keys
}
});
return
keys
;
};
// 获取内容
const
getChapterVal
=
async
()
=>
{
setLoading
(
true
)
;
const
data
=
await
getInfoByChapterId
({
chapter_id
:
chapterId
})
;
dispatch
(
setTreeChapter
({
saveBookId
:
id
,
saveChapterId
:
chapterId
}))
;
setLoading
(
true
)
const
data
=
await
getInfoByChapterId
({
chapter_id
:
chapterId
})
dispatch
(
setTreeChapter
({
saveBookId
:
id
,
saveChapterId
:
chapterId
}))
const
{
content
,
id
:
cId
}
=
data
;
const
{
content
,
id
:
cId
}
=
data
if
(
content
)
{
setContentMd5
(
md5
(
content
))
;
setContentMd5
(
md5
(
content
))
}
setHtml
(
content
)
;
setContentId
(
cId
)
;
setHtml
(
content
)
setContentId
(
cId
)
if
(
editorRef
.
current
&&
editorRef
.
current
.
editor
)
{
editorRef
.
current
.
editor
.
setHtml
(
content
);
editorRef
.
current
.
editor
.
setHtml
(
content
)
}
setLoading
(
false
)
}
setLoading
(
false
);
};
const
onExpand
=
(
expandedKeys
)
=>
{
setExpandedKeys
(
expandedKeys
)
;
}
;
const
onExpand
=
expandedKeys
=>
{
setExpandedKeys
(
expandedKeys
)
}
const
handleSelect
=
async
(
checkedKeys
,
info
)
=>
{
const
{
key
}
=
info
.
node
;
const
childInKey
=
findTreeElementByKey
(
gData
,
'key'
,
key
)
;
const
{
key
}
=
info
.
node
const
childInKey
=
findTreeElementByKey
(
gData
,
'key'
,
key
)
if
(
childInKey
.
children
&&
childInKey
.
children
.
length
>
0
)
{
clearInterval
(
saveInterRef
.
current
)
;
clearInterval
(
saveInterRef
.
current
)
// 进行展开操作
const
tempExpandeds
=
JSON
.
parse
(
JSON
.
stringify
(
expandedKeys
))
;
let
newExpand
=
''
;
const
tempExpandeds
=
JSON
.
parse
(
JSON
.
stringify
(
expandedKeys
))
let
newExpand
=
''
if
(
expandedKeys
.
includes
(
key
))
{
newExpand
=
tempExpandeds
.
filter
(
(
item
)
=>
parseInt
(
item
)
!==
parseInt
(
key
));
newExpand
=
tempExpandeds
.
filter
(
item
=>
parseInt
(
item
)
!==
parseInt
(
key
))
}
else
{
newExpand
=
[...
tempExpandeds
,
key
]
;
newExpand
=
[...
tempExpandeds
,
key
]
}
setExpandedKeys
(
newExpand
)
;
setCheckedKeys
([
key
])
;
return
;
setExpandedKeys
(
newExpand
)
setCheckedKeys
([
key
])
return
}
else
{
const
{
key
,
title
}
=
info
.
node
;
const
{
key
,
title
}
=
info
.
node
if
(
info
.
selected
===
false
)
{
setLoading
(
true
)
;
setChapterId
(
key
)
;
setCheckedKeys
([
key
])
;
setNowTitle
(
title
)
;
setLoading
(
false
)
;
setLoading
(
true
)
setChapterId
(
key
)
setCheckedKeys
([
key
])
setNowTitle
(
title
)
setLoading
(
false
)
}
else
{
setLoading
(
true
)
;
clearInterval
(
saveInterRef
.
current
)
;
await
saveContent
()
;
setLoading
(
true
)
clearInterval
(
saveInterRef
.
current
)
await
saveContent
()
if
(
key
!==
chapterId
)
{
editorRef
.
current
.
editor
.
clear
();
editorRef
.
current
.
editor
.
clear
()
}
setLoading
(
true
)
setChapterId
(
checkedKeys
[
0
])
setCheckedKeys
([
checkedKeys
[
0
]])
setNowTitle
(
info
.
node
.
title
)
setLoading
(
false
)
}
setLoading
(
true
);
setChapterId
(
checkedKeys
[
0
]);
setCheckedKeys
([
checkedKeys
[
0
]]);
setNowTitle
(
info
.
node
.
title
);
setLoading
(
false
);
}
}
};
useEffect
(()
=>
{
getChapterTreeList
()
;
getQuanXian
()
;
},
[])
;
getChapterTreeList
()
getQuanXian
()
},
[])
useEffect
(()
=>
{
if
(
chapterId
)
{
setCheckedKeys
([
chapterId
])
;
getChapterVal
()
;
setCheckedKeys
([
chapterId
])
getChapterVal
()
}
},
[
chapterId
])
;
},
[
chapterId
])
// 保存方法
const
saveContent
=
async
()
=>
{
if
((
contentId
,
editorRef
.
current
,
editorRef
.
current
.
editor
,
!
loading
))
{
const
contentHtml
=
editorRef
.
current
.
editor
.
getHtml
()
;
const
newMd5
=
md5
(
contentHtml
)
;
clearInterval
(
saveInterRef
.
current
)
;
const
word_count
=
editorRef
.
current
.
editor
.
getText
().
length
;
const
contentHtml
=
editorRef
.
current
.
editor
.
getHtml
()
const
newMd5
=
md5
(
contentHtml
)
clearInterval
(
saveInterRef
.
current
)
const
word_count
=
editorRef
.
current
.
editor
.
getText
().
length
// 保存
const
data
=
await
sectionEdit
({
id
:
contentId
,
content
:
contentHtml
,
word_count
,
})
;
word_count
})
if
(
data
)
{
if
((
data
.
code
&&
data
.
code
===
3000
)
||
data
.
code
===
'3000'
)
{
navigate
(
'/books/management/list'
)
;
return
;
navigate
(
'/books/management/list'
)
return
}
else
{
setContentMd5
(
newMd5
);
autoSaveContent
(
newMd5
);
dispatch
(
setAutosaveTime
(
Date
.
now
()));
setContentMd5
(
newMd5
)
autoSaveContent
(
newMd5
)
dispatch
(
setAutosaveTime
(
Date
.
now
()))
}
}
}
}
};
// 定时器轮询
const
autoSaveContent
=
(
newMd5
)
=>
{
const
autoSaveContent
=
newMd5
=>
{
// clearInterval(saveInterRef.current);
// saveInterRef.current = setInterval(async () => {
// if (!contentMd5 || (newMd5 !== contentMd5 && contentMd5)) {
...
...
@@ -276,149 +246,153 @@ const Examine = () => {
// // console.log('save', newMd5, '一样的');
// }
// }, 5000);
}
;
}
useEffect
(()
=>
{
if
(
!
loading
&&
contentId
)
{
let
newMd5
=
md5
(
html
)
;
clearInterval
(
saveInterRef
.
current
)
;
autoSaveContent
(
newMd5
)
;
let
newMd5
=
md5
(
html
)
clearInterval
(
saveInterRef
.
current
)
autoSaveContent
(
newMd5
)
}
else
{
clearInterval
(
saveInterRef
.
current
)
;
clearInterval
(
saveInterRef
.
current
)
}
},
[
contentId
,
loading
,
contentMd5
,
html
])
;
},
[
contentId
,
loading
,
contentMd5
,
html
])
const
addChapterParent
=
()
=>
{
setEditKey
(
-
1
)
;
setEditValue
(
''
)
;
setParentId
(
-
1
)
;
}
;
setEditKey
(
-
1
)
setEditValue
(
''
)
setParentId
(
-
1
)
}
const
dropDownMenuHandler
=
async
(
e
,
node
)
=>
{
e
.
domEvent
.
stopPropagation
()
;
e
.
domEvent
.
preventDefault
()
;
e
.
domEvent
.
stopPropagation
()
e
.
domEvent
.
preventDefault
()
if
(
parseInt
(
e
.
key
)
===
1
)
{
// 展开
const
ids
=
findNodeById
(
gData
,
'key'
,
node
.
key
)
;
const
expandedKeysTemp
=
[...
expandedKeys
,
node
.
key
]
;
const
ids
=
findNodeById
(
gData
,
'key'
,
node
.
key
)
const
expandedKeysTemp
=
[...
expandedKeys
,
node
.
key
]
ids
.
forEach
((
item
,
index
)
=>
{
if
(
!
expandedKeysTemp
.
includes
(
item
))
{
expandedKeysTemp
.
push
(
item
)
;
expandedKeysTemp
.
push
(
item
)
}
})
;
setExpandedKeys
(
expandedKeysTemp
)
;
})
setExpandedKeys
(
expandedKeysTemp
)
}
else
if
(
parseInt
(
e
.
key
)
===
2
)
{
// 插入子节
// 编辑
setEditKey
(
-
1
)
;
setEditValue
(
''
)
;
setParentId
(
node
.
key
)
;
setEditKey
(
-
1
)
setEditValue
(
''
)
setParentId
(
node
.
key
)
}
else
if
(
parseInt
(
e
.
key
)
===
3
)
{
// 编辑
setEditKey
(
node
.
key
)
;
setEditValue
(
node
.
title
)
;
setParentId
(
false
)
;
setEditKey
(
node
.
key
)
setEditValue
(
node
.
title
)
setParentId
(
false
)
}
else
if
(
parseInt
(
e
.
key
)
===
4
)
{
setDelNode
(
node
);
setOpenDel
(
true
);
setDelNode
(
node
)
setOpenDel
(
true
)
}
}
};
const
delChapter
=
async
(
node
)
=>
{
setDelLoading
(
true
)
;
const
childInKey
=
findParentLevelOne
(
gData
,
'key'
,
node
.
key
)
;
let
current
=
null
;
const
delChapter
=
async
node
=>
{
setDelLoading
(
true
)
const
childInKey
=
findParentLevelOne
(
gData
,
'key'
,
node
.
key
)
let
current
=
null
if
(
childInKey
&&
childInKey
.
children
&&
childInKey
.
children
.
length
>
0
)
{
const
index
=
findTreeToIndex
(
childInKey
.
children
,
'key'
,
node
.
key
)
;
const
index
=
findTreeToIndex
(
childInKey
.
children
,
'key'
,
node
.
key
)
if
(
childInKey
.
children
.
length
===
1
)
{
current
=
childInKey
;
current
=
childInKey
}
else
{
// 前面还有
if
(
index
<=
childInKey
.
children
.
length
)
{
if
(
index
===
0
)
{
current
=
childInKey
.
children
[
index
+
1
]
;
current
=
childInKey
.
children
[
index
+
1
]
}
else
{
current
=
childInKey
.
children
[
index
-
1
]
;
current
=
childInKey
.
children
[
index
-
1
]
}
}
}
}
else
{
const
index
=
findTreeToIndex
(
gData
,
'key'
,
node
.
key
)
;
let
child
=
null
;
const
index
=
findTreeToIndex
(
gData
,
'key'
,
node
.
key
)
let
child
=
null
if
(
index
===
0
)
{
child
=
gData
[
index
+
1
]
;
child
=
gData
[
index
+
1
]
}
else
{
child
=
gData
[
index
-
1
]
;
child
=
gData
[
index
-
1
]
}
if
(
child
&&
child
.
children
&&
child
.
children
.
length
>
0
)
{
current
=
child
.
children
[
0
]
;
current
=
child
.
children
[
0
]
}
else
{
current
=
child
;
current
=
child
}
}
const
data
=
await
chapterDel
({
id
:
node
.
key
})
;
const
data
=
await
chapterDel
({
id
:
node
.
key
})
if
(
data
)
{
await
dispatch
(
setTreeChapter
({
saveBookId
:
id
,
saveChapterId
:
current
?
current
.
key
:
null
}),
);
setDelNode
({});
setOpenDel
(
false
);
await
getChapterTreeList
();
await
dispatch
(
setTreeChapter
({
saveBookId
:
id
,
saveChapterId
:
current
?
current
.
key
:
null
}))
setDelNode
({})
setOpenDel
(
false
)
await
getChapterTreeList
()
}
setDelLoading
(
false
)
}
setDelLoading
(
false
);
};
useEffect
(()
=>
{
if
(
gData
&&
gData
.
length
>
0
&&
treeChapter
&&
Object
.
entries
(
treeChapter
).
length
>
0
)
{
if
(
parseInt
(
treeChapter
.
saveBookId
)
===
parseInt
(
id
))
{
const
childInKey
=
findTreeElementByKey
(
gData
,
'key'
,
treeChapter
.
saveChapterId
)
;
const
childInKey
=
findTreeElementByKey
(
gData
,
'key'
,
treeChapter
.
saveChapterId
)
if
(
childInKey
&&
Object
.
entries
(
childInKey
).
length
>
0
)
{
setChapterId
(
parseInt
(
treeChapter
.
saveChapterId
))
;
setCheckedKeys
([
parseInt
(
treeChapter
.
saveChapterId
)])
;
setNowTitle
(
childInKey
.
title
)
;
setChapterId
(
parseInt
(
treeChapter
.
saveChapterId
))
setCheckedKeys
([
parseInt
(
treeChapter
.
saveChapterId
)])
setNowTitle
(
childInKey
.
title
)
}
}
}
},
[
gData
,
treeChapter
])
;
},
[
gData
,
treeChapter
])
useEffect
(()
=>
{
console
.
log
(
expandedKeys
)
;
},
[
expandedKeys
])
;
console
.
log
(
expandedKeys
)
},
[
expandedKeys
])
// 编辑章节名称
const
titleRenderDom
=
(
node
)
=>
{
const
titleRenderDom
=
node
=>
{
return
(
<
div
className=
'tree-customer-item'
>
<
div
className=
'title'
>
<
div
className=
'title-node'
>
{
node
.
title
}
</
div
>
<
div
className=
"tree-customer-item"
>
<
div
className=
"title"
>
<
div
className=
"title-node"
>
{
node
.
title
}
</
div
>
</
div
>
<
div
className=
'opaeration'
>
<
div
className=
"opaeration"
>
{
quanXian
?
(
<
Dropdown
trigger=
{
[
'click'
]
}
overlayClassName=
'dropmenu_list'
overlayClassName=
"dropmenu_list"
destroyPopupOnHide=
{
true
}
menu=
{
{
items
:
[
{
key
:
'1'
,
label
:
<
Button
type=
'text'
>
展开全部
</
Button
>,
label
:
<
Button
type=
"text"
>
展开全部
</
Button
>
},
{
key
:
'2'
,
label
:
<
Button
type=
'text'
>
添加子节
</
Button
>,
label
:
<
Button
type=
"text"
>
添加子节
</
Button
>
},
{
key
:
'
4
'
,
label
:
<
Button
type=
'text'
>
删除
</
Button
>,
key
:
'
5
'
,
label
:
<
Button
type=
"text"
>
设置编者
</
Button
>
},
{
key
:
'3'
,
label
:
<
Button
type=
"text"
>
重命名
</
Button
>
},
{
key
:
'4'
,
label
:
<
Button
type=
"text"
>
删除
</
Button
>
}
],
onClick
:
(
e
)
=>
dropDownMenuHandler
(
e
,
node
),
}
}
>
<
div
className=
'dashed'
>
onClick
:
e
=>
dropDownMenuHandler
(
e
,
node
)
}
}
>
<
div
className=
"dashed"
>
<
span
></
span
>
<
span
></
span
>
<
span
></
span
>
...
...
@@ -428,31 +402,30 @@ const Examine = () => {
)
:
(
<
Dropdown
trigger=
{
[
'click'
]
}
overlayClassName=
'dropmenu_list'
overlayClassName=
"dropmenu_list"
destroyPopupOnHide=
{
true
}
menu=
{
{
items
:
[
{
key
:
'1'
,
label
:
<
Button
type=
'text'
>
展开全部
</
Button
>,
label
:
<
Button
type=
"text"
>
展开全部
</
Button
>
},
{
key
:
'2'
,
label
:
<
Button
type=
'text'
>
添加子节
</
Button
>,
label
:
<
Button
type=
"text"
>
添加子节
</
Button
>
},
{
key
:
'3'
,
label
:
<
Button
type=
'text'
>
编辑
</
Button
>,
label
:
<
Button
type=
"text"
>
编辑
</
Button
>
},
{
key
:
'4'
,
label
:
<
Button
type=
'text'
>
删除
</
Button
>,
}
,
label
:
<
Button
type=
"text"
>
删除
</
Button
>
}
],
onClick
:
(
e
)
=>
dropDownMenuHandler
(
e
,
node
),
}
}
>
<
div
className=
'dashed'
>
onClick
:
e
=>
dropDownMenuHandler
(
e
,
node
)
}
}
>
<
div
className=
"dashed"
>
<
span
></
span
>
<
span
></
span
>
<
span
></
span
>
...
...
@@ -462,20 +435,20 @@ const Examine = () => {
)
}
</
div
>
</
div
>
)
;
}
;
)
}
const
onDrop
=
async
(
info
)
=>
{
const
dropToGap
=
info
.
dropToGap
;
// true 为同级或最大级 false 为子级
const
nowDropNode
=
info
.
dragNode
.
key
;
const
sideDropNode
=
info
.
node
.
key
;
const
onDrop
=
async
info
=>
{
const
dropToGap
=
info
.
dropToGap
// true 为同级或最大级 false 为子级
const
nowDropNode
=
info
.
dragNode
.
key
const
sideDropNode
=
info
.
node
.
key
let
data
=
null
;
let
data
=
null
if
(
dropToGap
)
{
// 同级
const
dragOverGapBottom
=
info
.
node
.
dragOverGapBottom
;
const
dragOver
=
info
.
node
.
dragOver
;
const
dragOverGapTop
=
info
.
node
.
dragOverGapTop
;
const
dragOverGapBottom
=
info
.
node
.
dragOverGapBottom
const
dragOver
=
info
.
node
.
dragOver
const
dragOverGapTop
=
info
.
node
.
dragOverGapTop
if
(
!
dragOverGapTop
&&
!
dragOver
&&
!
dragOverGapBottom
)
{
// 拖到最底部
// 拖拽到最顶层
...
...
@@ -483,8 +456,8 @@ const Examine = () => {
book_id
:
id
,
drag_node
:
nowDropNode
,
position_node
:
sideDropNode
,
drop_type
:
'after'
,
})
;
drop_type
:
'after'
})
}
else
{
if
(
dragOverGapTop
)
{
// 拖拽到最顶层
...
...
@@ -492,15 +465,15 @@ const Examine = () => {
book_id
:
id
,
drag_node
:
nowDropNode
,
position_node
:
sideDropNode
,
drop_type
:
'before'
,
})
;
drop_type
:
'before'
})
}
else
if
(
dragOverGapBottom
)
{
data
=
await
dragOrder
({
book_id
:
id
,
drag_node
:
nowDropNode
,
position_node
:
sideDropNode
,
drop_type
:
'after'
,
})
;
drop_type
:
'after'
})
}
}
}
else
{
...
...
@@ -509,58 +482,46 @@ const Examine = () => {
book_id
:
id
,
drag_node
:
nowDropNode
,
position_node
:
sideDropNode
,
drop_type
:
'inner'
,
})
;
drop_type
:
'inner'
})
}
if
(
data
)
{
await
getChapterTreeList
();
await
getChapterTreeList
()
}
}
};
return
(
<
div
className=
'examine'
>
<
div
className=
'content-box'
>
<
Row
gutter=
{
10
}
style=
{
{
height
:
'100%'
}
}
className=
'book-content-row'
>
<
Col
span=
{
isCollapse
?
1
:
4
}
className=
'book-content-tree'
>
<
div
className=
'border'
>
<
div
className=
"examine"
>
<
div
className=
"content-box"
>
<
Row
gutter=
{
10
}
style=
{
{
height
:
'100%'
}
}
className=
"book-content-row"
>
<
Col
span=
{
isCollapse
?
1
:
4
}
className=
"book-content-tree"
>
<
div
className=
"border"
>
{
!
isCollapse
?
(
<>
<
Descriptions
layout=
'vertical'
rootClassName=
'section-left-top'
layout=
"vertical"
rootClassName=
"section-left-top"
bordered
items=
{
[
{
key
:
'1'
,
label
:
(
<>
<
Row
gutter=
{
5
}
justify=
{
'space-between'
}
style=
{
{
alignItems
:
'center'
}
}
>
<
Row
gutter=
{
5
}
justify=
{
'space-between'
}
style=
{
{
alignItems
:
'center'
}
}
>
<
Col
>
章节目录
</
Col
>
<
Col
>
<
Button
type=
'text'
icon=
{
<
DiffOutlined
/>
}
onClick=
{
addChapterParent
}
></
Button
>
<
Button
type=
'text'
icon=
{
<
MenuFoldOutlined
/>
}
onClick=
{
()
=>
setisCollapse
(
true
)
}
></
Button
>
<
Button
type=
"text"
icon=
{
<
DiffOutlined
/>
}
onClick=
{
addChapterParent
}
></
Button
>
<
Button
type=
"text"
icon=
{
<
MenuFoldOutlined
/>
}
onClick=
{
()
=>
setisCollapse
(
true
)
}
></
Button
>
</
Col
>
</
Row
>
</>
)
,
}
,
)
}
]
}
/>
{
gData
&&
gData
.
length
>
0
&&
(
<
Tree
className=
'draggable-tree'
className=
"draggable-tree"
onSelect=
{
handleSelect
}
defaultExpandAll
defaultExpandedKeys=
{
expandedKeys
}
...
...
@@ -573,42 +534,34 @@ const Examine = () => {
disabled=
{
loading
}
treeData=
{
gData
}
onExpand=
{
onExpand
}
titleRender=
{
(
nodeData
)
=>
titleRenderDom
(
nodeData
)
}
titleRender=
{
nodeData
=>
titleRenderDom
(
nodeData
)
}
/>
)
}
</>
)
:
(
<
Descriptions
layout=
'vertical'
layout=
"vertical"
bordered
items=
{
[
{
key
:
'1'
,
label
:
(
<>
<
Row
gutter=
{
5
}
justify=
{
'space-between'
}
style=
{
{
alignItems
:
'center'
}
}
>
<
Row
gutter=
{
5
}
justify=
{
'space-between'
}
style=
{
{
alignItems
:
'center'
}
}
>
<
Col
>
<
Button
type=
'text'
icon=
{
<
MenuUnfoldOutlined
/>
}
onClick=
{
()
=>
setisCollapse
(
false
)
}
></
Button
>
<
Button
type=
"text"
icon=
{
<
MenuUnfoldOutlined
/>
}
onClick=
{
()
=>
setisCollapse
(
false
)
}
></
Button
>
</
Col
>
</
Row
>
</>
)
,
}
,
)
}
]
}
/>
)
}
</
div
>
</
Col
>
<
Col
span=
{
isCollapse
?
23
:
20
}
className=
'book-content-tree'
>
<
div
className=
'editor-right'
>
<
Col
span=
{
isCollapse
?
23
:
20
}
className=
"book-content-tree"
>
<
div
className=
"editor-right"
>
<
Spin
spinning=
{
loading
}
>
<
WangEditorCustomer
ref=
{
editorRef
}
...
...
@@ -639,9 +592,8 @@ const Examine = () => {
closeIcon=
{
false
}
// 添加这一行以隐藏关闭按钮
onCancel=
{
()
=>
setEditKey
(
false
)
}
classNames=
{
{
wrapper
:
'chapter-title-modal'
,
}
}
>
wrapper
:
'chapter-title-modal'
}
}
>
<
EditChapterTitle
setEditKey=
{
setEditKey
}
editValue=
{
editValue
}
...
...
@@ -668,30 +620,28 @@ const Examine = () => {
closeIcon=
{
false
}
// 添加这一行以隐藏关闭按钮
onCancel=
{
()
=>
setOpenDel
(
false
)
}
classNames=
{
{
wrapper
:
'chapter-title-modal'
,
}
}
>
wrapper
:
'chapter-title-modal'
}
}
>
<
Divider
/>
<
div
className=
''
>
确认删除子节【
{
delNode
.
title
}
】?
</
div
>
<
div
className=
''
style=
{
{
display
:
'flex'
,
justifyContent
:
'flex-end'
}
}
>
<
div
className=
""
>
确认删除子节【
{
delNode
.
title
}
】?
</
div
>
<
div
className=
""
style=
{
{
display
:
'flex'
,
justifyContent
:
'flex-end'
}
}
>
<
Space
>
<
Button
type=
'primary'
danger
loading=
{
delLoading
}
onClick=
{
()
=>
delChapter
(
delNode
)
}
>
<
Button
type=
"primary"
danger
loading=
{
delLoading
}
onClick=
{
()
=>
delChapter
(
delNode
)
}
>
确定
</
Button
>
<
Button
type=
'default'
type=
"default"
onClick=
{
()
=>
{
setDelNode
({});
setOpenDel
(
false
);
}
}
>
setDelNode
({})
setOpenDel
(
false
)
}
}
>
取消
</
Button
>
</
Space
>
</
div
>
</
Modal
>
</
div
>
)
;
}
;
)
}
export
default
Examine
;
export
default
Examine
src/pages/editor/index.jsx
0 → 100644
浏览文件 @
f77be006
import
Editor
from
'@/components/editor/index'
export
default
Editor
src/routes/routes.jsx
浏览文件 @
f77be006
...
...
@@ -307,6 +307,10 @@ const baseRouter = [
{
path
:
'userinfo'
,
Component
:
lazy
(()
=>
import
(
'@/pages/user-module/userInfo'
))
},
{
path
:
'editor'
,
Component
:
lazy
(()
=>
import
(
'@/pages/editor/index'
))
}
]
},
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论