1/** 2 * Created by Liu.Jun on 2020/4/17 17:05. 3 */ 4 5// is object 6export function isObject(object) { 7 return Object.prototype.toString.call(object) === '[object Object]'; 8} 9 10// is arguments 11function isArguments(object) { 12 return Object.prototype.toString.call(object) === '[object Arguments]'; 13} 14 15// 定义的数据推导出schema 类型 16export const guessType = function guessType(value) { 17 if (Array.isArray(value)) { 18 return 'array'; 19 } if (typeof value === 'string') { 20 return 'string'; 21 } if (value == null) { 22 return 'null'; 23 } if (typeof value === 'boolean') { 24 return 'boolean'; 25 // eslint-disable-next-line no-restricted-globals 26 } if (!isNaN(value)) { 27 return 'number'; 28 } if (typeof value === 'object') { 29 return 'object'; 30 } 31 // Default to string if we can't figure it out 32 return 'string'; 33}; 34 35export function union(arr1, arr2) { 36 return [...new Set([...arr1, ...arr2])]; 37} 38 39// Recursively merge deeply nested schemas. 40// The difference between mergeSchemas and mergeObjects 41// is that mergeSchemas only concats arrays for 42// values under the "required" keyword, and when it does, 43// it doesn't include duplicate values. 44export function mergeSchemas(obj1, obj2) { 45 const acc = Object.assign({}, obj1); // Prevent mutation of source object. 46 // eslint-disable-next-line no-shadow 47 return Object.keys(obj2).reduce((acc, key) => { 48 const left = obj1 ? obj1[key] : {}; 49 const right = obj2[key]; 50 if (obj1 && obj1.hasOwnProperty(key) && isObject(right)) { 51 acc[key] = mergeSchemas(left, right); 52 } else if ( 53 obj1 54 && obj2 55 && (getSchemaType(obj1) === 'object' || getSchemaType(obj2) === 'object') 56 && key === 'required' 57 && Array.isArray(left) 58 && Array.isArray(right) 59 ) { 60 // Don't include duplicate values when merging 61 // "required" fields. 62 acc[key] = union(left, right); 63 } else { 64 acc[key] = right; 65 } 66 return acc; 67 }, acc); 68} 69 70// 合并对象数据 71export function mergeObjects(obj1, obj2, concatArrays = false) { 72 // Recursively merge deeply nested objects. 73 const preAcc = Object.assign({}, obj1); // Prevent mutation of source object. 74 if (!isObject(obj2)) return preAcc; 75 76 return Object.keys(obj2).reduce((acc, key) => { 77 const left = obj1 ? obj1[key] : {}; 78 const right = obj2[key]; 79 if (obj1 && obj1.hasOwnProperty(key) && isObject(right)) { 80 acc[key] = mergeObjects(left, right, concatArrays); 81 } else if (concatArrays && Array.isArray(left) && Array.isArray(right)) { 82 acc[key] = left.concat(right); 83 } else { 84 acc[key] = right; 85 } 86 return acc; 87 }, preAcc); 88} 89 90// 获取给定 schema 类型。 91export function getSchemaType(schema) { 92 const { type } = schema; 93 94 // 通过const 申明的常量 做类型推断 95 if (!type && schema.const) { 96 return guessType(schema.const); 97 } 98 99 // 枚举默认字符串 100 if (!type && schema.enum) { 101 return 'string'; 102 } 103 104 // items 推断为 array 类型 105 if (!type && (schema.items)) { 106 return 'array'; 107 } 108 109 // anyOf oneOf 不申明 type 字段 110 if (!type && (schema.properties || schema.additionalProperties)) { 111 return 'object'; 112 } 113 114 if (type instanceof Array && type.length === 2 && type.includes('null')) { 115 return type.find(curType => curType !== 'null'); 116 } 117 118 return type; 119} 120 121// 深度相等对比 122export function deepEquals(a, b, ca = [], cb = []) { 123 // Partially extracted from node-deeper and adapted to exclude comparison 124 // checks for functions. 125 // https://github.com/othiym23/node-deeper 126 if (a === b) { 127 return true; 128 } if (typeof a === 'function' || typeof b === 'function') { 129 // Assume all functions are equivalent 130 // see https://github.com/mozilla-services/react-jsonschema-form/issues/255 131 return true; 132 } if (typeof a !== 'object' || typeof b !== 'object') { 133 return false; 134 } if (a === null || b === null) { 135 return false; 136 } if (a instanceof Date && b instanceof Date) { 137 return a.getTime() === b.getTime(); 138 } if (a instanceof RegExp && b instanceof RegExp) { 139 return ( 140 a.source === b.source 141 && a.global === b.global 142 && a.multiline === b.multiline 143 && a.lastIndex === b.lastIndex 144 && a.ignoreCase === b.ignoreCase 145 ); 146 } if (isArguments(a) || isArguments(b)) { 147 if (!(isArguments(a) && isArguments(b))) { 148 return false; 149 } 150 const slice = Array.prototype.slice; 151 return deepEquals(slice.call(a), slice.call(b), ca, cb); 152 } 153 if (a.constructor !== b.constructor) { 154 return false; 155 } 156 157 const ka = Object.keys(a); 158 const kb = Object.keys(b); 159 // don't bother with stack acrobatics if there's nothing there 160 if (ka.length === 0 && kb.length === 0) { 161 return true; 162 } 163 if (ka.length !== kb.length) { 164 return false; 165 } 166 167 let cal = ca.length; 168 // eslint-disable-next-line no-plusplus 169 while (cal--) { 170 if (ca[cal] === a) { 171 return cb[cal] === b; 172 } 173 } 174 ca.push(a); 175 cb.push(b); 176 177 ka.sort(); 178 kb.sort(); 179 // eslint-disable-next-line no-plusplus 180 for (let j = ka.length - 1; j >= 0; j--) { 181 if (ka[j] !== kb[j]) { 182 return false; 183 } 184 } 185 186 let key; 187 // eslint-disable-next-line no-plusplus 188 for (let k = ka.length - 1; k >= 0; k--) { 189 key = ka[k]; 190 if (!deepEquals(a[key], b[key], ca, cb)) { 191 return false; 192 } 193 } 194 195 ca.pop(); 196 cb.pop(); 197 198 return true; 199} 200 201// 只保证同时生成不重复 202export const genId = (function genIdFn() { 203 let preKey = `${+new Date()}`; 204 let key = 0; 205 return () => { 206 const curTimestamp = `${+new Date()}`; 207 if (curTimestamp === preKey) { 208 key += 1; 209 } else { 210 // 重置 key 211 key = 0; 212 } 213 214 preKey = curTimestamp; 215 return `${preKey}x${key}`; 216 }; 217}()); 218 219// 空对象 220export function isEmptyObject(obj) { 221 if (!obj) return true; 222 223 for (const key in obj) { 224 if (Object.prototype.hasOwnProperty.call(obj, key)) { 225 return false; 226 } 227 } 228 return true; 229} 230 231// 过滤和转换对象的key 232export function filterObject(obj, filterFn) { 233 return Object.entries(obj).reduce((preVal, [key, value]) => { 234 const newKey = filterFn(key, value); 235 if (undefined !== newKey) { 236 preVal[newKey] = value; 237 } 238 return preVal; 239 }, {}); 240} 241 242const f = s => `0${s}`.substr(-2); 243export function parseDateString(dateString, includeTime = true) { 244 if (!dateString) { 245 return { 246 year: -1, 247 month: -1, 248 day: -1, 249 hour: includeTime ? -1 : 0, 250 minute: includeTime ? -1 : 0, 251 second: includeTime ? -1 : 0, 252 }; 253 } 254 const date = new Date(dateString); 255 if (Number.isNaN(date.getTime())) { 256 throw new Error(`Unable to parse date ${dateString}`); 257 } 258 return { 259 year: date.getFullYear(), 260 month: f(date.getMonth() + 1), // oh you, javascript. 261 day: f(date.getDate()), 262 hour: f(includeTime ? date.getHours() : 0), 263 minute: f(includeTime ? date.getMinutes() : 0), 264 second: f(includeTime ? date.getSeconds() : 0), 265 }; 266} 267 268export function toDateString( 269 { 270 year, month, day, hour = 0, minute = 0, second = 0 271 }, 272 time = true 273) { 274 const utcTime = Date.UTC(year, month - 1, day, hour, minute, second); 275 const datetime = new Date(utcTime).toJSON(); 276 return time ? datetime : datetime.slice(0, 10); 277} 278 279export function pad(num, size) { 280 let s = String(num); 281 while (s.length < size) { 282 s = `0${s}`; 283 } 284 return s; 285} 286 287// dataUrl 转 Blob文件对象 288export function dataURItoBlob(dataURI) { 289 // Split metadata from data 290 const splitted = dataURI.split(','); 291 // Split params 292 const params = splitted[0].split(';'); 293 // Get mime-type from params 294 const type = params[0].replace('data:', ''); 295 // Filter the name property from params 296 const properties = params.filter(param => param.split('=')[0] === 'name'); 297 // Look for the name and use unknown if no name property. 298 let name; 299 if (properties.length !== 1) { 300 name = 'unknown'; 301 } else { 302 // Because we filtered out the other property, 303 // we only have the name case here. 304 name = properties[0].split('=')[1]; 305 } 306 307 // Built the Uint8Array Blob parameter from the base64 string. 308 const binary = atob(splitted[1]); 309 const array = []; 310 // eslint-disable-next-line no-plusplus 311 for (let i = 0; i < binary.length; i++) { 312 array.push(binary.charCodeAt(i)); 313 } 314 // Create the blob object 315 const blob = new window.Blob([new Uint8Array(array)], { type }); 316 317 return { blob, name }; 318} 319 320// 字符串首字母小写 321export function lowerCase(str) { 322 if (undefined === str) return str; 323 return String(str).replace(/^./, s => s.toLocaleLowerCase()); 324} 325 326// 最大公约数 327export function gcd(a, b) { 328 if (b === 0) return a; 329 return gcd(b, a % b); 330} 331 332// 最小公倍数 333export function scm(a, b) { 334 return (a * b) / gcd(a, b); 335} 336 337// 打开新页面 338export function openNewPage(url, target = '_blank') { 339 const a = document.createElement('a'); 340 a.style.display = 'none'; 341 a.target = target; 342 a.href = url; 343 document.body.appendChild(a); 344 a.click(); 345 document.body.removeChild(a); 346} 347