index.vue 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  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. style="width: 100%; margin-top: 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 round
  23. class="btn"
  24. :type="(selectHotWord === hotWord ? 'primary' : 'default')"
  25. v-for="hotWord in hotWords"
  26. :key="hotWord"
  27. @click="handlerHotWordClick(hotWord)"
  28. >
  29. {{ hotWord }}
  30. </el-button>
  31. </el-space>
  32. </div>
  33. <div class="image-size">
  34. <div>
  35. <el-text tag="b">尺寸</el-text>
  36. </div>
  37. <el-space wrap class="size-list">
  38. <div class="size-item"
  39. v-for="imageSize in imageSizeList"
  40. :key="imageSize.key"
  41. @click="handlerSizeClick(imageSize)">
  42. <div :class="selectImageSize === imageSize ? 'size-wrapper selectImageSize' : 'size-wrapper'">
  43. <div :style="imageSize.style"></div>
  44. </div>
  45. <div class="size-font">{{ imageSize.key }}</div>
  46. </div>
  47. </el-space>
  48. </div>
  49. <div class="version">
  50. <div>
  51. <el-text tag="b">版本</el-text>
  52. </div>
  53. <el-space wrap class="version-list">
  54. <el-select
  55. v-model="selectVersion"
  56. class="version-select"
  57. clearable
  58. placeholder="请选择版本"
  59. style="width: 350px"
  60. @change="handlerChangeVersion"
  61. >
  62. <el-option
  63. v-for="item in versionList"
  64. :key="item.value"
  65. :label="item.label"
  66. :value="item.value"
  67. />
  68. </el-select>
  69. </el-space>
  70. </div>
  71. <div class="model">
  72. <div>
  73. <el-text tag="b">模型</el-text>
  74. </div>
  75. <el-space wrap class="model-list">
  76. <div
  77. :class="selectModel === model ? 'modal-item selectModel' : 'modal-item'"
  78. v-for="model in models"
  79. :key="model.key"
  80. >
  81. <el-image
  82. :src="model.image"
  83. fit="contain"
  84. @click="handlerModelClick(model)"
  85. />
  86. <div class="model-font">{{model.name}}</div>
  87. </div>
  88. </el-space>
  89. </div>
  90. <div class="btns">
  91. <!-- <el-button size="large" round>重置内容</el-button>-->
  92. <el-button type="primary" size="large" round @click="handlerGenerateImage">生成内容</el-button>
  93. </div>
  94. </template>
  95. <script setup lang="ts">
  96. // image 模型
  97. import {ImageApi, ImageMidjourneyImagineReqVO} from "@/api/ai/image";
  98. // message
  99. const message = useMessage()
  100. // 定义 emits
  101. const emits = defineEmits(['onDrawStart', 'onDrawComplete'])
  102. interface ImageModelVO {
  103. key: string
  104. name: string
  105. image: string
  106. }
  107. // image 大小
  108. interface ImageSizeVO {
  109. key: string
  110. style: string,
  111. width: string,
  112. height: string,
  113. }
  114. // 定义属性
  115. const prompt = ref<string>('') // 提示词
  116. const selectHotWord = ref<string>('') // 选中的热词
  117. const hotWords = ref<string[]>(['中国旗袍', '古装美女', '卡通头像', '机甲战士', '童话小屋', '中国长城']) // 热词
  118. const selectModel = ref<any>() // 选中的热词
  119. const models = ref<ImageModelVO[]>([
  120. {
  121. key: 'midjourney',
  122. name: 'MJ',
  123. image: 'https://bigpt8.com/pc/_nuxt/mj.34a61377.png',
  124. },
  125. {
  126. key: 'niji',
  127. name: 'NIJI',
  128. image: 'https://bigpt8.com/pc/_nuxt/nj.ca79b143.png',
  129. },
  130. ]) // 模型
  131. selectModel.value = models.value[0] // 默认选中
  132. const selectImageSize = ref<ImageSizeVO>({} as ImageSizeVO) // 选中 size
  133. const imageSizeList = ref<ImageSizeVO[]>([
  134. {
  135. key: '1:1',
  136. width: "1",
  137. height: "1",
  138. style: 'width: 30px; height: 30px;background-color: #dcdcdc;',
  139. },
  140. {
  141. key: '3:4',
  142. width: "3",
  143. height: "4",
  144. style: 'width: 30px; height: 40px;background-color: #dcdcdc;',
  145. },
  146. {
  147. key: '4:3',
  148. width: "4",
  149. height: "3",
  150. style: 'width: 40px; height: 30px;background-color: #dcdcdc;',
  151. },
  152. {
  153. key: '9:16',
  154. width: "9",
  155. height: "16",
  156. style: 'width: 30px; height: 50px;background-color: #dcdcdc;',
  157. },
  158. {
  159. key: '16:9',
  160. width: "16",
  161. height: "9",
  162. style: 'width: 50px; height: 30px;background-color: #dcdcdc;',
  163. },
  164. ]) // size
  165. selectImageSize.value = imageSizeList.value[0]
  166. // version
  167. let versionList = ref<any>([]) // version 列表
  168. const midjourneyVersionList = ref<any>([
  169. {
  170. value: '6.0',
  171. label: 'v6.0',
  172. },
  173. {
  174. value: '5.2',
  175. label: 'v5.2',
  176. },
  177. {
  178. value: '5.1',
  179. label: 'v5.1',
  180. },
  181. {
  182. value: '5.0',
  183. label: 'v5.0',
  184. },
  185. {
  186. value: '4.0',
  187. label: 'v4.0',
  188. },
  189. ])
  190. const nijiVersionList = ref<any>([
  191. {
  192. value: '5',
  193. label: 'v5',
  194. },
  195. ])
  196. const selectVersion = ref<any>('6.0') // 选中的 version
  197. versionList.value = midjourneyVersionList.value // 默认选择 midjourney
  198. /** 热词 - click */
  199. const handlerHotWordClick = async (hotWord: string) => {
  200. // 取消
  201. if (selectHotWord.value == hotWord) {
  202. selectHotWord.value = ''
  203. return
  204. }
  205. // 选中
  206. selectHotWord.value = hotWord
  207. // 设置提示次
  208. prompt.value = hotWord
  209. }
  210. /** size - click */
  211. const handlerSizeClick = async (imageSize: ImageSizeVO) => {
  212. if (selectImageSize.value === imageSize) {
  213. selectImageSize.value = {} as ImageSizeVO
  214. return
  215. }
  216. selectImageSize.value = imageSize
  217. }
  218. /** 模型 - click */
  219. const handlerModelClick = async (model: ImageModelVO) => {
  220. selectModel.value = model
  221. if (model.key === 'niji') {
  222. versionList.value = nijiVersionList.value // 默认选择 niji
  223. } else {
  224. versionList.value = midjourneyVersionList.value // 默认选择 midjourney
  225. }
  226. selectVersion.value = versionList.value[0].value
  227. }
  228. /** version - click */
  229. const handlerChangeVersion = async (version) => {
  230. console.log('version', version)
  231. }
  232. /** 图片生产 */
  233. const handlerGenerateImage = async () => {
  234. // 二次确认
  235. await message.confirm(`确认生成内容?`)
  236. // todo @范 图片生产逻辑
  237. try {
  238. // 回调
  239. emits('onDrawStart', selectModel.value.key)
  240. // 发送请求
  241. const req = {
  242. prompt: prompt.value,
  243. model: selectModel.value.key,
  244. width: selectImageSize.value.width,
  245. height: selectImageSize.value.height,
  246. version: selectVersion.value,
  247. base64Array: [],
  248. } as ImageMidjourneyImagineReqVO
  249. await ImageApi.midjourneyImagine(req)
  250. } finally {
  251. // 回调
  252. emits('onDrawComplete', selectModel.value.key)
  253. }
  254. }
  255. </script>
  256. <style scoped lang="scss">
  257. // 提示词
  258. .prompt {
  259. }
  260. // 热词
  261. .hot-words {
  262. display: flex;
  263. flex-direction: column;
  264. margin-top: 30px;
  265. .word-list {
  266. display: flex;
  267. flex-direction: row;
  268. flex-wrap: wrap;
  269. justify-content: start;
  270. margin-top: 15px;
  271. .btn {
  272. margin: 0;
  273. }
  274. }
  275. }
  276. // version
  277. .version {
  278. margin-top: 20px;
  279. .version-list {
  280. margin-top: 20px;
  281. width: 100%;
  282. }
  283. }
  284. // 模型
  285. .model {
  286. margin-top: 30px;
  287. .model-list {
  288. margin-top: 15px;
  289. .modal-item {
  290. display: flex;
  291. flex-direction: column;
  292. align-items: center;
  293. width: 150px;
  294. //outline: 1px solid blue;
  295. overflow: hidden;
  296. border: 3px solid transparent;
  297. cursor: pointer;
  298. .model-font {
  299. font-size: 14px;
  300. color: #3e3e3e;
  301. font-weight: bold;
  302. }
  303. }
  304. .selectModel {
  305. border: 3px solid #1293ff;
  306. border-radius: 5px;
  307. }
  308. }
  309. }
  310. // 尺寸
  311. .image-size {
  312. width: 100%;
  313. margin-top: 30px;
  314. .size-list {
  315. display: flex;
  316. flex-direction: row;
  317. justify-content: space-between;
  318. width: 100%;
  319. margin-top: 20px;
  320. .size-item {
  321. display: flex;
  322. flex-direction: column;
  323. align-items: center;
  324. cursor: pointer;
  325. .size-wrapper {
  326. display: flex;
  327. flex-direction: column;
  328. align-items: center;
  329. justify-content: center;
  330. border-radius: 7px;
  331. padding: 4px;
  332. width: 50px;
  333. height: 50px;
  334. background-color: #fff;
  335. border: 1px solid #fff;
  336. }
  337. .size-font {
  338. font-size: 14px;
  339. color: #3e3e3e;
  340. font-weight: bold;
  341. }
  342. }
  343. }
  344. .selectImageSize {
  345. border: 1px solid #1293ff !important;
  346. }
  347. }
  348. .btns {
  349. display: flex;
  350. justify-content: center;
  351. margin-top: 50px;
  352. }
  353. </style>