diff --git a/api/v1/pages/app/__init__.py b/api/v1/pages/app/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/api/v1/pages/desktop/app_market.py b/api/v1/pages/desktop/app_market.py index 129b6b56b..538e00661 100644 --- a/api/v1/pages/desktop/app_market.py +++ b/api/v1/pages/desktop/app_market.py @@ -5,10 +5,10 @@ router = routers.FrontRouter( - path=app_market_tag, + path="", name=app_market_name, icon='app', - page=app_market_tag, + page=[app_market_tag], ) diff --git a/api/v1/pages/user/__init__.py b/api/v1/pages/user/__init__.py index fd183e8bc..cab092b2b 100644 --- a/api/v1/pages/user/__init__.py +++ b/api/v1/pages/user/__init__.py @@ -9,4 +9,6 @@ children=[ user_list.router, ], -) \ No newline at end of file +) + +# router.change_page_tag('core') \ No newline at end of file diff --git a/api/v1/pages/user/user_list.py b/api/v1/pages/user/user_list.py index c649a5d89..d905aaf87 100644 --- a/api/v1/pages/user/user_list.py +++ b/api/v1/pages/user/user_list.py @@ -5,14 +5,6 @@ user_list_name = '用户列表' -router = routers.FrontRouter( - path=user_list_tag, - name='用户管理', - icon='user', - page=user_list_tag, -) - - page = pages.FrontPage( tag=user_list_tag, name=user_list_name, @@ -113,6 +105,14 @@ ] ) + +router = routers.FrontRouter( + path=user_list_tag, + name='用户管理', + icon='user', + page=page, +) + pages.register_front_pages(page) pages.register_front_pages(user_create_page) -pages.register_front_pages(user_edit_page) +pages.register_front_pages(user_edit_page) \ No newline at end of file diff --git a/arkid/core/extension/__init__.py b/arkid/core/extension/__init__.py index c8a1ac53a..ad71fbfea 100644 --- a/arkid/core/extension/__init__.py +++ b/arkid/core/extension/__init__.py @@ -268,7 +268,8 @@ def register_front_routers(self, router, primary=''): self.front_routers.append((router, primary)) def register_front_pages(self, page): - page.tag = self.package + '_' + page.tag + page:core_page.FrontPage + page.add_tag_pre(self.package) core_page.register_front_pages(page) self.front_pages.append(page) diff --git a/arkid/core/openapi.py b/arkid/core/openapi.py index 157a9fc01..33e4a8557 100644 --- a/arkid/core/openapi.py +++ b/arkid/core/openapi.py @@ -9,7 +9,7 @@ def get_openapi_schema(self, path_prefix: Optional[str] = None) -> OpenAPISchema path_prefix = self.root_path schema = get_schema(api=self, path_prefix=path_prefix) schema["routers"] = routers.global_routers - schema["pages"] = pages.global_pages + schema["pages"] = pages.get_global_pages() # permissions = get_permissions(self) # schema["permissions"] = permissions diff --git a/arkid/core/pages.py b/arkid/core/pages.py index 275f9f695..190ca06ee 100644 --- a/arkid/core/pages.py +++ b/arkid/core/pages.py @@ -2,20 +2,21 @@ from collections import OrderedDict from uuid import uuid4 from enum import Enum -global_pages = [] - +global_pages = {} class FrontPageType(Enum): """前端页面类型枚举类 Type页面类型 [可扩展]: - >>> 表格型页面 (table) - >>> 表单型页面 (form) - >>> 描述型页面 (description) - >>> 树状型页面 (tree) - >>> 切换型页面 (tabs)- 暂不支持 - >>> 列表型页面 (list)- 暂不支持 - >>> 卡片型页面 (cards)- 暂不支持 - >>> 网格型页面 (grid)- 暂不支持 + ``` + - 表格型页面 (table) + - 表单型页面 (form) + - 描述型页面 (description) + - 树状型页面 (tree) + - 切换型页面 (tabs)- 暂不支持 + - 列表型页面 (list)- 暂不支持 + - 卡片型页面 (cards)- 暂不支持 + - 网格型页面 (grid)- 暂不支持 + ``` """ FORM_PAGE = 'form' TABLE_PAGE = 'table' @@ -26,14 +27,14 @@ class FrontPageType(Enum): CARDS_PAGE = 'cards' GRID_PAGE = 'grid' -global_tags = [] +global_tags = [] # 全局tag列表 def gen_tag(tag:str=None,tag_pre:str=None) -> str: """ 为页面或者行为生成tag Args: - tag (str, optional): tag字符串,可指定亦可动态生成. Defaults to None. - tag_pre (str, optional): tag前缀,一般可为插件名称或者其他. Defaults to None. + tag (str, optional): tag字符串,可指定亦可动态生成. + tag_pre (str, optional): tag前缀,一般可为插件名称或者其他. Returns: str: tag字符串 @@ -46,6 +47,44 @@ def gen_tag(tag:str=None,tag_pre:str=None) -> str: class FrontPage(OrderedDict): """ 前端页面配置类 + + Examples: + >>> from arkid.core import pages + >>> from arkid.core.translation import gettext_default as _ + >>> + >>> # 申明一个页面 + >>> page = pages.FrontPage( + >>> tag="user_list", + >>> name="user_list", + >>> page_type=pages.FrontPageType.TABLE_PAGE, + >>> init_action=pages.FrontAction( + >>> path='/api/v1/tenant/{tenant_id}/users/', + >>> method=pages.FrontActionMethod.GET + >>> ) + >>> ) + >>> # 添加局部动作 + >>> page.add_local_action( + >>> [ + >>> pages.FrontAction( + >>> name=_("编辑"), + >>> page=user_edit_page, + >>> icon="icon-edit", + >>> action_type=pages.FrontActionType.OPEN_ACTION + >>> ), + >>> ... + >>> ] + >>> ) + >>> # 添加全局动作 + >>> page.add_global_action( + >>> [ + >>> pages.FrontAction( + >>> name="创建", + >>> page=user_create_page, + >>> icon="icon-create", + >>> action_type=pages.FrontActionType.OPEN_ACTION + >>> ) + >>> ] + >>> ) """ def __init__(self, name:str, page_type:FrontPageType, init_action, tag:str=None, tag_pre:str=None, *args, **kwargs): """初始化函数 @@ -54,8 +93,8 @@ def __init__(self, name:str, page_type:FrontPageType, init_action, tag:str=None, name (str): 页面名称 page_type (FrontPageType): 页面类型 init_action (FrontAction|OrderedDict): 初始化动作 - tag (str, optional): 标识. Defaults to None. - tag_pre (str, optional): 标识前缀. Defaults to None. + tag (str, optional): 标识. + tag_pre (str, optional): 标识前缀. """ self["tag"] = gen_tag(tag,tag_pre) self["name"] = name @@ -97,21 +136,33 @@ def add_node_action(self, actions): actions = list(actions) if not self.get('node'): self['node'] = [] - self['node'].extend(actions) + self['node'].extend(actions) + + def add_tag_pre(self,tag_pre:str): + """添加标识前缀 + + 用于插件中生成页面时给页面的标识添加前缀 + + Args: + tag_pre (str): 前缀 + """ + self["tag"] = gen_tag(self["tag"],tag_pre) + class FrontActionType(Enum): """前端动作类型枚举类 ActionType操作类型 [可扩展]: - - >>> direct 直接操作类型 - >>> open 打开新页面类型 - >>> cancel 取消操作类型 - >>> reset 重置表单类型 - >>> import 导入数据类型 - >>> node 节点点击类型(页面中将隐藏该操作) - >>> url 内外链接类型 - >>> password 编辑密码类型 + ``` + - direct 直接操作类型 + - open 打开新页面类型 + - cancel 取消操作类型 + - reset 重置表单类型 + - import 导入数据类型 + - node 节点点击类型(页面中将隐藏该操作) + - url 内外链接类型 + - password 编辑密码类型 + ``` """ DIRECT_ACTION = 'direct' @@ -124,14 +175,16 @@ class FrontActionType(Enum): PASSWORD_ACTION = 'password' class FrontActionMethod(Enum): - """前端动作类型枚举类 + """ 前端动作类型枚举类 ActionMethod 动作方法 [可扩展]: - >>> get - >>> post - >>> put - >>> delete + ``` + - get + - post + - put + - delete + ``` """ GET = 'get' @@ -141,21 +194,39 @@ class FrontActionMethod(Enum): class FrontAction(OrderedDict): - """前端页面动作类 + """ 前端页面动作类 + + Examples: + >>> from arkid.core import pages + >>> from arkid.core.translation import gettext_default as _ + >>> + >>> edit_action = pages.FrontAction( + >>> name=_("编辑"), + >>> page=user_edit_page, + >>> icon="icon-edit", + >>> action_type=pages.FrontActionType.OPEN_ACTION + >>> ) + >>> delete_action = pages.FrontAction( + >>> name=_("删除"), + >>> method=pages.FrontActionMethod.DELETE, + >>> path="/api/v1/tenant/{tenant_id}/users/{id}/", + >>> icon="icon-delete", + >>> action_type=pages.FrontActionType.DIRECT_ACTION + >>> ) """ def __init__(self, tag:str=None, action_type:FrontActionType=None,name:str=None, page=None, path:str=None, method:FrontActionMethod=None, icon:str=None,tag_pre:str=None, *args, **kwargs): """初始化函数 Args: - tag (str, optional): 标识. Defaults to None. - action_type (FrontActionType, optional): 动作类型. Defaults to None. - name (str, optional): 名称. Defaults to None. - page (FrontPage|str, optional): 指向页面,此处存储页面的标识. Defaults to None. - path (str, optional): 请求路径. Defaults to None. - method (FrontActionMethod, optional): 请求方法. Defaults to None. - icon (str, optional): 图标名称. Defaults to None. - tag_pre (str, optional): 标识前缀. Defaults to None. + tag (str, optional): 标识. + action_type (FrontActionType, optional): 动作类型. + name (str, optional): 名称. + page (FrontPage|str, optional): 指向页面,此处存储页面的标识. + path (str, optional): 请求路径. + method (FrontActionMethod, optional): 请求方法. + icon (str, optional): 图标名称. + tag_pre (str, optional): 标识前缀. """ self["tag"] = gen_tag(tag,tag_pre) @@ -175,6 +246,13 @@ def __init__(self, tag:str=None, action_type:FrontActionType=None,name:str=None, self["type"] = action_type.value super().__init__(*args, **kwargs) + def add_tag_pre(self,tag_pre:str): + """ 添加标识前缀 + + Args: + tag_pre (str): 标识前缀 + """ + self["tag"] = gen_tag(self["tag"],tag_pre) def register_front_pages(pages): """注册前端页面 @@ -184,7 +262,9 @@ def register_front_pages(pages): """ if not isinstance(pages, tuple) or not isinstance(pages, list): pages = [pages] - global_pages.extend(pages) + + for page in pages: + global_pages[page["tag"]] = page def unregister_front_pages(pages): @@ -197,4 +277,9 @@ def unregister_front_pages(pages): pages = [pages] for page in pages: - global_pages.remove(page) \ No newline at end of file + global_pages.pop(page["tag"]) + +def get_global_pages(): + """获取页面列表 + """ + return list(global_pages.values()) \ No newline at end of file diff --git a/arkid/core/routers.py b/arkid/core/routers.py index 73fdfd029..10feb9d39 100644 --- a/arkid/core/routers.py +++ b/arkid/core/routers.py @@ -1,11 +1,41 @@ from collections import OrderedDict - +from arkid.core.pages import FrontPage global_routers = [] class FrontRouter(OrderedDict): - def __init__(self, path, name=None, icon=None, children=None, redirect=None, page=None, url=None, *args, **kwargs): + """_前端路由类 + + Examples: + >>> from arkid.core import routers + >>> + >>> router = routers.FrontRouter( + >>> path='user', + >>> name='用户管理', + >>> icon='user', + >>> children=[ + >>> routers.FrontRouter( + >>> path=user_list_tag, + >>> name='用户管理', + >>> icon='user', + >>> page=user_list_tag, + >>> ) + >>> ], + >>> ) + """ + def __init__(self, path:str, name:str=None, icon:str=None, children=None, redirect=None, page=None, url=None, *args, **kwargs): + """初始化 + + Args: + path (str): 路由路径 + name (str, optional): 路由名称. Defaults to None. + icon (str, optional): 图标. Defaults to None. + children (List, optional): 子路由列表. Defaults to None. + redirect (str, optional): 跳转链接. Defaults to None. + page (FrontPage, optional): 页面. Defaults to None. + url (str, optional): 链接地址. Defaults to None. + """ self['path'] = path if name: self['name'] = name @@ -16,30 +46,54 @@ def __init__(self, path, name=None, icon=None, children=None, redirect=None, pag if redirect: self['redirect'] = redirect if page: - self['page'] = page + self['page'] = page["tag"] if isinstance(page,FrontPage) else page if url: self['url'] = url super().__init__(*args, **kwargs) def add_child(self, child): + """添加子路由 + + Args: + child (OrderedDict): 子路由描述 + """ if not self["children"]: self["children"] = [] self["children"].append(child) def remove_child(self, child): + """移除子路由 + + Args: + child (OrderedDict): 子路由描述 + """ if not self["children"]: return self["children"].remove(child) def change_page_tag(self, header): - if self['page']: - self['page'] = header + '_' + self['page'] - if self['children']: + """更改页面标识,主要用于插件中添加标识前缀以注明该页面来源 + + 注意: 此处会将子页面标识一并更改 + + Args: + header (str): 页面标识前缀 + """ + if hasattr(self,"page"): + self['page']:FrontPage + self['page'].add_tag_pre(header) + if hasattr(self,"children"): for child in self['children']: child.change_page_tag(header) def register_front_routers(routers, primary: str = ''): + """注册前端路由 + + Args: + routers (list): 路由列表或者路由 + primary (str, optional): 主路由. Defaults to ''. + """ if not isinstance(routers, tuple) or not isinstance(routers, list): routers = list(routers) @@ -53,6 +107,12 @@ def register_front_routers(routers, primary: str = ''): def unregister_front_routers(routers, primary: str = ''): + """卸载前端路由 + + Args: + routers (list): 路由列表或路由 + primary (str, optional): 主路由. Defaults to ''. + """ if not isinstance(routers, tuple) or not isinstance(routers, list): routers = list(routers) diff --git "a/docs/ \345\274\200\345\217\221\350\200\205\346\214\207\345\215\227/ \346\217\222\344\273\266\346\214\207\345\215\227/\345\211\215\347\253\257\347\225\214\351\235\242.md" "b/docs/ \345\274\200\345\217\221\350\200\205\346\214\207\345\215\227/ \346\217\222\344\273\266\346\214\207\345\215\227/\345\211\215\347\253\257\347\225\214\351\235\242.md" index baab898cb..1ae830dc1 100644 --- "a/docs/ \345\274\200\345\217\221\350\200\205\346\214\207\345\215\227/ \346\217\222\344\273\266\346\214\207\345\215\227/\345\211\215\347\253\257\347\225\214\351\235\242.md" +++ "b/docs/ \345\274\200\345\217\221\350\200\205\346\214\207\345\215\227/ \346\217\222\344\273\266\346\214\207\345\215\227/\345\211\215\347\253\257\347\225\214\351\235\242.md" @@ -255,3 +255,123 @@ ActionType操作类型 [可扩展] 1. 通过`pages`选项, 读取 init/global/local 等信息, 根据里面的`path`和`method`去`paths`中匹配相应内容 1. 在`paths`中获取到相应的`operationId`/`parameters`/`responses`/`requestBody`等信息, 继续匹配`components`中的相应内容 1. 根据`components`中的描述生成页面结构, 并根据配置中的路径获取数据和完成增删改查等操作 + +#### **后端配置说明** + +根据上述配置信息结构说明,我们需要在后端接口(/api/v1/openapi.json)中对应生成页面配置数据以供前端解析,以下是基础的代码步骤: + +1. 创建一个page + +``` python + +from arkid.core import pages + +user_list_tag = 'user_list' +user_list_name = '用户列表' + + +page = pages.FrontPage( + tag=user_list_tag, + name=user_list_name, + page_type=pages.FrontPageType.TABLE_PAGE, + init_action=pages.FrontAction( + path='/api/v1/tenant/{tenant_id}/users/', + method=pages.FrontActionMethod.GET + ) +) + +``` + +2. 为page添加action +``` python +... + +page.add_local_action( + [ + pages.FrontAction( + name=_("删除"), + method=pages.FrontActionMethod.DELETE, + path="/api/v1/tenant/{tenant_id}/users/{id}/", + icon="icon-delete", + action_type=pages.FrontActionType.DIRECT_ACTION + ) + ] +) + +... + +``` + +3. 将page注册到全局 +``` python + +pages.register_front_pages(page) + +``` + +4. 写入路由信息 +``` python +from arkid.core import routers + +user_list_router = routers.FrontRouter( + path="user_list", + name='用户管理', + icon='user', + page=page, +) + +router = routers.FrontRouter( + path='user', + name='用户管理', + icon='user', + children=[ + user_list_router, + ], +) + +``` + +5. 最后在访问/api/v1/openapi.json接口时可得到数据为: +``` json +{ + ... + "routers": [ + { + "path": "user", + "name": "用户管理", + "icon": "user", + "children": [ + { + "path": "user_list", + "name": "用户管理", + "icon": "user", + "page": "user_list" + } + ] + } + ], + "pages": [ + { + "tag": "user_list", + "name": "用户列表", + "type": "table", + "init": { + "tag": "16058e11df284ae1a58fd1220a85e501", + "path": "/api/v1/tenant/{tenant_id}/users/", + "method": "get" + }, + "local": [ + { + "tag": "9181f711ffcb4ac5b5a793e043468595", + "name": "删除", + "path": "/api/v1/tenant/{tenant_id}/users/{id}/", + "method": "delete", + "icon": "icon-delete", + "type": "direct" + } + ], + }, + ] +} +... +``` \ No newline at end of file diff --git "a/docs/ \345\274\200\345\217\221\350\200\205\346\214\207\345\215\227/\345\217\202\350\200\203\346\226\207\346\241\243/ \345\206\205\346\240\270API.md" "b/docs/ \345\274\200\345\217\221\350\200\205\346\214\207\345\215\227/\345\217\202\350\200\203\346\226\207\346\241\243/ \345\206\205\346\240\270API.md" index fd778915a..0cb9270d2 100644 --- "a/docs/ \345\274\200\345\217\221\350\200\205\346\214\207\345\215\227/\345\217\202\350\200\203\346\226\207\346\241\243/ \345\206\205\346\240\270API.md" +++ "b/docs/ \345\274\200\345\217\221\350\200\205\346\214\207\345\215\227/\345\217\202\350\200\203\346\226\207\346\241\243/ \345\206\205\346\240\270API.md" @@ -12,7 +12,7 @@ ## 事件 ::: arkid.core.event -## 前度页面 +## 前端页面 ::: arkid.core.pages ## 前端路由