嵌入式养成计划-46----QT--简易版网络聊天室实现--QT如何连接数据库

一百一十九、简易版网络聊天室实现

119.1 QT实现连接TCP协议

119.1.1 基于TCP的通信流程

在这里插入图片描述
在这里插入图片描述

119.1.2 QT中实现服务器过程

  1. 使用QTcpServer实例化一个服务器对象
  2. 设置监听状态,通过listen()函数,可以监听特定的主机,也可以监听所有客户端,端口号可以是系统自动分配的,也可以是指定端口号。
  3. 如果有客户端发来连接请求,那么服务器就自动发射一个newConnection信号,我们就可以将该信号连接到自定义的槽函数中处理该客户端的操作。
  4. 此时服务器和客户端已经建起了连接,可以调用nextPandingConnection获取最新连接的客户端套接字,可以将该套接字存放在服务器的客户端容器中。
  5. 当客户端发来数据时,该客户端就会自动发射一个readyRead信号,我们可以将该信号连接到自定义的槽函数中读取客户端数据。
  6. 通过read(),readLine(),readAll()读取套接字里的数据,可以通过write()往套接字中写入数据
  7. 关闭服务器使用close即可

119.1.3 QT中实现客户端过程

  1. 使用QTcpSocket实例化一个客户端对象
  2. 将客户端连接到服务器,使用connectToHost, 给定主机地址,端口号
  3. 如果连接成功,该客户端会自动发射connected信号,我们可以将该信号连接到自定义的槽函数中处理相关逻辑代码。
  4. 此时,客户端和服务器已经建立了连接,如果服务端发来数据,那么该客户端会自定发射readyRead信号,可以将该信号连接到自定义的槽函数中读取服务端中数据
  5. 可以使用read() readLine() readAll()读取套接字中数据,使用write往套接字中写入数据
  6. 客户端断开与服务器的连接,使用disConnectFromHost, 如果断开成功,客户端会自动发射disconnected信号,我们可以将该信号连接到自定义的槽函数中处理相关逻辑代码。

119.2 服务器端

119.2.1 UI 界面

在这里插入图片描述

119.2.2 qt_server.h

#ifndef QT_SERVER_H
#define QT_SERVER_H#include <QWidget>
#include <QTcpServer>
#include <QList>
#include <QTcpSocket>
#include <QMessageBox>
#include <QDebug>QT_BEGIN_NAMESPACE
namespace Ui { class Qt_Server; }
QT_END_NAMESPACEclass Qt_Server : public QWidget
{Q_OBJECTpublic:Qt_Server(QWidget *parent = nullptr);~Qt_Server();private slots:void on_startBtn_clicked();public slots://  自定义的 处理连接的槽函数声明void newConnection_slot();//  自定义的 处理接收数据的槽函数声明void readyRead_slot();private:Ui::Qt_Server *ui;QTcpServer *server;QList<QTcpSocket *> socketList;
};
#endif // QT_SERVER_H

119.2.3 qt_server.cpp

