国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Vue2+Echarts+SpringBoot+Websocket+Scheduled實現(xiàn)大屏圖表數(shù)據(jù)實時展示

這篇具有很好參考價值的文章主要介紹了Vue2+Echarts+SpringBoot+Websocket+Scheduled實現(xiàn)大屏圖表數(shù)據(jù)實時展示。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

1.簡介

近期在學習websocket的相關(guān)技術(shù),用于做前后端的數(shù)據(jù)實時交互,結(jié)合網(wǎng)上資料和個人理解,整理了一個小白入門案例,不喜勿噴?。。。?!

1.1 webSocket

  • WebSocket是HTML5下一種新的協(xié)議(websocket協(xié)議本質(zhì)上是一個基于tcp的協(xié)議)
  • 它實現(xiàn)了瀏覽器與服務器全雙工通信,能更好的節(jié)省服務器資源和帶寬并達到實時通訊的目的
  • Websocket是一個持久化的協(xié)議vue大屏展示定時任務,Java知識日常分享,echarts,前端,javascript,java,spring boot,websocket

WebSocket有以下特點:

  • 是真正的全雙工方式,建立連接后客戶端與服務器端是完全平等的,可以互相主動請求。而HTTP長連接基于HTTP,是傳統(tǒng)的客戶端對服務器發(fā)起請求的模式。
  • HTTP長連接中,每次數(shù)據(jù)交換除了真正的數(shù)據(jù)部分外,服務器和客戶端還要大量交換HTTP header,信息交換效率很低。Websocket協(xié)議通過第一個request建立了TCP連接之后,之后交換的數(shù)據(jù)都不需要發(fā)送 HTTP header就能交換數(shù)據(jù),這顯然和原有的HTTP協(xié)議有區(qū)別所以它需要對服務器和客戶端都進行升級才能實現(xiàn)(主流瀏覽器都已支持HTML5)

2.效果圖展示

vue大屏展示定時任務,Java知識日常分享,echarts,前端,javascript,java,spring boot,websocket

圖 1 初始狀態(tài)

vue大屏展示定時任務,Java知識日常分享,echarts,前端,javascript,java,spring boot,websocket

圖 2 推送一段時間之后的狀態(tài)?

socket演示

demo中以餅圖和柱形圖為例,結(jié)合websocket,對后端數(shù)據(jù)進行實時推送

3.代碼實現(xiàn)

3.1 前端代碼

該項目為前后端分離項目,前端主要使用Vue2+原生websocket進行頁面搭建和服務端數(shù)據(jù)交互

  • 主要代碼實現(xiàn)部分
<template>
  <div class="hello">
   <!-- 柱形圖渲染 -->
   <div id="chart">

   </div>
   <!-- 餅圖渲染 -->
   <div id="pie">

  </div>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  data(){
    return {
      ws: null,
      url:"ws://127.0.0.1:8080/websocket/",
      barChart: null,
      pieChart: null,
      userId: null
    }
  },
  mounted(){
    //掛載的時候進行初始化
    this.init()
  },
  created(){
  
  },
  methods:{
    initChart(data){
      if(!this.barChart){
        this.barChart= this.$echarts.init(document.getElementById('chart'))
      }
      let chart = this.barChart
      const option = {
        title: {
          text: 'websocket測試',
          textStyle:{
            color: "white"
          }
        },
        tooltip: {
          trigger: 'axis'
        },
        legend: {
          data: ['日銷量'],
          textStyle:{
            color: "white"
          }
        },
        xAxis: {
          name: "日期",
          type: 'category',
          data: data.xAxisData,
          axisLabel:{
            color: "white"
          },
          nameTextStyle:{
            color: "white"
          }
        },
        yAxis: {
          type: 'value',
          name:"銷量",
          axisLabel:{
            color: "white"
          },
          nameTextStyle:{
            color: "white"
          }
        },
        series: [
          {
            name: '日銷量',
            type: 'bar',
            data: data.yAxisData
          }
        ]
      } 
      chart.setOption(option)  
    },
    init(){
      let obj = this.getUrlParams2(window.location.href)
      this.ws = new WebSocket(this.url+obj.userid)
      this.ws.onopen = this.websocketOnOpen;
      this.ws.onmessage = this.websocketOnMessage;
    },
    websocketOnOpen(){
      console.log("連接成功")
    },
    websocketOnMessage(datas){
      console.log(datas)
      if(datas.data !== "連接成功"){
        let res = JSON.parse(datas.data)
        if(res.type == 1){
            this.$message({
              type: "warning",
              message: res.msg
            })
        }else{
          this.initChart(JSON.parse(datas.data).server)
          this.initPie(JSON.parse(datas.data).pie)
        }
        
      }
    },
    getUrlParams2(url){
      let urlStr = url.split('?')[1]
      const urlSearchParams = new URLSearchParams(urlStr)
      const result = Object.fromEntries(urlSearchParams.entries())
      return result
    },
    initPie(pies){
      if(!this.pieChart){
        this.pieChart= this.$echarts.init(document.getElementById('pie'))
      }
      let chart = this.pieChart
      const option = {
          // legend 圖例組件配置項 (圖例,其實就是顏色指示器)
          legend: {
            top: "bottom", // 圖例組件離容器上側(cè)的距離。
          },
          // 工具欄組件
          toolbox: {
            show: true, // 是否顯示工具欄組件
            feature: {
              mark: { show: false },
              dataView: { show: true, readOnly: false }, // 數(shù)據(jù)視圖工具
              restore: { show: true },  // 配置項還原
              saveAsImage: { show: true }, // 保存圖片
            },
          },
          series: [
            {
              name: "Nightingale Chart", // 名稱
              type: "pie",  // 類型 餅圖
              radius: [50, 150], // 餅圖的半徑 `50, 250 => 內(nèi)半徑 外半徑`
              center: ["50%", "50%"], // 餅圖的中心(圓心)坐標,數(shù)組的第一項是橫坐標,第二項是縱坐標。
              roseType: "area", // 是否展示成南丁格爾圖,通過半徑區(qū)分數(shù)據(jù)大小
              // 圖形的顏色
              itemStyle: {
                borderRadius: 8,
              },
              // 圖表的數(shù)據(jù)
              data: pies.pie,
            },
          ],
        };
        // 3.5 將配置參數(shù)和圖表關(guān)聯(lián)
        chart.setOption(option);
    }
  }
}
</script>

