topfans/docs/superpowers/plans/2026-06-08-income-curve-default-tooltip.md
2026-06-09 00:37:42 +08:00

12 KiB
Raw Blame History

IncomeCurve.vue Default Tooltip Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Make IncomeCurve.vue (七日收益曲线) show its tooltip on the last data point by default after first render, while preserving the existing tap-to-switch behavior.

Architecture: Listen to qiun-data-charts's @complete event, read the last data point's actual screen coordinates from opts.chartData.calPoints[0][last], construct a fake touch event, and invoke the chart component's private _showTooltip(e) to render the default tooltip. Guard against re-firing and missing data with a lastShownLen cursor.

Tech Stack: Vue 3 Composition API, uni-app (vite), qiun-data-charts 1.5.x (u-charts under the hood)

Spec: docs/superpowers/specs/2026-06-08-income-curve-default-tooltip-design.md


File Structure

Modified (1 file):

  • frontend/pages/dashboard/components/IncomeCurve.vue — single component; add chartRef, onChartComplete handler, lastShownLen cursor; remove dead code (currentIndex, its watch, onChartTap, @tap binding)

Created (0 files):

No new files. No new dependencies. No new test files (project has no frontend test framework — uni-app + HBuilderX/WeChat DevTools manual verification, consistent with the rest of the codebase).


Task 1: Add chartRef and onChartComplete handler (minimal, no behavior change yet)

Files:

  • Modify: frontend/pages/dashboard/components/IncomeCurve.vue:1-78

Why first: Establishes the new state without changing visible behavior. Lets us verify the ref wiring works before adding the actual _showTooltip call.

  • Step 1: Add chartRef and lastShownLen to the script block

Open frontend/pages/dashboard/components/IncomeCurve.vue and replace the existing import line (line 43):

import { computed, ref, watch } from "vue";

with:

import { computed, nextTick, ref, watch } from "vue";

Then immediately after defineEmits(["retry"]); (line 52), insert two new refs:

// [新增] 拿到 qiun-data-charts 实例,用于首次渲染后触发默认 tooltip
const chartRef = ref(null);
// [新增] 上次已展示 tooltip 的数据点数量,避免重复触发
const lastShownLen = ref(0);
  • Step 2: Bind the ref on the chart component

In the template <qiun-data-charts ...> opening tag (line 23), add ref="chartRef" as the first attribute:

<qiun-data-charts
  ref="chartRef"
  type="area"
  :opts="chartOpts"
  :chartData="chartData"
  :ontouch="true"
  :onmovetip="true"
  :in-scroll-view="true"
  :tooltipShow="true"
  :canvas2d="false"
  canvasId="incomeCurveCanvas"
  :canvasHeight="240"
  ontap
  @tap="onChartTap"
/>
  • Step 3: Add a placeholder onChartComplete handler (no behavior yet)

In the script block, after the two new refs added in Step 1, insert this empty placeholder:

// [新增] 图表首次渲染完成回调占位Task 2 填充真实逻辑)
const onChartComplete = (_e) => {
  // 故意留空 — Task 2 接入 fake event 触发
};

Also bind it on the template — change line 35 from @tap="onChartTap" to:

  ontap
  @tap="onChartTap"
  @complete="onChartComplete"
/>
  • Step 4: Verify the chart still renders normally

Run H5 preview (HBuilderX → 运行 → 运行到浏览器 → Chrome) or the dev server:

cd frontend && npm run dev:h5

Expected: dashboard page loads; chart renders the 7-day curve; no console errors. Touching a point still updates tooltip as before. The placeholder handler is a no-op, so visible behavior is unchanged.

  • Step 5: Commit
git add frontend/pages/dashboard/components/IncomeCurve.vue
git commit -m "feat(income-curve): wire chartRef + onChartComplete placeholder"

Task 2: Implement the default tooltip trigger

Files:

  • Modify: frontend/pages/dashboard/components/IncomeCurve.vue (replace the placeholder from Task 1)

  • Step 1: Replace the placeholder onChartComplete with the real implementation

Find the placeholder block added in Task 1 Step 3:

// [新增] 图表首次渲染完成回调占位Task 2 填充真实逻辑)
const onChartComplete = (_e) => {
  // 故意留空 — Task 2 接入 fake event 触发
};

Replace it with:

// [新增] 图表渲染完成回调:
//   从 e.opts.chartData.calPoints[0] 拿最后一点的真实屏幕坐标,
//   构造 fake event 喂给 _showTooltip让 u-charts 在最后一点渲染 tooltip
//   ⚠️ _showTooltip 是 qiun-data-charts 私有方法(下划线开头)
//   风险与回退见 spec: "风险与回退" 章节
const onChartComplete = async (e) => {
  const len = props.points.length;
  if (!len || !chartRef.value) return;
  // 数据点数量未变化则不重复触发(防 complete 事件多次触发)
  if (lastShownLen.value === len) return;
  await nextTick();
  const series = e?.opts?.chartData?.calPoints?.[0];
  if (!series || !series[len - 1]) return;
  const { x, y } = series[len - 1];
  const fakeE = { changedTouches: [{ x, y }] };
  try {
    chartRef.value._showTooltip(fakeE);
  } catch (err) {
    console.warn("[IncomeCurve] show default tooltip failed:", err);
  }
  lastShownLen.value = len;
};
  • Step 2: Verify default tooltip shows on last point

Reload the dashboard page in H5 preview (HBuilderX). Watch the income-curve chart.

Expected: Within ~1 frame after the chart's curve animates in, a tooltip appears above the rightmost data point showing +<income> (e.g. +1234) and MM-DD (e.g. 06-08). No hollow dot should appear (u-charts draws the active point only after a real touch).

  • Step 3: Verify tap-to-switch still works

On the same chart, tap a point in the middle (e.g. day 4 from the left).

Expected: Tooltip moves to that touched point. Tap the rightmost point → tooltip returns to the rightmost. Tap empty space below the curve → tooltip should disappear (u-charts default behavior). All these should work as before, because the chart's internal _tap handler is untouched.

  • Step 4: Verify guard against repeated complete events

Without leaving the page, trigger a re-render (e.g. dev tools → toggle a points prop in Vue devtools, or just resize the window — u-charts emits complete on resize).

Expected: Tooltip stays on the rightmost point; no flicker / no duplicate tooltip drawn. Open devtools console; no errors.

  • Step 5: Verify empty-data fallback

Temporarily change the parent (or use dev tools) to pass points: [].

Expected: Component enters skeleton state (the v-else-if="loading || !points || points.length === 0" branch); the qiun-data-charts is not mounted, so onChartComplete is never called; no console errors.

Restore the data after verification.

  • Step 6: Commit
git add frontend/pages/dashboard/components/IncomeCurve.vue
git commit -m "feat(income-curve): show default tooltip on last data point"

Task 3: Remove dead code (currentIndex, watch, onChartTap, @tap binding)

Files:

  • Modify: frontend/pages/dashboard/components/IncomeCurve.vue (script + template)

Why: currentIndex ref and its watcher are unused (u-charts feeds the actual touch index to tooltipCustom directly, not from the parent). onChartTap is a dead handler. Cleaning these up keeps the component lean and prevents future readers from being misled.

  • Step 1: Remove the currentIndex ref + watcher + onChartTap function

In the script block, find and delete these lines (originally around line 54-69 of the unmodified file):

// 当前选中的数据点索引默认指向最后一条tap 时更新
const currentIndex = ref(0);
watch(
  () => props.points.length,
  (len) => {
    currentIndex.value = Math.max(0, len - 1);
  },
  { immediate: true },
);

const onChartTap = (e) => {
  const idx = e?.index;
  if (typeof idx === "number" && idx >= 0 && idx < props.points.length) {
    currentIndex.value = idx;
  }
};
  • Step 2: Remove watch from the import line (still need nextTick + ref)

Change:

import { computed, nextTick, ref, watch } from "vue";

to:

import { computed, nextTick, ref } from "vue";

(watch is no longer used after Step 1.)

  • Step 3: Remove @tap="onChartTap" from the template

