@trimjs/web-app
v0.2.2
Published
web app sdk for nas
Readme
@trimjs/web-app
一个用于应用与基座平台通讯的 SDK。
简介
@trimjs/web-app 是一个跨平台通讯 SDK,让你的应用能够轻松调用基座平台的能力。
核心特性:
- 🌐 跨平台支持 - 同时支持在 Web iframe 和 App WebView 中运行
- 🎯 统一 API - 提供统一的接口调用基座平台能力(如设置标题、打开文件等)
- 🔧 自动兼容 - 开发者无需关心底层平台差异,SDK 自动处理兼容性
- 📦 开箱即用 - 简单初始化即可使用,无需复杂配置
安装
使用 npm、yarn 或 pnpm 安装:
# npm
npm install @trimjs/web-app
# yarn
yarn add @trimjs/web-app
# pnpm
pnpm add @trimjs/web-app快速开始
基础使用
import { TrimApp } from "@trimjs/web-app";
// 创建实例
const app = new TrimApp();
// 调用方法
await app.setTitle("我的应用");完整示例
import { TrimApp } from "@trimjs/web-app";
// 创建实例
const app = new TrimApp({
debug: true, // 开启调试模式,会输出日志
});
// 设置应用标题
await app.setTitle("我的应用");
// 打开文件
await app.openFile("/path/to/file.pdf");
// 打开文件管理器
await app.openFileManager("/path/to/folder");
// 设置退出页面提示
await app.setExitPageTips({
title: "确认退出?",
content: "你有未保存的更改",
});
// 获取平台配置
const config = await app.getPlatformConfig();
console.log("主题:", config.theme);
console.log("语言:", config.language);
console.log("日期格式:", config.format.date);
console.log("时间格式:", config.format.time);
// 请求接口(仅 Web 平台)
if (app.isWeb) {
const response = await app.query({
api: "xxx.FileStation.List",
method: "list",
version: 2,
params: { folder_path: "/home" },
});
console.log("文件列表:", response);
}
// 选择文件
const selectedFiles = await app.pickFile({
multiple: true,
accept: [".pdf", ".doc", ".docx"],
title: "选择文档文件",
});
console.log("选中的文件:", selectedFiles);注意:所有方法都会自动等待 SDK 初始化完成,你可以直接调用,无需手动等待。
API 文档
TrimApp
构造函数
new TrimApp(options?: TrimAppOptions)创建 TrimApp 实例。
参数:
options(可选) - 配置选项debug?: boolean- 是否开启调试模式,默认false
示例:
// 不带配置
const app = new TrimApp();
// 开启调试模式
const app = new TrimApp({ debug: true });setTitle
async setTitle(title: string): Promise<void | null>设置应用标题。
参数:
title: string- 标题文本
返回值:
- 成功时返回
Promise<void> - 失败时返回
Promise<null>
示例:
await app.setTitle("我的应用");openFile
async openFile(path: string): Promise<void | null>通过应用打开指定文件。
参数:
path: string- 文件路径
返回值:
- 成功时返回
Promise<void> - 失败时返回
Promise<null>
示例:
await app.openFile("/path/to/document.pdf");openFileManager
async openFileManager(path: string): Promise<void | null>打开文件管理器并定位到指定路径。
参数:
path: string- 文件夹路径
返回值:
- 成功时返回
Promise<void> - 失败时返回
Promise<null>
示例:
await app.openFileManager("/path/to/folder");setExitPageTips
async setExitPageTips(params?: { title?: string; content?: string }): Promise<void | null>设置退出页面时的提示信息。
参数:
params(可选) - 提示参数title?: string- 提示标题content?: string- 提示内容
返回值:
- 成功时返回
Promise<void> - 失败时返回
Promise<null>
示例:
// 设置提示
await app.setExitPageTips({
title: "确认退出?",
content: "你有未保存的更改",
});
// 清除提示
await app.setExitPageTips();getPlatformConfig
async getPlatformConfig(): Promise<PlatformConfig>获取平台配置信息,包括主题、语言、日期时间格式等。
返回值:
Promise<PlatformConfig>- 平台配置对象
PlatformConfig 结构:
interface PlatformConfig {
theme: "dark" | "light"; // 主题模式
language: string; // 语言代码,如 "zh-CN"、"en-US"
appVersion?: string; // 应用版本号
systemVersion: string; // 系统版本号
format: {
date?: string; // 日期格式,如 "YYYY-MM-DD"、"MM-DD-YYYY"
time?: string; // 时间格式,"12h" 或 "24h"
};
}示例:
const config = await app.getPlatformConfig();
// 根据主题调整 UI
if (config.theme === "dark") {
document.body.classList.add("dark-mode");
}
// 根据语言设置国际化
i18n.setLocale(config.language);
// 根据日期格式格式化日期
const dateFormatter = new Intl.DateTimeFormat(config.language, {
dateStyle: "short",
});close
async close(): Promise<void>关闭应用。
返回值:
Promise<void>
示例:
app.close();ready
async ready(): Promise<void>等待 SDK 初始化完成。虽然所有方法都会自动等待初始化,但在某些场景下你可能需要显式等待。
返回值:
Promise<void>- 初始化完成时 resolve
示例:
const app = new TrimApp();
// 显式等待初始化完成
await app.ready();
// 现在可以安全地访问属性
console.log("当前平台:", app.isWeb ? "Web" : "Mobile");getOptions
getOptions(): TrimAppOptions获取当前配置选项。
返回值:
TrimAppOptions- 当前配置对象
示例:
const options = app.getOptions();
console.log("调试模式:", options.debug);setOptions
setOptions(options: Partial<TrimAppOptions>): void更新配置选项。
参数:
options: Partial<TrimAppOptions>- 要更新的配置项
示例:
// 开启调试模式
app.setOptions({ debug: true });
// 关闭调试模式
app.setOptions({ debug: false });query
async query<T>(params: any, config?: QueryConfig): Promise<ResponseData<T>>请求接口(仅支持 Web 平台)。用于调用 NAS 系统的 API。
参数:
params: any- 请求参数,通常包含 API 名称、方法、版本等信息config?: QueryConfig- 可选配置timer?: boolean- 是否是定时器请求observable?: boolean- 是否返回 ObservableskipPreflight?: boolean- 是否跳过预检请求autoFailTips?: boolean- 是否自动显示失败提示
返回值:
Promise<ResponseData<T>>- 响应数据
ResponseData 结构:
interface ResponseData<T> {
reqid: string; // 请求 ID
result: ResponseStatus; // 响应状态:'succ' | 'fail' | 'cancel' | 'doing'
taskId?: string; // 任务 ID(如果适用)
type?: string; // 类型
errmsg?: string; // 错误消息
errno?: number; // 错误码
extra?: string; // 额外信息
// ... 其他 T 类型的字段
}注意:
- 此方法仅在 Web 平台(iframe)中可用
- 在移动端调用会抛出错误
示例:
// 调用文件列表 API
const response = await app.query({
api: "x'x'x.FileStation.List",
method: "list",
version: 2,
params: {
folder_path: "/home",
additional: ["size", "time", "perm", "type"],
},
});
if (response.result === "succ") {
console.log("文件列表:", response);
} else {
console.error("请求失败:", response.errmsg);
}
// 使用配置选项
const response2 = await app.query(
{ api: "xxxx", method: "query", version: 1 },
{
timer: false,
autoFailTips: true,
}
);pickFile
async pickFile(params: FilePickerParams): Promise<string[]>打开文件选择器,让用户选择文件或文件夹。
参数:
params: FilePickerParams- 文件选择器参数multiple?: boolean- 是否多选,默认falsedirectory?: boolean- 是否选择文件夹,默认falseaccept?: string[]- 接受的文件类型,如['.pdf', '.doc', '.docx']sidebarGroup?: string[]- 侧边栏分组,可选值:'myFiles'、'otherShare'、'external'、'remote'、'favorites'、'storage'、'torrent'、'team',默认['myFiles']disabledPaths?: string[]- 不可选的路径列表selectablePatterns?: [string, string][]- 可选择的正则表达式(预留)expandFiles?: Array<{ title: string; extension: string }>- 扩充搜索的文件类型creatable?: boolean- 是否允许创建文件夹,默认truetitle?: string- 对话框标题,默认'选择文件'或'选择文件夹'okText?: string- 确认按钮文本,默认'确定'
返回值:
Promise<string[]>- 选中的文件路径数组
示例:
// 选择单个文件
const files = await app.pickFile({
multiple: false,
accept: [".pdf", ".doc", ".docx"],
title: "选择文档文件",
});
console.log("选中的文件:", files[0]);
// 选择多个文件
const multipleFiles = await app.pickFile({
multiple: true,
accept: [".jpg", ".png", ".gif"],
title: "选择图片文件",
});
console.log("选中的文件:", multipleFiles);
// 选择文件夹
const folders = await app.pickFile({
directory: true,
title: "选择文件夹",
});
console.log("选中的文件夹:", folders[0]);
// 限制侧边栏范围
const files = await app.pickFile({
multiple: true,
sidebarGroup: ["myFiles", "favorites"],
accept: [".txt", ".md"],
});
// 禁用某些路径
const files = await app.pickFile({
disabledPaths: ["/vol1/1000/private", "/vol2/1000/temp"],
accept: [".pdf"],
});callAppMethod
async callAppMethod<T>(fn: string, params?: string): Promise<T | null>调用移动端 App 的方法(仅移动端可用)。
参数:
fn: string- 要调用的方法名params?: string- 方法参数(JSON 字符串)
返回值:
Promise<T | null>- 方法返回结果,失败时返回null
注意:
- 此方法仅在移动端(WebView)中可用
- 在 Web 平台调用会返回
null - 建议使用 SDK 提供的标准方法(如
setTitle、openFile等),而不是直接调用此方法
示例:
// 调用自定义方法
const result = await app.callAppMethod<{ success: boolean }>(
"customMethod",
JSON.stringify({ key: "value" })
);实例属性
isWeb
readonly isWeb: boolean判断当前运行环境是否为 Web 平台。
true- 运行在 Web iframe 中false- 运行在移动端 WebView 中
示例:
const app = new TrimApp();
await app.ready();
if (app.isWeb) {
console.log("运行在 Web 平台");
// Web 平台特有逻辑
} else {
console.log("运行在移动端");
// 移动端特有逻辑
}配置选项
TrimAppOptions
interface TrimAppOptions {
/** 是否开启调试模式,开启后会输出日志 */
debug?: boolean;
}选项说明:
debug(可选) - 是否开启调试模式- 类型:
boolean - 默认值:
false - 说明:开启后会在控制台输出 SDK 运行日志,方便调试
- 类型:
示例:
// 开启调试模式
const app = new TrimApp({ debug: true });
// 运行时切换调试模式
app.setOptions({ debug: false });PlatformConfig
interface PlatformConfig {
theme: "dark" | "light"; // 主题模式
language: string; // 语言代码,如 "zh-CN"、"en-US"
appVersion?: string; // 应用版本号
systemVersion: string; // 系统版本号
format: {
date?: string; // 日期格式,如 "YYYY-MM-DD"、"MM-DD-YYYY"、"DD-MM-YYYY"、"YYYY/MM/DD"、"MM/DD/YYYY"、"DD/MM/YYYY"
time?: string; // 时间格式,"12h" 或 "24h"
};
}字段说明:
theme- 当前主题模式"dark"- 深色模式"light"- 浅色模式
language- 语言代码,遵循 BCP 47 标准- 常见值:
"zh-CN"(简体中文)、"en-US"(美式英语)等
- 常见值:
appVersion(可选) - 应用版本号systemVersion- 系统版本号format.date(可选) - 日期格式- 支持格式:
"YYYY-MM-DD"、"MM-DD-YYYY"、"DD-MM-YYYY"、"YYYY/MM/DD"、"MM/DD/YYYY"、"DD/MM/YYYY"
- 支持格式:
format.time(可选) - 时间格式"12h"- 12 小时制"24h"- 24 小时制
平台差异
Web 平台(iframe)
在 Web 平台中,SDK 通过 postmate 与父窗口(OS)进行通信。
支持的方法:
- ✅
setTitle- 设置标题 - ✅
setExitPageTips- 设置退出提示 - ✅
openFile- 打开文件 - ✅
openFileManager- 打开文件管理器 - ✅
getPlatformConfig- 获取平台配置 - ✅
close- 关闭应用 - ✅
query- 请求接口 - ✅
pickFile- 选择文件
移动端平台(WebView)
在移动端平台中,SDK 通过 flutter_inappwebview 与原生 App 进行通信。
支持的方法:
- ✅
setTitle- 设置标题 - ✅
setExitPageTips- 设置退出提示 - ✅
openFile- 打开文件 - ✅
openFileManager- 打开文件管理器 - ✅
getPlatformConfig- 获取平台配置 - ✅
close- 关闭页面 - ❌
query- 不支持(会抛出错误) - ✅
pickFile- 选择文件 - ✅
callAppMethod- 调用自定义 App 方法
类型导出
SDK 导出了以下类型,方便你在 TypeScript 项目中使用:
import type { TrimAppOptions, PlatformConfig } from "@trimjs/web-app";如果需要使用其他类型(如 FilePickerParams、QueryConfig、ResponseData),可以从 @fn/micro-app-postmate 导入:
import type {
FilePickerParams,
QueryConfig,
ResponseData,
} from "@fn/micro-app-postmate/src/child/type";
import type { ResponseData as ResponseDataType } from "@fn/micro-app-postmate/src/child/os-type";使用场景
场景 1:根据平台配置调整 UI
const app = new TrimApp({ debug: true });
const config = await app.getPlatformConfig();
// 根据主题切换 UI
if (config.theme === "dark") {
document.body.classList.add("dark-mode");
}
// 根据语言设置国际化
i18n.setLocale(config.language);
// 根据日期格式显示日期
const dateFormat = config.format.date || "YYYY-MM-DD";
const formattedDate = dayjs().format(dateFormat);场景 2:文件上传功能
// 让用户选择文件
const selectedFiles = await app.pickFile({
multiple: true,
accept: [".jpg", ".png", ".pdf"],
title: "选择要上传的文件",
});
if (selectedFiles && selectedFiles.length > 0) {
// 处理选中的文件
for (const filePath of selectedFiles) {
console.log("上传文件:", filePath);
// 执行上传逻辑...
}
}场景 3:调用 NAS API
// 仅在 Web 平台调用 API
if (app.isWeb) {
try {
const response = await app.query({
api: "xxx.FileStation.List",
method: "list",
version: 2,
params: {
folder_path: "/home",
additional: ["size", "time", "perm"],
},
});
if (response.result === "succ") {
console.log("文件列表获取成功");
}
} catch (error) {
console.error("API 调用失败:", error);
}
}场景 4:页面退出确认
// 在用户有未保存数据时设置退出提示
app.setExitPageTips({
title: "确认离开?",
content: "您有未保存的更改,确定要离开吗?",
});
// 保存数据后清除提示
async function saveData() {
// 保存逻辑...
await app.setExitPageTips(); // 清除提示
}注意事项
初始化时机:SDK 会在创建实例时自动初始化,所有方法都会自动等待初始化完成,无需手动调用
ready()。平台检测:SDK 会自动检测运行环境(Web 或移动端),通过
isWeb属性可以判断当前平台。错误处理:某些方法在特定平台可能不可用(如
query在移动端),调用前建议检查isWeb属性。调试模式:开启
debug选项后,SDK 会在控制台输出详细的运行日志,方便开发和调试。异步方法:所有与平台交互的方法都是异步的,记得使用
await或.then()处理。文件选择器:
pickFile方法会打开系统文件选择器,用户可能取消选择,返回的数组可能为空,记得处理这种情况。API 请求:
query方法返回的ResponseData包含result字段,需要检查状态是否为'succ'来判断请求是否成功。
常见问题
Q: 如何判断当前运行在哪个平台?
A: 使用 isWeb 属性:
const app = new TrimApp();
await app.ready();
if (app.isWeb) {
console.log("运行在 Web 平台");
} else {
console.log("运行在移动端");
}Q: 为什么调用某些方法会报错?
A: 某些方法只在特定平台可用:
query()仅在 Web 平台可用- 在移动端调用这些方法会抛出错误
建议在调用前检查平台:
if (app.isWeb) {
await app.query();
} else {
console.log("当前平台不支持 query 方法");
}Q: 如何等待 SDK 初始化完成?
A: 虽然所有方法都会自动等待初始化,但如果你需要显式等待:
const app = new TrimApp();
await app.ready();
// 现在可以安全地使用所有功能Q: pickFile 返回空数组是什么意思?
A: 如果用户取消了文件选择,pickFile 会返回空数组。记得处理这种情况:
const files = await app.pickFile({ multiple: true });
if (files.length === 0) {
console.log("用户取消了选择");
return;
}
// 处理选中的文件...Q: 如何调试 SDK 的问题?
A: 开启 debug 模式,SDK 会在控制台输出详细日志:
const app = new TrimApp({ debug: true });
// 现在所有操作都会输出日志Q: query 方法返回的 result 字段有哪些值?
A: result 字段可能的值:
'succ'- 成功'fail'- 失败'cancel'- 用户取消'doing'- 任务进行中
const response = await app.query({
/* ... */
});
if (response.result === "succ") {
// 处理成功情况
} else if (response.result === "fail") {
// 处理失败情况
console.error("错误:", response.errmsg);
}Q: 可以在移动端使用 query 方法吗?
A: 不可以。query 方法仅在 Web 平台可用。在移动端调用会抛出错误。如果需要调用 API,建议在 Web 平台使用,或者通过 callAppMethod 调用移动端原生方法。
开发
# 安装依赖
pnpm install
# 开发模式
pnpm dev
# 构建
pnpm build
# 类型检查
pnpm typecheck
# 代码检查
pnpm lint许可证
MIT