<style scoped lang="less">
  .hello{
    display: flex;
    #chart,#pie{
      width: 600px;
      height: 400px;
      background-color: black;
    }
    #pie{
      margin-left: 20px;
    }
  }
</style>

3.2 后端代碼

后端我是用的是Java語言,主要使用SpringBoot生態(tài)進行服務端的搭建

3.2.1?項目目錄結(jié)構(gòu)

vue大屏展示定時任務,Java知識日常分享,echarts,前端,javascript,java,spring boot,websocket?3.2.2 導入相關(guān)依賴

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--   spring-web依賴    -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--   websocket相關(guān)依賴     -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
        <!--   lombok     -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!--EntityUtils工具類包-->
        <dependency>
            <groupId>xin.altitude.cms</groupId>
            <artifactId>ucode-cms-common</artifactId>
            <version>1.5.8</version>
        </dependency>
        <!--    hutool工具類    -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.18</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>
    </dependencies>

?3.3.3 WebSocket配置文件

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@Configuration
public class WebSocketConfig {
    /**
     * 	注入ServerEndpointExporter,
     * 	這個bean會自動注冊使用了@ServerEndpoint注解聲明的Websocket endpoint
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
    
}

3.3.4 WebSocket服務類

@ServerEndpoint("/websocket/{userId}")
@Component
public class WebSocketServer {
    static Log log= LogFactory.get(WebSocketServer.class);
    /**靜態(tài)變量,用來記錄當前在線連接數(shù)。應該把它設計成線程安全的。*/
    private static int onlineCount = 0;
    /**concurrent包的線程安全Set,用來存放每個客戶端對應的MyWebSocket對象。*/
    private static ConcurrentHashMap<String,WebSocketServer> webSocketMap = new ConcurrentHashMap<>();
    /**與某個客戶端的連接會話,需要通過它來給客戶端發(fā)送數(shù)據(jù)*/
    private Session session;
    /**接收userId*/
    private String userId="";
    @Autowired
    private ItemService itemService;

    /**
     * 連接建立成功調(diào)用的方法*/
    @OnOpen
    public void onOpen(Session session, @PathParam("userId") String userId) {
        this.session = session;
        this.userId=userId;
        if(webSocketMap.containsKey(userId)){
            webSocketMap.remove(userId);
            webSocketMap.put(userId,this);
            //加入set中
        }else{
            webSocketMap.put(userId,this);
            //加入set中
            addOnlineCount();
            //在線數(shù)加1
        }

        log.info("用戶連接:"+userId+",當前在線人數(shù)為:" + getOnlineCount());

        try {
            Map<String,Object> map = new HashMap<>();
            map.put("server",itemService.getData());
            map.put("pie", itemService.getPieData());
            JSONObject jsonObject =  new JSONObject(map);
            sendMessage(jsonObject.toString());
        } catch (Exception e) {
            log.error("用戶:"+userId+",網(wǎng)絡異常!!!!!!");
        }
    }

