소스 검색

nft详情

nieyuge 2 년 전
부모
커밋
41fb78a054
6개의 변경된 파일661개의 추가작업 그리고 4개의 파일을 삭제
  1. 112 0
      src/components/join-group-finish-dialog.vue
  2. 3 4
      src/pages/nav-message.vue
  3. 386 0
      src/pages/nav-nft-detail.vue
  4. 149 0
      src/pages/nav-nft-index.vue
  5. 10 0
      src/router/index.js
  6. 1 0
      src/uilts/messageCenter/messageEnum.js

+ 112 - 0
src/components/join-group-finish-dialog.vue

@@ -0,0 +1,112 @@
+<template>
+    <div class="join-group-overlay" :style="{'position': position}" v-if="dialogVisible">
+        <div class="content-wrapper" :style="contentStyle">
+            <img :src="require('@/assets/svg/icon-celebration.svg')" 
+                class="icon-celebration"
+                :style="iconStyle">
+            <div class="desc" :style="descStyle">{{content}}</div>
+            <div class="btn-wrapper">
+                <div class="btn confirm" @click="confirm">Finish</div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { defineEmits, defineProps } from "vue";
+defineProps({
+    dialogVisible: {
+        type: Boolean,
+        default: false,
+    },
+    content: {
+        type: String,
+        default: 'Joined Successfully'
+    },
+    position: {
+        type: String,
+        default: 'fixed'
+    },
+    contentStyle: {
+        type: Object,
+        default: () => {
+            return {}
+        }
+    },
+    iconStyle: {
+        type: Object,
+        default: () => {
+            return {}
+        }
+    },
+    descStyle: {
+        type: Object,
+        default: () => {
+            return {}
+        }
+    }
+});
+
+const emits = defineEmits(["confirm"]);
+
+const confirm = () => {
+    emits("confirm", {});
+};
+
+</script>
+
+<style lang="scss" scoped>
+.join-group-overlay {
+    position: fixed;
+    top: 0;
+    right: 0;
+    bottom: 0;
+    left: 0;
+    z-index: 3000;
+    height: 100%;
+    background-color: rgba(0, 0, 0, 0.5);
+    overflow: auto;
+
+    .content-wrapper {
+        position: absolute;
+        left: 50%;
+        top: 50%;
+        background: #FFFFFF;
+        border-radius: 20px;
+        box-sizing: border-box;
+        transform: translate(-50%, -50%);
+        text-align: center;
+        width: 500px;
+
+        .icon-celebration {
+            width: 120px;
+            margin-top: 60px;
+        }
+
+        .desc {
+            font-weight: 600;
+            font-size: 22px;
+            margin-top: 36px;
+            margin-bottom: 58px;
+        }
+
+        .btn-wrapper {
+            display: flex;
+            align-items: center;
+            justify-content: space-between;
+            .confirm {
+                width: 100%;
+                padding: 14px;
+                box-sizing: border-box;
+                background: #1D9BF0;
+                color: #fff;
+                border-radius: 100px;
+                font-weight: 600;
+                font-size: 16px;
+                cursor: pointer;
+                margin: 0px 20px 20px 20px;
+            }
+        }
+    }
+}
+</style>

+ 3 - 4
src/pages/nav-message.vue

@@ -305,7 +305,6 @@ import { getChromeStorageFromExtension } from "@/uilts/chromeExtension";
 import messageCenter from "@/uilts/messageCenter";
 import MESSAGE_ENUM from "@/uilts/messageCenter/messageEnum";
 import { PlayType, RewardType } from '@/types';
-import messageEnum from "@/uilts/messageCenter/messageEnum";
 
 var moment = require("moment");
 let currentTabIndex = ref(0);
@@ -411,7 +410,7 @@ const pageScroll = (e) => {
  */
 const sendTwitter = (params) => {
   messageCenter.send({
-    actionType: messageEnum.IFRAME_MESSAGE_PAGE_PUBLISH_TWITTER,
+    actionType: MESSAGE_ENUM.IFRAME_MESSAGE_PAGE_PUBLISH_TWITTER,
     data: {
       srcContent: params.postTaskLuckdrop.srcContent,
       postId: params.postTaskLuckdrop.postId,
@@ -488,12 +487,12 @@ const setMessageCount = () => {
       if (unReadCountTotal > 0) {
         let text = unReadCountTotal > 99 ? '99+' : unReadCountTotal + '';
         messageCenter.send({
-          actionType: messageEnum.IFRAME_MESSAGE_PAGE_SETBADGEINFO,
+          actionType: MESSAGE_ENUM.IFRAME_MESSAGE_PAGE_SETBADGEINFO,
           data: { text }
         })
       } else {
         messageCenter.send({
-          actionType: messageEnum.IFRAME_MESSAGE_PAGE_HIDEBADGE,
+          actionType: MESSAGE_ENUM.IFRAME_MESSAGE_PAGE_HIDEBADGE,
           data: {}
         })
       }

+ 386 - 0
src/pages/nav-nft-detail.vue

@@ -0,0 +1,386 @@
+<template>
+  <div class="nft-detail-wrapper">
+    <div class="back-bar">
+      <img :src="require('@/assets/svg/icon-nft-back-arrow.svg')" class="icon-arrow" @click="back" />
+      {{ NFTInfo.nftItemName }}
+    </div>
+    <div class="content">
+      <div class="nft-img">
+        <img class="img" :src="NFTInfo.imagePath" @click="clickNFTImg" v-if="NFTInfo.imagePath" />
+        <nft-card :nftItemId="NFTInfo.nftItemId" :item="NFTInfo.createImageInfo" :width="343" v-else>
+        </nft-card>
+      </div>
+      <div class="desc item" v-if="nftMetaData.description">
+        <div class="title">Description</div>
+        <div class="desc-content" v-html="nftMetaData.description"></div>
+      </div>
+      <div class="prop item" v-if="nftMetaData.properties && nftMetaData.properties.length">
+        <div class="title">Properties</div>
+        <div class="prop-content">
+          <div class="prop-item" v-for="(filedValueItem, filedValueIndex) in nftMetaData.properties"
+            :key="filedValueIndex">
+            {{  filedValueItem.name  }}
+            <div class="prop-name">
+              {{  filedValueItem.value  }}
+            </div>
+            {{  filedValueItem.description  }}
+          </div>
+        </div>
+      </div>
+
+      <div class="about item" v-if="nftMetaData.about">
+        <div class="title">About</div>
+        <div class="about-content" v-html="nftMetaData.about"></div>
+      </div>
+      <div class="detail item" v-if="nftDetailData.details">
+        <div class="title">Details</div>
+        <div class="detail-content">
+          <div class="detail-item">
+            <div class="left">Contract Address</div>
+            <div class="right address" @click="clickAddress">
+              <span>{{ nftDetailData.details.contractAddress }}</span>
+              <span>{{ nftDetailData.details.contractAddress }}</span>
+            </div>
+          </div>
+          <div class="detail-item">
+            <div class="left">Token ID</div>
+            <div class="right token" @click="clickToken">
+              {{ nftDetailData.details.tokenId }}
+            </div>
+          </div>
+          <div class="detail-item">
+            <div class="left">Token Standard</div>
+            <div class="right">
+              {{ nftDetailData.details.tokenStandard }}
+            </div>
+          </div>
+          <div class="detail-item">
+            <div class="left">Blockchain</div>
+            <div class="right">
+              {{ nftDetailData.details.blockChain }}
+            </div>
+          </div>
+          <div class="detail-item">
+            <div class="left">Creator Fees</div>
+            <div class="right">
+              {{ nftDetailData.details.creatorFees }}
+            </div>
+          </div>
+          <div class="detail-item">
+            <div class="left">Transaction Royalties</div>
+            <div class="right">
+              {{ nftDetailData.details.transactionRoyalties }}
+            </div>
+          </div>
+        </div>
+      </div>
+      <div class="date item" v-if="nftDetailData.dateOfPossession">
+        <div class="title">Date of possession</div>
+        <div class="date-content">{{ nftDetailData.dateOfPossession }}</div>
+      </div>
+
+      <div class="price item" v-if="nftDetailData.purchasePrice">
+        <div class="title">Purchase price</div>
+        <div class="price-content">{{ nftDetailData.purchasePrice }}</div>
+      </div>
+    </div>
+    <div class="bottom-bar">
+      <div class="sale" @click="transfer">
+        <span>Transfer</span>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, onMounted } from "vue";
+import { useRouter } from "vue-router";
+import nftCard from "@/components/nft-card.vue";
+import { getNFTDetail } from "@/http/nft.js";
+import messageCenter from "@/uilts/messageCenter";
+import MESSAGE_ENUM from "@/uilts/messageCenter/messageEnum";
+
+let timer = ref(null);
+let nftMetaData = ref({});
+let nftDetailData = ref({});
+let router = useRouter();
+
+let NFTInfo = ref({
+  imagePath: '',
+  nftItemName: ''
+});
+
+const back = () => {
+  messageCenter.send({
+    actionType: MESSAGE_ENUM.IFRAME_SHOW_FOOTER_MENU,
+    data: {
+      showMenu: true,
+    }
+  })
+  router.back();
+};
+
+const clickAddress = () => {
+  let { contractAddressUrl = '' } = nftDetailData.value.details;
+  if (contractAddressUrl) {
+    window.open(contractAddressUrl);
+  }
+}
+
+const clickToken = () => {
+  let { tokenIdUrl = '' } = nftDetailData.value.details;
+  if (tokenIdUrl) {
+    window.open(tokenIdUrl);
+  }
+}
+
+const clickNFTImg = () => {
+  // window.open(NFTInfo.value.imagePath);
+};
+
+const getDetail = () => {
+  getNFTDetail({
+    params: {
+      nftItemId: NFTInfo.value.nftItemId
+    }
+  }).then(res => {
+    if (res.code == 0) {
+      console.log(res)
+      let { metadata = '{}' } = res.data || {};
+      nftDetailData.value = res.data;
+      nftMetaData.value = JSON.parse(metadata);
+    }
+  }).catch(() => {
+  })
+}
+
+const transfer = () => {
+  if (Object.keys(nftDetailData.value).length) {
+    clearTimeout(timer.value);
+    router.push({
+      name: 'NFTTransfer',
+      query: {
+        params: JSON.stringify({
+          nftItemId: nftDetailData.value?.nftItemId,
+          chainInfo: nftDetailData.value?.chainInfo,
+          transferFeeCurrencyInfo: nftDetailData.value?.transferFeeCurrencyInfo,
+          transferFeeAmountValue: nftDetailData.value?.transferFeeAmountValue,
+        })
+      }
+    })
+  } else {
+    clearTimeout(timer.value);
+    timer.value = setTimeout(() => {
+      transfer()
+    }, 300)
+  }
+}
+
+onMounted(() => {
+  let { params = '{}' } = router.currentRoute.value.query;
+  NFTInfo.value = JSON.parse(params);
+  getDetail();
+})
+
+
+</script>
+
+<style scoped lang="scss">
+.nft-detail-wrapper {
+  width: 100%;
+  height: 100%;
+
+  .back-bar {
+    height: 48px;
+    background: #ffffff;
+    box-shadow: 0px 0.5px 0px #d1d9dd;
+    box-sizing: border-box;
+    padding: 14px;
+    font-weight: 500;
+    font-size: 16px;
+    display: flex;
+    align-items: center;
+
+    .icon-arrow {
+      width: 24px;
+      margin-right: 12px;
+      cursor: pointer;
+    }
+  }
+
+  .content {
+    width: 100%;
+    height: calc(100% - 120px);
+    padding: 0 16px;
+    box-sizing: border-box;
+    overflow-y: auto;
+
+    .nft-img {
+      margin-top: 23px;
+      margin-bottom: 20px;
+      text-align: center;
+      cursor: pointer;
+
+      .img {
+        width: 280px;
+        border-radius: 10px;
+      }
+    }
+
+    .item {
+      border: 1px solid #e3e3e3;
+      border-radius: 10px;
+      padding: 14px;
+      box-sizing: border-box;
+      margin-bottom: 12px;
+
+      .title {
+        font-weight: 600;
+        font-size: 14px;
+      }
+    }
+
+    .desc {
+      margin-top: 10px;
+
+      .desc-content {
+        font-weight: 500;
+        font-size: 14px;
+        color: #929292;
+
+        span {
+          color: #1d9bf0;
+        }
+      }
+    }
+
+    .prop {
+      .prop-content {
+        display: flex;
+        flex-wrap: wrap;
+        margin-top: 12px;
+
+        .prop-item {
+          width: 48%;
+          min-height: 88px;
+          background: #f8f8f8;
+          border-radius: 10px;
+          display: flex;
+          flex-direction: column;
+          justify-content: center;
+          padding: 8px;
+          box-sizing: border-box;
+          align-items: center;
+          font-weight: 500;
+          font-size: 12px;
+          color: #929292;
+          margin-bottom: 10px;
+
+          .prop-name {
+            font-weight: 700;
+            font-size: 17px;
+            margin-top: 6px;
+            margin-bottom: 8px;
+            color: #000;
+            word-break: break-all;
+          }
+        }
+
+        .prop-item:nth-child(odd) {
+          margin-right: 8px;
+        }
+      }
+    }
+
+    .about-content {
+      margin-top: 22px;
+
+      .section {
+        font-weight: 400;
+        font-size: 14px;
+        margin-bottom: 20px;
+      }
+    }
+
+    .section {
+      font-weight: 400;
+      font-size: 14px;
+      margin-bottom: 10px;
+    }
+
+    .detail-content {
+      margin-top: 15px;
+
+      .detail-item {
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        height: 24px;
+        font-weight: 400;
+        font-size: 14px;
+
+        .right {
+          color: #929292;
+        }
+
+        .token {
+          color: #1d9bf0 !important;
+          cursor: pointer;
+        }
+
+        .address {
+          width: 100px;
+          white-space: nowrap;
+          color: #1d9bf0 !important;
+          cursor: pointer;
+
+          >span {
+            display: inline-block;
+            overflow: hidden;
+            text-overflow: ellipsis;
+            width: 50%;
+
+            +span {
+              width: calc(50% + 10px);
+              direction: rtl;
+              margin-left: -11px;
+            }
+          }
+        }
+      }
+    }
+
+    .date-content,
+    .price-content {
+      margin-top: 10px;
+      font-weight: 500;
+      font-size: 14px;
+      color: #929292;
+    }
+  }
+
+  .bottom-bar {
+    background: #ffffff;
+    box-shadow: inset 0px 1px 0px #ececec;
+    height: 70px;
+    padding: 0 16px;
+    box-sizing: border-box;
+    display: flex;
+    align-items: center;
+    justify-content: right;
+
+    .sale {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      cursor: pointer;
+      width: 97px;
+      height: 37px;
+      font-size: 14px;
+      color: #1D9BF0;
+      border-radius: 20px;
+      border: 1px solid #1D9BF0;
+    }
+  }
+}
+</style>

+ 149 - 0
src/pages/nav-nft-index.vue

@@ -0,0 +1,149 @@
+<template>
+  <div class="nft-page-wrapper" ref="pageWrapperDom" @scroll="pageScroll">
+    <div class="content" ref="pageListDom">
+      <div class="item" v-for="(item, index) in listData" :key="index" @click="clickNFT(item)">
+        <img :src="item.imagePath" class="img" v-if="item.imagePath" />
+        <nft-card :nftItemId="item.nftItemId" :item="item.createImageInfo" :width="103" v-else></nft-card>
+        <div class="name">{{  item.nftItemName  }}</div>
+      </div>
+    </div>
+    <join-group-finish-dialog :dialogVisible="joinGroupFinishShow" :position="'absolute'"
+      :contentStyle="{ width: '315px' }" :iconStyle="{ width: '80px', marginTop: '26px' }"
+      :descStyle="{ marginTop: '24px', marginBottom: '25px', fontSize: '19px' }" @confirm="confirmFinish">
+    </join-group-finish-dialog>
+  </div>
+</template>
+
+<script setup>
+import { ref, onMounted } from "vue";
+import { useRouter } from "vue-router";
+import { nftListMine } from "@/http/nft.js";
+import messageCenter from "@/uilts/messageCenter";
+import MESSAGE_ENUM from "@/uilts/messageCenter/messageEnum";
+import nftCard from "@/components/nft-card.vue";
+import joinGroupFinishDialog from "@/components/join-group-finish-dialog.vue";
+
+let listData = ref([]);
+
+let NFTReqParams = {
+  params: {
+    pageNum: 1,
+    pageSize: 30,
+  },
+  loadMore: false,
+};
+
+let pageWrapperDom = ref(null);
+let pageListDom = ref(null);
+let joinGroupFinishShow = ref(false);
+let router = useRouter()
+
+const clickNFT = (params) => {
+  console.log(params)
+  messageCenter.send({
+    actionType: MESSAGE_ENUM.IFRAME_SHOW_FOOTER_MENU,
+    data: {
+      showMenu: false,
+    }
+  })
+  router.push({
+    name: 'navNftDetail',
+    query: {
+      params: JSON.stringify(params)
+    }
+  })
+}
+
+const getNFTListMine = () => {
+  nftListMine({
+    params: NFTReqParams.params,
+  }).then((res) => {
+    if (res.data && res.data.length) {
+      if (NFTReqParams.params.pageNum < 2) {
+        listData.value = res.data;
+      } else {
+        let data = listData.value;
+        data = data.concat(res.data);
+        listData.value = data;
+      }
+      NFTReqParams.loadMore = false;
+    }
+  });
+}
+
+const pageScroll = (e) => {
+  let wrapperHeight = pageWrapperDom.value.offsetHeight;
+  let pageListHeight = pageListDom.value.offsetHeight;
+  let scrollTop = e.target.scrollTop || 0;
+  if (
+    NFTReqParams.loadMore === false &&
+    wrapperHeight + scrollTop >= pageListHeight - 60
+  ) {
+    NFTReqParams.loadMore = true;
+    NFTReqParams.params.pageNum++;
+    getNFTListMine();
+  }
+};
+
+const onMessage = () => {
+  messageCenter.listen(MESSAGE_ENUM.CONTENT_POPUP_PAGE_SHOW, (req) => {
+    getNFTListMine();
+    showJoinFinishHandler(req.data);
+  })
+}
+
+const showJoinFinishHandler = (params) => {
+  let { path, showJoinGroupFinish } = params;
+  if (path == '/NFT' && showJoinGroupFinish) {
+    joinGroupFinishShow.value = true;
+  } else if (joinGroupFinishShow.value) {
+    joinGroupFinishShow.value = false;
+  }
+}
+
+const confirmFinish = () => {
+  joinGroupFinishShow.value = false;
+}
+
+onMounted(() => {
+  onMessage();
+  getNFTListMine();
+})
+</script>
+
+<style scoped lang="scss">
+.nft-page-wrapper {
+  width: 100%;
+  height: 100%;
+  overflow-y: auto;
+
+  .content {
+    width: 100%;
+    display: flex;
+    flex-wrap: wrap;
+    padding: 5px 2px 0 16px;
+    box-sizing: border-box;
+
+    .item {
+      width: 33%;
+      box-sizing: border-box;
+      padding-right: 14px;
+      margin-top: 15px;
+      cursor: pointer;
+
+      .img {
+        width: 100%;
+        border-radius: 5px;
+        height: 104px;
+        object-fit: cover;
+      }
+
+      .name {
+        font-weight: 400;
+        font-size: 12px;
+        margin-top: 6px;
+      }
+    }
+  }
+}
+</style>

+ 10 - 0
src/router/index.js

@@ -12,6 +12,16 @@ const routes = [
         path: '/nav-message',
         name: 'navMessage',
         component: () => require('../pages/nav-message')
+    },
+    {
+        path: '/nav-nft-index',
+        name: 'navNftIndex',
+        component: () => require('../pages/nav-nft-index')
+    },
+    {
+        path: '/nav-nft-detail',
+        name: 'navNftDetail',
+        component: () => require('../pages/nav-nft-detail')
     }
 ]
 

+ 1 - 0
src/uilts/messageCenter/messageEnum.js

@@ -11,6 +11,7 @@ const SEND_MESSAGE_ENUM =  {
     IFRAME_MESSAGE_PAGE_SETBADGEINFO: 'IFRAME_MESSAGE_PAGE_SETBADGEINFO',
     IFRAME_MESSAGE_PAGE_HIDEBADGE: 'IFRAME_MESSAGE_PAGE_HIDEBADGE',
     IFRAME_RUNTIME_CONNECT_POPUP: 'IFRAME_RUNTIME_CONNECT_POPUP',
+    IFRAME_SHOW_FOOTER_MENU: 'IFRAME_SHOW_FOOTER_MENU',
 }
 
 /** 接收父窗口的事件定义 */