百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程网 > 正文

AppBox快速开发框架(开源)开发流程介绍

yuyutoo 2024-10-12 01:29 18 浏览 0 评论

??目前很多低代码平台都是基于Web用拖拽方式生成界面,确实可以极大的提高开发效率,但也存在一些问题:

  1. 大部分平台灵活性不够,特殊需求需要较大的自定义开发;
  2. 解析json配置的执行效率不是太高;
  3. 大部分平台缺乏后端支持或复杂的业务逻辑支持;
  4. 与后端的数据结构及业务服务不存在强关联,修改后端容易造成前端配置失效;
  5. 大部分平台缺乏移动端及桌面端支持;

??作者通过不断尝试及多年的经验积累创建了AppBox项目,一个快速开发框架,其将应用系统所涉及的数据结构、业务逻辑、用户界面、工作流、报表、权限等抽象为各类型的模型,通过组合模型形成完整的应用系统,也可以在线修改模型以适应业务的需求变更。 由于模型具备规范性和关联性约束,这样可以高效的分析模型间的关系,并减少因修改模型时引入新的缺陷。本文以客户信息管理作为示例简单介绍使用AppBox的开发流程,以便小伙伴们能够快速了解本框架。

一、运行前准备

  • 准备一个空的数据库,目前仅支持PostgreSql;
  • 克隆仓库git clone --recurse-submodules https://github.com/enjoycode/AppBox.git
  • 编译发布WebHost项目;
  • 编译发布BlazorApp项目,并将发布目录内的wwwroot文件夹复制到WebHost的发布目录内;
  • 修改WebHost目录内的appsettings.json文件中的数据库链接;
  • 终端进入WebHost的发布目录,执行dotnet AppBoxWebHost.dll,首次执行会初始化数据库并创建一些内置的模型(如下图所示);
  • 打开浏览器输入开发环境入口 localhost:5000/#/dev ,登录用户名: Admin 密码: 760wb

懒得编译请加作者微信或邮件直接发打包好的(本想用GitHub Release打包,但超过大小限制)

二、创建实体模型

??实体模型用于描述数据结构,可映射存储至指定数据库,也可以不映射至数据库(DTO)。参考下图先选择模型树的Applications->sys->Entities文件夹,然后点击顶部主菜单的New->Entity,在弹出的对话框内输入实体名称"Customer"并选择映射的数据库"Default"。

??在实体设计器的工具条点击"Add"按钮添加实体成员,其中MemberType(成员类型)中的EntityField代表字段,EntityRef代表一对一引用,EntitySet代表一对多引用。

??点击实体设计器工具条点击"Options"按钮切换至选项面板,用于设置实体的主键及索引。

??上述操作完成后,点击主菜单Models->Save保存当前模型,并且点击Models->Publish发布当前实体模型,发布过程中会在数据库创建对应的数据表。

三、创建服务模型

??服务模型以伪代码的形式提供具体的业务逻辑服务,通过主菜单New->Service创建服务模型,并参考下图输入增删改查的方法。同样在操作完成后,点击主菜单Models->Save保存当前模型,并且点击Models->Publish发布当前模型,发布过程中会将伪代码转换为真正的运行时代码并编译为服务插件备用。

四、创建视图模型

??视图模型有两种形式:一种是拖拽方式生成json配置并渲染的界面,适用于快速配置如大屏页面及简单的增删改查页面;另一种是代码的形式描述用户界面,百分百灵活且经过编译后运行性能高。这里只介绍代码形式,通过主菜单New->View新建视图模型,新建对话框的类型选择"Code"方式,参考以下代码分别建立一个表单视图及一个列表视图,并且保存发布。

  • CustomerForm视图
using sys.Entities;

namespace sys.Views;

public sealed class CustomerForm : View
{
    public static Widget Preview() => new CustomerForm(
        new Customer { Code = "", Name = "", Phone = "", Address = "" }
    );

    public CustomerForm(Customer obj)
    {
        Child = new Column
        {
            Spacing = 5,
            Children = 
            {
                new Text("客户信息") { FontSize = 28 },
                new Form
                {
                    LabelWidth = 60,
                    Children =
                    {
                        new ("编号:", new TextInput(obj.Observe(c => c.Code))),
                        new ("名称:", new TextInput(obj.Observe(c => c.Name))),
                        new ("电话:", new TextInput(obj.Observe(c => c.Phone))),
                        new ("地址:", new TextInput(obj.Observe(c => c.Address))),
                    }
                },
                new Container
                {
                    Padding = EdgeInsets.Only(70, 0, 5, 0),
                    Child = new Button("保存", MaterialIcons.Save)
                    {
                        Width = float.MaxValue,
                        OnTap = _ => Save(obj),
                    }
                },
            }
        };
    }
    
