feat: hyperf-admin init

This commit is contained in:
daodao97
2020-06-16 22:33:55 +08:00
commit 8d89932c98
198 changed files with 20104 additions and 0 deletions

0
docs/.nojekyll Normal file
View File

34
docs/README.md Normal file
View File

@@ -0,0 +1,34 @@
`hyperf-admin`是前后端分离的后台管理系统, 前端基于`vue``vue-admin-template`, 针对后台业务`列表`, `表单`等场景封装了大量业务组件, 后端基于`hyperf`实现, 整体思路是后端定义页面渲染规则, 前端页面渲染时首先拉取配置, 然后组件根据具体配置完成页面渲染, 方便开发者仅做少量的配置工作就能完成常见的`CRUD`工作, 同时支持自定义组件和自定义页面, 以开发更为复杂的页面.
![hyperf-admin架构](https://cdn.jsdelivr.net/gh/daodao97/FigureBed@master/uPic/sJaJti.png)
前端为`vue multiple page`多页模式, 可以按模块打包, 默认包含两个模块`default` 默认模块, `system`系统管理模块, 绝大部分业务组件在`src/components`目录, 前端文档详见 [这里](/frontend/)
后端为`composer包`模式, 目前包含组件
- 基础组件
- `composer require hyperf-admin/base-utils` hyperf-admin的基础组件包, 脚手架主要功能封装
- `composer require hyperf-admin/validation` 参数验证包, 对规则和参数提示做了较多优化
- `composer require hyperf-admin/alert-manager` 企微/钉钉机器人报警包
- `composer require hyperf-admin/rule-engine` 规则引擎
- `composer require hyperf-admin/event-bus` mq/nsq/kafka消息派发器
- `composer require hyperf-admin/process-manager` 进程管理组件
- 业务组件 (业务组件为包含特定业务功能的包)
- `composer require hyperf-admin/admin` 系统管理业务包
- `composer require hyperf-admin/dev-tools` 开发者工具包, 主要是代码生成, 辅助开发
- `composer require hyperf-admin/cron-center` 定时任务管理, 后台化管理任务
- `composer require hyperf-admin/data-focus` 数据面板模块, 帮你快速制作数据大盘
后端的详细文档见[这里](/backend/)
## 依赖 & 参考
- 前端
- [Vue](https://github.com/vuejs/vue)
- [ElementUI](https://github.com/ElemeFE/element)
- [FormCreate](http://www.form-create.com/v2/guide)
- [vue-admin-template](https://github.com/PanJiaChen/vue-admin-template)
- [Vue 渲染函数 & JSX](https://cn.vuejs.org/v2/guide/render-function.html)
- 后端
- [Hyperf](http://hyperf.wiki/)
- [Swoole](http://wiki.swoole.com)

12
docs/_coverpage.md Normal file
View File

@@ -0,0 +1,12 @@
![logo](_media/icon.svg)
# docsify
> A magical documentation site generator.
* Simple and lightweight (~12kb gzipped)
* Multiple themes
* Not build static html files
[GitHub](https://github.com/docsifyjs/docsify/)
[Get Started](#quick-start)

0
docs/_navbar.md Normal file
View File

21
docs/_sidebar.md Normal file
View File

@@ -0,0 +1,21 @@
* 入门
* [介绍](guide/desc.md)
* [安装](guide/install.md)
* [开发样例](guide/dev_example.md)
* 后端
* [脚手架](backend/scaffold.md)
* [表单详解](backend/form.md)
* [列表详解](backend/list.md)
* [按钮详解](backend/super-button.md)
* [通用配置](backend/common-config.md)
* [辅助函数](backend/functions.md)
* 业务组件
* [DevTools-开发者工具](backend/components/dev-tools.md)
* [DataFocus-数据面板](backend/components/data-focus.md)
* [CronCenter-任务中心](backend/components/cron-center.md)
* [开发一个业务组件](backend/make_component.md)
* 前端
* [表单](frontend/form.md)
* [列表](frontend/list.md)
* [图表](frontend/chart.md)

View File

@@ -0,0 +1,38 @@
通用配置在只需要使用表单搜集信息, 没有过多业务逻辑和校验规则时使用, 可以无需开发, 完成表单的定义和使用
1. 定义表单 http://localhost:9528/system/#/cconf/list, 此处表单规则同控制器中的form定义
![](http://qupinapptest.oss-cn-beijing.aliyuncs.com/img/DGD103.png)
2. 投放表单, 创建 `/***/cconf_{1中的表单名称}` 即可通过该路由访问
![](http://qupinapptest.oss-cn-beijing.aliyuncs.com/img/b9Pw1z.png)
3. 访问 http://localhost:9528/hyperf/#/lucky/cconf_lucky_round
![](http://qupinapptest.oss-cn-beijing.aliyuncs.com/img/I3pgnz.png)
数据存储位置`hyperf_admin.common_config`
```mysql
CREATE TABLE `common_config` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`namespace` varchar(50) NOT NULL DEFAULT '' COMMENT '命名空间, 字母',
`name` varchar(100) NOT NULL COMMENT '配置名, 字母',
`title` varchar(100) NOT NULL DEFAULT '' COMMENT '可读配置名',
`remark` varchar(100) NOT NULL DEFAULT '' COMMENT '备注',
`rules` text COMMENT '配置规则描述',
`value` text COMMENT '具体配置值 key:value',
`permissions` text COMMENT '权限',
`create_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `unique` (`name`,`namespace`),
KEY `namespace` (`namespace`),
KEY `update_at` (`update_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='通用配置';
```
可以通过 http://localhost:9528/system/#/cconf/list 管理命名空间
系统中可以使用`Rcok\BaseUtils\Service\CommonConfig`获取相应的提交数据

View File

@@ -0,0 +1,7 @@
脚本作业的管理中心, 可以在代码中实现`class`类型, `command`类型的脚本作业, 在后台添加相关任务, 即可在相应脚本机上执行
可以对任务的入口, 执行规划, 执行节点, 执行参数 等进行配置, 也可在列表主动触发任务
作业必须基于`App\Util\CronCenter\ClassJobAbstract`, 或`App/Util/CronCenter/CommandJobAbstract.php`抽象类进行实现, 才可进行执行状态的跟踪
`CronCenter`的实现基于`hyperf-crontab`进行实现, 具体代码在`app/Util/CronCenter`, 更多细节可查看[文档](https://hyperf.wiki/#/zh-cn/crontab)

View File

@@ -0,0 +1,97 @@
数据大盘
DataFocus (焦点数据) 用途是帮助大家快速构建一个如下的数据面板, 对数据可视化做了大量封装, 让开发者只用关心数据来源和数据处理, 无需处理复杂的图标构建, 即可轻松制作出漂亮的看板, 为业务决策这提供更直观的参考.
数据面板的定义
数据面板中可以定义, `sql`, `json`, `php代码`, `markdown` , `html` 等级数据格式, 通过统一转换由相应前端组件渲染成`图标``列表`
目前支持的图标样式`LineChart`, `ColumnChart`, `PieChart`, `NumberPanel` 分别会渲染为`曲线图`, `柱状图`, `饼图`, `数字面板`, `列表`
### sql节点
1. 定义改节点的属性
1. id 节点名称, 必须, 不可重复
2. dsn sql 查询说使用的dsn, 可用dsn的范围是 hyperf/config/databaes 中 DataFocus Dsn 中定义的数据源
3. chart 图标类型, 格式为`图表名|X轴,Y轴1,Y轴2,…`, 图标名为必须
4. show_table 默认 `false`, 为`true`时除了渲染图表, 还会渲染数据列表
5. table_plugin 表级的插件, 可以对结果数据做二次干预, 可以调用 DataFocus/plugin_fucntion 中定义的全局插件, 也可在调用当前面板中自定义插件
1. 自定义插件为 当前页面的一段`php function`代码
2. 面板中的所有自定义`php`方法, 必须以`df_`开头
6. span 布局 总24的栅格布局, 具体参见
2. 节点内容, 填写构造数据的查询`sql`即可
1. 一个`<sql></slq>` 节点 只能定义一个`sql`语句
2. 只能使用`select`语句
下面的样例中定义的一个以日期`date``X轴`, 其他数据指标为`Y轴`的曲线图
```php
<sql id="近30日访问趋势" dsn="hyperf_admin" chart='LineChart|date' >
select
visitor_uv as "人数",
visitor_pv as "次数"
data_date as date
from
visitor_log
where
data_date >= {{ date('Y-m-d', strtotime('-30 day')) }}
group by date
</sql>
```
其他类型的图标也基本类似, 只用调整相应的`chart`数据即可, 比如下面的饼图
```php
<sql id="今日访问地区占比" dsn="rock_admin" chart="PieChart" table_plugin="df_list_transposition:地区,数量" span="12">
select
area as "地区",
count(1) as "数量"
from
visitor_log
where
data_date >= {{ date('Y-m-d', strtotime('-1 day')) }}
group by area
</sql>
```
3. 模板变量
细心的同学可能已经发现, 上面的`sql`内容中使用了`{{ date('Y-m-d', strtotime('-1 day')) }}`这样的变量定义,模板变量的定义类似 `twig` 语法, 本质上是把花括号的内容转换为`php`代码进行运算, 然后替换模板变量, 执行`sql`.
格式: `{{ func|pip1|pip2 }}`
`func` 为数据的产生源头, 是必须的. 后面`|` 竖线分隔的管道, 让会源头输出的结构做二次处理, 最终替换到模板中
次数模板替换时, 会默认给变量加引号, 比如上方的`sql` 最终会替换为 `data >= '2020-06-11'`, 若需要原样输出, 可只用 `raw` 管道
管道也可以是一个自定义`php` 方法
更多样例见`系统管理/DataFocus/数据面板` 菜单下
### json
节点属性同 sql
节点内容, 可以填入`json`格式数据
```php
<json>
{
"lable":"value"
}
</json>
```
### php
php并非一个单独节点, 而是可以作为一个片段, 迁移任意节点内
```php
<json>
<?php return json_encode(df_******());?>
</json>
```
### md
### html

View File

@@ -0,0 +1,11 @@
## 代码自动生成工具
针对 数据库, `Model`, `Controller` 通用模型, 可以使用后台提供的`代码生成工具`来初始化大部分代码.
![9fnAUL(1)](https://cdn.jsdelivr.net/gh/daodao97/FigureBed@master/uPic/9fnAUL%20%281%29.png)
连接池对应`config.autoload.databases` 中配置的可用链接, 选择好`连接池`, `数据库`, `表` 后下方表单会根据表结构字段渲染, 完成具体字段的配置, 点击提交.
![b4lL49](http://qupinapptest.oss-cn-beijing.aliyuncs.com/img/b4lL49.png)
相应的`Modle`, `Controller` 便已创建成功.

430
docs/backend/form.md Normal file
View File

@@ -0,0 +1,430 @@
## 字段规则
```php
'field|字段名称' => [
// 字段验证规则,
'rule' => 'required|max:',
// 请参考http://www.form-create.com/v2/element-ui/components/input.html
'type' => 'input',
// 表单默认值
'default' => '',
'info' => '字段备注',
// 只读属性,当编辑时有效
'readonly' => true,
// 表单选项只有支持options选项的组件设置才有效可以定义一个callback方法可以参考formOptionsConvert方法
'options' => [],
// 其他组件属性请参考具体组件的props的定义
'props' => [],
// 定义依赖项
'depend' => [
'field' => 'target_type',
'value' => [],
],
// col 布局规则 http://www.form-create.com/v2/element-ui/col.html
'col' => [
// 表单长度
'span' => 12,
// 标签宽度
'labelWidth' => 150,
],
// 动态修改其他字段规则 详见下方联动小节
'compute' => [
"will_set_field" => [
"when" => ['=', 1],
"set" => [
//
]
]
],
// 该字段规则回调方法,可以用于重置字段规则
'render' => function () {
},
// 是否虚拟字段虚拟字段在查询脚手架model时会忽略该字段
'virtual_field' => true,
// 该字段在表单中是否渲染默认true
'form' => false,
],
```
*rule: 后端字段验证规则*
rule完整支持 hyperf 原生的 validation 的校验[规则]([https://hyperf.wiki/#/zh-cn/validation?id=%e9%aa%8c%e8%af%81%e8%a7%84%e5%88%99](https://hyperf.wiki/#/zh-cn/validation?id=验证规则)), 且切封装了高度灵活的自定义校验 `app/Service/ValidationCustomRule.php` 其中定义的方法均可在`rule` 中直接使用, 还支持`cb_***` 调用定义在当前控制器中的自定义验证.
> 注意:目前还没有根据该规则生成前端的验证规则,前端目前只验证了是否必填的
## 内置组件
type表单项类型以下是支持的组件列表以下所有组件 props 均可支持原始文档中的所有属性
### 1. 普通输入框
[原始文档](http://www.form-create.com/v2/iview/components/input.html)
```php
[
"field_name|字段名" => "required|***"
// or
"field_name|字段名" => [
"type" => "input" // 可省略, 默认 input
"rule" => "required|***",
"default" => "默认值" // 非必须,
"info" => "字段提示文字",
"props" => [
"showCopy" => true, // 开启 copy 功能
]
]
]
```
### 2. 数字(整数)
[原始文档](http://www.form-create.com/v2/iview/components/input-number.html)
```php
[
"field_name|字段名" => [
"type" => "number"
]
]
```
### 3. 数字(两位小数)
[原始文档](http://www.form-create.com/v2/iview/components/input-number.html)
```php
[
"field_name|字段名" => [
"type" => "float",
"props" => [
"precision" => 2, // 小数保留位数, 默认2
]
]
]
```
### 4. 多行输入
[原始文档](http://www.form-create.com/v2/iview/components/input.html)
```php
[
"field_name|字段名" => [
"type" => "textarea",
"props" => [
"row" => 6, // 行数, 默认6
]
]
]
```
### 5. 开关(0/1)
[原始文档](http://www.form-create.com/v2/iview/components/switch.html)
```php
[
"field_name|字段名" => [
"type" => "switch"
]
]
```
### 6. 时间控件
[原始文档](http://www.form-create.com/v2/iview/components/date-picker.html)
```php
[
"field_name|字段名" => [
"type" => "datetime"
]
]
```
### 7. 时间区间
[原始文档](http://www.form-create.com/v2/iview/components/date-picker.html)
```php
[
"field_name|字段名" => [
"type" => "datetime_range",
"props" => [
"range" => [
"after" => date('Y-m-d'),
"before" => date('Y-m-d', strtotime('+6 days'))
],
// or 简写
"range" => "afterToday", // afterToday 今天之后, beforeToday 今天之前, 包含今天
]
]
]
```
### 8. 日期
[原始文档](http://www.form-create.com/v2/iview/components/date-picker.html)
```php
[
"field_name|字段名" => [
"type" => "date"
]
]
```
### 9. 日期区间
[原始文档](http://www.form-create.com/v2/iview/components/date-picker.html)
```php
[
"field_name|字段名" => [
"type" => "date_range"
]
]
```
### 10. 下拉选择框
[原始文档](http://www.form-create.com/v2/iview/components/select.html)
```php
[
"field_name|字段名" => [
"type" => "select",
"options" => [ // 远程搜索是无需 支持回调函数 function() { return 备选项; }
1 => "lable1",
2 => "lable2",
],
"props" => [
"selectApi" => "/coupon/act", // 远程搜索模式
"multiple" => true, // 是否多选, 默认false
"multipleLimit" => 10, // 多选时的上限
]
]
]
```
### 11. 上传图片
[原始文档](http://www.form-create.com/v2/iview/components/upload.html)
```php
[
"field_name|字段名" => [
"type" => "image",
"props" => [
// 上传张数上线, 默认1单个
"limit" => 1,
//支持下载
"downloadable" => true,
// 限制上传文件的后缀名
"format " => ['jpg', 'jpeg', 'png', 'gif'],
// 限制上传文件的大小 单位是 kb
"maxSize" => 200,
// 上传的目标 bucket
'bucket' => 'aliyuncs',
// 是否为私有
'private' => true,
]
]
]
```
### 12. 上传文件
[原始文档](http://www.form-create.com/v2/iview/components/upload.html)
```php
[
"field_name|字段名" => [
"type" => "file",
"props" => [
// 上传张数上线, 默认1单个
"limit" => 1,
//支持下载
"downloadable" => true,
// 限制上传文件的后缀名
"format " => ['doc', 'exl', 'ppt'],
// 限制上传文件的大小 单位是 kb
"maxSize" => 200,
]
]
]
```
### 13. 级联选择器
```php
[
"field_name|字段名" => [
"type" => "cascader",
"props" => [
"limit" => 1, // 上传张数上线, 默认1单个
]
]
]
```
### 14. json 组件
```php
[
"field_name|字段名" => [
"type" => "json",
]
]
```
### 15. 富文本
```php
[
"field_name|字段名" => [
"type" => "html",
]
]
```
### 16. 图标选择器
```php
[
"field_name|字段名" => [
"type" => "icon-select",
]
]
```
示例效果:
![icon](http://qupinapptest.oss-cn-beijing.aliyuncs.com/1/202005/9e62be2e0affafc0cdee8bc7fba3c0fd.png)
### 17. 嵌套表单 SubForm
```php
'test|嵌套表单' => [
'type' => 'sub-form',
'children' => [ // 子表单的规则, 同一级规则
'test_sub|嵌套1' => 'required',
'test_sub1|嵌套2' => 'required',
],
'repeat' => true, // 是否可动态添加
'default' => [ // 默认值
[
'test_sub' => 1,
'test_sub1' => 1,
],
[
'test_sub' => 1,
'test_sub1' => 1,
],
],
],
```
示例效果:
![subForm](http://qupinapptest.oss-cn-beijing.aliyuncs.com/img/Snipaste_2020-02-21_19-29-35.png)
### 18. 区域输入框
[前端文档](http://localhost:8080/hyperfdoc/frontend/components/3_InputRange.html)
```php
'test|区域输入框' => [
'type' => 'inputRange',
// value值 type Array or String
// - Array例如[1, 10], 返回结果也将是数组
// - String例如1,10, 返回结果也将是字符串
'value' => [1, 10] or '1,10',
'props' => {
// 是否允许清除
'clearable': true,
// 可控制item宽度等样式 默认宽度300px
'style': 'width: 300px',
// 开始值和结束值的placeholder
'placeholder': ['min', 'max']
},
],
```
## 组件联动
```php
// depend
[
"field_1" => "",
"field_2" => [
"depend" => [
"field" => "field_1", // 依赖字段
"value" => '1' // 当 field_1 = 1 时 field_2 此项才会显示
]
]
]
// hidden
[
"field_1" => "",
"field_2" => [
"hidden" => [
"field" => "field_1", // 影响字段
"value" => '1' // 当 field_2 = 1 时 field_1 项会隐藏
]
]
]
// 备选项 条件控制
[
"field_1" => [
"type" => "select",
"options" => [
[
"value" => 1,
"lable" => ""
],
[
"value" => 0,
"lable" => "",
// 当 disabled_when 条件运算结果, 即为 disabled 的值
"disabled_when" => [
"field_1", '=', 0
],
// or
"disabled_when" => [
["field_2", '=', 0],
["field_3", '!=', 4],
],
]
]
]
]
// 进阶用法 compute 动态计算
[
"field" => [
"compute" => [
"when" => ['=', 1], // 注意这里只有个 比较操作符 和 比较值
// set 操作项
"set" => [
"field_2" => [
// 此处支持控件除 type 外, 完整属性设置
"value" => 1,
// 此处支持 值为 callable
"value" => function() { return time();},
// 重写rule
"rule" => "required"
"props" => [
// ...
]
],
// ... "field_3"
],
// append 项, 尚未实现
// remove 项, 尚未实现
]
]
]
```

78
docs/backend/functions.md Normal file
View File

@@ -0,0 +1,78 @@
## 数组函数
#### array_group_k2k
#### array_group_by
#### array_node_append
#### array_map_recursive
#### array_copy
#### array_sort_by_key_length
#### array_sort_by_value_length
#### array_to_kv
#### array_flat
#### array_depth
#### array_merge_node
#### array_change_v2k
#### array_group
#### array_last
#### array_split
#### array_get_by_keys
#### array_remove
#### array_get_node
#### array_remove_keys_not_in
#### array_remove_keys
#### mt_array_merge
## 系统函数
#### server
#### swoole_server
#### dispatcher
#### register_route
#### move_local_file_to_oss
#### oss_private_url
#### call_self_api
#### select_options
#### process_list_filter
#### get_sub_dir
#### db_complete
#### format_exception
## 内置常量
`DAY`, `HOUR`, `MINUTE`, `YES`, `NO`

571
docs/backend/list.md Normal file
View File

@@ -0,0 +1,571 @@
![](http://km.innotechx.com/download/attachments/82253382/image2020-5-8_17-43-53.png?version=1&modificationDate=1588931034077&api=v2)
```php
return [
......
// 列表定义
'table' => [
// 树型结构列表默认false
"is_tree" => true
// tree 节点为非必须, 默认pid的名称为pid, 不同时需要重写
"tree" => [
"pid" => "pid"
],
// tabs 列表页分页签
'tabs' => [],
// 定义渲染列表, 未定义则获取 form 中所有
'columns' => [],
// 订单行操作按钮
'rowActions' => [],
// 列表上方批量操作的按钮
'batchButtons' => [],
// 页面上方操作按钮
'topActions' => [],
],
];
```
## 列定义
`columns`中定义列表中显示的字段与表头,具体配置如下:
```php
'columns' => [ // 非必须项, 无则从form转义
'字段', // 简写模式, 直接从form配置转义
// or
[
'field' => 'mall_name',
'title' => '店铺',
// 是否显示该字段默认false
'hidden' => true,
// 字段渲染规则,默认为空, 详见列渲染
'type' => '',
// 是否虚拟字段虚拟字段在查询脚手架model时会忽略该字段
'virtual_field' => true,
// 设置Popover提示信息其中
'popover' => [
'messages' => [
'原因:{remark}',
],
'when' => [
['status', '=', Activity::STATUS_OFF]
]
],
// 表头说明
'info' => '括号内为商家承担',
// 定义该字段显示在哪些tab选项中
'depend' => [
'tab' => [(string)Coupon::TYPE_MALL_MONEY_OFF],
],
// 按字段升降查询功能
'sortable' => true,
// 是否允许编辑,调用*/rowchange/:id接口
'edit' => true,
// 允许编辑的条件
'when' => [
['status', '=', Activity::STATUS_OFF]
],
// 枚举值可以options中的数据转换成Tag显示效果https://element.eleme.cn/#/zh-CN/component/tag
'options' => [],
'enum' => [ // tag 的 type 类型, 参见 element 标签
0 => 'info',
1 => 'success',
],
// 列宽设置,默认为均分模式,不支持百分比
'width': '100px',
],
],
```
## when用法
### 使用说明
```php
'when' => ["field_1", ">", 1],
// or 多个条件时为"与"判断
'when' => [
["field_1", ">", 1],
["field_2", "=", 1]
]
```
::: warning 注意
操作符支持`=``>``>=``<``<=``!=``in``not_in`,多个条件时为"与"判断,且注意参数的数据类型是否一致。
:::
- 可以控制行操作及批量按钮是否显示;
- 可以控制批量操作的过滤掉不满足条件的行;
```
'batchButtons' => [
[
.....
// 控制根据条件显示这里的条件字段来源为queryString
'when'=> [
[字段, 操作符, 值]
],
// 为区别控制显示的when关键字这里使用`selectFilter`
'selectFilter' => [
[字段, 操作符, 值]
]
]
]
```
- 控制列表行的某一列的编辑状态
- 控制列渲染的popover模式下的启用状态
### 查看源码
```javascript
export function whereFilter(obj, where, fakeKey) {
if (!where) {
return true
}
let ret = true
let real_where = where
if (where[0] && typeof where[0] === 'string') {
real_where = [where]
}
for (let i = 0; i < real_where.length; i++) {
const item = real_where[i]
const key = fakeKey ? item[0].replace('.', '-') : item[0]
if (item[1] === '=') {
ret = obj[key] === item[2]
}
if (item[1] === '>') {
ret = obj[key] > item[2]
}
if (item[1] === '<') {
ret = obj[key] < item[2]
}
if (item[1] === '>=') {
ret = obj[key] >= item[2]
}
if (item[1] === '<=') {
ret = obj[key] <= item[2]
}
if (item[1] === '!=') {
ret = obj[key] !== item[2]
}
if (item[1] === 'in') {
ret = item[2].indexOf(obj[key]) !== -1
}
if (item[1] === 'not_in') {
ret = item[2].indexOf(obj[key]) === -1
}
if (!ret) {
return false
}
}
return ret
}
```
## 列渲染
渲染类型:
- `number``switch``input`
- `icon``image``extrude``tag``link``iframe``html`
### number
**渲染条件** `'edit' => true` 且 满足when中定义的条件
**用法**
```php
'type' => 'number',
'edit' => true`,
'when' => [
['status', '=', Activity::STATUS_OFF]
],
```
### switch
**渲染条件** `'edit' => true` 且 满足when中定义的条件
**用法**
```php
'type' => 'switch',
'edit' => true`,
'when' => [
['status', '=', Activity::STATUS_OFF]
],
```
### input
**渲染条件** `'edit' => true` 且 满足when中定义的条件
**用法**
```php
'edit' => true`,
'when' => [
['status', '=', Activity::STATUS_OFF]
],
```
::: warning 注意
目前行编辑的表单组件只支持以上三种类型且不能定义该组件的props
:::
### icon
**渲染条件** `'type' => 'icon'`
**效果展示**<i class="omsfont">&#xe65d;</i>
### image
**渲染条件** `'type' => 'image'`,数据为数组时可以渲染多张图片
**效果展示**![u](http://km.innotechx.com/download/attachments/82253382/image2020-5-8_14-6-56.png?version=1&modificationDate=1588918016653&api=v2)
#### extrude
**用法**
```php
[
"field" => "field_1",
"type" => 'extrude',
"render" => function($field_value, $row) {
// return "<优惠券|balck|yellow>*****"; 单个
// or 支持多个
return [
"<优惠券{replace_field}|balck|yellow>*****{replace_field}"
]; // 格式 <文字|背景色|文字色>, 支持前端变量替换
}
]
```
**效果展示**![o](http://km.innotechx.com/download/attachments/47482497/Snipaste_2020-02-20_09-19-03.png?version=1&modificationDate=1582161564093&api=v2)
### tag
**用法**
```php
[
"field" => "field_1",
"options" => [
0 => '禁用',
1 => '启用',
],
"enum" => [ // tag 样式, 参见 https://element.eleme.io/#/zh-CN/component/tag
0 => 'info',
1 => 'success'
]
]
```
**效果展示**![p](http://km.innotechx.com/download/attachments/47482497/Snipaste_2020-02-20_09-27-03.png?version=1&modificationDate=1582162039284&api=v2)
### link
**用法**
```php
[
"field" => "field_1",
"href" => "http://hyperfadmin.com/page/{id}"
]
// or
[
"field" => "field_1",
"href" => [
"href" => "http://hyperfadmin.com/page/{id}",
"type" => "primary",
"target" => "_blank"
]
]
```
**效果展示**![p](http://km.innotechx.com/download/attachments/47482497/Snipaste_2020-02-20_09-22-39.png?version=1&modificationDate=1582161776808&api=v2)
### iframe
**用法**
```php
[
'field' => 'state_info',
'title' => '运行状态',
'type' => 'iframe',
// 列以button形式显示style控制按钮type, [info|success|primary|danger|warning]
'style' => 'primary',
// 弹出窗口宽度,默认500px
'width' => '500px',
// 弹出窗口高度,默认600px
'height' => '600px'
"render" => function($field_value, $row) {
return "url";// 返回 iframe的src路径
}
]
```
### html
**用法**
```php
[
'field' => 'state_info',
'title' => '运行状态',
'type' => 'html',
"render" => function($field_value, $row) {
return '<p>xxxxxxx<br>xxxxx</p>'; //
}
]
```
### supperButton
**用法**
```php
[
'field' => 'field',
'type' => 'supperButton',
// supperButton的配置信息 必须有
// 配置文档详见supperButton部分
'config' => {
}
]
```
### popover弹出框
支持以上除编辑模式下的列渲染类型
**用法**
```php
[
'field' => 'state_info',
'title' => '运行状态',
'popover' => [
'messages' => [
'上线时间: {state.start_time}',
'最后活跃时间: {state.last_time}',
'运行次数: {state.counter}',
],
'when' => ["field_1", ">", 1],
// or
'when' => [
["field_1", ">", 1],
["field_2", "=", 1]
]
]
]
```
**效果展示**![popover弹出框](http://qupinapptest.oss-cn-beijing.aliyuncs.com/img/Snipaste_2020-02-21_18-06-06.png)
### progress
**用法**
```php
[
'field' => 'sync_progress',
'title' => '同步进度',
'type' => 'progress',
// 'props' => []
]
```
> props里是progress组件的属性配置请参考[Progress 进度条](https://element.eleme.cn/#/zh-CN/component/progress#attributes)
**效果展示**![Progress 进度条](http://qupinapptest.oss-cn-beijing.aliyuncs.com/img/Snipaste_2020-02-21_18-06-06.png)
## 搜索项
```php
[
......
// 搜索条件, 前端页面会根据此处配置渲染搜索条件,可以像表单一样配置规则
// 搜索条件中支持模糊搜索也很简单 %field_name%, field_name%, %field_name, 如此定义字段即可
'filter' => [
'id',
'username%',
'create_at|创建时间' => [
'type' => 'date_range',
'search_type' => 'between',
'default' => [date('Y-m-d', time() - DAY * 7), date('Y-m-d', time() + DAY)]
]
],
]
```
## tab切换
```php
'tabs' => [
[
'label' => '平台券',
'value' => (string) Coupon::TYPE_PLATFORM_MONEY_OFF,
'icon' => 'el-icon-s-grid',
],
[
'label' => '店铺券',
'value' => (string) Coupon::TYPE_MALL_MONEY_OFF,
'icon' => 'el-icon-s-grid',
],
],
'columns' => [
[
'field' => 'state_info',
'title' => '运行状态',
// 定义该字段显示在哪些tab选项中
'depend' => [
'tab' => [(string)Coupon::TYPE_MALL_MONEY_OFF],
],
]
]
```
**效果展示**![](http://qupinapptest.oss-cn-beijing.aliyuncs.com/img/Snipaste_2020-02-21_19-46-14.png)
## 操作按钮
支持 列操作按钮, 批量操作按钮, 列表页顶部按钮,前端组件为`SuperButton`
### 基础属性
```php
[
"text" => "", //按钮文案,支持参数替换
"type" => "jump", // 按钮类型 默认 jump, 可选 form, api
"target" => "", // 动作目标 本地路由, 网址, 后端api支持参数替换
"props" => [ // 按钮属性, 更多可见 https://element.eleme.io/#/zh-CN/component/button
"icon" => "", // 按钮图标 默认无
"circle" => false, // 圆角 默认false
"size" => "small", // 默认 small, 可选 medium / small / mini
"type" => "info", //默认text, primary / success / warning / danger / info / text
],
"when" => [ // 当前按钮的显示条件, 默认无
["field_1", "=", 1] // filter过滤的比对数据为当前行 或者 当前页面基础数据
]
]
// 除以上基础属性外, 根据按钮类型, 也会有其他额外属性, 具体见下面
```
### 普通跳转按钮
```javascript
[
"text" => "编辑",
"target" => "/user/12"
]
// or
[
"text" => "文档",
"target" => "http://hyperf.wiki"
]
```
### 动作按钮(请求后端API)
点击按钮后, 提示二次确认, 确认后请求后端api
```javascript
[
"text" => "删除",
"target" => "/user/12",
"method" => "POST", // 请求api的方式, 默认POST
]
```
### 表单型按钮
点击按钮后将以弹窗形式渲染指定表单, 然后搜集后端数据请求到指定api
```javascript
[
"text" => "审核通过",
"target" => "/user/12",
"method" => "POST", // 请求api的方式, 默认POST
"rules" => [ // 表单的rule规则具体参见表单部分
"reason|原因" => [
"rule" => "required"
]
]
]
// 有联动时
[
"text" => "审核通过",
"target" => "/user/12",
"method" => "POST", // 请求api的方式, 默认POST
"rules" => [
// 待补充
]
]
// or
[
"text" => "审核通过",
"target" => "/user/form", // get 方式拉取表单配置, post 方式保存数据
"method" => "POST", // 请求api的方式, 默认POST
]
```
### 列表型按钮
点击按钮后将以弹窗的形式渲染指定列表
```php
[
'type' => 'table', // 调用 src/components/Scaffold/tablist.vue 渲染
'target' => '', // target 留空
'props' => [
'listApi' => '/merchantlog/list?merchant_id={id}', // 列表数据拉取接口
'infoApi' => '/merchantlog/info', // 列表 配置拉取接口
'options' => [ // 表单的配置项
'showFilter' => false,
'createAble' => false
]
],
'text' => '招商记录',
]
```
### 抽屉型按钮
点击按钮后将打开抽屉, 抽屉内部指定动态调用指定组件
```php
[
'type' => 'drawer',
'target' => '', // target 留空
'text' => '查看日志',
'props' => [
'component' => 'SocketList', // 需动态调用的组件 src/components/Common 下
'componentProps' => [ // 组件的 props
'url' => env('OMS_WEBSOCKET_URL') . '/cronlog?name={name}'
],
// drawer** 为抽屉属性的定义
// 详见 https://element.eleme.io/#/zh-CN/component/drawer
'drawerWithHeader' => false,
'drawerSize' => '80%',
'drawerTitle' => '{title}日志',
'drawerDirection' => 'ttb'
]
]
```
### 下拉按钮
上面 SuperButton 的结构改为数组形式即可

408
docs/backend/scaffold.md Normal file
View File

@@ -0,0 +1,408 @@
## 路由注册
一个独立的业务模块需要在`config/routes/`下添加业务的路由文件,在该文件内完成业务模块所有的路由定义。可以使用`register_route`方法来定义您的路由。
```php
<?php
use Hyperf\HttpServer\Router\Router;
use App\Controller\IndexController;
register_route('/index', IndexController::class, function ($controller) {
// 其他路由的定义
Router::get('/hello-hyperf', [$controller, 'hello']);
});
```
!> 如果完全是自定义的前端页面,建议不使用`register_route`注册路由,`register_route`内部会注册一些脚手架路由
**脚手架路由**
| uri | 请求方式 | 控制器方法 | 说明 |
| :----------------------------------------------------------- | :------- | :---------------- | :---------------------------------------------- |
| `path`/list.json<br>`path`/info | GET | info | 下发列表页的配置 |
| `path`/form.json<br>`path`/form<br>`path`/{id:\d+}.json<br>`path`/{id:\d+} | GET | form、edit | 下发表单配置 |
| `path`/list | GET | list | 下发列表数据 |
| `path`/form | POST | save | 新增时数据保存接口 |
| `path`/{id:\d+} | POST | save | 编辑时数据保存接口 |
| `path`/delete | POST | delete | 删除接口 |
| `path`/batchdel | POST | batchDelete | 批量删除接口 |
| `path`/rowchange/{id:\d+} | POST | rowChange | 行编辑数据保存接口 |
| `path`/childs/{id:\d+} | GET | getTreeNodeChilds | 树结构的列表页动态获取子节点的接口 |
| `path`/newversion/{id:\d+}/{last_ver_id:\d+} | GET | newVersion | 表单编辑时或数据对象的版本信息接口 |
| `path`/export | POST | export | 导出任务接口 |
| `path`/act | GET | act | 可用于当前model提供select组件远程搜索的数据接口 |
| `path`/import | POST | import | 导入接口 |
## 脚手架概览
在编写控制器时需`继承`脚手架的抽象类`AbstractController`,并在`scaffoldOptions`方法中定义页面的配置。
```php
<?php
declare(strict_types=1);
namespace App\Controller;
use Hyperf\Admin\Controller\AdminAbstractController;
class IndexController extends AdminAbstractController
{
// 操作的 model 对象
public $model_class = User::class;
// 操作的entity
// entity 为脚手架抽象出的一个实体, 包含对象的 curd 操作
// 目前支持 mysql/es/mongo/api
// model 和 entity 任选其一即可
public $entity_class = UserEntity::class;
// 脚手架核心配置
public function scaffoldOptions()
{
return [
// 自定义创建按钮的跳转路由, 默认 /user/form
'form_path' => '/custom/path',
// 是否允许创建, 默认 true, false怎隐藏页面列表上方的新建按钮
'createAble' => false,
// 是否允许删除, 默认 true
'deleteAble' => true,
// 是否开启通知查询功能,开启后在页面路由发生变化时,会根据当前页面参数查询页面有没有通知消息
'noticeAble' => true,
// 是否需要分页器 默认true
'paginationEnable' => true,
// 是否显示导出按钮, 默认true
'exportAble' => true,
// 列表页是否默认执行查询, 默认执行查询
'defaultList' => false,
// 搜索条件, 前端页面会根据此处配置渲染搜索条件,可以像表单一样配置规则
// 搜索条件中支持模糊搜索也很简单 %field_name%, field_name%, %field_name, 如此定义字段即可
'filter' => ['id', 'username%', 'create_at'],
// 列表的基础筛选条件, 列表的查询均会携带上此处的条件, 详情请查看where2query方法
'where' => [
'type' => User::STATUS_ON,
],
// 筛选条件是否同步到地址栏
"filterSyncToQuery" => false,
// 列表的排序
'order_by' => 'id desc',
// 表单页面的UI配置, 详参 http://form-create.com/v2/iview/global.html
'formUI' => [
'form' => [
'lableWidth' => '300px'
],
'submitBtn' => [
'innerText' => '这是提交按钮'
]
],
// form表单的定义, 核心配置, 不可或缺,请求请查看表单页配置
'form' => [
'field|字段名称' => [
// 字段验证规则
'rule' => 'required|max',
'type' => 'input',
'info' => '字段备注',
],
],
// 页面提示
'notices' => [
[
'type' => 'warning',
'message' => '提示信息',
'actionsPlacement' => 'right',
'closable' => true,
'actions' => [
[
'props' => [
'size' => 'mini',
'type' => 'success',
],
// 勿动!! 选品页面是监听点击事件同当前 text 比对.
'text' => '点我更新',
'type' => 'native',
]
],
'when' => function($filters) { return true;}
]
],
// 第三方数据补充, 子项定义规范, 详见下方列表第三方数据补充部分
'hasOne' => [
'mt_oms.mt_oms.user_role:user_id,role_id'
],
// 列表定义
'table' => [
// tabs 列表页分页签
'tabs' => [],
// 定义渲染列表, 未定义则获取 form 中所有
'columns' => [],
// 订单行操作按钮
'rowActions' => [],
// 列表上方批量操作的按钮
'batchButtons' => [],
// 页面上方操作按钮
'topActions' => [],
],
];
}
}
```
## 内置钩子
```php
// 列表页下发配置接口的前置钩子
public function beforeInfo(&$info) {}
// 列表页执行搜索前的钩子, 可用于修改 where 条件
public function beforeListQuery(&$conditions) {}
// 列表数据响应前的钩子, 可用于补充额外数据
public function beforeListResponse(&$list) {}
// form 规则下发前的干预钩子
public function meddleFormRule($id, &$form_rule) {}
// form 响应前的钩子
public function beforeFormResponse($id, &$record) {}
// 表单保存前端钩子函数
public function beforeSave($pk_val, &$data) {}
// 表单保存后的钩子函数
public function afterSave($pk_val, &$data) {}
// 删除前的回调钩子
public function beforeDelete($pk_val) {}
// 删除后的回调钩子
public function afterDelete($pk_val, $deleted) {}
```
## 表单定义
```php
public function scaffoldOptions()
{
return [
....
// 表单配置
'form' => [
// 字段验证规则
// 请参考 https://hyperf.wiki/#/zh-cn/validation?id=%e9%aa%8c%e8%af%81%e8%a7%84%e5%88%99
'rule' => 'required|max:10',
// 请参考 http://www.form-create.com/v2/element-ui/components/input.html
'type' => 'input',
// 表单默认值
'default' => '',
'info' => '字段备注',
// 只读属性,当编辑时有效
'readonly' => true,
// 表单选项只有支持options选项的组件设置才有效可以定义一个callback方法可以参考formOptionsConvert方法
'options' => [],
// 其他组件属性请参考具体组件的props的定义
'props' => [],
// 定义依赖项
'depend' => [
'field' => 'target_type',
'value' => [],
],
// col 布局规则 http://www.form-create.com/v2/element-ui/col.html
'col' => [
// 表单长度
'span' => 12,
// 标签宽度
'labelWidth' => 150,
],
// 动态修改其他字段规则 详见下方联动小节
'compute' => [
"will_set_field" => [
"when" => ['=', 1],
"set" => [
//
]
]
],
// 该字段规则回调方法,可以用于重置字段规则
'render' => function () {
},
// 开启input框的复制功能
'copy_show' => true,
],
];
}
```
## 列表定义
```php
public function scaffoldOptions()
{
return [
....
// 非必须项, 没有定义则从form转义
'columns' => [
'字段名', // 简写模式, 直接从form配置转义
[
'field' => 'mall_name',
'title' => '店铺',
// 字段渲染规则,默认为空
'type' => '',
// 是否虚拟字段虚拟字段在查询脚手架model时会忽略该字段
'virtual_field' => true,
// 表头说明
'info' => '括号内为商家承担',
// 定义该字段显示在哪些tab选项中
'depend' => [
'tab' => [(string)Coupon::TYPE_MALL_MONEY_OFF],
],
// 按字段升降查询功能
'sortable' => true,
// 是否允许编辑,调用*/rowchange/:id接口
'edit' => true,
// 枚举值可以options中的数据转换成Tag显示效果https://element.eleme.cn/#/zh-CN/component/tag
'options' => [],
'enum' => [ // tag 的 type 类型, 参见 element 标签
0 => 'info',
1 => 'success',
],
// 单独处理某个字段
'render' => function($val, $row) { return $val;}
],
],
];
}
```
## 按钮
`rowActions`, `topButtons`, `topActions`, `notices.*.actions` 的节点定义
1. 页面跳转
```php
[
'type' => 'jump',
'target' => '/crontab/{id}', // 本地路由或三方地址
'text' => '编辑',
'props' => [] // element el-button 的属性
]
```
2. 请求后端api
```php
[
'type' => 'api',
'target' => '/resource/delete', // 支持变量替换
'text' => '删除',
'method' => 'POST', // 默认POST
'props' => [
'type' => 'danger',
],
// 当前按钮可以定义依赖条件, 动态显示
'when' => [
['gid', '=', Resource::RESOURCE_ROOT_ID]
]
],
```
3. model弹窗表单
```php
// 直接定义表单规则 rules
[
'action' => 'module',
'target' => '/user/test/{id}',
'text' => '弹窗',
// rules 的定义同 form rule
'rules' => [
'file|视频' => [
'type' => 'file',
],
],
]
// 调用其他Controller form表单
[
'action' => 'module',
// 若没有rules节点则自定调用 target 接口拉取表单配置
'target' => '/user/form',
'text' => '弹窗',
]
// 调用其他 Controller 的列表
[
'type' => 'table',
'target' => '',
'props' => [
'listApi' => '/role/list?id={id}',
'infoApi' => '/role/info',
'options' => [
'showFilter' => false,
'createAble' => false
]
],
'text' => '**记录',
]
```
按钮较多时均可调整为按钮组
```php
[
[
$action_conf1,
$action_conf2
]
]
```
## 关联数据
```php
public function scaffoldOptions()
{
return [
....
// 一对一关系
'hasOne' => [
// 此处定义了补充的第三方数据是什么, 从哪里取
// [pool.]db.table:[local_key->]foreign_key,other_key
'hyperf_admin.hyperf_admin.user_role:id->user_id,role_id', // 完整定义
'hyperf_admin.user_role:id->user_id,role_id', // 缺省 pool
'hyperf_admin.user_role:user_id,role_id', // 缺省 pool,local_key
'hyperf_admin.user_role:user_id,role_id as rid', // 补充字段使用别名, 避免覆盖list中同名字段
],
// 一对多或多对多关系
'hasMany' => [
'hyperf_admin.hyperf_admin.operator_log:id->user_id,username'
]
];
}
```
`[pool.]db.table:[local_key->]foreign_key,other_key`
分别对应`连接池`(非必须, 默认dfault), `库名`, `表名`, `本地关联字段`(非必须, 默认id), `逻辑外键`, `其他要补充的字段`, 若系统为查询到第三方数据, 相应的补充字段将初始为 `null`
## 页面提示
```php
public function scaffoldOptions()
{
return [
....
// 页面提示信息
'notices' => [
[
'type' => 'warning',
'message' => '提示信息',
'actionsPlacement' => 'right',
'closable' => true,
'actions' => [], // 按钮
'when' => function($filters) { return true;}
]
]
];
}
```

View File

@@ -0,0 +1,132 @@
支持 列操作按钮, 批量操作按钮, 列表页顶部按钮
### 基础属性
```php
[
"text" => "", //按钮文案
"type" => "jump", // 按钮类型 默认 jump, 可选 form, api
"target" => "", // 动作目标 本地路由, 网址, 后端api
"props" => [ // 按钮属性, 更多可见 https://element.eleme.io/#/zh-CN/component/button
"icon" => "", // 按钮图标 默认无
"circle" => false, // 圆角 默认false
"size" => "small", // 默认 small, 可选 medium / small / mini
"type" => "info", //默认text, primary / success / warning / danger / info / text
],
"when" => [ // 当前按钮的显示条件, 默认无
["field_1", "=", 1] // filter过滤的比对数据为当前行 或者 当前页面基础数据
]
]
// 除以上基础属性外, 根据按钮类型, 也会有其他额外属性, 具体见下面
```
#### 普通跳转按钮
```javascript
[
"text" => "编辑",
"target" => "/user/12"
]
// or
[
"text" => "文档",
"target" => "http://hyperf.wiki"
]
```
#### 动作按钮(请求后端API)
点击按钮后, 提示二次确认, 确认后请求后端api
```javascript
[
"text" => "删除",
"target" => "/user/12",
"method" => "POST", // 请求api的方式, 默认POST
]
```
#### 表单型按钮
点击按钮后将以弹窗形式渲染指定表单, 然后搜集后端数据请求到指定api
```javascript
[
"text" => "审核通过",
"target" => "/user/12",
"method" => "POST", // 请求api的方式, 默认POST
"rules" => [ // 表单的rule规则具体参见表单部分
"reason|原因" => [
"rule" => "required"
]
]
]
// 有联动时
[
"text" => "审核通过",
"target" => "/user/12",
"method" => "POST", // 请求api的方式, 默认POST
"rules" => [
// 待补充
]
]
// or
[
"text" => "审核通过",
"target" => "/user/form", // get 方式拉取表单配置, post 方式保存数据
"method" => "POST", // 请求api的方式, 默认POST
]
```
#### 列表型按钮
点击按钮后将以弹窗的形式渲染指定列表
```php
[
'type' => 'table', // 调用 src/components/Scaffold/tablist.vue 渲染
'target' => '', // target 留空
'props' => [
'listApi' => '/merchantlog/list?merchant_id={id}', // 列表数据拉取接口
'infoApi' => '/merchantlog/info', // 列表 配置拉取接口
'options' => [ // 表单的配置项
'showFilter' => false,
'createAble' => false
]
],
'text' => '招商记录',
]
```
抽屉型按钮
点击按钮后将打开抽屉, 抽屉内部指定动态调用指定组件
```php
[
'type' => 'drawer',
'target' => '', // target 留空
'text' => '查看日志',
'props' => [
'component' => 'SocketList', // 需动态调用的组件 src/components/Common 下
'componentProps' => [ // 组件的 props
'url' => env('OMS_WEBSOCKET_URL') . '/cronlog?name={name}'
],
// drawer** 为抽屉属性的定义
// 详见 https://element.eleme.io/#/zh-CN/component/drawer
'drawerWithHeader' => false,
'drawerSize' => '80%',
'drawerTitle' => '{title}日志',
'drawerDirection' => 'ttb'
]
]
```
### SuperButtonGroup 下拉按钮
上面 SuperButton 的结构改为数组形式即可

BIN
docs/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

1
docs/frontend/chart.md Normal file
View File

@@ -0,0 +1 @@
图表

1
docs/frontend/form.md Normal file
View File

@@ -0,0 +1 @@
表单

1
docs/frontend/list.md Normal file
View File

@@ -0,0 +1 @@
图表

37
docs/guide/desc.md Normal file
View File

@@ -0,0 +1,37 @@
`hyperf-admin`是前后端分离的后台管理系统, 前端基于`vue``vue-admin-template`, 针对后台业务`列表`, `表单`等场景封装了大量业务组件, 后端基于`hyperf`实现, 整体思路是后端定义页面渲染规则, 前端页面渲染时首先拉取配置, 然后组件根据具体配置完成页面渲染, 方便开发者仅做少量的配置工作就能完成常见的`CRUD`工作, 同时支持自定义组件和自定义页面, 以开发更为复杂的页面.
### 架构
![hyperf-admin架构](https://cdn.jsdelivr.net/gh/daodao97/FigureBed@master/uPic/sJaJti.png)
前端为`vue multiple page`多页模式, 可以按模块打包, 默认包含两个模块`default` 默认模块, `system`系统管理模块, 绝大部分业务组件在`src/components`目录, 前端文档详见 [这里](/frontend/)
后端为`composer包`模式, 目前包含组件
- 基础组件
- `composer require hyperf-admin/base-utils` hyperf-admin的基础组件包, 脚手架主要功能封装
- `composer require hyperf-admin/validation` 参数验证包, 对规则和参数提示做了较多优化
- `composer require hyperf-admin/alert-manager` 企微/钉钉机器人报警包
- `composer require hyperf-admin/rule-engine` 规则引擎
- `composer require hyperf-admin/event-bus` mq/nsq/kafka消息派发器
- `composer require hyperf-admin/process-manager` 进程管理组件
- 业务组件 (业务组件为包含特定业务功能的包)
- `composer require hyperf-admin/admin` 系统管理业务包
- `composer require hyperf-admin/dev-tools` 开发者工具包, 主要是代码生成, 辅助开发
- `composer require hyperf-admin/cron-center` 定时任务管理, 后台化管理任务
- `composer require hyperf-admin/data-focus` 数据面板模块, 帮你快速制作数据大盘
后端的详细文档见[这里](/backend/)
### 依赖 & 参考
- 前端
- [Vue](https://github.com/vuejs/vue)
- [ElementUI](https://github.com/ElemeFE/element)
- [FormCreate](http://www.form-create.com/v2/guide)
- [vue-admin-template](https://github.com/PanJiaChen/vue-admin-template)
- [Vue 渲染函数 & JSX](https://cn.vuejs.org/v2/guide/render-function.html)
- 后端
- [Hyperf](http://hyperf.wiki/)
- [Swoole](http://wiki.swoole.com)

119
docs/guide/dev_example.md Normal file
View File

@@ -0,0 +1,119 @@
我们通过一个具体案例, 来看看如何应用`hyperf-admin`快速实现
### 1.需求描述
实现一个某校年级内各班级学生的各科成绩管理后台, 要求如下
1. 列表显示学生 年级,班级,学习,学科,成绩,时间,性别,年龄
2. 可以按成绩 倒序/正序排列
3. 可以批量导入/导出学生成绩
4. 可以通过 年级,学生名称,班级 等条件筛选
5. 最好列表可以分页签直接显示各科成绩
6. 没有原型图
### 2. 数据库定义
这里不做太复杂的设计, 仅用一张表来完成此需求
```sql
CREATE TABLE `student_score` (
`id` int(12) unsigned NOT NULL AUTO_INCREMENT,
`grade` tinyint(4) unsigned NOT NULL COMMENT '年级',
`class` tinyint(4) unsigned NOT NULL DEFAULT '0' COMMENT '班级',
`subject` tinyint(4) unsigned NOT NULL COMMENT '学科',
`score` int(12) unsigned NOT NULL DEFAULT '0' COMMENT '分数',
`name` varchar(10) NOT NULL DEFAULT '' COMMENT '学生名称',
`sex` tinyint(4) unsigned NOT NULL DEFAULT '0' COMMENT '性别, 0女生, 1难受',
`create_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
```
接下来在`MySql`中创建表
### 3.功能开发
1. `hyperf`中添加db信息
```php
// config/autoload/databases.php
'local' => db_complete([
'host' => '127.0.0.1',
'database' => 'test',
'username' => 'root',
'password' => 'root'
])
```
2. 通过`DevTools`开发者工具创建 `student_score` 相关的 `Model`, `Controller`
![YPdEli](https://cdn.jsdelivr.net/gh/daodao97/FigureBed@master/uPic/YPdEli.png)
选择好相应的表后, 点击提交, 此时工具已经帮我们创建好相应的`app/Controller/StudentScoreController.php`和`app/Model/Test/StudentScore.php`
3. 添加目录和菜单
![cs0SYX](https://cdn.jsdelivr.net/gh/daodao97/FigureBed@master/uPic/cs0SYX.png)
注册路由
```php
// config/routes.php
register_route('/student_score', StudentScoreController::class);
```
此时我们也已经完成了基础的`CRUD`开发
![MEoM4p](https://cdn.jsdelivr.net/gh/daodao97/FigureBed@master/uPic/MEoM4p.png)
哦对了, 还有各种筛选条件呢? 也很简单, 在 `scaffoldOptions` 中增加 `filter`配置即可
```php
public function scaffoldOptions()
{
return [
'filter' => [
'grade', 'class', 'subject', 'name%',
'score|分数' => [
'type' => 'input-range',
'select_type' => 'between'
]
],
];
}
```
![u68v1D](https://cdn.jsdelivr.net/gh/daodao97/FigureBed@master/uPic/u68v1D.png)
还有, 大家别忘了, 需求中还要去可以按页签显示, 改怎么办呢, 这个ui可有点复杂啊, 不过在`hyperf-admin`里也同样简单
`scaffoldOptions` 中增加 `table.tabs`配置即可
```php
public function scaffoldOptions()
{
return [
'table' => [
'tabs' => [
[
'label' => '语文',
'value' => 1,
'icon' => 'el-icon-s-grid',
],
[
'label' => '数学',
'value' => 2,
'icon' => 'el-icon-s-grid',
],
]
],
];
}
```
![Ax0WWD](https://cdn.jsdelivr.net/gh/daodao97/FigureBed@master/uPic/Ax0WWD.png)
至此我们已经完成了绝大部分的功能开发, 如果使用熟练, 我们应该能在十分钟内完成整个功能的前后端开发, 而且还支持复杂的前端效果.
?> 当然`hyperf-admin`还支持更多复杂的功能, 快快用你明亮的眼睛去发现他吧.

39
docs/guide/install.md Normal file
View File

@@ -0,0 +1,39 @@
?> `hyperf-admin`目前尚未开源, 敬请期待.
## 前端
```shell
# 环境依赖
# 1. node ^v11.2.0 https://nodejs.org/zh-cn/download/
# 2. npm ^6.4.1
git clone https://github.com/hyperf-admin/hyperf-admin-front.git
cd hyperf-admin-front
npm i
npm run dev
```
!> 请根据实际情况修改`vue.config.js`中的代理 `proxy.target`地址
```shell
# 打包
npm run build:prod
npm run build:test
```
## 后端
```shell
# 环境依赖 php ^7.1 composer swoole
# 下载demo项目
git clone https://github.com/hyperf-admin/hyperf-admin-skeleton.git
cd hyperf-admin-skeleton
composer i
# 启动
composer watch
```
## nginx配置
```nginx
# conf
```

63
docs/index.html Normal file
View File

@@ -0,0 +1,63 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>hyperf-admin</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="description" content="Description">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<link rel="stylesheet" href="//unpkg.com/docsify/lib/themes/vue.css">
<link rel="shortcut icon" type="image/icon" href="/favicon.ico">
</head>
<body>
<div id="app"></div>
<script src="//unpkg.com/docsify/lib/docsify.min.js"></script>
<script>
window.$docsify = {
name: 'hyperf-admin',
//logo: '/logo.png',
repo: 'https://github.com/hyperf-admin',
//coverpage: true, // 开启封面
loadNavbar: true,
loadSidebar: true,
maxLevel: 4,
subMaxLevel: 3,
autoHeader: true,
search: {
maxAge: 86400000, // 过期时间,单位毫秒,默认一天
paths: 'auto', // or 'auto'
placeholder: 'Type to search'
},
plugins: [
function (hook, vm) {
hook.beforeEach(function (html) {
if (/githubusercontent\.com/.test(vm.route.file)) {
url = vm.route.file
.replace('raw.githubusercontent.com', 'github.com')
.replace(/\/master/, '/blob/master')
} else {
url = 'https://github.com/hyperf-admin/hyperf-admin.github.io/edit/master/' + vm.route.file
}
var editHtml = '[:memo: 编辑文档!](' + url + ')&nbsp;&nbsp;'
return html
+ '\n\n----\n\n'
+ editHtml
+ '<a href="https://docsify.js.org" target="_blank" style="color: inherit; font-weight: normal; text-decoration: none;">Powered by docsify</a>'
})
}
]
}
if (typeof navigator.serviceWorker !== 'undefined') {
navigator.serviceWorker.register('https://raw.githubusercontent.com/hyperf-admin/hyperf-admin.github.io/master/ws.js')
}
</script>
<script src="//unpkg.com/prismjs/components/prism-bash.js"></script>
<script src="//unpkg.com/prismjs/components/prism-php.js"></script>
<script src="//unpkg.com/prismjs/components/prism-json.js"></script>
<script src="//unpkg.com/prismjs/components/prism-sql.js"></script>
<script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/search.min.js"></script>
<script src="//unpkg.com/docsify-copy-code"></script>
<script src="//unpkg.com/docsify/lib/plugins/zoom-image.js"></script>
</body>
</html>

BIN
docs/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

83
docs/ws.js Normal file
View File

@@ -0,0 +1,83 @@
/* ===========================================================
* docsify sw.js
* ===========================================================
* Copyright 2016 @huxpro
* Licensed under Apache 2.0
* Register service worker.
* ========================================================== */
const RUNTIME = 'docsify'
const HOSTNAME_WHITELIST = [
self.location.hostname,
'fonts.gstatic.com',
'fonts.googleapis.com',
'cdn.jsdelivr.net'
]
// The Util Function to hack URLs of intercepted requests
const getFixedUrl = (req) => {
var now = Date.now()
var url = new URL(req.url)
// 1. fixed http URL
// Just keep syncing with location.protocol
// fetch(httpURL) belongs to active mixed content.
// And fetch(httpRequest) is not supported yet.
url.protocol = self.location.protocol
// 2. add query for caching-busting.
// Github Pages served with Cache-Control: max-age=600
// max-age on mutable content is error-prone, with SW life of bugs can even extend.
// Until cache mode of Fetch API landed, we have to workaround cache-busting with query string.
// Cache-Control-Bug: https://bugs.chromium.org/p/chromium/issues/detail?id=453190
if (url.hostname === self.location.hostname) {
url.search += (url.search ? '&' : '?') + 'cache-bust=' + now
}
return url.href
}
/**
* @Lifecycle Activate
* New one activated when old isnt being used.
*
* waitUntil(): activating ====> activated
*/
self.addEventListener('activate', event => {
event.waitUntil(self.clients.claim())
})
/**
* @Functional Fetch
* All network requests are being intercepted here.
*
* void respondWith(Promise<Response> r)
*/
self.addEventListener('fetch', event => {
// Skip some of cross-origin requests, like those for Google Analytics.
if (HOSTNAME_WHITELIST.indexOf(new URL(event.request.url).hostname) > -1) {
// Stale-while-revalidate
// similar to HTTP's stale-while-revalidate: https://www.mnot.net/blog/2007/12/12/stale
// Upgrade from Jake's to Surma's: https://gist.github.com/surma/eb441223daaedf880801ad80006389f1
const cached = caches.match(event.request)
const fixedUrl = getFixedUrl(event.request)
const fetched = fetch(fixedUrl, { cache: 'no-store' })
const fetchedCopy = fetched.then(resp => resp.clone())
// Call respondWith() with whatever we get first.
// If the fetch fails (e.g disconnected), wait for the cache.
// If theres nothing in cache, wait for the fetch.
// If neither yields a response, return offline pages.
event.respondWith(
Promise.race([fetched.catch(_ => cached), cached])
.then(resp => resp || fetched)
.catch(_ => { /* eat any errors */ })
)
// Update the cache with the version we fetched (only for ok status)
event.waitUntil(
Promise.all([fetchedCopy, caches.open(RUNTIME)])
.then(([response, cache]) => response.ok && cache.put(event.request, response))
.catch(_ => { /* eat any errors */ })
)
}
})