panhui 4 лет назад
Родитель
Сommit
c2f2e36363

BIN
src/main/nine-space/src/assets/icon-sosuo.png


BIN
src/main/nine-space/src/assets/info_icon_ershoushichang.png


BIN
src/main/nine-space/src/assets/info_icon_xiliehuodong.png


BIN
src/main/nine-space/src/assets/info_icon_zhuzaoshangdian.png


BIN
src/main/nine-space/src/assets/info_icon_zhuzaozhe.png


BIN
src/main/nine-space/src/assets/nav_logo.png


+ 243 - 0
src/main/nine-space/src/components/Loading.vue

@@ -0,0 +1,243 @@
+<template>
+    <div class="loader">
+        <!-- <span class="text">Loading</span> -->
+        <span class="spinner"></span>
+    </div>
+</template>
+
+<style lang="less" scoped>
+.loader {
+    position: absolute;
+    top: 50%;
+    left: 50%;
+    -webkit-transform: translate(-50%, -50%);
+    -moz-transform: translate(-50%, -50%);
+    -mos-transform: translate(-50%, -50%);
+    -o-transform: translate(-50%, -50%);
+    transform: translate(-50%, -50%);
+    text-align: center;
+    /* disable selection and cursor changes */
+    -webkit-touch-callout: none;
+    -webkit-user-select: none;
+    -khtml-user-select: none;
+    -moz-user-select: none;
+    -ms-user-select: none;
+    user-select: none;
+    cursor: default;
+}
+
+/* Text align it the center of screen and connect the looped animation for 2.5 seconds */
+.loader .text {
+    position: absolute;
+    top: 18px;
+    left: 4px;
+    z-index: 5;
+    font-size: 20px;
+    text-transform: uppercase;
+    -webkit-animation: text 2.5s cubic-bezier(0.75, 0, 0.5, 1) infinite normal;
+    -moz-animation: text 2.5s cubic-bezier(0.75, 0, 0.5, 1) infinite normal;
+    -ms-animation: text 2.5s cubic-bezier(0.75, 0, 0.5, 1) infinite normal;
+    -o-animation: text 2.5s cubic-bezier(0.75, 0, 0.5, 1) infinite normal;
+    animation: text 2.5s cubic-bezier(0.75, 0, 0.5, 1) infinite normal;
+}
+
+/* Create a container for animation*/
+.spinner {
+    position: relative;
+    width: 50px;
+    height: 50px;
+    color: #fff;
+}
+
+.spinner:before,
+.spinner:after {
+    content: '';
+    position: relative;
+    display: block;
+}
+
+/* Create cube and set animation*/
+.spinner:before {
+    -webkit-animation: spinner 2.5s cubic-bezier(0.75, 0, 0.5, 1) infinite normal;
+    -moz-animation: spinner 2.5s cubic-bezier(0.75, 0, 0.5, 1) infinite normal;
+    -ms-animation: spinner 2.5s cubic-bezier(0.75, 0, 0.5, 1) infinite normal;
+    -o-animation: spinner 2.5s cubic-bezier(0.75, 0, 0.5, 1) infinite normal;
+    animation: spinner 2.5s cubic-bezier(0.75, 0, 0.5, 1) infinite normal;
+    width: 50px;
+    height: 50px;
+    background-color: #FFB178;
+}
+
+/* Create shadow and set animation*/
+.spinner:after {
+    -webkit-animation: shadow 2.5s cubic-bezier(0.75, 0, 0.5, 1) infinite normal;
+    -moz-animation: shadow 2.5s cubic-bezier(0.75, 0, 0.5, 1) infinite normal;
+    -ms-animation: shadow 2.5s cubic-bezier(0.75, 0, 0.5, 1) infinite normal;
+    -o-animation: shadow 2.5s cubic-bezier(0.75, 0, 0.5, 1) infinite normal;
+    animation: shadow 2.5s cubic-bezier(0.75, 0, 0.5, 1) infinite normal;
+    position: relative;
+    bottom: -17.5px;
+    height: 2.5px;
+    border-radius: 50%;
+    background-color: #322b27;
+}
+
+/* Animation keys */
+/* from cube to circle */
+@-webkit-keyframes spinner {
+    50% {
+        -webkit-border-radius: 50%;
+        -webkit-transform: scale(0.5) rotate(360deg);
+        background-color: #FFFECB;
+    }
+    100% {
+        -webkit-transform: scale(1) rotate(720deg);
+        background-color: #FFB178;
+    }
+}
+
+@-moz-keyframes spinner {
+    50% {
+        -moz-border-radius: 50%;
+        -moz-transform: scale(0.5) rotate(360deg);
+        background-color: #FFFECB;
+    }
+    100% {
+        -moz-transform: scale(1) rotate(720deg);
+        background-color: #FFB178;
+    }
+}
+
+@-mos-keyframes spinner {
+    50% {
+        -mos-border-radius: 50%;
+        -mos-transform: scale(0.5) rotate(360deg);
+        background-color: #FFFECB;
+    }
+    100% {
+        -mos-transform: scale(1) rotate(720deg);
+        background-color: #FFB178;
+    }
+}
+
+@-o-keyframes spinner {
+    50% {
+        -o-border-radius: 50%;
+        -o-transform: scale(0.5) rotate(360deg);
+        background-color: #FFFECB;
+    }
+    100% {
+        -o-transform: scale(1) rotate(720deg);
+        background-color: #FFB178;
+    }
+}
+
+@keyframes spinner {
+    50% {
+        border-radius: 50%;
+        transform: scale(0.5) rotate(360deg);
+        background-color: #FFFECB;
+    }
+    100% {
+        transform: scale(1) rotate(720deg);
+        background-color: #FFB178;
+    }
+}
+
+/* animation shadow */
+@-webkit-keyframes shadow {
+    50% {
+        -webkit-transform: scale(0.5);
+        background-color: #322b27;
+    }
+}
+
+@-moz-keyframes shadow {
+    50% {
+        -moz-transform: scale(0.5);
+        background-color: #322b27;
+    }
+}
+
+@-mos-keyframes shadow {
+    50% {
+        -mos-transform: scale(0.5);
+        background-color: #322b27;
+    }
+}
+
+@-o-keyframes shadow {
+    50% {
+        -o-transform: scale(0.5);
+        background-color: #322b27;
+    }
+}
+
+@keyframes shadow {
+    50% {
+        transform: scale(0.5);
+        background-color: #322b27;
+    }
+}
+
+/* animation text */
+@-webkit-keyframes text {
+    0% {
+        -webkit-transform: scale(1, 1);
+    }
+    50% {
+        -webkit-transform: scale(0.5, 0.5);
+    }
+    100% {
+        -webkit-transform: scale(1, 1);
+    }
+}
+
+@-moz-keyframes text {
+    0% {
+        -moz-transform: scale(1, 1);
+    }
+    50% {
+        -moz-transform: scale(0.5, 0.5);
+    }
+    100% {
+        -moz-transform: scale(1, 1);
+    }
+}
+
+@-mos-keyframes text {
+    0% {
+        -mos-transform: scale(1, 1);
+    }
+    50% {
+        -mos-transform: scale(0.5, 0.5);
+    }
+    100% {
+        -mos-transform: scale(1, 1);
+    }
+}
+
+@-o-keyframes text {
+    0% {
+        -o-transform: scale(1, 1);
+    }
+    50% {
+        -o-transform: scale(0.5, 0.5);
+    }
+    100% {
+        -o-transform: scale(1, 1);
+    }
+}
+
+@keyframes text {
+    0% {
+        transform: scale(1, 1);
+    }
+    50% {
+        transform: scale(0.5, 0.5);
+    }
+    100% {
+        transform: scale(1, 1);
+    }
+}
+</style>

