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监听事件,通过事件名和附带的参数进行其他操作。举例:登录成功之后,需要在个人中心加载个人信息,在首页加载相关个人的数据;添加某项数据之后,需要进行刷新列表等等。
APICloud AVM框架 开发CRM客户管理系统
文章图片

APICloud AVM框架 开发CRM客户管理系统
文章图片

methods: {
login(){ if (!this.data.username) { this.showToast("姓名不能为空"); return; } if (!this.data.password) { this.showToast("密码不能为空"); return; } var data=https://www.it610.com/article/{ 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,可情况全部缓存,也可选择清除多少天前的缓存。
APICloud AVM框架 开发CRM客户管理系统
文章图片

消息推送采用极光推送,需要集成ajpush模块。
APICloud AVM框架 开发CRM客户管理系统
文章图片

具体使用方法可详细阅读官方模块文档。
APICloud AVM框架 开发CRM客户管理系统
文章图片

推送功能初始化需要在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(); } }

用户视频画面需要根据当前视频用户数,进行计算调整。



.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; }


通讯录
基于scroll-view进行开发实现通讯录功能,可直接拨打电话



.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; }


echarts图表
由于AVM无法解析cavans,所有图表相关的页面采用的是html,页面添加在html文件夹中,通过open.frame()进行打开。采用的Echarts.js.可去echarts官方下载,如果图标不复杂,建议使用自定义下载只选择用到的控件,减小文件体积;样式也可以自定义然后下载转述js文件,下载连接
文件目录
APICloud AVM框架 开发CRM客户管理系统
文章图片

APICloud AVM框架 开发CRM客户管理系统
文章图片

APICloud AVM框架 开发CRM客户管理系统
文章图片



统计-客户 - 锐客网 body{background:#efefef; padding: 10px 10px 50px 10px; } .chart-box{ background-color: #ffffff; border-radius: 5px; margin-top: 10px; }


扫描二维码 模块文档中推荐了2种方式,如没特殊需求,推荐使用第一种。
APICloud AVM框架 开发CRM客户管理系统
文章图片

APICloud AVM框架 开发CRM客户管理系统
文章图片

//入口

扫码


//使用
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:'扫码失败,请再次尝试!' }) } }); }

数据列表及分页
数据列表的分页查询,主要使用到的是上拉刷新和下拉刷新动作,在动作的回调中处理需要的参数,来实现数据的加载和刷新。其中处理的参数需要配个后台提供的接口进行重定义。



.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; }


导航栏底部出现“白边”问题处理
如果navigationBar的背景设置了其他颜色,shadow使用默认的颜色,如果页面背景设置成黑色,会有条明显的“白边”效果,这时需要通过设置shadow的颜色来消除“白边”。
1.可在需要的页面通过setNavBarAttr进行设置,具体颜色值根据情况自行选择。
api.setNavBarAttr({
shadow:'#000000'

});
2.如果全局使用,则可在index.json中 设置。
APICloud AVM框架 开发CRM客户管理系统
文章图片

后台代码
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(); } }

}

    推荐阅读