diff --git a/AGENTS.md b/AGENTS.md index 905cc9f..43a17c0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -64,6 +64,25 @@ miniprogram/ typings/ # 全局类型声明(IAppOption、PlatformInfoType 等)+ 微信 API 类型 ``` +## 内置工具模块 + +所有工具位于 `miniprogram/utils/`,均有对应的详细文档: + +| 模块 | 文件 | 文档 | 用途 | +|------|------|------|------| +| 网络请求 | `request.ts` | `docs/request.md` | axios-miniprogram 封装,Token 注入、loading 管理、401 跳转、`upload()` | +| AES 加解密 | `aes.ts` | `docs/aes.md` | AES-256-CBC,`Encrypt`/`Decrypt`/`BASE64Encrypt`/`BASE64Decrypt` | +| 图片工具 | `image.ts` | `docs/image.md` | iOS ATS 兼容,HTTP 图片自动转 base64,`getImageDisplayUrl`/`urlToBase64` | +| 日期格式化 | `util.ts` | `docs/util.md` | `formatTime`/`formatDateTime`,支持多输入类型和自定义模板 | +| 平台检测 | `platform.ts` | — | `getPlatformInfo()`,启动时调用,结果存入 `globalData.platformInfo` | + +所有组件文档: + +| 组件 | 目录 | 文档 | 用途 | +|------|------|------|------| +| RootPageLayout | `components/root-page-layout/` | `docs/root-page-layout.md` | 页面容器,封装 TDesign Navbar,自动返回按钮逻辑、安全区适配 | +| 自定义 TabBar | `custom-tab-bar/` | `docs/custom-tab-bar.md` | 基于 TDesign t-tab-bar,需在 TabBar 页 `onShow` 中调用 `init()` | + ## 核心架构决策 ### 自定义导航栏 diff --git a/README.md b/README.md index e792df5..707216a 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,9 @@ ts-weapp-template/ │ ├── utils/ # 工具函数 │ │ ├── request.ts # 网络请求封装 │ │ ├── aes.ts # AES 加密解密工具 -│ │ └── util.ts # 其他工具函数 +│ │ ├── image.ts # 图片 URL 兼容工具 +│ │ ├── util.ts # 日期格式化工具 +│ │ └── platform.ts # 平台信息检测 │ ├── app.ts # 小程序入口文件 │ ├── app.json # 小程序配置文件 │ └── app.scss # 全局样式 @@ -103,256 +105,16 @@ ts-weapp-template/ ## 核心功能说明 -### 网络请求封装 +本模板内置了以下工具和组件,方便开发者进行小程序业务的开发: -项目提供了基于 `axios-miniprogram` 的请求封装,位于 `miniprogram/utils/request.ts`。 - -**主要特性:** - -- 自动添加 Authorization 请求头(支持白名单配置) -- 统一的响应格式处理(标准格式:`{ code, data, message }`) -- 智能加载动画管理(支持并发请求计数) -- 401 未授权自动跳转登录页 -- 完整的 TypeScript 类型支持 -- 文件上传功能封装 - -**使用示例:** - -```typescript -import request from '../utils/request' - -// GET 请求 -const data = await request.get('/api/user/info') - -// POST 请求 -const result = await request.post('/api/user/update', { name: '张三' }) - -// 禁用加载动画 -const data = await request.get('/api/user/info', { loading: false }) - -// 文件上传 -import { upload } from '../utils/request' -const result = await upload({ - apiUrl: '/api/upload', - filePath: 'tempFilePath', - name: 'file', - formData: { userId: 123 }, -}) -``` - -### AES 加密解密工具 - -项目提供了完整的 AES-256 加密解密工具,位于 `miniprogram/utils/aes.ts`。 - -**主要功能:** - -- 标准 AES-256-CBC 加密解密 -- URL Safe Base64 格式支持 -- 完整的错误处理和类型校验 -- 自定义密钥和 IV 支持 - -**使用示例:** - -```typescript -import { Encrypt, Decrypt, BASE64Encrypt, BASE64Decrypt } from '../utils/aes' - -// 标准加密解密 -const encrypted = Encrypt('hello world') -const decrypted = Decrypt(encrypted) - -// URL Safe Base64 加密解密 -const urlSafeEncrypted = BASE64Encrypt('hello world') -const urlSafeDecrypted = BASE64Decrypt(urlSafeEncrypted) - -// 自定义密钥和 IV -const customEncrypted = Encrypt('hello world', 'your-key-32-bytes-long', 'your-iv-16-bytes') -``` - -### 图片 URL 兼容工具(iOS ATS) - -项目提供了图片 URL 兼容工具,位于 `miniprogram/utils/image.ts`,解决微信小程序在 iOS/Mac WKWebView 下 HTTP 图片因 ATS 限制无法加载的问题。 - -**主要功能:** - -- 自动检测当前设备平台,仅在 iOS/Mac 下对 HTTP 图片触发 base64 转码 -- HTTPS 链接和非 iOS/Mac 平台直接返回原始 URL,零额外开销 -- 非完整 URL(不以 `http://` 或 `https://` 开头)会 console.warn 提示后原样返回 -- `urlToBase64` 使用 `wx.request` + `arraybuffer`,不经过 axios 拦截器 - -**使用示例:** - -```typescript -import { getImageDisplayUrl, urlToBase64 } from '../utils/image' - -// 推荐:直接获取可用于 显示的 URL(自动判断是否需要转 base64) -const displayUrl = await getImageDisplayUrl('http://example.com/photo.jpg') - -// 或手动将任意图片转为 base64 -const base64 = await urlToBase64('https://example.com/photo.jpg') -``` - -### 时间日期格式化工具 - -项目提供了灵活的时间日期格式化工具,位于 `miniprogram/utils/util.ts`。 - -**主要功能:** - -- 支持 Date 对象、时间戳、日期字符串等多种输入格式 -- 自定义格式化模板,支持丰富的占位符 -- 兼容中文日期格式(如 "2020年1月1日") -- 兼容 iOS/Safari 日期解析问题 -- 支持 12/24 小时制转换 -- 支持毫秒显示 - -**使用示例:** - -```typescript -import { formatTime, formatDateTime } from '../utils/util' - -// 标准格式:YYYY/MM/DD HH:mm:ss -const time1 = formatTime(new Date()) -// 输出:2024/01/15 14:30:45 - -// 自定义格式 -const time2 = formatDateTime(new Date(), 'YYYY-MM-DD HH:mm:ss') -// 输出:2024-01-15 14:30:45 - -// 只显示日期 -const date = formatDateTime(new Date(), 'YYYY年MM月DD日') -// 输出:2024年01月15日 - -// 12小时制 -const time12 = formatDateTime(new Date(), 'YYYY/MM/DD hh:mm:ss A') -// 输出:2024/01/15 02:30:45 下午 - -// 时间戳格式化 -const timestamp = formatDateTime(1705291845000, 'YYYY-MM-DD HH:mm') -// 输出:2024-01-15 14:30 - -// 字符串日期格式化 -const strDate = formatDateTime('2024-01-15', 'YYYY年MM月DD日') -// 输出:2024年01月15日 -``` - -**支持的占位符:** - -- `YYYY`: 4位年份 -- `YY`: 2位年份 -- `MM`: 2位月份 -- `M`: 1位月份 -- `DD`: 2位日期 -- `D`: 1位日期 -- `HH`: 2位小时(24小时制) -- `H`: 1位小时(24小时制) -- `hh`: 2位小时(12小时制) -- `h`: 1位小时(12小时制) -- `mm`: 2位分钟 -- `m`: 1位分钟 -- `ss`: 2位秒 -- `s`: 1位秒 -- `SSS`: 3位毫秒 -- `A`: 上午/下午 -- `a`: 上午/下午(小写) - -### RootPageLayout 页面容器组件 - -项目提供了基于 TDesign Navbar 封装的页面容器组件,位于 `miniprogram/components/root-page-layout/` 目录。 - -**主要特性:** - -- 基于 TDesign Navbar 组件,提供统一的导航栏和内容区域布局 -- 自动判断返回按钮显示(根据页面栈深度) -- 支持安全区域适配,适配不同设备 -- 支持自定义导航栏样式和插槽内容 -- 完整的返回事件回调支持 - -详细使用说明请参考 [RootPageLayout 组件使用手册](docs/root-page-layout.md)。 - -### 自定义 TabBar - -项目提供了基于 TDesign 组件的自定义 TabBar 实现,位于 `miniprogram/custom-tab-bar/` 目录。 - -**主要特性:** - -- 基于 TDesign 的 `t-tab-bar` 和 `t-tab-bar-item` 组件,样式统一美观 -- 自动状态同步,根据当前页面路由自动更新激活状态 -- 支持页面切换,点击 TabBar 项自动跳转到对应页面 -- 完整的 TypeScript 类型支持 -- 易于扩展和自定义 - -**配置说明:** - -1. **在 `app.json` 中启用自定义 TabBar:** - -```json -{ - "tabBar": { - "custom": true, - "color": "#000000", - "selectedColor": "#000000", - "backgroundColor": "#000000", - "list": [ - { - "pagePath": "pages/one/index", - "text": "页面一" - }, - { - "pagePath": "pages/two/index", - "text": "页面二" - }, - { - "pagePath": "pages/three/index", - "text": "页面三" - }, - { - "pagePath": "pages/four/index", - "text": "页面四" - } - ] - } -} -``` - -2. **TabBar 组件配置:** - -在 `miniprogram/custom-tab-bar/index.ts` 中配置 TabBar 项: - -```typescript -data: { - value: 'label_1', - list: [ - { value: 'label_1', label: '首页', icon: 'home', pagePath: '/pages/one/index' }, - { value: 'label_2', label: '应用', icon: 'app', pagePath: '/pages/two/index' }, - { value: 'label_3', label: '聊天', icon: 'chat', pagePath: '/pages/three/index' }, - { value: 'label_4', label: '我的', icon: 'user', pagePath: '/pages/four/index' }, - ], -} -``` - -**在 TabBar 页面中使用:** - -每个 TabBar 页面需要在 `onShow` 生命周期中调用 `init()` 方法来同步 TabBar 状态: - -```typescript -Page({ - onShow() { - // 同步 TabBar 激活状态 - this.getTabBar().init() - }, -}) -``` - -**自定义 TabBar 项:** - -如需添加或修改 TabBar 项,请按以下步骤操作: - -1. 在 `miniprogram/custom-tab-bar/index.ts` 的 `data.list` 数组中添加或修改配置项 -2. 在 `miniprogram/app.json` 的 `tabBar.list` 数组中同步更新配置 -3. 确保对应的页面已创建并在 `app.json` 的 `pages` 数组中注册 - -**支持的图标类型:** - -TDesign TabBar 组件支持多种内置图标,常用的有:`home`、`app`、`chat`、`user`、`setting` 等。更多图标类型请参考 [TDesign 图标文档](https://tdesign.tencent.com/miniprogram/components/tab-bar)。 +| 工具 / 组件 | 说明 | 文档 | +|------------|------|------| +| 网络请求封装 | 基于 axios-miniprogram,Token 注入、loading 管理、401 跳转、文件上传 | [查看文档](docs/request.md) | +| AES 加密解密 | AES-256-CBC 加密解密,支持标准 Base64 和 URL Safe Base64 | [查看文档](docs/aes.md) | +| 图片 URL 兼容 | iOS WKWebView 下 HTTP 图片因 ATS 无法加载的自动转 base64 方案 | [查看文档](docs/image.md) | +| 日期时间格式化 | 多输入格式兼容、自定义模板、iOS 日期解析修复 | [查看文档](docs/util.md) | +| RootPageLayout 组件 | 基于 TDesign Navbar 的页面容器,自动返回按钮、安全区适配 | [查看文档](docs/root-page-layout.md) | +| 自定义 TabBar | 基于 TDesign 的自定义 TabBar,路由同步、状态管理 | [查看文档](docs/custom-tab-bar.md) | ## 开发指南 diff --git a/docs/aes.md b/docs/aes.md new file mode 100644 index 0000000..814396d --- /dev/null +++ b/docs/aes.md @@ -0,0 +1,34 @@ +# AES 加密解密工具 + +完整的 AES-256 加密解密工具,位于 `miniprogram/utils/aes.ts`。 + +## 主要功能 + +- 标准 AES-256-CBC 加密解密 +- URL Safe Base64 格式支持 +- 完整的错误处理和类型校验(`AESError`) +- 自定义密钥和 IV 支持 + +## 使用示例 + +```typescript +import { Encrypt, Decrypt, BASE64Encrypt, BASE64Decrypt } from '../utils/aes' + +// 标准加密解密 +const encrypted = Encrypt('hello world') +const decrypted = Decrypt(encrypted) + +// URL Safe Base64 加密解密 +const urlSafeEncrypted = BASE64Encrypt('hello world') +const urlSafeDecrypted = BASE64Decrypt(urlSafeEncrypted) + +// 自定义密钥和 IV +const customEncrypted = Encrypt('hello world', 'your-key-32-bytes-long', 'your-iv-16-bytes') +``` + +## 注意事项 + +- 默认的 AES 密钥和 IV 仅用于开发测试,**生产环境请务必修改** +- Key 强制要求 32 字节以触发 AES-256,不足或超出会抛出 `AESError` +- IV 强制要求 16 字节 +- 解密失败(密钥不匹配/密文被篡改)会抛出 `AESError`,请调用方 catch 处理 diff --git a/docs/custom-tab-bar.md b/docs/custom-tab-bar.md new file mode 100644 index 0000000..fabe7f6 --- /dev/null +++ b/docs/custom-tab-bar.md @@ -0,0 +1,87 @@ +# 自定义 TabBar + +基于 TDesign 组件的自定义 TabBar 实现,位于 `miniprogram/custom-tab-bar/` 目录。 + +## 主要特性 + +- 基于 TDesign 的 `t-tab-bar` 和 `t-tab-bar-item` 组件,样式统一美观 +- 自动状态同步,根据当前页面路由自动更新激活状态 +- 支持页面切换,点击 TabBar 项自动跳转到对应页面 +- 完整的 TypeScript 类型支持 +- 易于扩展和自定义 + +## 配置说明 + +### 1. 在 `app.json` 中启用自定义 TabBar + +```json +{ + "tabBar": { + "custom": true, // 配置为 true 开启自定义 tabbar + "color": "#000000", + "selectedColor": "#000000", + "backgroundColor": "#000000", + "list": [ + { + "pagePath": "pages/one/index", + "text": "页面一" + }, + { + "pagePath": "pages/two/index", + "text": "页面二" + }, + { + "pagePath": "pages/three/index", + "text": "页面三" + }, + { + "pagePath": "pages/four/index", + "text": "页面四" + } + ] + } +} +``` + +### 2. TabBar 组件配置 + +在 `miniprogram/custom-tab-bar/index.ts` 中配置 TabBar 项: + +```typescript +data: { + value: 'label_1', + list: [ + { value: 'label_1', label: '首页', icon: 'home', pagePath: '/pages/one/index' }, + { value: 'label_2', label: '应用', icon: 'app', pagePath: '/pages/two/index' }, + { value: 'label_3', label: '聊天', icon: 'chat', pagePath: '/pages/three/index' }, + { value: 'label_4', label: '我的', icon: 'user', pagePath: '/pages/four/index' }, + ], +} +``` + +## 在 TabBar 页面中使用 + +每个 TabBar 页面需要在 `onShow` 生命周期中调用 `init()` 方法来同步 TabBar 状态: + +```typescript +Page({ + onShow() { + // 同步 TabBar 激活状态 + this.getTabBar().init() + }, +}) +``` + +## 自定义 TabBar 项 + +如需添加或修改 TabBar 项,请按以下步骤操作: + +1. 在 `miniprogram/custom-tab-bar/index.ts` 的 `data.list` 数组中添加或修改配置项 +2. 在 `miniprogram/app.json` 的 `tabBar.list` 数组中同步更新配置 +3. 确保对应的页面已创建并在 `app.json` 的 `pages` 数组中注册 + +## 注意事项 + +- TabBar 页面必须在 `onShow` 中调用 `this.getTabBar().init()`,否则可能出现状态不同步 +- `app.json` 的 `tabBar.list` 和 `custom-tab-bar/index.ts` 的 `data.list` 配置需保持一致(页面路径和顺序) +- 支持的 TDesign 内置图标:`home`、`app`、`chat`、`user`、`setting` 等,更多参考 [TDesign 图标文档](https://tdesign.tencent.com/miniprogram/components/tab-bar) diff --git a/docs/image.md b/docs/image.md new file mode 100644 index 0000000..7f0a9f6 --- /dev/null +++ b/docs/image.md @@ -0,0 +1,26 @@ +# 图片 URL 兼容工具(iOS ATS) + +解决微信小程序在 iOS/Mac WKWebView 下 HTTP 图片因 ATS 限制无法加载的问题,位于 `miniprogram/utils/image.ts`。 + +## 背景 + +微信小程序在 iOS/Mac 平台的 WKWebView 下,`` 组件加载 HTTP 图片会被 ATS(App Transport Security)拦截,必须通过 HTTPS 才能远程渲染。本工具自动判断当前平台和协议,对 HTTP 图片在 iOS/Mac 下通过 `wx.request` 下载后转为 base64 data URL,确保图片正常显示。 + +## 主要功能 + +- 自动检测当前设备平台(读取 `app.globalData.platformInfo`),仅在 iOS/Mac 下对 HTTP 图片触发 base64 转码 +- HTTPS 链接和非 iOS/Mac 平台直接返回原始 URL,零额外开销 +- 非完整 URL(不以 `http://` 或 `https://` 开头)会 `console.warn` 提示后原样返回 +- `urlToBase64` 使用 `wx.request` + `arraybuffer`,不经过 axios 拦截器 + +## 使用示例 + +```typescript +import { getImageDisplayUrl, urlToBase64 } from '../utils/image' + +// 推荐:直接获取可用于 显示的 URL(自动判断是否需要转 base64) +const displayUrl = await getImageDisplayUrl('http://example.com/photo.jpg') + +// 或手动将任意图片转为 base64 +const base64 = await urlToBase64('https://example.com/photo.jpg') +``` diff --git a/docs/request.md b/docs/request.md new file mode 100644 index 0000000..147ccc5 --- /dev/null +++ b/docs/request.md @@ -0,0 +1,44 @@ +# 网络请求封装 + +基于 `axios-miniprogram` 的网络请求封装,位于 `miniprogram/utils/request.ts`。 + +## 主要特性 + +- 自动添加 Authorization 请求头(支持白名单配置) +- 统一的响应格式处理(标准格式:`{ code, data, message }`) +- 智能加载动画管理(支持并发请求计数) +- 401 未授权自动跳转登录页 +- 完整的 TypeScript 类型支持 +- 文件上传功能封装 + +## 使用示例 + +```typescript +import request from '../utils/request' + +// GET 请求 +const data = await request.get('/api/user/info') + +// POST 请求 +const result = await request.post('/api/user/update', { name: '张三' }) + +// 禁用加载动画 +const data = await request.get('/api/user/info', { loading: false }) + +// 文件上传 +import { upload } from '../utils/request' +const result = await upload({ + apiUrl: '/api/upload', + filePath: 'tempFilePath', + name: 'file', + formData: { userId: 123 }, +}) +``` + +## 注意事项 + +1. **API 地址配置**: 请在 `request.ts` 中配置正确的 `baseURL` +2. **Token 管理**: Token 存储在 `app.globalData.token` 中,登录成功后需要手动设置 +3. **请求白名单**: 不需要认证的接口需要在 `request.ts` 的 `requestWhiteList` 数组中配置 +4. 响应拦截器在 `code === 200` 时自动解包返回 `result.data`,调用方直接拿到业务数据 +5. 并发请求的 loading 动画通过计数器管理,避免闪烁 diff --git a/docs/util.md b/docs/util.md new file mode 100644 index 0000000..89219f0 --- /dev/null +++ b/docs/util.md @@ -0,0 +1,64 @@ +# 时间日期格式化工具 + +灵活的时间日期格式化工具,位于 `miniprogram/utils/util.ts`。 + +## 主要功能 + +- 支持 Date 对象、时间戳、日期字符串等多种输入格式 +- 自定义格式化模板,支持丰富的占位符 +- 兼容中文日期格式(如 "2020年1月1日") +- 兼容 iOS/Safari 日期解析问题 +- 支持 12/24 小时制转换 +- 支持毫秒显示 + +## 使用示例 + +```typescript +import { formatTime, formatDateTime } from '../utils/util' + +// 标准格式:YYYY/MM/DD HH:mm:ss +const time1 = formatTime(new Date()) +// 输出:2024/01/15 14:30:45 + +// 自定义格式 +const time2 = formatDateTime(new Date(), 'YYYY-MM-DD HH:mm:ss') +// 输出:2024-01-15 14:30:45 + +// 只显示日期 +const date = formatDateTime(new Date(), 'YYYY年MM月DD日') +// 输出:2024年01月15日 + +// 12小时制 +const time12 = formatDateTime(new Date(), 'YYYY/MM/DD hh:mm:ss A') +// 输出:2024/01/15 02:30:45 下午 + +// 时间戳格式化 +const timestamp = formatDateTime(1705291845000, 'YYYY-MM-DD HH:mm') +// 输出:2024-01-15 14:30 + +// 字符串日期格式化 +const strDate = formatDateTime('2024-01-15', 'YYYY年MM月DD日') +// 输出:2024年01月15日 +``` + +## 占位符一览 + +| 占位符 | 说明 | 示例 | +|--------|------|------| +| `YYYY` | 4位年份 | 2024 | +| `YY` | 2位年份 | 24 | +| `MM` | 2位月份 | 01 | +| `M` | 1位月份 | 1 | +| `DD` | 2位日期 | 15 | +| `D` | 1位日期 | 15 | +| `HH` | 2位小时(24小时制) | 14 | +| `H` | 1位小时(24小时制) | 14 | +| `hh` | 2位小时(12小时制) | 02 | +| `h` | 1位小时(12小时制) | 2 | +| `mm` | 2位分钟 | 30 | +| `m` | 1位分钟 | 30 | +| `ss` | 2位秒 | 45 | +| `s` | 1位秒 | 45 | +| `SSS` | 3位毫秒 | 123 | +| `A` | 上午/下午 | 下午 | +| `a` | am/pm | pm |