admin 请假申请

main
panliang 2024-04-02 10:49:14 +08:00
parent 03797a1e7c
commit a013b50bcf
13 changed files with 376 additions and 5 deletions

View File

@ -0,0 +1,158 @@
<?php
namespace App\Admin\Controllers\Hr;
use App\Admin\Controllers\AdminController;
use App\Admin\Services\HolidayApplyService;
use Slowlyo\OwlAdmin\Renderers\Form;
use Slowlyo\OwlAdmin\Renderers\Page;
use Slowlyo\OwlAdmin\Admin;
use App\Enums\{CheckStatus, EmployeeStatus};
use App\Models\HolidayApply;
use App\Traits\HasCheckActions;
/**
* 请假管理
*/
class HolidayController extends AdminController
{
use HasCheckActions;
protected string $serviceName = HolidayApplyService::class;
public function list(): Page
{
$crud = $this->baseCRUD()
->tableLayout('fixed')
->headerToolbar([
$this->createTypeButton('drawer', 'xl')->visible(Admin::user()->can('admin.hr.holiday.create')),
...$this->baseHeaderToolBar(),
])
->bulkActions([])
->filter($this->baseFilter()->body([
amis()->GroupControl()->mode('horizontal')->body([
amisMake()->SelectControl()->name('store_id')->label(__('holiday_apply.store_id'))
->source(admin_url('api/stores?_all=1'))
->labelField('title')
->valueField('id')
->searchable()
->columnRatio(3)
->clearable(),
amisMake()->TextControl()->name('employee_name')->label(__('holiday_apply.employee_id'))
->placeholder(__('employee.name').'/'.__('employee.phone'))
->columnRatio(3)
->clearable(),
amisMake()->SelectControl()->name('type_id')->label(__('holiday_apply.type_id'))
->source(admin_url('api/keywords/tree-list?parent_key=holiday_type'))
->labelField('name')
->valueField('key')
->columnRatio(3)
->clearable(),
amisMake()->SelectControl()->name('check_status')->label(__('holiday_apply.check_status'))
->options(CheckStatus::options())
->columnRatio(3)
->clearable(),
]),
]))
->columns([
amisMake()->TableColumn()->name('store.title')->label(__('employee_sign.store_id')),
amisMake()->TableColumn()->name('employee.name')->label(__('holiday_apply.employee_id')),
amisMake()->TableColumn()->name('type.name')->label(__('holiday_apply.type_id')),
amisMake()->TableColumn()->name('reason')->label(__('holiday_apply.reason')),
amisMake()->TableColumn()->name('start_at')->label(__('holiday_apply.start_at')),
amisMake()->TableColumn()->name('end_at')->label(__('holiday_apply.end_at')),
amisMake()->TableColumn()->name('check_status')->label(__('holiday_apply.check_status'))->set('type', 'mapping')->map(CheckStatus::options()),
amisMake()->TableColumn()->name('created_at')->label(__('holiday_apply.created_at')),
$this->rowActions([
$this->rowShowButton()->visible(Admin::user()->can('admin.hr.holiday.view')),
$this->rowEditTypeButton('drawer', 'xl')
->visible(Admin::user()->can('admin.hr.holiday.update'))
->visibleOn('${OR(check_status == '.CheckStatus::None->value.', check_status == '.CheckStatus::Cancel->value.', check_status == '.CheckStatus::Fail->value.')}'),
$this->rowDeleteButton()
->visible(Admin::user()->can('admin.hr.holiday.delete'))
->visibleOn('${OR(check_status == '.CheckStatus::None->value.', check_status == '.CheckStatus::Cancel->value.', check_status == '.CheckStatus::Fail->value.')}'),
$this->applyAction(),
$this->cancelAction(),
]),
]);
return $this->baseList($crud);
}
public function form($edit): Form
{
return $this->baseForm()->title('')->body([
amisMake()->SelectControl()->name('employee_id')->label(__('holiday_apply.employee_id'))
->source(admin_url('api/employees?_all=1&store_id_gt=0&employee_status='.EmployeeStatus::Online->value))
->labelField('name')
->valueField('id')
->searchable()
->joinValues(false)
->extractValue()
->required(),
amisMake()->SelectControl()
->name('type_id')
->label(__('holiday_apply.type_id'))
->source(admin_url('api/keywords/tree-list?parent_key=holiday_type'))
->labelField('name')
->valueField('key')
->required(),
amisMake()->DateRangeControl()
->format('YYYY-MM-DD')
->name('date_range')
->label(__('holiday_apply.date'))
->required(),
amisMake()->TextControl()->name('reason')->label(__('holiday_apply.reason'))->required(),
]);
}
public function detail(): Form
{
$subjectType = $this->getMorphAlias();
$detail = amisMake()->Property()->items([
['label' => __('holiday_apply.store_id'), 'content' => '${store.title}'],
['label' => __('holiday_apply.employee_id'), 'content' => '${employee.name}'],
['label' => __('holiday_apply.type_id'), 'content' => '${type.name}'],
['label' => __('holiday_apply.date'), 'content' => '${start_at} ${end_at}'],
['label' => __('holiday_apply.reason'), 'content' => '${reason}'],
['label' => __('holiday_apply.created_at'), 'content' => '${created_at}'],
['label' => __('holiday_apply.check_status'), 'content' => amisMake()->Mapping()->name('check_status')->map(CheckStatus::options())],
['label' => __('holiday_apply.checked_at'), 'content' => '${checked_at}'],
['label' => __('holiday_apply.check_remarks'), 'content' => '${check_remarks}'],
]);
$table = amisMake()->Service()
->id('holiday-checklog-table')
->initFetch(false)
->api(
amisMake()->BaseApi()->method('get')->url(admin_url('api/workflow/logs'))->data([
'subject_type' => $subjectType,
'subject_id' => '${id}',
])
)
->body(
amisMake()->Table()->columnsTogglable(false)->itemActions([
$this->succesAction()->reload('holiday-detail'),
$this->failAction()->reload('holiday-detail'),
])->columns([
amisMake()->TableColumn()->name('batch_id')->label(__('workflow_log.batch_id')),
amisMake()->TableColumn()->name('check_name')->label(__('workflow_log.check_name')),
amisMake()->TableColumn()->name('check_user.name')->label(__('workflow_log.check_user_id')),
amisMake()->TableColumn()->name('check_status')->label(__('workflow_log.check_status'))->set('type', 'mapping')->map(CheckStatus::options()),
amisMake()->TableColumn()->name('checked_at')->label(__('workflow_log.checked_at')),
amisMake()->TableColumn()->name('remarks')->label(__('workflow_log.remarks')),
])
);
return $this->baseDetail()->id('holiday-detail')->title('')->onEvent([
'inited' => [
'actions' => [
['actionType' => 'reload', 'componentId' => 'holiday-checklog-table'],
]
]
])->body([$detail, amisMake()->Divider(), $table]);
}
public function getMorphAlias()
{
return (new HolidayApply)->getMorphClass();
}
}

