2023-11-10 JS复制表格到Excel.md

简单示例,这里用的是Fusion的Table

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import { Table, Button } from "@alifd/next";

function App() {

const dataSource = [
{
item: '棉鞋1',
img: 'https://img.alicdn.com/imgextra/i3/O1CN01oa2oWk1THYOTjD8um_!!6000000002357-2-tps-110-110.png',
sku: '34码',
amount: 999,
induction: {
a: 10,
b: 12
}
},
{
item: '棉鞋1',
img: 'https://img.alicdn.com/imgextra/i2/O1CN010URR3q1qa5aKdpsOh_!!6000000005511-2-tps-110-110.png',
sku: '35码',
amount: 88,
induction: {
a: 23,
b: 77
}
},
{
item: '拖鞋1',
img: 'https://img.alicdn.com/imgextra/i4/O1CN01xUEUfU1TCyQuxXEps_!!6000000002347-2-tps-110-110.png',
sku: '38码',
amount: 0,
induction: {
a: 0,
b: 100
}
},
{
item: '拖鞋2',
img: 'https://img.alicdn.com/imgextra/i2/O1CN01wtSTlr28OmrVgPKQ9_!!6000000007923-2-tps-200-200.png',
sku: '35码',
amount: 123,
induction: {
a: 90,
b: 8
}
},

]
const cellProps = (rowIndex, colIndex) => {
if (rowIndex === 0 && colIndex === 0) {
return {
// take 3 rows's space
rowSpan: 2
};
}

};

const convertTableToExcel = (
id = undefined, // Fusion Table 的id
filterHeader = false, // 复制时是否滤掉表头
) => {
return new Promise((resolve, reject) => {
try {
let htmlString = document.getElementById(id).innerHTML
const filterDOM = (selector) => {
// 创建一个临时div元素
var tempDiv = document.createElement('div');
// 将HTML字符串赋值给临时div的innerHTML属性
tempDiv.innerHTML = htmlString;
// 使用querySelectorAll方法获取所有要过滤的元素,并将其从临时div中移除
tempDiv.querySelectorAll(selector).forEach(function (element) {
element.remove();
});
// 获取过滤后的HTML字符串
return tempDiv.innerHTML;
}
htmlString = filterDOM('.next-table-lock-left')
if (filterHeader) {
htmlString = filterDOM('thead')
}

console.log(htmlString)
const type = "text/plain";
const blob = new Blob([htmlString], { type });
const data = [new ClipboardItem({ [type]: blob })];

navigator.clipboard.write(data).then(
() => resolve(),
(error) => reject(error),
);
} catch (error) {
reject(error)
}
})
}
return (
<div>
<Button type="primary" onClick={() => convertTableToExcel('table1')} style={{ marginRight: 10 }}>复制</Button>
<Button type="primary" onClick={() => convertTableToExcel('table1', true)}>复制(不含表头)</Button>
<Table id="table1" dataSource={dataSource} cellProps={cellProps}>
<Table.Column title="商品" width={300} dataIndex="item" lock="left" />
<Table.ColumnGroup title="信息">
<Table.Column title="规格" width={200} dataIndex="sku" />
{/* <Table.Column title="图片" width={200} dataIndex="img" cell={(val) => <img src={val} />} /> */}
<Table.Column title="库存" width={200} dataIndex="amount" />
<Table.Column title="说明" width={200} dataIndex="induction" cell={(val) => {
return <div>
<div>全包:{val.a}%</div>
<div>半包:{val.b}%</div>
</div>
}} />

</Table.ColumnGroup>
</Table>
</div>
)
}

export default App

实际上核心代码就这几行

1
2
3
4
5
6
7
8
9
const htmlString = '' // 这里就是你要复制的东西
const type = "text/plain";
const blob = new Blob([htmlString], { type });
const data = [new ClipboardItem({ [type]: blob })];

navigator.clipboard.write(data).then(
() => resolve(),
(error) => reject(error),
);

2023-08-04 JavaScript统计字符串广义上的长度

就这一个函数就可解决,非常好用

