登陆demo效果如图:

用户登陆历史使用Popup+ListView组件,model采用c++model实现。
qloginnamelistmodel.h
#ifndef QLOGINNAMELISTMODEL_H
#define QLOGINNAMELISTMODEL_H
#include <QAbstractListModel>
class QLoginNameListModel : public QAbstractListModel
{
Q_OBJECT
public:
enum LoginNameRole {
ul_role_name
};
Q_ENUM(LoginNameRole)
explicit QLoginNameListModel(QObject *parent = nullptr);
// Basic functionality:
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QHash<int, QByteArray> roleNames() const override;
//记住用户名
Q_INVOKABLE int rememberLoginName(QString qsName);
//上次登录的用户名
Q_INVOKABLE QVariant lastLoginName();
//获取记录的条数
Q_INVOKABLE uint userListCount();
private:
//同步数据
int synchroData();
QList<QStringList> m_data;
};
#endif // QLOGINNAMELISTMODEL_H
qloginnamelistmodel.cpp
#include "qloginnamelistmodel.h"
#include <QFile>
#include <QCoreApplication>
#include <QDebug>
#include <QSet>
QLoginNameListModel::QLoginNameListModel(QObject *parent)
: QAbstractListModel(parent)
{
synchroData();
}
int QLoginNameListModel::rowCount(const QModelIndex &parent) const
{
// For list models only the root node (an invalid parent) should return the list's size. For all
// other (valid) parents, rowCount() should return 0 so that it does not become a tree model.
if (parent.isValid())
return 0;
// FIXME: Implement me!
return m_data.count();
}
QVariant QLoginNameListModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (index.row() >= rowCount())
{
return "";
}
switch (role) {
case LoginNameRole::ul_role_name:
return m_data.at(index.row()).at(0);
break;
default:
break;
}
return QVariant();
}
QHash<int, QByteArray> QLoginNameListModel::roleNames() const
{
static const QHash<int, QByteArray> roles{
{ ul_role_name, "ul_role_name"},
};
return roles;
}
int QLoginNameListModel::synchroData()
{
//从文件读取登录用户名
QFile file(QCoreApplication::applicationDirPath() + "/logininfo.txt");
bool isOk = file.open(QIODevice::ReadOnly | QIODevice::Text); //以读写方式打开
if (isOk)
{
beginResetModel();
m_data.clear();
//这里使用一行一行的读
QStringList strNamenInfo;
QTextStream in(&file); //QFile中保留了换行符'\n';QTextStream中去掉了换行符'\n'
while (!in.atEnd())
{
strNamenInfo.clear();
//读一行
strNamenInfo << in.readLine();
if (!strNamenInfo.isEmpty())
{
m_data.append(strNamenInfo);
}
}
endResetModel();
}
file.close();
return 0;
}
int QLoginNameListModel::rememberLoginName(QString qsName)
{
//按顺序读取文件中记录的所有用户名
//在首位插入用户名
//取前10个用户名,如果小于等于10个全部取出,按顺序重新写入文件
QList<QStringList> dataList;
QStringList nameInfo;
nameInfo << qsName;
dataList.append(nameInfo);
//QList<QString> uniqueDataList = m_data.toSet().toList(); //用户名去重(注意这个会对数据重新按字典序排序)
QList<QStringList> uniqueDataList = m_data;
QList<QStringList>::iterator iter = uniqueDataList.begin();
for (; iter != uniqueDataList.end();)
{
if (dataList.size() >= 10)
break;
if (qsName == (*iter).at(0))
{
//用户名已经存在,则删除之前的用户名,重新插入,保证每次默认显示的都是最新登录成功的
iter = uniqueDataList.erase(iter);
}
else
{
dataList.append(*iter);
++iter;
}
}
QFile file(QCoreApplication::applicationDirPath() + "/logininfo.txt");
bool isOk = file.open(QIODevice::WriteOnly | QIODevice::Text | QFile::Truncate); //以清空的方式打开
if (isOk)
{
QTextStream out(&file);
int i = 0;
foreach(QStringList qsNameInfo, dataList)
{
if (i >= 10)
break;
foreach (QString nameItem, qsNameInfo) {
out << nameItem;
}
out << endl;
++i;
}
}
file.flush();
file.close();
synchroData(); //更新model数据
return 0;
}
QVariant QLoginNameListModel::lastLoginName()
{
if (m_data.size())
{
return m_data[0].at(0);
}
else
{
return "";
}
}
uint QLoginNameListModel::userListCount()
{
return m_data.size();
}
main.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import LoginNameListModel 1.0
Window {
id: appWin
width: 640
height: 480
visible: true
title: qsTr("Hello World")
property string loginerrreason: ""
signal loginSignal(var uname, var password)
//用户历史
LoginNameListModel {
id: loginusername
}
Component {
id: login
Login{
anchors.fill: parent
}
}
Component {
id: appPage
AppPannel{
anchors.fill: parent
}
}
Loader {
id: pageloder
anchors.fill: parent
sourceComponent: login
}
function loginFunc(uname, password){
if(("admin" === uname || "test" === uname)
&& "123456" === password){
pageloder.sourceComponent = appPage
//记住用户名
loginusername.rememberLoginName(uname)
}else{
loginerrreason = qsTr("用户名密码错误!")
}
}
function logoutFunc(){
pageloder.sourceComponent = login
}
}
import QtQuick 2.12
import QtQuick.Controls 2.12
Rectangle {
anchors.fill: parent
Button {
text: qsTr("注销")
anchors.right: parent.right
onClicked: {
appWin.logoutFunc()
}
}
Text {
id: name
text: qsTr("Welcome LoginDemo!")
anchors.centerIn: parent
font.pixelSize: 14
}
}
Login.qml
import QtQuick 2.12
import QtQuick.Controls 2.12
Rectangle {
id: login
anchors.fill: parent
Column {
spacing: 20
anchors.centerIn: parent
Row{
id: t
spacing: 10
Column {
spacing: 20
Text {
text: qsTr("用户名")
height: 40
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
}
Text {
text: qsTr("密码")
height: 40
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
}
}
Column {
spacing: 20
TextField {
id: name
height: 40
selectByMouse: true
placeholderText: qsTr("请输入用户名")
text: loginusrlist.selectName
onPressed: {
loginusrlist.x = name.mapToItem(login, 0, 0).x
loginusrlist.y = name.mapToItem(login, 0, 0).y+name.height+1
loginusrlist.open()
}
}
TextField {
id: pwd
height: 40
selectByMouse: true
placeholderText: qsTr("请输入密码")
}
}
}
Text {
id: errCode
color: "red"
text: appWin.loginerrreason
height: 20
}
Button {
text: qsTr("登 陆")
height: 40
width: t.width
onClicked: {
appWin.loginerrreason = ""
console.log("login: name=", name.text, "; pwd=", pwd.text)
appWin.loginFunc(name.text, pwd.text)
}
}
}
LoginUserList {
id: loginusrlist
}
}
LoginUserList.qml
import QtQuick 2.12
import QtQuick.Controls 2.12
Popup {
id: ulItem
height: loginusername.userListCount()*40
width: 240
property string selectName: ""
background: ListView {
anchors.fill: parent
model: loginusername
clip: true //对超出划定边界的数据进行裁剪
delegate: Rectangle {
id: userItem
color: "lightblue"
width: ulItem.width
height: nameTxt.text === "" ? 0: 40
Text {
id: nameTxt
x: 11
anchors.verticalCenter: parent.verticalCenter
text: ul_role_name
}
Rectangle {
width: ulItem.width
height: 1
color: "gray"
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
onContainsMouseChanged: {
if(containsMouse) {
parent.color = "lightgreen"
}else{
parent.color = "lightblue"
}
}
onClicked: {
ulItem.selectName = ul_role_name
ulItem.visible = false
}
}
}
}
}
main.qml
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <qqml.h>
#include "qloginnamelistmodel.h"
int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QGuiApplication app(argc, argv);
qmlRegisterType<QLoginNameListModel>(
"LoginNameListModel",
1, 0,
"LoginNameListModel");
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}