index.vue 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. <!-- dall3 -->
  2. <template>
  3. <div class="prompt">
  4. <el-text tag="b">画面描述</el-text>
  5. <el-text tag="p">建议使用“形容词+动词+风格”的格式,使用“,”隔开</el-text>
  6. <!-- TODO @fan:style 看看能不能哟 unocss 替代 -->
  7. <el-input
  8. v-model="prompt"
  9. maxlength="1024"
  10. rows="5"
  11. style="width: 100%; margin-top: 15px;"
  12. input-style="border-radius: 7px;"
  13. placeholder="例如:童话里的小屋应该是什么样子?"
  14. show-word-limit
  15. type="textarea"
  16. />
  17. </div>
  18. <div class="hot-words">
  19. <div>
  20. <el-text tag="b">随机热词</el-text>
  21. </div>
  22. <el-space wrap class="word-list">
  23. <el-button round
  24. class="btn"
  25. :type="(selectHotWord === hotWord ? 'primary' : 'default')"
  26. v-for="hotWord in hotWords"
  27. :key="hotWord"
  28. @click="handlerHotWordClick(hotWord)"
  29. >
  30. {{ hotWord }}
  31. </el-button>
  32. </el-space>
  33. </div>
  34. <div class="group-item">
  35. <div>
  36. <el-text tag="b">采样</el-text>
  37. </div>
  38. <el-space wrap class="group-item-body">
  39. <el-select
  40. v-model="selectSampler"
  41. placeholder="Select"
  42. size="large"
  43. style="width: 350px;"
  44. >
  45. <el-option
  46. v-for="item in sampler"
  47. :key="item.key"
  48. :label="item.name"
  49. :value="item.key"
  50. />
  51. </el-select>
  52. </el-space>
  53. </div>
  54. <div class="group-item">
  55. <div>
  56. <el-text tag="b">图片尺寸</el-text>
  57. </div>
  58. <el-space wrap class="group-item-body">
  59. <el-select
  60. v-model="selectImageSize"
  61. placeholder="Select"
  62. size="large"
  63. style="width: 350px;"
  64. >
  65. <el-option
  66. v-for="item in imageSizeList"
  67. :key="item.key"
  68. :label="item.key"
  69. :value="item.key"
  70. />
  71. </el-select>
  72. </el-space>
  73. </div>
  74. <div class="group-item">
  75. <div>
  76. <el-text tag="b">迭代步数</el-text>
  77. </div>
  78. <el-space wrap class="group-item-body">
  79. <el-input v-model="steps" type="number" size="large" style="width: 350px" placeholder="Please input" />
  80. </el-space>
  81. </div>
  82. <div class="group-item">
  83. <div>
  84. <el-text tag="b">随机性</el-text>
  85. </div>
  86. <el-space wrap class="group-item-body">
  87. <el-input v-model="seed" type="number" size="large" style="width: 350px" placeholder="Please input" />
  88. </el-space>
  89. </div>
  90. <div class="btns">
  91. <el-button type="primary"
  92. size="large"
  93. round
  94. :loading="drawIn"
  95. @click="handlerGenerateImage">
  96. {{drawIn ? '生成中' : '生成内容'}}
  97. </el-button>
  98. </div>
  99. </template>
  100. <script setup lang="ts">
  101. import {ImageApi, ImageDrawReqVO} from '@/api/ai/image';
  102. // image 模型
  103. interface ImageModelVO {
  104. key: string
  105. name: string
  106. }
  107. // image 大小
  108. interface ImageSizeVO {
  109. key: string
  110. width: string,
  111. height: string,
  112. }
  113. // 定义属性
  114. const prompt = ref<string>('') // 提示词
  115. const drawIn = ref<boolean>(false) // 生成中
  116. const selectHotWord = ref<string>('') // 选中的热词
  117. const hotWords = ref<string[]>(['中国旗袍', '古装美女', '卡通头像', '机甲战士', '童话小屋', '中国长城']) // 热词
  118. const selectSampler = ref<any>({}) // 模型
  119. // message
  120. const message = useMessage()
  121. // 模型
  122. const sampler = ref<ImageModelVO[]>([
  123. {
  124. key: 'Euler a',
  125. name: 'Euler a',
  126. },
  127. {
  128. key: 'DPM++ 2S a Karras',
  129. name: 'DPM++ 2S a Karras',
  130. },
  131. {
  132. key: 'DPM++ 2M Karras',
  133. name: 'DPM++ 2M Karras',
  134. },
  135. {
  136. key: 'DPM++ SDE Karras',
  137. name: 'DPM++ SDE Karras',
  138. },
  139. {
  140. key: 'DPM++ 2M SDE Karras',
  141. name: 'DPM++ 2M SDE Karras',
  142. },
  143. ]) // 模型
  144. selectSampler.value = sampler.value[0]
  145. const selectImageSize = ref<ImageSizeVO>({} as ImageSizeVO) // 选中 size
  146. const imageSizeList = ref<ImageSizeVO[]>([
  147. {
  148. key: '512x512',
  149. width: '512',
  150. height: '512',
  151. },
  152. {
  153. key: '1024x1024',
  154. width: '1024',
  155. height: '1024',
  156. },
  157. {
  158. key: '1024x1792',
  159. width: '1024',
  160. height: '1792',
  161. },
  162. {
  163. key: '1792x1024',
  164. width: '1792',
  165. height: '1024',
  166. },
  167. {
  168. key: '2048x2048',
  169. width: '2048',
  170. height: '2048',
  171. },
  172. {
  173. key: '720x1280',
  174. width: '720',
  175. height: '1280',
  176. },
  177. {
  178. key: '1080x1920',
  179. width: '1080',
  180. height: '1920',
  181. },
  182. {
  183. key: '1440x2560',
  184. width: '1440',
  185. height: '2560',
  186. },
  187. {
  188. key: '2160x3840',
  189. width: '2160',
  190. height: '3840',
  191. },
  192. ]) // size
  193. selectImageSize.value = imageSizeList.value[0]
  194. const steps = ref<number>(20) // 迭代步数
  195. const seed = ref<number>(-1) // 控制生成图像的随机性
  196. // 定义 Props
  197. const props = defineProps({})
  198. // 定义 emits
  199. const emits = defineEmits(['onDrawStart', 'onDrawComplete'])
  200. // TODO @fan:如果是简单注释,建议用 /** */,主要是现在项目里是这种风格哈,保持一致好点~
  201. // TODO @fan:handler 应该改成 handle 哈
  202. /** 热词 - click */
  203. const handlerHotWordClick = async (hotWord: string) => {
  204. // 取消选中
  205. if (selectHotWord.value == hotWord) {
  206. selectHotWord.value = ''
  207. return
  208. }
  209. // 选中
  210. selectHotWord.value = hotWord
  211. // 替换提示词
  212. prompt.value = hotWord
  213. }
  214. /** 图片生产 */
  215. const handlerGenerateImage = async () => {
  216. // 二次确认
  217. await message.confirm(`确认生成内容?`)
  218. try {
  219. // 加载中
  220. drawIn.value = true
  221. // 回调
  222. emits('onDrawStart', 'StableDiffusion')
  223. const form = {
  224. platform: 'StableDiffusion',
  225. model: 'stable-diffusion-v1-6',
  226. prompt: prompt.value, // 提示词
  227. width: selectImageSize.value.width, // 图片宽度
  228. height: selectImageSize.value.height, // 图片高度
  229. options: {
  230. sampler: selectSampler.value.key, // 采样算法
  231. seed: seed.value, // 随机种子
  232. steps: steps.value, // 图片生成步数
  233. },
  234. } as ImageDrawReqVO
  235. // 发送请求
  236. await ImageApi.drawImage(form)
  237. } finally {
  238. // 回调
  239. emits('onDrawComplete', 'StableDiffusion')
  240. // 加载结束
  241. drawIn.value = false
  242. }
  243. }
  244. </script>
  245. <style scoped lang="scss">
  246. // 提示词
  247. .prompt {
  248. }
  249. // 热词
  250. .hot-words {
  251. display: flex;
  252. flex-direction: column;
  253. margin-top: 30px;
  254. .word-list {
  255. display: flex;
  256. flex-direction: row;
  257. flex-wrap: wrap;
  258. justify-content: start;
  259. margin-top: 15px;
  260. .btn {
  261. margin: 0;
  262. }
  263. }
  264. }
  265. // 模型
  266. .group-item {
  267. margin-top: 30px;
  268. .group-item-body {
  269. margin-top: 15px;
  270. width: 100%;
  271. }
  272. }
  273. .btns {
  274. display: flex;
  275. justify-content: center;
  276. margin-top: 50px;
  277. }
  278. </style>