zoukankan      html  css  js  c++  java
  • Django 权限管理

    一 权限管理 初始版

    结构

    1.创建rbac应用

    2.在models中创建对象

    models

    from django.db import models
    
    class Permission(models.Model):
        """
        权限表
        """
        title = models.CharField(verbose_name='标题',max_length=32)
        url = models.CharField(verbose_name="含正则URL",max_length=64)
        is_menu = models.BooleanField(verbose_name="是否是菜单")
    
        class Meta:
            verbose_name_plural = "权限表"
    
        def __str__(self):
            return self.title
    
    class User(models.Model):
        """
        用户表
        """
        username = models.CharField(verbose_name='用户名',max_length=32)
        password = models.CharField(verbose_name='密码',max_length=64)
        email = models.CharField(verbose_name='邮箱',max_length=32)
    
        roles = models.ManyToManyField(verbose_name='具有的所有角色',to="Role",blank=True)
    
        class Meta:
            verbose_name_plural = "用户表"
    
        def __str__(self):
            return self.username
    
    class Role(models.Model):
        """
        角色表
        """
        title = models.CharField(max_length=32)
        permissions = models.ManyToManyField(verbose_name='具有的所有权限',to='Permission',blank=True)
        class Meta:
            verbose_name_plural = "角色表"
    
        def __str__(self):
            return self.title
    models

    3.基于Django admin录入权限数据

    注意;需要在admin.py 中作如下操作(只针对从用admin导入数据时配置,当然也可以从数据库中直接添加)

    from django.contrib import admin
    
    from . import models
    
    
    admin.site.register(models.Permission)
    admin.site.register(models.User)
    admin.site.register(models.Role)

    4.用户登录程序

    根据输入的用户名和密码得到相应的user,
    根据user对象获取其拥有的角色和具有的权限并去重并且将权限表中的url放入seesion中,将这部分操作的代码抽取到service包下的init_permission.py
    下的init_permission(request,user)方法中,然后在views中调用该方法即可,
    - 获取当前用户具有的所有权限(去重)
    - 获取权限中的url,放置到session中

    def init_permission(user,request):
        """
        初始化权限信息,获取权限信息并放置到session中。
        :param user:
        :param request:
        :return:
        """
        permission_list = user.roles.values('permissions__title', 'permissions__url', 'permissions__is_menu').distinct()
        url_list = []
        for item in permission_list:
            url_list.append(item['permissions__url'])
        print(url_list)
        request.session['permission_url_list'] = url_list
    init_permission.py

    5.编写中间件

    import re
    
    from django.shortcuts import redirect,HttpResponse
    from django.conf import settings
    
    class MiddlewareMixin(object):
        def __init__(self, get_response=None):
            self.get_response = get_response
            super(MiddlewareMixin, self).__init__()
    
        def __call__(self, request):
            response = None
            if hasattr(self, 'process_request'):
                response = self.process_request(request)
            if not response:
                response = self.get_response(request)
            if hasattr(self, 'process_response'):
                response = self.process_response(request, response)
            return response
    
    
    class RbacMiddleware(MiddlewareMixin):
    
        def process_request(self,request):
            # 1. 获取当前请求的URL
            # request.path_info
            # 2. 获取Session中保存当前用户的权限
            # request.session.get("permission_url_list')
            current_url = request.path_info
    
            # 当前请求不需要执行权限验证(白名单)
            for url in settings.VALID_URL:
                if re.match(url,current_url):
                    return None
    
            permission_list = request.session.get("permission_url_list")
            if not permission_list:
                return redirect('/login/')
    
            flag = False
            for db_url in permission_list:
                regax = "^{0}$".format(db_url)
                if re.match(regax, current_url):
                    flag = True
                    break
    
            if not flag:
                return HttpResponse('无权访问')
    rbac.py

    a,获取当前访问的路径 request.path_info
    b,在setting中配置不需要验证的url--白名单(人人登录后就可以访问的如login admin.*)然后调用

    VALID_URL = [
        "/login/",
        "/admin.*"
    ]

    根据正则判断当前路径是否在白名单中,白名单中的路径要严格的控制以什么开头和以什么结尾,如果是白名单return None 继续执行后面的代码
    如果不是直接跳转到登录
    c,不是白名单的话,则判断是否已经登录,最简单的方法就是获取当前session 看是里面的url列表是否为空,如果为空的话说明没有登录,直接
    调转到登陆,不让他执行后续操作
    d,url list不为空的话就说明已经登陆了,进一步看当前的访问路径是否在是否在urllist中,在的话就说明用户具有操作该url的权限否则就说明该用户没有
    访问权限,直接return HttpResponse("无权访问")
    注意:中间件创建完成之后。需要在settings中的MIDDLEWARE最后添加'rbac.middlewares.rbac.RbacMiddleware',

    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
        'rbac.middlewares.rbac.RbacMiddleware',
    ]

    示例一权限管理 加强

    对于权限管理,不单单的只是控制能不能访问某个路径,而且还需要根据用户的权限,当用户访问某个页面时,在页面上展示什么,比如某些用户
    虽然能访问首页,但是他没有添加用户的权限,这时就不能将添加按钮展现在首页,而对于具有添加用户权限的用户则需要将添加用户的按钮展示
    在首页上

    在访问列表页面时,是否需要判断:有无添加权限,有无删除权限,有无编辑权限;

    1.在rbac下的models中添加Group类,在权限表中添加code字段和外键group

    class Group(models.Model):
        """
        权限组
        """
        caption = models.CharField(verbose_name='组名称',max_length=16)
        
    class Permission(models.Model):
        """
        权限表
        """
        title = models.CharField(verbose_name='标题',max_length=32)
        url = models.CharField(verbose_name="含正则URL",max_length=64)
        is_menu = models.BooleanField(verbose_name="是否是菜单")
        code = models.CharField(verbose_name="代码",max_length=16)
        group = models.ForeignKey(verbose_name='所属组',to="Group")
    
        class Meta:
            verbose_name_plural = "权限表"
    
        def __str__(self):
            return self.title


    2.在rbac/service/init_permission.py/init_permission类中进行修改

    结构化数据模型

    data = {
                1: {
                    'codes': ['list','add','edit','del'],
                    'urls':[
                        /userinfo/,
                        /userinfo/add/,
                        /userinfo/edit/(d+)/,
                        /userinfo/del/(d+)/,
                    ]
                },
                2: {
                    'codes': ['list','add','edit','del'],
                    'urls':[
                        /userinfo/,
                        /userinfo/add/,
                        /userinfo/edit/(d+)/,
                        /userinfo/del/(d+)/,
                    ]
                },
                
            }
    permission_list = user.roles.values('permissions__title',
                                            "permissions__code",
                                            'permissions__url',
                                            'permissions__is_menu',
                                            "permissions__group__id",
                                            ).distinct()
    
    
    
    result={}
    for item in permission_list:
        groupid=item["permissions__group__id"]
        code=item["permissions__code"]
        url=item["permissions__url"]
    
        if groupid in result:
            result[groupid]["codes"].append(code)
            result[groupid]["urls"].append(url)
        else:
            result[groupid]={
                "codes":[code,],
                "urls":[url,]
            }
    
    
    print(result)
    
    
    request.session[settings.PERMISSIONS_URL_DICT_KEY] = result

    3.对中间件进行修改

    import re
    
    from django.shortcuts import redirect,HttpResponse
    from django.conf import settings
    
    class MiddlewareMixin(object):
        def __init__(self, get_response=None):
            self.get_response = get_response
            super(MiddlewareMixin, self).__init__()
    
        def __call__(self, request):
            response = None
            if hasattr(self, 'process_request'):
                response = self.process_request(request)
            if not response:
                response = self.get_response(request)
            if hasattr(self, 'process_response'):
                response = self.process_response(request, response)
            return response
    
    
    class RbacMiddleware(MiddlewareMixin):
    
        def process_request(self,request):
            # 1. 获取当前请求的URL
            # request.path_info
            # 2. 获取Session中保存当前用户的权限
            # request.session.get("permission_url_list')
            current_url = request.path_info
    
            # 当前请求不需要执行权限验证
            for url in settings.VALID_URL:
                if re.match(url,current_url):
                    return None
    
            permission_dict = request.session.get(settings.PERMISSION_URL_DICT_KEY)
            if not permission_dict:
                return redirect('/login/')
    
            flag = False
            for group_id,code_url in permission_dict.items():
    
                for db_url in code_url['urls']:
                    regax = "^{0}$".format(db_url)
                    if re.match(regax, current_url):
                        request.permission_code_list = code_url['codes']
                        flag = True
                        break
                if flag:
                    break
    
            if not flag:
                return HttpResponse('无权访问')
    rbac.py

    4.对views进行操作,是否页面上显示功能按钮:

    方法1:在模块中进行判断

    {% if "add/edit/del" in request.permission_code_list %}
      <a href="">添加/编辑/删除</a>
    {% endif%}

    方法二:
    在views中利用面向对象

    class BasePagePermission(object):
        def __init__(self,code_list):
            self.code_list = code_list
    
        def has_add(self):
            if "add" in self.code_list:
                return True
    
        def has_edit(self):
            if 'edit' in self.code_list:
                return True
        def has_del(self):
            if 'del' in self.code_list:
                return True
    
    def userinfo(request):
        page_permission = BasePagePermission(request.permission_code_list)
        data_list = [
            {'id':1,'name':'xxx1'},
            {'id':2,'name':'xxx2'},
            {'id':3,'name':'xxx3'},
            {'id':4,'name':'xxx4'},
            {'id':5,'name':'xxx5'},
        ]
    
        return render(request,'userinfo.html',{'data_list':data_list,'page_permission':page_permission})

    5.模块中进行判断

    {%   if pagepermission.has_add %}
    <p><a href="">添加</a></p>
    {% endif %}

    示例二 菜单展示

    1.在models中添加Menu对象(表)以及和Group建立起一对多的对应关系

    from django.db import models
    
    class Menu(models.Model):
        """
        菜单组
        """
        title = models.CharField(max_length=32)
    
    
    class Group(models.Model):
        """
        权限组
        """
        caption = models.CharField(verbose_name='组名称',max_length=16)
        menu = models.ForeignKey(verbose_name='所属菜单',to='Menu')
    
    
    class Permission(models.Model):
        """
        权限表
        """
        title = models.CharField(verbose_name='标题',max_length=32)
        url = models.CharField(verbose_name="含正则URL",max_length=64)
        is_menu = models.BooleanField(verbose_name="是否是菜单")
        code = models.CharField(verbose_name="代码",max_length=16)
        group = models.ForeignKey(verbose_name='所属组',to="Group")
    
        class Meta:
            verbose_name_plural = "权限表"
    
        def __str__(self):
            return self.title
    
    class User(models.Model):
        """
        用户表
        """
        username = models.CharField(verbose_name='用户名',max_length=32)
        password = models.CharField(verbose_name='密码',max_length=64)
        email = models.CharField(verbose_name='邮箱',max_length=32)
    
        roles = models.ManyToManyField(verbose_name='具有的所有角色',to="Role",blank=True)
    
        class Meta:
            verbose_name_plural = "用户表"
    
        def __str__(self):
            return self.username
    
    class Role(models.Model):
        """
        角色表
        """
        title = models.CharField(max_length=32)
        permissions = models.ManyToManyField(verbose_name='具有的所有权限',to='Permission',blank=True)
        class Meta:
            verbose_name_plural = "角色表"
    
        def __str__(self):
            return self.title
    models

    2.- 初始化: 获取菜单信息+权限信息

    from django.conf import settings
    
    
    def init_permission(user,request):
        """
        初始化权限信息,获取权限信息并放置到session中。
        :param user:
        :param request:
        :return:
        """
        permission_list = user.roles.values('permissions__title',              # 用户列表
                                            'permissions__url',
                                            'permissions__code',
                                            'permissions__is_menu',            # 是否是菜单
                                            'permissions__group_id',
                                            'permissions__group__menu_id',     # 菜单ID
                                            'permissions__group__menu__title',#  菜单名称
                                            ).distinct()
    
        menu_list = []
        # 去掉不是菜单的URL
        for item in permission_list:
            if not item['permissions__is_menu']:
                continue
            tpl = {
                    'menu_id':item['permissions__group__menu_id'],
                    'menu_title':item['permissions__group__menu__title'],
                    'title':item['permissions__title'],
                    'url':item['permissions__url'],
                    'active':False,
            }
            menu_list.append(tpl)
    
        request.session[settings.PERMISSION_MENU_KEY] = menu_list
    
    
        # 权限相关
        result = {}
        for item in  permission_list:
            group_id = item['permissions__group_id']
            code = item['permissions__code']
            url = item['permissions__url']
            if group_id in result:
                result[group_id]['codes'].append(code)
                result[group_id]['urls'].append(url)
            else:
                result[group_id] = {
                    'codes':[code,],
                    'urls':[url,]
                }
    
        request.session[settings.PERMISSION_URL_DICT_KEY] = result
    init_permission.py

    结构化数据 示例;

    mport re
    menu_list = [
        {'menu_id':1, 'menu_title':'菜单一','title':'用户列表','url':'/userinfo/','active':False},
        {'menu_id':1, 'menu_title':'菜单一','title':'订单列表','url':'/order/','active':False},
        {'menu_id':2, 'menu_title':'菜单二','title':'xxx列表','url':'/xxx/','active':False},
        {'menu_id':2, 'menu_title':'菜单二','title':'iii列表','url':'/uuu/','active':False},
    ]
    
    current_url = "/userinfo/"
    
    res={}
    
    for tem in menu_list:
        mid=tem["menu_id"]
        mtitle=tem["menu_title"]
        title=tem["title"]
        url=tem["url"]
        active=False
        if re.match(url,current_url):
            active=True
        if mid in res:
            res[mid]["children"].append({"title":title,"url":url,"active":active})
            if active:
                res[mid]["active"]=True
        else:
    
            res[mid]={
                "menu_id":mid,
                "menu_title":mtitle,
                "active":active,
                "children":[
                    {"title":title,"url":url,"active":True},
    
                ]
            }
    
    print(res)

    结果:

    aa={
        1:
            {
                'menu_id': 1,
                'menu_title': '菜单一',
                'active': True,
                'children': [{'title': '用户列表', 'url': '/userinfo/', 'active': True},
                             {'title': '订单列表', 'url': '/order/', 'active': True}]
            },
        2:
            {
            'menu_id': 2,
            'menu_title': '菜单二',
            'active': True,
            'children': [{'title': 'xxx列表', 'url': '/xxx/', 'active': True},
                         {'title': 'iii列表', 'url': '/uuu/', 'active': True}]
           }
    }

    3.显示多级菜单

    模块中操作
    其中菜单部分由自定义标签生产
    具体展示页面则用模块继承:如userinfo

    a. base.html

    {% load rbac %}
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <link rel="stylesheet" href="/static/rbac/rbac.css">
    </head>
    <body>
    <div style="float: left; 20%;height: 900px;background-color: darkgrey">
      {% menu_html request %}
    </div>
    <div style="float: left; 80%">
       {% block content %}
        {% endblock %}
    </div>
    </body>
    <script src="/static/jquery-3.2.1.min.js"></script>
    <script src="/static/rbac/rbac.js"></script>
    </html>

    b. userinfo.html

    {%  extends "base.html" %}
    {% block content %}
    {%   if pagepermission.has_add %}
    <p><a href="">添加</a></p>
    {% endif %}
    <table>
        <thead>
        <th>id</th>
        <th>name</th>
        <th>操作</th>
        </thead>
    
        <tbody>
        {% for foo in data_list %}
          <tr>
          <td>{{ foo.id }}</td>
          <td>{{ foo.name }}</td>
          <td>
            {% if pagepermission.has_edit %}
                <a href="#">编辑</a>
            {% endif %}
    
          {% if pagepermission.has_del %}
              <a href="#">删除</a>
          {% endif %}
          </td>
          </tr>
        {% endfor %}
    
    {% endblock %}

    c.建立自定义标签

    rbac/templatetags/rabc.py

    import re
    from django.template import Library
    from django.conf import settings
    register = Library()
    
    @register.inclusion_tag("menuList.html")
    def menu_html(request):
        menu_list = request.session[settings.PERMISSION_MENU_KEY]
        current_url = request.path_info
        result = {}
        for item in menu_list:
            url = item['url']
            regex = "^{0}$".format(url)
            active = False
            if re.match(regex,current_url):
                active = True
    
            menu_id = item['menu_id']
            if menu_id in result:
                result[menu_id]['children'].append({'title': item['title'], 'url': item['url'], 'active': active})
                if active:
                    result[menu_id]['active'] = True
            else:
                result[menu_id] = {
                    'menu_id': menu_id,
                    'menu_title': item['menu_title'],
                    'active': active,
                    'children': [
                        {'title': item['title'], 'url': item['url'], 'active': active},
                    ]
                }
    
        return {'menu_dict':result}
    rbac.py

    d. menuList.html

    {% for k,item in menu_dict.items %}
     <div class="item">
           <div class="item_title">{{ item.menu_title }}</div>
           {% if item.active %}
            <div class="item_permissions">
               {% else %}
               <div class="item_permissions hide">
           {% endif %}
              {% for v in item.children %}
               {% if v.active %}
                   <a href="#" class="active">{{ v.title }}</a>
                   {% else %}
                   <a href="#">{{ v.title }}</a>
               {% endif %}
              {% endfor %}
           </div>
       </div>
    {% endfor %}

    e.相关css和js

    在rbac建立static/rbac目录在其中创建rabc.css和rabc.js

    .item_permissions{
        padding: 3px 10px;
    }
    
    .item_permissions a{
        display: block;
    }
    
    .item_permissions a.active{
        color: red;
    }
    
    .hide{
        display: none;
    }
    rbac.css
    $(function () {
        $(".item_title").click(function () {
            $(this).next().toggleClass("hide")
        });
    })
    rbac.js

    四.菜单展示改进

    因为当我们对用户部分进行增删改时,用户列表也应该事展开的,但是我们上面所写的当我们点击增加删除编辑按钮时,用户菜单会闭合而不是展开的
    对此我们进行了改进

    1..在权限表中增加字段menu_gp,并删除is_menu字段

    from django.db import models
    
    class Menu(models.Model):
        '''
        菜单表
        '''
        title=models.CharField(max_length=32,verbose_name="菜单名称")
    
    class Group(models.Model):
        '''
        权限组
        '''
        caption=models.CharField(max_length=32,verbose_name="组名称")
        menu=models.ForeignKey(verbose_name="所属菜单",to="Menu",default=1)
    
    class Permission(models.Model):
        """
        权限表
        """
        title = models.CharField(verbose_name='标题',max_length=32)
        url = models.CharField(verbose_name="含正则URL",max_length=64)
        # is_menu = models.BooleanField(verbose_name="是否是菜单")
        menu_gp=models.ForeignKey(verbose_name="组内菜单",to="Permission",null=True,blank=True)
        code=models.CharField(max_length=32,verbose_name="代码",default="list")
        group=models.ForeignKey(verbose_name="s所在权限组",to="Group",default=1)
    
    
        class Meta:
            verbose_name_plural = "权限表"
    
        def __str__(self):
            return self.title
    
    class User(models.Model):
        """
        用户表
        """
        username = models.CharField(verbose_name='用户名',max_length=32)
        password = models.CharField(verbose_name='密码',max_length=64)
        email = models.CharField(verbose_name='邮箱',max_length=32)
    
        roles = models.ManyToManyField(verbose_name='具有的所有角色',to="Role",blank=True)
    
        class Meta:
            verbose_name_plural = "用户表"
    
        def __str__(self):
            return self.username
    
    class Role(models.Model):
        """
        角色表
        """
        title = models.CharField(max_length=32)
        permissions = models.ManyToManyField(verbose_name='具有的所有权限',to='Permission',blank=True)
        class Meta:
            verbose_name_plural = "角色表"
    
        def __str__(self):
            return self.title
    moedels.py

    2.修改初始化中菜单

    from django.conf import settings
    def init_permission(user,request):
        """
        初始化权限信息,获取权限信息并放置到session中。
        :param user:
        :param request:
        :return:
        """
        permission_list = user.roles.values('permissions__title',
                                            "permissions__code",
                                            "permissions__id",
                                            'permissions__url',
                                            'permissions__menu_gp_id',
                                            "permissions__group__id",
                                            "permissions__group__menu_id",
                                            "permissions__group__menu__title",
                                            ).distinct()
    
    
    
        menu_list=[]
        for item in permission_list:
            tpl={
                "id":item["permissions__id"],
                "title":item["permissions__title"],
                "menu_title":item["permissions__group__menu__title"],
                "url":item["permissions__url"],
                "menu_id":item["permissions__group__menu_id"],
                "menu_gp_id":item["permissions__menu_gp_id"],
            }
            menu_list.append(tpl)
    
        request.session[settings.PERMISSIONS_MENU_KEY]=menu_list
    
    
    
        # menu_list=[]
        # for item in permission_list:
        #     if not item["permissions__is_menu"]:
        #         continue
        #
        #     tpl={
        #         "menu_id":item["permissions__group__menu_id"],
        #         "menu_title":item["permissions__group__menu__title"],
        #         "title":item["permissions__title"],
        #         "url":item["permissions__url"],
        #         "active":False,
        #     }
        #
        #     menu_list.append(tpl)
        # print(menu_list)
        # request.session[settings.PERMISSIONS_MENU_KEY]=menu_list
         #权限管理
        result={}
        for item in permission_list:
            groupid=item["permissions__group__id"]
            code=item["permissions__code"]
            url=item["permissions__url"]
    
            if groupid in result:
                result[groupid]["codes"].append(code)
                result[groupid]["urls"].append(url)
            else:
                result[groupid]={
                    "codes":[code,],
                    "urls":[url,]
                }
    
    
        print(result)
    
    
        request.session[settings.PERMISSIONS_URL_DICT_KEY] = result
    init_permission.py

    3.修改定义标签

    import  re
    from django.conf import settings
    from django.template import Library
    register = Library()
    @register.inclusion_tag("menuList.html")
    def menu_html(request):
        menu_list=request.session.get(settings.PERMISSIONS_MENU_KEY)
        currenturl=request.path_info
    
    
        menu_dict={}
        for item in menu_list:
            if not item["menu_gp_id"]:
                menu_dict[item["id"]]=item
        for item in menu_list:
            regex="^{0}$".format(item["url"])
            if re.match(regex,currenturl):
                menu_gp_id=item["menu_gp_id"]
                if not menu_gp_id:
                    menu_dict[item["id"]]["active"]=True
                else:
                    menu_dict[item["menu_gp_id"]]["active"]=True
        '''
        menu_dict={
        1: {'id': 1, 'title': '用户列表', 'url': '/userinfo/', 'menu_gp_id': None, 'menu_id': 1, 'menu_title': '菜单管理', 'active': True},
        5: {'id': 5, 'title': '订单列表', 'url': '/order/', 'menu_gp_id': None, 'menu_id': 2, 'menu_title': '菜单2'}}
        '''
        print(menu_dict,"11111111111111111111111111111111111111111111")
        result = {}
        for item in menu_dict.values():
            menu_id=item["menu_id"]
            menu_title=item["menu_title"]
            active=item.get("active")
            url=item["url"]
            title=item["title"]
    
            if menu_id in result:
                result[menu_id]["children"].append({"title":title,"url":url,"active":active})
                if active:
                    result[menu_id]["active"]=True
            else:
                result[menu_id]={
                    "menu_id":menu_id,
                    "menu_title":menu_title,
                    "active":active,
                    "children":[
                        {"title":title,"url":url,"active":active},
                    ]
    
                }
    
        print(result)
    
        # for item in menu_list:
        #     menu_id=item["menu_id"]
        #     menu_title=item["menu_title"]
        #     title=item["title"]
        #     url=item["url"]
        #     active=False
        #     regex="^{0}$".format(url)
        #     if re.match(regex,currenturl):
        #         active=True
        #
        #     if menu_id in result:
        #         result[menu_id]["children"].append({{"title":title,"url":url,"active":active},})
        #         if active:
        #             result[menu_id]["active"]=active
        #     else:
        #         result[menu_id]={
        #             "menu_id":menu_id,
        #             "menu_title":menu_title,
        #             "active":active,
        #             "children":[
        #                 {"title":title,"url":url,"active":active},
        #             ]
        #         }
        # print(result)
        return {"menu_dict":result}
    rbac.py

    其他不做修改

     

  • 相关阅读:
    炒股,短线学习
    炒股,短线学习
    截图贴图snipaste工具有什么用处、ppt疯狂小插件iSlide —— 最好用的PPT插件,人手必备
    截图贴图snipaste工具有什么用处、ppt疯狂小插件iSlide —— 最好用的PPT插件,人手必备
    截图贴图snipaste工具有什么用处
    网络模拟工具使用和学习GNS3+Wireshark
    VS Code行内样式提示插件
    javascript 两张图片切换 三目运算符
    小程序获取openid 小程序授权
    小程序点击跳转外部链接 微信小程序提示:不支持打开非业务域名怎么办 使用web-view 配置业务域名
  • 原文地址:https://www.cnblogs.com/ctztake/p/7812041.html
Copyright © 2011-2022 走看看