!479 AI 大模型的草稿提交(code review 代码)

Merge pull request !479 from 芋道源码/dev
pull/486/MERGE
芋道源码 2024-07-17 00:52:23 +00:00 committed by Gitee
commit 21c7c17e8f
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
21 changed files with 345 additions and 199 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 348 KiB

View File

@ -191,26 +191,24 @@ ps核心功能已经实现正在对接微信小程序中...
### 商城系统
演示地址:<https://doc.iocoder.cn/mall-preview/>
![功能图](/.image/common/mall-feature.png)
![功能图](/.image/common/mall-preview.png)
_前端基于 crmeb uniapp 经过授权重构优化代码实现接入芋道快速开发平台_
演示地址:<https://doc.iocoder.cn/mall-preview/>
### ERP 系统
![功能图](/.image/common/erp-feature.png)
演示地址:<https://doc.iocoder.cn/erp-preview/>
![功能图](/.image/common/erp-feature.png)
### CRM 系统
![功能图](/.image/common/crm-feature.png)
演示地址:<https://doc.iocoder.cn/crm-preview/>
![功能图](/.image/common/crm-feature.png)
## 🐷 演示图
### 系统功能

View File

@ -56,6 +56,10 @@ export const ImageApi = {
getImagePageMy: async (params: PageParam) => {
return await request.get({ url: `/ai/image/my-page`, params })
},
// 获取公开的绘图记录
getImagePagePublic: async (params: PageParam) => {
return await request.get({ url: `/ai/image/public-page`, params })
},
// 获取【我的】绘图记录
getImageMy: async (id: number) => {
return await request.get({ url: `/ai/image/get-my?id=${id}` })

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

@ -36,7 +36,13 @@
<el-text tag="b">平台</el-text>
</div>
<el-space wrap class="group-item-body">
<el-select v-model="otherPlatform" placeholder="Select" size="large" class="!w-350px" @change="handlerPlatformChange">
<el-select
v-model="otherPlatform"
placeholder="Select"
size="large"
class="!w-350px"
@change="handlerPlatformChange"
>
<el-option
v-for="item in OtherPlatformEnum"
:key="item.key"
@ -52,12 +58,7 @@
</div>
<el-space wrap class="group-item-body">
<el-select v-model="model" placeholder="Select" size="large" class="!w-350px">
<el-option
v-for="item in models"
:key="item.key"
:label="item.name"
:value="item.key"
/>
<el-option v-for="item in models" :key="item.key" :label="item.name" :value="item.key" />
</el-select>
</el-space>
</div>
@ -77,12 +78,14 @@
</div>
</template>
<script setup lang="ts">
import {ImageApi, ImageDrawReqVO, ImageVO} from '@/api/ai/image'
import { ImageApi, ImageDrawReqVO, ImageVO } from '@/api/ai/image'
import {
AiPlatformEnum,
ChatGlmModels,
ImageHotWords,
ImageModelVO,
OtherPlatformEnum,
QianFanModels,
TongYiWanXiangModels
} from '@/views/ai/utils/constants'
@ -96,10 +99,9 @@ const prompt = ref<string>('') // 提示词
const width = ref<number>(512) //
const height = ref<number>(512) //
const otherPlatform = ref<string>(AiPlatformEnum.TONG_YI) //
const models = ref<ImageModelVO[]>(TongYiWanXiangModels) //
const models = ref<ImageModelVO[]>(TongYiWanXiangModels) // TongYiWanXiangModelsQianFanModels
const model = ref<string>(models.value[0].key) //
const emits = defineEmits(['onDrawStart', 'onDrawComplete']) // emits
/** 选择热词 */
@ -131,9 +133,8 @@ const handleGenerateImage = async () => {
prompt: prompt.value, //
width: width.value, //
height: height.value, //
options: {
}
} as ImageDrawReqVO
options: {}
} as unknown as ImageDrawReqVO
await ImageApi.drawImage(form)
} finally {
//
@ -148,21 +149,24 @@ const settingValues = async (detail: ImageVO) => {
prompt.value = detail.prompt
width.value = detail.width
height.value = detail.height
}
/** 平台切换 */
const handlerPlatformChange = async (platform) => {
const handlerPlatformChange = async (platform: string) => {
//
if (AiPlatformEnum.YI_YAN === platform) {
if (AiPlatformEnum.TONG_YI === platform) {
models.value = TongYiWanXiangModels
} else if (AiPlatformEnum.YI_YAN === platform) {
models.value = QianFanModels
} else if (AiPlatformEnum.ZHI_PU === platform) {
models.value = ChatGlmModels
} else {
models.value = []
}
//
if (models.value.length > 0) {
model.value = models.value[0].key
} else {
} else {
model.value = ''
}
}

View File

@ -62,7 +62,7 @@ const platformOptions = [
value: AiPlatformEnum.STABLE_DIFFUSION
},
{
label: '其',
label: '其',
value: 'other'
}
]
@ -88,6 +88,7 @@ const handleRegeneration = async (image: ImageVO) => {
} else if (image.platform === AiPlatformEnum.STABLE_DIFFUSION) {
stableDiffusionRef.value.settingValues(image)
}
// TODO @fan other
}
</script>

View File

@ -0,0 +1,41 @@
<template>
<div class="card-list">
<div v-for="item in publicList" :key="item.id" class="card">
<img :src="item.picUrl" class="img" />
</div>
</div>
</template>
<script setup lang="ts">
import { ImageApi, ImageVO } from '@/api/ai/image'
/** 属性 */
// TODO @fanqueryParams
const pageNo = ref<number>(1)
const pageSize = ref<number>(20)
const publicList = ref<ImageVO[]>([])
/** 获取数据 */
const getListData = async () => {
const res = await ImageApi.getImagePagePublic({ pageNo: pageNo.value, pageSize: pageSize.value })
publicList.value = res.list as ImageVO[]
console.log('publicList.value', publicList.value)
}
onMounted(async () => {
await getListData()
})
</script>
<style scoped lang="scss">
.card-list {
//display: flex;
//flex-direction: column;
column-count: 4;
column-gap: 3px;
}
.card {
.img {
width: 50%;
}
}
</style>

View File

@ -1,5 +1,5 @@
<template>
<div class="flex h-1/1">
<div class="flex ">
<!-- 模式 -->
<Mode class="flex-none" @generate-music="generateMusic"/>
<!-- 音频列表 -->
@ -13,7 +13,7 @@ import List from './list/index.vue'
defineOptions({ name: 'Index' })
const listRef = ref<{generateMusic: (...args) => void} | null>(null)
const listRef = ref<Nullable<{generateMusic: (...args) => void}>>(null)
function generateMusic (args: {formData: Recordable}) {
unref(listRef)?.generateMusic(args.formData)

View File

@ -1,9 +1,68 @@
<template>
<div class="h-72px bg-[var(--el-bg-color-overlay)] b-solid b-1 b-[var(--el-border-color)] b-l-none">播放器</div>
<div class="flex items-center justify-between px-2 h-72px bg-[var(--el-bg-color-overlay)] b-solid b-1 b-[var(--el-border-color)] b-l-none">
<!-- 歌曲信息 -->
<div class="flex gap-[10px]">
<el-image src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png" class="w-[45px]"/>
<div>
<div>我很好</div>
<div class="text-[12px] text-gray-400">刘大壮</div>
</div>
</div>
<!-- 音频controls -->
<div class="flex gap-[12px] items-center">
<Icon icon="majesticons:back-circle" :size="20" class="text-gray-300 cursor-pointer"/>
<Icon :icon="audioProps.paused ? 'mdi:arrow-right-drop-circle' : 'solar:pause-circle-bold'" :size="30" class=" cursor-pointer" @click="toggleStatus('paused')"/>
<Icon icon="majesticons:next-circle" :size="20" class="text-gray-300 cursor-pointer"/>
<div class="flex gap-[16px] items-center">
<span>{{audioProps.currentTime}}</span>
<el-slider v-model="audioProps.duration" color="#409eff" class="w-[160px!important] "/>
<span>{{ audioProps.duration }}</span>
</div>
<!-- 音频 -->
<audio v-bind="audioProps" ref="audioRef" controls v-show="!audioProps" @timeupdate="audioTimeUpdate">
<source :src="response"/>
</audio>
</div>
<!-- 音量控制器 -->
<div class="flex gap-[16px] items-center">
<Icon :icon="audioProps.muted ? 'tabler:volume-off' : 'tabler:volume'" :size="20" class="cursor-pointer" @click="toggleStatus('muted')"/>
<el-slider v-model="audioProps.volume" color="#409eff" class="w-[160px!important] "/>
</div>
</div>
</template>
<script lang="ts" setup>
import { formatPast } from '@/utils/formatTime'
import response from '@/assets/audio/response.mp3'
defineOptions({ name: 'Index' })
const audioRef = ref<Nullable<HTMLElement>>(null)
// https://www.runoob.com/tags/ref-av-dom.html
const audioProps = reactive({
autoplay: true,
paused: false,
currentTime: '00:00',
duration: '00:00',
muted: false,
volume: 50,
})
function toggleStatus (type: string) {
audioProps[type] = !audioProps[type]
if (type === 'paused' && audioRef.value) {
if (audioProps[type]) {
audioRef.value.pause()
} else {
audioRef.value.play()
}
}
}
//
function audioTimeUpdate (args) {
audioProps.currentTime = formatPast(new Date(args.timeStamp), 'mm:ss')
}
</script>

View File

@ -1,5 +1,5 @@
<template>
<div class="flex flex-col h-full">
<div class="flex flex-col h-[600px]">
<div class="flex-auto flex overflow-hidden">
<el-tabs v-model="currentType" class="flex-auto px-[var(--app-content-padding)]">
<!-- 我的创作 -->

View File

@ -1,5 +1,5 @@
<template>
<ContentWrap class="w-300px h-full">
<ContentWrap class="w-300px h-full mb-[0!important]">
<el-radio-group v-model="generateMode" class="mb-15px">
<el-radio-button label="desc">
描述模式
@ -28,10 +28,7 @@ const emits = defineEmits(['generate-music'])
const generateMode = ref('lyric')
interface ModeRef {
formData: Recordable
}
const modeRef = ref<ModeRef | null>(null)
const modeRef = ref<Nullable<{ formData: Recordable }>>(null)
/*
*@Description: 根据信息生成音乐

View File

@ -20,17 +20,21 @@ export const AiPlatformEnum = {
Ollama: 'Ollama',
STABLE_DIFFUSION: 'StableDiffusion', // Stability AI
MIDJOURNEY: 'Midjourney', // Midjourney
SUNO: 'Suno', // Suno AI
SUNO: 'Suno' // Suno AI
}
export const OtherPlatformEnum:ImageModelVO [] = [
export const OtherPlatformEnum: ImageModelVO[] = [
{
key: AiPlatformEnum.TONG_YI,
name: '通义万相'
},
{
key: AiPlatformEnum.YI_YAN,
name: '百度图片'
name: '百度千帆'
},
{
key: AiPlatformEnum.ZHI_PU,
name: '智谱 AI'
}
]
@ -60,6 +64,12 @@ export enum AiWriteTypeEnum {
REPLY // 回复
}
// 表格展示对照map
export const AiWriteTypeTableRender = {
[AiWriteTypeEnum.WRITING]: '撰写',
[AiWriteTypeEnum.REPLY]: '回复',
}
// ========== 【图片 UI】相关的枚举 ==========
export const ImageHotWords = [
'中国旗袍',
@ -200,54 +210,6 @@ export const StableDiffusionStylePresets: ImageModelVO[] = [
}
]
// todo @芋艿 这些是通义的风格,看要不要删除
export const TongYiWanXiangStylePresets: ImageModelVO[] = [
{
key: '-1',
name: '上传图像风格'
},
{
key: '0',
name: '复古漫画'
},
{
key: '1',
name: '3D童话'
},
{
key: '2',
name: '二次元'
},
{
key: '3',
name: '小清新'
},
{
key: '4',
name: '未来科技'
},
{
key: '5',
name: '国画古风'
},
{
key: '6',
name: '将军百战'
},
{
key: '7',
name: '炫彩卡通'
},
{
key: '8',
name: '清雅国风'
},
{
key: '9',
name: '喜迎新年'
}
]
export const TongYiWanXiangModels: ImageModelVO[] = [
{
key: 'wanx-v1',
@ -259,6 +221,20 @@ export const TongYiWanXiangModels: ImageModelVO[] = [
}
]
export const QianFanModels: ImageModelVO[] = [
{
key: 'sd_xl',
name: 'sd_xl'
}
]
export const ChatGlmModels: ImageModelVO[] = [
{
key: 'cogview-3',
name: 'cogview-3'
}
]
export const StableDiffusionClipGuidancePresets: ImageModelVO[] = [
{
key: 'NONE',
@ -318,7 +294,7 @@ export const Dall3StyleList: ImageModelVO[] = [
export interface ImageSizeVO {
key: string
name: string
name?: string
style: string
width: string
height: string

View File

@ -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] }
}
}
/** 提交写作 */

View File

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

View File

@ -1,3 +1,4 @@
<!-- 标签选项 -->
<template>
<div class="flex flex-wrap gap-[8px]">
<span

View File

@ -1,6 +1,5 @@
<template>
<!-- TODO @hhhero整体没啥问题了感觉整体框框的样子可以优化下可以参考下绘图界面例如说1写作的预览和绘图的绘图任务 header2左右的边界有个竖线之类的 -->
<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, WriteVO } from '@/api/ai/write'
import { WriteExample } from '@/views/ai/utils/constants'
const message = useMessage()
@ -38,7 +37,7 @@ const stopStream = () => {
/** 执行写作 */
const rightRef = ref<InstanceType<typeof Right>>()
const submit = (data) => {
const submit = (data: WriteVO) => {
abortController.value = new AbortController()
writeResult.value = ''
isWriting.value = true
@ -66,7 +65,7 @@ const submit = (data) => {
}
/** 点击示例触发 */
const handleExampleClick = (type: keyof typeof WriteExampleDataJson) => {
const handleExampleClick = (type: keyof typeof WriteExample) => {
writeResult.value = WriteExample[type].data
}

View File

@ -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 {

View File

@ -28,9 +28,7 @@ const message = useMessage() // 消息弹窗
// ======================= WebSocket start =======================
const server = ref(
(import.meta.env.VITE_BASE_URL + '/infra/ws/').replace('http', 'ws') +
'?token=' +
getAccessToken()
(import.meta.env.VITE_BASE_URL + '/infra/ws').replace('http', 'ws') + '?token=' + getAccessToken()
) // WebSocket
/** 发起 WebSocket 连接 */

5
types/global.d.ts vendored
View File

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