小程序初始提交

This commit is contained in:
jdc
2025-11-13 10:36:23 +08:00
parent f26b4f9a2f
commit 5db3b180eb
447 changed files with 83351 additions and 0 deletions

View File

@@ -0,0 +1,612 @@
/**
* 检查值是否为数组
* @example isArray([1, 2, 3]) // true
* @example isArray('123') // false
*/
export function isArray(value: any): boolean {
return Array.isArray(value);
}
/**
* 检查值是否为对象
* @example isObject({}) // true
* @example isObject([]) // false
*/
export function isObject(value: any): boolean {
return typeof value == "object" && !Array.isArray(value) && !isNull(value);
}
/**
* 检查值是否为字符串
* @example isString('abc') // true
* @example isString(123) // false
*/
export function isString(value: any): boolean {
return typeof value == "string";
}
/**
* 检查值是否为数字
* @example isNumber(123) // true
* @example isNumber('123') // false
*/
export function isNumber(value: any): boolean {
return typeof value == "number" && !isNaN(value);
}
/**
* 检查值是否为布尔值
* @example isBoolean(true) // true
* @example isBoolean(1) // false
*/
export function isBoolean(value: any): boolean {
return typeof value == "boolean";
}
/**
* 检查值是否为函数
* @example isFunction(() => {}) // true
* @example isFunction({}) // false
*/
export function isFunction(value: any): boolean {
return typeof value == "function";
}
/**
* 检查值是否为 null
* @example isNull(null) // true
* @example isNull(undefined) // true
*/
export function isNull(value?: any | null): boolean {
// #ifdef APP
return value == null;
// #endif
// #ifndef APP
return value == null || value == undefined;
// #endif
}
/**
* 检查值是否为空
* @example isEmpty([]) // true
* @example isEmpty('') // true
* @example isEmpty({}) // true
*/
export function isEmpty(value: any): boolean {
if (isArray(value)) {
return (value as any[]).length == 0;
}
if (isString(value)) {
return value == "";
}
if (isObject(value)) {
return keys(value).length == 0;
}
return false;
}
/**
* 返回对象的所有键名
* @example keys({a: 1, b: 2}) // ['a', 'b']
*/
export function keys(value: any): string[] {
// @ts-ignore
return UTSJSONObject.keys(value as UTSJSONObject);
}
/**
* 返回数组的第一个元素
* @example first([1, 2, 3]) // 1
* @example first([]) // null
*/
export function first<T>(array: T[]): T | null {
return isArray(array) && array.length > 0 ? array[0] : null;
}
/**
* 返回数组的最后一个元素
* @example last([1, 2, 3]) // 3
* @example last([]) // null
*/
export function last<T>(array: T[]): T | null {
return isArray(array) && array.length > 0 ? array[array.length - 1] : null;
}
/**
* 截取数组的一部分
* @example slice([1, 2, 3], 1) // [2, 3]
* @example slice([1, 2, 3], 1, 2) // [2]
*/
export function slice<T>(array: T[], start: number = 0, end: number = array.length): T[] {
if (!isArray(array)) return [];
const result: T[] = [];
for (let i = start; i < end && i < array.length; i++) {
result.push(array[i]);
}
return result;
}
/**
* 检查对象是否包含指定属性
* @example has({a: 1}, 'a') // true
* @example has({a: 1}, 'b') // false
*/
export function has(object: any, key: string): boolean {
return keys(object).includes(key);
}
/**
* 获取对象的属性值
* @example get({a: {b: 1}}, 'a.b') // 1
* @example get({a: {b: 1}}, 'a.c', 'default') // 'default'
*/
export function get(object: any, path: string, defaultValue: any | null = null): any | null {
if (isNull(object)) {
return defaultValue;
}
// @ts-ignore
const value = new UTSJSONObject(object).getAny(path);
if (isNull(value)) {
return defaultValue;
}
return value;
}
/**
* 设置对象的属性值
* @example set({a: 1}, 'b', 2) // {a: 1, b: 2}
*/
export function set(object: any, key: string, value: any | null): void {
(object as UTSJSONObject)[key] = value;
}
/**
* 遍历数组并返回新数组
* @example map([1, 2, 3], x => x * 2) // [2, 4, 6]
*/
export function map<T, U>(array: T[], iteratee: (item: T, index: number) => U): U[] {
const result: U[] = [];
if (!isArray(array)) return result;
for (let i = 0; i < array.length; i++) {
result.push(iteratee(array[i], i));
}
return result;
}
/**
* 将数组归约为单个值
* @example reduce([1, 2, 3], (sum, n) => sum + n, 0) // 6
*/
export function reduce<T, U>(
array: T[],
iteratee: (accumulator: U, value: T, index: number) => U,
initialValue: U
): U {
if (!isArray(array)) return initialValue;
let accumulator: U = initialValue;
for (let i = 0; i < array.length; i++) {
accumulator = iteratee(accumulator, array[i], i);
}
return accumulator;
}
/**
* 检查数组中的所有元素是否都满足条件
* @example every([2, 4, 6], x => x % 2 == 0) // true
*/
export function every<T>(array: T[], predicate: (value: T, index: number) => boolean): boolean {
if (!isArray(array)) return true;
for (let i = 0; i < array.length; i++) {
if (!predicate(array[i], i)) {
return false;
}
}
return true;
}
/**
* 检查数组中是否有元素满足条件
* @example some([1, 2, 3], x => x > 2) // true
*/
export function some<T>(array: T[], predicate: (value: T, index: number) => boolean): boolean {
if (!isArray(array)) return false;
for (let i = 0; i < array.length; i++) {
if (predicate(array[i], i)) {
return true;
}
}
return false;
}
/**
* 创建去重后的数组
* @example uniq([1, 2, 2, 3]) // [1, 2, 3]
*/
export function uniq<T>(array: T[]): T[] {
if (!isArray(array)) return [];
const result: T[] = [];
const seen = new Map<T, boolean>();
for (let i = 0; i < array.length; i++) {
const item = array[i];
const key = item;
if (!seen.has(item)) {
seen.set(key, true);
result.push(item);
}
}
return result;
}
/**
* 将数组扁平化一层
* @example flatten([1, [2, 3], 4]) // [1, 2, 3, 4]
*/
export function flatten(array: any[]): any[] {
if (!isArray(array)) return [];
const result: any[] = [];
for (let i = 0; i < array.length; i++) {
const item = array[i];
if (isArray(item)) {
result.push(...(item as any[]));
} else {
result.push(item);
}
}
return result;
}
/**
* 将数组完全扁平化
* @example flattenDeep([1, [2, [3, [4]]]]) // [1, 2, 3, 4]
*/
export function flattenDeep(array: any[]): any[] {
if (!isArray(array)) return [];
const result: any[] = [];
for (let i = 0; i < array.length; i++) {
const item = array[i];
if (isArray(item)) {
result.push(...flattenDeep(item as any[]));
} else {
result.push(item);
}
}
return result;
}
/**
* 对数组进行排序
* @example sort([3, 1, 2]) // [1, 2, 3]
* @example sort(['c', 'a', 'b'], 'desc') // ['c', 'b', 'a']
*/
export function sort<T>(array: T[], order: "asc" | "desc" = "asc"): T[] {
const result = [...array];
return result.sort((a, b) => {
if (typeof a == "number" && typeof b == "number") {
return order == "asc" ? a - b : b - a;
}
if (typeof a == "string" && typeof b == "string") {
return order == "asc" ? a.localeCompare(b) : b.localeCompare(a);
}
return 0;
});
}
/**
* 根据对象属性对数组进行排序
* @example orderBy([{age: 30}, {age: 20}], 'age') // [{age: 20}, {age: 30}]
*/
export function orderBy<T>(array: T[], key: string, order: "asc" | "desc" = "asc"): T[] {
if (!isArray(array)) return [];
const result = [...array];
result.sort((a, b) => {
const valueA = get(a as any, key) as number;
const valueB = get(b as any, key) as number;
if (order == "asc") {
return valueA > valueB ? 1 : -1;
} else {
return valueA < valueB ? 1 : -1;
}
});
return result;
}
/**
* 根据对象属性对数组进行分组
* @example groupBy([{type: 'a'}, {type: 'b'}, {type: 'a'}], 'type') // {a: [{type: 'a'}, {type: 'a'}], b: [{type: 'b'}]}
*/
export function groupBy<T>(array: T[], key: string) {
if (!isArray(array)) return {};
const result = {};
for (let i = 0; i < array.length; i++) {
const item = array[i];
let value = get(item as any, key)!;
if (typeof value == "number") {
value = value.toString();
}
if (typeof value == "string") {
if (!isArray(result[value])) {
result[value] = new Array<T>();
}
(result[value] as T[]).push(item);
}
}
return result;
}
/**
* 将多个对象的属性合并到一个对象中
* @example assign({a: 1}, {b: 2}) // {a: 1, b: 2}
*/
export function assign(...items: any[]) {
// @ts-ignore
return UTSJSONObject.assign(...items.map((item) => item as UTSJSONObject));
}
/**
* 获取数组中指定索引的元素
* @example nth([1, 2, 3], 1) // 2
* @example nth([1, 2, 3], -1) // 3
*/
export function nth<T>(array: T[], index: number): T | null {
if (index >= 0) {
return array[index];
}
return array[array.length + index];
}
/**
* 从数组中移除指定的值
* @example pull([1, 2, 3, 1, 2, 3], 1, 2) // [3, 3]
*/
export function pull<T>(array: T[], ...values: T[]): T[] {
if (!isArray(array)) return [];
return array.filter((item) => !values.includes(item));
}
/**
* 从数组中移除满足条件的元素
* @example remove([1, 2, 3, 4], x => x % 2 == 0) // [1, 3]
*/
export function remove<T>(array: T[], predicate: (value: T, index: number) => boolean): T[] {
if (!isArray(array)) return [];
const result: T[] = [];
for (let i = 0; i < array.length; i++) {
if (!predicate(array[i], i)) {
result.push(array[i]);
}
}
return result;
}
/**
* 遍历数组
* @example forEach([1, 2, 3], x => console.log(x))
*/
export function forEach<T>(data: T[], iteratee: (value: T, index: number) => void): void {
if (isArray(data)) {
const array = data as T[];
for (let i = 0; i < array.length; i++) {
if (array[i] != null) {
iteratee(array[i], i);
}
}
}
}
/**
* 查找数组中第一个满足条件的元素
* @example find([1, 2, 3, 4], x => x > 2) // 3
*/
export function find<T>(array: T[], predicate: (value: T, index: number) => boolean): T | null {
if (!isArray(array)) return null;
for (let i = 0; i < array.length; i++) {
if (predicate(array[i], i)) {
return array[i];
}
}
return null;
}
/**
* 遍历对象
* @example forInObject({a: 1, b: 2}, (value, key) => console.log(key, value))
*/
export function forInObject(data: any, iteratee: (value: any, key: string) => void): void {
if (isObject(data)) {
const objKeys = keys(data);
for (let i = 0; i < objKeys.length; i++) {
const key = objKeys[i];
iteratee(get(data, key)!, key);
}
}
}
/**
* 对象转数组
* @example toArray({a: 1, b: 2}, (value, key) => ({key, value})) // [{key: 'a', value: 1}, {key: 'b', value: 2}]
*/
export function toArray<T>(data: any, iteratee: (value: any, key: string) => T): T[] {
const result: T[] = [];
if (isObject(data)) {
forInObject(data, (value, key) => {
result.push(iteratee(value, key));
});
}
return result;
}
/**
* 生成UUID
* @example uuid() // "123e4567-e89b-12d3-a456-426614174000"
*/
export function uuid(): string {
let uuid = "";
let i: number;
let random: number;
for (i = 0; i < 36; i++) {
if (i == 8 || i == 13 || i == 18 || i == 23) {
uuid += "-";
} else if (i == 14) {
uuid += "4";
} else if (i == 19) {
random = (Math.random() * 16) | 0;
uuid += ((random & 0x3) | 0x8).toString(16);
} else {
random = (Math.random() * 16) | 0;
uuid += random.toString(16);
}
}
return uuid;
}
/**
* 创建一个防抖函数,在指定延迟后执行函数,如果在延迟期间再次调用则重新计时
* @example debounce(() => console.log('执行'), 300)
*/
export function debounce(func: () => void, delay: number): () => number {
let timeoutId = 0;
return function (): number {
// 清除之前的定时器
if (timeoutId != 0) {
clearTimeout(timeoutId);
}
// 设置新的定时器
// @ts-ignore
timeoutId = setTimeout(() => {
func();
timeoutId = 0;
}, delay);
return timeoutId;
};
}
/**
* 创建一个节流函数,在指定时间间隔内只会执行一次
* @example
* const throttled = throttle(() => console.log('执行'), 300)
* throttled()
*/
export function throttle(func: () => void, delay: number): () => number {
let timeoutId: number = 0;
let lastExec: number = 0;
return function (): number {
const now: number = Date.now();
// 如果距离上次执行已超过delay则立即执行
if (now - lastExec >= delay) {
func();
lastExec = now;
return 0;
}
// 否则在剩余时间后执行
if (timeoutId != 0) {
clearTimeout(timeoutId);
}
const remaining: number = delay - (now - lastExec);
// @ts-ignore
timeoutId = setTimeout(() => {
func();
lastExec = Date.now();
timeoutId = 0;
}, remaining);
return timeoutId;
};
}
/**
* 生成指定范围内的随机数
* @example random(1, 10) // 随机生成1到10之间的整数
*/
export function random(min: number, max: number): number {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
/**
* 将base64转换为blob
* @param data base64数据
* @returns blob数据
*/
export function base64ToBlob(data: string, type: string = "image/jpeg"): Blob {
// #ifdef H5
let bytes = window.atob(data.split(",")[1]);
let ab = new ArrayBuffer(bytes.length);
let ia = new Uint8Array(ab);
for (let i = 0; i < bytes.length; i++) {
ia[i] = bytes.charCodeAt(i);
}
return new Blob([ab], { type });
// #endif
}
/**
* 检查两个值是否相等
* @param a 值1
* @param b 值2
* @returns 是否相等
*/
export function isEqual(a: any, b: any): boolean {
if (isObject(a) && isObject(b)) {
return isEqual(JSON.stringify(a), JSON.stringify(b));
} else if (isArray(a) && isArray(b)) {
return isEqual(JSON.stringify(a), JSON.stringify(b));
}
return a == b;
}

324
cool-unix/cool/utils/day.ts Normal file
View File

@@ -0,0 +1,324 @@
/**
* 轻量级日期工具类
* 基于 uni-app-x UTS Date API
* 参考 dayjs 设计理念
*/
export class DayUts {
private _date: Date;
constructor(date?: Date | string | number | null) {
if (date == null || date == "") {
this._date = new Date();
} else if (typeof date == "string") {
this._date = new Date(date);
} else if (typeof date == "number") {
this._date = new Date(date);
} else if (date instanceof Date) {
this._date = new Date(date.getTime());
} else {
this._date = new Date();
}
}
/**
* 格式化日期
* @param template 格式模板,支持 YYYY-MM-DD HH:mm:ss 等
*/
format(template: string): string {
// 使用传入的模板
let actualTemplate: string = template;
const year = this._date.getFullYear();
const month = this._date.getMonth() + 1;
const date = this._date.getDate();
const hours = this._date.getHours();
const minutes = this._date.getMinutes();
const seconds = this._date.getSeconds();
const milliseconds = this._date.getMilliseconds();
// 使用数组来依次替换避免UTS的replace方法兼容性问题
let result: string = actualTemplate;
// 按照长度从长到短替换,避免冲突
// 替换年份 (YYYY 必须在 YY 之前)
result = result.replace("YYYY", year.toString());
result = result.replace("YY", year.toString().slice(-2));
// 替换月份 (MM 必须在 M 之前)
result = result.replace("MM", month.toString().padStart(2, "0"));
result = result.replace("M", month.toString());
// 替换日期 (DD 必须在 D 之前)
result = result.replace("DD", date.toString().padStart(2, "0"));
result = result.replace("D", date.toString());
// 替换小时 (HH 必须在 H 之前)
result = result.replace("HH", hours.toString().padStart(2, "0"));
result = result.replace("H", hours.toString());
// 替换分钟 (mm 必须在 m 之前)
result = result.replace("mm", minutes.toString().padStart(2, "0"));
result = result.replace("m", minutes.toString());
// 替换秒数 (ss 必须在 s 之前)
result = result.replace("ss", seconds.toString().padStart(2, "0"));
result = result.replace("s", seconds.toString());
// 替换毫秒
result = result.replace("SSS", milliseconds.toString().padStart(3, "0"));
return result;
}
/**
* 本月多少天
*/
getDays(): number {
return new Date(this._date.getFullYear(), this._date.getMonth() + 1, 0).getDate();
}
/**
* 是否为闰年
*/
isLeapYear(): boolean {
return this._date.getFullYear() % 4 == 0 && this._date.getFullYear() % 100 != 0;
}
/**
* 星期几
*/
getDay(): number {
return this._date.getDay();
}
/**
* 获取某个单位的开始时间
*/
startOf(unit: "month" | "day" | "year" | "week"): DayUts {
const newDate = new Date(this._date.getTime());
switch (unit) {
case "year":
newDate.setMonth(0);
newDate.setDate(1);
newDate.setHours(0);
newDate.setMinutes(0);
newDate.setSeconds(0);
newDate.setMilliseconds(0);
break;
case "month":
newDate.setDate(1);
newDate.setHours(0);
newDate.setMinutes(0);
newDate.setSeconds(0);
newDate.setMilliseconds(0);
break;
case "week":
newDate.setDate(newDate.getDate() - newDate.getDay());
newDate.setHours(0);
newDate.setMinutes(0);
newDate.setSeconds(0);
newDate.setMilliseconds(0);
break;
case "day":
newDate.setHours(0);
newDate.setMinutes(0);
newDate.setSeconds(0);
newDate.setMilliseconds(0);
break;
}
return new DayUts(newDate);
}
/**
* 获取某个单位的结束时间
*/
endOf(unit: "month" | "day" | "year" | "week"): DayUts {
const newDate = new Date(this._date.getTime());
switch (unit) {
case "year":
newDate.setMonth(11);
newDate.setDate(31);
newDate.setHours(23);
newDate.setMinutes(59);
newDate.setSeconds(59);
newDate.setMilliseconds(999);
break;
case "month":
newDate.setMonth(newDate.getMonth() + 1);
newDate.setDate(0);
newDate.setHours(23);
newDate.setMinutes(59);
newDate.setSeconds(59);
newDate.setMilliseconds(999);
break;
case "week":
const day = newDate.getDay();
const diff = 6 - day;
newDate.setDate(newDate.getDate() + diff);
newDate.setHours(23);
newDate.setMinutes(59);
newDate.setSeconds(59);
newDate.setMilliseconds(999);
break;
case "day":
newDate.setHours(23);
newDate.setMinutes(59);
newDate.setSeconds(59);
newDate.setMilliseconds(999);
break;
}
return new DayUts(newDate);
}
/**
* 判断是否早于另一个日期
*/
isBefore(date: DayUts | Date | string | number): boolean {
const compareDate = this._parseDate(date);
return this._date.getTime() < compareDate.getTime();
}
/**
* 判断是否晚于另一个日期
*/
isAfter(date: DayUts | Date | string | number): boolean {
const compareDate = this._parseDate(date);
return this._date.getTime() > compareDate.getTime();
}
/**
* 判断是否与另一个日期相同
*/
isSame(date: DayUts | Date | string | number): boolean {
const compareDate = this._parseDate(date);
return this._date.getTime() == compareDate.getTime();
}
/**
* 计算与另一个日期的差值(毫秒)
*/
diff(date: DayUts | Date | string | number): number {
const compareDate = this._parseDate(date);
return this._date.getTime() - compareDate.getTime();
}
/**
* 计算与另一个日期的差值(指定单位)
*/
diffUnit(
date: DayUts | Date | string | number,
unit: "day" | "hour" | "minute" | "second" | "millisecond"
): number {
const compareDate = this._parseDate(date);
const diffMs = this._date.getTime() - compareDate.getTime();
switch (unit) {
case "day":
return Math.floor(diffMs / (1000 * 60 * 60 * 24));
case "hour":
return Math.floor(diffMs / (1000 * 60 * 60));
case "minute":
return Math.floor(diffMs / (1000 * 60));
case "second":
return Math.floor(diffMs / 1000);
case "millisecond":
default:
return diffMs;
}
}
/**
* 添加时间
*/
add(value: number, unit: "day" | "month" | "year" | "hour" | "minute" | "second"): DayUts {
const newDate = new Date(this._date.getTime());
switch (unit) {
case "year":
newDate.setFullYear(newDate.getFullYear() + value);
break;
case "month":
newDate.setMonth(newDate.getMonth() + value);
break;
case "day":
newDate.setDate(newDate.getDate() + value);
break;
case "hour":
newDate.setHours(newDate.getHours() + value);
break;
case "minute":
newDate.setMinutes(newDate.getMinutes() + value);
break;
case "second":
newDate.setSeconds(newDate.getSeconds() + value);
break;
}
return new DayUts(newDate);
}
/**
* 减少时间
*/
subtract(value: number, unit: "day" | "month" | "year" | "hour" | "minute" | "second"): DayUts {
return this.add(-value, unit);
}
/**
* 获取时间戳
*/
valueOf(): number {
return this._date.getTime();
}
/**
* 获取原生Date对象
*/
toDate(): Date {
return new Date(this._date.getTime());
}
/**
* 获取日期数组
*/
toArray(): number[] {
return [
this._date.getFullYear(),
this._date.getMonth() + 1,
this._date.getDate(),
this._date.getHours(),
this._date.getMinutes(),
this._date.getSeconds()
];
}
/**
* 私有方法:解析不同类型的日期参数
*/
private _parseDate(date: DayUts | Date | string | number): Date {
if (date instanceof DayUts) {
return date.toDate();
} else if (date instanceof Date) {
return date;
} else if (typeof date == "string") {
return new Date(date);
} else if (typeof date == "number") {
return new Date(date);
} else {
// 如果都不匹配,返回当前时间
return new Date();
}
}
}
/**
* 创建 DayUts 实例
*/
export function dayUts(date: Date | string | number | null = new Date()): DayUts {
return new DayUts(date);
}

View File

@@ -0,0 +1,84 @@
/**
* 检查是否为小程序环境
* @returns 是否为小程序环境
*/
export const isMp = (): boolean => {
// #ifdef MP
return true;
// #endif
return false;
};
/**
* 检查是否为App环境
* @returns 是否为App环境
*/
export const isApp = (): boolean => {
// #ifdef APP
return true;
// #endif
return false;
};
/**
* 检查是否为App-IOS环境
* @returns 是否为App-IOS环境
*/
export const isAppIOS = (): boolean => {
// #ifdef APP-IOS
return true;
// #endif
return false;
};
/**
* 检查是否为App-Android环境
* @returns 是否为App-Android环境
*/
export const isAppAndroid = (): boolean => {
// #ifdef APP-ANDROID
return true;
// #endif
return false;
};
/**
* 检查是否为H5环境
* @returns 是否为H5环境
*/
export const isH5 = (): boolean => {
// #ifdef H5
return true;
// #endif
return false;
};
/**
* 检查是否为鸿蒙环境
* @returns 是否为鸿蒙环境
*/
export const isHarmony = (): boolean => {
// #ifdef APP-HARMONY
return true;
// #endif
return false;
};
/**
* 获取设备像素比
* @returns 设备像素比
*/
export const getDevicePixelRatio = (): number => {
const dpr = uni.getDeviceInfo().devicePixelRatio ?? 1;
// #ifdef MP
// 微信小程序高清处理
return 3;
// #endif
return dpr;
};

View File

@@ -0,0 +1,74 @@
import { base64ToBlob, uuid } from "./comm";
/**
* 将canvas转换为png图片
* @param options 转换参数
* @returns 图片路径
*/
export function canvasToPng(canvasRef: UniElement): Promise<string> {
return new Promise((resolve) => {
// #ifdef APP
canvasRef.parentElement!.takeSnapshot({
success(res) {
resolve(res.tempFilePath);
},
fail(err) {
console.error(err);
resolve("");
}
});
// #endif
// #ifdef H5
const url = URL.createObjectURL(
base64ToBlob(
(canvasRef as unknown as HTMLCanvasElement)?.toDataURL("image/png", 1) ?? ""
)
);
resolve(url);
// #endif
// #ifdef MP
uni.createCanvasContextAsync({
id: canvasRef.id,
component: canvasRef.$vm,
success(context) {
// 获取2D绘图上下文
const ctx = context.getContext("2d")!;
// 获取canvas对象
const canvas = ctx.canvas;
// 将canvas转换为base64格式的PNG图片数据
const data = canvas.toDataURL("image/png", 1);
// 获取文件系统管理器
const fileMg = uni.getFileSystemManager();
// 生成临时文件路径
// @ts-ignore
const filepath = `${wx.env.USER_DATA_PATH}/${uuid()}.png`;
// 将base64数据写入文件
fileMg.writeFile({
filePath: filepath,
data: data.split(",")[1],
encoding: "base64",
success() {
resolve(filepath);
},
fail(err) {
console.error(err);
resolve("");
}
});
},
fail(err) {
console.error(err);
resolve("");
}
});
// #endif
});
}

View File

@@ -0,0 +1,9 @@
export * from "./tailwind";
export * from "./comm";
export * from "./day";
export * from "./device";
export * from "./file";
export * from "./parse";
export * from "./path";
export * from "./rect";
export * from "./storage";

View File

@@ -0,0 +1,244 @@
import { forEach, forInObject, isArray, isObject, isString } from "./comm";
/**
* 解析数据
* @example parse<Response>(res.data)
*/
export function parse<T>(data: any): T | null {
// #ifdef APP-ANDROID
// @ts-ignore
return (data as UTSJSONObject).parse<T>();
// #endif
// #ifndef APP-ANDROID
return data as T;
// #endif
}
/**
* 解析JSON对象
* @param data 要解析的数据
* @returns 解析后的JSON对象
*/
export function parseObject<T>(data: string): T | null {
// #ifdef APP-ANDROID
return JSON.parseObject<T>(data);
// #endif
// #ifndef APP-ANDROID
return JSON.parse(data) as T;
// #endif
}
/**
* 解析透传样式对象
* @param data 要解析的数据
* @returns 解析后的透传样式对象
* @template T 透传样式对象的类型
*/
export function parsePt<T>(data: any): T {
// #ifdef APP-ANDROID
// @ts-ignore
return (data as UTSJSONObject).parse<T>() ?? ({} as T);
// #endif
// #ifndef APP-ANDROID
return data as T;
// #endif
}
/**
* 解析对象为类名字符串
* @param obj 要解析的对象,key为类名,value为布尔值表示是否启用该类名
* @returns 解析后的类名字符串,多个类名以空格分隔
* @example
* parseClass({ 'active': true, 'disabled': false }) // 返回 'active'
* parseClass(['ml-2', 'mr-2']) // 返回 'ml-2 mr-2'
* parseClass([{ 'mr-2': true, 'mt-2': false }]) // 返回 'mr-2'
* parseClass([[true, 'mr-2 pt-2', 'mb-2']]) // 返回 'mr-2 pt-2'
*/
export const parseClass = (data: any): string => {
// 存储启用的类名
const names: string[] = [];
// 解析数据
function deep(d: any) {
// 如果obj是数组,则将数组中的每个元素添加到names中
if (isArray(d)) {
forEach(d as any[], (value: any) => {
if (isString(value)) {
// @example 2
names.push(value as string);
} else if (isArray(value)) {
// @example 4
const [a, b] = value as any[];
if (a as boolean) {
names.push(b as string);
} else {
if (value.length > 2) {
names.push(value[2] as string);
}
}
} else if (isObject(value)) {
// @example 3
deep(value);
}
});
}
// 遍历对象的每个属性
if (isObject(d)) {
// @example 1
forInObject(d, (value, key) => {
// 如果属性值为true,则将类名添加到数组中
if (value == true && key != "") {
names.push(key.trim());
}
});
}
}
deep(data);
// 将类名数组用空格连接成字符串返回
return names.join(" ");
};
/**
* 将自定义类型数据转换为UTSJSONObject对象
* @param data 要转换的数据
* @returns 转换后的UTSJSONObject对象
*/
export function parseToObject<T>(data: T): UTSJSONObject {
// #ifdef APP-ANDROID
return JSON.parseObject(JSON.stringify(data ?? {})!)!;
// #endif
// #ifndef APP-ANDROID
return JSON.parse(JSON.stringify(data || {})) as UTSJSONObject;
// #endif
}
/**
* 将rpx单位转换为px单位
* @param rpx 要转换的rpx值
* @returns 转换后的px值
* @example
*/
export const rpx2px = (rpx: number): number => {
let px: number;
// #ifdef MP
px = rpx / (750 / uni.getWindowInfo().windowWidth);
// #endif
// #ifndef MP
px = uni.rpx2px(rpx);
// #endif
return px;
};
/**
* 将px单位转换为rpx单位
* @param px 要转换的px值
* @returns 转换后的rpx值
* @example
*/
export const px2rpx = (px: number): number => {
return px / rpx2px(1);
};
/**
* 将数值或字符串转换为rpx单位的字符串
* @param val 要转换的值,可以是数字或字符串
* @returns 转换后的rpx单位字符串
* @example
* parseRpx(10) // 返回 '10rpx'
* parseRpx('10rpx') // 返回 '10rpx'
* parseRpx('10px') // 返回 '10px'
*/
export const parseRpx = (val: number | string): string => {
if (typeof val == "number") {
return val + "rpx";
}
return val;
};
/**
* 示例: 获取数值部分
* @example
* getNum("10rpx") // 返回 10
* getNum("10px") // 返回 10
* getNum("10") // 返回 10
* getNum("-5.5px") // 返回 -5.5
* @param val - 输入值,例如 "10rpx"、"10px"、"10"
* @returns number - 返回提取的数值
*/
export const getNum = (val: string): number => {
// 使用正则提取数字部分,支持小数和负数
const match = val.match(/-?\d+(\.\d+)?/);
return match != null ? parseFloat(match[0] ?? "0") : 0;
};
/**
* 示例: 获取单位
* @example
* getUnit("10rpx") // 返回 "rpx"
* getUnit("10px") // 返回 "px"
* @param val - 输入值,例如 "10rpx"、"10px"
* @returns string - 返回单位字符串,如 "rpx" 或 "px"
*/
export const getUnit = (val: string): string => {
const num = getNum(val);
return val.replace(`${num}`, "");
};
/**
* 示例: 转换为 rpx 值
* @example
* getRpx("10rpx") // 返回 10
* getRpx("10px") // 返回 px2rpx(10)
* getRpx(10) // 返回 10
* @param val - 输入值,可以是 "10rpx"、"10px" 或数字 10
* @returns number - 返回对应的 rpx 数值
*/
export const getRpx = (val: number | string): number => {
if (typeof val == "number") {
return val;
}
const num = getNum(val);
const unit = getUnit(val);
if (unit == "px") {
return px2rpx(num);
}
return num;
};
/**
* 示例: 转换为 px 值
* @example
* getPx("10rpx") // 返回 rpx2px(10)
* getPx("10px") // 返回 10
* getPx(10) // 返回 rpx2px(10)
* @param val - 输入值,可以是 "10rpx"、"10px" 或数字 10
* @returns number - 返回对应的 px 数值
*/
export const getPx = (val: string | number) => {
if (typeof val == "number") {
return rpx2px(val);
}
const num = getNum(val);
const unit = getUnit(val);
if (unit == "rpx") {
return rpx2px(num);
}
return num;
};

View File

@@ -0,0 +1,60 @@
/**
* 获取文件名
* @example filename("a/b/c.txt") // "c"
*/
export function filename(path: string): string {
return basename(path.substring(0, path.lastIndexOf(".")));
}
/**
* 获取路径的最后一部分
* @example basename("a/b/c.txt") // "c.txt"
*/
export function basename(path: string): string {
let index = path.lastIndexOf("/");
index = index > -1 ? index : path.lastIndexOf("\\");
if (index < 0) {
return path;
}
return path.substring(index + 1);
}
/**
* 获取文件扩展名
* @example extname("a/b/c.txt") // "txt"
*/
export function extname(path: string): string {
let index = path.lastIndexOf(".");
if (index < 0) {
return "";
}
return path.substring(index + 1);
}
/**
* 首字母大写
* @example firstUpperCase("useInfo") // "UseInfo"
*/
export function firstUpperCase(value: string): string {
return value.charAt(0).toLocaleUpperCase() + value.slice(1);
}
/**
* 获取地址栏参数
* @example getUrlParam("a") // "1"
*/
export function getUrlParam(name: string): string | null {
// #ifdef H5
const params = new URLSearchParams(window.location.search);
const value = params.get(name);
return value !== null ? decodeURIComponent(value) : null;
// #endif
}
/**
* 连接路径
* @example pathJoin("https://www.baidu.com/", "/a/b/c.txt") // "https://www.baidu.com/a/b/c.txt"
*/
export function pathJoin(...parts: string[]): string {
return parts.map((part) => part.replace(/(^\/+|\/+$)/g, "")).join("/");
}

View File

@@ -0,0 +1,68 @@
import { config } from "@/config";
import { router } from "../router";
import { isH5, isHarmony } from "./device";
import { ctx } from "../ctx";
import { getPx } from "./parse";
/**
* 是否需要计算 tabBar 高度
* @returns boolean
*/
export function hasCustomTabBar() {
if (router.isTabPage()) {
if (isHarmony()) {
return false;
}
return config.isCustomTabBar || isH5();
}
return false;
}
/**
* 是否存在自定义 topbar
* @returns boolean
*/
export function hasCustomTopbar() {
return router.route()?.isCustomNavbar ?? false;
}
/**
* 获取安全区域高度
* @param type 类型
* @returns 安全区域高度
*/
export function getSafeAreaHeight(type: "top" | "bottom") {
const { safeAreaInsets } = uni.getWindowInfo();
let h: number;
if (type == "top") {
h = safeAreaInsets.top;
} else {
h = safeAreaInsets.bottom;
// #ifdef APP-ANDROID
if (h == 0) {
h = 16;
}
// #endif
}
return h;
}
/**
* 获取 tabBar 高度
* @returns tabBar 高度
*/
export function getTabBarHeight() {
let h = ctx.tabBar.height == null ? 50 : getPx(ctx.tabBar.height!);
if (hasCustomTabBar()) {
h += getSafeAreaHeight("bottom");
}
return h;
}

View File

@@ -0,0 +1,158 @@
// 过期时间后缀,用于标识存储数据的过期时间键名
const EXPIRES_SUFFIX = "_deadtime";
/**
* 存储管理类
*
* 封装了 uni-app 的存储 API提供更便捷的存储操作
* 支持数据过期时间管理,自动处理过期数据
*/
class Storage {
/**
* 获取存储数据
*
* @param key 存储键名
* @returns 存储的数据,如果不存在则返回 null
*
* @example
* const userData = storage.get('user');
* if (userData != null) {
* console.log(userData);
* }
*/
get(key: string): any | null {
return uni.getStorageSync(key);
}
/**
* 获取所有存储数据的信息
*
* 遍历所有存储键,返回包含所有键值对的对象
* 注意:此方法会读取所有存储数据,大量数据时需注意性能
*
* @returns 包含所有存储数据的对象
*
* @example
* const allData = storage.info();
* console.log('所有存储数据:', allData);
*/
info() {
// 获取存储信息,包含所有键名
const info = uni.getStorageInfoSync();
// 创建空对象用于存放所有数据
const d = {};
// 遍历所有键名,获取对应的值
info.keys.forEach((e) => {
d[e] = this.get(e);
});
return d;
}
/**
* 设置存储数据
*
* @param key 存储键名
* @param value 要存储的数据,支持任意类型
* @param expires 过期时间默认为0表示永不过期
*
* @example
* // 存储永久数据
* storage.set('user', { name: '张三', age: 25 }, 0);
*
* // 存储5分钟后过期的数据
* storage.set('token', 'abc123', 300);
*/
set(key: string, value: any, expires: number): void {
// 存储主要数据
uni.setStorageSync(key, value);
// 如果设置了过期时间,则存储过期时间戳
if (expires > 0) {
// 计算过期时间戳:当前时间 + 过期时间(秒转毫秒)
const expireTime = new Date().getTime() + expires * 1000;
uni.setStorageSync(`${key}${EXPIRES_SUFFIX}`, expireTime);
}
}
/**
* 检查数据是否已过期
*
* @param key 存储键名
* @returns true表示已过期或无过期时间设置false表示未过期
*
* @example
* if (storage.isExpired('token')) {
* console.log('token已过期');
* }
*/
isExpired(key: string): boolean {
// 获取过期时间戳
const value = uni.getStorageSync(`${key}${EXPIRES_SUFFIX}`) as number | null;
// 如果没有设置过期时间,视为已过期
if (value == null) {
return true;
}
// 比较过期时间戳与当前时间,判断是否过期
return value - new Date().getTime() <= 0;
}
/**
* 删除存储数据
*
* 会同时删除数据本身和对应的过期时间
*
* @param key 存储键名
*
* @example
* storage.remove('user');
* storage.remove('token');
*/
remove(key: string) {
// 删除主要数据
uni.removeStorageSync(key);
// 删除对应的过期时间数据
uni.removeStorageSync(`${key}${EXPIRES_SUFFIX}`);
}
/**
* 清空所有存储数据
*
* 警告:此操作会删除所有本地存储数据,请谨慎使用
*
* @example
* storage.clear(); // 清空所有数据
*/
clear() {
uni.clearStorageSync();
}
/**
* 获取数据后立即删除(一次性读取)
*
* 适用于临时数据、一次性令牌等场景
* 读取后数据会被自动删除,确保数据的一次性使用
*
* @param key 存储键名
* @returns 存储的数据,如果不存在则返回 null
*
* @example
* const tempToken = storage.once('temp_token');
* // tempToken 使用后,存储中的 temp_token 已被删除
*/
once(key: string): any | null {
// 先获取数据
const value = this.get(key);
// 立即删除数据
this.remove(key);
// 返回获取到的数据
return value;
}
}
// 导出存储实例,提供全局访问
export const storage = new Storage();

View File

@@ -0,0 +1,28 @@
/**
* 判断 Tailwind class 字符串中是否包含文本颜色类(如 text-red, text-red-500, text-sky 等)
* @param className 传入的 class 字符串
* @returns 是否包含文本颜色类
*/
export function hasTextColor(className: string): boolean {
if (className == "") return false;
const regex =
/\btext-(primary|surface|red|blue|green|yellow|purple|pink|indigo|gray|grey|black|white|orange|amber|lime|emerald|teal|cyan|sky|violet|fuchsia|rose|slate|zinc|neutral|stone)(?:-\d+)?\b/;
return regex.test(className);
}
/**
* 判断 Tailwind class 字符串中是否包含字体大小类
* 支持如 text-xs, text-sm, text-base, text-lg, text-xl, 以及 text-[22px]、text-[22rpx] 等自定义写法
* @param className 传入的 class 字符串
* @returns 是否包含字体大小类
*/
export function hasTextSize(className: string): boolean {
if (className == "") return false;
const regex =
/\btext-(xs|sm|md|base|lg|xl|2xl|3xl|4xl|5xl|6xl|7xl|8xl|9xl|\[\d+[a-zA-Z%]*\])\b/;
return regex.test(className);
}