12 KiB
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; addchartRef,onChartCompletehandler,lastShownLencursor; remove dead code (currentIndex, itswatch,onChartTap,@tapbinding)
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
chartRefandlastShownLento 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
onChartCompletehandler (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
onChartCompletewith 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
currentIndexref + watcher +onChartTapfunction
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
watchfrom the import line (still neednextTick+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 ofu-charts.js:var calPoints = opts.chartData.calPoints?opts.chartData.calPoints:[]). ✅_showTooltip— called Task 2 Step 1, defined inqiun-data-charts.vue:1080. ✅
Gaps: None. All spec requirements are covered by tasks above.