vuex之webApp购物流程实现

vuex之购物流程实现

以下项目中前端以vue框架、vuex状态管理 、vue-router 管理路由跳转、ul框架vant 为技术栈,后端以nodejs搭建服务器、mongodb数据库存放数据。

购物车页面实现功能

1、store管理的state中的bookInfo数据

 computed:{
    // store管理的state中的bookInfo数据
    bookInfo() {
      return this.$store.state.cart.bookInfo;
    },
 }

2、点击单选和全选按钮,当全选时全部选中,当点击单选后,有一个没选中,则全选不选中,否则选中

 methods:{
    // 全选按钮 判断单选/全选商品的选中状态
    checkboxAll: {
      get() {
        return this.bookInfo.every((item) => item.checked);
      },
      set(val) {
        // console.log(val, 999);
        this.bookInfo.map((item) => {
          item.checked = val;
          return item;
        });
      },
    },
  }

3、计算总价格

 computed:{
   // 总价格
    totalPrice() {
      return this.$store.getters.totalPrice;
    },
 }

4、在购物车中对数据的删除、修改、清空

 methods: {
    // 导航栏
    onClickLeft() {
      // Toast("返回");
      this.$router.push("/home");
    },
    // 删除数据
    removeItem(id) {
      this.$store.commit("remove", id);
    },
    // 修改数量
    changeQty(id, qty) {
      this.$store.commit("changeQty", { _id: id, qty });
    },
    onSubmit() {},
    // 清空商品
    clearCart() {
      // console.log(this.checkboxAll)
      this.$store.commit("clear");
    },
  },

5、点击商品图片时跳转到详情页,并把id传递过去,在页面渲染时这条数据的id能获取得到

 methods:{
  // 跳转详情页
    gotoDetail(id) {
      this.$router.push("/Detail/" + id);
    },
 }

6、跳转路由时,隐藏和显示tabbar(因为下面显示需要不一样)

 created() {
    // 跳转路由时,隐藏下面tabbar
    // this.$store.state.showMenu = false;
    this.$store.commit("displayTabbar", false);
    console.log( this.$store.state)
  },
  destroyed() {
    // 跳转路由时,显示下面tabbar
    // this.$store.state.showMenu = true;
    this.$store.commit("displayTabbar", true);
  },

购物车页面(Cart.vue)

<template>
  <div>
    <!-- nav-bar 导航栏-->
    <van-nav-bar title="购物车" left-text="返回首页" left-arrow @click-left="onClickLeft" />

    <!-- 步骤条 -->
    <van-steps :active="active">
      <van-step>买家下单</van-step>
      <van-step>商家接单</van-step>
      <van-step>买家提货</van-step>
      <van-step>交易完成</van-step>
    </van-steps>

    <div class="cardShow">
      <!-- card  卡片展示 -->
      <van-card
        :title="item.name"
        :thumb="item.imgurl"
        v-for="item in bookInfo"
        :key="item.name"
        @click-thumb="gotoDetail(item._id)"
        class="cardSty"
      >
        <template #tags class="tagSty">
          <van-checkbox v-model="item.checked" class="checkedOne"></van-checkbox>
        </template>
        <template #footer>
          <van-button type="danger" plain size="small" @click.stop="removeItem(item._id)">
            <van-icon size="16px" name="delete" />
          </van-button>
        </template>
        <template #bottom>
          <span class="Nowprice">{{item.price}}</span>
          <van-stepper v-model="item.qty" @change="changeQty(item._id,$event)" />
        </template>
      </van-card>
      <div style="padding:10px">
        <van-button plain type="danger" size="small" @click="clearCart">清空购物车</van-button>
      </div>
    </div>

    <!-- submit-bar 提交 -->
    <van-submit-bar :price="totalPrice" button-text="提交订单" @submit="onSubmit">
      <van-checkbox v-model="checkboxAll">全选</van-checkbox>
      <template #tip>
        你的收货地址不支持同城送,
        <span>修改地址</span>
      </template>
    </van-submit-bar>
  </div>
</template>

<script>
import Vue from "vue";
import { Card, Step, Steps, SubmitBar, Checkbox } from "vant";
Vue.use(Card);
Vue.use(Step);
Vue.use(Steps);
Vue.use(SubmitBar);
Vue.use(Checkbox);