1
2
3
4
5
6
7
8
9
10
function unicodeLengthIgnoreSequence(str) {
let segmenter = new Intl.Segmenter();
let segments = segmenter.segment(str);
return [...segments].length;
}

console.log(
'👪𠄘👨‍👩‍👧‍👦'.length,
unicodeLengthIgnoreSequence('👪𠄘👨‍👩‍👧‍👦'),
)

得到结果👇🏻

1
> 15 3

Hexo怎么部署到Cloudflare Pages、怎么绑定阿里云的域名

先注册并登录Cloudflare Pages的网址

https://pages.cloudflare.com/

点击左侧菜单栏的Workers 和 Pages,然后点击创建应用程序

点击 Pages,然后点击连接到Git

选择好自己的Hexo博客仓库以后,在这里填写构建命令和构建产物目录

点击自定义域后,输入自己想要自定义的域名,例如我是blog.xxx.xxx

然后点击开始CNAME设置

上图的名称对应阿里云的主机记录目标对应阿里云的记录值

然后去阿里云解析域名

解析完域名后,再回到cloudflare pages点击检查DNS记录就大功告成啦

Flutter 3.0首次运行时卡在"Running Gradle task assembleDebug"的解决办法

第一步

找到flutter sdk路径下的这个文件

flutter/packages/flutter_tools/gradle/flutter.gradle

打开,并找到这个位置

1
2
3
4
5
6
7
buildscript {
repositories {
google()
mavenCentral()
}
...
}

注释掉 google()mavenCentral() ,并更改为

1
2
3
4
5
6
7
8
9
10
buildscript {
repositories {
// google()
// mavenCentral()
maven { url 'https://maven.aliyun.com/repository/google' }
maven { url 'https://maven.aliyun.com/repository/jcenter' }
maven { url 'https://maven.aliyun.com/nexus/content/groups/public' }
}
...
}

第二步

找到当前工程路径下的这个文件

/android/build.gradle

和第一步一样,打开,并注释掉 google()mavenCentral()并更改

注意第二步有两处,所以要改两个地方

第三步

还是这个文件

/android/build.gradle

找到DEFAULT_MAVEN_HOST并替换为国内的镜像地址

1
2
3
4
5
class FlutterPlugin implements Plugin<Project> {
// private static final String DEFAULT_MAVEN_HOST = "https://storage.googleapis.com";
private static final String DEFAULT_MAVEN_HOST = "https://storage.flutter-io.cn";
...
}

基本上完成上面这三步,就可以运行如下命令尝试再次启动了

1
flutter clean
1
flutter run

什么?你还不行啊。。。我倒,接着往下看吧,真没想到你这么倒霉

第四步

有可能是第三步的镜像地址挂了,可以把第三步再改回来,咱们改host

打开这个网站 https://tool.chinaz.com/speedtest/storage.googleapis.com

获取你的能访问到的加速ip地址,改hosts

有好多个呢,你自己试试吧

1
2
3
4
5
# localhost name resolution is handled within DNS itself.
# 127.0.0.1 localhost
# ::1 localhost

142.251.43.16 storage.googleapis.com # 例如这样改,Mac和Linux不做介绍

再试试咯,不行就往下走

1
flutter clean
1
flutter run

第五步

到这基本上宣告你的gradle是死活下载不下来了,只能离线下载了,找到工程目录下的这个文件打开,找到里面的distributionUrl

/android/gradle/wrapper/gradle-wrapper.properties

1
2
...
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip

你看啊,我这里是gradle-7.5-all.zip

那就去访问gradle的离线下载网站吧,找到gradle-7.5-all.zip并且下载下来

https://services.gradle.org/distributions/

然后去系统的gradle存放目录替换掉死活下载不下来的那些东西

例如我是C:\Users\vear\.gradle\wrapper\dists\gradle-7.5-all\6qsw290k5lz422uaf8jf6m7co\gradle-7.5-all.zip

那就把C:\Users\vear\.gradle\wrapper\dists\gradle-7.5-all\6qsw290k5lz422uaf8jf6m7co下面的东西全删了

把新下载的gradle-7.5-all.zip拷贝进去

