234 lines
6.0 KiB
Vue
Raw Normal View History

2024-05-26 20:47:29 +08:00
<template>
<el-card class="dr-task" body-class="task-card" shadow="never">
<template #header>绘画任务</template>
<!-- 图片列表 -->
<div class="task-image-list" ref="imageListRef">
<ImageCard
v-for="image in imageList"
:key="image.id"
:detail="image"
@on-btn-click="handleImageButtonClick"
@on-mj-btn-click="handleImageMidjourneyButtonClick"
/>
</div>
2024-06-19 10:41:33 +08:00
<div class="task-image-pagination">
<Pagination
:total="pageTotal"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getImageList"
2024-06-19 10:41:33 +08:00
/>
</div>
2024-05-26 20:47:29 +08:00
</el-card>
<!-- 图片详情 -->
<ImageDetail
:show="isShowImageDetail"
:id="showImageDetailId"
@handle-drawer-close="handleDetailClose"
/>
2024-05-26 20:47:29 +08:00
</template>
<script setup lang="ts">
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'
2024-07-03 13:52:00 +08:00
import { AiImageStatusEnum } from '@/views/ai/utils/constants'
import download from '@/utils/download'
const message = useMessage() // 消息弹窗
// 图片分页相关的参数
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) // 图片详情的图片编号
/** 查看图片的详情 */
const handleDetailOpen = async () => {
isShowImageDetail.value = true
}
/** 关闭图片的详情 */
const handleDetailClose = async () => {
isShowImageDetail.value = false
}
/** 获得 image 图片列表 */
const getImageList = async () => {
try {
// 1. 加载图片列表
imageListLoadingInstance.value = ElLoading.service({
target: imageListRef.value,
2024-06-18 14:35:42 +08:00
text: '加载中...'
} as LoadingOptionsResolved)
const { list, total } = await ImageApi.getImagePageMy(queryParams)
imageList.value = list
2024-06-19 10:41:33 +08:00
pageTotal.value = total
// 2. 计算需要轮询的图片
const newWatImages = {}
imageList.value.forEach((item) => {
2024-07-03 13:52:00 +08:00
if (item.status === AiImageStatusEnum.IN_PROGRESS) {
newWatImages[item.id] = item
}
})
inProgressImageMap.value = newWatImages
} finally {
// 关闭正在“加载中”的 Loading
if (imageListLoadingInstance.value) {
imageListLoadingInstance.value.close()
imageListLoadingInstance.value = null
2024-06-18 14:35:42 +08:00
}
}
}
/** 轮询生成中的 image 列表 */
const refreshWatchImages = async () => {
const imageIds = Object.keys(inProgressImageMap.value).map(Number)
if (imageIds.length == 0) {
return
}
const list = (await ImageApi.getImageListMyByIds(imageIds)) as ImageVO[]
const newWatchImages = {}
list.forEach((image) => {
2024-07-03 13:52:00 +08:00
if (image.status === AiImageStatusEnum.IN_PROGRESS) {
newWatchImages[image.id] = image
} else {
const index = imageList.value.findIndex((oldImage) => image.id === oldImage.id)
if (index >= 0) {
// 更新 imageList
imageList.value[index] = image
}
}
})
inProgressImageMap.value = newWatchImages
}
/** 图片的点击事件 */
const handleImageButtonClick = async (type: string, imageDetail: ImageVO) => {
// 详情
if (type === 'more') {
showImageDetailId.value = imageDetail.id
await handleDetailOpen()
return
}
// 删除
if (type === 'delete') {
await message.confirm(`是否删除照片?`)
await ImageApi.deleteImageMy(imageDetail.id)
await getImageList()
message.success('删除成功!')
return
}
// 下载
if (type === 'download') {
await download.image(imageDetail.picUrl)
return
}
// 重新生成
if (type === 'regeneration') {
await emits('onRegeneration', imageDetail)
return
}
}
/** 处理 Midjourney 按钮点击事件 */
const handleImageMidjourneyButtonClick = async (
button: ImageMidjourneyButtonsVO,
imageDetail: ImageVO
) => {
// 1. 构建 params 参数
const data = {
2024-06-05 16:41:21 +08:00
id: imageDetail.id,
customId: button.customId
} as ImageMidjourneyActionVO
// 2. 发送 action
await ImageApi.midjourneyAction(data)
// 3. 刷新列表
2024-06-05 16:41:21 +08:00
await getImageList()
}
defineExpose({ getImageList }) // 暴露组件方法
const emits = defineEmits(['onRegeneration'])
/** 组件挂在的时候 */
onMounted(async () => {
// 获取 image 列表
await getImageList()
// 自动刷新 image 列表
inProgressTimer.value = setInterval(async () => {
await refreshWatchImages()
}, 1000 * 3)
})
/** 组件取消挂在的时候 */
onUnmounted(async () => {
if (inProgressTimer.value) {
clearInterval(inProgressTimer.value)
}
})
2024-05-26 20:47:29 +08:00
</script>
<style lang="scss">
2024-07-10 21:03:02 +08:00
.dr-task {
width: 100%;
height: 100%;
}
.task-card {
margin: 0;
padding: 0;
height: 100%;
position: relative;
}
.task-image-list {
position: relative;
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-content: flex-start;
height: 100%;
overflow: auto;
padding: 20px 20px 140px;
box-sizing: border-box; /* 确保内边距不会增加高度 */
> div {
margin-right: 20px;
margin-bottom: 20px;
}
> div:last-of-type {
//margin-bottom: 100px;
}
}
2024-06-19 10:41:33 +08:00
.task-image-pagination {
position: absolute;
bottom: 60px;
height: 50px;
line-height: 90px;
width: 100%;
z-index: 999;
background-color: #ffffff;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
}
</style>