清洁任务审批流程

main
Jing Li 2024-04-21 17:49:56 +08:00
parent 9b5184cbbf
commit da9b11de12
11 changed files with 174 additions and 27 deletions

View File

@ -4,14 +4,15 @@ namespace App\Admin\Controllers\Plan;
use App\Admin\Controllers\AdminController; use App\Admin\Controllers\AdminController;
use App\Admin\Services\Plan\PlanService; use App\Admin\Services\Plan\PlanService;
use App\Enums\CheckStatus;
use App\Enums\PlanStatus; use App\Enums\PlanStatus;
use App\Enums\TaskHygieneStatus; use App\Enums\TaskHygieneStatus;
use App\Enums\TaskLedgerStatus; use App\Enums\TaskLedgerStatus;
use App\Enums\TaskPerformanceStatus; use App\Enums\TaskPerformanceStatus;
use App\Enums\TaskStatus;
use App\Models\PlanHygiene; use App\Models\PlanHygiene;
use App\Models\PlanLedger; use App\Models\PlanLedger;
use App\Models\PlanPerformance; use App\Models\PlanPerformance;
use App\Models\TaskHygiene;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Slowlyo\OwlAdmin\Admin; use Slowlyo\OwlAdmin\Admin;
@ -254,6 +255,8 @@ class PlanController extends AdminController
amis()->TableColumn('completed_at', __('plan.task.completed_at')), amis()->TableColumn('completed_at', __('plan.task.completed_at')),
amis()->TableColumn('created_at', __('plan.task.created_at')), amis()->TableColumn('created_at', __('plan.task.created_at')),
$this->rowActions([ $this->rowActions([
$this->taskRowShowButton()
->visible(Admin::user()->can('admin.plan.plans.task_view')),
$this->taskRowDeleteButton() $this->taskRowDeleteButton()
->visible(Admin::user()->can('admin.plan.plans.task_delete')), ->visible(Admin::user()->can('admin.plan.plans.task_delete')),
]), ]),
@ -381,17 +384,66 @@ class PlanController extends AdminController
); );
} }
/**
* 任务 - 行查看按钮
*/
protected function taskRowShowButton(): DrawerAction
{
$taskDetailId = 'task-detail';
$workflowLogListId = 'task-workflow-service';
$detail = $this->baseDetail()
->id($taskDetailId)
->initApi(admin_url('/plan/tasks/${id}?_action=getData'))
->onEvent([
'inited' => [
'actions' => [
['actionType' => 'reload', 'componentId' => $workflowLogListId],
],
],
])
->body([
amis()->Property()->items([
['label' => __('plan.task_hygiene.month'), 'content' => '${taskable.month}'],
['label' => __('plan.task_hygiene.store'), 'content' => '${taskable.store.title}'],
['label' => __('plan.task_hygiene.store_master'), 'content' => '${taskable.store_master.name}'],
['label' => __('plan.task_hygiene.status'), 'content' => amis()->Mapping()->name('taskable.task_status')->map(TaskHygieneStatus::labelMap())],
['label' => __('plan.task.completed_at'), 'content' => '${completed_at}'],
['label' => __('plan.task.created_at'), 'content' => '${created_at}'],
['label' => __('plan.task_hygiene.description'), 'content' => '${taskable.description}', 'span' => 3],
['label' => __('plan.task_hygiene.photos'), 'content' => amis()->Images()->enlargeAble()->source('${taskable.photos}')->enlargeWithGallary(), 'span' => 3],
['label' => __('workflow_log.check_status'), 'content' => amis()->Mapping()->name('taskable.workflow.check_status')->map(CheckStatus::labelMap())],
['label' => __('workflow_log.checked_at'), 'content' => '${taskable.workflow.checked_at}'],
['label' => __('workflow_log.remarks'), 'content' => '${taskable.workflow.check_remarks}'],
]),
amis()->Divider(),
(new TaskController)->baseWorkflowLogList("{$taskDetailId},task-hygiene-table")
->id($workflowLogListId)
->api(admin_url('/api/workflow/logs?id=${taskable.workflow.id}'))
->visibleOn('${taskable_type === "'.(new TaskHygiene())->getMorphClass().'"}'),
]);
$drawer = Drawer::make()
->title(__('admin.show'))
->size('xl')
->closeOnOutside()
->body($detail);
return DrawerAction::make()
->label(__('admin.show'))
->icon('fa fa-eye')
->level('link')
->drawer($drawer);
}
/** /**
* 任务 - 行删除按钮 * 任务 - 行删除按钮
*/ */
protected function taskRowDeleteButton(): AjaxAction protected function taskRowDeleteButton(): AjaxAction
{ {
return amis()->AjaxAction() return (new TaskController())->rowDeleteButton()->api('delete:'.admin_url('/plan/tasks/${id}'));
->label(__('admin.delete'))
->icon('fa-regular fa-trash-can')
->level('link')
->confirmText(__('admin.confirm_delete'))
->api('delete:'.admin_url('/plan/tasks/${id}'));
} }
protected function planableTypeOptions(): array protected function planableTypeOptions(): array

