Bladeren bron

视频&未开售

panhui 2 jaren geleden
bovenliggende
commit
5c35ee8399

BIN
src/assets/not-ava.png


BIN
src/assets/png-AGREEMENT.png


BIN
src/assets/png-TUTORIAL.png


+ 100 - 0
src/components/IntroductionModal.vue

@@ -0,0 +1,100 @@
+<template>
+    <ion-modal
+        :is-open="show"
+        ref="modalRef"
+        class="open-modal"
+        @didDismiss="show = false"
+        :enter-animation="enterAnimation"
+        :leave-animation="leaveAnimation"
+    >
+        <ion-content>
+            <video ref="videoRef" class="video" playsinline="true" webkit-playsinline="true" autoplay loop controls>
+                <source
+                    src="https://paimaide.s3.ap-northeast-1.amazonaws.com/video/2023-01-30-08-00-18IHzGzzvq.mp4"
+                    type="video/mp4"
+                />
+            </video>
+            <van-button class="back" round @click="show = false">关闭</van-button>
+        </ion-content>
+    </ion-modal>
+</template>
+
+<script setup>
+import { ref, onMounted, defineExpose } from 'vue'
+import {
+    createAnimation,
+    IonButtons,
+    IonButton,
+    IonModal,
+    IonHeader,
+    IonContent,
+    IonToolbar,
+    IonTitle,
+    IonItem,
+    IonList,
+    IonAvatar,
+    IonImg,
+    IonLabel
+} from '@ionic/vue'
+
+const show = ref(false)
+const modalRef = ref(false)
+const videoRef = ref(false)
+
+function dismiss() {
+    modalRef.value.$el.dismiss()
+}
+
+const enterAnimation = baseEl => {
+    const root = baseEl.shadowRoot
+
+    const backdropAnimation = createAnimation()
+        .addElement(root.querySelector('ion-backdrop'))
+        .fromTo('opacity', '0.01', 'var(--backdrop-opacity)')
+
+    const wrapperAnimation = createAnimation()
+        .addElement(root.querySelector('.modal-wrapper'))
+        .keyframes([
+            { offset: 0, opacity: '0', transform: 'scale(0)' },
+            { offset: 1, opacity: '0.99', transform: 'scale(1)' }
+        ])
+
+    return createAnimation()
+        .addElement(baseEl)
+        .easing('ease-out')
+        .duration(500)
+        .addAnimation([backdropAnimation, wrapperAnimation])
+}
+
+const leaveAnimation = baseEl => {
+    return enterAnimation(baseEl).direction('reverse')
+}
+
+function init() {
+    show.value = true
+}
+
+defineExpose({
+    init
+})
+</script>
+
+<style lang="less" scoped>
+.video {
+    width: 100vw;
+    height: 100%;
+    display: block;
+}
+
+.back {
+    position: fixed;
+    right: 30px;
+    top: calc(var(--ion-safe-area-top) + 30px);
+    z-index: 20;
+    width: 60px;
+    height: 26px;
+    background-color: rgba(255, 255, 255, 0.1);
+    border-width: 0;
+    color: #fff;
+}
+</style>

+ 66 - 14
src/components/ProductInfo.vue

@@ -11,25 +11,39 @@
             <div class="flex1"></div>
             <div class="datas">
                 <div class="data">
-                    <div class="val">
-                        <small>{{ $t('balance.symbol') }}</small>
+                    <div class="val" v-if="!notStock">
+                        <span>{{ $t('balance.symbol') }}</span>
                         <span>{{ info.currentPrice }}</span>
                     </div>
+                    <div class="val" v-else>
+                        <span>***</span>
+                    </div>
                     <div class="name">{{ $t('product.priceNow') }}</div>
                 </div>
                 <div class="data">
-                    <div class="val">{{ riseRatePercent }}%</div>
+                    <div class="val" v-if="!notStock">{{ riseRatePercent }}%</div>
+                    <div class="val" v-else>
+                        <span>***</span>
+                    </div>
                     <div class="name">{{ $t('product.dailyEarning') }}</div>
                 </div>
                 <div class="data">
-                    <div class="val">
-                        <small>{{ $t('balance.symbol') }}</small>
+                    <div class="val" v-if="!notStock">
+                        <span>{{ $t('balance.symbol') }}</span>
                         <span>{{ nextPrice || 0 }}</span>
                     </div>
