JSNeedAttention
JS Attention
push 返回的是数组增加后的长度!!!
对象名可变
setFieldsValue({ [`bank${index}`]: val });
判断是否空对象
Object.keys(obj);
JSON.stringify(obj) !== "{}";
还有
$.isEmptyObject(data2);
Object.getOwnPropertyNames(data3); // 和Object.keys
类似
关闭标签替换背景图
注意这里的background-size
属性是cover
,也就是让背景图撑满的关键属性。
.exit-btn {
position: fixed;
right: 30px;
top: 30px;
color: white;
background-image: url(../../../../../assets/closeNormal@2x.png);
&:hover {
background-image: url(../../../../../assets/closeHover@2x.png);
}
background-size: cover;
border-radius: 2px;
width: 50px;
height: 49px;
cursor: pointer;
}
获取鼠标点击 DOM 的类名
Event.currentTarget
Event 接口的只读属性 currentTarget 表示的,标识是当事件沿着 DOM 触发时事件的当前目标。它总是指向事件绑定的元素,而 Event.target 则是事件触发的元素。
// 根据当前点击的选色器,取index
const clickWhichOne = (e: any) => {
const clickDom = e.currentTarget;
if (clickDom) {
// 得到当前类名 形如 ChartColorConfig-x
const rightClassName = clickDom.classList[2];
// 转换成数字
const name = rightClassName.split("ChartColorConfig-")[1];
setChooseIndex(name - 0);
}
};
splice 删除与 slice 保留
splice 移除 start 到 end 的词
slice 保留 start 到 end-1 的词
树的修改
// 树的过滤方法
export function treeFilter(tree, func) {
// 使用map复制一下节点,避免修改到原树
return tree
.map((node) => ({ ...node }))
.filter((node) => {
node.children = node.children && treeFilter(node.children, func);
return func(node) || (node.children && node.children.length);
});
}
getGlobalChooseValues;
// 树的寻找
export function treeFind(tree, func, keyWord) {
for (const data of tree) {
if (func(data, keyWord)) return data;
if (data.children) {
const res = treeFind(data.children, func, keyWord);
if (res) return res;
}
}
return null;
}
enum 枚举类型
首字母大写
// 首字母大写 tableTrColor -> TableTrColor
function upperFirstWord(str: string) {
const wordList = str.split("");
const oneWord = wordList[0];
const otherWord = wordList.slice(1);
otherWord.unshift(oneWord.toUpperCase());
return otherWord.join("");
}
掘金更换动头
// 沸点界面F12 输入以下内容 回车发起 等待新头像审核通过即可拥有 动头!
var ajax = new XMLHttpRequest();
ajax.open("post", "https://juejin.cn/web/user/update/user_info/", true);
ajax.setRequestHeader("content-type", "application/x-www-form-urlencoded");
// avatar更换为你想要的图片链接
ajax.send(
"aid=2608&avatar=https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c38e0574bf6949c28e3c75957a3e3893~tplv-k3u1fbpfcp-jj-mark:345:345:345:345:q75.awebp"
);
ajax.onreadystatechange = function () {
if (ajax.readyState == 4 && ajax.status == 200) {
var c = ajax.responseText;
console.log(c);
}
};
文件下载
iframe 下载
function iframeDownload(url) {
try {
const elemIF = document.createElement('iframe');
elemIF.src = url;
elemIF.style.display = 'none';
document.body.appendChild(elemIF);
} catch (e) {
console.log('下载异常!');
}
}
```
> https://www.cnblogs.com/xiong950413/p/14209813.html
重定向下载
window.location.href = "www.baidu.com";
需要注意的一个问题
如果是在非 https 环境下,重定向到其他页面,会有一个报错提示
The file at 'http://192.168.208.163/OI9G84ddvg2-0aF_QWpYsxIqhgJCfYI9M5GnJ90ZXyWA4xMpHYP2bGFZw9-GJOqaIQDNEleKjF4G_5t9AmQN_XHuH7gKrzSjpImjNHK5xsJwjivlNU9Zs1YWAuv9yqSh.ofd' was loaded over an insecure connection. This file should be served over HTTPS
与代码和网页安全性相关。当浏览器尝试通过不安全的 HTTP 连接加载资源时,会出现此警告。要解决这个问题,你应该确保所有资源都通过安全的 HTTPS 连接提供。
关于 debounce
今天修 bug 的时候,遇到一个需用防抖的,但我发现即使用了debounce
方法,期内需要运行的fn
妹有运行,实际上就是对函数的不理解。
我的写法就是把他当作一个普通的箭头函数直接用了,实际上,当时debounce
返回的只是fn
,还需要debounce()
(即fn()
)使用。
也可以参照下面这种写法,直接运行debounce
。
btnGetResumeContainer.onclick = debounce(function () {
getResume(dataResume);
}, 3000);
//防抖debounce代码:
function debounce(fn, delay) {
var timer = null; // 创建一个标记用来存放定时器的返回值
return function (e) {
// 每当用户输入的时候把前一个 setTimeout clear 掉
if (timer !== null) {
clearTimeout(timeout);
}
// 然后又创建一个新的 setTimeout, 这样就能保证interval 间隔内如果时间持续触发,就不会执行 fn 函数
timer = setTimeout(() => {
timer = null;
fn.apply(this, arguments);
}, delay);
};
}
清除页面路由
const cleanQuery = () => {
// 获取当前URL
const url = window.location.href;
const isHaveQuery = url.includes("isJumpToBussinessVoucherTemplate");
if (isHaveQuery) {
// 创建URLSearchParams对象
const params = new URLSearchParams(url);
// 清除查询参数
params.delete("isJumpToBussinessVoucherTemplate"); // 假设要清除的查询参数名为queryParam1
// 跳转到新的URL
router.location("/setting/voucher-template");
}
};
科学计数法
Number 强转 出来的还是科学计数法
function getFullNum(num) {
// 处理非数字
if (isNaN(num)) return num;
// 处理不需要转换的数字
var str = "" + num;
if (!/e/i.test(str)) return num;
return num.toFixed(18).replace(/\.?0+$/, "");
}
https://juejin.cn/post/7194002669755695161
项目里用到的 util
// 科学计数法数字转为正常数字字符串
util.getFullNum = (num) => {
if (isNaN(num)) {
return num;
}
const str = "" + num;
if (!/e/i.test(str)) {
return num;
}
const fixed = ("" + num).match(/\d+$/)[0];
return Number(num).toFixed(fixed);
};
edge 中拓展不可用
找到隐私和服务中的Microsoft Defender Smartscreen
并关掉他
关于批量按顺序下载
参考文章
promise resolve reject
https://www.cnblogs.com/lunlunshiwo/p/8852984.html#4917337
reduce 按顺序调用
https://juejin.cn/post/7030625338065420302?searchId=202311041036275432B88F9F3A984960AA
demo
// import React from 'react';
import { Button } from "antd";
const Test = () => {
// 准备回调的次数 模拟待发送的文件数量
const waitForUpdateList = [1, 2, 3];
let resultList = [];
function simulateRequest(lastOneValue) {
// 这里传入的index - 1,是为了获取上一轮的返回值
console.log("lastOneValue", lastOneValue);
const time = 500;
return new Promise(function (resolve, reject) {
setTimeout(() => {
let num = Math.ceil(Math.random() * 10);
if (num > 5) {
// 上一轮值存在 且是res 就绑定在这一轮
if (lastOneValue !== undefined) {
resolve(num + lastOneValue);
} else {
resolve(num);
}
} else {
reject(num);
}
}, time);
});
}
// 循环顺序请求
function cycleRequest() {
console.log("新的一轮开始请求");
// 一个请求周期,这边为了模拟方便长度为 10,实际情况可能是 10000 或 99999 这样的
const arr = waitForUpdateList;
arr.reduce(async (last, _, index) => {
let lastOneValue;
await last;
if (index) {
lastOneValue = resultList[index - 1];
}
return simulateRequest(lastOneValue)
.then((res) => {
console.log("res", res);
resultList.push(res);
})
.catch((e) => {
console.log("e", e);
resultList.push(e);
})
.finally(() => {
// 达到边界 显示所有的promise结果
// 为什么不用useState? 会有延迟
if (index + 1 === arr.length) {
console.log("最终", resultList);
}
});
}, undefined);
}
// 启动
const everyThingDone = () => {
console.log("每轮重新开始,清空result");
resultList = [];
cycleRequest();
};
return <Button onClick={everyThingDone}>探索链式调用</Button>;
};
export default Test;
关于轮询 以获取数电文件为例
注意这里每次调用的时机,是在确保上次任务存在的情况下,再调用
async getBtnLoading() {
const { dispatch } = this.props;
try {
const res = await services.getNoDiskExtractStateForFile();
if (res) {
// 递归 如果当前还是任务中 就再次调用
this.times = setTimeout(() => {
if (this.times) {
this.getBtnLoading();
}
}, 10000);
}
} catch (e) {
// 丢到这里的如果是 执行失败\成功 需要更新横幅
// ...
}
}
如果是多个轮询同时调用,这里要将定时器的优先级提高!
// 置于当前/effect/index.js的顶级
let timer;
async getList(params) {
const { year, pageSize, current, risk, accountName = '' } = this.getState();
const deliver = { pageSize, current, year, accountName, risk, ...params };
try {
const res = await services.list(deliver);
const { list, executingAccountIdList, total } = res;
this.updateState({ dataSource: list, executingAccountIdList, total });
// 当前是否存在正在生成的列表 是的话 继续轮询
const isExistBuilding = executingAccountIdList && executingAccountIdList.length;
if (isExistBuilding) {
// 似乎这两步可以去掉一个?
timer && clearTimeout(timer);
timer = setTimeout(() => {
if (timer) {
// 当前定时器存在 就清除再开始轮询
clearTimeout(timer);
this.getList(params);
}
}, 10000);
}
} catch (e) {
// ...
}
}
简略策略模式优化 if else
// 客户列表
const currencyList = ["美元", "澳元", "泰铢", "欧元"];
// 汇率
const rateList = {
美元: 0.144881,
澳元: 0.216258,
泰铢: 5.3383,
欧元: 0.143649,
};
// 实际开发中,上述两个数据都是动态从后端获得的
const rateKeys = Object.keys(rateList);
// 策略容器
const stratagy = {};
// 为每种汇率添加一个key,并赋值为一个计算对应价格的方法
rateKeys.forEach((item) => {
// 传入 RMB 价格作为基数
stratagy[item] = (RMPPrice) => {
return RMPPrice * rateList[item];
};
});
function getAbroadPrice(RMBPrice = 100, target = "美元") {
return stratagy[target](RMBPrice);
}
getAbroadPrice(100, "美元"); // 14.481...
input 无法重上传图片
e.target.value=''
https://www.cnblogs.com/momo798/p/13926851.html
原生列表美化
<table>
<thead>
<tr>
<th class="risk-level">风险等级</th>
<th>风险明细</th>
</tr>
<tr>
<td>高</td>
<td>往来款项存在长期挂账</td>
</tr>
<tr>
<td>低</td>
<td>广告和业务宣传费累计发生85,302,23,超过营业收入的15%</td>
</tr>
</thead>
</table>
table {
margin-top: 1rem;
width: 100%;
border: 1px #e6e7e8 solid;
border-collapse: collapse;
text-align: center;
}
td,
th {
border: 1px solid #e6e7e8;
}
th {
background: #e9ecf0;
border: 1px solid #dcdddf;
}
thead {
height: 40px;
background-color: #e9ecf0;
font-family: MicrosoftYaHei-Bold;
font-size: 18px;
color: #3f3f3f;
letter-spacing: 0.9px;
text-align: center;
font-weight: 700;
}
tr {
height: 4rem;
background-color: #fff;
}
td {
font-family: MicrosoftYaHei;
font-size: 18px;
color: #3f3f3f;
letter-spacing: 0.9px;
font-weight: 400;
}
/* 注意 04 05 页中的数字项是右对齐 写在了单独样式里面
通用的是左对齐
*/
td:not(:first-child) {
text-align: left;
padding-left: 1rem;
}
/* 奇数项添加背景色 */
tr:nth-child(odd) {
background-color: #f8fafb;
}
按钮禁用样式 不触发点击动作
.delete-disabled {
color: #c8c9cc;
&:hover {
pointer-events: none;
}
}
关于文件流下载
https://juejin.cn/post/7027431356443394084
https://juejin.cn/post/6878912072780873742
// 使用
// downloadType 0 预览 1 下载
const deliver = { year, quarters, accountIdList, downloadType: 1 };
const url = '/instead/v2/customer/financial/report/download.do';
const res = await axios({
method: 'post',
url,
responseType: 'blob', // 必须,服务器返回的数据类型
headers: {
'Content-Type': 'application/json',
},
data: deliver,
});
// 下载正常处理
downloadFileByBlod(res, fileName);
```
```js
import { message } from "antd";
// 文件流下载
export const downloadFileByBlod = (res, fileName) => {
// 兼容ie11
if (window.navigator.msSaveOrOpenBlob) {
try {
const blobObject = new Blob([res.data]);
window.navigator.msSaveOrOpenBlob(blobObject, fileName);
} catch (e) {
console.log(e);
}
return;
}
// a标签实现下载
const curUrl = window.URL || window.webkitURL || window.moxURL;
const link = document.createElement("a");
link.style.display = "none";
// 创建下载链接,将文件流转化为一个文件地址
link.href = curUrl.createObjectURL(new Blob([res.data]));
link.setAttribute("download", fileName); // 下载的文件名
document.body.appendChild(link);
link.click(); // 触发点击事件执行下载
document.body.removeChild(link); // 下载完成进行释放
};
// 根据文件名后缀获取当前文件类型
export const getLastFileTypeByName = (value) => {
const dotArray = value.split(".");
const lastDotArray = dotArray[dotArray.length - 1];
return lastDotArray;
};
export const getFileTypeBySource = (file, legitTypeList) => {
// 注意这里的类型判断一定要从文件名读取! ios 无 file.type!!!
const exactFileValue = file.name;
const lastFileTypeByName = getLastFileTypeByName(exactFileValue);
// 实测移动端 不走相册 直接选图片文件 尾缀为大写PNG 这里多加一层逻辑
// ↑ 从H5抄过来的 问题不大
// 这里全部转换成小写 进入匹配
let isLegitType = false;
isLegitType = legitTypeList.some((key) =>
lastFileTypeByName.toLowerCase().includes(key)
);
return isLegitType;
};
// 1 KB = 1024 B
// 转换文件大小为可读单位
export const getFileSize = (fileSize) => {
let result = "";
if (fileSize >= 1073741824) {
// B => GB
result =
fileSize % 1073741824 === 0
? `${fileSize / 1073741824}G`
: `${Math.trunc(fileSize / 1073741824)}G`;
} else if (fileSize >= 1048576) {
// B => MB
result =
fileSize % 1048576 === 0
? `${fileSize / 1048576}MB`
: `${Math.trunc(fileSize / 1048576)}MB`;
} else if (fileSize >= 1024) {
// B => KB
result =
fileSize % 1024 === 0
? `${fileSize / 1024}KB`
: `${Math.trunc(fileSize / 1024)}KB`;
} else if (fileSize !== undefined) {
result = `${fileSize}B`;
}
return result;
};
// 上传文件前置校验 文件类型 文件大小
export const beforeUpload = (files, fileSizeLimit, legTypeList) => {
const readableMaxFileSize = getFileSize(fileSizeLimit);
const fileSupportTypeText = legTypeList.join("、");
// if (!(files && files.length)) {
// message.error(`请添加${kind}`);
// return false;
// }
let flag = true;
// eslint-disable-next-line consistent-return
files.forEach((file) => {
const isLegFileType = getFileTypeBySource(file, legTypeList);
if (!isLegFileType) {
message.error(`文件支持格式: ${fileSupportTypeText}`);
flag = false;
}
if (file.size > fileSizeLimit) {
message.error(`文件最大支持${readableMaxFileSize}`);
flag = false;
}
});
return flag;
};
关于安卓滚动条样式
一个关键样式
overflow:overlay
悬浮在上方,目前只有移动端支持webkit
内核的可用,PC 上已废弃
https://zhuanlan.zhihu.com/p/88347981
https://zhuanlan.zhihu.com/p/88347981
使用 JS 直接获取子元素
https://www.coder.work/article/5383247
JQ 遍历元素方法
https://www.runoob.com/jquery/jquery-ref-traversing.html
禁止输入 emoji
请注意,由于 Emoji 字符的范围广泛且可能随时间变化,无法保证完全过滤所有的 Emoji 字符。因此,如果需要更严格的过滤或更全面的 Emoji 支持,可能需要使用专门的 Emoji 过滤库或更复杂的验证逻辑。
https://segmentfault.com/q/1010000007329875
// 过滤掉emoji表情
// 基本思想就是遍历字符串中的每个字符,如果该字符的长度为4个字节,就将其删除。
function filterEmoji($str)
{
$str = preg_replace_callback(
'/./u',
function (array $match) {
return strlen($match[0]) >= 4 ? '' : $match[0];
},
$str);
return $str;
}
input 的聚焦动作无效 autofocus
https://juejin.cn/post/7034707796545241095
react 中要用autoFocus
moment 对象操作的影响去除
moment()再包裹 生成新的对象