Преглед изворни кода

Merge branch 'dev_1.1.6' of https://git.yishihui.com/DeNet/de-net into dev_1.1.6

zhangwei пре 2 година
родитељ
комит
5da21d74ed

BIN
src/assets/img/icon-box2.png


Разлика између датотеке није приказан због своје велике величине
+ 1 - 0
src/assets/svg/icon-create-nfts.svg


+ 5 - 0
src/assets/svg/icon-transaction-s.svg

@@ -0,0 +1,5 @@
+<svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
+<rect width="17" height="17" rx="8.5" transform="matrix(-1 0 0 1 17 0)" fill="#4CD6D6"/>
+<path d="M9.6611 5.16894L10.7911 4.5L12.6981 7.68105H4.5V6.37508H10.3846L9.6611 5.16688V5.16894Z" fill="white"/>
+<path d="M6.4148 12.4985L7.54684 11.8316L7.54581 11.8295L6.8213 10.6213H12.7079V9.31641H4.50781L6.4148 12.4985Z" fill="white"/>
+</svg>

+ 8 - 0
src/http/configAPI.js

@@ -20,12 +20,20 @@ const page = {
 	development: 'https://testh5.denetme.net'
 }
 
+const website = {
+	production: "https://denet.me",
+	pre: "https://pre.denet.me",
+	development: 'https://test.denet.me'
+}
+
 export const baseAPIUrl = api[process.env.NODE_ENV] + '/denet'
 
 export const logAPIUrl = logApi[process.env.NODE_ENV] + '/log-center'
 
 export const pageUrl = page[process.env.NODE_ENV]
 
+export const websiteUrl = website[process.env.NODE_ENV]
+
 export const discordAuthRedirectUri = `${pageUrl}/auth/discordCallback`;
 
 export const faceShareRedirectUrl = `${pageUrl}/facebook/shareCallback`;

+ 8 - 0
src/http/nft.js

@@ -71,4 +71,12 @@ export function getTwitterNftGroupInfo(params) {
         method: 'post',
         data: params
     })
+}
+
+export function transferRequest(params) {
+    return service({
+        url: `/nft/transfer/request`,
+        method: 'post',
+        data: params
+    })
 }

+ 5 - 0
src/log-center/logEnum.js