View File

@ -5,20 +5,61 @@ namespace App\Admin\Controllers\Plan;
use App\Admin\Controllers\AdminController; use App\Admin\Controllers\AdminController;
use App\Admin\Filters\TaskFilter; use App\Admin\Filters\TaskFilter;
use App\Admin\Services\Plan\TaskService; use App\Admin\Services\Plan\TaskService;
use App\Enums\CheckStatus;
use App\Enums\TaskHygieneStatus;
use App\Models\Task; use App\Models\Task;
use App\Models\TaskHygiene; use App\Models\TaskHygiene;
use App\Models\TaskLedger; use App\Models\TaskLedger;
use App\Models\TaskPerformance; use App\Models\TaskPerformance;
use App\Traits\HasCheckActions;
use Illuminate\Database\Eloquent\Relations\MorphTo; use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Slowlyo\OwlAdmin\Renderers\Form;
/** /**
* @property TaskService $service * @property TaskService $service
*/ */
class TaskController extends AdminController class TaskController extends AdminController
{ {
use HasCheckActions;
protected string $serviceName = TaskService::class; protected string $serviceName = TaskService::class;
public function detail(): Form
{
$taskDetailId = 'task-detail';
$workflowLogListId = 'task-workflow-service';
return $this->baseDetail()->id($taskDetailId)->title('')->onEvent([
'inited' => [
'actions' => [
['actionType' => 'reload', 'componentId' => $workflowLogListId],
],
],
])->body([
amis()->Property()->items([
['label' => __('plan.task_hygiene.month'), 'content' => '${taskable.month}'],
['label' => __('plan.task_hygiene.store'), 'content' => '${taskable.store.title}'],
['label' => __('plan.task_hygiene.store_master'), 'content' => '${taskable.store_master.name}'],
['label' => __('plan.task_hygiene.status'), 'content' => amis()->Mapping()->name('taskable.task_status')->map(TaskHygieneStatus::labelMap())],
['label' => __('plan.task.completed_at'), 'content' => '${completed_at}'],
['label' => __('plan.task.created_at'), 'content' => '${created_at}'],
['label' => __('plan.task_hygiene.description'), 'content' => '${taskable.description}', 'span' => 3],
['label' => __('plan.task_hygiene.photos'), 'content' => amis()->Images()->enlargeAble()->source('${taskable.photos}')->enlargeWithGallary(), 'span' => 3],
['label' => __('workflow_log.check_status'), 'content' => amis()->Mapping()->name('taskable.workflow.check_status')->map(CheckStatus::labelMap())],
['label' => __('workflow_log.checked_at'), 'content' => '${taskable.workflow.checked_at}'],
['label' => __('workflow_log.remarks'), 'content' => '${taskable.workflow.check_remarks}'],
]),
amis()->Divider(),
$this->baseWorkflowLogList($taskDetailId)
->id($workflowLogListId)
->api(admin_url('/api/workflow/logs?id=${taskable.workflow.id}'))
->visibleOn('${taskable_type === "'.(new TaskHygiene())->getMorphClass().'"}'),
]);
}
public function shareList(Request $request) public function shareList(Request $request)
{ {
$tasks = Task::filter($request->input(), TaskFilter::class) $tasks = Task::filter($request->input(), TaskFilter::class)

View File

@ -52,9 +52,7 @@ class PlanService extends BaseService
], ],
); );
$planableType = Relation::getMorphedModel($data['planable_type']); switch (Relation::getMorphedModel($data['planable_type'])) {
switch ($planableType) {
// 清洁卫生 // 清洁卫生
case PlanHygiene::class: case PlanHygiene::class:
$payload = $data['plan_hygiene'] ?? []; $payload = $data['plan_hygiene'] ?? [];
@ -69,7 +67,7 @@ class PlanService extends BaseService
], ],
); );
$planable = PlanPerformance::create($payload); $planable = PlanHygiene::create($payload);
$planable->plan()->create([ $planable->plan()->create([
'name' => $data['name'], 'name' => $data['name'],

View File

@ -12,8 +12,10 @@ use App\Models\PlanPerformance;
use App\Models\Store; use App\Models\Store;
use App\Models\Task; use App\Models\Task;
use App\Models\TaskHygiene; use App\Models\TaskHygiene;
use App\Models\TaskLedger;
use App\Models\TaskPerformance; use App\Models\TaskPerformance;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Support\Carbon; use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\Validator;
@ -27,6 +29,15 @@ class TaskService extends BaseService
protected string $modelFilterName = TaskFilter::class; protected string $modelFilterName = TaskFilter::class;
public function getDetail($id)
{
if ($task = parent::getDetail($id)) {
$task->taskable->setRelation('task', $task->withoutRelations());
}
return $task;
}
public function store($data): bool public function store($data): bool
{ {
if (! isset($data['plan_id'])) { if (! isset($data['plan_id'])) {
@ -194,4 +205,19 @@ class TaskService extends BaseService
$tasks->each(fn (Task $task) => $task->taskable()->delete()); $tasks->each(fn (Task $task) => $task->taskable()->delete());
} }
public function addRelations($query, string $scene = 'list')
{
if (in_array($scene, ['edit', 'detail'])) {
$query->with([
'taskable' => function (MorphTo $morphTo) {
$morphTo->morphWith([
TaskLedger::class => ['store', 'storeMaster'],
TaskPerformance::class => ['store', 'storeMaster'],
TaskHygiene::class => ['store', 'storeMaster', 'workflow'],
]);
},
]);
}
}
} }

View File

@ -28,7 +28,7 @@ class WorkFlowService extends BaseService
* *
* @param WorkflowCheck $check 待审核记录 * @param WorkflowCheck $check 待审核记录
* @param Employee $user 申请人 * @param Employee $user 申请人
* *
* @return bool true: 成功, false: 失败, $this->getError(): 错误消息 * @return bool true: 成功, false: 失败, $this->getError(): 错误消息
*/ */
public function apply(WorkflowCheck $check, Employee $user) public function apply(WorkflowCheck $check, Employee $user)

View File

@ -155,6 +155,7 @@ Route::group([
$router->resource('plans', PlanController::class); $router->resource('plans', PlanController::class);
$router->post('/plans/{plan}/publish', [PlanController::class, 'publish'])->name('plans.publish'); $router->post('/plans/{plan}/publish', [PlanController::class, 'publish'])->name('plans.publish');
$router->post('/tasks', [TaskController::class, 'store'])->name('plans.task_create'); $router->post('/tasks', [TaskController::class, 'store'])->name('plans.task_create');
$router->get('/tasks/{task}', [TaskController::class, 'show'])->name('plans.task_view');
$router->put('/tasks/{task}', [TaskController::class, 'update'])->name('plans.task_update'); $router->put('/tasks/{task}', [TaskController::class, 'update'])->name('plans.task_update');
$router->delete('/tasks/{task}', [TaskController::class, 'destroy'])->name('plans.task_delete'); $router->delete('/tasks/{task}', [TaskController::class, 'destroy'])->name('plans.task_delete');
}); });

View File

@ -78,7 +78,7 @@ MySQL;
throw tap($th, fn ($th) => report($th)); throw tap($th, fn ($th) => report($th));
} }
return response()->json(); return response()->noContent();
} }
protected function handleTaskSubmit(Task $task, Request $request): void protected function handleTaskSubmit(Task $task, Request $request): void

