Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
phsl
/
admin
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
8d4c9e9c
authored
Jul 30, 2024
by
hhhero
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[代码优化]AI: 思维导图
parent
ed36d2bf
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
45 additions
and
41 deletions
+45
-41
src/utils/download.ts
+20
-5
src/views/ai/image/index/components/ImageList.vue
+1
-1
src/views/ai/mindmap/index/components/Right.vue
+17
-28
src/views/ai/mindmap/index/index.vue
+7
-7
No files found.
src/utils/download.ts
View file @
8d4c9e9c
...
@@ -34,16 +34,31 @@ const download = {
...
@@ -34,16 +34,31 @@ const download = {
download0
(
data
,
fileName
,
'text/markdown'
)
download0
(
data
,
fileName
,
'text/markdown'
)
},
},
// 下载图片(允许跨域)
// 下载图片(允许跨域)
image
:
(
url
:
string
)
=>
{
image
:
({
url
,
canvasWidth
,
canvasHeight
,
drawWithImageSize
=
true
}:
{
url
:
string
canvasWidth
?:
number
// 指定画布宽度
canvasHeight
?:
number
// 指定画布高度
drawWithImageSize
?:
boolean
// 将图片绘制在画布上时带上图片的宽高值, 默认是要带上的
})
=>
{
const
image
=
new
Image
()
const
image
=
new
Image
()
image
.
setAttribute
(
'crossOrigin'
,
'anonymous'
)
//
image.setAttribute('crossOrigin', 'anonymous')
image
.
src
=
url
image
.
src
=
url
image
.
onload
=
()
=>
{
image
.
onload
=
()
=>
{
const
canvas
=
document
.
createElement
(
'canvas'
)
const
canvas
=
document
.
createElement
(
'canvas'
)
canvas
.
width
=
image
.
width
canvas
.
width
=
canvasWidth
||
image
.
width
canvas
.
height
=
image
.
height
canvas
.
height
=
canvasHeight
||
image
.
height
const
ctx
=
canvas
.
getContext
(
'2d'
)
as
CanvasDrawImage
const
ctx
=
canvas
.
getContext
(
'2d'
)
as
CanvasRenderingContext2D
ctx
?.
clearRect
(
0
,
0
,
canvas
.
width
,
canvas
.
height
)
if
(
drawWithImageSize
)
{
ctx
.
drawImage
(
image
,
0
,
0
,
image
.
width
,
image
.
height
)
ctx
.
drawImage
(
image
,
0
,
0
,
image
.
width
,
image
.
height
)
}
else
{
ctx
.
drawImage
(
image
,
0
,
0
)
}
const
url
=
canvas
.
toDataURL
(
'image/png'
)
const
url
=
canvas
.
toDataURL
(
'image/png'
)
const
a
=
document
.
createElement
(
'a'
)
const
a
=
document
.
createElement
(
'a'
)
a
.
href
=
url
a
.
href
=
url
...
...
src/views/ai/image/index/components/ImageList.vue
View file @
8d4c9e9c
...
@@ -150,7 +150,7 @@ const handleImageButtonClick = async (type: string, imageDetail: ImageVO) => {
...
@@ -150,7 +150,7 @@ const handleImageButtonClick = async (type: string, imageDetail: ImageVO) => {
}
}
// 下载
// 下载
if
(
type
===
'download'
)
{
if
(
type
===
'download'
)
{
await
download
.
image
(
imageDetail
.
picUrl
)
await
download
.
image
(
{
url
:
imageDetail
.
picUrl
}
)
return
return
}
}
// 重新生成
// 重新生成
...
...
src/views/ai/mindmap/index/components/Right.vue
View file @
8d4c9e9c
...
@@ -19,7 +19,7 @@
...
@@ -19,7 +19,7 @@
<div
class=
"flex flex-col items-center justify-center"
v-html=
"html"
></div>
<div
class=
"flex flex-col items-center justify-center"
v-html=
"html"
></div>
</div>
</div>
<div
ref=
"mind
m
apRef"
class=
"wh-full"
>
<div
ref=
"mind
M
apRef"
class=
"wh-full"
>
<svg
ref=
"svgRef"
class=
"w-full"
:style=
"{ height: `${contentAreaHeight}px` }"
/>
<svg
ref=
"svgRef"
class=
"w-full"
:style=
"{ height: `${contentAreaHeight}px` }"
/>
<div
ref=
"toolBarRef"
class=
"absolute bottom-[10px] right-5"
></div>
<div
ref=
"toolBarRef"
class=
"absolute bottom-[10px] right-5"
></div>
</div>
</div>
...
@@ -32,20 +32,20 @@ import { Markmap } from 'markmap-view'
...
@@ -32,20 +32,20 @@ import { Markmap } from 'markmap-view'
import
{
Transformer
}
from
'markmap-lib'
import
{
Transformer
}
from
'markmap-lib'
import
{
Toolbar
}
from
'markmap-toolbar'
import
{
Toolbar
}
from
'markmap-toolbar'
import
markdownit
from
'markdown-it'
import
markdownit
from
'markdown-it'
import
download
from
'@/utils/download'
const
md
=
markdownit
()
const
md
=
markdownit
()
const
message
=
useMessage
()
// 消息弹窗
const
message
=
useMessage
()
// 消息弹窗
// TODO @hhero:mindmap 改成 mindMap 更精准哈
const
props
=
defineProps
<
{
const
props
=
defineProps
<
{
mindmapResult
:
string
// 生成结果 TODO @hhero 改成 generatedContent 会不会好点
generatedContent
:
string
// 生成结果
isEnd
:
boolean
// 是否结束
isEnd
:
boolean
// 是否结束
isGenerating
:
boolean
// 是否正在生成
isGenerating
:
boolean
// 是否正在生成
isStart
:
boolean
// 开始状态,开始时需要清除 html
isStart
:
boolean
// 开始状态,开始时需要清除 html
}
>
()
}
>
()
const
contentRef
=
ref
<
HTMLDivElement
>
()
// 右侧出来header以下的区域
const
contentRef
=
ref
<
HTMLDivElement
>
()
// 右侧出来header以下的区域
const
mdContainerRef
=
ref
<
HTMLDivElement
>
()
// markdown 的容器,用来滚动到底下的
const
mdContainerRef
=
ref
<
HTMLDivElement
>
()
// markdown 的容器,用来滚动到底下的
const
mind
m
apRef
=
ref
<
HTMLDivElement
>
()
// 思维导图的容器
const
mind
M
apRef
=
ref
<
HTMLDivElement
>
()
// 思维导图的容器
const
svgRef
=
ref
<
SVGElement
>
()
// 思维导图的渲染 svg
const
svgRef
=
ref
<
SVGElement
>
()
// 思维导图的渲染 svg
const
toolBarRef
=
ref
<
HTMLDivElement
>
()
// 思维导图右下角的工具栏,缩放等
const
toolBarRef
=
ref
<
HTMLDivElement
>
()
// 思维导图右下角的工具栏,缩放等
const
html
=
ref
(
''
)
// 生成过程中的文本
const
html
=
ref
(
''
)
// 生成过程中的文本
...
@@ -66,15 +66,16 @@ onMounted(() => {
...
@@ -66,15 +66,16 @@ onMounted(() => {
}
}
})
})
watch
(
props
,
({
mindmapResul
t
,
isGenerating
,
isEnd
,
isStart
})
=>
{
watch
(
props
,
({
generatedConten
t
,
isGenerating
,
isEnd
,
isStart
})
=>
{
// 开始生成的时候清空一下 markdown 的内容
// 开始生成的时候清空一下 markdown 的内容
if
(
isStart
)
{
if
(
isStart
)
{
html
.
value
=
''
html
.
value
=
''
}
}
// 生成内容的时候使用 markdown 来渲染
// 生成内容的时候使用 markdown 来渲染
if
(
isGenerating
)
{
if
(
isGenerating
)
{
html
.
value
=
md
.
render
(
mindmapResul
t
)
html
.
value
=
md
.
render
(
generatedConten
t
)
}
}
// 生成结束时更新思维导图
if
(
isEnd
)
{
if
(
isEnd
)
{
update
()
update
()
}
}
...
@@ -83,7 +84,7 @@ watch(props, ({ mindmapResult, isGenerating, isEnd, isStart }) => {
...
@@ -83,7 +84,7 @@ watch(props, ({ mindmapResult, isGenerating, isEnd, isStart }) => {
/** 更新思维导图的展示 */
/** 更新思维导图的展示 */
const
update
=
()
=>
{
const
update
=
()
=>
{
try
{
try
{
const
{
root
}
=
transformer
.
transform
(
processContent
(
props
.
mindmapResul
t
))
const
{
root
}
=
transformer
.
transform
(
processContent
(
props
.
generatedConten
t
))
markMap
?.
setData
(
root
)
markMap
?.
setData
(
root
)
markMap
?.
fit
()
markMap
?.
fit
()
}
catch
(
e
)
{
}
catch
(
e
)
{
...
@@ -106,31 +107,19 @@ const processContent = (text: string) => {
...
@@ -106,31 +107,19 @@ const processContent = (text: string) => {
}
}
/** 下载图片 */
/** 下载图片 */
// TODO @hhhero:可以抽到 download 这个里面,src/utils/download.ts 么?复用 image 方法?
// download SVG to png file
// download SVG to png file
const
downloadImage
=
()
=>
{
const
downloadImage
=
()
=>
{
const
svgElement
=
mind
m
apRef
.
value
const
svgElement
=
mind
M
apRef
.
value
// 将 SVG 渲染到图片对象
// 将 SVG 渲染到图片对象
const
serializer
=
new
XMLSerializer
()
const
serializer
=
new
XMLSerializer
()
const
source
=
const
source
=
`<?xml version="1.0" standalone="no"?>\r\n
${
serializer
.
serializeToString
(
svgRef
.
value
!
)}
`
'
<
?
xml
version
=
"1.0"
standalone
=
"no"
?
>
\
r
\
n
' + serializer.serializeToString(svgRef.value!)
const
base64Url
=
`data:image/svg+xml;charset=utf-8,
${
encodeURIComponent
(
source
)}
`
const image = new Image()
download
.
image
({
image.src = '
data
:
image
/
svg
+
xml
;
charset
=
utf
-
8
,
' + encodeURIComponent(source)
url
:
base64Url
,
canvasWidth
:
svgElement
?.
offsetWidth
,
// 将图片对象渲染
canvasHeight
:
svgElement
?.
offsetHeight
,
const canvas = document.createElement('
canvas
')
drawWithImageSize
:
false
canvas.width = svgElement?.offsetWidth || 0
})
canvas.height = svgElement?.offsetHeight || 0
let context = canvas.getContext('
2
d
')
context?.clearRect(0, 0, canvas.width, canvas.height)
image.onload = function () {
context?.drawImage(image, 0, 0)
const a = document.createElement('
a
')
a.download = '
mindmap
.
png
'
a
.
href
=
canvas
.
toDataURL
(
`image/png`
)
a
.
click
()
}
}
}
defineExpose
({
defineExpose
({
...
...
src/views/ai/mindmap/index/index.vue
View file @
8d4c9e9c
...
@@ -10,7 +10,7 @@
...
@@ -10,7 +10,7 @@
<!--右边生成思维导图区域-->
<!--右边生成思维导图区域-->
<Right
<Right
ref=
"rightRef"
ref=
"rightRef"
:
mindmapResult=
"mindmapResul
t"
:
generatedContent=
"generatedConten
t"
:isEnd=
"isEnd"
:isEnd=
"isEnd"
:isGenerating=
"isGenerating"
:isGenerating=
"isGenerating"
:isStart=
"isStart"
:isStart=
"isStart"
...
@@ -33,7 +33,7 @@ const isStart = ref(false) // 开始生成,用来清空思维导图
...
@@ -33,7 +33,7 @@ const isStart = ref(false) // 开始生成,用来清空思维导图
const
isEnd
=
ref
(
true
)
// 用来判断结束的时候渲染思维导图
const
isEnd
=
ref
(
true
)
// 用来判断结束的时候渲染思维导图
const
message
=
useMessage
()
// 消息提示
const
message
=
useMessage
()
// 消息提示
const
mindmapResul
t
=
ref
(
''
)
// 生成思维导图结果
const
generatedConten
t
=
ref
(
''
)
// 生成思维导图结果
const
leftRef
=
ref
<
InstanceType
<
typeof
Left
>>
()
// 左边组件
const
leftRef
=
ref
<
InstanceType
<
typeof
Left
>>
()
// 左边组件
const
rightRef
=
ref
<
InstanceType
<
typeof
Right
>>
()
// 右边组件
const
rightRef
=
ref
<
InstanceType
<
typeof
Right
>>
()
// 右边组件
...
@@ -41,7 +41,7 @@ const rightRef = ref<InstanceType<typeof Right>>() // 右边组件
...
@@ -41,7 +41,7 @@ const rightRef = ref<InstanceType<typeof Right>>() // 右边组件
/** 使用已有内容直接生成 **/
/** 使用已有内容直接生成 **/
const
directGenerate
=
(
existPrompt
:
string
)
=>
{
const
directGenerate
=
(
existPrompt
:
string
)
=>
{
isEnd
.
value
=
false
// 先设置为false再设置为true,让子组建的watch能够监听到
isEnd
.
value
=
false
// 先设置为false再设置为true,让子组建的watch能够监听到
mindmapResul
t
.
value
=
existPrompt
generatedConten
t
.
value
=
existPrompt
isEnd
.
value
=
true
isEnd
.
value
=
true
}
}
...
@@ -58,7 +58,7 @@ const submit = (data: AiMindMapGenerateReqVO) => {
...
@@ -58,7 +58,7 @@ const submit = (data: AiMindMapGenerateReqVO) => {
isStart
.
value
=
true
isStart
.
value
=
true
isEnd
.
value
=
false
isEnd
.
value
=
false
ctrl
.
value
=
new
AbortController
()
// 请求控制赋值
ctrl
.
value
=
new
AbortController
()
// 请求控制赋值
mindmapResul
t
.
value
=
''
// 清空生成数据
generatedConten
t
.
value
=
''
// 清空生成数据
AiMindMapApi
.
generateMindMap
({
AiMindMapApi
.
generateMindMap
({
data
,
data
,
onMessage
:
async
(
res
)
=>
{
onMessage
:
async
(
res
)
=>
{
...
@@ -68,13 +68,13 @@ const submit = (data: AiMindMapGenerateReqVO) => {
...
@@ -68,13 +68,13 @@ const submit = (data: AiMindMapGenerateReqVO) => {
stopStream
()
stopStream
()
return
return
}
}
mindmapResult
.
value
=
mindmapResul
t
.
value
+
data
generatedContent
.
value
=
generatedConten
t
.
value
+
data
await
nextTick
()
await
nextTick
()
rightRef
.
value
?.
scrollBottom
()
rightRef
.
value
?.
scrollBottom
()
},
},
onClose
()
{
onClose
()
{
isEnd
.
value
=
true
isEnd
.
value
=
true
leftRef
.
value
?.
setGeneratedContent
(
mindmapResul
t
.
value
)
leftRef
.
value
?.
setGeneratedContent
(
generatedConten
t
.
value
)
stopStream
()
stopStream
()
},
},
onError
(
err
)
{
onError
(
err
)
{
...
@@ -87,6 +87,6 @@ const submit = (data: AiMindMapGenerateReqVO) => {
...
@@ -87,6 +87,6 @@ const submit = (data: AiMindMapGenerateReqVO) => {
/** 初始化 */
/** 初始化 */
onMounted
(()
=>
{
onMounted
(()
=>
{
mindmapResul
t
.
value
=
MindMapContentExample
generatedConten
t
.
value
=
MindMapContentExample
})
})
</
script
>
</
script
>
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment