# 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): ```js import { computed, ref, watch } from "vue"; ``` with: ```js import { computed, nextTick, ref, watch } from "vue"; ``` Then immediately after `defineEmits(["retry"]);` (line 52), insert two new refs: ```js // [新增] 拿到 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 `` opening tag (line 23), add `ref="chartRef"` as the first attribute: ```vue ``` - [ ] **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: ```js // [新增] 图表首次渲染完成回调(占位,Task 2 填充真实逻辑) const onChartComplete = (_e) => { // 故意留空 — Task 2 接入 fake event 触发 }; ``` Also bind it on the template — change line 35 from `@tap="onChartTap"` to: ```vue ontap @tap="onChartTap" @complete="onChartComplete" /> ``` - [ ] **Step 4: Verify the chart still renders normally** Run H5 preview (HBuilderX → 运行 → 运行到浏览器 → Chrome) **or** the dev server: ```bash 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** ```bash 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: ```js // [新增] 图表首次渲染完成回调(占位,Task 2 填充真实逻辑) const onChartComplete = (_e) => { // 故意留空 — Task 2 接入 fake event 触发 }; ``` Replace it with: ```js // [新增] 图表渲染完成回调: // 从 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 `+` (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** ```bash 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): ```js // 当前选中的数据点索引:默认指向最后一条,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: ```js import { computed, nextTick, ref, watch } from "vue"; ``` to: ```js import { computed, nextTick, ref } from "vue"; ``` (`watch` is no longer used after Step 1.) - [ ] **Step 3: Remove `@tap="onChartTap"` from the template** In the `` tag, find: ```vue ontap @tap="onChartTap" @complete="onChartComplete" /> ``` Change to: ```vue 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** ```bash 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)** ```bash 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.