uni-app项目

360 阅读5分钟

请求封装

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来进行条件编译