admin 出差报备

main
panliang 2024-04-02 12:22:29 +08:00
parent 4f49fefbb2
commit 90dc552bc0
11 changed files with 349 additions and 0 deletions

View File

@ -0,0 +1,145 @@
<?php
namespace App\Admin\Controllers\Hr;
use App\Admin\Controllers\AdminController;
use App\Admin\Services\OfficalBusinessService;
use Slowlyo\OwlAdmin\Renderers\Form;
use Slowlyo\OwlAdmin\Renderers\Page;
use Slowlyo\OwlAdmin\Admin;
use App\Enums\{CheckStatus, EmployeeStatus};
use App\Models\OfficalBusiness;
use App\Traits\HasCheckActions;
/**
* 出差报备
*/
class OfficalBusinessController extends AdminController
{
use HasCheckActions;
protected string $serviceName = OfficalBusinessService::class;
public function list(): Page
{
$crud = $this->baseCRUD()
->tableLayout('fixed')
->headerToolbar([
$this->createTypeButton('drawer', 'xl')->visible(Admin::user()->can('admin.hr.business.create')),
...$this->baseHeaderToolBar(),
])
->bulkActions([])
->filter($this->baseFilter()->body([
amis()->GroupControl()->mode('horizontal')->body([
amisMake()->SelectControl()->name('store_id')->label(__('offical_business.store_id'))
->source(admin_url('api/stores?_all=1'))
->labelField('title')
->valueField('id')
->searchable()
->columnRatio(3)
->clearable(),
amisMake()->TextControl()->name('employee_name')->label(__('offical_business.employee_id'))
->placeholder(__('employee.name').'/'.__('employee.phone'))
->columnRatio(3)
->clearable(),
amisMake()->SelectControl()->name('check_status')->label(__('offical_business.check_status'))
->options(CheckStatus::options())
->columnRatio(3)
->clearable(),
]),
]))
->columns([
amisMake()->TableColumn()->name('store.title')->label(__('offical_business.store_id')),
amisMake()->TableColumn()->name('employee.name')->label(__('offical_business.employee_id')),
amisMake()->TableColumn()->name('start_at')->label(__('offical_business.date')),
amisMake()->TableColumn()->name('end_at')->label(__('offical_business.date')),
amisMake()->TableColumn()->name('address')->label(__('offical_business.address')),
amisMake()->TableColumn()->name('reason')->label(__('offical_business.reason')),
amisMake()->TableColumn()->name('check_status')->label(__('offical_business.check_status'))->set('type', 'mapping')->map(CheckStatus::options()),
amisMake()->TableColumn()->name('created_at')->label(__('offical_business.created_at')),
$this->rowActions([
$this->rowShowButton()->visible(Admin::user()->can('admin.hr.business.view')),
$this->rowEditTypeButton('drawer', 'xl')
->visible(Admin::user()->can('admin.hr.business.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.business.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(__('offical_business.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()->DateRangeControl()
->name('date_range')
->label(__('offical_business.date'))
->required(),
amisMake()->TextControl()->name('address')->label(__('offical_business.address'))->required(),
amisMake()->TextControl()->name('reason')->label(__('offical_business.reason')),
]);
}
public function detail(): Form
{
$subjectType = $this->getMorphAlias();
$detail = amisMake()->Property()->items([
['label' => __('offical_business.store_id'), 'content' => '${store.title}'],
['label' => __('offical_business.employee_id'), 'content' => '${employee.name}'],
['label' => __('offical_business.date'), 'content' => '${start_at} ${end_at}'],
['label' => __('offical_business.address'), 'content' => '${address}'],
['label' => __('offical_business.reason'), 'content' => '${reason}'],
['label' => __('offical_business.created_at'), 'content' => '${created_at}'],
['label' => __('offical_business.check_status'), 'content' => amisMake()->Mapping()->name('check_status')->map(CheckStatus::options())],
['label' => __('offical_business.checked_at'), 'content' => '${checked_at}'],
['label' => __('offical_business.check_remarks'), 'content' => '${check_remarks}'],
]);
$table = amisMake()->Service()
->id('offical-business-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('offical-business-detail'),
$this->failAction()->reload('offical-business-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('offical-business-detail')->title('')->onEvent([
'inited' => [
'actions' => [
['actionType' => 'reload', 'componentId' => 'offical-business-checklog-table'],
]
]
])->body([$detail, amisMake()->Divider(), $table]);
}
public function getMorphAlias()
{
return (new OfficalBusiness)->getMorphClass();
}
}

View File

@ -0,0 +1,39 @@
<?php
namespace App\Admin\Filters;
use EloquentFilter\ModelFilter;
use Carbon\Carbon;
class OfficalBusinessFilter 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 checkStatus($key)
{
$this->where('check_status', $key);
}
public function dateRange($key)
{
$dates = explode(',', $key);
$start = Carbon::createFromTimestamp(data_get($dates, 0, time()))->startOfDay();
$end = Carbon::createFromTimestamp(data_get($dates, 1, time()))->endOfDay();
$this->whereBetween('created_at', [$start, $end]);
}
}

View File

@ -0,0 +1,55 @@
<?php
namespace App\Admin\Services;
use App\Admin\Filters\OfficalBusinessFilter;
use App\Models\{OfficalBusiness, Employee};
use Illuminate\Validation\Rule;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
use Carbon\Carbon;
class OfficalBusinessService extends BaseService
{
protected array $withRelationships = ['store', 'employee'];
protected string $modelName = OfficalBusiness::class;
protected string $modelFilterName = OfficalBusinessFilter::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'])) {
$time = explode(',', $data['date_range']);
$start = Carbon::createFromTimestamp(data_get($time, 0))->startOfDay();
$end = Carbon::createFromTimestamp(data_get($time, 1))->endOfDay();
$data['start_at'] = $start;
$data['end_at'] = $end;
}
return $data;
}
public function validate($data, $model = null)
{
// 验证申请时间是否重叠
if (OfficalBusiness::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'],
'start_at' => ['required'],
'end_at' => ['required'],
'address' => ['required'],
];
$updateRules = [];
$validator = Validator::make($data, $model ? $updateRules : $createRules, []);
if ($validator->fails()) {
return $validator->errors()->first();
}
return true;
}
}

View File

@ -37,6 +37,10 @@ class OvertimeApplyService extends BaseService
public function validate($data, $model = null)
{
// 验证申请时间是否重叠
if (OvertimeApply::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'],
'start_at' => ['required'],

View File

@ -14,6 +14,7 @@ use App\Admin\Controllers\Hr\SignLogController;
use App\Admin\Controllers\Hr\SignRepairController;
use App\Admin\Controllers\Hr\HolidayController;
use App\Admin\Controllers\Hr\OvertimeController;
use App\Admin\Controllers\Hr\OfficalBusinessController;
use App\Admin\Controllers\Store\DeviceController;
use App\Admin\Controllers\Store\EmployeeController as StoreEmployeeController;
use App\Admin\Controllers\Store\StoreController;
@ -88,6 +89,8 @@ Route::group([
$router->resource('holiday', HolidayController::class);
// 加班申请
$router->resource('overtime', OvertimeController::class);
// 出差报备
$router->resource('business', OfficalBusinessController::class);
});
/*

View File

@ -0,0 +1,38 @@
<?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 OfficalBusiness extends Model implements Checkable
{
use HasCheckable, HasDateTimeFormatter, Filterable;
protected $table = 'offical_business';
protected $fillable = ['store_id', 'employee_id', 'start_at', 'end_at', 'address', '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\OfficalBusinessFilter::class;
}
public function store()
{
return $this->belongsTo(Store::class, 'store_id');
}
}

View File

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

View File

@ -0,0 +1,38 @@
<?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('offical_business', function (Blueprint $table) {
$table->id();
$table->foreignId('store_id')->comment('门店, stores.id');
$table->foreignId('employee_id')->comment('申请人, employees.id');
$table->timestamp('start_at')->comment('结束时间');
$table->timestamp('end_at')->comment('开始时间');
$table->string('address')->comment('目的地');
$table->string('reason')->nullable()->comment('事由');
$table->unsignedInteger('check_status')->default(App\Enums\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('offical_business');
}
};

View File

@ -140,6 +140,12 @@ class AdminPermissionSeeder extends Seeder
'uri' => '/hr/overtime',
'resource' => true,
],
'business' => [
'name' => '出差报备',
'icon' => '',
'uri' => '/hr/business',
'resource' => true,
],
],
],

View File

@ -21,6 +21,7 @@ class WorkflowSeeder extends Seeder
['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()],
['key' => 'overtime_apply', 'name' => '加班申请', 'config' => $config, 'created_at' => now(), 'updated_at' => now()],
['key' => 'offical_business', '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' => '结束时间',
'address' => '出差地点',
'date' => '出差时间',
'store_id' => '门店',
'employee_id' => '员工',
'reason' => '出差事由',
];