!482 [代码优化]AI: 写作页面样式调整,目录更名writer => write,写作管理更改
Merge pull request !482 from hhhero/devpull/479/head
commit
35b2708ca6
|
@ -0,0 +1,85 @@
|
|||
import { fetchEventSource } from '@microsoft/fetch-event-source'
|
||||
|
||||
import { getAccessToken } from '@/utils/auth'
|
||||
import { config } from '@/config/axios/config'
|
||||
import { AiWriteTypeEnum } from '@/views/ai/utils/constants'
|
||||
import request from '@/config/axios'
|
||||
|
||||
export interface WriteVO {
|
||||
type: AiWriteTypeEnum.WRITING | AiWriteTypeEnum.REPLY // 1:撰写 2:回复
|
||||
prompt: string // 写作内容提示 1。撰写 2回复
|
||||
originalContent: string // 原文
|
||||
length: number // 长度
|
||||
format: number // 格式
|
||||
tone: number // 语气
|
||||
language: number // 语言
|
||||
userId?: number // 用户编号
|
||||
platform?: string // 平台
|
||||
model?: string // 模型
|
||||
generatedContent?: string // 生成的内容
|
||||
errorMessage: string // 错误信息
|
||||
createTime?: Date // 创建时间
|
||||
}
|
||||
|
||||
export interface AiWritePageReqVO extends PageParam {
|
||||
userId?: number // 用户编号
|
||||
type?: AiWriteTypeEnum // 写作类型
|
||||
platform?: string // 平台
|
||||
createTime?: [string, string] // 创建时间
|
||||
}
|
||||
|
||||
export interface AiWriteRespVo {
|
||||
id: number
|
||||
userId: number
|
||||
type: number
|
||||
platform: string
|
||||
model: string
|
||||
prompt: string
|
||||
generatedContent: string
|
||||
originalContent: string
|
||||
length: number
|
||||
format: number
|
||||
tone: number
|
||||
language: number
|
||||
errorMessage: string
|
||||
createTime: string
|
||||
}
|
||||
|
||||
export const WriteApi = {
|
||||
writeStream: ({
|
||||
data,
|
||||
onClose,
|
||||
onMessage,
|
||||
onError,
|
||||
ctrl
|
||||
}: {
|
||||
data: WriteVO
|
||||
onMessage?: (res: any) => void
|
||||
onError?: (...args: any[]) => void
|
||||
onClose?: (...args: any[]) => void
|
||||
ctrl: AbortController
|
||||
}) => {
|
||||
const token = getAccessToken()
|
||||
return fetchEventSource(`${config.base_url}/ai/write/generate-stream`, {
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
},
|
||||
openWhenHidden: true,
|
||||
body: JSON.stringify(data),
|
||||
onmessage: onMessage,
|
||||
onerror: onError,
|
||||
onclose: onClose,
|
||||
signal: ctrl.signal
|
||||
})
|
||||
},
|
||||
// 获取写作列表
|
||||
getWritePage: (params: AiWritePageReqVO) => {
|
||||
return request.get<PageResult<AiWriteRespVo[]>>({ url: `/ai/write/page`, params })
|
||||
},
|
||||
// 删除写作
|
||||
deleteWrite(id: number) {
|
||||
return request.delete({ url: `/ai/write/delete`, params: { id } })
|
||||
}
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
import { fetchEventSource } from '@microsoft/fetch-event-source'
|
||||
import request from '@/config/axios'
|
||||
|
||||
import { getAccessToken } from '@/utils/auth'
|
||||
import { config } from '@/config/axios/config'
|
||||
import { AiWriteTypeEnum } from '@/views/ai/utils/constants'
|
||||
|
||||
export interface WriteVO {
|
||||
type: AiWriteTypeEnum.WRITING | AiWriteTypeEnum.REPLY // 1:撰写 2:回复
|
||||
prompt: string // 写作内容提示 1。撰写 2回复
|
||||
originalContent: string // 原文
|
||||
length: number // 长度
|
||||
format: number // 格式
|
||||
tone: number // 语气
|
||||
language: number // 语言
|
||||
userId?: number // 用户编号
|
||||
platform?: string // 平台
|
||||
model?: string // 模型
|
||||
generatedContent?: string // 生成的内容
|
||||
errorMessage: string // 错误信息
|
||||
createTime?: Date // 创建时间
|
||||
}
|
||||
|
||||
// TODO @hhero:搞成 WriteApi,类似 ConversationApi 一样。这样更有类的概念,后续引入某个 Api,然后调用它的方法就可以了。
|
||||
export const writeStream = ({
|
||||
data,
|
||||
onClose,
|
||||
onMessage,
|
||||
onError,
|
||||
ctrl
|
||||
}: {
|
||||
data: WriteVO
|
||||
onMessage?: (res: any) => void
|
||||
onError?: (...args: any[]) => void
|
||||
onClose?: (...args: any[]) => void
|
||||
ctrl: AbortController
|
||||
}) => {
|
||||
const token = getAccessToken()
|
||||
return fetchEventSource(`${config.base_url}/ai/write/generate-stream`, {
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
},
|
||||
openWhenHidden: true,
|
||||
body: JSON.stringify(data),
|
||||
onmessage: onMessage,
|
||||
onerror: onError,
|
||||
onclose: onClose,
|
||||
signal: ctrl.signal
|
||||
})
|
||||
}
|
||||
|
||||
// AI 写作 API
|
||||
export const WriteApi = {
|
||||
// 查询AI 写作分页
|
||||
getWritePage: async (params: any) => {
|
||||
return await request.get({ url: `/ai/write/page`, params })
|
||||
},
|
||||
|
||||
// 删除AI 写作
|
||||
deleteWrite: async (id: number) => {
|
||||
return await request.delete({ url: `/ai/write/delete?id=` + id })
|
||||
}
|
||||
}
|
|
@ -64,6 +64,12 @@ export enum AiWriteTypeEnum {
|
|||
REPLY // 回复
|
||||
}
|
||||
|
||||
// 表格展示对照map
|
||||
export const AiWriteTypeTableRender = {
|
||||
[AiWriteTypeEnum.WRITING]: '撰写',
|
||||
[AiWriteTypeEnum.REPLY]: '回复',
|
||||
}
|
||||
|
||||
// ========== 【图片 UI】相关的枚举 ==========
|
||||
export const ImageHotWords = [
|
||||
'中国旗袍',
|
||||
|
|
|
@ -24,26 +24,28 @@
|
|||
</h3>
|
||||
</DefineLabel>
|
||||
|
||||
<div class="relative" v-bind="$attrs">
|
||||
<div class="flex flex-col" v-bind="$attrs">
|
||||
<!-- tab -->
|
||||
<div
|
||||
class="absolute left-1/2 top-2 -translate-x-1/2 w-[303px] rounded-full bg-[#DDDFE3] p-1 z-10"
|
||||
>
|
||||
<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="selectedTab === AiWriteTypeEnum.REPLY && 'after:transform after:translate-x-[100%]'"
|
||||
>
|
||||
<ReuseTab
|
||||
v-for="tab in tabs"
|
||||
:key="tab.value"
|
||||
:text="tab.text"
|
||||
:active="tab.value === selectedTab"
|
||||
:itemClick="() => switchTab(tab.value)"
|
||||
/>
|
||||
<div class="w-full pt-2 bg-[#f5f7f9] flex justify-center">
|
||||
<div class="w-[303px] rounded-full bg-[#DDDFE3] p-1 z-10">
|
||||
<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="
|
||||
selectedTab === AiWriteTypeEnum.REPLY && 'after:transform after:translate-x-[100%]'
|
||||
"
|
||||
>
|
||||
<ReuseTab
|
||||
v-for="tab in tabs"
|
||||
:key="tab.value"
|
||||
:text="tab.text"
|
||||
:active="tab.value === selectedTab"
|
||||
:itemClick="() => switchTab(tab.value)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="px-7 pb-2 pt-[46px] overflow-y-auto lg:block w-[380px] box-border bg-[#ECEDEF] h-full"
|
||||
class="px-7 pb-2 flex-grow overflow-y-auto lg:block w-[380px] box-border bg-[#f5f7f9] h-full"
|
||||
>
|
||||
<div>
|
||||
<template v-if="selectedTab === 1">
|
||||
|
@ -102,7 +104,7 @@
|
|||
import { createReusableTemplate } from '@vueuse/core'
|
||||
import { ref } from 'vue'
|
||||
import Tag from './Tag.vue'
|
||||
import { WriteVO } from '@/api/ai/writer'
|
||||
import { WriteVO } from 'src/api/ai/write'
|
||||
import { omit } from 'lodash-es'
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { AiWriteTypeEnum, WriteExample } from '@/views/ai/utils/constants'
|
||||
|
@ -177,10 +179,18 @@ const initData: WriteVO = {
|
|||
}
|
||||
const formData = ref<WriteVO>({ ...initData })
|
||||
|
||||
/** 用来记录切换之前所填写的数据,切换的时候给赋值回来 **/
|
||||
const recordFormData = {} as Record<AiWriteTypeEnum, WriteVO>
|
||||
|
||||
/** 切换tab **/
|
||||
const switchTab = (value: TabType) => {
|
||||
selectedTab.value = value
|
||||
formData.value = { ...initData }
|
||||
if (value !== selectedTab.value) {
|
||||
// 保存之前的久数据
|
||||
recordFormData[selectedTab.value] = formData.value
|
||||
selectedTab.value = value
|
||||
// 将之前的旧数据赋值回来
|
||||
formData.value = { ...initData, ...recordFormData[value] }
|
||||
}
|
||||
}
|
||||
|
||||
/** 提交写作 */
|
|
@ -1,17 +1,19 @@
|
|||
<template>
|
||||
<div class="h-full box-border flex flex-col px-7">
|
||||
<h3 class="m-0 h-14 -mx-7 px-7 shrink-0 flex items-center justify-between bg-[#ecedef]">
|
||||
<span>预览</span>
|
||||
<!-- 展示在右上角 -->
|
||||
<el-button color="#846af7" v-show="showCopy" @click="copyContent" size="small">
|
||||
<template #icon>
|
||||
<Icon icon="ph:copy-bold" />
|
||||
</template>
|
||||
复制
|
||||
</el-button>
|
||||
</h3>
|
||||
<el-card class="my-card h-full">
|
||||
<template #header
|
||||
><h3 class="m-0 px-7 shrink-0 flex items-center justify-between">
|
||||
<span>预览</span>
|
||||
<!-- 展示在右上角 -->
|
||||
<el-button color="#846af7" v-show="showCopy" @click="copyContent" size="small">
|
||||
<template #icon>
|
||||
<Icon icon="ph:copy-bold" />
|
||||
</template>
|
||||
复制
|
||||
</el-button>
|
||||
</h3></template
|
||||
>
|
||||
|
||||
<div ref="contentRef" class="hide-scroll-bar flex-grow box-border overflow-y-auto">
|
||||
<div ref="contentRef" class="hide-scroll-bar h-full box-border overflow-y-auto">
|
||||
<div class="w-full min-h-full relative flex-grow bg-white box-border p-3 sm:p-7">
|
||||
<!-- 终止生成内容的按钮 -->
|
||||
<el-button
|
||||
|
@ -36,7 +38,7 @@
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
@ -102,4 +104,17 @@ watch(copied, (val) => {
|
|||
height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.my-card{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
:deep(.el-card__body) {
|
||||
box-sizing: border-box;
|
||||
flex-grow: 1;
|
||||
overflow-y: auto;
|
||||
padding: 0;
|
||||
@extend .hide-scroll-bar;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,6 +1,5 @@
|
|||
<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="absolute top-0 left-0 right-0 bottom-0 flex">
|
||||
<Left
|
||||
:is-writing="isWriting"
|
||||
class="h-full"
|
||||
|
@ -21,7 +20,7 @@
|
|||
<script setup lang="ts">
|
||||
import Left from './components/Left.vue'
|
||||
import Right from './components/Right.vue'
|
||||
import * as WriteApi from '@/api/ai/writer'
|
||||
import { WriteApi } from '@/api/ai/write'
|
||||
import { WriteExample } from '@/views/ai/utils/constants'
|
||||
|
||||
const message = useMessage()
|
||||
|
@ -66,7 +65,7 @@ const submit = (data) => {
|
|||
}
|
||||
|
||||
/** 点击示例触发 */
|
||||
const handleExampleClick = (type: keyof typeof WriteExampleDataJson) => {
|
||||
const handleExampleClick = (type: keyof typeof WriteExample) => {
|
||||
writeResult.value = WriteExample[type].data
|
||||
}
|
||||
|
|
@ -39,7 +39,7 @@
|
|||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="平台" prop="platform">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择平台" clearable class="!w-240px">
|
||||
<el-select v-model="queryParams.platform" placeholder="请选择平台" clearable class="!w-240px">
|
||||
<el-option
|
||||
v-for="dict in getStrDictOptions(DICT_TYPE.AI_PLATFORM)"
|
||||
:key="dict.value"
|
||||
|
@ -70,6 +70,7 @@
|
|||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
</el-button>
|
||||
<!-- TODO @YunaiV 目前没有导出接口,需要导出吗 -->
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
|
@ -103,7 +104,13 @@
|
|||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="模型" align="center" prop="model" width="180" />
|
||||
<el-table-column label="生成内容提示" align="center" prop="prompt" width="180" />
|
||||
<el-table-column
|
||||
label="生成内容提示"
|
||||
align="center"
|
||||
prop="prompt"
|
||||
width="180"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column label="生成的内容" align="center" prop="generatedContent" width="180" />
|
||||
<el-table-column label="原文" align="center" prop="originalContent" width="180" />
|
||||
<el-table-column label="长度" align="center" prop="length">
|
||||
|
@ -136,6 +143,7 @@
|
|||
<el-table-column label="错误信息" align="center" prop="errorMessage" />
|
||||
<el-table-column label="操作" align="center">
|
||||
<template #default="scope">
|
||||
<!-- TODO @YunaiV 目前没有修改接口,写作要可以更改吗-->
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
|
@ -168,8 +176,8 @@
|
|||
<script setup lang="ts">
|
||||
import { DICT_TYPE, getIntDictOptions, getStrDictOptions } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
// TODO 芋艿:这里应该是 write
|
||||
import { WriteApi, WriteVO } from '@/api/ai/writer'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { WriteApi, AiWritePageReqVO, AiWriteRespVo } from '@/api/ai/write'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
|
||||
/** AI 写作列表 */
|
||||
|
@ -177,17 +185,18 @@ defineOptions({ name: 'AiWriteManager' })
|
|||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
const router = useRouter() // 路由
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const list = ref<WriteVO[]>([]) // 列表的数据
|
||||
const list = ref<AiWriteRespVo[]>([]) // 列表的数据
|
||||
const total = ref(0) // 列表的总页数
|
||||
const queryParams = reactive({
|
||||
const queryParams = reactive<AiWritePageReqVO>({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
userId: undefined,
|
||||
type: undefined,
|
||||
platform: undefined,
|
||||
createTime: []
|
||||
createTime: undefined
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const userList = ref<UserApi.UserVO[]>([]) // 用户列表
|
||||
|
@ -216,6 +225,15 @@ const resetQuery = () => {
|
|||
handleQuery()
|
||||
}
|
||||
|
||||
/** 新增方法,跳转到写作页面 **/
|
||||
const openForm = (type: string, id?: number) => {
|
||||
switch (type) {
|
||||
case 'create':
|
||||
router.push('/ai/write')
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
|
|
|
@ -50,4 +50,9 @@ declare global {
|
|||
name: string
|
||||
children?: Tree[] | any[]
|
||||
}
|
||||
// 分页数据公共返回
|
||||
interface PageResult<T> {
|
||||
list: T // 数据
|
||||
total: number // 总量
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue