从零到一自建云表格系统

从零到一自建云表格系统

大部分业务系统、管理后台 70% 的功能是数据表格的增删改查。这些功能的实现,以复制粘贴修改字段为主。本文将尝试用一种更优雅的 “复制粘贴” 方式编写此类功能。如果你希望告别枯燥乏味的重复劳动,或者需要自建一套云表格系统,接下来的内容可以帮到你。

DEMO 演示

通过表格编辑器,在线设计制作表格类功能模块。适用于用户管理、供应商管理、订单管理等表格 CURD 操作的功能模块。

image.png

res.png

源码地址: https://github.com/YaoApp/demo-widget

**一键部署: **https://letsinfra.com/openapp/demo-widget

构建工具

YAO 开源应用引擎

yao-arch-white.png YAO 是一款开源应用引擎,使用 Golang 编写,以一个命令行工具的形式存在, 下载即用。适合用于搭建业务系统、网站/APP API 接口、管理后台等应用程序。

YAO 采用 flow-based 的编程模式,通过编写 YAO DSL (JSON 格式逻辑描述) 或 JavaScript 处理器,实现各种功能。 YAO DSL 可以有多种编写方式:

  1. 纯手工编写
  2. 使用自动化脚本,根据上下文逻辑生成
  3. 使用可视化编辑器,通过“拖拉拽”制作

**GitHub 地址: ** https://github.com/yaoapp/yao
Github Stars: 4.3K 开源协议: Apache 2.0 官方文档: https://yaoapps.com/doc

YAO Widget

YAO 将通用功能模块抽象为 Widget,通过 YAO DSL 描述差异,快速复制各种功能。 在开发中,如果开发一个相似的功能模块,需要将已有的功能复制粘贴,批量替换差异内容。 YAO 提供一种新方式 ,使用 DSL 描述差异内容,快速“复制粘贴”一个功能模块,以此来提升开发效率。

**YAO 内建了一组 Widgets, **涵盖大部分应用程序开发常见功能。

内建 Widget 功能
model 数据结构建模
flow 数据逻辑编排
api RESTFul API
table 表格 CURD
chart 数据图表
login 用户登录
register 用户注册
task 并发任务
schedule 计划任务
socket Socket
websocket WebSocket

Widget 支持开发者自定义,可基于自身业务逻辑特征,自定义 DSL, 快速复制各种通用功能。本文将通过自定义 Widget 的方式,实现一套云表格系统。

实战: 极简实用的云表格系统

Part I: 制作 dyform Widget

准备工作: 安装 YAO 命令

阅读以下内容,需要具备基础编程能力,熟悉 RESTFul API ,关系数据库,JavaScript 语言等编程常识。 参考文档 入门指南 , 在本地安装 YAO 命令 ( 版本: v0.10.1),并熟悉基本使用方法。
**在本地创建一个应用: **

# 创建一个项目文件夹
mkdir -p /your/project/root

# 初始化项目
yao init

# 创建数据表 & 初始化菜单
yao migrate && yao run flows.setmenu

第一步: 设计 Widge DSL 数据结构

根据业务逻辑特征,设计 DSL 结构,本例中,不同表格间的差异为 表格字段、搜索器以及表格名称。 **dyform Widget DSL 数据结构: **

{
  "name": "TEST",
  "decription": "A TEST DYFORM",
  "columns": [
    {
      "title": "First Name",
      "name": "name",
      "type": "input",
      "search": true,
      "props": {
        "placeholder": "Please input your first name"
      }
    },
    {
      "title": "Amount",
      "name": "amount",
      "type": "input",
      "search": true,
      "props": {
        "placeholder": "Please input amount"
      }
    },
    {
      "title": "Description",
      "name": "desc",
      "type": "textArea",
      "props": {
        "placeholder": "Please input Description"
      }
    }
  ]
}

字段描述 DSL :

配置项 说明
title 在表格中显示的标题
name 字段名称, 对应数据库字段名
type 填写表单时,使用的组件
props 组件属性
search 该字段是否可以作为筛查项

创建 dyforms 文件,用于保存 DSL

cd /your/project/root
mkdir dyforms

将上面 DSL 保存到 **dyforms/demo.form.json **文件,用于调试。

第二步: 创建 dyform Widget

创建 dyform widget:

cd /your/project/root
mkdir -p widgets/dyform

# 创建 widget 文件
touch widgets/dyform/widget.json  # Widget 基本信息
touch widgets/dyform/compile.js   # dyform DSL 编译器
touch widgets/dyform/process.js   # dyform 处理器
touch widgets/dyform/export.js    # dyform 导出内建 Widget 定义

