From d0c3c066591181ea11bca20f5e038332395568d3 Mon Sep 17 00:00:00 2001 From: "zoomtk@126.com" Date: Thu, 23 Feb 2023 20:14:36 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Mall/Controller/IndexController.php | 19 +- app/System/Controller/IndexController.php | 15 + app/System/Controller/LoginController.php | 8 +- app/System/Service/ModuleService.php | 2 +- app/System/Service/SystemUserService.php | 1 - builder/Crontab/MineExecutor.php | 2 - builder/Entity/EntityBean.php | 6 - builder/Entity/MenuEntity.php | 2 - builder/Entity/MenuItemEntity.php | 2 - builder/Entity/UISettingEntity.php | 11 - builder/Entity/UserEntity.php | 2 - builder/Exception/ApiException.php | 12 + builder/Exception/AppException.php | 12 + builder/Exception/BusinessException.php | 11 + builder/Exception/CaptchaException.php | 1 - builder/Exception/MineException.php | 11 - builder/Exception/ValidateException.php | 12 + builder/Helper/functions.php | 113 +- builder/Interfaces/MenuInterface.php | 20 + builder/MineApi.php | 2 - builder/MineCollection.php | 7 - builder/MineController.php | 2 - builder/MineModel.php | 3 - builder/MineRequest.php | 2 - builder/MineServer.php | 2 +- builder/MineStart.php | 3 +- builder/Traits/ControllerTrait.php | 1 - builder/Traits/JsonBuilder.php | 27 + builder/Traits/MapperTrait.php | 5 +- .../Annotations/RequestValidation.php | 59 + builder/Validate/Annotations/Validation.php | 57 + builder/Validate/Aspect/ValidationAspect.php | 145 ++ builder/Validate/ConfigProvider.php | 20 + .../Validate/Exception/ValidateException.php | 8 + builder/Validate/Validate.php | 1512 +++++++++++++++++ builder/Validate/ValidateRule.php | 158 ++ builder/View.php | 42 + builder/View/Actions/BaseAction.php | 125 ++ builder/View/Actions/BaseRowAction.php | 31 + builder/View/Components/Antv/Area.php | 14 + builder/View/Components/Antv/Column.php | 14 + builder/View/Components/Antv/Funnel.php | 59 + builder/View/Components/Antv/Line.php | 59 + builder/View/Components/Antv/Pie.php | 51 + builder/View/Components/Antv/StepLine.php | 16 + builder/View/Components/Attrs/Button.php | 101 ++ .../View/Components/Attrs/CascaderOption.php | 49 + .../View/Components/Attrs/CascaderProps.php | 16 + builder/View/Components/Attrs/Depend.php | 51 + builder/View/Components/Attrs/ElDialog.php | 153 ++ builder/View/Components/Attrs/ElLink.php | 49 + .../View/Components/Attrs/SelectOption.php | 52 + builder/View/Components/Attrs/Step.php | 62 + .../View/Components/Attrs/TransferData.php | 37 + builder/View/Components/Component.php | 106 ++ builder/View/Components/Form/CSwitch.php | 165 ++ builder/View/Components/Form/Cascader.php | 276 +++ builder/View/Components/Form/Checkbox.php | 138 ++ .../View/Components/Form/CheckboxGroup.php | 113 ++ builder/View/Components/Form/ColorPicker.php | 97 ++ builder/View/Components/Form/DatePicker.php | 274 +++ .../View/Components/Form/DateTimePicker.php | 13 + builder/View/Components/Form/IconChoose.php | 84 + builder/View/Components/Form/Input.php | 354 ++++ builder/View/Components/Form/InputNumber.php | 190 +++ builder/View/Components/Form/ItemSelect.php | 15 + builder/View/Components/Form/Radio.php | 93 + builder/View/Components/Form/RadioGroup.php | 94 + builder/View/Components/Form/Rate.php | 196 +++ builder/View/Components/Form/Select.php | 304 ++++ builder/View/Components/Form/Slider.php | 186 ++ builder/View/Components/Form/TimePicker.php | 282 +++ builder/View/Components/Form/Transfer.php | 118 ++ builder/View/Components/Form/Upload.php | 222 +++ builder/View/Components/Form/WangEditor.php | 156 ++ builder/View/Components/Grid/Avatar.php | 149 ++ builder/View/Components/Grid/Boole.php | 12 + builder/View/Components/Grid/GSwitch.php | 164 ++ builder/View/Components/Grid/Icon.php | 12 + builder/View/Components/Grid/Image.php | 121 ++ builder/View/Components/Grid/Link.php | 77 + builder/View/Components/Grid/Route.php | 47 + builder/View/Components/Grid/Tag.php | 116 ++ builder/View/Components/GridComponent.php | 7 + builder/View/Components/Widgets/Alert.php | 117 ++ builder/View/Components/Widgets/Badge.php | 92 + builder/View/Components/Widgets/Button.php | 73 + builder/View/Components/Widgets/Card.php | 64 + builder/View/Components/Widgets/Category.php | 86 + builder/View/Components/Widgets/Dialog.php | 19 + .../View/Components/Widgets/DialogButton.php | 20 + builder/View/Components/Widgets/Divider.php | 50 + builder/View/Components/Widgets/Html.php | 32 + builder/View/Components/Widgets/Markdown.php | 26 + builder/View/Components/Widgets/Steps.php | 129 ++ builder/View/Components/Widgets/Text.php | 26 + builder/View/Components/Widgets/Tooltip.php | 185 ++ builder/View/Form.php | 944 ++++++++++ builder/View/Form/FormActions.php | 134 ++ builder/View/Form/FormAttrs.php | 26 + builder/View/Form/FormItem.php | 773 +++++++++ builder/View/Form/FormTab.php | 58 + builder/View/Form/HasHooks.php | 225 +++ builder/View/Form/HasRef.php | 34 + builder/View/Form/Model.php | 34 + builder/View/Form/TraitFormAttrs.php | 218 +++ builder/View/Form/Utils/VIfEval.php | 58 + builder/View/Grid.php | 483 ++++++ builder/View/Grid/Actions.php | 53 + builder/View/Grid/Actions/ActionButton.php | 61 + builder/View/Grid/Actions/ActionLink.php | 46 + builder/View/Grid/Actions/DeleteAction.php | 11 + builder/View/Grid/Actions/EditAction.php | 26 + builder/View/Grid/Actions/VueRouteAction.php | 51 + builder/View/Grid/BatchActions.php | 84 + .../View/Grid/BatchActions/BatchAction.php | 109 ++ .../View/Grid/BatchActions/DeleteAction.php | 25 + builder/View/Grid/Column.php | 171 ++ builder/View/Grid/Column/Attributes.php | 83 + builder/View/Grid/Concerns/CanExportGrid.php | 88 + builder/View/Grid/Concerns/HasActions.php | 129 ++ .../Grid/Concerns/HasColumnAttributes.php | 239 +++ builder/View/Grid/Concerns/HasDefaultSort.php | 27 + builder/View/Grid/Concerns/HasDialog.php | 15 + builder/View/Grid/Concerns/HasExport.php | 44 + builder/View/Grid/Concerns/HasFilter.php | 32 + .../View/Grid/Concerns/HasGridAttributes.php | 265 +++ .../View/Grid/Concerns/HasPageAttributes.php | 85 + builder/View/Grid/Concerns/HasQuickSearch.php | 229 +++ builder/View/Grid/Exporter.php | 145 ++ .../View/Grid/Exporters/AbstractExporter.php | 136 ++ builder/View/Grid/Exporters/CsvExporter.php | 231 +++ .../View/Grid/Exporters/ExporterInterface.php | 11 + builder/View/Grid/Filter.php | 211 +++ builder/View/Grid/Filter/AbstractFilter.php | 120 ++ builder/View/Grid/Filter/Between.php | 50 + builder/View/Grid/Filter/Date.php | 24 + builder/View/Grid/Filter/Day.php | 15 + builder/View/Grid/Filter/EndsWith.php | 7 + builder/View/Grid/Filter/Equal.php | 7 + builder/View/Grid/Filter/Gt.php | 27 + builder/View/Grid/Filter/Ilike.php | 7 + builder/View/Grid/Filter/In.php | 28 + builder/View/Grid/Filter/Like.php | 37 + builder/View/Grid/Filter/Lt.php | 28 + builder/View/Grid/Filter/Month.php | 15 + builder/View/Grid/Filter/NotEqual.php | 19 + builder/View/Grid/Filter/NotIn.php | 10 + builder/View/Grid/Filter/Scope.php | 75 + builder/View/Grid/Filter/StartsWith.php | 7 + builder/View/Grid/Filter/Where.php | 64 + builder/View/Grid/Filter/Year.php | 14 + builder/View/Grid/Model.php | 517 ++++++ builder/View/Grid/Table/Attributes.php | 69 + builder/View/Grid/Toolbars.php | 92 + builder/View/Grid/Tools/CreateButton.php | 29 + builder/View/Grid/Tools/QuickSearch.php | 24 + builder/View/Grid/Tools/ToolButton.php | 51 + builder/View/Layout/Buildable.php | 7 + builder/View/Layout/Column.php | 107 ++ builder/View/Layout/Content.php | 75 + builder/View/Layout/Row.php | 108 ++ builder/View/Tree.php | 440 +++++ builder/View/Tree/Actions.php | 51 + builder/View/Tree/Column.php | 170 ++ builder/View/Tree/Toolbars.php | 89 + config/autoload/jwt.php | 8 +- 167 files changed, 16265 insertions(+), 76 deletions(-) create mode 100644 builder/Exception/ApiException.php create mode 100644 builder/Exception/AppException.php create mode 100644 builder/Exception/BusinessException.php create mode 100644 builder/Exception/ValidateException.php create mode 100644 builder/Interfaces/MenuInterface.php create mode 100644 builder/Traits/JsonBuilder.php create mode 100644 builder/Validate/Annotations/RequestValidation.php create mode 100644 builder/Validate/Annotations/Validation.php create mode 100644 builder/Validate/Aspect/ValidationAspect.php create mode 100644 builder/Validate/ConfigProvider.php create mode 100644 builder/Validate/Exception/ValidateException.php create mode 100644 builder/Validate/Validate.php create mode 100644 builder/Validate/ValidateRule.php create mode 100644 builder/View/Actions/BaseAction.php create mode 100644 builder/View/Actions/BaseRowAction.php create mode 100644 builder/View/Components/Antv/Area.php create mode 100644 builder/View/Components/Antv/Column.php create mode 100644 builder/View/Components/Antv/Funnel.php create mode 100644 builder/View/Components/Antv/Line.php create mode 100644 builder/View/Components/Antv/Pie.php create mode 100644 builder/View/Components/Antv/StepLine.php create mode 100644 builder/View/Components/Attrs/Button.php create mode 100644 builder/View/Components/Attrs/CascaderOption.php create mode 100644 builder/View/Components/Attrs/CascaderProps.php create mode 100644 builder/View/Components/Attrs/Depend.php create mode 100644 builder/View/Components/Attrs/ElDialog.php create mode 100644 builder/View/Components/Attrs/ElLink.php create mode 100644 builder/View/Components/Attrs/SelectOption.php create mode 100644 builder/View/Components/Attrs/Step.php create mode 100644 builder/View/Components/Attrs/TransferData.php create mode 100644 builder/View/Components/Component.php create mode 100644 builder/View/Components/Form/CSwitch.php create mode 100644 builder/View/Components/Form/Cascader.php create mode 100644 builder/View/Components/Form/Checkbox.php create mode 100644 builder/View/Components/Form/CheckboxGroup.php create mode 100644 builder/View/Components/Form/ColorPicker.php create mode 100644 builder/View/Components/Form/DatePicker.php create mode 100644 builder/View/Components/Form/DateTimePicker.php create mode 100644 builder/View/Components/Form/IconChoose.php create mode 100644 builder/View/Components/Form/Input.php create mode 100644 builder/View/Components/Form/InputNumber.php create mode 100644 builder/View/Components/Form/ItemSelect.php create mode 100644 builder/View/Components/Form/Radio.php create mode 100644 builder/View/Components/Form/RadioGroup.php create mode 100644 builder/View/Components/Form/Rate.php create mode 100644 builder/View/Components/Form/Select.php create mode 100644 builder/View/Components/Form/Slider.php create mode 100644 builder/View/Components/Form/TimePicker.php create mode 100644 builder/View/Components/Form/Transfer.php create mode 100644 builder/View/Components/Form/Upload.php create mode 100644 builder/View/Components/Form/WangEditor.php create mode 100644 builder/View/Components/Grid/Avatar.php create mode 100644 builder/View/Components/Grid/Boole.php create mode 100644 builder/View/Components/Grid/GSwitch.php create mode 100644 builder/View/Components/Grid/Icon.php create mode 100644 builder/View/Components/Grid/Image.php create mode 100644 builder/View/Components/Grid/Link.php create mode 100644 builder/View/Components/Grid/Route.php create mode 100644 builder/View/Components/Grid/Tag.php create mode 100644 builder/View/Components/GridComponent.php create mode 100644 builder/View/Components/Widgets/Alert.php create mode 100644 builder/View/Components/Widgets/Badge.php create mode 100644 builder/View/Components/Widgets/Button.php create mode 100644 builder/View/Components/Widgets/Card.php create mode 100644 builder/View/Components/Widgets/Category.php create mode 100644 builder/View/Components/Widgets/Dialog.php create mode 100644 builder/View/Components/Widgets/DialogButton.php create mode 100644 builder/View/Components/Widgets/Divider.php create mode 100644 builder/View/Components/Widgets/Html.php create mode 100644 builder/View/Components/Widgets/Markdown.php create mode 100644 builder/View/Components/Widgets/Steps.php create mode 100644 builder/View/Components/Widgets/Text.php create mode 100644 builder/View/Components/Widgets/Tooltip.php create mode 100644 builder/View/Form.php create mode 100644 builder/View/Form/FormActions.php create mode 100644 builder/View/Form/FormAttrs.php create mode 100644 builder/View/Form/FormItem.php create mode 100644 builder/View/Form/FormTab.php create mode 100644 builder/View/Form/HasHooks.php create mode 100644 builder/View/Form/HasRef.php create mode 100644 builder/View/Form/Model.php create mode 100644 builder/View/Form/TraitFormAttrs.php create mode 100644 builder/View/Form/Utils/VIfEval.php create mode 100644 builder/View/Grid.php create mode 100644 builder/View/Grid/Actions.php create mode 100644 builder/View/Grid/Actions/ActionButton.php create mode 100644 builder/View/Grid/Actions/ActionLink.php create mode 100644 builder/View/Grid/Actions/DeleteAction.php create mode 100644 builder/View/Grid/Actions/EditAction.php create mode 100644 builder/View/Grid/Actions/VueRouteAction.php create mode 100644 builder/View/Grid/BatchActions.php create mode 100644 builder/View/Grid/BatchActions/BatchAction.php create mode 100644 builder/View/Grid/BatchActions/DeleteAction.php create mode 100644 builder/View/Grid/Column.php create mode 100644 builder/View/Grid/Column/Attributes.php create mode 100644 builder/View/Grid/Concerns/CanExportGrid.php create mode 100644 builder/View/Grid/Concerns/HasActions.php create mode 100644 builder/View/Grid/Concerns/HasColumnAttributes.php create mode 100644 builder/View/Grid/Concerns/HasDefaultSort.php create mode 100644 builder/View/Grid/Concerns/HasDialog.php create mode 100644 builder/View/Grid/Concerns/HasExport.php create mode 100644 builder/View/Grid/Concerns/HasFilter.php create mode 100644 builder/View/Grid/Concerns/HasGridAttributes.php create mode 100644 builder/View/Grid/Concerns/HasPageAttributes.php create mode 100644 builder/View/Grid/Concerns/HasQuickSearch.php create mode 100644 builder/View/Grid/Exporter.php create mode 100644 builder/View/Grid/Exporters/AbstractExporter.php create mode 100644 builder/View/Grid/Exporters/CsvExporter.php create mode 100644 builder/View/Grid/Exporters/ExporterInterface.php create mode 100644 builder/View/Grid/Filter.php create mode 100644 builder/View/Grid/Filter/AbstractFilter.php create mode 100644 builder/View/Grid/Filter/Between.php create mode 100644 builder/View/Grid/Filter/Date.php create mode 100644 builder/View/Grid/Filter/Day.php create mode 100644 builder/View/Grid/Filter/EndsWith.php create mode 100644 builder/View/Grid/Filter/Equal.php create mode 100644 builder/View/Grid/Filter/Gt.php create mode 100644 builder/View/Grid/Filter/Ilike.php create mode 100644 builder/View/Grid/Filter/In.php create mode 100644 builder/View/Grid/Filter/Like.php create mode 100644 builder/View/Grid/Filter/Lt.php create mode 100644 builder/View/Grid/Filter/Month.php create mode 100644 builder/View/Grid/Filter/NotEqual.php create mode 100644 builder/View/Grid/Filter/NotIn.php create mode 100644 builder/View/Grid/Filter/Scope.php create mode 100644 builder/View/Grid/Filter/StartsWith.php create mode 100644 builder/View/Grid/Filter/Where.php create mode 100644 builder/View/Grid/Filter/Year.php create mode 100644 builder/View/Grid/Model.php create mode 100644 builder/View/Grid/Table/Attributes.php create mode 100644 builder/View/Grid/Toolbars.php create mode 100644 builder/View/Grid/Tools/CreateButton.php create mode 100644 builder/View/Grid/Tools/QuickSearch.php create mode 100644 builder/View/Grid/Tools/ToolButton.php create mode 100644 builder/View/Layout/Buildable.php create mode 100644 builder/View/Layout/Column.php create mode 100644 builder/View/Layout/Content.php create mode 100644 builder/View/Layout/Row.php create mode 100644 builder/View/Tree.php create mode 100644 builder/View/Tree/Actions.php create mode 100644 builder/View/Tree/Column.php create mode 100644 builder/View/Tree/Toolbars.php diff --git a/app/Mall/Controller/IndexController.php b/app/Mall/Controller/IndexController.php index b03db54..90b72ad 100644 --- a/app/Mall/Controller/IndexController.php +++ b/app/Mall/Controller/IndexController.php @@ -17,9 +17,11 @@ use Builder\MineController; use Builder\Entity\UISettingEntity; use Builder\Entity\UserEntity; +use Builder\View; +use Builder\View\Layout\Content; +use Builder\View\Components\Widgets\Alert; use Builder\Entity\MenuEntity; use Builder\Entity\MenuItemEntity; -use Builder\View; /** * Class IndexController * @package App\Mall\Controller @@ -35,4 +37,19 @@ class IndexController extends MineController $setting->setUser($user); return View::make($setting); } + + #[GetMapping("test")] + public function test() + { + $content = new Content(); + $content->showHeader() + ->title('测试') + ->body( + Alert::make() + ->type('error') + ->title('这里是警告信息') + ->description('我是提示消息哈哈哈') + ); + return $content; + } } \ No newline at end of file diff --git a/app/System/Controller/IndexController.php b/app/System/Controller/IndexController.php index 159a81e..e197341 100644 --- a/app/System/Controller/IndexController.php +++ b/app/System/Controller/IndexController.php @@ -19,6 +19,7 @@ use Builder\Entity\UISettingEntity; use Builder\Entity\UserEntity; use Builder\MineController; use Builder\View; +use Builder\View\Layout\Content; /** * Class IndexController * @package App\System\Controller @@ -35,4 +36,18 @@ class IndexController extends MineController return View::make($setting); } + + #[GetMapping("test")] + public function test(){ + + $content = new Content(); + $content->showHeader()->title('测试')->body(Alert::make()->type('error') + ->title('这里是警告信息') + ->description('我是提示消息哈哈哈') + ); + return $content; + var_dump(5555); + + return 1; + } } \ No newline at end of file diff --git a/app/System/Controller/LoginController.php b/app/System/Controller/LoginController.php index 16165c5..2a4dc96 100644 --- a/app/System/Controller/LoginController.php +++ b/app/System/Controller/LoginController.php @@ -30,12 +30,6 @@ class LoginController extends MineController #[Inject] protected UserServiceInterface $userService; - - public function index(){ - - - } - /** * @param SystemUserRequest $request * @return ResponseInterface @@ -68,6 +62,8 @@ class LoginController extends MineController /** * 用户信息 + * /** + * @param SystemUserRequest $request * @return ResponseInterface * @throws \Psr\Container\ContainerExceptionInterface * @throws \Psr\Container\NotFoundExceptionInterface diff --git a/app/System/Service/ModuleService.php b/app/System/Service/ModuleService.php index 422ce84..b016c12 100644 --- a/app/System/Service/ModuleService.php +++ b/app/System/Service/ModuleService.php @@ -8,9 +8,9 @@ use Hyperf\Utils\Filesystem\Filesystem; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Output\NullOutput; +use Builder\Mine; use Builder\Abstracts\AbstractService; use Builder\Generator\ModuleGenerator; -use Builder\Mine; class ModuleService extends AbstractService { /** diff --git a/app/System/Service/SystemUserService.php b/app/System/Service/SystemUserService.php index bcc96fb..b651458 100644 --- a/app/System/Service/SystemUserService.php +++ b/app/System/Service/SystemUserService.php @@ -2,7 +2,6 @@ declare(strict_types=1); namespace App\System\Service; - use App\System\Mapper\SystemUserMapper; use Hyperf\Cache\Annotation\Cacheable; use Hyperf\Cache\Annotation\CacheEvict; diff --git a/builder/Crontab/MineExecutor.php b/builder/Crontab/MineExecutor.php index b950d92..83e0355 100644 --- a/builder/Crontab/MineExecutor.php +++ b/builder/Crontab/MineExecutor.php @@ -170,12 +170,10 @@ class MineExecutor { return function () use ($crontab, $runnable) { $taskMutex = $this->getTaskMutex(); - if ($taskMutex->exists($crontab) || ! $taskMutex->create($crontab)) { $this->logger->info(sprintf('Crontab task [%s] skipped execution at %s.', $crontab->getName(), date('Y-m-d H:i:s'))); return; } - try { $runnable(); } finally { diff --git a/builder/Entity/EntityBean.php b/builder/Entity/EntityBean.php index 6fd19bd..cc55751 100644 --- a/builder/Entity/EntityBean.php +++ b/builder/Entity/EntityBean.php @@ -1,10 +1,6 @@ - * @Link https://gitee.com/xmo/MineAdmin - */ - declare(strict_types=1); namespace Builder\Exception; - class MineException extends \RuntimeException { diff --git a/builder/Exception/ValidateException.php b/builder/Exception/ValidateException.php new file mode 100644 index 0000000..1652210 --- /dev/null +++ b/builder/Exception/ValidateException.php @@ -0,0 +1,12 @@ +get(StdoutLoggerInterface::class); } - } if (! function_exists('logger')) { @@ -235,4 +237,109 @@ if (! function_exists('add_queue')) { ->get(\App\System\Service\SystemQueueLogService::class) ->addQueue($amqpQueueVo); } -} \ No newline at end of file +} +if (!function_exists('request')) { + /** + * Get admin path. + * @param null $key + * @param null $default + * @return string + */ + function request($key = null, $default = null) + { + if ($key !== null) { + return (new Request())->all()[$key] ?? $default; + } + return new Request(); + } +} +if (!function_exists('response')) { + /** + * Get admin path. + * + * @param string $path + * + * @return string + */ + function response(): ResponseInterface + { + return new Response();//return $res->withBody(new SwooleStream(json_encode($re_data,256))); + } +} + +if (!function_exists('admin_api_url')) { + /** + * Get admin url. + * + * @param string $path + * @param mixed $parameters + * @param bool $secure + * + * @return string + */ + function admin_api_url($path = '', $parameters = [], $secure = null) + { + return '/' . str_replace('/list', '', $path); + } +} + +//输出控制台日志 +if (!function_exists('p')) { + function p($val, $title = null, $starttime = '') + { + print_r('[ ' . date("Y-m-d H:i:s") . ']:'); + if ($title != null) { + print_r("[" . $title . "]:"); + } + print_r($val); + print_r("\r\n"); + } +} +if (!function_exists('uuid')) { + function uuid($length) + { + if (function_exists('random_bytes')) { + $uuid = bin2hex(\random_bytes($length)); + } else if (function_exists('openssl_random_pseudo_bytes')) { + $uuid = bin2hex(\openssl_random_pseudo_bytes($length)); + } else { + $pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + $uuid = substr(str_shuffle(str_repeat($pool, 5)), 0, $length); + } + return $uuid; + } +} + +if (!function_exists('route')) { + function route($url, $param = []) + { + if (is_validURL($url)) { + return $url; + } + $prefix = config('admin.route.prefix'); + $https = config('admin.https'); + $uri = request()->getUri(); + $param = http_build_query($param); + if ($https) { + $uri = $uri->withScheme('https'); + } + $uri = $uri->withQuery($param); + if (substr($url, 0, 1) !== '/') { + $url = '/' . $prefix . '/' . $url; + } + return $uri->withPath($url)->__toString(); + } +} + +if (!function_exists('is_validURL')) { + function is_validURL($url) + { + $check = 0; + if (filter_var($url, FILTER_VALIDATE_URL) !== false) { + $check = 1; + } + return $check; + } +} + + diff --git a/builder/Interfaces/MenuInterface.php b/builder/Interfaces/MenuInterface.php new file mode 100644 index 0000000..84d78b6 --- /dev/null +++ b/builder/Interfaces/MenuInterface.php @@ -0,0 +1,20 @@ +toArray(); if (empty($data)) return []; - $routers = []; foreach ($data as $menu) { array_push($routers, $this->setRouter($menu)); @@ -69,11 +68,8 @@ class MineCollection extends Collection public function toTree(array $data = [], int $parentId = 0, string $id = 'id', string $parentField = 'parent_id', string $children='children'): array { $data = $data ?: $this->toArray(); - if (empty($data)) return []; - $tree = []; - foreach ($data as $value) { if ($value[$parentField] == $parentId) { $child = $this->toTree($data, $value[$id], $id, $parentField, $children); @@ -83,11 +79,9 @@ class MineCollection extends Collection array_push($tree, $value); } } - unset($data); return $tree; } - /** * 导出数据 * @param string $dto @@ -129,5 +123,4 @@ class MineCollection extends Collection } return $excel->import($model, $closure); } - } \ No newline at end of file diff --git a/builder/MineController.php b/builder/MineController.php index c47d03a..ef39147 100644 --- a/builder/MineController.php +++ b/builder/MineController.php @@ -11,9 +11,7 @@ declare(strict_types=1); namespace Builder; - use Hyperf\Di\Annotation\Inject; -use Psr\Container\ContainerInterface; use Builder\Traits\ControllerTrait; /** diff --git a/builder/MineModel.php b/builder/MineModel.php index 3b3f5fd..4709423 100644 --- a/builder/MineModel.php +++ b/builder/MineModel.php @@ -8,10 +8,8 @@ * @Author X.Mo * @Link https://gitee.com/xmo/MineAdmin */ - declare(strict_types=1); namespace Builder; - use Hyperf\DbConnection\Model\Model; use Hyperf\ModelCache\Cacheable; use Builder\Traits\ModelMacroTrait; @@ -48,7 +46,6 @@ class MineModel extends Model public function __construct(array $attributes = []) { parent::__construct($attributes); - //注册常用方法 $this->registerBase(); //注册用户数据权限方法 diff --git a/builder/MineRequest.php b/builder/MineRequest.php index ddb53ab..5853b9a 100644 --- a/builder/MineRequest.php +++ b/builder/MineRequest.php @@ -14,7 +14,6 @@ namespace Builder; use Hyperf\Di\Annotation\Inject; use Hyperf\HttpServer\Request; - class MineRequest extends Request { /** @@ -39,7 +38,6 @@ class MineRequest extends Request } else if (isset($headers['http_x_forwarded_for'])) { $ip = $headers['http_x_forwarded_for'][0]; } - return $ip; } diff --git a/builder/MineServer.php b/builder/MineServer.php index 7603612..8616b0b 100644 --- a/builder/MineServer.php +++ b/builder/MineServer.php @@ -16,7 +16,7 @@ use Hyperf\HttpServer\Server; class MineServer extends Server { - protected ?string $serverName = 'MineAdmin'; + protected ?string $serverName = 'ViewUI'; protected $routes; diff --git a/builder/MineStart.php b/builder/MineStart.php index 343b2bd..f801fcd 100644 --- a/builder/MineStart.php +++ b/builder/MineStart.php @@ -25,11 +25,12 @@ class MineStart extends ServerStartCallback $service = container()->get(ModuleService::class); $service->setModuleCache(); $console = console(); - $console->info('MineAdmin start success...'); + $console->info('ViewUI start success...'); $console->info($this->welcome()); $console->info('current booting the user: ' . shell_exec('whoami')); } + protected function welcome(): string { return sprintf(' diff --git a/builder/Traits/ControllerTrait.php b/builder/Traits/ControllerTrait.php index 454fcb4..c285e9f 100644 --- a/builder/Traits/ControllerTrait.php +++ b/builder/Traits/ControllerTrait.php @@ -10,7 +10,6 @@ */ declare(strict_types = 1); - namespace Builder\Traits; use Hyperf\Di\Annotation\Inject; use Psr\Http\Message\ResponseInterface; diff --git a/builder/Traits/JsonBuilder.php b/builder/Traits/JsonBuilder.php new file mode 100644 index 0000000..5a05b43 --- /dev/null +++ b/builder/Traits/JsonBuilder.php @@ -0,0 +1,27 @@ +hideAttrs)->push("hideAttrs")->toArray(); + foreach ($this as $key => $val) { + if (!in_array($key, $hide) && $val !== null) { + $data[$key] = $val; + } + } + return $data; + } + + public function __toString(): string + { + // TODO: Implement __toString() method. + return json_encode($this->jsonSerialize(), JSON_UNESCAPED_UNICODE); + } +} diff --git a/builder/Traits/MapperTrait.php b/builder/Traits/MapperTrait.php index 8eb18dc..6045c81 100644 --- a/builder/Traits/MapperTrait.php +++ b/builder/Traits/MapperTrait.php @@ -10,14 +10,13 @@ */ namespace Builder\Traits; - use Hyperf\Contract\LengthAwarePaginatorInterface; use Hyperf\Database\Model\Builder; use Hyperf\Database\Model\Model; + use Builder\Annotation\Transaction; use Builder\MineCollection; use Builder\MineModel; - trait MapperTrait { /** @@ -124,7 +123,6 @@ trait MapperTrait if (isset($params['_mineadmin_tree'])) { $query->orderBy($params['_mineadmin_tree_pid']); } - if ($params['orderBy'] ?? false) { if (is_array($params['orderBy'])) { foreach ($params['orderBy'] as $key => $order) { @@ -134,7 +132,6 @@ trait MapperTrait $query->orderBy($params['orderBy'], $params['orderType'] ?? 'asc'); } } - return $query; } diff --git a/builder/Validate/Annotations/RequestValidation.php b/builder/Validate/Annotations/RequestValidation.php new file mode 100644 index 0000000..5d00b77 --- /dev/null +++ b/builder/Validate/Annotations/RequestValidation.php @@ -0,0 +1,59 @@ +bindMainProperty('scene', $value); + } +} \ No newline at end of file diff --git a/builder/Validate/Annotations/Validation.php b/builder/Validate/Annotations/Validation.php new file mode 100644 index 0000000..7e01b01 --- /dev/null +++ b/builder/Validate/Annotations/Validation.php @@ -0,0 +1,57 @@ +bindMainProperty('scene', $value); + } +} \ No newline at end of file diff --git a/builder/Validate/Aspect/ValidationAspect.php b/builder/Validate/Aspect/ValidationAspect.php new file mode 100644 index 0000000..e6ae24d --- /dev/null +++ b/builder/Validate/Aspect/ValidationAspect.php @@ -0,0 +1,145 @@ +container = $container; + $this->request = $this->container->get(ServerRequestInterface::class); + } + + /** + * @param ProceedingJoinPoint $proceedingJoinPoint + * @return mixed + * @throws Exception + * @throws ValidateException + */ + public function process(ProceedingJoinPoint $proceedingJoinPoint) + { + foreach ($proceedingJoinPoint->getAnnotationMetadata()->method as $validation) { + /** + * @var Validation $validation + */ + switch (true) { + case $validation instanceof RequestValidation: + $verData = $this->request->all(); + $this->validationData($validation, $verData, $validation->validate, $proceedingJoinPoint, true); + break; + case $validation instanceof Validation: + $verData = $proceedingJoinPoint->arguments['keys'][$validation->field]; + $this->validationData($validation, $verData, $validation->validate, $proceedingJoinPoint); + break; + default: + break; + } + } + return $proceedingJoinPoint->process(); + } + + /** + * @param $validation + * @param $verData + * @param $class + * @param $proceedingJoinPoint + * @param $isRequest + * @throws ValidateException + */ + private function validationData($validation, $verData, $class, $proceedingJoinPoint, $isRequest = false) + { + /** + * @var RequestValidation $validation + */ + /** + * @var Validate $validate + */ + if ($validation->rules != null) { + $validate = new Validate(); + $rules = $validation->rules; + } else { + if (class_exists($class)) { + $validate = new $class; + } else { + throw new ValidateException('class not exists:' . $class); + } + if ($validation->scene == '') { + $validation->scene = $proceedingJoinPoint->methodName; + } + $rules = $validate->getSceneRule($validation->scene); + } + + if ($validate->batch($validation->batch)->check($verData, $rules, $validation->scene) === false) { + throw new ValidateException($validate->getError()); + } + if ($validation->security) { + $fields = $this->getFields($rules); + foreach ($verData as $key => $item) { + if (!in_array($key, $fields)) { + throw new ValidateException('params ' . $key . ' invalid'); + } + } + }; + if ($validation->filter) { + $fields = $this->getFields($rules); + $verData = array_filter($verData, function ($value, $key) use ($fields) { + return in_array($key, $fields); + }, ARRAY_FILTER_USE_BOTH); + + switch ($isRequest) { + case true: + Context::override(ServerRequestInterface::class, function (ServerRequestInterface $request) use ($verData) { + return $request->withParsedBody($verData); + }); + break; + default: + $proceedingJoinPoint->arguments['keys'][$validation->field] = $verData; + break; + } + } + } + + protected function getFields(array $rules) + { + $fields = []; + foreach ($rules as $field => $rule) { + if (is_numeric($field)) { + $field = $rule; + } + if (strpos($field, '|')) { + // 字段|描述 用于指定属性名称 + list($field,) = explode('|', $field); + } + $fields[] = $field; + } + return $fields; + } +} \ No newline at end of file diff --git a/builder/Validate/ConfigProvider.php b/builder/Validate/ConfigProvider.php new file mode 100644 index 0000000..963937a --- /dev/null +++ b/builder/Validate/ConfigProvider.php @@ -0,0 +1,20 @@ + [ + 'scan' => [ + 'paths' => [ + __DIR__, + ] + ] + ] + ]; + } +} \ No newline at end of file diff --git a/builder/Validate/Exception/ValidateException.php b/builder/Validate/Exception/ValidateException.php new file mode 100644 index 0000000..935452c --- /dev/null +++ b/builder/Validate/Exception/ValidateException.php @@ -0,0 +1,8 @@ +' => 'gt', '>=' => 'egt', '<' => 'lt', '<=' => 'elt', '=' => 'eq', 'same' => 'eq', + ]; + + /** + * 当前验证规则 + * @var array + */ + protected $rule = []; + + /** + * 验证提示信息 + * @var array + */ + protected $message = []; + + /** + * 验证字段描述 + * @var array + */ + protected $field = []; + + /** + * 默认规则提示 + * @var array + */ + protected static $typeMsg = [ + 'require' => ':attribute不能为空', + 'number' => ':attribute必须是数字', + 'integer' => ':attribute必须是整数', + 'float' => ':attribute必须是浮点数', + 'boolean' => ':attribute必须是布尔值', + 'email' => ':attribute格式不符', + 'array' => ':attribute必须是数组', + 'accepted' => ':attribute必须是yes、on或者1', + 'date' => ':attribute格式不符合', + 'file' => ':attribute不是有效的上传文件', + 'image' => ':attribute不是有效的图像文件', + 'alpha' => ':attribute只能是字母', + 'alphaNum' => ':attribute只能是字母和数字', + 'alphaDash' => ':attribute只能是字母、数字和下划线_及破折号-', + 'activeUrl' => ':attribute不是有效的域名或者IP', + 'chs' => ':attribute只能是汉字', + 'chsAlpha' => ':attribute只能是汉字、字母', + 'chsAlphaNum' => ':attribute只能是汉字、字母和数字', + 'chsDash' => ':attribute只能是汉字、字母、数字和下划线_及破折号-', + 'url' => ':attribute不是有效的URL地址', + 'ip' => ':attribute不是有效的IP地址', + 'dateFormat' => ':attribute必须使用日期格式 :rule', + 'in' => ':attribute必须在 :rule 范围内', + 'notIn' => ':attribute不能在 :rule 范围内', + 'between' => ':attribute只能在 :1 - :2 之间', + 'notBetween' => ':attribute不能在 :1 - :2 之间', + 'length' => ':attribute长度不符合要求 :rule', + 'max' => ':attribute长度不能超过 :rule', + 'min' => ':attribute长度不能小于 :rule', + 'after' => ':attribute日期不能小于 :rule', + 'before' => ':attribute日期不能超过 :rule', + 'expire' => '不在有效期内 :rule', + 'allowIp' => '不允许的IP访问', + 'denyIp' => '禁止的IP访问', + 'confirm' => ':attribute和确认字段:2不一致', + 'confirmed' => ':attribute和确认字段:2不一致', + 'different' => ':attribute和比较字段:2不能相同', + 'egt' => ':attribute必须大于等于 :rule', + 'gt' => ':attribute必须大于 :rule', + 'elt' => ':attribute必须小于等于 :rule', + 'lt' => ':attribute必须小于 :rule', + 'eq' => ':attribute必须等于 :rule', + 'regex' => ':attribute不符合指定规则', + 'method' => '无效的请求类型', + 'fileSize' => '上传文件大小不符', + 'fileExt' => '上传文件后缀不符', + 'fileMime' => '上传文件类型不符', + 'unique' => ':attribute 已存在', + ]; + + /** + * 当前验证场景 + * @var array + */ + protected $currentScene = null; + + /** + * 内置正则验证规则 + * @var array + */ + protected $regex = [ + 'alpha' => '/^[A-Za-z]+$/', + 'alphaNum' => '/^[A-Za-z0-9]+$/', + 'alphaDash' => '/^[A-Za-z0-9\-\_]+$/', + 'chs' => '/^[\x{4e00}-\x{9fa5}]+$/u', + 'chsAlpha' => '/^[\x{4e00}-\x{9fa5}a-zA-Z]+$/u', + 'chsAlphaNum' => '/^[\x{4e00}-\x{9fa5}a-zA-Z0-9]+$/u', + 'chsDash' => '/^[\x{4e00}-\x{9fa5}a-zA-Z0-9\_\-]+$/u', + 'mobile' => '/^1[3-9][0-9]\d{8}$/', + 'idCard' => '/(^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$)|(^[1-9]\d{5}\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{2}$)/', + 'zip' => '/\d{6}/', + ]; + + /** + * Filter_var 规则 + * @var array + */ + protected $filter = [ + 'email' => FILTER_VALIDATE_EMAIL, + 'ip' => [FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6], + 'integer' => FILTER_VALIDATE_INT, + 'url' => FILTER_VALIDATE_URL, + 'macAddr' => FILTER_VALIDATE_MAC, + 'float' => FILTER_VALIDATE_FLOAT, + ]; + + /** + * 验证场景定义 + * @var array + */ + protected $scene = []; + + /** + * 验证失败错误信息 + * @var array + */ + protected $error = []; + + /** + * 是否批量验证 + * @var bool + */ + protected $batch = false; + + /** + * 场景需要验证的规则 + * @var array + */ + protected $only = []; + + /** + * 场景需要移除的验证规则 + * @var array + */ + protected $remove = []; + + /** + * 场景需要追加的验证规则 + * @var array + */ + protected $append = []; + + /** + * 架构函数 + * @access public + * @param array $rules 验证规则 + * @param array $message 验证提示信息 + * @param array $field 验证字段描述信息 + */ + public function __construct(array $rules = [], $message = [], $field = []) + { + $this->rule = $rules + $this->rule; + $this->message = array_merge($this->message, $message); + $this->field = array_merge($this->field, $field); + } + + /** + * 创建一个验证器类 + * @access public + * @param array $rules 验证规则 + * @param array $message 验证提示信息 + * @param array $field 验证字段描述信息 + */ + public static function make($rules = [], $message = [], $field = []) + { + return new self($rules, $message, $field); + } + + /** + * 添加字段验证规则 + * @access protected + * @param string|array $name 字段名称或者规则数组 + * @param mixed $rule 验证规则或者字段描述信息 + * @return $this + */ + public function rule($name, $rule = '') + { + if (is_array($name)) { + $this->rule = $name + $this->rule; + if (is_array($rule)) { + $this->field = array_merge($this->field, $rule); + } + } else { + $this->rule[$name] = $rule; + } + + return $this; + } + + /** + * 注册扩展验证(类型)规则 + * @access public + * @param string $type 验证规则类型 + * @param mixed $callback callback方法(或闭包) + * @return void + */ + public static function extend($type, $callback = null) + { + if (is_array($type)) { + self::$type = array_merge(self::$type, $type); + } else { + self::$type[$type] = $callback; + } + } + + /** + * 设置验证规则的默认提示信息 + * @access public + * @param string|array $type 验证规则类型名称或者数组 + * @param string $msg 验证提示信息 + * @return void + */ + public static function setTypeMsg($type, $msg = null) + { + if (is_array($type)) { + self::$typeMsg = array_merge(self::$typeMsg, $type); + } else { + self::$typeMsg[$type] = $msg; + } + } + + /** + * 设置提示信息 + * @access public + * @param string|array $name 字段名称 + * @param string $message 提示信息 + * @return Validate + */ + public function message($name, $message = '') + { + if (is_array($name)) { + $this->message = array_merge($this->message, $name); + } else { + $this->message[$name] = $message; + } + + return $this; + } + + /** + * 设置验证场景 + * @access public + * @param string $name 场景名 + * @return $this + */ + public function scene($name) + { + // 设置当前场景 + $this->currentScene = $name; + + return $this; + } + + /** + * 判断是否存在某个验证场景 + * @access public + * @param string $name 场景名 + * @return bool + */ + public function hasScene($name) + { + return isset($this->scene[$name]) || method_exists($this, 'scene' . $name); + } + + /** + * 设置批量验证 + * @access public + * @param bool $batch 是否批量验证 + * @return $this + */ + public function batch($batch = true) + { + $this->batch = $batch; + + return $this; + } + + /** + * 指定需要验证的字段列表 + * @access public + * @param array $fields 字段名 + * @return $this + */ + public function only($fields) + { + $this->only = $fields; + + return $this; + } + + /** + * 移除某个字段的验证规则 + * @access public + * @param string|array $field 字段名 + * @param mixed $rule 验证规则 true 移除所有规则 + * @return $this + */ + public function remove($field, $rule = true) + { + if (is_array($field)) { + foreach ($field as $key => $rule) { + if (is_int($key)) { + $this->remove($rule); + } else { + $this->remove($key, $rule); + } + } + } else { + if (is_string($rule)) { + $rule = explode('|', $rule); + } + + $this->remove[$field] = $rule; + } + + return $this; + } + + /** + * 追加某个字段的验证规则 + * @access public + * @param string|array $field 字段名 + * @param mixed $rule 验证规则 + * @return $this + */ + public function append($field, $rule = null) + { + if (is_array($field)) { + foreach ($field as $key => $rule) { + $this->append($key, $rule); + } + } else { + if (is_string($rule)) { + $rule = explode('|', $rule); + } + + $this->append[$field] = $rule; + } + + return $this; + } + + /** + * 数据自动验证 + * @access public + * @param array $data 数据 + * @param mixed $rules 验证规则 + * @param string $scene 验证场景 + * @return bool + */ + public function check($data, $rules = [], $scene = '') + { + $this->error = []; + + if (empty($rules)) { + // 读取验证规则 + $rules = $this->rule; + } + + // 获取场景定义 + $this->getScene($scene); + + foreach ($this->append as $key => $rule) { + if (!isset($rules[$key])) { + $rules[$key] = $rule; + } + } + + foreach ($rules as $key => $rule) { + + // field => 'rule1|rule2...' field => ['rule1','rule2',...] + if (is_numeric($key)) { + $key = $rule; + $rule = $this->rule[$key] ?? ''; + } + if (is_array($rule)) { + $rule = array_filter($rule); + } + + if (empty($rule)) { + continue; + } + + // field => 'rule1|rule2...' field => ['rule1','rule2',...] + if (strpos($key, '|')) { + // 字段|描述 用于指定属性名称 + list($key, $title) = explode('|', $key); + } else { + $title = isset($this->field[$key]) ? $this->field[$key] : $key; + } + + // 获取数据 支持二维数组 + $value = $this->getDataValue($data, $key); + switch (true) { + case $rule instanceof \Closure: + $result = call_user_func_array($rule, [$value, $data]); + break; + case $rule instanceof ValidateRule: + $result = $this->checkItem($key, $value, $rule->getRule(), $data, $rule->getTitle() ?: $title, $rule->getMsg()); + break; + case is_array($rule) && is_integer(key($rule)): #判断是否是二维数组验证 + $result = $this->checkItem($key, $value, $rule, $data, $title); + break; + case is_array($rule): + if (!isset($data[$key]) || !is_array($data[$key])) { + $result = '参数' . $key . "必须为二维数组"; + break; + } + $ruleStr = []; + foreach ($rule as $ruleKey => $itemRule) { + $field = str_replace('*.', '', $ruleKey); + $ruleStr[$field] = $itemRule; + } + foreach ($data[$key] as $item) { + if (!is_array($item)) { + $result = $key . "必须为二维数组"; + break; + } + $result = $this->check($item, $ruleStr, $key); + if ($result !== true) return false; + } + break; + default: + $result = $this->checkItem($key, $value, $rule, $data, $title); + break; + } + if (true !== $result) { + // 没有返回true 则表示验证失败 + if (!empty($this->batch)) { + // 批量验证 + if (is_array($result)) { + $this->error = array_merge($this->error, $result); + } else { + $this->error[$key] = $result; + } + } else { + $this->error = $result; + return false; + } + } + } + + return !empty($this->error) ? false : true; + } + + /** + * 根据验证规则验证数据 + * @access public + * @param mixed $value 字段值 + * @param mixed $rules 验证规则 + * @return bool + */ + public function checkRule($value, $rules) + { + if ($rules instanceof \Closure) { + return call_user_func_array($rules, [$value]); + } elseif ($rules instanceof ValidateRule) { + $rules = $rules->getRule(); + } elseif (is_string($rules)) { + $rules = explode('|', $rules); + } + foreach ($rules as $key => $rule) { + if ($rule instanceof \Closure) { + $result = call_user_func_array($rule, [$value]); + } else { + // 判断验证类型 + list($type, $rule) = $this->getValidateType($key, $rule); + + $callback = isset(self::$type[$type]) ? self::$type[$type] : [$this, $type]; + + $result = call_user_func_array($callback, [$value, $rule]); + } + + if (true !== $result) { + return $result; + } + } + + return true; + } + + /** + * 获取当前验证类型及规则 + * @access public + * @param mixed $key + * @param mixed $rule + * @return array + */ + protected function getValidateType($key, $rule) + { + // 判断验证类型 + if (!is_numeric($key)) { + return [$key, $rule, $key]; + } + + if (strpos($rule, ':')) { + list($type, $rule) = explode(':', $rule, 2); + if (isset($this->alias[$type])) { + // 判断别名 + $type = $this->alias[$type]; + } + $info = $type; + } elseif (method_exists($this, $rule)) { + $type = $rule; + $info = $rule; + $rule = ''; + } else { + $type = 'is'; + $info = $rule; + } + + return [$type, $rule, $info]; + } + + /** + * 验证单个字段规则 + * @access protected + * @param string $field 字段名 + * @param mixed $value 字段值 + * @param mixed $rules 验证规则 + * @param array $data 数据 + * @param string $title 字段描述 + * @param array $msg 提示信息 + * @return mixed + */ + protected function checkItem($field, $value, $rules, $data, $title = '', $msg = []) + { + if (isset($this->remove[$field]) && true === $this->remove[$field] && empty($this->append[$field])) { + // 字段已经移除 无需验证 + return true; + } + + // 支持多规则验证 require|in:a,b,c|... 或者 ['require','in'=>'a,b,c',...] + if (is_string($rules)) { + $rules = explode('|', $rules); + } + + if (isset($this->append[$field])) { + // 追加额外的验证规则 + $rules = array_merge($rules, $this->append[$field]); + } + + $i = 0; + foreach ($rules as $key => $rule) { + if ($rule instanceof \Closure) { + $result = call_user_func_array($rule, [$value, $data]); + $info = is_numeric($key) ? '' : $key; + } else { + // 判断验证类型 + list($type, $rule, $info) = $this->getValidateType($key, $rule); + + if (isset($this->remove[$field]) && in_array($info, $this->remove[$field])) { + // 规则已经移除 + $i++; + continue; + } + + if ('must' == $info || 0 === strpos($info, 'require') || (!is_null($value) && '' !== $value)) { + // 验证类型 + $callback = isset(self::$type[$type]) ? self::$type[$type] : [$this, $type]; + // 验证数据 + $result = call_user_func_array($callback, [$value, $rule, $data, $field, $title]); + } else { + $result = true; + } + } + + if (false === $result) { + // 验证失败 返回错误信息 + if (!empty($msg[$i])) { + $message = $msg[$i]; + } else { + $message = $this->getRuleMsg($field, $title, $info, $rule); + } + + return $message; + } elseif (true !== $result) { + // 返回自定义错误信息 + if (is_string($result) && false !== strpos($result, ':')) { + $result = str_replace( + [':attribute', ':rule'], + [$title, (string)$rule], + $result); + } + + return $result; + } + $i++; + } + + return $result; + } + + /** + * 验证是否和某个字段的值一致 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @param array $data 数据 + * @param string $field 字段名 + * @return bool + */ + public function confirm($value, $rule, $data = [], $field = '') + { + if ('' == $rule) { + if (strpos($field, '_confirm')) { + $rule = strstr($field, '_confirm', true); + } else { + $rule = $field . '_confirm'; + } + } + + return $this->getDataValue($data, $rule) === $value; + } + + /** + * 验证是否和某个字段的值一致 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @param array $data 数据 + * @param string $field 字段名 + * @return bool + */ + public function confirmed($value, $rule, array $data = [], string $field = ''): bool + { + if ('' == $rule) { + if (strpos($field, '_confirm') !== false) { + $rule = strstr($field, '_confirm', true); + } else { + $rule = $field . '_confirm'; + } + } + //_confirmation + if ($this->getDataValue($data, $rule) === $value) { + return true; + } + // + if ($this->getDataValue($data, $field . "_confirmation") === $value) { + return true; + } + return false; + } + + /** + * 验证是否和某个字段的值是否不同 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @param array $data 数据 + * @return bool + */ + public function different($value, $rule, $data = []) + { + return $this->getDataValue($data, $rule) != $value; + } + + /** + * 验证是否大于等于某个值 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @param array $data 数据 + * @return bool + */ + public function egt($value, $rule, $data = []) + { + return $value >= $this->getDataValue($data, $rule); + } + + /** + * 验证是否大于某个值 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @param array $data 数据 + * @return bool + */ + public function gt($value, $rule, $data) + { + return $value > $this->getDataValue($data, $rule); + } + + /** + * 验证是否小于等于某个值 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @param array $data 数据 + * @return bool + */ + public function elt($value, $rule, $data = []) + { + return $value <= $this->getDataValue($data, $rule); + } + + /** + * 验证是否小于某个值 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @param array $data 数据 + * @return bool + */ + public function lt($value, $rule, $data = []) + { + return $value < $this->getDataValue($data, $rule); + } + + /** + * 验证是否等于某个值 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @return bool + */ + public function eq($value, $rule) + { + return $value == $rule; + } + + /** + * 必须验证 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @return bool + */ + public function must($value, $rule = null) + { + return !empty($value) || '0' == $value; + } + + /** + * 验证字段值是否为有效格式 + * @access public + * @param mixed $value 字段值 + * @param string $rule 验证规则 + * @param array $data 验证数据 + * @return bool + */ + public function is($value, $rule, $data = []) + { + $rule = preg_replace_callback('/_([a-zA-Z])/', function ($match) { + return strtoupper($match[1]); + }, $rule); + switch (lcfirst($rule)) { + case 'require': + case 'required': + // 必须 + $result = !empty($value) || '0' == $value; + break; + case 'string': + // 接受 + $result = is_string($value); + break; + case 'accepted': + // 接受 + $result = in_array($value, ['1', 'on', 'yes']); + break; + case 'date': + // 是否是一个有效日期 + $result = false !== strtotime($value); + break; + case 'activeUrl': + // 是否为有效的网址 + $result = checkdnsrr($value); + break; + case 'boolean': + case 'bool': + // 是否为布尔值 + $result = in_array($value, [true, false, 0, 1, '0', '1'], true); + break; + case 'number': + $result = is_numeric($value); + break; + case 'array': + // 是否为数组 + $result = is_array($value); + break; + case 'file': + $result = $value instanceof SplFileObject; + break; + case 'image': + $result = $value instanceof SplFileObject && in_array($this->getImageType($value->getRealPath()), [1, 2, 3, 6]); + break; + default: + if (isset(self::$type[$rule])) { + // 注册的验证规则 + $result = call_user_func_array(self::$type[$rule], [$value]); + } elseif (isset($this->filter[$rule])) { + // Filter_var验证规则 + $result = $this->filter($value, $this->filter[$rule]); + } else { + // 正则验证 + $result = $this->regex($value, $rule); + } + } + + return $result; + } + + // 判断图像类型 + protected function getImageType($image) + { + if (function_exists('exif_imagetype')) { + return exif_imagetype($image); + } else { + try { + $info = getimagesize($image); + return $info ? $info[2] : false; + } catch (\Exception $e) { + return false; + } + } + } + + /** + * 验证是否为合格的域名或者IP 支持A,MX,NS,SOA,PTR,CNAME,AAAA,A6, SRV,NAPTR,TXT 或者 ANY类型 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @return bool + */ + public function activeUrl($value, $rule = 'MX') + { + if (!in_array($rule, ['A', 'MX', 'NS', 'SOA', 'PTR', 'CNAME', 'AAAA', 'A6', 'SRV', 'NAPTR', 'TXT', 'ANY'])) { + $rule = 'MX'; + } + + return checkdnsrr($value, $rule); + } + + /** + * 验证是否有效IP + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 ipv4 ipv6 + * @return bool + */ + public function ip($value, $rule = 'ipv4') + { + if (!in_array($rule, ['ipv4', 'ipv6'])) { + $rule = 'ipv4'; + } + + return $this->filter($value, [FILTER_VALIDATE_IP, 'ipv6' == $rule ? FILTER_FLAG_IPV6 : FILTER_FLAG_IPV4]); + } + + /** + * 检测上传文件后缀 + * @param SplFileObject $file 上传文件 + * @param array|string $ext 允许后缀 + * @return bool + */ + protected function checkExt($file, $ext) + { + $extension = strtolower(pathinfo($file->getfilename(), PATHINFO_EXTENSION)); + + if (is_string($ext)) { + $ext = explode(',', $ext); + } + + if (!in_array($extension, $ext)) { + return false; + } + + return true; + } + + /** + * 验证上传文件后缀 + * @access public + * @param SplFileObject $file 上传文件 + * @param mixed $rule 验证规则 + * @return bool + */ + public function fileExt($file, $rule) + { + if (!($file instanceof SplFileObject)) { + return false; + } + + return $this->checkExt($file, $rule); + } + + /** + * 获取文件类型信息 + * @param SplFileObject $file 上传文件 + * @return string + */ + protected function getMime($file) + { + $finfo = finfo_open(FILEINFO_MIME_TYPE); + + return finfo_file($finfo, $file->getRealPath() ?: $file->getPathname()); + } + + /** + * 检测上传文件类型 + * @param SplFileObject $file 上传文件 + * @param array|string $mime 允许类型 + * @return bool + */ + protected function checkMime($file, $mime) + { + if (is_string($mime)) { + $mime = explode(',', $mime); + } + + if (!in_array(strtolower($this->getMime($file)), $mime)) { + return false; + } + + return true; + } + + /** + * 验证上传文件类型 + * @access public + * @param SplFileObject $file 上传文件 + * @param mixed $rule 验证规则 + * @return bool + */ + public function fileMime($file, $rule) + { + if (!($file instanceof SplFileObject)) { + return false; + } + + return $this->checkMime($file, $rule); + } + + /** + * 验证上传文件大小 + * @access public + * @param SplFileObject $file 上传文件 + * @param mixed $rule 验证规则 + * @return bool + */ + public function fileSize($file, $rule) + { + if (!($file instanceof SplFileObject)) { + return false; + } + + return $file->getSize() <= $rule; + } + + /** + * 验证图片的宽高及类型 + * @access public + * @param SplFileObject $file 上传文件 + * @param mixed $rule 验证规则 + * @return bool + */ + public function image($file, $rule) + { + if (!($file instanceof SplFileInfo)) { + return false; + } + + if ($rule) { + $rule = explode(',', $rule); + + list($width, $height, $type) = getimagesize($file->getRealPath()); + + if (isset($rule[2])) { + $imageType = strtolower($rule[2]); + + if ('jpeg' == $imageType) { + $imageType = 'jpg'; + } + + if (image_type_to_extension($type, false) != $imageType) { + return false; + } + } + + list($w, $h) = $rule; + + return $w == $width && $h == $height; + } + return in_array($this->getImageType($file->getRealPath()), [1, 2, 3, 6]); + } + + /** + * 验证请求类型 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @return bool + */ + public function method($value, $rule) + { + return strtoupper($rule) == $_SERVER['REQUEST_METHOD']; + } + + /** + * 验证时间和日期是否符合指定格式 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @return bool + */ + public function dateFormat($value, $rule) + { + $info = date_parse_from_format($rule, $value); + return 0 == $info['warning_count'] && 0 == $info['error_count']; + } + + /** + * 使用filter_var方式验证 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @return bool + */ + public function filter($value, $rule) + { + if (is_string($rule) && strpos($rule, ',')) { + list($rule, $param) = explode(',', $rule); + } elseif (is_array($rule)) { + $param = isset($rule[1]) ? $rule[1] : null; + $rule = $rule[0]; + } else { + $param = null; + } + + return false !== filter_var($value, is_int($rule) ? $rule : filter_id($rule), $param); + } + + /** + * 验证某个字段等于某个值的时候必须 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @param array $data 数据 + * @return bool + */ + public function requireIf($value, $rule, $data) + { + list($field, $val) = explode(',', $rule); + + if ($this->getDataValue($data, $field) == $val) { + return !empty($value) || '0' == $value; + } else { + return true; + } + } + + /** + * 通过回调方法验证某个字段是否必须 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @param array $data 数据 + * @return bool + */ + public function requireCallback($value, $rule, $data) + { + $result = call_user_func_array($rule, [$value, $data]); + + if ($result) { + return !empty($value) || '0' == $value; + } else { + return true; + } + } + + /** + * 验证某个字段有值的情况下必须 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @param array $data 数据 + * @return bool + */ + public function requireWith($value, $rule, $data) + { + $val = $this->getDataValue($data, $rule); + + if (!empty($val)) { + return !empty($value) || '0' == $value; + } else { + return true; + } + } + + /** + * 验证是否在范围内 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @return bool + */ + public function in($value, $rule) + { + return in_array($value, is_array($rule) ? $rule : explode(',', $rule)); + } + + /** + * 验证是否不在某个范围 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @return bool + */ + public function notIn($value, $rule) + { + return !in_array($value, is_array($rule) ? $rule : explode(',', $rule)); + } + + /** + * between验证数据 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @return bool + */ + public function between($value, $rule) + { + if (is_string($rule)) { + $rule = explode(',', $rule); + } + list($min, $max) = $rule; + + return $value >= $min && $value <= $max; + } + + /** + * 使用notbetween验证数据 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @return bool + */ + public function notBetween($value, $rule) + { + if (is_string($rule)) { + $rule = explode(',', $rule); + } + list($min, $max) = $rule; + + return $value < $min || $value > $max; + } + + /** + * 验证数据长度 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @return bool + */ + public function length($value, $rule) + { + if (is_array($value)) { + $length = count($value); + } elseif ($value instanceof File) { + $length = $value->getSize(); + } else { + $length = mb_strlen((string)$value); + } + + if (strpos($rule, ',')) { + // 长度区间 + list($min, $max) = explode(',', $rule); + return $length >= $min && $length <= $max; + } else { + // 指定长度 + return $length == $rule; + } + } + + /** + * 验证数据最大长度 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @return bool + */ + public function max($value, $rule) + { + if (is_array($value)) { + $length = count($value); + } elseif ($value instanceof File) { + $length = $value->getSize(); + } else { + $length = mb_strlen((string)$value); + } + + return $length <= $rule; + } + + /** + * 验证数据最小长度 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @return bool + */ + public function min($value, $rule) + { + if (is_array($value)) { + $length = count($value); + } elseif ($value instanceof File) { + $length = $value->getSize(); + } else { + $length = mb_strlen((string)$value); + } + + return $length >= $rule; + } + + /** + * 验证日期 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @return bool + */ + public function after($value, $rule) + { + return strtotime($value) >= strtotime($rule); + } + + /** + * 验证日期 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @return bool + */ + public function before($value, $rule) + { + return strtotime($value) <= strtotime($rule); + } + + /** + * 验证有效期 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @return bool + */ + public function expire($value, $rule) + { + if (is_string($rule)) { + $rule = explode(',', $rule); + } + + list($start, $end) = $rule; + + if (!is_numeric($start)) { + $start = strtotime($start); + } + + if (!is_numeric($end)) { + $end = strtotime($end); + } + + return $_SERVER['REQUEST_TIME'] >= $start && $_SERVER['REQUEST_TIME'] <= $end; + } + + /** + * 验证IP许可 + * @access public + * @param string $value 字段值 + * @param mixed $rule 验证规则 + * @return mixed + */ + public function allowIp($value, $rule) + { + return in_array($value, is_array($rule) ? $rule : explode(',', $rule)); + } + + /** + * 验证IP禁用 + * @access public + * @param string $value 字段值 + * @param mixed $rule 验证规则 + * @return mixed + */ + public function denyIp($value, $rule) + { + return !in_array($value, is_array($rule) ? $rule : explode(',', $rule)); + } + + /** + * 验证是否唯一 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 格式:数据表,字段名,排除ID,主键名 + * @param array $data 数据 + * @param string $field 验证字段名 + * @return bool + */ + public function unique($value, $rule, array $data = [], string $field = ''): bool + { + if (is_string($rule)) { + $rule = explode(',', $rule); + } + if (false !== strpos($rule[0], '\\')) { + // 指定模型类 + $db = new $rule[0]; + } else { + $db = Db::table($rule[0]); + } + $key = $rule[1] ?? $field; + $map = []; + + if (strpos($key, '^')) { + // 支持多个字段验证 + $fields = explode('^', $key); + foreach ($fields as $key) { + if (isset($data[$key])) { + $map[] = [$key, '=', $data[$key]]; + } + } + } elseif (isset($data[$field])) { + $map[] = [$key, '=', $data[$field]]; + } else { + $map = []; + } + + $pk = !empty($rule[3]) ? $rule[3] : 'id'; + + if (isset($data[$pk])) { + $map[] = [$pk, '<>', $data[$pk]]; + } elseif (isset($rule[2]) && $rule[2] != '') { + $map[] = [$pk, '<>', $rule[2]]; + } + if ($db->where($map)->count()) { + return false; + } + return true; + } + + + /** + * 使用正则验证数据 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 正则规则或者预定义正则名 + * @return mixed + */ + public function regex($value, $rule) + { + if (isset($this->regex[$rule])) { + $rule = $this->regex[$rule]; + } + + if (0 !== strpos($rule, '/') && !preg_match('/\/[imsU]{0,4}$/', $rule)) { + // 不是正则表达式则两端补上/ + $rule = '/^' . $rule . '$/'; + } + + return is_scalar($value) && 1 === preg_match($rule, (string)$value); + } + + // 获取错误信息 + public function getError() + { + return $this->error; + } + + /** + * 获取数据值 + * @access protected + * @param array $data 数据 + * @param string $key 数据标识 支持二维 + * @return mixed + */ + protected function getDataValue($data, $key) + { + if (is_numeric($key)) { + $value = $key; + } elseif (strpos($key, '.')) { + // 支持二维数组验证 + list($name1, $name2) = explode('.', $key); + $value = isset($data[$name1][$name2]) ? $data[$name1][$name2] : null; + } else { + $value = isset($data[$key]) ? $data[$key] : null; + } + + return $value; + } + + /** + * 获取验证规则的错误提示信息 + * @access protected + * @param string $attribute 字段英文名 + * @param string $title 字段描述名 + * @param string $type 验证规则名称 + * @param mixed $rule 验证规则数据 + * @return string + */ + protected function getRuleMsg($attribute, $title, $type, $rule) + { + if (isset($this->message[$attribute . '.' . $type])) { + $msg = $this->message[$attribute . '.' . $type]; + } elseif (isset($this->message[$attribute][$type])) { + $msg = $this->message[$attribute][$type]; + } elseif (isset($this->message[$attribute])) { + $msg = $this->message[$attribute]; + } elseif (isset(self::$typeMsg[$type])) { + $msg = self::$typeMsg[$type]; + } elseif (0 === strpos($type, 'require')) { + $msg = self::$typeMsg['require']; + } else { + $msg = $title . '规则不符'; + } + + if (is_string($msg) && is_scalar($rule) && false !== strpos($msg, ':')) { + // 变量替换 + if (is_string($rule) && strpos($rule, ',')) { + $array = array_pad(explode(',', $rule), 3, ''); + } else { + $array = array_pad([], 3, ''); + } + $msg = str_replace( + [':attribute', ':rule', ':1', ':2', ':3'], + [$title, (string)$rule, $array[0], $array[1], $array[2]], + $msg); + } + + return $msg; + } + + /** + * 获取数据验证的场景 + * @access protected + * @param string $scene 验证场景 + * @return array + */ + protected function getScene($scene = '') + { + if (empty($scene)) { + // 读取指定场景 + $scene = $this->currentScene; + } + + $this->only = $this->append = $this->remove = []; + + if (empty($scene)) { + return; + } + + if (method_exists($this, 'scene' . $scene)) { + call_user_func([$this, 'scene' . $scene]); + } elseif (isset($this->scene[$scene])) { + // 如果设置了验证适用场景 + $scene = $this->scene[$scene]; + + if (is_string($scene)) { + $scene = explode(',', $scene); + } + + $this->only = $scene; + } + } + + /** + * 动态方法 直接调用is方法进行验证 + * @access protected + * @param string $method 方法名 + * @param array $args 调用参数 + * @return bool + */ + public function __call($method, $args) + { + if ('is' == strtolower(substr($method, 0, 2))) { + $method = substr($method, 2); + } + + array_push($args, lcfirst($method)); + + return call_user_func_array([$this, 'is'], $args); + } + + public function getSceneRule(string $name) + { + return $this->scene[$name] ?? $this->rule; + } + +} \ No newline at end of file diff --git a/builder/Validate/ValidateRule.php b/builder/Validate/ValidateRule.php new file mode 100644 index 0000000..d263427 --- /dev/null +++ b/builder/Validate/ValidateRule.php @@ -0,0 +1,158 @@ +rule[$name] = $rule; + } else { + $this->rule[] = $name; + } + + $this->message[] = $msg; + + return $this; + } + + /** + * 获取验证规则 + * @access public + * @return array + */ + public function getRule() + { + return $this->rule; + } + + /** + * 获取验证字段名称 + * @access public + * @return string + */ + public function getTitle() + { + return $this->title; + } + + /** + * 获取验证提示 + * @access public + * @return array + */ + public function getMsg() + { + return $this->message; + } + + /** + * 设置验证字段名称 + * @access public + * @return $this + */ + public function title($title) + { + $this->title = $title; + + return $this; + } + + public function __call($method, $args) + { + if ('is' == strtolower(substr($method, 0, 2))) { + $method = substr($method, 2); + } + + array_unshift($args, lcfirst($method)); + + return call_user_func_array([$this, 'addItem'], $args); + } + + public static function __callStatic($method, $args) + { + $rule = new static(); + if ('is' == strtolower(substr($method, 0, 2))) { + $method = substr($method, 2); + } + array_unshift($args, lcfirst($method)); + return call_user_func_array([$rule, 'addItem'], $args); + } +} \ No newline at end of file diff --git a/builder/View.php b/builder/View.php index 0ecc7f7..bcfdc27 100644 --- a/builder/View.php +++ b/builder/View.php @@ -17,6 +17,48 @@ use Builder\Entity\UISettingEntity; use Hyperf\HttpServer\Response; class View extends MineResponse { + + public static $metaTitle; + + public static function response($data, $message = '', $code = 200, $headers = []) + { + $re_data = [ + 'code' => $code, + 'message' => $message, + ]; + if ($data) { + $re_data['data'] = $data; + } + $response = new Response(); + return $response->withBody(new SwooleStream(json_encode($re_data, 256))); + } + + public static function responseMessage($message = '', $code = 200) + { + return self::response([], $message, $code); + } + + + public static function responseError($message = '', $code = 400) + { + return self::response([], $message, $code); + } + + /** + * @param $url + * @param bool $isVueRoute + * @param string $message + * @param string $type info/success/warning/error + */ + public static function responseRedirect($url, $isVueRoute = true, $message = null, $type = 'success') + { + return self::response([ + 'url' => $url, + 'isVueRoute' => $isVueRoute, + 'type' => $type, + ], $message, 301); + } + /*** * @param UISettingEntity $setting * @param null $content diff --git a/builder/View/Actions/BaseAction.php b/builder/View/Actions/BaseAction.php new file mode 100644 index 0000000..3ff780b --- /dev/null +++ b/builder/View/Actions/BaseAction.php @@ -0,0 +1,125 @@ +resource = admin_api_url(request()->path()); + } + + /** + * 设置request模式请求类型 + * @param string $requestMethod + * @return $this + */ + public function requestMethod(string $requestMethod) + { + $this->requestMethod = $requestMethod; + return $this; + } + + /** + * 确认操作提示信息 + * @param mixed $message + * @return $this + */ + public function message($message) + { + $this->message = $message; + return $this; + } + + /** + * 按钮气泡信息 + * @param mixed $tooltip + * @return $this + */ + public function tooltip($tooltip) + { + $this->tooltip = $tooltip; + return $this; + } + + /** + * 请求前出发事件 + * @param string $beforeEmit + * @param mixed $data + * @return $this + */ + public function beforeEmit(string $beforeEmit, $data=null) + { + $this->beforeEmit = collect($this->beforeEmit)->push(["eventName" => $beforeEmit, "eventData" => $data]); + return $this; + } + + /** + * 操作成功后触发事件 + * @param string $successEmit + * @param mixed $data + * @return $this + */ + public function successEmit(string $successEmit, $data=null) + { + $this->successEmit = collect($this->successEmit)->push(["eventName" => $successEmit, "eventData" => $data]); + return $this; + } + + /** + * 操作完成后触发事件,失败成功都会触发 + * @param string $afterEmit + * @param mixed $data + * @return $this + */ + public function afterEmit(string $afterEmit, $data=null) + { + $this->afterEmit = collect($this->afterEmit)->push(["eventName" => $afterEmit, "eventData" => $data]); + return $this; + } + + /** + * @return string + */ + public function getResource() + { + return $this->resource; + } + + public function jsonSerialize() + { + $data = []; + $hide = collect($this->hideAttrs)->push("hideAttrs")->toArray(); + foreach ($this as $key => $val) { + if (!in_array($key, $hide)) { + $data[$key] = $val; + } + } + return $data; + } +} diff --git a/builder/View/Actions/BaseRowAction.php b/builder/View/Actions/BaseRowAction.php new file mode 100644 index 0000000..b2e2cc9 --- /dev/null +++ b/builder/View/Actions/BaseRowAction.php @@ -0,0 +1,31 @@ +order = $order; + return $this; + } + + /** + * 设置操作vif属性算法 + * @param array $vif + * @return $this + */ + public function vif($vif) + { + $this->vif = $vif; + return $this; + } +} diff --git a/builder/View/Components/Antv/Area.php b/builder/View/Components/Antv/Area.php new file mode 100644 index 0000000..819da36 --- /dev/null +++ b/builder/View/Components/Antv/Area.php @@ -0,0 +1,14 @@ +canvasId = \Hyperf\Utils\Str::random(); + } + + public static function make() + { + return new Funnel(); + } + + /** + * 设置数据 + * @param mixed $data + * @return $this + */ + public function data($data) + { + if ($data instanceof \Closure) { + $this->data = call_user_func($data); + } else { + $this->data = $data; + } + + return $this; + } + + /** + * 设置配置信息 + * @param mixed $config + * @return $this + */ + public function config($config) + { + if ($config instanceof \Closure) { + $this->config = call_user_func($config); + } else { + $this->config = $config; + } + return $this; + } + + public function class(string $class) + { + $this->class = $class; + return $this; + } +} diff --git a/builder/View/Components/Antv/Line.php b/builder/View/Components/Antv/Line.php new file mode 100644 index 0000000..012cf93 --- /dev/null +++ b/builder/View/Components/Antv/Line.php @@ -0,0 +1,59 @@ +canvasId = Str::random(); + } + + + public static function make() + { + return new Line(); + } + + /** + * 设置数据 + * @param mixed $data + * @return $this + */ + public function data($data) + { + if ($data instanceof \Closure) { + $this->data = call_user_func($data); + } else { + $this->data = $data; + } + + + return $this; + } + + /** + * 设置配置信息 + * @param mixed $config + * @return $this + */ + public function config($config) + { + if ($config instanceof \Closure) { + $this->config = call_user_func($config); + } else { + $this->config = $config; + } + + return $this; + } + + +} diff --git a/builder/View/Components/Antv/Pie.php b/builder/View/Components/Antv/Pie.php new file mode 100644 index 0000000..7726deb --- /dev/null +++ b/builder/View/Components/Antv/Pie.php @@ -0,0 +1,51 @@ +canvasId = \Hyperf\Utils\Str::random(); + } + + public static function make() + { + return new Pie(); + } + + /** + * 设置数据 + * @param mixed $data + * @return $this + */ + public function data($data) + { + if ($data instanceof \Closure) { + $this->data = call_user_func($data); + } else { + $this->data = $data; + } + return $this; + } + + /** + * 设置配置信息 + * @param mixed $config + * @return $this + */ + public function config($config) + { + if ($config instanceof \Closure) { + $this->config = call_user_func($config); + } else { + $this->config = $config; + } + return $this; + } +} diff --git a/builder/View/Components/Antv/StepLine.php b/builder/View/Components/Antv/StepLine.php new file mode 100644 index 0000000..dc3b364 --- /dev/null +++ b/builder/View/Components/Antv/StepLine.php @@ -0,0 +1,16 @@ +content = $content; + return $this; + } + /** + * @param mixed $size + * @return $this + */ + public function size($size) + { + $this->size = $size; + return $this; + } + /** + * @param mixed $type + * @return $this + */ + public function type($type) + { + $this->type = $type; + return $this; + } + /** + * @param bool $plain + * @return $this + */ + public function plain(bool $plain = true) + { + $this->plain = $plain; + return $this; + } + + /** + * @param bool $round + * @return $this + */ + public function round(bool $round = true) + { + $this->round = $round; + return $this; + } + + /** + * @param bool $circle + * @return $this + */ + public function circle(bool $circle = true) + { + $this->circle = $circle; + return $this; + } + /** + * @param bool $disabled + * @return $this + */ + public function disabled(bool $disabled = true) + { + $this->disabled = $disabled; + return $this; + } + /** + * @param mixed $icon + * @return $this + */ + public function icon($icon) + { + $this->icon = $icon; + return $this; + } + /** + * @param bool $autofocus + * @return $this + */ + public function autofocus(bool $autofocus = true) + { + $this->autofocus = $autofocus; + return $this; + } + +} diff --git a/builder/View/Components/Attrs/CascaderOption.php b/builder/View/Components/Attrs/CascaderOption.php new file mode 100644 index 0000000..6e82c6f --- /dev/null +++ b/builder/View/Components/Attrs/CascaderOption.php @@ -0,0 +1,49 @@ +type = "default"; + $op->value = $value; + $op->label = $label; + return $op; + } + + /** + * @param CascaderOption[] $children + * @return $this + */ + public function children($children) + { + $this->children = $children; + return $this; + } + + /** + * @param bool $leaf + * @return $this + */ + public function leaf($leaf=true) + { + $this->leaf = $leaf; + return $this; + } + + public function jsonSerialize() + { + $data = []; + foreach ($this as $key => $val) { + if (!empty($val)) $data[$key] = $val; + } + return $data; + } + +} diff --git a/builder/View/Components/Attrs/CascaderProps.php b/builder/View/Components/Attrs/CascaderProps.php new file mode 100644 index 0000000..a2e7ae1 --- /dev/null +++ b/builder/View/Components/Attrs/CascaderProps.php @@ -0,0 +1,16 @@ +depend = $depend; + return $this; + } + + /** + * @param mixed $paginate + * @return $this + */ + public function paginate($paginate) + { + $this->paginate = $paginate; + return $this; + } + /** + * @param mixed $extUrlParams + * @return $this + */ + public function extUrlParams($extUrlParams) + { + $this->extUrlParams = $extUrlParams; + return $this; + } + /** + * 远程加载时的默认显示名称,因远程时一般仅有value + * @param object form 所属表单 + * @param array $label ['key'=>'model','value'=>['value'=>'id','label'=>['title','label']]] // 所属关联模型 用于从data取值 + * @return $this + */ + public function label(&$form, $label) + { + $this->label = $label; + $form->item($label['key'])->vif('label' . mt_rand(10000, 99999), false); + return $this; + } +} diff --git a/builder/View/Components/Attrs/ElDialog.php b/builder/View/Components/Attrs/ElDialog.php new file mode 100644 index 0000000..3216b9c --- /dev/null +++ b/builder/View/Components/Attrs/ElDialog.php @@ -0,0 +1,153 @@ +url = $url; + } + + protected $slot; + /** + * @param mixed $title + * @return $this + */ + public function title($title) + { + $this->title = $title; + return $this; + } + + /** + * @param string $width + * @return $this + */ + public function width(string $width) + { + $this->width = $width; + return $this; + } + + /** + * @param bool $fullscreen + * @return $this + */ + public function fullscreen(bool $fullscreen) + { + $this->fullscreen = $fullscreen; + return $this; + } + + /** + * @param string $top + * @return $this + */ + public function top(string $top) + { + $this->top = $top; + return $this; + } + + /** + * @param bool $modal + * @return $this + */ + public function modal(bool $modal) + { + $this->modal = $modal; + return $this; + } + + /** + * @param bool $lockScroll + * @return $this + */ + public function lockScroll(bool $lockScroll) + { + $this->lockScroll = $lockScroll; + return $this; + } + + /** + * @param mixed $customClass + * @return $this + */ + public function customClass($customClass) + { + $this->customClass = $customClass; + return $this; + } + + /** + * @param bool $closeOnClickModal + * @return $this + */ + public function closeOnClickModal(bool $closeOnClickModal) + { + $this->closeOnClickModal = $closeOnClickModal; + return $this; + } + + /** + * @param bool $closeOnPressEscape + * @return $this + */ + public function closeOnPressEscape(bool $closeOnPressEscape) + { + $this->closeOnPressEscape = $closeOnPressEscape; + return $this; + } + /** + * @param bool $showClose + * @return $this + */ + public function showClose(bool $showClose) + { + $this->showClose = $showClose; + return $this; + } + /** + * @param bool $center + * @return $this + */ + public function center(bool $center) + { + $this->center = $center; + return $this; + } + /** + * @param bool $destroyOnClose + * @return $this + */ + public function destroyOnClose(bool $destroyOnClose) + { + $this->destroyOnClose = $destroyOnClose; + return $this; + } + public function slot(\Closure $closure) + { + $this->slot = Content::make(); + call_user_func($closure, $this->slot); + } +} diff --git a/builder/View/Components/Attrs/ElLink.php b/builder/View/Components/Attrs/ElLink.php new file mode 100644 index 0000000..58e7bf2 --- /dev/null +++ b/builder/View/Components/Attrs/ElLink.php @@ -0,0 +1,49 @@ +type = $type; + return $this; + } + + /** + * @param bool $underline + * @return $this + */ + public function underline(bool $underline = true) + { + $this->underline = $underline; + return $this; + } + + /** + * @param bool $disabled + * @return $this + */ + public function disabled(bool $disabled = true) + { + $this->disabled = $disabled; + return $this; + } + /** + * @param mixed $icon + * @return $this + */ + public function icon($icon) + { + $this->icon = $icon; + return $this; + } +} diff --git a/builder/View/Components/Attrs/SelectOption.php b/builder/View/Components/Attrs/SelectOption.php new file mode 100644 index 0000000..173992d --- /dev/null +++ b/builder/View/Components/Attrs/SelectOption.php @@ -0,0 +1,52 @@ +type = "default"; + $op->value = $value; + $op->label = $label; + return $op; + } + + /** + * 是否禁用该选项 + * @param bool $disabled + * @return $this + */ + public function disabled($disabled = true) + { + $this->disabled = $disabled; + return $this; + } + + /** + * @param string $avatar + * @return $this + */ + public function avatar($avatar) + { + $this->avatar = $avatar; + return $this; + } + + /** + * @param mixed $desc + * @return $this + */ + public function desc($desc) + { + $this->desc = $desc; + return $this; + } +} diff --git a/builder/View/Components/Attrs/Step.php b/builder/View/Components/Attrs/Step.php new file mode 100644 index 0000000..8a12743 --- /dev/null +++ b/builder/View/Components/Attrs/Step.php @@ -0,0 +1,62 @@ +title = $title; + return $this; + } + + /** + * 描述性文字 + * @param string $description + * @return $this + */ + public function description($description) + { + $this->description = $description; + return $this; + } + + /** + * 图标 + * @param string $icon + * @return $this + */ + public function icon($icon) + { + $this->icon = $icon; + return $this; + } + + /** + * 设置当前步骤的状态,不设置则根据 steps 确定状态 + * @param mixed $status + * @return $this + */ + public function status($status) + { + $this->status = $status; + return $this; + } + + +} diff --git a/builder/View/Components/Attrs/TransferData.php b/builder/View/Components/Attrs/TransferData.php new file mode 100644 index 0000000..0e7fe2d --- /dev/null +++ b/builder/View/Components/Attrs/TransferData.php @@ -0,0 +1,37 @@ +key = $key; + $this->label = $label; + $this->disabled = $disabled; + } + /** + * @inheritDoc + */ + public function jsonSerialize() + { + $data = []; + foreach ($this as $key => $val) { + $data[$key] = $val; + } + return $data; + } +} diff --git a/builder/View/Components/Component.php b/builder/View/Components/Component.php new file mode 100644 index 0000000..93276db --- /dev/null +++ b/builder/View/Components/Component.php @@ -0,0 +1,106 @@ +componentValue($value); + } + + /** + * class + * @param mixed $className + * @return $this + */ + public function className($className) + { + $this->className = $className; + return $this; + } + + /** + * style + * @param mixed $style + * @return $this + */ + public function style($style) + { + $this->style = $style; + return $this; + } + + /** + * @param $value + * @return $this + */ + public function componentValue($value) + { + $this->componentValue = $value; + return $this; + } + + /** + * @return mixed + */ + public function getComponentValue() + { + return $this->componentValue; + } + + /** + * @return mixed + */ + public function getRef() + { + return $this->ref; + } + + /** + * 注册ref + * @param string $ref + * @return $this + */ + public function ref(string $ref) + { + $this->ref = $ref; + return $this; + } + + /** + * ref动态注入 + * @param string $ref 选择已注册的ref组件 + * @param string|\Closure $refData 目标组件注入的js代码 + * @param string $event 当前组件触发什么事件进行注入 click + * @return $this + */ + public function refData(string $ref, $refData, string $event = "click") + { + if ($refData instanceof \Closure) { + $data = call_user_func($refData); + } else { + $data = $refData; + } + $this->refData = [ + 'ref' => $ref, + "data" => $data + ]; + return $this; + } +} diff --git a/builder/View/Components/Form/CSwitch.php b/builder/View/Components/Form/CSwitch.php new file mode 100644 index 0000000..59aa55b --- /dev/null +++ b/builder/View/Components/Form/CSwitch.php @@ -0,0 +1,165 @@ +activeValue); + } + + /** + * 是否禁用 + * @param bool $disabled + * @return $this + */ + public function disabled($disabled) + { + $this->disabled = $disabled; + return $this; + } + + /** + * switch 的宽度(像素) + * @param int $width + * @return $this + */ + public function width(int $width) + { + $this->width = $width; + return $this; + } + + /** + * switch 打开时所显示图标的类名,设置此项会忽略 active-text + * @param string $activeIconClass + * @return $this + */ + public function activeIconClass($activeIconClass) + { + $this->activeIconClass = $activeIconClass; + return $this; + } + + /** + * switch 关闭时所显示图标的类名,设置此项会忽略 inactive-text + * @param string $inactiveIconClass + * @return $this + */ + public function inactiveIconClass($inactiveIconClass) + { + $this->inactiveIconClass = $inactiveIconClass; + return $this; + } + + /** + * switch 打开时的文字描述 + * @param string $activeText + * @return $this + */ + public function activeText($activeText) + { + $this->activeText = $activeText; + return $this; + } + + /** + * switch 关闭时的文字描述 + * @param string $inactiveText + * @return $this + */ + public function inactiveText($inactiveText) + { + $this->inactiveText = $inactiveText; + return $this; + } + + /** + * switch 打开时的值 + * @param bool $activeValue + * @return $this + */ + public function activeValue($activeValue) + { + $this->activeValue = $activeValue; + return $this; + } + + /** + * switch 关闭时的值 + * @param bool $inactiveValue + * @return $this + */ + public function inactiveValue($inactiveValue) + { + $this->inactiveValue = $inactiveValue; + return $this; + } + + /** + * switch 打开时的背景色 + * @param string $activeColor + * @return $this + */ + public function activeColor($activeColor) + { + $this->activeColor = $activeColor; + return $this; + } + + /** + * switch 关闭时的背景色 + * @param string $inactiveColor + * @return $this + */ + public function inactiveColor($inactiveColor) + { + $this->inactiveColor = $inactiveColor; + return $this; + } + + /** + * switch 对应的 name 属性 + * @param string $name + * @return $this + */ + public function name($name) + { + $this->name = $name; + return $this; + } + + /** + * 改变 switch 状态时是否触发表单的校验 + * @param bool $validateEvent + * @return $this + */ + public function validateEvent($validateEvent = true) + { + $this->validateEvent = $validateEvent; + return $this; + } + + +} diff --git a/builder/View/Components/Form/Cascader.php b/builder/View/Components/Form/Cascader.php new file mode 100644 index 0000000..109ce28 --- /dev/null +++ b/builder/View/Components/Form/Cascader.php @@ -0,0 +1,276 @@ +props = new CascaderProps(); + return $cascader; + } + + /** + * 显示为面板模式 + * @param bool $panel + * @return $this + */ + public function panel($panel = true) + { + $this->panel = $panel; + return $this; + } + + + /** + * 尺寸 + * @param string $size + * @return $this + */ + public function size($size) + { + $this->size = $size; + return $this; + } + + /** + * 输入框占位文本 + * @param string $placeholder + * @return $this + */ + public function placeholder($placeholder) + { + $this->placeholder = $placeholder; + return $this; + } + + /** + * 是否禁用 + * @param bool $disabled + * @return $this + */ + public function disabled($disabled = true) + { + $this->disabled = $disabled; + return $this; + } + + /** + * 是否支持清空选项 + * @param bool $clearable + * @return $this + */ + public function clearable($clearable = true) + { + $this->clearable = $clearable; + return $this; + } + + /** + * 输入框中是否显示选中值的完整路径 + * @param bool $showAllLevels + * @return $this + */ + public function showAllLevels($showAllLevels = true) + { + $this->showAllLevels = $showAllLevels; + return $this; + } + + /** + * 多选模式下是否折叠Tag + * @param bool $collapseTags + * @return $this + */ + public function collapseTags($collapseTags = true) + { + $this->collapseTags = $collapseTags; + return $this; + } + + /** + * 选项分隔符 + * @param string $separator + * @return $this + */ + public function separator($separator) + { + $this->separator = $separator; + return $this; + } + + /** + * 是否可搜索选项 + * @param bool $filterable + * @return $this + */ + public function filterable($filterable = true) + { + $this->filterable = $filterable; + return $this; + } + + /** + * debounce + * @param int $debounce + * @return $this + */ + public function debounce($debounce) + { + $this->debounce = $debounce; + return $this; + } + + /** + * 自定义浮层类名 + * @param mixed $popperClass + * @return $this + */ + public function popperClass($popperClass) + { + $this->popperClass = $popperClass; + return $this; + } + + /** + * 次级菜单的展开方式 + * click / hover + * @param string $expandTrigger + * @return $this + */ + public function expandTrigger($expandTrigger) + { + $this->props->expandTrigger = $expandTrigger; + return $this; + } + + /** + * 是否多选 + * @param bool $multiple + * @return $this + */ + public function multiple($multiple = true) + { + $this->props->multiple = $multiple; + return $this; + } + + /** + * 是否严格的遵守父子节点不互相关联 + * @param bool $checkStrictly + * @return $this + */ + public function checkStrictly($checkStrictly = true) + { + $this->props->checkStrictly = $checkStrictly; + return $this; + } + + /** + * 在选中节点改变时,是否返回由该节点所在的各级菜单的值所组成的数组,若设置 false,则只返回该节点的值 + * @param bool $emitPath + * @return $this + */ + public function emitPath($emitPath = true) + { + $this->props->emitPath = $emitPath; + return $this; + } + + /** + * 是否动态加载子节点,需与 lazyLoad 方法结合使用 + * @param bool $layz + * @param string $lazyUrl + * @return $this + */ + public function lazy($layz = true, $lazyUrl = '') + { + $this->props->lazy = $layz; + $this->props->lazyUrl = $lazyUrl; + return $this; + } + + /** + * 指定选项的值为选项对象的某个属性值 + * @param string $value + * @return $this + */ + public function value($value) + { + $this->props->value = $value; + return $this; + } + + /** + * 指定选项标签为选项对象的某个属性值 + * @param string $label + * @return $this + */ + public function label($label) + { + $this->props->label = $label; + return $this; + } + + /** + * 指定选项的子选项为选项对象的某个属性值 + * @param string $children + * @return $this + */ + public function children($children) + { + $this->props->children = $children; + return $this; + } + + /** + * 指定选项的叶子节点的标志位为选项对象的某个属性值 + * @param string $leaf + * @return $this + */ + public function leaf($leaf) + { + $this->props->leaf = $leaf; + return $this; + } + + /** + * 设置选项值数组 + * @param array $options + * @return $this + */ + public function options($options) + { + $this->options = $options; + return $this; + } + + +} diff --git a/builder/View/Components/Form/Checkbox.php b/builder/View/Components/Form/Checkbox.php new file mode 100644 index 0000000..bbe7e7a --- /dev/null +++ b/builder/View/Components/Form/Checkbox.php @@ -0,0 +1,138 @@ +label($label)->title($title); + } + + /** + * 选中状态的值(只有在checkbox-group或者绑定对象类型为array时有效) + * @param string|int|bool $label + * @return $this + */ + public function label($label) + { + $this->label = $label; + return $this; + } + + /** + * 选中时的值 + * @param string|int $trueLabel + * @return $this + */ + public function trueLabel($trueLabel) + { + $this->trueLabel = $trueLabel; + return $this; + } + + /** + * 没有选中时的值 + * @param string|int $falseLabel + * @return $this + */ + public function falseLabel($falseLabel) + { + $this->falseLabel = $falseLabel; + return $this; + } + + /** + * 是否禁用 + * @param bool $disabled + * @return $this + */ + public function disabled(bool $disabled = true) + { + $this->disabled = $disabled; + return $this; + } + + /** + * 是否显示边框 + * @param bool $border + * @return $this + */ + public function border(bool $border = true) + { + $this->border = $border; + return $this; + } + + /** + * Checkbox 的尺寸,仅在 border 为真时有效 + * @param string $size + * @return $this + */ + public function size($size) + { + $this->size = $size; + return $this; + } + + /** + * 原生 name 属性 + * @param string $name + * @return $this + */ + public function name($name) + { + $this->name = $name; + return $this; + } + + /** + * 当前是否勾选 + * @param bool $checked + * @return $this + */ + public function checked(bool $checked = true) + { + $this->checked = $checked; + return $this; + } + + /** + * 设置 indeterminate 状态,只负责样式控制 + * @param bool $indeterminate + * @return $this + */ + public function indeterminate(bool $indeterminate = true) + { + $this->indeterminate = $indeterminate; + return $this; + } + + public function title(string $title) + { + $this->title = $title; + return $this; + } + +} diff --git a/builder/View/Components/Form/CheckboxGroup.php b/builder/View/Components/Form/CheckboxGroup.php new file mode 100644 index 0000000..2bbbd6c --- /dev/null +++ b/builder/View/Components/Form/CheckboxGroup.php @@ -0,0 +1,113 @@ +options($options); + } + + /** + * 多选框组尺寸,仅对按钮形式的 Checkbox 或带有边框的 Checkbox 有效 + * @param string $size + * @return $this + */ + public function size(string $size) + { + $this->size = $size; + return $this; + } + + /** + * 是否禁用 + * @param bool $disabled + * @return $this + */ + public function disabled(bool $disabled) + { + $this->disabled = $disabled; + return $this; + } + + /** + * 可被勾选的 checkbox 的最小数量 + * @param int $min + * @return $this + */ + public function min(int $min) + { + $this->min = $min; + return $this; + } + + /** + * 可被勾选的 checkbox 的最大数量 + * @param int $max + * @return $this + */ + public function max(int $max) + { + $this->max = $max; + return $this; + } + + /** + * 按钮形式的 Checkbox 激活时的文本颜色 + * @param string $textColor + * @return $this + */ + public function textColor(string $textColor) + { + $this->textColor = $textColor; + return $this; + } + + /** + * 按钮形式的 Checkbox 激活时的填充色和边框色 + * @param string $fill + * @return $this + */ + public function fill(string $fill) + { + $this->fill = $fill; + return $this; + } + + /** + * @param Checkbox[] $options + * @return $this + */ + public function options(array $options) + { + $this->options = $options; + if (!$this->max) { + $this->max = count($options); + } + return $this; + } +} diff --git a/builder/View/Components/Form/ColorPicker.php b/builder/View/Components/Form/ColorPicker.php new file mode 100644 index 0000000..92d401e --- /dev/null +++ b/builder/View/Components/Form/ColorPicker.php @@ -0,0 +1,97 @@ +disabled = $disabled; + return $this; + } + + /** + * 尺寸 medium / small / mini + * @param string $size + * @return $this + */ + public function size($size) + { + $this->size = $size; + return $this; + } + + /** + * 是否支持透明度选择 + * @param bool $showAlpha + * @return $this + */ + public function showAlpha($showAlpha = true) + { + $this->showAlpha = $showAlpha; + return $this; + } + + /** + * 写入 v-model 的颜色的格式 + * hsl / hsv / hex / rgb + * @param string $colorFormat + * @return $this + */ + public function colorFormat($colorFormat) + { + $this->colorFormat = $colorFormat; + return $this; + } + + /** + * ColorPicker 下拉框的类名 + * @param string $popperClass + * @return $this + */ + public function popperClass($popperClass) + { + $this->popperClass = $popperClass; + return $this; + } + + /** + * 预定义颜色 + * @param array $predefine + * @return $this + */ + public function predefine($predefine) + { + $this->predefine = $predefine; + return $this; + } + + +} diff --git a/builder/View/Components/Form/DatePicker.php b/builder/View/Components/Form/DatePicker.php new file mode 100644 index 0000000..d2bb37c --- /dev/null +++ b/builder/View/Components/Form/DatePicker.php @@ -0,0 +1,274 @@ +type($type); + } + + /** + * 完全只读 + * @param bool $readonly + * @return $this + */ + public function readonly($readonly = true) + { + $this->readonly = $readonly; + return $this; + } + + /** + * 禁用 + * @param bool $disabled + * @return $this + */ + public function disabled($disabled) + { + $this->disabled = $disabled; + return $this; + } + + /** + * 文本框可输入 + * @param bool $editable + * @return $this + */ + public function editable($editable = true) + { + $this->editable = $editable; + return $this; + } + + /** + * 是否显示清除按钮 + * @param bool $clearable + * @return $this + */ + public function clearable($clearable) + { + $this->clearable = $clearable; + return $this; + } + + /** + * 输入框尺寸 + * @param string $size + * @return $this + */ + public function size($size) + { + $this->size = $size; + return $this; + } + + /** + * 非范围选择时的占位内容 + * @param string $placeholder + * @return $this + */ + public function placeholder($placeholder) + { + $this->placeholder = $placeholder; + return $this; + } + + /** + * 范围选择时开始日期的占位内容 + * @param string $startPlaceholder + * @return $this + */ + public function startPlaceholder($startPlaceholder) + { + $this->startPlaceholder = $startPlaceholder; + return $this; + } + + /** + * 范围选择时结束日期的占位内容 + * @param string $endPlaceholder + * @return $this + */ + public function endPlaceholder($endPlaceholder) + { + $this->endPlaceholder = $endPlaceholder; + return $this; + } + + /** + * 显示类型 + * year/month/date/dates/week/datetime/datetimerange/daterange/monthrange + * @param string $type + * @return $this + */ + public function type($type) + { + $this->type = $type; + return $this; + } + + /** + * 显示在输入框中的格式 + * @param string $format + * @return $this + */ + public function format($format) + { + $this->format = $format; + return $this; + } + + /** + * 对齐方式 + * @param string $align + * @return $this + */ + public function align($align) + { + $this->align = $align; + return $this; + } + + /** + * DatePicker 下拉框的类名 + * @param string $popperClass + * @return $this + */ + public function popperClass($popperClass) + { + $this->popperClass = $popperClass; + return $this; + } + + /** + * 当前时间日期选择器特有的选项参考下表 + * @param array $pickerOptions + * @return $this + */ + public function pickerOptions($pickerOptions) + { + $this->pickerOptions = $pickerOptions; + return $this; + } + + /** + * 选择范围时的分隔符 + * @param string $rangeSeparator + * @return $this + */ + public function rangeSeparator($rangeSeparator) + { + $this->rangeSeparator = $rangeSeparator; + return $this; + } + + /** + * 可选,选择器打开时默认显示的时间 + * @param Date $defaultValue + * @return $this + */ + public function defaultValue($defaultValue) + { + $this->defaultValue = $defaultValue; + return $this; + } + + /** + * 范围选择时选中日期所使用的当日内具体时刻 + * @param string[] $defaultTime + * @return $this + */ + public function defaultTime($defaultTime) + { + $this->defaultTime = $defaultTime; + return $this; + } + + /** + * 可选,绑定值的格式。不指定则绑定值为 Date 对象 + * @param mixed $valueFormat + * @return $this + */ + public function valueFormat($valueFormat) + { + $this->valueFormat = $valueFormat; + return $this; + } + + /** + * 在范围选择器里取消两个日期面板之间的联动 + * @param bool $unlinkPanels + * @return $this + */ + public function unlinkPanels($unlinkPanels = true) + { + $this->unlinkPanels = $unlinkPanels; + return $this; + } + + /** + * 自定义头部图标的类名 + * @param string $prefixIcon + * @return $this + */ + public function prefixIcon($prefixIcon) + { + $this->prefixIcon = $prefixIcon; + return $this; + } + + /** + * 自定义清空图标的类名 + * @param string $clearIcon + * @return $this + */ + public function clearIcon($clearIcon) + { + $this->clearIcon = $clearIcon; + return $this; + } + + /** + * 输入时是否触发表单的校验 + * @param bool $validateEvent + * @return $this + */ + public function validateEvent($validateEvent = true) + { + $this->validateEvent = $validateEvent; + return $this; + } + + +} diff --git a/builder/View/Components/Form/DateTimePicker.php b/builder/View/Components/Form/DateTimePicker.php new file mode 100644 index 0000000..ec21aee --- /dev/null +++ b/builder/View/Components/Form/DateTimePicker.php @@ -0,0 +1,13 @@ +type($type); + } +} diff --git a/builder/View/Components/Form/IconChoose.php b/builder/View/Components/Form/IconChoose.php new file mode 100644 index 0000000..9f5dd49 --- /dev/null +++ b/builder/View/Components/Form/IconChoose.php @@ -0,0 +1,84 @@ +placement = $placement; + return $this; + } + + /** + * 是否可清空 + * @param bool $clearable + * @return $this + */ + public function clearable(bool $clearable = true) + { + $this->clearable = $clearable; + return $this; + } + + /** + * 是否用户可输入 + * @param bool $userInput + * @return $this + */ + public function userInput(bool $userInput = true) + { + $this->userInput = $userInput; + return $this; + } + + /** + * 选择后关闭 + * @param bool $autoClose + * @return $this + */ + public function autoClose(bool $autoClose = true) + { + $this->autoClose = $autoClose; + return $this; + } + + /** + * 占位符 + * @param string $placeholder + * @return $this + */ + public function placeholder(string $placeholder = '') + { + $this->placeholder = $placeholder; + return $this; + } + /** + * 是否禁用 + * @param bool $disabled + * @return $this + */ + public function disabled(bool $disabled = true) + { + $this->disabled = $disabled; + return $this; + } +} diff --git a/builder/View/Components/Form/Input.php b/builder/View/Components/Form/Input.php new file mode 100644 index 0000000..3e5548b --- /dev/null +++ b/builder/View/Components/Form/Input.php @@ -0,0 +1,354 @@ +type = "textarea"; + $this->rows = $rows; + return $this; + } + + /** + * @return $this + */ + public function password() + { + $this->type = "password"; + return $this; + } + + /** + * 类型 + * text,textarea 和其他 原生 input 的 type 值 + * @param string $type + * @return $this + */ + public function type($type) + { + $this->type = $type; + return $this; + } + + /** + * 原生属性,最大输入长度 + * @param string $maxlength + * @return $this + */ + public function maxlength($maxlength) + { + $this->maxlength = $maxlength; + return $this; + } + + /** + * 原生属性,最小输入长度 + * @param string $minlength + * @return $this + */ + public function minlength($minlength) + { + $this->minlength = $minlength; + return $this; + } + + /** + * 是否显示输入字数统计,只在 type = "text" 或 type = "textarea" 时有效 + * 必须设置maxlength才会生效 + * @param bool $showWordLimit + * @return $this + */ + public function showWordLimit(bool $showWordLimit = true) + { + $this->showWordLimit = $showWordLimit; + return $this; + } + + /** + * 输入框占位文本 + * @param string $placeholder + * @return $this + */ + public function placeholder($placeholder) + { + $this->placeholder = $placeholder; + return $this; + } + + /** + * 是否可清空 + * @param bool $clearable + * @return $this + */ + public function clearable(bool $clearable = true) + { + $this->clearable = $clearable; + return $this; + } + + /** + * 是否显示切换密码图标 + * @param bool $showPassword + * @return $this + */ + public function showPassword(bool $showPassword = true) + { + $this->showPassword = $showPassword; + return $this; + } + + /** + * 禁用 + * @param bool $disabled + * @return $this + */ + public function disabled(bool $disabled = true) + { + $this->disabled = $disabled; + return $this; + } + + /** + * 输入框尺寸,只在 type!="textarea" 时有效 + * medium / small / mini + * @param string $size + * @return $this + */ + public function size($size) + { + $this->size = $size; + return $this; + } + + /** + * 输入框头部图标 + * @param string $prefixIcon + * @return $this + */ + public function prefixIcon($prefixIcon) + { + $this->prefixIcon = $prefixIcon; + return $this; + } + + /** + * 输入框尾部图标 + * @param string $suffixIcon + * @return $this + */ + public function suffixIcon($suffixIcon) + { + $this->suffixIcon = $suffixIcon; + return $this; + } + + /** + * 输入框行数,只对 type="textarea" 有效 + * @param int $rows + * @return $this + */ + public function rows(int $rows) + { + $this->rows = $rows; + return $this; + } + + /** + * 自适应内容高度,只对 type="textarea" 有效,可传入对象,如,{ minRows: 2, maxRows: 6 } + * @param bool $autosize + * @return $this + */ + public function autosize(bool $autosize) + { + $this->autosize = $autosize; + return $this; + } + + /** + * 原生属性,自动补全 + * @param string $autocomplete + * @return $this + */ + public function autocomplete(string $autocomplete) + { + $this->autocomplete = $autocomplete; + return $this; + } + + /** + * 原生属性,是否只读 + * @param bool $readonly + * @return $this + */ + public function readonly(bool $readonly = true) + { + $this->readonly = $readonly; + return $this; + } + + /** + * 原生属性,设置最大值 + * @param string $max + * @return $this + */ + public function max($max) + { + $this->max = $max; + return $this; + } + + /** + * 原生属性,设置最小值 + * @param string $min + * @return $this + */ + public function min($min) + { + $this->min = $min; + return $this; + } + + /** + * 原生属性,设置输入字段的合法数字间隔 + * @param string $step + * @return $this + */ + public function step($step) + { + $this->step = $step; + return $this; + } + + /** + * 控制是否能被用户缩放 + * @param string $resize + * @return $this + */ + public function resize($resize) + { + $this->resize = $resize; + return $this; + } + + /** + * 原生属性,自动获取焦点 + * @param bool $autofocus + * @return $this + */ + public function autofocus(bool $autofocus = true) + { + $this->autofocus = $autofocus; + return $this; + } + + /** + * 原生属性 + * @param string $form + * @return $this + */ + public function form($form) + { + $this->form = $form; + return $this; + } + + /** + * 输入框关联的label文字 + * @param string $label + * @return $this + */ + public function label($label) + { + $this->label = $label; + return $this; + } + + /** + * 输入框的tabindex + * @param string $tabindex + * @return $this + */ + public function tabindex($tabindex) + { + $this->tabindex = $tabindex; + return $this; + } + + /** + * 输入时是否触发表单的校验 + * @param bool $validateEvent + * @return $this + */ + public function validateEvent(bool $validateEvent = true) + { + $this->validateEvent = $validateEvent; + return $this; + } + + /** + * 输入框前置内容 + * @param mixed $prepend + * @return $this + */ + public function prepend($prepend) + { + $this->prepend = $prepend; + return $this; + } + + /** + * 输入框后置内容 + * @param mixed $append + * @return $this + */ + public function append($append) + { + $this->append = $append; + return $this; + } + + +} diff --git a/builder/View/Components/Form/InputNumber.php b/builder/View/Components/Form/InputNumber.php new file mode 100644 index 0000000..a914110 --- /dev/null +++ b/builder/View/Components/Form/InputNumber.php @@ -0,0 +1,190 @@ +min = $min; + return $this; + } + + /** + * 设置计数器允许的最大值 + * @param int|float $max + * @return $this + */ + public function max($max) + { + $this->max = $max; + return $this; + } + + /** + * 计数器步长 + * @param int|float $step + * @return $this + */ + public function step($step) + { + $this->step = $step; + return $this; + } + + /** + * 是否只能输入 step 的倍数 + * @param bool $stepStrictly + * @return $this + */ + public function stepStrictly(bool $stepStrictly = true) + { + $this->stepStrictly = $stepStrictly; + return $this; + } + + /** + * 数值精度 + * @param int $precision + * @return $this + */ + public function precision(int $precision) + { + $this->precision = $precision; + return $this; + } + + /** + * 计数器尺寸 + * large, small + * @param string $size + * @return $this + */ + public function size(string $size) + { + $this->size = $size; + return $this; + } + + /** + * 是否禁用计数器 + * @param bool $disabled + * @return $this + */ + public function disabled(bool $disabled = true) + { + $this->disabled = $disabled; + return $this; + } + + /** + * 是否使用控制按钮 + * @param bool $controls + * @return $this + */ + public function controls(bool $controls = true) + { + $this->controls = $controls; + return $this; + } + + /** + * 控制按钮位置 + * @param string $controlsPosition + * @return $this + */ + public function controlsPosition(string $controlsPosition) + { + $this->controlsPosition = $controlsPosition; + return $this; + } + + /** + * 原生属性 + * @param string $name + * @return $this + */ + public function name(string $name) + { + $this->name = $name; + return $this; + } + + /** + * 输入框关联的label文字 + * @param string $label + * @return $this + */ + public function label(string $label) + { + $this->label = $label; + return $this; + } + + /** + * 输入框默认 placeholder + * @param string $placeholder + * @return $this + */ + public function placeholder(string $placeholder) + { + $this->placeholder = $placeholder; + return $this; + } + + +} diff --git a/builder/View/Components/Form/ItemSelect.php b/builder/View/Components/Form/ItemSelect.php new file mode 100644 index 0000000..bf8cb7e --- /dev/null +++ b/builder/View/Components/Form/ItemSelect.php @@ -0,0 +1,15 @@ +label($value)->title($title); + } + + /** + * Radio 的 value + * @param $label + * @return $this + */ + public function label($label) + { + $this->label = $label; + return $this; + } + + /** + * 是否禁用 + * @param bool $disabled + * @return $this + */ + public function disabled(bool $disabled) + { + $this->disabled = $disabled; + return $this; + } + + /** + * 是否显示边框 + * @param bool $border + * @return $this + */ + public function border(bool $border = true) + { + $this->border = $border; + return $this; + } + + /** + * Radio 的尺寸,仅在 border 为真时有效 + * @param string $size + * @return $this + */ + public function size(string $size) + { + $this->size = $size; + return $this; + } + + /** + * 原生 name 属性 + * @param string $name + * @return $this + */ + public function name(string $name) + { + $this->name = $name; + return $this; + } + + public function title(string $title) + { + $this->title = $title; + return $this; + } + +} diff --git a/builder/View/Components/Form/RadioGroup.php b/builder/View/Components/Form/RadioGroup.php new file mode 100644 index 0000000..b6e7286 --- /dev/null +++ b/builder/View/Components/Form/RadioGroup.php @@ -0,0 +1,94 @@ +options($options); + } + + /** + * @param string $size + * @return $this + */ + public function size($size) + { + $this->size = $size; + return $this; + } + + /** + * @param bool $disabled + * @return RadioGroup + */ + public function disabled(bool $disabled = true) + { + $this->disabled = $disabled; + return $this; + } + + /** + * @param string $textColor + * @return RadioGroup + */ + public function textColor(string $textColor) + { + $this->textColor = $textColor; + return $this; + } + + /** + * @param string $fill + * @return RadioGroup + */ + public function fill(string $fill) + { + $this->fill = $fill; + return $this; + } + + /** + * @param Radio[]|\Closure $options + * @return RadioGroup + */ + public function options($options) + { + if ($options instanceof \Closure) { + $this->options = call_user_func($options); + } else { + $this->options = $options; + } + + return $this; + } + + + public function border() + { + collect($this->options)->each(function (Radio $radio) { + $radio->setBorder(); + }); + + return $this; + } + +} diff --git a/builder/View/Components/Form/Rate.php b/builder/View/Components/Form/Rate.php new file mode 100644 index 0000000..145bfdb --- /dev/null +++ b/builder/View/Components/Form/Rate.php @@ -0,0 +1,196 @@ +max = $max; + return $this; + } + + /** + * 是否为只读 + * @param bool $disabled + * @return $this + */ + public function disabled($disabled) + { + $this->disabled = $disabled; + return $this; + } + + /** + * 是否允许半选 + * @param bool $allowHalf + * @return $this + */ + public function allowHalf($allowHalf) + { + $this->allowHalf = $allowHalf; + return $this; + } + + /** + * 低分和中等分数的界限值,值本身被划分在低分中 + * @param int $lowThreshold + * @return $this + */ + public function lowThreshold($lowThreshold) + { + $this->lowThreshold = $lowThreshold; + return $this; + } + + /** + * 高分和中等分数的界限值,值本身被划分在高分中 + * @param int $highThreshold + * @return $this + */ + public function highThreshold($highThreshold) + { + $this->highThreshold = $highThreshold; + return $this; + } + + /** + * icon 的颜色。若传入数组,共有 3 个元素,为 3 个分段所对应的颜色;若传入对象,可自定义分段,键名为分段的界限值,键值为对应的颜色 + * @param array $colors + * @return $this + */ + public function colors($colors) + { + $this->colors = $colors; + return $this; + } + + /** + * 未选中 icon 的颜色 + * @param string $voidColor + * @return $this + */ + public function voidColor($voidColor) + { + $this->voidColor = $voidColor; + return $this; + } + + /** + * 只读时未选中 icon 的颜色 + * @param string $disabledVoidColor + * @return $this + */ + public function disabledVoidColor($disabledVoidColor) + { + $this->disabledVoidColor = $disabledVoidColor; + return $this; + } + + /** + * icon 的类名。若传入数组,共有 3 个元素,为 3 个分段所对应的类名;若传入对象,可自定义分段,键名为分段的界限值,键值为对应的类名 + * @param array $iconClasses + * @return $this + */ + public function iconClasses($iconClasses) + { + $this->iconClasses = $iconClasses; + return $this; + } + + /** + * 未选中 icon 的类名 + * @param string $voidIconClass + * @return $this + */ + public function voidIconClass($voidIconClass) + { + $this->voidIconClass = $voidIconClass; + return $this; + } + + /** + * 只读时未选中 icon 的类名 + * @param string $disabledVoidIconClass + * @return $this + */ + public function disabledVoidIconClass($disabledVoidIconClass) + { + $this->disabledVoidIconClass = $disabledVoidIconClass; + return $this; + } + + /** + * 是否显示辅助文字,若为真,则会从 texts 数组中选取当前分数对应的文字内容 + * @param bool $showText + * @return $this + */ + public function showText($showText=true) + { + $this->showText = $showText; + return $this; + } + + /** + * 是否显示当前分数,show-score 和 show-text 不能同时为真 + * @param bool $showScore + * @return $this + */ + public function showScore($showScore=true) + { + $this->showScore = $showScore; + return $this; + } + + /** + * 辅助文字的颜色 + * @param string $textColor + * @return $this + */ + public function textColor($textColor) + { + $this->textColor = $textColor; + return $this; + } + + /** + * 辅助文字数组 + * @param array $texts + * @return $this + */ + public function texts($texts) + { + $this->texts = $texts; + return $this; + } + + +} diff --git a/builder/View/Components/Form/Select.php b/builder/View/Components/Form/Select.php new file mode 100644 index 0000000..1d2ba66 --- /dev/null +++ b/builder/View/Components/Form/Select.php @@ -0,0 +1,304 @@ +style)->put('display','block'); + $this->style($style); + } + return $this; + } + + /** + * 是否多选 + * @param bool $multiple + * @return $this + */ + public function multiple($multiple = true) + { + $this->multiple = $multiple; + if (!$this->componentValue) { + $this->componentValue([]); + } + + return $this; + } + + /** + * 是否禁用 + * @param bool $disabled + * @return $this + */ + public function disabled($disabled = true) + { + $this->disabled = $disabled; + return $this; + } + + /** + * 输入框尺寸 + * @param string $size + * @return $this + */ + public function size($size) + { + $this->size = $size; + return $this; + } + + /** + * 是否可以清空选项 + * @param bool $clearable + * @return $this + */ + public function clearable($clearable = true) + { + $this->clearable = $clearable; + return $this; + } + + /** + * 多选时是否将选中值按文字的形式展示 + * @param bool $collapseTags + * @return $this + */ + public function collapseTags($collapseTags = true) + { + $this->collapseTags = $collapseTags; + return $this; + } + + /** + * 多选时用户最多可以选择的项目数,为 0 则不限制 + * @param int $multipleLimit + * @return $this + */ + public function multipleLimit($multipleLimit) + { + $this->multipleLimit = $multipleLimit; + return $this; + } + + /** + * select input 的 name 属性 + * @param string $name + * @return $this + */ + public function name($name) + { + $this->name = $name; + return $this; + } + + /** + * select input 的 autocomplete 属性 + * @param string $autocomplete + * @return $this + */ + public function autocomplete($autocomplete) + { + $this->autocomplete = $autocomplete; + return $this; + } + + /** + * 占位符 + * @param string $placeholder + * @return $this + */ + public function placeholder($placeholder) + { + $this->placeholder = $placeholder; + return $this; + } + + /** + * 是否可搜索 + * @param bool $filterable + * @return $this + */ + public function filterable($filterable = true) + { + $this->filterable = $filterable; + return $this; + } + + /** + * 是否允许用户创建新条目 + * @param string $allowCreateUrl 创建新条目Url + * @return $this + */ + public function allowCreate($allowCreateUrl = null) + { + $this->allowCreate = true; + $this->filterable = true; + $this->allowCreateUrl = $allowCreateUrl; + return $this; + } + + /** + * 是否为远程搜索 + * @param string $remoteUrl 远程搜索URL + * @return $this + */ + public function remote($remoteUrl) + { + $this->remote = true; + $this->remoteUrl = $remoteUrl; + return $this; + } + + /** + * 远程加载时显示的文字 + * @param string $loadingText + * @return $this + */ + public function loadingText($loadingText) + { + $this->loadingText = $loadingText; + return $this; + } + + /** + * 搜索条件无匹配时显示的文字 + * @param string $noMatchText + * @return $this + */ + public function noMatchText($noMatchText) + { + $this->noMatchText = $noMatchText; + return $this; + } + + /** + * 选项为空时显示的文字 + * @param string $noDataText + * @return $this + */ + public function noDataText($noDataText) + { + $this->noDataText = $noDataText; + return $this; + } + + /** + * Select 下拉框的类名 + * @param string $popperClass + * @return $this + */ + public function popperClass($popperClass) + { + $this->popperClass = $popperClass; + return $this; + } + + /** + * 多选且可搜索时,是否在选中一个选项后保留当前的搜索关键词 + * @param bool $reserveKeyword + * @return $this + */ + public function reserveKeyword($reserveKeyword = true) + { + $this->reserveKeyword = $reserveKeyword; + return $this; + } + + /** + * 在输入框按下回车,选择第一个匹配项。需配合 filterable 或 remote 使用 + * @param bool $defaultFirstOption + * @return $this + */ + public function defaultFirstOption($defaultFirstOption = true) + { + $this->defaultFirstOption = $defaultFirstOption; + return $this; + } + + /** + * 是否将弹出框插入至 body 元素。在弹出框的定位出现问题时,可将该属性设置为 false + * @param bool $popperAppendToBody + * @return $this + */ + public function popperAppendToBody($popperAppendToBody = true) + { + $this->popperAppendToBody = $popperAppendToBody; + return $this; + } + + /** + * 对于不可搜索的 Select,是否在输入框获得焦点后自动弹出选项菜单 + * @param bool $automaticDropdown + * @return $this + */ + public function automaticDropdown($automaticDropdown = true) + { + $this->automaticDropdown = $automaticDropdown; + return $this; + } + + /** + * @param $options + * @return $this + */ + public function options($options) + { + if ($options instanceof \Closure) { + $this->options = call_user_func($options); + } else { + $this->options = $options; + } + + return $this; + } + +} diff --git a/builder/View/Components/Form/Slider.php b/builder/View/Components/Form/Slider.php new file mode 100644 index 0000000..b4cd0b1 --- /dev/null +++ b/builder/View/Components/Form/Slider.php @@ -0,0 +1,186 @@ +min = $min; + return $this; + } + + /** + * 最大值 + * @param int $max + * @return $this + */ + public function max($max) + { + $this->max = $max; + return $this; + } + + /** + * 是否禁用 + * @param bool $disabled + * @return $this + */ + public function disabled($disabled) + { + $this->disabled = $disabled; + return $this; + } + + /** + * 步长 + * @param int $step + * @return $this + */ + public function step($step) + { + $this->step = $step; + return $this; + } + + /** + * 是否显示输入框,仅在非范围选择时有效 + * @param bool $showInput + * @return $this + */ + public function showInput($showInput = true) + { + $this->showInput = $showInput; + return $this; + } + + /** + * 在显示输入框的情况下,是否显示输入框的控制按钮 + * @param bool $showInputControls + * @return $this + */ + public function showInputControls($showInputControls = true) + { + $this->showInputControls = $showInputControls; + return $this; + } + + /** + * 输入框的尺寸 + * large / medium / small / mini + * @param string $inputSize + * @return $this + */ + public function inputSize($inputSize) + { + $this->inputSize = $inputSize; + return $this; + } + + /** + * 是否显示间断点 + * @param bool $showStops + * @return $this + */ + public function showStops($showStops = true) + { + $this->showStops = $showStops; + return $this; + } + + /** + * 是否显示 tooltip + * @param bool $showTooltip + * @return $this + */ + public function showTooltip($showTooltip = true) + { + $this->showTooltip = $showTooltip; + return $this; + } + + /** + * 是否为范围选择 + * @param bool $range + * @return $this + */ + public function range($range = true) + { + $this->range = $range; + return $this; + } + + /** + * 是否竖向模式 + * @param bool $vertical + * @param string $height + * @return $this + */ + public function vertical($vertical = true, $height = "100px") + { + $this->vertical = $vertical; + $this->height = $height; + return $this; + } + + /** + * Slider 高度,竖向模式时必填 + * @param mixed $height + * @return $this + */ + public function height($height) + { + $this->height = $height; + return $this; + } + + /** + * 屏幕阅读器标签 + * @param string $label + * @return $this + */ + public function label($label) + { + $this->label = $label; + return $this; + } + + /** + * tooltip 的自定义类名 + * @param string $tooltipClass + * @return $this + */ + public function tooltipClass($tooltipClass) + { + $this->tooltipClass = $tooltipClass; + return $this; + } +} diff --git a/builder/View/Components/Form/TimePicker.php b/builder/View/Components/Form/TimePicker.php new file mode 100644 index 0000000..7fa0524 --- /dev/null +++ b/builder/View/Components/Form/TimePicker.php @@ -0,0 +1,282 @@ +type($type); + } + + /** + * 完全只读 + * @param bool $readonly + * @return $this + */ + public function readonly($readonly = true) + { + $this->readonly = $readonly; + return $this; + } + + /** + * 禁用 + * @param bool $disabled + * @return $this + */ + public function disabled($disabled = true) + { + $this->disabled = $disabled; + return $this; + } + + /** + * 文本框可输入 + * @param bool $editable + * @return $this + */ + public function editable($editable = true) + { + $this->editable = $editable; + return $this; + } + + /** + * 是否显示清除按钮 + * @param bool $clearable + * @return $this + */ + public function clearable($clearable = true) + { + $this->clearable = $clearable; + return $this; + } + + /** + * 输入框尺寸 + * @param string $size + * @return $this + */ + public function size($size) + { + $this->size = $size; + return $this; + } + + /** + * 非范围选择时的占位内容 + * @param string $placeholder + * @return $this + */ + public function placeholder($placeholder) + { + $this->placeholder = $placeholder; + return $this; + } + + /** + * 范围选择时开始日期的占位内容 + * @param string $startPlaceholder + * @return $this + */ + public function startPlaceholder($startPlaceholder) + { + $this->startPlaceholder = $startPlaceholder; + return $this; + } + + /** + * 范围选择时开始日期的占位内容 + * @param string $endPlaceholder + * @return $this + */ + public function endPlaceholder($endPlaceholder) + { + $this->endPlaceholder = $endPlaceholder; + return $this; + } + + /** + * 是否为时间范围选择,仅对有效 + * @param bool $isRange + * @return $this + */ + public function isRange($isRange = true) + { + $this->isRange = $isRange; + $this->type = "picker"; + return $this; + } + + /** + * 是否使用箭头进行时间选择,仅对有效 + * @param bool $arrowControl + * @return $this + */ + public function arrowControl($arrowControl = true) + { + $this->arrowControl = $arrowControl; + return $this; + } + + /** + * 对齐方式 + * @param string $align + * @return $this + */ + public function align($align) + { + $this->align = $align; + return $this; + } + + /** + * TimePicker 下拉框的类名 + * @param mixed $popperClass + * @return $this + */ + public function popperClass($popperClass) + { + $this->popperClass = $popperClass; + return $this; + } + + /** + * 当前时间日期选择器特有的选项参考下表 + * @param array $pickerOptions + * @return $this + */ + public function pickerOptions($pickerOptions) + { + $this->pickerOptions = $pickerOptions; + return $this; + } + + /** + * 选择范围时的分隔符 + * @param string $rangeSeparator + * @return $this + */ + public function rangeSeparator($rangeSeparator) + { + $this->rangeSeparator = $rangeSeparator; + return $this; + } + + /** + * 可选,仅TimePicker时可用,绑定值的格式。不指定则绑定值为 Date 对象 + * @param string $valueFormat + * @return $this + */ + public function valueFormat($valueFormat) + { + $this->valueFormat = $valueFormat; + return $this; + } + + /** + * 可选,选择器打开时默认显示的时间 + * @param string $defaultValue + * @return $this + */ + public function defaultValue($defaultValue) + { + $this->defaultValue = $defaultValue; + return $this; + } + + /** + * 原生属性 + * @param string $name + * @return $this + */ + public function name($name) + { + $this->name = $name; + return $this; + } + + /** + * 自定义头部图标的类名 + * @param mixed $prefixIcon + * @return $this + */ + public function prefixIcon($prefixIcon) + { + $this->prefixIcon = $prefixIcon; + return $this; + } + + /** + * 自定义清空图标的类名 + * @param mixed $clearIcon + * @return $this + */ + public function clearIcon($clearIcon) + { + $this->clearIcon = $clearIcon; + return $this; + } + + /** + * 可选时间段,例如'18:30:00 - 20:30:00'或者传入数组['09:30:00 - 12:00:00', '14:30:00 - 18:30:00'] + * @param mixed $selectableRange + * @return $this + */ + public function selectableRange($selectableRange) + { + $this->selectableRange = $selectableRange; + return $this; + } + + /** + * 时间格式化(TimePicker) + * @param string $format + * @return $this + */ + public function format($format) + { + $this->format = $format; + return $this; + } + + /** + * 组件类型 select / picker + * @param string $type + * @return $this + */ + public function type($type) + { + $this->type = $type; + return $this; + } + + +} diff --git a/builder/View/Components/Form/Transfer.php b/builder/View/Components/Form/Transfer.php new file mode 100644 index 0000000..3f64b0c --- /dev/null +++ b/builder/View/Components/Form/Transfer.php @@ -0,0 +1,118 @@ +data = call_user_func($data); + } else { + $this->data = $data; + } + + return $this; + } + + /** + * 是否可搜索 + * @param bool $filterable + * @return $this + */ + public function filterable($filterable = true) + { + $this->filterable = $filterable; + return $this; + } + + /** + * 搜索框占位符 + * @param string $filterPlaceholder + * @return $this + */ + public function filterPlaceholder($filterPlaceholder) + { + $this->filterPlaceholder = $filterPlaceholder; + return $this; + } + + /** + * 右侧列表元素的排序策略:若为 original,则保持与数据源相同的顺序;若为 push,则新加入的元素排在最后;若为 unshift,则新加入的元素排在最前 + * original / push / unshift + * @param string $targetOrder + * @return $this + */ + public function targetOrder($targetOrder) + { + $this->targetOrder = $targetOrder; + return $this; + } + + /**自定义列表标题 + * @param string[] $titles + * @return $this + */ + public function titles($titles) + { + $this->titles = $titles; + return $this; + } + + /** + * 自定义按钮文案 + * @param string[] $buttonTexts + * @return $this + */ + public function buttonTexts($buttonTexts) + { + $this->buttonTexts = $buttonTexts; + return $this; + } + + /** + * 初始状态下左侧列表的已勾选项的 key 数组 + * @param array $leftDefaultChecked + * @return $this + */ + public function leftDefaultChecked($leftDefaultChecked) + { + $this->leftDefaultChecked = $leftDefaultChecked; + return $this; + } + + /** + * 初始状态下右侧列表的已勾选项的 key 数组 + * @param array $rightDefaultChecked + * @return $this + */ + public function rightDefaultChecked($rightDefaultChecked) + { + $this->rightDefaultChecked = $rightDefaultChecked; + return $this; + } + + +} diff --git a/builder/View/Components/Form/Upload.php b/builder/View/Components/Form/Upload.php new file mode 100644 index 0000000..05e0599 --- /dev/null +++ b/builder/View/Components/Form/Upload.php @@ -0,0 +1,222 @@ +action = route('/upload/image'); + $this->host = config('admin.upload.host', route('/')); + $this->componentValue($value); + } + + static public function make($value = null) + { + return new Upload($value); + } + + public function destroy(FormItem $formItem) + { + $files = []; + if (is_array($formItem->original)) { + $files = $formItem->original; + } else { + $files[] = $formItem->original; + } + $storage = Storage()->getDriver(); + collect($files)->each(function ($file) use ($storage) { + if (!empty($this->valueName)) { + $file = $file[$this->valueName]; + } + if (!empty($file)){ + $path = parse_url($file)['path'] ?? ''; + $action = Version::isV2() ? 'fileExists' : 'has'; + $storage->{$action}($path) && $storage->delete($path); + } + }); + } + + /** + * @param string $action + * @return $this + */ + public function action($action) + { + $this->action = $action; + return $this; + } + + + /** + * @param string $host + * @return $this; + */ + public function host(string $host) + { + $this->host = $host; + return $this; + } + + + /** + * @param bool $multiple + * @param null|string $keyName + * @param null|string $valueName + * @return $this + */ + public function multiple(bool $multiple = true, $keyName = null, $valueName = null) + { + $this->multiple = $multiple; + $this->keyName = $keyName; + $this->valueName = $valueName; + return $this; + } + + /** + * @param array $data + * @return $this + */ + public function data($data) + { + + foreach ($data as $key => $value) { + $this->data = Arr::set($this->data, $key, $value); + } + + return $this; + } + + /** + * 文件保存目录 + * @param $path + * @return $this + */ + public function path($path) + { + $this->data = Arr::set($this->data, "path", $path); + return $this; + } + + /** + * 自动生成文件名 + * @return $this + */ + public function uniqueName() + { + $this->data = Arr::set($this->data, "uniqueName", true); + return $this; + } + + /** + * @param bool $drag + * @return $this + */ + public function drag(bool $drag = true) + { + $this->drag = $drag; + return $this; + } + + /** + * @param string $accept + * @return $this + */ + public function accept($accept) + { + $this->accept = $accept; + return $this; + } + + + /** + * @param bool $disabled + * @return $this + */ + public function disabled(bool $disabled = true) + { + $this->disabled = $disabled; + return $this; + } + + /** + * @param int $limit + * @return $this + */ + public function limit(int $limit) + { + $this->limit = $limit; + return $this; + } + + /** + * @return $this; + */ + public function image() + { + $this->type = "image"; + return $this; + } + + public function avatar() + { + $this->type = "avatar"; + return $this; + } + + public function file() + { + $this->type = "file"; + $this->action = route('/upload/file'); + return $this; + } + + /** + * @param int $width + * @return $this + */ + public function width(int $width) + { + $this->width = $width; + return $this; + } + + /** + * @param int $height + * @return $this + */ + public function height(int $height) + { + $this->height = $height; + return $this; + } +} diff --git a/builder/View/Components/Form/WangEditor.php b/builder/View/Components/Form/WangEditor.php new file mode 100644 index 0000000..52acd45 --- /dev/null +++ b/builder/View/Components/Form/WangEditor.php @@ -0,0 +1,156 @@ +menus = $menus; + return $this; + } + + /** + * 关闭样式过滤 + * @param bool $showFullScreen + * @return $this + */ + public function pasteFilterStyle(bool $pasteFilterStyle = false) + { + $this->pasteFilterStyle = $pasteFilterStyle; + return $this; + } + + /** + * 开启全屏按钮 + * @param bool $showFullScreen + * @return $this + */ + public function showFullScreen(bool $showFullScreen = true) + { + $this->showFullScreen = $showFullScreen; + return $this; + } + + /** + * 编辑区域的 z-index + * @param int $zIndex + * @return $this + */ + public function zIndex(int $zIndex) + { + $this->zIndex = $zIndex; + return $this; + } + + /** + * 使用 base64 保存图片 + * @param bool $uploadImgShowBase64 + * @return $this + */ + public function uploadImgShowBase64(bool $uploadImgShowBase64 = true) + { + $this->uploadImgShowBase64 = $uploadImgShowBase64; + return $this; + } + + /** + * 配置服务器端地址 + * @param string $uploadImgServer + * @return $this + */ + public function uploadImgServer(string $uploadImgServer) + { + $this->uploadImgServer = $uploadImgServer; + return $this; + } + + /** + * 自定义 fileName + * @param mixed $uploadFileName + * @return WangEditor + */ + public function uploadFileName(string $uploadFileName) + { + $this->uploadFileName = $uploadFileName; + return $this; + } + + /** + * 自定义 header + * @param mixed $uploadImgHeaders + * @return WangEditor + */ + public function uploadImgHeaders(array $uploadImgHeaders) + { + $this->uploadImgHeaders = $uploadImgHeaders; + return $this; + } + + /** + * @param mixed $component + * @return WangEditor + */ + public function component($component) + { + $this->component = $component; + return $this; + } +} diff --git a/builder/View/Components/Grid/Avatar.php b/builder/View/Components/Grid/Avatar.php new file mode 100644 index 0000000..fce7aab --- /dev/null +++ b/builder/View/Components/Grid/Avatar.php @@ -0,0 +1,149 @@ +host = config('admin.upload.host');// Storage::disk(config('admin.upload.disk')); + $this->componentValue($value); + $this->defaultSrc = config('admin.default_avatar'); + } + + + + static public function make($value = null) + { + return new Avatar($value); + } + + + /** + * 设置头像的图标类型,参考 Icon 组件 + * @param string $icon + * @return $this + */ + public function icon(string $icon) + { + $this->icon = $icon; + return $this; + } + + /** + * 设置头像的大小 number / large / medium / small + * @param int|string $size + * @return $this + */ + public function size($size) + { + $this->size = $size; + return $this; + } + + /** + * 设置头像的形状 circle / square + * @param string $shape + * @return $this + */ + public function shape(string $shape) + { + $this->shape = $shape; + return $this; + } + + /** + * 图片头像的资源地址 + * @param string $src + * @return $this + */ + public function src(string $src) + { + $this->src = $src; + return $this; + } + + /** + * 以逗号分隔的一个或多个字符串列表表明一系列用户代理使用的可能的图像 + * @param string $srcSet + * @return $this + */ + public function srcSet(string $srcSet) + { + $this->srcSet = $srcSet; + return $this; + } + + /** + * 描述图像的替换文本 + * @param string $alt + * @return $this + */ + public function alt(string $alt) + { + $this->alt = $alt; + return $this; + } + + /** + * 当展示类型为图片的时候,设置图片如何适应容器框 + * fill / contain / cover / none / scale-down + * @param string $fit + * @return $this + */ + public function fit(string $fit) + { + $this->fit = $fit; + return $this; + } + + /** + * 设置默认头像 + * @param string $defaultSrc + * @return $this + */ + public function defaultSrc($defaultSrc) + { + $this->defaultSrc = $defaultSrc; + return $this; + } +} diff --git a/builder/View/Components/Grid/Boole.php b/builder/View/Components/Grid/Boole.php new file mode 100644 index 0000000..1597e2b --- /dev/null +++ b/builder/View/Components/Grid/Boole.php @@ -0,0 +1,12 @@ +disabled = $disabled; + return $this; + } + + /** + * switch 的宽度(像素) + * @param int $width + * @return $this + */ + public function width(int $width) + { + $this->width = $width; + return $this; + } + + /** + * switch 打开时所显示图标的类名,设置此项会忽略 active-text + * @param string $activeIconClass + * @return $this + */ + public function activeIconClass($activeIconClass) + { + $this->activeIconClass = $activeIconClass; + return $this; + } + + /** + * switch 关闭时所显示图标的类名,设置此项会忽略 inactive-text + * @param string $inactiveIconClass + * @return $this + */ + public function inactiveIconClass($inactiveIconClass) + { + $this->inactiveIconClass = $inactiveIconClass; + return $this; + } + + /** + * switch 打开时的文字描述 + * @param string $activeText + * @return $this + */ + public function activeText($activeText) + { + $this->activeText = $activeText; + return $this; + } + + /** + * switch 关闭时的文字描述 + * @param string $inactiveText + * @return $this + */ + public function inactiveText($inactiveText) + { + $this->inactiveText = $inactiveText; + return $this; + } + + /** + * switch 打开时的值 + * @param bool $activeValue + * @return $this + */ + public function activeValue($activeValue) + { + $this->activeValue = $activeValue; + return $this; + } + + /** + * switch 关闭时的值 + * @param bool $inactiveValue + * @return $this + */ + public function inactiveValue($inactiveValue) + { + $this->inactiveValue = $inactiveValue; + return $this; + } + + /** + * switch 打开时的背景色 + * @param string $activeColor + * @return $this + */ + public function activeColor($activeColor) + { + $this->activeColor = $activeColor; + return $this; + } + + /** + * switch 关闭时的背景色 + * @param string $inactiveColor + * @return $this + */ + public function inactiveColor($inactiveColor) + { + $this->inactiveColor = $inactiveColor; + return $this; + } + + /** + * switch 对应的 name 属性 + * @param string $name + * @return $this + */ + public function name($name) + { + $this->name = $name; + return $this; + } + + /** + * 改变 switch 状态时是否触发表单的校验 + * @param bool $validateEvent + * @return $this + */ + public function validateEvent($validateEvent = true) + { + $this->validateEvent = $validateEvent; + return $this; + } + + +} diff --git a/builder/View/Components/Grid/Icon.php b/builder/View/Components/Grid/Icon.php new file mode 100644 index 0000000..08aaaa8 --- /dev/null +++ b/builder/View/Components/Grid/Icon.php @@ -0,0 +1,12 @@ +host = config('admin.upload.host'); + $this->componentValue($value); + } + + + static public function make($value = null) + { + return new Image($value); + } + + /** + * 图片源,同原生 + * @param mixed $src + * @return $this + */ + public function src($src) + { + $this->src = $src; + return $this; + } + + /** + * 确定图片如何适应容器框,同原生 object-fit + * @param string $fit + * @return $this + */ + public function fit($fit) + { + $this->fit = $fit; + return $this; + } + + /** + * 是否开启懒加载 + * @param bool $lazy + * @return $this + */ + public function lazy($lazy = true) + { + $this->lazy = $lazy; + return $this; + } + + /** + * 开启懒加载后,监听 scroll 事件的容器 + * @param mixed $scrollContainer + * @return $this + */ + public function scrollContainer($scrollContainer) + { + $this->scrollContainer = $scrollContainer; + return $this; + } + + /** + * 开启图片预览功能 + * @param bool $preview + * @return $this + */ + public function preview($preview = true) + { + $this->preview = $preview; + return $this; + } + + /** + * 设置图片预览的 z-index + * @param int $zIndex + * @return $this + */ + public function zIndex($zIndex) + { + $this->zIndex = $zIndex; + return $this; + } + + /** + * 设置图片大小 + * @param $width + * @param $height + * @return $this + */ + public function size($width = null, $height = null) + { + if ($width) $this->style(collect($this->style)->push(['width' => is_int($width) ? $width . 'px' : $width])); + if ($height) $this->style(collect($this->style)->push(['height' => is_int($height) ? $height . 'px' : $height])); + + return $this; + } + + /** + * @param string $host + * @return $this + */ + public function host(string $host) + { + $this->host = $host; + return $this; + } + + +} diff --git a/builder/View/Components/Grid/Link.php b/builder/View/Components/Grid/Link.php new file mode 100644 index 0000000..74164a5 --- /dev/null +++ b/builder/View/Components/Grid/Link.php @@ -0,0 +1,77 @@ +type = [ + 'data' => $type, + 'random' => $random + ]; + return $this; + } + + /** + * @param bool $underline + * @return $this + */ + public function underline($underline = true) + { + $this->underline = $underline; + return $this; + } + + /** + * @param bool $disabled + * @return $this + */ + public function disabled(bool $disabled = true) + { + $this->disabled = $disabled; + return $this; + } + + /** + * @param string $href + * @return $this + */ + public function href($href) + { + $this->href = $href; + return $this; + } + + /** + * @param string $icon + * @return $this + */ + public function icon($icon) + { + $this->icon = $icon; + return $this; + } + + +} diff --git a/builder/View/Components/Grid/Route.php b/builder/View/Components/Grid/Route.php new file mode 100644 index 0000000..567cad6 --- /dev/null +++ b/builder/View/Components/Grid/Route.php @@ -0,0 +1,47 @@ +uri = $uri; + } + + + public static function make($url = "") + { + return new Route($url); + } + + /** + * 类型 + * primary / success / warning / danger / info + * @param mixed $type + * @return $this + */ + public function type($type) + { + $this->type = $type; + return $this; + } + + /** + * 图标类名 + * @param mixed $icon + * @return $this + */ + public function icon($icon) + { + $this->icon = $icon; + return $this; + } +} diff --git a/builder/View/Components/Grid/Tag.php b/builder/View/Components/Grid/Tag.php new file mode 100644 index 0000000..ad516a6 --- /dev/null +++ b/builder/View/Components/Grid/Tag.php @@ -0,0 +1,116 @@ +type = [ + 'data' => $type, + 'random' => $random + ]; + + return $this; + } + + /** + * 是否可关闭 + * @param bool $closable + * @return $this + */ + public function closable(bool $closable = true) + { + $this->closable = $closable; + return $this; + } + + /** + * 是否禁用渐变动画 + * @param bool $disableTransitions + * @return $this + */ + public function disableTransitions(bool $disableTransitions = true) + { + $this->disableTransitions = $disableTransitions; + return $this; + } + + /** + * 是否有边框描边 + * @param bool $hit + * @return $this + */ + public function hit(bool $hit = true) + { + $this->hit = $hit; + return $this; + } + + /** + * 背景色 + * @param string|array $color + * @param bool $random + * @return $this + */ + public function color($color, $random = true) + { + $this->color = [ + 'data' => $color, + 'random' => $random + ];; + return $this; + } + + /** + * 尺寸 medium / small / mini + * @param string $size + * @return $this + */ + public function size(string $size) + { + $this->size = $size; + return $this; + } + + /** + * 主题 dark / light / plain + * @param string $effect + * @return $this + */ + public function effect(string $effect) + { + $this->effect = $effect; + return $this; + } + +} diff --git a/builder/View/Components/GridComponent.php b/builder/View/Components/GridComponent.php new file mode 100644 index 0000000..cd771f2 --- /dev/null +++ b/builder/View/Components/GridComponent.php @@ -0,0 +1,7 @@ +title = $title; + $alert->description = $description; + return $alert; + } + + /** + * 标题 + * @param string $title + * @return $this + */ + public function title(string $title) + { + $this->title = $title; + return $this; + } + + /** + * 主题 + * success/warning/info/error + * @param string $type + * @return $this + */ + public function type(string $type) + { + $this->type = $type; + return $this; + } + + /** + * 辅助性文字。也可通过默认 slot 传入 + * @param string $description + * @return $this + */ + public function description(string $description) + { + $this->description = $description; + return $this; + } + + /** + * 是否可关闭 + * @param bool $closable + * @return $this + */ + public function closable(bool $closable=true) + { + $this->closable = $closable; + return $this; + } + + /** + * 文字是否居中 + * @param bool $center + * @return $this + */ + public function center(bool $center=true) + { + $this->center = $center; + return $this; + } + + /** + * 关闭按钮自定义文本 + * @param string $closeText + * @return $this + */ + public function closeText(string $closeText) + { + $this->closeText = $closeText; + return $this; + } + + /** + * 是否显示图标 + * @param bool $showIcon + * @return $this + */ + public function showIcon(bool $showIcon=true) + { + $this->showIcon = $showIcon; + return $this; + } + + /** + * 选择提供的主题 + * light/dark + * @param string $effect + * @return $this + */ + public function effect(string $effect) + { + $this->effect = $effect; + return $this; + } +} diff --git a/builder/View/Components/Widgets/Badge.php b/builder/View/Components/Widgets/Badge.php new file mode 100644 index 0000000..51e1e73 --- /dev/null +++ b/builder/View/Components/Widgets/Badge.php @@ -0,0 +1,92 @@ +value = $value; + } + + public static function make($value) + { + return new Badge($value); + } + + /** + * @param string|int $value + * @return $this + */ + public function value($value) + { + $this->value = $value; + return $this; + } + + /** + * @param int $max + * @return $this + */ + public function max(int $max) + { + $this->max = $max; + return $this; + } + + /** + * @param bool $isDot + * @return $this + */ + public function isDot(bool $isDot) + { + $this->isDot = $isDot; + return $this; + } + + /** + * @param bool $hidden + * @return $this + */ + public function hidden(bool $hidden) + { + $this->hidden = $hidden; + return $this; + } + + /** + * @param string $type + * @return $this + */ + public function type(string $type) + { + $this->type = $type; + return $this; + } + + /** + * @param mixed $child + * @return $this + */ + public function child($child) + { + $this->child = $child; + return $this; + } + + +} \ No newline at end of file diff --git a/builder/View/Components/Widgets/Button.php b/builder/View/Components/Widgets/Button.php new file mode 100644 index 0000000..af2cea5 --- /dev/null +++ b/builder/View/Components/Widgets/Button.php @@ -0,0 +1,73 @@ +content = $content; + } + + /** + * @param string $content 按钮内容 + * @return $this + */ + public static function make($content = null) + { + return new Button($content); + } + + /** + * @param mixed $uri + * @return $this + */ + public function uri($uri) + { + $this->uri = $uri; + return $this; + } + + /** + * @param string $handler 响应类型 request|route|link + * @return $this + */ + public function handler($handler) + { + if (!in_array($handler, [self::HANDLER_LINK, self::HANDLER_REQUEST, self::HANDLER_ROUTE])) { + throw new BusinessException(400, "ActionButton 事件类型错误"); + } + $this->handler = $handler; + return $this; + } + + public function route($uri) + { + $this->uri = $uri; + $this->handler = self::HANDLER_ROUTE; + return $this; + } + + public function addSubItem(SubForm $formItems) + { + $this->subFormEmit = $formItems->getSubFormEmit(); + $this->subForm = $formItems->getFormItems(); + return $this; + } +} diff --git a/builder/View/Components/Widgets/Card.php b/builder/View/Components/Widgets/Card.php new file mode 100644 index 0000000..698ba8a --- /dev/null +++ b/builder/View/Components/Widgets/Card.php @@ -0,0 +1,64 @@ +header = instance_content($header); + return $this; + } + + /** + * 设置 body 的样式 + * @param string|array $bodyStyle + * @return $this + */ + public function bodyStyle($bodyStyle) + { + $this->bodyStyle = $bodyStyle; + return $this; + } + + /** + * 设置阴影显示时机 + * always / hover / never + * @param string $shadow + * @return $this + */ + public function shadow(string $shadow) + { + $this->shadow = $shadow; + return $this; + } + + /** + * 设置内容组件 + * @param $content + * @return $this + */ + public function content($content) + { + $this->content = instance_content($content); + return $this; + } + +} diff --git a/builder/View/Components/Widgets/Category.php b/builder/View/Components/Widgets/Category.php new file mode 100644 index 0000000..194dca4 --- /dev/null +++ b/builder/View/Components/Widgets/Category.php @@ -0,0 +1,86 @@ +attributes = new Attributes(); + $this->dataUrl = admin_api_url(request()->path()).'/list'; + $this->model = new Model($model, $this); + if ($model) { + $this->keyName = $model->getKeyName(); + $this->defaultSort($model->getKeyName(), "asc"); + } + $this->isGetData = request('get_data') == "true"; + $this->toolbars = new Toolbars($this); + $this->batchActions = new BatchActions(); + $this->filter = new Filter($this->model); + } + + public static function make() + { + return new Category(); + } +} diff --git a/builder/View/Components/Widgets/Dialog.php b/builder/View/Components/Widgets/Dialog.php new file mode 100644 index 0000000..3bf887e --- /dev/null +++ b/builder/View/Components/Widgets/Dialog.php @@ -0,0 +1,19 @@ +content = $content; + } + + public static function make($content) + { + return new Divider($content); + } + + + /** + * @param string $direction + * @return $this + */ + public function direction(string $direction) + { + $this->direction = $direction; + return $this; + } + + /** + * @param string $contentPosition + * @return $this + */ + public function contentPosition(string $contentPosition) + { + $this->contentPosition = $contentPosition; + return $this; + } + + +} diff --git a/builder/View/Components/Widgets/Html.php b/builder/View/Components/Widgets/Html.php new file mode 100644 index 0000000..bb71d79 --- /dev/null +++ b/builder/View/Components/Widgets/Html.php @@ -0,0 +1,32 @@ +html = $html; + } + + + public static function make($html = "") + { + return new Html($html); + } + + /** + * @param string $html + * @return $this + */ + public function html(string $html) + { + $this->html = $html; + return $this; + } +} diff --git a/builder/View/Components/Widgets/Markdown.php b/builder/View/Components/Widgets/Markdown.php new file mode 100644 index 0000000..89070be --- /dev/null +++ b/builder/View/Components/Widgets/Markdown.php @@ -0,0 +1,26 @@ +content = $content; + } + + public static function make($content=""){ + return new Markdown($content); + } + + public function content($content) + { + $this->content = $content; + return $this; + } + +} diff --git a/builder/View/Components/Widgets/Steps.php b/builder/View/Components/Widgets/Steps.php new file mode 100644 index 0000000..3cd3088 --- /dev/null +++ b/builder/View/Components/Widgets/Steps.php @@ -0,0 +1,129 @@ +stepList = collect(); + } + + + public static function make() + { + return new Steps(); + } + + /** + * 每个 step 的间距,不填写将自适应间距。支持百分比。 + * @param int|string $space + * @return $this + */ + public function space($space) + { + $this->space = $space; + return $this; + } + + /** + * 显示方向 + * vertical / horizontal + * @param string $direction + * @return $this + */ + public function direction(string $direction) + { + $this->direction = $direction; + return $this; + } + + /** + * 设置当前激活步骤 + * @param int $active + * @return $this + */ + public function active(int $active) + { + $this->active = $active; + return $this; + } + + /** + * 设置当前步骤的状态 + * wait / process / finish / error / success + * @param string $processStatus + * @return $this + */ + public function processStatus(string $processStatus) + { + $this->processStatus = $processStatus; + return $this; + } + + /** + * 设置结束步骤的状态 + * wait / process / finish / error / success + * @param string $finishStatus + * @return $this + */ + public function finishStatus(string $finishStatus) + { + $this->finishStatus = $finishStatus; + return $this; + } + + /** + * 进行居中对齐 + * @param bool $alignCenter + * @return $this + */ + public function alignCenter(bool $alignCenter = true) + { + $this->alignCenter = $alignCenter; + return $this; + } + + /** + * 是否应用简洁风格 + * @param bool $simple + * @return $this + */ + public function simple(bool $simple = true) + { + $this->simple = $simple; + return $this; + } + + /** + * @param Step[]|\Closure $stepList + * @return $this + */ + public function stepList($stepList) + { + if ($stepList instanceof \Closure) { + call_user_func($stepList, $this->stepList); + } else { + $this->stepList = $stepList; + } + return $this; + } +} diff --git a/builder/View/Components/Widgets/Text.php b/builder/View/Components/Widgets/Text.php new file mode 100644 index 0000000..37e2322 --- /dev/null +++ b/builder/View/Components/Widgets/Text.php @@ -0,0 +1,26 @@ +text = $text; + } + + + static public function make($text = "") + { + return new Text($text); + } +} diff --git a/builder/View/Components/Widgets/Tooltip.php b/builder/View/Components/Widgets/Tooltip.php new file mode 100644 index 0000000..93cfaa6 --- /dev/null +++ b/builder/View/Components/Widgets/Tooltip.php @@ -0,0 +1,185 @@ +content = $content; + } + + + public static function make($content) + { + return new Tooltip($content); + + } + + /** + * @param string $effect + * @return $this + */ + public function effect(string $effect) + { + $this->effect = $effect; + return $this; + } + + /** + * @param bool $disabled + * @return $this + */ + public function disabled(bool $disabled) + { + $this->disabled = $disabled; + return $this; + } + + + + /** + * @param string $content + * @return $this + */ + public function content(string $content) + { + $this->content = $content; + return $this; + } + + /** + * @param string $placement + * @return $this + */ + public function placement(string $placement) + { + $this->placement = $placement; + return $this; + } + + /** + * @param int $offset + * @return $this + */ + public function offset(int $offset) + { + $this->offset = $offset; + return $this; + } + + /** + * @param string $transition + * @return $this + */ + public function transition(string $transition) + { + $this->transition = $transition; + return $this; + } + + /** + * @param bool $visibleArrow + * @return $this + */ + public function visibleArrow(bool $visibleArrow) + { + $this->visibleArrow = $visibleArrow; + return $this; + } + + /** + * @param int $openDelay + * @return $this + */ + public function openDelay(int $openDelay) + { + $this->openDelay = $openDelay; + return $this; + } + + /** + * @param bool $manual + * @return $this + */ + public function manual(bool $manual) + { + $this->manual = $manual; + return $this; + } + + /** + * @param string $popperClass + * @return $this + */ + public function popperClass(string $popperClass) + { + $this->popperClass = $popperClass; + return $this; + } + + /** + * @param bool $enterable + * @return $this + */ + public function enterable(bool $enterable) + { + $this->enterable = $enterable; + return $this; + } + + /** + * @param int $hideAfter + * @return $this + */ + public function hideAfter(int $hideAfter) + { + $this->hideAfter = $hideAfter; + return $this; + } + + /** + * @param int $tabindex + * @return $this + */ + public function tabindex(int $tabindex) + { + $this->tabindex = $tabindex; + return $this; + } + + /** + * @param mixed $slot + * @return $this + */ + public function slot($slot) + { + if ($slot instanceof \Closure) { + $this->slot = call_user_func($slot); + } else { + $this->slot = $slot; + } + return $this; + } +} diff --git a/builder/View/Form.php b/builder/View/Form.php new file mode 100644 index 0000000..32c8e29 --- /dev/null +++ b/builder/View/Form.php @@ -0,0 +1,944 @@ +attrs = new FormAttrs(); + $this->model = $model; + $this->dataUrl = admin_api_url(request()->path()); + $this->isGetData = request()->header('getData')=="true"; + $this->actions = new FormActions($this); + } + + + public static function make($model = null) + { + $form = new static($model); + $form->action(admin_api_url(request()->path().'?'.\http_build_query(request()->query()))); + return $form; + } + + /** + * 快捷生成字段 + * @param $prop + * @param string $label + * @param string $field + * @return FormItem + */ + public function item($prop, $label = '', $field = '') + { + $item = $this->addItem($prop, $label, $field); + $this->row(function (Row $row) use ($item) { + $row->item($item); + }); + return $item; + } + + /** + * 多列布局字段 + * + * @param $prop + * @param string $label + * @param string $field + * + * @return FormItem + */ + public function rowItem($prop, $label = '', $field = '') + { + return $this->addItem($prop, $label, $field); + } + + + /** + * 表单自定义布局 + * + * @param \Closure $closure + * + * @return $this + */ + public function row(\Closure $closure) + { + $row = new Row(); + call_user_func($closure, $row, $this); + $this->tab("default", function (FormTab $formTab) use ($row) { + $formTab->row($row); + }); + return $this; + } + + /** + * 自定义tab布局 + * @param $tabName + * @param \Closure $closure + * @return $this + */ + public function tab($tabName, \Closure $closure) + { + + $tab = collect($this->formItemLayout)->filter(function (FormTab $formTab) use ($tabName) { + return $formTab->getName() == $tabName; + })->first(); + if (empty($tab)) { + $tab = new FormTab($tabName, $this); + call_user_func($closure, $tab, $this); + $this->formItemLayout[] = $tab; + } else { + call_user_func($closure, $tab, $this); + } + return $this; + } + + /** + * tab位置 + * + * @param $tabPosition + * + * @return $this + */ + public function tabPosition($tabPosition) + { + $this->tabPosition = $tabPosition; + return $this; + } + + /** + * @param $prop + * @param $label + * @param $field + * + * @return FormItem + */ + protected function addItem($prop, $label, $field) + { + $item = new FormItem($prop, $label, $field); + $item->setForm($this); + $this->formItems[] = $item; + return $item; + } + + /** + * @param array $items + */ + protected function items($items = []) + { + $this->ignoreEmptyProps = collect($items)->filter(function (FormItem $item) { + return $item->isIgnoreEmpty(); + })->map(function (FormItem $item) { + return $item->getProp(); + })->flatten()->all(); + // 根据所处模式抛弃组件 + $this->formItemsAttr = collect($items)->filter(function (FormItem $item) { + return $this->isMode($item->gethiddenMode()); + })->map(function (FormItem $item) { + return $item->getProp(); + }); + /**@var FormItem $item */ + foreach ($items as $item) { + Arr::set($this->formItemsValue, $item->getProp(), $item->getDefaultValue()); + Arr::set($this->formRules, $item->getProp(), $item->getRules()); + } + } + + + /** + * 自定义表单动作 + * + * @param $closure + * + * @return $this + */ + public function actions(\Closure $closure) + { + call_user_func($closure, $this->actions); + return $this; + } + + /** + * 表单头部组件 + * + * @param $closure + * + * @return $this + */ + public function top($closure) + { + $this->top = new Content(); + call_user_func($closure, $this->top); + return $this; + } + + /** + * 表单底部组件 + * + * @param $closure + * + * @return $this + */ + public function bottom($closure) + { + $this->bottom = new Content(); + call_user_func($closure, $this->bottom); + return $this; + } + + /** + * @return string + */ + public function getAction(): string + { + if ($this->action) { + return $this->action; + } + if ($this->isMode(static::MODE_EDIT)) { + return $this->resource() . '/' . $this->id; + } + + if ($this->isMode(static::MODE_CREATE)) { + return $this->resource(-1); + } + return admin_api_url(request()->path()); + } + + /** + * 设置表单编辑模式获取编辑数据地址 + * + * @param string $dataUrl + * + * @return $this + */ + public function dataUrl(string $dataUrl) + { + $this->dataUrl = $dataUrl; + return $this; + } + + + /** + * 设置表单提交地址 + * + * @param string $action + * + * @return $this + */ + public function action($action) + { + $this->action = $action; + return $this; + } + + protected function setMode($mode = 'create') + { + $this->mode = $mode; + } + + public function isMode($mode): bool + { + return $this->mode === $mode; + } + + public function setResourceId($id) + { + $this->id = $id; + } + + public function getResourceId() + { + return $this->id; + } + + public function resource($slice = -1): string + { + $segments = explode('/', trim(admin_api_url(request()->path()), '/')); + if ($slice !== 0) { + $segments = array_slice($segments, 0, $slice); + } + return '/' . implode('/', $segments); + } + + + /** + * @return string + */ + public function getMode(): string + { + return $this->mode; + } + + /** + * 获取模型 + * @return Model + */ + public function model() + { + return $this->model; + } + + /** + * 设置清除模型缓存 + * + * @param bool $cachePut + * + * @return self + */ + public function cachePut(bool $cachePut = true) + { + if (property_exists($this->model, 'useCacheBuilder')) { + $this->model->useCacheBuilder = $cachePut; + } + + return $this; + } + + /** + * 获取表单是否是编辑模式 + * @return bool + */ + public function isEdit() + { + return $this->isEdit; + } + + /** + * 获取表单是否是编辑模式 + * + * @param bool $isEdit + * + * @return void + */ + public function setEdit($isEdit = false) + { + $this->isEdit = $isEdit; + } + + /** + * 添加表单验证规则 + * + * @param $rules + * @param $message + * + * @return $this + */ + public function addValidatorRule($rules, $message = []) + { + $this->addRule = $rules; + $this->addRuleMessage = $message; + + return $this; + } + + /** + * @param $data + * + * @return string + */ + protected function validatorData($data) + { + $rules = []; + $ruleMessages = []; + $field = []; + /* @var FormItem $formItem */ + foreach ($this->formItems as $formItem) { + if (empty($formItem->getServeRole())) { + continue; + } + $field[$formItem->getField()] = $formItem->getLabel(); + $rules[$formItem->getField()] = $formItem->getServeRole(); + $messages = $formItem->getServeRulesMessage(); + if (is_array($messages)) { + foreach ($messages as $key => $message) { + $ruleMessages[$formItem->getField() . '.' . $key] = $message; + } + } + } + $rules = array_merge($rules, $this->addRule); + $ruleMessages = array_merge($ruleMessages, $this->addRuleMessage); + $validator = new Validate($rules, $ruleMessages, $field); + if ($validator->check($data) !== true) { + throw new ValidateException(422, (string)$validator->getError()); + } + } + + public function input($key, $value = null) + { + if (is_null($value)) { + return Arr::get($this->inputs, $key); + } + return Arr::set($this->inputs, $key, $value); + } + + protected function prepare($data = []) + { + //处理要过滤的字段 + $this->inputs = array_merge($this->removeIgnoredFields($data), $this->inputs); + //处理表单提交时事件 + if (($response = $this->callSaving()) instanceof Response) { + return $response; + } + //处理关联字段 + $this->relations = $this->getRelationInputs($this->inputs); + $this->updates = Arr::except($this->inputs, array_keys($this->relations)); + } + + protected function removeIgnoredFields($input): array + { + Arr::forget($input, $this->ignored); + return $input; + } + + protected function getRelationInputs($inputs = []): array + { + $relations = []; + foreach ($inputs as $column => $value) { + $column = Str::camel($column); + if (!method_exists($this->model, $column)) { + continue; + } + $relation = call_user_func([$this->model, $column]); + if ($relation instanceof Relation) { + $relations[$column] = $value; + } + } + return $relations; + } + + + protected function prepareInsert($inserts) + { + $prepared = []; + $columns = Schema::getColumnListing($this->model()->getTable()); + foreach ($inserts as $key => $value) { + if (in_array($key, $columns)) { + Arr::set($prepared, $key, $value); + } + } + return $prepared; + } + + public function getRelations(): array + { + $relations = []; + $columns = collect($this->formItems)->map(function (FormItem $item) { + return $item->getProp(); + })->toArray(); + + foreach (Arr::flatten($columns) as $column) { + + if (Str::contains($column, '.')) { + [$relation] = explode('.', $column); + + if (method_exists($this->model, $relation) && + $this->model->$relation() instanceof Relation + ) { + + $relations[] = $relation; + } + } else if (method_exists($this->model, $column)) { + $relations[] = $column; + } + } + return array_unique($relations); + } + + public function store() + { + if (($result = $this->callSubmitted()) instanceof Response) { + return $result; + } + $data = request()->all(); + if ($validationMessages = $this->validatorData($data)) { + return UI::responseError($validationMessages); + } + + if (($response = $this->prepare($data)) instanceof Response) { + return $response; + } + + DB::transaction(function () use ($data) { + $inserts = $this->prepareInsert($this->updates); + foreach ($inserts as $key => $value) { + $this->model->setAttribute($key, $value); + } + $this->model->save(); + $this->updateRelation($this->relations); + if (($result = $this->callDbTransaction()) instanceof Response) { + throw new \Exception(400, $result->getBody()->getContents()); + } + }); + if (($result = $this->callSaved()) instanceof Response) { + return $result; + } + return UI::responseMessage('保存成功'); + } + + /** + * 编辑 + * + * @param $id + * + * @return array|string + */ + public function edit($id = 0) + { + $this->isEdit = true; + $this->setMode(self::MODE_EDIT); + $this->setResourceId($id); + return $this; + } + + protected function deleteFiles(Model $model, $forceDelete = false) + { + $data = $model->toArray(); + collect($this->formItems)->filter(function (FormItem $formItem) { + return $formItem->getComponent() instanceof Upload; + })->each(function (FormItem $formItem) use ($data) { + $formItem->setOriginal($data); + /**@var Upload $component */ + $component = $formItem->getComponent(); + $component->destroy($formItem); + }); + } + + /** + * 模型删除 + * @param $id + * @return mixed + */ + public function destroy($id) + { + try { + if (($ret = $this->callDeleting($id)) instanceof Response) { + return $ret; + } + collect(explode(',', $id))->each(function ($id) { + $builder = $this->model()->newQuery(); + $relations = $this->getRelations(); + $this->model = $model = $builder->with($relations)->findOrFail($id); + //删除文件 + $this->deleteFiles($model); + //删除关联模型数据 + $this->deleteRelation($relations); + $model->delete(); + }); + if (($ret = $this->callDeleted()) instanceof Response) { + return $ret; + } + return UI::responseMessage('删除成功'); + } catch (\Exception $exception) { + return UI::responseError($exception->getMessage() ?: '删除成功'); + } + } + + /** + * @param $id + * @param null $data + * + * @return \Illuminate\Http\JsonResponse + * @throws \Throwable + */ + public function update($id, $data = null) + { + $this->isEdit = true; + + if (($result = $this->callSubmitted()) instanceof Response) { + return $result; + } + $data = ($data) ?: request()->all(); + + $this->setResourceId($id); + + $builder = $this->model(); + $this->model = $builder->findOrFail($id); + + + $this->validatorData($data); + + if (($response = $this->prepare($data)) instanceof Response) { + return $response; + } + DB::transaction(function () use ($data) { + $updates = $this->prepareUpdate($this->updates); + foreach ($updates as $key => $value) { + $this->model->setAttribute($key, $value); + } + $this->model->save(); + $this->updateRelation($this->relations); + if (($result = $this->callDbTransaction()) instanceof Response) { + throw new \Exception(400, $result->getBody()->getContents()); + } + }); + + if (($result = $this->callSaved()) instanceof Response) { + return $result; + } + + return UI::responseMessage('修改成功'); + } + + protected function prepareUpdate(array $updates, $oneToOneRelation = false) + { + $prepared = []; + $columns = Schema::getColumnListing($this->model()->getTable()); + foreach ($updates as $key => $value) { + if (in_array($key, $columns)) { + Arr::set($prepared, $key, $value); + } + } + return $prepared; + } + + private function deleteRelation($relations) + { + + foreach ($relations as $name) { + if (!method_exists($this->model, $name)) { + continue; + } + $relation = $this->model->$name(); + switch (true) { + case $relation instanceof Relations\HasOne: + $relation->delete(); + break; + } + + } + } + + private function updateRelation($relationsData) + { + + foreach ($relationsData as $name => $values) { + if (!method_exists($this->model, $name)) { + continue; + } + $relation = $this->model->$name(); + + $oneToOneRelation = $relation instanceof Relations\HasOne + || $relation instanceof Relations\MorphOne + || $relation instanceof Relations\BelongsTo; + + //$prepared = $this->prepareUpdate([$name => $values], $oneToOneRelation); + + $prepared = [$name => $values]; + + if (empty($prepared)) { + continue; + } + switch (true) { + case $relation instanceof Relations\BelongsToMany: + case $relation instanceof Relations\MorphToMany: + if (isset($prepared[$name])) { + $relation->sync($prepared[$name]); + } + break; + case $relation instanceof Relations\HasOne: + + $related = $this->model->$name; + + // if related is empty + if (is_null($related)) { + $related = $relation->getRelated(); + $qualifiedParentKeyName = $relation->getQualifiedParentKeyName(); + $localKey = Arr::last(explode('.', $qualifiedParentKeyName)); + $related->{$relation->getForeignKeyName()} = $this->model->{$localKey}; + } + + foreach ($prepared[$name] as $column => $value) { + $related->setAttribute($column, $value); + } + + $related->save(); + break; + case $relation instanceof Relations\BelongsTo: + case $relation instanceof Relations\MorphTo: + + $parent = $this->model->$name; + + // if related is empty + if (is_null($parent)) { + $parent = $relation->getRelated(); + } + + foreach ($prepared[$name] as $column => $value) { + $parent->setAttribute($column, $value); + } + + $parent->save(); + // When in creating, associate two models + if (!$this->model->{$relation->getForeignKeyName()}) { + $this->model->{$relation->getForeignKeyName()} = $parent->getKey(); + $this->model->save(); + } + break; + case $relation instanceof Relations\MorphOne: + $related = $this->model->$name; + if ($related === null) { + $related = $relation->make(); + } + foreach ($prepared[$name] as $column => $value) { + $related->setAttribute($column, $value); + } + $related->save(); + break; + case $relation instanceof Relations\HasMany: + case $relation instanceof Relations\MorphMany: + + foreach ($prepared[$name] as $related) { + /** @var Relations\Relation $relation */ + $relation = $this->model()->$name(); + + $keyName = $relation->getRelated()->getKeyName(); + + $instance = $relation->findOrNew(Arr::get($related, $keyName)); + + //处理已删除的关联 + try { + if ($related[static::REMOVE_FLAG_NAME] == 1) { + $instance->delete(); + continue; + } + Arr::forget($related, static::REMOVE_FLAG_NAME); + } catch (\Exception $exception) { + + } + //过滤不存在的字段 + foreach ($related as $key => $value) { + if (Schema::hasColumn($instance->getTable(), $key)) { + $instance->setAttribute($key, $value); + } + + } + $instance->save(); + } + + break; + } + } + } + + /** + * 获取编辑数据 + * + * @param $id + * + * @return array + */ + public function editData($id) + { + $this->isEdit = true; + if (($result = $this->callEditing($id)) instanceof Response) { + return $result; + } + $this->setMode(self::MODE_EDIT); + $this->setResourceId($id); + $this->editData = $this->model = $this->model->with($this->getRelations())->findOrFail($this->getResourceId()); + $data = []; + /**@var FormItem $formItem */ + foreach ($this->formItems as $formItem) { + $field = $formItem->getField(); + $prop = $formItem->getProp(); + $component = $formItem->getComponent(); + // 利用model的hidden属性 + if (in_array($prop, $this->model->getHidden())) { + Arr::set($data, $prop, $formItem->getData(null, $this->model, $component)); + } else { + Arr::set($data, $prop, $formItem->getData(Arr::get($this->editData, $prop), $this->model, $component)); + } + } + foreach ($this->formItems as $formItem) { + $prop = $formItem->getProp(); + if ($formItem->getCopyProp()) { + Arr::set($data, $prop, Arr::get($data, $formItem->getCopyProp())); + } + } + $this->editData = $data; + if (($result = $this->callEdiQuery($data)) instanceof Response) { + return $result; + } + return [ + 'code' => 200, + 'data' => $this->editData, + ]; + } + + /** + * 设置是否加载数据 + * + * @param bool $isGetData + * + * @return $this + */ + public function isGetData(bool $isGetData) + { + $this->isGetData = $isGetData; + return $this; + } + + + /** + * @inheritDoc + */ + public function jsonSerialize() + { + if ($this->isGetData) { + return $this->editData($this->getResourceId()); + } + $this->items($this->formItems); + return array_filter([ + 'componentName' => $this->componentName, + 'action' => $this->getAction(), + 'dataUrl' => $this->dataUrl, + 'mode' => $this->getMode(), + 'attrs' => $this->attrs, + 'ignoreEmptyProps' => $this->ignoreEmptyProps, + 'formItemLayout' => $this->formItemLayout, + 'tabPosition' => $this->tabPosition, + 'defaultValues' => (object)array_merge($this->formItemsValue, $this->formValue), + 'formRules' => (object)$this->formRules, + 'ref' => $this->ref, + 'refData' => $this->refData, + 'formRefData' => $this->FormRefDataBuild(), + 'top' => $this->top, + 'bottom' => $this->bottom, + 'actions' => $this->actions->builderActions() + ]); + } + + /** + * 填充表单默认值 + * + * @param $name + * @param string $value + * + * @return $this + */ + public function setFormValue($name, $value = '') + { + if (is_array($name)) { + $this->formValue = $name; + return $this; + } + if ($value === null) { + unset($this->formValue[$name]); + return $this; + } + $this->formValue[$name] = $value; + return $this; + } + + public function __get($name) + { + return $this->input($name); + } + + public function __set($name, $value) + { + return Arr::set($this->inputs, $name, $value); + } +} diff --git a/builder/View/Form/FormActions.php b/builder/View/Form/FormActions.php new file mode 100644 index 0000000..34fe62f --- /dev/null +++ b/builder/View/Form/FormActions.php @@ -0,0 +1,134 @@ +form = $form; + $this->cancelButton = new Button("取消"); + $this->cancelButton->type("default"); + $this->submitButton = new Button("提交"); + } + + /** + * 添加自定义Action + * @param $action + * @return $this + */ + public function addLeft($action) + { + if ($action instanceof \Closure) { + $this->addLeftActions = collect($this->addLeftActions)->push(call_user_func($action))->all(); + } else { + $this->addLeftActions = collect($this->addLeftActions)->push($action)->all(); + } + return $this; + } + + /** + * 添加自定义Action + * @param $action + * @return $this + */ + public function addRight($action) + { + if ($action instanceof \Closure) { + $this->addRightActions = collect($this->addRightActions)->push(call_user_func($action))->all(); + } else { + $this->addRightActions = collect($this->addRightActions)->push($action)->all(); + } + return $this; + } + + /** + * 获取取消按钮对象,注意此按钮只支持基本按钮属性 + * @return Button + */ + public function cancelButton() + { + return $this->cancelButton; + } + + /** + * 获取提交按钮对象,注意此按钮只支持基本按钮属性 + * @return Button + */ + public function submitButton() + { + return $this->submitButton; + } + + + /** + * 隐藏取消按钮 + * @return $this + */ + public function hideCancelButton() + { + $this->hideCancelButton = true; + return $this; + } + + /** + * 隐藏提交按钮 + * @return $this + */ + public function hideSubmitButton() + { + $this->hideSubmitButton = true; + return $this; + } + + /** + * 固定操作栏 + * @return $this + */ + public function fixed() + { + $this->fixed = true; + return $this; + } + + /** + * @return Form + */ + public function getForm() + { + return $this->form; + } + + public function builderActions() + { + $addLeftActions = collect($this->addLeftActions); + $addRightActions = collect($this->addRightActions); + $cancelButton = null; + if (!$this->hideCancelButton) { + $cancelButton = $this->cancelButton; + } + $submitButton = null; + if (!$this->hideSubmitButton) { + $submitButton = $this->submitButton; + } + return [ + 'addLeftActions' => $addLeftActions, + 'addRightActions' => $addRightActions, + 'cancelButton' => $cancelButton, + 'submitButton' => $submitButton, + 'fixed' => $this->fixed, + ]; + } +} diff --git a/builder/View/Form/FormAttrs.php b/builder/View/Form/FormAttrs.php new file mode 100644 index 0000000..d5cdbf5 --- /dev/null +++ b/builder/View/Form/FormAttrs.php @@ -0,0 +1,26 @@ + null, + 'value' => null, + 'anyValue' => false, + ]; + + protected $vifEval; + + /** + * FormItem constructor. + * + * @param $prop + * @param $label + * @param $field + */ + public function __construct($prop, $label, $field) + { + $this->prop = $prop; + $this->label = $this->formatLabel($label); + $this->field = $field; + if (empty($this->field)) { + $this->field = $this->prop; + } + if (Str::contains($prop, '.')) { + [$relationName, $relationValueKey] = explode('.', $prop); + $this->relationName = $relationName; + $this->relationValueKey = $relationValueKey; + } + $this->component = Input::make(); + } + + + /** + * @return mixed + */ + public function getLabel() + { + return $this->label; + } + + protected function formatLabel($label) + { + if ($label) { + return $label; + } + $label = ucfirst($this->prop); + return str_replace(['.', '_'], ' ', $label); + } + + public function setOriginal($data) + { + $this->original = Arr::get($data, $this->prop); + } + + /** + * 隐藏Label + * @return $this + */ + public function hideLabel() + { + $this->labelWidth("0px"); + $this->hideLabel = true; + return $this; + } + + + /** + * 设置头部组件 + * + * @param $component + * + * @return $this + */ + public function topComponent($component) + { + if ($component instanceof \Closure) { + $this->topComponent = call_user_func($component); + } else { + $this->topComponent = $component; + } + return $this; + } + + /** + * 表单域组件上面附加组件 + * + * @param $component + * + * @return $this + */ + public function componentTopComponent($component) + { + if ($component instanceof \Closure) { + $this->componentTopComponent = call_user_func($component); + } else { + $this->componentTopComponent = $component; + } + return $this; + } + + /** + * 表单域组件下面附加组件 + * + * @param $component + * + * @return $this + */ + public function componentBottomComponent($component) + { + if ($component instanceof \Closure) { + $this->componentBottomComponent = call_user_func($component); + } else { + $this->componentBottomComponent = $component; + } + return $this; + } + + /** + * 表单域组件左边附加组件 + * + * @param $component + * + * @return $this + */ + public function componentLeftComponent($component) + { + if ($component instanceof \Closure) { + $this->componentLeftComponent = call_user_func($component); + } else { + $this->componentLeftComponent = $component; + } + return $this; + } + + /** + * 表单域组件右边附加组件 + * + * @param $component + * + * @return $this + */ + public function componentRightComponent($component) + { + if ($component instanceof \Closure) { + $this->componentRightComponent = call_user_func($component); + } else { + $this->componentRightComponent = $component; + } + return $this; + } + + + /** + * 设置底部组件 + * + * @param $component + * + * @return $this + * @deprecated + */ + public function footerComponent($component) + { + if ($component instanceof \Closure) { + $this->footerComponent = call_user_func($component); + } else { + $this->footerComponent = $component; + } + return $this; + } + + /** + * 设置底部组件 + * + * @param $component + * + * @return $this + */ + public function bottomComponent($component) + { + if ($component instanceof \Closure) { + $this->footerComponent = call_user_func($component); + } else { + $this->footerComponent = $component; + } + return $this; + } + + /** + * 设置组件 + * + * @param $component + * + * @return $this + */ + public function component($component) + { + if ($component instanceof \Closure) { + $this->component = call_user_func($component); + } else { + $this->component = $component; + } + return $this; + } + + /** + * @return mixed + */ + public function getComponent() + { + return $this->component; + } + + public function setForm($form) + { + $this->form = $form; + } + + /** + * @return mixed + */ + public function getProp() + { + return $this->prop; + } + + /** + * @return mixed + */ + public function getField() + { + return $this->field; + } + + /** + * 后端验证规则 + * + * @param string|array $serveRules + * + * @return $this + */ + public function serveRules($serveRules) + { + $this->serveRules = $serveRules; + return $this; + } + + /** + * @param mixed $serveCreationRules + * + * @return FormItem + */ + public function serveCreationRules($serveCreationRules) + { + $this->serveCreationRules = $serveCreationRules; + return $this; + } + + /** + * @param mixed $serveUpdateRules + * + * @return FormItem + */ + public function serveUpdateRules($serveUpdateRules) + { + $this->serveUpdateRules = $serveUpdateRules; + return $this; + } + + /** + * @param mixed $serveRulesMessage + * + * @return FormItem + */ + public function serveRulesMessage($serveRulesMessage) + { + $this->serveRulesMessage = $serveRulesMessage; + return $this; + } + + /** + * 设置默认值 + * + * @param mixed $defaultValue + * + * @return $this + */ + public function defaultValue($defaultValue) + { + $this->defaultValue = $defaultValue; + + if ($this->component) $this->component->componentValue($defaultValue); + + return $this; + } + + /** + * @return mixed + */ + public function getDefaultValue() + { + return $this->defaultValue !== null ? $this->defaultValue : $this->component->getComponentValue(); + } + + /** + * 复制其他组件的值 + * + * @param string $copyProp + * + * @return $this + */ + public function copyValue($copyProp) + { + $this->copyProp = $copyProp; + + return $this; + } + + /** + * @return mixed + */ + public function getCopyProp() + { + return $this->copyProp; + } + + public function getData($data, $model, $component) + { + if (!method_exists($model, $this->prop)) { + if (method_exists($component, "getValue")) { + return $component->getValue($data); + } + return $data; + } else { + if ($model->{$this->prop}() instanceof BelongsToMany) { + /**@var BelongsToMany $re */ + $re = $model->{$this->prop}(); + $data = collect($data)->pluck($re->getRelatedKeyName()); + } + } + return $data; + } + + public function getServeRole() + { + + if (request()->isMethod('POST')) { + $rules = $this->serveCreationRules ?: $this->serveRules; + } else if (request()->isMethod('PUT')) { + $rules = $this->serveUpdateRules ?: $this->serveRules; + } else { + $rules = $this->rules; + } + + if ($rules instanceof \Closure) { + $rules = $rules->call($this, $this->form); + } + + if (is_string($rules)) { + $rules = array_filter(explode('|', $rules)); + } + + if (!$this->form) { + return $rules; + } + + if (!$id = $this->form->model()->getKey()) { + return $rules; + } + + if (is_array($rules)) { + foreach ($rules as &$rule) { + if (is_string($rule)) { + $rule = str_replace('{{id}}',(string)$id, $rule); + } + } + } + + return $rules; + } + + /** + * @return mixed + */ + public function getServeRulesMessage() + { + return $this->serveRulesMessage; + } + + /** + * 表单域标签的的宽度,例如 '50px'。支持 auto + * + * @param mixed $labelWidth + * + * @return $this + */ + public function labelWidth($labelWidth) + { + if ($labelWidth == "auto") return $this; + $this->labelWidth = $labelWidth; + return $this; + } + + /** + * 表单域输入区域宽度 1 - 24 + * + * @param int $inputWidth + * + * @return $this + */ + public function inputWidth(int $inputWidth) + { + $this->inputWidth = $inputWidth; + return $this; + } + + /** + * 是否必填,如不设置,则会根据校验规则自动生成 + * + * @param null $message + * @param string $type + * @param string $trigger + * + * @return $this + */ + public function required($type = "string", $message = null, $trigger = "blur") + { + $this->required = true; + $message = $message ?? '请填写' . $this->label; + if (!$this->serveRules) { + $this->serveRules('required'); + $this->serveRulesMessage(['required' => $message]); + } + if (!$this->rules) { + $this->vueRule(true, $type, $message, $trigger); + } + + return $this; + } + + /** + * @param mixed $rules + * + * @return $this + * @deprecated + * 表单验证规则, 请使用 vueRule ,多条规则可设置多次 + */ + public function rules($rules) + { + $this->rules = $rules; + return $this; + } + + /** + * 表单验证规则,多条规则可设置多次 + * + * @param bool $required + * @param string $type + * @param null $message + * @param string $trigger + * + * @return $this + */ + public function vueRule(bool $required = true, $type = "string", $message = null, $trigger = "blur") + { + $rule = ['type' => $type, 'required' => true, "message" => $message, "trigger" => $trigger]; + $this->rules = collect($this->rules)->push($rule)->all(); + return $this; + } + + /** + * 表单验证规则原生写法,多条规则可设置多次 + * + * @param array $raw + * + * @return $this + */ + public function vueRuleRaw(array $raw) + { + $this->rules = collect($this->rules)->push($raw)->all(); + return $this; + } + + /** + * @return mixed + */ + public function getRules() + { + return $this->rules; + } + + + /** + * 表单域验证错误信息, 设置该值会使表单验证状态变为error,并显示该错误信息 + * + * @param string $error + * + * @return $this + */ + public function error($error) + { + $this->error = $error; + return $this; + } + + /** + * 是否显示校验错误信息 + * + * @param bool $showMessage + * + * @return $this + */ + public function showMessage(bool $showMessage = true) + { + $this->showMessage = $showMessage; + return $this; + } + + /** + * 以行内形式展示校验信息 + * + * @param bool $inlineMessage + * + * @return $this + */ + public function inlineMessage($inlineMessage = true) + { + $this->inlineMessage = $inlineMessage; + return $this; + } + + /** + * 用于控制该表单域下组件的尺寸 + * medium / small / mini + * + * @param mixed $size + * + * @return $this + */ + public function size($size) + { + $this->size = $size; + return $this; + } + + /** + * 帮助信息,支持html + * + * @param $help + * + * @return $this + */ + public function help($help) + { + $this->help = $help; + return $this; + } + + /** + * @param $key + * @param string|array $values + * @param bool $anyValue + * + * @return $this + */ + public function vif($key, $values, $anyValue = false) + { + $values = !is_array($values) ? [$values] : $values; + + $this->vif = [ + 'key' => $key, + 'value' => $values, + 'anyValue' => $anyValue, + ]; + return $this; + } + + /** + * @param \Closure $closure + * + * @return FormItem + */ + public function vifEval(\Closure $closure) + { + $vifEval = new Form\Utils\VIfEval(); + + call_user_func($closure, $vifEval); + + $this->vifEval = $vifEval->build(); + return $this; + } + + + /** + * @param string $tab + * + * @return FormItem + * @deprecated 已抛弃,设置无效 + * 设置字段所属tab名称 + */ + public function tab(string $tab) + { + $this->tab = $tab; + return $this; + } + + /** + * @return string + */ + public function getTab(): string + { + return $this->tab; + } + + /** + * If Null Dont Return + * @return $this + */ + public function ignoreEmpty() + { + $this->ignoreEmpty = true; + return $this; + } + + /** + * @return bool + */ + public function isIgnoreEmpty(): bool + { + return $this->ignoreEmpty; + } + + + /** + * 传递当前组件所在模式 + * + * @param string $value + * + * @return $this + */ + public function hiddenMode($value = '') + { + $this->hiddenMode = $value; + return $this; + } + + /** + * @return mixed + */ + public function hiddenInCreate() + { + $this->hiddenMode = Form::MODE_CREATE; + return $this; + } + + /** + * @return mixed + */ + public function hiddenInEdit() + { + $this->hiddenMode = Form::MODE_EDIT; + return $this; + } + + /** + * @return mixed + */ + public function getHiddenMode() + { + return $this->hiddenMode; + } + + public function getAttrs() + { + return [ + 'componentName' => $this->componentName, + 'prop' => $this->prop, + 'label' => $this->label, + 'field' => $this->field, + 'hideLabel' => $this->hideLabel, + 'labelWidth' => $this->labelWidth, + 'inputWidth' => $this->inputWidth, + 'required' => $this->required, + //'rules' => $this->rules, + 'error' => $this->error, + 'showMessage' => $this->showMessage, + 'inlineMessage' => $this->inlineMessage, + 'size' => $this->size, + 'help' => $this->help, + 'component' => $this->component, + 'componentTopComponent' => $this->componentTopComponent, + 'componentBottomComponent' => $this->componentBottomComponent, + 'componentLeftComponent' => $this->componentLeftComponent, + 'componentRightComponent' => $this->componentRightComponent, + 'topComponent' => $this->topComponent, + 'footerComponent' => $this->footerComponent, + 'relationName' => $this->relationName, + 'relationValueKey' => $this->relationValueKey, + 'vif' => $this->vif, + 'vifEval' => $this->vifEval, + 'tab' => $this->tab, + 'ignoreEmpty' => $this->ignoreEmpty, + 'hidden' => $this->form->isMode($this->hiddenMode), + 'ref' => $this->ref, + 'refData' => $this->refData + ]; + } + + public function jsonSerialize() + { + return $this->getAttrs(); + } +} diff --git a/builder/View/Form/FormTab.php b/builder/View/Form/FormTab.php new file mode 100644 index 0000000..061d88c --- /dev/null +++ b/builder/View/Form/FormTab.php @@ -0,0 +1,58 @@ +name = $name; + $this->form = $form; + } + + /** + * @return mixed + */ + public function getName() + { + return $this->name; + } + + /** + * @param mixed $name + * @return FormTab + */ + public function name($name) + { + $this->name = $name; + return $this; + } + + + /** + * @param Row|\Closure $closure + * @return $this + */ + public function row($closure) + { + if ($closure instanceof \Closure) { + $row = new Row(); + call_user_func($closure, $row, $this->form); + $this->rows = collect($this->rows)->push($row); + } else { + $this->rows = collect($this->rows)->push($closure); + } + return $this; + } + + public function jsonSerialize() + { + return ['name' => $this->name, 'rows' => $this->rows]; + } +} \ No newline at end of file diff --git a/builder/View/Form/HasHooks.php b/builder/View/Form/HasHooks.php new file mode 100644 index 0000000..c4426ae --- /dev/null +++ b/builder/View/Form/HasHooks.php @@ -0,0 +1,225 @@ +hooks[$name][] = $callback; + return $this; + } + + /** + * Call hooks by giving name. + * + * @param string $name + * @param array $parameters + * + * @return Response + */ + protected function callHooks($name, $parameters = []) + { + $hooks = Arr::get($this->hooks, $name, []); + foreach ($hooks as $func) { + if (!$func instanceof Closure) { + continue; + } + $response = call_user_func($func, $this, $parameters); + if ($response instanceof Response) { + return $response; + } + } + } + + /** + * Set after getting editing model callback. + * + * @param Closure $callback + * + * @return $this + */ + public function editing(Closure $callback) + { + return $this->registerHook('editing', $callback); + } + + public function editQuery(Closure $callback) + { + return $this->registerHook('editQuery', $callback); + } + + /** + * Set submitted callback. + * + * @param Closure $callback + * + * @return $this + */ + public function submitted(Closure $callback) + { + return $this->registerHook('submitted', $callback); + } + + /** + * Set saving callback. + * + * @param Closure $callback + * + * @return $this + */ + public function saving(Closure $callback) + { + return $this->registerHook('saving', $callback); + } + + /** + * Set saved callback. + * + * @param Closure $callback + * + * @return $this + */ + public function saved(Closure $callback) + { + return $this->registerHook('saved', $callback); + } + + /** + * 数据事务处理时回调 + * @param Closure $callback + * @return HasHooks + */ + public function DbTransaction(Closure $callback) + { + return $this->registerHook('DbTransaction', $callback); + } + + /** + * 表单验证时回调 + * @param Closure $callback + * @return HasHooks + */ + public function validating(Closure $callback) + { + return $this->registerHook('validating', $callback); + } + + /** + * @param Closure $callback + * + * @return $this + */ + public function deleting(Closure $callback) + { + return $this->registerHook('deleting', $callback); + } + + /** + * @param Closure $callback + * + * @return $this + */ + public function deleted(Closure $callback) + { + return $this->registerHook('deleted', $callback); + } + + /** + * Call editing callbacks. + * + * @param $id + * @return mixed + */ + protected function callEditing($id) + { + return $this->callHooks('editing', $id); + } + + /** + * @param $data + * @return mixed + */ + protected function callEdiQuery($data) + { + return $this->callHooks('editQuery', $data); + } + + /** + * Call submitted callback. + * + * @return mixed + */ + protected function callSubmitted() + { + return $this->callHooks('submitted'); + } + + /** + * Call saving callback. + * + * @return mixed + */ + protected function callSaving() + { + return $this->callHooks('saving'); + } + + /** + * Callback after saving a Model. + * + * @return mixed|null + */ + protected function callSaved() + { + return $this->callHooks('saved'); + } + + /** + * Call hooks when deleting. + * + * @param mixed $id + * + * @return mixed + */ + protected function callDeleting($id) + { + return $this->callHooks('deleting', $id); + } + + /** + * @return mixed + */ + protected function callDeleted() + { + return $this->callHooks('deleted'); + } + + protected function callDbTransaction() + { + return $this->callHooks('DbTransaction'); + } + + protected function callValidating($validator) + { + return $this->callHooks('validating', $validator); + } + +} diff --git a/builder/View/Form/HasRef.php b/builder/View/Form/HasRef.php new file mode 100644 index 0000000..5890e07 --- /dev/null +++ b/builder/View/Form/HasRef.php @@ -0,0 +1,34 @@ +successRefData = [ + 'ref' => $ref, + "data" => $data + ]; + return $this; + } + + public function FormRefDataBuild() + { + return [ + 'successRefData' => $this->successRefData + ]; + } + +} diff --git a/builder/View/Form/Model.php b/builder/View/Form/Model.php new file mode 100644 index 0000000..c848c41 --- /dev/null +++ b/builder/View/Form/Model.php @@ -0,0 +1,34 @@ +model = $model; + $this->originalModel = $model; + $this->form = $form; + } + + public function editData(){ + return $this->model->findOrFail($this->form->getResourceId()); + } +} diff --git a/builder/View/Form/TraitFormAttrs.php b/builder/View/Form/TraitFormAttrs.php new file mode 100644 index 0000000..4812173 --- /dev/null +++ b/builder/View/Form/TraitFormAttrs.php @@ -0,0 +1,218 @@ +attrs->className = $className; + return $this; + } + + public function style($style) + { + $this->attrs->style = $style; + return $this; + } + + /** + * 表单验证规则 + * @param array $rules + * @return $this + */ + public function rules($rules) + { + $this->attrs->rules = $rules; + return $this; + } + + /** + * 行内表单模式 + * @param bool $inline + * @return $this + */ + public function inline(bool $inline = true) + { + $this->attrs->inline = $inline; + return $this; + } + + /** + * 表单域标签的位置,如果值为 left 或者 right 时,则需要设置 label-width + * right/left/top + * @param string $labelPosition + * @return $this + */ + public function labelPosition(string $labelPosition) + { + $this->attrs->labelPosition = $labelPosition; + return $this; + } + + /** + * 表单域标签的宽度,例如 '50px'。作为 Form 直接子元素的 form-item 会继承该值。支持 auto + * @param string $labelWidth + * @return $this + */ + public function labelWidth(string $labelWidth) + { + $this->attrs->labelWidth = $labelWidth; + return $this; + } + + /** + * 表单域标签的后缀 + * @param string $labelSuffix + * @return $this + */ + public function labelSuffix(string $labelSuffix) + { + $this->attrs->labelSuffix = $labelSuffix; + return $this; + } + + /** + * 是否显示必填字段的标签旁边的红色星号 + * @param bool $hideRequiredAsterisk + * @return $this + */ + public function hideRequiredAsterisk(bool $hideRequiredAsterisk = true) + { + $this->attrs->hideRequiredAsterisk = $hideRequiredAsterisk; + return $this; + } + + /** + * 是否显示校验错误信息 + * @param bool $showMessage + * @return $this + */ + public function showMessage(bool $showMessage = true) + { + $this->attrs->showMessage = $showMessage; + return $this; + } + + /** + * + * 是否以行内形式展示校验信息 + * @param bool $inlineMessage + * @return $this + */ + public function inlineMessage(bool $inlineMessage = true) + { + $this->attrs->inlineMessage = $inlineMessage; + return $this; + } + + /** + * 是否在输入框中显示校验结果反馈图标 + * @param bool $statusIcon + * @return $this + */ + public function statusIcon(bool $statusIcon = true) + { + $this->attrs->statusIcon = $statusIcon; + return $this; + } + + /** + * 是否在 rules 属性改变后立即触发一次验证 + * @param bool $validateOnRuleChange + * @return $this + */ + public function validateOnRuleChange(bool $validateOnRuleChange = true) + { + $this->attrs->validateOnRuleChange = $validateOnRuleChange; + return $this; + } + + /** + * 尺寸 medium / small / mini + * 用于控制该表单内组件的尺寸 + * @param mixed $size + * @return $this + */ + public function size($size) + { + $this->attrs->size = $size; + return $this; + } + + /** + * 是否禁用该表单内的所有组件。若设置为 true,则表单内组件上的 disabled 属性不再生效 + * @param bool $disabled + * @return $this + */ + public function disabled(bool $disabled = true) + { + $this->attrs->disabled = $disabled; + return $this; + } + + /** + * 隐藏tab + * @param bool $hideTab + * @return $this + */ + public function hideTab(bool $hideTab = true) + { + $this->attrs->hideTab = $hideTab; + return $this; + } + + /** + * 创建按钮名称 + * @param $createButtonName + * @return $this + */ + public function createButtonName($createButtonName) + { + $this->attrs->createButtonName = $createButtonName; + return $this; + } + + /** + * 修改按钮名称 + * @param $updateButtonName + * @return $this + */ + public function updateButtonName($updateButtonName) + { + $this->attrs->updateButtonName = $updateButtonName; + return $this; + } + + /** + * 返回按钮名称 + * @param $backButtonName + * @return $this + */ + public function backButtonName($backButtonName) + { + $this->attrs->backButtonName = $backButtonName; + return $this; + } + + public function buttonWidth($buttonWidth) + { + $this->attrs->buttonWidth = $buttonWidth; + return $this; + } + + /** + * 弹窗模式 + * @return $this + */ + public function isDialog() + { + $this->attrs->isDialog = true; + $this->action = $this->resource(0); + return $this; + } +} diff --git a/builder/View/Form/Utils/VIfEval.php b/builder/View/Form/Utils/VIfEval.php new file mode 100644 index 0000000..debd529 --- /dev/null +++ b/builder/View/Form/Utils/VIfEval.php @@ -0,0 +1,58 @@ +functionPath = $functionPath; + return $this; + } + + /** + * @param mixed $functionStr + * @return VIfEval + */ + public function functionStr($functionStr) + { + $this->functionStr = $functionStr; + return $this; + } + + /** + * @param array $props + * @return VIfEval + */ + public function props(array $props) + { + $this->props = $props; + return $this; + } + + + public function build() + { + $expression = ""; + if ($this->functionStr) { + $expression = $this->functionStr; + } + if ($this->functionPath) { + if (!file_exists($this->functionPath)) { + throw new BusinessException(400, 'functionPath文件不存在'); + } + $expression = file_get_contents($this->functionPath); + } + return [ + "expression" => $expression, + "props" => $this->props + ]; + } +} diff --git a/builder/View/Grid.php b/builder/View/Grid.php new file mode 100644 index 0000000..275579d --- /dev/null +++ b/builder/View/Grid.php @@ -0,0 +1,483 @@ +attributes = new Attributes(); + $this->dataUrl = admin_api_url(request()->path()) . '/list'; + $this->model = new Model($model, $this); + if ($model) { + $this->table = $model->getTable(); + $this->keyName = $model->getKeyName(); + $this->defaultSort($model->getKeyName(), "asc"); + } +// $this->isGetData = request('get_data') == "true"; + $this->isGetData = request()->header('getData')=="true"; + $this->toolbars = new Toolbars($this); + $this->batchActions = new BatchActions(); + $this->filter = new Filter($this->model); + } + + /** + * 获取自定义数据模型 + * @return Model|Builder + */ + public function model() + { + return $this->model; + } + + /** + * @return string + */ + public function getKeyName(): string + { + return $this->keyName; + } + + /** + * 自定义数据源路径 + * @param string $dataUrl + * @return $this + */ + public function dataUrl(string $dataUrl) + { + $this->dataUrl = $dataUrl; + return $this; + } + + /** + * @param string $method + * @return $this + */ + public function method(string $method) + { + $this->method = $method; + return $this; + } + + /** + * @return array + */ + public function getAppendFields(): array + { + return $this->appendFields; + } + + /** + * 数据返回附加字段 + * @param array $appendFields + * @return $this + */ + public function appendFields(array $appendFields) + { + $this->appendFields = $appendFields; + return $this; + } + + + /** + * @return bool + */ + public function isGetData(): bool + { + return $this->isGetData; + } + + + /** + * 设置树形表格 + * @param bool $tree + * @return $this + */ + public function tree($tree = true) + { + $this->tree = $tree; + return $this; + } + + + /** + * Grid添加字段 + * @param string $name 对应列内容的字段名 + * @param string $label 显示的标题 + * @param string $columnKey 排序查询等数据操作字段名称 + * @return Column + */ + public function column($name, $label = '', $columnKey = null) + { + if (Str::contains($name, '.')) { + $this->addRelationColumn($name, $label); + } + + return $this->addColumn($name, $label, $columnKey); + } + + /** + * @param string $name + * @param string $label + * @param $columnKey + * @return Column + */ + protected function addColumn($name = '', $label = '', $columnKey = null) + { + $column = new Column($name, $label, $columnKey); + $column->setGrid($this); + $this->columns[] = $column; + return $column; + } + + /** + * Add a relation column to grid. + * + * @param string $name + * @param string $label + * + * @return $this|bool|Column + */ + protected function addRelationColumn($name, $label = '') + { + if ($this->model) { + list($relation, $column) = explode('.', $name); + $model = $this->model()->eloquent(); + if (!method_exists($model, $relation) || !$model->{$relation}() instanceof Relations\Relation) { + } else { + $this->model()->with($relation); + } + } + } + + /** + * @param Column[] $columns + */ + protected function columns($columns) + { + $this->columnAttributes = collect($columns)->map(function (Column $column) { + return $column->getAttributes(); + })->toArray(); + } + + public function getColumns() + { + return $this->columns; + } + + public function applyWhere() + { + //快捷搜索 + $this->applyQuickSearch(); + } + + public function applyQuery() + { + //快捷搜索 + $this->applyQuickSearch(); + + $this->applyFilter(false); + } + + /** + * 自定义toolbars + * @param $closure + * @return $this + */ + public function toolbars($closure) + { + call_user_func($closure, $this->toolbars); + return $this; + } + + /** + * 自定义行操作 + * @param $closure + * @return $this + */ + public function actions($closure) + { + $this->actions = $closure; + return $this; + } + + /** + * 自定义批量操作 + * @param \Closure $closure + * @return $this + */ + public function batchActions(\Closure $closure) + { + call_user_func($closure, $this->batchActions); + return $this; + } + + /** + * 获取行操作 + * @param $row + * @param $key + * @return mixed + */ + public function getActions($row, $key) + { + $actions = new Actions($this); + $actions->row($row)->key($key); + if ($this->actions) call_user_func($this->actions, $actions); + return $actions->builderActions(); + } + + /** + * @param Form $dialogForm + * @param $width + * @param $title + * @return Grid + */ + public function dialogForm(Form $dialogForm, $width = '750px', $title = ['添加', '修改']) + { + $this->dialogForm = $dialogForm; + // $this->dialogForm->labelPosition('right')->labelWidth('90px'); + $this->dialogFormWidth = $width; + $this->dialogTitle = $title; + return $this; + } + + /** + * @return Form + */ + public function getDialogForm() + { + return $this->dialogForm; + } + + + /** + * @param $closure + * @return $this + */ + public function top($closure) + { + $this->top = new Content(); + call_user_func($closure, $this->top); + return $this; + } + + /** + * @param $closure + * @return $this + */ + public function bottom($closure) + { + $this->bottom = new Content(); + call_user_func($closure, $this->bottom); + return $this; + } + + + /** + * 自定义数据 + * @param $data + * @param $current_page + * @param $per_page + * @param $last_page + * @param $total + * @return $this + */ + public function customData($data, $current_page = 0, $per_page = 0, $last_page = 0, $total = 0) + { + $this->customData = [ + 'current_page' => (int)$current_page, + 'per_page' => (int)$per_page, + 'last_page' => (int)$last_page, + 'total' => (int)$total, + 'data' => $data, + ]; + return $this; + } + + + /** + * data + * @return array + */ + protected function data() + { + if ($this->customData) { + $this->customData['data'] = $this->model()->displayData($this->customData['data']); + return [ + 'code' => 200, + 'data' => $this->isHidePage() ? $this->customData['data'] : $this->customData + ]; + } + $this->applyQuery(); + $data = $this->model->buildData(); + return [ + 'code' => 200, + 'data' => $data + ]; + } + + /** + * @inheritDoc + */ + public function jsonSerialize() + { + if (count($this->columnAttributes) <= 0) { + $this->columns($this->columns); + } + if ($this->isGetData) { + return $this->data(); + } else { + $viewData['componentName'] = $this->componentName; + $viewData['routers'] = [ + 'resource' => admin_api_url(request()->path()), + ]; + $viewData['keyName'] = $this->keyName; + $viewData['selection'] = $this->attributes->selection; + $viewData['tree'] = $this->tree; + $viewData['defaultSort'] = $this->defaultSort; + $viewData['columnAttributes'] = $this->columnAttributes; + $viewData['attributes'] = (array)$this->attributes; + $viewData['dataUrl'] = $this->dataUrl; + $viewData['export'] = $this->enableExport; + $viewData['method'] = $this->method; + $viewData['hidePage'] = $this->isHidePage(); + $viewData['pageSizes'] = $this->pageSizes; + $viewData['perPage'] = $this->perPage; + $viewData['pageLayout'] = $this->pageLayout; + $viewData['pageBackground'] = $this->pageBackground; + $viewData['toolbars'] = $this->toolbars->builderData(); + $viewData['batchActions'] = $this->batchActions->builderActions(); + $viewData['quickSearch'] = $this->quickSearch; + $viewData['filter'] = $this->filter->buildFilter(); + $viewData['top'] = $this->top; + $viewData['bottom'] = $this->bottom; + $viewData['dialogForm'] = $this->dialogForm; + $viewData['dialogFormWidth'] = $this->dialogFormWidth; + $viewData['dialogTitle'] = $this->dialogTitle; + $viewData['ref'] = $this->getRef(); + return $viewData; + } + } + + /** + * @return string + */ + public function getComponentName(): string + { + return $this->componentName; + } + + /** + * @param string $componentName + */ + public function setComponentName(string $componentName) + { + $this->componentName = $componentName; + return $this; + } + + /** + * @param bool $isGetData + */ + public function setGetData(bool $isGetData): void + { + $this->isGetData = $isGetData; + } + + public function getTable() + { + return $this->table; + } +} diff --git a/builder/View/Grid/Actions.php b/builder/View/Grid/Actions.php new file mode 100644 index 0000000..91a67c3 --- /dev/null +++ b/builder/View/Grid/Actions.php @@ -0,0 +1,53 @@ +row; + } + + /** + * @param mixed $row + * @return $this + */ + public function row($row) + { + $this->row = $row; + return $this; + } + + /** + * 当前行下标 + * @return mixed + */ + public function getKey() + { + return $this->key; + } + + /** + * @param mixed $key + * @return $this + */ + public function key($key) + { + $this->key = $key; + return $this; + } + + +} diff --git a/builder/View/Grid/Actions/ActionButton.php b/builder/View/Grid/Actions/ActionButton.php new file mode 100644 index 0000000..3388e2d --- /dev/null +++ b/builder/View/Grid/Actions/ActionButton.php @@ -0,0 +1,61 @@ +content = $content; + $this->type("text"); + } + + /** + * @param string $content 按钮内容 + * @return ActionButton + */ + public static function make($content) + { + return new ActionButton($content); + } + + /** + * @param mixed $uri + * @return $this + */ + public function uri($uri) + { + $this->uri = $uri; + return $this; + } + + /** + * @param string $handler 响应类型 request|route|link + * @return $this + */ + public function handler($handler) + { + if (!in_array($handler, [self::HANDLER_LINK, self::HANDLER_REQUEST, self::HANDLER_ROUTE])) { + throw new BusinessException(400, "ActionButton 事件类型错误"); + }; + $this->handler = $handler; + return $this; + } + + public function route($uri) + { + $this->uri = $uri; + $this->handler = self::HANDLER_ROUTE; + return $this; + } +} diff --git a/builder/View/Grid/Actions/ActionLink.php b/builder/View/Grid/Actions/ActionLink.php new file mode 100644 index 0000000..087dd11 --- /dev/null +++ b/builder/View/Grid/Actions/ActionLink.php @@ -0,0 +1,46 @@ +content = $content; + } + + protected static function make($content) + { + return new ActionLink($content); + } + + /** + * @param mixed $uri + * @return $this + */ + public function uri($uri) + { + $this->uri = $uri; + $this->href = $uri; + return $this; + } + + /** + * @param string $handler 响应类型 request|route|link + * @return $this + */ + public function handler($handler) + { + $this->handler = $handler; + return $this; + } +} diff --git a/builder/View/Grid/Actions/DeleteAction.php b/builder/View/Grid/Actions/DeleteAction.php new file mode 100644 index 0000000..f134300 --- /dev/null +++ b/builder/View/Grid/Actions/DeleteAction.php @@ -0,0 +1,11 @@ +isDialog = $isDialog; + return $this; + } +} diff --git a/builder/View/Grid/Actions/VueRouteAction.php b/builder/View/Grid/Actions/VueRouteAction.php new file mode 100644 index 0000000..8c00729 --- /dev/null +++ b/builder/View/Grid/Actions/VueRouteAction.php @@ -0,0 +1,51 @@ +name = $name; + return $this; + } + + /** + * @param mixed $path + * @return $this + */ + public function path($path) + { + $this->path = $path; + return $this; + } + + /** + * @param mixed $httpPath + * @return $this + */ + public function httpPath($httpPath) + { + $this->httpPath = $httpPath; + return $this; + } + + +} diff --git a/builder/View/Grid/BatchActions.php b/builder/View/Grid/BatchActions.php new file mode 100644 index 0000000..fa03877 --- /dev/null +++ b/builder/View/Grid/BatchActions.php @@ -0,0 +1,84 @@ +deleteAction = DeleteAction::make(); + } + + /** + * 添加自定义Action + * @param $action + * @return $this + */ + public function add($action) + { + if ($action instanceof \Closure) { + $this->addActions = collect($this->addActions)->push(call_user_func($action))->all(); + } else { + $this->addActions = collect($this->addActions)->push($action)->all(); + } + + + return $this; + } + + + public function deleteAction() + { + return $this->deleteAction; + } + + /** + * 隐藏删除操作 + * @return $this + */ + public function hideDeleteAction() + { + $this->hideDeleteAction = true; + return $this; + } + + /** + * 获取当前Grid选择keys + * @return string + */ + public function getKeys(): string + { + return $this->keys; + } + + + public function builderActions() + { + $actions = collect($this->actions); + if (!$this->hideDeleteAction) { + $actions->push($this->deleteAction); + } + foreach ($this->addActions as $addAction) { + $actions->push($addAction); + } + return $actions; + } +} diff --git a/builder/View/Grid/BatchActions/BatchAction.php b/builder/View/Grid/BatchActions/BatchAction.php new file mode 100644 index 0000000..b9ac538 --- /dev/null +++ b/builder/View/Grid/BatchActions/BatchAction.php @@ -0,0 +1,109 @@ +content = $content; + } + + /** + * 批量操作名称 + * @param mixed $content + * @return $this + */ + public function content($content) + { + $this->content = $content; + return $this; + } + + /** + * @param string $content 按钮内容 + * @return BatchAction + */ + public static function make($content) + { + return new BatchAction($content); + } + + /** + * 批量操作路径 + * @param mixed $uri + * @return $this + */ + public function uri($uri) + { + $this->uri = $uri; + return $this; + } + + + /** + * 批量操作响应事件类型 + * @param string $handler 响应类型 request|route|link + * @return $this + */ + public function handler($handler) + { + if (!in_array($handler, [self::HANDLER_LINK, self::HANDLER_REQUEST, self::HANDLER_ROUTE])) { + throw new BusinessException(400, "ActionButton 事件类型错误"); + } + $this->handler = $handler; + return $this; + } + + /** + * vue路由快捷设置方法 + * @param $uri + * @return $this + */ + public function route($uri) + { + $this->uri = $uri; + $this->handler = self::HANDLER_ROUTE; + return $this; + } + + + + /** + * 获取批量选择key + * 注意:只可用于uri设置 + * @return string + */ + public function getKeys(): string + { + return $this->keys; + } + + /** + * 确认操作提示信息 + * @param mixed $message + * @return $this + */ + public function message($message) + { + $this->message = $message; + return $this; + } + +} diff --git a/builder/View/Grid/BatchActions/DeleteAction.php b/builder/View/Grid/BatchActions/DeleteAction.php new file mode 100644 index 0000000..aa5c17b --- /dev/null +++ b/builder/View/Grid/BatchActions/DeleteAction.php @@ -0,0 +1,25 @@ +content = $content; + $this->handler = self::HANDLER_REQUEST; + $this->requestMethod = "delete"; + $this->uri = $this->resource . '/' . $this->getKeys(); + $this->message = "确定要批量删除吗?"; + $this->beforeEmit("tableSetLoading", true); + $this->successEmit("tableReload"); + $this->afterEmit("tableSetLoading", false); + } + + public static function make($content = "批量删除") + { + return new DeleteAction($content); + } +} diff --git a/builder/View/Grid/Column.php b/builder/View/Grid/Column.php new file mode 100644 index 0000000..0e47b81 --- /dev/null +++ b/builder/View/Grid/Column.php @@ -0,0 +1,171 @@ +attributes = new Attributes(); + $this->name = $name; + $this->columnKey = $columnKey; + $this->label = $this->formatLabel($label); + $this->initAttributes(); + } + + protected function initAttributes() + { + $this->attributes->prop = $this->name; + $this->attributes->label = $this->label; + if (empty($this->columnKey)) { + $this->columnKey = $this->name; + } + $this->attributes->columnKey = $this->columnKey; + $name = str_replace('.', '-', $this->name); + } + + protected function formatLabel($label) + { + if ($label) { + return $label; + } + $label = ucfirst($this->name); + return (str_replace(['.', '_'], ' ', $label)); + } + + public function setGrid(Grid $grid) + { + $this->grid = $grid; + } + + /** + * 自定义值 + * + * @param Closure $callback + * + * @return $this + */ + public function customValue(Closure $callback) + { + $this->displayCallbacks = $callback; + + return $this; + } + + public function customValueUsing($row, $value) + { + return $this->displayCallbacks ? call_user_func($this->displayCallbacks, $row, $value) : $value; + } + + /** + * 设置组件 + * @param $component + * @return $this + * @deprecated + */ + public function displayComponent($component) + { + if ($component instanceof Closure) { + $this->displayComponentAttrs(call_user_func($component)); + } else { + $this->displayComponentAttrs($component); + } + return $this; + } + + /** + * 设置组件 + * @param $component + * @return $this + */ + public function component($component) + { + if ($component instanceof Closure) { + $this->displayComponentAttrs(call_user_func($component)); + } else { + $this->displayComponentAttrs($component); + } + return $this; + } + + public function getAttributes() + { + return $this->attributes; + } + + /** + * @return Grid + */ + public function getGrid() + { + return $this->grid; + } + + /** + * @return array|string|null + */ + public function getLabel() + { + return $this->label; + } + + /** + * @return mixed + */ + public function getName() + { + return $this->name; + } + + + /** + * @return null + */ + public function getColumnKey() + { + return $this->columnKey; + } + + /** + * 获取默认值 + * @return mixed + */ + public function getDefaultValue() + { + return $this->defaultValue; + } + + /** + * 设置默认值 + * @param mixed $defaultValue + * @return $this + */ + public function defaultValue($defaultValue) + { + $this->defaultValue = $defaultValue; + return $this; + } +} diff --git a/builder/View/Grid/Column/Attributes.php b/builder/View/Grid/Column/Attributes.php new file mode 100644 index 0000000..7ce5628 --- /dev/null +++ b/builder/View/Grid/Column/Attributes.php @@ -0,0 +1,83 @@ +query(Exporter::$queryName)) { + return; + } + if (!$this->enableExport) { + throw new MineException(403, "未开启导出功能!"); + } + $this->hidePage(); + return $this->getExporter($scope)->export(); + } + + /** + * @param string $scope + * + * @return AbstractExporter + */ + protected function getExporter($scope) + { + return (new Exporter($this))->resolve($this->exporter)->withScope($scope); + } + + /** + * Set exporter driver for Table to export. + * + * @param $exporter + * + * @return $this + */ + public function exporter($exporter) + { + $this->enableExport = true; + $this->exporter = $exporter; + return $this; + } + + /** + * Get the export url. + * + * @param int $scope + * @param null $args + * + * @return string + */ + public function getExportUrl($scope = 1, $args = null) + { + $input = array_merge(request()->all(), Exporter::formatExportQuery($scope, $args)); + + if ($constraints = $this->model()->getConstraints()) { + $input = array_merge($input, $constraints); + } + return $this->resource() . '?' . http_build_query($input); + } + + /** + * @param \Closure $callback + */ + public function export(\Closure $callback) + { + $this->enableExport = true; + if (!$scope = request()->query(Exporter::$queryName)) { + return; + } + $this->getExporter($scope)->setCallback($callback); + return $this; + } +} diff --git a/builder/View/Grid/Concerns/HasActions.php b/builder/View/Grid/Concerns/HasActions.php new file mode 100644 index 0000000..2071c9e --- /dev/null +++ b/builder/View/Grid/Concerns/HasActions.php @@ -0,0 +1,129 @@ +grid = $grid; + $this->editAction = new EditAction(); + $this->deleteAction = new DeleteAction(); + } + + /** + * 编辑操作实例 + * @return EditAction + */ + public function editAction(): EditAction + { + return $this->editAction; + } + + /** + * 删除操作实例 + * @return DeleteAction + */ + public function deleteAction(): DeleteAction + { + return $this->deleteAction; + } + + + /** + * 隐藏所有操作 + * @return $this + */ + public function hideActions() + { + $this->hide = true; + return $this; + } + + /** + * 隐藏详情操作 + * @return $this + */ + public function hideViewAction() + { + $this->hideViewAction = true; + return $this; + } + + /** + * 隐藏编辑操作 + * @return $this + */ + public function hideEditAction() + { + $this->hideEditAction = true; + return $this; + } + + /** + * 隐藏删除操作 + * @return $this + */ + public function hideDeleteAction() + { + $this->hideDeleteAction = true; + return $this; + } + + /** + * 添加自定义Action + * @param $action + * @return $this + */ + public function add($action) + { + if ($action instanceof \Closure) { + $this->addActions = collect($this->addActions)->push(call_user_func($action))->all(); + } else { + $this->addActions = collect($this->addActions)->push($action)->all(); + } + return $this; + } + + + public function builderActions() + { + $actions = collect($this->actions); + if ($this->grid->getDialogForm()) { + $this->editAction->isDialog(true); + } + if (!$this->hideEditAction) { + $actions->push($this->editAction); + } + if (!$this->hideDeleteAction) { + $actions->push($this->deleteAction); + } + foreach ($this->addActions as $addAction) { + $actions->push($addAction); + } + return [ + 'hide' => $this->hide, + 'data' => $actions + ]; + } +} diff --git a/builder/View/Grid/Concerns/HasColumnAttributes.php b/builder/View/Grid/Concerns/HasColumnAttributes.php new file mode 100644 index 0000000..70a7d89 --- /dev/null +++ b/builder/View/Grid/Concerns/HasColumnAttributes.php @@ -0,0 +1,239 @@ +attributes->type = $type; + return $this; + } + + /** + * column 的 key + * @param string $columnKey + * @return $this + */ + public function columnKey($columnKey) + { + $this->attributes->columnKey = $columnKey; + return $this; + } + + /** + * 显示的标题 + * @param string $label + * @return $this + */ + private function label(string $label) + { + $this->attributes->label = $label; + + return $this; + } + + /** + * 对应列内容的字段名 + * @param string $prop + * @return $this + */ + public function prop(string $prop) + { + $this->attributes->prop = $prop; + return $this; + } + + /** + * 对应列的宽度 + * @param string|int $width + * @return $this + */ + public function width($width) + { + $this->attributes->width = $width; + return $this; + } + + /** + * 对应列的最小宽度,与 width 的区别是 width 是固定的,min-width 会把剩余宽度按比例分配给设置了 min-width 的列 + * @param string $minWidth + * @return $this + */ + public function minWidth(string $minWidth) + { + $this->attributes->minWidth = $minWidth; + return $this; + } + + /** + * 列是否固定在左侧或者右侧,true 表示固定在左侧 + * true, left, right + * @param bool|string $fixed + * @return $this + */ + public function fixed($fixed = true) + { + $this->attributes->fixed = $fixed; + return $this; + } + + /** + * 当内容过长被隐藏时显示 tooltip + * @param bool $showOverflowTooltip + * @return $this + */ + public function showOverflowTooltip($showOverflowTooltip = true) + { + $this->attributes->showOverflowTooltip = $showOverflowTooltip; + return $this; + } + + /** + * 对齐方式 left/center/right + * @param string $align + * @return $this + */ + public function align(string $align) + { + $this->attributes->align = $align; + return $this; + } + + /** + * 表头对齐方式,若不设置该项,则使用表格的对齐方式 left/center/right + * @param string $headerAlign + * @return $this + */ + public function headerAlign($headerAlign) + { + $this->attributes->headerAlign = $headerAlign; + return $this; + } + + /** + * 列的 className + * @param string $className + * @return $this + */ + public function className($className) + { + $this->attributes->className = $className; + return $this; + } + + /** + * 当前列标题的自定义类名 + * @param string $labelClassName + * @return $this + */ + public function labelClassName($labelClassName) + { + $this->attributes->labelClassName = $labelClassName; + return $this; + } + + /** + * 表头提示内容 + * @param string $help + * @return $this + */ + public function help($help) + { + $this->attributes->help = $help; + return $this; + } + + /** + * 对应列是否可以排序,如果设置为 'custom',则代表用户希望远程排序,需要监听 Table 的 sort-change 事件 + * true, false, 'custom' + * @param string $sortable + * @return $this + */ + public function sortable($sortable = 'custom') + { + $this->attributes->sortable = $sortable == 'custom'; + return $this; + } + + + /** + * 暂不支持 + * 数据过滤的选项,数组格式,数组中的元素需要有 text 和 value 属性。 + * @param array $filters + * @return $this + */ + public function filters(array $filters) + { + $this->attributes->filters = $filters; + return $this; + } + + /** + * 过滤弹出框的定位 与 Tooltip 的 placement 属性相同 + * @param $filterPlacement + * @return $this + */ + public function filterPlacement($filterPlacement) + { + $this->attributes->filterPlacement = $filterPlacement; + return $this; + } + + + /** + * 数据过滤的选项是否多选 + * @param bool $filterMultiple + * @return $this + */ + public function filterMultiple(bool $filterMultiple) + { + $this->attributes->filterMultiple = $filterMultiple; + return $this; + } + + + /** + * 前缀 + * @param string $itemPrefix + * @return $this + */ + public function itemPrefix(string $itemPrefix) + { + $this->attributes->itemPrefix = $itemPrefix; + return $this; + } + + /** + * 后缀 + * @param string $itemSuffix + * @return $this + */ + public function itemSuffix(string $itemSuffix) + { + $this->attributes->itemSuffix = $itemSuffix; + return $this; + } + + /** + * @param $displayComponentAttrs + * @return $this + */ + private function displayComponentAttrs($displayComponentAttrs) + { + $this->attributes->displayComponentAttrs = $displayComponentAttrs; + return $this; + } + +} diff --git a/builder/View/Grid/Concerns/HasDefaultSort.php b/builder/View/Grid/Concerns/HasDefaultSort.php new file mode 100644 index 0000000..4f46e3c --- /dev/null +++ b/builder/View/Grid/Concerns/HasDefaultSort.php @@ -0,0 +1,27 @@ +defaultSort = [ + 'sort_prop' => $prop, + 'sort_order' => $order, + 'sort_field' => $field ? $field : $prop + ]; + return $this; + } + + public function getDefaultSort() + { + return $this->defaultSort; + } +} diff --git a/builder/View/Grid/Concerns/HasDialog.php b/builder/View/Grid/Concerns/HasDialog.php new file mode 100644 index 0000000..e3ef1ea --- /dev/null +++ b/builder/View/Grid/Concerns/HasDialog.php @@ -0,0 +1,15 @@ +dialog = new Dialog(); + call_user_func($closure, $this->dialog); + return $this; + } +} diff --git a/builder/View/Grid/Concerns/HasExport.php b/builder/View/Grid/Concerns/HasExport.php new file mode 100644 index 0000000..b61e2e0 --- /dev/null +++ b/builder/View/Grid/Concerns/HasExport.php @@ -0,0 +1,44 @@ +simpleExport = $simpleExport; + return $this; + } + + /** + * 搜索条件导出路径例如 /admin/user/export 后面不需要参数,会根据搜索参数自动加上的 + * @param null $search + * @return $this + */ + public function exportPath($exportPath = '') + { + $this->exportPath = $exportPath; + return $this; + } +} diff --git a/builder/View/Grid/Concerns/HasFilter.php b/builder/View/Grid/Concerns/HasFilter.php new file mode 100644 index 0000000..04322e1 --- /dev/null +++ b/builder/View/Grid/Concerns/HasFilter.php @@ -0,0 +1,32 @@ +filter; + } + + public function applyFilter($toArray = true) + { + return $this->filter->execute($toArray); + } + + public function filter(Closure $callback) + { + call_user_func($callback, $this->filter); + } +} diff --git a/builder/View/Grid/Concerns/HasGridAttributes.php b/builder/View/Grid/Concerns/HasGridAttributes.php new file mode 100644 index 0000000..d83c80f --- /dev/null +++ b/builder/View/Grid/Concerns/HasGridAttributes.php @@ -0,0 +1,265 @@ +attributes->height = $height; + return $this; + } + + /** + * js计算页面高度,使表格高度撑满窗口 + * @return $this + */ + public function autoHeight(){ + $this->attributes->height = 'auto'; + return $this; + } + + + /** + * Table 的最大高度。合法的值为数字或者单位为 px 的高度。 + * @param string|int $maxHeight + * @return $this + */ + public function maxHeight($maxHeight) + { + $this->attributes->maxHeight = $maxHeight; + return $this; + } + + /** + * 是否为斑马纹 table + * @param bool $stripe + * @return $this + */ + public function stripe($stripe = true) + { + $this->attributes->stripe = $stripe; + return $this; + } + + /** + * 是否带有纵向边框 + * @param bool $border + * @return $this + */ + public function border($border = true) + { + $this->attributes->border = $border; + return $this; + } + + /** + * Table 的尺寸 + * medium / small / mini + * @param string $size + * @return $this + */ + public function size(string $size) + { + $this->attributes->size = $size; + return $this; + } + + + /** + * 列的宽度是否自撑开 + * @param bool $fit + * @return $this + */ + public function fit(bool $fit = true) + { + $this->attributes->fit = $fit; + return $this; + } + + + /** + * 是否显示表头 + * @param bool $showHeader + * @return $this + */ + public function showHeader($showHeader = true) + { + $this->attributes->showHeader = $showHeader; + return $this; + } + + + /** + * 是否要高亮当前行 + * @param bool $highlightCurrentRow + * @return $this + */ + public function highlightCurrentRow($highlightCurrentRow = true) + { + $this->attributes->highlightCurrentRow = $highlightCurrentRow; + return $this; + } + + /** + * 空数据时显示的文本内容 + * @param string $emptyText + * @return $this + */ + public function emptyText($emptyText) + { + $this->attributes->emptyText = $emptyText; + return $this; + } + + /** + * tooltip effect 属性 + * dark/light + * @param string $tooltipEffect + * @return $this + */ + public function tooltipEffect($tooltipEffect) + { + $this->attributes->tooltipEffect = $tooltipEffect; + return $this; + } + + public function rowKey($rowKey) + { + $this->attributes->rowKey = $rowKey; + return $this; + } + + + /** + * @param $url + * @return $this + * @deprecated + * 开启拖拽排序 + */ + public function draggable($url) + { + $this->attributes->draggable = true; + $this->attributes->draggableUrl = $url; + return $this; + } + + /** + * 是否默认展开所有行,当 Table 包含展开行存在或者为树形表格时有效 + * @param bool $defaultExpandAll + * @return $this + */ + public function defaultExpandAll($defaultExpandAll = true) + { + $this->attributes->defaultExpandAll = $defaultExpandAll; + return $this; + } + + public function treeProps($hasChildren, $children) + { + $this->attributes->treeProps = [ + 'hasChildren' => $hasChildren, + 'children' => $children, + ]; + } + + + public function getTreeChildrenName() + { + return $this->attributes->treeProps['children']; + } + + /** + * 是否开启多选 + * @param bool $selection + * @return $this + */ + public function selection($selection = true) + { + $this->attributes->selection = $selection; + return $this; + } + + /** + * 操作栏宽度 + * @param $actionWidth + * @return $this + */ + public function actionWidth($actionWidth) + { + $this->attributes->actionWidth = $actionWidth; + return $this; + } + + /** + * 操作栏名称 + * @param $actionLabel + * @return $this + */ + public function actionLabel($actionLabel) + { + $this->attributes->actionLabel = $actionLabel; + return $this; + } + + /** + * 操作栏对齐 + * left right center + * @param $actionAlign + * @return $this + */ + public function actionAlign($actionAlign) + { + $this->attributes->actionAlign = $actionAlign; + return $this; + } + + /** + * 操作栏固定位置 + * @param $actionFixed + * @return $this + */ + public function actionFixed($actionFixed) + { + $this->attributes->actionFixed = $actionFixed; + return $this; + } + + /** + * 隐藏操作栏 + * @return $this + */ + public function hideActions() + { + $this->attributes->hideActions = true; + return $this; + } + public function getHideActions() + { + return $this->attributes->hideActions; + + } + + /** + * 表格数据是否存入vuex + * @param $dataVuex + * @return $this + */ + public function dataVuex($dataVuex = true) + { + $this->attributes->dataVuex = $dataVuex; + return $this; + } + +} diff --git a/builder/View/Grid/Concerns/HasPageAttributes.php b/builder/View/Grid/Concerns/HasPageAttributes.php new file mode 100644 index 0000000..739aef1 --- /dev/null +++ b/builder/View/Grid/Concerns/HasPageAttributes.php @@ -0,0 +1,85 @@ +,prev, pager, next, jumper"; + protected $pageBackground = true; + protected $hidePage = false; + /** + * 设置分页布局,子组件名用逗号分隔 + * prev, pager, next, jumper, ->, total + * @param string $layout + * @return $this + */ + public function pageLayout(string $layout) + { + $this->pageLayout = $layout; + return $this; + } + + + /** + * 每页显示个数选择器的选项设置 + * @param array $sizes + * @return $this + */ + public function pageSizes($sizes) + { + $this->pageSizes = $sizes; + return $this; + } + + /** + * 每页显示条目个数 + * @param int $perPage + * @return $this + */ + public function perPage($perPage) + { + $this->perPage = $perPage; + return $this; + } + + /** + * 是否为分页按钮添加背景色 + * @param bool $pageBackground + * @return $this + */ + public function pageBackground(bool $pageBackground = true) + { + $this->pageBackground = $pageBackground; + return $this; + } + + /** + * @return int + */ + public function getPerPage(): int + { + return $this->perPage; + } + + /** + * @return bool + */ + public function isHidePage(): bool + { + return $this->hidePage; + } + + /** + * 隐藏分页 + * @return $this + */ + public function hidePage() + { + $this->hidePage = true; + if ($this->model) { + $this->model->usePaginate(false); + } + return $this; + } +} diff --git a/builder/View/Grid/Concerns/HasQuickSearch.php b/builder/View/Grid/Concerns/HasQuickSearch.php new file mode 100644 index 0000000..8f26083 --- /dev/null +++ b/builder/View/Grid/Concerns/HasQuickSearch.php @@ -0,0 +1,229 @@ +quickSearch = new QuickSearch(); + if (func_num_args() > 1) { + $this->quickSearch->search = func_get_args(); + } else { + $this->quickSearch->search = $search; + } + return $this; + } + + /** + * 设置快捷搜索占位符,开启快捷搜索后生效 + * @param $placeholder + * @return $this + */ + public function quickSearchPlaceholder($placeholder) + { + if ($this->quickSearch) $this->quickSearch->placeholder = $placeholder; + return $this; + } + /** + * Apply the search query to the query. + * + * @return mixed|void + */ + public function applyQuickSearch() + { + if (!$this->quickSearch) { + return; + } + if (!$query = request()->query($this->quickSearch->searchKey)) { + return; + } + if ($this->quickSearch->search instanceof \Closure) { + return call_user_func($this->quickSearch->search, $this->model(), $query); + } + if (is_string($this->quickSearch->search)) { + $this->quickSearch->search = [$this->quickSearch->search]; + } + if (is_array($this->quickSearch->search)) { + $this->model()->where(function ($queryW) use ($query) { + foreach ($this->quickSearch->search as $key => $column) { + $this->addWhereLikeBinding($queryW, $column, true, '%' . $query . '%'); + } + }); + } elseif (is_null($this->quickSearch->search)) { + $this->addWhereBindings($query); + } + } + + /** + * Add where bindings. + * + * @param string $query + */ + protected function addWhereBindings($query) + { + $queries = preg_split('/\s(?=([^"]*"[^"]*")*[^"]*$)/', trim($query)); + foreach ($this->parseQueryBindings($queries) as list($column, $condition, $or)) { + if (preg_match('/(?!?)\((?.+)\)/', $condition, $match) !== 0) { + $this->addWhereInBinding($column, $or, (bool)$match['not'], $match['values']); + continue; + } + if (preg_match('/\[(?.*?),(?.*?)]/', $condition, $match) !== 0) { + $this->addWhereBetweenBinding($column, $or, $match['start'], $match['end']); + continue; + } + if (preg_match('/(?date|time|day|month|year),(?.*)/', $condition, $match) !== 0) { + $this->addWhereDatetimeBinding($column, $or, $match['function'], $match['value']); + continue; + } + if (preg_match('/(?%[^%]+%)/', $condition, $match) !== 0) { + $this->addWhereLikeBinding($column, $or, $match['pattern']); + continue; + } + if (preg_match('/\/(?.*)\//', $condition, $match) !== 0) { + $this->addWhereBasicBinding($column, $or, 'REGEXP', $match['value']); + continue; + } + if (preg_match('/(?>=?|<=?|!=|%){0,1}(?.*)/', $condition, $match) !== 0) { + $this->addWhereBasicBinding($column, $or, $match['operator'], $match['value']); + continue; + } + } + } + + /** + * Parse quick query bindings. + * + * @param array $queries + * + * @return array + */ + protected function parseQueryBindings(array $queries) + { + $columnMap = collect($this->columns)->mapWithKeys(function (Column $column) { + $label = $column->getLabel(); + $name = $column->getName(); + return [$label => $name, $name => $name]; + }); + return collect($queries)->map(function ($query) use ($columnMap) { + $segments = explode(':', $query, 2); + if (count($segments) != 2) { + return; + } + $or = false; + list($column, $condition) = $segments; + if (Str::startsWith($column, '|')) { + $or = true; + $column = substr($column, 1); + } + $column = $columnMap[$column]; + return [$column, $condition, $or]; + })->filter()->toArray(); + } + + /** + * Add where like binding to model query. + * + * @param $query + * @param string $column + * @param bool $or + * @param string $pattern + */ + protected function addWhereLikeBinding($query, string $column, bool $or, string $pattern) + { + $connectionType = $this->model()->eloquent()->getConnection()->getDriverName(); + $likeOperator = $connectionType == 'pgsql' ? 'ilike' : 'like'; + $method = $or ? 'orWhere' : 'where'; + $query->{$method}($column, $likeOperator, $pattern); + } + + /** + * Add where date time function binding to model query. + * + * @param string $column + * @param bool $or + * @param string $function + * @param string $value + */ + protected function addWhereDatetimeBinding(string $column, bool $or, string $function, string $value) + { + $method = ($or ? 'orWhere' : 'where') . ucfirst($function); + $this->model()->{$method}($column, $value); + } + + /** + * Add where in binding to the model query. + * + * @param string $column + * @param bool $or + * @param bool $not + * @param string $values + */ + protected function addWhereInBinding(string $column, bool $or, bool $not, string $values) + { + $values = explode(',', $values); + foreach ($values as $key => $value) { + if ($value === 'NULL') { + $values[$key] = null; + } + } + $where = $or ? 'orWhere' : 'where'; + $method = $where . ($not ? 'NotIn' : 'In'); + $this->model()->{$method}($column, $values); + } + + /** + * Add where between binding to the model query. + * + * @param string $column + * @param bool $or + * @param string $start + * @param string $end + */ + protected function addWhereBetweenBinding(string $column, bool $or, string $start, string $end) + { + $method = $or ? 'orWhereBetween' : 'whereBetween'; + + $this->model()->{$method}($column, [$start, $end]); + } + + /** + * Add where basic binding to the model query. + * @param string $column + * @param bool $or + * @param string $operator + * @param string $value + */ + protected function addWhereBasicBinding(string $column, bool $or, string $operator, string $value) + { + $method = $or ? 'orWhere' : 'where'; + $operator = $operator ?: '='; + if ($operator == '%') { + $operator = 'like'; + $value = "%{$value}%"; + } + if ($value === 'NULL') { + $value = null; + } + if (Str::startsWith($value, '"') && Str::endsWith($value, '"')) { + $value = substr($value, 1, -1); + } + $this->model()->{$method}($column, $operator, $value); + } +} diff --git a/builder/View/Grid/Exporter.php b/builder/View/Grid/Exporter.php new file mode 100644 index 0000000..f65b46b --- /dev/null +++ b/builder/View/Grid/Exporter.php @@ -0,0 +1,145 @@ +grid = $grid; + $this->grid->model()->usePaginate(false); + } + + /** + * Set export query name. + * + * @param $name + */ + public static function setQueryName($name) + { + static::$queryName = $name; + } + + /** + * Extends new exporter driver. + * + * @param $driver + * @param $extend + */ + public static function extend($driver, $extend) + { + static::$drivers[$driver] = $extend; + } + + /** + * Resolve export driver. + * + * @param string $driver + * + * @return CsvExporter + */ + public function resolve($driver) + { + if ($driver instanceof Grid\Exporters\AbstractExporter) { + $driver->setGrid($this->grid); + return $driver; + } + + return $this->getExporter($driver); + } + + /** + * Get export driver. + * + * @param string $driver + * + * @return CsvExporter + */ + protected function getExporter($driver) + { + if ($this->exporter) { + return $this->exporter; + } + + if (!array_key_exists($driver, $this->drivers)) { + return $this->exporter = $this->getDefaultExporter(); + } + + return $this->exporter = new $this->drivers[$driver]($this->grid); + } + + /** + * Get default exporter. + * + * @return CsvExporter + */ + public function getDefaultExporter() + { + return new CsvExporter($this->grid); + } + + /** + * Format query for export url. + * + * @param int $scope + * @param null $args + * + * @return array + */ + public static function formatExportQuery($scope = '', $args = null) + { + $query = ''; + + if ($scope == static::SCOPE_ALL) { + $query = 'all'; + } + + if ($scope == static::SCOPE_CURRENT_PAGE) { + $query = "page:$args"; + } + + if ($scope == static::SCOPE_SELECTED_ROWS) { + $query = "selected:$args"; + } + + return [static::$queryName => $query]; + } +} diff --git a/builder/View/Grid/Exporters/AbstractExporter.php b/builder/View/Grid/Exporters/AbstractExporter.php new file mode 100644 index 0000000..28f2018 --- /dev/null +++ b/builder/View/Grid/Exporters/AbstractExporter.php @@ -0,0 +1,136 @@ +setGrid($grid); + } + } + + public function setGrid(Grid $grid) + { + $this->grid = $grid; + } + + /** + * Get grid of grid. + * + * @return string + */ + public function getGrid() + { + return $this->grid; + } + + /** + * Get data with export query. + * + * @param bool $toArray + * + * @return array|\Illuminate\Support\Collection|mixed + */ + public function getData($toArray = true) + { + return $this->grid->applyFilter($toArray); + } + + /** + * @param callable $callback + * @param int $count + * + * @return bool + */ + public function chunk(callable $callback, $count = 100) + { + //加上快速搜索条件 + return $this->grid->getFilter()->chunk($callback, $count); + } + + /** + * @return Collection + */ + public function getCollection() + { + return collect($this->getData()); + } + + /** + * @return Builder|Model + */ + public function getQuery() + { + $model = $this->grid->model(); + + $queryBuilder = $model->newQuery(); + + // Export data of giving page number. + if ($this->page) { + $keyName = $this->grid->getKeyName(); + $perPage = request()->query($model->getPerPageName(), $model->getPerPage()); + $scope = (clone $queryBuilder) + ->select([$keyName]) + ->setEagerLoads([]) + ->forPage($this->page, $perPage)->get(); + // If $querybuilder is a Model, it must be reassigned, unless it is a eloquent/query builder. + $queryBuilder = $queryBuilder->whereIn($keyName, $scope->pluck($keyName)); + } + + return $queryBuilder; + } + + /** + * Export data with scope. + * + * @param string $scope + * + * @return $this + */ + public function withScope($scope) + { + if ($scope == Grid\Exporter::SCOPE_ALL) { + return $this; + } + + if ($scope == Grid\Exporter::SCOPE_WHERE) { + $this->grid->applyWhere(); + return $this; + } + + list($scope, $args) = explode(':', $scope); + if ($scope == Grid\Exporter::SCOPE_CURRENT_PAGE) { + $this->grid->model()->usePaginate(true); + $this->page = $args ?: 1; + } + + if ($scope == Grid\Exporter::SCOPE_SELECTED_ROWS) { + $selected = explode(',', $args); + $this->grid->model()->whereIn($this->grid->getKeyName(), $selected); + } + + return $this; + } + + /** + * {@inheritdoc} + */ + abstract public function export(); +} diff --git a/builder/View/Grid/Exporters/CsvExporter.php b/builder/View/Grid/Exporters/CsvExporter.php new file mode 100644 index 0000000..d6cd975 --- /dev/null +++ b/builder/View/Grid/Exporters/CsvExporter.php @@ -0,0 +1,231 @@ +filename = $filename; + + return $this; + } + + /** + * @param \Closure $closure + */ + public function setCallback(\Closure $closure): self + { + $this->callback = $closure; + + return $this; + } + + /** + * @param array $columns + * + * @return $this + */ + public function except(array $columns = []): self + { + $this->exceptColumns = $columns; + + return $this; + } + + /** + * @param array $columns + * + * @return $this + */ + public function only(array $columns = []): self + { + $this->onlyColumns = $columns; + + return $this; + } + + /** + * @param array $columns + * + * @return $this + */ + public function originalValue($columns = []): self + { + $this->columnUseOriginalValue = $columns; + + return $this; + } + + /** + * @param string $name + * @param \Closure $callback + * + * @return $this + */ + public function column(string $name, \Closure $callback): self + { + $this->columnCallbacks[$name] = $callback; + + return $this; + } + + /** + * @param string $name + * @param \Closure $callback + * + * @return $this + */ + public function title(string $name, \Closure $callback): self + { + $this->titleCallbacks[$name] = $callback; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function export() + { + if ($this->callback) { + call_user_func($this->callback, $this); + } + $titles = []; + $items = collect(); + $columns = $this->grid->getColumns(); + //增加一个bom头避免再微软office下打开乱码 + $csv = "\xEF\xBB\xBF"; + $csv .= implode(",", $this->getVisiableTitles()) . PHP_EOL; + $this->chunk(function ($data, $page) use ($items, $columns, &$csv) { + $original = $data->toArray(); + // Write rows + foreach ($original as $index => $record) { + $cols = $this->getVisiableFields($record, $original[$index]); + foreach ($cols as $k => $col) + $cols[$k] = '"'.$col.'"';//避免字段中遇到,以及换行符等,直接以文本格式处理 + $csv .= implode(",",$cols). PHP_EOL; + } + }, 100); + if (!$this->filename) { + $this->filename = $this->grid->getTable() . ".csv"; + } + $response = new \Hyperf\HttpServer\Response(); + $response = $response->withHeader('Content-Type', 'text/csv;charset=UTF-8'); + $response = $response->withHeader('Expires', '0'); + $response = $response->withHeader('Pragma', 'public'); + $response = $response->withHeader('Content-Transfer-Encoding', 'binary'); + $response = $response->withHeader('Content-Disposition', 'attachment;filename=' . $this->filename); + return $response->withBody(new SwooleStream($csv)); + } + + /** + * @return array + */ + protected function getVisiableTitles() + { + $titles = collect($this->grid->getColumns()) + ->mapWithKeys(function (Column $column) { + $columnName = $column->getName(); + $columnTitle = $column->getLabel(); + if (isset($this->titleCallbacks[$columnName])) { + $columnTitle = $this->titleCallbacks[$columnName]($columnTitle); + } + return [$columnName => $columnTitle]; + }); + + if ($this->onlyColumns) { + $titles = $titles->only($this->onlyColumns); + } + if ($this->exceptColumns) { + $titles = $titles->except($this->exceptColumns); + } + $this->visibleColumns = $titles->keys(); + return $titles->values()->toArray(); + } + + /** + * @param array $value + * @param array $original + * + * @return array + */ + public function getVisiableFields(array $value, array $original): array + { + $fields = []; + foreach ($this->visibleColumns as $column) { + $fields[] = $this->getColumnValue( + $column, + data_get($value, $column), + data_get($original, $column) + ); + } + return $fields; + } + + /** + * @param string $column + * @param mixed $value + * @param mixed $original + * + * @return mixed + */ + protected function getColumnValue(string $column, $value, $original) + { + if (!empty($this->columnUseOriginalValue) + && in_array($column, $this->columnUseOriginalValue)) { + return $original; + } + + if (isset($this->columnCallbacks[$column])) { + return $this->columnCallbacks[$column]($value, $original); + } + + return $value; + } +} diff --git a/builder/View/Grid/Exporters/ExporterInterface.php b/builder/View/Grid/Exporters/ExporterInterface.php new file mode 100644 index 0000000..6398e83 --- /dev/null +++ b/builder/View/Grid/Exporters/ExporterInterface.php @@ -0,0 +1,11 @@ + Filter\Equal::class, + 'notEqual' => Filter\NotEqual::class, + 'ilike' => Filter\Ilike::class, + 'like' => Filter\Like::class, + 'gt' => Filter\Gt::class, + 'lt' => Filter\Lt::class, + 'between' => Filter\Between::class, + 'where' => Filter\Where::class, + 'in' => Filter\In::class, + 'notIn' => Filter\NotIn::class, + 'date' => Filter\Date::class, + 'day' => Filter\Day::class, + 'month' => Filter\Month::class, + 'year' => Filter\Year::class, + 'contains' => Filter\Like::class, + 'startsWith' => Filter\StartsWith::class, + 'endsWith' => Filter\EndsWith::class, + ]; + + public function __construct($model) + { + $this->model = $model; + $this->scopes = new Collection(); + } + + + public function conditions() + { + $inputs = Arr::dot(request()->all()); + $inputs = array_filter($inputs, function ($input) { + return $input !== '' && !is_null($input); + }); + $this->sanitizeInputs($inputs); + if (empty($inputs)) { + return []; + } + $params = []; + foreach ($inputs as $key => $value) { + Arr::set($params, $key, $value); + } + $conditions = []; + foreach ($this->filters() as $filter) { + $conditions[] = $filter->condition($params); + } + return tap(array_filter($conditions), function ($conditions) { + if (!empty($conditions)) { + $this->expand(); + } + }); + } + + + protected function sanitizeInputs(&$inputs) + { + if (!$this->name) { + return $inputs; + } + $inputs = collect($inputs)->filter(function ($input, $key) { + return Str::startsWith($key, "{$this->name}_"); + })->mapWithKeys(function ($val, $key) { + $key = str_replace("{$this->name}_", '', $key); + return [$key => $val]; + })->toArray(); + } + + + public function expand() + { + $this->expand = true; + return $this; + } + + /** + * @param callable $callback + * @param int $count + * + * @return bool + */ + public function chunk(callable $callback, $count = 100) + { + $conditions = array_merge( + $this->conditions(), + $this->scopeConditions() + ); + return $this->model->addConditions($conditions)->chunk($callback, $count); + } + + public function execute($toArray = true) + { + if (method_exists($this->model->eloquent(), 'paginate')) { + $this->model->usePaginate(true); + return $this->model->buildData($toArray); + } + $conditions = array_merge( + $this->conditions(), + $this->scopeConditions() + ); + return $this->model->addConditions($conditions)->buildData($toArray); + } + + public function getCurrentScope() + { + $key = request()->query(Scope::QUERY_NAME); + return $this->scopes->first(function ($scope) use ($key) { + return $scope->key == $key; + }); + } + + protected function scopeConditions() + { + if ($scope = $this->getCurrentScope()) { + return $scope->condition(); + } + return []; + } + + /** + * @return array + */ + public function filters(): array + { + return $this->filters; + } + + + /** + * @return array + */ + public function buildFilter(): array + { + foreach ($this->filters as $filter) { + Arr::set($this->filterFormData, $filter->getColumn(), $filter->getDefaultValue()); + } + return [ + 'filters' => $this->filters, + 'filterFormData' => $this->filterFormData, + ]; + } + + protected function addFilter(AbstractFilter $filter) + { + return $this->filters[] = $filter; + } + + public function resolveFilter($abstract, $arguments) + { + if (isset(static::$supports[$abstract])) { + return new static::$supports[$abstract](...$arguments); + } + } + + public function __call($method, $arguments) + { + if ($filter = $this->resolveFilter($method, $arguments)) { + return $this->addFilter($filter); + } + return $this; + } + +} diff --git a/builder/View/Grid/Filter/AbstractFilter.php b/builder/View/Grid/Filter/AbstractFilter.php new file mode 100644 index 0000000..21cb961 --- /dev/null +++ b/builder/View/Grid/Filter/AbstractFilter.php @@ -0,0 +1,120 @@ +column = $column; + $this->label = $label != null ? $this->formatLabel($label) : null; + $this->component = Input::make(); + + } + + protected function formatLabel($label) + { + $label = $label ?: ucfirst($this->column); + return str_replace(['.', '_'], ' ', $label); + } + + /** + * @param mixed $defaultValue + * @return $this + */ + public function defaultValue($defaultValue) + { + $this->defaultValue = $defaultValue; + if ($this->component) { + $this->component->componentValue($defaultValue); + } + return $this; + } + + /** + * @param Component $component + * @return $this + */ + public function component($component) + { + $this->component = $component; + if ($this->defaultValue) { + $this->component->componentValue($this->defaultValue); + } elseif ($this->component->getComponentValue()) { + $this->defaultValue = $this->component->getComponentValue(); + } + return $this; + } + + /** + * @return mixed + */ + public function getDefaultValue() + { + return $this->defaultValue; + } + + /** + * @return mixed + */ + public function getColumn() + { + return $this->column; + } + + public function condition($inputs) + { + if ($this->ignore) { + return; + } + + $value = Arr::get($inputs, $this->column); + + if (!isset($value)) { + return; + } + + $this->value = $value; + + return $this->buildCondition($this->column, $this->value); + } + + protected function buildCondition() + { + $column = explode('.', $this->column); + + if (count($column) == 1) { + return [$this->query => func_get_args()]; + } + + return $this->buildRelationQuery(...func_get_args()); + } + + protected function buildRelationQuery() + { + $args = func_get_args(); + + list($relation, $args[0]) = explode('.', $this->column); + + return ['whereHas' => [$relation, function ($relation) use ($args) { + call_user_func_array([$relation, $this->query], $args); + }]]; + } + +} diff --git a/builder/View/Grid/Filter/Between.php b/builder/View/Grid/Filter/Between.php new file mode 100644 index 0000000..f5b0f73 --- /dev/null +++ b/builder/View/Grid/Filter/Between.php @@ -0,0 +1,50 @@ +column)) { + return; + } + + $this->value = Arr::get($inputs, $this->column); + + if (!is_array($this->value)) { + return; + } + + $value = array_filter($this->value, function ($val) { + return $val !== ''; + }); + + if (empty($value)) { + return; + } + + if (!isset($value[0])) { + return $this->buildCondition($this->column, '<=', $value[0]); + } + + if (!isset($value[1])) { + return $this->buildCondition($this->column, '>=', $value[1]); + } + + $this->query = 'whereBetween'; + + return $this->buildCondition($this->column, $this->value); + } + + +} diff --git a/builder/View/Grid/Filter/Date.php b/builder/View/Grid/Filter/Date.php new file mode 100644 index 0000000..e1d6a5c --- /dev/null +++ b/builder/View/Grid/Filter/Date.php @@ -0,0 +1,24 @@ +column); + if (is_null($value)) { + return; + } + $this->value = $value; + return $this->buildCondition($this->column, '>=', $this->value); + } +} diff --git a/builder/View/Grid/Filter/Ilike.php b/builder/View/Grid/Filter/Ilike.php new file mode 100644 index 0000000..e2af394 --- /dev/null +++ b/builder/View/Grid/Filter/Ilike.php @@ -0,0 +1,7 @@ +column); + if (is_null($value)) { + return; + } + $this->value = (array)$value; + return $this->buildCondition($this->column, $this->value); + } +} diff --git a/builder/View/Grid/Filter/Like.php b/builder/View/Grid/Filter/Like.php new file mode 100644 index 0000000..4c2fce7 --- /dev/null +++ b/builder/View/Grid/Filter/Like.php @@ -0,0 +1,37 @@ +column); + if (is_array($value)) { + $value = array_filter($value); + } + if (is_null($value) || empty($value)) { + return; + } + $this->value = $value; + $expr = str_replace('{value}', $this->value, $this->exprFormat); + return $this->buildCondition($this->column, $this->operator, $expr); + } +} diff --git a/builder/View/Grid/Filter/Lt.php b/builder/View/Grid/Filter/Lt.php new file mode 100644 index 0000000..8916916 --- /dev/null +++ b/builder/View/Grid/Filter/Lt.php @@ -0,0 +1,28 @@ +column); + if (is_null($value)) { + return; + } + $this->value = $value; + return $this->buildCondition($this->column, '<=', $this->value); + } +} diff --git a/builder/View/Grid/Filter/Month.php b/builder/View/Grid/Filter/Month.php new file mode 100644 index 0000000..8f5f547 --- /dev/null +++ b/builder/View/Grid/Filter/Month.php @@ -0,0 +1,15 @@ +column); + if (!isset($value)) { + return; + } + $this->value = $value; + return $this->buildCondition($this->column, '!=', $this->value); + } +} diff --git a/builder/View/Grid/Filter/NotIn.php b/builder/View/Grid/Filter/NotIn.php new file mode 100644 index 0000000..d41ee9c --- /dev/null +++ b/builder/View/Grid/Filter/NotIn.php @@ -0,0 +1,10 @@ +key = $key; + $this->label = $label ? $label : Str::studly($key); + + $this->queries = new Collection(); + } + + /** + * Get label. + * + * @return string + */ + public function getLabel() + { + return $this->label; + } + + /** + * Get model query conditions. + * + * @return array + */ + public function condition() + { + return $this->queries->map(function ($query) { + return [$query['method'] => $query['arguments']]; + })->toArray(); + } + + + /** + * @param string $method + * @param array $arguments + * + * @return $this + */ + public function __call($method, $arguments) + { + $this->queries->push(compact('method', 'arguments')); + + return $this; + } +} diff --git a/builder/View/Grid/Filter/StartsWith.php b/builder/View/Grid/Filter/StartsWith.php new file mode 100644 index 0000000..2246314 --- /dev/null +++ b/builder/View/Grid/Filter/StartsWith.php @@ -0,0 +1,7 @@ +where = $query; + $this->label = $this->formatLabel($label); + $this->column = $column ?: static::getQueryHash($query, $this->label); + $this->component = Input::make(); + } + + /** + * Get the hash string of query closure. + * @param \Closure $closure + * @param string $label + * @return string + * @throws \ReflectionException + */ + public static function getQueryHash(\Closure $closure, $label = '') + { + $reflection = new ReflectionFunction($closure); + + return md5($reflection->getFileName() . $reflection->getStartLine() . $reflection->getEndLine() . $label); + } + + /** + * Get condition of this filter. + * @param $inputs + * @return array|void + * @throws \ReflectionException + */ + public function condition($inputs) + { + + $value = Arr::get($inputs, $this->column ?: static::getQueryHash($this->where, $this->label)); + + if (is_null($value)) { + return; + } + + $this->input = $this->value = $value; + + return $this->buildCondition($this->where->bindTo($this)); + } +} diff --git a/builder/View/Grid/Filter/Year.php b/builder/View/Grid/Filter/Year.php new file mode 100644 index 0000000..87c9081 --- /dev/null +++ b/builder/View/Grid/Filter/Year.php @@ -0,0 +1,14 @@ +model = $model; + $this->sModel = $model; + $this->originalModel = $model; + $this->grid = $grid; + $this->queries = collect(); + + } + + public function eloquent() + { + return $this->model; + } + + public function usePaginate($use = true) + { + $this->usePaginate = $use; + } + + /** + * @return bool + */ + public function isUsePaginate(): bool + { + return $this->usePaginate; + } + + + public function getModel() + { + return $this->model; + } + + protected function findQueryByMethod($method) + { + return $this->queries->first(function ($query) use ($method) { + return $query['method'] == $method; + }); + } + + protected function resolvePerPage($paginate) + { + if ($perPage = request($this->perPageName)) { + if (is_array($paginate)) { + $paginate['arguments'][0] = (int)$perPage; + + return $paginate['arguments']; + } + + $this->perPage = (int)$perPage; + } else { + $this->perPage = $this->grid->getPerPage(); + } + + if (isset($paginate['arguments'][0])) { + return $paginate['arguments']; + } + + + return [$this->perPage]; + } + + /** + * 设置每页大小 + */ + protected function setPaginate() + { + $paginate = $this->findQueryByMethod('paginate'); + + $this->queries = $this->queries->reject(function ($query) { + return $query['method'] == 'paginate'; + }); + + if ($this->grid->isHidePage()) { + $query = [ + 'method' => 'get', + 'arguments' => [], + ]; + } else { + $query = [ + 'method' => 'paginate', + 'arguments' => $this->resolvePerPage($paginate), + ]; + } + + $this->queries->push($query); + } + + + public function with($relations) + { + if (is_array($relations)) { + if (Arr::isAssoc($relations)) { + $relations = array_keys($relations); + } + + $this->eagerLoads = array_merge($this->eagerLoads, $relations); + } + + if (is_string($relations)) { + if (Str::contains($relations, '.')) { + $relations = explode('.', $relations)[0]; + } + + if (Str::contains($relations, ':')) { + $relations = explode(':', $relations)[0]; + } + + if (in_array($relations, $this->eagerLoads)) { + return $this; + } + + $this->eagerLoads[] = $relations; + } + + return $this->__call('with', (array)$relations); + } + + + /** + * 设置排序 + */ + protected function setSort() + { + $column = request('sort_field', null); + $sort_field = request('sort_field', null); + $type = request('sort_order', null); + + + if ($sort_field && in_array($type, ['asc', 'desc'])) { + $this->sort = [ + 'column' => $sort_field, + 'type' => $type + ]; + } else { + $defaultSort = $this->grid->getDefaultSort(); + $this->sort = [ + 'column' => $defaultSort['sort_field'], + 'type' => $defaultSort['sort_order'] + ]; + } + + + if (!is_array($this->sort)) { + return; + } + + if (empty($this->sort['column']) || empty($this->sort['type'])) { + return; + } + + if (Str::contains($this->sort['column'], '.')) { + $this->setRelationSort($this->sort['column']); + + } else { + $this->resetOrderBy(); + + // get column. if contains "cast", set set column as cast + if (!empty($this->sort['cast'])) { + $column = "CAST({$this->sort['column']} AS {$this->sort['cast']}) {$this->sort['type']}"; + $method = 'orderByRaw'; + $arguments = [$column]; + } else { + $column = $this->sort['column']; + $method = 'orderBy'; + $arguments = [$column, $this->sort['type']]; + } + $this->queries->push([ + 'method' => $method, + 'arguments' => $arguments, + ]); + } + } + + /** + * @param $column + * @throws \Exception + */ + protected function setRelationSort($column) + { + list($relationName, $relationColumn) = explode('.', $column); + + + if ($this->queries->contains(function ($query) use ($relationName) { + return $query['method'] == 'with' && in_array($relationName, $query['arguments']); + })) { + $relation = $this->model->$relationName(); + + + $this->queries->push([ + 'method' => 'select', + 'arguments' => [$this->model->getTable() . '.*'], + ]); + + + $this->queries->push([ + 'method' => 'join', + 'arguments' => $this->joinParameters($relation), + ]); + + $this->resetOrderBy(); + + $this->queries->push([ + 'method' => 'orderBy', + 'arguments' => [ + $relation->getRelated()->getTable() . '.' . $relationColumn, + $this->sort['type'], + ], + ]); + } + } + + public function resetOrderBy() + { + $this->queries = $this->queries->reject(function ($query) { + return $query['method'] == 'orderBy' || $query['method'] == 'orderByDesc'; + }); + } + + /** + * @param Relation $relation + * @return array + * @throws \Exception + */ + protected function joinParameters(Relation $relation) + { + $relatedTable = $relation->getRelated()->getTable(); + + if ($relation instanceof BelongsTo) { + return [ + $relatedTable, + $relation->getForeignKeyName(), + '=', + $relatedTable . '.' . $relation->getRelated()->getKeyName(), + ]; + } + + if ($relation instanceof HasOne) { + + + return [ + $relatedTable, function ($join) use ($relation) { + $join->on($relation->getQualifiedParentKeyName(), "=", $relation->getQualifiedForeignKeyName()) + ->where(function (Builder $query) use ($relation) { + collect($relation->getBaseQuery()->wheres)->filter(function ($item) { + return $item['value'] ?? false; + })->each(function ($item) use ($query) { + if ($item['type'] == 'Basic') { + $query->where($item['column'], $item['value']); + } + + }); + }); + } + ]; + } + + throw new \Exception('Related sortable only support `HasOne` and `BelongsTo` relation.'); + } + + protected function handleInvalidPage(LengthAwarePaginator $paginator) + { + if ($paginator->lastPage() && $paginator->currentPage() > $paginator->lastPage()) { + $lastPageUrl = Request::fullUrlWithQuery([ + $paginator->getPageName() => $paginator->lastPage(), + ]); + } + } + + public function addConditions(array $conditions) + { + foreach ($conditions as $condition) { + call_user_func_array([$this, key($condition)], current($condition)); + } + + return $this; + } + + /** + * @param callable $callback + * @param int $count + * + * @return bool + */ + public function chunk($callback, $count = 100) + { + if ($this->usePaginate) { + return $this->buildData(false)->chunk($count)->each($callback); + } + + $this->queries->reject(function ($query) { + return $query['method'] == 'paginate'; + })->each(function ($query) { + $this->model = $this->model->{$query['method']}(...$query['arguments']); + }); + return $this->model->chunk($count, $callback); + } + + public function buildData($toArray = false) + { + + if (empty($this->data)) { + $this->data = $this->get(); + } + + if ($toArray) { + return $this->data->toArray(); + } + + return $this->data; + } + + public function displayData($data) + { + $columns = $this->grid->getColumns(); + $items = collect(); + foreach ($data as $key => $row) { + $item = []; + foreach ($this->grid->getAppendFields() as $field) { + data_set($item, $field, data_get($row, $field)); + } + foreach ($columns as $column) { + if (Str::contains($column->getName(), '.')) { + list($relationName, $relationColumn) = explode('.', $column->getName()); + //如果是集合 + if (data_get($row, $relationName) instanceof Collection || is_array(data_get($row, $relationName))) { + $value = collect(data_get($row, $relationName))->pluck($relationColumn); + $c_value = $column->customValueUsing($row, $value); + data_set($item, $column->getName(), $c_value); + } else { + $value = data_get($row, $column->getName(), $column->getDefaultValue()); + $c_value = $column->customValueUsing($row, $value); + data_set($item, $column->getName(), $c_value); + } + } else { + $dataValue = data_get($row, $column->getName(), $column->getDefaultValue()); + if ($this->sModel != null && $dataValue instanceof \Carbon\Carbon) { + $dataValue = $dataValue->format($this->sModel->getDateFormat()); + } + $n_value = $column->customValueUsing($row, $dataValue); + data_set($item, $column->getName(), $n_value); + } + } + if (!$this->grid->getHideActions()) { + data_set($item, 'grid_actions', $this->grid->getActions($row, $key)); + } + data_set($item, $this->grid->getKeyName(), data_get($row, $this->grid->getKeyName())); + //如果存在下级 + if ($TreeChildren = data_get($row, $this->grid->getTreeChildrenName())) { + //递归处理下级列表 + data_set($item, $this->grid->getTreeChildrenName(), $this->displayData($TreeChildren)); + } + $items->push($item); + } + + return $items->all(); + + } + + public function __call($method, $arguments) + { + + $this->queries->push([ + 'method' => $method, + 'arguments' => $arguments, + ]); + return $this; + } + + + public function __get($key) + { + $data = $this->buildData(); + + if (array_key_exists($key, $data)) { + return $data[$key]; + } + } + + + public function get() + { + + if ($this->model instanceof LengthAwarePaginator) { + return $this->model; + } + if ($this->relation) { + $this->model = $this->relation->getQuery(); + } + $this->setSort(); + $this->setPaginate(); + $this->queries->unique()->each(function ($query) { + $this->model = call_user_func_array([$this->model, $query['method']], $query['arguments']); + }); + $data = $this->model; + if ($this->model instanceof Collection) { + if ($data->count() > 0) { + return $this->displayData($data); + } else { + return $data; + } + } + if ($this->model instanceof LengthAwarePaginator) { + return [ + 'current_page' => $this->model->currentPage(), + 'per_page' => $this->model->perPage(), + 'last_page' => $this->model->lastPage(), + 'total' => $this->model->total(), + 'data' => $this->displayData($data->items()) + ]; + } + + } +} diff --git a/builder/View/Grid/Table/Attributes.php b/builder/View/Grid/Table/Attributes.php new file mode 100644 index 0000000..14a86af --- /dev/null +++ b/builder/View/Grid/Table/Attributes.php @@ -0,0 +1,69 @@ + 'hasChildren', 'children' => 'children']; + + + public $hideActions=false; + public $actionWidth; + public $actionLabel = "操作"; + public $actionFixed; + public $actionAlign = "left"; + + public $selection = false; + + public $dataVuex = false; + + +} diff --git a/builder/View/Grid/Toolbars.php b/builder/View/Grid/Toolbars.php new file mode 100644 index 0000000..d8280f6 --- /dev/null +++ b/builder/View/Grid/Toolbars.php @@ -0,0 +1,92 @@ +grid = $grid; + $this->createButton = new CreateButton(); + $this->createButton->content("添加")->icon("el-icon-plus"); + } + + /** + * @return CreateButton + */ + public function createButton(): CreateButton + { + return $this->createButton; + } + + /** + * 隐藏工具栏 + * @return Toolbars + */ + public function hide() + { + $this->show = false; + return $this; + } + + + /** + * @param mixed $hideCreateButton + * @return $this + */ + public function hideCreateButton($hideCreateButton = true) + { + $this->hideCreateButton = $hideCreateButton; + return $this; + } + + public function addLeft($action) + { + $this->addLeft = collect($this->addLeft)->push($action)->all(); + return $this; + } + + + public function addRight($action) + { + $this->addRight = collect($this->addRight)->push($action)->all(); + return $this; + } + + + public function builderData() + { + $left = collect($this->left); + $right = collect($this->right); + if (!$this->hideCreateButton) { + $right->push($this->createButton); + }; + foreach ($this->addLeft as $addLeft) { + $left->push($addLeft); + } + foreach ($this->addRight as $addRight) { + $right->prepend($addRight); + } + if ($this->grid->getDialogForm()) { + $this->createButton->isDialog(true); + } + return [ + "show" => $this->show, + "left" => $left, + "right" => $right + ]; + } +} diff --git a/builder/View/Grid/Tools/CreateButton.php b/builder/View/Grid/Tools/CreateButton.php new file mode 100644 index 0000000..9e5e08b --- /dev/null +++ b/builder/View/Grid/Tools/CreateButton.php @@ -0,0 +1,29 @@ +isDialog = $isDialog; + return $this; + } + + + + + +} diff --git a/builder/View/Grid/Tools/QuickSearch.php b/builder/View/Grid/Tools/QuickSearch.php new file mode 100644 index 0000000..2e0d05f --- /dev/null +++ b/builder/View/Grid/Tools/QuickSearch.php @@ -0,0 +1,24 @@ + $this->searchKey, + 'placeholder' => $this->placeholder, + ]); + } +} diff --git a/builder/View/Grid/Tools/ToolButton.php b/builder/View/Grid/Tools/ToolButton.php new file mode 100644 index 0000000..0ddb84e --- /dev/null +++ b/builder/View/Grid/Tools/ToolButton.php @@ -0,0 +1,51 @@ +content = $content; + } + + /** + * @param string $content 按钮内容 + * @return ToolButton + */ + public static function make($content) + { + return new ToolButton($content); + } + + /** + * @param mixed $uri + * @return $this + */ + public function uri($uri) + { + $this->uri = $uri; + return $this; + } + + /** + * @param string $handler 响应类型 request|route|link + * @return $this + */ + public function handler($handler) + { + $this->handler = $handler; + return $this; + } + + +} diff --git a/builder/View/Layout/Buildable.php b/builder/View/Layout/Buildable.php new file mode 100644 index 0000000..31635e9 --- /dev/null +++ b/builder/View/Layout/Buildable.php @@ -0,0 +1,7 @@ +append($content); + } + $this->width = $width; + } + + public function append($content) + { + $this->contents[] = $content; + return $this; + } + + public function row($content) + { + if ($content instanceof \Closure) { + $row = new Row(); + call_user_func($content, $row); + } else { + $row = new Row($content); + } + $this->append($row); + } + + /** + * @param mixed $span + * @return $this + */ + public function span($span) + { + $this->span = $span; + return $this; + } + + /** + * @param mixed $offset + * @return $this + */ + public function offset($offset) + { + $this->offset = $offset; + return $this; + } + + /** + * @param mixed $push + * @return $this + */ + public function push($push) + { + $this->push = $push; + return $this; + } + + /** + * @param mixed $pull + * @return $this + */ + public function pull($pull) + { + $this->pull = $pull; + return $this; + } + + /** + * @param mixed $tag + * @return $this + */ + public function tag($tag) + { + $this->tag = $tag; + return $this; + } + + /** + * @param array $contents + * @return $this + */ + public function contents(array $contents) + { + $this->contents = $contents; + return $this; + } +} diff --git a/builder/View/Layout/Content.php b/builder/View/Layout/Content.php new file mode 100644 index 0000000..bb8f0c9 --- /dev/null +++ b/builder/View/Layout/Content.php @@ -0,0 +1,75 @@ +row($content); + } + + + public function row($content) + { + if ($content instanceof Closure) { + $row = new Row(); + call_user_func($content, $row); + $this->addRow($row); + } else { + $row = new Row($content); + $this->addRow($row); + } + return $this; + } + + + protected function addRow(Row $row) + { + $this->rows[] = $row; + } + + /** + * @param bool $showHeader + * @return $this + */ + public function showHeader($showHeader = true) + { + $this->showHeader = $showHeader; + return $this; + } + + /** + * @param string $title + * @return $this + */ + public function title($title) + { + $this->title = $title; + return $this; + } + + /** + * @param string $description + * @return $this + */ + public function description($description) + { + $this->description = $description; + return $this; + } +} diff --git a/builder/View/Layout/Row.php b/builder/View/Layout/Row.php new file mode 100644 index 0000000..6ab5972 --- /dev/null +++ b/builder/View/Layout/Row.php @@ -0,0 +1,108 @@ +column(24, Html::make()->html($content)); + } + $this->column(24, $content); + } + } + + public function item($item) + { + if ($item instanceof \Closure) { + $this->addColumn(call_user_func($item)); + } else { + $this->addColumn($item); + } + } + + public function column($width, $content) + { + $width = $width < 1 ? round(24 * $width) : $width; + $column = new Column($content, $width); + $this->addColumn($column); + return $column; + } + + + protected function addColumn($column) + { + $this->columns[] = $column; + } + + /** + * 栅格间隔 + * @param int $gutter + * @return $this + */ + public function gutter($gutter) + { + $this->gutter = $gutter; + return $this; + } + + /** + * 布局模式,可选 flex,现代浏览器下有效 + * @param mixed $type + * @return $this + */ + public function rowType($type) + { + $this->rowType = $type; + return $this; + } + + /** + * flex 布局下的水平排列方式 + * start/end/center/space-around/space-between + * @param string $justify + * @return $this + */ + public function justify(string $justify) + { + $this->justify = $justify; + return $this; + } + + /** + * flex 布局下的垂直排列方式 + * @param string $align + * @return $this + */ + public function align(string $align) + { + $this->align = $align; + return $this; + } + + /** + * 自定义元素标签 + * @param string $tag + * @return $this + */ + public function tag(string $tag) + { + $this->tag = $tag; + return $this; + } +} diff --git a/builder/View/Tree.php b/builder/View/Tree.php new file mode 100644 index 0000000..c966a09 --- /dev/null +++ b/builder/View/Tree.php @@ -0,0 +1,440 @@ +attributes = new Attributes(); + $this->dataUrl = admin_api_url(request()->path()) . '/list'; + $this->isGetData = request()->header('getData')=="true"; + $this->toolbars = new Toolbars($this); + $this->batchActions = new BatchActions(); + $this->filter = new Filter($this->model); + } + + /** + * 获取自定义数据模型 + * @return Model|Builder + */ + public function model() + { + return $this->model; + } + + /** + * @return string + */ + public function getKeyName(): string + { + return $this->keyName; + } + + /** + * 自定义数据源路径 + * @param string $dataUrl + * @return $this + */ + public function dataUrl(string $dataUrl) + { + $this->dataUrl = $dataUrl; + return $this; + } + + /** + * @param string $method + * @return $this + */ + public function method(string $method) + { + $this->method = $method; + return $this; + } + + /** + * @return array + */ + public function getAppendFields(): array + { + return $this->appendFields; + } + + /** + * 数据返回附加字段 + * @param array $appendFields + * @return $this + */ + public function appendFields(array $appendFields) + { + $this->appendFields = $appendFields; + return $this; + } + + + /** + * @return bool + */ + public function isGetData(): bool + { + return $this->isGetData; + } + + + /** + * 设置树形表格 + * @param bool $tree + * @return $this + */ + public function tree($tree = true) + { + $this->showCheckbox = $tree; + return $this; + } + + + /** + * Grid添加字段 + * @param string $name 对应列内容的字段名 + * @param string $label 显示的标题 + * @param string $columnKey 排序查询等数据操作字段名称 + * @return Column + */ + public function column($name, $label = '', $columnKey = null) + { + if (Str::contains($name, '.')) { + $this->addRelationColumn($name, $label); + } + return $this->addColumn($name, $label, $columnKey); + } + + /** + * @param string $name + * @param string $label + * @param $columnKey + * @return Column + */ + protected function addColumn($name = '', $label = '', $columnKey = null) + { + $column = new Column($name, $label, $columnKey); + $column->setTree($this); + $this->columns[] = $column; + return $column; + } + + /** + * Add a relation column to grid. + * + * @param string $name + * @param string $label + * + * @return $this|bool|Column + */ + protected function addRelationColumn($name, $label = '') + { + if ($this->model) { + list($relation, $column) = explode('.', $name); + $model = $this->model()->eloquent(); + if (!method_exists($model, $relation) || !$model->{$relation}() instanceof Relations\Relation) { + } else { + $this->model()->with($relation); + } + + } + + } + + /** + * @param Column[] $columns + */ + protected function columns($columns) + { + $this->columnAttributes = collect($columns)->map(function (Column $column) { + return $column->getAttributes(); + })->toArray(); + } + + public function getColumns() + { + return $this->columns; + } + + + protected function applyQuery() + { + //快捷搜索 + $this->applyQuickSearch(); + $this->applyFilter(false); + } + + /** + * 自定义toolbars + * @param $closure + * @return $this + */ + public function toolbars($closure) + { + call_user_func($closure, $this->toolbars); + return $this; + } + + /** + * 自定义行操作 + * @param $closure + * @return $this + */ + public function actions($closure) + { + $this->actions = $closure; + return $this; + } + + /** + * 自定义批量操作 + * @param \Closure $closure + * @return $this + */ + public function batchActions(\Closure $closure) + { + call_user_func($closure, $this->batchActions); + return $this; + } + + /** + * 获取行操作 + * @param $row + * @param $key + * @return mixed + */ + public function getActions($row, $key) + { + $actions = new Actions($this); + $actions->row($row)->key($key); + if ($this->actions) call_user_func($this->actions, $actions); + return $actions->builderActions(); + } + + /** + * @param $bool + * @return $this + */ + public function top(bool $bool = true) + { + $this->top = $bool;// new Content(); + // call_user_func($closure, $this->top); + return $this; + } + + /** + * @param $closure + * @return $this + */ + public function bottom($closure) + { + $this->bottom = new Content(); + call_user_func($closure, $this->bottom); + return $this; + } + + + /** + * 自定义数据 + * @param $data + * @param $current_page + * @param $per_page + * @param $last_page + * @param $total + * @return $this + */ + public function customData($data, $current_page = 0, $per_page = 0, $last_page = 0, $total = 0) + { + $this->customData = [ + 'current_page' => (int)$current_page, + 'per_page' => (int)$per_page, + 'last_page' => (int)$last_page, + 'total' => (int)$total, + 'data' => $data, + ]; + return $this; + } + + + /** + * data + * @return array + */ + protected function data() + { + if ($this->customData) { + $this->customData['data'] = $this->model()->displayData($this->customData['data']); + return [ + 'code' => 200, + 'data' => $this->customData + ]; + } + $this->applyQuery(); + $data = $this->model->buildData(); + return [ + 'code' => 200, + 'data' => $data + ]; + } + + /** + * @inheritDoc + */ + public function jsonSerialize() + { + if (count($this->columnAttributes) <= 0) { + $this->columns($this->columns); + } + if ($this->isGetData) { + return $this->data(); + } else { + $viewData['componentName'] = $this->componentName; + $viewData['routers'] = [ + 'resource' => admin_api_url(request()->path()), + ]; + $viewData['keyName'] = $this->keyName; + $viewData['selection'] = $this->attributes->selection; + $viewData['showCheckbox'] = $this->showCheckbox; + $viewData['columnAttributes'] = $this->columnAttributes; + $viewData['attributes'] = (array)$this->attributes; + $viewData['dataUrl'] = $this->dataUrl; + $viewData['method'] = $this->method; + $viewData['toolbars'] = $this->toolbars->builderData(); + $viewData['actions'] = $this->actions; + $viewData['quickSearch'] = $this->quickSearch; + $viewData['top'] = $this->top; + $viewData['checkedKeys'] = $this->checkedKeys; + $viewData['bottom'] = $this->bottom; + $viewData['ref'] = $this->getRef(); + $viewData['refData'] = $this->refData; + return array_filter($viewData); + } + } + /** + * @return bool + */ + public function isShowCheckbox(): bool + { + return $this->showCheckbox; + } + + /** + * @param bool $showCheckbox + */ + public function setShowCheckbox(bool $showCheckbox): void + { + $this->showCheckbox = $showCheckbox; + } + + public function setCheckedKeys(array $checkedKeys) + { + $this->checkedKeys = $checkedKeys; + return $this; + } + + public function setGetData(bool $false) + { + $this->isGetData = $false; + return $this; + } +} diff --git a/builder/View/Tree/Actions.php b/builder/View/Tree/Actions.php new file mode 100644 index 0000000..8b7e9a4 --- /dev/null +++ b/builder/View/Tree/Actions.php @@ -0,0 +1,51 @@ +row; + } + + /** + * @param mixed $row + * @return $this + */ + public function row($row) + { + $this->row = $row; + return $this; + } + + /** + * 当前行下标 + * @return mixed + */ + public function getKey() + { + return $this->key; + } + + /** + * @param mixed $key + * @return $this + */ + public function key($key) + { + $this->key = $key; + return $this; + } +} diff --git a/builder/View/Tree/Column.php b/builder/View/Tree/Column.php new file mode 100644 index 0000000..887124c --- /dev/null +++ b/builder/View/Tree/Column.php @@ -0,0 +1,170 @@ +attributes = new Attributes(); + $this->name = $name; + $this->columnKey = $columnKey; + $this->label = $this->formatLabel($label); + $this->initAttributes(); + } + + protected function initAttributes() + { + $this->attributes->prop = $this->name; + $this->attributes->label = $this->label; + if (empty($this->columnKey)) { + $this->columnKey = $this->name; + } + $this->attributes->columnKey = $this->columnKey; + $name = str_replace('.', '-', $this->name); + } + + protected function formatLabel($label) + { + if ($label) { + return $label; + } + $label = ucfirst($this->name); + return (str_replace(['.', '_'], ' ', $label)); + } + + public function setTree(Tree $tree) + { + $this->tree = $tree; + } + + /** + * 自定义值 + * + * @param Closure $callback + * + * @return $this + */ + public function customValue(Closure $callback) + { + $this->displayCallbacks = $callback; + + return $this; + } + + public function customValueUsing($row, $value) + { + return $this->displayCallbacks ? call_user_func($this->displayCallbacks, $row, $value) : $value; + } + + /** + * 设置组件 + * @param $component + * @return $this + * @deprecated + */ + public function displayComponent($component) + { + if ($component instanceof Closure) { + $this->displayComponentAttrs(call_user_func($component)); + } else { + $this->displayComponentAttrs($component); + } + return $this; + } + + /** + * 设置组件 + * @param $component + * @return $this + */ + public function component($component) + { + if ($component instanceof Closure) { + $this->displayComponentAttrs(call_user_func($component)); + } else { + $this->displayComponentAttrs($component); + } + return $this; + } + + public function getAttributes() + { + return $this->attributes; + } + + /** + * @return Grid + */ + public function getTree() + { + return $this->tree; + } + + /** + * @return array|string|null + */ + public function getLabel() + { + return $this->label; + } + + /** + * @return mixed + */ + public function getName() + { + return $this->name; + } + + + /** + * @return null + */ + public function getColumnKey() + { + return $this->columnKey; + } + + /** + * 获取默认值 + * @return mixed + */ + public function getDefaultValue() + { + return $this->defaultValue; + } + + /** + * 设置默认值 + * @param mixed $defaultValue + * @return $this + */ + public function defaultValue($defaultValue) + { + $this->defaultValue = $defaultValue; + return $this; + } + +} diff --git a/builder/View/Tree/Toolbars.php b/builder/View/Tree/Toolbars.php new file mode 100644 index 0000000..eb23b84 --- /dev/null +++ b/builder/View/Tree/Toolbars.php @@ -0,0 +1,89 @@ +grid = $grid; + $this->createButton = new CreateButton(); + $this->createButton->content("添加")->icon("el-icon-plus"); + } + + /** + * @return CreateButton + */ + public function createButton(): CreateButton + { + return $this->createButton; + } + + /** + * 隐藏工具栏 + * @return Toolbars + */ + public function hide() + { + $this->show = false; + return $this; + } + + + + /** + * @param mixed $hideCreateButton + * @return $this + */ + public function hideCreateButton($hideCreateButton = true) + { + $this->hideCreateButton = $hideCreateButton; + return $this; + } + + public function addLeft($action) + { + $this->addLeft = collect($this->addLeft)->push($action)->all(); + return $this; + } + + + public function addRight($action) + { + $this->addRight = collect($this->addRight)->push($action)->all(); + return $this; + } + + public function builderData() + { + $left = collect($this->left); + $right = collect($this->right); + if (!$this->hideCreateButton) { + $right->push($this->createButton); + }; + foreach ($this->addLeft as $addLeft) { + $left->push($addLeft); + } + foreach ($this->addRight as $addRight) { + $right->prepend($addRight); + } + return [ + "show"=>$this->show, + "left" => $left, + "right" => $right + ]; + } +} diff --git a/config/autoload/jwt.php b/config/autoload/jwt.php index 88459d5..f596bf5 100644 --- a/config/autoload/jwt.php +++ b/config/autoload/jwt.php @@ -2,14 +2,14 @@ declare(strict_types=1); return [ - 'login_type' => env('JWT_LOGIN_TYPE', 'sso'), // 登录方式,sso为单点登录,mpop为多点登录 + 'login_type' => env('JWT_LOGIN_TYPE', 'mpop'), // 登录方式,sso为单点登录,mpop为多点登录 /** * 单点登录自定义数据中必须存在uid的键值,这个key你可以自行定义,只要自定义数据中存在该键即可 */ 'sso_key' => 'id', - 'secret' => env('JWT_SECRET', 'mineAdmin'), // 非对称加密使用字符串,请使用自己加密的字符串 + 'secret' => env('JWT_SECRET', 'TkVue.com'), // 非对称加密使用字符串,请使用自己加密的字符串 /** * JWT 权限keys @@ -74,9 +74,9 @@ return [ /** * 黑名单缓存token时间,注意:该时间一定要设置比token过期时间要大一点,默认为1天,最好设置跟过期时间一样 */ - 'blacklist_cache_ttl' => env('JWT_TTL', 7200), + 'blacklist_cache_ttl' => env('JWT_TTL', 86400), - 'blacklist_prefix' => 'MineAdmin_jwt', // 黑名单缓存的前缀 + 'blacklist_prefix' => 'black_jwt', // 黑名单缓存的前缀 /** * 区分不同场景的token,比如你一个项目可能会有多种类型的应用接口鉴权,下面自行定义,我只是举例子