In the <qiun-data-charts> tag, find:

  ontap
  @tap="onChartTap"
  @complete="onChartComplete"
/>

Change to:

  ontap
  @complete="onChartComplete"
/>

(ontap (no value) is the native event binding that lets u-charts' internal _tap still handle real touches; we just drop the parent-side listener.)

  • Step 4: Verify nothing regressed

Reload dashboard in H5 preview.

Expected:

  • Tooltip still shows on the rightmost point by default

  • Tapping any other point still moves the tooltip to that point

  • No console errors

  • No unused-variable warnings (if eslint is configured — check with cd frontend && npx eslint pages/dashboard/components/IncomeCurve.vue 2>/dev/null || echo "no eslint config"; project has no eslint, so this is just a no-op check)

  • Step 5: Commit

git add frontend/pages/dashboard/components/IncomeCurve.vue
git commit -m "refactor(income-curve): remove dead currentIndex/onChartTap"

Task 4: Cross-platform smoke test (WeChat mini-program / H5 / App)

Files: none

Why: The fake event and _showTooltip rely on canvas event behavior that can differ across uni-app's compile targets. Verify before declaring done.

  • Step 1: Build for WeChat mini-program and verify

In HBuilderX: 发行 → 微信小程序(仅编译,不上传).

Open the built unpackage/dist/dev/mp-weixin/ in WeChat DevTools. Navigate to the dashboard page.

Expected: Same as H5 — tooltip on rightmost by default, tap-to-switch works, no errors in WeChat console.

  • Step 2: If an App build is in use, verify there too

In HBuilderX: 运行 → 运行到 App 基座.

Expected: Same behavior. (Skip this step if App build is not part of the dev workflow for this project.)

  • Step 3: Document any platform-specific quirk

If you observe different behavior on any platform, add a one-line note in the spec's "跨端验证" section and a defensive comment in IncomeCurve.vue near the _showTooltip call. If all platforms pass, no action needed.

  • Step 4: Final commit (if any notes were added)
git add frontend/pages/dashboard/components/IncomeCurve.vue docs/superpowers/specs/2026-06-08-income-curve-default-tooltip-design.md
git commit -m "docs(income-curve): note cross-platform verification results"

Self-Review

1. Spec coverage:

Spec section Task
核心机制 (calPoints + fake event) Task 1 + Task 2
模板改动 (ref + @complete) Task 1 Steps 2-3
脚本改动 (onChartComplete + lastShownLen) Task 1 Step 1, Task 2 Step 1
关键边界条件 (空数据 / calPoints 未填充 / 多次 complete / 私有方法不可用) Task 2 Step 5 (empty) + Task 2 Step 4 (re-entry) + Task 2 Step 1 (try/catch + 守卫)
模板清理 (@tap="onChartTap" 移除) Task 3 Step 3
死代码清理 (currentIndex, watch, onChartTap) Task 3 Steps 1-2
手动验证 (默认显示 / 触摸切换 / 数据切换) Task 2 Steps 2-3
空态/错误态 Task 2 Step 5
跨端验证 Task 4
风险与回退 Implicit in code (try/catch + 守卫); not a separate task

2. Placeholder scan:

  • "verify" steps: each has explicit Expected output
  • No "TBD" / "TODO" / "implement later"
  • All code blocks contain complete code (no "// similar to..." shortcuts)

3. Type / naming consistency:

  • chartRef — declared Task 1 Step 1, used Task 1 Step 2 (template) + Task 2 Step 1 (script).
  • lastShownLen — declared Task 1 Step 1, used Task 2 Step 1.
  • onChartComplete — declared Task 1 Step 3 (placeholder), updated Task 2 Step 1, bound in template Task 1 Step 3.
  • e.opts.chartData.calPoints[0] — referenced in Task 2 Step 1, matches the spec's data flow diagram and u-charts source (line 572 of u-charts.js: var calPoints = opts.chartData.calPoints?opts.chartData.calPoints:[]).
  • _showTooltip — called Task 2 Step 1, defined in qiun-data-charts.vue:1080.

Gaps: None. All spec requirements are covered by tasks above.