邃芒慧语、照片说话(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.
 
 
 
 
 

712 regels
19 KiB

  1. <template>
  2. <div class="page">
  3. <!-- 搜索框 -->
  4. <div class="top">
  5. <div class="flex justify-between" style="width: 300px">
  6. <el-input
  7. v-model="title"
  8. :placeholder="$t('myCreating.search')"
  9. @keyup.enter="searchVideoList"
  10. />
  11. <el-icon class="icon-edit" @click="searchVideoList"
  12. ><i-ep-search
  13. /></el-icon>
  14. </div>
  15. </div>
  16. <!-- 作品列表 -->
  17. <div v-if="!isLoading">
  18. <div v-if="videoList.length > 0" class="workList flex">
  19. <div class="workItem" v-for="item in videoList" :key="item.id">
  20. <div class="workImg">
  21. <img
  22. v-if="item.coverImg"
  23. class="coverImg inmiddle"
  24. :src="item.coverImg"
  25. alt=""
  26. />
  27. <img
  28. v-else
  29. class="takePlace inmiddle"
  30. src="@/assets/img/left-top-logo.png"
  31. alt=""
  32. />
  33. </div>
  34. <div :class="'statusName ' + item.statusName">
  35. {{ $t("myCreating." + item.statusName) }}
  36. </div>
  37. <div class="workInfo flex">
  38. <span>{{ item.title }}</span>
  39. <span>{{ dayjs(item.createDate).format("YYYY-MM-DD") }}</span>
  40. </div>
  41. <!-- 更多遮罩层 -->
  42. <div
  43. class="more"
  44. @mouseleave="mouseleave"
  45. @click="clickVideoItem(item)"
  46. >
  47. <el-icon
  48. v-if="[0].includes(item.videoStatus)"
  49. class="icon-videoPlay"
  50. ><i-ep-edit
  51. /></el-icon>
  52. <el-icon
  53. v-if="[1, 2, 4, 6].includes(item.videoStatus)"
  54. class="icon-videoPlay"
  55. ><i-ep-loading
  56. /></el-icon>
  57. <el-icon
  58. v-if="[3].includes(item.videoStatus)"
  59. class="icon-videoPlay"
  60. ><i-ep-closeBold
  61. /></el-icon>
  62. <el-icon
  63. v-if="[5].includes(item.videoStatus)"
  64. class="icon-videoPlay"
  65. ><i-ep-videoPlay
  66. /></el-icon>
  67. <div class="showBtn" @click.stop="mouseenter(item)">...</div>
  68. <div class="btns" v-show="showPopup">
  69. <!-- 下载按钮 -->
  70. <div
  71. class="btns-item"
  72. @click.stop="handleDownload(item.videoPlayUrl)"
  73. >
  74. <div class="btns-item-icon">
  75. <el-icon class="icon-delete"><i-ep-download /></el-icon>
  76. </div>
  77. <div class="text">{{ $t("myCreating.download") }}</div>
  78. </div>
  79. <!-- 删除按钮 -->
  80. <div
  81. class="btns-item"
  82. @click.stop="handelDelete(item.id, item.title)"
  83. >
  84. <div class="btns-item-icon">
  85. <el-icon class="icon-delete"><i-ep-delete /></el-icon>
  86. </div>
  87. <div class="text">{{ $t("myCreating.delete") }}</div>
  88. </div>
  89. </div>
  90. </div>
  91. </div>
  92. </div>
  93. <div v-if="videoList.length == 0" class="noList">
  94. <div class="title">
  95. {{ $t("myCreating.noList") }}
  96. </div>
  97. <div class="goCreate" @click="goCreate">
  98. {{ $t("myCreating.goCreate") }}
  99. </div>
  100. </div>
  101. </div>
  102. <div v-if="isLoading" class="loadingIcon">
  103. <el-icon class="is-loading">
  104. <svg
  105. viewBox="0 0 1024 1024"
  106. xmlns="http://www.w3.org/2000/svg"
  107. data-v-ea893728=""
  108. >
  109. <path
  110. fill="currentColor"
  111. d="M512 64a32 32 0 0 1 32 32v192a32 32 0 0 1-64 0V96a32 32 0 0 1 32-32zm0 640a32 32 0 0 1 32 32v192a32 32 0 1 1-64 0V736a32 32 0 0 1 32-32zm448-192a32 32 0 0 1-32 32H736a32 32 0 1 1 0-64h192a32 32 0 0 1 32 32zm-640 0a32 32 0 0 1-32 32H96a32 32 0 0 1 0-64h192a32 32 0 0 1 32 32zM195.2 195.2a32 32 0 0 1 45.248 0L376.32 331.008a32 32 0 0 1-45.248 45.248L195.2 240.448a32 32 0 0 1 0-45.248zm452.544 452.544a32 32 0 0 1 45.248 0L828.8 783.552a32 32 0 0 1-45.248 45.248L647.744 692.992a32 32 0 0 1 0-45.248zM828.8 195.264a32 32 0 0 1 0 45.184L692.992 376.32a32 32 0 0 1-45.248-45.248l135.808-135.808a32 32 0 0 1 45.248 0zm-452.544 452.48a32 32 0 0 1 0 45.248L240.448 828.8a32 32 0 0 1-45.248-45.248l135.808-135.808a32 32 0 0 1 45.248 0z"
  112. ></path>
  113. </svg>
  114. </el-icon>
  115. <span class="loadingText">{{ $t("myCreating.loading") }}</span>
  116. </div>
  117. <!-- 弹出框预览作品 -->
  118. <el-dialog
  119. destroy-on-close
  120. class="elDialog"
  121. v-model="showDialog"
  122. :title="presentItem.title"
  123. width="40%"
  124. center
  125. >
  126. <div class="videoBox">
  127. <video
  128. v-if="presentItem.videoPlayUrl"
  129. controls="true"
  130. class=""
  131. :src="presentItem.videoPlayUrl"
  132. ></video>
  133. <div v-else class="insteadVideo">
  134. {{ $t("myCreating." + presentItem.statusName + "Text") }}
  135. <p v-if="lanChange == 'zh-cn'">
  136. 错误原因: {{ presentItem.videoMsg }}
  137. </p>
  138. <p v-if="lanChange == 'en'">
  139. Error cause: {{ presentItem.videoMsg }}
  140. </p>
  141. <!-- <span v-if="presentItem.videoStatus == 3">{{
  142. presentItem.videoMsg
  143. }}</span> -->
  144. </div>
  145. <!-- 有视频路径的页脚 -->
  146. <div class="footer" v-if="presentItem.videoPlayUrl">
  147. <span class="dialog-footer">
  148. <!-- dialog下载按钮 -->
  149. <button
  150. class="elBtn"
  151. type="primary"
  152. @click.stop="handleDownload(presentItem.videoPlayUrl)"
  153. >
  154. <el-icon class="icon-delete"><i-ep-download /></el-icon
  155. >{{ $t("myCreating.download") }}
  156. </button>
  157. <!-- dialog删除按钮 -->
  158. <button
  159. class="elBtn"
  160. @click.stop="handelDelete(presentItem.id, presentItem.title)"
  161. >
  162. <el-icon class="icon-delete"><i-ep-delete /></el-icon
  163. >{{ $t("myCreating.delete") }}
  164. </button>
  165. </span>
  166. </div>
  167. <!-- 无视频路径的页脚 -->
  168. <div class="footer" v-if="!presentItem.videoPlayUrl">
  169. <span class="dialog-footer">
  170. <!-- dialog编辑按钮 -->
  171. <button
  172. class="elBtn"
  173. type="primary"
  174. @click.stop="
  175. router.push({
  176. path: '/createVideo',
  177. query: { id: presentItem.id },
  178. })
  179. "
  180. v-if="[0, 3].includes(presentItem.videoStatus)"
  181. >
  182. {{ $t("myCreating.edit") }}
  183. </button>
  184. </span>
  185. </div>
  186. </div>
  187. </el-dialog>
  188. <!-- 分页 -->
  189. <div v-if="!isLoading" class="pagination">
  190. <el-pagination
  191. v-model:current-page="pageNum"
  192. v-model:page-size="pageSize"
  193. :page-sizes="[10, 20, 50, 100, 200]"
  194. :small="false"
  195. :disabled="false"
  196. :background="true"
  197. layout="total, sizes, prev, pager, next, jumper"
  198. :total="total"
  199. @size-change="handleSizeChange"
  200. @current-change="handleCurrentChange"
  201. />
  202. </div>
  203. </div>
  204. </template>
  205. <script setup>
  206. import { userPhotoVideoList, delUserPhotoVideoByID } from "@/apis/myCreating";
  207. import { timestampToTime } from "@/utils/tools";
  208. import { useI18n } from "vue-i18n";
  209. import { getStatus } from "./videoStatus";
  210. import { useRouter } from "vue-router";
  211. import dayjs from "dayjs";
  212. const router = useRouter();
  213. // 当前语言状态与国际化组件
  214. const { locale } = useI18n();
  215. const lanChange = ref(locale);
  216. const title = ref("");
  217. const pageNum = ref(1);
  218. const pageSize = ref(10);
  219. const total = ref(0);
  220. // 弹窗显示隐藏
  221. const showDialog = ref(false);
  222. const isLoading = ref(true);
  223. // 当前作品
  224. const presentItem = ref("");
  225. // 作品列表
  226. const videoList = ref([]);
  227. const handleSizeChange = (val) => {
  228. pageSize.value = val;
  229. getPhotoVideoList(pageNum.value, pageSize.value, title.value);
  230. };
  231. const handleCurrentChange = (val) => {
  232. pageNum.value = val;
  233. getPhotoVideoList(pageNum.value, pageSize.value, title.value);
  234. };
  235. // 点击作品
  236. function clickVideoItem(item) {
  237. showDialog.value = true;
  238. presentItem.value = item;
  239. }
  240. // 删除框是否显示
  241. let showPopup = ref(false);
  242. function mouseleave() {
  243. showPopup.value = false;
  244. }
  245. function mouseenter(item) {
  246. presentItem.value = item;
  247. showPopup.value = !showPopup.value;
  248. }
  249. // 下载
  250. function handleDownload(itemVideoPlayUrl) {
  251. if (itemVideoPlayUrl) {
  252. const link = document.createElement("a");
  253. link.href = itemVideoPlayUrl;
  254. document.body.appendChild(link);
  255. link.click();
  256. document.body.removeChild(link);
  257. ElMessage.success(
  258. lanChange.value == "zh-cn" ? `下载成功` : `Download Successfully`
  259. );
  260. } else {
  261. ElMessage.error(
  262. lanChange.value == "zh-cn" ? `无法下载` : `Unable to download`
  263. );
  264. }
  265. }
  266. function goCreate() {
  267. router.push("/createVideo");
  268. }
  269. function searchVideoList() {
  270. getPhotoVideoList(pageNum.value, pageSize.value, title.value, true);
  271. }
  272. function handelDelete(id, title) {
  273. const msg =
  274. lanChange.value == "zh-cn"
  275. ? `确定要删除这个作品吗? 作品标题:${title}`
  276. : `Are you sure to delete this video ? Title:${title};`;
  277. const notice = lanChange.value == "zh-cn" ? "提示" : "Notice";
  278. const confirm = lanChange.value == "zh-cn" ? "确定" : "Confirm";
  279. const cancel = lanChange.value == "zh-cn" ? "取消" : "Cancel";
  280. ElMessageBox.confirm(msg, notice, {
  281. confirmButtonText: confirm,
  282. cancelButtonText: cancel,
  283. }).then(() => {
  284. delPhotoVideo(id);
  285. });
  286. }
  287. /**
  288. * @description:获取我的视频作品
  289. * @return: data
  290. */
  291. function getPhotoVideoList(pageNum, pageSize, title, searchFlag) {
  292. userPhotoVideoList(pageNum, pageSize, title)
  293. .then((res) => {
  294. console.log(res, "res");
  295. res.data.list.forEach((item) => {
  296. // item.createDate = timestampToTime(item.createDate) || "";
  297. // item.updateDate = timestampToTime(item.updateDate) || "";
  298. // item.createVideoDate = timestampToTime(item.createVideoDate) || "";
  299. item.statusName = getStatus(item.videoStatus);
  300. });
  301. videoList.value = res.data.list;
  302. total.value = res.data.total * 1;
  303. if (searchFlag) {
  304. const total = res.data.total;
  305. const msg1 =
  306. lanChange.value == "zh-cn"
  307. ? `查询成功!找到${total}条符合条件的视频`
  308. : `Found! Match ${total} videos`;
  309. const msg2 =
  310. lanChange.value == "zh-cn"
  311. ? `查询成功!没有符合条件的视频`
  312. : `Found! No match videos`;
  313. const msg3 =
  314. lanChange.value == "zh-cn"
  315. ? `查询成功!已加载全部视频`
  316. : `Found! Present all videos`;
  317. return title
  318. ? ElMessage.success(total > 0 ? msg1 : msg2)
  319. : ElMessage.success(msg3);
  320. }
  321. isLoading.value = false;
  322. })
  323. .catch((err) => {
  324. console.log(err, "err");
  325. });
  326. }
  327. /**
  328. * @description:删除视频作品
  329. * @param: videoID
  330. * @return: data
  331. */
  332. function delPhotoVideo(videoID) {
  333. delUserPhotoVideoByID(videoID)
  334. .then((res) => {
  335. console.log(res, "res");
  336. const msg = lanChange.value == "zh-cn" ? `删除成功!` : `Success !`;
  337. ElMessage.success(msg);
  338. showDialog.value = false;
  339. getPhotoVideoList(pageNum.value, pageSize.value, title.value);
  340. })
  341. .catch((err) => {
  342. console.log(err, "err");
  343. ElMessage.error(err.message);
  344. });
  345. }
  346. onMounted(() => {
  347. isLoading.value = true;
  348. getPhotoVideoList(pageNum.value, pageSize.value, title.value);
  349. });
  350. </script>
  351. <style lang="scss" scoped>
  352. .page {
  353. min-height: calc(100vh - 80px);
  354. padding: 15px 130px;
  355. background-color: #fff;
  356. border-radius: 25px 0 0;
  357. width: 100%;
  358. overflow: hidden;
  359. .top {
  360. margin-bottom: 35px;
  361. }
  362. .loadingIcon {
  363. height: 100px;
  364. line-height: 100px;
  365. text-align: center;
  366. transform: scale(2);
  367. .loadingText {
  368. position: relative;
  369. top: -2px;
  370. left: 4px;
  371. }
  372. }
  373. .workList {
  374. flex-wrap: wrap;
  375. justify-content: flex-start;
  376. width: 100%;
  377. // height: 200px;
  378. margin-top: 5px;
  379. padding-bottom: 50px;
  380. cursor: pointer;
  381. .workItem {
  382. position: relative;
  383. width: 300px;
  384. // height: 200px;
  385. margin: 0 20px 20px 0;
  386. border: 2px solid #c2c2c2;
  387. border-radius: 15px;
  388. cursor: pointer;
  389. overflow: hidden;
  390. .workImg {
  391. position: relative;
  392. width: 100%;
  393. height: 170px;
  394. cursor: pointer;
  395. padding: 10px;
  396. overflow: hidden;
  397. .coverImg {
  398. max-width: 90%;
  399. max-height: 170px;
  400. cursor: pointer;
  401. }
  402. .takePlace {
  403. position: absolute;
  404. top: 40%;
  405. width: 80%;
  406. }
  407. }
  408. .more {
  409. position: absolute;
  410. top: 0;
  411. left: 0;
  412. width: 100%;
  413. height: 170px;
  414. background-color: rgb(0 0 0 / 30%);
  415. cursor: pointer;
  416. opacity: 0;
  417. transition: all 0.2s;
  418. .icon-videoPlay {
  419. position: absolute;
  420. top: 50%;
  421. left: 50%;
  422. width: 50px;
  423. height: 50px;
  424. font-size: 50px;
  425. color: #fff;
  426. text-align: center;
  427. transform: translate(-50%, -50%);
  428. cursor: pointer;
  429. }
  430. .showBtn {
  431. position: absolute;
  432. top: 10px;
  433. right: 10px;
  434. width: 20px;
  435. height: 20px;
  436. font-size: 20px;
  437. line-height: 10px;
  438. color: #fff;
  439. text-align: center;
  440. transition: all 0.3s;
  441. cursor: pointer;
  442. border-radius: 2px;
  443. &:hover {
  444. background-color: #00000056;
  445. }
  446. }
  447. .btns {
  448. position: absolute;
  449. top: 45px;
  450. right: 10px;
  451. // width: 70px;
  452. // height: 60px;
  453. overflow: hidden;
  454. border-radius: 10px;
  455. cursor: pointer;
  456. &-item {
  457. // width: 70px;
  458. // height: 30px;
  459. text-align: center;
  460. background-color: #fff;
  461. padding: 0 10px;
  462. transition: all 0.3s;
  463. cursor: pointer;
  464. &-icon {
  465. position: relative;
  466. display: inline-block;
  467. width: 20px;
  468. height: 20px;
  469. cursor: pointer;
  470. .icon-delete {
  471. position: absolute;
  472. top: 4px;
  473. left: 0;
  474. width: 20px;
  475. height: 20px;
  476. font-size: 16px;
  477. color: #000;
  478. text-align: center;
  479. cursor: pointer;
  480. }
  481. }
  482. .text {
  483. display: inline-block;
  484. height: 30px;
  485. padding-left: 5px;
  486. margin-top: -5px;
  487. line-height: 20px;
  488. cursor: pointer;
  489. }
  490. &:hover {
  491. color: #ffffff;
  492. background-color: #0000001a;
  493. }
  494. }
  495. }
  496. }
  497. .workInfo {
  498. justify-content: space-between;
  499. height: 40px;
  500. padding: 0 10px;
  501. line-height: 40px;
  502. border-top: 1px solid #afafaf;
  503. white-space: nowrap;
  504. overflow: hidden;
  505. text-overflow: ellipsis;
  506. font-size: 13px;
  507. }
  508. .statusName {
  509. position: absolute;
  510. top: 10%;
  511. left: -10%;
  512. width: 120px;
  513. text-align: center;
  514. color: #ffffff;
  515. font-size: 14px;
  516. transform: rotate(-45deg);
  517. &.manuscript {
  518. /* 草稿 */
  519. background: linear-gradient(to right, #d7d2cc 0%, #304352 100%);
  520. }
  521. &.generating {
  522. /* 生成中 */
  523. background: linear-gradient(270deg, #4b61dc, #15d1b8);
  524. }
  525. &.generationFailed {
  526. /* 生成失败 */
  527. background: linear-gradient(120deg, #f093fb 0%, #f5576c 100%);
  528. }
  529. &.generationSucceed {
  530. /* 生成成功 */
  531. background: linear-gradient(120deg, #84fab0 0%, #8fd3f4 100%);
  532. }
  533. }
  534. &:hover {
  535. .more {
  536. opacity: 1;
  537. }
  538. }
  539. }
  540. }
  541. .noList {
  542. .title {
  543. font-size: 30px;
  544. text-align: center;
  545. margin-top: 100px;
  546. background-image: -webkit-linear-gradient(
  547. left,
  548. #4b61dc,
  549. #15d1b8 25%,
  550. #4b61dc 50%,
  551. #15d1b8 75%,
  552. #4b61dc
  553. ) !important;
  554. -webkit-text-fill-color: transparent !important;
  555. -webkit-background-clip: text !important;
  556. background-clip: text !important;
  557. -webkit-background-size: 200% 100% !important;
  558. background-size: 200% 100% !important;
  559. }
  560. .goCreate {
  561. width: 200px;
  562. height: 40px;
  563. line-height: 40px;
  564. text-align: center;
  565. color: #ffffff;
  566. border-radius: 6px;
  567. background: linear-gradient(270deg, #4b61dc, #15d1b8);
  568. margin: 30px auto;
  569. cursor: pointer;
  570. transition: all 0.3s;
  571. &:hover {
  572. transform: scale(1.1);
  573. }
  574. }
  575. }
  576. .pagination {
  577. position: fixed;
  578. bottom: 20px;
  579. left: 50%;
  580. transform: translateX(-50%);
  581. background-color: #ffffff;
  582. padding: 10px 20px;
  583. border-radius: 8px;
  584. transition: all 0.3s;
  585. cursor: pointer;
  586. &:hover {
  587. background-color: #b8b8b873;
  588. }
  589. }
  590. }
  591. :deep(.el-input__wrapper) {
  592. border: none;
  593. border-bottom: 2px solid #000;
  594. border-radius: 0;
  595. box-shadow: none;
  596. }
  597. .el-icon.icon-edit {
  598. width: 30px;
  599. height: 30px;
  600. font-size: 30px;
  601. color: #8c939d;
  602. text-align: center;
  603. cursor: pointer;
  604. transition: all 0.3s;
  605. &:hover {
  606. color: #292929;
  607. }
  608. }
  609. .elDialog {
  610. .videoBox {
  611. // display: flex;
  612. // justify-content: space-between;
  613. width: 100%;
  614. padding: 20px;
  615. video {
  616. width: 100%;
  617. max-height: 600px;
  618. margin: auto;
  619. }
  620. .insteadVideo {
  621. font-size: 20px;
  622. font-weight: 400;
  623. margin: 20px auto;
  624. text-align: center;
  625. }
  626. p {
  627. font-weight: 700;
  628. }
  629. }
  630. .dialog-footer {
  631. display: flex;
  632. justify-content: space-between;
  633. .elBtn {
  634. color: #fff;
  635. border-radius: 10px;
  636. padding: 10px 15px;
  637. cursor: pointer;
  638. background-color: #000;
  639. transition: all 0.3s;
  640. &:hover {
  641. background-color: #15d1b8;
  642. }
  643. }
  644. :deep(.el-icon) {
  645. margin-right: 5px;
  646. font-weight: 700;
  647. }
  648. }
  649. }
  650. :deep(.el-dialog) {
  651. overflow: hidden;
  652. border-radius: 25px;
  653. }
  654. // :deep(.el-dialog__header) {
  655. // }
  656. :deep(.el-dialog__title) {
  657. background-image: -webkit-linear-gradient(
  658. left,
  659. #4b61dc,
  660. #15d1b8 25%,
  661. #4b61dc 50%,
  662. #15d1b8 75%,
  663. #4b61dc
  664. ) !important;
  665. -webkit-text-fill-color: transparent !important;
  666. -webkit-background-clip: text !important;
  667. background-clip: text !important;
  668. -webkit-background-size: 200% 100% !important;
  669. background-size: 200% 100% !important;
  670. }
  671. :deep(.el-dialog__body) {
  672. padding: 0;
  673. }
  674. :deep(.dialog__footer) {
  675. padding: 0;
  676. }
  677. </style>