zoukankan      html  css  js  c++  java
  • QtControlCenter-master(智能快递柜上位机-传感器串口通信-场景页面交互-页面美化)

     

     

    【】串口操作函数 使用线程 CSerialPort.cpp

    CSerialPort.cpp

     

    #pragma once

    #pragma execution_character_set("utf-8")

    #include <QObject>

    #include <QSerialPort>

    #include <QSerialPortInfo>

    #include "define.h"

    #include <QThread>

    #include <QWaitCondition>

    #include "include/HSingleton/HSingleton.h"

     

    class CSerialPort_232 : public QObject, public QThread

    {

        Q_OBJECT

    public:

        CSerialPort_232(QObject *parent = nullptr);

     

        ~CSerialPort_232();

    public:

        void InitSerial();            //��ʼ����������

        void close();

    public:

        void AddTask(QString strSend);

     

        virtual void run();

     

        void StopThread();

     

        void setReadState(bool bRead);

     

    signals:

        void sig_ReadData(QString strData);

     

    public slots:

        void readData(); //��ȡ��������

     

    private slots:

        void sendData(QString data); //���ʹ������ݴ�����

     

    private:

     

        QextSerialPort * com; //����ͨ�Ŷ���

        QTimer *timer; //����������ʱ��

        bool comOk; //�����Ƿ�����

        QWaitCondition Thread_Wait;

    private:

        QMutex m_QueyeMutex;

        QQueue<QString>     m_szSendQueue;                //ϴ�����

        volatile bool m_bAlive;

    public:

        bool m_bRead;

    };

     

     

    #define gbl232SerialPort HSingletonTemplatePtr<CSerialPort_232>::get()

     

     

     

     

    class CSerialPort_485 : public QObject, public QThread

    {

        Q_OBJECT

     

    public:

        CSerialPort_485(QObject *parent =nullptr);

     

        ~CSerialPort_485();

     

        void InitSerial();            //��ʼ����������

     

     

        void close();

     

    public:

        void AddTask(QString strSend);

     

        virtual void run();

     

        void StopThread();

    signals:

        void sig_ReadData(QString strData);

     

    public slots:

        void readData(); //��ȡ��������

     

    private slots:

        void sendData(QString data); //���ʹ������ݴ�����

    private:

     

        QextSerialPort *com; //����ͨ�Ŷ���

        QTimer *timer; //����������ʱ��

        bool comOk; //�����Ƿ�����

        QWaitCondition Thread_Wait;

    private:

        QMutex m_QueyeMutex;

        QQueue<QString>     m_szSendQueue;                //ϴ�����

        volatile bool m_bAlive;

     

    };

     

     

    #define gbl485SerialPort HSingletonTemplatePtr<CSerialPort_485>::get()

     

     

     

     

    class CHeightPort_485 : public QObject, public QThread

    {

        Q_OBJECT

     

    public:

        CHeightPort_485(QObject *parent = nullptr);

     

        ~CHeightPort_485();

     

        void InitSerial();            //��ʼ����������

     

     

        void close();

     

    public:

        void AddTask(QString strSend);

     

        virtual void run();

     

        void StopThread();

    signals:

        void sig_ReadData(QString strData);

     

    public slots:

        void readData(); //��ȡ��������

     

    private slots:

        void sendData(QString data); //���ʹ������ݴ�����

    private:

     

        QextSerialPort * com; //����ͨ�Ŷ���

        QTimer *timer; //����������ʱ��

        bool comOk; //�����Ƿ�����

        QWaitCondition Thread_Wait;

    private:

        QMutex m_QueyeMutex;

        QQueue<QString>     m_szSendQueue;                //ϴ�����

        volatile bool m_bAlive;

     

    };

     

     

    #define gbl485HeightPort HSingletonTemplatePtr<CHeightPort_485>::get()

     

    +使用队列存储需要发送的消息

        QMutex m_QueyeMutex;

        QQueue<QString>     m_szSendQueue;                //ϴ�����

        volatile bool m_bAlive;

    +从队列中取出消息并且发送(在线程的run函数中实现)使用了数据锁机制

    void CSerialPort_485::run()

    {

        while (m_bAlive)

        {

            m_QueyeMutex.lock();

            int nSize = m_szSendQueue.size();

            if (nSize > 0)

            {

                QString strSend = m_szSendQueue.dequeue();

                m_QueyeMutex.unlock();

                this->msleep(50);

                sendData(strSend);        

                continue;

            }

            else

            {

                Thread_Wait.wait(&m_QueyeMutex);

                m_QueyeMutex.unlock();

            }

        }

    }

    【】串口 上层控制函数 控制开门 读取 传感器信息

    void QPortControl::slot_ReadData(QString strRecevice)

    {

        LOG_INFO("slot_ReadData =[%s]", strRecevice.toStdString().c_str());

     

     

     

     

        //上位机收到开门请求

        if (strRecevice.toUpper() == WantOpenDoor)

        {

            //上位机发送开始执行开门动作

            m_Action = Action_OpenDoor;

            gbl485SerialPort->AddTask(StartOpenDoor);

            return;

        }

        //上位机发送开始执行开门动作

        if (strRecevice.toUpper() == StartOpenDoor && m_Action == Action_OpenDoor)

        {        

            gbl485SerialPort->AddTask(AskOpenDoor);

            return;

        }

        //上位机发送开始执行开门动作

        if (strRecevice.toUpper() == OpenDoorFail && m_Action == Action_OpenDoor)

        {

            gbl485SerialPort->AddTask(AskOpenDoor);

            return;

        }

        //上位机收到开门成功的信号

        if (strRecevice.toUpper() == OpenDoorSucess&& m_Action == Action_OpenDoor)

        {

            m_Action = Action_Null;

            emit sig_OpendoorFinish();    

            return;

        }

     

        /**********关门*****************/

        //上位机收到关门请求

        if (strRecevice.toUpper() == WantDoorClose)

        {

            //上位机发送开始执行关门动作

            m_Action = Action_CloseDoor;

            gbl485SerialPort->AddTask(StartCloseDoor);

            return;

        }

        //上位机询问是否完成关门门动作?

        if (strRecevice.toUpper() == StartCloseDoor && m_Action == Action_CloseDoor)

        {

            gbl485SerialPort->AddTask(AskCloseDoor);

            return;

        }

        //上位机发送开始执行关门动作

        if (strRecevice.toUpper() == CloseDoorFail && m_Action == Action_CloseDoor)

        {

            gbl485SerialPort->AddTask(AskCloseDoor);

            return;

        }

        //上位机收到关门成功的信号

        if (strRecevice.toUpper() == CloseDoorSucess && m_Action == Action_CloseDoor)

        {

            m_Action = Action_Null;

            emit sig_ClosedoorFinish();    

            return;

        }

     

        /**********吸住托盘*****************/

        //上位机收到吸住托盘请求

        if (strRecevice.toUpper() == WantSuckTray)

        {

            m_Action = Action_SuckTray;

            //上位机发送执行电磁体吸住托盘开始命令

            gbl485SerialPort->AddTask(StartSuckTray);

            return;

        }    

        if (strRecevice.toUpper() == StartSuckTray && m_Action == Action_SuckTray)

        {

            //上位机读取电磁吸盘动作是否执行完成?

            gbl485SerialPort->AddTask(AskSuckTray);

            m_nError = 0;

            return;

        }

        //上位机读取电磁吸盘动作是否执行完成

        if (strRecevice.toUpper() == SuckTrayFail && m_Action == Action_SuckTray)

        {

            if (m_nError == ERRORNUM)

            {

                m_Action = Action_Null;

                emit sig_SuckTrayFinish();

                m_nError = 0;

                return;

            }

            gbl485SerialPort->AddTask(AskSuckTray);    

            m_nError = m_nError + 1;

            return;

        }

        //完成吸住托盘

        if (strRecevice.toUpper() == SuckTraySucess && m_Action == Action_SuckTray)

        {

            m_Action = Action_Null;

            emit sig_SuckTrayFinish();

            return;

        }

     

        /*********放开托盘*****************/

        //上位机收到放开托盘请求

        if (strRecevice.toUpper() == WantDropTray)

        {

            //上位机发送执行电磁体放开托盘开始命令

            m_Action = Action_DropTray;

            gbl485SerialPort->AddTask(StartDropTray);

            return;

        }

        if (strRecevice.toUpper() == StartDropTray && m_Action == Action_DropTray)

        {

            //上位机读取电磁放盘动作是否执行完成?

            gbl485SerialPort->AddTask(AskDropTray );

            m_nError = 0;

            return;

        }

        //上位机读取电磁放开盘动作是否执行完成

        if (strRecevice.toUpper() == DropTrayFail && m_Action == Action_DropTray)

        {

            if (m_nError == ERRORNUM)

            {

                m_Action = Action_Null;

                emit sig_DropTrayFinish();

                m_nError = 0;

                return;

     

            }

            gbl485SerialPort->AddTask(AskDropTray);

            m_nError = m_nError + 1;

            return;

        }

        //完成放下托盘

        if (strRecevice.toUpper() == DropTraySucess && m_Action == Action_DropTray)

        {

            m_Action = Action_Null;

            emit sig_DropTrayFinish();

            return;

        }

     

        /**********拉取货物*****************/

        //PLC接收到上位机开门命令

        if (strRecevice.toUpper() == WantGetGoods)

        {

            //上位机发送开始执行拉取货物命令

            m_Action = Action_GetGoods;

            gbl485SerialPort->AddTask(StartGetGoods);

            return;

        }

        if (strRecevice.toUpper() == StartGetGoods && m_Action == Action_GetGoods)

        {

            //上位机读取电磁吸盘动作是否执行完成?

            gbl485SerialPort->AddTask(AskGetGoods);

            Sleep(10);

            GetHeight();

            return;

        }

        //上位机读取电磁吸盘动作是否执行完成

        if (strRecevice.toUpper() == GetGoodsFail && m_Action == Action_GetGoods)

        {

            gbl485SerialPort->AddTask(AskGetGoods);

            Sleep(10);

            GetHeight();

            return;

        }

        //完成拉取货物命令

        if (strRecevice.toUpper() == GetGoodsSucess && m_Action == Action_GetGoods)

        {

            m_Action = Action_Null;

            emit sig_GetGoodsFinish();

            return;

        }

     

        /**********推送到位*****************/

        //PLC接收到上位机推送货物命令

        if (strRecevice.toUpper() == WantPushGoods)

        {

            //收到并执行推送货物

            m_Action = Action_PushGoods;

            gbl485SerialPort->AddTask(StartPushGoods);

            return;

        }

        if (strRecevice.toUpper() == StartPushGoods && m_Action == Action_PushGoods)

        {

            //推送到位没有??

            gbl485SerialPort->AddTask(AskPushGoods);

            return;

        }

        //推送到位没有?

        if (strRecevice.toUpper() == PushGoodsFail && m_Action == Action_PushGoods)

        {

            gbl485SerialPort->AddTask(AskPushGoods);

            return;

        }

        //已经推送到位

        if (strRecevice.toUpper() == PushGoodsSucess && m_Action == Action_PushGoods)

        {

            m_Action = Action_Null;

            emit sig_PushGoodsFinish();

            return;

        }

     

        /********运动到行列******************/

        //PLC收到行数信息

        if (strRecevice.toUpper() == m_strMoveToX)

        {

            //发送列数信息

            m_Action = Action_Move;

            gbl485SerialPort->AddTask(m_strMoveToY);

            return;

        }

        //PLC收到列数信息

        if (strRecevice.toUpper() == m_strMoveToY && m_Action == Action_Move)

        {

            //开始运动到相应的行、列

            gbl485SerialPort->AddTask(StartMove);

            return;

        }

        //PLC收到列数信息

        if (strRecevice.toUpper() == StartMove && m_Action == Action_Move)

        {

            //开始运动到相应的行、列

            gbl485SerialPort->AddTask(AskMove);

            return;

        }

     

        //上位机询问是否达到相应的行、列位置?

        if (strRecevice.toUpper() == MoveFail && m_Action == Action_Move)

        {

            gbl485SerialPort->AddTask(AskMove);

            return;

        }

        //已达到指定行、列位置

        if (strRecevice.toUpper() == MoveSucess && m_Action == Action_Move)

        {

            m_Action = Action_Null;

            emit sig_MoveFinish();

            return;

        }

     

        if (strRecevice.indexOf("3A 30 32 30 33 30 32") != -1)

        {

            emit sig_ReadSetting(m_Action, strRecevice);

            return;

        }

    }

     

    【】模板形式实现 类的单例 实现只有一个CRunTime配置类

     

     

    *【】串口配置界面

     

    *标题

     

    *【】配置文件设置 使用Qsettings文件读写 设置参数 读取 串口和网络 网络自动重连

    /* $Copyright (c) 2006-2016 Green Net World

    *

    * Author    :    Maga version 1.0

    * DateTime    :    2016-7-8 9:39:22

    *

    * http://www.gwchina.cn/

    *

    * CRunTime 运行时缓存数据

    */

     

     

     

     

    #ifndef CRunTime_H

    #define CRunTime_H

    #include "../include/HSingleton/HSingleton.h"

     

    #if _MSC_VER >= 1600

    #pragma execution_character_set("utf-8")

    #endif

     

     

    enum WIDGET_TYPE

    {

        MSGBOX_GET = 0x0001,

        MSGBOX_SAVE = 0x0002,

        MSGBOX_CLOSE = 0x0004,

        MSGBOX_BACK = 0x0008,

        MSGBOX_HOME = 0x00010,

        MSGBOX_OK = 0x00020,

        MSGBOX_RETURN = 0x00040

    };

     

    class QString;

    /** 单例模式类 CRunTime */

    class CRunTime

    {

    public:

     

        /** 默认构造函数(使用私有,不允许外部直接构造) */

        CRunTime();

     

    public:

     

        /** 默认析构函数 */

        ~CRunTime();

     

        // 清空

        void        Clear();

    public:

    QString PortName; //串口号

    int BaudRate; //波特率

    int DataBit; //数据位

    QString Parity;                 //校验位

    double StopBit; //停止位

     

    bool HexSend; //16进制发送

    bool HexReceive; //16进制接收

     

        QString com232_PortName; //串口号

        int com232_BaudRate; //波特率

        int com232_DataBit; //数据位

        QString com232_Parity;                 //校验位

        double com232_StopBit; //停止位

        bool com232_HexSend; //16进制发送

        bool com232_HexReceive; //16进制接收

     

        QString com485_PortName; //串口号

        int com485_BaudRate; //波特率

        int com485_DataBit; //数据位

        QString com485_Parity;                 //校验位

        double com485_StopBit; //停止位

        bool com485_HexSend; //16进制发送

        bool com485_HexReceive; //16进制接收

     

     

    bool Debug; //模拟设备

    bool AutoClear; //自动清空

     

    bool AutoSend; //自动发送

    int SendInterval; //自动发送间隔

    bool AutoSave; //自动保存

    int SaveInterval; //自动保存间隔

     

    QString SendFileName; //发送配置文件名

    QString DeviceFileName; //模拟设备数据文件名

     

    QString Mode; //转换模式

    QString ServerIP; //服务器IP

        QString strComputerUrl; //公司的网址

    int ServerPort; //服务器端口

    int ListenPort; //监听端口

    int SleepTime; //延时时间

    bool AutoConnect; //自动重连

     

    void ReadConfig(); //读取配置文件,在main函数最开始加载程序载入

    void WriteConfig(); //写入配置文件,在更改配置文件程序关闭时调用

    void NewConfig(); //以初始值新建配置文件

    bool CheckConfig(); //校验配置文件

    void WriteError(QString str);//写入错误信息

    void NewDir(QString dirName);//新建目录

     

     

        QString Recevice_485;

        QString Recevice_232;

        QString Recevice_Height;

        QString strWeight;

        QString strHeight;

        int InputNum;

        WIDGET_TYPE m_type;

     

    };

     

     

    #define gblRuntimeData HSingletonTemplatePtr<CRunTime>::get()

     

    #endif // CRunTime_H

     

    *标题

     

    *标题

     

     

    【】*取件码键盘

     

    +设置无边框

    this->setupUi(this);

        setWindowFlags(Qt::FramelessWindowHint | Qt::WindowMinimizeButtonHint );

        this->setWindowIcon(QIcon(":/Image/Resources/Image/zhihuiwu.ico"));

        display->setFocus();

    +正则表达式

     

        QRegExp regx("[0-9]+$");

        QValidator *validator = new QRegExpValidator(regx, display);

        display->setValidator(validator);

        display->hide();

     

    +按键设置样式表

     

    void NumKeyBoard::button_init()

    {

        //数字色值:#7a7675。 字号64px

     

        display->setStyleSheet("QLineEdit{font-size:25px;color:rgb(0,0,0);"

            "height:50px;border:2px solid rgb(4,167,240);"

            "border-radius:10px;}");

        toolButton_0->setStyleSheet("QToolButton{border:2px groove white;border-radius:10px;"

            "background-color:rgb(4,167,240);color:white;font-size:25px}");

        toolButton_1->setStyleSheet("QToolButton{border:2px groove white;border-radius:10px;"

            "background-color:rgb(4,167,240);color:white;font-size:25px}");

        toolButton_2->setStyleSheet("QToolButton{border:2px groove white;border-radius:10px;"

            "background-color:rgb(4,167,240);color:white;font-size:25px}");

        toolButton_3->setStyleSheet("QToolButton{border:2px groove white;border-radius:10px;"

            "background-color:rgb(4,167,240);color:white;font-size:25px}");

        toolButton_4->setStyleSheet("border:2px groove gray;border-radius:10px;"

            " background-color:rgb(4,167,240);color:white;font-size:25px");

        toolButton_5->setStyleSheet("QToolButton{border:2px groove white;border-radius:10px;"

            "background-color:rgb(4,167,240);color:white;font-size:25px}");

        toolButton_6->setStyleSheet("QToolButton{border:2px groove white;border-radius:10px;"

            "background-color:rgb(4,167,240);color:white;font-size:25px}");

        toolButton_7->setStyleSheet("QToolButton{border:2px groove white;border-radius:10px;"

            "background-color:rgb(4,167,240);color:white;font-size:25px}");

        toolButton_8->setStyleSheet("border:2px groove gray;border-radius:10px;"

            " background-color:rgb(4,167,240);color:white;font-size:25px");

        toolButton_9->setStyleSheet("QToolButton{border:2px groove white;border-radius:10px;"

            "background-color:rgb(4,167,240);color:white;font-size:25px}");

        toolButton_backspace->setStyleSheet("border:2px groove gray;border-radius:10px;"

            " background-color:rgb(4,167,240);color:white;font-size:25px");

        toolButton_enter->setStyleSheet("QToolButton{border:2px groove white;border-radius:10px;"

            "background-color:rgb(4,167,240);color:white;font-size:25px}");

     

     

    }

    +设置存件 和 取件 样式不一样

     

    void NumKeyBoard::Init(WIDGET_TYPE types)

    {

        m_type = types;

        if (types & MSGBOX_GET)

        {

            label_title->setStyleSheet("min-379px;"

                                     "max-379px;"

             "min-height:71px;"

                                     "max-height:71px;"

                                     "border-image: url(:/Image/Resources/Image/请输入取件码.png);");

        }

        if (types & MSGBOX_SAVE)

        {

            label_title->setStyleSheet("min-379px;"

                                     "max-379px;"

                                     "min-height:71px;"

                 "max-height:71px;"

                 "border-image: url(:/Image/Resources/Image/请输入存件码.png);");

        }

    }

     

    +输入字符串有误 判断

     

        waitingForOperand = true;

        text = display->text();

        int i = text.indexOf("-");

        if (text.length()!=6 || i!=-1)

        {

            QMessageBox::about(nullptr, "onEnter", "输入的字符有误");

            return;

        }

     

    *【】自定义键盘(QSignalMapper所有按键,获取键值)

    +设置大小写

     

    void KeyBoard::onCapslock()

    {

    caps_Lock = !caps_Lock;

    if(caps_Lock)

    {

    //letter

    ui->toolButton_a->setText("A");

    ui->toolButton_b->setText("B");

    ui->toolButton_c->setText("C");

    ui->toolButton_d->setText("D");

    ui->toolButton_e->setText("E");

    ui->toolButton_f->setText("F");

    ui->toolButton_g->setText("G");

    ui->toolButton_h->setText("H");

    ui->toolButton_i->setText("I");

    ui->toolButton_j->setText("J");

    ui->toolButton_k->setText("K");

    ui->toolButton_l->setText("L");

    ui->toolButton_m->setText("M");

    ui->toolButton_n->setText("N");

    ui->toolButton_o->setText("O");

    ui->toolButton_p->setText("P");

    ui->toolButton_q->setText("Q");

    ui->toolButton_r->setText("R");

    ui->toolButton_s->setText("S");

    ui->toolButton_t->setText("T");

    ui->toolButton_u->setText("U");

    ui->toolButton_v->setText("V");

    ui->toolButton_w->setText("W");

    ui->toolButton_x->setText("X");

    ui->toolButton_y->setText("Y");

    ui->toolButton_z->setText("Z");

    // toolButton_line1->setText("<");

    // toolButton_line2->setText(">");

    // toolButton_line3->setText("-");

    // toolButton_dot->setText("*");

    // toolButton_wenhao->setText("#");

    }

    +键盘输入框设置为密码模式 正常模式

     

    void KeyBoard::onInvMode()

    {

    if(inputMode == iMode_Normal)

    {

    inputMode = iMode_Passwd;

    }

    else if(inputMode == iMode_Passwd)

    {

    inputMode = iMode_Normal;

    }

     

    if(inputMode == iMode_Normal)

    {

    ui->display->setEchoMode(QLineEdit::Normal);

    }

    else if(inputMode == iMode_Passwd)

    {

    ui->display->setEchoMode(QLineEdit::Password);

    }

    }

    +按键回退backspace

     

    void KeyBoard::onBackspace()

    {

    ui->display->backspace();

    }

    +鼠标拖拽

     

    /*支持鼠标拖拽键盘*/

    void KeyBoard::mouseMoveEvent(QMouseEvent *event)

    {

    if (event->buttons() == Qt::LeftButton)

    {

    move(event->globalPos() - dragPosition);

    event->accept();

    }

    }

     

    void KeyBoard::mousePressEvent(QMouseEvent *event)

    {

    if (event->button() == Qt::LeftButton)

    {

    dragPosition = event->globalPos() - frameGeometry().topLeft();

    event->accept();

    }

    if (event->button() == Qt::RightButton)

    {

    close();

    }

    }

    +光标右移 左移

    void KeyBoard::onCursorRight()

    {

    ui->display->cursorForward(false,1);

    }

    void KeyBoard::onCursorRight()

    {

    ui->display->cursorForward(false,1);

    }

    【】*全局设置 使用静态成员函数 可以通过类名加域作用运算符访问,

    +延时

    //延时

    static void sleep(int sec)

    {

    QTime dieTime = QTime::currentTime().addMSecs(sec);

     

    while (QTime::currentTime() < dieTime) {

    QCoreApplication::processEvents(QEventLoop::AllEvents, 100);

    }

    }

     

    +窗体居中显示

     

    +设置系统日期时间

     

    +十六进制与字符 十进制 互化

     

    +生成二维码

    //生成二维码

        static QPixmap MakeQRPixmap(const QString& strQRCode, const QPixmap& midPix, int size)

        {

            CQR_Encode m_encode;

            m_encode.EncodeData(QR_LEVEL_M, 0, true, -1, strQRCode.toStdString().c_str());

            int imageSize = m_encode.m_nSymbleSize + (QR_MARGIN * 2);

            QImage temp(QSize(imageSize, imageSize), QImage::Format_RGB32);

            temp.fill(QColor(255, 255, 255));

            for (int i = 0; i < m_encode.m_nSymbleSize; i++)

            {

                for (int j = 0; j < m_encode.m_nSymbleSize; j++)

                {

                    if (m_encode.m_byModuleData[i][j])

                    {

                        temp.setPixel(i + QR_MARGIN, j + QR_MARGIN, RGB(0, 0, 0));

                    }

                }

            }

            QPixmap pix = QPixmap::fromImage(temp);

            pix = pix.scaled(QSize(size, size), Qt::KeepAspectRatio);

            QPainter painter(&pix);

            if (!midPix.isNull())

            {

                painter.drawPixmap((pix.width() - midPix.width()) / 2, (pix.height() - midPix.height()) / 2, midPix);

                painter.end();

            }

            return pix;

        }

     

    *延时

     

    *延时

     

    *延时

     

    *延时

     

    *延时

     

    *延时

     

    *延时

     

    *延时

     

    *延时

     

  • 相关阅读:
    Javaweb学习12.4
    Javaweb学习11.23
    Javaweb学习11.29
    Javaweb学习12.3
    Javaweb学习12.1
    Javaweb学习11.27
    2020年8月25日Java学习日记
    2020年8月18日Java学习日记
    2020年8月22日Java学习日记
    2020年8月21日Java学习日记
  • 原文地址:https://www.cnblogs.com/tangyuanjie/p/14304630.html
Copyright © 2011-2022 走看看