(一).PC端后台管理系统开发总结
(1)vue+elementui进阶之路el-table中显示图片
1、table中显示图片
2、当需要遍历图片时,不能直接使用prop绑定值
一张图片
<el-table-column label="头像">
<template slot-scope="scope">
<img :src="scope.row.img" width="50" height="50"/>
</template>
</el-table-column>
多张图片
<el-table-column prop="pictures" label="描述图片">
<template scope="scope">
<img v-for="item in scope.row.pictures" :src="item" width="50" height="50"/>
</template>
</el-table-colunm>
使用@change="handleStatusChange(scope.row)"这种方式进行点击事件传值
<el-table-column label="状态" align="center">
<template slot-scope="scope">
<el-switch v-model="scope.row.status"
active-value="0"
inactive-color="1"
@change="handleStatusChange(scope.row)">
</el-switch>
</template>
</el-table-column>
(2)上传图片
1、转成base64上传图片
<!-- 上传图片的 -->
<input type="file" id="uploadIcon" @change="uploadImg" accept="image/*" />
<el-image style="width: 100px; height: 100px" :src="dataForm.testSubjectImg"></el-image>
// 上传图片
uploadImg: function (e) {
console.log(e.target.files[0]);
// this.dataForm.file=e.target.files[0]
let _this = this;
if (!e || !window.FileReader) return; // 看支持不支持FileReader
let reader = new FileReader();
reader.readAsDataURL(e.target.files[0]);
reader.onloadend = function () {
_this.dataForm.testSubjectImg = this.result;
_this.dataForm.files = this.result;
};
},
2、上传图片
<!-- 上传图片 -->
<el-form-item label="上传图片">
<img v-if="form.image" :src="form.image" class="avatar">
<el-upload action="#" :http-request="requestUpload" :before-upload="beforeUpload"> <el-button size="small" > 上传 <i class="el-icon-upload el-icon--right"></i> </el-button> </el-upload> </el-form-item>
// 上传预处理
beforeUpload(file) {
if (file.type.indexOf("image/") == -1) {
this.msgError("文件格式错误,请上传图片类型,如:JPG,PNG后缀的文件。");
} //
else {
// const reader = new FileReader();
// reader.readAsDataURL(file);
// reader.onload = () => { //
// console.log(reader.result) //
this.form.image = reader.result; //
this.form.file=reader.result; //
}; // } },
// 覆盖默认的上传行为
requestUpload(e) {
console.log(e)
let formdata=new FormData()
formdata.append('avatarfile',e.file)
uploadImage(formdata).then(response => {
console.log(response)
if(response.code===200){
this.newImage=response.imgUrl;
this.form.image= process.env.VUE_APP_BASE_API + response.imgUrl
}
});
},
<el-row :gutter="20">
<el-col :span="1.5"> 选择文件 </el-col>
<el-col :span="1.5">
<el-upload action="#" :limit="1" :show-file-list="false" :on-change="handleChange">
<el-button size="mini" type="primary">上传文件</el-button>
</el-upload>
</el-col>
<el-col :span="8">
<span>{{ name }}</span>
</el-col>
<el-col :span="6" class="fl_btn">
<el-button size="mini" type="primary" @click="upLoadImg">上传</el-button>
</el-col>
</el-row>
// 上传
handleChange(img) {
this.name = img.name;
this.formData = img.raw;
console.log(img);
},
// 上传图片
upLoadImg() {
var formData = new window.FormData();
formData.append("file", this.formData);
formData.append("type", this.type);
formData.append("uid", this.uid);
console.log(formData);
uploadImage(formData).then((res) => {
console.log(res);
if (res.code == 200) { //
this.$emit("upload", res.data.fileUrl);
this.name = "未选择文件";
this.getImgList();
this.msgSuccess("上传成功");
} else {
this.msgError("失败");
} }); },
(3).element-ui样式调整
1.vue使用elementUI form表单label样式修改
(1)删除style标签中的scoped属性
(2)在对应el-form-item的label属性中加入class样式
<el-form-item label="用户名" class="item">
<el-input v-model="ruleForm.username" placeholder="请输入用户名" maxlength="10"></el-input>
</el-form-item>
(3)审查元素,找到label对应的class
如:.el-form-item__label 添加如下样式代码
.item .el-form-item__label{
color: wheat;
}
(4)父子组件传值
1.父组件: :color="color"
2.子组件接受值
props:{
color:{
type:String,
default:""
}
}
3.在页面中显示
v-bind:style="{color:color}"
(5) VUE兄弟组件之间传值,Vue.prototype.bus=new Vue(),this.bus.$on的使用方法
1.在main.js中添加一个bus实例
Vue.prototype.bus=new Vue();
2.分享按钮 加点击事件;把这个true值传给组件二
<a href="#"
class="wap-zl-share"
@click="share1">
</a>
<script>
export default {
data () {
return {
'iscancle': true
}
},
methods: {
sharecl () {
this.bus.$emit('sharecl', this.iscancle)
// console.log(this.iscancle)
}
},
}
</script>
3。默认组件二的分享组件为隐藏;组件二分享组件接受一传过来的值;点击取消按钮iscancle改为false
<div class="wapshare"
v-show="iscancle">
我是分享内容
<p class="share_cancle"
@click="cancle">取消</p>
</div>
<script>
export default {
data () {
return {
'iscancle': false,
}
},
created () {
this.bus.$on('sharecl', iscancle => {
this.iscancle = iscancle
})
},
methods: {
cancle () {
this.iscancle = false
// console.log(iscancle)
}
},
}
</script>
(6)更改table中分页的样式
可以添加多个
<style lang="scss" scoped>
</style>
<style lang='scss'>
.pagination-container{
background:none !important;
color:#fff;
.el-pagination{
.el-pagination__total{
color:#fff !important;
}
.el-pagination__jump{
color:#fff !important;
}
}
}
</style>
(7)父子组件中显示
父组件
<base-header :title="title" :color="color"></base-header>
子组件
<span v-bind:style="{color:color}">{{title}}</span>
props:{
title:{
type:String,
default:''
},
color:{
type:String,
defalt:''
}
}
(8)VUE中监听路由参数的变化
当传值到下一页判断下一页内容显示问题时,使用string类型判断,如果使用Boolean类型传值到下一页的话会自动转类型
this.$router.push({
path:'/proccess/examine',
query:{isSee:"0"}
})
当值不同时,判断是否一样,如果不一样的时候就赋值新的值
watch:{
$route(to,from){
if(to.query.isSee!==from.query.isSee){
this.isSee=this.$route.query.isSee
}
}
}
(9)el-form嵌套在table
<el-form :model="fromData" ref="from">
<el-table :data="fromData.domain" border size="larget">
<el-table-coloumn label="开始放假时间">
<template slot-scope="scope">
<el-form-item
:prop="'domains.'+scope.$index+'.startTime'"
:rules="fromDataRules.startTime"
>
<el-picker
type="date"
v-model="scope.row.startTime"
></el-picker>
</el-form-item>
</template>
</el-table-column>
<el-table-coloumn label="结束放假时间"> <template slot-scope="scope">
<el-form-item
:prop="'domains.'+scope.$index+'.endTime'"
:rules="fromDataRules.endTime"
>
<el-picker
type="date"
v-model="scope.row.endTime"
></el-picker>
</el-form-item>
</template>
</el-table-column>
</el-table>
</el-form>
fromDataRules:{
startTime:[
{required:true,message:'请输入开始时间',trigger:'blur'}
]
}
data(){
return{
数组中时间判断,使用@change事件拿到索引值进行判断
}
}
(9)上传和下载功能
<el-form-item prop="stddspfile">
<el-upload
v-model="form.stddspfile"
action="#"
:http-request="requestUpload"
:show-file-list="false"
>
<el-button type="primary">上传</el-button>
</el-upload></el-form-item>
requestUpload(e){
let formdata=new FormData()
formdata.append('file',e.file)
//调取接口
uploadImg(formdata).then(res=>{})进行处理
}
下载功能是先调下载接口
const baseURL = process.env.VUE_APP_BASE_API
export function download(fileName){
window.loacation.href=baseURL+'/common/download?fileName='+encodeURL(fileName)+'&delete='+true;
}
(10)当使用本地缓存存Boolean类型时,localStorage.getItem('isshow')
eval(localStorage.getItem('isshow').toLowerCse())
(11)vue如何监听浏览器刷新事件
在created、mounted 都行啊。window.addEventListener('beforeunload', e => { localStorage.setItem("store",JSON.stringify(this.$store.state))});
(12)typeof 与instanceof的区别
js是一个弱类型的语言,所以一般想知道当前变量是哪一种类型必须判断类型,都知道判断类型的两种方式:typeof、instanceof。它们各有缺点:typeof适用于基础数据类型判断,引用类型判断都是object。instanceof判断一个实例是否属于某种类型,但严重存在原型继承,所以判断最好在两个对象之间。
tyeof
typeof是一个一元运算,放在一个运算数之前,运算数可以是任意类型。它返回值是一个字符串,该字符串说明运算数的类型。typeof可以用来检测给定变量的数据类型。
var a = [34,4,3,54],
b = 34,
c = 'adsfas',
d = function(){console.log('我是函数')},
e = true,
f = null,
g;
console.log(typeof(a));//object
console.log(typeof(b));//number
console.log(typeof(c));//string
console.log(typeof(d));//function
console.log(typeof(e));//boolean
console.log(typeof(f));//object
console.log(typeof(g));//undefined
但是你可能会发现,typeof在判断null,array,object以及函数实例(new +函数)时,得到的都是object(其实都是原型继承)。这使得在判断这些数据类型的时候,得不到真的数据类型。由此引出instanceof
instanceof
instanceof运算符用来判断一个构造函数的prototype属性所指向的对象是否存在另外一个要检测对象的原型链上。通常来讲,使用 instanceof 就是判断一个实例是否属于某种类型。 a instanceof b:判断a是否为b的实例,可以用于继承关系中 b是c的父对象,a是c的实例,a instanceof b 与 a instanceof c 结果均为true [] instanceof Object // true [] instanceof Array // true 因为Array也是Object的实例对象,由于原型继承造成这种情况
instanceof 只能处理两个对象是否属于的实例关系
(三)sass加载不出来,如下图所示,以及解决办法
1. 错误代码:原因是sass-loader版本过高导致的
Module build failed: TypeError [ERR_INVALID_ARG_TYPE]:
The "path" argument must be of type string. Received type undefined
解决方案:重装低版本sass-loader
npm uninstall sass-loader
npm install --save-dev sass-loader@7.3.1
2.node-sass的安装,以及出现的问题及解决方法
node-sass是一个项目依赖,在一个项目中在使用sass语法的时候,必须通过sass-loader来解析sass,从而sass语法变成浏览器能够识别的css语法,而node-sass模块就是对sass-node的支持模块,如果不安装node-sass,sass-loader就不能正常工作。
安装sass时经常出错的解决办法:
npm install -g cnpm --registry=https://registry.npm.taobao.org (安装淘宝镜像)
cnpm install node-sass --save (使用淘宝镜像安装node-sass
原本以为安装好淘宝镜像之后就可以成功安装node-sass,但是visual studio code终端又出现了 “cnpm - 解决“cnpm:无法加载文件C:*******,因为在此系统上禁止运行脚本……(等有关信息)”
这次我们可以在win10搜索框中输入Windows PowerShell,然后选择管理员身份运行(记得是要管理员身份,要不然就出错)
然后在输入命令:set-ExecutionPolicy RemoteSigned,然后修改权限为A,最后最后通过 get-ExecutionPolicy 查看当前的状态,如下图显示就是成功了
接着回到visual studio code的终端,运行npm install node-sass --save,问题就就解决了。
3.前端启动项目的时候报错,最好的做法就是
1.安装cnpm
2.把该项目文件夹下的node_modules彻底删掉,必须彻底
3.然后cnpm install(安装依赖包) cnpm run dev(运行)
(四)element-ui表单重置函数resetFields()使用注意事项
<el-form :model="queryParams" ref="queryForm">
//封装
export function resetForm(refName){
if(this.$refs[refName]){
this.$refs[refName].resetFields();
}
在main.js中引入
import {resetForm} from '路径'
全局方法挂载
Vue.prototype.resetForm=resetForm;
在需要的页面
this.resetForm("传入节点")
(五)字符串拼接+es6字符串拼接
"' + userIds + '"
es6拼接
`${userIds}`
(六)vue-treeselect的使用
实现:
(1)首先安装包 "@riophae/vue-treeselect"
(2)在要用的vue页面上引入
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
(3)注册组件
components: { Treeselect }
7、转类型
// 转换字符串,undefined,null等转化为""
export function praseStrEmpty(str) {
if (!str || str == "undefined" || str == "null") {
return "";
}
return str;
}
8、全局组件挂载
1、引入
import RightToolbar from '@/components/RightToolbar'
2、全局组件挂载
Vue.component('RightToolbar',RightToolbar)
3、在页面中引用
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
4、v-show="showSearch"
9、vue-router中router
1、$route对象(当前路由信息对象)
表示当前激活的路由的状态信息,包含了当前URL解析得到的信息,还有URL匹配到的route records(路由记录)
路由信息对象:即$router会被注入每个组件中,可以利用它进行一些信息的获取
$route.path //字符串,对应当前路由的路径,如"/foo/bar"
$route.params //一个key/value对象,包含了动态片段和全匹配片段,如果没有路由参数
,就是一个空对象
$route.query //一个key/value对象,表示URL查询参数
//对于路径/foo?user=1,则有$route.query.user==1
//如果没有查询参数,则是个空对象
$route.hash //
$route.fullPath //完成解析后的URL,查询参数和hash的完整路径
$route.matched //数组,包含当前匹配的路径中所包含的所有片段所对应的配置参数对象
$route.name //当前路径名称
$route.meta //路由元信息
10、Vue组件component创建及使用
Vue提供了component,来展示对应名称的组件
component是一个占位符,:is属性可以用来指定要展示的组件名称
使用component占位符来展示组件
注意:is是绑定的属性,需要在实例的data中绑定,组件的名称是字符串
其中 他还提供了mode属性来切换动画的先进先出的问题
11、vue.js如何根据后台返回来的图片进行图片下载
在左下方进行下载图片
//HTML
<el-table-column label="文件名称" prop="fileName">
<template slot-scope="scope">
<a @click="downLoadImg(scope.row)">{{ scope.row.fileName }}</a>
</template>
</el-table-column>
// 下载图片
downLoadImg(row) {
var that = this;
downloadBlob(row.filePath, row.fileName);
},
function download(href, name) {
let eleLink = document.createElement("a");
eleLink.download = name;
eleLink.href = href; e
leLink.click();
eleLink.remove();}
function downloadBlob(url, name) {
let image = new Image();
image.setAttribute("crossOrigin", "anonymous");
image.src = url; image.onload = () => { let canvas = document.createElement("canvas"); canvas.width = image.width; canvas.height = image.height; let ctx = canvas.getContext("2d"); ctx.drawImage(image, 0, 0, image.width, image.height); canvas.toBlob((blob) => { let url = URL.createObjectURL(blob); download(url, name); // 用完释放URL对象 URL.revokeObjectURL(url); }); };}export { download, downloadBlob}
然后就可以下载了
12、点击预览放大图片
<template> <!-- 过渡动画 --> <transition name="fade"> <div class="img-view" @click="bigImg" v-if="show===true"> <!-- 遮罩层 --> <div class="img-layer"></div> <div class="img"> <img :src="imgSrc" /> </div> </div> </transition></template><script>export default { props: ["imgSrc", "show"], methods: { bigImg() { // 发送事件 this.$emit("clickit", { show: false }); }, },};</script><style scoped>body,html { width: 100%; height: 100%;}/*动画*/.fade-enter-active,.fade-leave-active { transition: all .2s linear; transform: translate3D(0, 0, 0);}.fade-enter,.fade-leave-active { transform: translate3D(100%, 0, 0);}.img-view { position: fixed; top: 0; left: 0; right: 0; bottom: 0; width: 100%; height: 100%; z-index: 3000;}/*遮罩层样式*/.img-view .img-layer { position: fixed; z-index: 1999; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.7); width: 100%; height: 100%; overflow: hidden;}.img-view .img { /* width: 80%; */ display: block; position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); z-index: 3001;}/*不限制图片大小,实现居中*/.img-view .img img { max-width: 100%; height: 100%;}</style>
<large-img :imgSrc="imgSrc" :show="show" @clickit="clickit"></large-img>
// 预览handleSeeInfo(row) { this.imgSrc = row.filePath; this.show = true;},
/**预览图片 */clickit(thing) { this.show = thing.show;},
13.如何实现高性能的在线PDF预览
用户需要在我们可站点上在线查看 PDF 文件,并且查看时,用户可以对 PDF 文件的进行旋转、缩放、跳转到指定页码等操作。
- 使用 iframe、embed、object 标签直接加载
采用此方案,只需要直接将 PDF 的在线地址设置为标签的 src 属性
- 使用第三方库 PDF.js 加载
PDF内容分票加载
如何让用户快速打开内容,减少等待时间。由于现有方案都是将pdf文件内容全部下载完成之后才开始渲染,如果文件比较大的时候,用户第一次打卡时就可能需要等待很长时间。()可以不下载全部的文件内容就开始渲染
因为用户不可能一眼看到所有的 PDF 内容,每次只能看到屏幕显示范围内的几页。所以我们可以将可视范围内的PDF 页面内容优先下载并展示,可视范围外的我们根据用户浏览的实际位置按需下载和渲染。这样就可以减少第一次打开时用户的等待时间了。(类似与数据分页、图片懒加载的思想,目的是提高首屏性能。)
我们需要跟后端约定好PDF文件分片之后的数据格式。加入分片的大小为5(即每次请求5页内容)
{
"startPage":1,//分片的开始页码
"endPage":5,//分片结束页码
"totalPage":100,//pdf总页码数
"url":"http://test.com/asset/fhdf82372837283.pdf" // 分片内容下载地址
}
pdf上传与预览
<div class="importPDF"> <el-dialog title="PDF数据导入" :visible.sync = showDataImportDialog center width="35%" style="left: 10%"> <div> <label style="font-weight: bold;margin-right: 10px;">文 件:</label> <el-input v-model="fileName" size="mini" style="width:52%; margin-left: 18px"></el-input> <el-upload class="upload-demo" ref="upload" action="/pdf/upload" :show-file-list="false" :before-upload="beforeUpload"> <el-button slot="trigger" size="mini" type="danger">选取文件</el-button> </el-upload> <el-form ref="pdfForm" :model="pdfForm" > <el-form-item label="公告类型" style="margin-top: 10px"> <el-select class="formItem" size="mini" placeholder="请选择公告类型" v-model="pdfForm.announcementType"> <el-option v-for="item in announcementTypeOptions" :key="item.value" :label="item.label" :value="item.value"></el-option> </el-select> </el-form-item> <el-form-item label="文件级别" style="margin-top: -20px"> <el-select class="formItem" size="mini" placeholder="请选择文件级别" v-model="pdfForm.fileLevel"> <el-option v-for="item in fileLevelOptions" :key="item.value" :label="item.label" :value="item.value"></el-option> </el-select> </el-form-item> </el-form> <div style="margin-top: 10px">备注:仅限上传一个PDF文件</div> </div> <div slot="footer" style="text-align: center;"> <el-button size="mini" @click="viewfile">预览</el-button> <el-button size="mini" type="primary" @click="upLoadpdf()">上传</el-button> <el-button size="mini" @click="showDataImportDialog = false">取 消</el-button> </div> </el-dialog> </div>
//上传PDF文件之前 beforeUpload(file){ console.log("文件", file); this.file = file; this.fileName = file.name; // this.fileSize = file.size; const extension = file.name.split('.').slice(-1) == 'pdf' if (!extension) { this.$message.warning('上传模板只能是pdf格式!') return } let reader = new FileReader(); reader.readAsDataURL(file); let that = this; reader.onload = function() { that.fileData = reader.result; }; return false; // 返回false不会自动上传 }, //预览文件 viewfile(){ console.log("viewFile"); var win = window.open(); win.document.write( '<body style="margin:0px;"><object data="' + this.fileData + '" type="application/pdf" width="100%" height="100%"><iframe src="' + this.fileData + '" scrolling="no" width="100%" height="100%" frameborder="0" ></iframe></object></body>' ); // win.document.write( // '<body style="margin:0px;"><iframe src="' + // this.fileData + // '" scrolling="no" width="100%" height="100%" frameborder="0" ></iframe></body>' // ); }, //上传文件按钮 upLoadpdf(){ if(this.fileName === ''){ this.$message.warning('请选择要上传的文件!'); return false } if(this.pdfForm.announcementType === ''){ this.$message.warning('请选择公告类型'); return false } if(this.pdfForm.fileLevel === ''){ this.$message.warning('请选择公告级别'); return false } //后端需要三个参数file、announcementType、fileLevel,用fileFormData包起来防止 // 出现 headers: 'multipart/form-data'等错误 // 若后端只需一个参数 添加 // let requestConfig = { //headers: { // 'Content-Type': 'multipart/form-data' //}, //} //this.axios.post('/market/upload',formData,requestConfig).then((res)=> let fileFormData = new FormData(); fileFormData.append("file", this.file); fileFormData.append('announcementType',this.pdfForm.announcementType) fileFormData.append('fileLevel',this.pdfForm.fileLevel) this.axios.post('/admin/uploadUserFile',fileFormData).then(res =>{ if (res.data.status===1){ this.$message({ message:res.data.msg, type:'success', duration: 3000, }); this.showDataImportDialog=false }else{ this.messageLabel = this.$message({ message:res.data.msg, type:'error', showClose:true, duration:0, }); } }) }
14.文件大小转换,字节转换成K M G T单位
// 计算文件大小函数(保留两位小数),Size为字节大小// size:初始文件大小function getfilesize(size) { if (!size) return ""; var num = 1024.0; //byte if (size < num) return size + "B"; if (size < Math.pow(num, 2)) return (size / num).toFixed(2) + "K"; //kb if (size < Math.pow(num, 3)) return (size / Math.pow(num, 2)).toFixed(2) + "M"; //M if (size < Math.pow(num, 4)) return (size / Math.pow(num, 3)).toFixed(2) + "G"; //G return (size / Math.pow(num, 4)).toFixed(2) + "T"; //T}//Math.pow(x,y) //返回 x 的 y 次幂的值//NumberObject.toFixed(num) //可把 Number 四舍五入为指定小数位数的数字
14.Vue实现PC端分辨率自适应
方案:
lib-flexible + px2remLoader
lib-flexible:阿里可伸缩布局方案
px2rem-loader:px转rem
安装依赖
npm install px2rem-loader -D
npm install lib-flexible -S
引入依赖
在main.js引入lib-flexible
import 'lib-flexible'
px转换成rem
vue-loader的options和其他样式文件loader最终都是由build/utils.js里的方法生成的,我们只需在cssLoader后再加上一个px2remLoader即可,px2rem-loader的remUnit选项意思是1rem=多少像素,结合lib-flexible的方案,我们将px2remLoader的options.remUinit设置成设计稿宽度的1/10,这里假设设计稿宽为1920px
15.elementUI里面的el-input框有时候无法输入的问题
最近发现项目中需要输入内容的时候,input框有时候会无法输入进去
发现elementUI中@input事件可以拿到当前输入的值,
问题找到了,视图没有更新的问题
解决方法 this.$forceUpdate()
16.vue强制更新$forceUpdate()的使用及原理
调用强制更新方法this.$forceUpdate()会更新视图和数据,触发updated生命周期。
<button @click="reload()">强制更新</button>
updated(){
console.log("更新了");
},
methods:{
reload(){
this.$forceUpdate();
}
}