上次完成了6, 我觉得7有点鸡肋(因为我们总是在单元格内编辑,失去单元格焦点时就已经提交了),可以直接跳过。本次完成8,那么交互式表格的基本功能就完成了。
大约3个小时左右,完成了功能的开发和验证
虽然方法可能不是最优雅的,但是可以在这么短的时间内达到可用的效果,还是很让我满意的。前端这个事让我挂意了很久,虽然理论上是简单的,真的要实现起来总是觉得很麻烦。
这样,我基本上可以不用数据库工具软件了,这个会极大提高生产效率。
总体上,这次的功能增加了两个视图函数,略微修改了一个视图函数
add_a_row: 增加一个新行,主要是分配一个id给到前端的表格,这样后面的新增行就是更新了。
del_a_row: 删除行本质上也是更新,通过is_enable来控制。
效果这样:

注意这里的删除是从操作层面上感知的,数据库中仍然存在对应的数据。到了数据库,操作就比较轻松了:
表格增加一列tickbox
var table = new Tabulator("#example-table", {
height:205, // set height of table (in CSS or here), this enables the Virtual DOM and improves render speed dramatically (can be any valid css height value)
data:[], //assign data to table
layout:"fitColumns", //fit columns to width of table (optional)
addRowPos: "selected",
columns:[ //Define Table Columns
{formatter:"rowSelection", titleFormatter:"rowSelection", hozAlign:"center", headerSort:false, cellClick:function(e, cell){
cell.getRow().toggleSelect();
}},
{title:"Name", field:"name", width:150,editor:MyCellEdit01},
{title:"Habit", field:"habit",editor:MyCellEdit01},
{title:"Create Time", field:"create_time", sorter:"date", hozAlign:"center"},
{title:"Update Time", field:"update_time", sorter:"date", hozAlign:"center"},
],
rowClick:function(e, row){
_curRow = row;
}
});
编辑函数做修改,增加了更新时间显示
var MyCellEdit01 = function(cell, onRendered, success, cancel){
//cell - the cell component for the editable cell
//onRendered - function to call when the editor has been rendered
//success - function to call to pass thesuccessfully updated value to Tabulator
//cancel - function to call to abort the edit and return to a normal cell
//create and style input
// var cellValue = luxon.DateTime.fromFormat(cell.getValue(), "dd/MM/yyyy").toFormat("yyyy-MM-dd"),
var cellValue = cell.getValue()
input = document.createElement("input");
row = cell.getRow()
$(row.getElement()).css("background-color",'')
$(row.getElement()).css("color", "")
// input.setAttribute("type", "date");
input.style.padding = "4px";
input.style.width = "100%";
input.style.boxSizing = "border-box";
input.value = cellValue;
onRendered(function(){
input.focus();
input.style.height = "100%";
input.style.background ='yellow'
});
function onChange(){
if(input.value != cellValue){
// the_id = cell.getData().id
// alert(the_id )
// success(alert(input.value));
row_id = cell.getData().id
colname = cell.getColumn().getField()
// row = cell.getRow()
// row.style.background ='red'
filter_dict = {}
attr_dict = {}
filter_dict['tid'] = row_id
attr_dict[colname] = input.value
attr_dict['is_enable'] = 1
attr_dict['update_time']= getDatetime()
// alert(colname)
para_dict = {}
para_dict['filter_dict'] = filter_dict
para_dict['attr_dict'] = attr_dict
row.update({'update_time' :attr_dict['update_time']})
post_json('/update_a_cell/', para_dict, $(cell.getElement()))
success(input.value);
// cellValue = input.value
}else{
cancel();
}
}
//submit new value on blur or change
input.addEventListener("blur", onChange);
//submit new value on enter
input.addEventListener("keydown", function(e){
if(e.keyCode == 13){
onChange();
}
if(e.keyCode == 27){
cancel();
}
});
return input;
};
在创建新行时,和后端要有数据交互,主要获得新的id。要通过函数获得结果,要把ajax改为同步async:false,,这样可以获得结果然后让调用数据请求的函数使用。
// 单元格提交一个数据
function post_json_addrow(url, para_dict, target_obj){
let data = null;
$.ajax({
url:url,
type:'POST',
async:false,
data:JSON.stringify(para_dict),
dataType:'json',
contentType:'application/json',
success:function(res_data){
data_status = res_data.status ;
data = res_data.data
if(data_status==true){
$(target_obj).css("background-color", "green")
$(target_obj).css("color", "white")
}
else{
$(target_obj).css("background-color", "red")
$(target_obj).css("color", "white")
}
},
error:function(){
$(target_obj).css("background-color", "red")
$(target_obj).css("color", "white")
}
},'json')
return data
}
单元格的提交,根据后台的状态更新单元格的颜色
function post_json(url, para_dict,target_obj){
$.ajax({
url:url,
type:'POST',
data:JSON.stringify(para_dict),
dataType:'json',
contentType:'application/json',
success:function(res_data){
data_status = res_data.status ;
// alert(data_status)
// // alert('success')
if(data_status==true){
$(target_obj).css("background-color", "green")
$(target_obj).css("color", "white")
}
else{
$(target_obj).css("background-color", "red")
$(target_obj).css("color", "white")
}
},
error:function(){
// $('#is_human_correct_old_model').css("background-color","red")
$(target_obj).css("background-color", "red")
$(target_obj).css("color", "white")
}
},'json')
}
根据要删除的行在后台执行。
function post_json_del(url, para_dict){
$.ajax({
url:url,
type:'POST',
data:JSON.stringify(para_dict),
dataType:'json',
contentType:'application/json',
success:function(res_data){
data_status = res_data.status ;
// alert(data_status)
// // alert('success')
if(data_status==true){
}
else{
alert('删除有问题')
}
},
error:function(){
// $('#is_human_correct_old_model').css("background-color","red")
alert('删除有问题')
}
},'json')
}
这个还是很方便的,表格自身的改变和展示会根据操作做变化
document.getElementById("ajax-trigger").addEventListener("click", function(){
table.setData("/get_test_data_wmongo/");})
//Add row on "Add Row" button click
document.getElementById("add-row").addEventListener("click", function(){
table.addRow({})})
//Delete row on "Delete Row" button click
document.getElementById("del-row").addEventListener("click", function(){
// rowCount = table.getDataCount();
// alert(rowCount)
rows = table.getRows("selected")
rows.forEach(function(row){
alert(row.getData().id)
table.deleteRow(row.getData().id)
})
;})
进度
接下来就简单了(之前已经实现过),使用多级下拉框来选择不同的服务器、数据库和表。唯一一个要注意的就是不同表的主键问题:大部分主键是不同名的。当然也不是所有表都需要/允许编辑。
约定:每个可编辑的数据表都有一个整型的主键id,名字叫task_id,这个我已经有一个可手动开启的worker来长期运行。所以如果要将某个表转为可编辑状态时,启动这个worker去守护这张表就可以了。
可编辑与不可编辑的原则
一般来说,原始数据,或者中间状态的数据是不可编辑的。
而元数据,例如任务的运行状态,数据的标签(对错等),以及服务的启停等,这些可以通过前端控制。