makeDetail.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679
  1. <template>
  2. <view class="page">
  3. <!-- 引入FontAwesome -->
  4. <view>
  5. <link rel="stylesheet"
  6. href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
  7. </view>
  8. <!-- 顶部导航栏 -->
  9. <view class="custom-navbar">
  10. <view class="navbar-left" @click="goBack">
  11. <text class="fa fa-angle-left"></text>
  12. </view>
  13. <view class="navbar-center">
  14. <view class="navbar-title">
  15. <image class="navbar-avatar" :src="myinfo.avatar" mode="aspectFill"></image>
  16. <text class="navbar-text">{{ myinfo.nickname }}</text>
  17. <text class="navbar-badge" v-if="myinfo.is_vip > 0">VIP</text>
  18. </view>
  19. </view>
  20. <view class="navbar-right" @click="showActionSheet">
  21. <text class="fa fa-ellipsis-h"></text>
  22. </view>
  23. </view>
  24. <!-- 内容头图区域 -->
  25. <view class="topUser">
  26. <image :src="home_image" class="home_image" mode="aspectFill"></image>
  27. <!-- 图片指示器 -->
  28. <!-- <view class="image-indicator" v-if="image_list.length > 1">
  29. <text>{{ selImg + 1 }}/{{ image_list.length }}</text>
  30. </view> -->
  31. <!-- 音乐类型时显示歌词 -->
  32. <view class="lyrics-overlay" v-if="queueDetail.task_type == 2" @mousedown="startDrag" @mouseup="stopDrag" @mousemove="drag">
  33. <text class="lyrics-text" ref="lyricsText" :style="{ transform: `translateY(${offsetY}px)` }">{{ queueDetail.description }}</text>
  34. </view>
  35. <!-- 音乐类型且状态为9时显示播放按钮 -->
  36. <view class="play-button" v-if="queueDetail.task_type == 2 && queueDetail.status >= 9" @click="toggleAudio">
  37. <text class="fa" :class="isPlaying ? 'fa-pause' : 'fa-play'"></text>
  38. </view>
  39. </view>
  40. <view class="body">
  41. <view class="article-header">
  42. <text class="title">{{ queueDetail.title }}</text>
  43. <view class="meta-info">
  44. <view class="meta-item">
  45. <text class="fa fa-clock-o"></text>
  46. <text class="meta-text">{{ queueDetail.create_time }}</text>
  47. </view>
  48. <view class="meta-item">
  49. <text class="fa fa-tag"></text>
  50. <text class="meta-text">{{ queueDetail.task_type == 1 ? '灵感创作' : '音乐创作' }}</text>
  51. </view>
  52. <view class="meta-item">
  53. <text class="fa fa-circle" :style="getStatusStyle(queueDetail.status)"></text>
  54. <text class="meta-text">{{ getStatusText(queueDetail.status) }}</text>
  55. </view>
  56. </view>
  57. </view>
  58. <view class="divider"></view>
  59. <view class="article-content">
  60. <!-- 灵感创作显示description -->
  61. <view v-if="queueDetail.task_type == 1">
  62. <text class="content">{{ queueDetail.description }}</text>
  63. </view>
  64. <!-- 音乐创作显示lyrics -->
  65. <view v-if="queueDetail.task_type == 2">
  66. <text class="content">{{ queueDetail.lyrics }}</text>
  67. </view>
  68. <!-- 显示创作详情 -->
  69. <view class="creation-details" v-if="queueDetail.task_type == 1">
  70. <view class="detail-item" v-if="queueDetail.action">
  71. <text class="detail-label">行为动作:</text>
  72. <text class="detail-value">{{ queueDetail.action }}</text>
  73. </view>
  74. <view class="detail-item" v-if="queueDetail.environment">
  75. <text class="detail-label">主体环境:</text>
  76. <text class="detail-value">{{ queueDetail.environment }}</text>
  77. </view>
  78. <view class="detail-item" v-if="queueDetail.subject">
  79. <text class="detail-label">主体形象:</text>
  80. <text class="detail-value">{{ queueDetail.subject }}</text>
  81. </view>
  82. <view class="detail-item" v-if="queueDetail.style">
  83. <text class="detail-label">参考风格:</text>
  84. <text class="detail-value">{{ queueDetail.style }}</text>
  85. </view>
  86. </view>
  87. <!-- 音乐创作显示歌曲名称 -->
  88. <view class="creation-details" v-if="queueDetail.task_type == 2">
  89. <view class="detail-item" v-if="queueDetail.song_name">
  90. <text class="detail-label">歌曲名称:</text>
  91. <text class="detail-value">{{ queueDetail.song_name }}</text>
  92. </view>
  93. </view>
  94. <!-- 显示状态信息 -->
  95. <view class="status-info" v-if="queueDetail.status < 4">
  96. <view class="queue-info">
  97. <text class="queue-text">队列位置:
  98. {{ queueDetail.queue_position }}/{{ queueDetail.all_position }}</text>
  99. <view class="progress-bar">
  100. <view class="progress-fill" :style="{ width: getProgressWidth() }"></view>
  101. </view>
  102. </view>
  103. </view>
  104. <!-- 显示错误信息 -->
  105. <view class="error-message" v-if="queueDetail.status == 3">
  106. <text class="error-text">{{ queueDetail.error_msg }}</text>
  107. </view>
  108. </view>
  109. </view>
  110. <view class="thread2"></view>
  111. <!-- 音频元素 -->
  112. <audio id="audioPlayer" :src="queueDetail.result_audio" style="display:none;"></audio>
  113. <!-- 底部漂浮栏 -->
  114. <view class="floating-bar" v-if="queueDetail.status == 9">
  115. <view class="floating-bar-content">
  116. <view class="add-note-btn" @click="showAddNotePopup">
  117. <text>添加说明</text>
  118. </view>
  119. <view class="publish-btn" @click="publishWork">
  120. <text>公布作品</text>
  121. </view>
  122. </view>
  123. </view>
  124. <!-- 添加说明弹窗 -->
  125. <view class="popup-mask" v-if="showNotePopup" @click="closeAddNotePopup"></view>
  126. <view class="note-popup" v-if="showNotePopup">
  127. <view class="popup-header">
  128. <text class="popup-title">添加说明</text>
  129. </view>
  130. <view class="popup-content">
  131. <textarea class="note-textarea" v-model="noteContent" placeholder="请描述你想添加的内容。"
  132. maxlength="500"></textarea>
  133. <view class="word-count">{{noteContent.length}}/500</view>
  134. </view>
  135. <view class="popup-footer">
  136. <view class="cancel-btn" @click="closeAddNotePopup">
  137. <text>取消</text>
  138. </view>
  139. <view class="confirm-btn" @click="confirmAddNote">
  140. <text>确认</text>
  141. </view>
  142. </view>
  143. </view>
  144. </view>
  145. </template>
  146. <script>
  147. import previewImage from '@/components/kxj-previewImage/kxj-previewImage.vue'; //引用插件
  148. export default {
  149. components: {
  150. previewImage
  151. },
  152. data() {
  153. return {
  154. title: '',
  155. arcID: 0,
  156. selImg: 0,
  157. home_image: '',
  158. myinfo: {},
  159. tag_list: [],
  160. image_list: [],
  161. imgs: [],
  162. descs: [],
  163. isPlaying: false,
  164. audioPlayer: null,
  165. showNotePopup: false,
  166. noteContent: '',
  167. // 队列详情数据
  168. queueDetail: {
  169. id: 0,
  170. sso_id: 0,
  171. task_type: 1,
  172. title: '',
  173. description: '',
  174. action: '',
  175. environment: '',
  176. subject: '',
  177. style: '',
  178. song_name: '',
  179. lyrics: '',
  180. generate_uuid: '',
  181. result_images: '',
  182. result_audio: '',
  183. queue_position: 0,
  184. status: 1,
  185. generate_status: 1,
  186. points_cost: 0,
  187. error_msg: '',
  188. create_time: '',
  189. update_time: '',
  190. all_position: 0
  191. },
  192. myinfo: {},
  193. offsetY: 0,
  194. isDragging: false,
  195. startY: 0,
  196. initialOffsetY: 0
  197. }
  198. },
  199. onLoad(parms) {
  200. let self = this;
  201. this.arcID = parms.id || 3;
  202. this.getMyInfo();
  203. },
  204. onShow() {
  205. this.loadData();
  206. },
  207. onReady() {
  208. // 获取音频元素
  209. this.audioPlayer = uni.createInnerAudioContext();
  210. this.audioPlayer.onEnded(() => {
  211. this.isPlaying = false;
  212. });
  213. },
  214. onUnload() {
  215. // 页面卸载时停止音频播放
  216. if (this.audioPlayer) {
  217. this.audioPlayer.stop();
  218. this.audioPlayer.destroy();
  219. }
  220. },
  221. methods: {
  222. // 返回上一页
  223. goBack() {
  224. uni.navigateBack({
  225. delta: 1
  226. });
  227. },
  228. getMyInfo() {
  229. uni.request({
  230. url: this.$apiHost + '/My/getnum',
  231. method: 'GET',
  232. header: {
  233. 'content-type': 'application/json',
  234. 'sign': getApp().globalData.headerSign
  235. },
  236. data: {
  237. uuid: getApp().globalData.uuid
  238. },
  239. success: (res) => {
  240. console.log("获取用户信息:", res.data);
  241. this.myinfo = res.data
  242. }
  243. })
  244. },
  245. selPhoto(item, sel) {
  246. this.selImg = sel;
  247. this.home_image = this.image_list[sel];
  248. },
  249. toArr(imgs) {
  250. let arr = imgs.split("|");
  251. return arr;
  252. },
  253. previewOpen(imgs1, index) {
  254. this.imgs = imgs1.split("|");
  255. setTimeout(() => this.$refs.previewImage.open(index), 0)
  256. // 传入当前选中的图片地址或序号
  257. return; //如需测试和uni原生预览差别可注释这两行
  258. },
  259. // 切换音频播放状态
  260. toggleAudio() {
  261. if (!this.queueDetail.result_audio) return;
  262. if (this.isPlaying) {
  263. this.audioPlayer.pause();
  264. this.isPlaying = false;
  265. } else {
  266. this.audioPlayer.src = this.queueDetail.result_audio;
  267. this.audioPlayer.play();
  268. this.isPlaying = true;
  269. }
  270. },
  271. // 获取状态文本
  272. getStatusText(status) {
  273. const statusMap = {
  274. 1: '排队中',
  275. 2: '生成中',
  276. 3: '生成失败',
  277. 4: '已完成',
  278. 9: '已完成'
  279. };
  280. return statusMap[status] || '未知状态';
  281. },
  282. // 获取状态样式
  283. getStatusStyle(status) {
  284. const colorMap = {
  285. 1: '#ffa500', // 橙色 - 排队中
  286. 2: '#2979ff', // 蓝色 - 生成中
  287. 3: '#ff5151', // 红色 - 生成失败
  288. 4: '#4caf50', // 绿色 - 已完成
  289. 9: '#4caf50' // 绿色 - 已完成
  290. };
  291. return `color: ${colorMap[status] || '#999'}`;
  292. },
  293. // 获取进度条宽度
  294. getProgressWidth() {
  295. if (this.queueDetail.all_position === 0) return '0%';
  296. const progress = (1 - (this.queueDetail.queue_position / this.queueDetail.all_position)) * 100;
  297. return `${progress}%`;
  298. },
  299. // 加载数据
  300. loadData() {
  301. uni.showLoading({
  302. title: '加载中...'
  303. });
  304. uni.request({
  305. url: this.$apiHost + '/WorkAI/getQueueDetail',
  306. data: {
  307. uuid: getApp().globalData.uuid,
  308. id: this.arcID
  309. },
  310. header: {
  311. "content-type": "application/json",
  312. 'sign': getApp().globalData.headerSign
  313. },
  314. success: (res) => {
  315. console.log("队列详情:", res.data);
  316. if (res.data.success === "yes") {
  317. // 更新队列详情
  318. this.queueDetail = res.data.data;
  319. this.noteContent = res.data.data.content;
  320. // 更新图片列表
  321. if (this.queueDetail.result_images && this.queueDetail.result_images !== "") {
  322. this.image_list = this.queueDetail.result_images.split(",");
  323. this.home_image = this.image_list[0];
  324. } else {
  325. this.home_image = "../../static/home/avator.png";
  326. }
  327. // 如果是音频类型,设置音频源
  328. if (this.queueDetail.task_type == 2 && this.queueDetail.result_audio) {
  329. this.audioPlayer.src = this.queueDetail.result_audio;
  330. }
  331. } else {
  332. uni.showToast({
  333. title: '获取详情失败',
  334. icon: 'none'
  335. });
  336. }
  337. },
  338. complete: () => {
  339. uni.hideLoading();
  340. },
  341. fail: (e) => {
  342. console.log("请求失败:", e);
  343. uni.showToast({
  344. title: '网络请求失败',
  345. icon: 'none'
  346. });
  347. }
  348. });
  349. },
  350. showActionSheet() {
  351. // 显示操作列表
  352. uni.showActionSheet({
  353. itemList: ['修改封面', '删除作品'],
  354. success: (res) => {
  355. switch (res.tapIndex) {
  356. case 0:
  357. // 修改封面
  358. this.editCover();
  359. break;
  360. case 1:
  361. // 删除作品
  362. this.deleteWork();
  363. break;
  364. }
  365. },
  366. fail: (res) => {
  367. console.log(res.errMsg);
  368. }
  369. });
  370. },
  371. // 修改封面
  372. editCover() {
  373. var _self = this;
  374. uni.chooseImage({
  375. count: 1,
  376. sizeType: ['compressed'],
  377. sourceType: ['album', 'camera'],
  378. success: function(res) {
  379. console.log('res:', res)
  380. if (res.tempFilePaths.length > 0) {
  381. _self.imglocal = res.tempFilePaths[0]
  382. const tempFilePaths = res.tempFilePaths[0];
  383. console.log('tempFilePaths:', tempFilePaths);
  384. const uploadTask = uni.uploadFile({
  385. url: _self.$apiHost + '/Xweb/upload_img?skey=' + _self.skey,
  386. filePath: res.tempFilePaths[0],
  387. name: 'file',
  388. success: function(uploadFileRes) {
  389. let resdata = JSON.parse(uploadFileRes.data)
  390. console.log('Success11:', uploadFileRes);
  391. console.log('Success21:', resdata);
  392. if (resdata.success == 'yes') {
  393. _self.home_image = resdata.url;
  394. // 调用修改封面接口
  395. uni.request({
  396. url: _self.$apiHost + '/WorkAI/queueAction',
  397. method: 'GET',
  398. data: {
  399. uuid: getApp().globalData.uuid,
  400. act: 'editImg',
  401. result_images: resdata.url,
  402. id: _self.arcID
  403. },
  404. header: {
  405. 'content-type': 'application/json',
  406. 'sign': getApp().globalData.headerSign
  407. },
  408. success: (res) => {
  409. if (res.data.success === "yes") {
  410. uni.showToast({
  411. title: '修改封面成功',
  412. icon: 'success'
  413. });
  414. } else {
  415. uni.showToast({
  416. title: '修改封面失败',
  417. icon: 'none'
  418. });
  419. }
  420. },
  421. fail: () => {
  422. uni.showToast({
  423. title: '修改封面失败',
  424. icon: 'none'
  425. });
  426. }
  427. });
  428. }
  429. },
  430. fail: function(uploadFileFail) {
  431. console.log('Error:', uploadFileFail.data);
  432. uni.showToast({
  433. title: '图片上传失败',
  434. icon: 'none'
  435. });
  436. }
  437. });
  438. }
  439. },
  440. error: function(e) {
  441. console.log(e);
  442. uni.showToast({
  443. title: '选择图片失败',
  444. icon: 'none'
  445. });
  446. }
  447. });
  448. },
  449. // 删除作品
  450. deleteWork() {
  451. // 显示确认对话框
  452. uni.showModal({
  453. title: '确认删除',
  454. content: '确定要删除这个作品吗?',
  455. confirmColor: '#ff5151',
  456. success: (res) => {
  457. if (res.confirm) {
  458. // 用户点击确定,执行删除操作
  459. this.confirmDelete();
  460. }
  461. }
  462. });
  463. },
  464. // 确认删除
  465. confirmDelete() {
  466. uni.showLoading({
  467. title: '删除中...'
  468. });
  469. uni.request({
  470. url: this.$apiHost + '/WorkAI/queueAction',
  471. method: 'GET',
  472. data: {
  473. uuid: getApp().globalData.uuid,
  474. act: 'del',
  475. id: this.arcID
  476. },
  477. header: {
  478. 'content-type': 'application/json',
  479. 'sign': getApp().globalData.headerSign
  480. },
  481. success: (res) => {
  482. uni.hideLoading();
  483. if (res.data.success === "yes") {
  484. uni.showToast({
  485. title: '删除成功',
  486. icon: 'success'
  487. });
  488. // 删除成功后返回上一页
  489. setTimeout(() => {
  490. uni.navigateBack({
  491. delta: 1
  492. });
  493. }, 1500);
  494. } else {
  495. uni.showToast({
  496. title: '删除失败',
  497. icon: 'none'
  498. });
  499. }
  500. },
  501. fail: () => {
  502. uni.hideLoading();
  503. uni.showToast({
  504. title: '删除失败',
  505. icon: 'none'
  506. });
  507. }
  508. });
  509. },
  510. // 显示添加说明弹窗
  511. showAddNotePopup() {
  512. this.showNotePopup = true;
  513. },
  514. // 关闭添加说明弹窗
  515. closeAddNotePopup() {
  516. this.showNotePopup = false;
  517. },
  518. // 确认添加说明
  519. confirmAddNote() {
  520. if (!this.noteContent.trim()) {
  521. uni.showToast({
  522. title: '请输入说明内容',
  523. icon: 'none'
  524. });
  525. return;
  526. }
  527. uni.showLoading({
  528. title: '保存中...'
  529. });
  530. uni.request({
  531. url: this.$apiHost + '/WorkAI/queueAction',
  532. method: 'GET',
  533. data: {
  534. uuid: getApp().globalData.uuid,
  535. act: 'editContent',
  536. content: this.noteContent,
  537. id: this.arcID
  538. },
  539. header: {
  540. 'content-type': 'application/json',
  541. 'sign': getApp().globalData.headerSign
  542. },
  543. success: (res) => {
  544. uni.hideLoading();
  545. if (res.data.success === "yes") {
  546. uni.showToast({
  547. title: '添加说明成功',
  548. icon: 'success'
  549. });
  550. this.showNotePopup = false;
  551. // this.noteContent = '';
  552. // 重新加载数据
  553. this.loadData();
  554. } else {
  555. uni.showToast({
  556. title: '添加说明失败',
  557. icon: 'none'
  558. });
  559. }
  560. },
  561. fail: () => {
  562. uni.hideLoading();
  563. uni.showToast({
  564. title: '添加说明失败',
  565. icon: 'none'
  566. });
  567. }
  568. });
  569. },
  570. // 发布作品
  571. publishWork() {
  572. uni.showLoading({
  573. title: '发布中...'
  574. });
  575. uni.request({
  576. url: this.$apiHost + '/WorkAI/queueAction',
  577. method: 'GET',
  578. data: {
  579. uuid: getApp().globalData.uuid,
  580. act: 'fabu',
  581. id: this.arcID
  582. },
  583. header: {
  584. 'content-type': 'application/json',
  585. 'sign': getApp().globalData.headerSign
  586. },
  587. success: (res) => {
  588. console.log("resddd", res.data);
  589. uni.hideLoading();
  590. if (res.data.success === "yes") {
  591. uni.showToast({
  592. title: '发布成功',
  593. icon: 'success'
  594. });
  595. // 重新加载数据
  596. // this.loadData();
  597. } else {
  598. uni.showToast({
  599. title: res.data.str || '发布失败',
  600. icon: 'none'
  601. });
  602. }
  603. },
  604. fail: () => {
  605. uni.hideLoading();
  606. uni.showToast({
  607. title: '发布失败',
  608. icon: 'none'
  609. });
  610. }
  611. });
  612. },
  613. startDrag(event) {
  614. this.isDragging = true;
  615. this.startY = event.clientY;
  616. this.initialOffsetY = this.offsetY || 0;
  617. },
  618. stopDrag() {
  619. this.isDragging = false;
  620. },
  621. drag(event) {
  622. if (this.isDragging) {
  623. const deltaY = event.clientY - this.startY;
  624. const newOffsetY = this.initialOffsetY + deltaY;
  625. // Get the height of the lyrics text
  626. const lyricsTextHeight = this.$refs.lyricsText ? this.$refs.lyricsText.clientHeight : 0;
  627. // Define the threshold limits based on the height of the lyrics text
  628. const minY = -lyricsTextHeight + 20; // Allow some space above
  629. const maxY = 20; // Allow some space below
  630. // Apply the limits
  631. this.offsetY = Math.min(Math.max(newOffsetY, minY), maxY);
  632. // Log the height and current offset
  633. console.log('Lyrics Text Height:', lyricsTextHeight);
  634. console.log('Current Offset Y:', this.offsetY);
  635. }
  636. }
  637. }
  638. }
  639. </script>
  640. <style scoped lang="scss">
  641. @import 'makeDetail.scss';
  642. </style>