ImageCard.vue 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. <template>
  2. <el-card
  3. body-class=""
  4. class="!w-80 !h-auto !rounded-10px !relative !flex !flex-col"
  5. >
  6. <div class="!flex !flex-row !justify-between">
  7. <div>
  8. <el-button type="primary" text bg v-if="detail?.status === AiImageStatusEnum.IN_PROGRESS">
  9. 生成中
  10. </el-button>
  11. <el-button text bg v-else-if="detail?.status === AiImageStatusEnum.SUCCESS">
  12. 已完成
  13. </el-button>
  14. <el-button type="danger" text bg v-else-if="detail?.status === AiImageStatusEnum.FAIL">
  15. 异常
  16. </el-button>
  17. </div>
  18. <!-- 操作区 -->
  19. <div>
  20. <el-button
  21. class="!p-10px !m-0"
  22. text
  23. :icon="Download"
  24. @click="handleButtonClick('download', detail)"
  25. />
  26. <el-button
  27. class="!p-10px !m-0"
  28. text
  29. :icon="RefreshRight"
  30. @click="handleButtonClick('regeneration', detail)"
  31. />
  32. <el-button
  33. class="!p-10px !m-0"
  34. text
  35. :icon="Delete"
  36. @click="handleButtonClick('delete', detail)"
  37. />
  38. <el-button
  39. class="!p-10px !m-0"
  40. text
  41. :icon="More"
  42. @click="handleButtonClick('more', detail)"
  43. />
  44. </div>
  45. </div>
  46. <div class="!overflow-hidden !mt-20px !h-280px !flex-1" ref="cardImageRef">
  47. <el-image
  48. class="!w-full !rounded-10px"
  49. :src="detail?.picUrl"
  50. :preview-src-list="[detail.picUrl]"
  51. preview-teleported
  52. />
  53. <div v-if="detail?.status === AiImageStatusEnum.FAIL">
  54. {{ detail?.errorMessage }}
  55. </div>
  56. </div>
  57. <!-- Midjourney 专属操作 -->
  58. <div class="!mt-5px !w-full !flex !flex-row !flex-wrap !justify-start">
  59. <el-button
  60. size="small"
  61. v-for="button in detail?.buttons"
  62. :key="button"
  63. class="min-w-40px ml-0 mr-10px mt-5px"
  64. @click="handleMidjourneyBtnClick(button)"
  65. >
  66. {{ button.label }}{{ button.emoji }}
  67. </el-button>
  68. </div>
  69. </el-card>
  70. </template>
  71. <script setup lang="ts">
  72. import { Delete, Download, More, RefreshRight } from '@element-plus/icons-vue'
  73. import { ImageVO, ImageMidjourneyButtonsVO } from '@/api/ai/image'
  74. import { PropType } from 'vue'
  75. import { ElLoading, LoadingOptionsResolved } from 'element-plus'
  76. import { AiImageStatusEnum } from '@/views/ai/utils/constants'
  77. const message = useMessage() // 消息
  78. const props = defineProps({
  79. detail: {
  80. type: Object as PropType<ImageVO>,
  81. require: true
  82. }
  83. })
  84. const cardImageRef = ref<any>() // 卡片 image ref
  85. const cardImageLoadingInstance = ref<any>() // 卡片 image ref
  86. /** 处理点击事件 */
  87. const handleButtonClick = async (type, detail: ImageVO) => {
  88. emits('onBtnClick', type, detail)
  89. }
  90. /** 处理 Midjourney 按钮点击事件 */
  91. const handleMidjourneyBtnClick = async (button: ImageMidjourneyButtonsVO) => {
  92. // 确认窗体
  93. await message.confirm(`确认操作 "${button.label} ${button.emoji}" ?`)
  94. emits('onMjBtnClick', button, props.detail)
  95. }
  96. const emits = defineEmits(['onBtnClick', 'onMjBtnClick']) // emits
  97. /** 监听详情 */
  98. const { detail } = toRefs(props)
  99. watch(detail, async (newVal, oldVal) => {
  100. await handleLoading(newVal.status as string)
  101. })
  102. /** 处理加载状态 */
  103. const handleLoading = async (status: number) => {
  104. // 情况一:如果是生成中,则设置加载中的 loading
  105. if (status === AiImageStatusEnum.IN_PROGRESS) {
  106. cardImageLoadingInstance.value = ElLoading.service({
  107. target: cardImageRef.value,
  108. text: '生成中...'
  109. } as LoadingOptionsResolved)
  110. // 情况二:如果已经生成结束,则移除 loading
  111. } else {
  112. if (cardImageLoadingInstance.value) {
  113. cardImageLoadingInstance.value.close()
  114. cardImageLoadingInstance.value = null
  115. }
  116. }
  117. }
  118. /** 初始化 */
  119. onMounted(async () => {
  120. await handleLoading(props.detail.status as string)
  121. })
  122. </script>