918 lines
19 KiB
Markdown
918 lines
19 KiB
Markdown
---
|
||
title: "「合集」常用的 JavaScript 代码"
|
||
date: 2025-03-16T22:02:06Z
|
||
lastmod: 2025-03-16T22:02:33Z
|
||
tags: [合集,代码片段,Web 前端,JavaScript]
|
||
---
|
||
|
||
# 「合集」常用的 JavaScript 代码
|
||
|
||
## document
|
||
|
||
#### 焦点不在当前窗口时改变标题
|
||
|
||
```javascript
|
||
document.addEventListener('visibilitychange', function () {
|
||
if (document.hidden) {
|
||
document.title = '焦点不在当前窗口';
|
||
} else {
|
||
document.title = '焦点在当前窗口';
|
||
}
|
||
});
|
||
```
|
||
|
||
#### 解除离开页面检测
|
||
|
||
```javascript
|
||
document.hasFocus = function () {
|
||
return true;
|
||
};
|
||
```
|
||
|
||
#### 使网页内容(body)可编辑
|
||
|
||
**开启**
|
||
|
||
```javascript
|
||
document.body.contentEditable = 'true';
|
||
document.designMode = 'on';
|
||
```
|
||
|
||
```javascript
|
||
(function () {
|
||
let items = document.querySelectorAll('iframe');
|
||
for (let i = 0; i < items.length; i++) {
|
||
try {
|
||
items[i].contentDocument.body.contentEditable = 'true';
|
||
items[i].contentDocument.designMode = 'on';
|
||
} catch (err) {
|
||
console.log(err);
|
||
}
|
||
}
|
||
})();
|
||
```
|
||
|
||
**关闭**
|
||
|
||
```javascript
|
||
document.body.contentEditable = 'false';
|
||
document.designMode = 'off';
|
||
```
|
||
|
||
```javascript
|
||
(function () {
|
||
let items = document.querySelectorAll('iframe');
|
||
for (let i = 0; i < items.length; i++) {
|
||
try {
|
||
items[i].contentDocument.body.contentEditable = 'false';
|
||
items[i].contentDocument.designMode = 'off';
|
||
} catch (err) {
|
||
console.log(err);
|
||
}
|
||
}
|
||
})();
|
||
```
|
||
|
||
---
|
||
|
||
## function
|
||
|
||
#### Blob 转 DataURL
|
||
|
||
```javascript
|
||
/**
|
||
* @description 转换 Blob 为 DataURL
|
||
* @param {Blob} blob
|
||
* @returns {Promise<{ dataURL: string; success: boolean; }>}
|
||
*/
|
||
function blobToDataURL(blob) {
|
||
return new Promise((resolve) => {
|
||
|
||
let fileReader = new FileReader();
|
||
|
||
// 处理读取异常
|
||
fileReader.onerror = function () {
|
||
resolve({
|
||
dataURL: '',
|
||
success: false,
|
||
});
|
||
};
|
||
|
||
// 处理读取完成
|
||
fileReader.onload = function () {
|
||
resolve({
|
||
dataURL: fileReader.result,
|
||
success: true,
|
||
});
|
||
};
|
||
|
||
// 开始读取
|
||
fileReader.readAsDataURL(blob);
|
||
|
||
});
|
||
|
||
}
|
||
```
|
||
|
||
#### 查找元素的 Vue 对象
|
||
|
||
```javascript
|
||
/**
|
||
* @typedef {object} FunctionResult
|
||
* @property {boolean} state 是否查找成功
|
||
* @property {HTMLElement} element Vue 对象所在的元素
|
||
* @property {object} data 找到的 Vue 对象
|
||
* @property {object[]} parents 父 Vue 对象
|
||
*/
|
||
|
||
/**
|
||
* @description 查找元素的 Vue 对象
|
||
* @param {HTMLElement} el 需要查找的元素
|
||
* @returns {FunctionResult} 返回查找结果信息
|
||
*/
|
||
function findElementVue(el) {
|
||
|
||
/** @type {FunctionResult} */
|
||
const result = {
|
||
state: false,
|
||
element: null,
|
||
data: null,
|
||
parents: [],
|
||
};
|
||
|
||
const attrName = '__vue__';
|
||
|
||
// 查找属性
|
||
while (el) {
|
||
const data = el[attrName];
|
||
if (data) {
|
||
result.state = true;
|
||
result.element = el;
|
||
result.data = data;
|
||
break;
|
||
} else {
|
||
el = el.parentElement;
|
||
}
|
||
}
|
||
|
||
// 查找父对象
|
||
if (result.state) {
|
||
let attrName = '$parent';
|
||
let parent = result.data[attrName];
|
||
|
||
while (parent) {
|
||
result.parents.push(parent);
|
||
parent = parent[attrName];
|
||
}
|
||
}
|
||
|
||
return result;
|
||
|
||
}
|
||
```
|
||
|
||
#### 查找字符 `char` 在 `str` 中第 `num` 次出现的位置
|
||
|
||
```javascript
|
||
function findChar(str = '', char = '', num = 1) {
|
||
var index = str.indexOf(char);
|
||
num = num - 1;
|
||
if (num > 0) {
|
||
for (var i = 0; i < num; i++) {
|
||
index = str.indexOf(char, index + 1);
|
||
}
|
||
}
|
||
return index;
|
||
}
|
||
```
|
||
|
||
#### 处理过渡效果
|
||
|
||
```javascript
|
||
// https://www.npmjs.com/package/d3-ease
|
||
|
||
/**
|
||
* @description 处理过渡效果
|
||
* @param {object} opts
|
||
* @param {number} opts.duration 时长(毫秒)
|
||
* @param {Function} opts.easeFn 过渡计算函数
|
||
* @param {Function} opts.onStop 结束时回调
|
||
* @param {Function} opts.onTick 进行时回调
|
||
* @returns {Promise<boolean>}
|
||
*/
|
||
function easeHandler(opts = {}) {
|
||
|
||
const {
|
||
duration = 0,
|
||
easeFn = easeLinear,
|
||
onStop = null,
|
||
onTick = null,
|
||
} = opts;
|
||
|
||
const isValid = (
|
||
(typeof duration === 'number' && duration >= 0) &&
|
||
(typeof easeFn === 'function') &&
|
||
(onStop ? typeof onStop === 'function' : true) &&
|
||
(onTick ? typeof onTick === 'function' : true)
|
||
);
|
||
|
||
if (!isValid) {
|
||
console.error('处理失败:参数错误');
|
||
return Promise.resolve(false);
|
||
}
|
||
|
||
// 时长为 0,直接结束
|
||
if (duration === 0) {
|
||
onTick && onTick(1);
|
||
onStop && onStop();
|
||
return Promise.resolve(true);
|
||
}
|
||
|
||
const sTime = Date.now();
|
||
const eTIme = sTime + duration;
|
||
|
||
return new Promise((resolve) => {
|
||
|
||
const fn = function () {
|
||
try {
|
||
|
||
const curr = Date.now();
|
||
const diff = Math.min(duration, curr - sTime);
|
||
const p = parseFloat((diff / duration).toFixed(3));
|
||
const n = parseFloat(easeFn(p).toFixed(3));
|
||
|
||
if (curr < eTIme) {
|
||
// [继续]
|
||
onTick && onTick(n);
|
||
requestAnimationFrame(fn);
|
||
} else {
|
||
// [结束]
|
||
onTick && onTick(1);
|
||
onStop && onStop();
|
||
resolve(true);
|
||
}
|
||
|
||
} catch (error) {
|
||
console.error('处理失败:');
|
||
console.error(error);
|
||
resolve(false);
|
||
}
|
||
};
|
||
|
||
// 开始
|
||
fn();
|
||
|
||
});
|
||
|
||
}
|
||
```
|
||
|
||
#### 对有符号整型、无符号整型、浮点型、十六进制、二进制的数据处理
|
||
|
||
参考:https://www.cnblogs.com/zhouheblog/p/13578012.html
|
||
|
||
```javascript
|
||
// 十六进制字符串转有符号整型(支持 S8、S16、S32)
|
||
function hexToInt(hex) {
|
||
if (!hex) {
|
||
return NaN;
|
||
}
|
||
if (hex.length % 2 !== 0) {
|
||
hex = '0' + hex;
|
||
}
|
||
let num = parseInt(hex, 16);
|
||
let max = Math.pow(2, hex.length / 2 * 8);
|
||
if (num > max / 2 - 1) {
|
||
num = num - max;
|
||
}
|
||
return num;
|
||
}
|
||
```
|
||
|
||
```javascript
|
||
// 十六进制字符串转无符号整型(支持 U8、U16、U32)
|
||
parseInt(str, 16);
|
||
```
|
||
|
||
```javascript
|
||
/**
|
||
* @description 合并对象(lodash.mergeWith)
|
||
* - 将会修改目标对象
|
||
* - 若来源属性为数组,将会直接替换
|
||
* - 若目标属性为对象,但来源属性为 `null`,则跳过
|
||
* @template TObject
|
||
* @param {TObject} obj 合并目标
|
||
* @param {TObject} src 合并来源
|
||
* @returns {TObject}
|
||
*/
|
||
function mergeObject(obj, src) {
|
||
return mergeWith(obj, src, function (objValue, srcValue) {
|
||
if (Array.isArray(srcValue)) {
|
||
return srcValue;
|
||
} else if (srcValue === null && isObject(objValue)) {
|
||
return objValue;
|
||
}
|
||
});
|
||
}
|
||
```
|
||
|
||
#### 格式化时间
|
||
|
||
```javascript
|
||
/**
|
||
* @description 格式化时间
|
||
* @param {(number|string|Date)} [time] 要格式化的时间(默认当前)
|
||
* @param {string} [format] 需要的格式(默认 yyyy-mm-dd hh:ii:ss)
|
||
* @returns {string} 格式化后的时间
|
||
*/
|
||
function formatTime(time = new Date(), format = 'yyyy-mm-dd hh:ii:ss') {
|
||
|
||
let timeType = typeof time;
|
||
|
||
/** @type {Date} */
|
||
let dateObj = null;
|
||
|
||
if (timeType == 'number' || timeType == 'string') {
|
||
dateObj = new Date(time);
|
||
} else if (timeType == 'object') {
|
||
dateObj = time;
|
||
}
|
||
|
||
// 时间信息
|
||
let timeInfo = {
|
||
// 年月日
|
||
y: dateObj.getFullYear(),
|
||
m: dateObj.getMonth() + 1,
|
||
d: dateObj.getDate(),
|
||
// 时分秒
|
||
h: dateObj.getHours(),
|
||
i: dateObj.getMinutes(),
|
||
s: dateObj.getSeconds(),
|
||
};
|
||
|
||
// 格式占位符正则
|
||
let formatReg = {
|
||
y: /y+/g,
|
||
m: /m+/g,
|
||
d: /d+/g,
|
||
h: /h+/g,
|
||
i: /i+/g,
|
||
s: /s+/g,
|
||
};
|
||
|
||
for (let key in formatReg) {
|
||
|
||
// 正则匹配
|
||
let matched = format.match(formatReg[key]);
|
||
|
||
// 获取对应的时间
|
||
let timeValue = String(timeInfo[key]);
|
||
|
||
// 无匹配结果
|
||
if (!matched) {
|
||
continue;
|
||
}
|
||
|
||
// 根据匹配结果(位数)进行替换
|
||
matched.forEach(function (v) {
|
||
let tLength = timeValue.length;
|
||
let vLength = v.length;
|
||
// 长度不足,补零
|
||
if (tLength < vLength) {
|
||
timeValue = timeValue.padStart(v.length, '0');
|
||
}
|
||
// 长度超出,截取
|
||
// if (tLength > vLength) {
|
||
// timeValue = timeValue.substring(tLength - vLength);
|
||
// }
|
||
// 替换对应的值
|
||
format = format.replace(v, timeValue);
|
||
});
|
||
|
||
}
|
||
|
||
return format;
|
||
|
||
}
|
||
```
|
||
|
||
#### 获取随机的十六进制色
|
||
|
||
```javascript
|
||
/** 获取随机的十六进制色 */
|
||
function getHEXColor() {
|
||
var codes = [
|
||
'0', '1', '2', '3', '4', '5', '6', '7',
|
||
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
|
||
];
|
||
var colors = ['#'];
|
||
for (let i = 0; i < 6; i++) {
|
||
let index = Math.floor(Math.random() * 16);
|
||
colors.push(codes[index]);
|
||
}
|
||
return colors.join('');
|
||
}
|
||
```
|
||
|
||
#### 获取元素的边界信息
|
||
|
||
```javascript
|
||
/**
|
||
* @description 获取元素的边界信息(当前元素必须在父元素内)
|
||
* @param {Element} curr 当前 DOM 元素
|
||
* @param {Element} parent 父 DOM 元素
|
||
*/
|
||
function getBoundaryInfo(curr, parent) {
|
||
|
||
if (!curr || !parent) {
|
||
console.error('获取失败,缺少参数!');
|
||
return null;
|
||
}
|
||
|
||
var result = {};
|
||
var currRect = curr.getBoundingClientRect();
|
||
var parentRect = parent.getBoundingClientRect();
|
||
|
||
// 当前元素四角坐标和宽高
|
||
result.curr = {
|
||
x0: currRect.left,
|
||
x1: currRect.right,
|
||
y0: currRect.top,
|
||
y1: currRect.bottom,
|
||
w: currRect.width,
|
||
h: currRect.height,
|
||
};
|
||
|
||
// 父元素四角坐标
|
||
result.parent = {
|
||
x0: parentRect.left,
|
||
x1: parentRect.right,
|
||
y0: parentRect.top,
|
||
y1: parentRect.bottom,
|
||
w: parentRect.width,
|
||
h: parentRect.height,
|
||
};
|
||
|
||
// 距离
|
||
result.distance = {
|
||
top: result.curr.y0 - result.parent.y0,
|
||
bottom: result.parent.y1 - result.curr.y1,
|
||
left: result.curr.x0 - result.parent.x0,
|
||
right: result.parent.x1 - result.curr.x1,
|
||
};
|
||
|
||
return result;
|
||
|
||
}
|
||
```
|
||
|
||
#### 获取元素的坐标信息
|
||
|
||
```javascript
|
||
/**
|
||
* @description 获取元素的坐标信息(四个角以及宽高)
|
||
* @param {Element} element DOM 元素
|
||
*/
|
||
function getElemPosInfo(element) {
|
||
if (!element) {
|
||
console.error('获取失败,缺少参数!');
|
||
return null;
|
||
}
|
||
let rect = element.getBoundingClientRect();
|
||
let data = {
|
||
x0: rect.left,
|
||
x1: rect.right,
|
||
y0: rect.top,
|
||
y1: rect.bottom,
|
||
w: rect.width,
|
||
h: rect.height,
|
||
};
|
||
return data;
|
||
}
|
||
```
|
||
|
||
#### 获取月份第一天和最后一天的时间戳
|
||
|
||
```javascript
|
||
/**
|
||
* @description 获取月份第一天和最后一天的时间戳
|
||
* @param {number} year 年份
|
||
* @param {number} month 月份
|
||
* @returns `{ start: 第一天, end: 最后一天 }`
|
||
*/
|
||
function getTimestampOfMonth(year, month) {
|
||
var start = new Date(year, month - 1, 1)
|
||
var end = new Date(year, month, 0);
|
||
var time = {
|
||
start: start.getTime(),
|
||
end: end.getTime()
|
||
};
|
||
return time;
|
||
}
|
||
```
|
||
|
||
#### 获取坐标下方的元素
|
||
|
||
```javascript
|
||
/**
|
||
* @description 获取坐标下方的元素(从子元素到父元素)
|
||
* @param {number} x 横坐标
|
||
* @param {number} y 纵坐标
|
||
*/
|
||
function elemsFromPoint(x, y) {
|
||
|
||
if (x === undefined || y === undefined) {
|
||
return [];
|
||
}
|
||
|
||
x = Math.floor(x);
|
||
y = Math.floor(y);
|
||
|
||
let item = document.elementFromPoint(x, y);
|
||
let items = [];
|
||
|
||
if (item) {
|
||
items.push(item);
|
||
} else {
|
||
return [];
|
||
}
|
||
|
||
while (item.parentElement) {
|
||
item = item.parentElement;
|
||
items.push(item);
|
||
}
|
||
|
||
return items;
|
||
|
||
};
|
||
```
|
||
|
||
#### 计算字符串的字符数
|
||
|
||
```javascript
|
||
/**
|
||
* @description 计算字符串的字符数(数字英文字母 +1,其他 +3)
|
||
* @param {string} str 被检测的字符串
|
||
* @returns {number} 字符数
|
||
*/
|
||
function calcChars(str) {
|
||
var reg = /[0-9a-zA-Z]/;
|
||
var sum = 0;
|
||
for (let i in str) {
|
||
if (reg.test(str.charAt(i))) {
|
||
sum += 1;
|
||
} else {
|
||
sum += 3;
|
||
}
|
||
}
|
||
return sum;
|
||
}
|
||
```
|
||
|
||
#### 加减法精度
|
||
|
||
```javascript
|
||
/**
|
||
* @description 加减法精度
|
||
* @param {string} type 类型(plus、sub)
|
||
* @param {number} [num1] 数值1,默认为 0
|
||
* @param {number} [num2] 数值2,默认为 0
|
||
* @returns {(number|null)} 返回两个数值相加或相减后的结果
|
||
*/
|
||
function accPlusAndSub(type, num1 = 0, num2 = 0) {
|
||
|
||
var decimalsNum1 = (String(num1).split('.')[1] || '').length;
|
||
var decimalsNum2 = (String(num2).split('.')[1] || '').length;
|
||
var decimalsMax = Math.max(decimalsNum1, decimalsNum2);
|
||
var multiplies = Math.pow(10, decimalsMax);
|
||
|
||
if (type === 'plus') {
|
||
return ((num1 * multiplies + num2 * multiplies) / multiplies);
|
||
} else if (type === 'sub') {
|
||
return ((num1 * multiplies - num2 * multiplies) / multiplies);
|
||
} else {
|
||
return null;
|
||
}
|
||
|
||
}
|
||
```
|
||
|
||
#### 检测用户是否离开页面
|
||
|
||
```javascript
|
||
/**
|
||
* @description 检测用户是否离开页面(一秒检测一次)
|
||
* @param {object} [options] 配置选项
|
||
* @param {function} [options.onblur] 用户离开页面时的回调函数
|
||
* @param {function} [options.onfocus] 用户返回页面时的回调函数
|
||
* @param {string} [options.blurDelay] 设定用户离开页面多久后才调用 onblur
|
||
* - 单位为秒
|
||
* - 默认 0
|
||
* @param {string} [options.timerName] 定时器名称
|
||
* - 用于 setInterval()
|
||
* - 默认 tCheckPageBlur
|
||
*/
|
||
function checkPageBlur(options) {
|
||
|
||
var config = {
|
||
onblur: null,
|
||
onfocus: null,
|
||
blurDelay: 0,
|
||
timerName: 'tCheckPageBlur',
|
||
};
|
||
|
||
Object.assign(config, options);
|
||
|
||
var timerName = config.timerName; // 定时器名称
|
||
var checkDelay = 0; // 延时
|
||
var blurTriggered = false; // 标记状态
|
||
|
||
clearInterval(window[timerName]);
|
||
window[timerName] = setInterval(function () {
|
||
var isFocus = document.hasFocus();
|
||
|
||
if (isFocus && blurTriggered) {
|
||
// 在页面且触发过 blur
|
||
blurTriggered = false;
|
||
checkDelay = 0;
|
||
try {
|
||
config.onfocus && (config.onfocus());
|
||
} catch (err) {
|
||
console.error('[检测] 回调函数 onfocus 出错\n', err);
|
||
}
|
||
} else if (!isFocus && !blurTriggered) {
|
||
// 不在页面且未触发 blur
|
||
if (checkDelay >= config.blurDelay) {
|
||
blurTriggered = true;
|
||
checkDelay = 0;
|
||
try {
|
||
config.onblur && (config.onblur());
|
||
} catch (err) {
|
||
console.error('[检测] 回调函数 onblur 出错\n', err);
|
||
}
|
||
} else {
|
||
checkDelay += 1;
|
||
}
|
||
}
|
||
}, 1000);
|
||
|
||
}
|
||
|
||
// 调用
|
||
checkPageBlur({
|
||
onblur: function () {
|
||
console.log('[检测] 用户离开页面');
|
||
},
|
||
onfocus: function () {
|
||
console.log('[检测] 用户返回页面');
|
||
},
|
||
});
|
||
```
|
||
|
||
#### 解析 URL 地址的参数(?=...)为一个对象
|
||
|
||
```javascript
|
||
function queriesToObj(url = '') {
|
||
var split = url.split('?')[1];
|
||
var arr = split.split('&');
|
||
var obj = {};
|
||
|
||
arr.forEach(function (item) {
|
||
var kv = item.split('=');
|
||
obj[kv[0]] = kv[1];
|
||
});
|
||
|
||
return obj;
|
||
}
|
||
```
|
||
|
||
#### 矩形碰撞检测
|
||
|
||
```javascript
|
||
/**
|
||
* @description 矩形碰撞检测
|
||
* @param {Element} elemA 当前元素
|
||
* @param {Element} elemB 目标元素
|
||
* @param {boolean} [checkAside] 是否包含边缘碰撞,默认包含
|
||
* @returns {object} `{ error: 是否检测失败, hit: 是否碰撞 }`
|
||
*/
|
||
function rectColisionCheck(elemA, elemB, checkAside = true) {
|
||
const result = {
|
||
error: false,
|
||
hit: false,
|
||
};
|
||
|
||
if (!(elemA && elemB)) {
|
||
console.error('缺少参数');
|
||
result.error = true;
|
||
return result;
|
||
}
|
||
|
||
const rectA = elemA.getBoundingClientRect();
|
||
const rectB = elemB.getBoundingClientRect();
|
||
|
||
if (checkAside) {
|
||
result.hit = !(
|
||
rectA.bottom < rectB.top ||
|
||
rectA.left > rectB.right ||
|
||
rectA.top > rectB.bottom ||
|
||
rectA.right < rectB.left
|
||
);
|
||
} else {
|
||
result.hit = !(
|
||
rectA.bottom <= rectB.top ||
|
||
rectA.left >= rectB.right ||
|
||
rectA.top >= rectB.bottom ||
|
||
rectA.right <= rectB.left
|
||
);
|
||
}
|
||
|
||
return result;
|
||
}
|
||
```
|
||
|
||
#### 设置事件对象属性
|
||
|
||
```javascript
|
||
/**
|
||
* @description 设置事件对象属性
|
||
* @param {Eveny} ev 事件对象
|
||
* @param {object} props 要设置的属性
|
||
*/
|
||
function setEvProps(ev, props = {}) {
|
||
if (ev) {
|
||
for (let key in props) {
|
||
Object.defineProperty(ev, key, {
|
||
configurable: true,
|
||
enumerable: true,
|
||
get: function () {
|
||
return props[key];
|
||
},
|
||
});
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 通过点路径访问对象属性
|
||
|
||
```javascript
|
||
/**
|
||
* @description 通过点路径访问对象属性
|
||
* @param {object} obj
|
||
* @param {string} path
|
||
*/
|
||
function getObjValue(obj, path = '') {
|
||
|
||
if (typeof obj !== 'object') {
|
||
console.error('访问失败,参数 obj 错误!');
|
||
return;
|
||
}
|
||
|
||
if (typeof path !== 'string') {
|
||
console.error('访问失败,参数 path 错误!');
|
||
return;
|
||
}
|
||
|
||
if (path === '') {
|
||
return obj;
|
||
}
|
||
|
||
return path.split('.').reduce((a, b) => {
|
||
return (a === undefined ? a : a[b]);
|
||
}, obj);
|
||
|
||
}
|
||
```
|
||
|
||
#### 暂停执行代码一段时间
|
||
|
||
```javascript
|
||
/**
|
||
* @description 暂停执行代码一段时间
|
||
* @param {number} [time] 时长(毫秒),默认 1000
|
||
* @returns {Promise} Promise
|
||
*/
|
||
function sleep(time = 1000) {
|
||
return new Promise(function (resolve) {
|
||
setTimeout(() => {
|
||
resolve();
|
||
}, time);
|
||
});
|
||
}
|
||
|
||
await sleep(1000);
|
||
```
|
||
|
||
#### 字符串转文件
|
||
|
||
```javascript
|
||
/**
|
||
* @description 字符串转文件
|
||
* @param {string} data 字符串数据
|
||
* @param {string} filename 文件名
|
||
* @param {string} filetype 文件类型(MIME)
|
||
*/
|
||
function strToFile(data = '', filename = 'export.txt', filetype = 'text/plain') {
|
||
|
||
// 转为 Blob
|
||
var strToBlob = new Blob([data], { type: filetype });
|
||
// URL 对象兼容性处理
|
||
var urlObject = window.URL || window.webkitURL || window;
|
||
// 创建对象 URL
|
||
var blobURL = urlObject.createObjectURL(strToBlob);
|
||
// 创建 a 元素
|
||
var aElem = document.createElement('a');
|
||
|
||
// 设置属性
|
||
aElem.classList.add('hidden');
|
||
aElem.download = filename;
|
||
aElem.href = blobURL;
|
||
aElem.target = '_blank';
|
||
|
||
// 添加元素
|
||
document.body.appendChild(aElem);
|
||
|
||
// 模拟点击
|
||
aElem.click();
|
||
|
||
// 移除元素
|
||
aElem.remove();
|
||
|
||
// 释放对象
|
||
urlObject.revokeObjectURL(blobURL);
|
||
|
||
}
|
||
```
|
||
|
||
|
||
|
||
#### 转换 JSON 为 SQLite 插入语句
|
||
|
||
```javascript
|
||
/**
|
||
* @description 生成 SQLite 插入语句
|
||
* @param {string} t 表名
|
||
* @param {string[]} k 键名列表
|
||
* @param {(number|string)[]} v 值列表
|
||
*/
|
||
function getSqlInsert(t, k, v) {
|
||
k = k.map(val => `"${val}"`);
|
||
v = v.map(val => (val === null ? 'NULL' : `'${val}'`));
|
||
return `INSERT INTO "${t}" (${k.join(', ')}) VALUES (${v.join(', ')})`;
|
||
}
|
||
|
||
/**
|
||
* @description 转换 JSON 为 SQLite 语句(INSERT)
|
||
* @param {string} table 表名
|
||
* @param {Record<string, number|string>[]} data
|
||
* @param {string[]} [keys] 属性过滤
|
||
*/
|
||
function jsonToSql(table, data, keys) {
|
||
try {
|
||
|
||
const result = [];
|
||
const filter = Array.isArray(keys);
|
||
|
||
if (!table) {
|
||
throw new Error('缺少 table 参数');
|
||
}
|
||
|
||
if (!Array.isArray(data)) {
|
||
throw new Error('缺少 data 参数');
|
||
}
|
||
|
||
for (let i = 0; i < data.length; i++) {
|
||
let item = data[i];
|
||
let keys = [];
|
||
let values = [];
|
||
let sql = '';
|
||
// 记录 key 和 value
|
||
if (filter) {
|
||
for (let key in item) {
|
||
if (keys.includes(key)) {
|
||
keys.push(key);
|
||
values.push(item[key]);
|
||
}
|
||
}
|
||
} else {
|
||
for (let key in item) {
|
||
keys.push(key);
|
||
values.push(item[key]);
|
||
}
|
||
}
|
||
// 生成 SQL 插入语句
|
||
sql = getSqlInsert(table, keys, values);
|
||
result.push(sql);
|
||
}
|
||
|
||
return result;
|
||
|
||
} catch (error) {
|
||
console.error('转换失败:');
|
||
console.error(error);
|
||
return null;
|
||
}
|
||
}
|
||
```
|