Browse Source

添加admin权限页面

wangwei 4 years ago
parent
commit
79a664d8ba
45 changed files with 1811 additions and 730 deletions
  1. BIN
      server/api/__pycache__/__init__.cpython-38.pyc
  2. BIN
      server/api/__pycache__/admin.cpython-38.pyc
  3. BIN
      server/api/__pycache__/models.cpython-38.pyc
  4. BIN
      server/api/__pycache__/serializers.cpython-38.pyc
  5. BIN
      server/api/__pycache__/urls.cpython-38.pyc
  6. BIN
      server/api/__pycache__/views.cpython-38.pyc
  7. 0 3
      server/api/admin.py
  8. 0 5
      server/api/apps.py
  9. 0 46
      server/api/form.py
  10. 0 52
      server/api/models.py
  11. 0 30
      server/api/serializers.py
  12. 0 3
      server/api/tests.py
  13. 0 32
      server/api/urls.py
  14. 0 331
      server/api/views.py
  15. 0 22
      server/manage.py
  16. 0 2
      server/server/__init__.py
  17. BIN
      server/server/__pycache__/__init__.cpython-38.pyc
  18. BIN
      server/server/__pycache__/settings.cpython-38.pyc
  19. BIN
      server/server/__pycache__/urls.cpython-38.pyc
  20. BIN
      server/server/__pycache__/wsgi.cpython-38.pyc
  21. 0 16
      server/server/asgi.py
  22. 0 137
      server/server/settings.py
  23. 0 20
      server/server/urls.py
  24. 0 16
      server/server/wsgi.py
  25. 2 2
      src/api/sys/user.ts
  26. 4 0
      src/locales/lang/en/routes/permission.ts
  27. 4 0
      src/locales/lang/zh_CN/routes/permission.ts
  28. 16 0
      src/router/menus/modules/permission.ts
  29. 41 1
      src/router/routes/modules/permission.ts
  30. 1 1
      src/router/routes/modules/table.ts
  31. 1 1
      src/router/routes/modules/test.ts
  32. 192 0
      src/views/permission/admin/add.vue
  33. 69 0
      src/views/permission/admin/data.ts
  34. 265 0
      src/views/permission/admin/index.vue
  35. 0 0
      src/views/permission/admin_log/data.ts
  36. 181 0
      src/views/permission/admin_log/index.vue
  37. 110 0
      src/views/permission/admin_log/model.vue
  38. 196 0
      src/views/permission/group/add.vue
  39. 0 0
      src/views/permission/group/data.ts
  40. 233 0
      src/views/permission/group/index.vue
  41. 9 9
      src/views/permission/role/index.vue
  42. 142 0
      src/views/permission/rule/add.vue
  43. 85 0
      src/views/permission/rule/data.ts
  44. 259 0
      src/views/permission/rule/index.vue
  45. 1 1
      src/views/table/table/index.vue

BIN
server/api/__pycache__/__init__.cpython-38.pyc


BIN
server/api/__pycache__/admin.cpython-38.pyc


BIN
server/api/__pycache__/models.cpython-38.pyc


BIN
server/api/__pycache__/serializers.cpython-38.pyc


BIN
server/api/__pycache__/urls.cpython-38.pyc


BIN
server/api/__pycache__/views.cpython-38.pyc


+ 0 - 3
server/api/admin.py

@@ -1,3 +0,0 @@
-from django.contrib import admin
-
-# Register your models here.

+ 0 - 5
server/api/apps.py

@@ -1,5 +0,0 @@
-from django.apps import AppConfig
-
-
-class ApiConfig(AppConfig):
-    name = 'api'

+ 0 - 46
server/api/form.py

@@ -1,46 +0,0 @@
-# -*- encoding: utf-8 -*-
-'''
-@Desc    :   用户自定义表单
-@File    :   form.py
-@Time    :   2021/03/26 12:23:15
-@Author  :   wang
-@Version :   1.0
-'''
-
-# here put the import lib
-
-from django import forms
-from django.core.exceptions import ValidationError
-
-
-class MenuForm(forms.Form):
-    path = forms.CharField(required=True, error_messages={
-        'required': '路径必须输入',
-    })
-    name = forms.CharField(required=True, error_messages={
-        'required': '路径名必须输入',
-    })
-    component = forms.CharField(required=True, error_messages={
-        'required': '组件路径必须输入',
-    })
-    title = forms.CharField(required=True, error_messages={
-        'required': '菜单名必须输入',
-    })
-    # icon = forms.CharField(required=True,error_messages={
-    #   'required':'菜单图标必须输入',
-    # })
-
-    # # 单个字段验证 clean_xxx
-    # def clean_password(self):
-    #   password = self.cleaned_data.get('password')
-    #   if password and password.isdigit():
-    #     raise ValidationError("密码不能是纯数字")
-    #   return password
-
-    # # 全局验证
-    # def clean(self):
-    #   password = self.cleaned_data.get('password',None)
-    #   confirm = self.cleaned_data.get('confirm',None)
-    #   if password != confirm:
-    #     raise ValidationError({'confirm':"两次密码输入不一致"})
-    #   return self.cleaned_data

+ 0 - 52
server/api/models.py

@@ -1,52 +0,0 @@
-from django.db import models
-from django.utils import timezone
-
-
-class User(models.Model):
-    username = models.CharField(max_length=20, unique=True, null=False)
-    nickname = models.CharField(max_length=20, null=True)
-    password = models.CharField(max_length=32, null=False)
-    roleName = models.CharField(max_length=32, null=True)
-    value = models.CharField(max_length=32, null=True)
-    status = models.BooleanField(default=1)
-    detail = models.CharField(max_length=124, null=True)
-    token = models.CharField(max_length=500, unique=True, null=False)
-    menus = models.ManyToManyField("Menu", null=True)
-
-    def __unicode__(self):
-        return self.username
-
-# class Metas(models.Model):
-  # title = models.CharField(max_length=32)
-  # affix = models.BooleanField(default=1)
-  # icon = models.CharField(max_length=32)
-
-
-class Menu(models.Model):
-    path = models.CharField(max_length=64, blank=True, null=True)  # 映射数据路径
-    name = models.CharField(max_length=64, verbose_name="菜单名称")  # 菜单名称
-    component = models.CharField(
-        max_length=512, null=True, blank=True)  # view页面路径
-    parent = models.ForeignKey(
-        "Menu", on_delete=models.DO_NOTHING, null=True, blank=True)  # 父节点
-    redirect = models.CharField(max_length=64, null=True, blank=True)  # 重定向
-    detail = models.CharField(max_length=64, null=True, blank=True)  # 备注
-    status = models.BooleanField(default=1)
-    create_time = models.DateTimeField(
-        auto_created=True, default=timezone.now)
-    # meta = models.OneToOneField(Metas,null=True,on_delete=models.CASCADE)
-    # users = models.ManyToManyField("User", null=True)
-    # 以下三个为菜单meta配置
-    title = models.CharField(max_length=32, null=True)
-    affix = models.BooleanField(default=1)
-    icon = models.CharField(max_length=120, null=True)
-
-    def __unicode__(self):
-        return self.name
-
-    def __str__(self):
-        return self.name
-
-    class Meta():
-        verbose_name = "菜单"
-        verbose_name_plural = verbose_name

+ 0 - 30
server/api/serializers.py

@@ -1,30 +0,0 @@
-# -*- encoding: utf-8 -*-
-'''
-@Desc    :
-@File    :   serializers.py
-@Time    :   2021/04/07 15:19:03
-@Author  :   wang
-@Version :   1.0
-'''
-
-# here put the import lib
-
-from rest_framework import serializers
-from api.models import *
-
-
-# 通过模型生成序列化器
-class UserSerializer(serializers.ModelSerializer):
-    class Meta:
-        model = User
-        fields = ["username", "password"]
-        # fields = "__all__"
-        # exclude = ["token"]
-
-
-class MenuSerializer(serializers.ModelSerializer):
-    class Meta:
-        model = Menu
-        # fields = []
-        fields = "__all__"
-        # exclude = []

+ 0 - 3
server/api/tests.py

@@ -1,3 +0,0 @@
-from django.test import TestCase
-
-# Create your tests here.

+ 0 - 32
server/api/urls.py

@@ -1,32 +0,0 @@
-# -*- encoding: utf-8 -*-
-'''
-@Desc    :
-@File    :   urls.py
-@Time    :   2021/03/24 10:10:25
-@Author  :   wang
-@Version :   1.0
-'''
-
-# here put the import lib
-
-from django.urls import path
-from . import views
-
-app_name = 'api'  # namespace 命名空间
-
-# 路由列表 urlpatterns
-urlpatterns = [
-    # 不能以/ 开头
-    path('login', views.login.as_view(), name='login'),
-    path('userInfo/id', views.getUserInfoById.as_view(), name='get_user_info'),
-    path('getUserList', views.getUserList.as_view(), name='get_user_list'),
-    path('addUser', views.addUser.as_view(), name='add_user'),
-    path('editUser', views.editUser.as_view(), name='edit_user'),
-    path('deleteUser', views.deleteUser.as_view(), name='delete_user'),
-    path('getMenuListById', views.getMenuListById.as_view(), name='get_menu'),
-    path('getAllMenuList', views.getAllMenuList.as_view(), name='get_all_menu'),
-    path('addMenu', views.addMenu.as_view(), name='add_menu'),
-    path('editMenu', views.editMenu.as_view(), name='edit_menu'),
-    path('deleteMenu', views.deleteMenu.as_view(), name='delete_menu'),
-    path('upload', views.FileUpload.as_view(), name='upload'),
-]

+ 0 - 331
server/api/views.py

@@ -1,331 +0,0 @@
-from django.shortcuts import render, redirect, HttpResponse
-from django.http import JsonResponse, HttpResponseRedirect
-from django.views.generic import View
-# from . import models
-from .models import User, Menu
-from django.core.serializers import serialize
-from rest_framework.views import APIView
-from api.serializers import *
-import json
-
-# Create your views here.
-
-
-def format_useinfo(user):
-    format_user = json.loads(
-        serialize('json', user, ensure_ascii=False)
-    )
-    for item in format_user:
-        res = item.get('fields')
-        res['userId'] = item.get('pk')
-        res['roles'] = [
-            {
-                'roleName': res.get('roleName'), 'value': res.get('value')
-            }
-        ]
-        res.pop('roleName')
-        res.pop('value')
-        res.pop('password')
-    return res
-
-
-def format_uselist(list):
-    json_user = json.loads(
-        serialize('json', list, ensure_ascii=False)
-    )
-    res = []
-    for item in json_user:
-        item['fields'].pop('value')
-        item['fields'].pop('token')
-        user = item.get('fields')
-        user['id'] = item.get('pk')
-        # res['roles'] = [
-        #     {
-        #         'roleName': res.get('roleName'), 'value': res.get('value')
-        #     }
-        # ]
-        # res.pop('roleName')
-        # res.pop('value')
-        # res.pop('password')
-        res.append(user)
-    return res
-
-
-def format_login_result(user):
-    format_user = json.loads(
-        serialize('json', user, ensure_ascii=False)
-    )
-    for item in format_user:
-        res = {}
-        res['userId'] = item.get('pk')
-        res['menus'] = item.get('menus')
-        res['token'] = item.get('fields').get('token')
-        res['roles'] = [
-            {
-                'roleName': item.get('fields').get('roleName'), 'value': item.get('fields').get('value')
-            }
-        ]
-    return res
-
-
-def format_menus(querySetObj, mode):
-    # querySet 转json对象
-    data = json.loads(serialize('json', querySetObj, ensure_ascii=False))
-    # 定义一级菜单列表(无父id)
-    menus = []
-    # 子菜单列表
-    childrens = []
-    for item in data:
-        item['fields']['id'] = item.get('pk')
-        # mode True -- 否将某些字段合并处理成前台路由需要的数据结构;
-        if mode:
-            item['fields']['meta'] = {
-                "title": item['fields'].pop('title'),
-                "affix":  item['fields'].pop('affix'),
-                "icon": item['fields'].pop('icon'),
-            }
-            if (item.get('fields').get('parent') == None):
-                menus.append(item.get('fields'))
-            else:
-                childrens.append(item.get('fields'))
-
-            for children in childrens:
-                # 将列表转化成树形菜单
-                getTree(children, menus, childrens)
-
-        else:  # False -- 不必转成树结构, menuName 语言包需要到前台转中文
-            item['fields']["menuName"] = item['fields'].pop("title")
-            item['fields'].pop('affix')
-            menus.append(item.get('fields'))
-
-    return menus
-
-
-"""
-def getTree(children, menus, childrens)
-根据传递过来的父菜单id,递归设置各层次父菜单的子菜单列表
-:children: 单个子菜单
-:param menus: 父菜单列表 一级菜单
-:childrens: 子菜单列表,第二次调用
-"""
-
-
-def getTree(children, menus, childrens):
-    # 遍历父菜单
-    for m in menus:
-        # 当前菜单的id 和 子菜单的父id 是否匹配
-        if m.get('id') == children.get('parent'):
-            if not m.get('children'):
-                m['children'] = []
-            if not children in m['children']:  # 判单子菜单是否已存在
-                m['children'].append(children)
-
-            if m.get('children'):
-                for children in childrens:
-                    # 判断子菜单是否还有匹配的子菜单
-                    getTree(children, m.get('children'), childrens)
-
-
-# 请求---------------------------------------------------------------------
-class login(View):
-    def post(self, request):
-
-        data = request.POST
-
-        user = User.objects.all().filter(username=data.get(
-            'username'), password=data.get('password'))
-        print(user)
-        if user:
-            res = format_login_result(user)
-            return JsonResponse({'code': 1, 'msg': 'success', 'result': res}, json_dumps_params={"ensure_ascii": False})
-        else:
-            return JsonResponse({'code': 0, 'error': '用户名或密码不正确'}, json_dumps_params={"ensure_ascii": False})
-
-
-class getUserInfoById(View):
-    def post(self, request):
-        userid = request.POST.get('userId')
-        print('====================udsaersadf======')
-        print(userid)
-        print('====================udsaersadf======')
-
-        user = User.objects.all().filter(id=userid)
-        if user:
-            # res = {'codeList': ['1000', '3000', '5000']}
-            res = format_useinfo(user)
-            return JsonResponse({'code': 1, 'result': res, }, json_dumps_params={"ensure_ascii": False})
-        else:
-            return JsonResponse({'code': 0, 'type': 'error', 'message': 'no role,create role'}, json_dumps_params={"ensure_ascii": False})
-
-
-class getUserList(View):
-    def get(self, request):
-        users = User.objects.all()
-        if users:
-            res = format_uselist(users)
-            return JsonResponse({'code': 1, 'result': res, }, json_dumps_params={"ensure_ascii": False})
-        else:
-            return JsonResponse({'code': 0, 'type': 'error', 'message': 'no role,create role'}, json_dumps_params={"ensure_ascii": False})
-
-
-class addUser(APIView):
-    serialize_class = UserSerializer
-
-    def post(self, request):
-        # user = json.loads(request.body)
-        user = request.POST
-        userSer = UserSerializer(data=user)
-        if userSer.is_valid():
-            menuIdList = user.pop('menus')
-            menus = Menu.objects.all().filter(id__in=menuIdList)
-            user['token'] = user.get('username') + 'token'
-            userObj = User.objects.create(**user)
-            userObj.menus.set(menus)
-            return JsonResponse({'code': 1, 'result': {'data': '角色创建成功'}}, json_dumps_params={"ensure_ascii": False})
-        else:
-            print(userSer.errors)
-            return JsonResponse({'code': 0, 'type': 'error', 'message': '字段校验未通过', "result": userSer.errors}, json_dumps_params={"ensure_ascii": False})
-
-        # if User.objects.all().filter(username=data.get('username')):
-        #     return JsonResponse({'code': 0, 'type': 'error', 'message': '该角色名已被占用'}, json_dumps_params={"ensure_ascii": False})
-        # else:
-        #     data['token'] = data.get('username') + 'token'
-        #     userObj = User.objects.create(**data)
-        #     userObj.menus.set(menus)
-
-        #     return JsonResponse({'code': 1, 'result': {'data': '角色创建成功'}}, json_dumps_params={"ensure_ascii": False})
-
-
-class editUser(APIView):
-    serialize_class = UserSerializer
-
-    def post(self, request):
-        # data = json.loads(request.body)
-        data = request.POST
-        userSer = UserSerializer(data=data)
-        user = User.objects.all().filter(id=data.get('id')).first()
-
-        if not userSer.is_valid() and user.username != data.get('username'):
-            return JsonResponse({'code': 0, 'type': 'error', 'message': '字段校验未通过', 'result': userSer.errors}, json_dumps_params={"ensure_ascii": False})
-        else:
-            menuIdList = data.pop('menus')
-            menus = Menu.objects.all().filter(id__in=menuIdList)
-            user.username = data.get('username')
-            user.password = data.get('password')
-            user.nickname = data.get('nickname')
-            user.status = data.get('status')
-            user.detail = data.get('detail')
-            user.menus.set(menus)
-            user.save()
-            return JsonResponse({'code': 1, 'type': 'error', 'message': '修改成功', 'result': {'data': '修改成功'}}, json_dumps_params={"ensure_ascii": False})
-
-
-class deleteUser(View):
-    def post(self, request):
-        data = request.POST
-        user = User.objects.all().filter(id=data.get('id'))
-        try:
-            if user:
-                user.delete()
-                return JsonResponse({'code': 1, 'result': {'data': '删除角色成功'}}, json_dumps_params={"ensure_ascii": False})
-            else:
-                return JsonResponse({'code': 0, 'type': 'error', 'message': '查无此人'}, json_dumps_params={"ensure_ascii": False})
-        except Exception as e:
-            print(e)
-            return JsonResponse({'code': 0, 'type': 'error', 'message': e}, json_dumps_params={"ensure_ascii": False})
-
-
-class getMenuListById(View):
-    def post(self, request):
-        # 获取参数id
-        userId = request.POST.get('id')
-        print(userId)
-        # 当前用户
-        querSetUser = User.objects.all().filter(id=userId)
-        user = format_useinfo(querSetUser)
-        # 获取当前user 的menus(id集合)
-        menuIdList = user.get('menus')
-        # 匹配查询 id__in=[1,2,3]
-        querySetMenu = Menu.objects.all().filter(id__in=menuIdList)
-        menus = format_menus(querySetMenu, True)
-        return JsonResponse({'code': 1, 'result': menus})
-
-
-class getAllMenuList(View):
-    def get(self, request):
-        querySetObj = Menu.objects.all()
-        menus = format_menus(querySetObj, False)
-        return JsonResponse({'code': 1, 'result': {'format': True, 'menus': menus}}, json_dumps_params={"ensure_ascii": False})
-
-
-class addMenu(APIView):
-    serialize_class = MenuSerializer
-
-    def post(self, request):
-        # data = json.loads(request.body)
-        data = request.POST
-        data.title = data.pop('menuName')
-        menuSer = MenuSerializer(data=data)
-        if menuSer.is_valid():
-            if data.get('parent'):
-                parentId = data.pop('parent')
-                parent = Menu.objects.all().filter(id=parentId).first()
-                data['parent'] = parent
-            menuObj = Menu.objects.create(**data)
-            return JsonResponse({'code': 1, 'result': {'data': '菜单添加成功'}}, json_dumps_params={"ensure_ascii": False})
-        else:
-            return JsonResponse({'code': 0, 'type': 'error', 'message': '字段校验未通过', "result": menuSer.errors}, json_dumps_params={"ensure_ascii": False})
-
-
-class editMenu(APIView):
-    serialize_class = MenuSerializer
-
-    def post(self, request):
-        data = request.POST
-        data.title = data.pop('menuName')
-        menuSer = MenuSerializer(data=data)
-        if menuSer.is_valid():
-            menu = Menu.objects.all().filter(id=data.get('id')).first()
-            menu.icon = data.get('icon')
-            menu.title = data.get('title')
-            menu.path = data.get('path')
-            menu.reditect = data.get('reditect')
-            menu.component = data.get('component')
-            menu.status = data.get('status')
-            menu.detail = data.get('detail')
-            menu.parent = Menu.objects.all().filter(id=data.get('parent')).first()
-            menu.save()
-            return JsonResponse({'code': 1, 'result': {'data': '菜单修改成功'}}, json_dumps_params={"ensure_ascii": False})
-        else:
-            return JsonResponse({'code': 0, 'type': 'error', 'message': '字段校验未通过', "result": menuSer.errors}, json_dumps_params={"ensure_ascii": False})
-
-
-class deleteMenu(View):
-    def post(self, request):
-        data = request.POST
-        menu = Menu.objects.all().filter(id=data.get('id'))
-        try:
-            if menu:
-                menu.delete()
-                return JsonResponse({'code': 1, 'result': {'data': '删除菜单成功'}}, json_dumps_params={"ensure_ascii": False})
-            else:
-                return JsonResponse({'code': 0, 'type': 'error', 'message': '没有找到匹配菜单'}, json_dumps_params={"ensure_ascii": False})
-        except Exception as e:
-            print(e)
-            return JsonResponse({'code': 0, 'type': 'error', 'message': 'server error'}, json_dumps_params={"ensure_ascii": False})
-
-
-class FileUpload(View):
-    def post(self, request):
-        # data = request.body
-        print('=====================sendPage==================')
-        img = request.FILES.get("file", None)
-        print(img)
-        print(img.name)   # 获取文件名称
-        print(img.chunks)  # 获取文件内容
-        # 创建文件
-        print('=====================sendPage==================')
-
-        # 报存到数据库
-        # FileUpload.objects.create(name=img.name)
-        return HttpResponse("ok")

