0.3.2 • Published 2 months ago

zerl v0.3.2

Weekly downloads
-
License
ISC
Repository
-
Last release
2 months ago

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 部署环境下实现非阻塞式启动
  1. 使用 vi/vim/nano 或其他编辑工具创建 run.sh 文件
  2. 粘贴以下内容并修改应用程序名称进行保存
  3. 运行./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
0.3.2

2 months ago

0.3.1

2 months ago

0.3.0

4 months ago

0.2.25

4 months ago

0.2.24

4 months ago

0.2.23

4 months ago

0.2.22

4 months ago

0.2.21

4 months ago

0.2.20

4 months ago

0.2.19

5 months ago

0.2.18

5 months ago

0.2.17

5 months ago

0.2.16

5 months ago

0.2.15

5 months ago

0.2.14

5 months ago

0.2.13

5 months ago

0.2.12

5 months ago

0.2.11

5 months ago

0.2.10

5 months ago

0.2.9

5 months ago

0.2.8

5 months ago

0.2.7

5 months ago

0.2.6

5 months ago

0.2.5

6 months ago

0.2.4

6 months ago

0.2.3

6 months ago

0.2.2

6 months ago

0.2.1

6 months ago

0.2.0

6 months ago

0.1.13

6 months ago

0.1.12

6 months ago

0.1.11

6 months ago

0.1.10

6 months ago

0.1.9

6 months ago

0.1.8

6 months ago

0.1.7

6 months ago

0.1.6

6 months ago

0.1.5

6 months ago

0.1.4

6 months ago

0.1.3

6 months ago

0.1.2

7 months ago

0.1.1

7 months ago

0.1.0

7 months ago

0.0.9

7 months ago

0.0.8

7 months ago

0.0.7

7 months ago

0.0.6

7 months ago

0.0.5

7 months ago

0.0.4

7 months ago

0.0.3

7 months ago

0.0.2

7 months ago

0.0.1

7 months ago