export default {
  data() {
    return {
      active: 0,
      stepValue: 1,
    };
  },

  methods: {
    // 导航栏
    onClickLeft() {
      // Toast("返回");
      this.$router.push("/home");
    },
    // 跳转详情页
    gotoDetail(id) {
      this.$router.push("/Detail/" + id);
    },
    // 删除数据
    removeItem(id) {
      this.$store.commit("remove", id);
    },
    // 修改数量
    changeQty(id, qty) {
      this.$store.commit("changeQty", { _id: id, qty });
    },
    onSubmit() {},
    // 清空商品
    clearCart() {
      // console.log(this.checkboxAll)
      this.$store.commit("clear");
    },
  },

  computed: {
    // store管理的state中的bookInfo数据
    bookInfo() {
      return this.$store.state.cart.bookInfo;
    },
    // 全选按钮 判断单选/全选商品的选中状态
    checkboxAll: {
      get() {
        return this.bookInfo.every((item) => item.checked);
      },
      set(val) {
        // console.log(val, 999);
        this.bookInfo.map((item) => {
          item.checked = val;
          return item;
        });
      },
    },
    // 总价格
    totalPrice() {
      return this.$store.getters.totalPrice;
    },
  },

  created() {
    // 跳转路由时,隐藏下面tabbar
    // this.$store.state.showMenu = false;
    this.$store.commit("displayTabbar", false);
    console.log( this.$store.state)
  },
  destroyed() {
    // 跳转路由时,显示下面tabbar
    // this.$store.state.showMenu = true;
    this.$store.commit("displayTabbar", true);
  },
};
</script>

<style>
.tagSty {
  margin-right: 20px;
}
.checkedOne {
  position: absolute;
  width: 20px;
  padding: 0;
  left: -120px;
  top: 30px;
}
.cardShow {
  margin-bottom: 90px;
}
.cardSty {
  padding-left: 30px;
}
.Nowprice {
  font-size: 18px;
  color: #f10;
}
.Nowprice::before {
  content: "¥";
}
</style>

详情页页面实现功能

1、当首页点击商品后,路由跳转 到detail页面时,把id传过来到详情页,取得id后,发送请求,得到数据后,渲染数据到页面

 // 当路由跳转到detail页面时,接收传递过来的参数 就是id,并发送请求渲染数据到页面
  created() {
    // console.log(this.$route);
    const pid = this.$route.params.id;
    // console.log(pid);
    // this.id = pid;
    this.getData(pid);
    this.recData();
  },
  
  methods:{
   // 获取当前id的数据
    async getData(id) {
      const {
        data: {
          data: { result: bookInfo },
        },
      } = await this.$request.get("/books/" + id);
      // console.log(bookInfo);
      this.bookInfo = bookInfo[0];
      // console.log(this.bookInfo)
    },
    // 获取推荐数据
    async recData() {
      const {
        data: { data: recList },
      } = await this.$request.get("/books", {
        params: {
          size: 6,
        },
      });
      this.recommond = recList.result;
    },
   }

2、路由跳转到detail页面时,推荐数据的获取,通过发送请求后,渲染数据到页面

 // 当路由跳转到detail页面时,接收传递过来的参数 就是id,并发送请求渲染数据到页面
  created() {
    // console.log(this.$route);
    const pid = this.$route.params.id;
    // console.log(pid);
    // this.id = pid;
    this.getData(pid);
    this.recData();
  },
  
  methods:{
   // 获取当前id的数据
    async getData(id) {
      const {
        data: {
          data: { result: bookInfo },
        },
      } = await this.$request.get("/books/" + id);
      // console.log(bookInfo);
      this.bookInfo = bookInfo[0];
      // console.log(this.bookInfo)
    },
    // 获取推荐数据
    async recData() {
      const {
        data: { data: recList },
      } = await this.$request.get("/books", {
        params: {
          size: 6,
        },
      });
      this.recommond = recList.result;
    },
   }

