panhui 3 rokov pred
rodič
commit
ebe9f7f392

BIN
src/assets/icon_jiage_lv.png


+ 396 - 0
src/components/product/productPrefixNameInfo.vue

@@ -0,0 +1,396 @@
+<template>
+    <div class="product" :style="{ background: bg }" @click="goDetail" :class="{ dark: dark, domain: domain }">
+        <div class="product-top">
+            <van-image
+                width="100%"
+                height="calc(50vw - 24px)"
+                :src="getImg(changeImgs(info.pic, 600))"
+                :fit="info.type === 'PICTURE' ? 'cover' : 'contain'"
+            />
+            <div class="number" v-if="info.number">编号:{{ info.number }}</div>
+        </div>
+
+        <div class="content">
+            <div class="name van-ellipsis">
+                <img
+                    :src="
+                        info.salable
+                            ? require('@assets/png-xiao-lv@3x (1).png')
+                            : require('@assets/png-xiao-zi@3x (1).png')
+                    "
+                    alt=""
+                    class="name_img"
+                    v-if="info.type == 'DOMAIN'"
+                />
+                {{ info.name }}
+            </div>
+
+            <!-- <div class="sale-list">
+                <div class="sales">
+                    <span class="sales-fir">流通</span>
+                    <span>233份</span>
+                </div>
+            </div> -->
+
+            <!-- <template v-if="info.type === 'PICTURE'">
+                <div class="types">
+                    <div class="type">个人作品</div>
+                </div>
+
+                <div class="types-status">仅展示</div>
+            </template>
+
+            <template v-else>
+                
+            </template> -->
+            <div class="text" v-if="info.type !== 'PICTURE'">
+                <div class="price-content" v-if="info.salable">
+                    <div class="price">
+                        <img src="@assets/icon_jiage_lv.png" alt="" />
+                        <span>{{ info.price }}</span>
+                        <small>起</small>
+                    </div>
+                    <div class="flex1"></div>
+                    <div class="text1" v-if="info.total !== 1">
+                        <span>{{ Math.min(info.sale, info.total) }}/</span>
+                        <span>{{ info.total }}</span>
+                    </div>
+                </div>
+                <div class="status" v-else>仅展示</div>
+                <div class="flex1"></div>
+                <!-- <like-button :isLike="info.liked" @click.stop="likeProduct">
+                    {{ info.likes }}
+                </like-button> -->
+            </div>
+            <div class="sold xianliang" v-if="time && info.salable">
+                <img src="@assets/shizhong.png" alt="" />
+                <span>{{ startTime || time }}</span>
+            </div>
+            <div class="sold" v-if="isSolded">已售罄</div>
+            <div class="sold zhifu" v-else-if="info.inPaying">
+                <img src="@assets/zhi_fu_zhong.png" alt="" />
+                <span>支付中</span>
+            </div>
+            <div class="sold" v-else-if="isSold" style="color: #ff4f50">即将售罄</div>
+        </div>
+    </div>
+</template>
+
+<script>
+import product from '../../mixins/product';
+export default {
+    mixins: [product],
+    props: {
+        info: {
+            type: Object,
+            default: () => {
+                return {};
+            }
+        },
+        dark: {
+            type: Boolean,
+            default: false
+        },
+        domain: {
+            type: Boolean,
+            default: false
+        },
+        type: {
+            type: String,
+            default: ''
+        },
+        bg: {
+            type: String,
+            default: ''
+        }
+    },
+    computed: {
+        time() {
+            if (this.info.startTime) {
+                if (this.dayjs().isSameOrBefore(this.info.startTime, 'YYYY-MM-DD HH:mm:ss')) {
+                    return this.dayjs(this.info.startTime, 'YYYY-MM-DD HH:mm:ss').format('MM月DD日');
+                }
+            }
+
+            return '';
+        }
+    },
+    mounted() {
+        if (this.info.startTime) {
+            var x = this.dayjs(this.info.startTime);
+            var y = this.dayjs();
+            let d = this.dayjs.duration(x.diff(y));
+            let day = parseInt(d.asDays());
+            if (day <= 0) {
+                this.getTime(this.info.startTime);
+            }
+        }
+    },
+    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('取消收藏');
+                });
+            }
+        },
+        goDetail() {
+            this.$router.push({
+                path: '/prefixNameList',
+                query: {
+                    id: this.info.id
+                }
+            });
+        }
+    }
+};
+</script>
+
+<style lang="less" scoped>
+.product {
+    @prim: #00fe1e;
+    width: calc(50vw - 24px);
+    margin: 8px;
+    background-color: @bg2;
+    // background: #373B3E;
+    display: inline-block;
+    border-radius: 8px;
+    overflow: hidden;
+    position: relative;
+
+    .van-image {
+        overflow: hidden;
+        display: block;
+    }
+    .content {
+        padding: 6px 10px 8px;
+
+        .name {
+            font-size: @font2;
+            color: @text0;
+            // color: #FFFFFF;
+            line-height: 24px;
+            display: flex;
+            align-items: center;
+            .name_img {
+                width: 10px;
+                height: 18px;
+                margin-right: 4px;
+            }
+        }
+
+        .price {
+            font-size: @font4;
+            font-family: OSP;
+            color: @prim;
+            line-height: 18px;
+            // padding: 12px 0;
+            .flex();
+
+            img {
+                display: inline-block;
+                width: 8px;
+                margin-top: 3px;
+            }
+
+            small {
+                font-size: 12px;
+                font-weight: bold;
+                color: #00fe1e;
+                line-height: 18px;
+                margin: 2px 0 0 2px;
+            }
+        }
+        .status {
+            line-height: 24px;
+            padding: 10px 0 8px;
+            color: @text3;
+            font-size: @font2;
+        }
+    }
+
+    .text {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        padding-top: 12px;
+        .text1 {
+            font-weight: 400;
+            color: @text3;
+            line-height: 24px;
+            span {
+                &:last-child {
+                    color: #fff;
+                    font-size: @font1;
+                }
+            }
+        }
+    }
+
+    &.dark {
+        background-color: #373b3e;
+
+        .name {
+            color: #fff;
+        }
+
+        .price {
+            color: #00fe1e;
+        }
+    }
+    &.domain {
+        background: #161414;
+
+        .name {
+            color: #fff;
+        }
+
+        .price {
+            color: #00fe1e;
+        }
+    }
+}
+.price-content {
+    display: flex;
+    align-items: center;
+    width: 100%;
+    .text1 {
+        font-size: @font1;
+        color: @text3;
+        line-height: 12px;
+    }
+}
+
+.minter {
+    display: flex;
+    font-size: @font1;
+    color: @text3;
+    line-height: 22px;
+    align-items: center;
+    margin-right: 10px;
+    overflow: hidden;
+    .van-image {
+        flex-shrink: 0;
+    }
+    span {
+        margin-left: 4px;
+    }
+}
+.sold {
+    background-color: @bg2;
+    font-size: @font1;
+    color: @text3;
+    padding: 0 17px;
+    border-radius: 13px;
+    line-height: 24px;
+    position: absolute;
+    top: 6px;
+    left: 6px;
+    z-index: 5;
+
+    &.zhifu {
+        background: #ffefd2;
+        padding: 0 8px;
+        .flex();
+        img {
+            width: 18px;
+            height: 18px;
+        }
+        span {
+            font-size: 12px;
+            color: #bf8c31;
+            line-height: 20px;
+            margin-left: 1px;
+        }
+    }
+}
+
+.xianliang {
+    position: absolute;
+    top: 16px;
+    left: 16px;
+    font-size: @font1;
+    color: @prim;
+    display: flex;
+    align-items: center;
+    // border-radius: 13px !important;
+    z-index: 4;
+    padding: 0 10px !important;
+
+    img {
+        width: 18px;
+        height: 18px;
+        margin-right: 3px;
+    }
+}
+.product-top {
+    position: relative;
+    overflow: hidden;
+    // box-shadow: inset 0 0 5px 10px #000;
+}
+.number {
+    position: absolute;
+    bottom: 0px;
+    left: 0;
+    right: 0;
+    font-size: 12px;
+    color: @text3;
+    line-height: 22px;
+    background-color: fade(#000, 80%);
+    padding: 0 10px;
+    height: 22px;
+}
+.types {
+    .flex();
+    padding-top: 6px;
+    .type {
+        font-size: 11px;
+        color: #3b260c;
+        line-height: 18px;
+        padding: 0 6px;
+        background: #ffe196;
+        border-radius: 2px;
+    }
+}
+.types-status {
+    font-size: 12px;
+    color: #939599;
+    line-height: 18px;
+    margin-top: 8px;
+}
+.sale-list {
+    padding-top: 4px;
+}
+.sales {
+    overflow: hidden;
+    font-size: @font1;
+    border-radius: 4px;
+
+    span {
+        padding: 0 10px;
+        line-height: 18px;
+        height: 18px;
+        display: inline-block;
+        &.sales-fir {
+            background: #00fe1e;
+            color: #000000;
+        }
+        background-color: rgba(255, 255, 255, 0.08);
+        color: #00fe1e;
+    }
+}
+</style>

+ 2 - 5
src/components/rice/Share.vue

@@ -44,13 +44,9 @@ import http from '../../plugins/http';
 import resolveUrl from 'resolve-url';
 import resolveUrl from 'resolve-url';
 import store from '../../store';
 import store from '../../store';
 
 
-const emit = defineEmits(['send']);
+const emit = defineEmits(['refreashTask']);
 const show = ref(false);
 const show = ref(false);
 
 
-function send() {
-    emit('send');
-}
-
 function init() {
 function init() {
     show.value = true;
     show.value = true;
     share();
     share();
@@ -102,6 +98,7 @@ onMounted(() => {
                     shareSucc.value = Number(res.code);
                     shareSucc.value = Number(res.code);
                     store.commit('setRiceShare', false);
                     store.commit('setRiceShare', false);
                     sessionStorage.removeItem('riceShare');
                     sessionStorage.removeItem('riceShare');
+                    emit('refreashTask');
                 });
                 });
         }
         }
     }, 1000);
     }, 1000);