    private async void Save(Customer obj)
    {
        try
        {
            await sys.Services.CustomerService.Save(obj);
            obj.AcceptChanges();
            Notification.Success("保存成功!");
        }
        catch (Exception ex)
        {
            Notification.Error(#34;保存失败: {ex.Message}");
        }
    }
}
  • CustomerList视图
using sys.Entities;

namespace sys.Views;

public sealed class CustomerList : View
{
    public CustomerList()
    {
        Padding = EdgeInsets.All(10);
        Child = new Column
        {
            Spacing = 10,
            Children =
            {
                new Card { Padding = EdgeInsets.All(5), Child = BuildHeader() },
                new Card { Child = BuildBody() }
            }
        };
    }

    private readonly State<string> _searchKey = "";
    private readonly DataGridController<Customer> _dgController = new();

    private Widget BuildHeader() => new Row
    {
        Height = 30,
        Spacing = 10,
        Children =
        {
            new Expanded(),
            new TextInput(_searchKey) { Width = 100, Suffix = new Icon(MaterialIcons.Search) },
            new Button("查询") { OnTap = _ => Fetch() },
            new ButtonGroup
            {
                Children =
                {
                    new Button("新增") { OnTap = _ => OnCreate() },
                    new Button("编辑") { OnTap = _ => OnEdit() },
                    new Button("删除") { OnTap = _  => OnDelete() }
                }
            }
        }
    };

    private Widget BuildBody() => new Expanded(new DataGrid<Customer>(_dgController)
    {
        Columns =
        {
            new DataGridTextColumn<Customer>("编号", t => t.Code) { Width = 60 },
            new DataGridTextColumn<Customer>("名称", t => t.Name),
            new DataGridGroupColumn<Customer>("联系方式")
            {
                Children =
                {
                    new DataGridTextColumn<Customer>("电话", t => t.Phone),
                    new DataGridTextColumn<Customer>("地址", t => t.Address),
                }
            }
         }
    });

    protected override void OnMounted() => Fetch();

