邃芒慧语、照片说话(PC) https://photo.metavatar.cc/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

2238 line
62 KiB

  1. <template>
  2. <div class="page flex justify-around">
  3. <!-- 左侧栏 -->
  4. <div class="left">
  5. <!-- 顶部 -->
  6. <!-- 选中AiFace -->
  7. <div class="left-top" v-show="showUploadBtn == 1">
  8. <el-row class="justify-between">
  9. <!-- title框 -->
  10. <div
  11. v-show="!submitImgBtn"
  12. class="flex justify-between"
  13. style="width: 300px"
  14. >
  15. <el-input
  16. maxlength="15"
  17. v-model="title"
  18. :placeholder="$t('createVideo.typeTitle')"
  19. />
  20. <el-icon class="icon-edit"><i-ep-edit /></el-icon>
  21. </div>
  22. </el-row>
  23. <!-- 中间展示大图 -->
  24. <div class="bigImg inmiddle">
  25. <img :src="imgUrl" alt="" class="inmiddle" />
  26. </div>
  27. </div>
  28. <!-- 选中需要上传的图片前 -->
  29. <div class="left-top" v-show="showUploadBtn == 2">
  30. <!-- 中间展示大图 -->
  31. <div class="noImg">
  32. <my-loading
  33. style="margin-top: 150px"
  34. :isLoading="compressionImgLoad"
  35. ></my-loading>
  36. <div v-if="!compressionImgLoad" class="trueImg inmiddle">
  37. <div class="imgBox">
  38. <img src="../../assets/img/trueImg.png" alt="" />
  39. </div>
  40. <div class="icon">✔</div>
  41. </div>
  42. <div v-if="!compressionImgLoad" class="upload inmiddle">
  43. +
  44. <input accept="image/*" type="file" @change="lookImg" />
  45. </div>
  46. <div v-if="!compressionImgLoad" class="falseImg inmiddle">
  47. <div>
  48. <div class="imgBox">
  49. <img src="../../assets/img/false1.png" alt="" />
  50. </div>
  51. <div class="icon">×</div>
  52. </div>
  53. <div>
  54. <div class="imgBox">
  55. <img src="../../assets/img/false2.png" alt="" />
  56. </div>
  57. <div class="icon">×</div>
  58. </div>
  59. <div>
  60. <div class="imgBox">
  61. <img src="../../assets/img/false3.png" alt="" />
  62. </div>
  63. <div class="icon">×</div>
  64. </div>
  65. </div>
  66. </div>
  67. </div>
  68. <!-- loading -->
  69. <div class="left-top" v-show="showUploadBtn == 3">
  70. <!-- 中间展示大图 -->
  71. <div class="noImg">
  72. <my-loading
  73. style="margin-top: 150px"
  74. :isLoading="compressionImgLoad"
  75. ></my-loading>
  76. <div v-if="!compressionImgLoad" class="trueImg inmiddle">
  77. <div class="imgBox">
  78. <img src="../../assets/img/trueImg.png" alt="" />
  79. </div>
  80. <div class="icon">✔</div>
  81. </div>
  82. <div v-if="!compressionImgLoad" class="upload inmiddle">
  83. +
  84. <input accept="image/*" type="file" @change="lookImg" />
  85. </div>
  86. <div v-if="!compressionImgLoad" class="falseImg inmiddle">
  87. <div>
  88. <div class="imgBox">
  89. <img src="../../assets/img/false1.png" alt="" />
  90. </div>
  91. <div class="icon">×</div>
  92. </div>
  93. <div>
  94. <div class="imgBox">
  95. <img src="../../assets/img/false2.png" alt="" />
  96. </div>
  97. <div class="icon">×</div>
  98. </div>
  99. <div>
  100. <div class="imgBox">
  101. <img src="../../assets/img/false3.png" alt="" />
  102. </div>
  103. <div class="icon">×</div>
  104. </div>
  105. </div>
  106. </div>
  107. </div>
  108. <!-- 选中需要上传的图片后 -->
  109. <div class="left-top" v-show="showUploadBtn == 4">
  110. <el-row style="display: flex; justify-content: flex-end">
  111. <!-- 上传按钮 废弃了-->
  112. <!-- <div v-if="false" class="btnBox" v-show="submitImgBtn"> -->
  113. <div v-if="false" class="btnBox">
  114. <button class="uploadBtn">
  115. {{ $t("createVideo.reselect") }}
  116. <input accept="image/*" type="file" @change="lookImg" />
  117. </button>
  118. <button class="uploadBtn" @click="changeFile">
  119. {{ $t("createVideo.sureUpload") }}
  120. </button>
  121. </div>
  122. </el-row>
  123. <!-- 中间展示大图 -->
  124. <div class="bigImg inmiddle">
  125. <img :src="imgUrl" alt="" class="inmiddle" />
  126. </div>
  127. </div>
  128. <!-- AI生成图片展示区 -->
  129. <div class="left-top" v-show="showUploadBtn == 5">
  130. <div class="editAiPicBox">
  131. <div class="edit">
  132. <div class="TagBox"></div>
  133. </div>
  134. <div class="AIpicBox">
  135. <div class="picBox">
  136. <img src="../../assets/img/trueImg.png" alt="" />
  137. </div>
  138. </div>
  139. </div>
  140. </div>
  141. <!-- 底部 -->
  142. <div class="left-bottom">
  143. <!-- tab栏 -->
  144. <div class="tab flex">
  145. <div
  146. class="pointer"
  147. :class="showFace == 0 ? 'tabAvtive' : ''"
  148. @click="chooSystemImg"
  149. >
  150. {{ $t("createVideo.availableMould") }}
  151. </div>
  152. <div
  153. v-if="AccessToken"
  154. class="pointer"
  155. :class="showFace == 1 ? 'tabAvtive' : ''"
  156. @click="chooseMyImg"
  157. >
  158. {{ $t("createVideo.personalWarehouse") }}
  159. </div>
  160. <div
  161. v-if="AccessToken"
  162. class="pointer aiPicTab"
  163. :class="showFace == 2 ? 'tabAvtive' : ''"
  164. @click="chooseAiPic"
  165. >
  166. <div class="subscript subscript1">
  167. <span>AI &nbsp;</span>
  168. <img src="../../assets/img/star.png" alt="" />
  169. </div>
  170. {{ $t("createVideo.Aipicture") }}
  171. </div>
  172. </div>
  173. <!-- 系统相册 -->
  174. <div
  175. class="faceList"
  176. v-if="showFace == 0"
  177. v-infinite-scroll="loadMoreAiFaceSystem"
  178. :infinite-scroll-disabled="overLoadSystem"
  179. >
  180. <!-- 上传图片组件 -->
  181. <div v-if="showFace == 1" class="faceItem" @click="clickAddBtn">
  182. <span class="uploadImg">+ </span>
  183. </div>
  184. <my-loading :isLoading="feceItemLoading"></my-loading>
  185. <!-- face列表 -->
  186. <div
  187. :class="faceItemActive == item.id ? 'faceItemActive' : ''"
  188. class="faceItem pointer"
  189. @click="setImgUrl(item.material, item.id)"
  190. v-for="item in AiFaceList"
  191. :key="item.id"
  192. >
  193. <div class="imgBox">
  194. <img :src="item.material" alt="" />
  195. </div>
  196. s
  197. </div>
  198. </div>
  199. <!-- 个人相册 -->
  200. <div
  201. class="faceList"
  202. v-if="showFace == 1"
  203. v-infinite-scroll="loadMoreAiFaceUser"
  204. :infinite-scroll-disabled="overLoadUser"
  205. >
  206. <!-- 上传图片组件 -->
  207. <div v-if="showFace == 1" class="faceItem" @click="clickAddBtn">
  208. <span class="uploadImg">+ </span>
  209. </div>
  210. <my-loading :isLoading="feceItemLoading2"></my-loading>
  211. <!-- face列表 -->
  212. <div
  213. :class="faceItemActive == item.id ? 'faceItemActive' : ''"
  214. class="faceItem pointer"
  215. @click="setImgUrl(item.material, item.id)"
  216. v-for="item in userAiFaceList"
  217. :key="item.id"
  218. >
  219. <div class="imgBox">
  220. <img :src="item.material" alt="" />
  221. </div>
  222. <div
  223. v-if="faceItemActive == item.id"
  224. @click="deleteFaceImg(item.id)"
  225. class="deleteIcon"
  226. >
  227. ✖︎
  228. </div>
  229. </div>
  230. </div>
  231. <!-- AI生成图片 -->
  232. <div
  233. class="Aipicture"
  234. v-if="showFace == 2"
  235. v-infinite-scroll="loadMoreAiFaceUser"
  236. :infinite-scroll-disabled="overLoadUser"
  237. >
  238. <div class="title">
  239. <el-icon><i-ep-Lock /></el-icon>
  240. {{ $t("createVideo.comingSoon") }}
  241. </div>
  242. </div>
  243. </div>
  244. </div>
  245. <!-- 右侧栏 -->
  246. <div class="right">
  247. <div v-if="AccessToken" class="subscript">
  248. <span>GPT</span>
  249. <img src="../../assets/img/star.png" alt="" />
  250. </div>
  251. <el-tabs type="border-card" class="tabs">
  252. <!-- ----------------------------- 1 -->
  253. <el-tab-pane :label="$t('createVideo.typeScript')">
  254. <div class="card1">
  255. <div class="text">
  256. <!-- 文本域 -->
  257. <el-input
  258. maxlength="3000"
  259. id="textInputId"
  260. v-model="textareaContent"
  261. :rows="15"
  262. type="textarea"
  263. :placeholder="$t('createVideo.aiText')"
  264. />
  265. <span
  266. class="pointer"
  267. @click="getPreviewAudio"
  268. style="margin-right: 10px; font-size: 20px"
  269. >🔊</span
  270. >
  271. <span class="pointer" @click="insertIcon">🕗</span>
  272. </div>
  273. <p>{{ $t("createVideo.languages") }}</p>
  274. <!-- 语言 -->
  275. <el-select
  276. @change="chooseType"
  277. v-model="language"
  278. :placeholder="$t('createVideo.select')"
  279. style="width: 100%"
  280. >
  281. <el-option
  282. v-for="item in languageOptions"
  283. :key="item.value"
  284. :label="item.name"
  285. :value="item.id"
  286. >
  287. <img
  288. :src="item.img"
  289. alt=""
  290. style="
  291. width: 20px;
  292. margin-right: 10px;
  293. vertical-align: text-top;
  294. "
  295. />
  296. <span style="line-height: 20px">{{ item.name }}</span>
  297. </el-option>
  298. </el-select>
  299. <p>{{ $t("createVideo.voices") }}</p>
  300. <!-- 声音 -->
  301. <el-select
  302. @change="chooseStyle"
  303. :disabled="language ? false : true"
  304. v-model="sound"
  305. :placeholder="$t('createVideo.select')"
  306. style="width: 100%"
  307. >
  308. <el-option
  309. v-for="item in soundOptions"
  310. :key="item.value"
  311. :label="item.displayName"
  312. :value="item.id"
  313. />
  314. </el-select>
  315. <p>{{ $t("createVideo.styles") }}</p>
  316. <!-- 风格 -->
  317. <el-select
  318. :disabled="soundStyleOptions ? false : true"
  319. v-model="soundStyle"
  320. :placeholder="$t('createVideo.select')"
  321. style="width: 100%"
  322. >
  323. <el-option
  324. v-for="item in soundStyleOptions"
  325. :key="item"
  326. :label="item"
  327. :value="item"
  328. />
  329. </el-select>
  330. <!-- 保存视频 -->
  331. <el-button
  332. @click="saveOrUpdate(1, 'saveVideo')"
  333. color="#000"
  334. type="primary"
  335. >
  336. <my-loading :isLoading="createLoading"></my-loading>
  337. <span v-if="!createLoading">{{
  338. $t("createVideo.saveVideo")
  339. }}</span></el-button
  340. >
  341. <!-- 生成视频 -->
  342. <el-button
  343. style="margin-left: 0"
  344. @click="saveOrUpdate(1)"
  345. color="#000"
  346. type="primary"
  347. >
  348. <my-loading :isLoading="createLoading"></my-loading>
  349. <span v-if="!createLoading">{{
  350. $t("createVideo.generateVideo")
  351. }}</span></el-button
  352. >
  353. </div>
  354. </el-tab-pane>
  355. <!-- ------------------------ 2 -->
  356. <el-tab-pane :label="$t('createVideo.uploadAudio')">
  357. <div class="card2">
  358. <!-- 上传音频 -->
  359. <p>
  360. <el-icon class="icon-Headset"><i-ep-Headset /></el-icon>
  361. {{ $t("createVideo.uploadAudio") }}
  362. </p>
  363. <div class="uploadVideo">
  364. +
  365. <input
  366. accept="audio/*"
  367. type="file"
  368. ref="fileInput"
  369. @click="e.target.value = null"
  370. @change="handleFileChange"
  371. />
  372. </div>
  373. <!-- 录制MP3上传语音 -->
  374. <p>
  375. <el-icon class="icon-Mic"><i-ep-Mic /></el-icon>
  376. {{ $t("createVideo.recordedAudio") }}
  377. </p>
  378. <div class="uploadAudio">
  379. <div class="btnBox">
  380. <button
  381. class="pointer"
  382. :disabled="!isRecording"
  383. @click="startRecording"
  384. >
  385. {{ $t("createVideo.startRecorded") }}
  386. </button>
  387. <button
  388. class="pointer"
  389. @click="stopRecording"
  390. :disabled="isRecording"
  391. >
  392. {{ $t("createVideo.stopRecorded") }}
  393. </button>
  394. </div>
  395. </div>
  396. <div class="AudioBox" v-if="audioUrl">
  397. <audio controls :src="audioUrl" id="uploadAudio"></audio>
  398. </div>
  399. <!-- 保存视频 -->
  400. <el-button
  401. @click="saveOrUpdate(2, 'saveVideo')"
  402. color="#000"
  403. type="primary"
  404. >
  405. <my-loading :isLoading="createLoading"></my-loading>
  406. <span v-if="!createLoading">{{
  407. $t("createVideo.saveVideo")
  408. }}</span></el-button
  409. >
  410. <!-- 生成视频 -->
  411. <el-button
  412. style="margin-left: 0"
  413. @click="saveOrUpdate(2)"
  414. color="#000"
  415. type="primary"
  416. >
  417. <my-loading :isLoading="createLoading"></my-loading>
  418. <span v-if="!createLoading">{{
  419. $t("createVideo.generateVideo")
  420. }}</span>
  421. </el-button>
  422. </div>
  423. </el-tab-pane>
  424. <!-- ----------------------- 3 -->
  425. <el-tab-pane :label="$t('createVideo.music')">
  426. <div class="card3">
  427. <!-- 选择音乐 -->
  428. <p>
  429. <el-icon class="icon-Headset"><i-ep-Headset /></el-icon>
  430. {{ $t("createVideo.chooseMusic") }}
  431. </p>
  432. <!-- tab栏 -->
  433. <div class="musicTab">
  434. <div
  435. class="musicTabItem"
  436. :class="musicTabActive == 1 ? 'musicTabItemActive' : ''"
  437. @click="musicTabActive = 1"
  438. >
  439. {{ $t("createVideo.hotMusic") }}
  440. </div>
  441. <div
  442. v-if="AccessToken"
  443. class="musicTabItem"
  444. :class="musicTabActive == 2 ? 'musicTabItemActive' : ''"
  445. @click="musicTabActive = 2"
  446. >
  447. {{ $t("createVideo.personalWarehouse") }}
  448. </div>
  449. </div>
  450. <!-- 上传音乐 -->
  451. <div v-if="musicTabActive == 2" class="uploadVideo">
  452. +
  453. <input
  454. accept="audio/*"
  455. type="file"
  456. ref="fileMusicInput"
  457. @click="e.target.value = null"
  458. @change="uploadMusic"
  459. />
  460. </div>
  461. <!-- 系统音乐列表展示 -->
  462. <div v-if="musicTabActive == 1" class="musicList">
  463. <div
  464. @click="chooseMusicUrl(item.url, item.id)"
  465. class="musicItem"
  466. :class="musicItemAction == item.id ? 'musicItemAvtive' : ''"
  467. v-for="item in musicListSystem"
  468. :key="item.id"
  469. >
  470. <span class="musicNmae"> 🎵{{ item.name }}</span>
  471. <span class="musicTime"> {{ handleTime(item.time) }}</span>
  472. </div>
  473. </div>
  474. <!-- 个人音乐列表展示 -->
  475. <div v-if="musicTabActive == 2" class="musicList">
  476. <div
  477. @click="chooseMusicUrl(item.url, item.id)"
  478. class="musicItem"
  479. :class="musicItemAction == item.id ? 'musicItemAvtive' : ''"
  480. v-for="item in musicListPerson"
  481. :key="item.id"
  482. >
  483. <span class="musicNmae"> 🎵{{ item.name }}</span>
  484. <span class="musicTime"> {{ handleTime(item.time) }}</span>
  485. </div>
  486. </div>
  487. <!-- 系统分页 -->
  488. <div v-if="musicTabActive == 1" class="pagination">
  489. <el-pagination
  490. v-model:current-page="musicListPageNumSystem"
  491. v-model:page-size="musicListPageSize"
  492. :small="false"
  493. :disabled="false"
  494. :background="true"
  495. layout="total, prev, pager, next, jumper"
  496. :total="musicListTotalSystem"
  497. @current-change="musicListPageNumSystemChange"
  498. />
  499. </div>
  500. <!-- 个人分页 -->
  501. <div v-if="musicTabActive == 2" class="pagination">
  502. <el-pagination
  503. v-model:current-page="musicListPageNumPerson"
  504. v-model:page-size="musicListPageSize"
  505. :small="false"
  506. :disabled="false"
  507. :background="true"
  508. layout="total, prev, pager, next, jumper"
  509. :total="musicListTotalPerson"
  510. @current-change="musicListPageNumPersonChange"
  511. />
  512. </div>
  513. <div class="AudioBox" v-if="audioMusicUrl">
  514. <audio controls :src="audioMusicUrl" id="uploadAudio"></audio>
  515. </div>
  516. <!-- 保存视频 -->
  517. <el-button
  518. @click="saveOrUpdate(3, 'saveVideo')"
  519. color="#000"
  520. type="primary"
  521. >
  522. <my-loading :isLoading="createLoading"></my-loading>
  523. <span v-if="!createLoading">{{
  524. $t("createVideo.saveVideo")
  525. }}</span></el-button
  526. >
  527. <!-- 生成视频 -->
  528. <el-button
  529. style="margin-left: 0"
  530. @click="saveOrUpdate(3)"
  531. color="#000"
  532. type="primary"
  533. >
  534. <my-loading :isLoading="createLoading"></my-loading>
  535. <span v-if="!createLoading">{{
  536. $t("createVideo.generateVideo")
  537. }}</span>
  538. </el-button>
  539. </div>
  540. </el-tab-pane>
  541. <!-- ----------------------- 4 -->
  542. <el-tab-pane v-if="AccessToken" :label="$t('createVideo.aiScript')">
  543. <div class="card4">
  544. <div class="text">
  545. <!-- 文本域 -->
  546. <el-input
  547. maxlength="3000"
  548. id="textInputId"
  549. v-model="textareaContent"
  550. :rows="15"
  551. type="textarea"
  552. :placeholder="$t('createVideo.aiText')"
  553. />
  554. <span
  555. class="pointer"
  556. @click="getPreviewAudio"
  557. style="margin-right: 10px; font-size: 20px"
  558. >🔊</span
  559. >
  560. <span class="pointer" @click="insertIcon">🕗</span>
  561. </div>
  562. <p>{{ $t("createVideo.languages") }}</p>
  563. <!-- 语言 -->
  564. <el-select
  565. @change="chooseType"
  566. v-model="language"
  567. :placeholder="$t('createVideo.select')"
  568. style="width: 100%"
  569. >
  570. <el-option
  571. v-for="item in languageOptions"
  572. :key="item.value"
  573. :label="item.name"
  574. :value="item.id"
  575. >
  576. <img
  577. :src="item.img"
  578. alt=""
  579. style="
  580. width: 20px;
  581. margin-right: 10px;
  582. vertical-align: text-top;
  583. "
  584. />
  585. <span style="line-height: 20px">{{ item.name }}</span>
  586. </el-option>
  587. </el-select>
  588. <p>{{ $t("createVideo.voices") }}</p>
  589. <!-- 声音 -->
  590. <el-select
  591. @change="chooseStyle"
  592. :disabled="language ? false : true"
  593. v-model="sound"
  594. :placeholder="$t('createVideo.select')"
  595. style="width: 100%"
  596. >
  597. <el-option
  598. v-for="item in soundOptions"
  599. :key="item.value"
  600. :label="item.displayName"
  601. :value="item.id"
  602. />
  603. </el-select>
  604. <p>{{ $t("createVideo.styles") }}</p>
  605. <!-- 风格 -->
  606. <el-select
  607. :disabled="soundStyleOptions ? false : true"
  608. v-model="soundStyle"
  609. :placeholder="$t('createVideo.select')"
  610. style="width: 100%"
  611. >
  612. <el-option
  613. v-for="item in soundStyleOptions"
  614. :key="item"
  615. :label="item"
  616. :value="item"
  617. />
  618. </el-select>
  619. <!-- 保存视频 -->
  620. <el-button
  621. @click="saveOrUpdate(1, 'saveVideo')"
  622. color="#000"
  623. type="primary"
  624. >
  625. <my-loading :isLoading="createLoading"></my-loading>
  626. <span v-if="!createLoading">{{
  627. $t("createVideo.saveVideo")
  628. }}</span></el-button
  629. >
  630. <!-- 生成视频 -->
  631. <el-button
  632. style="margin-left: 0"
  633. @click="saveOrUpdate(1)"
  634. color="#000"
  635. type="primary"
  636. >
  637. <my-loading :isLoading="createLoading"></my-loading>
  638. <span v-if="!createLoading">{{
  639. $t("createVideo.generateVideo")
  640. }}</span></el-button
  641. >
  642. </div>
  643. </el-tab-pane>
  644. <!-- -------------------------脚标 -->
  645. </el-tabs>
  646. </div>
  647. <!-- 试听的mp3 -->
  648. <div style="opacity: 0; width: 0">
  649. <audio autoplay :src="previewAudioUrl" id="audio"></audio>
  650. </div>
  651. </div>
  652. </template>
  653. <script setup>
  654. import { getCurrentInstance } from "vue";
  655. import {
  656. personPhotoList,
  657. personPhotoUserListList,
  658. awsImgUpload,
  659. delImgApi,
  660. videoUpload,
  661. saveImgApi,
  662. uploadProgressApi,
  663. saveAudioApi,
  664. voiceTotalApi,
  665. chooseTypeApi,
  666. saveOrUpdateApi,
  667. createVideoApi,
  668. previewAudioApi,
  669. findByIdApi,
  670. musicListSystemApi,
  671. musicListPersonApi,
  672. saveMusicApi,
  673. } from "@/apis/createVideo";
  674. import { useI18n } from "vue-i18n";
  675. import Recorder from "js-audio-recorder"; //mp3插件
  676. import imageCompression from "browser-image-compression"; //压缩图片插件
  677. import { useRoute, useRouter } from "vue-router";
  678. const route = useRoute();
  679. const router = useRouter();
  680. const { locale } = useI18n();
  681. const lanChange = ref(locale);
  682. const AccessToken = localStorage.getItem("AccessToken"); // 判断有没有登录
  683. //#region 头像区的逻辑
  684. // 获取AI头像
  685. const AiFaceList = ref([]); //系统相册列表
  686. const userAiFaceList = ref([]); //个人相册列表
  687. const feceItemLoading = ref(false);
  688. const feceItemLoading2 = ref(false);
  689. function getAiFaceList(pageNum, pageSize, sendType) {
  690. feceItemLoading.value = true;
  691. personPhotoList(pageNum, pageSize, sendType)
  692. .then((res) => {
  693. AiFaceList.value = res.data.list;
  694. feceItemLoading.value = false;
  695. })
  696. .catch((err) => {
  697. feceItemLoading.value = false;
  698. console.log(err, "err");
  699. });
  700. }
  701. function getAiFaceUserList(pageNum, pageSize, sendType) {
  702. feceItemLoading2.value = true;
  703. personPhotoUserListList(pageNum, pageSize, sendType)
  704. .then((res) => {
  705. userAiFaceList.value = res.data.list;
  706. feceItemLoading2.value = false;
  707. })
  708. .catch((err) => {
  709. feceItemLoading2.value = false;
  710. console.log(err, "err");
  711. });
  712. }
  713. const pageNumSystem = ref(1);
  714. const overLoadSystem = ref(false); //禁用系统相册加载更多
  715. const pageNumUser = ref(1);
  716. const overLoadUser = ref(false); //禁用个人相册加载更多
  717. //系统相册触底加载更多
  718. function loadMoreAiFaceSystem() {
  719. pageNumSystem.value += 1;
  720. personPhotoList(pageNumSystem.value, 10, 1)
  721. .then((res) => {
  722. AiFaceList.value.push(...res.data.list);
  723. if (res.data.list.length == 0) {
  724. overLoadSystem.value = true;
  725. pageNumSystem.value = pageNumSystem.value - 1;
  726. }
  727. })
  728. .catch((err) => {
  729. console.log(err, "err");
  730. });
  731. }
  732. //个人相册触底加载更多
  733. function loadMoreAiFaceUser() {
  734. pageNumUser.value += 1;
  735. personPhotoUserListList(pageNumUser.value, 10, 3)
  736. .then((res) => {
  737. if (res.data.list.length == 0) {
  738. overLoadUser.value = true;
  739. pageNumUser.value = pageNumUser.value - 1;
  740. }
  741. userAiFaceList.value.push(...res.data.list);
  742. })
  743. .catch((err) => {
  744. console.log(err, "err");
  745. });
  746. }
  747. // 选择显示的face列表
  748. const showFace = ref("0");
  749. function ChangeShowFaceList(index) {
  750. showFace.value = index;
  751. }
  752. const faceItemActive = ref(); //高亮处理和当前选择图片的ID
  753. // 点击aifece
  754. function setImgUrl(url, id) {
  755. faceItemActive.value = id;
  756. imgUrl.value = url;
  757. showUploadBtn.value = 1;
  758. submitImgBtn.value = false;
  759. }
  760. // 删除个人相册照片
  761. async function deleteFaceImg(id) {
  762. const msg =
  763. lanChange.value == "zh-cn"
  764. ? `确定要删除这张图片吗? `
  765. : `Are you sure to delete this image ? `;
  766. const notice = lanChange.value == "zh-cn" ? "提示" : "Notice";
  767. const confirm = lanChange.value == "zh-cn" ? "确定" : "Confirm";
  768. const cancel = lanChange.value == "zh-cn" ? "取消" : "Cancel";
  769. ElMessageBox.confirm(msg, notice, {
  770. confirmButtonText: confirm,
  771. cancelButtonText: cancel,
  772. }).then(async () => {
  773. try {
  774. const res = await delImgApi(id);
  775. imgUrl.value = null;
  776. faceItemActive.value = null;
  777. userAiFaceList.value = [];
  778. getAiFaceUserList(1, 10, 3);
  779. pageNumUser.value = 1;
  780. overLoadUser.value = false;
  781. } catch (error) {
  782. ElMessage.error(errpr);
  783. }
  784. });
  785. }
  786. //#endregion ------------------------------
  787. //#region 预览图片
  788. const compressionImgLoad = ref(false);
  789. async function lookImg(e) {
  790. if (e.target.files[0]) {
  791. faceItemActive.value = null;
  792. imgUrl.value = null;
  793. imgData.value = null;
  794. showUploadBtn.value = 3;
  795. submitImgBtn.value = false;
  796. compressionImgLoad.value = true;
  797. // 压缩配置
  798. const options = {
  799. maxSizeMB: 2, // 最大压缩大小为 1MB
  800. useWebWorker: true, // 使用 Web Worker 进行压缩,提高性能
  801. };
  802. const compressedFile = await imageCompression(e.target.files[0], options);
  803. imgUrl.value = URL.createObjectURL(compressedFile);
  804. imgData.value = compressedFile;
  805. showUploadBtn.value = 4;
  806. submitImgBtn.value = true;
  807. compressionImgLoad.value = false;
  808. await changeFile();
  809. } else {
  810. return;
  811. }
  812. }
  813. //#endregion ------------------
  814. // 标题
  815. const title =
  816. lanChange.value == "zh-cn" ? ref("未定义标题") : ref("Undefined Title");
  817. //#region 上传图片
  818. const imgUrl = ref(""); //图片回显路径
  819. const imgData = ref(""); // 上传图片文件
  820. const showUploadBtn = ref(1); //上传图片+号的显示隐藏
  821. const submitImgBtn = ref(false); //右上角俩按钮显示与隐藏
  822. // 点击系统仓库tab
  823. function chooSystemImg(params) {
  824. showFace.value = 0;
  825. showUploadBtn.value = 1;
  826. }
  827. // 上传图片
  828. async function changeFile() {
  829. compressionImgLoad.value = true;
  830. showUploadBtn.value = 3;
  831. imgUrl.value = URL.createObjectURL(imgData.value);
  832. let formData = new FormData();
  833. const timestamp = Date.now();
  834. formData.append("file", imgData.value, timestamp + ".png");
  835. try {
  836. // 上传图片
  837. const res = await awsImgUpload(formData);
  838. // 保存图片
  839. const res2 = await saveImgApi({ material: res.data.url });
  840. // 重新获取列表
  841. compressionImgLoad.value = false;
  842. showUploadBtn.value = 4;
  843. userAiFaceList.value = [];
  844. getAiFaceUserList(1, 10, 3);
  845. pageNumUser.value = 1;
  846. overLoadUser.value = false;
  847. if (res2.code == 200) {
  848. ElMessage.success(
  849. lanChange.value == "zh-cn" ? `上传成功` : `Upload successfully`
  850. );
  851. } else {
  852. ElMessage.error(
  853. lanChange.value == "zh-cn" ? `上传失败` : `Upload failure`
  854. );
  855. }
  856. faceItemActive.value = res2.data.id;
  857. imgUrl.value = res2.data.material;
  858. submitImgBtn.value = false;
  859. console.log(123654);
  860. showUploadBtn.value = 1;
  861. } catch (error) {
  862. ElMessage.error(error);
  863. }
  864. }
  865. // 点击个人仓库tab
  866. function chooseMyImg() {
  867. showFace.value = 1;
  868. showUploadBtn.value = 2;
  869. }
  870. // 点击AI头像区的+号
  871. function clickAddBtn() {
  872. faceItemActive.value = null;
  873. showUploadBtn.value = 2;
  874. }
  875. //#endregion ---------------------------
  876. //#region AI生成图片
  877. function chooseAiPic() {
  878. showFace.value = 2;
  879. // showUploadBtn.value = 5;
  880. }
  881. //#endregion --------------------------
  882. //#region card1的逻辑
  883. const textareaContent = ref("");
  884. function insertIcon() {
  885. const textarea = document.getElementById("textInputId");
  886. const icon = "🕗"; // 这里使用了一个星星作为示例图标,你可以替换为你自己的图标
  887. // 获取光标位置
  888. const startPos = textarea.selectionStart;
  889. const endPos = textarea.selectionEnd;
  890. console.log(startPos);
  891. console.log(endPos);
  892. // 在光标位置插入图标
  893. textareaContent.value =
  894. textareaContent.value.substring(0, startPos) +
  895. icon +
  896. textareaContent.value.substring(endPos, textareaContent.value.length);
  897. // 设置光标位置为插入图标后的位置
  898. const newCursorPos = startPos + icon.length;
  899. textarea.setSelectionRange(newCursorPos, newCursorPos);
  900. textarea.focus();
  901. }
  902. const language = ref(""); //选择的语言
  903. const sound = ref(""); // 选择的声音
  904. const soundStyle = ref(""); //选择的风格
  905. // 语言下拉框内容
  906. const languageOptions = ref([
  907. {
  908. id: "0",
  909. name: "中文",
  910. },
  911. {
  912. id: "1",
  913. name: "English",
  914. },
  915. ]);
  916. async function voiceTotal() {
  917. const res = await voiceTotalApi();
  918. languageOptions.value = res.data;
  919. }
  920. // 声音下拉框内容
  921. const soundOptions = ref([]);
  922. async function chooseType() {
  923. const res = await chooseTypeApi(language.value);
  924. soundOptions.value = res.data;
  925. sound.value = null;
  926. }
  927. // 风格下拉框内容
  928. const soundStyleOptions = ref(null);
  929. async function chooseStyle() {
  930. const item = soundOptions.value.find((element) => element.id == sound.value);
  931. soundStyle.value = null;
  932. soundStyleOptions.value = item.style;
  933. }
  934. // 试听
  935. const previewAudioUrl = ref(null);
  936. async function getPreviewAudio() {
  937. if (!textareaContent.value) {
  938. ElMessage.error(
  939. lanChange.value == "zh-cn"
  940. ? `您还没有输入文案`
  941. : `You have not entered the copy yet`
  942. );
  943. return;
  944. }
  945. if (!sound.value) {
  946. ElMessage.error(
  947. lanChange.value == "zh-cn"
  948. ? `您还没有选择声音`
  949. : `You have not selected the sound`
  950. );
  951. return;
  952. }
  953. try {
  954. const res = await previewAudioApi(
  955. textareaContent.value,
  956. sound.value,
  957. soundStyle.value
  958. );
  959. if (!res.data.data) {
  960. ElMessage.error(
  961. lanChange.value == "zh-cn"
  962. ? `暂时无法试听,请稍后重试`
  963. : `Unable to listen, please try again later`
  964. );
  965. }
  966. previewAudioUrl.value = res.data.data;
  967. document.getElementById("audio").play();
  968. } catch (error) {
  969. ElMessage.error(error);
  970. }
  971. }
  972. //#endregion
  973. //#region 录制MP3上传语音
  974. const parameter = {
  975. sampleBits: 16, // 采样位数,支持 8 或 16,默认是16
  976. sampleRate: 8000, // 采样率,支持 11025、16000、22050、24000、44100、48000,根据浏览器默认值,我的chrome是48000
  977. numChannels: 1, // 声道,支持 1 或 2, 默认是1
  978. };
  979. const recorder = new Recorder(parameter);
  980. const isRecording = ref(true);
  981. const audioUrl = ref(null); // audio控件的url
  982. const audioData = ref("");
  983. const videoId = ref(null); //上传mp3的videoId,创建视频会用到
  984. const startRecording = async () => {
  985. try {
  986. await recorder.start();
  987. console.log("Recording started");
  988. isRecording.value = false;
  989. } catch (error) {
  990. console.error(error);
  991. }
  992. };
  993. const stopRecording = async () => {
  994. try {
  995. const recording = await recorder.stop();
  996. audioUrl.value = URL.createObjectURL(recorder.getWAVBlob());
  997. audioData.value = recorder.getWAVBlob();
  998. isRecording.value = true;
  999. console.log(document.getElementById("uploadAudio").duration);
  1000. } catch (error) {
  1001. console.error(error);
  1002. }
  1003. };
  1004. const playRecording = () => {
  1005. if (audioUrl.value) {
  1006. const audio = new Audio(audioUrl.value);
  1007. audio.play();
  1008. }
  1009. };
  1010. // 上传音频
  1011. async function uploadAudio(isSaveVideo) {
  1012. createLoading.value = true;
  1013. let formData = new FormData();
  1014. const timestamp = Date.now();
  1015. formData.append("file", audioData.value, timestamp + ".mp3");
  1016. try {
  1017. const res = await videoUpload(formData);
  1018. videoId.value = res.data.videoId;
  1019. if (res) {
  1020. // ElMessage.success("上传成功!");
  1021. } else {
  1022. ElMessage.error(
  1023. lanChange.value == "zh-cn" ? `上传音频失败!` : `Failed to upload audio!`
  1024. );
  1025. return;
  1026. }
  1027. // 查询进度
  1028. intervalId = setInterval(() => {
  1029. uploadProgress(res.data.videoId, isSaveVideo);
  1030. }, 1000);
  1031. // ElMessage.success("上传成功!");
  1032. } catch (error) {
  1033. ElMessage.error(error);
  1034. }
  1035. }
  1036. //#endregion ------------------------------------
  1037. //#region 上传音频功能
  1038. const fileInput = ref(null);
  1039. let intervalId = null; // 用于存储 setInterval 的返回值
  1040. const uploadAudioTime = ref(null); // 音频时长
  1041. const handleFileChange = async () => {
  1042. const file = fileInput.value.files[0];
  1043. if (file) {
  1044. const videoUrl = URL.createObjectURL(file);
  1045. audioUrl.value = videoUrl;
  1046. audioData.value = file;
  1047. const audio2 = new Audio();
  1048. audio2.src = videoUrl;
  1049. countAudioTime(audio2);
  1050. }
  1051. };
  1052. // 计算音频的时长
  1053. const countAudioTime = async (audio) => {
  1054. while (isNaN(audio.duration) || audio.duration === Infinity) {
  1055. // 延迟一会 不然网页都卡死
  1056. await new Promise((resolve) => setTimeout(resolve, 200));
  1057. // 设置随机播放时间,模拟调进度条
  1058. audio.currentTime = 10000000 * Math.random();
  1059. }
  1060. // console.log("音频的总时长:", Math.floor(audio.duration));
  1061. uploadAudioTime.value = Math.floor(audio.duration);
  1062. };
  1063. // 获取视频上传进度
  1064. const afterSaveAudioId = ref(null);
  1065. async function uploadProgress(videoId, isSaveVideo) {
  1066. const res = await uploadProgressApi(videoId);
  1067. if (res.data == "complete") {
  1068. clearInterval(intervalId);
  1069. try {
  1070. const res2 = await saveAudioApi({ videoId: videoId });
  1071. afterSaveAudioId.value = res2.data.id; // 得到id
  1072. } catch (error) {
  1073. ElMessage.error(error);
  1074. }
  1075. // 定义参数
  1076. const saveOrUpdateData = {
  1077. id: id.value, //从草稿或者重新编辑回到创建视频时有id
  1078. title: title.value,
  1079. personPhotoId: faceItemActive.value, //任务照片id,
  1080. voiceFrom: tabFrom.value, //:声音来源 1:来自选择模板,2:来自用户上传
  1081. paperwork: textareaContent.value, //文案
  1082. speakTypeStr: soundStyle.value, //声音风格
  1083. voiceMouldId: sound.value, //选择声音id voiceFrom=1 时必传
  1084. voiceMaterialId: afterSaveAudioId.value, //上传音频且保存音频后得到的id voiceFrom=2 时必传
  1085. };
  1086. try {
  1087. const res3 = await saveOrUpdateApi(saveOrUpdateData); // 保存数据
  1088. // 这个if判断是不是仅仅保存视频
  1089. if (isSaveVideo == "saveVideo") {
  1090. ElMessage.success(
  1091. lanChange.value == "zh-cn" ? `已保存为草稿` : `Saved as manuscript`
  1092. );
  1093. createLoading.value = false;
  1094. router.push("/myCreating");
  1095. return;
  1096. }
  1097. const res4 = await createVideoApi({ id: res3.data.id }); // 生成视频
  1098. if (res4.message == "success") {
  1099. ElMessage.success(
  1100. lanChange.value == "zh-cn"
  1101. ? `视频正在生成中,请耐心等待`
  1102. : `The video is being generated, please wait patiently`
  1103. );
  1104. }
  1105. createLoading.value = false;
  1106. router.push("/myCreating");
  1107. } catch (error) {
  1108. ElMessage.error(error);
  1109. createLoading.value = false;
  1110. }
  1111. }
  1112. }
  1113. //#endregion --------------------------------------
  1114. //#region 生成视频
  1115. // tab栏切换
  1116. const tabFrom = ref(1);
  1117. const createLoading = ref(false);
  1118. // 上传音频后,得到videoId
  1119. // 保存数据
  1120. async function saveOrUpdate(index, isSaveVideo) {
  1121. if (createLoading.value == true) {
  1122. return;
  1123. }
  1124. tabFrom.value = index; // 判断是哪一个tab栏内的生成视频按钮
  1125. const saveOrUpdateData = {
  1126. id: id.value, //从草稿或者重新编辑回到创建视频时有id
  1127. title: title.value,
  1128. personPhotoId: faceItemActive.value, //任务照片id,
  1129. voiceFrom: tabFrom.value, //:声音来源 1:来自选择模板,2:来自用户上传
  1130. paperwork: textareaContent.value, //文案
  1131. speakTypeStr: soundStyle.value, //声音风格
  1132. voiceMouldId: sound.value, //选择声音id voiceFrom=1 时必传
  1133. voiceMaterialId: afterSaveAudioId.value, //上传音频且保存音频后得到的id voiceFrom=2 时必传
  1134. musicId: musicItemAction.value, // 选中音乐的id,voiceFrom=3 时必传
  1135. };
  1136. if (index == 1) {
  1137. createLoading.value = true;
  1138. if (!saveOrUpdateData.personPhotoId) {
  1139. ElMessage.error(
  1140. lanChange.value == "zh-cn" ? `请选择图片` : `Please select a picture`
  1141. );
  1142. createLoading.value = false;
  1143. return;
  1144. }
  1145. if (!saveOrUpdateData.paperwork) {
  1146. ElMessage.error(
  1147. lanChange.value == "zh-cn" ? `请输入文案` : `Please enter copy`
  1148. );
  1149. createLoading.value = false;
  1150. return;
  1151. }
  1152. if (!saveOrUpdateData.voiceMouldId) {
  1153. ElMessage.error(
  1154. lanChange.value == "zh-cn" ? `请选择声音` : `Please select sound`
  1155. );
  1156. createLoading.value = false;
  1157. return;
  1158. }
  1159. try {
  1160. const res2 = await saveOrUpdateApi(saveOrUpdateData); // 保存数据
  1161. if (res2.code == 1002) {
  1162. }
  1163. // 这个if判断是不是仅仅保存视频
  1164. if (isSaveVideo == "saveVideo") {
  1165. ElMessage.success(
  1166. lanChange.value == "zh-cn" ? `已保存为草稿` : `Saved as manuscript`
  1167. );
  1168. createLoading.value = false;
  1169. router.push("/myCreating");
  1170. return;
  1171. }
  1172. const res3 = await createVideoApi({ id: res2.data.id }); // 生成视频
  1173. if (res3.message == "success") {
  1174. ElMessage.success(
  1175. lanChange.value == "zh-cn"
  1176. ? `视频正在生成中,请耐心等待`
  1177. : `The video is being generated, please wait patiently`
  1178. );
  1179. }
  1180. createLoading.value = false;
  1181. router.push("/myCreating");
  1182. } catch (error) {
  1183. // ElMessage.error($t(error.message));
  1184. ElMessage.error(error);
  1185. createLoading.value = false;
  1186. }
  1187. }
  1188. if (index == 2) {
  1189. if (!saveOrUpdateData.personPhotoId) {
  1190. ElMessage.error(
  1191. lanChange.value == "zh-cn" ? `请选择图片` : `Please select a picture`
  1192. );
  1193. createLoading.value = false;
  1194. return;
  1195. }
  1196. if (!saveOrUpdateData.voiceMaterialId) {
  1197. ElMessage.error(
  1198. lanChange.value == "zh-cn"
  1199. ? `请选择音频文件`
  1200. : `Please select an audio file`
  1201. );
  1202. createLoading.value = false;
  1203. return;
  1204. }
  1205. try {
  1206. const res1 = await uploadAudio(isSaveVideo); // 上传音频
  1207. } catch (error) {
  1208. ElMessage.error(error);
  1209. createLoading.value = false;
  1210. }
  1211. }
  1212. if (index == 3) {
  1213. try {
  1214. if (!saveOrUpdateData.personPhotoId) {
  1215. ElMessage.error(
  1216. lanChange.value == "zh-cn" ? `请选择图片` : `Please select a picture`
  1217. );
  1218. createLoading.value = false;
  1219. return;
  1220. }
  1221. if (!saveOrUpdateData.musicId) {
  1222. ElMessage.error(
  1223. lanChange.value == "zh-cn"
  1224. ? `请选择音乐文件`
  1225. : `Please select an music file`
  1226. );
  1227. createLoading.value = false;
  1228. return;
  1229. }
  1230. const res1 = await saveOrUpdateApi(saveOrUpdateData); // 保存数据
  1231. // 这个if判断是不是仅仅保存视频
  1232. if (isSaveVideo == "saveVideo") {
  1233. ElMessage.success(
  1234. lanChange.value == "zh-cn" ? `已保存为草稿` : `Saved as manuscript`
  1235. );
  1236. createLoading.value = false;
  1237. router.push("/myCreating");
  1238. return;
  1239. }
  1240. console.log("ceshi");
  1241. const res2 = await createVideoApi({ id: res1.data.id }); // 生成视频
  1242. if (res2.message == "success") {
  1243. ElMessage.success(
  1244. lanChange.value == "zh-cn"
  1245. ? `视频正在生成中,请耐心等待`
  1246. : `The video is being generated, please wait patiently`
  1247. );
  1248. }
  1249. createLoading.value = false;
  1250. router.push("/myCreating");
  1251. } catch (error) {
  1252. ElMessage.error(error);
  1253. createLoading.value = false;
  1254. }
  1255. }
  1256. }
  1257. //#endregion ----------------------------
  1258. //#region 根据作品id查询作品信息并回显
  1259. const id = ref(null); //作品的id
  1260. async function findById(findId) {
  1261. const res = await findByIdApi(findId);
  1262. id.value = res.data.id;
  1263. title.value = res.data.title; //标题回显
  1264. faceItemActive.value = res.data.personPhotoId; //aiface高亮和图片id
  1265. tabFrom.value = res.data.voiceFrom; //输入文案还是上传mp3模式
  1266. textareaContent.value = res.data.paperwork; //文案内容
  1267. afterSaveAudioId.value = res.data.voiceMaterialId; //保存音频后得到的id
  1268. imgUrl.value = res.data.personPhotoUrl; //图片回显
  1269. audioUrl.value = res.data.voiceMaterialUrl; // mp3回显
  1270. musicItemAction.value = res.data.musicId; //音乐id回显
  1271. language.value = res.data.voiceInfo ? res.data.voiceInfo.languageId : null;
  1272. if (language.value) {
  1273. await chooseType(language.value);
  1274. }
  1275. sound.value = res.data.voiceMouldId; //声音id
  1276. chooseStyle();
  1277. soundStyle.value = res.data.speakTypeStr; //选择风格
  1278. }
  1279. //#endregion ----------------------------
  1280. //#region 音乐tab
  1281. const musicListSystem = ref(null); //展示音乐列表
  1282. const musicListPerson = ref(null); //展示音乐列表
  1283. const audioMusicUrl = ref(null); //选中音乐Url回显
  1284. const musicItemAction = ref(null); //选中高亮
  1285. const musicListPageNumSystem = ref(1); //系统音乐页数
  1286. const musicListTotalSystem = ref(10); //系统音乐总条数
  1287. const musicListPageNumPerson = ref(1); //个人音乐页数
  1288. const musicListTotalPerson = ref(10); //个人音乐总条数
  1289. const musicListPageSize = ref(8); //
  1290. const musicTabActive = ref(1); //热门歌曲和个人仓库高亮
  1291. const musicData = ref(null); //上传歌曲的数据
  1292. const fileMusicInput = ref(null); // 上传组件的dom
  1293. // 获取系统音乐列表
  1294. async function getmusicListSystem(pageNum, pageSize) {
  1295. try {
  1296. const res = await musicListSystemApi(pageNum, pageSize);
  1297. musicListSystem.value = res.data.list;
  1298. musicListTotalSystem.value = Number(res.data.total);
  1299. } catch (error) {
  1300. console.log(error);
  1301. }
  1302. }
  1303. // 获取个人音乐列表
  1304. async function getmusicListPerson(pageNum, pageSize) {
  1305. try {
  1306. const res = await musicListPersonApi(pageNum, pageSize);
  1307. musicListPerson.value = res.data.list;
  1308. musicListTotalPerson.value = Number(res.data.total);
  1309. } catch (error) {
  1310. console.log(error);
  1311. }
  1312. }
  1313. // 系统页数发生改变
  1314. const musicListPageNumSystemChange = (val) => {
  1315. musicListPageNumSystem.value = val;
  1316. getmusicListSystem(musicListPageNumSystem.value, musicListPageSize.value);
  1317. };
  1318. // 个人页数发生改变
  1319. const musicListPageNumPersonChange = (val) => {
  1320. musicListPageNumPerson.value = val;
  1321. getmusicListPerson(musicListPageNumPerson.value, musicListPageSize.value);
  1322. };
  1323. // 点击选择音乐
  1324. function chooseMusicUrl(url, id) {
  1325. audioMusicUrl.value = url;
  1326. musicItemAction.value = id;
  1327. }
  1328. // 转化秒为分秒
  1329. function handleTime(time) {
  1330. let m = Math.floor(time / 60).toFixed(0);
  1331. let s = (time % 60).toFixed(0);
  1332. if (s < 10) {
  1333. s = "0" + s;
  1334. }
  1335. return m + ":" + s;
  1336. }
  1337. // 选择文件上传音乐
  1338. async function uploadMusic() {
  1339. const file = fileMusicInput.value.files[0];
  1340. if (file) {
  1341. const videoUrl = URL.createObjectURL(file);
  1342. musicData.value = file;
  1343. let formData = new FormData();
  1344. formData.append("file", musicData.value, file.name + ".mp3");
  1345. try {
  1346. const res = await videoUpload(formData);
  1347. if (res) {
  1348. // ElMessage.success("上传成功!");
  1349. } else {
  1350. ElMessage.error(
  1351. lanChange.value == "zh-cn"
  1352. ? `上传音频失败!`
  1353. : `Failed to upload audio!`
  1354. );
  1355. return;
  1356. }
  1357. // 查询进度
  1358. intervalId = setInterval(async () => {
  1359. const res2 = await uploadProgressApi(res.data.videoId);
  1360. if (res2.data == "complete") {
  1361. clearInterval(intervalId);
  1362. try {
  1363. const res3 = await saveMusicApi({ voiceId: res.data.videoId });
  1364. afterSaveAudioId.value = res2.data.id; // 得到id
  1365. } catch (error) {
  1366. ElMessage.error(error);
  1367. }
  1368. }
  1369. }, 1000);
  1370. musicListPageNumPerson.value = 1;
  1371. const res4 = await musicListPersonApi(
  1372. musicListPageNumPerson.value,
  1373. musicListPageSize.value
  1374. );
  1375. musicListPerson.value = res4.data.list;
  1376. musicListTotalPerson.value = Number(res4.data.total);
  1377. ElMessage.success(
  1378. lanChange.value == "zh-cn" ? `上传成功!` : `Upload successfully`
  1379. );
  1380. } catch (error) {
  1381. ElMessage.error(error);
  1382. }
  1383. }
  1384. }
  1385. //#endregion ----------------------------
  1386. //#region
  1387. //#endregion ----------------------------
  1388. //#region
  1389. //#endregion ----------------------------
  1390. // $t在script中的使用
  1391. const {
  1392. appContext: {
  1393. config: { globalProperties },
  1394. },
  1395. } = getCurrentInstance();
  1396. const string = "createVideo.typeScript";
  1397. const t = globalProperties.$t(string);
  1398. onMounted(async () => {
  1399. getAiFaceList(1, 10, 1);
  1400. if (AccessToken) {
  1401. getAiFaceUserList(1, 10, 3);
  1402. await getmusicListPerson(
  1403. musicListPageNumPerson.value,
  1404. musicListPageSize.value
  1405. );
  1406. }
  1407. await voiceTotal();
  1408. if (route.query.id) {
  1409. await findById(route.query.id);
  1410. }
  1411. await getmusicListSystem(
  1412. musicListPageNumSystem.value,
  1413. musicListPageSize.value
  1414. );
  1415. musicListSystem.value.forEach((item) => {
  1416. if (item.id == musicItemAction.value) {
  1417. audioMusicUrl.value = item.url;
  1418. }
  1419. });
  1420. musicListPerson.value.forEach((item) => {
  1421. if (item.id == musicItemAction.value) {
  1422. audioMusicUrl.value = item.url;
  1423. }
  1424. });
  1425. });
  1426. </script>
  1427. <style lang="scss" scoped>
  1428. .page {
  1429. height: 1200px;
  1430. // height: calc(100vh - 80px);
  1431. // padding: 20px 0 0 40px;
  1432. background-color: #fff;
  1433. border-radius: 25px 0 0;
  1434. }
  1435. .left {
  1436. width: 68%;
  1437. height: 90%;
  1438. padding: 20px;
  1439. :deep(.el-input__wrapper) {
  1440. border: none;
  1441. border-bottom: 2px solid #000;
  1442. border-radius: 0;
  1443. box-shadow: none;
  1444. }
  1445. .el-icon.icon-edit {
  1446. width: 35px;
  1447. height: 35px;
  1448. font-size: 35px;
  1449. color: #8c939d;
  1450. text-align: center;
  1451. }
  1452. &-top {
  1453. position: relative;
  1454. width: 100%;
  1455. // height: 65%;
  1456. // height: 580px;
  1457. height: 65%;
  1458. .uploadBtn {
  1459. position: relative;
  1460. padding: 10px 15px;
  1461. margin-left: 10px;
  1462. border-radius: 5px;
  1463. transition: all 0.3s;
  1464. background-color: #000;
  1465. color: #fff;
  1466. &:hover {
  1467. background-color: #15d1b8;
  1468. }
  1469. }
  1470. .bigImg {
  1471. display: flex;
  1472. align-items: center;
  1473. justify-content: space-between;
  1474. width: 80%;
  1475. height: 88%;
  1476. margin-top: 20px;
  1477. img {
  1478. max-width: 100%;
  1479. max-height: 100%;
  1480. border-radius: 15px;
  1481. }
  1482. }
  1483. .noImg {
  1484. position: relative;
  1485. width: 100%;
  1486. height: 100%;
  1487. border: 1px solid #ccc;
  1488. border-radius: 30px;
  1489. .imgBox {
  1490. position: relative;
  1491. width: 100%;
  1492. height: 100%;
  1493. border-radius: 50%;
  1494. overflow: hidden;
  1495. border: 1px solid #ccc;
  1496. img {
  1497. position: absolute;
  1498. top: 50%;
  1499. left: 50%;
  1500. transform: translate(-50%, -50%);
  1501. max-width: 120%;
  1502. }
  1503. }
  1504. .trueImg {
  1505. position: relative;
  1506. top: 60px;
  1507. width: 100px;
  1508. height: 100px;
  1509. border-radius: 50%;
  1510. .icon {
  1511. position: absolute;
  1512. top: 0%;
  1513. right: -5%;
  1514. border: 2px solid #00ff95;
  1515. color: #00ff95;
  1516. }
  1517. }
  1518. .el-upload {
  1519. position: absolute;
  1520. top: 0;
  1521. left: 0;
  1522. width: 100%;
  1523. height: 100%;
  1524. opacity: 0;
  1525. }
  1526. .upload {
  1527. top: 200px;
  1528. width: 100px;
  1529. height: 100px;
  1530. font-size: 50px;
  1531. line-height: 100px;
  1532. text-align: center;
  1533. background-color: #ccc;
  1534. border-radius: 50%;
  1535. }
  1536. .falseImg {
  1537. top: 350px;
  1538. // background-color: #795562;
  1539. display: flex;
  1540. justify-content: space-evenly;
  1541. width: 700px;
  1542. height: 100px;
  1543. > div {
  1544. position: relative;
  1545. width: 100px;
  1546. height: 100px;
  1547. background-color: #ccc;
  1548. border-radius: 50%;
  1549. .icon {
  1550. position: absolute;
  1551. top: 0%;
  1552. right: -5%;
  1553. border: 2px solid red;
  1554. color: red;
  1555. }
  1556. }
  1557. }
  1558. }
  1559. // 公共类名
  1560. input {
  1561. position: absolute;
  1562. top: 0;
  1563. left: 0;
  1564. width: 100%;
  1565. height: 100%;
  1566. opacity: 0;
  1567. cursor: pointer;
  1568. }
  1569. .icon {
  1570. position: absolute;
  1571. right: 2px;
  1572. width: 30px;
  1573. height: 30px;
  1574. font-size: 20px;
  1575. font-weight: 700;
  1576. line-height: 26px;
  1577. text-align: center;
  1578. border: 2px solid #000;
  1579. border-radius: 50%;
  1580. }
  1581. .editAiPicBox {
  1582. position: relative;
  1583. display: flex;
  1584. width: 100%;
  1585. height: 100%;
  1586. border: 1px solid #ccc;
  1587. background-color: #f1f2f6;
  1588. border-radius: 30px;
  1589. overflow: hidden;
  1590. .edit {
  1591. width: 50%;
  1592. height: 100%;
  1593. background-color: #fff;
  1594. .TagBox {
  1595. width: 100%;
  1596. height: 100%;
  1597. border-radius: 0 0 30px 0;
  1598. background-color: #f1f2f6;
  1599. }
  1600. }
  1601. .AIpicBox {
  1602. position: relative;
  1603. width: 50%;
  1604. height: 100%;
  1605. border-radius: 30px 0 0 0;
  1606. background-color: #fff;
  1607. .picBox {
  1608. position: relative;
  1609. width: 100%;
  1610. height: 100%;
  1611. border-radius: 30px 0 0 0;
  1612. background-color: #fff;
  1613. img {
  1614. position: absolute;
  1615. top: 50%;
  1616. left: 50%;
  1617. transform: translate(-50%, -50%);
  1618. max-width: 100%;
  1619. max-height: 100%;
  1620. border-radius: 30px;
  1621. }
  1622. }
  1623. }
  1624. }
  1625. }
  1626. &-bottom {
  1627. width: 100%;
  1628. height: 32%;
  1629. margin-top: 10px;
  1630. .tab {
  1631. padding: 10px;
  1632. > div {
  1633. margin-right: 20px;
  1634. padding: 10px 15px;
  1635. transition: all 0.3s;
  1636. border-radius: 5px;
  1637. &:hover {
  1638. background-color: #000;
  1639. color: #fff;
  1640. }
  1641. }
  1642. .tabAvtive {
  1643. background-color: #000;
  1644. color: #fff;
  1645. }
  1646. }
  1647. .ai {
  1648. width: 100%;
  1649. }
  1650. .faceList {
  1651. padding: 8px;
  1652. display: flex;
  1653. flex-wrap: wrap;
  1654. justify-content: flex-start;
  1655. width: 100%;
  1656. height: 180px;
  1657. overflow-y: scroll;
  1658. .faceItem {
  1659. position: relative;
  1660. margin-right: 30px;
  1661. margin-bottom: 35px;
  1662. width: 150px;
  1663. height: 150px;
  1664. // padding: 0 25px;
  1665. line-height: 150px;
  1666. text-align: center;
  1667. border-radius: 50%;
  1668. background-color: #fbf8f8;
  1669. // border: 2px solid #fff;
  1670. box-shadow: rgba(0, 0, 0, 0.3) 0px 0px 5px 5px;
  1671. // overflow: hidden;
  1672. transition: all 0.3s;
  1673. .imgBox {
  1674. cursor: pointer;
  1675. position: absolute;
  1676. top: 0;
  1677. left: 0;
  1678. right: 0;
  1679. bottom: 0;
  1680. z-index: 3;
  1681. width: 150px;
  1682. height: 150px;
  1683. border-radius: 50%;
  1684. background-color: #fbf8f8;
  1685. overflow: hidden;
  1686. img {
  1687. position: absolute;
  1688. top: 50%;
  1689. left: 50%;
  1690. transform: translate(-50%, -50%);
  1691. width: 100%;
  1692. }
  1693. }
  1694. .deleteIcon {
  1695. position: absolute;
  1696. top: 0;
  1697. right: 15px;
  1698. z-index: 999;
  1699. width: 25px;
  1700. height: 25px;
  1701. border: 2px solid #6d76ef;
  1702. border-radius: 50%;
  1703. color: #6d76ef;
  1704. font-size: 20px;
  1705. line-height: 23px;
  1706. cursor: pointer;
  1707. }
  1708. .uploadImg {
  1709. line-height: 145px;
  1710. font-size: 50px;
  1711. font-weight: 700;
  1712. text-align: center;
  1713. }
  1714. }
  1715. .faceItemActive {
  1716. position: relative;
  1717. transition: all 0.3s;
  1718. &::before {
  1719. content: "";
  1720. position: absolute;
  1721. // top: -3px;
  1722. // right: -7px;
  1723. // left: -3px;
  1724. // bottom: -7px;
  1725. top: -3px;
  1726. right: -3px;
  1727. left: -3px;
  1728. bottom: -3px;
  1729. z-index: 2;
  1730. border-radius: 50%;
  1731. background: linear-gradient(
  1732. 225deg,
  1733. rgb(81 217 202 / 100%),
  1734. rgb(114 100 245 / 100%)
  1735. );
  1736. }
  1737. }
  1738. }
  1739. .Aipicture {
  1740. width: 100%;
  1741. height: 180px;
  1742. .title {
  1743. font-size: 30px;
  1744. text-align: center;
  1745. line-height: 80px;
  1746. background-image: -webkit-linear-gradient(
  1747. left,
  1748. #4b61dc,
  1749. #15d1b8 25%,
  1750. #4b61dc 50%,
  1751. #15d1b8 75%,
  1752. #4b61dc
  1753. ) !important;
  1754. -webkit-text-fill-color: transparent !important;
  1755. -webkit-background-clip: text !important;
  1756. background-clip: text !important;
  1757. -webkit-background-size: 200% 100% !important;
  1758. background-size: 200% 100% !important;
  1759. .el-icon {
  1760. vertical-align: text-top;
  1761. font-size: 32px;
  1762. }
  1763. }
  1764. }
  1765. .aiPicTab {
  1766. position: relative;
  1767. z-index: 2;
  1768. .subscript {
  1769. position: absolute;
  1770. top: -20px;
  1771. right: -20px;
  1772. width: 40px;
  1773. height: 40px;
  1774. z-index: 1;
  1775. img {
  1776. position: absolute;
  1777. top: 0;
  1778. left: 0px;
  1779. width: 100%;
  1780. height: 100%;
  1781. z-index: 3;
  1782. }
  1783. span {
  1784. position: absolute;
  1785. line-height: 45px;
  1786. z-index: 4;
  1787. padding-left: 0px;
  1788. padding-bottom: 5px;
  1789. font-size: 16px;
  1790. color: #17ceb9;
  1791. font-weight: 900;
  1792. transform: rotate(45deg);
  1793. }
  1794. }
  1795. .subscript1 {
  1796. position: absolute;
  1797. top: -20px;
  1798. right: -20px;
  1799. width: 40px;
  1800. height: 40px;
  1801. z-index: 1;
  1802. img {
  1803. position: absolute;
  1804. top: 0;
  1805. left: 0px;
  1806. width: 100%;
  1807. height: 100%;
  1808. z-index: 3;
  1809. }
  1810. span {
  1811. position: absolute;
  1812. line-height: 45px;
  1813. z-index: 4;
  1814. padding-left: 6px;
  1815. padding-bottom: 5px;
  1816. font-size: 16px;
  1817. color: #17ceb9;
  1818. font-weight: 900;
  1819. transform: rotate(45deg);
  1820. }
  1821. }
  1822. }
  1823. }
  1824. }
  1825. .right {
  1826. width: 32%;
  1827. min-height: 100%;
  1828. padding: 30px 30px 20px 30px;
  1829. background-color: #f1f2f6;
  1830. border-radius: 30px 0 0 0;
  1831. position: relative;
  1832. z-index: 2;
  1833. .subscript {
  1834. position: absolute;
  1835. top: 30px;
  1836. right: 45px;
  1837. width: 40px;
  1838. height: 40px;
  1839. z-index: 3;
  1840. img {
  1841. position: absolute;
  1842. top: 0;
  1843. left: 0px;
  1844. width: 100%;
  1845. height: 100%;
  1846. z-index: 3;
  1847. }
  1848. span {
  1849. position: absolute;
  1850. line-height: 45px;
  1851. z-index: 4;
  1852. padding-left: 0px;
  1853. padding-bottom: 5px;
  1854. font-size: 16px;
  1855. color: #17ceb9;
  1856. font-weight: 900;
  1857. transform: rotate(45deg);
  1858. }
  1859. }
  1860. :deep(.el-tabs--border-card) {
  1861. font-size: 20px;
  1862. border: none;
  1863. }
  1864. .tabs {
  1865. min-height: 100%;
  1866. padding: 20px 20px 0px 20px;
  1867. border-radius: 20px;
  1868. overflow-y: scroll;
  1869. .card1 {
  1870. .text {
  1871. padding: 10px;
  1872. border: 1px solid #c0c4cc;
  1873. .el-button {
  1874. float: right;
  1875. margin-top: 10px;
  1876. }
  1877. :deep(.el-textarea__inner) {
  1878. resize: none;
  1879. box-shadow: none;
  1880. }
  1881. }
  1882. p {
  1883. margin: 7px 0;
  1884. }
  1885. > .el-button {
  1886. width: 100%;
  1887. height: 50px;
  1888. margin-top: 10px;
  1889. }
  1890. :deep(.el-input .is-focus) {
  1891. // box-shadow: none !important;
  1892. // box-shadow: 0 0 0 1px #000 !important;
  1893. }
  1894. }
  1895. .card2 {
  1896. padding: 10px;
  1897. .el-icon {
  1898. // color: red;
  1899. vertical-align: text-top;
  1900. }
  1901. > div {
  1902. position: relative;
  1903. margin-top: 10px;
  1904. width: 100%;
  1905. border: #000 1px solid;
  1906. border-radius: 10px;
  1907. overflow: hidden;
  1908. transition: all 0.3s;
  1909. box-shadow: #00000026 2px 2px 2px 2px;
  1910. &:hover {
  1911. transform: translateY(-10px);
  1912. }
  1913. }
  1914. .uploadAudio {
  1915. padding: 20px;
  1916. // margin-top: 30px;
  1917. margin-bottom: 20px;
  1918. height: 70px;
  1919. .btnBox {
  1920. display: flex;
  1921. justify-content: space-between;
  1922. // justify-content: flex-end;
  1923. margin-bottom: 10px;
  1924. }
  1925. audio {
  1926. height: 50px;
  1927. width: 100%;
  1928. }
  1929. button {
  1930. border-radius: 10px;
  1931. height: 30px;
  1932. line-height: 22px;
  1933. padding: 5px 10px;
  1934. background-color: #fff;
  1935. transition: all 0.3s;
  1936. &:hover {
  1937. background-color: #000;
  1938. color: #fff;
  1939. }
  1940. }
  1941. }
  1942. .uploadAudioAvtion {
  1943. height: 170px;
  1944. }
  1945. .uploadVideo {
  1946. text-align: center;
  1947. margin-bottom: 20px;
  1948. height: 100px;
  1949. font-size: 50px;
  1950. font-weight: 700;
  1951. line-height: 100px;
  1952. input {
  1953. position: absolute;
  1954. top: 0;
  1955. left: 0;
  1956. width: 100%;
  1957. height: 100%;
  1958. opacity: 0;
  1959. }
  1960. video {
  1961. position: absolute;
  1962. top: 0;
  1963. left: 0;
  1964. z-index: -1;
  1965. width: 100%;
  1966. height: 100%;
  1967. }
  1968. }
  1969. .AudioBox {
  1970. display: flex;
  1971. justify-content: space-around;
  1972. padding: 20px;
  1973. transition: all 0.3s;
  1974. }
  1975. > .el-button {
  1976. width: 100%;
  1977. height: 50px;
  1978. margin-top: 10px;
  1979. // background-color: #000;
  1980. }
  1981. }
  1982. .card3 {
  1983. width: 100%;
  1984. padding: 10px;
  1985. .el-icon {
  1986. vertical-align: text-top;
  1987. }
  1988. .musicTab {
  1989. display: flex;
  1990. width: 100%;
  1991. height: 50px;
  1992. line-height: 50px;
  1993. text-align: center;
  1994. .musicTabItem {
  1995. background-color: #f5f7fa;
  1996. width: 50%;
  1997. cursor: pointer;
  1998. }
  1999. .musicTabItemActive {
  2000. background-color: #000;
  2001. color: #fff;
  2002. }
  2003. }
  2004. .uploadVideo {
  2005. position: relative;
  2006. margin-top: 10px;
  2007. width: 100%;
  2008. border: #000 1px solid;
  2009. border-radius: 10px;
  2010. overflow: hidden;
  2011. transition: all 0.3s;
  2012. box-shadow: #00000026 2px 2px 2px 2px;
  2013. &:hover {
  2014. transform: translateY(-10px);
  2015. }
  2016. text-align: center;
  2017. margin-bottom: 20px;
  2018. height: 100px;
  2019. font-size: 50px;
  2020. font-weight: 700;
  2021. line-height: 100px;
  2022. input {
  2023. position: absolute;
  2024. top: 0;
  2025. left: 0;
  2026. width: 100%;
  2027. height: 100%;
  2028. opacity: 0;
  2029. }
  2030. }
  2031. .musicList {
  2032. width: 100%;
  2033. display: flex;
  2034. flex-wrap: wrap;
  2035. justify-content: space-between;
  2036. .musicItem {
  2037. width: 100%;
  2038. height: 30px;
  2039. position: relative;
  2040. margin-top: 10px;
  2041. padding: 0 20px;
  2042. border: #000 1px solid;
  2043. border-radius: 10px;
  2044. overflow: hidden;
  2045. transition: all 0.3s;
  2046. box-shadow: #00000026 2px 2px 2px 2px;
  2047. transition: all 0.3s;
  2048. cursor: pointer;
  2049. .musicNmae {
  2050. display: inline-block;
  2051. width: 280px;
  2052. margin-right: 20px;
  2053. overflow: hidden;
  2054. text-overflow: ellipsis;
  2055. white-space: nowrap;
  2056. }
  2057. .musicTime {
  2058. display: inline-block;
  2059. max-width: 300px;
  2060. overflow: hidden;
  2061. text-overflow: ellipsis;
  2062. white-space: nowrap;
  2063. }
  2064. }
  2065. .musicItemAvtive {
  2066. background-color: #000;
  2067. color: #fff;
  2068. }
  2069. }
  2070. .pagination {
  2071. // position: fixed;
  2072. // bottom: 20px;
  2073. // left: 50%;
  2074. // transform: translateX(-50%);
  2075. margin-top: 10px;
  2076. background-color: #ffffff;
  2077. padding: 10px 20px;
  2078. border-radius: 8px;
  2079. transition: all 0.3s;
  2080. cursor: pointer;
  2081. &:hover {
  2082. background-color: #b8b8b873;
  2083. }
  2084. }
  2085. .AudioBox {
  2086. display: flex;
  2087. justify-content: space-around;
  2088. width: 100%;
  2089. padding: 20px;
  2090. transition: all 0.3s;
  2091. audio {
  2092. width: 100%;
  2093. }
  2094. }
  2095. > .el-button {
  2096. width: 100%;
  2097. height: 50px;
  2098. margin-top: 10px;
  2099. // background-color: #000;
  2100. }
  2101. :deep(.el-input__wrapper) {
  2102. border: none;
  2103. border-bottom: 2px solid #000;
  2104. border-radius: 0;
  2105. box-shadow: none;
  2106. }
  2107. .el-icon.icon-edit {
  2108. width: 30px;
  2109. height: 30px;
  2110. font-size: 30px;
  2111. color: #8c939d;
  2112. text-align: center;
  2113. cursor: pointer;
  2114. transition: all 0.3s;
  2115. &:hover {
  2116. color: #292929;
  2117. }
  2118. }
  2119. }
  2120. .card4 {
  2121. .text {
  2122. padding: 10px;
  2123. border: 1px solid #000;
  2124. .el-button {
  2125. float: right;
  2126. margin-top: 10px;
  2127. }
  2128. :deep(.el-textarea__inner) {
  2129. resize: none;
  2130. box-shadow: none;
  2131. }
  2132. }
  2133. p {
  2134. margin: 7px 0;
  2135. }
  2136. > .el-button {
  2137. width: 100%;
  2138. height: 50px;
  2139. margin-top: 10px;
  2140. }
  2141. :deep(.el-input .is-focus) {
  2142. // box-shadow: none !important;
  2143. // box-shadow: 0 0 0 1px #000 !important;
  2144. }
  2145. }
  2146. :deep(.el-tabs__item.is-active) {
  2147. color: #909393;
  2148. background-color: #000;
  2149. }
  2150. :deep(.el-tabs__nav) {
  2151. width: 100%;
  2152. }
  2153. :deep(.el-tabs__item) {
  2154. width: 26%;
  2155. }
  2156. :deep(.el-tabs__item:hover) {
  2157. color: #909393;
  2158. }
  2159. :deep(.el-tabs__content) {
  2160. height: calc(100% - 60px);
  2161. padding: 0;
  2162. }
  2163. }
  2164. }
  2165. </style>