    /**
     * 連接關(guān)閉調(diào)用的方法
     */
    @OnClose
    public void onClose() {
        if(webSocketMap.containsKey(userId)){
            webSocketMap.remove(userId);
            //從set中刪除
            subOnlineCount();
        }
        log.info("用戶退出:"+userId+",當前在線人數(shù)為:" + getOnlineCount());
    }

    /**
     * 收到客戶端消息后調(diào)用的方法
     *
     * @param message 客戶端發(fā)送過來的消息*/
    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("用戶消息:"+userId+",報文:"+message);
        //可以群發(fā)消息
        //消息保存到數(shù)據(jù)庫、redis
        if(StringUtils.isNotBlank(message)){
            try {
                //解析發(fā)送的報文
                JSONObject jsonObject = JSON.parseObject(message);
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }

    /**
     *
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("用戶錯誤:"+this.userId+",原因:"+error.getMessage());
        error.printStackTrace();
    }

    /**
     * 實現(xiàn)服務器主動推送
     */
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }
    /**
     * 消息單發(fā)
     */
    public static void sendOneMessage(String userId,String message) throws IOException {
        WebSocketServer webSocketServer = webSocketMap.get(userId);
        webSocketServer.session.getBasicRemote().sendText(message);
    }


    /**
     * 實現(xiàn)服務器主動推送
     */
    public static void sendAllMessage(String message) throws IOException {
        ConcurrentHashMap.KeySetView<String, WebSocketServer> userIds = webSocketMap.keySet();
        for (String userId : userIds) {
            WebSocketServer webSocketServer = webSocketMap.get(userId);
            webSocketServer.session.getBasicRemote().sendText(message);
            System.out.println("webSocket實現(xiàn)服務器主動推送成功userIds===="+userIds);
        }
    }

    /**
     * 發(fā)送自定義消息
     * */
    public static void sendInfo(String message,@PathParam("userId") String userId) throws IOException {
        log.info("發(fā)送消息到:"+userId+",報文:"+message);
        if(StringUtils.isNotBlank(userId)&&webSocketMap.containsKey(userId)){
            webSocketMap.get(userId).sendMessage(message);
        }else{
            log.error("用戶"+userId+",不在線!");
        }
    }

    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        WebSocketServer.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        WebSocketServer.onlineCount--;
    }
}

以上便完成了websocket服務的搭建,上述中設置了單發(fā)和群發(fā)兩種通信方式

3.3.5 測試實體類

?分別創(chuàng)建柱形圖(Bar)和餅圖(Pie)的實體類

@AllArgsConstructor
@NoArgsConstructor
@Data
public class Bar {
    private String name;
    private int data;
}
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Pie {
    private String name;
    private int value;
}

3.3.6 編寫Service從數(shù)據(jù)庫獲取實時數(shù)據(jù)

此處為服務端從數(shù)據(jù)庫中獲取最新數(shù)據(jù)的過程,demo中,并沒有進行查庫操作,而是寫了一個模擬數(shù)據(jù)進行測試,使用static靜態(tài)變量對數(shù)據(jù)進行靜態(tài)初始化,如需操作數(shù)據(jù)庫可以進行數(shù)據(jù)庫連接即可,持久層可使用mybatis或者JPA對數(shù)據(jù)庫進行操作

?初始化完成后,分別獲取餅圖和柱形圖的相關(guān)數(shù)據(jù),通過Math.random()方法對初始數(shù)據(jù)進行加工,使得每次獲取到的數(shù)據(jù)都是不一樣的,從而模擬數(shù)據(jù)的變化更新

