makeImgDetail.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. <template>
  2. <view class="makedetail-container">
  3. <!-- <view class="status-bar"></view> -->
  4. <!-- 顶部导航 -->
  5. <view class="nav-bar">
  6. <view class="left">
  7. <view class="uni-btn-icon" @click="goBack">&#xe601;</view>
  8. <view class="create">灵感创作</view>
  9. <image src="@/static/makedetail/cz_icon_lingganchuangzuo.png" class="edit"></image>
  10. </view>
  11. <view class="right">
  12. <view class="coinM" @click="isRecharge ? goPage('/pages/vip/M_purchase') : ''">
  13. <image src="/static/icon/coin_m.png" mode="aspectFit"></image>
  14. <text>{{ myinfo.num_gmm | formatNumberToK }}</text>
  15. <image class="money-add" v-if="isRecharge" src="/static/icon/coin_add.png" mode="aspectFit"></image>
  16. </view>
  17. <view class="coinC" @click="isRecharge ? goPage('/pages/my/job?type=recharge') : ''">
  18. <image src="/static/icon/coin_cd.png" mode="aspectFit"></image>
  19. <text>{{ myinfo.num_gmd | formatNumberToK }}</text>
  20. <image class="money-add" v-if="isRecharge" src="/static/icon/coin_add.png" mode="aspectFit"></image>
  21. </view>
  22. </view>
  23. </view>
  24. <!-- 排队预览区域 -->
  25. <view class="preview-section lineUp-section" v-if="queuing">
  26. <view class="section-title">
  27. <text>创作预览</text>
  28. <view class="member-box">
  29. <image src="@/static/makedetail/wd_icon_vip(1).png" mode="aspectFit"></image>
  30. 升级会员插队加速
  31. </view>
  32. </view>
  33. <view class="preview-card">
  34. <image src="@/static/makedetail/cz_icon_jiazai.png" mode="aspectFit"></image>
  35. <view class="text1">排队中{{ queuePosition + '/' + allPosition }}</view>
  36. <view class="text2">退出不影响继续生成</view>
  37. </view>
  38. </view>
  39. <!-- 创作预览区域 -->
  40. <view class="preview-section" v-if="inQueue">
  41. <view class="section-title">创作预览</view>
  42. <view class="preview-card">
  43. <image src="@/static/makedetail/cz_icon_shengcheng.png" mode="aspectFit"></image>
  44. <view class="text1">生成中{{ progress }}%</view>
  45. <view class="text2">退出不影响继续生成</view>
  46. </view>
  47. </view>
  48. <!-- 以下区域在非排队状态下显示 -->
  49. <view style=" flex: 1;">
  50. <!-- 创作描述输入区 -->
  51. <view class="description-section">
  52. <view class="section-header">
  53. <text class="section-title" style="margin-bottom: 0;">创作描述<text style="display: none;" class="required">*</text></text>
  54. <view class="clear-text" @click="doYouWantToEdit() ? state() : clearDescription()">
  55. <image src="/static/clear.png" mode="aspectFit"></image>
  56. <view class="clear-box">
  57. <image src="@/static/makedetail/cz_icon_qingkongwenben.png" class="edit"></image>
  58. <text>清空文本</text>
  59. </view>
  60. </view>
  61. </view>
  62. <textarea auto-height class="input-area" v-model="description" placeholder="请输入描述语,例如:穿着白色运动服,外表俊朗..."
  63. maxlength="1000" @input="onDescriptionInput" :disabled="doYouWantToEdit()"></textarea>
  64. <view class="word-count">{{ descriptionLength }}/1000</view>
  65. </view>
  66. <!-- 行为动作选择区 -->
  67. <view class="action-section">
  68. <view class="section-title">行为动作</view>
  69. <input :disabled="doYouWantToEdit()" class="input-box" v-model="action" placeholder="可输入也可点击推荐词" />
  70. <view class="tag-group">
  71. <text class="tag" v-for="(item, index) in actionTags" :key="index"
  72. @click="doYouWantToEdit() ? state() : selectAction(item)" :class="{ active: action == item }">{{
  73. item }}</text>
  74. </view>
  75. </view>
  76. <!-- 背景环境选择区 -->
  77. <view class="environment-section">
  78. <view class="section-title">背景环境</view>
  79. <input class="input-box" v-model="environment" :disabled="doYouWantToEdit()" placeholder="可输入也可点击推荐词" />
  80. <view class="tag-group">
  81. <text class="tag" v-for="(item, index) in environmentTags" :key="index"
  82. @click="doYouWantToEdit() ? state() : selectEnvironment(item)"
  83. :class="{ active: environment == item }">{{ item }}</text>
  84. </view>
  85. </view>
  86. <!-- 主题装扮选择区 -->
  87. <view class="image-section">
  88. <view class="section-title">主题装扮</view>
  89. <input class="input-box" v-model="image" :disabled="doYouWantToEdit()" placeholder="可输入也可点击推荐词" />
  90. <view class="tag-group">
  91. <text class="tag" v-for="(item, index) in imageTags" :key="index"
  92. @click="doYouWantToEdit() ? state() : selectImage(item)" :class="{ active: image == item }">{{
  93. item
  94. }}</text>
  95. </view>
  96. </view>
  97. <!-- 参考风格选择区 -->
  98. <view class="style-section" >
  99. <view class="section-title">选择参考风格<text style="display: none;" class="required">*</text></view>
  100. <scroll-view class="style-scroll" scroll-x>
  101. <view class="style-item" v-for="(item, index) in styleList" :key="index"
  102. :class="{ 'active': selectedStyle === index }"
  103. @click="doYouWantToEdit() ? state() : selectStyle(index)">
  104. <view class="image">
  105. <image :src="item.image" mode="aspectFill"></image>
  106. </view>
  107. <text>{{ item.name }}</text>
  108. </view>
  109. </scroll-view>
  110. </view>
  111. <!-- 底部按钮 -->
  112. <view class="bottom-button">
  113. <button v-if="!doYouWantToEdit()" class="generate-btn" @click="generateImage">立即生成
  114. <image src="/static/icon/coin_cd.png" mode="aspectFit"></image>
  115. 10
  116. </button>
  117. <view v-else class="generate-btn prohibit">生成中
  118. </view>
  119. <view v-if="isRecharge" class="promotion-link" @click="goPage('/pages/vip/index')">
  120. <image class="vip" src="/static/makedetail/wd_icon_vip(1).png" mode="aspectFit"></image>
  121. <text> 即刻开通订阅,获取各种福利! </text>
  122. <image class="jiantou" src="/static/makedetail/cz_icon_jiantou.png" mode="aspectFit"></image>
  123. </view>
  124. </view>
  125. </view>
  126. <!-- 新手引导组件 -->
  127. <novice-guidance :step="step" ></novice-guidance>
  128. </view>
  129. </template>
  130. <script>
  131. import { mapState } from 'vuex'
  132. export default {
  133. data() {
  134. return {
  135. queuePosition: '0',
  136. allPosition: '0',
  137. progress: '', //创作进度
  138. description: '',
  139. descriptionLength: 0,
  140. action: '',
  141. environment: '',
  142. image: '',
  143. timer: null, // 添加定时器变量
  144. selectedStyle: -1,
  145. actionTags: ['跳舞', '唱歌', '喝咖啡', '看书', '运动'],
  146. environmentTags: ['都市大道', '大树底下', '办公室', '厨房'],
  147. imageTags: ['戴着墨镜', '戴着耳机', '戴着帽子', '手持冲浪板'],
  148. styleList: [{
  149. name: '国风新绎',
  150. image: '../../static/makedetail/style-1.png'
  151. },
  152. {
  153. name: '休闲自然',
  154. image: '../../static/makedetail/style-2.png'
  155. },
  156. {
  157. name: '西方魔法',
  158. image: '../../static/makedetail/style-3.png'
  159. },
  160. {
  161. name: '可爱甜蜜',
  162. image: '../../static/makedetail/style-4.png'
  163. }
  164. ],
  165. inQueue: false,//是否创作中
  166. queuing: false,//是否排队中
  167. queueMessage: '',
  168. myinfo: {},
  169. step: {
  170. name: "makeImgGuide",
  171. guideList: [
  172. {
  173. el: ".right",
  174. tips: "积分可在这里查看,每日签到可获得积分!",
  175. next: "知道了",
  176. // style: "width: 120px; left: 490rpx;"
  177. },
  178. {
  179. el: ".input-area",
  180. tips: "输入关键词,描述我想要的画面!",
  181. next: "知道了",
  182. },
  183. {
  184. el: ".action-section",
  185. tips: "选择或输入主体的行为动作!",
  186. next: "知道了",
  187. },
  188. {
  189. el: ".environment-section",
  190. tips: "选择或输入主体所处的环境场景",
  191. next: "知道了",
  192. },
  193. {
  194. el: ".image-section",
  195. tips: "选择或输入主题装扮特征",
  196. next: "知道了",
  197. },
  198. {
  199. el: ".style-scroll",
  200. tips: "选择您喜欢的参考风格",
  201. next: "知道了",
  202. },
  203. {
  204. el: ".generate-btn",
  205. tips: "点击这里开始生成您的图片作品",
  206. next: "完成",
  207. }]
  208. }
  209. }
  210. },
  211. computed: {
  212. ...mapState('switchingModule', ['isRecharge', 'isGuiding'])
  213. },
  214. onLoad(e) {
  215. // this.checkQueueStatus()
  216. this.getMyInfo();
  217. if (e.id) {
  218. this.getQueueDetail(e.id)
  219. // 启动轮询
  220. this.startPolling(e.id);
  221. }
  222. },
  223. onHide() {
  224. // 组件卸载时清除定时器
  225. this.clearPolling();
  226. },
  227. methods: {
  228. doYouWantToEdit() {
  229. return this.inQueue || this.queuing
  230. },
  231. state() {
  232. if (this.inQueue) {
  233. uni.showToast({
  234. title: '正在创作中无法修改',
  235. icon: 'none'
  236. })
  237. } else if (this.queuing) {
  238. uni.showToast({
  239. title: '正在排队中无法修改',
  240. icon: 'none'
  241. })
  242. }
  243. },
  244. goBack() {
  245. uni.navigateBack()
  246. },
  247. getMyInfo() {
  248. uni.request({
  249. url: this.$apiHost + '/My/getnum',
  250. method: 'GET',
  251. header: {
  252. 'content-type': 'application/json',
  253. 'sign': getApp().globalData.headerSign
  254. },
  255. data: {
  256. uuid: getApp().globalData.uuid
  257. },
  258. success: (res) => {
  259. console.log("获取用户信息:", res.data);
  260. this.myinfo = res.data
  261. }
  262. })
  263. },
  264. checkQueueStatus() {
  265. uni.request({
  266. url: this.$apiHost + '/WorkAI/queueStatus',
  267. method: 'GET',
  268. header: {
  269. 'Content-Type': 'application/x-www-form-urlencoded',
  270. 'sign': getApp().globalData.headerSign
  271. },
  272. data: {
  273. uuid: getApp().globalData.uuid,
  274. task_type: 1
  275. },
  276. success: (res) => {
  277. console.log("队列状态:", res.data);
  278. if (res.data.success === "yes") {
  279. this.inQueue = res.data.in_queue
  280. if (this.inQueue) {
  281. this.queueMessage = res.data.str
  282. }
  283. }
  284. },
  285. fail: (err) => {
  286. console.log('获取队列状态失败:', err);
  287. uni.showToast({
  288. title: '获取状态失败',
  289. icon: 'none'
  290. });
  291. }
  292. })
  293. },
  294. clearDescription() {
  295. this.description = ''
  296. this.descriptionLength = 0
  297. },
  298. onDescriptionInput(e) {
  299. this.descriptionLength = e.detail.value.length
  300. },
  301. selectAction(tag) {
  302. this.action = tag
  303. },
  304. selectEnvironment(tag) {
  305. this.environment = tag
  306. },
  307. selectImage(tag) {
  308. this.image = tag
  309. },
  310. selectStyle(index) {
  311. this.selectedStyle = index
  312. },
  313. generateImage() {
  314. if (!this.description) {
  315. uni.showToast({
  316. title: '请输入创作描述',
  317. icon: 'none'
  318. })
  319. return
  320. }
  321. // if (this.selectedStyle === -1) {
  322. // uni.showToast({
  323. // title: '请选择参考风格',
  324. // icon: 'none'
  325. // })
  326. // return
  327. // }
  328. let style = '';
  329. if (this.selectedStyle !== -1) {
  330. style = this.styleList[this.selectedStyle].name
  331. }
  332. let that = this
  333. uni.request({
  334. url: this.$apiHost + '/WorkAI/creatorLG',
  335. data: {
  336. uuid: getApp().globalData.uuid,
  337. description: this.description,
  338. action: this.action,
  339. environment: this.environment,
  340. subject: this.image,
  341. style: style
  342. },
  343. method: 'POST',
  344. header: {
  345. 'Content-Type': 'application/x-www-form-urlencoded',
  346. 'sign': getApp().globalData.headerSign
  347. },
  348. dataType: 'json',
  349. success: (res) => {
  350. console.log("生成结果:", res.data);
  351. uni.showToast({
  352. title: res.data.str || '请求成功',
  353. icon: 'none'
  354. });
  355. if (res.data.success == "yes") {
  356. // TODO: 处理生成成功后的逻辑
  357. setTimeout(function () {
  358. // that.checkQueueStatus()
  359. // uni.navigateBack()
  360. // 使用全局变量存储状态
  361. getApp().globalData.needSwitchToGenerating = true;
  362. uni.$emit('switchToMyPage', { type: 'generatingInProgress' });
  363. uni.switchTab({ url: '/pages/my/my' });
  364. }, 1500);
  365. }
  366. },
  367. fail: (err) => {
  368. console.log('生成失败:', err);
  369. uni.showToast({
  370. title: '生成请求失败',
  371. icon: 'none'
  372. });
  373. }
  374. })
  375. },
  376. getQueueDetail(id) {
  377. if (!id) {
  378. return
  379. }
  380. let that = this
  381. uni.request({
  382. url: this.$apiHost + '/WorkAI/getQueueDetail',
  383. data: {
  384. uuid: getApp().globalData.uuid,
  385. id: id
  386. },
  387. header: {
  388. 'Content-Type': 'application/x-www-form-urlencoded',
  389. 'sign': getApp().globalData.headerSign
  390. },
  391. dataType: 'json',
  392. success: (res) => {
  393. console.log("查询单个结果:", res.data);
  394. if (res.data.success == "yes") {
  395. var {id, queuePosition, allPosition, progress, description, action, environment, environment, style ,work_id} = res.data.data
  396. that.queuePosition = queuePosition
  397. that.allPosition = allPosition
  398. that.progress = progress
  399. that.description = description
  400. that.descriptionLength = description.length
  401. that.action = action
  402. that.environment = environment
  403. that.selectedStyle = that.styleList.findIndex(item => item.name == style)
  404. if (progress >=100 && work_id) {
  405. uni.showToast({
  406. title: '生成完成',
  407. icon: 'none'
  408. });
  409. that.clearPolling();
  410. setTimeout(function () {
  411. that.goPage("/pages/makedetail/makeDetail?id="+id +'&&queueId'+work_id)
  412. }, 500);
  413. }
  414. if (queuePosition == allPosition) {
  415. // 创作中逻辑
  416. that.inQueue = true
  417. } else if (queuePosition < allPosition) {
  418. // 排队中逻辑
  419. that.queuing = true
  420. }
  421. }
  422. },
  423. fail: (err) => {
  424. console.log('查询失败失败:', err);
  425. uni.showToast({
  426. title: '查询失败请求失败',
  427. icon: 'none'
  428. });
  429. }
  430. })
  431. },
  432. goPage(page) {
  433. uni.navigateTo({
  434. url: page,
  435. });
  436. },
  437. // 开始轮询
  438. startPolling(queueId) {
  439. this.clearPolling(); // 先清除可能存在的定时器
  440. this.timer = setInterval(() => {
  441. this.getQueueDetail( queueId);
  442. }, 5000); // 10秒轮询一次
  443. },
  444. // 清除轮询
  445. clearPolling() {
  446. if (this.timer) {
  447. clearInterval(this.timer);
  448. this.timer = null;
  449. }
  450. },
  451. }
  452. }
  453. </script>
  454. <style lang="scss">
  455. @import './makeImgDetail.scss';
  456. </style>