提示: YAO 自定义 widget 放置在 widgets 目录,需要手动创建这个目录。

使用编辑器打开 ** dyform/widget.json** 填写 dyform widget 基本信息

{
  "label": "Dynamic Form",
  "description": "A form widget. users can design forms online",
  "version": "0.1.0",
  "root": "dyforms",
  "extension": ".form.json",
  "modules": ["Models", "Tables"]
}

**字段说明: **

配置项 说明
label Widget 名称,在可视化编辑器显示。
description Widget 介绍, 在可视化编辑器显示。
version 版本号
root DSL 文件保存路径(相对于项目根目录)
extension DSL 文件扩展名
modules 需要导出的模块。 在 export.js 中根据 DSL 描述,转换的 YAO 内建 widgets。 如 model, table 等。这些 widgets 与保存在项目目录中的 DSL 文件等效。

第三步: 编写 dyform 处理器

编写 **dyform/process.js **脚本,实现 **Model, Table **处理器,将自定义 DSL 转换为 YAO 数据模型 widget 和 表格 widget。

编写 Export 函数,声明要导出的处理器, 查看源码

function Export() {
  return { Model: "Model", Table: "Table" };
}

实现 Model & Table 处理器逻辑

处理器 说明 源码链接
Model 将 dyform DSL 转换为 model DSL, 等效于在 models 文件夹手动编写一个 xxx.mod.json DSL 文件 查看源码
Table 将 dyform DSL 转换为 table DSL, 等效于在 tables 文件夹手动编写一个 xxx..json DSL 文件 查看源码

提示: 建议使用 yao run 命令调试处理器,参考源码注释文档。

第四步: 导出为 Model & Table widget

编写 dyform/export.js 脚本, 实现 Models, Tables 函数,导出为 YAO model & table widgets.

导出函数 说明 源码链接
Models 调用 Model 处理器,导出 Yao model widget, 数据结构为 {MODEL_NAME:String: Model_DSL:Object} 查看源码
Tables 调用 Table 处理器, 导出为 Yao table widget , 数据结构为: {TABLE_NAME:String: Table_DSL:Object} 查看源码

效果预览

在 dyforms 文件夹下,添加 **xxx.form.json **文件, 编写 dyform DSL 描述文件,即可快速创建一组表格管理模块、表格 API、表格处理器和模型处理器。

demo.png

使用 yao migrate 命令创建数据表结构,yao start 命令启动服务,登录管理界面,即可使用表格管理功能。 路由地址: http://127.0.0.1:5099/xiang/table/dyform.xxx

Part II: 将 dyform DSL 保存到数据库,实现动态读写

如果希望把表格制作功能开放给用户,且在通常情况下代码目录应设定为只读,这就无法动态创建 DSL 文件。对 dyform widget 稍加改造,将 dyform DSL 保存在数据库中即可。

第一步: 创建一个 template model, 存储 dyform DSL

如何创建使用数据模型 models/template.mod.json 和管理表格 tables/template.tab.json, 参考Model 文档Table 文档 models/template.mod.json 如下

{
  "name": "Template",
  "table": { "name": "template", "comment": "For dyform DSL source" },
  "columns": [
    { "label": "ID", "name": "id", "type": "ID", "comment": "ID" },
    { "label": "Name", "name": "name", "type": "string", "index": true },
    {
      "label": "DSL",
      "name": "dsl",
      "type": "text",
      "comment": "DSL"
    }
  ],
  "values": [],
  "option": { "timestamps": true, "soft_deletes": true }
}

DSL 管理界面 admin.png

第二步: 给 dyform Widget 添加 Save & Delete 处理器

编写 Export 函数,声明要导出的处理器, 查看源码

function Export() {
  return { Model: "Model", Table: "Table", Save: "Save", Delete: "Delete" };
}

实现 Save & Delete 处理器逻辑

处理器 说明 源码链接
Save 根据 dyform DSL 转换为 model DSL, 更新对应数据模型结构,同时重新加载对应实例。 查看源码
Delete 删除数据模型对应数据表 查看源码

编辑 tables/template.tab.json,添加 hooks 脚本,在表格保存和删除时,更新/删除数据表,创建/删除菜单。

**修改 tables/template.tab.json 添加 Hook **查看源码** **

  "hooks": {
    "after:save": "scripts.template.AfterSave",
    "after:delete": "scripts.template.AfterDelete"
  },

**添加 hooks 脚本 scripts/template.js **查看源码

Hook 说明
AfterSave 根据 DSL ,更新 dyform 数据表,并添加菜单
Delete 删除 dyform 数据表,并移除菜单

第三步: 给 dyform Widget 添加内容源

编辑 widgets/dyform/compile.js , 从数据库中读取 DSL