@Service
public class ItemService {
    private static final List<Bar> items = new ArrayList<>();
    private static final List<Pie> pie = new ArrayList<>();
    static {
        items.add(new Bar("周一",12));
        items.add(new Bar("周二",20));
        items.add(new Bar("周三",15));
        items.add(new Bar("周四",8));
        items.add(new Bar("周五",7));
        items.add(new Bar("周六",11));
        items.add(new Bar("周日",13));
        pie.add(new Pie("rose1",38));
        pie.add(new Pie("rose2",46));
        pie.add(new Pie("rose3",12));
        pie.add(new Pie("rose4",30));
        pie.add(new Pie("rose5",54));
        pie.add(new Pie("rose6",36));
    }
    //獲取柱形圖數(shù)據(jù)
    public Map<String,?> getData(){
        //模擬數(shù)據(jù)更新
        items.forEach(e -> e.setData(e.getData()+(int)(Math.random() * 5 + 1)));
        HashMap<String,Object> map = new HashMap<>();
        map.put("xAxisData", EntityUtils.toList(items,Bar::getName));
        map.put("yAxisData", EntityUtils.toList(items,Bar::getData));
        return map;
    }
    //獲取餅圖數(shù)據(jù)
    public Map<String,?> getPieData(){
        //模擬數(shù)據(jù)更新
        pie.forEach(e -> e.setValue(e.getValue()+(int)(Math.random() * 5 + 1)));
        HashMap<String,Object> map = new HashMap<>();
        map.put("pie",pie);
        return map;
    }
}

3.3.7 編寫測試接口

@RestController
@RequestMapping("/news")
@Slf4j
public class TestApi {
    @Autowired
    private ItemService itemService;
    /**
     * 3秒執(zhí)行一次
     * @return
     * @throws Exception
     */
    @Scheduled(fixedRate = 3 * 1000)
    @GetMapping("/send")
    public String send() throws Exception {
        Map<String,Object> map = new HashMap<>();
        map.put("server",itemService.getData());
        map.put("pie",itemService.getPieData());
        JSONObject jsonObject =  new JSONObject(map);
        WebSocketServer.sendAllMessage(jsonObject.toString());
        return jsonObject.toString();
    }
}

以上接口編寫完成之后,使用SpringBoot自帶的定時任務Scheduled類對接口進行設置,我這里是設置3秒執(zhí)行一次,即@Scheduled(fixedRate = 3 * 1000),同時將每次獲取到的最新數(shù)據(jù)通過websocket向客戶端進行消息推送

至此 ,完成整個項目的搭建,本人也不太會,都是慢慢摸索的,有啥講的不對的地方,歡迎大家批評指正?。。?!如果覺得對您有幫助的話,請不要吝嗇您的點贊+關(guān)注+評論喲?。。?!文章來源地址http://www.zghlxwxcb.cn/news/detail-828914.html

到了這里,關(guān)于Vue2+Echarts+SpringBoot+Websocket+Scheduled實現(xiàn)大屏圖表數(shù)據(jù)實時展示的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權(quán),不承擔相關(guān)法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領支付寶紅包贊助服務器費用

