• 用 Fleck 实现 websocket 通信


    <html lang="en">
    <head>
        <meta charset="utf-8">
        <title>rfid</title>
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta content="width=device-width, initial-scale=1" name="viewport">
        <meta content="" name="description">
        <meta content="" name="author">
        <link href="css/bootstrap.css" rel="stylesheet" type="text/css">
        <link href="css/components-rounded.css" rel="stylesheet" id="style_components" type="text/css">
        <link href="css/layout.css" rel="stylesheet" type="text/css">
        <script type="text/javascript" src="echarts.min.js"></script>
        <script type="text/javascript" src="jquery-3.3.1.min.js"></script>
        <script type="text/javascript">
        </script>
    </head>
    <body class="page-container-bg-solid page-boxed" style="overflow-x:hidden;">
        <div class="page-container">
            <div class="page-content-wrapper">
                <div class="page-content">
                    <div class="container">
                        <div class="page-content-inner">
                            <div class="row" style="background-color:lightblue;">
                                <div class="caption font-blue-dark" style="font-size:16px;">
                                    <span class="caption-subject bold uppercase" style="margin-left:5px;">综合概况</span>
                                </div>
                            </div>
                            <div class="row" style="padding:5px;background-color:lightblue;">
                                <div class="col-md-6 col-sm-6" style="25%;">
                                    <div class="portlet light ">
                                        <div class="portlet-title">
                                            <div class="caption font_blue">
                                                <span class="caption-md"></span>
                                                <span class="caption-desc">按部门(%)</span>
                                            </div>
                                            <div class="actions">
                                            </div>
                                        </div>
                                        <div class="portlet-body">
                                            <div id="dashboard_amchart_4" class="CSSAnimationChart">
                                                <div class="amcharts-main-div">
                                                    <div class="amcharts-chart-div">
                                                        <div id="div1" onclick="sendMessage('10');" style="height:300px;margin-top:-20px;"></div>
                                                    </div>
                                                </div>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                                <div class="col-md-6 col-sm-6" style="25%;">
                                    <div class="portlet light ">
                                        <div class="portlet-title">
                                            <div class="caption font_blue">
                                                <span class="caption-md"></span>
                                                <span class="caption-desc">按类别(%)</span>
                                            </div>
                                            <div class="actions">
                                            </div>
                                        </div>
                                        <div class="portlet-body">
                                            <div id="dashboard_amchart_4" class="CSSAnimationChart">
                                                <div class="amcharts-main-div">
                                                    <div class="amcharts-chart-div">
                                                        <div id="div2" onclick="sendMessage('11');" style="height:300px;margin-top:-20px;"></div>
                                                    </div>
                                                </div>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            </div>
                            <div class="row" style="padding:5px;background-color:lightgray;">
                                <div class="row" style="margin-left:-15px;margin-right:-15px;">
                                    <div class="portlet light" style="background-color:#556422;height:290px;margin-left:15px;margin-right:15px;">
                                        <div class="col-md-6" style="400px;">
                                            <table id="tableData" class="table table-bordered table-hover">
                                                <thead>
                                                    <tr style="background-color:#31753c;">
                                                        <td>楼层</td>
                                                        <td>
                                                            <img src="../监控数.png" />
                                                            <label>监控数</label>
                                                        </td>
                                                        <td>
                                                            <img src="../丢失数.png" />
                                                            <label>缺失数</label>
                                                        </td>
                                                    </tr>
                                                </thead>
                                                <tbody>
                                                    <tr></tr>
                                                    <tr></tr>
                                                    <tr></tr>
                                                    <tr></tr>
                                                    <tr></tr>
                                                    <tr></tr>
                                                    <tr></tr>
                                                    <tr></tr>
                                                    <tr></tr>
                                                    <tr></tr>
                                                </tbody>
                                            </table>
                                        </div>
                                        <div style="float:right;">
                                            <img id="imgbg" style="800px;height:290px;" src="../bgpart0.png" />
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <script type="text/javascript">
            //echart params
            var echartDiv1 = echarts.init(document.getElementById('div1'));
            var echartDiv2 = echarts.init(document.getElementById('div2'));
    
            //websocket
            var ws = new WebSocket('ws://localhost:7181/');
            //连接上服务端后触发
            ws.onopen = function () { console.log('Connection to server opened'); }
            //收到服务端发来的消息后触发
            ws.onmessage = function (evt) {
                var data = JSON.parse(evt.data);
    
                echartDiv1.setOption(data.chartOption1);
                echartDiv2.setOption(data.chartOption2);
    
                //位置实时统计数据
                var realTimeMonitorData = data.realTimeMonitors;
                var len = realTimeMonitorData.length;
                for (var i = 0; i < len; i++) {
                    var flooNo = realTimeMonitorData[i].FloorNo;
                    var floorNo1 = flooNo + 400;
                    var floorNo2 = flooNo + 500;
                    var trStr = "";
                    trStr += "<td onclick="sendMessage('" + floorNo1 + "'); "> " + flooNo + " </td>";
                    trStr += "<td onclick="sendMessage('" + floorNo2 + "1'); ">" + realTimeMonitorData[i].MonitorSum + "</td>";
                    trStr += "<td onclick="sendMessage('" + floorNo2 + "2'); ">" + realTimeMonitorData[i].LossSum + "</td>";
    
                    //注意:tr绑定的事件不能失效
                    $(".table-hover > tbody > tr").eq(i).html(trStr);
                }
    
            }
            //当服务端关闭后触发
            ws.onclose = function () { console.log("Connection closed"); }
    
            //发送消息给服务器端
            function sendMessage(msg) {
                if (ws.readyState == WebSocket.OPEN) {
                    ws.send(msg);
                }
            }
            //断开与服务器端的连接
            function disConnect() {
                ws.close();
            }
            
            //page navigate
            $(".table-hover > tbody > tr").mouseover(function () {
                var idx = $(this).index();
                $("#imgbg").attr("src", "../bgpart" + (10 - idx) + ".png");
            });
            $(".table-hover").mouseleave(function () {
                $("#imgbg").attr("src", "../bgpart0.png");
            });
        </script>
    </body>
    </html>
    using DevExpress.XtraBars.Ribbon;
    using Fleck;
    using IMS.DBHelper;
    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.Linq;
    using System.Windows.Forms;
    using WHC.Framework.Commons;
    using RfidPositioning.Common;
    using IMS.BaseFramework;
    using System.Threading;
    
    namespace IMS.WfmClient
    {
        public partial class WebMonitor : RibbonForm
        {
            static System.Timers.Timer _refreshDataTimer = new System.Timers.Timer();
            static List<IWebSocketConnection> _connections = new List<IWebSocketConnection>();
            DataPackage _dataPackage = new DataPackage();
            WebSocketServer _wsServer = new WebSocketServer("ws://0.0.0.0:7181");
    
            public WebMonitor()
            {
                InitializeComponent();
    
                //启动线程开始从数据库获取页面所需的数据
                Thread th = new Thread(GetDataForChart);
                th.IsBackground = true;
                th.Start();
    
                //设置监控界面对应的网页
                string appPath = AppDomain.CurrentDomain.BaseDirectory;
                string chartsPath = "file:///" + appPath + "Resources/eCharts/sample.html";
                webBrowser1.Navigate(new Uri(chartsPath));
    
                //启动webSocket侦听服务
                _wsServer.Start(item =>
                {
                    //与客户端建立连接后触发
                    item.OnOpen = () =>
                    {
                        _connections.Add(item);
    
                        //首次建立连接后立刻发送一次数据,之后由定时器来刷新数据
                        string jsonData = _dataPackage.ToJson();
                        _connections.ToList().ForEach(s => s.Send(jsonData));
                    };
    
                    //收到客户端发来的消息后触发
                    item.OnMessage = message =>
                    {
                        int msg = 0;
                        int.TryParse(message, out msg);
    
                        ClientMessageHandler(msg);
                    };
    
                    //客户端关闭连接后触发
                    item.OnClose = () =>
                    {
                        _connections.Remove(item);
                    };
                });
            }
    
            private void FrmIndex_Load(object sender, EventArgs e)
            {
                // 启动定时器
                _refreshDataTimer.Enabled = true;
                _refreshDataTimer.Interval = 5 * 60 * 1000;
                _refreshDataTimer.Elapsed += new System.Timers.ElapsedEventHandler(timersTimer_Elapsed);
                _refreshDataTimer.Start();
            }
    
            private void timersTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
            {
                GetDataForChart();
    
                string jsonData = _dataPackage.ToJson();
                _connections.ToList().ForEach(s => s.Send(jsonData));
            }
    
            private void ClientMessageHandler(int msg)
            {
                if (msg >= 500)
                {
                    SysConfig.FloorNumber = (msg % 100).ToString();
                    this.Invoke(new Action(() =>
                    {
                        ChildWinManagement.LoadMdiForm(SysConfig.mainform, typeof(frmTracePlayback));
                    }));
                }
                else if (msg >= 400)
                {
                    SysConfig.FloorNumber = (msg / 10 % 100).ToString();
                    if (msg % 10 == 1)
                    {
                        SysConfig.WarningType = "正常";
                    }
                    if (msg % 10 == 2)
                    {
                        SysConfig.WarningType = "缺失报警";
                    }                
                    this.Invoke(new Action(() =>
                    {
                        ChildWinManagement.LoadMdiForm(SysConfig.mainform, typeof(AssetWatchDetail));
                    }));
                }
                else
                {
                    StaticsTypeEnum type = (StaticsTypeEnum)msg;
    
                    int typeScale = 0;
                    int typeChecks = 0;
                    int typePosition = 0;
                    int typeInstrument = 0;
                    getStaticsType(out typeScale, out typeChecks, out typePosition, out typeInstrument);
    
                    switch (msg)
                    {
                        case 10:
                        case 11:
                            SysConfig.ReportByAssetsType = msg == 10 ? false : true;
                            NavigateToForm(typePosition, typeof(FrmAssetsValueReport));
                            break;
                        case 12:
                        case 13:
                            SysConfig.ReportByAssetsType = msg == 12 ? false : true;
                            NavigateToForm(typePosition, typeof(FrmInventoryReport));
                            break;                    
                        case 30:
                            webBrowser1.Refresh();
                            break;
                        case 31:
                            this.Invoke(new Action(() =>
                            {
                                Form form = new SetStaticsType();
                                form.Owner = this;
                                form.StartPosition = FormStartPosition.CenterScreen;
                                form.ShowDialog();
                            }));
                            break;
                        default:
                            break;
                    }
                }
            }
    
            private void getStaticsType(out int typeScale, out int typeChecks, out int typePosition, out int typeInstrument)
            {
                typeScale = 0;
                typeChecks = 0;
                typePosition = 0;
                typeInstrument = 0;
    
                //查询默认选择的统计方式(从数据库获取已设置过的数据)            
                string strsql = "select * from HT_MonitorStaticsType;";
                DataSet ds = SQLHelper.Query(SQLHelper._connstr, strsql);
                if (ds != null && ds.Tables != null && ds.Tables.Count > 0 && ds.Tables[0].Rows.Count > 0)
                {
                    typeScale = Convert.ToInt32(ds.Tables[0].Rows[0]["Monitor"].ToString());
                    typeChecks = Convert.ToInt32(ds.Tables[0].Rows[0]["Checks"].ToString());
                    typePosition = Convert.ToInt32(ds.Tables[0].Rows[0]["Position"].ToString());
                    typeInstrument = Convert.ToInt32(ds.Tables[0].Rows[0]["Instrument"].ToString());
                }
            }
    
            private void NavigateToForm(int type, Type formType)
            {
                if (type == 1) //按月
                {
                    SysConfig.ReportStartDate = DateTime.Now.ToString("yyyyMM") + "01";
                    SysConfig.ReportEndDate = DateTime.Now.AddMonths(1).ToString("yyyyMM") + "01";
                }
                else
                {
                    SysConfig.ReportStartDate = DateTime.Now.AddYears(-1).ToString("yyyyMMdd");
                    SysConfig.ReportEndDate = DateTime.Now.ToString("yyyyMMdd");
                }
    
                this.Invoke(new Action(() =>
                {
                    ChildWinManagement.LoadMdiForm(SysConfig.mainform, formType);
                }));
            }
    
            #region 图表数据
    
            class RealTimeMonitor
            {
                public string FloorNo { get; set; }
                public string MonitorSum { get; set; }
                public string LossSum { get; set; }
                public string PositionSum { get; set; }
                public string LowSum { get; set; }
            }
    
            class ChartAssetWatch
            {
                public string Name { get; set; }
                public string Value { get; set; }
                public string StaticsType { get; set; }
            }
    
            class ChartCheck
            {
                public string Name { get; set; }
                public string Value { get; set; }
                public string DataType { get; set; }
                public string StaticsType { get; set; }
            }
    
            class ChartPosition
            {
                public string Name { get; set; }
                public string Value { get; set; }
                public string DataType { get; set; }
                public string StaticsType { get; set; }
            }
    
            class ChartInstrument
            {
                public string Name { get; set; }
                public string Value { get; set; }
                public string StaticsType { get; set; }
            }
    
            class DataPackage
            {
                public ChartOptionForAssetWatch chartOption1 { get; set; }
                public ChartOptionForAssetWatch chartOption2 { get; set; }
                public ChartOption chartOption3 { get; set; }
                public ChartOption chartOption4 { get; set; }public List<RealTimeMonitor> realTimeMonitors { get; set; }
            }
    
            void GetDataForChart()
            {
                int typeScale = 0;
                int typeChecks = 0;
                int typePosition = 0;
                int typeInstrument = 0;
                getStaticsType(out typeScale, out typeChecks, out typePosition, out typeInstrument);
    
                _dataPackage.chartOption1 = getChartDataAssetWatch("HT_ChartAssetWatchDept", typeScale);
                _dataPackage.chartOption2 = getChartDataAssetWatch("HT_ChartAssetWatchType", typeScale);
    
                _dataPackage.chartOption3 = getChartDataCheck("HT_ChartCheckDept", typeChecks);
                _dataPackage.chartOption4 = getChartDataCheck("HT_ChartCheckType", typeChecks);
    
                List<RealTimeMonitor> realTimeMonitors = getRealTimeMonitorData();
                _dataPackage.realTimeMonitors = realTimeMonitors;
            }
    
            ChartOptionForAssetWatch getChartDataAssetWatch(string tableName, int staticsType)
            {
                ChartOptionForAssetWatch chartOption = new ChartOptionForAssetWatch();
                try
                {
                    string sqlStr = @"SELECT [Name],[Value],[StaticsType] FROM XXX.[dbo].[" + tableName + "] WHERE StaticsType = " + staticsType + " order by Name desc;";
                    DataSet ds = SQLHelper.Query(SQLHelper._connstr, sqlStr);
    
                    List<ChartAssetWatch> chartData = ds.Tables[0].ConvertToModel<ChartAssetWatch>();
    
                    //截取长度超出5的字符串
                    chartData.ForEach(item =>
                    {
                        if (item.Name.Length > 5)
                        {
                            item.Name = item.Name.Substring(0, 4) + "..";
                        }
                    });
    
                    //合并数量少的分组
                    List<ChartAssetWatch> chartDataNew = new List<ChartAssetWatch>();
                    chartDataNew.AddRange(chartData.OrderByDescending(p => p.Value).Take(5)); //取前5条数据
                    chartDataNew.ForEach(p => { chartData.Remove(p); });
                    chartDataNew.Add(new ChartAssetWatch { Name = "其他", Value = chartData.Sum(p => Convert.ToDecimal(p.Value)).ToString() });
    
                    chartOption.tooltip = new tooltip();
                    chartOption.tooltip.trigger = "item";
                    chartOption.tooltip.formatter = "{a} <br/>{b}: {c} ({d}%)";
                    chartOption.legend = new legend();
                    chartOption.legend.type = "scroll";
                    chartOption.legend.orient = "vertical";
                    chartOption.legend.left = "0";
                    chartOption.legend.top = "0";
                    chartOption.legend.data = chartDataNew.Select(p => p.Name).ToArray();
                    chartOption.series = new SeriesForAssetWatch[1];
                    SeriesForAssetWatch series = new SeriesForAssetWatch();
                    series.name = "";
                    series.type = "pie";
                    series.radius = new string[] { "60%", "30%" };
                    series.center = new string[] { "55%", "55%" };
                    series.avoidLabelOverlap = "false";
                    series.label = new label();
                    series.label.normal = new normal();
                    series.label.normal.show = false;
                    series.label.normal.position = "center";
                    series.label.emphasis = new emphasis();
                    series.label.emphasis.show = "true";
                    series.label.emphasis.textStyle = new textStyle();
                    series.label.emphasis.textStyle.fontSize = "20";
                    series.labelLine = new labelLine();
                    series.labelLine.normal = new normal();
                    series.labelLine.normal.show = false;
                    List<DataNew> d = new List<DataNew>();
                    chartDataNew.ForEach(p => { d.Add(new DataNew { name = p.Name, value = p.Value }); });
                    series.data = d.ToArray();
                    chartOption.series = new SeriesForAssetWatch[1];
                    chartOption.series[0] = series;
    
                    return chartOption;
                }
                catch { return null; }
            }
    
            ChartOption getChartDataCheck(string tableName, int staticsType)
            {
                ChartOption chartOption = new ChartOption();
                try
                {
                    string sqlStr = @"SELECT [Name],[Value],[DataType],[StaticsType] FROM [Lonix_Fas_1].[dbo].[" + tableName + "] WHERE StaticsType = " + staticsType + " order by Name desc;";
                    DataSet ds = SQLHelper.Query(SQLHelper._connstr, sqlStr);
    
                    List<ChartCheck> chartData = ds.Tables[0].ConvertToModel<ChartCheck>();
    
                    //截取长度超出5的字符串
                    chartData.ForEach(item =>
                    {
                        if (item.Name.Length > 5)
                        {
                            item.Name = item.Name.Substring(0, 4) + "..";
                        }
                    });
    
                    //合并数量少的分组
                    List<ChartCheck> chartInstrumentsNew1 = new List<ChartCheck>();
                    chartInstrumentsNew1.AddRange(chartData.Where(p => p.DataType == "1").OrderByDescending(p => p.Value).Take(5)); //取前5条数据
                    chartInstrumentsNew1.ForEach(p => { chartData.Remove(p); });
                    chartInstrumentsNew1.Add(new ChartCheck { Name = "其他", Value = chartData.Where(p => p.DataType == "1").Sum(p => Convert.ToDecimal(p.Value)).ToString() });
    
                    List<ChartCheck> chartInstrumentsNew2 = new List<ChartCheck>();
                    chartInstrumentsNew2.AddRange(chartData.Where(p => p.DataType == "2").OrderByDescending(p => p.Value).Take(5)); //取前5条数据
                    chartInstrumentsNew2.ForEach(p => { chartData.Remove(p); });
                    chartInstrumentsNew2.Add(new ChartCheck { Name = "其他", Value = chartData.Where(p => p.DataType == "2").Sum(p => Convert.ToDecimal(p.Value)).ToString() });
    
                    chartOption.tooltip = new tooltip();
                    chartOption.tooltip.trigger = "axis";
                    chartOption.tooltip.axisPointer = new axisPointer();
                    chartOption.tooltip.axisPointer.type = "shadow";
    
                    chartOption.legend = new legend();
                    chartOption.legend.orient = "horizontal";
                    chartOption.legend.data = new string[] { "盘亏数", "已盘数" };
    
                    chartOption.grid = new grid();
                    chartOption.grid.top = "80";
                    chartOption.grid.left = "3%";
                    chartOption.grid.right = "6%";
                    chartOption.grid.bottom = "30";
                    chartOption.grid.containLabel = true;
    
                    chartOption.xAxis = new Axis();
                    chartOption.xAxis.type = "value";
                    chartOption.xAxis.axisLabel = new axisLabel();
                    chartOption.xAxis.axisLabel.interval = "0";
                    chartOption.xAxis.axisLabel.rotate = "0";
    
                    chartOption.yAxis = new Axis();
                    chartOption.yAxis.type = "category";
                    chartOption.yAxis.data = chartInstrumentsNew1.Select(p => p.Name).ToArray();
                    chartOption.yAxis.axisLabel = new axisLabel();
                    chartOption.yAxis.axisLabel.interval = "0";
                    chartOption.yAxis.axisLabel.rotate = "0";
    
                    chartOption.series = new Series[2];
                    Series s1 = new Series();
                    s1.name = "盘亏数";
                    s1.type = "bar";
                    s1.stack = "总量";
                    s1.label = new label();
                    s1.label.normal = new normal();
                    s1.label.normal.show = true;
                    s1.label.normal.position = "insideRight";
                    s1.data = chartInstrumentsNew1.Select(p => p.Value).ToArray();
                    chartOption.series[0] = s1;
                    Series s2 = new Series();
                    s2.name = "已盘数";
                    s2.type = "bar";
                    s2.stack = "总量";
                    s2.label = new label();
                    s2.label.normal = new normal();
                    s2.label.normal.show = true;
                    s2.label.normal.position = "insideRight";
                    s2.data = chartInstrumentsNew2.Select(p => p.Value).ToArray();
                    chartOption.series[1] = s2;
    
                    return chartOption;
                }
                catch { return null; }
            }
    
            List<RealTimeMonitor> getRealTimeMonitorData()
            {
                string sqlStr = @"select [FloorNo],[MonitorSum],[LossSum],[PositionSum],[LowSum] from XXX.[dbo].[HT_RealTimeMonitor] order by FloorNo desc;";
                DataSet ds = SQLHelper.Query(SQLHelper._connstr, sqlStr);
                return ds.Tables[0].ConvertToModel<RealTimeMonitor>();
            }
    
            #endregion
        }
    }
    namespace IMS.WfmClient
    {
        public class ChartOption
        {
            public tooltip tooltip { get; set; }
            public legend legend { get; set; }
            public grid grid { get; set; }
            public Axis xAxis { get; set; }
            public Axis yAxis { get; set; }
            public Series[] series { get; set; }
        }
    
        public class ChartOptionForAssetWatch
        {
            public tooltip tooltip { get; set; }
            public legend legend { get; set; }
            public grid grid { get; set; }
            public Axis xAxis { get; set; }
            public Axis yAxis { get; set; }
            public SeriesForAssetWatch[] series { get; set; }
        }
    
        public class tooltip
        {
            public string trigger { get; set; }
            public string formatter { get; set; }
            public axisPointer axisPointer { get; set; }
        }
        public class axisPointer
        {
            public string type { get; set; }
        }
        public class legend
        {
            public string type { get; set; }
            public string orient { get; set; }
            public string left { get; set; }
            public string top { get; set; }
            public string[] data { get; set; }
        }
        public class grid
        {
            public string top { get; set; }
            public string left { get; set; }
            public string right { get; set; }
            public string bottom { get; set; }
            public bool containLabel { get; set; }
        }
        public class Axis
        {
            public string type { get; set; }
            public axisLabel axisLabel { get; set; }
            public string[] data { get; set; }
        }
        public class axisLabel
        {
            public string interval { get; set; }
            public string rotate { get; set; }
        }
        public class Series
        {
            public string name { get; set; }
            public string type { get; set; }
            public string center { get; set; }
            public string avoidLabelOverlap { get; set; }
            public string stack { get; set; }
            public label label { get; set; }
            public string[] data { get; set; }
        }
        public class SeriesForAssetWatch
        {
            public string name { get; set; }
            public string type { get; set; }
            public string[] radius { get; set; }
            public string[] center { get; set; }
            public string avoidLabelOverlap { get; set; }
            public string stack { get; set; }
            public label label { get; set; }
            public labelLine labelLine { get; set; }
            public DataNew[] data { get; set; }
        }
        public class DataNew
        {
            public string value { get; set; }
            public string name { get; set; }
        }
        public class label
        {
            public normal normal { get; set; }
            public emphasis emphasis { get; set; }
        }
        public class normal
        {
            public bool show { get; set; }
            public string position { get; set; }
        }
        public class emphasis
        {
            public string show { get; set; }
            public textStyle textStyle { get; set; }
        }
        public class textStyle
        {
            public string fontSize { get; set; }
        }
        public class labelLine
        {
            public normal normal { get; set; }
        }
    }
  • 相关阅读:
    C语言-10-位域与共用体
    python-并发编程
    计算机操作系统
    网络编程-Socket
    网络编程-基础
    python-面向对象进阶
    python-面向对象
    python-模块分类与导入
    python-函数进阶
    python-函数内置方法
  • 原文地址:https://www.cnblogs.com/hellowzl/p/10370581.html
Copyright © 2020-2023  润新知