laravel-admin中文文档
https://laravel-admin.org/docs/zh
laravel文档
https://laravel-china.org/docs/laravel/5.7
https://laravelacademy.org/laravel-docs-5_7
参照这篇文章
https://blog.csdn.net/qq_41249766/article/details/81121596
laravel-admin框架安装
https://laravel-admin.org/docs/zh/installation
创建模型实例最简单的办法就是使用 Artisan 命令 make:model:
php artisan make:model User
如果你想要在生成模型时生成数据库迁移,可以使用 --migration 或 -m 选项:
php artisan make:model User --migration
php artisan make:model User -m
例:在laravel-admin中创建一个文章model和一个文章类型的model
在app/Admin下新建一个Models的文件夹
php artisan make:model Admin/Models/Article
php artisan make:model Admin/Models/ArticleType
注意我们并没有告诉 Eloquent 我们的 Flight 模型使用哪张表,默认规则是小写的模型类名复数格式作为与其对应的表名(除非在模型类中明确指定了其它名称)。
namespace App\Admin\Models;
use Illuminate\Database\Eloquent\Model;
class ArticleType extends Model
{
// * 关联到模型的数据表
// *
// * @var string
// */
protected $table = 'article_type';
}
使用下面的命令来创建一个对应App\Admin\Models\ArticleType模型的路由器
//php artisan admin:make UserController --model=App\\User
// 在windows系统中
php artisan admin:make ArticleTypeController --model=App\Admin\Models\ArticleType
在laravel-admin的路由配置文件app/Admin/routes.php里添加一行:
$router->resource('ArticleType', ArticleTypeController::class);
打开http://localhost:8000/admin/auth/menu,添加对应的menu
再具体的修改到生成的Controller进行修改
要使用model-tree,要遵守约定的表结构:
表结构里面有三个必要的字段parent_id、order、title,其它字段没有要求。
namespace App\Admin\Models;
use Encore\Admin\Traits\AdminBuilder;
use Encore\Admin\Traits\ModelTree;
use Illuminate\Database\Eloquent\Model;
class ArticleType extends Model
{
use ModelTree, AdminBuilder;
// * 关联到模型的数据表
// *
// * @var string
// */
protected $table = 'article_type';
//表结构中的三个字段parent_id、order、title的字段名也是可以修改的:
//public function __construct(array $attributes = [])
//{
// parent::__construct($attributes);
// $this->setParentColumn('pid');
// $this->setOrderColumn('sort');
// $this->setTitleColumn('name');
// }
}
页面中使用model-tree,在controller修改index
public function index()
{
return Admin::content(function (Content $content) {
$content->header('树状模型');
$content->body(Category::tree());
});
}
修改行数据的显示
ArticleType::tree(function ($tree) {
$tree->branch(function ($branch) {
$src = config('admin.upload.host') . '/' . $branch['logo'] ;
$logo = "";
return "{$branch['id']} - {$branch['title']} $logo";
});
})
修改模型的查询
ArticleType::tree(function ($tree) {
$tree->query(function ($model) {
return $model->where('type', 1);
});
})
laravel-admin 报错 Disk [admin] not configured, please add a disk config in config/filesystems.php.
在config/filesystems.php中添加:
'disks' => [
'local' => [
'driver' => 'local',
'root' => storage_path('app'),
],
'public' => [
'driver' => 'local',
'root' => storage_path('app/public'),
'url' => env('APP_URL').'/storage',
'visibility' => 'public',
],
's3' => [
'driver' => 's3',
'key' => env('AWS_KEY'),
'secret' => env('AWS_SECRET'),
'region' => env('AWS_REGION'),
'bucket' => env('AWS_BUCKET'),
],
//添加如下代码
'admin' => [
'driver' => 'local',
'root' => public_path('upload'),
'visibility' => 'public',
'url' => env('APP_URL').'/upload/',
],
],
用默认的导出按钮,导出的中文有乱码
在vendor\encore\laravel-admin\src\Grid\Exporters\CsvExporter.php中
$headers = [
'Content-Encoding' => 'UTF-8',
'Content-Type' => 'text/csv;charset=UTF-8',
'Content-Disposition' => "attachment; filename=\"$filename\"",
];
//添加的代码
print(chr(0xEF).chr(0xBB).chr(0xBF));
安装完成之后,会生成两个配置文件,用来对管理后台进行配置,config/admin.php和app/Admin/bootstrap.php
https://laravel-admin.org/docs/zh/configuration
composer require mews/captcha
修改config/app.php
‘providers’ => [
// …
Mews\Captcha\CaptchaServiceProvider::class,
]
‘aliases’ => [
// …
‘Captcha’ => Mews\Captcha\Facades\Captcha::class,
]
发布
php artisan vendor:publish
修改config/captcha.php的default
return [
'default' => [
'length' => 5,
'width' => 120,
'height' => 36,
'quality' => 90,
],
// ...
];
修改登录方法
复制vendor/encore/laravel-admin/src/Controllers/AuthController.php到app/Admin/Controllers/AuthController.php,修改代码如下:
namespace App\Admin\Controllers;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Lang;
use Illuminate\Support\Facades\Redirect;
use Illuminate\Support\Facades\Validator;
class AuthController extends Controller
{
/**
* Login page.
*
* @return \Illuminate\Contracts\View\Factory|Redirect|\Illuminate\View\View
*/
public function getLogin()
{
if (!Auth::guard('admin')->guest()) {
return redirect(config('admin.route.prefix'));
}
return view('admin.login');
}
/**
* @param Request $request
*
* @return mixed
*/
public function postLogin(Request $request)
{
$credentials = $request->only(['username', 'password','captcha']);
$validator = Validator::make($credentials, [
'username' => 'required',
'password' => 'required',
'captcha' => 'required|captcha'
]);
if ($validator->fails()) {
return Redirect::back()->withInput()->withErrors($validator);
}
unset($credentials['captcha']);
if (Auth::guard('admin')->attempt($credentials)) {
admin_toastr(trans('admin.login_successful'));
return redirect()->intended(config('admin.route.prefix'));
}
return Redirect::back()->withInput()->withErrors(['username' => $this->getFailedLoginMessage()]);
}
/**
* @return string|\Symfony\Component\Translation\TranslatorInterface
*/
protected function getFailedLoginMessage()
{
return Lang::has('auth.failed')
? trans('auth.failed')
: 'These credentials do not match our records.';
}
}
添加验证错误(根据自己的lang修改); 在resources/lang/zh-CN/validation.php中添加。
'captcha' => ':attribute 错误',
'attributes' => [
'captcha' => '验证码',
//...
]
复制vendor/encore/laravel-admin/resources/views/login.blade.php到resources/views/admin/login.blade.php。
新增
<div class="form-group has-feedback {!! !$errors->has('password') ?: 'has-error' !!}">
@if($errors->has('password'))
@foreach($errors->get('password') as $message)
<label class="control-label" for="inputError"><i class="fa fa-times-circle-o"></i>{{$message}}</label></br>
@endforeach
@endif
<input type="password" class="form-control" placeholder="{{ trans('admin::lang.password') }}" name="password" value="{{ old('username') }}">
<span class="glyphicon glyphicon-lock form-control-feedback"></span>
</div>
<div class="row">
<div class="form-group has-feedback {!! !$errors->has('captcha') ?: 'has-error' !!}">
@if($errors->has('captcha'))
@foreach($errors->get('captcha') as $message)
<label class="control-label" for="inputError" style=" margin-left: 15px"><i class="fa fa-times-circle-o"></i>{{$message}}</label></br>
@endforeach
@endif
<input type="text" class="form-control" style="display: inline;width: 55%; margin-left: 15px" placeholder="{{ trans('admin.captcha') }}" name="captcha">
<span class="glyphicon glyphicon-refresh form-control-feedback captcha" style="right:39%;z-index: 100"></span>
<img class="captcha" src="{{ captcha_src('admin') }}" onclick="refresh()">
</div>
<script>
function refresh(){
$('img[class="captcha"]').attr('src','{{ captcha_src('admin') }}'+Math.random());
}
</script>
app/Admin/routes.php中新增,覆盖原来的路由
$router->get('auth/login', 'AuthController@getLogin');
$router->post('auth/login', 'AuthController@postLogin');
在config/app.php设置locale,默认为en
‘locale’ => ‘zh-CN’,//en
在设置完语言以后,打开
resources/lang/zh-CN/ 发现只有一个admin文件夹将en文件夹中的其它四个文件复制进去,不然错误提示会提示查找不到文件
在model里添加关联表
例:ArticleType通过userid关联管理员表
/*** 关联管理员表 */
public function admin_user()
{
return $this->belongsTo(AdminUser::class,'userid');
}
在controller里的grid里面获取关联表的数据
$grid->admin_user()->username('更新管理员');
$grid->filter(function($filter){
// 去掉默认的id过滤器
$filter->disableIdFilter();
// 在这里添加字段过滤器
$filter->like('name', 'name');
// 多条件查询
$filter->scope('new', '最近修改')
->whereDate('created_at', date('Y-m-d'))
->orWhere('updated_at', date('Y-m-d'));
// 关联关系查询
$filter->scope('address')->whereHas('profile', function ($query) {
$query->whereNotNull('address');});
//查询软删除数据
$filter->scope('trashed', '被软删除的数据')->onlyTrashed();
//radio查询
$filter->equal('display','展示状态')->radio([
'' => '全部',
1 => '显示',
0 => '隐藏',
]);
});
详情
https://laravel-admin.org/docs/zh/model-grid-filters
use Encore\Admin\Facades\Admin;
//在方法里调用,获取当前管理员的id
Admin::user()->id
//如果form提交时数据库需要存入当前管理员的id
$form->hidden('userid')->default(Admin::user()->id);
model-grid默认有两个行操作编辑和删除,可以通过下面的方式关闭它们:
$grid->actions(function ($actions) {
$actions->disableDelete();
$actions->disableEdit();
$actions->disableView();
});
可以通过传入的$actions参数来获取当前行的数据:
$grid->actions(function ($actions) {
// 当前行的数据数组
$actions->row;
// 获取当前行主键值
$actions->getKey();
});
如果有自定义的操作按钮,可以通过下面的方式添加:
$grid->actions(function ($actions) {
// append一个操作
$actions->append('');
// prepend一个操作
$actions->prepend('');
});
如果有比较复杂的操作
$grid->actions(function ($actions) {
$actions->disableDelete();
$actions->append(new AuditingInteract($actions->getKey(),$actions->row));
});
/**
* Created by PhpStorm.
* User: Administrator
* Date: 2018/12/12
* Time: 18:22
*/
namespace App\Admin\Extensions;
use Encore\Admin\Admin;
class AuditingInteract
{
protected $id;
protected $row;
public function __construct($id,$row)
{ $this->id = $id;
$this->row=$row;
$this->check=$row->check;
}
protected function script()
{ return <<<SCRIPT
$('.AuditingInteact{$this->id}').on('click', function () {
$.ajax({
method: 'get',
url: '/admin/AuditingInteact',
data: {
id:{$this->id},
check:{$this->check}
},
success: function () {
location.href=location.href
toastr.success('操作成功');
}
});
});
SCRIPT;
}
protected function render()
{
Admin::script($this->script());
return "审";
}
public function __toString()
{
return $this->render();
}
}
$router->get(‘AuditingInteract’, ‘TpInvestInteractController@AuditingInteract’);
use Illuminate\Http\Request;
/**审核*/
public function AuditingInteract(Request $request)
{
$id=$request->id;
$check=$request->check;
if($check==0){
$flight = TpInvestInteract::find($id);
$flight->check = 1;
$flight->save();
}
if($check==1){
$flight = TpInvestInteract::find($id);
$flight->check = 0;
$flight->save();
}
}
在对应的model中添加,然后再生成controller
/**
* 模型日期列的存储格式
*
* @var string
*/
protected $dateFormat = 'U';
将时间戳格式在grid中显示为正常时间格式
$grid->created_at('创建时间')->display(function ($time){
if(empty($time)){
return '——';
}
else{
return date('Y-m-d h:i:s',$time);
}
});
$grid->updated_at('更新时间')->display(function ($time){
if(empty($time)){
return '——';
}
else{
return date('Y-m-d h:i:s',$time);
}
});
https://laravelacademy.org/post/8194.html#
软删除需要数据库表中必须有deleted_at字段,然后在Model中添加SoftDeletes
namespace App\Admin\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class GrFriendlyLink extends Model
{
use SoftDeletes;
/**
* 关联到模型的数据表
*
* @var string
*/
protected $table = 'gr_friendly_link';
}
protected function restoredelete(Request $request)
{
$id=$request->id;
GrNewsInformation::withTrashed()
->where('id', $id)
->restore();
}
在controller的grid中
$grid->model()->onlyTrashed()->orderby('created_at','desc');
在model中
public function delete(){
$this->status =0;
$this->save();
}
//对查询数据进行处理
$grid->model()->where('status','!=',0)->orderby('created_at','desc');
//结合having 查出名字重复的
$grid->model()->select('name_en')->groupBy('name_en')->havingRaw('count(name_en)>1')
//嵌套子查询 查出名字重复的全部信息
$grid->model()->select('id','name_en')->whereIn('name_en',Project::select('name_en')->groupBy('name_en')->havingRaw('count(name_en)>1'))->orderBy('name_en');
//显示图片
$grid->photo('图片')->image();
// 第一列显示id字段,并将这一列设置为可排序列,并可以在页面进行修改
$grid->id('ID')->sortable()->editable('text');
//展示状态,通过判断展示不同的图标
$grid->column('display', '展示状态')->display(function ($status) {
return $status==1 ? '' : '';
});
// 第二列显示title字段,由于title字段名和Grid对象的title方法冲突,所以用Grid的column()方法代替
$grid->column('title');
//关联管理员表
$grid->admin_user()->username('更新管理员');
//修改显示输出
$grid->text()->display(function($text) {
return str_limit($text, 30, '...');
});
$grid->name()->display(function ($name) {
return "$name";
});
// 添加不存在的字段
$grid->column('column_not_in_table')->display(function () {
return 'blablabla....';
});
//显示director字段,通过display($callback)方法设置这一列的显示内容为users表中对应的用户名
$grid->director()->display(function($userId) {
return User::find($userId)->name;
});
// 默认为每页20条
$grid->paginate(15);
$form->text('name', '姓名')->rules('required');
$form->image('photo', '图片')->rules('required');
$form->number('sort', '排序')->default(1);
$form->switch('display', '展示状态')->default(1);
$form->switch('is_top', '置顶状态')->default(1);
$form->editor('introduce', '简介');
$form->hidden('userid')->default(Admin::user()->id);
$show->id('Id');
$show->name('姓名');
$show->photo('图片')->image();
$show->introduce('简介')->unescape()->as(function ($body) {
return "{$body}
";
});
$show->display( '展示状态')->using([1 => '√', 0 => '×']);
$show->is_top( '是否置顶')->using([1 => '置顶',0 => '正常']);
$show->sort('排序');
$show->updated_at('更新时间');
$show->created_at('创建时间');
其它数据参照Laravel-admin文档
https://laravel-admin.org/docs/zh
使用Laravel自带SMTP邮件组件实现发送邮件
实验使用邮箱的163,QQ邮箱
需要注意的是:使用QQ邮箱的话,需要开启POP3和SMTP服务。开启方式如下:
当开启成功会生成密钥,这个东西会在配置中用到
当需要记录发信记录时,你还需要进行相关配置
打开config/mail.php,进行配置
return [
//driver用于配置默认的邮件发送驱动
'driver' => env('MAIL_DRIVER', 'smtp'),
//host是邮箱所在主机,比如我们使用163邮箱,对应值是smtp.163.com,使用QQ邮箱的话,对应值是smtp.qq.com
'host' => env('MAIL_HOST', 'smtp.qq.com'),
//port用于配置邮箱发送服务端口号,比如一般默认值是25,但如果设置SMTP使用SSL加密,该值为465。
'port' => env('MAIL_PORT', 25),
//from配置项包含address和name,前者表示发送邮箱,后者表示发送邮件使用的用户名。
'from' => ['address' => '[email protected]','name' => '董秘'],
//encryption表示加密类型,可以设置为null表示不使用任何加密,也可以设置为tls或ssl。
'encryption' => env('MAIL_ENCRYPTION', 'tls'),
//username表示邮箱账号
'username' => env('MAIL_USERNAME'),
//password表示上述邮箱登录对应登录密码。注意QQ邮箱的话应该开启POP3|SMTP服务时给的密钥。
'password' => env('MAIL_PASSWORD'),
//sendmail是在设置driver为sendmail时使用,用于指定sendmail命令路径。
'sendmail' => '/usr/sbin/sendmail -bs',
'markdown' => [
'theme' => 'default',
'paths' => [
resource_path('views/vendor/mail'),
],
],
'log_channel' => env('MAIL_LOG_CHANNEL'),
];
打开.env文件进行配置
MAIL_DRIVER=smtp
MAIL_HOST=smtp.qq.com
MAIL_PORT=25
MAIL_USERNAME=2502187899@qq.com
MAIL_PASSWORD=qq的密匙
MAIL_ENCRYPTION=null
发送纯文本邮件
$form->text('title', '提问人名称');
$form->email('email', '提问人');
$form->textarea('content', '提问内容');
$form->text('re_title', '回复标题')->rules('required');
$form->textarea('re_content', '回复内容')->rules('required');
// 在表单提交前调用
$form->submitted(function (Form $form) {
});
// 在保存前调用,发送邮件
$form->saving(function (Form $form) {
$this->name = $form->email;
$this->retitle=$form->re_title;
$re_content=$form->re_content;
Mail::raw( $re_content, function ($message) {
$message ->to($this->name)->subject($this->retitle);
});
});
//保存后回调,更改数据库数据
$form->saved(function (Form $form) {
// 返回的一个错误数组,利用此可以判断是否发送成功
if(count(Mail::failures()) < 1){
$id= $form->model()->id;
$flight = TpInvestEmail::find($id);
$flight->re_email = 1;
$flight->re_time = time();
$flight->save();
}
else{
$id= $form->model()->id;
$flight = TpInvestEmail::find($id);
$flight->re_email = 0;
$flight->save();
}
});
其它方式发送
参照下面文章
https://www.jianshu.com/p/8ccb2820df23
https://github.com/laravel-admin-extensions/UEditor
https://github.com/laravel-admin-extensions/large-file-upload
关于config/aetherupload里面的配置信息
return [
"ENABLE_EXAMPLE_PAGE" => true, # 启用示例页面,访问域名/aetherupload,生产环境下请将该选项设置为false
"CHUNK_SIZE" => 1 * 1000 * 1000, # 上传时的分块大小(B),默认为1M,越大传输越快,需要小于web服务器和php.ini中设置的上传限值
"UPLOAD_PATH" => storage_path(). DIRECTORY_SEPARATOR . "app".DIRECTORY_SEPARATOR."public".DIRECTORY_SEPARATOR."uploads".DIRECTORY_SEPARATOR."aetherupload", # 上传目录的本地物理路径
"HEAD_DIR" => "_head", # 指针头文件目录的名称,建议保持默认
"FILE_SUB_DIR" => @date("Ym", time()), #资源文件目录的子目录生成规则,变量或常量均可
"REDIS_KEY" => "aetherupload_file_hashes", #redis中hashes的key名称
"GROUPS" => [ # 分组,可设置多个不同分组,各自拥有独立配置
"file" => [ # 新增分组请尽量使用video、audio等有意义的分组名
"FILE_MAXSIZE" => 0, # 被允许的资源文件大小(MB),0为不限制
"FILE_EXTENSIONS" => "", # 被允许的资源文件扩展名,空为不限制,多个值以逗号分隔
"MIDDLEWARE_PREPROCESS" => [], # 上传预处理时的路由中间件
"MIDDLEWARE_SAVE_CHUNK" => [], # 上传文件分块时的路由中间件
"MIDDLEWARE_DISPLAY" => [], # 文件展示时的路由中间件
"MIDDLEWARE_DOWNLOAD" => [], # 文件下载时的路由中间件
"EVENT_BEFORE_UPLOAD_COMPLETE" => '', # 上传完成前触发的事件(临时文件),Receiver的实例被注入
"EVENT_UPLOAD_COMPLETE" => '', # 上传完成后触发的事件(已存文件),Receiver的实例被注入
],
],
];
https://github.com/laravel-admin-extensions/media-player
在route里面
//集团简介type=1
$router->resource('grGrbriefIntroduction', GrBasicFactsController::class);
//企业文化type=2
$router->resource('grBusinessCulture', GrBasicFactsController::class);
//组织架构type=3
$router->resource('grOrgaStructure', GrBasicFactsController::class);
在controller进行区分
use Illuminate\Support\Facades\Route;
use HasResourceActions;
protected $routeName;
public function __construct()
{
//通过该方法可以查询到输入的route路径
$host = explode('/',Route::getFacadeRoot()->current()->uri);
$this->routeName = $host[1];
}
public function index(Content $content)
{
if($this->routeName=='grGrbriefIntroduction'){
return $content
->header('集团简介')
->description('数据列表')
->body($this->grid());
}
if($this->routeName=='grBusinessCulture'){
return $content
->header('企业文化')
->description('数据列表')
->body($this->grid());
}
if($this->routeName=='grOrgaStructure'){
return $content
->header('组织架构')
->description('数据列表')
->body($this->grid());
}
}
关于全局搜索功能的简单实现
namespace App\Admin\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Storage;
/**全局搜索
* @package App\Admin\Models
*/
class TpSitequery extends Model
{
/**
* 关联到模型的数据表
*
* @var string
*/
protected $table = 'tp_sitequery1';
}
在需要全局搜索的表里的form里面添加
/**
* Make a form builder.
*
* @return Form
*/
protected function form()
{
$form = new Form(new TpProductBooks);
$form->saved(function (Form $form) {
$form->select('typeid','图书类别')->options(TpProductBookType::all()->pluck('name', 'id'))->rules('required');
$form->hidden('userid')->default(Admin::user()->id);
//拼接要跳转的路径
$url = '/pdctbooks/'.$form->model()->typeid.'/detail/'.$form->model()->id;
$this->optSitequery($form,$url);
});
return $form;
}
private function optSitequery($form,$url){
$sitequery = TpSitequery::where('url',$url)->first();
if(empty($sitequery)){
//插入
$tpSitequery= new TpSitequery;
$tpSitequery->title = $form->model()->name;
$tpSitequery->url = $url;
$tpSitequery->display=$form->model()->display;
$tpSitequery->status=$form->model()->status;
$tpSitequery->save();
}else{
//更新
$sq = TpSitequery::find($sitequery->id);
$sq->title = $form->model()->name;
$sq->url = $url;
$sq->display=$form->model()->display;
$sq->status=$form->model()->status;
$sq->save();
}
}
在admin/permission里面通过绑定路由建立新的权限
在admin/roles里面通过分配权限建立新的角色
在admin/users里面通过给管理员分配角色来实现权限管理
在admin/menu里面通过给页面绑定角色来实现管理员能不能看见菜单列表