generated from liutk/owl-admin-base
admin 门店-店员管理
parent
3191976a75
commit
00ca851db4
|
|
@ -6,7 +6,7 @@ use App\Admin\Services\EmployeeService;
|
|||
use App\Enums\EmployeeStatus;
|
||||
use App\Models\Employee;
|
||||
use Illuminate\Http\Request;
|
||||
use Slowlyo\OwlAdmin\Controllers\AdminController;
|
||||
use App\Admin\Controllers\AdminController;
|
||||
use Slowlyo\OwlAdmin\Renderers\Form;
|
||||
use Slowlyo\OwlAdmin\Renderers\Page;
|
||||
|
||||
|
|
@ -33,18 +33,15 @@ class EmployeeController extends AdminController
|
|||
amisMake()->TableColumn()->name('id')->label(__('employee.id')),
|
||||
amisMake()->TableColumn()->name('name')->label(__('employee.name')),
|
||||
amisMake()->TableColumn()->name('phone')->label(__('employee.phone')),
|
||||
amisMake()->TableColumn()->name('employee_status_text')->label(__('employee.employee_status'))->set('type', 'tag')->set('color', '${employee_status_color}'),
|
||||
amisMake()->TableColumn()->name('employee_status')->label(__('employee.employee_status'))
|
||||
->type('switch')
|
||||
->trueValue(EmployeeStatus::Online)
|
||||
->falseValue(EmployeeStatus::Offline),
|
||||
amisMake()->TableColumn()->name('created_at')->label(__('employee.created_at')),
|
||||
$this->rowActions([
|
||||
$this->rowShowButton(),
|
||||
$this->rowEditButton(true),
|
||||
$this->rowDeleteButton(),
|
||||
amisMake()->AjaxAction()
|
||||
->label(__('employee.leave'))
|
||||
->level('link')
|
||||
->icon('fa fa-sign-out')
|
||||
->confirmText(__('employee.leave_confirm'))
|
||||
->api('post:'.admin_url('hr/employees/${id}/leave')),
|
||||
]),
|
||||
]);
|
||||
|
||||
|
|
@ -57,15 +54,26 @@ class EmployeeController extends AdminController
|
|||
amisMake()->TextControl()->name('name')->label(__('employee.name'))->required(),
|
||||
amisMake()->TextControl()->name('phone')->label(__('employee.phone'))->required(),
|
||||
|
||||
amisMake()->SelectControl()->name('employee_status')->label(__('employee.employee_status'))->options(EmployeeStatus::options())->required(),
|
||||
amisMake()->TagControl()->name('jobs')->label(__('employee.jobs'))
|
||||
->source(admin_url('api/keywords/tree-list').'?parent_key='.Employee::JOB_KEY)
|
||||
->labelField('name')
|
||||
->valueField('key')
|
||||
->joinValues(),
|
||||
amisMake()->DateControl()->name('join_at')->label(__('employee.join_at'))->format('YYYY-MM-DD'),
|
||||
amisMake()->TextControl()->name('username')->label(__('admin.username'))->value('${admin_user.username}')->required(! $edit),
|
||||
amisMake()->TextControl()->name('password')->set('type', 'input-password')->label(__('admin.password'))->required(! $edit),
|
||||
amisMake()->TextControl()->name('confirm_password')->set('type', 'input-password')->label(__('admin.confirm_password'))->required(! $edit),
|
||||
amisMake()->ImageControl()->name('prize_images')->label(__('employee.prize_images'))
|
||||
->multiple()
|
||||
->receiver($this->uploadImagePath() . '?full-url=1')
|
||||
->joinValues(false)
|
||||
->extractValue(true),
|
||||
amisMake()->ImageControl()->name('skill_images')->label(__('employee.skill_images'))
|
||||
->multiple()
|
||||
->receiver($this->uploadImagePath() . '?full-url=1')
|
||||
->joinValues(false)
|
||||
->extractValue(true),
|
||||
amisMake()->TextControl()->name('username')->label(__('admin.username'))->value('${admin_user.username}')->visible(! $edit)->required(! $edit),
|
||||
amisMake()->TextControl()->name('password')->set('type', 'input-password')->label(__('admin.password'))->visible(! $edit)->required(! $edit),
|
||||
amisMake()->TextControl()->name('confirm_password')->set('type', 'input-password')->label(__('admin.confirm_password'))->visible(! $edit)->required(! $edit),
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
@ -76,22 +84,12 @@ class EmployeeController extends AdminController
|
|||
['label' => __('employee.phone'), 'content' => '${phone}'],
|
||||
['label' => __('employee.jobs'), 'content' => amisMake()->Each()->name('jobs')->items(amisMake()->Tag()->label('${name}'))],
|
||||
|
||||
['label' => __('admin.username'), 'content' => '${admin_user.username}'],
|
||||
['label' => __('employee.employee_status'), 'content' => amisMake()->Tag()->label('${employee_status_text}')->color('${employee_status_color}')],
|
||||
['label' => __('employee.join_at'), 'content' => '${join_at}'],
|
||||
['label' => __('employee.leave_at'), 'content' => '${leave_at}'],
|
||||
['label' => __('admin.username'), 'content' => '${admin_user.username}', 'span' => 3],
|
||||
['label' => __('employee.prize_images'), 'content' => amisMake()->Images()->source('${prize_images}')->enlargeAble(), 'span' => 3],
|
||||
['label' => __('employee.skill_images'), 'content' => amisMake()->Images()->source('${skill_images}')->enlargeAble(), 'span' => 3],
|
||||
]));
|
||||
}
|
||||
|
||||
// 员工离职
|
||||
public function leave($id, Request $request)
|
||||
{
|
||||
$user = Employee::findOrFail($id);
|
||||
$user->update([
|
||||
'leave_at' => $request->input('leave_at', now()),
|
||||
'employee_status' => EmployeeStatus::Offline,
|
||||
]);
|
||||
|
||||
return $this->response()->success(null, '操作成功');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,11 +2,18 @@
|
|||
|
||||
namespace App\Admin\Controllers\Store;
|
||||
|
||||
use App\Models\{Store, Employee};
|
||||
use Illuminate\Http\Request;
|
||||
use App\Enums\{BusinessStatus, StoreRole};
|
||||
use App\Admin\Services\StoreService;
|
||||
use Slowlyo\OwlAdmin\Controllers\AdminController;
|
||||
use Slowlyo\OwlAdmin\Renderers\Form;
|
||||
use Slowlyo\OwlAdmin\Renderers\Page;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use App\Admin\Controllers\AdminController;
|
||||
|
||||
/**
|
||||
* 门店管理
|
||||
*/
|
||||
class StoreController extends AdminController
|
||||
{
|
||||
protected string $serviceName = StoreService::class;
|
||||
|
|
@ -22,7 +29,7 @@ class StoreController extends AdminController
|
|||
->filter($this->baseFilter()->body([
|
||||
amis()->GroupControl()->mode('horizontal')->body([
|
||||
amisMake()->TextControl()->name('title')->label(__('store.title'))->columnRatio(3)->clearable(),
|
||||
amisMake()->SelectControl()->name('category_id')->label(__('store.category_id'))->columnRatio(3)
|
||||
amisMake()->TreeSelectControl()->name('category_id')->label(__('store.category_id'))->columnRatio(3)
|
||||
->source(admin_url('api/keywords/tree-list?parent_key=store_category'))
|
||||
->labelField('name')
|
||||
->valueField('key')
|
||||
|
|
@ -40,11 +47,11 @@ class StoreController extends AdminController
|
|||
->labelField('name')
|
||||
->valueField('key')
|
||||
->clearable(),
|
||||
amisMake()->InputCityControl()->name('region')->label(__('store.region'))->columnRatio(3)
|
||||
->allowDistrict(false)
|
||||
->extractValue(false)
|
||||
->clearable(),
|
||||
amisMake()->SelectControl()->name('business_status')->label(__('store.business_status'))->columnRatio(3)->clearable(),
|
||||
// amisMake()->InputCityControl()->name('region')->label(__('store.region'))->columnRatio(3)
|
||||
// ->allowDistrict(false)
|
||||
// ->extractValue(false)
|
||||
// ->clearable(),
|
||||
amisMake()->SelectControl()->name('business_status')->label(__('store.business_status'))->options(BusinessStatus::options())->columnRatio(3)->clearable(),
|
||||
]),
|
||||
]))
|
||||
->columns([
|
||||
|
|
@ -54,7 +61,7 @@ class StoreController extends AdminController
|
|||
amisMake()->TableColumn()->name('business.name')->label(__('store.business_id')),
|
||||
amisMake()->TableColumn()->name('level.name')->label(__('store.level_id')),
|
||||
amisMake()->TableColumn()->name('region')->label(__('store.region'))->set('type', 'tpl')->set('tpl', '${region.province}-${region.city}'),
|
||||
amisMake()->TableColumn()->name('business_status_text')->label(__('store.business_status'))->set('type', 'tag')->set('color', '${business_status_color}'),
|
||||
amisMake()->TableColumn()->name('business_status')->label(__('store.business_status'))->type('switch')->trueValue(BusinessStatus::Open)->falseValue(BusinessStatus::Close),
|
||||
amisMake()->TableColumn()->name('created_at')->label(__('store.created_at')),
|
||||
$this->rowActions([
|
||||
$this->rowShowButton(),
|
||||
|
|
@ -98,7 +105,7 @@ class StoreController extends AdminController
|
|||
|
||||
public function detail(): Form
|
||||
{
|
||||
return $this->baseDetail()->title('')->body(amisMake()->Property()->items([
|
||||
$detail = amisMake()->Property()->items([
|
||||
['label' => __('store.title'), 'content' => '${title}'],
|
||||
['label' => __('store.master_id'), 'content' => '${master.name}'],
|
||||
['label' => __('store.business_status'), 'content' => amisMake()->Tag()->label('${business_status_text}')->color('${business_status_color}')],
|
||||
|
|
@ -106,7 +113,103 @@ class StoreController extends AdminController
|
|||
['label' => __('store.business_id'), 'content' => '${business.name}'],
|
||||
['label' => __('store.level_id'), 'content' => '${level.name}'],
|
||||
['label' => __('store.region'), 'content' => '${region.province}-${region.city}'],
|
||||
['label' => __('store.profit_ratio'), 'content' => '${profit_ratio}%'],
|
||||
['label' => __('store.address'), 'content' => '${region.address}', 'span' => 2],
|
||||
['label' => __('store.profit_ratio'), 'content' => '${profit_ratio}%', 'span' => 3],
|
||||
]);
|
||||
|
||||
// 员工列表
|
||||
$sales = amisMake()->Service()->id('store-employees-table')->api(admin_url('store/stores/${id}/employees'))->initFetch(false)->body(amisMake()->Table()->columns([
|
||||
amisMake()->TableColumn()->name('name')->label(__('employee.name')),
|
||||
amisMake()->TableColumn()->name('phone')->label(__('employee.phone')),
|
||||
])->itemActions([
|
||||
amisMake()->AjaxAction()
|
||||
->label(__('admin.delete'))
|
||||
->level('link')
|
||||
->confirmText(__('admin.confirm_delete'))
|
||||
->api('delete:'.admin_url('store/stores/${id}/employees').'?employees=${id}')
|
||||
->reload('store-employees-table')
|
||||
]));
|
||||
return $this->baseDetail()->title('')->body([$detail, amisMake()->Divider()->title(__('store.employees')), $sales]);
|
||||
}
|
||||
|
||||
public function show($id)
|
||||
{
|
||||
if ($this->actionOfGetData()) {
|
||||
return $this->response()->success($this->service->getDetail($id));
|
||||
}
|
||||
|
||||
$form = amisMake()->Form()
|
||||
->labelWidth(0)
|
||||
->api('post:' . admin_url('store/stores/'.$id.'/employees'))
|
||||
->redirect('')
|
||||
->onEvent([
|
||||
'submitSucc' => [
|
||||
'actions' => [
|
||||
['actionType' => 'reload','componentId' => 'store-employees-table']
|
||||
]
|
||||
]
|
||||
])
|
||||
->body([
|
||||
amisMake()->TransferControl()->name('employees')
|
||||
->source(admin_url('store/stores/employee_options') . '?store_id='.$id)
|
||||
->selectMode('table')
|
||||
->resultListModeFollowSelect()
|
||||
->joinValues(false)
|
||||
->extractValue(true)
|
||||
->columns([
|
||||
amisMake()->Column()->name('label')->label(__('employee.name')),
|
||||
amisMake()->Column()->name('phone')->label(__('employee.phone')),
|
||||
]),
|
||||
]);
|
||||
$dialog = amisMake()->Dialog()->size('lg')->title('添加'.__('store.employees'))->body($form);
|
||||
$detail = amis()
|
||||
->Card()
|
||||
->className('base-form')
|
||||
->header(['title' => __('admin.detail')])
|
||||
->body($this->detail())
|
||||
->toolbar([
|
||||
amisMake()->DialogAction()->label('添加'. __('store.employees'))->level('primary')->className('mr-1')->dialog($dialog),
|
||||
$this->backButton(),
|
||||
]);
|
||||
|
||||
$page = $this->basePage()->body($detail);
|
||||
|
||||
return $this->response()->success($page);
|
||||
}
|
||||
|
||||
public function employees($id)
|
||||
{
|
||||
$store = Store::findOrFail($id);
|
||||
$list = $store->employees()->wherePivot('role', StoreRole::Employee)->get();
|
||||
return $this->response()->success($list);
|
||||
}
|
||||
|
||||
public function employeeAdd($id, Request $request)
|
||||
{
|
||||
$employees = $request->input('employees');
|
||||
$store = Store::findOrFail($id);
|
||||
$service = $this->service;
|
||||
if ($service->attachEmployee($store, $employees)) {
|
||||
return $this->response()->success();
|
||||
}
|
||||
return $this->response()->fail($service->getError());
|
||||
}
|
||||
|
||||
public function employeeDestroy($id, Request $request)
|
||||
{
|
||||
$employees = $request->input('employees');
|
||||
$store = Store::findOrFail($id);
|
||||
$service = $this->service;
|
||||
if ($service->destroyEmployee($store, is_array($employees) ? $employees : explode(',', $employees))) {
|
||||
return $this->response()->success();
|
||||
}
|
||||
return $this->response()->fail($service->getError());
|
||||
}
|
||||
|
||||
public function employeeOptions()
|
||||
{
|
||||
$ignore = DB::table('store_employees')->pluck('employee_id');
|
||||
$list = Employee::select(['name as label', 'id as value', 'phone'])->whereNotIn('id', $ignore)->get();
|
||||
return $this->response()->success($list);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,4 +6,28 @@ use EloquentFilter\ModelFilter;
|
|||
|
||||
class StoreFilter extends ModelFilter
|
||||
{
|
||||
public function title($name)
|
||||
{
|
||||
$this->whereLike('title', $name);
|
||||
}
|
||||
|
||||
public function category($key)
|
||||
{
|
||||
$this->where('category_id', $key);
|
||||
}
|
||||
|
||||
public function business($key)
|
||||
{
|
||||
$this->where('business_id', $key);
|
||||
}
|
||||
|
||||
public function level($key)
|
||||
{
|
||||
$this->where('level_id', $key);
|
||||
}
|
||||
|
||||
public function businessStatus($key)
|
||||
{
|
||||
$this->where('business_status', $key);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ use Illuminate\Database\Eloquent\Builder;
|
|||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Slowlyo\OwlAdmin\Models\AdminUser;
|
||||
use Slowlyo\OwlAdmin\Services\AdminUserService;
|
||||
|
||||
|
|
@ -70,7 +71,6 @@ class EmployeeService extends BaseService
|
|||
|
||||
public function resloveData($data, $model = null)
|
||||
{
|
||||
// 管理员信息
|
||||
$adminUserService = AdminUserService::make();
|
||||
if ($model) {
|
||||
// 修改管理员信息
|
||||
|
|
@ -108,11 +108,19 @@ class EmployeeService extends BaseService
|
|||
|
||||
public function preDelete(array $ids)
|
||||
{
|
||||
// 店长关联
|
||||
if (DB::table('store_employees')->whereIn('employee_id', $ids)->exists()) {
|
||||
return '员工已关联门店, 请先从门店中删除';
|
||||
}
|
||||
|
||||
// 删除管理员
|
||||
$adminUserIds = Employee::whereIn('id', $ids)->pluck('admin_user_id')->implode(',');
|
||||
$adminUserService = AdminUserService::make();
|
||||
$adminUserService->delete($adminUserIds);
|
||||
|
||||
// 删除职位关联
|
||||
DB::table('employee_jobs')->whereIn('employee_id', $ids)->delete();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,11 +2,12 @@
|
|||
|
||||
namespace App\Admin\Services;
|
||||
|
||||
use App\Admin\Filters\StoreFilter;
|
||||
use App\Enums\StoreRole;
|
||||
use App\Models\Store;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Validation\Rule;
|
||||
use App\Models\{Store, Employee};
|
||||
use App\Admin\Filters\StoreFilter;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
class StoreService extends BaseService
|
||||
{
|
||||
|
|
@ -48,18 +49,44 @@ class StoreService extends BaseService
|
|||
|
||||
// 修改店长
|
||||
if (isset($data['master_id']) && $data['master_id'] != $model->master_id) {
|
||||
$store->employees()->detach($model->master_id);
|
||||
$store->employees()->attach([$data['master_id'] => ['role' => StoreRole::Master]]);
|
||||
$model->employees()->detach($model->master_id);
|
||||
$model->employees()->attach([$data['master_id'] => ['role' => StoreRole::Master]]);
|
||||
}
|
||||
|
||||
return $model->update($data);
|
||||
}
|
||||
|
||||
// 添加员工
|
||||
public function attachEmployee(Store $store, array $employeeIds)
|
||||
{
|
||||
$data = [];
|
||||
$employees = Employee::whereIn('id', $employeeIds)->get();
|
||||
// 每个员工只能有一个门店
|
||||
foreach ($employees as $employee) {
|
||||
if (DB::table('store_employees')->where('employee_id', $employee->id)->exists()) {
|
||||
$this->setError($employee->name.' 已经是店员');
|
||||
return false;
|
||||
}
|
||||
$data[$employee->id] = ['role' => StoreRole::Employee];
|
||||
}
|
||||
$store->employees()->attach($data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 移除员工
|
||||
public function destroyEmployee(Store $store, array $employeeIds)
|
||||
{
|
||||
$store->employees()->detach($employeeIds);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function resloveData($data, $model = null)
|
||||
{
|
||||
if (isset($data['location'])) {
|
||||
$data['lon'] = data_get($data['location'], 'lng');
|
||||
$data['lat'] = data_get($data['location'], 'lat');
|
||||
$data['address'] = data_get($data['location'], 'address');
|
||||
}
|
||||
|
||||
return $data;
|
||||
|
|
@ -78,7 +105,7 @@ class StoreService extends BaseService
|
|||
'lat' => ['required'],
|
||||
];
|
||||
$updateRules = [
|
||||
'master_id' => [Rule::unique($model, 'master_id')],
|
||||
'master_id' => [Rule::unique('stores', 'master_id')->ignore($model, 'master_id')],
|
||||
];
|
||||
$validator = Validator::make($data, $model ? $updateRules : $createRules, [
|
||||
'master_id.unique' => '已经是店长了',
|
||||
|
|
|
|||
|
|
@ -23,6 +23,10 @@ Route::group([
|
|||
| 门店管理
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
$router->get('store/stores/{id}/employees', [\App\Admin\Controllers\Store\StoreController::class, 'employees']);
|
||||
$router->get('store/stores/employee_options', [\App\Admin\Controllers\Store\StoreController::class, 'employeeOptions']);
|
||||
$router->post('store/stores/{id}/employees', [\App\Admin\Controllers\Store\StoreController::class, 'employeeAdd']);
|
||||
$router->delete('store/stores/{id}/employees', [\App\Admin\Controllers\Store\StoreController::class, 'employeeDestroy']);
|
||||
$router->resource('store/stores', \App\Admin\Controllers\Store\StoreController::class);
|
||||
|
||||
/*
|
||||
|
|
@ -31,7 +35,6 @@ Route::group([
|
|||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
$router->resource('hr/employees', \App\Admin\Controllers\Hr\EmployeeController::class);
|
||||
$router->post('hr/employees/{id}/leave', [\App\Admin\Controllers\Hr\EmployeeController::class, 'leave']);
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Casts\StorageJson;
|
||||
use App\Enums\EmployeeStatus;
|
||||
use App\Traits\HasDateTimeFormatter;
|
||||
use EloquentFilter\Filterable;
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ class Store extends Model
|
|||
{
|
||||
use Filterable, HasDateTimeFormatter;
|
||||
|
||||
protected $fillable = ['title', 'master_id', 'category_id', 'business_id', 'level_id', 'region', 'lon', 'lat', 'profit_ratio', 'profit_money', 'business_status'];
|
||||
protected $fillable = ['title', 'master_id', 'category_id', 'business_id', 'level_id', 'region', 'lon', 'lat', 'address', 'profit_ratio', 'profit_money', 'business_status'];
|
||||
|
||||
protected $casts = [
|
||||
// 地区 {province: 四川省, city: 成都市}
|
||||
|
|
|
|||
|
|
@ -62,6 +62,9 @@ trait UploadTrait
|
|||
}
|
||||
|
||||
$path = $file->store(Admin::config('admin.upload.tem_directory.'.$type).'/'.date('Y-m-d'), Admin::config('admin.upload.disk'));
|
||||
if (request()->has('full-url')) {
|
||||
$path = Storage::disk(Admin::config('admin.upload.disk'))->url($path);
|
||||
}
|
||||
|
||||
return $this->response()->success(['value' => $path]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ return new class extends Migration
|
|||
$table->string('business_id')->comment('经营分类, keywords.store_business');
|
||||
$table->string('level_id')->comment('门店等级, keywords.store_level');
|
||||
$table->json('region')->comment('地区{province,city}');
|
||||
$table->string('address')->nullable()->comment('详细地址');
|
||||
$table->string('lon')->comment('精度');
|
||||
$table->string('lat')->comment('纬度');
|
||||
$table->unsignedInteger('profit_ratio')->default(0)->comment('佣金比例(0-100)');
|
||||
|
|
@ -31,7 +32,7 @@ return new class extends Migration
|
|||
Schema::create('store_employees', function (Blueprint $table) {
|
||||
$table->foreignId('store_id');
|
||||
$table->foreignId('employee_id');
|
||||
$table->unsignedInteger('role')->default(1)->comment('身份(1: 店长, 2: 店员)');
|
||||
$table->unsignedInteger('role')->default(2)->comment('身份(1: 店长, 2: 店员)');
|
||||
$table->comment('门店-店员');
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,4 +16,7 @@ return [
|
|||
'profit_ratio' => '佣金比例',
|
||||
'profit_money' => '店长提成',
|
||||
'business_status' => '状态',
|
||||
'role' => '身份',
|
||||
'address' => '详细地址',
|
||||
'employees' => '店员',
|
||||
];
|
||||
|
|
|
|||
Loading…
Reference in New Issue