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

781 lines
19 KiB

  1. <template>
  2. <div class="page flex">
  3. <!-- 左侧内容 -->
  4. <div class="left">
  5. <!-- 头像 -->
  6. <div class="left-one border flex justify-between flex-wrap">
  7. <div class="avatar">
  8. <img
  9. :src="
  10. userInfoPinia.userInfo.avatarUrl
  11. ? userInfoPinia.userInfo.avatarUrl
  12. : defaultAvater
  13. "
  14. alt=""
  15. />
  16. </div>
  17. <div class="info">
  18. <div class="name" v-if="userInfoPinia.userInfo.name">
  19. {{ userInfoPinia.userInfo.name }}
  20. </div>
  21. <span>{{ userInfoPinia.userInfo.email }}</span>
  22. <div>
  23. <span class="password pointer" @click="showEditInfoDialog = true">{{
  24. $t("account.editInfo")
  25. }}</span>
  26. <span
  27. class="password pointer"
  28. style="margin-right: 10px"
  29. @click="router.push({ name: 'login', params: { type: 4 } })"
  30. >{{ $t("account.reSetPwd") }}</span
  31. >
  32. </div>
  33. </div>
  34. </div>
  35. <!-- 剩余次数 -->
  36. <div class="left-two border">
  37. <div v-if="lanChange == 'en'" class="number">
  38. Free for a limited time
  39. </div>
  40. <div v-if="lanChange == 'zh-cn'" class="number">限时免费</div>
  41. <div class="time">{{ $t("account.until") }} 2023/7/31</div>
  42. <div class="getMore pointer" @click="routerTo('/myStore')">
  43. {{ $t("account.getMore") }}
  44. </div>
  45. </div>
  46. <!-- 我的邀请码 -->
  47. <div class="left-three border flex justify-between">
  48. <!-- <div class="codeImg">
  49. <img ref="codeImgRef" :src="defaultAvater" alt="" />
  50. </div> -->
  51. <div class="other">
  52. <div v-if="lanChange == 'en'">
  53. <span>Invite friends to get</span>
  54. <span class="bigWord">free</span>
  55. <span>video</span>
  56. <span class="bigWord">Generation!</span>
  57. <p class="myInviteCode">
  58. your Invitation code:
  59. <span id="InviteCodeId">{{ myInviteCode }}</span>
  60. </p>
  61. </div>
  62. <div v-if="lanChange == 'zh-cn'">
  63. <span>邀请更多好友</span>
  64. <span class="bigWord">免费</span>
  65. <span>生成视频!</span>
  66. <p class="myInviteCode">
  67. 邀请码:
  68. <span id="InviteCodeId">{{ myInviteCode }}</span>
  69. </p>
  70. </div>
  71. <div class="btns">
  72. <span class="myBtn" @click="copyInviteCode">{{
  73. $t("account.copyCode")
  74. }}</span>
  75. <!-- <span class="myBtn" @click="DownloadCodeImg">{{
  76. $t("account.saveCode")
  77. }}</span> -->
  78. </div>
  79. </div>
  80. </div>
  81. <!-- 输入邀请码 !isUseState-->
  82. <div class="left-four border" v-if="false">
  83. <el-input
  84. class="w-50 m-2"
  85. v-model="inputCode"
  86. :placeholder="$t('account.inputCode')"
  87. />
  88. <span class="submitCodeBtn" @click="submitUseCode">{{
  89. $t("account.submit")
  90. }}</span>
  91. </div>
  92. </div>
  93. <!-- 右侧内容 -->
  94. <div class="right">
  95. <el-tabs type="border-card" class="tabs">
  96. <!-- ----------------------------- 1 -->
  97. <el-tab-pane :label="$t('account.buyHistory')">
  98. <div class="card scrolly">
  99. <div class="orderItem" v-for="item in 0" :key="item">
  100. <p>{{ $t("account.orderID") }}:123456</p>
  101. <div class="orderItem-content">
  102. <div class="orderCode"><img :src="defaultAvater" alt="" /></div>
  103. <div class="info">
  104. <h5>疯狂星期{{ item }}</h5>
  105. <p>{{ $t("account.price") }}:88$</p>
  106. <p>{{ $t("account.date") }}:2023/5/2</p>
  107. </div>
  108. <div class="state">{{ $t("account.successed") }}</div>
  109. </div>
  110. </div>
  111. <!-- 无数据时 -->
  112. <div v-if="InviteList.length == 0" class="noList">
  113. <div class="title">
  114. {{ $t("account.noListOrder") }}
  115. </div>
  116. </div>
  117. </div>
  118. </el-tab-pane>
  119. <!-- ------------------------ 2 -->
  120. <el-tab-pane :label="$t('account.inviteHistory')">
  121. <div class="card scrolly card2">
  122. <div v-if="InviteList.length != 0">
  123. <div class="orderItem" v-for="item in InviteList" :key="item">
  124. <div class="orderItem-content">
  125. <div class="orderCode">
  126. <img
  127. :src="item.avatarUrl ? item.avatarUrl : defaultAvater"
  128. alt=""
  129. />
  130. </div>
  131. <div class="info">
  132. <h5 v-if="item.name">{{ item.name }}</h5>
  133. <p>E-mail:{{ item.email }}</p>
  134. <p>
  135. {{ $t("account.date") }}:{{
  136. dayjs(item.createDate).format("YYYY-MM-DD HH:mm:ss")
  137. }}
  138. </p>
  139. </div>
  140. <div class="state">{{ $t("account.msg1") }}</div>
  141. </div>
  142. </div>
  143. </div>
  144. <!-- 无数据时 -->
  145. <div v-if="InviteList.length == 0" class="noList">
  146. <div class="title">
  147. {{ $t("account.noListInvite") }}
  148. </div>
  149. </div>
  150. </div>
  151. </el-tab-pane>
  152. </el-tabs>
  153. </div>
  154. <!-- 修改个人信息弹窗 -->
  155. <el-dialog
  156. width="30%"
  157. class="editInfoDialog"
  158. v-model="showEditInfoDialog"
  159. :title="$t('account.editInfo')"
  160. destroy-on-close
  161. @close="closeInfoDialog"
  162. >
  163. <el-row>
  164. <el-col :span="4"> {{ $t("public.avatar") }}: </el-col>
  165. <el-col :span="20">
  166. <div class="dialogAvatar">
  167. <input accept="image/*" type="file" @change="chooseImg" />
  168. <div class="add">+</div>
  169. <img v-if="dialogAvatarUrl" :src="dialogAvatarUrl" alt="" />
  170. </div>
  171. </el-col>
  172. </el-row>
  173. <el-row>
  174. <el-col :span="4"> {{ $t("public.username") }}: </el-col>
  175. <el-col :span="20">
  176. <el-input v-model="userNameinput" />
  177. </el-col>
  178. </el-row>
  179. <el-row>
  180. <div class="customBtn" @click="showEditInfoDialog = false">
  181. {{ $t("public.cancel") }}
  182. </div>
  183. <div class="customBtn" @click="submitUserInfo">
  184. {{ $t("public.confirm") }}
  185. </div>
  186. </el-row>
  187. </el-dialog>
  188. </div>
  189. </template>
  190. <script setup>
  191. //#region 导入文件
  192. import { useI18n } from "vue-i18n";
  193. import { useRouter } from "vue-router";
  194. import { userInfoModules } from "@/store/modules/userInfo";
  195. import dayjs from "dayjs";
  196. import imageCompression from "browser-image-compression"; //压缩图片插件
  197. import {
  198. inviteCodeApi,
  199. useCodeApi,
  200. isUseApi,
  201. useCodeUserApi,
  202. updUserInfoApi,
  203. } from "../../apis/myAccount";
  204. import { awsImgUpload } from "@/apis/createVideo";
  205. import defaultAvater from "../../assets/img/left-top-logo.png";
  206. //#endregion ---------------------------
  207. const { locale } = useI18n();
  208. const lanChange = ref(locale); //国际化
  209. const userInfoPinia = userInfoModules(); //pinia
  210. //#region 编辑个人信息
  211. const showEditInfoDialog = ref(false); //控制弹窗显隐
  212. const userNameinput = ref(null); //用户名输入框绑定
  213. const dialogAvatarUrl = ref(null); //图片回显地址
  214. const dialogAvatarData = ref(null); //图片上传数据
  215. const infoDialogLoad = ref(false); //load效果
  216. // 选择图片
  217. async function chooseImg(e) {
  218. if (e.target.files[0]) {
  219. dialogAvatarUrl.value = null;
  220. dialogAvatarData.value = null;
  221. // 压缩配置
  222. const options = {
  223. maxSizeMB: 2, // 最大压缩大小为 1MB
  224. useWebWorker: true, // 使用 Web Worker 进行压缩,提高性能
  225. };
  226. const compressedFile = await imageCompression(e.target.files[0], options);
  227. dialogAvatarUrl.value = URL.createObjectURL(compressedFile);
  228. dialogAvatarData.value = compressedFile;
  229. } else {
  230. return;
  231. }
  232. }
  233. // 上传编辑信息
  234. async function submitUserInfo() {
  235. let formData = new FormData();
  236. const timestamp = Date.now();
  237. formData.append("file", dialogAvatarData.value, timestamp + ".png");
  238. const res1 = await awsImgUpload(formData);
  239. const data = {
  240. name: userNameinput.value,
  241. avatarUrl: res1.data.url,
  242. };
  243. try {
  244. const res2 = await updUserInfoApi(data);
  245. ElMessage.success(
  246. lanChange.value == "zh-cn" ? `修改成功` : `Modified successfully`
  247. );
  248. userInfoPinia.getUserInfo();
  249. showEditInfoDialog.value = false;
  250. } catch (error) {
  251. ElMessage.error(
  252. lanChange.value == "zh-cn"
  253. ? `修改失败,请稍后重试`
  254. : `The modification failed. Please try again later`
  255. );
  256. }
  257. }
  258. // 关闭弹窗清空
  259. function closeInfoDialog() {
  260. userNameinput.value = userInfoPinia.userInfo.name;
  261. dialogAvatarUrl.value = userInfoPinia.userInfo.avatarUrl;
  262. dialogAvatarData.value = null;
  263. }
  264. //#endregion ---------------------------
  265. //#region 二维码邀请码区域
  266. const myInviteCode = ref(null); //我的邀请码
  267. async function getMyInviteCode() {
  268. const res = await inviteCodeApi();
  269. myInviteCode.value = res.data;
  270. }
  271. // 点击复制邀请码
  272. function copyInviteCode() {
  273. const inviteCode = document.getElementById("InviteCodeId").innerText;
  274. navigator.clipboard
  275. .writeText(inviteCode)
  276. .then(() => {
  277. ElMessage.success(
  278. lanChange.value == "zh-cn"
  279. ? "邀请码已复制到剪贴板"
  280. : `The invitation code has been copied to the clipboard`
  281. );
  282. })
  283. .catch((error) => {
  284. ElMessage.error(
  285. lanChange.value == "zh-cn"
  286. ? "无法复制邀请码:"
  287. : `Unable to copy invitation code`
  288. );
  289. });
  290. }
  291. // 下载二维码图片
  292. const codeImgRef = ref();
  293. function DownloadCodeImg() {
  294. const link = document.createElement("a");
  295. link.href = codeImgRef.value.src;
  296. link.download = "image.jpg";
  297. link.click();
  298. }
  299. //#endregion
  300. //#region 输入邀请码区
  301. const inputCode = ref(""); // 邀请码输入框内容
  302. const isUseState = ref(false); //用户是否填写过过邀请码
  303. // 提交邀请码
  304. async function submitUseCode() {
  305. try {
  306. const res = await useCodeApi(inputCode.value);
  307. console.log(res.code);
  308. if (res.code == 200) {
  309. ElMessage.success(
  310. lanChange.value == "zh-cn"
  311. ? `提交邀请码成功`
  312. : `The invitation code was submitted successfully`
  313. );
  314. }
  315. if (res.code == 1001) {
  316. ElMessage.error(
  317. lanChange.value == "zh-cn"
  318. ? `不能使用自己的邀请码`
  319. : `You cannot use your own invitation code`
  320. );
  321. } else {
  322. ElMessage.error(
  323. lanChange.value == "zh-cn"
  324. ? `提交邀请码成失败`
  325. : `Failed to submit the invitation code`
  326. );
  327. }
  328. await getIsUseState();
  329. } catch (error) {
  330. console.log(error);
  331. }
  332. }
  333. // 查询是否使用过邀请码
  334. async function getIsUseState() {
  335. const res = await isUseApi();
  336. isUseState.value = res.data;
  337. }
  338. //#endregion ---------------------------
  339. //#region 右侧列表
  340. const InviteList = ref([]); //邀请列表
  341. async function getInviteList() {
  342. const res = await useCodeUserApi();
  343. InviteList.value = res.data;
  344. }
  345. //#endregion ---------------------------
  346. //#region 页面初始化
  347. onMounted(() => {
  348. getMyInviteCode(); // 查询用户的邀请码
  349. getIsUseState(); //查询是否使用过邀请码
  350. getInviteList();
  351. console.log(userInfoPinia.userInfo);
  352. console.log(localStorage.userInfo);
  353. userNameinput.value = userInfoPinia.userInfo.name;
  354. dialogAvatarUrl.value = userInfoPinia.userInfo.avatarUrl;
  355. });
  356. //#endregion ---------------------------
  357. //#region
  358. //#endregion ---------------------------
  359. const router = useRouter();
  360. function routerTo(url) {
  361. router.push(url);
  362. }
  363. </script>
  364. <style lang="scss" scoped>
  365. .page {
  366. height: calc(100vh - 80px);
  367. // height: calc(100vh - 84px);
  368. background-color: #fff;
  369. border-radius: 25px 0 0;
  370. }
  371. .left {
  372. width: 39%;
  373. height: 100%;
  374. padding: 30px;
  375. .border {
  376. padding: 10px;
  377. margin-bottom: 10px;
  378. border: 1px solid #000;
  379. border-radius: 15px;
  380. }
  381. &-one {
  382. .avatar {
  383. position: relative;
  384. width: 80px;
  385. height: 80px;
  386. margin-right: 10px;
  387. border-radius: 50%;
  388. border: 1px solid #ccc;
  389. overflow: hidden;
  390. img {
  391. position: absolute;
  392. top: 50%;
  393. left: 50%;
  394. transform: translate(-50%, -50%);
  395. max-width: 100%;
  396. max-height: 100%;
  397. }
  398. }
  399. .info {
  400. flex: 1;
  401. .name {
  402. height: 30px;
  403. margin-top: 10px;
  404. font-size: 20px;
  405. }
  406. .password {
  407. display: inline-block;
  408. float: right;
  409. padding: 2px 8px;
  410. border: 1px solid #000;
  411. border-radius: 5px;
  412. cursor: pointer;
  413. transition: all 0.3s;
  414. &:hover {
  415. color: #fff;
  416. background-color: #000;
  417. }
  418. }
  419. }
  420. }
  421. &-two {
  422. position: relative;
  423. height: 80px;
  424. overflow: hidden;
  425. .number {
  426. font-size: 30px;
  427. font-weight: 700;
  428. }
  429. .time {
  430. position: absolute;
  431. right: 90px;
  432. }
  433. .getMore {
  434. position: absolute;
  435. top: -1px;
  436. right: -1px;
  437. padding: 5px;
  438. width: 80px;
  439. height: 80px;
  440. line-height: 70px;
  441. text-align: center;
  442. background-color: #000;
  443. color: #fff;
  444. font-size: 14px;
  445. transition: all 0.3s;
  446. &:hover {
  447. background-color: #15d1b8;
  448. color: #fff;
  449. }
  450. }
  451. }
  452. &-three {
  453. height: 200px;
  454. .codeImg {
  455. position: relative;
  456. padding: 10px;
  457. width: 120px;
  458. height: 120px;
  459. margin-right: 10px;
  460. border: 1px solid #ccc;
  461. border-radius: 10px;
  462. // background-color: red;
  463. img {
  464. position: absolute;
  465. top: 50%;
  466. transform: translateY(-50%);
  467. width: 100px;
  468. max-height: 100px;
  469. }
  470. }
  471. .other {
  472. position: relative;
  473. flex: 1;
  474. .btns {
  475. position: absolute;
  476. bottom: 10px;
  477. // padding: 0 80px;
  478. display: flex;
  479. justify-content: space-evenly;
  480. width: 100%;
  481. .myBtn {
  482. margin-right: 10px;
  483. cursor: pointer;
  484. transition: all 0.3s;
  485. &:hover {
  486. color: #fff;
  487. background-color: #000;
  488. }
  489. }
  490. }
  491. .myInviteCode {
  492. margin-top: 20px;
  493. }
  494. }
  495. }
  496. &-four {
  497. .el-input {
  498. width: 80%;
  499. }
  500. :deep(.el-input__wrapper.is-focus) {
  501. box-shadow: 0 0 0 1px #000 !important;
  502. }
  503. // padding: 30px 50px !important;
  504. div {
  505. margin-top: 5px;
  506. height: 40px;
  507. }
  508. .submitCodeBtn {
  509. display: inline-block;
  510. width: 15%;
  511. padding: 10px 15px;
  512. color: #000;
  513. border-radius: 10px;
  514. text-align: center;
  515. cursor: pointer;
  516. transition: all 0.3s;
  517. &:hover {
  518. color: #fff;
  519. background-color: #000;
  520. }
  521. }
  522. }
  523. }
  524. .right {
  525. background-color: #f1f2f6;
  526. padding: 30px;
  527. width: 61%;
  528. height: 100%;
  529. // height: 95%;
  530. border-radius: 30px 0 0 0;
  531. overflow: hidden;
  532. // element组件样式
  533. :deep(.el-tabs--border-card) {
  534. font-size: 20px;
  535. border: none;
  536. }
  537. .tabs {
  538. // height: 760px;
  539. height: 100%;
  540. padding: 20px;
  541. border-radius: 20px;
  542. overflow: hidden;
  543. .noList {
  544. .title {
  545. font-size: 30px;
  546. text-align: center;
  547. margin-top: 100px;
  548. background-image: -webkit-linear-gradient(
  549. left,
  550. #4b61dc,
  551. #15d1b8 25%,
  552. #4b61dc 50%,
  553. #15d1b8 75%,
  554. #4b61dc
  555. ) !important;
  556. -webkit-text-fill-color: transparent !important;
  557. -webkit-background-clip: text !important;
  558. background-clip: text !important;
  559. -webkit-background-size: 200% 100% !important;
  560. background-size: 200% 100% !important;
  561. }
  562. }
  563. .card {
  564. // height: 100%;
  565. height: 740px;
  566. padding: 10px;
  567. // height: 79vh;
  568. .orderItem {
  569. width: 100%;
  570. height: 110px;
  571. padding: 8px;
  572. margin-bottom: 10px;
  573. border: 1px solid #000;
  574. border-radius: 5px;
  575. p {
  576. font-size: 12px;
  577. }
  578. &-content {
  579. display: flex;
  580. width: 100%;
  581. // justify-content: space-between;
  582. .orderCode {
  583. position: relative;
  584. width: 70px;
  585. height: 70px;
  586. padding: 5px;
  587. border: 1px solid #ccc;
  588. border-radius: 5px;
  589. overflow: hidden;
  590. img {
  591. position: absolute;
  592. top: 50%;
  593. left: 50%;
  594. transform: translate(-50%, -50%);
  595. max-width: 60px;
  596. max-height: 60px;
  597. }
  598. }
  599. .info {
  600. // width: 50%;
  601. flex: 1;
  602. margin-left: 10px;
  603. h5 {
  604. margin: 0;
  605. }
  606. p {
  607. margin-top: 5px;
  608. }
  609. }
  610. .state {
  611. margin-right: 30px;
  612. }
  613. }
  614. }
  615. }
  616. .card2 {
  617. .orderItem {
  618. padding: 20px;
  619. &-content {
  620. .orderCode {
  621. position: relative;
  622. border: 1px solid #ccc;
  623. border-radius: 50%;
  624. overflow: hidden;
  625. img {
  626. position: absolute;
  627. top: 50%;
  628. left: 50%;
  629. transform: translate(-50%, -50%);
  630. max-width: 100%;
  631. max-height: 100%;
  632. }
  633. }
  634. .state {
  635. font-size: 14px;
  636. line-height: 60px;
  637. }
  638. }
  639. }
  640. }
  641. :deep(.el-tabs__item.is-active) {
  642. color: #fff;
  643. background-color: #000;
  644. }
  645. :deep(.el-tabs__nav) {
  646. width: 100%;
  647. }
  648. :deep(.el-tabs__item) {
  649. width: 34%;
  650. }
  651. :deep(.el-tabs__item:hover) {
  652. color: #909393;
  653. }
  654. :deep(.el-tabs__content) {
  655. height: 760px;
  656. // height: calc(100% - 60px);
  657. padding: 0;
  658. }
  659. }
  660. }
  661. .editInfoDialog {
  662. .el-row {
  663. display: flex;
  664. justify-content: space-evenly;
  665. }
  666. .el-col {
  667. margin-bottom: 10px;
  668. text-align: right;
  669. line-height: 32px;
  670. padding-right: 20px;
  671. }
  672. .dialogAvatar {
  673. position: relative;
  674. width: 200px;
  675. height: 200px;
  676. background-color: rgba(204, 204, 204, 0.2);
  677. border-radius: 30px;
  678. overflow: hidden;
  679. &:hover {
  680. img {
  681. z-index: 0;
  682. }
  683. .add {
  684. background-color: rgba(0, 0, 0, 0.2);
  685. }
  686. }
  687. input {
  688. position: absolute;
  689. top: 0;
  690. left: 0;
  691. width: 100%;
  692. height: 100%;
  693. opacity: 0;
  694. z-index: 3;
  695. cursor: pointer;
  696. }
  697. .add {
  698. position: absolute;
  699. top: 0;
  700. left: 0;
  701. width: 100%;
  702. height: 100%;
  703. z-index: 1;
  704. text-align: center;
  705. line-height: 200px;
  706. font-size: 50px;
  707. font-weight: 700;
  708. color: #000;
  709. transition: all 0.3s;
  710. }
  711. img {
  712. position: absolute;
  713. top: 50%;
  714. left: 50%;
  715. transform: translate(-50%, -50%);
  716. padding: 10px;
  717. max-width: 100%;
  718. max-height: 100%;
  719. z-index: 2;
  720. transition: all 0.3s;
  721. }
  722. }
  723. }
  724. // 公共类名
  725. .bigWord {
  726. font-size: 30px;
  727. font-weight: 700;
  728. margin: 0 10px;
  729. }
  730. </style>