feature-mod-index.vue 63 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311
  1. <template>
  2. <div class="design-agent-block">
  3. <symbol id="icon-aiyoujiantou" viewBox="0 0 1024 1024"><path d="M350.165333 97.834667a42.666667 42.666667 0 0 0-60.330666 60.330666L643.626667 512 289.834667 865.834667a42.666667 42.666667 0 0 0-2.496 57.621333l2.496 2.709333a42.666667 42.666667 0 0 0 60.330666 0l384-384a42.666667 42.666667 0 0 0 0-60.330666l-384-384z"></path></symbol>
  4. <div class="tool-direct-black">
  5. <div class="tools-header-block">
  6. <header class="header-block">
  7. <h1>
  8. <svg
  9. xmlns="http://www.w3.org/2000/svg"
  10. width="24"
  11. height="24"
  12. viewBox="0 0 24 24"
  13. fill="none"
  14. stroke="currentColor"
  15. stroke-width="2"
  16. stroke-linecap="round"
  17. stroke-linejoin="round"
  18. ><path d="M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z" /><path d="M20 3v4" /><path d="M22 5h-4" /><path d="M4 17v2" /><path d="M5 18H3" /></svg>
  19. {{ pagesData.title }}
  20. </h1>
  21. <p>
  22. {{ pagesData.desc }}
  23. </p>
  24. </header>
  25. </div>
  26. <div class="tool-direct-content-black">
  27. <!-- <div class="chat-block" v-if="pages == 'chat'">
  28. <iframe src="https://aids.gloria.com.cn:8443/chat/Q2zXZ4uwp3yN4Tmn" frameborder="0"></iframe>
  29. </div> -->
  30. <div class="tools-block">
  31. <div class="tool-direct-content">
  32. <div class="tool-direct-left">
  33. <!-- <div class="tool-direct-left__title">
  34. <svg
  35. xmlns="http://www.w3.org/2000/svg"
  36. width="24"
  37. height="24"
  38. viewBox="0 0 24 24"
  39. fill="none"
  40. stroke="currentColor"
  41. stroke-width="2"
  42. stroke-linecap="round"
  43. stroke-linejoin="round"
  44. ><path d="M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z" /><path d="M20 3v4" /><path d="M22 5h-4" /><path d="M4 17v2" /><path d="M5 18H3" /></svg>
  45. <h3>{{ pagesData.title }}</h3>
  46. </div>
  47. <div class="tool-direct-left__desc">{{ pagesData.desc }}</div> -->
  48. <el-form :model="ruleForm" :rules="rules" ref="ruleForm" class="demo-ruleForm" label-position="top">
  49. <div class="ruleFormContent">
  50. <el-scrollbar>
  51. <!-- 图生图 -->
  52. <el-form-item class="batch-form" v-if="pagesData.applicationId == 2">
  53. <template #label>
  54. <div class="label-title">
  55. <span>批量</span>
  56. </div>
  57. <el-switch v-model="ruleForm.isBatch" @change="handleBatch"></el-switch>
  58. </template>
  59. </el-form-item>
  60. <el-form-item class="images-group-form" prop="editableTabs" required v-if="ruleForm.isBatch">
  61. <template #label>
  62. <div class="label-title">
  63. <span><em class="no">1</em> 上衣图片</span>
  64. </div>
  65. </template>
  66. <div style="margin-bottom: 10px;display:flex;justify-content: flex-end;">
  67. <el-button
  68. size="small"
  69. @click="addTab(ruleForm.editableTabsValue)"
  70. >
  71. 添加上衣图片项
  72. </el-button>
  73. </div>
  74. <el-tabs v-model="ruleForm.editableTabsValue" type="card" closable @tab-remove="removeTab" @tab-click="clickTabs">
  75. <el-tab-pane
  76. v-for="(item, parentIndex) in ruleForm.editableTabs"
  77. :key="item.name"
  78. :label="`图片组 ${parentIndex+1}`"
  79. :name="item.name"
  80. >
  81. <div class="tools-upload">
  82. <!-- 自定义显示每个图片的重新上传按钮 -->
  83. <div class="swiper-container-block" v-if="item.content.length">
  84. <div class="swiper-container my-swiper" :ref="`swiperContainer-${parentIndex}`">
  85. <draggable
  86. tag="div"
  87. class="swiper-wrapper"
  88. :list="item.content"
  89. :options="{ animation: 150 }"
  90. >
  91. <div class="swiper-slide" v-for="(group, index) in item.content" :key="index">
  92. <div class="img-group">
  93. <div class="custom-file-list" >
  94. <div class="picture-card-wrapper">
  95. <img :src="group" class="picture-card-image" />
  96. <div class="custom-file__btns">
  97. <el-button
  98. class="replace-btn"
  99. @click="replaceFileGroup(index, parentIndex)">
  100. <svg t="1760412251487" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6466" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M902.314667 206.741333a21.333333 21.333333 0 0 1 0 30.122667l-46.933334 47.146667-122.581333 123.178666a21.333333 21.333333 0 0 1-30.229333 0l-31.829334-31.957333a21.333333 21.333333 0 0 1 0-30.122667l42.666667-42.816c13.376-13.44 3.84-36.394667-15.146667-36.394666H375.189333c-46.933333 0-91.157333 18.389333-124.373333 51.733333a175.914667 175.914667 0 0 0-51.52 124.885333v91.712a21.333333 21.333333 0 0 1-21.333333 21.333334H132.970667a21.333333 21.333333 0 0 1-21.333334-21.333334v-91.818666c0-146.090667 118.037333-264.618667 263.530667-264.618667h323.008c18.986667 0 28.522667-22.933333 15.125333-36.373333l-42.666666-42.837334a21.333333 21.333333 0 0 1 0-30.101333l31.829333-31.978667a21.333333 21.333333 0 0 1 30.250667 0l122.581333 123.093334 47.018667 47.146666z m-97.941334 353.706667c0 47.146667-18.325333 91.52-51.541333 124.885333a174.464 174.464 0 0 1-124.373333 51.733334H325.653333c-18.986667 0-28.522667-22.954667-15.125333-36.394667l42.752-42.922667a21.333333 21.333333 0 0 0 0-30.122666l-31.829333-31.957334a21.333333 21.333333 0 0 0-30.229334 0l-122.602666 123.008-46.933334 47.146667a21.333333 21.333333 0 0 0 0 30.08l46.933334 47.146667 122.602666 123.093333a21.333333 21.333333 0 0 0 30.229334 0l31.829333-31.957333a21.333333 21.333333 0 0 0 0-30.101334l-42.453333-42.624c-13.397333-13.44-3.861333-36.394667 15.104-36.394666H628.48c145.493333 0 263.530667-118.528 263.530667-264.618667v-90.538667a21.333333 21.333333 0 0 0-21.333334-21.333333h-44.970666a21.333333 21.333333 0 0 0-21.333334 21.333333v90.538667z" p-id="6467" fill="#ffffff"></path></svg>
  101. 重新上传
  102. </el-button>
  103. <el-button
  104. class="replace-btn"
  105. @click="imgUploadDelGroup(index, parentIndex)">
  106. <svg t="1760412304801" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7509" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M853.333333 256H170.666667V853.333333a85.333333 85.333333 0 0 0 85.333333 85.333334h512A85.333333 85.333333 0 0 0 853.333333 853.333333V256zM85.333333 170.666667h170.666667V85.333333A85.333333 85.333333 0 0 1 341.333333 0h341.333334a85.333333 85.333333 0 0 1 85.333333 85.333333V170.666667h213.333333a42.666667 42.666667 0 0 1 0 85.333333H938.666667V853.333333a170.666667 170.666667 0 0 1-170.666667 170.666667h-512a170.666667 170.666667 0 0 1-170.666667-170.666667V256H42.666667a42.666667 42.666667 0 1 1 0-85.333333H85.333333zM341.333333 170.666667h341.333334V85.333333H341.333333V170.666667z m42.666667 256c23.552 0 42.666667 19.114667 42.666667 42.666666v256a42.666667 42.666667 0 0 1-85.333334 0v-256c0-23.552 19.114667-42.666667 42.666667-42.666666z m256 0c23.552 0 42.666667 19.114667 42.666667 42.666666v256a42.666667 42.666667 0 0 1-85.333334 0v-256c0-23.552 19.114667-42.666667 42.666667-42.666666z" fill="#ffffff" p-id="7510"></path></svg>
  107. 删除图片
  108. </el-button>
  109. </div>
  110. </div>
  111. </div>
  112. </div>
  113. </div>
  114. </draggable>
  115. <!-- 左右箭头 -->
  116. <div class="swiper-button-prev swiper-button">
  117. <svg t="1760505375902" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10647" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M307.6 104.6c-14.2 14.2-14.2 37.2 0 51.4L655 503.4c2.8 2.9 2.8 7.5 0 10.3L307.6 861.2c-14.2 14.2-14.2 37.2 0 51.4 14.2 14.2 37.2 14.2 51.4 0l347.4-347.4c15.6-15.6 23.4-36 23.4-56.5s-7.8-41-23.4-56.5L359 104.6c-14.2-14.2-37.2-14.2-51.4 0z" p-id="10648"></path></svg>
  118. </div>
  119. <div class="swiper-button-next swiper-button">
  120. <svg t="1760505375902" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10647" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M307.6 104.6c-14.2 14.2-14.2 37.2 0 51.4L655 503.4c2.8 2.9 2.8 7.5 0 10.3L307.6 861.2c-14.2 14.2-14.2 37.2 0 51.4 14.2 14.2 37.2 14.2 51.4 0l347.4-347.4c15.6-15.6 23.4-36 23.4-56.5s-7.8-41-23.4-56.5L359 104.6c-14.2-14.2-37.2-14.2-51.4 0z" p-id="10648"></path></svg>
  121. </div>
  122. </div>
  123. </div>
  124. <el-upload
  125. :ref="`uploadRef-${parentIndex}`"
  126. class="upload-demo"
  127. :class="{'upload-demo__data': item.content.length}"
  128. drag
  129. :action="action"
  130. :show-file-list="false"
  131. :headers="{
  132. 'Authorization': 'Bearer ' + token
  133. }"
  134. multiple
  135. :on-success="handleVideoSuccessGroup"
  136. :before-upload="beforeUploadVideo"
  137. >
  138. <div v-loading="uploading">
  139. <template v-if="item.content.length">
  140. <i class="el-icon-plus"></i>
  141. <el-button type="primary" size="mini" @click.stop="handleProductRepo('tabs')">产品库</el-button>
  142. </template>
  143. <div class="el-upload__info" v-else>
  144. <i class="el-icon-upload" style="margin: 0;"></i>
  145. <div class="el-upload__text">点击或将图片拖拽到这里上传</div>
  146. <div class="el-upload__tip" slot="tip">图片要求:支持JPG、PNG和WEBP,最大为20M</div>
  147. <div class="el-upload__btn">
  148. <el-button type="primary" size="small" @click.stop="handleProductRepo('tabs')">从产品库选择</el-button>
  149. </div>
  150. </div>
  151. </div>
  152. </el-upload>
  153. </div>
  154. <span class="tips" v-if="item.content.length">拖动图片改变图片位置</span>
  155. <div class="tools-sku">
  156. SKU <el-input v-model="item.sku" size="small" placeholder="请输入sku"></el-input>
  157. </div>
  158. </el-tab-pane>
  159. </el-tabs>
  160. </el-form-item>
  161. <el-form-item prop="imageList" required v-if="pagesData.applicationId == 2">
  162. <template #label>
  163. <div class="label-title">
  164. <span><em class="no">{{ ruleForm.isBatch ? 2 : 1 }}</em> {{ ruleForm.isBatch ? '上传姿势图' : '上传图片' }}</span>
  165. </div>
  166. </template>
  167. <div class="tools-upload">
  168. <!-- 自定义显示每个图片的重新上传按钮 -->
  169. <div class="swiper-container-block" v-if="ruleForm.imageList.length">
  170. <div class="swiper-container my-swiper" ref="swiperContainer">
  171. <draggable
  172. tag="div"
  173. class="swiper-wrapper"
  174. :list="ruleForm.imageList"
  175. :options="{ animation: 150 }"
  176. >
  177. <div class="swiper-slide" v-for="(group, index) in ruleForm.imageList" :key="index">
  178. <div class="img-group">
  179. <div class="custom-file-list" >
  180. <div class="picture-card-wrapper">
  181. <img :src="group" class="picture-card-image" />
  182. <div class="custom-file__btns">
  183. <el-button
  184. class="replace-btn"
  185. @click="replaceFile(index)">
  186. <svg t="1760412251487" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6466" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M902.314667 206.741333a21.333333 21.333333 0 0 1 0 30.122667l-46.933334 47.146667-122.581333 123.178666a21.333333 21.333333 0 0 1-30.229333 0l-31.829334-31.957333a21.333333 21.333333 0 0 1 0-30.122667l42.666667-42.816c13.376-13.44 3.84-36.394667-15.146667-36.394666H375.189333c-46.933333 0-91.157333 18.389333-124.373333 51.733333a175.914667 175.914667 0 0 0-51.52 124.885333v91.712a21.333333 21.333333 0 0 1-21.333333 21.333334H132.970667a21.333333 21.333333 0 0 1-21.333334-21.333334v-91.818666c0-146.090667 118.037333-264.618667 263.530667-264.618667h323.008c18.986667 0 28.522667-22.933333 15.125333-36.373333l-42.666666-42.837334a21.333333 21.333333 0 0 1 0-30.101333l31.829333-31.978667a21.333333 21.333333 0 0 1 30.250667 0l122.581333 123.093334 47.018667 47.146666z m-97.941334 353.706667c0 47.146667-18.325333 91.52-51.541333 124.885333a174.464 174.464 0 0 1-124.373333 51.733334H325.653333c-18.986667 0-28.522667-22.954667-15.125333-36.394667l42.752-42.922667a21.333333 21.333333 0 0 0 0-30.122666l-31.829333-31.957334a21.333333 21.333333 0 0 0-30.229334 0l-122.602666 123.008-46.933334 47.146667a21.333333 21.333333 0 0 0 0 30.08l46.933334 47.146667 122.602666 123.093333a21.333333 21.333333 0 0 0 30.229334 0l31.829333-31.957333a21.333333 21.333333 0 0 0 0-30.101334l-42.453333-42.624c-13.397333-13.44-3.861333-36.394667 15.104-36.394666H628.48c145.493333 0 263.530667-118.528 263.530667-264.618667v-90.538667a21.333333 21.333333 0 0 0-21.333334-21.333333h-44.970666a21.333333 21.333333 0 0 0-21.333334 21.333333v90.538667z" p-id="6467" fill="#ffffff"></path></svg>
  187. 重新上传
  188. </el-button>
  189. <el-button
  190. class="replace-btn"
  191. @click="imgUploadDel(index)">
  192. <svg t="1760412304801" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7509" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M853.333333 256H170.666667V853.333333a85.333333 85.333333 0 0 0 85.333333 85.333334h512A85.333333 85.333333 0 0 0 853.333333 853.333333V256zM85.333333 170.666667h170.666667V85.333333A85.333333 85.333333 0 0 1 341.333333 0h341.333334a85.333333 85.333333 0 0 1 85.333333 85.333333V170.666667h213.333333a42.666667 42.666667 0 0 1 0 85.333333H938.666667V853.333333a170.666667 170.666667 0 0 1-170.666667 170.666667h-512a170.666667 170.666667 0 0 1-170.666667-170.666667V256H42.666667a42.666667 42.666667 0 1 1 0-85.333333H85.333333zM341.333333 170.666667h341.333334V85.333333H341.333333V170.666667z m42.666667 256c23.552 0 42.666667 19.114667 42.666667 42.666666v256a42.666667 42.666667 0 0 1-85.333334 0v-256c0-23.552 19.114667-42.666667 42.666667-42.666666z m256 0c23.552 0 42.666667 19.114667 42.666667 42.666666v256a42.666667 42.666667 0 0 1-85.333334 0v-256c0-23.552 19.114667-42.666667 42.666667-42.666666z" fill="#ffffff" p-id="7510"></path></svg>
  193. 删除图片
  194. </el-button>
  195. </div>
  196. </div>
  197. </div>
  198. </div>
  199. </div>
  200. </draggable>
  201. <!-- 左右箭头 -->
  202. <div class="swiper-button-prev swiper-button">
  203. <svg t="1760505375902" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10647" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M307.6 104.6c-14.2 14.2-14.2 37.2 0 51.4L655 503.4c2.8 2.9 2.8 7.5 0 10.3L307.6 861.2c-14.2 14.2-14.2 37.2 0 51.4 14.2 14.2 37.2 14.2 51.4 0l347.4-347.4c15.6-15.6 23.4-36 23.4-56.5s-7.8-41-23.4-56.5L359 104.6c-14.2-14.2-37.2-14.2-51.4 0z" p-id="10648"></path></svg>
  204. </div>
  205. <div class="swiper-button-next swiper-button">
  206. <svg t="1760505375902" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10647" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M307.6 104.6c-14.2 14.2-14.2 37.2 0 51.4L655 503.4c2.8 2.9 2.8 7.5 0 10.3L307.6 861.2c-14.2 14.2-14.2 37.2 0 51.4 14.2 14.2 37.2 14.2 51.4 0l347.4-347.4c15.6-15.6 23.4-36 23.4-56.5s-7.8-41-23.4-56.5L359 104.6c-14.2-14.2-37.2-14.2-51.4 0z" p-id="10648"></path></svg>
  207. </div>
  208. </div>
  209. </div>
  210. <el-upload
  211. ref="uploadRef"
  212. class="upload-demo"
  213. :class="{'upload-demo__data': ruleForm.imageList.length}"
  214. drag
  215. :action="action"
  216. :show-file-list="false"
  217. :headers="{
  218. 'Authorization': 'Bearer ' + token
  219. }"
  220. multiple
  221. :on-success="handleVideoSuccess"
  222. :before-upload="beforeUploadVideo"
  223. >
  224. <div v-loading="uploading">
  225. <template v-if="ruleForm.imageList.length">
  226. <i class="el-icon-plus"></i>
  227. <el-button type="primary" size="mini" @click.stop="handleProductRepo">产品库</el-button>
  228. </template>
  229. <div class="el-upload__info" v-else>
  230. <i class="el-icon-upload" style="margin: 0;"></i>
  231. <div class="el-upload__text">点击或将图片拖拽到这里上传,最多上传 5 张图片</div>
  232. <div class="el-upload__tip" slot="tip">图片要求:支持JPG、PNG和WEBP,最大为20M</div>
  233. <div class="el-upload__btn">
  234. <el-button type="primary" size="small" @click.stop="handleProductRepo">从产品库选择</el-button>
  235. </div>
  236. </div>
  237. </div>
  238. </el-upload>
  239. </div>
  240. <span class="tips" v-if="ruleForm.imageList.length">拖动图片改变图片位置</span>
  241. </el-form-item>
  242. <el-form-item prop="imageUrl" required v-if="pagesData.applicationId == 3">
  243. <template #label>
  244. <div class="label-title">
  245. <span><em class="no">1</em> 上传图片</span>
  246. </div>
  247. </template>
  248. <div class="tools-upload">
  249. <el-upload
  250. class="upload-demo"
  251. drag
  252. :action="action"
  253. :show-file-list="false"
  254. :headers="{
  255. 'Authorization': 'Bearer ' + token
  256. }"
  257. multiple
  258. :on-success="handleVideoSuccess"
  259. :before-upload="beforeUploadVideo">
  260. <div v-loading="uploading">
  261. <div v-if="ruleForm.imageUrl" class="avatar-black">
  262. <img :src="ruleForm.imageUrl" class="avatar">
  263. <div class="custom-file__btns">
  264. <el-button
  265. class="replace-btn"
  266. @click="replaceFile()">
  267. <svg t="1760412251487" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6466" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M902.314667 206.741333a21.333333 21.333333 0 0 1 0 30.122667l-46.933334 47.146667-122.581333 123.178666a21.333333 21.333333 0 0 1-30.229333 0l-31.829334-31.957333a21.333333 21.333333 0 0 1 0-30.122667l42.666667-42.816c13.376-13.44 3.84-36.394667-15.146667-36.394666H375.189333c-46.933333 0-91.157333 18.389333-124.373333 51.733333a175.914667 175.914667 0 0 0-51.52 124.885333v91.712a21.333333 21.333333 0 0 1-21.333333 21.333334H132.970667a21.333333 21.333333 0 0 1-21.333334-21.333334v-91.818666c0-146.090667 118.037333-264.618667 263.530667-264.618667h323.008c18.986667 0 28.522667-22.933333 15.125333-36.373333l-42.666666-42.837334a21.333333 21.333333 0 0 1 0-30.101333l31.829333-31.978667a21.333333 21.333333 0 0 1 30.250667 0l122.581333 123.093334 47.018667 47.146666z m-97.941334 353.706667c0 47.146667-18.325333 91.52-51.541333 124.885333a174.464 174.464 0 0 1-124.373333 51.733334H325.653333c-18.986667 0-28.522667-22.954667-15.125333-36.394667l42.752-42.922667a21.333333 21.333333 0 0 0 0-30.122666l-31.829333-31.957334a21.333333 21.333333 0 0 0-30.229334 0l-122.602666 123.008-46.933334 47.146667a21.333333 21.333333 0 0 0 0 30.08l46.933334 47.146667 122.602666 123.093333a21.333333 21.333333 0 0 0 30.229334 0l31.829333-31.957333a21.333333 21.333333 0 0 0 0-30.101334l-42.453333-42.624c-13.397333-13.44-3.861333-36.394667 15.104-36.394666H628.48c145.493333 0 263.530667-118.528 263.530667-264.618667v-90.538667a21.333333 21.333333 0 0 0-21.333334-21.333333h-44.970666a21.333333 21.333333 0 0 0-21.333334 21.333333v90.538667z" p-id="6467" fill="#ffffff"></path></svg>
  268. 重新上传
  269. </el-button>
  270. <el-button
  271. class="replace-btn"
  272. @click.stop="imgUploadDel()">
  273. <svg t="1760412304801" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7509" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M853.333333 256H170.666667V853.333333a85.333333 85.333333 0 0 0 85.333333 85.333334h512A85.333333 85.333333 0 0 0 853.333333 853.333333V256zM85.333333 170.666667h170.666667V85.333333A85.333333 85.333333 0 0 1 341.333333 0h341.333334a85.333333 85.333333 0 0 1 85.333333 85.333333V170.666667h213.333333a42.666667 42.666667 0 0 1 0 85.333333H938.666667V853.333333a170.666667 170.666667 0 0 1-170.666667 170.666667h-512a170.666667 170.666667 0 0 1-170.666667-170.666667V256H42.666667a42.666667 42.666667 0 1 1 0-85.333333H85.333333zM341.333333 170.666667h341.333334V85.333333H341.333333V170.666667z m42.666667 256c23.552 0 42.666667 19.114667 42.666667 42.666666v256a42.666667 42.666667 0 0 1-85.333334 0v-256c0-23.552 19.114667-42.666667 42.666667-42.666666z m256 0c23.552 0 42.666667 19.114667 42.666667 42.666666v256a42.666667 42.666667 0 0 1-85.333334 0v-256c0-23.552 19.114667-42.666667 42.666667-42.666666z" fill="#ffffff" p-id="7510"></path></svg>
  274. 删除图片
  275. </el-button>
  276. </div>
  277. </div>
  278. <div class="el-upload__info" v-else>
  279. <i class="el-icon-upload"></i>
  280. <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
  281. <div class="el-upload__tip" slot="tip">图片要求:支持JPG、PNG和WEBP,最大为20M</div>
  282. </div>
  283. </div>
  284. </el-upload>
  285. </div>
  286. </el-form-item>
  287. <el-form-item prop="screenDescription" class="tool-direct-left__submit">
  288. <template #label>
  289. <div class="label-title">
  290. <span>
  291. <em class="no">{{pagesData.applicationId == 1 ? '1' : (ruleForm.isBatch ? '3' : '2')}}</em> 画面描述{{ pagesData.applicationId == 3 ? '(选填)' : '' }}
  292. </span>
  293. <el-tooltip
  294. class="item"
  295. effect="dark"
  296. :content="pagesData.contentTips"
  297. placement="top"
  298. >
  299. <i class="el-icon-info" style="color: #ccc;cursor: pointer;" />
  300. </el-tooltip>
  301. </div>
  302. </template>
  303. <el-input
  304. type="textarea"
  305. :rows="5"
  306. :placeholder="ruleForm.isBatch ? '例如:把图2女士身上衣服替换到图1模特身上' : pagesData.placeholder"
  307. :maxlength="1000"
  308. show-word-limit
  309. v-model="ruleForm.screenDescription">
  310. </el-input>
  311. <span class="clear-block" v-if="ruleForm.screenDescription" @click.stop="clearDescription()">清除</span>
  312. </el-form-item>
  313. <el-form-item v-if="pagesData.applicationId == 2 && ruleForm.isBatch">
  314. <template #label>
  315. <div class="label-title">
  316. <span><em class="no">4</em> 批量生产</span>
  317. </div>
  318. </template>
  319. <div>
  320. <el-input-number size="small" v-model="ruleForm.batchCount" :controls="false"></el-input-number> 张
  321. </div>
  322. </el-form-item>
  323. <template v-if="pagesData.applicationId == 2 && !ruleForm.isBatch">
  324. <el-form-item>
  325. <template #label>
  326. <div class="label-title">
  327. <span><em class="no">3</em> 图片比例(选填)</span>
  328. </div>
  329. </template>
  330. <div class="tool-direct-left__imageRatio">
  331. <div class="image-ratio__block">
  332. <div class="el-checkbox-group">
  333. <div
  334. v-for="item in ratioList"
  335. :key="item"
  336. :class="['el-checkbox', { 'is-checked': ruleForm.imageRatioList === item }]"
  337. @click="toggle(item)"
  338. >
  339. <span class="el-checkbox__input"></span>
  340. {{ item }}
  341. </div>
  342. </div>
  343. </div>
  344. </div>
  345. </el-form-item>
  346. <el-form-item>
  347. <template #label>
  348. <div class="label-title">
  349. <span><em class="no">4</em> 图片尺寸(选填)</span>
  350. </div>
  351. </template>
  352. <div class="tool-direct-left__imageSize">
  353. <el-select clearable v-model="ruleForm.imageSizeSelect" placeholder="请选择" size="small">
  354. <el-option
  355. v-for="item in imageSizes"
  356. :key="item.value"
  357. :label="item.label"
  358. :value="item.value">
  359. </el-option>
  360. </el-select>
  361. <el-button type="primary" size="small" @click="handleCustom">自定义</el-button>
  362. </div>
  363. <div class="image-ratio__custom" v-if="hasCustom">
  364. <el-input-number :controls="false" v-model="ruleForm.imageSize.width" size="small"></el-input-number>
  365. X
  366. <el-input-number :controls="false" v-model="ruleForm.imageSize.height" size="small"></el-input-number>
  367. </div>
  368. </el-form-item>
  369. </template>
  370. </el-scrollbar>
  371. </div>
  372. <el-form-item class="el-form-item__btns">
  373. <div class="tool-direct-left__btns">
  374. <el-button class="save" @click="submitForm()" type="primary" :loading="comfirmLoading">
  375. 生成图片
  376. </el-button>
  377. <div class="clear" @click="resetForm()">
  378. <el-button>清空</el-button>
  379. </div>
  380. </div>
  381. </el-form-item>
  382. </el-form>
  383. </div>
  384. <div
  385. class="tool-direct-right"
  386. flex="1 ~ col"
  387. v-loading="viewLoading"
  388. >
  389. <!-- <div class="tool-direct-right__title">
  390. <h3>生成结果</h3>
  391. </div> -->
  392. <div
  393. class="tool-direct-right__result"
  394. flex="1 ~ col"
  395. h-full
  396. >
  397. <div
  398. v-if="comfirmLoading"
  399. class="tool-direct-right__loading"
  400. flex="1 ~ col"
  401. >
  402. <svg
  403. xmlns="http://www.w3.org/2000/svg"
  404. width="24"
  405. height="24"
  406. viewBox="0 0 24 24"
  407. fill="none"
  408. stroke="currentColor"
  409. stroke-width="2"
  410. stroke-linecap="round"
  411. stroke-linejoin="round"
  412. style="color: rgb(154, 120, 110);"
  413. ><path d="M21 12a9 9 0 1 1-6.219-8.56" /></svg>
  414. AI正在为您生成图片...
  415. </div>
  416. <template v-else>
  417. <div class="result-success" v-if="resultImage">
  418. <div class="preview-black__images">
  419. <div class="images-preview">
  420. <el-image
  421. :src="resultImage"
  422. :preview-src-list="srcList"
  423. ></el-image>
  424. </div>
  425. <!-- <el-tooltip class="item" effect="dark" content="预览" placement="top">
  426. <div class="images-preview__search">
  427. <el-image
  428. :src="resultImage"
  429. :preview-src-list="srcList"
  430. ></el-image>
  431. <svg t="1759203455323" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4663" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M928 886.4l-160-160c128-150.4 120-377.6-20.8-520C596.8 56 353.6 56 204.8 206.4c-72 72-112 169.6-112 272s40 198.4 112 272c72 72 169.6 112 272 112 91.2 0 179.2-32 248-91.2l160 160c12.8 12.8 32 12.8 44.8 0 11.2-12.8 11.2-32-1.6-44.8zM249.6 705.6c-60.8-60.8-94.4-140.8-94.4-225.6s33.6-166.4 94.4-225.6c62.4-62.4 144-92.8 225.6-92.8s163.2 30.4 225.6 92.8c124.8 124.8 124.8 328 0 452.8C640 768 560 801.6 475.2 801.6c-84.8-3.2-164.8-35.2-225.6-96z" fill="#333333" p-id="4664"></path></svg>
  432. </div>
  433. </el-tooltip> -->
  434. <div class="images-preview__icon">
  435. <el-tooltip class="item" v-if="!isSave" effect="dark" content="保存到我的作品" placement="top">
  436. <div class="like-icons icons" @click="saveUserWork">
  437. <svg
  438. xmlns="http://www.w3.org/2000/svg"
  439. width="24"
  440. height="24"
  441. viewBox="0 0 24 24"
  442. fill="none"
  443. stroke="#EF4444"
  444. stroke-width="2"
  445. stroke-linecap="round"
  446. stroke-linejoin="round"
  447. ><path d="M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z" /></svg>
  448. </div>
  449. </el-tooltip>
  450. <el-tooltip v-else class="item" effect="dark" content="从作品中移除" placement="top">
  451. <div class="like-icons icons" @click="updateUserWork(true)">
  452. <svg t="1760494995280" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7979" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M512.042667 193.237333a255.914667 255.914667 0 0 1 351.658666 9.728 256 256 0 0 1 10.069334 351.402667l-361.813334 362.325333-361.728-362.325333a256 256 0 0 1 361.813334-361.130667z" fill="#EF4444" p-id="7980"></path></svg>
  453. </div>
  454. </el-tooltip>
  455. <el-tooltip class="item" effect="dark" content="下载" placement="top">
  456. <div class="download-icon icons" @click="handleDownload">
  457. <svg
  458. xmlns="http://www.w3.org/2000/svg"
  459. width="24"
  460. height="24"
  461. viewBox="0 0 24 24"
  462. fill="none"
  463. stroke="currentColor"
  464. stroke-width="2"
  465. stroke-linecap="round"
  466. stroke-linejoin="round"
  467. ><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" /><polyline points="7 10 12 15 17 10" /><line
  468. x1="12"
  469. x2="12"
  470. y1="15"
  471. y2="3"
  472. /></svg>
  473. </div>
  474. </el-tooltip>
  475. </div>
  476. </div>
  477. </div>
  478. <div
  479. v-else
  480. class="result-empty"
  481. flex="1 ~ col"
  482. >
  483. <svg t="1760405750612" class="icon" viewBox="0 0 1025 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4580" xmlns:xlink="http://www.w3.org/1999/xlink" width="200.1953125" height="200"><path d="M512.016 1024C229.232 1024 0.016 794.784 0.016 512 0.016 229.216 229.232 0 512.016 0 794.784 0 1024 229.216 1024 512 1024 794.784 794.784 1024 512.016 1024ZM512.016 64C264.976 64 64.016 264.96 64.016 512 64.016 759.024 264.976 960 512.016 960 759.04 960 960 759.024 960 512 960 264.96 759.04 64 512.016 64ZM510.336 833.456C509.056 833.456 507.744 833.488 506.448 833.488 310.992 833.488 229.024 657.12 225.616 649.552 218.336 633.424 225.488 614.496 241.584 607.216 257.712 599.968 276.576 607.088 283.888 623.088 286.64 629.12 352.928 769.488 506.576 769.488 507.584 769.488 508.576 769.456 509.584 769.456 672.896 767.552 738.368 624.768 739.024 623.344 746.176 607.216 765.024 599.872 781.264 607.152 797.392 614.336 804.672 633.248 797.456 649.408 794.176 656.8 714.208 831.056 510.336 833.456ZM671.504 479.84C636.224 479.84 607.664 451.232 607.664 415.984 607.664 380.768 636.224 352.176 671.504 352.176 706.768 352.176 735.344 380.768 735.344 415.984 735.344 451.232 706.768 479.84 671.504 479.84ZM351.504 479.84C316.224 479.84 287.664 451.232 287.664 415.984 287.664 380.768 316.224 352.176 351.504 352.176 386.768 352.176 415.344 380.768 415.344 415.984 415.344 451.232 386.768 479.84 351.504 479.84Z" p-id="4581" fill="#ae8878"></path></svg>
  484. 等您好久了 <br>
  485. 在右侧工作区生成图片吧~
  486. </div>
  487. </template>
  488. </div>
  489. </div>
  490. </div>
  491. </div>
  492. </div>
  493. </div>
  494. <product-repo-modal ref="productRepoModal" @setImageList="setImageList"></product-repo-modal>
  495. </div>
  496. </template>
  497. <script>
  498. import { api } from "@/api/api";
  499. import { getToken } from '@/utils/auth'
  500. import downloadUtil from "@/utils/downloadUtil";
  501. import { genCidHex16, fetchStreamText } from '@/utils/index'
  502. import request from '@/utils/request'
  503. import productRepoModal from './components/productRepoModal'
  504. import draggable from "vuedraggable";
  505. // 引入 Swiper 及样式
  506. import Swiper from 'swiper'
  507. import 'swiper/dist/css/swiper.min.css'
  508. export default {
  509. components: {
  510. productRepoModal,
  511. draggable
  512. },
  513. data() {
  514. return {
  515. action: api.fileUrl,
  516. uploading: false,
  517. comfirmLoading: false,
  518. viewLoading: false,
  519. resultImage: '',
  520. pagesData: {},
  521. pagesId: null,
  522. replaceIndex: null,
  523. tabParentIndex: 0,
  524. replaceTabIndex: null,
  525. isSave: false,
  526. workId: null,
  527. hasTabs: null,
  528. ruleForm: {
  529. imageUrl: '',
  530. imageList: [],
  531. screenDescription: '',
  532. imageRatioList: '',
  533. imageSizeSelect: '',
  534. imageSize: {},
  535. isBatch: false,
  536. editableTabsValue: '1',
  537. editableTabs: [{
  538. title: '图片组 1',
  539. name: '1',
  540. sku: '',
  541. content: []
  542. }],
  543. tabIndex: 1,
  544. batchCount: null
  545. },
  546. swiperList: {},
  547. ratioList: ['3:2', '16:9', '1:1', '2:3', '3:4', '9:16'],
  548. rules: {
  549. imageUrl: [
  550. { validator: (rule, value, callback) => this.checkImage(rule, value, callback), trigger: "change" }
  551. ],
  552. imageList: [
  553. { validator: (rule, value, callback) => this.checkImageList(rule, value, callback), trigger: "change" }
  554. ],
  555. editableTabs: [
  556. { validator: (rule, value, callback) => this.checkImageGroup(rule, value, callback), trigger: "change" }
  557. ],
  558. screenDescription: [
  559. {
  560. validator: (rule, value, callback) => {
  561. // 根据条件动态判断是否必填
  562. if (this.pagesData.applicationId != 3 && !value) {
  563. callback(new Error('请输入画面描述'))
  564. } else if (value.trim() && value.trim().length < 3) {
  565. callback(new Error('画面描述至少需要 3 个字符'))
  566. } else {
  567. callback()
  568. }
  569. },
  570. trigger: ['blur', 'change']
  571. }
  572. ]
  573. },
  574. hasCustom: false,
  575. srcList: [],
  576. swiper: null
  577. };
  578. },
  579. //页面创建的时候执行
  580. mounted() {
  581. if (!this.pagesId) {
  582. this.pagesId = this.$route.params.id;
  583. }
  584. // this.clearFormData();
  585. if (this.pagesId == 1) {
  586. this.pagesData = {
  587. title: '创意生图',
  588. applicationId: this.pagesId, // 1:文生图 2:图生图 3:图片生成线稿
  589. desc: '通过详细的文字描述,AI将为您生成符合要求的服装设计方案。',
  590. contentTips: '请详细描述您想要的服装款式、风格、颜色、材质等',
  591. placeholder: '例如:一件适合夏季穿着的波西米亚风格长裙,采用丝绸面料,以佩斯利和花纹为主,土色调为橙色和蓝色..'
  592. }
  593. }
  594. if (this.pagesId == 2) {
  595. this.pagesData = {
  596. title: '图生图',
  597. applicationId: this.pagesId, // 1:文生图 2:图生图 3:图片生成线稿
  598. desc: '上传参考图片,AI将基于图片内容创作新的服装设计。',
  599. contentTips: '请描述你希望修改或增强的元素',
  600. placeholder: '例如:“保持整体款式,但改为红色印花,增加轻微褶皱感”'
  601. }
  602. }
  603. if (this.pagesId == 3) {
  604. this.pagesData = {
  605. title: '线稿生成',
  606. applicationId: this.pagesId, // 1:文生图 2:图生图 3:图片生成线稿
  607. desc: '上传确定好的服装设计图,AI将提取静物轮廓生成专业的线稿。',
  608. contentTips: '请描述设计草图的关键细节',
  609. placeholder: '例如:“画出正反面线稿,重点表现领口、袖口和裙摆褶皱”'
  610. }
  611. }
  612. },
  613. computed: {
  614. token() {
  615. return getToken();
  616. },
  617. imageSizes() {
  618. return [
  619. {label: '1600x1600', value: '1600x1600'},
  620. {label: '1800x2400', value: '1800x2400'},
  621. {label: '1600x2400', value: '1600x2400'},
  622. {label: '1080×1350', value: '1080×1350'},
  623. {label: '1200×1500', value: '1200×1500'},
  624. {label: '1920×1080', value: '1920×1080'},
  625. {label: '2000×2000', value: '2000×2000'}
  626. ]
  627. }
  628. },
  629. watch: {
  630. 'ruleForm.imageList': {
  631. deep: true,
  632. immediate: true, // 页面初次挂载时也会执行一次
  633. handler(newVal) {
  634. // 没有图片就不初始化
  635. if (!newVal || !newVal.length) return
  636. // 确保 DOM 已经渲染完毕
  637. this.$nextTick(() => {
  638. if (!this.swiper) {
  639. this.initSwiper() // 首次初始化
  640. } else {
  641. // this.swiper.update() // 后续新增 slide 更新
  642. }
  643. })
  644. }
  645. },
  646. 'ruleForm.editableTabs': {
  647. deep: true,
  648. immediate: true, // 页面初次挂载时也会执行一次
  649. handler(newVal) {
  650. // 没有图片就不初始化
  651. if (!newVal[this.tabParentIndex] || !newVal[this.tabParentIndex].content || !newVal[this.tabParentIndex].content.length) return
  652. // 确保 DOM 已经渲染完毕
  653. this.$nextTick(() => {
  654. if (!this.swiperList[this.tabParentIndex]) {
  655. this.initSwiperGroup(this.tabParentIndex) // 首次初始化
  656. }
  657. })
  658. }
  659. },
  660. 'ruleForm.isBatch'(val) {
  661. this.$nextTick(() => {
  662. setTimeout(() => {
  663. const index = this.tabParentIndex;
  664. const swiper = this.swiperList[index];
  665. if (swiper) {
  666. swiper.destroy(true, true);
  667. this.$set(this.swiperList, index, null);
  668. }
  669. if (this.swiper) {
  670. this.swiper.destroy(true, true);
  671. this.swiper = null;
  672. }
  673. this.initSwiper();
  674. if (val) {
  675. this.initSwiperGroup(index);
  676. }
  677. }, 0) // 🔥 给 el-switch / el-tabs 动画时间
  678. })
  679. }
  680. },
  681. methods: {
  682. handleBatch() {
  683. this.$nextTick(() => {
  684. // this.$refs.ruleForm.clearValidate()
  685. })
  686. },
  687. addTab(targetName) {
  688. this.tabParentIndex = targetName
  689. let newTabName = ++this.ruleForm.tabIndex + '';
  690. this.ruleForm.editableTabs.push({
  691. title: `图片组 ${targetName}`,
  692. name: newTabName,
  693. sku: '',
  694. content: []
  695. });
  696. this.ruleForm.editableTabsValue = newTabName;
  697. },
  698. removeTab(targetName) {
  699. const tabs = this.ruleForm.editableTabs
  700. if (tabs.length <= 1) {
  701. this.$message.error('至少存在一个图片组!')
  702. return
  703. }
  704. const tabIndex = tabs.findIndex(tab => tab.name === targetName)
  705. if (tabIndex === -1) return
  706. const hasContent = tabs[tabIndex].content.length > 0
  707. const doRemove = () => {
  708. this.handleActiveTabChange(targetName)
  709. this.destroySwiperByIndex(tabIndex)
  710. this.ruleForm.editableTabs = tabs.filter(tab => tab.name !== targetName)
  711. this.$notify({
  712. title: '成功',
  713. message: '删除成功',
  714. type: 'success',
  715. duration: 3000
  716. })
  717. }
  718. if (hasContent) {
  719. this.$confirm('该组有图片数据,确定要删除吗?', '提示', {
  720. confirmButtonText: '确定',
  721. cancelButtonText: '取消',
  722. type: 'warning'
  723. }).then(doRemove)
  724. } else {
  725. doRemove()
  726. }
  727. },
  728. handleActiveTabChange(removedName) {
  729. let activeName = this.ruleForm.editableTabsValue
  730. if (activeName !== removedName) return
  731. const tabs = this.ruleForm.editableTabs
  732. const index = tabs.findIndex(tab => tab.name === removedName)
  733. const nextTab = tabs[index + 1] || tabs[index - 1]
  734. this.ruleForm.editableTabsValue = nextTab ? nextTab.name : ''
  735. },
  736. destroySwiperByIndex(index) {
  737. const swiper = this.swiperList[index]
  738. if (!swiper) return
  739. swiper.destroy(true, true)
  740. // Vue 2 响应式安全删除
  741. this.$delete(this.swiperList, index)
  742. },
  743. clickTabs(tab) {
  744. const parentIndex = this.ruleForm.editableTabs.findIndex(
  745. item => item.name === tab.name
  746. )
  747. if (parentIndex !== -1) {
  748. this.tabParentIndex = parentIndex
  749. this.initSwiperGroup(parentIndex)
  750. }
  751. },
  752. toggle(item) {
  753. // 点击已选中 → 取消
  754. if (this.ruleForm.imageRatioList === item) {
  755. this.ruleForm.imageRatioList = '';
  756. } else {
  757. this.ruleForm.imageRatioList = item;
  758. }
  759. },
  760. initSwiper() {
  761. const container = this.$refs.swiperContainer
  762. if (!container) return
  763. // 初始化实例
  764. this.swiper = new Swiper(container, {
  765. slidesPerView: 2.5,
  766. slidesPerGroup: 2,
  767. spaceBetween: 10,
  768. loop: false,
  769. allowTouchMove: false,
  770. navigation: {
  771. nextEl: '.swiper-button-next',
  772. prevEl: '.swiper-button-prev'
  773. }
  774. })
  775. },
  776. initSwiperGroup(parentIndex) {
  777. this.$nextTick(() => {
  778. const refName = `swiperContainer-${parentIndex}`
  779. const container = this.$refs[refName]
  780. if (!container) return
  781. // 防止重复初始化
  782. if (this.swiperList && this.swiperList[parentIndex]) {
  783. this.swiperList[parentIndex].destroy(true, true)
  784. }
  785. const swiper = new Swiper(container, {
  786. slidesPerView: 2.5,
  787. slidesPerGroup: 2,
  788. spaceBetween: 10,
  789. loop: false,
  790. allowTouchMove: false,
  791. navigation: {
  792. nextEl: '.swiper-button-next',
  793. prevEl: '.swiper-button-prev'
  794. }
  795. })
  796. // 统一管理 swiper 实例
  797. if (!this.swiperList) {
  798. this.swiperList = {}
  799. }
  800. this.swiperList[parentIndex] = swiper
  801. })
  802. },
  803. handleCustom() {
  804. this.hasCustom = !this.hasCustom
  805. if (!this.hasCustom) {
  806. this.ruleForm.imageSize = {}
  807. }
  808. },
  809. checkImage(rule, value, callback) {
  810. if (!this.ruleForm.imageUrl) {
  811. callback(new Error('请上传图片!'));
  812. } else {
  813. callback();
  814. }
  815. },
  816. checkImageList(rule, value, callback) {
  817. if (!this.ruleForm.imageList.length) {
  818. callback(new Error('请至少上传一张图片!'));
  819. } else {
  820. callback();
  821. }
  822. },
  823. checkImageGroup(rule, value, callback) {
  824. const imgs = this.ruleForm.editableTabs.filter(acc => acc.content.length)
  825. const skus = this.ruleForm.editableTabs.filter(acc => acc.sku)
  826. if (!imgs.length || !skus.length) {
  827. if (!skus.length) {
  828. callback(new Error('SKU为必填项!'));
  829. } else {
  830. callback(new Error('请至少上传一组上衣图片!'));
  831. }
  832. } else {
  833. callback();
  834. }
  835. },
  836. submitForm() {
  837. this.$refs['ruleForm'].validate(async (valid) => {
  838. if (valid) {
  839. if (this.pagesData.applicationId == 2 && this.ruleForm.isBatch) {
  840. if (!this.ruleForm.screenDescription) {
  841. this.$message.error('画面描述为必填项!');
  842. return;
  843. }
  844. if (this.ruleForm.screenDescription.trim() && this.ruleForm.screenDescription.trim().length < 3) {
  845. this.$message.error('画面描述至少需要 3 个字符!');
  846. return;
  847. }
  848. this.comfirmLoading = true;
  849. let payload = {
  850. sessionId: `${Date.now() + 10}_${genCidHex16()}`,
  851. applicationId: 5,
  852. prompt: this.ruleForm.screenDescription,
  853. poseImages: this.ruleForm.imageList,
  854. count: this.ruleForm.batchCount,
  855. clothesItems: this.ruleForm.editableTabs.map(acc => {
  856. return {
  857. sku: acc.sku,
  858. clothesImages: acc.content
  859. }
  860. })
  861. }
  862. request({
  863. url: '/imageTask/submit',
  864. method: 'post',
  865. data: payload
  866. }).then(res => {
  867. if (res.code == 200) {
  868. this.$router.push(`/cropList/cropListIndex`);
  869. this.$message.success(res.msg || '操作成功!');
  870. }
  871. }).finally(() => {
  872. this.comfirmLoading = false;
  873. })
  874. return;
  875. }
  876. this.comfirmLoading = true;
  877. this.workId = null;
  878. this.isSave = false;
  879. let payload = {
  880. sessionId: `${Date.now() + 10}_${genCidHex16()}`,
  881. applicationId: this.pagesData.applicationId,
  882. prompt: this.ruleForm.screenDescription
  883. }
  884. let ajaxName = 'textMessage'
  885. if (this.pagesData.applicationId == 2) {
  886. // 图生图
  887. ajaxName = 'imageMessage'
  888. let imageRatio = []
  889. if (this.ruleForm.imageRatioList) {
  890. imageRatio = this.ruleForm.imageRatioList.trim().split(':').map(s => Number(s.trim()))
  891. }
  892. let imageSize = []
  893. if (this.ruleForm.imageSizeSelect) {
  894. imageSize = this.ruleForm.imageSizeSelect.split(/[×xX*]/).map(Number)
  895. }
  896. if (this.ruleForm.imageSize.height && this.ruleForm.imageSize.width) {
  897. imageSize = [this.ruleForm.imageSize.width, this.ruleForm.imageSize.height]
  898. }
  899. payload = {
  900. sessionId: `${Date.now() + 10}_${genCidHex16()}`,
  901. applicationId: this.pagesData.applicationId,
  902. prompt: this.ruleForm.screenDescription,
  903. images: this.ruleForm.imageList
  904. }
  905. if (imageRatio.length) {
  906. payload.ratio = imageRatio;
  907. }
  908. if (imageSize.length) {
  909. payload.imageSize = imageSize;
  910. }
  911. }
  912. if (this.pagesData.applicationId == 3) {
  913. ajaxName = 'generateSketch'
  914. payload = {
  915. sessionId: `${Date.now() + 10}_${genCidHex16()}`,
  916. applicationId: this.pagesData.applicationId,
  917. prompt: this.ruleForm.screenDescription,
  918. image: this.ruleForm.imageUrl
  919. }
  920. }
  921. try {
  922. const result = await fetchStreamText(`/app/ai/send/${ajaxName}`, payload, {
  923. onChunk: (chunk) => {
  924. console.log('实时分片:', chunk)
  925. // this.replyText += chunk // Vue 可实时更新聊天内容
  926. }
  927. })
  928. try {
  929. if (result && typeof result == 'string') {
  930. console.log('完整文本:', result)
  931. const data = JSON.parse(result)
  932. if (result.code == 200) {
  933. this.resultImage = data.image
  934. this.srcList = [data.image]
  935. } else {
  936. if (result.code == 401) {
  937. this.$router.push(`/login`);
  938. } else {
  939. this.$message.error(result.msg || result.errorContent || '请求出错,请联系管理员!');
  940. }
  941. }
  942. } else {
  943. console.log('JSON 对象12', result)
  944. if (result.code == 200) {
  945. this.resultImage = result.image
  946. this.srcList = [result.image]
  947. } else {
  948. if (result.code == 401) {
  949. this.$router.push(`/login`);
  950. } else {
  951. this.$message.error(result.msg || result.errorContent || '请求出错,请联系管理员!');
  952. }
  953. }
  954. }
  955. } catch (e) {
  956. console.error('报错:', e)
  957. }
  958. } catch (e) {
  959. console.error('请求错误:', e)
  960. } finally {
  961. this.comfirmLoading = false
  962. }
  963. }
  964. });
  965. },
  966. clearFormData() {
  967. this.$nextTick(() => {
  968. this.ruleForm = {
  969. imageUrl: '',
  970. imageList: [],
  971. screenDescription: '',
  972. imageRatioList: '',
  973. imageSizeSelect: '',
  974. imageSize: {}
  975. }
  976. this.$refs.ruleForm && this.$refs.ruleForm.resetFields();
  977. })
  978. },
  979. resetForm() {
  980. this.$confirm("确定要清空所有选项吗?", "提示", {
  981. confirmButtonText: "确定",
  982. cancelButtonText: "取消",
  983. type: "warning"
  984. }).then(() => {
  985. this.clearFormData()
  986. this.$notify({
  987. title: "成功",
  988. message: "操作成功",
  989. type: "success",
  990. duration: 3000
  991. });
  992. });
  993. },
  994. replaceFile(itemIndex) {
  995. this.replaceIndex = itemIndex
  996. if (this.$refs.uploadRef) {
  997. // 清空上传队列
  998. this.$refs.uploadRef.uploadFiles = []
  999. // 手动触发上传框选择文件
  1000. const input = this.$refs.uploadRef.$el.querySelector('input[type=file]')
  1001. input.click()
  1002. }
  1003. // 在 handleSuccess 中会替换对应文件
  1004. },
  1005. beforeUploadVideo(file) {
  1006. this.uploading = true;
  1007. const isLt10M = file.size / 1024 / 1024 < 200;
  1008. const imagesTypes = ['image/png','image/jpeg','image/webp']
  1009. if (!imagesTypes.includes(file.type)) {
  1010. this.uploading = false;
  1011. this.$message.error('图片只能是jpg、png和webp格式!');
  1012. return false;
  1013. }
  1014. if (!isLt10M) {
  1015. this.$message.error('上传文件大小不能超过20MB哦!');
  1016. return false;
  1017. }
  1018. return true
  1019. },
  1020. handleVideoSuccess(res, file) {
  1021. this.uploading = false
  1022. this.$refs.ruleForm.clearValidate(['imageList']);
  1023. this.$refs.ruleForm.clearValidate(['imageUrl']);
  1024. if (this.ruleForm.imageList.length >= 5) {
  1025. this.$message.error('最多只能上传5张图片!');
  1026. return;
  1027. }
  1028. if (this.pagesData.applicationId == 2) {
  1029. if (this.replaceIndex !== null) {
  1030. // 用新图片替换原有 URL
  1031. if (res.code == 200) {
  1032. this.$set(this.ruleForm.imageList, this.replaceIndex, res.data.url)
  1033. }
  1034. this.replaceIndex = null
  1035. } else {
  1036. // 新增上传
  1037. if (res.code == 200) {
  1038. this.ruleForm.imageList.push(res.data.url)
  1039. } else {
  1040. this.$message.error('上传失败,请重新上传!');
  1041. }
  1042. this.$nextTick(() => {
  1043. if (this.swiper) {
  1044. this.swiper.update()
  1045. const lastIndex = this.ruleForm.imageList.length - 1
  1046. this.swiper.slideTo(lastIndex, 300) // 平滑滚动到最后
  1047. } else {
  1048. this.initSwiper()
  1049. }
  1050. })
  1051. }
  1052. } else {
  1053. if (res.code == 200) {
  1054. this.ruleForm.imageUrl = res.data.url
  1055. } else {
  1056. this.$message.error('上传失败,请重新上传!');
  1057. }
  1058. }
  1059. },
  1060. imgUploadDel(itemIndex) {
  1061. //删除
  1062. this.$confirm("确定要删除吗?", "提示", {
  1063. confirmButtonText: "确定",
  1064. cancelButtonText: "取消",
  1065. type: "warning"
  1066. }).then(() => {
  1067. if (this.pagesData.applicationId == 2) {
  1068. this.ruleForm.imageList.splice(itemIndex, 1);
  1069. this.swiper.update()
  1070. } else {
  1071. this.ruleForm.imageUrl = ''
  1072. }
  1073. if (this.ruleForm.imageList.length == 0) {
  1074. if (this.swiper) {
  1075. this.swiper.destroy(true, true) // 完全销毁 Swiper
  1076. this.swiper = null
  1077. }
  1078. }
  1079. this.$notify({
  1080. title: "成功",
  1081. message: "删除成功",
  1082. type: "success",
  1083. duration: 3000
  1084. });
  1085. });
  1086. },
  1087. replaceFileGroup(itemIndex, parentIndex) {
  1088. this.replaceTabIndex = itemIndex
  1089. if (this.$refs[`uploadRef-${parentIndex}`]) {
  1090. // 清空上传队列
  1091. this.$refs[`uploadRef-${parentIndex}`][0].uploadFiles = []
  1092. // 手动触发上传框选择文件
  1093. const input = this.$refs[`uploadRef-${parentIndex}`][0].$el.querySelector('input[type=file]')
  1094. input.click()
  1095. }
  1096. // 在 handleSuccess 中会替换对应文件
  1097. },
  1098. handleVideoSuccessGroup(res, file) {
  1099. this.uploading = false
  1100. this.$refs.ruleForm.clearValidate(['editableTabs']);
  1101. // if (this.ruleForm.editableTabs[this.tabParentIndex].content.length >= 5) {
  1102. // this.$message.error('最多只能上传5张图片!');
  1103. // return;
  1104. // }
  1105. if (this.replaceTabIndex !== null) {
  1106. // 用新图片替换原有 URL
  1107. if (res.code == 200) {
  1108. this.$set(this.ruleForm.editableTabs[this.tabParentIndex].content, this.replaceTabIndex, res.data.url);
  1109. }
  1110. this.tabParentIndex = null
  1111. } else {
  1112. // 新增上传
  1113. if (res.code == 200) {
  1114. this.ruleForm.editableTabs[this.tabParentIndex].content.push(res.data.url)
  1115. } else {
  1116. this.$message.error('上传失败,请重新上传!');
  1117. }
  1118. this.$nextTick(() => {
  1119. if (this.swiperList[this.tabParentIndex]) {
  1120. this.swiperList[this.tabParentIndex].update()
  1121. const lastIndex = this.ruleForm.editableTabs[this.tabParentIndex].content.length - 1
  1122. this.swiperList[this.tabParentIndex].slideTo(lastIndex, 300) // 平滑滚动到最后
  1123. } else {
  1124. this.initSwiper()
  1125. }
  1126. })
  1127. }
  1128. },
  1129. imgUploadDelGroup(itemIndex) {
  1130. //删除
  1131. this.$confirm("确定要删除吗?", "提示", {
  1132. confirmButtonText: "确定",
  1133. cancelButtonText: "取消",
  1134. type: "warning"
  1135. }).then(() => {
  1136. this.ruleForm.editableTabs[this.tabParentIndex].content.splice(itemIndex, 1);
  1137. this.swiperList[this.tabParentIndex].update()
  1138. if (this.ruleForm.editableTabs[this.tabParentIndex].content.length == 0) {
  1139. if (this.swiperList[this.tabParentIndex]) {
  1140. this.swiperList[this.tabParentIndex].destroy(true, true)
  1141. delete this.swiperList[this.tabParentIndex]
  1142. }
  1143. }
  1144. this.$notify({
  1145. title: "成功",
  1146. message: "删除成功",
  1147. type: "success",
  1148. duration: 3000
  1149. });
  1150. });
  1151. },
  1152. clearDescription() {
  1153. this.$refs.ruleForm.clearValidate(['screenDescription']);
  1154. this.ruleForm.screenDescription = ''
  1155. },
  1156. openPreview() {
  1157. this.$refs.imgRef.$el.click()
  1158. },
  1159. // 视频下载
  1160. async handleDownload() {
  1161. const match = this.resultImage.match(/\/([^\/?#]+)$/);
  1162. if (match) {
  1163. this.viewLoading = true
  1164. const fileName = match[1]
  1165. try {
  1166. const res = await downloadUtil.fileDownload(this.resultImage, fileName);
  1167. if (res) {
  1168. this.$message.success("下载成功");
  1169. }
  1170. } catch (error) {
  1171. this.$message.error(error.message);
  1172. } finally {
  1173. this.viewLoading = false
  1174. }
  1175. }
  1176. },
  1177. saveUserWork() {
  1178. if (this.workId) {
  1179. this.updateUserWork(false);
  1180. return;
  1181. }
  1182. this.viewLoading = true
  1183. request({
  1184. url: '/userWork/add',
  1185. method: 'post',
  1186. data: {
  1187. applicationId: this.pagesData.applicationId,
  1188. image: this.resultImage
  1189. }
  1190. }).then(res => {
  1191. if (res.code == 200) {
  1192. this.isSave = true;
  1193. this.workId = res.data;
  1194. this.$message.success(res.msg || '操作成功!');
  1195. }
  1196. }).finally(() => {
  1197. this.viewLoading = false
  1198. })
  1199. },
  1200. updateUserWork(bool) {
  1201. this.viewLoading = true
  1202. request({
  1203. url: '/userWork/updateDeleteStatus',
  1204. method: 'post',
  1205. data: {
  1206. id: this.workId || '',
  1207. deleted: bool
  1208. }
  1209. }).then(res => {
  1210. if (res.code == 200) {
  1211. this.isSave = !bool;
  1212. this.$message.success(res.msg || '操作成功!');
  1213. }
  1214. }).finally(() => {
  1215. this.viewLoading = false
  1216. })
  1217. },
  1218. setImageList(row = [], dels = []) {
  1219. const isTabs = this.hasTabs === 'tabs';
  1220. const parentIndex = this.tabParentIndex;
  1221. // 1️⃣ 取目标图片数组
  1222. const targetList = isTabs
  1223. ? this.ruleForm.editableTabs[parentIndex].content
  1224. : this.ruleForm.imageList;
  1225. // 2️⃣ 新增图片(去重)
  1226. row.forEach(item => {
  1227. if (!targetList.includes(item)) {
  1228. targetList.push(item);
  1229. }
  1230. })
  1231. // 3️⃣ 删除图片(安全方式)
  1232. if (Array.isArray(dels) && dels.length) {
  1233. const keepSet = new Set(row);
  1234. for (let i = targetList.length - 1; i >= 0; i--) {
  1235. if (!keepSet.has(targetList[i])) {
  1236. targetList.splice(i, 1);
  1237. }
  1238. }
  1239. }
  1240. // 4️⃣ Swiper 更新
  1241. this.$nextTick(() => {
  1242. if (isTabs) {
  1243. const swiper = this.swiperList[parentIndex];
  1244. swiper ? swiper.update() : this.initSwiperGroup(parentIndex);
  1245. } else {
  1246. this.swiper ? this.swiper.update() : this.initSwiper();
  1247. }
  1248. })
  1249. },
  1250. handleProductRepo(type) {
  1251. if (this.$refs.productRepoModal) {
  1252. if (type === 'tabs') {
  1253. this.hasTabs = type;
  1254. this.$refs.productRepoModal.show(this.ruleForm.editableTabs[this.tabParentIndex].content, type);
  1255. } else {
  1256. this.hasTabs = null;
  1257. this.$refs.productRepoModal.show(this.ruleForm.imageList, this.ruleForm.isBatch ? 'tabs' : null);
  1258. }
  1259. }
  1260. }
  1261. }
  1262. };
  1263. </script>
  1264. <style rel="stylesheet/scss" lang="scss" scoped>
  1265. @import "@/styles/layout.scss";
  1266. @import "./design-agent.scss";
  1267. </style>