View File

@ -42,6 +42,11 @@ class TaskHygiene extends Model
return $this->belongsTo(Employee::class, 'store_master_id'); return $this->belongsTo(Employee::class, 'store_master_id');
} }
public function checkSuccess()
{
$this->task->markAsSuccess();
}
protected function photos(): Attribute protected function photos(): Attribute
{ {
return Attribute::make( return Attribute::make(
@ -69,15 +74,15 @@ class TaskHygiene extends Model
return TaskHygieneStatus::None; return TaskHygieneStatus::None;
} }
if ($this->task->isSuccess() || $this->checkSuccess()) { if ($this->task->isSuccess() || $this->isCheckSuccess()) {
return TaskHygieneStatus::Success; return TaskHygieneStatus::Success;
} }
if ($this->checkProcessing()) { if ($this->isCheckProcessing()) {
return TaskHygieneStatus::Checking; return TaskHygieneStatus::Checking;
} }
if ($this->checkFail()) { if ($this->isCheckFail()) {
return TaskHygieneStatus::Unpassed; return TaskHygieneStatus::Unpassed;
} }

View File

@ -35,35 +35,55 @@ trait HasCheckable
} }
/** /**
* 审核 * 审核通过
*/ */
public function checkProcessing(): bool public function checkApply()
{ {
return $this->workflow->check_status === CheckStatus::Processing;
} }
/** /**
* 审核通过 * 审核通过
*/ */
public function checkSuccess(): bool public function checkSuccess()
{ {
return $this->workflow->check_status === CheckStatus::Success;
} }
/** /**
* 审核未通过 * 审核未通过
*/ */
public function checkFail(): bool public function checkFail()
{ {
return $this->workflow->check_status === CheckStatus::Fail;
} }
/** /**
* 取消申请 * 取消申请
*/ */
public function checkCancel(): bool public function checkCancel()
{ {
return $this->workflow->check_status === CheckStatus::Cancel; }
/**
* 是否审核中
*/
public function isCheckProcessing(): bool
{
return $this->workflow->check_status === CheckStatus::Processing;
}
/**
* 是否审核通过
*/
public function isCheckSuccess(): bool
{
return $this->workflow->check_status === CheckStatus::Success;
}
/**
* 是否审核未通过
*/
public function isCheckFail(): bool
{
return $this->workflow->check_status === CheckStatus::Fail;
} }
public function canUpdate(): bool public function canUpdate(): bool

