【vue3.0】14.0 某东到家(十四)——购物车

目前页面如下:


image.png

新建组件src\views\shop\Cart.vue:

<template>
  <div class="cart">
    <div class="check">
      <div class="check__icon">
        <img src="/i18n/9_16/img/basket.png" alt="" class="check__icon__img" />
        <div class="check__icon__tag">1</div>
      </div>
      <div class="check__info">
        总计:<span class="check__info__price">&yen;128</span>
      </div>
      <div class="check__btn">去结算</div>
    </div>
  </div>
</template>

<script>
import { ref } from 'vue'
export default {
  name: 'OrderConfirmation',
  setup() {
    const a = ref(0)
    return { a }
  }
}
</script>
<style lang="scss" scoped>
.cart {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 0.5rem;
}
.check {
}
</style>

image.png

进一步完善页面:

<template>
  <div class="cart">
    <div class="check">
      <div class="check__icon">
        <img src="/i18n/9_16/img/basket.png" alt="" class="check__icon__img" />
        <div class="check__icon__tag">1</div>
      </div>
      <div class="check__info">
        总计:<span class="check__info__price">&yen;128</span>
      </div>
      <div class="check__btn">去结算</div>
    </div>
  </div>
</template>

<script>
import { ref } from 'vue'
export default {
  name: 'OrderConfirmation',
  setup() {
    const a = ref(0)
    return { a }
  }
}
</script>
<style lang="scss" scoped>
.cart {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
}
.check {
  display: flex;
  box-sizing: border-box; //往内塞入border
  line-height: 0.49rem;
  height: 0.49rem;
  border-top: 0.01rem solid #f1f1f1;
  &__icon {
    width: 0.84rem;
    position: relative;
    &__img {
      margin: 0.12rem auto;
      display: block;
      width: 0.28rem;
      height: 0.28rem;
    }
    &__tag {
      // 乘以2然后等比例缩小
      position: absolute;
      right: 0.2rem;
      top: 0.04rem;
      width: 0.2rem;
      height: 0.2rem;
      line-height: 0.2rem;
      text-align: center;
      background-color: #e93b3b;
      border-radius: 50%;
      font-size: 0.12rem;
      color: #fff;
    }
  }
  &__info {
    flex: 1;
    color: #333;
    font-size: 0.12rem;
    &__price {
      line-height: 0.49rem;
      color: #e93b3b;
      font-size: 0.18rem;
    }
  }
  &__btn {
    width: 0.98rem;
    background-color: #4fb0f9;
    text-align: center;
    color: #fff;
    font-size: 0.14rem;
  }
}
</style>
image.png

进一步优化style

......
<style lang="scss" scoped>
@import '@/style/viriables.scss';
.cart {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
}
.check {
  display: flex;
  box-sizing: border-box; //往内塞入border
  line-height: 0.49rem;
  height: 0.49rem;
  border-top: 0.01rem solid $content-bg-color;
  &__icon {
    width: 0.84rem;
    position: relative;
    &__img {
      margin: 0.12rem auto;
      display: block;
      width: 0.28rem;
      height: 0.28rem;
    }
    &__tag {
      // 乘以2然后等比例缩小
      position: absolute;
      right: 0.2rem;
      top: 0.04rem;
      width: 0.2rem;
      height: 0.2rem;
      line-height: 0.2rem;
      text-align: center;
      background-color: $height-light-font-color;
      border-radius: 50%;
      font-size: 0.12rem;
      color: $bg-color;
    }
  }
  &__info {
    flex: 1;
    color: $content-font-color;
    font-size: 0.12rem;
    &__price {
      line-height: 0.49rem;
      color: $height-light-font-color;
      font-size: 0.18rem;
    }
  }
  &__btn {
    width: 0.98rem;
    background-color: #4fb0f9;
    text-align: center;
    color: $bg-color;
    font-size: 0.14rem;
  }
}
</style>

加入购物车的数据存储

首先需要根据商铺的不同存储不同的购物车信息。
这里我们需要运用vuex的store。
这里预设src\store\index.js中将要存储的数据结构如下:

import { createStore } from 'vuex'

