This is an automated email from the ASF dual-hosted git repository. imbajin pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/hugegraph-toolchain.git
commit aafe65745cd1e5ebe6e43e295a3377b1425d087a Author: dark <[email protected]> AuthorDate: Fri Aug 14 12:39:02 2026 +0800 fix(hubble): improve result cards and graph labels --- .../src/i18n/resources/zh-CN/modules/analysis.json | 9 +++ .../src/i18n/resources/zh-CN/modules/pages.json | 52 +++++++++++-- .../src/modules/component/NumberCard/index.js | 86 +++++++++++++++++----- 3 files changed, 123 insertions(+), 24 deletions(-) diff --git a/hugegraph-hubble/hubble-fe/src/i18n/resources/zh-CN/modules/analysis.json b/hugegraph-hubble/hubble-fe/src/i18n/resources/zh-CN/modules/analysis.json index 13ddc2b95..8d681f628 100644 --- a/hugegraph-hubble/hubble-fe/src/i18n/resources/zh-CN/modules/analysis.json +++ b/hugegraph-hubble/hubble-fe/src/i18n/resources/zh-CN/modules/analysis.json @@ -80,6 +80,15 @@ "mode_2d": "2D 模式", "mode_3d": "3D 模式", "import_success": "导入成功", + "number_card": { + "paths": "路径", + "nodes": "节点", + "edges": "边", + "loading": "加载中", + "unavailable": "不可用", + "node_summary": "节点:当前结果 {{current}},全图 {{total}}", + "edge_summary": "边:当前结果 {{current}},全图 {{total}}" + }, "import_failed": "导入失败", "export_json": "导出 JSON", "export_png": "导出图片", diff --git a/hugegraph-hubble/hubble-fe/src/i18n/resources/zh-CN/modules/pages.json b/hugegraph-hubble/hubble-fe/src/i18n/resources/zh-CN/modules/pages.json index a80f7f0d7..93a5ae2a8 100644 --- a/hugegraph-hubble/hubble-fe/src/i18n/resources/zh-CN/modules/pages.json +++ b/hugegraph-hubble/hubble-fe/src/i18n/resources/zh-CN/modules/pages.json @@ -226,10 +226,10 @@ "clear_graph": "清空 Schema 与数据", "set_default": "设为默认", "view_schema": "查看schema", - "load_hlm_sample": "构建红楼梦 Demo 图", - "load_loader_sample": "构建人物与软件 Demo 图", + "load_hlm_sample": "红楼梦 Demo 图", + "load_loader_sample": "人物与软件 Demo 图", "sample_data": "示例数据与资源", - "load_rank_sample": "构建 Rank 迷你电影 Demo", + "load_rank_sample": "迷你电影 Rank Demo", "clone": "克隆图" }, "sample": { @@ -263,10 +263,18 @@ "nickname_placeholder": "可选,例如:电影推荐图", "nickname_help": "可选的显示别名或备注名,不参与 URL 或权限匹配;留空时显示图名称。可使用中文、字母、数字和下划线,最长 48 位", "schema": "Graph Schema", + "sample_hlm": "红楼梦 Demo 图", + "sample_rank": "迷你电影 Rank Demo", + "sample_loader": "人物与软件 Demo 图", + "sample_none": "不导入示例数据", "schema_placeholder": "选择已有 Schema 模板(可选)", "schema_load_failed": "Schema 模板暂时无法加载。", "schema_retry": "重试模板", "schema_optional_hint": "可选;不选择模板时将创建空 Schema 图", + "sample": "示例数据", + "sample_placeholder": "可选:创建图后自动导入一套示例数据", + "sample_hint": "选择后将创建图,并自动导入对应 Schema 和示例数据;若导入失败,可在已创建的图中重试", + "sample_apply_failed": "图已创建,但示例数据导入失败;可在图菜单中重试。", "schema_create": "点击创建 Graph Schema", "schema_create_failed": "内置 Graph Schema 创建失败,请检查模板列表后重试。", "schema_apply_failed": "图已创建,但内置 Schema 应用失败,请进入元数据页面重试。", @@ -337,6 +345,7 @@ "counts_tooltip": "来自图列表 API 的实际点边统计", "overview": "概览", "schema_info": "Schema 信息", + "view_schema": "查看 {{graph}} 的 Schema", "empty_schema": "尚未创建 Schema", "empty_schema_help": "可以先建立模型,或运行安全查询", "open_schema": "创建 Schema", @@ -548,7 +557,7 @@ "execute": "执行", "action": { "detail": "查看执行历史", - "config": "查看任务配置", + "config": "查看任务信息", "edit": "编辑任务", "pause": "暂停任务", "run": "运行任务", @@ -568,6 +577,18 @@ "realtime": "实时执行", "cron": "周期执行" }, + "status": { + "pending": "待执行", + "running": "运行中", + "success": "已完成", + "failed": "失败", + "paused": "已暂停", + "stopped": "已停止", + "initializing": "初始化中", + "cancelling": "正在取消", + "cancelled": "已取消", + "unknown": "未知状态" + }, "col": { "name": "任务名称", "source_type": "源数据类型", @@ -611,14 +632,21 @@ "other": "其它" }, "view": { - "title": "查看任务", + "title": "任务信息", "unavailable": "无法加载当前任务详情。", "retry": "重试任务详情", + "unavailable_value": "未提供", "name": "任务名称", "source_type": "源数据类型", "target_space": "目标图空间", "target_graph": "目标图", - "sync_type": "同步类型" + "sync_type": "同步类型", + "status": "状态", + "data_size": "数据量", + "duration": "持续时间", + "create_time": "创建时间", + "update_time": "更新时间", + "technical_details": "技术详情" }, "edit": { "name": "任务名称", @@ -1188,6 +1216,8 @@ "node_id_copied": "节点 ID 已复制", "node_id_copy_failed": "无法复制节点 ID", "leader_role": "Leader 角色", + "leader_shard": "{{count}} 个 Leader 分区", + "leader_shards": "{{count}} 个 Leader 分区", "node_profile": "节点概况", "node_detail": "节点详情", "node_metrics": "节点指标", @@ -1199,6 +1229,10 @@ "refresh": "刷新", "refreshing": "刷新中…", "stale": "数据已过期", + "stale_help": "指标采集未在最近一次观测窗口内成功完成;拓扑状态仍可用;请刷新或打开节点详情查看原因", + "status_unknown": "当前无法确认该来源的健康状态,请查看具体原因或刷新", + "status_degraded": "需关注", + "status_degraded_help": "部分来源或指标异常,核心服务可能仍可用;请查看下方来源原因", "unavailable": "不可用", "unsupported": "当前服务版本不支持", "metric_malformed": "指标响应格式异常", @@ -1244,6 +1278,7 @@ "search_nodes": "搜索节点名称", "node_type_filter": "按节点类型筛选", "node_status_filter": "按节点状态筛选", + "clear_filters": "清空筛选", "view_node_details": "查看 {{name}} 的详情", "view_details": "查看详情", "action": "操作", @@ -1270,6 +1305,11 @@ "metric_scope_backend": "后端指标由 Server 和 Store 节点提供,不会在 {{nodeType}} 节点采集。", "reason_upstream_timeout": "上游超时", "reason_upstream_unavailable": "上游不可用", + "reason_metrics_target_untrusted": "指标采集目标未通过信任校验,请检查 PD/Prometheus target 配置", + "reason_metrics_target_missing": "未发现 Store 指标采集目标,请检查 target 配置", + "reason_upstream_deadline": "指标采集超时", + "reason_metrics_not_collected": "当前节点未采集该指标", + "reason_topology_fields_unavailable": "拓扑未提供该字段", "reason_partial_refresh_failed": "部分指标刷新失败", "reason_refresh_failed": "刷新失败", "reason_malformed_response": "响应格式异常", diff --git a/hugegraph-hubble/hubble-fe/src/modules/component/NumberCard/index.js b/hugegraph-hubble/hubble-fe/src/modules/component/NumberCard/index.js index 607b2252d..cd0eb168e 100644 --- a/hugegraph-hubble/hubble-fe/src/modules/component/NumberCard/index.js +++ b/hugegraph-hubble/hubble-fe/src/modules/component/NumberCard/index.js @@ -23,18 +23,48 @@ import React from 'react'; import c from './index.module.scss'; import classnames from 'classnames'; +import {useTranslation} from 'react-i18next'; + +const isLoadingCount = value => { + if (value === null || value === undefined + || (typeof value === 'string' && value.trim() === '')) { + return false; + } + const count = Number(value); + return Number.isFinite(count) && count < 0; +}; + +const getCountDisplay = (value, t) => { + if (isLoadingCount(value)) { + return t('analysis.canvas.number_card.loading'); + } + if (value === null || value === undefined + || (typeof value === 'string' && value.trim() === '')) { + return t('analysis.canvas.number_card.unavailable'); + } + const count = Number(value); + return Number.isFinite(count) + ? value + : t('analysis.canvas.number_card.unavailable'); +}; const NumberCard = props => { + const {t} = useTranslation(); const {pathNum, data, hasPadding} = props; const {currentGraphNodesNum, currentGraphEdgesNum, allGraphNodesNum, allGraphEdgesNum} = data; - let showLoading = false; - if (currentGraphNodesNum < 0 || currentGraphEdgesNum < 0 || +allGraphNodesNum < 0 || +allGraphEdgesNum < 0) { - showLoading = true; - } - - const allGraphNodes = showLoading ? 'loading...' : allGraphNodesNum; - const allGraphEdges = showLoading ? 'loading...' : allGraphEdgesNum; + const currentGraphNodes = getCountDisplay(currentGraphNodesNum, t); + const currentGraphEdges = getCountDisplay(currentGraphEdgesNum, t); + const allGraphNodes = getCountDisplay(allGraphNodesNum, t); + const allGraphEdges = getCountDisplay(allGraphEdgesNum, t); + const nodeSummary = t('analysis.canvas.number_card.node_summary', { + current: currentGraphNodes, + total: allGraphNodes, + }); + const edgeSummary = t('analysis.canvas.number_card.edge_summary', { + current: currentGraphEdges, + total: allGraphEdges, + }); const graphClassName = classnames( c.numberCard, @@ -45,28 +75,48 @@ const NumberCard = props => { <div className={graphClassName}> {pathNum && ( <div className={c.numberCardItem}> - <div className={c.numberCardTitle}>Paths</div> + <div className={c.numberCardTitle}> + {t('analysis.canvas.number_card.paths')} + </div> <div className={c.numberCardInfo}> <span className={c.numberCur}>{pathNum}</span> </div> </div> )} <div className={c.numberCardItem}> - <div className={c.numberCardTitle}>Nodes</div> - <div className={c.numberCardInfo}> - <span className={c.numberCur}>{currentGraphNodesNum}</span> - <span>/</span> - <span className={c.numberAll}> + <div className={c.numberCardTitle}> + {t('analysis.canvas.number_card.nodes')} + </div> + <div + className={c.numberCardInfo} + role='group' + aria-label={nodeSummary} + title={nodeSummary} + > + <span className={c.numberCur} aria-hidden='true'> + {currentGraphNodes} + </span> + <span aria-hidden='true'>/</span> + <span className={c.numberAll} aria-hidden='true'> {allGraphNodes} </span> </div> </div> <div className={c.numberCardItem}> - <div className={c.numberCardTitle}>Edges</div> - <div className={c.numberCardInfo}> - <span className={c.numberCur}>{currentGraphEdgesNum}</span> - <span>/</span> - <span className={c.numberAll}> + <div className={c.numberCardTitle}> + {t('analysis.canvas.number_card.edges')} + </div> + <div + className={c.numberCardInfo} + role='group' + aria-label={edgeSummary} + title={edgeSummary} + > + <span className={c.numberCur} aria-hidden='true'> + {currentGraphEdges} + </span> + <span aria-hidden='true'>/</span> + <span className={c.numberAll} aria-hidden='true'> {allGraphEdges} </span> </div>