View File

@ -182,18 +182,20 @@ class AdminPermissionSeeder extends Seeder
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
*/ */
'plan' => [ 'plan' => [
'name' => '任务管理', 'name' => '任务计划',
'icon' => 'mingcute:send-plane-line', 'icon' => 'mingcute:send-plane-line',
'uri' => '/plan', 'uri' => '/plan',
'order' => 4, 'order' => 4,
'children' => [ 'children' => [
'plans' => [ 'plans' => [
'name' => '任务列表', 'name' => '任务计划',
'icon' => 'tdesign:task', 'icon' => 'tdesign:task',
'uri' => '/plan/plans', 'uri' => '/plan/plans',
'resource' => true, 'resource' => true,
'children' => [ 'children' => [
'publish' => '发布',
'task_create' => '创建任务', 'task_create' => '创建任务',
'task_view' => '查看任务',
'task_update' => '编辑任务', 'task_update' => '编辑任务',
'task_delete' => '删除任务', 'task_delete' => '删除任务',
], ],

View File

@ -56,5 +56,7 @@ return [
'store' => '门店', 'store' => '门店',
'store_master' => '店长', 'store_master' => '店长',
'status' => '状态', 'status' => '状态',
'description' => '清洁范围',
'photos' => '清洁结果',
], ],
]; ];