添加广告管理

mall
vine_liutk 2023-12-21 16:19:31 +08:00
parent 32a95b96d7
commit 64ff120a9e
15 changed files with 385 additions and 17 deletions

View File

@ -78,10 +78,14 @@ class Components extends BaseRenderer {
* 图片上传,带裁剪
*/
public function cropImageControl($name, $label, $aspectRatio = null){
return amis()->ImageControl($name, $label)->joinValues(false)
->crop([
'aspectRatio' => $aspectRatio ?? 1
])->autoUpload(true);
$cropImage = amis()->ImageControl($name, $label)->joinValues(false);
// if($aspectRatio){
$cropImage->crop([
'aspectRatio' => $aspectRatio ?? 1
]);
// }
$cropImage->autoUpload(true);
return $cropImage;
}
/**
@ -90,7 +94,6 @@ class Components extends BaseRenderer {
public function fileControl($name, $label, $accept = '.txt', $multiple = false){
return amis()->FileControl($name, $label ?? __('admin.components.files'))
->multiple($multiple)
// ->receiver()
->joinValues(false)
->useChunk(false)
->maxSize(20*1024*1024)

View File

@ -0,0 +1,79 @@
<?php
namespace App\Admin\Controllers;
use Slowlyo\OwlAdmin\Admin;
use Slowlyo\OwlAdmin\Renderers\Page;
use Slowlyo\OwlAdmin\Renderers\Form;
use Slowlyo\OwlAdmin\Controllers\AdminController;
use Slowlyo\OwlAdmin\Renderers\Operation;
use App\Services\Admin\AdService;
use App\Admin\Components;
use App\Models\Keyword;
use App\Models\Ad;
/**
* 广告管理
*
* @property AdService $service
*/
class AdController extends AdminController
{
protected string $serviceName = AdService::class;
public function list(): Page
{
$crud = $this->baseCRUD()->tableLayout('fixed')
->headerToolbar([
$this->createButton(),
...$this->baseHeaderToolBar(),
])
->filter($this->baseFilter()->body())
->columns([
amis()->TableColumn('id', __('admin.ads.id'))->width('50px')->sortable(),
amis()->TableColumn('address', __('admin.ads.address'))->type('mapping')
->map(Keyword::allChildrenOfKey('banner_address')->pluck('name', 'key')->toArray())
->itemSchema(amis()->Tag()->label('${item}')->color(Admin::setting()->get('system_theme_setting')['theme_color'] ?? '#1677ff')),
amis()->TableColumn('sort', __('admin.ads.sort')),
amis()->TableColumn('resource', __('admin.ads.resource'))->type('image')->height('50px')->width('150px')->enlargeAble(true),
amis()->TableColumn('remark', __('admin.ads.remark')),
amis()->TableColumn('published_at', __('admin.ads.published_at'))->remark(__('admin.ads.published_at_remark')),
amis()->TableColumn('is_enable', __('admin.ads.is_enable'))->type('switch'),
amis()->TableColumn('created_at', __('admin.created_at'))->type('datetime')->sortable(true),
Operation::make()->label(__('admin.actions'))->buttons([
$this->rowEditButton(),
$this->rowDeleteButton(),
])
]);
return $this->baseList($crud);
}
public function form($isEdit = false): Form
{
return $this->baseForm()->body([
Components::make()->parentControl(admin_url('api/keywords/tree-list?parent_name=banner_address&has_owner=0'), 'address', __('admin.ads.address'), 'name', 'key')->required(true),
Components::make()->imageControl('resource', __('admin.ads.resource'))->required(true),
amis()->TextControl('remark', __('admin.ads.remark')),
Components::make()->sortControl('sort', __('admin.ads.sort')),
amis()->DateTimeControl('published_at', __('admin.ads.published_at'))->format('YYYY-MM-DD HH:mm:ss')->description(__('admin.ads.published_at_remark')),
amis()->SwitchControl('is_enable', __('admin.ads.is_enable'))->value(false),
amis()->RadiosControl('jump_type', __('admin.ads.jump_type'))->selectFirst(true)->options(Ad::jumpTypeMap())->required(true),
amis()->TextControl('jump_config.web_link', __('admin.ads.jump_config_arr.web_link'))->visibleOn('this.jump_type == '.Ad::TYPE_WEB)->required(true),
amis()->TextControl('jump_config.app_link', __('admin.ads.jump_config_arr.app_link'))->visibleOn('this.jump_type == '.Ad::TYPE_APP)->required(true),
amis()->GroupControl()->body([
amis()->TextControl('jump_config.mini_id', __('admin.ads.jump_config_arr.mini_id'))->visibleOn('this.jump_type == '.Ad::TYPE_MINI)->required(true),
amis()->TextControl('jump_config.mini_link', __('admin.ads.jump_config_arr.mini_link'))->visibleOn('this.jump_type == '.Ad::TYPE_MINI)->required(true),
]),
]);
}
public function detail(): Form
{
return $this->baseDetail()->body([
amis()->TextControl('id', 'ID')->static(),
amis()->TextControl('created_at', __('admin.created_at'))->static(),
amis()->TextControl('updated_at', __('admin.updated_at'))->static()
]);
}
}

View File

@ -23,7 +23,7 @@ class ArticleController extends AdminController
...$this->baseHeaderToolBar(),
])
->filter($this->baseFilter()->body())
->itemBadge([
->itemBadge([//行角标
'text' => __('admin.articles.is_recommend'),
'mode' => 'ribbon',
'position' => 'top-left',
@ -32,7 +32,7 @@ class ArticleController extends AdminController
'size' => 15
])
->columns([
amis()->TableColumn('id', __('admin.id')),
amis()->TableColumn('id', __('admin.id'))->sortable(true),
amis()->TableColumn('title', __('admin.articles.title'))->width('300px'),
amis()->TableColumn('category', __('admin.articles.category'))->type('mapping')
->map(Keyword::allChildrenOfKey('article_category')->pluck('name', 'key')->toArray())
@ -42,7 +42,7 @@ class ArticleController extends AdminController
amis()->TableColumn('published_at', __('admin.articles.published_at'))->remark(__('admin.articles.published_at_remark')),
amis()->TableColumn('is_enable', __('admin.articles.is_enable'))->type('switch'),
amis()->TableColumn('is_recommend', __('admin.articles.is_recommend'))->type('switch'),
amis()->TableColumn('created_at', __('admin.created_at')),
amis()->TableColumn('created_at', __('admin.created_at'))->type('datetime')->sortable(true),
amis()->Operation()->label(__('admin.actions'))->buttons([
$this->rowEditButton(),
$this->rowDeleteButton(),
@ -65,7 +65,7 @@ class ArticleController extends AdminController
amis()->DateTimeControl('published_at', __('admin.articles.published_at'))->format('YYYY-MM-DD HH:mm:ss')->description(__('admin.articles.published_at_remark')),
amis()->SwitchControl('is_enable', __('admin.articles.is_enable'))->value(false),
amis()->SwitchControl('is_recommend', __('admin.articles.is_recommend'))->value(false),
Components::make()->fileControl('appendixes', __('admin.articles.appendixes'), '.xsl,.xlsx,.txt,.doc,.docx,.pdf,.pptx'),
Components::make()->fileControl('appendixes', __('admin.articles.appendixes'), '.xsl,.xlsx,.txt,.doc,.docx,.pdf,.pptx', true),
])->md(4),
amis()->Wrapper()->body([
Components::make()->fuEditorControl('content', __('admin.articles.content')),

View File

@ -30,6 +30,8 @@ Route::group([
$router->resource('articles', \App\Admin\Controllers\ArticleController::class);
$router->resource('ads', \App\Admin\Controllers\AdController::class);
//修改上传
$router->post('upload_file', [\App\Admin\Controllers\IndexController::class, 'uploadFile']);
$router->post('upload_image', [\App\Admin\Controllers\IndexController::class, 'uploadImage']);

86
app/Models/Ad.php 100644
View File

@ -0,0 +1,86 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use EloquentFilter\Filterable;
use App\Casts\Storage;
class Ad extends Model
{
use HasFactory;
use Filterable;
public const TYPE_OFF = 0; //无跳转
public const TYPE_WEB = 1; //网页跳转
public const TYPE_APP = 2; //应用跳转
public const TYPE_MINI = 3;//小程序跳转
protected function serializeDate(\DateTimeInterface $date)
{
return $date->format('Y-m-d H:i:s');
}
protected $casts = [
'created_at' => 'datetime',
'updated_at' => 'datetime',
'published_at' => 'datetime',
'is_enable' => 'boolean',
'resource' => Storage::class,
'jump_config' => 'array',
];
protected $fillable = [
'resource',
'address',
'remark',
'published_at',
'is_enable',
'sort',
'jump_type',
'jump_config',
];
public static function jumpTypeMap() :array
{
return [
self::TYPE_WEB => '网页跳转',
self::TYPE_APP => '应用跳转',
self::TYPE_MINI => '小程序跳转',
self::TYPE_OFF => '无跳转',
];
}
public static function jumpTypeMapLabel()
{
return [
self::TYPE_OFF => "<span class='label'>无跳转</span>",
self::TYPE_WEB => "<span class='label label-info'>网页跳转</span>",
self::TYPE_APP => "<span class='label label-warning'>应用跳转</span>",
self::TYPE_MINI => "<span class='label label-green'>小程序跳转</span>",
'*'=>'其他:${jump_type}'
];
}
public static function typeMapLabel()
{
return [
self::TYPE_IN => "<span class='label label-info'>入住缴费</span>",
self::TYPE_CONTINUE => "<span class='label label-warning'>续住缴费</span>",
self::TYPE_EXIT => "<span class='label label-danger'>离开结算</span>",
'*'=>'其他:${live_in}'
];
}
public function scopeShow(){
$q->where('is_enable', true)->where('published_at', '>=', now());
}
public function scopeSort($q)
{
$q->orderBy('sort', 'asc')
->orderBy('published_at', 'desc')
->orderBy('created_at', 'desc');
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace App\Models\Filters;
use EloquentFilter\ModelFilter;
class AdFilter extends ModelFilter
{
/**
* 标题
*/
public function name($name)
{
return $this->where('name','like', '%'.$name.'%');
}
}

View File

@ -0,0 +1,112 @@
<?php
namespace App\Services\Admin;
use App\Models\Ad;
use App\Models\Filters\AdFilter;
use App\Traits\UploadTrait;
use Illuminate\Support\Arr;
/**
* @method Ad getModel()
* @method Ad|\Illuminate\Database\Query\Builder query()
*/
class AdService extends BaseService
{
use UploadTrait;
protected string $modelName = Ad::class;
protected string $modelFilterName = AdFilter::class;
protected bool $modelSortAble = true;
public function store($data): bool
{
$columns = $this->getTableColumns();
$model = $this->getModel();
$isEnable = Arr::get($data, 'is_enabled');
$publishedAt = Arr::get($data, 'published_at');
if ($isEnable && empty($publishedAt)) {
$data['published_at'] = now();
}
$data['resource'] = $this->saveImage('resource', 'ads/resource')[0];
//处理跳转配置
$jumpType = Arr::get($data, 'jump_type');
if($jumpType !== null){
switch($jumpType){
case Ad::TYPE_OFF:
$data['jump_config'] = null;
break;
case Ad::TYPE_WEB:
$data['jump_config'] = Arr::only($data['jump_config'], 'web_link');
break;
case Ad::TYPE_APP:
$data['jump_config'] = Arr::only($data['jump_config'], 'app_link');;
break;
case Ad::TYPE_MINI:
$data['jump_config'] = Arr::only($data['jump_config'], ['mini_id', 'mini_link']);;
break;
}
}
foreach ($data as $k => $v) {
if (!in_array($k, $columns)) {
continue;
}
$model->setAttribute($k, $v);
}
return $model->save();
}
public function update($primaryKey, $data): bool
{
$columns = $this->getTableColumns();
$model = $this->query()->whereKey($primaryKey)->first();
$isEnable = Arr::get($data, 'is_enable');
$publishedAt = Arr::get($data, 'published_at');
if ($isEnable && empty($publishedAt) && empty($model->published_at)) {
$data['published_at'] = now();
}
if(isset($data['resource'])){
$data['resource'] = $this->saveImage('resource', 'ads/resource')[0];
}
//处理跳转配置
$jumpType = Arr::get($data, 'jump_type');
if($jumpType !== null){
switch($jumpType){
case Ad::TYPE_OFF:
$data['jump_config'] = null;
break;
case Ad::TYPE_WEB:
$data['jump_config'] = Arr::only($data['jump_config'], 'web_link');
break;
case Ad::TYPE_APP:
$data['jump_config'] = Arr::only($data['jump_config'], 'app_link');;
break;
case Ad::TYPE_MINI:
$data['jump_config'] = Arr::only($data['jump_config'], ['mini_id', 'mini_link']);;
break;
}
}
foreach ($data as $k => $v) {
if (!in_array($k, $columns)) {
continue;
}
$model->setAttribute($k, $v);
}
return $model->save();
}
}

View File

@ -128,6 +128,8 @@ class AdminUserService extends BaseService
$query->where('username', 'like', "%{$keyword}%")->orWhere('name', 'like', "%{$keyword}%");
});
$this->sortable($query);
$items = (clone $query)->paginate(request()->input('perPage', 20))->items();
$total = (clone $query)->count();

View File

@ -19,6 +19,8 @@ class ArticleService extends BaseService
protected string $modelFilterName = ArticleFilter::class;
protected bool $modelSortAble = true;
public function store($data): bool
{
$columns = $this->getTableColumns();
@ -55,9 +57,13 @@ class ArticleService extends BaseService
if ($isEnable && empty($publishedAt) && empty($model->published_at)) {
$data['published_at'] = now();
}
if(isset($data['cover'])){
$data['cover'] = $this->saveImage('cover', 'articles/cover')[0];
}
$data['cover'] = $this->saveImage('cover', 'articles/cover')[0];
$data['appendixes'] = $this->saveFile('appendixes', 'articles/appendixes');
if(isset($data['appendixes'])){
$data['appendixes'] = $this->saveFile('appendixes', 'articles/appendixes');
}
foreach ($data as $k => $v) {
if (!in_array($k, $columns)) {

View File

@ -14,6 +14,8 @@ class BaseService extends AdminService
protected string $modelFilterName = '';
protected bool $modelSortAble = false;
public function getTree()
{
$list = $this->query()->orderByDesc('sort')->get();
@ -40,7 +42,13 @@ class BaseService extends AdminService
$query->filter(request()->input(), $filter);
}
return $query->orderByDesc($model->getUpdatedAtColumn() ?? $model->getKeyName());
if($this->modelSortAble){
$query->sort();
}
$this->sortable($query);
return $query;
}
public function getDetail($id)

View File

@ -19,7 +19,7 @@ class KeywordService extends BaseService
{
$list = $this->query()->filter(request()->all(), $this->modelFilterName)->orderByDesc('sort')->get();
$minNum = $list->min('parent_id');
return array2tree($list->toArray(), $minNum);
return !$list->isEmpty() ? array2tree($list->toArray(), $minNum) :[];
}
public function parentIsChild($id, $pid): bool

View File

@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('ads', function (Blueprint $table) {
$table->id();
$table->string('address')->comment('位置');
$table->string('resource')->comment('资源');
$table->string('remark')->nullable()->comment('备注');
$table->timestamp('published_at')->nullable()->comment('发布时间');
$table->unsignedTinyInteger('is_enable')->default(1)->comment('显示开关');
$table->unsignedInteger('sort')->default(0)->comment('排序');
$table->unsignedTinyInteger('jump_type')->default(0)->comment('跳转0不跳转。1网页跳转2应用跳转3微信小程序跳转');
$table->text('jump_config')->nullable()->comment('跳转配置');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('ads');
}
};

View File

@ -33,7 +33,7 @@ class AdminMenuSeeder extends Seeder
['title' => 'web_content', 'icon' => 'ic:outline-collections-bookmark', 'url' => '', 'order'=>3,
'children' =>[
['title'=>'articles', 'icon'=>'ic:outline-article','url'=>'/articles', 'order'=>1],
['title'=>'banners', 'icon'=>'lets-icons:img-box','url'=>'/banners', 'order'=>2],
['title'=>'ads', 'icon'=>'lets-icons:img-box','url'=>'/ads', 'order'=>2],
]
]
];

View File

@ -60,7 +60,7 @@ return [
'cancel' => '取消',
'please_login' => '请先登录',
'unauthorized' => '无权访问',
'id' => '主键',
'id' => 'ID',
'components' => [
'content' => '内容',
@ -274,7 +274,7 @@ return [
'parent_keyword' => '父级关键字',
],
'articles' => [
'id' => '主键',
'id' => 'ID',
'title' => '标题',
'content' => '内容',
'cover' =>'封面',
@ -288,5 +288,24 @@ return [
'sort' => '排序',
'appendixes' => '附件',
'published_at_remark' => '*若未设置发布时间且操作设置为显示,则默认生成发布时间'
],
'ads' => [
'id' => 'ID',
'address' => '位置',
'resource' =>'内容',
'published_at' => '定时发布',
'published_at_g' => '发布时间',
'is_enable' => '显示',
'remark' => '备注',
'sort' => '排序',
'published_at_remark' => '*若未设置发布时间且操作设置为显示,则默认生成发布时间',
'jump_type' => '跳转类型',
'jump_config'=>'跳转配置',
'jump_config_arr'=>[
'web_link' => '网页地址',
'app_link' => '应用路径',
'mini_id' => '小程序ID',
'mini_link'=> '小程序路径'
],
]
];

View File

@ -12,5 +12,5 @@ return [
'keywords' => '数据字典',
'web_content' => '内容管理',
'articles' => '文章管理',
'banners' => '广告管理',
'ads' => '广告管理',
];