邃芒慧语、照片说话(PC) https://photo.metavatar.cc/
Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.
 
 
 
 
 

2607 righe
73 KiB

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