# <!-- Powered by BMAD™ Core -->

# Story 016.002: 搬迁配置和工具函数到独立模块并添加类型定义

## Status

Ready for Review

## Story

**作为** 图表库开发者，
**我想要** 将 u-charts 核心库中的配置和工具函数**搬迁**到独立模块并添加 TypeScript 类型定义，
**以便** 提高代码的可维护性、可测试性和类型安全性，为后续 React 组件封装打下基础。

## Acceptance Criteria

1. `config.ts` 创建完成，包含 config、assign、util 并有完整类型注解
2. `utils/` 目录下所有文件创建完成，每个函数都有类型注解
3. 类型检查通过（pnpm typecheck），无 any 类型（除非必要）
4. 每个文件控制在 500 行以内
5. 代码逻辑与原始 u-charts.ts 完全一致

## Tasks / Subtasks

- [x] Task 1: 分析配置和工具函数 (AC: 1, 2, 5)
  - [x] 1.1 识别 config、assign、util 等配置对象
  - [x] 1.2 识别颜色工具（hexToRgb等）
  - [x] 1.3 识别数学工具（findRange、normalInt等）
  - [x] 1.4 识别坐标转换工具（convertCoordinateOrigin等）
  - [x] 1.5 识别文本测量工具（measureText等）
  - [x] 1.6 识别碰撞检测工具（avoidCollision等）
  - [x] 1.7 识别其他工具函数（getH5Offset、isInAngleRange等）

- [x] Task 2: 创建模块化目录结构 (AC: 1, 2)
  - [x] 2.1 创建 `src/lib/config.ts`
  - [x] 2.2 创建 `src/lib/utils/` 目录
  - [x] 2.3 创建 `src/lib/utils/color.ts`
  - [x] 2.4 创建 `src/lib/utils/math.ts`
  - [x] 2.5 创建 `src/lib/utils/coordinate.ts`
  - [x] 2.6 创建 `src/lib/utils/text.ts`
  - [x] 2.7 创建 `src/lib/utils/collision.ts`
  - [x] 2.8 创建 `src/lib/utils/misc.ts`（其他工具函数）
  - [x] 2.9 创建 `src/lib/utils/index.ts` 统一导出

- [x] Task 3: **搬迁**配置对象并添加类型注解 (AC: 1, 3, 4, 5)
  - [x] 3.1 将 config 对象**搬迁**到 config.ts，添加类型注解
  - [x] 3.2 将 assign 函数**搬迁**到 config.ts，添加类型注解
  - [x] 3.3 将 util 对象**搬迁**到 config.ts，添加类型注解
  - [x] 3.4 为 config、assign、util 添加完整的 TypeScript 类型定义

- [x] Task 4: **搬迁**颜色工具函数并添加类型注解 (AC: 2, 3, 4, 5)
  - [x] 4.1 将 hexToRgb 函数**搬迁**到 utils/color.ts
  - [x] 4.2 为 hexToRgb 添加参数和返回值类型注解
  - [x] 4.3 导出函数并验证类型正确性

- [x] Task 5: **搬迁**数学工具函数并添加类型注解 (AC: 2, 3, 4, 5)
  - [x] 5.1 将 findRange 函数**搬迁**到 utils/math.ts
  - [x] 5.2 将 calCandleMA 函数**搬迁**到 utils/math.ts
  - [x] 5.3 为数学函数添加参数和返回值类型注解
  - [x] 5.4 导出函数并验证类型正确性

- [x] Task 6: **搬迁**坐标转换工具函数并添加类型注解 (AC: 2, 3, 4, 5)
  - [x] 6.1 将 convertCoordinateOrigin 函数**搬迁**到 utils/coordinate.ts
  - [x] 6.2 将 isInAngleRange 函数**搬迁**到 utils/coordinate.ts
  - [x] 6.3 为坐标转换函数添加参数和返回值类型注解
  - [x] 6.4 定义坐标点、中心点等类型接口
  - [x] 6.5 导出函数和类型并验证类型正确性

- [x] Task 7: **搬迁**文本测量工具函数并添加类型注解 (AC: 2, 3, 4, 5)
  - [x] 7.1 将 measureText 函数**搬迁**到 utils/text.ts
  - [x] 7.2 为 measureText 添加参数和返回值类型注解
  - [x] 7.3 定义文本度量相关类型接口
  - [x] 7.4 导出函数和类型并验证类型正确性

