邃芒慧语、照片说话(PC) https://photo.metavatar.cc/
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 
 

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