diff --git a/app/Admin/Controllers/HomeController.php b/app/Admin/Controllers/HomeController.php index 34bf37b..ab1a55f 100644 --- a/app/Admin/Controllers/HomeController.php +++ b/app/Admin/Controllers/HomeController.php @@ -20,6 +20,7 @@ class HomeController extends AdminController public function index(): JsonResponse|JsonResource { $page = $this->basePage()->css($this->css())->body([ + // amis()->IFrame()->src('')//数据驾驶仓地址-todo Grid::make()->columns([ $this->frameworkInfo()->md(5), Flex::make()->items([ diff --git a/app/Admin/Controllers/Plan/PlanController.php b/app/Admin/Controllers/Plan/PlanController.php index e51793b..34cc2ec 100644 --- a/app/Admin/Controllers/Plan/PlanController.php +++ b/app/Admin/Controllers/Plan/PlanController.php @@ -5,6 +5,7 @@ namespace App\Admin\Controllers\Plan; use App\Admin\Controllers\AdminController; use App\Admin\Services\Plan\PlanService; use App\Enums\PlanStatus; +use App\Enums\TaskStatus; use Illuminate\Support\Arr; use Slowlyo\OwlAdmin\Admin; use Slowlyo\OwlAdmin\Renderers\Form; @@ -161,6 +162,28 @@ class PlanController extends AdminController public function detail(): Form { return $this->baseDetail()->title('')->body([ + amis()->Property()->items([ + ['label' => __('plan.plan.name'), 'content' => '${name}'], + ['label' => __('plan.plan.type'), 'content' => amis()->Mapping()->name('planable_type')->map($this->planableTypeLabelMap)], + ['label' => __('plan.plan.status'), 'content' => amis()->Mapping()->name('plan_status')->map(PlanStatus::labelMap())], + ]), + + amis()->Divider(), + + // 总账录入 + amis()->CRUDTable() + ->api(admin_url('api/tasks?plan_id=${id}')) + ->columns([ + amis()->TableColumn('id', __('plan.task.id')), + amis()->TableColumn('name', __('plan.task.name')), + amis()->TableColumn('taskable.date', __('plan.task_ledger.date')), + amis()->TableColumn('taskable.store.title', __('plan.task_ledger.store')), + amis()->TableColumn('taskable.store.master.name', __('plan.task_ledger.store_master')), + amis()->TableColumn('task_status', __('plan.task.status'))->type('mapping')->map(TaskStatus::labelMap()), + amis()->TableColumn('completed_at', __('plan.task.completed_at')), + amis()->TableColumn('created_at', __('plan.task.created_at')), + ]) + ->visibleOn('${planable_type == "plan_ledgers"}'), ]); } } diff --git a/app/Admin/Controllers/Plan/TaskController.php b/app/Admin/Controllers/Plan/TaskController.php new file mode 100644 index 0000000..32298d6 --- /dev/null +++ b/app/Admin/Controllers/Plan/TaskController.php @@ -0,0 +1,35 @@ +input(), TaskFilter::class) + ->with([ + 'taskable' => function (MorphTo $morphTo) { + $morphTo->morphWith([ + TaskLedger::class => ['store.master'], + ]); + }, + ]) + ->latest('id') + ->get(); + + return $this->response()->success($tasks); + } +} diff --git a/app/Admin/Filters/TaskFilter.php b/app/Admin/Filters/TaskFilter.php new file mode 100644 index 0000000..e33157a --- /dev/null +++ b/app/Admin/Filters/TaskFilter.php @@ -0,0 +1,13 @@ +where('plan_id', $id); + } +} diff --git a/app/Admin/Services/Plan/TaskService.php b/app/Admin/Services/Plan/TaskService.php new file mode 100644 index 0000000..9a839d6 --- /dev/null +++ b/app/Admin/Services/Plan/TaskService.php @@ -0,0 +1,19 @@ +post('workflow/success', [WorkflowController::class, 'success']); $router->post('workflow/fail', [WorkflowController::class, 'fail']); $router->get('workflow/logs', [WorkflowController::class, 'logs']); + + $router->get('tasks', [TaskController::class, 'shareList']); }); }); diff --git a/app/Console/Commands/TaskLedgerGenerateCommand.php b/app/Console/Commands/TaskLedgerGenerateCommand.php new file mode 100644 index 0000000..aa01cbb --- /dev/null +++ b/app/Console/Commands/TaskLedgerGenerateCommand.php @@ -0,0 +1,99 @@ +argument('date'); + + $this->generateTasks( + $date ? Carbon::parse($date) : today() + ); + } + + protected function generateTasks(Carbon $datetime): void + { + /** @var \App\Models\PlanLedger */ + $planable = DB::transaction(function () use ($datetime) { + /** @var \App\Models\PlanLedger */ + $planable = PlanLedger::firstOrNew([ + 'date' => $datetime->format('Y-m-d'), + ]); + + if ($planable->exists) { + return $planable; + } + + $planable->save(); + + $plan = $planable->plan()->create([ + 'name' => "{$planable->date} 总账录入", + 'plan_status' => PlanStatus::Published, + ]); + + return $planable->setRelation('plan', $plan); + }); + + $stores = Store::all(); + + /** @var \App\Models\Store */ + foreach ($stores as $store) { + DB::transaction(function () use ($store, $planable) { + $taskable = TaskLedger::firstOrNew([ + 'store_id' => $store->id, + 'date' => $planable->date, + ]); + + if ($taskable->exists) { + return; + } + + $taskable->save(); + + $ledger = Ledger::where('store_id', $store->id) + ->where('date', $planable->date) + ->first(); + + $date = Carbon::parse($planable->date); + + $taskable->task()->create([ + 'plan_id' => $planable->plan->id, + 'name' => '总账录入', + 'start_at' => $date->copy()->startOfDay(), + 'end_at' => $date->copy()->endOfDay(), + 'task_status' => $ledger ? TaskStatus::Success : TaskStatus::Pending, + 'completed_at' => $ledger?->created_at, + ]); + }); + } + } +} diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index e6b9960..5c2613a 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -12,7 +12,7 @@ class Kernel extends ConsoleKernel */ protected function schedule(Schedule $schedule): void { - // $schedule->command('inspire')->hourly(); + $schedule->command(Commands\TaskLedgerGenerateCommand::class)->dailyAt('01:00'); } /** diff --git a/app/Enums/TaskStatus.php b/app/Enums/TaskStatus.php new file mode 100644 index 0000000..79b8399 --- /dev/null +++ b/app/Enums/TaskStatus.php @@ -0,0 +1,39 @@ +value]; + } + + public static function options(): array + { + return [ + self::Pending->value => '待完成', + self::Processing->value => '进行中', + self::Success->value => '已完成', + self::Failed->value => '未完成', + self::Revoked->value => '已撤销', + ]; + } + + public static function labelMap(): array + { + return [ + self::Pending->value => ''.self::Pending->text().'', + self::Processing->value => ''.self::Processing->text().'', + self::Success->value => ''.self::Success->text().'', + self::Failed->value => ''.self::Failed->text().'', + self::Revoked->value => ''.self::Revoked->text().'', + ]; + } +} diff --git a/app/Models/PlanLedger.php b/app/Models/PlanLedger.php index befaae7..c53ec6f 100644 --- a/app/Models/PlanLedger.php +++ b/app/Models/PlanLedger.php @@ -11,10 +11,6 @@ class PlanLedger extends Model { use HasFactory, HasDateTimeFormatter; - protected $casts = [ - 'date' => 'date', - ]; - protected $fillable = [ 'date', ]; diff --git a/app/Models/Task.php b/app/Models/Task.php new file mode 100644 index 0000000..c95e87b --- /dev/null +++ b/app/Models/Task.php @@ -0,0 +1,42 @@ + TaskStatus::Pending, + ]; + + protected $casts = [ + 'start_at' => 'datetime', + 'end_at' => 'datetime', + 'task_status' => TaskStatus::class, + 'completed_at' => 'datetime', + ]; + + protected $fillable = [ + 'plan_id', + 'taskable_type', + 'taskable_id', + 'name', + 'start_at', + 'end_at', + 'task_status', + 'completed_at', + ]; + + public function taskable(): MorphTo + { + return $this->morphTo(); + } +} diff --git a/app/Models/TaskLedger.php b/app/Models/TaskLedger.php new file mode 100644 index 0000000..3bed357 --- /dev/null +++ b/app/Models/TaskLedger.php @@ -0,0 +1,29 @@ +morphOne(Task::class, 'taskable'); + } + + public function store(): BelongsTo + { + return $this->belongsTo(Store::class); + } +} diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index e3bfef3..c9fc7c3 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -45,6 +45,7 @@ class AppServiceProvider extends ServiceProvider \App\Models\PlanPerformance::class, \App\Models\Reimbursement::class, \App\Models\StoreMasterCommission::class, + \App\Models\TaskLedger::class, \App\Models\EmployeePromotion::class, \App\Models\Agreement::class, ])->mapWithKeys(fn ($model) => [(new $model)->getTable() => $model])->all() diff --git a/config/admin.php b/config/admin.php index 7a63480..af01553 100644 --- a/config/admin.php +++ b/config/admin.php @@ -2,7 +2,7 @@ return [ // 应用名称 - 'name' => 'Owl Admin', + 'name' => '门店助手-管理后台', // 应用 logo 'logo' => '/admin-assets/logo.png', diff --git a/database/migrations/2024_04_08_223111_create_plans_table.php b/database/migrations/2024_04_08_223111_create_plans_table.php index ac02e8a..18f17c3 100644 --- a/database/migrations/2024_04_08_223111_create_plans_table.php +++ b/database/migrations/2024_04_08_223111_create_plans_table.php @@ -13,10 +13,13 @@ return new class extends Migration { Schema::create('plans', function (Blueprint $table) { $table->id(); + $table->string('planable_type'); + $table->unsignedBigInteger('planable_id'); $table->string('name')->comment('名称'); - $table->morphs('planable'); $table->tinyInteger('plan_status')->comment('状态'); $table->timestamps(); + + $table->unique(['planable_type', 'planable_id'], 'planable_type'); }); } diff --git a/database/migrations/2024_04_15_075647_create_tasks_table.php b/database/migrations/2024_04_15_075647_create_tasks_table.php new file mode 100644 index 0000000..8f552d0 --- /dev/null +++ b/database/migrations/2024_04_15_075647_create_tasks_table.php @@ -0,0 +1,37 @@ +id(); + $table->string('taskable_type'); + $table->unsignedBigInteger('taskable_id'); + $table->foreignId('plan_id')->comment('任务计划'); + $table->string('name')->comment('任务名称'); + $table->dateTime('start_at')->nullable()->comment('开始时间'); + $table->dateTime('end_at')->nullable()->comment('结束时间'); + $table->tinyInteger('task_status')->comment('任务状态'); + $table->dateTime('completed_at')->nullable()->comment('完成时间'); + $table->timestamps(); + + $table->unique(['taskable_type', 'taskable_id'], 'taskable_type'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('tasks'); + } +}; diff --git a/database/migrations/2024_04_15_081617_create_task_ledgers_table.php b/database/migrations/2024_04_15_081617_create_task_ledgers_table.php new file mode 100644 index 0000000..07b128e --- /dev/null +++ b/database/migrations/2024_04_15_081617_create_task_ledgers_table.php @@ -0,0 +1,29 @@ +id(); + $table->foreignId('store_id')->comment('门店ID'); + $table->date('date')->comment('日期'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('task_ledgers'); + } +}; diff --git a/database/seeders/AdminPermissionSeeder.php b/database/seeders/AdminPermissionSeeder.php index 3ff14a3..926d563 100644 --- a/database/seeders/AdminPermissionSeeder.php +++ b/database/seeders/AdminPermissionSeeder.php @@ -27,7 +27,9 @@ class AdminPermissionSeeder extends Seeder 'name' => '主页', 'icon' => 'line-md:home-twotone-alt', 'uri' => '/dashboard', + 'is_home' => 1, 'children' => [], + 'order' => 1, ], /* @@ -37,50 +39,59 @@ class AdminPermissionSeeder extends Seeder */ 'store' => [ 'name' => '门店管理', - 'icon' => 'material-symbols:store-rounded', + 'icon' => 'material-symbols:store-outline', 'uri' => '/store', + 'order' =>2, 'children' => [ 'stores' => [ - 'name' => '门店管理', - 'icon' => '', + 'name' => '门店列表', + 'icon' => 'ic:outline-storefront', 'uri' => '/store/stores', 'resource' => true, + 'order' => 1, ], 'employees' => [ 'name' => '店员管理', - 'icon' => '', + 'icon' => 'ic:baseline-people-outline', 'uri' => '/store/employees', 'resource' => ['list', 'create', 'delete'], + 'order' => 2, ], 'categories' => [ 'name' => '门店分类', - 'icon' => '', + 'icon' => 'tabler:category', 'uri' => '/store/categories?parent_key=store_category', 'resource' => true, + 'order' => 3, ], 'levels' => [ 'name' => '门店等级', - 'icon' => '', + 'icon' => 'carbon:skill-level', 'uri' => '/store/levels?parent_key=store_levels', 'resource' => true, + 'order' => 4, + ], 'business' => [ 'name' => '经营类别', - 'icon' => '', + 'icon' => 'mingcute:certificate-line', 'uri' => '/store/business?parent_key=store_business', 'resource' => true, + 'order' => 5, ], 'devices' => [ 'name' => '彩票机管理', - 'icon' => '', + 'icon' => 'streamline:money-cashier-shop-shopping-pay-payment-cashier-store-cash-register-machine', 'uri' => '/store/devices', 'resource' => true, + 'order' => 6, ], 'lottery-types' => [ 'name' => '彩种类型', - 'icon' => '', + 'icon' => 'mingcute:sunflower-line', 'uri' => '/store/lottery-types?parent_key=lottery_type', 'resource' => true, + 'order' => 7, ], ], ], @@ -92,65 +103,75 @@ class AdminPermissionSeeder extends Seeder */ 'hr' => [ 'name' => '人事管理', - 'icon' => 'flowbite:user-settings-solid', + 'icon' => 'material-symbols:deployed-code-account-outline', 'uri' => '/hr', + 'order' => 3, 'children' => [ 'employees' => [ 'name' => '员工管理', - 'icon' => '', + 'icon' => 'material-symbols:user-attributes-outline', 'uri' => '/hr/employees', 'resource' => true, 'children' => [ 'leave' => '离职', ], + 'order' => 1, ], 'jobs' => [ 'name' => '职位管理', - 'icon' => '', + 'icon' => 'material-symbols:frame-person-outline-sharp', 'uri' => '/hr/jobs?parent_key=job', 'resource' => true, + 'order' => 2, ], 'rests' => [ - 'name' => '休息管理', - 'icon' => '', + 'name' => '休息日管理', + 'icon' => 'material-symbols-light:timelapse-outline-rounded', 'uri' => '/hr/rests', 'resource' => ['list', 'create', 'delete'], + 'order' => 3, ], 'signs' => [ 'name' => '考勤打卡', - 'icon' => '', + 'icon' => 'material-symbols:calendar-clock-outline', 'uri' => '/hr/signs', 'resource' => ['list', 'view'], + 'order' => 4, ], 'repairs' => [ 'name' => '补卡申请', - 'icon' => '', + 'icon' => 'mdi:table-clock', 'uri' => '/hr/repairs', 'resource' => true, + 'order' => 5, ], 'holiday' => [ 'name' => '请假申请', - 'icon' => '', + 'icon' => 'ic:outline-more-time', 'uri' => '/hr/holiday', 'resource' => true, + 'order' => 6, ], 'overtime' => [ 'name' => '加班申请', - 'icon' => '', + 'icon' => 'ic:round-add-alarm', 'uri' => '/hr/overtime', 'resource' => true, + 'order' => 7, ], 'business' => [ 'name' => '出差报备', - 'icon' => '', + 'icon' => 'material-symbols:car-tag-outline', 'uri' => '/hr/business', 'resource' => true, + 'order' => 8, ], 'promotion' => [ 'name' => '升职申请', - 'icon' => '', + 'icon' => 'fluent:people-star-32-regular', 'uri' => '/hr/promotion', 'resource' => true, + 'order' => 9, ], ], ], @@ -161,13 +182,14 @@ class AdminPermissionSeeder extends Seeder |-------------------------------------------------------------------------- */ 'plan' => [ - 'name' => '任务计划', - 'icon' => 'flowbite:user-settings-solid', - 'uri' => '/hr', + 'name' => '任务管理', + 'icon' => 'mingcute:send-plane-line', + 'uri' => '/plan', + 'order' => 4, 'children' => [ 'plans' => [ - 'name' => '任务计划', - 'icon' => '', + 'name' => '任务列表', + 'icon' => 'tdesign:task', 'uri' => '/plan/plans', 'resource' => true, 'children' => [], @@ -184,6 +206,7 @@ class AdminPermissionSeeder extends Seeder 'name' => '投诉意见', 'icon' => 'mdi:star-four-points-box-outline', 'uri' => '/complaint', + 'order' => 5, 'children' => [ 'complaints' => [ 'name' => '举报投诉', @@ -214,6 +237,7 @@ class AdminPermissionSeeder extends Seeder 'name' => '财务报表', 'icon' => 'material-symbols:finance-mode', 'uri' => '/finance', + 'order' => 6, 'children' => [ 'ledgers' => [ 'name' => '上报数据', @@ -270,24 +294,25 @@ class AdminPermissionSeeder extends Seeder 'train' => [ 'name' => '培训管理', - 'icon' => '', + 'icon' => 'solar:people-nearby-broken', 'uri' => '/train', + 'order' => 7, 'children' => [ 'books' => [ 'name' => '课件管理', - 'icon' => '', + 'icon' => 'material-symbols:library-books-outline-rounded', 'uri' => '/train/books', 'resource' => true, ], 'questions' => [ 'name' => '题库管理', - 'icon' => '', + 'icon' => 'ph:books-duotone', 'uri' => '/train/questions', 'resource' => true, ], 'examinations' => [ 'name' => '考试管理', - 'icon' => '', + 'icon' => 'material-symbols:checkbook-outline', 'uri' => '/train/examinations', 'resource' => true, ], @@ -295,7 +320,7 @@ class AdminPermissionSeeder extends Seeder ], 'agreement' => [ 'name' => '合同管理', - 'icon' => '', + 'icon' => 'flowbite:inbox-full-outline', 'uri' => '/agreement', 'resource' => true, 'children' => [ @@ -360,7 +385,7 @@ class AdminPermissionSeeder extends Seeder ], 'workflows' => [ 'name' => '审核流程', - 'icon' => '', + 'icon' => 'carbon:flow-data', 'uri' => '/system/workflows', 'resource' => true, ], diff --git a/lang/zh_CN/plan.php b/lang/zh_CN/plan.php index ec98acb..0e86ad2 100644 --- a/lang/zh_CN/plan.php +++ b/lang/zh_CN/plan.php @@ -14,4 +14,18 @@ return [ 'store_level' => '门店等级', 'performance' => '业绩', ], + + 'task' => [ + 'id' => 'ID', + 'name' => '任务名称', + 'status' => '状态', + 'completed_at' => '完成时间', + 'created_at' => '创建时间', + ], + + 'task_ledger' => [ + 'date' => '日期', + 'store' => '门店', + 'store_master' => '店长', + ], ]; diff --git a/public/admin-assets/logo.png b/public/admin-assets/logo.png index a46d3ef..38c5c3c 100644 Binary files a/public/admin-assets/logo.png and b/public/admin-assets/logo.png differ