zerl
v0.4.0
Published
云原生全栈技术框架
Downloads
184
Maintainers
Readme
ZERL 云原生全栈技术框架开发指南
简介
一条来自 MVVM 江湖的传说:
武林至尊,宝刀 React
Vue 不出,谁与争锋。
天下武功,无坚不摧,唯 Solid 不破。
什么是 ZERL ?
ZERL 是基于 Solid.js MVVM 框架打造的全栈技术开发框架。
ZERL 全面采用 Typescript 进行开发,Typescript 能提供更良好的协作体验,提供实时的代码校验,实现更高质量的交付。
ZERL 将更深层次地诠释全栈技术。全栈,一般定义是对工程师技术能力的泛指。是一个组织开发成本控制的有效手段。因此全栈是个人能力的追求,也是组织发展的需要。
ZERL 对全栈实现作出了以下的尝试:
- 保留了 MVVM 框架的优势,并非只是实现了 SSR
- 实现了技术栈的统一,而非简单的语言统一或交互标准统一
- 提供了简便的工程构建和发布部署工具包
- 构建了云原生开发模式,客户端可直接导入服务端模块并在客户端使用,无需实现 api
开始使用
创建应用
$ npx zerli@latest create zerlapp
或
$ npx zerli@latest -c zerlapp
开发
- 运行开发环境
$ cd zerlapp
$ npx zerli@latest
或
$ npm run dev
UI 模块开发
规范
UI 模块运行于浏览器上,开发规范及安全性要求须符合 W3C 标准。
ZERL 所有 UI 组件构建于 solid.js 的 MVVM 框架之上,zerl 的所有组件完全兼容 solid.js 所提供的 API。了解 solid.js API 请前往solidjs.com
UI 模块 API
// 导入ZERL API
import { Button } from 'zerl'
// 导入SolidJS API
import { createSignal, createMemo } from 'zerl'
服务模块开发
规范
服务模块运行于服务容器内,负责数据的存储与处理。服务模块使用 Node.JS 为运行时核心,因此可调用 Node.JS 的一切接口。了解 Node.JS API 前往nodejs.org。
服务模块由逻辑组件和任务组件构成。
逻辑组件实现与 UI 组件或其他客户端进行数据通信,文件命名规则为:[组件名称].mod.ts。
任务组件在应用启动时触运行,可用于数据初始化、定时任务或启动自定义实例等场景应用,文件命名规则为:[组件名称].job.ts。
服务模块 API
// 导入ZERL API
import { Expose } from 'zerl'
封装
$ npx zerli build@latest
或
$ npm run build
运行 Build 命令后根据运行需求选择相应提示的封装选项即可,封装后把生成的运行文件上传到服务器,直接运行即可。
支持 x64 与 arm64 架构的运行文件封装,不支持交叉编译。生成 x64 运行文件须在 x64cpu 的计算机上进行封装,生成 arm64 运行文件须在 arm64cpu 的计算机上进行封装。
部署
- Linux 部署环境下实现非阻塞式启动
- 使用 vi/vim/nano 或其他编辑工具创建 run.sh 文件
- 粘贴以下内容并修改应用程序名称进行保存
- 运行./run.sh 即可
log="`date +%Y%m%d%H%M%S`.log"
killall zerlapp*
nohup ./zerlapp-linux > $log 2& > &1 &
- Docker
应用在封装时,自动检测有否安装 docker,如有安装则会出现 docker 生成镜像的选项,打包后会在本地生成 docker 镜像。
配置
- 在工程目录(src)内创建工程配置文件(非必要),命名为 application.yml
- 配置文格式如下:
# app名称
name: your_app_name
# 程序源码目录
root: ./src
# 服务端口
port: 8080
# MySQL数据库连接设置
mysql:
### 最大连接数
connectionLimit: 200
### 数据库地址
host: localhost
### 数据库访问端口
port: 3306
### 数据库登录用户名
user: root
### 数据库登录密码
password: '123456'
### 数据库名称
database: mysql
# Redis连接设置
redis:
## 数据库地址
host: localhost
## 数据库访问端口
port: 6379
## 数据库登录密码
password: '123456'
UI 组件
布局
导入组件
import { Col, Row, Line } from 'zerl'
水平布局组件(Row)
左上角对齐
<Row>
<div class={styles.block} style={{ height: '50px' }}></div>
<div class={styles.block} style={{ height: '100px' }}></div>
<div class={styles.block} style={{ height: '150px' }}></div>
</Row>
左对齐垂直居中
<Row middle class='fill'>
<div class={styles.block} style={{ height: '50px' }}></div>
<div class={styles.block} style={{ height: '100px' }}></div>
<div class={styles.block} style={{ height: '150px' }}></div>
</Row>
左下角对齐
<Row bottom class='fill'>
<div class={styles.block} style={{ height: '50px' }}></div>
<div class={styles.block} style={{ height: '100px' }}></div>
<div class={styles.block} style={{ height: '150px' }}></div>
</Row>
完全居中
<Row center class='fill'>
<div class={styles.block} style={{ height: '50px' }}></div>
<div class={styles.block} style={{ height: '100px' }}></div>
<div class={styles.block} style={{ height: '150px' }}></div>
</Row>
靠顶部水平居中
<Row top center class='fill'>
<div class={styles.block} style={{ height: '50px' }}></div>
<div class={styles.block} style={{ height: '100px' }}></div>
<div class={styles.block} style={{ height: '150px' }}></div>
</Row>
靠底部水平居中
<Row bottom center class='fill'>
<div class={styles.block} style={{ height: '50px' }}></div>
<div class={styles.block} style={{ height: '100px' }}></div>
<div class={styles.block} style={{ height: '150px' }}></div>
</Row>
右上角对齐
<Row right class='fill'>
<div class={styles.block} style={{ height: '50px' }}></div>
<div class={styles.block} style={{ height: '100px' }}></div>
<div class={styles.block} style={{ height: '150px' }}></div>
</Row>
右对齐垂直居中
<Row right middle class='fill'>
<div class={styles.block} style={{ height: '50px' }}></div>
<div class={styles.block} style={{ height: '100px' }}></div>
<div class={styles.block} style={{ height: '150px' }}></div>
</Row>
右下角对齐
<Row right bottom class='fill'>
<div class={styles.block} style={{ height: '50px' }}></div>
<div class={styles.block} style={{ height: '100px' }}></div>
<div class={styles.block} style={{ height: '150px' }}></div>
</Row>
属性说明
垂直布局组件(Col)
左上角对齐
<Col class='fill' style={{ 'min-height': '300px' }}>
<div class={styles.block} style={{ width: '50px' }}></div>
<div class={styles.block} style={{ width: '100px' }}></div>
<div class={styles.block} style={{ width: '150px' }}></div>
</Col>
靠左居中
<Col middle class='fill' style={{ 'min-height': '300px' }}>
<div class={styles.block} style={{ width: '50px' }}></div>
<div class={styles.block} style={{ width: '100px' }}></div>
<div class={styles.block} style={{ width: '150px' }}></div>
</Col>
左下角对齐
<Col bottom class='fill'>
<div class={styles.block} style={{ height: '50px' }}></div>
<div class={styles.block} style={{ height: '100px' }}></div>
<div class={styles.block} style={{ height: '150px' }}></div>
</Col>
完全居中
<Col center class='fill'>
<div class={styles.block} style={{ height: '50px' }}></div>
<div class={styles.block} style={{ height: '100px' }}></div>
<div class={styles.block} style={{ height: '150px' }}></div>
</Col>
靠顶部水平居中
<Col top center class='fill'>
<div class={styles.block} style={{ height: '50px' }}></div>
<div class={styles.block} style={{ height: '100px' }}></div>
<div class={styles.block} style={{ height: '150px' }}></div>
</Col>
靠底部水平居中
<Col bottom center class='fill'>
<div class={styles.block} style={{ height: '50px' }}></div>
<div class={styles.block} style={{ height: '100px' }}></div>
<div class={styles.block} style={{ height: '150px' }}></div>
</Col>
右上角对齐
<Col right class='fill'>
<div class={styles.block} style={{ height: '50px' }}></div>
<div class={styles.block} style={{ height: '100px' }}></div>
<div class={styles.block} style={{ height: '150px' }}></div>
</Col>
右对齐垂直居中
<Col right middle class='fill'>
<div class={styles.block} style={{ height: '50px' }}></div>
<div class={styles.block} style={{ height: '100px' }}></div>
<div class={styles.block} style={{ height: '150px' }}></div>
</Col>
右下角对齐
<Col right bottom class='fill'>
<div class={styles.block} style={{ height: '50px' }}></div>
<div class={styles.block} style={{ height: '100px' }}></div>
<div class={styles.block} style={{ height: '150px' }}></div>
</Col>
属性说明
分割线
水平分割线
<Line style={{ width: '400px' }} />
垂直分割线
<Line vertical style={{ height: '50px' }} />
属性说明
路由
导入组件
import { Route, Navigate } from 'zerl'
import Home from './page/home.tsx'
视图加载
同步加载
<div class="main">
<Route default="/home" routers={import.meta.globEager("./pages/**/*.tsx")}/>
</div>
异步加载
<div class="main">
<Route default="/home" routers={import.meta.glob("./pages/**/*.tsx")}/>
</div>
自定义加载
<div class="main">
<Route routers={{"/": Home,"/about": () => lazy(import("./pages/about.tsx"))}}/>
</div>
导航
菜单导航
<Menu width={200} theme="simple">
<Group name="功能组">
<Item route="/main">
功能
</Item>
</Group>
</Menu>
脚本导航
Navigate('/main')
属性说明
按钮
导入组件
import { Button } from 'zerl'
普通按钮
<Button>普通按钮</Button>
缩小设置
小号
<Button mini>小号按钮</Button>
超小号
<Button tiny>超小号按钮</Button>
状态设置
成功状态
<Button success>成功状态</Button>
<Button success mini>成功状态</Button>
<Button success tiny>成功状态</Button>
警告状态
<Button warning>警告状态</Button>
<Button warning mini>警告状态</Button>
<Button warning tiny>警告状态</Button>
危险状态
<Button danger>危险状态</Button>
<Button danger mini>危险状态</Button>
<Button danger tiny>危险状态</Button>
信息状态
<Button info>信息状态</Button>
<Button info mini>信息状态</Button>
<Button info tiny>信息状态</Button>
简单状态
<Button simple>简单状态</Button>
<Button simple mini>简单状态</Button>
<Button simple tiny>简单状态</Button>
形状设置
圆角
<Button rounded>圆角按钮</Button>
<Button rounded mini>圆角按钮</Button>
<Button rounded tiny>圆角按钮</Button>
圆形
<Button circle>圆</Button>
<Button circle mini>圆</Button>
<Button circle tiny>圆</Button>
方形
<Button square>方</Button>
<Button square mini>方</Button>
<Button square tiny>方</Button>
明亮模式
<Button lighten>明亮模式</Button>
<Button lighten success>明亮模式</Button>
<Button lighten warning>明亮模式</Button>
<Button lighten danger>明亮模式</Button>
<Button lighten info>明亮模式</Button>
属性说明
单选
导入组件
import { createSignal, createMemo, Button, Option } from 'zerl'
定义变量
const [getRadioValue1, setRadioValue1] = createSignal(0)
const [getRadioValue2, setRadioValue2] = createSignal(1)
const getRadioValue3 = createMemo(() => 2)
水平排列
<Button name='radio1' type='radio' value={getRadioValue1()} onChange={value => setRadioValue1(value)}>
<Option name='优秀' value={0} />
<Option name='良好' value={1} />
<Option name='一般' value={2} />
<Option name='差' value={3} />
</Button>
垂直排列
<Button name='radio2' vertical type='radio' value={getRadioValue2()} onChange={value => setRadioValue2(value)}>
<Option name='优秀' value={0} />
<Option name='良好' value={1} />
<Option name='一般' value={2} />
<Option name='差' value={3} />
</Button>
只读模式
<Button readonly name='radio3' type='radio' value={getRadioValue3()}>
<Option name='优秀' value={0} />
<Option name='良好' value={1} />
<Option name='一般' value={2} />
<Option name='差' value={3} />
</Button>
属性说明
多选
导入组件
import { createSignal, createMemo, Button, Option } from 'zerl'
定义变量
const [getCheckValue1, setCheckValue1] = createSignal([0])
const [getCheckValue2, setCheckValue2] = createSignal([1])
const getCheckValue3 = createMemo(() => [0, 2])
水平排列
<Button name='check1' type='check' value={getCheckValue1()} onChange={value => setCheckValue1(value)}>
<Option name='优秀' value={0} />
<Option name='良好' value={1} />
<Option name='一般' value={2} />
<Option name='差' value={3} />
</Button>
垂直排列
<Button name='check2' vertical type='check' value={getCheckValue2()} onChange={value => setCheckValue2(value)}>
<Option name='优秀' value={0} />
<Option name='良好' value={1} />
<Option name='一般' value={2} />
<Option name='差' value={3} />
</Button>
只读模式
<Button readonly name='check3' type='check' value={getCheckValue3()}>
<Option name='优秀' value={0} />
<Option name='良好' value={1} />
<Option name='一般' value={2} />
<Option name='差' value={3} />
</Button>
属性说明
提示
导入组件
import { Alert } from 'zerl'
简单的提示
Alert('这是一个提示')
提示样式
Alert('这是一个成功样式的提示', { type: 'success' })
Alert('这是一个警告样式的提示', { type: 'warning' })
Alert('这是一个危险样式的提示', { type: 'danger' })
Alert('这是一个信息样式的提示', { type: 'info' })
提示方向
Alert('这是一个上方弹出的提示', { direction: 'ttb' })
Alert('这是一个下方弹出的提示', { direction: 'btt' })
Alert('这是一个右上方弹出的提示', { direction: 'rttl' })
Alert('这是一个右下方弹出的提示', { direction: 'rbtt' })
自动关闭
Alert('这是一个自动关闭的提示', { delay: 2 })
深色模式
Alert('这是一个深色模式的提示', { mode: 'darken' })
参数说明
options 参数说明
确认
导入组件
import { Confirm } from 'zerl'
组件使用
async function() {
const res = await Confirm('确认提醒信息', () => (
<div>
<h3>提示信息</h3>
<span>这是一条提示信息</span>
</div>
))
}
参数说明
返回值说明
输入
导入组件
import { createSignal, createMemo, Text } from 'zerl'
定义变量
const [getInputValue, setInputValue] = createSignal('')
const [getDateValue, setDateValue] = createSignal(new Date())
const [getDateRangeValue, setDateRangeValue] = createSignal([])
const getValue = createMemo(() => 'readonly')
普通输入框
<Text placeholder='这是一个普通的输入框' value={getInputValue1()} onInput={e => setInputValue(e)} />
数字输入
<Text type='digit' placeholder='这是一个只能输入数字的输入框' value={getInputValue()} onInput={e => setInputValue(e)} />
密码输入
<Text type='password' placeholder='这是一个密码输入框'value={getInputValue()} onInput={e => setInputValue(e)}/>
只读模式
<Text readonly value={getValue()} />
多行输入
多行输入框
<Text type='multi' placeholder='这是一个普通的多行输入框' value={getInputValue()} onInput={e => setInputValue(e)}/>
设置行数
<Text type='multi' rows={10} placeholder='这是一个设置高度为10行的多行输入框' value={getInputValue()} onInput={e => setInputValue(e)}/>
富文本编辑器
<Text type='rich' value={getInputValue()} onInput={e => setInputValue(e)} placeholder='这是一个富文本编辑器' style={{ height: '300px' }}/>
日期时间输入
日期输入框
<Text type='date' placeholder='这是一个日期组件' value={getDateValue()} onInput={e => setDateValue(new Date(e))}/>
日期范围输入框
<Text type='daterange' placeholder='这是一个日期范围组件' value={getDateRangeValue()} onInput={([a, b]) => setDateRangeValue([new Date(a), b ? new Date(b) : null])}/>
时间输入框
<Text type='time' placeholder='这是一个时间组件' value={getDateValue()} onInput={e => setDateValue(new Date(e))}/>
日期时间输入框
<Text type='datetime'placeholder='这是一个日期时间组件' value={getDateValue9()} onInput={e => setDateValue(new Date(e))}/>
日期时间范围输入框
<Text type='datetimerange' placeholder='这是一个日期时间范围组件' value={getDateRangeValue()} onInput={([a, b]) => setDateRangeValue([new Date(a), b ? new Date(b) : null])}/>
属性说明
下拉
导入组件
import { Picker, Option, createSignal } from 'zerl'
定义变量
const [getSingleValue, setSingleValue] = createSignal()
const [getMultiValue, setMultiValue] = createSignal([])
单选框
<Picker placeholder='这是一个单选下拉组件' value={getSingleValue()} onSelected={e => { setSingleValue(e) }}>
<Option name='红色' value='Red' />
<Option name='黄色' value='Yellow' />
<Option name='蓝色' value='Blue' />
<Option name='白色' value='White' />
<Option name='黑色' value='Black' />
</Picker>
只读
<Picker readonly placeholder='这是一个单选下拉组件' value={getSingleValue()} onSelected={e => { setSingleValue(e) }}>
<Option name='红色' value='Red' />
<Option name='黄色' value='Yellow' />
<Option name='蓝色' value='Blue' />
<Option name='白色' value='White' />
<Option name='黑色' value='Black' />
</Picker>
搜索
<Picker max={3} placeholder='这是一个单选下拉组件' value={getSingleValue()} onSelected={e => { setSingleValue(e) }}>
<Option name='红色' value='Red' />
<Option name='黄色' value='Yellow' />
<Option name='蓝色' value='Blue' />
<Option name='白色' value='White' />
<Option name='黑色' value='Black' />
</Picker>
多选框
<Picker multi placeholder='这是一个多选下拉组件' value={getMultiValue2()} onSelected={e => {setMultiValue2(e) }}>
<Option name='红色' value='Red' />
<Option name='黄色' value='Yellow' />
<Option name='蓝色' value='Blue' />
<Option name='白色' value='White' />
<Option name='黑色' value='Black' />
<Option name='青色' value='Cyan' />
<Option name='紫色' value='Purple' />
</Picker>
只读
<Picker multi readonly placeholder='这是一个多选下拉组件' value={getMultiValue2()} onSelected={e => {setMultiValue2(e) }}>
<Option name='红色' value='Red' />
<Option name='黄色' value='Yellow' />
<Option name='蓝色' value='Blue' />
<Option name='白色' value='White' />
<Option name='黑色' value='Black' />
<Option name='青色' value='Cyan' />
<Option name='紫色' value='Purple' />
</Picker>
搜索
<Picker multi max={3} placeholder='这是一个多选下拉组件' value={getMultiValue2()} onSelected={e => {setMultiValue2(e) }}>
<Option name='红色' value='Red' />
<Option name='黄色' value='Yellow' />
<Option name='蓝色' value='Blue' />
<Option name='白色' value='White' />
<Option name='黑色' value='Black' />
<Option name='青色' value='Cyan' />
<Option name='紫色' value='Purple' />
</Picker>
属性说明
表格
导入组件
import { createSignal, createMemo, Table, Meta } from 'zerl'
数据定义
const data = [
{
module: '用户管理',
name: '用户信息',
type: 'ILF',
fp: 10,
degree: '高'
},
{
module: '用户管理',
name: '添加、修改用户信息',
type: 'EI',
fp: 4,
degree: '高'
},
{
module: '用户管理',
name: '删除用户信息',
type: 'EI',
fp: 4,
degree: '高'
},
{
module: '用户管理',
name: '用户信息列表、条件筛选',
type: 'EQ',
fp: 4,
degree: '高'
},
{
module: '用户管理',
name: '用户详细信息',
type: 'EQ',
fp: 4,
degree: '高'
}
]
基本表格
<Table dataset={data}>
<Meta name='模块' key='module' />
<Meta name='功能点名称' key='name' />
<Meta name='功能点类型' key='type' />
<Meta name='UFP' key='fp' />
<Meta name='重用度' key='degree' />
</Table>
列属性设置
<Table dataset={data} width='700'>
<Meta name='模块' key='module' width='100' align='center' />
<Meta name='功能点名称' key='name' align='center' />
<Meta name='功能点类型' key='type' width='100' align='center' />
<Meta name='UFP' key='fp' width='100' align='center' />
<Meta name='重用度' key='degree' width='100' align='center' />
</Table>
定义单元格数据
<Table dataset={data}>
<Meta name='序号' width='50' align='center'>
{(_, index) => index + 1}
</Meta>
<Meta name='模块' key='module' width='100' align='center' />
<Meta name='功能点名称' key='name' align='center' />
<Meta name='功能点类型' key='type' width='100' align='center' />
<Meta name='UFP' key='fp' width='100' align='center' />
<Meta name='重用度' key='degree' width='100' align='center' />
<Meta name='费用(元)' width='120' align='center'>
{(item) => {
return (
(20000 *
item.fp *
1.21 *
(item.degree === '高' ? 1 / 3 : item.degree === '中' ? 2 / 3 : 1) *
7.19) /
174
).toFixed(2)
}}
</Meta>
</Table>
紧凑模式
<Table dataset={data} slim>
<Meta name='模块' key='module' width='100' align='center' />
<Meta name='功能点名称' key='name' align='center' />
<Meta name='功能点类型' key='type' width='100' align='center' />
<Meta name='UFP' key='fp' width='100' align='center' />
<Meta name='重用度' key='degree' width='100' align='center' />
</Table>
主题
深色
<Table dataset={data} theme='darken'>
<Meta name='模块' key='module' width='100' align='center' />
<Meta name='功能点名称' key='name' align='center' />
<Meta name='功能点类型' key='type' width='100' align='center' />
<Meta name='UFP' key='fp' width='100' align='center' />
<Meta name='重用度' key='degree' width='100' align='center' />
</Table>
浅色
<Table dataset={data} theme='lighten'>
<Meta name='模块' key='module' width='100' align='center' />
<Meta name='功能点名称' key='name' align='center' />
<Meta name='功能点类型' key='type' width='100' align='center' />
<Meta name='UFP' key='fp' width='100' align='center' />
<Meta name='重用度' key='degree' width='100' align='center' />
</Table>
属性说明
分页
导入组件
import { Pagi, PagiHandler } from 'zerl'
组件使用
let pagi: PagiHandler
const pageChange = (page) => dataloader(page)
const setLength = () => pagi.setLength
<Pagi ref={ (handler::PagiHandler) => pagi = handler} onClick={ (page:Number) => pageChange(page)}/>
属性说明
对话框
导入组件
import { Dialog } from 'zerl'
数据定义
const title = () => (
<div>
这里设置<span style={{ color: '#857' }}>标题</span>
</div>
)
const content = () => <div>这里设置对话框内容</div>
普通对话框
const dialog = Dialog({ content })
dialog.open() //打开对话框
设置对话框标题
const dialog = Dialog({ title, content })
dialog.open() //打开对话框
设置对话框尺寸
const dialog = Dialog({ title, content, width: 500, height: 300 })
dialog.open() //打开对话框
设置全屏对话框
const dialog = Dialog({ title, content, fullscreen: true })
dialog.open() //打开对话框
设置对话框背景滤镜
const dialog = Dialog({ title, content, filter: 30 })
dialog.open() //打开对话框
设置抽屉
从左往右打开
const dialog = Dialog({ title, content, direction: 'ltr' })
dialog.open() //打开对话框
从上向下打开
const dialog = Dialog({ title, content, direction: 'ttb' })
dialog.open() //打开对话框
从右向左打开
const dialog = Dialog({ title, content, direction: 'rtl' })
dialog.open() //打开对话框
从下往上打开
const dialog = Dialog({ title, content, direction: 'btt' })
dialog.open() //打开对话框
关闭对话框
dialog.close() //关闭对话框
属性说明
菜单
导入组件
import { Menu, Group, Item } from 'zerl'
基本菜单
<Menu width={200}>
<Group name='功能组一'>
<Item>功能一</Item>
<Item>功能二</Item>
</Group>
<Group name='功能组二' expanded>
<Item>功能三</Item>
<Item>功能四</Item>
</Group>
</Menu>
横向排列
<Menu width={400} direction='horizontal'>
<Group name='功能组一'>
<Item>功能一</Item>
<Item>功能二</Item>
</Group>
<Group name='功能组二' expanded>
<Item>功能三</Item>
<Item>功能四</Item>
</Group>
</Menu>
主题
深色
<Menu width={200} theme='darken'>
<Group name='功能组一'>
<Item>功能一</Item>
<Item>功能二</Item>
</Group>
<Group name='功能组二' expanded>
<Item>功能三</Item>
<Item>功能四</Item>
</Group>
</Menu>
简单
<Menu width={200} theme='simple'>
<Group name='功能组一'>
<Item>功能一</Item>
<Item>功能二</Item>
</Group>
<Group name='功能组二' expanded>
<Item>功能三</Item>
<Item>功能四</Item>
</Group>
</Menu>
属性说明
树
导入组件
import { Tree } from 'zerl'
初始化数据
let treeHandler: TreeHandler
const data = [
{
id: '1',
title: '新节点1',
children: [
{ id: '4', title: '新节点1.1' },
{ id: '5', title: '新节点1.2' }
]
},
{ id: '2', title: '新节点2' },
{ id: '3', title: '新节点2' }
]
组件的使用
<Tree title='Root Name' callback={(el:TreeHandler) => treeHandler = el} dataset={data}
/>
属性说明
页签
导入组件
import { Tabs, Tab } from 'zerl'
简单的页签
<Tabs>
<Tab name='tab1' label='页签一'>
<h1>这是页签一</h1>
</Tab>
<Tab name='tab2' label='页签二'>
<h1>这是页签二</h1>
</Tab>
<Tab name='tab3' label='页签三'>
<h1>这是页签三</h1>
</Tab>
</Tabs>
可关闭页签
<Tabs>
<Tab name='tab1' label='页签一' closable>
<h1>这是页签一</h1>
</Tab>
<Tab name='tab2' label='页签二'>
<h1>这是页签二</h1>
</Tab>
<Tab name='tab3' label='页签三' closable>
<h1>这是页签三</h1>
</Tab>
</Tabs>
动态页签
const [tabs, setTabs] = createSignal()
const tabHandler = () => {
tabs().create({
name: 'tab1',
label: '动态页签',
closable: true,
children: <h1>这是新页签,新添加的</h1>
})
}
<Tabs ref={el => setTabs(el)}>
属性说明
轮播
导入组件
import { Row, Swiper, Slide } from 'zerl'
简单的轮播组件
<Swiper>
<Slide>
<Row class='fill' center>
<h1>Slide A</h1>
</Row>
</Slide>
<Slide>
<Row class='fill' center>
<h1>Slide B</h1>
</Row>
</Slide>
</Swiper>
带导航的轮播组件
<Swiper nav>
<Slide>
<Row class='fill' center>
<h1>Slide A</h1>
</Row>
</Slide>
<Slide>
<Row class='fill' center>
<h1>Slide B</h1>
</Row>
</Slide>
</Swiper>
带分页的轮播组件
普通分页
<Swiper pagi>
<Slide>
<Row class='fill' center>
<h1>Slide A</h1>
</Row>
</Slide>
<Slide>
<Row class='fill' center>
<h1>Slide B</h1>
</Row>
</Slide>
</Swiper>
数字分页
<Swiper pagi='fraction'>
<Slide>
<Row class='fill' center>
<h1>Slide A</h1>
</Row>
</Slide>
<Slide>
<Row class='fill' center>
<h1>Slide B</h1>
</Row>
</Slide>
</Swiper>
进度条分页
<Swiper pagi='progressbar'>
<Slide>
<Row class='fill' center>
<h1>Slide A</h1>
</Row>
</Slide>
<Slide>
<Row class='fill' center>
<h1>Slide B</h1>
</Row>
</Slide>
</Swiper>
自定义分页
<Swiper pagi={(index, className) => (
<div class={`${className} ${styles.bullets}`}>{index + 1}</div>
)}
>
<Slide>
<Row class='fill' center>
<h1>Slide A</h1>
</Row>
</Slide>
<Slide>
<Row class='fill' center>
<h1>Slide B</h1>
</Row>
</Slide>
</Swiper>
纵向轮播
<Swiper pagi vertical>
<Slide>
<Row class='fill' center>
<h1>Slide A</h1>
</Row>
</Slide>
<Slide>
<Row class='fill' center>
<h1>Slide B</h1>
</Row>
</Slide>
</Swiper>
自动轮播
<Swiper pagi autoplay='5'>
<Slide>
<Row class='fill' center>
<h1>Slide A</h1>
</Row>
</Slide>
<Slide>
<Row class='fill' center>
<h1>Slide B</h1>
</Row>
</Slide>
</Swiper>
循环轮播
<Swiper pagi loop nav>
<Slide>
<Row class='fill' center>
<h1>Slide A</h1>
</Row>
</Slide>
<Slide>
<Row class='fill' center>
<h1>Slide B</h1>
</Row>
</Slide>
</Swiper>
属性说明
表单
表单的基本结构
获取表单句柄
const [getForm, setForm] = createSignal()
<form action='form.simple' ref={el => setForm1(el)}>
...
</form>
action 指向处理数据的模块,格式:[模块名称].[方法名称]
表单的验证
<form action='form.simple' ref={el => setForm2(el)}>
<Col>
<Row class='fill' middle>
<Text
title='标题'
name='title'
rule={val => val.length > 4}
placeholder='请输入信息标题'
hint='至少输入不少于5个字符'
/>
<Button
type='radio'
title='类型'
name='category'
rule='required'
hint='需要选择类型'
>
<Option name='内部信息' value='inner' />
<Option name='保密信息' value='secret' />
<Option name='对外信息' value='external' />
</Button>
</Row>
<Row class='fill' middle>
<Text
type='date'
title='显示日期'
name='displayDate'
rule='required'
placeholder='请输入显示日期'
hint='显示日期不能为空'
/>
<Button
type='check'
title='标签'
name='label'
rule={val => val.length > 2}
hint='至少选择三项'
>
<Option name='文娱' value='culture' />
<Option name='科技' value='tech' />
<Option name='教育' value='edu' />
<Option name='健康' value='health' />
<Option name='时事' value='news' />
</Button>
</Row>
<Row class='fill' middle>
<Text type='multi' title='内容' name='content' />
</Row>
<Row class='fill' right>
<Button
onClick={async () => {
const result = await form2().submit()
if (result) {
Confirm(
'表单提交信息',
<div>
{Object.keys(result).map(key => {
return (
<Row>
<div
style={{
width: '120px',
'font-weight': 'bold'
}}
>
{key}
</div>
{JSON.stringify(result[key])}
</Row>
)
})}
</div>
)
}
}}
>
保存
</Button>
</Row>
</Col>
</form>
文件
导入组件
import { FileList, File } from 'zerl'
简单的文件组件
<FileList />
文件多选
<FileList multiple/>
初始化文件列表
<FileList multiple>
<File id='2fed96c8130e804e8c6f940981370fb7'></File>
<File id='096f60ff49ec8b3039902e190c26ee10'></File>
</FileList>
文件上传
let fileList
<FileList ref={el=>fileList = el}/>
<Button onClick={async ()=>{const fileIds = await fileList.upload()}}/>
属性说明
服务组件
逻辑
组件功能
实现服务端数据处理的逻辑组件,用于与浏览器或其他客户端进行数据交互
模块文件
在模块文件头部添加 #!module 标志
组件使用
组件定义
#!module
import type { Context } from 'zerl'
export default class {
public async save(data: Schema) {
return data
}
public async load(data: queryData, ctx: Context) {
return [data]
}
}
UI 模块调用
import { Button } from 'zerl'
import Service from './Service'
const service = new Service()
<Button onClick = {async ()=> {
const remoteData = await service.save({data: ‘test data’})
}
}>
get remote data
</Button>
远程组件调用
// 普通远程组件调用
import Service from 'rpc://192.168.1.10:8080/Service'
// ssl远程组件调用
import Service from 'rpcs://192.168.1.11:8080?Service'
任务
组件功能
应用启动时一并启动的工作任务线程,可设置一次性运行任务与周期性运行任务
任务文件
在任务文件头部添加 #!job 标志
组件使用
#!job
import { Once, Timer } from "zerl"
import type { Context, ServerInstance } from "zerl"
export default class {
// 声明后自动注入当前启动的服务实例
private getCurrentInstance!: ServerInstance
@Once()
public init(){
// 获取服务实例
const server = this.getCurrentInstance()
// 添加新的服务中间件
server.use(async (ctx: Context, next:any) => {
if(/^\/api/?/.test(ctx.url)){
ctx.body = "append new middleware"
} else {
await next()
}
})
}
@Timer(60)
public synchronize(){
...
}
}
访问控制
组件功能
服务模块的会根据客户端发起的请求进行访问权限验证,使用@Expose 装饰器则不验证访问权限
授权与撤销权限
#!module
import { Expose, Grent, Revoke } from 'zerl'
import type { Context } from 'zerl'
export default class {
// 访问时,不验证UI端访问权限
@Expose public async login(data, ctx: Context) {
Grent(ctx) //授权访问权限
}
// 只有授权后,UI端才能访问此方法
public async logout(data: ant, ctx: Context) {
Revoke(ctx) //回收访问权限
}
}
MySQL
组件使用
#!module
import { MySQL, Schema } from 'zerl'
@Schema('TABLE', 'ID')
export default class extends MySQL {
// 数据插入
public async insertData(data: any) {
this.insert(data)
return { result: 'ok' }
}
// 数据更新
public async updateData(data: any) {
this.update(data)
return { result: 'ok' }
}
// 数据删除
public async removeData(data: any) {
this.remove({ ID: data.id })
return { result: 'ok' }
}
// 数据查询
public async queryData(data: any) {
return await this.query('SELECT * FROM TABLE WHERE ID=?', [data.id])
}
// 事务处理
public async transaction(data: any) {
const flow = this.flow()
flow.insert(data)
flow.update({ name: 'new name' })
flow.pipe('DELETE FROM TABLE WHERE ID = 3')
await flow.exec()
return { result: 'ok' }
}
}
SQL 文件
命名
[文件名].sql
文件格式
--table.sql
--define query
SELECT * FROM TABLE;
每个 sql 语句结束后必须加“;”,sql 语句头部需要定义语句的变量名,声明格式:--define xxx
调用 SQL 语句
this.query('sql:table.query')
Redis
组件使用
#!module
import { Redis } from "zerl"
// 数据模型
export default class{
public async load(data: any){
return await Redis(async (client) => {
const { keys } = await
client.scan(0, { MATCH: "*,name,*" })
return client.hGetAll(keys[0])
}
}
}
操作命令参考Redis 命令文档
装饰器
MySQL 控制表定义
@Schema([表名],[主键])
// 设置表定义后,所有MySQL操作都默认使用设置的表名和主键名
#!module
@Schema('TABLE', 'ID')
export default class extends MySQL {
...
}
访问暴露(逻辑组件内使用)
// 设置访问暴露装饰器后,该方法访问对外完全公开,不受权限控制影响。
@Expose public async login(data: any){
...
}
一次性执行方法(任务组件内使用)
// 应用程序启动时立即启动
@Once() public runAtStart(){
...
}
// 应用程序启动时,延迟5秒启动
@Once(5) public runAtStart(){
...
}
定时执行方法(任务组件内使用)
// 应用程序启动时,触发启动定时器,方法每5秒执行一次
@Timer(5) public tikTok(){
...
}
扩展类型
日期+
格式化日期
// fmt: 日期格式 y-年,M-月,d-日,h-小时,m-分钟,s-秒,q-季度,S-毫秒
Date.format(fmt: string): string
日期推算
// year 当前日期累加年数
// month 当前日期累加月数
// day 当前日期累加日数
Date.calc(year: number, month: number, day: number): Date
输出日期文本格式 yyyy-MM-dd
// year 当前日期累加年数
// month 当前日期累加月数
// day 当前日期累加日数
Date.date(year?: number, month?: number, day?: number): string
输出时间文本格式 hh:mm:ss
Date.time(): string
输出日期时间文本格式 yyyy-MM-dd hh:mm:ss
// year 当前日期累加年数
// month 当前日期累加月数
// day 当前日期累加日数
Date.datetime(year?: number, month?: number, day?: number): string
月份数量计算
// reference 比较日期 yyyy-MM-dd hh:mm:ss
// 返回相隔月份数量
Date.monthdiff(reference: string | Date): number
时间戳
Date.timestamp(): number
字符串+
驼峰格式转连接符格式
// separator: 连接字符
String.camelfalt(separator: string): string
定长前补零
// digit: 字符串总长度
String.prefix(digit:number): string
Base64 字符串转 Blob 类型
String.blob(): Blob
字符串加密(服务组件内使用)
String.encode(): string
字符串解密(服务组件内使用)
String.decode(): string
生成 MD5 摘要信息
String.md5(): string
数字+
千分位格式
Number.thou(): string
数组+
数组分组
// algorithm: 分组函数 function(element: any): string, element为数组中的每个元素的值,返回值为分组名称
Array.group(algorithm: Function): object