- [x] Task 8: **搬迁**碰撞检测工具函数并添加类型注解 (AC: 2, 3, 4, 5)
  - [x] 8.1 将 avoidCollision 函数**搬迁**到 utils/collision.ts
  - [x] 8.2 将 util.isCollision 函数**搬迁**到 utils/collision.ts
  - [x] 8.3 为碰撞检测函数添加参数和返回值类型注解
  - [x] 8.4 定义碰撞对象类型接口
  - [x] 8.5 导出函数和类型并验证类型正确性

- [x] Task 9: **搬迁**其他工具函数并添加类型注解 (AC: 2, 3, 4, 5)
  - [x] 9.1 将 getH5Offset 函数**搬迁**到 utils/misc.ts
  - [x] 9.2 将 createCurveControlPoints 函数**搬迁**到 utils/misc.ts
  - [x] 9.3 将 calValidDistance 函数**搬迁**到 utils/coordinate.ts
  - [x] 9.4 将 getTouches 函数**搬迁**到 utils/misc.ts
  - [x] 9.5 为其他工具函数添加参数和返回值类型注解
  - [x] 9.6 定义相关类型接口
  - [x] 9.7 导出函数和类型并验证类型正确性

- [x] Task 10: 更新导出和验证搬迁结果 (AC: 1, 2, 3, 5)
  - [x] 10.1 更新 src/lib/utils/index.ts 统一导出所有工具函数
  - [x] 10.2 更新 src/index.ts 导出新模块
  - [x] 10.3 运行类型检查验证所有类型注解正确
  - [x] 10.4 确保所有工具函数正确导出
  - [x] 10.5 确认代码逻辑与原始文件完全一致
  - [x] 10.6 验证每个文件控制在 500 行以内

## Dev Notes

### 前置故事见解

**故事 016.001 完成状态**:
- ✅ 已创建 `mini-charts` 包基础结构
- ✅ 已迁移 u-charts 核心库到 `src/lib/u-charts.ts`（7680行代码）
- ✅ 包可以成功构建和类型检查通过
- ⚠️ 使用 `@ts-nocheck` 绕过原始库中的类型问题
- ⚠️ 存在变量重复声明问题（将在本故事中通过模块化解决）

**故事 016.001 关键技术决策**:
- 使用 `@ts-nocheck` 绕过原始 u-charts 库的类型问题
- 修复了 `process` 变量与 Node.js 全局对象的冲突（重命名为 `chartProcess`）
- 修复了 `uChartsEvent` 类语法问题
- 保留了原始 u-charts 库的所有功能逻辑

**迁移策略**: 保持 u-charts 核心绘制逻辑不变，渐进式模块化并添加类型定义

### 技术栈要求

**来源**: [tech-stack.md](../../architecture/tech-stack.md)

- **TypeScript**: 5.4.5 [来源: tech-stack.md]
- **React**: 18.0.0（mini 包使用 React 18，不是 React 19）[来源: 故事 016.001 Dev Notes]
- **Taro**: 4.1.4 [来源: tech-stack.md]
- **Node.js**: 20.18.3（运行时环境）[来源: tech-stack.md]
- **包管理器**: pnpm workspace [来源: source-tree.md]

### 项目结构指南

**来源**: [source-tree.md](../../architecture/source-tree.md)

```
mini-ui-packages/
└── mini-charts/              # mini-charts 包
    ├── src/
    │   ├── index.ts          # 主入口文件
    │   └── lib/
    │       ├── u-charts.ts   # u-charts 核心库（7680行）
    │       ├── config.ts     # [新增] 配置对象
    │       └── utils/        # [新增] 工具函数目录
    │           ├── index.ts  # 工具函数统一导出
    │           ├── color.ts  # 颜色工具函数
    │           ├── math.ts   # 数学工具函数
    │           ├── coordinate.ts  # 坐标转换工具
    │           ├── text.ts   # 文本测量工具
    │           ├── collision.ts   # 碰撞检测工具
    │           └── misc.ts   # 其他工具函数
    ├── tests/
    │   └── u-charts.test.ts
    ├── package.json
    ├── tsconfig.json
    └── jest.config.cjs
```

### TypeScript 配置规范

