feat/vtk-3d-view #7
|
|
@ -64,6 +64,7 @@ add_executable(geopro_desktop WIN32
|
|||
panels/chart/BarChartView.cpp
|
||||
panels/chart/LineChartView.cpp
|
||||
panels/chart/TrajectoryMapView.cpp
|
||||
panels/web/ProjectWebView.cpp
|
||||
panels/chart/DetailViewFactory.cpp
|
||||
resources/map/map.qrc
|
||||
resources/keys.qrc
|
||||
|
|
|
|||
|
|
@ -119,48 +119,6 @@ QPixmap renderAvatar(const QString& initials, int px, const QColor& bg, const QC
|
|||
}
|
||||
|
||||
// ── 四个菜单(结构对齐需求;叶子项当前为静态占位,后续接真实页面)──
|
||||
QMenu* buildViewMenu(QWidget* p)
|
||||
{
|
||||
auto* m = new QMenu(QStringLiteral("视图"), p);
|
||||
m->addAction(QStringLiteral("分析视图"));
|
||||
m->addAction(QStringLiteral("大屏视图"));
|
||||
return m;
|
||||
}
|
||||
|
||||
QMenu* buildProjectMenu(QWidget* p)
|
||||
{
|
||||
auto* m = new QMenu(QStringLiteral("项目管理"), p);
|
||||
m->addAction(QStringLiteral("数据视图"));
|
||||
auto* cfg = m->addMenu(QStringLiteral("项目配置"));
|
||||
cfg->addAction(QStringLiteral("基本信息"));
|
||||
cfg->addAction(QStringLiteral("项目结构"));
|
||||
cfg->addAction(QStringLiteral("视图配置"));
|
||||
m->addAction(QStringLiteral("数据管理"));
|
||||
auto* biz = m->addMenu(QStringLiteral("业务管理"));
|
||||
biz->addAction(QStringLiteral("异常管理"));
|
||||
biz->addAction(QStringLiteral("异常体管理"));
|
||||
auto* mon = m->addMenu(QStringLiteral("在线监测"));
|
||||
mon->addAction(QStringLiteral("项目设备"));
|
||||
mon->addAction(QStringLiteral("在线任务管理"));
|
||||
auto* doc = m->addMenu(QStringLiteral("项目资料管理"));
|
||||
doc->addAction(QStringLiteral("项目资料管理"));
|
||||
doc->addAction(QStringLiteral("报告列表"));
|
||||
auto* tools = m->addMenu(QStringLiteral("工具组件"));
|
||||
tools->addAction(QStringLiteral("装置与脚本"));
|
||||
tools->addAction(QStringLiteral("色阶配置"));
|
||||
tools->addAction(QStringLiteral("异常类型管理"));
|
||||
tools->addAction(QStringLiteral("模型管理"));
|
||||
auto* exp = m->addMenu(QStringLiteral("批量导出"));
|
||||
exp->addAction(QStringLiteral("文件导出"));
|
||||
exp->addAction(QStringLiteral("报告导出"));
|
||||
auto* alarm = m->addMenu(QStringLiteral("告警管理"));
|
||||
alarm->addAction(QStringLiteral("设备告警"));
|
||||
alarm->addAction(QStringLiteral("告警查询"));
|
||||
m->addAction(QStringLiteral("自动任务"));
|
||||
m->addAction(QStringLiteral("模板管理"));
|
||||
return m;
|
||||
}
|
||||
|
||||
QMenu* buildToolsMenu(QWidget* p)
|
||||
{
|
||||
auto* m = new QMenu(QStringLiteral("业务工具"), p);
|
||||
|
|
@ -259,8 +217,8 @@ TopBar::TopBar(QWidget* parent) : QWidget(parent) {
|
|||
|
||||
// 一级菜单 → 工具条按钮(视图/项目管理/业务工具/设备),纯文字 + 下拉箭头。
|
||||
// 复用原菜单构造器;菜单作为 popup 挂到按钮(按钮文字取菜单标题)。
|
||||
lay->addWidget(makeMenuButton(this, buildViewMenu(this)));
|
||||
lay->addWidget(makeMenuButton(this, buildProjectMenu(this)));
|
||||
lay->addWidget(makeMenuButton(this, buildViewMenu()));
|
||||
lay->addWidget(makeMenuButton(this, buildProjectMenu()));
|
||||
lay->addWidget(makeMenuButton(this, buildToolsMenu(this)));
|
||||
lay->addWidget(makeMenuButton(this, buildDeviceMenu(this)));
|
||||
|
||||
|
|
@ -332,6 +290,49 @@ TopBar::TopBar(QWidget* parent) : QWidget(parent) {
|
|||
lay->addWidget(userRow_);
|
||||
}
|
||||
|
||||
// 视图菜单。「分析视图」=默认工作台(emit analysisViewRequested,中央区从 web 整窗切回工作台);
|
||||
// 「大屏视图」当前为占位。
|
||||
QMenu* TopBar::buildViewMenu() {
|
||||
auto* m = new QMenu(QStringLiteral("视图"), this);
|
||||
QObject::connect(m->addAction(QStringLiteral("分析视图")), &QAction::triggered, this,
|
||||
[this] { emit analysisViewRequested(); });
|
||||
m->addAction(QStringLiteral("大屏视图"));
|
||||
return m;
|
||||
}
|
||||
|
||||
// 项目管理菜单。仅保留需「直接嵌入」的 web 页(Excel「单个项目」页签第 10~21 行带嵌入地址者):
|
||||
// 在线监测 / 工具组件 / 批量导出 / 告警管理,点击 emit webPageRequested,由 main 独立整窗加载。
|
||||
// 其余全部隐藏(数据视图、项目配置、数据管理、业务管理、项目资料管理、自动任务、模板管理 …)。
|
||||
QMenu* TopBar::buildProjectMenu() {
|
||||
auto* m = new QMenu(QStringLiteral("项目管理"), this);
|
||||
// web 叶子项:携带 target 路径,点击发信号。
|
||||
auto addWeb = [this](QMenu* parent, const QString& title, const QString& target) {
|
||||
auto* a = parent->addAction(title);
|
||||
QObject::connect(a, &QAction::triggered, this,
|
||||
[this, title, target] { emit webPageRequested(title, target); });
|
||||
};
|
||||
|
||||
auto* mon = m->addMenu(QStringLiteral("在线监测"));
|
||||
addWeb(mon, QStringLiteral("项目设备"), QStringLiteral("/projectSpace/onlineMonitor/projectDevice"));
|
||||
addWeb(mon, QStringLiteral("在线任务管理"), QStringLiteral("/projectSpace/onlineMonitor/onlineTask"));
|
||||
|
||||
auto* tools = m->addMenu(QStringLiteral("工具组件"));
|
||||
addWeb(tools, QStringLiteral("装置与脚本"), QStringLiteral("/projectSpace/toolComponent/deviceScript"));
|
||||
addWeb(tools, QStringLiteral("色阶配置"), QStringLiteral("/projectSpace/toolComponent/levelConfigure"));
|
||||
addWeb(tools, QStringLiteral("异常类型管理"), QStringLiteral("/projectSpace/toolComponent/exceptionType"));
|
||||
addWeb(tools, QStringLiteral("模型管理"), QStringLiteral("/projectSpace/toolComponent/modelManage"));
|
||||
|
||||
auto* exp = m->addMenu(QStringLiteral("批量导出"));
|
||||
addWeb(exp, QStringLiteral("文件导出"), QStringLiteral("/projectSpace/bulkExport/fileExport"));
|
||||
addWeb(exp, QStringLiteral("报告导出"), QStringLiteral("/projectSpace/bulkExport/templateExport"));
|
||||
|
||||
auto* alarm = m->addMenu(QStringLiteral("告警管理"));
|
||||
addWeb(alarm, QStringLiteral("设备告警"), QStringLiteral("/projectSpace/alarmManage/deviceAlarm"));
|
||||
addWeb(alarm, QStringLiteral("告警查询"), QStringLiteral("/projectSpace/alarmManage/alarmQuery"));
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
bool TopBar::eventFilter(QObject* obj, QEvent* event) {
|
||||
if (obj == userRow_ && event->type() == QEvent::MouseButtonRelease) {
|
||||
if (userMenu_)
|
||||
|
|
|
|||
|
|
@ -29,8 +29,14 @@ signals:
|
|||
void allProjectsRequested(); // 点击"全部项目…"
|
||||
void logoutRequested(); // 头像菜单「退出登录」
|
||||
void settingsRequested(); // 点击齿轮图标 → 打开设置
|
||||
// 项目管理菜单中「直接嵌入」的 web 页被点击:title=窗口标题,target=嵌入页 target 路径。
|
||||
void webPageRequested(const QString& title, const QString& target);
|
||||
void analysisViewRequested(); // 视图菜单「分析视图」→ 中央区切回默认工作台
|
||||
|
||||
private:
|
||||
QMenu* buildViewMenu(); // 视图菜单(成员:「分析视图」需 emit 信号)
|
||||
QMenu* buildProjectMenu(); // 项目管理菜单(成员:webview 叶子项需 emit 信号)
|
||||
|
||||
QToolButton* wsBtn_ = nullptr;
|
||||
QToolButton* projBtn_ = nullptr;
|
||||
QWidget* userRow_ = nullptr; // 用户区整块(头像+姓名/职务+箭头)
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@
|
|||
#include <QStringList>
|
||||
#include <QTabWidget>
|
||||
#include <QMainWindow>
|
||||
#include <QStackedWidget>
|
||||
#include <QStatusBar>
|
||||
#include <QStyle>
|
||||
#include <QSurfaceFormat>
|
||||
|
|
@ -113,6 +114,7 @@
|
|||
#include "ProjectListDialog.hpp"
|
||||
#include "ObjectFormDialog.hpp"
|
||||
#include "ImportDatasetDialog.hpp"
|
||||
#include "panels/web/ProjectWebView.hpp"
|
||||
#include "WorkbenchNavController.hpp"
|
||||
#include "VtkSceneController.hpp"
|
||||
#include "VtkSceneView.hpp"
|
||||
|
|
@ -252,7 +254,8 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
|||
geopro::data::IColorTemplateRepository& colorTplRepo,
|
||||
geopro::data::IDatasetCommandRepository& cmdRepo,
|
||||
geopro::controller::WorkbenchNavController& nav,
|
||||
geopro::controller::DatasetDetailController& detailCtrl)
|
||||
geopro::controller::DatasetDetailController& detailCtrl,
|
||||
const QString& sessionToken)
|
||||
{
|
||||
// ── 世界系:启动取一次 grid1 的 lat/lon,用中位数作 GeoLocalFrame 原点 ──
|
||||
// 全项目共享(shared_ptr 持有):所有帘面用同一 frame 投影,保证多条测线空间配准。
|
||||
|
|
@ -325,7 +328,11 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
|||
ads::CDockManager::setAutoHideConfigFlags(ads::CDockManager::AutoHideFlags()); // 禁用自动隐藏
|
||||
|
||||
auto* dockManager = new ads::CDockManager(&window);
|
||||
window.setCentralWidget(dockManager);
|
||||
// 中央区用 QStackedWidget 承载:page0=工作台(dockManager,默认「分析视图」),
|
||||
// page1=项目管理 web 页(点项目管理菜单整窗加载,视图菜单「分析视图」切回 page0)。
|
||||
auto* centralStack = new QStackedWidget(&window);
|
||||
centralStack->addWidget(dockManager); // index 0:工作台
|
||||
window.setCentralWidget(centralStack);
|
||||
|
||||
// 覆盖 ADS 自带分隔条样式:其 default.css 用 palette(dark) 把面板间分隔画成深灰粗线,
|
||||
// 且设在管理器自身、选择器更具体(优先级高于全局主题)。在其后追加同选择器、按主题着色的极淡分隔覆盖。
|
||||
|
|
@ -1239,6 +1246,11 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
|||
vtkDock->setWidget(centerWidget);
|
||||
auto* centerDockArea = dockManager->addDockWidget(ads::CenterDockWidgetArea, vtkDock);
|
||||
|
||||
// 项目管理「直接嵌入」web 页:作为中央 QStackedWidget 的第二页,整窗加载(覆盖整个工作台)。
|
||||
// 单实例复用——点不同菜单项时重新 load;token 已注入页面 localStorage。
|
||||
auto* projectWebView = new geopro::app::ProjectWebView(sessionToken);
|
||||
centralStack->addWidget(projectWebView); // index 1:项目管理 web 整窗
|
||||
|
||||
// ── 下方「数据详情」dock:平面图表多 Tab 面板(QGraphicsView,VTK 仅算几何)──
|
||||
// 单击数据集 → 聚焦已开页;双击 → 新建/聚焦页(真实反演剖面/散点/异常/色阶)。
|
||||
auto* detailPanel = new geopro::app::DatasetDetailPanel();
|
||||
|
|
@ -1488,6 +1500,24 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
|||
QObject::connect(topBar, &geopro::app::TopBar::workspaceSwitchRequested, &nav,
|
||||
&geopro::controller::WorkbenchNavController::switchWorkspace);
|
||||
|
||||
// 项目管理「直接嵌入」web 页:拼当前项目的嵌入 URL,在中央区整窗加载(切到 web 页)。
|
||||
// space=3 为「项目空间(projectSpace)」固定常量——Excel 所有 projectSpace 页均 space=3,
|
||||
// 与租户/工作空间 id 无关(误用工作空间 snowflake 会被后端拒:space 参数无效)。
|
||||
QObject::connect(
|
||||
topBar, &geopro::app::TopBar::webPageRequested, &window,
|
||||
[projectWebView, centralStack, &nav](const QString& /*title*/, const QString& target) {
|
||||
const QString url =
|
||||
QStringLiteral("http://tenant.geomative.cn/#/embed?space=3&projectId=%1&target=%2")
|
||||
.arg(nav.currentProjectId(), target);
|
||||
projectWebView->load(url);
|
||||
centralStack->setCurrentWidget(projectWebView); // 整窗切到 web 页
|
||||
});
|
||||
// 视图菜单「分析视图」:中央区切回工作台(默认视图)。
|
||||
QObject::connect(topBar, &geopro::app::TopBar::analysisViewRequested, &window,
|
||||
[centralStack, dockManager]() {
|
||||
centralStack->setCurrentWidget(dockManager);
|
||||
});
|
||||
|
||||
// 切换到「不同项目」时先清空中央区,避免新项目残留旧项目的三栏数据与 VTK 渲染。
|
||||
// 仅真正换项目用(delete-refresh 等 switchProject(currentProjectId) 不走此处,避免误清)。
|
||||
auto clearCentral = [sceneCtrl, emptyState, checkedProfiles, checkedAnalysis,
|
||||
|
|
@ -2207,7 +2237,7 @@ int main(int argc, char* argv[])
|
|||
// 而是给出可见错误提示。否则登录后窗口未显示、进程消失,用户无从排查。
|
||||
try {
|
||||
buildWorkbench(*window, repo, projectRepo, datasetRepo, colorTplRepo, cmdRepo, nav,
|
||||
detailCtrl);
|
||||
detailCtrl, token);
|
||||
} catch (const std::exception& e) {
|
||||
QMessageBox::critical(
|
||||
nullptr, QStringLiteral("启动失败"),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,63 @@
|
|||
#include "panels/web/ProjectWebView.hpp"
|
||||
|
||||
#include <QUrl>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWebEnginePage>
|
||||
#include <QWebEngineScript>
|
||||
#include <QWebEngineScriptCollection>
|
||||
#include <QWebEngineView>
|
||||
|
||||
namespace geopro::app {
|
||||
|
||||
namespace {
|
||||
|
||||
// 把字符串转成安全的 JS 字面量(带引号、转义),用于拼进注入脚本。
|
||||
QString jsStringLiteral(const QString& s) {
|
||||
// QJsonValue::toJson 不直接给单值字符串;手工转义足够(token 仅含 base64/空格)。
|
||||
QString out;
|
||||
out.reserve(s.size() + 2);
|
||||
out += QLatin1Char('"');
|
||||
for (const QChar c : s) {
|
||||
switch (c.unicode()) {
|
||||
case '\\': out += QStringLiteral("\\\\"); break;
|
||||
case '"': out += QStringLiteral("\\\""); break;
|
||||
case '\n': out += QStringLiteral("\\n"); break;
|
||||
case '\r': out += QStringLiteral("\\r"); break;
|
||||
case '\t': out += QStringLiteral("\\t"); break;
|
||||
default: out += c; break;
|
||||
}
|
||||
}
|
||||
out += QLatin1Char('"');
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ProjectWebView::ProjectWebView(const QString& token, QWidget* parent) : QWidget(parent) {
|
||||
auto* lay = new QVBoxLayout(this);
|
||||
lay->setContentsMargins(0, 0, 0, 0);
|
||||
lay->setSpacing(0);
|
||||
|
||||
view_ = new QWebEngineView(this);
|
||||
lay->addWidget(view_, 1);
|
||||
|
||||
// token 注入:DocumentCreation 阶段把登录 token 写入 localStorage["token"],
|
||||
// 早于嵌入页 SPA 启动脚本,保证其读取鉴权时已就绪。每次 load 都会重新执行。
|
||||
if (!token.isEmpty()) {
|
||||
QWebEngineScript script;
|
||||
script.setName(QStringLiteral("inject-geopro-token"));
|
||||
script.setInjectionPoint(QWebEngineScript::DocumentCreation);
|
||||
script.setWorldId(QWebEngineScript::MainWorld);
|
||||
script.setRunsOnSubFrames(true);
|
||||
script.setSourceCode(
|
||||
QStringLiteral("try{localStorage.setItem('token', %1);}catch(e){}")
|
||||
.arg(jsStringLiteral(token)));
|
||||
view_->page()->scripts().insert(script);
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectWebView::load(const QString& url) {
|
||||
view_->load(QUrl(url));
|
||||
}
|
||||
|
||||
} // namespace geopro::app
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
#pragma once
|
||||
#include <QString>
|
||||
#include <QWidget>
|
||||
|
||||
class QWebEngineView;
|
||||
|
||||
namespace geopro::app {
|
||||
|
||||
// 项目管理 webview 宿主:内嵌 QWebEngineView,承载需「直接嵌入」的 web 管理页
|
||||
// (在线监测 / 工具组件 / 批量导出 / 告警管理)。
|
||||
// 构造期注入 DocumentCreation 脚本,把登录 token 写入页面 localStorage["token"],
|
||||
// 早于页面自身脚本执行,确保 web 端读取鉴权时已就绪。
|
||||
class ProjectWebView : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ProjectWebView(const QString& token, QWidget* parent = nullptr);
|
||||
|
||||
// 加载嵌入页(完整 URL,含 #/embed?space=..&projectId=..&target=..)。
|
||||
void load(const QString& url);
|
||||
|
||||
private:
|
||||
QWebEngineView* view_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace geopro::app
|
||||
Loading…
Reference in New Issue