1.解决使用vue-awesome-swiper组件分页器pagination样式设置失效问题
解决方案:给父标签设置一个id,例如父标签id="parent",在sass/less中使用/deep/样式穿透,例:
#parent /deep/ .swiper-pagination-bullet{
background-color: 'red';
}
在stulys中使用:>>>实现样式穿透:
#parent >>> .swiper-pagination-bullet-active {
border-radius: 'unset';
}
在一次这样的需求中,需要实现滑倒底部时自动请求数据,需要动态创建节点然后追加到某元素中,这期间遇到的问题就是在动态创建节点后,类名也已经加上了 ,但是样式就是没有生效,最后发现原因的产生竟然是<style scoped></style>中scoped属性,该属性的作用是用来绑定当前样式不被污染。
这时就需要通过 >>>
穿透scoped
stylus的样式穿透 使用>>>。iview中需要在组件上使用i-class声明第三方组件类名
<style scoped>
外层 >>> 第三方组件类名{
样式
}
</style>
有些Sass 、Less之类的预处理器无法正确解析>>>。可以使用/deep/操作符( >>> 的别名)
<style lang="sass" scoped>
/deep/ 第三方组件类名 {
样式
}
</style>
实例:
<template>
<input i-class="ivu-input">
</template>
// 样式
.num-input {
width: 90px;
margin-top: 15px;
>>> .ivu-input {
text-align: center!important;
}
}
// Less || Sass
.num-input {
width: 90px;
margin-top: 15px;
/deep/ .ivu-input {
text-align: center!important;
}
}
关于vue项目中使用 vue-awesome-swiper组件 的详细博客:https://segmentfault.com/a/1190000014609379
2. axios 发 post 请求,后端接收不到参数的解决方案
这里直接推荐一篇博客,我觉得写得很详细,而且能完美的解决这个问题:https://www.cnblogs.com/yiyi17/p/9409249.html
3. vue中生成二维码
①. 安装qrcodejs2插件,在控制台输入:
npm install qrcodejs2 --save
* 注意:这里安装的是qrcodejs2,不是qrcode,否则会报错
②. 页面引入——在入口文件(默认是main.js)或者 所用插件的 .vue 文件里引入:
import QRCode from 'qrcodejs2'
③. 在对应的Html页面中,添加html标签
<div id="qrcode" ref="qrcode"></div>
④. 在methods方法里配置:
qrcode () {
let qrcode = new QRCode('qrcode',{
width: 200, // 设置宽度,单位像素
height: 200, // 设置高度,单位像素
text: 'https://www.baidu.com' // 设置二维码内容或跳转地址
})
}
⑤. 调用
this.$nextTick(() => {
this.qrcode()
})
* 注意,调用的时候必须保证: 此DOM为显示状态,否则会出现 *appendChild null错误,就是id为qrcode的dom获取不到,返回结果为null
4. vue中插件qs的使用
有时在请求数据时使用axios请求数据传参时无法正常的获取数据。之后也是一通百度,发现原因是传递参数要将参数序列化。简单来说,qs 是一个增加了一些安全性的查询字符串解析和序列化字符串的库。使用步骤如下:
①. 首先先下载:
npm i qs
②. 在需要用到 qs插件 的文件引入:
import qs from 'qs
③. qs主要有两个方法 :
方法一:qs.stringify()方法一:将对象序列化,多个对象之间用&拼接(拼接是由底层处理,无需手动操作)
qs.stringify() 转换成查询字符串
let comments = {content: this.inputValue}
let comValue = qs.stringify(comments)
方法二:qs.parse()是将URL解析成对象的形式
qs.parse() 转换成json对象
let comValue = qs.parse(comments)
5. vue无限滚动,实现数据懒加载。
一般后台会对数据做分页,这里用mintui的知识来解决就会简单不少,但是它也有不少坑,
v-infinite-scroll
的值设为我的请求数据的函数,发现会无限调用,导致页面卡死,将请求函数放在loadMore
函数中调用便避免了这个问题ul
必须设置overflow:hidden
,否则会导致滚动到底部时不请求v-infinite-scroll
中的loadMore
函数会在页面加载后立即执行,所以不需要在created
中进行初始化请求loading
的值初始为false
,否则页面不会请求page++
bRequest
,默认为true
,如果获取的数据数组长度为0,将bRequset
设置为false
,每次请求之前检查这个变量的值代码:
//需要滚动饿元素标签内添加如下代码,详细饿去看mintui官网
v-infinite-scroll="loadMore"
infinite-scroll-disabled="loading"
nfinite-scroll-distance="10"
<div class="scroll" v-if="have"
v-infinite-scroll="loadMore"
infinite-scroll-disabled="loading"
infinite-scroll-distance="10">
<div class="rec" v-for="(item,index) in goodsList" :key="index">
<img src="../assets/record2.png" alt="">
<div class="right">
<div class="rt">
<div class="tit">{{item.name}}</div>
<div class="pay">{{item.pay_money}}</div>
</div>
<div class="rb">
<div class="time">{{item.pay_time}}</div>
<div class="no">节省:{{item.discount_money}}</div>
</div>
</div>
</div>
<div class="des">
<div class="loading-box tc" v-if="isLoading">
<span class="loading-more-txt">加载中</span>
<mt-spinner type="triple-bounce" color="#16A6F7" :size="15"></mt-spinner>
</div>
<div class="no-more" v-if="noMore">没有更多了~</div>
</div>
</div>
js:
data(){
return{
loading: false,
bRequest: true,
page: 1,
goodsList: [],
isLoading : false,
noMore : false,
}
},
methods:{
loadMore(){
if (this.bRequest) {
this.loadList(this.page);
}
},
loadList (page){
this.post({
data:{
request:'private.consume.order.record.page',
token: this.$store.getters.token,
page: page,
page_size: 6
},
success: res => {
// console.log(res);
this.record = res.data;
if(res.data.data.length === 0){
this.bRequest = false;
this.isLoading = false;
this.noMore = true;
}else {
this.goodsList = this.goodsList.concat(res.data.data);
this.isLoading = true;
this.noMore = false;
}
this.page++;
}
})
}
}
参考博客:https://rosenprivate.gitee.io/2018/08/26/mint-ui%E4%B8%AD%E6%97%A0%E9%99%90%E6%BB%9A%E5%8A%A8%E7%BB%84%E4%BB%B6%E7%9A%84%E7%94%A8%E6%B3%95/
以上情况是在后端将数据分页的情况下的代码,如果后台没有将数据分页,前端需要自己处理数据哦。可参考如下博客:https://blog.csdn.net/lb1135909273/article/details/83856800
如果是不能用mintui去写,你可以去看看这个博客:https://www.jianshu.com/p/29aa8ac3e1c5
6. axios的兼容性问题
axios支持IE8+,但原理是基于promise之上实现的,因此会存在不兼容IE的问题。
trident内核的浏览器下会报:vuex requires a Promise polyfill in this browser
解决方案:
(1)、首先安装 babel-polyfill,来解决IE不支持 promise对象的问题
npm install babel-polyfill -s
安装成功以后需要在 main.js 中引入 babel-polyfill
import 'babel-polyfill'
(2)、一般会配置 webpack.base.config.js 中 entry
module.exports = {
context: path.resolve(__dirname, '../'),
entry: {
app: ["babel-polyfill", "./src/main.js"]
// app: './src/main.js'
},
}
在较低版本的安卓手机中发现发现封装的axios请求无效,主要原因还是低版本的安卓手机无法使用promise
注意:安卓4.3以下的手机不支持axios的使用,无法使用promise,加上 polyfill就可以了。
解决方案:
(1)、项目中安装 es6-promise
npm install es6-promise -s
(2)、引入 es6-promise
import promise from 'es6-promise'
(3)、注册 es6-promise (一定要在axios之前注册)
// 注意:es6-promise 一定要在 axios 之前注册
promise.polyfill()
或者
require('es6-promise').polyfill();
import axios from 'axios'
7. CSS实现div的高度填满剩余空间
利用定位强制定义盒模型的区域。
html:
<div id="main">
<div id="nav">nav</div>
<div id="content">content</div>
</div>
css:
#nav {
background-color: #85d989;
width: 100%;
height: 50px;
}
#content {
background-color: #cc85d9;
width: 100%;
position: absolute;
top: 50px;
bottom: 0px;
left: 0px;
}
8. css实现表单输入框前面的文字两端对齐
一个全角空格就可以解决了。效果如图:
用户名:
密 码:
9. vue跳转页面传输对象,再刷新浏览器数据丢失问题(vue使用router传递数据)
vue Router跳转传字符串是这样的:(params传值需在路由中做配置刷新后数据才不会丢失)
① 点击事件跳转
// 传值
this.$router.push({path:'throughList',query:{id:id}});
//接收
this.ids = this.$route.query.id
还可以这样:
// 传值
this.$router.push({path:'/oil_pay/' + this.oilDes.id})
//router-index.js
{path: '/oil_pay/:sid', name: 'oil_pay',meta:{index:3}, component: (resolve) => require(['../views/oil_pay'], resolve)},
//接收
this.store_id = this.$route.params.sid;
② 标签 router-link 跳转
// 传值
<router-link v-for="(item,index) in station" :key="index" :to="{name:'oil_des',query:{id:item.id}}"></router-link>
//接收
this.ids = this.$route.query.id
params传值同上就不再说了,咱们开始看重点。如果vue Router跳转传对象,刷新数据仍然会丢失,那我们该怎么办呢?
① 点击事件跳转
// 传值
this.$router.push({path:'throughList',query:{deviceInfo:JSON.stringify(deviceInfo)}});
//接收
this.ids = JSON.parse(this.$route.query.deviceInfo),
② 标签 router-link 跳转
// 传值
<router-link v-for="(item,index) in station" :key="index" :to="{name:'oil_des',query:{oilDes:JSON.stringify(item)}}"></router-link>
//接收
this.oilDes: JSON.parse(this.$route.query.oilDes),
10. vue有时会报下图所示的错误,已解决。
解决办法:
先来看一下后台返回的数据例子:
而我定义的 data 是这样的:
data(){
return{
noPay: {}
}
}
正确的定义的 data 应该是这样的:
data(){
return{
noPay: {
oil:{
data:{}
},
product:{}
}
}
}
11. 微信公众号+vue+图片上传
首先做好微信的授权配置,我这里需要定位,支付,图片上传的功能,所以我封装了一个js文件 src->utils->WXUntil.js
import wx from "weixin-jsapi";
import { getJSSDK } from './api/appid';//获取appid信息的接口,以后台人员接口为准
import { payorders } from "./api/pay";//一个更具订单id获取appid的接口
const wxUtils = (token,url) => {
return new Promise((resolve, reject) => {
getJSSDK(token,url).then(data => {
wx.config({
debug: false, // TODO: 测试阶段使用
appId: data.appId,
timestamp: data.timestamp,
nonceStr: data.noncestr,
signature: data.signature,
jsApiList: [
'getLocation',
'hideMenuItems',
'chooseImage',
'uploadImage'
]
});
wxReady(resolve)
}).catch(error => {
reject();
console.log(error);
})
})
};
// 微信jssdk加载完成
const wxReady = resolve => { //不让分享
wx.ready(() => {
wx.hideMenuItems({
menuList: [
'menuItem:share:timeline', // 分享给朋友圈
'menuItem:share:qq', // 分享到QQ
'menuItem:share:weiboApp', // 分享到Weibo
'menuItem:favorite', // 收藏
'menuItem:share:QZone', // 分享到 QQ 空间
'menuItem:copyUrl', // 复制链接
'menuItem:openWithQQBrowser', // 在QQ浏览器中打开
'menuItem:openWithSafari', // 在Safari中打开
'menuItem:share:email', // 邮件
'menuItem:readMode', // 阅读模式
'menuItem:originPage' // 原网页
] // 要隐藏的菜单项,只能隐藏“传播类”和“保护类”按钮,所有menu项见附录3
});
resolve();
});
};
// 微信支付
const WXinvoke = (token,orderId, resolve) => { //orderId 订单ID
payorders(token,orderId).then(res => {
wx.invoke(
'getBrandWCPayRequest', {
"appId": res.appId, // 公众号名称,由商户传入
"timeStamp": res.timestamp, // 时间戳,自1970年以来的秒数
"nonceStr": res.nonceStr, // 随机串
"package": res.package,
"signType": res.signType, // 微信签名方式:
"paySign": res.signature // 微信签名
},
function (res) {
setTimeout(function () {
if (res.err_msg == "get_brand_wcpay_request:ok") {
resolve()
}
}, 500);
}
);
})
};
// 获取地理位置
const getLocation = () => {
return new Promise((resolve, reject) => {
wx.getLocation({
type: 'wgs84', // 默认为wgs84的gps坐标,如果要返回直接给openLocation用的火星坐标,可传入'gcj02'
success: response => {
resolve(response);
},
fail: err => {
reject(err);
}
});
});
};
export { getLocation, WXinvoke, wxUtils };
export default wxUtils;
文件中又封装了请求方法 ( 定位功能 ):src->api->appid.js
import { getReq } from '../../http/http';
const getJSSDK=(token,url)=>{
return new Promise((resolve,reject) => {
getReq({
data:{
request:'',
token:token,
url: url,
request_type:'request'
},
success: res => {
if(res.code == 0){
resolve(res.data)
}else{
reject(res.msg?res.msg:{msg:'request err'})
}
}
});
})
};
export { getJSSDK }
当然了,这里用到的请求也是自己封装的:src->http->http.js
import Vue from 'vue';
import promise from 'es6-promise';
promise.polyfill();
import axios from 'axios';
import store from '../store/index';
import qs from 'qs';
Vue.config.baseHttpUrl = 'https://www.ykelai.com/yocolung/api/'; //网络请求地址
Vue.config.platform = 'wxgzh'; //终端类型
// axios 配置
axios.defaults.timeout = 10000; //网络请求超时时间
axios.defaults.baseURL = Vue.config.baseHttpUrl;
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8';
// http request 拦截器
axios.interceptors.request.use(
config => {
if(config.method === 'post'){
config.data = qs.stringify(config.data);
}
return config;
},
err => {
return Promise.reject(err);
});
Vue.prototype.axios = axios;
var getReq = function(obj){
const _this = this;
var url = '';
obj.data.platform = obj.data.platform || Vue.config.platform;
if(obj.url){
url = obj.url;
}
axios.post(url,obj.data)
.then(function (response) {
try{
if(obj.success && typeof obj.success == 'function'){
if(response.data.code == 999){
// store.dispatch('setLoginStatus', 0)
// store.dispatch('setToken', '')
// location.reload();
}else{
obj.success(response.data)
}
}else{
throw "function 'success' undefind or it's not a function: This HttpRequest has no 'success' methods"
}
}
catch(err){
console.log('err',err);
}
})
.catch(function (error) {
if(obj.fail && typeof obj.fail == 'function'){
obj.fail(error)
}else{
obj.fail = function(error){
console.log('error',error);
alert('网络请求错误');
}
}
});
};
Vue.prototype.post = getReq;
export { getReq }
export default{
getLoginUrl(redirectUri){
return new Promise((resolve, reject) => {
axios.post('', {
request: 'public.auth.redirect.uri.get',
platform: 'wxgzh',
weburl: redirectUri,
request_type:'request'
}).then((data) => {
console.log(data);
if (data.data.code == 0) {
resolve(data.data.data.url)
}
}).catch((err) => {
reject(err)
});
})
},
getToken(code){
return new Promise((resolve, reject) => {
axios.post('', {
request: 'public.auth.login.to.user.action',
platform: 'wxgzh',
code: code,
request_type:'request'
}).then((data) => {
if (data.data.code == 0) {
resolve(data.data.data.token)
}
}).catch((err) => {
reject(err)
});
})
}
}
封装的另一个请求方法( 支付功能 ):src->api->pay.js
import { getReq } from '../../http/http';
const payorders=(token,orderId)=>{
return new Promise((resolve,reject) => {
getReq({
data:{
request:'',
token:token,
request_type:'request',
id: orderId
},
success: res => {
if(res.code == 0){
console.log('支付请求成功');
console.log(res.data);
resolve(res)
}else{
console.log(res.msg ? res.msg : {msg: 'request err'});
reject(res)
}
}
});
})
};
export { payorders }
微信公众号vue文件中使用定位功能是这样使用的:
import { getLocation,wxUtils } from "@/utils/WXUntil";
import config from '@/utils/config';
created(){
wxUtils(this.$store.getters.token,config.baseURL).then(() => {
this.getLocationFn();
}).catch(() => {});
},
methods: {
getLocationFn(){
getLocation().then((res) => {
//获取的经纬度传到 vuex 里
this.$store.commit('getLocation',res)
}).catch((err) => {
console.log(err)
})
},
config.js文件是接口传入的url ( 公共地址+ 路由 ) , src->utils->config.js:
export default {
baseURL:''
}
微信公众号vue文件中使用上传图片功能是这样使用的:
//上传截图
addVipImage:function(){
wxUtils(this.$store.getters.token,config.baseURL + this.$route.path).then(() => {
wx.chooseImage({
count: 1, // 默认9
sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
success: res => {
var localIds = res.localIds; // 返回选定照片的本地ID列表,localId可以作为img标签的src属性显示图片
this.imgSrc = localIds[0];
this.uploadImages(localIds[0])
}
});
}).catch(() => {});
},
uploadImages:function(localIds){
wx.uploadImage({
localId: localIds, // 需要上传的图片的本地ID,由chooseImage接口获得
isShowProgressTips: 1, // 默认为1,显示进度提示
success: res => {
this.serverId = res.serverId; // 返回图片的服务器端ID
}
});
},
获取的 图片的服务器端ID 将在请求后台接口时作为参数传递。以上的上传图片功能是一张图片,多张图片仍然要对数据进行必要的处理。一下是我看到讲解多张图片最清楚的博客(其实是代码清晰
):https://blog.csdn.net/weixin_42330073/article/details/82992107
微信公众号vue文件中使用支付功能是这样使用的:
import { WXinvoke,wxUtils } from "@/utils/WXUntil";
//必要的时候调用此方法进行微信配置初始化
created(){
wxUtils(this.$store.getters.token,location.href).then(() => {}).catch(() => {})
}
// 支付的点击事件
wxPayInside:function () {
this.post({
data:{
request:'',
token:this.$store.getters.token,
request_type: 'request',
gun_id: this.oilQ,
oil_money: this.oilM,
store_id: this.store_id
},
success: res => {
if(res.code === 0){
//获取orderId
var order_id = res.id;
WXinvoke(this.$store.getters.token, order_id, this.orderSuccess);
}
}
})
},
// 订单提交成功的回调
orderSuccess(){
this.$router.push('/nearby_oil');
}
参考微信js-SDK说明文档:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html
12. html表格表头不动,表头以下可滑动
人狠话不多,上代码。
html:
<div class="m2">
<div class="m-demo">
<table border="0">
<thead>
<tr>
<th>好友手机号</th>
<th>获得时间</th>
<th>奖励的礼品</th>
</tr>
</thead>
</table>
<div class="gains">
<table border="0">
<tbody>
<tr>
<td>135****1652</td>
<td>2018-9-24</td>
<td style="color:#FDD31B">5元优惠券</td>
</tr>
</tbody>
</table>
</div>
<div class="noMore" v-if="false">暂无邀请战绩</div>
</div>
</div>
css:
.m2{
width: 100%;
box-sizing: border-box;
padding: 25rem/@wid 20rem/@wid 27rem/@wid 20rem/@wid;
background-color: rgba(0,0,0,0.3);
margin: 20rem/@wid 0;
border-radius: 15rem/@wid;
color: #fff;
table{
width: 100%;
border-collapse:collapse;
border-spacing:0;
tr{
th{
width: 33.3%;
font-weight: normal;
line-height: 50rem/@wid;
font-size:24rem/@wid;
}
td{
width: 33.3%;
text-align: center;
line-height: 48rem/@wid;
font-size:22rem/@wid;
}
}
}
.m-demo{width: 100%;}
.m-demo .gains{width: 100%;height:230rem/@wid;border-top:0;overflow-y:auto;}
.m-demo .gains::-webkit-scrollbar{display: none}
.m-demo .gains .noMore{width: 100%;text-align:center;margin-top: 30rem/@wid;color: #999999;font-size: 30rem/@wid}
.m-demo table{width:100%;table-layout:fixed;}
.m-demo thead th:last-child,.m-demo tbody td:last-child{width:auto;}
.m-demo tbody tr:first-child td{border-top:0;}
.m-demo tbody tr:last-child td{border-bottom:0;}
.m-demo tbody tr td:first-child{border-left:0;}
.m-demo tbody tr td:last-child{border-right:0;}
}
13. input自动获取焦点
<input ref="input">
mounted() {
this.$refs['input'].focus()
// this.$refs['input'].value = ''
}