+                    <div class="val" v-else>
+                        <span>***</span>
+                    </div>
                     <div class="name">{{ $t('product.tomorrowBuy') }}</div>
                 </div>
             </div>
         </div>
+
+        <div class="not" v-if="notStock">
+            <img src="@/assets/not-ava.png" alt="" />
+            <span>{{ $t('common.notAvailable') }}...</span>
+        </div>
     </div>
 </template>
 
@@ -38,6 +52,7 @@ import { computed } from 'vue'
 import { useRouter } from 'vue-router'
 import { useSystemStore } from '../stores/system'
 import { accAdd, accMul } from '../plugins/calc'
+import toast from '@/utils/toast'
 
 const props = defineProps({
     info: {
@@ -49,6 +64,10 @@ const props = defineProps({
     list: {
         type: Boolean,
         default: false
+    },
+    stopOfficial: {
+        type: Boolean,
+        default: false
     }
 })
 const category = computed(() => {
@@ -61,13 +80,21 @@ const width = computed(() => {
     return props.list ? 104 : 78
 })
 
+const notStock = computed(() => {
+    return props.stopOfficial && props.info.sales === 0
+})
+
 const goDetail = () => {
-    router.push({
-        path: '/productDetail',
-        query: {
-            id: props.info.id
-        }
-    })
+    if (notStock.value) {
+        toast(this.$t('common.notAvailable') + '...')
+    } else {
+        router.push({
+            path: '/productDetail',
+            query: {
+                id: props.info.id
+            }
+        })
+    }
 }
 
 const riseRatePercent = computed(() => {
@@ -89,6 +116,31 @@ const nextPrice = computed(() => {
     .van-image {
         flex-shrink: 0;
     }
+    position: relative;
+}
+
+.not {
+    .f();
+    background-color: #ffe6b8;
+    height: 20px;
+    padding: 2px 6px;
+    position: absolute;
+    top: 0;
+    left: 0;
+    z-index: 20;
+    border-radius: 8px 0 8px 0;
+    img {
+        width: 12px;
+        height: 12px;
+    }
+
+    span {
+        font-size: 10px;
+        line-height: 15px;
+        font-weight: bold;
+        color: rgba(194, 136, 0, 1);
+        margin-left: 5px;
+    }
 }
 
 .content {
@@ -110,7 +162,7 @@ const nextPrice = computed(() => {
         margin-top: 4px;
         .badge {
             font-size: 10px;
-            color: #9f8dff;
+            color: #c085ff;
             // background: #2d2d56;
             line-height: 16px;
             border-radius: 2px;
@@ -173,9 +225,9 @@ const nextPrice = computed(() => {
         }
         .badges {
             .badge {
-                color: #9b68d2;
+                color: #c085ff;
                 font-weight: bold;
-                border: 0.5px solid #9b68d2;
+                border: 0.5px solid #c085ff;
 
                 &:nth-child(2) {
                     color: #39f3bb;

+ 58 - 15
src/components/ProductSmallInfo.vue

@@ -10,14 +10,23 @@
             </div>
             <div class="datas">
                 <div class="data">
-                    <div class="val">
-                        <small>{{ $t('balance.symbol') }}</small>
+                    <div class="val" v-if="!notStock">
+                        <span>{{ $t('balance.symbol') }}</span>
                         <span>{{ info.currentPrice }}</span>
                     </div>
+
+                    <div class="val" v-else>
+                        <span>***</span>
+                    </div>
                     <div class="name">{{ $t('product.priceNow') }}</div>
                 </div>
             </div>
         </div>
+
+        <div class="not" v-if="notStock">
+            <img src="@/assets/not-ava.png" alt="" />
+            <span>{{ $t('common.notAvailable') }}...</span>
+        </div>
     </div>
 </template>
 
@@ -26,6 +35,7 @@ import { computed } from 'vue'
 import { useRouter } from 'vue-router'
 import { useSystemStore } from '../stores/system'
 import { accAdd, accMul } from '../plugins/calc'
+import toast from '@/utils/toast'
 
 const props = defineProps({
     info: {
@@ -37,20 +47,32 @@ const props = defineProps({
     list: {
         type: Boolean,
         default: false
+    },
+    stopOfficial: {
+        type: Boolean,
+        default: false
     }
 })
 const category = computed(() => {
     return props.info.category || {}
 })
 
+const notStock = computed(() => {
+    return props.stopOfficial && props.info.sales === 0
+})
+
 const router = useRouter()
 const goDetail = () => {
-    router.push({
-        path: '/productDetail',
-        query: {
-            id: props.info.id
-        }
-    })
+    if (notStock.value) {
+        toast(this.$t('common.notAvailable') + '...')
+    } else {
+        router.push({
+            path: '/productDetail',
+            query: {
+                id: props.info.id
+            }
+        })
+    }
 }
 
 const riseRatePercent = computed(() => {
@@ -69,11 +91,36 @@ const nextPrice = computed(() => {
 <style lang="less" scoped>
 .product-info {
     .f-col();
+    position: relative;
     .van-image {
         flex-shrink: 0;
     }
 }
 
+.not {
+    .f();
+    background-color: #ffe6b8;
+    height: 20px;
+    padding: 2px 6px;
+    position: absolute;
+    top: 0;
+    left: 0;
+    z-index: 20;
+    border-radius: 8px 0 8px 0;
+    img {
+        width: 12px;
+        height: 12px;
+    }
+
+    span {
+        font-size: 10px;
+        line-height: 15px;
+        font-weight: bold;
+        color: rgba(194, 136, 0, 1);
+        margin-left: 5px;
+    }
+}
+
 .content {
     align-self: stretch;
     padding: 6px 8px;
@@ -98,10 +145,10 @@ const nextPrice = computed(() => {
             line-height: 16px;
             border-radius: 2px;
             font-weight: bold;
-            color: #9f8dff;
+            color: #c085ff;
             padding: 0 4px;
             white-space: nowrap;
-            border: 0.5px solid #9b68d2;
+            border: 0.5px solid #c085ff;
             &:nth-child(2) {
                 color: #39f3bb;
                 border: 0.5px solid #39f3bb;
@@ -126,14 +173,10 @@ const nextPrice = computed(() => {
         .val {
             .f();
             align-items: flex-end;
-            font-size: 14px;
+            font-size: 18px;
             font-weight: bold;
             color: var(--ion-color-light-contrast);
             color: var(--yellow);
-            small {
-                font-size: 10px;
-                padding-top: 4px;
-            }
         }
         .name {
             font-size: 10px;

+ 109 - 0
src/components/TutorialModal.vue

@@ -0,0 +1,109 @@
+<template>
+    <ion-modal
+        :is-open="show"
+        ref="modalRef"
+        class="open-modal"
+        @didDismiss="show = false"
+        :enter-animation="enterAnimation"
+        :leave-animation="leaveAnimation"
+    >
+        <ion-content>
+            <video ref="videoRef" class="video" playsinline="true" webkit-playsinline="true" autoplay loop controls>
+                <source
+                    src="https://paimaide.s3.ap-northeast-1.amazonaws.com/video/2023-01-30-07-43-43NAoUVgVk.mp4"
+                    type="video/mp4"
+                />
+            </video>
+            <van-button class="back" v-if="showClose" round @click="show = false">关闭</van-button>
+            <van-button class="back" v-else round @click="show = false">跳过</van-button>
+        </ion-content>
+    </ion-modal>
+</template>
+
+<script setup>
+import { ref, onMounted, defineExpose } from 'vue'
+import {
+    createAnimation,
+    IonButtons,
+    IonButton,
+    IonModal,
+    IonHeader,
+    IonContent,
+    IonToolbar,
+    IonTitle,
+    IonItem,
+    IonList,
+    IonAvatar,
+    IonImg,
+    IonLabel
+} from '@ionic/vue'
+
+const show = ref(false)
+const modalRef = ref(false)
+const videoRef = ref(false)
+const showClose = ref(true)
+
+function dismiss() {
+    modalRef.value.$el.dismiss()
+}
+
+const enterAnimation = baseEl => {
+    const root = baseEl.shadowRoot
+
+    const backdropAnimation = createAnimation()
+        .addElement(root.querySelector('ion-backdrop'))
+        .fromTo('opacity', '0.01', 'var(--backdrop-opacity)')
+
+    const wrapperAnimation = createAnimation()
+        .addElement(root.querySelector('.modal-wrapper'))
+        .keyframes([
+            { offset: 0, opacity: '0', transform: 'scale(0)' },
+            { offset: 1, opacity: '0.99', transform: 'scale(1)' }
+        ])
+
+    return createAnimation()
+        .addElement(baseEl)
+        .easing('ease-out')
+        .duration(500)
+        .addAnimation([backdropAnimation, wrapperAnimation])
+}
+
+const leaveAnimation = baseEl => {
+    return enterAnimation(baseEl).direction('reverse')
+}
+onMounted(() => {
+    if (localStorage.getItem('showTuTorial') !== '1') {
+        showClose.value = false
+        show.value = true
+        localStorage.setItem('showTuTorial', '1')
+    }
+})
+
+function init() {
+    show.value = true
+}
+
+defineExpose({
+    init
+})
+</script>
+
+<style lang="less" scoped>
+.video {
+    width: 100vw;
+    height: 100%;
+    display: block;
+}
+
+.back {
+    position: fixed;
+    right: 30px;
+    top: calc(var(--ion-safe-area-top) + 30px);
+    z-index: 20;
+    width: 60px;
+    height: 26px;
+    background-color: rgba(255, 255, 255, 0.1);
+    border-width: 0;
+    color: #fff;
+}
+</style>

+ 14 - 2
src/views/HomePage.vue

@@ -162,10 +162,13 @@
                 <div class="tool-info" @click="agreement">
                     <img src="../assets/png-AGREEMENT.png" alt="" />
                 </div>
-                <div class="tool-info" @click="tutorial">
+                <div class="tool-info" @click="showTutorial">
                     <img src="../assets/png-TUTORIAL.png" alt="" />
                 </div>
             </div>
+
+            <tutorial-modal ref="tutorialModalRef"></tutorial-modal>
+            <introduction-modal ref="introductionModalRef"></introduction-modal>
         </ion-content>
     </ion-page>
 </template>
@@ -189,6 +192,8 @@ import toast from '@/utils/toast'
 import { usePageVisibility } from '@vant/use'
 import { Browser } from '@capacitor/browser'
 import { useI18n } from 'vue-i18n'
+import TutorialModal from '../components/TutorialModal.vue'
+import IntroductionModal from '../components/IntroductionModal.vue'
 
 const router = useRouter()
 function navigate() {
@@ -336,14 +341,21 @@ function goRank() {
     router.push('/rank')
 }
 
+const introductionModalRef = ref(null)
 function agreement() {
-    router.push('/rule')
+    introductionModalRef.value.init()
 }
 
 function tutorial() {
     router.push('/turtorial')
     // Browser.open({ url: `${location.origin}/static/PrivacyPolicy.html` })
 }
+
+const tutorialModalRef = ref(null)
+function showTutorial() {
+    console.log(tutorialModalRef.value)
+    tutorialModalRef.value.init()
+}
 </script>
 
 <style lang="less" scoped>

+ 19 - 4
src/views/ProductListPage.vue

@@ -44,12 +44,16 @@
             >
                 <template v-if="listType === 'list'">
                     <div class="product-list" v-for="(item, index) in list" :key="index">
-                        <product-info list v-model:info="list[index]"></product-info>
+                        <product-info list v-model:info="list[index]" :stopOfficial="stopOfficial"></product-info>
                     </div>
                 </template>
                 <template v-else>
                     <div class="product" v-for="(item, index) in list" :key="index">
-                        <product-small-info list v-model:info="list[index]"></product-small-info>
+                        <product-small-info
+                            list
+                            v-model:info="list[index]"
+                            :stopOfficial="stopOfficial"
+                        ></product-small-info>
                     </div>
                 </template>
             </van-list>
@@ -91,6 +95,17 @@ function getBatch() {
     })
 }
 
+//官方未开售(存在销量>0的藏品,销量为0的不可售)
+const stopOfficial = computed(() => {
+    let flag = false
+    list.value.forEach(item => {
+        if (item.sales > 0) {
+            flag = true
+        }
+    })
+    return flag
+})
+
 const listType = ref('row')
 const changeType = type => {
     listType.value = type
@@ -142,7 +157,7 @@ ion-button {
     .product {
         margin: 8px;
         background: var(--ion-color-step-50);
-        border-radius: 4px;
+        border-radius: 8px;
         width: calc(50vw - 24px);
         display: inline-block;
         overflow: hidden;
@@ -150,7 +165,7 @@ ion-button {
     .product-list {
         margin: 8px;
         background: var(--ion-color-step-50);
-        border-radius: 4px;
+        border-radius: 8px;
         overflow: hidden;
     }
     .product-list + .product-list {