文章摘要moonshot-v1-8k
Standby API Responsing..
撤销编辑
说到消息“撤回”,应该能想到 qq 或者微信提供的消息撤回功能,应该算是互联网上的一剂后悔药,这样做不仅能减少评论错误,还能极大程度提升用户使用体验,这两全其美的事情何乐而不为,可我们修改的对象 Valine 一如既往的没有提供这些人性化的功能,所以我们还得和以前一样看看文档写写 bug
才能把功能捣鼓出来..(想法来自 @Zsedczy 的评论重新编辑和撤销删除)
具体实现
既然又是 leancloud 那就先聊下,因为 leancloud 官方文档里有写到一个 revert()
方法可以撤销尚未保存的修改,不过我捣鼓半天没看懂这玩意咋能套进 Valine 实现保存数据前修改再保存,所以放弃了这个思路,转而像更简单粗暴的“数据更新”方法。
实现思路
同样的,利用 leancloud 数据储存的数据更新方法,对已经成功储存的指定数据进行修改再储存,简单来说就是更新数据(和之前更新点赞数据相似)当 Valine 提交评论后我们提供按钮对指定 id 的数据进行查询并修改的过程,而撤回评论则是使用 leancloud 提供的 destory()
方法进行云端数据的删除操作。
实践
了解完思路我们仍然从 Valine.js 下手,首先新增/修改按钮,定位到 <button type="button" title="Ctrl+Enter" class="vsubmit vbtn">' + e.locale.ctrl.reply + '</button>
修改为
<button type="button" title="Ctrl+Enter" id="pushBtn" class="vsubmit vbtn">' + e.locale.ctrl.reply + '</button><button type="button" id="repushBtn" class="vsubmit vbtn" style="display:none"> 重新提交 </button>
我们需要找到 valine 提交评论数据后的位置,定位到 n.setACL(I()),n.save().then(function(e){
后添加
let input = _.comment.value, //输入框评论(已提交)
objId = e.id, //评论id(已提交)
redo = AV.Object.createWithoutData('Comment', objId), //更新当前评论数据方法
el = document.createElement("div"),
div = '<p>评论<ins style="color:#fff"> '+input+' </ins>已成功提交!你还可以 <button id="redo"> 重新编辑 </button> 或 <button id="del"> 撤销 </button> 该评论!</p>',
push = document.getElementById("pushBtn"), //旧按钮
repush = document.getElementById("repushBtn"), //新按钮
pushBack = (e)=>{
push.style.display="block";
repush.style.display="none";
document.getElementById(e).remove();
};
el.setAttribute("id","reEdit");
document.body.appendChild(el);
el.innerHTML = div;
//重新编辑点击事件
document.getElementById("redo").onclick=function(){
_.comment.focus(); //聚焦输入框
_.comment.value = input; //将已提交的评论内容重新写入到输入框
push.style.display="none"; //隐藏旧按钮
repush.style.display="block"; //显示新按钮
//重新编辑按钮点击事件
repush.onclick=function(){
redo.set("comment", "<p>"+_.comment.value+"</p>"); //更新数据为:当前输入框内容
//redo.set("isEdited", true); //可选(设定重复编辑判断规则,和之前的置顶实现一样在控制台新增指定列,然后更新数据做判断后写入元素,这里就不重复列了)
//保存更新后的数据
redo.save().then(function(e){
pushBack("reEdit"); //隐藏新按钮 显示旧按钮 移除提示框
document.getElementById(objId).querySelector(".vcontent p").innerHTML = _.comment.value; //更新本地显示内容(伪即时更新)
B() //执行预设函数 B()(数据清空)
}).catch(function(e) {
console.log("reEdit Err.")
})
}
};
//撤销点击事件
document.getElementById("del").onclick=function(){
//定义删除函数
let del = () =>{
let v = u.find(t.el, ".vnum");
//递减当前评论数量并写入本地显示
if(v!=null){
let n = Number(v.innerText);
n--;
v.innerText=n
}
redo.destroy(); //删除云端数据
pushBack("reEdit"); //隐藏新按钮 显示旧按钮 移除提示框
document.getElementById(objId).remove(); //更新本地显示内容(伪即时更新)
B() //执行预设函数 B()(数据清空)
};
let cf = confirm(" 即将删除评论(不可逆): "+input+",确定?"); //弹窗确认删除
cf==true ? del() : false; //删除逻辑
};
问题修复
以上代码是已经可以实现重复编辑和撤销评论的逻辑了,不过这时候发现更新数据提交后没有响应,打开控制台才发现报了 400 错误,一看是有关于 ACL 权限的就懂了,看了下 save 之前的 setACL,是一个定义好的函数 I()
,是这么写的(其实这里已经很明了了0!就是开权限了)
I = function() {
var e = new AV.ACL;
return e.setPublicReadAccess(!0),
e.setPublicWriteAccess(!1),
e
}
两个读写权限设置,这一看报错就是因为没有权限写入更新数据,去 leancloud 控制台一看,果不其然
然后我在 官方 ACL 文档 里找到了一些设置权限的方法,
I = function() {
var e = new AV.ACL,
admin = new AV.Role('admin');
return e.setPublicReadAccess(!0),
e.setPublicWriteAccess(!1), //实际这里改为 !0 就可以了(不考虑安全因素
//e.setRoleWriteAccess(admin, true); //指定用户编辑
//e.setWriteAccess(AV.User.current(), true), //当前 id 可编辑(Owner 有 bug 用户首次评论点击重复编辑按钮会获取到上一个id,弃用了)
e
}
以上的当前用户可修改评论和指定用户可修改最终效果都不太理想,所以还是将 e.setPublicWriteAccess(!1)
改为 e.setPublicWriteAccess(!0)
全读写了。
拓展
要实现评论是否被重复编辑,一个是记录并判断评论提交时间,不过太麻烦,还有一个就和置顶 topset
的实现是一样的,在 leancloud 控制台新建一个 isEdited
列默认值 flase
,然后在重复编辑评论提交时 set
一个 isEdited
的参数就行了,剩下的就和上次笔记写的一样了(写个判断,在写入元素前新增个元素并将值作为判断 isEdited
的结果即可)
问题优化
ACL 权限全读写还没完全搞懂,虽然可以用不过还是不推荐这么搞,记得经常备份才行。
F12 400 报错:Error: 此电子邮箱已经被占用问题
参考链接 leancloud 官方文档
“因果关系”
因为长期对 valine 的修改,现在发现了一个很早就出现的 bug.. 页面没有评论时第一次在页面评论不会刷出评论数量(刷新后又好了)不知道是哪里出问题了,因为我有很大量备份索性回滚,无果。
后期有时间好好排查下什么情况引起的,改的东西太多就容易出错,还不容易察觉,就是这么蛋疼..
已知 bug
- 取消回复后,没有清除已@到的人
- 重新编辑提交后,没有@到人
以上,有问题评论区留言。
评论留言
既来之则留之~ 欢迎在下方留言评论,提交评论后还可以撤销或重新编辑。(Valine 会自动保存您的评论信息到浏览器)