| import { type Ref } from "vue"; | ||
| export declare const useRef: <T extends abstract new (...args: any[]) => any>(_c: T) => Ref<InstanceType<T>, InstanceType<T>>; | ||
| export declare function useRefs(): { | ||
| refs: { | ||
| [key: string]: any; | ||
| }; | ||
| setRefs: (name: string) => (el: any) => () => any; | ||
| }; | ||
| export declare function useParent(name: string, r: Ref): Ref<any, any>; | ||
| export * from "./useEffect"; | ||
| export * from "./useBrowser"; | ||
| export * from "./useLoading"; |
| import { ref, reactive, getCurrentInstance } from "vue"; | ||
| export const useRef = (_c) => { | ||
| return ref(); | ||
| }; | ||
| export function useRefs() { | ||
| const refs = reactive({}); | ||
| function setRefs(name) { | ||
| return (el) => { | ||
| refs[name] = el; | ||
| return () => refs[name]; | ||
| }; | ||
| } | ||
| return { refs, setRefs }; | ||
| } | ||
| export function useParent(name, r) { | ||
| const d = getCurrentInstance(); | ||
| if (d) { | ||
| let parent = d.proxy?.$.parent; | ||
| if (parent) { | ||
| while (parent && parent.type?.name != name) { | ||
| parent = parent?.parent; | ||
| } | ||
| if (parent) { | ||
| if (parent.type.name == name) { | ||
| r.value = parent.exposed; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| return r; | ||
| } | ||
| export * from "./useEffect"; | ||
| export * from "./useBrowser"; | ||
| export * from "./useLoading"; |
| export declare function useBrowser(): { | ||
| browser: { | ||
| height: number; | ||
| width: number; | ||
| type: string; | ||
| plat: string; | ||
| tag: string; | ||
| prefix: string; | ||
| isMobile: boolean; | ||
| isIOS: boolean; | ||
| isPC: boolean; | ||
| isMini: boolean; | ||
| screen: string; | ||
| }; | ||
| onScreenChange(ev: () => void, immediate?: boolean): void; | ||
| }; |
| import { reactive, watch } from "vue"; | ||
| import { getBrowser } from "../utils"; | ||
| const browser = reactive(getBrowser()); | ||
| const events = []; | ||
| watch(() => browser.screen, () => { | ||
| events.forEach((ev) => ev()); | ||
| }); | ||
| window.addEventListener("resize", () => { | ||
| Object.assign(browser, getBrowser()); | ||
| }); | ||
| export function useBrowser() { | ||
| return { | ||
| browser, | ||
| onScreenChange(ev, immediate = true) { | ||
| events.push(ev); | ||
| if (immediate) { | ||
| ev(); | ||
| } | ||
| }, | ||
| }; | ||
| } |
| export declare const useEffect: (effect: any, deps: any) => void; |
| import { ref, watchEffect, watch, onMounted, onUnmounted } from "vue"; | ||
| export const useEffect = (effect, deps) => { | ||
| let cleanup; | ||
| const isMounted = ref(false); | ||
| // 处理清理函数 | ||
| const runCleanup = () => { | ||
| if (cleanup && typeof cleanup === "function") { | ||
| cleanup(); | ||
| } | ||
| }; | ||
| // 主执行逻辑 | ||
| const execute = () => { | ||
| runCleanup(); | ||
| cleanup = effect(); | ||
| }; | ||
| if (!deps) { | ||
| // 无依赖项 - 每次更新都运行 | ||
| watchEffect((onInvalidate) => { | ||
| if (!isMounted.value) { | ||
| isMounted.value = true; | ||
| return; | ||
| } | ||
| execute(); | ||
| onInvalidate(runCleanup); | ||
| }); | ||
| } | ||
| else if (deps.length === 0) { | ||
| // 空依赖数组 - 仅挂载时运行 | ||
| onMounted(() => { | ||
| execute(); | ||
| }); | ||
| } | ||
| else { | ||
| // 有依赖项 - 依赖变化时运行 | ||
| watch(deps, (newVal, oldVal, onInvalidate) => { | ||
| execute(); | ||
| onInvalidate(runCleanup); | ||
| }, { immediate: true }); | ||
| } | ||
| // 组件卸载时清理 | ||
| onUnmounted(() => { | ||
| runCleanup(); | ||
| }); | ||
| }; |
| export declare function useLoading(initValue?: boolean): { | ||
| loading: import("vue").Ref<boolean, boolean>; | ||
| setLoading: (value: boolean) => void; | ||
| toggle: () => void; | ||
| }; |
| import { ref } from "vue"; | ||
| export function useLoading(initValue = false) { | ||
| const loading = ref(initValue); | ||
| const setLoading = (value) => { | ||
| loading.value = value; | ||
| }; | ||
| const toggle = () => { | ||
| loading.value = !loading.value; | ||
| }; | ||
| return { | ||
| loading, | ||
| setLoading, | ||
| toggle, | ||
| }; | ||
| } |
| export * from './hooks'; | ||
| export * from './utils'; | ||
| export * from '../lib/createService.ts'; |
| /** | ||
| * 深度优化遍历的深拷贝 | ||
| * @param {<T>} x | ||
| */ | ||
| export declare function cloneDeep(x: { | ||
| [k in string]: any; | ||
| }, cache?: boolean): { | ||
| [x: string]: any; | ||
| }; |
| /* | ||
| * 作者: merlinhong | ||
| * 版本: [1.0] | ||
| * 描述: 深拷贝 | ||
| */ | ||
| /** | ||
| * 深度优化遍历的深拷贝 | ||
| * @param {<T>} x | ||
| */ | ||
| export function cloneDeep(x, cache = false) { | ||
| const root = {}; | ||
| // 初始条件,循环的初始条件为一个节点栈,栈中存放当前需要执行操作的对象,这里是深克隆 | ||
| const looplist = [ | ||
| { | ||
| parent: root, | ||
| key: '', | ||
| data: x, | ||
| }, | ||
| ]; | ||
| // 判断条件,判断条件为栈是否为空,也就是判断所有深层次对象是否全部克隆完毕 | ||
| while (looplist.length) { | ||
| // 循环体,取出当前待克隆的节点属性,包括节点的id、节点的val、以及父节点(这里都是指向最终克隆完成的root对象) | ||
| const node = looplist.pop(); | ||
| const parent = node?.parent; // 对象引用指向 root {} | ||
| const key = node?.key; | ||
| const data = node?.data; | ||
| // 此处是深克隆的核心循环体代码, | ||
| let res = {}; // 创建一个临时变量,用来传递引用关系 | ||
| if (typeof key !== "undefined") { | ||
| res = parent[key] = {}; // 继续给当前节点创建拷贝对象,通过引用传递指向root同一内存空间 | ||
| } | ||
| // 循环条件, 这里通过判断子节点是否为对象作为循环体执行下去继续深克隆的循环条件 | ||
| for (let k in data) { | ||
| if (Object.prototype.hasOwnProperty.call(data, k)) { | ||
| if (typeof data[k] === "object" && data[k] !== null) { | ||
| // 下一次循环 | ||
| looplist.push({ | ||
| parent: res, | ||
| key: k, | ||
| data: data[k], | ||
| }); | ||
| } | ||
| else { | ||
| res[k] = data[k]; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| return root; | ||
| } |
| export declare class MyDate extends Date { | ||
| /** | ||
| * 定义静态方法,解析成拓展的日期对象 | ||
| * @param {string} dateString 日期字符串 | ||
| * @param {string} pattern 匹配字符串,可以手动传入,或者采取默认 | ||
| * @return {MyDate} 返回一个MyDate对象 | ||
| */ | ||
| parseDate(dateString: string, pattern?: string): MyDate; | ||
| /** | ||
| * 写自己的原型方法,这里实现一个按任何格式的输出方法 | ||
| * @param {string} fmt 自己需要输出的格式,经典的yyyyMMddhhmmssiiww系列 | ||
| * @return {string} 返回一个字符串 | ||
| */ | ||
| format(fmt?: string): string; | ||
| /** | ||
| * 获取绝对毫秒数,这里补上了getTime造成的时区差 | ||
| * @param {boolean} isTimezoneOffsetFixed 是否补齐时区误差 | ||
| * @return {number} 返回绝对时间戳 | ||
| */ | ||
| getAbsoluteMillonsTime(isTimezoneOffsetFixed: boolean): number; | ||
| /** | ||
| * 返回字符串时间戳 | ||
| * @param {boolean} isTimezoneOffsetFixed 是否补齐时区误差 | ||
| * @return {string} 返回绝对时间戳,19700101到现在的 | ||
| */ | ||
| toTimeStap(isTimezoneOffsetFixed: boolean): string; | ||
| /** | ||
| * 返回往后n秒的时间 | ||
| * @param {number} num 往后几秒小时,默认为0 | ||
| * @return {MyDate} 返回一个计算后的MyDate对象 | ||
| */ | ||
| nextSeconds(num?: number): MyDate; | ||
| nextMinutes(num?: number): MyDate; | ||
| nextHours(num?: number): MyDate; | ||
| nextDays(num?: number): MyDate; | ||
| /** | ||
| * 返回往后n月的时间,这里会自动计算每月的天数,1月后面就是2月,紧接着3月 | ||
| * @param {number} num 往后几秒小时,默认为0 | ||
| * @return {MyDate} 返回一个计算后的MyDate对象 | ||
| */ | ||
| nextMonths(num?: number): MyDate; | ||
| nextYears(num?: number): MyDate; | ||
| /** | ||
| * 和另一个时间比较,必须也是MyDate类型 | ||
| * @param {MyDate} another 另一个日期对象,也可以是一个普通的日期对象 | ||
| * @param {string} pattern 模式字符串,需要比较到何等程度 | ||
| * @return {number} 返回一个便宜数字,大于0代表当前时间大于比较时间,小于0就是小于,否则等于 | ||
| */ | ||
| compare(another: MyDate, pattern?: string): number; | ||
| } |
| /** | ||
| * 将字符串多余的长度补齐0 | ||
| * @param {string} s 需补齐的字符串 | ||
| * @param {number} len 补齐长度 | ||
| * @return {string} 补齐后的字符串 | ||
| */ | ||
| const paddingFillWith0 = (s, len) => { | ||
| len -= `${s}`.length; | ||
| for (let i = 0; i < len; i++) { | ||
| s = `0${s}`; | ||
| } | ||
| return s; | ||
| }; | ||
| /** | ||
| * 日期匹配的正则表达式 | ||
| * Y:年 | ||
| * M:月 | ||
| * D:日 | ||
| * h:小时 | ||
| * m:分钟 | ||
| * s:秒 | ||
| * i:毫秒 | ||
| * w:星期(小写的) | ||
| * W:星期(大写的) | ||
| */ | ||
| const SIGN_DATE_REG = /([YyMDdHhmsiWw])(\1*)/g; | ||
| /** | ||
| * 默认的pattern | ||
| * 'YYYY-MM-DD hh:mm:ss:iii' | ||
| */ | ||
| const DEFAULT_PATTERN = "YYYY-MM-DD hh:mm:ss:iii"; | ||
| export class MyDate extends Date { | ||
| /** | ||
| * 定义静态方法,解析成拓展的日期对象 | ||
| * @param {string} dateString 日期字符串 | ||
| * @param {string} pattern 匹配字符串,可以手动传入,或者采取默认 | ||
| * @return {MyDate} 返回一个MyDate对象 | ||
| */ | ||
| parseDate(dateString, pattern) { | ||
| if (!dateString || typeof dateString === "number") { | ||
| // 普通的构造,时间戳、纯数字以及其它多参数构造情况(多参数情况,第一个参数也是数字) | ||
| return new (Function.prototype.bind.apply(MyDate, [MyDate].concat(Array.prototype.slice.call(arguments))))(); | ||
| } | ||
| try { | ||
| dateString = dateString || ""; | ||
| // 判断需要的匹配模式 | ||
| pattern = pattern || DEFAULT_PATTERN; | ||
| // 格式的正则匹配式,譬如YYYY-MM-DD hh:mm:ss:iii | ||
| const matchs1 = pattern.match(SIGN_DATE_REG); | ||
| // 值的正则匹配式,将实际的时间数据提取出来,和前面一一对应 | ||
| const matchs2 = dateString.match(/(\d)+/g); | ||
| if (matchs1 && matchs1.length > 0) { | ||
| const myDate = new MyDate(1970, 0, 1); | ||
| for (let i = 0, len = matchs1.length; i < len; i++) { | ||
| const mTarget = parseInt(matchs2[i], 10) || 0; | ||
| switch (matchs1[i].charAt(0) || "") { | ||
| case "Y": | ||
| myDate.setFullYear(mTarget); | ||
| break; | ||
| case "M": | ||
| myDate.setMonth(mTarget - 1); | ||
| break; | ||
| case "D": | ||
| myDate.setDate(mTarget); | ||
| break; | ||
| case "h": | ||
| myDate.setHours(mTarget); | ||
| break; | ||
| case "m": | ||
| myDate.setMinutes(mTarget); | ||
| break; | ||
| case "s": | ||
| myDate.setSeconds(mTarget); | ||
| break; | ||
| case "i": | ||
| myDate.setMilliseconds(mTarget); | ||
| break; | ||
| default: | ||
| break; | ||
| } | ||
| } | ||
| return myDate; | ||
| } | ||
| } | ||
| catch (e) { | ||
| throw new Error("解析成MyDate失败,请检查传入格式!"); | ||
| } | ||
| } | ||
| /** | ||
| * 写自己的原型方法,这里实现一个按任何格式的输出方法 | ||
| * @param {string} fmt 自己需要输出的格式,经典的yyyyMMddhhmmssiiww系列 | ||
| * @return {string} 返回一个字符串 | ||
| */ | ||
| format(fmt) { | ||
| const pattern = fmt || DEFAULT_PATTERN; | ||
| const value = this; | ||
| return pattern.replace(SIGN_DATE_REG, ($0) => { | ||
| switch ($0.charAt(0)) { | ||
| case "Y": | ||
| case "y": | ||
| return paddingFillWith0(value.getFullYear().toString(), $0.length); | ||
| case "M": | ||
| return paddingFillWith0((value.getMonth() + 1).toString(), $0.length); | ||
| case "D": | ||
| case "d": | ||
| return paddingFillWith0(value.getDate().toString(), $0.length); | ||
| case "H": | ||
| case "h": | ||
| return paddingFillWith0(value.getHours().toString(), $0.length); | ||
| case "m": | ||
| return paddingFillWith0(value.getMinutes().toString(), $0.length); | ||
| case "s": | ||
| return paddingFillWith0(value.getSeconds().toString(), $0.length); | ||
| case "i": | ||
| return paddingFillWith0(value.getMilliseconds().toString(), $0.length); | ||
| case "w": | ||
| return value.getDay().toString(); | ||
| case "W": | ||
| const week = ["日", "一", "二", "三", "四", "五", "六"]; | ||
| return paddingFillWith0(week[value.getDay()], $0.length); | ||
| default: | ||
| return ""; | ||
| } | ||
| }); | ||
| } | ||
| /** | ||
| * 获取绝对毫秒数,这里补上了getTime造成的时区差 | ||
| * @param {boolean} isTimezoneOffsetFixed 是否补齐时区误差 | ||
| * @return {number} 返回绝对时间戳 | ||
| */ | ||
| getAbsoluteMillonsTime(isTimezoneOffsetFixed) { | ||
| const offset = isTimezoneOffsetFixed | ||
| ? this.getTimezoneOffset() * 60 * 1000 | ||
| : 0; | ||
| return this.getTime() + offset; | ||
| } | ||
| /** | ||
| * 返回字符串时间戳 | ||
| * @param {boolean} isTimezoneOffsetFixed 是否补齐时区误差 | ||
| * @return {string} 返回绝对时间戳,19700101到现在的 | ||
| */ | ||
| toTimeStap(isTimezoneOffsetFixed) { | ||
| return `${this.getAbsoluteMillonsTime(isTimezoneOffsetFixed)}`; | ||
| } | ||
| /** | ||
| * 返回往后n秒的时间 | ||
| * @param {number} num 往后几秒小时,默认为0 | ||
| * @return {MyDate} 返回一个计算后的MyDate对象 | ||
| */ | ||
| nextSeconds(num = 1) { | ||
| return new MyDate(this.getTime() + num * 1000); | ||
| } | ||
| nextMinutes(num = 1) { | ||
| return this.nextSeconds(60 * num); | ||
| } | ||
| nextHours(num = 1) { | ||
| return this.nextMinutes(60 * num); | ||
| } | ||
| nextDays(num = 1) { | ||
| return this.nextHours(24 * num); | ||
| } | ||
| /** | ||
| * 返回往后n月的时间,这里会自动计算每月的天数,1月后面就是2月,紧接着3月 | ||
| * @param {number} num 往后几秒小时,默认为0 | ||
| * @return {MyDate} 返回一个计算后的MyDate对象 | ||
| */ | ||
| nextMonths(num = 0) { | ||
| const originMonth = this.getMonth() + 1; | ||
| const originYear = this.getFullYear(); | ||
| const daysPerMonth = [31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; | ||
| let days = 0; | ||
| for (let i = 0; i < num; i++) { | ||
| let currMonth = originMonth + i; | ||
| const currYear = originYear + Math.floor(currMonth / 12); | ||
| const isLeapYear = currYear % 4 === 0 && (currYear % 100 !== 0 || currYear % 400 !== 0); | ||
| daysPerMonth[1] = isLeapYear ? 29 : 28; | ||
| currMonth %= 12; | ||
| if (currMonth === 0) { | ||
| currMonth = 11; | ||
| } | ||
| else { | ||
| currMonth -= 1; | ||
| } | ||
| days += daysPerMonth[currMonth]; | ||
| } | ||
| return this.nextDays(days); | ||
| } | ||
| nextYears(num = 0) { | ||
| return this.nextMonths(12 * num); | ||
| } | ||
| /** | ||
| * 和另一个时间比较,必须也是MyDate类型 | ||
| * @param {MyDate} another 另一个日期对象,也可以是一个普通的日期对象 | ||
| * @param {string} pattern 模式字符串,需要比较到何等程度 | ||
| * @return {number} 返回一个便宜数字,大于0代表当前时间大于比较时间,小于0就是小于,否则等于 | ||
| */ | ||
| compare(another, pattern = "s") { | ||
| if (!(another instanceof MyDate)) { | ||
| throw new Error("比较类型错误,必须是MyDate型"); | ||
| } | ||
| let formatPattern = ""; | ||
| switch (pattern.charAt(0)) { | ||
| case "Y": | ||
| case "y": | ||
| formatPattern = "YYYY"; | ||
| break; | ||
| case "M": | ||
| formatPattern = "YYYYMM"; | ||
| break; | ||
| case "D": | ||
| case "d": | ||
| formatPattern = "YYYYMMDD"; | ||
| break; | ||
| case "H": | ||
| case "h": | ||
| formatPattern = "YYYYMMDDhh"; | ||
| break; | ||
| case "m": | ||
| formatPattern = "YYYYMMDDhhmm"; | ||
| break; | ||
| case "S": | ||
| case "s": | ||
| formatPattern = "YYYYMMDDhhmmss"; | ||
| break; | ||
| case "I": | ||
| case "i": | ||
| formatPattern = "YYYYMMDDhhmmssiii"; | ||
| break; | ||
| default: | ||
| break; | ||
| } | ||
| const now = +this.format(formatPattern); | ||
| const anotherTime = +another.format(formatPattern); | ||
| return now - anotherTime; | ||
| } | ||
| } |
| export declare function deepMerge<T extends Object>(...objs: (Object)[]): T; |
| import { isPlainObject } from "."; | ||
| export function deepMerge(...objs) { | ||
| const result = {}; | ||
| for (const obj of objs.filter(isPlainObject)) { | ||
| for (const [key, val] of Object.entries(obj)) { | ||
| if (isPlainObject(val)) { | ||
| const rVal = result[key]; | ||
| result[key] = isPlainObject(rVal) ? deepMerge(rVal, val) : deepMerge(val); | ||
| } | ||
| else { | ||
| result[key] = val; | ||
| } | ||
| } | ||
| } | ||
| return result; | ||
| } |
| export declare function isPlainObject<T extends Object>(value: any): value is T; | ||
| export declare function getUrlParam(name: string, isHash?: boolean, target?: Window | null): string | null; | ||
| /** | ||
| * 生成随机id | ||
| * @param options | ||
| * @returns {string} | ||
| */ | ||
| export declare const uuid: (options: { | ||
| radix?: number; | ||
| len?: number; | ||
| type?: "default"; | ||
| }) => string; | ||
| export declare function sleep(duration: number): Promise<unknown>; | ||
| /** | ||
| * 浏览器AES-GCM编码 | ||
| * @param data | ||
| * @param base64Key | ||
| * @param iv | ||
| * @returns | ||
| */ | ||
| export declare function encrypt(data: string, base64Key: string, iv?: Uint8Array): Promise<string>; | ||
| export declare function getBrowser(): { | ||
| height: number; | ||
| width: number; | ||
| type: string; | ||
| plat: string; | ||
| tag: string; | ||
| prefix: string; | ||
| isMobile: boolean; | ||
| isIOS: boolean; | ||
| isPC: boolean; | ||
| isMini: boolean; | ||
| screen: string; | ||
| }; | ||
| export * from './cloneDeep'; | ||
| export * from './date'; | ||
| export * from './storage'; | ||
| export * from './type'; | ||
| export * from './loading'; | ||
| export * from './deepMerge'; |
| const _toString = Object.prototype.toString; | ||
| const IV = new Uint8Array([2, 2, 4, 8, 5, 7, 7, 8, 0, 9, 1, 4, 3, 8, 5, 6]); //初始向量 | ||
| export function isPlainObject(value) { | ||
| return _toString.call(value) === "[object Object]"; | ||
| } | ||
| // 获取地址栏参数 | ||
| export function getUrlParam(name, isHash = false, target = window) { | ||
| const reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)"); | ||
| if (isHash) { | ||
| const hash = target?.location.hash; | ||
| // // 创建 URL 对象,注意去掉 # 符号 | ||
| const urlObj = new URL((hash ?? "").slice(1), window.location.origin); | ||
| // // 使用 URLSearchParams 获取查询参数 | ||
| const params = new URLSearchParams(urlObj.search); | ||
| return params.get(name); | ||
| } | ||
| else { | ||
| const r = target?.location.search.substring(1).match(reg); | ||
| if (r != null) | ||
| return decodeURIComponent(r[2]); | ||
| return null; | ||
| } | ||
| } | ||
| /** | ||
| * 生成随机id | ||
| * @param options | ||
| * @returns {string} | ||
| */ | ||
| export const uuid = (options) => { | ||
| options = options || {}; | ||
| const chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split(""); | ||
| const uuidArr = []; | ||
| let i; | ||
| let radix = options.radix || chars.length; | ||
| let len = options.len || 5; | ||
| const type = options.type || "default"; | ||
| len = Math.min(len, 36); | ||
| len = Math.max(len, 4); | ||
| radix = Math.min(radix, 62); | ||
| radix = Math.max(radix, 2); | ||
| if (len) { | ||
| for (i = 0; i < len; i++) { | ||
| uuidArr[i] = chars[0 | (Math.random() * radix)]; | ||
| } | ||
| if (type === "default") { | ||
| len > 23 && (uuidArr[23] = "-"); | ||
| len > 18 && (uuidArr[18] = "-"); | ||
| len > 13 && (uuidArr[13] = "-"); | ||
| len > 8 && (uuidArr[8] = "-"); | ||
| } | ||
| } | ||
| return uuidArr.join(""); | ||
| }; | ||
| // 延迟 | ||
| export function sleep(duration) { | ||
| return new Promise((resolve) => { | ||
| setTimeout(() => { | ||
| resolve(true); | ||
| }, duration); | ||
| }); | ||
| } | ||
| /** | ||
| * 浏览器AES-GCM编码 | ||
| * @param data | ||
| * @param base64Key | ||
| * @param iv | ||
| * @returns | ||
| */ | ||
| export async function encrypt(data, base64Key, iv = IV) { | ||
| // 将 Base64 编码的密钥解码为 Uint8Array | ||
| const key = await window.crypto.subtle.importKey("raw", Uint8Array.from(atob(base64Key), (c) => c.charCodeAt(0)), "AES-GCM", true, ["encrypt"]); | ||
| // 生成随机 IV | ||
| // const iv = window.crypto.getRandomValues(new Uint8Array(12)); | ||
| // 将数据编码为 Uint8Array | ||
| const encodedData = new TextEncoder().encode(data); | ||
| // 使用 AES-GCM 加密数据 | ||
| const encryptedBytes = await window.crypto.subtle.encrypt({ | ||
| name: "AES-GCM", | ||
| iv, | ||
| tagLength: 128, | ||
| }, key, encodedData); | ||
| // 将 IV 和加密数据一起编码为 Base64 字符串 | ||
| const encryptedDataWithIv = new Uint8Array(12 + encryptedBytes.byteLength); | ||
| encryptedDataWithIv.set(iv); | ||
| encryptedDataWithIv.set(new Uint8Array(encryptedBytes), 12); | ||
| return btoa(String.fromCharCode(...encryptedDataWithIv)); | ||
| } | ||
| // 浏览器信息 | ||
| export function getBrowser() { | ||
| const { clientHeight, clientWidth } = document.documentElement; | ||
| // 浏览器信息 | ||
| const ua = navigator.userAgent.toLowerCase(); | ||
| // 浏览器类型 | ||
| let type = (ua.match(/firefox|chrome|safari|opera/g) || "other")[0]; | ||
| if ((ua.match(/msie|trident/g) || [])[0]) { | ||
| type = "msie"; | ||
| } | ||
| // 平台标签 | ||
| let tag = ""; | ||
| const isTocuh = "ontouchstart" in window || ua.indexOf("touch") !== -1 || ua.indexOf("mobile") !== -1; | ||
| if (isTocuh) { | ||
| if (ua.indexOf("ipad") !== -1) { | ||
| tag = "pad"; | ||
| } | ||
| else if (ua.indexOf("mobile") !== -1) { | ||
| tag = "mobile"; | ||
| } | ||
| else if (ua.indexOf("android") !== -1) { | ||
| tag = "androidPad"; | ||
| } | ||
| else { | ||
| tag = "pc"; | ||
| } | ||
| } | ||
| else { | ||
| tag = "pc"; | ||
| } | ||
| // 浏览器内核 | ||
| let prefix = ""; | ||
| switch (type) { | ||
| case "chrome": | ||
| case "safari": | ||
| case "mobile": | ||
| prefix = "webkit"; | ||
| break; | ||
| case "msie": | ||
| prefix = "ms"; | ||
| break; | ||
| case "firefox": | ||
| prefix = "Moz"; | ||
| break; | ||
| case "opera": | ||
| prefix = "O"; | ||
| break; | ||
| default: | ||
| prefix = "webkit"; | ||
| break; | ||
| } | ||
| // 操作平台 | ||
| const plat = ua.indexOf("android") > 0 ? "android" : navigator.platform.toLowerCase(); | ||
| // 屏幕信息 | ||
| let screen = "full"; | ||
| if (clientWidth < 768) { | ||
| screen = "xs"; | ||
| } | ||
| else if (clientWidth < 992) { | ||
| screen = "sm"; | ||
| } | ||
| else if (clientWidth < 1200) { | ||
| screen = "md"; | ||
| } | ||
| else if (clientWidth < 1920) { | ||
| screen = "xl"; | ||
| } | ||
| else { | ||
| screen = "full"; | ||
| } | ||
| // 是否 ios | ||
| const isIOS = !!navigator.userAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); | ||
| // 是否 PC 端 | ||
| const isPC = tag === "pc"; | ||
| // 是否移动端 | ||
| const isMobile = isPC ? false : true; | ||
| // 是否移动端 + 屏幕宽过小 | ||
| const isMini = screen === "xs" || isMobile; | ||
| return { | ||
| height: clientHeight, | ||
| width: clientWidth, | ||
| type, | ||
| plat, | ||
| tag, | ||
| prefix, | ||
| isMobile, | ||
| isIOS, | ||
| isPC, | ||
| isMini, | ||
| screen, | ||
| }; | ||
| } | ||
| export * from './cloneDeep'; | ||
| export * from './date'; | ||
| export * from './storage'; | ||
| export * from './type'; | ||
| export * from './loading'; | ||
| export * from './deepMerge'; |
| export declare const Loading: { | ||
| resolve: () => void; | ||
| next: Promise<void>; | ||
| set(list: Promise<any>[]): Promise<void>; | ||
| wait(): Promise<any>; | ||
| close(): void; | ||
| }; |
| export const Loading = { | ||
| resolve: null, | ||
| next: null, | ||
| async set(list) { | ||
| try { | ||
| await Promise.all(list); | ||
| } | ||
| catch (e) { | ||
| console.error("[Loading] Error: ", e); | ||
| } | ||
| if (this.resolve) { | ||
| this.resolve(); | ||
| } | ||
| }, | ||
| async wait() { | ||
| if (this.next) { | ||
| return this.next; | ||
| } | ||
| return Promise.resolve(); | ||
| }, | ||
| close() { | ||
| const el = document.getElementById("Loading"); | ||
| if (el) { | ||
| setTimeout(() => { | ||
| el.className += " is-hide"; | ||
| }, 0); | ||
| } | ||
| } | ||
| }; | ||
| Loading.next = new Promise((resolve) => { | ||
| Loading.resolve = resolve; | ||
| }); |
| export declare const store: { | ||
| suffix: string; | ||
| /** | ||
| * 获取 | ||
| * @param {string} key 关键字 | ||
| */ | ||
| get(key: string): any; | ||
| /** | ||
| * 获取全部 | ||
| */ | ||
| info(): any; | ||
| /** | ||
| * 设置 | ||
| * @param {string} key 关键字 | ||
| * @param {*} value 值 | ||
| * @param {number} expires 过期时间 | ||
| */ | ||
| set(key: string, value: any, expires?: any): void; | ||
| /** | ||
| * 是否过期 | ||
| * @param {string} key 关键字 | ||
| */ | ||
| isExpired(key: string): boolean; | ||
| /** | ||
| * 获取到期时间 | ||
| * @param {string} key 关键字 | ||
| */ | ||
| getExpiration(key: string): any; | ||
| /** | ||
| * 移除 | ||
| * @param {string} key 关键字 | ||
| */ | ||
| remove(key: string): void; | ||
| /** | ||
| * 移除到期时间 | ||
| * @param {string} key 关键字 | ||
| */ | ||
| removeExpiration(key: string): void; | ||
| /** | ||
| * 清理 | ||
| */ | ||
| clearAll(): void; | ||
| }; |
| import Store from "Store"; | ||
| export const store = { | ||
| // 后缀标识 | ||
| suffix: "_deadtime", | ||
| /** | ||
| * 获取 | ||
| * @param {string} key 关键字 | ||
| */ | ||
| get(key) { | ||
| return Store.get(key); | ||
| }, | ||
| /** | ||
| * 获取全部 | ||
| */ | ||
| info() { | ||
| const d = {}; | ||
| Store.each(function (value, key) { | ||
| d[key] = value; | ||
| }); | ||
| return d; | ||
| }, | ||
| /** | ||
| * 设置 | ||
| * @param {string} key 关键字 | ||
| * @param {*} value 值 | ||
| * @param {number} expires 过期时间 | ||
| */ | ||
| set(key, value, expires) { | ||
| Store.set(key, value); | ||
| if (expires) { | ||
| Store.set(`${key}${this.suffix}`, Date.parse(String(new Date())) + expires * 1000); | ||
| } | ||
| }, | ||
| /** | ||
| * 是否过期 | ||
| * @param {string} key 关键字 | ||
| */ | ||
| isExpired(key) { | ||
| return (this.getExpiration(key) || 0) - Date.parse(String(new Date())) <= 2000; | ||
| }, | ||
| /** | ||
| * 获取到期时间 | ||
| * @param {string} key 关键字 | ||
| */ | ||
| getExpiration(key) { | ||
| return this.get(key + this.suffix); | ||
| }, | ||
| /** | ||
| * 移除 | ||
| * @param {string} key 关键字 | ||
| */ | ||
| remove(key) { | ||
| Store.remove(key); | ||
| this.removeExpiration(key); | ||
| }, | ||
| /** | ||
| * 移除到期时间 | ||
| * @param {string} key 关键字 | ||
| */ | ||
| removeExpiration(key) { | ||
| Store.remove(key + this.suffix); | ||
| }, | ||
| /** | ||
| * 清理 | ||
| */ | ||
| clearAll() { | ||
| Store.clearAll(); | ||
| } | ||
| }; |
| export declare const type: (o: any, strict?: boolean) => string; |
| export const type = (o, strict = false) => { | ||
| var toString = Object.prototype.toString; | ||
| if (o === null) { | ||
| return 'null'; | ||
| } | ||
| if (typeof o == 'number' || typeof o == 'string' || typeof o === 'boolean') { | ||
| return typeof o; | ||
| } | ||
| if (typeof o == 'number' && isNaN(o)) { | ||
| return 'NaN'; | ||
| } | ||
| var cls; | ||
| var clsLow; | ||
| try { | ||
| cls = toString.call(o).slice(8, -1); | ||
| clsLow = cls.toLowerCase(); | ||
| } | ||
| catch (e) { | ||
| // ie下的 activex对象 | ||
| return 'object'; | ||
| } | ||
| if (clsLow !== 'object') { | ||
| if (strict) { | ||
| // 区分NaN和new Number | ||
| if (clsLow === 'number' && isNaN(o)) { | ||
| return 'NaN'; | ||
| } | ||
| // 区分 String() 和 new String() | ||
| if (clsLow === 'number' || clsLow === 'boolean' || clsLow === 'string') { | ||
| return cls; | ||
| } | ||
| } | ||
| return clsLow; | ||
| } | ||
| // eslint-disable-next-line eqeqeq | ||
| if (o.constructor == Object) { | ||
| return clsLow; | ||
| } | ||
| // Object.create(null) | ||
| try { | ||
| // __proto__ 部分早期firefox浏览器 | ||
| if (Object.getPrototypeOf(o) === null || o.__proto__ === null) { | ||
| return 'object'; | ||
| } | ||
| } | ||
| catch (e) { | ||
| // ie下无Object.getPrototypeOf会报错 | ||
| } | ||
| // function A() {}; new A | ||
| try { | ||
| var cname = o.constructor.name; | ||
| if (typeof cname === 'string') { | ||
| return cname; | ||
| } | ||
| } | ||
| catch (e) { | ||
| // 无constructor | ||
| } | ||
| // function A() {}; A.prototype.constructor = null; new A | ||
| return 'unknown'; | ||
| }; |
+1
-3
| export * from './createService'; | ||
| export * from './hooks'; | ||
| // export * from './utils'; | ||
| export * from './useProvider'; | ||
| export * from './type'; | ||
| export * from './utils'; |
+12
-4
| { | ||
| "name": "mooljs", | ||
| "version": "1.2.1", | ||
| "version": "2.0.0-alpha.1", | ||
| "description": "function tool library for mooljs", | ||
| "main": "lib/index.ts", | ||
| "module": "lib/index.ts", | ||
| "main": "dist/index.js", | ||
| "module": "dist/index.js", | ||
| "exports": { | ||
| ".": "./lib/index.ts" | ||
| ".": "./dist/index.js", | ||
| "./useProvider":"./dist/useProvider.js", | ||
| "./hooks":"./dist/hooks/index", | ||
| "./utils":"./dist/utils/index" | ||
| }, | ||
| "types": "index.d.ts", | ||
| "repository": { | ||
@@ -20,2 +24,6 @@ "type": "git", | ||
| ], | ||
| "files": [ | ||
| "dist", | ||
| "lib" | ||
| ], | ||
| "author": "Merlin Hong", | ||
@@ -22,0 +30,0 @@ "license": "MIT", |
-95
| import { Router } from "vue-router"; | ||
| import { App, DefineComponent, VNode } from "vue"; | ||
| type GetInitialStateReturnType<T> = T extends () => infer R ? R : never; | ||
| // 定义全局配置接口 | ||
| export interface RuntimeConfig { | ||
| routes?: IMenuRoutes[]; | ||
| layout?: | ||
| | ILayout | ||
| | (( | ||
| option: GetInitialStateReturnType<RuntimeConfig["getInitialState"]>, | ||
| ) => LayoutConfig); | ||
| getInitialState?: (typeof import("src/app.ts"))["getInitialState"]|(typeof import("src/app.tsx"))["getInitialState"]; | ||
| } | ||
| export type GetInitialState = GetInitialStateReturnType<RuntimeConfig['getInitialState']>; | ||
| // 定义路由配置接口 | ||
| export interface IMenuRoutes { | ||
| path: string; | ||
| /** | ||
| * 当前页面渲染的组件,must be a absolute path | ||
| */ | ||
| component?: string; | ||
| meta?: RouteMeta; | ||
| routes?: IMenuRoutes[]; | ||
| } | ||
| // 定义路由元数据接口 | ||
| export interface RouteMeta { | ||
| /** | ||
| * 是否在菜单中隐藏 | ||
| */ | ||
| hideInMenu?: boolean; | ||
| /** | ||
| * 是否在菜单中隐藏其子节点 | ||
| */ | ||
| hideChildrenInMenu?: boolean; | ||
| /** | ||
| * 是否渲染菜单 | ||
| */ | ||
| menuRender?: boolean; | ||
| /** | ||
| * 是否渲染页脚 | ||
| */ | ||
| footerRender?: boolean; | ||
| /** | ||
| * 是否渲染头部 | ||
| */ | ||
| headerRender?: boolean; | ||
| /** | ||
| * 当前路由是否使用内置pro layout | ||
| */ | ||
| layout?: boolean; | ||
| /** | ||
| * 权限相关设置 | ||
| */ | ||
| access?: string; | ||
| /** | ||
| * 是否打平菜单 | ||
| */ | ||
| flatMenu?: boolean; | ||
| /** | ||
| * 当前菜单icon | ||
| */ | ||
| icon?: string; | ||
| /** | ||
| * 菜单或当前页面标题 | ||
| */ | ||
| title: string; | ||
| } | ||
| export interface ILayout { | ||
| headerRender?: DefineComponent<{}, {}, any> | (() => VNode); | ||
| footerRender?: DefineComponent<{}, {}, any> | (() => VNode); | ||
| rightRender?: DefineComponent<{}, {}, any> | (() => VNode); | ||
| unAccessible?: DefineComponent<{}, {}, any> | (() => VNode); | ||
| noFound?: DefineComponent<{}, {}, any> | (() => VNode); | ||
| logout?: DefineComponent<{}, {}, any> | (() => VNode); | ||
| } | ||
| // 定义布局配置接口 | ||
| export interface LayoutConfig extends ILayout { | ||
| menu?: { | ||
| request?: () => Promise<IMenuRoutes[]>; | ||
| }; | ||
| } | ||
| // 定义useProvider函数的参数类型 | ||
| export interface UseProviderOptions { | ||
| config: RuntimeConfig; | ||
| access?: { | ||
| default:(typeof import("src/access"))["default"]; | ||
| } | ||
| router?: Router; | ||
| } |
| import { App, inject } from "vue"; | ||
| import { IMenuRoutes, LayoutConfig } from "./type"; // 假设type.ts和hooks文件在同一目录 | ||
| export const useAccess = () => { | ||
| return (inject(ACCESS_KEY, '') as ReturnType< | ||
| (typeof import("src/access"))["default"] | ||
| >) | ||
| }; | ||
| export const useMenuRoutes = () => { | ||
| return inject(MENU_ROUTES,[]) as IMenuRoutes[] | ||
| }; | ||
| export const useLayout = () => { | ||
| return inject(LAYOUT_CONFIG,'') as LayoutConfig | ||
| } | ||
| export const useStore = <T = any>(namespace: string): T => { | ||
| const modules = inject(STORE_KEY,'') | ||
| if (!modules?.[namespace]) { | ||
| throw new Error(`[useStore] 未找到命名空间为 ${namespace} 的模块`) | ||
| } | ||
| return modules[namespace]() as T | ||
| } |
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
66800
68.27%37
131.25%2162
63.54%2
100%