6
0
Fork 0

门店管理后台

release
panliang 2022-05-16 13:19:42 +08:00
parent 7969745c4c
commit 59c7c241ec
14 changed files with 300 additions and 117 deletions

View File

@ -1,9 +1,9 @@
<?php
namespace App\Admin\Controllers;
namespace App\Admin\Controllers\Store;
use Dcat\Admin\Form;
use App\Models\Store;
use App\Models\Store\Store;
use Dcat\Admin\Http\Controllers\AdminController;
use Dcat\Admin\Layout\Content;
use Dcat\Admin\Models\Administrator;
@ -11,16 +11,16 @@ use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Dcat\Admin\Traits\HasFormResponse;
class StoreAdminController extends Controller
class AdministratorController extends Controller
{
use HasFormResponse;
public function create($store_id)
{
$store = Store::findOrFail($store_id);
$form = Form::make();
$form->tree('admin_user', '选择管理员')
$form->tree('administrator_id', '选择管理员')
->nodes(Administrator::all()->toArray())
->value($store->adminUsers()->pluck('id'))
->value($store->administrators()->select(['admin_users.id', 'admin_users.name', 'admin_users.username'])->pluck('id'))
->setTitleColumn('name');
return (new Content())->title('新增')->body($form);
}
@ -28,11 +28,11 @@ class StoreAdminController extends Controller
public function store($store_id, Request $request)
{
$store = Store::findOrFail($store_id);
if ($request->input('admin_user')) {
$admin_ids = explode(',', $request->input('admin_user'));
$store->adminUsers()->sync($admin_ids);
if ($request->input('administrator_id')) {
$admin_ids = explode(',', $request->input('administrator_id'));
$store->administrators()->sync($admin_ids);
} else {
$store->adminUsers()->detach();
$store->administrators()->detach();
}
return $this->sendResponse($this->response()->success(trans('admin.save_succeeded')));
@ -42,7 +42,7 @@ class StoreAdminController extends Controller
{
$store = Store::findOrFail($store_id);
$admin_ids = explode(',', $id);
$store->adminUsers()->detach($admin_ids);
$store->administrators()->detach($admin_ids);
return $this->sendResponse($this->response()->success(trans('admin.delete_succeeded')));
}
}

View File

@ -1,8 +1,8 @@
<?php
namespace App\Admin\Controllers;
namespace App\Admin\Controllers\Store;
use App\Models\Store;
use App\Models\Store\{Store, StockLog};
use Dcat\Admin\Form;
use Dcat\Admin\Http\Controllers\AdminController;
use Dcat\Admin\Layout\Content;
@ -11,9 +11,20 @@ use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Dcat\Admin\Traits\HasFormResponse;
class StoreProductController extends Controller
class ProductController extends Controller
{
use HasFormResponse;
public function index($store_id, Request $request)
{
$store = Store::findOrFail($store_id);
$query = $store->productSkus();
if ($request->filled('key')) {
$query->where('name', 'like', '%'.$request->input('key').'%');
}
$list = $query->paginate();
return $list;
}
public function create($store_id)
{
$store = Store::findOrFail($store_id);
@ -22,8 +33,8 @@ class StoreProductController extends Controller
->from(\App\Admin\Renderable\ProductSkuSimpleTable::make())
->model(\App\Models\ProductSku::class, 'id', 'name')
->required();
$form->number('amount', '库存')->min(0);
$form->switch('status', '状态')->default(1);
// $form->number('amount', '库存')->min(0);
$form->switch('status', '上架')->default(1);
return (new Content())->title('新增')->body($form);
}
@ -34,10 +45,10 @@ class StoreProductController extends Controller
if ($product_id) {
$product = $store->productSkus()->find($product_id);
if ($product) {
$store->productSkus()->updateExistingPivot($product_id, $request->only(['amount', 'status']));
$store->productSkus()->updateExistingPivot($product_id, $request->only(['status']));
} else {
$store->productSkus()->attach([
$product_id => $request->only(['amount', 'status'])
$product_id => $request->only(['status'])
]);
}
}
@ -49,14 +60,18 @@ class StoreProductController extends Controller
{
$store = Store::findOrFail($store_id);
$product = $store->productSkus()->wherePivot('id', $id)->firstOrFail();
$store->productSkus()->updateExistingPivot($product->id, $request->only(['status', 'amount']));
$store->productSkus()->updateExistingPivot($product->id, $request->only(['status']));
return $this->sendResponse($this->response()->success(trans('admin.update_succeeded')));
}
public function destroy($store_id, $id)
{
$store = Store::findOrFail($store_id);
dd($id);
$product = $store->productSkus()->wherePivot('id', $id)->firstOrFail();
// 删除库存记录
$store->stockLogs()->where('product_sku_id', $product->id)->delete();
// 删除商品关联
$store->productSkus()->wherePivot('id', $id)->detach();
return $this->sendResponse($this->response()->success(trans('admin.delete_succeeded')));
}
}

View File

@ -0,0 +1,69 @@
<?php
namespace App\Admin\Controllers\Store;
use App\Models\Store\Store;
use App\Models\Tag;
use Dcat\Admin\{Form, Admin};
use Dcat\Admin\Http\Controllers\AdminController;
use Dcat\Admin\Layout\Content;
use Dcat\Admin\Models\Administrator;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Dcat\Admin\Traits\HasFormResponse;
use Illuminate\Validation\Rule;
class StockController extends Controller
{
use HasFormResponse;
public function create($store_id)
{
$store = Store::findOrFail($store_id);
$form = Form::make();
$form->select('tag', '类目')->options(Tag::where('type', Tag::TYPE_STORE_STOCK)->pluck('name', 'id'));
$form->text('tag_name', '自定义类目');
$form->select('product', '商品')->options($store->productSkus()->select(['store_product_skus.id', 'product_skus.name'])->pluck('name', 'id'))->required();
$form->number('amount', '库存')->help('正数为增加, 负数为减少')->default(0)->required();
$form->text('remarks', '备注');
return (new Content())->title('新增')->body($form);
}
public function store($store_id, Request $request)
{
$request->validate([
'amount' => ['required', Rule::notIn([0])],
'product' => 'required',
'tag' => 'required_without:tag_name'
], [
'amount.not_in' => '库存不能为0',
'tag.required_without' => '类目必填',
]);
if ($request->filled('tag')) {
$tag = Tag::findOrFail($request->input('tag'));
} else {
$tag = Tag::firstOrCreate([
'type' => Tag::TYPE_STORE_STOCK,
'name' => $request->input('tag_name')
]);
}
$store = Store::findOrFail($store_id);
$product = $store->productSkus()->wherePivot('id', $request->input('product'))->firstOrFail();
$administrator = Admin::user();
$amount = $request->input('amount');
if ($product->pivot->amount + $amount < 0) {
return $this->sendResponse($this->response()->error('扣减库存不能超过现有库存'));
}
$store->productSkus()->updateExistingPivot($product->id, [
'amount' => $product->pivot->amount + $amount
]);
$store->stockLogs()->create([
'administrator_id' => $administrator->id,
'amount' => $request->input('amount'),
'product_sku_id' => $product->id,
'remarks' => $request->input('remarks'),
'tag_id' => $tag->id
]);
return $this->sendResponse($this->response()->success(trans('admin.save_succeeded')));
}
}

View File

@ -1,8 +1,8 @@
<?php
namespace App\Admin\Controllers;
namespace App\Admin\Controllers\Store;
use App\Models\{Store, StoreProductSku, ProductSku};
use App\Models\Store\{Store, ProductSku, Administrator as StoreAdministrator, StockLog};
use Dcat\Admin\Form;
use Dcat\Admin\Grid;
use Dcat\Admin\Show;
@ -27,7 +27,7 @@ class StoreController extends AdminController
$user = Admin::user();
$canAdmin = $this->canAdmin();
if (!$canAdmin) {
$ids = DB::table('store_admin_users')->where('admin_user_id', $user->id)->pluck('store_id');
$ids = DB::table('store_admin_users')->where('administrator_id', $user->id)->pluck('store_id');
$query = $query->whereIn('id', $ids);
}
@ -43,6 +43,7 @@ class StoreController extends AdminController
$grid->column('created_at');
$grid->disableCreateButton(!$canAdmin);
$grid->enableDialogCreate();
$grid->showEditButton();
$grid->showViewButton();
$grid->showDeleteButton($canAdmin);
@ -67,7 +68,7 @@ class StoreController extends AdminController
$user = Admin::user();
$canAdmin = $this->canAdmin();
if (!$canAdmin) {
$ids = DB::table('store_admin_users')->where('admin_user_id', $user->id)->pluck('store_id');
$ids = DB::table('store_admin_users')->where('administrator_id', $user->id)->pluck('store_id');
$query = $query->whereIn('id', $ids);
}
$row = new Row();
@ -89,6 +90,7 @@ class StoreController extends AdminController
$tab = Tab::make()->theme();
$tab->add('商品', Card::make($this->gridProduct($id)));
$tab->add('库存', Card::make($this->gridStock($id)));
if ($canAdmin) {
$tab->add('管理员', Card::make($this->gridAdmin($id)));
}
@ -104,11 +106,11 @@ class StoreController extends AdminController
*/
protected function form()
{
$query = Store::with(['adminUsers', 'productSkus']);
$query = Store::query();
$user = Admin::user();
$canAdmin = $this->canAdmin();
if (!$canAdmin) {
$ids = DB::table('store_admin_users')->where('admin_user_id', $user->id)->pluck('store_id');
$ids = DB::table('store_admin_users')->where('administrator_id', $user->id)->pluck('store_id');
$query = $query->whereIn('id', $ids);
}
return Form::make($query, function (Form $form) use ($canAdmin) {
@ -116,28 +118,6 @@ class StoreController extends AdminController
$form->text('title');
$form->switch('status')->default(1);
$form->number('sort')->min(1)->default(1);
// if ($canAdmin) {
// $form->multipleSelectTable('admin_users')
// ->from(\App\Admin\Renderable\AdministratorTable::make())
// ->model(Administrator::class, 'id', 'name')
// ->customFormat(function ($v) {
// if (! $v) {
// return [];
// }
// return array_column($v, 'id');
// });
// }
$form->multipleSelectTable('product_skus')
->from(\App\Admin\Renderable\ProductSkuSimpleTable::make())
->model(\App\Models\ProductSku::class, 'id', 'name')
->customFormat(function ($v) {
if (! $v) {
return [];
}
return array_column($v, 'id');
});
$form->image('image')
->required()
->move('store/'.Carbon::now()->toDateString())
@ -152,16 +132,6 @@ class StoreController extends AdminController
});
}
public function destroy($id)
{
$info = Store::find($id);
// 删除店铺关联的数据
$info->adminUsers()->detach();
$info->productSkus()->detach();
return parent::destroy($id);
}
protected function canAdmin()
{
return Admin::user()->isRole('administrator');
@ -169,16 +139,14 @@ class StoreController extends AdminController
protected function gridAdmin($id)
{
$grid = Grid::make(new Administrator());
$grid->model()->join('store_admin_users', function ($join) use ($id) {
$join->on('store_admin_users.admin_user_id', 'id')->where('store_id', '=', $id);
});
$grid = Grid::make(StoreAdministrator::with(['administrator']));
$grid->model()->where('store_id', $id);
$grid->setResource('store/' . $id . '/admin');
$grid->column('id');
$grid->column('username');
$grid->column('name');
$grid->column('administrator.id', 'ID');
$grid->column('administrator.username', '登录名');
$grid->column('administrator.name', '姓名');
$grid->paginate(10);
$grid->showCreateButton();
@ -192,14 +160,14 @@ class StoreController extends AdminController
protected function gridProduct($id)
{
$grid = new Grid(StoreProductSku::with(['productSku']));
$grid->model()->where('store_id', $id);
$grid = new Grid(ProductSku::with(['productSku']));
$grid->model()->where('store_id', $id)->orderBy('id', 'desc');
$grid->setResource('store/'.$id.'/product');
$grid->column('productSku.id', 'ID');
$grid->column('productSku.name', '名称');
$grid->column('amount', '库存')->editable();
$grid->column('amount', '库存');
$grid->column('productSku.specs', '规格')->label();
$grid->column('status', '状态')->switch();
$grid->paginate(10);
@ -207,8 +175,30 @@ class StoreController extends AdminController
$grid->showCreateButton();
$grid->enableDialogCreate();
$grid->disableActions();
$grid->showDeleteButton();
$grid->showRowSelector();
return $grid;
}
protected function gridStock($id)
{
$grid = new Grid(StockLog::with(['productSku', 'administrator', 'source', 'tag']));
$grid->model()->where('store_id', $id)->orderBy('created_at', 'desc');
$grid->setResource('store/'.$id.'/stock');
$grid->column('productSku.name', '商品');
$grid->column('amount', '库存');
$grid->column('tag.name', '类目');
$grid->column('administrator.name', '操作人');
$grid->column('remarks', '备注');
$grid->column('created_at', '操作时间');
$grid->disableActions();
$grid->showCreateButton();
$grid->enableDialogCreate();
return $grid;
}
}

View File

@ -9,6 +9,7 @@ use Dcat\Admin\Form;
use Dcat\Admin\Grid;
use Dcat\Admin\Http\Controllers\AdminController;
use Dcat\Admin\Show;
use Illuminate\Validation\Rule;
class VipController extends AdminController
{
@ -81,7 +82,13 @@ class VipController extends AdminController
{
return Form::make(new Vip(), function (Form $form) {
$form->display('id');
$form->number('sort')->min(1)->required()->help('不可重复');
$form->number('sort')->min(1)->required()->help('不可重复')->rules(function (Form $form) {
$rule = Rule::unique('vips');
if ($id = $form->model()->id) {
$rule->ignore($id);
}
return $rule;
});
$form->text('name')->required();
$form->radio('slug')->options(Vip::$typeMap)->default(Vip::TYPE_FAVOITE);
$form->number('ratio')->min(0)->max(100)->help('例如: 60%, 填写 60 即可');

View File

@ -176,9 +176,10 @@ Route::group([
/** 调试接口 **/
// $router->get('test', 'HomeController@test');
$router->resource('store/{store_id}/product', 'StoreProductController');
$router->resource('store/{store_id}/admin', 'StoreAdminController');
$router->resource('store', 'StoreController');
$router->resource('store/{store_id}/product', 'Store\ProductController');
$router->resource('store/{store_id}/admin', 'Store\AdministratorController');
$router->resource('store/{store_id}/stock', 'Store\StockController');
$router->resource('store', 'Store\StoreController');
$router->resource('profit', 'OrderProfitController');
});

View File

@ -1,37 +0,0 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Dcat\Admin\Traits\HasDateTimeFormatter;
class Store extends Model
{
use HasFactory, HasDateTimeFormatter;
protected $attributes = [
'status' => 1,
'sort' => 1
];
public function adminUsers()
{
return $this->belongsToMany(\Dcat\Admin\Models\Administrator::class, 'store_admin_users', 'store_id', 'admin_user_id');
}
public function productSkus()
{
return $this->belongsToMany(ProductSku::class, 'store_product_skus', 'store_id', 'product_sku_id');
}
public function scopeEffective($q)
{
return $q->where('status', 1);
}
public function scopeSort($q)
{
return $q->orderBy('sort')->orderBy('id', 'desc');
}
}

View File

@ -0,0 +1,25 @@
<?php
namespace App\Models\Store;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Administrator extends Model
{
protected $table = 'store_administrators';
protected $fillable = ['store_id', 'administrator_id'];
public $timestamps = false;
public function store()
{
return $this->belongsTo(Store::class, 'store_id');
}
public function administrator()
{
return $this->belongsTo(\Dcat\Admin\Models\Administrator::class, 'administrator_id');
}
}

View File

@ -1,16 +1,18 @@
<?php
namespace App\Models;
namespace App\Models\Store;
use Illuminate\Database\Eloquent\Relations\Pivot;
use Illuminate\Database\Eloquent\Model;
class StoreProductSku extends Pivot
class ProductSku extends Model
{
protected $table = 'store_product_skus';
protected $fillable = ['amount', 'product_sku_id', 'status', 'store_id'];
public $timestamps = false;
public function store()
{
return $this->belongsTo(Store::class, 'store_id');
@ -18,6 +20,6 @@ class StoreProductSku extends Pivot
public function productSku()
{
return $this->belongsTo(ProductSku::class, 'product_sku_id');
return $this->belongsTo(\App\Models\ProductSku::class, 'product_sku_id');
}
}

View File

@ -0,0 +1,41 @@
<?php
namespace App\Models\Store;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Dcat\Admin\Traits\HasDateTimeFormatter;
class StockLog extends Model
{
use HasFactory, HasDateTimeFormatter;
protected $table = 'store_stock_logs';
protected $fillable = ['administrator_id', 'amount', 'product_sku_id', 'remarks', 'source_id', 'source_type', 'store_id', 'tag_id'];
public function store()
{
return $this->belongsTo(Store::class, 'store_id');
}
public function productSku()
{
return $this->belongsTo(\App\Models\ProductSku::class, 'product_sku_id');
}
public function administrator()
{
return $this->belongsTo(\Dcat\Admin\Models\Administrator::class, 'administrator_id');
}
public function source()
{
return $this->morphTo();
}
public function tag()
{
return $this->belongsTo(\App\Models\Tag::class, 'tag_id');
}
}

View File

@ -0,0 +1,54 @@
<?php
namespace App\Models\Store;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Dcat\Admin\Traits\HasDateTimeFormatter;
class Store extends Model
{
use HasFactory, HasDateTimeFormatter;
protected $attributes = [
'status' => 1,
'sort' => 1
];
protected static function booted()
{
static::deleted(function ($model) {
// 商品记录
$model->productSkus()->detach();
// 库存记录
$model->stockLogs()->delete();
// 管理员记录
$model->administrators()->detach();
});
}
public function administrators()
{
return $this->belongsToMany(\Dcat\Admin\Models\Administrator::class, 'store_administrators', 'store_id', 'administrator_id');
}
public function productSkus()
{
return $this->belongsToMany(\App\Models\ProductSku::class, 'store_product_skus', 'store_id', 'product_sku_id')->withPivot('amount', 'status');
}
public function stockLogs()
{
return $this->hasMany(StockLog::class, 'store_id');
}
public function scopeEffective($q)
{
return $q->where('status', 1);
}
public function scopeSort($q)
{
return $q->orderBy('sort')->orderBy('id', 'desc');
}
}

View File

@ -14,7 +14,9 @@ class Tag extends Model
public const TYPE_ORDER = 1;
public const TYPE_PACKAGE = 2;
public const TYPE_AFTER_SALE = 3;
public const TYPE_STORE_STOCK = 4;
protected $fillable = ['type', 'name'];
// /**
// * 标签下的订单

View File

@ -23,19 +23,32 @@ class CreateStoresTable extends Migration
$table->timestamps();
});
Schema::create('store_admin_users', function (Blueprint $table) {
Schema::create('store_administrators', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('store_id');
$table->unsignedBigInteger('admin_user_id');
$table->unsignedBigInteger('administrator_id');
});
Schema::create('store_product_skus', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('store_id');
$table->unsignedBigInteger('product_sku_id');
$table->unsignedInteger('amount')->comment('库存');
$table->unsignedInteger('amount')->default(0)->comment('库存');
$table->tinyInteger('status')->default(1)->comment('状态(1: 可用, 0: 不可用)');
});
Schema::create('store_stock_logs', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('store_id');
$table->unsignedBigInteger('product_sku_id');
$table->integer('amount')->comment('库存变更数量(正/负值)');
$table->unsignedBigInteger('tag_id')->comment('变更类目(tags.id)');
$table->unsignedBigInteger('administrator_id')->nullable()->comment('操作管理员');
$table->string('remarks')->nullable()->comment('备注');
$table->string('source_type')->nullable()->comment('来源');
$table->unsignedBigInteger('source_id')->nullable()->comment('来源');
$table->timestamps();
});
}
/**
@ -46,7 +59,8 @@ class CreateStoresTable extends Migration
public function down()
{
Schema::dropIfExists('stores');
Schema::dropIfExists('store_admin_users');
Schema::dropIfExists('store_administrators');
Schema::dropIfExists('store_product_skus');
Schema::dropIfExists('store_stock_logs');
}
}

View File

@ -15,7 +15,7 @@ class AddSlugToVips extends Migration
{
Schema::table('vips', function (Blueprint $table) {
$table->string('slug')->comment('标识');
$table->unsignedInteger('sort')->unique()->comment('等级');
$table->unsignedInteger('sort')->comment('等级');
$table->integer('ratio')->comment('返佣比例, 10 => 10%');
});
}