View File

@ -32,4 +32,9 @@ class EmployeeSignRepairFilter extends ModelFilter
$end = Carbon::createFromTimestamp(data_get($dates, 1, time()))->endOfDay();
$this->whereBetween('date', [$start, $end]);
}
public function checkStatus($key)
{
$this->where('check_status', $key);
}
}

View File

@ -0,0 +1,36 @@
<?php
namespace App\Admin\Filters;
use EloquentFilter\ModelFilter;
use Carbon\Carbon;
class HolidayApplyFilter extends ModelFilter
{
protected $drop_id = false;
public $relations = [
'store' => [
'store_title' => 'title',
],
'employee' => [
'employee_name' => 'name',
'employee_search' => 'search',
],
];
public function employeeId($key)
{
$this->where('employee_id', $key);
}
public function typeId($key)
{
$this->where('type_id', $key);
}
public function checkStatus($key)
{
$this->where('check_status', $key);
}
}

View File

@ -0,0 +1,55 @@
<?php
namespace App\Admin\Services;
use App\Admin\Filters\HolidayApplyFilter;
use App\Models\{HolidayApply, Employee};
use Illuminate\Validation\Rule;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
use Carbon\Carbon;
class HolidayApplyService extends BaseService
{
protected array $withRelationships = ['store', 'employee', 'type'];
protected string $modelName = HolidayApply::class;
protected string $modelFilterName = HolidayApplyFilter::class;
public function resloveData($data, $model = null)
{
// 获取员工所在的门店
if (!isset($data['store_id']) && isset($data['employee_id'])) {
$data['store_id'] = Employee::where('id', $data['employee_id'])->value('store_id');
}
// 处理日期
if (isset($data['date_range']) && !isset($data['start_at']) && !isset($data['end_at'])) {
$dates = explode(',', $data['date_range']);
$data['start_at'] = Carbon::createFromFormat('Y-m-d', data_get($dates, 0))->startOfDay();
$data['end_at'] = Carbon::createFromFormat('Y-m-d', data_get($dates, 1))->endOfDay();
}
return $data;
}
public function validate($data, $model = null)
{
// 验证申请时间是否重叠
if (HolidayApply::where('employee_id', data_get($data, 'employee_id', $model?->employee_id))->where(fn($q) => $q->whereBetween('start_at', [$data['start_at'], $data['end_at']])->orWhereBetween('end_at', [$data['start_at'], $data['end_at']]))->exists()) {
return '该时间段已经申请过了';
}
$createRules = [
'employee_id' => ['required'],
'type_id' => ['required'],
'reason' => ['required'],
'start_at' => ['required'],
'end_at' => ['required'],
];
$updateRules = [];
$validator = Validator::make($data, $model ? $updateRules : $createRules, []);
if ($validator->fails()) {
return $validator->errors()->first();
}
return true;
}
}

View File

