ImageTaskCard.vue 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. <template>
  2. <el-card body-class="" class="image-card">
  3. <div class="image-operation">
  4. <div>
  5. <el-button
  6. type="primary"
  7. text
  8. bg
  9. v-if="imageDetail?.status === AiImageStatusEnum.IN_PROGRESS"
  10. >
  11. 生成中
  12. </el-button>
  13. <el-button text bg v-else-if="imageDetail?.status === AiImageStatusEnum.SUCCESS">
  14. 已完成
  15. </el-button>
  16. <el-button type="danger" text bg v-else-if="imageDetail?.status === AiImageStatusEnum.FAIL">
  17. 异常
  18. </el-button>
  19. </div>
  20. <!-- TODO @fan:1)按钮要不调整成详情、下载、再次生成、删除?;2)如果是再次生成,就把当前的参数填写到左侧的框框里? -->
  21. <div>
  22. <el-button
  23. class="btn"
  24. text
  25. :icon="Download"
  26. @click="handlerBtnClick('download', imageDetail)"
  27. />
  28. <el-button
  29. class="btn"
  30. text
  31. :icon="RefreshRight"
  32. @click="handlerBtnClick('regeneration', imageDetail)"
  33. />
  34. <el-button
  35. class="btn"
  36. text
  37. :icon="Delete"
  38. @click="handlerBtnClick('delete', imageDetail)"
  39. />
  40. <el-button class="btn" text :icon="More" @click="handlerBtnClick('more', imageDetail)" />
  41. </div>
  42. </div>
  43. <div class="image-wrapper" ref="cardImageRef">
  44. <!-- TODO @fan:要不加个点击,大图预览? -->
  45. <img class="image" :src="imageDetail?.picUrl" />
  46. <div v-if="imageDetail?.status === AiImageStatusEnum.FAIL">
  47. {{ imageDetail?.errorMessage }}
  48. </div>
  49. </div>
  50. <!-- TODO @fan:style 使用 unocss 替代下 -->
  51. <div class="image-mj-btns">
  52. <el-button
  53. size="small"
  54. v-for="button in imageDetail?.buttons"
  55. :key="button"
  56. style="min-width: 40px; margin-left: 0; margin-right: 10px; margin-top: 5px"
  57. @click="handlerMjBtnClick(button)"
  58. >
  59. {{ button.label }}{{ button.emoji }}
  60. </el-button>
  61. </div>
  62. </el-card>
  63. </template>
  64. <script setup lang="ts">
  65. import {Delete, Download, More, RefreshRight} from '@element-plus/icons-vue'
  66. import { ImageVO, ImageMjButtonsVO } from '@/api/ai/image'
  67. import { PropType } from 'vue'
  68. import { ElLoading } from 'element-plus'
  69. import { AiImageStatusEnum } from '@/views/ai/utils/constants'
  70. const cardImageRef = ref<any>() // 卡片 image ref
  71. const cardImageLoadingInstance = ref<any>() // 卡片 image ref
  72. const message = useMessage()
  73. const props = defineProps({
  74. imageDetail: {
  75. type: Object as PropType<ImageVO>,
  76. require: true
  77. }
  78. })
  79. /** 按钮 - 点击事件 */
  80. const handlerBtnClick = async (type, imageDetail: ImageVO) => {
  81. emits('onBtnClick', type, imageDetail)
  82. }
  83. const handlerLoading = async (status: number) => {
  84. // TODO @fan:这个搞成 Loading 组件,然后通过数据驱动,这样搞可以哇?
  85. if (status === AiImageStatusEnum.IN_PROGRESS) {
  86. cardImageLoadingInstance.value = ElLoading.service({
  87. target: cardImageRef.value,
  88. text: '生成中...'
  89. })
  90. } else {
  91. if (cardImageLoadingInstance.value) {
  92. cardImageLoadingInstance.value.close()
  93. cardImageLoadingInstance.value = null
  94. }
  95. }
  96. }
  97. /** mj 按钮 click */
  98. const handlerMjBtnClick = async (button: ImageMjButtonsVO) => {
  99. // 确认窗体
  100. await message.confirm(`确认操作 "${button.label} ${button.emoji}" ?`)
  101. emits('onMjBtnClick', button, props.imageDetail)
  102. }
  103. // watch
  104. const { imageDetail } = toRefs(props)
  105. watch(imageDetail, async (newVal, oldVal) => {
  106. await handlerLoading(newVal.status as string)
  107. })
  108. // emits
  109. const emits = defineEmits(['onBtnClick', 'onMjBtnClick'])
  110. //
  111. onMounted(async () => {
  112. await handlerLoading(props.imageDetail.status as string)
  113. })
  114. </script>
  115. <style scoped lang="scss">
  116. .image-card {
  117. width: 320px;
  118. height: auto;
  119. border-radius: 10px;
  120. position: relative;
  121. display: flex;
  122. flex-direction: column;
  123. .image-operation {
  124. display: flex;
  125. flex-direction: row;
  126. justify-content: space-between;
  127. .btn {
  128. //border: 1px solid red;
  129. padding: 10px;
  130. margin: 0;
  131. }
  132. }
  133. .image-wrapper {
  134. overflow: hidden;
  135. margin-top: 20px;
  136. height: 280px;
  137. flex: 1;
  138. .image {
  139. width: 100%;
  140. border-radius: 10px;
  141. }
  142. }
  143. .image-mj-btns {
  144. margin-top: 5px;
  145. width: 100%;
  146. display: flex;
  147. flex-direction: row;
  148. flex-wrap: wrap;
  149. justify-content: flex-start;
  150. }
  151. }
  152. </style>