一个简单H5投票页面的踩坑记录
前端时间公司要做一个投票相关的传统H5页面,要求对于每组投票要有限制,但又不能完全限制。听到这里我就懵逼了,这怎么做呢。最后制定的规则是:每人每天只能投10票。感觉好像这样的功能都在后端,前端只需要处理用户的操作逻辑就ok了。最后证明自己很傻很天真!
魔怔开启
后端同学说需求方不需要用户登录,你直接Cookie存一下就可以了。开始我以为是让我把Cookie附带传给他呢,就因为这最后接口写好后发现他的意思是我在前端存储这些信息!!!既然这样,没办法就自己吭哧吭哧搞呗。
重新梳理需求&制定方案
既然要存储数据,那就从头至尾重新梳理下需求:
1.不需要用户登录,要求每个人限投10票(其实只有用设备来识别)
2.投完票后再次进入页面要显示“已投票”
3.奖项列表页和公司详情页投票有关联性
基于以上的需求,以及后端同学给的数据结构。拟定的前端存储的数据结构
后端给的数据结构是这样的
奖项投票数据
{
"reward_name": "奖项7",
"reward_id": 1222,
"votes_count": 10, # 可投票的候选数
"candidates": [
[
"产品68", # 公司名称
"16856", # candidate_id
"23" # 投票数
],
[
"公司67",
"16854",
"1"
],
[
"产品66",
"16852",
"4252"
]
]
}
公司详情投票
{
"candidate_id": 16894,
"rewards": [
[
"2017最受机构青睐私募基金公司·股票型", #奖项名称
"77", #奖项ID
"0", #奖项可参与投票的公司数量,为0时,不可投票
"32" # 被投票数
]
],
"products": [
{
"name": "兴业信托•朱雀18期",
"candidate_id": 16900,
"rewards": [
[
"2017最具人气私募基金·股票型(三年期)",
"80",
"10", # 结构及其含义 同上
"32" # 被投票数
]
]
}
],
"candidate_name": "上海朱雀资产管理公司"
}
说实话,这样的数据结构在我看来相当不友好。要不是后端同学当面说明真是完全不知所以,沟通了一下发现需求方给的数据更加扯淡,最后能抽象成这样已经很不错了。没办法,既然接口好了,那就将就一下吧。
想了想如果用Cookie的话,对于简单的数据结构还可以。但是显然这这里不太适合,因为最后的这数据结构不可能太简单。我设计的前端数据存储结构是这样:
{
"rewards_id": 23, //奖项id
"count": 10, //可投票数
"candidates":[45, 34, 44, 32] //已投公司
}
这样的话准备用localStorage来进行存储,但是明天怎么清空本地存储记录呢。那同样存个时间戳,每次登陆的时候去检测是否超过一天,如果超过就全部清空本地存储。看来这样可行。
每次投完票,就去遍历localstore。查看rewards_id是否存在,存在则去检索其中的candidates是否有id,没有就加进去,票数-1,发送请求。否则就return false
代码如下
//投票操作
$('.presents-box').on('click', '.js-vote', function() {
var that = $(this);
var rewards_id = that.parents().parents().parents().attr('reward_id');
var candidates_id = that.attr('candidates_id');
var flag = true;
localArr = JSON.parse(localStorage.getItem('presents'));
if(localArr != null) {
for(var i in localArr) {
if(localArr[i].rewards_id == rewards_id) {
if(localArr[i].count > 0) {
localArr[i].candidates.push(candidates_id);
localArr[i].count -= 1;
localStorage.setItem('presents', JSON.stringify(localArr));
} else {
alert('您已不能再投票!');
return false;
}
flag = false;
}
}
if(flag) {console.log((parseInt($('.detail-votes').attr('count')) - 1))
var obj = {
rewards_id: parseInt(rewards_id),
count: (parseInt($('.detail-votes').attr('count')) - 1),
candidates:[candidates_id]
};
localArr.push(obj);
localStorage.setItem('presents', JSON.stringify(localArr));
}
}
//投票请求
$.ajax({
type: 'post',
url: 'http://www.nbd.com.cn/activity/jdj2/votes?reward_id='+rewards_id+'&candidate_id='+candidates_id,
success: function(res) {
that.parents().parents().addClass('voted');
that.removeClass('js-vote');
that.text('已投票');
}
});
});
重刷新要显示已投结果,就用上面存储的本地数据去操作dom。这样需求就基本实现了,收工。
投票后的结果如图,
刷新后结果不变,跳转到公司详情页的投票页,已投也会显示。如图,
开始踩坑
第一脚坑
这样测试了几个数据,没什么问题。但当真是数据上去之后就出现了问题,投票好像老是不对。开始快速查原因,从逻辑开始,好像逻辑没有问题,只有一步一步打印结果。最后发现这个坑是我自己造成的
就是字段的名字给写错了,真是自己的坑自己填。
第二脚坑
在安卓上没有任何问题,当在ios上记录存储数据好像就有些问题了,刷新后不能记录。一开始我以为是平台差异性,在操作dom上会有些许差异。然后真机测试发现没有问题。那是怎么回事呢?设备上不好调试,但这种现象又只出现在真机设备上。之后copy一份在真机上调试打印数据,一点一点排查最终发现问题所在。
oldTime是存储的本地时间,当第一次时localStorage是null,并且在iOS上是string。而在Android上是null,所以在Android上没问题,在iOS上每次都是true。所以都会清除掉存储的本地数据。然后就做了如下修改,
var arr = JSON.parse(localStorage.getItem('presents')),
nowTime = nowTime = new Date().getDate(),
oldTime = JSON.parse(localStorage.getItem('time')),
newArr = [];
//iOS上
oldTime = !!(navigator.userAgent).match(/(i[^;]+;( U;)? CPU.+Mac OS X/) && (oldTime == 'null') ? null : oldTime;
//固定时间间隔清除localStorage
if(Math.abs(nowTime - oldTime) >= 1 || oldTime == null ) {
localStorage.clear();
arr = JSON.parse(localStorage.getItem('presents'));
}
其实说坑也算不上很大,但是遇到的时候也是蛮坑人的。还有就是写代码变量名一定要明晰,造成不必要的麻烦。