@ -11,6 +11,7 @@ use App\Admin\Controllers\Hr\RestController;
use App\Admin\Controllers\Hr\SignController;
use App\Admin\Controllers\Hr\SignLogController;
use App\Admin\Controllers\Hr\SignRepairController;
use App\Admin\Controllers\Hr\HolidayController;
use App\Admin\Controllers\Store\DeviceController;
use App\Admin\Controllers\Store\EmployeeController as StoreEmployeeController;
use App\Admin\Controllers\Store\StoreController;
@ -81,6 +82,8 @@ Route::group([
$router->resource('signs', SignController::class)->only(['index', 'show']);
// 补卡申请
$router->resource('repairs', SignRepairController::class);
// 请假申请
$router->resource('holiday', HolidayController::class);
});
/*

View File

@ -3,7 +3,7 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use App\Enums\SignTime;
use App\Enums\{SignTime, CheckStatus};
use App\Traits\HasDateTimeFormatter;
use EloquentFilter\Filterable;
use App\Contracts\Checkable;
@ -23,7 +23,8 @@ class EmployeeSignRepair extends Model implements Checkable
protected $casts = [
'date' => 'date:Y-m-d',
'checked_at' => 'datetime',
'repair_type' => SignTime::class
'repair_type' => SignTime::class,
'check_status' => CheckStatus::class,
];
public function modelFilter()

View File

@ -0,0 +1,42 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use App\Contracts\Checkable;
use App\Traits\HasCheckable;
use App\Traits\HasDateTimeFormatter;
use EloquentFilter\Filterable;
use App\Enums\{CheckStatus};
/**
* 请假申请
*/
class HolidayApply extends Model implements Checkable
{
use Filterable, HasCheckable, HasDateTimeFormatter;
protected $fillable = ['store_id', 'employee_id', 'start_at', 'end_at', 'type_id', 'reason', 'check_status', 'checked_at', 'check_remarks'];
protected $casts = [
'start_at' => 'date:Y-m-d',
'end_at' => 'date:Y-m-d',
'check_status' => CheckStatus::class,
];
public function modelFilter()
{
return App\Admin\Filters\HolidayApplyFilter::class;
}
public function store()
{
return $this->belongsTo(Store::class, 'store_id');
}
public function type()
{
// holiday_type
return $this->belongsTo(Keyword::class, 'type_id', 'key');
}
}

View File

@ -32,6 +32,7 @@ class AppServiceProvider extends ServiceProvider
collect([
\App\Models\AdminUser::class,
\App\Models\EmployeeSignRepair::class,
\App\Models\HolidayApply::class,
])->mapWithKeys(fn ($model) => [(new $model)->getTable() => $model])->all()
);
}

View File

@ -0,0 +1,39 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use App\Enums\CheckStatus;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('holiday_applies', function (Blueprint $table) {
$table->id();
$table->foreignId('store_id')->comment('门店, stores.id');
$table->foreignId('employee_id')->comment('申请人, employees.id');
$table->datetime('start_at')->comment('结束时间');
$table->datetime('end_at')->comment('开始时间');
$table->string('type_id')->comment('类型(holiday_type), keywords.key');
$table->string('reason')->comment('事由');
$table->unsignedInteger('check_status')->default(CheckStatus::None->value)->comment('审核状态');
$table->timestamp('checked_at')->nullable()->comment('审核通过时间');
$table->string('check_remarks')->nullable()->comment('审核未通过原因');
$table->timestamps();
$table->comment('请假申请');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('holiday_applies');
}
};

View File

@ -128,6 +128,12 @@ class AdminPermissionSeeder extends Seeder
'uri' => '/hr/repairs',
'resource' => true,
],
'holiday' => [
'name' => '请假申请',
'icon' => '',
'uri' => '/hr/holiday',
'resource' => true,
]
],
],

View File

@ -84,6 +84,11 @@ class KeywordSeeder extends Seeder
'name' => '上报数据类型',
'children' => ['大乐透', '七星彩', '排列三', '排列五', '合买', '即开', ['key' => 'ledger_item_type_other', 'name' => '其他']],
],
[
'key' => 'holiday_type',
'name' => '请假类型',
'children' => ['病假', '事假'],
]
];
$this->insertKeywors($keywords);

View File

@ -13,12 +13,13 @@ class WorkflowSeeder extends Seeder
public function run(): void
{
$now = now();
$config = [
$config = json_encode([
["sort" => 1, "type" => "user", "user" => 1, "title" => "员工", "value" => 1, "subTitle" => "Admin"],
];
]);
Workflow::truncate();
Workflow::insert([
['key' => 'employee_sign_repair', 'name' => '补卡申请', 'config' => json_encode($config), 'created_at' => now(), 'updated_at' => now()]
['key' => 'employee_sign_repair', 'name' => '补卡申请', 'config' => $config, 'created_at' => now(), 'updated_at' => now()],
['key' => 'holiday_apply', 'name' => '请假申请', 'config' => $config, 'created_at' => now(), 'updated_at' => now()],
]);
}
}

View File

@ -0,0 +1,19 @@
<?php
return [
'id' => 'ID',
'created_at' => '提交日期',
'updated_at' => '更新时间',
'check_status' => '审核状态',
'checked_at' => '审核通过时间',
'check_remarks' => '审核未通过原因',
'start_at' => '开始时间',
'end_at' => '结束时间',
'date' => '请假日期',
'store_id' => '门店',
'employee_id' => '员工',
'reason' => '请假事由',
'type_id' => '请假类型',
];