Compare commits
10
Commits
57c2c18274
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e2cf53dc9 | ||
|
|
880d8e758f | ||
|
|
11c6fb21cd | ||
|
|
3f75ab4ef6 | ||
|
|
99e49ef286 | ||
|
|
f19f4b8d41 | ||
|
|
58cd96aaa3 | ||
|
|
caf5803e44 | ||
|
|
7e05fc2a67 | ||
|
|
f371245e7a |
No files matched your search
@@ -0,0 +1,144 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to AI Coding Agent when working with code in this repository.
|
||||
|
||||
## 项目概况
|
||||
|
||||
微信小程序 TypeScript 模板 — 基于 TypeScript + SASS 的小程序开发脚手架,集成 TDesign 组件库、axios-miniprogram 网络请求、crypto-js AES 加密等。
|
||||
|
||||
## 常用命令
|
||||
|
||||
```bash
|
||||
# 安装依赖(需要 pnpm)
|
||||
pnpm install
|
||||
|
||||
# ESLint 检查
|
||||
pnpm lint
|
||||
|
||||
# ESLint 自动修复
|
||||
pnpm lint:fix
|
||||
|
||||
# Prettier 格式化
|
||||
pnpm format
|
||||
|
||||
# Prettier 格式检查
|
||||
pnpm format:check
|
||||
```
|
||||
|
||||
**微信开发者工具操作:**
|
||||
|
||||
- 修改 `package.json` 依赖后,需在微信开发者工具中执行「工具 → 构建 npm」
|
||||
- TypeScript 和 SASS 编译由微信开发者工具内置编译器完成(无独立构建命令)
|
||||
- 开发时在微信开发者工具中点击「编译」即可预览
|
||||
|
||||
## 技术栈
|
||||
|
||||
| 类别 | 技术 |
|
||||
| -------- | -------------------------------------------------------------------- |
|
||||
| 语言 | TypeScript (ES2020 target, CommonJS module) |
|
||||
| 样式 | SASS/SCSS(通过 `project.config.json` 的 `useCompilerPlugins` 启用) |
|
||||
| UI 组件 | TDesign Miniprogram v1.12+ |
|
||||
| HTTP | axios-miniprogram v2.7+ |
|
||||
| 加密 | crypto-js v4.2+ |
|
||||
| 包管理 | pnpm |
|
||||
| 代码规范 | ESLint 9 + TypeScript ESLint + Prettier |
|
||||
|
||||
## 项目架构
|
||||
|
||||
```
|
||||
miniprogram/
|
||||
├── app.ts # 入口:计算导航栏/安全区尺寸、初始化 platformInfo 到 globalData
|
||||
├── app.json # 页面注册、自定义 TabBar、navigationStyle: "custom"
|
||||
├── app.scss # 全局样式重置
|
||||
├── api/ # API 接口层(每接口一个文件 + types.ts 定义 StandardResponse<T>)
|
||||
├── components/ # 共享组件(目前仅有 root-page-layout)
|
||||
├── custom-tab-bar/ # 自定义 TabBar(基于 TDesign t-tab-bar)
|
||||
├── pages/ # 页面(index, login, date, one~four)
|
||||
├── utils/
|
||||
│ ├── request.ts # axios-miniprogram 封装:Token 注入、loading 管理、401 跳转
|
||||
│ ├── aes.ts # AES-256-CBC 加密/解密 + URL Safe Base64 变体
|
||||
│ ├── util.ts # 日期格式化(formatTime / formatDateTime)
|
||||
│ ├── image.ts # 图片 URL 兼容(iOS ATS http→base64)
|
||||
│ └── platform.ts # 微信宿主平台信息检测(设备类别/操作系统/桌面or移动)
|
||||
└── miniprogram_npm/ # 微信开发者工具构建的 npm 产物(已 gitignore,不直接编辑)
|
||||
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()` |
|
||||
|
||||
## 核心架构决策
|
||||
|
||||
### 自定义导航栏
|
||||
|
||||
`app.json` 中 `window.navigationStyle: "custom"` 全局关闭了原生导航栏。所有需要导航栏的页面必须使用 `root-page-layout` 组件(封装 TDesign `t-navbar`),该组件自动根据页面栈深度决定是否显示返回按钮。
|
||||
|
||||
### 网络请求模式
|
||||
|
||||
所有 API 调用遵循统一模式:
|
||||
|
||||
1. `api/types.ts` 定义 `StandardResponse<T>` 结构(`{ code, data, message }`)
|
||||
2. 每个 API 模块从 `utils/request` 导入 axios 实例,定义请求函数
|
||||
3. 请求拦截器自动注入 `Authorization` header(从 `app.globalData.token` 读取),白名单接口除外
|
||||
4. 响应拦截器在 `code === 200` 时自动解包返回 `result.data`,401 时 `reLaunch` 到登录页
|
||||
5. 并发请求的 loading 动画通过计数器管理(`netWorkNum`),避免闪烁
|
||||
|
||||
### GlobalData 结构
|
||||
|
||||
`app.globalData` 在 `onLaunch` 中初始化以下数据:
|
||||
|
||||
- **UI 布局数据**:`navBarHeight`、`navBarContentHeight`、`statusBarHeight`、`menuRight`、`menuTop`、`menuHeight`、`safeAreaBottom`、`safeAreaInsetBottom` — 通过 `wx.getMenuButtonBoundingClientRect()` 和 `wx.getWindowInfo()` 计算
|
||||
- **平台信息**:`platformInfo` — 通过 `utils/platform.ts` 的 `getPlatformInfo()` 获取,包含 hardware、software、isMobile、isDesktop、isDevtools
|
||||
- **认证**:`token`(登录成功后由业务代码设置)
|
||||
|
||||
类型定义位于 `typings/index.d.ts` 的 `IAppOption` 接口。
|
||||
|
||||
### 自定义 TabBar
|
||||
|
||||
- `app.json` 中 `tabBar.custom: true` 启用自定义 TabBar
|
||||
- 组件位于 `miniprogram/custom-tab-bar/`,基于 TDesign `t-tab-bar`
|
||||
- TabBar 页面必须在 `onShow` 中调用 `this.getTabBar().init()` 同步激活状态
|
||||
- `app.json` 的 `tabBar.list` 和 `custom-tab-bar/index.ts` 的 `data.list` 需保持配置一致
|
||||
|
||||
### SASS 全局注入
|
||||
|
||||
`project.config.json` 中通过 `sassSetting` 配置了 `sassCommonUseFilePath: "global.sass"` 和 `scssCommonUseFilePath: "global.scss"`,微信开发者工具会自动将这些全局文件注入每个 SCSS/SASS 文件。当前 miniprogram 根目录下尚无 `global.scss` / `global.sass` 文件,需手动创建后使用。
|
||||
|
||||
### 无独立构建系统
|
||||
|
||||
本项目不依赖 Webpack/Vite 等构建工具。TypeScript 和 SASS 编译由微信开发者工具的 SWC 编译器内置完成(`project.config.json` 中 `swc: true`)。ESLint/Prettier 仅用于代码质量检查,不参与编译流程。
|
||||
|
||||
## 关键约定
|
||||
|
||||
- **npm 镜像**:`.npmrc` 配置了 npmmirror.com 镜像和 node-sass 二进制镜像
|
||||
- **代码风格**:分号关闭、单引号、100 字符宽度、2 空格缩进、LF 换行
|
||||
- **ESLint**:`no-console` 为 warn 级别(仅允许 `console.warn` 和 `console.error`),`@typescript-eslint/no-explicit-any` 为 warn
|
||||
- **路径别名**:无路径别名 — 所有 import 使用相对路径
|
||||
- **微信基础库版本**:`libVersion: "3.14.1"`(`project.config.json`),`3.17.0`(`project.private.config.json`)
|
||||
- **npm 构建模式**:手动模式(`packNpmManually: true`),npm 包输出到 `miniprogram/` 目录
|
||||
- **Git 忽略**:`miniprogram/miniprogram_npm/`、`project.private.config.json`、`*.js.map` 等不纳入版本控制
|
||||
|
||||
## 类型系统
|
||||
|
||||
全局类型通过 `typings/index.d.ts` 的三斜线指令引入,定义了:
|
||||
|
||||
- `IAppOption` — App 实例类型(含 `globalData` 完整结构)
|
||||
- `PlatformInfoType` / `HardwarePlatformInfoType` / `SoftwarePlatformInfoType` — 平台信息层级类型
|
||||
- `ClientPlatformType` / `OperatingSystemType` / `HardwareDeviceClassType` — 联合类型
|
||||
- 微信小程序 API 类型位于 `typings/types/wx/` 目录
|
||||
@@ -17,6 +17,7 @@
|
||||
- 完整的 TypeScript 类型支持,提供更好的开发体验和代码提示
|
||||
- 基于 axios-miniprogram 的网络请求封装,支持自动 Token 注入、统一错误处理、智能加载动画管理
|
||||
- AES-256 加密解密工具,支持标准 Base64 和 URL Safe Base64 两种格式
|
||||
- 图片 URL 兼容工具,自动处理 iOS WKWebView 下 HTTP 图片无法加载的问题(ATS 限制),按需转 base64
|
||||
- 集成 TDesign 组件库,提供丰富的 UI 组件
|
||||
- 自定义 TabBar 实现,基于 TDesign 组件,支持自动状态同步和页面切换
|
||||
- 完善的代码规范配置,支持 ESLint 和 Prettier 自动格式化
|
||||
@@ -89,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 # 全局样式
|
||||
@@ -102,233 +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')
|
||||
```
|
||||
|
||||
### 时间日期格式化工具
|
||||
|
||||
项目提供了灵活的时间日期格式化工具,位于 `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`: 上午/下午(小写)
|
||||
|
||||
### PageContainer 页面容器组件
|
||||
|
||||
项目提供了基于 TDesign Navbar 封装的页面容器组件,位于 `miniprogram/components/page-container/` 目录。
|
||||
|
||||
**主要特性:**
|
||||
|
||||
- 基于 TDesign Navbar 组件,提供统一的导航栏和内容区域布局
|
||||
- 自动判断返回按钮显示(根据页面栈深度)
|
||||
- 支持安全区域适配,适配不同设备
|
||||
- 支持自定义导航栏样式和插槽内容
|
||||
- 完整的返回事件回调支持
|
||||
|
||||
详细使用说明请参考 [PageContainer 组件使用手册](docs/page-container.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) |
|
||||
|
||||
## 开发指南
|
||||
|
||||
|
||||
+34
@@ -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 处理
|
||||
@@ -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)
|
||||
@@ -0,0 +1,26 @@
|
||||
# 图片 URL 兼容工具(iOS ATS)
|
||||
|
||||
解决微信小程序在 iOS/Mac WKWebView 下 HTTP 图片因 ATS 限制无法加载的问题,位于 `miniprogram/utils/image.ts`。
|
||||
|
||||
## 背景
|
||||
|
||||
微信小程序在 iOS/Mac 平台的 WKWebView 下,`<image>` 组件加载 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'
|
||||
|
||||
// 推荐:直接获取可用于 <image> 显示的 URL(自动判断是否需要转 base64)
|
||||
const displayUrl = await getImageDisplayUrl('http://example.com/photo.jpg')
|
||||
|
||||
// 或手动将任意图片转为 base64
|
||||
const base64 = await urlToBase64('https://example.com/photo.jpg')
|
||||
```
|
||||
@@ -1,214 +0,0 @@
|
||||
# PageContainer 组件使用手册
|
||||
|
||||
## 概述
|
||||
|
||||
PageContainer 是一个基于 TDesign Navbar 组件封装的页面容器组件,提供了统一的导航栏和内容区域布局,简化了小程序页面的开发流程。组件自动处理返回按钮显示逻辑、安全区域适配等常见需求。
|
||||
|
||||
## 功能特性
|
||||
|
||||
- 基于 TDesign Navbar 组件,样式统一美观
|
||||
- 自动判断返回按钮显示(根据页面栈深度)
|
||||
- 支持安全区域适配(适配刘海屏等特殊设备)
|
||||
- 支持固定导航栏和占位符
|
||||
- 支持自定义导航栏样式(背景色、文本颜色)
|
||||
- 提供多个插槽支持自定义内容
|
||||
- 支持返回事件回调
|
||||
|
||||
## 引入组件
|
||||
|
||||
在页面的 `json` 配置文件中引入组件:
|
||||
|
||||
```json
|
||||
{
|
||||
"usingComponents": {
|
||||
"page-container": "../../components/page-container/page-container"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 基础用法
|
||||
|
||||
### 最简单的用法
|
||||
|
||||
```xml
|
||||
<page-container title="页面标题">
|
||||
<view>页面内容</view>
|
||||
</page-container>
|
||||
```
|
||||
|
||||
### 自定义导航栏样式
|
||||
|
||||
```xml
|
||||
<page-container
|
||||
title="页面标题"
|
||||
backgroundColor="#123321"
|
||||
textColor="#ffffff">
|
||||
<view>页面内容</view>
|
||||
</page-container>
|
||||
```
|
||||
|
||||
### 隐藏返回按钮
|
||||
|
||||
```xml
|
||||
<page-container title="页面标题" left-arrow="{{false}}">
|
||||
<view>页面内容</view>
|
||||
</page-container>
|
||||
```
|
||||
|
||||
### 隐藏导航栏
|
||||
|
||||
```xml
|
||||
<page-container title="页面标题" visible="{{false}}">
|
||||
<view>页面内容</view>
|
||||
</page-container>
|
||||
```
|
||||
|
||||
## 属性说明
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 说明 |
|
||||
|--------|------|--------|------|
|
||||
| title | String | '' | 导航栏标题 |
|
||||
| leftArrow | Boolean | true | 是否显示返回按钮 |
|
||||
| fixed | Boolean | true | 导航栏是否固定 |
|
||||
| placeholder | Boolean | true | 导航栏占位 |
|
||||
| safeAreaInsetTop | Boolean | true | 是否适配安全区域顶部 |
|
||||
| zIndex | Number | 10000 | 导航栏层级 |
|
||||
| animation | Boolean | true | 是否带动画效果 |
|
||||
| delta | Number | 1 | 后退的层数 |
|
||||
| titleMaxLength | Number | undefined | 标题最大长度,超出用 "…" 表示 |
|
||||
| visible | Boolean | true | 是否显示导航栏 |
|
||||
| backgroundColor | String | '#ffffff' | 导航栏背景色 |
|
||||
| textColor | String | '#000000' | 导航栏文本颜色(标题、返回按钮等) |
|
||||
|
||||
## 插槽说明
|
||||
|
||||
组件提供了三个插槽用于自定义导航栏内容:
|
||||
|
||||
### left 插槽
|
||||
|
||||
自定义左侧内容(返回按钮区域):
|
||||
|
||||
```xml
|
||||
<page-container title="页面标题">
|
||||
<view slot="left">自定义左侧内容</view>
|
||||
<view>页面内容</view>
|
||||
</page-container>
|
||||
```
|
||||
|
||||
### capsule 插槽
|
||||
|
||||
自定义胶囊区域内容:
|
||||
|
||||
```xml
|
||||
<page-container title="页面标题">
|
||||
<view slot="capsule">自定义胶囊内容</view>
|
||||
<view>页面内容</view>
|
||||
</page-container>
|
||||
```
|
||||
|
||||
### title 插槽
|
||||
|
||||
自定义标题区域内容:
|
||||
|
||||
```xml
|
||||
<page-container title="页面标题">
|
||||
<view slot="title">自定义标题内容</view>
|
||||
<view>页面内容</view>
|
||||
</page-container>
|
||||
```
|
||||
|
||||
## 事件说明
|
||||
|
||||
| 事件名 | 说明 | 回调参数 |
|
||||
|--------|------|----------|
|
||||
| success | 返回成功时触发 | event.detail |
|
||||
| fail | 返回失败时触发 | event.detail |
|
||||
| complete | 返回完成时触发(无论成功或失败) | event.detail |
|
||||
|
||||
### 事件使用示例
|
||||
|
||||
```xml
|
||||
<page-container
|
||||
title="页面标题"
|
||||
bind:success="onBackSuccess"
|
||||
bind:fail="onBackFail"
|
||||
bind:complete="onBackComplete">
|
||||
<view>页面内容</view>
|
||||
</page-container>
|
||||
```
|
||||
|
||||
```typescript
|
||||
Page({
|
||||
onBackSuccess(res) {
|
||||
console.log('返回成功', res)
|
||||
},
|
||||
onBackFail(err) {
|
||||
console.log('返回失败', err)
|
||||
},
|
||||
onBackComplete(res) {
|
||||
console.log('返回完成', res)
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
## 返回按钮显示逻辑
|
||||
|
||||
组件会自动判断是否显示返回按钮,判断规则如下:
|
||||
|
||||
1. 如果页面栈中只有一个页面(首页),则不显示返回按钮
|
||||
2. 如果 `leftArrow` 属性为 `false`,则不显示返回按钮
|
||||
3. 如果 `visible` 属性为 `false`,则不显示返回按钮
|
||||
4. 其他情况显示返回按钮
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 组件依赖 TDesign 的 `t-navbar` 组件,确保已正确安装和构建 TDesign 组件库
|
||||
2. 使用组件时,建议在页面的 `json` 配置中关闭原生导航栏:
|
||||
|
||||
```json
|
||||
{
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
```
|
||||
|
||||
3. 组件会自动处理返回按钮的显示逻辑,无需手动判断页面栈
|
||||
4. 如需自定义返回行为,可以通过监听 `success`、`fail`、`complete` 事件来实现
|
||||
5. 使用 `delta` 属性可以控制返回的页面层数,默认为 1(返回上一页)
|
||||
|
||||
## 完整示例
|
||||
|
||||
```xml
|
||||
<!-- pages/example/index.wxml -->
|
||||
<page-container
|
||||
title="示例页面"
|
||||
backgroundColor="#ffffff"
|
||||
textColor="#000000"
|
||||
left-arrow="{{true}}"
|
||||
fixed="{{true}}"
|
||||
placeholder="{{true}}"
|
||||
safe-area-inset-top="{{true}}"
|
||||
bind:success="onBackSuccess">
|
||||
<view class="content">
|
||||
<view>这是页面内容区域</view>
|
||||
</view>
|
||||
</page-container>
|
||||
```
|
||||
|
||||
```json
|
||||
// pages/example/index.json
|
||||
{
|
||||
"navigationStyle": "custom",
|
||||
"usingComponents": {
|
||||
"page-container": "../../components/page-container/page-container"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```typescript
|
||||
// pages/example/index.ts
|
||||
Page({
|
||||
onBackSuccess(res) {
|
||||
console.log('页面返回成功', res)
|
||||
},
|
||||
})
|
||||
```
|
||||
@@ -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 动画通过计数器管理,避免闪烁
|
||||
@@ -0,0 +1,268 @@
|
||||
# RootPageLayout 组件使用手册
|
||||
|
||||
## 概述
|
||||
|
||||
RootPageLayout 是一个基于 TDesign `t-navbar` 封装的页面容器组件,提供统一的导航栏与内容区域布局。组件内部已引入 `t-navbar`,并通过样式变量透传导航栏与容器背景配置,适用于需要自定义导航栏的页面。
|
||||
|
||||
## 功能特性
|
||||
|
||||
- 基于 TDesign Navbar 组件,样式统一美观
|
||||
- 根据页面栈深度与属性配置,自动计算返回按钮显示
|
||||
- 支持安全区域适配(顶部安全区由 `t-navbar` 处理,底部安全区由容器处理)
|
||||
- 支持固定导航栏和占位符
|
||||
- 支持自定义导航栏样式(背景色、文本颜色)
|
||||
- 支持自定义内容区域背景色
|
||||
- 提供多个插槽支持自定义导航栏内容与页面内容
|
||||
- 支持返回事件回调
|
||||
|
||||
## 引入组件
|
||||
|
||||
在页面的 `json` 配置文件中引入组件:
|
||||
|
||||
```json
|
||||
{
|
||||
"usingComponents": {
|
||||
"root-page-layout": "../../components/root-page-layout/root-page-layout"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 基础用法
|
||||
|
||||
### 最简单的用法
|
||||
|
||||
```xml
|
||||
<root-page-layout nav-bar-title="页面标题">
|
||||
<view>页面内容</view>
|
||||
</root-page-layout>
|
||||
```
|
||||
|
||||
### 自定义导航栏样式
|
||||
|
||||
```xml
|
||||
<root-page-layout
|
||||
nav-bar-title="页面标题"
|
||||
nav-bar-background-color="#123321"
|
||||
nav-bar-text-color="#ffffff"
|
||||
>
|
||||
<view>页面内容</view>
|
||||
</root-page-layout>
|
||||
```
|
||||
|
||||
### 隐藏返回按钮
|
||||
|
||||
```xml
|
||||
<root-page-layout nav-bar-title="页面标题" nav-bar-left-arrow="{{false}}">
|
||||
<view>页面内容</view>
|
||||
</root-page-layout>
|
||||
```
|
||||
|
||||
### 隐藏导航栏
|
||||
|
||||
```xml
|
||||
<root-page-layout nav-bar-title="页面标题" nav-bar-visible="{{false}}">
|
||||
<view>页面内容</view>
|
||||
</root-page-layout>
|
||||
```
|
||||
|
||||
### 关闭底部安全区适配
|
||||
|
||||
```xml
|
||||
<root-page-layout
|
||||
nav-bar-title="页面标题"
|
||||
container-safe-area-inset-bottom="{{false}}"
|
||||
>
|
||||
<view>页面内容</view>
|
||||
</root-page-layout>
|
||||
```
|
||||
|
||||
## 样式与主题
|
||||
|
||||
### 容器背景色
|
||||
|
||||
组件通过样式变量 `--pg-container-bg-color` 设置容器背景色,对应属性 `containerBackgroundColor`。
|
||||
|
||||
### 导航栏颜色
|
||||
|
||||
组件通过 `t-navbar` 的样式变量设置导航栏颜色:
|
||||
|
||||
- `--td-navbar-bg-color`:对应 `navBarBackgroundColor`
|
||||
- `--td-navbar-color`:对应 `navBarTextColor`
|
||||
|
||||
## 属性说明
|
||||
|
||||
### 导航栏相关
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 说明 |
|
||||
|--------|------|--------|------|
|
||||
| navBarTitle | String | '' | 导航栏标题 |
|
||||
| navBarLeftArrow | Boolean | true | 是否显示返回按钮 |
|
||||
| navBarFixed | Boolean | true | 导航栏是否固定 |
|
||||
| navBarPlaceholder | Boolean | true | 导航栏占位 |
|
||||
| navBarSafeAreaInsetTop | Boolean | true | 是否适配安全区域顶部 |
|
||||
| navBarZIndex | Number | 10000 | 导航栏层级 |
|
||||
| navBarAnimation | Boolean | true | 是否带动画效果 |
|
||||
| navBarTitleMaxLength | Number | undefined | 标题最大长度,超出用 "…" 表示 |
|
||||
| navBarVisible | Boolean | true | 是否显示导航栏 |
|
||||
| navBarBackgroundColor | String | '#ffffff' | 导航栏背景色 |
|
||||
| navBarTextColor | String | '#000000' | 导航栏文本颜色(标题、返回按钮等) |
|
||||
|
||||
### 内容容器相关
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 说明 |
|
||||
|--------|------|--------|------|
|
||||
| containerSafeAreaInsetBottom | Boolean | true | 是否开启底部安全区适配 |
|
||||
| containerBackgroundColor | String | '#f5f5f5' | 内容区域背景色 |
|
||||
|
||||
## 插槽说明
|
||||
|
||||
组件提供导航栏插槽与默认插槽,用于自定义页面内容布局。
|
||||
|
||||
### 默认插槽
|
||||
|
||||
默认插槽用于渲染页面主体内容(位于内容区域 `root-page-layout__content` 内):
|
||||
|
||||
```xml
|
||||
<root-page-layout nav-bar-title="页面标题">
|
||||
<view>页面内容</view>
|
||||
</root-page-layout>
|
||||
```
|
||||
|
||||
### left 插槽
|
||||
|
||||
自定义左侧内容(返回按钮区域):
|
||||
|
||||
```xml
|
||||
<root-page-layout nav-bar-title="页面标题">
|
||||
<view slot="left">自定义左侧内容</view>
|
||||
<view>页面内容</view>
|
||||
</root-page-layout>
|
||||
```
|
||||
|
||||
### capsule 插槽
|
||||
|
||||
自定义胶囊区域内容:
|
||||
|
||||
```xml
|
||||
<root-page-layout nav-bar-title="页面标题">
|
||||
<view slot="capsule">自定义胶囊内容</view>
|
||||
<view>页面内容</view>
|
||||
</root-page-layout>
|
||||
```
|
||||
|
||||
### title 插槽
|
||||
|
||||
自定义标题区域内容:
|
||||
|
||||
```xml
|
||||
<root-page-layout nav-bar-title="页面标题">
|
||||
<view slot="title">自定义标题内容</view>
|
||||
<view>页面内容</view>
|
||||
</root-page-layout>
|
||||
```
|
||||
|
||||
## 事件说明
|
||||
|
||||
| 事件名 | 说明 | 回调参数 |
|
||||
|--------|------|----------|
|
||||
| success | `wx.navigateBack` 成功时触发 | `event.detail`(navigateBack 成功回调参数) |
|
||||
| fail | `wx.navigateBack` 失败时触发 | `event.detail`(navigateBack 失败回调参数) |
|
||||
| complete | `wx.navigateBack` 完成时触发(无论成功或失败) | `event.detail`(success/fail 的参数之一) |
|
||||
|
||||
### 事件使用示例
|
||||
|
||||
```xml
|
||||
<root-page-layout
|
||||
nav-bar-title="页面标题"
|
||||
bind:success="onBackSuccess"
|
||||
bind:fail="onBackFail"
|
||||
bind:complete="onBackComplete"
|
||||
>
|
||||
<view>页面内容</view>
|
||||
</root-page-layout>
|
||||
```
|
||||
|
||||
```typescript
|
||||
Page({
|
||||
onBackSuccess(res) {
|
||||
console.log('返回成功', res)
|
||||
},
|
||||
onBackFail(err) {
|
||||
console.log('返回失败', err)
|
||||
},
|
||||
onBackComplete(res) {
|
||||
console.log('返回完成', res)
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
## 返回按钮显示逻辑
|
||||
|
||||
组件在实例进入页面节点树时,根据以下条件计算返回按钮是否显示:
|
||||
|
||||
```text
|
||||
showLeftArrow = getCurrentPages().length > 1 && navBarLeftArrow && navBarVisible
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 组件内部依赖 TDesign 的 `t-navbar`(组件自身已在 `usingComponents` 中引入),确保项目已正确集成 `tdesign-miniprogram`
|
||||
2. 使用组件时,建议在页面的 `json` 配置中关闭原生导航栏:
|
||||
|
||||
```json
|
||||
{
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
```
|
||||
|
||||
3. 组件会自动处理返回按钮的显示逻辑,无需手动判断页面栈
|
||||
4. 如需自定义返回行为,可以通过监听 `success`、`fail`、`complete` 事件来实现
|
||||
|
||||
## 完整示例
|
||||
|
||||
```xml
|
||||
<!-- pages/example/index.wxml -->
|
||||
<root-page-layout
|
||||
nav-bar-title="示例页面"
|
||||
nav-bar-background-color="#ffffff"
|
||||
nav-bar-text-color="#000000"
|
||||
nav-bar-left-arrow="{{true}}"
|
||||
nav-bar-fixed="{{true}}"
|
||||
nav-bar-placeholder="{{true}}"
|
||||
nav-bar-safe-area-inset-top="{{true}}"
|
||||
container-background-color="#f5f5f5"
|
||||
container-safe-area-inset-bottom="{{true}}"
|
||||
bind:success="onBackSuccess"
|
||||
bind:fail="onBackFail"
|
||||
bind:complete="onBackComplete"
|
||||
>
|
||||
<view class="content">
|
||||
<view>这是页面内容区域</view>
|
||||
</view>
|
||||
</root-page-layout>
|
||||
```
|
||||
|
||||
```json
|
||||
// pages/example/index.json
|
||||
{
|
||||
"navigationStyle": "custom",
|
||||
"usingComponents": {
|
||||
"root-page-layout": "../../components/root-page-layout/root-page-layout"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```typescript
|
||||
// pages/example/index.ts
|
||||
Page({
|
||||
onBackSuccess(res) {
|
||||
console.log('页面返回成功', res)
|
||||
},
|
||||
onBackFail(err) {
|
||||
console.log('页面返回失败', err)
|
||||
},
|
||||
onBackComplete(res) {
|
||||
console.log('页面返回完成', res)
|
||||
},
|
||||
})
|
||||
```
|
||||
@@ -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 |
|
||||
+31
-5
@@ -1,4 +1,6 @@
|
||||
// app.ts
|
||||
import { getPlatformInfo } from './utils/platform'
|
||||
|
||||
App<IAppOption>({
|
||||
globalData: {
|
||||
navBarHeight: 0, // 导航栏总高度
|
||||
@@ -10,19 +12,43 @@ App<IAppOption>({
|
||||
tabbarHeight: 0, // 底部导航栏高度
|
||||
safeAreaBottom: 0, // 底部安全区域坐标
|
||||
safeAreaInsetBottom: 0, // 中间区域内容的底边坐标
|
||||
system: '', // 系统信息
|
||||
platformInfo: {
|
||||
hardware: {
|
||||
deviceClass: 'unknown',
|
||||
brand: '',
|
||||
model: '',
|
||||
abi: '',
|
||||
deviceAbi: '',
|
||||
cpuType: '',
|
||||
memorySize: '',
|
||||
},
|
||||
software: {
|
||||
clientPlatform: 'unknown',
|
||||
rawClientPlatform: '',
|
||||
operatingSystem: 'unknown',
|
||||
system: '',
|
||||
wechatVersion: '',
|
||||
baseLibraryVersion: '',
|
||||
hostAppId: '',
|
||||
},
|
||||
isMobile: false,
|
||||
isDesktop: false,
|
||||
isDevtools: false,
|
||||
},
|
||||
token: '',
|
||||
},
|
||||
onLaunch() {
|
||||
// 1. 获取胶囊按钮位置信息
|
||||
// 获取小程序宿主环境信息
|
||||
const platformInfo = getPlatformInfo()
|
||||
this.globalData.platformInfo = platformInfo
|
||||
|
||||
// 获取胶囊按钮位置信息
|
||||
const menuButtonInfo = wx.getMenuButtonBoundingClientRect()
|
||||
|
||||
// 2. 获取系统窗口信息
|
||||
// 获取系统窗口信息
|
||||
const windowInfo = wx.getWindowInfo()
|
||||
const deviceInfo = wx.getDeviceInfo()
|
||||
|
||||
// 赋值基础信息
|
||||
this.globalData.system = deviceInfo.system
|
||||
this.globalData.menuRight = windowInfo.screenWidth - menuButtonInfo.right
|
||||
this.globalData.menuTop = menuButtonInfo.top
|
||||
this.globalData.menuHeight = menuButtonInfo.height
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
/* components/page-container/page-container.wxss */
|
||||
|
||||
.page-container {
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.page-container__content {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
overflow-y: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
<!--components/page-container/page-container.wxml-->
|
||||
<view class="page-container">
|
||||
<!-- 导航栏 -->
|
||||
<t-navbar
|
||||
title="{{title}}"
|
||||
left-arrow="{{showLeftArrow}}"
|
||||
fixed="{{fixed}}"
|
||||
placeholder="{{placeholder}}"
|
||||
safe-area-inset-top="{{safeAreaInsetTop}}"
|
||||
z-index="{{zIndex}}"
|
||||
animation="{{animation}}"
|
||||
delta="{{delta}}"
|
||||
title-max-length="{{titleMaxLength}}"
|
||||
visible="{{visible}}"
|
||||
bind:go-back="goBack"
|
||||
bind:success="handleBackSuccess"
|
||||
bind:fail="handleBackFail"
|
||||
bind:complete="handleBackComplete"
|
||||
style="--td-navbar-color: {{textColor}};--td-navbar-bg-color: {{backgroundColor}};"
|
||||
>
|
||||
<!-- 左侧自定义内容插槽 -->
|
||||
<slot name="left" slot="left"></slot>
|
||||
<!-- 胶囊区域自定义内容插槽 -->
|
||||
<slot name="capsule" slot="capsule"></slot>
|
||||
<!-- 标题自定义内容插槽 -->
|
||||
<slot name="title" slot="title"></slot>
|
||||
</t-navbar>
|
||||
|
||||
<!-- 内容区域 -->
|
||||
<view
|
||||
class="page-container__content"
|
||||
>
|
||||
<slot></slot>
|
||||
</view>
|
||||
</view>
|
||||
File renamed without changes.
@@ -0,0 +1,26 @@
|
||||
/* components/root-page-layout/root-page-layout.wxss */
|
||||
|
||||
.root-page-layout {
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
background-color: var(--pg-container-bg-color);
|
||||
|
||||
// 预留安全区
|
||||
&--safe {
|
||||
padding-bottom: constant(safe-area-inset-bottom);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
}
|
||||
|
||||
.root-page-layout__content {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
+39
-59
@@ -1,4 +1,4 @@
|
||||
// components/page-container/page-container.ts
|
||||
// components/root-page-layout/root-page-layout.ts
|
||||
|
||||
Component({
|
||||
/**
|
||||
@@ -6,65 +6,70 @@ Component({
|
||||
*/
|
||||
properties: {
|
||||
// 导航栏标题
|
||||
title: {
|
||||
navBarTitle: {
|
||||
type: String,
|
||||
value: '',
|
||||
},
|
||||
// 是否显示返回按钮
|
||||
leftArrow: {
|
||||
navBarLeftArrow: {
|
||||
type: Boolean,
|
||||
value: true,
|
||||
},
|
||||
// 导航栏是否固定
|
||||
fixed: {
|
||||
navBarFixed: {
|
||||
type: Boolean,
|
||||
value: true,
|
||||
},
|
||||
// 导航栏占位
|
||||
placeholder: {
|
||||
navBarPlaceholder: {
|
||||
type: Boolean,
|
||||
value: true,
|
||||
},
|
||||
// 是否适配安全区域顶部
|
||||
safeAreaInsetTop: {
|
||||
navBarSafeAreaInsetTop: {
|
||||
type: Boolean,
|
||||
value: true,
|
||||
},
|
||||
// 导航栏层级
|
||||
zIndex: {
|
||||
navBarZIndex: {
|
||||
type: Number,
|
||||
value: 10000,
|
||||
},
|
||||
// 是否带动画效果
|
||||
animation: {
|
||||
navBarAnimation: {
|
||||
type: Boolean,
|
||||
value: true,
|
||||
},
|
||||
// 后退的层数
|
||||
delta: {
|
||||
type: Number,
|
||||
value: 1,
|
||||
},
|
||||
// 标题最大长度,超出用 "…" 表示
|
||||
titleMaxLength: {
|
||||
navBarTitleMaxLength: {
|
||||
type: Number,
|
||||
value: undefined,
|
||||
},
|
||||
// 是否显示导航栏
|
||||
visible: {
|
||||
navBarVisible: {
|
||||
type: Boolean,
|
||||
value: true,
|
||||
},
|
||||
// 导航栏背景色
|
||||
backgroundColor: {
|
||||
navBarBackgroundColor: {
|
||||
type: String,
|
||||
value: '#ffffff',
|
||||
},
|
||||
// 导航栏文本颜色(标题、返回按钮等)
|
||||
textColor: {
|
||||
navBarTextColor: {
|
||||
type: String,
|
||||
value: '#000000',
|
||||
},
|
||||
// 是否开启底部安全区适配
|
||||
containerSafeAreaInsetBottom: {
|
||||
type: Boolean,
|
||||
value: true,
|
||||
},
|
||||
// rootPageLayout 背景色
|
||||
containerBackgroundColor: {
|
||||
type: String,
|
||||
value: '#f5f5f5',
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -106,8 +111,8 @@ Component({
|
||||
*/
|
||||
checkShowLeftArrow() {
|
||||
const pages = getCurrentPages()
|
||||
const leftArrow = this.properties.leftArrow
|
||||
const visible = this.properties.visible
|
||||
const leftArrow = this.properties.navBarLeftArrow
|
||||
const visible = this.properties.navBarVisible
|
||||
const showLeftArrow = pages.length > 1 && leftArrow && visible
|
||||
this.setData({
|
||||
showLeftArrow,
|
||||
@@ -118,46 +123,21 @@ Component({
|
||||
* 返回按钮点击事件
|
||||
*/
|
||||
goBack() {
|
||||
const pages = getCurrentPages()
|
||||
const delta = this.properties.delta
|
||||
if (pages.length > 1 && delta > 0) {
|
||||
wx.navigateBack({
|
||||
delta,
|
||||
success: (res) => {
|
||||
// 转发成功事件
|
||||
this.triggerEvent('success', res)
|
||||
// 转发完成事件
|
||||
this.triggerEvent('complete', res)
|
||||
},
|
||||
fail: (err) => {
|
||||
// 转发失败事件
|
||||
this.triggerEvent('fail', err)
|
||||
// 转发完成事件
|
||||
this.triggerEvent('complete', err)
|
||||
},
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 处理返回成功事件(从 t-navbar 转发)
|
||||
*/
|
||||
handleBackSuccess(e: WechatMiniprogram.CustomEvent) {
|
||||
this.triggerEvent('success', e.detail)
|
||||
},
|
||||
|
||||
/**
|
||||
* 处理返回失败事件(从 t-navbar 转发)
|
||||
*/
|
||||
handleBackFail(e: WechatMiniprogram.CustomEvent) {
|
||||
this.triggerEvent('fail', e.detail)
|
||||
},
|
||||
|
||||
/**
|
||||
* 处理返回完成事件(从 t-navbar 转发)
|
||||
*/
|
||||
handleBackComplete(e: WechatMiniprogram.CustomEvent) {
|
||||
this.triggerEvent('complete', e.detail)
|
||||
console.warn('点击了箭头按钮')
|
||||
wx.navigateBack({
|
||||
success: (res) => {
|
||||
// 转发成功事件
|
||||
this.triggerEvent('success', res)
|
||||
// 转发完成事件
|
||||
this.triggerEvent('complete', res)
|
||||
},
|
||||
fail: (err) => {
|
||||
// 转发失败事件
|
||||
this.triggerEvent('fail', err)
|
||||
// 转发完成事件
|
||||
this.triggerEvent('complete', err)
|
||||
},
|
||||
})
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,34 @@
|
||||
<!--components/root-page-layout/root-page-layout.wxml-->
|
||||
<view
|
||||
class="root-page-layout {{containerSafeAreaInsetBottom ? 'root-page-layout--safe' : ''}}"
|
||||
style="--pg-container-bg-color: {{containerBackgroundColor}};"
|
||||
>
|
||||
<!-- 导航栏 -->
|
||||
<t-navbar
|
||||
title="{{navBarTitle}}"
|
||||
left-arrow="{{showLeftArrow}}"
|
||||
fixed="{{navBarFixed}}"
|
||||
placeholder="{{navBarPlaceholder}}"
|
||||
safe-area-inset-top="{{navBarSafeAreaInsetTop}}"
|
||||
z-index="{{navBarZIndex}}"
|
||||
animation="{{navBarAnimation}}"
|
||||
title-max-length="{{navBarTitleMaxLength}}"
|
||||
visible="{{navBarVisible}}"
|
||||
bind:go-back="goBack"
|
||||
style="--td-navbar-color: {{navBarTextColor}};--td-navbar-bg-color: {{navBarBackgroundColor}};"
|
||||
>
|
||||
<!-- 左侧自定义内容插槽 -->
|
||||
<slot name="left" slot="left"></slot>
|
||||
<!-- 胶囊区域自定义内容插槽 -->
|
||||
<slot name="capsule" slot="capsule"></slot>
|
||||
<!-- 标题自定义内容插槽 -->
|
||||
<slot name="title" slot="title"></slot>
|
||||
</t-navbar>
|
||||
|
||||
<!-- 内容区域 -->
|
||||
<view
|
||||
class="root-page-layout__content"
|
||||
>
|
||||
<slot></slot>
|
||||
</view>
|
||||
</view>
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"usingComponents": {
|
||||
"page-container": "../../components/page-container/page-container"
|
||||
"root-page-layout": "../../components/root-page-layout/root-page-layout"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--pages/date/date.wxml-->
|
||||
<page-container title="日期时间格式化示例" backgroundColor="#123321" textColor="#ffffff">
|
||||
<root-page-layout navBarTitle="日期时间格式化示例" navBarBackgroundColor="#123321" navBarTextColor="#ffffff">
|
||||
<view class="container">
|
||||
<!-- formatTime 函数示例 -->
|
||||
<view class="section">
|
||||
@@ -69,4 +69,4 @@
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</page-container>
|
||||
</root-page-layout>
|
||||
@@ -3,7 +3,7 @@
|
||||
"navigationBarBackgroundColor": "#FFFFFF",
|
||||
"navigationBarTextStyle": "black",
|
||||
"usingComponents": {
|
||||
"page-container": "../../components/page-container/page-container",
|
||||
"root-page-layout": "../../components/root-page-layout/root-page-layout",
|
||||
"t-input": "/miniprogram_npm/tdesign-miniprogram/input/input",
|
||||
"t-button": "/miniprogram_npm/tdesign-miniprogram/button/button"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--pages/login/login.wxml-->
|
||||
<page-container title="Request 使用示例">
|
||||
<root-page-layout title="Request 使用示例">
|
||||
<view class="login-container">
|
||||
<!-- 登录表单区域 -->
|
||||
<view class="form-section">
|
||||
@@ -27,4 +27,4 @@
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</page-container>
|
||||
</root-page-layout>
|
||||
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* 图片工具 — iOS ATS 兼容处理
|
||||
*
|
||||
* 背景:微信小程序在 iOS WKWebView 下,<image> 组件加载 HTTP 图片会被 ATS 拦截,
|
||||
* 必须先通过 wx.request 下载为 ArrayBuffer,再转为 base64 data URL 才能正常显示。
|
||||
*/
|
||||
|
||||
// ==================== 图片 URL 兼容(iOS ATS) ====================
|
||||
|
||||
/**
|
||||
* 将网络图片转为 base64,用于 iOS 图片回显等场景
|
||||
* 使用 wx.request 直接请求,避免经过 axios 的 StandardResponse 拦截逻辑
|
||||
* @param url 图片完整 URL
|
||||
* @returns Promise<data:image/xxx;base64,xxx> 格式的 base64 字符串
|
||||
*/
|
||||
function urlToBase64(url: string): Promise<string> {
|
||||
if (!url || typeof url !== 'string') {
|
||||
return Promise.reject(new Error('url 不能为空'))
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
wx.request({
|
||||
url,
|
||||
method: 'GET',
|
||||
responseType: 'arraybuffer',
|
||||
success: (res) => {
|
||||
if (res.statusCode !== 200) {
|
||||
reject(new Error(`请求失败: ${res.statusCode}`))
|
||||
return
|
||||
}
|
||||
const arrayBuffer = res.data as ArrayBuffer
|
||||
const base64 = wx.arrayBufferToBase64(arrayBuffer)
|
||||
const ext = (url.match(/\.(jpg|jpeg|png|gif|webp)/i) || ['', 'png'])[1]
|
||||
resolve(`data:image/${ext};base64,${base64}`)
|
||||
},
|
||||
fail: (err) => reject(err),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取图片显示用的 URL,仅在 HTTP + iOS/Mac 时转为 base64 以解决 ATS 回显问题
|
||||
* - HTTPS 链接:任何平台均直接返回原始 URL
|
||||
* - HTTP 链接 + iOS/Mac:转为 base64
|
||||
* - HTTP 链接 + 其他平台:直接返回原始 URL
|
||||
* @param url 完整的图片 URL(必须是 http:// 或 https:// 开头)
|
||||
* @returns Promise<string> 返回可用的 url(网络链接或 base64 字符串)
|
||||
*/
|
||||
async function getImageDisplayUrl(url: string): Promise<string> {
|
||||
const isFullUrl = /^https?:\/\//i.test(url)
|
||||
if (!isFullUrl) {
|
||||
console.warn(
|
||||
'[getImageDisplayUrl] 传入的不是完整 URL,请传入 http:// 或 https:// 开头的完整链接',
|
||||
)
|
||||
return url
|
||||
}
|
||||
|
||||
const app: IAppOption = getApp()
|
||||
const { clientPlatform } = app.globalData.platformInfo.software
|
||||
|
||||
const isHttp = /^http:\/\//i.test(url)
|
||||
const isIosOrMac = clientPlatform === 'ios' || clientPlatform === 'mac'
|
||||
|
||||
if (isHttp && isIosOrMac) {
|
||||
return urlToBase64(url).catch(() => url)
|
||||
}
|
||||
|
||||
return url
|
||||
}
|
||||
|
||||
export { urlToBase64, getImageDisplayUrl }
|
||||
@@ -0,0 +1,81 @@
|
||||
const MOBILE_CLIENT_PLATFORMS: ClientPlatformType[] = ['ios', 'android', 'ohos']
|
||||
const DESKTOP_CLIENT_PLATFORMS: ClientPlatformType[] = ['windows', 'mac', 'ohos_pc']
|
||||
|
||||
function normalizeClientPlatform(platform: string): ClientPlatformType {
|
||||
switch (platform.toLowerCase()) {
|
||||
case 'ios':
|
||||
case 'android':
|
||||
case 'ohos':
|
||||
case 'ohos_pc':
|
||||
case 'windows':
|
||||
case 'mac':
|
||||
case 'devtools':
|
||||
return platform.toLowerCase() as ClientPlatformType
|
||||
default:
|
||||
return 'unknown'
|
||||
}
|
||||
}
|
||||
|
||||
function resolveOperatingSystem(platform: ClientPlatformType): OperatingSystemType {
|
||||
switch (platform) {
|
||||
case 'ios':
|
||||
return 'ios'
|
||||
case 'android':
|
||||
return 'android'
|
||||
case 'ohos':
|
||||
case 'ohos_pc':
|
||||
return 'harmonyos'
|
||||
case 'windows':
|
||||
return 'windows'
|
||||
case 'mac':
|
||||
return 'macos'
|
||||
case 'devtools':
|
||||
return 'devtools'
|
||||
default:
|
||||
return 'unknown'
|
||||
}
|
||||
}
|
||||
|
||||
function resolveDeviceClass(platform: ClientPlatformType): HardwareDeviceClassType {
|
||||
if (MOBILE_CLIENT_PLATFORMS.includes(platform)) {
|
||||
return 'mobile'
|
||||
}
|
||||
|
||||
if (DESKTOP_CLIENT_PLATFORMS.includes(platform)) {
|
||||
return 'desktop'
|
||||
}
|
||||
|
||||
return platform === 'devtools' ? 'devtools' : 'unknown'
|
||||
}
|
||||
|
||||
export function getPlatformInfo(): PlatformInfoType {
|
||||
const deviceInfo = wx.getDeviceInfo()
|
||||
const appBaseInfo = wx.getAppBaseInfo()
|
||||
const rawClientPlatform = deviceInfo.platform || ''
|
||||
const clientPlatform = normalizeClientPlatform(rawClientPlatform)
|
||||
const deviceClass = resolveDeviceClass(clientPlatform)
|
||||
|
||||
return {
|
||||
hardware: {
|
||||
deviceClass,
|
||||
brand: deviceInfo.brand || '',
|
||||
model: deviceInfo.model || '',
|
||||
abi: deviceInfo.abi || '',
|
||||
deviceAbi: deviceInfo.deviceAbi || '',
|
||||
cpuType: deviceInfo.cpuType || '',
|
||||
memorySize: deviceInfo.memorySize || '',
|
||||
},
|
||||
software: {
|
||||
clientPlatform,
|
||||
rawClientPlatform,
|
||||
operatingSystem: resolveOperatingSystem(clientPlatform),
|
||||
system: deviceInfo.system || '',
|
||||
wechatVersion: appBaseInfo.version || '',
|
||||
baseLibraryVersion: appBaseInfo.SDKVersion || '',
|
||||
hostAppId: appBaseInfo.host?.appId || '',
|
||||
},
|
||||
isMobile: deviceClass === 'mobile',
|
||||
isDesktop: deviceClass === 'desktop',
|
||||
isDevtools: deviceClass === 'devtools',
|
||||
}
|
||||
}
|
||||
+5
-1
@@ -38,7 +38,11 @@
|
||||
"swc": true,
|
||||
"disableSWC": false,
|
||||
"disableUseStrict": false,
|
||||
"urlCheck": false
|
||||
"urlCheck": false,
|
||||
"sassSetting": {
|
||||
"sassCommonUseFilePath": "global.sass",
|
||||
"scssCommonUseFilePath": "global.scss"
|
||||
}
|
||||
},
|
||||
"simulatorType": "wechat",
|
||||
"simulatorPluginLibVersion": {},
|
||||
|
||||
Vendored
+38
-1
@@ -1,6 +1,43 @@
|
||||
/// <reference path="./types/index.d.ts" />
|
||||
/// <reference path="./npm.d.ts" />
|
||||
|
||||
/** 微信小程序当前运行的客户端平台,对应 wx.getDeviceInfo().platform */
|
||||
type ClientPlatformType = 'ios' | 'android' | 'ohos' | 'ohos_pc' | 'windows' | 'mac' | 'devtools' | 'unknown'
|
||||
|
||||
/** 根据客户端平台归一化后的操作系统类型 */
|
||||
type OperatingSystemType = 'ios' | 'android' | 'harmonyos' | 'windows' | 'macos' | 'devtools' | 'unknown'
|
||||
|
||||
/** 根据客户端平台归类的硬件设备类别 */
|
||||
type HardwareDeviceClassType = 'mobile' | 'desktop' | 'devtools' | 'unknown'
|
||||
|
||||
interface HardwarePlatformInfoType {
|
||||
deviceClass: HardwareDeviceClassType
|
||||
brand: string
|
||||
model: string
|
||||
abi: string
|
||||
deviceAbi: string
|
||||
cpuType: string
|
||||
memorySize: string
|
||||
}
|
||||
|
||||
interface SoftwarePlatformInfoType {
|
||||
clientPlatform: ClientPlatformType
|
||||
rawClientPlatform: string
|
||||
operatingSystem: OperatingSystemType
|
||||
system: string
|
||||
wechatVersion: string
|
||||
baseLibraryVersion: string
|
||||
hostAppId: string
|
||||
}
|
||||
|
||||
interface PlatformInfoType {
|
||||
hardware: HardwarePlatformInfoType
|
||||
software: SoftwarePlatformInfoType
|
||||
isMobile: boolean
|
||||
isDesktop: boolean
|
||||
isDevtools: boolean
|
||||
}
|
||||
|
||||
interface IAppOption {
|
||||
globalData: {
|
||||
navBarHeight: number,
|
||||
@@ -12,7 +49,7 @@ interface IAppOption {
|
||||
tabbarHeight: number,
|
||||
safeAreaBottom: number,
|
||||
safeAreaInsetBottom: number
|
||||
system: string,
|
||||
platformInfo: PlatformInfoType
|
||||
token: string
|
||||
userInfo?: WechatMiniprogram.UserInfo
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user