+ 44 - 0
src/main/nine-space/src/components/Loading1.vue

@@ -0,0 +1,44 @@
+<template>
+    <div class="loading"><span class="loader-53"> </span></div>
+</template>
+
+<style lang="less" scoped>
+.loading {
+    display: flex;
+    justify-content: center;
+    padding: 50px 0;
+}
+.loader-53 {
+    width: 48px;
+    height: 48px;
+    display: inline-block;
+    position: relative;
+    background: linear-gradient(0deg, #fffecb 0%, #ffb178 100%);
+    border-radius: 4px;
+    -webkit-animation: flipX 2s linear infinite;
+    animation: flipX 2s linear infinite;
+}
+
+@-webkit-keyframes flipX {
+    0% {
+        transform: perspective(200px) rotateX(0deg) rotateY(0deg);
+    }
+    50% {
+        transform: perspective(200px) rotateX(-360deg) rotateY(0deg);
+    }
+    100% {
+        transform: perspective(200px) rotateX(-360deg) rotateY(-360deg);
+    }
+}
+@keyframes flipX {
+    0% {
+        transform: perspective(200px) rotateX(0deg) rotateY(0deg);
+    }
+    50% {
+        transform: perspective(200px) rotateX(-360deg) rotateY(0deg);
+    }
+    100% {
+        transform: perspective(200px) rotateX(-360deg) rotateY(-360deg);
+    }
+}
+</style>

+ 150 - 0
src/main/nine-space/src/components/product/productLarge.vue

@@ -0,0 +1,150 @@
+<template>
+    <router-link
+        :to="{
+            path: '/productDetail',
+            query: {
+                id: info.id
+            }
+        }"
+        class="product"
+    >
+        <van-image width="100%" height="calc(37.2vw - 17.9px)" :src="getImg(changeImgs(info.pic))" fit="cover" />
+
+        <div class="content">
+            <div class="name van-ellipsis">{{ info.name }}</div>
+            <div class="sales">
+                <span>限量</span>
+                <span>155份</span>
+            </div>
+            <div class="bottom">
+                <div class="miner">
+                    <van-image width="18" height="18" radius="18" :src="getImg(info.minterAvatar)" fit="cover" />
+                    <span>{{ info.minter }}</span>
+                </div>
+                <div class="flex1"></div>
+                <div class="price">
+                    <i class="font_family icon-icon_jiage"></i>
+                    <span> {{ info.price }}</span>
+                </div>
+            </div>
+        </div>
+    </router-link>
+</template>
+
+<script>
+import product from '../../mixins/product';
+export default {
+    mixins: [product],
+    props: {
+        info: {
+            type: Object,
+            default: () => {
+                return {};
+            }
+        }
+    },
+    methods: {
+        likeProduct() {
+            if (!this.info.liked) {
+                this.$http.get(`/collection/${this.info.id}/like`).then(() => {
+                    this.$emit('update:info', {
+                        ...this.info,
+                        liked: true,
+                        likes: this.info.likes + 1
+                    });
+                    this.$toast.success('收藏成功');
+                });
+            } else {
+                this.$http.get(`/collection/${this.info.id}/unlike`).then(() => {
+                    this.$emit('update:info', {
+                        ...this.info,
+                        liked: false,
+                        likes: this.info.likes - 1
+                    });
+                    this.$toast.success('取消收藏');
+                });
+            }
+        }
+    }
+};
+</script>
+
+<style lang="less" scoped>
+.product {
+    width: calc(100vw - 32px);
+    margin: 8px 16px;
+    position: relative;
+    background-color: #1c1e25;
+    display: inline-block;
+    border-radius: 12px;
+    overflow: hidden;
+
+    .van-image {
+        display: block;
+    }
+    .content {
+        padding: 10px;
+        .name {
+            font-size: @font2;
+            font-weight: bold;
+            color: #ffffff;
+            line-height: 24px;
+        }
+
+        .sales {
+            border-radius: 4px;
+            overflow: hidden;
+            font-size: @font1;
+            margin-top: 10px;
+
+            span {
+                padding: 0 10px;
+                line-height: 20px;
+                height: 20px;
+                display: inline-block;
+                &:first-child {
+                    background: @prim;
+                    color: @bg;
+                }
+                &:last-child {
+                    background-color: #3d3d3a;
+                    color: @prim;
+                }
+            }
+        }
+
+        .bottom {
+            display: flex;
+
+            .miner {
+                display: flex;
+                align-items: center;
+
+                span {
+                    color: @text3;
+                    font-size: @font1;
+                    margin-left: 6px;
+                }
+            }
+
+            .price {
+                font-size: 24px;
+                color: #fff;
+                line-height: 22px;
+                font-family: OSP;
+                .font_family {
+                    font-size: 11px;
+                    margin-bottom: 3px;
+                    display: inline-block;
+                }
+            }
+        }
+    }
+
+    .text {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+    }
+}
+</style>

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
src/main/nine-space/src/utils/84860-my-first-ever-lottie.json


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
src/main/nine-space/src/utils/84896-loader-25d-bear.json


