工具函數
:::info 內容來源
本頁內容源自 appfuse-web/lib/utils/README.md,如有差異以原始碼為準。
:::
AppFuse Web 提供 10+ 工具模組,涵蓋 HTTP 請求、日期處理、國際化等常見需求。
匯入方式
所有工具函數統一從 @appfuse/appfuse-web/utils 匯入:
import { createHttpClient, time, logger, cn, i18n, cookie, environ, browser, numeral, template } from '@appfuse/appfuse-web/utils'
模組總覽
| 模組 | 用途 |
|---|---|
createHttpClient / HttpClientProvider / useHttpClient | HTTP 客戶端 |
Filter | 查詢過濾器(RSQL/FIQL) |
time / format / toDateString | 日期時間處理 |
i18n / useTranslation / I18nProvider | 國際化 |
numeral | 數字格式化 |
cn | CSS 類別合併 |
cookie | Cookie 管理 |
browser | 瀏覽器偵測 |
environ | 環境配置 |
logger | 日誌系統 |
template | 字串模板 |
http - HTTP 客戶端
基於 axios 的 HTTP 客戶端,支援自動日期轉換和檔案上傳。
建立客戶端
import { createHttpClient } from '@appfuse/appfuse-web/utils';
const http = createHttpClient({
baseURL: '/api',
timeout: 30000,
});
基本請求
// GET
const users = await http.get<User[]>('/users');
// GET with params
const users = await http.get<User[]>('/users', {
params: { status: 'active', page: 1 }
});
// POST
const user = await http.post<User>('/users', {
name: 'John',
email: 'john@example.com'
});
// PUT
await http.put(`/users/${id}`, userData);
// DELETE
await http.delete(`/users/${id}`);
自動日期轉換
HTTP 客戶端會自動將 ISO 8601 日期字串轉換為 Date 物件:
// API 回傳
{ "createdAt": "2024-01-15T10:30:00Z" }
// 自動轉換為
{ createdAt: Date } // JavaScript Date 物件
檔案上傳
HTTP 客戶端會自動偵測請求資料中的 File 物件並處理上傳,支援多種策略:
// 自動處理:資料中包含 File 時,自動轉為 multipart/form-data
const product = await http.post('/products', {
name: 'Product',
image: file // File 物件,自動處理
});
可透過 createHttpClient 的 fileUpload 選項配置上傳策略:
binary(預設):使用 multipart/form-database64:檔案轉 base64 內嵌於 JSON
filter - 查詢過濾器
使用 Filter 類別建構 RSQL/FIQL 標準的查詢字串。
基本用法
import { Filter } from '@appfuse/appfuse-web/utils';
// 簡單查詢
const query = Filter.eq('status', 'active').toString();
// → 'status=="active"'
// 模糊查詢
const query = Filter.like('name', 'John').toString();
// → 'name=="*John*"'
支援的運算子
| 靜態方法 | 運算子 | 說明 |
|---|---|---|
Filter.eq(field, value) | == | 等於 |
Filter.notEq(field, value) | != | 不等於 |
Filter.gt(field, value) | =gt= | 大於 |
Filter.ge(field, value) | =ge= | 大於等於 |
Filter.lt(field, value) | =lt= | 小於 |
Filter.le(field, value) | =le= | 小於等於 |
Filter.like(field, value) | == + 萬用字元 | 模糊比對 |
Filter.in(field, values) | =in= | 包含於 |
Filter.notIn(field, values) | =out= | 不包含於 |
Filter.has(field, value) | =has= | 包含 |
Filter.is(field, value) | =is= | 是(null 或 boolean) |
Filter.isNot(field, value) | =isnt= | 不是(null 或 boolean) |
組合查詢
// AND 條件
const query = Filter.eq('status', 'active')
.and(Filter.ge('price', 100))
.toString();
// → '(status=="active";price=ge=100)'
// OR 條件
const query = Filter.eq('category', 'A')
.or(Filter.eq('category', 'B'))
.toString();
// → '(category=="A",category=="B")'
// 空值自動忽略
Filter.eq('name', '').toString(); // → null
time - 日期時間
基於 date-fns 的日期時間處理工具,提供格式化、解析、計算與 Duration 操作。
格式化
import { format, toDateString } from '@appfuse/appfuse-web/utils';
const date = new Date();
// 使用自訂格式
format(date, 'yyyy-MM-dd'); // "2024-01-15"
format(date, 'HH:mm:ss'); // "10:30:00"
format(date, 'yyyy-MM-dd HH:mm'); // "2024-01-15 10:30"
// 使用預設格式常數
import { TimeFormat } from '@appfuse/appfuse-web/utils';
format(date, TimeFormat.LOCAL_DATE); // "2024-01-15"
format(date, TimeFormat.LOCAL_DATETIME); // "2024-01-15T10:30:00"
// API 日期欄位轉換
toDateString(date); // "2024-01-15"(YYYY-MM-DD 格式)
解析
import { parse, TimeFormat } from '@appfuse/appfuse-web/utils';
parse('2024-01-15', TimeFormat.LOCAL_DATE); // Date 物件
parse('2024-01-15T10:30:00+08:00', TimeFormat.DATETIME); // Date 物件
計算
import { plus, minus, Duration, isBefore, isAfter } from '@appfuse/appfuse-web/utils';
const today = new Date();
// 加減時間
const tomorrow = plus(today, Duration.days(1));
const lastWeek = minus(today, Duration.days(7));
const later = plus(today, Duration.hours(2));
// 比較
isBefore(date1, date2); // date1 < date2
isAfter(date1, date2); // date1 > date2
Duration
import { Duration } from '@appfuse/appfuse-web/utils';
// 建立 Duration
const d = Duration.minutes(90);
d.toHours(); // 1.5
d.toMinutes(); // 90
d.hours(); // 1(小時部分)
d.minutes(); // 30(分鐘部分)
// 靜態工廠方法
Duration.hours(2);
Duration.days(7);
Duration.seconds(30);
// 運算
const total = Duration.hours(1).add(Duration.minutes(30));
// 格式化(ISO 8601)
Duration.minutes(90).toISOString(); // "PT1H30M"
i18n - 國際化
多語言支援工具,提供翻譯、參數插值與語言回退。
初始化
import { i18n } from '@appfuse/appfuse-web/utils';
// 新增翻譯
i18n.addTranslation('zh-TW', {
welcome: '歡迎,${name}!',
items: '你有 ${count} 個項目',
});
i18n.addTranslation('en', {
welcome: 'Welcome, ${name}!',
items: 'You have ${count} items',
});
// 設定語言
i18n.language = 'zh-TW';
// 或使用 configure 一次配置
i18n.configure({
language: 'zh-TW',
fallback: 'en',
translations: {
'zh-TW': { welcome: '歡迎' },
'en': { welcome: 'Welcome' },
},
});
翻譯
// 簡單翻譯
i18n.t('welcome'); // "歡迎"
// 帶具名參數
i18n.t('welcome', { name: 'John' }); // "歡迎,John!"
// 找不到翻譯時回傳原文
i18n.t('Not translated'); // "Not translated"
語言回退
// 設定回退語言
i18n.fallback = 'en';
// zh-TW 找不到時會嘗試 zh,再嘗試 en
React 整合
import { useTranslation } from '@appfuse/appfuse-web/utils';
function MyComponent() {
const { t, language } = useTranslation();
return <button>{t('Save')}</button>;
}
numeral - 數字格式化
使用 Intl API 的數字格式化工具,所有方法皆為 numeral 實例的方法。
數字格式化
import { numeral } from '@appfuse/appfuse-web/utils';
// 十進位數字
numeral.formatDecimal(1234567.89); // "1,234,567.89"
numeral.formatDecimal(1234567.89, 0); // "1,234,568"(無小數)
// 貨幣
numeral.formatCurrency(1234.56, 'USD'); // "$1,234.56"
numeral.formatCurrency(1234.56, 'TWD'); // "NT$1,235"
// 百分比
numeral.formatPercent(0.1234); // "12%"
numeral.formatPercent(0.1234, 2); // "12.34%"
// 通用格式化(使用模式字串)
numeral.format(1234.56, 'currency:usd'); // "$1,234.56"
numeral.format(0.1234, 'percent'); // "12%"
cn - CSS 類別合併
智慧合併 Tailwind CSS 類別。
基本用法
import { cn } from '@appfuse/appfuse-web/utils';
// 合併類別
cn('px-4 py-2', 'bg-blue-500');
// "px-4 py-2 bg-blue-500"
// 條件類別
cn('btn', isActive && 'btn-active');
// isActive ? "btn btn-active" : "btn"
// 覆蓋衝突類別
cn('px-4', 'px-8');
// "px-8"(後者覆蓋前者)
// 物件語法
cn({
'btn': true,
'btn-primary': isPrimary,
'btn-disabled': isDisabled,
});
cookie - Cookie 管理
透過 cookie 實例進行類型安全的 Cookie 操作。
基本操作
import { cookie } from '@appfuse/appfuse-web/utils';
// 讀取
const token = cookie.get('token');
const theme = cookie.get('theme', 'light'); // 含預設值
// 讀取為布林值
const isAuth = cookie.getAsBoolean('isAuth', false);
// 設定
cookie.set('token', 'abc123', {
expiredInDays: 7,
path: '/',
secure: true,
sameSite: 'Strict',
});
// 檢查存在
if (cookie.has('sessionId')) {
// ...
}
// 刪除
cookie.delete('sessionId');
browser - 瀏覽器偵測
透過 browser 實例進行瀏覽器和語言偵測。
語言偵測
import { browser } from '@appfuse/appfuse-web/utils';
// 取得瀏覽器語言
const lang = browser.language; // "zh-TW"
// 取得所有偏好語言
const langs = browser.languages; // ["zh-TW", "en-US", ...]
// 取得解析後的 Locale
const locale = browser.locale;
// { language: 'zh', country: 'TW' }
environ - 環境配置
透過 environ 實例進行類型安全的環境配置管理。配置只能設定一次,設定後不可變。
配置與讀取
import { environ } from '@appfuse/appfuse-web/utils';
// 初始化配置(只能呼叫一次)
environ.configure({
app: {
name: 'My App',
version: '1.0.0',
stage: 'development',
basename: '/',
baseURL: '/api',
},
i18n: {
languages: ['zh-TW', 'en'],
},
});
// 讀取配置
environ.baseURL; // '/api'
environ.basename; // '/'
environ.app.name; // 'My App'
environ.app.version; // '1.0.0'
environ.isDev; // true
environ.isProd; // false
environ.isConfigured; // true
logger - 日誌系統
基於 loglevel 的日誌工具,透過 logger 實例操作。
基本用法
import { logger } from '@appfuse/appfuse-web/utils';
// 不同等級
logger.debug('Debug message');
logger.info('Info message');
logger.warn('Warning message');
logger.error('Error message');
// 條件式日誌
logger.warnIf(items.length > 100, 'Large dataset detected', items.length);
// 效能計時
logger.time('fetchData');
await fetchData();
logger.timeEnd('fetchData');
建立子 logger
// 建立模組專用 logger
const authLogger = logger.getLogger('auth');
authLogger.info('User logged in'); // 帶時間戳輸出
動態設定等級
// 透過 setter 設定等級
logger.level = 'debug'; // 開發環境
logger.level = 'warn'; // 生產環境
template - 字串模板
透過 template 實例進行字串插值,使用 ${...} 佔位符語法。
位置參數
import { template } from '@appfuse/appfuse-web/utils';
template.format('Hello, ${0}!', 'World');
// "Hello, World!"
template.format('${0} + ${1} = ${2}', 1, 2, 3);
// "1 + 2 = 3"
命名參數
template.format('Hello, ${name}! You have ${count} messages.', {
name: 'John',
count: 5
});
// "Hello, John! You have 5 messages."
Tree-shaking 匯入
除了統一匯入,也支援個別模組匯入以優化打包體積:
// 統一匯入
import { createHttpClient, logger } from '@appfuse/appfuse-web/utils'
// 個別模組匯入(僅打包使用的模組)
import { createHttpClient } from '@appfuse/appfuse-web/utils/http'
import { logger } from '@appfuse/appfuse-web/utils/logger'
最佳實踐
- 統一 HTTP 客戶端 - 應用程式只建立一個 http 實例
- 使用 Filter - 透過
Filter靜態方法建構查詢,避免手動拼接字串 - 日期處理 - 使用
format()、plus()、minus()等 time 工具函數 - CSS 合併 - 使用
cn()處理條件樣式