!482 [代码优化]AI: 写作页面样式调整,目录更名writer => write,写作管理更改

Merge pull request !482 from hhhero/dev
pull/479/head
芋道源码 2024-07-16 04:55:39 +00:00 committed by Gitee
commit 35b2708ca6
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
9 changed files with 181 additions and 108 deletions

85
src/api/ai/write/index.ts Normal file
View File

@ -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 } })
}
}

View File

@ -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 })
}
}

View File

@ -64,6 +64,12 @@ export enum AiWriteTypeEnum {
REPLY // 回复 REPLY // 回复
} }
// 表格展示对照map
export const AiWriteTypeTableRender = {
[AiWriteTypeEnum.WRITING]: '撰写',
[AiWriteTypeEnum.REPLY]: '回复',
}
// ========== 【图片 UI】相关的枚举 ========== // ========== 【图片 UI】相关的枚举 ==========
export const ImageHotWords = [ export const ImageHotWords = [
'中国旗袍', '中国旗袍',

View File

@ -24,14 +24,15 @@
</h3> </h3>
</DefineLabel> </DefineLabel>
<div class="relative" v-bind="$attrs"> <div class="flex flex-col" v-bind="$attrs">
<!-- tab --> <!-- tab -->
<div <div class="w-full pt-2 bg-[#f5f7f9] flex justify-center">
class="absolute left-1/2 top-2 -translate-x-1/2 w-[303px] rounded-full bg-[#DDDFE3] p-1 z-10" <div class="w-[303px] rounded-full bg-[#DDDFE3] p-1 z-10">
>
<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 === AiWriteTypeEnum.REPLY && '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"
@ -42,8 +43,9 @@
/> />
</div> </div>
</div> </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> <div>
<template v-if="selectedTab === 1"> <template v-if="selectedTab === 1">
@ -102,7 +104,7 @@
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 { WriteVO } from '@/api/ai/writer' import { WriteVO } from 'src/api/ai/write'
import { omit } from 'lodash-es' import { omit } from 'lodash-es'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { AiWriteTypeEnum, WriteExample } from '@/views/ai/utils/constants' import { AiWriteTypeEnum, WriteExample } from '@/views/ai/utils/constants'
@ -177,10 +179,18 @@ const initData: WriteVO = {
} }
const formData = ref<WriteVO>({ ...initData }) const formData = ref<WriteVO>({ ...initData })
/** 用来记录切换之前所填写的数据,切换的时候给赋值回来 **/
const recordFormData = {} as Record<AiWriteTypeEnum, WriteVO>
/** 切换tab **/ /** 切换tab **/
const switchTab = (value: TabType) => { const switchTab = (value: TabType) => {
if (value !== selectedTab.value) {
//
recordFormData[selectedTab.value] = formData.value
selectedTab.value = value selectedTab.value = value
formData.value = { ...initData } //
formData.value = { ...initData, ...recordFormData[value] }
}
} }
/** 提交写作 */ /** 提交写作 */

View File

@ -1,6 +1,7 @@
<template> <template>
<div class="h-full box-border flex flex-col px-7"> <el-card class="my-card h-full">
<h3 class="m-0 h-14 -mx-7 px-7 shrink-0 flex items-center justify-between bg-[#ecedef]"> <template #header
><h3 class="m-0 px-7 shrink-0 flex items-center justify-between">
<span>预览</span> <span>预览</span>
<!-- 展示在右上角 --> <!-- 展示在右上角 -->
<el-button color="#846af7" v-show="showCopy" @click="copyContent" size="small"> <el-button color="#846af7" v-show="showCopy" @click="copyContent" size="small">
@ -9,9 +10,10 @@
</template> </template>
复制 复制
</el-button> </el-button>
</h3> </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"> <div class="w-full min-h-full relative flex-grow bg-white box-border p-3 sm:p-7">
<!-- 终止生成内容的按钮 --> <!-- 终止生成内容的按钮 -->
<el-button <el-button
@ -36,7 +38,7 @@
/> />
</div> </div>
</div> </div>
</div> </el-card>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@ -102,4 +104,17 @@ watch(copied, (val) => {
height: 0; 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> </style>

View File

@ -1,6 +1,5 @@
<template> <template>
<!-- TODO @hhhero整体没啥问题了感觉整体框框的样子可以优化下可以参考下绘图界面例如说1写作的预览和绘图的绘图任务 header2左右的边界有个竖线之类的 --> <div class="absolute top-0 left-0 right-0 bottom-0 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"
class="h-full" class="h-full"
@ -21,7 +20,7 @@
<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 { WriteApi } from '@/api/ai/write'
import { WriteExample } from '@/views/ai/utils/constants' import { WriteExample } from '@/views/ai/utils/constants'
const message = useMessage() 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 writeResult.value = WriteExample[type].data
} }

View File

@ -39,7 +39,7 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="平台" prop="platform"> <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 <el-option
v-for="dict in getStrDictOptions(DICT_TYPE.AI_PLATFORM)" v-for="dict in getStrDictOptions(DICT_TYPE.AI_PLATFORM)"
:key="dict.value" :key="dict.value"
@ -70,6 +70,7 @@
> >
<Icon icon="ep:plus" class="mr-5px" /> 新增 <Icon icon="ep:plus" class="mr-5px" /> 新增
</el-button> </el-button>
<!-- TODO @YunaiV 目前没有导出接口需要导出吗 -->
<el-button <el-button
type="success" type="success"
plain plain
@ -103,7 +104,13 @@
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="模型" align="center" prop="model" width="180" /> <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="generatedContent" width="180" />
<el-table-column label="原文" align="center" prop="originalContent" width="180" /> <el-table-column label="原文" align="center" prop="originalContent" width="180" />
<el-table-column label="长度" align="center" prop="length"> <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" prop="errorMessage" />
<el-table-column label="操作" align="center"> <el-table-column label="操作" align="center">
<template #default="scope"> <template #default="scope">
<!-- TODO @YunaiV 目前没有修改接口写作要可以更改吗-->
<el-button <el-button
link link
type="primary" type="primary"
@ -168,8 +176,8 @@
<script setup lang="ts"> <script setup lang="ts">
import { DICT_TYPE, getIntDictOptions, getStrDictOptions } from '@/utils/dict' import { DICT_TYPE, getIntDictOptions, getStrDictOptions } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime' import { dateFormatter } from '@/utils/formatTime'
// TODO write import { useRouter } from 'vue-router'
import { WriteApi, WriteVO } from '@/api/ai/writer' import { WriteApi, AiWritePageReqVO, AiWriteRespVo } from '@/api/ai/write'
import * as UserApi from '@/api/system/user' import * as UserApi from '@/api/system/user'
/** AI 写作列表 */ /** AI 写作列表 */
@ -177,17 +185,18 @@ defineOptions({ name: 'AiWriteManager' })
const message = useMessage() // const message = useMessage() //
const { t } = useI18n() // const { t } = useI18n() //
const router = useRouter() //
const loading = ref(true) // const loading = ref(true) //
const list = ref<WriteVO[]>([]) // const list = ref<AiWriteRespVo[]>([]) //
const total = ref(0) // const total = ref(0) //
const queryParams = reactive({ const queryParams = reactive<AiWritePageReqVO>({
pageNo: 1, pageNo: 1,
pageSize: 10, pageSize: 10,
userId: undefined, userId: undefined,
type: undefined, type: undefined,
platform: undefined, platform: undefined,
createTime: [] createTime: undefined
}) })
const queryFormRef = ref() // const queryFormRef = ref() //
const userList = ref<UserApi.UserVO[]>([]) // const userList = ref<UserApi.UserVO[]>([]) //
@ -216,6 +225,15 @@ const resetQuery = () => {
handleQuery() handleQuery()
} }
/** 新增方法,跳转到写作页面 **/
const openForm = (type: string, id?: number) => {
switch (type) {
case 'create':
router.push('/ai/write')
break
}
}
/** 删除按钮操作 */ /** 删除按钮操作 */
const handleDelete = async (id: number) => { const handleDelete = async (id: number) => {
try { try {

5
types/global.d.ts vendored
View File

@ -50,4 +50,9 @@ declare global {
name: string name: string
children?: Tree[] | any[] children?: Tree[] | any[]
} }
// 分页数据公共返回
interface PageResult<T> {
list: T // 数据
total: number // 总量
}
} }