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
5730707b
authored
Jul 09, 2024
by
hhhero
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[代码优化]AI: 写作添加注释,增加可读性,调整代码,方便后续调整
parent
62246021
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
131 additions
and
151 deletions
+131
-151
src/api/ai/writer/index.ts
+9
-32
src/views/ai/utils/constants.ts
+8
-0
src/views/ai/utils/utils.ts
+13
-0
src/views/ai/writer/components/Left.vue
+42
-50
src/views/ai/writer/components/Right.vue
+15
-14
src/views/ai/writer/data.json
+0
-11
src/views/ai/writer/index/index.vue
+44
-44
No files found.
src/api/ai/writer/index.ts
View file @
5730707b
...
@@ -3,37 +3,14 @@ import { fetchEventSource } from '@microsoft/fetch-event-source'
...
@@ -3,37 +3,14 @@ import { fetchEventSource } from '@microsoft/fetch-event-source'
import
{
getAccessToken
}
from
'@/utils/auth'
import
{
getAccessToken
}
from
'@/utils/auth'
import
{
config
}
from
'@/config/axios/config'
import
{
config
}
from
'@/config/axios/config'
// TODO @hhhero:可以改成 WriteVO 哈,主要是保持一致
export
interface
WriteVO
{
export
interface
WriteParams
{
type
:
1
|
2
// 1:撰写 2:回复
// TODO @hhhero:注释。每个属性的后面哈。会更简洁一点
prompt
:
string
// 写作内容提示 1。撰写 2回复
/**
originalContent
:
string
// 原文
* 1:撰写 2:回复
length
:
number
// 长度
*/
format
:
number
// 格式
type
:
1
|
2
tone
:
number
// 语气
/**
language
:
number
// 语言
* 写作内容提示 1。撰写 2回复
*/
prompt
:
string
/**
* 原文
*/
originalContent
:
string
/**
* 长度
*/
length
:
number
/**
* 格式
*/
format
:
number
/**
* 语气
*/
tone
:
number
/**
* 语言
*/
language
:
number
}
}
export
const
writeStream
=
({
export
const
writeStream
=
({
...
@@ -43,7 +20,7 @@ export const writeStream = ({
...
@@ -43,7 +20,7 @@ export const writeStream = ({
onError
,
onError
,
ctrl
ctrl
}:
{
}:
{
data
:
Write
Params
data
:
Write
VO
onMessage
?:
(
res
:
any
)
=>
void
onMessage
?:
(
res
:
any
)
=>
void
onError
?:
(...
args
:
any
[])
=>
void
onError
?:
(...
args
:
any
[])
=>
void
onClose
?:
(...
args
:
any
[])
=>
void
onClose
?:
(...
args
:
any
[])
=>
void
...
...
src/views/ai/utils/constants.ts
View file @
5730707b
...
@@ -40,3 +40,11 @@ export const AiMusicStatusEnum = {
...
@@ -40,3 +40,11 @@ export const AiMusicStatusEnum = {
SUCCESS
:
20
,
// 已完成
SUCCESS
:
20
,
// 已完成
FAIL
:
30
// 已失败
FAIL
:
30
// 已失败
}
}
/**
* AI 写作类型的枚举
*/
export
enum
AiWriteTypeEnum
{
WRITING
=
1
,
// 撰写
REPLY
// 回复
}
src/views/ai/utils/utils.ts
View file @
5730707b
...
@@ -11,3 +11,16 @@
...
@@ -11,3 +11,16 @@
export
const
hasChinese
=
async
(
str
)
=>
{
export
const
hasChinese
=
async
(
str
)
=>
{
return
/
[\u
4e00-
\u
9fa5
]
/
.
test
(
str
)
return
/
[\u
4e00-
\u
9fa5
]
/
.
test
(
str
)
}
}
/** 写作点击示例时的数据 **/
export
const
WriteExampleDataJson
=
{
write
:
{
prompt
:
'vue'
,
data
:
'Vue.js 是一种用于构建用户界面的渐进式 JavaScript 框架。它的核心库只关注视图层,易于上手,同时也便于与其他库或已有项目整合。\n\nVue.js 的特点包括:\n- 响应式的数据绑定:Vue.js 会自动将数据与 DOM 同步,使得状态管理变得更加简单。\n- 组件化:Vue.js 允许开发者通过小型、独立和通常可复用的组件构建大型应用。\n- 虚拟 DOM:Vue.js 使用虚拟 DOM 实现快速渲染,提高了性能。\n\n在 Vue.js 中,一个典型的应用结构可能包括:\n1. 根实例:每个 Vue 应用都需要一个根实例作为入口点。\n2. 组件系统:可以创建自定义的可复用组件。\n3. 指令:特殊的带有前缀 v- 的属性,为 DOM 元素提供特殊的行为。\n4. 插值:用于文本内容,将数据动态地插入到 HTML。\n5. 计算属性和侦听器:用于处理数据的复杂逻辑和响应数据变化。\n6. 条件渲染:根据条件决定元素的渲染。\n7. 列表渲染:用于显示列表数据。\n8. 事件处理:响应用户交互。\n9. 表单输入绑定:处理表单输入和验证。\n10. 组件生命周期钩子:在组件的不同阶段执行特定的函数。\n\nVue.js 还提供了官方的路由器 Vue Router 和状态管理库 Vuex,以支持构建复杂的单页应用(SPA)。\n\n在开发过程中,开发者通常会使用 Vue CLI,这是一个强大的命令行工具,用于快速生成 Vue 项目脚手架,集成了诸如 Babel、Webpack 等现代前端工具,以及热重载、代码检测等开发体验优化功能。\n\nVue.js 的生态系统还包括大量的第三方库和插件,如 Vuetify(UI 组件库)、Vue Test Utils(测试工具)等,这些都极大地丰富了 Vue.js 的开发生态。\n\n总的来说,Vue.js 是一个灵活、高效的前端框架,适合从小型项目到大型企业级应用的开发。它的易用性、灵活性和强大的社区支持使其成为许多开发者的首选框架之一。'
},
reply
:
{
originalContent
:
'领导,我想请假'
,
prompt
:
'不批'
,
data
:
'您的请假申请已收悉,经核实和考虑,暂时无法批准您的请假申请。\n\n如有特殊情况或紧急事务,请及时与我联系。\n\n祝工作顺利。\n\n谢谢。'
}
}
src/views/ai/writer/components/Left.vue
View file @
5730707b
...
@@ -24,7 +24,7 @@
...
@@ -24,7 +24,7 @@
</h3>
</h3>
</DefineLabel>
</DefineLabel>
<!-- TODO 小屏幕的时候是定位在左边的,大屏是分开的 -->
<!-- TODO
@hhhero
小屏幕的时候是定位在左边的,大屏是分开的 -->
<div
class=
"relative"
v-bind=
"$attrs"
>
<div
class=
"relative"
v-bind=
"$attrs"
>
<!-- tab -->
<!-- tab -->
<div
<div
...
@@ -32,7 +32,7 @@
...
@@ -32,7 +32,7 @@
>
>
<div
<div
class=
"flex items-center relative after:content-[''] after:block after:bg-white after:h-[30px] after:w-1/2 after:absolute after:top-0 after:left-0 after:transition-transform after:rounded-full"
class=
"flex items-center relative after:content-[''] after:block after:bg-white after:h-[30px] after:w-1/2 after:absolute after:top-0 after:left-0 after:transition-transform after:rounded-full"
:class=
"selectedTab ===
2
&& 'after:transform after:translate-x-[100%]'"
:class=
"selectedTab ===
AiWriteTypeEnum.REPLY
&& 'after:transform after:translate-x-[100%]'"
>
>
<ReuseTab
<ReuseTab
v-for=
"tab in tabs"
v-for=
"tab in tabs"
...
@@ -53,7 +53,7 @@
...
@@ -53,7 +53,7 @@
type=
"textarea"
type=
"textarea"
:rows=
"5"
:rows=
"5"
:maxlength=
"500"
:maxlength=
"500"
v-model=
"
writeForm
.prompt"
v-model=
"
formData
.prompt"
placeholder=
"请输入写作内容"
placeholder=
"请输入写作内容"
showWordLimit
showWordLimit
/>
/>
...
@@ -65,7 +65,7 @@
...
@@ -65,7 +65,7 @@
type=
"textarea"
type=
"textarea"
:rows=
"5"
:rows=
"5"
:maxlength=
"500"
:maxlength=
"500"
v-model=
"
writeForm
.originalContent"
v-model=
"
formData
.originalContent"
placeholder=
"请输入原文"
placeholder=
"请输入原文"
showWordLimit
showWordLimit
/>
/>
...
@@ -75,20 +75,20 @@
...
@@ -75,20 +75,20 @@
type=
"textarea"
type=
"textarea"
:rows=
"5"
:rows=
"5"
:maxlength=
"500"
:maxlength=
"500"
v-model=
"
writeForm
.prompt"
v-model=
"
formData
.prompt"
placeholder=
"请输入回复内容"
placeholder=
"请输入回复内容"
showWordLimit
showWordLimit
/>
/>
</
template
>
</
template
>
<ReuseLabel
label=
"长度"
/>
<ReuseLabel
label=
"长度"
/>
<Tag
v-model=
"
writeForm.length"
:tags=
"writeTags.lenTags
"
/>
<Tag
v-model=
"
formData.length"
:tags=
"getIntDictOptions('ai_write_length')
"
/>
<ReuseLabel
label=
"格式"
/>
<ReuseLabel
label=
"格式"
/>
<Tag
v-model=
"
writeForm.format"
:tags=
"writeTags.formatTags
"
/>
<Tag
v-model=
"
formData.format"
:tags=
"getIntDictOptions('ai_write_format')
"
/>
<ReuseLabel
label=
"语气"
/>
<ReuseLabel
label=
"语气"
/>
<Tag
v-model=
"
writeForm.tone"
:tags=
"writeTags.toneTags
"
/>
<Tag
v-model=
"
formData.tone"
:tags=
"getIntDictOptions('ai_write_tone')
"
/>
<ReuseLabel
label=
"语言"
/>
<ReuseLabel
label=
"语言"
/>
<Tag
v-model=
"
writeForm.language"
:tags=
"writeTags.langTags
"
/>
<Tag
v-model=
"
formData.language"
:tags=
"getIntDictOptions('ai_write_language')
"
/>
<div
class=
"flex items-center justify-center mt-3"
>
<div
class=
"flex items-center justify-center mt-3"
>
<el-button
:disabled=
"isWriting"
>
重置
</el-button>
<el-button
:disabled=
"isWriting"
>
重置
</el-button>
...
@@ -103,12 +103,13 @@
...
@@ -103,12 +103,13 @@
import
{
createReusableTemplate
}
from
'@vueuse/core'
import
{
createReusableTemplate
}
from
'@vueuse/core'
import
{
ref
}
from
'vue'
import
{
ref
}
from
'vue'
import
Tag
from
'./Tag.vue'
import
Tag
from
'./Tag.vue'
import
{
Write
Params
}
from
'@/api/ai/writer'
import
{
Write
VO
}
from
'@/api/ai/writer'
import
{
omit
}
from
'lodash-es'
import
{
omit
}
from
'lodash-es'
import
{
getIntDictOptions
}
from
'@/utils/dict'
import
{
getIntDictOptions
}
from
'@/utils/dict'
import
dataJson
from
'../data.json'
import
{
WriteExampleDataJson
}
from
'@/views/ai/utils/utils'
import
{
AiWriteTypeEnum
}
from
"@/views/ai/utils/constants"
;
type
TabType
=
Write
Params
[
'type'
]
type
TabType
=
Write
VO
[
'type'
]
const
message
=
useMessage
()
const
message
=
useMessage
()
...
@@ -117,25 +118,25 @@ defineProps<{
...
@@ -117,25 +118,25 @@ defineProps<{
}
>
()
}
>
()
const
emits
=
defineEmits
<
{
const
emits
=
defineEmits
<
{
(
e
:
'submit'
,
params
:
Partial
<
Write
Params
>
)
(
e
:
'submit'
,
params
:
Partial
<
Write
VO
>
)
(
e
:
'example'
,
param
:
'write'
|
'reply'
)
(
e
:
'example'
,
param
:
'write'
|
'reply'
)
}
>
()
}
>
()
const
example
=
(
type
:
'write'
|
'reply'
)
=>
{
const
example
=
(
type
:
'write'
|
'reply'
)
=>
{
writeForm
.
value
=
{
formData
.
value
=
{
...
initData
,
...
initData
,
...
omit
(
d
ataJson
[
type
],
[
'data'
])
...
omit
(
WriteExampleD
ataJson
[
type
],
[
'data'
])
}
}
emits
(
'example'
,
type
)
emits
(
'example'
,
type
)
}
}
const
selectedTab
=
ref
<
TabType
>
(
1
)
const
selectedTab
=
ref
<
TabType
>
(
AiWriteTypeEnum
.
WRITING
)
const
tabs
:
{
const
tabs
:
{
text
:
string
text
:
string
value
:
TabType
value
:
TabType
}[]
=
[
}[]
=
[
{
text
:
'撰写'
,
value
:
1
},
// TODO @hhhero:1、2 这个枚举到 constants 里。方便后续万一要调整
{
text
:
'撰写'
,
value
:
AiWriteTypeEnum
.
WRITING
},
{
text
:
'回复'
,
value
:
2
}
{
text
:
'回复'
,
value
:
AiWriteTypeEnum
.
REPLY
}
]
]
const
[
DefineTab
,
ReuseTab
]
=
createReusableTemplate
<
{
const
[
DefineTab
,
ReuseTab
]
=
createReusableTemplate
<
{
active
?:
boolean
active
?:
boolean
...
@@ -143,7 +144,21 @@ const [DefineTab, ReuseTab] = createReusableTemplate<{
...
@@ -143,7 +144,21 @@ const [DefineTab, ReuseTab] = createReusableTemplate<{
itemClick
:
()
=>
void
itemClick
:
()
=>
void
}
>
()
}
>
()
const
initData
:
WriteParams
=
{
/**
* 可以在template里边定义可复用的组件,DefineLabel,ReuseLabel是采用的解构赋值,都是Vue组件
* 直接通过组件的形式使用,<DefineLabel v-slot="{ label, hint, hintClick }">中间是需要复用的组件代码</DefineLabel>,通过<ReuseLabel />来使用定义的组件
* DefineLabel里边的v-slot="{ label, hint, hintClick }“相当于是解构了组件的prop,需要注意的是boolean类型,需要显式的赋值比如 <ReuseLabel :flag="true" />
* 事件也得以prop形式传入,不能是@event的形式,比如下面的hintClick需要<ReuseLabel :hintClick="() => { doSomething }"/>
* @see https://vueuse.org/createReusableTemplate
*/
const
[
DefineLabel
,
ReuseLabel
]
=
createReusableTemplate
<
{
label
:
string
class
?:
string
hint
?:
string
hintClick
?:
()
=>
void
}
>
()
const
initData
:
WriteVO
=
{
type
:
1
,
type
:
1
,
prompt
:
''
,
prompt
:
''
,
originalContent
:
''
,
originalContent
:
''
,
...
@@ -152,49 +167,26 @@ const initData: WriteParams = {
...
@@ -152,49 +167,26 @@ const initData: WriteParams = {
length
:
1
,
length
:
1
,
format
:
1
format
:
1
}
}
// TODO @hhhero:这个字段,要不叫 formData,和其他模块保持一致。然后 initData 和它也更好对应上
const
formData
=
ref
<
WriteVO
>
({
...
initData
})
const
writeForm
=
ref
<
WriteParams
>
({
...
initData
})
/** 切换tab **/
// TODO @hhhero:这种一次性的变量,要不直接 vue template 直接调用。目的是:让 ts 这块,更专注逻辑哈。
const
writeTags
=
{
// 长度 TODO @hhhero:注释放在和面哈;
// TODO @hhhero:一般 length 不用缩写哈。更完整会更容易阅读;
lenTags
:
getIntDictOptions
(
'ai_write_length'
),
// 格式
formatTags
:
getIntDictOptions
(
'ai_write_format'
),
// 语气
toneTags
:
getIntDictOptions
(
'ai_write_tone'
),
// 语言
langTags
:
getIntDictOptions
(
'ai_write_language'
)
//
}
// TODO @hhhero:这个写法不错。要不写个简单的注释,我怕很多人不懂哈。
const
[
DefineLabel
,
ReuseLabel
]
=
createReusableTemplate
<
{
label
:
string
class
?:
string
hint
?:
string
hintClick
?:
()
=>
void
}
>
()
const
switchTab
=
(
value
:
TabType
)
=>
{
const
switchTab
=
(
value
:
TabType
)
=>
{
selectedTab
.
value
=
value
selectedTab
.
value
=
value
writeForm
.
value
=
{
...
initData
}
formData
.
value
=
{
...
initData
}
}
}
const
submit
=
()
=>
{
const
submit
=
()
=>
{
if
(
selectedTab
.
value
===
2
&&
!
writeForm
.
value
.
originalContent
)
{
if
(
selectedTab
.
value
===
2
&&
!
formData
.
value
.
originalContent
)
{
message
.
warning
(
'请输入原文'
)
message
.
warning
(
'请输入原文'
)
return
return
}
}
if
(
!
writeForm
.
value
.
prompt
)
{
if
(
!
formData
.
value
.
prompt
)
{
message
.
warning
(
`请输入
${
selectedTab
.
value
===
1
?
'写作'
:
'回复'
}
内容`
)
message
.
warning
(
`请输入
${
selectedTab
.
value
===
1
?
'写作'
:
'回复'
}
内容`
)
return
return
}
}
emits
(
'submit'
,
{
emits
(
'submit'
,
{
...(
selectedTab
.
value
===
1
?
omit
(
writeForm
.
value
,
[
'originalContent'
])
:
writeForm
.
value
),
/** 撰写的时候没有 originalContent 字段**/
...(
selectedTab
.
value
===
1
?
omit
(
formData
.
value
,
[
'originalContent'
])
:
formData
.
value
),
/** 使用选中tab值覆盖当前的type类型 **/
type
:
selectedTab
.
value
type
:
selectedTab
.
value
})
})
}
}
...
...
src/views/ai/writer/components/Right.vue
View file @
5730707b
...
@@ -5,9 +5,8 @@
...
@@ -5,9 +5,8 @@
<el-button
<el-button
color=
"#846af7"
color=
"#846af7"
v-show=
"showCopy"
v-show=
"showCopy"
@
click=
"copyMsg"
@
click=
"copyContent"
class=
"absolute top-2 right-2 copy-btn"
class=
"absolute top-2 right-2"
:data-clipboard-target=
"inputId"
>
>
复制
复制
</el-button>
</el-button>
...
@@ -23,7 +22,7 @@
...
@@ -23,7 +22,7 @@
<el-input
<el-input
id=
"inputId"
id=
"inputId"
type=
"textarea"
type=
"textarea"
v-model=
"comp
Msg
"
v-model=
"comp
Content
"
autosize
autosize
:input-style=
"
{ boxShadow: 'none' }"
:input-style=
"
{ boxShadow: 'none' }"
resize="none"
resize="none"
...
@@ -41,25 +40,27 @@ const message = useMessage()
...
@@ -41,25 +40,27 @@ const message = useMessage()
const
{
copied
,
copy
}
=
useClipboard
()
const
{
copied
,
copy
}
=
useClipboard
()
const
props
=
defineProps
({
const
props
=
defineProps
({
msg
:
{
content
:
{
// 生成的结果
type
:
String
,
type
:
String
,
default
:
''
default
:
''
},
},
isWriting
:
{
isWriting
:
{
// 是否正在生成文章
type
:
Boolean
,
type
:
Boolean
,
default
:
false
default
:
false
}
}
})
})
const
emits
=
defineEmits
([
'update:
msg
'
,
'stopStream'
])
const
emits
=
defineEmits
([
'update:
content
'
,
'stopStream'
])
//
TODO @hhhero:是不是 Msg 改成 Content 这种哈。或者 Message。
//
通过计算属性,双向绑定,更改生成的内容,考虑到用户想要更改生成文章的情况
const
comp
Msg
=
computed
({
const
comp
Content
=
computed
({
get
()
{
get
()
{
return
props
.
msg
return
props
.
content
},
},
set
(
val
)
{
set
(
val
)
{
emits
(
'update:
msg
'
,
val
)
emits
(
'update:
content
'
,
val
)
}
}
})
})
...
@@ -72,12 +73,12 @@ defineExpose({
...
@@ -72,12 +73,12 @@ defineExpose({
})
})
/** 点击复制的时候复制内容 */
/** 点击复制的时候复制内容 */
const
showCopy
=
computed
(()
=>
props
.
msg
&&
!
props
.
isWriting
)
// 是否展示拷贝
const
showCopy
=
computed
(()
=>
props
.
content
&&
!
props
.
isWriting
)
// 是否展示复制按钮,在生成内容完成的时候展示
const
inputId
=
computed
(()
=>
getCurrentInstance
()?.
uid
)
// TODO @hhhero:这个可以写个注释哈
const
copyContent
=
()
=>
{
const
copyMsg
=
()
=>
{
copy
(
props
.
content
)
copy
(
props
.
msg
)
}
}
// 复制成功的时候copied.value为true
watch
(
copied
,
(
val
)
=>
{
watch
(
copied
,
(
val
)
=>
{
if
(
val
)
{
if
(
val
)
{
message
.
success
(
'复制成功'
)
message
.
success
(
'复制成功'
)
...
...
src/views/ai/writer/data.json
deleted
100644 → 0
View file @
62246021
{
"write"
:
{
"prompt"
:
"vue"
,
"data"
:
"Vue.js 是一种用于构建用户界面的渐进式 JavaScript 框架。它的核心库只关注视图层,易于上手,同时也便于与其他库或已有项目整合。
\n\n
Vue.js 的特点包括:
\n
- 响应式的数据绑定:Vue.js 会自动将数据与 DOM 同步,使得状态管理变得更加简单。
\n
- 组件化:Vue.js 允许开发者通过小型、独立和通常可复用的组件构建大型应用。
\n
- 虚拟 DOM:Vue.js 使用虚拟 DOM 实现快速渲染,提高了性能。
\n\n
在 Vue.js 中,一个典型的应用结构可能包括:
\n
1. 根实例:每个 Vue 应用都需要一个根实例作为入口点。
\n
2. 组件系统:可以创建自定义的可复用组件。
\n
3. 指令:特殊的带有前缀 v- 的属性,为 DOM 元素提供特殊的行为。
\n
4. 插值:用于文本内容,将数据动态地插入到 HTML。
\n
5. 计算属性和侦听器:用于处理数据的复杂逻辑和响应数据变化。
\n
6. 条件渲染:根据条件决定元素的渲染。
\n
7. 列表渲染:用于显示列表数据。
\n
8. 事件处理:响应用户交互。
\n
9. 表单输入绑定:处理表单输入和验证。
\n
10. 组件生命周期钩子:在组件的不同阶段执行特定的函数。
\n\n
Vue.js 还提供了官方的路由器 Vue Router 和状态管理库 Vuex,以支持构建复杂的单页应用(SPA)。
\n\n
在开发过程中,开发者通常会使用 Vue CLI,这是一个强大的命令行工具,用于快速生成 Vue 项目脚手架,集成了诸如 Babel、Webpack 等现代前端工具,以及热重载、代码检测等开发体验优化功能。
\n\n
Vue.js 的生态系统还包括大量的第三方库和插件,如 Vuetify(UI 组件库)、Vue Test Utils(测试工具)等,这些都极大地丰富了 Vue.js 的开发生态。
\n\n
总的来说,Vue.js 是一个灵活、高效的前端框架,适合从小型项目到大型企业级应用的开发。它的易用性、灵活性和强大的社区支持使其成为许多开发者的首选框架之一。"
},
"reply"
:
{
"originalContent"
:
"领导,我想请假"
,
"prompt"
:
"不批"
,
"data"
:
"您的请假申请已收悉,经核实和考虑,暂时无法批准您的请假申请。
\n\n
如有特殊情况或紧急事务,请及时与我联系。
\n\n
祝工作顺利。
\n\n
谢谢。"
}
}
src/views/ai/writer/index.vue
→
src/views/ai/writer/index
/index
.vue
View file @
5730707b
<!-- TODO @hhhero:挪到 write/index/index.vue 里。因为后续会有 write/manager/index.vue 管理内容 -->
<
template
>
<
template
>
<div
class=
"h-[calc(100vh-var(--top-tool-height)-var(--app-footer-height)-40px)] -m-5 flex"
>
<div
class=
"h-[calc(100vh-var(--top-tool-height)-var(--app-footer-height)-40px)] -m-5 flex"
>
<Left
:is-writing=
"isWriting"
class=
"h-full"
@
submit=
"submit"
@
example=
"handleExampleClick"
/>
<Left
:is-writing=
"isWriting"
class=
"h-full"
@
submit=
"submit"
@
example=
"handleExampleClick"
/>
...
@@ -9,59 +8,60 @@
...
@@ -9,59 +8,60 @@
@
stop-stream=
"stopStream"
@
stop-stream=
"stopStream"
ref=
"rightRef"
ref=
"rightRef"
class=
"flex-grow"
class=
"flex-grow"
v-model:
msg
=
"writeResult"
v-model:
content
=
"writeResult"
/>
/>
</div>
</div>
</
template
>
</
template
>
<
script
setup
lang=
"ts"
>
<
script
setup
lang=
"ts"
>
import
Left
from
'./components/Left.vue'
import
Left
from
'../components/Left.vue'
import
Right
from
'./components/Right.vue'
import
Right
from
'../components/Right.vue'
// TODO @hhhero:搞成 WriteApi 哈
import
*
as
WriteApi
from
'@/api/ai/writer'
import
{
writeStream
}
from
'@/api/ai/writer'
import
{
WriteExampleDataJson
}
from
'@/views/ai/utils/utils'
// TODO @hhhero:dataJson 放到 ai/utils/utils.ts
import
dataJson
from
'./data.json'
const
message
=
useMessage
()
const
message
=
useMessage
()
const
writeResult
=
ref
(
''
)
// 写作结果
const
writeResult
=
ref
(
''
)
// 写作结果
const
isWriting
=
ref
(
false
)
// 是否正在写作中
const
isWriting
=
ref
(
false
)
// 是否正在写作中
const
abortController
=
ref
<
AbortController
>
()
// // 写作进行中 abort 控制器(控制 stream 写作)
const
abortController
=
ref
<
AbortController
>
()
// // 写作进行中 abort 控制器(控制 stream 写作)
/** 停止 stream 生成 */
/** 停止 stream 生成 */
const
stopStream
=
()
=>
{
const
stopStream
=
()
=>
{
abortController
.
value
?.
abort
()
abortController
.
value
?.
abort
()
isWriting
.
value
=
false
isWriting
.
value
=
false
}
}
/** 执行写作 */
/** 执行写作 */
const
rightRef
=
ref
<
InstanceType
<
typeof
Right
>>
()
const
rightRef
=
ref
<
InstanceType
<
typeof
Right
>>
()
const
submit
=
async
(
data
)
=>
{
const
submit
=
(
data
)
=>
{
abortController
.
value
=
new
AbortController
()
abortController
.
value
=
new
AbortController
()
writeResult
.
value
=
''
writeResult
.
value
=
''
isWriting
.
value
=
true
isWriting
.
value
=
true
await
writeStream
({
WriteApi
.
writeStream
({
data
,
data
,
onMessage
:
async
(
res
)
=>
{
onMessage
:
async
(
res
)
=>
{
const
{
code
,
data
,
msg
}
=
JSON
.
parse
(
res
.
data
)
const
{
code
,
data
,
msg
}
=
JSON
.
parse
(
res
.
data
)
if
(
code
!==
0
)
{
if
(
code
!==
0
)
{
message
.
alert
(
`写作异常!
${
msg
}
`
)
message
.
alert
(
`写作异常!
${
msg
}
`
)
stopStream
()
return
}
writeResult
.
value
=
writeResult
.
value
+
data
nextTick
(()
=>
{
rightRef
.
value
?.
scrollToBottom
()
})
},
ctrl
:
abortController
.
value
,
onClose
:
stopStream
,
onError
:
(...
err
)
=>
{
console
.
error
(
'写作异常'
,
...
err
)
stopStream
()
stopStream
()
return
}
}
writeResult
.
value
=
writeResult
.
value
+
data
})
nextTick
(()
=>
{
}
rightRef
.
value
?.
scrollToBottom
()
})
},
ctrl
:
abortController
.
value
,
onClose
:
stopStream
,
onError
:
stopStream
// TODO @hhhero: error 的时候,是不是要打印下错误哈
})
}
/** 点击示例触发 */
/** 点击示例触发 */
const
handleExampleClick
=
(
type
:
keyof
typeof
d
ataJson
)
=>
{
const
handleExampleClick
=
(
type
:
keyof
typeof
WriteExampleD
ataJson
)
=>
{
writeResult
.
value
=
d
ataJson
[
type
].
data
writeResult
.
value
=
WriteExampleD
ataJson
[
type
].
data
}
}
</
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