再回来试试咯,反正我是行了,你不行就只能另请高明了

1
flutter clean
1
flutter run

简单手写防抖节流

不多逼逼,直接上代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

export const debounce = (func: Function, timeout: number) => {
let timeoutList: Array<NodeJS.Timeout> = [];

return (...args: any[]) => {
let timeoutId: NodeJS.Timeout;
timeoutList.forEach(_timeoutId => clearTimeout(_timeoutId))
timeoutId = setTimeout(() => {
func(...args);
}, timeout);
timeoutList.push(timeoutId)
};
};



export const throttle = (func: Function, timeout: number) => {
let flag = true
return (...args: any[]) => {
if (flag) {
flag = false
setTimeout(() => {
func(...args);
flag = true
}, timeout);
}
};
};

百度、谷歌、搜狗、必应、360怎么提交网站收录、网址索引

为了加快网站被各大搜索引擎收录,建议新站点手动提交一下网站

百度搜索(baidu)主动提交网站索引的地址

https://ziyuan.baidu.com/site/index

其次百度也有一个手动匿名提交网站收录的入口

https://ziyuan.baidu.com/linksubmit/url

在百度的搜索资源平台-用户中心-站点管理里可以手动提交站点

咱们可以选择百度提供的如下3种验证方式进行站点的验证

  • 文件验证
  • HTML标签验证
  • CNAME验证

具体页面长这样👇,还是比较简单的

搜狗搜索(sogo)主动提交网站索引的地址

https://zhanzhang.sogou.com/index.php/dashboard/index

在搜狗的资源平台-网站支持-站点验证里可以手动提交站点

咱们可以选择搜狗提供的如下2种验证方式进行站点的验证

  • 文件验证
  • HTML标签验证

具体页面长这样👇,也还是比较简单的,不过比较操蛋的是我用HTML标签验证一直验证不成功

360搜索(so)主动提交网站索引的地址

https://info.so.360.cn/site_submit.html

直接点击链接就可以提交网址验证了,360搜索的网站收录提交是匿名的,没那么麻烦

具体页面长这样👇,超级简单了

谷歌搜索(google)主动提交网站索引的地址

https://search.google.com/search-console/not-verified

点击链接登录谷歌账号以后,点击左上角的搜索资源下拉添加自己的网站域名

咱们可以选择谷歌提供的如下1种验证方式进行站点的验证

  • DNS解析验证

具体页面长这样👇,需要自己去域名服务商那里解析一个TXT值,就是这个谷歌给出的这个一串字符

必应搜索(bing)主动提交网站索引的地址

https://www.bing.com/webmasters

咱们可以选择必应提供的如下3种验证方式进行站点的验证

  • 文件验证
  • HTML标签验证
  • CNAME验证

具体页面长这样👇,还是比较简单的

Fetch简单封装

不多逼逼,直接上代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
function request(option = {
method: 'GET',
formData: false
}) {
let url = option.url
if (option.param) {
url = `${url}?${qs(option.param)}`
}
function qs(data, flag) {
const typeObj = (obj) => Object.prototype.toString.call(obj).slice(8, -1) === 'Object'
const typeArr = (obj) => Object.prototype.toString.call(obj).slice(8, -1) === 'Array'
var str = '', originData, encodeData;
for (var i in data) {
if (data.hasOwnProperty(i)) {
originData = data[i]
if (typeObj(originData) || typeArr(originData)) {
str += qs(originData, i)
} else {
encodeData = encodeURIComponent(originData)
if (typeObj(data) && flag) {
str += `${flag}[${i}]=${encodeData}&`
} else if (typeArr(data) && flag) {
str += `${flag}[${i}]=${encodeData}&`
// str += `${flag}=${encodeData}&`
} else {
if (str.length > 0 && str[str.length - 1] !== '&') str += '&';
str += `${i}=${encodeData}&`
}
}
}
}
return str[str.length - 1] === '&' ? str.slice(0, -1) : str
}
return fetch(url, {
method: option.method,
headers: option.formData ? undefined : {
'Content-Type': 'application/json',
},
body: option.formData ? Object.keys(option.data).reduce((ret, val) => {
ret.append(val, option.data[val])
return ret
}, new FormData()) : JSON.stringify(option.data),
}).then(response => response.json())
}

