请求封装
import config from '@/common/config.js';
import {clearLoginInfo, objToQuery, shouldLogin,} from '@/common/utils.js';
export const adornUrl = (actionName, who = 'proxyLl') => {
var proxyApi = "";
proxyApi = config.baseUrl69
// // #ifdef H5
// if (process.env.NODE_ENV === 'development') {
// proxyApi = location.origin + '/' + who;
// }else{
// proxyApi = config.baseUrl69
// }
// // #endif
var newName = actionName.replace('op=', '/').replace('&func=', '/');
switch(who){
case 'proxyUpload':
return config.baseUrl70 + '/gzjxjy/api' + newName;
break;
case 'proxyExam':
return config.baseUrl72 + '/question/api' + newName;
break;
default:
return proxyApi + '/gzjxjy/front/api' + newName;
}
}
export const http = (options, config = {}) => {
let header ={}
if(uni.getStorageSync('authorization')){
header = {
"authorization": uni.getStorageSync('authorization'), // authorization
'Content-Type': config.contentType ? config.contentType : 'application/x-www-form-urlencoded',
}
}else{
header = {
'Content-Type': config.contentType ? config.contentType : 'application/x-www-form-urlencoded',
}
}
// 处理data
options.data = handleData(options.data);
// 是否要显示加载框
if(config.showLoading) {
uni.showLoading({
title: config.loadingTitle || '加载中'
})
}
return new Promise((res, rej) => {
uni.request({
...options,
timeout: 1000 * 30,
withCredentials: true,
header,
success(response) {
if(config.showLoading) {
uni.hideLoading();
}
// 响应拦截器
if(!response.data) {
uni.showToast({
title: '请求没数据',
icon: 'none',
duration: 2000
});
return;
}
if (response.data.code === 401) { // 401, token失效
clearLoginInfo();
// uni.removeStorageSync('authorization');
// uni.removeStorageSync('userInfo');
if(config.loginPop) {
uni.showModal({
title: '未登录',
content: '是否前去登录',
confirmText: '去登录',
success({confirm}) {
if(confirm) {
shouldLogin();
}
}
})
}else {
shouldLogin();
// let routes = getCurrentPages(); // 获取当前打开过的页面路由数组
// let curRoute = routes[routes.length - 1].route //获取当前页面路由
// let curParam = routes[routes.length - 1].options; //获取路由参数
// curParam.path = curRoute;
// uni.redirectTo({
// url: '/pages/common/login' + objToQuery(curParam),
// })
}
}else if(response.data.code === 200) {
res(response.data);
}else {
uni.showToast({
title: response.data.status_desc,
icon: 'none',
duration: 2000
});
rej(response.data);
}
},
fail(err) {
if(config.showLoading) {
uni.hideLoading();
}
// 这边应该是请求断掉才会触发
rej(err);
},
})
})
}
export const getRequest = (url, data, config) => {
url = url + objToQuery(data);
return http({
method: "GET",
data,
url,
}, config)
}
export const postRequest = (url, data, config) => {
return http({
method: "POST",
data,
url,
}, config)
}
function handleData(data = {}) {
// condition
let ret = '';
if (JSON.stringify(data) == '{}') { // 如果是空对象 主动性添加 condition key
data = {
'condition': '{}'
}
}
for (let it in data) {
try {
let tempParam = JSON.parse(data[it])
if ((typeof tempParam) == 'object') {
tempParam = JSON.stringify(tempParam)
ret += encodeURIComponent(it) + '=' + encodeURIComponent(tempParam) + '&'
} else {
ret += encodeURIComponent(it) + '=' + encodeURIComponent(data[it]) + '&'
}
} catch (error) {
ret += encodeURIComponent(it) + '=' + encodeURIComponent(data[it]) + '&'
}
}
return ret;
}
config.js:
export default {
// baseUrl69: process.env.NODE_ENV === 'production' ? "http://192.168.60.131:8269" : 'http://192.168.60.131:8269',
// baseUrl70: process.env.NODE_ENV === 'production' ? "http://192.168.60.131:8270" : 'http://192.168.60.131:8270',
// baseUrl72: process.env.NODE_ENV === 'production' ? "http://192.168.60.131:8272" : 'http://192.168.60.131:8272',
baseUrl69: process.env.NODE_ENV === 'production' ? "https://guizhou-jxjypttest.yl1001.com" : 'https://guizhou-jxjypttest.yl1001.com',
baseUrl70: process.env.NODE_ENV === 'production' ? "https://guizhou-jxjypttest.yl1001.com" : 'https://guizhou-jxjypttest.yl1001.com',
baseUrl72: process.env.NODE_ENV === 'production' ? "https://japitest.yl1001.com/exam" : 'https://japitest.yl1001.com/exam',
}
遇到的问题
1、动态改变顶部的navigationBarTitle的时候会出现有时候不显示只显示了默认的文字问题
原处理:
onLoad(option) {
uni.setNavigationBarTitle({
title: option.NavigationBarTitle || '通知公告'
})
if(option.NavigationBarTitle == '通知公告'){
this.form.categoryId = 10;
}else if(option.NavigationBarTitle == '新闻资讯'){
this.form.categoryId = 30;
}else if(option.NavigationBarTitle == '政策法规'){
this.form.categoryId = 20;
}else if(option.NavigationBarTitle == '学习帮助'){
this.form.categoryId = 70;
}
},
解决:
onReady() {//解决ios上有时候不显示正确标题的问题
let routes = getCurrentPages(); // 获取当前打开过的页面路由数组
let option = routes[routes.length - 1].options; //获取路由参数
uni.setNavigationBarTitle({
title: option.NavigationBarTitle || '通知公告'
})
},
onLoad(option) {
if(option.NavigationBarTitle == '通知公告'){
this.form.categoryId = 10;
}else if(option.NavigationBarTitle == '新闻资讯'){
this.form.categoryId = 30;
}else if(option.NavigationBarTitle == '政策法规'){
this.form.categoryId = 20;
}else if(option.NavigationBarTitle == '学习帮助'){
this.form.categoryId = 70;
}
},
2、下拉刷新效果和触底加载更多冲突问题
原处理为:触底加载更多使用的是scroll-view的@scrolltoupper来实现,而下拉刷新使用的是onPullDownRefresh,这两者一起使用会导致下拉刷新效果无效。 解决:放弃scroll-view的触底加载更多改为使用onReachBottom来实现触发加载更多
pages.josn:
"pages": [
{
"path": "pages/home",
"style": {
"onReachBottomDistance": 40 ,//距离底部多远时触发 单位px
"enablePullDownRefresh": true //设置为true表示当前页面开启下拉刷新
}
},
]
home.vue:
data(){
return {}
},
//触底加载更多
onReachBottom(){
if(!this.hasMoreData) return;//说明没有更多数据
this.form.pageNo++;
this.getDataList()
},
//下拉刷新
onPullDownRefresh() {
this.dataList = []
this.hasMoreData = true
this.form.pageNo = 1
this.getDataList()
},
methods:{
getDataList() {
this.getRequest(this.adornUrl("/home/list", "proxyLl"), {}, {
showLoading: true,
loadingTitle: '加载中',
}).then(data => {
if(data.code == 200){
if(this.dataList.length>0){
this.dataList.push(...data.data)
}else{
this.dataList = data.data
}
if(this.dataList.length>=data.pageparam.sums){
this.hasMoreData = false;//没有更多数据了
}
}
//停止下拉刷新动画
uni.stopPullDownRefresh();
});
},
}
3、tabBar的页面不销毁问题
原代码:
pages.js:
"tabBar": {
"color": "#7A7E83",
"selectedColor": "#0FA4E2",
"borderStyle": "black",
"backgroundColor": "#F8F8F8",
"fontSize": "13",
"list": [
{
"pagePath": "pages/home/index",
"iconPath": "static/images/tabbar/home.png",
"selectedIconPath": "static/images/tabbar/home-active.png",
"text": "首页"
},
{
"pagePath": "pages/exam/index",
"iconPath": "static/images/tabbar/exam.png",
"selectedIconPath": "static/images/tabbar/exam-active.png",
"text": "考试"
},
]
},
exam/index.vue:
data(){
return{
form: {
userId: JSON.parse(uni.getStorageSync('userInfo')).id
},
}
}
如上面代码显示,在data中直接进行获取本地存储的值会在tabbar的页面会出现id获取的一直为用户第一次使用该app登录成功后保存到的用户状态,并且onReady()等生命周期没有触发。因此我们要将获取id的行为放在onLoad()中,进过测试虽然tabbar的页面虽然不会出发onReady生命周期但是会触发onLoad等生命周期。修改后的代码为:
exam/index.vue:
data(){
return{
form: {
userId: JSON.parse(uni.getStorageSync('userInfo')).id
},
}
}
onLoad(option) {
this.form.userId = JSON.parse(uni.getStorageSync('userInfo')).id
},
4、时间在ios上显示为NaN 场景:需要做一个倒计时的时间,后端返回的时间格式为2020-01-10 12:10:00这样的格式,但是该格式只能在安卓等手机可以被识别,ios则不认识带-的时候,所以在ios中运行new Date(2020-01-10 12:10:00)会报无效时间问题。解决为将返回的格式转换为安卓和ios都能够识别的2020/01/10 12:10:00格式:
let time = new Date(this.time.replace(/-/g,'/'));
time.valueOf();//获取到毫秒数,即时间戳
5、uni-popup和video使用,会被video层级过高覆盖问题 原因为:原生的video标签的层级会高于插件的uni-popup的层级,而这层级使用css的z-index来控制是无效的,根据官方推荐的方案使用nvue或者同样为原生的showModel等交互事件,而使用的cover-view只会在h5生效而在app上效果没有显示,原因使用cover-view在app显示就必须cover-view包裹的只有文字等不含任何其他标签的情况才可以,否则app上一律不显示。而nvue样式需要参考weex的编写规范,不然你会发现你所写的样式大部分都不起作用,例如他的flex方向默认为column等,下面只粘贴我所研究的部分代码,交互等我没有研究:
pages.josn
{
"path": "pages/course/detail",
"style": {
"navigationBarTitleText": "课程详情",
"navigationBarBackgroundColor": "#FFFFFF",
"navigationBarTextStyle": "black",
"enablePullDownRefresh": true, //设置为true表示当前页面开启下拉刷新
"app-plus": {
"titleNView": false, //不启用系统导航
"subNVues":[{
"id": "popup", // 唯一标识
"path": "pages/course/subnvue/popup", // 页面路径
// "type": "popup", //原生子窗口内置样式,可取值:'popup',弹出层;"navigationBar",导航栏
"style": {
"width": "750rpx",
"height": "100%",
"position":"absolute",
"background": "rgba(0,0,0,0.2)",
"margin": "auto"
}
}]
}
}
},
subnvue/popup.nvue:(注意为nvue)
<template>
<view id="popup">
<view class="main">
<view class="dialog-box">
<!-- 顶部位置 -->
<view class="title">
<text class="title-text"> 课程练习</text>
<image src="/static/images/course/close.png" style="width: 32rpx; height: 32rpx;" @click='continueVideo'></image>
</view>
<!-- 题目内容 -->
<view class="item" v-if="hangUpExamQuestion">
<!-- 单选1 判断3 -->
<view class="radio-type" v-if="hangUpExamQuestion.type==1 || hangUpExamQuestion.type==3">
<text class="item-title">{{hangUpExamQuestion.title}}({{hangUpExamQuestion.type==1?'单选题':'判断题'}})</text>
<radio-group @change="radioChange($event,hangUpExamQuestion)">
<label class="uni-list-cell uni-list-cell-pd" v-for="option in hangUpExamQuestion.options" :key="option.id">
<view :class="['item-box',option.isChoose?'active':'']">
<radio class="radio" :value="option.id" :checked="option.isChoose" />
<text class='text-name'>{{option.name}}</text>
</view>
</label>
</radio-group>
</view>
<!-- 多选2 -->
<view class="checkbox-type" v-else>
<text class="item-title">{{hangUpExamQuestion.title}}(多选题)</text>
<checkbox-group @change="checkboxChange($event,hangUpExamQuestion)">
<view class="checkbox-item" v-for="option in hangUpExamQuestion.options" :key="option.id">
<label :class="['item-box',option.isChoose?'active':'']">
<checkbox :value="option.id" :checked="option.isChoose" />
<text class='text-name'>{{option.name}}</text>
</label>
</view>
</checkbox-group>
</view>
</view>
<!-- 按钮 -->
<text class="btn" @click='continueVideo'>确定</text>
</view>
</view>
</view>
</template>
<script>
export default {
data(){
return {
hangUpExamQuestion: {},
courseId: '',
popup: true
}
},
created() {
uni.$on('page-popup',(data)=>{
this.hangUpExamQuestion = data.hangUpExamQuestion
})
console.log(this.hangUpExamQuestion)
},
methods:{
// //继续播放视频
continueVideo(){
uni.$emit('continueVideo')
// console.log("触发了")
// const subNvue=uni.getSubNVueById('popup'); //获取
// subNvue.hide() // 显示
},
// //防挂机单选框被选中
// radioChange(evt,item) {
// for (let i = 0; i < item.options.length; i++) {
// if (item.options[i].id == evt.target.value) {
// this.$set(item.options[i],'isChoose',true)
// }else{
// this.$set(item.options[i],'isChoose',false)
// }
// }
// },
// //防挂机多选框被选中
// checkboxChange(e,item) {
// var values = e.detail.value;
// for (let i = 0; i < item.options.length; i++) {
// if(values.includes(item.options[i].id)){
// this.$set(item.options[i],'isChoose',true)
// }else{
// this.$set(item.options[i],'isChoose',false)
// }
// }
// },
}
}
</script>
<style lang="scss">
.dialog-box{
margin-left: 75rpx;
margin-top: 400rpx;
width: 600rpx;
padding: 32rpx 40rpx;
background-color: #fff;
border-radius: 32rpx;
}
.title{
justify-content: space-between;
flex-direction: row;
margin-bottom: 40rpx;
}
.title-text{
font-size: 32rpx;
font-weight: 600;
flex: 1;
}
.item-title{
font-size: 28rpx;
line-height: 40rpx;
color: #222222;
margin-bottom: 32rpx;
}
.radio-type{
margin-bottom: 20rpx;
}
.checkbox-type{
margin-bottom: 20rpx;
}
.checkbox-item{
}
.item-box{
background-color: #fff;
margin-bottom: 28rpx;
align-items: center;
line-height: 36rpx;
flex-direction: row;
justify-content: flex-start;
margin-bottom: 28rpx;
}
.active{
color: #0FA4E2;
}
.text-name{
font-size: 26rpx;
color: #222222;
}
.btn{
font-size: 28rpx;
line-height: 28rpx;
background: #0FA4E2;
border-radius: 8rpx;
padding: 20rpx;
padding-left: 240rpx;
color: #FFFFFF;
}
</style>
使用:
onLoad(){
// #ifdef APP-PLUS
uni.$emit('page-popup', {// 向 popup 传递消息
hangUpExamQuestion: this.hangUpExamQuestion
});
const subNvue=uni.getSubNVueById('popup'); //获取
subNvue.show() // 显示
// #endif
}
注意: 只能在app上显示效果,使用h5测试会报错,所以需要加// #ifdef APP-PLUS来进行条件编译