    private async void Fetch()
    {
        try
        {
            var list = await sys.Services.CustomerService.Fetch(_searchKey.Value);
            _dgController.DataSource = list;
            _dgController.TrySelectFirstRow();
        }
        catch (Exception ex)
        {
            Notification.Error(#34;查询客户列表失败: {ex.Message}");
        }
    }

    private void OnCreate() => Dialog.Show("新建客户",
        d => new CustomerForm(new Customer { Code = "", Name = "", Phone = "", Address = "" }
    ));

    private void OnEdit()
    {
        var obj = _dgController.CurrentRow;
        if (obj == null) return;
        Dialog.Show("编辑客户", d => new CustomerForm(obj));
    }

    private async void OnDelete()
    {
        var obj = _dgController.CurrentRow;
        if (obj == null) return;
        try
        {
            await sys.Services.CustomerService.Delete(obj);
            Fetch();
        }
        catch (Exception ex)
        {
            Notification.Error(#34;删除客户失败: {ex.Message}");
        }
    }
}

Tip1: 可以点击视图模型编辑器上方工具条的"Preview"按钮实时预览效果,也可以点击左侧工具栏的大纲按钮查看预览视图的组件树及其布局,如下图所示:

Tip2: 另外可以在代码编辑器内光标位置右键菜单选择"Goto Definition"跳转至相应的模型定义内,如下动图所示光标定位实体属性然后跳转至实体设计器内:

五、设置路由并生成应用

??以上步骤完成后,我们需要修改HomePage视图注册客户列表视图的路由,先选择HomePage视图,然后主菜单Models->Checkout签出待修改,添加如下图高亮行所示代码,修改HomePage视图后同样需要保存发布,最后需要点击主菜单Apps->BuildApp生成Web应用。

??这样我们就可以在浏览器地址栏直接输入localhost:5000/#/customers访问客户列表视图,如下图所示:

六、小结

??作者个人能力实在有限,目前还有很多Bug待修复,还有工作流引擎及报表引擎待从旧版移植过来,如有问题请邮件联系或Github Issue,欢迎感兴趣的小伙伴们加入共同完善,当然更欢迎赞助项目或给作者介绍工作(目前找工作中)。

相关推荐

Mysql和Oracle实现序列自增(oracle创建序列的sql)

Mysql和Oracle实现序列自增/*ORACLE设置自增序列oracle本身不支持如mysql的AUTO_INCREMENT自增方式,我们可以用序列加触发器的形式实现,假如有一个表T_WORKM...

关于Oracle数据库12c 新特性总结(oracle数据库19c与12c)

概述今天主要简单介绍一下Oracle12c的一些新特性,仅供参考。参考:http://docs.oracle.com/database/121/NEWFT/chapter12102.htm#NEWFT...

MySQL CREATE TABLE 简单设计模板交流

推荐用MySQL8.0(2018/4/19发布,开发者说同比5.7快2倍)或同类型以上版本....

mysql学习9:创建数据库(mysql5.5创建数据库)

前言:我也是在学习过程中,不对的地方请谅解showdatabases;#查看数据库表createdatabasename...

MySQL面试题-CREATE TABLE AS 与CREATE TABLE LIKE的区别

执行"CREATETABLE新表ASSELECT*FROM原表;"后,新表与原表的字段一致,但主键、索引不会复制到新表,会把原表的表记录复制到新表。...

Nike Dunk High Volt 和 Bright Spruce 预计将于 12 月推出

在街上看到的PandaDunk的超载可能让一些球鞋迷们望而却步,但Dunk的浪潮仍然强劲,看不到尽头。我们看到的很多版本都是为女性和儿童制作的,这种新配色为后者引入了一种令人耳目一新的新选择,而...

美国多功能舰载雷达及美国海军舰载多功能雷达系统技术介绍

多功能雷达AN/SPY-1的特性和技术能力,该雷达已经在美国海军服役了30多年,其修改-AN/SPY-1A、AN/SPY-1B(V)、AN/SPY-1D、AN/SPY-1D(V),以及雷神...

汽车音响怎么玩,安装技术知识(汽车音响怎么玩,安装技术知识视频)

全面分析汽车音响使用或安装技术常识一:主机是大多数人最熟习的音响器材,有关主机的各种性能及规格,也是耳熟能详的事,以下是一些在使用或安装时,比较需要注意的事项:LOUDNESS:几年前的主机,此按...

【推荐】ProAc Response系列扬声器逐个看

有考牌(公认好声音)扬声器之称ProAcTablette小音箱,相信不少音响发烧友都曾经,或者现在依然持有,正当大家逐渐掌握Tablette的摆位设定与器材配搭之后,下一步就会考虑升级至表现更全...

#本站首晒# 漂洋过海来看你 — BLACK&amp;DECKER 百得 BDH2000L无绳吸尘器 开箱

作者:初吻给了烟sco混迹张大妈时日不短了,手没少剁。家里有了汪星人,吸尘器使用频率相当高,偶尔零星打扫用卧式的实在麻烦(汪星人:你这分明是找借口,我掉毛是满屋子都有,铲屎君都是用卧式满屋子吸的,你...

专题|一个品牌一件产品(英国篇)之Quested(罗杰之声)

Quested(罗杰之声)代表产品:Q212FS品牌介绍Quested(罗杰之声)是录音监听领域的传奇品牌,由英国录音师RogerQuested于1985年创立。在成立Quested之前,Roger...

常用半导体中英对照表(建议收藏)(半导体英文术语)

作为一个源自国外的技术,半导体产业涉及许多英文术语。加之从业者很多都有海外经历或习惯于用英文表达相关技术和工艺节点,这就导致许多英文术语翻译成中文后,仍有不少人照应不上或不知如何翻译。为此,我们整理了...

Fyne Audio F502SP 2.5音路低音反射式落地音箱评测

FyneAudio的F500系列,有新成员了!不过,新成员不是新的款式,却是根据原有款式提出特别版。特别版产品在原有型号后标注了SP字样,意思是SpecialProduction。Fyne一共推出...

有哪些免费的内存数据库(In-Memory Database)

以下是一些常见的免费的内存数据库:1.Redis:Redis是一个开源的内存数据库,它支持多种数据结构,如字符串、哈希表、列表、集合和有序集合。Redis提供了快速的读写操作,并且支持持久化数据到磁...

RazorSQL Mac版(SQL数据库查询工具)

RazorSQLMac特别版是一款看似简单实则功能非常出色的SQL数据库查询、编辑、浏览和管理工具。RazorSQLformac特别版可以帮你管理多个数据库,支持主流的30多种数据库,包括Ca...

取消回复欢迎 发表评论: