diff --git a/app/Admin/Controllers/Hr/EmployeeController.php b/app/Admin/Controllers/Hr/EmployeeController.php index 492fdab..6538dc4 100644 --- a/app/Admin/Controllers/Hr/EmployeeController.php +++ b/app/Admin/Controllers/Hr/EmployeeController.php @@ -51,6 +51,14 @@ class EmployeeController extends AdminController ->api('post:'.admin_url('hr/employees/${id}/leave')) ->visible(Admin::user()->can('admin.hr.employees.leave')) ->visibleOn('${employee_status == '.EmployeeStatus::Online->value.'}'), + amisMake()->AjaxAction() + ->label(__('employee.reback')) + ->level('link') + ->icon('fa fa-sign-out') + ->confirmText(__('employee.leave_confirm')) + ->api('post:'.admin_url('hr/employees/${id}/leave')) + ->visible(Admin::user()->can('admin.hr.employees.leave')) + ->visibleOn('${employee_status == '.EmployeeStatus::Offline->value.'}'), ]), ]); @@ -108,11 +116,23 @@ class EmployeeController extends AdminController public function leave($id, Request $request) { $user = Employee::findOrFail($id); - $user->update([ - 'leave_at' => $request->input('leave_at', now()), - 'employee_status' => EmployeeStatus::Offline, - ]); + if ($user->employee_status == EmployeeStatus::Online) { + $user->update([ + 'leave_at' => $request->input('leave_at', now()), + 'employee_status' => EmployeeStatus::Offline, + ]); + } else { + $user->update([ + 'leave_at' => null, + 'employee_status' => EmployeeStatus::Online, + ]); + } return $this->response()->success(null, '操作成功'); } + + public function shareList() + { + return $this->service->listQuery()->get(['id', 'name', 'phone']); + } } diff --git a/app/Admin/Controllers/Hr/RestController.php b/app/Admin/Controllers/Hr/RestController.php index f174619..bec5812 100644 --- a/app/Admin/Controllers/Hr/RestController.php +++ b/app/Admin/Controllers/Hr/RestController.php @@ -9,7 +9,7 @@ use Slowlyo\OwlAdmin\Renderers\Form; use Slowlyo\OwlAdmin\Renderers\Page; /** - * 休息日管理 + * 休息管理 */ class RestController extends AdminController { @@ -46,7 +46,7 @@ class RestController extends AdminController { return $this->baseForm()->title('')->body([ amisMake()->SelectControl()->name('employees')->label(__('employee_sign.employee_id')) - ->source(admin_url('hr/employees?_action=getData&_all=1&employee_status='.EmployeeStatus::Online->value)) + ->source(admin_url('api/employees?_all=1&employee_status='.EmployeeStatus::Online->value)) ->labelField('name') ->valueField('id') ->searchable() diff --git a/app/Admin/Controllers/Hr/SignController.php b/app/Admin/Controllers/Hr/SignController.php new file mode 100644 index 0000000..84c407d --- /dev/null +++ b/app/Admin/Controllers/Hr/SignController.php @@ -0,0 +1,52 @@ +baseCRUD() + ->tableLayout('fixed') + ->headerToolbar([ + ...$this->baseHeaderToolBar(), + ]) + ->bulkActions([]) + ->filter($this->baseFilter()->body([ + amis()->GroupControl()->mode('horizontal')->body([ + amisMake()->SelectControl()->source(admin_url('api/stores?_all=1'))->labelField('title')->valueField('id')->searchable()->name('store_id')->label(__('employee_sign.store_id'))->columnRatio(3)->clearable(), + amisMake()->TextControl()->name('employee_name')->label(__('employee_sign.employee_id'))->placeholder(__('employee.name').'/'.__('employee.phone'))->columnRatio(3)->clearable(), + amisMake()->DateRangeControl()->name('date_range')->label(__('employee_sign.date'))->columnRatio(3)->clearable(), + ]), + ])) + ->columns([ + amisMake()->TableColumn()->name('store.name')->label(__('employee_sign.store_id')), + amisMake()->TableColumn()->name('employee.name')->label(__('employee.name')), + amisMake()->TableColumn()->name('sign_type')->label(__('employee_sign.sign_type')), + amisMake()->TableColumn()->name('first_time')->label(__('employee_sign.first_time')), + amisMake()->TableColumn()->name('last_time')->label(__('employee_sign.last_time')), + amisMake()->TableColumn()->name('sign_status')->label(__('employee_sign.sign_status')), + $this->rowActions([ + $this->rowShowButton(), + ]), + ]); + + return $this->baseList($crud); + } + + public function detail(): Form + { + return $this->baseDetail()->title('')->body(amisMake()->Property()->items([ + ])); + } +} diff --git a/app/Admin/Controllers/Store/DeviceController.php b/app/Admin/Controllers/Store/DeviceController.php index 52b5006..43c8af1 100644 --- a/app/Admin/Controllers/Store/DeviceController.php +++ b/app/Admin/Controllers/Store/DeviceController.php @@ -27,7 +27,7 @@ class DeviceController extends AdminController ->filter($this->baseFilter()->body([ amis()->GroupControl()->mode('horizontal')->body([ amisMake()->SelectControl()->name('store_id')->label(__('store_device.store_id')) - ->source(admin_url('store/stores?_action=getData&_all=1')) + ->source(admin_url('api/stores?_all=1')) ->labelField('title') ->valueField('id') ->columnRatio(3) @@ -55,7 +55,7 @@ class DeviceController extends AdminController { return $this->baseForm()->title('')->body([ amisMake()->SelectControl()->name('store_id')->label(__('store_device.store_id')) - ->source(admin_url('store/stores?_action=getData&_all=1')) + ->source(admin_url('api/stores?_all=1')) ->labelField('title') ->valueField('id') ->required(), diff --git a/app/Admin/Controllers/Store/EmployeeController.php b/app/Admin/Controllers/Store/EmployeeController.php index acea2b5..dba63e1 100644 --- a/app/Admin/Controllers/Store/EmployeeController.php +++ b/app/Admin/Controllers/Store/EmployeeController.php @@ -27,7 +27,7 @@ class EmployeeController extends AdminController ->filter($this->baseFilter()->body([ amis()->GroupControl()->mode('horizontal')->body([ amisMake()->SelectControl()->name('store_id')->label(__('employee.store_id')) - ->source(admin_url('store/stores?_action=getData&_all=1')) + ->source(admin_url('api/stores?_all=1')) ->labelField('title') ->valueField('id') ->columnRatio(3) @@ -38,8 +38,8 @@ class EmployeeController extends AdminController ->columns([ amisMake()->TableColumn()->name('id')->label(__('employee.id')), amisMake()->TableColumn()->name('store.title')->label(__('employee.store_id')), - amisMake()->TableColumn()->name('employee.name')->label(__('store.employees')), - amisMake()->TableColumn()->name('employee.phone')->label(__('employee.phone')), + amisMake()->TableColumn()->name('name')->label(__('store.employees')), + amisMake()->TableColumn()->name('phone')->label(__('employee.phone')), $this->rowActions([ // $this->rowEditButton(true)->visible(Admin::user()->can('admin.store.employees.update')), $this->rowDeleteButton()->visible(Admin::user()->can('admin.store.employees.delete')), @@ -53,14 +53,18 @@ class EmployeeController extends AdminController { return $this->baseForm()->title('')->body([ amisMake()->SelectControl()->name('store_id')->label(__('employee.store_id')) - ->source(admin_url('store/stores?_action=getData&_all=1')) + ->source(admin_url('api/stores?_all=1')) ->labelField('title') ->valueField('id') ->required(), amisMake()->SelectControl()->name('employee_id')->label(__('store.employees')) - ->source(admin_url('hr/employees?_action=getData&_all=1')) + ->source(admin_url('api/employees?_all=1&store_id=0&master_store_id=0&enable=1')) ->labelField('name') ->valueField('id') + ->multiple() + ->joinValues(false) + ->extractValue() + ->searchable() ->required(), ]); } diff --git a/app/Admin/Controllers/Store/StoreController.php b/app/Admin/Controllers/Store/StoreController.php index f864282..8fdc2cf 100644 --- a/app/Admin/Controllers/Store/StoreController.php +++ b/app/Admin/Controllers/Store/StoreController.php @@ -80,7 +80,7 @@ class StoreController extends AdminController return $this->baseForm()->title('')->body([ amisMake()->TextControl()->name('title')->label(__('store.title'))->required(), amisMake()->SelectControl()->name('master_id')->label(__('store.master_id')) - ->source(admin_url('hr/employees?_action=getData&_all=1&employee_status='.EmployeeStatus::Online->value)) + ->source(admin_url('api/employees?_all=1&employee_status='.EmployeeStatus::Online->value)) ->labelField('name') ->valueField('id') ->searchable() diff --git a/app/Admin/Filters/EmployeeFilter.php b/app/Admin/Filters/EmployeeFilter.php index 892e308..9c0d4ce 100644 --- a/app/Admin/Filters/EmployeeFilter.php +++ b/app/Admin/Filters/EmployeeFilter.php @@ -22,8 +22,23 @@ class EmployeeFilter extends ModelFilter $this->whereLike('phone', $key); } + public function store($key) + { + $this->where('store_id', $key); + } + + public function masterStore($key) + { + $this->where('master_store_id', $key); + } + public function employeeStatus($key) { $this->whereIn('employee_status', is_array($key) ? $key : explode(',', $key)); } + + public function enable($key) + { + $this->query->enable(); + } } diff --git a/app/Admin/Filters/StoreEmployeeFilter.php b/app/Admin/Filters/EmployeeSignFilter.php similarity index 57% rename from app/Admin/Filters/StoreEmployeeFilter.php rename to app/Admin/Filters/EmployeeSignFilter.php index e38ade5..19205ac 100644 --- a/app/Admin/Filters/StoreEmployeeFilter.php +++ b/app/Admin/Filters/EmployeeSignFilter.php @@ -2,9 +2,10 @@ namespace App\Admin\Filters; +use Carbon\Carbon; use EloquentFilter\ModelFilter; -class StoreEmployeeFilter extends ModelFilter +class EmployeeSignFilter extends ModelFilter { protected $drop_id = false; @@ -27,4 +28,12 @@ class StoreEmployeeFilter extends ModelFilter { $this->where('store_id', $key); } + + public function dateRange($dates) + { + $dates = explode(',', $dates); + $start = Carbon::createFromTimestamp(data_get($dates, 0, time()))->startOfDay(); + $end = Carbon::createFromTimestamp(data_get($dates, 1, time()))->endOfDay(); + $this->whereBetween('date', [$start, $end]); + } } diff --git a/app/Admin/Services/EmployeeSignService.php b/app/Admin/Services/EmployeeSignService.php new file mode 100644 index 0000000..41253ab --- /dev/null +++ b/app/Admin/Services/EmployeeSignService.php @@ -0,0 +1,15 @@ +getModel(); + $filter = $this->getModelFilter(); + + $query = $this->query()->where('store_id', '>', 0); + if ($this->withRelationships) { + $query->with($this->withRelationships); + } + + if ($filter) { + $query->filter(request()->input(), $filter); + } + + if ($this->modelSortAble) { + $query->sort(); + } + + $this->sortable($query); + + return $query; + } + + public function store($data): bool + { + $storeId = $data['store_id']; + $employees = Employee::whereIn('id', $data['employee_id'])->get(); + foreach ($employees as $employee) { + if ($employee->store_id > 0 && $employee->store_id != $storeId) { + $this->setError($employee->name . ' 已经有门店了'); + return false; + } + if ($employee->master_store_id > 0) { + $this->setError($employee->name . ' 已经是店长了'); + return false; + } + } + Employee::whereIn('id', $data['employee_id'])->update(['store_id' => $storeId]); + return true; + } + + public function delete(string $ids): mixed + { + $id = explode(',', $ids); + Employee::whereIn('id', $id)->update(['store_id' => 0]); + return true; + } public function validate($data, $model = null) { $createRules = [ + 'employee_id' => ['required'], 'store_id' => ['required'], - 'employee_id' => ['required', Rule::unique('store_employees', 'employee_id')], ]; $updateRules = [ - 'employee_id' => [Rule::unique('store_employees', 'employee_id')->ignore($model, 'employee_id')], ]; - $validator = Validator::make($data, $model ? $updateRules : $createRules, [ - 'employee_id.unique' => '已经是店员了', - ]); + $validator = Validator::make($data, $model ? $updateRules : $createRules); if ($validator->fails()) { return $validator->errors()->first(); } - return true; } } diff --git a/app/Admin/Services/StoreService.php b/app/Admin/Services/StoreService.php index 5b8d695..e9820d5 100644 --- a/app/Admin/Services/StoreService.php +++ b/app/Admin/Services/StoreService.php @@ -3,7 +3,6 @@ namespace App\Admin\Services; use App\Admin\Filters\StoreFilter; -use App\Enums\StoreRole; use App\Models\Employee; use App\Models\Store; use Illuminate\Support\Facades\DB; @@ -18,72 +17,6 @@ class StoreService extends BaseService protected string $modelFilterName = StoreFilter::class; - public function store($data): bool - { - $data = $this->resloveData($data); - - $validate = $this->validate($data); - if ($validate !== true) { - $this->setError($validate); - - return false; - } - - $model = $this->modelName::create($data); - - // 设置店长 - $model->employees()->attach([$data['master_id'] => ['role' => StoreRole::Master]]); - - return true; - } - - public function update($primaryKey, $data): bool - { - $model = $this->query()->whereKey($primaryKey)->firstOrFail(); - $data = $this->resloveData($data, $model); - $validate = $this->validate($data, $model); - if ($validate !== true) { - $this->setError($validate); - - return false; - } - - // 修改店长 - if (isset($data['master_id']) && $data['master_id'] != $model->master_id) { - $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'])) { @@ -92,6 +25,11 @@ class StoreService extends BaseService $data['address'] = data_get($data['location'], 'address'); } + // 绑定店长 + if (isset($data['master_id'])) { + Employee::where('id', $data['master_id'])->update(['master_store_id' => $model->id]); + } + return $data; } @@ -122,8 +60,8 @@ class StoreService extends BaseService public function preDelete(array $ids) { - // 删除店员 - DB::table('store_employees')->whereIn('store_id', $ids)->delete(); + // 修改员工关联 + Employee::whereIn('store_id', $ids)->update(['store_id' => 0]); return true; } diff --git a/app/Admin/routes.php b/app/Admin/routes.php index bb4c4ad..aac5006 100644 --- a/app/Admin/routes.php +++ b/app/Admin/routes.php @@ -4,6 +4,7 @@ use App\Admin\Controllers\BaseKeywordController; use App\Admin\Controllers\Finance\LedgerController; use App\Admin\Controllers\Hr\EmployeeController; use App\Admin\Controllers\Hr\RestController; +use App\Admin\Controllers\Hr\SignController; use App\Admin\Controllers\Store\DeviceController; use App\Admin\Controllers\Store\EmployeeController as StoreEmployeeController; use App\Admin\Controllers\Store\StoreController; @@ -66,7 +67,10 @@ Route::group([ $router->post('employees/{id}/leave', [EmployeeController::class, 'leave'])->name('employees.leave'); // 职位管理 $router->resource('jobs', BaseKeywordController::class); - $router->resource('rests', RestController::class); + // 休息管理 + $router->resource('rests', RestController::class)->only(['index', 'create', 'store', 'destroy']); + // 打卡情况 + $router->resource('signs', SignController::class)->only(['index', 'show']); }); /* @@ -135,6 +139,7 @@ Route::group([ 'prefix' => 'api', ], function (Router $router) { $router->get('stores', [StoreController::class, 'shareList']); + $router->get('employees', [EmployeeController::class, 'shareList']); $router->get('keywords/tree-list', [KeywordController::class, 'getTreeList'])->name('api.keywords.tree-list'); }); }); diff --git a/app/Enums/StoreRole.php b/app/Enums/StoreRole.php deleted file mode 100644 index 15e1d46..0000000 --- a/app/Enums/StoreRole.php +++ /dev/null @@ -1,35 +0,0 @@ -value => '店长', - self::Employee->value => '店员', - ]; - } - - public static function options() - { - $list = []; - foreach (self::map() as $key => $value) { - array_push($list, ['label' => $value, 'value' => $key]); - } - - return $list; - } - - public function text() - { - return data_get(self::map(), $this->value); - } -} diff --git a/app/Models/Employee.php b/app/Models/Employee.php index abc3b07..727eeb0 100644 --- a/app/Models/Employee.php +++ b/app/Models/Employee.php @@ -19,7 +19,7 @@ class Employee extends Model const JOB_KEY = 'job'; - protected $fillable = ['name', 'phone', 'prize_images', 'skill_images', 'employee_status', 'admin_user_id', 'leave_at', 'join_at', 'remarks']; + protected $fillable = ['store_id', 'master_store_id', 'name', 'phone', 'prize_images', 'skill_images', 'employee_status', 'admin_user_id', 'leave_at', 'join_at', 'remarks']; protected $casts = [ 'employee_status' => EmployeeStatus::class, @@ -53,11 +53,16 @@ class Employee extends Model return $this->belongsTo(AdminUser::class, 'admin_user_id'); } - // 关联的门店 - public function stores() + // 关联的门店(店员) + public function store() { - // role(1: 店长, 2: 店员) - return $this->belongsToMany(Store::class, 'store_employees', 'employee_id', 'store_id')->withPivot(['role']); + return $this->belongsTo(Store::class, 'store_id'); + } + + // 管理的门店(店长) + public function masterStore() + { + return $this->belongsTo(Store::class, 'master_store_id'); } public function scopeEnable($q) diff --git a/app/Models/EmployeeSign.php b/app/Models/EmployeeSign.php index 9745be6..d2d01ea 100644 --- a/app/Models/EmployeeSign.php +++ b/app/Models/EmployeeSign.php @@ -6,13 +6,14 @@ use App\Enums\SignStatus; use App\Enums\SignType; use App\Traits\HasDateTimeFormatter; use Illuminate\Database\Eloquent\Model; +use EloquentFilter\Filterable; /** * 员工-打卡情况 */ class EmployeeSign extends Model { - use HasDateTimeFormatter; + use HasDateTimeFormatter, Filterable; protected $table = 'employee_sign_dates'; diff --git a/app/Models/Store.php b/app/Models/Store.php index 0ba47f4..d9153a9 100644 --- a/app/Models/Store.php +++ b/app/Models/Store.php @@ -7,13 +7,14 @@ use App\Traits\HasDateTimeFormatter; use EloquentFilter\Filterable; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Factories\HasFactory; /** * 门店 */ class Store extends Model { - use Filterable, HasDateTimeFormatter; + use Filterable, HasDateTimeFormatter, HasFactory; protected $fillable = ['title', 'master_id', 'category_id', 'business_id', 'level_id', 'region', 'lon', 'lat', 'address', 'profit_ratio', 'profit_money', 'business_status']; @@ -53,7 +54,7 @@ class Store extends Model public function employees() { // role(1: 店长, 2: 店员) - return $this->belongsToMany(Employee::class, 'store_employees', 'store_id', 'employee_id')->using(StoreEmployee::class); + return $this->hasMany(Employee::class, 'store_id'); } protected function businessStatusText(): Attribute diff --git a/app/Models/StoreEmployee.php b/app/Models/StoreEmployee.php deleted file mode 100644 index 6f2a2ed..0000000 --- a/app/Models/StoreEmployee.php +++ /dev/null @@ -1,28 +0,0 @@ -belongsTo(Store::class, 'store_id'); - } - - public function employee() - { - return $this->belongsTo(Employee::class, 'employee_id'); - } -} diff --git a/database/factories/StoreFactory.php b/database/factories/StoreFactory.php index e89acc5..df348af 100644 --- a/database/factories/StoreFactory.php +++ b/database/factories/StoreFactory.php @@ -5,7 +5,6 @@ namespace Database\Factories; use App\Models\Employee; use App\Models\Keyword; use App\Models\Store; -use App\Models\StoreEmployee; use Illuminate\Database\Eloquent\Factories\Factory; /** @@ -22,10 +21,7 @@ class StoreFactory extends Factory */ public function definition(): array { - do { - $master = Employee::inRandomOrder()->first(); - } while (StoreEmployee::where('employee_id', $master->id)->exists()); - + $master = Employee::where('store_id', 0)->inRandomOrder()->first(); return [ 'title' => $this->faker->word(), 'master_id' => $master->id, @@ -38,4 +34,12 @@ class StoreFactory extends Factory 'address' => '重庆市南川区东城街道办事处东环路三号', ]; } + + public function configure(): static + { + return $this->afterMaking(function (Store $model) { + })->afterCreating(function (Store $model) { + Employee::where('id', $model->master_id)->update(['master_store_id' => $model->id]); + }); + } } diff --git a/database/migrations/2024_03_22_091409_create_employees_table.php b/database/migrations/2024_03_22_091409_create_employees_table.php index 7af8fd1..935ddcd 100644 --- a/database/migrations/2024_03_22_091409_create_employees_table.php +++ b/database/migrations/2024_03_22_091409_create_employees_table.php @@ -13,6 +13,8 @@ return new class extends Migration { Schema::create('employees', function (Blueprint $table) { $table->id(); + $table->foreignId('store_id')->default(0)->comment('所属门店, stores.id'); + $table->foreignId('master_store_id')->default(0)->comment('管理的门店, stores.id'); $table->string('name')->comment('姓名'); $table->string('phone')->comment('电话'); $table->json('prize_images')->nullable()->comment('荣誉证书'); diff --git a/database/migrations/2024_03_23_095309_create_stores_table.php b/database/migrations/2024_03_23_095309_create_stores_table.php index 8615fdf..cff07d8 100644 --- a/database/migrations/2024_03_23_095309_create_stores_table.php +++ b/database/migrations/2024_03_23_095309_create_stores_table.php @@ -28,14 +28,6 @@ return new class extends Migration $table->timestamps(); $table->comment('门店'); }); - - Schema::create('store_employees', function (Blueprint $table) { - $table->id(); - $table->foreignId('store_id'); - $table->foreignId('employee_id'); - $table->unsignedInteger('role')->default(2)->comment('身份(1: 店长, 2: 店员)'); - $table->comment('门店-店员'); - }); } /** @@ -44,6 +36,5 @@ return new class extends Migration public function down(): void { Schema::dropIfExists('stores'); - Schema::dropIfExists('store_employees'); } }; diff --git a/database/seeders/AdminPermissionSeeder.php b/database/seeders/AdminPermissionSeeder.php index f52aec6..2d2afcb 100644 --- a/database/seeders/AdminPermissionSeeder.php +++ b/database/seeders/AdminPermissionSeeder.php @@ -114,8 +114,14 @@ class AdminPermissionSeeder extends Seeder 'name' => '休息管理', 'icon' => '', 'uri' => '/hr/rests', - 'resource' => true, + 'resource' => ['list', 'create', 'delete'], ], + 'signs' => [ + 'name' => '考勤打卡', + 'icon' => '', + 'uri' => '/hr/signs', + 'resource' => ['list', 'view'], + ] ], ], diff --git a/database/seeders/EmployeeSeeder.php b/database/seeders/EmployeeSeeder.php index e746804..b0a7728 100644 --- a/database/seeders/EmployeeSeeder.php +++ b/database/seeders/EmployeeSeeder.php @@ -19,9 +19,8 @@ class EmployeeSeeder extends Seeder DB::table('employee_jobs')->truncate(); Employee::truncate(); (new EmployeeFactory)->count(100)->create(['admin_user_id' => 1]); - - DB::table('store_employees')->truncate(); + Store::truncate(); - (new StoreFactory)->count(10)->create(); + Store::factory()->count(10)->create(); } }