Skip to content

Tostr

toastr.options = { // toastr配置
"closeButton": true, //给消息框加一个可以主动关闭的 X
// 自定义关闭的HTML 与上一个设置连用 可以出现效果
"closeHtml": '<button><i class="icon-off">自定义内容</i></button>'
"debug": false, //是否使用debug模式
"progressBar": true, //是否显示进度条,当为false时候不显示;当为true时候,显示进度条,当进度条缩短到0时候,消息通知弹窗消失
"positionClass": "toast-top-center",//位置
"showDuration": "400", //显示的动画时间
"hideDuration": "1000", //消失的动画时间
"timeOut": "7000", //展现时间
"extendedTimeOut": "1000", // 当用户鼠标从消息上移开之后多久之后消失
"showEasing": "swing", //显示时的动画缓冲方式
"hideEasing": "linear", //消失时的动画缓冲方式
"showMethod": "fadeIn", //显示时的动画方式
"hideMethod": "fadeOut", //消失时的动画方式
"rtl": true, // 让文字从右往左排列
"preventDuplicates": true,// 防止重复 如果消息内容重复 只会显示第一条
}
// 如果想要让html标签正常在消息提示中显示,可以设置这个
toastr.options.escapeHtml = true;
// 新的消息出现的顺序 默认新消息出现在上面 设置之后新消息出现在下面
toastr.options.newestOnTop = false;
//为了防止消息自动消失 可以将以上两个时间设置为 0 点击后自动消失
toastr.options.timeOut = 0;
toastr.options.extendedTimeOut = 0;
// 点击关闭按钮时的动画设置
toastr.options.closeMethod = 'fadeOut';
toastr.options.closeDuration = 300;
toastr.options.closeEasing = 'swing';
// 渐变效果
toastr.options.showEasing = 'swing';
toastr.options.hideEasing = 'linear';
toastr.options.closeEasing = 'linear';
// 动画方法
toastr.options.showMethod = 'slideDown';
toastr.options.hideMethod = 'slideUp';
toastr.options.closeMethod = 'slideUp';
//事件回调
toastr.options.onShown = function() { console.log('hello'); } // 消息展示的回调
toastr.options.onHidden = function() { console.log('goodbye'); }// 消息隐藏的回调
toastr.options.onclick = function() { console.log('clicked'); } // 点击消息的回调
toastr.options.onCloseClick = function() { console.log('close button clicked'); }//点击关闭按钮的回调
toastr.success('保存成功!');
//移除所有,没有动画效果
toastr.remove()
//立即移除消息提示,带动画效果
toastr.clear()

js 加载框

var opts = {
lines: 13, // line 的个数
length: 38, // line 的长度
width: 17, // 单个 line 的宽度
radius: 45, // loading 中心空白圆的大小
scale: 1, // 放大缩小
animation: 'spinner-line-shrink', // 动画样式
color: '#ffffff', // 言责
top: '50%', // Top position relative to parent
left: '50%', // Left position relative to parent
zIndex: 2000000000, // The z-index (defaults to 2e9)
position: 'absolute', // Element positioning
};
//js
var spinner = new Spin.Spinner(opts).spin(target); //show
spinner.stop() //hide
//esm
var spinner = new Spinner().spin();
spinner.stop() //hide
//复用spinner对象
targetEl.appendChild(spinner.el);

spin 添加 spin-mask,在 spin.js 中找到这段代码,并且替换

var Spinner = /** @class */ (function () {
function Spinner(opts) {
if (opts === void 0) { opts = {}; }
this.opts = __assign(__assign({}, defaults), opts);
}
/**
* Adds the spinner to the given target element. If this instance is already
* spinning, it is automatically removed from its previous target by calling
* stop() internally.
*/
Spinner.prototype.spin = function (target) {
this.stop();
let tempEl = document.createElement('div');
tempEl = document.createElement('div');
this.el = document.createElement('div');
this.el.setAttribute('class', 'spin-mask');
tempEl.className = this.opts.className;
tempEl.setAttribute('role', 'progressbar');
css(tempEl, {
position: this.opts.position,
width: 0,
zIndex: this.opts.zIndex,
left: this.opts.left,
top: this.opts.top,
transform: "scale(" + this.opts.scale + ")",
});
drawLines(tempEl, this.opts);
this.el.append(tempEl)
console.log(this.el);
if (target) {
target.insertBefore(this.el, target.firstChild || null);
}
return this;
};
/**
* Stops and removes the Spinner.
* Stopped spinners may be reused by calling spin() again.
*/
Spinner.prototype.stop = function () {
if (this.el) {
if (typeof requestAnimationFrame !== 'undefined') {
cancelAnimationFrame(this.animateId);
}
else {
clearTimeout(this.animateId);
}
if (this.el.parentNode) {
this.el.parentNode.removeChild(this.el);
}
this.el = undefined;
}
return this;
};
return Spinner;
}());

