界面


代码
<?php
namespace App\Admin\Controllers\Demo;
use App\Admin\Extensions\Tools\UserGender;
use App\Http\Controllers\Controller;
use App\Models\ChinaArea;
use App\Models\User;
use Encore\Admin\Form;
use Encore\Admin\Grid;
use Encore\Admin\Facades\Admin;
use Encore\Admin\Controllers\ModelForm;
use Encore\Admin\Layout\Content;
use Encore\Admin\Widgets\Box;
use Encore\Admin\Widgets\Tab;
use Encore\Admin\Widgets\Table;
use Illuminate\Support\Facades\Request;
class UserController extends Controller
{
use ModelForm;
...
protected function grid()
{
return Admin::grid(User::class, function (Grid $grid) {
$grid->model()->gender(Request::get('gender'));
$grid->id('ID')->sortable();
$grid->name()->editable();
$grid->column('expand')->expand(function () {
$profile = (array) $this->profile;
$profile = array_only($profile, ['homepage', 'gender', 'birthday', 'address', 'last_login_at', 'last_login_ip', 'lat', 'lng']);
return new Table([], $profile);
}, 'Profile');
$grid->column('position')->openMap(function () {
return [$this->profile['lat'], $this->profile['lng']];
}, 'Position');
$grid->column('profile.homepage')->urlWrapper();
$grid->email()->prependIcon('envelope');
$grid->profile()->mobile()->prependIcon('phone');
$grid->column('profile.age')->progressBar(['success', 'striped'], 'xs')->sortable();
$grid->created_at();
$grid->updated_at();
$grid->filter(function ($filter) {
$filter->useModal();
$filter->like('name');
$filter->like('email');
$filter->equal('address.province_id', 'Province')
->select(ChinaArea::province()->pluck('name', 'id'))
->load('address.city_id', '/admin/api/china-area/city');
$filter->equal('address.city_id', 'City')
->select(function ($id) {
return ChinaArea::options($id);
})->load('address.district_id', '/admin/api/china-area/district');
$filter->equal('address.district_id', 'District')
->select(function ($id) {
return ChinaArea::options($id);
});
});
$grid->tools(function ($tools) {
//$tools->append(new RefreshTimer(5000));
$tools->append(new UserGender());
//$tools->append(new ButtonGroup());
});
$grid->actions(function ($actions) {
if ($actions->getKey() % 2 == 0) {
$actions->disableDelete();
$actions->append('<a href=""><i class="fa fa-eye"></i></a>');
} else {
$actions->disableEdit();
$actions->prepend('<a href=""><i class="fa fa-paper-plane"></i></a>');
}
});
$grid->disableBatchDeletion();
});
}
public function form()
{
return User::form(function (Form $form) {
$form->model()->makeVisible('password');
$form->tab('Basic', function (Form $form) {
$form->display('id');
$form->text('name')->rules('required');
$form->email('email')->rules('required');
$form->display('created_at');
$form->display('updated_at');
})->tab('Profile', function (Form $form) {
$form->url('profile.homepage');
$form->ip('profile.last_login_ip');
$form->datetime('profile.last_login_at');
$form->color('profile.color')->default('#c48c20');
$form->mobile('profile.mobile')->default(13524120142);
$form->date('profile.birthday');
// $form->map('profile.lat', 'profile.lng', 'Position')->useTencentMap();
$form->slider('profile.age', 'Age')->options(['max' => 50, 'min' => 20, 'step' => 1, 'postfix' => 'years old']);
$form->datetimeRange('profile.created_at', 'profile.updated_at', 'Time line');
})->tab('Sns info', function (Form $form) {
$form->text('sns.qq');
$form->text('sns.wechat');
$form->text('sns.weibo');
$form->text('sns.github');
$form->text('sns.google');
$form->text('sns.facebook');
$form->text('sns.twitter');
$form->display('sns.created_at');
$form->display('sns.updated_at');
})->tab('Address', function (Form $form) {
$form->select('address.province_id')->options(
ChinaArea::province()->pluck('name', 'id')
)->load('address.city_id', '/admin/api/china-area/city');
$form->select('address.city_id')->options(function ($id) {
return ChinaArea::options($id);
})->load('address.district_id', '/admin/api/china-area/district');
$form->select('address.district_id')->options(function ($id) {
return ChinaArea::options($id);
});
$form->text('address.address');
})->tab('Password', function (Form $form) {
$form->password('password')->rules('confirmed');
$form->password('password_confirmation');
});
$form->ignore(['password_confirmation']);
});
...
}
模型
app/Models/User.php
<?php
namespace App\Models;
use Encore\Admin\Traits\AdminBuilder;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
use AdminBuilder;
protected $table = 'demo_users';
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = ['name', 'email', 'password'];
/**
* The attributes excluded from the model's JSON form.
*
* @var array
*/
protected $hidden = ['password', 'remember_token'];
/**
* @return \Illuminate\Database\Eloquent\Relations\HasOne
*/
public function profile()
{
return $this->hasOne(UserProfile::class);
}
public function sns()
{
return $this->hasOne(UserSns::class);
}
public function address()
{
return $this->hasOne(UserAddress::class);
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function friends()
{
return $this->belongsToMany(User::class, 'friends', 'user_id', 'friend_id');
}
/**
*
*
* @param $query
* @param $gender
* @return mixed
*/
public function scopeGender($query, $gender)
{
if (!in_array($gender, ['m', 'f'])) {
return $query;
}
return $query->whereHas('profile', function ($query) use ($gender) {
$query->where('gender', $gender);
});
}
}
app/Models/UserProfile.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class UserProfile extends Model
{
protected $table = 'demo_user_profiles';
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function user()
{
return $this->belongsTo(User::class);
}
}
app/Models/UserSns.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class UserSns extends Model
{
protected $table = 'demo_user_sns';
public function user()
{
return $this->belongsTo(User::class);
}
}
app/Models/UserAddress.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class UserAddress extends Model
{
protected $table = 'demo_user_address';
public function user()
{
return $this->belongsTo(User::class);
}
}
表结构
CREATE TABLE `demo_users` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
`email` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
`avatar` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL,
`password` varchar(60) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `demo_user_profiles` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`homepage` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`mobile` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`avatar` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`document` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`gender` enum('m','f') COLLATE utf8_unicode_ci DEFAULT NULL,
`birthday` date DEFAULT NULL,
`address` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`color` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL,
`age` tinyint(4) DEFAULT NULL,
`last_login_at` timestamp NULL DEFAULT NULL,
`last_login_ip` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`lat` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`lng` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `demo_user_sns` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(11) unsigned NOT NULL,
`qq` varchar(255) COLLATE utf8_unicode_ci DEFAULT '',
`wechat` varchar(255) COLLATE utf8_unicode_ci DEFAULT '',
`weibo` varchar(255) COLLATE utf8_unicode_ci DEFAULT '',
`github` varchar(255) COLLATE utf8_unicode_ci DEFAULT '',
`google` varchar(255) COLLATE utf8_unicode_ci DEFAULT '',
`facebook` varchar(255) COLLATE utf8_unicode_ci DEFAULT '',
`twitter` varchar(255) COLLATE utf8_unicode_ci DEFAULT '',
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`),
KEY `user_id` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `demo_user_address` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(11) unsigned NOT NULL,
`province_id` int(11) unsigned DEFAULT '0',
`city_id` int(11) unsigned DEFAULT '0',
`district_id` int(11) unsigned DEFAULT '0',
`address` varchar(255) COLLATE utf8_unicode_ci DEFAULT '',
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`),
KEY `user_id` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
扩展
Add following code in `app/Admin/bootstrap.php`:
use App\Admin\Extensions\Column\ExpandRow;
use App\Admin\Extensions\Column\OpenMap;
use App\Admin\Extensions\Column\FloatBar;
use App\Admin\Extensions\Column\UrlWrapper;
use Encore\Admin\Grid\Column;
Admin::js('/packages/clipboard/dist/clipboard.min.js');
Column::extend('expand', ExpandRow::class);
Column::extend('openMap', OpenMap::class);
Column::extend('floatBar', FloatBar::class);
Column::extend('urlwrapper', UrlWrapper::class);
Column::extend('prependIcon', function ($value, $icon) {
return "<span style='color: #999;'><i class='fa fa-$icon'></i> $value</span>";
});
Admin/Extensions/Column/ExpandRow.php
<?php
namespace App\Admin\Extensions\Column;
use Encore\Admin\Admin;
use Encore\Admin\Grid\Displayers\AbstractDisplayer;
class ExpandRow extends AbstractDisplayer
{
public function display(\Closure $callback = null, $btn = '')
{
$callback = $callback->bindTo($this->row);
$html = call_user_func($callback);
$script = <<<EOT
$('.grid-expand').on('click', function () {
if ($(this).data('inserted') == '0') {
var key = $(this).data('key');
var row = $(this).closest('tr');
var html = $('template.grid-expand-'+key).html();
row.after("<tr><td colspan='"+row.find('td').length+"' style='padding:0 !important; border:0px;'>"+html+"</td></tr>");
$(this).data('inserted', 1);
}
$("i", this).toggleClass("fa-caret-right fa-caret-down");
});
EOT;
Admin::script($script);
$btn = $btn ?: $this->column->getName();
$key = $this->getKey();
return <<<EOT
<a class="btn btn-xs btn-default grid-expand" data-inserted="0" data-key="{$key}" data-toggle="collapse" data-target="#grid-collapse-{$key}">
<i class="fa fa-caret-right"></i> $btn
</a>
<template class="grid-expand-{$key}">
<div id="grid-collapse-{$key}" class="collapse">$html</div>
</template>
EOT;
}
}
Admin/Extensions/Column/OpenMap.php
<?php
namespace App\Admin\Extensions\Column;
use Encore\Admin\Admin;
use Encore\Admin\Grid\Displayers\AbstractDisplayer;
class OpenMap extends AbstractDisplayer
{
public function display(\Closure $callback = null, $btn = '')
{
$callback = $callback->bindTo($this->row);
list($latitude, $longitude) = call_user_func($callback);
$key = $this->getKey();
$name = $this->column->getName();
Admin::script($this->script());
return <<<EOT
<button class="btn btn-xs btn-default grid-open-map" data-key="{$key}" data-lat="$latitude" data-lng="$longitude" data-toggle="modal" data-target="#grid-modal-{$name}-{$key}">
<i class="fa fa-map-marker"></i> $btn
</button>
<div class="modal" id="grid-modal-{$name}-{$key}" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span></button>
<h4 class="modal-title">$btn</h4>
</div>
<div class="modal-body">
<div id="grid-map-$key" style="height:450px;"></div>
</div>
</div>
<!-- /.modal-content -->
</div>
<!-- /.modal-dialog -->
</div>
EOT;
}
protected function script()
{
return <<<EOT
$('.grid-open-map').on('click', function() {
var key = $(this).data('key');
var lat = $(this).data('lat');
var lng = $(this).data('lng');
var center = new qq.maps.LatLng(lat, lng);
var container = document.getElementById("grid-map-"+key);
var map = new qq.maps.Map(container, {
center: center,
zoom: 13
});
var marker = new qq.maps.Marker({
position: center,
draggable: true,
map: map
});
});
EOT;
}
}
Admin/Extensions/Column/FloatBar.php
<?php
namespace App\Admin\Extensions\Column;
use Encore\Admin\Admin;
use Encore\Admin\Grid\Displayers\AbstractDisplayer;
class FloatBar extends AbstractDisplayer
{
protected function script()
{
return <<<EOT
$('.grid-float-bar').closest('tr').mouseover(function () {
$(this).find('.grid-float-bar').removeClass('hide');
});
$('.grid-float-bar').closest('tr').mouseout(function () {
$(this).find('.grid-float-bar').addClass('hide');
});
EOT;
}
public function display()
{
Admin::script($this->script());
return <<<EOT
<div style="width:220px;">
<div class="hide grid-float-bar">
<a class="btn btn-xs btn-default"><i class="fa fa-thumbs-up"></i> Up</a>
<a class="btn btn-xs btn-default"><i class="fa fa-thumbs-down"></i> Down</a>
<a class="btn btn-xs btn-default"><i class="fa fa-heart"></i> Like</a>
<a class="btn btn-xs btn-default"><i class="fa fa-share"></i> Share</a>
</div>
</div>
EOT;
}
}
Admin/Extensions/Column/UrlWrapper.php
<?php
namespace App\Admin\Extensions\Column;
use Encore\Admin\Facades\Admin;
use Encore\Admin\Grid\Displayers\AbstractDisplayer;
class UrlWrapper extends AbstractDisplayer
{
protected function script()
{
return <<<EOT
$('.grid-qrcode').popover({
title: "Scan code to visit",
html: true,
trigger: 'focus'
});
new Clipboard('.clipboard');
$('.clipboard').tooltip({
trigger: 'click',
placement: 'bottom'
}).mouseout(function (e) {
$(this).tooltip('hide');
});
EOT;
}
public function display()
{
Admin::script($this->script());
$qrcode = "<img src='https://api.qrserver.com/v1/create-qr-code/?size=150x150&data={$this->value}' style='height: 150px;width: 150px;'/>";
return <<<EOT
<div class="input-group" style="width:250px;">
<input type="text" id="grid-homepage-{$this->getKey()}" class="form-control input-sm" value="{$this->value}" />
<span class="input-group-btn">
<button class="btn btn-default btn-sm clipboard" data-clipboard-target="#grid-homepage-{$this->getKey()}" title="Copied!">
<i class="fa fa-clipboard"></i>
</button>
<a class="btn btn-default btn-sm grid-qrcode" data-content="$qrcode" data-toggle='popover' tabindex='0'>
<i class="fa fa-qrcode"></i>
</a>
</span>
</div>
EOT;
}
}
app/Admin/Extensions/Tools/UserGender.php
<?php
namespace App\Admin\Extensions\Tools;
use Encore\Admin\Admin;
use Encore\Admin\Grid\Tools\AbstractTool;
use Illuminate\Support\Facades\Request;
class UserGender extends AbstractTool
{
public function script()
{
$url = Request::fullUrlWithQuery(['gender' => '_gender_']);
return <<<EOT
$('input:radio.user-gender').change(function () {
var url = "$url".replace('_gender_', $(this).val());
$.pjax({container:'#pjax-container', url: url });
});
EOT;
}
public function render()
{
Admin::script($this->script());
$options = [
'all' => 'All',
'm' => 'Male',
'f' => 'Female',
];
return view('admin.tools.gender', compact('options'));
}
}
resources/views/admin/tools/gender.blade.php
<div class="btn-group" data-toggle="buttons">
@foreach($options as $option => $label)
<label class="btn btn-default btn-sm {{ \Request::get('gender', 'all') == $option ? 'active' : '' }}">
<input type="radio" class="user-gender" value="{{ $option }}">{{$label}}
</label>
@endforeach
</div>
-END-