在 10 分钟内构建看板 TaskManager 应用程序

一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / Java 学习路线 / 一对一提问 / 学习打卡/ 赠书活动

目前,正在 星球 内带小伙伴们做第一个项目:全栈前后端分离博客项目,采用技术栈 Spring Boot + Mybatis Plus + Vue 3.x + Vite 4手把手,前端 + 后端全栈开发,从 0 到 1 讲解每个功能点开发步骤,1v1 答疑,陪伴式直到项目上线,目前已更新了 204 小节,累计 32w+ 字,讲解图:1416 张,还在持续爆肝中,后续还会上新更多项目,目标是将 Java 领域典型的项目都整上,如秒杀系统、在线商城、IM 即时通讯、权限管理等等,已有 870+ 小伙伴加入,欢迎点击围观

任务管理是企业最重要的功能之一。虽然这个问题有很多解决方案,但还没有灵丹妙药,因为每个业务领域都有自己的特点。在这种情况下开发满足特定要求的应用程序并不少见。 AllcountJS 允许您从头开始非常快速地构建自定义任务管理应用程序。

如果您不熟悉 AllcountJS,请先查看此 入门 指南。虽然 AllcountJS 允许您使用 Node.js 的所有功能,但在本文中我们将仅使用 App Config 代码。为了运行演示代码,您应该先执行 npm install allcountjs-cli ,然后执行 allcountjs init ,或者直接 我们的演示页面运行代码,您将得到如下内容:

模型

让我们从模型声明开始。任务管理中最重要的事情可能是拥有一个任务对象(显然)和一些状态流。让我们在 main.js 中定义我们的模型:


 A.app({
  appName: "Task manager",
  onlyAuthenticated: true,
  menuItems: [
    {
      name: "Planning",
      entityTypeId: "Task"
    }, {
      name: "Statuses",
      entityTypeId: "Status"
    }
  ],
  entities: function(Fields) {
    return {
      Task: {
        fields: {
          summary: Fields.textarea("Summary").required(),
          dueDate: Fields.date("Due Date").required(),
          status: Fields.fixedReference("Status", "Status")
        }
      },
      Status: {
        fields: {
          name: Fields.text("Name").required(),
          order: Fields.integer("Order").required()
        },
        referenceName: "name"
      }
    }
  }
});

此 App Config 生成一个工作应用程序,该应用程序具有两个实体:Task 和 Status。任务对象具有摘要和截止日期字段以及状态参考。状态实体有一个名称字段用作参考名称,同时显示一个组合框和一个顺序字段,稍后将用于定义状态的顺序。如您所见,必填字段标有 .required() 。这意味着在填写这些字段之前无法创建或保存实体。您还可以注意到 onlyAuthenticated 标志,该标志指定未经身份验证无法访问应用程序。

状态顺序

我们可以通过添加 sorting: [['order', 1]], Status 实体类型来定义我们的状态优先级:


 A.app({
  appName: "Task manager",
  onlyAuthenticated: true,
  menuItems: [
    {
      name: "Planning",
      entityTypeId: "Task"
    }, {
      name: "Statuses",
      entityTypeId: "Status"
    }
  ],
  entities: function(Fields) {
    return {
      Task: {
        fields: {
          summary: Fields.textarea("Summary").required(),
          dueDate: Fields.date("Due Date").required(),
          status: Fields.fixedReference("Status", "Status")
        }
      },
      Status: {
        fields: {
          name: Fields.text("Name").required(),
          order: Fields.integer("Order").required()
        },
        referenceName: "name"
      }
    }
  }
});

此声明指示 AllcountJS 按顺序字段按升序对 Status 实体进行排序。

板视图

AllcountJS 最强大的功能之一是视图概念。视图也是一个实体,但作为存储,它使用另一个实体。因此,有机会为同一数据源创建多个行为和可视化。让我们通过添加以下属性为我们的 Task 实体创建一个板视图


 A.app({
  appName: "Task manager",
  onlyAuthenticated: true,
  menuItems: [
    {
      name: "Planning",
      entityTypeId: "Task"
    }, {
      name: "Statuses",
      entityTypeId: "Status"
    }
  ],
  entities: function(Fields) {
    return {
      Task: {
        fields: {
          summary: Fields.textarea("Summary").required(),
          dueDate: Fields.date("Due Date").required(),
          status: Fields.fixedReference("Status", "Status")
        }
      },
      Status: {
        fields: {
          name: Fields.text("Name").required(),
          order: Fields.integer("Order").required()
        },
        referenceName: "name"
      }
    }
  }
});

并创建新的菜单项以打开面板:


 A.app({
  appName: "Task manager",
  onlyAuthenticated: true,
  menuItems: [
    {
      name: "Planning",
      entityTypeId: "Task"
    }, {
      name: "Statuses",
      entityTypeId: "Status"
    }
  ],
  entities: function(Fields) {
    return {
      Task: {
        fields: {
          summary: Fields.textarea("Summary").required(),
          dueDate: Fields.date("Due Date").required(),
          status: Fields.fixedReference("Status", "Status")
        }
      },
      Status: {
        fields: {
          name: Fields.text("Name").required(),
          order: Fields.integer("Order").required()
        },
        referenceName: "name"
      }
    }
  }
});

我们还应该创建我们引用的自定义视图板( board.jade


 A.app({
  appName: "Task manager",
  onlyAuthenticated: true,
  menuItems: [
    {
      name: "Planning",
      entityTypeId: "Task"
    }, {
      name: "Statuses",
      entityTypeId: "Status"
    }
  ],
  entities: function(Fields) {
    return {
      Task: {
        fields: {
          summary: Fields.textarea("Summary").required(),
          dueDate: Fields.date("Due Date").required(),
          status: Fields.fixedReference("Status", "Status")
        }
      },
      Status: {
        fields: {
          name: Fields.text("Name").required(),
          order: Fields.integer("Order").required()
        },
        referenceName: "name"
      }
    }
  }
});

AllcountJS 默认使用 jade 模板语言 board.jade 中的代码定义了我们板的自定义视图的模板。它定义了描述卡片外观的 panelBody 块。我们应该得到 main.js 的以下结果


 A.app({
  appName: "Task manager",
  onlyAuthenticated: true,
  menuItems: [
    {
      name: "Planning",
      entityTypeId: "Task"
    }, {
      name: "Statuses",
      entityTypeId: "Status"
    }
  ],
  entities: function(Fields) {
    return {
      Task: {
        fields: {
          summary: Fields.textarea("Summary").required(),
          dueDate: Fields.date("Due Date").required(),
          status: Fields.fixedReference("Status", "Status")
        }
      },
      Status: {
        fields: {
          name: Fields.text("Name").required(),
          order: Fields.integer("Order").required()
        },
        referenceName: "name"
      }
    }
  }
});

如果您运行此代码示例,您将看到带有状态的看板,您可以在其中在状态之间移动卡片。由于排序,状态顺序由 order 字段定义。

抛光

为了完善您可能想要为菜单项和应用程序添加图标的东西。您可以通过简单地参考 Font Awesome 图标 来做到这一点。让我们为菜单添加 appIcon 和图标,我们将获得应用程序的最终版本。


 A.app({
  appName: "Task manager",
  onlyAuthenticated: true,
  menuItems: [
    {
      name: "Planning",
      entityTypeId: "Task"
    }, {
      name: "Statuses",
      entityTypeId: "Status"
    }
  ],
  entities: function(Fields) {
    return {
      Task: {
        fields: {
          summary: Fields.textarea("Summary").required(),
          dueDate: Fields.date("Due Date").required(),
          status: Fields.fixedReference("Status", "Status")
        }
      },
      Status: {
        fields: {
          name: Fields.text("Name").required(),
          order: Fields.integer("Order").required()
        },
        referenceName: "name"
      }
    }
  }
});

尝试在我们的 演示库 中运行它。