index.vue 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. <!-- dall3 -->
  2. <template>
  3. <div class="prompt">
  4. <el-text tag="b">画面描述</el-text>
  5. <el-text tag="p">建议使用“形容词+动词+风格”的格式,使用“,”隔开.</el-text>
  6. <el-input
  7. v-model="prompt"
  8. maxlength="1024"
  9. rows="5"
  10. class="w-100% mt-15px"
  11. input-style="border-radius: 7px;"
  12. placeholder="例如:童话里的小屋应该是什么样子?"
  13. show-word-limit
  14. type="textarea"
  15. />
  16. </div>
  17. <div class="hot-words">
  18. <div>
  19. <el-text tag="b">随机热词</el-text>
  20. </div>
  21. <el-space wrap class="word-list">
  22. <el-button
  23. round
  24. class="btn"
  25. :type="selectHotWord === hotWord ? 'primary' : 'default'"
  26. v-for="hotWord in ImageHotWords"
  27. :key="hotWord"
  28. @click="handleHotWordClick(hotWord)"
  29. >
  30. {{ hotWord }}
  31. </el-button>
  32. </el-space>
  33. </div>
  34. <div class="image-size">
  35. <div>
  36. <el-text tag="b">尺寸</el-text>
  37. </div>
  38. <el-space wrap class="size-list">
  39. <div
  40. class="size-item"
  41. v-for="imageSize in MidjourneySizeList"
  42. :key="imageSize.key"
  43. @click="handleSizeClick(imageSize)"
  44. >
  45. <div
  46. :class="selectSize === imageSize.key ? 'size-wrapper selectImageSize' : 'size-wrapper'"
  47. >
  48. <div :style="imageSize.style"></div>
  49. </div>
  50. <div class="size-font">{{ imageSize.key }}</div>
  51. </div>
  52. </el-space>
  53. </div>
  54. <div class="model">
  55. <div>
  56. <el-text tag="b">模型</el-text>
  57. </div>
  58. <el-space wrap class="model-list">
  59. <div
  60. :class="selectModel === model.key ? 'modal-item selectModel' : 'modal-item'"
  61. v-for="model in MidjourneyModels"
  62. :key="model.key"
  63. >
  64. <el-image :src="model.image" fit="contain" @click="handleModelClick(model)" />
  65. <div class="model-font">{{ model.name }}</div>
  66. </div>
  67. </el-space>
  68. </div>
  69. <div class="version">
  70. <div>
  71. <el-text tag="b">版本</el-text>
  72. </div>
  73. <el-space wrap class="version-list">
  74. <el-select
  75. v-model="selectVersion"
  76. class="version-select !w-350px"
  77. clearable
  78. placeholder="请选择版本"
  79. >
  80. <el-option
  81. v-for="item in versionList"
  82. :key="item.value"
  83. :label="item.label"
  84. :value="item.value"
  85. />
  86. </el-select>
  87. </el-space>
  88. </div>
  89. <div class="model">
  90. <div>
  91. <el-text tag="b">参考图</el-text>
  92. </div>
  93. <el-space wrap class="model-list">
  94. <UploadImg v-model="referImageUrl" height="120px" width="120px" />
  95. </el-space>
  96. </div>
  97. <div class="btns">
  98. <el-button type="primary" size="large" round @click="handleGenerateImage">
  99. {{ drawIn ? '生成中' : '生成内容' }}
  100. </el-button>
  101. </div>
  102. </template>
  103. <script setup lang="ts">
  104. import { ImageApi, ImageMidjourneyImagineReqVO, ImageVO } from '@/api/ai/image'
  105. import {
  106. AiPlatformEnum,
  107. ImageHotWords,
  108. ImageSizeVO,
  109. ImageModelVO,
  110. MidjourneyModels,
  111. MidjourneySizeList,
  112. MidjourneyVersions,
  113. NijiVersionList
  114. } from '@/views/ai/utils/constants'
  115. const message = useMessage() // 消息弹窗
  116. // 定义属性
  117. const drawIn = ref<boolean>(false) // 生成中
  118. const selectHotWord = ref<string>('') // 选中的热词
  119. // 表单
  120. const prompt = ref<string>('') // 提示词
  121. const referImageUrl = ref<any>() // 参考图
  122. const selectModel = ref<string>('midjourney') // 选中的模型
  123. const selectSize = ref<string>('1:1') // 选中 size
  124. const selectVersion = ref<any>('6.0') // 选中的 version
  125. const versionList = ref<any>(MidjourneyVersions) // version 列表
  126. const emits = defineEmits(['onDrawStart', 'onDrawComplete']) // 定义 emits
  127. /** 选择热词 */
  128. const handleHotWordClick = async (hotWord: string) => {
  129. // 情况一:取消选中
  130. if (selectHotWord.value == hotWord) {
  131. selectHotWord.value = ''
  132. return
  133. }
  134. // 情况二:选中
  135. selectHotWord.value = hotWord // 选中
  136. prompt.value = hotWord // 设置提示次
  137. }
  138. /** 点击 size 尺寸 */
  139. const handleSizeClick = async (imageSize: ImageSizeVO) => {
  140. selectSize.value = imageSize.key
  141. }
  142. /** 点击 model 模型 */
  143. const handleModelClick = async (model: ImageModelVO) => {
  144. selectModel.value = model.key
  145. if (model.key === 'niji') {
  146. versionList.value = NijiVersionList // 默认选择 niji
  147. } else {
  148. versionList.value = MidjourneyVersions // 默认选择 midjourney
  149. }
  150. selectVersion.value = versionList.value[0].value
  151. }
  152. /** 图片生成 */
  153. const handleGenerateImage = async () => {
  154. // 二次确认
  155. await message.confirm(`确认生成内容?`)
  156. try {
  157. // 加载中
  158. drawIn.value = true
  159. // 回调
  160. emits('onDrawStart', AiPlatformEnum.MIDJOURNEY)
  161. // 发送请求
  162. const imageSize = MidjourneySizeList.find(
  163. (item) => selectSize.value === item.key
  164. ) as ImageSizeVO
  165. const req = {
  166. prompt: prompt.value,
  167. model: selectModel.value,
  168. width: imageSize.width,
  169. height: imageSize.height,
  170. version: selectVersion.value,
  171. referImageUrl: referImageUrl.value
  172. } as ImageMidjourneyImagineReqVO
  173. await ImageApi.midjourneyImagine(req)
  174. } finally {
  175. // 回调
  176. emits('onDrawComplete', AiPlatformEnum.MIDJOURNEY)
  177. // 加载结束
  178. drawIn.value = false
  179. }
  180. }
  181. /** 填充值 */
  182. const settingValues = async (detail: ImageVO) => {
  183. // 提示词
  184. prompt.value = detail.prompt
  185. // image size
  186. const imageSize = MidjourneySizeList.find(
  187. (item) => item.key === `${detail.width}:${detail.height}`
  188. ) as ImageSizeVO
  189. selectSize.value = imageSize.key
  190. // 选中模型
  191. const model = MidjourneyModels.find((item) => item.key === detail.options?.model) as ImageModelVO
  192. await handleModelClick(model)
  193. // 版本
  194. selectVersion.value = versionList.value.find(
  195. (item) => item.value === detail.options?.version
  196. ).value
  197. // image
  198. referImageUrl.value = detail.options.referImageUrl
  199. }
  200. /** 暴露组件方法 */
  201. defineExpose({ settingValues })
  202. </script>
  203. <style scoped lang="scss">
  204. // 提示词
  205. .prompt {
  206. }
  207. // 热词
  208. .hot-words {
  209. display: flex;
  210. flex-direction: column;
  211. margin-top: 30px;
  212. .word-list {
  213. display: flex;
  214. flex-direction: row;
  215. flex-wrap: wrap;
  216. justify-content: start;
  217. margin-top: 15px;
  218. .btn {
  219. margin: 0;
  220. }
  221. }
  222. }
  223. // version
  224. .version {
  225. margin-top: 20px;
  226. .version-list {
  227. margin-top: 20px;
  228. width: 100%;
  229. }
  230. }
  231. // 模型
  232. .model {
  233. margin-top: 30px;
  234. .model-list {
  235. margin-top: 15px;
  236. .modal-item {
  237. display: flex;
  238. flex-direction: column;
  239. align-items: center;
  240. width: 150px;
  241. //outline: 1px solid blue;
  242. overflow: hidden;
  243. border: 3px solid transparent;
  244. cursor: pointer;
  245. .model-font {
  246. font-size: 14px;
  247. color: #3e3e3e;
  248. font-weight: bold;
  249. }
  250. }
  251. .selectModel {
  252. border: 3px solid #1293ff;
  253. border-radius: 5px;
  254. }
  255. }
  256. }
  257. // 尺寸
  258. .image-size {
  259. width: 100%;
  260. margin-top: 30px;
  261. .size-list {
  262. display: flex;
  263. flex-direction: row;
  264. justify-content: space-between;
  265. width: 100%;
  266. margin-top: 20px;
  267. .size-item {
  268. display: flex;
  269. flex-direction: column;
  270. align-items: center;
  271. cursor: pointer;
  272. .size-wrapper {
  273. display: flex;
  274. flex-direction: column;
  275. align-items: center;
  276. justify-content: center;
  277. border-radius: 7px;
  278. padding: 4px;
  279. width: 50px;
  280. height: 50px;
  281. background-color: #fff;
  282. border: 1px solid #fff;
  283. }
  284. .size-font {
  285. font-size: 14px;
  286. color: #3e3e3e;
  287. font-weight: bold;
  288. }
  289. }
  290. }
  291. .selectImageSize {
  292. border: 1px solid #1293ff !important;
  293. }
  294. }
  295. .btns {
  296. display: flex;
  297. justify-content: center;
  298. margin-top: 50px;
  299. }
  300. </style>