@@ -52,6 +52,9 @@ export const objectType = {
     background_function_catch: 'background-function-catch',
     // background 文件chrome 函数 try
     background_function_try:'background-function-try',
+    // create Nft
+    create_nfts_button: 'create-nfts-button',
+    confirm_transfer_button: 'confirm-transfer-button',
 }
 
 export const pageSource = {
@@ -66,7 +69,9 @@ export const pageSource = {
     denetWithdrawForm: "denet-withdraw-form",
     denetWithdrawConfirm: "denet-withdraw-confirm",
     denetTopupSelector: "denet-topup-selector",
+    denetMorePage: "denet-more-page",
     denetSelector: "denet-selector",
+    denetNftTransferPage: "denet-nft-transfer-page",
     // 待开红包页
     pending_page: 'pending-page',
     // 已领取任务页

+ 10 - 4
src/log-center/logger.js

@@ -16,13 +16,13 @@ export async function reportLog(eventData = {}, extParams = {}) {
     if (!mid) {
         mid = await getChromeStorage('mid') || '';
     }
+    let isMobile = navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i);
+    let platform = isMobile ? `mobile` : `pc`;
+    let browser = getBrowser();
     if (chrome && chrome.tabs) {
         chrome.tabs.getCurrent((tab) => {
             if (tab && tab.url) {
-                let isMobile = navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i);
                 let { url = '' } = tab;
-                let platform = isMobile ? `mobile` : `pc`;
-                let browser = getBrowser();
                 let extData = {
                     url,
                     platform,
@@ -32,7 +32,13 @@ export async function reportLog(eventData = {}, extParams = {}) {
                 }
                 paramsPretreatmentAndRequest(logType.denet, extData, extParams)
             }else{
-                paramsPretreatmentAndRequest(logType.denet, eventData, extParams)
+                let extData = {
+                    platform,
+                    browser,
+                    twitterId: userInfo && userInfo.nickName || '',
+                    ...eventData,
+                }
+                paramsPretreatmentAndRequest(logType.denet, extData, extParams)
             }
         })
     } else {

+ 3 - 2
src/logic/background/twitter.js

@@ -1,5 +1,5 @@
 import { fetchTtwitterRequestToken, fetchTwitterLogin, fetchTwitterShortUrl, fetchAllMessageInfo, fetchReadTaskAllMsg, getDiscordUserInfo, fetchGetTwitterNftPostPre, fetchPublish, fetchGetAllUnReadNotices } from '@/logic/background/fetch/twitter.js'
-import { LANDING_PAGE, LANDING_PAGE_MID, setChromeStorage, setChromeCookie, getChromeCookie, getChromeStorage, removeChromeCookie, LANDING_PAGE_JUMP_INFO } from '@/uilts/chromeExtension.js'
+import { LANDING_PAGE, WEBSITE_USER_INFO, LANDING_PAGE_MID, setChromeStorage, setChromeCookie, getChromeCookie, getChromeStorage, removeChromeCookie, LANDING_PAGE_JUMP_INFO } from '@/uilts/chromeExtension.js'
 import { guid } from '@/uilts/help.js'
 import { discordAuthRedirectUri } from '@/http/configAPI'
 import { setContentMessage } from '@/logic/background/help.js'
@@ -88,6 +88,7 @@ export function twitterPinLoginCode(sender, code) {
         fetchTwitterLogin(authToken, consumerKey, code, receivedIds).then(res => {
             if (res.code == 0) {
                 setChromeStorage({ userInfo: JSON.stringify(res.data) })
+                setChromeCookie(WEBSITE_USER_INFO, res.data)
 
                 sendActivetabMessage({
                     actionType: 'BG_LOGIN_SET_USERINFO_CB'
@@ -290,7 +291,7 @@ export function onInstalledCreateTab() {
                 }
                 break
             // NFT
-            case 'ntf_info':
+            case 'nft_info':
                 if (res && res.twitterAccount && res.nftProjectId) {
                     created_detail = true
                     url = `https://twitter.com/${res.twitterAccount}`

+ 1 - 1
src/logic/content/twitter.js

@@ -927,7 +927,7 @@ export function init() {
     }
 
     if (window.location.host.includes('twitter.com')) {
-        onPageVisbile();
+        // onPageVisbile();
         renderDom();
         showNFTCard()
         showNFTGroupIcon()

+ 2 - 1
src/manifest.json

@@ -99,7 +99,8 @@
                 "/iframe/tool-box-guide.html",
                 "/iframe/tool-box.html",
                 "/iframe/test.html",
-                "/iframe/ach-cashier.html"
+                "/iframe/ach-cashier.html",
+                "/img/icon-denet-logo.svg"
             ],
             "matches": [
                 "<all_urls>"

+ 6 - 1
src/router/popup.js

@@ -7,7 +7,7 @@ import Message from '@/view/popup/tabbar-page/message/index.vue'
 import More from '@/view/popup/tabbar-page/more/index.vue'
 
 import NFTDetail  from '@/view/popup/tabbar-page/nft/detail.vue'
-
+import NFTTransfer from '@/view/popup/tabbar-page/nft/transfer.vue'
 import Withdraw from '@/view/popup/withdraw/index.vue'
 import WithdrawInfo from '@/view/popup/withdraw/info.vue'
 import WithdrawConfirm from '@/view/popup/withdraw/confirm.vue'
@@ -120,6 +120,11 @@ const routes = [
         name: 'NFTDetail',
         component: NFTDetail
     },
+    {
+        path: '/NFTTransfer',
+        name: 'NFTTransfer',
+        component: NFTTransfer
+    },
     {
         path: '/currencyDetail',
         name: 'currencyDetail',

+ 6 - 1
src/uilts/chromeExtension.js

@@ -1,4 +1,4 @@
-import { pageUrl } from "@/http/configAPI.js"
+import { pageUrl, websiteUrl } from "@/http/configAPI.js"
 import { guid } from "@/uilts/help";
 
 export const LANDING_PAGE = {
@@ -16,6 +16,11 @@ export const LANDING_PAGE_JUMP_INFO = {
     url: pageUrl
 }
 
+export const WEBSITE_USER_INFO = {
+    name: 'userInfo',
+    url: websiteUrl
+}
+
 export function setChromeStorage(params, callback) {
     try {
         if (callback) {

+ 114 - 0
src/view/components/nft-card.vue

@@ -0,0 +1,114 @@
+<template>
+    <div class="show" :style="{ zoom: zoom }">
+        <div class="card" :class="item.modelName">
+            <div class="logo">
+                <img v-if="item.logoImagePath" :src="item.logoImagePath" alt="" />
+            </div>
+            <div class="member">{{ item.projectName === '' ? 'xxxx' : item.projectName }}</div>
+            <div class="number">{{ nftItemId === '' ? '0000' : nftItemId }}</div>
+        </div>
+        <img class="bg" :src="item.modelImagePath" />
+    </div>
+</template>
+
+<script setup>
+import { onBeforeMount, defineProps, ref } from 'vue'
+
+const zoom = ref(1);
+
+const props = defineProps({
+    item: {
+        type: Object,
+        default: {},
+    },
+    nftItemId: {
+        type: String,
+        default: '0000',
+    },
+    width: {
+        type: Number,
+        default: 400
+    }
+})
+
+onBeforeMount(() => {
+    if (props.width) {
+        zoom.value = props.width / 400
+    }
+})
+</script>
+
+<style lang='scss' scoped>
+.show {
+    position: relative;
+    overflow: hidden;
+    width: 400px;
+    height: 400px;
+    .card {
+        position: absolute;
+        left: 53px;
+        top: 103px;
+        width: 294px;
+        height: 186px;
+        .logo {
+            position: absolute;
+            top: 50%;
+            left: 50%;
+            transform: translate(-50%, -50%);
+            width: 100px;
+            height: 100px;
+            border-radius: 50%;
+            background-color: #fff;
+            img {
+                width: 100%;
+                height: 100%;
+                border-radius: 50%;
+                object-fit: cover;
+            }
+        }
+        .member {
+            position: absolute;
+            top: 11px;
+            left: 11px;
+            width: 228px;
+            font-size: 12px;
+            text-align: left;
+            font-weight: 800;
+            line-height: 13px;
+        }
+        .number {
+            position: absolute;
+            top: 11px;
+            right: 10px;
+            font-size: 12px;
+            font-weight: 800;
+            line-height: 13px;
+            letter-spacing: 1px;
+        }
+        &.s1 {
+            .member, .number {
+                color: #ffffff;
+            }
+        }
+        &.s2 {
+            .member, .number {
+                color: #4AC3E1;
+            }
+        }
+        &.s3 {
+            .member, .number {
+                color: #606C94;
+            }
+        }
+        &.s4 {
+            .member, .number {
+                color: #504215;
+            }
+        }
+    }
+    .bg {
+        width: 100%;
+        height: 100%;
+    }
+}
+</style>

+ 29 - 2
src/view/components/popup-transactions.vue

@@ -43,6 +43,18 @@
                                 require('@/assets/svg/icon-list-top-up.svg')
                             " />
                         </template>
+                        <!-- 收入 - 盲盒 -->
+                        <template v-else-if="item.bizType == 7">
+                            <img class="icon-avatar" style="margin-left:-4px" :src="item.bizData.imagePath" v-if="item.bizData && item.bizData.imagePath" />
+                            <img class="icon-avatar" style="margin-left:-4px" :src="require('@/assets/svg/icon-wallter-list-blind-box.svg')" v-else />
+                            <img class="icon-give" :src="require('@/assets/svg/icon-list-withdraw-s.svg')" />
+                        </template>
+                        <!-- 收入 - 盲盒 -->
+                        <template v-else-if="item.bizType == 8">
+                            <img class="icon-avatar" style="margin-left:-4px" :src="item.bizData.imagePath" v-if="item.bizData && item.bizData.imagePath" />
+                            <img class="icon-avatar" style="margin-left:-4px" :src="require('@/assets/svg/icon-wallter-list-blind-box.svg')" v-else />
+                            <img class="icon-give" :src="require('@/assets/svg/icon-get-giveaways-s.svg')" />
+                        </template>
                         <!-- 支出 - 提现 -->
                         <template v-else-if="item.bizType == -1">
                             <img style="margin-left:-4px" :src="
@@ -69,6 +81,12 @@
                                 require('@/assets/svg/icon-list-withdraw-s.svg')
                             " />
                         </template>
+                        <!-- 交易链手续费 -->
+                        <template v-else-if="item.bizType == -5">
+                            <img class="icon-avatar" style="margin-left:-4px" :src="item.bizData.imagePath" v-if="item.bizData && item.bizData.imagePath" />
+                            <img class="icon-avatar" style="margin-left:-4px" :src="require('@/assets/img/icon-box2.png')" v-else />
+                            <img class="icon-give" :src="require('@/assets/svg/icon-transaction-s.svg')" />
+                        </template>
                     </div>
                     <div class="info-wrapper">
                         <div class="left">
@@ -91,6 +109,12 @@
                                 <template v-else-if="item.bizType == 6">
                                     Lottery Refund
                                 </template>
+                                <template v-else-if="item.bizType == 7">
+                                    Sell NFT Mystery box*{{(item.bizData && item.bizData.nftItemCount || '')}}
+                                </template>
+                                <template v-else-if="item.bizType == 8">
+                                    NFT Refund
+                                </template>
                                 <template v-else-if="item.bizType == -1">
                                     Withdrawal
                                 </template>
@@ -98,11 +122,14 @@
                                     Giveaways
                                 </template>
                                 <template v-else-if="item.bizType == -3">
-                                    Mystery box*{{(item.bizData && item.bizData.nftItemCount || '')}}  Sold
+                                    Buy NFT Mystery box*{{(item.bizData && item.bizData.nftItemCount || '')}}
                                 </template>
                                 <template v-else-if="item.bizType == -4">
                                     Lottery Giveaway
                                 </template>
+                                <template v-else-if="item.bizType == -5">
+                                    Transaction Royalties
+                                </template>
                             </div>
                             <div class="time">{{ moment(item.createTimestamp).format('MM-DD HH:mm:ss') }}</div>
                         </div>
@@ -183,7 +210,7 @@
                                         :class="{'balance-direction': item.trxAmountCurrencyInfo.tokenSymbol.length + ('' + item.trxAmountValue).length > 12}">
 
                                         <!--支出—— -2:零钱余额支付 、-3: NFT盲盒余额支付 -->
-                                        <span class="amount" v-if="item.bizType == -2 || item.bizType == -3 || item.bizType == -4">
+                                        <span class="amount" v-if="item.bizType == -2 || item.bizType == -3 || item.bizType == -4 || item.bizType == -5">
                                             <a-tooltip :title="'-' + item.trxAmountValue">
                                                 -{{ getBit(item.trxAmountValue) || 0 }}
                                             </a-tooltip>

+ 8 - 4
src/view/iframe/buy-nft/buy/home.vue

@@ -16,8 +16,7 @@
             <!-- 首页 -->
             <div class="mark">
                 <div class="sold">SOLD: {{ state.data.itemSoldCount || 0 }}/{{ state.data.itemTotalCount || 0 }} </div>
-                <div class="limit">Buy Limit: {{ state.data.userBuyCount || 0 }}/{{ state.data.perUserBuyLimit || 0 }}
-                </div>
+                <div class="limit" v-if="showDesc">Buy Limit: {{ state.data.userBuyCount || 0 }}/{{ state.data.perUserBuyLimit || 0 }}</div>
             </div>
             <div class="btn-area">
                 <template v-for="item in state.data.salePlans.splice(0, 2).reverse()">
@@ -81,12 +80,13 @@
 </template>
 <script setup>
 import { useRouter } from 'vue-router'
-import { onMounted, reactive, inject, } from "vue";
+import { onMounted, reactive, inject, ref } from "vue";
 import { getNftMysteryBoxSaleInfo } from "@/http/nft";
 import BtnLoading from '../components/btn-loading.vue'
 import { getQueryString } from "@/uilts/help";
 let pay_info = inject('pay_info');
-const router = useRouter()
+let router = useRouter()
+let showDesc = ref(true)
 let dialogStyle = reactive({
     height: '800'
 })
@@ -150,6 +150,10 @@ onMounted(() => {
         if (res.code == 0) {
             state.data = res.data
             pay_info.home = res.data
+            let { perUserBuyLimit, itemTotalCount } = res.data;
+            if (perUserBuyLimit && itemTotalCount && perUserBuyLimit >= itemTotalCount) {
+                showDesc.value = false;
+            }
         } else {
 
         }

+ 14 - 2
src/view/iframe/buy-nft/buy/open-box.vue

@@ -2,9 +2,20 @@
     <!-- gif -->
     <div class="box_content">
         <img :src="require('@/assets/gif/box.gif')" class="box" v-show="state.box.show" />
-        <div class="nft" v-for="item in state.nft.data" v-show="item.show">
+        <div
+            class="nft"
+            :key="index"
+            v-for="(item, index) in state.nft.data"
+            v-show="item.show">
             <div class="detail">
-                <img :src="item.imagePath" alt="" />
+                <img
+                    v-if="item.imagePath"
+                    :src="item.imagePath" />
+                <nft-card
+                    v-else
+                    :nftItemId="item.nftItemId"
+                    :item="item.createImageInfo">
+                </nft-card>
             </div>
             <p>{{ item.nftItemName }}</p>
         </div>
@@ -12,6 +23,7 @@
 </template>
 <script setup>
 import { reactive, onMounted, inject } from 'vue'
+import nftCard from "@/view/components/nft-card.vue"
 import router from "@/router/buy-nft.js";
 let pay_info = inject('pay_info');
 let state = reactive({

+ 27 - 4
src/view/iframe/nft/card.vue

@@ -2,9 +2,9 @@
     <div class="nft" :class="{ border: isShare }">
         <template v-if="!isLoading">
             <div class="title">
-                <div class="tag">
+                <div class="tag" :class="{ share: !isShare }">
                     <img class="logo" :src="saleData.nftProjectAvatar" />
-                    <font class="text">{{saleData.nftProjectName}}</font>
+                    <font class="text" :class="{ share: !isShare }">{{saleData.nftProjectName}}</font>
                     <img class="tagImg" :src=" require('@/assets/img/icon-nft.png') " />
                 </div>
                 <div class="share" v-if="!isShare" @click="share">
@@ -14,7 +14,16 @@
             <div class="content">
                 <img :src="saleData.windowImagePath" />
             </div>
-            <div class="buy" @click="buy">
+            <div
+                v-if="saleData.purchaseStatus === 0"
+                class="buy disabled">
+                <img class="guide" v-if="isShowGuide" :src=" require('@/assets/img/icon-arrow.png') " />
+                <button>Buy NFT</button>
+            </div>
+            <div
+                v-else
+                class="buy"
+                @click="buy">
                 <img class="guide" v-if="isShowGuide" :src=" require('@/assets/img/icon-arrow.png') " />
                 <button>Buy NFT</button>
             </div>
@@ -196,8 +205,12 @@ body {
 
         .tag {
             display: flex;
+            max-width: 93%;
             align-items: center;
             padding-left: 15px;
+            &.share {
+                max-width: 84%;
+            }
 
             .logo {
                 overflow: hidden;
@@ -210,7 +223,14 @@ body {
             .text {
                 font-size: 18px;
                 font-weight: bold;
+                max-width: calc(100% - 80px);
                 margin: 0 7px;
+                overflow: hidden;
+                white-space: nowrap;
+                text-overflow: ellipsis;
+                &.share {
+                    max-width: calc(100% - 80px);
+                }
             }
 
             .tagImg {
@@ -221,7 +241,7 @@ body {
 
         .share {
             cursor: pointer;
-            padding-right: 15px;
+            padding-right: 10px;
 
             img {
                 width: 19px;
@@ -244,6 +264,9 @@ body {
         height: 54px;
         display: flex;
         justify-content: center;
+        &.disabled {
+            opacity: .1;
+        }
         .guide {
             position: absolute;
             top: 6px;

+ 2 - 2
src/view/iframe/publish/components/preview-card.vue

@@ -3,7 +3,7 @@
     <div class="wrapper">
         <div class="card-container">
             <!-- 安装之后的卡片样式 -->
-            <div v-if="installStatus" class="left" :style="{'width': reviewCanvasParams.width+ 'px'}">
+            <div v-show="installStatus" class="left" :style="{'width': reviewCanvasParams.width+ 'px'}">
                 <div class="head" :style="{'zoom': reviewCanvasParams.zoom}">
                     <img :src="userInfo.avatarUrl"
                         class="avatar"/>
@@ -40,7 +40,7 @@
 
             <!-- 安装之前的卡片样式 -->
             <div class="content-before"
-                v-else 
+                v-show="!installStatus"
                 :style="{'width': (baseFormData.rewardType === RewardType.money) ? `${reviewCanvasParams.width}px` : '100%'}">
                 <div class="head" 
                     :style="{'zoom': reviewCanvasParams.zoom}">

+ 1 - 0
src/view/popup/currency-detail.vue

@@ -7,6 +7,7 @@
             :transactionsRouterParams="{
               backUrl: 'back'
             }"
+            back_url="/"
             @on-refresh="onRefresh" />
     <div class="top">
       <img

+ 3 - 1
src/view/popup/setting/index.vue

@@ -11,7 +11,7 @@
 
         <div class="item">
             <div class="l">Announcement Notification</div>
-            <div class="r">
+            <div class="r" v-if="noticeShow">
                 <a-switch v-model:checked="noticeStatus" @change="change"></a-switch>
             </div>
         </div>
@@ -24,6 +24,7 @@ import router from "@/router/popup.js";
 import { getSetting, putSetting } from "@/http/user";
 
 let noticeStatus = ref(true);
+let noticeShow = ref(false);
 
 function back() {
     router.back();
@@ -65,6 +66,7 @@ onBeforeMount(() => {
         if (code === 0) {
             noticeStatus.value = data.noticeSwitch === 1 ? true : false;
         }
+        noticeShow.value = true;
     });
 });
 </script>

+ 25 - 2
src/view/popup/tabbar-page/message/index.vue

@@ -15,10 +15,24 @@
                   <img class="icon-give" :src="require('@/assets/svg/icon-get-giveaways-s.svg')" />
                 </template>
                 <!-- 发出去红包 -->
-                <template v-else-if="2">
+                <template v-else-if="item.type == 2">
                   <img class="icon-big-give" :src="require('@/assets/svg/icon-send-giveaways-s.svg')" />
                   <img class="icon-mark-give" :src="require('@/assets/svg/icon-send-giveaways-mark.svg')" />
                 </template>
+                <!-- 转出NFT记录 -->
+                <template v-else-if="item.type == 4">
+                  <img
+                    v-if="item.nftItemVO?.imagePath"
+                    class="icon-big-give"
+                    :src="item.nftItemVO?.imagePath" />
+                  <div v-else style="margin-top: 12px;">
+                    <nft-card
+                      :nftItemId="item.nftItemId"
+                      :item="item.nftItemVO.createImageInfo"
+                      :width="34">
+                    </nft-card>
+                  </div>
+                </template>
               </div>
               <div class="info-wrapper" :class="{ 'info-center': item.type == 1 }">
                 <div class="left">
@@ -32,6 +46,9 @@
                     <template v-else-if="item.type == 3">
                       Lottery
                     </template>
+                    <template v-else-if="item.type == 4">
+                      Transfer NFT
+                    </template>
                   </div>
                   <div class="time">
                     {{ moment(item.timestamp).format("MM-DD HH:mm:ss") }}
@@ -139,6 +156,11 @@
                           Giveaway Expired
                         </template>
                       </template>
+                      <template v-else-if="item.type == 4">
+                        <template v-if="item.status == 0 || item.status == 1">Transferring</template>
+                        <template v-else-if="item.status == 2">Successful</template>
+                        <template v-else-if="item.status == 3">Transfe Failed</template>
+                      </template>
                     </div>
                     <!-- 发出的红包显示 -->
                     <div class="desc" v-if="item.type == 2">
@@ -208,7 +230,7 @@
                     </div>
                   </div>
                   <!-- 发红包—— 未发出、进行中 隐藏 -->
-                  <img v-if="item.type != 2" class="icon" :src="require('@/assets/svg/icon-cell-arrow-right.svg')" />
+                  <img v-if="item.type != 2 && item.type != 4" class="icon" :src="require('@/assets/svg/icon-cell-arrow-right.svg')" />
                 </div>
               </div>
             </div>
@@ -229,6 +251,7 @@ import { ref, onMounted, inject, onBeforeUnmount } from "vue";
 
 import modal from "@/view/popup/components/modal.vue";
 import redDot from "@/view/components/red-dot.vue";
+import nftCard from '@/view/components/nft-card.vue'
 
 import { getBit, formatSecondsAsDaysOrTime } from "@/uilts/help";
 import { getMineLuckdropRecords } from "@/http/account";

+ 73 - 24
src/view/popup/tabbar-page/more/index.vue

@@ -1,58 +1,101 @@
 <template>
-  <div class="more-page">
-    <div class="more-list">
-      <div
-        class="cell"
-        v-for="(item, index) in moreTabList"
-        :key="index"
-        @click="moreItemHandle(item)"
-      >
-        <img class="icon" :src="item.icon" />
-        <div class="info-wrapper">
-          <div class="left">
-            {{ item.label }}
-          </div>
-          <div class="right">
-            <img
-              class="icon"
-              :src="require('@/assets/svg/icon-cell-arrow-right.svg')"
-            />
-          </div>
+    <div class="more-page">
+        <div class="more-list">
+            <template
+                :key="index"
+                v-for="(item, index) in moreTabList">
+                <template v-if="item.icon">
+                    <div class="cell" @click="moreItemHandle(item)">
+                        <img class="icon" :src="item.icon" />
+                        <div class="info-wrapper">
+                            <div class="left">
+                                {{ item.label }}
+                            </div>
+                            <div class="right">
+                                <img class="icon" :src="require('@/assets/svg/icon-cell-arrow-right.svg')" />
+                            </div>
+                        </div>
+                    </div>
+                </template>
+                <template v-else>
+                    <div class="clear"></div>
+                </template>
+            </template>
         </div>
-      </div>
     </div>
-  </div>
 </template>
 
 <script setup>
-import { ref } from "vue";
+import Report from "@/log-center/log"
+import { ref, onMounted } from "vue";
+import { websiteUrl } from '@/http/configAPI'
+import { getChromeStorage } from '@/uilts/chromeExtension.js'
 
+let userStorage = ref({})
 let moreTabList = ref([
   {
     icon: require("@/assets/svg/icon-website.svg"),
     label: "Official Website",
     href: "https://www.denet.me",
+    type: 'link'
   },
   {
     icon: require("@/assets/svg/icon-twitter.svg"),
     label: "Twitter",
     href: "https://twitter.com/denet2022",
+    type: 'link'
   },
   // {
   //   icon: require("@/assets/svg/icon-discord.svg"),
   //   label: "Discord",
   //   href: "https://discord.gg/wZSz9p8ddG",
+  //   type: 'link'
   // },
   {
       icon: require("@/assets/svg/icon-telegram.svg"),
       label: "Telegram",
-      href: 'https://t.me/denetpro'
+      href: 'https://t.me/denetpro',
+      type: 'link'
+  },
+  {},
+  {
+      icon: require("@/assets/svg/icon-create-nfts.svg"),
+      label: "Create NFTs",
+      type: 'jumpWebsite'
   }
 ]);
 
 const moreItemHandle = (params) => {
-  window.open(params.href);
+    switch (params.type) {
+        case 'link':
+            window.open(params.href);
+            break;
+        case 'jumpWebsite':
+            let url = websiteUrl + `/nft/list?params=${btoa(JSON.stringify(userStorage.value))}`
+            window.open(url);
+            // report
+            Report.reportLog({
+                pageSource: Report.pageSource.denetMorePage,
+                businessType: Report.businessType.buttonClick,
+                objectType: Report.objectType.create_nfts_button,
+            });
+            break;
+    }
 };
+
+const getUserInfo = async () => {
+    userStorage.value = await getChromeStorage('userInfo') || {}
+}
+
+onMounted(() => {
+    getUserInfo()
+    // report
+    Report.reportLog({
+        pageSource: Report.pageSource.denetMorePage,
+        businessType: Report.businessType.pageView,
+    });
+})
+
 </script>
 
 <style scoped lang="scss">
@@ -112,4 +155,10 @@ const moreItemHandle = (params) => {
     }
   }
 }
+
+.clear {
+    overflow: hidden;
+    height: 10px;
+    background-color: #f6f6f6;
+}
 </style>

+ 45 - 80
src/view/popup/tabbar-page/nft/detail.vue

@@ -14,7 +14,14 @@
                     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>
@@ -96,23 +103,9 @@
                 </div>
         </div>
         <div class="bottom-bar">
-            <div class="default">NFT Sale function, coming soon</div>
-            <!-- <div class="sell">
-                    <div class="sell-btn">
-                        Sell
-                    </div>
-                </div> 
-                <div class="cancel-sale">
-                    <div class="left">
-                        233 USDT
-                        <div class="final">
-                            (Final 203.5 USDT)
-                        </div>
-                    </div>
-                    <div class="cancel-btn">
-                        Cancel sale
-                    </div>
-                </div> -->
+            <div class="sale" @click="transfer">
+                <span>Transfer</span>
+            </div>
         </div>
     </div>
 </template>
@@ -120,9 +113,10 @@
 <script setup>
 import { ref, onMounted } from "vue";
 import router from "@/router/popup.js";
-
+import nftCard from "@/view/components/nft-card.vue"
 import {getNFTDetail} from "@/http/nft.js";
 
+let timer = ref(null);
 let nftMetaData = ref({});
 let nftDetailData = ref({});
 
@@ -170,6 +164,28 @@ const getDetail = () => {
     })
 }
 
+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);
@@ -352,73 +368,22 @@ onMounted(() => {
         background: #ffffff;
         box-shadow: inset 0px 1px 0px #ececec;
         height: 70px;
-        padding: 15px 16px;
+        padding: 0 16px;
         box-sizing: border-box;
         display: flex;
         align-items: center;
-        justify-content: center;
-
-        .default {
-            font-weight: 500;
-            font-size: 16px;
-            color: #a8a8a8;
-            text-align: center;
-        }
-
-        .sell {
-            width: 100%;
-            height: 100%;
-
-            .sell-btn {
-                width: 120px;
-                height: 40px;
-                box-sizing: border-box;
-                border: 1px solid #e9e9e9;
-                border-radius: 100px;
-                font-weight: 500;
-                font-size: 16px;
-                color: #1d9bf0;
-                display: flex;
-                align-items: center;
-                justify-content: center;
-                position: absolute;
-                right: 16px;
-                cursor: pointer;
-            }
-        }
-
-        .cancel-sale {
-            width: 100%;
-            height: 100%;
+        justify-content: right;
+        .sale {
             display: flex;
             align-items: center;
-            justify-content: space-between;
-
-            .left {
-                font-weight: 500;
-                font-size: 15px;
-                .final {
-                    font-weight: 500;
-                    font-size: 12px;
-                    color: #929292;
-                    margin-top: 6px;
-                }
-            }
-
-            .cancel-btn {
-                width: 120px;
-                height: 40px;
-                box-sizing: border-box;
-                border: 1px solid #e9e9e9;
-                border-radius: 100px;
-                font-weight: 500;
-                font-size: 16px;
-                color: #ff0000;
-                display: flex;
-                align-items: center;
-                justify-content: center;
-                cursor: pointer;
-            }
+            justify-content: center;
+            cursor: pointer;
+            width: 97px;
+            height: 37px;
+            font-size: 14px;
+            color: #1D9BF0;
+            border-radius: 20px;
+            border: 1px solid #1D9BF0;
         }
     }
 }

+ 3 - 1
src/view/popup/tabbar-page/nft/index.vue

@@ -2,7 +2,8 @@
   <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">
+        <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>
@@ -20,6 +21,7 @@ import router from "@/router/popup.js";
 
 import { nftListMine } from "@/http/nft.js";
 
+import nftCard from "@/view/components/nft-card.vue"
 import joinGroupFinishDialog from "@/view/components/join-group-finish-dialog.vue";
 
 let listData = ref([]);

+ 300 - 0
src/view/popup/tabbar-page/nft/transfer.vue

@@ -0,0 +1,300 @@
+<template>
+    <div class="info">
+        <v-head
+            :title="'Transfer'"
+            :show_more="true"
+            :transactionsRouterParams="{
+                backUrl: 'back'
+            }">
+        </v-head>
+        <template v-if="!isSuccess">
+            <div class="content">
+                <div class="token">
+                    <div class="title">Network</div>
+                    <div class="box">
+                        <img :src="transChainInfo?.iconPath" alt="" />
+                        <span>{{ transChainInfo?.chainName }}</span>
+                    </div>
+                </div>
+                <div class="token">
+                    <div class="title">Address</div>
+                    <div class="box">
+                        <input type="text" v-model="address" placeholder="Click to enter" />
+                    </div>
+                </div>
+            </div>
+            <div class="footer">
+                <div class="left">
+                    <div class="txt">Network fee</div>
+                    <span class="money"><a-tooltip :title="feeAmountValue || 0" >{{getBit(feeAmountValue || 0)}}</a-tooltip> {{ feeCurrencyInfo?.tokenSymbol }}</span>
+                    <div class="tips" v-if="showTips">Insufficient balance</div>
+                </div>
+                <div class="right">
+                    <div class="btn enter" @click="next" v-if="isNext">Confirm</div>
+                    <div class="btn" v-else>Confirm</div>
+                </div>
+            </div>
+        </template>
+        <template v-else>
+            <div class="withdraw-status">
+                <img :src="require('@/assets/svg/icon-withdraw-status.svg')" alt="" />
+                <div>
+                    <div class="title">Submitted successfully</div>
+                    <div class="desc">
+                        Please check the status at the message tab
+                    </div>
+                </div>
+            </div>
+            <div class="confirm-btn" @click="doneHandle">Done</div>
+        </template>
+    </div>
+</template>
+
+<script setup>
+import Report from "@/log-center/log"
+import { ref, onMounted, watchEffect } from 'vue'
+import { message } from 'ant-design-vue';
+import { getCurrencyInfoByCode } from '@/http/publishApi'
+import { transferRequest } from '@/http/nft'
+import router from "@/router/popup.js";
+import VHead from '@/view/popup/components/head.vue'
+import Zoom from '@/view/components/component-zoom.vue'
+import { getBit } from "@/uilts/help";
+
+const isNext = ref(false)
+const isSuccess = ref(false)
+const isPayment = ref(false)
+const showTips = ref(false)
+const transChainInfo = ref({})
+const feeAmountValue = ref(0)
+const feeCurrencyInfo = ref({})
+const address = ref('')
+const nftId = ref('')
+
+const getCurrentyInfo = (data) => {
+    getCurrencyInfoByCode({
+        params: {
+            currencyCode: data?.currencyCode
+        }
+    }).then(res => {
+        let { code, data } = res;
+        if (code === 0) {
+            isPayment.value = Number(data?.balance) >= Number(feeAmountValue.value)
+            showTips.value = Number(data?.balance) < Number(feeAmountValue.value)
+        }
+    })
+}
+
+const next = () => {
+    transferRequest({
+        params: {
+            nftItemId: nftId.value,
+            receiveAddress: address.value
+        }
+    }).then(res => {
+        let { code } = res;
+        let transfer;
+        if (code === 0) {
+            isSuccess.value = true;
+            transfer = 'success'
+        } else if (code === 5302) {
+            transfer = 'fail'
+            message.error(`NFT transfer is under system maintenance, try again in two or three hours.`);
+        } else {
+            transfer = 'fail'
+            message.error(`Transfer failed, please try again`);
+        }
+        // report
+        Report.reportLog({
+            pageSource: Report.pageSource.denetNftTransferPage,
+            businessType: Report.businessType.buttonClick,
+            objectType: Report.objectType.confirm_transfer_button,
+        }, {
+            transfer: transfer
+        });
+    })
+}
+
+const doneHandle = () => {
+    router.push({ name: 'NFT' })
+}
+
+watchEffect(() => {
+    isNext.value = address.value !== '' && isPayment.value
+})
+
+onMounted(() => {
+    let { params = '{}' } = router.currentRoute.value.query;
+    let { chainInfo, nftItemId, transferFeeAmountValue, transferFeeCurrencyInfo } = JSON.parse(params);
+    // set
+    nftId.value = nftItemId;
+    transChainInfo.value = chainInfo
+    feeAmountValue.value = transferFeeAmountValue
+    feeCurrencyInfo.value = transferFeeCurrencyInfo
+
+    getCurrentyInfo(feeCurrencyInfo.value)
+
+    // report
+    Report.reportLog({
+        pageSource: Report.pageSource.denetNftTransferPage,
+        businessType: Report.businessType.pageView,
+    });
+})
+</script>
+
+<style lang="scss" scoped>
+.info {
+    position: relative;
+    height: 100%;
+    .content {
+        height: calc(100% - 48px - 80px);
+        overflow: auto;
+        padding: 13px 16px 0 13px;
+        .token {
+            margin-bottom: 20px;
+            .title {
+                font-weight: 500;
+                font-size: 12px;
+                margin-bottom: 6px;
+                color: #8B8B8B;
+            }
+            .box {
+                border: 1px solid #DBDBDB;
+                border-radius: 8px;
+                height: 44px;
+                display: flex;
+                align-items: center;
+                padding: 0 15px 0 10px;
+                display: flex;
+                flex-wrap: nowrap;
+                justify-content: space-between;
+                img {
+                    width: 20px;
+                    height: 20px;
+                }
+                .up {
+                    width: 13px;
+                    height: 12px;
+                    cursor: pointer;
+                }
+                input {
+                    outline: none;
+                    border: 0;
+                    flex: 1;
+                    height: 18px;
+                    padding: 0 6px;
+                    font-weight: 500;
+                    font-size: 15px;
+                    &::placeholder {
+                        color: #B8B8B8;
+                    }
+                }
+                input::-webkit-outer-spin-button,
+                input::-webkit-inner-spin-button {
+                    -webkit-appearance: none;
+                }
+                input[type='number'] {
+                    -moz-appearance: textfield;
+                }
+                span {
+                    flex: 1;
+                    margin-left: 6px;
+                    font-size: 14px;
+                    font-weight: 500;
+                }
+            }
+        }
+    }
+    .footer {
+        z-index: 11;
+        background: #fff;
+        border-top: 1px solid #DBDBDB;
+        bottom: 0;
+        height: 80px;
+        display: flex;
+        position: absolute;
+        justify-content: space-between;
+        width: 100%;
+        bottom: 0;
+        align-items: center;
+        .left {
+            margin-left: 16px;
+
+            .txt {
+                color: #9D9D9D;
+                font-weight: 400;
+                font-size: 12px;
+            }
+
+            .money {
+                color: #000000;
+                margin-right: auto;
+                font-size: 15px;
+                font-weight: bold;
+            }
+
+            .tips {
+                color: #FF0000;
+                font-weight: 500;
+                font-size: 12px;
+            }
+        }
+
+        .right {
+            margin-right: 16px;
+            .btn {
+                cursor: pointer;
+                width: 140px;
+                height: 46px;
+                line-height: 46px;
+                text-align: center;
+                font-weight: 600;
+                font-size: 18px;
+                color: #FFFFFF;
+                background: #D2D2D2;
+                border-radius: 100px;
+            }
+            .enter {
+                background: #1D9BF0;
+            }
+        }
+    }
+}
+
+.withdraw-status {
+    text-align: center;
+    img {
+        margin-top: 40px;
+        margin-bottom: 34px;
+    }
+    .title {
+        font-weight: 500;
+        font-size: 20px;
+        margin-bottom: 10px;
+    }
+    .desc {
+        font-size: 15px;
+        color: rgba($color: #000000, $alpha: 0.5);
+    }
+}
+
+.confirm-btn {
+    width: 335px;
+    height: 60px;
+    text-align: center;
+    line-height: 60px;
+    border-radius: 100px;
+    background: #1D9BF0;
+    font-weight: 600;
+    font-size: 18px;
+    color: #fff;
+    position: absolute;
+    left: 50%;
+    bottom: 35px;
+    transform: translateX(-50%);
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    cursor: pointer;
+}
+</style>

Неке датотеке нису приказане због велике количине промена