相關(guān)文章

  • Springboot+Flask+Neo4j+Vue2+Vuex+Uniapp+Mybatis+Echarts+Swagger綜合項目學習筆記

    Springboot+Flask+Neo4j+Vue2+Vuex+Uniapp+Mybatis+Echarts+Swagger綜合項目學習筆記

    項目訪問入口 Neo4j高性能圖數(shù)據(jù)庫從入門到實戰(zhàn) 教程博客:Neo4j 開啟命令 醫(yī)學知識圖譜問答系統(tǒng) neo4j知識圖譜 Vue+flask 中藥中醫(yī)方劑大數(shù)據(jù)可視化系統(tǒng) ECharts數(shù)據(jù)可視化項目、 D3js: 數(shù)據(jù)可視化入門D3.js 展示地址:數(shù)據(jù)可視化 子絕父相 立即執(zhí)行函數(shù)(function(){})(); ECharts官網(wǎng):

    2024年02月16日
    瀏覽(26)
  • websocket實現(xiàn)聊天室(vue2 + node)

    websocket實現(xiàn)聊天室(vue2 + node)

    需求分析如圖: 搭建的項目結(jié)構(gòu)如圖: 前端步驟: vue create socket_demo (創(chuàng)建項目) views下面建立Home , Login組件 路由里面配置路徑 Home組件內(nèi)部開啟websocket連接 前端相關(guān)組件代碼: Login組件 Home組件 router/index.js 后端步驟: 在項目外層創(chuàng)建server文件夾(src目錄同級) npm init -y創(chuàng)建

    2024年01月22日
    瀏覽(102)
  • springboot+echarts+mysql制作數(shù)據(jù)可視化大屏(滑動大屏)

    springboot+echarts+mysql制作數(shù)據(jù)可視化大屏(滑動大屏)

    ?作者水平低,如有錯誤,懇請指正!謝謝!?。。。?項目簡單,適合大學生參考 分類專欄還有其它的可視化博客哦! 專欄地址:https://blog.csdn.net/qq_55906442/category_11906804.html?spm=1001.2014.3001.5482 目錄 ?一、數(shù)據(jù)源 二、所需工具 三、項目框架搭建 四、代碼編寫 溫度堆疊折線圖

    2024年02月11日
    瀏覽(27)
  • vue項目業(yè)務實現(xiàn),視頻監(jiān)控-文件流,大屏適配方案(v-scale-screen),websocket前端

    vue項目業(yè)務實現(xiàn),視頻監(jiān)控-文件流,大屏適配方案(v-scale-screen),websocket前端

    最近把以前的業(yè)務場景及解決方案整理了一下,具體實現(xiàn)的工具如下: 監(jiān)控-視頻文件流==video.js + videojs-contrib-hls 大屏適配方案== v-scale-screen websocket==sockjs-client+ webstomp-client 下載video插件, 使用方法 (1)導入 (2)模板中寫入標簽 (3)頁面渲染時調(diào)用函數(shù),渲染視頻 option

    2024年02月16日
    瀏覽(25)
  • Springboot+Flask+Neo4j+Vue2+Vuex+Uniapp+Mybatis+Echarts+Swagger+JWT+Nginx+VueRouter綜合項目學習筆記【包括項目部署】

    Springboot+Flask+Neo4j+Vue2+Vuex+Uniapp+Mybatis+Echarts+Swagger+JWT+Nginx+VueRouter綜合項目學習筆記【包括項目部署】

    項目訪問入口 Neo4j高性能圖數(shù)據(jù)庫從入門到實戰(zhàn) 教程博客:Neo4j 開啟命令 醫(yī)學知識圖譜問答系統(tǒng) neo4j知識圖譜 Vue+flask 中藥中醫(yī)方劑大數(shù)據(jù)可視化系統(tǒng) ECharts數(shù)據(jù)可視化項目、 D3js: 數(shù)據(jù)可視化入門D3.js 展示地址:數(shù)據(jù)可視化 子絕父相 立即執(zhí)行函數(shù)(function(){})(); ECharts官網(wǎng):

    2024年02月03日
    瀏覽(18)
  • springboot+echarts +mysql制作數(shù)據(jù)可視化大屏(四圖)

    springboot+echarts +mysql制作數(shù)據(jù)可視化大屏(四圖)

    作者水平低,如有錯誤,懇請指正!謝謝!?。。?! 項目簡單,適合大學生參考 分類專欄還有其它的可視化博客哦! 專欄地址:https://blog.csdn.net/qq_55906442/category_11906804.html?spm=1001.2014.3001.5482 成果展示: 1)可以使用自己的MySQL數(shù)據(jù)庫; 2)使用我提供的數(shù)據(jù)。(要數(shù)據(jù)私信

    2024年02月13日
    瀏覽(25)
  • springboot+mybatis+echarts +mysql制作數(shù)據(jù)可視化大屏

    springboot+mybatis+echarts +mysql制作數(shù)據(jù)可視化大屏

    作者水平低,如有錯誤,懇請指正!謝謝!?。。?! 目錄 一、數(shù)據(jù)源 二、所需工具 三、項目框架搭建 3.1新建springboot項目 3.1.1進入官網(wǎng) 3.1.2創(chuàng)建項目 四、后端代碼編寫 4.1根據(jù)需求修改pom.xml 4.2配置數(shù)據(jù)源 4.3創(chuàng)建目錄結(jié)構(gòu) 4.4后端編寫代碼 4.4.1entity類 4.4.2dao 4.4.3service 4.4.4co

    2024年02月03日
    瀏覽(27)
  • 大屏可視化(VUE2 + DataV)

    大屏可視化(VUE2 + DataV)

    dataV地址:DataV

    2024年02月14日
    瀏覽(27)
  • 基于Echarts+Vue3的低代碼可視化數(shù)據(jù)大屏拖拽設計器 vue拖拽設計大屏

    基于Echarts+Vue3的低代碼可視化數(shù)據(jù)大屏拖拽設計器 vue拖拽設計大屏

    本產(chǎn)品是一款基于Vue3開發(fā)的可視化數(shù)據(jù)大屏拖拽設計器。提供一種簡單易用的拖拽式數(shù)據(jù)可視化大屏設計方案,可幫助用戶快速創(chuàng)建和定制自己的數(shù)據(jù)大屏,通過拖拽組件、調(diào)整布局和設置屬性,實現(xiàn)數(shù)據(jù)展示的自由組合和個性化定制。 可視化編輯:通過拖拽組件、調(diào)整布

    2024年02月03日
    瀏覽(86)
  • Vue2 Echarts 3D餅圖

    2024年01月19日
    瀏覽(15)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包