From ea79dd81f1665a7db908c70085e33d3e0ae4ff57 Mon Sep 17 00:00:00 2001 From: panliang <1163816051@qq.com> Date: Sun, 8 Oct 2023 15:25:52 +0800 Subject: [PATCH] admin recorx --- app/Admin/Controllers/CategoryController.php | 32 +++----- .../Controllers/PatientRecordController.php | 79 ++++++++++++++++--- .../Controllers/TotalRecordController.php | 28 ++++--- app/Admin/Services/PatientRecordService.php | 38 ++++----- app/Admin/Services/TotalRecordService.php | 9 +-- app/Casts/StorageFile.php | 2 +- config/admin.php | 2 +- database/factories/AdminUserFactory.php | 1 + database/factories/PatientFactory.php | 7 +- database/factories/PatientRecordFactory.php | 21 ++++- lang/zh_CN/category.php | 4 + lang/zh_CN/patient-record.php | 6 +- lang/zh_CN/total-record.php | 4 +- 13 files changed, 152 insertions(+), 81 deletions(-) diff --git a/app/Admin/Controllers/CategoryController.php b/app/Admin/Controllers/CategoryController.php index 7dc728a..af38d43 100644 --- a/app/Admin/Controllers/CategoryController.php +++ b/app/Admin/Controllers/CategoryController.php @@ -71,25 +71,10 @@ class CategoryController extends AdminController amisMake()->TextControl()->name('key')->label(__('category.key'))->required(true), amisMake()->TextControl()->name('name')->label(__('category.name'))->required(true), amisMake()->ImageControl()->name('image')->label(__('category.image')), - amisMake()->JSONSchemaEditorControl()->set('type', 'json-schema')->name('options')->label(__('keywords.data'))->schema([ - 'type' => 'object', - 'properties' => [ - 'doctor_ratio' => [ - 'type' => 'number', - 'title' => '医生提成比例', - 'description' => '0-100', - ], - 'inviter_ratio' => [ - 'type' => 'number', - 'title' => '邀请人提成比例', - 'description' => '0-100', - ], - 'saler_ratio' => [ - 'type' => 'number', - 'title' => '业务员提成比例', - 'description' => '0-100', - ] - ] + amisMake()->ComboControl()->name('options')->label(__('category.options'))->multiLine()->subFormMode('horizontal')->items([ + amisMake()->NumberControl()->min(0)->max(100)->name('doctor_ratio')->label(__('category.doctor_ratio')), + amisMake()->NumberControl()->min(0)->max(100)->name('inviter_ratio')->label(__('category.inviter_ratio')), + amisMake()->NumberControl()->min(0)->max(100)->name('saler_ratio')->label(__('category.saler_ratio')), ]), // amisMake()->inputKV()->name('options')->label(__('keywords.data')), // Components::make()->fuEditorControl()->name('content')->label(__('category.content')), @@ -103,9 +88,14 @@ class CategoryController extends AdminController amisMake()->TextControl()->name('key')->label(__('category.key'))->static(true), amisMake()->TextControl()->name('name')->label(__('category.name'))->static(true), amisMake()->TextControl()->name('image')->label(__('category.image'))->static()->staticSchema(amisMake()->Images()), - amisMake()->TextControl()->name('options')->label(__('keywords.data'))->static(true)->staticSchema(amisMake()->Json()), + amisMake()->ComboControl()->name('options')->label(__('category.options'))->multiLine()->subFormMode('horizontal')->items([ + amisMake()->NumberControl()->min(0)->max(100)->name('doctor_ratio')->label(__('category.doctor_ratio')), + amisMake()->NumberControl()->min(0)->max(100)->name('inviter_ratio')->label(__('category.inviter_ratio')), + amisMake()->NumberControl()->min(0)->max(100)->name('saler_ratio')->label(__('category.saler_ratio')), + ])->static(), + // amisMake()->TextControl()->name('options')->label(__('keywords.data'))->static(true)->staticSchema(amisMake()->Json()), // Components::make()->fuEditorControl()->name('content')->label(__('category.content'))->static(), - amisMake()->TextareaControl()->fuEditorControl()->name('content')->label(__('category.content'))->static(), + amisMake()->TextareaControl()->name('content')->label(__('category.content'))->static(), ]); } diff --git a/app/Admin/Controllers/PatientRecordController.php b/app/Admin/Controllers/PatientRecordController.php index 7f21a37..1e7485a 100644 --- a/app/Admin/Controllers/PatientRecordController.php +++ b/app/Admin/Controllers/PatientRecordController.php @@ -3,14 +3,13 @@ namespace App\Admin\Controllers; use App\Enums\OrderStatus; -use App\Admin\Components; use App\Models\{Keyword, Patient}; -use Slowlyo\OwlAdmin\Models\AdminUser; use App\Admin\Services\PatientRecordService; use Slowlyo\OwlAdmin\Controllers\AdminController; use Slowlyo\OwlAdmin\Renderers\Form; use Slowlyo\OwlAdmin\Renderers\Page; use Slowlyo\OwlAdmin\Services\AdminUserService; +use Slowlyo\OwlAdmin\Support\Excel\AdminExport; /** * 病历管理 @@ -48,20 +47,23 @@ class PatientRecordController extends AdminController amisMake()->TableColumn()->name('patient.name')->label(__('patient-record.patient_id')), amisMake()->Mapping()->map($this->getTypeOptions()->pluck('label', 'value'))->name('type_id')->label(__('patient-record.type_id')), amisMake()->TableColumn()->name('treat_at')->label(__('patient-record.treat_at')), - amisMake()->TableColumn()->name('origin_price')->label(__('patient-record.origin_price')), amisMake()->TableColumn()->name('sell_price')->label(__('patient-record.sell_price')), amisMake()->TableColumn()->name('doctor.name')->label(__('patient-record.doctor_id')), + amisMake()->TableColumn()->name('doctor_money')->label(__('patient-record.doctor_money')), amisMake()->TableColumn()->name('inviter.name')->label(__('patient-record.inviter_id')), + amisMake()->TableColumn()->name('inviter_money')->label(__('patient-record.inviter_money')), amisMake()->TableColumn()->name('saler.name')->label(__('patient-record.saler_id')), + amisMake()->TableColumn()->name('saler_money')->label(__('patient-record.saler_money')), $this->rowActions(), ]) ->alwaysShowPagination() ->affixRowClassName('text-info-dk') ->affixRow([ - ['type' => 'text', 'text' => '总计', 'colSpan' => 4], - ['type' => 'text', 'text' => __('total-record.origin_price') . ': ${origin_price}'], + ['type' => 'text', 'text' => '总计: ${total}', 'colSpan' => 4], ['type' => 'text', 'text' => __('total-record.sell_price') . ': ${sell_price}'], - ['type' => 'text', 'text' => '', 'colSpan' => 5] + ['type' => 'text', 'text' => __('patient-record.doctor_money').': ${doctor_money}', 'colSpan' => 2], + ['type' => 'text', 'text' => __('patient-record.inviter_money').': ${inviter_money}', 'colSpan' => 2], + ['type' => 'text', 'text' => __('patient-record.saler_money').': ${saler_money}', 'colSpan' => 2], ]); return $this->baseList($crud); @@ -171,6 +173,65 @@ class PatientRecordController extends AdminController return $this->adminUserOptions; } + protected function exportAction($disableSelectedItem = false) + { + $event = fn($script) => ['click' => ['actions' => [['actionType' => 'custom', 'script' => $script]]]]; + $downloadPath = '/' . admin_url('_download_export', true); + $exportPath = $this->getExportPath(); + $doAction = <<onEvent( + $event(<<DropdownButton() + ->label(__('admin.export.title')) + ->set('icon', 'fa-solid fa-download') + ->buttons($buttons) + ->align('right') + ->closeOnClick(); + } + + protected function export() + { + // 默认在 storage/app/ 下 + $path = sprintf('%s-%s.xlsx', $this->exportFileName(), date('YmdHis')); + + // 导出本页和导出选中项都是通过 _ids 查询 + $ids = request()->input('_ids'); + + // listQuery() 为列表查询条件,与获取列表数据一致 + $query = $this->service->listQuery() + ->when($ids, fn($query) => $query->whereIn($this->service->primaryKey(), explode(',', $ids))); + + // 此处使用 laravel-excel 导出,可自行修改 + AdminExport::make($query) + ->setHeadings($this->exportHeadings()) + ->setMap(fn($row) => $this->exportColumns($row)) + ->store($path); + + return $this->response()->success(compact('path')); + } + protected function exportHeadings() { return [ @@ -182,13 +243,10 @@ class PatientRecordController extends AdminController __('patient-record.sell_price'), __('patient-record.order_status'), __('patient-record.doctor_id'), - __('patient-record.doctor_ratio'), __('patient-record.doctor_money'), __('patient-record.inviter_id'), - __('patient-record.inviter_ratio'), __('patient-record.inviter_money'), __('patient-record.saler_id'), - __('patient-record.saler_ratio'), __('patient-record.saler_money'), __('patient-record.creator_id'), __('patient-record.created_at'), @@ -206,13 +264,10 @@ class PatientRecordController extends AdminController $row->sell_price, $row->order_status->text(), data_get($row->doctor, 'name'), - $row->doctor_ratio, $row->doctor_money, data_get($row->inviter, 'name'), - $row->inviter_ratio, $row->inviter_money, data_get($row->saler, 'name'), - $row->saler_ratio, $row->saler_money, data_get($row->creator, 'name'), $row->created_at, diff --git a/app/Admin/Controllers/TotalRecordController.php b/app/Admin/Controllers/TotalRecordController.php index a7f710d..e5559b9 100644 --- a/app/Admin/Controllers/TotalRecordController.php +++ b/app/Admin/Controllers/TotalRecordController.php @@ -3,7 +3,7 @@ namespace App\Admin\Controllers; use Slowlyo\OwlAdmin\Controllers\AdminController; -use App\Models\{Patient, PatientRecord}; +use App\Models\{Keyword, Patient, PatientRecord}; use Illuminate\Support\Facades\DB; use App\Admin\Services\TotalRecordService; @@ -16,31 +16,33 @@ class TotalRecordController extends AdminController protected $patientOptions; + protected $typeOptions; + public function list() { $crud = $this->baseCRUD() ->filterTogglable(false) ->columnsTogglable(false) - ->headerToolbar([ - // amis('reload')->align('right'), - ]) + ->headerToolbar([]) ->filter($this->baseFilter()->actions()->body([ amisMake()->SelectControl()->options($this->getPatientOptions())->searchable()->name('patient_id')->label(__('patient-record.patient_id'))->size('md')->clearable(), + amisMake()->SelectControl()->options($this->getTypeOptions())->name('type_id')->label(__('patient-record.type_id'))->size('md')->clearable(), amisMake()->DateRangeControl()->name('treat_range')->label(__('total-record.treat_at'))->clearable()->size('md'), // amisMake()->Button()->label(__('admin.reset'))->actionType('clear-and-submit'), amisMake()->Component()->setType('submit')->label(__('admin.search'))->level('primary'), ])) ->columns([ amisMake()->Column()->name('patient.name')->label(__('total-record.name')), + amisMake()->Mapping()->map($this->getTypeOptions()->pluck('label', 'value'))->name('type_id')->label(__('patient-record.type_id')), amisMake()->Date()->name('min_treat_at')->label(__('total-record.min_treat_at'))->sortable(true), amisMake()->Date()->name('max_treat_at')->label(__('total-record.max_treat_at'))->sortable(true), amisMake()->Column()->name('count')->label(__('total-record.count'))->sortable(true), - amisMake()->Column()->name('origin_price')->label(__('total-record.origin_price'))->sortable(true), amisMake()->Column()->name('sell_price')->label(__('total-record.sell_price'))->sortable(true), - ])->affixRowClassName('text-info-dk')->affixRow([ - ['type' => 'text', 'text' => '总计', 'colSpan' => 3], - ['type' => 'text', 'text' => '记录数: ${total}'], - ['type' => 'text', 'text' => __('total-record.origin_price') . ': ${origin_price}'], + ]) + ->affixRowClassName('text-info-dk') + ->affixRow([ + ['type' => 'text', 'text' => '总计: ${total}', 'colSpan' => 4], + ['type' => 'text', 'text' => '看病次数: ${count}'], ['type' => 'text', 'text' => __('total-record.sell_price') . ': ${sell_price}'], ]) ->alwaysShowPagination(); @@ -56,5 +58,13 @@ class TotalRecordController extends AdminController return $this->patientOptions; } + public function getTypeOptions() + { + if (!$this->typeOptions) { + $this->typeOptions = Keyword::where('type_key', 'treat_type')->select(['id as value', 'name as label'])->get(); + } + + return $this->typeOptions; + } } diff --git a/app/Admin/Services/PatientRecordService.php b/app/Admin/Services/PatientRecordService.php index 1a17abd..4c25593 100644 --- a/app/Admin/Services/PatientRecordService.php +++ b/app/Admin/Services/PatientRecordService.php @@ -20,7 +20,6 @@ class PatientRecordService extends BaseService public function listQuery() { - $model = $this->getModel(); $filter = $this->getModelFilter(); $query = $this->query(); @@ -42,10 +41,12 @@ class PatientRecordService extends BaseService $list = $query->clone()->paginate(request()->input('perPage', 20)); $items = $list->items(); $total = $list->total(); - $origin_price = floatval($query->clone()->sum('origin_price')); $sell_price = floatval($query->clone()->sum('sell_price')); + $doctor_money = floatval($query->clone()->sum('doctor_money')); + $inviter_money = floatval($query->clone()->sum('inviter_money')); + $saler_money = floatval($query->clone()->sum('saler_money')); - return compact('items', 'total', 'sell_price', 'origin_price'); + return compact('items', 'total', 'sell_price', 'doctor_money', 'inviter_money', 'saler_money'); } public function resloveData($data, $model = null) @@ -57,34 +58,29 @@ class PatientRecordService extends BaseService if ($images = data_get($data, 'images')) { $data['images'] = is_array($images) ? $images : explode(',', $images); } - $patient = Patient::findOrFail(data_get($data, 'patient_id')); - $type = Keyword::findOrFail(data_get($data, 'type_id')); - - if (!data_get($data, 'inviter_id')) { - $data['inviter_id'] = $patient->inviter_id; - } - if (!data_get($data, 'saler_id')) { - $data['saler_id'] = $patient->saler_id; - } if (!$model) { + $patient = Patient::findOrFail(data_get($data, 'patient_id')); + $type = Keyword::findOrFail(data_get($data, 'type_id')); + $data['saler_id'] = $patient->saler_id; + $data['inviter_id'] = $patient->inviter_id; if (data_get($data, 'doctor_id')) { - $data['doctor_ratio'] = data_get($type->options, 'doctor_ratio'); - } - if (data_get($data, 'doctor_ratio')) { + $data['doctor_ratio'] = data_get($type->options, 'doctor_ratio', 0); $data['doctor_money'] = floor($data['sell_price'] * $data['doctor_ratio']) / 100; } if (data_get($data, 'inviter_id')) { - $data['inviter_ratio'] = data_get($type->options, 'inviter_ratio'); - } - if (data_get($data, 'inviter_ratio')) { + $data['inviter_ratio'] = data_get($type->options, 'inviter_ratio', 0); $data['inviter_money'] = floor($data['sell_price'] * $data['inviter_ratio']) / 100; } if (data_get($data, 'saler_id')) { - $data['saler_ratio'] = data_get($type->options, 'saler_ratio'); - } - if (data_get($data, 'saler_ratio')) { + $data['saler_ratio'] = data_get($type->options, 'saler_ratio', 0); $data['saler_money'] = floor($data['sell_price'] * $data['saler_ratio']) / 100; } + } else { + if (data_get($data, 'sell_price')) { + $data['doctor_money'] = floor($data['sell_price'] * $model->doctor_ratio) / 100; + $data['inviter_money'] = floor($data['sell_price'] * $model->inviter_ratio) / 100; + $data['saler_money'] = floor($data['sell_price'] * $model->saler_ratio) / 100; + } } return $data; diff --git a/app/Admin/Services/TotalRecordService.php b/app/Admin/Services/TotalRecordService.php index 65cefd4..3bc76a1 100644 --- a/app/Admin/Services/TotalRecordService.php +++ b/app/Admin/Services/TotalRecordService.php @@ -31,7 +31,6 @@ class TotalRecordService extends BaseService public function listQuery() { - $model = $this->getModel(); $filter = $this->getModelFilter(); $query = $this->query(); @@ -45,12 +44,12 @@ class TotalRecordService extends BaseService $query->select([ 'patient_id', + 'type_id', DB::raw('count(1) as count'), - DB::raw('sum(`origin_price`) as `origin_price`'), DB::raw('sum(`sell_price`) as `sell_price`'), DB::raw('min(`treat_at`) as `min_treat_at`'), DB::raw('max(`treat_at`) as `max_treat_at`'), - ])->groupBy('patient_id'); + ])->groupBy('patient_id', 'type_id'); $this->sortable($query); @@ -64,9 +63,9 @@ class TotalRecordService extends BaseService $list = (clone $query)->paginate(request()->input('perPage', 20)); $items = $list->items(); $total = $list->total(); - $origin_price = floatval((new PatientRecordService())->listQuery()->sum('origin_price')); + $count = floatval((new PatientRecordService())->listQuery()->count()); $sell_price = floatval((new PatientRecordService())->listQuery()->sum('sell_price')); - return compact('items', 'total', 'sell_price', 'origin_price'); + return compact('items', 'total', 'sell_price', 'count'); } } diff --git a/app/Casts/StorageFile.php b/app/Casts/StorageFile.php index 4511279..e118550 100644 --- a/app/Casts/StorageFile.php +++ b/app/Casts/StorageFile.php @@ -32,7 +32,7 @@ class StorageFile implements CastsAttributes */ public function get($model, $key, $value, $attributes) { - return $value ? (Str::startsWith($value, ['http://', 'https://']) ? $value : Storage::disk($this->disk)->url($value)) : ''; + return $value ? (Str::startsWith($value, ['http://', 'https://']) ? $value : Storage::disk($this->disk)->url($value)) : null; } /** diff --git a/config/admin.php b/config/admin.php index 83fbfc4..81d684b 100644 --- a/config/admin.php +++ b/config/admin.php @@ -37,7 +37,7 @@ return [ 'controller' => \Slowlyo\OwlAdmin\Controllers\AuthController::class, 'guard' => 'sanctum', 'except' => [ - + 'system/admin_users' ], ], diff --git a/database/factories/AdminUserFactory.php b/database/factories/AdminUserFactory.php index d193a19..674087d 100644 --- a/database/factories/AdminUserFactory.php +++ b/database/factories/AdminUserFactory.php @@ -24,6 +24,7 @@ class AdminUserFactory extends Factory 'username' => $this->faker->username, 'name' => $name, 'avatar' => 'https://ui-avatars.com/api/?name=' . $name, + // admin 'password' => '$2y$10$MXFSBvoV02d.nMg/5//5ru/os27JgSJhsePidDr/uiIw6UQ.MigP2', ]; } diff --git a/database/factories/PatientFactory.php b/database/factories/PatientFactory.php index 6ff3574..d58d23b 100644 --- a/database/factories/PatientFactory.php +++ b/database/factories/PatientFactory.php @@ -5,6 +5,7 @@ namespace Database\Factories; use Illuminate\Database\Eloquent\Factories\Factory; use App\Models\Patient; use App\Enums\Gender; +use Slowlyo\OwlAdmin\Models\AdminUser; /** * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Patient> @@ -12,7 +13,7 @@ use App\Enums\Gender; class PatientFactory extends Factory { protected $model = Patient::class; - + /** * Define the model's default state. * @@ -29,7 +30,9 @@ class PatientFactory extends Factory 'birthday' => $faker->dateTimeBetween('-30 years', '-10 years'), 'treat_at' => $faker->dateTimeBetween('-7 days'), 'illness' => '基本稳定', - 'doctor_id' => 1, + 'doctor_id' => AdminUser::inRandomOrder()->value('id'), + 'inviter_id' => AdminUser::inRandomOrder()->value('id'), + 'saler_id' => AdminUser::inRandomOrder()->value('id'), ]; } } diff --git a/database/factories/PatientRecordFactory.php b/database/factories/PatientRecordFactory.php index 7257b53..5161ec4 100644 --- a/database/factories/PatientRecordFactory.php +++ b/database/factories/PatientRecordFactory.php @@ -6,7 +6,6 @@ use Illuminate\Database\Eloquent\Factories\Factory; use App\Models\{PatientRecord, Patient, Keyword}; use Slowlyo\OwlAdmin\Models\AdminUser; use App\Enums\OrderStatus; -use Slowlyo\OwlAdmin\Models\AdminUser; /** * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\PatientRecord> @@ -23,11 +22,25 @@ class PatientRecordFactory extends Factory public function definition(): array { $faker = $this->faker; + $sell_price = $faker->numberBetween(100, 200); + $patient = Patient::inRandomOrder()->first(); + $type = Keyword::where('type_key', 'treat_type')->inRandomOrder()->first(); + $doctor_ratio = data_get($type->options, 'doctor_ratio', 0); + $inviter_ratio = data_get($type->options, 'inviter_ratio', 0); + $saler_ratio = data_get($type->options, 'saler_ratio', 0); return [ - 'patient_id' => Patient::inRandomOrder()->value('id'), - 'type_id' => Keyword::where('type_key', 'treat_type')->inRandomOrder()->value('id'), + 'patient_id' => $patient->id, + 'type_id' => $type->id, 'treat_at' => $faker->dateTimeBetween('-7 days'), 'doctor_id' => AdminUser::inRandomOrder()->value('id'), + 'doctor_ratio' => $doctor_ratio, + 'doctor_money' => floor($sell_price * $doctor_ratio) / 100, + 'inviter_id' => $patient->inviter_id, + 'inviter_ratio' => $inviter_ratio, + 'inviter_money' => $patient->inviter_id ? floor($sell_price * $inviter_ratio) / 100 : 0, + 'saler_id' => $patient->saler_id, + 'saler_ratio' => $saler_ratio, + 'saler_money' => $patient->saler_id ? floor($sell_price * $saler_ratio) / 100 : 0, 'content' => '逐渐恢复', 'origin_price' => $faker->numberBetween(200, 300), 'sell_price' => $faker->numberBetween(100, 200), @@ -37,7 +50,7 @@ class PatientRecordFactory extends Factory 'notify_remarks' => '带上医保卡', 'is_notified' => 0, 'next_treat_at' => $faker->dateTimeBetween('now', '+7 days'), - 'creator_id' => 1 + 'creator_id' => 1, ]; } } diff --git a/lang/zh_CN/category.php b/lang/zh_CN/category.php index f29fc48..332b61d 100644 --- a/lang/zh_CN/category.php +++ b/lang/zh_CN/category.php @@ -11,7 +11,11 @@ return [ 'sort' => '排序(倒序)', 'level' => '层级', 'data' => '扩展', + 'options' => '扩展', 'image' => '图片', 'description' => '描述', 'content' => '内容', + 'doctor_ratio' => '医生提成比例(%)', + 'saler_ratio' => '业务员提成比例(%)', + 'inviter_ratio' => '推荐人提成比例(%)', ]; diff --git a/lang/zh_CN/patient-record.php b/lang/zh_CN/patient-record.php index 12f968a..dbb97e5 100644 --- a/lang/zh_CN/patient-record.php +++ b/lang/zh_CN/patient-record.php @@ -8,13 +8,13 @@ return [ 'admin_user_id' => '相关人员', 'doctor_id' => '就诊医生', 'doctor_ratio' => '医生提成比例(%)', - 'doctor_money' => '医生提成金额', + 'doctor_money' => '医生提成', 'saler_id' => '业务员', 'saler_ratio' => '业务员提成比例(%)', - 'saler_money' => '业务员提成金额', + 'saler_money' => '业务员提成', 'inviter_id' => '推荐人', 'inviter_ratio' => '推荐人提成比例(%)', - 'inviter_money' => '推荐人提成金额', + 'inviter_money' => '推荐人提成', 'content' => '诊疗情况', 'origin_price' => '划线价', 'sell_price' => '实收价', diff --git a/lang/zh_CN/total-record.php b/lang/zh_CN/total-record.php index bb7855d..aca4abc 100644 --- a/lang/zh_CN/total-record.php +++ b/lang/zh_CN/total-record.php @@ -6,6 +6,6 @@ return [ 'min_treat_at' => '开始时间', 'max_treat_at' => '结束时间', 'count' => '看病次数', - 'origin_price' => '划价总金额', - 'sell_price' => '实收总金额', + 'origin_price' => '划价', + 'sell_price' => '实收', ];