+ 0 - 22
server/manage.py

@@ -1,22 +0,0 @@
-#!/usr/bin/env python
-"""Django's command-line utility for administrative tasks."""
-import os
-import sys
-
-
-def main():
-    """Run administrative tasks."""
-    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'server.settings')
-    try:
-        from django.core.management import execute_from_command_line
-    except ImportError as exc:
-        raise ImportError(
-            "Couldn't import Django. Are you sure it's installed and "
-            "available on your PYTHONPATH environment variable? Did you "
-            "forget to activate a virtual environment?"
-        ) from exc
-    execute_from_command_line(sys.argv)
-
-
-if __name__ == '__main__':
-    main()

+ 0 - 2
server/server/__init__.py

@@ -1,2 +0,0 @@
-import pymysql
-pymysql.install_as_MySQLdb()

BIN
server/server/__pycache__/__init__.cpython-38.pyc


BIN
server/server/__pycache__/settings.cpython-38.pyc


BIN
server/server/__pycache__/urls.cpython-38.pyc


BIN
server/server/__pycache__/wsgi.cpython-38.pyc


+ 0 - 16
server/server/asgi.py

@@ -1,16 +0,0 @@
-"""
-ASGI config for server project.
-
-It exposes the ASGI callable as a module-level variable named ``application``.
-
-For more information on this file, see
-https://docs.djangoproject.com/en/3.1/howto/deployment/asgi/
-"""
-
-import os
-
-from django.core.asgi import get_asgi_application
-
-os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'server.settings')
-
-application = get_asgi_application()

+ 0 - 137
server/server/settings.py

@@ -1,137 +0,0 @@
-"""
-Django settings for server project.
-
-Generated by 'django-admin startproject' using Django 3.1.7.
-
-For more information on this file, see
-https://docs.djangoproject.com/en/3.1/topics/settings/
-
-For the full list of settings and their values, see
-https://docs.djangoproject.com/en/3.1/ref/settings/
-"""
-
-from pathlib import Path
-
-# Build paths inside the project like this: BASE_DIR / 'subdir'.
-BASE_DIR = Path(__file__).resolve().parent.parent
-
-
-# Quick-start development settings - unsuitable for production
-# See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/
-
-# SECURITY WARNING: keep the secret key used in production secret!
-SECRET_KEY = 'b06y0-4h3ao9mdkdfn0r^1@!9r6)&8-q*!f8q_^1hvrw=ri95_'
-
-# SECURITY WARNING: don't run with debug turned on in production!
-DEBUG = True
-
-ALLOWED_HOSTS = []
-
-
-# Application definition
-
-INSTALLED_APPS = [
-    'django.contrib.admin',
-    'django.contrib.auth',
-    'django.contrib.contenttypes',
-    'django.contrib.sessions',
-    'django.contrib.messages',
-    'django.contrib.staticfiles',
-    'api',
-    'corsheaders',
-    'rest_framework'
-]
-
-MIDDLEWARE = [
-    'django.middleware.security.SecurityMiddleware',
-    'django.contrib.sessions.middleware.SessionMiddleware',
-    'corsheaders.middleware.CorsMiddleware',  # 添加跨域中间件,需注意顺序
-    'django.middleware.common.CommonMiddleware',
-    # 'django.middleware.csrf.CsrfViewMiddleware',
-    'django.contrib.auth.middleware.AuthenticationMiddleware',
-    'django.contrib.messages.middleware.MessageMiddleware',
-    'django.middleware.clickjacking.XFrameOptionsMiddleware',
-]
-CORS_ALLOW_CREDENTIALS = True
-CORS_ORIGIN_ALLOW_ALL = True
-# 指定白名单
-# CORS_ORIGIN_WHITELIST = (
-#     'http://google.com',
-#     'http://hostname.example.com',
-#     'http://localhost:8000',
-#     'http://127.0.0.1:9000'
-# )
-
-ROOT_URLCONF = 'server.urls'
-
-TEMPLATES = [
-    {
-        'BACKEND': 'django.template.backends.django.DjangoTemplates',
-        'DIRS': [],
-        'APP_DIRS': True,
-        'OPTIONS': {
-            'context_processors': [
-                'django.template.context_processors.debug',
-                'django.template.context_processors.request',
-                'django.contrib.auth.context_processors.auth',
-                'django.contrib.messages.context_processors.messages',
-            ],
-        },
-    },
-]
-
-WSGI_APPLICATION = 'server.wsgi.application'
-
-
-# Database
-# https://docs.djangoproject.com/en/3.1/ref/settings/#databases
-
-DATABASES = {
-    'default': {
-        'ENGINE': 'django.db.backends.mysql',
-        'NAME': 'ant_backend',  # 数据库名
-        'HOST': '127.0.0.1',
-        'USER': 'root',
-        'PASSWORD': 'password',
-        'PORT': 3306
-    }
-}
-
-
-# Password validation
-# https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators
-
-AUTH_PASSWORD_VALIDATORS = [
-    {
-        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
-    },
-    {
-        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
-    },
-    {
-        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
-    },
-    {
-        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
-    },
-]
-
-
-# Internationalization
-# https://docs.djangoproject.com/en/3.1/topics/i18n/
-
-LANGUAGE_CODE = 'zh-hans'
-
-TIME_ZONE = 'Asia/Shanghai'
-
-USE_I18N = True
-
-USE_L10N = True
-
-USE_TZ = False
-
-
-# Static files (CSS, JavaScript, Images)
-# https://docs.djangoproject.com/en/3.1/howto/static-files/
-
-STATIC_URL = '/static/'

+ 0 - 20
server/server/urls.py

@@ -1,20 +0,0 @@
-"""server URL Configuration
-
-The `urlpatterns` list routes URLs to views. For more information please see:
-    https://docs.djangoproject.com/en/3.1/topics/http/urls/
-Examples:
-Function views
-    1. Add an import:  from my_app import views
-    2. Add a URL to urlpatterns:  path('', views.home, name='home')
-Class-based views
-    1. Add an import:  from other_app.views import Home
-    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
-Including another URLconf
-    1. Import the include() function: from django.urls import include, path
-    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
-"""
-from django.urls import path, include
-
-urlpatterns = [
-    path('admin/', include('api.urls')),
-]

+ 0 - 16
server/server/wsgi.py

@@ -1,16 +0,0 @@
-"""
-WSGI config for server project.
-
-It exposes the WSGI callable as a module-level variable named ``application``.
-
-For more information on this file, see
-https://docs.djangoproject.com/en/3.1/howto/deployment/wsgi/
-"""
-
-import os
-
-from django.core.wsgi import get_wsgi_application
-
-os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'server.settings')
-
-application = get_wsgi_application()

+ 2 - 2
src/api/sys/user.ts

