h5机器人对话式交互设计方案(干货)
前言:
今天要分享的是我最近在开发的h5
机器人诊断的心路历程,本文主要讲述的是我自己的方案跟思路,以及琢磨的过程(仅供参考,以下内容均以我的产品为例进行讲解),先上效果图。
第一张图是开始的对话,第二张图是中途的修改,第三张图是最终的诊断结果,大致就是这么个交互过程。
从零开始的思路开启
由于之前没有做过类似的东西,所以我跟大家一样,思路比较的林乱,也不知道要开始从哪里着手,但是不要慌,我们可以慢慢来分析一下。
界面结构分析
首先我分析了功能界面,主要包含四种类型动态的内容,
第一:用户的回复(纯文本),
第二:机器人单选对话块。
第三:机器人多选对话块,
第四:诊断结果内容块。
因为机器人对话是个动态的交互过程,所以不能写死流程,用隐藏显示块的方式来构建一个完整的界面。所以需要把动态库有组件化的思维封装起来。
(这里声明一下,我并没有采用当前比较流行vue
框架来构建组件,因为老夫的项目是dom
操作框架。)vue
的方式是将数据跟view
分离,用数据来渲染出整体界面,待会儿会讲解一下,vue
的处理方式。我这里用的是
这样的方式来生成dom
结构体。
接口设计分析
之前本来是准备把流程写在前端,让前端来决定每个问题问什么内容,后来这个方案很快的就被我推翻了,如果全部的逻辑写在前端,那就太死板了,没有生命力,而且接口也变得没有活性。特别是针对上下文逻辑紧密结合的对话式分析的话,逻辑万万不能存在前端。
所以我推荐的方案是:前后端逻辑分离。
前端只接收服务端的问题,并回答服务端的问题,服务端接收到问题的答案之后,继续提问新的问题。前端全程不理会我之前回答的是什么,现在回答的是什么,只管回答即可。
界面操作性
作为一个对话诊断,中途修改回答的内容,是个硬需求。
所以我给的设计是最新的问题,直接操作回答,之前的问题,一律是不能随便再去操作,也就是变成disable
模式,(disable
模式是针对事件跟样式的,事件是代码去拦截,样式是根据disable
来展现成不可操作的css
)
当然点击右边的修改按钮,之后可以允许那一步的问题可以修改,然后下面的问题重新根据最新的回答来提问。
由于我是dom
操作框架,所以我的方式是根据当前的问题块是否是最新的机器人对话框,如果是,就认为是当前问题可以操作,如果小于当前问题块的index
,则认为是过去回答的问题,则不允许修改。
这里的会有个问题,如果用户要中途修改的时候,这个逻辑明显就不符合要求,不严谨,所以我需要有两个条件来限定这个不允许修改的状态。
两个限定条件:
一:index
二:对话框是否含有'is-disabled'
的class
(这里来说说vue
的框架可以这么做,他的组件元素是否点击或者选择,都是由他对应的控制参数来决定的,所以不用dom
这样的繁琐,参数数据控制即可),所以我个人认为这种方式在操作控制方面是比dom
操作要具有一定的优势。
回退还原问题
上面已经介绍了正向所需要的考虑流程,可以一步步的走向到最终的诊断结果。
但是你做过h5
,特别是移动端的h5
的时候,你就知道什么叫回退问题了。
就是你机器人诊断到疾病结果的时候,点击疾病栏目,跳转到疾病的详情界面,然后再后退的时候,你就会发现你的机器人诊断界面有可能会刷新到最开始起点,之前的对话内容都没有了。
我这里简单的归类了一下刷新情况
(android
版的微信浏览器,chrome
浏览器回退会刷新)
(ios
版的微信浏览器回退不会刷新)。
作为一个复杂的诊断对话,好不容易选完了,一回退说没就没,从头开始,这个估计是人都能难接受。所以我们要解决它。
回退还原问题解决思路
我先说会刷新的情况,(不要以为回退不刷新就没有问题了,有彩蛋
,还是要处理一下),
当我们的浏览器回退会刷新的时候,我们需要理清楚两个问题,
1.数据从哪里找回来,
2.数据找回来了,之后,组件的事件怎么办,是不是会丢失,这是我们最需要考虑的问题。
我为了解决这个问题尝试过很多方案,我之前在尝试的时候,一直在想我如果只保留dom
结构,那我的事件岂不是就丢失了吗,因为我的组件都是动态的生成dom
,而且他们的事件也都是在他们生成dom
结构的时候,动态添加上去的,所以我当时觉得,光光保留dom
结构是不行的,所以我去找了一些能保留dom
节点,同时能保留dom
自身的事件的方法。
还别说,还真被我找到了一种,就是几乎没什么人用过的jquery
框架的clone()
方法,这个方法可以带参数,默认是false
,如果clone(false)
的话,可以完整的克隆dom
结构。但是没有克隆事件。如果clone(true)
的话,可以完整的克隆dom
结构,同时还可以保留自身的事件,简直牛逼。$("p").clone(true)
。当我以为这就是我的救星的时候,我却发现了一个更操蛋的事情,
这个clone
出来的对象,只能放在变量里面,没法转成字符串,存在本地,或者其他地方。我试了很多次之后,放弃了,没法保存有个卵用。还是不舍的放弃了。
后面我调整了思路,如果我先把dom
的结构保存下来,还原了,事件我是不是可以在其他地方处理。
结果还是被我想出来了,事件不要动态化添加,就在界面一开始的时候,在最外层的dom
节点上给各个可能出现的组件元素on
上点击事件。这样我还原了dom
结构之后,事件还可以用,完美。
别高兴太早。我们的组件他的事件的处理范围是不该超过他本身的对话框的范畴,这又怎么办了,有办法!如图
看见mainPart
了没有,如此的骚操作就可以解决上面的问题。
这样两者结合,就可以实现还原操作。dom
结构可以这么获取var chatContent = $("#main-chat-box")[0].innerHTML;
然后把这个chatContent
,存到localstorage
里面。等返回的时候,刷新的时候先去localstorage
去找是否有chatContent
的数据,如果有就拿它还原对话的内容界面。
存缓存的时机
这里的缓存数据要什么时候存,什么时候取呢?
我推荐一个方案:就是点击疾病栏目跳详情的时候,要离开当前的界面的时候,存一份到localstorage
,回退的时候,再拿出来,还原上去,并记住及时的清理掉localstorage
的缓存数据。
(彩蛋
来了,这样的代码操作导致一个问题,就是那些不刷新的浏览器,存了一份缓存数据,没有机会去删除了,也就是你下次进入这个机器人界面的时候,就会把上次缓存的东西显示出来,是不是很操蛋,别急,俺有办法,让他也变得回退刷新就好了,这个思路不错)。
让回退不刷新的浏览器,回退也刷新的骚操作
$(function(){ var isPageHide = false; window.addEventListener('pageshow', function () { if (isPageHide) { window.location.reload(); } }); window.addEventListener('pagehide', function () { isPageHide = true; }); }
这样就可以了,骚的不像话,亲测有效。
总结:
一番长篇大论之后,文章也基本讲完了,如果你耐心的看完了,恭喜你,又习得一招骚操作,我会不定期的书写技术文章,有兴趣的可以关注我的公众号:锵哥的觉悟。最新文章,一手沙发。拜拜