用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// get请求
await request({
url: 'https://netease-cloud-music-api-dv7rufaa3-vearvip.vercel.app/search',
method: 'GET',
param: {
keywords: '海阔天空'
}
})
// post请求
// formData自行决定是否开启,默认不开启
await request({
url: 'https://netease-cloud-music-api-dv7rufaa3-vearvip.vercel.app/search',
method: 'POST',
formData: true,
data: {
keywords: '海阔天空'
}
})

Js转文件为Base64和Js下载Base64文件

非常的好用!

js转文件为base64字符串

1
2
3
4
5
6
7
8
function fileToBase64(file, callback) {
let reader = new FileReader();
reader.addEventListener('load', (e) => {
callback(e.target.result);
reader = null;
});
reader.readAsDataURL(file);
}

竟然还有童鞋不知道咋上传文件,下面封装了一下,直接调用pickerFileBase64这个函数就可以选取文件并且得到base64字符串了

用例:纯Js获取文件并转base64

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function pickerFileBase64() {
return new Promise((resolve) => {
const inputFile = document.createElement('input')
inputFile.type = 'file'
inputFile.onchange = event => {
const file = event.target.files[0]
function fileToBase64(file, callback) {
const reader = new FileReader();
reader.addEventListener('load', (e) => {
callback(e.target.result);
reader = null;
});
reader.readAsDataURL(file);
}
fileToBase64(file, resolve)
}
inputFile.click()
})
}

(async () => {
const base64 = await pickerFileBase64()
console.log(base64)
})()

下载base64字符串文件也是非常简单的,直接调用就完事了

js下载base64字符串文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function downloadBase64File(base64,name){
function dataURLtoBlob(dataurl) {
var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], { type: mime });
}

function downloadFile(url,name=new Date().toLocaleString()){
var a = document.createElement("a")
a.setAttribute("href",url)
a.setAttribute("download",name)
a.setAttribute("target","_blank")
let clickEvent = document.createEvent("MouseEvents");
clickEvent.initEvent("click", true, true);
a.dispatchEvent(clickEvent);
}
var myBlob = dataURLtoBlob(base64)
var myUrl = URL.createObjectURL(myBlob)
downloadFile(myUrl,name)
}

Unity笔记-时钟代码

学习自这个教程: https://mp.weixin.qq.com/s/QaEZuMRGTf07pml_h1rhxA

首先创造一个表盘

GameObject → 3D Object → Cylinder 创建1个圆柱体

给它加上刻度

GameObject → 3D Object → Cube 创建12个方块

再加上指针

GameObject → 3D Object → Cube 创建3个方块

最后加上脚本让他动起来

效果如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
using System;
using UnityEngine;

public class Clock : MonoBehaviour
{
const float degressPreHour = 30f;
const float degressPreMinuteSecond = 6f;
public Transform HourArmTransform;
public Transform MinusArmTransform;
public Transform SecondArmTransform;

// 每秒动一次
void makeArmRun()
{
var time = DateTime.Now;
HourArmTransform.localRotation = Quaternion.Euler(0, time.Hour * degressPreHour, 0);
MinusArmTransform.localRotation = Quaternion.Euler(0, time.Minute * degressPreMinuteSecond, 0);
SecondArmTransform.localRotation = Quaternion.Euler(0, time.Second * degressPreMinuteSecond, 0);
}
// 一直都动
void makeArmRunContinuous()
{
var time = DateTime.Now.TimeOfDay;
HourArmTransform.localRotation = Quaternion.Euler(0, (float)time.TotalHours * degressPreHour, 0);
MinusArmTransform.localRotation = Quaternion.Euler(0, (float)time.TotalMinutes * degressPreMinuteSecond, 0);
SecondArmTransform.localRotation = Quaternion.Euler(0, (float)time.TotalSeconds * degressPreMinuteSecond, 0);
}

// Update is called once per frame
void Update()
{
//makeArmRun();
makeArmRunContinuous();

}
}