You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Global-sales/src/utils/commons.js

1026 lines
30 KiB
JavaScript

export function copy(obj) {
return JSON.parse(JSON.stringify(obj))
}
export function splitArray2Parts(arr, size) {
const result = []
for (let i = 0; i < arr.length; i += size) {
result.push(arr.slice(i, i + size))
}
return result
}
export function camelCase(name) {
return name.substr(0, 1).toLowerCase() + name.substr(1)
}
export class UrlBuilder {
constructor(url) {
this.url = url
this.paramList = []
}
append(name, value) {
if (isNotEmpty(value)) {
this.paramList.push({ name: name, value: value })
}
return this
}
build() {
this.paramList.forEach((e, i, a) => {
if (i === 0) {
this.url += "?"
} else {
this.url += "&"
}
this.url += e.name + "=" + e.value
})
return this.url
}
}
export function isNotEmpty(val) {
return val !== undefined && val !== null && val !== ""
}
// export function isEmpty(val) {
// return val === undefined || val === null || val === ""
// }
export function prepareUrl(url) {
return new UrlBuilder(url)
}
// export function debounce(fn, delay = 500) {
// let timer
// return e => {
// e.persist()
// clearTimeout(timer)
// timer = setTimeout(() => {
// fn(e)
// }, delay)
// }
// }
export function throttle(fn, delay, atleast) {
let timeout = null,
startTime = new Date()
return function () {
let curTime = new Date()
clearTimeout(timeout)
if (curTime - startTime >= atleast) {
fn()
startTime = curTime
} else {
timeout = setTimeout(fn, delay)
}
}
}
export function clickUrl(url) {
const httpLink = document.createElement("a")
httpLink.href = url
httpLink.target = "_blank"
httpLink.click()
}
export function escape2Html(str) {
var temp = document.createElement("div")
temp.innerHTML = str
var output = temp.innerText || temp.textContent
temp = null
return output
}
/**
* ! 不支持计算 Set 或 Map
* @param {*} val
* @example
* true if: 0, [], {}, null, '', undefined
* false if: 'false', 'undefined'
*/
export function isEmpty(val) {
// return val === undefined || val === null || val === "";
return [Object, Array].includes((val || {}).constructor) && !Object.entries(val || {}).length;
}
/**
* 数组排序
*/
export const sortBy = (key) => {
return (a, b) => (a[key] > b[key] ? 1 : b[key] > a[key] ? -1 : 0);
};
/**
* Object排序keys
*/
export const sortKeys = (obj) =>
Object.keys(obj)
.sort()
.reduce((a, k2) => ({ ...a, [k2]: obj[k2] }), {});
export function sortObjectsByKeysMap(objects, keyOrder) {
if (!objects) return {} // Handle null/undefined input
if (!keyOrder || keyOrder.length === 0) return objects
const objectMap = new Map(Object.entries(objects))
const sortedMap = new Map()
for (const key of keyOrder) {
if (objectMap.has(key)) {
sortedMap.set(key, objectMap.get(key))
objectMap.delete(key) // Optimization: Remove from original map after adding
}
}
// Add remaining keys
for (const [key, value] of objectMap) {
sortedMap.set(key, value)
}
return Object.fromEntries(sortedMap)
}
/**
* 数组排序, 给定排序数组
* @param {array} items 需要排序的数组
* @param {array} keyName 排序的key
* @param {array} keyOrder 给定排序
* @returns
*/
export const sortArrayByOrder = (items, keyName, keyOrder) => {
return items.sort((a, b) => {
return keyOrder.indexOf(a[keyName]) - keyOrder.indexOf(b[keyName]);
});
};
/**
* 合并Object, 递归地
*/
export function merge(...objects) {
const isDeep = objects.some((obj) => obj !== null && typeof obj === 'object');
const result = objects[0] || (isDeep ? {} : objects[0]);
for (let i = 1; i < objects.length; i++) {
const obj = objects[i];
if (!obj) continue;
Object.keys(obj).forEach((key) => {
const val = obj[key];
if (isDeep) {
if (Array.isArray(val)) {
result[key] = [].concat(Array.isArray(result[key]) ? result[key] : [result[key]], val);
} else if (typeof val === 'object') {
result[key] = merge(result[key], val);
} else {
result[key] = val;
}
} else {
result[key] = typeof val === 'boolean' ? val : result[key];
}
});
}
return result;
}
/**
* 数组分组
* - 相当于 lodash 的 _.groupBy
* @see https://www.lodashjs.com/docs/lodash.groupBy#_groupbycollection-iteratee_identity
*/
export function groupBy(array = [], callback) {
return array.reduce((groups, item) => {
const key = typeof callback === 'function' ? callback(item) : item[callback];
if (!groups[key]) {
groups[key] = [];
}
groups[key].push(item);
return groups;
}, {});
}
/**
* 创建一个从 object 中选中的属性的对象。
* @param {*} object
* @param {array} keys
*/
export function pick(object, keys) {
return keys.reduce((obj, key) => {
if (object && Object.prototype.hasOwnProperty.call(object, key)) {
obj[key] = object[key];
}
return obj;
}, {});
}
/**
* 返回对象的副本,经过筛选以省略指定的键。
* @param {*} object
* @param {string[]} keysToOmit
* @returns
*/
export function omit(object, keysToOmit) {
return Object.fromEntries(Object.entries(object).filter(([key]) => !keysToOmit.includes(key)));
}
/**
* 去除无效的值: undefined, null, '', []
* * 只删除 null undefined: 用 flush 方法;
*/
export const omitEmpty = _object => {
Object.keys(_object).forEach(key => (_object[key] == null || _object[key] === '' || _object[key].length === 0) && delete _object[key]);
return _object;
};
/**
* 深拷贝
*/
export function cloneDeep(value) {
// return structuredClone(value);
if (typeof value !== 'object' || value === null) {
return value;
}
const result = Array.isArray(value) ? [] : {};
for (const key in value) {
if (Object.prototype.hasOwnProperty.call(value, key)) {
result[key] = cloneDeep(value[key]);
}
}
return result;
}
/**
* 向零四舍五入, 固定精度设置
*/
function curriedFix(precision = 0) {
return function (number) {
// Shift number by precision places
const shift = Math.pow(10, precision);
const shiftedNumber = number * shift;
// Round to nearest integer
const roundedNumber = Math.round(shiftedNumber);
// Shift back decimal place
return roundedNumber / shift;
};
}
/**
* 向零四舍五入, 保留2位小数
*/
export const fixTo2Decimals = curriedFix(2);
/**
* 向零四舍五入, 保留4位小数
*/
export const fixTo4Decimals = curriedFix(4);
export const fixTo1Decimals = curriedFix(1);
export const fixToInt = curriedFix(0);
/**
* 映射
* @example
* const keyMap = {
a: [{key: 'a1'}, {key: 'a2', transform: v => v * 2}],
b: {key: 'b1'}
};
const result = objectMapper({a: 1, b: 3}, keyMap);
// result = {a1: 1, a2: 2, b1: 3}
*
*/
export function objectMapper(input, keyMap) {
// Loop through array mapping
if (Array.isArray(input)) {
return input.map((obj) => objectMapper(obj, keyMap));
}
if (typeof input === 'object') {
const mappedObj = {};
Object.keys(input).forEach((key) => {
// Keep original keys not in keyMap
if (!keyMap[key]) {
mappedObj[key] = input[key];
}
// Handle array of maps
if (Array.isArray(keyMap[key])) {
keyMap[key].forEach((map) => {
let value = input[key];
if (map.transform) value = map.transform(value);
mappedObj[map.key] = value;
});
// Handle single map
} else {
const map = keyMap[key];
if (map) {
let value = input[key];
if (map.transform) value = map.transform(value);
mappedObj[map.key || key] = value;
}
}
});
return mappedObj;
}
return input;
}
/**
* 创建一个对应于对象路径的值数组
*/
export function at(obj, path) {
let result;
if (Array.isArray(obj)) {
// array case
const indexes = path.split('.').map((i) => parseInt(i));
result = [];
for (let i = 0; i < indexes.length; i++) {
result.push(obj[indexes[i]]);
}
} else {
// object case
const indexes = path.split('.').map((i) => i);
result = [obj];
for (let i = 0; i < indexes.length; i++) {
result = [result[0][indexes[i]]];
}
}
return result;
}
/**
* 删除 null/undefined
*/
export function flush(collection) {
let result, len, i;
if (!collection) {
return undefined;
}
if (Array.isArray(collection)) {
result = [];
len = collection.length;
for (i = 0; i < len; i++) {
const elem = collection[i];
if (elem != null) {
result.push(elem);
}
}
return result;
}
if (typeof collection === 'object') {
result = {};
const keys = Object.keys(collection);
len = keys.length;
for (i = 0; i < len; i++) {
const key = keys[i];
const value = collection[key];
if (value != null) {
result[key] = value;
}
}
return result;
}
return undefined;
}
/**
* 千分位 格式化数字
*/
export const numberFormatter = (number) => {
return new Intl.NumberFormat().format(number);
};
/**
* @example
* const obj = { a: { b: 'c' } };
* const keyArr = ['a', 'b'];
* getNestedValue(obj, keyArr); // Returns: 'c'
*/
export const getNestedValue = (obj, keyArr) => {
return keyArr.reduce((acc, curr) => {
return acc && Object.prototype.hasOwnProperty.call(acc, curr) ? acc[curr] : undefined;
// return acc && acc[curr];
}, obj);
};
/**
* 计算笛卡尔积
*/
export const cartesianProductArray = (arr, sep = '_', index = 0, prefix = '') => {
let result = [];
if (index === arr.length) {
return [prefix];
}
arr[index].forEach((item) => {
result = result.concat(cartesianProductArray(arr, sep, index + 1, prefix ? `${prefix}${sep}${item}` : `${item}`));
});
return result;
};
export const stringToColour = (str='', withFlag = true) => {
var hash = 0
if (str.length === 0) return hash;
for (let i = 0; i < str.length; i++) {
hash = str.charCodeAt(i) + ((hash << 5) - hash)
}
var colour = withFlag ? '#' : ''
for (let i = 0; i < 3; i++) {
var value = (hash >> (i * 8)) & 0xff
value = (value % 150) + 50
colour += ('00' + value.toString(16)).substr(-2)
}
return colour
}
export const debounce = (func, wait, immediate) => {
var timeout;
return function () {
var context = this,
args = arguments;
clearTimeout(timeout);
if (immediate && !timeout) func.apply(context, args);
timeout = setTimeout(function () {
timeout = null;
if (!immediate) func.apply(context, args);
}, wait);
};
}
export const removeFormattingChars = (str) => {
const regex = /[\r\n\t\v\f]/g;
str = str.replace(regex, ' ');
// Replace more than four consecutive spaces with a single space
str = str.replace(/\s{4,}/g, ' ');
return str;
}
export const olog = (text, ...args) => {
console.log(
`%c ${text} `,
'background:#fb923c ; padding: 1px; border-radius: 3px; color: #fff',...args
);
};
export const sanitizeFilename = (str) => {
// Remove whitespace and replace with hyphens
str = str.replace(/\s+/g, '-');
// Remove invalid characters and replace with hyphens
str = str.replace(/[^a-zA-Z0-9.-]/g, '-');
// Replace consecutive hyphens with a single hyphen
str = str.replace(/-+/g, '-');
// Trim leading and trailing hyphens
str = str.replace(/^-+|-+$/g, '');
return str;
}
export const formatBytes = (bytes, decimals = 2) => {
if (bytes === 0) return '';
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
};
export const calcCacheSizes = async () => {
try {
let swCacheSize = 0;
let diskCacheSize = 0;
let indexedDBSize = 0;
// 1. Get the service worker cache size
if ('caches' in window) {
const cacheNames = await caches.keys();
for (const name of cacheNames) {
const cache = await caches.open(name);
const requests = await cache.keys();
for (const request of requests) {
const response = await cache.match(request);
swCacheSize += Number(response.headers.get('Content-Length')) || 0;
}
}
}
// 2. Get the disk cache size
// const diskCacheName = 'disk-cache';
// const diskCache = await caches.open(diskCacheName);
// const diskCacheKeys = await diskCache.keys();
// for (const request of diskCacheKeys) {
// const response = await diskCache.match(request);
// diskCacheSize += Number(response.headers.get('Content-Length')) || 0;
// }
// 3. Get the IndexedDB cache size
// const indexedDBNames = await window.indexedDB.databases();
// for (const dbName of indexedDBNames) {
// const db = await window.indexedDB.open(dbName.name);
// const objectStoreNames = db.objectStoreNames;
// if (objectStoreNames !== undefined) {
// const objectStores = Array.from(objectStoreNames).map((storeName) => db.transaction([storeName], 'readonly').objectStore(storeName));
// for (const objectStore of objectStores) {
// const request = objectStore.count();
// request.onsuccess = () => {
// indexedDBSize += request.result;
// };
// }
// }
// }
return { swCacheSize, diskCacheSize, indexedDBSize, totalSize: Number(swCacheSize) + Number(diskCacheSize) + indexedDBSize };
} catch (error) {
console.error('Error getting cache sizes:', error);
}
};
export const clearAllCaches = async (cb) => {
try {
// 1. Clear the service worker cache
if ('caches' in window) {
// if (navigator.serviceWorker) {
const cacheNames = await caches.keys();
await Promise.all(cacheNames.map((name) => caches.delete(name)));
}
// 2. Clear the disk cache (HTTP cache)
// const diskCacheName = 'disk-cache';
// await window.caches.delete(diskCacheName);
// const diskCache = await window.caches.open(diskCacheName);
// const diskCacheKeys = await diskCache.keys();
// await Promise.all(diskCacheKeys.map((request) => diskCache.delete(request)));
// 3. Clear the IndexedDB cache
const indexedDBNames = await window.indexedDB.databases();
await Promise.all(indexedDBNames.map((dbName) => window.indexedDB.deleteDatabase(dbName.name)));
// Unregister the service worker
const registration = await navigator.serviceWorker.getRegistration();
if (registration) {
await registration.unregister();
console.log('Service worker unregistered');
} else {
console.log('No service worker registered');
}
if (typeof cb === 'function' ) {
cb();
}
} catch (error) {
console.error('Error clearing caches or unregistering service worker:', error);
}
};
export const loadScript = (src) => {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.type = 'text/javascript';
script.onload = resolve;
script.onerror = reject;
script.crossOrigin = 'anonymous';
script.src = src;
if (document.head.append) {
document.head.append(script);
} else {
document.getElementsByTagName('head')[0].appendChild(script);
}
});
};
export const TagColorStyle = (tag, outerStyle = false) => {
const color = stringToColour(tag);
const outerStyleObj = outerStyle ? { borderColor: `${color}66`, backgroundColor: `${color}0D` } : {};
return { color: `${color}`, ...outerStyleObj };
};
// 数组去掉重复
export function unique(arr) {
const x = new Set(arr);
return [...x];
}
export const uniqWith = (arr, fn) => arr.filter((element, index) => arr.findIndex((step) => fn(element, step)) === index);
/**
* Creates a new tree node object.
* @param {string} key - The unique identifier for the node.
* @param {string} name - The display name of the node.
* @param {string|null} parent - The key of the parent node, or null if it's a root.
* @returns {object} A plain JavaScript object representing the tree node.
*/
function createTreeNode(key, name, parent = null, keyMap={}, _raw={}) {
return {
key: key,
title: name,
parent: parent,
icon: _raw?.icon,
iconIndex: _raw?.[keyMap.iconIndex],
_raw: _raw,
children: [],
parentTitle: '',
parentIconIndex: '',
};
}
/**
* Builds a tree structure from a flat list of nodes.
* @returns {Array<object>} An array of root tree nodes.
*/
export const buildTree = (list, keyMap={ rootKeys: [], ignoreKeys: [] }) => {
if (!list || list.length === 0) {
return []
}
const nodeMap = new Map()
const treeRoots = []
list.forEach((item) => {
const node = createTreeNode(item[keyMap.key], item[keyMap.name], item[keyMap.parent], keyMap, item)
nodeMap.set(item[keyMap.key], node)
})
list.forEach((item) => {
const node = nodeMap.get(item[keyMap.key])
if (keyMap.rootKeys.includes(item[keyMap.parent]) || item[keyMap.parent] === null || item[keyMap.parent] === undefined) {
// This is a root node
treeRoots.push(node)
} else {
const parentNode = nodeMap.get(item[keyMap.parent])
if (keyMap.ignoreKeys.includes(item[keyMap.parent])) {
const grandParentNode = nodeMap.get(parentNode.parent);
node.rawParent = node.parent;
node.parent = parentNode.parent;
node.parentTitle = parentNode.title;
node.parentIconIndex = parentNode.iconIndex;
grandParentNode.children.push(node)
} else if (keyMap.ignoreKeys.includes(item[keyMap.key])) {
//
}
else if (parentNode) {
node.parentTitle = parentNode.title;
node.parentIconIndex = parentNode.iconIndex;
parentNode.children.push(node)
} else {
console.warn(`Parent with key '${item[keyMap.parent]}' not found for node '${item[keyMap.key]}'. This node will be treated as a root.`)
treeRoots.push(node)
}
}
})
return treeRoots
}
/**
*
*/
const INDEXED_DB_VERSION = 3;
export const logWebsocket = (message, direction) => {
var open = indexedDB.open('LogWebsocketData', INDEXED_DB_VERSION)
open.onupgradeneeded = function () {
var db = open.result
// 数据库是否存在
if (!db.objectStoreNames.contains('LogStore')) {
var store = db.createObjectStore('LogStore', { keyPath: 'id', autoIncrement: true })
store.createIndex('timestamp', 'timestamp', { unique: false })
} else {
const logStore = open.transaction.objectStore('LogStore')
if (!logStore.indexNames.contains('timestamp')) {
logStore.createIndex('timestamp', 'timestamp', { unique: false })
}
}
}
open.onsuccess = function () {
var db = open.result
var tx = db.transaction('LogStore', 'readwrite')
var store = tx.objectStore('LogStore')
store.put({ direction, message, date: new Date().toLocaleString(), timestamp: Date.now() })
tx.oncomplete = function () {
db.close()
}
}
};
export const readWebsocketLog = (limit = 20) => {
return new Promise((resolve, reject) => {
let openRequest = indexedDB.open('LogWebsocketData')
openRequest.onupgradeneeded = function () {
var db = openRequest.result
// 数据库是否存在
if (!db.objectStoreNames.contains('LogStore')) {
var store = db.createObjectStore('LogStore', { keyPath: 'id', autoIncrement: true })
store.createIndex('timestamp', 'timestamp', { unique: false })
} else {
const logStore = openRequest.transaction.objectStore('LogStore')
if (!logStore.indexNames.contains('timestamp')) {
logStore.createIndex('timestamp', 'timestamp', { unique: false })
}
}
}
openRequest.onerror = function (e) {
reject('Error opening database.')
}
openRequest.onsuccess = function (e) {
let db = e.target.result
// 数据库是否存在
if (!db.objectStoreNames.contains('LogStore')) {
resolve('Database does not exist.')
return
}
let transaction = db.transaction('LogStore', 'readonly')
let store = transaction.objectStore('LogStore')
const request = store.openCursor(null, 'prev'); // 从后往前
const results = [];
let count = 0;
request.onerror = function (e) {
reject('Error getting records.')
}
request.onsuccess = function (e) {
const cursor = e.target.result
if (cursor) {
if (count < limit) {
results.unshift(cursor.value)
count++
cursor.continue()
} else {
console.log(JSON.stringify(results))
resolve(results)
}
} else {
console.log(JSON.stringify(results))
resolve(results)
}
}
}
})
};
/**
* @deprecated
*/
export const clearWebsocketLog = () => {
let openRequest = indexedDB.open('LogWebsocketData')
openRequest.onerror = function (e) {}
openRequest.onsuccess = function (e) {
let db = e.target.result
if (!db.objectStoreNames.contains('LogStore')) {
return
}
let transaction = db.transaction('LogStore', 'readwrite')
let store = transaction.objectStore('LogStore')
// Clear the store
let clearRequest = store.clear()
clearRequest.onerror = function (e) {}
clearRequest.onsuccess = function (e) {}
}
}
export const createIndexedDBStore = (tables, database) => {
var open = indexedDB.open(database, INDEXED_DB_VERSION)
open.onupgradeneeded = function () {
console.log('readIndexDB onupgradeneeded', database, )
var db = open.result
// 数据库是否存在
for (const table of tables) {
if (!db.objectStoreNames.contains(table)) {
var store = db.createObjectStore(table, { keyPath: 'key' })
store.createIndex('timestamp', 'timestamp', { unique: false })
} else {
const objectStore = open.transaction.objectStore(table)
if (!objectStore.indexNames.contains('timestamp')) {
objectStore.createIndex('timestamp', 'timestamp', { unique: false })
}
}
}
}
};
export const writeIndexDB = (rows, table, database) => {
var open = indexedDB.open(database, INDEXED_DB_VERSION)
open.onupgradeneeded = function () {
console.log('readIndexDB onupgradeneeded', table, )
var db = open.result
// 数据库是否存在
if (!db.objectStoreNames.contains(table)) {
var store = db.createObjectStore(table, { keyPath: 'key' })
store.createIndex('timestamp', 'timestamp', { unique: false })
} else {
const objectStore = open.transaction.objectStore(table)
if (!objectStore.indexNames.contains('timestamp')) {
objectStore.createIndex('timestamp', 'timestamp', { unique: false })
}
}
}
open.onsuccess = function () {
var db = open.result
var tx = db.transaction(table, 'readwrite')
var store = tx.objectStore(table)
rows.forEach(row => {
store.put({ ...row, _date: new Date().toLocaleString(), timestamp: Date.now() })
});
tx.oncomplete = function () {
db.close()
}
}
};
export const readIndexDB = (key=null, table, database) => {
return new Promise((resolve, reject) => {
let openRequest = indexedDB.open(database)
openRequest.onupgradeneeded = function () {
console.log('readIndexDB onupgradeneeded', table, )
var db = openRequest.result
// 数据库是否存在
if (!db.objectStoreNames.contains(table)) {
var store = db.createObjectStore(table, { keyPath: 'key' })
store.createIndex('timestamp', 'timestamp', { unique: false })
} else {
const logStore = openRequest.transaction.objectStore(table)
if (!logStore.indexNames.contains('timestamp')) {
logStore.createIndex('timestamp', 'timestamp', { unique: false })
}
}
}
openRequest.onerror = function (e) {
console.error(`Error opening database.`, table, e)
reject('Error opening database.')
}
openRequest.onsuccess = function (e) {
let db = e.target.result
// 数据库是否存在
if (!db.objectStoreNames.contains(table)) {
resolve('Database does not exist.')
return
}
let transaction = db.transaction(table, 'readonly')
let store = transaction.objectStore(table)
// read by key
const getRequest = isEmpty(key) ? store.all() : store.get(key);
getRequest.onsuccess = (event) => {
const result = event.target.result
if (result) {
console.log(`Found record with key ${key}:`, result)
resolve(result)
} else {
console.log(`No record found with key ${key}.`)
resolve();
}
}
getRequest.onerror = (event) => {
console.error(`Error getting record with key ${key}:`, event.target.error)
}
// const request = store.openCursor(null, 'prev'); // 从后往前
// const results = [];
// let count = 0;
// request.onerror = function (e) {
// reject('Error getting records.')
// }
// request.onsuccess = function (e) {
// const cursor = e.target.result
// if (cursor) {
// if (count < limit) {
// results.unshift(cursor.value)
// count++
// cursor.continue()
// } else {
// console.log(JSON.stringify(results))
// resolve(results)
// }
// } else {
// console.log(JSON.stringify(results))
// resolve(results)
// }
// }
}
})
};
export const deleteIndexDBbyKey = (key, table, database) => {
var open = indexedDB.open(database, INDEXED_DB_VERSION)
open.onupgradeneeded = function () {
// var db = open.result
// // 数据库是否存在
// if (!db.objectStoreNames.contains(table)) {
// var store = db.createObjectStore(table, { keyPath: 'id', autoIncrement: true })
// }
}
open.onsuccess = function () {
var db = open.result
var tx = db.transaction(table, 'readwrite')
var store = tx.objectStore(table)
store.delete(key)
tx.oncomplete = function () {
db.close()
}
}
};
function cleanOldData(database, storeNames=[], dateKey = 'timestamp') {
return function (daysToKeep = 7) {
return new Promise((resolve, reject) => {
let deletedCount = 0
const recordsToDelete = new Set()
let openRequest = indexedDB.open(database, INDEXED_DB_VERSION)
openRequest.onupgradeneeded = function () {
// var db = openRequest.result
// 数据库是否存在
// if (!db.objectStoreNames.contains(storeName)) {
// var store = db.createObjectStore(storeName, { keyPath: 'id', autoIncrement: true })
// store.createIndex('timestamp', 'timestamp', { unique: false })
// } else {
// const logStore = openRequest.transaction.objectStore(storeName)
// if (!logStore.indexNames.contains('timestamp')) {
// logStore.createIndex('timestamp', 'timestamp', { unique: false })
// }
// }
}
openRequest.onsuccess = function (e) {
let db = e.target.result
// 数据库是否存在
// if (!db.objectStoreNames.contains(storeName)) {
// resolve('Database does not exist.')
// return
// }
// Calculate the cutoff timestamp for "X days ago"
const cutoffTimestamp = Date.now() - daysToKeep * 24 * 60 * 60 * 1000
const objectStoreNames = isEmpty(storeNames) ? db.objectStoreNames : storeNames
if (!isEmpty(objectStoreNames)) {
const objectStores = Array.from(objectStoreNames).map((storeName) => db.transaction([storeName], 'readwrite').objectStore(storeName))
for (const objectStore of objectStores) {
// Identify old data using the date index and primary key ID
if (!objectStore.indexNames.contains(`${dateKey}`)) {
// Clear the store
let clearRequest = objectStore.clear()
console.log(`Cleanup complete. clear ${objectStore.name} records.`)
resolve()
clearRequest.onerror = function (e) {}
clearRequest.onsuccess = function (e) {}
return
}
// Get records older than 'daysToKeep' using the index
const dateIndex = objectStore.index(`${dateKey}`)
const dateRange = IDBKeyRange.upperBound(cutoffTimestamp, false) // Get keys < cutoffTimestamp (strictly older)
const dateCursorRequest = dateIndex.openCursor(dateRange)
dateCursorRequest.onsuccess = (event) => {
const cursor = event.target.result
if (cursor) {
recordsToDelete.add(cursor.primaryKey) // Add the primary key of the record to the set
cursor.continue()
} else {
const storeName = objectStore.name;
// Delete identified data in a new transaction
const deleteTransaction = db.transaction([storeName], 'readwrite')
const deleteObjectStore = deleteTransaction.objectStore(storeName)
deleteTransaction.oncomplete = () => {
console.log(`Cleanup complete. Deleted ${deletedCount} records in ${database}.${storeName}.`)
resolve(deletedCount)
}
deleteTransaction.onerror = (event) => {
console.error('Deletion transaction error:', event.target.error)
reject(event.target.error)
}
// Convert Set to Array for forEach
Array.from(recordsToDelete).forEach((key) => {
const deleteRequest = deleteObjectStore.delete(key)
deleteRequest.onsuccess = () => {
deletedCount++
}
deleteRequest.onerror = (event) => {
console.warn(`Failed to delete record with key ${key}:`, event.target.error)
}
})
}
}
dateCursorRequest.onerror = (event) => {
console.error('Error opening date cursor for deletion:', event.target.error)
reject(event.target.error)
}
}
}
}
openRequest.onerror = function (e) {
reject('Error opening database:'+database, e)
}
})
}
}
export const clean7DaysWebsocketLog = cleanOldData('LogWebsocketData', ['LogStore']);
export const clean7DaysMailboxLog = cleanOldData('mailbox');