• qt实现web服务器加载vue应用进行C++和html混合编程-连载【6】-企业级系统开发实战连载系列 -技术栈(vue、element-ui、qt、c++、sqlite)


    https://blog.csdn.net/m0_49654513/article/details/108353619

    标题作者背景描述:
    为什么写此系列文章?
    解决方案:
    预览Demo
    应用程序的架构
    下面是实操步骤:
    添加全局静态对象文件
    global.h文件
    main.cpp 添加静态资源文件支持:
    修改 requestmapper.cpp 文件
    修改配置文件BitPos.ini
    运行应用程序
    编译vue项目
    标题作者背景描述:
    本人就职于外资IT企业,担任电商订单处理产品开发经理一职,领导过非常多次大小项目的开发工作,对电商平台订单处理流程非常熟悉。

    公司专注鞋服行业相关软件开发和服务,公司规模100多人以上,在台北,广州,成都,上海,北京,国外等均有分公司。

    为什么写此系列文章?
    本人在学校至工作到现在十余年时间,使用.net C# 开发语言,结合在公司实际开发,和市场的需求中,NET.开发的商业企业级系统遇到的缺点有如下:

    程序首次加载慢,因为虚拟机编译的原因。
    WINFORM界面开发不够炫丽,精美。
    WINFORM界面设计人员难找。
    程序可以被反编译。
    安装包过大,部署麻烦,framework.
    跨平台不够好。
    解决方案:
    结合近年来前端设计的走向,最终选择了qt+vue+element UI+sqlite(数据库根据需要情况选择)

    qt负责接口和硬件处理

    sqlite做数据存储

    vue+element UI 实现前端。

    预览Demo


    应用程序的架构
    我看先看看要实现BitPos应用程序的架构:

    应用程序嵌套了google 浏览器内核,和http web 服务器.
    google 浏览器内核实现了前端UI,
    前端通过 http request 与后端C++代码进行将互。
    当然,代码经过小调整,重新编译,前后端也可以分开部署到任何类型的操作系统中。

    下面是实操步骤:
    打开上一节的项目源码:bitpos
    上一节的文章链接如下:
    https://blog.csdn.net/m0_49654513/article/details/108184324

    添加全局静态对象文件
    添加全局静态对象文件 global.cpp
    选中src 右键,添加新项:

    global.h文件
    如下:

    #pragma once
    #include <httpserver/staticfilecontroller.h>
    using namespace stefanfrings;

    /** Controller for static files */
    extern StaticFileController* staticFileController;

    1
    2
    3
    4
    5
    6
    7
    global.cpp 文件如下:

    #include "global.h"

    StaticFileController* staticFileController;

    1
    2
    3
    4
    main.cpp 添加静态资源文件支持:
    #include "src/global.h"
    1
    添加静态资源文件支持,如下:


    // Configure static file controller
    QSettings* fileSettings=new QSettings(configFileName,QSettings::IniFormat,&a);
    fileSettings->beginGroup("docroot");
    staticFileController=new StaticFileController(fileSettings,&a);

    修改访问地址如下:
    view.setUrl(QUrl("http://localhost:5050"));

    完整的main.cpp文件如下:
    #include "BitPos.h"
    #include <QtWidgets/QApplication>
    #include <QWebEngineView>
    #include <httpserver/httplistener.h>
    #include <logging/filelogger.h>
    #include <qdir.h>
    #include "src/requestmapper.h"
    #include "src/global.h"

    using namespace stefanfrings;

    /** Search the configuration file */
    QString searchConfigFile()
    {
    QString binDir = QCoreApplication::applicationDirPath();
    QString appName = QCoreApplication::applicationName();
    QString fileName(appName + ".ini");

    QStringList searchList;
    searchList.append(binDir);
    searchList.append(binDir + "/etc");
    searchList.append(binDir + "/../etc");
    searchList.append(binDir + "/../../etc"); // for development without shadow build
    searchList.append(binDir + "/../" + appName + "/etc"); // for development with shadow build
    searchList.append(binDir + "/../../" + appName + "/etc"); // for development with shadow build
    searchList.append(binDir + "/../../../" + appName + "/etc"); // for development with shadow build
    searchList.append(binDir + "/../../../../" + appName + "/etc"); // for development with shadow build
    searchList.append(binDir + "/../../../../../" + appName + "/etc"); // for development with shadow build
    searchList.append(QDir::rootPath() + "etc/opt");
    searchList.append(QDir::rootPath() + "etc");

    foreach(QString dir, searchList)
    {
    QFile file(dir + "/" + fileName);
    if (file.exists())
    {
    // found
    fileName = QDir(file.fileName()).canonicalPath();
    qDebug("Using config file %s", qPrintable(fileName));
    return fileName;
    }
    }

    // not found
    foreach(QString dir, searchList)
    {
    qWarning("%s/%s not found", qPrintable(dir), qPrintable(fileName));
    }
    qFatal("Cannot find config file %s", qPrintable(fileName));
    }

    int main(int argc, char* argv[])
    {
    QApplication a(argc, argv);
    // Find the configuration file
    QString configFileName = searchConfigFile();

    // Configure static file controller
    QSettings* fileSettings = new QSettings(configFileName, QSettings::IniFormat, &a);
    fileSettings->beginGroup("docroot");
    staticFileController = new StaticFileController(fileSettings, &a);

    // Configure and start the TCP listener
    QSettings* listenerSettings = new QSettings(configFileName, QSettings::IniFormat, &a);
    listenerSettings->beginGroup("listener");
    new HttpListener(listenerSettings, new RequestMapper(&a), &a);
    //浏览器
    QWebEngineView view;
    //设置访问地址
    //view.setUrl(QUrl("http://localhost:5050/vue-element-admin/api/Login?user_code=333&password=3445"));
    view.setUrl(QUrl("http://localhost:5050"));
    //显示浏览器窗口。
    view.show();
    return a.exec();
    }


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    修改 requestmapper.cpp 文件
    添加头文件:

    #include "global.h"
    1
    修改函数圈中的位置如下:


    完整函数如下:

    void RequestMapper::service(HttpRequest& request, HttpResponse& response)
    {
    QByteArray path=request.getPath().toLower();
    qDebug("RequestMapper: path=%s",path.data());
    fprintf(stderr, "request: %s ", path.data());
    //实现跨域访问,js 调用API 提供了支持。
    response.setHeader("Connection", "keep-alive");
    auto origin = request.getHeader("Origin");
    response.setHeader("Access-Control-Allow-Origin", origin);
    response.setHeader("Access-Control-Allow-Methods", "POST,GET,OPTIONS");
    response.setHeader("Access-Control-Allow-Headers", "X-PINGOTHER,Content-Type,x-token");
    response.setHeader("Access-Control-Max-Age", "86400");
    response.setHeader("Vary", "Accept-Encoding,Origin");
    response.setHeader("Keep-Alive", "timeout=2,max=99");
    //set api header
    response.setHeader("Content-Type", "application/json; charset=utf-8");
    //response.setHeader("Access-Control-Allow-Origin", "*"); // also important , if not set , the html application wont run.
    if (request.getMethod() == "OPTIONS")
    {
    response.setStatus(200,"OK");
    qDebug("RequestMapper: finished request");
    // Clear the log buffer

    return;
    }
    else
    {

    }

    // For the following pathes, each request gets its own new instance of the related controller.
    QByteArrayList items = path.split('/');
    QByteArray areaname;
    QByteArray controlname;
    QByteArray actionname;
    QByteArray a, b, c;

    for (int i = 0; i < items.length(); i++)
    {
    QByteArray first = items[i];
    if (first.isEmpty())
    continue;
    else
    {
    //get control and action of name.
    a = first;
    if(i+1<items.length())
    b = items[i + 1].toLower();

    if (i + 2 < items.length())
    c = items[i + 2].toLower();
    break;
    }
    }
    QList<QString> controls;
    //判断是否是路由。
    if (Area.contains(a))
    {
    areaname = a;
    controlname = b;
    actionname = c;
    controls=Area.values(a);
    }
    else
    {
    controlname = a;
    actionname = b;
    }

    QString className = (controlname + "Controller").toLower();

    int id = QMetaType::type(className.toLatin1());
    HttpRequestHandler* result = NULL;
    //判断area
    if (id != QMetaType::UnknownType)
    {
    if (controls.count() > 0 && !controls.contains(className))
    {

    staticFileController->service(request, response);
    qDebug() << "访问静态文件!";
    qDebug("RequestMapper: finished request");

    return;
    }
    }

    if (id != QMetaType::UnknownType)
    {
    result = static_cast<HttpRequestHandler*>(QMetaType::create(id));
    const QMetaObject * theMetaObject = result->metaObject();
    int nMetathodCount = theMetaObject->methodCount();
    QByteArray method;
    //查找方法
    for (int nMetathodIndex = 0; nMetathodIndex < nMetathodCount; nMetathodIndex++)
    {
    QByteArray oneMethod = theMetaObject->method(nMetathodIndex).name();
    if (actionname.compare(oneMethod, Qt::CaseSensitivity::CaseInsensitive)==0)
    {
    method = oneMethod;
    break;
    }
    }
    if (!method.isEmpty())
    {
    auto token=request.getHeader("X - Token");
    //判断token是否是可用。
    auto v = QMetaObject::invokeMethod(result, method.data(), Qt::DirectConnection,
    Q_ARG(HttpRequest &, request),
    Q_ARG(HttpResponse &, response));
    if (!v)
    qDebug() << method.data()<<" method invokeMethod is error!";
    }
    else
    {
    //不存在的方法。
    auto v = QMetaObject::invokeMethod(result, "ApiResult", Qt::DirectConnection,
    Q_ARG(const QString&, actionname+" action not found !"),
    Q_ARG(int, 1),
    Q_ARG(HttpRequest &, request),
    Q_ARG(HttpResponse &, response));
    if (!v)
    qDebug() << " service method invokeMethod is error!";
    }
    delete result;
    }
    else
    {
    staticFileController->service(request, response);
    qDebug() << "访问静态文件!";
    }

    qDebug("RequestMapper: finished request");

    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    修改配置文件BitPos.ini
    [docroot]
    path=docroot
    encoding=UTF-8
    maxAge=60000
    cacheTime=60000
    cacheSize=1000000
    maxCachedFileSize=65536

    1
    2
    3
    4
    5
    6
    7
    8
    静态资源目录为docroot
    我们在etc目录下添加一个index.html文件,测试显示html5能力,内容如下:

    <!DOCTYPE html>
    <html>
    <head lang="en">
    <meta charset="UTF-8">
    <title>这是一个例子</title>
    <script>

    function drawOneStar(ctx, i) {
    //i为五边形的边长
    ctx.beginPath();
    ctx.moveTo(0, i * Math.sin(Math.PI / 5));//B点
    ctx.lineTo(i * 2 * Math.cos(Math.PI / 5), i * Math.sin(Math.PI / 5));//BE
    ctx.lineTo(i * Math.cos(Math.PI / 5 * 2), (i * Math.sin(Math.PI / 5 * 2)) + i * Math.sin(Math.PI / 5));//EC
    ctx.lineTo(i * Math.cos(Math.PI / 5), 0);//CA
    ctx.lineTo(i + i * Math.cos(Math.PI / 5 * 2), (i * Math.sin(Math.PI / 5 * 2)) + i * Math.sin(Math.PI / 5));//AD
    ctx.lineTo(0, i * Math.sin(Math.PI / 5));//DB
    ctx.closePath();
    ctx.fillStyle = "red";
    ctx.fill();
    }
    function drawStar() {
    var canvas = document.getElementById("canvas");
    var ctx = canvas.getContext("2d");
    ctx.shadowBlur = 30;
    ctx.shadowColor = "red";
    ctx.translate(300, 600);
    ctx.rotate(-Math.PI / 8);
    drawOneStar(ctx, 50);
    for (var i = 0; i < 50; i++) {
    ctx.translate(70, -25);
    ctx.rotate(-Math.PI / 8);
    ctx.scale(0.95, 0.95);

    drawOneStar(ctx, 50);
    }

    }

    </script>
    </head>
    <body marginwidth="0px" marginheight="0px" onload="drawStar()">
    <canvas id="canvas" width="700px" height="800px" style="background-color:yellow"></canvas>
    </body>
    </html>


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    运行应用程序
    在vs2019 按ctrl+f5 运行效果非常酷,如下:


    编译vue项目
    我们把第一节打包出来的vuex项目放到docroot目录下,重新启动程序。要看第一节的教程,参考这里:
    https://blog.csdn.net/m0_49654513/article/details/108045868
    执行命令行cmd,切换到项目 vue01
    输入

    cd C:UsersAdministratorvue01
    1

    输入编译命令,编译vue项目 ,如下:

    npm run build dev
    1

    项目目录下出现dist 目录就是编译好的应用,我们进入目录,把访目录下的文档复制到docroot目录下,

    复制后:

    在vs2019按f5执行


    效果动画如下:
    源码下载:
    https://download.csdn.net/download/m0_49654513/12801482
    下一节,我们集成前端框架。

    索取源码,技术沟通,编译报错,请加QQ群561506606 加群无需验证。
    点击链接加入群聊【企业级系统实战-qt vue.j】:https://jq.qq.com/?_wv=1027&k=CCmkgYYu
    ————————————————
    版权声明:本文为CSDN博主「m0_49654513」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/m0_49654513/article/details/108353619

  • 相关阅读:
    【转载】STL之priority_queue
    数据结构作业——直通车(并查集)
    Codeforces Round #342 (Div. 2) D. Finals in arithmetic(想法题/构造题)
    Size Balance Tree(SBT模板整理)
    平衡二叉查找树(AVL)的理解与实现
    查找树ADT——二叉搜索树
    (转载)通过金矿模型介绍动态规划
    动态规划(DP)基础
    hdu 1969 Pie(二分查找)
    poj 3104 Drying(二分查找)
  • 原文地址:https://www.cnblogs.com/chinasoft/p/15010927.html
Copyright © 2020-2023  润新知