import { isRef, toRaw, UnwrapRef, computed, ref, Ref, WritableComputedRef, ComputedRef, shallowReactive, reactive, watchEffect, nextTick, watch, readonly } from 'vue' import { round, toRawType } from './index' import { local } from './store' // 复制vue源数据 防止引用修改 export const blocking = (data: T): UnwrapRef => { const raw = toRaw(isRef(data) ? data.value : data) as any return raw // const rawType = toRawType(raw) // switch(rawType) { // case 'Array': // return recursionCopy(raw, raw) // case 'Object': // return recursionCopy(raw, raw) // default: // return data as UnwrapRef // } } // 执行函数时保证参数没有其他地方引用 export const callFunArgsBlocking = ) => any>( fun: T ): T => ((...args) => { let result try { result = fun.apply(this, blocking(args)) } catch (e) { console.error(e) } return result }) as T // 对象所有函数隔离传入参数 const blockfcMap = new WeakMap() export const blockingFunCall = (data: T): T => new Proxy(data, { get(...args) { let value = Reflect.get(...args) as any if (blockfcMap.has(value)) { return blockfcMap.get(value) } const valueType = toRawType(value) if (valueType === 'Object' || valueType === 'Array') { const blockfc = blockingFunCall(value) blockfcMap.set(value, blockfc) value = blockfc } else if (valueType === 'Function') { value = callFunArgsBlocking(value) } return value } }) export type Tree = { children?: Array } export const getFlatTree = ( tree: T, itself: boolean = true ): Array => { const options = [] as Array if (itself) { options.push(tree) } if (tree.children && tree.children.length > 0) { for (const item of tree.children) { options.push(...(getFlatTree(item, itself) as Array)) } } else if (!itself) { options.push(tree) } return options } export type LinkageTree = { select: WritableComputedRef selects: Ref> children: Array> options: Array current: T } // 生成树联动 export const linkageSelectTree = ( tree: T, itself: boolean = true, defSelect?: boolean, selects = ref([]) as Ref> ): LinkageTree => { const alloptions = getFlatTree(tree, itself) const childrenCheck = tree.children ? tree.children.map(item => linkageSelectTree(item, itself, false, selects)) : [] const changeCheck = (check: boolean) => { alloptions.forEach(item => { let index = selects.value.indexOf(item) if (!~index) { index = selects.value.indexOf(reactive(item as any)) } if (~index && !check) { selects.value.splice(index, 1) // selects.value = [...selects.value] } else if (!~index && check) { selects.value.push(item) // selects.value = [...selects.value] } }) } let cache: boolean const allCheck = computed({ get: () => { let current = true for (const option of alloptions) { let i = 0 for (; i < selects.value.length; i++) { const select = toRaw(selects.value[i]) if (toRaw(option) === select) { break } } if (i === selects.value.length) { current = false break } } cache = current return current }, set: check => { changeCheck(check) } }) if (defSelect) { nextTick(() => (allCheck.value = defSelect)) } return { current: tree, select: allCheck, children: childrenCheck, selects, options: alloptions } } export type SimleTree = Omit< LinkageTree, 'select' | 'selects' | 'children' > & { children: Array> } //获取选择树路径 export const getLinkageTreeLocal = ( tree: SimleTree, select: T ) => { if (toRaw(tree) === toRaw(select)) { return [select] } else if (tree.children) { for (const item of tree.children) { const locals = getLinkageTreeLocal(item, select) if (locals.length) { return [tree, ...locals] } } } return [] } export const getLinkageOptionLocal = ( tree: SimleTree, option: T ): Array => { if (tree.options.includes(option)) { for (const item of tree.children) { const locals = getLinkageOptionLocal(item, option) if (locals.length) { return [tree.current, ...locals] } } return [tree.current] } return [] } export type Stack = { push: (raw: T) => void pop: () => T length: ComputedRef current: ComputedRef } // 栈构建 export const stackFactory = (initVal?: T, debug?: boolean): Stack => { const stack = shallowReactive([]) as Array if (initVal !== void 0) { stack.push(initVal) } return { push(raw: T) { stack.push(raw) }, pop() { let ret = stack[stack.length-- - 1] return ret }, current: computed(() => { return stack[stack.length - 1] }), length: computed(() => stack.length) } } export const fastStacksValue = }>( stacks: T ) => { const result = {} as { [key in keyof T]: UnwrapRef } const keys = Object.keys(stacks) as Array const proxy = new Proxy(result, { get(_, key: keyof T) { if (keys.includes(key)) { return isRef(stacks[key].current.value) ? (stacks[key].current.value as any).value : stacks[key].current.value } else { return stacks[key] } }, set(_, key: keyof T, val) { if (isRef(stacks[key].current.value)) { ;(stacks[key].current.value as any).value = val return true } else { return false } } }) return proxy } export type MarkStack = { push(raw: K, key: T): ReturnType['push']> pop(key: T): ReturnType['pop']> get(key: T): Stack['current'] } // 标识栈构建 export const markStackFactory = () => { const markMap = shallowReactive(new Map>()) return { push(raw: K, key: T) { if (!markMap.has(key)) { markMap.set(key, stackFactory()) } const stack = markMap.get(key) stack.push(raw) }, pop(key: T) { const stack = markMap.get(key) if (stack) { const ret = stack.pop() if (!stack.length.value) { markMap.delete(key) } return ret } }, get(key: T) { const stack = markMap.get(key) if (stack) { return stack.current } } } } export type ManageStack = { push(raw: K, key?: T): ReturnType['push']> pop(key?: T): ReturnType['pop']> get(key?: T): Stack['current'] } export const mangeStackFactory = () => { const stack = stackFactory() const markStack = markStackFactory() return { push: (raw: K, key?: T) => key ? markStack.push(raw, key) : stack.push(raw), pop: (key: T) => { return key ? markStack.pop(key) : stack.pop() }, get: (key: T) => { return key && markStack.get(key) ? markStack.get(key) : stack.current } } } export const watchGroupChange = ( origin: Ref> | Array, addCallback: (atom: T) => void, removeCallback?: (atom: T) => void ) => { watch( origin, (ctrls: Array, oldCtrls: Array) => { if (oldCtrls && removeCallback) { oldCtrls.filter(item => !ctrls.includes(item)).forEach(removeCallback) } ;(oldCtrls ? ctrls.filter(item => !oldCtrls.includes(item)) : ctrls ).forEach(addCallback) }, { immediate: true } ) } export const genCountDown = ( localKey: string, mis: number, preTime?: number ) => { if (!preTime) { preTime = Number(local.get(localKey)) || 0 } const countmis = ref(0) let time const update = () => { const now = Date.now() const downmis = round((now - preTime) / 1000, 0) if (downmis < mis) { countmis.value = mis - downmis time = setTimeout(update, 1000) } else { countmis.value = 0 local.del(localKey) } } local.set(localKey, preTime.toString()) update() return { count: countmis, update: (time: number) => { clearTimeout(time) preTime = time local.set(localKey, preTime.toString()) update() } } } function isTabletf() { // 获取设备的屏幕宽度和高度 var screenWidth = window.screen.width; var screenHeight = window.screen.height; // 计算屏幕的对角线长度 var diagonalSize = Math.sqrt(Math.pow(screenWidth, 2) + Math.pow(screenHeight, 2)); // 判断屏幕的对角线长度是否大于等于7英寸(一般认为大于等于7英寸的设备是平板) if (diagonalSize >= 7) { return true; } else { return false; } } function isPCf() { const ua = navigator.userAgent.toLowerCase(); const agents = ["android", "iphone", "symbianos", "windows phone", "ipad", "ipod"]; for (let i = 0; i < agents.length; i++) { if (ua.indexOf(agents[i]) !== -1) { console.error(agents[i]) return false; } } return true; } export const os = (function () { let ua = navigator.userAgent.toLowerCase(); let isWindowsPhone = /(?:windows phone)/.test(ua) let isSymbian = /(?:symbianOS)/.test(ua) || isWindowsPhone let isAndroid = /(?:android)/.test(ua) let isFireFox = /(?:firefox)/.test(ua) let isChrome = /(?:chrome|crios)/.test(ua) let isTablet = isTabletf() let isPhone = /(?:iphone)/.test(ua) && !isTablet // let isPc = !isPhone && !isAndroid && !isSymbian && !isTablet let isPc = isPCf() let isWX = /(?:microMessenger)/.test(ua) // if (isPc && navigator.maxTouchPoints > 1) { // isTablet = true // } const isHorizontal = ref(false) const getHorizotal = () => window.innerWidth > window.innerHeight && window.innerWidth - 80 > 320 let timeout if (!isPc) { const changHorizontal = () => { isHorizontal.value = getHorizotal() clearTimeout(timeout) timeout = setTimeout(() => { isHorizontal.value = getHorizotal() }, 300) } window.addEventListener('resize', changHorizontal) changHorizontal() } return { isTablet: isTablet, isPhone: isPhone, isAndroid: isAndroid, isPc: isPc, isWX: isWX, isHorizontal } })()