1.完成新版菜单管理
This commit is contained in:
@@ -178,6 +178,7 @@ CHANNEL_LAYERS = {
|
|||||||
"BACKEND": "channels.layers.InMemoryChannelLayer"
|
"BACKEND": "channels.layers.InMemoryChannelLayer"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
REDIS_URL = locals().get('REDIS_URL', "")
|
||||||
# CHANNEL_LAYERS = {
|
# CHANNEL_LAYERS = {
|
||||||
# 'default': {
|
# 'default': {
|
||||||
# 'BACKEND': 'channels_redis.core.RedisChannelLayer',
|
# 'BACKEND': 'channels_redis.core.RedisChannelLayer',
|
||||||
@@ -399,10 +400,11 @@ TENANT_SHARED_APPS = []
|
|||||||
PLUGINS_URL_PATTERNS = []
|
PLUGINS_URL_PATTERNS = []
|
||||||
# ********** 一键导入插件配置开始 **********
|
# ********** 一键导入插件配置开始 **********
|
||||||
# 例如:
|
# 例如:
|
||||||
# from dvadmin_upgrade_center.settings import * # 升级中心
|
from dvadmin3_upgrade_center.settings import * # 升级中心
|
||||||
# from dvadmin_celery.settings import * # celery 异步任务
|
# from dvadmin_celery.settings import * # celery 异步任务
|
||||||
# from dvadmin_third.settings import * # 第三方用户管理
|
# from dvadmin_third.settings import * # 第三方用户管理
|
||||||
# from dvadmin_ak_sk.settings import * # 秘钥管理管理
|
# from dvadmin_ak_sk.settings import * # 秘钥管理管理
|
||||||
# from dvadmin_tenants.settings import * # 租户管理
|
# from dvadmin_tenants.settings import * # 租户管理
|
||||||
|
# from dvadmin_uniapp.settings import *
|
||||||
# ...
|
# ...
|
||||||
# ********** 一键导入插件配置结束 **********
|
# ********** 一键导入插件配置结束 **********
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ class MenuInitSerializer(CustomModelSerializer):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Menu
|
model = Menu
|
||||||
fields = ['name', 'icon', 'sort', 'is_link', 'is_catalog', 'web_path', 'component', 'component_name', 'status',
|
fields = ['name', 'icon', 'sort', 'is_link', 'menu_type', 'web_path', 'component', 'component_name', 'status',
|
||||||
'cache', 'visible', 'parent', 'children', 'menu_button', 'creator', 'dept_belong_id']
|
'cache', 'visible', 'parent', 'children', 'menu_button', 'creator', 'dept_belong_id']
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
'creator': {'write_only': True},
|
'creator': {'write_only': True},
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"icon": "iconfont icon-xitongshezhi",
|
"icon": "iconfont icon-xitongshezhi",
|
||||||
"sort": 1,
|
"sort": 1,
|
||||||
"is_link": false,
|
"is_link": false,
|
||||||
"is_catalog": true,
|
"menu_type": 0,
|
||||||
"web_path": "/system",
|
"web_path": "/system",
|
||||||
"component": "",
|
"component": "",
|
||||||
"component_name": "",
|
"component_name": "",
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
"icon": "iconfont icon-caidan",
|
"icon": "iconfont icon-caidan",
|
||||||
"sort": 1,
|
"sort": 1,
|
||||||
"is_link": false,
|
"is_link": false,
|
||||||
"is_catalog": false,
|
"menu_type": 1,
|
||||||
"web_path": "/menu",
|
"web_path": "/menu",
|
||||||
"component": "system/menu/index",
|
"component": "system/menu/index",
|
||||||
"component_name": "menu",
|
"component_name": "menu",
|
||||||
@@ -87,7 +87,7 @@
|
|||||||
"icon": "dot-circle-o",
|
"icon": "dot-circle-o",
|
||||||
"sort": 2,
|
"sort": 2,
|
||||||
"is_link": false,
|
"is_link": false,
|
||||||
"is_catalog": false,
|
"menu_type": 1,
|
||||||
"web_path": "/menuButton",
|
"web_path": "/menuButton",
|
||||||
"component": "system/menuButton/index",
|
"component": "system/menuButton/index",
|
||||||
"component_name": "menuButton",
|
"component_name": "menuButton",
|
||||||
@@ -127,7 +127,7 @@
|
|||||||
"icon": "ele-OfficeBuilding",
|
"icon": "ele-OfficeBuilding",
|
||||||
"sort": 3,
|
"sort": 3,
|
||||||
"is_link": false,
|
"is_link": false,
|
||||||
"is_catalog": false,
|
"menu_type": 1,
|
||||||
"web_path": "/dept",
|
"web_path": "/dept",
|
||||||
"component": "system/dept/index",
|
"component": "system/dept/index",
|
||||||
"component_name": "dept",
|
"component_name": "dept",
|
||||||
@@ -203,7 +203,7 @@
|
|||||||
"icon": "ele-ColdDrink",
|
"icon": "ele-ColdDrink",
|
||||||
"sort": 4,
|
"sort": 4,
|
||||||
"is_link": false,
|
"is_link": false,
|
||||||
"is_catalog": false,
|
"menu_type": 1,
|
||||||
"web_path": "/role",
|
"web_path": "/role",
|
||||||
"component": "system/role/index",
|
"component": "system/role/index",
|
||||||
"component_name": "role",
|
"component_name": "role",
|
||||||
@@ -255,7 +255,7 @@
|
|||||||
"icon": "iconfont icon-bolangneng",
|
"icon": "iconfont icon-bolangneng",
|
||||||
"sort": 5,
|
"sort": 5,
|
||||||
"is_link": false,
|
"is_link": false,
|
||||||
"is_catalog": false,
|
"menu_type": 1,
|
||||||
"web_path": "/columns",
|
"web_path": "/columns",
|
||||||
"component": "system/columns/index",
|
"component": "system/columns/index",
|
||||||
"component_name": "columns",
|
"component_name": "columns",
|
||||||
@@ -313,7 +313,7 @@
|
|||||||
"icon": "iconfont icon-icon-",
|
"icon": "iconfont icon-icon-",
|
||||||
"sort": 6,
|
"sort": 6,
|
||||||
"is_link": false,
|
"is_link": false,
|
||||||
"is_catalog": false,
|
"menu_type": 1,
|
||||||
"web_path": "/user",
|
"web_path": "/user",
|
||||||
"component": "system/user/index",
|
"component": "system/user/index",
|
||||||
"component_name": "user",
|
"component_name": "user",
|
||||||
@@ -383,7 +383,7 @@
|
|||||||
"icon": "iconfont icon-xiaoxizhongxin",
|
"icon": "iconfont icon-xiaoxizhongxin",
|
||||||
"sort": 7,
|
"sort": 7,
|
||||||
"is_link": false,
|
"is_link": false,
|
||||||
"is_catalog": false,
|
"menu_type": 1,
|
||||||
"web_path": "/messageCenter",
|
"web_path": "/messageCenter",
|
||||||
"component": "system/messageCenter/index",
|
"component": "system/messageCenter/index",
|
||||||
"component_name": "messageCenter",
|
"component_name": "messageCenter",
|
||||||
@@ -429,7 +429,7 @@
|
|||||||
"icon": "ele-SetUp",
|
"icon": "ele-SetUp",
|
||||||
"sort": 8,
|
"sort": 8,
|
||||||
"is_link": false,
|
"is_link": false,
|
||||||
"is_catalog": false,
|
"menu_type": 1,
|
||||||
"web_path": "/apiWhiteList",
|
"web_path": "/apiWhiteList",
|
||||||
"component": "system/whiteList/index",
|
"component": "system/whiteList/index",
|
||||||
"component_name": "whiteList",
|
"component_name": "whiteList",
|
||||||
@@ -478,7 +478,7 @@
|
|||||||
"icon": "iconfont icon-configure",
|
"icon": "iconfont icon-configure",
|
||||||
"sort": 2,
|
"sort": 2,
|
||||||
"is_link": false,
|
"is_link": false,
|
||||||
"is_catalog": true,
|
"menu_type": 0,
|
||||||
"web_path": "/generalConfig",
|
"web_path": "/generalConfig",
|
||||||
"component": "",
|
"component": "",
|
||||||
"component_name": "",
|
"component_name": "",
|
||||||
@@ -491,7 +491,7 @@
|
|||||||
"icon": "iconfont icon-system",
|
"icon": "iconfont icon-system",
|
||||||
"sort": 0,
|
"sort": 0,
|
||||||
"is_link": false,
|
"is_link": false,
|
||||||
"is_catalog": false,
|
"menu_type": 1,
|
||||||
"web_path": "/config",
|
"web_path": "/config",
|
||||||
"component": "system/config/index",
|
"component": "system/config/index",
|
||||||
"component_name": "config",
|
"component_name": "config",
|
||||||
@@ -537,7 +537,7 @@
|
|||||||
"icon": "iconfont icon-dict",
|
"icon": "iconfont icon-dict",
|
||||||
"sort": 1,
|
"sort": 1,
|
||||||
"is_link": false,
|
"is_link": false,
|
||||||
"is_catalog": false,
|
"menu_type": 1,
|
||||||
"web_path": "/dictionary",
|
"web_path": "/dictionary",
|
||||||
"component": "system/dictionary/index",
|
"component": "system/dictionary/index",
|
||||||
"component_name": "dictionary",
|
"component_name": "dictionary",
|
||||||
@@ -583,7 +583,7 @@
|
|||||||
"icon": "iconfont icon-Area",
|
"icon": "iconfont icon-Area",
|
||||||
"sort": 2,
|
"sort": 2,
|
||||||
"is_link": false,
|
"is_link": false,
|
||||||
"is_catalog": false,
|
"menu_type": 1,
|
||||||
"web_path": "/areas",
|
"web_path": "/areas",
|
||||||
"component": "system/areas/index",
|
"component": "system/areas/index",
|
||||||
"component_name": "areas",
|
"component_name": "areas",
|
||||||
@@ -629,7 +629,7 @@
|
|||||||
"icon": "iconfont icon-file",
|
"icon": "iconfont icon-file",
|
||||||
"sort": 3,
|
"sort": 3,
|
||||||
"is_link": false,
|
"is_link": false,
|
||||||
"is_catalog": false,
|
"menu_type": 1,
|
||||||
"web_path": "/file",
|
"web_path": "/file",
|
||||||
"component": "system/fileList/index",
|
"component": "system/fileList/index",
|
||||||
"component_name": "file",
|
"component_name": "file",
|
||||||
@@ -672,7 +672,7 @@
|
|||||||
"icon": "iconfont icon-rizhi",
|
"icon": "iconfont icon-rizhi",
|
||||||
"sort": 3,
|
"sort": 3,
|
||||||
"is_link": false,
|
"is_link": false,
|
||||||
"is_catalog": true,
|
"menu_type": 0,
|
||||||
"web_path": "/log",
|
"web_path": "/log",
|
||||||
"component": "",
|
"component": "",
|
||||||
"component_name": "",
|
"component_name": "",
|
||||||
@@ -685,7 +685,7 @@
|
|||||||
"icon": "iconfont icon-guanlidenglurizhi",
|
"icon": "iconfont icon-guanlidenglurizhi",
|
||||||
"sort": 1,
|
"sort": 1,
|
||||||
"is_link": false,
|
"is_link": false,
|
||||||
"is_catalog": false,
|
"menu_type": 1,
|
||||||
"web_path": "/loginLog",
|
"web_path": "/loginLog",
|
||||||
"component": "system/log/loginLog/index",
|
"component": "system/log/loginLog/index",
|
||||||
"component_name": "loginLog",
|
"component_name": "loginLog",
|
||||||
@@ -713,7 +713,7 @@
|
|||||||
"icon": "iconfont icon-caozuorizhi",
|
"icon": "iconfont icon-caozuorizhi",
|
||||||
"sort": 2,
|
"sort": 2,
|
||||||
"is_link": false,
|
"is_link": false,
|
||||||
"is_catalog": false,
|
"menu_type": 1,
|
||||||
"web_path": "/operationLog",
|
"web_path": "/operationLog",
|
||||||
"component": "system/log/operationLog/index",
|
"component": "system/log/operationLog/index",
|
||||||
"component_name": "operationLog",
|
"component_name": "operationLog",
|
||||||
|
|||||||
@@ -156,22 +156,24 @@ class Menu(CoreModel):
|
|||||||
help_text="上级菜单",
|
help_text="上级菜单",
|
||||||
)
|
)
|
||||||
icon = models.CharField(max_length=64, verbose_name="菜单图标", null=True, blank=True, help_text="菜单图标")
|
icon = models.CharField(max_length=64, verbose_name="菜单图标", null=True, blank=True, help_text="菜单图标")
|
||||||
name = models.CharField(max_length=64, verbose_name="菜单名称", help_text="菜单名称")
|
name = models.CharField(max_length=64, verbose_name="目录名称/菜单名称/按钮名称", help_text="目录名称/菜单名称/按钮名称")
|
||||||
sort = models.IntegerField(default=1, verbose_name="显示排序", null=True, blank=True, help_text="显示排序")
|
sort = models.IntegerField(default=1, verbose_name="显示排序", null=True, blank=True, help_text="显示排序")
|
||||||
ISLINK_CHOICES = (
|
|
||||||
(0, "否"),
|
|
||||||
(1, "是"),
|
|
||||||
)
|
|
||||||
is_link = models.BooleanField(default=False, verbose_name="是否外链", help_text="是否外链")
|
is_link = models.BooleanField(default=False, verbose_name="是否外链", help_text="是否外链")
|
||||||
is_catalog = models.BooleanField(default=False, verbose_name="是否目录", help_text="是否目录")
|
MENU_TYPE_CHOICES =(
|
||||||
|
(0, "目录"),
|
||||||
|
(1, "菜单"),
|
||||||
|
(2, "按钮"),
|
||||||
|
)
|
||||||
|
menu_type = models.IntegerField(default=0, verbose_name="菜单类型", help_text="菜单类型")
|
||||||
web_path = models.CharField(max_length=128, verbose_name="路由地址", null=True, blank=True, help_text="路由地址")
|
web_path = models.CharField(max_length=128, verbose_name="路由地址", null=True, blank=True, help_text="路由地址")
|
||||||
component = models.CharField(max_length=128, verbose_name="组件地址", null=True, blank=True, help_text="组件地址")
|
component = models.CharField(max_length=200, verbose_name="组件地址/按钮权限值", null=True, blank=True, help_text="组件地址/按钮权限值")
|
||||||
component_name = models.CharField(max_length=50, verbose_name="组件名称", null=True, blank=True,
|
component_name = models.CharField(max_length=50, verbose_name="组件名称", null=True, blank=True,
|
||||||
help_text="组件名称")
|
help_text="组件名称")
|
||||||
status = models.BooleanField(default=True, blank=True, verbose_name="菜单状态", help_text="菜单状态")
|
status = models.BooleanField(default=True, blank=True, verbose_name="菜单状态", help_text="菜单状态")
|
||||||
cache = models.BooleanField(default=False, blank=True, verbose_name="是否页面缓存", help_text="是否页面缓存")
|
cache = models.BooleanField(default=False, blank=True, verbose_name="是否页面缓存", help_text="是否页面缓存")
|
||||||
visible = models.BooleanField(default=True, blank=True, verbose_name="侧边栏中是否显示",
|
visible = models.BooleanField(default=True, blank=True, verbose_name="侧边栏中是否显示",
|
||||||
help_text="侧边栏中是否显示")
|
help_text="侧边栏中是否显示")
|
||||||
|
frame_out = models.BooleanField(default=False, blank=True, verbose_name="是否主框架外", help_text="是否主框架外")
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
db_table = table_prefix + "system_menu"
|
db_table = table_prefix + "system_menu"
|
||||||
@@ -184,7 +186,6 @@ class Columns(CoreModel):
|
|||||||
role = models.ForeignKey(to='Role', on_delete=models.CASCADE, verbose_name='角色', db_constraint=False)
|
role = models.ForeignKey(to='Role', on_delete=models.CASCADE, verbose_name='角色', db_constraint=False)
|
||||||
app = models.CharField(max_length=64, verbose_name='应用名')
|
app = models.CharField(max_length=64, verbose_name='应用名')
|
||||||
model = models.CharField(max_length=64, verbose_name='表名')
|
model = models.CharField(max_length=64, verbose_name='表名')
|
||||||
menu = models.ForeignKey(to='Menu', on_delete=models.CASCADE, verbose_name='菜单', db_constraint=False)
|
|
||||||
field_name = models.CharField(max_length=64, verbose_name='模型表字段名')
|
field_name = models.CharField(max_length=64, verbose_name='模型表字段名')
|
||||||
title = models.CharField(max_length=64, verbose_name='字段显示名')
|
title = models.CharField(max_length=64, verbose_name='字段显示名')
|
||||||
is_query = models.BooleanField(default=1, verbose_name='是否可查询')
|
is_query = models.BooleanField(default=1, verbose_name='是否可查询')
|
||||||
@@ -457,7 +458,7 @@ class SystemConfig(CoreModel):
|
|||||||
help_text="父级",
|
help_text="父级",
|
||||||
)
|
)
|
||||||
title = models.CharField(max_length=50, verbose_name="标题", help_text="标题")
|
title = models.CharField(max_length=50, verbose_name="标题", help_text="标题")
|
||||||
key = models.CharField(max_length=20, verbose_name="键", help_text="键", db_index=True)
|
key = models.CharField(max_length=50, verbose_name="键", help_text="键", db_index=True)
|
||||||
value = models.JSONField(max_length=100, verbose_name="值", help_text="值", null=True, blank=True)
|
value = models.JSONField(max_length=100, verbose_name="值", help_text="值", null=True, blank=True)
|
||||||
sort = models.IntegerField(default=0, verbose_name="排序", help_text="排序", blank=True)
|
sort = models.IntegerField(default=0, verbose_name="排序", help_text="排序", blank=True)
|
||||||
status = models.BooleanField(default=True, verbose_name="启用状态", help_text="启用状态")
|
status = models.BooleanField(default=True, verbose_name="启用状态", help_text="启用状态")
|
||||||
|
|||||||
@@ -32,10 +32,9 @@ class ColumnViewSet(CustomModelViewSet):
|
|||||||
role_id = request.query_params.get('role')
|
role_id = request.query_params.get('role')
|
||||||
app_name = request.query_params.get('app')
|
app_name = request.query_params.get('app')
|
||||||
model_name = request.query_params.get('model')
|
model_name = request.query_params.get('model')
|
||||||
menu = request.query_params.get('menu')
|
if not role_id or not model_name or not app_name:
|
||||||
if not role_id or not model_name or not app_name or not menu:
|
|
||||||
return SuccessResponse([])
|
return SuccessResponse([])
|
||||||
queryset = self.filter_queryset(self.get_queryset().filter(role_id=role_id, model=model_name, app=app_name,menu_id=menu))
|
queryset = self.filter_queryset(self.get_queryset().filter(role_id=role_id, model=model_name, app=app_name))
|
||||||
page = self.paginate_queryset(queryset)
|
page = self.paginate_queryset(queryset)
|
||||||
if page is not None:
|
if page is not None:
|
||||||
serializer = self.get_serializer(page, many=True, request=request)
|
serializer = self.get_serializer(page, many=True, request=request)
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ from rest_framework.decorators import action
|
|||||||
|
|
||||||
from dvadmin.system.models import Menu, RoleMenuPermission
|
from dvadmin.system.models import Menu, RoleMenuPermission
|
||||||
from dvadmin.system.views.menu_button import MenuButtonSerializer
|
from dvadmin.system.views.menu_button import MenuButtonSerializer
|
||||||
from dvadmin.utils.json_response import SuccessResponse, ErrorResponse
|
from dvadmin.utils.json_response import SuccessResponse, ErrorResponse, DetailResponse
|
||||||
from dvadmin.utils.serializers import CustomModelSerializer
|
from dvadmin.utils.serializers import CustomModelSerializer
|
||||||
from dvadmin.utils.viewset import CustomModelViewSet
|
from dvadmin.utils.viewset import CustomModelViewSet
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ class MenuSerializer(CustomModelSerializer):
|
|||||||
菜单表的简单序列化器
|
菜单表的简单序列化器
|
||||||
"""
|
"""
|
||||||
menuPermission = serializers.SerializerMethodField(read_only=True)
|
menuPermission = serializers.SerializerMethodField(read_only=True)
|
||||||
hasChild = serializers.SerializerMethodField()
|
hasChildren = serializers.SerializerMethodField()
|
||||||
|
|
||||||
def get_menuPermission(self, instance):
|
def get_menuPermission(self, instance):
|
||||||
queryset = instance.menuPermission.order_by('-name').values('id', 'name', 'value')
|
queryset = instance.menuPermission.order_by('-name').values('id', 'name', 'value')
|
||||||
@@ -31,7 +31,7 @@ class MenuSerializer(CustomModelSerializer):
|
|||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_hasChild(self, instance):
|
def get_hasChildren(self, instance):
|
||||||
hasChild = Menu.objects.filter(parent=instance.id)
|
hasChild = Menu.objects.filter(parent=instance.id)
|
||||||
if hasChild:
|
if hasChild:
|
||||||
return True
|
return True
|
||||||
@@ -71,7 +71,7 @@ class WebRouterSerializer(CustomModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = Menu
|
model = Menu
|
||||||
fields = (
|
fields = (
|
||||||
'id', 'parent', 'icon', 'sort', 'path', 'name', 'title', 'is_link', 'is_catalog', 'web_path', 'component',
|
'id', 'parent', 'icon', 'sort', 'path', 'name', 'title', 'is_link', 'menu_type', 'web_path', 'component',
|
||||||
'component_name', 'cache', 'visible', 'status')
|
'component_name', 'cache', 'visible', 'status')
|
||||||
read_only_fields = ["id"]
|
read_only_fields = ["id"]
|
||||||
|
|
||||||
@@ -90,37 +90,31 @@ class MenuViewSet(CustomModelViewSet):
|
|||||||
create_serializer_class = MenuCreateSerializer
|
create_serializer_class = MenuCreateSerializer
|
||||||
update_serializer_class = MenuCreateSerializer
|
update_serializer_class = MenuCreateSerializer
|
||||||
search_fields = ['name', 'status']
|
search_fields = ['name', 'status']
|
||||||
filter_fields = ['parent', 'name', 'status', 'is_link', 'visible', 'cache', 'is_catalog']
|
filter_fields = ['parent', 'name', 'status', 'is_link', 'visible', 'cache', 'menu_type']
|
||||||
|
|
||||||
def list(self, request):
|
@action(methods=['get'], detail=False)
|
||||||
|
def tree(self, request):
|
||||||
"""懒加载"""
|
"""懒加载"""
|
||||||
request.query_params._mutable = True
|
|
||||||
params = request.query_params
|
params = request.query_params
|
||||||
parent = params.get('parent', None)
|
menu_type = params.get('menu_type', 0)
|
||||||
page = params.get('page', None)
|
queryset = Menu.objects.filter(menu_type=menu_type).order_by('sort')
|
||||||
limit = params.get('limit', None)
|
|
||||||
if page:
|
|
||||||
del params['page']
|
|
||||||
if limit:
|
|
||||||
del params['limit']
|
|
||||||
if params:
|
|
||||||
if parent:
|
|
||||||
queryset = self.queryset.filter(parent=parent)
|
|
||||||
else:
|
|
||||||
queryset = self.queryset.filter()
|
|
||||||
else:
|
|
||||||
queryset = self.queryset.filter(parent__isnull=True)
|
|
||||||
queryset = self.filter_queryset(queryset)
|
|
||||||
serializer = MenuSerializer(queryset, many=True, request=request)
|
serializer = MenuSerializer(queryset, many=True, request=request)
|
||||||
data = serializer.data
|
data = serializer.data
|
||||||
return SuccessResponse(data=data)
|
return DetailResponse(data=data)
|
||||||
|
|
||||||
|
@action(methods=['get'], detail=True)
|
||||||
|
def getChildren(self,request,pk):
|
||||||
|
queryset = Menu.objects.filter(parent=pk)
|
||||||
|
serializer = MenuSerializer(queryset, many=True, request=request)
|
||||||
|
data = serializer.data
|
||||||
|
return DetailResponse(data=data)
|
||||||
|
|
||||||
@action(methods=['GET'], detail=False, permission_classes=[])
|
@action(methods=['GET'], detail=False, permission_classes=[])
|
||||||
def web_router(self, request):
|
def web_router(self, request):
|
||||||
"""用于前端获取当前角色的路由"""
|
"""用于前端获取当前角色的路由"""
|
||||||
user = request.user
|
user = request.user
|
||||||
is_admin = user.role.values_list('admin', flat=True)
|
is_admin = user.role.values_list('admin', flat=True)
|
||||||
if user.is_superuser or True in is_admin:
|
if user.is_superuser:
|
||||||
queryset = self.queryset.filter(status=1)
|
queryset = self.queryset.filter(status=1)
|
||||||
else:
|
else:
|
||||||
role_list = user.role.values_list('id', flat=True)
|
role_list = user.role.values_list('id', flat=True)
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ class RoleMenuPermissionSerializer(CustomModelSerializer):
|
|||||||
|
|
||||||
def get_columns(self, instance):
|
def get_columns(self, instance):
|
||||||
params = self.request.query_params
|
params = self.request.query_params
|
||||||
col_list = Columns.objects.filter(role__id=params.get('role'),menu__id=instance['id'])
|
col_list = Columns.objects.filter(role__id=params.get('role'))
|
||||||
serializer = RoleColumnsSerializer(col_list,many=True,request=self.request)
|
serializer = RoleColumnsSerializer(col_list,many=True,request=self.request)
|
||||||
return serializer.data
|
return serializer.data
|
||||||
|
|
||||||
|
|||||||
@@ -79,6 +79,5 @@ class CustomPagination(PageNumberPagination):
|
|||||||
('total', total),
|
('total', total),
|
||||||
('is_next', is_next),
|
('is_next', is_next),
|
||||||
('is_previous', is_previous),
|
('is_previous', is_previous),
|
||||||
('data', data),
|
('data', data)
|
||||||
('permission', self.request.permission_fields)
|
|
||||||
]))
|
]))
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { request } from '/@/utils/service';
|
|||||||
//扩展包
|
//扩展包
|
||||||
import { FsExtendsEditor,FsExtendsUploader } from '@fast-crud/fast-extends';
|
import { FsExtendsEditor,FsExtendsUploader } from '@fast-crud/fast-extends';
|
||||||
import '@fast-crud/fast-extends/dist/style.css';
|
import '@fast-crud/fast-extends/dist/style.css';
|
||||||
import { successMessage, successNotification } from '/@/utils/message';
|
import { ElMessage } from "element-plus";
|
||||||
export default {
|
export default {
|
||||||
async install(app: any, options: any) {
|
async install(app: any, options: any) {
|
||||||
// 先安装ui
|
// 先安装ui
|
||||||
@@ -18,9 +18,9 @@ export default {
|
|||||||
app.use(FastCrud, {
|
app.use(FastCrud, {
|
||||||
//i18n, //i18n配置,可选,默认使用中文,具体用法请看demo里的 src/i18n/index.js 文件
|
//i18n, //i18n配置,可选,默认使用中文,具体用法请看demo里的 src/i18n/index.js 文件
|
||||||
// 此处配置公共的dictRequest(字典请求)
|
// 此处配置公共的dictRequest(字典请求)
|
||||||
async dictRequest({ dict }: any) {
|
async dictRequest({ url }: any) {
|
||||||
//根据dict的url,异步返回一个字典数组
|
//根据dict的url,异步返回一个字典数组
|
||||||
return await request({ url: dict.url, params: dict.params || {} }).then((res:any)=>{
|
return await request({ url: url, }).then((res:any)=>{
|
||||||
return res.data
|
return res.data
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -41,14 +41,21 @@ export default {
|
|||||||
transformRes: ({ res }: any) => {
|
transformRes: ({ res }: any) => {
|
||||||
//将pageRequest的返回数据,转换为fast-crud所需要的格式
|
//将pageRequest的返回数据,转换为fast-crud所需要的格式
|
||||||
//return {records,currentPage,pageSize,total};
|
//return {records,currentPage,pageSize,total};
|
||||||
|
if(res.page){
|
||||||
return { records: res.data, currentPage: res.page, pageSize: res.limit, total: res.total };
|
return { records: res.data, currentPage: res.page, pageSize: res.limit, total: res.total };
|
||||||
|
}else{
|
||||||
|
return { records: res.data,currentPage: 1, pageSize: res.data.length, total: res.data.length };
|
||||||
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
form: {
|
form: {
|
||||||
afterSubmit(ctx: any) {
|
afterSubmit(ctx:any ) {
|
||||||
// 增加crud提示
|
const {mode} = ctx
|
||||||
if (ctx.res.code == 2000) {
|
if (mode === "add") {
|
||||||
successNotification(ctx.res.msg);
|
ElMessage.success({ message: "添加成功" });
|
||||||
|
} else if (mode === "edit") {
|
||||||
|
ElMessage.success({ message: "保存成功" });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -100,10 +107,12 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
successHandle(ret) {
|
successHandle(ret) {
|
||||||
|
console.log(111,ret)
|
||||||
// 上传完成后的结果处理, 此处应返回格式为{url:xxx,key:xxx}
|
// 上传完成后的结果处理, 此处应返回格式为{url:xxx,key:xxx}
|
||||||
return {
|
return {
|
||||||
url: getBaseURL() + ret.data.url,
|
url: ret.data.url,
|
||||||
key: ret.data.id
|
key: ret.data.id,
|
||||||
|
...ret.data
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,16 @@ import { DictionaryStore } from '/@/stores/dictionary';
|
|||||||
/**
|
/**
|
||||||
* @method 获取指定name字典
|
* @method 获取指定name字典
|
||||||
*/
|
*/
|
||||||
export const dictionary = (name: string) => {
|
export const dictionary = (key: string,value:string|number) => {
|
||||||
const dict = DictionaryStore()
|
const dict = DictionaryStore()
|
||||||
const dictionary = toRaw(dict.data)
|
const dictionary = toRaw(dict.data)
|
||||||
return dictionary[name]
|
if(value!==null || value !==''){
|
||||||
|
for (let item of dictionary[key]) {
|
||||||
|
if (item.value === value) {
|
||||||
|
return item.label
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
return dictionary[key]
|
||||||
}
|
}
|
||||||
@@ -12,5 +12,5 @@ export const scanAndInstallPlugins = (app: any) => {
|
|||||||
pluginNames.add(pluginsName);
|
pluginNames.add(pluginsName);
|
||||||
}
|
}
|
||||||
pluginsAll = Array.from(pluginNames);
|
pluginsAll = Array.from(pluginNames);
|
||||||
console.log('已发现插件:', pluginsAll);
|
console.table('已注册插件:', pluginsAll);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-drawer size="70%" v-model="drawer" direction="rtl" destroy-on-close :before-close="handleClose">
|
<el-drawer size="70%" v-model="drawer" direction="rtl" destroy-on-close :before-close="handleClose">
|
||||||
<fs-page>
|
|
||||||
<fs-crud ref="crudRef" v-bind="crudBinding"> </fs-crud>
|
<fs-crud ref="crudRef" v-bind="crudBinding"> </fs-crud>
|
||||||
</fs-page>
|
|
||||||
</el-drawer>
|
</el-drawer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -25,6 +23,7 @@ const handleClose = (done: () => void) => {
|
|||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
done();
|
done();
|
||||||
|
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
// catch error
|
// catch error
|
||||||
|
|||||||
@@ -5,12 +5,19 @@ export const apiPrefix = '/api/system/menu/';
|
|||||||
|
|
||||||
export function GetList(query: UserPageQuery) {
|
export function GetList(query: UserPageQuery) {
|
||||||
return request({
|
return request({
|
||||||
url: apiPrefix,
|
url: apiPrefix+"tree/",
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query,
|
params: query,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function GetChildren(id: InfoReq) {
|
||||||
|
return request({
|
||||||
|
url: apiPrefix + id + '/getChildren/',
|
||||||
|
method: 'get',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function GetObj(id: InfoReq) {
|
export function GetObj(id: InfoReq) {
|
||||||
return request({
|
return request({
|
||||||
url: apiPrefix + id + '/',
|
url: apiPrefix + id + '/',
|
||||||
|
|||||||
364
web/src/views/system/menu/crud.ts
Normal file
364
web/src/views/system/menu/crud.ts
Normal file
@@ -0,0 +1,364 @@
|
|||||||
|
import * as api from './api';
|
||||||
|
import { CreateCrudOptionsProps, CreateCrudOptionsRet, dict, useCompute } from '@fast-crud/fast-crud';
|
||||||
|
const { compute } = useCompute();
|
||||||
|
import { shallowRef } from "vue";
|
||||||
|
import IconSelector from "/@/components/IconSelector/index.vue"
|
||||||
|
export default function ({ crudExpose, onAddCatalog, onAddChildren, onAddButton }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||||
|
const pageRequest = async (query) => {
|
||||||
|
return await api.GetList(query);
|
||||||
|
};
|
||||||
|
const editRequest = async ({ form, row }) => {
|
||||||
|
form.id = row.id;
|
||||||
|
await api.UpdateObj(form);
|
||||||
|
if (row.parent) {
|
||||||
|
//刷新父节点的状态
|
||||||
|
reloadTreeChildren(row.parent);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const delRequest = async ({ row }:any) => {
|
||||||
|
await api.DelObj(row.id);
|
||||||
|
if (row.parent) {
|
||||||
|
//刷新父节点的状态
|
||||||
|
reloadTreeChildren(row.parent);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const addRequest = async (context:any) => {
|
||||||
|
return await api.AddObj(context.form);
|
||||||
|
};
|
||||||
|
|
||||||
|
//刷新父节点状态
|
||||||
|
function reloadTreeChildren(parent:string|number) {
|
||||||
|
const data = crudExpose.getBaseTableRef().store.states.treeData;
|
||||||
|
if (data.value != null) {
|
||||||
|
const item = data.value[parent];
|
||||||
|
if (item != null) {
|
||||||
|
item.loaded = false;
|
||||||
|
item.expanded = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const createFilter = (queryString: string) => {
|
||||||
|
return (file: any) => {
|
||||||
|
return file.value.toLowerCase().indexOf(queryString.toLowerCase()) !== -1;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取组件地址
|
||||||
|
const getCompoent = (queryString: string, cb: any) => {
|
||||||
|
const files: any = import.meta.glob('@views/**/*.vue');
|
||||||
|
let fileLists: Array<any> = [];
|
||||||
|
Object.keys(files).forEach((queryString: string) => {
|
||||||
|
fileLists.push({
|
||||||
|
label: queryString.replace(/(\.\/|\.vue)/g, ''),
|
||||||
|
value: queryString.replace(/(\.\/|\.vue)/g, ''),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
const results = queryString ? fileLists.filter(createFilter(queryString)) : fileLists;
|
||||||
|
// 统一去掉/src/views/前缀
|
||||||
|
results.forEach((val) => {
|
||||||
|
val.label = val.label.replace('/src/views/', '');
|
||||||
|
val.value = val.value.replace('/src/views/', '');
|
||||||
|
});
|
||||||
|
cb(results);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 验证路由地址
|
||||||
|
const { getFormData } = crudExpose;
|
||||||
|
const validateWebPath = (rule: any, value: string, callback: Function) => {
|
||||||
|
let pattern = /^\/.*?/;
|
||||||
|
let patternUrl = /http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w- .\/?%&=]*)?/;
|
||||||
|
const reg = getFormData().is_link ? patternUrl.test(value) : pattern.test(value);
|
||||||
|
if (reg) {
|
||||||
|
callback();
|
||||||
|
} else {
|
||||||
|
callback(new Error('请输入正确的地址'));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
crudOptions: {
|
||||||
|
request: {
|
||||||
|
pageRequest,
|
||||||
|
addRequest,
|
||||||
|
editRequest,
|
||||||
|
delRequest,
|
||||||
|
},
|
||||||
|
pagination: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
labelWidth: '120px',
|
||||||
|
row: { gutter: 20 },
|
||||||
|
// group: {
|
||||||
|
// groupType: 'tabs', //collapse, tabs
|
||||||
|
// accordion: false,
|
||||||
|
// groups: {
|
||||||
|
// catalog: {
|
||||||
|
// label: '目录',
|
||||||
|
// icon: 'el-icon-goods',
|
||||||
|
// columns: ['name', 'icon', 'sort', 'status'],
|
||||||
|
// },
|
||||||
|
// menu: {
|
||||||
|
// label: '菜单',
|
||||||
|
// icon: 'el-icon-price-tag',
|
||||||
|
// columns: ['parent', 'name', 'icon', 'is_link', 'web_path', 'component', 'cache', 'visible', 'frame_out', 'sort', 'status'],
|
||||||
|
// },
|
||||||
|
// info: {
|
||||||
|
// label: '按钮',
|
||||||
|
// collapsed: true, //默认折叠
|
||||||
|
// icon: 'el-icon-warning-outline',
|
||||||
|
// columns: ['name', 'component'],
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
},
|
||||||
|
table: {
|
||||||
|
lazy: true,
|
||||||
|
load: async (row: any, treeNode: unknown, resolve: (date: any[]) => void) => {
|
||||||
|
//懒加载,更新和删除后,需要刷新父节点的状态,见上方
|
||||||
|
const obj = await api.GetChildren(row.id);
|
||||||
|
resolve([...obj.data]);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
columns: {
|
||||||
|
id: {
|
||||||
|
title: 'ID',
|
||||||
|
key: 'id',
|
||||||
|
type: 'number',
|
||||||
|
column: {
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
menu_type: {
|
||||||
|
title: '类型',
|
||||||
|
type: 'dict-radio',
|
||||||
|
dict: dict({
|
||||||
|
data: [
|
||||||
|
{ label: '目录', value: 0 },
|
||||||
|
{ label: '菜单', value: 1 },
|
||||||
|
{ label: '按钮', value: 2 },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
form: {
|
||||||
|
value:0,
|
||||||
|
valueChange({ form, value, getComponentRef }) {
|
||||||
|
if (value) {
|
||||||
|
getComponentRef("parent").reloadDict(); // 执行city的select组件的reloadDict()方法,触发“city”重新加载字典
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
column: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
parent: {
|
||||||
|
title: '父级',
|
||||||
|
dict: dict({
|
||||||
|
prototype: true,
|
||||||
|
url({form}){
|
||||||
|
if(form && form.menu_type===1){
|
||||||
|
return '/api/system/menu/tree/?menu_type=0'
|
||||||
|
}else{
|
||||||
|
return `/api/system/menu/tree/?menu_type=1`
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
},
|
||||||
|
label: 'name',
|
||||||
|
value: 'id',
|
||||||
|
}),
|
||||||
|
type: 'dict-select',
|
||||||
|
form: {
|
||||||
|
show:compute(({form})=>{
|
||||||
|
return [1,2].includes(form.menu_type);
|
||||||
|
}),
|
||||||
|
rules: [{ required: true, message: '必填项' }],
|
||||||
|
component: {},
|
||||||
|
},
|
||||||
|
column: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
title: '名称',
|
||||||
|
search: { show: true },
|
||||||
|
type: 'text',
|
||||||
|
form: {
|
||||||
|
rules: [{ required: true, message: '请输入名称' }],
|
||||||
|
component: {
|
||||||
|
placeholder: '请输入名称',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
title: '图标',
|
||||||
|
form:{
|
||||||
|
show:compute(({form})=>{
|
||||||
|
return [0,1].includes(form.menu_type);
|
||||||
|
}),
|
||||||
|
component:{
|
||||||
|
name: shallowRef(IconSelector),
|
||||||
|
vModel: "modelValue",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
column: {
|
||||||
|
component: {
|
||||||
|
style: 'font-size:18px',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
sort: {
|
||||||
|
title: '排序',
|
||||||
|
type: 'number',
|
||||||
|
form: {
|
||||||
|
value: 1,
|
||||||
|
show:compute(({form})=>{
|
||||||
|
return [0,1].includes(form.menu_type);
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
is_link: {
|
||||||
|
title: '外链接',
|
||||||
|
type: 'dict-switch',
|
||||||
|
dict: dict({
|
||||||
|
data: [
|
||||||
|
{ label: '是', value: true },
|
||||||
|
{ label: '否', value: false },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
form: {
|
||||||
|
value: false,
|
||||||
|
show:compute(({form})=>{
|
||||||
|
return [1].includes(form.menu_type);
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
web_path: {
|
||||||
|
title: '路由地址',
|
||||||
|
form: {
|
||||||
|
show:compute(({form})=>{
|
||||||
|
return [1].includes(form.menu_type);
|
||||||
|
}),
|
||||||
|
helper: compute(({ form }) => {
|
||||||
|
return form.is_link ? '请输入http开头的地址' : '浏览器中url的地址,请以/开头';
|
||||||
|
}),
|
||||||
|
rules: [{ required: true, message: '请输入路由地址', validator: validateWebPath, trigger: 'blur' }],
|
||||||
|
component: {
|
||||||
|
placeholder: '请输入路由地址',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
column: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
component: {
|
||||||
|
title: '组件地址',
|
||||||
|
form: {
|
||||||
|
show: compute(({ form }) => {
|
||||||
|
return [1,2].includes(form.menu_type)
|
||||||
|
}),
|
||||||
|
helper: compute(({ form }) => {
|
||||||
|
return form.menu_type === 1 ? 'src/views下的文件夹地址' : '按钮权限值是唯一的标识';
|
||||||
|
}),
|
||||||
|
rules: [{ required: true, message: '请输入组件地址', trigger: 'blur' }],
|
||||||
|
component: {
|
||||||
|
style: {
|
||||||
|
width: '100%',
|
||||||
|
},
|
||||||
|
disabled: compute(({ form }) => {
|
||||||
|
if(form.is_link&&form.menu_type===1){
|
||||||
|
form.component ="无"
|
||||||
|
return form.is_link
|
||||||
|
}
|
||||||
|
form.component =null
|
||||||
|
return false
|
||||||
|
}),
|
||||||
|
name: compute(({ form }) => {
|
||||||
|
return [1,2].includes(form.menu_type)? 'el-autocomplete' : 'el-input';
|
||||||
|
}),
|
||||||
|
triggerOnFocus: false,
|
||||||
|
fetchSuggestions: (query, cb) => {
|
||||||
|
return getCompoent(query, cb);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
column: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
visible: {
|
||||||
|
title: '侧边可见',
|
||||||
|
search: { show: true },
|
||||||
|
type: 'dict-radio',
|
||||||
|
dict: dict({
|
||||||
|
data: [
|
||||||
|
{ label: '是', value: true },
|
||||||
|
{ label: '否', value: false },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
form: {
|
||||||
|
value: true,
|
||||||
|
show:compute(({form})=>{
|
||||||
|
return [1].includes(form.menu_type)
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
cache: {
|
||||||
|
title: '是否缓存',
|
||||||
|
search: { show: true },
|
||||||
|
type: 'dict-radio',
|
||||||
|
dict: dict({
|
||||||
|
data: [
|
||||||
|
{ label: '是', value: true },
|
||||||
|
{ label: '否', value: false },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
form: {
|
||||||
|
value: false,
|
||||||
|
show:compute(({form})=>{
|
||||||
|
return [1].includes(form.menu_type)
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
frame_out: {
|
||||||
|
title: '主框架外展示',
|
||||||
|
search: { show: true },
|
||||||
|
type: 'dict-radio',
|
||||||
|
dict: dict({
|
||||||
|
data: [
|
||||||
|
{ label: '是', value: true },
|
||||||
|
{ label: '否', value: false },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
form: {
|
||||||
|
value: false,
|
||||||
|
show:compute(({form})=>{
|
||||||
|
return [1].includes(form.menu_type)
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
status: {
|
||||||
|
title: '状态',
|
||||||
|
search: { show: true },
|
||||||
|
type: 'dict-radio',
|
||||||
|
dict: dict({
|
||||||
|
data: [
|
||||||
|
{ label: '启用', value: true },
|
||||||
|
{ label: '禁用', value: false },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
form: {
|
||||||
|
value: true,
|
||||||
|
show:compute(({form})=>{
|
||||||
|
return [0,1].includes(form.menu_type);
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,142 +1,124 @@
|
|||||||
<template>
|
<template>
|
||||||
<fs-page>
|
<fs-page>
|
||||||
<el-row class="menu-el-row">
|
<fs-crud ref="crudRef" v-bind="crudBinding">
|
||||||
<el-col :span="6">
|
|
||||||
<div class="menu-box menu-left-box">
|
|
||||||
<MenuTreeCom
|
|
||||||
ref="menuTreeRef"
|
|
||||||
:treeData="menuTreeData"
|
|
||||||
@treeClick="handleTreeClick"
|
|
||||||
@updateDept="handleUpdateMenu"
|
|
||||||
@deleteDept="handleDeleteMenu"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
|
|
||||||
<el-col :span="18">
|
</fs-crud>
|
||||||
<div class="menu-box menu-right-box">
|
<fs-form-wrapper ref="addChildrenRef" />
|
||||||
<MenuButtonCom ref="menuButtonRef" />
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
|
|
||||||
<el-drawer v-model="drawerVisible" title="菜单配置" direction="rtl" size="500px" :close-on-click-modal="false" :before-close="handleDrawerClose">
|
|
||||||
<MenuFormCom
|
|
||||||
v-if="drawerVisible"
|
|
||||||
:initFormData="drawerFormData"
|
|
||||||
:cacheData="menuTreeCacheData"
|
|
||||||
:treeData="menuTreeData"
|
|
||||||
@drawerClose="handleDrawerClose"
|
|
||||||
/>
|
|
||||||
</el-drawer>
|
|
||||||
</fs-page>
|
</fs-page>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup name="menuPages">
|
<script lang="ts" setup>
|
||||||
import { ref, onMounted } from 'vue';
|
import {ref, onMounted, reactive, computed} from "vue";
|
||||||
import XEUtils from 'xe-utils';
|
import createCrudOptions from "./crud";
|
||||||
import { ElMessageBox } from 'element-plus';
|
import {useExpose, useCrud, useCompute} from "@fast-crud/fast-crud";
|
||||||
import MenuTreeCom from './components/MenuTreeCom/index.vue';
|
import {AddObj} from "./api";
|
||||||
import MenuButtonCom from './components/MenuButtonCom/index.vue';
|
import {ElMessage } from "element-plus"
|
||||||
import MenuFormCom from './components/MenuFormCom/index.vue';
|
|
||||||
import { GetList, DelObj } from './api';
|
|
||||||
import { successNotification } from '/@/utils/message';
|
|
||||||
import { APIResponseData, MenuTreeItemType } from './types';
|
|
||||||
|
|
||||||
let menuTreeData = ref([]);
|
|
||||||
let menuTreeCacheData = ref<MenuTreeItemType[]>([]);
|
|
||||||
let drawerVisible = ref(false);
|
|
||||||
let drawerFormData = ref<Partial<MenuTreeItemType>>({});
|
|
||||||
let menuTreeRef = ref<InstanceType<typeof MenuTreeCom> | null>(null);
|
|
||||||
let menuButtonRef = ref<InstanceType<typeof MenuButtonCom> | null>(null);
|
|
||||||
|
|
||||||
const getData = () => {
|
|
||||||
GetList({}).then((ret: APIResponseData) => {
|
|
||||||
const responseData = ret.data;
|
|
||||||
const result = XEUtils.toArrayTree(responseData, {
|
|
||||||
parentKey: 'parent',
|
|
||||||
children: 'children',
|
|
||||||
strict: true,
|
|
||||||
});
|
|
||||||
menuTreeData.value = result;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
// crud组件的ref
|
||||||
* 菜单的点击事件
|
const crudRef = ref();
|
||||||
*/
|
// crud 配置的ref
|
||||||
const handleTreeClick = (record: MenuTreeItemType) => {
|
const crudBinding = ref();
|
||||||
menuButtonRef.value?.handleRefreshTable(record);
|
// 暴露的方法
|
||||||
};
|
const { crudExpose } = useExpose({ crudRef, crudBinding });
|
||||||
|
|
||||||
/**
|
|
||||||
* 部门的 新增 or 编辑 事件
|
|
||||||
*/
|
// 添加目录
|
||||||
const handleUpdateMenu = (type: string, record?: MenuTreeItemType) => {
|
const onAddCatalog = (row:any)=>{
|
||||||
if (type === 'update' && record) {
|
const childrenOptions = ref();
|
||||||
const parentData = menuTreeRef.value?.treeRef?.currentNode.parent.data || {};
|
childrenOptions.value = {...crudBinding.value.addForm};
|
||||||
menuTreeCacheData.value = [parentData];
|
childrenOptions.value.columns.parent_name.show = false
|
||||||
drawerFormData.value = record;
|
childrenOptions.value.columns.icon.show = true
|
||||||
|
childrenOptions.value.columns.web_path.show = false
|
||||||
|
childrenOptions.value.columns.is_link.show = false
|
||||||
|
childrenOptions.value.columns.cache.show = false
|
||||||
|
childrenOptions.value.columns.web_path.show = false
|
||||||
|
childrenOptions.value.columns.visible.show = false
|
||||||
|
childrenOptions.value.columns.frame_out.show = false
|
||||||
|
childrenOptions.value.columns.sort.show = true
|
||||||
|
childrenOptions.value.columns.status.show = true
|
||||||
|
childrenOptions.value.columns.component.show = false
|
||||||
|
childrenOptions.value.initialForm = { menu_type:0,web_path:'/' };
|
||||||
|
//覆盖提交方法
|
||||||
|
childrenOptions.value.doSubmit=({ form })=>{
|
||||||
|
AddObj(form).then(res=>{
|
||||||
|
ElMessage.success('添加成功')
|
||||||
|
childrenOptions.value.onSuccess()
|
||||||
|
addChildrenRef.value.close()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
drawerVisible.value = true;
|
addChildrenRef.value.open(childrenOptions.value);
|
||||||
};
|
}
|
||||||
const handleDrawerClose = (type?: string) => {
|
|
||||||
if (type === 'submit') {
|
|
||||||
getData();
|
|
||||||
}
|
|
||||||
drawerVisible.value = false;
|
|
||||||
drawerFormData.value = {};
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
// 添加子级
|
||||||
* 部门的删除事件
|
const addChildrenRef = ref();
|
||||||
*/
|
const onAddChildren = (row:any)=>{
|
||||||
const handleDeleteMenu = (id: string, callback: Function) => {
|
const childrenOptions = ref();
|
||||||
ElMessageBox.confirm('您确认删除该菜单项吗?', '温馨提示', {
|
childrenOptions.value = {...crudBinding.value.addForm};
|
||||||
confirmButtonText: '确认',
|
childrenOptions.value.columns.parent_name.show = true
|
||||||
cancelButtonText: '取消',
|
childrenOptions.value.columns.is_link.show = true
|
||||||
type: 'warning',
|
childrenOptions.value.columns.cache.show = true
|
||||||
}).then(async () => {
|
childrenOptions.value.columns.web_path.show = true
|
||||||
const res: APIResponseData = await DelObj(id);
|
childrenOptions.value.columns.component.show = true
|
||||||
callback();
|
childrenOptions.value.columns.component.title = "组件地址"
|
||||||
if (res?.code === 2000) {
|
childrenOptions.value.columns.visible.show = true
|
||||||
successNotification(res.msg as string);
|
childrenOptions.value.columns.frame_out.show = true
|
||||||
getData();
|
childrenOptions.value.columns.status.show = true
|
||||||
|
childrenOptions.value.initialForm = { parent_name: row.name,menu_type:1 };
|
||||||
|
//覆盖提交方法
|
||||||
|
childrenOptions.value.doSubmit=({ form })=>{
|
||||||
|
form.parent = row.id
|
||||||
|
AddObj(form).then(res=>{
|
||||||
|
ElMessage.success('添加成功')
|
||||||
|
childrenOptions.value.onSuccess()
|
||||||
|
addChildrenRef.value.close()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
});
|
addChildrenRef.value.open(childrenOptions.value);
|
||||||
};
|
}
|
||||||
|
|
||||||
|
// 添加按钮
|
||||||
|
const onAddButton = (row:any)=>{
|
||||||
|
const childrenOptions = ref();
|
||||||
|
childrenOptions.value = {...crudBinding.value.addForm};
|
||||||
|
childrenOptions.value.columns.parent_name.show = true
|
||||||
|
childrenOptions.value.columns.icon.show = false
|
||||||
|
childrenOptions.value.columns.web_path.show = false
|
||||||
|
childrenOptions.value.columns.is_link.show = false
|
||||||
|
childrenOptions.value.columns.cache.show = false
|
||||||
|
childrenOptions.value.columns.web_path.show = false
|
||||||
|
childrenOptions.value.columns.visible.show = false
|
||||||
|
childrenOptions.value.columns.frame_out.show = false
|
||||||
|
childrenOptions.value.columns.sort.show = false
|
||||||
|
childrenOptions.value.columns.status.show = false
|
||||||
|
childrenOptions.value.columns.component.show = true
|
||||||
|
childrenOptions.value.columns.component.title = "按钮权限值"
|
||||||
|
childrenOptions.value.initialForm = { parent_name: row.name,menu_type:2 };
|
||||||
|
//覆盖提交方法
|
||||||
|
childrenOptions.value.doSubmit=({ form })=>{
|
||||||
|
form.parent = row.id
|
||||||
|
AddObj(form).then(res=>{
|
||||||
|
ElMessage.success('添加成功')
|
||||||
|
childrenOptions.value.onSuccess()
|
||||||
|
addChildrenRef.value.close()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
addChildrenRef.value.open(childrenOptions.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 你的crud配置
|
||||||
|
const { crudOptions } = createCrudOptions({ crudExpose,onAddCatalog,onAddChildren,onAddButton });
|
||||||
|
// 初始化crud配置
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars,no-unused-vars
|
||||||
|
const { resetCrudOptions } = useCrud({ crudExpose, crudOptions });
|
||||||
|
// 你可以调用此方法,重新初始化crud配置
|
||||||
|
// resetCrudOptions(options)
|
||||||
|
|
||||||
|
// 页面打开后获取列表数据
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getData();
|
crudExpose.doRefresh();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.menu-el-row {
|
|
||||||
height: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
.el-col {
|
|
||||||
height: 100%;
|
|
||||||
padding: 10px 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-box {
|
|
||||||
height: 100%;
|
|
||||||
padding: 10px;
|
|
||||||
background-color: #fff;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-left-box {
|
|
||||||
position: relative;
|
|
||||||
border-radius: 0 8px 8px 0;
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-right-box {
|
|
||||||
border-radius: 8px 0 0 8px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
Reference in New Issue
Block a user