1
0
Fork 0

admin recorx

master
panliang 2023-10-08 15:25:52 +08:00
parent 6fa9d49bad
commit ea79dd81f1
13 changed files with 152 additions and 81 deletions

View File

@ -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(),
]);
}

View File

@ -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 = <<<JS
doAction([
{ actionType: "ajax", args: { api: { url: url.toString(), method: "get" } } },
{
actionType: "custom",
expression: "\${event.data.responseResult.responseStatus === 0}",
script: "window.open('{$downloadPath}?path='+event.data.responseResult.responseData.path)"
}
])
JS;
$buttons = [
amisMake()->VanillaAction()->label(__('admin.export.all'))->onEvent(
$event(<<<JS
let url = new URL("{$exportPath}", window.location.origin)
let param = window.location.href.split('?')[1]
if (param) {
url = url + '&' + param
}
{$doAction}
JS
)
),
];
return amisMake()
->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,

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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');
}
}

View File

@ -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;
}
/**

View File

@ -37,7 +37,7 @@ return [
'controller' => \Slowlyo\OwlAdmin\Controllers\AuthController::class,
'guard' => 'sanctum',
'except' => [
'system/admin_users'
],
],

View File

@ -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',
];
}

View File

@ -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'),
];
}
}

View File

@ -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,
];
}
}

View File

@ -11,7 +11,11 @@ return [
'sort' => '排序(倒序)',
'level' => '层级',
'data' => '扩展',
'options' => '扩展',
'image' => '图片',
'description' => '描述',
'content' => '内容',
'doctor_ratio' => '医生提成比例(%)',
'saler_ratio' => '业务员提成比例(%)',
'inviter_ratio' => '推荐人提成比例(%)',
];

View File

@ -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' => '实收价',

View File

@ -6,6 +6,6 @@ return [
'min_treat_at' => '开始时间',
'max_treat_at' => '结束时间',
'count' => '看病次数',
'origin_price' => '划价总金额',
'sell_price' => '实收总金额',
'origin_price' => '划价',
'sell_price' => '实收',
];