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
ece15334
authored
Jul 10, 2024
by
YunaiV
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
【代码评审】写作:生成部分
parent
e53786e8
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
46 additions
and
44 deletions
+46
-44
src/api/ai/writer/index.ts
+3
-1
src/views/ai/utils/constants.ts
+15
-0
src/views/ai/utils/utils.ts
+0
-13
src/views/ai/writer/index/components/Left.vue
+15
-11
src/views/ai/writer/index/components/Right.vue
+6
-13
src/views/ai/writer/index/components/Tag.vue
+0
-0
src/views/ai/writer/index/index.vue
+7
-6
No files found.
src/api/ai/writer/index.ts
View file @
ece15334
...
@@ -2,9 +2,10 @@ import { fetchEventSource } from '@microsoft/fetch-event-source'
...
@@ -2,9 +2,10 @@ 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'
import
{
AiWriteTypeEnum
}
from
'@/views/ai/utils/constants'
export
interface
WriteVO
{
export
interface
WriteVO
{
type
:
1
|
2
// 1:撰写 2:回复
type
:
AiWriteTypeEnum
.
WRITING
|
AiWriteTypeEnum
.
REPLY
// 1:撰写 2:回复
prompt
:
string
// 写作内容提示 1。撰写 2回复
prompt
:
string
// 写作内容提示 1。撰写 2回复
originalContent
:
string
// 原文
originalContent
:
string
// 原文
length
:
number
// 长度
length
:
number
// 长度
...
@@ -13,6 +14,7 @@ export interface WriteVO {
...
@@ -13,6 +14,7 @@ export interface WriteVO {
language
:
number
// 语言
language
:
number
// 语言
}
}
// TODO @hhero:搞成 WriteApi,类似 ConversationApi 一样。这样更有类的概念,后续引入某个 Api,然后调用它的方法就可以了。
export
const
writeStream
=
({
export
const
writeStream
=
({
data
,
data
,
onClose
,
onClose
,
...
...
src/views/ai/utils/constants.ts
View file @
ece15334
...
@@ -353,3 +353,18 @@ export const NijiVersionList = [
...
@@ -353,3 +353,18 @@ export const NijiVersionList = [
label
:
'v5'
label
:
'v5'
}
}
]
]
// ========== 【写作 UI】相关的枚举 ==========
/** 写作点击示例时的数据 **/
export
const
WriteExample
=
{
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/utils/utils.ts
View file @
ece15334
...
@@ -11,16 +11,3 @@
...
@@ -11,16 +11,3 @@
export
const
hasChinese
=
(
str
:
string
)
=>
{
export
const
hasChinese
=
(
str
:
string
)
=>
{
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
→
src/views/ai/writer/
index/
components/Left.vue
View file @
ece15334
...
@@ -24,7 +24,6 @@
...
@@ -24,7 +24,6 @@
</h3>
</h3>
</DefineLabel>
</DefineLabel>
<!-- TODO @hhhero 小屏幕的时候是定位在左边的,大屏是分开的 -->
<div
class=
"relative"
v-bind=
"$attrs"
>
<div
class=
"relative"
v-bind=
"$attrs"
>
<!-- tab -->
<!-- tab -->
<div
<div
...
@@ -106,12 +105,11 @@ import Tag from './Tag.vue'
...
@@ -106,12 +105,11 @@ import Tag from './Tag.vue'
import
{
WriteVO
}
from
'@/api/ai/writer'
import
{
WriteVO
}
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
{
WriteExampleDataJson
}
from
'@/views/ai/utils/utils'
import
{
AiWriteTypeEnum
,
WriteExample
}
from
'@/views/ai/utils/constants'
import
{
AiWriteTypeEnum
}
from
"@/views/ai/utils/constants"
;
type
TabType
=
WriteVO
[
'type'
]
type
TabType
=
WriteVO
[
'type'
]
const
message
=
useMessage
()
const
message
=
useMessage
()
// 消息弹窗
defineProps
<
{
defineProps
<
{
isWriting
:
boolean
isWriting
:
boolean
...
@@ -127,15 +125,17 @@ const emits = defineEmits<{
...
@@ -127,15 +125,17 @@ const emits = defineEmits<{
const
example
=
(
type
:
'write'
|
'reply'
)
=>
{
const
example
=
(
type
:
'write'
|
'reply'
)
=>
{
formData
.
value
=
{
formData
.
value
=
{
...
initData
,
...
initData
,
...
omit
(
WriteExample
DataJson
[
type
],
[
'data'
])
...
omit
(
WriteExample
[
type
],
[
'data'
])
}
}
emits
(
'example'
,
type
)
emits
(
'example'
,
type
)
}
}
/** 重置,将表单值作为初选值 **/
/** 重置,将表单值作为初选值 **/
const
reset
=
()
=>
{
const
reset
=
()
=>
{
formData
.
value
=
{
...
initData
}
formData
.
value
=
{
...
initData
}
emits
(
'reset'
)
emits
(
'reset'
)
}
}
const
selectedTab
=
ref
<
TabType
>
(
AiWriteTypeEnum
.
WRITING
)
const
selectedTab
=
ref
<
TabType
>
(
AiWriteTypeEnum
.
WRITING
)
const
tabs
:
{
const
tabs
:
{
text
:
string
text
:
string
...
@@ -151,10 +151,12 @@ const [DefineTab, ReuseTab] = createReusableTemplate<{
...
@@ -151,10 +151,12 @@ const [DefineTab, ReuseTab] = createReusableTemplate<{
}
>
()
}
>
()
/**
/**
* 可以在template里边定义可复用的组件,DefineLabel,ReuseLabel是采用的解构赋值,都是Vue组件
* 可以在 template 里边定义可复用的组件,DefineLabel,ReuseLabel 是采用的解构赋值,都是 Vue 组件
* 直接通过组件的形式使用,<DefineLabel v-slot="{ label, hint, hintClick }">中间是需要复用的组件代码</DefineLabel>,通过<ReuseLabel />来使用定义的组件
*
* DefineLabel里边的v-slot="{ label, hint, hintClick }“相当于是解构了组件的prop,需要注意的是boolean类型,需要显式的赋值比如 <ReuseLabel :flag="true" />
* 直接通过组件的形式使用,<DefineLabel v-slot="{ label, hint, hintClick }"> 中间是需要复用的组件代码 <DefineLabel />,通过 <ReuseLabel /> 来使用定义的组件
* 事件也得以prop形式传入,不能是@event的形式,比如下面的hintClick需要<ReuseLabel :hintClick="() => { doSomething }"/>
* DefineLabel 里边的 v-slot="{ label, hint, hintClick }"相当于是解构了组件的 prop,需要注意的是 boolean 类型,需要显式的赋值比如 <ReuseLabel :flag="true" />
* 事件也得以 prop 形式传入,不能是 @event的形式,比如下面的 hintClick 需要<ReuseLabel :hintClick="() => { doSomething }"/>
*
* @see https://vueuse.org/createReusableTemplate
* @see https://vueuse.org/createReusableTemplate
*/
*/
const
[
DefineLabel
,
ReuseLabel
]
=
createReusableTemplate
<
{
const
[
DefineLabel
,
ReuseLabel
]
=
createReusableTemplate
<
{
...
@@ -174,12 +176,14 @@ const initData: WriteVO = {
...
@@ -174,12 +176,14 @@ const initData: WriteVO = {
format
:
1
format
:
1
}
}
const
formData
=
ref
<
WriteVO
>
({
...
initData
})
const
formData
=
ref
<
WriteVO
>
({
...
initData
})
/** 切换tab **/
/** 切换tab **/
const
switchTab
=
(
value
:
TabType
)
=>
{
const
switchTab
=
(
value
:
TabType
)
=>
{
selectedTab
.
value
=
value
selectedTab
.
value
=
value
formData
.
value
=
{
...
initData
}
formData
.
value
=
{
...
initData
}
}
}
/** 提交写作 */
const
submit
=
()
=>
{
const
submit
=
()
=>
{
if
(
selectedTab
.
value
===
2
&&
!
formData
.
value
.
originalContent
)
{
if
(
selectedTab
.
value
===
2
&&
!
formData
.
value
.
originalContent
)
{
message
.
warning
(
'请输入原文'
)
message
.
warning
(
'请输入原文'
)
...
@@ -192,7 +196,7 @@ const submit = () => {
...
@@ -192,7 +196,7 @@ const submit = () => {
emits
(
'submit'
,
{
emits
(
'submit'
,
{
/** 撰写的时候没有 originalContent 字段**/
/** 撰写的时候没有 originalContent 字段**/
...(
selectedTab
.
value
===
1
?
omit
(
formData
.
value
,
[
'originalContent'
])
:
formData
.
value
),
...(
selectedTab
.
value
===
1
?
omit
(
formData
.
value
,
[
'originalContent'
])
:
formData
.
value
),
/** 使用选中
tab值覆盖当前的type
类型 **/
/** 使用选中
tab 值覆盖当前的 type
类型 **/
type
:
selectedTab
.
value
type
:
selectedTab
.
value
})
})
}
}
...
...
src/views/ai/writer/components/Right.vue
→
src/views/ai/writer/
index/
components/Right.vue
View file @
ece15334
...
@@ -3,22 +3,15 @@
...
@@ -3,22 +3,15 @@
<h3
class=
"m-0 h-14 -mx-7 px-7 shrink-0 flex items-center justify-between bg-[#ecedef]"
>
<h3
class=
"m-0 h-14 -mx-7 px-7 shrink-0 flex items-center justify-between bg-[#ecedef]"
>
<span>
预览
</span>
<span>
预览
</span>
<!-- 展示在右上角 -->
<!-- 展示在右上角 -->
<el-button
<el-button
color=
"#846af7"
v-show=
"showCopy"
@
click=
"copyContent"
size=
"small"
>
color=
"#846af7"
v-show=
"showCopy"
@
click=
"copyContent"
size=
"small"
>
<template
#
icon
>
<template
#
icon
>
<Icon
icon=
"ph:copy-bold"
/>
<Icon
icon=
"ph:copy-bold"
/>
</
template
>
</
template
>
复制
复制
</el-button>
</el-button>
</h3>
</h3>
<div
ref=
"contentRef"
class=
"hide-scroll-bar flex-grow box-border overflow-y-auto
"
>
<div
ref=
"contentRef"
class=
"hide-scroll-bar flex-grow box-border overflow-y-auto"
>
<div
class=
"w-full min-h-full relative flex-grow bg-white box-border p-3 sm:p-7"
>
<div
class=
"w-full min-h-full relative flex-grow bg-white box-border p-3 sm:p-7"
>
<!-- 终止生成内容的按钮 -->
<!-- 终止生成内容的按钮 -->
<el-button
<el-button
...
@@ -49,8 +42,8 @@
...
@@ -49,8 +42,8 @@
<
script
setup
lang=
"ts"
>
<
script
setup
lang=
"ts"
>
import
{
useClipboard
}
from
'@vueuse/core'
import
{
useClipboard
}
from
'@vueuse/core'
const
message
=
useMessage
()
const
message
=
useMessage
()
// 消息弹窗
const
{
copied
,
copy
}
=
useClipboard
()
const
{
copied
,
copy
}
=
useClipboard
()
// 粘贴板
const
props
=
defineProps
({
const
props
=
defineProps
({
content
:
{
content
:
{
...
@@ -67,7 +60,7 @@ const props = defineProps({
...
@@ -67,7 +60,7 @@ const props = defineProps({
const
emits
=
defineEmits
([
'update:content'
,
'stopStream'
])
const
emits
=
defineEmits
([
'update:content'
,
'stopStream'
])
/
/ 通过计算属性,双向绑定,更改生成的内容,考虑到用户想要更改生成文章的情况
/
** 通过计算属性,双向绑定,更改生成的内容,考虑到用户想要更改生成文章的情况 */
const
compContent
=
computed
({
const
compContent
=
computed
({
get
()
{
get
()
{
return
props
.
content
return
props
.
content
...
@@ -91,7 +84,7 @@ const copyContent = () => {
...
@@ -91,7 +84,7 @@ const copyContent = () => {
copy
(
props
.
content
)
copy
(
props
.
content
)
}
}
/
/ 复制成功的时候copied.value为true
/
** 复制成功的时候 copied.value 为 true */
watch
(
copied
,
(
val
)
=>
{
watch
(
copied
,
(
val
)
=>
{
if
(
val
)
{
if
(
val
)
{
message
.
success
(
'复制成功'
)
message
.
success
(
'复制成功'
)
...
...
src/views/ai/writer/components/Tag.vue
→
src/views/ai/writer/
index/
components/Tag.vue
View file @
ece15334
File moved
src/views/ai/writer/index/index.vue
View file @
ece15334
<
template
>
<
template
>
<!-- TODO @hhhero:整体没啥问题了。感觉整体框框的样子可以优化下,可以参考下绘图界面。例如说:1)写作的“预览”和绘图的“绘图任务”的 header;2)左右的边界,有个竖线之类的。 -->
<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
<Left
:is-writing=
"isWriting"
:is-writing=
"isWriting"
...
@@ -18,10 +19,10 @@
...
@@ -18,10 +19,10 @@
</
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'
import
*
as
WriteApi
from
'@/api/ai/writer'
import
*
as
WriteApi
from
'@/api/ai/writer'
import
{
WriteExample
DataJson
}
from
'@/views/ai/utils/util
s'
import
{
WriteExample
}
from
'@/views/ai/utils/constant
s'
const
message
=
useMessage
()
const
message
=
useMessage
()
...
@@ -51,9 +52,9 @@ const submit = (data) => {
...
@@ -51,9 +52,9 @@ const submit = (data) => {
return
return
}
}
writeResult
.
value
=
writeResult
.
value
+
data
writeResult
.
value
=
writeResult
.
value
+
data
nextTick
(()
=>
{
// 滚动到底部
await
nextTick
()
rightRef
.
value
?.
scrollToBottom
()
rightRef
.
value
?.
scrollToBottom
()
})
},
},
ctrl
:
abortController
.
value
,
ctrl
:
abortController
.
value
,
onClose
:
stopStream
,
onClose
:
stopStream
,
...
@@ -66,7 +67,7 @@ const submit = (data) => {
...
@@ -66,7 +67,7 @@ const submit = (data) => {
/** 点击示例触发 */
/** 点击示例触发 */
const
handleExampleClick
=
(
type
:
keyof
typeof
WriteExampleDataJson
)
=>
{
const
handleExampleClick
=
(
type
:
keyof
typeof
WriteExampleDataJson
)
=>
{
writeResult
.
value
=
WriteExample
DataJson
[
type
].
data
writeResult
.
value
=
WriteExample
[
type
].
data
}
}
/** 点击重置的时候清空写作的结果**/
/** 点击重置的时候清空写作的结果**/
...
...
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