index.vue 8.9 KB

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