武林至尊,宝刀 React
Vue 不出,谁与争锋。
天下武功,无坚不摧,唯 Solid 不破。
-
ZERL 是基于 Solid.js MVVM 框架打造的全栈技术开发框架。
-
ZERL 全面采用 Typescript 进行开发,Typescript 能提供更良好的协作体验,提供实时的代码校验,实现更高质量的交付。
-
ZERL 将更深层次地诠释全栈技术。全栈,一般定义是对工程师技术能力的泛指。是一个组织开发成本控制的有效手段。因此全栈是个人能力的追求,也是组织发展的需要。
- 保留了 MVVM 框架的优势,并非只是实现了 SSR
- 实现了技术栈的统一,而非简单的语言统一或交互标准统一
- 提供了简便的工程构建和发布部署工具包
- 构建了云原生开发模式,客户端可直接导入服务端模块并在客户端使用,无需实现 api
$ npx zerli@latest create zerlapp
或
$ npx zerli@latest -c zerlapp
- 运行开发环境
$ cd zerlapp
$ npx zerli@latest
或
$ npm run dev
UI 模块运行于浏览器上,开发规范及安全性要求须符合 W3C 标准。
ZERL 所有 UI 组件构建于 solid.js 的 MVVM 框架之上,zerl 的所有组件完全兼容 solid.js 所提供的 API。了解 solid.js API 请前往solidjs.com
// 导入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。
// 导入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'
import { Col, Row, Line } from 'zerl'
<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>
名称 | 属性 | 描述 |
---|---|---|
middle | Boolean | 设置布局垂直居中 |
top | Boolean | 设置布局顶部对齐 |
bottom | Boolean | 设置布局底部对齐 |
center | Boolean | 设置布局水平居中 |
right | Boolean | 设置布局靠右对齐 |
class | String | 设置布局样式模块 |
style | String/CSS Object | 设置布局样式 |
<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>
名称 | 属性 | 描述 |
---|---|---|
middle | Boolean | 设置布局垂直居中 |
top | Boolean | 设置布局顶部对齐 |
bottom | Boolean | 设置布局底部对齐 |
center | Boolean | 设置布局水平居中 |
right | Boolean | 设置布局靠右对齐 |
class | String | 设置布局样式模块 |
style | String/CSS对象 | 设置布局样式 |
<Line style={{ width: '400px' }} />
<Line vertical style={{ height: '50px' }} />
名称 | 属性 | 描述 |
---|---|---|
vertical | Boolean | 设置为垂直分割线 |
class | String | 设置分割线样式模块 |
style | String/CSS对象 | 设置分割线样式 |
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')
名称 | 属性 | 描述 |
---|---|---|
default | String | 设置默认路由页面路径,路径为"/"时会跳转到该页面 |
routers | Object | 采用import.meta.glob方式导入所有路由页面对象 |
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>
分类 | 名称 | 属性 | 描述 |
---|---|---|---|
缩小 | mini | Boolean | 按钮缩小为小号 |
缩小 | tiny | Boolean | 按钮缩小为超小号 |
状态 | success | Boolean | 按钮设置为成功状态 |
状态 | warning | Boolean | 按钮设置为警告状态 |
状态 | danger | Boolean | 按钮设置为危险状态 |
状态 | info | Boolean | 按钮设置为信息状态 |
状态 | simple | Boolean | 按钮设置为简单状态 |
形状 | rounded | Boolean | 按钮设置为圆角形状 |
形状 | circle | Boolean | 按钮设置为圆形 |
形状 | square | Boolean | 按钮设置为方形 |
模式 | lighten | Boolean | 按钮设置为明亮模式 |
样式 | style | String或CSS对象 | 设置按钮样式 |
样式 | class | String | 设置按钮样式模块 |
事件 | onClick | Function | 按钮点击事件,function(e){...},e为当前按钮点击事件对象 |
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>
组件 | 名称 | 属性 | 描述 |
---|---|---|---|
Button | name | String | 设置单选按钮名称 |
Button | type | String | 设置按钮类型,type="radio"为单选按钮 |
Button | value | Number/String | 设置按钮当前选项的值 |
Button | readonly | Boolean | 把单选按钮设置为只读状态 |
Button | vertical | Boolean | 把单选按钮设置为纵向排列模式 |
Button | onChange | Function | 选择内容时触发的事件,function(e){...},e为当前选择项的值信息,类型为Number/String |
Option | name | String | 设置单选按钮选项的显示名称 |
Option | value | Number/String | 设置单选按钮选项的值 |
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>
组件 | 名称 | 属性 | 描述 |
---|---|---|---|
Button | name | String | 设置单选按钮名称 |
Button | type | String | 设置按钮类型,type="check"为单选按钮 |
Button | value | Number[]/String[] | 设置按钮当前选项的值 |
Button | readonly | Boolean | 把多选按钮设置为只读状态 |
Button | vertical | Boolean | 把多选按钮设置为纵向排列模式 |
Button | onChange | Function | 选择内容时触发的事件,function(e){...},e为当前选择项的值信息,类型为Number[]/String[] |
Option | name | String | 设置多选按钮选项的显示名称 |
Option | value | Number/String | 设置多选按钮选项的值 |
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' })
名称 | 属性 | 描述 |
---|---|---|
message | String/JSX.Element | 展示的消息内容,可以是文本消息内容/组件消息内容 |
options | 选项参数 | 设置消息提示选项参数 |
名称 | 属性 | 描述 |
---|---|---|
type | String | 消息提示样式,可选值为:default-默认样式,success-成功样式,warning-警告样式,danger-危险样式,info-信息样式,默认为default |
mode | String | 消息显示模式,可选值为:lighten-浅色模式,darken-深色模式,默认为lighten |
diretion | String | 消息弹出方向,可选值为:ttb-上方弹出,btt-下方弹出,rttl-右上方弹出,rbtt-右下方弹出,默认为ttb |
delay | Number | 消息展示时间,单位:秒,不设置时消息需要手动关闭 |
import { Confirm } from 'zerl'
async function() {
const res = await Confirm('确认提醒信息', () => (
<div>
<h3>提示信息</h3>
<span>这是一条提示信息</span>
</div>
))
}
名称 | 属性 | 描述 |
---|---|---|
title | String/JSX.Element | 提示信息标题 |
content | String/JSX.Element | 提示信息内容 |
名称 | 属性 | 描述 |
---|---|---|
result | Promise(Boolean) | 点击确认返回true,点击取消返回false |
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])}/>
名称 | 属性 | 描述 |
---|---|---|
type | String | 设置输入组件类型。"text":文本输入框;"password":密码输入框;"digit":数字输入框;multi:为多行文本框;"date":日期输入框;"datetime":日期时间输入框;"time":时间输入框;"daterange":日期范围输入框;"datetimerange":日期时间范围输入框;"rich":富文本编辑器。默认为"text"。 |
name | String | 输入组件名称 |
value | Number/String | 输入组件显示的值。type属性为"date"、"datetime"、"time"时输入日期的文本格式/日期对象;type属性为"daterange"、"datetimerange"时输入日期的文本格式数组/日期对象数组,数组第一个元素为开始日期,第二个元素为结束日期。 |
readonly | Boolean | 设置输入组件为只读状态 |
rows | Number | type属性为multi时生效,设置输入组件行数高度 |
all | Boolean | type属性为rich时生效,设置全功能富文本编辑器 |
image-width | String | type属性为rich时生效,设置上传图片压缩的宽度 |
image-height | String | type属性为rich时生效,设置上传图片压缩的高度 |
video-width | String | type属性为rich时生效,设置上传视频显示的宽度 |
video-height | String | type属性为rich时生效,设置上传视频显示的高度 |
rule | required/RegExp/Function | 设置为"required"时,该输入组件为必填;设置正则表达式时验证输入组件的输入合法性;验证函数则自定义验证规则,返回true时为验证通过 |
title | String | 设置输入组件的标题信息 |
onInput | Function | 输入组件输入内容时触发的事件,function(e){...},e为当前触发输入事件的对象。type属性为"date"、"datetime"、"time"时返回日期的文本格式;"daterange"、"datetimerange"时输入日期的文本格式数组,数组第一个元素为开始日期,第二个元素为结束日期。 |
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>
组件 | 名称 | 属性 | 描述 |
---|---|---|---|
Picker | multi | Boolean | 设置选择类型,multi=true为多选 |
Picker | value | String | 输入组件名称 |
Picker | value | Number/String/Number[]/String[] | 设置选择组件当前选项的值,multi=false时,传入Number/String的值;multi=true时传入Number[]/String[] |
Picker | readonly | Boolean | 设置选择组件为只读状态 |
Picker | max | Number | 设置选项显示的项目数量,设置max的值时选项出现筛选功能。 |
Picker | onSelected | Function | 选择内容时触发的事件,function(e){...},multi=false时,e为当前选择项的Number/String的值;multi=true时,e为当前选择项的Number[]/String[]。 |
Option | name | String | 设置选项的显示名称 |
Option | value | Number/String | 设置选项的值 |
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>
组件 | 名称 | 属性 | 描述 |
---|---|---|---|
Table | dataset | Object[] | 表格内呈现的数据集 |
Table | width | String/Number | 表格宽度 |
Table | slim | Boolean | 设置表格为紧凑模式 |
Table | theme | String | 设置表格主题模式。现代主题:theme="modern";浅色主题:theme="lighten";深色主题:theme="darken" |
Meta | name | String | 列显示名称 |
Meta | key | String | 映射数据键值的名称 |
Meta | align | String | 单元格对齐模式设置。left:单元格左对齐;right:单元格右对齐;center:单元格居中对齐 |
Meta | width | String/Number | 固定列宽设置,不设置为自适应列宽 |
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)}/>
组件 | 名称 | 属性 | 描述 |
---|---|---|---|
Pagi | ref | Function | 获取分页组件句柄,function(hanler:PagiHandler){...} |
Pagi | page | String/Number | 设置默认页码,默认值:1 |
Pagi | size | String/Number | 设置每页记录数,默认值:10 |
Pagi | length | String/Number | 设置设置记录总数 |
Pagi | count | String/Number | 设置分页器显示按钮数量,默认值:5 |
Pagi | onClick | Function | 分页器点击按钮时触发的事件,参数为相对应的页码,function(page: Number){...} |
PagiHandler | setPage | Function | 设置页码,setPage(page: number) |
PagiHandler | getPage | Function | 获取当前页码 |
PagiHandler | setSize | Function | 设置每页记录条数,setSize(size: number) |
PagiHandler | getSize | Function | 获取当前记录条数 |
PagiHandler | setLength | Function | 设置记录数,setLength(length: number) |
PagiHandler | getLength | Function | 获取当前设置的记录数 |
PagiHandler | setCount | Function | 设置显示页码按钮数,setCount(count: number) |
PagiHandler | getCount | Function | 获取当前显示页码按钮数 |
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() //关闭对话框
名称 | 属性 | 描述 |
---|---|---|
title | JSXElement | 对话框标题内容。 |
content | JSXElement | 对话框内容。 |
width | String/Number | 设置对话框宽度。设置fullscreen时该属性无效 |
height | JSXElement | 设置对话框高度。设置fullscreen时该属性无效 |
filter | Number | 设置对话框对背景的滤镜效果,取值为[1-100]。设置fullscreen时该属性无效 |
fullscreen | Boolean | 设置对话框全屏状态 |
direction | String | 设置对话框高度。设置fullscreen时该属性无效 |
filter | Number | 设置抽屉模式,ttb:从上到下打开;ltr:从左到右打开;rtl:从右到左打开;btt:从下到上打开。 |
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>
组件 | 名称 | 属性 | 描述 |
---|---|---|---|
Menu | width | String/Number | 设置菜单固定宽度。默认宽度自动填充。 |
Menu | height | String/Number | 设置菜单固定高度。默认高度自动填充。 |
Menu | top | Boolean | 设置菜单是否置顶显示。 |
Menu | direction | Boolean | 设置菜单扩展方向。direction="vertical":菜单垂直扩展;direction="horizontal":菜单水平扩展。默认为"vertical" |
Menu | theme | String | 设置菜单主题模式。theme="darken":菜单设置为深色模式;theme="lighten":菜单设置为浅色模式;theme="simple":菜单设置为简单主题模式。默认为"lighten" |
Group | name | String | 设置菜单分组名称。 |
Group | expanded | Boolean | 设置菜单分组时候是否展开。direction="vertical"时有效。 |
Item | onClick | Function | 菜单项点击时触发的事件,function(e){...},e为当前触发点击事件的对象。 |
Item | select | Boolean | 设置是否选中 |
Item | route | String | 设置点击跳转的路由,设置此属性时onClick失效 |
Item | icon | String | 设置菜单图标 |
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}
/>
组件 | 名称 | 属性 | 描述 |
---|---|---|---|
Tree | title | String | 树标题,根节点标题名称 |
Tree | callback | Function | 返回节点控制句柄,function(h:TreeHandler){...} |
Tree | dataset | NodeData[] | 初始化树数据,非必要项 |
TreeHandler | eventName | String | 获取树回调事件名称,'onLoad' | 'nodeClick' | 'expanded' | 'unexpanded' |
TreeHandler | getCurrentID | String | 获取当前选择节点的ID |
TreeHandler | appendChild | Function | 在当前节点下加载新的子节点,function(id:string, title:string){...} |
TreeHandler | appendChildren | Function | 在当前节点下加载所有子节点,function(node: {id:string, title:string}[]){...} |
TreeHandler | rename | Function | 修改当前节点的标题内容,function(title:string){...} |
TreeHandler | remove | Function | 删除当前选择节点,function(){...} |
NodeData | id | String | 树节点ID |
NodeData | title | String | 树节点标题 |
NodeData | children | NodeData[] | 子节点数据 |
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)}>
组件 | 名称 | 属性 | 描述 |
---|---|---|---|
Tabs | ref | Function | 组件属性,通过方法获取Tabs句柄,function(tabs: Tabs){...} |
Tabs | create | Function | 组件方法,动态创建页签,类型:方法;参数:name-页签名称,唯一标识,label-页签显示名称,closable-页签是否可以关闭,children-页签内显示内容。 |
Tabs | getElement | Function | 组件方法,返回页签HTML对象 |
Tab | name | String | 页签名称,唯一标识 |
Tab | label | String/JSXElement | 页签显示名称 |
Tab | closable | Boolean | 页签是否可以关闭 |
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>
组件 | 名称 | 属性 | 描述 |
---|---|---|---|
Swiper | class | String | 设置轮播组件样式模块 |
Swiper | style | String/CSS对象 | 设置轮播组件样式 |
Swiper | nav | Boolean | 设置导航按钮 |
Swiper | loop | Boolean | 设置循环播放 |
Swiper | autoplay | Boolean/Number/String | 设置自动播放,设置数值/字符时,设置停留时间,单位(秒) |
Swiper | vertical | Boolean | 设置为纵向轮播模式 |
Swiper | pagi | Boolean/String/Function | Boolean类型时,显示分页小圆点。String类型时,progressbar-用进度条样式显示分页,fraction-用数字样式显示分页。Function类型时,自定义样式-function(index,className){...},index-页码,className-基础样式模块名称。 |
Slide | class | String | 设置轮播组件样式模块 |
Slide | style | String/CSS对象 | 设置轮播组件样式 |
获取表单句柄
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()}}/>
组件 | 名称 | 属性 | 描述 |
---|---|---|---|
FileList | ref | Function | 属性,获取文件对象句柄,function(el: FileList){...} |
FileList | upload | Function | 文件上传函数,上传成功后返回文件ID |
FileList | files | Function | 返回组件中的文件信息 |
FileList | accept | String | 设置筛选可选择文件的类型 |
FileList | multiple | Boolean | 设置是否可以上传多个文件 |
FileList | title | String | 设置组件的标题 |
File | id | String | 文件指针(ID),该ID在文件上传后返回 |
实现服务端数据处理的逻辑组件,用于与浏览器或其他客户端进行数据交互
在模块文件头部添加 #!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]
}
}
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) //回收访问权限
}
}
#!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
--table.sql
--define query
SELECT * FROM TABLE;
每个 sql 语句结束后必须加“;”,sql 语句头部需要定义语句的变量名,声明格式:--define xxx
this.query('sql:table.query')
#!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 命令文档
@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
// year 当前日期累加年数
// month 当前日期累加月数
// day 当前日期累加日数
Date.date(year?: number, month?: number, day?: number): string
Date.time(): string
// 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
String.blob(): Blob
String.encode(): string
String.decode(): string
String.md5(): string
Number.thou(): string
// algorithm: 分组函数 function(element: any): string, element为数组中的每个元素的值,返回值为分组名称
Array.group(algorithm: Function): object