import fs from 'fs' import path from 'path' import { ComponentConstantList, presudos } from './const' import { get, set } from 'idb-keyval' import { computed, getCurrentInstance } from 'vue-demi' export function resolveDebuger(name) { fs.appendFile(path.resolve(__dirname, './log.txt'), `${name}\n`, () => { }) } export function kebabCase(key) { const result = key.replace(/([A-Z])/g, ' $1').trim() return result.split(' ').join('-').toLowerCase() } export function isAsyncFunc(func) { // @ts-ignore return func[Symbol(Symbol.toStringTag)] === 'AsyncFunction' } export function closest(that, componentName: string) { let vm: any = that let result while (vm && !result) { const name = vm.$options.name if (name === componentName) { result = vm } vm = vm.$parent } return result } export function useClosest(componentName: string) { const that = getCurrentInstance()?.proxy return computed(() => { if (!that) { return undefined } return closest.call(that, that, componentName) }) } export function getDirs() { return ComponentConstantList.map(item => 'node_modules/@94ai/' + kebabCase(item) + '/lib') } export function getTranspileDependencies() { return ComponentConstantList.map(item => '@94ai/' + kebabCase(item)) } export const getResolver = () => { return { type: 'component', resolve: (name) => { resolveDebuger(name) if (/^Nf[A-Za-z](?!.*[Mm][Dd]$).*$/.test(name)) { const kebabCasePackageName = kebabCase(name) return { from: `@94ai/${kebabCasePackageName}/lib/${kebabCasePackageName}.esm-bundler.js`, sideEffects: [] } } } } } export const getJsComponentSampleConfig = (config: Record = {}) => { return { exclude: [/[\\/]\.git[\\/]/, /[\\/]\.nuxt[\\/]/], include: [ /\.vue$/, /\.vue\?vue/, /\.vue\?v=/, /\.js$/, /\.js\?vue/, /\.js\?v=/ ], deep: true, transformer: 'vue2', resolvers: [], ...config } } export const getTsComponentSampleConfig = (config: Record = {}) => { return { include: [ /\.vue$/, /\.vue\?vue/, /\.md$/, /\.md\?vue/, /\.jsx$/, /\.jsx\?vue/, /\.tsx$/, /\.tsx\?vue/, /\.js$/, /\.js\?vue/, ], deep: true, extensions: ['vue', 'md', 'jsx', 'tsx', 'mjs', 'mts', 'js'], dirs: ['src/components', 'src/views', 'doc'], resolvers: [], ...config } } export function generateUUID() { // Public Domain/MIT let d = Date.now(); if (typeof performance !== 'undefined' && typeof performance.now === 'function'){ d += performance.now(); //use high-precision timer if available } return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { const r = (d + Math.random() * 16) % 16 | 0; d = Math.floor(d / 16); return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16); }); } /** * 一个中文算两个英文字符 * @param str * @returns {number} */ export const getCharStringLength = (str) => { let leng = 0 for (let i = 0; i < str.length; i += 1) { if (str.charCodeAt(i) > 127 || str.charCodeAt(i) == 94) { leng += 2 } else { leng += 1 } } return leng } export const deepClone = (source) => { // if (!source && typeof source !== 'object') { // throw new Error('error arguments shallowClone') // } if (!source || typeof source !== 'object') { return source } const targetObj: Record | Record[] = source.constructor === Array ? [] : {} Object.keys(source).forEach((keys) => { if (source[keys] && typeof source[keys] === 'object') { targetObj[keys] = source[keys].constructor === Array ? [] : {} targetObj[keys] = deepClone(source[keys]) } else { targetObj[keys] = source[keys] } }) return targetObj } export function debounce(func, wait, immediate) { let timeout, args, context, timestamp, result const later = function() { // 据上一次触发时间间隔 const last = +new Date() - timestamp // 上次被包装函数被调用时间间隔last小于设定时间间隔wait if (last < wait && last > 0) { timeout = setTimeout(later, wait - last) } else { timeout = null // 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用 if (!immediate) { result = func.apply(context, args) if (!timeout) context = args = null } } } return function(...args) { // @ts-ignore context = this timestamp = +new Date() const callNow = immediate && !timeout // 如果延时不存在,重新设定延时 if (!timeout) timeout = setTimeout(later, wait) if (callNow) { result = func.apply(context, args) // @ts-ignore context = args = null } return result } } /** * 创建唯一的字符串 * @return {string} ojgdvbvaua40 */ export function createUniqueString(randomLength = 36) { return Number(Math.random().toString().substr(3, randomLength) + Date.now()).toString(36) } export function getHalfYear(date = new Date()) { const currMonth = date.getMonth() + 1 return Math.floor((currMonth % 6 == 0 ? (currMonth / 6) : (currMonth / 6 + 1))) } export function getQuarter(date = new Date()) { const currMonth = date.getMonth() + 1 return Math.floor((currMonth % 3 == 0 ? (currMonth / 3) : (currMonth / 3 + 1))) } export function getNextYear(date = new Date()) { return new Date(date.setFullYear(date.getFullYear() + 1)) } export function endOf(now = new Date(), unit) { if (unit === 'year') { return new Date(now.getFullYear(), 11, 31, 23, 59, 59, 999); } if (unit === 'month') { return new Date(new Date(now.getFullYear(), now.getMonth() + 1, 1).getTime() - 1); } if (unit === 'week') { return new Date(now.getFullYear(), now.getMonth(), now.getDate() + (6 - now.getDay()), 23, 59, 59, 999); } return new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59, 999); } export function startOf(date, unit) { const newDate = new Date(date) if (unit === 'year') { newDate.setMonth(0) newDate.setDate(1) newDate.setHours(0, 0, 0, 0) } else if (unit === 'month') { newDate.setDate(1) newDate.setHours(0, 0, 0, 0) } else if (unit === '') { newDate.setHours(0, 0, 0, 0) } else if (unit === 'hour') { newDate.setMinutes(0, 0, 0) } else if (unit === 'minute') { newDate.setSeconds(0, 0) } else if (unit === 'second') { newDate.setMilliseconds(0) } return newDate } export function formatDate(date, format) { const year = date.getFullYear() const month = date.getMonth() + 1 const day = date.getDate() const hours = date.getHours() const minutes = date.getMinutes() const seconds = date.getSeconds() format = format.replace('YYYY', year) format = format.replace('MM', String(month).padStart(2, '0')) format = format.replace('DD', String(day).padStart(2, '0')) format = format.replace('HH', String(hours).padStart(2, '0')) format = format.replace('mm', String(minutes).padStart(2, '0')) format = format.replace('ss', String(seconds).padStart(2, '0')) return format } /** * hex颜色转rgb颜色 * @param str * @constructor */ export function HexToRgb (str: string) { const r = /^#?[0-9A-Fa-f]{6}$/ // test方法检查在字符串中是否存在一个模式,如果存在则返回true,否则返回false if (!r.test(str)) return // replace替换查找的到的字符串 str = str.replace('#', '') // match得到查询数组 const hxs = str.match(/../g) // alert('bf:'+hxs) for (let i = 0; i < 3; i++) { // @ts-ignore hxs[i] = parseInt(hxs[i], 16) } // alert(parseInt(80, 16)) // console.log(hxs); return hxs } /** * GRB颜色转Hex颜色 * @param a * @param b * @param c * @constructor */ export function RgbToHex (a: number, b: number, c: number) { const r = /^\d{1,3}$/ if (!r.test(String(a)) || !r.test(String(b)) || !r.test(String(c))) return const hexs = [a.toString(16), b.toString(16), c.toString(16)] for (let i = 0; i < 3; i++) if (hexs[i].length === 1) hexs[i] = '0' + hexs[i] return '#' + hexs.join('') } /** * 得到hex颜色值为color的加深颜色值,level为加深的程度,限0-1之间 * @param color * @param level */ export function getDarkColor (color: string, level:number) { const r = /^#?[0-9A-Fa-f]{6}$/ if (!r.test(color)) return color const rgbc = HexToRgb(color) // floor 向下取整 for (let i = 0; i < 3; i++) { // @ts-ignore rgbc[i] = Math.floor(rgbc[i] * (1 - level)) } // @ts-ignore return RgbToHex(rgbc[0], rgbc[1], rgbc[2]) } /** * 得到hex颜色值为color的减淡颜色值,level为加深的程度,限0-1之间 * @param color * @param level */ export function getLightColor (color: string, level: number) { const r = /^#?[0-9A-Fa-f]{6}$/ if (!r.test(color)) return color const rgbc = HexToRgb(color) for (let i = 0; i < 3; i++) { // @ts-ignore rgbc[i] = Math.floor((255 - rgbc[i]) * level + rgbc[i]) } // @ts-ignore return RgbToHex(rgbc[0], rgbc[1], rgbc[2]) } // 把驼峰转换成横杠连接 export const toLine = (value: string) => { return value.replace(/(A-Z)g/, '-$1').toLocaleLowerCase() } /** * 转驼峰 * @param { string } word */ export function upperCamelCase(word: string): string { const cleanedString = word.replace(/\W/g, ''); const words = cleanedString.toLowerCase().split(/\B(?=[A-Z])/); const upperCamelCase = words.map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(''); return upperCamelCase; } /** * flex: none => flex: 0 0 auto * flex: 0 => flex: 0 1 0% * flex: 1 => flex: 1 1 0% * 没有flex => flex: 0 1 auto * @param { HTMLElement } el */ export function isAutoFlexBasis(el: HTMLElement): boolean { const flexBasis = _getComputedStyle(el, 'flexBasis') as string; return flexBasis === 'auto'; } /** * 兼容各浏览器getComputedStyle * @param { HTMLElement } el * @param { string } k */ export function _getComputedStyle(el: HTMLElement, k: string): string | undefined { const computedStyle = getComputedStyle(el); for (let i = 0; i <= presudos.length; i++) { const key = presudos[i] ? `${presudos[i]}${upperCamelCase(k)}` : k; if (computedStyle[key]) { return computedStyle[key as keyof CSSStyleDeclaration] as string; } } return undefined; } /** * 获取元素flex方向 * @param { HTMLElement } el */ export function getFlexDirection(el: HTMLElement): { /** * 是否竖向 */ vertical: boolean, reverse: boolean, } { const flexDirection = _getComputedStyle(el, 'flexDirection') as string; const [direction, reverse] = flexDirection.split('-'); return { vertical: direction === 'column', reverse: !!reverse, }; } export function getBlobURL(jsCode) { const blob = new Blob([jsCode], { type: 'text/javascript' }) const blobURL = URL.createObjectURL(blob) return blobURL } export function toBase64(str) { return btoa(unescape(encodeURIComponent(str))) } export function generateID() { return Math.random().toString(36).slice(2, 12) + Date.now() } export const copyFile = (sourcePath: string, targetPath: string) => { const isExist = (path) => { if (!fs.existsSync(path)) { fs.mkdirSync(path) } } isExist(targetPath) const sourceFile = fs.readdirSync(sourcePath, { withFileTypes: true }) sourceFile.forEach((file) => { const newSourcePath = path.resolve(sourcePath, file.name) const newTargetPath = path.resolve(targetPath, file.name) if (file.isDirectory()) { isExist(newTargetPath) copyFile(newSourcePath, newTargetPath) return } fs.copyFileSync(newSourcePath, newTargetPath) }) } // 加法 export const add = (arg1: number, arg2: number) => { let r1, r2, m try { r1 = (arg1.toString().split('.')[1] || '').length } catch (e) { r1 = 0 } try { r2 = (arg2.toString().split('.')[1] || '').length } catch (e) { r2 = 0 } m = Math.pow(10, Math.max(r1, r2)) return div((mul(arg1, m) + mul(arg2, m)), m) } // 减法 export const sub = (arg1: number, arg2: number) => { return add(arg1, -arg2) } // 乘法 export const mul = (arg1: number, arg2: number) => { let m = 0; const s1 = arg1.toString(); const s2 = arg2.toString() m += (s1.split('.')[1] || '').length m += (s2.split('.')[1] || '').length return Number(s1.replace('.', '')) * Number(s2.replace('.', '')) / Math.pow(10, m) } // 除法 export const div = (arg1: number, arg2: number) => { const t1 = (arg1.toString().split('.')[1] || '').length const t2 = (arg2.toString().split('.')[1] || '').length const r1 = Number(arg1.toString().replace('.', '')) const r2 = Number(arg2.toString().replace('.', '')) return (r1 / r2) * Math.pow(10, t2 - t1) } // 获取时区时间 export function getLocalTime(i) { let time = i if (typeof time === 'string') { const arr = time.split(':') if (arr.length < 2) { // 空字符串用系统时区 return new Date() } time = add(Number(arr[0]), div(Math.floor(Number(arr[1])), 60)) } if (typeof time !== 'number') { return new Date() } const d = new Date() // 获取当地时区时间戳 const len = d.getTime() const offset = d.getTimezoneOffset() * 60000 // 获取utc时间戳 const utcTime = len + offset return new Date(utcTime + 3600000 * time) // 获取指定时区时间 } export function toPascalCase(str) { const camelCase = str.replace(/-([a-z])/g, function (_match, letter) { return letter.toUpperCase() }) return camelCase.charAt(0).toUpperCase() + camelCase.slice(1) } export class CachedManager { static cachedEntitys = {} static async asyncCachedGet(key) { if (!this.cachedEntitys[key]) { this.cachedEntitys[key] = await get(key) } return this.cachedEntitys[key] } static syncCachedGet(key) { return this.cachedEntitys[key] } static async asyncCachedSet(key, value) { this.cachedEntitys[key] = value await set(key, value) } static syncCachedSet(key,value) { this.cachedEntitys[key] = value set(key, value) } } /** * 返回当前值或若为函数则返回函数返回值,并支持函数传参 * @param {any | ((...args: any[]) => any)} state * @returns {any} */ export function resolveValue( state: any | Function, ...args: any[] ): any { // 如果 state 是函数,调用该函数并传入 args;否则返回 !state if (typeof state === 'function') { return state?.(...args) // 展开 args 数组 } return state // 如果不是函数,则返回 !state }