index.vue 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  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="model">
  35. <div>
  36. <el-text tag="b">模型选择</el-text>
  37. </div>
  38. <el-space wrap class="model-list">
  39. <div
  40. :class="selectModel === model ? 'modal-item selectModel' : 'modal-item'"
  41. v-for="model in models"
  42. :key="model.key"
  43. >
  44. <el-image
  45. :src="model.image"
  46. fit="contain"
  47. @click="handlerModelClick(model)"
  48. />
  49. <div class="model-font">{{model.name}}</div>
  50. </div>
  51. </el-space>
  52. </div>
  53. <div class="image-style">
  54. <div>
  55. <el-text tag="b">风格选择</el-text>
  56. </div>
  57. <el-space wrap class="image-style-list">
  58. <div
  59. :class="selectImageStyle === imageStyle ? 'image-style-item selectImageStyle' : 'image-style-item'"
  60. v-for="imageStyle in imageStyleList"
  61. :key="imageStyle.key"
  62. >
  63. <el-image
  64. :src="imageStyle.image"
  65. fit="contain"
  66. @click="handlerStyleClick(imageStyle)"
  67. />
  68. <div class="style-font">{{imageStyle.name}}</div>
  69. </div>
  70. </el-space>
  71. </div>
  72. <div class="image-size">
  73. <div>
  74. <el-text tag="b">画面比例</el-text>
  75. </div>
  76. <el-space wrap class="size-list">
  77. <div class="size-item"
  78. v-for="imageSize in imageSizeList"
  79. :key="imageSize.key"
  80. @click="handlerSizeClick(imageSize)">
  81. <div :class="selectImageSize === imageSize ? 'size-wrapper selectImageSize' : 'size-wrapper'">
  82. <div :style="imageSize.style"></div>
  83. </div>
  84. <div class="size-font">{{ imageSize.name }}</div>
  85. </div>
  86. </el-space>
  87. </div>
  88. <div class="btns">
  89. <el-button type="primary"
  90. size="large"
  91. round
  92. :loading="drawIn"
  93. @click="handlerGenerateImage">
  94. {{drawIn ? '生成中' : '生成内容'}}
  95. </el-button>
  96. </div>
  97. </template>
  98. <script setup lang="ts">
  99. import {ImageApi, ImageDrawReqVO} from '@/api/ai/image';
  100. // image 模型
  101. interface ImageModelVO {
  102. key: string
  103. name: string
  104. image: string
  105. }
  106. // image 大小
  107. interface ImageSizeVO {
  108. key: string
  109. name: string,
  110. style: string,
  111. width: string,
  112. height: string,
  113. }
  114. // 定义属性
  115. const prompt = ref<string>('') // 提示词
  116. const drawIn = ref<boolean>(false) // 生成中
  117. const selectHotWord = ref<string>('') // 选中的热词
  118. const hotWords = ref<string[]>(['中国旗袍', '古装美女', '卡通头像', '机甲战士', '童话小屋', '中国长城']) // 热词
  119. const selectModel = ref<any>({}) // 模型
  120. // TODO @fan:image 改成项目里自己的哈
  121. // TODO @fan:这个 image,要不看看网上有没合适的图片,作为占位符,啊哈哈
  122. const models = ref<ImageModelVO[]>([
  123. {
  124. key: 'dall-e-3',
  125. name: 'DALL·E 3',
  126. image: 'https://h5.cxyhub.com/images/model_2.png',
  127. },
  128. {
  129. key: 'dall-e-2',
  130. name: 'DALL·E 2',
  131. image: 'https://h5.cxyhub.com/images/model_1.png',
  132. },
  133. ]) // 模型
  134. selectModel.value = models.value[0]
  135. const selectImageStyle = ref<any>({}) // style 样式
  136. // TODO @fan:image 改成项目里自己的哈
  137. const imageStyleList = ref<ImageModelVO[]>([
  138. {
  139. key: 'vivid',
  140. name: '清晰',
  141. image: 'https://h5.cxyhub.com/images/model_1.png',
  142. },
  143. {
  144. key: 'natural',
  145. name: '自然',
  146. image: 'https://h5.cxyhub.com/images/model_2.png',
  147. },
  148. ]) // style
  149. selectImageStyle.value = imageStyleList.value[0]
  150. const selectImageSize = ref<ImageSizeVO>({} as ImageSizeVO) // 选中 size
  151. const imageSizeList = ref<ImageSizeVO[]>([
  152. {
  153. key: '1024x1024',
  154. name: '1:1',
  155. width: '1024',
  156. height: '1024',
  157. style: 'width: 30px; height: 30px;background-color: #dcdcdc;',
  158. },
  159. {
  160. key: '1024x1792',
  161. name: '3:5',
  162. width: '1024',
  163. height: '1792',
  164. style: 'width: 30px; height: 50px;background-color: #dcdcdc;',
  165. },
  166. {
  167. key: '1792x1024',
  168. name: '5:3',
  169. width: '1792',
  170. height: '1024',
  171. style: 'width: 50px; height: 30px;background-color: #dcdcdc;',
  172. }
  173. ]) // size
  174. selectImageSize.value = imageSizeList.value[0]
  175. // 定义 Props
  176. const props = defineProps({})
  177. // 定义 emits
  178. const emits = defineEmits(['onDrawStart', 'onDrawComplete'])
  179. // TODO @fan:如果是简单注释,建议用 /** */,主要是现在项目里是这种风格哈,保持一致好点~
  180. // TODO @fan:handler 应该改成 handle 哈
  181. /** 热词 - click */
  182. const handlerHotWordClick = async (hotWord: string) => {
  183. // 取消选中
  184. if (selectHotWord.value == hotWord) {
  185. selectHotWord.value = ''
  186. return
  187. }
  188. // 选中
  189. selectHotWord.value = hotWord
  190. // 替换提示词
  191. prompt.value = hotWord
  192. }
  193. /** 模型 - click */
  194. const handlerModelClick = async (model: ImageModelVO) => {
  195. if (selectModel.value === model) {
  196. selectModel.value = {} as ImageModelVO
  197. return
  198. }
  199. selectModel.value = model
  200. }
  201. /** 样式 - click */
  202. const handlerStyleClick = async (imageStyle: ImageModelVO) => {
  203. if (selectImageStyle.value === imageStyle) {
  204. selectImageStyle.value = {} as ImageModelVO
  205. return
  206. }
  207. selectImageStyle.value = imageStyle
  208. }
  209. /** size - click */
  210. const handlerSizeClick = async (imageSize: ImageSizeVO) => {
  211. if (selectImageSize.value === imageSize) {
  212. selectImageSize.value = {} as ImageSizeVO
  213. return
  214. }
  215. selectImageSize.value = imageSize
  216. }
  217. /** 图片生产 */
  218. const handlerGenerateImage = async () => {
  219. try {
  220. // 加载中
  221. drawIn.value = true
  222. // 回调
  223. emits('onDrawStart', selectModel.value.key)
  224. const form = {
  225. platform: 'OpenAI',
  226. prompt: prompt.value, // 提示词
  227. model: selectModel.value.key, // 模型
  228. width: selectImageSize.value.width, // size 不能为空
  229. height: selectImageSize.value.height, // size 不能为空
  230. options: {
  231. style: selectImageStyle.value.key, // 图像生成的风格
  232. }
  233. } as ImageDrawReqVO
  234. // 发送请求
  235. await ImageApi.drawImage(form)
  236. } finally {
  237. // 回调
  238. emits('onDrawComplete', selectModel.value.key)
  239. // 加载结束
  240. drawIn.value = false
  241. }
  242. }
  243. </script>
  244. <style scoped lang="scss">
  245. // 提示词
  246. .prompt {
  247. }
  248. // 热词
  249. .hot-words {
  250. display: flex;
  251. flex-direction: column;
  252. margin-top: 30px;
  253. .word-list {
  254. display: flex;
  255. flex-direction: row;
  256. flex-wrap: wrap;
  257. justify-content: start;
  258. margin-top: 15px;
  259. .btn {
  260. margin: 0;
  261. }
  262. }
  263. }
  264. // 模型
  265. .model {
  266. margin-top: 30px;
  267. .model-list {
  268. margin-top: 15px;
  269. .modal-item {
  270. width: 110px;
  271. //outline: 1px solid blue;
  272. overflow: hidden;
  273. display: flex;
  274. flex-direction: column;
  275. align-items: center;
  276. border: 3px solid transparent;
  277. cursor: pointer;
  278. .model-font {
  279. font-size: 14px;
  280. color: #3e3e3e;
  281. font-weight: bold;
  282. }
  283. }
  284. .selectModel {
  285. border: 3px solid #1293ff;
  286. border-radius: 5px;
  287. }
  288. }
  289. }
  290. // 样式 style
  291. .image-style {
  292. margin-top: 30px;
  293. .image-style-list {
  294. margin-top: 15px;
  295. .image-style-item {
  296. width: 110px;
  297. //outline: 1px solid blue;
  298. overflow: hidden;
  299. display: flex;
  300. flex-direction: column;
  301. align-items: center;
  302. border: 3px solid transparent;
  303. cursor: pointer;
  304. .style-font {
  305. font-size: 14px;
  306. color: #3e3e3e;
  307. font-weight: bold;
  308. }
  309. }
  310. .selectImageStyle {
  311. border: 3px solid #1293ff;
  312. border-radius: 5px;
  313. }
  314. }
  315. }
  316. // 尺寸
  317. .image-size {
  318. width: 100%;
  319. margin-top: 30px;
  320. .size-list {
  321. display: flex;
  322. flex-direction: row;
  323. justify-content: space-between;
  324. width: 100%;
  325. margin-top: 20px;
  326. .size-item {
  327. display: flex;
  328. flex-direction: column;
  329. align-items: center;
  330. cursor: pointer;
  331. .size-wrapper {
  332. display: flex;
  333. flex-direction: column;
  334. align-items: center;
  335. justify-content: center;
  336. border-radius: 7px;
  337. padding: 4px;
  338. width: 50px;
  339. height: 50px;
  340. background-color: #fff;
  341. border: 1px solid #fff;
  342. }
  343. .size-font {
  344. font-size: 14px;
  345. color: #3e3e3e;
  346. font-weight: bold;
  347. }
  348. }
  349. }
  350. .selectImageSize {
  351. border: 1px solid #1293ff !important;
  352. }
  353. }
  354. .btns {
  355. display: flex;
  356. justify-content: center;
  357. margin-top: 50px;
  358. }
  359. </style>