|
|
@@ -146,7 +146,66 @@ function savePreferences(config: DashboardPreferences) {
|
|
|
const preferences = reactive<DashboardPreferences>(normalizePreferences(Local.get(DASHBOARD_PREFERENCE_KEY)));
|
|
|
|
|
|
// ─── 时间筛选 ─────────────────────────────────────────────────
|
|
|
-const dashboardPeriod = shallowRef<S8Period | undefined>('this_month');
|
|
|
+// S8-FINAL-BLOCKERS-FIX-AND-TARGETED-CERT-1:
|
|
|
+// 原先这里写死 'this_month',而日期选择器由 createDefaultDateRange() 初始化为近 7 天,
|
|
|
+// 于是页面**首次渲染就自相矛盾**:芯片高亮「本月」,实际取数却按近 7 天。
|
|
|
+// 改为初始不选中任何预设——默认窗口仍由日期选择器决定(默认取数行为不变),
|
|
|
+// 但芯片不再显示一个与实际口径不符的高亮。
|
|
|
+const dashboardPeriod = shallowRef<S8Period | undefined>(undefined);
|
|
|
+
|
|
|
+/**
|
|
|
+ * 预设 → 日期选择器可表达的**闭区间** [start, end]。
|
|
|
+ *
|
|
|
+ * 语义严格镜像后端 `S8PeriodHelper.Resolve`(半开区间 [from, to)),
|
|
|
+ * 这里把 to 减一天换算成闭区间,保证「界面显示的范围」「请求里的 beginTime/endTime」
|
|
|
+ * 「后端按 period 自行解析的范围」三者一致。**不另造一套时间口径。**
|
|
|
+ *
|
|
|
+ * `last_24h` 返回 null:它是**跨小时**区间(now-24h ~ now),
|
|
|
+ * 日期粒度的选择器无法如实表达;此时清空选择器、只发 period,
|
|
|
+ * 由后端按其权威语义解析(后端契约:显式日期优先于 period,故清空是必要的)。
|
|
|
+ */
|
|
|
+function resolvePresetRange(period: S8Period | undefined): { start: Date; end: Date } | null {
|
|
|
+ if (!period) return null;
|
|
|
+ const today = new Date();
|
|
|
+ today.setHours(0, 0, 0, 0);
|
|
|
+ const day = (base: Date, offset: number) => {
|
|
|
+ const d = new Date(base);
|
|
|
+ d.setDate(d.getDate() + offset);
|
|
|
+ return d;
|
|
|
+ };
|
|
|
+ switch (period) {
|
|
|
+ case 'today':
|
|
|
+ return { start: today, end: today };
|
|
|
+ case 'this_week': {
|
|
|
+ // 与后端一致:周一为周起始
|
|
|
+ const offset = (today.getDay() - 1 + 7) % 7;
|
|
|
+ const monday = day(today, -offset);
|
|
|
+ return { start: monday, end: day(monday, 6) };
|
|
|
+ }
|
|
|
+ case 'this_month': {
|
|
|
+ const start = new Date(today.getFullYear(), today.getMonth(), 1);
|
|
|
+ return { start, end: new Date(today.getFullYear(), today.getMonth() + 1, 0) };
|
|
|
+ }
|
|
|
+ case 'this_quarter': {
|
|
|
+ const qStartMonth = Math.floor(today.getMonth() / 3) * 3;
|
|
|
+ const start = new Date(today.getFullYear(), qStartMonth, 1);
|
|
|
+ return { start, end: new Date(today.getFullYear(), qStartMonth + 3, 0) };
|
|
|
+ }
|
|
|
+ case 'last_7d':
|
|
|
+ return { start: day(today, -6), end: today };
|
|
|
+ case 'last_30d':
|
|
|
+ return { start: day(today, -29), end: today };
|
|
|
+ case 'last_24h':
|
|
|
+ return null; // 见上:交由后端按 period 解析
|
|
|
+ default:
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/** 用户手动改日期后,取消预设高亮——避免芯片继续标注一个已不成立的范围。 */
|
|
|
+function onManualDateChange() {
|
|
|
+ dashboardPeriod.value = undefined;
|
|
|
+}
|
|
|
|
|
|
// ─── 状态 ────────────────────────────────────────────────────
|
|
|
const loading = ref(false);
|
|
|
@@ -192,7 +251,11 @@ function severityTagType(s: string) {
|
|
|
return 'info';
|
|
|
}
|
|
|
function statusTagType(s: string) {
|
|
|
- return ({ NEW: 'info', ASSIGNED: '', IN_PROGRESS: 'warning', RESOLVED: 'success', CLOSED: 'success' } as any)[s] ?? 'info';
|
|
|
+ // S8-FINAL-BLOCKERS-FIX-AND-TARGETED-CERT-1:ASSIGNED 原映射为空字符串,
|
|
|
+ // 而 el-tag 的 type 只接受 primary/success/info/warning/danger,空串会触发
|
|
|
+ // 「Invalid prop: validation failed for prop "type"」告警(每渲染一条 ASSIGNED 行报 2 条)。
|
|
|
+ // 此前预设按钮失效、默认窗口取不到 ASSIGNED 行,该问题一直未暴露。
|
|
|
+ return ({ NEW: 'info', ASSIGNED: 'primary', IN_PROGRESS: 'warning', RESOLVED: 'success', CLOSED: 'success' } as any)[s] ?? 'info';
|
|
|
}
|
|
|
function fmtDate(v: string | null | undefined) {
|
|
|
return v ? new Date(v).toLocaleString('zh-CN', { hour12: false }) : '—';
|
|
|
@@ -568,7 +631,22 @@ function onDetailPageChange(page: number) {
|
|
|
void loadDetail();
|
|
|
}
|
|
|
|
|
|
-watch(dashboardPeriod, () => { void loadOverview(); void loadChartData(); void loadDimTrends(); });
|
|
|
+// S8-FINAL-BLOCKERS-FIX-AND-TARGETED-CERT-1:预设点击必须**真正改变时间范围**。
|
|
|
+// 修复前这里只重新取数,`period` 变了但 dateStart/dateEnd 仍是旧值;
|
|
|
+// 而请求同时携带二者、后端「显式日期优先于 period」,导致预设按钮点了等于没点。
|
|
|
+watch(dashboardPeriod, (period) => {
|
|
|
+ if (period) {
|
|
|
+ const range = resolvePresetRange(period);
|
|
|
+ const filter = currentPanelFilter.value;
|
|
|
+ // last_24h 无法用日期粒度表达 → 清空显式日期,交后端按 period 解析。
|
|
|
+ filter.dateStart = range ? range.start : null;
|
|
|
+ filter.dateEnd = range ? range.end : null;
|
|
|
+ }
|
|
|
+ void loadOverview();
|
|
|
+ void loadChartData();
|
|
|
+ void loadDimTrends();
|
|
|
+ void loadDetail();
|
|
|
+});
|
|
|
|
|
|
onMounted(() => void loadAll());
|
|
|
onBeforeUnmount(() => { mainChart?.dispose(); trendChart?.dispose(); });
|
|
|
@@ -608,6 +686,7 @@ onBeforeUnmount(() => { mainChart?.dispose(); trendChart?.dispose(); });
|
|
|
type="date"
|
|
|
placeholder="选择日期"
|
|
|
style="width:130px"
|
|
|
+ @change="onManualDateChange"
|
|
|
/>
|
|
|
</el-form-item>
|
|
|
<el-form-item label="结束日期">
|
|
|
@@ -616,6 +695,7 @@ onBeforeUnmount(() => { mainChart?.dispose(); trendChart?.dispose(); });
|
|
|
type="date"
|
|
|
placeholder="选择日期"
|
|
|
style="width:130px"
|
|
|
+ @change="onManualDateChange"
|
|
|
/>
|
|
|
</el-form-item>
|
|
|
<el-form-item v-if="activePanel === 'process'" label="流程节点">
|
|
|
@@ -669,9 +749,12 @@ onBeforeUnmount(() => { mainChart?.dispose(); trendChart?.dispose(); });
|
|
|
<el-empty description="暂无数据">
|
|
|
<template #description>
|
|
|
<p style="color:#64748b;font-size:13px">当前面板暂无数据</p>
|
|
|
+ <!-- S8-FINAL-BLOCKERS-FIX-AND-TARGETED-CERT-1:原文案「请先执行数据库初始化脚本…」
|
|
|
+ 是面向实施人员的内部操作指引,不应出现在业务用户界面。
|
|
|
+ 改为如实描述当前状态,不暴露 DB / 脚本 / 迁移等内部实现。 -->
|
|
|
<p v-if="activePanel === 'object' || activePanel === 'process'"
|
|
|
style="color:#94a3b8;font-size:12px;margin-top:4px">
|
|
|
- 请先执行数据库初始化脚本,为异常记录分配流程节点和关联对象
|
|
|
+ 当前时间范围内的异常尚无流程节点与关联对象信息
|
|
|
</p>
|
|
|
</template>
|
|
|
</el-empty>
|