Merge remote-tracking branch 'refs/remotes/yudao/dev' into dev-crm

pull/471/head
puhui999 2024-07-05 18:01:40 +08:00
commit d3c596dcaf
17 changed files with 299 additions and 219 deletions

View File

@ -43,8 +43,8 @@ export const ChatConversationApi = {
}, },
// 删除【我的】所有对话,置顶除外 // 删除【我的】所有对话,置顶除外
deleteMyAllExceptPinned: async () => { deleteChatConversationMyByUnpinned: async () => {
return await request.delete({ url: `/ai/chat/conversation/delete-my-all-except-pinned` }) return await request.delete({ url: `/ai/chat/conversation/delete-by-unpinned` })
}, },
// 获得【我的】聊天对话列表 // 获得【我的】聊天对话列表

View File

@ -16,6 +16,7 @@ export interface ImageVO {
taskId: number // 任务编号 taskId: number // 任务编号
buttons: ImageMjButtonsVO[] // mj 操作按钮 buttons: ImageMjButtonsVO[] // mj 操作按钮
createTime: string // 创建时间 createTime: string // 创建时间
finishTime: string // 完成时间
} }
export interface ImagePageReqVO { export interface ImagePageReqVO {

BIN
src/assets/ai/dall2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

BIN
src/assets/ai/dall3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

BIN
src/assets/ai/qingxi.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

BIN
src/assets/ai/ziran.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

View File

@ -29,10 +29,29 @@ const download = {
html: (data: Blob, fileName: string) => { html: (data: Blob, fileName: string) => {
download0(data, fileName, 'text/html') download0(data, fileName, 'text/html')
}, },
// 下载 MarkdownView 方法 // 下载 Markdown 方法
markdown: (data: Blob, fileName: string) => { markdown: (data: Blob, fileName: string) => {
download0(data, fileName, 'text/markdown') download0(data, fileName, 'text/markdown')
} }
} }
export default download export default download
/** 图片下载(通过浏览器图片下载) */
export const downloadImage = async (imageUrl) => {
const image = new Image()
image.setAttribute('crossOrigin', 'anonymous')
image.src = imageUrl
image.onload = () => {
const canvas = document.createElement('canvas')
canvas.width = image.width
canvas.height = image.height
const ctx = canvas.getContext('2d') as CanvasDrawImage
ctx.drawImage(image, 0, 0, image.width, image.height)
const url = canvas.toDataURL('image/png')
const a = document.createElement('a')
a.href = url
a.download = 'image.png'
a.click()
}
}

View File

@ -1,11 +1,10 @@
<!-- AI 对话 --> <!-- AI 对话 -->
<template> <template>
<el-aside width="260px" class="conversation-container" style="height: 100%;"> <el-aside width="260px" class="conversation-container" style="height: 100%">
<!-- 左顶部对话 --> <!-- 左顶部对话 -->
<div style="height: 100%;"> <div style="height: 100%">
<el-button class="w-1/1 btn-new-conversation" type="primary" @click="createConversation"> <el-button class="w-1/1 btn-new-conversation" type="primary" @click="createConversation">
<Icon icon="ep:plus" class="mr-5px"/> <Icon icon="ep:plus" class="mr-5px" />
新建对话 新建对话
</el-button> </el-button>
@ -18,17 +17,19 @@
@keyup="searchConversation" @keyup="searchConversation"
> >
<template #prefix> <template #prefix>
<Icon icon="ep:search"/> <Icon icon="ep:search" />
</template> </template>
</el-input> </el-input>
<!-- 左中间对话列表 --> <!-- 左中间对话列表 -->
<div class="conversation-list"> <div class="conversation-list">
<el-empty v-if="loading" description="." :v-loading="loading" /> <el-empty v-if="loading" description="." :v-loading="loading" />
<div v-for="conversationKey in Object.keys(conversationMap)" :key="conversationKey"> <div v-for="conversationKey in Object.keys(conversationMap)" :key="conversationKey">
<div class="conversation-item classify-title" v-if="conversationMap[conversationKey].length"> <div
class="conversation-item classify-title"
v-if="conversationMap[conversationKey].length"
>
<el-text class="mx-1" size="small" tag="b">{{ conversationKey }}</el-text> <el-text class="mx-1" size="small" tag="b">{{ conversationKey }}</el-text>
</div> </div>
<div <div
@ -40,25 +41,27 @@
@mouseout="hoverConversationId = ''" @mouseout="hoverConversationId = ''"
> >
<div <div
:class="conversation.id === activeConversationId ? 'conversation active' : 'conversation'" :class="
conversation.id === activeConversationId ? 'conversation active' : 'conversation'
"
> >
<div class="title-wrapper"> <div class="title-wrapper">
<img class="avatar" :src="conversation.roleAvatar || roleAvatarDefaultImg"/> <img class="avatar" :src="conversation.roleAvatar || roleAvatarDefaultImg" />
<span class="title">{{ conversation.title }}</span> <span class="title">{{ conversation.title }}</span>
</div> </div>
<div class="button-wrapper" v-show="hoverConversationId === conversation.id"> <div class="button-wrapper" v-show="hoverConversationId === conversation.id">
<el-button class="btn" link @click.stop="handlerTop(conversation)" > <el-button class="btn" link @click.stop="handlerTop(conversation)">
<el-icon title="置顶" v-if="!conversation.pinned"><Top /></el-icon> <el-icon title="置顶" v-if="!conversation.pinned"><Top /></el-icon>
<el-icon title="置顶" v-if="conversation.pinned"><Bottom /></el-icon> <el-icon title="置顶" v-if="conversation.pinned"><Bottom /></el-icon>
</el-button> </el-button>
<el-button class="btn" link @click.stop="updateConversationTitle(conversation)"> <el-button class="btn" link @click.stop="updateConversationTitle(conversation)">
<el-icon title="编辑" > <el-icon title="编辑">
<Icon icon="ep:edit"/> <Icon icon="ep:edit" />
</el-icon> </el-icon>
</el-button> </el-button>
<el-button class="btn" link @click.stop="deleteChatConversation(conversation)"> <el-button class="btn" link @click.stop="deleteChatConversation(conversation)">
<el-icon title="删除对话" > <el-icon title="删除对话">
<Icon icon="ep:delete"/> <Icon icon="ep:delete" />
</el-icon> </el-icon>
</el-button> </el-button>
</div> </div>
@ -66,20 +69,19 @@
</div> </div>
</div> </div>
<!-- 底部站位 --> <!-- 底部站位 -->
<div style="height: 160px; width: 100%;"></div> <div style="height: 160px; width: 100%"></div>
</div> </div>
</div> </div>
<!-- 左底部工具栏 --> <!-- 左底部工具栏 -->
<!-- TODO @fan下面两个 icon可以使用类似 <Icon icon="ep:question-filled" /> 替代哈 --> <!-- TODO @fan下面两个 icon可以使用类似 <Icon icon="ep:question-filled" /> 替代哈 -->
<div class="tool-box"> <div class="tool-box">
<div @click="handleRoleRepository"> <div @click="handleRoleRepository">
<Icon icon="ep:user"/> <Icon icon="ep:user" />
<el-text size="small">角色仓库</el-text> <el-text size="small">角色仓库</el-text>
</div> </div>
<div @click="handleClearConversation"> <div @click="handleClearConversation">
<Icon icon="ep:delete"/> <Icon icon="ep:delete" />
<el-text size="small">清空未置顶对话</el-text> <el-text size="small">清空未置顶对话</el-text>
</div> </div>
</div> </div>
@ -88,17 +90,16 @@
<!-- 角色仓库抽屉 --> <!-- 角色仓库抽屉 -->
<el-drawer v-model="drawer" title="角色仓库" size="754px"> <el-drawer v-model="drawer" title="角色仓库" size="754px">
<Role/> <Role />
</el-drawer> </el-drawer>
</el-aside> </el-aside>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import {ChatConversationApi, ChatConversationVO} from '@/api/ai/chat/conversation' import { ChatConversationApi, ChatConversationVO } from '@/api/ai/chat/conversation'
import {ref} from "vue"; import { ref } from 'vue'
import Role from "@/views/ai/chat/role/index.vue"; import Role from '@/views/ai/chat/role/index.vue'
import {Bottom, Top} from "@element-plus/icons-vue"; import { Bottom, Top } from '@element-plus/icons-vue'
import roleAvatarDefaultImg from '@/assets/ai/gpt.svg' import roleAvatarDefaultImg from '@/assets/ai/gpt.svg'
const message = useMessage() // const message = useMessage() //
@ -107,8 +108,8 @@ const message = useMessage() // 消息弹窗
const searchName = ref<string>('') // const searchName = ref<string>('') //
const activeConversationId = ref<string | null>(null) // null const activeConversationId = ref<string | null>(null) // null
const hoverConversationId = ref<string | null>(null) // const hoverConversationId = ref<string | null>(null) //
const conversationList = ref([] as ChatConversationVO[]) // const conversationList = ref([] as ChatConversationVO[]) //
const conversationMap = ref<any>({}) // () const conversationMap = ref<any>({}) // ()
const drawer = ref<boolean>(false) // TODO @fanroleDrawer const drawer = ref<boolean>(false) // TODO @fanroleDrawer
const loading = ref<boolean>(false) // const loading = ref<boolean>(false) //
const loadingTime = ref<any>() // const loadingTime = ref<any>() //
@ -138,7 +139,7 @@ const searchConversation = async (e) => {
conversationMap.value = await conversationTimeGroup(conversationList.value) conversationMap.value = await conversationTimeGroup(conversationList.value)
} else { } else {
// //
const filterValues = conversationList.value.filter(item => { const filterValues = conversationList.value.filter((item) => {
return item.title.includes(searchName.value.trim()) return item.title.includes(searchName.value.trim())
}) })
conversationMap.value = await conversationTimeGroup(filterValues) conversationMap.value = await conversationTimeGroup(filterValues)
@ -150,7 +151,7 @@ const searchConversation = async (e) => {
*/ */
const handleConversationClick = async (id: string) => { const handleConversationClick = async (id: string) => {
// //
const filterConversation = conversationList.value.filter(item => { const filterConversation = conversationList.value.filter((item) => {
return item.id === id return item.id === id
}) })
// onConversationClick // onConversationClick
@ -211,20 +212,20 @@ const getChatConversationList = async () => {
const conversationTimeGroup = async (list: ChatConversationVO[]) => { const conversationTimeGroup = async (list: ChatConversationVO[]) => {
// (30) // (30)
const groupMap = { const groupMap = {
'置顶': [], 置顶: [],
'今天': [], 今天: [],
'一天前': [], 一天前: [],
'三天前': [], 三天前: [],
'七天前': [], 七天前: [],
'三十天前': [] 三十天前: []
} }
// //
const now = Date.now(); const now = Date.now()
// //
const oneDay = 24 * 60 * 60 * 1000; const oneDay = 24 * 60 * 60 * 1000
const threeDays = 3 * oneDay; const threeDays = 3 * oneDay
const sevenDays = 7 * oneDay; const sevenDays = 7 * oneDay
const thirtyDays = 30 * oneDay; const thirtyDays = 30 * oneDay
for (const conversation: ChatConversationVO of list) { for (const conversation: ChatConversationVO of list) {
// //
if (conversation.pinned) { if (conversation.pinned) {
@ -232,7 +233,7 @@ const conversationTimeGroup = async (list: ChatConversationVO[]) => {
continue continue
} }
// //
const diff = now - conversation.updateTime; const diff = now - conversation.updateTime
// //
if (diff < oneDay) { if (diff < oneDay) {
groupMap['今天'].push(conversation) groupMap['今天'].push(conversation)
@ -271,7 +272,7 @@ const createConversation = async () => {
*/ */
const updateConversationTitle = async (conversation: ChatConversationVO) => { const updateConversationTitle = async (conversation: ChatConversationVO) => {
// 1. // 1.
const {value} = await ElMessageBox.prompt('修改标题', { const { value } = await ElMessageBox.prompt('修改标题', {
inputPattern: /^[\s\S]*.*\S[\s\S]*$/, // inputPattern: /^[\s\S]*.*\S[\s\S]*$/, //
inputErrorMessage: '标题不能为空', inputErrorMessage: '标题不能为空',
inputValue: conversation.title inputValue: conversation.title
@ -285,7 +286,7 @@ const updateConversationTitle = async (conversation: ChatConversationVO) => {
// 3. // 3.
await getChatConversationList() await getChatConversationList()
// 4. // 4.
const filterConversationList = conversationList.value.filter(item => { const filterConversationList = conversationList.value.filter((item) => {
return item.id === conversation.id return item.id === conversation.id
}) })
if (filterConversationList.length > 0) { if (filterConversationList.length > 0) {
@ -310,8 +311,7 @@ const deleteChatConversation = async (conversation: ChatConversationVO) => {
await getChatConversationList() await getChatConversationList()
// //
emits('onConversationDelete', conversation) emits('onConversationDelete', conversation)
} catch { } catch {}
}
} }
/** /**
@ -343,16 +343,13 @@ const handleRoleRepository = async () => {
*/ */
const handleClearConversation = async () => { const handleClearConversation = async () => {
// TODO @fan使 await message.confirm( 使 await // TODO @fan使 await message.confirm( 使 await
ElMessageBox.confirm( ElMessageBox.confirm('确认后对话会全部清空,置顶的对话除外。', '确认提示', {
'确认后对话会全部清空,置顶的对话除外。', confirmButtonText: '确认',
'确认提示', cancelButtonText: '取消',
{ type: 'warning'
confirmButtonText: '确认', })
cancelButtonText: '取消',
type: 'warning',
})
.then(async () => { .then(async () => {
await ChatConversationApi.deleteMyAllExceptPinned() await ChatConversationApi.deleteChatConversationMyByUnpinned()
ElMessage({ ElMessage({
message: '操作成功!', message: '操作成功!',
type: 'success' type: 'success'
@ -364,8 +361,7 @@ const handleClearConversation = async () => {
// //
emits('onConversationClear') emits('onConversationClear')
}) })
.catch(() => { .catch(() => {})
})
} }
// ============ onMounted // ============ onMounted
@ -377,7 +373,7 @@ watch(activeId, async (newValue, oldValue) => {
}) })
// public // public
defineExpose({createConversation}) defineExpose({ createConversation })
onMounted(async () => { onMounted(async () => {
// //
@ -394,11 +390,9 @@ onMounted(async () => {
} }
} }
}) })
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.conversation-container { .conversation-container {
position: relative; position: relative;
display: flex; display: flex;

View File

@ -2,7 +2,7 @@
<el-drawer <el-drawer
v-model="showDrawer" v-model="showDrawer"
title="图片详细" title="图片详细"
@close="handlerDrawerClose" @close="handleDrawerClose"
custom-class="drawer-class" custom-class="drawer-class"
> >
<!-- 图片 --> <!-- 图片 -->
@ -22,8 +22,7 @@
<div class="tip">时间</div> <div class="tip">时间</div>
<div class="body"> <div class="body">
<div>提交时间{{ imageDetail.createTime }}</div> <div>提交时间{{ imageDetail.createTime }}</div>
<!-- TODO @fan要不加个完成时间的字段 finishTimeupdateTime 不算特别合理哈 --> <div>生成时间{{ imageDetail.finishTime }}</div>
<div>生成时间{{ imageDetail.updateTime }}</div>
</div> </div>
</div> </div>
<!-- 模型 --> <!-- 模型 -->
@ -79,8 +78,8 @@ const props = defineProps({
}) })
/** 抽屉 - close */ /** 抽屉 - close */
const handlerDrawerClose = async () => { const handleDrawerClose = async () => {
emits('handlerDrawerClose') emits('handleDrawerClose')
} }
/** 获取 - 图片 detail */ /** 获取 - 图片 detail */
@ -90,7 +89,7 @@ const getImageDetail = async (id) => {
} }
/** 任务 - detail */ /** 任务 - detail */
const handlerTaskDetail = async () => { const handleTaskDetail = async () => {
showDrawer.value = true showDrawer.value = true
} }
@ -107,7 +106,7 @@ watch(id, async (newVal, oldVal) => {
} }
}) })
// //
const emits = defineEmits(['handlerDrawerClose']) const emits = defineEmits(['handleDrawerClose'])
// //
onMounted(async () => {}) onMounted(async () => {})
</script> </script>

View File

@ -6,8 +6,8 @@
v-for="image in imageList" v-for="image in imageList"
:key="image" :key="image"
:image-detail="image" :image-detail="image"
@on-btn-click="handlerImageBtnClick" @on-btn-click="handleImageBtnClick"
@on-mj-btn-click="handlerImageMjBtnClick" @on-mj-btn-click="handleImageMjBtnClick"
/> />
</div> </div>
<div class="task-image-pagination"> <div class="task-image-pagination">
@ -16,7 +16,7 @@
layout="prev, pager, next" layout="prev, pager, next"
:default-page-size="pageSize" :default-page-size="pageSize"
:total="pageTotal" :total="pageTotal"
@change="handlerPageChange" @change="handlePageChange"
/> />
</div> </div>
</el-card> </el-card>
@ -24,7 +24,7 @@
<ImageDetailDrawer <ImageDetailDrawer
:show="isShowImageDetail" :show="isShowImageDetail"
:id="showImageDetailId" :id="showImageDetailId"
@handler-drawer-close="handlerDrawerClose" @handle-drawer-close="handleDrawerClose"
/> />
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@ -33,6 +33,7 @@ import ImageDetailDrawer from './ImageDetailDrawer.vue'
import ImageTaskCard from './ImageTaskCard.vue' import ImageTaskCard from './ImageTaskCard.vue'
import { ElLoading, LoadingOptionsResolved } from 'element-plus' import { ElLoading, LoadingOptionsResolved } from 'element-plus'
import { AiImageStatusEnum } from '@/views/ai/utils/constants' import { AiImageStatusEnum } from '@/views/ai/utils/constants'
import { downloadImage } from '@/utils/download'
const message = useMessage() // const message = useMessage() //
@ -49,12 +50,12 @@ const pageSize = ref<number>(10) // page size
const pageTotal = ref<number>(0) // page size const pageTotal = ref<number>(0) // page size
/** 抽屉 - close */ /** 抽屉 - close */
const handlerDrawerClose = async () => { const handleDrawerClose = async () => {
isShowImageDetail.value = false isShowImageDetail.value = false
} }
/** 任务 - detail */ /** 任务 - detail */
const handlerDrawerOpen = async () => { const handleDrawerOpen = async () => {
isShowImageDetail.value = true isShowImageDetail.value = true
} }
@ -117,12 +118,12 @@ const refreshWatchImages = async () => {
} }
/** 图片 - btn click */ /** 图片 - btn click */
const handlerImageBtnClick = async (type: string, imageDetail: ImageVO) => { const handleImageBtnClick = async (type: string, imageDetail: ImageVO) => {
// image detail id // image detail id
showImageDetailId.value = imageDetail.id showImageDetailId.value = imageDetail.id
// btn // btn
if (type === 'more') { if (type === 'more') {
await handlerDrawerOpen() await handleDrawerOpen()
} else if (type === 'delete') { } else if (type === 'delete') {
await message.confirm(`是否删除照片?`) await message.confirm(`是否删除照片?`)
await ImageApi.deleteImageMy(imageDetail.id) await ImageApi.deleteImageMy(imageDetail.id)
@ -130,11 +131,15 @@ const handlerImageBtnClick = async (type: string, imageDetail: ImageVO) => {
message.success('删除成功!') message.success('删除成功!')
} else if (type === 'download') { } else if (type === 'download') {
await downloadImage(imageDetail.picUrl) await downloadImage(imageDetail.picUrl)
} else if (type === 'regeneration') {
// Midjourney
console.log('regeneration', imageDetail.id)
await emits('onRegeneration', imageDetail)
} }
} }
/** 图片 - mj btn click */ /** 图片 - mj btn click */
const handlerImageMjBtnClick = async (button: ImageMjButtonsVO, imageDetail: ImageVO) => { const handleImageMjBtnClick = async (button: ImageMjButtonsVO, imageDetail: ImageVO) => {
// 1 params // 1 params
const data = { const data = {
id: imageDetail.id, id: imageDetail.id,
@ -146,28 +151,8 @@ const handlerImageMjBtnClick = async (button: ImageMjButtonsVO, imageDetail: Ima
await getImageList() await getImageList()
} }
/** 下载 - image */
// TODO @fan download
const downloadImage = async (imageUrl) => {
const image = new Image()
image.setAttribute('crossOrigin', 'anonymous')
image.src = imageUrl
image.onload = () => {
const canvas = document.createElement('canvas')
canvas.width = image.width
canvas.height = image.height
const ctx = canvas.getContext('2d') as CanvasDrawImage
ctx.drawImage(image, 0, 0, image.width, image.height)
const url = canvas.toDataURL('image/png')
const a = document.createElement('a')
a.href = url
a.download = 'image.png'
a.click()
}
}
// page change // page change
const handlerPageChange = async (page) => { const handlePageChange = async (page) => {
pageNo.value = page pageNo.value = page
await getImageList(false) await getImageList(false)
} }
@ -175,6 +160,9 @@ const handlerPageChange = async (page) => {
/** 暴露组件方法 */ /** 暴露组件方法 */
defineExpose({ getImageList }) defineExpose({ getImageList })
// emits
const emits = defineEmits(['onRegeneration'])
/** 组件挂在的时候 */ /** 组件挂在的时候 */
onMounted(async () => { onMounted(async () => {
// image // image

View File

@ -17,21 +17,26 @@
异常 异常
</el-button> </el-button>
</div> </div>
<!-- TODO @fan1按钮要不调整成详情下载再次生成删除2如果是再次生成就把当前的参数填写到左侧的框框里 -->
<div> <div>
<el-button <el-button
class="btn" class="btn"
text text
:icon="Download" :icon="Download"
@click="handlerBtnClick('download', imageDetail)" @click="handleBtnClick('download', imageDetail)"
/>
<el-button
class="btn"
text
:icon="RefreshRight"
@click="handleBtnClick('regeneration', imageDetail)"
/> />
<el-button <el-button
class="btn" class="btn"
text text
:icon="Delete" :icon="Delete"
@click="handlerBtnClick('delete', imageDetail)" @click="handleBtnClick('delete', imageDetail)"
/> />
<el-button class="btn" text :icon="More" @click="handlerBtnClick('more', imageDetail)" /> <el-button class="btn" text :icon="More" @click="handleBtnClick('more', imageDetail)" />
</div> </div>
</div> </div>
<div class="image-wrapper" ref="cardImageRef"> <div class="image-wrapper" ref="cardImageRef">
@ -48,7 +53,7 @@
v-for="button in imageDetail?.buttons" v-for="button in imageDetail?.buttons"
:key="button" :key="button"
style="min-width: 40px; margin-left: 0; margin-right: 10px; margin-top: 5px" style="min-width: 40px; margin-left: 0; margin-right: 10px; margin-top: 5px"
@click="handlerMjBtnClick(button)" @click="handleMjBtnClick(button)"
> >
{{ button.label }}{{ button.emoji }} {{ button.label }}{{ button.emoji }}
</el-button> </el-button>
@ -56,10 +61,10 @@
</el-card> </el-card>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { Delete, Download, More } from '@element-plus/icons-vue' import {Delete, Download, More, RefreshRight} from '@element-plus/icons-vue'
import { ImageVO, ImageMjButtonsVO } from '@/api/ai/image' import { ImageVO, ImageMjButtonsVO } from '@/api/ai/image'
import { PropType } from 'vue' import { PropType } from 'vue'
import { ElLoading } from 'element-plus' import {ElLoading, LoadingOptionsResolved} from 'element-plus'
import { AiImageStatusEnum } from '@/views/ai/utils/constants' import { AiImageStatusEnum } from '@/views/ai/utils/constants'
const cardImageRef = ref<any>() // image ref const cardImageRef = ref<any>() // image ref
@ -73,17 +78,17 @@ const props = defineProps({
}) })
/** 按钮 - 点击事件 */ /** 按钮 - 点击事件 */
const handlerBtnClick = async (type, imageDetail: ImageVO) => { const handleBtnClick = async (type, imageDetail: ImageVO) => {
emits('onBtnClick', type, imageDetail) emits('onBtnClick', type, imageDetail)
} }
const handlerLoading = async (status: number) => { const handleLoading = async (status: number) => {
// TODO @fan Loading // TODO @ Loading
if (status === AiImageStatusEnum.IN_PROGRESS) { if (status === AiImageStatusEnum.IN_PROGRESS) {
cardImageLoadingInstance.value = ElLoading.service({ cardImageLoadingInstance.value = ElLoading.service({
target: cardImageRef.value, target: cardImageRef.value,
text: '生成中...' text: '生成中...'
}) } as LoadingOptionsResolved)
} else { } else {
if (cardImageLoadingInstance.value) { if (cardImageLoadingInstance.value) {
cardImageLoadingInstance.value.close() cardImageLoadingInstance.value.close()
@ -93,7 +98,7 @@ const handlerLoading = async (status: number) => {
} }
/** mj 按钮 click */ /** mj 按钮 click */
const handlerMjBtnClick = async (button: ImageMjButtonsVO) => { const handleMjBtnClick = async (button: ImageMjButtonsVO) => {
// //
await message.confirm(`确认操作 "${button.label} ${button.emoji}" ?`) await message.confirm(`确认操作 "${button.label} ${button.emoji}" ?`)
emits('onMjBtnClick', button, props.imageDetail) emits('onMjBtnClick', button, props.imageDetail)
@ -102,7 +107,7 @@ const handlerMjBtnClick = async (button: ImageMjButtonsVO) => {
// watch // watch
const { imageDetail } = toRefs(props) const { imageDetail } = toRefs(props)
watch(imageDetail, async (newVal, oldVal) => { watch(imageDetail, async (newVal, oldVal) => {
await handlerLoading(newVal.status as string) await handleLoading(newVal.status as string)
}) })
// emits // emits
@ -110,7 +115,7 @@ const emits = defineEmits(['onBtnClick', 'onMjBtnClick'])
// //
onMounted(async () => { onMounted(async () => {
await handlerLoading(props.imageDetail.status as string) await handleLoading(props.imageDetail.status as string)
}) })
</script> </script>

View File

@ -25,7 +25,7 @@
:type="(selectHotWord === hotWord ? 'primary' : 'default')" :type="(selectHotWord === hotWord ? 'primary' : 'default')"
v-for="hotWord in hotWords" v-for="hotWord in hotWords"
:key="hotWord" :key="hotWord"
@click="handlerHotWordClick(hotWord)" @click="handleHotWordClick(hotWord)"
> >
{{ hotWord }} {{ hotWord }}
</el-button> </el-button>
@ -37,7 +37,7 @@
</div> </div>
<el-space wrap class="model-list"> <el-space wrap class="model-list">
<div <div
:class="selectModel === model ? 'modal-item selectModel' : 'modal-item'" :class="selectModel === model.key ? 'modal-item selectModel' : 'modal-item'"
v-for="model in models" v-for="model in models"
:key="model.key" :key="model.key"
@ -45,7 +45,7 @@
<el-image <el-image
:src="model.image" :src="model.image"
fit="contain" fit="contain"
@click="handlerModelClick(model)" @click="handleModelClick(model)"
/> />
<div class="model-font">{{model.name}}</div> <div class="model-font">{{model.name}}</div>
</div> </div>
@ -57,14 +57,14 @@
</div> </div>
<el-space wrap class="image-style-list"> <el-space wrap class="image-style-list">
<div <div
:class="selectImageStyle === imageStyle ? 'image-style-item selectImageStyle' : 'image-style-item'" :class="selectImageStyle === imageStyle.key ? 'image-style-item selectImageStyle' : 'image-style-item'"
v-for="imageStyle in imageStyleList" v-for="imageStyle in imageStyleList"
:key="imageStyle.key" :key="imageStyle.key"
> >
<el-image <el-image
:src="imageStyle.image" :src="imageStyle.image"
fit="contain" fit="contain"
@click="handlerStyleClick(imageStyle)" @click="handleStyleClick(imageStyle)"
/> />
<div class="style-font">{{imageStyle.name}}</div> <div class="style-font">{{imageStyle.name}}</div>
</div> </div>
@ -78,8 +78,8 @@
<div class="size-item" <div class="size-item"
v-for="imageSize in imageSizeList" v-for="imageSize in imageSizeList"
:key="imageSize.key" :key="imageSize.key"
@click="handlerSizeClick(imageSize)"> @click="handleSizeClick(imageSize)">
<div :class="selectImageSize === imageSize ? 'size-wrapper selectImageSize' : 'size-wrapper'"> <div :class="selectImageSize === imageSize.key ? 'size-wrapper selectImageSize' : 'size-wrapper'">
<div :style="imageSize.style"></div> <div :style="imageSize.style"></div>
</div> </div>
<div class="size-font">{{ imageSize.name }}</div> <div class="size-font">{{ imageSize.name }}</div>
@ -91,13 +91,13 @@
size="large" size="large"
round round
:loading="drawIn" :loading="drawIn"
@click="handlerGenerateImage"> @click="handleGenerateImage">
{{drawIn ? '生成中' : '生成内容'}} {{drawIn ? '生成中' : '生成内容'}}
</el-button> </el-button>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import {ImageApi, ImageDrawReqVO} from '@/api/ai/image'; import {ImageApi, ImageDrawReqVO, ImageVO} from '@/api/ai/image';
// image // image
interface ImageModelVO { interface ImageModelVO {
@ -120,42 +120,38 @@ const prompt = ref<string>('') // 提示词
const drawIn = ref<boolean>(false) // const drawIn = ref<boolean>(false) //
const selectHotWord = ref<string>('') // const selectHotWord = ref<string>('') //
const hotWords = ref<string[]>(['中国旗袍', '古装美女', '卡通头像', '机甲战士', '童话小屋', '中国长城']) // const hotWords = ref<string[]>(['中国旗袍', '古装美女', '卡通头像', '机甲战士', '童话小屋', '中国长城']) //
const selectModel = ref<any>({}) // const selectModel = ref<string>('dall-e-3') //
// message // message
const message = useMessage() const message = useMessage()
// TODO @fanimage
// TODO @fan image
const models = ref<ImageModelVO[]>([ const models = ref<ImageModelVO[]>([
{ {
key: 'dall-e-3', key: 'dall-e-3',
name: 'DALL·E 3', name: 'DALL·E 3',
image: 'https://h5.cxyhub.com/images/model_2.png', image: `/src/assets/ai/dall2.jpg`,
}, },
{ {
key: 'dall-e-2', key: 'dall-e-2',
name: 'DALL·E 2', name: 'DALL·E 2',
image: 'https://h5.cxyhub.com/images/model_1.png', image: `/src/assets/ai/dall3.jpg`,
}, },
]) // ]) //
selectModel.value = models.value[0]
const selectImageStyle = ref<any>({}) // style const selectImageStyle = ref<string>('vivid') // style
// TODO @fanimage
const imageStyleList = ref<ImageModelVO[]>([ const imageStyleList = ref<ImageModelVO[]>([
{ {
key: 'vivid', key: 'vivid',
name: '清晰', name: '清晰',
image: 'https://h5.cxyhub.com/images/model_1.png', image: `/src/assets/ai/qingxi.jpg`,
}, },
{ {
key: 'natural', key: 'natural',
name: '自然', name: '自然',
image: 'https://h5.cxyhub.com/images/model_2.png', image: `/src/assets/ai/ziran.jpg`,
}, },
]) // style ]) // style
selectImageStyle.value = imageStyleList.value[0]
const selectImageSize = ref<ImageSizeVO>({} as ImageSizeVO) // size const selectImageSize = ref<string>('1024x1024') // size
const imageSizeList = ref<ImageSizeVO[]>([ const imageSizeList = ref<ImageSizeVO[]>([
{ {
key: '1024x1024', key: '1024x1024',
@ -179,17 +175,14 @@ const imageSizeList = ref<ImageSizeVO[]>([
style: 'width: 50px; height: 30px;background-color: #dcdcdc;', style: 'width: 50px; height: 30px;background-color: #dcdcdc;',
} }
]) // size ]) // size
selectImageSize.value = imageSizeList.value[0]
// Props // Props
const props = defineProps({}) const props = defineProps({})
// emits // emits
const emits = defineEmits(['onDrawStart', 'onDrawComplete']) const emits = defineEmits(['onDrawStart', 'onDrawComplete'])
// TODO @fan /** */~
// TODO @fanhandler handle
/** 热词 - click */ /** 热词 - click */
const handlerHotWordClick = async (hotWord: string) => { const handleHotWordClick = async (hotWord: string) => {
// //
if (selectHotWord.value == hotWord) { if (selectHotWord.value == hotWord) {
selectHotWord.value = '' selectHotWord.value = ''
@ -202,64 +195,66 @@ const handlerHotWordClick = async (hotWord: string) => {
} }
/** 模型 - click */ /** 模型 - click */
const handlerModelClick = async (model: ImageModelVO) => { const handleModelClick = async (model: ImageModelVO) => {
if (selectModel.value === model) { selectModel.value = model.key
selectModel.value = {} as ImageModelVO
return
}
selectModel.value = model
} }
/** 样式 - click */ /** 样式 - click */
const handlerStyleClick = async (imageStyle: ImageModelVO) => { const handleStyleClick = async (imageStyle: ImageModelVO) => {
if (selectImageStyle.value === imageStyle) { selectImageStyle.value = imageStyle.key
selectImageStyle.value = {} as ImageModelVO
return
}
selectImageStyle.value = imageStyle
} }
/** size - click */ /** size - click */
const handlerSizeClick = async (imageSize: ImageSizeVO) => { const handleSizeClick = async (imageSize: ImageSizeVO) => {
if (selectImageSize.value === imageSize) { selectImageSize.value = imageSize.key
selectImageSize.value = {} as ImageSizeVO
return
}
selectImageSize.value = imageSize
} }
/** 图片生产 */ /** 图片生产 */
const handlerGenerateImage = async () => { const handleGenerateImage = async () => {
// //
await message.confirm(`确认生成内容?`) await message.confirm(`确认生成内容?`)
try { try {
// //
drawIn.value = true drawIn.value = true
// //
emits('onDrawStart', selectModel.value.key) emits('onDrawStart', selectModel.value)
const imageSize = imageSizeList.value.find(item => item.key === selectImageSize.value) as ImageSizeVO
const form = { const form = {
platform: 'OpenAI', platform: 'OpenAI',
prompt: prompt.value, // prompt: prompt.value, //
model: selectModel.value.key, // model: selectModel.value, //
width: selectImageSize.value.width, // size width: imageSize.width, // size
height: selectImageSize.value.height, // size height: imageSize.height, // size
options: { options: {
style: selectImageStyle.value.key, // style: selectImageStyle.value, //
} }
} as ImageDrawReqVO } as ImageDrawReqVO
// //
await ImageApi.drawImage(form) await ImageApi.drawImage(form)
} finally { } finally {
// //
emits('onDrawComplete', selectModel.value.key) emits('onDrawComplete', selectModel.value)
// //
drawIn.value = false drawIn.value = false
} }
} }
/** 填充值 */
const settingValues = async (imageDetail: ImageVO) => {
prompt.value = imageDetail.prompt
selectModel.value = imageDetail.model
//
selectImageStyle.value = imageDetail.options?.style
//
const imageSize = imageSizeList.value.find(item => item.key === `${imageDetail.width}x${imageDetail.height}`) as ImageSizeVO
await handleSizeClick(imageSize)
}
/** 暴露组件方法 */
defineExpose({ settingValues })
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
// //
.prompt { .prompt {
} }

View File

@ -8,18 +8,23 @@
<div class="modal-switch-container"> <div class="modal-switch-container">
<Dall3 <Dall3
v-if="selectPlatform === AiPlatformEnum.OPENAI" v-if="selectPlatform === AiPlatformEnum.OPENAI"
@on-draw-start="handlerDrawStart" ref="dall3Ref"
@on-draw-complete="handlerDrawComplete" @on-draw-start="handleDrawStart"
@on-draw-complete="handleDrawComplete"
/>
<Midjourney
v-if="selectPlatform === AiPlatformEnum.MIDJOURNEY"
ref="midjourneyRef"
/> />
<Midjourney v-if="selectPlatform === AiPlatformEnum.MIDJOURNEY" />
<StableDiffusion <StableDiffusion
v-if="selectPlatform === AiPlatformEnum.STABLE_DIFFUSION" v-if="selectPlatform === AiPlatformEnum.STABLE_DIFFUSION"
@on-draw-complete="handlerDrawComplete" ref="stableDiffusionRef"
@on-draw-complete="handleDrawComplete"
/> />
</div> </div>
</div> </div>
<div class="main"> <div class="main">
<ImageTask ref="imageTaskRef" /> <ImageTask ref="imageTaskRef" @on-regeneration="handleRegeneration" />
</div> </div>
</div> </div>
</template> </template>
@ -31,8 +36,13 @@ import Midjourney from './midjourney/index.vue'
import StableDiffusion from './stable-diffusion/index.vue' import StableDiffusion from './stable-diffusion/index.vue'
import ImageTask from './ImageTask.vue' import ImageTask from './ImageTask.vue'
import { AiPlatformEnum } from '@/views/ai/utils/constants' import { AiPlatformEnum } from '@/views/ai/utils/constants'
import {ImageVO} from "@/api/ai/image";
const imageTaskRef = ref<any>() // image task ref const imageTaskRef = ref<any>() // image task ref
const dall3Ref = ref<any>() // openai ref
const midjourneyRef = ref<any>() // midjourney ref
const stableDiffusionRef = ref<any>() // stable diffusion ref
// //
const selectPlatform = ref('StableDiffusion') const selectPlatform = ref('StableDiffusion')
@ -50,20 +60,37 @@ const platformOptions = [
value: AiPlatformEnum.STABLE_DIFFUSION value: AiPlatformEnum.STABLE_DIFFUSION
} }
] ]
const drawIn = ref<boolean>(false) //
/** 绘画 - start */ /** 绘画 - start */
const handlerDrawStart = async (type) => { const handleDrawStart = async (type) => {
// todo @fan
drawIn.value = true
} }
/** 绘画 - complete */ /** 绘画 - complete */
const handlerDrawComplete = async (type) => { const handleDrawComplete = async (type) => {
drawIn.value = false
// todo
await imageTaskRef.value.getImageList() await imageTaskRef.value.getImageList()
} }
/** 绘画 - 重新生成 */
const handleRegeneration = async (imageDetail: ImageVO) => {
//
selectPlatform.value = imageDetail.platform
console.log('切换平台', imageDetail.platform)
// imageDetail
if (imageDetail.platform === AiPlatformEnum.MIDJOURNEY) {
await nextTick(async () => {
midjourneyRef.value.settingValues(imageDetail)
})
} else if (imageDetail.platform === AiPlatformEnum.OPENAI) {
await nextTick(async () => {
dall3Ref.value.settingValues(imageDetail)
})
} else if (imageDetail.platform === AiPlatformEnum.STABLE_DIFFUSION) {
await nextTick(async () => {
stableDiffusionRef.value.settingValues(imageDetail)
})
}
}
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@ -24,7 +24,7 @@
:type="(selectHotWord === hotWord ? 'primary' : 'default')" :type="(selectHotWord === hotWord ? 'primary' : 'default')"
v-for="hotWord in hotWords" v-for="hotWord in hotWords"
:key="hotWord" :key="hotWord"
@click="handlerHotWordClick(hotWord)" @click="handleHotWordClick(hotWord)"
> >
{{ hotWord }} {{ hotWord }}
</el-button> </el-button>
@ -38,8 +38,8 @@
<div class="size-item" <div class="size-item"
v-for="imageSize in imageSizeList" v-for="imageSize in imageSizeList"
:key="imageSize.key" :key="imageSize.key"
@click="handlerSizeClick(imageSize)"> @click="handleSizeClick(imageSize)">
<div :class="selectImageSize === imageSize ? 'size-wrapper selectImageSize' : 'size-wrapper'"> <div :class="selectImageSize === imageSize.key ? 'size-wrapper selectImageSize' : 'size-wrapper'">
<div :style="imageSize.style"></div> <div :style="imageSize.style"></div>
</div> </div>
<div class="size-font">{{ imageSize.key }}</div> <div class="size-font">{{ imageSize.key }}</div>
@ -57,7 +57,7 @@
clearable clearable
placeholder="请选择版本" placeholder="请选择版本"
style="width: 350px" style="width: 350px"
@change="handlerChangeVersion" @change="handleChangeVersion"
> >
<el-option <el-option
v-for="item in versionList" v-for="item in versionList"
@ -74,7 +74,7 @@
</div> </div>
<el-space wrap class="model-list"> <el-space wrap class="model-list">
<div <div
:class="selectModel === model ? 'modal-item selectModel' : 'modal-item'" :class="selectModel === model.key ? 'modal-item selectModel' : 'modal-item'"
v-for="model in models" v-for="model in models"
:key="model.key" :key="model.key"
@ -82,21 +82,29 @@
<el-image <el-image
:src="model.image" :src="model.image"
fit="contain" fit="contain"
@click="handlerModelClick(model)" @click="handleModelClick(model)"
/> />
<div class="model-font">{{model.name}}</div> <div class="model-font">{{model.name}}</div>
</div> </div>
</el-space> </el-space>
</div> </div>
<div class="model">
<div>
<el-text tag="b">参考图</el-text>
</div>
<el-space wrap class="model-list">
<UploadImg v-model="referImage" height="80px" width="80px" />
</el-space>
</div>
<div class="btns"> <div class="btns">
<!-- <el-button size="large" round>重置内容</el-button>--> <!-- <el-button size="large" round>重置内容</el-button>-->
<el-button type="primary" size="large" round @click="handlerGenerateImage"></el-button> <el-button type="primary" size="large" round @click="handleGenerateImage"></el-button>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
// image // image
import {ImageApi, ImageMidjourneyImagineReqVO} from "@/api/ai/image"; import {ImageApi, ImageMidjourneyImagineReqVO, ImageVO} from "@/api/ai/image";
// message // message
const message = useMessage() const message = useMessage()
// emits // emits
@ -118,9 +126,10 @@ interface ImageSizeVO {
// //
const prompt = ref<string>('') // const prompt = ref<string>('') //
const referImage = ref<any>() //
const selectHotWord = ref<string>('') // const selectHotWord = ref<string>('') //
const hotWords = ref<string[]>(['中国旗袍', '古装美女', '卡通头像', '机甲战士', '童话小屋', '中国长城']) // const hotWords = ref<string[]>(['中国旗袍', '古装美女', '卡通头像', '机甲战士', '童话小屋', '中国长城']) //
const selectModel = ref<any>() // const selectModel = ref<string>('midjourney') //
const models = ref<ImageModelVO[]>([ const models = ref<ImageModelVO[]>([
{ {
key: 'midjourney', key: 'midjourney',
@ -133,9 +142,8 @@ const models = ref<ImageModelVO[]>([
image: 'https://bigpt8.com/pc/_nuxt/nj.ca79b143.png', image: 'https://bigpt8.com/pc/_nuxt/nj.ca79b143.png',
}, },
]) // ]) //
selectModel.value = models.value[0] //
const selectImageSize = ref<ImageSizeVO>({} as ImageSizeVO) // size const selectImageSize = ref<string>('1:1') // size
const imageSizeList = ref<ImageSizeVO[]>([ const imageSizeList = ref<ImageSizeVO[]>([
{ {
key: '1:1', key: '1:1',
@ -168,10 +176,8 @@ const imageSizeList = ref<ImageSizeVO[]>([
style: 'width: 50px; height: 30px;background-color: #dcdcdc;', style: 'width: 50px; height: 30px;background-color: #dcdcdc;',
}, },
]) // size ]) // size
selectImageSize.value = imageSizeList.value[0]
// version // version
let versionList = ref<any>([]) // version
const midjourneyVersionList = ref<any>([ const midjourneyVersionList = ref<any>([
{ {
value: '6.0', value: '6.0',
@ -201,10 +207,11 @@ const nijiVersionList = ref<any>([
}, },
]) ])
const selectVersion = ref<any>('6.0') // version const selectVersion = ref<any>('6.0') // version
let versionList = ref<any>([]) // version
versionList.value = midjourneyVersionList.value // midjourney versionList.value = midjourneyVersionList.value // midjourney
/** 热词 - click */ /** 热词 - click */
const handlerHotWordClick = async (hotWord: string) => { const handleHotWordClick = async (hotWord: string) => {
// //
if (selectHotWord.value == hotWord) { if (selectHotWord.value == hotWord) {
selectHotWord.value = '' selectHotWord.value = ''
@ -217,17 +224,13 @@ const handlerHotWordClick = async (hotWord: string) => {
} }
/** size - click */ /** size - click */
const handlerSizeClick = async (imageSize: ImageSizeVO) => { const handleSizeClick = async (imageSize: ImageSizeVO) => {
if (selectImageSize.value === imageSize) { selectImageSize.value = imageSize.key
selectImageSize.value = {} as ImageSizeVO
return
}
selectImageSize.value = imageSize
} }
/** 模型 - click */ /** 模型 - click */
const handlerModelClick = async (model: ImageModelVO) => { const handleModelClick = async (model: ImageModelVO) => {
selectModel.value = model selectModel.value = model.key
if (model.key === 'niji') { if (model.key === 'niji') {
versionList.value = nijiVersionList.value // niji versionList.value = nijiVersionList.value // niji
} else { } else {
@ -237,33 +240,53 @@ const handlerModelClick = async (model: ImageModelVO) => {
} }
/** version - click */ /** version - click */
const handlerChangeVersion = async (version) => { const handleChangeVersion = async (version) => {
console.log('version', version) console.log('version', version)
} }
/** 图片生产 */ /** 图片生产 */
const handlerGenerateImage = async () => { const handleGenerateImage = async () => {
// //
await message.confirm(`确认生成内容?`) await message.confirm(`确认生成内容?`)
// todo @ // todo @
try { try {
// //
emits('onDrawStart', selectModel.value.key) emits('onDrawStart', selectModel.value)
// //
const imageSize = imageSizeList.value.find(item => selectImageSize.value === item.key) as ImageSizeVO
const req = { const req = {
prompt: prompt.value, prompt: prompt.value,
model: selectModel.value.key, model: selectModel.value,
width: selectImageSize.value.width, width: imageSize.width,
height: selectImageSize.value.height, height: imageSize.height,
version: selectVersion.value, version: selectVersion.value,
base64Array: [], referImageUrl: referImage.value,
} as ImageMidjourneyImagineReqVO } as ImageMidjourneyImagineReqVO
await ImageApi.midjourneyImagine(req) await ImageApi.midjourneyImagine(req)
} finally { } finally {
// //
emits('onDrawComplete', selectModel.value.key) emits('onDrawComplete', selectModel.value)
} }
} }
/** 填充值 */
const settingValues = async (imageDetail: ImageVO) => {
//
prompt.value = imageDetail.prompt
// image size
const imageSize = imageSizeList.value.find(item => item.key === `${imageDetail.width}:${imageDetail.height}`) as ImageSizeVO
selectImageSize.value = imageSize.key
//
const model = models.value.find(item => item.key === imageDetail.options?.model) as ImageModelVO
await handleModelClick(model)
//
selectVersion.value = versionList.value.find(item => item.value === imageDetail.options?.version).value
// image
referImage.value = imageDetail.options.referImageUrl
}
/** 暴露组件方法 */
defineExpose({ settingValues })
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@ -120,7 +120,8 @@
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import {ImageApi, ImageDrawReqVO} from '@/api/ai/image' import {ImageApi, ImageDrawReqVO, ImageVO} from '@/api/ai/image'
import {hasChinese} from '../../utils/common-utils'
// image // image
interface ImageModelVO { interface ImageModelVO {
@ -146,8 +147,8 @@ const hotWords = ref<string[]>([
// message // message
const message = useMessage() const message = useMessage()
// TODO @fan Euler aDPM++ 2S aDPM++ 2MDPM++ SDEDPM++ 2M SDEUniPCRestart image stableDiffusionSampler //
const selectSampler = ref<any>({}) // const selectSampler = ref<string>('DDIM') //
// DDIM DDPM K_DPMPP_2M K_DPMPP_2S_ANCESTRAL K_DPM_2 K_DPM_2_ANCESTRAL K_EULER K_EULER_ANCESTRAL K_HEUN K_LMS // DDIM DDPM K_DPMPP_2M K_DPMPP_2S_ANCESTRAL K_DPM_2 K_DPM_2_ANCESTRAL K_EULER K_EULER_ANCESTRAL K_HEUN K_LMS
const sampler = ref<ImageModelVO[]>([ const sampler = ref<ImageModelVO[]>([
{ {
@ -191,12 +192,11 @@ const sampler = ref<ImageModelVO[]>([
name: 'K_LMS' name: 'K_LMS'
}, },
]) ])
selectSampler.value = sampler.value[0]
// //
// 3d-model analog-film anime cinematic comic-book digital-art enhance fantasy-art isometric // 3d-model analog-film anime cinematic comic-book digital-art enhance fantasy-art isometric
// line-art low-poly modeling-compound neon-punk origami photographic pixel-art tile-texture // line-art low-poly modeling-compound neon-punk origami photographic pixel-art tile-texture
const selectStylePreset = ref<any>({}) // const selectStylePreset = ref<string>('3d-model') //
const stylePresets = ref<ImageModelVO[]>([ const stylePresets = ref<ImageModelVO[]>([
{ {
key: '3d-model', key: '3d-model',
@ -268,13 +268,11 @@ const stylePresets = ref<ImageModelVO[]>([
name: 'tile-texture' name: 'tile-texture'
}, },
]) ])
selectStylePreset.value = stylePresets.value[0]
// (clip_guidance_preset) CLIP // (clip_guidance_preset) CLIP
// https://platform.stability.ai/docs/api-reference#tag/SDXL-and-SD1.6/operation/textToImage // https://platform.stability.ai/docs/api-reference#tag/SDXL-and-SD1.6/operation/textToImage
// FAST_BLUE FAST_GREEN NONE SIMPLE SLOW SLOWER SLOWEST // FAST_BLUE FAST_GREEN NONE SIMPLE SLOW SLOWER SLOWEST
const selectClipGuidancePreset = ref<any>({}) // const selectClipGuidancePreset = ref<string>('NONE') //
const clipGuidancePresets = ref<ImageModelVO[]>([ const clipGuidancePresets = ref<ImageModelVO[]>([
{ {
key: 'NONE', key: 'NONE',
@ -305,7 +303,6 @@ const clipGuidancePresets = ref<ImageModelVO[]>([
name: 'SLOWEST' name: 'SLOWEST'
}, },
]) ])
selectClipGuidancePreset.value = clipGuidancePresets.value[0]
const steps = ref<number>(20) // const steps = ref<number>(20) //
const seed = ref<number>(42) // const seed = ref<number>(42) //
@ -333,6 +330,10 @@ const handleHotWordClick = async (hotWord: string) => {
const handleGenerateImage = async () => { const handleGenerateImage = async () => {
// //
await message.confirm(`确认生成内容?`) await message.confirm(`确认生成内容?`)
if (await hasChinese(prompt.value)) {
message.alert('暂不支持中文!')
return
}
try { try {
// //
drawIn.value = true drawIn.value = true
@ -349,9 +350,9 @@ const handleGenerateImage = async () => {
seed: seed.value, // seed: seed.value, //
steps: steps.value, // steps: steps.value, //
scale: scale.value, // scale: scale.value, //
sampler: selectSampler.value.key, // sampler: selectSampler.value, //
clipGuidancePreset: selectClipGuidancePreset.value.key, // CLIP clipGuidancePreset: selectClipGuidancePreset.value, // CLIP
stylePreset: selectStylePreset.value.key, // stylePreset: selectStylePreset.value, //
} }
} as ImageDrawReqVO } as ImageDrawReqVO
await ImageApi.drawImage(form) await ImageApi.drawImage(form)
@ -362,6 +363,22 @@ const handleGenerateImage = async () => {
drawIn.value = false drawIn.value = false
} }
} }
/** 填充值 */
const settingValues = async (imageDetail: ImageVO) => {
prompt.value = imageDetail.prompt
imageWidth.value = imageDetail.width
imageHeight.value = imageDetail.height
seed.value = imageDetail.options?.seed
steps.value = imageDetail.options?.steps
scale.value = imageDetail.options?.scale
selectSampler.value = imageDetail.options?.sampler
selectClipGuidancePreset.value = imageDetail.options?.clipGuidancePreset
selectStylePreset.value = imageDetail.options?.stylePreset
}
/** 暴露组件方法 */
defineExpose({ settingValues })
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
// //

View File

@ -0,0 +1,13 @@
/**
* Created by
*
* AI
*
* src/utils/common-utils.ts
* AI /views/ai/utils/common-utils.ts
*/
/** 判断字符串是否包含中文 */
export const hasChinese = async (str) => {
return /[\u4e00-\u9fa5]/.test(str)
}

1
types/env.d.ts vendored
View File

@ -19,7 +19,6 @@ interface ImportMetaEnv {
readonly VITE_UPLOAD_URL: string readonly VITE_UPLOAD_URL: string
readonly VITE_API_URL: string readonly VITE_API_URL: string
readonly VITE_BASE_PATH: string readonly VITE_BASE_PATH: string
readonly VITE_STATIC_URL: string
readonly VITE_DROP_DEBUGGER: string readonly VITE_DROP_DEBUGGER: string
readonly VITE_DROP_CONSOLE: string readonly VITE_DROP_CONSOLE: string
readonly VITE_SOURCEMAP: string readonly VITE_SOURCEMAP: string