/**
 * Source
 * Where to get the source of DSL
 */
function Source() {
  var sources = {};
  tpls = Process("models.template.Get", { select: ["id", "dsl"], limit: 1000 });
  if (tpls.code && tpls.message) {
    log.Error("Load dyform sources: %s", tpls.message);
    return sources;
  }

  tpls.forEach((tpl) => {
    tpl = tpl || {};
    try {
      instance = `instance_${tpl.id}`;
      dsl = JSON.parse(tpl.dsl);
      sources[instance] = dsl;
    } catch (e) {
      log.Error("Source %v DSL: %s", tpl.id, e.message);
      return;
    }
  });

  return sources;
}

**Compile.js 方法说明 **查看文档

Hook 说明
Source 自定义 DSL 数据源读取逻辑
Compile 根据上下文或环境信息,调整 DSL 结构或准备相关资源
OnLoad 当 DSL 加载完成,调用此方法。

效果预览

登录管理后台,动态添加 dyform DSL ,即可动态创建删除对应表格。 admin2.png res.png

Part III: 可视化表单编辑器

可以使用表单制作工具来生成 form DSL。有很多优秀的开源表单编辑器可以选择,比如 XXXX , 为了演示效果,XGEN 快速实现了一个 DEMO 级的编辑器组件,产品级的实现将在 XGEN-NEXT (正式版) 发布时提供。 源码地址: https://github.com/YaoApp/xgen/tree/main/src/cloud/components/form/FormPrinter image.png

第一步: 适配编辑器组件输入输出

为 template 表格添加 Hook, 用来转换 DSL 结构。 编辑 tables/template.tab.json 文件、 scripts/template.js文件

**添加 **before:save** hook ** 将编辑器组件输出,转换为 form DSL
tables/template.tab.json

{
  ...
	"hooks": {
    "before:save": "scripts.template.BeforeSave",
    "after:save": "scripts.template.AfterSave",
    "after:delete": "scripts.template.AfterDelete"
  }
  ...
}

scripts/template.js

/**
 * BeforSave Hook: transform the form editor data to the DSL
 * @param {*} payload
 */
function BeforeSave(payload) {
  payload = payload || {};
  columns = payload.dsl || [];
  payload["dsl"] = { columns: [], name: payload.name || "UNTITLE" };
  columns.forEach((column) => {
    let type = column.id || "";
    payload["dsl"].columns.push({
      title: column.title || "UNTITLE",
      name: column.bind,
      type: type.toLowerCase(),
      props: column.props || {},
    });
  });
  payload["dsl"] = JSON.stringify(payload["dsl"]);
  return [payload];
}

tips : 可是使用 yao run命令调试例如:

yao run scripts.template.BeforeSave '::{"dsl":...}'

添加 **after:find** hook 将 form DSL 转换为组件输入结构

tables/template.tab.json

{
  ...
  "hooks": {
    "after:find": "scripts.template.AfterFind",
    "before:save": "scripts.template.BeforeSave",
    "after:save": "scripts.template.AfterSave",
    "after:delete": "scripts.template.AfterDelete"
  }
  ...
}

scripts/template.js

/**
 * AfterFind Hook: transform the DSL format to the form editor needs
 * @param {*} id
 * @param {*} template
 */
function AfterFind(template, id) {
  let dsl = JSON.parse(template["dsl"]);
  let columns = dsl.columns || [];
  let types = { input: "Input", select: "Select" };

  template["dsl"] = [];
  columns.forEach((column) => {
    // let props = column.props || {};
    // let search = props.showSearch ? true : false;
    template["dsl"].push({
      title: column.title,
      bind: column.name,
      id: types[column.type],
      props: column.props || {},
      search: true,
      width: 6,
      chosen: false,
      selected: false,
    });
  });

  return template;
}

第二步: 更新 DSL 绑定的组件

编辑 tables/template.tab.json 文件, 将 DSL 字段编辑组件设定为 FormPrinter。 源码地址: https://github.com/YaoApp/demo-widget/blob/main/tables/template.tab.json

{
  ....
  "Form Editor": {
      "label": "Form Editor",
      "edit": { "type": "FormPrinter", "props": { "value": ":dsl" } }
  }
  ...
}

效果预览

打开 templates 表格,点击编辑一个模板,即可使用可视化编辑器制作表单。 image.png

Part IV: 制作 C 端表单页面

对于活动报名、预约注册等 C 端页面,需要个性化设计来提升用户体验。对于此类场景,可以有针对性的设计一组模板,使用任意前端技术栈实现。这里提供一个 VUE3 实现的 DEMO。 **源码地址: **https://github.com/YaoApp/demo-widget-front