+ 1 - 1
src/components/rice/Task.vue

@@ -106,7 +106,7 @@ function init() {
     show.value = true;
     show.value = true;
     getTask();
     getTask();
 }
 }
-defineExpose({ init });
+defineExpose({ init, getTask });
 
 
 const {
 const {
     appContext: {
     appContext: {

+ 10 - 0
src/router/index.js

@@ -353,6 +353,16 @@ const routes = [
             menuPage: true
             menuPage: true
         }
         }
     },
     },
+    {
+        path: '/prefixNameList',
+        name: 'prefixNameList',
+        component: () => import('../views/product/PrefixNameList.vue'),
+        meta: {
+            pageType: Page.Every,
+            tabColor: '#181818',
+            menuPage: true
+        }
+    },
     {
     {
         path: '/newsDetail',
         path: '/newsDetail',
         name: 'newsDetail',
         name: 'newsDetail',

+ 6 - 0
src/styles/app.less

@@ -347,3 +347,9 @@ input:-webkit-autofill {
         }
         }
     }
     }
 }
 }
+
+.preSticky {
+    position: relative;
+    z-index: 99;
+}
+

+ 4 - 0
src/styles/font.less

@@ -28,6 +28,10 @@
     font-family: 'AlibabaPuHuiTi';
     font-family: 'AlibabaPuHuiTi';
     src: url(https://cdn.raex.vip/font/2023-03-07-14-51-35DDenbsJb.ttf);
     src: url(https://cdn.raex.vip/font/2023-03-07-14-51-35DDenbsJb.ttf);
 }
 }
