Commit e13571a0 authored by wanli's avatar wanli

feat(菜单管理模块): 编写菜单模块代码

parent 1fcb2f54
<!--
* @Author: your name
* @Date: 2021-07-15 09:33:39
* @LastEditTime: 2021-07-19 17:04:37
* @LastEditTime: 2021-07-28 14:40:25
* @LastEditors: Please set LastEditors
* @Description: In User Settings Edit
* @FilePath: \evm-store\tools\README.md
......@@ -21,7 +21,8 @@
- https://docs.pyfilesystem.org/en/latest/index.html
- https://jinja.palletsprojects.com/en/3.0.x/
- https://www.jb51.net/article/205786.htm
- https://blog.csdn.net/weixin_39352048/article/details/80363326
- https://www.cnblogs.com/jackadam/p/9620144.html
- https://www.cnblogs.com/estragon/p/12298128.html
# 问题
......
......@@ -59,16 +59,17 @@ def _custom_abort(http_status_code, **kwargs):
"""
自定义abort 400响应数据格式
"""
if http_status_code == 400:
message = kwargs.get('msg')
if isinstance(message, dict):
param, info = list(message.items())[0]
data = '{}:{}!'.format(param, info)
return abort(jsonify(response_result(ResponseCode.HTTP_INVAILD_REQUEST, data=data)))
else:
return abort(jsonify(response_result(ResponseCode.HTTP_INVAILD_REQUEST, data=message)))
# return { 'code': http_status_code, 'msg': kwargs.get('message') }
return abort(http_status_code)
logger.info(kwargs)
# if http_status_code == 400:
# message = kwargs.get('msg')
# if isinstance(message, dict):
# param, info = list(message.items())[0]
# data = '{}:{}!'.format(param, info)
# return abort(jsonify(response_result(ResponseCode.HTTP_INVAILD_REQUEST, data=data)))
# else:
# return abort(jsonify(response_result(ResponseCode.HTTP_INVAILD_REQUEST, data=message)))
return abort(jsonify({ 'code': http_status_code, 'msg': kwargs }))
# return abort(http_status_code)
def _access_control(response):
"""
......
'''
Author: your name
Date: 2021-06-30 17:43:46
LastEditTime: 2021-07-14 18:40:19
LastEditors: Please set LastEditors
Description: In User Settings Edit
FilePath: \evm-store\tools\build_out\application\config.py
'''
# -*- coding: utf-8 -*-
import os
import multiprocessing
......@@ -80,16 +72,16 @@ class DevelopConfig(object):
MYSQL_USER = 'debian-sys-maint'
MYSQL_PWD = 'XMigC2B2uugnv18y'
SQLALCHEMY_BINDS = {
'app-store': 'sqlite:///../test.db'
'app-store': 'sqlite:///../evue-store.db'
}
SQLALCHEMY_DATABASE_URI = 'sqlite:///../test.db'
SQLALCHEMY_DATABASE_URI = 'sqlite:///../evue-store.db'
SQLALCHEMY_TRACK_MODIFICATIONS = True
SQLALCHEMY_ECHO = False
def __init__(self):
super().__init__()
self.SQLALCHEMY_DATABASE_URI = 'sqlite:///../test.db'
self.SQLALCHEMY_DATABASE_URI = 'sqlite:///../evue-store.db'
if MODE == 'production':
config = ProductionConfig()
......
'''
Author: your name
Date: 2021-07-15 09:33:39
LastEditTime: 2021-07-27 20:11:47
LastEditors: Please set LastEditors
Description: In User Settings Edit
FilePath: \evm-store\tools\build_out\application\signal_manager.py
'''
#!/usr/bin/env python
# -*- coding: utf_8 -*-
......@@ -53,6 +45,9 @@ class SignalManager(object):
actionTreelist = PySignal()
actionTreenodes = PySignal()
actionUpdatebyid = PySignal()
actionMenuCreate = PySignal()
actionUpdateSort = PySignal()
actionMenuBatch = PySignal()
# file manager api
actionGetFileInit = PySignal()
actionGetFileContent = PySignal()
......
'''
Author: your name
Date: 2021-07-15 09:33:39
LastEditTime: 2021-07-28 17:07:05
LastEditors: your name
Description: In User Settings Edit
FilePath: \evm-store\tools\build_out\controllers\__init__.py
'''
#!/usr/bin/env python
# -*- coding: utf_8 -*-
......@@ -58,9 +66,12 @@ def initConnect():
signalManager.actionGetMonitorimage.connect(monitorImageManager.get)
signalManager.actionGetlistMonitorevm.connect(monitorEvmManager.getList)
signalManager.actionGetMonitorevm.connect(monitorEvmManager.get)
signalManager.actionTreelist.connect(menuManager.Treelist)
signalManager.actionTreenodes.connect(menuManager.Treenodes)
signalManager.actionUpdatebyid.connect(menuManager.Updatebyid)
signalManager.actionTreelist.connect(menuManager.treeList)
signalManager.actionTreenodes.connect(menuManager.treeNodes)
signalManager.actionUpdatebyid.connect(menuManager.updateById)
signalManager.actionMenuCreate.connect(menuManager.create)
signalManager.actionUpdateSort.connect(menuManager.updateSort)
signalManager.actionMenuBatch.connect(menuManager.batch)
# file manager api
signalManager.actionGetFileContent.connect(fileManager.content)
signalManager.actionGetFileDisk.connect(fileManager.disk)
......
......@@ -9,8 +9,10 @@ FilePath: \evm-store\tools\build_out\controllers\menu.py
#!/usr/bin/env python
# -*- coding: utf_8 -*-
from pprint import pprint
from datetime import datetime
from application.app import db
from models.user import UserModel
from models.menu import MenuModel
from webcreator.log import logger
from webcreator.response import ResponseCode
......@@ -19,27 +21,133 @@ class MenuResource(object):
def __init__(self):
super().__init__()
def Treelist(self, params={}, jwt={}):
def print_tree(self, group_name, tab=1):
"""
:param str group_name:要查找的树的根的名称
:param int tab: 格式化用的-数量
"""
group = db.session.query(MenuModel).filter_by(name=group_name).one_or_none()
if not group:
return
# group found - print name and find children
print('- ' * tab + group.name)
for child_group in group.children: # type: TreeGroup
# new tabulation value for child record
self.print_tree(child_group.name, tab * 2)
def batch(self, params={}, jwt={}):
# 如果id里面包含无法转换为数字的字符,那么该接口将会报错
ids = list(map(lambda x:int(x), params.get("ids").split(",")))
result = db.session.query(MenuModel).filter(MenuModel.id in ids).delete()
db.session.commit() # 这是批量删除模式
logger.info(result)
return True, ResponseCode.HTTP_SUCCESS
def updateSort(self, params={}, jwt={}):
user = db.session.query(UserModel).filter(UserModel.uuid==jwt.get("uuid")).one_or_none()
if not user:
return False, ResponseCode.USER_NOT_EXISTS
# 接口字段可能和数据库字段不一致
result = db.session.query(MenuModel).filter(MenuModel.id==params.get("id")).one_or_none()
if result:
sort = 1
if params.get("direction") == "bottom":
sort = -1
result.update_at = datetime.now()
result.update_by = user.id
result.sort = result.sort + sort
db.session.commit()
return True, ResponseCode.HTTP_SUCCESS
else:
return False, ResponseCode.HTTP_NOT_FOUND
def create(self, params={}, jwt={}):
user = db.session.query(UserModel).filter(UserModel.uuid==jwt.get("uuid")).one_or_none()
if not user:
return False, ResponseCode.USER_NOT_EXISTS
# 接口字段可能和数据库字段不一致
extends = MenuModel.fields_mapping()
data = dict(is_delete=False, sort=0, remarks="")
data.update({
'create_by': user.id,
'create_at': datetime.niw(),
'update_by': user.id,
'update_at': datetime.now()
})
for k in params.keys():
if k in extends:
k[extends[k]] = params[k]
continue
if hasattr(MenuModel, k):
data[k] = params[k]
if data.get("sort") == 0:
data.sort = db.session.query(MenuModel).filter().count() + 1
# 默认设置sort等于表内记录个数
logger.info(data.sort)
result = MenuModel(**data)
db.session.add(result)
db.session.commit()
return True, ResponseCode.HTTP_SUCCESS
def treeList(self, params={}, jwt={}):
filters = [MenuModel.is_delete==False]
result = MenuModel.query.filter(*filters).all()
result = db.session.query(MenuModel).filter(*filters).order_by(MenuModel.sort).order_by(MenuModel.update_at.desc()).all()
data = []
if result:
data = []
print(result)
tab = 1
for item in result:
t = item.dict()
data.append(t)
return result, ResponseCode.HTTP_SUCCESS
return None, ResponseCode.HTTP_NOT_FOUND
tmp = item.get_tree(session=db.session)
# print(tmp, tmp.get("node"))
for tt in tmp:
logger.info(tt)
logger.info(tt.get("node"))
# pprint(item.get_tree(session=db.session, json=True))
pprint(item.get_children(session=db.session))
# for child_group in i.children: # type: TreeGroup
# # new tabulation value for child record
# self.print_tree(child_group.name, tab * 2)
# for item in result:
# t = item.dict()
# data.append(t)
# return result, ResponseCode.HTTP_SUCCESS
return data, ResponseCode.HTTP_NOT_FOUND
def Treenodes(self, params={}, jwt={}):
filters = [MenuModel.is_delete==False]
result = MenuModel.query.filter(*filters).first()
def treeNodes(self, params={}, jwt={}):
filters = [MenuModel.is_delete==False, MenuModel.parent_id==0]
result = db.session.query(MenuModel).filter(*filters).order_by(MenuModel.sort).order_by(MenuModel.update_at.desc()).all()
logger.info(result)
if result:
return result, ResponseCode.HTTP_SUCCESS
return None, ResponseCode.HTTP_NOT_FOUND
def Updatebyid(self, params={}, jwt={}):
filters = [MenuModel.is_delete==False]
result = MenuModel.query.filter(*filters).first()
def updateById(self, params={}, jwt={}):
user = db.session.query(UserModel).filter(UserModel.uuid==jwt.get("uuid")).one_or_none()
if not user:
return False, ResponseCode.USER_NOT_EXISTS
# 接口字段可能和数据库字段不一致
extends = MenuModel.fields_mapping()
data = dict(is_delete=False)
data.update({
'update_at': datetime.now(),
'update_by': user.id
})
for k in params.keys():
if k in extends:
k[extends[k]] = params[k]
continue
if hasattr(MenuModel, k):
data[k] = params[k]
logger.info(data)
# filters = [MenuModel.is_delete==False]
# result = MenuModel.query.filter(*filters).first()
result = MenuModel.query.filter(MenuModel.id==params.get("id")).update(data, synchronize_session=False)
db.session.commit()
logger.info(result)
if result:
return result, ResponseCode.HTTP_SUCCESS
return None, ResponseCode.HTTP_NOT_FOUND
......
'''
Author: your name
Date: 2021-07-15 09:33:39
LastEditTime: 2021-07-28 15:29:13
LastEditors: your name
Description: In User Settings Edit
FilePath: \evm-store\tools\build_out\models\__init__.py
'''
'''
Author: your name
Date: 2021-04-22 18:04:10
LastEditTime: 2021-07-27 20:04:43
LastEditors: Please set LastEditors
......@@ -19,22 +27,12 @@ class BaseModelMixin(object):
return self.__name__.lower()
# __table_args__ = {'mysql_engine': 'InnoDB'}
__mapper_args__= {'always_refresh': True}
id = Column(Integer, primary_key=True)
BaseModel = declarative_base(cls=BaseModelMixin)
class MyMixin(object):
@declared_attr
def __tablename__(self):
return self.__name__.lower()
# __table_args__ = {'mysql_engine': 'InnoDB'}
__mapper_args__= {'always_refresh': True}
id = Column(Integer, primary_key=True)
class MyModel(MyMixin, BaseModel):
class MyModel(BaseModel, BaseModelMixin):
__tablename__ = 'evm_store_app_logs'
app_name = Column(String(20))
app_path = Column(String(32))
......@@ -43,7 +41,7 @@ class MyModel(MyMixin, BaseModel):
create_at = Column(String(32))
create_by = Column(Integer())
class MyModel2(MyMixin, BaseModel):
class MyModel2(BaseModel, BaseModelMixin):
__tablename__ = 'monitor_request'
host = Column(String(32))
path = Column(String(32))
......
......@@ -9,27 +9,48 @@ FilePath: \evm-store\tools\build_out\models\menu.py
#!/usr/bin/env python
# -*- coding: utf_8 -*-
import copy
from application.app import db, ma
from .base import PrimaryModel
from marshmallow import Schema, fields, INCLUDE, EXCLUDE
from datetime import datetime
from application.app import db
from .base import PrimaryModel
# from sqlalchemy.orm import relationship
from sqlalchemy_mptt.mixins import BaseNestedSets
class MenuModel(PrimaryModel):
class MenuModel(PrimaryModel, BaseNestedSets):
__tablename__ = 'evm_menu'
id = db.Column(db.Integer, primary_key=True)
visible = db.Column(db.Boolean)
name = db.Column(db.String(100), nullable = True)
parent_id = db.Column(db.Integer, default=0, nullable = True)
# parent_id = db.Column(db.Integer, db.ForeignKey("evm_menu.id"), default=0, nullable = True) # 这句话会由mptt自动插入进去
path = db.Column(db.String(100), nullable = True)
disabled = db.Column(db.Boolean, nullable = True, default = False)
fixed = db.Column(db.Boolean, nullable = False, default = False)
has_children = db.Column(db.Boolean, nullable = True, default = False)
icon = db.Column(db.String(100), nullable = True, default = '')
# __table_args__ = (
# db.Index('idx_xxx', 'xxx', mysql_using='btree'),
# )
def __init__(self, name, parentId, path, disabled=False, fixed=False, hasChildren=False, icon=''):
# create_at = db.Column(db.DateTime, default=datetime.now) # 下面这些被注释的字段,都已经从PrimaryModel继承过来了,无需重写
# create_by = db.Column(db.String(64))
# update_at = db.Column(db.DateTime, default=datetime.now)
# update_by = db.Column(db.String(64))
# remarks = db.Column(db.String(255), default="")
# sort = db.Column(db.Integer, default=0)
# is_delete = db.Column(db.BOOLEAN, default=0)
# parent = db.Column(db.Integer, db.ForeignKey('evm_menu.id'), index=True)
# 这句话的含义请百度sqlalchemy表自关联,https://www.jianshu.com/p/fa611ab55dd2
parent = db.relationship("MenuModel", remote_side=[id], backref="ppp")
# sqlalchemy_mptt 调优
# sqlalchemy_mptt 在混入ORM对象时,只对rgt, lft, level 设置了索引,导致每次 CRUD操作都需要从头到尾查找。
# 经测试,添加tree_id索引大约能快4倍,添加parent_id大约快100倍,加起来就能够满足快速插入的需求了
# https://www.cnblogs.com/estragon/p/12298128.html
__table_args__ = (
db.Index('right_idx', 'rgt'),
db.Index('left_idx', 'lft'),
db.Index('level_idx', 'level'),
db.Index('treeid_idx', 'tree_id'),
db.Index('parent_idx', 'parent_id'),
)
def __init__(self, name=None, parentId=None, path="", disabled=False, fixed=False, hasChildren=False, icon='', is_delete=False, sort=0, remarks=""):
self.name = name
self.parent_id = parentId
self.path = path
......@@ -37,9 +58,19 @@ class MenuModel(PrimaryModel):
self.fixed = fixed
self.has_children = hasChildren
self.icon = icon
self.is_delete = is_delete
self.sort = sort
self.remarks = remarks
def __repr__(self):
return '<MenuModel %r>' % (self.name)
return '<MenuModel %r>' % (self.id)
@classmethod
def fields_mapping(self):
return {
'parendId': 'parent_id',
'hasChildren': 'has_children'
}
def to_dict(self):
return {
......@@ -58,73 +89,4 @@ class MenuModel(PrimaryModel):
'updateUser': self.update_by,
'createTime': self.create_at.strftime("%Y-%m-%d %H:%M:%S") if self.create_at else None,
"updateTime": self.update_at.strftime("%Y-%m-%d %H:%M:%S") if self.update_at else None,
}
class TreeMenuNode:
"""树形菜单节点"""
def __init__(self, pk, title, code, icon_class, url, order, child, parent):
self.pk = pk
self.title = title
self.code = code
self.icon_class = icon_class
self.url = url
self.order = order
self.child = child
self.parent = parent
def count(self):
if not self.child:
return 0
return len(self.child)
class HandleTreeMenu:
"""处理树形侧边栏菜单"""
def __init__(self, menus):
self.menus = menus
self.tree_menu = []
def __iter__(self):
if self.tree_menu:
return iter(self.tree_menu)
return iter([])
@staticmethod
def get_complete_menu_structure(_menu_list):
"""得到完整的菜单结构"""
menu_list = copy.copy(_menu_list)
for menu in menu_list:
if menu.parent and menu.parent not in menu_list:
menu_list.append(menu.parent)
menu_list.extend(HandleTreeMenu.get_complete_menu_structure(menu_list))
return menu_list
def convert_to_tree(self):
"""转换BaseQuery为MenuTree结构"""
complete_menu = HandleTreeMenu.get_complete_menu_structure(self.menus)
# 所有顶级菜单
top_menu = [menu for menu in complete_menu if not menu.parent]
for menu in top_menu:
current_menu = TreeMenuNode(menu.pk, menu.title, menu.code, menu.icon_class, menu.url, menu.order, [], None)
current_menu = self._recursive_menu(menu, current_menu)
self.tree_menu.append(current_menu)
def _recursive_menu(self, base_menu, base_tree_menu):
"""由上往下递归菜单得到所有菜单并转换为TreeMenuNode格式"""
complete_menu = HandleTreeMenu.get_complete_menu_structure(self.menus)
child_menu = [menu for menu in complete_menu if menu.parent == base_menu] # 父节点为base_menu的子节点
for menu in child_menu:
current_menu = TreeMenuNode(menu.pk, menu.title, menu.code, menu.icon_class, menu.url, menu.order, [], base_tree_menu)
current_menu = self._recursive_menu(menu, current_menu)
base_tree_menu.child.append(current_menu)
return base_tree_menu
if __name__ == '__main__':
# 使用方法
menus = MenuModel.query.all() # 这里是直接在数据库里面查询出来的, 用户私有权限表
tree_menu = HandleTreeMenu(menus)# 创建一个对象
tree_menu.convert_to_tree() # 执行转换方法
print(tree_menu.tree_menu) # 得到已经转换的菜单
}
\ No newline at end of file
'''
Author: your name
Date: 2021-07-27 19:08:58
LastEditTime: 2021-07-28 16:15:39
LastEditors: your name
Description: In User Settings Edit
FilePath: \evm-store\tools\build_out\views\__init__.py
'''
# -*- coding: utf-8 -*-
from flask import Blueprint
......@@ -57,9 +65,12 @@ api.add_resource(monitorImage.MonitorImageResourceList, '/monitorImage')
api.add_resource(monitorEvm.MonitorEvmResource, '/monitorEvm/<string:uuid>')
api.add_resource(monitorEvm.MonitorEvmResourceList, '/monitorEvm')
api.add_resource(menu.Treelist, '/api/system/menu/treeList')
api.add_resource(menu.Treenodes, '/api/system/menu/treeNodes')
api.add_resource(menu.Updatebyid, '/api/system/menu/updateById')
api.add_resource(menu.Batch, '/menu/delete/batch')
api.add_resource(menu.Create, '/menu/create')
api.add_resource(menu.Treelist, '/menu/treeList')
api.add_resource(menu.Treenodes, '/menu/treeNodes')
api.add_resource(menu.Updatebyid, '/menu/updateById')
api.add_resource(menu.UpdateSort, '/menu/updateSort')
api.add_resource(openapi.AppReviewResource, '/api/app-review')
api.add_resource(openapi.CStringToolResource, '/api/convert-to-c-string')
......
'''
Author: your name
Date: 2021-07-15 09:33:39
LastEditTime: 2021-07-28 17:16:06
LastEditors: your name
Description: In User Settings Edit
FilePath: \evm-store\tools\build_out\views\menu.py
'''
#!/usr/bin/env python
# -*- coding: utf_8 -*-
import traceback
from flask import current_app, jsonify, request
from flask_restful import Resource
from flask_restful.reqparse import RequestParser
......@@ -9,6 +18,59 @@ from application.signal_manager import signalManager
from webcreator.log import logger
from webcreator.response import ResponseCode, response_result
class Batch(Resource):
def __init__(self):
self.parser = RequestParser()
def post(self):
self.parser.add_argument("ids", type=str, location="args", required=True)
args = self.parser.parse_args()
try:
print("========>", args)
result, message = signalManager.actionMenuBatch.emit(args)
return response_result(message, data=result)
except Exception as e:
current_app.logger.error(e)
return response_result(ResponseCode.HTTP_SERVER_ERROR)
class UpdateSort(Resource):
def __init__(self):
self.parser = RequestParser()
def post(self):
self.parser.add_argument("id", type=int, location="json", required=True)
self.parser.add_argument("direction", type=str, choices=["top", "bottom"], location="json", required=True)
args = self.parser.parse_args()
try:
print("========>", args)
result, message = signalManager.actionUpdateSort.emit(args)
return response_result(message, data=result)
except Exception as e:
current_app.logger.error(e)
return response_result(ResponseCode.HTTP_SERVER_ERROR)
class Create(Resource):
def __init__(self):
self.parser = RequestParser()
def post(self):
self.parser.add_argument("parentId", type=int, location="json", default=None, required=False)
self.parser.add_argument("icon", type=str, location="json", required=True)
self.parser.add_argument("path", type=str, location="json", required=True)
self.parser.add_argument("name", type=str, location="json", required=True)
self.parser.add_argument("remarks", type=str, location="json", required=True)
args = self.parser.parse_args()
try:
print("========>", args)
result, message = signalManager.actionMenuCreate.emit(args)
return response_result(message, data=result)
except Exception as e:
# traceback.print_exc()
current_app.logger.error(e)
return response_result(ResponseCode.HTTP_SERVER_ERROR)
class Treelist(Resource):
def __init__(self):
......
......@@ -1837,7 +1837,7 @@
},
{
"name": "menu",
"enable": true,
"enable": false,
"controller": {
"className": "Resource"
},
......@@ -1906,7 +1906,7 @@
{
"name": "treeList",
"auth": true,
"path": "/api/system/menu/treeList",
"path": "/menu/treeList",
"method": "post",
"params": [
{
......@@ -1928,7 +1928,7 @@
{
"name": "treeNodes",
"auth": true,
"path": "/api/system/menu/treeNodes",
"path": "/menu/treeNodes",
"method": "get",
"params": [
{
......@@ -1950,7 +1950,7 @@
{
"name": "UpdateById",
"auth": true,
"path": "/api/system/menu/updateById",
"path": "/menu/updateById",
"method": "post",
"params": [
{
......
alembic==1.6.5
aniso8601==9.0.1
appdirs==1.4.4
asgiref==3.3.4
cffi==1.14.5
click==8.0.1
colorama==0.4.4
Flask==2.0.1
Flask-JWT-Extended==4.2.1
flask-marshmallow==0.14.0
Flask-Migrate==3.0.1
Flask-RESTful==0.3.9
Flask-Script==2.0.6
Flask-SQLAlchemy==2.5.1
fs==2.4.13
gevent==21.1.2
greenlet==1.1.0
gunicorn==20.1.0
h11==0.12.0
hashids==1.3.1
itsdangerous==2.0.1
Jinja2==3.0.1
Mako==1.1.4
MarkupSafe==2.0.1
marshmallow==3.12.1
marshmallow-sqlalchemy==0.26.1
pycparser==2.20
PyJWT==2.1.0
python-dateutil==2.8.1
python-editor==1.0.4
pytz==2021.1
six==1.16.0
SQLAlchemy==1.4.17
sqlalchemy-mptt==0.2.5
tornado==6.1
uvicorn==0.14.0
Werkzeug==2.0.1
zope.event==4.5.0
zope.interface==5.4.0
......@@ -13,7 +13,7 @@ class {{ config['name'] | capitalize }}Resource(object):
{% if "restful" in config.get("view") and config.get("view").get("restful") == False %}
{%- for item in config.get("view").get("routes", []) %}
def {{ item.get("name") | title }}(self, params={}, jwt={}):
def {{ item.get("name") }}(self, params={}, jwt={}):
filters = [{{ config['name'] | capitalize }}Model.is_delete==False]
result = {{ config['name'] | capitalize }}Model.query.filter(*filters).first()
if result:
......
......@@ -10,7 +10,7 @@ def initConnect():
{%- for api in config %}
{%- if "restful" in api.get("view") and api.get("view").get("restful") == False %}
{%- for r in api.get("view").get("routes", []) %}
signalManager.action{{ r.get("name") | capitalize }}.connect({{ api.get("name") }}Manager.{{ r.get("name") | capitalize }})
signalManager.action{{ r.get("name") | capitalize }}.connect({{ api.get("name") }}Manager.{{ r.get("name") }})
{%- endfor %}
{%- else %}
{%- for key, value in api.get("view").items() %}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment