index.vue 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  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="handleHotWordClick(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="handleSizeClick(imageSize)">
  42. <div :class="selectImageSize === imageSize.key ? '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="handleChangeVersion"
  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.key ? '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="handleModelClick(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="handleGenerateImage">生成内容</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<string>('midjourney') // 选中的热词
  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. const selectImageSize = ref<string>('1:1') // 选中 size
  141. const imageSizeList = ref<ImageSizeVO[]>([
  142. {
  143. key: '1:1',
  144. width: "1",
  145. height: "1",
  146. style: 'width: 30px; height: 30px;background-color: #dcdcdc;',
  147. },
  148. {
  149. key: '3:4',
  150. width: "3",
  151. height: "4",
  152. style: 'width: 30px; height: 40px;background-color: #dcdcdc;',
  153. },
  154. {
  155. key: '4:3',
  156. width: "4",
  157. height: "3",
  158. style: 'width: 40px; height: 30px;background-color: #dcdcdc;',
  159. },
  160. {
  161. key: '9:16',
  162. width: "9",
  163. height: "16",
  164. style: 'width: 30px; height: 50px;background-color: #dcdcdc;',
  165. },
  166. {
  167. key: '16:9',
  168. width: "16",
  169. height: "9",
  170. style: 'width: 50px; height: 30px;background-color: #dcdcdc;',
  171. },
  172. ]) // size
  173. // version
  174. const midjourneyVersionList = ref<any>([
  175. {
  176. value: '6.0',
  177. label: 'v6.0',
  178. },
  179. {
  180. value: '5.2',
  181. label: 'v5.2',
  182. },
  183. {
  184. value: '5.1',
  185. label: 'v5.1',
  186. },
  187. {
  188. value: '5.0',
  189. label: 'v5.0',
  190. },
  191. {
  192. value: '4.0',
  193. label: 'v4.0',
  194. },
  195. ])
  196. const nijiVersionList = ref<any>([
  197. {
  198. value: '5',
  199. label: 'v5',
  200. },
  201. ])
  202. const selectVersion = ref<any>('6.0') // 选中的 version
  203. let versionList = ref<any>([]) // version 列表
  204. versionList.value = midjourneyVersionList.value // 默认选择 midjourney
  205. /** 热词 - click */
  206. const handleHotWordClick = async (hotWord: string) => {
  207. // 取消
  208. if (selectHotWord.value == hotWord) {
  209. selectHotWord.value = ''
  210. return
  211. }
  212. // 选中
  213. selectHotWord.value = hotWord
  214. // 设置提示次
  215. prompt.value = hotWord
  216. }
  217. /** size - click */
  218. const handleSizeClick = async (imageSize: ImageSizeVO) => {
  219. selectImageSize.value = imageSize.key
  220. }
  221. /** 模型 - click */
  222. const handleModelClick = async (model: ImageModelVO) => {
  223. selectModel.value = model.key
  224. if (model.key === 'niji') {
  225. versionList.value = nijiVersionList.value // 默认选择 niji
  226. } else {
  227. versionList.value = midjourneyVersionList.value // 默认选择 midjourney
  228. }
  229. selectVersion.value = versionList.value[0].value
  230. }
  231. /** version - click */
  232. const handleChangeVersion = async (version) => {
  233. console.log('version', version)
  234. }
  235. /** 图片生产 */
  236. const handleGenerateImage = async () => {
  237. // 二次确认
  238. await message.confirm(`确认生成内容?`)
  239. // todo @芋艿 图片生产逻辑
  240. try {
  241. // 回调
  242. emits('onDrawStart', selectModel.value)
  243. // 发送请求
  244. const imageSize = imageSizeList.value.find(item => selectImageSize.value === item.key) as ImageSizeVO
  245. const req = {
  246. prompt: prompt.value,
  247. model: selectModel.value,
  248. width: imageSize.width,
  249. height: imageSize.height,
  250. version: selectVersion.value,
  251. referImageUrl: referImage.value,
  252. } as ImageMidjourneyImagineReqVO
  253. await ImageApi.midjourneyImagine(req)
  254. } finally {
  255. // 回调
  256. emits('onDrawComplete', selectModel.value)
  257. }
  258. }
  259. /** 填充值 */
  260. const settingValues = async (imageDetail: ImageVO) => {
  261. // 提示词
  262. prompt.value = imageDetail.prompt
  263. // image size
  264. const imageSize = imageSizeList.value.find(item => item.key === `${imageDetail.width}:${imageDetail.height}`) as ImageSizeVO
  265. selectImageSize.value = imageSize.key
  266. // 选中模型
  267. const model = models.value.find(item => item.key === imageDetail.options?.model) as ImageModelVO
  268. await handleModelClick(model)
  269. // 版本
  270. selectVersion.value = versionList.value.find(item => item.value === imageDetail.options?.version).value
  271. // image
  272. referImage.value = imageDetail.options.referImageUrl
  273. }
  274. /** 暴露组件方法 */
  275. defineExpose({ settingValues })
  276. </script>
  277. <style scoped lang="scss">
  278. // 提示词
  279. .prompt {
  280. }
  281. // 热词
  282. .hot-words {
  283. display: flex;
  284. flex-direction: column;
  285. margin-top: 30px;
  286. .word-list {
  287. display: flex;
  288. flex-direction: row;
  289. flex-wrap: wrap;
  290. justify-content: start;
  291. margin-top: 15px;
  292. .btn {
  293. margin: 0;
  294. }
  295. }
  296. }
  297. // version
  298. .version {
  299. margin-top: 20px;
  300. .version-list {
  301. margin-top: 20px;
  302. width: 100%;
  303. }
  304. }
  305. // 模型
  306. .model {
  307. margin-top: 30px;
  308. .model-list {
  309. margin-top: 15px;
  310. .modal-item {
  311. display: flex;
  312. flex-direction: column;
  313. align-items: center;
  314. width: 150px;
  315. //outline: 1px solid blue;
  316. overflow: hidden;
  317. border: 3px solid transparent;
  318. cursor: pointer;
  319. .model-font {
  320. font-size: 14px;
  321. color: #3e3e3e;
  322. font-weight: bold;
  323. }
  324. }
  325. .selectModel {
  326. border: 3px solid #1293ff;
  327. border-radius: 5px;
  328. }
  329. }
  330. }
  331. // 尺寸
  332. .image-size {
  333. width: 100%;
  334. margin-top: 30px;
  335. .size-list {
  336. display: flex;
  337. flex-direction: row;
  338. justify-content: space-between;
  339. width: 100%;
  340. margin-top: 20px;
  341. .size-item {
  342. display: flex;
  343. flex-direction: column;
  344. align-items: center;
  345. cursor: pointer;
  346. .size-wrapper {
  347. display: flex;
  348. flex-direction: column;
  349. align-items: center;
  350. justify-content: center;
  351. border-radius: 7px;
  352. padding: 4px;
  353. width: 50px;
  354. height: 50px;
  355. background-color: #fff;
  356. border: 1px solid #fff;
  357. }
  358. .size-font {
  359. font-size: 14px;
  360. color: #3e3e3e;
  361. font-weight: bold;
  362. }
  363. }
  364. }
  365. .selectImageSize {
  366. border: 1px solid #1293ff !important;
  367. }
  368. }
  369. .btns {
  370. display: flex;
  371. justify-content: center;
  372. margin-top: 50px;
  373. }
  374. </style>