export default createStore({
  state: {
    cartList: {
      // 第一层级:商铺的id
      // 第二层内容是商品内容以及购物数量
      // shopId: {
      //   productID: {
      //     _id: '1',
      //     name: '番茄250g/份',
      //     imgUrl: '/i18n/9_16/img/tomato.png',
      //     sales: 10,
      //     price: 33.6,
      //     oldPrice: 39.6,
      //     count: 0
      //   }
      // }
    }
  },
  mutations: {},
  actions: {},
  modules: {}
})

调整优化src\views\shop\Content.vue关于加入购物车的逻辑

<template>
  <div class="content">
......
    <div class="product">
      <div class="product__item" v-for="item in list" :key="item._id">
        <img class="product__item__img" :src="item.imgUrl" />
      ......
        <div class="product__number">
          <span class="product__number__minus">-</span>
          {{ cartList?.[shopId]?.[item._id]?.count || 0 }}
          <span class="product__number__plus">+</span>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import { reactive, ref, toRefs, watchEffect } from 'vue'
import { useRoute } from 'vue-router' // 路由跳转方法
import { useStore } from 'vuex' // 路由跳转方法
import { get } from '@/utils/request.js'

const categories = [
  {
    name: '全部商品',
    tab: 'all'
  },
  {
    name: '秒杀',
    tab: 'seckill'
  },
  {
    name: '新鲜水果',
    tab: 'fruit'
  },
  {
    name: '休闲食品',
    tab: 'snack'
  }
]

// 和tab切换相关的逻辑
const useTabEffect = () => {
  const currentTab = ref(categories[0].tab)
  const handleTabClick = tab => {
    console.log('click:' + tab)
    currentTab.value = tab
  }
  return { currentTab, handleTabClick }
}
// 当前列表内容相关的函数
const useContentListEffect = (currentTab, shopId) => {
  const content = reactive({ list: [] })
  const getContentData = async () => {
    const result = await get(`/api/shop/${shopId}/products`, {
      tab: currentTab.value
    })
    console.log('result:' + result)
    if (result?.code === 200 && result?.data?.length) {
      content.list = result.data
    }
  }
  // watchEffect:当首次页面加载时,或当其中监听的数据发生变化时执行
  watchEffect(() => {
    getContentData()
  })
  const { list } = toRefs(content)
  return { list }
}
// 添加到购物车功能
const useCartEffect = () => {
  const store = useStore()
  const { cartList } = toRefs(store.state)
  return { cartList }
}

export default {
  name: 'Content',
  props: {
    id: String
  },
  setup() {
    const route = useRoute() // 获取路由
    const shopId = route.params.id
    const { currentTab, handleTabClick } = useTabEffect()
    const { list } = useContentListEffect(currentTab, shopId)
    const { cartList } = useCartEffect()
    return { list, categories, handleTabClick, currentTab, cartList, shopId }
  }
}
</script>

<style lang="scss" scoped>
......
</style>

image.png

进一步优化:
src\store\index.js

import { createStore } from 'vuex'

export default createStore({
  state: {
    cartList: {
      // 第一层级:商铺的id
      // 第二层内容是商品内容以及购物数量
      // shopId: {
      //   productID: {
      //     _id: '1',
      //     name: '番茄250g/份',
      //     imgUrl: '/i18n/9_16/img/tomato.png',
      //     sales: 10,
      //     price: 33.6,
      //     oldPrice: 39.6,
      //     count: 0
      //   }
      // }
    }
  },
  mutations: {
    changeItemToCart(state, payload) {
      const { shopId, productId, productInfo, num } = payload
      console.log(shopId, productId, productInfo)
      let shopInfo = state.cartList[shopId]
      if (!shopInfo) {
        shopInfo = {}
      }

      let product = shopInfo[productId]
      if (!product) {
        product = productInfo // 初始化
        product.count = 0
      }

      product.count += num
      if (product.count <= 0) {
        shopInfo[productId].count = 0
      }
      shopInfo[productId] = product
      // 赋值
      state.cartList[shopId] = shopInfo
    }
  },
  actions: {},
  modules: {}
})

src\views\shop\Content.vue代码优化如下,利用vuex实现购物车增减功能:

<template>
  <div class="content">
    <div class="category">
      <div
        :class="{
          category__item: true,
          'category__item--active': currentTab === item.tab
        }"
        v-for="item in categories"
        :key="item.tab"
        @click="handleTabClick(item.tab)"
      >
        {{ item.name }}
      </div>
    </div>
    <div class="product">
      <div class="product__item" v-for="item in list" :key="item._id">
        <img class="product__item__img" :src="item.imgUrl" />
        <div class="product__item__detail">
          <h4 class="product__item__title">{{ item.name }}</h4>
          <p class="product__item__sales">月售{{ item.sales }}件</p>
          <p class="product__item__price">
            <span class="product__item__yen"> &yen;{{ item.price }} </span>
            <span class="product__item__origin">
              &yen;{{ item.oldPrice }}
            </span>
          </p>
        </div>
        <div class="product__number">
          <span
            class="product__number__minus"
            @click="
              () => {
                changeItemToCart(shopId, item._id, item, -1)
              }
            "
            >-</span
          >
          {{ cartList?.[shopId]?.[item._id]?.count || 0 }}
          <span
            class="product__number__plus"
            @click="
              () => {
                changeItemToCart(shopId, item._id, item, 1)
              }
            "
            >+</span
          >
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import { reactive, ref, toRefs, watchEffect } from 'vue'
import { useRoute } from 'vue-router' // 路由跳转方法
import { useStore } from 'vuex' // 路由跳转方法
import { get } from '@/utils/request.js'

const categories = [
  {
    name: '全部商品',
    tab: 'all'
  },
  {
    name: '秒杀',
    tab: 'seckill'
  },
  {
    name: '新鲜水果',
    tab: 'fruit'
  },
  {
    name: '休闲食品',
    tab: 'snack'
  }
]

// 和tab切换相关的逻辑
const useTabEffect = () => {
  const currentTab = ref(categories[0].tab)
  const handleTabClick = tab => {
    console.log('click:' + tab)
    currentTab.value = tab
  }
  return { currentTab, handleTabClick }
}
// 当前列表内容相关的函数
const useContentListEffect = (currentTab, shopId) => {
  const content = reactive({ list: [] })
  const getContentData = async () => {
    const result = await get(`/api/shop/${shopId}/products`, {
      tab: currentTab.value
    })
    console.log('result:' + result)
    if (result?.code === 200 && result?.data?.length) {
      content.list = result.data
    }
  }
  // watchEffect:当首次页面加载时,或当其中监听的数据发生变化时执行
  watchEffect(() => {
    getContentData()
  })
  const { list } = toRefs(content)
  return { list }
}
// 添加、减少到购物车功能
const useCartEffect = () => {
  const store = useStore()
  const { cartList } = toRefs(store.state)
  debugger
  const changeItemToCart = (shopId, productId, productInfo, num) => {
    console.log(
      'changeItemToCart:',
      'shopId:' + shopId,
      'productId:' + productId,
      'productInfo:' + JSON.stringify(productInfo),
      'num:' + num
    )
    store.commit('changeItemToCart', { shopId, productId, productInfo, num })
  }

  return { cartList, changeItemToCart }
}

export default {
  name: 'Content',
  props: {
    id: String
  },
  setup() {
    const route = useRoute() // 获取路由
    const shopId = route.params.id
    const { currentTab, handleTabClick } = useTabEffect()
    const { list } = useContentListEffect(currentTab, shopId)
    const { cartList, changeItemToCart } = useCartEffect()
    return {
      list,
      categories,
      handleTabClick,
      currentTab,
      cartList,
      shopId,
      changeItemToCart
    }
  }
}
</script>

<style lang="scss" scoped>
......
</style>

image.png

至此,src\views\shop\Content.vue完整代码如下:

<template>
  <div class="content">
    <div class="category">
      <div
        :class="{
          category__item: true,
          'category__item--active': currentTab === item.tab
        }"
        v-for="item in categories"
        :key="item.tab"
        @click="handleTabClick(item.tab)"
      >
        {{ item.name }}
      </div>
    </div>
    <div class="product">
      <div class="product__item" v-for="item in list" :key="item._id">
        <img class="product__item__img" :src="item.imgUrl" />
        <div class="product__item__detail">
          <h4 class="product__item__title">{{ item.name }}</h4>
          <p class="product__item__sales">月售{{ item.sales }}件</p>
          <p class="product__item__price">
            <span class="product__item__yen"> &yen;{{ item.price }} </span>
            <span class="product__item__origin">
              &yen;{{ item.oldPrice }}
            </span>
          </p>
        </div>
        <div class="product__number">
          <span
            class="product__number__minus"
            @click="
              () => {
                changeItemToCart(shopId, item._id, item, -1)
              }
            "
            >-</span
          >
          {{ cartList?.[shopId]?.[item._id]?.count || 0 }}
          <span
            class="product__number__plus"
            @click="
              () => {
                changeItemToCart(shopId, item._id, item, 1)
              }
            "
            >+</span
          >
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import { reactive, ref, toRefs, watchEffect } from 'vue'
import { useRoute } from 'vue-router' // 路由跳转方法
import { useStore } from 'vuex' // 路由跳转方法
import { get } from '@/utils/request.js'