+@font-face {
+    font-family: 'AlimamaShuHeiTi';
+    src: url(https://cdn.raex.vip/font/2023-03-24-10-09-25HtghnVXP.ttf);
+}
 
 
 .font_family {
 .font_family {
     font-family: 'font_family' !important;
     font-family: 'font_family' !important;

+ 8 - 7
src/views/Discover.vue

@@ -207,12 +207,9 @@
                 :immediate-check="false"
                 :immediate-check="false"
             >
             >
                 <template v-for="(item, index) in showList" :key="item.id">
                 <template v-for="(item, index) in showList" :key="item.id">
-                    <product-info
-                        v-if="sort === 'collection' || sort === 'collection_MY' || sort === 'domain'"
-                        v-model:info="list[index]"
-                        dark
-                    >
+                    <product-info v-if="sort === 'collection_MY' || sort === 'domain'" v-model:info="list[index]" dark>
                     </product-info>
                     </product-info>
+                    <prefixName-info v-if="sort === 'collection'" v-model:info="list[index]" dark></prefixName-info>
                     <creator-small v-else-if="sort === 'creator'" v-model:info="list[index]"> </creator-small>
                     <creator-small v-else-if="sort === 'creator'" v-model:info="list[index]"> </creator-small>
                     <show-info v-else-if="sort === 'art_Exhibition'" v-model:info="list[item.index]" list> </show-info>
                     <show-info v-else-if="sort === 'art_Exhibition'" v-model:info="list[item.index]" list> </show-info>
                     <show-info v-else-if="sort === 'hot'" v-model:info="list[index]" list> </show-info>
                     <show-info v-else-if="sort === 'hot'" v-model:info="list[index]" list> </show-info>
@@ -233,6 +230,7 @@
 <script>
 <script>
 import product from '../mixins/product';
 import product from '../mixins/product';
 import ProductInfo from '../components/product/productInfo.vue';
 import ProductInfo from '../components/product/productInfo.vue';
+import PrefixNameInfo from '../components/product/productPrefixNameInfo.vue';
 import banner from '../mixins/banner';
 import banner from '../mixins/banner';
 import CreatorSmall from '../components/creator/CreatorSmall.vue';
 import CreatorSmall from '../components/creator/CreatorSmall.vue';
 import ShowInfo from '../components/asset/showInfo.vue';
 import ShowInfo from '../components/asset/showInfo.vue';
@@ -244,7 +242,8 @@ export default {
     components: {
     components: {
         ProductInfo,
         ProductInfo,
         CreatorSmall,
         CreatorSmall,
-        ShowInfo
+        ShowInfo,
+        PrefixNameInfo
     },
     },
     data() {
     data() {
         return {
         return {
@@ -430,6 +429,8 @@ export default {
             let query = {
             let query = {
                 onShelf: true,
                 onShelf: true,
                 del: false,
                 del: false,
+                salable: true,
+                distinctPrefix: true,
                 source: this.$store.state.reviewPay ? 'OFFICIAL' : '',
                 source: this.$store.state.reviewPay ? 'OFFICIAL' : '',
                 notLike: this.notLike,
                 notLike: this.notLike,
                 type: 'DEFAULT,BLIND_BOX'
                 type: 'DEFAULT,BLIND_BOX'
@@ -559,7 +560,7 @@ export default {
         }
         }
     },
     },
     beforeRouteLeave(to, from, next) {
     beforeRouteLeave(to, from, next) {
-        if (!to.meta.menuPage || to.path === '/hall' || to.name === 'productDetail') {
+        if (!to.meta.menuPage || to.path === '/hall' || to.name === 'productDetail' || to.path === '/prefixNameList') {
             this.scrollTop = this.scrollWrapper.scrollTop;
             this.scrollTop = this.scrollWrapper.scrollTop;
             this.setKeeps(['index', 'discover']);
             this.setKeeps(['index', 'discover']);
         } else {
         } else {

+ 469 - 0
src/views/product/PrefixNameList.vue

@@ -0,0 +1,469 @@
+<template>
+    <div class="page">
+        <van-sticky class="preSticky" @change="onSticky" z-index="99">
+            <div class="navBar" :class="{ isFixed: isFixed }">
+                <img src="@assets/icon_fanhui2.png" @click="goBack" class="back" alt="" />
+                <div class="title">{{ collectionInfo.name }}</div>
+            </div>
+        </van-sticky>
+        <div class="page-top">
+            <van-image
+                class="page-bg"
+                width="100%"
+                height="220"
+                :src="getImg(changeImgs(collectionInfo.pic))"
+                fit="cover"
+            />
+            <div class="page-box">
+                <div class="minter">
+                    <van-image :src="collectionInfo.minterAvatar" width="50" height="50" :radius="100"></van-image>
+                    <div class="minter-title">{{ collectionInfo.minter }}</div>
+                    <div class="sale-list">
+                        <div class="sales">
+                            <span class="sales-fir">流通</span>
+                            <span>{{ totalElements }}份</span>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+
+        <div class="page-pla"></div>
+
+        <div class="page-content">
+            <div class="tabs">
+                <div class="tab" :class="{ prim: sort === 'id,desc' }" @click="changeSelect('id,desc')">
+                    <span>综合排序</span>
+                </div>
+                <div
+                    class="tab price"
+                    :class="[{ prim: sort === 'price' }, price_direction]"
+                    @click="changeSelect('price')"
+                >
+                    <span>价格排序</span> <van-icon size="8" name="arrow-up" /> <van-icon size="8" name="arrow-down" />
+                </div>
+            </div>
+            <van-list
+                class="list"
+                v-model:loading="loading"
+                :finished="finished"
+                finished-text=""
+                @load="getData"
+                :immediate-check="false"
+            >
+                <div class="product-info" @click="goDetail(item)" v-for="(item, index) in list" :key="index">
+                    <div class="top">
+                        <div class="user">
+                            <van-image width="24" height="24" radius="24" :src="item.ownerAvatar" fit="cover" />
+                            <span>{{ item.owner }}</span>
+                            <span class="status out" v-if="item.inPaying">支付中</span>
+                            <span class="status out" v-else-if="!item.salable">仅展示</span>
+                            <span class="status prim" v-else>寄售</span>
+                        </div>
+                        <div class="price" v-if="item.salable">¥{{ item.price }}</div>
+                    </div>
+                    <div class="bottom">
+                        <div class="name">{{ item.name }}</div>
+                        <div class="num" v-if="getNum(item)">#{{ getNum(item) }}</div>
+                        <div class="num" v-else></div>
+                        <div class="buy" v-if="!item.soldOut">
+                            <span>{{ !item.salable ? '去看看' : '去购买' }}</span>
+
+                            <img src="../../assets/icon-jiantou2.png" alt="" />
+                        </div>
+                    </div>
+                </div>
+            </van-list>
+        </div>
+    </div>
+</template>
+
+<script>
+let fromRoute = null;
+import { useRoute } from 'vue-router';
+import { computed, onMounted, ref } from 'vue';
+import http from '@/plugins/http';
+//获取列表
+import useList from '@/plugins/list';
+export default {
+    setup() {
+        let beforeData = {
+            query: {
+                onShelf: true,
+                del: false,
+                salable: true,
+                prefixName: ''
+            },
+            sort: 'id,desc'
+        };
+        function getBeforeData() {
+            if (collectionInfo.value.prefixName) {
+                beforeData.query.prefixName = collectionInfo.value.prefixName;
+            }
+            if (sort.value) {
+                beforeData.sort = sort.value === 'price' ? sort.value + ',' + price_direction.value : sort.value;
+            }
+        }
+        const { empty, loading, finished, list, totalElements, getData } = useList('/collection/all', beforeData);
+
+        const collectionId = computed(() => {
+            return useRoute().query.id;
+        });
+
+        const collectionInfo = ref({});
+        const sort = ref('id,desc');
+        const directions = ['asc', 'desc'];
+        const price_direction = ref('asc');
+        onMounted(() => {
+            finished.value = true;
+            http.http.get('/collection/get/' + collectionId.value).then(res => {
+                collectionInfo.value = res;
+                getBeforeData();
+                finished.value = false;
+                getData(true);
+            });
+        });
+
+        function changeImgs(list = []) {
+            return list.map(item => {
+                if (item.type === 'video/mp4') {
+                    return item.thumb;
+                } else {
+                    return item.url;
+                }
+            });
+        }
+
+        function getNum(info) {
+            if (info.number) {
+                return info.number;
+            } else if (info.name) {
+                let number = info.name.split('#')[1];
+                return number;
+            } else {
+                return '';
+            }
+        }
+
+        function changeSelect(_sort) {
+            if (_sort === 'price' && sort.value === _sort) {
+                let _index = directions.findIndex(item => {
+                    return item === price_direction.value;
+                });
+                console.log(_index);
+                price_direction.value = directions[(_index + 1) % 2];
+            }
+            sort.value = _sort;
+            getBeforeData();
+            getData(true);
+        }
+
+        const isFixed = ref(false);
+        function onSticky(_isFixed) {
+            isFixed.value = _isFixed;
+        }
+
+        return {
+            collectionInfo,
+            sort,
+            price_direction,
+            changeImgs,
+            getNum,
+            changeSelect,
+            getData,
+            isFixed,
+            empty,
+            loading,
+            finished,
+            list,
+            totalElements,
+            onSticky
+        };
+    },
+    name: 'prefixNameList',
+    inject: ['setKeeps', 'scrollWrapper', 'changeScroll'],
+    data() {
+        return {
+            scrollTop: 0
+        };
+    },
+    beforeRouteEnter(to, from) {
+        fromRoute = from;
+    },
+    methods: {
+        goBack() {
+            if (!fromRoute || !fromRoute.name) {
+                this.$router.replace('/home');
+            } else {
+                this.$router.back();
+            }
+        },
+        goDetail(info) {
+            this.$router.push({
+                path: '/productDetail/' + info.id
+            });
+        }
+    },
+    activated() {
+        this.$nextTick(() => {
+            this.changeScroll(this.scrollTop);
+        });
+    },
+    beforeRouteLeave(to, from, next) {
+        if (to.name === 'productDetail') {
+            this.scrollTop = this.scrollWrapper.scrollTop;
+            this.setKeeps(['prefixNameList']);
+        } else {
+            this.scrollTop = 0;
+            this.setKeeps(['prefixNameList'], false);
+        }
+        next();
+    }
+};
+</script>
+
+<style lang="less" scoped>
+.page-pla {
+    height: calc(var(--safe-top) + 160px);
+}
+.page-top {
+    position: absolute;
+    z-index: 1;
+    top: 0;
+    left: 0;
+    right: 0;
+}
+.page-box {
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    z-index: 20;
+    background: rgba(0, 0, 0, 0.6);
+    padding-top: calc(var(--safe-top) + 50px);
+}
+.navBar {
+    position: relative;
+    padding: calc(var(--safe-top) + 13px) 0 13px;
+
+    .back {
+        position: absolute;
+        width: 24px;
+        height: 24px;
+        left: 16px;
+        top: calc(var(--safe-top) + 13px);
+    }
+    .title {
+        font-size: 16px;
+        font-weight: bold;
+        color: #ffffff;
+        line-height: 24px;
+        text-align: center;
+    }
+
+    &.isFixed {
+        background-color: #000;
+    }
+}
+
+.minter {
+    .flex-col();
+    align-items: center;
+    padding-top: 22px;
+
+    .minter-title {
+        font-size: 14px;
+        font-weight: bold;
+        color: #ffffff;
+        line-height: 24px;
+        margin-top: 6px;
+    }
+
+    .sale-list {
+        margin-top: 8px;
+
+        .sales {
+            overflow: hidden;
+            font-size: @font1;
+            border-radius: 4px;
+
+            span {
+                padding: 0 10px;
+                line-height: 18px;
+                height: 18px;
+                display: inline-block;
+                &.sales-fir {
+                    background: #00fe1e;
+                    color: #000000;
+                }
+                background-color: rgba(255, 255, 255, 0.08);
+                color: #00fe1e;
+            }
+        }
+    }
+}
+
+.page-content {
+    background-color: #fff;
+    border-radius: 16px 16px 0px 0px;
+    position: relative;
+    z-index: 2;
+    padding: 10px 16px 16px;
+}
+.list {
+    padding-top: 10px;
+}
+.product-info {
+    background: #f5f7fa;
+    border-radius: 12px;
+    padding: 12px;
+    display: block;
+
+    .top {
+        .flex();
+        justify-content: space-between;
+
+        .user {
+            .flex();
+            span {
+                font-size: 12px;
+                color: #939599;
+                line-height: 24px;
+                margin-left: 6px;
+            }
+
+            .status {
+                display: inline-block;
+                min-width: 44px;
+                height: 20px;
+                border-radius: 4px;
+                border: 1px solid #c8c9cc;
+                text-align: center;
+                line-height: 20px;
+                margin-left: 10px;
+                // &.prim {
+                //     color: @prim;
+                //     border-color: @prim;
+                // }
+                &.out {
+                    background-color: #c8c9cc;
+                    color: #fff;
+                }
+            }
+        }
+
+        .price {
+            font-size: 16px;
+            font-weight: bold;
+            color: #000000;
+            line-height: 24px;
+        }
+    }
+
+    .bottom {
+        .flex();
+
+        font-size: 14px;
+        font-weight: bold;
+        color: #000000;
+        line-height: 24px;
+        margin-top: 8px;
+
+        .name {
+            max-width: 100px;
+            overflow: hidden;
+            white-space: nowrap;
+            text-overflow: ellipsis;
+        }
+        .num {
+            flex-grow: 1;
+        }
+
+        .buy {
+            font-size: 12px;
+            .flex();
+            img {
+                width: 18px;
+                height: 18px;
+                margin-left: 1px;
+            }
+        }
+    }
+}
+.product-info + .product-info {
+    margin-top: 12px;
+}
+.tabs {
+    .flex();
+    justify-content: center;
+    height: 40px;
+    position: sticky;
+    top: 50px;
+    background: #fff;
+    z-index: 20;
+
+    .tab {
+        font-size: 16px;
+        font-family: PingFangSC-Regular, PingFang SC;
+        font-weight: 400;
+        color: #939599;
+        line-height: 24px;
+        position: relative;
+        padding: 0 20px;
+        &.price {
+            .van-icon {
+                position: absolute;
+                right: 10px;
+                color: #939599;
+
+                &.van-icon-arrow-down {
+                    top: 12px;
+                }
+
+                &.van-icon-arrow-up {
+                    bottom: 12px;
+                }
+            }
+        }
+
+        &::after {
+            content: '';
+            position: absolute;
+            bottom: -2px;
+            width: 0;
+            height: 2px;
+            background: #131313;
+            border-radius: 2px;
+            left: 50%;
+            transform: translateX(-50%);
+        }
+
+        &.prim {
+            font-size: 16px;
+            font-weight: bold;
+            color: #000000;
+
+            &.desc {
+                .van-icon {
+                    &.van-icon-arrow-down {
+                        color: #000;
+                    }
+                }
+            }
+
+            &.asc {
+                .van-icon {
+                    &.van-icon-arrow-up {
+                        color: #000;
+                    }
+                }
+            }
+
+            &::after {
+                width: 34px;
+            }
+        }
+    }
+}
+</style>