SpringBoot3+Vue3 前后端分离项目整合ECharts

1、实现效果

2、 后端实现

2.1 月销量

    @Operation(summary = "按产品统计每月销量")
    @PostMapping("/saleStatistics")
    public SaResult saleStatistics(@RequestBody StringDTO stringDTO){
        LocalDate start = LocalDate.parse(stringDTO.getStr(), DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        LocalDate end;

        if(start.getYear() < LocalDate.now().getYear()){
            // 往年的结束日期默认是往年的最后一天
            end = start.with(TemporalAdjusters.lastDayOfYear());
        }else{
            // 今年的结束日期默认是今天日期
            end = LocalDate.now();
        }

        // 获取结束日期月份
        Month endMonth = end.getMonth();

        // 创建一个只包含当前月份和之前月份的集合
        Set<Month> months = IntStream.rangeClosed(1, endMonth.getValue())
                .mapToObj(Month::of)
                .collect(Collectors.toSet());

        List<PurchaseSale> list = purchaseSaleService.list();

        // 产品列表
        List<Product> products = productService.list();

        // 初始化statistics
        // Function.identity() 直接使用元素本身作为键
        Map<String, Map<Month, Integer>> statistics = products.stream()
                .distinct()
                .collect(Collectors.toMap(
                        Product::getId,
                        type -> months.stream().collect(Collectors.toMap(Function.identity(), month -> 0))));

        for (PurchaseSale item : list) {
            LocalDate date = item.getCreateTime().toLocalDate();
            // 不在开始日期之前,不在结束日期之后(包含开始日期和结束日期),类型(1:采购,2:销售)
            if(!date.isBefore(start) && !date.isAfter(end) && item.getType()==2){
                Month month = date.getMonth();
                String id = item.getProductId();
                Integer amount = item.getAmount();

                // merge 方法的第一个参数是键(这里是月份),第二个参数是新值(账单金额),第三个参数是一个合并函数,我们使用 Double::sum 表示将新值累加到总和
                statistics.get(id).merge(month, amount, Integer::sum);
            }
        }

        return SaResult.ok().setData(statistics);
    }

         请求月销量接口,后端返回的数据

{
  "code": 200,
  "msg": "ok",
  "data": {
    "e4c11c628c443ba16ac575c7288bc619": {
      "MARCH": 0,
      "JANUARY": 0,
      "APRIL": 0,
      "JULY": 0,
      "MAY": 0,
      "FEBRUARY": 0,
      "JUNE": 0
    },
    "7c4ac98bd384722c4d028a95ea8f118e": {
      "MARCH": 0,
      "JANUARY": 0,
      "APRIL": 0,
      "JULY": 2,
      "MAY": 0,
      "FEBRUARY": 0,
      "JUNE": 0
    },
    "c22d5558a8b7ce6d7ec6ca309395a2f8": {
      "MARCH": 0,
      "JANUARY": 0,
      "APRIL": 0,
      "JULY": 0,
      "MAY": 0,
      "FEBRUARY": 0,
      "JUNE": 2
    }
  }
}

2.2 月销售额

    @Operation(summary = "统计每月的销售额")
    @PostMapping("/totalStatistics")
    public SaResult totalStatistics(@RequestBody StringDTO stringDTO){
        LocalDate start = LocalDate.parse(stringDTO.getStr(), DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        LocalDate end;

        if(start.getYear() < LocalDate.now().getYear()){
            // 往年的结束日期默认是往年的最后一天
            end = start.with(TemporalAdjusters.lastDayOfYear());
        }else{
            // 今年的结束日期默认是今天日期
            end = LocalDate.now();
        }

        // 获取结束日期月份
        Month endMonth = end.getMonth();

        // 创建一个只包含当前月份和之前月份的集合
        Set<Month> months = IntStream.rangeClosed(1, endMonth.getValue())
                .mapToObj(Month::of)
                .collect(Collectors.toSet());

        List<PurchaseSale> list = purchaseSaleService.list();

        // 初始化statistics
        // Function.identity() 直接使用元素本身作为键
        Map<Month, Double> statistics = months.stream().collect(Collectors.toMap(Function.identity(), month -> 0.0));

        for (PurchaseSale item : list) {
            LocalDate date = item.getCreateTime().toLocalDate();
            // 不在开始日期之前,不在结束日期之后(包含开始日期和结束日期),类型(1:采购,2:销售)
            if(!date.isBefore(start) && !date.isAfter(end) && item.getType()==2){
                Month month = date.getMonth();
                Double total = item.getTotal();

                // merge 方法的第一个参数是键(这里是月份),第二个参数是新值(账单金额),第三个参数是一个合并函数,我们使用 Double::sum 表示将新值累加到总和
                statistics.merge(month, total, Double::sum);
            }
        }

        return SaResult.ok().setData(statistics);
    }

         请求月销售额接口,后端返回的数据

{
  "code": 200,
  "msg": "ok",
  "data": {
    "MARCH": 0,
    "JANUARY": 0,
    "APRIL": 0,
    "JULY": 1200,
    "MAY": 0,
    "FEBRUARY": 0,
    "JUNE": 600
  }
}

2.3 库存

    @Operation(summary = "统计库存")
    @GetMapping("/stockStatistics")
    public SaResult stockStatistics(){
        List<Product> list = productService.list();
        
        // 初始化statistics
        Map<String, Integer> statistics = new HashMap<>();

        for (Product item : list) {
            statistics.put(item.getName(),item.getStock());
        }

        return SaResult.ok().setData(statistics);
    }

        请求库存接口,后端返回的数据

{
  "code": 200,
  "msg": "ok",
  "data": {
    "产品1": 1,
    "产品3": 1,
    "产品2": 0
  }
}

2.4 月销售退货量

    @Operation(summary = "按产品统计每月销售退货量")
    @PostMapping("/statistics")
    public SaResult statistics(@RequestBody StringDTO stringDTO){
        LocalDate start = LocalDate.parse(stringDTO.getStr(), DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        LocalDate end;

        if(start.getYear() < LocalDate.now().getYear()){
            // 往年的结束日期默认是往年的最后一天
            end = start.with(TemporalAdjusters.lastDayOfYear());
        }else{
            // 今年的结束日期默认是今天日期
            end = LocalDate.now();
        }

        // 获取结束日期月份
        Month endMonth = end.getMonth();

        // 创建一个只包含当前月份和之前月份的集合
        Set<Month> months = IntStream.rangeClosed(1, endMonth.getValue())
                .mapToObj(Month::of)
                .collect(Collectors.toSet());

        List<Returns> list = returnsService.list();

        // 产品列表
        List<Product> products = productService.list();

        // 初始化statistics
        // Function.identity() 直接使用元素本身作为键
        Map<String, Map<Month, Integer>> statistics = products.stream()
                .distinct()
                .collect(Collectors.toMap(
                        Product::getId,
                        type -> months.stream().collect(Collectors.toMap(Function.identity(), month -> 0))));

        for (Returns item : list) {
            LocalDate date = item.getCreateTime().toLocalDate();
            // 不在开始日期之前,不在结束日期之后(包含开始日期和结束日期),类型(1:采购退货,2:销售退货)
            if(!date.isBefore(start) && !date.isAfter(end) && item.getType()==2){
                Month month = date.getMonth();
                PurchaseSale purchaseSale = purchaseSaleService.getById(item.getPurchaseSaleId());
                Product product = productService.getById(purchaseSale.getProductId());
                String id=product.getId();
                Integer amount = item.getAmount();

                // merge 方法的第一个参数是键(这里是月份),第二个参数是新值(账单金额),第三个参数是一个合并函数,我们使用 Double::sum 表示将新值累加到总和
                statistics.get(id).merge(month, amount, Integer::sum);
            }
        }

        return SaResult.ok().setData(statistics);
    }

        请求月销售退货量接口,后端返回的数据

{
  "code": 200,
  "msg": "ok",
  "data": {
    "e4c11c628c443ba16ac575c7288bc619": {
      "MARCH": 0,
      "JANUARY": 0,
      "APRIL": 0,
      "JULY": 0,
      "MAY": 0,
      "FEBRUARY": 0,
      "JUNE": 0
    },
    "7c4ac98bd384722c4d028a95ea8f118e": {
      "MARCH": 0,
      "JANUARY": 0,
      "APRIL": 0,
      "JULY": 1,
      "MAY": 0,
      "FEBRUARY": 0,
      "JUNE": 0
    },
    "c22d5558a8b7ce6d7ec6ca309395a2f8": {
      "MARCH": 0,
      "JANUARY": 0,
      "APRIL": 0,
      "JULY": 0,
      "MAY": 0,
      "FEBRUARY": 0,
      "JUNE": 0
    }
  }
}

3、前端实现

<template>
  <el-card class="container">
    <template #header>
      <div class="header">
        <el-breadcrumb :separator-icon="ArrowRight">
          <el-breadcrumb-item :to="{ path: '/home/index' }" class="title">首页</el-breadcrumb-item>
          <el-breadcrumb-item class="title">产品管理</el-breadcrumb-item>
          <el-breadcrumb-item class="title">产品统计</el-breadcrumb-item>
        </el-breadcrumb>
      </div>
    </template>

    <div class="top">
      <div class="date-picker">
        <el-date-picker
          v-model="year"
          type="year"
          format="YYYY"
          value-format="YYYY-MM-DD"
          @change="change"
        />
      </div>

      <div class="statistics">
        <el-form inline>
          <el-form-item label="年销售额(+)">
            <el-input v-model="sale" :type="saleVisible ? 'text' : 'password'" disabled >
              <template #append>
                <el-button :icon="saleVisible ? Hide : View" @click="showSale" />
              </template>
            </el-input>
          </el-form-item>

          <el-form-item label="年成本(-)">
            <el-input v-model="cost" :type="costVisible ? 'text' : 'password'" disabled>
              <template #append>
                <el-button :icon="costVisible ? Hide : View" @click="showCost" />
              </template>
            </el-input>
          </el-form-item>

          <el-form-item label="年销售退货额(-)">
            <el-input v-model="saleReturns" :type="saleReturnsVisible ? 'text' : 'password'" disabled>
              <template #append>
                <el-button :icon="saleReturnsVisible ? Hide : View" @click="showSaleReturns" />
              </template>
            </el-input>
          </el-form-item>

          <el-form-item label="年采购退货额(+)">
            <el-input v-model="purchaseReturns" :type="purchaseReturnsVisible ? 'text' : 'password'" disabled>
              <template #append>
                <el-button :icon="purchaseReturnsVisible ? Hide : View" @click="showPurchaseReturns" />
              </template>
            </el-input>
          </el-form-item>

          <el-form-item label="年利润">
            <el-input v-model="profit" :type="profitVisible ? 'text' : 'password'" disabled>
              <template #append>
                <el-button :icon="profitVisible ? Hide : View" @click="showProfit" />
              </template>
            </el-input>
          </el-form-item>
        </el-form>
      </div>
    </div>

    <el-scrollbar height="670px">
      <div class="mycharts">
        <!-- 1、各产品月销量 -->
        <div id="saleVolume" class="mychart"></div>
        <!-- 2、月销售总额 -->
        <div id="saleRevenue" class="mychart"></div>
      </div>

      <div class="mycharts">
        <!-- 3、库存 -->
        <div id="stock" class="mychart"></div>
        <!-- 4、月销售退货量 -->
        <div id="returns" class="mychart"></div>
      </div>
    </el-scrollbar>

  </el-card>
</template>

<script setup lang="ts">
  import purchaseSaleApi from '@/api/product/purchaseSale';
  import productApi from '@/api/product/product';
  import { onMounted, reactive, ref } from 'vue'
  import { ArrowRight,View,Hide } from '@element-plus/icons-vue'
  import * as echarts from 'echarts';
  import returnsApi from '@/api/product/returns';

  let year=ref('2024-01-01')

  // 1:年成本(采购总额+销售邮费+退货邮费),2:年销售额,3:年利润
  let maps=reactive(new Map()) as any;
  // 1:年采购退货总额,2:年销售退货总额,3:年退货邮费
  let returnsMap=reactive(new Map()) as any;
  
  // 年成本(-):采购总额+销售邮费+退货邮费
  const cost=ref(0.00)
  // 年销售额(+)
  const sale=ref(0.00)
  // 年利润
  const profit=ref(0.0)
  // 年销售退货额(-)
  const saleReturns=ref(0.00)
  // 年采购退货额(+)
  const purchaseReturns=ref(0.00)

  const costVisible=ref(false)
  const saleVisible=ref(false)
  const profitVisible=ref(false)
  const saleReturnsVisible=ref(false)
  const purchaseReturnsVisible=ref(false)

  const showCost= ()=>{
    costVisible.value = !costVisible.value;
  }
  const showSale= ()=>{
    saleVisible.value = !saleVisible.value;
  }
  const showProfit= ()=>{
    profitVisible.value = !profitVisible.value;
  }
  const showSaleReturns= ()=>{
    saleReturnsVisible.value = !saleReturnsVisible.value;
  }
  const showPurchaseReturns= ()=>{
    purchaseReturnsVisible.value = !purchaseReturnsVisible.value;
  }

  const init= ()=>{
    cost.value=maps.get(1);
    sale.value=maps.get(2);
    saleReturns.value=returnsMap.get(2);
    purchaseReturns.value=returnsMap.get(1);
    profit.value=(sale.value+purchaseReturns.value)-(cost.value+saleReturns.value);
  }

  // 1:年成本(采购总额+销售邮费+退货邮费),2:年销售额,3:年利润
  const getData= async()=>{
    const response = await purchaseSaleApi.yearStatistics(year.value);
     Object.entries()函数时,它会将对象的键转换为字符串类型
    for (const [key, value] of Object.entries(response.data)) {
      // 将字符串键转换回数字
      const Key = Number(key);
      maps.set(Key, value);
    }
    // 成本增加退货邮费
    maps.set(1,maps.get(1)+returnsMap.get(3));
    
    init();
  }

  // 1:年采购退货总额,2:年销售退货总额,3:年退货邮费
  const getReturnsData= async()=>{
    const response = await returnsApi.yearStatistics(year.value);
     Object.entries()函数时,它会将对象的键转换为字符串类型
    for (const [key, value] of Object.entries(response.data)) {
      // 将字符串键转换回数字
      const Key = Number(key);
      returnsMap.set(Key, value);
    }
    getData();
  }

  // 1、各产品月销量 折线图
  const drawSaleVolume= async()=>{
    // 配置项
    const option=reactive({
        title: {
          text: '月销量',
          top: 5,
        },
        // 设置图例
        legend:{
          data: [],
          top: 10
        },
        tooltip: {
          trigger:"axis", // 坐标轴触发
        },
        xAxis: {
          type: 'category',
          data: []
        },
        yAxis: {
          // name: '各产品月销量',
          type: 'value'
        },
        // 选中高亮
        emphasis:{
          focus:"series"
        },
        series: []
      });

    // xAxisData 与后端返回的数据适配
    const xAxisData=reactive(['JANUARY', 'FEBRUARY', 'MARCH', 'APRIL', 'MAY', 'JUNE', 'JULY', 'AUGUST', 'SEPTEMBER', 'OCTOBER', 'NOVEMBER', 'DECEMBER']) as any
    // xData 用于前端展示
    const xData=reactive(['1月', '2月', '3月', '4月','5月', '6月', '7月', '8月','9月', '10月', '11月', '12月']) as any
    
    // series数据
    const seriesData = reactive([]) as any;
    // 图例
    let legendData = reactive([]) as any;


    // 后端返回的map数据
    let map=reactive(new Map()) as any;

    // 初始化 Echarts 实例
    const myEchart=echarts.init(document.getElementById("saleVolume"));

    // 折线图 填充x轴数据
    option.xAxis.data=xData;

    // 将后端数据转换为y轴适配的数据(map->list),定义了一个匿名的异步函数,并立即执行
    (async() => {
      const response = await purchaseSaleApi.saleStatistics(year.value);
       Object.entries()函数时,它会将对象的键转换为字符串类型
      for (const [key, value] of Object.entries(response.data)) {
        map.set(key, value);
      }
      return map;
    })().then(async()=>{
      // map.forEach(async(value:any, key:any) => {
      //   seriesData.push({
      //     name: key,
      //     type: 'line',
      //     // 然后,为这个series填充data,data应该是对应月份的数值
      //     data: xAxisData.map((month:any) => value[month] || 0)
      //   });
      //   keys.push(key);
      // })
      // // 填充 折线图 y轴数据
      // option.series=seriesData;
      // // option.legend.data=legendData;

      // 创建一个Promise数组,用于等待所有产品名称的获取
      const promises = Array.from(map.entries()).map(async ([key, value] : any) => {
        const res = await productApi.getNameById(key);
        seriesData.push({
          name: res.data,
          type: 'line',
          smooth: true,
          data: xAxisData.map((month:any) => value[month]),
        });
        // 同时添加到图例数据
        legendData.push(res.data);
      });

      // 等待所有Promise完成
      await Promise.all(promises);

      // 更新图表配置
      option.series = seriesData;
      option.legend.data = legendData;

      // 绘制图表
      myEchart.setOption(option);
    });
  }

  // 2、月销售总额 折线图
  const drawSaleRevenue= async()=>{
    // 配置项
    const option=reactive({
        title: {
          text: '月销售额',
          top: 5,
        },
        // 设置图例
        legend:{
          data: [],
          top: 10
        },
        tooltip: {
          trigger:"axis", // 坐标轴触发
        },
        xAxis: {
          type: 'category',
          data: []
        },
        yAxis: {
          // name: '月销售总额',
          type: 'value'
        },
        // 选中高亮
        emphasis:{
          focus:"series"
        },
        series: []
      });

    // xAxisData 与后端返回的数据适配
    const xAxisData=reactive(['JANUARY', 'FEBRUARY', 'MARCH', 'APRIL', 'MAY', 'JUNE', 'JULY', 'AUGUST', 'SEPTEMBER', 'OCTOBER', 'NOVEMBER', 'DECEMBER']) as any
    // xData 用于前端展示
    const xData=reactive(['1月', '2月', '3月', '4月','5月', '6月', '7月', '8月','9月', '10月', '11月', '12月']) as any
    
    // series数据
    let seriesData = reactive([]) as any;
    // 图例
    let legendData = reactive([]) as any;


    // 后端返回的map数据
    let map=reactive(new Map()) as any;

    // 初始化 Echarts 实例
    const myEchart=echarts.init(document.getElementById("saleRevenue"));

    // 折线图 填充x轴数据
    option.xAxis.data=xData;

    // 将后端数据转换为y轴适配的数据(map->list),定义了一个匿名的异步函数,并立即执行
    (async() => {
      const response = await purchaseSaleApi.totalStatistics(year.value);
       Object.entries()函数时,它会将对象的键转换为字符串类型
      for (const [key, value] of Object.entries(response.data)) {
        map.set(key, value);
      }
      return map;
    })().then(()=>{
      seriesData.push({
        name: '月销售额',
        type: 'line',
        smooth: true,
        data: xAxisData.map((month:any) => map.get(month))
      });
      // 设置图例数据
      legendData.push('月销售额');

      // 更新图表配置
      option.series = seriesData;
      option.legend.data = legendData;

      // 绘制图表
      myEchart.setOption(option);
    });
  }

  // 3、库存 饼图
  const drawStock= async()=>{
    // 配置项
    const option=reactive({
      title: {
        text: '库存',
        top: 5,
      },
      // 设置图例
      legend:{
        data: [],
        top: 40,
        left:"left",
        orient:"vertical", // 竖直排列
      },
      tooltip: {},
      // 选中高亮
      emphasis:{
        focus:"series"
      },
      series: []
    });
    
    // series数据
    let seriesData = reactive([]) as any;
    // 图例
    let legendData = reactive([]) as any;

    // 后端返回的map数据
    let map=reactive(new Map()) as any;

    // 初始化 Echarts 实例
    const myEchart=echarts.init(document.getElementById("stock"));

    // 将后端数据转换为y轴适配的数据(map->list),定义了一个匿名的异步函数,并立即执行
    (async() => {
      const response = await productApi.stockStatistics();
       Object.entries()函数时,它会将对象的键转换为字符串类型
      for (const [key, value] of Object.entries(response.data)) {
        map.set(key, value);
      }
      return map;
    })().then(()=>{
      seriesData.push({
        // name: '库存',
        type: 'pie',
        data: [],
        radius: '70%',
        label:{
          show:true,
          formatter: `{b}:{c}`,
          position: "outside", //outside 外部显示  inside 内部显示
        }
      });

      map.forEach((value:any, key:any) => {
        seriesData[0].data.push({ name: key, value: value });
        // 设置图例
        legendData.push(key);
      });

      // 更新图表配置
      option.series = seriesData;
      option.legend.data = legendData;

      // 绘制图表
      myEchart.setOption(option);
    });
  }

  // 4、各产品月销售退货量 折线图
  const drawReturns= async()=>{
    // 配置项
    const option=reactive({
        title: {
          text: '月销售退货量',
          top: 5,
        },
        // 设置图例
        legend:{
          data: [],
          top: 10
        },
        tooltip: {
          trigger:"axis", // 坐标轴触发
        },
        xAxis: {
          type: 'category',
          data: []
        },
        yAxis: {
          // name: '各产品月销量',
          type: 'value'
        },
        // 选中高亮
        emphasis:{
          focus:"series"
        },
        series: []
      });

    // xAxisData 与后端返回的数据适配
    const xAxisData=reactive(['JANUARY', 'FEBRUARY', 'MARCH', 'APRIL', 'MAY', 'JUNE', 'JULY', 'AUGUST', 'SEPTEMBER', 'OCTOBER', 'NOVEMBER', 'DECEMBER']) as any
    // xData 用于前端展示
    const xData=reactive(['1月', '2月', '3月', '4月','5月', '6月', '7月', '8月','9月', '10月', '11月', '12月']) as any
    
    // series数据
    const seriesData = reactive([]) as any;
    // 图例
    let legendData = reactive([]) as any;


    // 后端返回的map数据
    let map=reactive(new Map()) as any;

    // 初始化 Echarts 实例
    const myEchart=echarts.init(document.getElementById("returns"));

    // 折线图 填充x轴数据
    option.xAxis.data=xData;

    // 将后端数据转换为y轴适配的数据(map->list),定义了一个匿名的异步函数,并立即执行
    (async() => {
      const response = await returnsApi.statistics(year.value);
       Object.entries()函数时,它会将对象的键转换为字符串类型
      for (const [key, value] of Object.entries(response.data)) {
        map.set(key, value);
      }
      return map;
    })().then(async()=>{
      // 创建一个Promise数组,用于等待所有产品名称的获取
      const promises = Array.from(map.entries()).map(async ([key, value] : any) => {
        const res = await productApi.getNameById(key);
        seriesData.push({
          name: res.data,
          type: 'line',
          smooth: true,
          data: xAxisData.map((month:any) => value[month]),
        });
        // 同时添加到图例数据
        legendData.push(res.data);
      });

      // 等待所有Promise完成
      await Promise.all(promises);

      // 更新图表配置
      option.series = seriesData;
      option.legend.data = legendData;

      // 绘制图表
      myEchart.setOption(option);
    });
  }

  const change= ()=>{
    drawSaleVolume();
    drawSaleRevenue();
    drawStock();
    drawReturns();
    getReturnsData();
  }

  onMounted(()=>{
    drawSaleVolume();
    drawSaleRevenue();
    drawStock();
    drawReturns();
    getReturnsData();
  })

</script>

<style scoped lang="less">
  .container{
    height: 100%;
    box-sizing: border-box; 
  }
  .header{
    display: flex;
    align-items: center;
    justify-content: space-between;
  }
  .title{
    font-size: large;
    font-weight: 600;
  }

  .mycharts{
    display: flex;
  }

  /* 要设置宽高,否则无法显示 */
  .mychart{
    width: 100%;
    height: 320px;
    border: 1px solid pink;
    margin: 5px;
  }

  .date-picker{
    margin-left: 5px;
    margin-bottom: 5px;
    margin-right: 20px;
  }

  .statistics .el-form .el-form-item .el-input{
    width: 150px;
  }

  .top{
    display: flex;
    justify-content: space-between;
    height: 40px;
  }

</style>

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值