const categories = [
  {
    name: '全部商品',
    tab: 'all'
  },
  {
    name: '秒杀',
    tab: 'seckill'
  },
  {
    name: '新鲜水果',
    tab: 'fruit'
  },
  {
    name: '休闲食品',
    tab: 'snack'
  }
]

// 和tab切换相关的逻辑
const useTabEffect = () => {
  const currentTab = ref(categories[0].tab)
  const handleTabClick = tab => {
    console.log('click:' + tab)
    currentTab.value = tab
  }
  return { currentTab, handleTabClick }
}
// 当前列表内容相关的函数
const useContentListEffect = (currentTab, shopId) => {
  const content = reactive({ list: [] })
  const getContentData = async () => {
    const result = await get(`/api/shop/${shopId}/products`, {
      tab: currentTab.value
    })
    console.log('result:' + result)
    if (result?.code === 200 && result?.data?.length) {
      content.list = result.data
    }
  }
  // watchEffect:当首次页面加载时,或当其中监听的数据发生变化时执行
  watchEffect(() => {
    getContentData()
  })
  const { list } = toRefs(content)
  return { list }
}
// 添加、减少到购物车功能
const useCartEffect = () => {
  const store = useStore()
  const { cartList } = toRefs(store.state)
  debugger
  const changeItemToCart = (shopId, productId, productInfo, num) => {
    console.log(
      'changeItemToCart:',
      'shopId:' + shopId,
      'productId:' + productId,
      'productInfo:' + JSON.stringify(productInfo),
      'num:' + num
    )
    store.commit('changeItemToCart', { shopId, productId, productInfo, num })
  }

  return { cartList, changeItemToCart }
}

export default {
  name: 'Content',
  props: {
    id: String
  },
  setup() {
    const route = useRoute() // 获取路由
    const shopId = route.params.id
    const { currentTab, handleTabClick } = useTabEffect()
    const { list } = useContentListEffect(currentTab, shopId)
    const { cartList, changeItemToCart } = useCartEffect()
    return {
      list,
      categories,
      handleTabClick,
      currentTab,
      cartList,
      shopId,
      changeItemToCart
    }
  }
}
</script>

<style lang="scss" scoped>
@import '@/style/viriables.scss';
@import '@/style/mixins.scss';
.content {
  display: flex;
  position: absolute;
  left: 0;
  right: 0;
  top: 1.6rem;
  bottom: 0.5rem;
}
.category {
  overflow-y: scroll;
  width: 0.76rem;
  background: $search-bg-color;
  height: 100%;
  &__item {
    line-height: 0.4rem;
    text-align: center;
    font-size: 14px;
    color: $content-font-color;
    &--active {
      background: $bg-color;
    }
  }
}
.product {
  overflow-y: scroll;
  flex: 1;
  &__item {
    position: relative;
    display: flex;
    padding: 0.12rem 0.16rem;
    margin: 0 0.16rem;
    border-bottom: 0.01rem solid $content-bg-color;
    // 配合解决超出长度以省略号显示而不会出现换行
    &__detail {
      overflow: hidden;
    }
    &__img {
      width: 0.68rem;
      height: 0.68rem;
      margin-right: 0.16rem;
    }
    &__title {
      margin: 0;
      line-height: 0.2rem;
      font-size: 0.14rem;
      color: $content-font-color;
      // 超出长度以省略号显示而不会出现换行
      @include ellipsis;
    }
    &__sales {
      margin: 0.06rem 0;
      line-height: 0.16rem;
      font-size: 0.12rem;
      color: $content-font-color;
    }
    &__price {
      margin: 0;
      line-height: 0.2rem;
      font-size: 0.14rem;
      color: $height-light-font-color;
    }
    &__yen {
      font-size: 0.12rem;
    }
    &__origin {
      margin-left: 0.06rem;
      line-height: 0.2rem;
      font-size: 0.12rem;
      color: $light-font-color;
      text-decoration: line-through; //中划线
    }
    // 购物车选购数量和加减号
    .product__number {
      position: absolute;
      right: 0rem;
      bottom: 0.12rem;
      &__minus,
      &__plus {
        display: inline-block;
        width: 0.2rem;
        height: 0.2rem;
        line-height: 0.16rem;
        border-radius: 50%;
        font-size: 0.2rem;
        text-align: center;
      }
      // 边框白色
      &__minus {
        border: 0.01rem solid $medium-font-color;
        color: $medium-font-color;
        margin-right: 0.05rem;
      }
      //无边框,背景蓝色
      &__plus {
        color: $bg-color;
        background: $btn-bg-color;
        margin-left: 0.05rem;
      }
    }
  }
}
</style>

