<listing id="r7f1v"></listing>
<listing id="r7f1v"><var id="r7f1v"></var></listing><listing id="r7f1v"><cite id="r7f1v"><i id="r7f1v"></i></cite></listing>
<listing id="r7f1v"><cite id="r7f1v"></cite></listing>
<listing id="r7f1v"></listing>
<listing id="r7f1v"></listing>
<thead id="r7f1v"><cite id="r7f1v"></cite></thead>
<listing id="r7f1v"></listing>
<listing id="r7f1v"><cite id="r7f1v"></cite></listing>
<listing id="r7f1v"></listing>
<listing id="r7f1v"></listing>
<progress id="r7f1v"><var id="r7f1v"></var></progress>
<listing id="r7f1v"></listing>
APP開發平臺 > Blog > APICloud AVM框架開發CRM客戶管理系統

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文件,下載連接

文件目錄

image.png
image.png
image.png

<!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/> : {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/> : {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/> : {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 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();
  }
}

}


高效的App定制平臺,標準化、便宜、快!

提交APP定制開發需求
欧美激情一区二区,国产精品区免费视频,欧美激情视频在线播放,久久久亚洲综合久久98,久久国产精品99精品国产