目录
通过window.sessionStorage.getItem获取的值,一定要 通过JSON.parse()转成对象
前端实现文件下载功能
原文文章:前端实现文件下载功能
1.最简单的,有文件的地址,直接使用a标签实现下载:
<a href="/user/test/xxxx.txt" download="文件名.txt">点击下载</a>
但是有个情况,比如txt,png,jpg等这些浏览器支持直接打开的文件是不会执行下载任务的,而是会直接打开文件,这个时候就需要给a标签添加一个属性“download”;
2. window.open()方法,后端提供下载接口:
html:
<button type="button" id="btn1">window.open()方法下载</button>
js:
var $eleBtn1 = $("#btn1");
//已知一个下载文件的后端接口:
https://codeload.github.com/douban/douban-client/legacy.zip/master
//方法一:window.open()
$eleBtn1.click(function(){
var url = "https://codeload.github.com/douban/douban-client/legacy.zip/master";
window.open(url);
});
3.通过form表单提交的方式:
html:
<button type="button" id="btn2">form方法下载</button>
js:
var $eleBtn2 = $("#btn2");
//已知一个下载文件的后端接口:
https://codeload.github.com/douban/douban-client/legacy.zip/master
$eleBtn2.click(function(){
var $eleForm = $("<form method='get'></form>");
$eleForm.attr("action","https://codeload.github.com/douban/douban-client/legacy.zip/master");
$(document.body).append($eleForm);
//提交表单,实现下载
$eleForm.submit();
});
还有几个参考文章:
我在项目中使用的是第一种方法,顺便把代码贴出来(表格的点击事件):
handleDownload (index,row) { // row为当前行的数据
let cV = row.cV;
let applyUrl = this.$sourcePrefix + cV; //this.$sourcePrefix为资源前缀
if(cV!='' && cV !=undefined) {
let url = applyUrl;
let link = document.createElement('a');
link.style.display = 'none';
link.href = url;
link.setAttribute('download', cV);
link.setAttribute('target', "_blank");
document.body.appendChild(link);
link.click();
} else {
}
}
前端实现图片上传功能
直接上代码:最好是把组件的props、data、computed、watch、mounted、methods来看参数、函数的作用,这样更容易明白
- 头像上传:
<template>
<div class="upload-avatar-component">
<div
class="preview"
style="height:100%"
>
<div>
<img :src="fileSrc" alt="UploadImg">
</div>
</div>
<div class="button-upload">
<el-upload
class="upload-img"
:action="theUrl"
:before-upload="BeforeUpload"
ref="upload"
:show-file-list="false"
>编辑</el-upload>
</div>
</div>
</template>
<script>
import * as connector from '@api/interfaceList'
import { post } from '@api/api.request'
export default {
name: "uploadImg",
model: {
prop: "uploadedFile",
event: "updateFile"
},
data() {
return {
imgFile: null,
fileSrc: "",
errorNotify: {
ImgFormatError: null,
ImgExceededSize: null,
ImgDimensionError: null,
ImgDimensionRangeError: null
},
iconPrefix: ""
};
},
props: {
/*validateEvent: {
type: Boolean,
default: true
},*/
/* showPreview:{
type:Boolean,
default:true
}, */
uploadedFile: {},
disabled: {
type: Boolean,
default: false
},
defaultImg: {
//自定义默认未上传时显示图片
default: require("@images/home/default_avatar.png")
},
itemName: {
//上传图片对应表单项名称
type: String,
required: true
},
/*itemProp:{//接收父组件传来的表单prop值,用于在父组件中应用该组件多次时,进行各子实例的区分
},*/
imgDisplayWidth: {
//图片上传后的预览宽度
type: Number,
default: 74
},
/*defaultImgUrl:{
type:String,
required: true
},*/
imgDisplayHeight: {
//图片上传后的预览高度
type: Number,
default: 75
},
itemMessage: {
//表单项文字信息
type: Object,
default: function() {
return {};
}
/*default:function () {
return {
instruction:'请提交有效期内的教师资格证照片,需确保头像和文字清晰',//String/html标记语言(换行用<br/>) ;showLimitRules为false时,不受影响,显示
extraDescribe:['亦可提交高等学历证,专业证书等其他资格证明']//Array 上传要求的额外描述,【showLimitRules为false时不显示】
}
}*/
},
itemRule: {
/*限制条件*/
type: Object,
/*required: true,*/
validator: function(value) {
// 宽度范围与高度范围中的最大最小的数值需符合逻辑
if (
value.widthRange &&
value.widthRange.minWidth &&
value.widthRange.maxWidth
) {
return value.widthRange.minWidth < value.widthRange.maxWidth;
} else if (
value.heightRange &&
value.heightRange.minHeight &&
value.heightRange.maxHeight
) {
return value.heightRange.minHeight < value.heightRange.maxHeight;
} else {
return true;
}
},
default: function() {
return {
format: ["jpg", "jpeg", "png"], //Array 文件格式
maxSize: 1024, //Number[kb] 文件大小
width: undefined, //Number[px] 图片宽度
height: undefined, //Number[px] 图片高度
widthRange: {
minWidth: undefined, //Number[px] 图片最小宽度
maxWidth: undefined //Number[px] 图片最大宽度
},
heightRange: {
minHeight: undefined, //Number[px] 图片最小高度
maxHeight: undefined //Number[px] 图片最大高度
}
};
}
},
showLimitRules: {
//是否展示根据限制条件自动生成的规则提示语qualification
type: Boolean,
default: true
},
uploadUrl: {
type: String
}
},
watch: {
/* 监听上传之后返回的值 */
uploadedFile: function(newVal) {
if (!newVal) {
this.fileSrc = this.defaultImg;
this.dispatch("ElFormItem", "el.form.change", [""]);
return false;
}
if (newVal.indexOf(this.iconPrefix) > 0) {
this.fileSrc = newVal;
} else {
this.fileSrc = this.iconPrefix + "/" + newVal;
}
},
// 监听iconPrefix变化
iconPrefix_C (val) {
if(val) {
/* 判断上传之后返回的值是否带有前缀 */
let urlPattern = /[a-zA-z]+:\/\/[^\s]*/;
let uploadedFile = String(this.uploadedFile || "");
if (!uploadedFile) {
this.fileSrc = this.defaultImg;
} else if (urlPattern.test(uploadedFile)) {
this.fileSrc = this.uploadedFile;
} else {
this.fileSrc = this.iconPrefix +"/" + this.uploadedFile;
}
} else {
this.getsourcePrefix();
}
}
},
mounted: function() {
/*this.$notify.config({
top: 130,
duration: 0
});*/
/* 获取资源前缀 */
this.getsourcePrefix();
},
computed: {
/*validateState() {
return this.elFormItem ? this.elFormItem.validateState : '';
},*/
iconPrefix_C () {
return this.iconPrefix;
},
/* 设置上传图片的地址 */
theUrl: function() {
if (!this.uploadUrl || this.uploadUrl === "") {
return this.$uploadUrl; //todo:默认的上传地址(注册于Vue.prototype)
} else {
return this.uploadUrl;
}
},
/* 规则提示语的 */
qualification: function() {
let qualification = [];
if (this.itemRule) {
if (this.itemRule.maxSize) {
let maxSize =
this.itemRule.maxSize < 1024
? this.itemRule.maxSize + "Kb"
: this.itemRule.maxSize / 1024 + "Mb";
let sizeLimit = "图片大小不超过" + maxSize;
qualification.push(sizeLimit);
}
if (this.itemRule.format) {
let formatLimit = "支持";
for (let i = 0; i < this.itemRule.format.length; i++) {
if (i !== this.itemRule.format.length - 1) {
formatLimit = formatLimit + this.itemRule.format[i] + "/";
} else {
formatLimit = formatLimit + this.itemRule.format[i];
}
}
formatLimit = formatLimit + "格式的图片";
qualification.push(formatLimit);
}
if (this.itemRule.width || this.itemRule.height) {
let widthLimit = this.itemRule.width
? this.itemRule.width + "px(像素)"
: "不限";
let heightLimit = this.itemRule.height
? this.itemRule.height + "px(像素)"
: "不限";
let dimensionLimit =
"图片尺寸为" + "宽:" + widthLimit + ";高:" + heightLimit;
qualification.push(dimensionLimit);
}
/*if(!this.itemRule.width && !this.itemRule.height){//只有穿参中没有宽或高固定限制,才进行宽度或高度范围的条件校验
//此处为自动生成的宽度、高度范围校验的描述文字【现在不想写】
}*/
}
if (this.itemMessage.extraDescribe) {
qualification = qualification.concat(this.itemMessage.extraDescribe);
}
return qualification;
}
/* // 设置请求头
headers() {
return {
// 设置Content-Type类型为multipart/form-data
'ContentType': 'multipart/form-data'
}
} */
},
methods: {
/* 获取配置的地址 */
getsourcePrefix() {
this.$http.hosCommon.fetchConfig().then(data => {
this.iconPrefix = "http://" + data.qiniu;
});
},
/* 警告 */
notifyWarning(title, message, duration) {
let that = this;
setTimeout(() => {
that.$notify.warning({
/*this.errorNotify['ImgFormatError'] = */
title: title,
message: message,
duration: duration
});
}, 20); //elementUI组件bug(that.$notify的触发时间接近的提示窗叠在一起bug),需要添加20ms的延时触发(0,1的延时触发均不能解决)
},
/* 上传之前的验证操作 */
BeforeUpload(file) {
/*this.closeError();*/
if (!this.itemRule) {
this.customUpload(file);
return false;
}
let validateResult = this.validate(file);
/*alert(validateResult);*/
/* 在客户端读取文件 */
let reader = new FileReader();
let that = this;
//这是一个回调函数,需要设置reader.readAsDataURL(file),并在该函数执行结束后才会触发这个回调
reader.onload = function(e) {
let data = e.target.result;
/*that.fileSrc = data;//本地加载展示图片*/
//加载图片获取图片真实宽度和高度用于校验
let image = new Image();
/* 这是个回调,在图像加载完毕后调用 */
image.onload = function() {
/*宽高范围校验*/
if (!that.itemRule.width && !that.itemRule.height) {
let widthRangeInconform = false;
let heightRangeInconform = false;
if (that.itemRule.widthRange) {
widthRangeInconform = that.itemRule.widthRange.maxWidth
? image.width > that.itemRule.widthRange.maxWidth
: false;
if (!widthRangeInconform) {
widthRangeInconform = that.itemRule.widthRange.minWidth
? image.width < that.itemRule.widthRange.minWidth
: false;
}
} else if (that.itemRule.heightRange) {
heightRangeInconform = that.itemRule.heightRange.maxHeight
? image.height > that.itemRule.heightRange.maxHeight
: false;
if (!heightRangeInconform) {
heightRangeInconform = that.itemRule.heightRange.minHeight
? image.height < that.itemRule.heightRange.minHeight
: false;
}
} else {
}
if (widthRangeInconform || heightRangeInconform) {
that.notifyWarning(
that.itemName + "图片长宽尺寸不符合要求!",
"请重新上传符合宽、高规定的图片",
5000
);
file = null;
that.imgFile = null;
that.noticeParent(undefined);
//清除展示
that.fileSrc = that.defaultImg;
} else if (validateResult) {
//所有校验均通过
that.imgFile = file;
that.customUpload(file);
/*console.log('进入了手动上传')*/
} else {
//宽高校验通过,但格式或大小校验不通过
file = null;
this.imgFile = null;
that.noticeParent(undefined);
//清除展示
that.fileSrc = that.defaultImg;
}
} else {
/*固定宽/高校验(校验通过为false)*/
let widthInconform = that.itemRule.width
? image.width !== that.itemRule.width
: false;
let heightInconform = that.itemRule.height
? image.height !== that.itemRule.height
: false;
if (widthInconform || heightInconform) {
//宽高校验未通过
let widthLimit = that.itemRule.width
? that.itemRule.width + "px"
: "不限";
let heightLimit = that.itemRule.height
? that.itemRule.height + "px"
: "不限";
that.notifyWarning(
that.itemName + "图片长宽尺寸不符合要求!",
"请重新上传:宽度为 " +
widthLimit +
"*" +
heightLimit +
"的图片",
5000
);
file = null;
that.imgFile = null;
that.noticeParent(undefined);
//清除展示
that.fileSrc = that.defaultImg;
} else if (validateResult) {
//所有校验均通过
that.imgFile = file;
/*that.$refs['upload'].post(file);//采用手动上传*/
that.customUpload(file);
console.log("进入了手动上传");
} else {
//宽高校验通过,但格式或大小校验不通过
file = null;
this.imgFile = null;
that.noticeParent(undefined);
//清除展示
that.fileSrc = that.defaultImg;
}
}
};
image.src = data;
};
// 以DataURL的形式读取文件:
reader.readAsDataURL(file);
return false; //采用手动上传
},
/* 校验格式与文件大小 */
validate(file) {
let result = true; //校验通过旗标
if (this.itemRule) {
if (this.itemRule.format.length) {
const _file_format = file.name
.split(".")
.pop()
.toLocaleLowerCase();
const checked = this.itemRule.format.some(
item => item.toLocaleLowerCase() === _file_format
);
if (!checked) {
this.handleFormatError();
result = false;
}
}
// check maxSize
if (this.itemRule.maxSize) {
if (file.size > this.itemRule.maxSize * 1024) {
this.handleExceedSize();
result = false;
}
}
}
return result;
},
/* 上传格式报错的函数处理 */
handleFormatError() {
this.notifyWarning(
this.itemName + "图片格式错误!",
"请重新上传" + this.itemRule.format + "格式的图片",
5000
);
},
/* 上传图片大小报错的函数处理 */
handleExceedSize() {
let maxSize =
this.itemRule.maxSize < 1024
? this.itemRule.maxSize + "Kb"
: this.itemRule.maxSize / 1024 + "Mb";
this.notifyWarning(
this.itemName + "图片体积过大!",
"请重新上传不超过" + maxSize + "的图片",
5000
);
},
/* 手动上传的函数处理 */
customUpload(file) {
this.progressObj = {
status: "text",
progressPercentage: 0
};
let that = this;
let onUploadProgress = function(progressEvent) {
let progress = Number(
((progressEvent.loaded / progressEvent.total) * 100).toFixed(2)
);
that.progressObj.progressPercentage =
progress >= 99.99 ? 99.99 : progress;
};
let reqConfig = {
headers: {
fileType: "file",
"Content-Type": "multipart/form-data"
},
onUploadProgress: onUploadProgress
};
let rawFile = new FormData();
rawFile.append("a", file);
this.$http.hosCommon.putFile(this.theUrl, rawFile, reqConfig.headers).then(data => {
this.noticeParent(data[0]);
this.fileSrc = "http://w.y7edu.com/" + data[0];
/* console.log(this.fileSrc); */
setTimeout(() => {
that.progressObj = {
status: "success",
progressPercentage: 100
};
}, 150);
})
.catch(error => {
that.progressObj.status = "exception";
});
},
/* 同步父、子组件上传文件状态【null/Object[file]】 */
noticeParent(msg) {
/*let notice = {itemProp:this.itemProp,uploadedImg:msg};*/
this.$emit("updateFile", msg);
// this.dispatch("ElFormItem", "el.form.change", [msg]);
},
/* 使用了elFormItem组件作为父组件,该图片上传组件的v-model绑定的数据更新时,通知elFormItem组件 */
dispatch(componentName, eventName, params) {
let parent = this.$parent || this.$root;
let name = parent.$options.componentName;
while (parent && (!name || name !== componentName)) {
parent = parent.$parent;
if (parent) {
name = parent.$options.componentName;
}
}
if (parent) {
parent.$emit.apply(parent, [eventName].concat(params));
}
}
}
};
</script>
<style lang="less">
.upload-avatar-component {
height: 110px;
position: relative;
display: inline-block;
.el-upload {
width: 100%;
}
.button-upload {
position: absolute;
top: 0;
left: 0;
height: 110px;
line-height: 110px;
width: 100%;
.upload-img {
display: none;
border-radius: 50%;
overflow: hidden;
.el-upload--text {
height: 40px;
line-height: 40px;
border-radius: 5px;
text-align: center;
font-size: 18px;
color: #00b8c4;
background-size: 100% 100%;
background-position: center;
&:hover{
opacity: 0.85;
}
}
}
&:hover {
.upload-img {
display: block;
overflow: hidden;
background-color: #333;
opacity: 0.85;
border-radius: 50%;
}
}
}
}
.preview {
height: 110px;
width: 100%;
line-height: 110px;
text-align: center;
img {
height: 110px;
width: 110px;
border-radius: 50%
}
}
</style>
使用:
<avatar
:showPreview="false"
:imgDisplayWidth="uploadConfig.previewWidth"
:imgDisplayHeight="uploadConfig.previewHeight"
:itemRule="uploadConfig.imgRequirement"
v-model="userInfo.avatarUrl"
:itemName="uploadConfig.label"
@updateFile="updateFile"
></avatar>
配置(data中定义):
uploadConfig: {
previewWidth: 110,
previewHeight: 110,
label: "",
itemMessage: {
instruction: "请提交你的头像", //String/html标记语言(换行用<br/>) 表单上传提示语/提示语+上传文件规则
extraDescribe: []
},
imgRequirement: {
format: ["jpg", "jpeg", "png"]
/* width:1920,//Number[px] 图片宽度
height:768,//Number[px] 图片高度
maxSize:4096,//Number[kb] 文件大小 */
}
}
方法:
// 上传头像的事件
updateFile (msg) {
window.sessionStorage.setItem("avatar", msg);
let params = {
'icon': msg
};
this.$http.hosCommon.editUserInfo(params).then(data => {
this.$message({
message: '编辑用户头像成功',
type: 'success'
});
this.$store.commit("SET_AVATAR", msg);
})
}
注意:this.$http.hosCommon.editUserInfo是我封装的方法
- 图片上传:
<template>
<div class="upload-img-component">
<!-- <div
class="preview"
:style="{width:imgDisplayWidth+'px',height:imgDisplayHeight + 'px'}"
style="height:100%"
>
<div>
<img :src="fileSrc" alt="UploadImg">
</div>
</div> -->
<div class="button-upload">
<el-upload
class="upload-img"
v-if="fileSrc === defaultImg"
:action="theUrl"
:before-upload="BeforeUpload"
ref="upload"
:show-file-list="false"
>点击上传</el-upload>
<el-upload
class="uploaded-img"
v-if="fileSrc != defaultImg"
:action="theUrl"
:before-upload="BeforeUpload"
ref="upload"
:show-file-list="false"
>重新上传</el-upload>
</div>
</div>
</template>
<script>
import * as connector from '@api/interfaceList'
import { post } from '@api/api.request'
export default {
name: "uploadImg",
model: {
prop: "uploadedFile",
event: "updateFile"
},
data() {
return {
imgFile: null,
fileSrc: "",
errorNotify: {
ImgFormatError: null,
ImgExceededSize: null,
ImgDimensionError: null,
ImgDimensionRangeError: null
},
iconPrefix: ""
};
},
props: {
/*validateEvent: {
type: Boolean,
default: true
},*/
/* showPreview:{
type:Boolean,
default:true
}, */
uploadedFile: {},
disabled: {
type: Boolean,
default: false
},
defaultImg: {
//自定义默认未上传时显示图片
default: require("@images/login/bg_zyzs.png")
},
itemName: {
//上传图片对应表单项名称
type: String,
required: true
},
/*itemProp:{//接收父组件传来的表单prop值,用于在父组件中应用该组件多次时,进行各子实例的区分
},*/
imgDisplayWidth: {
//图片上传后的预览宽度
type: Number,
default: 74
},
/*defaultImgUrl:{
type:String,
required: true
},*/
imgDisplayHeight: {
//图片上传后的预览高度
type: Number,
default: 75
},
itemMessage: {
//表单项文字信息
type: Object,
default: function() {
return {};
}
/*default:function () {
return {
instruction:'请提交有效期内的教师资格证照片,需确保头像和文字清晰',//String/html标记语言(换行用<br/>) ;showLimitRules为false时,不受影响,显示
extraDescribe:['亦可提交高等学历证,专业证书等其他资格证明']//Array 上传要求的额外描述,【showLimitRules为false时不显示】
}
}*/
},
itemRule: {
/*限制条件*/
type: Object,
/*required: true,*/
validator: function(value) {
// 宽度范围与高度范围中的最大最小的数值需符合逻辑
if (
value.widthRange &&
value.widthRange.minWidth &&
value.widthRange.maxWidth
) {
return value.widthRange.minWidth < value.widthRange.maxWidth;
} else if (
value.heightRange &&
value.heightRange.minHeight &&
value.heightRange.maxHeight
) {
return value.heightRange.minHeight < value.heightRange.maxHeight;
} else {
return true;
}
},
default: function() {
return {
format: ["jpg", "jpeg", "png"], //Array 文件格式
maxSize: 1024, //Number[kb] 文件大小
width: undefined, //Number[px] 图片宽度
height: undefined, //Number[px] 图片高度
widthRange: {
minWidth: undefined, //Number[px] 图片最小宽度
maxWidth: undefined //Number[px] 图片最大宽度
},
heightRange: {
minHeight: undefined, //Number[px] 图片最小高度
maxHeight: undefined //Number[px] 图片最大高度
}
};
}
},
showLimitRules: {
//是否展示根据限制条件自动生成的规则提示语qualification
type: Boolean,
default: true
},
uploadUrl: {
type: String
}
},
watch: {
/* 监听上传之后返回的值 */
uploadedFile: function(newVal) {
console.log(newVal)
if (!newVal) {
this.fileSrc = this.defaultImg;
this.dispatch("ElFormItem", "el.form.change", [""]);
return false;
}
if (newVal.indexOf(this.iconPrefix) > 0) {
this.fileSrc = newVal;
} else {
this.fileSrc = this.iconPrefix + "/" + newVal;
}
}
},
mounted: function() {
/*this.$notify.config({
top: 130,
duration: 0
});*/
/* 获取资源前缀 */
this.getsourcePrefix();
/* 判断上传之后返回的值是否带有前缀 */
let urlPattern = /[a-zA-z]+:\/\/[^\s]*/;
let uploadedFile = String(this.uploadedFile || "");
if (!uploadedFile) {
this.fileSrc = this.defaultImg;
} else if (urlPattern.test(uploadedFile)) {
this.fileSrc = this.uploadedFile;
} else {
this.fileSrc = this.iconPrefix + this.uploadedFile;
}
},
computed: {
/*validateState() {
return this.elFormItem ? this.elFormItem.validateState : '';
},*/
/* 设置上传图片的地址 */
theUrl: function() {
if (!this.uploadUrl || this.uploadUrl === "") {
return this.$uploadUrl; //todo:默认的上传地址(注册于Vue.prototype)
} else {
return this.uploadUrl;
}
},
/* 规则提示语的 */
qualification: function() {
let qualification = [];
if (this.itemRule) {
if (this.itemRule.maxSize) {
let maxSize =
this.itemRule.maxSize < 1024
? this.itemRule.maxSize + "Kb"
: this.itemRule.maxSize / 1024 + "Mb";
let sizeLimit = "图片大小不超过" + maxSize;
qualification.push(sizeLimit);
}
if (this.itemRule.format) {
let formatLimit = "支持";
for (let i = 0; i < this.itemRule.format.length; i++) {
if (i !== this.itemRule.format.length - 1) {
formatLimit = formatLimit + this.itemRule.format[i] + "/";
} else {
formatLimit = formatLimit + this.itemRule.format[i];
}
}
formatLimit = formatLimit + "格式的图片";
qualification.push(formatLimit);
}
if (this.itemRule.width || this.itemRule.height) {
let widthLimit = this.itemRule.width
? this.itemRule.width + "px(像素)"
: "不限";
let heightLimit = this.itemRule.height
? this.itemRule.height + "px(像素)"
: "不限";
let dimensionLimit =
"图片尺寸为" + "宽:" + widthLimit + ";高:" + heightLimit;
qualification.push(dimensionLimit);
}
/*if(!this.itemRule.width && !this.itemRule.height){//只有穿参中没有宽或高固定限制,才进行宽度或高度范围的条件校验
//此处为自动生成的宽度、高度范围校验的描述文字【现在不想写】
}*/
}
if (this.itemMessage.extraDescribe) {
qualification = qualification.concat(this.itemMessage.extraDescribe);
}
return qualification;
}
/* // 设置请求头
headers() {
return {
// 设置Content-Type类型为multipart/form-data
'ContentType': 'multipart/form-data'
}
} */
},
methods: {
/* 获取配置的地址 */
getsourcePrefix() {
this.$http.hosCommon.fetchConfig().then(data => {
this.iconPrefix = "http://" + data.qiniu;
});
},
/* 警告 */
notifyWarning(title, message, duration) {
let that = this;
setTimeout(() => {
that.$notify.warning({
/*this.errorNotify['ImgFormatError'] = */
title: title,
message: message,
duration: duration
});
}, 20); //elementUI组件bug(that.$notify的触发时间接近的提示窗叠在一起bug),需要添加20ms的延时触发(0,1的延时触发均不能解决)
},
/* 上传之前的验证操作 */
BeforeUpload(file) {
/*this.closeError();*/
if (!this.itemRule) {
this.customUpload(file);
return false;
}
let validateResult = this.validate(file);
/*alert(validateResult);*/
/* 在客户端读取文件 */
let reader = new FileReader();
let that = this;
//这是一个回调函数,需要设置reader.readAsDataURL(file),并在该函数执行结束后才会触发这个回调
reader.onload = function(e) {
let data = e.target.result;
/*that.fileSrc = data;//本地加载展示图片*/
//加载图片获取图片真实宽度和高度用于校验
let image = new Image();
/* 这是个回调,在图像加载完毕后调用 */
image.onload = function() {
/*宽高范围校验*/
if (!that.itemRule.width && !that.itemRule.height) {
let widthRangeInconform = false;
let heightRangeInconform = false;
if (that.itemRule.widthRange) {
widthRangeInconform = that.itemRule.widthRange.maxWidth
? image.width > that.itemRule.widthRange.maxWidth
: false;
if (!widthRangeInconform) {
widthRangeInconform = that.itemRule.widthRange.minWidth
? image.width < that.itemRule.widthRange.minWidth
: false;
}
} else if (that.itemRule.heightRange) {
heightRangeInconform = that.itemRule.heightRange.maxHeight
? image.height > that.itemRule.heightRange.maxHeight
: false;
if (!heightRangeInconform) {
heightRangeInconform = that.itemRule.heightRange.minHeight
? image.height < that.itemRule.heightRange.minHeight
: false;
}
} else {
}
if (widthRangeInconform || heightRangeInconform) {
that.notifyWarning(
that.itemName + "图片长宽尺寸不符合要求!",
"请重新上传符合宽、高规定的图片",
5000
);
file = null;
that.imgFile = null;
that.noticeParent(undefined);
//清除展示
that.fileSrc = that.defaultImg;
} else if (validateResult) {
//所有校验均通过
that.imgFile = file;
that.customUpload(file);
/*console.log('进入了手动上传')*/
} else {
//宽高校验通过,但格式或大小校验不通过
file = null;
this.imgFile = null;
that.noticeParent(undefined);
//清除展示
that.fileSrc = that.defaultImg;
}
} else {
/*固定宽/高校验(校验通过为false)*/
let widthInconform = that.itemRule.width
? image.width !== that.itemRule.width
: false;
let heightInconform = that.itemRule.height
? image.height !== that.itemRule.height
: false;
if (widthInconform || heightInconform) {
//宽高校验未通过
let widthLimit = that.itemRule.width
? that.itemRule.width + "px"
: "不限";
let heightLimit = that.itemRule.height
? that.itemRule.height + "px"
: "不限";
that.notifyWarning(
that.itemName + "图片长宽尺寸不符合要求!",
"请重新上传:宽度为 " +
widthLimit +
"*" +
heightLimit +
"的图片",
5000
);
file = null;
that.imgFile = null;
that.noticeParent(undefined);
//清除展示
that.fileSrc = that.defaultImg;
} else if (validateResult) {
//所有校验均通过
that.imgFile = file;
/*that.$refs['upload'].post(file);//采用手动上传*/
that.customUpload(file);
console.log("进入了手动上传");
} else {
//宽高校验通过,但格式或大小校验不通过
file = null;
this.imgFile = null;
that.noticeParent(undefined);
//清除展示
that.fileSrc = that.defaultImg;
}
}
};
image.src = data;
};
// 以DataURL的形式读取文件:
reader.readAsDataURL(file);
return false; //采用手动上传
},
/* 校验格式与文件大小 */
validate(file) {
let result = true; //校验通过旗标
if (this.itemRule) {
if (this.itemRule.format.length) {
const _file_format = file.name
.split(".")
.pop()
.toLocaleLowerCase();
const checked = this.itemRule.format.some(
item => item.toLocaleLowerCase() === _file_format
);
if (!checked) {
this.handleFormatError();
result = false;
}
}
// check maxSize
if (this.itemRule.maxSize) {
if (file.size > this.itemRule.maxSize * 1024) {
this.handleExceedSize();
result = false;
}
}
}
return result;
},
/* 上传格式报错的函数处理 */
handleFormatError() {
this.notifyWarning(
this.itemName + "图片格式错误!",
"请重新上传" + this.itemRule.format + "格式的图片",
5000
);
},
/* 上传图片大小报错的函数处理 */
handleExceedSize() {
let maxSize =
this.itemRule.maxSize < 1024
? this.itemRule.maxSize + "Kb"
: this.itemRule.maxSize / 1024 + "Mb";
this.notifyWarning(
this.itemName + "图片体积过大!",
"请重新上传不超过" + maxSize + "的图片",
5000
);
},
/* 手动上传的函数处理 */
customUpload(file) {
this.progressObj = {
status: "text",
progressPercentage: 0
};
let that = this;
let onUploadProgress = function(progressEvent) {
let progress = Number(
((progressEvent.loaded / progressEvent.total) * 100).toFixed(2)
);
that.progressObj.progressPercentage =
progress >= 99.99 ? 99.99 : progress;
};
let reqConfig = {
headers: {
fileType: "file",
"Content-Type": "multipart/form-data"
},
onUploadProgress: onUploadProgress
};
let rawFile = new FormData();
rawFile.append("a", file);
this.$http.hosCommon.putFile(this.theUrl, rawFile, reqConfig.headers).then(data => {
this.noticeParent(data[0]);
this.fileSrc = "http://w.y7edu.com/" + data[0];
/* console.log(this.fileSrc); */
setTimeout(() => {
that.progressObj = {
status: "success",
progressPercentage: 100
};
}, 150);
})
.catch(error => {
that.progressObj.status = "exception";
});
},
/* 同步父、子组件上传文件状态【null/Object[file]】 */
noticeParent(msg) {
/*let notice = {itemProp:this.itemProp,uploadedImg:msg};*/
this.$emit("updateFile", msg);
this.dispatch("ElFormItem", "el.form.change", [msg]);
},
/* 使用了elFormItem组件作为父组件,该图片上传组件的v-model绑定的数据更新时,通知elFormItem组件 */
dispatch(componentName, eventName, params) {
let parent = this.$parent || this.$root;
let name = parent.$options.componentName;
while (parent && (!name || name !== componentName)) {
parent = parent.$parent;
if (parent) {
name = parent.$options.componentName;
}
}
if (parent) {
parent.$emit.apply(parent, [eventName].concat(params));
}
}
}
};
</script>
<style lang="less">
.upload-img-component {
height: 40px;
position: relative;
display: inline-block;
.el-upload {
width: 100%;
}
.button-upload {
position: absolute;
height: 40px;
padding: 0 20px;
min-width: 160px;
top: 0;
left: 0;
.upload-img {
.el-upload--text {
height: 40px;
line-height: 40px;
border-radius: 5px;
text-align: center;
font-size: 18px;
color: #FFF;
background-color: #00b8c4;
background-size: 100% 100%;
background-position: center;
&:hover{
opacity: 0.85;
}
}
}
.uploaded-img {
height: 40px;
line-height: 40px;
border-radius: 5px;
text-align: center;
font-size: 18px;
color: rgba(255, 255, 255, 1);
background-color: #909399;
background-size: 100% 100%;
background-position: center;
&:hover{
opacity: 0.85;
}
}
}
// .preview {
// & > div {
// width: 100%;
// height: 100%;
// border-radius: 6px;
// }
// img {
// height: 100%;
// width: 100%;
// background: #dddddd;
// border-radius: 6px;
// }
// }
}
</style>
使用:
<upload-img
:showPreview="false"
:imgDisplayWidth="uploadConfig.previewWidth"
:imgDisplayHeight="uploadConfig.previewHeight"
:itemRule="uploadConfig.imgRequirement"
v-model="imgUrl"
:itemName="uploadConfig.label"
></upload-img>
这里的图片上传就不做赘述了,请看上面的头像上传的详细说明
参考文章:
- Element :upload组件实例
- input和formdata文件上传,formdata在append上传的文件file之后,formdata还是空的?
- HTML5的 input:file上传类型控制
element ui 表单验证不通过
参考问题:element ui 表单验证 this.$refs[formName].validate()里面的内容死活不执行
this.$nextTick(() => {})的作用
参考问题:vue中mounted函数中添加setTimeout有什么作用
JavaScript 判断对象中是否有某属性的常用方法
原文文章:JavaScript 判断对象中是否有某属性的常用方法
- 第一种:点( . )或者方括号( [ ] )
通过点或者方括号可以获取对象的属性值,如果对象上不存在该属性,则会返回undefined。当然,这里的“不存在”指的是对象自身和原型链上都不存在,如果原型链有该属性,则会返回原型链上的属性值。
// 创建对象
let test = {name : 'lei'}
// 获取对象的自身的属性
test.name //"lei"
test["name"] //"lei"
// 获取不存在的属性
test.age //undefined
// 获取原型上的属性
test["toString"] //toString() { [native code] }
// 新增一个值为undefined的属性
test.un = undefined
test.un //undefined 不能用在属性值存在,但可能为 undefined的场景
所以,我们可以根据 Obj.x !== undefined
的返回值 来判断Obj是否有x属性。
这种方式很简单方便,局限性就是:不能用在x的属性值存在,但可能为 undefined的场景。 in运算符可以解决这个问题
- 第二种:in 运算符
MDN 上对in运算符的介绍:如果指定的属性在指定的对象或其原型链中,则in 运算符返回true。
'name' in test //true
'un' in test //true
'toString' in test //true
'age' in test //false
示例中可以看出,值为undefined的属性也可正常判断。
这种方式的局限性就是无法区分自身和原型链上的属性,在只需要判断自身属性是否存在时,这种方式就不适用了。这时需要hasOwnProperty()
- 第三种:hasOwnProperty()
test.hasOwnProperty('name') //true 自身属性
test.hasOwnProperty('age') //false 不存在
test.hasOwnProperty('toString') //false 原型链上属性
可以看到,只有自身存在该属性时,才会返回true。适用于只判断自身属性的场景。
参考的文章:
JS根据身份证号码算年龄
原文文章:JS根据身份证号码算年龄
element ui的表格鼠标定义右键菜单
上代码:
<template>
<!--
@property { tableData : {Array} 表格数据 }
@property { columData : {Array} 表头数据 }
@property { menuList : {Array} 右键菜单 }
@property { myKey : {String} 唯一标识。}
@property { isRadio : {Boolean} 是否单选,默认false。}
@property { emptyText : {String} 空数据时显示的文本内容,默认暂无数据。}
@method { selectItem : {Function(selection, row)} 当用户手动勾选数据行的 Checkbox 时触发的事 }
@method { selectAll : {Function(selection)} 当用户手动勾选全选 Checkbox 时触发的事件 }
@method { selectionChange : {Function(selection)} 当选择项发生变化时会触发该事件 }
@method { rowClick : {Function(row, column, event)} 当某一行被点击时会触发该事件 }
@method { rowContextmenu : {Function(row, column, event)} 当某一行被鼠标右键点击时会触发该事件 }
@method { rowDblclick : {Function(row, column, event)} 当某一行被双击时会触发该事件 }
-->
<div class="air-table-wrapper">
<el-table
ref="table"
id="table"
:data="tableData"
:height="height"
:highlight-current-row="true"
tooltip-effect="dark"
style="width: 100%;"
:row-class-name="rowClassName"
:header-row-class-name="headerClassName"
@empty-text="emptyText"
@select="selectItem"
@select-all="selectAll"
@selection-change="selectionChange"
@row-click="rowClick"
@row-contextmenu="rowContextmenu"
@row-dblclick="rowDblclick"
>
<!-- checkbox -->
<el-table-column type="selection" v-if="showSelection" width="56" align="center"></el-table-column>
<!-- index 序号 -->
<el-table-column v-if="showIndex" label="序号" type="index" width="50"></el-table-column>
<!--列表的表头循环-->
<el-table-column
v-for="(column, index) in columData"
:key="index"
:prop="column.prop"
:label="column.label"
:width="column.width"
:align="column.align||'left'"
show-overflow-tooltip
>
<template slot-scope="scope">
<img
v-if="column.isImg"
:src="scope.row[column.prop]?$sourcePrefix+scope.row[column.prop]:'javascript:;'"
style="max-width: 100%;height:auto"
>
<span
v-if="!column.isImg&&!column.codeMap&&column.type !== 'select'&&column.type !== 'text'&&column.type !== 'op-icon' &&column.type !== 'cj-icon'
&& column.type !== 'datetime' && column.type !== 'select_input'"
v-html="scope.row[column.prop]"
:class="column.diffClassList?column.diffClassList(scope.row[column.prop]):[]"
></span>
<!-- type为op-icon时,为门诊工作站的患者列表的带转入标注 -->
<span
class="icon"
v-if="column.type === 'op-icon'"
:class="column.diffClassList?column.diffClassList(scope.row[column.prop]):[]"
>
<span v-if="scope.row[column.prop] === '待转出'">
<img src="~@images/work/icon_zhuanru.png" alt>
</span>
<span v-if="scope.row[column.prop] === '外院转入'">
<img src="~@images/work/icon_zhuanchu.png" alt>
</span>
<span v-if="scope.row[column.prop] === '跟台手术患者'">
<img src="~@images/work/icon_gentai.png" alt>
</span>
<span v-if="scope.row[column.prop] === '跟台手术患者(审核)'">
<img src="~@images/work/icon_gentai_weishenhe.png" alt>
</span>
</span>
<!-- type为cj-icon时,为采集工作站的标本类型 -->
<span
class="icon"
v-if="column.type === 'cj-icon'"
:class="column.diffClassList?column.diffClassList(scope.row[column.prop]):[]"
>
<span v-if="scope.row[column.prop] === '血沉'">
<img src="~@images/work/icon_shiguan_hei.png" alt>
</span>
<span v-if="scope.row[column.prop] === '生化检验'">
<img src="~@images/work/icon_shiguan_hong.png" alt>
</span>
<span v-if="scope.row[column.prop] === '凝血常规'">
<img src="~@images/work/icon_shiguan_lan.png" alt>
</span>
<span v-if="scope.row[column.prop] === '血流变学'">
<img src="~@images/work/icon_shiguan_lv.png" alt>
</span>
<span v-if="scope.row[column.prop] === '血常规'">
<img src="~@images/work/icon_shiguan_zi.png" alt>
</span>
</span>
<!-- type为text时,为门诊工作站的医嘱列表的带转入标注 -->
<span
class="text"
v-if="column.type === 'text' && scope.row[column.prop] === item.status"
v-for="(item,index) of column.option"
:key="index"
v-html="item.title"
:class="column.diffClassList?column.diffClassList(scope.row[column.prop]):[]"
:style="{'color': (scope.row[column.prop]==column.option[0].title?column.option[0].color:(scope.row[column.prop] === column.option[1].title?column.option[1].color:(scope.row[column.prop]==column.option[2].title?column.option[2].color:column.option[3].color)))}"
></span>
<!-- type为select时,为下拉选择框 -->
<span
v-if="column.type === 'select'"
:class="column.diffClassList?column.diffClassList(scope.row[column.prop]):[]"
>
<el-select
v-model="scope.row[column.prop]"
:class="scope.row[column.prop] === 2 ? column.textCalss :''"
placeholder="请选择"
>
<el-option
v-for="item in column.options"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
</span>
<!-- type为select时,为input在下拉选择框中 -->
<span
v-if="column.type === 'select_input'"
>
<div v-if="column.tableCommands.type==='buttons'">
<el-button
v-for="(btn,index) in column.tableCommands.commandList"
v-if="btn.hasAuth && btn.commandAvailable(scope.$index,scope.row)"
:style="{'color':btn.label === '删除'? '#FB7B7B':''}"
:key="index"
type="text"
size="small"
@click="btn.command(scope.$index,scope.row)"
>{{btn.label}}</el-button>
</div>
<!--下拉类型操作-->
<el-dropdown
v-if="column.tableCommands.type==='dropDown'"
@command="handleDropDownMenuCommand"
style="cursor:pointer"
:trigger="column.tableCommands.trigger||'hover'"
>
<span class="el-dropdown-link">
操作
<i class="el-icon-caret-bottom el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item
v-for="(option,index) in column.tableCommands.commandList"
v-if="option.hasAuth && option.commandAvailable(scope.$index,scope.row)"
:key="index"
:command="{callback:option.command,row:scope.row,index:scope.$index}"
>{{option.label}}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<!-- <el-select
v-model="scope.row[column.prop]"
:class="scope.row[column.prop] === 2 ? column.textCalss :''"
placeholder="请选择"
>
<el-option :label="column.options[0].label" :value="column.options[0].value"></el-option>
<el-option :label="column.options[1].label" :value="column.options[1].value">
</el-option>
<el-option
v-for="item in column.options"
:key="item.value"
:label="item.label"
:value="item.value"
>
<el-input v-if="item.value===1" v-model="scope.row[column.prop]" placeholder="请输入内容"></el-input>
</el-option>
</el-select> -->
</span>
<!-- 时间选择器 -->
<span
v-if="column.type === 'datetime'"
>
<el-date-picker
v-model="scope.row[column.prop]"
type="datetime"
format="yyyy-MM-dd HH:mm:ss"
value-format="yyyy-MM-dd HH:mm:ss"
placeholder="选择停止时间">
</el-date-picker>
</span>
<!--不含codeMap,服务器返回内容即显示内容-->
<!--:class="column.diffStyle?column.diffStyle(scope.row[column.prop]):[]"-->
<span
v-if="!column.isImg&&column.codeMap&&column.type != 'select'"
v-html="column.codeMap[scope.row[column.prop]]"
:class="column.diffClassList?column.diffClassList(scope.row[column.prop]):[]"
></span>
<!--含有codeMap(数字码与内容的映射),服务器返回的是code,根据code映射显示实际内容-->
<!-- 右键菜单 -->
</template>
</el-table-column>
<!-- 表格内的按钮 -->
<el-table-column
v-if="tableCommands"
label="操作"
:width="tableCommands"
:align="tableCommands.align||'left'"
>
<template slot-scope="scope">
<!--按钮组类型操作-->
<div v-if="tableCommands.type==='buttons'">
<el-button
v-for="(btn,index) in tableCommands.commandList"
v-if="btn.hasAuth && btn.commandAvailable(scope.$index,scope.row)"
:style="{'color':btn.label === '删除'? '#FB7B7B':''}"
:key="index"
type="text"
size="small"
@click="btn.command(scope.$index,scope.row)"
>{{btn.label}}</el-button>
</div>
<!--下拉类型操作-->
<el-dropdown
v-if="tableCommands.type==='dropDown'"
@command="handleDropDownMenuCommand"
style="cursor:pointer"
:trigger="tableCommands.trigger||'hover'"
>
<span class="el-dropdown-link">
操作
<i class="el-icon-caret-bottom el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item
v-for="(option,index) in tableCommands.commandList"
v-if="option.hasAuth && option.commandAvailable(scope.$index,scope.row)"
:key="index"
:command="{callback:option.command,row:scope.row,index:scope.$index}"
>{{option.label}}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
<!--v-if="commandAvailable(option.disabledRows,option.disabledEffectInPage,scope.$index)"-->
</el-table-column>
<!-- 添加按钮 -->
<template slot="append" v-if="showAdd">
<Button buttonType="colorButtonroundOrange" @click="addClick"></Button>
</template>
</el-table>
<div class="remark" v-if="tableData.length >100">查询数据多余100条最大限制,请调整查询条件后重试!</div>
<div v-if="dialogVisible" @click="dialogVisible = false" class="modal">
<div
class="air-contex-menu"
v-if="dialogVisible"
v-on:click="hidePanel"
:style="{top: positionD.top + 'px',left: positionD.left+'px'}"
>
<!-- <el-button class="return" type="text" icon="el-icon-close" @click="dialogVisible = false"></el-button> -->
<ul>
<li
v-for="(btn,btnIndex) of menuList"
:key="btnIndex"
v-if="btn.hasAuth && btn.commandAvailable(listData.index,listData.row)"
>
<div
class="menu_item"
@click="btn.command(listData.index,listData.row);dialogVisible=false"
>{{btn.label}}</div>
</li>
</ul>
</div>
</div>
</div>
</template>
<script>
export default {
name: "airtable",
// 框选的指令
directives: {
drag: {
// 指令的定义
inserted: function(el, binding, vnode) {
var oDiv = el;
vnode.context.tableTop = getY(oDiv);
vnode.context.tableLeft = getX(oDiv);
//方法是确定列表到屏幕的位置
function getX(obj) {
var parObj = obj;
var left = obj.offsetLeft;
while ((parObj = parObj.offsetParent)) {
left += parObj.offsetLeft;
}
return left;
}
//方法是确定列表到屏幕的位置
function getY(obj) {
var parObj = obj;
var top = obj.offsetTop;
while ((parObj = parObj.offsetParent)) {
top += parObj.offsetTop;
}
return top;
}
// 禁止右键默认菜单
oDiv.oncontextmenu = function() {
return false;
};
oDiv.onmousedown = function(ev) {
// 防止奇葩操作
if ([2, 3, 4, 5].indexOf(ev.buttons) > -1) {
if (selDiv) {
oDiv
.getElementsByClassName("el-table__body")[0]
.removeChild(selDiv);
}
return;
}
// 获取当前scrollTop
var scrollingTop = vnode.context.targetScroll;
if (vnode.context.isRadio) return;
vnode.context.$selectState(true);
//初始化不显示
vnode.context.toggleShow = false;
//确保用户在移动鼠标的时候只初始化一次选中
var flag = true;
// 获取表格的宽高
let tableWidth = vnode.context.$el.clientWidth;
let tableHeight = vnode.context.$el.clientHeight;
//用来存储列表
var selList = [];
//获得指令下的dom对应的表格
var fileNodes = oDiv.getElementsByTagName("tr");
//获得鼠标
var evt = window.event || arguments[0];
var startX = evt.x || evt.clientX;
var startY = evt.y || evt.clientY;
var top, left;
//时时获得
top = getY(oDiv);
left = getX(oDiv);
var selDiv = document.createElement("div");
selDiv.style.cssText =
"position:absolute;width:0px;height:0px;font-size:0px;margin:0px;padding:0px;border:1px solid rgba(255,165,22,0.38);background-color:rgba(0,0,0,0.38);z-index:1000;filter:alpha(opacity:60);opacity:0.6;display:none;";
selDiv.id = "selectDiv";
oDiv.getElementsByClassName("el-table__body")[0].appendChild(selDiv);
selDiv.style.left = startX + "px";
selDiv.style.top = startY + "px";
var _x = null;
var _y = null;
vnode.context.clearEventBubble(evt);
// 打开开关
vnode.context.mouseflag = true;
// 鼠标拖动时画框
document.onmousemove = function(ev) {
evt = window.event || arguments[0];
_x = evt.x || evt.clientX;
_y = evt.y || evt.clientY;
// 为了确定是点击事件还是移动事件
if (Math.abs(_x - startX) < 5 && Math.abs(_y - startY) < 5) {
return;
}
vnode.context.selectItemFlag = false;
// 为了确保只有一次的渲染每次框选都把默认选中为空(针对当前页)
if (flag) {
// 重置选中css
for (var i = 0; i < fileNodes.length; i++) {
if (fileNodes[i].className.indexOf("el-table__row") != -1) {
fileNodes[i].className = "el-table__row";
selList.push(fileNodes[i]);
}
}
// 判断翻页是否保存选中数据
// if (vnode.context.pageDataFlag) {
// 判断当前页是否有选中的
vnode.context.tableData.forEach(item => {
vnode.context.$refs.table.toggleRowSelection(item, false);
vnode.context.multipleSelection.forEach((ele, i) => {
if (item[vnode.context.myKey] === ele[vnode.context.myKey]) {
vnode.context.multipleSelection.splice(i, 1);
i = i - 1;
}
});
});
// } else {
// vnode.context.multipleSelection = []
// }
flag = false;
}
// 判断鼠标移动是否超出在当前表格区域
if (
_x <= left ||
_x >= left + tableWidth ||
_y <= top ||
_y >= top + tableHeight
) {
document.onmousemove = null;
if (selDiv) {
oDiv
.getElementsByClassName("el-table__body")[0]
.removeChild(selDiv);
}
(selList = null),
(_x = null),
(_y = null),
(selDiv = null),
(startX = null),
(startY = null),
(evt = null);
vnode.context.mouseflag = false;
vnode.context.$handleSelect();
}
if (vnode.context.mouseflag) {
if (selDiv.style.display === "none") {
selDiv.style.display = "";
}
var scrolling = oDiv.getElementsByClassName(
"el-table__body-wrapper"
);
// 两个表格的时候需增加滚屏的高度
// var main_scrolling=document.getElementsByClassName("common_table_height");
// selDiv.style.left = Math.min(_x, startX)-left+scrolling[0].scrollLeft +main_scrolling[0].scrollLeft+ "px";
// console.log(scrolling)
selDiv.style.left =
Math.min(_x, startX) - left + scrolling[0].scrollLeft + "px";
//48是表头的高度
// selDiv.style.top = Math.min(_y, startY)-top - 48 + scrolling[0].scrollTop+main_scrolling[0].scrollTop+ "px";
selDiv.style.top =
Math.min(_y, startY) -
top -
oDiv.getElementsByClassName("is-leaf")[0].offsetHeight +
scrollingTop +
"px";
selDiv.style.width = Math.abs(_x - startX) + "px";
if (scrolling[0].scrollTop > scrollingTop) {
selDiv.style.height =
Math.abs(_y - startY) + scrolling[0].scrollTop + "px";
} else {
selDiv.style.height = Math.abs(_y - startY) + "px";
}
// ---------------- 关键算法确定列表的选中靠的是index---------------------
var _l = selDiv.offsetLeft,
_t = selDiv.offsetTop;
var _w = selDiv.offsetWidth,
_h = selDiv.offsetHeight;
for (var i = 0; i < selList.length; i++) {
var sl = selList[i].offsetWidth + selList[i].offsetLeft;
var st = selList[i].offsetHeight + selList[i].offsetTop;
if (
sl > _l &&
st > _t &&
selList[i].offsetLeft < _l + _w &&
selList[i].offsetTop < _t + _h
) {
if (
selList[i].className.indexOf("seled") === -1 &&
!vnode.context.tableData[i][vnode.context.rowDisabled]
) {
selList[i].className = selList[i].className + " seled";
vnode.context.$refs.table.toggleRowSelection(
vnode.context.tableData[i],
true
);
vnode.context.multipleSelection.push(
vnode.context.tableData[i]
);
}
} else {
if (selList[i].className.indexOf("seled") != -1) {
selList[i].className = "el-table__row";
vnode.context.$refs.table.toggleRowSelection(
vnode.context.tableData[i],
false
);
let index = vnode.context.multipleSelection.indexOf(
vnode.context.tableData[i]
);
vnode.context.multipleSelection.splice(index, 1);
}
}
}
// ---------------- 关键算法结束---------------------
}
if (document.onmousemove) {
vnode.context.clearEventBubble(evt);
}
};
//在鼠标抬起后做的重置
oDiv.onmouseup = function() {
//把鼠标移动事初始化
document.onmousemove = null;
if (selDiv) {
oDiv
.getElementsByClassName("el-table__body")[0]
.removeChild(selDiv);
}
(selList = null),
(_x = null),
(_y = null),
(selDiv = null),
(startX = null),
(startY = null),
(evt = null);
vnode.context.mouseflag = false;
vnode.context.$selectState(false);
// 防止重复$handleSelect()事件
if (!vnode.context.selectItemFlag) {
vnode.context.$handleSelect();
vnode.context.selectItemFlag = false;
}
};
};
}
}
},
props: {
// 对于列表中唯一的字段myKey默认为id
myKey: {
type: String,
default: "id"
},
//有没有checkbox
showSelection: {
type: Boolean,
default: false
},
// 是否显示表头
showIndex: {
type: Boolean,
default: false
},
//列表的数据
tableData: {
type: Array,
default: () => []
},
//传过来的表头信息
columData: {
type: Array,
default: () => []
},
/* 表格类的按钮 */
tableCommands: {
type: Object,
default: () => {}
},
//右键菜单
menuList: {
type: Array,
default: () => []
},
/* 是否设置右键菜单 */
iscontextmenu: {
type: Boolean,
default: true
},
/* 设置是否有双击事件 */
isDblclick: {
type: Boolean,
default: true
},
// 当表格需要单选的时候
isRadio: {
type: Boolean,
default: false
},
// 是否显示添加按钮
showAdd: {
type: Boolean,
default: false
},
// 表格高度
height: {
type: [Number, String],
default: 500
},
// 空数据时显示的文本内容
emptyText: {
type: String,
default: "暂无数据"
}
},
data() {
return {
// 当前行的数据
listData: {
index: "",
row: ""
},
//指令中确定的时候是鼠标按下事件
mouseflag: false,
//选中的数组
multipleSelection: [],
//控制右键菜单弹出显示
dialogVisible: false,
//右键鼠标的位置
position: {
left: 0,
top: 0
},
//当前右键点击的列
currentRow: [],
//当前滚动的距离,
targetScroll: 0,
// 是否点击单选按钮
selectItemFlag: false
};
},
computed: {
// 通过滚动距计算阴影 class
headerClassName() {
if (this.targetScroll === 0) {
return "air-table-header__class";
} else if (this.targetScroll > 0 && this.targetScroll <= 100) {
return "air-table-header__class air-table-header__scroll1";
} else if (this.targetScroll > 100 && this.targetScroll <= 200) {
return "air-table-header__class air-table-header__scroll2";
} else {
return "air-table-header__class air-table-header__scroll3";
}
},
menuH() {
return 25 + this.menuList.length * 34;
},
positionD() {
return this.position;
},
size() {
return this.getViewportSize();
}
},
mounted() {
this.$refs.table.$refs.bodyWrapper.addEventListener(
"scroll",
this.handleScroll
);
},
beforeDestroy() {
this.$refs.table.$refs.bodyWrapper.removeEventListener(
"scroll",
this.handleScroll
);
clearTimeout(this.timer);
},
methods: {
//清除默认事件
clearEventBubble(evt) {
if (evt.stopPropagation) evt.stopPropagation();
else evt.cancelBubble = true;
if (evt.preventDefault) evt.preventDefault();
else evt.returnValue = false;
},
hidePanel(event) {
event.stopPropagation();
},
//当单选时触发的事件
selectItem(selection, row) {
this.$emit("clickSelect", selection);
},
//当表头全选是触发的事件
selectAll(selection) {
this.$emit("clickSelect", selection);
},
// 选项发生改变事件
selectionChange(selection) {
this.$emit("clickSelect", selection);
},
//列表单击选中事件
rowClick(row, column, event) {
this.$emit("rowClick", row, column, event);
},
//列表右键点击事件
rowContextmenu(row,column, event) {
if (this.iscontextmenu && this.menuList.length > 0) {
this.dialogVisible = false;
this.dialogVisible = true;
let defaultIndex = 0;
this.tableData.forEach((el, index) => {
if ((el === row)) {
defaultIndex = index;
}
});
let toRightX = this.size.width - event.clientX;
let toBottomY = this.size.height - event.clientY;
if (toRightX < 250) {
this.position.left = event.clientX - 250;
} else {
this.position.left = event.clientX;
}
if (toBottomY < this.menuH) {
this.position.top = event.clientY - this.menuH;
} else {
this.position.top = event.clientY;
}
//阻止默认事件
event.preventDefault();
event.returnValue = false;
/* event.stopPropagation(); */
this.listData.index = defaultIndex;
this.listData.row = row;
//显示弹出窗
this.$emit("contextmenu", row, event);
}
},
//双击事件
rowDblclick(row, column, event) {
if (this.isDblclick === true) {
this.$emit("rowDblclick", row, column, event);
}
},
//监听表格的滚动
handleScroll(e) {
this.targetScroll = e.target.scrollTop;
},
// 添加按钮点击事件
addClick() {
this.$emit("addClick");
},
handleClose(e) {
this.dialogVisible = false;
},
rowClassName() {
return "rowClassName";
},
handleDropDownMenuCommand(commandObj) {
let { callback, row, index } = commandObj;
callback(index, row);
}
}
};
</script>
<style lang="less">
.air-table-wrapper {
position: relative;
.air-table-header__class {
background-color: #e9e9e9;
th {
background-color: #e9e9e9;
& > .cell {
font-size: 12px;
font-weight: 400;
color: #666666;
}
}
}
.el-table__body-wrapper {
background: #f9f9f9;
}
.remark {
position: absolute;
box-sizing: border-box;
z-index: 100;
bottom: 0;
left: 0;
width: 100%;
line-height: 30px;
padding: 0 10px;
background: #e9e9e9;
color: #00b8c4;
text-align: center;
font-size: 14px;
}
}
.el-table td {
background: #f9f9f9;
border-bottom: 1px solid #dddddd;
}
.el-table {
.el-table__row {
.cell.el-tooltip {
position: relative;
font-size: 12px;
font-weight: 400;
color: #666666;
.icon {
position: absolute;
top: 0;
left: 0;
text-align: center;
width: 100%;
height: 100%;
line-height: 0;
z-index: 1;
}
}
}
}
.rowClassName:hover,
.current-row {
td {
background-color: #f1f7f9;
}
.cell.el-tooltip {
color: #00b8c4;
}
}
#table {
}
.red_style {
.el-input__inner {
color: #fb7b7b;
}
}
</style>
<style lang="less" scoped>
@import "./index";
</style>
使用:
<CommonTable
:tableData="WtableList"
:columData="tableObj.tableColumns"
:menuList="tableObj.commandList" //tableObj.commandList为菜单配置
:height="tableHeight"
@rowDblclick="rowDblclick"
@rowClick="rowClick"
></CommonTable>
commandList: [
{
label: "编辑患者信息",
hasAuth: true,
command: this.handleEdit,
commandAvailable: function() {
return true;
}
},
{
label: "下达医嘱",
hasAuth: true,
command: this.handleRDAdvice,
commandAvailable: function() {
return true;
}
},
{
label: "预约住院",
hasAuth: true,
command: this.handleAhospitalization,
commandAvailable: function() {
return true;
}
},
{
label: "打印预约入院证明",
hasAuth: true,
command: this.handlePrint,
commandAvailable: function() {
return true;
}
},
{
label: "完成看诊",
hasAuth: true,
command: this.handleCVisit,
commandAvailable: function() {
return true;
}
},
{
label: "撤回",
hasAuth: true,
command: this.handleRetract,
commandAvailable: function() {
return true;
}
}
]
这些方法就可以在methods中使用了
参考的文章:
vue 操作数组的变异方法和非变异方法
文章地址: vue 操作数组的变异方法和非变异方法
vue文档:变异方法 (mutation method)
变异方法 (mutation method),顾名思义,会改变被这些方法调用的原始数组:
- push( )
- pop( )
- shift( )
- unshift( )
- splice( )
- sort( )
- reverse( )
也有非变异 (non-mutating method) 方法,例如:
- filter( )
- concat( )
- slice( )
这些不会改变原始数组,但总是返回一个新数组。当使用非变异方法时,可以用新数组替换旧数组
vue使用lodash
原文文章:vue使用lodash
使用lodash可以做什么:
javascript页面刷新的几种方法
原文文章:javascript页面刷新的几种方法
vue watch对象内的属性
原文文章:vue watch对象内的属性
通过window.sessionStorage.getItem获取的值,一定要 通过JSON.parse()转成对象
原文文章:通过window.sessionStorage.getItem获取的值,一定要 通过JSON.parse()转成对象