zoukankan      html  css  js  c++  java
  • 巨蟒django之权限7:动态生成一级&&二级菜单

    内容回顾:

    1. 权限的控制 
            1. 表结构设计  存权限的信息
                用户表
                    - name 用户名
                    - pwd 密码
                    - roles  多对多
                    
                角色表
                    - name
                    - permissions 多对多
                    
                权限表
                    - url  含正则url  /customer/list/  /customer/edit/(d+)/    没有^$
                    - title  标题 
                    
                用户和角色关系表
                    - user_id
                    - role_id
                    
                角色和权限的关系表
                    - role_id
                    - permission_id
                    
            2. 流程
                1. 登录
                    - 中间件
                        白名单
                    - 认证成功
                        ORM 获取到当前用户的权限信息
                        保存到session中
                2. 中间件
                    - 获取到当前访问的url
                    - 白名单
                    - 没有登录重定向去登录
                    - 免认证
                    - 权限校验
                        - 获取当前用户的权限信息
                        - 循环权限 一一对比 
                            - 对比成功  有权限  return
                            - 对比不成功  没有权限  return HTTPResponse()

    今日内容:

        1. 动态生成一级菜单    
        2. 动态生成二级菜单
        
            客户管理  - 一级菜单
                客户列表  - 二级菜单
                
            财务管理
                缴费列表

    数据结构整理:

    [{
                'permissions__url': '/customer/list/',
                'permissions__title': '展示客户',
                'permissions__menu__title': '客户管理',
                'permissions__menu__icon': 'fa-user-o',
                'permissions__menu_id': 1
            }, {
                'permissions__url': '/customer/add/',
                'permissions__title': '添加用户',
                'permissions__menu__title': None,
                'permissions__menu__icon': None,
                'permissions__menu_id': None
            }, {
                'permissions__url': '/customer/edit/(\d+)/',
                'permissions__title': '编辑用户',
                'permissions__menu__title': None,
                'permissions__menu__icon': None,
                'permissions__menu_id': None
            }, {
                'permissions__url': '/customer/del/(\d+)/',
                'permissions__title': '删除用户',
                'permissions__menu__title': None,
                'permissions__menu__icon': None,
                'permissions__menu_id': None
            }, {
                'permissions__url': '/payment/list/',
                'permissions__title': '缴费列表',
                'permissions__menu__title': '财务管理',
                'permissions__menu__icon': 'fa-usd',
                'permissions__menu_id': 2
            }, {
                'permissions__url': '/oder/list/',
                'permissions__title': '账单列表',
                'permissions__menu__title': '财务管理',
                'permissions__menu__icon': 'fa-usd',
                'permissions__menu_id': 2
            }, 
            
            {
                'permissions__url': '/payment/add/',
                'permissions__title': '添加缴费',
                'permissions__menu__title': None,
                'permissions__menu__icon': None,
                'permissions__menu_id': None
            }, {
                'permissions__url': '/payment/edit/(\d+)/',
                'permissions__title': '编辑缴费',
                'permissions__menu__title': None,
                'permissions__menu__icon': None,
                'permissions__menu_id': None
            }, {
                'permissions__url': '/payment/del/(\d+)/',
                'permissions__title': '删除缴费',
                'permissions__menu__title': None,
                'permissions__menu__icon': None,
                'permissions__menu_id': None
            }]
    
        
            {
               2:{
                    'title':'财务管理',
                    'icon':'fa-usd',
                    'children' : [
                        { 'title':'缴费列表','url':'/payment/list/' }
                        { 'title':'账单列表','url':'/oder/list/' }
                    ]
                        
                    }    
                
            }
    View Code

    首先,我们给秘书角色添加一个权限,展示客户

    下面是"账单管理":

    现在我们的需求是,有权限的,我们就进行显示,没有权限的不显示,否则很尴尬.

    1.动态生成一级菜单

    下图展示的是,目前的所有权限.

     下图的"客户管理"和"账户管理"也是两个权限

     

     首先,我们需要做权限的区分,需要在表中加上字段,进行判断,哪个是菜单?

    也就是修改表结构

    修改权限表Permission,加上一个字段is_menu,默认是False

    =>加上标题,是否是菜单

    下面我们加上图标,不同图标可以不同展示

     目前的状况是,只有是菜单的情况下,才需要图标,其他情况不需要图标,

    下面我们对数据库进行迁移一下:

    下面,我们需要在admin中进行展示一下:

    原来的样式:

    配置完之后的样式:

    最终的目标是,我们需要展示出来?如何做到?

    将a标签分两步处理,一步存,一步取.

    存储的话,我们需要存储到session中,然后通过用户拿到.

    login函数,原来的样子:

    现在的状态,也就是"符号标签","是否是菜单这个样式".这样就出来了

    修改后的样子:

    然后,我们打印一下:

    点击"登录"

    服务端得到的结果:

    我们需要将permission_query存储到session当中,依然是需要什么结构?按照字典会方便一些,需要是多个字典,存储在列表当中

    下面我们需要定义权限的列表和菜单的列表:向session中存到

    现在,我们上图相当于存储的是一个列表.

     唯一改变的是键变成了url,值还是原来queryset查询出来的,permission__url原来对应的值.

    这个时候,我们再次取值的过程中,会发生改变

    上图所示的位置变成了url,这样权限的信息就不受影响了,,上图的文件是中间件对应的rbac.py文件.

    这个时候,我们的菜单的列表应该如何处理?

    登录alex的账户:

     

    我们打印一下,让现实的更清楚一些:

    同样,我们再次登录alex的账户

     显示的是两个权限:

    下面我们看下root的账户:

    服务端看到的结果:

    一类是菜单,另一类不是菜单.

    现在我们想要做的事情是,将是菜单的字典,加到菜单列表当中.这个时候该如何处理?

     对照上边的代码:

    通过权限是菜单进行判断,然后将菜单列表依次添加"url","title","icon"

    经过这样处理之后,权限信息加到了权限列表中了,菜单列表加入和菜单的信息了.,不是的过滤掉了

    我们再向session当中添加一个,menu信息

    下面,我们打印menu_list看一下信息.

    再次登录,root账户,查看一下:

    这个时候,我们得到了菜单的信息:

    再看一下:强哥的账户:

    这个时候,存储的信息就完成了,

     原来的情况是写死的,现在我们将前端的母版灵活起来.

    模板信息通过拿出来,request,我们通过for循环拿取数据

    将人图标icon和url全部灵活写,以及标题title

    下面再强哥的地址里边,刷新一下:

    产生这种效果的原因:存在这个菜单就显示,不存在这个菜单就不显示,就是这么简单,我们通过for循环判断.

    思路:auth里边的login, 这个时候,我们只需要循环展示就可以了,通过查询是个信息,2个列表,i代表权限的信息.

    最后,我们再通过其他方式拿出来.

    再走中间件=>白名单=>获取登录状态(没有登录就跳转到登录页面)=>免认证=>获取当前用户的权限=>权限的校验

    免认证index,菜单通过渲染...先存到某个地方,需要的时候再查出来.这就是一级菜单的核心

    2.一级菜单默认选中

     

    如果下图,画红线的部分改成其他的内容,源码里边的东西全部都需要修改,用到再改就晚了,如何做?可改就是放到settings当中

    下面我们进行settings.py的配置

    我们做成可配置的session

     

     

    上边是存的时候改,下面是取的时候改.

    上边的中间件,也是需要修改的.

    还有一个在layout里边进行修改:

    在前端里边没有中括号进行修改,如何操作?组件需要给别人用,如何用?从后端传递会好一些?如何操作?

    自定义方法进行处理inclusion_tag

    注意,在web里边创建的自定义方法必须叫templatetags,然后在里边创建my_tags.py

     下面开始写程序,在my_tags.py

    将layout.py里边的信息放到自定义的menu.html里边

    上边的request.session.menu取的时候不方便,我们可以通过在my_tags.py里边.

    返回,请求的信息.

    然后我们将menu_list传给模板

    模板完成不了,我们就写在python的代码里边,需要引入一下

    这个时候在layout.html

     这个时候,我们在menu.html中传递

    重启一下:

     

    我们需要,在选中的情况下,显示出被选中的样式.

    这个需要如何做?

    在python里边做会好一些.前端里边如何做?用js做,做正则表达式的匹配,拿去当前地址的方式

    拿,当前路径的地址,见下图:

    然后和上边的

     

    刷新一下:

    得到下面的地址和列表:

    这个时候,我们可以开始取这个数据了.

    i是个字典,将i中放入选择的这个字段  class=active,相当于

    在my_tags.py里边表示的是,for循环出在里边,如果匹配到这个路径,就添加到这个路径里边.也就是当前点击的这个操作菜单

     我们再看一下layout.html,也就是菜单的请求.

    没有空格导致图标显示不出来,加上空格之后,这个时候就可以显示出图标了 

    3.rbac组件功能整合

     现在我们事先了哪些功能?

    权限控制,动态生成菜单

    写了哪些部分的代码?

    中间件rbac文件夹里边//登录认证有一部分//inclusion_tags有一部分  这些都和权限控制有关系,

    现在我们需要进行分一下类,用起来更方便一些.

     

    上图所示的部分和登录业务没有太大的关系,只是一些额外的操作,登录成功之后,才会做这些额外的操作.

    下一个项目需要的时候需要再写一遍.需要拿出来,放在文件夹rbac文件中会好一些.

     

    新建上边的文件

     下面我们写成一个函数:

    将刚才括起来的文件,放在下面的文件中:

    auth.py文件:

     我们只需要将request和obj传递到permission文件中就可以了

    #认证成功,进行权限信息的初始化(权限,菜单)

     

    登录部分完成,中间件需要再写一下,还有哪些东西需要写?还有一个动态生成菜单写在web里边,需要修改一下

    其实和权限相关,所以应该写在rbac里边,我们需要将web文件中的templatetags文件放在rbac里边.直接拖过去

     

    修改之后的内容:

    将上边的名字my_tags.py修改名字问rbac.py

    再次调用的时候也就会发生了变化.

     将菜单的样式放在rbac里边,将下面的5条放在rbac里边

     在rbac新建static文件,下面建立css文件,再创建一个menu.css文件放入刚才的文件

     

    在layout.html导入css文件:

    为了防止重名,我们可以在rbac下面的static静态文件,再加上一层rbac进行处理,同时在layout.html也需要修改引入的css样式,目的:防止重名

    现在我们将就有了几部分内容,

    A:rbac下面的service/permission里边的函数init_permission

    B:rbac下面的middlewares/rbac对应的类RbacMiddleWare中间件.注册到settings.py中,就可以用到权限的控制

    C:还有动态生成一级菜单,

    也就是在模板layout.html中导入rbac和css样式

    登录之后,需要做权限信息的初始化的函数,以及进行权限校验的中间件,动态生成以及菜单的inclusiontag,现在在rbac文件夹下的templatetags下的rabc里边了.

    4.动态生成二级菜单+js效果

    下面我们需要处理的是动态生成二级菜单:

     

    在权限表中分为两种,一种是可以是菜单的权限,另一种不能是菜单的普通权限.

    现在我们可以做二级菜单的,需要进行分配,相当于原来的一级修改成二级,思考,如何记录?加字段合适吗?也是不合适的

    通过加表实现.也就是菜单表.

    现在,我们将权限进行分成,一种是二级菜单的权限,另一种是普通权限,如何区分?也就是说,有外键的是二级菜单权限,没有外键的是普通权限.思考,如何区分?

    is_menu就没有用了,icon在权限中也没有用了,只需要在一级菜单中有就可以了

    这样我们就修改完了表结构

    现在的权限表中,关联菜单menu=>二级菜单

            不关联menu=>普通的权限

     运行:报下面的错误.

    也就是说,现在后边,两个字段就不存在内容了,原因是上边经过了修改

    现在,我们再执行下面的两条语句

    报错,需要在admin.py下面添加上Menu菜单启动程序:

    运行:

    这个时候,我们在RBAC中,就多了一个Menus,现在,我们再菜单中添加,一级菜单的结果:

    报上图错误的原因是,我们没有执行完数据库迁移的第二条命令

    执行完成之后,再次操作

    这个时候就得到了一个对象

     

     这个时候得到下面的结果:

    不显示具体的内容我们加上str

     

    这个时候,我们再次刷新一下,得到的结果是:

    我们需要再分配一下权限,我们需要将二级菜单,分配到一级菜单的下面

     下面是我们刚才改完之后的结构,其余的六条也可以进行修改,添加进去

     

     如何展示出来?分成两步,首先获取出来数据,保存到session当中,然后,循环取出来,进行展示.

    先获取,

    我们需要将上边的两个字段删除掉,因为已经没有了,我们现在拿到的是models的一个权限.

    我们现在拿的是二级菜单的url和title都拿出来了,但是我们还需要展示一级菜单的结果

     

    上图是我们拿到的一级下拉菜单框的结果.一会循环的时候,我们需要构建成一个数据结构.我们需要一个层级结构,每一个一级菜单下面相关的二级菜单相关的结果.

     如何找到?通过外键title,最终还是menu的id才是核心,也就是主键的id,这个时候我们得到下图所示的menu_id

     

    现在,我们打印一下结果:

    运行一下程序,重新等于一下root

    点击登录:

    不需要管上边的错误

    服务端得到的结果是:所有的权限

     

    <QuerySet [{'permission__url': '/customer/list/', 'permission__title': '展示客户', 'permission__menu__title': '客户管理', 'permission__menu__icon': 'fa-user'}, {'permission__url': '/customer/add/', 'permission__title': '添加用户', 'permission__menu__title': None, 'permission__menu__icon': None}, {'permission__url': '/customer/edit/(\d+)/', 'permission__title': '编辑用户', 'permission__menu__title': None, 'permission__menu__icon': None}, {'permission__url': '/customer/del/(\d+)/', 'permission__title': '删除用户', 'permission__menu__title': None, 'permission__menu__icon': None}, {'permission__url': '/payment/list/', 'permission__title': '缴费列表', 'permission__menu__title': '财务管理', 'permission__menu__icon': 'fa-usd'}, {'permission__url': '/payment/add/', 'permission__title': '添加缴费', 'permission__menu__title': None, 'permission__menu__icon': None}, {'permission__url': '/payment/edit/(\d+)/', 'permission__title': '编辑缴费', 'permission__menu__title': None, 'permission__menu__icon': None}, {'permission__url': '/payment/del/(\d+)/', 'permission__title': '删除缴费', 'permission__menu__title': None, 'permission__menu__icon': None}]>
    View Code

    有menu_id代表是个二级菜单

     上边的信息缺少了menu_id,下面我们添加上

    再次运行,再次登录root账户.再讲服务端得到的信息,复制到bejson中,进行处理,可以看到相关的功能.

    也就是我们刚才找到的两个权限,

    我们现在需要将现在得到的数据结果,得到另一种数据结果:

    现在,我们需要在下面进行处理,将财务管理多添加一个"账单列表"

    服务端整体得到的数据结构:

    < QuerySet[{
        'permission__url': '/customer/list/',
        'permission__title': '展示客户',
        'permission__menu__title': '客户管理',
        'permission__menu__icon': 'fa-user',
        'permission__menu__id': 1
    }, {
        'permission__url': '/customer/add/',
        'permission__title': '添加用户',
        'permission__menu__title': None,
        'permission__menu__icon': None,
        'permission__menu__id': None
    }, {
        'permission__url': '/customer/edit/(\d+)/',
        'permission__title': '编辑用户',
        'permission__menu__title': None,
        'permission__menu__icon': None,
        'permission__menu__id': None
    }, {
        'permission__url': '/customer/del/(\d+)/',
        'permission__title': '删除用户',
        'permission__menu__title': None,
        'permission__menu__icon': None,
        'permission__menu__id': None
    }, {
        'permission__url': '/payment/list/',
        'permission__title': '缴费列表',
        'permission__menu__title': '财务管理',
        'permission__menu__icon': 'fa-usd',
        'permission__menu__id': 2
    },  {
        'permission__url': '/payment/list/',
        'permission__title': '账单列表',
        'permission__menu__title': '财务管理',
        'permission__menu__icon': 'fa-usd',
        'permission__menu__id': 2
    }, {
        'permission__url': '/payment/add/',
        'permission__title': '添加缴费',
        'permission__menu__title': None,
        'permission__menu__icon': None,
        'permission__menu__id': None
    }, {
        'permission__url': '/payment/edit/(\d+)/',
        'permission__title': '编辑缴费',
        'permission__menu__title': None,
        'permission__menu__icon': None,
        'permission__menu__id': None
    }, {
        'permission__url': '/payment/del/(\d+)/',
        'permission__title': '删除缴费',
        'permission__menu__title': None,
        'permission__menu__icon': None,
        'permission__menu__id': None
    }] >
    View Code

    我们添加了"账单列表"之后的数据结构:

    < QuerySet[{
        'permission__url': '/customer/list/',
        'permission__title': '展示客户',
        'permission__menu__title': '客户管理',
        'permission__menu__icon': 'fa-user',
        'permission__menu__id': 1
    }, {
        'permission__url': '/customer/add/',
        'permission__title': '添加用户',
        'permission__menu__title': None,
        'permission__menu__icon': None,
        'permission__menu__id': None
    }, {
        'permission__url': '/customer/edit/(\d+)/',
        'permission__title': '编辑用户',
        'permission__menu__title': None,
        'permission__menu__icon': None,
        'permission__menu__id': None
    }, {
        'permission__url': '/customer/del/(\d+)/',
        'permission__title': '删除用户',
        'permission__menu__title': None,
        'permission__menu__icon': None,
        'permission__menu__id': None
    }, {
        'permission__url': '/payment/list/',
        'permission__title': '缴费列表',
        'permission__menu__title': '财务管理',
        'permission__menu__icon': 'fa-usd',
        'permission__menu__id': 2
    },  {
        'permission__url': '/order/list/',
        'permission__title': '账单列表',
        'permission__menu__title': '财务管理',
        'permission__menu__icon': 'fa-usd',
        'permission__menu__id': 2
    }, {
        'permission__url': '/payment/add/',
        'permission__title': '添加缴费',
        'permission__menu__title': None,
        'permission__menu__icon': None,
        'permission__menu__id': None
    }, {
        'permission__url': '/payment/edit/(\d+)/',
        'permission__title': '编辑缴费',
        'permission__menu__title': None,
        'permission__menu__icon': None,
        'permission__menu__id': None
    }, {
        'permission__url': '/payment/del/(\d+)/',
        'permission__title': '删除缴费',
        'permission__menu__title': None,
        'permission__menu__icon': None,
        'permission__menu__id': None
    }] >
    View Code

    我们需要,最终得到的结果结构是:

    s={
        2:{
            'title':'财务管理',
            'icon':'fa-usd',
            'children':[
                {'title':'缴费列表','url':'/payment/list/'},
                {'title':'账单列表','url':'/payment/list/'}
            ]
        }
    }

    除了上边id等于1的也是需要构造的.

     思路应该是怎样的?

    title代表一级菜单,children里边的title代表的二级菜单的id

    运行:

    上边都是一些权限信息,我们需要清除,哪些需要,哪些权限不需要,需要清楚知道这些内容.

     menu_id=None代表的是普通的权限:

     我们可以用"笨一点"的方法,一次搞不定我们就搞两次,

     也就是说,我们将外层的先构建出来,键children先写一个:空

     

    第二次循环,我们再加上children里边的内容:得到相应的结果,我们想要一次搞定,

    首先,我们开始一点点拿(上图使我们需要得到的结构):

    下面的这样一条是"二级菜单"

     构建的方法:

    data = [{
        'permission__url': '/customer/list/',
        'permission__title': '展示客户',
        'permission__menu__title': '客户管理',
        'permission__menu__icon': 'fa-user',
        'permission__menu__id': 1
    }, {
        'permission__url': '/customer/add/',
        'permission__title': '添加用户',
        'permission__menu__title': None,
        'permission__menu__icon': None,
        'permission__menu__id': None
    }, {
        'permission__url': '/customer/edit/(\d+)/',
        'permission__title': '编辑用户',
        'permission__menu__title': None,
        'permission__menu__icon': None,
        'permission__menu__id': None
    }, {
        'permission__url': '/customer/del/(\d+)/',
        'permission__title': '删除用户',
        'permission__menu__title': None,
        'permission__menu__icon': None,
        'permission__menu__id': None
    }, {
        'permission__url': '/payment/list/',
        'permission__title': '缴费列表',
        'permission__menu__title': '财务管理',
        'permission__menu__icon': 'fa-usd',
        'permission__menu__id': 2
    }, {
        'permission__url': '/order/list/',
        'permission__title': '账单列表',
        'permission__menu__title': '财务管理',
        'permission__menu__icon': 'fa-usd',
        'permission__menu__id': 2
    }, {
        'permission__url': '/payment/add/',
        'permission__title': '添加缴费',
        'permission__menu__title': None,
        'permission__menu__icon': None,
        'permission__menu__id': None
    }, {
        'permission__url': '/payment/edit/(\d+)/',
        'permission__title': '编辑缴费',
        'permission__menu__title': None,
        'permission__menu__icon': None,
        'permission__menu__id': None
    }, {
        'permission__url': '/payment/del/(\d+)/',
        'permission__title': '删除缴费',
        'permission__menu__title': None,
        'permission__menu__icon': None,
        'permission__menu__id': None
    }]
    ret = {}
    for i in data:
        menu_id = i.get('permission__menu__id')  # 首先获取到id
        if not menu_id:  # 没有菜单id的就continue跳过
            continue  # 我们现在需要的是有menu_id的信息,就是二级菜单
        # 一种是普通权限,另一种是二级菜单.
        ret[menu_id] = {
            # permission__title
            'title': i['permission__menu__title'],
            'icon': i['permission__menu__icon'],
            'children': [
                {'title': i['permission__title'], 'url': i['permission__url']}
            ]
        }
    print(ret)
    View Code

    运行之后的结果:

    注意,在这里这个列表里边,不能加"逗号"

     在构建的时候,一定不能心急,需要一点点构建这个数据结构

    需要对照的核心图:

    (1)

     

    (2)

     

    (3)慢慢构建出来.

     我们将获取的结果放在bejson中

     

    在上图的财务管理里边,只有一个账单列表.

    应该有两个,但是现在只有一个.也就是说,在下图的原始数据中,当拿到第二个的时候,将第一个覆盖掉了

    正确的写法应该是?

    第二次进来就不是重新设置了,需要重写一下代码判断:

    不在就创建,在就不创建,只是添加

     运行:得到结果:

     这样就得到了两个信息.

     

    思路相同,实现方式不同

     换一种写法用setdefault

     

    同样,可以得到结果:

    思路要清晰,再考虑实现方法.

    我们将结果放在permission.py里边:

     我们需要将下图,红框内的内容注释掉

     循环的数据改成权限的查询结果:

     在上边,将菜单的列表,修改成菜单的字典

     

    下面的ret替换成"menu_dict"

     

    下面的菜单列表,修改成菜单字典

     我们再看一下:

    两个for循环是一样的,可以把下面的for循环去掉

     现在的情况,结合下图,我们把得到的字典,放在了session当中了.

     

    运行,,我们再次登录一下alex的账户

    得到下面的结果:

    这个时候相当于进去了.

    但是出错的原因是?我们在前面已经将menu_list修改成了,menu_dict

    下面我们进行修改一下:修改之后的内容

    在处理menu.html之前,我们需要将menu.html移动到rbac文件夹中

     

    menu中存储的是一级菜单的结果:

    下面我们开始写二级菜单:

     上层表示的是一级菜单,下层表示的是二级菜单:

    运行程序:

    目前的状况是,列表存在问题:

     我们.需要将下图所示的div拿到menu.html中

    下面是展示的效果:

    我们将css样式放在里面,让左侧的导航栏显示的好看一点:

     点击,刷新浏览器,没有显示效果,如何处理?(清除"缓存的内容")这样结果就能显示出来了

     现在的情况是html不应该是手写的,应该是for循环出来的

    现在,相当于是,我们拿到的是键后边的两个字典

     下面,我们通过字典来进行循环生成,

    title显示的是一级菜单的内容,body显示的是二级菜单的内容

     

     一定要注意,这些细节的内容,都是需要改的

    这样就可以得到下面的界面了

    现在我们想要的是点击"菜单"进行收缩菜单

    通过js进行操作

     

    上边加上异常hide,可以成功进行隐藏.

    移除之后,就可以展开了

     下面我们开始在layout.html中进行操作,防止重名加上父级标签

     点击事件:

    下一个时间

    $(this)就是当前点击的标签.next之后,就找到下一个了,加上"hide"

    运行:

    点击一下,就关上了,但是打不开了,我们需要再点击一下要移除掉hide类

    将addClass换成toggleClass

    总结:很短的代码实现强大的功能:

    这样就可以实现点击,展开和收缩了.

    我们点击的是title标签

    next()指代的是,下面的body里边的整个内容.

     

  • 相关阅读:
    ie8 不能加载dll的问题解决
    Delphi 释放数组中的数据
    CSS: 首字母字体变大时下划线不对齐的解决方法
    谈谈一些有趣的CSS题目(十三)-- 巧妙地制作背景色渐变动画!
    吃透css3之3d属性--perspective和transform
    vue-cli中的webpack配置
    转载 webstrom识别 React语法
    CSS 布局整理(************************************************)
    巧用chrome开发者工具
    详解Webpack2的那些路径
  • 原文地址:https://www.cnblogs.com/studybrother/p/10567848.html
Copyright © 2011-2022 走看看