diff --git a/app/Admin/Controllers/Train/BookController.php b/app/Admin/Controllers/Train/BookController.php new file mode 100644 index 0000000..49d569b --- /dev/null +++ b/app/Admin/Controllers/Train/BookController.php @@ -0,0 +1,95 @@ +baseCRUD() + ->tableLayout('fixed') + ->headerToolbar([ + $this->createTypeButton('drawer', 'xl')->visible(Admin::user()->can('admin.train.books.create')), + ...$this->baseHeaderToolBar(), + ]) + ->bulkActions([]) + ->filter($this->baseFilter()->body([ + amis()->GroupControl()->mode('horizontal')->body([ + amisMake()->SelectControl()->name('category_id')->label(__('train_book.category_id')) + ->source(admin_url('api/keywords/tree-list?parent_key=book_category')) + ->labelField('name') + ->valueField('key') + ->columnRatio(3) + ->clearable(), + amisMake()->TextControl()->name('search')->label(__('train_book.title'))->columnRatio(3)->clearable(), + ]), + ])) + ->columns([ + amisMake()->TableColumn()->name('id')->label(__('train_book.id')), + amisMake()->TableColumn()->name('category.name')->label(__('train_book.category_id')), + amisMake()->TableColumn()->name('title')->label(__('train_book.title')), + amisMake()->TableColumn()->name('description')->label(__('train_book.description')), + amisMake()->TableColumn()->name('cover_image')->label(__('train_book.cover_image'))->set('type', 'image')->set('width', '80px')->set('height', '60px'), + amisMake()->TableColumn()->name('created_at')->label(__('train_book.created_at')), + $this->rowActions([ + $this->rowShowButton()->visible(Admin::user()->can('admin.train.books.view')), + $this->rowEditButton()->visible(Admin::user()->can('admin.train.books.update')), + $this->rowDeleteButton()->visible(Admin::user()->can('admin.train.books.delete')), + ]), + ]); + + return $this->baseList($crud); + } + + public function form($edit): Form + { + return $this->baseForm()->data(['type' => BookType::Text->value])->title('')->body([ + amisMake()->SelectControl()->name('category_id')->label(__('train_book.category_id')) + ->source(admin_url('api/keywords/tree-list?parent_key=book_category')) + ->labelField('name') + ->valueField('key') + ->required(), + amisMake()->TextControl()->name('title')->label(__('train_book.title'))->required(), + amisMake()->ImageControl()->name('cover_image')->label(__('train_book.cover_image')), + amisMake()->TextControl()->name('description')->label(__('train_book.description')), + amisMake()->RadiosControl()->options(BookType::options())->name('type')->label(__('train_book.type')), + Components::make()->fuEditorControl('content', __('train_book.content'))->visibleOn('${type == '.BookType::Text->value.'}'), + amisMake()->FileControl()->name('video')->label(__('train_book.video'))->visibleOn('${type == '.BookType::Video->value.'}'), + amisMake()->FileControl()->multiple()->joinValues(false)->name('files')->label(__('train_book.files'))->visibleOn('${type == '.BookType::File->value.'}'), + ]); + } + + public function detail(): Form + { + $list = amisMake()->ListRenderer()->source('${files}')->listItem( + amisMake()->ListItem()->body( + amisMake()->Link()->body('${name}')->href('${url}')->blank() + ) + ); + return $this->baseDetail()->title('')->body(amisMake()->Property()->items([ + ['label' => __('train_book.category_id'), 'content' => '${category.name}'], + ['label' => __('train_book.title'), 'content' => '${title}'], + ['label' => __('train_book.description'), 'content' => '${description}'], + ['label' => __('train_book.cover_image'), 'content' => amisMake()->Image()->name('cover_image')->width('80px')->height('60px')], + ['label' => __('train_book.type'), 'content' => amisMake()->Mapping()->map(BookType::options())->name('type')], + ['label' => __('train_book.created_at'), 'content' => '${created_at}'], + ['label' => __('train_book.content'), 'content' => Components::make()->fuEditorControl('content', false, 'auto')->static(), 'span' => 3], + ['label' => __('train_book.video'), 'content' => amisMake()->Video()->src('${video}'), 'span' => 3], + // amisMake()->Each()->name('files')->items(amisMake()->Link()->body('${name}')->href('${url}')->blank()) + ['label' => __('train_book.files'), 'content' => $list, 'span' => 3], + ])); + } +} diff --git a/app/Admin/Filters/TrianBookFilter.php b/app/Admin/Filters/TrianBookFilter.php new file mode 100644 index 0000000..521a521 --- /dev/null +++ b/app/Admin/Filters/TrianBookFilter.php @@ -0,0 +1,28 @@ +where(fn ($q) => $q->where('title', 'like', $condition)->orWhere('description', 'like', $condition)); + } + + public function category($key) + { + return $this->where('category_id', $key); + } + + public function dateRange($dates) + { + $dates = explode(',', $dates); + $start = Carbon::createFromTimestamp(data_get($dates, 0, time()))->startOfDay(); + $end = Carbon::createFromTimestamp(data_get($dates, 1, time()))->endOfDay(); + $this->whereBetween('created_at', [$start, $end]); + } +} diff --git a/app/Admin/Services/Train/BookService.php b/app/Admin/Services/Train/BookService.php new file mode 100644 index 0000000..ee7f108 --- /dev/null +++ b/app/Admin/Services/Train/BookService.php @@ -0,0 +1,58 @@ + ['required'], + 'catgeory_id' => ['required'], + 'type' => ['required'], + 'files' => ['array'], + ]; + $updateRules = [ + 'files' => ['array'], + ]; + $validator = Validator::make($data, $model ? $updateRules : $createRules); + if ($validator->fails()) { + return $validator->errors()->first(); + } + return true; + } +} diff --git a/app/Admin/routes.php b/app/Admin/routes.php index bc79b32..db182c8 100644 --- a/app/Admin/routes.php +++ b/app/Admin/routes.php @@ -181,6 +181,15 @@ Route::group([ $router->resource('ads', \App\Admin\Controllers\AdController::class); + /* + |-------------------------------------------------------------------------- + | 培训管理 + |-------------------------------------------------------------------------- + */ + $router->group(['prefix' => 'train', 'as' => 'train.'], function (Router $router) { + $router->resource('books', \App\Admin\Controllers\Train\BookController::class); + }); + $router->post('agreement/download', [AgreementController::class, 'download'])->name('agreement.download'); $router->resource('agreement', AgreementController::class); diff --git a/app/Enums/BookType.php b/app/Enums/BookType.php new file mode 100644 index 0000000..ef4ab1a --- /dev/null +++ b/app/Enums/BookType.php @@ -0,0 +1,36 @@ +value => '文章', + self::Video->value => '视频', + self::File->value => '附件', + ]; + } + + public function text() + { + return data_get(self::options(), $this->value); + } +} diff --git a/app/Enums/QuestionCate.php b/app/Enums/QuestionCate.php new file mode 100644 index 0000000..8cfb987 --- /dev/null +++ b/app/Enums/QuestionCate.php @@ -0,0 +1,32 @@ +value => '单选', + self::Checkbox->value => '多选', + ]; + } + + public function text() + { + return data_get(self::options(), $this->value); + } +} diff --git a/app/Models/Train/Book.php b/app/Models/Train/Book.php new file mode 100644 index 0000000..aee9977 --- /dev/null +++ b/app/Models/Train/Book.php @@ -0,0 +1,34 @@ + \App\Enums\BookType::class, + 'files' => 'json', + ]; + + public function modelFilter() + { + return \App\Admin\Filters\TrianBookFilter::class; + } + + public function category() + { + return $this->belongsTo(\App\Models\Keyword::class, 'category_id', 'key'); + } +} diff --git a/database/migrations/2024_04_09_123559_create_train_table.php b/database/migrations/2024_04_09_123559_create_train_table.php new file mode 100644 index 0000000..eb9952e --- /dev/null +++ b/database/migrations/2024_04_09_123559_create_train_table.php @@ -0,0 +1,85 @@ +id(); + $table->string('category_id')->comment('分类(book_category), keywords.key'); + $table->string('title')->comment('标题'); + $table->string('cover_image')->nullable()->comment('封面图'); + $table->string('description')->nullable()->comment('描述'); + $table->unsignedInteger('type')->default(BookType::Text->value)->comment('课件类型'); + $table->text('content')->nullable()->comment('文章内容'); + $table->json('video')->nullable()->comment('视频'); + $table->json('files')->nullable()->comment('附件'); + $table->timestamps(); + + $table->comment('培训-课件'); + }); + + Schema::create('train_question_banks', function (Blueprint $table) { + $table->id(); + $table->string('name')->comment('题库名称'); + $table->string('remarks')->nullable()->comment('备注'); + $table->timestamps(); + + $table->comment('培训-题库'); + }); + + Schema::create('train_questions', function (Blueprint $table) { + $table->id(); + $table->foreignId('bank_id')->comment('题库'); + $table->text('title')->comment('题目'); + $table->tinyInteger('cate')->default(QuestionCate::Radio->value)->comment('类型'); + $table->json('options')->nullable()->comment('选项[{text: 选项1, is_true: 是否正确答案}]'); + + $table->timestamps(); + $table->comment('培训-考题'); + }); + + Schema::create('train_examinations', function (Blueprint $table) { + $table->id(); + $table->string('name')->comment('考试名称'); + $table->json('questions')->comment('考题内容[{title: 题目, cate: 类型, options: [选项1, 选项2], score: 分值, answer: 正确选项}]'); + $table->string('remarks')->nullable()->comment('备注'); + $table->timestamps(); + + $table->comment('培训-考试记录'); + }); + + Schema::create('train_examination_papers', function ($table) { + $table->id(); + $table->foreignId('employee_id')->comment('考生'); + $table->foreignId('examination_id')->comment('考试, train_examinations.id'); + $table->json('content')->comment('考卷内容[{title: 题目, cate: 类型, options: [选项1, 选项2], score: 分值, answer: 正确选项, user_score: 得分, user_answer: 回答}]'); + $table->decimal('mark', 12, 2)->nullable()->comment('分数'); + $table->timestamp('start_at')->nullable()->comment('答题开始时间'); + $table->timestamp('end_at')->nullable()->comment('答题结束时间'); + $table->timestamps(); + + $table->comment('培训-考卷记录'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('train_books'); + Schema::dropIfExists('train_questions'); + Schema::dropIfExists('train_question_banks'); + Schema::dropIfExists('train_examination_papers'); + Schema::dropIfExists('train_examinations'); + } +}; diff --git a/database/seeders/AdminPermissionSeeder.php b/database/seeders/AdminPermissionSeeder.php index 8030430..d331322 100644 --- a/database/seeders/AdminPermissionSeeder.php +++ b/database/seeders/AdminPermissionSeeder.php @@ -250,6 +250,19 @@ class AdminPermissionSeeder extends Seeder ], ], + 'train' => [ + 'name' => '培训管理', + 'icon' => '', + 'uri' => '/train', + 'children' => [ + 'book' => [ + 'name' => '课件管理', + 'icon' => '', + 'uri' => '/train/books', + 'resource' => true, + ] + ] + ], 'agreement' => [ 'name' => '合同管理', 'icon' => '', diff --git a/database/seeders/KeywordSeeder.php b/database/seeders/KeywordSeeder.php index 48a8f6c..a36b981 100644 --- a/database/seeders/KeywordSeeder.php +++ b/database/seeders/KeywordSeeder.php @@ -94,6 +94,11 @@ class KeywordSeeder extends Seeder 'name' => '报销类型', 'children' => ['门店日常支出', '门店活动支出', '日常费用', '器材费', '差旅费', '车辆类报销', '会议费'], ], + [ + 'key' => 'book_category', + 'name' => '课件分类', + 'children' => ['专业知识', '服务知识', '专业技巧', '企业文化'], + ] ]; $this->insertKeywors($keywords); diff --git a/lang/zh_CN/train_book.php b/lang/zh_CN/train_book.php new file mode 100644 index 0000000..9f0893e --- /dev/null +++ b/lang/zh_CN/train_book.php @@ -0,0 +1,16 @@ + 'ID', + 'created_at' => '创建时间', + 'updated_at' => '更新时间', + + 'title' => '标题', + 'category_id' => '分类', + 'cover_image' => '封面图', + 'description' => '描述', + 'type' => '类型', + 'content' => '内容', + 'video' => '视频', + 'files' => '附件', +];