Update函数的多帧调用来计算时间(FixUpdate应该也行)Updata函数中,并通过一个流程来实现按键的状态切换。下图中POINTSTATE 枚举用于改变当前按键状态,通过外部修改按键状态,或者自身状态的变更,来实现按键的多种状态监测。(这里不添加拖拽状态,拖拽状态将会另外添加一个拖拽脚本专门用于响应拖拽事件)
public enum POINTSTATE{
NONE,
DOWN,
STAY,
UP,
UPSPACE,
EXIT,
EXITSPACE,
}
public class UIEvent: MonoBehaviour
{
void Update()
{
if (!gameObject.activeSelf) return;
switch(state)
{
case POINTSTATE.NONE: //无状态
return;
case POINTSTATE.DOWN: //按键按下
return;
case POINTSTATE.STAY: //点击停留时
return;
case POINTSTATE.UP: //抬起
return;
case POINTSTATE.UPSPACE:
return;
case POINTSTATE.EXIT: //退出
return;
case POINTSTATE.EXITSPACE:
return;
}
}
}
OnClick方法中没有进行状态改变)[RequireComponent(typeof(Image))] //有图片组件才能被继承
public class UIEvent: MonoBehaviour , IPointerDownHandler, IPointerClickHandler, IPointerExitHandler, IPointerUpHandler
{
//点击回调
Action OnClick = null;
//按下回调
Action OnDown = null;
//抬起回调
Action OnUp = null;
//结束回调
Action OnExit = null;
//当前点击状态
POINTSTATE state = POINTSTATE.NONE;
public void OnPointerClick(PointerEventData eventData)
{
if (OnClick != null) OnClick();
}
public void OnPointerDown(PointerEventData eventData)
{
state = POINTSTATE.DOWN;
if (OnDown != null) OnDown();
}
public void OnPointerUp(PointerEventData eventData)
{
state = POINTSTATE.UP;
if (OnDown != null) OnUp();
}
public void OnPointerExit(PointerEventData eventData)
{
state = POINTSTATE.EXIT;
if (OnDown != null) OnExit();
}
}
//点击回调
Action OnClick = null;
//按下回调
Action OnDown = null;
//抬起回调
Action OnUp = null;
//结束回调
Action OnExit = null;
//长按回调 单次相应
Action OnLongClick = null;
//按下回调 多次相应,且相应速度加快
Action OnPress = null;
// 设置点击间隔(防止短时间大量连点操作)
public static void SetClickSpace(int space)
// 注册一般点击
public static void AddClick(GameObject _go, Action _func)
// 注册按键按下
public static void AddDown(GameObject _go, Action _func)
// 注册按键抬起
public static void AddUp(GameObject _go, Action _func)
// 注册按键退出
public static void AddExit(GameObject _go, Action _func)
// 注册长点击
public static void AddLongClick(GameObject _go, Action _func, float _space = 1f)
// 注册长按
public static void AddPress(GameObject _go, Action _func, float _space = 1f, float _minSpace = 0.1f)
UIEvent的静态接口,给需要的节点添加事件,并在按键按下时,在Update函数中调用已经添加的事件。首先实现一个可能被多次调用的接口, 对传入的对象更改RayCastTarget,并返回UIEvent脚本,没有挂载就添加一个。
这里为了方便起见直接写在UIEvent中,如果想结构更加清晰一点可以分开写
//检查图像并挂载脚本
static UIEvent Get(GameObject _go)
{
Graphic graphic = _go.GetComponent<Graphic>();
if (graphic) graphic.raycastTarget = true;
UIEvent uiEvent = _go.GetComponent<UIEvent>();
if (uiEvent == null) uiEvent = _go.AddComponent<UIEvent>();
return uiEvent;
}
LongClick函数和Press函数可以额外传入长按时间、最短响应间隔时间。#region 外部注册相关接口
// 设置点击间隔
public static void SetClickSpace(int space)
{
timeClickSpace = space;
}
/// <summary>
/// 注册点击
/// </summary>
/// <param name="_go">被注册对象</param>
/// <param name="_func">回调函数</param>
public static void AddClick(GameObject _go, Action _func)
{
UIEvent uIEvent = Get(_go);
if (uIEvent) uIEvent.OnClick = _func;
}
/// <summary>
/// 注册按下
/// </summary>
/// <param name="_go">被注册对象</param>
/// <param name="_func">回调函数</param>
public static void AddDown(GameObject _go, Action _func)
{
UIEvent uIEvent = Get(_go);
if (uIEvent) uIEvent.OnDown = _func;
}
/// <summary>
/// 注册抬起
/// </summary>
/// <param name="_go">被注册对象</param>
/// <param name="_func">回调函数</param>
public static void AddUp(GameObject _go, Action _func)
{
UIEvent uIEvent = Get(_go);
if (uIEvent) uIEvent.OnUp = _func;
}
/// <summary>
/// 注册结束点击
/// </summary>
/// <param name="_go">被注册对象</param>
/// <param name="_func">回调函数</param>
public static void AddExit(GameObject _go, Action _func)
{
UIEvent uIEvent = Get(_go);
if (uIEvent) uIEvent.OnClick = _func;
}
/// <summary>
/// 注册长点击
/// </summary>
/// <param name="_go">被注册对象</param>
/// <param name="_func">回调函数</param>
/// <param name="_space">长按时间</param>
public static void AddLongClick(GameObject _go, Action _func, float _space = 1f)
{
UIEvent uIEvent = Get(_go);
if (uIEvent)
{
uIEvent.timeLongClickSpace = _space;
uIEvent.OnLongClick = _func;
}
}
/// <summary>
/// 注册长按
/// </summary>
/// <param name="_go">被注册对象</param>
/// <param name="_func">回调函数</param>
/// <param name="_space">长按时间</param>
/// <param name="_minSpace">最短响应间隔</param>
public static void AddPress(GameObject _go, Action _func, float _space = 1f, float _minSpace = 0.1f)
{
UIEvent uIEvent = Get(_go);
if (uIEvent)
{
uIEvent.timePressSpace = _space;
uIEvent.OnPress = _func;
uIEvent.timePressMinSpace = _minSpace;
}
}
Update中LongClick和Press事件的执行条件Update中监测到state值变化,就开始一个新的流程如下POINTSTATE.DOWN,初始化需要用到的值Update函数,此时state已被改为POINTSTATE.STAYLongClick和Press回调,有则执行,并且在这个时候,state值不会Update函数内被改变POINTSTATE.UP为止#region 注册相关的属性
//点击回调
Action OnClick = null;
//按下回调
Action OnDown = null;
//抬起回调
Action OnUp = null;
//结束回调
Action OnExit = null;
//长按回调 单次相应
Action OnLongClick = null;
//按下回调 多次相应,且相应速度加快
Action OnPress = null;
//单词击计数器
float timeClick = 0;
//一秒内点击限制
static int timeClickSpace = 10;
//长点击计数器
float timeLongClick = 0;
//长点击生效时长
float timeLongClickSpace = 1.0f;
//长按计数器
float timePress = 0;
//长按递减间隔
float timePressSpace = 1.0f;
//长按递减间隔缓存
float timePressSpaceCache = 0;
//长按最低间隔
float timePressMinSpace = 0.1f;
//当前点击状态
POINTSTATE state = POINTSTATE.NONE;
#endregion
//事件响应流程写在Update中
void Update()
{
if (!gameObject.activeSelf) return;
switch(state)
{
case POINTSTATE.NONE: //无状态
return;
case POINTSTATE.DOWN: //
timeLongClick = Time.time; //记录长点击开始时间
// 如果注册了OnPress事件则直接执行
if (OnPress != null && Time.time - timePress > (1 / timeClickSpace)) OnPress();
timePress = Time.time; //记录长按开始时间
timePressSpaceCache = timePressSpace; // 长按事件响应间隔
state = POINTSTATE.STAY;
return;
case POINTSTATE.STAY: //点击停留时
if(OnLongClick != null) //长点击
{
if(Time.time - timeLongClick > timeLongClickSpace) //到时间了开始执行
{
OnLongClick();
state = POINTSTATE.NONE; //进入结束状态
}
}
if(OnPress != null) //长按
{
float spaceTime = Time.time - timePress; //距离上一次间隔时间
if(spaceTime > timePressSpaceCache && spaceTime > (1 / timeClickSpace))
{
//记录相应时间点
timePress = Time.time;
//缩短相应间隔
timePressSpaceCache *= (2 / 3f);
timePressSpaceCache = Mathf.Max(timePressSpaceCache, timePressMinSpace);
//执行回调
OnPress();
}
}
return;
case POINTSTATE.UP: //抬起
state = POINTSTATE.UPSPACE;
return;
case POINTSTATE.UPSPACE:
return;
case POINTSTATE.EXIT: //退出
state = POINTSTATE.EXITSPACE;
return;
case POINTSTATE.EXITSPACE:
return;
}
}
关于窗口拖拽事件,这里没有选择实现在UIEvent上,只是使用了UIEvent 进行事件注册,过程与之前的方式类似。两个函数分别是
static UIEventDrag GetDrag(GameObject _go)
{
UIEventDrag uIEventDrag = _go.GetComponent<UIEventDrag>();
if (!uIEventDrag) uIEventDrag = _go.AddComponent<UIEventDrag>();
return uIEventDrag;
}
/// <summary>
/// 注册拖拽
/// </summary>
/// <param name="_go">被移动对象</param>
/// <param name="_window">需要点击的对象</param>
public static void AddDrag(GameObject _go, Transform _window)
{
UIEventDrag uIEvent = GetDrag(_go);
uIEvent.IsWindowDrag = true;
uIEvent.Window = _window;
}
UIEventDrag是另一个挂了Nomo的脚本,代码贴在最后面了,它的实现可以相对独立;这里的实现针对性较强,大家用的时候可以根据需要进行自定义 //双击回调
Action OnDoubleClick = null;
//双击最大间隔时间
private float timeDoubleClick = 0.5f;
//双击计时
private float timeDoubleClickCount = 0f;
void Update()
{
switch (pointState)
{
case POINTERSTATE.UP:
//Log.Print("抬起");
//双击
if (OnDoubleClick != null && Time.time - timeDoubleClickCount <= timeDoubleClick) OnDoubleClick();
timeDoubleClickCount = Time.time;
break;
}
}
//注册双击
public static void AddDoubleClick(GameObject _go, Action _fun, float _space = 0.7f)
{
var uievent = Get(_go);
if (uievent)
{
uievent.timeDoubleClick = _space;
uievent.OnDoubleClick = _fun;
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public enum POINTSTATE{
NONE,
DOWN,
STAY,
UP,
UPSPACE,
EXIT,
EXITSPACE,
}
[RequireComponent(typeof(Image))] //有图片组件才能被继承
public class UIEvent: MonoBehaviour , IPointerDownHandler, IPointerClickHandler, IPointerExitHandler, IPointerUpHandler
{
#region 注册相关的属性
//点击回调
Action OnClick = null;
//按下回调
Action OnDown = null;
//抬起回调
Action OnUp = null;
//结束回调
Action OnExit = null;
//长按回调 单次相应
Action OnLongClick = null;
//按下回调 多次相应,且相应速度加快
Action OnPress = null;
//双击回调
Action OnDoubleClick = null;
//单词击计数器
float timeClick = 0;
//一秒内点击限制
static int timeClickSpace = 10;
//双击最大间隔时间
private float timeDoubleClick = 0.5f;
//双击计时
private float timeDoubleClickCount = 0f;
//长点击计数器
float timeLongClick = 0;
//长点击生效时长
float timeLongClickSpace = 1.0f;
//长按计数器
float timePress = 0;
//长按递减间隔
float timePressSpace = 1.0f;
//长按递减间隔缓存
float timePressSpaceCache = 0;
//长按最低间隔
float timePressMinSpace = 0.1f;
//当前点击状态
POINTSTATE state = POINTSTATE.NONE;
#endregion
//事件响应流程写在Update中
void Update()
{
if (!gameObject.activeSelf) return;
switch(state)
{
case POINTSTATE.NONE: //无状态
return;
case POINTSTATE.DOWN: //
timeLongClick = Time.time; //记录长点击开始时间
// 如果注册了OnPress事件则直接执行
if (OnPress != null && Time.time - timePress > (1 / timeClickSpace)) OnPress();
timePress = Time.time; //记录长按开始时间
timePressSpaceCache = timePressSpace; // 长按事件响应间隔
state = POINTSTATE.STAY;
return;
case POINTSTATE.STAY: //点击停留时
if(OnLongClick != null) //长点击
{
if(Time.time - timeLongClick > timeLongClickSpace) //到时间了开始执行
{
OnLongClick();
state = POINTSTATE.NONE; //进入结束状态
}
}
if(OnPress != null) //长按
{
float spaceTime = Time.time - timePress; //距离上一次间隔时间
if(spaceTime > timePressSpaceCache && spaceTime > (1 / timeClickSpace))
{
//记录相应时间点
timePress = Time.time;
//缩短相应间隔
timePressSpaceCache *= (2 / 3f);
timePressSpaceCache = Mathf.Max(timePressSpaceCache, timePressMinSpace);
//执行回调
OnPress();
}
}
return;
case POINTSTATE.UP: //抬起
//双击
if (OnDoubleClick != null && Time.time - timeDoubleClickCount <= timeDoubleClick) OnDoubleClick();
timeDoubleClickCount = Time.time;
state = POINTSTATE.UPSPACE;
return;
case POINTSTATE.UPSPACE:
return;
case POINTSTATE.EXIT: //退出
state = POINTSTATE.EXITSPACE;
return;
case POINTSTATE.EXITSPACE:
return;
}
}
//检查图像并挂载脚本
static UIEvent Get(GameObject _go)
{
Graphic graphic = _go.GetComponent<Graphic>();
if (graphic) graphic.raycastTarget = true;
UIEvent uiEvent = _go.GetComponent<UIEvent>();
if (uiEvent == null) uiEvent = _go.AddComponent<UIEvent>();
return uiEvent;
}
static UIEventDrag GetDrag(GameObject _go)
{
UIEventDrag uIEventDrag = _go.GetComponent<UIEventDrag>();
if (!uIEventDrag) uIEventDrag = _go.AddComponent<UIEventDrag>();
return uIEventDrag;
}
#region 外部注册相关接口
// 设置点击间隔
public static void SetClickSpace(int space)
{
timeClickSpace = space;
}
/// <summary>
/// 注册点击
/// </summary>
/// <param name="_go">被注册对象</param>
/// <param name="_func">回调函数</param>
public static void AddClick(GameObject _go, Action _func)
{
UIEvent uIEvent = Get(_go);
if (uIEvent) uIEvent.OnClick = _func;
}
/// <summary>
/// 注册按下
/// </summary>
/// <param name="_go">被注册对象</param>
/// <param name="_func">回调函数</param>
public static void AddDown(GameObject _go, Action _func)
{
UIEvent uIEvent = Get(_go);
if (uIEvent) uIEvent.OnDown = _func;
}
/// <summary>
/// 注册抬起
/// </summary>
/// <param name="_go">被注册对象</param>
/// <param name="_func">回调函数</param>
public static void AddUp(GameObject _go, Action _func)
{
UIEvent uIEvent = Get(_go);
if (uIEvent) uIEvent.OnUp = _func;
}
/// <summary>
/// 注册结束点击
/// </summary>
/// <param name="_go">被注册对象</param>
/// <param name="_func">回调函数</param>
public static void AddExit(GameObject _go, Action _func)
{
UIEvent uIEvent = Get(_go);
if (uIEvent) uIEvent.OnClick = _func;
}
/// <summary>
/// 注册长点击
/// </summary>
/// <param name="_go">被注册对象</param>
/// <param name="_func">回调函数</param>
/// <param name="_space">长按时间</param>
public static void AddLongClick(GameObject _go, Action _func, float _space = 1f)
{
UIEvent uIEvent = Get(_go);
if (uIEvent)
{
uIEvent.timeLongClickSpace = _space;
uIEvent.OnLongClick = _func;
}
}
/// <summary>
/// 注册长按
/// </summary>
/// <param name="_go">被注册对象</param>
/// <param name="_func">回调函数</param>
/// <param name="_space">长按时间</param>
/// <param name="_minSpace">最短响应间隔</param>
public static void AddPress(GameObject _go, Action _func, float _space = 1f, float _minSpace = 0.1f)
{
UIEvent uIEvent = Get(_go);
if (uIEvent)
{
uIEvent.timePressSpace = _space;
uIEvent.OnPress = _func;
uIEvent.timePressMinSpace = _minSpace;
}
}
/// <summary>
/// 注册拖拽
/// </summary>
/// <param name="_go">被移动对象</param>
/// <param name="_window">需要点击的对象</param>
public static void AddDrag(GameObject _go, Transform _window)
{
UIEventDrag uIEvent = GetDrag(_go);
uIEvent.IsWindowDrag = true;
uIEvent.Window = _window;
}
//注册双击
public static void AddDoubleClick(GameObject _go, Action _fun, float _space = 0.7f)
{
var uievent = Get(_go);
if (uievent)
{
uievent.timeDoubleClick = _space;
uievent.OnDoubleClick = _fun;
}
}
#endregion
#region 点击重写
public void OnPointerClick(PointerEventData eventData)
{
//检查间隔
if (Time.time - timeClick < 1.0f / timeClickSpace) { return; }
if (OnClick != null)
{
OnClick();
timeClick = Time.time;
}
}
public void OnPointerDown(PointerEventData eventData)
{
state = POINTSTATE.DOWN;
if (OnDown != null) OnDown();
}
public void OnPointerUp(PointerEventData eventData)
{
state = POINTSTATE.UP;
if (OnDown != null) OnUp();
}
public void OnPointerExit(PointerEventData eventData)
{
state = POINTSTATE.EXIT;
if (OnDown != null) OnExit();
}
#endregion
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class UIEventDrag : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
public bool IsWindowDrag = false;
public Transform Window;
private Vector2 winDiffPoint;
private Vector2 winDeltaPoint;
private float lx;
private float ly;
public void OnDrag(PointerEventData _eventData)
{
if (IsWindowDrag)
{
winDeltaPoint = _eventData.position - winDiffPoint;
Window.localPosition += new Vector3(winDeltaPoint.x, winDeltaPoint.y, 0);
if (Window.localPosition.x > lx) Window.localPosition = new Vector3(lx, Window.localPosition.y, 0);
if (Window.localPosition.x < -lx2) Window.localPosition = new Vector3(-lx2, Window.localPosition.y, 0);
if (Window.localPosition.y > ly) Window.localPosition = new Vector3(Window.localPosition.x, ly, 0);
if (Window.localPosition.y < -ly2) Window.localPosition = new Vector3(Window.localPosition.x, -ly2, 0);
winDiffPoint = _eventData.position;
}
}
public void OnBeginDrag(PointerEventData _eventData)
{
if (IsWindowDrag)
{
winDiffPoint = _eventData.position;
RectTransform rt = GetComponent<RectTransform>();
lx = (Screen.width - rt.rect.width) / 2 - transform.localPosition.x;
lx2 = (Screen.width - rt.rect.width) / 2 + transform.localPosition.x;
ly = (Screen.height - rt.rect.height) / 2 - transform.localPosition.y;
ly2 = (Screen.height - rt.rect.height) / 2 + transform.localPosition.y;
}
}
public void OnEndDrag(PointerEventData eventData)
{
}
}
是否有简单的方法来更改默认ISO格式(yyyy-mm-dd)的ActiveAdmin日期过滤器显示格式? 最佳答案 您可以像这样为日期选择器提供额外的选项,而不是覆盖js:=f.input:my_date,as::datepicker,datepicker_options:{dateFormat:"mm/dd/yy"} 关于ruby-on-rails-事件管理员日期过滤器日期格式自定义,我们在StackOverflow上找到一个类似的问题: https://s
电脑0x0000001A蓝屏错误怎么U盘重装系统教学分享。有用户电脑开机之后遇到了系统蓝屏的情况。系统蓝屏问题很多时候都是系统bug,只有通过重装系统来进行解决。那么蓝屏问题如何通过U盘重装新系统来解决呢?来看看以下的详细操作方法教学吧。 准备工作: 1、U盘一个(尽量使用8G以上的U盘)。 2、一台正常联网可使用的电脑。 3、ghost或ISO系统镜像文件(Win10系统下载_Win10专业版_windows10正式版下载-系统之家)。 4、在本页面下载U盘启动盘制作工具:系统之家U盘启动工具。 U盘启动盘制作步骤: 注意:制作期间,U盘会被格式化,因此U盘中的重要文件请注
在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList()Obt
需求:要创建虚拟机,就需要给他提供一个虚拟的磁盘,我们就在/opt目录下创建一个10G大小的raw格式的虚拟磁盘CentOS-7-x86_64.raw命令格式:qemu-imgcreate-f磁盘格式磁盘名称磁盘大小qemu-imgcreate-f磁盘格式-o?1.创建磁盘qemu-imgcreate-fraw/opt/CentOS-7-x86_64.raw10G执行效果#ls/opt/CentOS-7-x86_64.raw2.安装虚拟机使用virt-install命令,基于我们提供的系统镜像和虚拟磁盘来创建一个虚拟机,另外在创建虚拟机之前,提前打开vnc客户端,在创建虚拟机的时候,通过vnc
我正在尝试将以下SQL查询转换为ActiveRecord,它正在融化我的大脑。deletefromtablewhereid有什么想法吗?我想做的是限制表中的行数。所以,我想删除少于最近10个条目的所有内容。编辑:通过结合以下几个答案找到了解决方案。Temperature.where('id这给我留下了最新的10个条目。 最佳答案 从您的SQL来看,您似乎想要从表中删除前10条记录。我相信到目前为止的大多数答案都会如此。这里有两个额外的选择:基于MurifoX的版本:Table.where(:id=>Table.order(:id).
因为我现在正在做一些时间测量,我想知道是否可以在不使用Benchmark类或命令行实用程序time的情况下测量用户时间或系统时间。使用Time类只显示挂钟时间,而不显示系统和用户时间,但是我正在寻找具有相同灵active的解决方案,例如time=TimeUtility.now#somecodeuser,system,real=TimeUtility.now-time原因是我有点不喜欢Benchmark,因为它不能只返回数字(编辑:我错了-它可以。请参阅下面的答案。)。当然,我可以解析输出,但感觉不对。*NIX系统的time实用程序也应该可以解决我的问题,但我想知道是否已经在Ruby中实
这是我在ActiveAdmin中的自定义页面ActiveAdmin.register_page"Settings"doaction_itemdolink_to('Importprojects','settings/importprojects')endcontentdopara"Text"endcontrollerdodefimportprojectssystem"rakedataspider:import_projects_ninja"para"OK"endendend我想做的是,当我单击“导入项目”按钮时,我想在Controller中执行rake任务。但是我无法访问该方法。可能是什
在Ruby中,以毫秒为单位获取自纪元(1970)以来的当前系统时间的正确方法是什么?我试过了Time.now.to_i,好像不是我想要的结果。我需要结果显示毫秒并且使用long类型,而不是float或double。 最佳答案 (Time.now.to_f*1000).to_iTime.now.to_f显示包含十进制数字的时间。要获得毫秒数,只需将时间乘以1000。 关于ruby-以毫秒为单位获取当前系统时间,我们在StackOverflow上找到一个类似的问题:
例如,假设我有一个名为Products的模型,并且在ProductsController中,我有以下代码用于product_listView以显示已排序的产品。@products=Product.order(params[:order_by])让我们想象一下,在product_listView中,用户可以使用下拉菜单按价格、评级、重量等进行排序。数据库中的产品不会经常更改。我很难理解的是,每次用户选择新的order_by过滤器时,rails是否必须查询,或者rails是否能够以某种方式缓存事件记录以在服务器端重新排序?有没有一种方法可以编写它,以便在用户排序时rails不会重新查询结果
关闭。这个问题需要更多focused.它目前不接受答案。想改进这个问题吗?更新问题,使其只关注一个问题editingthispost.关闭8年前。Improvethisquestion我们有以下(以及更多)系统,我们将数据从一个应用推送/拉取到另一个:托管CRM(InsideSales.com)Asterisk电话系统(内部)横幅广告系统(openx,我们托管)潜在客户生成系统(自行开发)电子商务商店(spree,我们托管)工作板(本土)一些工作网站抓取+入站工作提要电子邮件传送系统(如Mailchimp,自主开发)事件管理系统(如eventbrite,自主开发)仪表板系统(大量图表和