@@ -12,8 +12,8 @@ import {
 import { ErrorMessageMode } from '/@/utils/http/axios/types';
 
 enum Api {
-  Login = '/token',
-  GetUserInfoById = '/user/info',
+  Login = '/login',
+  GetUserInfoById = '/info',
   GetUserList = '/getUserList',
   AddUser = '/addUser',
   EditUser = '/editUser',

+ 4 - 0
src/locales/lang/en/routes/permission.ts

@@ -1,5 +1,9 @@
 export default {
   management: 'permission',
+  admin: 'admin',
+  logs: 'admin log',
+  group: 'group',
+  rule: 'rule',
   role: 'role',
   menu: 'menu',
 };

+ 4 - 0
src/locales/lang/zh_CN/routes/permission.ts

@@ -1,5 +1,9 @@
 export default {
   management: '权限管理',
+  admin: '管理员管理',
+  logs: '管理员日志',
+  group: '角色组',
+  rule: '菜单规则',
   role: '角色管理',
   menu: '菜单管理',
 };

+ 16 - 0
src/router/menus/modules/permission.ts

@@ -8,6 +8,22 @@ const menu: MenuModule = {
     name: t('routes.permission.management'),
     children: [
       {
+        path: 'admin',
+        name: t('routes.permission.admin'),
+      },
+      {
+        path: 'logs',
+        name: t('routes.permission.logs'),
+      },
+      {
+        path: 'group',
+        name: t('routes.permission.group'),
+      },
+      {
+        path: 'rule',
+        name: t('routes.permission.rule'),
+      },
+      {
         path: 'role',
         name: t('routes.permission.role'),
       },

+ 41 - 1
src/router/routes/modules/permission.ts

@@ -7,13 +7,53 @@ const dashboard: AppRouteModule = {
   path: '/permission',
   name: 'Permission',
   component: LAYOUT,
-  redirect: '/permission/detail',
+  redirect: '/permission/admin',
   meta: {
     icon: 'bx:bx-lock',
     title: t('routes.permission.management'),
   },
   children: [
     {
+      path: 'admin',
+      name: 'Admin',
+      component: () => import('/@/views/permission/admin/index.vue'),
+      meta: {
+        title: t('routes.permission.admin'),
+        affix: false,
+        icon: 'bx:bx-lock',
+      },
+    },
+    {
+      path: 'logs',
+      name: 'Logs',
+      component: () => import('/@/views/permission/admin_log/index.vue'),
+      meta: {
+        title: t('routes.permission.logs'),
+        affix: false,
+        icon: 'bx:bx-lock',
+      },
+    },
+    {
+      path: 'group',
+      name: 'Group',
+      component: () => import('/@/views/permission/group/index.vue'),
+      meta: {
+        title: t('routes.permission.group'),
+        affix: false,
+        icon: 'bx:bx-lock',
+      },
+    },
+    {
+      path: 'rule',
+      name: 'Rule',
+      component: () => import('/@/views/permission/rule/index.vue'),
+      meta: {
+        title: t('routes.permission.rule'),
+        affix: false,
+        icon: 'bx:bx-lock',
+      },
+    },
+    {
       path: 'role',
       name: 'Role',
       component: () => import('/@/views/permission/role/index.vue'),

+ 1 - 1
src/router/routes/modules/table.ts

@@ -7,7 +7,7 @@ const table: AppRouteModule = {
   path: '/table',
   name: 'Table',
   component: LAYOUT,
-  redirect: '/table/index',
+  redirect: '/table/table',
   meta: {
     icon: 'bx:bx-table',
     title: t('routes.table.table'),

+ 1 - 1
src/router/routes/modules/test.ts

@@ -6,7 +6,7 @@ const test: AppRouteModule = {
   path: '/test',
   name: 'TestDemo',
   component: LAYOUT,
-  redirect: '/table/basic',
+  redirect: '/test/basic',
   meta: {
     icon: 'ant-design:table-outlined',
     title: t('routes.table.test'),

+ 192 - 0
src/views/permission/admin/add.vue

@@ -0,0 +1,192 @@
+<template>
+  <BasicModal v-bind="$attrs" @register="register" @ok="confirm" :title="title">
+    <BasicForm @register="registerForm" :model="model" />
+    <!-- <MenuTree /> -->
+  </BasicModal>
+</template>
+<script lang="ts">
+
+  import { Tree } from 'ant-design-vue';
+  import { defineComponent, PropType, reactive, ref, toRefs, watch } from 'vue';
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import { BasicForm, FormSchema, useForm } from '/@/components/Form/index';
+  import { adapt } from '/@/utils/adapt'
+
+  interface ModelData {
+    title: string,
+    treeData: object[],
+    checkedKeys: string[],
+    selectedKeys: string[],
+    expandedKeys: string[]
+  }
+  interface Role {
+    id: string|number
+  }
+
+  export default defineComponent({
+    components: { BasicModal, BasicForm, [Tree.name]: Tree },
+    props: {
+      modelData: {
+        type: Object as PropType<ModelData>,
+        default: {},
+      },
+    },
+    setup(props, { emit }) {
+      const role = reactive<Role>({
+        id:0
+      })
+      const modelData:ModelData = props.modelData
+      console.log(modelData)
+      const modelRef = ref({});
+      const adaptWidth = adapt()
+      watch(modelData, () => {
+        console.log('expandedKeys', modelData.expandedKeys);
+        console.log('selectedKeys', modelData.selectedKeys);
+        console.log('checkedKeys', modelData.checkedKeys);
+      });
+
+      const schemas: FormSchema[] = [
+        {
+          field: 'username',
+          component: 'Input',
+          label: '用户名',
+          labelWidth: adaptWidth.labelWidth,
+          colProps: {
+            span: adaptWidth.elContainer,
+          }
+        },
+        {
+          field: 'nickname',
+          component: 'Input',
+          label: '昵称',
+          labelWidth: adaptWidth.labelWidth,
+          colProps: {
+            span: adaptWidth.elContainer,
+          }
+        },
+        {
+          field: 'email',
+          component: 'Input',
+          label: 'Email',
+          labelWidth: adaptWidth.labelWidth,
+          colProps: {
+            span: adaptWidth.elContainer,
+          },
+        },
+        {
+          field: 'password',
+          component: 'Input',
+          label: '密码',
+          labelWidth: adaptWidth.labelWidth,
+          colProps: {
+            span: adaptWidth.elContainer,
+          },
+        },
+        {
+          field: 'status',
+          label: '状态',
+          component: 'RadioButtonGroup',
+          componentProps: {
+            options: [
+              { label: '启用', value: true },
+              { label: '停用', value: false },
+            ],
+          },
+          labelWidth: adaptWidth.labelWidth,
+          colProps: {
+            span: adaptWidth.elContainer,
+          },
+        },
+      ];
+
+      const [
+        registerForm,
+        {
+          getFieldsValue,
+          // setProps
+        },
+      ] = useForm({
+        labelWidth: 120,
+        schemas,
+        showActionButtonGroup: false,
+        actionColOptions: {
+          span: 24,
+        },
+      });
+      const [register, { closeModal } ] = useModalInner((data) => {
+        // 方式1
+        // setFieldsValue({
+        //   field2: data.data,
+        //   field1: data.info,
+        // });
+
+        // 方式2
+        modelRef.value = {
+          username: data.username,
+          nickname: data.nickname,
+          status: data.status,
+          menus: data.menus
+          };
+
+        // setProps({
+        //   model:{ field2: data.data, field1: data.info }
+        // })
+
+        role.id = data.id
+      });
+
+      function confirm() {
+        console.log('确定')
+        const data = getFieldsValue()
+        if(role.id) {
+          data.id = role.id
+        }
+        // const data = {
+        //   info,
+          data.menus = [...modelData.checkedKeys]
+        // }
+        // console.log(data)
+        // console.log(getFieldsValue())  // 表单数据
+        // console.log('------ 菜单key ------')
+        // console.log(modelData.checkedKeys)
+        emit('saveData', data)
+        closeModal()  // 关闭弹窗
+      }
+      return {
+        register,
+        schemas,
+        registerForm,
+        model: modelRef,
+        confirm,
+        adaptWidth,
+        ...toRefs(modelData)
+      };
+    },
+  });
+</script>
+<style lang='less'>
+.ant-form-item-label {
+  text-align: center !important;
+}
+
+.tree-label {
+  width: 20.6%;
+  margin-top: 8px;
+  margin-bottom: 1em;
+  text-align: center;
+}
+
+@media (max-width: 639px) {
+  .ant-form-item-label {
+  line-height: 2.5715 !important;
+  text-align: center !important;
+ }
+
+  .tree-label {
+  width: 33%;
+  margin-top: 8px;
+  margin-bottom: 1em;
+  text-align: center;
+}
+}
+</style>

+ 69 - 0
src/views/permission/admin/data.ts

@@ -0,0 +1,69 @@
+import { FormSchema } from '/@/components/Table';
+
+// const isDir = (type: string) => type === '0';
+// const isMenu = (type: string) => type === '1';
+// const isButton = (type: string) => type === '2';
+
+export const formSchema: FormSchema[] = [
+  {
+    field: 'parent',
+    label: '上级菜单',
+    component: 'TreeSelect',
+    componentProps: {
+      replaceFields: {
+        title: 'menuName',
+        key: 'id',
+        value: 'id',
+      },
+      getPopupContainer: () => document.body,
+    },
+  },
+  {
+    field: 'rule',
+    label: '规则',
+    component: 'Input',
+    required: true,
+  },
+  {
+    field: 'menuName',
+    label: '标题',
+    component: 'Input',
+    required: true,
+  },
+
+  {
+    field: 'icon',
+    label: '图标',
+    component: 'IconPicker',
+    required: true,
+  },
+  {
+    field: 'order',
+    label: '权重',
+    component: 'InputNumber',
+    required: true,
+  },
+  {
+    field: 'condition',
+    label: '规则条件',
+    component: 'Input',
+  },
+  {
+    field: 'remark',
+    label: '备注',
+    component: 'Input',
+  },
+
+  {
+    field: 'status',
+    label: '状态',
+    component: 'RadioButtonGroup',
+    defaultValue: true,
+    componentProps: {
+      options: [
+        { label: '启用', value: true },
+        { label: '禁用', value: false },
+      ],
+    },
+  },
+];

+ 265 - 0
src/views/permission/admin/index.vue

@@ -0,0 +1,265 @@
+<template>
+  <div class="p-4">
+    <BasicTable
+      ref="tableRef"
+      @register="registerTable"
+      rowKey="id"
+      :pagination="{ pageSize: 2, defaultPageSize: 2, showSizeChanger: false }"
+    >
+      <template #toolbar>
+        <a-button type="primary" @click="addRole" >
+            添加
+        </a-button>
+      </template>
+      <template #action="{ record, column }">
+        <TableAction :actions="createActions(record, column)" />
+      </template>
+      <!-- <template #form-custom > custom-slot </template> -->
+    </BasicTable>
+    <Add @register="addRegister" :modelData = "modelData" @saveData="saveData" />
+  </div>
+</template>
+<script lang="ts">
+  import { defineComponent, reactive, ref, unref } from 'vue';
+  import Add from './add.vue'
+  import { useModal } from '/@/components/Modal';
+  import { getAllMenuList } from '/@/api/sys/menu';
+  import { h } from 'vue';
+  import { Tag } from 'ant-design-vue';
+  import { getUserList, addUser, editUser,deleteUser } from '/@/api/sys/user';
+  import {BasicTable, useTable, TableAction, BasicColumn, ActionItem, EditRecordRow, TableActionType } from '/@/components/Table';
+
+//   interface ModelData {
+//     title: string,
+//     treeData: object[],
+//     checkedKeys: string[] | number[],
+//     selectedKeys: string[] | number[],
+//     expandedKeys: string[] | number[]
+//   }
+
+  const columns: BasicColumn[] = [
+    {
+      title: 'ID',
+      dataIndex: 'id',
+      editComponentProps: {
+        prefix: '$',
+      },
+      width: 80,
+    },
+    {
+      title: '用户名',
+      dataIndex: 'username',
+      width: 130,
+    },
+    {
+      title: '昵称',
+      dataIndex: 'nickname',
+      width: 150,
+    },
+    {
+      title: 'Email',
+      dataIndex: 'email',
+      width: 170,
+    },
+    // {
+    //   title: '所属组别',
+    //   dataIndex: 'password',
+    //   width: 160,
+    // },
+    {
+      title: '状态',
+      dataIndex: 'status',
+      width: 80,
+      customRender: ({ record }) => {
+        const status = record.status;
+        // const enable = ~~status === 0;
+        const color = status ? 'green' : 'red';
+        const text = status ? '启用' : '停用';
+        return h(Tag, { color: color }, () => text);
+      },
+    },
+    {
+      title: '最后登录',
+      dataIndex: 'detail',
+      width: 150,
+    }
+  ];
+
+  export default defineComponent({
+    components: { BasicTable, TableAction, Add },
+    setup() {
+      const tableRef = ref<Nullable<TableActionType>>(null);
+      const currentEditKeyRef = ref('');
+      const modelData = reactive({
+        title: '添加',
+        treeData: [],
+        checkedKeys: [],
+        selectedKeys: [],
+        expandedKeys: []
+      })
+      getTreeData()
+      const [registerTable] = useTable({
+        title: "角色组",
+        titleHelpMessage: "角色组可以有多个,角色有上下级层级关系,如果子角色有角色组和管理员的权限则可以派生属于自己组别的下级角色组或管理员",
+        rowSelection: { type: 'checkbox' },
+        columns: columns,
+        clickToRowSelect: false, // 点击行不勾选
+        api: getUserList,
+        actionColumn: {
+          width: 160,
+          title: '操作',
+          dataIndex: 'action',
+          slots: { customRender: 'action' },
+          fixed: undefined,
+        },
+        showIndexColumn: false,
+        bordered: true,
+      });
+      const [addRegister, { openModal: openAdd }] = useModal();
+
+      function getTableAction() { // 获取组件
+        const tableAction = unref(tableRef);
+        if (!tableAction) {
+          throw new Error('tableAction is null');
+        }
+        return tableAction;
+      }
+
+       function getSelectRowList() { // 获取选中行
+        console.log(getTableAction().getSelectRows());
+      }
+      function getSelectRowKeyList() { // 获取选中行的key --- id
+        console.log(getTableAction().getSelectRowKeys());
+      }
+
+      function addRole() {
+        modelData.title = '添加'
+        modelData.checkedKeys = []
+        modelData.selectedKeys = []
+        modelData.expandedKeys = []
+        openAdd(true, {
+          username: '',
+          detail: '',
+          menus: [],
+          roleName: '',
+          nickname: '',
+          status: true
+        });
+      }
+
+      function handleEdit(record: EditRecordRow) {
+        currentEditKeyRef.value = record.id;  // record.key
+        console.log(record)
+        modelData.title = '编辑'
+
+        const data = getTableAction().getDataSource()
+        data.map(item => {
+          if (item.id === record.id) {
+            record = item
+          }
+        })
+        modelData.checkedKeys = record.menus
+        modelData.selectedKeys = []
+        modelData.expandedKeys = []
+        openAdd(true, record);
+      }
+
+      async function handleDelete(record: Recordable) {
+        console.log('点击了删除', record.id);
+        console.log(record)
+        await deleteUser({id:record.id}).then(res => {
+          console.log(res)
+          getTableAction().reload()
+        })
+        // const data = getTableAction().getDataSource()
+        // console.log(data)
+        // getTableAction().setTableData(data.filter(item => item.id !== record.id))
+      }
+
+      async function saveData(data: any) {
+        // const datas = getTableAction().getDataSource()
+        // const info = reactive({...data.info})
+        console.log('------------save---------')
+        console.log(data)
+        if(!data.id) {
+          await addUser(data).then(res => {
+            console.log(res)
+            getTableAction().reload()
+          })
+          console.log('----------add---')
+        }else {
+          await editUser(data).then(res => {
+            console.log(res)
+            getTableAction().reload()
+          })
+          console.log('----------edit---')
+
+        }
+        // if (modelData.title === "添加") {
+        //   datas.map(item => {
+        //     if (info.id === 0 || info.id === item.id) {
+        //       info.id = item.id + 1
+        //     }
+        //   })
+        //   datas.push(info)
+        //   getTableAction().setTableData(datas)
+        // } else {
+        //   console.log('编辑')
+        //   // getTableAction().setTableData(datas.filter(item => item.id !== info.id))
+        //   let dataArr:object[] = []
+        //   datas.map(item => {
+        //     if (item.id === info.id) {
+        //       item = info
+        //     }
+        //     dataArr.push(item)
+        //   })
+        //   getTableAction().setTableData(dataArr)
+        // }
+      }
+      async function getTreeData() {
+        let treeData = await getAllMenuList() as any
+        modelData.treeData = JSON.parse(JSON.stringify(treeData).replace(/menuName/g,"title").replace(/id/g,"key"))
+
+        console.log(modelData.treeData)
+      }
+
+      function createActions(record: EditRecordRow, column: BasicColumn): ActionItem[] {
+        if (false) {
+          console.log(column)
+        }
+        return [
+          {
+            label: '编辑',
+            icon: 'ant-design:edit-outlined',
+            color: 'warning',
+            onClick: handleEdit.bind(null, record),
+          },
+          {
+            label: '删除',
+            color: 'error',
+            icon: 'ic:outline-delete-outline',
+            popConfirm: {
+              title: '是否确认删除',
+              confirm: handleDelete.bind(null, record),
+            },
+          },
+        ];
+      }
+      return {
+        modelData,
+        tableRef,
+        registerTable,
+        addRole,
+        handleEdit,
+        createActions,
+        getTableAction,
+        getSelectRowList,
+        getSelectRowKeyList,
+        addRegister,
+        saveData,
+        getUserList,
+        getTreeData
+      };
+    },
+  });
+</script>

+ 0 - 0
server/api/__init__.py → src/views/permission/admin_log/data.ts


+ 181 - 0
src/views/permission/admin_log/index.vue

@@ -0,0 +1,181 @@
+<template>
+  <div class="p-4">
+    <BasicTable
+      ref="tableRef"
+      @register="registerTable"
+      rowKey="id"
+      :pagination="{ pageSize: 2, defaultPageSize: 2, showSizeChanger: false }"
+    >
+      <template #toolbar>
+        <a-button type="danger" >
+            删除
+        </a-button>
+      </template>
+      <template #action="{ record, column }">
+        <TableAction :actions="createActions(record, column)" />
+      </template>
+    </BasicTable>
+    <Model @register="addRegister" :modelData="modelData" />
+  </div>
+</template>
+<script lang="ts">
+  import { defineComponent, reactive, ref, unref } from 'vue';
+  import Model from './model.vue'
+  import { useModal } from '/@/components/Modal';
+  import { getUserList, deleteUser } from '/@/api/sys/user';
+  import {BasicTable, useTable, TableAction, BasicColumn, ActionItem, EditRecordRow, TableActionType } from '/@/components/Table';
+
+//   interface ModelData {
+//     title: string,
+//     treeData: object[],
+//     checkedKeys: string[] | number[],
+//     selectedKeys: string[] | number[],
+//     expandedKeys: string[] | number[]
+//   }
+
+  const columns: BasicColumn[] = [
+    {
+      title: 'ID',
+      dataIndex: 'id',
+      editComponentProps: {
+        prefix: '$',
+      },
+      width: 80,
+    },
+    {
+      title: '用户名',
+      dataIndex: 'username',
+      width: 130,
+    },
+    {
+      title: '标题',
+      dataIndex: 'title',
+      width: 150,
+    },
+    {
+      title: 'Url',
+      dataIndex: 'url',
+      width: 150,
+    },
+    {
+      title: 'IP',
+      dataIndex: 'IP',
+      width: 150,
+    },
+    {
+      title: 'Browser',
+      dataIndex: 'Browser',
+      width: 170,
+    },
+    {
+      title: '创建时间',
+      dataIndex: 'time',
+      width: 150,
+    }
+  ];
+
+  export default defineComponent({
+    components: { BasicTable, TableAction, Model },
+    setup() {
+      const tableRef = ref<Nullable<TableActionType>>(null);
+      const currentEditKeyRef = ref('');
+      const modelData = reactive({
+        title: '详情',
+      })
+      const [registerTable] = useTable({
+        title: "管理员日志",
+        titleHelpMessage: "管理员可以查看自己所拥有的权限的管理员日志",
+        rowSelection: { type: 'checkbox' },
+        columns: columns,
+        clickToRowSelect: false, // 点击行不勾选
+        api: getUserList,
+        actionColumn: {
+          width: 160,
+          title: '操作',
+          dataIndex: 'action',
+          slots: { customRender: 'action' },
+          fixed: undefined,
+        },
+        showIndexColumn: false,
+        bordered: true,
+      });
+      const [addRegister, { openModal: openAdd }] = useModal();
+
+      function getTableAction() { // 获取组件
+        const tableAction = unref(tableRef);
+        if (!tableAction) {
+          throw new Error('tableAction is null');
+        }
+        return tableAction;
+      }
+
+       function getSelectRowList() { // 获取选中行
+        console.log(getTableAction().getSelectRows());
+      }
+      function getSelectRowKeyList() { // 获取选中行的key --- id
+        console.log(getTableAction().getSelectRowKeys());
+      }
+
+
+
+      function handleDetail(record: EditRecordRow) {
+        currentEditKeyRef.value = record.id;  // record.key
+
+        const data = getTableAction().getDataSource()
+        data.map(item => {
+          if (item.id === record.id) {
+            record = item
+          }
+        })
+        openAdd(true, record);
+      }
+
+      async function handleDelete(record: Recordable) {
+        console.log('点击了删除', record.id);
+        console.log(record)
+        await deleteUser({id:record.id}).then(res => {
+          console.log(res)
+          getTableAction().reload()
+        })
+        // const data = getTableAction().getDataSource()
+        // console.log(data)
+        // getTableAction().setTableData(data.filter(item => item.id !== record.id))
+      }
+
+      function createActions(record: EditRecordRow, column: BasicColumn): ActionItem[] {
+        if (false) {
+          console.log(column)
+        }
+        return [
+          {
+            label: '详情',
+            icon: 'akar-icons:eye-open',
+            color: 'success',
+            onClick: handleDetail.bind(null, record),
+          },
+          {
+            label: '删除',
+            color: 'error',
+            icon: 'ic:outline-delete-outline',
+            popConfirm: {
+              title: '是否确认删除',
+              confirm: handleDelete.bind(null, record),
+            },
+          },
+        ];
+      }
+      return {
+        modelData,
+        tableRef,
+        registerTable,
+        handleDetail,
+        createActions,
+        getTableAction,
+        getSelectRowList,
+        getSelectRowKeyList,
+        addRegister,
+        getUserList,
+      };
+    },
+  });
+</script>

+ 110 - 0
src/views/permission/admin_log/model.vue

@@ -0,0 +1,110 @@
+<template>
+  <BasicModal v-bind="$attrs" @register="register" :showOkBtn='false' cancelText='关闭' :title="title">
+    <Description
+    :collapseOptions="{ canExpand: false, helpMessage: '日志详情' }"
+    :column="1"
+    :data="model"
+    :schema="schema"
+    />
+  </BasicModal>
+</template>
+<script lang="ts">
+
+  import { defineComponent, PropType, ref, toRefs } from 'vue';
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import { Description, DescItem } from '/@/components/Description/index';
+  import { adapt } from '/@/utils/adapt'
+
+  interface ModelData {
+    title: string,
+  }
+
+  export default defineComponent({
+    components: { BasicModal, Description },
+    props: {
+      modelData: {
+        type: Object as PropType<ModelData>,
+        default: {},
+      },
+    },
+    setup(props) {
+      const modelData:ModelData = props.modelData
+
+      const modelRef = ref({});
+      const adaptWidth = adapt()
+      const schema: DescItem[] = [
+        {
+            field: 'id',
+            label: 'id',
+        },
+        {
+            field: 'admin_id',
+            label: 'admin_id',
+        },
+        {
+            field: 'username',
+            label: '用户名',
+        },
+        {
+            field: 'title',
+            label: '标题',
+        },
+        {
+            field: 'url',
+            label: 'Url',
+        },
+        {
+            field: 'IP',
+            label: 'IP',
+        },
+        {
+            field: 'Browser',
+            label: 'Browser',
+        },
+        {
+            field: 'time',
+            label: '创建时间',
+        },
+      ];
+      const [register ] = useModalInner((data) => {
+
+        // 方式2
+        modelRef.value = data
+      });
+
+      return {
+        register,
+        adaptWidth,
+        schema,
+        model: modelRef,
+        ...toRefs(modelData)
+      };
+    },
+  });
+</script>
+<style lang='less'>
+.ant-form-item-label {
+  text-align: center !important;
+}
+
+.tree-label {
+  width: 20.6%;
+  margin-top: 8px;
+  margin-bottom: 1em;
+  text-align: center;
+}
+
+@media (max-width: 639px) {
+  .ant-form-item-label {
+  line-height: 2.5715 !important;
+  text-align: center !important;
+ }
+
+  .tree-label {
+  width: 33%;
+  margin-top: 8px;
+  margin-bottom: 1em;
+  text-align: center;
+}
+}
+</style>

+ 196 - 0
src/views/permission/group/add.vue

@@ -0,0 +1,196 @@
+<template>
+  <BasicModal v-bind="$attrs" @register="register" @ok="confirm" :title="title">
+    <BasicForm @register="registerForm" :model="model" />
+    <!-- <MenuTree /> -->
+    <div style="display: flex;">
+      <p class="tree-label">权限</p>
+      <a-tree
+      checkable
+      :tree-data="treeData"
+      v-model:expandedKeys="expandedKeys"
+      v-model:selectedKeys="selectedKeys"
+      v-model:checkedKeys="checkedKeys"
+      >
+        <!-- <template #title0010><span style="color: #1890ff;">sss</span></template> -->
+      </a-tree>
+    </div>
+  </BasicModal>
+</template>
+<script lang="ts">
+
+  import { Tree } from 'ant-design-vue';
+  import { defineComponent, PropType, reactive, ref, toRefs, watch } from 'vue';
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import { BasicForm, FormSchema, useForm } from '/@/components/Form/index';
+  import { adapt } from '/@/utils/adapt'
+
+  interface ModelData {
+    title: string,
+    treeData: object[],
+    checkedKeys: string[],
+    selectedKeys: string[],
+    expandedKeys: string[]
+  }
+  interface Role {
+    id: string|number
+  }
+
+  export default defineComponent({
+    components: { BasicModal, BasicForm, [Tree.name]: Tree },
+    props: {
+      modelData: {
+        type: Object as PropType<ModelData>,
+        default: {},
+      },
+    },
+    setup(props, { emit }) {
+      const role = reactive<Role>({
+        id:0
+      })
+      const modelData:ModelData = props.modelData
+      console.log(modelData)
+      const modelRef = ref({});
+      const adaptWidth = adapt()
+      watch(modelData, () => {
+        console.log('expandedKeys', modelData.expandedKeys);
+        console.log('selectedKeys', modelData.selectedKeys);
+        console.log('checkedKeys', modelData.checkedKeys);
+      });
+
+      const schemas: FormSchema[] = [
+        {
+            field: 'parent',
+            label: '父级',
+            component: 'TreeSelect',
+            componentProps: {
+            replaceFields: {
+                title: 'menuName',
+                key: 'id',
+                value: 'id',
+            },
+            getPopupContainer: () => document.body,
+            },
+            labelWidth: adaptWidth.labelWidth,
+            colProps: {
+              span: adaptWidth.elContainer,
+          }
+        },
+        {
+          field: 'uname',
+          component: 'Input',
+          label: '名称',
+          labelWidth: adaptWidth.labelWidth,
+          colProps: {
+            span: adaptWidth.elContainer,
+          }
+        },
+        {
+          field: 'status',
+          label: '状态',
+          component: 'RadioButtonGroup',
+          componentProps: {
+            options: [
+              { label: '启用', value: true },
+              { label: '停用', value: false },
+            ],
+          },
+          labelWidth: adaptWidth.labelWidth,
+          colProps: {
+            span: adaptWidth.elContainer,
+          },
+        },
+      ];
+
+      const [
+        registerForm,
+        {
+          getFieldsValue,
+          // setProps
+        },
+      ] = useForm({
+        labelWidth: 120,
+        schemas,
+        showActionButtonGroup: false,
+        actionColOptions: {
+          span: 24,
+        },
+      });
+      const [register, { closeModal } ] = useModalInner((data) => {
+        // 方式1
+        // setFieldsValue({
+        //   field2: data.data,
+        //   field1: data.info,
+        // });
+
+        // 方式2
+        modelRef.value = {
+          username: data.username,
+          nickname: data.nickname,
+          password: data.password,
+          status: data.status,
+          detail: data.detail,
+          menus: data.menus
+          };
+
+        // setProps({
+        //   model:{ field2: data.data, field1: data.info }
+        // })
+
+        role.id = data.id
+      });
+
+      function confirm() {
+        console.log('确定')
+        const data = getFieldsValue()
+        if(role.id) {
+          data.id = role.id
+        }
+        // const data = {
+        //   info,
+          data.menus = [...modelData.checkedKeys]
+        // }
+        // console.log(data)
+        // console.log(getFieldsValue())  // 表单数据
+        // console.log('------ 菜单key ------')
+        // console.log(modelData.checkedKeys)
+        emit('saveData', data)
+        closeModal()  // 关闭弹窗
+      }
+      return {
+        register,
+        schemas,
+        registerForm,
+        model: modelRef,
+        confirm,
+        adaptWidth,
+        ...toRefs(modelData)
+      };
+    },
+  });
+</script>
+<style lang='less'>
+.ant-form-item-label {
+  text-align: center !important;
+}
+
+.tree-label {
+  width: 20.6%;
+  margin-top: 8px;
+  margin-bottom: 1em;
+  text-align: center;
+}
+
+@media (max-width: 639px) {
+  .ant-form-item-label {
+  line-height: 2.5715 !important;
+  text-align: center !important;
+ }
+
+  .tree-label {
+  width: 33%;
+  margin-top: 8px;
+  margin-bottom: 1em;
+  text-align: center;
+}
+}
+</style>

+ 0 - 0
src/views/permission/group/data.ts


+ 233 - 0
src/views/permission/group/index.vue

@@ -0,0 +1,233 @@
+<template>
+  <div class="p-4">
+    <BasicTable
+      ref="tableRef"
+      @register="registerTable"
+      rowKey="id"
+    >
+      <template #toolbar>
+        <a-button type="primary" @click="addRole" >
+          添加
+        </a-button>
+        <a-button type="danger" >
+          删除
+        </a-button>
+      </template>
+      <template #action="{ record, column }">
+        <TableAction :actions="createActions(record, column)" />
+      </template>
+      <!-- <template #form-custom > custom-slot </template> -->
+    </BasicTable>
+    <Add @register="addRegister" :modelData = "modelData" @saveData="saveData" />
+  </div>
+</template>
+<script lang="ts">
+  import { defineComponent, reactive, ref, unref } from 'vue';
+  import Add from './add.vue'
+  import { useModal } from '/@/components/Modal';
+  import { getAllMenuList } from '/@/api/sys/menu';
+  import { h } from 'vue';
+  import { Tag } from 'ant-design-vue';
+  import { getUserList, addUser, editUser,deleteUser } from '/@/api/sys/user';
+  import {BasicTable, useTable, TableAction, BasicColumn, ActionItem, EditRecordRow, TableActionType } from '/@/components/Table';
+
+//   interface ModelData {
+//     title: string,
+//     treeData: object[],
+//     checkedKeys: string[] | number[],
+//     selectedKeys: string[] | number[],
+//     expandedKeys: string[] | number[]
+//   }
+
+  const columns: BasicColumn[] = [
+    {
+      title: 'ID',
+      dataIndex: 'id',
+      editComponentProps: {
+        prefix: '$',
+      },
+      width: 80,
+    },
+    {
+      title: '父级',
+      dataIndex: 'parent',
+      // editRow: true,
+      width: 160,
+    },
+    {
+      title: '名称',
+      dataIndex: 'uname',
+      // editRow: true,
+      width: 160,
+    },
+    {
+      title: '状态',
+      dataIndex: 'status',
+      width: 80,
+      customRender: ({ record }) => {
+        const status = record.status;
+        // const enable = ~~status === 0;
+        const color = status ? 'green' : 'red';
+        const text = status ? '启用' : '停用';
+        return h(Tag, { color: color }, () => text);
+      },
+    }
+  ];
+
+  export default defineComponent({
+    components: { BasicTable, TableAction, Add },
+    setup() {
+      const tableRef = ref<Nullable<TableActionType>>(null);
+      const currentEditKeyRef = ref('');
+      const modelData = reactive({
+        title: '添加',
+        treeData: [],
+        checkedKeys: [],
+        selectedKeys: [],
+        expandedKeys: []
+      })
+      getTreeData()
+      const [registerTable] = useTable({
+        title: "角色列表",
+        titleHelpMessage: "角色组可以有多个,角色有上下级层级关系,如果子角色有角色组和管理员的权限则可以派生属于自己组别的下级角色组或管理员",
+        rowSelection: { type: 'checkbox' },
+        columns: columns,
+        clickToRowSelect: false, // 点击行不勾选
+        api: getUserList,
+        actionColumn: {
+          width: 160,
+          title: '操作',
+          dataIndex: 'action',
+          slots: { customRender: 'action' },
+          fixed: undefined,
+        },
+        showIndexColumn: false,
+        bordered: true,
+      });
+      const [addRegister, { openModal: openAdd }] = useModal();
+
+      function getTableAction() { // 获取组件
+        const tableAction = unref(tableRef);
+        if (!tableAction) {
+          throw new Error('tableAction is null');
+        }
+        return tableAction;
+      }
+
+       function getSelectRowList() { // 获取选中行
+        console.log(getTableAction().getSelectRows());
+      }
+      function getSelectRowKeyList() { // 获取选中行的key --- id
+        console.log(getTableAction().getSelectRowKeys());
+      }
+
+      function addRole() {
+        console.log('添加')
+        modelData.title = '添加'
+        modelData.checkedKeys = []
+        modelData.selectedKeys = []
+        modelData.expandedKeys = []
+        openAdd(true, {
+          username: '',
+          password: '',
+          detail: '',
+          menus: [],
+          roleName: '',
+          nickname: '',
+          status: true
+        });
+      }
+
+      function handleEdit(record: EditRecordRow) {
+        currentEditKeyRef.value = record.id;  // record.key
+        console.log(record)
+        modelData.title = '编辑'
+
+        const data = getTableAction().getDataSource()
+        data.map(item => {
+          if (item.id === record.id) {
+            record = item
+          }
+        })
+        modelData.checkedKeys = record.menus
+        modelData.selectedKeys = []
+        modelData.expandedKeys = []
+        openAdd(true, record);
+      }
+
+      async function handleDelete(record: Recordable) {
+        console.log('点击了删除', record.id);
+        console.log(record)
+        await deleteUser({id:record.id}).then(res => {
+          console.log(res)
+          getTableAction().reload()
+        })
+      }
+
+      async function saveData(data: any) {
+        // const datas = getTableAction().getDataSource()
+        // const info = reactive({...data.info})
+        console.log('------------save---------')
+        console.log(data)
+        if(!data.id) {
+          await addUser(data).then(res => {
+            console.log(res)
+            getTableAction().reload()
+          })
+          console.log('----------add---')
+        }else {
+          await editUser(data).then(res => {
+            console.log(res)
+            getTableAction().reload()
+          })
+          console.log('----------edit---')
+
+        }
+      }
+      async function getTreeData() {
+        let treeData = await getAllMenuList() as any
+        modelData.treeData = JSON.parse(JSON.stringify(treeData).replace(/menuName/g,"title").replace(/id/g,"key"))
+
+        console.log(modelData.treeData)
+      }
+
+      function createActions(record: EditRecordRow, column: BasicColumn): ActionItem[] {
+        if (false) {
+          console.log(column)
+        }
+        return [
+          {
+            label: '编辑',
+            icon: 'ant-design:edit-outlined',
+            color: 'warning',
+            onClick: handleEdit.bind(null, record),
+          },
+          {
+            label: '删除',
+            color: 'error',
+            icon: 'ic:outline-delete-outline',
+            popConfirm: {
+              title: '是否确认删除',
+              confirm: handleDelete.bind(null, record),
+            },
+          },
+        ];
+      }
+      return {
+        modelData,
+        tableRef,
+        registerTable,
+        addRole,
+        handleEdit,
+        createActions,
+        getTableAction,
+        getSelectRowList,
+        getSelectRowKeyList,
+        addRegister,
+        saveData,
+        getUserList,
+        getTreeData
+      };
+    },
+  });
+</script>

+ 9 - 9
src/views/permission/role/index.vue

@@ -15,7 +15,7 @@
       </template>
       <!-- <template #form-custom > custom-slot </template> -->
     </BasicTable>
-    <Add @register="addRegister" :modelData:ModelDate="modelData" @saveData="saveData" />
+    <Add @register="addRegister" :modelData = "modelData" @saveData="saveData" />
   </div>
 </template>
 <script lang="ts">
@@ -28,13 +28,13 @@
   import { getUserList, addUser, editUser,deleteUser } from '/@/api/sys/user';
   import {BasicTable, useTable, TableAction, BasicColumn, ActionItem, EditRecordRow, TableActionType } from '/@/components/Table';
 
-  interface ModelData {
-    title: string,
-    treeData: object[],
-    checkedKeys: string[] | number[],
-    selectedKeys: string[] | number[],
-    expandedKeys: string[] | number[]
-  }
+//   interface ModelData {
+//     title: string,
+//     treeData: object[],
+//     checkedKeys: string[] | number[],
+//     selectedKeys: string[] | number[],
+//     expandedKeys: string[] | number[]
+//   }
 
   const columns: BasicColumn[] = [
     {
@@ -88,7 +88,7 @@
     setup() {
       const tableRef = ref<Nullable<TableActionType>>(null);
       const currentEditKeyRef = ref('');
-      const modelData = reactive<ModelData>({
+      const modelData = reactive({
         title: '添加',
         treeData: [],
         checkedKeys: [],

+ 142 - 0
src/views/permission/rule/add.vue

@@ -0,0 +1,142 @@
+<template>
+  <BasicModal v-bind="$attrs" @register="register" @ok="confirm" :title="title">
+    <BasicForm @register="registerForm" :model="model" />
+    <!-- <MenuTree /> -->
+  </BasicModal>
+</template>
+<script lang="ts">
+
+  import { Tree } from 'ant-design-vue';
+  import { defineComponent, PropType, reactive, ref, toRefs, watch } from 'vue';
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import { BasicForm, FormSchema, useForm } from '/@/components/Form/index';
+  import { adapt } from '/@/utils/adapt'
+  import { formSchema,  } from './data'
+
+
+  interface ModelData {
+    title: string,
+    treeData: object[],
+    checkedKeys: string[],
+    selectedKeys: string[],
+    expandedKeys: string[]
+  }
+  interface Role {
+    id: string|number
+  }
+
+  export default defineComponent({
+    components: { BasicModal, BasicForm, [Tree.name]: Tree },
+    props: {
+      modelData: {
+        type: Object as PropType<ModelData>,
+        default: {},
+      },
+    },
+    setup(props, { emit }) {
+      const role = reactive<Role>({
+        id:0
+      })
+      const modelData:ModelData = props.modelData
+      console.log(modelData)
+      const modelRef = ref({});
+      const adaptWidth = adapt()
+      watch(modelData, () => {
+        console.log('expandedKeys', modelData.expandedKeys);
+        console.log('selectedKeys', modelData.selectedKeys);
+        console.log('checkedKeys', modelData.checkedKeys);
+      });
+
+      const schemas: FormSchema[] = formSchema
+
+      const [
+        registerForm,
+        {
+          getFieldsValue,
+          // setProps
+        },
+      ] = useForm({
+        labelWidth: 120,
+        schemas,
+        showActionButtonGroup: false,
+        actionColOptions: {
+          span: 24,
+        },
+      });
+      const [register, { closeModal } ] = useModalInner((data) => {
+        // 方式1
+        // setFieldsValue({
+        //   field2: data.data,
+        //   field1: data.info,
+        // });
+
+        // 方式2
+        modelRef.value = {
+          username: data.username,
+          nickname: data.nickname,
+          status: data.status,
+          menus: data.menus
+          };
+
+        // setProps({
+        //   model:{ field2: data.data, field1: data.info }
+        // })
+
+        role.id = data.id
+      });
+
+      function confirm() {
+        console.log('确定')
+        const data = getFieldsValue()
+        if(role.id) {
+          data.id = role.id
+        }
+        // const data = {
+        //   info,
+          data.menus = [...modelData.checkedKeys]
+        // }
+        // console.log(data)
+        // console.log(getFieldsValue())  // 表单数据
+        // console.log('------ 菜单key ------')
+        // console.log(modelData.checkedKeys)
+        emit('saveData', data)
+        closeModal()  // 关闭弹窗
+      }
+      return {
+        register,
+        schemas,
+        registerForm,
+        model: modelRef,
+        confirm,
+        adaptWidth,
+        ...toRefs(modelData)
+      };
+    },
+  });
+</script>
+<style lang='less'>
+.ant-form-item-label {
+  text-align: center !important;
+}
+
+.tree-label {
+  width: 20.6%;
+  margin-top: 8px;
+  margin-bottom: 1em;
+  text-align: center;
+}
+
+@media (max-width: 639px) {
+  .ant-form-item-label {
+  line-height: 2.5715 !important;
+  text-align: center !important;
+ }
+
+  .tree-label {
+  width: 33%;
+  margin-top: 8px;
+  margin-bottom: 1em;
+  text-align: center;
+}
+}
+</style>

+ 85 - 0
src/views/permission/rule/data.ts

@@ -0,0 +1,85 @@
+import { FormSchema } from '/@/components/Table';
+
+// const isDir = (type: string) => type === '0';
+// const isMenu = (type: string) => type === '1';
+// const isButton = (type: string) => type === '2';
+
+export const formSchema: FormSchema[] = [
+  // {
+  //   field: 'type',
+  //   label: '菜单类型',
+  //   component: 'RadioButtonGroup',
+  //   defaultValue: '0',
+  //   componentProps: {
+  //     options: [
+  //       { label: '目录', value: '0' },
+  //       { label: '菜单', value: '1' },
+  //       { label: '按钮', value: '2' },
+  //     ],
+  //   },
+  //   colProps: { lg: 24, md: 24 },
+  // },
+
+  {
+    field: 'parent',
+    label: '父级',
+    component: 'TreeSelect',
+    componentProps: {
+      replaceFields: {
+        title: 'menuName',
+        key: 'id',
+        value: 'id',
+      },
+      getPopupContainer: () => document.body,
+    },
+    required: true,
+  },
+  {
+    field: 'rule',
+    label: '规则',
+    component: 'Input',
+    required: true,
+  },
+  {
+    field: 'menuName',
+    label: '标题',
+    component: 'Input',
+    required: true,
+  },
+
+  {
+    field: 'icon',
+    label: '图标',
+    component: 'IconPicker',
+    required: true,
+  },
+  {
+    field: 'order',
+    label: '权重',
+    component: 'InputNumber',
+    required: true,
+  },
+  {
+    field: 'condition',
+    label: '规则条件',
+    component: 'Input',
+  },
+  {
+    field: 'remark',
+    label: '备注',
+    component: 'Input',
+  },
+
+  {
+    field: 'status',
+    label: '状态',
+    component: 'RadioButtonGroup',
+    defaultValue: true,
+    componentProps: {
+      options: [
+        { label: '启用', value: true },
+        { label: '禁用', value: false },
+      ],
+    },
+  },
+];

+ 259 - 0
src/views/permission/rule/index.vue

@@ -0,0 +1,259 @@
+<template>
+  <div class="p-4">
+    <BasicTable
+      ref="tableRef"
+      @register="registerTable"
+      rowKey="id"
+      :pagination="{ pageSize: 2, defaultPageSize: 2, showSizeChanger: false }"
+    >
+      <template #toolbar>
+        <a-button type="primary" @click="addRole" >
+          添加
+        </a-button>
+        <a-button type="danger" >
+          删除
+        </a-button>
+      </template>
+      <template #action="{ record, column }">
+        <TableAction :actions="createActions(record, column)" />
+      </template>
+      <!-- <template #form-custom > custom-slot </template> -->
+    </BasicTable>
+    <Add @register="addRegister" :modelData = "modelData" @saveData="saveData" />
+  </div>
+</template>
+<script lang="ts">
+  import { defineComponent, reactive, ref, unref } from 'vue';
+  import Add from './add.vue'
+  import { useModal } from '/@/components/Modal';
+  import { getAllMenuList } from '/@/api/sys/menu';
+  import { h } from 'vue';
+  import { Tag } from 'ant-design-vue';
+  import { getUserList, addUser, editUser,deleteUser } from '/@/api/sys/user';
+  import {BasicTable, useTable, TableAction, BasicColumn, ActionItem, EditRecordRow, TableActionType } from '/@/components/Table';
+
+//   interface ModelData {
+//     title: string,
+//     treeData: object[],
+//     checkedKeys: string[] | number[],
+//     selectedKeys: string[] | number[],
+//     expandedKeys: string[] | number[]
+//   }
+
+  const columns: BasicColumn[] = [
+    {
+      title: 'ID',
+      dataIndex: 'id',
+      editComponentProps: {
+        prefix: '$',
+      },
+      width: 80,
+    },
+    {
+      title: '标题',
+      dataIndex: 'title',
+      width: 150,
+    },
+    {
+      title: '规则',
+      dataIndex: 'uri',
+      width: 150,
+    },
+    {
+      title: '权重',
+      dataIndex: 'order',
+      width: 150,
+    },
+
+    {
+      title: '状态',
+      dataIndex: 'status',
+      width: 80,
+      customRender: ({ record }) => {
+        const status = record.status;
+        // const enable = ~~status === 0;
+        const color = status ? 'green' : 'red';
+        const text = status ? '启用' : '停用';
+        return h(Tag, { color: color }, () => text);
+      },
+    },
+  ];
+
+  export default defineComponent({
+    components: { BasicTable, TableAction, Add },
+    setup() {
+      const tableRef = ref<Nullable<TableActionType>>(null);
+      const currentEditKeyRef = ref('');
+      const modelData = reactive({
+        title: '添加',
+        treeData: [],
+        checkedKeys: [],
+        selectedKeys: [],
+        expandedKeys: []
+      })
+      getTreeData()
+      const [registerTable] = useTable({
+        title: "菜单规则",
+        titleHelpMessage: "规则通常对应一个控制器的方法,同时左侧的菜单栏数据也从规则中体现,通常建议通过命令行进行生成规则节点",
+        rowSelection: { type: 'checkbox' },
+        columns: columns,
+        clickToRowSelect: false, // 点击行不勾选
+        api: getUserList,
+        actionColumn: {
+          width: 160,
+          title: '操作',
+          dataIndex: 'action',
+          slots: { customRender: 'action' },
+          fixed: undefined,
+        },
+        showIndexColumn: false,
+        bordered: true,
+      });
+      const [addRegister, { openModal: openAdd }] = useModal();
+
+      function getTableAction() { // 获取组件
+        const tableAction = unref(tableRef);
+        if (!tableAction) {
+          throw new Error('tableAction is null');
+        }
+        return tableAction;
+      }
+
+       function getSelectRowList() { // 获取选中行
+        console.log(getTableAction().getSelectRows());
+      }
+      function getSelectRowKeyList() { // 获取选中行的key --- id
+        console.log(getTableAction().getSelectRowKeys());
+      }
+
+      function addRole() {
+        modelData.title = '添加'
+        modelData.checkedKeys = []
+        modelData.selectedKeys = []
+        modelData.expandedKeys = []
+        openAdd(true, {
+          username: '',
+          detail: '',
+          menus: [],
+          roleName: '',
+          nickname: '',
+          status: true
+        });
+      }
+
+      function handleEdit(record: EditRecordRow) {
+        currentEditKeyRef.value = record.id;  // record.key
+        console.log(record)
+        modelData.title = '编辑'
+
+        const data = getTableAction().getDataSource()
+        data.map(item => {
+          if (item.id === record.id) {
+            record = item
+          }
+        })
+        modelData.checkedKeys = record.menus
+        modelData.selectedKeys = []
+        modelData.expandedKeys = []
+        openAdd(true, record);
+      }
+
+      async function handleDelete(record: Recordable) {
+        console.log('点击了删除', record.id);
+        console.log(record)
+        await deleteUser({id:record.id}).then(res => {
+          console.log(res)
+          getTableAction().reload()
+        })
+        // const data = getTableAction().getDataSource()
+        // console.log(data)
+        // getTableAction().setTableData(data.filter(item => item.id !== record.id))
+      }
+
+      async function saveData(data: any) {
+        // const datas = getTableAction().getDataSource()
+        // const info = reactive({...data.info})
+        console.log('------------save---------')
+        console.log(data)
+        if(!data.id) {
+          await addUser(data).then(res => {
+            console.log(res)
+            getTableAction().reload()
+          })
+          console.log('----------add---')
+        }else {
+          await editUser(data).then(res => {
+            console.log(res)
+            getTableAction().reload()
+          })
+          console.log('----------edit---')
+
+        }
+        // if (modelData.title === "添加") {
+        //   datas.map(item => {
+        //     if (info.id === 0 || info.id === item.id) {
+        //       info.id = item.id + 1
+        //     }
+        //   })
+        //   datas.push(info)
+        //   getTableAction().setTableData(datas)
+        // } else {
+        //   console.log('编辑')
+        //   // getTableAction().setTableData(datas.filter(item => item.id !== info.id))
+        //   let dataArr:object[] = []
+        //   datas.map(item => {
+        //     if (item.id === info.id) {
+        //       item = info
+        //     }
+        //     dataArr.push(item)
+        //   })
+        //   getTableAction().setTableData(dataArr)
+        // }
+      }
+      async function getTreeData() {
+        let treeData = await getAllMenuList() as any
+        modelData.treeData = JSON.parse(JSON.stringify(treeData).replace(/menuName/g,"title").replace(/id/g,"key"))
+
+        console.log(modelData.treeData)
+      }
+
+      function createActions(record: EditRecordRow, column: BasicColumn): ActionItem[] {
+        if (false) {
+          console.log(column)
+        }
+        return [
+          {
+            label: '编辑',
+            icon: 'ant-design:edit-outlined',
+            color: 'warning',
+            onClick: handleEdit.bind(null, record),
+          },
+          {
+            label: '删除',
+            color: 'error',
+            icon: 'ic:outline-delete-outline',
+            popConfirm: {
+              title: '是否确认删除',
+              confirm: handleDelete.bind(null, record),
+            },
+          },
+        ];
+      }
+      return {
+        modelData,
+        tableRef,
+        registerTable,
+        addRole,
+        handleEdit,
+        createActions,
+        getTableAction,
+        getSelectRowList,
+        getSelectRowKeyList,
+        addRegister,
+        saveData,
+        getUserList,
+        getTreeData
+      };
+    },
+  });
+</script>

+ 1 - 1
src/views/table/table/index.vue

@@ -2,7 +2,7 @@
   <div class="p-4">
     <BasicTable
       title="基础示例"
-      titleHelpMessage="温馨提醒"
+      titleHelpMessage="温馨提醒123"
       :columns="columns"
       :dataSource="data"
       :canResize="canResize"