Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
C
center-book
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
EzijingWeb
center-book
Commits
227c5f8b
提交
227c5f8b
authored
6月 14, 2024
作者:
王鹏飞
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
chore: update
上级
253a8d2e
隐藏空白字符变更
内嵌
并排
正在显示
15 个修改的文件
包含
697 行增加
和
615 行删除
+697
-615
chuangkit.js
src/api/chuangkit.js
+6
-0
index.jsx
src/common/wangeditor-customer/ai-drawer/index.jsx
+10
-11
gallery.jsx
src/common/wangeditor-customer/components/gallery.jsx
+15
-15
galleryItem.jsx
src/common/wangeditor-customer/components/galleryItem.jsx
+78
-41
image.jsx
src/common/wangeditor-customer/components/image.jsx
+46
-34
index.less
src/common/wangeditor-customer/components/index.less
+29
-2
onlineImageList.jsx
...common/wangeditor-customer/components/onlineImageList.jsx
+27
-0
Gallery.js
src/common/wangeditor-customer/customer/Gallery.js
+1
-1
GalleryOnline.js
src/common/wangeditor-customer/customer/GalleryOnline.js
+7
-7
Image.js
src/common/wangeditor-customer/customer/Image.js
+1
-1
ImageOnline.js
src/common/wangeditor-customer/customer/ImageOnline.js
+12
-12
index.jsx
src/common/wangeditor-customer/index.jsx
+50
-33
gallery.js
src/common/wangeditor-customer/node/gallery.js
+161
-166
galleryInline.js
src/common/wangeditor-customer/node/galleryInline.js
+71
-95
image.jsx
src/common/wangeditor-customer/node/image.jsx
+183
-197
没有找到文件。
src/api/chuangkit.js
0 → 100644
浏览文件 @
227c5f8b
import
axios
from
'@/utils/axios'
// 查询我的设计
export
function
getChuangKitDesigns
(
data
)
{
return
axios
.
post
(
'/api/ai/chuangKit/designs'
,
data
)
}
src/common/wangeditor-customer/ai-drawer/index.jsx
浏览文件 @
227c5f8b
import
React
,
{
useState
,
useEffect
,
useCallback
}
from
'react'
import
{
useState
,
useEffect
}
from
'react'
import
{
SendOutlined
}
from
'@ant-design/icons'
import
{
SendOutlined
}
from
'@ant-design/icons'
import
{
Input
,
Space
,
Button
,
message
}
from
'antd'
import
{
Input
,
Button
,
message
}
from
'antd'
import
md5
from
'js-md5'
import
md5
from
'js-md5'
import
dayjs
from
'dayjs'
import
dayjs
from
'dayjs'
import
{
sleep
}
from
'@/utils/common'
import
{
sleep
}
from
'@/utils/common'
...
@@ -14,11 +14,11 @@ const UUID = 'f3846153ba784b6d86bdcd5533259c88'
...
@@ -14,11 +14,11 @@ const UUID = 'f3846153ba784b6d86bdcd5533259c88'
const
auth_key
=
'f3846153ba784b6d86bdcd5533259c88'
const
auth_key
=
'f3846153ba784b6d86bdcd5533259c88'
const
auth_secret
=
'HO4IyLEwEOHpeOXBxaLQUOqWslJRGs1M'
const
auth_secret
=
'HO4IyLEwEOHpeOXBxaLQUOqWslJRGs1M'
const
AIDrawerComponent
=
props
=>
{
const
AIDrawerComponent
=
(
props
)
=>
{
const
{
selectText
}
=
props
const
{
selectText
}
=
props
const
dispatch
=
useDispatch
()
const
dispatch
=
useDispatch
()
const
{
userInfo
}
=
useSelector
(
state
=>
state
.
user
)
// 用户状态信息
const
{
userInfo
}
=
useSelector
(
(
state
)
=>
state
.
user
)
// 用户状态信息
const
{
editorAi
}
=
useSelector
(
state
=>
state
.
editor
)
// ai信息设置
const
{
editorAi
}
=
useSelector
(
(
state
)
=>
state
.
editor
)
// ai信息设置
const
[
value
,
setValue
]
=
useState
(
selectText
)
// 输入值
const
[
value
,
setValue
]
=
useState
(
selectText
)
// 输入值
const
[
loading
,
setLoading
]
=
useState
(
false
)
// loading
const
[
loading
,
setLoading
]
=
useState
(
false
)
// loading
...
@@ -31,7 +31,7 @@ const AIDrawerComponent = props => {
...
@@ -31,7 +31,7 @@ const AIDrawerComponent = props => {
if
(
editorAi
&&
Object
.
entries
(
editorAi
).
length
>
0
)
{
if
(
editorAi
&&
Object
.
entries
(
editorAi
).
length
>
0
)
{
let
tempJSON
=
JSON
.
parse
(
JSON
.
stringify
(
chatContentList
))
let
tempJSON
=
JSON
.
parse
(
JSON
.
stringify
(
chatContentList
))
let
conversationId
=
editorAi
.
conversationId
let
conversationId
=
editorAi
.
conversationId
const
item
=
tempJSON
.
filter
(
item
=>
item
.
conversationId
===
conversationId
)
const
item
=
tempJSON
.
filter
(
(
item
)
=>
item
.
conversationId
===
conversationId
)
if
(
item
&&
item
.
length
>
0
)
{
if
(
item
&&
item
.
length
>
0
)
{
tempJSON
[
tempJSON
.
length
-
1
]
=
editorAi
tempJSON
[
tempJSON
.
length
-
1
]
=
editorAi
}
else
{
}
else
{
...
@@ -57,7 +57,6 @@ const AIDrawerComponent = props => {
...
@@ -57,7 +57,6 @@ const AIDrawerComponent = props => {
}
}
},
[])
},
[])
let
cData
=
[]
let
buffer
=
''
let
buffer
=
''
const
readBuffer
=
(
reader
,
decoder
)
=>
{
const
readBuffer
=
(
reader
,
decoder
)
=>
{
...
@@ -85,7 +84,7 @@ const AIDrawerComponent = props => {
...
@@ -85,7 +84,7 @@ const AIDrawerComponent = props => {
})
})
}
}
const
processBuffer
=
text
=>
{
const
processBuffer
=
(
text
)
=>
{
let
aiContent
=
{}
let
aiContent
=
{}
try
{
try
{
const
content
=
text
.
replace
(
/
\n
+/g
,
''
).
split
(
'data:'
)
const
content
=
text
.
replace
(
/
\n
+/g
,
''
).
split
(
'data:'
)
...
@@ -129,7 +128,7 @@ const AIDrawerComponent = props => {
...
@@ -129,7 +128,7 @@ const AIDrawerComponent = props => {
}),
}),
mode
:
'cors'
mode
:
'cors'
})
})
.
then
(
response
=>
{
.
then
(
(
response
)
=>
{
// response.body 是一个 ReadableStream 对象
// response.body 是一个 ReadableStream 对象
const
stream
=
response
.
body
const
stream
=
response
.
body
// 使用流式数据
// 使用流式数据
...
@@ -147,7 +146,7 @@ const AIDrawerComponent = props => {
...
@@ -147,7 +146,7 @@ const AIDrawerComponent = props => {
// 开始读取流数据
// 开始读取流数据
readBuffer
(
reader
,
decoder
)
readBuffer
(
reader
,
decoder
)
})
})
.
catch
(
error
=>
{
.
catch
(
(
error
)
=>
{
// 处理请求错误
// 处理请求错误
console
.
error
(
'Request failed:'
,
error
)
console
.
error
(
'Request failed:'
,
error
)
})
})
...
@@ -200,7 +199,7 @@ const AIDrawerComponent = props => {
...
@@ -200,7 +199,7 @@ const AIDrawerComponent = props => {
defaultValue=
{
value
}
defaultValue=
{
value
}
allowClear
allowClear
disabled=
{
loading
}
disabled=
{
loading
}
onChange=
{
e
=>
setValue
(
e
.
target
.
value
)
}
onChange=
{
(
e
)
=>
setValue
(
e
.
target
.
value
)
}
palceholder=
"请输入内容"
palceholder=
"请输入内容"
style=
{
{
height
:
'80px'
,
resize
:
'none'
}
}
></
Input
.
TextArea
>
style=
{
{
height
:
'80px'
,
resize
:
'none'
}
}
></
Input
.
TextArea
>
</
div
>
</
div
>
...
...
src/common/wangeditor-customer/components/gallery.jsx
浏览文件 @
227c5f8b
...
@@ -7,14 +7,9 @@ import { SlateEditor, SlateTransforms, SlateElement } from '@wangeditor/editor'
...
@@ -7,14 +7,9 @@ import { SlateEditor, SlateTransforms, SlateElement } from '@wangeditor/editor'
import
{
findNodeWithParent
,
fontFizeList
}
from
'../utils/setting'
import
{
findNodeWithParent
,
fontFizeList
}
from
'../utils/setting'
import
GalleryFormItem
from
'./galleryItem'
import
GalleryFormItem
from
'./galleryItem'
const
temp
=
[
'http://zxts-book-file.zijingebook.com/2024/02/27/gallery-1709002611091-nrxnqw595pc.png'
,
'http://zxts-book-file.zijingebook.com/2024/02/27/gallery-1709002615115-ngpulfnmspr.jpg'
]
const
randomOne
=
Math
.
random
().
toString
(
16
).
substring
(
2
,
10
)
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
}
=
props
const
{
editor
,
ossClient
,
galleryInfo
,
bookId
,
chapterId
,
setGalleryVisible
,
setGalleryInfo
,
selectionSize
=
18
,
isOnline
=
false
}
=
props
const
[
form
]
=
Form
.
useForm
()
const
[
form
]
=
Form
.
useForm
()
const
[
loading
,
setLoading
]
=
useState
(
false
)
const
[
loading
,
setLoading
]
=
useState
(
false
)
...
@@ -52,7 +47,9 @@ const GalleryModal = props => {
...
@@ -52,7 +47,9 @@ const GalleryModal = props => {
const
initialItems
=
[
const
initialItems
=
[
{
{
label
:
'图 1'
,
label
:
'图 1'
,
children
:
<
GalleryFormItem
ossClient=
{
ossClient
}
form=
{
form
}
galleryInfo=
{
galleryInfo
}
activeKey=
{
randomOne
}
setPicList=
{
setPicList
}
/>,
children
:
(
<
GalleryFormItem
ossClient=
{
ossClient
}
form=
{
form
}
galleryInfo=
{
galleryInfo
}
activeKey=
{
randomOne
}
setPicList=
{
setPicList
}
isOnline=
{
isOnline
}
/>
),
key
:
randomOne
,
key
:
randomOne
,
forceRender
:
true
forceRender
:
true
}
}
...
@@ -60,7 +57,7 @@ const GalleryModal = props => {
...
@@ -60,7 +57,7 @@ const GalleryModal = props => {
const
[
activeKey
,
setActiveKey
]
=
useState
(
initialItems
[
0
].
key
)
const
[
activeKey
,
setActiveKey
]
=
useState
(
initialItems
[
0
].
key
)
const
[
items
,
setItems
]
=
useState
(
initialItems
)
const
[
items
,
setItems
]
=
useState
(
initialItems
)
const
onChange
=
newActiveKey
=>
{
const
onChange
=
(
newActiveKey
)
=>
{
setActiveKey
(
newActiveKey
)
setActiveKey
(
newActiveKey
)
}
}
const
add
=
()
=>
{
const
add
=
()
=>
{
...
@@ -71,7 +68,9 @@ const GalleryModal = props => {
...
@@ -71,7 +68,9 @@ const GalleryModal = props => {
const
newActiveKey
=
`
${
random
}
`
const
newActiveKey
=
`
${
random
}
`
newPanes
.
push
({
newPanes
.
push
({
label
:
`图
${
index
+
1
}
`
,
label
:
`图
${
index
+
1
}
`
,
children
:
<
GalleryFormItem
ossClient=
{
ossClient
}
form=
{
form
}
galleryInfo=
{
galleryInfo
}
activeKey=
{
newActiveKey
}
setPicList=
{
setPicList
}
/>,
children
:
(
<
GalleryFormItem
ossClient=
{
ossClient
}
form=
{
form
}
galleryInfo=
{
galleryInfo
}
activeKey=
{
newActiveKey
}
setPicList=
{
setPicList
}
isOnline=
{
isOnline
}
/>
),
key
:
newActiveKey
,
key
:
newActiveKey
,
forceRender
:
true
forceRender
:
true
})
})
...
@@ -82,8 +81,8 @@ const GalleryModal = props => {
...
@@ -82,8 +81,8 @@ const GalleryModal = props => {
setItems
(
newPanes
)
setItems
(
newPanes
)
setActiveKey
(
newActiveKey
)
setActiveKey
(
newActiveKey
)
}
}
const
remove
=
async
targetKey
=>
{
const
remove
=
async
(
targetKey
)
=>
{
const
tempGallery
=
picList
.
filter
(
item
=>
item
.
key
!==
targetKey
)
const
tempGallery
=
picList
.
filter
(
(
item
)
=>
item
.
key
!==
targetKey
)
await
setPicList
(
tempGallery
)
await
setPicList
(
tempGallery
)
console
.
log
(
tempGallery
)
console
.
log
(
tempGallery
)
form
.
setFieldsValue
({
gallery
:
tempGallery
})
form
.
setFieldsValue
({
gallery
:
tempGallery
})
...
@@ -95,7 +94,7 @@ const GalleryModal = props => {
...
@@ -95,7 +94,7 @@ const GalleryModal = props => {
lastIndex
=
i
-
1
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
(
newPanes
.
length
&&
newActiveKey
===
targetKey
)
{
if
(
lastIndex
>=
0
)
{
if
(
lastIndex
>=
0
)
{
newActiveKey
=
newPanes
[
lastIndex
].
key
newActiveKey
=
newPanes
[
lastIndex
].
key
...
@@ -169,6 +168,7 @@ const GalleryModal = props => {
...
@@ -169,6 +168,7 @@ const GalleryModal = props => {
form=
{
form
}
form=
{
form
}
setPicList=
{
setPicList
}
setPicList=
{
setPicList
}
activeKey=
{
newActiveKey
}
activeKey=
{
newActiveKey
}
isOnline=
{
isOnline
}
/>
/>
),
),
key
:
newActiveKey
,
key
:
newActiveKey
,
...
@@ -184,7 +184,7 @@ const GalleryModal = props => {
...
@@ -184,7 +184,7 @@ const GalleryModal = props => {
}
}
},
[
galleryInfo
])
},
[
galleryInfo
])
const
onFinish
=
async
values
=>
{
const
onFinish
=
async
(
values
)
=>
{
editor
.
restoreSelection
()
editor
.
restoreSelection
()
// setLoading(true);
// setLoading(true);
const
{
galleryTitle
,
flex
,
gallery
,
fontSize
,
theme
=
''
}
=
values
const
{
galleryTitle
,
flex
,
gallery
,
fontSize
,
theme
=
''
}
=
values
...
@@ -193,7 +193,7 @@ const GalleryModal = props => {
...
@@ -193,7 +193,7 @@ const GalleryModal = props => {
if
(
parseInt
(
flex
)
!==
2
)
{
if
(
parseInt
(
flex
)
!==
2
)
{
// 删除空白的p标签
// 删除空白的p标签
const
nodeEntries
=
SlateEditor
.
nodes
(
editor
,
{
const
nodeEntries
=
SlateEditor
.
nodes
(
editor
,
{
match
:
node
=>
{
match
:
(
node
)
=>
{
// JS syntax
// JS syntax
if
(
SlateElement
.
isElement
(
node
))
{
if
(
SlateElement
.
isElement
(
node
))
{
if
(
node
.
type
===
'paragraph'
)
{
if
(
node
.
type
===
'paragraph'
)
{
...
...
src/common/wangeditor-customer/components/galleryItem.jsx
浏览文件 @
227c5f8b
...
@@ -2,12 +2,13 @@ import { useState, useEffect, useImperativeHandle, forwardRef } from 'react'
...
@@ -2,12 +2,13 @@ import { useState, useEffect, useImperativeHandle, forwardRef } from 'react'
import
{
CloudUploadOutlined
}
from
'@ant-design/icons'
import
{
CloudUploadOutlined
}
from
'@ant-design/icons'
import
{
Form
,
Input
,
Spin
,
Upload
,
Button
}
from
'antd'
import
{
Form
,
Input
,
Spin
,
Upload
,
Button
}
from
'antd'
import
{
partSize
,
normalUploader
,
multipartUploader
}
from
'../utils/upload'
import
{
partSize
,
normalUploader
,
multipartUploader
}
from
'../utils/upload'
import
OnlineImageList
from
'./onlineImageList'
const
fileAccept
=
[
'.png'
,
'.jpg'
,
'.jpeg'
,
'.svg'
]
const
fileAccept
=
[
'.png'
,
'.jpg'
,
'.jpeg'
,
'.svg'
]
const
randomOne
=
Math
.
random
().
toString
(
16
).
substring
(
2
,
10
)
const
randomOne
=
Math
.
random
().
toString
(
16
).
substring
(
2
,
10
)
const
GalleryFormItem
=
(
props
,
ref
)
=>
{
const
GalleryFormItem
=
(
props
,
ref
)
=>
{
const
{
activeKey
,
ossClient
,
form
,
setPicList
,
galleryInfo
}
=
props
const
{
activeKey
,
ossClient
,
form
,
setPicList
,
galleryInfo
,
isOnline
=
false
}
=
props
const
[
uploading
,
setUploading
]
=
useState
(
false
)
// 文件上传状态
const
[
uploading
,
setUploading
]
=
useState
(
false
)
// 文件上传状态
const
[
progress
,
setProgress
]
=
useState
(
0
)
const
[
progress
,
setProgress
]
=
useState
(
0
)
...
@@ -35,7 +36,7 @@ const GalleryFormItem = (props, ref) => {
...
@@ -35,7 +36,7 @@ const GalleryFormItem = (props, ref) => {
if
(
galleryArr
.
length
>
0
)
{
if
(
galleryArr
.
length
>
0
)
{
const
values
=
{
[
'content'
]:
{}
}
const
values
=
{
[
'content'
]:
{}
}
galleryArr
.
forEach
(
item
=>
{
galleryArr
.
forEach
(
(
item
)
=>
{
values
[
'content'
][
item
.
key
]
=
{
values
[
'content'
][
item
.
key
]
=
{
url
:
item
.
url
,
url
:
item
.
url
,
title
:
item
.
title
,
title
:
item
.
title
,
...
@@ -44,7 +45,7 @@ const GalleryFormItem = (props, ref) => {
...
@@ -44,7 +45,7 @@ const GalleryFormItem = (props, ref) => {
})
})
form
.
setFieldsValue
({
...
values
,
gallery
:
galleryArr
})
form
.
setFieldsValue
({
...
values
,
gallery
:
galleryArr
})
const
item
=
galleryArr
.
filter
(
item
=>
item
.
key
===
activeKey
)
const
item
=
galleryArr
.
filter
(
(
item
)
=>
item
.
key
===
activeKey
)
if
(
item
.
length
>
0
)
{
if
(
item
.
length
>
0
)
{
const
itemGallery
=
item
[
0
]
const
itemGallery
=
item
[
0
]
setImgUrl
(
itemGallery
.
url
)
setImgUrl
(
itemGallery
.
url
)
...
@@ -55,7 +56,7 @@ const GalleryFormItem = (props, ref) => {
...
@@ -55,7 +56,7 @@ const GalleryFormItem = (props, ref) => {
useEffect
(()
=>
{},
[])
useEffect
(()
=>
{},
[])
const
normFile
=
e
=>
{
const
normFile
=
(
e
)
=>
{
if
(
Array
.
isArray
(
e
))
{
if
(
Array
.
isArray
(
e
))
{
return
e
return
e
}
}
...
@@ -117,7 +118,7 @@ const GalleryFormItem = (props, ref) => {
...
@@ -117,7 +118,7 @@ const GalleryFormItem = (props, ref) => {
}
}
}
}
const
changeTitleValue
=
e
=>
{
const
changeTitleValue
=
(
e
)
=>
{
const
allContent
=
form
.
getFieldValue
(
'content'
)
const
allContent
=
form
.
getFieldValue
(
'content'
)
const
newGalleryList
=
[]
const
newGalleryList
=
[]
// eslint-disable-next-line guard-for-in
// eslint-disable-next-line guard-for-in
...
@@ -127,7 +128,30 @@ const GalleryFormItem = (props, ref) => {
...
@@ -127,7 +128,30 @@ const GalleryFormItem = (props, ref) => {
setPicList
(
newGalleryList
)
setPicList
(
newGalleryList
)
form
.
setFieldsValue
({
gallery
:
newGalleryList
})
form
.
setFieldsValue
({
gallery
:
newGalleryList
})
}
}
const
changeDescValue
=
e
=>
{
const
changeDescValue
=
(
e
)
=>
{
const
allContent
=
form
.
getFieldValue
(
'content'
)
const
newGalleryList
=
[]
// eslint-disable-next-line guard-for-in
for
(
let
key
in
allContent
)
{
newGalleryList
.
push
({
...
allContent
[
key
],
key
:
key
})
}
setPicList
(
newGalleryList
)
form
.
setFieldsValue
({
gallery
:
newGalleryList
})
}
const
onOnlineImageChange
=
(
url
)
=>
{
setImgUrl
(
url
)
const
values
=
{
[
'content'
]:
{
[
activeKey
]:
{
url
:
url
,
title
:
''
,
desc
:
''
}
}
}
setImgUrl
(
url
)
form
.
setFieldsValue
({
...
values
})
const
allContent
=
form
.
getFieldValue
(
'content'
)
const
allContent
=
form
.
getFieldValue
(
'content'
)
const
newGalleryList
=
[]
const
newGalleryList
=
[]
// eslint-disable-next-line guard-for-in
// eslint-disable-next-line guard-for-in
...
@@ -140,42 +164,55 @@ const GalleryFormItem = (props, ref) => {
...
@@ -140,42 +164,55 @@ const GalleryFormItem = (props, ref) => {
return
(
return
(
<>
<>
<
Form
.
Item
{
isOnline
?
(
label=
"上传图片"
<
Form
.
Item
valuePropName=
"fileList"
label=
"在线图片"
getValueFromEvent=
{
normFile
}
valuePropName=
"fileList"
extra=
"最多可上传10张"
getValueFromEvent=
{
normFile
}
name=
{
[
'content'
,
activeKey
,
'url'
]
}
extra=
"最多可上传10张"
rules=
{
[{
required
:
true
,
message
:
'请上传画廊图片'
}]
}
>
name=
{
[
'content'
,
activeKey
,
'url'
]
}
<
Spin
spinning=
{
uploading
}
tip=
"Loading"
>
rules=
{
[{
required
:
true
,
message
:
'请选择图片'
}]
}
>
<
div
className=
"editor-dragger"
>
<
OnlineImageList
imgUrl=
{
imgUrl
}
onChange=
{
onOnlineImageChange
}
></
OnlineImageList
>
<
Upload
.
Dragger
{
...
uploadProps
}
showUploadList=
{
false
}
>
</
Form
.
Item
>
{
!
imgUrl
?
(
)
:
(
<>
<
Form
.
Item
<
div
className=
"editor-uploader-process"
>
label=
"上传图片"
<
p
className=
"ant-upload-drag-icon"
>
valuePropName=
"fileList"
<
CloudUploadOutlined
style=
{
{
fontSize
:
40
}
}
/>
getValueFromEvent=
{
normFile
}
</
p
>
extra=
"最多可上传10张"
<
p
className=
"ant-upload-text"
>
点击上传或拖拽到此处上传
</
p
>
name=
{
[
'content'
,
activeKey
,
'url'
]
}
<
p
className=
"ant-upload-hint"
>
支持上传 .png、.jpg、.jpeg、.svg格式的图片
</
p
>
rules=
{
[{
required
:
true
,
message
:
'请上传画廊图片'
}]
}
>
<
Spin
spinning=
{
uploading
}
tip=
"Loading"
>
<
div
className=
"editor-dragger"
>
<
Upload
.
Dragger
{
...
uploadProps
}
showUploadList=
{
false
}
>
{
!
imgUrl
?
(
<>
<
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
>
</
div
>
</>
)
:
(
<
div
className=
"editor-uploader-result"
>
<
div
className=
"editor-uploader-result-img"
>
<
img
src=
{
imgUrl
}
alt=
""
/>
</
div
>
<
div
className=
"editor-uploader-result-tips"
>
<
Button
size=
"small"
type=
"primary"
ghost
onClick=
{
()
=>
setImgUrl
(
null
)
}
>
替换
</
Button
>
</
div
>
</
div
>
</
div
>
</>
)
}
)
:
(
</
Upload
.
Dragger
>
<
div
className=
"editor-uploader-result"
>
</
div
>
<
div
className=
"editor-uploader-result-img"
>
</
Spin
>
<
img
src=
{
imgUrl
}
alt=
""
/>
</
Form
.
Item
>
</
div
>
)
}
<
div
className=
"editor-uploader-result-tips"
>
<
Button
size=
"small"
type=
"primary"
ghost
onClick=
{
()
=>
setImgUrl
(
null
)
}
>
替换
</
Button
>
</
div
>
</
div
>
)
}
</
Upload
.
Dragger
>
</
div
>
</
Spin
>
</
Form
.
Item
>
<
Form
.
Item
label=
"标题"
rules=
{
[{
required
:
false
,
message
:
'请输入图片标题'
}]
}
name=
{
[
'content'
,
activeKey
,
'title'
]
}
extra=
"最多输入100字"
>
<
Form
.
Item
label=
"标题"
rules=
{
[{
required
:
false
,
message
:
'请输入图片标题'
}]
}
name=
{
[
'content'
,
activeKey
,
'title'
]
}
extra=
"最多输入100字"
>
<
Input
maxLength=
{
100
}
placeholder=
""
allowClear
onChange=
{
changeTitleValue
}
/>
<
Input
maxLength=
{
100
}
placeholder=
""
allowClear
onChange=
{
changeTitleValue
}
/>
</
Form
.
Item
>
</
Form
.
Item
>
...
...
src/common/wangeditor-customer/components/image.jsx
浏览文件 @
227c5f8b
...
@@ -3,13 +3,14 @@ import { Divider, Upload, Input, Space, Button, Form, Spin, message } from 'antd
...
@@ -3,13 +3,14 @@ import { Divider, Upload, Input, Space, Button, Form, Spin, message } from 'antd
import
{
CloudUploadOutlined
}
from
'@ant-design/icons'
import
{
CloudUploadOutlined
}
from
'@ant-design/icons'
import
{
SlateTransforms
,
SlateEditor
,
SlateElement
}
from
'@wangeditor/editor'
import
{
SlateTransforms
,
SlateEditor
,
SlateElement
}
from
'@wangeditor/editor'
import
'./index.less'
import
'./index.less'
import
OnlineImageList
from
'./onlineImageList'
import
{
partSize
,
normalUploader
,
multipartUploader
}
from
'../utils/upload'
import
{
partSize
,
normalUploader
,
multipartUploader
}
from
'../utils/upload'
const
{
Dragger
}
=
Upload
const
{
Dragger
}
=
Upload
const
ImageModal
=
(
props
,
ref
)
=>
{
const
ImageModal
=
(
props
,
ref
)
=>
{
const
{
editor
,
ossClient
,
setImageVisible
,
imageInfo
,
setImageInfo
}
=
props
const
{
editor
,
ossClient
,
setImageVisible
,
imageInfo
,
setImageInfo
,
isOnline
=
false
}
=
props
const
[
imgUrl
,
setImgUrl
]
=
useState
(
''
)
const
[
imgUrl
,
setImgUrl
]
=
useState
(
''
)
const
fileAccept
=
[
'.png'
,
'.jpg'
,
'.jpeg'
,
'.svg'
]
const
fileAccept
=
[
'.png'
,
'.jpg'
,
'.jpeg'
,
'.svg'
]
...
@@ -34,7 +35,7 @@ const ImageModal = (props, ref) => {
...
@@ -34,7 +35,7 @@ const ImageModal = (props, ref) => {
}
}
},
[
imageInfo
])
},
[
imageInfo
])
const
normFile
=
e
=>
{
const
normFile
=
(
e
)
=>
{
if
(
Array
.
isArray
(
e
))
{
if
(
Array
.
isArray
(
e
))
{
return
e
return
e
}
}
...
@@ -87,7 +88,8 @@ const ImageModal = (props, ref) => {
...
@@ -87,7 +88,8 @@ const ImageModal = (props, ref) => {
setImageVisible
(
false
)
setImageVisible
(
false
)
}
}
const
onFinish
=
values
=>
{
const
onFinish
=
(
values
)
=>
{
console
.
log
(
values
)
editor
.
restoreSelection
()
editor
.
restoreSelection
()
// editor.insertNode({
// editor.insertNode({
// type: 'chapterImage',
// type: 'chapterImage',
...
@@ -98,7 +100,7 @@ const ImageModal = (props, ref) => {
...
@@ -98,7 +100,7 @@ const ImageModal = (props, ref) => {
// });
// });
const
nodeEntries
=
SlateEditor
.
nodes
(
editor
,
{
const
nodeEntries
=
SlateEditor
.
nodes
(
editor
,
{
match
:
node
=>
{
match
:
(
node
)
=>
{
// JS syntax
// JS syntax
if
(
SlateElement
.
isElement
(
node
))
{
if
(
SlateElement
.
isElement
(
node
))
{
if
(
node
.
type
===
'paragraph'
)
{
if
(
node
.
type
===
'paragraph'
)
{
...
@@ -179,42 +181,52 @@ const ImageModal = (props, ref) => {
...
@@ -179,42 +181,52 @@ const ImageModal = (props, ref) => {
clear
()
clear
()
}
}
const
onOnlineImageChange
=
(
url
)
=>
{
setImgUrl
(
url
)
form
.
setFieldsValue
({
imgUrl
:
url
})
}
return
(
return
(
<
div
>
<
div
>
<
Divider
/>
<
Divider
/>
<
div
className=
"editor-content-form"
>
<
div
className=
"editor-content-form"
>
<
Form
layout=
"vertical"
name=
"validate_other"
form=
{
form
}
onFinish=
{
onFinish
}
initialValues=
{
initValues
}
>
<
Form
layout=
"vertical"
name=
"validate_other"
form=
{
form
}
onFinish=
{
onFinish
}
initialValues=
{
initValues
}
>
<
Form
.
Item
label=
"上传图片"
valuePropName=
"fileList"
name=
"imgUrl"
getValueFromEvent=
{
normFile
}
rules=
{
[{
required
:
true
,
message
:
'请上传图片'
}]
}
>
{
isOnline
?
(
<
Spin
spinning=
{
uploading
}
tip=
{
`${progress}%`
}
>
<
Form
.
Item
label=
"在线图片"
name=
"imgUrl"
rules=
{
[{
required
:
true
,
message
:
'请选择图片'
}]
}
>
<
div
className=
"editor-dragger"
>
<
OnlineImageList
imgUrl=
{
imgUrl
}
onChange=
{
onOnlineImageChange
}
></
OnlineImageList
>
<
Dragger
{
...
uploadProps
}
showUploadList=
{
false
}
>
</
Form
.
Item
>
{
!
imgUrl
&&
(
)
:
(
<
div
className=
"editor-uploader-process"
>
<
Form
.
Item
label=
"上传图片"
valuePropName=
"fileList"
name=
"imgUrl"
getValueFromEvent=
{
normFile
}
rules=
{
[{
required
:
true
,
message
:
'请上传图片'
}]
}
>
<
p
className=
"ant-upload-drag-icon"
>
<
Spin
spinning=
{
uploading
}
tip=
{
`${progress}%`
}
>
<
CloudUploadOutlined
style=
{
{
fontSize
:
40
}
}
/>
<
div
className=
"editor-dragger"
>
</
p
>
<
Dragger
{
...
uploadProps
}
showUploadList=
{
false
}
>
<
p
className=
"ant-upload-text"
>
点击上传或拖拽到此处上传
</
p
>
{
!
imgUrl
&&
(
<
p
className=
"ant-upload-hint"
>
支持上传 .png、.jpg、.jpeg、.svg格式的图片
</
p
>
<
div
className=
"editor-uploader-process"
>
</
div
>
<
p
className=
"ant-upload-drag-icon"
>
)
}
<
CloudUploadOutlined
style=
{
{
fontSize
:
40
}
}
/>
{
imgUrl
&&
(
</
p
>
<>
<
p
className=
"ant-upload-text"
>
点击上传或拖拽到此处上传
</
p
>
<
div
className=
"editor-uploader-result"
>
<
p
className=
"ant-upload-hint"
>
支持上传 .png、.jpg、.jpeg、.svg格式的图片
</
p
>
<
div
className=
"editor-uploader-result-img"
>
<
img
src=
{
imgUrl
}
alt=
""
/>
</
div
>
<
div
className=
"editor-uploader-result-tips"
>
<
Button
size=
"small"
type=
"primary"
ghost
onClick=
{
()
=>
setImgUrl
(
null
)
}
>
替换
</
Button
>
</
div
>
</
div
>
</
div
>
</>
)
}
)
}
{
imgUrl
&&
(
</
Dragger
>
<>
</
div
>
<
div
className=
"editor-uploader-result"
>
</
Spin
>
<
div
className=
"editor-uploader-result-img"
>
</
Form
.
Item
>
<
img
src=
{
imgUrl
}
alt=
""
/>
</
div
>
<
div
className=
"editor-uploader-result-tips"
>
<
Button
size=
"small"
type=
"primary"
ghost
onClick=
{
()
=>
setImgUrl
(
null
)
}
>
替换
</
Button
>
</
div
>
</
div
>
</>
)
}
</
Dragger
>
</
div
>
</
Spin
>
</
Form
.
Item
>
)
}
<
Form
.
Item
label=
"图片标题"
name=
"imgTitle"
extra=
"最多输入100字"
>
<
Form
.
Item
label=
"图片标题"
name=
"imgTitle"
extra=
"最多输入100字"
>
<
Input
maxLength=
{
100
}
placeholder=
""
allowClear
/>
<
Input
maxLength=
{
100
}
placeholder=
""
allowClear
/>
</
Form
.
Item
>
</
Form
.
Item
>
...
...
src/common/wangeditor-customer/components/index.less
浏览文件 @
227c5f8b
...
@@ -125,4 +125,32 @@
...
@@ -125,4 +125,32 @@
display: flex;
display: flex;
justify-content: center;
justify-content: center;
}
}
}
}
\ No newline at end of file
.online-image-list {
display: flex;
background: #ffffff;
border-width: 1px;
border-style: solid;
border-color: #d9d9d9;
border-radius: 6px;
padding: 10px;
gap: 10px;
flex-wrap: wrap;
.online-image-list-item {
width: 200px;
height: 100px;
border-radius: 10px;
overflow: hidden;
box-sizing: border-box;
cursor: pointer;
img {
width: 100%;
height: 100%;
object-fit: cover;
}
&.is-active {
border: 4px solid #ab1941;
}
}
}
src/common/wangeditor-customer/components/onlineImageList.jsx
0 → 100644
浏览文件 @
227c5f8b
import
{
getChuangKitDesigns
}
from
'@/api/chuangkit'
import
{
useState
,
useEffect
}
from
'react'
import
{
useSelector
}
from
'react-redux'
export
default
function
OnlineImageList
({
imgUrl
,
onChange
})
{
const
[
list
,
setList
]
=
useState
([])
const
{
userInfo
}
=
useSelector
((
state
)
=>
state
.
user
)
useEffect
(()
=>
{
getChuangKitDesigns
({
params
:
{
time_order
:
1
,
user_flag
:
userInfo
.
id
}
}).
then
((
res
)
=>
{
setList
(
res
.
data
.
list
)
})
},
[
userInfo
])
return
(
<
div
className=
"online-image-list"
>
{
list
.
map
((
item
)
=>
{
return
(
<
div
className=
{
'online-image-list-item '
+
(
imgUrl
===
item
.
thumbUrl
?
'is-active'
:
''
)
}
key=
{
item
.
designId
}
onClick=
{
()
=>
onChange
(
item
.
thumbUrl
)
}
>
<
img
src=
{
item
.
thumbUrl
}
/>
</
div
>
)
})
}
</
div
>
)
}
src/common/wangeditor-customer/customer/Gallery.js
浏览文件 @
227c5f8b
...
@@ -2,7 +2,7 @@ import { DomEditor, SlateTransforms, SlateRange } from '@wangeditor/editor';
...
@@ -2,7 +2,7 @@ import { DomEditor, SlateTransforms, SlateRange } from '@wangeditor/editor';
class
GalleryAuto
{
class
GalleryAuto
{
constructor
()
{
constructor
()
{
this
.
title
=
'画廊'
this
.
title
=
'
离线
画廊'
this
.
iconSvg
=
`<svg width="24px" height="19px" viewBox="0 0 24 19" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
this
.
iconSvg
=
`<svg width="24px" height="19px" viewBox="0 0 24 19" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>图标</title>
<title>图标</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
...
...
src/common/wangeditor-customer/customer/GalleryOnline.js
浏览文件 @
227c5f8b
import
{
DomEditor
,
SlateRange
}
from
'@wangeditor/editor'
import
{
DomEditor
,
SlateRange
}
from
'@wangeditor/editor'
class
GalleryAuto
{
class
GalleryAuto
Online
{
constructor
()
{
constructor
()
{
this
.
title
=
'画廊'
this
.
title
=
'
在线
画廊'
this
.
iconSvg
=
`<svg width="24px" height="19px" viewBox="0 0 24 19" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
this
.
iconSvg
=
`<svg width="24px" height="19px" viewBox="0 0 24 19" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>图标</title>
<title>图标</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
...
@@ -39,7 +39,7 @@ class GalleryAuto {
...
@@ -39,7 +39,7 @@ class GalleryAuto {
// if (hasVoidElem) return true // 选中了 void 元素,禁用
// if (hasVoidElem) return true // 选中了 void 元素,禁用
// eslint-disable-next-line array-callback-return
// eslint-disable-next-line array-callback-return
const
hasPreElem
=
selectedElems
.
some
(
elem
=>
{
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
if
(
type
===
'pre'
||
type
===
'blockquote'
||
type
===
'table'
||
type
===
'table-row'
||
type
===
'table-cell'
)
return
true
...
@@ -53,15 +53,15 @@ class GalleryAuto {
...
@@ -53,15 +53,15 @@ class GalleryAuto {
if
(
this
.
isDisabled
(
editor
))
{
if
(
this
.
isDisabled
(
editor
))
{
return
return
}
}
editor
.
emit
(
'GalleryMenuClick'
)
editor
.
emit
(
'Gallery
Online
MenuClick'
)
}
}
}
}
export
default
{
export
default
{
key
:
'GalleryAuto'
,
// 定义 menu key :要保证唯一、不重复(重要)
key
:
'GalleryAuto
Online
'
,
// 定义 menu key :要保证唯一、不重复(重要)
factory
()
{
factory
()
{
return
new
GalleryAuto
()
// 把 `YourMenuClass` 替换为你菜单的 class
return
new
GalleryAuto
Online
()
// 把 `YourMenuClass` 替换为你菜单的 class
}
}
}
}
export
{
GalleryAuto
}
export
{
GalleryAuto
Online
}
src/common/wangeditor-customer/customer/Image.js
浏览文件 @
227c5f8b
...
@@ -3,7 +3,7 @@ import { DomEditor, SlateTransforms as Transforms, SlateRange } from '@wangedito
...
@@ -3,7 +3,7 @@ import { DomEditor, SlateTransforms as Transforms, SlateRange } from '@wangedito
// Extend menu
// Extend menu
class
ImageAuto
{
class
ImageAuto
{
constructor
()
{
constructor
()
{
this
.
title
=
'图片'
this
.
title
=
'
离线
图片'
this
.
iconSvg
=
`<svg width="23px" height="21px" viewBox="0 0 23 21" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
this
.
iconSvg
=
`<svg width="23px" height="21px" viewBox="0 0 23 21" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>编辑器图片样式</title>
<title>编辑器图片样式</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
...
...
src/common/wangeditor-customer/customer/ImageOnline.js
浏览文件 @
227c5f8b
import
{
DomEditor
,
SlateTransforms
as
Transforms
,
SlateRange
}
from
'@wangeditor/editor'
import
{
DomEditor
,
SlateTransforms
as
Transforms
,
SlateRange
}
from
'@wangeditor/editor'
// Extend menu
// Extend menu
class
ImageAuto
{
class
ImageAuto
Online
{
constructor
()
{
constructor
()
{
this
.
title
=
'图片'
this
.
title
=
'
在线
图片'
this
.
iconSvg
=
`<svg width="23px" height="21px" viewBox="0 0 23 21" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
this
.
iconSvg
=
`<svg width="23px" height="21px" viewBox="0 0 23 21" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>编辑器图片样式</title>
<title>编辑器图片样式</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
...
@@ -29,11 +29,11 @@ class ImageAuto {
...
@@ -29,11 +29,11 @@ class ImageAuto {
const
selectedElems
=
DomEditor
.
getSelectedElems
(
editor
)
const
selectedElems
=
DomEditor
.
getSelectedElems
(
editor
)
const
hasVoidElem
=
selectedElems
.
some
(
elem
=>
editor
.
isVoid
(
elem
))
const
hasVoidElem
=
selectedElems
.
some
(
(
elem
)
=>
editor
.
isVoid
(
elem
))
if
(
hasVoidElem
)
return
true
// 选中了 void 元素,禁用
if
(
hasVoidElem
)
return
true
// 选中了 void 元素,禁用
// eslint-disable-next-line array-callback-return
// eslint-disable-next-line array-callback-return
const
hasPreElem
=
selectedElems
.
some
(
elem
=>
{
const
hasPreElem
=
selectedElems
.
some
(
(
elem
)
=>
{
const
type
=
DomEditor
.
getNodeType
(
elem
)
const
type
=
DomEditor
.
getNodeType
(
elem
)
// 代码块 引用 表格 禁用
// 代码块 引用 表格 禁用
if
(
type
===
'pre'
||
type
===
'blockquote'
)
return
true
if
(
type
===
'pre'
||
type
===
'blockquote'
)
return
true
...
@@ -48,7 +48,7 @@ class ImageAuto {
...
@@ -48,7 +48,7 @@ class ImageAuto {
exec
(
editor
,
value
)
{
exec
(
editor
,
value
)
{
// editor.insertText(value) // value 即 this.getValue(editor) 的返回值
// editor.insertText(value) // value 即 this.getValue(editor) 的返回值
editor
.
emit
(
'ImageMenuClick'
)
editor
.
emit
(
'Image
Online
MenuClick'
)
if
(
this
.
isDisabled
(
editor
))
{
if
(
this
.
isDisabled
(
editor
))
{
return
return
...
@@ -71,30 +71,30 @@ class ImageAuto {
...
@@ -71,30 +71,30 @@ class ImageAuto {
}
}
Transforms
.
setNodes
(
editor
,
props
,
{
Transforms
.
setNodes
(
editor
,
props
,
{
match
:
n
=>
DomEditor
.
checkNodeType
(
n
,
'chapterImage'
)
match
:
(
n
)
=>
DomEditor
.
checkNodeType
(
n
,
'chapterImage'
)
})
})
}
}
}
}
class
ImageWidthCustomer100
extends
ImageAuto
{
class
ImageWidthCustomer100
extends
ImageAuto
Online
{
title
=
'100%'
// 菜单标题
title
=
'100%'
// 菜单标题
value
=
'100%'
// css width 的值
value
=
'100%'
// css width 的值
}
}
class
ImageWidthCustomer50
extends
ImageAuto
{
class
ImageWidthCustomer50
extends
ImageAuto
Online
{
title
=
'50%'
// 菜单标题
title
=
'50%'
// 菜单标题
value
=
'50%'
// css width 的值
value
=
'50%'
// css width 的值
}
}
class
ImageWidthCustomer30
extends
ImageAuto
{
class
ImageWidthCustomer30
extends
ImageAuto
Online
{
title
=
'30%'
// 菜单标题
title
=
'30%'
// 菜单标题
value
=
'30%'
// css width 的值
value
=
'30%'
// css width 的值
}
}
export
default
{
export
default
{
key
:
'ImageAuto'
,
// 定义 menu key :要保证唯一、不重复(重要)
key
:
'ImageAuto
Online
'
,
// 定义 menu key :要保证唯一、不重复(重要)
factory
()
{
factory
()
{
return
new
ImageAuto
()
// 把 `YourMenuClass` 替换为你菜单的 class
return
new
ImageAuto
Online
()
// 把 `YourMenuClass` 替换为你菜单的 class
}
}
}
}
...
@@ -119,4 +119,4 @@ const imageWidth30MenuChapterConf = {
...
@@ -119,4 +119,4 @@ const imageWidth30MenuChapterConf = {
}
}
}
}
export
{
ImageAuto
,
imageWidth100MenuChapterConf
,
imageWidth50MenuChapterConf
,
imageWidth30MenuChapterConf
}
export
{
ImageAuto
Online
,
imageWidth100MenuChapterConf
,
imageWidth50MenuChapterConf
,
imageWidth30MenuChapterConf
}
src/common/wangeditor-customer/index.jsx
浏览文件 @
227c5f8b
import
React
,
{
useState
,
useEffect
,
useRef
,
forwardRef
,
useImperativeHandle
}
from
'react'
import
{
useState
,
useEffect
,
useRef
,
forwardRef
,
useImperativeHandle
}
from
'react'
import
{
Input
,
Spin
,
message
,
Button
,
Divider
,
Space
,
Modal
,
Row
,
Drawer
}
from
'antd'
import
{
Button
,
Divider
,
Space
,
Modal
,
Drawer
}
from
'antd'
import
{
EyeOutlined
,
SaveOutlined
,
CloseOutlined
,
HistoryOutlined
}
from
'@ant-design/icons'
import
{
EyeOutlined
,
SaveOutlined
,
CloseOutlined
,
HistoryOutlined
}
from
'@ant-design/icons'
import
AliOSS
from
'ali-oss'
import
AliOSS
from
'ali-oss'
import
dayjs
from
'dayjs'
import
dayjs
from
'dayjs'
...
@@ -7,7 +7,7 @@ import './utils/iconfont'
...
@@ -7,7 +7,7 @@ import './utils/iconfont'
import
{
useSelector
,
useDispatch
}
from
'react-redux'
import
{
useSelector
,
useDispatch
}
from
'react-redux'
import
{
setPracticeRandom
}
from
'@/store/modules/editor'
import
{
setPracticeRandom
}
from
'@/store/modules/editor'
import
formulaEditor
from
'easy-formula-editor'
//
import formulaEditor from 'easy-formula-editor'
import
'./utils/jax.less'
import
'./utils/jax.less'
import
{
Boot
}
from
'@wangeditor/editor'
import
{
Boot
}
from
'@wangeditor/editor'
...
@@ -24,7 +24,9 @@ import { storageChange } from '@/utils/storage.js'
...
@@ -24,7 +24,9 @@ import { storageChange } from '@/utils/storage.js'
// import PaddingSpace from './customer/padding';
// import PaddingSpace from './customer/padding';
import
ImageAutoOnlineConf
from
'./customer/ImageOnline'
import
GalleryAutoConf
from
'./customer/Gallery'
import
GalleryAutoConf
from
'./customer/Gallery'
import
GalleryAutoOnlineConf
from
'./customer/GalleryOnline'
import
VideoAutoConf
from
'./customer/Video'
import
VideoAutoConf
from
'./customer/Video'
import
AudioAutoConf
from
'./customer/Audio'
import
AudioAutoConf
from
'./customer/Audio'
import
ChapterTitleAutoConf
from
'./customer/ChapterTitle'
import
ChapterTitleAutoConf
from
'./customer/ChapterTitle'
...
@@ -75,21 +77,12 @@ import PreviewModal from './components/preview'
...
@@ -75,21 +77,12 @@ import PreviewModal from './components/preview'
import
HistoryModal
from
'./history/'
import
HistoryModal
from
'./history/'
import
$
from
'jquery'
import
$
from
'jquery'
import
{
getInfoByChapterId
}
from
'@/pages/books/section/request'
import
{
getAliOSSSTSToken
}
from
'./utils/request'
import
{
getAliOSSSTSToken
,
getRecordInfo
}
from
'./utils/request'
import
'./index.less'
import
'./index.less'
const
jsonContent
=
[
{
type
:
'paragraph'
,
lineHeight
:
'1.5'
,
children
:
[{
text
:
''
,
fontFamily
:
'黑体'
,
fontSize
:
'16px'
}]
}
]
const
menuArr0
=
[
'重做'
,
'撤销'
]
const
menuArr0
=
[
'重做'
,
'撤销'
]
const
menuArr1
=
[
'字体样式'
,
'字号'
,
'行高'
]
const
menuArr1
=
[
'字体样式'
,
'字号'
,
'行高'
]
const
menuArr2
=
[
'
图片'
,
'
画廊'
,
'视频'
,
'音频'
,
'表格'
]
const
menuArr2
=
[
'
离线图片'
,
'在线图片'
,
'离线画廊'
,
'在线
画廊'
,
'视频'
,
'音频'
,
'表格'
]
const
menuArr3
=
[
'代码块'
,
'引用'
,
'链接'
,
'公式'
,
'章头'
,
'节头'
,
'交互练习'
,
'气泡'
,
'扩展阅读'
]
const
menuArr3
=
[
'代码块'
,
'引用'
,
'链接'
,
'公式'
,
'章头'
,
'节头'
,
'交互练习'
,
'气泡'
,
'扩展阅读'
]
const
menuArr4
=
[
'改写'
,
'扩写'
,
'缩写'
,
'总结'
]
const
menuArr4
=
[
'改写'
,
'扩写'
,
'缩写'
,
'总结'
]
const
colorList
=
[
'#ab1941'
,
'#2970f6'
,
'#2ad882'
,
'#eb3351'
]
const
colorList
=
[
'#ab1941'
,
'#2970f6'
,
'#2ad882'
,
'#eb3351'
]
...
@@ -98,7 +91,9 @@ const bookBucketName = 'zxts-book-file'
...
@@ -98,7 +91,9 @@ const bookBucketName = 'zxts-book-file'
const
module
=
{
const
module
=
{
menus
:
[
menus
:
[
// ImageAutoConf,
// ImageAutoConf,
ImageAutoOnlineConf
,
GalleryAutoConf
,
GalleryAutoConf
,
GalleryAutoOnlineConf
,
VideoAutoConf
,
VideoAutoConf
,
AudioAutoConf
,
AudioAutoConf
,
FormulaAutoConf
,
FormulaAutoConf
,
...
@@ -150,15 +145,13 @@ const tabsMenu = [
...
@@ -150,15 +145,13 @@ const tabsMenu = [
storageChange
()
storageChange
()
const
WangEditorCustomer
=
(
props
,
ref
)
=>
{
const
WangEditorCustomer
=
(
props
,
ref
)
=>
{
const
{
chapterId
,
bookId
,
contentId
,
html
,
setHtml
,
saveContent
,
gData
,
nowTitle
,
recordList
}
=
props
const
{
chapterId
,
bookId
,
contentId
,
html
,
setHtml
,
saveContent
,
gData
,
nowTitle
}
=
props
const
dispatch
=
useDispatch
()
const
dispatch
=
useDispatch
()
// 自动保存时间
// 自动保存时间
const
{
autosaveTime
}
=
useSelector
(
state
=>
state
.
editor
)
const
{
autosaveTime
}
=
useSelector
(
(
state
)
=>
state
.
editor
)
const
toolbarRef
=
useRef
()
const
toolbarRef
=
useRef
()
const
paddingSpaceRef
=
useRef
()
const
[
cId
,
setCId
]
=
useState
(
contentId
)
const
[
ossClient
,
setOssClient
]
=
useState
(
null
)
// oss 操作
const
[
ossClient
,
setOssClient
]
=
useState
(
null
)
// oss 操作
const
[
STSToken
,
setSTSToken
]
=
useState
(
null
)
// oss 过期设置
const
[
STSToken
,
setSTSToken
]
=
useState
(
null
)
// oss 过期设置
...
@@ -167,7 +160,6 @@ const WangEditorCustomer = (props, ref) => {
...
@@ -167,7 +160,6 @@ const WangEditorCustomer = (props, ref) => {
const
[
loading
,
setLoading
]
=
useState
(
true
)
const
[
loading
,
setLoading
]
=
useState
(
true
)
// editor 实例
// editor 实例
const
[
editor
,
setEditor
]
=
useState
(
null
)
const
[
editor
,
setEditor
]
=
useState
(
null
)
const
[
editorNodes
,
setEditorNodes
]
=
useState
(
null
)
const
[
content
,
setContent
]
=
useState
(
html
)
const
[
content
,
setContent
]
=
useState
(
html
)
const
saveRef
=
useRef
()
const
saveRef
=
useRef
()
...
@@ -193,11 +185,11 @@ const WangEditorCustomer = (props, ref) => {
...
@@ -193,11 +185,11 @@ const WangEditorCustomer = (props, ref) => {
const
[
priviewVisible
,
setPriviewVisible
]
=
useState
(
false
)
const
[
priviewVisible
,
setPriviewVisible
]
=
useState
(
false
)
const
[
historyVisible
,
setHistoryVisible
]
=
useState
(
false
)
//点击历史
const
[
historyVisible
,
setHistoryVisible
]
=
useState
(
false
)
//点击历史
const
[
delMoadal
,
setDelModal
]
=
useState
(
false
)
const
[
selectedRecordName
,
setSelectedRecordName
]
=
useState
(
''
)
const
[
selectionSize
,
setSelectionSize
]
=
useState
(
16
)
// 当前字号大小
const
[
selectionSize
,
setSelectionSize
]
=
useState
(
16
)
// 当前字号大小
const
[
isOnline
,
setIsOnline
]
=
useState
(
false
)
window
.
addEventListener
(
'setItemEvent'
,
function
(
e
)
{
window
.
addEventListener
(
'setItemEvent'
,
function
(
e
)
{
if
(
e
.
key
===
'chapterTitleNum'
)
{
if
(
e
.
key
===
'chapterTitleNum'
)
{
setTitleInfo
(
JSON
.
parse
(
e
.
newValue
))
setTitleInfo
(
JSON
.
parse
(
e
.
newValue
))
...
@@ -256,7 +248,7 @@ const WangEditorCustomer = (props, ref) => {
...
@@ -256,7 +248,7 @@ const WangEditorCustomer = (props, ref) => {
}
}
})
})
const
listenNodeStyle
=
path
=>
{
const
listenNodeStyle
=
(
path
)
=>
{
const
children
=
editor
.
children
const
children
=
editor
.
children
let
node
=
null
let
node
=
null
if
(
path
[
1
]
===
0
)
{
if
(
path
[
1
]
===
0
)
{
...
@@ -384,7 +376,9 @@ const WangEditorCustomer = (props, ref) => {
...
@@ -384,7 +376,9 @@ const WangEditorCustomer = (props, ref) => {
index
:
30
,
index
:
30
,
keys
:
[
keys
:
[
'ImageAuto'
,
'ImageAuto'
,
'ImageAutoOnline'
,
'GalleryAuto'
,
'GalleryAuto'
,
'GalleryAutoOnline'
,
'VideoAuto'
,
'VideoAuto'
,
'AudioAuto'
,
'AudioAuto'
,
'CustomerLink'
,
'CustomerLink'
,
...
@@ -537,7 +531,9 @@ const WangEditorCustomer = (props, ref) => {
...
@@ -537,7 +531,9 @@ const WangEditorCustomer = (props, ref) => {
const
itemBox4
=
document
.
createElement
(
'div'
)
const
itemBox4
=
document
.
createElement
(
'div'
)
itemBox4
.
setAttribute
(
'class'
,
'custom-bar-box media'
)
itemBox4
.
setAttribute
(
'class'
,
'custom-bar-box media'
)
toolbarElement
.
insertBefore
(
itemBox4
,
allChildren
[
8
])
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
[
13
]).
detach
())
$
(
allChildren
[
8
]).
append
(
$
(
allChildren
[
13
]).
detach
())
...
@@ -609,18 +605,18 @@ const WangEditorCustomer = (props, ref) => {
...
@@ -609,18 +605,18 @@ const WangEditorCustomer = (props, ref) => {
setLoading
(
false
)
setLoading
(
false
)
},
350
)
},
350
)
}
}
editorConfig
.
onCreated
=
editor
=>
{
editorConfig
.
onCreated
=
(
editor
)
=>
{
setLoading
(
true
)
setLoading
(
true
)
}
}
editorConfig
.
onFocus
=
editor
=>
{
editorConfig
.
onFocus
=
(
editor
)
=>
{
clearTimeout
(
saveRef
.
current
)
clearTimeout
(
saveRef
.
current
)
}
}
editorConfig
.
onBlur
=
editor
=>
{
editorConfig
.
onBlur
=
(
editor
)
=>
{
// 失焦保存
// 失焦保存
setHtml
(
editor
.
getHtml
())
setHtml
(
editor
.
getHtml
())
setContent
(
editor
.
getHtml
())
setContent
(
editor
.
getHtml
())
}
}
editorConfig
.
onChange
=
editor
=>
{
editorConfig
.
onChange
=
(
editor
)
=>
{
setHtml
(
editor
.
getHtml
())
setHtml
(
editor
.
getHtml
())
setContent
(
editor
.
getHtml
())
setContent
(
editor
.
getHtml
())
}
}
...
@@ -635,6 +631,13 @@ const WangEditorCustomer = (props, ref) => {
...
@@ -635,6 +631,13 @@ const WangEditorCustomer = (props, ref) => {
// 图片上传
// 图片上传
editor
.
on
(
'ImageMenuClick'
,
()
=>
{
editor
.
on
(
'ImageMenuClick'
,
()
=>
{
console
.
log
(
'ImageMenuClick'
,
'----'
)
console
.
log
(
'ImageMenuClick'
,
'----'
)
setIsOnline
(
false
)
setImageVisible
(
true
)
})
// 在线图片
editor
.
on
(
'ImageOnlineMenuClick'
,
()
=>
{
console
.
log
(
'ImageOnlineMenuClick'
,
'----'
)
setIsOnline
(
true
)
setImageVisible
(
true
)
setImageVisible
(
true
)
})
})
// 画廊上传
// 画廊上传
...
@@ -644,6 +647,17 @@ const WangEditorCustomer = (props, ref) => {
...
@@ -644,6 +647,17 @@ const WangEditorCustomer = (props, ref) => {
}
}
console
.
log
(
'GalleryMenuClick'
,
'----'
)
console
.
log
(
'GalleryMenuClick'
,
'----'
)
// galleryRef.current.setVisible(true);
// galleryRef.current.setVisible(true);
setIsOnline
(
false
)
setGalleryVisible
(
true
)
})
// 在线画廊
editor
.
on
(
'GalleryOnlineMenuClick'
,
()
=>
{
if
(
editor
.
selection
)
{
listenNodeStyle
(
editor
.
selection
.
anchor
.
path
)
}
console
.
log
(
'GalleryOnlineMenuClick'
,
'----'
)
// galleryRef.current.setVisible(true);
setIsOnline
(
true
)
setGalleryVisible
(
true
)
setGalleryVisible
(
true
)
})
})
// 视频上传
// 视频上传
...
@@ -681,7 +695,7 @@ const WangEditorCustomer = (props, ref) => {
...
@@ -681,7 +695,7 @@ const WangEditorCustomer = (props, ref) => {
console
.
log
(
'ImageEditorClick'
,
'----'
)
console
.
log
(
'ImageEditorClick'
,
'----'
)
const
nodeEntries
=
SlateEditor
.
nodes
(
editor
,
{
const
nodeEntries
=
SlateEditor
.
nodes
(
editor
,
{
match
:
node
=>
{
match
:
(
node
)
=>
{
// JS syntax
// JS syntax
if
(
SlateElement
.
isElement
(
node
))
{
if
(
SlateElement
.
isElement
(
node
))
{
if
(
node
.
type
===
'paragraph'
)
{
if
(
node
.
type
===
'paragraph'
)
{
...
@@ -707,6 +721,7 @@ const WangEditorCustomer = (props, ref) => {
...
@@ -707,6 +721,7 @@ const WangEditorCustomer = (props, ref) => {
info
.
path
=
path
info
.
path
=
path
}
}
setImageInfo
(
info
)
setImageInfo
(
info
)
setIsOnline
(
false
)
setImageVisible
(
true
)
setImageVisible
(
true
)
})
})
// 气泡
// 气泡
...
@@ -799,7 +814,7 @@ const WangEditorCustomer = (props, ref) => {
...
@@ -799,7 +814,7 @@ const WangEditorCustomer = (props, ref) => {
}
}
},
[
gData
,
editor
])
},
[
gData
,
editor
])
const
tabKeyChange
=
key
=>
{
const
tabKeyChange
=
(
key
)
=>
{
if
(
key
===
'text'
)
{
if
(
key
===
'text'
)
{
toolSetttingReplace
()
toolSetttingReplace
()
}
}
...
@@ -883,15 +898,15 @@ const WangEditorCustomer = (props, ref) => {
...
@@ -883,15 +898,15 @@ const WangEditorCustomer = (props, ref) => {
})()
})()
},
[])
},
[])
const
setColor
=
type
=>
{
const
setColor
=
(
type
)
=>
{
const
headers
=
document
.
querySelectorAll
(
`.w-e-scroll .chapter-item-header`
)
const
headers
=
document
.
querySelectorAll
(
`.w-e-scroll .chapter-item-header`
)
const
sections
=
document
.
querySelectorAll
(
`.w-e-scroll .chapter-item-section`
)
const
sections
=
document
.
querySelectorAll
(
`.w-e-scroll .chapter-item-section`
)
headers
.
forEach
(
item
=>
{
headers
.
forEach
(
(
item
)
=>
{
const
node
=
DomEditor
.
toSlateNode
(
editor
,
item
)
const
node
=
DomEditor
.
toSlateNode
(
editor
,
item
)
const
path
=
DomEditor
.
findPath
(
editor
,
node
)
const
path
=
DomEditor
.
findPath
(
editor
,
node
)
SlateTransforms
.
setNodes
(
editor
,
{
...
node
,
textColor
:
'#ffffff'
,
bgColor
:
colorList
[
type
-
1
]
},
{
at
:
path
})
SlateTransforms
.
setNodes
(
editor
,
{
...
node
,
textColor
:
'#ffffff'
,
bgColor
:
colorList
[
type
-
1
]
},
{
at
:
path
})
})
})
sections
.
forEach
(
item
=>
{
sections
.
forEach
(
(
item
)
=>
{
const
node
=
DomEditor
.
toSlateNode
(
editor
,
item
)
const
node
=
DomEditor
.
toSlateNode
(
editor
,
item
)
const
path
=
DomEditor
.
findPath
(
editor
,
node
)
const
path
=
DomEditor
.
findPath
(
editor
,
node
)
SlateTransforms
.
setNodes
(
editor
,
{
...
node
,
textColor
:
'#ffffff'
,
bgColor
:
colorList
[
type
-
1
]
},
{
at
:
path
})
SlateTransforms
.
setNodes
(
editor
,
{
...
node
,
textColor
:
'#ffffff'
,
bgColor
:
colorList
[
type
-
1
]
},
{
at
:
path
})
...
@@ -948,7 +963,7 @@ const WangEditorCustomer = (props, ref) => {
...
@@ -948,7 +963,7 @@ const WangEditorCustomer = (props, ref) => {
<
div
className=
"tabs"
>
<
div
className=
"tabs"
>
{
tabsMenu
&&
{
tabsMenu
&&
tabsMenu
.
length
&&
tabsMenu
.
length
&&
tabsMenu
.
map
(
item
=>
{
tabsMenu
.
map
(
(
item
)
=>
{
return
(
return
(
<
div
className=
{
`tabs-item ${item.key === tabKey ? 'active' : ''}`
}
key=
{
item
.
key
}
onClick=
{
()
=>
tabKeyChange
(
item
.
key
)
}
>
<
div
className=
{
`tabs-item ${item.key === tabKey ? 'active' : ''}`
}
key=
{
item
.
key
}
onClick=
{
()
=>
tabKeyChange
(
item
.
key
)
}
>
{
item
.
title
}
{
item
.
title
}
...
@@ -1028,6 +1043,7 @@ const WangEditorCustomer = (props, ref) => {
...
@@ -1028,6 +1043,7 @@ const WangEditorCustomer = (props, ref) => {
onCancel=
{
()
=>
setImageVisible
(
false
)
}
>
onCancel=
{
()
=>
setImageVisible
(
false
)
}
>
<
ImageModal
<
ImageModal
ref=
{
imageRef
}
ref=
{
imageRef
}
isOnline=
{
isOnline
}
editor=
{
editor
}
editor=
{
editor
}
ossClient=
{
ossClient
}
ossClient=
{
ossClient
}
STSToken=
{
STSToken
}
STSToken=
{
STSToken
}
...
@@ -1053,6 +1069,7 @@ const WangEditorCustomer = (props, ref) => {
...
@@ -1053,6 +1069,7 @@ const WangEditorCustomer = (props, ref) => {
width=
"800px"
>
width=
"800px"
>
<
GalleryModal
<
GalleryModal
ref=
{
galleryRef
}
ref=
{
galleryRef
}
isOnline=
{
isOnline
}
editor=
{
editor
}
editor=
{
editor
}
ossClient=
{
ossClient
}
ossClient=
{
ossClient
}
STSToken=
{
STSToken
}
STSToken=
{
STSToken
}
...
...
src/common/wangeditor-customer/node/gallery.js
浏览文件 @
227c5f8b
import
{
import
{
DomEditor
,
SlateTransforms
}
from
'@wangeditor/editor'
DomEditor
,
import
{
h
}
from
'snabbdom'
SlateTransforms
,
import
iconGallery
from
'@/assets/images/editor/icon_gallery_editor.png'
SlateEditor
,
import
iconClose
from
'@/assets/images/icon_chapter_close.png'
SlateElement
,
import
{
findNodeWithParent
}
from
'../utils/setting'
SlateNode
,
}
from
'@wangeditor/editor'
;
import
{
h
}
from
'snabbdom'
;
import
$
from
'jquery'
;
import
iconGallery
from
'@/assets/images/editor/icon_gallery_editor.png'
;
import
iconClose
from
'@/assets/images/icon_chapter_close.png'
;
import
{
findNodeWithParent
}
from
'../utils/setting'
;
const
withGalleryNode
=
(
editor
)
=>
{
const
withGalleryNode
=
(
editor
)
=>
{
const
{
is
Inline
,
isVoid
,
normalizeNode
}
=
editor
;
const
{
is
Void
,
normalizeNode
}
=
editor
const
newEditor
=
editor
;
const
newEditor
=
editor
// newEditor.isInline = (elem) => {
// newEditor.isInline = (elem) => {
// const type = DomEditor.getNodeType(elem);
// const type = DomEditor.getNodeType(elem);
...
@@ -22,25 +15,25 @@ const withGalleryNode = (editor) => {
...
@@ -22,25 +15,25 @@ const withGalleryNode = (editor) => {
// };
// };
newEditor
.
isVoid
=
(
elem
)
=>
{
newEditor
.
isVoid
=
(
elem
)
=>
{
const
type
=
DomEditor
.
getNodeType
(
elem
)
;
const
type
=
DomEditor
.
getNodeType
(
elem
)
if
(
type
===
'chapterGallery'
)
return
true
;
// 设置为 void
if
(
type
===
'chapterGallery'
)
return
true
// 设置为 void
return
isVoid
(
elem
)
;
return
isVoid
(
elem
)
}
;
}
// 重新 normalize
// 重新 normalize
newEditor
.
normalizeNode
=
([
node
,
path
])
=>
{
newEditor
.
normalizeNode
=
([
node
,
path
])
=>
{
const
type
=
DomEditor
.
getNodeType
(
node
)
;
const
type
=
DomEditor
.
getNodeType
(
node
)
if
(
type
!==
'chapterGallery'
)
{
if
(
type
!==
'chapterGallery'
)
{
// 未命中 chapterGallery ,执行默认的 normalizeNode
// 未命中 chapterGallery ,执行默认的 normalizeNode
return
normalizeNode
([
node
,
path
])
;
return
normalizeNode
([
node
,
path
])
}
}
// editor 顶级 node
// editor 顶级 node
const
topLevelNodes
=
newEditor
.
children
||
[]
;
const
topLevelNodes
=
newEditor
.
children
||
[]
// 后面必须跟一个 p header blockquote(否则后面无法继续输入文字)
// 后面必须跟一个 p header blockquote(否则后面无法继续输入文字)
const
nextNode
=
topLevelNodes
[
path
[
0
]
+
1
]
||
{}
;
const
nextNode
=
topLevelNodes
[
path
[
0
]
+
1
]
||
{}
const
nextNodeType
=
DomEditor
.
getNodeType
(
nextNode
)
;
const
nextNodeType
=
DomEditor
.
getNodeType
(
nextNode
)
if
(
if
(
nextNodeType
!==
'paragraph'
&&
nextNodeType
!==
'paragraph'
&&
nextNodeType
!==
'blockquote'
&&
nextNodeType
!==
'blockquote'
&&
...
@@ -53,45 +46,45 @@ const withGalleryNode = (editor) => {
...
@@ -53,45 +46,45 @@ const withGalleryNode = (editor) => {
!
nextNodeType
.
startsWith
(
'header'
)
!
nextNodeType
.
startsWith
(
'header'
)
)
{
)
{
// link-card node 后面不是 p 或 header ,则插入一个空 p
// link-card node 后面不是 p 或 header ,则插入一个空 p
const
p
=
{
type
:
'paragraph'
,
children
:
[{
text
:
''
}]
}
;
const
p
=
{
type
:
'paragraph'
,
children
:
[{
text
:
''
}]
}
const
insertPath
=
[
path
[
0
]
+
1
]
;
const
insertPath
=
[
path
[
0
]
+
1
]
SlateTransforms
.
insertNodes
(
newEditor
,
p
,
{
SlateTransforms
.
insertNodes
(
newEditor
,
p
,
{
at
:
insertPath
,
// 在 link-card 后面插入
at
:
insertPath
// 在 link-card 后面插入
})
;
})
}
}
}
;
}
return
newEditor
;
// 返回 newEditor ,重要!!!
return
newEditor
// 返回 newEditor ,重要!!!
}
;
}
// 在编辑器中渲染新元素
// 在编辑器中渲染新元素
// 定义 renderElem 函数
// 定义 renderElem 函数
const
renderGallery
=
(
elem
,
children
,
editor
)
=>
{
const
renderGallery
=
(
elem
,
children
,
editor
)
=>
{
// 获取“附件”的数据,参考上文 myResume 数据结构
// 获取“附件”的数据,参考上文 myResume 数据结构
const
{
title
=
''
,
galleryList
=
''
,
random
=
''
,
theme
=
''
,
flex
=
1
,
callback
}
=
elem
;
const
{
title
=
''
,
galleryList
=
''
,
random
=
''
,
theme
=
''
,
flex
=
1
,
callback
}
=
elem
const
galleryArr
=
JSON
.
parse
(
decodeURI
(
galleryList
))
;
const
galleryArr
=
JSON
.
parse
(
decodeURI
(
galleryList
))
// console.log('renderGallery', galleryArr);
// console.log('renderGallery', galleryArr);
const
titleNode
=
h
(
const
titleNode
=
h
(
'h6'
,
'h6'
,
{
{
props
:
{
props
:
{
className
:
'chapter-gallery-title'
,
className
:
'chapter-gallery-title'
},
},
style
:
{
style
:
{
textAlign
:
'center'
,
textAlign
:
'center'
,
color
:
'#333'
,
color
:
'#333'
,
fontSize
:
'14px'
,
fontSize
:
'14px'
,
margin
:
'0px'
,
margin
:
'0px'
,
lineHeight
:
'20px'
,
lineHeight
:
'20px'
}
,
}
},
},
[
title
]
,
[
title
]
)
;
)
const
element
=
[]
;
const
element
=
[]
let
galleryListNode
=
[]
;
let
galleryListNode
=
[]
if
(
parseInt
(
flex
)
!==
2
)
{
if
(
parseInt
(
flex
)
!==
2
)
{
for
(
let
i
=
0
;
i
<
galleryArr
.
length
;
i
++
)
{
for
(
let
i
=
0
;
i
<
galleryArr
.
length
;
i
++
)
{
if
(
parseInt
(
flex
)
===
1
)
{
if
(
parseInt
(
flex
)
===
1
)
{
...
@@ -100,25 +93,25 @@ const renderGallery = (elem, children, editor) => {
...
@@ -100,25 +93,25 @@ const renderGallery = (elem, children, editor) => {
'div'
,
'div'
,
{
{
props
:
{
props
:
{
className
:
'chapter-gallery-item'
,
className
:
'chapter-gallery-item'
},
},
style
:
{
style
:
{
flex
:
`0 0 50%`
,
flex
:
`0 0 50%`
,
padding
:
'10px'
,
padding
:
'10px'
,
boxSizing
:
'border-box'
,
boxSizing
:
'border-box'
}
,
}
},
},
[
[
h
(
'img'
,
{
h
(
'img'
,
{
props
:
{
src
:
galleryArr
[
i
].
url
},
props
:
{
src
:
galleryArr
[
i
].
url
},
style
:
{
height
:
'auto'
,
width
:
'100%'
}
,
style
:
{
height
:
'auto'
,
width
:
'100%'
}
}),
}),
h
(
'p'
,
{
props
:
{},
style
:
{
textAlign
:
'center'
}
},
[
galleryArr
[
i
].
title
])
,
h
(
'p'
,
{
props
:
{},
style
:
{
textAlign
:
'center'
}
},
[
galleryArr
[
i
].
title
])
]
,
]
)
;
)
galleryListNode
.
push
(
titleNode
)
;
galleryListNode
.
push
(
titleNode
)
}
else
{
}
else
{
break
;
break
}
}
}
else
if
(
parseInt
(
flex
)
===
3
)
{
}
else
if
(
parseInt
(
flex
)
===
3
)
{
if
(
i
===
0
)
{
if
(
i
===
0
)
{
...
@@ -126,48 +119,48 @@ const renderGallery = (elem, children, editor) => {
...
@@ -126,48 +119,48 @@ const renderGallery = (elem, children, editor) => {
'div'
,
'div'
,
{
{
props
:
{
props
:
{
className
:
'chapter-gallery-item one'
,
className
:
'chapter-gallery-item one'
},
},
style
:
{
style
:
{
flex
:
`1`
,
flex
:
`1`
,
padding
:
'10px'
,
padding
:
'10px'
,
boxSizing
:
'border-box'
,
boxSizing
:
'border-box'
}
,
}
},
},
[
[
h
(
'img'
,
{
h
(
'img'
,
{
props
:
{
src
:
galleryArr
[
i
].
url
},
props
:
{
src
:
galleryArr
[
i
].
url
},
style
:
{
height
:
'auto'
,
width
:
'100%'
}
,
style
:
{
height
:
'auto'
,
width
:
'100%'
}
}),
}),
h
(
'p'
,
{
props
:
{},
style
:
{
textAlign
:
'center'
}
},
[
galleryArr
[
i
].
title
])
,
h
(
'p'
,
{
props
:
{},
style
:
{
textAlign
:
'center'
}
},
[
galleryArr
[
i
].
title
])
]
,
]
)
;
)
galleryListNode
.
push
(
titleNode
)
;
galleryListNode
.
push
(
titleNode
)
}
else
{
}
else
{
break
;
break
}
}
}
else
{
}
else
{
const
titleNode
=
h
(
const
titleNode
=
h
(
'div'
,
'div'
,
{
{
props
:
{
props
:
{
className
:
'chapter-gallery-item'
,
className
:
'chapter-gallery-item'
},
},
style
:
{
style
:
{
flex
:
`0 0 50%`
,
flex
:
`0 0 50%`
,
padding
:
'10px'
,
padding
:
'10px'
,
boxSizing
:
'border-box'
,
boxSizing
:
'border-box'
}
,
}
},
},
[
[
h
(
'img'
,
{
h
(
'img'
,
{
props
:
{
src
:
galleryArr
[
i
].
url
},
props
:
{
src
:
galleryArr
[
i
].
url
},
style
:
{
height
:
'auto'
,
width
:
'100%'
}
,
style
:
{
height
:
'auto'
,
width
:
'100%'
}
}),
}),
h
(
'p'
,
{
props
:
{},
style
:
{
textAlign
:
'center'
}
},
[
galleryArr
[
i
].
title
])
,
h
(
'p'
,
{
props
:
{},
style
:
{
textAlign
:
'center'
}
},
[
galleryArr
[
i
].
title
])
]
,
]
)
;
)
galleryListNode
.
push
(
titleNode
)
;
galleryListNode
.
push
(
titleNode
)
}
}
}
}
...
@@ -176,16 +169,16 @@ const renderGallery = (elem, children, editor) => {
...
@@ -176,16 +169,16 @@ const renderGallery = (elem, children, editor) => {
'div'
,
'div'
,
{
{
props
:
{
props
:
{
className
:
'chapter-gallery-box'
,
className
:
'chapter-gallery-box'
},
},
style
:
{
display
:
'flex'
,
justifyContent
:
'flex-start'
,
flexFlow
:
'row wrap'
}
,
style
:
{
display
:
'flex'
,
justifyContent
:
'flex-start'
,
flexFlow
:
'row wrap'
}
},
},
[...
galleryListNode
]
,
[...
galleryListNode
]
)
;
)
element
.
push
(
galleryNode
)
;
element
.
push
(
galleryNode
)
}
}
if
(
title
)
{
if
(
title
)
{
element
.
push
(
titleNode
)
;
element
.
push
(
titleNode
)
}
}
// 附件元素 vnode
// 附件元素 vnode
...
@@ -197,21 +190,20 @@ const renderGallery = (elem, children, editor) => {
...
@@ -197,21 +190,20 @@ const renderGallery = (elem, children, editor) => {
props
:
{
props
:
{
// HTML 属性,驼峰式写法
// HTML 属性,驼峰式写法
contentEditable
:
false
,
contentEditable
:
false
,
className
:
'chapter-gallery-container'
,
className
:
'chapter-gallery-container'
},
},
style
:
{
padding
:
'10px 0px'
,
style
:
{
padding
:
'10px 0px'
,
position
:
'relative'
},
// style ,驼峰式写法
position
:
'relative'
},
// style ,驼峰式写法
dataset
:
{
dataset
:
{
galleryList
:
galleryList
,
galleryList
:
galleryList
,
random
:
random
,
random
:
random
,
flex
:
flex
,
flex
:
flex
,
theme
,
theme
,
title
,
title
},
},
on
:
{
on
:
{
click
(
ev
)
{
click
(
ev
)
{
ev
.
stopPropagation
()
;
ev
.
stopPropagation
()
ev
.
preventDefault
()
;
ev
.
preventDefault
()
localStorage
.
setItem
(
localStorage
.
setItem
(
'galleryNum'
,
'galleryNum'
,
...
@@ -220,55 +212,58 @@ const renderGallery = (elem, children, editor) => {
...
@@ -220,55 +212,58 @@ const renderGallery = (elem, children, editor) => {
random
:
random
,
random
:
random
,
i
:
Math
.
random
(),
i
:
Math
.
random
(),
galleryList
:
galleryList
,
galleryList
:
galleryList
,
flex
:
flex
,
flex
:
flex
})
,
})
)
;
)
callback
&&
callback
(
title
,
random
,
galleryList
,
flex
,);
callback
&&
callback
(
title
,
random
,
galleryList
,
flex
)
}
,
}
}
,
}
},
},
// 子节点
// 子节点
[...
element
,
h
(
[
'span'
,
...
element
,
{
h
(
props
:
{
'span'
,
contentEditable
:
false
,
{
className
:
`chapter-close`
,
props
:
{
},
contentEditable
:
false
,
style
:
{
className
:
`chapter-close`
position
:
'absolute'
,
},
top
:
'10px'
,
style
:
{
right
:
'15px'
,
position
:
'absolute'
,
display
:
'inline'
,
top
:
'10px'
,
width
:
'18px'
,
right
:
'15px'
,
height
:
'18px'
,
display
:
'inline'
,
},
width
:
'18px'
,
on
:
{
height
:
'18px'
async
click
(
ev
)
{
},
ev
.
stopPropagation
();
on
:
{
ev
.
preventDefault
();
async
click
(
ev
)
{
ev
.
stopPropagation
()
ev
.
preventDefault
()
try
{
try
{
const
path
=
findNodeWithParent
(
editor
.
children
,
'chapterGallery'
,
'random'
,
random
);
const
path
=
findNodeWithParent
(
editor
.
children
,
'chapterGallery'
,
'random'
,
random
)
console
.
log
(
editor
.
children
,
path
);
console
.
log
(
editor
.
children
,
path
)
SlateTransforms
.
removeNodes
(
editor
,
{
at
:
path
.
reverse
()
});
SlateTransforms
.
removeNodes
(
editor
,
{
at
:
path
.
reverse
()
})
}
catch
(
e
)
{
}
catch
(
e
)
{
console
.
log
(
e
);
console
.
log
(
e
)
}
}
}
}
,
}
},
},
},
[
[
h
(
'img'
,
{
h
(
'img'
,
{
props
:
{
src
:
iconClose
,
width
:
18
,
height
:
18
},
props
:
{
src
:
iconClose
,
width
:
18
,
height
:
18
},
style
:
{
cursor
:
'pointer'
}
style
:
{
cursor
:
'pointer'
},
})
}),
]
],
)
),],
]
)
;
)
if
(
parseInt
(
flex
)
!==
2
)
{
if
(
parseInt
(
flex
)
!==
2
)
{
return
attachVnode
;
return
attachVnode
}
else
{
}
else
{
const
span
=
h
(
const
span
=
h
(
'div'
,
'div'
,
...
@@ -277,12 +272,12 @@ const renderGallery = (elem, children, editor) => {
...
@@ -277,12 +272,12 @@ const renderGallery = (elem, children, editor) => {
dataset
:
{
dataset
:
{
galleryList
:
galleryList
,
galleryList
:
galleryList
,
random
:
random
,
random
:
random
,
flex
:
flex
,
flex
:
flex
},
},
on
:
{
on
:
{
click
(
ev
)
{
click
(
ev
)
{
ev
.
stopPropagation
()
;
ev
.
stopPropagation
()
ev
.
preventDefault
()
;
ev
.
preventDefault
()
localStorage
.
setItem
(
localStorage
.
setItem
(
'galleryNum'
,
'galleryNum'
,
...
@@ -291,47 +286,47 @@ const renderGallery = (elem, children, editor) => {
...
@@ -291,47 +286,47 @@ const renderGallery = (elem, children, editor) => {
random
:
random
,
random
:
random
,
i
:
Math
.
random
(),
i
:
Math
.
random
(),
galleryList
:
galleryList
,
galleryList
:
galleryList
,
flex
:
flex
,
flex
:
flex
})
,
})
)
;
)
callback
&&
callback
(
title
,
random
,
galleryList
,
flex
)
;
callback
&&
callback
(
title
,
random
,
galleryList
,
flex
)
}
,
}
}
,
}
},
},
[
h
(
'img'
,
{
props
:
{
src
:
iconGallery
},
style
:
{
width
:
'18px'
,
height
:
'18px'
}
})]
,
[
h
(
'img'
,
{
props
:
{
src
:
iconGallery
},
style
:
{
width
:
'18px'
,
height
:
'18px'
}
})]
)
;
)
return
span
;
return
span
}
}
}
;
}
const
renderElemConf
=
{
const
renderElemConf
=
{
type
:
'chapterGallery'
,
type
:
'chapterGallery'
,
renderElem
:
renderGallery
,
renderElem
:
renderGallery
}
;
}
// 把新元素转换为 HTML
// 把新元素转换为 HTML
const
chapterGalleryToHtml
=
(
elem
,
childrenHtml
)
=>
{
const
chapterGalleryToHtml
=
(
elem
)
=>
{
// console.log('chapterGalleryToHtml', elem);
// console.log('chapterGalleryToHtml', elem);
// 获取附件元素的数据
// 获取附件元素的数据
const
{
title
=
''
,
galleryList
=
[],
random
=
''
,
theme
=
''
,
flex
=
1
}
=
elem
;
const
{
title
=
''
,
galleryList
=
[],
random
=
''
,
theme
=
''
,
flex
=
1
}
=
elem
const
galleryArr
=
JSON
.
parse
(
decodeURI
(
galleryList
))
;
const
galleryArr
=
JSON
.
parse
(
decodeURI
(
galleryList
))
let
str
=
''
;
let
str
=
''
if
(
galleryArr
.
length
>
0
)
{
if
(
galleryArr
.
length
>
0
)
{
for
(
let
i
=
0
;
i
<
galleryArr
.
length
;
i
++
)
{
for
(
let
i
=
0
;
i
<
galleryArr
.
length
;
i
++
)
{
if
(
parseInt
(
flex
)
===
3
)
{
if
(
parseInt
(
flex
)
===
3
)
{
if
(
i
>
0
)
{
if
(
i
>
0
)
{
break
;
break
}
else
{
}
else
{
str
=
`<div class="chapter-gallery-item one" style="flex: 1; padding: 10px; box-sizing: border-box; align-items: center;"><img src="
${
galleryArr
[
i
].
url
}
" alt="
${
galleryArr
[
i
].
title
}
" style="height: auto; width: 100%;" /><p>
${
galleryArr
[
i
].
title
}
</p></div>`
;
str
=
`<div class="chapter-gallery-item one" style="flex: 1; padding: 10px; box-sizing: border-box; align-items: center;"><img src="
${
galleryArr
[
i
].
url
}
" alt="
${
galleryArr
[
i
].
title
}
" style="height: auto; width: 100%;" /><p>
${
galleryArr
[
i
].
title
}
</p></div>`
}
}
}
else
if
(
parseInt
(
flex
)
===
1
)
{
}
else
if
(
parseInt
(
flex
)
===
1
)
{
if
(
i
>
1
)
{
if
(
i
>
1
)
{
break
;
break
}
else
{
}
else
{
str
+=
`<div class="chapter-gallery-item" style="flex: 0 0 50%; padding: 10px; box-sizing: border-box; align-items: center;"><img src="
${
galleryArr
[
i
].
url
}
" alt="
${
galleryArr
[
i
].
title
}
" style="height: auto; width: 100%;" /><p>
${
galleryArr
[
i
].
title
}
</p></div>`
;
str
+=
`<div class="chapter-gallery-item" style="flex: 0 0 50%; padding: 10px; box-sizing: border-box; align-items: center;"><img src="
${
galleryArr
[
i
].
url
}
" alt="
${
galleryArr
[
i
].
title
}
" style="height: auto; width: 100%;" /><p>
${
galleryArr
[
i
].
title
}
</p></div>`
}
}
}
else
if
(
parseInt
(
flex
)
===
4
)
{
}
else
if
(
parseInt
(
flex
)
===
4
)
{
str
+=
`<div class="chapter-gallery-item" style="flex: 0 0 50%; padding: 10px; box-sizing: border-box; align-items: center;"><img src="
${
galleryArr
[
i
].
url
}
" alt="
${
galleryArr
[
i
].
title
}
" style="height: auto; width: 100%;" /><p>
${
galleryArr
[
i
].
title
}
</p></div>`
;
str
+=
`<div class="chapter-gallery-item" style="flex: 0 0 50%; padding: 10px; box-sizing: border-box; align-items: center;"><img src="
${
galleryArr
[
i
].
url
}
" alt="
${
galleryArr
[
i
].
title
}
" style="height: auto; width: 100%;" /><p>
${
galleryArr
[
i
].
title
}
</p></div>`
}
}
}
}
}
}
...
@@ -349,28 +344,28 @@ const chapterGalleryToHtml = (elem, childrenHtml) => {
...
@@ -349,28 +344,28 @@ const chapterGalleryToHtml = (elem, childrenHtml) => {
>
>
<div class="chapter-gallery-box" style="display: flex; justify-content: flex-start; flex-flow: row wrap">
${
str
}
</div>
<div class="chapter-gallery-box" style="display: flex; justify-content: flex-start; flex-flow: row wrap">
${
str
}
</div>
<h6 class="chapter-gallery-title" style="text-align: center; color: #333; font-size: 14px; margin: 0px; line-height: 20px">
${
title
}
</h6>
<h6 class="chapter-gallery-title" style="text-align: center; color: #333; font-size: 14px; margin: 0px; line-height: 20px">
${
title
}
</h6>
</div>`
;
</div>`
if
(
parseInt
(
flex
)
===
2
)
{
if
(
parseInt
(
flex
)
===
2
)
{
return
`<div class="chapter-gallery-container chapter-gallery-inline" data-w-e-type="chapterGallery" data-galleryList="
${
galleryList
}
" data-random="
${
random
}
" data-title="
${
title
}
" data-flex="
${
flex
}
"><img src="
${
iconGallery
}
" style="width: 14px; height: 14px; " /></div>`
;
return
`<div class="chapter-gallery-container chapter-gallery-inline" data-w-e-type="chapterGallery" data-galleryList="
${
galleryList
}
" data-random="
${
random
}
" data-title="
${
title
}
" data-flex="
${
flex
}
"><img src="
${
iconGallery
}
" style="width: 14px; height: 14px; " /></div>`
}
else
{
}
else
{
return
html
;
return
html
}
}
}
;
}
const
chapterGalleryElemToHtmlConf
=
{
const
chapterGalleryElemToHtmlConf
=
{
type
:
'chapterGallery'
,
// 新元素的 type ,重要!!!
type
:
'chapterGallery'
,
// 新元素的 type ,重要!!!
elemToHtml
:
chapterGalleryToHtml
,
elemToHtml
:
chapterGalleryToHtml
}
;
}
// 解析新元素 HTML 到编辑器
// 解析新元素 HTML 到编辑器
const
parseGalleryHtml
=
(
domElem
,
children
,
editor
)
=>
{
const
parseGalleryHtml
=
(
domElem
)
=>
{
// console.log('parseGalleryHtml', domElem);
// console.log('parseGalleryHtml', domElem);
// 从 DOM element 中获取“附件”的信息
// 从 DOM element 中获取“附件”的信息
const
galleryList
=
domElem
.
getAttribute
(
'data-galleryList'
)
||
''
;
const
galleryList
=
domElem
.
getAttribute
(
'data-galleryList'
)
||
''
const
title
=
domElem
.
getAttribute
(
'data-title'
)
||
''
;
const
title
=
domElem
.
getAttribute
(
'data-title'
)
||
''
const
random
=
domElem
.
getAttribute
(
'data-random'
)
||
''
;
const
random
=
domElem
.
getAttribute
(
'data-random'
)
||
''
const
flex
=
domElem
.
getAttribute
(
'data-flex'
)
||
1
;
const
flex
=
domElem
.
getAttribute
(
'data-flex'
)
||
1
const
theme
=
domElem
.
getAttribute
(
'data-theme'
)
||
''
;
const
theme
=
domElem
.
getAttribute
(
'data-theme'
)
||
''
// 生成“附件”元素(按照此前约定的数据结构)
// 生成“附件”元素(按照此前约定的数据结构)
const
myResume
=
{
const
myResume
=
{
...
@@ -380,22 +375,22 @@ const parseGalleryHtml = (domElem, children, editor) => {
...
@@ -380,22 +375,22 @@ const parseGalleryHtml = (domElem, children, editor) => {
flex
,
flex
,
theme
,
theme
,
galleryList
:
galleryList
,
galleryList
:
galleryList
,
children
:
[{
text
:
''
}]
,
// void node 必须有 children ,其中有一个空字符串,重要!!!
children
:
[{
text
:
''
}]
// void node 必须有 children ,其中有一个空字符串,重要!!!
}
;
}
return
myResume
;
return
myResume
}
;
}
const
parseGalleryConf
=
{
const
parseGalleryConf
=
{
selector
:
'div[data-w-e-type="chapterGallery"]'
,
// CSS 选择器,匹配特定的 HTML 标签
selector
:
'div[data-w-e-type="chapterGallery"]'
,
// CSS 选择器,匹配特定的 HTML 标签
parseElemHtml
:
parseGalleryHtml
,
parseElemHtml
:
parseGalleryHtml
}
;
}
const
chapterGalleryModule
=
{
const
chapterGalleryModule
=
{
editorPlugin
:
withGalleryNode
,
editorPlugin
:
withGalleryNode
,
renderElems
:
[
renderElemConf
],
renderElems
:
[
renderElemConf
],
elemsToHtml
:
[
chapterGalleryElemToHtmlConf
],
elemsToHtml
:
[
chapterGalleryElemToHtmlConf
],
parseElemsHtml
:
[
parseGalleryConf
]
,
parseElemsHtml
:
[
parseGalleryConf
]
}
;
}
export
default
chapterGalleryModule
;
export
default
chapterGalleryModule
export
{
withGalleryNode
,
renderElemConf
,
chapterGalleryElemToHtmlConf
,
parseGalleryConf
}
;
export
{
withGalleryNode
,
renderElemConf
,
chapterGalleryElemToHtmlConf
,
parseGalleryConf
}
src/common/wangeditor-customer/node/galleryInline.js
浏览文件 @
227c5f8b
import
{
import
{
DomEditor
,
SlateTransforms
}
from
'@wangeditor/editor'
DomEditor
,
import
{
h
}
from
'snabbdom'
SlateTransforms
,
SlateEditor
,
SlateElement
,
SlateNode
,
}
from
'@wangeditor/editor'
;
import
{
h
}
from
'snabbdom'
;
import
$
from
'jquery'
;
import
iconGalleryInline
from
'@/assets/images/editor/icon_gallery_editor.png'
;
const
withGalleryInlineNode
=
(
editor
)
=>
{
const
withGalleryInlineNode
=
(
editor
)
=>
{
const
{
isInline
,
isVoid
,
normalizeNode
}
=
editor
;
const
{
isInline
,
isVoid
,
normalizeNode
}
=
editor
const
newEditor
=
editor
;
const
newEditor
=
editor
newEditor
.
isInline
=
(
elem
)
=>
{
newEditor
.
isInline
=
(
elem
)
=>
{
const
type
=
DomEditor
.
getNodeType
(
elem
)
;
const
type
=
DomEditor
.
getNodeType
(
elem
)
if
(
type
===
'chapterGalleryInline'
)
return
true
;
// 设置为 inline
if
(
type
===
'chapterGalleryInline'
)
return
true
// 设置为 inline
return
isInline
(
elem
)
;
return
isInline
(
elem
)
}
;
}
newEditor
.
isVoid
=
(
elem
)
=>
{
newEditor
.
isVoid
=
(
elem
)
=>
{
const
type
=
DomEditor
.
getNodeType
(
elem
)
;
const
type
=
DomEditor
.
getNodeType
(
elem
)
if
(
type
===
'chapterGalleryInline'
)
return
true
;
// 设置为 void
if
(
type
===
'chapterGalleryInline'
)
return
true
// 设置为 void
return
isVoid
(
elem
)
;
return
isVoid
(
elem
)
}
;
}
// 重新 normalize
// 重新 normalize
newEditor
.
normalizeNode
=
([
node
,
path
])
=>
{
newEditor
.
normalizeNode
=
([
node
,
path
])
=>
{
const
type
=
DomEditor
.
getNodeType
(
node
)
;
const
type
=
DomEditor
.
getNodeType
(
node
)
if
(
type
!==
'chapterGalleryInline'
)
{
if
(
type
!==
'chapterGalleryInline'
)
{
// 未命中 chapterGalleryInline ,执行默认的 normalizeNode
// 未命中 chapterGalleryInline ,执行默认的 normalizeNode
return
normalizeNode
([
node
,
path
])
;
return
normalizeNode
([
node
,
path
])
}
}
const
topLevelNodes
=
newEditor
.
children
||
[]
;
const
topLevelNodes
=
newEditor
.
children
||
[]
const
nextNode
=
topLevelNodes
[
path
[
0
]]
||
{}
;
const
nextNode
=
topLevelNodes
[
path
[
0
]]
||
{}
if
(
nextNode
.
type
===
'paragraph'
)
{
if
(
nextNode
.
type
===
'paragraph'
)
{
const
lastChild
=
nextNode
.
children
[
nextNode
.
children
.
length
-
1
];
const
lastChild
=
nextNode
.
children
[
nextNode
.
children
.
length
-
1
]
if
(
lastChild
.
type
===
'chapterExpandReadSimple'
)
{
if
(
lastChild
.
type
===
'chapterExpandReadSimple'
)
{
SlateTransforms
.
insertNodes
(
newEditor
,
{
type
:
'span'
,
children
:
[{
text
:
''
}]
},
{
SlateTransforms
.
insertNodes
(
at
:
path
,
// 在 link-card 后面插入
newEditor
,
});
{
type
:
'span'
,
children
:
[{
text
:
''
}]
},
{
at
:
path
// 在 link-card 后面插入
}
)
}
}
}
}
}
;
}
return
newEditor
;
// 返回 newEditor ,重要!!!
return
newEditor
// 返回 newEditor ,重要!!!
}
;
}
// 在编辑器中渲染新元素
// 在编辑器中渲染新元素
// 定义 renderElem 函数
// 定义 renderElem 函数
const
renderGalleryInline
=
(
elem
,
children
,
editor
)
=>
{
const
renderGalleryInline
=
(
elem
,
children
,
editor
)
=>
{
// 获取“附件”的数据,参考上文 myResume 数据结构
// 获取“附件”的数据,参考上文 myResume 数据结构
const
{
const
{
title
=
''
,
galleryList
=
''
,
random
=
''
,
flex
=
1
,
theme
=
'#ab1941'
,
fontsize
=
18
,
callback
}
=
elem
title
=
''
,
galleryList
=
''
,
const
str
=
`<svg class="svg-icon" style="color:
${
theme
}
; fill: currentColor; overflow: hidden; width:
${
fontsize
}
px; height:
${
fontsize
}
px;" aria-hidden="true"><use xlink:href="#icon-hualangshangchuan"></use></svg>`
random
=
''
,
flex
=
1
,
theme
=
'#ab1941'
,
fontsize
=
18
,
callback
,
}
=
elem
;
const
str
=
`<svg class="svg-icon" style="color:
${
theme
}
; fill: currentColor; overflow: hidden; width:
${
fontsize
}
px; height:
${
fontsize
}
px;" aria-hidden="true"><use xlink:href="#icon-hualangshangchuan"></use></svg>`
;
const
span
=
h
(
const
span
=
h
(
'span'
,
'span'
,
...
@@ -72,7 +60,7 @@ const renderGalleryInline = (elem, children, editor) => {
...
@@ -72,7 +60,7 @@ const renderGalleryInline = (elem, children, editor) => {
width
:
`
${
fontsize
}
px`
,
width
:
`
${
fontsize
}
px`
,
height
:
`
${
fontsize
}
px`
,
height
:
`
${
fontsize
}
px`
,
alignItems
:
'center'
,
alignItems
:
'center'
,
display
:
'inline-flex'
,
display
:
'inline-flex'
},
},
dataset
:
{
dataset
:
{
galleryList
:
galleryList
,
galleryList
:
galleryList
,
...
@@ -80,12 +68,12 @@ const renderGalleryInline = (elem, children, editor) => {
...
@@ -80,12 +68,12 @@ const renderGalleryInline = (elem, children, editor) => {
flex
:
flex
,
flex
:
flex
,
theme
,
theme
,
title
,
title
,
fontsize
,
fontsize
},
},
on
:
{
on
:
{
click
(
ev
)
{
click
(
ev
)
{
ev
.
stopPropagation
()
;
ev
.
stopPropagation
()
ev
.
preventDefault
()
;
ev
.
preventDefault
()
localStorage
.
setItem
(
localStorage
.
setItem
(
'galleryNum'
,
'galleryNum'
,
...
@@ -96,11 +84,11 @@ const renderGalleryInline = (elem, children, editor) => {
...
@@ -96,11 +84,11 @@ const renderGalleryInline = (elem, children, editor) => {
galleryList
:
galleryList
,
galleryList
:
galleryList
,
flex
:
flex
,
flex
:
flex
,
theme
,
theme
,
fontsize
,
fontsize
})
,
})
)
;
)
}
,
}
}
,
}
},
},
[
[
h
(
'span'
,
{
h
(
'span'
,
{
...
@@ -108,49 +96,42 @@ const renderGalleryInline = (elem, children, editor) => {
...
@@ -108,49 +96,42 @@ const renderGalleryInline = (elem, children, editor) => {
style
:
{
style
:
{
width
:
`
${
fontsize
}
px`
,
width
:
`
${
fontsize
}
px`
,
height
:
`
${
fontsize
}
px`
,
height
:
`
${
fontsize
}
px`
,
marginLeft
:
'2px'
,
marginLeft
:
'2px'
}
,
}
}),
}),
h
(
'span'
,
{},
[
''
])
h
(
'span'
,
{},
[
''
])
]
,
]
)
;
)
return
span
;
return
span
}
;
}
const
renderElemConf
=
{
const
renderElemConf
=
{
type
:
'chapterGalleryInline'
,
type
:
'chapterGalleryInline'
,
renderElem
:
renderGalleryInline
,
renderElem
:
renderGalleryInline
}
;
}
// 把新元素转换为 HTML
// 把新元素转换为 HTML
const
chapterGalleryInlineToHtml
=
(
elem
,
childrenHtml
)
=>
{
const
chapterGalleryInlineToHtml
=
(
elem
,
childrenHtml
)
=>
{
// console.log('chapterGalleryInlineToHtml', elem);
// console.log('chapterGalleryInlineToHtml', elem);
// 获取附件元素的数据
// 获取附件元素的数据
const
{
const
{
title
=
''
,
galleryList
=
[],
random
=
''
,
theme
=
'#ab1941'
,
flex
=
1
,
fontsize
=
18
}
=
elem
title
=
''
,
const
str
=
`<svg class="svg-icon" style="color:
${
theme
}
; fill: currentColor; overflow: hidden; width:
${
fontsize
}
px; height:
${
fontsize
}
px;" aria-hidden="true"><use xlink:href="#icon-hualangshangchuan"></use></svg>`
galleryList
=
[],
random
=
''
,
return
`<span class="chapter-gallery-container chapter-gallery-inline" data-w-e-type="chapterGalleryInline" data-galleryList="
${
galleryList
}
" data-random="
${
random
}
" data-title="
${
title
}
" data-theme="
${
theme
}
" data-fontsize="
${
fontsize
}
" data-flex="
${
flex
}
" style="cursor: pointer; display: inline-flex; align-items: center; width:
${
fontsize
}
px; height:
${
fontsize
}
px; margin: 0 2px;">
${
str
}
</span>`
theme
=
'#ab1941'
,
}
flex
=
1
,
fontsize
=
18
,
}
=
elem
;
const
str
=
`<svg class="svg-icon" style="color:
${
theme
}
; fill: currentColor; overflow: hidden; width:
${
fontsize
}
px; height:
${
fontsize
}
px;" aria-hidden="true"><use xlink:href="#icon-hualangshangchuan"></use></svg>`
;
return
`<span class="chapter-gallery-container chapter-gallery-inline" data-w-e-type="chapterGalleryInline" data-galleryList="
${
galleryList
}
" data-random="
${
random
}
" data-title="
${
title
}
" data-theme="
${
theme
}
" data-fontsize="
${
fontsize
}
" data-flex="
${
flex
}
" style="cursor: pointer; display: inline-flex; align-items: center; width:
${
fontsize
}
px; height:
${
fontsize
}
px; margin: 0 2px;">
${
str
}
</span>`
;
};
const
chapterGalleryInlineElemToHtmlConf
=
{
const
chapterGalleryInlineElemToHtmlConf
=
{
type
:
'chapterGalleryInline'
,
// 新元素的 type ,重要!!!
type
:
'chapterGalleryInline'
,
// 新元素的 type ,重要!!!
elemToHtml
:
chapterGalleryInlineToHtml
,
elemToHtml
:
chapterGalleryInlineToHtml
}
;
}
// 解析新元素 HTML 到编辑器
// 解析新元素 HTML 到编辑器
const
parseGalleryInlineHtml
=
(
domElem
,
children
,
editor
)
=>
{
const
parseGalleryInlineHtml
=
(
domElem
,
children
,
editor
)
=>
{
// 从 DOM element 中获取“附件”的信息
// 从 DOM element 中获取“附件”的信息
const
galleryList
=
domElem
.
getAttribute
(
'data-galleryList'
)
||
''
;
const
galleryList
=
domElem
.
getAttribute
(
'data-galleryList'
)
||
''
const
title
=
domElem
.
getAttribute
(
'data-title'
)
||
''
;
const
title
=
domElem
.
getAttribute
(
'data-title'
)
||
''
const
random
=
domElem
.
getAttribute
(
'data-random'
)
||
''
;
const
random
=
domElem
.
getAttribute
(
'data-random'
)
||
''
const
flex
=
domElem
.
getAttribute
(
'data-flex'
)
||
1
;
const
flex
=
domElem
.
getAttribute
(
'data-flex'
)
||
1
const
theme
=
domElem
.
getAttribute
(
'data-theme'
)
||
'#ab1941'
;
const
theme
=
domElem
.
getAttribute
(
'data-theme'
)
||
'#ab1941'
const
fontsize
=
domElem
.
getAttribute
(
'data-fontsize'
)
||
18
;
const
fontsize
=
domElem
.
getAttribute
(
'data-fontsize'
)
||
18
// 生成“附件”元素(按照此前约定的数据结构)
// 生成“附件”元素(按照此前约定的数据结构)
const
myResume
=
{
const
myResume
=
{
...
@@ -161,27 +142,22 @@ const parseGalleryInlineHtml = (domElem, children, editor) => {
...
@@ -161,27 +142,22 @@ const parseGalleryInlineHtml = (domElem, children, editor) => {
theme
,
theme
,
fontsize
,
fontsize
,
galleryList
:
galleryList
,
galleryList
:
galleryList
,
children
:
[{
text
:
''
}]
,
// void node 必须有 children ,其中有一个空字符串,重要!!!
children
:
[{
text
:
''
}]
// void node 必须有 children ,其中有一个空字符串,重要!!!
}
;
}
return
myResume
;
return
myResume
}
;
}
const
parseGalleryInlineConf
=
{
const
parseGalleryInlineConf
=
{
selector
:
'span[data-w-e-type="chapterGalleryInline"]'
,
// CSS 选择器,匹配特定的 HTML 标签
selector
:
'span[data-w-e-type="chapterGalleryInline"]'
,
// CSS 选择器,匹配特定的 HTML 标签
parseElemHtml
:
parseGalleryInlineHtml
,
parseElemHtml
:
parseGalleryInlineHtml
}
;
}
const
chapterGalleryInlineModule
=
{
const
chapterGalleryInlineModule
=
{
editorPlugin
:
withGalleryInlineNode
,
editorPlugin
:
withGalleryInlineNode
,
renderElems
:
[
renderElemConf
],
renderElems
:
[
renderElemConf
],
elemsToHtml
:
[
chapterGalleryInlineElemToHtmlConf
],
elemsToHtml
:
[
chapterGalleryInlineElemToHtmlConf
],
parseElemsHtml
:
[
parseGalleryInlineConf
],
parseElemsHtml
:
[
parseGalleryInlineConf
]
};
}
export
default
chapterGalleryInlineModule
;
export
default
chapterGalleryInlineModule
export
{
export
{
withGalleryInlineNode
,
renderElemConf
,
chapterGalleryInlineElemToHtmlConf
,
parseGalleryInlineConf
}
withGalleryInlineNode
,
renderElemConf
,
chapterGalleryInlineElemToHtmlConf
,
parseGalleryInlineConf
,
};
src/common/wangeditor-customer/node/image.jsx
浏览文件 @
227c5f8b
import
{
DomEditor
,
SlateTransforms
}
from
'@wangeditor/editor'
;
import
{
DomEditor
,
SlateTransforms
}
from
'@wangeditor/editor'
import
{
h
}
from
'snabbdom'
;
import
{
h
}
from
'snabbdom'
import
{
throttle
}
from
'lodash-es'
;
import
{
throttle
}
from
'lodash-es'
import
$
from
'jquery'
;
import
$
from
'jquery'
import
{
getStyleValue
}
from
'@/utils/common'
;
import
{
getStyleValue
}
from
'@/utils/common'
import
ImageAutoConf
,
{
import
ImageAutoConf
,
{
imageWidth100MenuChapterConf
,
imageWidth50MenuChapterConf
,
imageWidth30MenuChapterConf
}
from
'../customer/Image'
imageWidth100MenuChapterConf
,
imageWidth50MenuChapterConf
,
imageWidth30MenuChapterConf
,
}
from
'../customer/Image'
;
const
withImageNode
=
(
editor
)
=>
{
const
withImageNode
=
(
editor
)
=>
{
const
{
isInline
,
isVoid
,
normalizeNode
}
=
editor
;
const
{
isInline
,
isVoid
,
normalizeNode
}
=
editor
const
newEditor
=
editor
;
const
newEditor
=
editor
newEditor
.
isInline
=
(
elem
)
=>
{
newEditor
.
isInline
=
(
elem
)
=>
{
const
type
=
DomEditor
.
getNodeType
(
elem
)
;
const
type
=
DomEditor
.
getNodeType
(
elem
)
if
(
type
===
'chapterImage'
)
return
true
;
// 设置为 inline
if
(
type
===
'chapterImage'
)
return
true
// 设置为 inline
return
isInline
(
elem
)
;
return
isInline
(
elem
)
}
;
}
newEditor
.
isVoid
=
(
elem
)
=>
{
newEditor
.
isVoid
=
(
elem
)
=>
{
const
type
=
DomEditor
.
getNodeType
(
elem
)
;
const
type
=
DomEditor
.
getNodeType
(
elem
)
if
(
type
===
'chapterImage'
)
return
true
;
// 设置为 void
if
(
type
===
'chapterImage'
)
return
true
// 设置为 void
return
isVoid
(
elem
)
;
return
isVoid
(
elem
)
}
;
}
// 重新 normalize
// 重新 normalize
newEditor
.
normalizeNode
=
([
node
,
path
])
=>
{
newEditor
.
normalizeNode
=
([
node
,
path
])
=>
{
const
type
=
DomEditor
.
getNodeType
(
node
)
;
const
type
=
DomEditor
.
getNodeType
(
node
)
if
(
type
!==
'chapterImage'
)
{
if
(
type
!==
'chapterImage'
)
{
// 未命中 chapterImage ,执行默认的 normalizeNode
// 未命中 chapterImage ,执行默认的 normalizeNode
return
normalizeNode
([
node
,
path
])
;
return
normalizeNode
([
node
,
path
])
}
}
// editor 顶级 node
// editor 顶级 node
const
topLevelNodes
=
newEditor
.
children
||
[]
;
const
topLevelNodes
=
newEditor
.
children
||
[]
// 后面必须跟一个 p header blockquote(否则后面无法继续输入文字)
// 后面必须跟一个 p header blockquote(否则后面无法继续输入文字)
const
nextNode
=
topLevelNodes
[
path
[
0
]
+
1
]
||
{};
const
nextNode
=
topLevelNodes
[
path
[
0
]
+
1
]
||
{}
const
nextNodeType
=
DomEditor
.
getNodeType
(
nextNode
);
const
nextNodeType
=
DomEditor
.
getNodeType
(
nextNode
)
if
(
if
(
nextNodeType
!==
'paragraph'
&&
nextNodeType
!==
'blockquote'
&&
!
nextNodeType
.
startsWith
(
'header'
))
{
nextNodeType
!==
'paragraph'
&&
nextNodeType
!==
'blockquote'
&&
!
nextNodeType
.
startsWith
(
'header'
)
)
{
// link-card node 后面不是 p 或 header ,则插入一个空 p
// link-card node 后面不是 p 或 header ,则插入一个空 p
const
p
=
{
type
:
'paragraph'
,
children
:
[{
text
:
''
}]
}
;
const
p
=
{
type
:
'paragraph'
,
children
:
[{
text
:
''
}]
}
const
insertPath
=
[
path
[
0
]
+
1
]
;
const
insertPath
=
[
path
[
0
]
+
1
]
SlateTransforms
.
insertNodes
(
newEditor
,
p
,
{
SlateTransforms
.
insertNodes
(
newEditor
,
p
,
{
at
:
insertPath
,
// 在 link-card 后面插入
at
:
insertPath
// 在 link-card 后面插入
})
;
})
}
}
}
;
}
return
newEditor
;
// 返回 newEditor ,重要!!!
return
newEditor
// 返回 newEditor ,重要!!!
}
;
}
function
genContainerId
(
editor
,
elemNode
)
{
function
genContainerId
(
editor
,
elemNode
)
{
const
{
id
}
=
DomEditor
.
findKey
(
editor
,
elemNode
)
;
// node 唯一 id
const
{
id
}
=
DomEditor
.
findKey
(
editor
,
elemNode
)
// node 唯一 id
return
`w-e-image-container-
${
id
}
`
;
return
`w-e-image-container-
${
id
}
`
}
}
const
renderImageContainer
=
(
editor
,
elemNode
,
imageVnode
,
imageInfo
)
=>
{
const
renderImageContainer
=
(
editor
,
elemNode
,
imageVnode
,
imageInfo
)
=>
{
const
{
width
,
height
}
=
imageInfo
;
const
{
width
,
height
}
=
imageInfo
const
style
=
{}
;
const
style
=
{}
if
(
width
)
style
.
width
=
width
;
if
(
width
)
style
.
width
=
width
if
(
height
)
style
.
height
=
height
;
if
(
height
)
style
.
height
=
height
const
containerId
=
genContainerId
(
editor
,
elemNode
)
;
const
containerId
=
genContainerId
(
editor
,
elemNode
)
return
h
(
'div'
,
{
return
h
(
'div'
,
{
props
:
{
id
:
containerId
,
className
:
'w-e-image-container'
,
innerHTML
:
imageVnode
},
props
:
{
id
:
containerId
,
className
:
'w-e-image-container'
,
innerHTML
:
imageVnode
},
style
:
style
,
style
:
style
})
;
})
}
;
}
/**
/**
* 选中状态下,渲染 image container(渲染拖拽容器,修改图片尺寸)
* 选中状态下,渲染 image container(渲染拖拽容器,修改图片尺寸)
*/
*/
function
renderResizeContainer
(
editor
,
elemNode
,
imageVnode
,
imageInfo
)
{
function
renderResizeContainer
(
editor
,
elemNode
,
imageVnode
,
imageInfo
)
{
const
$body
=
$
(
'body'
)
;
const
$body
=
$
(
'body'
)
const
containerId
=
genContainerId
(
editor
,
elemNode
)
;
const
containerId
=
genContainerId
(
editor
,
elemNode
)
const
{
width
,
height
}
=
imageInfo
;
const
{
width
,
height
}
=
imageInfo
let
originalX
=
0
;
let
originalX
=
0
let
originalWith
=
0
;
let
originalWith
=
0
let
originalHeight
=
0
;
let
originalHeight
=
0
let
revers
=
false
;
// 是否反转。如向右拖拽 right-top 需增加宽度(非反转),但向右拖拽 left-top 则需要减少宽度(反转)
let
revers
=
false
// 是否反转。如向右拖拽 right-top 需增加宽度(非反转),但向右拖拽 left-top 则需要减少宽度(反转)
let
$container
=
null
;
let
$container
=
null
function
getContainerElem
()
{
function
getContainerElem
()
{
const
$container
=
$
(
`#
${
containerId
}
`
)
;
const
$container
=
$
(
`#
${
containerId
}
`
)
if
(
$container
.
length
===
0
)
throw
new
Error
(
'Cannot find image container elem'
)
;
if
(
$container
.
length
===
0
)
throw
new
Error
(
'Cannot find image container elem'
)
return
$container
;
return
$container
}
}
/**
/**
* 初始化。监听事件,记录原始数据
* 初始化。监听事件,记录原始数据
*/
*/
function
init
(
clientX
)
{
function
init
(
clientX
)
{
$container
=
getContainerElem
()
;
$container
=
getContainerElem
()
// 记录当前 x 坐标值
// 记录当前 x 坐标值
originalX
=
clientX
;
originalX
=
clientX
// 记录 img 原始宽高
// 记录 img 原始宽高
const
$img
=
$container
.
find
(
'img'
)
;
const
$img
=
$container
.
find
(
'img'
)
if
(
$img
.
length
===
0
)
throw
new
Error
(
'Cannot find image elem'
)
;
if
(
$img
.
length
===
0
)
throw
new
Error
(
'Cannot find image elem'
)
originalWith
=
$img
.
width
()
;
originalWith
=
$img
.
width
()
originalHeight
=
$img
.
height
()
;
originalHeight
=
$img
.
height
()
// 监听 mousemove
// 监听 mousemove
$body
.
on
(
'mousemove'
,
onMousemove
)
;
$body
.
on
(
'mousemove'
,
onMousemove
)
// 监听 mouseup
// 监听 mouseup
$body
.
on
(
'mouseup'
,
onMouseup
)
;
$body
.
on
(
'mouseup'
,
onMouseup
)
// 隐藏 hoverbar
// 隐藏 hoverbar
const
hoverbar
=
DomEditor
.
getHoverbar
(
editor
)
;
const
hoverbar
=
DomEditor
.
getHoverbar
(
editor
)
if
(
hoverbar
)
hoverbar
.
hideAndClean
()
;
if
(
hoverbar
)
hoverbar
.
hideAndClean
()
}
}
// mouseover callback (节流)
// mouseover callback (节流)
const
onMousemove
=
throttle
((
e
)
=>
{
const
onMousemove
=
throttle
((
e
)
=>
{
e
.
preventDefault
()
;
e
.
preventDefault
()
const
{
clientX
}
=
e
;
const
{
clientX
}
=
e
const
gap
=
revers
?
originalX
-
clientX
:
clientX
-
originalX
;
// 考虑是否反转
const
gap
=
revers
?
originalX
-
clientX
:
clientX
-
originalX
// 考虑是否反转
const
newWidth
=
originalWith
+
gap
;
const
newWidth
=
originalWith
+
gap
const
newHeight
=
originalHeight
*
(
newWidth
/
originalWith
)
;
// 根据 width ,按比例计算 height
const
newHeight
=
originalHeight
*
(
newWidth
/
originalWith
)
// 根据 width ,按比例计算 height
// 实时修改 img 宽高 -【注意】这里只修改 DOM ,mouseup 时再统一不修改 node
// 实时修改 img 宽高 -【注意】这里只修改 DOM ,mouseup 时再统一不修改 node
if
(
$container
===
null
)
return
;
if
(
$container
===
null
)
return
if
(
newWidth
<=
15
||
newHeight
<=
15
)
return
;
// 最小就是 15px
if
(
newWidth
<=
15
||
newHeight
<=
15
)
return
// 最小就是 15px
$container
.
css
(
'width'
,
`
${
newWidth
}
px`
)
;
$container
.
css
(
'width'
,
`
${
newWidth
}
px`
)
$container
.
css
(
'height'
,
`
${
newHeight
}
px`
)
;
$container
.
css
(
'height'
,
`
${
newHeight
}
px`
)
},
100
)
;
},
100
)
function
onMouseup
(
e
)
{
function
onMouseup
(
e
)
{
// 取消监听 mousemove
// 取消监听 mousemove
$body
.
off
(
'mousemove'
,
onMousemove
)
;
$body
.
off
(
'mousemove'
,
onMousemove
)
if
(
$container
===
null
)
return
;
if
(
$container
===
null
)
return
const
newWidth
=
$container
.
width
().
toFixed
(
2
)
;
const
newWidth
=
$container
.
width
().
toFixed
(
2
)
const
newHeight
=
$container
.
height
().
toFixed
(
2
)
;
const
newHeight
=
$container
.
height
().
toFixed
(
2
)
// 修改 node
// 修改 node
const
props
=
{
const
props
=
{
style
:
{
style
:
{
...
elemNode
.
style
,
...
elemNode
.
style
,
width
:
`
${
newWidth
}
px`
,
width
:
`
${
newWidth
}
px`
,
height
:
`
${
newHeight
}
px`
,
height
:
`
${
newHeight
}
px`
}
,
}
}
;
}
Transforms
.
setNodes
(
editor
,
props
,
{
at
:
DomEditor
.
findPath
(
editor
,
elemNode
)
})
;
Transforms
.
setNodes
(
editor
,
props
,
{
at
:
DomEditor
.
findPath
(
editor
,
elemNode
)
})
// 取消监听 mouseup
// 取消监听 mouseup
$body
.
off
(
'mouseup'
,
onMouseup
)
;
$body
.
off
(
'mouseup'
,
onMouseup
)
}
}
const
style
=
{}
;
const
style
=
{}
if
(
width
)
style
.
width
=
width
;
if
(
width
)
style
.
width
=
width
if
(
height
)
style
.
height
=
height
;
if
(
height
)
style
.
height
=
height
// style.boxShadow = '0 0 0 1px #B4D5FF' // 自定义 selected 样式,因为有拖拽触手
// style.boxShadow = '0 0 0 1px #B4D5FF' // 自定义 selected 样式,因为有拖拽触手
const
normalStyle
=
const
normalStyle
=
'width: 7px; height: 7px; background-color: #4290f7; position: absolute; cursor: nwse-resize; z-index: 12;'
'width: 7px; height: 7px; background-color: #4290f7; position: absolute; cursor: nwse-resize; z-index: 12;'
;
const
strDom
=
`
${
imageVnode
}
<div className='w-e-image-dragger left-top' style="
${
normalStyle
}
left: 0; top: 0;"></div>
const
strDom
=
`
${
imageVnode
}
<div className='w-e-image-dragger left-top' style="
${
normalStyle
}
left: 0; top: 0;"></div>
<div className='w-e-image-dragger right-top' style="
${
normalStyle
}
right: 0; top: 0;"></div>
<div className='w-e-image-dragger right-top' style="
${
normalStyle
}
right: 0; top: 0;"></div>
<div className='w-e-image-dragger left-bottom' style="
${
normalStyle
}
left: 0; bottom: 0;"></div>
<div className='w-e-image-dragger left-bottom' style="
${
normalStyle
}
left: 0; bottom: 0;"></div>
<div className='w-e-image-dragger right-bottom' style="
${
normalStyle
}
right: 0; bottom: 0;"></div>`
;
<div className='w-e-image-dragger right-bottom' style="
${
normalStyle
}
right: 0; bottom: 0;"></div>`
return
h
(
return
h
(
'div'
,
'div'
,
{
{
props
:
{
props
:
{
id
:
containerId
,
id
:
containerId
,
className
:
'w-e-image-container w-e-selected-image-container'
,
className
:
'w-e-image-container w-e-selected-image-container'
// innerHTML: strDom,
// innerHTML: strDom,
},
},
style
:
style
,
style
:
style
},
},
[
[
imageVnode
,
imageVnode
,
...
@@ -194,23 +185,23 @@ function renderResizeContainer(editor, elemNode, imageVnode, imageInfo) {
...
@@ -194,23 +185,23 @@ function renderResizeContainer(editor, elemNode, imageVnode, imageInfo) {
position
:
'absolute'
,
position
:
'absolute'
,
cursor
:
'nwse-resize'
,
cursor
:
'nwse-resize'
,
left
:
0
,
left
:
0
,
top
:
0
,
top
:
0
},
},
on
:
{
on
:
{
mousedown
(
e
)
{
mousedown
(
e
)
{
const
$target
=
$
(
e
.
target
)
;
const
$target
=
$
(
e
.
target
)
if
(
!
$target
.
hasClass
(
'w-e-image-dragger'
))
{
if
(
!
$target
.
hasClass
(
'w-e-image-dragger'
))
{
// target 不是 .w-e-image-dragger 拖拽触手,则忽略
// target 不是 .w-e-image-dragger 拖拽触手,则忽略
return
;
return
}
}
e
.
preventDefault
()
;
e
.
preventDefault
()
if
(
$target
.
hasClass
(
'left-top'
)
||
$target
.
hasClass
(
'left-bottom'
))
{
if
(
$target
.
hasClass
(
'left-top'
)
||
$target
.
hasClass
(
'left-bottom'
))
{
revers
=
true
;
// 反转。向右拖拽,减少宽度
revers
=
true
// 反转。向右拖拽,减少宽度
}
}
init
(
e
.
clientX
)
;
// 初始化
init
(
e
.
clientX
)
// 初始化
}
,
}
}
,
}
}),
}),
h
(
'div'
,
{
h
(
'div'
,
{
props
:
{
className
:
'w-e-image-dragger right-top'
},
props
:
{
className
:
'w-e-image-dragger right-top'
},
...
@@ -221,23 +212,23 @@ function renderResizeContainer(editor, elemNode, imageVnode, imageInfo) {
...
@@ -221,23 +212,23 @@ function renderResizeContainer(editor, elemNode, imageVnode, imageInfo) {
position
:
'absolute'
,
position
:
'absolute'
,
cursor
:
'nwse-resize'
,
cursor
:
'nwse-resize'
,
right
:
0
,
right
:
0
,
top
:
0
,
top
:
0
},
},
on
:
{
on
:
{
mousedown
(
e
)
{
mousedown
(
e
)
{
const
$target
=
$
(
e
.
target
)
;
const
$target
=
$
(
e
.
target
)
if
(
!
$target
.
hasClass
(
'w-e-image-dragger'
))
{
if
(
!
$target
.
hasClass
(
'w-e-image-dragger'
))
{
// target 不是 .w-e-image-dragger 拖拽触手,则忽略
// target 不是 .w-e-image-dragger 拖拽触手,则忽略
return
;
return
}
}
e
.
preventDefault
()
;
e
.
preventDefault
()
if
(
$target
.
hasClass
(
'left-top'
)
||
$target
.
hasClass
(
'left-bottom'
))
{
if
(
$target
.
hasClass
(
'left-top'
)
||
$target
.
hasClass
(
'left-bottom'
))
{
revers
=
true
;
// 反转。向右拖拽,减少宽度
revers
=
true
// 反转。向右拖拽,减少宽度
}
}
init
(
e
.
clientX
)
;
// 初始化
init
(
e
.
clientX
)
// 初始化
}
,
}
}
,
}
}),
}),
h
(
'div'
,
{
h
(
'div'
,
{
props
:
{
className
:
'w-e-image-dragger left-bottom'
},
props
:
{
className
:
'w-e-image-dragger left-bottom'
},
...
@@ -248,23 +239,23 @@ function renderResizeContainer(editor, elemNode, imageVnode, imageInfo) {
...
@@ -248,23 +239,23 @@ function renderResizeContainer(editor, elemNode, imageVnode, imageInfo) {
position
:
'absolute'
,
position
:
'absolute'
,
cursor
:
'nwse-resize'
,
cursor
:
'nwse-resize'
,
left
:
0
,
left
:
0
,
bottom
:
0
,
bottom
:
0
},
},
on
:
{
on
:
{
mousedown
(
e
)
{
mousedown
(
e
)
{
const
$target
=
$
(
e
.
target
)
;
const
$target
=
$
(
e
.
target
)
if
(
!
$target
.
hasClass
(
'w-e-image-dragger'
))
{
if
(
!
$target
.
hasClass
(
'w-e-image-dragger'
))
{
// target 不是 .w-e-image-dragger 拖拽触手,则忽略
// target 不是 .w-e-image-dragger 拖拽触手,则忽略
return
;
return
}
}
e
.
preventDefault
()
;
e
.
preventDefault
()
if
(
$target
.
hasClass
(
'left-top'
)
||
$target
.
hasClass
(
'left-bottom'
))
{
if
(
$target
.
hasClass
(
'left-top'
)
||
$target
.
hasClass
(
'left-bottom'
))
{
revers
=
true
;
// 反转。向右拖拽,减少宽度
revers
=
true
// 反转。向右拖拽,减少宽度
}
}
init
(
e
.
clientX
)
;
// 初始化
init
(
e
.
clientX
)
// 初始化
}
,
}
}
,
}
}),
}),
h
(
'div'
,
{
h
(
'div'
,
{
props
:
{
className
:
'w-e-image-dragger right-bottom'
},
props
:
{
className
:
'w-e-image-dragger right-bottom'
},
...
@@ -275,85 +266,85 @@ function renderResizeContainer(editor, elemNode, imageVnode, imageInfo) {
...
@@ -275,85 +266,85 @@ function renderResizeContainer(editor, elemNode, imageVnode, imageInfo) {
position
:
'absolute'
,
position
:
'absolute'
,
cursor
:
'nwse-resize'
,
cursor
:
'nwse-resize'
,
right
:
0
,
right
:
0
,
bottom
:
0
,
bottom
:
0
},
},
on
:
{
on
:
{
mousedown
(
e
)
{
mousedown
(
e
)
{
const
$target
=
$
(
e
.
target
)
;
const
$target
=
$
(
e
.
target
)
if
(
!
$target
.
hasClass
(
'w-e-image-dragger'
))
{
if
(
!
$target
.
hasClass
(
'w-e-image-dragger'
))
{
// target 不是 .w-e-image-dragger 拖拽触手,则忽略
// target 不是 .w-e-image-dragger 拖拽触手,则忽略
return
;
return
}
}
e
.
preventDefault
()
;
e
.
preventDefault
()
if
(
$target
.
hasClass
(
'left-top'
)
||
$target
.
hasClass
(
'left-bottom'
))
{
if
(
$target
.
hasClass
(
'left-top'
)
||
$target
.
hasClass
(
'left-bottom'
))
{
revers
=
true
;
// 反转。向右拖拽,减少宽度
revers
=
true
// 反转。向右拖拽,减少宽度
}
}
init
(
e
.
clientX
)
;
// 初始化
init
(
e
.
clientX
)
// 初始化
}
,
}
}
,
}
})
,
})
]
,
]
)
;
)
}
}
// 在编辑器中渲染新元素
// 在编辑器中渲染新元素
// 定义 renderElem 函数
// 定义 renderElem 函数
const
renderImage
=
(
elem
,
children
,
editor
)
=>
{
const
renderImage
=
(
elem
,
children
,
editor
)
=>
{
// 获取“附件”的数据,参考上文 myResume 数据结构
// 获取“附件”的数据,参考上文 myResume 数据结构
const
{
title
=
''
,
imgUrl
=
''
,
imgDescript
=
''
,
style
=
{}
}
=
elem
;
const
{
title
=
''
,
imgUrl
=
''
,
imgDescript
=
''
,
style
=
{}
}
=
elem
const
{
width
=
''
,
height
=
''
}
=
style
;
const
{
width
=
''
,
height
=
''
}
=
style
let
imageStyle
=
''
;
let
imageStyle
=
''
if
(
width
)
imageStyle
+=
'width: 100%; '
;
if
(
width
)
imageStyle
+=
'width: 100%; '
if
(
height
)
imageStyle
+=
'height: 100%; '
;
if
(
height
)
imageStyle
+=
'height: 100%; '
const
isDisabled
=
editor
.
isDisabled
()
;
const
isDisabled
=
editor
.
isDisabled
()
const
selected
=
DomEditor
.
isNodeSelected
(
editor
,
elem
)
;
// 图片是否选中
const
selected
=
DomEditor
.
isNodeSelected
(
editor
,
elem
)
// 图片是否选中
const
vnode
=
`<img style="
${
imageStyle
}
" src=
${
imgUrl
}
alt="
${
imgDescript
}
" data-href="
${
imgUrl
}
" />`
;
const
vnode
=
`<img style="
${
imageStyle
}
" src=
${
imgUrl
}
alt="
${
imgDescript
}
" data-href="
${
imgUrl
}
" />`
let
str
=
''
;
let
str
=
''
if
(
selected
&&
!
isDisabled
)
{
if
(
selected
&&
!
isDisabled
)
{
str
=
renderResizeContainer
(
editor
,
elem
,
vnode
,
style
)
;
str
=
renderResizeContainer
(
editor
,
elem
,
vnode
,
style
)
}
else
{
}
else
{
str
=
renderImageContainer
(
editor
,
elem
,
vnode
,
style
)
;
str
=
renderImageContainer
(
editor
,
elem
,
vnode
,
style
)
}
}
const
imgNode
=
h
(
const
imgNode
=
h
(
'div'
,
'div'
,
{
{
props
:
{
props
:
{
className
:
'chapter-image-pic'
,
className
:
'chapter-image-pic'
},
},
style
:
{
style
:
{
position
:
'relative'
,
position
:
'relative'
,
fontSize
:
'0'
,
fontSize
:
'0'
}
,
}
},
},
[
str
]
,
[
str
]
)
;
)
const
titleNode
=
h
(
const
titleNode
=
h
(
'p'
,
'p'
,
{
{
props
:
{
props
:
{
className
:
'chapter-image-title'
,
className
:
'chapter-image-title'
},
},
style
:
{
color
:
'#666'
,
fontSize
:
'14px'
,
margin
:
'0'
,
lineHeight
:
'20px'
}
,
style
:
{
color
:
'#666'
,
fontSize
:
'14px'
,
margin
:
'0'
,
lineHeight
:
'20px'
}
},
},
[
title
]
,
[
title
]
)
;
)
const
titleDescriptNode
=
h
(
const
titleDescriptNode
=
h
(
'p'
,
'p'
,
{
{
props
:
{
props
:
{
className
:
'chapter-image-descript'
,
className
:
'chapter-image-descript'
},
},
style
:
{
color
:
'#999'
,
fontSize
:
'14px'
,
margin
:
'0'
,
lineHeight
:
'20px'
}
,
style
:
{
color
:
'#999'
,
fontSize
:
'14px'
,
margin
:
'0'
,
lineHeight
:
'20px'
}
},
},
[
imgDescript
]
,
[
imgDescript
]
)
;
)
const
element
=
[
imgNode
]
;
const
element
=
[
imgNode
]
if
(
title
)
{
if
(
title
)
{
// element.push(titleNode);
// element.push(titleNode);
}
}
...
@@ -370,35 +361,35 @@ const renderImage = (elem, children, editor) => {
...
@@ -370,35 +361,35 @@ const renderImage = (elem, children, editor) => {
props
:
{
props
:
{
// HTML 属性,驼峰式写法
// HTML 属性,驼峰式写法
contentEditable
:
false
,
contentEditable
:
false
,
className
:
'chapter-image-container'
,
className
:
'chapter-image-container'
},
},
style
:
{
padding
:
'10px 0px'
},
// style ,驼峰式写法
style
:
{
padding
:
'10px 0px'
},
// style ,驼峰式写法
on
:
{
on
:
{
click
()
{
click
()
{
console
.
log
(
'clicked'
)
;
console
.
log
(
'clicked'
)
}
/* 其他... */
,
}
/* 其他... */
}
,
}
},
},
// 子节点
// 子节点
[...
element
]
,
[...
element
]
)
;
)
return
attachVnode
;
return
attachVnode
}
;
}
const
renderElemConf
=
{
const
renderElemConf
=
{
type
:
'chapterImage'
,
type
:
'chapterImage'
,
renderElem
:
renderImage
,
renderElem
:
renderImage
}
;
}
// 把新元素转换为 HTML
// 把新元素转换为 HTML
const
chapterImageToHtml
=
(
elem
,
childrenHtml
)
=>
{
const
chapterImageToHtml
=
(
elem
,
childrenHtml
)
=>
{
// 获取附件元素的数据
// 获取附件元素的数据
const
{
title
=
''
,
imgUrl
=
''
,
imgDescript
,
style
=
{}
}
=
elem
;
const
{
title
=
''
,
imgUrl
=
''
,
imgDescript
,
style
=
{}
}
=
elem
const
{
width
=
''
,
height
=
''
}
=
style
;
const
{
width
=
''
,
height
=
''
}
=
style
let
styleStr
=
''
;
let
styleStr
=
''
if
(
width
)
styleStr
+=
`width:
${
width
}
;`
;
if
(
width
)
styleStr
+=
`width:
${
width
}
;`
if
(
height
)
styleStr
+=
`height:
${
height
}
;`
;
if
(
height
)
styleStr
+=
`height:
${
height
}
;`
// 生成 HTML 代码
// 生成 HTML 代码
const
html
=
`<span
const
html
=
`<span
...
@@ -412,24 +403,24 @@ const chapterImageToHtml = (elem, childrenHtml) => {
...
@@ -412,24 +403,24 @@ const chapterImageToHtml = (elem, childrenHtml) => {
<div class="chapter-image-pic" style="position: relative; font-size: 0px;">
<div class="chapter-image-pic" style="position: relative; font-size: 0px;">
<img src="
${
imgUrl
}
" alt="
${
imgDescript
}
" style="
${
styleStr
}
" />
<img src="
${
imgUrl
}
" alt="
${
imgDescript
}
" style="
${
styleStr
}
" />
</div>
</div>
</span>`
;
</span>`
// `<p class="chapter-image-title">${title}</p><p class="chapter-image-descript">${imgDescript}</p>`
// `<p class="chapter-image-title">${title}</p><p class="chapter-image-descript">${imgDescript}</p>`
return
html
;
return
html
}
;
}
const
chapterImageElemToHtmlConf
=
{
const
chapterImageElemToHtmlConf
=
{
type
:
'chapterImage'
,
// 新元素的 type ,重要!!!
type
:
'chapterImage'
,
// 新元素的 type ,重要!!!
elemToHtml
:
chapterImageToHtml
,
elemToHtml
:
chapterImageToHtml
}
;
}
// 解析新元素 HTML 到编辑器
// 解析新元素 HTML 到编辑器
const
parseImageHtml
=
(
domElem
,
children
,
editor
)
=>
{
const
parseImageHtml
=
(
domElem
,
children
,
editor
)
=>
{
// 从 DOM element 中获取“附件”的信息
// 从 DOM element 中获取“附件”的信息
const
imgUrl
=
domElem
.
getAttribute
(
'data-imgUrl'
)
||
''
;
const
imgUrl
=
domElem
.
getAttribute
(
'data-imgUrl'
)
||
''
const
imgDescript
=
domElem
.
getAttribute
(
'data-imgDescript'
)
||
''
;
const
imgDescript
=
domElem
.
getAttribute
(
'data-imgDescript'
)
||
''
const
title
=
domElem
.
getAttribute
(
'data-title'
)
||
''
;
const
title
=
domElem
.
getAttribute
(
'data-title'
)
||
''
const
$elem
=
$
(
domElem
)
;
const
$elem
=
$
(
domElem
)
// 生成“附件”元素(按照此前约定的数据结构)
// 生成“附件”元素(按照此前约定的数据结构)
const
myResume
=
{
const
myResume
=
{
...
@@ -439,30 +430,25 @@ const parseImageHtml = (domElem, children, editor) => {
...
@@ -439,30 +430,25 @@ const parseImageHtml = (domElem, children, editor) => {
imgDescript
,
imgDescript
,
style
:
{
style
:
{
width
:
getStyleValue
(
$elem
,
'width'
),
width
:
getStyleValue
(
$elem
,
'width'
),
height
:
getStyleValue
(
$elem
,
'height'
)
,
height
:
getStyleValue
(
$elem
,
'height'
)
},
},
children
:
[{
text
:
''
}]
,
// void node 必须有 children ,其中有一个空字符串,重要!!!
children
:
[{
text
:
''
}]
// void node 必须有 children ,其中有一个空字符串,重要!!!
}
;
}
return
myResume
;
return
myResume
}
;
}
const
parseImageConf
=
{
const
parseImageConf
=
{
selector
:
'span[data-w-e-type="chapterImage"]'
,
// CSS 选择器,匹配特定的 HTML 标签
selector
:
'span[data-w-e-type="chapterImage"]'
,
// CSS 选择器,匹配特定的 HTML 标签
parseElemHtml
:
parseImageHtml
,
parseElemHtml
:
parseImageHtml
}
;
}
const
chapterImageModule
=
{
const
chapterImageModule
=
{
editorPlugin
:
withImageNode
,
editorPlugin
:
withImageNode
,
renderElems
:
[
renderElemConf
],
renderElems
:
[
renderElemConf
],
elemsToHtml
:
[
chapterImageElemToHtmlConf
],
elemsToHtml
:
[
chapterImageElemToHtmlConf
],
parseElemsHtml
:
[
parseImageConf
],
parseElemsHtml
:
[
parseImageConf
],
menus
:
[
menus
:
[
ImageAutoConf
,
imageWidth100MenuChapterConf
,
imageWidth50MenuChapterConf
,
imageWidth30MenuChapterConf
]
ImageAutoConf
,
}
imageWidth100MenuChapterConf
,
imageWidth50MenuChapterConf
,
export
default
chapterImageModule
imageWidth30MenuChapterConf
,
export
{
withImageNode
,
renderElemConf
,
chapterImageElemToHtmlConf
,
parseImageConf
}
],
};
export
default
chapterImageModule
;
export
{
withImageNode
,
renderElemConf
,
chapterImageElemToHtmlConf
,
parseImageConf
};
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论