完善购物车下栏的交互逻辑:
src\views\shop\Cart.vue

<template>
  <div class="cart">
    <div class="check">
      <div class="check__icon">
        <img src="/i18n/9_16/img/basket.png" alt="" class="check__icon__img" />
        <div class="check__icon__tag">{{ total }}</div>
      </div>
      <div class="check__info">
        总计:<span class="check__info__price">&yen; {{ totalPrice }}</span>
      </div>
      <div class="check__btn">去结算</div>
    </div>
  </div>
</template>

<script>
import { computed } from 'vue'
import { useRoute } from 'vue-router' // 路由跳转方法
import { useStore } from 'vuex' // 路由跳转方法

const useCartEffect = () => {
  const store = useStore()
  const route = useRoute()

  // 计算shopId下所有cartList的商品数量total、价钱之和totalPrice
  const shopId = route.params.id // 店铺id
  const cartList = store.state.cartList // 加入购物车的商品列表
  const total = computed(() => {
    const productList = cartList[shopId]
    let count = 0
    if (productList) {
      for (const i in productList) {
        const product = productList[i]
        count += product.count
      }
    }
    return count
  })
  const totalPrice = computed(() => {
    const productList = cartList[shopId]
    let count = 0
    if (productList) {
      for (const i in productList) {
        const product = productList[i]
        count += product.count * product.price
      }
    }
    return count.toFixed(2)
  })
  return { total, totalPrice }
}
export default {
  name: 'Cart',
  setup() {
    const { total, totalPrice } = useCartEffect()
    return { total, totalPrice }
  }
}
</script>
<style lang="scss" scoped>
@import '@/style/viriables.scss';
.cart {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
}
.check {
  display: flex;
  box-sizing: border-box; //往内塞入border
  line-height: 0.49rem;
  height: 0.49rem;
  border-top: 0.01rem solid $content-bg-color;
  &__icon {
    width: 0.84rem;
    position: relative;
    &__img {
      margin: 0.12rem auto;
      display: block;
      width: 0.28rem;
      height: 0.28rem;
    }
    &__tag {
      // 乘以2然后等比例缩小
      position: absolute;
      left: 0.46rem;
      top: 0.04rem;
      padding: 0 0.04rem;
      min-width: 0.2rem;
      height: 0.2rem;
      line-height: 0.2rem;
      text-align: center;
      background-color: $height-light-font-color;
      border-radius: 0.1rem;
      font-size: 0.12rem;
      color: $bg-color;
      transform: scale(0.5);
      transform-origin: left center;
    }
  }
  &__info {
    flex: 1;
    color: $content-font-color;
    font-size: 0.12rem;
    &__price {
      line-height: 0.49rem;
      color: $height-light-font-color;
      font-size: 0.18rem;
    }
  }
  &__btn {
    width: 0.98rem;
    background-color: #4fb0f9;
    text-align: center;
    color: $bg-color;
    font-size: 0.14rem;
  }
}
</style>

最终效果如下:


image.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,881评论 4 368
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,052评论 1 301
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,598评论 0 250
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,407评论 0 217
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,823评论 3 294
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,872评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,037评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,778评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,505评论 1 247
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,745评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,233评论 1 264
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,568评论 3 260
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,231评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,141评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,939评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,954评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,784评论 2 275

推荐阅读更多精彩内容