API参考 (https://ajaxorg.github.io/ace-api-docs/index.html)

尝试 (https://mkslanc.github.io/ace-playground/)

有关更详细的选项列表,请参阅Configuring-Ace wiki 页面。 (https://github.com/ajaxorg/ace/wiki/Configuring-Ace)

// pass options to ace.edit
<script src="https://www.unpkg.com/ace-builds@latest/src-noconflict/ace.js"></script>
const editor = ace.edit(element, {
mode: "ace/mode/javascript",
theme: "ace/theme/eclipse",
selectionStyle: "text",
maxLines :最大行数
})
// use setOptions method to set several options at once
editor.setOptions({
//设置自动滚动
autoScrollEditorIntoView: true,
//如果选择为空,则复制/剪切整行,默认为false
copyWithEmptySelection: true,
});
// use setOptions method
editor.setOption("mergeUndoDeltas", "always");
// some options are also available as methods e.g.
// to get the value of the option use
editor.getOption("optionName");
//获取value值
editor.getSession().getValue()
editor.getSession().setValue()
//:设置主题
editor.setTheme("ace/theme/twilight");
//语言
editor.session.setMode("ace/mode/javascript");

(https://github.com/ajaxorg/ace/blob/master/src/ext/themelist.js)在运行时获取可用主题的列表。

  • 默认情况下,编辑器支持纯文本模式。所有其他语言模式都可以作为单独的模块提供,按需加载,如下所示:

Ace 仅在调整窗口大小时检查其容器大小的变化。如果您以其他方式调整编辑器 div 的大小,并且需要 Ace 调整大小,请使用以下命

令:

editor.resize()

设置和获取内容:

editor.setValue("the new text here");
editor.setValue("text2", -1); // set value and move cursor to the start of the text
editor.session.setValue("the new text here"); // set value and reset undo history
editor.getValue(); // or session.getValue
// 获取选中文本
editor.getSelectedText();
//获取值
editor.getSession().getValue()
//获取某一个范围
editor.session.getTextRange(editor.getSelectionRange());
//获取当前光标所在行和列:
editor.selection.getCursor();
//获取总行数:
editor.session.getLength();
//在光标处插入,模拟用户输入:
editor.insert("Something cool");
editor.session.insert({row: 0, column:0}, Date()+"");
//替换范围内的文本:
editor.session.replace(new ace.Range(0, 0, 1, 1), "new text");
//转到一行:
editor.gotoLine(lineNumber);
//设置默认选项卡大小:
editor.session.setTabSize(4);
//使用软选项卡:
editor.session.setUseSoftTabs(true);
//设置字体大小:
document.getElementById('editor').style.fontSize='12px';
//切换自动换行:
editor.session.setUseWrapMode(true);
//设置行突出显示:
editor.setHighlightActiveLine(false);
//设置打印边距可见性:
editor.setShowPrintMargin(false);
//将编辑器设置为只读:
editor.setReadOnly(true); // false to make it editable
editor.find('needle',{
backwards: false,
wrap: false,
caseSensitive: false,
wholeWord: false,
regExp: false
});
editor.findNext();
editor.findPrevious();

您可以使用以下选项作为搜索参数:

  • needle:您要查找的字符串或正则表达式
  • backwards:是否从光标当前位置向后搜索。默认为false.
  • wrap:当搜索结束时是否将搜索回绕到开头。默认为false.
  • caseSensitive:搜索是否应该区分大小写。默认为false.
  • wholeWord:搜索是否仅匹配整个单词。默认为false.
  • range:要搜索的范围。null将整个文档 设置为
  • regExp:搜索是否为正则表达式。默认为false.
  • start:开始搜索的 起始范围或光标位置
  • skipCurrent:是否在搜索中包含当前行。默认为false.
  • preventScroll:是否将光标移动到下一个匹配项。默认为false.

以下是执行替换的方法:

editor.find('foo');
editor.replace('bar');

这是全部替换:

editor.replaceAll('bar');

监听onchange

editor.session.on('change', function(delta) {
// delta.start, delta.end, delta.lines, delta.action
});

监听变化selection

editor.session.selection.on('changeSelection', function(e) {
});

监听变化cursor

editor.session.selection.on('changeCursor', function(e) {
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body {
background-color: #F2F5FA;
}
.containerCanvas {
margin: auto;
}
.file-upload {
display: none;
}
nav {
margin: auto;
display: flex;
width: 540px;
justify-content: space-between;
margin-bottom: 10px;
}
button {
background-color: #1D84FD;
color: aliceblue;
padding: 8px 12px;
border: none;
border-radius: 150px;
}
</style>
</head>
<body>
<input id="fileupload" class="file-upload" type="file" accept="image/*" name="picture"/>
<nav>
<button onclick="fileupload.click()">选择图片</button>
<button class="downBtn">下载图片</button>
</nav>
<canvas height="500" width="540" id="canvasBox"></canvas>
</body>
<script src="https://unpkg.com/fabric@4.6.0/dist/fabric.min.js"></script>
<script src="../js/index.js"></script>
<script>
fabric.Object.prototype.controls.mtr.withConnection = false;
// 修改控制点的形状,默认为`rect`矩形,可选的值还有`circle`圆形
// fabric.Object.prototype.cornerStyle = "circle";
// // 修改控制点的填充色为白色
// fabric.Object.prototype.cornerColor = "white";
// // 修改控制点的大小为10px
// fabric.Object.prototype.cornerSize = 10;
// // 设置控制点不透明,即可以盖住其下的控制线
// fabric.Object.prototype.transparentCorners = false;
// // 修改控制点的边框颜色为`gray`灰色
// fabric.Object.prototype.cornerStrokeColor = "gray";
// // 单独修改旋转控制点距离主体的纵向距离为-20px
// fabric.Object.prototype.controls.mtr.offsetY = -20;
// fabric.Object.prototype.controls.mtr.cornerSize = 0;
// // 单独修改旋转控制点,光标移动到该点上时的样式为`pointer`,一个手的形状
// fabric.Object.prototype.controls.mtr.cursorStyle = "pointer";
var canvas = new fabric.Canvas('canvasBox', {
backgroundColor: '#FFFFFF',
selectionLineWidth: 2,
containerClass: 'containerCanvas'
});
canvas.setBackgroundImage(
'../assets/image/bgc.png',
canvas.renderAll.bind(canvas), {

这个可以直接预览excel

https://view.officeapps.live.com/op/view.aspx?src=https://www.okkrep.com/quotation_1763540177337%20-%20%E5%89%AF%E6%9C%AC%20(2).xlsx
/* #ifdef APP-PLUS */
console.log('App Launch');
// 1. 检查本地存储中是否有 'appLaunched' 标记
const isFirstLaunch = uni.getStorageSync(APP_LAUNCHED);
if (!isFirstLaunch) {
// 2. 如果没有标记,说明是首次启动
console.log('这是应用首次启动');
// 跳转重置页重新配置站点
uni.navigateTo({
url: `/pages/reset/reset`
});
} else {
// 应用不是首次启动
uni.setTabBarItem({
index: 2,
visible: false
})
}
/* #endif */
/* #ifdef H5 */
uni.setTabBarItem({
index: 2,
visible: false
})
/* #endif */

提取图片

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<input type="file" id="fileInput">
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jszip@3.10.1/dist/jszip.min.js"></script>
<script>
document.getElementById('fileInput').addEventListener('change', function(event) {
/**
* @date: 2025-11-25
* @description: 这里的zip包一定是全局的,后面操作都要基于这个zip实例
*/
const zip = new JSZip();
const file = event.target.files[0];
const asyncList = []
extractImagesFromExcel(zip, file, async (map, content) => {
Object.keys(content.files).forEach((fileName) => {
// 拿到图片文件
if (fileName.indexOf('xl/media/') === -1 ) return
if (content.files[fileName].dir === true) return;
asyncList.push(new Promise(async resolve => {
const base64 = await zip.file(content.files[fileName].name).async('base64')
resolve({fileName, base64})
}))
})
const base64List = await Promise.all(asyncList);
// 将base64格式化一下让浏览器读取
const formatImageList = await Promise.all(base64List.map(({fileName, base64}) => {
return new Promise(async resolve => {
let imgType = detectImageType(base64);
let base64Img = await compressBase64(`data:image/${imgType};base64,${base64}`, imgType);
const idx = fileName.indexOf('.')
resolve({fileName: fileName.slice(0, idx), base64Img})
})
}));
// 根据文件名字生成map
const imageMap = formatImageList.reduce((pre, val) => {
pre[val.fileName] = val.base64Img
return pre
}, {})
Object.keys(map).forEach(key => {
const imageKey = key.split(',')[2]
// 这里拿到生成好的map添加文件名对应id,进行赋值,value是图片base64
if (imageMap[imageKey]) {
map[key] = imageMap[imageKey]
const divEl = document.createElement('div');
const imgEl = document.createElement('img');
imgEl.style.width = '100px'
imgEl.style.height = '100px'
imgEl.style.border = '1px solid red'
divEl.append(imgEl)
imgEl.src = imageMap[imageKey];
document.body.append(divEl)
}
})
console.log(map);
})
});
function extractImagesFromExcel(zip, file, callback) {
/**
* @date: 2025-11-25
* @description: 同步读取文件
*/
zip.loadAsync(file).then(async (content) => {
const map = {}
const asyncList = []
// 找到 drawings 文件,里面包含图片和文件对应关系
Object.keys(content.files).forEach(async (fileName) => {
if (fileName.indexOf('xl/drawings/') === -1 ) return;
//去掉文件目录
if (content.files[fileName].dir === true) return;
asyncList.push(zip.file(fileName).async("text"))
});
const xmlList = await Promise.all(asyncList)
xmlList.forEach(xmlContent => {
/**
* @date: 2025-11-25
* @description: 将读取的文本解析xml文件
*/
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(xmlContent, "application/xml");
console.log(xmlDoc);
// 这里你可以根据 XML 结构解析出图片的位置
// 例如,通过查找 <xdr:pic> 标签获取图像位置信息
const from = xmlDoc.getElementsByTagName("xdr:from");
for (let f of from) {
// 提取位置和大小等信息
// 你需要根据实际 XML 结构做相应的调整
const col = f.getElementsByTagName("xdr:col")[0].textContent;
const row = f.getElementsByTagName("xdr:row")[0].textContent;
const picEl = f.parentElement.getElementsByTagName("xdr:pic")[0]
// 获取图片id
const rid = picEl.getElementsByTagName("a:blip")[0].getAttribute('r:embed');
// 整理出来包含位置和图片id的key,用于后续替换,value后续保存图片base64
map[`${parseInt(row)+1},${parseInt(col)+1},xl/media/image${rid.slice(3)}`] = ''
}
})
callback(map, content)
});
}
/**
* 图片格式检测器(支持PNG/JPEG)
* @param {string} base64 - 包含DataURL前缀的Base64字符串
* @returns {'png' | 'jpeg'}
*/
function detectImageType(base64) {
// 性能优化:仅解码前8个Base64字符(对应6字节)
const dataPart =
base64.indexOf(',') >= 0
? base64.slice(base64.indexOf(',') + 1, base64.indexOf(',') + 9)
: base64.slice(0, 8);
try {
// 优化点:避免使用atob产生临时字符串
const byteArray = Uint8Array.from(atob(dataPart), (c) => c.charCodeAt(0));
// PNG检测:前8字节特征值
if (
byteArray.length >= 8 &&
byteArray === 0x89 &&
byteArray === 0x50 &&
byteArray === 0x4e &&
byteArray === 0x47
) {
return 'png';
}
// JPEG检测:前3字节特征值
if (byteArray.length >= 3 && byteArray === 0xff && byteArray === 0xd8 && byteArray === 0xff) {
return 'jpeg';
}
} catch (e) {
console.warn('Base64解码失败:', e);
}
return 'png';
}
/**
* @date: 2025-11-25
* @description: 获取图片大小
*/
function getBase64ImageSize(base64) {
// 移除URL中的前缀和数据后缀
const dataURI = base64.replace(/^data:image\/\w+;base64,/, '');
// 计算字节大小
const byteSize = dataURI.length * 0.75; // base64编码是3/4原始大小
return byteSize;
}
/**
* @date: 2025-11-25
* @description: 压缩zip
*/
function compressBase64(
base64Image,
imgType = 'jpeg',
maxWidth = 4096,
maxHeight = 4096,
quality = 0.8,
) {
return new Promise((resolve, reject) => {
/* if (props.compressImg == false) {
resolve(base64Image);
return;
}*/
const sizeInMB = getBase64ImageSize(base64Image) / (1024 * 1024); // 将字节转换为MB
if (sizeInMB <= 10) {
resolve(base64Image);
return;
}
console.log('压缩前图片大小:' + sizeInMB + 'm');
const img = new Image();
img.src = base64Image;
// 等待图片加载完成
img.onload = () => {
// 创建 canvas 元素
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// 获取原始图片的宽高
const width = img.width;
const height = img.height;
// 计算新的宽高,保持宽高比
let newWidth = width;
let newHeight = height;
// 如果图片宽度超过最大宽度,进行缩放
if (width > maxWidth) {
newWidth = maxWidth;
newHeight = (maxWidth / width) * height;
}
// 如果图片高度超过最大高度,进行缩放
if (newHeight > maxHeight) {
newHeight = maxHeight;
newWidth = (maxHeight / height) * width;
}
// 设置 canvas 的尺寸
canvas.width = newWidth;
canvas.height = newHeight;
// 将图片绘制到 canvas 上
ctx.drawImage(img, 0, 0, newWidth, newHeight);
// 将 canvas 转换为 base64 格式,指定质量
const compressedBase64 = canvas.toDataURL('image/' + imgType, quality);
console.log('压缩后图片大小:' + getBase64ImageSize(compressedBase64) / (1024 * 1024) + 'm');
// 返回压缩后的 base64 字符串
resolve(compressedBase64);
};
// 处理图片加载错误
img.onerror = (err) => {
reject('图片加载失败: ' + err);
};
});
}
</script>
</body>
</html>