433 lines
14 KiB
Python
433 lines
14 KiB
Python
"""
|
|
应用设置节点
|
|
|
|
接收应用设置配置,展示给用户确认或编辑
|
|
包括:应用配置、Logo配置、内置主题和布局
|
|
"""
|
|
import json
|
|
import logging
|
|
from typing import Any, Dict, Optional
|
|
|
|
from ..base import BaseNode, NodeContext, NodeResult
|
|
from ..registry import NodeRegistry
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
@NodeRegistry.register
|
|
class AppSettingsNode(BaseNode):
|
|
"""
|
|
应用设置节点
|
|
|
|
接收应用设置配置,展示给用户确认或编辑
|
|
包括:应用配置、Logo配置、内置主题和布局
|
|
"""
|
|
|
|
node_type = 'app_settings'
|
|
node_name = '应用设置'
|
|
node_category = 'application'
|
|
node_icon = 'settings'
|
|
node_description = '配置应用设置,包括应用信息、Logo、主题和布局'
|
|
|
|
inputs = [
|
|
{
|
|
'name': 'app_name',
|
|
'type': 'string',
|
|
'description': '应用名称',
|
|
'required': False,
|
|
},
|
|
{
|
|
'name': 'home_path',
|
|
'type': 'string',
|
|
'description': '首页路径',
|
|
'required': False,
|
|
},
|
|
{
|
|
'name': 'logo_source',
|
|
'type': 'string',
|
|
'description': 'Logo图片URL或文件ID',
|
|
'required': False,
|
|
},
|
|
{
|
|
'name': 'theme_builtin_type',
|
|
'type': 'string',
|
|
'description': '内置主题类型',
|
|
'required': False,
|
|
},
|
|
{
|
|
'name': 'theme_color_primary',
|
|
'type': 'string',
|
|
'description': '主题主色调',
|
|
'required': False,
|
|
},
|
|
{
|
|
'name': 'app_layout',
|
|
'type': 'string',
|
|
'description': '应用布局类型',
|
|
'required': False,
|
|
},
|
|
]
|
|
|
|
outputs = [
|
|
{
|
|
'name': 'settings',
|
|
'type': 'object',
|
|
'description': '应用设置配置',
|
|
},
|
|
{
|
|
'name': 'confirmed',
|
|
'type': 'boolean',
|
|
'description': '是否已确认',
|
|
},
|
|
]
|
|
|
|
# 默认配置
|
|
DEFAULT_SETTINGS = {
|
|
'app': {
|
|
'name': '',
|
|
'locale': 'zh-CN',
|
|
'dynamicTitle': True,
|
|
'watermark': False,
|
|
'watermarkContent': '',
|
|
'enableCheckUpdates': True,
|
|
'defaultHomePath': '/analytics',
|
|
'enablePreferences': True,
|
|
},
|
|
'logo': {
|
|
'enable': True,
|
|
'source': '',
|
|
'fit': 'contain',
|
|
},
|
|
'theme': {
|
|
'colorPrimary': 'hsl(212 100% 45%)',
|
|
'builtinType': 'default',
|
|
'mode': 'light',
|
|
'radius': '0.5',
|
|
'semiDarkSidebar': False,
|
|
'semiDarkHeader': False,
|
|
},
|
|
'sidebar': {
|
|
'enable': True,
|
|
'width': 230,
|
|
'collapsed': False,
|
|
'collapsedShowTitle': False,
|
|
'autoActivateChild': False,
|
|
'expandOnHover': True,
|
|
'collapsedButton': True,
|
|
'fixedButton': True,
|
|
},
|
|
'header': {
|
|
'enable': True,
|
|
'mode': 'fixed',
|
|
'menuAlign': 'start',
|
|
},
|
|
'footer': {
|
|
'enable': True,
|
|
'fixed': True,
|
|
},
|
|
'copyright': {
|
|
'enable': True,
|
|
'companyName': '',
|
|
'companySiteLink': '',
|
|
'date': '',
|
|
'icp': '',
|
|
'icpLink': '',
|
|
},
|
|
'navigation': {
|
|
'styleType': 'rounded',
|
|
'split': True,
|
|
'accordion': True,
|
|
},
|
|
'tabbar': {
|
|
'enable': True,
|
|
'showIcon': True,
|
|
'showMore': True,
|
|
'showMaximize': True,
|
|
'persist': True,
|
|
'draggable': True,
|
|
'wheelable': True,
|
|
'styleType': 'chrome',
|
|
'maxCount': 0,
|
|
'middleClickToClose': True,
|
|
},
|
|
'breadcrumb': {
|
|
'enable': True,
|
|
'showIcon': True,
|
|
'showHome': True,
|
|
'styleType': 'normal',
|
|
'hideOnlyOne': False,
|
|
},
|
|
'transition': {
|
|
'progress': True,
|
|
'name': 'fade-slide',
|
|
'loading': True,
|
|
'enable': True,
|
|
},
|
|
'widget': {
|
|
'globalSearch': True,
|
|
'fullscreen': True,
|
|
'languageToggle': True,
|
|
'notification': True,
|
|
'themeToggle': True,
|
|
'sidebarToggle': True,
|
|
'lockScreen': True,
|
|
'refresh': True,
|
|
},
|
|
'shortcutKeys': {
|
|
'enable': True,
|
|
'globalSearch': True,
|
|
'globalLogout': True,
|
|
'globalLockScreen': True,
|
|
},
|
|
}
|
|
|
|
# 布局类型选项
|
|
LAYOUT_OPTIONS = [
|
|
{'label': '侧边导航', 'value': 'sidebar-nav'},
|
|
{'label': '侧边混合导航', 'value': 'sidebar-mixed-nav'},
|
|
{'label': '顶部导航', 'value': 'header-nav'},
|
|
{'label': '混合导航', 'value': 'mixed-nav'},
|
|
{'label': '全屏内容', 'value': 'full-content'},
|
|
]
|
|
|
|
# 内置主题选项
|
|
BUILTIN_THEME_OPTIONS = [
|
|
{'label': '默认', 'value': 'default'},
|
|
{'label': '紫罗兰', 'value': 'violet'},
|
|
{'label': '粉红', 'value': 'pink'},
|
|
{'label': '玫瑰', 'value': 'rose'},
|
|
{'label': '天蓝', 'value': 'sky'},
|
|
{'label': '青色', 'value': 'cyan'},
|
|
{'label': '绿色', 'value': 'green'},
|
|
{'label': '橙色', 'value': 'orange'},
|
|
{'label': '黄色', 'value': 'yellow'},
|
|
{'label': '锌色', 'value': 'zinc'},
|
|
{'label': '中性', 'value': 'neutral'},
|
|
{'label': '石板', 'value': 'slate'},
|
|
{'label': '灰色', 'value': 'gray'},
|
|
{'label': '深灰', 'value': 'deep-blue'},
|
|
{'label': '深绿', 'value': 'deep-green'},
|
|
]
|
|
|
|
def execute(self, context: NodeContext) -> NodeResult:
|
|
"""执行节点"""
|
|
try:
|
|
# 检查是否有用户编辑的数据(从设计预览面板提交)
|
|
user_input = context.variables.get('__user_input__')
|
|
if user_input:
|
|
edited_data = self._parse_user_edit(user_input)
|
|
if edited_data:
|
|
logger.info(f'AppSettingsNode - 使用用户编辑的数据')
|
|
# 使用编辑后的数据,清除用户输入
|
|
context.variables.pop('__user_input__', None)
|
|
return self._build_result(edited_data, require_confirmation=False)
|
|
|
|
# 获取输入参数
|
|
app_name = context.resolve_template(self.config.get('app_name', ''))
|
|
home_path = context.resolve_template(self.config.get('home_path', ''))
|
|
logo_source = context.resolve_template(self.config.get('logo_source', ''))
|
|
theme_builtin_type = context.resolve_template(self.config.get('theme_builtin_type', 'default'))
|
|
theme_color_primary = context.resolve_template(self.config.get('theme_color_primary', ''))
|
|
app_layout = context.resolve_template(self.config.get('app_layout', 'sidebar-nav'))
|
|
|
|
logger.info(f'AppSettingsNode - 输入参数: app_name={app_name}, home_path={home_path}, layout={app_layout}')
|
|
|
|
# 构建设置数据(合并默认值和输入值)
|
|
settings = self._build_settings(
|
|
app_name=app_name,
|
|
home_path=home_path,
|
|
logo_source=logo_source,
|
|
theme_builtin_type=theme_builtin_type,
|
|
theme_color_primary=theme_color_primary,
|
|
app_layout=app_layout,
|
|
)
|
|
|
|
logger.info(f'AppSettingsNode - 输出设置配置')
|
|
|
|
# 检查是否需要确认(支持布尔值、字符串模式和变量引用)
|
|
require_confirmation = self.resolve_require_confirmation(context, default=True)
|
|
|
|
return self._build_result(settings, require_confirmation)
|
|
|
|
except Exception as e:
|
|
logger.exception(f'应用设置节点执行失败: {e}')
|
|
return NodeResult(
|
|
success=False,
|
|
error=f'节点执行失败: {str(e)}',
|
|
)
|
|
|
|
async def execute_async(self, context: NodeContext) -> NodeResult:
|
|
"""异步执行节点(直接调用同步方法)"""
|
|
return self.execute(context)
|
|
|
|
def _parse_user_edit(self, user_input: Any) -> Optional[Dict[str, Any]]:
|
|
"""解析用户编辑的数据"""
|
|
if isinstance(user_input, dict):
|
|
# 检查是否包含设置字段
|
|
if 'app' in user_input or 'theme' in user_input or 'logo' in user_input:
|
|
return user_input
|
|
return None
|
|
|
|
if isinstance(user_input, str):
|
|
try:
|
|
data = json.loads(user_input)
|
|
if isinstance(data, dict) and ('app' in data or 'theme' in data or 'logo' in data):
|
|
return data
|
|
except (json.JSONDecodeError, TypeError):
|
|
pass
|
|
|
|
return None
|
|
|
|
def _build_settings(
|
|
self,
|
|
app_name: str = '',
|
|
home_path: str = '',
|
|
logo_source: str = '',
|
|
theme_builtin_type: str = 'default',
|
|
theme_color_primary: str = '',
|
|
app_layout: str = 'sidebar-nav',
|
|
) -> Dict[str, Any]:
|
|
"""构建设置配置"""
|
|
import copy
|
|
settings = copy.deepcopy(self.DEFAULT_SETTINGS)
|
|
|
|
# 应用配置
|
|
if app_name:
|
|
settings['app']['name'] = app_name
|
|
|
|
# 首页路径配置
|
|
if home_path:
|
|
settings['app']['defaultHomePath'] = home_path
|
|
|
|
# 布局配置
|
|
if app_layout:
|
|
settings['app']['layout'] = app_layout
|
|
|
|
# Logo配置
|
|
if logo_source:
|
|
settings['logo']['source'] = logo_source
|
|
|
|
# 主题配置
|
|
if theme_builtin_type:
|
|
settings['theme']['builtinType'] = theme_builtin_type
|
|
if theme_color_primary:
|
|
settings['theme']['colorPrimary'] = theme_color_primary
|
|
|
|
return settings
|
|
|
|
def _build_result(self, settings: Dict[str, Any], require_confirmation: bool = False) -> NodeResult:
|
|
"""构建节点结果"""
|
|
# 构建预览数据
|
|
preview_data = {
|
|
'type': 'app_settings',
|
|
'title': '应用设置',
|
|
'data': settings,
|
|
'editable': True,
|
|
'layoutOptions': self.LAYOUT_OPTIONS,
|
|
'themeOptions': self.BUILTIN_THEME_OPTIONS,
|
|
} if require_confirmation else None
|
|
|
|
return NodeResult(
|
|
success=True,
|
|
output=settings,
|
|
output_variables={
|
|
'settings': settings,
|
|
'confirmed': not require_confirmation,
|
|
},
|
|
preview=preview_data,
|
|
waiting_for_input=require_confirmation,
|
|
waiting_config={
|
|
'type': 'design_preview',
|
|
'preview_type': 'app_settings',
|
|
'title': '应用设置',
|
|
'message': '请确认或编辑应用设置',
|
|
'data': settings,
|
|
'layoutOptions': self.LAYOUT_OPTIONS,
|
|
'themeOptions': self.BUILTIN_THEME_OPTIONS,
|
|
} if require_confirmation else {},
|
|
)
|
|
|
|
@classmethod
|
|
def get_config_schema(cls) -> Dict[str, Any]:
|
|
"""
|
|
获取节点配置 Schema(供前端表单渲染)
|
|
|
|
Returns:
|
|
配置 Schema
|
|
"""
|
|
return {
|
|
'type': 'object',
|
|
'properties': {
|
|
'app_name': {
|
|
'type': 'string',
|
|
'title': '应用名称',
|
|
'description': '应用的显示名称,支持变量引用如 {{llm_output}}',
|
|
'required': False,
|
|
'x-component': 'Input',
|
|
'x-component-props': {
|
|
'placeholder': '请输入应用名称',
|
|
},
|
|
},
|
|
'home_path': {
|
|
'type': 'string',
|
|
'title': '首页路径',
|
|
'description': '应用首页路径,如 /dashboard 或 /analytics',
|
|
'required': False,
|
|
'x-component': 'Input',
|
|
'x-component-props': {
|
|
'placeholder': '如 /dashboard',
|
|
},
|
|
},
|
|
'logo_source': {
|
|
'type': 'string',
|
|
'title': 'Logo图片',
|
|
'description': 'Logo图片URL或文件ID,支持变量引用',
|
|
'required': False,
|
|
'x-component': 'Input',
|
|
'x-component-props': {
|
|
'placeholder': '请输入Logo图片URL',
|
|
},
|
|
},
|
|
'theme_builtin_type': {
|
|
'type': 'string',
|
|
'title': '内置主题',
|
|
'description': '选择内置主题类型',
|
|
'default': 'default',
|
|
'enum': ['default', 'violet', 'pink', 'rose', 'sky', 'cyan', 'green', 'orange', 'yellow', 'zinc', 'neutral', 'slate', 'gray', 'deep-blue', 'deep-green'],
|
|
'x-component': 'Select',
|
|
'x-component-props': {
|
|
'placeholder': '请选择内置主题',
|
|
},
|
|
},
|
|
'theme_color_primary': {
|
|
'type': 'string',
|
|
'title': '主题主色调',
|
|
'description': '主题主色调,如 hsl(212 100% 45%)',
|
|
'required': False,
|
|
'x-component': 'Input',
|
|
'x-component-props': {
|
|
'placeholder': '如 hsl(212 100% 45%)',
|
|
},
|
|
},
|
|
'app_layout': {
|
|
'type': 'string',
|
|
'title': '应用布局',
|
|
'description': '应用布局类型',
|
|
'default': 'sidebar-nav',
|
|
'enum': ['sidebar-nav', 'sidebar-mixed-nav', 'header-nav', 'mixed-nav', 'full-content'],
|
|
'x-component': 'Select',
|
|
'x-component-props': {
|
|
'placeholder': '请选择布局类型',
|
|
},
|
|
},
|
|
'require_confirmation': {
|
|
'type': 'boolean',
|
|
'title': '需要确认',
|
|
'description': '设置完成后是否暂停等待用户确认或编辑',
|
|
'default': True,
|
|
'x-component': 'Switch',
|
|
},
|
|
},
|
|
'required': [],
|
|
}
|