CRM客户管理系统,通过信息技术以及互联网技术协调企业与顾客间在销售、营销和服务上的交互,从而晋升其治理形式,向客户提供翻新式的个性化的客户交互和服务的过程。其最终目标是吸引新客户、保留老客户以及将已有客户转为忠诚客户,减少市场。

APP 开发采纳APICloud AVM框架,后盾采纳PHP。

思维导图

性能介绍
1.客户治理:录入客户信息、客户跟进、客户销售记录、间接拨打客户电话、条件筛选查问、公共客户

2.申请、收、发货治理

3.文档库、知识库

4.工作日志、日程治理

5.产品治理、库存治理

6.门店治理、员工治理

7.统计分析:客户统计分析、门店统计分析、员工统计分析、销售统计分析

8.通讯录、音讯揭示

9.即时通讯、视频会议

利用模块

我的项目目录

开发介绍
首页导航
零碎首页应用tabLayout,能够将相干参数配置在JSON文件中,再在config.xml中将content的值设置成该JSON文件的门路。如果底部导航没有非凡需要这里强烈建议大家应用tabLayout为APP进行布局,官网曾经将各类手机屏幕及不同的分辨率进行了适配,免去了很多对于适配方面的问题。
{

"name": "root","hideNavigationBar": true,"navigationBar": {  "background": "#035dff",  "color": "#fff",  "shadow": "#035dff",  "hideBackButton": true},"tabBar": {  "scrollEnabled": false,  "background": "#fff",  "shadow": "#f1f1f1",  "color": "#8a8a8a",  "selectedColor": "#000000",  "index":0,  "preload": 0,  "frames": [{    "name": "home",    "url": "pages/main/home.stml",    "title": "主页"  }, {    "name": "notice",    "url": "pages/notice/notice-index.stml",    "title": "音讯告诉"  }, {    "name": "tellbook",    "url": "pages/main/tellbook.stml",    "title": "通讯录"  }, {    "name": "my",    "url": "pages/seeting/my.stml",    "title": "集体核心"  }],  "list": [{    "text": "主页",    "iconPath": "image/navbar/home-o.png",    "selectedIconPath": "image/navbar/home.png",    "scale":3  }, {    "text": "揭示",    "iconPath": "image/navbar/notice-o.png",    "selectedIconPath": "image/navbar/notice.png",    "scale":3  }, {    "text": "通讯录",    "iconPath": "image/navbar/book-o.png",    "selectedIconPath": "image/navbar/book.png",    "scale":3  }, {    "text": "设置",    "iconPath": "image/navbar/set-o.png",    "selectedIconPath": "image/navbar/set.png",    "scale":3  }]}

}
动静权限
在首页的apiready中依据提醒受权须要获取的权限,APP每次启动的时候就会判断是否已受权,如果未受权就是提醒进行受权。

        apiready(){            let limits=[];            //获取权限            var resultList = api.hasPermission({                list: ['storage', 'location', 'camera', 'photos', 'phone']            });            if (resultList[0].granted) {                // 已受权,能够持续下一步操作            } else {                limits.push(resultList[0].name);            }            if (resultList[1].granted) {                // 已受权,能够持续下一步操作            } else {                limits.push(resultList[1].name);            }            if (resultList[2].granted) {                // 已受权,能够持续下一步操作            } else {                limits.push(resultList[2].name);            }            if (resultList[3].granted) {                // 已受权,能够持续下一步操作            } else {                limits.push(resultList[3].name);            }            if (resultList[4].granted) {                // 已受权,能够持续下一步操作            } else {                limits.push(resultList[4].name);            }            if(limits.length>0){                api.requestPermission({                    list: limits,                }, (res) => {                                    });            }        }

音讯事件通过sendEvent把事件播送进来,而后在其余页面通过addEventListener监听事件,通过事件名和附带的参数进行其余操作。举例:登录胜利之后,须要在集体核心加载个人信息,在首页加载相干集体的数据;增加某项数据之后,须要进行刷新列表等等。

methods: {

        login(){            if (!this.data.username) {                this.showToast("姓名不能为空");                return;            }            if (!this.data.password) {                this.showToast("明码不能为空");                return;            }             var data={                secret:'',                user:this.data.username,                psw:this.data.password            };            api.showProgress();            POST('Index/queryuserinfo',data,{}).then(ret =>{                // console.log(JSON.stringify(ret));                if(ret.flag=='Success'){                    api.setPrefs({key:'username',value:ret.data.username});                    //api.setPrefs({key:'password',value:ret.data.password});                    api.setPrefs({key:'userid',value:ret.data.id});                    api.setPrefs({key:'roleid',value:ret.data.roleid});                    api.setPrefs({key:'rolename',value:ret.data.rolename});                    api.setPrefs({key:'organid',value:ret.data.organid});                    api.setPrefs({key:'organname',value:ret.data.organname});                    api.setPrefs({key:'organtype',value:ret.data.organtype});                    api.setPrefs({key:'phone',value:ret.data.phone});                            api.setPrefs({key:'name',value:ret.data.name});                        api.sendEvent({                        name: 'loginsuccess',                    });                    api.closeWin();                }                else{                    api.toast({                        msg:'登录失败!请稍后再试。'                    })                }                api.hideProgress();            }).catch(err =>{                api.toast({                    msg:JSON.stringify(err)                })            })        }    }

双击退出程序
在首页、登录页或其余须要双击退出程序的页面,在apiready中增加。

      apiready(){                //监听返回  双击退出程序        api.setPrefs({            key: 'time_last',            value: '0'        });        api.addEventListener({            name : 'keyback'            }, (ret, err) => {            var time_last = api.getPrefs({sync: true,key: 'time_last'});            var time_now = Date.parse(new Date());            if (time_now - time_last > 2000) {                api.setPrefs({key:'time_last',value:time_now});                api.toast({                    msg : '再按一次退出APP',                    duration : 2000,                    location : 'bottom'                });            } else {                api.closeWidget({                    silent : true                });            }        });      }

清空缓存
官网自带的API clearCache,可状况全副缓存,也可抉择革除多少天前的缓存。


音讯推送采纳极光推送,须要集成ajpush模块。

具体应用办法可具体浏览官网模块文档。

 推送性能初始化须要在APP每次启动的时候进行集成,将初始化极光推送的办法集成在util工具类中,在首页进行初始化。
fnReadyAJpush(){

    var jpush = api.require('ajpush');    api.addEventListener({name:'pause'}, function(ret,err) {        onPause();//监听利用进入后盾,告诉jpush暂停事件    })    api.addEventListener({name:'resume'}, function(ret,err) {        onResume();//监听利用复原到前台,告诉jpush复原事件    })    //设置初始化    jpush.init(function(ret, err){        if(ret && ret.status){            var ali=$api.getStorage('userid');            var tag=$api.getStorage('roleid');            //绑定别名            if($api.getStorage('userid')){                jpush.bindAliasAndTags({                    alias:ali,                    tags:[tag]                }, function(ret, err){                    if(ret.statusCode==0){                        api.toast({ msg: '推送初始化胜利'});                    }                    else{                        api.toast({ msg: '绑定别名失败'});                    }                });            }            //监听音讯            jpush.setListener(function(ret) {                var content = ret.content;                alert(content);            });            }        else{                api.toast({ msg: '推送服务初始化失败'});            }    });}

初始化应用,每次启动APP的时候须要,从新登陆之后可能存在切换账号的状况,也须要从新登陆。

定位性能
因为零碎中的定位只须要确定以后地位即可,所有定位性能应用的是aMapLBS模块,此模块没有关上地图的性能,只须要在用到的页面间接调用获取定位即可。

应用前须要在config.xml中进行配置,具体参数须要去高德开放平台去申请。

        //获取以后地位信息和经纬度                   setLocation(){            var aMapLBS = api.require('aMapLBS');            aMap.updateLocationPrivacy({                privacyAgree:'didAgree',                privacyShow:'didShow',                containStatus:'didContain'            });            aMapLBS.configManager({                accuracy: 'hundredMeters',                filter: 1            }, (ret, err) => {                if (ret.status) {                    aMapLBS.singleLocation({                        timeout: 2                    }, (ret, err) => {                        if (ret.status) {                                                this.data.lon = ret.lon;                            this.data.lat = ret.lat;                        }                    });                    aMapLBS.singleAddress({                        timeout: 2                    }, (ret, err) => {                        if (ret.status) {                            this.data.address = ret.formattedAddress;                        }                    });                }                else{                    api.toast({                        msg:'定位初始化失败,请开启手机定位。'                    })                    return false;                }            });        }

视频、语音通话
采纳tecnetRTC开发音视频通话性能。须要先去腾讯云平台创立利用申请key,在通过官网提供的办法生成userSig。

生成userSig代码
//获取腾讯视频RTC usersig

public function getQQrtcusersig(){  checkscret('secret');//验证受权码  checkdataPost('userid');//用户ID  $sdkappid=C('sdkappid');  $key=C('usersig_key');  $userid=$_POST['userid'];    require 'vendor/autoload.php';    $api = new \Tencent\TLSSigAPIv2($sdkappid, $key);  $sig = $api->genSig($userid);    if($sig){    returnApiSuccess('查问胜利',$sig);  }  else{    returnApiError( '查问失败,请稍后再试');    exit();  }}

用户视频画面须要依据以后视频用户数,进行计算调整。

<template>

<view class="page">    <view class="video-bk"></view>            <view class="footer">        <view class="footer-item" @click="setLoud">            <image class="footer-item-ico" src='../../image/loud-on.png' mode="widthFix" v-if="isLoud"></image>            <image class="footer-item-ico" src='../../image/loud-off.png' mode="widthFix" v-else></image>            <text class="footer-item-label">免提</text>        </view>        <view class="footer-item" @click="setRTC">            <image class="footer-item-ico" src='../../image/stop.png' mode="widthFix" v-if="isStart"></image>            <image class="footer-item-ico" src='../../image/start.png' mode="widthFix" v-else></image>            <text class="footer-item-label" v-if="isStart">完结</text>            <text class="footer-item-label" v-else>开始</text>        </view>        <view class="footer-item" @click="setMute">            <image class="footer-item-ico" src='../../image/mute-on.png' mode="widthFix" v-if="isMute"></image>            <image class="footer-item-ico" src='../../image/mute-off.png' mode="widthFix" v-else></image>            <text class="footer-item-label">静音</text>        </view>    </view></view>

</template>
<script>

import $util from '../../utils/utils.js'import {POST} from '../../script/req.js'export default {    name: 'rtcvideo',    apiready(){        this.data.roomId = api.pageParam.id;        this.data.meetStart = api.pageParam.userid;        var tencentTRTC= api.require('tencentTRTC');        api.setNavBarAttr({            shadow:'#000000'        });        //IOS禁用左右滑动        api.setWinAttr({            slidBackEnabled: false        });        api.addEventListener({            name:'keyback'        }, (ret) =>{            //禁用返回按钮        })        api.addEventListener({            name: 'navbackbtn'        }, (ret, err) => {            api.confirm({                title: '揭示',                msg: '你确定要来到本次会议吗?',                buttons: ['确定', '持续']            },(ret)=>{                var index = ret.buttonIndex;                if(index==1){                    tencentTRTC.exitRoom({                    });                    api.closeWin();                }            })        });         //增加右键切换摄像头        api.setNavBarAttr({            rightButtons: [{                iconPath:'widget://image/switch.png'            }]        });                    api.addEventListener({            name:'navitembtn'        }, (ret)=>{            if(ret.type=='right'){                 //切换前后摄像头                tencentTRTC.switchCamera({                });                api.toast({                    msg:'切换胜利'                })            }        })        //视频模块监听事件        var tencentTRTC= api.require('tencentTRTC');        //view disappear 监听用户间接敞开APP的状况 默认把用户本人退出房间        api.addEventListener({name:'viewdisappear'},function(ret,err){            tencentTRTC.exitRoom({            });        });        //监听RTC事件        tencentTRTC.setTRTCListener({},(ret, err) => {            // console.log(JSON.stringify(ret));            // console.log(JSON.stringify(err));            if(ret.status){                if(ret.action=='enterRoom'){                    //开启语音                    tencentTRTC.startLocalAudio({                    });                                    tencentTRTC.startLocalPreview({                        rect:{                            x: 0,                            y: api.safeArea.top+45,                            w: api.frameWidth,                            h: api.frameHeight/3                        }                    });                }                //有用户退出房间                else if(ret.action=='remoteUserEnterRoom'){                    // console.log(this.data.rectindex);                    if(this.data.index>4)                    var thisRect = {                        x: (api.frameWidth/4)*this.data.rectindex,                        y: api.frameHeight/3+api.safeArea.top+45,                        w: api.frameWidth/4,                        h: api.frameWidth/4                    }                    tencentTRTC.startRemoteView({                        rect:thisRect,                        remoteUid:ret.remoteUserEnterRoom.userId,                    });                    this.data.rectindex++;                }                //有用户来到房间                else if(ret.action=='remoteUserLeaveRoom'){                    tencentTRTC.stopRemoteView({                        remoteUid:ret.remoteUserLeaveRoom.userId,                    });                }            }            else{                api.toast({                    msg:JSON.stringify(err)                })            }        });    },    data() {        return{            isMute:false,            isLoud:false,            isStart:false,            rects:[],            rectindex:0,            roomId:'',            meetStart:'',            mTop:api.safeArea.top+45        }    },    methods: {        setMute(){            var tencentTRTC= api.require('tencentTRTC');            this.data.isMute = !this.data.isMute;            tencentTRTC.muteLocalAudio({                mute:this.data.isMute            });        },        setLoud(){            this.data.isLoud = !this.data.isLoud;        },        setRTC(){            var tencentTRTC= api.require('tencentTRTC');            if(this.data.isStart){                if(this.data.meetStart == api.getPrefs({sync: true,key: 'userid'})){                    //发起人来到房间 会议完结                    this.setRTCStatus('03');                }                tencentTRTC.exitRoom({                });                api.closeWin();            }            else{                var data={                    secret:'',                    userid: api.getPrefs({sync: true,key: 'userid'})                };                api.showProgress();                POST('Video/getQQrtcusersig',data,{}).then(ret =>{                    console.log(JSON.stringify(ret));                    if(ret.flag=='Success'){                                                this.data.isStart = !this.data.isStart;                        tencentTRTC.enterRoom({                            appId:14*******272,                            userId:api.getPrefs({sync: true,key: 'userid'}),                            roomId:this.data.roomId,                            userSig:ret.data,                            scene:1                        },(ret, err) => {                            //  console.log(JSON.stringify(ret));                            //  console.log(JSON.stringify(err));                            if(ret.result<0){                                api.toast({                                    msg: '网络谬误',                                    duration: 2000,                                    location: 'bottom'                                });                            }                        });                        //设置会议状态为开始                        this.setRTCStatus('02');                    }                    api.hideProgress();                }).catch(err =>{                    api.toast({                        msg:JSON.stringify(err)                    })                })            }        },        //视频设置最多9集体,自己画面占一行,其余8人每行4个共2行        setUserRect(){            for(var i=0;i<8;i++){                if(i<4){                    this.data.rects[i]={                    x: (api.frameWidth/4)*i,                    y: api.frameHeight/3+this.data.mTop,                    w: api.frameWidth/4,                    h: api.frameWidth/4                    };                }                else{                    this.data.rects[i]={                    x: (api.frameWidth/4)*(i-4),                    y: (api.frameHeight/3)+(api.frameWidth/4)+this.data.mTop,                    w: api.frameWidth/4,                    h: api.frameWidth/4                    };                }            }            // console.log(JSON.stringify(this.data.rects));        },        //设置会议状态        setRTCStatus(status){            var data={                    secret:'',                    id: this.data.roomId,                    flag:status                };                api.showProgress();                POST('Video/setmeeting',data,{}).then(ret =>{                    console.log(JSON.stringify(ret));                    if(ret.flag=='Success'){                        //在会议列表监听 刷新会议列表 已完结的不在显示                                            api.sendEvent({                            name: 'setmeeting'                        });                    }                    api.hideProgress();                }).catch(err =>{                    api.toast({                        msg:JSON.stringify(err)                    })                })        }    }}

</script>
<style>

.page {    height: 100%;    justify-content: space-between;    background-color: #ffffff;}.video-bk{    height: 100%;    background-color: #000000;}.footer{    height: 70px;    background-color: #ffffff;    flex-flow: row nowrap;    justify-content: space-around;    margin-bottom: 30px;    align-items: center;    margin-top: 20px;}.footer-item{    align-items: center;    justify-content: center;}.footer-item-ico{    width: 45px;}.footer-item-label{    font-size: 13px;}

</style>
通讯录
基于scroll-view进行开发实现通讯录性能,可间接拨打电话
<template>

<view>    <scroll-view class="page" scroll-y show-scrollbar="false" id="book">        <safe-area></safe-area>        <view class="item" v-for="(item, index) in list" v-show="item.children.length>0">            <view class="nav" id={item.zkey}>                <text class="nav-title">{item.zkey}</text>            </view>            <view class="box" v-for="(it, pindex) in item.children" data-phone={it.phone}  @click="takePhone">                <image class="avator" src='../../image/avator.png' mode="widthFix"></image>                <view class="right">                    <text class="name">{it.remark}</text>                    <view class="bt">                        <text class="bt-position">{it.position}</text>                        <text class="bt-part">{it.dept_name}</text>                    </view>                </view>            </view>        </view>            </scroll-view>    <scroll-view class="right-nav" scroll-y show-scrollbar="false">        <view class="right-nav-item" data-id={item.zkey} @click="scrollToE" v-for="(item, index) in list">            <text class={item.zkey==zIndex?'right-nav-item-on':'right-nav-item-off'}>{item.zkey}</text>        </view>    </scroll-view></view>  

</template>
<script>

import {POST} from '../../script/req.js'export default {    name: 'tellbook',    apiready(){        this.loadData();    },    data() {        return{            list:[],            zIndex:''        }    },    methods: {        loadData(){            var data={                secret:'',                userid:api.getPrefs({sync: true,key: 'userid'})            };            api.showProgress();            POST('Index/gettellbook',data,{}).then(ret =>{                            if(ret.flag=='Success'){                        this.toTree(ret.data);                }                api.hideProgress();            }).catch(err =>{                api.toast({                    msg:JSON.stringify(err)                })            })        },        //解决数据        toTree(data){            var book=[];            var  zm= 'A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z'.split(',');            zm.forEach(element => {                var arrz = data.filter((item) => {                    return item.zkey == element                })                book.push({'zkey':element,children:arrz});            });            this.data.list = book;            //console.log(JSON.stringify(book));        },        scrollToE(e){            var id = e.target.dataset.id;            var book = document.getElementById('book');            book.scrollTo({                view:id            })            this.data.zIndex = id;        },        takePhone(e){            var phone = e.target.dataset.phone;            api.call({                type: 'tel',                number: phone            });        }    }}

</script>
<style>

.page {    height: 100%;    background-color: #ffffff;}.nav{    margin: 0 10px;    padding: 0 10px;}.nav-title{    font-size: 20px;}.box{    flex-flow: row nowrap;    justify-content: flex-start;    align-items: center;    margin: 10px;    border-bottom: 1px solid #ccc;    padding-bottom: 10px;}.avator{    padding: 5px;}.right{    padding-left: 20px;}.bt{    flex-flow: row nowrap;    justify-content: flex-start;    align-items: center;}.bt-position{    font-size: 14px;    color: #666666;}.bt-part{    font-size: 14px;    color: #666666;    padding-left: 20px;}.right-nav{    position: absolute;    right: 10px;    width: 30px;    padding: 30px 0;    height: 100%;    align-items: center;}.right-nav-item{    padding-bottom: 5px;}.right-nav-item-on{    color: #035dff;}.right-nav-item-off{    color: #666666;}.avator{    width: 50px;}

</style>
echarts图表
因为AVM无奈解析cavans,所有图表相干的页面采纳的是html,页面增加在html文件夹中,通过open.frame()进行关上。采纳的Echarts.js.可去echarts官网下载,如果图标不简单,倡议应用自定义下载只抉择用到的控件,减小文件体积;款式也能够自定义而后下载转述js文件,下载连贯

文件目录



<!doctype html>
<html>

<head>    <meta charset="utf-8">    <meta name="viewport" content="maximum-scale=1.0,minimum-scale=1.0,user-scalable=0,width=device-width,initial-scale=1.0" />    <title>统计-客户</title>    <link rel="stylesheet" type="text/css" href="../css/api.css" />    <style>      body{background:#efefef;padding: 10px 10px 50px 10px;}      .chart-box{          background-color: #ffffff;          border-radius: 5px;          margin-top: 10px;      }    </style></head><body>  <div class="chart-box">    <div id="Chart1" style="height:200px;"></div>  </div>  <div class="chart-box">    <div id="Chart2" style="height:200px;"></div>  </div>  <div class="chart-box">    <div id="Chart3" style="height:200px;"></div>  </div>  <div class="chart-box">    <div id="Chart4" style="height:200px;"></div>  </div></body><script type="text/javascript" src="../script/api.js"></script><script type="text/javascript" src="../script/echarts.min.js"></script><script type="text/javascript" src="../script/customed.js"></script><script type="text/javascript" src="../script/remotedb.js"></script><script>    apiready = function() {        loaddemo1();        loaddemo2();    };//客户统计    function loaddemo1(){        var path='http://192.168.1.5/api.php/Home/Statistic/querykhzzl';        var data={values:{            secret:'776eca99-****-11e9-******00163e008b45',            year:'2021'        }};        fnPost(path, data, function(ret, err) {            // console.log(JSON.stringify(ret));            // console.log(JSON.stringify(err));            if(ret['flag']=='Success'){            var data=ret['data'];            var arryaxis=[],arrzzl=[],arrall=[];            for(var i=0;i<data.length;i++){                arryaxis[i]=MonthToZhcn(data[i]['mon']);                arrzzl[i]=data[i]['num'];                arrall[i]=data[i]['monall'];            }            var myChart = echarts.init(document.getElementById('Chart1'),'customed');            var option = {                title:{                    text:'2021年客户全年增长量和保有量'                },                tooltip: {                    trigger: 'axis',                    axisPointer: {                        type: 'shadow'                    }                },                legend: {                    data: ['增长客户','客户总量'],                    orient:'vertical',                    right:10,                    top:120                },                grid: {                    left: '3%',                    right: '4%',                    bottom: '3%',                    containLabel: true                },                xAxis: {                    type: 'value',                    boundaryGap: [0, 0.01]                },                yAxis: {                    type: 'category',                    data: arryaxis                },                series: [                    {                        name: '增长客户',                        type: 'bar',                        data: arrzzl                    },                    {                        name: '客户总量',                        type: 'bar',                        data: arrall                    }                ]            };            myChart.setOption(option);            }        })    }    //客户统计    function loaddemo2(){    var datayear=[];    var path='http://192.168.1.5/api.php/Home/Statistic/querynvbl';    var data={values:{        secret:'776eca99-a1e5-11e9-9897-00163e008b45'    }};    fnPost(path, data, function(ret, err) {        // console.log(JSON.stringify(ret));        // console.log(JSON.stringify(err));        if(ret['flag']=='Success'){        var data=ret['data'];        if(data['year']){            var year=data['year'];            datayear.push({value:year['num1'],name:'18-20岁'});            datayear.push({value:year['num2'],name:'21-30岁'});            datayear.push({value:year['num3'],name:'31-40岁'});            datayear.push({value:year['num4'],name:'41-50岁'});            datayear.push({value:year['num5'],name:'51岁以上'});        }        var myChart2 = echarts.init(document.getElementById('Chart2'),'customed');        var myChart3 = echarts.init(document.getElementById('Chart3'),'customed');        var myChart4 = echarts.init(document.getElementById('Chart4'),'customed');        option2 = {            title:{                text:'客户等级剖析'            },            tooltip : {                trigger: 'item',                formatter: "{a} <br/>{b} : {c} ({d}%)"            },            series : [                {                    name: '客户等级占比',                    type: 'pie',                    radius : '55%',                    center: ['50%', '60%'],                    data:data['csd'],                    itemStyle: {                        emphasis: {                            shadowBlur: 10,                            shadowOffsetX: 0,                            shadowColor: 'rgba(0, 0, 0, 0.5)'                        }                    }                }            ]        };        option3 = {            title:{                text:'客户性别剖析'            },            tooltip : {                trigger: 'item',                formatter: "{a} <br/>{b} : {c} ({d}%)"            },            series : [                {                    name: '客户性别占比',                    type: 'pie',                    radius : '55%',                    center: ['50%', '60%'],                    data:data['sex'],                    itemStyle: {                        emphasis: {                            shadowBlur: 10,                            shadowOffsetX: 0,                            shadowColor: 'rgba(0, 0, 0, 0.5)'                        }                    }                }            ]        };        option4 = {            title:{                text:'客户年龄剖析'            },            tooltip : {                trigger: 'item',                formatter: "{a} <br/>{b} : {c} ({d}%)"            },            series : [                {                    name: '客户年龄占比',                    type: 'pie',                    radius : '55%',                    center: ['50%', '60%'],                    data:datayear,                    itemStyle: {                        emphasis: {                            shadowBlur: 10,                            shadowOffsetX: 0,                            shadowColor: 'rgba(0, 0, 0, 0.5)'                        }                    }                }            ]        };        myChart2.setOption(option2);        myChart3.setOption(option3);        myChart4.setOption(option4);        }    })}</script>

</html>

扫描二维码 模块文档中举荐了2种形式,如没非凡需要,举荐应用第一种。 


//入口
<view class="column-item" @click="fnscanner">

<image class="column-item-ico" src='../../image/co-ico5.png' mode="widthFix"></image><text class="column-item-title">扫码</text>

</view>

//应用

        fnscanner(){            var FNScanner = api.require('FNScanner');            FNScanner.open({                autorotation: true            }, (ret, err) => {                console.log(JSON.stringify(ret));                console.log(JSON.stringify(err));                if(ret){                    if(ret.eventType=='success'){                        api.toast({                            msg:'扫码胜利,行将跳转详情页面'                        })                                }                }                else{                    api.toast({                        msg:'扫码失败,请再次尝试!'                    })                }            });        }

数据列表及分页
数据列表的分页查问,次要应用到的是上拉刷新和下拉刷新动作,在动作的回调中解决须要的参数,来实现数据的加载和刷新。其中解决的参数须要配个后盾提供的接口进行重定义。
<template>

<scroll-view scroll-y class="page" enable-back-to-top refresher-enabled refresher-triggered={refresherTriggered} onrefresherrefresh={this.onrefresherrefresh} onscrolltolower={this.onscrolltolower}>    <view>        <view class="item-box" v-for="(item, index) in list" data-id={item.id}>            <view class="top">                <image class="top-ico" src='../../image/flag01.png' mode="widthFix" v-if="item.flag=='01'"></image>                <image class="top-ico" src='../../image/flag02.png' mode="widthFix" v-else-if="item.flag=='02'"></image>                <image class="top-ico" src='../../image/flag03.png' mode="widthFix" v-else-if="item.flag=='03'"></image>                <image class="top-ico" src='../../image/flag04.png' mode="widthFix" v-else-if="item.flag=='04'"></image>                <image class="top-ico" src='../../image/flag05.png' mode="widthFix" v-else-if="item.flag=='05'"></image>                <image class="top-ico" src='../../image/flag06.png' mode="widthFix" v-else></image>                <text class="top-name">{item.name}</text>                <text class="top-level">★{item.dengji}</text>            </view>            <view class="mid">                <view>                    <text class="mid-tip">录入工夫</text>                    <text>{item.lrsj}</text>                </view>                <view>                    <text class="mid-tip">生日</text>                    <text>{item.birthday}</text>                </view>            </view>            <view class="btm">                <view class="btm-item" data-phone={item.phone} @click="call">                    <image class="btm-ico" src='../../image/TELL.png' mode="widthFix"></image>                    <text>打电话</text>                </view>                <view class="btm-item" data-id={item.id} @click="followRecords">                    <image class="btm-ico" src='../../image/GJ.png' mode="widthFix"></image>                    <text>跟进</text>                </view>                <view class="btm-item" data-id={item.id} @click="saleRecords">                    <image class="btm-ico" src='../../image/XS.png' mode="widthFix"></image>                    <text>销售</text>                </view>            </view>        </view>    </view>    <view class="footer">        <text class="loadDesc">{loadStateDesc}</text>    </view>        <safe-area></safe-area></scroll-view>

</template>
<script>

import $util from '../../utils/utils.js'import {POST} from '../../script/req.js'export default {    name: 'customer',    apiready(){        //设置筛选按钮        api.setNavBarAttr({            rightButtons: [{                text:'筛选',                color:'#ffffff'            }]        });                    api.addEventListener({            name:'navitembtn'        }, (ret)=>{            if(ret.type=='right'){                api.openFrame({                    name: 'customer-select',                    url: 'customer-select.stml',                    rect: {                        x: 0,                        y: 0,                        w: 'auto',                        h: 'auto'                    },                    pageParam: {                        name: 'test'                    }                });            }        })        api.addEventListener({            name:'doSearchCustomer'        }, (ret)=>{            //重置key            this.data.key = '';            // console.log(JSON.stringify(ret));            this.data.status = ret.value.status;            this.data.level = ret.value.level;            this.data.sex = ret.value.sex;            this.loadData();        })        this.data.key = api.pageParam.key;        //console.log(this.data.key);        this.loadData();    },    data() {        return{            key:'',            list:[],            skip: 0,            refresherTriggered: false,            haveMoreData: true,            loading: false,            status:'',            level:'',            sex:''        }    },    computed: {                    loadStateDesc(){            if (this.data.loading || this.data.haveMoreData) {                return '加载中...';            } else if (this.list.length > 0) {                return '没有更多啦';            } else {                return '临时没有内容';            }        }    },    methods: {        loadData(loadMore) {            this.data.loading = true;            var limit = 10;            var skip = loadMore?this.data.skip+limit:0;            var data={                            secret:'',                   key:this.data.key,                limit:limit,                skip:skip,                            userid:api.getPrefs({sync: true,key: 'userid'}),                roleid:api.getPrefs({sync: true,key: 'roleid'}),                organid:api.getPrefs({sync: true,key: 'organid'}),            };            api.showProgress();            POST('Customer/querycustomerlist',data,{}).then(ret =>{                // console.log(JSON.stringify(ret));                if(ret.flag=='Success'){                    let noticedata = ret.data;                    this.data.haveMoreData = noticedata.length == limit;                    if (loadMore) {                        this.data.list = this.data.list.concat(noticedata);                    } else {                        this.data.list = noticedata;                    }                    this.data.skip = skip;                }                else{                    this.data.haveMoreData = false;                    this.data.list=[];                }                this.data.loading = false;                this.data.refresherTriggered = false;                api.hideProgress();            })        },        /*下拉刷新页面*/        onrefresherrefresh(){            this.data.refresherTriggered = true;            this.loadData(false);        },        onscrolltolower() {            if (this.data.haveMoreData) {                this.loadData(true);            }        },        call(e){            var phone = e.target.dataset.phone;            api.call({                type: 'tel',                number: phone            });        },        followRecords(e){            var id = e.target.dataset.id;            $util.openWin({                name: 'followRecords',                url: 'followRecords.stml',                title: '客户跟进记录',                pageParam:{                    id:id                }            });        },        saleRecords(e){            var id = e.target.dataset.id;            $util.openWin({                name: 'saleRecords',                url: 'saleRecords.stml',                title: '客户销售记录',                pageParam:{                    id:id                }            });        }    }}

</script>
<style>

.page {    height: 100%;    background-color: #f0f0f0;}.item-box{    margin: 10px;    background-color: #ffffff;    border-radius: 5px;    padding: 10px;}.top{    flex-flow: row nowrap;    align-items: center;    justify-content: space-between;}.mid{    flex-flow: row nowrap;    align-items: center;    justify-content: space-between;    padding: 10px 0;}.mid-tip{    font-size: 13px;    color: #666666;}.top-level{    color: #ffd700;}.top-ico{    width: 30px;}.top-name{    font-size: 20px;}.btm{    flex-flow: row nowrap;    align-items: center;    justify-content: space-between;    padding-top: 10px;    border-top: 1px solid #ccc;}.btm-item{    flex-flow: row nowrap;    align-items: center;    justify-content: center;}.btm-ico{    width: 20px;    padding-right: 5px;}.footer {    height: 44px;    justify-content: center;    align-items: center;}.loadDesc {    width: 200px;    text-align: center;}

</style>
导航栏底部呈现“白边”问题解决

如果navigationBar的背景设置了其余色彩,shadow应用默认的色彩,如果页面背景设置成彩色,会有条显著的“白边”成果,这时须要通过设置shadow的色彩来打消“白边”。

1.可在须要的页面通过setNavBarAttr进行设置,具体色彩值依据状况自行抉择。
api.setNavBarAttr({

shadow:'#000000'

});

2.如果全局应用,则可在index.json中 设置。

后盾代码
<?php
namespace Home\Controller;
require 'vendor/autoload.php'; // 留神地位肯定要在 引入ThinkPHP入口文件 之前
use Think\Controller;
use JPush\Client as JPushClient;
class VideoController extends Controller {

  //查问视频会议列表  public function queryvideomeetinglist(){    checkscret('secret');//验证受权码    checkdataPost('limit');//下一次加载多少条    checkdataPost('userid');//用户ID    $limit=$_POST['limit'];    $skip=$_POST['skip'];    if(empty($skip)){      $skip=0;    }        $userid=$_POST['userid'];    $map['_string']='(find_in_set('.$userid.',getvideomeetingusers(id)) and flag<>\'03\') or (userid='.$userid.' and flag<>\'03\')';    $releaseInfo=M()->table('crm_video_audio_meeting')->field('id,title,getcode_value(\'音视频类型\',type) lx,type,flag,getusername(userid) fqr,userid,estimatetime,type,note')->where($map)->limit($skip,$limit)->order('estimatetime desc')->select();        if($releaseInfo){      returnApiSuccess('查问胜利',$releaseInfo);    }    else{      returnApiError( '查问失败!');      exit();    }  }  //查问加入音视频会议人员列表  public function queryvideomeetingpersonlist(){    checkscret('secret');//验证受权码    $releaseInfo=M()->table('crm_user')->field('id as employee_id,name,getorganname(organid) remark,getrolename(roleid) position,pinyin as phonetic')->where($map)->order('organid')->select();        if($releaseInfo){      returnApiSuccess('查问胜利',$releaseInfo);    }    else{      returnApiError( '查问失败!');      exit();    }  }  //减少视频会议  public function addvideomeeting(){    checkscret('secret');//验证受权码    checkdataPost('userid');//用户ID    $userid=$_POST['userid'];    $title=$_POST['title'];    $note=$_POST['note'];    $shijian=$_POST['shijian'];    $ids=$_POST['ids'];    $arrids=explode(',',$ids);    $data['userid']=$userid;    $data['title']=$title;    $data['note']=$note;        $data['estimatetime']=$shijian;    $data['estimatenum']=count($arrids);    $data['type']=count($arrids)>9?'01':'02';//01 音频  02 视频    $data['flag']='01';        $releaseInfo=M()->table('crm_video_audio_meeting')->data($data)->add();    if($releaseInfo){      //增加人员加入会议记录      foreach ($arrids as $v) {        $datap['video_meeting_id']=$releaseInfo;        $datap['userid']=$v;        $res=M()->table('crm_video_audio_meeting_users')->data($datap)->add();        //推送视频会议音讯        try{            //增加音讯记录            $content='有一个视频会议须要您的加入,工夫:'.$shijian;            $datam['title']='视频会议告诉';            $datam['content']=$content;            $datam['shijian']=time();            $datam['flag']='01';//未读            $datam['type']='03';//会议揭示            $datam['fqr']=$userid;            $datam['jsr']=$v;            $resm=M()->table('crm_message')->data($datam)->add();            $jpush = new JPushClient(C('JPUSH_APP_KEY'), C('JPUSH_MASTER_SECRET'));            $response = $jpush->push()                ->setPlatform('all')  //机型 IOS ANDROID                ->addAlias($v)                ->androidNotification($content)                ->iosNotification($content,'',0,true)                ->options(array(                    'apns_production' => true,                ))                ->send();        }        catch(\Exception $e){          returnApiSuccess('增加胜利',$releaseInfo);        }         }       returnApiSuccess('增加胜利',$releaseInfo);    }    else{      returnApiError( '增加失败!');      exit();    }  }  //设置会议状态  public function setmeeting(){    checkscret('secret');//验证受权码    checkdataPost('id');//会议ID    checkdataPost('flag');//会议状态    $flag=$_POST['flag'];    $map['id']=$_POST['id'];    $data['flag']=$flag;    if($flag=='02'){      $data['starttime']=time();    }    else if($flag=='03'){      $data['endtime']=time();    }    $releaseInfo=M()->table('crm_video_audio_meeting')->where($map)->save($data);    if($releaseInfo){      returnApiSuccess('设置胜利',$releaseInfo);    }    else{      returnApiError( '设置失败!');      exit();    }  }  //获取会议信息  public function GetMeetingInfo(){    checkscret('secret');//验证受权码    checkdataPost('id');//会议ID    $id=$_POST['id'];    $map['id']=$id;    $releaseInfo=M()->table('crm_video_audio_meeting')->field('title,note,estimatetime,getusername(userid) fqr')->where($map)->find();    if($releaseInfo){      //获取与会人员      $mapu['video_meeting_id']=$id;      $datau=M()->table('crm_video_audio_meeting_users')->field('userid,getusername(userid) username')->where($mapu)->select();      if($datau){        $releaseInfo['users']=$datau;      }      returnApiSuccess('查问胜利',$releaseInfo);    }    else{      returnApiError( '查问失败!');      exit();    }  }//获取腾讯视频RTC usersigpublic function getQQrtcusersig(){  checkscret('secret');//验证受权码  checkdataPost('userid');//用户ID  $sdkappid=C('sdkappid');  $key=C('usersig_key');  $userid=$_POST['userid'];    require 'vendor/autoload.php';    $api = new \Tencent\TLSSigAPIv2($sdkappid, $key);  $sig = $api->genSig($userid);    if($sig){    returnApiSuccess('查问胜利',$sig);  }  else{    returnApiError( '查问失败,请稍后再试');    exit();  }}

}