@fallen-zero/use
v1.3.5
Published
fallen-zero Api
Maintainers
Readme
Fallen-Zero Use
Vue 3 composition hooks and browser utilities used by Fallen Zero projects.
Install
# with npm
npm i @fallen-zero/use
# with yarn
yarn add @fallen-zero/use
# with pnpm
pnpm add @fallen-zero/useUsage
import { useRequest, useVisible } from '@fallen-zero/use';功能
请求与列表
| API | 说明 |
| --------------------- | ------------------------------------------------------------------------------------------- |
| useRequest | 包装 Promise 请求,返回 data、error、loading、run、setParams、reset、cancel。 |
| useRequestImmediate | 立即执行一次请求并返回响应式状态。 |
| useApiList | 分页列表加载,提供 list、total、loading、finished、onLoad、reset。 |
Vue Hooks
| API | 说明 |
| ---------------------------- | ----------------------------------------------------- |
| useVisible | 管理弹窗、抽屉等布尔显示状态。 |
| useDebounceRef | 创建带防抖写入的 ref。 |
| UseDefer | 按动画帧延迟渲染大量内容。 |
| useCompRef / useCompRefs | 为组件实例创建带类型的 ref。 |
| useIntervalDate | 每秒更新当前日期、时间和星期。 |
| useCountdown | 根据结束时间生成 HH:mm:ss 倒计时。 |
| useTimeDown | 基于 localStorage 的验证码/冷却倒计时。 |
| useTheme | 管理 light / dark 主题并写入 data-theme。 |
| useResize | 复用单个 ResizeObserver 监听元素尺寸变化。 |
| useRem | 根据设计稿宽度设置根元素 font-size。 |
| useLoopExec | 按固定间隔循环执行同步或异步任务。 |
| useAnimation | 使用 requestAnimationFrame 生成补间动画值。 |
| useStore / StorageEnum | 统一读写 sessionStorage、localStorage 和 Cookie。 |
工具函数
| API | 说明 |
| ------------------------------------------------------------------ | ------------------------------------------ |
| dateFormat / DATE_FORMAT | 常用日期格式化。 |
| decimalPlaces / toFixed / toPrecision / sum | 数字精度和求和工具。 |
| strToArray / arrayToStr / parseJSON | 字符串、数组和 JSON 转换。 |
| encryptName / encrypt | 姓名、手机号、身份证、银行卡、邮箱脱敏。 |
| getSystemAgent / UserAgent | 判断 iOS、Android、微信、钉钉等运行环境。 |
| FontSize | 根据屏幕宽度计算适配后的字体大小。 |
| useFormData | 将对象转换为 FormData。 |
| ImageCompression、base64ToBlob、base64ToFile、fileToBinary | 图片压缩和文件转换。 |
| getFileExt / getFileSize / getFileName / useFullUrl | 文件名、大小和 URL 处理。 |
| numToCapital / toChineseAmount | 数字转中文或人民币大写。 |
| getIdCardInfo / getAgeByIdCard / getSexByIdCard | 根据身份证号获取年龄和性别。 |
| isEmptyValue / isImage / isVideo | 空值、图片路径和视频路径判断。 |
| validatePhone / validateTel / validatePhoneAndTel | 手机号和电话号码校验。 |
| validatePhoneAndTelMD / isVehicleNumber | 脱敏号码和车牌号校验。 |
| anyIsEqual / arrayIsEqual / objectIsEqual / isAllEqual | 深比较数组、对象和值。 |
| convertColor | 将 hex/rgb/rgba 转为带透明度的 rgba。 |
| createField / fommaterIndex | 生成展示字段配置。 |
| Loader | 动态挂载和卸载 Vue 组件。 |
| createTagMsg | 基于 BroadcastChannel 的跨标签页消息。 |
| preformChunk / browserPerformChunk / timeoutPerformChunk | 分片执行大批量任务,避免长时间阻塞主线程。 |
| isFunction、isObject、isArray 等 | 常用类型判断函数。 |
使用案例
请求与列表
import {
CreateRequest,
useApiList,
useRequest,
useRequestImmediate,
} from '@fallen-zero/use';
const getUser = (id: number) => Promise.resolve({ id, name: 'Zero' });
const request = useRequest(getUser, [1], {
immediate: false,
success: (user) => console.log(user.name),
});
await request.run();
await request.setParams([2]);
await request.reset();
request.cancel();
const appRequest = new CreateRequest({
checkStatus: (res) => res.code === 0,
showError: (err) => console.error(err),
});
const userRequest = appRequest.requestApi(
() => Promise.resolve({ code: 0, data: [] }),
[],
{ immediate: false },
);
await userRequest.run();
const immediate = await useRequestImmediate(getUser, 1);
console.log(immediate.data.value);
const listApi = ({ page = 1, limit = 10 }) =>
Promise.resolve({
list: Array.from({ length: limit }, (_, index) => page * limit + index),
total: 100,
});
const { list, total, loading, finished, onLoad, reset, setParams } = useApiList(
listApi,
{ page: 1, limit: 20 },
{ immediate: false },
);
await onLoad();
await setParams({ limit: 30 });
await reset();
console.log(list.value, total.value, loading.value, finished.value);Vue Hooks
import {
StorageEnum,
UseDefer,
useAnimation,
useCompRef,
useCompRefs,
useCountdown,
useDebounceRef,
useIntervalDate,
useLoopExec,
useRem,
useResize,
useStore,
useTheme,
useTimeDown,
useVisible,
} from '@fallen-zero/use';
import UserCard from './UserCard.vue';
const modal = useVisible();
modal.open();
modal.toggle();
modal.close();
console.log(modal.visible.value);
const keyword = useDebounceRef('', 300);
keyword.value = 'search text';
const defer = new UseDefer(60);
const showRow = defer.defer(20);
defer.setMaxCount(120);
console.log(showRow, defer.getMaxCount());
const userCardRef = useCompRef(UserCard);
const userCardRefs = useCompRefs(UserCard);
const { currDate } = useIntervalDate();
console.log(currDate.date, currDate.time, currDate.week);
const countdown = useCountdown('2026-12-31 23:59:59');
console.log(countdown.currentTime.value, countdown.isEnd.value);
countdown.clearTimer();
countdown.startTimer();
const verifyCode = useTimeDown('login-code', 60, () => console.log('done'));
verifyCode.start();
console.log(verifyCode.time.value, verifyCode.finish.value);
verifyCode.stop();
const { theme, toggleTheme } = useTheme('light');
toggleTheme('dark');
console.log(theme.value);
const { observe, unobserve } = useResize();
const element = document.querySelector('#panel');
observe(element, () => console.log('resized'));
unobserve(element);
const { changeRem } = useRem({ designWidth: 1920, size: 192 });
changeRem({ rootWidth: 1440 });
const loop = useLoopExec(() => console.log(Date.now()), 2000);
loop.loop();
loop.cancel();
useAnimation(300, 0, 100, (value) => {
document.body.style.opacity = String(value / 100);
});
const store = useStore('demo');
store.set('token', 'abc', { storage: StorageEnum.LOCAL, expire: 3600 });
console.log(store.get<string>('token', StorageEnum.LOCAL));
store.remove('token', StorageEnum.LOCAL);日期、数字与字符串
import {
DATE_FORMAT,
arrayToStr,
dateFormat,
decimalPlaces,
fommaterIndex,
numToCapital,
parseJSON,
strToArray,
sum,
toChineseAmount,
toFixed,
toPrecision,
} from '@fallen-zero/use';
dateFormat(new Date(), DATE_FORMAT['DATETIMES-']);
decimalPlaces(3.1415);
toFixed(3.1415, 2);
toPrecision(3.1415, 2);
sum(1, 2, 3);
strToArray('a,b,c');
arrayToStr(['a', 'b', 'c']);
parseJSON('{"name":"Zero"}', { name: '' });
numToCapital(1203);
toChineseAmount(1203.45);
fommaterIndex(8);文件与浏览器工具
import {
FontSize,
ImageCompression,
UserAgent,
base64ToBlob,
base64ToFile,
convertColor,
createTagMsg,
fileToBinary,
getFileExt,
getFileName,
getFileSize,
getSystemAgent,
useFormData,
useFullUrl,
} from '@fallen-zero/use';
declare const file: File;
declare const base64: string;
const formData = useFormData({ name: 'Zero', tags: ['vue'] });
new ImageCompression({
file,
quality: 0.8,
success: (base64) => console.log(base64),
});
const blob = base64ToBlob(base64, 'image/png');
const imageFile = base64ToFile(base64, 'avatar.png', 'image/png');
const bytes = await fileToBinary(imageFile);
getFileExt('avatar.png');
getFileSize(file);
getFileName('/upload/avatar.png');
const fullUrl = useFullUrl('https://cdn.example.com');
fullUrl('~/avatar.png');
FontSize(16);
getSystemAgent(UserAgent.微信);
convertColor('#1677ff', 0.5);
const tagMsg = createTagMsg('app');
const stopListen = tagMsg.listenMsg((msg) => console.log(msg.type));
tagMsg.sendMsg('refresh', { id: 1 });
stopListen();校验、比较与类型判断
import {
anyIsEqual,
arrayIsEqual,
encrypt,
encryptName,
getAgeByIdCard,
getIdCardInfo,
getSexByIdCard,
isAllEqual,
isArray,
isBoolean,
isDate,
isEmptyValue,
isFile,
isFunction,
isImage,
isNull,
isNumber,
isObject,
isPromise,
isRegExp,
isString,
isUndefined,
isVehicleNumber,
isVideo,
objectIsEqual,
validatePhone,
validatePhoneAndTel,
validatePhoneAndTelMD,
validateTel,
} from '@fallen-zero/use';
declare const file: File;
encryptName('张三');
encrypt('phone', '13800138000');
getSexByIdCard('110101199001011234');
getAgeByIdCard('110101199001011234');
getIdCardInfo('110101199001011234');
isEmptyValue('');
isImage('avatar.webp');
isVideo('demo.mp4');
isVehicleNumber('浙A12345');
validatePhone('13800138000');
validateTel('0571-12345678');
validatePhoneAndTel('0571-12345678');
validatePhoneAndTelMD('138****8000');
isAllEqual({ a: 1 }, { a: 1 });
arrayIsEqual([1, 2], [2, 1], true);
objectIsEqual({ a: 1 }, { a: 1 });
anyIsEqual([{ a: 1 }], [{ a: 1 }]);
isFunction(() => {});
isObject({});
isArray([]);
isString('zero');
isNumber(1);
isBoolean(false);
isDate(new Date());
isRegExp(/zero/);
isUndefined(undefined);
isNull(null);
isPromise(Promise.resolve());
isFile(file);字段、加载器与分片任务
import {
Loader,
browserPerformChunk,
createField,
preformChunk,
timeoutPerformChunk,
} from '@fallen-zero/use';
import Toast from './Toast.vue';
const fields = createField(
[
{ label: '姓名', key: 'name' },
{ label: '年龄', key: 'age', visible: (age) => age > 0 },
],
{ name: 'Zero', age: 18 },
);
const loader = new Loader(Toast);
await loader.show();
loader.hide();
browserPerformChunk([1, 2, 3], (item) => console.log(item));
timeoutPerformChunk(1000, (_, index) => console.log(index), 16, 0);
preformChunk(
[1, 2, 3],
(item) => console.log(item),
(task) => task(() => true),
);
console.log(fields);