目标:达到下图拥有功能的实现
1.绑定编辑按钮
************思路****************
1.为编辑按钮添加样式,可以根据样式来进行判断在什么状态.
2.进入编辑模式,将可编辑的字段修改为input框,或者select框.退出时变为文本框.
我们这里只对配置文件中.拥有editEnable='true'的属性的标签进行编辑(因为进出编辑模式经常使用,所以写成两个函数)
3.做到双向选择,可以先进入编辑状态再选择checkbox也可反之.
a.如果先选择checkbox 则在编辑按钮里可以进入编辑模式
b.如果先选择编辑按钮,再选择checkbox,那么就给checkebox绑定事件.(进出编辑模式)
下面是绑定编辑按钮的js代码
function bindMenus() { // {# 绑定编辑按钮, #} // {# 进入编辑模式,修改文本内容为input框或者下拉框#} // {# 退出编辑模式,将修改的input框和下拉框的值修改到文本到没更新数据库#} $('#EditMode').click(function () { var editing = $('#EditMode').hasClass('btn-warning'); if (editing){ //退出编辑模式 $('#table_tb').find(':checked').each(function () { var $CurrentTr = $(this).parent().parent(); trOutEditMode($CurrentTr); }); $('#EditMode').removeClass('btn-warning'); $('#EditMode').text('进入编辑模式') } else { $('#EditMode').addClass('btn-warning'); $('#EditMode').text('退出编辑模式'); //进入编辑模式 $('#table_tb').find(':checked').each(function () { var $CurrentTr = $(this).parent().parent(); trInEditMode($CurrentTr); }) } }); }
2.进出编辑模式修改文本框模式
*******进入编辑模式思路********
1.遍历被选中checkbox框找到每一个checkbox的tr标签传进函数
2.遍历tr标签里的td便签,判断是否可编辑,是什么编辑框.
3.根据不同的编辑框进行操作
a.创建select框,则去全局变量里找到相应的数据,提取choice的id和文本内容放进option标签里
b.创建input框,将该文本的内容放进input框里
下面是实例代码
$tr.addClass('success');这里的success是bootstrap样式,进入编辑后修改样式
function trInEditMode($tr) { $tr.addClass('success'); $tr.attr('has-eidt','true'); $tr.children().each(function () { var editEnable = $(this).attr('edit-enalbe'); var editType = $(this).attr('edit-type'); if (editEnable == 'true'){ // 可以进入编辑模式 //<select> // <options value='1'></options> // <options value='2'></options> // </select> if (editType=='select'){ var $sel = $('<select class="form-control"></select>'); var GlobalName = $(this).attr('global-name'); var origin =$(this).attr('origin'); $.each(window[GlobalName],function (k1,v1) { var $op = $('<option></option>'); $op.attr('value',v1[0]); $op.html(v1[1]); $sel.append($op); $sel.val(origin); }); $(this).html($sel)} else if(editType=='input'){ var content = $(this).text(); var current_input = $('<input class="form-control">'); current_input.val(content); $(this).html(current_input) } } }) }
******退出编辑模式思路*****
1.遍历被选中checkbox框找到每一个checkbox的tr标签传进函数
2.遍历tr标签里的td便签,判断是否可编辑,是什么编辑框.
3.根据不同的编辑框进行操作
a.如果是select框,则去全局变量里找到选中的option对应的value值和文本并且增加new-val属性(为后面的保存数据做铺垫)
b.如果是input框,则把input里的val值放在html里即可.同时也增加new-val属性
function trOutEditMode($tr) { $tr.removeClass('success'); $tr.children().each(function () { var editEnable = $(this).attr('edit-enalbe'); var editType = $(this).attr('edit-type'); if (editEnable == 'true'){ //可以退出编辑模式 if (editType =='select'){ var $select= $(this).children().first(); var NewId = $select.val(); var NewText = $select[0].selectedOptions[0].innerHTML; $(this).html(NewText); $(this).attr('new-val',NewId) }else if(editType == 'input'){ var $input = $(this).children().first(); var inputval = $input.val(); $(this).html(inputval); $(this).attr('new-val',inputval) } } }) }
**var NewId = $select.val(); option里面的value值可以通过select.val()取到
**var NewText = $select[0].selectedOptions[0].innerHTML;
------$select[0]将jQuery对象变位document对象
------selectedOptions该代码是select选中的option 内部是一个列表
3.绑定checkbox
****思路****
1.找到所有的checkbox遍历.
2.每次都进行判断是否进入编辑状态:
a.如果是,则执行trInEditMode($current_tr)该tr进入编辑状态
b.如果否,则选中即可
通过这里就实现了标题1的双向选择
function bindCheckAll() { $('#CheckAll').click(function () { $('#table_tb').find(':checkbox').each(function () { //判断是否进入编辑模式 if ($('#EditMode').hasClass('btn-warning')) { var if_checked = $(this).prop('checked'); if (if_checked) { //进入模式点击全选 被选中不操作 } else { $(this).prop('checked', true); var $current_tr = $(this).parent().parent(); trInEditMode($current_tr); } } else { $(this).prop('checked', true); } }) }) }
4.绑定全选
****思路*****
1.判断是否进入了编辑模式
a.如果是,让每一个tr都进入编辑模式
b.如果否,则全部选中即可
$('#CheckAll').click(function () { $('#table_tb').find(':checkbox').each(function () { //判断是否进入编辑模式 if ($('#EditMode').hasClass('btn-warning')) { var if_checked = $(this).prop('checked'); if (if_checked) { //进入模式点击全选 被选中不操作 } else { $(this).prop('checked', true); var $current_tr = $(this).parent().parent(); trInEditMode($current_tr); } } else { $(this).prop('checked', true); } }) }) }
var if_checked = $(this).prop('checked');
if (if_checked) 判断是否选中
5.绑定取消
***思路***
1.判断是否已经进入了编辑模式
2.遍历已经被选中的checkbox
a.如果进入编辑模式,则退出
b.如果没有,则取消选中
function bindCheckCancleAll() { $('#CheckCancleAll').click(function () { $('#table_tb').find(':checked').each(function () { if ($('#EditMode').hasClass('btn-warning')){ var $current_tr = $(this).parent().parent(); trOutEditMode($current_tr); $(this).prop('checked',false) } else { $(this).prop('checked',false) } }) }) }
反选的思路都一样,这里直接贴代码
function bindOppositeCheck() { $('#OppositeCheck').click(function () { $('#table_tb').find(':checkbox').each(function () { if($('#EditMode').hasClass('btn-warning')){ //进入编辑模式 var $current_tr=$(this).parent().parent(); if ($(this).prop('checked')){ $(this).prop('checked',false); trOutEditMode($current_tr) }else { $(this).prop('checked',true); trInEditMode($current_tr) } }else { if($(this).prop('checked')){ $(this).prop('checked',false); }else {$(this).prop('checked',true);} } }) }) }
6.批量删除
***思路***
1.这里的删除也是双向的,在进入或者退出编辑模式都可以进行删除,
原理:在进行编辑模式的时候退出编辑模式,获取数据通过ajax提交给后台
2.拿到放置该checkbox的tr标签里的id值(在创建tr标签的时候生成),放在列表里传给后台
function DeleteconfirmAll() { if($('#EditMode').hasClass('btn-warning')){ $('#EditMode').removeClass('btn-warning'); $('#EditMode').text('进入编辑模式'); $('#table_tb').find(':checked').each(function () { var $CurrentTr = $(this).parent().parent(); trOutEditMode($CurrentTr); }) } var del_list =[]; $("#table_tb").find(':checked').each(function () { //$(this)--->input var $tr = $(this).parent().parent(); var RodId= parseInt($tr.attr('row-id')); del_list.push(RodId); }); $.ajax({ url:requestUrl, type:'DELETE', data:{del_list:del_list}, traditional: true, dataType:'JSON', success:function (arg) { if(arg.status){ $("#delModal").modal("hide"); bindShowStatus(); $.each(arg.msg,function (k,v) { $('tr[row-id="'+v+'"]').remove() }) } else { } } }) }
这里Ajax用的是type是delete,因为写django的时候用了cbv的模式.
*******用Ajax传列表的时候需要traditional: true代表只进行浅序列化.
****** $('tr[row-id=" '+v+' "]').remove() 前端删除数据.....这里用到了字符串的拼接
后端拿数据的代码:
response={'status':False,'error':None,'msg':None} req_del = QueryDict(request.body,encoding='utf-8') del_list = req_del.getlist('del_list') print(del_list) try: if del_list: models.UserInfo.objects.filter(id__in=del_list).delete() response['status']=True response['msg']=del_list except Exception as e: response['error']=str(e) return JsonResponse(response)
7.保存数据
**思路**
1.同上,双向提交数据.
2.在每次进入编辑模式的时候都添加has-eidt="true",那么在遍历提交数据的时候可以用该属性进行筛选
3.将可以编辑的td获取,标签中新的数据new-val,还有原来的数据origin(原来的数据在配置文件中设置)
如果新的数据和老的数据相同则不需要写入字典.
再用列表将push字典传装着多个字典的列表给后端
function bindSaveAll() { $('#SaveAll').click(function () { if($('#EditMode').hasClass('btn-warning')){ $('#EditMode').removeClass('btn-warning'); $('#EditMode').text('进入编辑模式'); $('#table_tb').find(':checked').each(function () { var $CurrentTr = $(this).parent().parent(); trOutEditMode($CurrentTr); }) } var Data_list = []; $("#table_tb").find('[has-eidt="true"]').each(function () { //$(this) -->tr var RowId = $(this).attr('row-id'); var Data_dict = {}; Data_dict['id']=parseInt(RowId); $(this).children('[edit-enalbe="true"]').each(function () { //$(this)-->td var Name = $(this).attr('name'); var Origin = $(this).attr('origin'); var NewVal = $(this).attr('new-val'); if(Origin!=NewVal && NewVal!= undefined){ Data_dict[Name]=NewVal } }); Data_list.push(Data_dict); }); $.ajax({ url:requestUrl, type:'PUT', data:{'Data_list':JSON.stringify(Data_list)}, success:function (arg) { bindShowStatus(); console.log(arg) } }); })}
成功提交数据以后,回调函数自动调用了bindShowStatus()函数,这里面主要是增加保存成功的按钮,5秒后自动关闭
function bindShowStatus() { var $ShowStatus = $('#SaveSuccess'); $ShowStatus.empty().addClass('btn btn-success').html('操作成功'); setTimeout(function () { $ShowStatus.empty().removeClass('btn btn-success') },5000) }
后端代码:
def put(self,request,*args,**kwargs): data_list =QueryDict(request.body,encoding='utf-8') print(data_list) data_list = json.loads(data_list.get('Data_list')) for row in data_list: nid = row.pop('id') if not row: continue models.UserInfo.objects.filter(id=nid).update(**row) return HttpResponse('...')
因为前段用ajax的时候 data:{'Data_list':JSON.stringify(Data_list)},进行了序列化
后端获取的时候:
data_list =QueryDict(request.body,encoding='utf-8')
data_list = json.loads(data_list.get('Data_list'))
需要注意的是:用put delete方法提交和get post提交获取的数据方式不一样
8.分页功能
**思路**
1.每次初始化数据的时候init,向后台获取数据时将该页的页码传给后端....第一次传为null
2.根据前端传的页码数值,用分页插件生成li标签放在配置文件中.
3.前端根据配置文件生成数据,用事件委托绑定页码标签,当页码被点击时将该执行函数获取该页码值,后执行init(page)方法重新获取数值.
注意
1.通过ajax在重新初始化内容的时候需要把内容empty()
2.用事件委托的时候考虑是否会事件累积.(如果是重复绑定标签,需要在委托之前off('click')清空上一次的事件)
function initPager(pager_list) { var $page_ul = $('#page_ul'); $page_ul.empty(); $page_ul.append(pager_list); $page_ul.off('click'); $page_ul.on('click','a',function () { //$(this) --><a> var page = $(this).attr('page'); init(page); }) }
9.组件
将已经写好的JS代码生成组件
用自执行函数来存放js代码
1.自执行函数里的代码不可被外面的代码调用,保证了组件无污染.
2.通过extend进行扩展,和外面的代码交互
/// js组件 (function(){ //自执行函数 var requestUrl =null;局部变量,作用域是自执行函数 ......... }) //加载完成执行该方法: jQuery.extend({ 'CURD':function (url) { requestUrl =url; //url会赋值给上面的requestUrl从而进行获取数据 init(); bindMenusFunc(); //bindMenusFunc封装了对click事件的加载. } }) () 非组件 <script> $(function () { $.CURD('/get_msg/'); //可以调用CURD将url传进来.... }); </script>