+ 1 - 0
src/main/nine-space/src/utils/data 1.json

@@ -0,0 +1 @@
+{"v":"5.5.8","fr":30,"ip":0,"op":75,"w":821,"h":444,"nm":"第九空间-加载","ddd":0,"assets":[{"id":"image_0","w":821,"h":444,"u":"images/","p":"img_0.png","e":0},{"id":"image_1","w":738,"h":269,"u":"images/","p":"img_1.png","e":0},{"id":"comp_0","layers":[{"ddd":0,"ind":1,"ty":2,"nm":"圆角矩形 1","refId":"image_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.409,"y":0},"t":0,"s":[155.5,171,0],"to":[3.79,0,0],"ti":[-124.71,0,0]},{"t":74,"s":[988.5,199,0]}],"ix":2},"a":{"a":0,"k":[410.5,222,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"a":0,"k":{"i":[[-4.507,-3.192],[0,0],[3.192,-4.507],[0,0],[4.507,3.192],[0,0],[-3.192,4.507],[0,0]],"o":[[0,0],[4.507,3.192],[0,0],[-3.192,4.507],[0,0],[-4.507,-3.192],[0,0],[3.192,-4.507]],"v":[[316.435,63.822],[380.086,108.906],[382.466,122.847],[81.901,547.183],[67.961,549.563],[4.31,504.478],[1.93,490.538],[302.495,66.202]],"c":true},"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":0,"ix":4},"nm":"蒙版 1"}],"ip":0,"op":74,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":2,"nm":"图层 2","refId":"image_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[404,234.5,0],"ix":2},"a":{"a":0,"k":[369,134.5,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":300,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"图层 2","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[410.5,222,0],"ix":2},"a":{"a":0,"k":[410.5,222,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"w":821,"h":444,"ip":0,"op":300,"st":0,"bm":0}],"markers":[]}

+ 1 - 0
src/main/nine-space/src/utils/data.json

@@ -0,0 +1 @@
+{"v":"5.5.8","fr":30,"ip":0,"op":74,"w":821,"h":444,"nm":"第九空间-加载 (1)","ddd":0,"assets":[{"id":"image_0","w":821,"h":444,"u":"images/","p":"img_0.png","e":0},{"id":"image_1","w":738,"h":269,"u":"images/","p":"img_1.png","e":0}],"layers":[{"ddd":0,"ind":1,"ty":2,"nm":"圆角矩形 1","refId":"image_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.861,"y":1},"o":{"x":0.799,"y":0},"t":0,"s":[155.5,171,0],"to":[3.79,0,0],"ti":[-124.71,0,0]},{"t":74,"s":[988.5,199,0]}],"ix":2},"a":{"a":0,"k":[410.5,222,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"a":0,"k":{"i":[[-4.507,-3.192],[0,0],[3.192,-4.507],[0,0],[4.507,3.192],[0,0],[-3.192,4.507],[0,0]],"o":[[0,0],[4.507,3.192],[0,0],[-3.192,4.507],[0,0],[-4.507,-3.192],[0,0],[3.192,-4.507]],"v":[[316.435,63.822],[380.086,108.906],[382.466,122.847],[81.901,547.183],[67.961,549.563],[4.31,504.478],[1.93,490.538],[302.495,66.202]],"c":true},"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":0,"ix":4},"nm":"蒙版 1"}],"ip":0,"op":74,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":2,"nm":"图层 2","refId":"image_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[404,234.5,0],"ix":2},"a":{"a":0,"k":[369,134.5,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":300,"st":0,"bm":0}],"markers":[]}

+ 295 - 0
src/main/nine-space/src/views/DiscoverPre.vue

@@ -0,0 +1,295 @@
+<template>
+    <div class="discover">
+        <van-sticky ref="top" :offset-top="bar.value.show ? 46 : 0">
+            <div class="top">
+                <div class="top-btn">
+                    <div class="btn active">收藏探索</div>
+                    <div class="btn" @click="$router.replace('/creator')">铸造者</div>
+                </div>
+                <div class="search" @click="$router.push('/productSearch')">
+                    <img src="../assets/svgs/search.svg" alt="" />
+                </div>
+            </div>
+        </van-sticky>
+
+        <swiper pagination class="mySwiper" :autoplay="{ delay: 3500 }" v-if="banners.length > 0">
+            <swiper-slide v-for="item in banners" :key="item.id">
+                <img :src="item.pic" @click="goNext(item)" />
+            </swiper-slide>
+        </swiper>
+
+        <van-grid :border="false">
+            <van-grid-item text="精选推荐" :to="{ path: '/productList' }">
+                <template v-slot:icon>
+                    <img class="grid-img" src="../assets/info_icon_jingxuanxilie.png" />
+                </template>
+            </van-grid-item>
+            <van-grid-item
+                text="原创系列"
+                :to="{
+                    path: '/productList',
+                    query: {
+                        type: 'DEFAULT'
+                    }
+                }"
+            >
+                <template v-slot:icon>
+                    <img class="grid-img" src="../assets/info_icon_yuanchangxilie.png" />
+                </template>
+            </van-grid-item>
+            <van-grid-item
+                text="数字盲盒"
+                :to="{
+                    path: '/productList',
+                    query: {
+                        type: 'BLIND_BOX'
+                    }
+                }"
+            >
+                <template v-slot:icon>
+                    <img class="grid-img" src="../assets/info_icon_manghexilie.png" />
+                </template>
+            </van-grid-item>
+            <van-grid-item text="拍卖系列" @click="wait">
+                <template v-slot:icon>
+                    <img class="grid-img" src="../assets/info_icon_paimaixilie.png" />
+                </template>
+            </van-grid-item>
+        </van-grid>
+
+        <div class="title">本期推荐</div>
+
+        <van-list class="box-list" v-model:loading="loading" :finished="finished" finished-text="" @load="getList">
+            <template v-for="(item, index) in list" :key="item.id">
+                <product-info v-model:info="list[index]"></product-info>
+            </template>
+        </van-list>
+
+        <div class="tabbar-placeholder"></div>
+    </div>
+</template>
+
+<script>
+import { Swiper, SwiperSlide } from 'swiper/vue';
+
+import 'swiper/swiper.min.css';
+import 'swiper/swiper-bundle.min.css';
+
+import SwiperCore, { Pagination, Autoplay } from 'swiper';
+
+// install Swiper modules
+SwiperCore.use([Pagination, Autoplay]);
+
+import ProductInfo from '../components/product/productInfo.vue';
+import banner from '../mixins/banner';
+
+export default {
+    name: 'discover',
+    inject: ['bar'],
+    mixins: [banner],
+    components: {
+        Swiper,
+        SwiperSlide,
+        ProductInfo
+    },
+    data() {
+        return {
+            stiky: null,
+            banners: [],
+            list: [],
+            loading: false,
+            finished: false,
+            page: 0,
+            empty: false
+        };
+    },
+    mounted() {
+        this.getInit();
+    },
+    methods: {
+        getInit() {
+            this.$toast.loading({
+                message: '加载中...',
+                forbidClick: true
+            });
+            this.getBanner();
+        },
+        getBanner() {
+            this.$http
+                .post(
+                    '/banner/all',
+                    {
+                        query: {
+                            type: 'DISCOVER'
+                        },
+                        sort: 'sort,asc;createdAt,desc'
+                    },
+                    { body: 'json' }
+                )
+                .then(res => {
+                    this.banners = res.content;
+                    this.$toast.clear();
+                });
+        },
+        getList() {
+            if (this.page === 0) {
+                this.list = [];
+            }
+            this.loading = true;
+            this.finished = false;
+            this.empty = false;
+            this.$http
+                .post(
+                    '/collection/all',
+                    {
+                        page: 0,
+                        size: 20,
+                        query: {
+                            onShelf: true,
+                            del: false
+                        },
+                        sort: 'createdAt,desc'
+                    },
+                    { body: 'json' }
+                )
+                .then(res => {
+                    this.list = [...this.list, ...res.content];
+                    this.empty = res.empty;
+                    this.loading = false;
+                    this.finished = res.last;
+                    if (!this.finished) {
+                        this.page = this.page + 1;
+                    }
+                });
+        }
+    }
+};
+</script>
+
+<style lang="less" scoped>
+.top {
+    display: flex;
+    padding: 10px 16px;
+    background-color: @bg;
+    .top-btn {
+        flex-grow: 1;
+        .btn {
+            font-size: 16px;
+            // font-family: ZhenyanGB;
+            // font-weight: 400;
+            line-height: 26px;
+            display: inline-block;
+            vertical-align: text-bottom;
+
+            &.active {
+                color: @prim;
+                font-size: 20px;
+                font-weight: bold;
+                line-height: 30px;
+            }
+        }
+
+        .btn + .btn {
+            margin-left: 30px;
+        }
+    }
+}
+
+.discover {
+    background-color: @bg3;
+}
+
+::v-deep(.mySwiper) {
+    width: calc(100vw - 32px);
+    height: calc(50vw - 16px);
+    padding-top: 12px;
+
+    .swiper-pagination {
+        bottom: 12px;
+    }
+
+    .swiper-pagination-bullet {
+        width: 6px;
+        height: 2px;
+        border-radius: 1px;
+        background: #d7d7d7;
+    }
+
+    .swiper-pagination-bullet-active {
+        background: @prim;
+    }
+}
+
+.swiper-slide {
+    text-align: center;
+    font-size: 18px;
+
+    /* Center slide text vertically */
+    display: -webkit-box;
+    display: -ms-flexbox;
+    display: -webkit-flex;
+    display: flex;
+    -webkit-box-pack: center;
+    -ms-flex-pack: center;
+    -webkit-justify-content: center;
+    justify-content: center;
+    -webkit-box-align: center;
+    -ms-flex-align: center;
+    -webkit-align-items: center;
+    align-items: center;
+}
+
+.swiper-slide img {
+    display: block;
+    width: 100%;
+    height: 100%;
+    object-fit: cover;
+    border-radius: 4px;
+}
+.grid-img {
+    display: block;
+    width: 38px;
+    height: 38px;
+}
+.van-grid {
+    margin-top: 12px;
+}
+/deep/ .van-grid-item__content {
+    padding: 14px 0 16px;
+}
+/deep/ .van-grid-item__text {
+    color: #fff;
+    font-size: 13px;
+    line-height: 18px;
+    margin-top: 4px;
+}
+
+.title {
+    padding: 16px 20px 8px;
+    color: @prim;
+    font-size: 18px;
+    font-weight: bold;
+}
+
+.box-list {
+    // display: flex;
+    // flex-wrap: wrap;
+    padding: 0 8px 100px;
+}
+
+/deep/.van-tab {
+    color: #fff;
+    flex: 0;
+    padding: 20px;
+    flex-shrink: 0;
+    min-width: 74px;
+
+    &.van-tab--active {
+        color: @prim;
+    }
+}
+
+/deep/ .van-tabs__line {
+    bottom: 20px;
+}
+</style>

+ 235 - 0
src/main/nine-space/src/views/HomePre.vue

@@ -0,0 +1,235 @@
+<template>
+    <div class="home">
+        <swiper
+            :effect="'coverflow'"
+            :grabCursor="true"
+            :centeredSlides="true"
+            :slidesPerView="'auto'"
+            :coverflowEffect="{
+                rotate: 30,
+                stretch: 0,
+                depth: 0,
+                modifier: 1,
+                slideShadows: true
+            }"
+            :autoplay="{ delay: 3500 }"
+            class="mySwiper"
+            v-if="banners.length > 0"
+        >
+            <template v-for="item in banners" :key="item.id">
+                <swiper-slide>
+                    <van-image
+                        @click="goNext(item)"
+                        :radius="6"
+                        width="100%"
+                        height="calc(50vw - 33px)"
+                        :src="item.pic"
+                        fit="cover"
+                    />
+                </swiper-slide>
+            </template>
+        </swiper>
+
+        <div class="box" v-if="box.length > 0">
+            <page-title title="数字盲盒" :to="{ path: '/productList', query: { type: 'BLIND_BOX' } }"></page-title>
+            <div class="box-list">
+                <product-info v-for="(item, index) in box" :key="item.id" v-model:info="box[index]"></product-info>
+            </div>
+        </div>
+
+        <div class="box">
+            <page-title title="最HOT收藏品" :to="{ path: '/productList', query: { type: 'DEFAULT' } }"></page-title>
+            <div class="box-list">
+                <template v-for="(item, index) in products" :key="item.id">
+                    <product-info v-model:info="products[index]"></product-info
+                ></template>
+            </div>
+        </div>
+
+        <!-- <page-title title="最HOT收藏品"></page-title>
+
+    <div class="hot">
+      <product-info></product-info>
+      <div class="hot-right">
+        <product-small></product-small>
+        <product-small></product-small>
+      </div>
+    </div> -->
+
+        <div class="casting">
+            <page-title title="最受欢迎铸造者" :to="{ path: '/creatorList' }"></page-title>
+            <template v-for="(item, index) in miners" :key="index">
+                <creator-info
+                    :rank="index < 3 ? index + 1 : 0"
+                    v-model:info="miners[index]"
+                    size="large"
+                ></creator-info>
+            </template>
+        </div>
+
+        <div class="tabbar-placeholder"></div>
+    </div>
+</template>
+
+<script>
+// @ is an alias to /src
+
+import { Swiper, SwiperSlide } from 'swiper/vue';
+
+// Import Swiper styles
+
+import 'swiper/swiper.min.css';
+import 'swiper/swiper-bundle.min.css';
+
+import SwiperCore, { EffectCoverflow, Autoplay } from 'swiper';
+import ProductInfo from '../components/product/productInfo.vue';
+// import ProductSmall from "../components/product/productSmall.vue";
+import CreatorInfo from '../components/creator/CreatorInfo.vue';
+import banner from '../mixins/banner';
+
+// install Swiper modules
+SwiperCore.use([EffectCoverflow, Autoplay]);
+
+export default {
+    name: 'Home',
+    inject: ['bs'],
+    mixins: [banner],
+    components: {
+        Swiper,
+        SwiperSlide,
+        ProductInfo,
+        CreatorInfo
+    },
+    data() {
+        return {
+            banners: [],
+            box: [],
+            products: [],
+            miners: []
+        };
+    },
+    mounted() {
+        this.$toast.loading({
+            message: '加载中...',
+            forbidClick: true
+        });
+        Promise.all([
+            this.$http
+                .post(
+                    '/banner/all',
+                    {
+                        query: {
+                            type: 'HOME'
+                        },
+                        sort: 'sort,asc;createdAt,desc'
+                    },
+                    { body: 'json' }
+                )
+                .then(res => {
+                    this.banners = res.content;
+                }),
+            this.getProduct('BLIND_BOX').then(res => {
+                this.box = res;
+            }),
+            this.getProduct().then(res => {
+                this.products = res;
+            }),
+            this.getMiner()
+        ]).then(() => {
+            this.$toast.clear();
+        });
+    },
+    methods: {
+        getProduct(type = 'DEFAULT') {
+            return this.$http
+                .post(
+                    '/collection/all',
+                    {
+                        page: 0,
+                        size: 4,
+                        query: {
+                            type: type,
+                            onShelf: true,
+                            del: false
+                        },
+                        sort: 'createdAt,desc'
+                    },
+                    { body: 'json' }
+                )
+                .then(res => {
+                    return Promise.resolve(res.content);
+                });
+        },
+        getMiner() {
+            this.$http
+                .post(
+                    '/user/all',
+                    {
+                        page: 0,
+                        query: { hasRole: 'ROLE_MINTER' },
+                        size: 5,
+                        sort: 'sales,desc'
+                    },
+                    { body: 'json' }
+                )
+                .then(res => {
+                    this.miners = res.content;
+                    // console.log(this.miners);
+                });
+        }
+    }
+};
+</script>
+
+<style lang="less" scoped>
+.swiper {
+    width: 100%;
+    padding-top: 50px;
+    padding-bottom: 50px;
+}
+
+.swiper-slide {
+    background-position: center;
+    background-size: cover;
+    width: calc(100vw - 66px);
+    height: calc(50vw - 33px);
+
+    img {
+        width: calc(100vw - 66px);
+        height: calc(50vw - 33px);
+        display: block;
+    }
+    .van-image {
+        border-radius: 6px;
+    }
+}
+
+.swiper-slide img {
+    display: block;
+    width: 100%;
+}
+.home {
+    padding: 10px 0 100px 0;
+}
+
+.hot {
+    display: flex;
+    padding: 0 8px;
+
+    .hot-right {
+        display: flex;
+        flex-direction: column;
+        justify-content: space-between;
+    }
+}
+
+.box-list {
+    padding: 0 8px;
+    display: flex;
+    flex-wrap: wrap;
+
+    .product {
+        margin-bottom: 16px;
+    }
+}
+</style>

+ 554 - 0
src/main/nine-space/src/views/product/DetailPre.vue

@@ -0,0 +1,554 @@
+<template>
+    <div class="detail">
+        <swiper pagination class="mySwiper" v-if="banners.length > 0">
+            <swiper-slide v-for="(item, index) in banners" :key="index">
+                <!-- <img :src="item" /> -->
+
+                <video
+                    class="swiper-video"
+                    v-if="isVideo(item)"
+                    :src="item.url"
+                    :poster="getImg(changeImgs([item]), '', 1200)"
+                    controls="controls"
+                >
+                    您的浏览器不支持 video 标签。
+                </video>
+                <van-image
+                    v-else
+                    @click="preview(index, changeImgs(banners))"
+                    :src="getImg(item.url, '', 1200)"
+                    width="100vw"
+                    height="100vw"
+                    fit="scale-down"
+                />
+            </swiper-slide>
+        </swiper>
+
+        <div class="info">
+            <div class="name" v-if="info.salable && startTime">
+                <div class="name1">首发抢购倒计时</div>
+                <div class="name2">{{ startTime }}</div>
+            </div>
+
+            <div class="price-line" v-if="info.salable">
+                <div class="price"><i class="font_family icon-icon_jiage"></i>{{ info.price }}</div>
+                <div class="price-sub" v-if="info.originalPrice">¥{{ info.originalPrice || 0 }}</div>
+                <div class="sub" v-if="info.royalties">
+                    含 <span>{{ info.royalties }}%</span> 版税
+                </div>
+                <div class="flex1"></div>
+                <div class="text" v-if="info.salable && info.total > 0">
+                    <span>已售 {{ info.sale }}</span>
+                    <span>剩余 {{ info.stock }}</span>
+                </div>
+            </div>
+            <div class="title">{{ info.name }}</div>
+            <div class="info-bottom">
+                <span class="text1"> 编号 {{ info.id }} </span>
+                <!-- <van-button
+          v-if="info.type !== 'DEFAULT'"
+          type="primary"
+          plain
+          size="mini"
+          >选择其他编号</van-button
+        > -->
+                <div class="flex1"></div>
+                <like-button :isLike="info.liked" @click="likeProduct">
+                    {{ info.likes }}
+                </like-button>
+            </div>
+        </div>
+
+        <driver />
+        <van-cell
+            value="进入主页"
+            is-link
+            class="creator"
+            :to="{
+                path: '/creatorDetail',
+                query: {
+                    id: info.minterId
+                }
+            }"
+        >
+            <template #icon>
+                <van-image width="40" height="40" :src="info.minterAvatar" fit="cover" radius="100" />
+            </template>
+            <template #title>
+                <div class="text1">{{ info.minter }}</div>
+                <div class="text2">铸造者</div>
+            </template>
+        </van-cell>
+
+        <driver />
+
+        <div class="goods">
+            <template v-if="boxs.length > 0">
+                <div class="page-title">盲盒详情</div>
+                <swiper :slidesPerView="'auto'" :spaceBetween="20" class="detail-swiper">
+                    <swiper-slide v-for="(item, index) in boxs" :key="index">
+                        <van-image
+                            width="100%"
+                            height="170"
+                            :src="getImg(item, '', 1000)"
+                            fit="cover"
+                            radius="12"
+                            @click="preview(index, boxs)"
+                        />
+                    </swiper-slide>
+                </swiper>
+            </template>
+            <template v-if="properties.length > 0">
+                <div class="page-title">商品特性</div>
+                <div class="specific-list">
+                    <div class="specific-item" v-for="(item, index) in properties" :key="index">
+                        <div class="text1">{{ item.name }}</div>
+                        <div class="text2">{{ item.value }}</div>
+                    </div>
+                </div>
+            </template>
+            <template v-if="info.detail">
+                <div class="page-title">作品描述</div>
+                <div class="page-text page-detail" v-html="info.detail"></div
+            ></template>
+        </div>
+        <div class="btn van-safe-area-bottom" v-if="info.isAppointment">
+            <div class="btns">
+                <van-button @click="appointment" type="primary" block round>
+                    {{ info.appointment ? '已预约' : '一键预约' }}
+                </van-button>
+            </div>
+        </div>
+
+        <div class="btn van-safe-area-bottom" ref="btn" v-if="isBuy">
+            <div class="btns">
+                <van-button type="primary" block round @click="buy">立即购买</van-button>
+            </div>
+        </div>
+
+        <!-- <driver /> -->
+
+        <!-- <van-collapse v-model="activeName" accordion>
+      <van-collapse-item title="交易记录" name="1">
+        <van-cell title="单元格" value="内容"> </van-cell>
+      </van-collapse-item>
+    </van-collapse> -->
+
+        <driver />
+
+        <post :info="info" />
+    </div>
+</template>
+
+<script>
+import { Swiper, SwiperSlide } from 'swiper/vue';
+
+import 'swiper/swiper.min.css';
+import 'swiper/swiper-bundle.min.css';
+
+import SwiperCore, { Pagination } from 'swiper';
+import Post from '../../components/Post.vue';
+import { ImagePreview } from 'vant';
+
+// install Swiper modules
+SwiperCore.use([Pagination]);
+import product from '../../mixins/product';
+
+export default {
+    components: {
+        Swiper,
+        SwiperSlide,
+        Post
+    },
+    mixins: [product],
+    data() {
+        return {
+            activeName: '1',
+            info: {},
+            liked: false,
+            btn: null,
+            blindBoxItems: []
+        };
+    },
+    computed: {
+        banners() {
+            return this.info.pic || [];
+        },
+        properties() {
+            return this.info.properties || [];
+        },
+        isBuy() {
+            return this.info.stock && this.info.onShelf && this.info.salable;
+        },
+        boxs() {
+            let list = [...this.blindBoxItems];
+            return list.map(item => {
+                return this.changeImgs(item.pic);
+            });
+        }
+    },
+    mounted() {
+        this.getProduct();
+    },
+    methods: {
+        preview(index = 0, list = []) {
+            ImagePreview({
+                images: [...list].map(item => {
+                    return item;
+                }),
+                startPosition: index
+            });
+        },
+        appointment() {
+            if (this.info.appointment) {
+                return;
+            }
+            this.$http
+                .post('/collection/appointment?id=' + this.info.id)
+                .then(res => {
+                    this.getProduct();
+                    this.$toast.success('预约成功');
+                })
+                .catch(e => {
+                    if (e.error) {
+                        this.$toast.warning(e.error);
+                    }
+                });
+        },
+        getProduct() {
+            this.$toast.loading({
+                message: '加载中...',
+                forbidClick: true
+            });
+            this.$http
+                .get('/collection/get/' + this.$route.query.id)
+                .then(res => {
+                    this.info = res;
+                    this.getTime(res.startTime);
+                    this.$nextTick(() => {
+                        if (this.isBuy) {
+                            this.btn = this.$refs.btn;
+                        }
+                    });
+                    this.$toast.clear();
+
+                    if (res.type === 'BLIND_BOX') {
+                        return this.$http.post(
+                            '/blindBoxItem/all',
+                            {
+                                query: {
+                                    blindBoxId: res.id
+                                }
+                            },
+                            { body: 'json' }
+                        );
+                    } else {
+                        return Promise.resolve();
+                    }
+                })
+                .then(res => {
+                    if (res) {
+                        this.blindBoxItems = res.content;
+                    }
+                });
+        },
+        likeProduct() {
+            if (!this.info.liked) {
+                this.$http.get(`/collection/${this.info.id}/like`).then(() => {
+                    this.getProduct();
+                    this.$toast.success('收藏成功');
+                });
+            } else {
+                this.$http.get(`/collection/${this.info.id}/unlike`).then(() => {
+                    this.getProduct();
+                    this.$toast.success('取消收藏');
+                });
+            }
+        },
+        buy() {
+            this.checkLogin().then(() => {
+                this.$router.push({
+                    path: '/submit',
+                    query: {
+                        id: this.$route.query.id
+                    }
+                });
+            });
+        }
+    }
+};
+</script>
+
+<style lang="less" scoped>
+.detail {
+    padding-bottom: 100px;
+}
+.info {
+    // height: 164px;
+    background-color: @bg;
+    border-radius: 20px 20px 0 0;
+    transform: translateY(-16px);
+    position: relative;
+    z-index: 2;
+    padding: 16px 16px 0;
+    box-sizing: border-box;
+    .name {
+        text-align: center;
+        .name1 {
+            font-size: 14px;
+            font-weight: 400;
+            color: #ffffff;
+            line-height: 24px;
+        }
+        .name2 {
+            font-size: 16px;
+            font-weight: 400;
+            color: #fdfb60;
+            line-height: 24px;
+            background: linear-gradient(135deg, #fdfb60 0%, #ff8f3e 100%);
+            -webkit-background-clip: text;
+            -webkit-text-fill-color: transparent;
+        }
+    }
+    .price {
+        font-size: 36px;
+        font-family: OSP;
+        color: #fdfb60;
+        line-height: 36px;
+        transform: translateY(3px);
+
+        .font_family {
+            font-size: 10px;
+            line-height: 24px;
+            vertical-align: middle;
+        }
+    }
+
+    .price-line {
+        display: flex;
+        align-items: flex-end;
+        .sub {
+            flex-grow: 1;
+            margin-left: 5px;
+            font-size: 14px;
+            color: #949699;
+            line-height: 16px;
+            span {
+                color: #fff;
+            }
+        }
+
+        .price-sub {
+            font-size: 14px;
+            color: @text3;
+            text-decoration: line-through;
+            margin-left: 10px;
+        }
+
+        .text {
+            font-size: 14px;
+            color: @text3;
+            line-height: 16px;
+            span {
+                margin-left: 10px;
+            }
+        }
+    }
+
+    .title {
+        font-size: 20px;
+        font-weight: bold;
+        color: #ffffff;
+        line-height: 28px;
+        margin-top: 12px;
+    }
+
+    .info-bottom {
+        display: flex;
+        position: relative;
+        margin-top: 4px;
+        height: 24px;
+        .text1 {
+            font-size: 14px;
+            color: #949699;
+            line-height: 24px;
+        }
+        .van-button {
+            font-size: 13px;
+            color: #fdfb60;
+            line-height: 24px;
+            background: linear-gradient(135deg, #fdfb60 0%, #ff8f3e 100%);
+            -webkit-background-clip: text;
+            -webkit-text-fill-color: transparent;
+            border-width: 0px;
+            margin-left: 10px;
+        }
+
+        .like {
+            position: absolute;
+            right: 0;
+        }
+    }
+}
+
+/deep/.creator {
+    align-items: center;
+    padding: 24px 16px 24px;
+    .van-cell__title {
+        margin-left: 12px;
+        .text1 {
+            font-size: 16px;
+            line-height: 24px;
+        }
+        .text2 {
+            font-size: 12px;
+            color: @text3;
+            line-height: 22px;
+        }
+    }
+
+    .van-cell__value {
+        font-size: 13px;
+    }
+}
+.buy {
+    width: 100%;
+    display: block;
+    height: 52px;
+    background: linear-gradient(135deg, @prim 0%, @warn 100%);
+    border-radius: 8px;
+    border-width: 0;
+    color: #000;
+    &:hover {
+        background: linear-gradient(135deg, darken(@prim, 10%), darken(@warn, 10%));
+    }
+
+    &.used {
+        background: linear-gradient(135deg, darken(@prim, 50%), darken(@warn, 50%));
+        color: @text3;
+    }
+}
+/deep/ .mySwiper {
+    width: 100vw;
+    height: 100vw;
+    z-index: 1;
+
+    .swiper-pagination {
+        bottom: 22px;
+    }
+
+    .swiper-pagination-bullet {
+        width: 6px;
+        height: 2px;
+        border-radius: 1px;
+        background: #d7d7d7;
+    }
+
+    .swiper-pagination-bullet-active {
+        background: @prim;
+    }
+
+    .swiper-slide img {
+        display: block;
+        width: 100%;
+        height: 100%;
+        object-fit: cover;
+        border-radius: 4px;
+    }
+}
+.goods {
+    padding: 20px 16px;
+
+    .page-title {
+        &:not(:first-child) {
+            padding-top: 16px;
+        }
+    }
+}
+
+.page-title {
+    font-size: 18px;
+    font-weight: bold;
+    color: #ffffff;
+    line-height: 28px;
+}
+.specific-list {
+    padding: 16px 0;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+}
+.specific-item {
+    width: 94px;
+    height: 62px;
+    border-radius: 4px;
+    border: solid 0px transparent;
+    padding: 1px;
+    background-image: linear-gradient(@bg, @bg), linear-gradient(135deg, #fdfb60, #ff8f3e);
+    background-origin: border-box;
+    box-sizing: border-box;
+    background-clip: content-box, border-box;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    flex-direction: column;
+
+    .text1 {
+        font-size: 14px;
+        color: @text3;
+        line-height: 24px;
+    }
+
+    .text2 {
+        font-size: 14px;
+        color: #ffffff;
+        line-height: 24px;
+    }
+}
+
+.page-text {
+    font-size: 14px;
+    color: #ffffff;
+    line-height: 28px;
+    margin-top: 10px;
+}
+
+.btn {
+    position: fixed;
+    z-index: 20;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    background: #202122ee;
+    .btns {
+        padding: 6px 42px;
+    }
+    .van-button {
+        background: linear-gradient(135deg, #fdfb60 0%, #ff8f3e 100%);
+        color: #333230;
+        font-size: 16px;
+        border-width: 0px;
+    }
+}
+
+.detail-swiper {
+    height: 170px;
+    margin-top: 12px;
+}
+
+.swiper-slide {
+    width: 35.2vw;
+}
+
+/deep/.page-detail {
+    img {
+        display: block;
+        max-width: 100%;
+        height: auto;
+    }
+}
+.swiper-video {
+    width: 100vw;
+    height: 100vw;
+}
+</style>

Некоторые файлы не были показаны из-за большого количества измененных файлов