1、换行符带来的签名问题
- 1、手机端换行 \n
- 2、pc换行 ↵
- 3、签名计算md5加密时 \n 签名校验失败
- 4、解决思路
- 换行符全部转化为 ↵ 或者特定字符(eg:$huanhang$) 进行签名计算
- 渲染时再转化为 \n 进行展示
- 5、需要在文本域(textarea)内使用
replaceMobileEnterToPc (str) {
return str.replace(/\n/g, "↵");
},
replacePcEnterToMobile (str) {
return str.replace(/↵/g, "\n");
}
2、图片上传,列表更新不同步问题(缓存)
- 1、窗口图片上传成功
- 2、列表数据更新成功,图片未更新
- 3、解决思路
<img :src="item.img ? item.img + '&t=' + (new Date().getTime()) : 'static/images/user/morenyonghu.png'" >
3、加载默认图片(失效、异常),img 陷入 onerror 事件死循环
- 1、设置了默认图片,但是默认图片失效,触发 onerror 事件
- 2、解决思路
- onerror 事件只触发一次 this.onerror=null
<img :src="item.img ? item.img + '&t=' + (new Date().getTime()) : 'static/images/device/default.png'" onerror="javascript:this.src='static/images/device/default.png';this.onerror=null;"
4、图片上传(原生)
- 1、手机拍照
- 2、预览
- 3、ajax 参数
- 4、解决思路
- 选择本地图片(文件)或者手机拍照存储为 [{id: 'file_' + 时间戳, file: file文件}]
- 通过 readAsDataURL(file) 方法存储 base64 格式图片作为预览缩略图
// html
<input type="file" accept="image/*" @change="uploadImgFile" ref="input" multiple />
// js
data () {
return {
fileData: [], // 存储上传原文件
imgArr: [], // 存储缩略图
previewImgs: [], // 存储预览大图
delImgArr: [], // 删除的图片id集合
delImgId: '' // 删除的图片id字符串 1,2,3,4
}
}
..........
uploadImgFile () {
let files = this.$refs.input.files;
for (let i = 0; i < files.length; i++) {
let tempObj = {};
let tempId = 'file_' + new Date().getTime() + Math.floor(Math.random() * 10000);
tempObj.id = tempId;
tempObj.file = files[i];
this.fileData.push(tempObj);
readAndPreview(files[i], tempId);
}
this.$refs.input.value = ''; // 避免上传同一个照片不触发 change 事件
console.log(this.fileData);
},
function readAndPreview (file, tempId) {
if (file !== undefined) {
if (/\.(jpe?g|png|gif)$/i.test(file.name)) {
let that = this;
let reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function (e) {
that.imgArr.push({ id: tempId, url: e.target.result }); // 缩略图集合
that.previewImgs.push(e.target.result); // 预览图集合
}
}
}
},
deleteImg (id) {
MessageBox.confirm('确定删除该照片?').then(action => {
if (/^file/.test(id)) { // 删除新增图片
let idx = this.fileData.findIndex(item => item.id === id);
this.fileData.splice(idx, 1);
} else { // 删除原有接口图片
this.delImgArr.push(id);
this.delImgId = this.delImgArr.join(',');
}
let index = this.imgArr.findIndex(item => item.id === id);
this.imgArr.splice(index, 1); // 删除缩略图
this.previewImgs.splice(index, 1); // 删除预览图
}).catch(res => {
console.log('点击了取消按钮');
});
},
// 接口参数处理
let formData = new FormData();
let param = {};
param.id = this.id;
param.describe = this.replaceMobileEnterToPc(this.describe); // 保证手机端换行符 \n 签名能够通过
if (this.delImgId !== '') param.file_id = this.delImgId;
let data = getDataAndSign(BASE, DETAIL_URI, param);
formData.append('v', data.v);
formData.append('t', data.t);
formData.append('token', data.token);
formData.append('s', data.s);
formData.append('id', data.id);
formData.append('describe', data.describe);
if (this.delImgId !== '') formData.append('file_id', this.delImgId);
// image1, image2, image3, ...... , image100
if (this.fileData.length > 0) {
for (let i = 0; i < this.fileData.length; i++) {
formData.append('image' + (i + 1), this.fileData[i].file);
}
}
// image[] 写法,(后端体现为数组)
if (this.fileData.length > 0) {
for (let i = 0; i < this.fileData.length; i++) {
formData.append('image[]', this.fileData[i].file);
}
}
// axios 请求接口(主要设置 header中Content-Type)
let options = {
method: 'post',
url: BASE + DETAIL_URI,
data: data,
headers: {
'Content-Type': 'multipart/form-data',
'Cache-Control': 'no-cache'
}
};
axios(options).then(response => {
// ...
})
5、数据导出为 Excel 格式
- 1、插件引入
- 2、网站参考
- 3、数据格式处理
- 4、最终模拟 a 标签点击事件
- 5、微信、企业微信内置浏览器屏蔽下载功能,浏览器正常
var aoa = [
['主要信息', null, null, '其它信息'], // 特别注意合并的地方后面预留2个null
['姓名', '性别', '年龄', '注册时间'],
['张三', '男', 18, new Date()],
['李四', '女', 22, new Date()]
];
var sheet = XLSX.utils.aoa_to_sheet(aoa);
sheet['!merges'] = [
// 设置A1-C1的单元格合并
{s: {r: 0, c: 0}, e: {r: 0, c: 2}}
];
openDownloadDialog(sheet2blob(sheet), '单元格合并示例.xlsx');
// 将一个sheet转成最终的excel文件的blob对象,然后利用URL.createObjectURL下载
function sheet2blob(sheet, sheetName) {
sheetName = sheetName || 'sheet1';
var workbook = {
SheetNames: [sheetName],
Sheets: {}
};
workbook.Sheets[sheetName] = sheet;
// 生成excel的配置项
var wopts = {
bookType: 'xlsx', // 要生成的文件类型
bookSST: false, // 是否生成Shared String Table,官方解释是,如果开启生成速度会下降,但在低版本IOS设备上有更好的兼容性
type: 'binary'
};
var wbout = XLSX.write(workbook, wopts);
var blob = new Blob([s2ab(wbout)], {type:"application/octet-stream"});
// 字符串转ArrayBuffer
function s2ab(s) {
var buf = new ArrayBuffer(s.length);
var view = new Uint8Array(buf);
for (var i=0; i!=s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
return buf;
}
return blob;
}
/**
* 通用的打开下载对话框方法,没有测试过具体兼容性
* @param url 下载地址,也可以是一个blob对象,必选
* @param saveName 保存文件名,可选
*/
function openDownloadDialog(url, saveName)
{
if(typeof url == 'object' && url instanceof Blob)
{
url = URL.createObjectURL(url); // 创建blob地址
}
var aLink = document.createElement('a');
aLink.href = url;
aLink.download = saveName || ''; // HTML5新增的属性,指定保存文件名,可以不要后缀,注意,file:///模式下不会生效
var event;
if(window.MouseEvent) event = new MouseEvent('click');
else
{
event = document.createEvent('MouseEvents');
event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
}
aLink.dispatchEvent(event);
}
6、导出数据为 csv 格式
// 定义必须参数并赋予测试数据
let data = [
// 真实数据需要与此测试数据格式保持一致。
{ name: "张三", url: "https://www.baidu.com" },
{ name: "zhangsan", url: "https://www.baidu.com" },
{ name: "zhangsan", url: "https://www.baidu.com" },
{ name: "zhangsan", url: "https://www.baidu.com" },
]
let header = { name: "张三", url: "路径" } // 表头
//数据处理, 这一部分可以封装为函数
data.unshift(header) // 添加表头
// 处理数据为csv的格式
let csvString = ""
data.map((item) => {
Object.keys(header).map((key) => {
let value = item[key]
csvString += value + ","
})
csvString += "\r\n"
})
// 保存为csv文件并添加下载按钮
csvString = "data:application/csv," + encodeURIComponent(csvString)
let btn = document.createElement("a")
btn.setAttribute("href", csvString)
btn.setAttribute("download", "data.csv")
btn.innerText = "下载"
// 定义一个修改元素样式的函数
function setStyle(dom, styles = {}) {
Object.keys(styles).map((key) => {
dom.style[key] = styles[key]
})
return dom
}
// 设置按钮的样式
btn = setStyle(btn, {
position: "fixed",
top: "10px",
left: "10px",
zIndex: "9999999999",
backgroundColor: "yellow", // 注意的是,样式名不能有下划线,要合并在一起改为首字母大写列如: background-color => backgroundColor
})
document.body.appendChild(btn)
7、禁止浏览器密码输入框点击显示保存的密码
- 1、场景:拥有记住账号密码功能之后,由于此前浏览器提醒记住了密码,再次点击密码框触发了浏览器管理账号密码机制,将对应账号密码列表显示出来了,要求禁止显示该列表
- 2、问题原因
- 浏览器是根据输入框的类型是password来判断你在保存密码
- 一旦登录成功之后提示你是否保存,而你点击了保存,账号密码就会保存在管理列表中
- 3、解决方案
- 核心就是不能设置type为password
- css有专门设置input文字改成密文的属性
-webkit-text-security:disc
// 指定要使用的形状来代替文字的显示
none 无。
circle 圆圈。
disc 圆形。
square 正方形。