【代码优化】AI:绘图 index.vue 代码梳理 30%(ImageList)

pull/474/MERGE
YunaiV 2024-07-09 00:00:53 +08:00
parent 80d87b8e99
commit 82b53b9b03
5 changed files with 110 additions and 119 deletions

View File

@ -14,16 +14,11 @@ export interface ImageVO {
errorMessage: string // 错误信息
options: object // 配置 Map<string, string>
taskId: number // 任务编号
buttons: ImageMjButtonsVO[] // mj 操作按钮
buttons: ImageMidjourneyButtonsVO[] // mj 操作按钮
createTime: string // 创建时间
finishTime: string // 完成时间
}
export interface ImagePageReqVO {
pageNo: number // 分页编号
pageSize: number // 分页大小
}
export interface ImageDrawReqVO {
platform: string // 平台
prompt: string // 提示词
@ -43,22 +38,22 @@ export interface ImageMidjourneyImagineReqVO {
version: string // 版本
}
export interface ImageMjActionVO {
export interface ImageMidjourneyActionVO {
id: number // 图片编号
customId: string // MJ::JOB::upsample::1::85a4b4c1-8835-46c5-a15c-aea34fad1862 动作标识
}
export interface ImageMjButtonsVO {
export interface ImageMidjourneyButtonsVO {
customId: string // MJ::JOB::upsample::1::85a4b4c1-8835-46c5-a15c-aea34fad1862 动作标识
emoji: string // 图标 emoji
label: string // Make Variations 文本
style: number // 样式: 2Primary、3Green
}
// AI API 密钥 API
// AI 图片 API
export const ImageApi = {
// 获取【我的】绘图分页
getImagePageMy: async (params: ImagePageReqVO) => {
getImagePageMy: async (params: PageParam) => {
return await request.get({ url: `/ai/image/my-page`, params })
},
// 获取【我的】绘图记录
@ -85,7 +80,7 @@ export const ImageApi = {
return await request.post({ url: `/ai/image/midjourney/imagine`, data })
},
// 【Midjourney】Action 操作(二次生成图片)
midjourneyAction: async (data: ImageMjActionVO) => {
midjourneyAction: async (data: ImageMidjourneyActionVO) => {
return await request.post({ url: `/ai/image/midjourney/action`, data })
},

View File

@ -32,26 +32,25 @@ const download = {
// 下载 Markdown 方法
markdown: (data: Blob, fileName: string) => {
download0(data, fileName, 'text/markdown')
},
// 下载图片(允许跨域)
image: (url: string) => {
const image = new Image()
image.setAttribute('crossOrigin', 'anonymous')
image.src = url
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()
}
}
}
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

@ -30,12 +30,7 @@
:icon="RefreshRight"
@click="handleBtnClick('regeneration', imageDetail)"
/>
<el-button
class="btn"
text
:icon="Delete"
@click="handleBtnClick('delete', imageDetail)"
/>
<el-button class="btn" text :icon="Delete" @click="handleBtnClick('delete', imageDetail)" />
<el-button class="btn" text :icon="More" @click="handleBtnClick('more', imageDetail)" />
</div>
</div>
@ -61,10 +56,10 @@
</el-card>
</template>
<script setup lang="ts">
import {Delete, Download, More, RefreshRight} from '@element-plus/icons-vue'
import { ImageVO, ImageMjButtonsVO } from '@/api/ai/image'
import { Delete, Download, More, RefreshRight } from '@element-plus/icons-vue'
import { ImageVO, ImageMidjourneyButtonsVO } from '@/api/ai/image'
import { PropType } from 'vue'
import {ElLoading, LoadingOptionsResolved} from 'element-plus'
import { ElLoading, LoadingOptionsResolved } from 'element-plus'
import { AiImageStatusEnum } from '@/views/ai/utils/constants'
const cardImageRef = ref<any>() // image ref
@ -98,7 +93,7 @@ const handleLoading = async (status: number) => {
}
/** mj 按钮 click */
const handleMjBtnClick = async (button: ImageMjButtonsVO) => {
const handleMjBtnClick = async (button: ImageMidjourneyButtonsVO) => {
//
await message.confirm(`确认操作 "${button.label} ${button.emoji}" ?`)
emits('onMjBtnClick', button, props.imageDetail)

View File

@ -2,22 +2,21 @@
<el-card class="dr-task" body-class="task-card" shadow="never">
<template #header>绘画任务</template>
<!-- 图片列表 -->
<div class="task-image-list" ref="imageTaskRef">
<div class="task-image-list" ref="imageListRef">
<ImageCard
v-for="image in imageList"
:key="image.id"
:image-detail="image"
@on-btn-click="handleImageBtnClick"
@on-mj-btn-click="handleImageMjBtnClick"
@on-btn-click="handleImageButtonClick"
@on-mj-btn-click="handleImageMidjourneyButtonClick"
/>
</div>
<div class="task-image-pagination">
<el-pagination
background
layout="prev, pager, next"
:default-page-size="pageSize"
<Pagination
:total="pageTotal"
@change="handlePageChange"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getImageList"
/>
</div>
</el-card>
@ -26,62 +25,63 @@
<ImageDetail
:show="isShowImageDetail"
:id="showImageDetailId"
@handle-drawer-close="handleDrawerClose"
@handle-drawer-close="handleDetailClose"
/>
</template>
<script setup lang="ts">
import { ImageApi, ImageVO, ImageMjActionVO, ImageMjButtonsVO } from '@/api/ai/image'
import {
ImageApi,
ImageVO,
ImageMidjourneyActionVO,
ImageMidjourneyButtonsVO
} from '@/api/ai/image'
import ImageDetail from './ImageDetail.vue'
import ImageCard from './ImageCard.vue'
import { ElLoading, LoadingOptionsResolved } from 'element-plus'
import { AiImageStatusEnum } from '@/views/ai/utils/constants'
import { downloadImage } from '@/utils/download'
import download from '@/utils/download'
const message = useMessage() //
const imageList = ref<ImageVO[]>([]) // image
const inProgressImageMap = ref<{}>({}) // image key image value image
const imageListInterval = ref<any>() // image
const isShowImageDetail = ref<boolean>(false) // task
const showImageDetailId = ref<number>(0) // task
const imageTaskRef = ref<any>() // ref
const imageTaskLoadingInstance = ref<any>() // loading
const imageTaskLoading = ref<boolean>(false) // loading
const pageNo = ref<number>(1) // page no
const pageSize = ref<number>(10) // page size
//
const queryParams = reactive({
pageNo: 1,
pageSize: 10
})
const pageTotal = ref<number>(0) // page size
const imageList = ref<ImageVO[]>([]) // image
const imageListLoadingInstance = ref<any>() // image
const imageListRef = ref<any>() // ref
//
const inProgressImageMap = ref<{}>({}) // image key image value image
const inProgressTimer = ref<any>() // image
//
const isShowImageDetail = ref<boolean>(false) //
const showImageDetailId = ref<number>(0) //
/** 抽屉 - close */
const handleDrawerClose = async () => {
isShowImageDetail.value = false
}
/** 任务 - detail */
const handleDrawerOpen = async () => {
/** 查看图片的详情 */
const handleDetailOpen = async () => {
isShowImageDetail.value = true
}
/**
* 获取 - image 列表
*/
const getImageList = async (apply: boolean = false) => {
imageTaskLoading.value = true
/** 关闭图片的详情 */
const handleDetailClose = async () => {
isShowImageDetail.value = false
}
/** 获得 image 图片列表 */
const getImageList = async () => {
try {
imageTaskLoadingInstance.value = ElLoading.service({
target: imageTaskRef.value,
// 1.
imageListLoadingInstance.value = ElLoading.service({
target: imageListRef.value,
text: '加载中...'
} as LoadingOptionsResolved)
const { list, total } = await ImageApi.getImagePageMy({
pageNo: pageNo.value,
pageSize: pageSize.value
})
if (apply) {
imageList.value = [...imageList.value, ...list]
} else {
imageList.value = list
}
const { list, total } = await ImageApi.getImagePageMy(queryParams)
imageList.value = list
pageTotal.value = total
// watch
// 2.
const newWatImages = {}
imageList.value.forEach((item) => {
if (item.status === AiImageStatusEnum.IN_PROGRESS) {
@ -90,9 +90,10 @@ const getImageList = async (apply: boolean = false) => {
})
inProgressImageMap.value = newWatImages
} finally {
if (imageTaskLoadingInstance.value) {
imageTaskLoadingInstance.value.close()
imageTaskLoadingInstance.value = null
// Loading
if (imageListLoadingInstance.value) {
imageListLoadingInstance.value.close()
imageListLoadingInstance.value = null
}
}
}
@ -119,50 +120,52 @@ const refreshWatchImages = async () => {
inProgressImageMap.value = newWatchImages
}
/** 图片 - btn click */
const handleImageBtnClick = async (type: string, imageDetail: ImageVO) => {
// image detail id
showImageDetailId.value = imageDetail.id
// btn
/** 图片的点击事件 */
const handleImageButtonClick = async (type: string, imageDetail: ImageVO) => {
//
if (type === 'more') {
await handleDrawerOpen()
} else if (type === 'delete') {
showImageDetailId.value = imageDetail.id
await handleDetailOpen()
return
}
//
if (type === 'delete') {
await message.confirm(`是否删除照片?`)
await ImageApi.deleteImageMy(imageDetail.id)
await getImageList()
message.success('删除成功!')
} else if (type === 'download') {
await downloadImage(imageDetail.picUrl)
} else if (type === 'regeneration') {
// Midjourney
console.log('regeneration', imageDetail.id)
return
}
//
if (type === 'download') {
await download.image(imageDetail.picUrl)
return
}
//
if (type === 'regeneration') {
await emits('onRegeneration', imageDetail)
return
}
}
/** 图片 - mj btn click */
const handleImageMjBtnClick = async (button: ImageMjButtonsVO, imageDetail: ImageVO) => {
// 1 params
/** 处理 Midjourney 按钮点击事件 */
const handleImageMidjourneyButtonClick = async (
button: ImageMidjourneyButtonsVO,
imageDetail: ImageVO
) => {
// 1. params
const data = {
id: imageDetail.id,
customId: button.customId
} as ImageMjActionVO
// 2 action
} as ImageMidjourneyActionVO
// 2. action
await ImageApi.midjourneyAction(data)
// 3
// 3.
await getImageList()
}
// page change
const handlePageChange = async (page) => {
pageNo.value = page
await getImageList(false)
}
defineExpose({ getImageList }) //
/** 暴露组件方法 */
defineExpose({ getImageList })
// emits
const emits = defineEmits(['onRegeneration'])
/** 组件挂在的时候 */
@ -170,15 +173,15 @@ onMounted(async () => {
// image
await getImageList()
// image
imageListInterval.value = setInterval(async () => {
inProgressTimer.value = setInterval(async () => {
await refreshWatchImages()
}, 1000 * 3)
})
/** 组件取消挂在的时候 */
onUnmounted(async () => {
if (imageListInterval.value) {
clearInterval(imageListInterval.value)
if (inProgressTimer.value) {
clearInterval(inProgressTimer.value)
}
})
</script>

View File

@ -126,7 +126,6 @@
<script setup lang="ts">
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import { CategoryApi, CategoryVO } from '@/api/bpm/category'
import CategoryForm from './CategoryForm.vue'