ImageTask.vue 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  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">
  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. <div class="task-image-pagination">
  13. <el-pagination background layout="prev, pager, next"
  14. :default-page-size="pageSize"
  15. :total="pageTotal"
  16. @change="handlerPageChange"
  17. />
  18. </div>
  19. </el-card>
  20. <!-- 图片 detail 抽屉 -->
  21. <ImageDetailDrawer
  22. :show="isShowImageDetail"
  23. :id="showImageDetailId"
  24. @handler-drawer-close="handlerDrawerClose"
  25. />
  26. </template>
  27. <script setup lang="ts">
  28. import {ImageApi, ImageDetailVO, ImageMjActionVO, ImageMjButtonsVO} from '@/api/ai/image';
  29. import ImageDetailDrawer from './ImageDetailDrawer.vue'
  30. import ImageTaskCard from './ImageTaskCard.vue'
  31. import {ElLoading} from "element-plus";
  32. const message = useMessage() // 消息弹窗
  33. const imageList = ref<ImageDetailVO[]>([]) // image 列表
  34. const imageListInterval = ref<any>() // image 列表定时器,刷新列表
  35. const isShowImageDetail = ref<boolean>(false) // 是否显示 task 详情
  36. const showImageDetailId = ref<number>(0) // 是否显示 task 详情
  37. const imageTaskRef = ref<any>() // ref
  38. const imageTaskLoadingInstance = ref<any>() // loading
  39. const imageTaskLoading = ref<boolean>(false) // loading
  40. const pageNo = ref<number>(1) // page no
  41. const pageSize = ref<number>(10) // page size
  42. const pageTotal = ref<number>(0) // page size
  43. /** 抽屉 - close */
  44. const handlerDrawerClose = async () => {
  45. isShowImageDetail.value = false
  46. }
  47. /** 任务 - detail */
  48. const handlerDrawerOpen = async () => {
  49. isShowImageDetail.value = true
  50. }
  51. /**
  52. * 获取 - image 列表
  53. */
  54. const getImageList = async (apply:boolean = false) => {
  55. imageTaskLoading.value = true
  56. try {
  57. imageTaskLoadingInstance.value = ElLoading.service({
  58. target: imageTaskRef.value,
  59. text: '加载中...'
  60. })
  61. const { list, total } = await ImageApi.getImageList({pageNo: pageNo.value, pageSize: pageSize.value})
  62. if (apply) {
  63. imageList.value = [...imageList.value, ...list]
  64. } else {
  65. imageList.value = list
  66. }
  67. pageTotal.value = total
  68. } finally {
  69. if (imageTaskLoadingInstance.value) {
  70. imageTaskLoadingInstance.value.close();
  71. imageTaskLoadingInstance.value = null;
  72. }
  73. }
  74. }
  75. /** 图片 - btn click */
  76. const handlerImageBtnClick = async (type, imageDetail: ImageDetailVO) => {
  77. // 获取 image detail id
  78. showImageDetailId.value = imageDetail.id
  79. console.log('type', imageDetail.id)
  80. // 处理不用 btn
  81. if (type === 'more') {
  82. await handlerDrawerOpen()
  83. } else if (type === 'delete') {
  84. await message.confirm(`是否删除照片?`)
  85. await ImageApi.deleteImage(imageDetail.id)
  86. await getImageList()
  87. await message.success("删除成功!")
  88. } else if (type === 'download') {
  89. await downloadImage(imageDetail.picUrl)
  90. }
  91. }
  92. /** 图片 - mj btn click */
  93. const handlerImageMjBtnClick = async (button: ImageMjButtonsVO, imageDetail: ImageDetailVO) => {
  94. // 1、构建 params 参数
  95. const params = {
  96. id: imageDetail.id,
  97. customId: button.customId,
  98. } as ImageMjActionVO
  99. // 2、发送 action
  100. await ImageApi.midjourneyAction(params)
  101. // 3、刷新列表
  102. await getImageList()
  103. }
  104. /** 下载 - image */
  105. // TODO @fan:貌似可以考虑抽到 download 里面,作为一个方法
  106. const downloadImage = async (imageUrl) => {
  107. const image = new Image()
  108. image.setAttribute('crossOrigin', 'anonymous')
  109. image.src = imageUrl
  110. image.onload = () => {
  111. const canvas = document.createElement('canvas')
  112. canvas.width = image.width
  113. canvas.height = image.height
  114. const ctx = canvas.getContext('2d') as CanvasDrawImage
  115. ctx.drawImage(image, 0, 0, image.width, image.height)
  116. const url = canvas.toDataURL('image/png')
  117. const a = document.createElement('a')
  118. a.href = url
  119. a.download = 'image.png'
  120. a.click()
  121. }
  122. }
  123. // page change
  124. const handlerPageChange = async (page) => {
  125. pageNo.value = page
  126. await getImageList(false)
  127. }
  128. /** 暴露组件方法 */
  129. defineExpose({getImageList})
  130. /** 组件挂在的时候 */
  131. onMounted(async () => {
  132. // 获取 image 列表
  133. await getImageList()
  134. // 自动刷新 image 列表
  135. imageListInterval.value = setInterval(async () => {
  136. await getImageList(false)
  137. }, 1000 * 20)
  138. })
  139. /** 组件取消挂在的时候 */
  140. onUnmounted(async () => {
  141. if (imageListInterval.value) {
  142. clearInterval(imageListInterval.value)
  143. }
  144. })
  145. </script>
  146. <style lang="scss">
  147. .task-card {
  148. margin: 0;
  149. padding: 0;
  150. height: 100%;
  151. position: relative;
  152. }
  153. .task-image-list {
  154. position: relative;
  155. display: flex;
  156. flex-direction: row;
  157. flex-wrap: wrap;
  158. align-content: flex-start;
  159. height: 100%;
  160. overflow: auto;
  161. padding: 20px;
  162. padding-bottom: 140px;
  163. box-sizing: border-box; /* 确保内边距不会增加高度 */
  164. >div {
  165. margin-right: 20px;
  166. margin-bottom: 20px;
  167. }
  168. >div:last-of-type {
  169. //margin-bottom: 100px;
  170. }
  171. }
  172. .task-image-pagination {
  173. position: absolute;
  174. bottom: 60px;
  175. height: 50px;
  176. line-height: 90px;
  177. width: 100%;
  178. z-index: 999;
  179. background-color: #ffffff;
  180. display: flex;
  181. flex-direction: row;
  182. justify-content: center;
  183. align-items: center;
  184. }
  185. </style>
  186. <style scoped lang="scss">
  187. .dr-task {
  188. width: 100%;
  189. height: 100%;
  190. }
  191. </style>