ImageTask.vue 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. <template>
  2. <el-card class="dr-task" body-class="task-card" shadow="never">
  3. <template #header>绘画任务</template>
  4. <div class="task-image-list" ref="imageTaskRef" @scroll="handleTabsScroll">
  5. <ImageTaskCard
  6. v-for="image in imageList"
  7. :key="image"
  8. :image-detail="image"
  9. @on-btn-click="handlerImageBtnClick"
  10. @on-mj-btn-click="handlerImageMjBtnClick"/>
  11. </div>
  12. </el-card>
  13. <!-- 图片 detail 抽屉 -->
  14. <ImageDetailDrawer
  15. :show="isShowImageDetail"
  16. :id="showImageDetailId"
  17. @handler-drawer-close="handlerDrawerClose"
  18. />
  19. </template>
  20. <script setup lang="ts">
  21. import {ImageApi, ImageDetailVO, ImageMjActionVO, ImageMjButtonsVO} from '@/api/ai/image';
  22. import ImageDetailDrawer from './ImageDetailDrawer.vue'
  23. import ImageTaskCard from './ImageTaskCard.vue'
  24. const message = useMessage() // 消息弹窗
  25. const imageList = ref<ImageDetailVO[]>([]) // image 列表
  26. const imageListInterval = ref<any>() // image 列表定时器,刷新列表
  27. const isShowImageDetail = ref<boolean>(false) // 是否显示 task 详情
  28. const showImageDetailId = ref<number>(0) // 是否显示 task 详情
  29. const imageTaskRef = ref<any>() // ref
  30. const imageTaskLoading = ref<boolean>(false) // loading
  31. const pageNo = ref<number>(1) // page no
  32. const pageSize = ref<number>(20) // page size
  33. /** 抽屉 - close */
  34. const handlerDrawerClose = async () => {
  35. isShowImageDetail.value = false
  36. }
  37. /** 任务 - detail */
  38. const handlerDrawerOpen = async () => {
  39. isShowImageDetail.value = true
  40. }
  41. /**
  42. * 获取 - image 列表
  43. */
  44. const getImageList = async () => {
  45. imageTaskLoading.value = true
  46. try {
  47. const { list } = await ImageApi.getImageList({pageNo: pageNo.value, pageSize: pageSize.value})
  48. imageList.value.push.apply(imageList.value, list)
  49. } finally {
  50. imageTaskLoading.value = false
  51. }
  52. }
  53. /** 图片 - btn click */
  54. const handlerImageBtnClick = async (type, imageDetail: ImageDetailVO) => {
  55. // 获取 image detail id
  56. showImageDetailId.value = imageDetail.id
  57. // 处理不用 btn
  58. if (type === 'more') {
  59. await handlerDrawerOpen()
  60. } else if (type === 'delete') {
  61. await message.confirm(`是否删除照片?`)
  62. await ImageApi.deleteImage(imageDetail.id)
  63. await getImageList()
  64. await message.success("删除成功!")
  65. } else if (type === 'download') {
  66. await downloadImage(imageDetail.picUrl)
  67. }
  68. }
  69. /** 图片 - mj btn click */
  70. const handlerImageMjBtnClick = async (button: ImageMjButtonsVO, imageDetail: ImageDetailVO) => {
  71. // 1、构建 params 参数
  72. const params = {
  73. id: imageDetail.id,
  74. customId: button.customId,
  75. } as ImageMjActionVO
  76. // 2、发送 action
  77. await ImageApi.midjourneyAction(params)
  78. // 3、刷新列表
  79. await getImageList()
  80. }
  81. /** 下载 - image */
  82. // TODO @fan:貌似可以考虑抽到 download 里面,作为一个方法
  83. const downloadImage = async (imageUrl) => {
  84. const image = new Image()
  85. image.setAttribute('crossOrigin', 'anonymous')
  86. image.src = imageUrl
  87. image.onload = () => {
  88. const canvas = document.createElement('canvas')
  89. canvas.width = image.width
  90. canvas.height = image.height
  91. const ctx = canvas.getContext('2d') as CanvasDrawImage
  92. ctx.drawImage(image, 0, 0, image.width, image.height)
  93. const url = canvas.toDataURL('image/png')
  94. const a = document.createElement('a')
  95. a.href = url
  96. a.download = 'image.png'
  97. a.click()
  98. }
  99. }
  100. const handleTabsScroll = async () => {
  101. if (imageTaskRef.value) {
  102. const { scrollTop, scrollHeight, clientHeight } = imageTaskRef.value;
  103. console.log('scrollTop', scrollTop, clientHeight, scrollHeight)
  104. if (scrollTop + clientHeight >= scrollHeight - 20 && !imageTaskLoading.value) {
  105. console.log('分页')
  106. pageNo.value = pageNo.value + 1
  107. await getImageList();
  108. }
  109. }
  110. }
  111. /** 暴露组件方法 */
  112. defineExpose({getImageList})
  113. /** 组件挂在的时候 */
  114. onMounted(async () => {
  115. // 获取 image 列表
  116. await getImageList()
  117. // 自动刷新 image 列表
  118. imageListInterval.value = setInterval(async () => {
  119. await getImageList()
  120. }, 1000 * 20)
  121. })
  122. /** 组件取消挂在的时候 */
  123. onUnmounted(async () => {
  124. if (imageListInterval.value) {
  125. clearInterval(imageListInterval.value)
  126. }
  127. })
  128. </script>
  129. <style lang="scss">
  130. .task-card {
  131. margin: 0;
  132. padding: 0;
  133. height: 100%;
  134. }
  135. .task-image-list {
  136. display: flex;
  137. flex-direction: row;
  138. flex-wrap: wrap;
  139. align-content: flex-start;
  140. height: 100%;
  141. overflow: auto;
  142. padding: 20px;
  143. padding-bottom: 300px;
  144. >div {
  145. margin-right: 20px;
  146. margin-bottom: 20px;
  147. }
  148. }
  149. </style>
  150. <style scoped lang="scss">
  151. .dr-task {
  152. width: 100%;
  153. height: 100%;
  154. }
  155. </style>