3、点击推荐商品后,把数据重新在detail页面上渲染(路由守卫)

 // 路由内守卫
  beforeRouteUpdate(to, from, next) {
    // console.log(to, from);
    if (to.params.id !== from.params.id) {
      this.getData(to.params.id);
      this.recData();
    }
    next();
  },
  
  methods:{
     // 跳转详情页
    gotoDetail(id) {
      this.$router.push({
        name: "Detail",
        params: {
          id,
          // name:123,
          // age:25
        },
      });
  }

4、添加商品到购物车,点击加入图书,先判断当前商品是否已经存在购物车中,如果存在,就让数量加1,否则不存在,添加到购物车中

 // 添加书籍
    addBook() {
      // 添加当前商品到购物车;
      // 判断当前商品是否已经存在购物车中
      // 存在:数量+1
      // 不存在:添加到购物车
      const { _id } = this.bookInfo;
      const current = this.cartlist.filter((item) => item._id === _id)[0];
      if (current) {
        this.$store.commit("changeQty", { _id, qty: current.qty + 1 });
      } else {
        const goods = {
          ...this.bookInfo,
          qty: 1,
        };
        // 调用mutation方法
        this.$store.commit("add", goods);
      }
    },
    
  computed: {
    // 购物车的数据
    cartlist() {
      return this.$store.state.cart.bookInfo;
    },
  },

5、立即购买,添加商品并跳转

 methods:{
    // 立即购买按钮 添加商品并跳转
    buyNow() {
      // 添加当前商品到购物车,并跳转到购物车页面
      this.addBook();
      this.$router.push("/cart");
    },
 }

6、点击购物车按钮跳转到购物车页面

 methods:{
 // 跳转到购物车
    gotoCart(id) {
      // console.log(this.$route)
      this.$router.push({
        name: "Cart",
        params: {
          id,
        },
      });
    },
 }

7、跳转路由时,隐藏和显示tabbar(因为下面的显示不一样)

 mounted() {
    // 跳转路由时,隐藏下面tabbar
    // this.$store.state.showMenu = false;
    this.$store.commit('displayTabbar',false)
  },
  destroyed() {
    // 跳转路由时,显示下面tabbar
    // this.$store.state.showMenu = true;
    this.$store.commit('displayTabbar',true)
  },

详情页页面(Detail.vue)

 <template>
  <div>
    <!-- nav-bar 导航栏-->
    <van-nav-bar title="书籍信息" left-text="返回首页" left-arrow @click-left="onClickLeft" />

    <!-- 商品详情信息 -->
    <div class="bookInfo">
      <van-image :src="bookInfo.imgurl" class="bookImg" @click="ImgPrev"></van-image>
      <h1 class="bookName">书名:{{bookInfo.name}}</h1>
      <h1 class="bookAuth">作者:{{bookInfo.auth}}</h1>
      <p class="bookIntro">简介:{{bookInfo.intro}}</p>
    </div>

    <!-- Grid 宫格 -->
    <van-grid :border="false" :column-num="2" class="gridBox">
      <van-grid-item
        v-for="item in recommond"
        :key="item.name"
        class="gridItem"
        @click="gotoDetail(item._id)"
      >
        <van-image :src="item.imgurl" />
        <p>{{item.name}}</p>
      </van-grid-item>
    </van-grid>

    <!-- tabbar 购物车 -->
    <van-goods-action>
      <van-goods-action-icon icon="chat-o" text="客服" color="#07c160" />
      <van-goods-action-icon icon="cart-o" text="书籍库" :badge="cartlist.length" @click="gotoCart" />
      <van-goods-action-icon icon="star" text="已收藏" color="#ff5000" />
      <van-goods-action-button type="warning" text="加入图书" @click="addBook" />
      <van-goods-action-button type="danger" text="立即购买" @click="buyNow" />
    </van-goods-action>
  </div>
</template>

<script>
import Vue from "vue";
import {
  Card,
  GoodsAction,
  GoodsActionButton,
  GoodsActionIcon,
  Step,
  Steps,
  Grid,
  GridItem,
  ImagePreview,
} from "vant";

Vue.use(Card);

Vue.use(GoodsAction);
Vue.use(GoodsActionButton);
Vue.use(GoodsActionIcon);
Vue.use(Step);
Vue.use(Steps);
Vue.use(Grid);
Vue.use(GridItem);
Vue.use(ImagePreview);

export default {
  data() {
    return {
      active: 0,
      // id: "",
      bookInfo: {},
      recommond: {},
    };
  },
  methods: {
    // 获取当前id的数据
    async getData(id) {
      const {
        data: {
          data: { result: bookInfo },
        },
      } = await this.$request.get("/books/" + id);
      // console.log(bookInfo);
      this.bookInfo = bookInfo[0];
      // console.log(this.bookInfo)
    },
    // 获取推荐数据
    async recData() {
      const {
        data: { data: recList },
      } = await this.$request.get("/books", {
        params: {
          size: 6,
        },
      });
      this.recommond = recList.result;
    },
    // 跳转到购物车
    gotoCart(id) {
      // console.log(this.$route)
      this.$router.push({
        name: "Cart",
        params: {
          id,
        },
      });
    },
    // 添加书籍
    addBook() {
      // 添加当前商品到购物车;
      // 判断当前商品是否已经存在购物车中
      // 存在:数量+1
      // 不存在:添加到购物车
      const { _id } = this.bookInfo;
      const current = this.cartlist.filter((item) => item._id === _id)[0];
      if (current) {
        this.$store.commit("changeQty", { _id, qty: current.qty + 1 });
      } else {
        const goods = {
          ...this.bookInfo,
          qty: 1,
        };
        // 调用mutation方法
        this.$store.commit("add", goods);
      }
    },
    // 跳转详情页
    gotoDetail(id) {
      this.$router.push({
        name: "Detail",
        params: {
          id,
          // name:123,
          // age:25
        },
      });
    },
    // 图片显示大图
    ImgPrev() {
      console.log(1);
      // ImagePreview([ this.bookInfo.imgurl,]);
      ImagePreview({
        images: [this.bookInfo.imgurl],
        closeable: true,
      });
    },
    // 导航栏 返回首页
    onClickLeft() {
      // Toast("返回首页");
      this.$router.push("/home");
    },
    // 立即购买按钮 添加商品并跳转
    buyNow() {
      // 添加当前商品到购物车,并跳转到购物车页面
      this.addBook();
      this.$router.push("/cart");
    },
  },
  computed: {
    // 购物车的数据
    cartlist() {
      return this.$store.state.cart.bookInfo;
    },
  },
  // 当路由跳转到detail页面时,接收传递过来的参数 就是id,并发送请求渲染数据到页面
  created() {
    // console.log(this.$route);
    const pid = this.$route.params.id;
    // console.log(pid);
    // this.id = pid;
    this.getData(pid);
    this.recData();
  },
  mounted() {
    // 跳转路由时,隐藏下面tabbar
    // this.$store.state.showMenu = false;
    this.$store.commit('displayTabbar',false)
  },
  destroyed() {
    // 跳转路由时,显示下面tabbar
    // this.$store.state.showMenu = true;
    this.$store.commit('displayTabbar',true)
  },
  // 路由内守卫
  beforeRouteUpdate(to, from, next) {
    // console.log(to, from);
    if (to.params.id !== from.params.id) {
      this.getData(to.params.id);
      this.recData();
    }
    next();
  },
};
</script>

<style>
.bookInfo {
  height: 400px;
}
.bookImg {
  width: 150px;
}
.bookName {
  font-size: 18px;
}
.bookAuth {
  font-size: 14px;
}
.bookIntro {
  font-size: 14px;
  height: 60px;
  text-overflow: -o-ellipsis-lastline;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  -webkit-box-orient: vertical;
}
</style>

Tabbar页面(Tabbar.vue)

<template>
  <van-tabbar route v-show="showTabbar">
    <van-tabbar-item
      :icon="item.icon"
      :to="item.path"
      v-for="item in menu"
      :key="item.name"
      :badge="item.name==='cart'?cartLength:''" 
    >{{item.text}}</van-tabbar-item>
  </van-tabbar>
</template>

<script>
import Vue from "vue";
import { Tabbar, TabbarItem } from "vant";
Vue.use(Tabbar);
Vue.use(TabbarItem);

export default {
  data() {
    return {
      menu: [
        {
          path: "/home",
          icon: "wap-home-o",
          text: "首页",
          name:'home'
        },
        {
          path: "/cart",
          icon: "cart-circle-o",
          text: "购物车",
          name:'cart'
        },
        {
          path: "/discover",
          icon: "browsing-history-o",
          text: "发现",
          name:'descover'
        },
        {
          path: "/profile",
          icon: "user-circle-o",
          text: "我的",
          name:'profile'
        },
      ],
    };
  },
  computed:{
    // 购物车里面的数据有多少个,数量
    cartLength(){
      return this.$store.state.cart.bookInfo.length
    },
    // 是否显示和隐藏 tabbar
    showTabbar(){
      return this.$store.state.common.showTabbar
    }
  },
  created(){
    // console.log(this.$store)
  }
};
</script>

<style>

</style>

App页面(App.vue)

<template>
  <div id="app">
    <router-view />
    <tab-bar></tab-bar>
  </div>
</template>

<script>
import Vue from "vue";
import {
  Button,
  Image,
  ImagePreview,
  NavBar,
  Tag,
  Radio,
  Icon,
  Stepper,
} from "vant";

Vue.use(Button);
Vue.use(Image);
Vue.use(ImagePreview);
Vue.use(NavBar);
Vue.use(Tag);
Vue.use(Radio);
Vue.use(Icon);
Vue.use(Stepper);

import TabBar from "./components/tabber/TabBar";

export default {
  components: {
    TabBar,
  },
};
</script>
<style lang="scss">
</style>

Home 页面(Home.vue)

<template>
  <div>
    <van-nav-bar title="首页" />
    <!-- swipe 轮播图 -->
    <van-swipe class="my-swipe" :autoplay="3000" indicator-color="white" @change="onChange">
      <van-swipe-item v-for="(item,index) in books" :key="index">
        <img :src="item.imgurl" alt />
      </van-swipe-item>
      <template #indicator>
        <div class="custom-indicator">{{ current + 1 }}/{{page}}</div>
      </template>
    </van-swipe>
    <!-- Grid 宫格 -->
    <van-grid :border="false" :column-num="2" class="gridBox">
      <van-grid-item
        v-for="item in showList"
        :key="item.name"
        class="gridItem"
        @click="gotoDetail(item._id)"
      >
        <van-image :src="item.imgurl" />
        <p>{{item.name}}</p>
      </van-grid-item>
    </van-grid>
  </div>
</template>

<script>
import Vue from "vue";
import { Swipe, SwipeItem, Image } from "vant";
import { Grid, GridItem } from "vant";

Vue.use(Swipe);
Vue.use(SwipeItem);
Vue.use(Image);
Vue.use(Grid);
Vue.use(GridItem);

export default {
  name: "Home",
  components: {},
  data() {
    return {
      current: 0,
      page: 0,
      books: [],
      showList: [],
    };
  },
  methods: {
    // 轮播图的当前页码
    onChange(index) {
      this.current = index;
    },
    // 获取轮播图数据
    async getData() {
      const {
        data: { data: books },
      } = await this.$request.get("/books", {
        params: {
          size: 6,
        },
      });
      // console.log(books)
      this.books = books.result;
      this.page = this.books.length;
      // console.log(this.books)
    },
    // 获取展示数据
    async showData() {
      const {
        data: { data: showList },
      } = await this.$request.get("/books", {
        params: {
          size: 10,
        },
      });
      // console.log(books)
      this.showList = showList.result;
      // console.log(this.showList);
    },
    // 跳转详情页
    gotoDetail(id) {
      // this.$router.push(`/detail"${id}`)
      this.$router.push({
        name: "Detail",
        params: {
          id,
          // name:123,
          // age:25
        },
      });
    },
  },
  created() {
    this.getData();
    this.showData();
  },
};
</script>

<style lang="scss">
.my-swipe .van-swipe-item {
  color: #fff;
  font-size: 20px;
  height: 100px;
  line-height: 100px;
  text-align: center;
  background-color: #39a9ed;
}
.my-swipe .van-swipe-item img {
  width: 120px;
  height: 100px;
}

.custom-indicator {
  position: absolute;
  right: 5px;
  bottom: 5px;
  padding: 2px 5px;
  font-size: 12px;
  background: rgba(0, 0, 0, 0.1);
}
</style>

router下的index.js

import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'

Vue.use(VueRouter)

const routes = [{
    path: '/',
    // redirect:'/home',
    redirect: {
      name: 'Home'
    }
  },
  {
    path: '/home',
    name: 'Home',
    component: Home
  },
  {
    path: '/discover',
    name: 'Discover',
    component: () => import('../views/Discover.vue')
  },
  {
    path: '/cart',
    name: 'Cart',
    component: () => import('../views/Cart.vue')
  },
  {
    path: '/detail/:id',
    name: 'Detail',
    component: () => import('../views/Detail.vue')
  },
  {
    path: '/profile',
    name: 'Profile',
    component: () => import('../views/Profile.vue')
  },
  {
    path: '/login',
    name: 'Login',
    component: () => import('../views/Login.vue')
  },
  {
    path: '/reg',
    name: 'Reg',
    component: () => import('../views/Reg.vue')
  }
]

const router = new VueRouter({
  routes
})

const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(location) {
  return originalPush.call(this, location).catch(err => err)
}

export default router

store文件下index.js

import Vue from 'vue'
import Vuex from 'vuex'
import cart from './cart'
import common from './common'

Vue.use(Vuex)

export default new Vuex.Store({
  // store 模块化
  modules:{
    cart,
    common
  }
})

store文件下cart.js

import request from '../utils/request'
import {Notify} from 'vant'

const cart = {
  state: {
    bookInfo: [{
        _id: "5f4f6b9dd08ac99a108eb027",
        name: "鼎定乾坤",
        date: "2020 08-19 11:28",
        intro: "玄黄缔造者……极致超脱之路,哪怕天难葬其身,地难灭其魂。亦难以跳出那个圈。星空崩塌,万族凋零。如何以一己之力逆转乾坤,颠倒阴阳。且看那一袭青衫,一柄长剑,一方圆鼎:脚踏修真,拳碎仙域,剑斩神界,鼎定至尊,极致超脱。书群,1141419286扣扣",
        auth: "浅山深水",
        imgurl: "imgbook1/f3aa24ea917f79a95b81ea86c91b4043.jpeg",
        price: 123.2,
        qty: 1,
        checked: false,
      },
      {
        _id: "5f4f6b9dd08ac99a108eb02a",
        name: "雷神传之雷神再世",
        date: "2020 08-18 22:47",
        intro: "以武侠小说的名义 ,说一段刻骨铭心的爱情故事 !",
        auth: "猛士七",
        imgurl: "imgbook1/ba83c1528c275b6c154ffd482d4541c3.jpeg",
        price: 12,
        qty: 1,
        checked: false,
      },
      {
        _id: "5f4f6b9dd08ac99a108eb030",
        name: "窃时之旅",
        date: "2020 08-18 11:30",
        intro: "意外获得时间之灵,由此开启了一段万界时间大盗的传奇。【北爱完,生逢完,越狱完,神盾进行中...】",
        auth: "周子曰不曰",
        imgurl: "imgbook1/e21e64a6f9b65cd51dc69823d80daa7c.jpeg",
        price: 144,
        qty: 1,
        checked: false,
      },
    ],
  },
  getters: {
    // 商品总价
    totalPrice(state) {
      return state.bookInfo.reduce((prev, item) => {
        return prev + item.price * item.qty
      }, 0) * 100
    }
  },
  mutations: {
    // 添加数据
    add(state, goods) {
      state.bookInfo.unshift(goods)
    },

    // 修改数据数量 传递参数 商品_id  数量qty
    changeQty(state, {
      _id,
      qty
    }) {
      state.bookInfo = state.bookInfo.map(item => {
        if (item._id === _id) {
          item.qty = qty
        }
        return item
      })
    },

    // 删除数据
    remove(state, _id) {
      state.bookInfo = state.bookInfo.filter(item => item._id !== _id)
    },
    // 清空购物车
    clear(state) {
      state.bookInfo = []
    }
  },
  actions: {},
  modules: {}
}

export default cart

store文件下的common.js

const common = {
  state: {
    showTabbar: true
  },
  getters: {

  },
  mutations: {
    displayTabbar(state, payload) {
      state.showTabbar = payload
    }
  },
  actions: {

  }
}

export default common

utils文件下的request.js

import axios from 'axios'

const request = axios.create({
  baseURL: 'http://localhost:3000/api',
  withCredentials: true
})

export default request

效果图

首页页面
在这里插入图片描述
购物车页面
在这里插入图片描述
详情页页面
在这里插入图片描述

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值