#include "qt_server.h"
#include "ui_qt_server.h"Qt_Server::Qt_Server(QWidget *parent): QWidget(parent), ui(new Ui::Qt_Server)
{ui->setupUi(this);//  给服务器指针创建空间server = new QTcpServer(this);
//    socketList = new QList<>();
}Qt_Server::~Qt_Server()
{delete ui;
}//  点击启动服务器按钮对应的槽函数实现
void Qt_Server::on_startBtn_clicked()
{if(ui->startBtn->text() == "启动服务器"){//  获取UI界面上输入的端口号quint16 port = ui->portEdit->text().toUInt();//  使服务器进入监听状态,返回值是bool类型if(server->listen(QHostAddress::Any,port)){QMessageBox::information(this,"提示","服务器启动成功");}else{QMessageBox::critical(this,"错误","服务器启动失败");}//  此时服务器已经进入监听状态,如果有客户端发来链接请求,//  则服务器会自动发射一个newConnection信号,需要将该信号连接到自定义的槽函数中处理连接的套接字connect(server, &QTcpServer::newConnection, this, &Qt_Server::newConnection_slot);ui->startBtn->setText("已启动服务器");ui->startBtn->setStyleSheet("background-color:green");}else{server->close();disconnect(server, &QTcpServer::newConnection, this, &Qt_Server::newConnection_slot);ui->startBtn->setStyleSheet("background-color:white");ui->startBtn->setText("启动服务器");}}//  自定义的 处理连接的槽函数声明
void Qt_Server::newConnection_slot()
{qDebug() << "有新客户的连接";//  获取最新连接的客户端套接字,并放入容器中,此时客户端与服务器已经建立起连接QTcpSocket *s = server->nextPendingConnection();socketList.push_back(s);//  如果有客户端发送数据,那么此客户端就会自动发射readyRead信号//  需要将该信号与自定义的处理接收数据的槽函数连接connect(s, &QTcpSocket::readyRead, this, &Qt_Server::readyRead_slot);
}//  自定义的 处理接收数据的槽函数实现
void Qt_Server::readyRead_slot()
{//  移除无效的客户端//  遍历所有的客户端for(int i=0; i<socketList.count(); i++){//  判断每个客户端的状态,返回值是枚举类型//  socketList.at(i)->state();if(0 == socketList.at(i)->state()){//  移除当前客户端,通过下标删除socketList.removeAt(i);}}//  遍历容器,找到有需要读取数据的客户端for(int i=0; i<socketList.count(); i++){//  如果当前客户端的 有效字节数 不为0,代表当前客户端有需要读取的数据if(0 != socketList.at(i)->bytesAvailable()){//  读取客户端中的数据QByteArray msg = socketList.at(i)->readAll();//  将读取的数据放到UI界面上ui->listWidget->addItem(QString::fromLocal8Bit(msg));//  将数据发送给所有客户端for (int j=0; j<socketList.count(); j++) {socketList.at(i)->write(msg);}}}
}

119.2.4 main.cpp

#include "qt_server.h"#include <QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);Qt_Server w;w.show();return a.exec();
}

119.3 客户端

119.3.1 UI 界面

在这里插入图片描述

119.3.2 qt_client.h

#ifndef QT_CLIENT_H
#define QT_CLIENT_H#include <QWidget>
#include <QTcpServer>
#include <QList>
#include <QTcpSocket>
#include <QMessageBox>
#include <QDebug>QT_BEGIN_NAMESPACE
namespace Ui { class Qt_Client; }
QT_END_NAMESPACEclass Qt_Client : public QWidget
{Q_OBJECTpublic:Qt_Client(QWidget *parent = nullptr);~Qt_Client();public slots:void connected_slot();void readyRead_slot();private slots:void on_connectBtn_clicked();void on_msgBtn_clicked();void on_disconnectBtn_clicked();private:Ui::Qt_Client *ui;//  定一个客户端对象QTcpSocket *socket;//  定义一个用户名变量QString uname;
};
#endif // QT_CLIENT_H

119.3.3 qt_client.cpp

#include "qt_client.h"
#include "ui_qt_client.h"Qt_Client::Qt_Client(QWidget *parent): QWidget(parent), ui(new Ui::Qt_Client)
{ui->setupUi(this);//  给客户端对象创建空间socket = new QTcpSocket(this);//  初始化UI界面的组件状态ui->msgEdit->setEnabled(false);ui->msgBtn->setEnabled(false);ui->disconnectBtn->setEnabled(false);}Qt_Client::~Qt_Client()
{delete ui;
}void Qt_Client::connected_slot()
{//  告诉服务器我进来了QString msg = uname + " 来了,快接驾";//  将这个消息发送给服务器socket->write(msg.toLocal8Bit());//  此时说明服务器与客户端已经建立连接//  现在将UI界面的组件状态修改一下ui->msgEdit->setEnabled(true);ui->msgBtn->setEnabled(true);ui->disconnectBtn->setEnabled(true);ui->unameEdit->setEnabled(false);ui->ipEdit->setEnabled(false);ui->portEdit->setEnabled(false);ui->connectBtn->setEnabled(false);//  如果服务器发来数据,客户端会自动发射readyRead信号//  因此需要将readyRead信号连接到自定义的槽函数//  因为只需要连接一次,所以也是该在构造函数中写连接函数connect(socket, &QTcpSocket::readyRead, this, &Qt_Client::readyRead_slot);
}void Qt_Client::readyRead_slot()
{//  走到了这一步,说明服务器给客户端发送了消息,现在需要进行读取QByteArray msg = socket->readAll();//  将这个数据放到UI界面的消息显示框中ui->listWidget->addItem(QString::fromLocal8Bit(msg));
}//  连接服务器按钮 对应的槽函数
void Qt_Client::on_connectBtn_clicked()
{//  获取UI界面的IP和PORT,还有unameQString ip = ui->ipEdit->text();quint16 port = ui->portEdit->text().toUInt();uname = ui->unameEdit->text();//  使客户端连接服务器socket->connectToHost(ip, port);//  判断客户端是否成功连接服务器,成功则客户端会自动发射connected信号//  将该信号连接到自定义的槽函数中//  因为只需要连接一次,所以连接函数应该写在构造函数中connect(socket, &QTcpSocket::connected, this, &Qt_Client::connected_slot);
}void Qt_Client::on_msgBtn_clicked()
{//  获取UI界面上输入的内容QString msg = uname + " : " + ui->msgEdit->toPlainText();//  将消息发送给服务器socket->write(msg.toLocal8Bit());}void Qt_Client::on_disconnectBtn_clicked()
{QString msg = uname + " 走咯,我还会再回来的";//  将消息发送给服务器socket->write(msg.toLocal8Bit());//  断开链接socket->close();disconnect(socket, &QTcpSocket::readyRead, this, &Qt_Client::readyRead_slot);disconnect(socket, &QTcpSocket::connected, this, &Qt_Client::connected_slot);//  更改UI界面的组件状态ui->msgEdit->setEnabled(false);ui->msgBtn->setEnabled(false);ui->disconnectBtn->setEnabled(false);ui->unameEdit->setEnabled(true);ui->ipEdit->setEnabled(true);ui->portEdit->setEnabled(true);ui->connectBtn->setEnabled(true);
}

119.3.4 main.cpp

#include "qt_client.h"#include <QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);Qt_Client w;w.show();return a.exec();
}

一百二十、QT连接数据库

120.1 QT将数据库分为三个层次

  1. 数据库驱动层:QSqlDriver、QSqlDriverCreator、QSqlDriverCreatorBase、QSqlDriverPlugin
  2. sql接口层:QSqlDatabase、QSqlQuery、QSqlRecord、QSqlError
  3. 用户接口层:提供一些模型QSqlQueryModel、QSqlTableModel、QSqlRelationalTableModel

120.2 实现数据库操作的相关方法

1.	添加数据库:[static] QSqlDatabase QSqlDatabase::addDatabase(QSqlDriver *driver, const QString &connectionName = QLatin1String(defaultConnection))                                                                QSQLITE SQLite version 3 or above
2.	设置数据库名称:void QSqlDatabase::setDatabaseName(const QString &name)
3.	包含数据库:bool QSqlDatabase::contains(const QString &connectionName = QLatin1String(defaultConnection))
4.	打开数据库:bool QSqlDriver::open(const QString &db)
5.	关闭数据库:void QSqlDatabase::close()
6.	错误信息:QSqlError QSqlDatabase::lastError()7.	sql语句执行:构造一个QSqlQuery类对象,调用其成员函数exec,执行sql语句:bool QSqlQuery::exec(const QString &query)
8.	bool QSqlQuery::next():遍历查询结果的函数

120.3 示例 :

120.3.1 UI 界面

120.3.2 database.h

#ifndef DATABASE_H
#define DATABASE_H#include <QWidget>
#include <QSqlDatabase> //  数据库管理类
#include <QSqlQuery>    //  数据库操作类
#include <QSqlRecord>   //  数据库记录类
#include <QSqlError>    //  数据库错误类
#include <QMessageBox>  //  消息对话框类
#include <QtDebug>QT_BEGIN_NAMESPACE
namespace Ui { class DataBase; }
QT_END_NAMESPACEclass DataBase : public QWidget
{Q_OBJECTpublic:DataBase(QWidget *parent = nullptr);~DataBase();private slots:void on_addBtn_clicked();void on_showAllEdit_clicked();void on_showEdit_clicked();void on_deleteBtn_clicked();void on_deleteAllBtn_clicked();private:Ui::DataBase *ui;//  实例化一个数据库对象QSqlDatabase db;
};
#endif // DATABASE_H

120.3.3 database.cpp

#include "database.h"
#include "ui_database.h"DataBase::DataBase(QWidget *parent): QWidget(parent), ui(new Ui::DataBase)
{ui->setupUi(this);//  判断数据库是否存在if(!db.contains()){//  不存在,则创建数据库db = QSqlDatabase::addDatabase("QSQLITE");   //  表示数据库驱动为sqlite3//  给刚创建的数据库起名db.setDatabaseName("stuInfo.db");//  提示用户:数据库创建成功QMessageBox::information(this,"提示","数据库创建成功");}//  打开数据库if(!db.open()){QMessageBox::critical(this,"错误","数据库无法打开");return;}//  创建数据库表QSqlQuery table;//  Sql语句QString sql = "create table if not exists stu_info_table(""id integer primary key autoincrement,""numb integer,""name varchar(20),""sex varchar(4),""score integer);";if(table.exec(sql)){QMessageBox::information(this,"提示","数据库学生表创建成功");}else{QMessageBox::critical(this,"错误","数据库学生表创建失败");}
}DataBase::~DataBase()
{delete ui;
}//  添加
void DataBase::on_addBtn_clicked()
{//  获取UI界面上的信息int num = ui->numEdit->text().toUInt();QString name = ui->nameEdit->text();QString sex = ui->sexEdit->text();int score = ui->scoreEdit->text().toUInt();//  保证用户输入完整信息if(num == 0 || name.isEmpty() || sex.isEmpty() || score == 0){QMessageBox::warning(this,"警告","请完善信息");return;}//  将信息存放到数据库学生表中QSqlQuery query;//  SQL语句QString sql = QString("insert into stu_info_table (numb, name, sex, score) ""values(%1,'%2','%3',%4)").arg(num).arg(name).arg(sex).arg(score);//  执行SQL语句if(query.exec(sql)){QMessageBox::information(this,"提示","数据添加成功");}else{QMessageBox::critical(this,"错误","数据添加失败");}DataBase::on_showAllEdit_clicked();
}//  查看所有
void DataBase::on_showAllEdit_clicked()
{ui->tableWidget->clear();//  将信息存放到数据库学生表中QSqlQuery query;//  SQL语句QString sql = QString("select * from stu_info_table");//  执行SQL语句if(query.exec(sql)){//  将数据库的内容放到UI界面上int i = 0;  //  记录行号//  用next()遍历while (query.next()) {for(int j=0; j<query.record().count(); j++){ui->tableWidget->setItem(i,j,new QTableWidgetItem(query.value(j+1).toString()));}i++;}}
}//  查询某个学生
void DataBase::on_showEdit_clicked()
{QString name = ui->nameEdit_2->text();if(name.isEmpty()){QMessageBox::information(this, "提示", "请输入要查询的学生姓名");return;}ui->tableWidget->clear();QSqlQuery query;QString sql = QString("select * from stu_info_table where name='%1';").arg(name);if(query.exec(sql)){int i=0;while (query.next()) {//            qDebug() << i;for(int j=0; j<query.record().count(); j++){ui->tableWidget->setItem(i, j, new QTableWidgetItem(query.value(j+1).toString()));}i++;}}else{QMessageBox::warning(this, "警告", "查询错误");return;}
}void DataBase::on_deleteBtn_clicked()
{QString name = ui->nameEdit_2->text();if(name.isEmpty()){QMessageBox::information(this, "提示", "请输入要删除的学生姓名");return ;}QString sql = QString("delete from stu_info_table where name='%1';").arg(name);QSqlQuery query;if(query.exec(sql)){QMessageBox::information(this, "提示", "删除成功");DataBase::on_showAllEdit_clicked();}else{QMessageBox::warning(this, "警告", "删除失败");DataBase::on_showAllEdit_clicked();}return ;
}void DataBase::on_deleteAllBtn_clicked()
{QString sql = QString("delete from stu_info_table");QSqlQuery query;if(query.exec(sql)){QMessageBox::information(this, "提示", "删除成功");DataBase::on_showAllEdit_clicked();}else{QMessageBox::warning(this, "警告", "删除失败");DataBase::on_showAllEdit_clicked();}ui->tableWidget->clear();return ;
}

120.3.4 main.cpp

#include "database.h"#include <QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);DataBase w;w.show();return a.exec();
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/168459.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Andriod学习笔记(二)

页面设计的零碎知识 通用属性设置文本大小设置视图宽高设置视图的对齐方式 页面布局LinearLayoutRelativeLayoutGridLayoutScollView 按钮触控ButtonImageViewImageButton 案例&#xff1a;简易计算机 通用属性 设置文本大小 纯数字的setTextSize方法&#xff0c;内部默认字体…

flutter doctor检测环境,出现CocoaPods installed but not working

1. 安装flutter, 地址: 安装和环境配置 - Flutter 中文文档 - Flutter 中文开发者网站 - Flutter 2. 安装成功后&#xff0c;通过flutter doctor检测环境。以mac为例&#xff0c;出现了CocoaPods installed but not working 错误提示时&#xff0c;以下为解决方案: 2.1 rvm i…

【JavaEE】JUC 常见的类 -- 多线程篇(8)

JUC 常见的类 1. Callable 接口2. ReentrantLock3. 原子类4. 线程池5. 信号量 Semaphore6. CountDownLatch 1. Callable 接口 Callable Interface 也是一种创建线程的方式 Runnable 能表示一个任务 (run方法) – 返回 voidCallable 也能表示一个任务(call方法) 返回一个具体的…

vue重修之自定义项目、ESLint和代码规范修复

文章目录 VueCli 自定义创建项目ESlint代码规范及手动修复代码规范错误 VueCli 自定义创建项目 安装脚手架 (已安装) npm i vue/cli -g创建项目 vue create xxx选项 Vue CLI v5.0.8 ? Please pick a preset:Default ([Vue 3] babel, eslint)Default ([Vue 2] babel, eslint) …

阿里云服务结构--长期更新

CNCF 全称Cloud Native Computing Foundation&#xff08;云原生计算基金会&#xff09;&#xff0c;成立于 2015 年7月21日&#xff08;于美国波特兰OSCON 2015上宣布&#xff09;&#xff0c;其最初的口号是坚持和整合开源技术来让编排容器作为微服务架构的一部分&#xff0…

5256C 5G终端综合测试仪

01 5256C 5G终端综合测试仪 产品综述&#xff1a; 5256C 5G终端综合测试仪主要用于5G终端、基带芯片的研发、生产、校准、检测、认证和教学等领域。该仪表具备5G信号发送功能、5G信号功率特性、解调特性和频谱特性分析功能&#xff0c;支持5G终端的产线高速校准及终端发射机…

集成学习方法(随机森林和AdaBoost)

释义 集成学习很好的避免了单一学习模型带来的过拟合问题 根据个体学习器的生成方式&#xff0c;目前的集成学习方法大致可分为两大类&#xff1a; Bagging(个体学习器间不存在强依赖关系、可同时生成的并行化方法) 流行版本&#xff1a;随机森林(random forest)Boosting(个体…

数据结构和算法——用C语言实现所有树形结构及相关算法

文章目录 前言树和森林基础概念二叉树二叉树的遍历二叉树的构造树和森林与二叉树之间的转化树和森林的遍历 满二叉树完全二叉树线索二叉树线索二叉树的构造寻找前驱和后继线索二叉树的遍历 最优二叉树&#xff08;哈夫曼树&#xff09;哈夫曼树的构造哈夫曼编码 二叉排序树&…

win10专业版驱动开发

我使用的系统版本如何下&#xff1a; 使用的visual studio为VS2019,使用的SDK,WDK如下&#xff1a; 在visual studio单个组件里选择SDK10.0.018362.0 在WDK里面选择版本为&#xff1a; 下载链接如下&#xff1a; 以前的 WDK 版本和其他下载 - Windows drivers | Microsoft Le…

Kubernetes - 一键安装部署 K8S(附:Kubernetes Dashboard)

问题描述 不知道大伙是如何安装 K8s&#xff0c;特别还是集群的时候&#xff0c;我上一次安装搭建的时候&#xff0c;那个恶心到我了&#xff0c;真的是一步一个脚印走完整个搭建流程&#xff0c;爬了不少坑。 于是&#xff0c;才有了今天的文章&#xff0c;到底有没有可以一…

YOLO V8训练自己的数据集并测试

目录 1 YOLOV8部署 2 标注软件labelme安装 3 将labelme转化为YOLOV8支持的数据格式 4 开始训练 5 利用训练结果进行测试 1 YOLOV8部署 我的一篇博客已经提到&#xff0c;这里不再赘述&#xff1a; YOLO V8语义分割模型部署-CSDN博客YOLO V8语义分割模型部署https://blog.cs…

【仙逆】王林用计灭富二代,有长命锁也没用,藤化元一怒请一人出山

【侵权联系删除】【文/郑尔巴金】 仙逆动漫第七集已经更新了。而这一集看下来&#xff0c;可以说非常精彩&#xff0c;全程在打&#xff0c;期间还能看到主角王林用谋&#xff0c;是如何一步步的把敌人藤厉引入陷阱灭杀的&#xff0c;更可以看到王林是如何筑基的。那么多的不说…

Xcode14创建github远程仓库Token

1.点击Create a Token on GitHub 2.在打开的网页中,登陆GitHub 3.点击生成Token 这是不能为空 4.Token创建成功如下: 5.复制Token到Xcode然后点击Sign In登陆 正在创建远程我仓库 正在将本地仓库代码推入远程仓库 创建成功

深入理解算法:从基础到实践

深入理解算法&#xff1a;从基础到实践 1. 算法的定义2. 算法的特性3. 算法的分类按解决问题的性质分类&#xff1a;按算法的设计思路分类&#xff1a; 4. 算法分析5. 算法示例a. 搜索算法示例&#xff1a;二分搜索b. 排序算法示例&#xff1a;快速排序c. 动态规划示例&#xf…

07、Python -- 序列相关函数与封包解包

目录 使用函数字符串也能比较大小序列封包序列解包多变量同时赋值 最大值、最小值、长度 序列解包与封包 使用函数 len()、max()、min() 函数可获取元组、列表的长度、最大值和最小值。 字符串也能比较大小 字符串比较大小时&#xff0c;将会依次按字符串中每个字符对应的编…

华为eNSP配置专题-RIP路由协议的配置

文章目录 华为eNSP配置专题-RIP路由协议的配置0、概要介绍1、前置环境1.1、宿主机1.2、eNSP模拟器 2、基本环境搭建2.1、终端构成和连接2.2、终端的基本配置 3、RIP路由的配置3.1、RIP路由的配置3.2、RIP路由的删除 华为eNSP配置专题-RIP路由协议的配置 0、概要介绍 路由信息…

【python入门篇】字符串(4)

这一章节来说下字符串的使用&#xff0c;字符串是 Python 中最常用的数据类型&#xff0c;我们可以使用单引号( &#xff09;或 双引号&#xff08; " )来创建字符串&#xff0c;那么接下来就进入本章节的一个学习。 一、环境配置 我这边python的环境是3.7.8版本的&…

2024王道考研计算机组成原理——指令系统

零、本章概要 指令寻址&#xff1a;解决的是PC"1"的问题 数据寻址&#xff1a;使用寄存器/内存/结合 基址寻址&#xff1a;用于多道程序的并发执行 直接寻址&#xff1a;call 0x12345678 变址寻址&#xff1a;esi edi用于循环&#xff0c;因为使用直接寻址需要一堆…

dashboard报错 错误:无法获取网络列表、dashboard报错 错误:无法获取云主机列表 解决流程

文章目录 错误说明dashboard上报错底层命令报错查看日志message日志httpd报错日志错误日志分析开始解决测试底层命令dashboard错误说明 dashboard上报错 首先,dashboard上无论是管理员还是其他项目,均无法获取云主机和网络信息,具体报错如下

uniapp实现登录组件之外区域置灰并引导登录

实现需求 每个页面需要根据用户是否登录决定是否显示登陆组件,登录组件半屏底部显示,登录组件之外区域置灰,功能按钮点击之后引导提示登录.页面效果如下: 实现思路说明 设置登录组件背景颜色为灰色,将页面分成登录区域(底部)和非登陆区域(上面灰色显示部分), 置灰区域添加…