index.vue 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  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 ImageHotEnglishWords"
  27. :key="hotWord"
  28. @click="handleHotWordClick(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 v-model="sampler" placeholder="Select" size="large" class="!w-350px">
  40. <el-option
  41. v-for="item in StableDiffusionSamplers"
  42. :key="item.key"
  43. :label="item.name"
  44. :value="item.key"
  45. />
  46. </el-select>
  47. </el-space>
  48. </div>
  49. <div class="group-item">
  50. <div>
  51. <el-text tag="b">CLIP</el-text>
  52. </div>
  53. <el-space wrap class="group-item-body">
  54. <el-select v-model="clipGuidancePreset" placeholder="Select" size="large" class="!w-350px">
  55. <el-option
  56. v-for="item in StableDiffusionClipGuidancePresets"
  57. :key="item.key"
  58. :label="item.name"
  59. :value="item.key"
  60. />
  61. </el-select>
  62. </el-space>
  63. </div>
  64. <div class="group-item">
  65. <div>
  66. <el-text tag="b">风格</el-text>
  67. </div>
  68. <el-space wrap class="group-item-body">
  69. <el-select v-model="stylePreset" placeholder="Select" size="large" class="!w-350px">
  70. <el-option
  71. v-for="item in StableDiffusionStylePresets"
  72. :key="item.key"
  73. :label="item.name"
  74. :value="item.key"
  75. />
  76. </el-select>
  77. </el-space>
  78. </div>
  79. <div class="group-item">
  80. <div>
  81. <el-text tag="b">图片尺寸</el-text>
  82. </div>
  83. <el-space wrap class="group-item-body">
  84. <el-input v-model="width" class="w-170px" placeholder="图片宽度" />
  85. <el-input v-model="height" class="w-170px" placeholder="图片高度" />
  86. </el-space>
  87. </div>
  88. <div class="group-item">
  89. <div>
  90. <el-text tag="b">迭代步数</el-text>
  91. </div>
  92. <el-space wrap class="group-item-body">
  93. <el-input
  94. v-model="steps"
  95. type="number"
  96. size="large"
  97. class="!w-350px"
  98. placeholder="Please input"
  99. />
  100. </el-space>
  101. </div>
  102. <div class="group-item">
  103. <div>
  104. <el-text tag="b">引导系数</el-text>
  105. </div>
  106. <el-space wrap class="group-item-body">
  107. <el-input
  108. v-model="scale"
  109. type="number"
  110. size="large"
  111. class="!w-350px"
  112. placeholder="Please input"
  113. />
  114. </el-space>
  115. </div>
  116. <div class="group-item">
  117. <div>
  118. <el-text tag="b">随机因子</el-text>
  119. </div>
  120. <el-space wrap class="group-item-body">
  121. <el-input
  122. v-model="seed"
  123. type="number"
  124. size="large"
  125. class="!w-350px"
  126. placeholder="Please input"
  127. />
  128. </el-space>
  129. </div>
  130. <div class="btns">
  131. <el-button
  132. type="primary"
  133. size="large"
  134. round
  135. :loading="drawIn"
  136. :disabled="prompt.length === 0"
  137. @click="handleGenerateImage"
  138. >
  139. {{ drawIn ? '生成中' : '生成内容' }}
  140. </el-button>
  141. </div>
  142. </template>
  143. <script setup lang="ts">
  144. import { ImageApi, ImageDrawReqVO, ImageVO } from '@/api/ai/image'
  145. import { hasChinese } from '@/views/ai/utils/utils'
  146. import {
  147. AiPlatformEnum,
  148. ImageHotEnglishWords,
  149. StableDiffusionClipGuidancePresets,
  150. StableDiffusionSamplers,
  151. StableDiffusionStylePresets
  152. } from '@/views/ai/utils/constants'
  153. import { ModelVO } from '@/api/ai/model/model'
  154. const message = useMessage() // 消息弹窗
  155. // 接收父组件传入的模型列表
  156. const props = defineProps({
  157. models: {
  158. type: Array<ModelVO>,
  159. default: () => [] as ModelVO[]
  160. }
  161. })
  162. const emits = defineEmits(['onDrawStart', 'onDrawComplete']) // 定义 emits
  163. // 定义属性
  164. const drawIn = ref<boolean>(false) // 生成中
  165. const selectHotWord = ref<string>('') // 选中的热词
  166. // 表单
  167. const prompt = ref<string>('') // 提示词
  168. const width = ref<number>(512) // 图片宽度
  169. const height = ref<number>(512) // 图片高度
  170. const sampler = ref<string>('DDIM') // 采样方法
  171. const steps = ref<number>(20) // 迭代步数
  172. const seed = ref<number>(42) // 控制生成图像的随机性
  173. const scale = ref<number>(7.5) // 引导系数
  174. const clipGuidancePreset = ref<string>('NONE') // 文本提示相匹配的图像(clip_guidance_preset) 简称 CLIP
  175. const stylePreset = ref<string>('3d-model') // 风格
  176. /** 选择热词 */
  177. const handleHotWordClick = async (hotWord: string) => {
  178. // 情况一:取消选中
  179. if (selectHotWord.value == hotWord) {
  180. selectHotWord.value = ''
  181. return
  182. }
  183. // 情况二:选中
  184. selectHotWord.value = hotWord // 选中
  185. prompt.value = hotWord // 替换提示词
  186. }
  187. /** 图片生成 */
  188. const handleGenerateImage = async () => {
  189. // 从 models 中查找匹配的模型
  190. const selectModel = 'stable-diffusion-v1-6'
  191. const matchedModel = props.models.find(
  192. (item) => item.model === selectModel && item.platform === AiPlatformEnum.STABLE_DIFFUSION
  193. )
  194. if (!matchedModel) {
  195. message.error('该模型不可用,请选择其它模型')
  196. return
  197. }
  198. // 二次确认
  199. if (hasChinese(prompt.value)) {
  200. message.alert('暂不支持中文!')
  201. return
  202. }
  203. await message.confirm(`确认生成内容?`)
  204. try {
  205. // 加载中
  206. drawIn.value = true
  207. // 回调
  208. emits('onDrawStart', AiPlatformEnum.STABLE_DIFFUSION)
  209. // 发送请求
  210. const form = {
  211. modelId: matchedModel.id,
  212. prompt: prompt.value, // 提示词
  213. width: width.value, // 图片宽度
  214. height: height.value, // 图片高度
  215. options: {
  216. seed: seed.value, // 随机种子
  217. steps: steps.value, // 图片生成步数
  218. scale: scale.value, // 引导系数
  219. sampler: sampler.value, // 采样算法
  220. clipGuidancePreset: clipGuidancePreset.value, // 文本提示相匹配的图像 CLIP
  221. stylePreset: stylePreset.value // 风格
  222. }
  223. } as ImageDrawReqVO
  224. await ImageApi.drawImage(form)
  225. } finally {
  226. // 回调
  227. emits('onDrawComplete', AiPlatformEnum.STABLE_DIFFUSION)
  228. // 加载结束
  229. drawIn.value = false
  230. }
  231. }
  232. /** 填充值 */
  233. const settingValues = async (detail: ImageVO) => {
  234. prompt.value = detail.prompt
  235. width.value = detail.width
  236. height.value = detail.height
  237. seed.value = detail.options?.seed
  238. steps.value = detail.options?.steps
  239. scale.value = detail.options?.scale
  240. sampler.value = detail.options?.sampler
  241. clipGuidancePreset.value = detail.options?.clipGuidancePreset
  242. stylePreset.value = detail.options?.stylePreset
  243. }
  244. /** 暴露组件方法 */
  245. defineExpose({ settingValues })
  246. </script>
  247. <style scoped lang="scss">
  248. // 提示词
  249. .prompt {
  250. }
  251. // 热词
  252. .hot-words {
  253. display: flex;
  254. flex-direction: column;
  255. margin-top: 30px;
  256. .word-list {
  257. display: flex;
  258. flex-direction: row;
  259. flex-wrap: wrap;
  260. justify-content: start;
  261. margin-top: 15px;
  262. .btn {
  263. margin: 0;
  264. }
  265. }
  266. }
  267. // 模型
  268. .group-item {
  269. margin-top: 30px;
  270. .group-item-body {
  271. margin-top: 15px;
  272. width: 100%;
  273. }
  274. }
  275. .btns {
  276. display: flex;
  277. justify-content: center;
  278. margin-top: 50px;
  279. }
  280. </style>