之前写过一篇文章 我理想中的低代码开发工具的形态,已经吐槽了各种封装 xxxForm,xxxTable 的行为,这里就不啰嗦了。今天再来看看我的工具达到了什么程度。

多图预警。。。

以管理后台一个列表页为例

选择对应的模板

截图查询区域,使用 OCR 初始化查询表单的配置

截图表头,使用 OCR 初始化 table 的配置

使用 ChatGPT 翻译中文字段

生成代码

效果

目前我们没有写一行代码,就已经达到了如下的效果

下面是一部分生成的代码

import { reactive, ref } from 'vue'

import { IFetchTableListResult } from './api'

interface ITableListItem {
/**
* 决算单状态
*/
settlementStatus: string
/**
* 主合同编号
*/
mainContractNumber: string
/**
* 客户名称
*/
customerName: string
/**
* 客户手机号
*/
customerPhone: string
/**
* 房屋地址
*/
houseAddress: string
/**
* 工程管理
*/
projectManagement: string
/**
* 接口返回的数据,新增字段不需要改 ITableListItem 直接从这里取
*/
apiResult: IFetchTableListResult['result']['records'][0]
} interface IFormData {
/**
* 决算单状态
*/
settlementStatus?: string
/**
* 主合同编号
*/
mainContractNumber?: string
/**
* 客户名称
*/
customerName?: string
/**
* 客户手机号
*/
customerPhone?: string
/**
* 工程管理
*/
projectManagement?: string
} interface IOptionItem {
label: string
value: string
} interface IOptions {
settlementStatus: IOptionItem[]
} const defaultOptions: IOptions = {
settlementStatus: [],
} export const defaultFormData: IFormData = {
settlementStatus: undefined,
mainContractNumber: undefined,
customerName: undefined,
customerPhone: undefined,
projectManagement: undefined,
} export const useModel = () => {
const filterForm = reactive<IFormData>({ ...defaultFormData }) const options = reactive<IOptions>({ ...defaultOptions }) const tableList = ref<(ITableListItem & { _?: unknown })[]>([]) const pagination = reactive<{
page: number
pageSize: number
total: number
}>({
page: 1,
pageSize: 10,
total: 0,
}) const loading = reactive<{ list: boolean }>({
list: false,
}) return {
filterForm,
options,
tableList,
pagination,
loading,
}
} export type Model = ReturnType<typeof useModel>

这就是用模板生成的好处,有规范,随时可以改,而封装 xxxForm,xxxTable 就是一个黑盒。

原理

下面大致说一下原理

首先是写好一个个模版,vscode 插件读取指定目录下模版显示到界面上

每个模版下可能包含如下内容:

选择模版后,进入动态表单配置界面

动态表单是读取 config/schema.json 里的内容进行动态渲染的,目前支持 amis、form-render、formily

配置表单是为了生成 JSON 数据,然后根据 JSON 数据生成代码。所以最终还是无法避免的使用私有的 DSL ,但是生成后的代码是没有私有 DSL 的痕迹的。生成代码本质是 JSON + EJS 模版引擎编译 src 目录下的 ejs 文件。

为了加快表单的配置,可以自定义脚本进行操作

这部分内容是读取 config/preview.json 内容进行显示的

选择对应的脚本方法后,插件会动态加载 script/index.js 脚本,并执行里面对应的方法

以 initColumnsFromImage 方法为例,这个方法是读取剪贴板里的图片,然后使用百度 OCR 解析出文本,再使用文本初始化表单

initColumnsFromImage: async (lowcodeContext) => {
context.lowcodeContext = lowcodeContext;
const res = await main.handleInitColumnsFromImage();
return res;
},
export async function handleInitColumnsFromImage() {
const { lowcodeContext } = context;
if (!lowcodeContext?.clipboardImage) {
window.showInformationMessage('剪贴板里没有截图');
return lowcodeContext?.model;
}
const ocrRes = await generalBasic({ image: lowcodeContext!.clipboardImage! });
env.clipboard.writeText(ocrRes.words_result.map((s) => s.words).join('\r\n'));
window.showInformationMessage('内容已经复制到剪贴板');
const columns = ocrRes.words_result.map((s) => ({
slot: false,
title: s.words,
dataIndex: s.words,
key: s.words,
}));
return { ...lowcodeContext.model, columns };
}

反正就是可以根据自己的需求定义各种各样的脚本。比如使用 ChatGPT 翻译 JSON 里的指定字段,可以看我的上一篇文章 TypeChat、JSONSchemaChat实战 - 让ChatGPT更听你的话

再比如要实现把中文翻译成英文,然后英文使用驼峰语法,这样就可以将中文转成英文代码变量,下面是实现的效果

选择对应的命令菜单后 vscode 插件会加载对应模版里的脚本,然后执行里面的 onSelect 方法。

main.ts 代码如下

import { env, window, Range } from 'vscode';
import { context } from './context'; export async function bootstrap() {
const clipboardText = await env.clipboard.readText();
const { selection, document } = window.activeTextEditor!;
const selectText = document.getText(selection).trim();
let content = await context.lowcodeContext!.createChatCompletion({
messages: [
{
role: 'system',
content: `你是一个翻译家,你的目标是把中文翻译成英文单词,请翻译时使用驼峰格式,小写字母开头,不要带翻译腔,而是要翻译得自然、流畅和地道,使用优美和高雅的表达方式。请翻译下面用户输入的内容`,
},
{
role: 'user',
content: selectText || clipboardText,
},
],
});
content = content.charAt(0).toLowerCase() + content.slice(1);
window.activeTextEditor?.edit((editBuilder) => {
if (window.activeTextEditor?.selection.isEmpty) {
editBuilder.insert(window.activeTextEditor.selection.start, content);
} else {
editBuilder.replace(
new Range(
window.activeTextEditor!.selection.start,
window.activeTextEditor!.selection.end,
),
content,
);
}
});
}

使用了 ChatGPT。

再来看看,之前生成管理后台 CURD 页面的时候,连 mock 也一起生成了,主要逻辑放在了 complete 方法里,这是插件的一个生命周期函数。

因为 mock 服务在另一个项目里,所以需要跨目录去生成代码,这里我在 mock 服务里加了个接口返回 mock 项目所在的目录

.get(`/mockProjectPath`, async (ctx, next) => {
ctx.body = {
status: 200,
msg: '',
result: __dirname,
};
})

生成代码的时候请求这个接口,就知道往哪个目录生成代码了

const mockProjectPathRes = await axios
.get('http://localhost:3001/mockProjectPath', { timeout: 1000 })
.catch(() => {
window.showInformationMessage(
'获取 mock 项目路径失败,跳过更新 mock 服务',
);
});
if (mockProjectPathRes?.data.result) {
const projectName = workspace.rootPath
?.replace(/\\/g, '/')
.split('/')
.pop();
const mockRouteFile = path.join(
mockProjectPathRes.data.result,
`${projectName}.js`,
);
let mockFileContent = `
import KoaRouter from 'koa-router';
import proxy from '../middleware/Proxy';
import { delay } from '../lib/util'; const Mock = require('mockjs'); const { Random } = Mock; const router = new KoaRouter();
router{{mockScript}}
module.exports = router;
`; if (fs.existsSync(mockRouteFile)) {
mockFileContent = fs.readFileSync(mockRouteFile).toString().toString();
const index = mockFileContent.lastIndexOf(')') + 1;
mockFileContent = `${mockFileContent.substring(
0,
index,
)}{{mockScript}}\n${mockFileContent.substring(index)}`;
}
mockFileContent = mockFileContent.replace(/{{mockScript}}/g, mockScript);
fs.writeFileSync(mockRouteFile, mockFileContent);
try {
execa.sync('node', [
path.join(
mockProjectPathRes.data.result
.replace(/\\/g, '/')
.replace('/src/routes', ''),
'/node_modules/eslint/bin/eslint.js',
),
mockRouteFile,
'--resolve-plugins-relative-to',
mockProjectPathRes.data.result
.replace(/\\/g, '/')
.replace('/src/routes', ''),
'--fix',
]);
} catch (err) {
console.log(err);
}

mock 项目也可以通过 vscode 插件快速创建和使用

插件源码 https://github.com/lowcoding/lowcode-vscode

上面展示的模版都放在了 https://github.com/lowcode-scaffold/lowcode-materials 仓库里,照着 README 步骤做就可以使用了。

还在封装 xxxForm,xxxTable 残害你的同事?试试这个工具的更多相关文章

  1. Go/Python/Erlang编程语言对比分析及示例 基于RabbitMQ.Client组件实现RabbitMQ可复用的 ConnectionPool(连接池) 封装一个基于NLog+NLog.Mongo的日志记录工具类LogUtil 分享基于MemoryCache(内存缓存)的缓存工具类,C# B/S 、C/S项目均可以使用!

    Go/Python/Erlang编程语言对比分析及示例   本文主要是介绍Go,从语言对比分析的角度切入.之所以选择与Python.Erlang对比,是因为做为高级语言,它们语言特性上有较大的相似性, ...

  2. 封装一个基于NLog+NLog.Mongo的日志记录工具类LogUtil

    封装一个基于NLog+NLog.Mongo的日志记录工具类LogUtil,代码比较简单,主要是把MongoTarget的配置.FileTarget的配置集成到类中,同时利用缓存依赖来判断是否需要重新创 ...

  3. 封装一个基于NLog+NLog.Mongo的日志记录工具类LogUtil,nloglogutil

    封装一个基于NLog+NLog.Mongo的日志记录工具类LogUtil,代码比较简单,主要是把MongoTarget的配置.FileTarget的配置集成到类中,同时利用缓存依赖来判断是否需要重新创 ...

  4. 基于JQ的单双日历,本人自己写的哈,还没封装,但是也能用

    <!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title>小 ...

  5. 封装一个简单好用的打印Log的工具类And快速开发系列 10个常用工具类

    快速开发系列 10个常用工具类 http://blog.csdn.net/lmj623565791/article/details/38965311 ------------------------- ...

  6. 还在用逆向工程?太Low了,试试通用Mapper吧!

    什么是通用Mapper? 通用mapper 可以极大的方便开发人员进行ORM,提供极其方便的单表增删改查. 什么是通用mapper,一句话简单说,它就是个辅助mybatis极简单表开发的组件.它不是为 ...

  7. 还在使用MyBatis Generator?试试这个工具

    代码生成 在企业软件开发过程中,大多数时间都是面向数据库表的增删改查开发.通过通用的增删改查代码生成器,可以有效的提高效率,降低成本:把有规则的重复性劳动让机器完成,解放开发人员. MyBatis G ...

  8. 网络协议之:还在用HTTP代理?弱爆了!快试试SOCKS5

    目录 简介 为什么要使用SOCKS SOCKS5 SOCKS5的使用 总结 简介 存在即是合理,SOCKS5的出现是为了解决SOCKS4中不支持身份认证的大问题而出现的,毕竟大家对网络中的安全越来越重 ...

  9. 还在用em strong吗?快来试试 text-emphasis

    大家好,我是半夏,一个刚刚开始写文的沙雕程序员.如果喜欢我的文章,可以关注 点赞 加我微信:frontendpicker,一起学习交流前端,成为更优秀的工程师-关注公众号:搞前端的半夏,了解更多前端知 ...

  10. 在PHP中使用CURL,“撩”服务器只需几行——php curl详细解析和常见大坑

    在PHP中使用CURL,"撩"服务器只需几行--php curl详细解析和常见大坑 七夕啦,作为开发,妹子没得撩就"撩"下服务器吧,妹子有得撩的同学那就左拥妹子 ...

随机推荐

  1. 小米手机抓取Log教程

    当小米手机遇到任何系统下的较为严重的故障时(即此时系统还是基本正常运行的,只是某些功能实现出了问题),例如软件自动退出,SD卡自动卸载,电话无法拨出等等.可以请用户协助抓取log即系统运行日志,来发送 ...

  2. Python+SVM

    # !/usr/bin/env python # encoding: utf-8 # SVM算法 支持向量机 from sklearn import svm import numpy as np fr ...

  3. WPF性能优化:Freezable 对象

    Freezable是WPF中一个特殊的基类,用于创建可以冻结(Freeze)的可变对象.冻结一个对象意味着将其状态设置为只读,从而提高性能并允许在多线程环境中共享对象. Freezable的应用 我们 ...

  4. docker 仓库-Harbor

    docker 仓库之分布式 Harbor: Harbor 是一个用于存储和分发docker镜像的企业级Registry服务器,由于Vmware 开源,其通过添加一些企业必须的功能特性,例如安全.标识和 ...

  5. JAVA异常的不正确处理方式

    最近收到一个求助信息,异常堆栈如下: ``` java.lang.RuntimeException: FF1C1005 at test_ssh.sftp.Pool.get(Pool.java:25) ...

  6. JUC并发编程学习笔记(十)线程池(重点)

    线程池(重点) 线程池:三大方法.七大参数.四种拒绝策略 池化技术 程序的运行,本质:占用系统的资源!优化资源的使用!-> 池化技术(线程池.连接池.对象池......):创建和销毁十分消耗资源 ...

  7. command_execution

    前置知识 可以通过ping的TTL来判断系统的版本 判断了是Linux之后就使用Linux的连接命令来进行操作 这里直接全局搜索flag相关的文件 linux全局查询文件_linux全局查找某个文件- ...

  8. 前端本地导出文件 导出txt sql (简版版的字符串案例)

    1.首页明确要导出的根据 一般有图片 excel  文字.针对不同的文件类型 配置不同的参数 2.知识点 Blob          URL.createObjectUrl new Blob( arr ...

  9. GitHub Actions 入门指南

    前言 GitHub Actions 可以构建一组自动化的工作流程,并提供了拉取请求.合并分支等事件来触发他们.一般成熟的开源项目会在每个版本发布时提供 releases ,它就是通过 Actions ...

  10. python数值列表之range()和list()

    range() 学习了for循环后,显示数字当然也可以很轻松啦,这个时候我们就可以用到range()函数 for list_2 in range(1, 5): print(list_2) range( ...