**来源**: [ui-package-standards.md](../../architecture/ui-package-standards.md#typescript配置)

```json
{
  "compilerOptions": {
    "target": "ES2020",
    "lib": ["DOM", "DOM.Iterable", "ES2020"],
    "module": "ESNext",
    "skipLibCheck": true,
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx",
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true
  },
  "include": ["src"]
}
```

**本故事重要**: 需要移除 `@ts-nocheck`，为所有函数添加完整的类型注解，确保 `strict: true` 模式下无类型错误。

### u-charts 源文件分析

**来源**: [mini-charts/src/lib/u-charts.ts](../../mini-ui-packages/mini-charts/src/lib/u-charts.ts)

**文件统计**:
- 总行数: 7680 行
- 导出函数: 约 60+ 个工具/数据处理函数

**配置对象**:
1. **config**: 默认配置对象，包含版本、颜色、字体等配置
2. **assign**: 深度合并对象的函数
3. **util**: 工具函数集合（toFixed、isFloat、approximatelyEqual、isSameSign、isSameXCoordinateArea、isCollision）

**颜色工具函数**:
- `hexToRgb(hexValue, opc)`: 十六进制颜色转 RGBA

**数学工具函数**:
- `findRange(num, type, limit)`: 计算数值范围
- `calCandleMA(dayArr, nameArr, colorArr, kdata)`: 计算 K 线图移动平均线
- `createCurveControlPoints(points, i)`: 创建曲线控制点

**坐标转换工具函数**:
- `convertCoordinateOrigin(x, y, center)`: 坐标原点转换
- `isInAngleRange(angle, startAngle, endAngle)`: 判断角度是否在范围内
- `calValidDistance(self, distance, chartData, config, opts)`: 计算有效滚动距离

**文本测量工具函数**:
- `measureText(text, fontSize, context)`: 测量文本宽度

**碰撞检测工具函数**:
- `avoidCollision(obj, target)`: 避免碰撞
- `util.isCollision(obj1, obj2)`: 检测两个对象是否碰撞

**其他工具函数**:
- `getH5Offset(e)`: 兼容 H5 点击事件
- `getTouches(touches, opts, e)`: 获取触摸点坐标
- `dataCombine(series)`: 数据合并
- `dataCombineStack(series, len)`: 堆叠数据合并
- `fixPieSeries(series, opts, config)`: 修复饼图系列数据
- `fillSeries(series, opts, config)`: 填充系列数据
- `fillCustomColor(linearType, customColor, series, config)`: 填充自定义颜色
- `getDataRange(minData, maxData)`: 获取数据范围

### 类型定义规范

**来源**: [ui-package-standards.md](../../architecture/ui-package-standards.md#类型定义规范)

**本故事重要**: 必须为所有函数添加完整的类型注解，避免使用 `any` 类型（除非必要）。

**类型定义示例**:

```typescript
// config.ts 类型定义示例
export interface UChartsConfig {
  version: string;
  yAxisWidth: number;
  xAxisHeight: number;
  padding: [number, number, number, number];
  rotate: boolean;
  fontSize: number;
  fontColor: string;
  dataPointShape: string[];
  color: string[];
  linearColor: string[];
  pieChartLinePadding: number;
  pieChartTextPadding: number;
  titleFontSize: number;
  subtitleFontSize: number;
  radarLabelTextMargin: number;
}

export const config: UChartsConfig = {
  version: 'v2.5.0-20230101',
  yAxisWidth: 15,
  // ... 其他配置
};

export function assign<T>(target: T, ...varArgs: Partial<T>[]): T {
  // 实现逻辑
}

export interface Util {
  toFixed(num: number, limit?: number): number | string;
  isFloat(num: number): boolean;
  approximatelyEqual(num1: number, num2: number): boolean;
  isSameSign(num1: number, num2: number): boolean;
  isSameXCoordinateArea(p1: Point, p2: Point): boolean;
  isCollision(obj1: CollisionObject, obj2: CollisionObject): boolean;
}

export const util: Util = {
  toFixed: function toFixed(num, limit = 2) {
    // 实现逻辑
  },
  // ... 其他工具函数
};
```

```typescript
// utils/color.ts 类型定义示例
export interface RgbColor {
  r: number;
  g: number;
  b: number;
}

export function hexToRgb(hexValue: string, opc: number): string {
  // 实现逻辑
}
```

```typescript
// utils/math.ts 类型定义示例
export function findRange(num: number, type?: 'upper' | 'lower', limit?: number): number {
  // 实现逻辑
}

export interface CandleDataPoint {
  [key: string]: number;
}

export interface MASeriesItem {
  data: (number | null)[];
  name: string;
  color: string;
}

export function calCandleMA(
  dayArr: number[],
  nameArr: string[],
  colorArr: string[],
  kdata: CandleDataPoint[]
): MASeriesItem[] {
  // 实现逻辑
}
```

```typescript
// utils/coordinate.ts 类型定义示例
export interface Point {
  x: number;
  y: number;
}

export interface Center {
  x: number;
  y: number;
}

export function convertCoordinateOrigin(x: number, y: number, center: Center): Point {
  // 实现逻辑
}

export function isInAngleRange(
  angle: number,
  startAngle: number,
  endAngle: number
): boolean {
  // 实现逻辑
}
```

```typescript
// utils/text.ts 类型定义示例
export interface TextMeasure {
  width: number;
}

export function measureText(
  text: string,
  fontSize: number,
  context: CanvasRenderingContext2D
): TextMeasure {
  // 实现逻辑
}
```

```typescript
// utils/collision.ts 类型定义示例
export interface CollisionObject {
  start: Point;
  end?: Point;
  width: number;
  height: number;
  center?: Point;
  area?: {
    start: Point;
    end: Point;
    width: number;
    height: number;
  };
}

export interface Point {
  x: number;
  y: number;
}

export function avoidCollision(
  obj: CollisionObject,
  target: CollisionObject[]
): CollisionObject[] {
  // 实现逻辑
}

export function isCollision(obj1: CollisionObject, obj2: CollisionObject): boolean {
  // 实现逻辑（来自 util.isCollision）
}
```

```typescript
// utils/misc.ts 类型定义示例
export interface H5Event {
  offsetX: number;
  offsetY: number;
  mp: {
    changedTouches: Array<{ x: number; y: number }>;
  };
}

export function getH5Offset(e: { offsetX: number; offsetY: number }): H5Event {
  // 实现逻辑
}

export interface TouchPoint {
  x: number;
  y: number;
}

export function getTouches(
  touches: TouchPoint[],
  opts: { width: number; area: number[] },
  e: any
): TouchPoint[] {
  // 实现逻辑
}
```

### 编码标准

**来源**: [coding-standards.md](../../architecture/coding-standards.md)

- **代码风格**: TypeScript 严格模式，一致的缩进和命名
- **类型注解**: 必须为所有函数参数和返回值添加类型注解
- **接口定义**: 为复杂数据结构定义 TypeScript 接口
- **导出规范**: 使用 ES6 `export` 语法导出函数和类型

### 测试策略

**来源**: [testing-strategy.md](../../architecture/testing-strategy.md)

**测试框架**: Jest（mini 包使用 Jest，不是 Vitest）

**测试位置**: `tests/` 目录（与源码并列）

**测试类型**:
- 单元测试: 测试工具函数的正确性
- **不要求**集成测试和 E2E 测试（这些在后续故事中）

**测试要求**:
1. 为每个工具函数编写单元测试
2. 测试覆盖率目标: 基础测试即可，不要求高覆盖率
3. Mock Canvas 上下文以支持依赖 Canvas 的函数测试

**测试命令**:
```bash
# 进入包目录
cd mini-ui-packages/mini-charts

# 运行所有测试
pnpm test

# 运行特定测试
pnpm test --testNamePattern "工具函数测试"

# 类型检查
pnpm typecheck
```

### 构建和发布规范

**来源**: [ui-package-standards.md](../../architecture/ui-package-standards.md#构建和发布规范)

mini-charts 包在 PNPM 工作空间中:
- **开发模式**: 直接使用 TypeScript 源码
- **构建**: 使用 `tsc` 编译到 `dist/` 目录
- **类型检查**: 使用 `tsc --noEmit` 验证类型
- **发布**: 本故事不要求发布，仅验证本地构建成功

**重要**: 本故事完成后，应该可以:
1. ✅ 运行 `pnpm build` 成功生成 `dist/` 目录
2. ✅ 运行 `pnpm typecheck` 无类型错误
3. ✅ 所有新模块正确导出，可以被主入口文件引用

### 开发流程

**来源**: [development-workflow.md](../../architecture/development-workflow.md)

**开发命令**:
```bash
# 进入包目录
cd mini-ui-packages/mini-charts

# 安装依赖（在根目录）
pnpm install

# 开发模式（监听文件变化）
pnpm dev

# 类型检查
pnpm typecheck

# 运行测试
pnpm test

# 构建
pnpm build
```

### 环境配置

**来源**: [CLAUDE.md](../../CLAUDE.md)

- **Node.js**: 20.19.2
- **包管理器**: pnpm
- **测试调试**: 使用 `pnpm test --testNamePattern "测试名称"` 运行特定测试
- **Mini 测试**: 需要 Jest，不是 Vitest
- **类型检查**: 使用 `pnpm typecheck` 加 grep 来过滤要检查的指定文件

### 文件位置规范

**来源**: [source-tree.md](../../architecture/source-tree.md)

所有新文件应创建在:
- 配置文件: `mini-ui-packages/mini-charts/src/lib/config.ts`
- 工具函数目录: `mini-ui-packages/mini-charts/src/lib/utils/`
- 工具函数文件:
  - `color.ts`: 颜色工具函数
  - `math.ts`: 数学工具函数
  - `coordinate.ts`: 坐标转换工具
  - `text.ts`: 文本测量工具
  - `collision.ts`: 碰撞检测工具
  - `misc.ts`: 其他工具函数
  - `index.ts`: 工具函数统一导出
- 主入口: `mini-ui-packages/mini-charts/src/index.ts`（需要更新导出）
- 测试文件: `mini-ui-packages/mini-charts/tests/`（可选，本故事不强制要求）

### 技术约束

1. **搬迁原则**: 保持代码逻辑完全不变，只改变文件组织方式
2. **类型注解**: 只在搬迁过程中添加 TypeScript 类型注解，不修改代码的实现逻辑
3. **文件大小**: 每个文件控制在 500 行以内
4. **类型安全**: 避免使用 `any` 类型（除非必要）
5. **向后兼容**: 保持与原始 u-charts.ts 的功能兼容性

### 验证标准

完成本故事后，应该满足:
1. ✅ `src/lib/config.ts` 文件存在，包含 config、assign、util 并有完整类型注解
2. ✅ `src/lib/utils/` 目录下所有文件存在，每个函数都有类型注解
3. ✅ 运行 `pnpm typecheck` 无类型错误
4. ✅ 每个文件控制在 500 行以内
5. ✅ 代码逻辑与原始 u-charts.ts 完全一致
6. ✅ 所有新模块正确导出，可以被主入口文件引用

### 不包含在本故事中的工作

**来源**: [epic-016 故事列表](../../prd/epic-016-mini-charts-package.md#故事列表)

以下工作**不在**本故事范围内:
- ❌ 数据处理函数模块化（故事 016.003）
- ❌ 图表数据点计算函数模块化（故事 016.004）
- ❌ 绘制函数模块化（故事 016.005）
- ❌ 核心类模块化（故事 016.006）
- ❌ React 组件封装（故事 016.007）

### Testing

**测试框架**: Jest（不是 Vitest）

**测试位置**: `tests/` 目录（与源码并列）

**测试要求**:
1. 创建基础测试验证工具函数的正确导入和导出
2. 测试工具函数的基本功能（可选，本故事不强制要求）
3. Mock Canvas 上下文以支持依赖 Canvas 的函数测试（如需要）
4. 测试覆盖率目标: 基础测试即可，不要求高覆盖率

**测试命令**:
```bash
# 运行所有测试
pnpm test

# 运行特定测试
pnpm test --testNamePattern "配置和工具函数测试"
```

**参考**: [testing-strategy.md](../../architecture/testing-strategy.md)

## Change Log

| Date | Version | Description | Author |
|------|---------|-------------|--------|
| 2025-12-24 | 1.0 | 创建故事文档 | Bob (Scrum Master) |

## Dev Agent Record

*此部分由开发代理在实施过程中填写*

### Agent Model Used

claude-sonnet

### Debug Log References

无

### Completion Notes List

1. ✅ 创建了 `src/lib/config.ts` (165 行) - 包含 config、assign、util 并有完整类型注解
2. ✅ 创建了 `src/lib/utils/` 目录及所有子模块文件
3. ✅ 所有文件行数均在 500 行以内（最大 201 行 misc.ts）
4. ✅ 类型检查通过（pnpm typecheck）
5. ✅ 为 Canvas 上下文使用 `any` 类型以兼容小程序环境
6. ⚠️ 测试需要 Jest 配置更新以支持 ES 模块（测试框架限制，本故事不强制要求）

### File List

**新增文件：**
- `mini-ui-packages/mini-charts/src/lib/config.ts` (165 行) - 配置对象和工具函数
- `mini-ui-packages/mini-charts/src/lib/utils/color.ts` (20 行) - 颜色工具函数
- `mini-ui-packages/mini-charts/src/lib/utils/math.ts` (92 行) - 数学工具函数
- `mini-ui-packages/mini-charts/src/lib/utils/coordinate.ts` (155 行) - 坐标转换工具
- `mini-ui-packages/mini-charts/src/lib/utils/text.ts` (59 行) - 文本测量工具
- `mini-ui-packages/mini-charts/src/lib/utils/collision.ts` (88 行) - 碰撞检测工具
- `mini-ui-packages/mini-charts/src/lib/utils/misc.ts` (201 行) - 其他工具函数
- `mini-ui-packages/mini-charts/src/lib/utils/index.ts` (33 行) - 统一导出

**修改文件：**
- `mini-ui-packages/mini-charts/src/index.ts` - 更新导出以支持新的模块化结构

## QA Results

*此部分由 QA 代理在审查完成后填写*
