| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547 |
- import {
- APP_NAME,
- APP_VERSION,
- APP_NAME_TEXT,
- ENABLE_UPGRADE_VIP,
- } from "@/config/index";
- import {LOGIN_ID_STORAGE_KEY } from '@/xiaolanbenlib/constant'
- import Taro, { FileSystemManager } from "@tarojs/taro";
- import { error } from "console";
- import { useRef, useCallback, useEffect } from "react";
- const appTokenKey = `${APP_NAME}+${APP_VERSION}token`;
- export const getToken = () => {
- return Taro.getStorageSync(appTokenKey);
- };
- export const setToken = (value: string) => {
- Taro.setStorageSync(appTokenKey, value);
- };
- export const removeToken = () => {
- Taro.setStorageSync(appTokenKey, "");
- };
- export const getSuffix = (filePath: string) => {
- const suffix = filePath.split(".").pop() ?? "";
- return suffix;
- };
- const chars =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
- // Use a lookup table to find the index.
- const lookup = typeof Uint8Array === "undefined" ? [] : new Uint8Array(256);
- for (let i = 0; i < chars.length; i++) {
- lookup[chars.charCodeAt(i)] = i;
- }
- export const encode = (arraybuffer: ArrayBuffer): string => {
- let bytes = new Uint8Array(arraybuffer),
- i,
- len = bytes.length,
- base64 = "";
- for (i = 0; i < len; i += 3) {
- base64 += chars[bytes[i] >> 2];
- base64 += chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)];
- base64 += chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)];
- base64 += chars[bytes[i + 2] & 63];
- }
- if (len % 3 === 2) {
- base64 = base64.substring(0, base64.length - 1) + "=";
- } else if (len % 3 === 1) {
- base64 = base64.substring(0, base64.length - 2) + "==";
- }
- return base64;
- };
- export const decode = (base64: string): ArrayBuffer => {
- let bufferLength = base64.length * 0.75,
- len = base64.length,
- i,
- p = 0,
- encoded1,
- encoded2,
- encoded3,
- encoded4;
- if (base64[base64.length - 1] === "=") {
- bufferLength--;
- if (base64[base64.length - 2] === "=") {
- bufferLength--;
- }
- }
- const arraybuffer = new ArrayBuffer(bufferLength),
- bytes = new Uint8Array(arraybuffer);
- for (i = 0; i < len; i += 4) {
- encoded1 = lookup[base64.charCodeAt(i)];
- encoded2 = lookup[base64.charCodeAt(i + 1)];
- encoded3 = lookup[base64.charCodeAt(i + 2)];
- encoded4 = lookup[base64.charCodeAt(i + 3)];
- bytes[p++] = (encoded1 << 2) | (encoded2 >> 4);
- bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2);
- bytes[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63);
- }
- return arraybuffer;
- };
- export function throttle(func: Function, delay: number): Function {
- let timer: NodeJS.Timeout | null = null;
- return function (...args: any[]) {
- if (!timer) {
- func.apply(this, args);
- timer = setTimeout(() => {
- timer = null;
- }, delay);
- }
- };
- }
- export function debounce(func: Function, delay: number): Function {
- let timer: NodeJS.Timeout | null = null;
- return function (...args: any[]) {
- if (timer) {
- clearTimeout(timer);
- }
- timer = setTimeout(() => {
- func.apply(this, args);
- timer = null;
- }, delay);
- };
- }
- /**
- * 自定义Hook实现debounce功能
- * @param value 要延迟的值
- * @param delay 延迟时间(毫秒)
- * @returns 经过debounce处理后的值
- */
- export function useDebounce(fn: () => void, delay: number) {
- const timer = useRef<null | ReturnType<typeof setTimeout>>(null);
- return () => {
- timer.current && clearTimeout(timer.current);
- timer.current = setTimeout(fn, delay);
- };
- }
- // 修改 useDebounce 使其支持任意参数
- export function useDebounceWithParams<T extends (...args: any[]) => any>(
- fn: T,
- delay: number
- ): T {
- const timerRef = useRef<NodeJS.Timeout | null>(null);
- return useCallback(
- (...args: Parameters<T>) => {
- if (timerRef.current) {
- clearTimeout(timerRef.current);
- }
- timerRef.current = setTimeout(() => {
- fn(...args);
- }, delay);
- },
- [fn, delay]
- ) as T;
- }
- export const formatDate = (date: Date): string => {
- const year = date.getFullYear();
- const month = String(date.getMonth() + 1).padStart(2, "0");
- const day = String(date.getDate()).padStart(2, "0");
- return `${year}-${month}-${day}`;
- };
- export const formatDateFull = (date: Date): string => {
- const year = date.getFullYear();
- const month = String(date.getMonth() + 1).padStart(2, "0"); // 月份从 0 开始,所以加 1
- const day = String(date.getDate()).padStart(2, "0");
- const hours = String(date.getHours()).padStart(2, "0");
- const minutes = String(date.getMinutes()).padStart(2, "0");
- return `${year}-${month}-${day} ${hours}:${minutes}`;
- };
- export const formatSeconds = (seconds: number): string => {
- // 计算分钟数
- const minutes = Math.floor(seconds / 60);
- // 计算剩余的秒数
- const remainingSeconds = seconds % 60;
- // 确保分钟和秒都以两位数的形式显示
- const formattedMinutes = String(minutes).padStart(2, "0");
- const formattedSeconds = String(remainingSeconds).padStart(2, "0");
- // 返回格式化的字符串
- return `${formattedMinutes}:${formattedSeconds}`;
- };
- // 是否是中文字符
- export function isChineseChatacter(str: string): boolean {
- const code = str.charCodeAt(0);
- if (
- (code >= 0x4e00 && code <= 0x9fff) ||
- (code >= 0x3400 && code <= 0x4dbf) ||
- (code >= 0x20000 && code <= 0x2a6df) ||
- (code >= 0x2a700 && code <= 0x2b73f) ||
- (code >= 0x2b740 && code <= 0x2b81f) ||
- (code >= 0x2b820 && code <= 0x2ceaf) ||
- (code >= 0xf900 && code <= 0xfaff) ||
- (code >= 0x2f800 && code <= 0x2fa1f)
- ) {
- return true;
- }
- return false;
- }
- export function countCharacters(str: string): number {
- let count = 0;
- const arr = Array.from(str);
- for (let i = 0, j = arr.length; i < j; i++) {
- // 判断是否为中文字符
- if (isChineseChatacter(arr[i])) {
- count += 1;
- } else {
- // 英文字符每两个算一个
- count += 0.5;
- }
- }
- return Math.ceil(count);
- }
- export const getStrByMaxLength = (str: string, length: number) => {
- const arr = Array.from(str);
- let result = "";
- let l = 0;
- for (let i = 0, j = arr.length; i < j; i++) {
- const s = arr[i];
- if (isChineseChatacter(s)) {
- l += 1;
- } else {
- l += 0.5;
- }
- result += s;
- if (Math.ceil(l) >= length) {
- return result;
- }
- }
- return result;
- };
- export const navToWebView = (url: string) => {
- Taro.navigateTo({
- url: `/pages/webview/index?url=${encodeURIComponent(url)}`,
- });
- };
- export const getCanvasTempPath = (
- canvas: any,
- canvasId: string
- ): Promise<string> => {
- return new Promise((resolve, reject) => {
- Taro.canvasToTempFilePath(
- {
- x: 0,
- y: 0,
- fileType: "jpg",
- quality: 0.8,
- canvas: canvas,
- canvasId: canvasId,
- success: (res) => {
- console.log(res, res.tempFilePath);
- resolve(res.tempFilePath);
- // wx.showToast({
- // title: 'canvas转图成功' + res.tempFilePath,
- // duration: 3000
- // })
- },
- fail: (res) => {
- console.log(res);
- Taro.showToast({
- title: "canvas转图片失败" + JSON.stringify(res),
- icon: "none",
- duration: 3000,
- });
- reject("");
- },
- },
- this
- );
- });
- };
- export const saveMediaFile = async (tmpPath: string, isVideo: boolean = false): Promise<boolean> => {
- return new Promise((resove) => {
- const params = {
- filePath: tmpPath,
- success() {
- console.log("save success");
- resove(true);
- },
- fail(error: any) {
- console.log("save fail", error);
- resove(false);
- },
- };
- isVideo ? Taro.saveVideoToPhotosAlbum(params) : Taro.saveImageToPhotosAlbum(params);
- });
- };
- export const getCloneVoiceIdentifier = (number: number): string => {
- const formattedNumber = number.toString().padStart(2, "0");
- return `克隆声音${formattedNumber}`;
- };
- export const navToVip = () => {
- if (ENABLE_UPGRADE_VIP) {
- Taro.navigateTo({ url: "/pages/vip/index" });
- return;
- }
- Taro.showModal({
- content: "此功能暂不可用",
- showCancel: false,
- });
- };
- export const getTempFileContent = <T>(
- tmpPath: string,
- encoding?: keyof FileSystemManager.Encoding
- ): Promise<T> => {
- return new Promise((resolve, reject) => {
- const fs = Taro.getFileSystemManager();
- fs.readFile({
- filePath: tmpPath,
- encoding: encoding,
- success: (res) => {
- const fileContent = res.data as T;
- // console.log(
- // "%c [ fileContent ]: ",
- // "color: #bf2c9f; background: pink; font-size: 13px;",
- // fileContent
- // );
- resolve(fileContent);
- },
- fail: (err) => {
- console.error("读取文件失败", err);
- reject(err);
- },
- });
- });
- };
- export const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
- export function generateRandomId(): string {
- const timestamp = Date.now().toString(36); // 当前时间戳转换为 base-36 字符串
- const randomNum = Math.random().toString(36).substring(2, 8); // 生成随机数并转换为 base-36 字符串
- return `${timestamp}-${randomNum}`; // 组合时间戳和随机数
- }
- /**
- * 生成符合 RFC 4122 版本 4 的 UUID
- * @returns 格式为 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx' 的 UUID 字符串
- */
- export function generateUUID(): string {
- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
- const r = (Math.random() * 16) | 0;
- const v = c === 'x' ? r : (r & 0x3) | 0x8;
- return v.toString(16);
- });
- }
- export const pickNonEmpty = <T extends object>(obj: T): Partial<T> => {
- return Object.fromEntries(
- Object.entries(obj).filter(([_, value]) =>
- value !== "" && value != null
- )
- ) as Partial<T>;
- };
- export const getLoginId = () => {
- return Taro.getStorageSync(LOGIN_ID_STORAGE_KEY) // 打开小程序时创建 login_uuid
- }
- export const isSuccess = (status: number)=> {
- return status >= 200 && status < 300
- }
- /**
- * 文件大小单位类型
- */
- export type FileSizeUnit = 'bit' | 'B' | 'KB' | 'MB' | 'GB' | 'TB';
- /**
- * 文件大小转换配置
- */
- export interface FileSizeOptions {
- /** 保留小数位数,默认2位 */
- precision?: number;
- /** 是否使用二进制单位(1024进制),默认为true */
- binary?: boolean;
- /** 强制转换为指定单位,不自动选择 */
- unit?: FileSizeUnit;
- /** 是否添加单位后缀,默认为true */
- withUnit?: boolean;
- }
- /**
- * 文件大小转换工具
- * @param size 文件大小(bit)
- * @param options 转换配置
- * @returns 转换后的文件大小字符串或数字
- */
- export function convertFileSize(size: number, options: FileSizeOptions = {}): string | number {
- const {
- precision = 2,
- binary = true,
- unit,
- withUnit = true
- } = options;
- // 检查输入是否为有效数字
- if (isNaN(size) || !isFinite(size)) {
- return withUnit ? 'Invalid size' : NaN;
- }
- // 处理零值
- if (size === 0) {
- return withUnit ? `0${unit || 'B'}` : 0;
- }
- // 计算转换基数
- const base = binary ? 1024 : 1000;
- // 定义单位列表
- const units: FileSizeUnit[] = ['bit', 'B', 'KB', 'MB', 'GB', 'TB'];
- // 如果指定了单位,直接转换
- if (unit) {
- const targetIndex = units.indexOf(unit);
- // 特殊处理bit到B的转换(1字节=8比特)
- if (unit === 'B') {
- size = size / 8;
- } else if (unit === 'bit') {
- // 如果目标是bit,直接返回原始值
- return withUnit ? `${size} bit` : size;
- }
- // 计算从B到目标单位的转换
- if (targetIndex > 1) {
- size = size / 8 / Math.pow(base, targetIndex - 1);
- }
- const result = Number(size.toFixed(precision));
- return withUnit ? `${result} ${unit}` : result;
- }
- // 特殊处理bit和B
- if (size < 8) {
- return withUnit ? `${size} bit` : size;
- }
- if (size < base * 8) {
- const result = Number((size / 8).toFixed(precision));
- return withUnit ? `${result} B` : result;
- }
- // 自动选择合适的单位
- let unitIndex = 2; // 从KB开始
- let convertedSize = size / 8; // 先转换为字节
- while (convertedSize >= base && unitIndex < units.length - 1) {
- convertedSize /= base;
- unitIndex++;
- }
- const result = Number(convertedSize.toFixed(precision));
- return withUnit ? `${result} ${units[unitIndex]}` : result;
- }
- //
- export const pxToRpx = (px: number) => {
- const systemInfo = Taro.getSystemInfoSync()
- return (750 / systemInfo.windowWidth) * px
- }
- /**
- * 为图片URL添加阿里云OSS处理参数
- * 如果URL中不包含?x-oss-process=,则添加?x-oss-process=image/quality,Q_60/format,jpg
- * 如果URL中已包含其他查询参数,则追加&x-oss-process=image/quality,Q_60/format,jpg
- * @param url 原始图片URL
- * @returns 处理后的图片URL
- */
- export function addOssProcessLowQualityParam(url: string): string {
- // 检查URL是否有效
- if (!url || typeof url !== 'string') {
- return url;
- }
- // 检查是否已经包含OSS处理参数
- if (url.includes('x-oss-process=')) {
- return url;
- }
- // 分离URL和查询参数
- const [baseUrl, existingQuery] = url.split('?');
- // 构建新的查询参数
- const ossParam = 'x-oss-process=image/quality,Q_60/format,jpg';
- const newQuery = existingQuery
- ? `${existingQuery}&${ossParam}` // 已有查询参数,追加OSS参数
- : ossParam; // 没有查询参数,直接使用OSS参数
- // 返回处理后的URL
- return `${baseUrl}?${newQuery}`;
- }
- // 使用示例
- // const exampleUrl1 = "https://example.com/image.jpg";
- // const exampleUrl2 = "https://example.com/image.png?width=200&height=200";
- // const exampleUrl3 = "https://example.com/image.webp?x-oss-process=image/resize,w_300";
- // console.log(addOssProcessParam(exampleUrl1));
- // // 输出: https://example.com/image.jpg?x-oss-process=image/quality,Q_60/format,jpg
- // console.log(addOssProcessParam(exampleUrl2));
- // // 输出: https://example.com/image.png?width=200&height=200&x-oss-process=image/quality,Q_60/format,jpg
- // console.log(addOssProcessParam(exampleUrl3));
- // // 输出: https://example.com/image.webp?x-oss-process=image/resize,w_300 (保持不变,因为已包含OSS参数)
- export function restrictedPage() {
- // 方式一:不定义分享相关函数,默认就是禁止分享
- // 方式二:显式返回不允许分享(两种分享方式都要处理)
- useEffect(() => {
- // 禁止单页分享
- Taro.showShareMenu({
- withShareTicket: false,
- menus: [] // 不提供任何分享菜单
- })
- // 禁止分享到朋友圈
- Taro.hideShareMenu({
- menus: ['shareAppMessage', 'shareTimeline']
- })
- }, [])
- }
|