Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
C
center-book
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
EzijingWeb
center-book
Commits
f77be006
提交
f77be006
authored
9月 12, 2024
作者:
王鹏飞
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
chore: 修改编辑器AI相关内容
上级
1cc62424
全部展开
隐藏空白字符变更
内嵌
并排
正在显示
31 个修改的文件
包含
465 行增加
和
587 行删除
+465
-587
package-lock.json
package-lock.json
+1
-0
index.jsx
src/common/wangeditor-customer/ai-drawer/index.jsx
+0
-214
chapter-item.jsx
src/common/wangeditor-customer/components/chapter-item.jsx
+0
-0
chapter-title.jsx
src/common/wangeditor-customer/components/chapter-title.jsx
+0
-0
expand.jsx
src/common/wangeditor-customer/components/expand.jsx
+0
-0
gallery.jsx
src/common/wangeditor-customer/components/gallery.jsx
+9
-9
link.jsx
src/common/wangeditor-customer/components/link.jsx
+0
-0
tooltip.jsx
src/common/wangeditor-customer/components/tooltip.jsx
+0
-0
ChapterItem.js
src/common/wangeditor-customer/customer/ChapterItem.js
+10
-13
index-padd.jsx
src/common/wangeditor-customer/index-padd.jsx
+0
-0
index.jsx
src/common/wangeditor-customer/index.jsx
+0
-0
index.less
src/common/wangeditor-customer/index.less
+26
-158
AIChat.jsx
src/common/wangeditor-customer/menu/AIChat.jsx
+13
-25
AIContentInspect.jsx
src/common/wangeditor-customer/menu/AIContentInspect.jsx
+21
-0
AIDigitalHuman.jsx
src/common/wangeditor-customer/menu/AIDigitalHuman.jsx
+27
-0
AIExpand.jsx
src/common/wangeditor-customer/menu/AIExpand.jsx
+11
-26
AIPolishing.jsx
src/common/wangeditor-customer/menu/AIPolishing.jsx
+32
-45
AIPunctuation.jsx
src/common/wangeditor-customer/menu/AIPunctuation.jsx
+21
-0
AIRewrite.jsx
src/common/wangeditor-customer/menu/AIRewrite.jsx
+14
-25
AISummary.jsx
src/common/wangeditor-customer/menu/AISummary.jsx
+13
-26
AIChatDrawer.jsx
src/common/wangeditor-customer/menu/common/AIChatDrawer.jsx
+108
-0
AIChatDrawer.less
src/common/wangeditor-customer/menu/common/AIChatDrawer.less
+2
-2
AIModal.jsx
src/common/wangeditor-customer/menu/common/AIModal.jsx
+20
-20
BaseModalMenu.jsx
src/common/wangeditor-customer/menu/common/BaseModalMenu.jsx
+26
-0
useAIChat.js
src/common/wangeditor-customer/menu/common/useAIChat.js
+84
-0
setting.js
src/common/wangeditor-customer/utils/setting.js
+27
-24
index.jsx
src/components/editor/index.jsx
+0
-0
styles.less
src/components/editor/styles.less
+0
-0
index.jsx
src/pages/books/section/index.jsx
+0
-0
index.jsx
src/pages/editor/index.jsx
+0
-0
routes.jsx
src/routes/routes.jsx
+0
-0
没有找到文件。
package-lock.json
浏览文件 @
f77be006
...
...
@@ -584,6 +584,7 @@
"version"
:
"3.0.6"
,
"resolved"
:
"https://registry.npmjs.org/@fortaine/fetch-event-source/-/fetch-event-source-3.0.6.tgz"
,
"integrity"
:
"sha512-621GAuLMvKtyZQ3IA6nlDWhV1V/7PGOTNIGLUifxt0KzM+dZIweJ6F3XvQF3QnqeNfS1N7WQ0Kil1Di/lhChEw=="
,
"license"
:
"MIT"
,
"engines"
:
{
"node"
:
">=16.15"
}
...
...
src/common/wangeditor-customer/ai-drawer/index.jsx
deleted
100644 → 0
浏览文件 @
1cc62424
import
{
useState
,
useEffect
}
from
'react'
import
{
SendOutlined
}
from
'@ant-design/icons'
import
{
Input
,
Button
,
message
}
from
'antd'
import
md5
from
'js-md5'
import
dayjs
from
'dayjs'
import
{
sleep
}
from
'@/utils/common'
import
'./index.less'
import
{
useSelector
,
useDispatch
}
from
'react-redux'
import
{
setEditorAi
}
from
'@/store/modules/editor'
import
normalAvatar
from
'@/assets/images/icon-normal-avatar.png'
const
UUID
=
'f3846153ba784b6d86bdcd5533259c88'
const
auth_key
=
'f3846153ba784b6d86bdcd5533259c88'
const
auth_secret
=
'HO4IyLEwEOHpeOXBxaLQUOqWslJRGs1M'
const
AIDrawerComponent
=
(
props
)
=>
{
const
{
selectText
}
=
props
const
dispatch
=
useDispatch
()
const
{
userInfo
}
=
useSelector
((
state
)
=>
state
.
user
)
// 用户状态信息
const
{
editorAi
}
=
useSelector
((
state
)
=>
state
.
editor
)
// ai信息设置
const
[
value
,
setValue
]
=
useState
(
selectText
)
// 输入值
const
[
loading
,
setLoading
]
=
useState
(
false
)
// loading
const
[
chatId
,
setChatId
]
=
useState
(
null
)
// 对话的id
const
[
chatContentList
,
setChatContentList
]
=
useState
([])
// 对话数据
const
element
=
document
.
querySelector
(
'#chat-content'
)
let
str
=
''
useEffect
(()
=>
{
if
(
editorAi
&&
Object
.
entries
(
editorAi
).
length
>
0
)
{
let
tempJSON
=
JSON
.
parse
(
JSON
.
stringify
(
chatContentList
))
let
conversationId
=
editorAi
.
conversationId
const
item
=
tempJSON
.
filter
((
item
)
=>
item
.
conversationId
===
conversationId
)
if
(
item
&&
item
.
length
>
0
)
{
tempJSON
[
tempJSON
.
length
-
1
]
=
editorAi
}
else
{
tempJSON
.
push
(
editorAi
)
}
setChatId
(
editorAi
.
chatId
)
setChatContentList
(
tempJSON
)
element
.
scrollTo
(
0
,
999999
)
}
else
{
setLoading
(
false
)
}
},
[
editorAi
])
useEffect
(()
=>
{
if
(
selectText
)
{
targetChat
()
}
return
()
=>
{
setValue
(
''
)
setChatContentList
([])
setChatId
(
null
)
}
},
[])
let
buffer
=
''
const
readBuffer
=
(
reader
,
decoder
)
=>
{
return
reader
.
read
().
then
(
async
({
done
,
value
})
=>
{
if
(
done
)
{
// 处理最后的缓冲数据
setLoading
(
false
)
setValue
(
''
)
dispatch
(
setEditorAi
({}))
str
=
''
processBuffer
(
buffer
)
return
}
await
sleep
(
80
)
buffer
+=
decoder
.
decode
(
value
,
{
stream
:
true
})
// 检查缓冲区中是否有完整的消息
const
messages
=
buffer
.
split
(
'
\
n
\
n'
)
// 假设每个消息以换行符结束
messages
.
forEach
((
message
,
index
)
=>
{
if
(
index
<
messages
.
length
-
1
)
{
processBuffer
(
message
)
}
})
buffer
=
messages
[
messages
.
length
-
1
]
// 更新缓冲区
readBuffer
(
reader
,
decoder
)
})
}
const
processBuffer
=
(
text
)
=>
{
let
aiContent
=
{}
try
{
const
content
=
text
.
replace
(
/
\n
+/g
,
''
).
split
(
'data:'
)
if
(
content
instanceof
Array
)
{
const
itemContent
=
JSON
.
parse
(
content
[
1
])
if
(
!
(
itemContent
.
finish
&&
itemContent
.
finish
===
true
))
{
str
+=
itemContent
.
content
aiContent
=
{
type
:
'ai'
,
content
:
str
,
chatId
:
itemContent
.
chatId
,
avatar
:
itemContent
.
role
.
avatar
,
conversationId
:
itemContent
.
conversationId
}
dispatch
(
setEditorAi
(
aiContent
))
}
}
}
catch
{}
}
// 提交对话
const
targetChat
=
async
()
=>
{
if
(
value
)
{
setLoading
(
true
)
const
timestamp
=
Date
.
now
()
fetch
(
'/api/tiangong/openapi/agent/chat/stream/v1'
,
{
method
:
'POST'
,
responseType
:
'stream'
,
// 设置响应类型为 'stream'
headers
:
{
'Content-Type'
:
'application/json'
,
authKey
:
auth_key
,
timestamp
:
timestamp
,
sign
:
md5
(
`
${
auth_key
}${
auth_secret
}${
timestamp
}
`
),
stream
:
true
// or change to "false" 不处理流式返回内容
},
body
:
JSON
.
stringify
({
agentId
:
UUID
,
chatId
:
chatId
,
userChatInput
:
value
}),
mode
:
'cors'
})
.
then
((
response
)
=>
{
// response.body 是一个 ReadableStream 对象
const
stream
=
response
.
body
// 使用流式数据
const
reader
=
stream
.
getReader
()
const
decoder
=
new
TextDecoder
()
// 用于解码二进制数据流
let
userContent
=
{
type
:
'user'
,
content
:
value
,
time
:
Date
.
now
()
}
let
tempJSON
=
JSON
.
parse
(
JSON
.
stringify
(
chatContentList
))
tempJSON
.
push
(
userContent
)
setChatContentList
(
tempJSON
)
// 开始读取流数据
readBuffer
(
reader
,
decoder
)
})
.
catch
((
error
)
=>
{
// 处理请求错误
console
.
error
(
'Request failed:'
,
error
)
})
}
else
{
message
.
error
(
'请输入对话内容!'
)
}
}
return
(
<
div
className=
"ai-drawer-content"
>
<
div
className=
"ai-chat-container"
>
<
div
className=
"chat-content"
id=
"chat-content"
>
<
div
className=
"chat-content-padd"
>
{
chatContentList
&&
chatContentList
.
length
>
0
&&
chatContentList
.
map
((
item
,
index
)
=>
{
return
(
<
div
className=
{
`chat-content-item`
}
key=
{
index
}
>
{
item
.
type
===
'user'
&&
<
div
className=
"time"
>
{
dayjs
(
item
.
time
).
format
(
'YYYY-MM-DD HH:mm'
)
}
</
div
>
}
{
item
.
type
===
'ai'
&&
(
<
div
className=
"inside"
>
<
div
className=
"ai-in-content"
>
<
div
className=
"img"
>
<
img
src=
{
item
.
avatar
}
alt=
""
/>
</
div
>
<
div
className=
"ask-content"
>
{
item
.
content
}
</
div
>
</
div
>
</
div
>
)
}
{
item
.
type
===
'user'
&&
(
<
div
className=
"inside inside-user"
>
<
div
className=
"user-in-content"
>
<
div
className=
"ask-content"
>
{
item
.
content
}
</
div
>
<
div
className=
"img"
>
<
img
src=
{
userInfo
.
pic
?
userInfo
.
pic
:
normalAvatar
}
alt=
"用户头像"
/>
</
div
>
</
div
>
</
div
>
)
}
</
div
>
)
})
}
</
div
>
</
div
>
</
div
>
<
div
className=
"ai-chat-ask"
>
<
div
className=
"text"
>
<
Input
.
TextArea
value=
{
value
}
defaultValue=
{
value
}
allowClear
disabled=
{
loading
}
onChange=
{
(
e
)
=>
setValue
(
e
.
target
.
value
)
}
palceholder=
"请输入内容"
style=
{
{
height
:
'80px'
,
resize
:
'none'
}
}
></
Input
.
TextArea
>
</
div
>
<
div
className=
"button"
>
<
Button
type=
"primary"
icon=
{
<
SendOutlined
/>
}
loading=
{
loading
}
size=
"large"
onClick=
{
targetChat
}
></
Button
>
</
div
>
</
div
>
</
div
>
)
}
export
default
AIDrawerComponent
src/common/wangeditor-customer/components/chapter-item.jsx
浏览文件 @
f77be006
差异被折叠。
点击展开。
src/common/wangeditor-customer/components/chapter-title.jsx
浏览文件 @
f77be006
差异被折叠。
点击展开。
src/common/wangeditor-customer/components/expand.jsx
浏览文件 @
f77be006
差异被折叠。
点击展开。
src/common/wangeditor-customer/components/gallery.jsx
浏览文件 @
f77be006
...
...
@@ -4,11 +4,11 @@ import './index.less'
import
{
addGallery
}
from
'../utils/request'
import
{
SlateEditor
,
SlateTransforms
,
SlateElement
}
from
'@wangeditor/editor'
import
{
findNodeWithParent
,
font
F
izeList
}
from
'../utils/setting'
import
{
findNodeWithParent
,
font
S
izeList
}
from
'../utils/setting'
import
GalleryFormItem
from
'./galleryItem'
const
randomOne
=
Math
.
random
().
toString
(
16
).
substring
(
2
,
10
)
const
GalleryModal
=
(
props
)
=>
{
const
GalleryModal
=
props
=>
{
const
{
editor
,
ossClient
,
galleryInfo
,
bookId
,
chapterId
,
setGalleryVisible
,
setGalleryInfo
,
selectionSize
=
18
,
isOnline
=
false
}
=
props
const
[
form
]
=
Form
.
useForm
()
...
...
@@ -57,7 +57,7 @@ const GalleryModal = (props) => {
const
[
activeKey
,
setActiveKey
]
=
useState
(
initialItems
[
0
].
key
)
const
[
items
,
setItems
]
=
useState
(
initialItems
)
const
onChange
=
(
newActiveKey
)
=>
{
const
onChange
=
newActiveKey
=>
{
setActiveKey
(
newActiveKey
)
}
const
add
=
()
=>
{
...
...
@@ -81,8 +81,8 @@ const GalleryModal = (props) => {
setItems
(
newPanes
)
setActiveKey
(
newActiveKey
)
}
const
remove
=
async
(
targetKey
)
=>
{
const
tempGallery
=
picList
.
filter
(
(
item
)
=>
item
.
key
!==
targetKey
)
const
remove
=
async
targetKey
=>
{
const
tempGallery
=
picList
.
filter
(
item
=>
item
.
key
!==
targetKey
)
await
setPicList
(
tempGallery
)
console
.
log
(
tempGallery
)
form
.
setFieldsValue
({
gallery
:
tempGallery
})
...
...
@@ -94,7 +94,7 @@ const GalleryModal = (props) => {
lastIndex
=
i
-
1
}
})
let
newPanes
=
items
.
filter
(
(
item
)
=>
item
.
key
!==
targetKey
)
let
newPanes
=
items
.
filter
(
item
=>
item
.
key
!==
targetKey
)
if
(
newPanes
.
length
&&
newActiveKey
===
targetKey
)
{
if
(
lastIndex
>=
0
)
{
newActiveKey
=
newPanes
[
lastIndex
].
key
...
...
@@ -184,7 +184,7 @@ const GalleryModal = (props) => {
}
},
[
galleryInfo
])
const
onFinish
=
async
(
values
)
=>
{
const
onFinish
=
async
values
=>
{
editor
.
restoreSelection
()
// setLoading(true);
const
{
galleryTitle
,
flex
,
gallery
,
fontSize
,
theme
=
''
}
=
values
...
...
@@ -193,7 +193,7 @@ const GalleryModal = (props) => {
if
(
parseInt
(
flex
)
!==
2
)
{
// 删除空白的p标签
const
nodeEntries
=
SlateEditor
.
nodes
(
editor
,
{
match
:
(
node
)
=>
{
match
:
node
=>
{
// JS syntax
if
(
SlateElement
.
isElement
(
node
))
{
if
(
node
.
type
===
'paragraph'
)
{
...
...
@@ -310,7 +310,7 @@ const GalleryModal = (props) => {
<
Col
span=
{
12
}
>
<
Form
.
Item
label=
"字号"
name=
"fontSize"
>
<
Select
>
{
font
F
izeList
.
map
((
item
,
index
)
=>
{
{
font
S
izeList
.
map
((
item
,
index
)
=>
{
return
(
<
Select
.
Option
value=
{
item
.
value
}
key=
{
index
}
>
{
item
.
name
}
...
...
src/common/wangeditor-customer/components/link.jsx
浏览文件 @
f77be006
差异被折叠。
点击展开。
src/common/wangeditor-customer/components/tooltip.jsx
浏览文件 @
f77be006
差异被折叠。
点击展开。
src/common/wangeditor-customer/customer/ChapterItem.js
浏览文件 @
f77be006
import
{
DomEditor
,
SlateTransforms
,
SlateRange
}
from
'@wangeditor/editor'
;
import
{
DomEditor
,
SlateTransforms
,
SlateRange
}
from
'@wangeditor/editor'
class
ChapterItem
{
constructor
()
{
this
.
title
=
'
章节
'
this
.
title
=
'
节头
'
this
.
iconSvg
=
`<svg width="22px" height="23px" viewBox="0 0 22 23" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>图标</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
...
...
@@ -14,14 +14,14 @@ class ChapterItem {
</g>
</g>
</g>
</svg>`
;
</svg>`
this
.
tag
=
'button'
}
getValue
(
editor
)
{
return
'hello, 图片, , , 图片'
}
isActive
(
editor
)
{
return
false
;
return
false
}
isDisabled
(
editor
)
{
const
{
selection
}
=
editor
...
...
@@ -35,7 +35,7 @@ class ChapterItem {
// eslint-disable-next-line array-callback-return
const
hasPreElem
=
selectedElems
.
some
(
elem
=>
{
const
type
=
DomEditor
.
getNodeType
(
elem
)
;
const
type
=
DomEditor
.
getNodeType
(
elem
)
// 代码块 引用 表格 禁用
if
(
type
===
'pre'
||
type
===
'blockquote'
||
type
===
'table'
||
type
===
'table-row'
||
type
===
'table-cell'
)
return
true
})
...
...
@@ -45,10 +45,10 @@ class ChapterItem {
}
exec
(
editor
,
value
)
{
// editor.insertText(value) // value 即 this.getValue(editor) 的返回值
if
(
this
.
isDisabled
(
editor
))
{
return
;
if
(
this
.
isDisabled
(
editor
))
{
return
}
editor
.
emit
(
'ChapterItemMenuClick'
)
;
editor
.
emit
(
'ChapterItemMenuClick'
)
}
}
...
...
@@ -56,9 +56,7 @@ export default {
key
:
'ChapterItem'
,
// 定义 menu key :要保证唯一、不重复(重要)
factory
()
{
return
new
ChapterItem
()
// 把 `YourMenuClass` 替换为你菜单的 class
}
,
}
}
export
{
ChapterItem
}
\ No newline at end of file
export
{
ChapterItem
}
src/common/wangeditor-customer/index-padd.jsx
deleted
100644 → 0
浏览文件 @
1cc62424
差异被折叠。
点击展开。
src/common/wangeditor-customer/index.jsx
浏览文件 @
f77be006
差异被折叠。
点击展开。
src/common/wangeditor-customer/index.less
浏览文件 @
f77be006
...
...
@@ -239,174 +239,42 @@
}
}
.editor-toolbar-container {
flex: 0 0 306px;
border: none !important;
margin: 10px;
.w-e-bar {
background-color: transparent;
padding: 5px 15px;
}
.w-e-bar-boxitem {
background-color: #fff;
width: 100%;
display: flex;
justify-content: space-around;
&.you {
flex-flow: row wrap;
.w-e-bar-item {
width: 16.6%;
flex: 0 0 16.6%;
}
}
&.line-height {
justify-content: flex-start;
}
}
.custom-bar-box {
background-color: #fff;
width: 100%;
display: flex;
justify-content: flex-start;
flex-flow: row wrap;
.w-e-menu-tooltip-v5 {
height: 50px;
}
&.two {
.w-e-bar-item {
width: 33%;
flex: 0 0 33%;
justify-content: flex-start;
text-align: left;
display: block;
p {
color: #333;
font-weight: bold;
}
button {
padding-left: 0;
}
}
}
&.input {
justify-content: space-around;
padding: 10px 0;
height: 62px;
.input-item {
flex: 1;
padding: 0 10px;
text-align: center;
input {
width: 100%;
color: #333;
text-align: center;
border: 1px solid #d9d9d9;
border-radius: 5px;
height: 30px;
line-height: 30px;
&:hover {
border-color: #b83956;
}
}
.text {
color: #999;
font-size: 12px;
margin-top: 8px;
}
}
}
&.media {
.w-e-bar-item {
width: 25%;
flex: 0 0 25%;
p {
font-size: 10px;
-webkit-transform: scale(1.2);
-moz-transform: scale(1.2);
-o-transform: scale(1.2);
-ms-transform: scale(1.2);
transform: scale(1.2);
}
}
}
&.hight {
.w-e-bar-item {
width: 25%;
flex: 0 0 25%;
p {
font-size: 10px;
-webkit-transform: scale(1.2);
-moz-transform: scale(1.2);
-o-transform: scale(1.2);
-ms-transform: scale(1.2);
transform: scale(1.2);
}
}
}
&.media,
&.hight {
.w-e-bar-item {
button {
display: block;
flex-direction: column;
padding: 10px;
height: 50px;
svg {
font-size: 22px;
}
}
p {
display: block;
width: 100%;
margin: 0px;
}
}
}
.w-e-bar-item {
width: 25%;
flex: 0 0 25%;
display: flex;
flex-direction: column;
height: 60px;
padding: 0;
svg {
width: 18px;
height: 18px;
}
}
.w-e-bar-divider {
display: block
;
height: 1
px;
margin: 0
;
padding-top: 10
px;
width: 100%;
margin: 8px 0;
&:nth-of-type(16) {
height: 0;
}
// &:nth-child(odd) {
// height: 0;
// }
}
.w-auto {
display: block;
width: 100%;
height: 40px;
height: auto;
background: #fafafa;
font-size: 14px;
font-weight: 600;
line-height: 40px;
&.type-heading {
font-weight: 600;
}
}
.w-e-bar {
svg {
width: 18px;
height: 18px;
&:nth-child(2) {
.w-e-bar-item {
height: auto;
button {
svg:nth-child(2) {
display: none;
}
&.has-title {
width: 100%;
height: 100%;
padding: 10px;
flex-direction: column;
align-items: center;
.title {
margin: 5px 0 0 0;
}
}
}
}
.w-e-select-list {
z-index: 10001;
width: 180px !important;
}
.w-e-menu-tooltip-v5:before {
z-index: 10001;
}
}
.ant-spin-nested-loading,
.ant-spin-container {
...
...
@@ -417,7 +285,7 @@
}
.menu-tabs-key {
flex:
1
;
flex:
0 0 306px
;
background: #fafafa;
border: 1px solid #e5e5e5;
min-width: 300px;
...
...
src/common/wangeditor-customer/
ai/AI.js
→
src/common/wangeditor-customer/
menu/AIChat.jsx
浏览文件 @
f77be006
// Extend menu
class
AISelectTextAuto
{
import
BaseModalMenu
from
'./common/BaseModalMenu'
import
AIChatDrawer
from
'./common/AIChatDrawer'
class
AIChat
extends
BaseModalMenu
{
constructor
()
{
this
.
title
=
'AI'
;
this
.
iconSvg
=
`<svg t="1713246032755" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="12352" width="16" height="16"><path d="M826.368 325.632c0-7.168 2.048-10.24 10.24-10.24h123.904c7.168 0 10.24 2.048 10.24 10.24v621.568c0 7.168-2.048 10.24-10.24 10.24h-122.88c-8.192 0-10.24-4.096-10.24-10.24l-1.024-621.568z m-8.192-178.176c0-50.176 35.84-79.872 79.872-79.872 48.128 0 79.872 32.768 79.872 79.872 0 52.224-33.792 79.872-81.92 79.872-46.08 1.024-77.824-27.648-77.824-79.872zM462.848 584.704C441.344 497.664 389.12 307.2 368.64 215.04h-2.048c-16.384 92.16-58.368 247.808-92.16 369.664h188.416zM243.712 712.704l-62.464 236.544c-2.048 7.168-4.096 8.192-12.288 8.192H54.272c-8.192 0-10.24-2.048-8.192-12.288l224.256-783.36c4.096-13.312 7.168-26.624 8.192-65.536 0-6.144 2.048-8.192 7.168-8.192H450.56c6.144 0 8.192 2.048 10.24 8.192l250.88 849.92c2.048 7.168 0 10.24-7.168 10.24H573.44c-7.168 0-10.24-2.048-12.288-7.168l-65.536-236.544c1.024 1.024-251.904 0-251.904 0z" fill="#cdcdcd" p-id="12353"></path></svg>`
;
this
.
tag
=
'button'
;
super
()
this
.
title
=
'AI'
this
.
iconSvg
=
`<svg t="1713246032755" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="12352" width="16" height="16"><path d="M826.368 325.632c0-7.168 2.048-10.24 10.24-10.24h123.904c7.168 0 10.24 2.048 10.24 10.24v621.568c0 7.168-2.048 10.24-10.24 10.24h-122.88c-8.192 0-10.24-4.096-10.24-10.24l-1.024-621.568z m-8.192-178.176c0-50.176 35.84-79.872 79.872-79.872 48.128 0 79.872 32.768 79.872 79.872 0 52.224-33.792 79.872-81.92 79.872-46.08 1.024-77.824-27.648-77.824-79.872zM462.848 584.704C441.344 497.664 389.12 307.2 368.64 215.04h-2.048c-16.384 92.16-58.368 247.808-92.16 369.664h188.416zM243.712 712.704l-62.464 236.544c-2.048 7.168-4.096 8.192-12.288 8.192H54.272c-8.192 0-10.24-2.048-8.192-12.288l224.256-783.36c4.096-13.312 7.168-26.624 8.192-65.536 0-6.144 2.048-8.192 7.168-8.192H450.56c6.144 0 8.192 2.048 10.24 8.192l250.88 849.92c2.048 7.168 0 10.24-7.168 10.24H573.44c-7.168 0-10.24-2.048-12.288-7.168l-65.536-236.544c1.024 1.024-251.904 0-251.904 0z" fill="#cdcdcd" p-id="12353"></path></svg>`
this
.
tag
=
'button'
}
getValue
(
editor
)
{
return
'hello, AI'
;
}
isActive
(
editor
)
{
return
false
;
// or true
}
isDisabled
(
editor
)
{
return
false
;
// or true
}
exec
(
editor
,
value
)
{
// editor.insertText(value) // value 即 this.getValue(editor) 的返回值
if
(
this
.
isDisabled
(
editor
))
{
return
;
}
editor
.
emit
(
'AISelectTextClick'
);
return
<
AIChatDrawer
key=
{
Date
.
now
()
}
editor=
{
editor
}
></
AIChatDrawer
>
}
}
export
default
{
key
:
'AI
SelectTextAuto
'
,
// 定义 menu key :要保证唯一、不重复(重要)
key
:
'AI
Chat
'
,
// 定义 menu key :要保证唯一、不重复(重要)
factory
()
{
return
new
AISelectTextAuto
();
// 把 `YourMenuClass` 替换为你菜单的 class
},
};
export
{
AISelectTextAuto
};
return
new
AIChat
()
// 把 `YourMenuClass` 替换为你菜单的 class
}
}
src/common/wangeditor-customer/menu/AIContentInspect.jsx
0 → 100644
浏览文件 @
f77be006
import
BaseModalMenu
from
'./common/BaseModalMenu'
import
AIModal
from
'./common/AIModal'
class
AIContentInspect
extends
BaseModalMenu
{
constructor
()
{
super
()
this
.
title
=
'内容检查'
this
.
iconSvg
=
`<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><path fill="currentColor" d="M11.5 13.5q.525 0 .988-.137t.912-.413l1.525 1.575q.2.225.513.213t.537-.213q.225-.225.238-.537T16 13.45l-1.55-1.55q.275-.425.413-.9T15 10q0-1.475-1.038-2.488T11.5 6.5T9.037 7.513T8 10t1.038 2.488T11.5 13.5m0-1.5q-.825 0-1.412-.587T9.5 10t.588-1.412T11.5 8q.8 0 1.4.588T13.5 10t-.587 1.413T11.5 12M2 21q-.425 0-.712-.288T1 20t.288-.712T2 19h20q.425 0 .713.288T23 20t-.288.713T22 21zm2-3q-.825 0-1.412-.587T2 16V5q0-.825.588-1.412T4 3h16q.825 0 1.413.588T22 5v11q0 .825-.587 1.413T20 18zm0-2h16V5H4zm0 0V5z"/></svg>`
}
getValue
(
editor
)
{
return
<
AIModal
key=
{
Date
.
now
()
}
editor=
{
editor
}
docAction=
"contentInspect"
></
AIModal
>
}
}
export
default
{
key
:
'AIContentInspect'
,
factory
()
{
return
new
AIContentInspect
()
}
}
src/common/wangeditor-customer/menu/AIDigitalHuman.jsx
0 → 100644
浏览文件 @
f77be006
// Extend menu
class
AIDigitalHuman
{
constructor
()
{
this
.
title
=
'数字人'
this
.
iconSvg
=
`<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 28 28"><path fill="currentColor" d="M12 5.5a2 2 0 0 0 1.491 1.935c.337.053.68.053 1.018 0A2 2 0 1 0 12 5.5m-1.337 1.058a3.5 3.5 0 1 1 6.675 0l4.419-1.436a2.477 2.477 0 1 1 1.53 4.712L18 11.552v3.822c0 .16.03.32.091.468l2.728 6.752a2.477 2.477 0 0 1-4.594 1.856l-2.243-5.553l-2.232 5.56a2.46 2.46 0 0 1-3.21 1.362a2.477 2.477 0 0 1-1.364-3.215l2.734-6.812q.09-.224.09-.466v-3.774L4.712 9.834a2.477 2.477 0 0 1 1.531-4.712zm2.518 2.346a5 5 0 0 1-.649-.162L5.78 6.548a.977.977 0 0 0-.604 1.859l5.46 1.774c.515.168.864.648.864 1.189v3.957c0 .35-.067.698-.198 1.024l-2.734 6.811a.977.977 0 0 0 .538 1.267a.96.96 0 0 0 1.252-.531l2.463-6.136c.42-1.045 1.897-1.047 2.319-.003l2.476 6.129a.977.977 0 1 0 1.812-.732L16.7 16.404a2.8 2.8 0 0 1-.2-1.03V11.37c0-.541.349-1.021.864-1.189l5.46-1.774a.977.977 0 1 0-.604-1.859l-6.752 2.194q-.32.104-.649.162a3.5 3.5 0 0 1-1.639 0"/></svg>`
this
.
tag
=
'button'
}
getValue
()
{
return
'hello, 音频'
}
isActive
()
{
return
false
}
isDisabled
()
{
return
true
}
exec
()
{
return
}
}
export
default
{
key
:
'AIDigitalHuman'
,
factory
()
{
return
new
AIDigitalHuman
()
}
}
src/common/wangeditor-customer/
ai/ExpandArticle.js
→
src/common/wangeditor-customer/
menu/AIExpand.jsx
浏览文件 @
f77be006
// Extend menu
class
ExpandArticleAuto
{
import
BaseModalMenu
from
'./common/BaseModalMenu'
import
AIModal
from
'./common/AIModal'
class
AIExpand
extends
BaseModalMenu
{
constructor
()
{
this
.
title
=
'扩写'
;
super
()
this
.
title
=
'扩写'
this
.
iconSvg
=
`<svg width="48px" height="48px" viewBox="0 0 48 48" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>图标</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="图标" transform="translate(-622, -216)" fill="#333333" fill-rule="nonzero">
<g id="文章扩写" transform="translate(622, 216)">
...
...
@@ -11,32 +12,16 @@ class ExpandArticleAuto {
</g>
</g>
</g>
</svg>`
;
this
.
tag
=
'button'
;
</svg>`
}
getValue
(
editor
)
{
return
'hello, 音频'
;
}
isActive
(
editor
)
{
return
false
;
// or true
}
isDisabled
(
editor
)
{
return
false
;
// or true
}
exec
(
editor
,
value
)
{
// editor.insertText(value) // value 即 this.getValue(editor) 的返回值
if
(
this
.
isDisabled
(
editor
))
{
return
;
}
editor
.
emit
(
'ExpandArticleMenuClick'
);
return
<
AIModal
key=
{
Date
.
now
()
}
editor=
{
editor
}
docAction=
"expand"
></
AIModal
>
}
}
export
default
{
key
:
'
ExpandArticleAuto'
,
// 定义 menu key :要保证唯一、不重复(重要)
key
:
'
AIExpand'
,
factory
()
{
return
new
ExpandArticleAuto
();
// 把 `YourMenuClass` 替换为你菜单的 class
},
};
export
{
ExpandArticleAuto
};
return
new
AIExpand
()
}
}
src/common/wangeditor-customer/
ai/Polishing.js
→
src/common/wangeditor-customer/
menu/AIPolishing.jsx
浏览文件 @
f77be006
// Extend menu
class
PolishingAuto
{
constructor
()
{
this
.
title
=
'缩写'
;
this
.
iconSvg
=
`<svg width="48px" height="48px" viewBox="0 0 48 48" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>图标</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="图标" transform="translate(-536, -221)" fill="#333333" fill-rule="nonzero">
<g id="缩写-(1)" transform="translate(536, 221)">
<path d="M1.95471575,0 L44.9584623,0 C46.2616061,0 46.913178,0.678713837 46.913178,2.03614151 L46.913178,2.03614151 C46.913178,3.39356919 46.2616061,4.07228302 44.9584623,4.07228302 L1.95471575,4.07228302 C0.651571917,4.07228302 0,3.39356919 0,2.03614151 L0,2.03614151 C0,0.678713837 0.651571917,0 1.95471575,0 Z" id="路径"></path>
<path d="M1.95471575,12.2168491 L29.3207363,12.2168491 C30.6238801,12.2168491 31.275452,12.8955629 31.275452,14.2529906 L31.275452,14.2529906 C31.275452,15.6104183 30.6238801,16.2891321 29.3207363,16.2891321 L1.95471575,16.2891321 C0.651571917,16.2891321 0,15.6104183 0,14.2529906 L0,14.2529906 C0,12.8955629 0.651571917,12.2168491 1.95471575,12.2168491 Z" id="路径"></path>
<path d="M1.95471575,24.4336981 L17.5924418,24.4336981 C18.8955856,24.4336981 19.5471575,25.112412 19.5471575,26.4698397 L19.5471575,26.4698397 C19.5471575,27.8272673 18.8955856,28.5059812 17.5924418,28.5059812 L1.95471575,28.5059812 C0.651571917,28.5059812 0,27.8272673 0,26.4698397 L0,26.4698397 C0,25.112412 0.651571917,24.4336981 1.95471575,24.4336981 Z" id="路径"></path>
<path d="M44.5362437,34.9727666 L36.4437205,39.5744464 L32.0221534,48 L27.6005864,39.5744464 L19.5158821,34.9727666 L27.6005864,30.3670145 L32.0221534,21.9373886 L36.439811,30.3670145 L44.5323343,34.9686943 L44.5362437,34.9727666 Z M44.7238964,23.712904 L48,22.7151947 L47.0421893,26.1277679 L48,29.5484856 L44.7238964,28.5385594 L41.4438834,29.5444133 L42.4056035,26.1318402 L41.4438834,22.7151947 L44.719987,23.712904 L44.7238964,23.712904 Z M20.1453005,41.4762026 L22.5965141,40.7228302 L21.8771787,43.280224 L22.5926047,45.8376177 L20.1374817,45.0842454 L17.6862681,45.8376177 L18.4056035,43.280224 L17.6901776,40.7228302 L20.1413911,41.4721303 L20.1413911,41.4762026 L20.1453005,41.4762026 Z" id="形状"></path>
</g>
</g>
</g>
</svg>`
;
this
.
tag
=
'button'
;
}
getValue
(
editor
)
{
return
'hello, 缩写'
;
}
isActive
(
editor
)
{
return
false
;
// or true
}
isDisabled
(
editor
)
{
return
false
;
// or true
}
exec
(
editor
,
value
)
{
// editor.insertText(value) // value 即 this.getValue(editor) 的返回值
if
(
this
.
isDisabled
(
editor
))
{
return
;
}
editor
.
emit
(
'PolishingMenuClick'
);
}
}
export
default
{
key
:
'PolishingAuto'
,
// 定义 menu key :要保证唯一、不重复(重要)
factory
()
{
return
new
PolishingAuto
();
// 把 `YourMenuClass` 替换为你菜单的 class
},
};
export
{
PolishingAuto
};
import
BaseModalMenu
from
'./common/BaseModalMenu'
import
AIModal
from
'./common/AIModal'
class
AIPolishing
extends
BaseModalMenu
{
constructor
()
{
super
()
this
.
title
=
'润色'
this
.
iconSvg
=
`<svg width="48px" height="48px" viewBox="0 0 48 48" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="图标" transform="translate(-536, -221)" fill="#333333" fill-rule="nonzero">
<g id="润色-(1)" transform="translate(536, 221)">
<path d="M1.95471575,0 L44.9584623,0 C46.2616061,0 46.913178,0.678713837 46.913178,2.03614151 L46.913178,2.03614151 C46.913178,3.39356919 46.2616061,4.07228302 44.9584623,4.07228302 L1.95471575,4.07228302 C0.651571917,4.07228302 0,3.39356919 0,2.03614151 L0,2.03614151 C0,0.678713837 0.651571917,0 1.95471575,0 Z" id="路径"></path>
<path d="M1.95471575,12.2168491 L29.3207363,12.2168491 C30.6238801,12.2168491 31.275452,12.8955629 31.275452,14.2529906 L31.275452,14.2529906 C31.275452,15.6104183 30.6238801,16.2891321 29.3207363,16.2891321 L1.95471575,16.2891321 C0.651571917,16.2891321 0,15.6104183 0,14.2529906 L0,14.2529906 C0,12.8955629 0.651571917,12.2168491 1.95471575,12.2168491 Z" id="路径"></path>
<path d="M1.95471575,24.4336981 L17.5924418,24.4336981 C18.8955856,24.4336981 19.5471575,25.112412 19.5471575,26.4698397 L19.5471575,26.4698397 C19.5471575,27.8272673 18.8955856,28.5059812 17.5924418,28.5059812 L1.95471575,28.5059812 C0.651571917,28.5059812 0,27.8272673 0,26.4698397 L0,26.4698397 C0,25.112412 0.651571917,24.4336981 1.95471575,24.4336981 Z" id="路径"></path>
<path d="M44.5362437,34.9727666 L36.4437205,39.5744464 L32.0221534,48 L27.6005864,39.5744464 L19.5158821,34.9727666 L27.6005864,30.3670145 L32.0221534,21.9373886 L36.439811,30.3670145 L44.5323343,34.9686943 L44.5362437,34.9727666 Z M44.7238964,23.712904 L48,22.7151947 L47.0421893,26.1277679 L48,29.5484856 L44.7238964,28.5385594 L41.4438834,29.5444133 L42.4056035,26.1318402 L41.4438834,22.7151947 L44.719987,23.712904 L44.7238964,23.712904 Z M20.1453005,41.4762026 L22.5965141,40.7228302 L21.8771787,43.280224 L22.5926047,45.8376177 L20.1374817,45.0842454 L17.6862681,45.8376177 L18.4056035,43.280224 L17.6901776,40.7228302 L20.1413911,41.4721303 L20.1413911,41.4762026 L20.1453005,41.4762026 Z" id="形状"></path>
</g>
</g>
</g>
</svg>`
}
getValue
(
editor
)
{
return
<
AIModal
key=
{
Date
.
now
()
}
editor=
{
editor
}
docAction=
"abbreviate"
></
AIModal
>
}
}
export
default
{
key
:
'AIPolishing'
,
// 定义 menu key :要保证唯一、不重复(重要)
factory
()
{
return
new
AIPolishing
()
// 把 `YourMenuClass` 替换为你菜单的 class
}
}
src/common/wangeditor-customer/menu/AIPunctuation.jsx
0 → 100644
浏览文件 @
f77be006
import
BaseModalMenu
from
'./common/BaseModalMenu'
import
AIModal
from
'./common/AIModal'
class
AIPunctuation
extends
BaseModalMenu
{
constructor
()
{
super
()
this
.
title
=
'标点校对'
this
.
iconSvg
=
`<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 1024 1024"><path fill="currentColor" d="M920 416H616c-4.4 0-8 3.6-8 8v112c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8v-56h60v320h-46c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h164c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8h-46V480h60v56c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8V424c0-4.4-3.6-8-8-8M656 296V168c0-4.4-3.6-8-8-8H104c-4.4 0-8 3.6-8 8v128c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-64h168v560h-92c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h264c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-92V232h168v64c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8"/></svg>`
}
getValue
(
editor
)
{
return
<
AIModal
key=
{
Date
.
now
()
}
editor=
{
editor
}
docAction=
"punctuation"
></
AIModal
>
}
}
export
default
{
key
:
'AIPunctuation'
,
factory
()
{
return
new
AIPunctuation
()
}
}
src/common/wangeditor-customer/
ai/Rewrite.js
→
src/common/wangeditor-customer/
menu/AIRewrite.jsx
浏览文件 @
f77be006
// Extend menu
class
RewriteAuto
{
import
BaseModalMenu
from
'./common/BaseModalMenu'
import
AIModal
from
'./common/AIModal'
class
AIRewrite
extends
BaseModalMenu
{
constructor
()
{
this
.
title
=
'改写'
;
super
()
this
.
title
=
'改写'
this
.
iconSvg
=
`<svg width="48px" height="48px" viewBox="0 0 48 48" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="图标" transform="translate(-731, -218)" fill="#333333" fill-rule="nonzero">
...
...
@@ -11,32 +15,17 @@ class RewriteAuto {
</g>
</g>
</g>
</svg>`
;
this
.
tag
=
'button'
;
</svg>`
this
.
tag
=
'button'
}
getValue
(
editor
)
{
return
'hello, 音频'
;
}
isActive
(
editor
)
{
return
false
;
// or true
}
isDisabled
(
editor
)
{
return
false
;
// or true
}
exec
(
editor
,
value
)
{
// editor.insertText(value) // value 即 this.getValue(editor) 的返回值
if
(
this
.
isDisabled
(
editor
))
{
return
;
}
editor
.
emit
(
'RewriteMenuClick'
);
return
<
AIModal
key=
{
Date
.
now
()
}
editor=
{
editor
}
docAction=
"rewrite"
></
AIModal
>
}
}
export
default
{
key
:
'
RewriteAuto'
,
// 定义 menu key :要保证唯一、不重复(重要)
key
:
'
AIRewrite'
,
factory
()
{
return
new
RewriteAuto
();
// 把 `YourMenuClass` 替换为你菜单的 class
},
};
export
{
RewriteAuto
};
return
new
AIRewrite
()
}
}
src/common/wangeditor-customer/
ai/Summary.js
→
src/common/wangeditor-customer/
menu/AISummary.jsx
浏览文件 @
f77be006
// Extend menu
class
SummaryAuto
{
import
BaseModalMenu
from
'./common/BaseModalMenu'
import
AIModal
from
'./common/AIModal'
class
AISummary
extends
BaseModalMenu
{
constructor
()
{
this
.
title
=
'总结'
;
super
()
this
.
title
=
'总结'
this
.
iconSvg
=
`<svg width="48px" height="48px" viewBox="0 0 48 48" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>图标</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="图标" transform="translate(-821, -216)" fill="#333333" fill-rule="nonzero">
<g id="总结" transform="translate(821, 216)">
...
...
@@ -13,32 +16,16 @@ class SummaryAuto {
</g>
</g>
</g>
</svg>`
;
this
.
tag
=
'button'
;
</svg>`
}
getValue
(
editor
)
{
return
'hello, 音频'
;
}
isActive
(
editor
)
{
return
false
;
// or true
}
isDisabled
(
editor
)
{
return
false
;
// or true
}
exec
(
editor
,
value
)
{
// editor.insertText(value) // value 即 this.getValue(editor) 的返回值
if
(
this
.
isDisabled
(
editor
))
{
return
;
}
editor
.
emit
(
'SummaryMenuClick'
);
return
<
AIModal
key=
{
Date
.
now
()
}
editor=
{
editor
}
docAction=
"summary"
></
AIModal
>
}
}
export
default
{
key
:
'
SummaryAuto'
,
// 定义 menu key :要保证唯一、不重复(重要)
key
:
'
AISummary'
,
factory
()
{
return
new
SummaryAuto
();
// 把 `YourMenuClass` 替换为你菜单的 class
},
};
export
{
SummaryAuto
};
return
new
AISummary
()
}
}
src/common/wangeditor-customer/menu/common/AIChatDrawer.jsx
0 → 100644
浏览文件 @
f77be006
import
{
useState
,
useEffect
}
from
'react'
import
{
SendOutlined
}
from
'@ant-design/icons'
import
{
Drawer
,
Input
,
Button
,
message
}
from
'antd'
import
dayjs
from
'dayjs'
import
{
useAIChat
}
from
'./useAIChat'
import
normalAvatar
from
'@/assets/images/icon-normal-avatar.png'
import
'./AIChatDrawer.less'
const
AIChatDrawer
=
props
=>
{
const
selectText
=
props
.
editor
.
getSelectionText
()
const
[
open
,
setOpen
]
=
useState
(
true
)
const
{
messages
,
post
,
isLoading
}
=
useAIChat
()
const
[
value
,
setValue
]
=
useState
(
selectText
)
// 输入值
useEffect
(()
=>
{
if
(
selectText
)
{
targetChat
()
}
return
()
=>
{
setValue
(
''
)
}
},
[
selectText
])
const
element
=
document
.
querySelector
(
'#chat-content'
)
useEffect
(()
=>
{
element
?.
scrollTo
(
0
,
999999
)
},
[
messages
,
element
])
// 提交对话
const
targetChat
=
async
()
=>
{
if
(
value
)
{
post
({
userChatInput
:
value
})
setValue
(
''
)
}
else
{
message
.
error
(
'请输入对话内容!'
)
}
}
return
(
<
Drawer
open=
{
open
}
width=
"600px"
title=
"AI对话"
destroyOnClose
onClose=
{
()
=>
setOpen
(
false
)
}
rootClassName=
"ai-drawer-wrapper"
className=
"ai-drawer-container"
>
<
div
className=
"ai-drawer-content"
>
<
div
className=
"ai-chat-container"
>
<
div
className=
"chat-content"
id=
"chat-content"
>
<
div
className=
"chat-content-padd"
>
{
messages
.
map
((
item
,
index
)
=>
{
return
(
<
div
className=
{
`chat-content-item`
}
key=
{
index
}
>
{
item
.
role_type
===
'user'
&&
<
div
className=
"time"
>
{
dayjs
(
item
.
time
).
format
(
'YYYY-MM-DD HH:mm'
)
}
</
div
>
}
{
item
.
role_type
===
'ai'
&&
(
<
div
className=
"inside"
>
<
div
className=
"ai-in-content"
>
<
div
className=
"img"
>
<
img
src=
{
item
.
role
?.
avatar
}
/>
</
div
>
<
div
className=
"ask-content"
>
{
item
.
content
}
</
div
>
</
div
>
</
div
>
)
}
{
item
.
role_type
===
'user'
&&
(
<
div
className=
"inside inside-user"
>
<
div
className=
"user-in-content"
>
<
div
className=
"ask-content"
>
{
item
.
content
}
</
div
>
<
div
className=
"img"
>
<
img
src=
{
normalAvatar
}
/>
</
div
>
</
div
>
</
div
>
)
}
</
div
>
)
})
}
</
div
>
</
div
>
</
div
>
<
div
className=
"ai-chat-ask"
>
<
div
className=
"text"
>
<
Input
.
TextArea
value=
{
value
}
defaultValue=
{
value
}
allowClear
disabled=
{
isLoading
}
onChange=
{
e
=>
setValue
(
e
.
target
.
value
)
}
placeholder=
"请输入内容"
style=
{
{
height
:
'80px'
,
resize
:
'none'
}
}
></
Input
.
TextArea
>
</
div
>
<
div
className=
"button"
>
<
Button
type=
"primary"
icon=
{
<
SendOutlined
/>
}
loading=
{
isLoading
}
size=
"large"
onClick=
{
targetChat
}
></
Button
>
</
div
>
</
div
>
</
div
>
</
Drawer
>
)
}
export
default
AIChatDrawer
src/common/wangeditor-customer/
ai-drawer/index
.less
→
src/common/wangeditor-customer/
menu/common/AIChatDrawer
.less
浏览文件 @
f77be006
...
...
@@ -58,7 +58,7 @@
border-radius: 5px;
}
}
.inside-user {
display: flex;
justify-content: flex-end;
...
...
@@ -102,7 +102,7 @@
flex: 1;
}
.button {
flex: 0 0
6
0px;
flex: 0 0
4
0px;
padding-left: 10px;
.ant-btn {
height: 80px;
...
...
src/common/wangeditor-customer/
components/aiWrite
.jsx
→
src/common/wangeditor-customer/
menu/common/AIModal
.jsx
浏览文件 @
f77be006
...
...
@@ -6,25 +6,27 @@ const { TextArea } = Input
const
actionMap
=
{
rewrite
:
{
name
:
'改写'
,
prompt
:
'帮我改写以下文字内容:'
},
expand
:
{
name
:
'扩写'
,
prompt
:
'帮我在以下文字内容基础上进行扩写:'
},
abbreviate
:
{
name
:
'缩写'
,
prompt
:
'帮我缩写以下文字内容:'
},
summary
:
{
name
:
'总结'
,
prompt
:
'帮我总结以下文字内容:'
}
abbreviate
:
{
name
:
'润色'
,
prompt
:
'帮我润色以下文字内容:'
},
summary
:
{
name
:
'总结'
,
prompt
:
'帮我总结以下文字内容:'
},
punctuation
:
{
name
:
'标点校对'
,
prompt
:
'帮我校对以下文字内容中的标点符号:'
},
contentInspect
:
{
name
:
'内容检查'
,
prompt
:
'帮我检查以下文字内容中的敏感词和错别字:'
}
}
export
default
function
AIWrite
({
editor
,
docAction
,
...
rest
})
{
export
default
function
AIModal
({
editor
,
docAction
})
{
const
[
isModalOpen
,
setIsModalOpen
]
=
useState
(
true
)
const
[
content
,
setContent
]
=
useState
(
''
)
const
{
text
,
fetch
,
isLoading
}
=
useAIEdit
()
const
actionText
=
actionMap
[
docAction
]?.
name
const
[
selectionText
,
setSelectionText
]
=
useState
(
''
)
useEffect
(()
=>
{
if
(
rest
.
open
)
{
const
selection
=
editor
.
getSelectionText
()
if
(
selection
)
{
setSelectionText
(
selection
)
setContent
(
selection
)
fetch
({
messages
:
[{
role
:
'user'
,
content
:
actionMap
[
docAction
].
prompt
+
selection
}]
})
}
const
selection
=
editor
.
getSelectionText
()
if
(
selection
)
{
setSelectionText
(
selection
)
setContent
(
selection
)
fetch
({
messages
:
[{
role
:
'user'
,
content
:
actionMap
[
docAction
].
prompt
+
selection
}]
})
}
},
[
rest
.
open
])
},
[
docAction
,
editor
,
fetch
])
useEffect
(()
=>
{
setContent
(
text
)
...
...
@@ -37,21 +39,19 @@ export default function AIWrite({ editor, docAction, ...rest }) {
const
handlePrimary
=
()
=>
{
editor
.
restoreSelection
()
editor
.
insertText
(
text
)
rest
.
onCancel
(
)
setIsModalOpen
(
false
)
}
return
(
<
Modal
title=
{
`以下是AI${actionText}结果:`
}
{
...
rest
}
open=
{
isModalOpen
}
footer=
{
null
}
classNames=
{
{
header
:
'editor-header-customer'
,
body
:
'editor-body-customer'
,
wrapper
:
'editor-wrapper-customer'
}
}
>
classNames=
{
{
header
:
'editor-header-customer'
,
body
:
'editor-body-customer'
,
wrapper
:
'editor-wrapper-customer'
}
}
onOk=
{
handlePrimary
}
onCancel=
{
()
=>
setIsModalOpen
(
false
)
}
>
<
Spin
spinning=
{
isLoading
}
>
<
TextArea
autoSize=
{
{
minRows
:
4
}
}
value=
{
content
}
onChange=
{
(
e
)
=>
setContent
(
e
.
target
.
value
)
}
/>
<
TextArea
autoSize=
{
{
minRows
:
4
}
}
value=
{
content
}
onChange=
{
e
=>
setContent
(
e
.
target
.
value
)
}
/>
</
Spin
>
<
br
/>
<
Flex
gap=
"small"
justify=
"center"
>
...
...
@@ -61,7 +61,7 @@ export default function AIWrite({ editor, docAction, ...rest }) {
<
Button
type=
"primary"
onClick=
{
handleFetch
}
>
重新
{
actionText
}
</
Button
>
<
Button
type=
"primary"
onClick=
{
rest
.
onCancel
}
>
<
Button
type=
"primary"
onClick=
{
()
=>
setIsModalOpen
(
false
)
}
>
取消
</
Button
>
</
Flex
>
...
...
src/common/wangeditor-customer/menu/common/BaseModalMenu.jsx
0 → 100644
浏览文件 @
f77be006
import
ReactDOM
from
'react-dom/client'
import
{
ConfigProvider
,
App
}
from
'antd'
import
zhCN
from
'antd/locale/zh_CN'
export
default
class
BaseModalMenu
{
constructor
()
{
this
.
tag
=
'button'
this
.
$el
=
document
.
createElement
(
'div'
)
document
.
body
.
appendChild
(
this
.
$el
)
this
.
$root
=
ReactDOM
.
createRoot
(
this
.
$el
)
}
isActive
()
{
return
false
// or true
}
isDisabled
()
{
return
false
// or true
}
exec
(
editor
,
value
)
{
this
.
$root
.
render
(
<
ConfigProvider
locale=
{
zhCN
}
theme=
{
{
token
:
{
colorPrimary
:
'#ab1941'
}
}
}
>
<
App
>
{
value
}
</
App
>
</
ConfigProvider
>
)
}
}
src/common/wangeditor-customer/menu/common/useAIChat.js
0 → 100644
浏览文件 @
f77be006
import
md5
from
'js-md5'
import
{
fetchEventSource
}
from
'@fortaine/fetch-event-source'
import
{
useState
,
useCallback
}
from
'react'
export
function
useAIChat
()
{
const
authKey
=
'f3846153ba784b6d86bdcd5533259c88'
const
authSecret
=
'HO4IyLEwEOHpeOXBxaLQUOqWslJRGs1M'
const
[
messages
,
setMessages
]
=
useState
([])
const
[
chatId
,
setChatId
]
=
useState
(
null
)
const
[
isLoading
,
setIsLoading
]
=
useState
(
false
)
const
addMessage
=
useCallback
(
message
=>
{
setMessages
(
prevMessages
=>
[...
prevMessages
,
message
])
},
[])
const
updateMessages
=
useCallback
(
newMessage
=>
{
setMessages
(
prevMessages
=>
{
const
existingMessage
=
prevMessages
.
find
(
msg
=>
msg
.
conversationId
===
newMessage
.
conversationId
)
const
content
=
newMessage
.
content
===
'
\
n'
?
'<br/>'
:
newMessage
.
content
||
''
if
(
existingMessage
)
{
// 更新现有消息
return
prevMessages
.
map
(
msg
=>
(
msg
.
conversationId
===
newMessage
.
conversationId
?
{
...
msg
,
content
:
msg
.
content
+
content
}
:
msg
))
}
else
{
// 新增消息
return
[...
prevMessages
,
{
...
newMessage
,
content
,
role_type
:
'ai'
,
time
:
Date
.
now
()
}]
}
})
},
[])
const
post
=
useCallback
(
async
data
=>
{
const
timestamp
=
Date
.
now
()
const
sign
=
md5
(
`
${
authKey
}${
authSecret
}${
timestamp
}
`
)
// 插入用户消息
addMessage
({
role_type
:
'user'
,
content
:
data
.
userChatInput
,
conversationId
:
`user-
${
timestamp
}
`
,
// 生成一个唯一的 conversationId
time
:
Date
.
now
()
})
setIsLoading
(
true
)
try
{
await
fetchEventSource
(
'/api/tiangong/openapi/agent/chat/stream/v1'
,
{
method
:
'POST'
,
headers
:
{
authKey
,
timestamp
,
sign
,
'Content-Type'
:
'application/json'
},
body
:
JSON
.
stringify
({
...
data
,
chatId
,
agentId
:
authKey
}),
onopen
(
response
)
{
if
(
!
response
.
ok
)
{
throw
new
Error
(
'Network response was not ok'
)
}
},
onmessage
(
event
)
{
const
message
=
JSON
.
parse
(
event
.
data
)
setChatId
(
message
.
chatId
)
updateMessages
(
message
)
},
onerror
(
error
)
{
console
.
error
(
'Fetch error:'
,
error
)
},
onclose
()
{
setIsLoading
(
false
)
}
})
}
catch
(
error
)
{
console
.
error
(
'Post error:'
,
error
)
setIsLoading
(
false
)
}
},
[
authKey
,
authSecret
,
chatId
,
addMessage
,
updateMessages
]
)
return
{
messages
,
isLoading
,
post
}
}
src/common/wangeditor-customer/utils/setting.js
浏览文件 @
f77be006
...
...
@@ -4,10 +4,10 @@ export const fontFamilyList = [
{
name
:
'黑体'
,
value
:
'黑体'
},
{
name
:
'宋体'
,
value
:
'宋体'
},
{
name
:
'楷体'
,
value
:
'楷体'
},
{
name
:
'仿宋'
,
value
:
'仿宋'
}
,
]
;
{
name
:
'仿宋'
,
value
:
'仿宋'
}
]
export
const
font
F
izeList
=
[
export
const
font
S
izeList
=
[
{
name
:
'初号'
,
value
:
56
},
{
name
:
'小初'
,
value
:
48
},
{
name
:
'一号'
,
value
:
34
},
...
...
@@ -23,55 +23,58 @@ export const fontFizeList = [
{
name
:
'六号'
,
value
:
10
},
{
name
:
'小六'
,
value
:
8
},
{
name
:
'七号'
,
value
:
7
},
{
name
:
'八号'
,
value
:
6
},
];
{
name
:
'八号'
,
value
:
6
}
]
export
const
lineHeightList
=
[
'1'
,
'1.25'
,
'1.5'
,
'2'
,
'2.5'
,
'3'
]
export
const
findElementPath
=
(
editor
,
elementType
)
=>
{
const
{
children
}
=
editor
;
const
{
children
}
=
editor
// 递归函数来遍历节点
function
traverseNodes
(
node
)
{
if
(
Array
.
isArray
(
node
))
{
for
(
let
i
=
0
;
i
<
node
.
length
;
i
++
)
{
const
child
=
node
[
i
]
;
traverseNodes
(
child
)
;
const
child
=
node
[
i
]
traverseNodes
(
child
)
if
(
child
.
type
===
elementType
)
{
// 当找到匹配的元素类型时,返回其路径
return
[
i
,
...
traverseNodes
(
child
)]
;
return
[
i
,
...
traverseNodes
(
child
)]
}
}
}
else
if
(
node
.
type
===
elementType
)
{
// 如果当前节点是元素节点且类型匹配,则返回其路径
return
[
traverseNodes
(
node
.
parent
)]
;
return
[
traverseNodes
(
node
.
parent
)]
}
return
null
;
return
null
}
// 从文档的根节点开始遍历
const
path
=
traverseNodes
(
children
)
;
return
path
;
}
;
const
path
=
traverseNodes
(
children
)
return
path
}
export
const
findNodeWithParent
=
(
node
,
type
,
key
=
'random'
,
value
)
=>
{
const
path
=
[]
;
// 用于存储当前节点的路径
const
path
=
[]
// 用于存储当前节点的路径
// 递归遍历节点的辅助函数
function
traverse
(
nodes
,
type
,
key
,
value
)
{
for
(
let
i
=
0
;
i
<
nodes
.
length
;
i
++
)
{
const
current
=
nodes
[
i
]
;
const
current
=
nodes
[
i
]
if
(
current
.
type
===
type
&&
String
(
current
[
key
])
===
String
(
value
))
{
// 找到匹配的节点,将索引添加到路径数组中
path
.
push
(
i
)
;
return
true
;
path
.
push
(
i
)
return
true
}
if
(
Array
.
isArray
(
current
.
children
)
&&
traverse
(
current
.
children
,
type
,
key
,
value
))
{
// 如果子节点中找到匹配的节点,添加当前节点的索引到路径
path
.
push
(
i
)
;
return
true
;
path
.
push
(
i
)
return
true
}
}
return
false
;
// 未找到匹配的节点
return
false
// 未找到匹配的节点
}
// 从根节点开始遍历
traverse
(
node
,
type
,
key
,
value
)
;
return
path
;
// 返回找到的路径,如果没有找到则返回空数组
}
;
traverse
(
node
,
type
,
key
,
value
)
return
path
// 返回找到的路径,如果没有找到则返回空数组
}
src/components/editor/index.jsx
浏览文件 @
f77be006
差异被折叠。
点击展开。
src/components/editor/styles.less
0 → 100644
浏览文件 @
f77be006
差异被折叠。
点击展开。
src/pages/books/section/index.jsx
浏览文件 @
f77be006
差异被折叠。
点击展开。
src/pages/editor/index.jsx
0 → 100644
浏览文件 @
f77be006
差异被折叠。
点击展开。
src/routes/routes.jsx
浏览文件 @
f77be006
差异被折叠。
点击展开。
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论