MichaelMano / Laramin
管理样式和脚本
This package is auto-updated.
Last update: 2024-09-06 11:41:07 UTC
README
Laramin 是一个以材料设计为灵感的仪表板模板。我创建 Laramin 的目的是让您的 Laravel 项目快速启动,无需花费太多时间在设计管理面板和处理相关资产(如标签输入)的逻辑。
Laramin 不处理任何后端逻辑。
目录
- 安装 _ 路由 _ 控制器 _ 视图 _ 登录表单
- 配置 _ 菜单 _ 项目管理器
- 元素 _ 默认 Blade _ 网格系统 _ 厨房水槽 _ 标签 _ 卡片 _ 表单 * 实用助手
- Vue 组件 _ 模态框 _ 图片裁剪器 _ 闪存消息 _ 加载覆盖 _ 标签页 _ 标签输入 * 工具提示
- Laravel 组件 * 删除项目
- 原生 JavaScript * 可排序项
安装
一旦您的 Laravel 项目运行起来,您可以使用 composer 安装 laramin:composer require michaelmano/laramin,然后将其添加到您的服务提供者中 config/app.php
MichaelMano\Laramin\LaraminServiceProvider::class,
然后使用以下命令发布资源:
php artisan vendor:publish --provider="MichaelMano\Laramin\LaraminServiceProvider"
强烈建议不要编辑 public/michaelmano/laramin 目录下的文件,因为 Laramin 仍在测试版,将会进行很多更新。
路由
由于 Laramin 是一个管理面板,我将从 php artisan make:auth 开始,然后编辑我的路由服务提供者 app/Providers/RouteServiceProvider.php,并在 mapWebRoutes 内添加以下内容。
protected function mapWebRoutes() { Route::middleware('web') ->namespace($this->namespace) ->group(base_path('routes/web.php')); Route::middleware('web') ->namespace($this->namespace) ->group(base_path('routes/dashboard.php')); }
然后在路由目录中创建一个名为 dashboard.php 的文件,这里是创建所有仪表板路由的地方。
<?php Route::group([ 'middleware' => 'auth', 'namespace' => 'Dashboard', 'prefix' => 'dashboard', 'as' => 'dashboard.' ], function () { Route::get('/', 'DashboardController@index')->name('index'); Route::resource('/pages', 'PageController'); Route::resource('/posts', 'PostController'); Route::resource('/faqs', 'FaqController'); });
为了阻止用户注册,我将编辑 app/Http/Controllers/Auth/RegisterController.php 并在底部添加一个名为 forbidden 的新公共函数
public function forbidden() { abort(403); }
然后编辑 web/routes.php 文件并添加以下内容:
Route::get('register', 'Auth\RegisterController@forbidden');
但是,如果您的项目使用注册,您可以设置自己的中间件来处理仪表板。我还会更新
app/Http/Controllers/Auth/LoginController.php app/Http/Controllers/Auth/RegisterController.php app/Http/Controllers/Auth/ResetPasswordController.php
使用 protected $redirectTo = '/home'; 修改为 protected $redirectTo = '/dashboard';
并且
app/Http/Middleware/RedirectIfAuthenticated.php
return redirect('/home'); 修改为 return redirect('/dashboard');
视图
现在我将创建一个名为 dashboard 的视图文件夹 resources/views/dashboard 并创建一个 index.blade.php 文件,并在其中放置以下代码。
@extends('laramin::layouts.standard') @push('styles') You can push any overrides or additional styles here. @endpush @section('standard-content') Content here @endsection @push('scripts') You can push any javascript here. @endpush
这是用于 laramin 的 blade 布局。
登录表单
现在覆盖 resources/views/auth/login.blade.php 下的登录页面,使用以下内容。
@extends('laramin::layouts.login') @section('login-form') <form class="Form Box Box--padded animated zoomIn"method="POST" action="{{ route('login') }}"> {{ csrf_field() }} <fieldset class="Form__fieldset"> <label class="Form__label" for="email">Email address</label> <input class="Form__input Form__input--email" name="email" id="email" type="email" placeholder="jane@doe.me" required autofocus> </fieldset> <fieldset class="Form__fieldset"> <label class="Form__label" for="password">Password</label> <input class="Form__input Form__input--password" name="password" id="password" type="password" required> </fieldset> <fieldset class="Form__fieldset"> <button class="Button" type="submit">Login</button> </fieldset> <fieldset class="Form__fieldset"> <a class="Button Button--text-link" href="{{ route('password.request') }}">Forgot password?</a> </fieldset> </form> @endsection
控制器
生成一个可以用于处理仪表板索引的控制器 php artisan make:controller Dashboard/DashboardController 以及可能需要的其他控制器,例如如果项目有动态页面,可以使用 php artisan make:controller Dashboard/PageController -r 通过传递 -r 参数为资源,例如 CRUD。
配置
配置文件位于项目下的 config/laramin.php
菜单
您可以通过编辑配置来设置菜单,此布局使用 font-awesome,用户头像将是一个 font awesome 用户图标,除非您的用户对象有指向图像 URL 的 $user->avatar。
'sidebar_links' => [ [ 'url' => '/dashboard', 'name' => 'Dashboard', 'icon' => 'fa-window-maximize', ], [ 'url' => '/dashboard/pages', 'name' => 'Pages', 'icon' => 'fa-file-text', ], [ 'url' => '/dashboard/posts', 'name' => 'Posts', 'icon' => 'fa-newspaper-o', ], [ 'url' => '/dashboard/faqs', 'name' => 'FAQ\'s', 'icon' => 'fa-question', ], ],
项目管理器
项目管理器用于创建一个帮助表单,客户可以使用它发送电子邮件或获取他们的联系信息,如果您删除值,则帮助按钮将不会显示。
'project_manager' => [ 'name' => 'John Smith', 'email' => 'jsmith@example.com', 'phone' => '555 55 555', 'contact_finalised_message' => 'Thank you for contacting us, we will get back to you as soon as possible.', ],
元素
网格系统
Laramin 使用 Buzuki,这是一个移动优先、响应式 BEM 风格 flexbox CSS 网格系统。
水槽
如果项目设置为本地环境,laramin 有一个路由可以查看您可以在系统中使用的所有元素,同时显示代码。您可以从 http://your-domain.tld/laramin 访问它。
框
框元素只是一个具有阴影的元素,您还可以添加一个修饰类 Box--padded,这将向框添加填充。
砖墙
laramin 中的砖墙使用 CSS 网格,不使用任何 JavaScript。下面的代码使用带有 Cards 的 Masonry 元素。
<div class="Masonry__panel"> <div class="Box Card"> <header class="Card__header"> <h5 class="Heading util-breakaway-bottom-2"><a href="{{ route('dashboard.posts.edit', $post) }}">{{ $post->title }}</a></h5> <small class="Heading__meta"><strong>Slug: </strong>{{ $post->slug }}</small> </header> <div class="Card__content"> {{ $post->body }} </div> <footer class="Card__footer Row Row--valign-center"> <div class="Cell Cell--12/12@xs Cell--4/12@xl"> <a href="{{ route('dashboard.posts.edit', $post) }}" class="Button Button--round"><i class="fa fa-pencil"></i></a> @include('laramin::components.confirm-delete', [ 'value' => $post->title, 'url' => route('dashboard.posts.destroy', $post), 'remove' => '.Masonry__panel' ]) </div> <div class="Cell Cell--12/12@xs Cell--8/12@xl Cell--align-right@xl"> @foreach($post->tags as $tag) <div class="Tag"> <div class="Tag__name">{{ $tag->name }}</div> </div> @endforeach </div> </footer> </div> </div>
标签
标签可以指定如下。
<div class="Tag"> <div class="Tag__name">Tag with info</div> <div class="Tag__info">v2</div> </div> <div class="Tag"> <div class="Tag__name">Tag without info</div> </div> <div class="Tag"> <div class="Tag__name">Tag with delete</div> <div class="Tag__remove"><i class="fa fa-times"></i></div> </div>
卡片
卡片组件有 3 个元素,标题、内容和页脚。下面我在标题中放置了帖子的标题,以 slug 作为标题元数据,然后是帖子内容作为 Card__content,在页脚中我渲染了 删除项目 laravel 组件和帖子的 标签。
<div class="Box Card"> <header class="Card__header"> <h5 class="Heading util-breakaway-bottom-2"><a href="{{ route('dashboard.posts.edit', $post) }}">{{ $post->title }}</a></h5> <small class="Heading__meta"><strong>Slug: </strong>{{ $post->slug }}</small> </header> <div class="Card__content"> {{ $post->body }} </div> <footer class="Card__footer Row Row--valign-center"> <div class="Cell Cell--12/12@xs Cell--4/12@xl"> <a href="{{ route('dashboard.posts.edit', $post) }}" class="Button Button--round"><i class="fa fa-pencil"></i></a> @include('laramin::components.confirm-delete', [ 'value' => $post->title, 'url' => route('dashboard.posts.destroy', $post), 'remove' => '.Masonry__panel' ]) </div> <div class="Cell Cell--12/12@xs Cell--8/12@xl Cell--align-right@xl"> @foreach($post->tags as $tag) <div class="Tag"> <div class="Tag__name">{{ $tag->name }}</div> </div> @endforeach </div> </footer> </div>
表单
<form enctype="multipart/form-data" class="Form" method="POST" action="{{ route('login') }}"> <fieldset class="Form__fieldset"> <label class="Form__label" for="fullName">Text</label> <input class="Form__input Form__input--text" name="fullName" id="fullName" type="text" required> </fieldset> <fieldset class="Form__fieldset"> <label class="Form__label" for="phone">Phone</label> <input class="Form__input Form__input--tel" name="phone" id="phone" type="tel" required> </fieldset> <fieldset class="Form__fieldset"> <label class="Form__label" for="comment">Textarea</label> <textarea class="Form__input Form__input--textarea"></textarea> </fieldset> <fieldset class="Form__fieldset"> <label class="Form__label" for="date">Date</label> <input class="Form__input Form__input--date" type="date"> </fieldset> <fieldset class="Form__fieldset"> <label class="Form__label" for="checkbox">Checkbox <input class="Form__input Form__input--checkbox" name="checkbox" id="checkbox" type="checkbox"> </label> </fieldset> <fieldset class="Form__fieldset"> <label class="Form__label" for="radio">Radio <input class="Form__input Form__input--radio" name="radio" id="radio" type="radio"> </label> </fieldset> <fieldset class="Form__fieldset"> <label class="Form__label" for="email">Email</label> <input class="Form__input Form__input--email" name="email" id="email" type="email"> </fieldset> <fieldset class="Form__fieldset"> <input type="file" name="file[]" id="file" class="Form__input Form__input--file" data-multiple-caption="{count} files selected" multiple /> <label for="file"><i class="fa fa-upload"></i> <span>Choose a file…</span></label> </fieldset> <fieldset class="Form__fieldset"> <label class="Form__label" for="month">Month</label> <input class="Form__input Form__input--month" name="month" id="month" type="month"> </fieldset> <fieldset class="Form__fieldset"> <label class="Form__label" for="number">Number</label> <input class="Form__input Form__input--number" name="number" id="number" type="number"> </fieldset> <fieldset class="Form__fieldset"> <label class="Form__label" for="password">Password</label> <input class="Form__input Form__input--password" name="password" id="password" type="password"> </fieldset> <fieldset class="Form__fieldset"> <label class="Form__label" for="range">Range</label> <input class="Form__input Form__input--range" name="range" id="range" type="range"> </fieldset> <fieldset class="Form__fieldset"> <label class="Form__label" for="search">Search</label> <input class="Form__input Form__input--search" name="search" id="search" type="search"> </fieldset> <fieldset class="Form__fieldset"> <label class="Form__label" for="time">Time</label> <input class="Form__input Form__input--time" name="time" id="time" type="time"> </fieldset> <fieldset class="Form__fieldset"> <label class="Form__label" for="url">Url</label> <input class="Form__input Form__input--url" name="url" id="url" type="url"> </fieldset> <fieldset class="Form__fieldset"> <label class="Form__label" for="week">Week</label> <input class="Form__input Form__input--week" name="week" id="week" type="week"> </fieldset> <fieldset class="Form__fieldset"> <button class="Button" type="submit">Submit</button> </fieldset> </form>
实用程序助手
边距
util-breakaway-${top/right/bottom/left}-${0/1/2/3/4/5/6/7/8}-${5} 可以这样使用 util-breakaway-bottom-0 从底部移除边距或 util-breakaway-top-1-5 向顶部添加 1.5rem 边距。
动画
Laramin 使用 animate.css 库 Animate.css
Vue 组件
模态框
<button class="Button" @click="showModal('modal')">Show Modal</button> <laramin-modal ref="modal" @close="hideModal"> <template slot="title">Modal</template> <template slot="body"> <p>Modal Body Content</p> </template> <p slot="footer">Footer Content</p> </laramin-modal>
按钮点击将触发一个名为 showModal() 的函数,该函数接受一个参数,即模态框的引用,该引用也必须是唯一的 ref="modal",并且模态框可以传递一个动画属性 <laramin-modal ref="name" @close="hideModal" animation-in="flipInX" animation-out="flipOutX"> 默认动画为 bounceInLeft 和 bounceOutRight。
图像裁剪器
<laramin-crop image="http://via.placeholder.com/1920x800" label="file button text (default is choose an image)" :min-width="1920" :min-height="800" name="image"></laramin-crop>
名称和图像是必需的,名称将设置文件输入的名称,并在后端应用程序中创建一个隐藏的输入 ${name}_dimentions,您可以使用它来裁剪上传的图像。
min-height 和 min-width 属性用于设置图片的宽高比,如果用户尝试选择小于这些尺寸的图片,将会出现一个错误信息并重置文件输入,同时确保裁剪器的宽高比与设置的 width / height 相同。
您传递的图片将是默认图片,用户必须使用上传按钮选择一张图片后,裁剪器才会启用。
以下是我用来管理媒体的一个示例,它还使用了 Intervention Image 包。
<?php namespace App\Traits; use Image; use Carbon\Carbon; use Illuminate\Support\Facades\Storage; trait MediaManager { public function uploadImage($data) { $dimentions = json_decode($data['dimentions'], true); $width = floor($dimentions['width']); $height = floor($dimentions['height']); $x = floor($dimentions['x']); $y = floor($dimentions['y']); $size = $data['size'] ?? '1920, 800'; $image_name = 'images/'.implode('-', [$this->id, Carbon::now()->timestamp, str_random(10)]).'.'.$data['image']->getClientOriginalExtension(); $img = Image::make(file_get_contents($data['image'])); $img->crop($width, $height, $x, $y); $img->resize($size); $img->stream(); Storage::put('public/'.$image_name, $img); return $image_name; } public function deleteImage($location) { if (!empty($location) && Storage::exists('public/'.$location)) { return Storage::delete('public/'.$location); } } }
现在您只需像这样在模型中使用这个属性
<?php namespace App; use App\Traits\MediaManager; use Illuminate\Database\Eloquent\Model; class Page extends Model { use MediaManager; }
在控制器中,您只需检查更新是否包含图片字段。
<?php namespace App\Http\Controllers\Dashboard; use App\Page; use Illuminate\Http\Request; use App\Http\Controllers\Controller; class PageController extends Controller { public function update(Page $page, Request $request) { if ($request->has(['image', 'image_dimentions'])) { $data = [ 'image' => $request->file('image'), 'dimentions' => $request->get('image_dimentions') ]; $page->deleteImage($page->feature_image); $page->feature_image = $page->uploadImage($data); $page->save(); } $page->update($request->only('title', 'body')); return back()->withMessage('The page has been updated.'); } }
闪存信息
闪存信息可以通过两种方式传递,一种是在会话中设置一个名为 message(或 laravel 传递到表单字段的错误)的变量。第二种方式是如果您使用 JavaScript,则可以像这样将消息推送到 laramin 对象 laramin.messages.push({type: 'error', 'message'});,其中有两种类型。 error 和 success。
加载遮罩
加载遮罩可以像上面的闪存信息一样触发。 laramin.loading = true; 但是您必须在加载完成后禁用此功能 laramin.loading = false;,除非您正在等待页面刷新。以下是使用 Promise 设置可排序项的示例。
let promises = []; laramin.loading = true; Q.each(sortableInput, (sortableInput, index) => { let promise = axios.post(sortableInput.form.action, { _method: "PATCH", order: index + 1 }); promises.push(promise); }); Promise.all(promises).then(() => { laramin.loading = false; });
因此,我设置了窗口为加载状态,创建了一个 Promise,然后在它们全部完成时将加载状态设置为 false。
标签
<laramin-tabs> <laramin-tab name="Tab 1"> <p>Content for tab 1</p> </laramin-tab> <laramin-tab name="Tab 2"> <p>Content for tab 2</p> </laramin-tab> </laramin-tabs>
这将创建一个标签组件,同时允许您通过使用名称作为哈希值链接到页面上的特定标签,例如 http://site-url.com/page-url#tab-2,这将打开标签 2 而不是 1。
标签输入
<laramin-tags-input name="tags[]" :autocomplete="['Suggestion 1',' Suggestion 2', 'Suggestion 3']" :tags="['tag1', 'tag2', 'tag3']"> </laramin-tags-input>`
这将创建一个输入并添加自动完成建议和默认标签,以便预先填充。name 属性还会创建具有标签输入中值的隐藏输入。例如,tag1、tag2、tag3,您可以这样使用。
<?php namespace App\Http\Controllers\Dashboard; use App\Post; use App\Tag; use App\Http\Controllers\Controller; class PostController extends Controller { public function update(Post $post) { $tags = collect(request('tags'))->map(function ($tag) { return Tag::firstOrCreate(['name' => ucfirst($tag)])->id; }); $post->tags()->sync($tags); $post->update(request()->except('_token', '_method', 'tags')); return back()->withMessage('The post has been updated'); } }
工具提示
工具提示是自解释的。 <laramin-tooltip tooltip="测试工具提示">这将显示测试工具提示悬停时显示的内容。</laramin-tooltip>
Laravel 组件
删除项
@include('laramin::components.confirm-delete', [ 'value' => $page->title, 'url' => route('dashboard.pages.destroy', $page), 'delete_text' => 'Delete Page', 'remove' => '.Masonry__panel' ])
这将创建一个按钮,文本为 删除页面。如果您未传递 delete_text,则它将仅显示一个垃圾桶,当点击时会显示一个包含输入的模态窗口,您必须在此处输入值才能删除它,例如页面标题。删除操作将通过 AJAX 完成并删除第一个具有指定类的父元素。
纯 JavaScript
可排序项
要使用可排序功能,您需要在模型中有一个名为 order 的字段,然后在渲染时像这样操作
<div class="Row js-sortable"> @foreach($faqs as $faq) <div class="Cell Cell--12/12@xs js-sortable-item"> <div class="Box Card"> <header class="Card__header"> <h6 class="Heading"><a href="{{ route('dashboard.faqs.edit', $faq) }}">{{ $faq->title }}</a></h6> </header> <div class="Card__content"> {{ $faq->body }} </div> <footer class="Card__footer Row Row--valign-center"> <div class="Cell Cell--align-left Cell--6/12@xs"> <button class="Button Button--round js-sortable-tile"><i class="fa fa-arrows"></i></button> <form action="{{ route('dashboard.faqs.update', $faq) }}" method="POST"> <input class="js-sortable-input" type="hidden" value="{{ $faq->order }}"> </form> </div> <div class="Cell Cell--align-right Cell--6/12@xs"> <a href="{{ route('dashboard.faqs.edit', $faq) }}" class="Button Button--round"><i class="fa fa-pencil"></i></a> @include('laramin::components.confirm-delete', [ 'value' => $faq->title, 'url' => route('dashboard.faqs.destroy', $faq), 'remove' => '.js-sortable-item' ]) </div> </footer> </div> </div> @endforeach </div>
这将渲染每个项目,每个项目都有一个隐藏的表单,它将更改 order 输入的值并将其发送到您指定的路由。只有通过可排序按钮 <button class="Button Button--round js-sortable-tile"><i class="fa fa-arrows"></i></button> 拖动时,项目才会更改顺序。