第一步: 添加一个 API,用来读取表格配置信息

添加一个 GET /page/form/setting/:name API , 在 apis 文件夹下,添加一个 page.http.json 文件, 添加一个接口,第一个参数为 表格名称.

{
  "name": "Table Setting",
  "version": "1.0.0",
  "description": "Table Setting",
  "group": "page",
  "guard": "-",
  "paths": [
    {
      "path": "/form/setting/:name",
      "method": "GET",
      "guard": "-",
      "process": "xiang.table.Setting",
      "in": ["$param.name", ":query"],
      "out": { "status": 200, "type": "application/json" }
    }
  ]
}

源码地址: https://github.com/YaoApp/demo-widget/blob/main/apis/page.http.json

第二步: 在 SETUP 时,请求配置接口,获取配置信息。

const url = `${window.location.protocol}//${window.location.host}/api/page/form/setting/${formName}`;
const response = fetch(url);
response
  .then((res) => {
    return res.json();
  })
  .then((data) => {
    formTitle.value = data.name;
    const fieldset = data?.edit?.layout?.fieldset || [];
    const columns = fieldset.length > 0 ? fieldset[0]?.columns : [];
    const mapping = data?.columns || {};
    columns.forEach((col: any) => {
      let column = mapping[col.name] || false;
      if (column?.edit) {
        let name = column.edit.props?.value || "";
        column.edit.label = col.name;
        column.edit.field = name.replace(":", "");
        column.edit.type = components[column.edit.type];
        formItems.value.push(column.edit);
      }
    });

    loading.value = true;
  })
  .catch((err) => {
    console.log("ERR", err);
    failure.value = err.message;
  });

源码地址: https://github.com/YaoApp/demo-widget-front/blob/main/vue3/src/App.vue

第三步: 动态渲染表单

使用 component 组件,根据配置渲染表单

<template>
  <div v-if="loading">
    <div class="header">{{ formTitle }}</div>
    <form>
      <div class="form-item" v-for="item of formItems" :key="item.label">
        <div class="label">{{ item.label }}</div>
        <component :is="item.type" :name="item.field"></component>
      </div>
      <div class="form-item" v-if="formItems">
        <button class="button" type="button">Submit</button>
      </div>
    </form>
  </div>
  <div v-else-if="failure" class="failure">{{ failure }}</div>
  <div v-else>
    <div>Loading...</div>
  </div>
</template>

源码地址: https://github.com/YaoApp/demo-widget-front/blob/main/vue3/src/App.vue

第四步: 将制品复制到 UI 目录

YAO 内建了一个 HTTP server,将制品复制到 ui 目录即可访问

npm run build
cp -r dist ../demo-widget/ui/vue3

源码地址: https://github.com/YaoApp/demo-widget/tree/main/ui/vue3

效果预览

打开浏览器,输入 http://your-host:port/xiang/login/admin 进入后台,录入并保存一个表单。 默认用户名: xiang@iqka.com 默认密码: A123456p+

image.png

打开 http://your-host:port/vue3/?form=dyform.instance_1 即可访问自定义表单页面 image.png

优秀的个人博客,低调大师

微信关注我们

原文链接:https://my.oschina.net/yaoapps/blog/5573272

转载内容版权归作者及来源网站所有!

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

相关文章

发表评论

资源下载

更多资源
优质分享Android(本站安卓app)

优质分享Android(本站安卓app)

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

Oracle Database,又名Oracle RDBMS

Oracle Database,又名Oracle RDBMS

Oracle Database,又名Oracle RDBMS,或简称Oracle。是甲骨文公司的一款关系数据库管理系统。它是在数据库领域一直处于领先地位的产品。可以说Oracle数据库系统是目前世界上流行的关系数据库管理系统,系统可移植性好、使用方便、功能强,适用于各类大、中、小、微机环境。它是一种高效率、可靠性好的、适应高吞吐量的数据库方案。

Apache Tomcat7、8、9(Java Web服务器)

Apache Tomcat7、8、9(Java Web服务器)

Tomcat是Apache 软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,由Apache、Sun 和其他一些公司及个人共同开发而成。因为Tomcat 技术先进、性能稳定,而且免费,因而深受Java 爱好者的喜爱并得到了部分软件开发商的认可,成为目前比较流行的Web 应用服务器。

Eclipse(集成开发环境)

Eclipse(集成开发环境)

Eclipse 是一个开放源代码的、基于Java的可扩展开发平台。就其本身而言,它只是一个框架和一组服务,用于通过插件组件构建开发环境。幸运的是,Eclipse 附带了一个标准的插件集,包括Java开发工具(Java Development Kit,JDK)。