Compare commits
197 Commits
e20253c2bf
...
5fde7566f4
| Author | SHA1 | Date |
|---|---|---|
|
|
5fde7566f4 | |
|
|
bdc88db320 | |
|
|
909eee158c | |
|
|
f719f38677 | |
|
|
09a24aaa83 | |
|
|
b01e8e9aac | |
|
|
b0d9dbe3f6 | |
|
|
8da20ed9ad | |
|
|
151f7877e4 | |
|
|
f11d16251d | |
|
|
de943fda5a | |
|
|
132c36e9ef | |
|
|
aec3b6a338 | |
|
|
1414cfae3d | |
|
|
eab2a29438 | |
|
|
ff775ec9d5 | |
|
|
101bb2c632 | |
|
|
de53ecbb16 | |
|
|
4c9d067325 | |
|
|
461761d4ae | |
|
|
45d9aa724d | |
|
|
8ba5c0e822 | |
|
|
0e10085b6e | |
|
|
4816a77d7f | |
|
|
cb40ea6383 | |
|
|
3cd7f6c206 | |
|
|
ba69244e2d | |
|
|
9f8dec1843 | |
|
|
bbbf09f8ab | |
|
|
2c2436aa8e | |
|
|
007f5bb08d | |
|
|
b1af62011e | |
|
|
fc8d414a19 | |
|
|
f61ebd0606 | |
|
|
58c83b5f7d | |
|
|
9cd82c64bc | |
|
|
692151b5b3 | |
|
|
9eca0a82ca | |
|
|
372f19fc0c | |
|
|
ae12a7aceb | |
|
|
da6634fd4e | |
|
|
8840e75eb2 | |
|
|
cf78e26df7 | |
|
|
cfb53e9096 | |
|
|
c96c4b5c85 | |
|
|
187086b50e | |
|
|
01668c19a5 | |
|
|
ff572e40de | |
|
|
fb22e5f045 | |
|
|
9173252975 | |
|
|
5eab5450d8 | |
|
|
0d735f631a | |
|
|
5811fdfb0e | |
|
|
b8b4762539 | |
|
|
95855a2ba7 | |
|
|
172c6fe895 | |
|
|
c5f6578377 | |
|
|
fc5213a00b | |
|
|
8dea83e32a | |
|
|
d77eb42256 | |
|
|
e5ddd219f4 | |
|
|
e15ea3e8c7 | |
|
|
b99678f96e | |
|
|
7853032f81 | |
|
|
1a251a3625 | |
|
|
3a9cdc6d9b | |
|
|
996a0dba68 | |
|
|
d4f90262e8 | |
|
|
5b69d6758a | |
|
|
6654d00ee3 | |
|
|
2660bd86bc | |
|
|
d97be45086 | |
|
|
d701d25b06 | |
|
|
96bb016325 | |
|
|
cf8f326515 | |
|
|
418e227866 | |
|
|
a33d981dcc | |
|
|
4b467fa7b0 | |
|
|
c01420471d | |
|
|
073f509618 | |
|
|
91c74b29c3 | |
|
|
d07a31741a | |
|
|
96c4744bd7 | |
|
|
7ffb4048b2 | |
|
|
f0312e34f0 | |
|
|
ce8ee04df9 | |
|
|
d1a48b3f45 | |
|
|
f5e1a5698e | |
|
|
7281a51b22 | |
|
|
4745c1dff4 | |
|
|
cbf27302e4 | |
|
|
d1b8d88a5e | |
|
|
041a588023 | |
|
|
3968270cf3 | |
|
|
51dd0f4e1f | |
|
|
4f92f3017c | |
|
|
0fd8af5a24 | |
|
|
7d120b8443 | |
|
|
892bc642e0 | |
|
|
d40af05598 | |
|
|
c4c8832a72 | |
|
|
08202b451f | |
|
|
4c3904f38f | |
|
|
94883ec937 | |
|
|
bd6fdcecdb | |
|
|
9eac19eafe | |
|
|
8dc26a1b30 | |
|
|
e0f73aff5d | |
|
|
d589404415 | |
|
|
c4ea661dcc | |
|
|
2ed2ce6968 | |
|
|
015e10682f | |
|
|
3d07fde008 | |
|
|
3fcbe33ddc | |
|
|
c551bcafeb | |
|
|
4646f1fd16 | |
|
|
3f43e9953f | |
|
|
180f2e070d | |
|
|
d1a14556f2 | |
|
|
fa9d5456f2 | |
|
|
2ab9c49655 | |
|
|
60fa9f32f2 | |
|
|
1dfee8c501 | |
|
|
c8831a3c66 | |
|
|
0d48e59c15 | |
|
|
0d177eb289 | |
|
|
9ec299c490 | |
|
|
177b2cee24 | |
|
|
8f8d5ff8ef | |
|
|
cfbd28918d | |
|
|
22d9248cd3 | |
|
|
cf11df2bf4 | |
|
|
7b641539cd | |
|
|
0eef056851 | |
|
|
f2a1be3adc | |
|
|
235fbeafe6 | |
|
|
d226abe231 | |
|
|
3d1d58ac61 | |
|
|
80ca23c2ed | |
|
|
a61e625f10 | |
|
|
9fbf167a0b | |
|
|
df2f45a26c | |
|
|
f8389d8d99 | |
|
|
7baf63523d | |
|
|
b67e8f04af | |
|
|
d675db43a3 | |
|
|
0fae071c1b | |
|
|
87b76c3311 | |
|
|
ce68167927 | |
|
|
994e0c7895 | |
|
|
0ce6575106 | |
|
|
8c38cfd497 | |
|
|
4995c1c367 | |
|
|
21cd0da1a7 | |
|
|
24a19ccfd7 | |
|
|
e7ab16e694 | |
|
|
aea7d202e6 | |
|
|
48337e440f | |
|
|
26a9d2458c | |
|
|
64ed17e654 | |
|
|
854f53e1d2 | |
|
|
1a1550ad56 | |
|
|
0e0fb98bd2 | |
|
|
e3c3ca0fed | |
|
|
37e119b8e2 | |
|
|
9704ac8520 | |
|
|
f7a908c593 | |
|
|
8abbca243c | |
|
|
412c97bd7a | |
|
|
16443e49e0 | |
|
|
15ed5a1a77 | |
|
|
91cf0dfcc8 | |
|
|
16aae8cfa2 | |
|
|
27cb9a7d52 | |
|
|
4904ef811e | |
|
|
980a3e8d57 | |
|
|
2dc87694db | |
|
|
65c2edc5a2 | |
|
|
ac066bacf9 | |
|
|
ec9f9acad9 | |
|
|
ed5591710d | |
|
|
f83d869e57 | |
|
|
36262e6295 | |
|
|
84cdc61cda | |
|
|
c7d2d8fcd7 | |
|
|
b8ab64b5e4 | |
|
|
75adf7f1c8 | |
|
|
7ef12f611e | |
|
|
278a41e60f | |
|
|
96fbe56898 | |
|
|
001e78a111 | |
|
|
4b2d6f95ac | |
|
|
9489c3ea3a | |
|
|
c7c3c9cd39 | |
|
|
7adb8316e1 | |
|
|
dddff27e78 | |
|
|
b8fbc8394a |
|
|
@ -4,7 +4,7 @@ APP_KEY=
|
|||
APP_DEBUG=true
|
||||
APP_URL=http://localhost
|
||||
|
||||
LOG_CHANNEL=stack
|
||||
LOG_CHANNEL=daily
|
||||
LOG_DEPRECATIONS_CHANNEL=null
|
||||
LOG_LEVEL=debug
|
||||
|
||||
|
|
@ -17,7 +17,7 @@ DB_PASSWORD=
|
|||
|
||||
BROADCAST_DRIVER=log
|
||||
CACHE_DRIVER=file
|
||||
FILESYSTEM_DISK=local
|
||||
FILESYSTEM_DISK=public
|
||||
QUEUE_CONNECTION=sync
|
||||
SESSION_DRIVER=file
|
||||
SESSION_LIFETIME=120
|
||||
|
|
@ -56,3 +56,5 @@ VITE_PUSHER_HOST="${PUSHER_HOST}"
|
|||
VITE_PUSHER_PORT="${PUSHER_PORT}"
|
||||
VITE_PUSHER_SCHEME="${PUSHER_SCHEME}"
|
||||
VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
|
||||
|
||||
NONG_JI_HOST = https://jxsam-beta.fjdynamics.com
|
||||
|
|
@ -16,3 +16,4 @@ yarn-error.log
|
|||
/.fleet
|
||||
/.idea
|
||||
/.vscode
|
||||
project.md
|
||||
72
README.md
72
README.md
|
|
@ -1,66 +1,6 @@
|
|||
<p align="center"><a href="https://laravel.com" target="_blank"><img src="https://raw.githubusercontent.com/laravel/art/master/logo-lockup/5%20SVG/2%20CMYK/1%20Full%20Color/laravel-logolockup-cmyk-red.svg" width="400" alt="Laravel Logo"></a></p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/laravel/framework/actions"><img src="https://github.com/laravel/framework/workflows/tests/badge.svg" alt="Build Status"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/dt/laravel/framework" alt="Total Downloads"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/v/laravel/framework" alt="Latest Stable Version"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/l/laravel/framework" alt="License"></a>
|
||||
</p>
|
||||
|
||||
## About Laravel
|
||||
|
||||
Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as:
|
||||
|
||||
- [Simple, fast routing engine](https://laravel.com/docs/routing).
|
||||
- [Powerful dependency injection container](https://laravel.com/docs/container).
|
||||
- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage.
|
||||
- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent).
|
||||
- Database agnostic [schema migrations](https://laravel.com/docs/migrations).
|
||||
- [Robust background job processing](https://laravel.com/docs/queues).
|
||||
- [Real-time event broadcasting](https://laravel.com/docs/broadcasting).
|
||||
|
||||
Laravel is accessible, powerful, and provides tools required for large, robust applications.
|
||||
|
||||
## Learning Laravel
|
||||
|
||||
Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework.
|
||||
|
||||
You may also try the [Laravel Bootcamp](https://bootcamp.laravel.com), where you will be guided through building a modern Laravel application from scratch.
|
||||
|
||||
If you don't feel like reading, [Laracasts](https://laracasts.com) can help. Laracasts contains over 2000 video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library.
|
||||
|
||||
## Laravel Sponsors
|
||||
|
||||
We would like to extend our thanks to the following sponsors for funding Laravel development. If you are interested in becoming a sponsor, please visit the Laravel [Patreon page](https://patreon.com/taylorotwell).
|
||||
|
||||
### Premium Partners
|
||||
|
||||
- **[Vehikl](https://vehikl.com/)**
|
||||
- **[Tighten Co.](https://tighten.co)**
|
||||
- **[Kirschbaum Development Group](https://kirschbaumdevelopment.com)**
|
||||
- **[64 Robots](https://64robots.com)**
|
||||
- **[Cubet Techno Labs](https://cubettech.com)**
|
||||
- **[Cyber-Duck](https://cyber-duck.co.uk)**
|
||||
- **[Many](https://www.many.co.uk)**
|
||||
- **[Webdock, Fast VPS Hosting](https://www.webdock.io/en)**
|
||||
- **[DevSquad](https://devsquad.com)**
|
||||
- **[Curotec](https://www.curotec.com/services/technologies/laravel/)**
|
||||
- **[OP.GG](https://op.gg)**
|
||||
- **[WebReinvent](https://webreinvent.com/?utm_source=laravel&utm_medium=github&utm_campaign=patreon-sponsors)**
|
||||
- **[Lendio](https://lendio.com)**
|
||||
|
||||
## Contributing
|
||||
|
||||
Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions).
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct).
|
||||
|
||||
## Security Vulnerabilities
|
||||
|
||||
If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed.
|
||||
|
||||
## License
|
||||
|
||||
The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).
|
||||
- `cp .env.example .env`
|
||||
- `php artisan key:generate`
|
||||
- `php artisan storage:link`
|
||||
- 创建数据库
|
||||
- 修改 .env 文件中的配置
|
||||
- `php artisan migrate --seed`
|
||||
|
|
@ -2,17 +2,217 @@
|
|||
|
||||
namespace App\Admin;
|
||||
|
||||
use App\Models\Keyword;
|
||||
use Slowlyo\OwlAdmin\Renderers\BaseRenderer;
|
||||
use Slowlyo\OwlAdmin\Renderers\WangEditor;
|
||||
|
||||
class Components extends BaseRenderer {
|
||||
|
||||
public function parentControl($apiUrl = '', $name ='parent_id', $labelField = 'name', $valueField = 'id')
|
||||
/**
|
||||
* 父级选择器
|
||||
*/
|
||||
public function parentControl($apiUrl = '', $name ='parent_id', $label ='父级', $labelField = 'name', $valueField = 'id')
|
||||
{
|
||||
return amisMake()->TreeSelectControl()
|
||||
->name('parent_id')->label('父级')
|
||||
->name($name)->label($label)
|
||||
->showIcon(false)
|
||||
->labelField($labelField)
|
||||
->valueField($valueField)
|
||||
->value(0)->source($apiUrl);
|
||||
->valueField($valueField)->source($apiUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* 排序字段
|
||||
*/
|
||||
public function sortControl($name ='sort', $label = '排序'){
|
||||
return amisMake()->NumberControl()
|
||||
->name($name)->label($label)
|
||||
->displayMode('enhance')
|
||||
->value(0)
|
||||
->min(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 2位小数输入框
|
||||
*/
|
||||
public function decimalControl($name ='decimal', $label = '数值'){
|
||||
return amisMake()->NumberControl()
|
||||
->name($name)->label($label)
|
||||
->kilobitSeparator(true)
|
||||
->percision(2)
|
||||
->step(0.01)
|
||||
->value(0.00)
|
||||
->min(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 富文本编辑器
|
||||
*/
|
||||
public function fuEditorControl($name ='content', $label = '内容')
|
||||
{
|
||||
return WangEditor::make()
|
||||
->name($name)
|
||||
->label($label)
|
||||
->height('auto')
|
||||
->excludeKeys(['group-video']);
|
||||
}
|
||||
|
||||
public function enableControl($name = 'is_enable', $label= '状态', $mode = 'horizontal'){
|
||||
return amisMake()->SwitchControl()
|
||||
->name($name)->label($label)
|
||||
->mode($mode)
|
||||
->onText(__('admin.extensions.status_map.enabled'))->offText(__('admin.extensions.status_map.disabled'));
|
||||
}
|
||||
|
||||
public function keywordsTagControl($name = 'tags', $label= '标签', $typeKey = ''){
|
||||
return amisMake()->TagControl()
|
||||
->name($name)->label($label)->maxTagLength(0)
|
||||
->options(Keyword::getByParentKey($typeKey)->pluck('name', 'id')->toArray());
|
||||
}
|
||||
|
||||
public function keywordsTag($label = '标签'){
|
||||
return amisMake()->Tag()->label($label)->displayMode('rounded')->color('inactive');
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成统计图config
|
||||
* 折线图或者柱状图
|
||||
*/
|
||||
public function chartLineBarConfig($title = '', array $x , array $y){
|
||||
$yAxisData = [];
|
||||
$seriesData = [];
|
||||
$color = [];
|
||||
if(!isset($y[0])){
|
||||
$_y = $y;
|
||||
$y = [0=>$_y];
|
||||
}
|
||||
$i = 0;
|
||||
$tips = '{b0}';
|
||||
foreach($y as $item) {
|
||||
//调色盘
|
||||
$color[] = $item['color'];
|
||||
//tips
|
||||
$tips.= '<br/> {a'.$i.'}: {c'.$i.'}'.($item['unit'] ?? '');
|
||||
//纵坐标
|
||||
$yAxisData[] = [
|
||||
'name'=>($item['unit'] ?? ''),
|
||||
'type' =>'value',
|
||||
'axisTick' => true,
|
||||
'alignTicks' => true,
|
||||
'axisLine' => [
|
||||
'show' => true,
|
||||
'lineStyle' => [
|
||||
'color' => $item['color'] ?? ''
|
||||
]
|
||||
],
|
||||
'axisLabel'=> [
|
||||
'formatter'=>'{value} '
|
||||
]
|
||||
];
|
||||
//数据
|
||||
$_series = [
|
||||
'name' => $item['name'] ?? '',
|
||||
'data' => $item['data'] ?? [],
|
||||
'type' => $item['type'] ?? 'line',
|
||||
'yAxisIndex' => $i,
|
||||
];
|
||||
switch($item['type']){
|
||||
case 'line':
|
||||
$_series = array_merge($_series, [
|
||||
'smooth'=> true,
|
||||
'symbol'=> 'none',
|
||||
'lineStyle' => [
|
||||
'color' => $item['color'] ?? ''
|
||||
],
|
||||
'areaStyle' => [
|
||||
'color' => $item['color'] ?? ''
|
||||
],
|
||||
]);
|
||||
break;
|
||||
case 'bar':
|
||||
$_series = array_merge($_series, [
|
||||
]);
|
||||
break;
|
||||
}
|
||||
$seriesData[] = $_series;
|
||||
$i++;
|
||||
}
|
||||
|
||||
return [
|
||||
'color' => $color,
|
||||
'title' => [
|
||||
'text' => $title,
|
||||
],
|
||||
"tooltip" => [//提示
|
||||
'trigger'=>'axis',//坐标轴触发
|
||||
'axisPointer' => [
|
||||
'type' => 'cross'
|
||||
],
|
||||
// 'formatter' => $tips
|
||||
],
|
||||
'grid' => [
|
||||
'left' => '8%',
|
||||
'right' => '8%',
|
||||
],
|
||||
'xAxis' => [
|
||||
'type' => 'category',
|
||||
'data' => $x,
|
||||
],
|
||||
'yAxis' => $yAxisData,
|
||||
'series' => $seriesData
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 散点图
|
||||
*/
|
||||
public function chartScatterConfig($title = '', array $x , array $y, array $yData = null){
|
||||
$yAxisData = [];
|
||||
$seriesData = [];
|
||||
$color = [];
|
||||
|
||||
if($yData){
|
||||
$yAxisData = [
|
||||
'type' =>'category',
|
||||
// 'splitLine'=>[
|
||||
// 'show'=>true,
|
||||
// 'lineStyle'=>[
|
||||
// 'type'=>'dashed'
|
||||
// ]
|
||||
// ],
|
||||
'axisTick' => [
|
||||
'alignWithLabel'=>true
|
||||
],
|
||||
'data'=> $yData
|
||||
];
|
||||
}
|
||||
|
||||
$seriesData = $y;
|
||||
|
||||
return [
|
||||
'color' => $color,
|
||||
'title' => [
|
||||
'text' => $title,
|
||||
],
|
||||
"tooltip" => [//提示
|
||||
'trigger'=>'axis',//坐标轴触发
|
||||
'axisPointer' => [
|
||||
'type' => 'cross'
|
||||
],
|
||||
],
|
||||
'xAxis' => [
|
||||
'type' => 'category',
|
||||
'data' => $x,
|
||||
],
|
||||
'yAxis' => $yAxisData,
|
||||
'series' => $seriesData
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成饼状图config
|
||||
* -todo
|
||||
*/
|
||||
public function chartPieConfig(){
|
||||
return ;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
namespace App\Admin\Controllers;
|
||||
|
||||
use Slowlyo\OwlAdmin\Renderers\{Form, Page};
|
||||
use Slowlyo\OwlAdmin\Renderers\{Component, Button, TableColumn, TextControl, SelectControl, DateTimeControl, SwitchControl, Tabs, Tab, Status, Html};
|
||||
use Slowlyo\OwlAdmin\Controllers\AdminController;
|
||||
use App\Services\Admin\AdminNoticeService;
|
||||
use App\Admin\Components;
|
||||
|
||||
class AdminNoticeController extends AdminController
|
||||
{
|
||||
protected string $serviceName = AdminNoticeService::class;
|
||||
|
||||
public function list(): Page
|
||||
{
|
||||
$crud = $this->baseCRUD()
|
||||
->filterTogglable(false)
|
||||
->headerToolbar([
|
||||
$this->createButton(true, 'lg'),
|
||||
...$this->baseHeaderToolBar(),
|
||||
])
|
||||
->filter($this->baseFilter()->actions([])->body([
|
||||
TextControl::make()->name('title')->label(__('admin-notice.title'))->size('md'),
|
||||
Button::make()->label(__('admin.reset'))->actionType('clear-and-submit'),
|
||||
Component::make()->setType('submit')->label(__('admin.search'))->level('primary'),
|
||||
]))
|
||||
->quickSaveItemApi(admin_url('quick-edit/admin-notices/$id'))
|
||||
->columns([
|
||||
TableColumn::make()->name('id')->label(__('admin-notice.id')),
|
||||
TableColumn::make()->name('title')->label(__('admin-notice.title')),
|
||||
TableColumn::make()->name('article.title')->label(__('admin-notice.article_id')),
|
||||
TableColumn::make()->name('is_enable')->type('switch')->label(__('admin-notice.is_enable'))->quickEdit(SwitchControl::make()->saveImmediately(true)->mode('inline')),
|
||||
TableColumn::make()->name('sort')->label(__('admin-notice.sort'))->align('center')->quickEdit(Components::make()->sortControl('sort', __('admin-notice.sort'))->saveImmediately(true)),
|
||||
TableColumn::make()->name('published_at')->label(__('admin-notice.published_at')),
|
||||
$this->rowActions(true, 'lg'),
|
||||
]);
|
||||
|
||||
return $this->baseList($crud);
|
||||
}
|
||||
|
||||
public function form(): Form
|
||||
{
|
||||
return $this->baseForm()->title('')->body([
|
||||
TextControl::make()->name('title')->label(__('admin-notice.title'))->required(true),
|
||||
SelectControl::make()->name('article_id')->label(__('admin-notice.article_id'))->source(admin_url('api/articles/options')),
|
||||
Components::make()->sortControl('sort', __('admin-notice.sort')),
|
||||
DateTimeControl::make()->name('published_at')->label(__('admin-notice.published_at'))->value(now())->format('YYYY-MM-DD HH:mm:ss')->description('*不填写则默认为创建时间'),
|
||||
SwitchControl::make()->name('is_enable')->label(__('admin-notice.is_enable'))->value(true),
|
||||
Components::make()->fuEditorControl('content', __('admin-notice.content'))
|
||||
]);
|
||||
}
|
||||
|
||||
public function detail(): Form
|
||||
{
|
||||
return $this->baseDetail()->title('')->body([
|
||||
TextControl::make()->static(true)->name('id')->label(__('admin-notice.id')),
|
||||
TextControl::make()->static(true)->name('title')->label(__('admin-notice.title')),
|
||||
TextControl::make()->static(true)->name('article.title')->label(__('admin-notice.article_id')),
|
||||
TextControl::make()->static(true)->name('sort')->label(__('admin-notice.sort')),
|
||||
TextControl::make()->name('is_enable')->label(__('admin-notice.is_enable'))->static(true)->staticSchema(Status::make()->source([
|
||||
['label' => '不显示', 'icon' => 'fa fa-close', 'color' => '#cc292e'],
|
||||
['label' => '显示', 'icon' => 'fa fa-check', 'color' => '#30bf13'],
|
||||
])),
|
||||
TextControl::make()->static(true)->name('published_at')->label(__('admin-notice.published_at')),
|
||||
TextControl::make()->static(true)->name('created_at')->label(__('admin-notice.created_at')),
|
||||
Components::make()->fuEditorControl('content', __('admin-notice.content'))->static(true),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
namespace App\Admin\Controllers;
|
||||
|
||||
use Slowlyo\OwlAdmin\Renderers\Page;
|
||||
use Slowlyo\OwlAdmin\Renderers\Form;
|
||||
use Slowlyo\OwlAdmin\Renderers\Operation;
|
||||
use Slowlyo\OwlAdmin\Services\AdminUserService;
|
||||
use Slowlyo\OwlAdmin\Services\AdminRoleService;
|
||||
use Slowlyo\OwlAdmin\Controllers\AdminController;
|
||||
|
||||
/**
|
||||
* @property AdminUserService $service
|
||||
*/
|
||||
class AdminUserController extends AdminController
|
||||
{
|
||||
protected string $serviceName = AdminUserService::class;
|
||||
|
||||
public function list(): Page
|
||||
{
|
||||
$crud = $this->baseCRUD()
|
||||
->headerToolbar([
|
||||
$this->createButton(true),
|
||||
...$this->baseHeaderToolBar(),
|
||||
])
|
||||
->filter($this->baseFilter()->body(
|
||||
amisMake()->TextControl('keyword', __('admin.keyword'))
|
||||
->size('md')
|
||||
->placeholder(__('admin.admin_user.search_username'))
|
||||
))
|
||||
->columns([
|
||||
amisMake()->TableColumn('id', 'ID')->sortable(),
|
||||
amisMake()->TableColumn('avatar', __('admin.admin_user.avatar'))->type('avatar')->src('${avatar}'),
|
||||
amisMake()->TableColumn('username', __('admin.username')),
|
||||
amisMake()->TableColumn('name', __('admin.admin_user.name')),
|
||||
amisMake()->TableColumn('roles', __('admin.admin_user.roles'))->type('each')->items(
|
||||
amisMake()->Tag()->label('${name}')->className('my-1')
|
||||
),
|
||||
amisMake()->TableColumn('created_at', __('admin.created_at'))->type('datetime')->sortable(true),
|
||||
Operation::make()->label(__('admin.actions'))->buttons([
|
||||
$this->rowEditButton(true),
|
||||
$this->rowDeleteButton()->visibleOn('${id != 1}'),
|
||||
]),
|
||||
]);
|
||||
|
||||
return $this->baseList($crud);
|
||||
}
|
||||
|
||||
public function form(): Form
|
||||
{
|
||||
return $this->baseForm()->body([
|
||||
amisMake()->ImageControl('avatar', __('admin.admin_user.avatar'))->receiver($this->uploadImagePath()),
|
||||
amisMake()->TextControl('username', __('admin.username'))->required(),
|
||||
amisMake()->TextControl('name', __('admin.admin_user.name'))->required(),
|
||||
amisMake()->TextControl('password', __('admin.password'))->type('input-password')->required()->validations(['minLength' => 6]),
|
||||
amisMake()->TextControl('confirm_password', __('admin.confirm_password'))->type('input-password')->required()->validations(['minLength' => 6]),
|
||||
amisMake()->SelectControl('roles', __('admin.admin_user.roles'))
|
||||
->searchable()
|
||||
->multiple()
|
||||
->labelField('name')
|
||||
->valueField('id')
|
||||
->joinValues(false)
|
||||
->extractValue()
|
||||
->options(AdminRoleService::make()->query()->get(['id', 'name'])),
|
||||
]);
|
||||
}
|
||||
|
||||
public function detail(): Form
|
||||
{
|
||||
return $this->baseDetail()->body([]);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
namespace App\Admin\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Slowlyo\OwlAdmin\Controllers\AdminController;
|
||||
use App\Services\Admin\AirLogService;
|
||||
|
||||
class AirLogController extends AdminController
|
||||
{
|
||||
protected string $serviceName = AirLogService::class;
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
|
||||
namespace App\Admin\Controllers;
|
||||
|
||||
use Slowlyo\OwlAdmin\Renderers\{Form, Page};
|
||||
use Slowlyo\OwlAdmin\Renderers\{Component, TableColumn, TextControl, Button, Image, Status, NumberControl, SwitchControl, ImageControl};
|
||||
use Slowlyo\OwlAdmin\Controllers\AdminController;
|
||||
use App\Services\Admin\ArticleCategoryService;
|
||||
use App\Admin\Components;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
class ArticleCategoryController extends AdminController
|
||||
{
|
||||
protected string $serviceName = ArticleCategoryService::class;
|
||||
|
||||
public function list(): Page
|
||||
{
|
||||
$crud = $this->baseCRUD()
|
||||
->filterTogglable(false)
|
||||
->loadDataOnce(true)
|
||||
->footerToolbar([])
|
||||
->headerToolbar([
|
||||
$this->createButton(true),
|
||||
...$this->baseHeaderToolBar(),
|
||||
])
|
||||
->filter($this->baseFilter()->actions([])->body([
|
||||
TextControl::make()->name('name')->label(__('article-category.name'))->size('md'),
|
||||
Components::make()->parentControl(admin_url('api/article-categories/tree-list'), 'parent_path')->size('lg'),
|
||||
Button::make()->label(__('admin.reset'))->actionType('clear-and-submit'),
|
||||
Component::make()->setType('submit')->label(__('admin.search'))->level('primary'),
|
||||
]))
|
||||
->quickSaveItemApi(admin_url('quick-edit/article-categories/$id'))
|
||||
->columns([
|
||||
TableColumn::make()->name('id')->label(__('article-category.id')),
|
||||
TableColumn::make()->name('name')->label(__('article-category.name')),
|
||||
TableColumn::make()->name('icon')->label(__('article-category.icon'))->type('image')->width(60),
|
||||
TableColumn::make()->name('sort')->label(__('article-category.sort'))->align('center')
|
||||
->quickEdit(
|
||||
Components::make()->sortControl('sort', __('article-category.sort'))->saveImmediately(true)),
|
||||
TableColumn::make()->name('is_enable')->label(__('article-category.is_enable'))->type('switch')
|
||||
->quickEdit(
|
||||
Components::make()->enableControl('is_enable', '', 'inline')->saveImmediately(true)
|
||||
),
|
||||
$this->rowActions(true),
|
||||
]);
|
||||
|
||||
return $this->baseList($crud);
|
||||
}
|
||||
|
||||
public function form(): Form
|
||||
{
|
||||
return $this->baseForm()->title('')->body([
|
||||
TextControl::make()->name('name')->label(__('article-category.name'))->required(true),
|
||||
ImageControl::make()->name('icon')->label(__('article-category.name'))->autoUpload(true),
|
||||
Components::make()->parentControl(admin_url('api/article-categories/tree-list')),
|
||||
Components::make()->sortControl('sort', __('article-category.sort')),
|
||||
Components::make()->enableControl()->value(true),
|
||||
]);
|
||||
}
|
||||
|
||||
public function detail(): Form
|
||||
{
|
||||
return $this->baseDetail()->body([
|
||||
TextControl::make()->name('id')->label(__('article-category.id'))->static(true),
|
||||
TextControl::make()->name('name')->label(__('article-category.name'))->static(true),
|
||||
TextControl::make()->name('icon')->label(__('article-category.icon'))->static(true)->staticSchema(Image::make()),
|
||||
TextControl::make()->name('sort')->label(__('article-category.sort'))->static(true),
|
||||
TextControl::make()->name('is_enable')->label(__('article-category.is_enable'))->static(true)->staticSchema(Status::make()->source([
|
||||
['label' => __('admin.extensions.status_map.disabled'), 'icon' => 'fa fa-close', 'color' => '#cc292e'],
|
||||
['label' => __('admin.extensions.status_map.enabled'), 'icon' => 'fa fa-check', 'color' => '#30bf13'],
|
||||
])),
|
||||
TextControl::make()->name('created_at')->label(__('article-category.created_at'))->static(true),
|
||||
]);
|
||||
}
|
||||
|
||||
public function getTreeList(Request $request)
|
||||
{
|
||||
return $this->service->getTree();
|
||||
}
|
||||
|
||||
public function multipleUpdate(Request $request)
|
||||
{
|
||||
$diff = $request->input('rowsDiff');
|
||||
foreach ($diff as $item) {
|
||||
$this->service->update(data_get($item, 'id'), Arr::except($item, ['id']));
|
||||
}
|
||||
return $this->response()->success();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
namespace App\Admin\Controllers;
|
||||
|
||||
use Slowlyo\OwlAdmin\Renderers\{Form, Page};
|
||||
use Slowlyo\OwlAdmin\Renderers\{Component, Button, TableColumn, TextControl, Image, ImageControl, DateTimeControl, SwitchControl, Tabs, Tab, Status, Html};
|
||||
use Slowlyo\OwlAdmin\Controllers\AdminController;
|
||||
use App\Services\Admin\ArticleService;
|
||||
use App\Admin\Components;
|
||||
|
||||
class ArticleController extends AdminController
|
||||
{
|
||||
protected string $serviceName = ArticleService::class;
|
||||
|
||||
public function list(): Page
|
||||
{
|
||||
$crud = $this->baseCRUD()
|
||||
->filterTogglable(false)
|
||||
->headerToolbar([
|
||||
$this->createButton(true, 'lg'),
|
||||
...$this->baseHeaderToolBar(),
|
||||
])
|
||||
->filter($this->baseFilter()->actions([])->body([
|
||||
TextControl::make()->name('title')->label(__('article.title'))->size('md'),
|
||||
Components::make()->parentControl(admin_url('api/article-categories/tree-list'), 'category_path', __('article.category_id'))->size('lg'),
|
||||
Button::make()->label(__('admin.reset'))->actionType('clear-and-submit'),
|
||||
Component::make()->setType('submit')->label(__('admin.search'))->level('primary'),
|
||||
]))
|
||||
->quickSaveItemApi(admin_url('quick-edit/article/$id'))
|
||||
->columns([
|
||||
TableColumn::make()->name('id')->label(__('article.id'))->sortable(true),
|
||||
TableColumn::make()->name('title')->label(__('article.title')),
|
||||
TableColumn::make()->name('category.name')->label(__('article.category_id'))->className('text-primary'),
|
||||
Image::make()->name('cover')->label(__('article.cover'))->width(100),
|
||||
TableColumn::make()->name('sub_title')->label(__('article.sub_title')),
|
||||
TableColumn::make()->name('sort')->label(__('article.sort'))->align('center')->quickEdit(Components::make()->sortControl('sort', __('article-category.sort'))->saveImmediately(true)),
|
||||
TableColumn::make()->name('is_enable')->type('switch')->label(__('article.is_enable'))->quickEdit(SwitchControl::make()->saveImmediately(true)->mode('inline')),
|
||||
TableColumn::make()->name('published_at')->label(__('article.published_at')),
|
||||
$this->rowActions(true, 'lg'),
|
||||
]);
|
||||
|
||||
return $this->baseList($crud);
|
||||
}
|
||||
|
||||
public function form(): Form
|
||||
{
|
||||
return $this->baseForm()->title('')->body([
|
||||
TextControl::make()->name('title')->label(__('article.title'))->required(true),
|
||||
Components::make()->parentControl(admin_url('api/article-categories/tree-list'), 'category_id', __('article.category_id')),
|
||||
TextControl::make()->name('sub_title')->label(__('article.sub_title')),
|
||||
ImageControl::make()->name('cover')->label(__('article.cover'))->autoUpload(true),
|
||||
Components::make()->sortControl('sort', __('article.sort')),
|
||||
DateTimeControl::make()->name('published_at')->label(__('article.published_at'))->value(now())->format('YYYY-MM-DD HH:mm:ss')->description('*不填写则默认为创建时间'),
|
||||
SwitchControl::make()->name('is_enable')->label(__('article.is_enable'))->value(true),
|
||||
Components::make()->fuEditorControl('content', __('article.content')),
|
||||
]);
|
||||
}
|
||||
|
||||
public function detail(): Form
|
||||
{
|
||||
return $this->baseDetail()->title('')->body([
|
||||
TextControl::make()->static(true)->name('id')->label(__('article.id')),
|
||||
TextControl::make()->static(true)->name('title')->label(__('article.title')),
|
||||
TextControl::make()->static(true)->name('category.name')->label(__('article.category_id')),
|
||||
TextControl::make()->static(true)->name('sub_title')->label(__('article.sub_title')),
|
||||
TextControl::make()->name('cover')->label(__('article.cover'))->static(true)->staticSchema(Image::make()),
|
||||
TextControl::make()->static(true)->name('sort')->label(__('article.sort')),
|
||||
TextControl::make()->name('is_enable')->label(__('article.is_enable'))->static(true)->staticSchema(Status::make()->source([
|
||||
['label' => '不显示', 'icon' => 'fa fa-close', 'color' => '#cc292e'],
|
||||
['label' => '显示', 'icon' => 'fa fa-check', 'color' => '#30bf13'],
|
||||
])),
|
||||
TextControl::make()->static(true)->name('published_at')->label(__('article.published_at')),
|
||||
TextControl::make()->static(true)->name('created_at')->label(__('article.created_at')),
|
||||
Components::make()->fuEditorControl('content', __('article.content'))->static(true),
|
||||
]);
|
||||
}
|
||||
|
||||
public function options()
|
||||
{
|
||||
$list = $this->service->listQuery()->select(['id as value', 'title as label'])->without('category')->get();
|
||||
|
||||
return $this->response()->success($list);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
namespace App\Admin\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Slowlyo\OwlAdmin\Controllers\AdminController;
|
||||
use App\Services\Admin\AtomizingLogService;
|
||||
|
||||
class AtomizingLogController extends AdminController
|
||||
{
|
||||
protected string $serviceName = AtomizingLogService::class;
|
||||
}
|
||||
|
|
@ -2,9 +2,77 @@
|
|||
|
||||
namespace App\Admin\Controllers;
|
||||
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Slowlyo\OwlAdmin\Controllers\AuthController as AdminAuthController;
|
||||
use Slowlyo\OwlAdmin\Renderers\{Page, Form, TextControl, ImageControl};
|
||||
|
||||
class AuthController extends AdminAuthController
|
||||
{
|
||||
public function userSetting(): \Illuminate\Http\JsonResponse|\Illuminate\Http\Resources\Json\JsonResource
|
||||
{
|
||||
$user = $this->user();
|
||||
|
||||
$form = Form::make()
|
||||
->title()
|
||||
->panelClassName('px-48 m:px-0')
|
||||
->mode('horizontal')
|
||||
->data(Arr::only($user->toArray(), ['avatar', 'name']))
|
||||
->api('put:' . admin_url('/user_setting'))
|
||||
// ->persistDataKeys(['avatar', 'name'])
|
||||
// ->resetAfterSubmit()
|
||||
->body([
|
||||
ImageControl::make()
|
||||
->label(__('admin.admin_user.avatar'))
|
||||
->name('avatar')
|
||||
->receiver($this->uploadImagePath()),
|
||||
TextControl::make()
|
||||
->label(__('admin.admin_user.name'))
|
||||
->name('name')
|
||||
->required(),
|
||||
TextControl::make()
|
||||
->type('input-password')
|
||||
->label(__('admin.old_password'))
|
||||
->name('old_password'),
|
||||
TextControl::make()
|
||||
->type('input-password')
|
||||
->label('新密码')
|
||||
->name('password')
|
||||
->validations(['minLength' => 6])
|
||||
->requiredOn('!!this.old_password'),
|
||||
TextControl::make()
|
||||
->type('input-password')
|
||||
->label(__('admin.confirm_password'))
|
||||
->name('confirm_password')
|
||||
->validations(['minLength' => 6])
|
||||
->requiredOn('!!this.old_password'),
|
||||
]);
|
||||
|
||||
return $this->response()->success(Page::make()->body($form));
|
||||
}
|
||||
|
||||
public function saveUserSetting(): \Illuminate\Http\JsonResponse|\Illuminate\Http\Resources\Json\JsonResource
|
||||
{
|
||||
$request = request();
|
||||
$request->validate([
|
||||
'old_password' => ['nullable', 'current_password:sanctum', Rule::requiredIf($request->filled(['password', 'confirm_password']))],
|
||||
'password' => Rule::requiredIf($request->filled('old_password')),
|
||||
'confirm_password' => [Rule::requiredIf($request->filled('old_password')), 'same:password'],
|
||||
], [
|
||||
'old_password.required' => '原密码必填',
|
||||
'password.required' => '新密码必填',
|
||||
'confirm_password.required' => '确认密码必填',
|
||||
'old_password.current_password' => __('admin.admin_user.old_password_error'),
|
||||
'confirm_password.same' => __('admin.admin_user.password_confirmation'),
|
||||
]);
|
||||
$user = $this->user();
|
||||
$attributes = $request->only(['avatar', 'name']);
|
||||
if ($request->filled('old_password')) {
|
||||
$attributes['password']
|
||||
= Hash::make($request->input('password'));
|
||||
}
|
||||
$user->update($attributes);
|
||||
return $this->response()->successMessage(__('admin.action_success'));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
namespace App\Admin\Controllers;
|
||||
|
||||
use Slowlyo\OwlAdmin\Renderers\{Page, Form};
|
||||
use Slowlyo\OwlAdmin\Renderers\{Component, TableColumn, TextControl, Image, Button, ImageControl, SelectControl, SwitchControl, DateTimeControl, InputKV, Status, Json};
|
||||
use Slowlyo\OwlAdmin\Controllers\AdminController;
|
||||
use App\Services\Admin\BannerService;
|
||||
use App\Admin\Components;
|
||||
|
||||
class BannerController extends AdminController
|
||||
{
|
||||
protected string $serviceName = BannerService::class;
|
||||
|
||||
public function list(): Page
|
||||
{
|
||||
$crud = $this->baseCRUD()
|
||||
->filterTogglable(false)
|
||||
->headerToolbar([
|
||||
$this->createButton(true, 'lg'),
|
||||
...$this->baseHeaderToolBar(),
|
||||
])
|
||||
->quickSaveItemApi(admin_url('quick-edit/banners/$id'))
|
||||
->filter($this->baseFilter()->actions([])->body([
|
||||
SelectControl::make()->name('place_id')->label(__('banner.place_id'))->source(admin_url('api/banner-places/options'))->size('md'),
|
||||
TextControl::make()->name('name')->label(__('banner.name'))->size('md'),
|
||||
Button::make()->label(__('admin.reset'))->actionType('clear-and-submit'),
|
||||
Component::make()->setType('submit')->label(__('admin.search'))->level('primary'),
|
||||
]))
|
||||
->columns([
|
||||
TableColumn::make()->name('place.name')->label(__('banner.place_id')),
|
||||
TableColumn::make()->name('name')->label(__('banner.name')),
|
||||
Image::make()->name('picture')->label(__('banner.picture'))->width(100),
|
||||
TableColumn::make()->name('sort')->label(__('banner.sort'))->align('center')->quickEdit(Components::make()->sortControl('sort', __('article-category.sort'))->saveImmediately(true)),
|
||||
TableColumn::make()->name('is_enable')->label(__('banner.is_enable'))->type('switch')->quickEdit(SwitchControl::make()->mode('inline')->saveImmediately(true)),
|
||||
$this->rowActions(true, 'lg'),
|
||||
]);
|
||||
|
||||
return $this->baseList($crud);
|
||||
}
|
||||
|
||||
public function form(): Form
|
||||
{
|
||||
return $this->baseForm()->title('')->body([
|
||||
SelectControl::make()->name('place_id')->label(__('banner.place_id'))->source(admin_url('api/banner-places/options'))->required(true),
|
||||
TextControl::make()->name('name')->label(__('banner.name'))->required(true),
|
||||
ImageControl::make()->name('picture')->label(__('banner.picture'))->autoUpload(true)->required(true),
|
||||
InputKV::make()->name('link_config')->label(__('banner.link_config'))->draggable(false),
|
||||
Components::make()->sortControl('sort', __('banner.sort')),
|
||||
DateTimeControl::make()->name('published_at')->label(__('banner.published_at'))->value(now())->format('YYYY-MM-DD HH:mm:ss')->description('*不填写则默认为创建时间'),
|
||||
SwitchControl::make()->name('is_enable')->label(__('banner.is_enable'))->value(true),
|
||||
]);
|
||||
}
|
||||
|
||||
public function detail(): Form
|
||||
{
|
||||
return $this->baseDetail()->title('')->body([
|
||||
TextControl::make()->name('place.name')->label(__('banner.place_id'))->static(true),
|
||||
TextControl::make()->name('name')->label(__('banner.name'))->static(true),
|
||||
TextControl::make()->name('picture')->label(__('banner.picture'))->static(true)->staticSchema(Image::make()),
|
||||
TextControl::make()->name('link_config')->label(__('banner.link_config'))->static(true)->staticSchema(Json::make()),
|
||||
TextControl::make()->name('sort')->label(__('banner.sort'))->static(true),
|
||||
TextControl::make()->name('published_at')->label(__('banner.published_at'))->static(true),
|
||||
TextControl::make()->name('is_enable')->label(__('banner.is_enable'))->static(true)->staticSchema(Status::make()->source([
|
||||
['label' => '不显示', 'icon' => 'fa fa-close', 'color' => '#cc292e'],
|
||||
['label' => '显示', 'icon' => 'fa fa-check', 'color' => '#30bf13'],
|
||||
])),
|
||||
TextControl::make()->name('created_at')->label(__('banner.created_at'))->static(true),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
|
||||
namespace App\Admin\Controllers;
|
||||
|
||||
use Slowlyo\OwlAdmin\Renderers\{Form, Page};
|
||||
use Slowlyo\OwlAdmin\Renderers\{TableColumn, TextControl, SwitchControl, NumberControl, Status, Button, Component};
|
||||
use Slowlyo\OwlAdmin\Controllers\AdminController;
|
||||
use App\Services\Admin\BannerPlaceService;
|
||||
|
||||
class BannerPlaceController extends AdminController
|
||||
{
|
||||
protected string $serviceName = BannerPlaceService::class;
|
||||
|
||||
protected string $pageTitle = '图片位置';
|
||||
|
||||
public function list(): Page
|
||||
{
|
||||
$crud = $this->baseCRUD()
|
||||
->filterTogglable(false)
|
||||
->headerToolbar([
|
||||
$this->createButton(true),
|
||||
...$this->baseHeaderToolBar(),
|
||||
])
|
||||
->quickSaveItemApi(admin_url('quick-edit/banner-places/$id'))
|
||||
->filter($this->baseFilter()->actions([])->body([
|
||||
TextControl::make()->name('key')->label(__('banner-place.key'))->size('md'),
|
||||
TextControl::make()->name('name')->label(__('banner-place.name'))->size('md'),
|
||||
Button::make()->label(__('admin.reset'))->actionType('clear-and-submit'),
|
||||
Component::make()->setType('submit')->label(__('admin.search'))->level('primary'),
|
||||
]))
|
||||
->columns([
|
||||
TableColumn::make()->name('id')->label(__('banner-place.id')),
|
||||
TableColumn::make()->name('key')->label(__('banner-place.key')),
|
||||
TableColumn::make()->name('name')->label(__('banner-place.name')),
|
||||
TableColumn::make()->name('is_enable')->type('switch')->label(__('banner-place.is_enable'))->quickEdit(SwitchControl::make()->saveImmediately(true)->mode('inline')),
|
||||
TableColumn::make()->name('remark')->label(__('banner-place.remark'))->quickEdit(TextControl::make()->saveImmediately(true)),
|
||||
$this->rowActions(true),
|
||||
]);
|
||||
|
||||
return $this->baseList($crud);
|
||||
}
|
||||
|
||||
public function form(): Form
|
||||
{
|
||||
return $this->baseForm()->title('')->body([
|
||||
TextControl::make()->name('key')->label('KEY')->required(true),
|
||||
TextControl::make()->name('name')->label('名称')->required(true),
|
||||
NumberControl::make()->name('width')->label(__('banner-place.width'))->step(1)->min(0),
|
||||
NumberControl::make()->name('height')->label(__('banner-place.height'))->step(1)->min(0),
|
||||
SwitchControl::make()->name('is_enable')->label(__('banner-place.is_enable'))->value(true),
|
||||
TextControl::make()->name('remark')->label('备注'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function detail(): Form
|
||||
{
|
||||
return $this->baseDetail()->body([
|
||||
TextControl::make()->static(true)->name('id')->label(__('banner-place.id')),
|
||||
TextControl::make()->static(true)->name('name')->label(__('banner-place.name')),
|
||||
TextControl::make()->static(true)->name('width')->label(__('banner-place.width')),
|
||||
TextControl::make()->static(true)->name('height')->label(__('banner-place.height')),
|
||||
TextControl::make()->name('is_enable')->label(__('banner-place.is_enable'))->static(true)->staticSchema(Status::make()->source([
|
||||
['label' => '不显示', 'icon' => 'fa fa-close', 'color' => '#cc292e'],
|
||||
['label' => '显示', 'icon' => 'fa fa-check', 'color' => '#30bf13'],
|
||||
])),
|
||||
TextControl::make()->static(true)->name('remark')->label(__('banner-place.remark')),
|
||||
TextControl::make()->static(true)->name('created_at')->label(__('banner-place.created_at')),
|
||||
]);
|
||||
}
|
||||
|
||||
public function options()
|
||||
{
|
||||
$list = $this->service->listQuery()->select(['id as value', 'name as label'])->get();
|
||||
|
||||
return $this->response()->success($list);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
namespace App\Admin\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Slowlyo\OwlAdmin\Controllers\AdminController;
|
||||
use App\Services\Admin\CropHarvestService;
|
||||
|
||||
class CropHarvestController extends AdminController
|
||||
{
|
||||
protected string $serviceName = CropHarvestService::class;
|
||||
}
|
||||
|
|
@ -0,0 +1,170 @@
|
|||
<?php
|
||||
|
||||
namespace App\Admin\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Slowlyo\OwlAdmin\Controllers\AdminController;
|
||||
use Slowlyo\OwlAdmin\Renderers\DialogAction;
|
||||
use Slowlyo\OwlAdmin\Renderers\Dialog;
|
||||
use App\Services\Admin\CropPlantService;
|
||||
use App\Admin\Components;
|
||||
use App\Models\RegionPlantLog;
|
||||
|
||||
class CropPlantController extends AdminController
|
||||
{
|
||||
protected string $serviceName = CropPlantService::class;
|
||||
|
||||
public function plantDetail(Request $request)
|
||||
{
|
||||
$id = $request->id;
|
||||
$plant = RegionPlantLog::find($id);
|
||||
$page = $this->basePage()->body([
|
||||
amisMake()->Grid()->columns([
|
||||
amisMake()->Wrapper()->sm(6)->body([
|
||||
amisMake()->Panel()->title('种植详情')
|
||||
->subFormMode('horizontal')
|
||||
->labelWidth(100)
|
||||
->body([
|
||||
\amisMake()->TextControl()->static(true)->name('plant_name')->label('作物名称')->value($plant->plant_name),
|
||||
\amisMake()->TextControl()->static(true)->name('director')->label('负责人')->value($plant->director),
|
||||
\amisMake()->TextControl()->static(true)->name('area')->label('种植面积m²')->value($plant->area),
|
||||
\amisMake()->TextControl()->static(true)->name('start_at')->label('开始时间')->value($plant->start_at),
|
||||
\amisMake()->TextControl()->static(true)->name('end_at')->label('结束时间')->value($plant->end_at),
|
||||
]),
|
||||
amisMake()->Panel()->title('收获记录')
|
||||
->body([
|
||||
DialogAction::make()->className('absolute top-1 right-4')->label(__('admin.create'))->dialog(
|
||||
Dialog::make()->title('收获记录')->body($this->harvestCreateForm($plant))->reload('plantDetail')
|
||||
)->reload('plant_detail'),
|
||||
\amisMake()->CRUDTable()
|
||||
->api(admin_url('crop-harvestes').'?_action=getData&plant_id='.$plant->id)
|
||||
->title('')
|
||||
->columns([
|
||||
amisMake()->TableColumn()->name('director')->label('负责人'),
|
||||
amisMake()->TableColumn()->name('area')->label('收获面积m²'),
|
||||
amisMake()->TableColumn()->name('output')->label('收获产量kg'),
|
||||
amisMake()->TableColumn()->name('harvest_at')->label('收获时间'),
|
||||
amisMake()->Operation()->label(__('admin.actions'))->buttons([
|
||||
\amisMake()
|
||||
->DialogAction()
|
||||
->label('编辑')
|
||||
->level('link')
|
||||
->dialog(Dialog::make()->title('编辑种植记录')->body($this->harvestEditForm())),
|
||||
\amisMake()
|
||||
->AjaxAction()
|
||||
->label('删除')
|
||||
->level('link')
|
||||
->actionType('ajax')
|
||||
->confirmText(__('admin.confirm_delete'))
|
||||
->api([
|
||||
'method' => 'delete',
|
||||
'url' => admin_url('crop-harvestes/${id}')
|
||||
])
|
||||
]),
|
||||
])
|
||||
]),
|
||||
]),
|
||||
amisMake()->Wrapper()->sm(6)->body([
|
||||
\amisMake()->grid()->columns([
|
||||
\amisMake()->Form()->title('搜索条件')->mode('inline')->body([
|
||||
amisMake()->DateControl()->name('start_at')->format('YYYY-MM-DD')->label('开始时间'),
|
||||
amisMake()->DateControl()->name('end_at')->format('YYYY-MM-DD')->label('结束时间'),
|
||||
amis('submit')->label(__('admin.search'))->level('primary'),
|
||||
])->target('plant_harvest_chart'),
|
||||
]),
|
||||
amisMake()->Card()->className('m-r')->body(
|
||||
amisMake()->Chart()->name('plant_harvest_chart')->api(admin_url('crop-plant-harveste-chart?plant_id=${id}&start_at=${start_at}&end_at=${end_at}'))->debug(true),
|
||||
),
|
||||
]),
|
||||
])
|
||||
]);
|
||||
return $this->response()->success($page);
|
||||
}
|
||||
|
||||
public function plantHarvestChart(Request $request)
|
||||
{
|
||||
$data = [];
|
||||
$plant = RegionPlantLog::find($request->plant_id);
|
||||
$startAt = $request->start_at;
|
||||
$endAt = $request->end_at;
|
||||
|
||||
if($plant){
|
||||
$query = $plant->harvestes();
|
||||
if($startAt){
|
||||
$query->where('harvest_at', '>=', $startAt);
|
||||
}
|
||||
if($endAt){
|
||||
$query->where('harvest_at', '<=', $endAt);
|
||||
}
|
||||
$harvestes = $query->get()->sortBy('harvest_at');
|
||||
|
||||
$times = $harvestes->pluck('harvest_at')->map(function($item, $key){
|
||||
return $item->format('Y-m-d');
|
||||
})->toArray();
|
||||
$areas = $harvestes->pluck('area')->toArray();
|
||||
$outputs = $harvestes->pluck('output')->toArray();
|
||||
$data = $this->plantHarvestChartConfig($times, $areas, $outputs);
|
||||
}
|
||||
|
||||
return $this->response()->success($data);
|
||||
}
|
||||
|
||||
private function harvestCreateForm(RegionPlantLog $plant = null) {
|
||||
return amisMake()->Form()
|
||||
->api([
|
||||
'method'=>'post',
|
||||
'url'=>admin_url('crop-harvestes'),
|
||||
"data" => [
|
||||
'plant_id'=>'${plant_id}',
|
||||
'director'=>'${harbest_director}',
|
||||
'area'=>'${harbest_area}',
|
||||
'output'=>'${output}',
|
||||
'harvest_at'=>'${harvest_at}',
|
||||
'is_last'=>'${is_last}',
|
||||
],
|
||||
])
|
||||
->body([
|
||||
\amisMake()->TextControl()->name('plant_id')->label('计划ID')->hidden(true)->value($plant?->id ?? 0),
|
||||
\amisMake()->TextControl()->name('harbest_director')->label('负责人')->required(true),
|
||||
Components::make()->decimalControl('harbest_area','收获面积m²')->required(true),
|
||||
Components::make()->decimalControl('output','收获产量kg')->required(true),
|
||||
\amisMake()->DateControl()->name('harvest_at')->format('YYYY-MM-DD')->label('收获时间')->required(true),
|
||||
amisMake()->SwitchControl()->name('is_last')->label('最后一次收获')->description('该次收获是否是此次种植最后一次?')
|
||||
]);
|
||||
}
|
||||
|
||||
private function harvestEditForm() {
|
||||
return amisMake()->Form()
|
||||
->api([
|
||||
'method'=>'put',
|
||||
'url'=>admin_url('crop-harvestes/${id}'),
|
||||
])->initApi(admin_url('crop-harvestes/${id}/edit').'?_action=getData')
|
||||
->body([
|
||||
\amisMake()->TextControl()->name('plant_id')->label('计划ID')->hidden(true),
|
||||
\amisMake()->TextControl()->name('director')->label('负责人')->required(true),
|
||||
Components::make()->decimalControl('area','收获面积m²')->required(true),
|
||||
Components::make()->decimalControl('output','收获产量kg')->required(true),
|
||||
\amisMake()->DateControl()->name('harvest_at')->format('YYYY-MM-DD')->label('收获时间')->required(true),
|
||||
]);
|
||||
}
|
||||
|
||||
private function plantHarvestChartConfig(array $times, array $areas, array $outputs)
|
||||
{
|
||||
return Components::make()->chartLineBarConfig('种植情况', $times, [
|
||||
[
|
||||
'name'=> '种植面积',
|
||||
'type' => 'line',
|
||||
'data' => $areas,
|
||||
'color' => '#91CC75',
|
||||
'unit' => 'm²'
|
||||
],
|
||||
[
|
||||
'name'=> '产量',
|
||||
'type'=>'bar',
|
||||
'data'=> $outputs,
|
||||
'color' => '#5470C6',
|
||||
'unit' => 'kg'
|
||||
]
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,199 @@
|
|||
<?php
|
||||
|
||||
namespace App\Admin\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Slowlyo\OwlAdmin\Controllers\AdminController;
|
||||
use App\Admin\Components;
|
||||
use App\Models\Region;
|
||||
use Slowlyo\OwlAdmin\Renderers\DialogAction;
|
||||
use Slowlyo\OwlAdmin\Renderers\Dialog;
|
||||
use Slowlyo\OwlAdmin\Renderers\Image;
|
||||
use Slowlyo\OwlAdmin\Renderers\TextControl;
|
||||
|
||||
class CustomRegionController extends AdminController
|
||||
{
|
||||
protected string $queryPath = 'custom-region';
|
||||
|
||||
public function regionIndex($type)
|
||||
{
|
||||
switch($type){
|
||||
case 'yuyang':
|
||||
// $this->pageTitle = '育秧列表';
|
||||
$categoryId = 1;
|
||||
break;
|
||||
case 'daotian':
|
||||
// $this->pageTitle = '稻田列表';
|
||||
$categoryId = 2;
|
||||
break;
|
||||
}
|
||||
|
||||
$page = $this->basePage()->body([
|
||||
\amisMake()->GridNav()->options(
|
||||
$this->regionList($categoryId)
|
||||
)
|
||||
]);
|
||||
|
||||
return $this->response()->success($page);
|
||||
}
|
||||
|
||||
private function regionList($categoryId)
|
||||
{
|
||||
$regionList = Region::with('monitorModes')->where('category_id', $categoryId)->get();
|
||||
$resList = [];
|
||||
foreach($regionList as $region){
|
||||
$tabs = Region::regionTabConfig($region);
|
||||
$resList[] = [
|
||||
"icon"=> $region->cover ?: url('default.svg'),
|
||||
"text"=> $region->name,
|
||||
'clickAction' => DialogAction::make()->dialog(
|
||||
Dialog::make()->title($region['name'])->body([
|
||||
\amisMake()->Tabs()->tabsMode('simple')->tabs($tabs),
|
||||
])->size('full')->actions([])
|
||||
),
|
||||
];
|
||||
}
|
||||
return $resList;
|
||||
}
|
||||
|
||||
public function getRegionTabs(Request $request){
|
||||
$region = Region::find($request->id);
|
||||
$tabs = Region::regionTabConfig($region);
|
||||
$page = \amisMake()->Tabs()->tabsMode('simple')->tabs($tabs);
|
||||
|
||||
return $this->response()->success($page);
|
||||
}
|
||||
|
||||
public function regionDetail(Request $request)
|
||||
{
|
||||
$id = $request->id;
|
||||
$region = Region::find($id);
|
||||
$page = $this->basePage()->body([
|
||||
amisMake()->Grid()->columns([
|
||||
amisMake()->Wrapper()->sm(4)->body([
|
||||
amisMake()->Panel()->title('基础详情')
|
||||
->subFormMode('horizontal')
|
||||
->labelWidth(100)
|
||||
->body([
|
||||
\amisMake()->TextControl()->static(true)->name('name')->label('名称')->value($region?->name),
|
||||
TextControl::make()->name('cover')->label(__('region.cover'))->static(true)->staticSchema(Image::make()->src($region?->cover)),
|
||||
\amisMake()->TextControl()->static(true)->name('director')->label('负责人')->value($region->director),
|
||||
\amisMake()->TextControl()->static(true)->name('category_name')->label('分类')->value($region->category?->name ?? ''),
|
||||
\amisMake()->TextControl()->static(true)->name('area')->label('面积m²')->value($region?->area ?? ''),
|
||||
\amisMake()->TextControl()->static(true)->name('sort')->label('排序')->value($region?->sort ?? '0'),
|
||||
\amisMake()->SwitchControl()->static(true)->label('显示')->value(!!$region->is_enable),
|
||||
]),
|
||||
amisMake()->Panel()->title('基础介绍')
|
||||
->body([
|
||||
amisMake()->Wrapper()->className('overflow-x-auto')->size('none')->body(
|
||||
Components::make()->fuEditorControl('content', '')->value($region?->description)->static(true)
|
||||
),
|
||||
])
|
||||
]),
|
||||
amisMake()->Wrapper()->sm(8)->body([
|
||||
amisMake()->Panel()->title('种植记录')
|
||||
->body([
|
||||
DialogAction::make()->className('absolute top-1 right-4')->label(__('admin.create'))->dialog(
|
||||
Dialog::make()->title('新增种植记录')->body($this->plantCreateForm($region))
|
||||
),
|
||||
\amisMake()->CRUDTable()->affixHeader(false)
|
||||
->title('')
|
||||
->api(admin_url('crop-plants').'?_action=getData®ion_id='.$region->id)
|
||||
->columns([
|
||||
amisMake()->TableColumn()->name('plant_name')->label('名称'),
|
||||
amisMake()->TableColumn()->name('director')->label('负责人'),
|
||||
amisMake()->TableColumn()->name('area')->label('种植面积m²'),
|
||||
amisMake()->TableColumn()->name('start_at')->label('种植时间'),
|
||||
amisMake()->TableColumn()->name('plant_state')->type('status')->source([
|
||||
1 => ['label' => '种植中','icon' => 'fa fa-warning','color' => '#ff9326'],
|
||||
2 => ['label' => '已结束','icon' => 'fa fa-check-circle','color' => '#ffb6b3']
|
||||
])->label('种植状态'),
|
||||
amisMake()->Operation()->label(__('admin.actions'))->buttons([
|
||||
amisMake()
|
||||
->DialogAction()
|
||||
->label('编辑')
|
||||
->level('link')
|
||||
->dialog(Dialog::make()->title('编辑种植记录')->body($this->plantEditForm())),
|
||||
amisMake()->AjaxAction()->label('删除')->level('link')
|
||||
->actionType('ajax')
|
||||
->confirmText(__('admin.confirm_delete'))
|
||||
->api([
|
||||
'method' => 'delete',
|
||||
'url' => admin_url('crop-plants/${id}')
|
||||
])
|
||||
]),
|
||||
])
|
||||
->itemAction([
|
||||
'type'=>'button',
|
||||
'actionType'=>'dialog',
|
||||
'dialog'=> \amisMake()->Dialog()->title('${plant_name}')
|
||||
->size('full')->actions([])->body([
|
||||
\amisMake()->Tabs()->tabsMode('simple')->name('detailTab')->tabs([
|
||||
[
|
||||
'title' => '种植详情',
|
||||
'value' => 'detail',
|
||||
'tab'=>\amisMake()->Service()->name('plant_detail')->schemaApi(admin_url('crop-plant-detail?id=${id}')),//传id
|
||||
'unmountOnExit' => true//每次切换tab都要销毁
|
||||
],
|
||||
// [
|
||||
// 'title' => '报警记录',
|
||||
// 'value' => 'warning',
|
||||
// // 'tab'=>\amisMake()->Service()->schemaApi(admin_url('custom-region-detail?id='.$region['id'])),
|
||||
// 'unmountOnExit' => true//每次切换tab都要销毁
|
||||
// ],
|
||||
[
|
||||
'title' => '虫情记录',
|
||||
'value' => 'warning',
|
||||
// 'tab'=>\amisMake()->Service()->schemaApi(admin_url('custom-region-detail?id='.$region['id'])),
|
||||
'unmountOnExit' => true//每次切换tab都要销毁
|
||||
],
|
||||
])
|
||||
])
|
||||
])
|
||||
])
|
||||
]),
|
||||
])
|
||||
]);
|
||||
|
||||
return $this->response()->success($page);
|
||||
}
|
||||
|
||||
private function plantCreateForm(Region $region = null){
|
||||
return amisMake()->Form()
|
||||
->api([
|
||||
'method'=>'post',
|
||||
'url'=>admin_url('crop-plants'),
|
||||
"data" => [
|
||||
'region_id'=>'${region_id}',
|
||||
'plant_name'=>'${plant_name}',
|
||||
'director'=>'${plant_director}',
|
||||
'area'=>'${plant_area}',
|
||||
'start_at'=>'${start_at}',
|
||||
],
|
||||
])
|
||||
->body([
|
||||
\amisMake()->TextControl()->name('region_id')->label('区域ID')->hidden(true)->value($region?->id ?? 0),
|
||||
\amisMake()->TextControl()->name('plant_name')->label('作物名称')->required(true),
|
||||
\amisMake()->TextControl()->name('plant_director')->label('负责人')->required(true),
|
||||
Components::make()->decimalControl('plant_area','种植面积')->required(true),
|
||||
\amisMake()->DateControl()->name('start_at')->format('YYYY-MM-DD')->label('种植时间')->required(true),
|
||||
]);
|
||||
}
|
||||
|
||||
private function plantEditForm(){
|
||||
return amisMake()->Form()
|
||||
->api([
|
||||
'method'=>'put',
|
||||
'url'=>admin_url('crop-plants/${id}'),
|
||||
])->initApi(admin_url('crop-plants/${id}/edit').'?_action=getData')
|
||||
->body([
|
||||
\amisMake()->TextControl()->name('id')->hidden(true),
|
||||
\amisMake()->TextControl()->name('region_id')->label('区域ID')->hidden(true),
|
||||
\amisMake()->TextControl()->name('plant_name')->label('作物名称')->required(true),
|
||||
\amisMake()->TextControl()->name('director')->label('负责人')->required(true),
|
||||
Components::make()->decimalControl('area','种植面积')->required(true),
|
||||
\amisMake()->DateControl()->name('start_at')->format('YYYY-MM-DD')->label('种植时间')->required(true),
|
||||
\amisMake()->DateControl()->name('end_at')->format('YYYY-MM-DD')->label('结束时间'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,489 @@
|
|||
<?php
|
||||
|
||||
namespace App\Admin\Controllers;
|
||||
|
||||
use Slowlyo\OwlAdmin\Renderers\{Button, Form, Page, TableColumn, TextControl, Json, Component, CRUDTable, Card, Video, InputDatetimeRange, DateTimeControl, Mapping, SelectControl};
|
||||
use Slowlyo\OwlAdmin\Controllers\AdminController;
|
||||
use App\Services\Admin\DeviceService;
|
||||
use App\Admin\Components;
|
||||
use App\Models\Device;
|
||||
use App\Models\Keyword;
|
||||
use App\Models\MonitorMode;
|
||||
use App\Models\RegionMonitor;
|
||||
use App\Models\Region;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Services\MqttService;
|
||||
|
||||
class DeviceController extends AdminController
|
||||
{
|
||||
protected string $serviceName = DeviceService::class;
|
||||
|
||||
protected string $pageTitle = '设备管理';
|
||||
|
||||
public function options(){
|
||||
$list = $this->service->listQuery()->select(['id as value', 'name as label'])->get();
|
||||
|
||||
return $this->response()->success($list);
|
||||
}
|
||||
|
||||
public function list(): Page
|
||||
{
|
||||
$crud = $this->baseCRUD()
|
||||
->filterTogglable(false)
|
||||
->headerToolbar([
|
||||
$this->createButton(true, 'lg'),
|
||||
...$this->baseHeaderToolBar(),
|
||||
])
|
||||
->filter($this->baseFilter()->actions([])->body([
|
||||
TextControl::make()->name('name')->label('名称')->size('md'),
|
||||
amisMake()->SelectControl()->name('factory')->label('厂家')->options(Keyword::getByParentKey('device-factory')->pluck('name', 'id')->toArray())->size('md'),
|
||||
amisMake()->SelectControl()->name('type')->label('类型')->options(Device::typeMap())->size('md'),
|
||||
Button::make()->label(__('admin.reset'))->actionType('clear-and-submit'),
|
||||
amis('submit')->label(__('admin.search'))->level('primary'),
|
||||
]))
|
||||
->columns([
|
||||
TableColumn::make()->name('id')->label('ID')->sortable(true),
|
||||
TableColumn::make()->name('name')->label('名称'),
|
||||
TableColumn::make()->name('factory.name')->label('厂家'),
|
||||
TableColumn::make()->name('sn')->label('编号'),
|
||||
TableColumn::make()->name('type')->type('mapping')->map(Device::typeMap())->label('类型'),
|
||||
TableColumn::make()->name('created_at')->label('创建时间')->type('datetime')->sortable(true),
|
||||
TableColumn::make()->name('updated_at')->label('更新时间')->type('datetime')->sortable(true),
|
||||
$this->rowActions(true, 'lg'),
|
||||
])->debug(true);
|
||||
|
||||
return $this->baseList($crud);
|
||||
}
|
||||
|
||||
public function form(): Form
|
||||
{
|
||||
return $this->baseForm()->body([
|
||||
TextControl::make()->name('name')->label('名称')->required(true),
|
||||
TextControl::make()->name('sn')->label('设备编号')->required(true),
|
||||
\amisMake()->SelectControl()->name('powered_by')->label('厂家')->options(Keyword::getByParentKey('device-factory')->pluck('name', 'id')->toArray())->required(true),
|
||||
TextControl::make()->name('model_sn')->label('型号'),
|
||||
\amisMake()->RadiosControl()->name('type')->label('类型')->options(Device::typeMap())->required(true),
|
||||
// 监控设备-额外参数
|
||||
// rtsp://admin:lcdx12345@172.16.40.2:554/Streaming/Channels/5201
|
||||
TextControl::make()->name('extends.rtsp_url')->hiddenOn('data.type != '.Device::TYPE_MONITOR)->label(__('device.rtsp_url')),
|
||||
// rtsp://admin:lcdx12345@172.16.40.2:554/Streaming/tracks/5201
|
||||
TextControl::make()->name('extends.rtsp_history')->hiddenOn('data.type != '.Device::TYPE_MONITOR)->label(__('device.rtsp_history')),
|
||||
]);
|
||||
}
|
||||
|
||||
public function detail(): Form
|
||||
{
|
||||
return $this->baseDetail()->body([
|
||||
TextControl::make()->static(true)->name('id')->label('ID'),
|
||||
TextControl::make()->static(true)->name('name')->label('名称'),
|
||||
TextControl::make()->static(true)->name('sn')->label('编号'),
|
||||
TextControl::make()->static(true)->name('factory.name')->label('厂家'),
|
||||
TextControl::make()->static(true)->name('model_sn')->label('型号'),
|
||||
TextControl::make()->static(true)->name('type')->label('类型')->staticSchema(
|
||||
Mapping::make()->map(Device::typeMap())),
|
||||
TextControl::make()->static(true)->name('extends')->label('扩展信息')->staticSchema(
|
||||
Json::make()),
|
||||
TextControl::make()->static(true)->name('created_at')->label('创建时间'),
|
||||
TextControl::make()->static(true)->name('updated_at')->label('更新时间')
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 监控设备列表
|
||||
*/
|
||||
public function monitorList()
|
||||
{
|
||||
if ($this->actionOfGetData()) {
|
||||
return $this->response()->success($this->service->list());
|
||||
}
|
||||
$regionId = request()->input('region_id', 0);
|
||||
if($regionId){
|
||||
$region = Region::find($regionId);
|
||||
$query = $region->monitorModes()->where('type', MonitorMode::TYPE_MONITOR)->pluck('name','monitor_id');
|
||||
}else{
|
||||
$query = MonitorMode::where('type', MonitorMode::TYPE_MONITOR)->pluck('name','id');
|
||||
}
|
||||
return CRUDTable::make()
|
||||
->mode('cards')
|
||||
->hideCheckToggler()
|
||||
->columnsCount(3)
|
||||
->perPage(6)
|
||||
->affixHeader(false)
|
||||
->filterTogglable(true)
|
||||
->set('primaryField', $this->service->primaryKey())
|
||||
->api(admin_url($this->queryPath . '?_action=getData&_type=' . Device::TYPE_MONITOR))
|
||||
->footerToolbar(['statistics', 'pagination'])
|
||||
->headerToolbar([])
|
||||
->filter($this->baseFilter()->actions([])->body([
|
||||
amisMake()->SelectControl('monitor_mode', '点位名称')->size('md')->options($query->toArray())->selectFirst(true),
|
||||
Button::make()->label(__('admin.reset'))->actionType('clear-and-submit'),
|
||||
Component::make()->setType('submit')->label(__('admin.search'))->level('primary'),
|
||||
]))
|
||||
->actions([])
|
||||
->itemClassName('col-sm-4')
|
||||
->card(Card::make()->header(['title' => '$name'])->body(Video::make()->videoType('video/x-flv')->muted(true)->autoPlay(true)->src('${src}')));
|
||||
}
|
||||
|
||||
/**
|
||||
* 监控历史视频
|
||||
*/
|
||||
public function monitorVideoList()
|
||||
{
|
||||
if ($this->actionOfGetData()) {
|
||||
return $this->response()->success($this->service->list());
|
||||
}
|
||||
$regionId = request()->input('region_id', 0);
|
||||
if($regionId){
|
||||
$region = Region::find($regionId);
|
||||
$query = $region->monitorModes()->where('type', MonitorMode::TYPE_MONITOR)->pluck('name','monitor_id');
|
||||
}else{
|
||||
$query = MonitorMode::where('type', MonitorMode::TYPE_MONITOR)->pluck('name','id');
|
||||
}
|
||||
return CRUDTable::make()
|
||||
->mode('cards')
|
||||
->hideCheckToggler()
|
||||
->columnsCount(3)
|
||||
->perPage(6)
|
||||
->affixHeader(false)
|
||||
->filterTogglable(true)
|
||||
->set('primaryField', $this->service->primaryKey())
|
||||
->api(admin_url($this->queryPath . '?_action=getData&_type=' . Device::TYPE_MONITOR . '&_mode=history'))
|
||||
->footerToolbar(['statistics', 'pagination'])
|
||||
->headerToolbar([])
|
||||
->filter($this->baseFilter()->actions([])->body([
|
||||
amisMake()->SelectControl('monitor_mode', '点位名称')->size('md')->options($query->toArray())->selectFirst(true),
|
||||
InputDatetimeRange::make()->name('date')->label('日期')->maxDate('now')->size('md'),
|
||||
Button::make()->label(__('admin.reset'))->actionType('clear-and-submit'),
|
||||
Component::make()->setType('submit')->label(__('admin.search'))->level('primary'),
|
||||
]))
|
||||
->actions([])
|
||||
->itemClassName('col-sm-4')
|
||||
->card(Card::make()->header(['title' => '$name'])->body(Video::make()->videoType('video/x-flv')->muted(true)->autoPlay(true)->src('${src}')));
|
||||
}
|
||||
|
||||
/**
|
||||
* 气象图表
|
||||
*/
|
||||
public function meteorologicalChart(){
|
||||
$regionId = request()->input('region_id', 0);
|
||||
if($regionId){
|
||||
$region = Region::find($regionId);
|
||||
$options = $region->monitorModes()->where('type', MonitorMode::TYPE_METEOROLOGICAL)->pluck('name','monitor_id')->toArray();
|
||||
}else{
|
||||
$options = MonitorMode::where('type', MonitorMode::TYPE_METEOROLOGICAL)->pluck('name','id')->toArray();
|
||||
}
|
||||
|
||||
return $this->basePage()->title('')->body([
|
||||
\amisMake()->grid()->columns([
|
||||
\amisMake()->Form()->title('搜索条件')->mode('inline')->body([
|
||||
amisMake()->SelectControl('monitor_mode', '监测点位')->size('md')->options($options)->selectFirst(true)->required(),
|
||||
\amisMake()->DateRangeControl('date_range', '时间范围')->clearable(false)->value('today,today')->required(),
|
||||
amis('submit')->label(__('admin.search'))->level('primary'),
|
||||
])->target('meteorological_chart'),
|
||||
]),
|
||||
amisMake()->Service()->name('meteorological_chart')
|
||||
->data([
|
||||
'monitor_mode'=>array_key_first($options),
|
||||
'date_range'=>mktime(0,0,0,date('m'),date('d'),date('Y')).','.mktime(0,0,0,date('m'),date('d'),date('Y'))
|
||||
])->schemaApi(
|
||||
admin_url('device-chart?monitor_mode=${monitor_mode}&date_range=${date_range}')
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
public function deviceChart(Request $request)
|
||||
{
|
||||
$dateRange = $request->input('date_range');
|
||||
if (!$dateRange) {
|
||||
$startTime = now()->startOfDay()->timestamp;
|
||||
$endTime = now()->endOfDay()->timestamp;
|
||||
} else {
|
||||
list($startTime, $endTime) = explode(',', $dateRange);
|
||||
}
|
||||
$startTime = date('Y-m-d', $startTime);
|
||||
$endTime = date('Y-m-d', $endTime);
|
||||
|
||||
$monitorMode = $request->monitor_mode ?? 0;
|
||||
$monitorMode = MonitorMode::find($monitorMode);
|
||||
|
||||
$data = $monitorMode ? $this->service->getMonitorModeDeviceChartConfig($monitorMode, $startTime.' 00:00:00', $endTime.' 23:59:59', 2):[];
|
||||
|
||||
return $this->response()->success($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 水质图表
|
||||
*/
|
||||
public function waterChart(){
|
||||
$regionId = request()->input('region_id', 0);
|
||||
if($regionId){
|
||||
$region = Region::find($regionId);
|
||||
$options = $region->monitorModes()->where('type', MonitorMode::TYPE_WATER_QUALITY)->pluck('name','monitor_id')->toArray();
|
||||
}else{
|
||||
$options = MonitorMode::where('type', MonitorMode::TYPE_WATER_QUALITY)->pluck('name','id')->toArray();
|
||||
}
|
||||
|
||||
return $this->basePage()->title('')->body([
|
||||
\amisMake()->grid()->columns([
|
||||
\amisMake()->Form()->title('搜索条件')->mode('inline')->body([
|
||||
amisMake()->SelectControl('monitor_mode', '监测点位')->size('md')->options($options)->selectFirst(true),
|
||||
\amisMake()->DateRangeControl('date_range', '时间范围')->value('today,today'),
|
||||
amis('submit')->label(__('admin.search'))->level('primary'),
|
||||
])->target('water_chart'),
|
||||
]),
|
||||
amisMake()->Service()->name('water_chart')
|
||||
->data([
|
||||
'monitor_mode'=>array_key_first($options),
|
||||
'date_range'=>mktime(0,0,0,date('m'),date('d'),date('Y')).','.mktime(0,0,0,date('m'),date('d'),date('Y'))
|
||||
])->schemaApi(
|
||||
admin_url('device-chart?monitor_mode=${monitor_mode}&date_range=${date_range}')
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 土壤图表
|
||||
*/
|
||||
public function soilChart(){
|
||||
$regionId = request()->input('region_id', 0);
|
||||
if($regionId){
|
||||
$region = Region::find($regionId);
|
||||
$options = $region->monitorModes()->where('type', MonitorMode::TYPE_SOIL)->pluck('name','monitor_id')->toArray();
|
||||
}else{
|
||||
$options = MonitorMode::where('type', MonitorMode::TYPE_SOIL)->pluck('name','id')->toArray();
|
||||
}
|
||||
|
||||
return $this->basePage()->title('')->body([
|
||||
\amisMake()->grid()->columns([
|
||||
\amisMake()->Form()->title('搜索条件')->mode('inline')->body([
|
||||
amisMake()->SelectControl('monitor_mode', '监测点位')->size('md')->options($options)->selectFirst(true),
|
||||
\amisMake()->DateRangeControl('date_range', '时间范围')->value('today,today'),
|
||||
amis('submit')->label(__('admin.search'))->level('primary'),
|
||||
])->target('soil_chart'),
|
||||
]),
|
||||
amisMake()->Service()->name('soil_chart')
|
||||
->data([
|
||||
'monitor_mode'=>array_key_first($options),
|
||||
'date_range'=>mktime(0,0,0,date('m'),date('d'),date('Y')).','.mktime(0,0,0,date('m'),date('d'),date('Y'))
|
||||
])->schemaApi(
|
||||
admin_url('device-chart?monitor_mode=${monitor_mode}&date_range=${date_range}')
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 空气条件设置
|
||||
*/
|
||||
public function airDetail(){
|
||||
$regionId = request()->input('region_id', 0);
|
||||
$config = null;
|
||||
$device = null;
|
||||
if($regionId){
|
||||
$region = Region::find($regionId);
|
||||
if($region){
|
||||
$monitorMode = $region->monitorModes()->where('type', MonitorMode::TYPE_AIR)->first();
|
||||
$device = $monitorMode->devices()->first();
|
||||
$config = $device?->extends ?? null;
|
||||
}
|
||||
}
|
||||
return amisMake()->Grid()->columns([
|
||||
amisMake()->Wrapper()->sm(6)->body([
|
||||
amisMake()->Panel()->title('智能开关设置')
|
||||
->labelWidth(100)
|
||||
->body([
|
||||
\amisMake()->Form()->title('')->mode('horizontal')
|
||||
->api([
|
||||
'method'=>'post',
|
||||
'url'=>admin_url('save-region-config').'/'.$regionId.'?type='.MonitorMode::TYPE_AIR,
|
||||
'data'=>[
|
||||
'open_is_enable' => '${open_is_enable}',
|
||||
'open_config' => '${open_config}',
|
||||
'close_is_enable' => '${close_is_enable}',
|
||||
'close_config' => '${close_config}',
|
||||
]
|
||||
])
|
||||
->data($config ? $config : [])
|
||||
->body([
|
||||
amisMake()->FieldSetControl()->title('智能开启')->body([
|
||||
\amisMake()->SwitchControl()->name('open_is_enable')->label('开关'),
|
||||
\amisMake()->ConditionBuilderControl()->name('open_config')->label('条件')->fields([
|
||||
[
|
||||
'label' => '温度', 'type'=>'number','step'=>'0.01','name'=>'box_temperature',
|
||||
'operators'=> [
|
||||
'less', 'less_or_equal', 'greater', 'greater_or_equal'
|
||||
// 'is_empty', 'is_not_empty'
|
||||
]
|
||||
],
|
||||
[
|
||||
'label' => '湿度', 'type'=>'number','step'=>'0.01','name'=>'box_humidity',
|
||||
'operators'=> [
|
||||
'less', 'less_or_equal', 'greater', 'greater_or_equal'
|
||||
// 'is_empty', 'is_not_empty'
|
||||
]
|
||||
],
|
||||
]),
|
||||
]),
|
||||
amisMake()->FieldSetControl()->title('智能关闭')->body([
|
||||
\amisMake()->SwitchControl()->name('close_is_enable')->label('开关'),
|
||||
\amisMake()->ConditionBuilderControl()->name('close_config')->label('条件')->fields([
|
||||
[
|
||||
'label' => '温度', 'type'=>'number','step'=>'0.01','name'=>'box_temperature',
|
||||
'operators'=> [
|
||||
'less', 'less_or_equal', 'greater', 'greater_or_equal',
|
||||
// 'is_empty', 'is_not_empty'
|
||||
]
|
||||
],
|
||||
[
|
||||
'label' => '湿度', 'type'=>'number','step'=>'0.01','name'=>'box_humidity',
|
||||
'operators'=> [
|
||||
'less', 'less_or_equal', 'greater', 'greater_or_equal'
|
||||
// 'is_empty', 'is_not_empty'
|
||||
]
|
||||
],
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
])
|
||||
]),
|
||||
amisMake()->Wrapper()->sm(6)->body([
|
||||
amisMake()->Panel()->title('开关记录')
|
||||
->body([
|
||||
\amisMake()->CRUDTable()
|
||||
->api(admin_url('air-logs').'?_action=getData&device_id='.($device?->id ?? 0))
|
||||
->title('')
|
||||
->columns([
|
||||
amisMake()->TableColumn()->name('content')->label('触发条件'),
|
||||
amisMake()->TableColumn()->name('type')->type('mapping')->map([
|
||||
"0"=>"<span class='label label-info'>未知</span>",
|
||||
"1"=>"<span class='label label-success'>开启</span>",
|
||||
"2"=>"<span class='label label-danger'>关闭</span>",
|
||||
"*"=> '其他:${type}'
|
||||
])->label('动作'),
|
||||
amisMake()->TableColumn()->name('created_at')->type('datetime')->label('执行时间'),
|
||||
])
|
||||
])
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 喷雾
|
||||
*/
|
||||
public function atomizingDetail(){
|
||||
$regionId = request()->input('region_id', 0);
|
||||
$config = null;
|
||||
$statusStr = '未知';
|
||||
$device = null;
|
||||
if($regionId){
|
||||
$region = Region::find($regionId);
|
||||
if($region){
|
||||
$monitorMode = $region->monitorModes()->where('type', MonitorMode::TYPE_ATOMIZING)->first();
|
||||
$device = $monitorMode->devices()->first();
|
||||
$config = $device?->extends ?? null;
|
||||
|
||||
if($monitorMode){
|
||||
//判断设备状态是否离线;
|
||||
$device = $monitorMode->devices()->first();
|
||||
if($device->state == Device::STATE_ONLINE){
|
||||
$res = (new MqttService())->getStatus();
|
||||
if($res['error']){
|
||||
switch($res['error']){
|
||||
case 1:
|
||||
$statusStr = '急停';
|
||||
break;
|
||||
case 2:
|
||||
$statusStr = '低水位报警';
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
if($res['is_running']){
|
||||
if($res['yv1']){
|
||||
$statusStr = '区域A运行中,喷雾量:'.$res['speed1'].'%';
|
||||
}elseif(['yv2']){
|
||||
$statusStr = '区域B运行中,喷雾量:'.$res['speed2'].'%';
|
||||
}
|
||||
}else{
|
||||
$statusStr = '未运行';
|
||||
}
|
||||
}
|
||||
}else{
|
||||
$statusStr = Device::stateMap()[$device->state];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return amisMake()->Grid()->columns([
|
||||
amisMake()->Wrapper()->sm(6)->body([
|
||||
amisMake()->Panel()->title('智能喷灌设置')
|
||||
->subFormMode('horizontal')
|
||||
->labelWidth(80)
|
||||
->body([
|
||||
\amisMake()->Form()->title('')->mode('horizontal')
|
||||
->api([
|
||||
'method'=>'post',
|
||||
'url'=>admin_url('save-region-config').'/'.$regionId.'?type='.MonitorMode::TYPE_ATOMIZING,
|
||||
'data'=>[
|
||||
'is_enable' => '${is_enable}',
|
||||
'config' => '${config}'
|
||||
]
|
||||
])
|
||||
->data($config ? $config : [])
|
||||
->body([
|
||||
amisMake()->FieldSetControl()->title('当前状态')->body([
|
||||
amisMake()->TextControl('status', '状态')->value($statusStr),
|
||||
])->static(),
|
||||
amisMake()->FieldSetControl()->className('mt-10')->title('定时喷灌')->body([
|
||||
\amisMake()->SwitchControl()->name('is_enable')->label('开关'),
|
||||
\amisMake()->ArrayControl()->name('config')->label('定时')->items([
|
||||
amisMake()->ComboControl()->items([
|
||||
amisMake()->SelectControl('value')->options([
|
||||
'a'=>'区域A','b'=>'区域B'
|
||||
]),
|
||||
\amisMake()->InputTimeRange()->name('time_zone'),
|
||||
Components::make()->decimalControl('input', '喷雾量%')->value(70)->percision(0)->step(1)->max(100)
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
])
|
||||
]),
|
||||
amisMake()->Wrapper()->sm(6)->body([
|
||||
amisMake()->Panel()->title('开关记录')
|
||||
->body([
|
||||
\amisMake()->CRUDTable()
|
||||
->api(admin_url('atomizing-logs').'?_action=getData&device_id='.($device?->id ?? 0))
|
||||
->title('')
|
||||
->columns([
|
||||
amisMake()->TableColumn()->name('type')->type('mapping')->map([
|
||||
"0"=>"<span class='label label-info'>未知</span>",
|
||||
"1"=>"<span class='label label-success'>开启</span>",
|
||||
"2"=>"<span class='label label-danger'>关闭</span>",
|
||||
"*"=> '其他:${type}'
|
||||
])->label('动作'),
|
||||
amisMake()->TableColumn()->name('content')->label('描述内容'),
|
||||
amisMake()->TableColumn()->name('created_at')->type('datetime')->label('操作时间'),
|
||||
])
|
||||
])
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
public function saveRegionConfig($id, Request $request)
|
||||
{
|
||||
if($id){
|
||||
$region = Region::find($id);
|
||||
if($region){
|
||||
$type = $request->input('type', 0);
|
||||
$monitorMode = $region->monitorModes()->where('type', $type)->first();
|
||||
$config = $request->input() ?? null;
|
||||
unset($config['type']);
|
||||
$res = $monitorMode->devices()->update([
|
||||
'extends' => $config,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->autoResponse($res, __('admin.save'));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
namespace App\Admin\Controllers;
|
||||
|
||||
use Slowlyo\OwlAdmin\Renderers\Page;
|
||||
use Slowlyo\OwlAdmin\Renderers\Form;
|
||||
use Slowlyo\OwlAdmin\Renderers\TableColumn;
|
||||
use Slowlyo\OwlAdmin\Renderers\TextControl;
|
||||
use Slowlyo\OwlAdmin\Controllers\AdminController;
|
||||
use App\Services\Admin\FriendLinkService;
|
||||
use App\Admin\Components;
|
||||
|
||||
class FriendLinkController extends AdminController
|
||||
{
|
||||
protected string $serviceName = FriendLinkService::class;
|
||||
|
||||
protected string $pageTitle = '友情链接';//待完善-todo
|
||||
|
||||
public function list(): Page
|
||||
{
|
||||
$crud = $this->baseCRUD()
|
||||
->filterTogglable(false)
|
||||
->headerToolbar([
|
||||
$this->createButton(true),
|
||||
...$this->baseHeaderToolBar(),
|
||||
])
|
||||
->columns([
|
||||
TableColumn::make()->name('id')->label('ID')->sortable(true),
|
||||
TableColumn::make()->name('name')->label('名称'),
|
||||
TableColumn::make()->name('created_at')->label('创建时间')->type('datetime')->sortable(true),
|
||||
TableColumn::make()->name('updated_at')->label('更新时间')->type('datetime')->sortable(true),
|
||||
$this->rowActions(true),
|
||||
]);
|
||||
|
||||
return $this->baseList($crud);
|
||||
}
|
||||
|
||||
public function form(): Form
|
||||
{
|
||||
return $this->baseForm()->body([
|
||||
TextControl::make()->name('name')->label('名称'),
|
||||
TextControl::make()->name('path')->label('链接')->required(true),
|
||||
amisMake()->ImageControl()->name('icon')->label('icon')->autoUpload(true),
|
||||
Components::make()->sortControl(),
|
||||
amisMake()->SwitchControl()->name('is_recommend')->label('推荐'),
|
||||
amisMake()->SwitchControl()->name('is_enable')->label('显示'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function detail(): Form
|
||||
{
|
||||
return $this->baseDetail()->body([
|
||||
TextControl::make()->static(true)->name('id')->label('ID'),
|
||||
TextControl::make()->static(true)->name('name')->label('名称'),
|
||||
TextControl::make()->static(true)->name('created_at')->label('创建时间'),
|
||||
TextControl::make()->static(true)->name('updated_at')->label('更新时间')
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -17,32 +17,71 @@ use Slowlyo\OwlAdmin\Controllers\AdminController;
|
|||
|
||||
class HomeController extends AdminController
|
||||
{
|
||||
protected string $queryPath = 'dashboard';
|
||||
|
||||
protected string $pageTitle = '首页';
|
||||
|
||||
public function index(): JsonResponse|JsonResource
|
||||
{
|
||||
$page = $this->basePage()->css($this->css())->body([
|
||||
Grid::make()->columns([
|
||||
// $this->frameworkInfo()->md(5),
|
||||
$this->frameworkInfo()->md(8),
|
||||
Flex::make()->items([
|
||||
$this->pieChart(),
|
||||
$this->cube(),
|
||||
]),
|
||||
]),
|
||||
Grid::make()->columns([
|
||||
$this->lineChart()->md(8),
|
||||
Flex::make()->className('h-full')->items([
|
||||
$this->clock(),
|
||||
// $this->giteeWidget(),
|
||||
$this->hitokoto(),
|
||||
])->direction('column'),
|
||||
]),
|
||||
// Grid::make()->columns([
|
||||
// $this->lineChart()->md(8),
|
||||
// Flex::make()->className('h-full')->items([
|
||||
// $this->clock(),
|
||||
// // $this->giteeWidget(),
|
||||
// ])->direction('column'),
|
||||
// ]),
|
||||
]);
|
||||
|
||||
return $this->response()->success($page);
|
||||
}
|
||||
|
||||
/**
|
||||
* 一言
|
||||
*/
|
||||
public function hitokoto()
|
||||
{
|
||||
return Card::make()
|
||||
->className('h-full clear-card-mb')
|
||||
->body(
|
||||
Custom::make()->html(<<<HTML
|
||||
<div class="h-full flex flex-col mt-5 py-8 px-7">
|
||||
<div>『</div>
|
||||
<div class="flex flex-1 items-center w-full justify-center" id="hitokoto">
|
||||
<a class="text-dark" href="#" id="hitokoto_text" target="_blank"></a>
|
||||
</div>
|
||||
<div class="flex justify-end">』</div>
|
||||
</div>
|
||||
<div class="flex justify-end mt-3">
|
||||
——
|
||||
<span id="hitokoto_from_who"></span>
|
||||
<span>「</span>
|
||||
<span id="hitokoto_from"></span>
|
||||
<span>」</span>
|
||||
</div>
|
||||
HTML
|
||||
|
||||
)->onMount(<<<JS
|
||||
fetch('https://v1.hitokoto.cn?c=i')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
const hitokoto = document.querySelector('#hitokoto_text')
|
||||
hitokoto.href = `https://hitokoto.cn/?uuid=\${data.uuid}`
|
||||
hitokoto.innerText = data.hitokoto
|
||||
document.querySelector('#hitokoto_from_who').innerText = data.from_who
|
||||
document.querySelector('#hitokoto_from').innerText = data.from
|
||||
})
|
||||
.catch(console.error)
|
||||
JS
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* gitee widget
|
||||
*/
|
||||
|
|
@ -125,25 +164,25 @@ JS
|
|||
Image::make()->src(url(config('admin.logo'))),
|
||||
Wrapper::make()->className('text-3xl mt-9')->body(config('admin.name')),
|
||||
Flex::make()->className('w-64 mt-5')->justify('space-around')->items([
|
||||
Action::make()
|
||||
->level('link')
|
||||
->label('Gitee')
|
||||
->blank(true)
|
||||
->actionType('url')
|
||||
->blank(true)
|
||||
->link('https://gitee.com/slowlyo/owl-admin'),
|
||||
Action::make()
|
||||
->level('link')
|
||||
->label('OwlAdmin 文档')
|
||||
->blank(true)
|
||||
->actionType('url')
|
||||
->link('https://slowlyo.gitee.io/owl-admin-doc/'),
|
||||
Action::make()
|
||||
->level('link')
|
||||
->label('Amis 文档')
|
||||
->blank(true)
|
||||
->actionType('url')
|
||||
->link('https://aisuda.bce.baidu.com/amis/zh-CN/docs/index'),
|
||||
// Action::make()
|
||||
// ->level('link')
|
||||
// ->label('Gitee')
|
||||
// ->blank(true)
|
||||
// ->actionType('url')
|
||||
// ->blank(true)
|
||||
// ->link('https://gitee.com/slowlyo/owl-admin'),
|
||||
// Action::make()
|
||||
// ->level('link')
|
||||
// ->label('OwlAdmin 文档')
|
||||
// ->blank(true)
|
||||
// ->actionType('url')
|
||||
// ->link('https://slowlyo.gitee.io/owl-admin-doc/'),
|
||||
// Action::make()
|
||||
// ->level('link')
|
||||
// ->label('Amis 文档')
|
||||
// ->blank(true)
|
||||
// ->actionType('url')
|
||||
// ->link('https://aisuda.bce.baidu.com/amis/zh-CN/docs/index'),
|
||||
]),
|
||||
]),
|
||||
])
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
namespace App\Admin\Controllers;
|
||||
|
||||
use Slowlyo\OwlAdmin\Controllers\IndexController as BaseController;
|
||||
use Slowlyo\OwlAdmin\Admin;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Slowlyo\OwlAdmin\Models\Extension;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class IndexController extends BaseController
|
||||
{
|
||||
public function settings(): JsonResponse|JsonResource
|
||||
{
|
||||
return $this->response()->success([
|
||||
'assets' => Admin::getAssets(),
|
||||
'app_name' => config('admin.name'),
|
||||
'locale' => config('app.locale'),
|
||||
'layout' => config('admin.layout'),
|
||||
'logo' => url(config('admin.logo')),
|
||||
|
||||
'login_captcha' => config('admin.auth.login_captcha'),
|
||||
'show_development_tools' => config('admin.show_development_tools'),
|
||||
'system_theme_setting' => array_merge(
|
||||
Admin::setting()->get('system_theme_setting', []),
|
||||
[
|
||||
"animateInDuration"=> 550,
|
||||
"animateInType"=> "alpha",
|
||||
"animateOutDuration" => 450,
|
||||
"animateOutType"=> "alpha",
|
||||
"breadcrumb"=> true,
|
||||
"breadcrumbIcon" => false,
|
||||
"footer"=> false,
|
||||
"keepAlive"=> false,
|
||||
"layoutMode"=> "default",
|
||||
"loginTemplate"=> "default",
|
||||
"menuWidth"=> 220,
|
||||
"siderTheme"=>"light",
|
||||
"theme"=>"light",
|
||||
"themeColor"=>"#4080FF",
|
||||
"topTheme"=> "light",
|
||||
'enableTab' => config('admin.layout.tab_enable'),
|
||||
'tabIcon' => config('admin.layout.tab_icon'),
|
||||
]
|
||||
),
|
||||
'enabled_extensions' => Extension::query()->where('is_enabled', 1)->pluck('name')?->toArray(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,216 @@
|
|||
<?php
|
||||
|
||||
namespace App\Admin\Controllers;
|
||||
|
||||
use Slowlyo\OwlAdmin\Renderers\Page;
|
||||
use Slowlyo\OwlAdmin\Renderers\Form;
|
||||
use Slowlyo\OwlAdmin\Renderers\TableColumn;
|
||||
use Slowlyo\OwlAdmin\Renderers\TextControl;
|
||||
use Slowlyo\OwlAdmin\Controllers\AdminController;
|
||||
use App\Services\Admin\MonitorModeService;
|
||||
use App\Admin\Components;
|
||||
use App\Models\MonitorMode;
|
||||
use App\Models\Device;
|
||||
use App\Models\Keyword;
|
||||
|
||||
/**
|
||||
* @property MonitorModeService $service
|
||||
*/
|
||||
class MonitorModeController extends AdminController
|
||||
{
|
||||
protected string $serviceName = MonitorModeService::class;
|
||||
|
||||
public function list(): Page
|
||||
{
|
||||
$crud = $this->baseCRUD()
|
||||
->filterTogglable(false)
|
||||
->headerToolbar([
|
||||
$this->createButton(true, 'lg'),
|
||||
...$this->baseHeaderToolBar(),
|
||||
])
|
||||
->filter($this->baseFilter()->actions([])->body([
|
||||
TextControl::make()->name('name')->label('名称')->size('md'),
|
||||
amisMake()->SelectControl()->name('type')->label('类型')->options(MonitorMode::typeMap())->size('md'),
|
||||
Components::make()->keywordsTagControl('group_tags', '分组', 'monitor-mode-group')->size('md'),
|
||||
amis('button')->label(__('admin.reset'))->actionType('clear-and-submit'),
|
||||
amis('submit')->label(__('admin.search'))->level('primary'),
|
||||
]))
|
||||
->columns([
|
||||
TableColumn::make()->name('id')->label('ID')->sortable(true),
|
||||
TableColumn::make()->name('name')->label('名称'),
|
||||
TableColumn::make()->name('type')->type('mapping')->map(MonitorMode::typeMap())->label('类型'),
|
||||
TableColumn::make()->name('tags')->type('mapping')->map(Keyword::tagsMap('monitor-mode-group'))->label('分组'),
|
||||
TableColumn::make()->name('created_at')->label(__('admin.created_at'))->type('datetime')->sortable(true),
|
||||
amisMake()->Operation()->label(__('admin.actions'))->buttons([
|
||||
$this->setAboutDevice(),
|
||||
$this->rowEditButton(true, 'lg'),
|
||||
$this->rowDeleteButton()
|
||||
]),
|
||||
]);
|
||||
|
||||
return $this->baseList($crud);
|
||||
}
|
||||
|
||||
public function form(): Form
|
||||
{
|
||||
return $this->baseForm()->body([
|
||||
TextControl::make()->name('name')->label('名称'),
|
||||
\amisMake()->RadiosControl()->name('type')->label('类型')->options(MonitorMode::typeMap())->required(true)->disabledOn('data.id > 0'),
|
||||
Components::make()->keywordsTagControl('group_tags', '分组', 'monitor-mode-group'),
|
||||
// Components::make()->sortControl('sort', __('admin.order')),
|
||||
// TextControl::make()->name('is_enable')->type('switch')->default(1)->label('显示'),
|
||||
// TextControl::make()->name('is_recommend')->type('switch')->default(0)->label('推荐'),
|
||||
]);
|
||||
}
|
||||
|
||||
private function setAboutDevice(){
|
||||
return amisMake()->DrawerAction()->label('设置监测设备')->icon('fa-solid fa-gear')->level('link')->drawer(
|
||||
amisMake()->Drawer()->title('设置监测设备')->resizable(true)->closeOnOutside(true)->closeOnEsc(true)->body([
|
||||
amisMake()
|
||||
->Form()
|
||||
->api(admin_url('monitor-mode-save-devices'))
|
||||
->initApi($this->getEditGetDataPath())
|
||||
->mode('normal')
|
||||
->data(['id' => '${id}'])
|
||||
->body([
|
||||
amisMake()->RadiosControl()->name('type')->label('类型')->options(MonitorMode::typeMap())->disabled(true),
|
||||
amisMake()->PickerControl('picker_devices', '监控设备')->visibleOn('data.type == '.MonitorMode::TYPE_MONITOR)
|
||||
->valueField('id')
|
||||
->labelField('name')
|
||||
->multiple(true)->joinValues(false)->extractValue(true)
|
||||
->size('lg')
|
||||
->source([
|
||||
'method' => 'get',
|
||||
'url' => admin_url('devices?_action=getData&type='.Device::TYPE_MONITOR),
|
||||
'data' => [
|
||||
'name'=>'${device_name}',
|
||||
]
|
||||
])
|
||||
->pickerSchema(
|
||||
[
|
||||
'mode' => 'table',
|
||||
'name' => 'monitor_list',
|
||||
'headerToolbar' => amisMake()->form()
|
||||
->wrapWithPanel(false)
|
||||
->className('text-right')
|
||||
->target('monitor_list')
|
||||
->mode('inline')
|
||||
->body([
|
||||
amisMake()->TextControl('device_name', '名称')->addOn(
|
||||
amis('submit')->label(__('admin.search'))->level('primary')
|
||||
)
|
||||
]),
|
||||
'columns' => [
|
||||
TableColumn::make()->name('id')->label('ID')->sortable(true),
|
||||
TableColumn::make()->name('name')->label('名称'),
|
||||
TableColumn::make()->name('sn')->label('编号'),
|
||||
TableColumn::make()->name('created_at')->label('创建时间')->type('datetime')->sortable(true),
|
||||
TableColumn::make()->name('updated_at')->label('更新时间')->type('datetime')->sortable(true),
|
||||
]
|
||||
]
|
||||
),
|
||||
amisMake()->ArrayControl('array_devices', '土壤监测')->visibleOn('data.type == '.MonitorMode::TYPE_SOIL)
|
||||
->items(
|
||||
amisMake()->ComboControl()->items([
|
||||
\amisMake()->CheckboxesControl('device_fields', '监测字段')->checkAll(true)->options(MonitorMode::fieldMap(MonitorMode::TYPE_SOIL)),
|
||||
\amisMake()->SelectControl('device_id', '监测设备')->options(Device::where('type', Device::TYPE_SOIL)->get()->pluck('name', 'id')),
|
||||
]),
|
||||
),
|
||||
amisMake()->ArrayControl('array_devices', '水质监测')->visibleOn('data.type == '.MonitorMode::TYPE_WATER_QUALITY)
|
||||
->items(
|
||||
amisMake()->ComboControl()->items([
|
||||
\amisMake()->CheckboxesControl('device_fields', '监测字段')->checkAll(true)->options(MonitorMode::fieldMap(MonitorMode::TYPE_WATER_QUALITY)),
|
||||
\amisMake()->SelectControl('device_id', '监测设备')->options(Device::where('type', Device::TYPE_WATER_QUALITY)->get()->pluck('name', 'id')),
|
||||
]),
|
||||
),
|
||||
amisMake()->ArrayControl('array_devices', '气象监测')->visibleOn('data.type == '.MonitorMode::TYPE_METEOROLOGICAL)
|
||||
->items(
|
||||
amisMake()->ComboControl()->items([
|
||||
\amisMake()->CheckboxesControl('device_fields', '监测字段')->checkAll(true)->options(MonitorMode::fieldMap(MonitorMode::TYPE_METEOROLOGICAL)),
|
||||
\amisMake()->SelectControl('device_id', '监测设备')->options(Device::where('type', Device::TYPE_METEOROLOGICAL)->get()->pluck('name', 'id')),
|
||||
]),
|
||||
),
|
||||
amisMake()->PickerControl('picker_devices', '通风设备')->visibleOn('data.type == '.MonitorMode::TYPE_AIR)
|
||||
->valueField('id')
|
||||
->labelField('name')
|
||||
->multiple(true)
|
||||
->size('lg')
|
||||
->source([
|
||||
'method' => 'get',
|
||||
'url' => admin_url('devices?_action=getData&type='.Device::TYPE_AIR),
|
||||
'data' => [
|
||||
'name'=>'${device_name}',
|
||||
]
|
||||
])
|
||||
->pickerSchema(
|
||||
[
|
||||
'mode' => 'table',
|
||||
'name' => 'monitor_list',
|
||||
'headerToolbar' => amisMake()->form()
|
||||
->wrapWithPanel(false)
|
||||
->className('text-right')
|
||||
->target('monitor_list')
|
||||
->mode('inline')
|
||||
->body([
|
||||
amisMake()->TextControl('device_name', '名称')->addOn(
|
||||
amis('submit')->label(__('admin.search'))->level('primary')
|
||||
)
|
||||
]),
|
||||
'columns' => [
|
||||
TableColumn::make()->name('id')->label('ID')->sortable(true),
|
||||
TableColumn::make()->name('name')->label('名称'),
|
||||
TableColumn::make()->name('sn')->label('编号'),
|
||||
TableColumn::make()->name('created_at')->label('创建时间')->type('datetime')->sortable(true),
|
||||
TableColumn::make()->name('updated_at')->label('更新时间')->type('datetime')->sortable(true),
|
||||
]
|
||||
]
|
||||
),
|
||||
amisMake()->PickerControl('picker_devices', '喷雾设备')->visibleOn('data.type == '.MonitorMode::TYPE_ATOMIZING)
|
||||
->valueField('id')
|
||||
->labelField('name')
|
||||
->multiple(false)//喷雾设备只能选择一个
|
||||
->size('lg')
|
||||
->source([
|
||||
'method' => 'get',
|
||||
'url' => admin_url('devices?_action=getData&type='.Device::TYPE_ATOMIZING),
|
||||
'data' => [
|
||||
'name'=>'${device_name}',
|
||||
]
|
||||
])
|
||||
->pickerSchema(
|
||||
[
|
||||
'mode' => 'table',
|
||||
'name' => 'monitor_list',
|
||||
'headerToolbar' => amisMake()->form()
|
||||
->wrapWithPanel(false)
|
||||
->className('text-right')
|
||||
->target('monitor_list')
|
||||
->mode('inline')
|
||||
->body([
|
||||
amisMake()->TextControl('device_name', '名称')->addOn(
|
||||
amis('submit')->label(__('admin.search'))->level('primary')
|
||||
)
|
||||
]),
|
||||
'columns' => [
|
||||
TableColumn::make()->name('id')->label('ID')->sortable(true),
|
||||
TableColumn::make()->name('name')->label('名称'),
|
||||
TableColumn::make()->name('sn')->label('编号'),
|
||||
TableColumn::make()->name('created_at')->label('创建时间')->type('datetime')->sortable(true),
|
||||
TableColumn::make()->name('updated_at')->label('更新时间')->type('datetime')->sortable(true),
|
||||
]
|
||||
]
|
||||
),
|
||||
])
|
||||
])->footer([
|
||||
amisMake()->Button()->label('保存')->type('submit')->level('primary'),
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
public function saveDevices()
|
||||
{
|
||||
$result = $this->service->saveDevices(request('id'), request());
|
||||
|
||||
return $this->autoResponse($result, __('admin.save'));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
<?php
|
||||
|
||||
namespace App\Admin\Controllers;
|
||||
|
||||
use Slowlyo\OwlAdmin\Renderers\Page;
|
||||
use Slowlyo\OwlAdmin\Renderers\Form;
|
||||
use Slowlyo\OwlAdmin\Renderers\TableColumn;
|
||||
use Slowlyo\OwlAdmin\Renderers\TextControl;
|
||||
use Slowlyo\OwlAdmin\Renderers\Image;
|
||||
use Slowlyo\OwlAdmin\Controllers\AdminController;
|
||||
use Slowlyo\OwlAdmin\Renderers\Button;
|
||||
use App\Services\Admin\RegionCategoryService;
|
||||
use App\Admin\Components;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class RegionCategoryController extends AdminController
|
||||
{
|
||||
protected string $serviceName = RegionCategoryService::class;
|
||||
|
||||
protected string $pageTitle = '区域分类';
|
||||
|
||||
public function list(): Page
|
||||
{
|
||||
// dd(Components::make()->parentControl('region-categories/tree-list'));
|
||||
$crud = $this->baseCRUD()
|
||||
//关闭查询
|
||||
->filterTogglable(false)
|
||||
//去掉分页-start
|
||||
->loadDataOnce(true)
|
||||
->footerToolbar([])
|
||||
//去掉分页-end
|
||||
->headerToolbar([
|
||||
$this->createButton(true),
|
||||
...$this->baseHeaderToolBar(),
|
||||
])
|
||||
->quickSaveItemApi(admin_url('quick-edit/region-categories/$id'))
|
||||
->filter(
|
||||
$this->baseFilter()->body([
|
||||
amisMake()->TextControl()->make('name')->label('分类名称')->name('name')->size('md'),
|
||||
Components::make()->parentControl(admin_url('api/region-categories/tree-list'))->size('md'),
|
||||
|
||||
Button::make()->label(__('admin.reset'))->actionType('clear-and-submit'),
|
||||
amis('submit')->label(__('admin.search'))->level('primary'),
|
||||
])->actions([])
|
||||
)
|
||||
->columns([
|
||||
TableColumn::make()->name('id')->label('ID')->sortable(true),
|
||||
TableColumn::make()->name('name')->label('名称'),
|
||||
Image::make()->name('icon')->label(__('region-category.icon'))->width(100),
|
||||
// TableColumn::make()->name('parent.name')->label('父级ID'),
|
||||
TableColumn::make()->name('sort')->label('排序'),
|
||||
TableColumn::make()->name('is_enable')->type('switch')->label('显示')->quickEdit(
|
||||
Components::make()->enableControl('is_enable', '', 'inline')->saveImmediately(true)
|
||||
),
|
||||
TableColumn::make()->name('created_at')->label('创建时间')->type('datetime')->sortable(true),
|
||||
$this->rowActions(true),
|
||||
]);
|
||||
|
||||
return $this->baseList($crud);
|
||||
}
|
||||
|
||||
public function form(): Form
|
||||
{
|
||||
return $this->baseForm()->body([
|
||||
amisMake()->TextControl()->name('name')->label('名称')->required(true),
|
||||
amisMake()->ImageControl()->name('icon')->label('封面图')->autoUpload(true),
|
||||
amisMake()->TextControl()->name('description')->label('描述'),
|
||||
Components::make()->parentControl(admin_url('api/region-categories/tree-list')),
|
||||
Components::make()->sortControl(),
|
||||
Components::make()->enableControl()->value(1),
|
||||
]);
|
||||
}
|
||||
|
||||
public function detail(): Form
|
||||
{
|
||||
return $this->baseDetail()->body([
|
||||
TextControl::make()->static(true)->name('id')->label('ID'),
|
||||
TextControl::make()->static(true)->name('name')->label('名称'),
|
||||
TextControl::make()->name('icon')->label(__('region-category.icon'))->static(true)->staticSchema(Image::make()),
|
||||
TextControl::make()->static(true)->name('description')->label('描述'),
|
||||
TextControl::make()->static(true)->name('created_at')->label('创建时间'),
|
||||
TextControl::make()->static(true)->name('updated_at')->label('更新时间')
|
||||
]);
|
||||
}
|
||||
|
||||
public function getTreeList(Request $request){
|
||||
return $this->service->getTree();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
<?php
|
||||
|
||||
namespace App\Admin\Controllers;
|
||||
|
||||
use Slowlyo\OwlAdmin\Renderers\Page;
|
||||
use Slowlyo\OwlAdmin\Renderers\Form;
|
||||
use Slowlyo\OwlAdmin\Renderers\Image;
|
||||
use Slowlyo\OwlAdmin\Renderers\TableColumn;
|
||||
use Slowlyo\OwlAdmin\Controllers\AdminController;
|
||||
use App\Services\Admin\RegionService;
|
||||
use Slowlyo\OwlAdmin\Renderers\Button;
|
||||
use App\Admin\Components;
|
||||
use App\Models\MonitorMode;
|
||||
use App\Models\Region;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class RegionController extends AdminController
|
||||
{
|
||||
protected string $serviceName = RegionService::class;
|
||||
|
||||
protected string $pageTitle = '实验田';
|
||||
|
||||
public function list(): Page
|
||||
{
|
||||
$crud = $this->baseCRUD()
|
||||
->filterTogglable(false)
|
||||
->headerToolbar([
|
||||
$this->createButton(true, 'lg'),
|
||||
...$this->baseHeaderToolBar(),
|
||||
])
|
||||
->filter(
|
||||
$this->baseFilter()->body([
|
||||
amisMake()->TextControl()->make('name')->label('名称')->name('name')->size('md'),
|
||||
Components::make()->parentControl(admin_url('api/region-categories/tree-list'), 'category_id', '分类')->size('md'),
|
||||
|
||||
Button::make()->label(__('admin.reset'))->actionType('clear-and-submit'),
|
||||
amis('submit')->label(__('admin.search'))->level('primary'),
|
||||
])->actions([])
|
||||
)
|
||||
->quickSaveItemApi(admin_url('quick-edit/regions/$id'))
|
||||
->columns([
|
||||
TableColumn::make()->name('id')->label('ID')->sortable(true),
|
||||
TableColumn::make()->name('name')->label('名称'),
|
||||
Image::make()->name('cover')->label(__('region.cover'))->width(100),
|
||||
TableColumn::make()->name('category.name')->label('分类')->className('text-primary'),
|
||||
TableColumn::make()->name('director')->label('负责人'),
|
||||
TableColumn::make()->name('area')->label('面积m²'),
|
||||
TableColumn::make()->name('is_enable')->type('switch')->label('显示')->quickEdit(Components::make()->enableControl('is_enable', '', 'inline')->saveImmediately(true)),
|
||||
TableColumn::make()->name('created_at')->label('创建时间')->type('datetime')->sortable(true),
|
||||
// TableColumn::make()->name('updated_at')->label('更新时间')->type('datetime')->sortable(true),
|
||||
amisMake()->Operation()->label(__('admin.actions'))->buttons([
|
||||
$this->showRegion(),
|
||||
$this->rowEditButton(true, 'lg'),
|
||||
$this->rowDeleteButton(),
|
||||
amisMake()->Button()->label('设置大屏位置')->level('link')->actionType('url')->blank(true)
|
||||
->url(url('/regions-position').'/'.'${id}')
|
||||
]),
|
||||
]);
|
||||
|
||||
return $this->baseList($crud);
|
||||
}
|
||||
|
||||
public function form(): Form
|
||||
{
|
||||
$monitorModeType = [];
|
||||
$monitorModes = MonitorMode::get()->groupBy('type');
|
||||
foreach(MonitorMode::typeMap() as $key => $item) {;
|
||||
$monitorModeType[] = [
|
||||
'label' => $item,
|
||||
'children' => $monitorModes->get($key)?->pluck('name', 'id')->toArray(),
|
||||
];
|
||||
}
|
||||
return $this->baseForm()->body([
|
||||
\amisMake()->TextControl()->name('name')->label('名称')->required(true),
|
||||
\amisMake()->TextControl()->name('director')->label('负责人')->required(true),
|
||||
Components::make()->parentControl(admin_url('api/region-categories/tree-list'), 'category_id', '分类')->required(),
|
||||
\amisMake()->ImageControl()->name('cover')->label('封面')->crop([
|
||||
'aspectRatio'=> 1.1,
|
||||
'scalable' => true,
|
||||
])->autoUpload(true),
|
||||
Components::make()->fuEditorControl('description', '介绍'),
|
||||
Components::make()->decimalControl('area', '面积m²'),
|
||||
Components::make()->sortControl(),
|
||||
\amisMake()->SwitchControl()->name('is_enable')->value(1)->label('显示'),
|
||||
\amisMake()->TransferControl()->name('monitorModes')->label(__('region.monitor'))
|
||||
->selectMode('chained')->searchable(true)
|
||||
->joinValues(false)->extractValue(true)
|
||||
->options($monitorModeType),
|
||||
]);
|
||||
}
|
||||
|
||||
private function showRegion(){
|
||||
return amisMake()->DialogAction()->icon('fa-regular fa-eye')->label('查看')->level('link')->dialog(
|
||||
amisMake()->Dialog()->title('查看详情')->body([
|
||||
\amisMake()->Service()->schemaApi(admin_url('custom-region-tabs?id=${id}'))
|
||||
])->size('full')->actions([])
|
||||
);
|
||||
}
|
||||
|
||||
public function setRegionPosition($id, Request $request){
|
||||
$region = Region::find($id);
|
||||
$x = $region?->position_x ?? 0;
|
||||
$y = $region?->position_y ?? 0;
|
||||
return view('region-position', compact('id', 'x', 'y'));
|
||||
}
|
||||
|
||||
public function saveRegionPosition(Request $request)
|
||||
{
|
||||
$id = $request->input('id', 0);
|
||||
$positionX = $request->input('x', 0);
|
||||
$positionY = $request->input('y', 0);
|
||||
$region = Region::find($id);
|
||||
if(!$region){
|
||||
return response()->json(['code'=>1, 'msg'=> '非法请求']);
|
||||
}
|
||||
if($positionX <= 0 || $positionY <= 0){
|
||||
return response()->json(['code'=>2, 'msg'=>'请点击地图,标记位置']);
|
||||
}
|
||||
$region->update([
|
||||
'position_x'=> $positionX,
|
||||
'position_y'=> $positionY
|
||||
]);
|
||||
//直接关闭窗口
|
||||
echo '<script>window.opener=null;window.close();</script>';
|
||||
return ;
|
||||
}
|
||||
}
|
||||
|
|
@ -5,10 +5,9 @@ namespace App\Admin\Controllers;
|
|||
use Illuminate\Http\Request;
|
||||
use Slowlyo\OwlAdmin\Renderers\Tab;
|
||||
use Slowlyo\OwlAdmin\Renderers\Tabs;
|
||||
use Slowlyo\OwlAdmin\Renderers\Alert;
|
||||
use Slowlyo\OwlAdmin\Renderers\InputKV;
|
||||
use Slowlyo\OwlAdmin\Renderers\TextControl;
|
||||
use Slowlyo\OwlAdmin\Controllers\AdminController;
|
||||
use App\Admin\Components;
|
||||
|
||||
class SettingController extends AdminController
|
||||
{
|
||||
|
|
@ -18,10 +17,7 @@ class SettingController extends AdminController
|
|||
|
||||
public function index()
|
||||
{
|
||||
$page = $this->basePage()->body([
|
||||
Alert::make()->showIcon(true)->body("此处内容仅供演示, 设置项无实际意义,实际开发中请根据实际情况进行修改。"),
|
||||
$this->form(),
|
||||
]);
|
||||
$page = $this->basePage()->body($this->form());
|
||||
|
||||
return $this->response()->success($page);
|
||||
}
|
||||
|
|
@ -32,15 +28,19 @@ class SettingController extends AdminController
|
|||
->redirect('')
|
||||
->api($this->getStorePath())
|
||||
->data(settings()->all())
|
||||
->panelClassName('')
|
||||
->title('')
|
||||
->body(
|
||||
Tabs::make()->tabs([
|
||||
Tab::make()->title('基本设置')->body([
|
||||
TextControl::make()->label('网站名称')->name('site_name'),
|
||||
InputKV::make()->label('附加配置')->name('addition_config'),
|
||||
Tab::make()->title('基地信息设置')->body([
|
||||
amisMake()->ComboControl('region_base', '基础信息')->multiple(true)->items([
|
||||
amisMake()->TextControl('name', '名称'),
|
||||
Components::make()->decimalControl('value', '值'),
|
||||
amisMake()->TextControl('unit', '单位'),
|
||||
])->description('以上明细前6项会展示在大屏中间底部。'),
|
||||
]),
|
||||
Tab::make()->title('上传设置')->body([
|
||||
TextControl::make()->label('上传域名')->name('upload_domain'),
|
||||
TextControl::make()->label('上传路径')->name('upload_path'),
|
||||
Tab::make()->title('系统设置')->body([
|
||||
TextControl::make()->label('RTSP 转流服务')->name('rtsp_url'),
|
||||
]),
|
||||
])
|
||||
);
|
||||
|
|
@ -49,10 +49,8 @@ class SettingController extends AdminController
|
|||
public function store(Request $request)
|
||||
{
|
||||
$data = $request->only([
|
||||
'site_name',
|
||||
'addition_config',
|
||||
'upload_domain',
|
||||
'upload_path',
|
||||
'rtsp_url',
|
||||
'region_base'
|
||||
]);
|
||||
|
||||
return settings()->adminSetMany($data);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
namespace App\Admin\Controllers;
|
||||
|
||||
use App\Models\MonitorMode;
|
||||
use Slowlyo\OwlAdmin\Controllers\AdminController;
|
||||
use Slowlyo\OwlAdmin\Renderers\Form;
|
||||
use Slowlyo\OwlAdmin\Renderers\Page;
|
||||
use Slowlyo\OwlAdmin\Renderers\TableColumn;
|
||||
use App\Services\Admin\WarningNoticeService;
|
||||
use App\Models\WarningNotice;
|
||||
|
||||
class WarningNoticeController extends AdminController
|
||||
{
|
||||
protected string $serviceName = WarningNoticeService::class;
|
||||
|
||||
protected string $pageTitle = '报警记录';
|
||||
|
||||
public function list(): Page
|
||||
{
|
||||
$crud = $this->baseCRUD()
|
||||
->filterTogglable(false)
|
||||
->headerToolbar([
|
||||
...$this->baseHeaderToolBar(),
|
||||
])
|
||||
->columns([
|
||||
TableColumn::make()->name('id')->label('ID')->sortable(true),
|
||||
TableColumn::make()->name('device.modes')->type('each')->items([
|
||||
'type' => 'tpl',
|
||||
'tpl' => "<span class='label label-info m-l-sm'><%= data.name %></span>",
|
||||
])->label('监测点位'),
|
||||
TableColumn::make()->name('device.name')->label('设备名称'),
|
||||
TableColumn::make()->name('lv')->type('mapping')->map(WarningNotice::lvMap())->label('报警等级')->className('text-primary'),
|
||||
TableColumn::make()->name('content')->label('报警内容'),
|
||||
|
||||
// TableColumn::make()->name('status')->label('状态'),//可以忽略
|
||||
TableColumn::make()->name('created_at')->label('报警时间')->type('datetime')->sortable(true),
|
||||
]);
|
||||
|
||||
return $this->baseList($crud);
|
||||
}
|
||||
|
||||
public function form(): Form
|
||||
{
|
||||
return $this->baseForm()->body([
|
||||
]);
|
||||
}
|
||||
|
||||
public function detail(): Form
|
||||
{
|
||||
return $this->baseDetail()->body([
|
||||
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
|
||||
namespace App\Admin\Controllers;
|
||||
|
||||
use App\Models\Device;
|
||||
use App\Models\MonitorMode;
|
||||
use App\Models\WarningNotice;
|
||||
use Illuminate\Http\Request;
|
||||
use Slowlyo\OwlAdmin\Controllers\AdminController;
|
||||
|
||||
class WarningSettingController extends AdminController
|
||||
{
|
||||
public function settingIndex(){
|
||||
$page = $this->basePage()->body([
|
||||
amisMake()->Card()->className('border-0')->body([
|
||||
$this->settingForm()
|
||||
]),
|
||||
]);
|
||||
|
||||
return $this->response()->success($page);
|
||||
}
|
||||
|
||||
private function settingForm(){
|
||||
return \amisMake()->Tabs()->name('detailTab')->tabs([
|
||||
[
|
||||
'title' => '气象预警',
|
||||
'value' => 'meteorological',
|
||||
'tab'=>$this->modeForm(Device::TYPE_METEOROLOGICAL),
|
||||
'unmountOnExit' => true//每次切换tab都要销毁
|
||||
],
|
||||
// [
|
||||
// 'title' => '水质预警',
|
||||
// 'value' => 'detail',
|
||||
// 'tab'=>'',
|
||||
// 'unmountOnExit' => true//每次切换tab都要销毁
|
||||
// ],
|
||||
[
|
||||
'title' => '土壤预警',
|
||||
'value' => 'detail',
|
||||
'tab'=>$this->modeForm(Device::TYPE_SOIL),
|
||||
'unmountOnExit' => true//每次切换tab都要销毁
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
private function modeForm($type)
|
||||
{
|
||||
$formData = $formBody = $fields = [];
|
||||
|
||||
//获取当前已设置警报内容
|
||||
$data = settings()->get('mode_warning_'.$type);
|
||||
$data && $formData = $data;
|
||||
foreach(MonitorMode::fieldMap($type) as $field => $fieldName){
|
||||
$fields[] = [
|
||||
'label' => $fieldName, 'type'=>'number','step'=>'0.01','name'=>$field,
|
||||
'operators'=> ['between']
|
||||
];
|
||||
}
|
||||
|
||||
foreach(WarningNotice::lvMap() as $lv => $lvName){
|
||||
if(!isset($formData[str($lv).'.conjunction'] )){
|
||||
$formData[str($lv).'.conjunction'] = 'or';
|
||||
}
|
||||
$formBody[] = amisMake()->ConditionBuilderControl(str($lv), $lvName)->builderMode('simple')->showANDOR(true)->fields( $fields);
|
||||
}
|
||||
return \amisMake()->Form()
|
||||
->data($formData)
|
||||
->api(admin_url('warning-setting-save'))
|
||||
->title('')->body(array_merge($formBody, [
|
||||
amisMake()->TextControl('type', '类别')->value('mode_warning_'.$type)->hidden(true),
|
||||
amis('submit')->label(__('admin.save'))->level('primary')
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
public function saveSettingConfig(Request $request){
|
||||
$res = false;
|
||||
$data = $request->input();
|
||||
$key = $data['type'] ?? '';
|
||||
if($key){
|
||||
unset($data['type']);
|
||||
$res = settings()->set($key, $data);
|
||||
}
|
||||
|
||||
return $this->autoResponse($res, __('admin.save'));
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +1,8 @@
|
|||
<?php
|
||||
use Slowlyo\OwlAdmin\Admin;
|
||||
|
||||
Admin::styles([
|
||||
'.cxd-Panel-heading { border-width: 0; background: #4080ffc7 !important; }',
|
||||
// '.cxd-Panel {box-shadow: none; }',
|
||||
'.cxd-Panel-title { color:white;font-size: 16px; }'
|
||||
]);
|
||||
|
|
@ -8,17 +8,101 @@ Route::group([
|
|||
'prefix' => config('admin.route.prefix'),
|
||||
'middleware' => config('admin.route.middleware'),
|
||||
], function (Router $router) {
|
||||
|
||||
$router->get('/_settings', '\App\Admin\Controllers\IndexController@settings');
|
||||
$router->resource('system/admin_users', App\Admin\Controllers\AdminUserController::class);
|
||||
|
||||
// 用户设置
|
||||
$router->get('user_setting', [App\Admin\Controllers\AuthController::class, 'userSetting']);
|
||||
$router->put('user_setting', [App\Admin\Controllers\AuthController::class, 'saveUserSetting']);
|
||||
|
||||
$router->group([
|
||||
'prefix' => 'api',
|
||||
], function (Router $router) {
|
||||
$router->get('keywords/tree-list', '\App\Admin\Controllers\KeywordController@getTreeList')->name('api.keywords.tree-list');
|
||||
$router->get('article-categories/tree-list', [\App\Admin\Controllers\ArticleCategoryController::class, 'getTreeList'])->name('api.article-categories.tree-list');
|
||||
$router->get('articles/options', [\App\Admin\Controllers\ArticleController::class, 'options'])->name('api.articles.options');
|
||||
$router->get('banner-places/options', [\App\Admin\Controllers\BannerPlaceController::class, 'options'])->name('api.banner-places.options');
|
||||
$router->get('region-categories/tree-list', '\App\Admin\Controllers\RegionCategoryController@getTreeList')->name('api.region-categories.tree-list');
|
||||
$router->get('devices/options', [\App\Admin\Controllers\DeviceController::class, 'options'])->name('api.devices.options');
|
||||
});
|
||||
|
||||
$router->resource('dashboard', \App\Admin\Controllers\HomeController::class);
|
||||
$router->get('dashboard', '\App\Admin\Controllers\HomeController@index');
|
||||
|
||||
//公告管理
|
||||
$router->resource('admin-notices', \App\Admin\Controllers\AdminNoticeController::class);
|
||||
$router->post('quick-edit/admin-notices/{admin_notice}',[ \App\Admin\Controllers\AdminNoticeController::class, 'update']);
|
||||
// 文章分类
|
||||
$router->resource('article-categories', \App\Admin\Controllers\ArticleCategoryController::class);
|
||||
$router->post('quick-edit/article-categories/{article_category}', [\App\Admin\Controllers\ArticleCategoryController::class, 'update']);
|
||||
$router->post('quick-edit/article-categories', [\App\Admin\Controllers\ArticleCategoryController::class, 'multipleUpdate']);
|
||||
// 文章管理
|
||||
$router->resource('articles', \App\Admin\Controllers\ArticleController::class);
|
||||
$router->post('quick-edit/article/{article}', [\App\Admin\Controllers\ArticleController::class, 'update']);
|
||||
// 图片位置
|
||||
$router->resource('banner-places', \App\Admin\Controllers\BannerPlaceController::class);
|
||||
$router->post('quick-edit/banner-places/{banner_place}',[\App\Admin\Controllers\BannerPlaceController::class, 'update']);
|
||||
// 图片管理
|
||||
$router->resource('banners', \App\Admin\Controllers\BannerController::class);
|
||||
$router->post('quick-edit/banners/{banner}',[\App\Admin\Controllers\BannerController::class, 'update']);
|
||||
//友情链接
|
||||
$router->resource('friend-links', \App\Admin\Controllers\FriendLinkController::class);
|
||||
//字典表
|
||||
$router->resource('keywords', \App\Admin\Controllers\KeywordController::class);
|
||||
//设备管理
|
||||
$router->resource('devices', \App\Admin\Controllers\DeviceController::class);
|
||||
$router->post('device-chart', '\App\Admin\Controllers\DeviceController@deviceChart');
|
||||
|
||||
//设备预警
|
||||
$router->get('warning-setting', '\App\Admin\Controllers\WarningSettingController@settingIndex');
|
||||
$router->post('warning-setting-save', '\App\Admin\Controllers\WarningSettingController@saveSettingConfig');
|
||||
$router->get('warning-notice', '\App\Admin\Controllers\WarningNoticeController@index');
|
||||
|
||||
// $router->resource('system/re-roles', \App\Admin\Controllers\AdminRoleController::class);
|
||||
//监测点位
|
||||
$router->resource('monitor-modes', \App\Admin\Controllers\MonitorModeController::class);
|
||||
$router->post('monitor-mode-save-devices', '\App\Admin\Controllers\MonitorModeController@saveDevices');
|
||||
|
||||
//区域分类
|
||||
$router->resource('region-categories', \App\Admin\Controllers\RegionCategoryController::class);
|
||||
$router->post('quick-edit/region-categories/{region_category}', '\App\Admin\Controllers\RegionCategoryController@update');
|
||||
//区域列表
|
||||
$router->resource('regions', \App\Admin\Controllers\RegionController::class);
|
||||
$router->post('quick-edit/regions/{region}',[\App\Admin\Controllers\RegionController::class, 'update']);
|
||||
|
||||
//特殊菜单
|
||||
$router->get('custom-region/{type}', '\App\Admin\Controllers\CustomRegionController@regionIndex');
|
||||
$router->post('custom-region-tabs', '\App\Admin\Controllers\CustomRegionController@getRegionTabs');
|
||||
$router->post('custom-region-detail', '\App\Admin\Controllers\CustomRegionController@regionDetail');
|
||||
|
||||
$router->get('custom-region-monitor', '\App\Admin\Controllers\DeviceController@monitorList');
|
||||
$router->post('custom-region-monitor', '\App\Admin\Controllers\DeviceController@monitorList');
|
||||
$router->get('custom-region-monitor-video', '\App\Admin\Controllers\DeviceController@monitorVideoList');
|
||||
|
||||
$router->get('custom-region-meteorological', '\App\Admin\Controllers\DeviceController@meteorologicalChart');
|
||||
$router->post('custom-region-meteorological', '\App\Admin\Controllers\DeviceController@meteorologicalChart');
|
||||
|
||||
$router->get('custom-region-water', '\App\Admin\Controllers\DeviceController@waterChart');
|
||||
$router->post('custom-region-water', '\App\Admin\Controllers\DeviceController@waterChart');
|
||||
|
||||
$router->get('custom-region-soil', '\App\Admin\Controllers\DeviceController@soilChart');
|
||||
$router->post('custom-region-soil', '\App\Admin\Controllers\DeviceController@soilChart');
|
||||
|
||||
$router->post('custom-region-air', '\App\Admin\Controllers\DeviceController@airDetail');
|
||||
|
||||
$router->post('custom-region-atomizing', '\App\Admin\Controllers\DeviceController@atomizingDetail');
|
||||
|
||||
$router->post('save-region-config/{id}', '\App\Admin\Controllers\DeviceController@saveRegionConfig');
|
||||
|
||||
//种植记录
|
||||
$router->resource('crop-plants', \App\Admin\Controllers\CropPlantController::class)->only(['index','store', 'edit', 'update', 'destroy']);
|
||||
$router->resource('crop-harvestes', \App\Admin\Controllers\CropHarvestController::class)->only(['index','store', 'edit', 'update', 'destroy']);
|
||||
$router->get('crop-plant-harveste-chart', '\App\Admin\Controllers\CropPlantController@plantHarvestChart');
|
||||
$router->post('crop-plant-detail', '\App\Admin\Controllers\CropPlantController@plantDetail');
|
||||
|
||||
$router->resource('system/settings', \App\Admin\Controllers\SettingController::class);
|
||||
|
||||
//喷雾自动开启/关闭日志
|
||||
$router->resource('atomizing-logs', \App\Admin\Controllers\AtomizingLogController::class)->only(['index']);
|
||||
//通风自动开启/关闭日志
|
||||
$router->resource('air-logs', \App\Admin\Controllers\AirLogController::class)->only(['index']);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
namespace App\Casts;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Facades\Storage as StorageFacades;
|
||||
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
|
||||
|
||||
/**
|
||||
* 转换文件存储路径
|
||||
*
|
||||
* get: 返回全路径
|
||||
* set: 原样保存
|
||||
*/
|
||||
class Storage implements CastsAttributes
|
||||
{
|
||||
protected $disk;
|
||||
|
||||
function __construct($disk = 'public')
|
||||
{
|
||||
$this->disk = $disk;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cast the given value.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Model $model
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @param array $attributes
|
||||
* @return array
|
||||
*/
|
||||
public function get($model, $key, $value, $attributes)
|
||||
{
|
||||
return $value ? (Str::startsWith($value, ['http://', 'https://']) ? $value : StorageFacades::disk($this->disk)->url($value)) : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the given value for storage.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Model $model
|
||||
* @param string $key
|
||||
* @param array $value
|
||||
* @param array $attributes
|
||||
* @return string
|
||||
*/
|
||||
public function set($model, $key, $value, $attributes)
|
||||
{
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Device;
|
||||
use App\Models\MeteorologicalDailyReport;
|
||||
use App\Models\MeteorologicalReport;
|
||||
use App\Models\SoilDailyReport;
|
||||
use App\Models\SoilReport;
|
||||
use App\Models\WaterQualityDailyReport;
|
||||
use App\Models\WaterQualityReport;
|
||||
use App\Services\DeviceLogService;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class DeviceLogDailyReportCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'device-log:daily-report
|
||||
{factory}
|
||||
{--sleep=300 : 监控报告生产后的休眠时间(秒)}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = '按设备厂商生成监控报告';
|
||||
|
||||
/**
|
||||
* @var \App\Services\DeviceLogService
|
||||
*/
|
||||
protected $deviceLogService;
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle(DeviceLogService $deviceLogService)
|
||||
{
|
||||
$this->deviceLogService = $deviceLogService;
|
||||
|
||||
$factory = $this->argument('factory');
|
||||
|
||||
$sleep = (int) value(fn ($sleep) => is_numeric($sleep) ? $sleep : 300, $this->option('sleep'));
|
||||
|
||||
while (true) {
|
||||
/** @var \Illuminate\Database\Eloquent\Collection */
|
||||
$devices = Device::with(['factory'])->poweredBy($factory)->get();
|
||||
|
||||
foreach ($devices as $device) {
|
||||
switch ($device->factory?->key) {
|
||||
case 'link-os':
|
||||
$this->createReportToLinkosDevice($device);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sleep($sleep);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 linkos 设备报告
|
||||
*/
|
||||
protected function createReportToLinkosDevice(Device $device): void
|
||||
{
|
||||
[$lastReportedAt, $latestReportedAt] = value(function (Device $device) {
|
||||
$lastReportedAt = null;
|
||||
|
||||
$latestReportedAt = null;
|
||||
|
||||
switch ($device->type) {
|
||||
case Device::TYPE_WATER_QUALITY:
|
||||
$lastReportedAt = WaterQualityDailyReport::where('device_id', $device->id)
|
||||
->latest('reported_at')
|
||||
->value('reported_at');
|
||||
|
||||
$lastReportedAt ??= WaterQualityReport::where('device_id', $device->id)
|
||||
->oldest('reported_at')
|
||||
->value('reported_at');
|
||||
|
||||
if ($lastReportedAt) {
|
||||
$latestReportedAt = WaterQualityReport::where('device_id', $device->id)
|
||||
->latest('reported_at')
|
||||
->value('reported_at');
|
||||
}
|
||||
break;
|
||||
|
||||
case Device::TYPE_METEOROLOGICAL:
|
||||
$lastReportedAt = MeteorologicalDailyReport::where('device_id', $device->id)
|
||||
->latest('reported_at')
|
||||
->value('reported_at');
|
||||
|
||||
$lastReportedAt ??= MeteorologicalReport::where('device_id', $device->id)
|
||||
->oldest('reported_at')
|
||||
->value('reported_at');
|
||||
|
||||
if ($lastReportedAt) {
|
||||
$latestReportedAt = MeteorologicalReport::where('device_id', $device->id)
|
||||
->latest('reported_at')
|
||||
->value('reported_at');
|
||||
}
|
||||
break;
|
||||
|
||||
case Device::TYPE_SOIL:
|
||||
$lastReportedAt = SoilDailyReport::where('device_id', $device->id)
|
||||
->latest('reported_at')
|
||||
->value('reported_at');
|
||||
|
||||
$lastReportedAt ??= SoilReport::where('device_id', $device->id)
|
||||
->oldest('reported_at')
|
||||
->value('reported_at');
|
||||
|
||||
if ($lastReportedAt) {
|
||||
$latestReportedAt = SoilReport::where('device_id', $device->id)
|
||||
->latest('reported_at')
|
||||
->value('reported_at');
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return [$lastReportedAt, $latestReportedAt];
|
||||
}, $device);
|
||||
|
||||
if ($lastReportedAt === null || $latestReportedAt === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var \Carbon\Carbon */
|
||||
$startAt = $lastReportedAt->copy()->startOfDay();
|
||||
|
||||
do {
|
||||
$this->deviceLogService->createDailyReportToLinkosDevice($device, $startAt->copy());
|
||||
|
||||
$startAt->addDay();
|
||||
} while ($latestReportedAt->gte($startAt));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Device;
|
||||
use App\Models\MeteorologicalReport;
|
||||
use App\Models\SoilReport;
|
||||
use App\Models\WaterQualityReport;
|
||||
use App\Services\DeviceLogService;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class DeviceLogReportCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'device-log:report
|
||||
{factory}
|
||||
{--sleep=300 : 监控报告生产后的休眠时间(秒)}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = '按设备厂商生成监控报告';
|
||||
|
||||
/**
|
||||
* @var \App\Services\DeviceLogService
|
||||
*/
|
||||
protected $deviceLogService;
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle(DeviceLogService $deviceLogService)
|
||||
{
|
||||
$this->deviceLogService = $deviceLogService;
|
||||
|
||||
$factory = $this->argument('factory');
|
||||
|
||||
$sleep = (int) value(fn ($sleep) => is_numeric($sleep) ? $sleep : 300, $this->option('sleep'));
|
||||
|
||||
while (true) {
|
||||
/** @var \Illuminate\Database\Eloquent\Collection */
|
||||
$devices = Device::with(['factory'])->poweredBy($factory)->get();
|
||||
|
||||
foreach ($devices as $device) {
|
||||
switch ($device->factory?->key) {
|
||||
case 'link-os':
|
||||
$this->createReportToLinkosDevice($device);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sleep($sleep);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 linkos 设备报告
|
||||
*/
|
||||
protected function createReportToLinkosDevice(Device $device): void
|
||||
{
|
||||
$lastReportedAt = match ($device->type) {
|
||||
Device::TYPE_SOIL => SoilReport::where('device_id', $device->id)->latest('reported_at')->value('reported_at'),
|
||||
Device::TYPE_WATER_QUALITY => WaterQualityReport::where('device_id', $device->id)->latest('reported_at')->value('reported_at'),
|
||||
Device::TYPE_METEOROLOGICAL => MeteorologicalReport::where('device_id', $device->id)->latest('reported_at')->value('reported_at'),
|
||||
default => null,
|
||||
};
|
||||
|
||||
if (is_null($lastReportedAt ??= $device->logs()->oldest('reported_at')->value('reported_at'))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_null($latestReportedAt = $device->logs()->latest('reported_at')->value('reported_at'))) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var \Carbon\Carbon */
|
||||
$startAt = $lastReportedAt->copy()->startOfHour();
|
||||
|
||||
do {
|
||||
$this->deviceLogService->createReportToLinkosDevice($device, $startAt->copy());
|
||||
|
||||
$startAt->addHour();
|
||||
} while ($latestReportedAt->gte($startAt));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Device;
|
||||
use App\Services\DeviceLogService;
|
||||
use Illuminate\Console\Command;
|
||||
use Throwable;
|
||||
|
||||
class DeviceLogSyncCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'device-log:sync
|
||||
{factory}
|
||||
{--sleep=60 : 设备数据同步完成后的休眠时间(秒)}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = '按设备厂商同步数据';
|
||||
|
||||
/**
|
||||
* @var \App\Services\DeviceLogService
|
||||
*/
|
||||
protected $deviceLogService;
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle(DeviceLogService $deviceLogService)
|
||||
{
|
||||
$this->deviceLogService = $deviceLogService;
|
||||
|
||||
$factory = $this->argument('factory');
|
||||
|
||||
$sleep = (int) value(fn ($sleep) => is_numeric($sleep) ? $sleep : 60, $this->option('sleep'));
|
||||
|
||||
while (true) {
|
||||
$this->runSync($factory);
|
||||
|
||||
sleep($sleep);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行同步
|
||||
*/
|
||||
protected function runSync(string $factory): void
|
||||
{
|
||||
$end = now();
|
||||
$start = $end->copy()->subHours(1);
|
||||
|
||||
$this->info('------------------------------------------');
|
||||
$this->info('开始时间: '. $start);
|
||||
$this->info('结束时间: '. $end);
|
||||
|
||||
/** @var \Illuminate\Database\Eloquent\Collection */
|
||||
$devices = Device::with(['factory'])->poweredBy($factory)->get();
|
||||
|
||||
/** @var \App\Models\Device */
|
||||
foreach ($devices as $device) {
|
||||
// 忽略通风设备
|
||||
if ($device->isTypeAir()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->info('==========================================');
|
||||
$this->info('设备编号: ' . $device->sn);
|
||||
$this->info('设备名称: ' . $device->name);
|
||||
$this->info('设备类型: ' . match ($device->type) {
|
||||
Device::TYPE_SOIL => '土壤设备',
|
||||
Device::TYPE_WATER_QUALITY => '水质设备',
|
||||
Device::TYPE_METEOROLOGICAL => '气象设备',
|
||||
Device::TYPE_AIR => '通风设备',
|
||||
default => '其它',
|
||||
});
|
||||
|
||||
try {
|
||||
$this->deviceLogService->sync($device, $start, $end);
|
||||
|
||||
$this->info('同步成功!');
|
||||
} catch (Throwable $e) {
|
||||
report($e);
|
||||
|
||||
$this->error('同步失败: '. $e->getMessage());
|
||||
}
|
||||
|
||||
$this->info('==========================================');
|
||||
}
|
||||
|
||||
$this->info('------------------------------------------');
|
||||
$this->newLine();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class ModelFillable extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'model:fillable {table} {--connection}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'get model fillable ';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$table = $this->argument('table');
|
||||
$ignore = ['id', 'created_at', 'updated_at', 'deleted_at'];
|
||||
$connection = $this->option('connection');
|
||||
if (!$connection) {
|
||||
$connection = config('database.default');
|
||||
}
|
||||
|
||||
if (Schema::connection($connection)->hasTable($table)) {
|
||||
$list = Schema::connection($connection)->getColumnListing($table);
|
||||
$list = array_filter($list, function ($value) use ($ignore) {
|
||||
return !in_array($value, $ignore);
|
||||
});
|
||||
$this->info("protected \$fillable = ['".implode('\', \'', $list)."'];");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Device;
|
||||
use App\Models\AtomizingLog;
|
||||
use App\Services\MqttService;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class MqttPenwuPlan extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'mqtt:penwu-plan';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'MQTT 喷雾控制';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
//首先获取当前状态
|
||||
$service = new MqttService();
|
||||
$status = $service->getStatus();
|
||||
//判断是否离线,或者状态异常;
|
||||
if($status && $status['error'] == 0 && $status['status'] == 1){
|
||||
//获取所有喷雾监控点,对应的自动喷雾配置
|
||||
$deviceList = Device::where('type', Device::TYPE_ATOMIZING)->get();
|
||||
$time = now()->format('H:i');//获取当前时间(时,分)
|
||||
$time = '01:00';
|
||||
foreach($deviceList as $device){
|
||||
$_config = $device->extends ?? [];
|
||||
if($_config && $_config['is_enable']){//判断该配置是否开启
|
||||
foreach($_config['config'] as $item){
|
||||
list($start, $end) = explode(',', $item['time_zone']);
|
||||
//决定开启,关闭,
|
||||
if($time == $start){//相等
|
||||
// 如果当前对应位置已开启,则不作为
|
||||
if(
|
||||
($item['value'] == 'a' && $status['yv1'] == 0)
|
||||
||($item['value'] == 'b' && $status['yv2'] == 0)
|
||||
){
|
||||
$service->open($item['value'], $item['input']);
|
||||
//记录触发日志
|
||||
AtomizingLog::create([
|
||||
'device_id' => $device->id,
|
||||
'type' => 1,
|
||||
'content' => '自动开启【区域'.strtoupper($item['value']).'】,喷雾量'.$item['input'].'%',
|
||||
]);
|
||||
}
|
||||
}elseif($time == $end){
|
||||
if($status['is_running']){
|
||||
if(
|
||||
($item['value'] == 'a' && $status['yv1'] == 1)
|
||||
||($item['value'] == 'b' && $status['yv2'] == 1)
|
||||
){
|
||||
$service->close();
|
||||
//记录触发日志
|
||||
AtomizingLog::create([
|
||||
'device_id' => $device->id,
|
||||
'type' => 2,
|
||||
'content' => '自动关闭【区域'.strtoupper($item['value']).'】',
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@ class Kernel extends ConsoleKernel
|
|||
protected function schedule(Schedule $schedule)
|
||||
{
|
||||
// $schedule->command('inspire')->hourly();
|
||||
$schedule->command('mqtt:penwu-plan')->everyMinute();//每分钟执行
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class BizException extends Exception
|
||||
{
|
||||
public function __construct(string $message, int $code = 400)
|
||||
{
|
||||
parent::__construct($message, $code);
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于响应的 HTTP 状态代码
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $status = 200;
|
||||
|
||||
/**
|
||||
* 设置用于响应的 HTTP 状态代码
|
||||
*
|
||||
* @param int $status
|
||||
* @return $this
|
||||
*/
|
||||
public function status(int $status)
|
||||
{
|
||||
$this->status = $status;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 报告异常
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function report()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -2,33 +2,30 @@
|
|||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Illuminate\Auth\AuthenticationException;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||
use Illuminate\Http\Exceptions\ThrottleRequestsException;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
use Throwable;
|
||||
|
||||
class Handler extends ExceptionHandler
|
||||
{
|
||||
/**
|
||||
* A list of exception types with their corresponding custom log levels.
|
||||
*
|
||||
* @var array<class-string<\Throwable>, \Psr\Log\LogLevel::*>
|
||||
*/
|
||||
protected $levels = [
|
||||
//
|
||||
];
|
||||
|
||||
/**
|
||||
* A list of the exception types that are not reported.
|
||||
*
|
||||
* @var array<int, class-string<\Throwable>>
|
||||
* @var string[]
|
||||
*/
|
||||
protected $dontReport = [
|
||||
//
|
||||
];
|
||||
|
||||
/**
|
||||
* A list of the inputs that are never flashed to the session on validation exceptions.
|
||||
* A list of the inputs that are never flashed for validation exceptions.
|
||||
*
|
||||
* @var array<int, string>
|
||||
* @var string[]
|
||||
*/
|
||||
protected $dontFlash = [
|
||||
'current_password',
|
||||
|
|
@ -36,6 +33,28 @@ class Handler extends ExceptionHandler
|
|||
'password_confirmation',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the default context variables for logging.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function context()
|
||||
{
|
||||
try {
|
||||
return array_merge(parent::context(), [
|
||||
'url' => request()->url(),
|
||||
'params' => request()->except($this->dontFlash),
|
||||
'accept' => request()->header('accept'),
|
||||
'client_app' => request()->header('client-app'),
|
||||
'client_version' => request()->header('client-version'),
|
||||
'client_platform' => request()->header('client-platform'),
|
||||
'client_os' => request()->header('client-os'),
|
||||
]);
|
||||
} catch (Throwable $e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the exception handling callbacks for the application.
|
||||
*
|
||||
|
|
@ -43,8 +62,98 @@ class Handler extends ExceptionHandler
|
|||
*/
|
||||
public function register()
|
||||
{
|
||||
$this->reportable(function (Throwable $e) {
|
||||
//
|
||||
$this->map(ModelNotFoundException::class, function ($e) {
|
||||
$resource = __($key = 'models.'.$e->getModel());
|
||||
|
||||
if ($key === $resource) {
|
||||
$resource = str_ireplace(['App\\Models\\Admin\\', 'App\\Models\\'], '', $e->getModel());
|
||||
}
|
||||
|
||||
return new HttpException(404, __(':resource not found', ['resource' => $resource]), $e);
|
||||
});
|
||||
|
||||
$this->renderable(function (BizException $e, $request) {
|
||||
if ($this->shouldReturnJson($request, $e)) {
|
||||
return response()->json($this->convertExceptionToArray($e), $e->status);
|
||||
}
|
||||
|
||||
if (! config('app.debug')) {
|
||||
$e = new HttpException($e->status, $e->getMessage(), $e);
|
||||
}
|
||||
|
||||
return $this->prepareResponse($request, $e);
|
||||
});
|
||||
|
||||
$this->renderable(function (ThrottleRequestsException $e, Request $request) {
|
||||
if ($request->acceptsJson()) {
|
||||
return response()->json(['code' => 429, 'message' => $e->getMessage()]);
|
||||
}
|
||||
|
||||
return $this->prepareResponse($request, $e);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function unauthenticated($request, AuthenticationException $exception)
|
||||
{
|
||||
return $this->shouldReturnJson($request, $exception)
|
||||
? response()->json([
|
||||
'code' => 401,
|
||||
'message' => $exception->getMessage(),
|
||||
], 401)
|
||||
: redirect()->guest($exception->redirectTo() ?? route('login'));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function invalidJson($request, ValidationException $exception)
|
||||
{
|
||||
return response()->json([
|
||||
'code' => 422,
|
||||
'message' => $exception->getMessage(),
|
||||
'errors' => $exception->errors(),
|
||||
], 200);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function convertExceptionToArray(Throwable $e)
|
||||
{
|
||||
$data = [
|
||||
'code' => 500,
|
||||
'message' => config('app.debug') ? $e->getMessage() : '服务器繁忙,请稍后再试',
|
||||
];
|
||||
if ($this->isBizException($e)) {
|
||||
$data = [
|
||||
'code' => $e->getCode(),
|
||||
'message' => $e->getMessage(),
|
||||
'data' => null,
|
||||
];
|
||||
|
||||
return $data;
|
||||
} elseif ($this->isHttpException($e)) {
|
||||
$data = [
|
||||
'code' => $e->getStatusCode(),
|
||||
'message' => $e->getMessage(),
|
||||
'data' => null,
|
||||
];
|
||||
}
|
||||
|
||||
return array_merge(parent::convertExceptionToArray($e), $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 确认给定的异常是否是自定义业务异常
|
||||
*
|
||||
* @param Throwable $e
|
||||
* @return bool
|
||||
*/
|
||||
protected function isBizException(Throwable $e): bool
|
||||
{
|
||||
return $e instanceof BizException;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace App\Filters\Admin;
|
||||
|
||||
use EloquentFilter\ModelFilter;
|
||||
|
||||
class AirLogFilter extends ModelFilter
|
||||
{
|
||||
/**
|
||||
* 所属种植计划
|
||||
*/
|
||||
public function device($deviceId)
|
||||
{
|
||||
return $this->where('device_id', $deviceId);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace App\Filters\Admin;
|
||||
|
||||
use EloquentFilter\ModelFilter;
|
||||
|
||||
class AtomizingLogFilter extends ModelFilter
|
||||
{
|
||||
/**
|
||||
* 所属种植计划
|
||||
*/
|
||||
public function device($deviceId)
|
||||
{
|
||||
return $this->where('device_id', $deviceId);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
namespace App\Filters\Admin;
|
||||
|
||||
use EloquentFilter\ModelFilter;
|
||||
use App\Models\Device;
|
||||
use App\Models\MonitorMode;
|
||||
|
||||
class DeviceFilter extends ModelFilter
|
||||
{
|
||||
/**
|
||||
* 名称
|
||||
*/
|
||||
public function name($name){
|
||||
return $this->where('name', 'like', '%'.$name.'%');
|
||||
}
|
||||
|
||||
/**
|
||||
* 厂家
|
||||
*/
|
||||
public function factory($factory){
|
||||
return $this->where('powered_by', $factory);
|
||||
}
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
public function type($type){
|
||||
return $this->where('type', $type);
|
||||
}
|
||||
|
||||
public function monitorMode($monitorMode){
|
||||
if($monitorMode){
|
||||
$deviceIds = MonitorMode::find($monitorMode)?->devices()->get()->pluck('id')->toArray();
|
||||
}
|
||||
return $this->whereIn('id', $deviceIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
public function typeName($typeName){
|
||||
$type = 0;
|
||||
foreach(Device::typeMap() as $key => $item){
|
||||
if($item == $typeName){
|
||||
$type = $key;
|
||||
break;
|
||||
}
|
||||
};
|
||||
return $this->where('type', $type);
|
||||
}
|
||||
/**
|
||||
* 分组
|
||||
*/
|
||||
public function groupTags($groupTags){
|
||||
return $this->whereRaw("FIND_IN_SET(group_tags,'$groupTags')");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
namespace App\Filters\Admin;
|
||||
|
||||
use EloquentFilter\ModelFilter;
|
||||
use App\Models\Device;
|
||||
|
||||
class MonitorModeFilter extends ModelFilter
|
||||
{
|
||||
/**
|
||||
* 名称
|
||||
*/
|
||||
public function name($name){
|
||||
return $this->where('name', 'like', '%'.$name.'%');
|
||||
}
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
public function type($type){
|
||||
return $this->where('type', $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
public function typeName($typeName){
|
||||
$type = 0;
|
||||
foreach(Device::typeMap() as $key => $item){
|
||||
if($item == $typeName){
|
||||
$type = $key;
|
||||
break;
|
||||
}
|
||||
};
|
||||
return $this->where('type', $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分组
|
||||
*/
|
||||
public function groupTags($groupTags){
|
||||
if(strpos($groupTags, ',')){
|
||||
$this->where(function($q) use ($groupTags) {
|
||||
foreach(explode(',', $groupTags) as $tag){
|
||||
$q->whereRaw("FIND_IN_SET('".$tag."',group_tags)");
|
||||
}
|
||||
return $q;
|
||||
});
|
||||
}else{
|
||||
return $this->whereRaw("FIND_IN_SET('".$groupTags."',group_tags)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace App\Filters\Admin;
|
||||
|
||||
use EloquentFilter\ModelFilter;
|
||||
|
||||
class PlantHarvestLogFilter extends ModelFilter
|
||||
{
|
||||
/**
|
||||
* 所属种植计划
|
||||
*/
|
||||
public function plant($plantId)
|
||||
{
|
||||
return $this->where('plant_id', $plantId);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace App\Filters\Admin;
|
||||
|
||||
use EloquentFilter\ModelFilter;
|
||||
|
||||
class RegionCategoryFilter extends ModelFilter
|
||||
{
|
||||
/**
|
||||
* 名称
|
||||
*/
|
||||
public function name($name){
|
||||
return $this->where('name', 'like', '%'.$name.'%');
|
||||
}
|
||||
|
||||
/**
|
||||
* 父级分类
|
||||
*/
|
||||
public function parent($parentId){
|
||||
return $this->where('parent_id', $parentId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否开启
|
||||
*/
|
||||
public function isEnable($isEnable){
|
||||
return $this->where('is_enbale', $isEnable);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace App\Filters\Admin;
|
||||
|
||||
use EloquentFilter\ModelFilter;
|
||||
|
||||
class RegionFilter extends ModelFilter
|
||||
{
|
||||
/**
|
||||
* 名称
|
||||
*/
|
||||
public function name($name){
|
||||
return $this->where('name', 'like', '%'.$name.'%');
|
||||
}
|
||||
|
||||
/**
|
||||
* 父级分类
|
||||
*/
|
||||
public function category($categoryId){
|
||||
return $this->where('category_id', $categoryId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否开启
|
||||
*/
|
||||
public function isEnable($isEnable){
|
||||
return $this->where('is_enbale', $isEnable);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
namespace App\Filters\Admin;
|
||||
|
||||
use EloquentFilter\ModelFilter;
|
||||
|
||||
class RegionPlantLogFilter extends ModelFilter
|
||||
{
|
||||
/**
|
||||
* 名称
|
||||
*/
|
||||
public function plantName($name){
|
||||
return $this->where('plant_name', 'like', '%'.$name.'%');
|
||||
}
|
||||
|
||||
/**
|
||||
* 所属区域
|
||||
*/
|
||||
public function region($regionId){
|
||||
return $this->where('region_id', $regionId);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
namespace App\Filters;
|
||||
|
||||
use EloquentFilter\ModelFilter;
|
||||
|
||||
class AdminNoticeFilter extends ModelFilter
|
||||
{
|
||||
/**
|
||||
* Related Models that have ModelFilters as well as the method on the ModelFilter
|
||||
* As [relationMethod => [input_key1, input_key2]].
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $relations = [];
|
||||
|
||||
public function title($v)
|
||||
{
|
||||
$this->whereLike('title', $v);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
namespace App\Filters;
|
||||
|
||||
use EloquentFilter\ModelFilter;
|
||||
|
||||
class ArticleCategoryFilter extends ModelFilter
|
||||
{
|
||||
protected $drop_id = false;
|
||||
|
||||
/**
|
||||
* Related Models that have ModelFilters as well as the method on the ModelFilter
|
||||
* As [relationMethod => [input_key1, input_key2]].
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $relations = [];
|
||||
|
||||
public function name($v)
|
||||
{
|
||||
$this->whereLike('name', $v);
|
||||
}
|
||||
|
||||
public function parentId($v)
|
||||
{
|
||||
$this->where('parent_id', $v);
|
||||
}
|
||||
|
||||
public function parentPath($v)
|
||||
{
|
||||
$this->where('path', 'like', '%-'.$v.'-%');
|
||||
}
|
||||
|
||||
public function level($v)
|
||||
{
|
||||
$this->where('level', $v);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
namespace App\Filters;
|
||||
|
||||
use EloquentFilter\ModelFilter;
|
||||
|
||||
class ArticleFilter extends ModelFilter
|
||||
{
|
||||
protected $drop_id = false;
|
||||
|
||||
/**
|
||||
* Related Models that have ModelFilters as well as the method on the ModelFilter
|
||||
* As [relationMethod => [input_key1, input_key2]].
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $relations = [];
|
||||
|
||||
public function title($v)
|
||||
{
|
||||
$this->whereLike('title', $v);
|
||||
}
|
||||
|
||||
public function categoryId($v)
|
||||
{
|
||||
$this->where('category_id', $v);
|
||||
}
|
||||
|
||||
public function categoryPath($v)
|
||||
{
|
||||
$this->where('category_path', 'like', '%-'.$v.'-%');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace App\Filters;
|
||||
|
||||
use EloquentFilter\ModelFilter;
|
||||
|
||||
class BannerFilter extends ModelFilter
|
||||
{
|
||||
/**
|
||||
* Related Models that have ModelFilters as well as the method on the ModelFilter
|
||||
* As [relationMethod => [input_key1, input_key2]].
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $relations = [];
|
||||
|
||||
public function place($id)
|
||||
{
|
||||
$this->where('place_id', $id);
|
||||
}
|
||||
|
||||
public function key($v)
|
||||
{
|
||||
$this->whereHas('place', fn ($q) => $q->enable()->whereIn('key', explode(',', $v)));
|
||||
}
|
||||
|
||||
public function name($v)
|
||||
{
|
||||
$this->whereLike('name', $v);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace App\Filters;
|
||||
|
||||
use EloquentFilter\ModelFilter;
|
||||
|
||||
class BannerPlaceFilter extends ModelFilter
|
||||
{
|
||||
/**
|
||||
* Related Models that have ModelFilters as well as the method on the ModelFilter
|
||||
* As [relationMethod => [input_key1, input_key2]].
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $relations = [];
|
||||
|
||||
public function key($v)
|
||||
{
|
||||
$this->whereLike('key', $v);
|
||||
}
|
||||
public function name($v)
|
||||
{
|
||||
$this->whereLike('name', $v);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace App\Filters;
|
||||
|
||||
use EloquentFilter\ModelFilter;
|
||||
use App\Models\Device;
|
||||
|
||||
class MonitorModeFilter extends ModelFilter
|
||||
{
|
||||
|
||||
public function region($region){
|
||||
return $this->whereHas('region', function($q)use($region){
|
||||
return $q->where('regions.id', $region);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
public function type($type){
|
||||
return $this->where('type', $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
public function typeName($typeName){
|
||||
$type = 0;
|
||||
foreach(Device::typeMap() as $key => $item){
|
||||
if($item == $typeName){
|
||||
$type = $key;
|
||||
break;
|
||||
}
|
||||
};
|
||||
return $this->where('type', $type);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace App\Filters;
|
||||
|
||||
use EloquentFilter\ModelFilter;
|
||||
|
||||
class WarningNoticeFilter extends ModelFilter
|
||||
{
|
||||
public function lv($lv){
|
||||
return $this->where('lv', $lv);
|
||||
}
|
||||
|
||||
public function status($status)
|
||||
{
|
||||
return $this->where('status', $status);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace App\Helpers;
|
||||
|
||||
class Paginator
|
||||
{
|
||||
/**
|
||||
* 解析每页显示的条数
|
||||
*
|
||||
* @param string $perPageName
|
||||
* @param int $default
|
||||
* @param int|null $max
|
||||
* @return int
|
||||
*/
|
||||
public static function resolvePerPage(string $perPageName = 'per_page', int $default = 20, ?int $max = null): int
|
||||
{
|
||||
$perPage = (int) request()->input($perPageName);
|
||||
|
||||
if ($perPage >= 1) {
|
||||
if ($max !== null && $max >= 1 && $perPage >= $max) {
|
||||
return $max;
|
||||
}
|
||||
|
||||
return $perPage;
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\AdminNotice;
|
||||
use App\Http\Resources\AdminNoticeResource;
|
||||
|
||||
class AdminNoticeController extends Controller
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
$query = AdminNotice::filter($request->all())->sort()->enable();
|
||||
|
||||
$list = $query->get();
|
||||
|
||||
return $this->json(AdminNoticeResource::collection($list));
|
||||
}
|
||||
|
||||
public function show($id)
|
||||
{
|
||||
$info = AdminNotice::enable()->findOrFail($id);
|
||||
return $this->json(AdminNoticeResource::make($info));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\{Article, ArticleCategory};
|
||||
use App\Http\Resources\{ArticleResource, ArticleCategoryResource};
|
||||
|
||||
class ArticleController extends Controller
|
||||
{
|
||||
public function category(Request $request)
|
||||
{
|
||||
$query = ArticleCategory::filter($request->all())->enable()->sort();
|
||||
|
||||
$list = $query->get();
|
||||
|
||||
return $this->json(ArticleCategoryResource::collection($list));
|
||||
}
|
||||
|
||||
public function tree(Request $request)
|
||||
{
|
||||
$pid = $request->input('parent_id');
|
||||
$list = ArticleCategory::filter(['parent_path' => $pid])->enable()->sort()->select(['id', 'name', 'icon', 'parent_id'])->get()->toArray();
|
||||
|
||||
return $this->json(array2tree($list, $pid ?? 0));
|
||||
}
|
||||
|
||||
public function index(Request $request)
|
||||
{
|
||||
$query = Article::filter($request->all())->enable()->sort();
|
||||
|
||||
$list = $query->paginate($request->input('per_page'));
|
||||
|
||||
|
||||
|
||||
return $this->json(ArticleResource::collection($list));
|
||||
}
|
||||
|
||||
public function show($id)
|
||||
{
|
||||
$info = Article::enable()->findOrFail($id);
|
||||
return $this->json(ArticleResource::make($info));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Slowlyo\OwlAdmin\Models\AdminUser;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
|
||||
class AuthController extends Controller
|
||||
{
|
||||
public function login(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'username' => 'required',
|
||||
'password' => 'required',
|
||||
]);
|
||||
|
||||
$user = AdminUser::where(['username' => $request->input('username')])->first();
|
||||
if (! $user) {
|
||||
return $this->error('用户名或密码错误');
|
||||
}
|
||||
if (! Hash::check($request->input('password'), $user->password)) {
|
||||
return $this->error('用户名或密码错误');
|
||||
}
|
||||
|
||||
// if ($user->is_enable !== 1) {
|
||||
// return $this->error('用户状态异常请联系管理员');
|
||||
// }
|
||||
|
||||
return $this->attemptUser($user);
|
||||
}
|
||||
|
||||
protected function attemptUser(AdminUser $user, $name = 'api')
|
||||
{
|
||||
$token = $user->createToken($name)->plainTextToken;
|
||||
|
||||
return $this->json(['token' => $token]);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\Banner;
|
||||
use App\Http\Resources\BannerResource;
|
||||
|
||||
class BannerController extends Controller
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'key' => 'required',
|
||||
]);
|
||||
$query = Banner::with(['place'])->filter($request->all())->sort()->enable();
|
||||
|
||||
$list = $query->get();
|
||||
|
||||
return $this->json(BannerResource::collection($list));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\{Device, MonitorDevice, RegionMonitor};
|
||||
use DB;
|
||||
|
||||
class DeviceController extends Controller
|
||||
{
|
||||
/**
|
||||
* 统计某个实验田下所有设备状态数量
|
||||
*/
|
||||
public function typeStateNum(Request $request)
|
||||
{
|
||||
$regionId = $request->input('region_id', 0);
|
||||
$monitorId = $request->input('monitor_id', 0);
|
||||
|
||||
$query = Device::query();
|
||||
|
||||
if($monitorId){
|
||||
$deviceIds = MonitorDevice::where('monitor_id', $monitorId)->pluck('id')->toArray();
|
||||
$query->whereIn('id', $deviceIds);
|
||||
}elseif($regionId){
|
||||
$monitorIds = RegionMonitor::where('region_id', $regionId)->pluck('id')->toArray();
|
||||
|
||||
if(count($monitorIds) > 0){
|
||||
$deviceIds = MonitorDevice::whereIn('monitor_id', $monitorIds)->pluck('id')->toArray();
|
||||
$query->whereIn('id', $deviceIds);
|
||||
}
|
||||
}
|
||||
|
||||
$query->groupBy('type')->groupBy('state');
|
||||
|
||||
$list = $query->select(DB::raw('type, state, count(1) as num '))->get();
|
||||
$resData = [];
|
||||
foreach ($list as $item) {
|
||||
$resData[$item->type][$item->state] = $item->num;
|
||||
}
|
||||
|
||||
//初始化数据;
|
||||
$data = [];
|
||||
foreach (Device::typeMap() as $typeKey => $typeName) {
|
||||
foreach (Device::stateMap() as $statusKey => $statusName) {
|
||||
$data[$typeKey][$statusKey] = $resData[$typeKey][$statusKey] ?? 0;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 农机短信提醒-todo
|
||||
*/
|
||||
public function deviceNotice(){
|
||||
return $this->success('操作成功');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Filters\MonitorModeFilter;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\DeviceResource;
|
||||
use App\Http\Resources\MonitorModeResource;
|
||||
use App\Models\MeteorologicalDailyReport;
|
||||
use App\Models\MonitorMode;
|
||||
use App\Services\Admin\DeviceService;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class MonitorModeController extends Controller
|
||||
{
|
||||
/**
|
||||
* 获取监控点列表;
|
||||
*/
|
||||
public function getMonitorMode(Request $request)
|
||||
{
|
||||
$monitors = MonitorMode::filter($request->all(), MonitorModeFilter::class)->get();//有推荐,排序字段,不过目前没开放;
|
||||
return $this->json(MonitorModeResource::collection($monitors));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取监控点位数据-可指定字段
|
||||
*/
|
||||
public function getMonitorDeviceData(Request $request)
|
||||
{
|
||||
$monitorId = $request->input('monitor_id');
|
||||
$column = $request->input('column'); //指定字段
|
||||
//监控类型(气象,土壤为折线图数据;虫情数据待定;监控设备rtsp流;其他类型无数据)
|
||||
$data = [];
|
||||
$monitor = MonitorMode::find($monitorId);
|
||||
if($monitor){
|
||||
switch($monitor->type){
|
||||
case MonitorMode::TYPE_MONITOR://监控视频
|
||||
$data = DeviceResource::collection($monitor->devices);
|
||||
break;
|
||||
case MonitorMode::TYPE_SOIL://土壤设备--只拿最近6小时数据
|
||||
$startTime = now()->subHours(6)->format('Y-m-d H:i:s');
|
||||
$endTime = now()->format('Y-m-d H:i:s');
|
||||
$data = (new DeviceService())->getMonitorModeDeviceData($monitor, $startTime, $endTime);
|
||||
if($column){
|
||||
$data = $data[$column] ?? [];
|
||||
}
|
||||
break;
|
||||
case MonitorMode::TYPE_METEOROLOGICAL://气象设备--只拿最新的一个值
|
||||
$fieldNameMap = MonitorMode::fieldMap(MonitorMode::TYPE_METEOROLOGICAL);
|
||||
$fieldUnitMap = MonitorMode::fieldUnitMap(MonitorMode::TYPE_METEOROLOGICAL);
|
||||
foreach($monitor->devices as $device){
|
||||
$_fields = explode(',', $device->pivot->fields);
|
||||
$reportData = MeteorologicalDailyReport::where('device_id', $device->id)->orderBy('reported_at', 'desc')->first()?->toArray() ?? [];
|
||||
foreach($_fields as $field){
|
||||
$data[$field] = [
|
||||
'name' => $fieldNameMap[$field],
|
||||
'unit' => $fieldUnitMap[$field],
|
||||
'value' => $reportData[$field] ?? 0,
|
||||
];
|
||||
if($field == 'wind_direction'){//单独处理风向
|
||||
$formatData = ['北风','东北风','东风','东南风','南风','西南风','西风','西北风'];
|
||||
$data[$field]['value'] = $formatData[$reportData[$field]]??'未知';
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MonitorMode::TYPE_INSECT://虫情设备-todo
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->json($data);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\{Region, RegionCategory};
|
||||
use App\Http\Resources\{RegionResource, RegionCategoryResource, RegionPlantResource, PlantHarvestResource};
|
||||
use App\Filters\Admin\RegionFilter;
|
||||
|
||||
class RegionController extends Controller
|
||||
{
|
||||
public function category(Request $request)
|
||||
{
|
||||
$list = RegionCategory::query()->show()->sort()->get();
|
||||
|
||||
return $this->json(RegionCategoryResource::collection($list));
|
||||
}
|
||||
|
||||
public function index(Request $request)
|
||||
{
|
||||
$query = Region::with(['currentPlant'])->filter($request->all(), RegionFilter::class)->sort()->show();
|
||||
|
||||
$list = $query->paginate($request->input('per_page'));
|
||||
|
||||
return $this->json(RegionResource::collection($list));
|
||||
}
|
||||
|
||||
public function show($id)
|
||||
{
|
||||
$info = Region::with(['currentPlant'])->show()->findOrFail($id);
|
||||
|
||||
return $this->json(RegionResource::make($info));
|
||||
}
|
||||
|
||||
public function plants($id, Request $request)
|
||||
{
|
||||
$info = Region::show()->findOrFail($id);
|
||||
|
||||
$query = $info->plants();
|
||||
$per = $request->input('per_page');
|
||||
if ($per == -1) {
|
||||
$list = $query->get();
|
||||
} else {
|
||||
$list = $query->paginate($per);
|
||||
}
|
||||
|
||||
return $this->json(RegionPlantResource::collection($list));
|
||||
}
|
||||
|
||||
public function harvests($id, Request $request)
|
||||
{
|
||||
$info = Region::show()->findOrFail($id);
|
||||
$query = $info->harvests()->filter($request->all());
|
||||
$per = $request->input('per_page');
|
||||
if ($per == -1) {
|
||||
$list = $query->get();
|
||||
} else {
|
||||
$list = $query->paginate($per);
|
||||
}
|
||||
|
||||
return $this->json(PlantHarvestResource::collection($list));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class SettingController extends Controller
|
||||
{
|
||||
/**
|
||||
* 基础数据信息-最多返回6项
|
||||
*/
|
||||
public function staticBaseData()
|
||||
{
|
||||
$baseData = settings()->get('region_base') ?? [];
|
||||
$data = [];
|
||||
foreach ($baseData as $index => $item) {
|
||||
if($index <= 6){
|
||||
$data[] = [
|
||||
'name' => $item['name'],
|
||||
'value' => number_format($item['value']),
|
||||
'unit' => $item['unit'],
|
||||
];
|
||||
}else{
|
||||
break;//最多放6个
|
||||
}
|
||||
}
|
||||
return $this->json($data);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class UserController extends Controller
|
||||
{
|
||||
public function logout()
|
||||
{
|
||||
$user = auth()->user();
|
||||
// $user->tokens()->delete();
|
||||
$user->currentAccessToken()->delete();
|
||||
|
||||
return $this->success('退出成功');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Helpers\Paginator;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\WarningNotice;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Filters\WarningNoticeFilter;
|
||||
use App\Http\Resources\WarningNoticeResource;
|
||||
use DB;
|
||||
|
||||
class WarningNoticeController extends Controller
|
||||
{
|
||||
/**
|
||||
* 获取警报数量
|
||||
*/
|
||||
public function warningLogNum(Request $request){
|
||||
$list = WarningNotice::filter($request->input(), WarningNoticeFilter::class)
|
||||
->select(DB::raw('lv, count(1) as num'))
|
||||
->groupBy('lv')
|
||||
->get()
|
||||
->pluck('num', 'lv')->toArray();
|
||||
for($i = 1; $i <= 4; $i++) {
|
||||
$data[$i] = $list[$i] ?? 0;
|
||||
}
|
||||
return $this->json($data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取警报记录
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function warningLog(Request $request)
|
||||
{
|
||||
$query = WarningNotice::with(['device.modes'])->filter($request->input(), WarningNoticeFilter::class)->orderBy('created_at', 'desc');
|
||||
$list = $query->simplePaginate(Paginator::resolvePerPage('per_page', 20, 50));
|
||||
|
||||
return $this->json(WarningNoticeResource::collection($list));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Callback;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Device;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class LinkosCallbackController extends Controller
|
||||
{
|
||||
public function __invoke(Request $request)
|
||||
{
|
||||
logger()->debug('linkos callback parameters -------->', $request->input());
|
||||
|
||||
if ($request->filled('notify_type')) {
|
||||
switch ($request->notify_type) {
|
||||
case 'online_state_change':
|
||||
$this->handleDeviceStateNotify($request);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return response()->json(['code' => 0, 'msg' => 'ok']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设备状态通知
|
||||
*/
|
||||
protected function handleDeviceStateNotify(Request $request): void
|
||||
{
|
||||
foreach ($request->input('data', []) as $item) {
|
||||
if (! isset($item['device_id'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$device = Device::where('sn', $item['device_id'])->first();
|
||||
|
||||
if ($device === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($item['online_state'])) {
|
||||
switch ($item['online_state']) {
|
||||
case 0:
|
||||
$device->forceFill([
|
||||
'state' => Device::STATE_OFFLINE,
|
||||
])->save();
|
||||
break;
|
||||
|
||||
case 1:
|
||||
$device->forceFill([
|
||||
'state' => Device::STATE_ONLINE,
|
||||
])->save();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,8 +6,30 @@ use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
|||
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||
use Illuminate\Routing\Controller as BaseController;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
class Controller extends BaseController
|
||||
{
|
||||
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
|
||||
|
||||
public function json($data, $code = 0, $message = '')
|
||||
{
|
||||
if ($data instanceof ResourceCollection) {
|
||||
$data = $data->resource;
|
||||
}
|
||||
$result = ['data' => $data ?: '', 'status' => $code, 'msg' => $message];
|
||||
return response()->json($result);
|
||||
}
|
||||
|
||||
public function success($message = '', $data = null)
|
||||
{
|
||||
return $this->json($data, 0, $message);
|
||||
}
|
||||
|
||||
public function error($message = '', $data = null)
|
||||
{
|
||||
return $this->json($data, 1, $message);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,6 @@ class VerifyCsrfToken extends Middleware
|
|||
* @var array<int, string>
|
||||
*/
|
||||
protected $except = [
|
||||
//
|
||||
'callback/*'
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class AdminNoticeResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'title' => $this->title,
|
||||
'content' => $this->content,
|
||||
'article_id' => $this->article_id,
|
||||
'sort' => $this->sort,
|
||||
'published_at' => $this->published_at ? $this->published_at->timestamp : '',
|
||||
'created_at' => $this->created_at->timestamp
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class ArticleCategoryResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'icon' => $this->icon,
|
||||
'name' => $this->name,
|
||||
'parent_id' => $this->parent_id,
|
||||
'level' => $this->level,
|
||||
'path' => $this->path,
|
||||
'sort' => $this->sort,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class ArticleResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'title' => $this->title,
|
||||
'sub_title' => $this->sub_title,
|
||||
'category_id' => $this->category_id,
|
||||
'category' => ArticleCategoryResource::make($this->whenLoaded('category')),
|
||||
'content' => $this->content,
|
||||
'cover' => $this->cover,
|
||||
'published_at' => $this->published_at ? $this->published_at->timestamp : '',
|
||||
'created_at' => $this->created_at->timestamp
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class BannerResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'place_id' => $this->place_id,
|
||||
'key' => data_get($this->whenLoaded('place'), 'key'),
|
||||
'name' => $this->name,
|
||||
'picture' => $this->picture,
|
||||
'link_config' => $this->link_config,
|
||||
'sort' => $this->sort,
|
||||
'published_at' => $this->published_at ? $this->published_at->timestamp : '',
|
||||
'created_at' => $this->created_at->timestamp,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use App\Models\Device;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class DeviceResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'type' => $this->type,
|
||||
'type_name' => Device::typeMap()[$this->type] ?? '未知',
|
||||
'state' => $this->state,
|
||||
'state_name' => Device::stateMap()[$this->state] ?? '未知',
|
||||
'extends' => $this->extends ?? [],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class MonitorModeResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'type' => $this->type,
|
||||
'name' => $this->name
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class PlantHarvestResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'plant_id' => $this->plant_id,
|
||||
'region_id' => $this->region_id,
|
||||
'director' => $this->director,
|
||||
'area' => $this->area,
|
||||
'output' => $this->output,
|
||||
'harvest_at' => $this->harvest_at ? $this->harvest_at->timestamp : null,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class RegionCategoryResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'name' => $this->name,
|
||||
'icon' => $this->icon,
|
||||
'description' => $this->description,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class RegionPlantResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'plant_name' => $this->plant_name,
|
||||
'director' => $this->director,
|
||||
'area' => $this->area,
|
||||
'start_at' => $this->start_at ? $this->start_at->timestamp : null,
|
||||
'end_at' => $this->end_at ? $this->end_at->timestamp : null,
|
||||
'plant_state' => $this->plant_state,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class RegionResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'name' => $this->name,
|
||||
'cover' => $this->cover,
|
||||
'director' => $this->director,
|
||||
'area' => $this->area,
|
||||
'description' => $this->description,
|
||||
'category_id' => $this->category_id,
|
||||
'category' => RegionCategoryResource::make($this->whenLoaded('category')),
|
||||
'current_plant' => RegionPlantResource::make($this->whenLoaded('currentPlant')),
|
||||
'position_x'=> $this->position_x,
|
||||
'position_y'=> $this->position_y,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class WarningNoticeResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'lv' => $this->lv,
|
||||
'monitor_modes' => MonitorModeResource::collection($this->whenLoaded('device')->modes),
|
||||
'status' => $this->status,
|
||||
'content' => $this->content,
|
||||
'created_at' => strtotime($this->created_at) ?? 0, //录入时间
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,160 @@
|
|||
<?php
|
||||
|
||||
namespace App\Iot\Linkos;
|
||||
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use RuntimeException;
|
||||
|
||||
class HttpClient
|
||||
{
|
||||
public const ENDPOINT_URL = 'http://service.easylinkin.com';
|
||||
|
||||
public function __construct(
|
||||
protected readonly string $apiKey,
|
||||
protected readonly string $apiSecret,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备的历史数据
|
||||
*
|
||||
* @param string $deviceId
|
||||
* @param \Illuminate\Support\Carbon $start
|
||||
* @param \Illuminate\Support\Carbon $end
|
||||
* @param int $page
|
||||
* @param int $perPage
|
||||
* @return array
|
||||
*/
|
||||
public function deviceFlowList(string $deviceId, Carbon $start, Carbon $end, int $page = 1, int $perPage = 50): array
|
||||
{
|
||||
$result = $this->post('/api/deviceFlow/v1/list', [
|
||||
'device_id' => $deviceId,
|
||||
'start_time' => $start->unix() * 1000,
|
||||
'end_time' => $end->unix() * 1000,
|
||||
'pageable' => [
|
||||
'page' => $page - 1,
|
||||
'size' => $perPage,
|
||||
],
|
||||
]);
|
||||
|
||||
if (data_get($result, 'success') !== true) {
|
||||
throw new RuntimeException(data_get($result, 'msg', '出错啦!'));
|
||||
}
|
||||
|
||||
return $result['data'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 设备数据下行
|
||||
*
|
||||
* @param string $deviceId
|
||||
* @param string $service
|
||||
* @param array $data
|
||||
* @param boolean $confirm
|
||||
* @param boolean $clear
|
||||
* @param boolean $schedule
|
||||
* @return array
|
||||
*/
|
||||
public function deviceDataDownlink(string $deviceId, string $service, array $data = [], bool $confirm = true, bool $clear = true, bool $schedule = false): array
|
||||
{
|
||||
return $this->post('/api/down', [
|
||||
'device_id' => $deviceId,
|
||||
'service_id' => $service,
|
||||
'parameter' => $data,
|
||||
'clear' => (int) $clear,
|
||||
'schedule' => (int) $schedule,
|
||||
'confirm' => (int) $confirm,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备最新属性数据
|
||||
*/
|
||||
public function getDeviceStatus(string $deviceId, array $props): array
|
||||
{
|
||||
$result = $this->get('/api/deviceStatus/v1/getDeviceStatus', [
|
||||
'deviceCode' => $deviceId,
|
||||
'prop' => implode(",", $props),
|
||||
]);
|
||||
|
||||
if (data_get($result, 'success') !== true) {
|
||||
throw new RuntimeException(data_get($result, 'msg', '出错啦!'));
|
||||
}
|
||||
|
||||
return $result['data'];
|
||||
}
|
||||
|
||||
public function get(string $url, array $query = []): array
|
||||
{
|
||||
return $this->request('GET', $url, [
|
||||
'query' => $query,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
* @param array $data
|
||||
* @return array
|
||||
*/
|
||||
public function post(string $url, array $data = []): array
|
||||
{
|
||||
return $this->request('POST', $url, [
|
||||
'json' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $method
|
||||
* @param string $url
|
||||
* @param array $options
|
||||
* @return array
|
||||
*
|
||||
* @throws \Illuminate\Http\Client\RequestException
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function request(string $method, string $url, array $options = []): array
|
||||
{
|
||||
$nonce = $this->nonce();
|
||||
|
||||
$timestamp = now()->getTimestampMs();
|
||||
|
||||
/** @var \Illuminate\Http\Client\Response */
|
||||
$response = Http::withHeaders([
|
||||
'Content-Type' => 'application/json',
|
||||
'api-key' => $this->apiKey,
|
||||
'Nonce' => $nonce,
|
||||
'Timestamp' => $timestamp,
|
||||
'Signature' => $this->sign(compact('nonce', 'timestamp')),
|
||||
])->baseUrl(self::ENDPOINT_URL)->send($method, $url, $options);
|
||||
|
||||
return $response->throw()->json();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @return string
|
||||
*/
|
||||
protected function sign(array $data): string
|
||||
{
|
||||
return sha1(
|
||||
sprintf(
|
||||
'%s%s%s',
|
||||
$data['nonce'] ?? '',
|
||||
$data['timestamp'] ?? '',
|
||||
$this->apiSecret
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
protected function nonce(): string
|
||||
{
|
||||
$nonce = '';
|
||||
|
||||
for ($i = 0; $i < 8; $i++) {
|
||||
$nonce .= mt_rand(0, 9);
|
||||
}
|
||||
|
||||
return $nonce;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use EloquentFilter\Filterable;
|
||||
|
||||
/**
|
||||
* 公告
|
||||
*/
|
||||
class AdminNotice extends Model
|
||||
{
|
||||
use Filterable;
|
||||
|
||||
protected $fillable = ['title', 'content', 'article_id', 'remark', 'is_enable', 'published_at' ,'sort'];
|
||||
protected $casts = [
|
||||
'published_at' => 'datetime:Y-m-d H:i:s',
|
||||
'created_at' => 'datetime:Y-m-d H:i:s',
|
||||
'updated_at' => 'datetime:Y-m-d H:i:s',
|
||||
'is_enable' => 'boolean'
|
||||
];
|
||||
|
||||
public function article()
|
||||
{
|
||||
return $this->belongsTo(Article::class, 'article_id');
|
||||
}
|
||||
|
||||
public function scopeSort($q)
|
||||
{
|
||||
return $q->orderBy('sort', 'desc')->orderBy('published_at', 'desc');
|
||||
}
|
||||
|
||||
public function scopeEnable($q)
|
||||
{
|
||||
return $q->where('is_enable', 1)->where('published_at', '<=', now());
|
||||
}
|
||||
|
||||
public function modelFilter()
|
||||
{
|
||||
return \App\Filters\AdminNoticeFilter::class;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use EloquentFilter\Filterable;
|
||||
|
||||
class AirLog extends Model
|
||||
{
|
||||
use Filterable;
|
||||
|
||||
protected $fillable = ['device_id', 'type', 'content', 'created_at', 'updated_at'];
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Casts\Storage;
|
||||
use EloquentFilter\Filterable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* 文章
|
||||
*/
|
||||
class Article extends Model
|
||||
{
|
||||
use Filterable;
|
||||
|
||||
protected $fillable = ['author', 'category_id', 'category_path', 'content', 'cover', 'is_enable', 'is_recommend', 'published_at', 'sort', 'sub_title', 'title'];
|
||||
|
||||
protected $casts = [
|
||||
'created_at' => 'datetime:Y-m-d H:i:s',
|
||||
'updated_at' => 'datetime:Y-m-d H:i:s',
|
||||
'published_at' => 'datetime:Y-m-d H:i:s',
|
||||
'is_enable' => 'boolean',
|
||||
'is_recommend' => 'boolean',
|
||||
'cover' => Storage::class,
|
||||
];
|
||||
|
||||
public function category()
|
||||
{
|
||||
return $this->belongsTo(ArticleCategory::class, 'category_id');
|
||||
}
|
||||
|
||||
public function scopeSort($q)
|
||||
{
|
||||
return $q->orderBy('sort', 'desc')->orderBy('published_at', 'desc');
|
||||
}
|
||||
|
||||
public function scopeEnable($q)
|
||||
{
|
||||
return $q->where('is_enable', 1)->where('published_at', '<=', now());
|
||||
}
|
||||
|
||||
public function modelFilter()
|
||||
{
|
||||
return \App\Filters\ArticleFilter::class;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use EloquentFilter\Filterable;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* 文章分类
|
||||
*/
|
||||
class ArticleCategory extends Model
|
||||
{
|
||||
use Filterable;
|
||||
|
||||
protected $casts = [
|
||||
'created_at' => 'datetime:Y-m-d H:i:s',
|
||||
'updated_at' => 'datetime:Y-m-d H:i:s',
|
||||
'is_enable' => 'boolean',
|
||||
];
|
||||
|
||||
protected $fillable = ['icon', 'is_enable', 'level', 'name', 'parent_id', 'path', 'sort'];
|
||||
|
||||
protected function icon(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn($value) => $value ? (Str::startsWith($value, ['http://', 'https://']) ? $value : Storage::url($value)) : '',
|
||||
);
|
||||
}
|
||||
|
||||
public function parent()
|
||||
{
|
||||
return $this->belongsTo(static::class, 'parent_id');
|
||||
}
|
||||
|
||||
public function children()
|
||||
{
|
||||
return $this->hasMany(static::class, 'parent_id');
|
||||
}
|
||||
|
||||
public function articles()
|
||||
{
|
||||
return $this->hasMany(Article::class, 'category_id');
|
||||
}
|
||||
|
||||
public function scopeSort($q)
|
||||
{
|
||||
return $q->orderBy('sort', 'desc');
|
||||
}
|
||||
|
||||
public function scopeEnable($q)
|
||||
{
|
||||
return $q->where('is_enable', 1);
|
||||
}
|
||||
|
||||
public function modelFilter()
|
||||
{
|
||||
return \App\Filters\ArticleCategoryFilter::class;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use EloquentFilter\Filterable;
|
||||
|
||||
class AtomizingLog extends Model
|
||||
{
|
||||
use Filterable;
|
||||
|
||||
protected $fillable = ['device_id', 'type', 'content', 'created_at', 'updated_at'];
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Casts\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
use App\Filters\BannerFilter;
|
||||
use EloquentFilter\Filterable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* 广告图
|
||||
*/
|
||||
class Banner extends Model
|
||||
{
|
||||
use Filterable;
|
||||
|
||||
protected $fillable = ['is_enable', 'link_config', 'name', 'picture', 'place_id', 'published_at', 'sort'];
|
||||
|
||||
protected $casts = [
|
||||
'created_at' => 'datetime:Y-m-d H:i:s',
|
||||
'updated_at' => 'datetime:Y-m-d H:i:s',
|
||||
'published_at' => 'datetime:Y-m-d H:i:s',
|
||||
'is_enable' => 'boolean',
|
||||
'link_config' => 'json',
|
||||
'picture' => Storage::class,
|
||||
];
|
||||
|
||||
public function place()
|
||||
{
|
||||
return $this->belongsTo(BannerPlace::class, 'place_id');
|
||||
}
|
||||
|
||||
public function scopeSort($q)
|
||||
{
|
||||
return $q->orderBy('sort', 'desc');
|
||||
}
|
||||
|
||||
public function scopeEnable($q)
|
||||
{
|
||||
return $q->where('is_enable', 1)->where('published_at', '<=', now());
|
||||
}
|
||||
|
||||
public function modelFilter()
|
||||
{
|
||||
return BannerFilter::class;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use EloquentFilter\Filterable;
|
||||
|
||||
/**
|
||||
* 广告位
|
||||
*/
|
||||
class BannerPlace extends Model
|
||||
{
|
||||
use Filterable;
|
||||
|
||||
protected $fillable = ['height', 'is_enable', 'key', 'name', 'remark', 'width'];
|
||||
|
||||
protected $casts = [
|
||||
'created_at' => 'datetime:Y-m-d H:i:s',
|
||||
'updated_at' => 'datetime:Y-m-d H:i:s',
|
||||
'is_enable' => 'boolean',
|
||||
];
|
||||
|
||||
public function banners()
|
||||
{
|
||||
return $this->hasMany(Banner::class, 'place_id');
|
||||
}
|
||||
|
||||
public function scopeEnable($q)
|
||||
{
|
||||
return $q->where('is_enable', 1);
|
||||
}
|
||||
|
||||
public function scopeSort($q)
|
||||
{
|
||||
return $q->orderBy('id', 'desc');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use EloquentFilter\Filterable;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class Device extends Model
|
||||
{
|
||||
use Filterable;
|
||||
|
||||
public const TYPE_MONITOR = 1; //监控设备
|
||||
public const TYPE_SOIL = 2; //土壤设备
|
||||
public const TYPE_WATER_QUALITY = 3; //水质设备
|
||||
public const TYPE_METEOROLOGICAL = 4; //气象设备
|
||||
public const TYPE_AIR = 5; //通风设备
|
||||
public const TYPE_ATOMIZING = 6; //喷雾设备
|
||||
public const TYPE_INSECT = 7; //虫情监测
|
||||
|
||||
public const STATE_DISABLED = 0;
|
||||
public const STATE_ONLINE = 1;
|
||||
public const STATE_OFFLINE = 2;
|
||||
public const STATE_FAULT = 3;
|
||||
|
||||
protected $casts = [
|
||||
'extends' => 'json'
|
||||
];
|
||||
|
||||
protected $fillable = [
|
||||
'name', 'sn', 'powered_by', 'type', 'model_sn', 'state', 'extends'
|
||||
];
|
||||
|
||||
public function scopePoweredBy(Builder $query, string $factory): void
|
||||
{
|
||||
$query->whereHas('factory', fn ($query) => $query->where('key', $factory));
|
||||
}
|
||||
|
||||
|
||||
protected function serializeDate(\DateTimeInterface $date){
|
||||
return $date->format('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
public static function typeMap()
|
||||
{
|
||||
return [
|
||||
self::TYPE_MONITOR => '监控设备',
|
||||
self::TYPE_SOIL => '土壤设备',
|
||||
// self::TYPE_WATER_QUALITY => '水质设备',
|
||||
self::TYPE_METEOROLOGICAL => '气象设备',
|
||||
self::TYPE_AIR => '通风设备',
|
||||
self::TYPE_ATOMIZING => '喷雾设备',
|
||||
self::TYPE_INSECT => '虫情监测',
|
||||
];
|
||||
}
|
||||
|
||||
public static function stateMap(){
|
||||
return [
|
||||
self::STATE_DISABLED => '禁用',
|
||||
self::STATE_ONLINE => '在线',
|
||||
self::STATE_OFFLINE => '离线',
|
||||
self::STATE_FAULT => '故障',
|
||||
];
|
||||
}
|
||||
|
||||
public function modes(){
|
||||
return $this->belongsToMany(MonitorMode::class, MonitorDevice::class, 'device_id', 'monitor_id')->withPivot('fields');
|
||||
}
|
||||
|
||||
public function logs(): HasMany
|
||||
{
|
||||
return $this->hasMany(DeviceLog::class);
|
||||
}
|
||||
|
||||
public function factory()
|
||||
{
|
||||
return $this->belongsTo(Keyword::class, 'powered_by');
|
||||
}
|
||||
|
||||
public function isTypeSoil(): bool
|
||||
{
|
||||
return $this->type === static::TYPE_SOIL;
|
||||
}
|
||||
|
||||
public function isTypeMeteorological(): bool
|
||||
{
|
||||
return $this->type === static::TYPE_METEOROLOGICAL;
|
||||
}
|
||||
|
||||
public function isTypeAir(): bool
|
||||
{
|
||||
return $this->type === static::TYPE_AIR;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class DeviceLog extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $casts = [
|
||||
'data' => 'json',
|
||||
'reported_at' => 'datetime',
|
||||
];
|
||||
|
||||
protected $fillable = [
|
||||
'device_id',
|
||||
'data',
|
||||
'reported_at',
|
||||
];
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class FriendLink extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
}
|
||||
|
|
@ -5,6 +5,8 @@ namespace App\Models;
|
|||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use EloquentFilter\Filterable;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use App\Admin\Components;
|
||||
|
||||
class Keyword extends Model
|
||||
{
|
||||
|
|
@ -12,6 +14,10 @@ class Keyword extends Model
|
|||
use Filterable;
|
||||
|
||||
protected $fillable = ['name', 'key', 'value', 'parent_id', 'type_key', 'path', 'sort', 'level'];
|
||||
|
||||
protected function serializeDate(\DateTimeInterface $date){
|
||||
return $date->format('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
protected static function boot()
|
||||
{
|
||||
|
|
@ -44,4 +50,15 @@ class Keyword extends Model
|
|||
return $this->hasMany(static::class, 'parent_id');
|
||||
}
|
||||
|
||||
public static function getByParentKey(String $key)
|
||||
{
|
||||
return self::query()->where('type_key', $key)->get();
|
||||
}
|
||||
|
||||
public static function tagsMap(String $key){
|
||||
$list = self::query()->where('type_key', $key)->get()->pluck('name', 'id')->toArray();
|
||||
return array_map(function($item){
|
||||
return Components::make()->keywordsTag($item);
|
||||
}, $list);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class MeteorologicalDailyReport extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
const WIND_DIRECTION_NORTH = 0;
|
||||
const WIND_DIRECTION_NORTHEAST = 1;
|
||||
const WIND_DIRECTION_EAST = 2;
|
||||
const WIND_DIRECTION_SOUTHEAST = 3;
|
||||
const WIND_DIRECTION_SOUTH = 4;
|
||||
const WIND_DIRECTION_SOUTHWEST = 5;
|
||||
const WIND_DIRECTION_WEST = 6;
|
||||
const WIND_DIRECTION_NORTHWEST = 7;
|
||||
|
||||
protected $casts = [
|
||||
'reported_at' => 'date'
|
||||
];
|
||||
|
||||
protected $fillable = [
|
||||
'device_id',
|
||||
'today_rainfall',
|
||||
'yesterday_rainfall',
|
||||
'accumulate_rainfall',
|
||||
'moment_rainfall',
|
||||
'pm10',
|
||||
'pm25',
|
||||
'box_illumination',
|
||||
'box_pressure',
|
||||
'box_co2',
|
||||
'box_temperature',
|
||||
'box_humidity',
|
||||
'box_noise',
|
||||
'wind_degree',
|
||||
'wind_direction',
|
||||
'wind_power',
|
||||
'wind_speed',
|
||||
'reported_at',
|
||||
];
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class MeteorologicalReport extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $casts = [
|
||||
'reported_at' => 'datetime'
|
||||
];
|
||||
|
||||
protected $fillable = [
|
||||
'device_id',
|
||||
'today_rainfall',
|
||||
'yesterday_rainfall',
|
||||
'accumulate_rainfall',
|
||||
'moment_rainfall',
|
||||
'pm10',
|
||||
'pm25',
|
||||
'box_illumination',
|
||||
'box_pressure',
|
||||
'box_co2',
|
||||
'box_temperature',
|
||||
'box_humidity',
|
||||
'box_noise',
|
||||
'wind_degree',
|
||||
'wind_direction',
|
||||
'wind_power',
|
||||
'wind_speed',
|
||||
'reported_at',
|
||||
];
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class MonitorDevice extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
public $timestamps = false;
|
||||
}
|
||||
|
|
@ -0,0 +1,132 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use EloquentFilter\Filterable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
|
||||
class MonitorMode extends Model
|
||||
{
|
||||
use Filterable;
|
||||
|
||||
protected $fillable = [
|
||||
'name', 'type',
|
||||
'is_enable', 'sort', 'is_recommend',
|
||||
'group_tags'
|
||||
];
|
||||
|
||||
protected $appends = ['tags'];
|
||||
|
||||
public const TYPE_MONITOR = 1; //视频监控
|
||||
public const TYPE_SOIL = 2; //土壤监测
|
||||
public const TYPE_WATER_QUALITY = 3; //水质监测
|
||||
public const TYPE_METEOROLOGICAL = 4; //气象监测
|
||||
public const TYPE_AIR = 5; //通风控制
|
||||
public const TYPE_ATOMIZING = 6; //喷雾控制
|
||||
public const TYPE_INSECT = 7; //虫情监测
|
||||
|
||||
public static function typeMap()
|
||||
{
|
||||
return [
|
||||
self::TYPE_MONITOR => '视频监控',
|
||||
self::TYPE_SOIL => '土壤监测',
|
||||
// self::TYPE_WATER_QUALITY => '水质监测',
|
||||
self::TYPE_METEOROLOGICAL => '气象监测',
|
||||
self::TYPE_AIR => '通风控制',
|
||||
self::TYPE_ATOMIZING => '喷雾控制',
|
||||
self::TYPE_INSECT => '虫情监测',
|
||||
];
|
||||
}
|
||||
|
||||
public static function fieldMap($type)
|
||||
{
|
||||
$arr = [];
|
||||
switch ($type) {
|
||||
case self::TYPE_SOIL:
|
||||
$arr = [
|
||||
'conductivity'=>'导电率',
|
||||
'humidity'=>'湿度',
|
||||
'temperature'=>'温度',
|
||||
'n'=>'氮',
|
||||
'p'=>'磷',
|
||||
'k'=>'钾'
|
||||
];
|
||||
break;
|
||||
case self::TYPE_WATER_QUALITY:
|
||||
$arr = [
|
||||
];
|
||||
break;
|
||||
case self::TYPE_METEOROLOGICAL:
|
||||
$arr = [
|
||||
'box_temperature' => '温度',
|
||||
'box_humidity' => '湿度',
|
||||
'box_illumination' => '光照强度',
|
||||
'moment_rainfall' => '降雨量',
|
||||
'wind_speed' => '风速',
|
||||
'wind_direction' => '风向',
|
||||
'box_noise' => '噪音',
|
||||
'pm10' => 'PM10',
|
||||
'pm25' => 'PM25',
|
||||
'box_co2' => 'CO2'
|
||||
];
|
||||
break;
|
||||
}
|
||||
return $arr;
|
||||
}
|
||||
|
||||
public static function fieldUnitMap($type)
|
||||
{
|
||||
$arr = [];
|
||||
switch ($type) {
|
||||
case self::TYPE_SOIL:
|
||||
$arr = [
|
||||
'conductivity'=>'us/cm',
|
||||
'humidity'=>'%RH',
|
||||
'temperature'=>'℃',
|
||||
'n'=>'n',
|
||||
'p'=>'p',
|
||||
'k'=>'k'
|
||||
];
|
||||
break;
|
||||
case self::TYPE_WATER_QUALITY:
|
||||
$arr = [
|
||||
];
|
||||
break;
|
||||
case self::TYPE_METEOROLOGICAL:
|
||||
$arr = [
|
||||
'box_temperature' => '℃',
|
||||
'box_humidity' => '%RH',
|
||||
'box_illumination' => 'Lux',
|
||||
'moment_rainfall' => 'mm',
|
||||
'wind_speed' => 'm/s',
|
||||
'wind_direction' => '',
|
||||
'box_noise' => 'db',
|
||||
'pm10' => 'ug/m3',
|
||||
'pm25' => 'ug/m3',
|
||||
'box_co2' => 'ppm'
|
||||
];
|
||||
break;
|
||||
}
|
||||
return $arr;
|
||||
}
|
||||
|
||||
protected function serializeDate(\DateTimeInterface $date){
|
||||
return $date->format('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
protected function tags():Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn($value) => $this->group_tags ? explode(',', $this->group_tags) : [],
|
||||
);
|
||||
}
|
||||
|
||||
public function region(){
|
||||
return $this->belongsToMany(Region::class, RegionMonitor::class, 'monitor_id', 'region_id')->withTimestamps();
|
||||
}
|
||||
|
||||
public function devices(){
|
||||
return $this->belongsToMany(Device::class, MonitorDevice::class, 'monitor_id', 'device_id')->withPivot('fields');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use EloquentFilter\Filterable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* 收获记录
|
||||
*/
|
||||
class PlantHarvestLog extends Model
|
||||
{
|
||||
use Filterable;
|
||||
|
||||
protected $casts = [
|
||||
'harvest_at' => 'datetime:Y-m-d',
|
||||
];
|
||||
|
||||
public function plant()
|
||||
{
|
||||
return $this->belongsTo(RegionPlantLog::class, 'plant_id');
|
||||
}
|
||||
|
||||
public function modelFilter()
|
||||
{
|
||||
return \App\Filters\Admin\PlantHarvestLogFilter::class;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Casts\Storage;
|
||||
use EloquentFilter\Filterable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
||||
/**
|
||||
* 实验田
|
||||
*/
|
||||
class Region extends Model
|
||||
{
|
||||
use Filterable;
|
||||
|
||||
protected $fillable = [
|
||||
'name', 'cover', 'director', 'area', 'description', 'category_id',
|
||||
'sort', 'is_recommend','is_enable',
|
||||
'position_x', 'position_y'
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'cover' => Storage::class,
|
||||
];
|
||||
|
||||
protected function serializeDate(\DateTimeInterface $date){
|
||||
return $date->format('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
public function scopeShow($q)
|
||||
{
|
||||
return $q->where('is_enable', 1);
|
||||
}
|
||||
|
||||
public function scopeSort($q)
|
||||
{
|
||||
return $q->orderBy('sort', 'desc')->latest('id');
|
||||
}
|
||||
|
||||
// 试验田分类
|
||||
public function category()
|
||||
{
|
||||
return $this->belongsTo(RegionCategory::class, 'category_id');
|
||||
}
|
||||
|
||||
public function monitorModes(){
|
||||
return $this->belongsToMany(MonitorMode::class, RegionMonitor::class, 'region_id', 'monitor_id')->withTimestamps()->withPivot('config');
|
||||
}
|
||||
|
||||
// 种植记录
|
||||
public function plants()
|
||||
{
|
||||
return $this->hasMany(RegionPlantLog::class, 'region_id')->orderBy('start_at', 'desc');
|
||||
}
|
||||
|
||||
// 当前种植
|
||||
public function currentPlant()
|
||||
{
|
||||
return $this->hasOne(RegionPlantLog::class, 'region_id')->whereNull('end_at')->orderBy('start_at', 'desc');
|
||||
}
|
||||
|
||||
// 收货记录
|
||||
public function harvests()
|
||||
{
|
||||
return $this->hasMany(PlantHarvestLog::class, 'region_id')->orderBy('harvest_at', 'desc');
|
||||
}
|
||||
|
||||
public static function regionTabConfig($region = null){
|
||||
$tabs = [
|
||||
[
|
||||
'title' => '基地详情',
|
||||
'value' => 'detail',
|
||||
'tab'=>\amisMake()->Service()->schemaApi(admin_url('custom-region-detail?id='.($region?->id ?? '${id}'))),
|
||||
'unmountOnExit' => true,//每次切换tab都要销毁
|
||||
'sort' => 0,
|
||||
],
|
||||
];
|
||||
if($region?->monitorModes){
|
||||
foreach($region->monitorModes as $monitorMode){
|
||||
switch($monitorMode->type)
|
||||
{
|
||||
case MonitorMode::TYPE_MONITOR:
|
||||
$tabs[] = [//有监控设备才有
|
||||
'title' => '监控视频',
|
||||
'value' => 'monitor',
|
||||
'tab'=>\amisMake()->Service()->schemaApi(admin_url('custom-region-monitor?region_id='.$region->id)),
|
||||
'unmountOnExit' => true,//每次切换tab都要销毁
|
||||
'sort' => 1,
|
||||
];
|
||||
break;
|
||||
case MonitorMode::TYPE_SOIL:
|
||||
$tabs[] = [//有土壤设备才有
|
||||
'title' => '土壤数据',
|
||||
'value' => 'soil',
|
||||
'tab'=>\amisMake()->Service()->schemaApi(admin_url('custom-region-soil?region_id='.$region->id)),
|
||||
'unmountOnExit' => true,//每次切换tab都要销毁
|
||||
'sort' => 2,
|
||||
];
|
||||
break;
|
||||
case MonitorMode::TYPE_WATER_QUALITY:
|
||||
$tabs[] = [//有水质设备才有
|
||||
'title' => '水质数据',
|
||||
'value' => 'quality',
|
||||
'tab'=>\amisMake()->Service()->schemaApi(admin_url('custom-region-water?region_id='.$region->id)),
|
||||
'unmountOnExit' => true,//每次切换tab都要销毁
|
||||
'sort' => 3,
|
||||
];
|
||||
break;
|
||||
case MonitorMode::TYPE_METEOROLOGICAL:
|
||||
$tabs[] = [//有气象设备才有
|
||||
'title' => '气象数据',
|
||||
'value' => 'meteorological',
|
||||
'tab'=>\amisMake()->Service()->schemaApi(admin_url('custom-region-meteorological?region_id='.$region->id)),
|
||||
'unmountOnExit' => true,//每次切换tab都要销毁
|
||||
'sort' => 4,
|
||||
];
|
||||
break;
|
||||
case MonitorMode::TYPE_AIR:
|
||||
$tabs[] = [//有通风设备才有
|
||||
'title' => '通风设备',
|
||||
'value' => 'air',
|
||||
'tab'=>\amisMake()->Service()->schemaApi(admin_url('custom-region-air?region_id='.$region->id)),
|
||||
'unmountOnExit' => true,//每次切换tab都要销毁
|
||||
'sort' => 5,
|
||||
];
|
||||
break;
|
||||
case MonitorMode::TYPE_ATOMIZING:
|
||||
$tabs[] = [//有喷雾设备才有
|
||||
'title' => '喷雾设备',
|
||||
'value' => 'atomizing',
|
||||
'tab'=>\amisMake()->Service()->schemaApi(admin_url('custom-region-atomizing?region_id='.$region->id)),
|
||||
'unmountOnExit' => true,//每次切换tab都要销毁
|
||||
'sort' => 6,
|
||||
];
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$tabs = array_merge(collect($tabs)->sortBy('sort')->toArray(), []);
|
||||
return $tabs;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use EloquentFilter\Filterable;
|
||||
use App\Casts\Storage;
|
||||
|
||||
/**
|
||||
* 区域分类
|
||||
*/
|
||||
class RegionCategory extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
use Filterable;
|
||||
|
||||
protected $fillable = ['name', 'icon', 'description', 'parent_id', 'level', 'sort', 'path', 'is_enable'];
|
||||
|
||||
protected $casts = [
|
||||
'icon' => Storage::class,
|
||||
];
|
||||
|
||||
protected function serializeDate(\DateTimeInterface $date){
|
||||
return $date->format('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
protected static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
// 监听 Category 的创建事件,用于初始化 path 和 level 字段值
|
||||
static::creating(function ($category) {
|
||||
// 如果创建的是一个根类目
|
||||
if (! $category->parent_id) {
|
||||
// 将层级设为 1
|
||||
$category->level = 1;
|
||||
// 将 path 设为 -
|
||||
$category->path = '-';
|
||||
} else {
|
||||
// 将层级设为父类目的层级 + 1
|
||||
$category->level = $category->parent->level + 1;
|
||||
// 将 path 值设为父类目的 path 追加父类目 ID 以及最后跟上一个 - 分隔符
|
||||
$category->path = $category->parent->path.$category->parent_id.'-';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function parent()
|
||||
{
|
||||
return $this->belongsTo(self::class, 'parent_id');
|
||||
}
|
||||
|
||||
public function scopeShow($q)
|
||||
{
|
||||
return $q->where('is_enable', 1);
|
||||
}
|
||||
|
||||
public function scopeSort($q)
|
||||
{
|
||||
return $q->orderBy('sort', 'desc')->latest('id');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class RegionMonitor extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use EloquentFilter\Filterable;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* 种植记录
|
||||
*/
|
||||
class RegionPlantLog extends Model
|
||||
{
|
||||
use Filterable;
|
||||
|
||||
protected $appends = ['plant_state'];
|
||||
|
||||
protected $casts = [
|
||||
'start_at' => 'datetime:Y-m-d',
|
||||
'end_at' => 'datetime:Y-m-d',
|
||||
];
|
||||
|
||||
//1进行中,2已结束
|
||||
protected function plantState():Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn($value) => $this->end_at ? 2 : 1,
|
||||
);
|
||||
}
|
||||
|
||||
// 收获记录
|
||||
public function harvestes()
|
||||
{
|
||||
return $this->hasMany(PlantHarvestLog::class, 'plant_id');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class SoilDailyReport extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $casts = [
|
||||
'reported_at' => 'date',
|
||||
];
|
||||
|
||||
protected $fillable = [
|
||||
'device_id',
|
||||
'temperature',
|
||||
'humidity',
|
||||
'n',
|
||||
'p',
|
||||
'k',
|
||||
'conductivity',
|
||||
'moisture',
|
||||
'reported_at',
|
||||
];
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class SoilReport extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $casts = [
|
||||
'reported_at' => 'datetime',
|
||||
];
|
||||
|
||||
protected $fillable = [
|
||||
'device_id',
|
||||
'temperature',
|
||||
'humidity',
|
||||
'n',
|
||||
'p',
|
||||
'k',
|
||||
'conductivity',
|
||||
'moisture',
|
||||
'reported_at',
|
||||
];
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue