From 58f42de9df002269473647d6d2db07ae57e2eb7a Mon Sep 17 00:00:00 2001 From: 7IN0SAN9 Date: Fri, 13 Mar 2026 14:32:37 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E4=BF=AE=E5=A4=8D=E7=9F=A5?= =?UTF-8?q?=E8=AF=86=E5=BA=93=E5=92=8C=E6=93=8D=E4=BD=9C=E6=8C=87=E5=BC=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Exports/ActivityLogExport.php | 2 +- .../Actions/ArchiveSopTemplateAction.php | 50 ---- .../Actions/ExportSopTemplateAction.php | 51 ---- .../Actions/ImportSopTemplateAction.php | 80 ------ .../Actions/PreviewSopTemplateAction.php | 90 ------ .../Actions/PublishSopTemplateAction.php | 85 ------ app/Filament/Pages/SearchPage.php | 18 +- .../Resources/ActivityLogResource.php | 4 +- .../Pages/ViewActivityLog.php | 2 +- app/Filament/Resources/GuideResource.php | 204 +++++++++++++ .../GuideResource/Pages/CreateGuide.php | 22 ++ .../GuideResource/Pages/EditGuide.php | 32 +++ .../GuideResource/Pages/ListGuides.php | 20 ++ .../GuideResource/Pages/ManageGuidePages.php | 88 ++++++ app/Filament/Resources/RoleResource.php | 4 +- .../Resources/RoleResource/Pages/ViewRole.php | 2 +- .../Resources/SopTemplateResource.php | 269 ------------------ .../Pages/CreateSopTemplate.php | 23 -- .../Pages/EditSopTemplate.php | 28 -- .../Pages/ListSopTemplates.php | 22 -- .../Pages/ViewSopTemplate.php | 99 ------- app/Filament/Resources/TerminalResource.php | 80 +++++- app/Filament/Resources/UserResource.php | 4 +- .../Resources/UserResource/Pages/ViewUser.php | 4 +- .../Controllers/Api/TerminalApiController.php | 227 +++++++++++++++ app/Http/Middleware/IdentifyTerminal.php | 38 +++ app/Models/Document.php | 10 + app/Models/Guide.php | 75 +++++ app/Models/GuidePage.php | 58 ++++ app/Models/KnowledgeBase.php | 10 + app/Models/SopInteractiveTask.php | 46 --- app/Models/SopStep.php | 54 ---- app/Models/SopTemplate.php | 90 ------ app/Models/SopTemplateVersion.php | 62 ---- app/Models/Terminal.php | 16 ++ app/Policies/GuidePolicy.php | 81 ++++++ app/Policies/SopTemplatePolicy.php | 95 ------- app/Providers/AppServiceProvider.php | 6 +- app/Services/DocumentSearchService.php | 5 + app/Services/KnowledgeContextService.php | 94 ++++++ app/Services/SopTemplateService.php | 222 --------------- bootstrap/app.php | 5 +- composer.json | 1 + composer.lock | 86 +++++- config/prompt_templates.php | 87 ++++-- config/scout.php | 4 +- database/factories/SopTemplateFactory.php | 61 ---- ...26_03_02_014623_create_terminals_table.php | 2 +- ...3_02_015106_create_sop_templates_table.php | 42 --- ...26_03_02_015107_create_sop_steps_table.php | 42 --- ...108_create_sop_interactive_tasks_table.php | 39 --- ...109_create_sop_template_versions_table.php | 41 --- ...03_12_051332_update_permissions_naming.php | 6 +- .../2026_03_13_010100_create_guides_table.php | 60 ++++ .../2026_03_13_010200_add_terminal_fields.php | 27 ++ ...d_knowledge_base_id_to_documents_table.php | 29 ++ database/seeders/DatabaseSeeder.php | 12 +- database/seeders/GuideSeeder.php | 219 ++++++++++++++ database/seeders/PermissionSeeder.php | 32 +-- database/seeders/SopTemplateSeeder.php | 237 --------------- database/seeders/TerminalSeeder.php | 8 +- package-lock.json | 214 +++++++------- .../filament-tree/filament-tree-min.css | 1 + public/guides/how-to-use-beam/step-1.html | 84 ++++++ public/guides/how-to-use-beam/step-2.html | 92 ++++++ public/guides/how-to-use-beam/step-3a.html | 72 +++++ public/guides/how-to-use-beam/step-3b.html | 91 ++++++ public/guides/how-to-use-beam/step-4a.html | 65 +++++ public/guides/how-to-use-beam/step-4b.html | 72 +++++ public/guides/how-to-use-beam/step-5.html | 94 ++++++ public/guides/vacuum-valve-issue/step-1.html | 98 +++++++ public/guides/vacuum-valve-issue/step-2.html | 89 ++++++ public/guides/vacuum-valve-issue/step-3.html | 108 +++++++ public/guides/vacuum-valve-issue/step-4.html | 96 +++++++ public/guides/vacuum-valve-issue/step-5.html | 123 ++++++++ public/guides/water-leak-alarm/step-1.html | 115 ++++++++ public/guides/water-leak-alarm/step-2.html | 94 ++++++ public/guides/water-leak-alarm/step-3.html | 109 +++++++ public/guides/water-leak-alarm/step-4.html | 108 +++++++ public/guides/water-leak-alarm/step-5.html | 71 +++++ .../components/filament-tree-component.js | 25 ++ public/scada/beamline-background.png | Bin 0 -> 251897 bytes public/scada/demo.html | 52 ++++ routes/api.php | 12 + tests/Feature/SopTemplateServiceTest.php | 246 ---------------- tests/Feature/SopTemplateTest.php | 200 ------------- tests/Feature/TerminalResourceTest.php | 10 +- tests/Feature/TerminalSyncTest.php | 6 +- 88 files changed, 3387 insertions(+), 2472 deletions(-) delete mode 100644 app/Filament/Actions/ArchiveSopTemplateAction.php delete mode 100644 app/Filament/Actions/ExportSopTemplateAction.php delete mode 100644 app/Filament/Actions/ImportSopTemplateAction.php delete mode 100644 app/Filament/Actions/PreviewSopTemplateAction.php delete mode 100644 app/Filament/Actions/PublishSopTemplateAction.php create mode 100644 app/Filament/Resources/GuideResource.php create mode 100644 app/Filament/Resources/GuideResource/Pages/CreateGuide.php create mode 100644 app/Filament/Resources/GuideResource/Pages/EditGuide.php create mode 100644 app/Filament/Resources/GuideResource/Pages/ListGuides.php create mode 100644 app/Filament/Resources/GuideResource/Pages/ManageGuidePages.php delete mode 100644 app/Filament/Resources/SopTemplateResource.php delete mode 100644 app/Filament/Resources/SopTemplateResource/Pages/CreateSopTemplate.php delete mode 100644 app/Filament/Resources/SopTemplateResource/Pages/EditSopTemplate.php delete mode 100644 app/Filament/Resources/SopTemplateResource/Pages/ListSopTemplates.php delete mode 100644 app/Filament/Resources/SopTemplateResource/Pages/ViewSopTemplate.php create mode 100644 app/Http/Controllers/Api/TerminalApiController.php create mode 100644 app/Http/Middleware/IdentifyTerminal.php create mode 100644 app/Models/Guide.php create mode 100644 app/Models/GuidePage.php delete mode 100644 app/Models/SopInteractiveTask.php delete mode 100644 app/Models/SopStep.php delete mode 100644 app/Models/SopTemplate.php delete mode 100644 app/Models/SopTemplateVersion.php create mode 100644 app/Policies/GuidePolicy.php delete mode 100644 app/Policies/SopTemplatePolicy.php create mode 100644 app/Services/KnowledgeContextService.php delete mode 100644 app/Services/SopTemplateService.php delete mode 100644 database/factories/SopTemplateFactory.php delete mode 100644 database/migrations/2026_03_02_015106_create_sop_templates_table.php delete mode 100644 database/migrations/2026_03_02_015107_create_sop_steps_table.php delete mode 100644 database/migrations/2026_03_02_015108_create_sop_interactive_tasks_table.php delete mode 100644 database/migrations/2026_03_02_015109_create_sop_template_versions_table.php create mode 100644 database/migrations/2026_03_13_010100_create_guides_table.php create mode 100644 database/migrations/2026_03_13_010200_add_terminal_fields.php create mode 100644 database/migrations/2026_03_13_060000_add_knowledge_base_id_to_documents_table.php create mode 100644 database/seeders/GuideSeeder.php delete mode 100644 database/seeders/SopTemplateSeeder.php create mode 100644 public/css/solution-forest/filament-tree/filament-tree-min.css create mode 100644 public/guides/how-to-use-beam/step-1.html create mode 100644 public/guides/how-to-use-beam/step-2.html create mode 100644 public/guides/how-to-use-beam/step-3a.html create mode 100644 public/guides/how-to-use-beam/step-3b.html create mode 100644 public/guides/how-to-use-beam/step-4a.html create mode 100644 public/guides/how-to-use-beam/step-4b.html create mode 100644 public/guides/how-to-use-beam/step-5.html create mode 100644 public/guides/vacuum-valve-issue/step-1.html create mode 100644 public/guides/vacuum-valve-issue/step-2.html create mode 100644 public/guides/vacuum-valve-issue/step-3.html create mode 100644 public/guides/vacuum-valve-issue/step-4.html create mode 100644 public/guides/vacuum-valve-issue/step-5.html create mode 100644 public/guides/water-leak-alarm/step-1.html create mode 100644 public/guides/water-leak-alarm/step-2.html create mode 100644 public/guides/water-leak-alarm/step-3.html create mode 100644 public/guides/water-leak-alarm/step-4.html create mode 100644 public/guides/water-leak-alarm/step-5.html create mode 100644 public/js/solution-forest/filament-tree/components/filament-tree-component.js create mode 100644 public/scada/beamline-background.png create mode 100644 public/scada/demo.html create mode 100644 routes/api.php delete mode 100644 tests/Feature/SopTemplateServiceTest.php delete mode 100644 tests/Feature/SopTemplateTest.php diff --git a/app/Exports/ActivityLogExport.php b/app/Exports/ActivityLogExport.php index faa16bb..8ede505 100644 --- a/app/Exports/ActivityLogExport.php +++ b/app/Exports/ActivityLogExport.php @@ -65,7 +65,7 @@ class ActivityLogExport implements FromQuery, WithHeadings, WithMapping, WithSty 'Document' => '文档', 'Group' => '分组', 'Terminal' => '终端', - 'SopTemplate' => 'SOP模板', + 'Guide' => '操作指引', default => $className, }; } diff --git a/app/Filament/Actions/ArchiveSopTemplateAction.php b/app/Filament/Actions/ArchiveSopTemplateAction.php deleted file mode 100644 index 87caeac..0000000 --- a/app/Filament/Actions/ArchiveSopTemplateAction.php +++ /dev/null @@ -1,50 +0,0 @@ -label('归档模板'); - - $this->icon('heroicon-o-archive-box'); - - $this->color('warning'); - - $this->requiresConfirmation(); - - $this->modalHeading('归档 SOP 模板'); - - $this->modalDescription('归档后,模板将不再对用户显示。确定要归档吗?'); - - $this->modalSubmitActionLabel('确认归档'); - - $this->visible(function ($record) { - return $record instanceof SopTemplate && $record->status === 'published'; - }); - - $this->action(function (SopTemplate $record) { - $record->update([ - 'status' => 'archived', - ]); - - Notification::make() - ->title('归档成功') - ->body('SOP 模板已成功归档') - ->success() - ->send(); - }); - } -} diff --git a/app/Filament/Actions/ExportSopTemplateAction.php b/app/Filament/Actions/ExportSopTemplateAction.php deleted file mode 100644 index 4584869..0000000 --- a/app/Filament/Actions/ExportSopTemplateAction.php +++ /dev/null @@ -1,51 +0,0 @@ -label('导出模板'); - - $this->icon('heroicon-o-arrow-down-tray'); - - $this->color('info'); - - $this->action(function (SopTemplate $record) { - $service = app(SopTemplateService::class); - $json = $service->exportToJson($record); - - $fileName = sprintf( - 'sop_template_%s_%s.json', - $record->id, - now()->format('YmdHis') - ); - - Notification::make() - ->title('导出成功') - ->body('SOP 模板已成功导出') - ->success() - ->send(); - - return Response::streamDownload(function () use ($json) { - echo $json; - }, $fileName, [ - 'Content-Type' => 'application/json', - ]); - }); - } -} diff --git a/app/Filament/Actions/ImportSopTemplateAction.php b/app/Filament/Actions/ImportSopTemplateAction.php deleted file mode 100644 index 141a228..0000000 --- a/app/Filament/Actions/ImportSopTemplateAction.php +++ /dev/null @@ -1,80 +0,0 @@ -label('导入模板'); - - $this->icon('heroicon-o-arrow-up-tray'); - - $this->color('success'); - - $this->modalHeading('导入 SOP 模板'); - - $this->modalDescription('从 JSON 文件导入 SOP 模板'); - - $this->modalSubmitActionLabel('导入'); - - $this->form([ - Forms\Components\FileUpload::make('file') - ->label('选择文件') - ->acceptedFileTypes(['application/json']) - ->required() - ->maxSize(5120), // 5MB - ]); - - $this->action(function (array $data) { - try { - $filePath = storage_path('app/public/' . $data['file']); - $json = file_get_contents($filePath); - - $service = app(SopTemplateService::class); - $template = $service->importFromJson($json); - - // 删除临时文件 - @unlink($filePath); - - Notification::make() - ->title('导入成功') - ->body("SOP 模板「{$template->name}」已成功导入") - ->success() - ->send(); - - // 重定向到编辑页面 - return redirect()->route('filament.admin.resources.sop-templates.edit', ['record' => $template]); - } catch (ValidationException $e) { - Notification::make() - ->title('导入失败') - ->body($e->getMessage()) - ->danger() - ->send(); - - throw $e; - } catch (\Exception $e) { - Notification::make() - ->title('导入失败') - ->body('文件格式错误或数据无效') - ->danger() - ->send(); - - throw $e; - } - }); - } -} diff --git a/app/Filament/Actions/PreviewSopTemplateAction.php b/app/Filament/Actions/PreviewSopTemplateAction.php deleted file mode 100644 index 34340ab..0000000 --- a/app/Filament/Actions/PreviewSopTemplateAction.php +++ /dev/null @@ -1,90 +0,0 @@ -label('预览模板'); - - $this->icon('heroicon-o-eye'); - - $this->color('info'); - - $this->modalHeading('SOP 模板预览'); - - $this->modalWidth('7xl'); - - $this->modalSubmitAction(false); - - $this->modalCancelActionLabel('关闭'); - - $this->infolist(function (SopTemplate $record): Infolist { - return Infolist::make() - ->record($record) - ->schema([ - Infolists\Components\Section::make('模板信息') - ->schema([ - Infolists\Components\TextEntry::make('name') - ->label('模板名称') - ->size(Infolists\Components\TextEntry\TextEntrySize::Large) - ->weight('bold'), - - Infolists\Components\TextEntry::make('description') - ->label('模板描述') - ->columnSpanFull(), - - Infolists\Components\TextEntry::make('category') - ->label('分类') - ->badge(), - - Infolists\Components\TextEntry::make('version') - ->label('版本'), - ]) - ->columns(2), - - Infolists\Components\Section::make('操作步骤') - ->schema([ - Infolists\Components\RepeatableEntry::make('steps') - ->label('') - ->schema([ - Infolists\Components\TextEntry::make('step_number') - ->label('步骤') - ->badge() - ->color('primary'), - - Infolists\Components\TextEntry::make('title') - ->label('标题') - ->weight('bold'), - - Infolists\Components\TextEntry::make('content') - ->label('内容') - ->html() - ->columnSpanFull(), - - Infolists\Components\TextEntry::make('is_required') - ->label('必需') - ->badge() - ->formatStateUsing(fn (bool $state): string => $state ? '是' : '否') - ->color(fn (bool $state): string => $state ? 'success' : 'gray'), - ]) - ->columns(3) - ->contained(false), - ]), - ]); - }); - } -} diff --git a/app/Filament/Actions/PublishSopTemplateAction.php b/app/Filament/Actions/PublishSopTemplateAction.php deleted file mode 100644 index 18852b2..0000000 --- a/app/Filament/Actions/PublishSopTemplateAction.php +++ /dev/null @@ -1,85 +0,0 @@ -label('发布模板'); - - $this->icon('heroicon-o-check-circle'); - - $this->color('success'); - - $this->requiresConfirmation(); - - $this->modalHeading('发布 SOP 模板'); - - $this->modalDescription('发布后,模板将对所有用户可见。确定要发布吗?'); - - $this->modalSubmitActionLabel('确认发布'); - - $this->form([ - Forms\Components\Textarea::make('change_log') - ->label('变更说明') - ->placeholder('请描述本次发布的主要变更内容...') - ->rows(3), - ]); - - $this->visible(function ($record) { - return $record instanceof SopTemplate && $record->status === 'draft'; - }); - - $this->action(function (SopTemplate $record, array $data) { - // 验证模板是否有步骤 - if ($record->steps()->count() === 0) { - Notification::make() - ->title('发布失败') - ->body('模板至少需要包含一个步骤才能发布') - ->danger() - ->send(); - - return; - } - - // 创建版本快照 - SopTemplateVersion::create([ - 'sop_template_id' => $record->id, - 'version' => $record->version, - 'change_log' => $data['change_log'] ?? '首次发布', - 'content_snapshot' => [ - 'template' => $record->toArray(), - 'steps' => $record->steps->toArray(), - ], - 'created_by' => auth()->id(), - 'created_at' => now(), - ]); - - // 更新模板状态 - $record->update([ - 'status' => 'published', - 'published_at' => now(), - ]); - - Notification::make() - ->title('发布成功') - ->body('SOP 模板已成功发布') - ->success() - ->send(); - }); - } -} diff --git a/app/Filament/Pages/SearchPage.php b/app/Filament/Pages/SearchPage.php index f6565dc..b257820 100644 --- a/app/Filament/Pages/SearchPage.php +++ b/app/Filament/Pages/SearchPage.php @@ -4,6 +4,7 @@ namespace App\Filament\Pages; use App\Models\Document; use App\Models\Group; +use App\Models\KnowledgeBase; use App\Services\DocumentSearchService; use App\Services\DocumentService; use Filament\Forms\Components\Select; @@ -41,6 +42,7 @@ class SearchPage extends Page implements HasForms, HasTable public ?string $searchQuery = null; public ?string $documentType = null; public ?int $groupId = null; + public ?int $knowledgeBaseId = null; // 搜索结果 public $searchResults = null; @@ -55,6 +57,7 @@ class SearchPage extends Page implements HasForms, HasTable 'searchQuery' => '', 'documentType' => null, 'groupId' => null, + 'knowledgeBaseId' => null, ]); } @@ -86,8 +89,15 @@ class SearchPage extends Page implements HasForms, HasTable ->options(Group::pluck('name', 'id')) ->searchable() ->native(false), + + Select::make('knowledgeBaseId') + ->label('知识库') + ->placeholder('全部知识库') + ->options(KnowledgeBase::pluck('name', 'id')) + ->searchable() + ->native(false), ]) - ->columns(3); + ->columns(4); } /** @@ -198,6 +208,9 @@ class SearchPage extends Page implements HasForms, HasTable if ($this->groupId) { $filters['group_id'] = $this->groupId; } + if ($this->knowledgeBaseId) { + $filters['knowledge_base_id'] = $this->knowledgeBaseId; + } // 执行搜索 $results = $searchService->search($this->searchQuery, $user, $filters); @@ -236,6 +249,7 @@ class SearchPage extends Page implements HasForms, HasTable $this->searchQuery = $data['searchQuery']; $this->documentType = $data['documentType']; $this->groupId = $data['groupId']; + $this->knowledgeBaseId = $data['knowledgeBaseId'] ?? null; $this->hasSearched = true; // 重置表格分页 @@ -256,11 +270,13 @@ class SearchPage extends Page implements HasForms, HasTable 'searchQuery' => '', 'documentType' => null, 'groupId' => null, + 'knowledgeBaseId' => null, ]); $this->searchQuery = null; $this->documentType = null; $this->groupId = null; + $this->knowledgeBaseId = null; $this->hasSearched = false; $this->resetTable(); diff --git a/app/Filament/Resources/ActivityLogResource.php b/app/Filament/Resources/ActivityLogResource.php index 38d22b8..f70ba09 100644 --- a/app/Filament/Resources/ActivityLogResource.php +++ b/app/Filament/Resources/ActivityLogResource.php @@ -99,7 +99,7 @@ class ActivityLogResource extends Resource 'Document' => '文档', 'Group' => '分组', 'Terminal' => '终端', - 'SopTemplate' => 'SOP模板', + 'Guide' => '操作指引', default => $className, }; }) @@ -195,7 +195,7 @@ class ActivityLogResource extends Resource 'App\\Models\\Document' => '文档', 'App\\Models\\Group' => '分组', 'App\\Models\\Terminal' => '终端', - 'App\\Models\\SopTemplate' => 'SOP模板', + 'App\\Models\\Guide' => '操作指引', ]) ->placeholder('全部类型'), ]) diff --git a/app/Filament/Resources/ActivityLogResource/Pages/ViewActivityLog.php b/app/Filament/Resources/ActivityLogResource/Pages/ViewActivityLog.php index 4b3e94f..cc82904 100644 --- a/app/Filament/Resources/ActivityLogResource/Pages/ViewActivityLog.php +++ b/app/Filament/Resources/ActivityLogResource/Pages/ViewActivityLog.php @@ -61,7 +61,7 @@ class ViewActivityLog extends ViewRecord 'Document' => '文档', 'Group' => '分组', 'Terminal' => '终端', - 'SopTemplate' => 'SOP模板', + 'Guide' => '操作指引', default => $className, }; }), diff --git a/app/Filament/Resources/GuideResource.php b/app/Filament/Resources/GuideResource.php new file mode 100644 index 0000000..1c23a21 --- /dev/null +++ b/app/Filament/Resources/GuideResource.php @@ -0,0 +1,204 @@ +user()?->can('guide.view') ?? false; + } + + public static function form(Form $form): Form + { + return $form + ->schema([ + Forms\Components\Section::make('基本信息') + ->schema([ + Forms\Components\TextInput::make('name') + ->label('指引名称') + ->required() + ->maxLength(255) + ->placeholder('例如: 如何用光'), + + Forms\Components\Select::make('category') + ->label('分类') + ->required() + ->options([ + 'operation' => '操作指引', + 'fault_handling' => '故障处理', + 'training' => '培训教程', + 'safety' => '安全规范', + 'maintenance' => '维护保养', + ]) + ->default('operation'), + + Forms\Components\Select::make('status') + ->label('状态') + ->required() + ->options([ + 'draft' => '草稿', + 'published' => '已发布', + 'archived' => '已归档', + ]) + ->default('draft'), + + Forms\Components\TagsInput::make('tags') + ->label('标签') + ->placeholder('输入标签后回车') + ->helperText('用于分类和搜索的关键词标签'), + + Forms\Components\Textarea::make('description') + ->label('描述') + ->maxLength(1000) + ->placeholder('简要描述此指引的用途') + ->columnSpanFull(), + ]) + ->columns(2), + + Forms\Components\Section::make('关联终端') + ->schema([ + Forms\Components\CheckboxList::make('terminals') + ->label('适用终端') + ->relationship('terminals', 'name') + ->searchable() + ->bulkToggleable() + ->helperText('选择此指引适用的终端,未关联终端的指引不会在终端显示') + ->columns(3), + ]) + ->description('配置此指引在哪些终端上可见'), + ]); + } + + public static function table(Table $table): Table + { + return $table + ->columns([ + Tables\Columns\TextColumn::make('name') + ->label('指引名称') + ->searchable() + ->sortable() + ->weight('bold'), + + Tables\Columns\TextColumn::make('category') + ->label('分类') + ->badge() + ->formatStateUsing(fn(string $state): string => match ($state) { + 'operation' => '操作指引', + 'fault_handling' => '故障处理', + 'training' => '培训教程', + 'safety' => '安全规范', + 'maintenance' => '维护保养', + default => $state, + }) + ->color(fn(string $state): string => match ($state) { + 'operation' => 'primary', + 'fault_handling' => 'danger', + 'training' => 'info', + 'safety' => 'warning', + 'maintenance' => 'gray', + default => 'gray', + }) + ->sortable(), + + Tables\Columns\TextColumn::make('status') + ->label('状态') + ->badge() + ->formatStateUsing(fn(string $state): string => match ($state) { + 'draft' => '草稿', + 'published' => '已发布', + 'archived' => '已归档', + default => $state, + }) + ->color(fn(string $state): string => match ($state) { + 'draft' => 'gray', + 'published' => 'success', + 'archived' => 'warning', + default => 'gray', + }) + ->sortable(), + + Tables\Columns\TextColumn::make('pages_count') + ->label('页数') + ->counts('pages') + ->sortable(), + + Tables\Columns\TextColumn::make('terminals_count') + ->label('关联终端') + ->counts('terminals') + ->sortable() + ->badge() + ->color(fn(int $state): string => $state > 0 ? 'success' : 'gray') + ->formatStateUsing(fn(int $state): string => $state > 0 ? "{$state} 个" : '未关联'), + + Tables\Columns\TextColumn::make('created_at') + ->label('创建时间') + ->dateTime('Y-m-d H:i') + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + ]) + ->filters([ + Tables\Filters\SelectFilter::make('category') + ->label('分类') + ->options([ + 'operation' => '操作指引', + 'fault_handling' => '故障处理', + 'training' => '培训教程', + 'safety' => '安全规范', + 'maintenance' => '维护保养', + ]), + + Tables\Filters\SelectFilter::make('status') + ->label('状态') + ->options([ + 'draft' => '草稿', + 'published' => '已发布', + 'archived' => '已归档', + ]), + ]) + ->actions([ + Tables\Actions\EditAction::make()->label('编辑'), + Tables\Actions\DeleteAction::make()->label('删除'), + ]) + ->bulkActions([ + Tables\Actions\BulkActionGroup::make([ + Tables\Actions\DeleteBulkAction::make()->label('批量删除'), + ]), + ]) + ->defaultSort('created_at', 'desc'); + } + + public static function getPages(): array + { + return [ + 'index' => Pages\ListGuides::route('/'), + 'create' => Pages\CreateGuide::route('/create'), + 'edit' => Pages\EditGuide::route('/{record}/edit'), + 'manage-pages' => Pages\ManageGuidePages::route('/{record}/manage-pages'), + ]; + } +} diff --git a/app/Filament/Resources/GuideResource/Pages/CreateGuide.php b/app/Filament/Resources/GuideResource/Pages/CreateGuide.php new file mode 100644 index 0000000..f36594f --- /dev/null +++ b/app/Filament/Resources/GuideResource/Pages/CreateGuide.php @@ -0,0 +1,22 @@ +id(); + + if ($data['status'] === 'published') { + $data['published_at'] = now(); + } + + return $data; + } +} diff --git a/app/Filament/Resources/GuideResource/Pages/EditGuide.php b/app/Filament/Resources/GuideResource/Pages/EditGuide.php new file mode 100644 index 0000000..38d3cbc --- /dev/null +++ b/app/Filament/Resources/GuideResource/Pages/EditGuide.php @@ -0,0 +1,32 @@ +label('编辑指引') + ->icon('heroicon-o-queue-list') + ->url(fn() => GuideResource::getUrl('manage-pages', ['record' => $this->record])), + Actions\DeleteAction::make()->label('删除'), + ]; + } + + protected function mutateFormDataBeforeSave(array $data): array + { + if ($data['status'] === 'published' && !$this->record->published_at) { + $data['published_at'] = now(); + } + + return $data; + } +} diff --git a/app/Filament/Resources/GuideResource/Pages/ListGuides.php b/app/Filament/Resources/GuideResource/Pages/ListGuides.php new file mode 100644 index 0000000..364860a --- /dev/null +++ b/app/Filament/Resources/GuideResource/Pages/ListGuides.php @@ -0,0 +1,20 @@ +label('创建指引'), + ]; + } +} diff --git a/app/Filament/Resources/GuideResource/Pages/ManageGuidePages.php b/app/Filament/Resources/GuideResource/Pages/ManageGuidePages.php new file mode 100644 index 0000000..087a84e --- /dev/null +++ b/app/Filament/Resources/GuideResource/Pages/ManageGuidePages.php @@ -0,0 +1,88 @@ +where('guide_id', $this->getOwnerRecord()->id); + } + + public function getTreeRecordTitle(?Model $record = null): string + { + if (!$record) { + return ''; + } + $prefix = $record->branch_option ? "[{$record->branch_option}] " : ''; + $suffix = !empty($record->options) ? ' 📋' : ''; + return $prefix . $record->title . $suffix; + } + + protected function getFormSchema(): array + { + return [ + Forms\Components\TextInput::make('page_number') + ->label('页码') + ->numeric() + ->minValue(1), + + Forms\Components\TextInput::make('title') + ->label('页面标题') + ->required() + ->maxLength(255), + + Forms\Components\TextInput::make('html_url') + ->label('HTML页面URL') + ->required() + ->url() + ->maxLength(500), + + Forms\Components\TagsInput::make('options') + ->label('选项按钮') + ->helperText('此页面展示的选项按钮,如"前门12"、"后门"。留空=无分支。'), + + Forms\Components\TextInput::make('branch_option') + ->label('所属分支选项') + ->maxLength(100) + ->helperText('此页面对应父页面的哪个选项值(根页面留空)'), + ]; + } + + protected function getTreeActions(): array + { + return [ + Actions\ViewAction::make(), + Actions\EditAction::make()->slideOver(), + Actions\DeleteAction::make(), + ]; + } + + protected function mutateFormDataBeforeCreate(array $data): array + { + $data['guide_id'] = $this->getOwnerRecord()->id; + return $data; + } + + protected function getOwnerRecord(): Guide + { + return Guide::findOrFail(request()->route('record')); + } +} diff --git a/app/Filament/Resources/RoleResource.php b/app/Filament/Resources/RoleResource.php index 07dd6cc..f272b5b 100644 --- a/app/Filament/Resources/RoleResource.php +++ b/app/Filament/Resources/RoleResource.php @@ -46,7 +46,7 @@ class RoleResource extends Resource 'system-setting' => ['name' => '系统设置', 'icon' => 'heroicon-o-cog-6-tooth'], 'activity-log' => ['name' => '操作日志', 'icon' => 'heroicon-o-clipboard-document-list'], 'terminal' => ['name' => '终端管理', 'icon' => 'heroicon-o-computer-desktop'], - 'sop-template' => ['name' => 'SOP模板', 'icon' => 'heroicon-o-document-text'], + 'guide' => ['name' => '操作指引', 'icon' => 'heroicon-o-book-open'], 'group' => ['name' => '分组管理', 'icon' => 'heroicon-o-user-group'], 'user' => ['name' => '用户管理', 'icon' => 'heroicon-o-users'], 'role' => ['name' => '角色管理', 'icon' => 'heroicon-o-shield-check'], @@ -156,7 +156,7 @@ class RoleResource extends Resource ->dehydrateStateUsing(function ($state, $get) { // 收集所有模块的权限 $allPermissions = []; - $modules = ['document', 'system-setting', 'activity-log', 'terminal', 'sop-template', 'group', 'user', 'role']; + $modules = ['document', 'system-setting', 'activity-log', 'terminal', 'guide', 'group', 'user', 'role']; foreach ($modules as $module) { $modulePermissions = $get("permissions_{$module}") ?? []; diff --git a/app/Filament/Resources/RoleResource/Pages/ViewRole.php b/app/Filament/Resources/RoleResource/Pages/ViewRole.php index c781b64..34703f3 100644 --- a/app/Filament/Resources/RoleResource/Pages/ViewRole.php +++ b/app/Filament/Resources/RoleResource/Pages/ViewRole.php @@ -80,7 +80,7 @@ class ViewRole extends ViewRecord 'system-setting' => '⚙️ 系统设置', 'activity-log' => '📋 操作日志', 'terminal' => '🖥️ 终端管理', - 'sop-template' => '📝 SOP模板', + 'guide' => '📖 操作指引', 'group' => '👥 分组管理', 'user' => '👤 用户管理', 'role' => '🛡️ 角色管理', diff --git a/app/Filament/Resources/SopTemplateResource.php b/app/Filament/Resources/SopTemplateResource.php deleted file mode 100644 index d0d4a27..0000000 --- a/app/Filament/Resources/SopTemplateResource.php +++ /dev/null @@ -1,269 +0,0 @@ -user()?->can('sop-template.view') ?? false; - } - - public static function form(Form $form): Form - { - return $form - ->schema([ - Forms\Components\Section::make('基本信息') - ->schema([ - Forms\Components\TextInput::make('name') - ->label('模板名称') - ->required() - ->maxLength(255), - - Forms\Components\Textarea::make('description') - ->label('模板描述') - ->rows(3) - ->maxLength(65535), - - Forms\Components\TextInput::make('category') - ->label('分类') - ->maxLength(100) - ->placeholder('例如:安全操作、设备维护、质量检查'), - - Forms\Components\TagsInput::make('tags') - ->label('标签') - ->placeholder('添加标签') - ->separator(','), - ]) - ->columns(2), - - Forms\Components\Section::make('适用范围') - ->schema([ - Forms\Components\TagsInput::make('applicable_departments') - ->label('适用部门') - ->placeholder('添加部门') - ->separator(','), - - Forms\Components\TagsInput::make('applicable_positions') - ->label('适用岗位') - ->placeholder('添加岗位') - ->separator(','), - ]) - ->columns(2), - - Forms\Components\Section::make('版本管理') - ->schema([ - Forms\Components\TextInput::make('version') - ->label('版本号') - ->default('1.0.0') - ->required() - ->maxLength(50), - - Forms\Components\Select::make('status') - ->label('状态') - ->options([ - 'draft' => '草稿', - 'published' => '已发布', - 'archived' => '已归档', - ]) - ->default('draft') - ->required(), - ]) - ->columns(2) - ->visible(fn ($livewire) => $livewire instanceof Pages\EditSopTemplate), - - // 步骤编辑器 - 只在编辑页面显示 - Forms\Components\Section::make('操作步骤') - ->schema([ - Forms\Components\Repeater::make('steps') - ->label('') - ->relationship('steps') - ->schema([ - Forms\Components\TextInput::make('step_number') - ->label('步骤序号') - ->numeric() - ->required() - ->default(fn ($get) => $get('../../steps') ? count($get('../../steps')) + 1 : 1), - - Forms\Components\TextInput::make('title') - ->label('步骤标题') - ->required() - ->maxLength(255) - ->columnSpanFull(), - - Forms\Components\RichEditor::make('content') - ->label('步骤内容') - ->toolbarButtons([ - 'bold', - 'italic', - 'underline', - 'strike', - 'bulletList', - 'orderedList', - 'h2', - 'h3', - 'link', - 'blockquote', - 'codeBlock', - ]) - ->columnSpanFull(), - - Forms\Components\Toggle::make('is_required') - ->label('是否必需') - ->default(true), - - Forms\Components\Hidden::make('sort_order') - ->default(fn ($get) => $get('step_number')), - ]) - ->columns(2) - ->reorderable('sort_order') - ->reorderableWithButtons() - ->collapsible() - ->itemLabel(fn (array $state): ?string => $state['title'] ?? '新步骤') - ->addActionLabel('添加步骤') - ->defaultItems(0) - ->mutateRelationshipDataBeforeCreateUsing(function (array $data): array { - $data['sort_order'] = $data['step_number']; - return $data; - }) - ->mutateRelationshipDataBeforeSaveUsing(function (array $data): array { - $data['sort_order'] = $data['step_number']; - return $data; - }), - ]) - ->visible(fn ($livewire) => $livewire instanceof Pages\EditSopTemplate), - ]); - } - - public static function table(Table $table): Table - { - return $table - ->columns([ - Tables\Columns\TextColumn::make('name') - ->label('模板名称') - ->searchable() - ->sortable(), - - Tables\Columns\TextColumn::make('category') - ->label('分类') - ->searchable() - ->sortable() - ->badge() - ->color('info'), - - Tables\Columns\TextColumn::make('version') - ->label('版本') - ->sortable(), - - Tables\Columns\TextColumn::make('status') - ->label('状态') - ->badge() - ->color(fn (string $state): string => match ($state) { - 'draft' => 'gray', - 'published' => 'success', - 'archived' => 'warning', - }) - ->formatStateUsing(fn (string $state): string => match ($state) { - 'draft' => '草稿', - 'published' => '已发布', - 'archived' => '已归档', - default => $state, - }), - - Tables\Columns\TextColumn::make('steps_count') - ->label('步骤数') - ->counts('steps') - ->sortable(), - - Tables\Columns\TextColumn::make('creator.name') - ->label('创建人') - ->sortable() - ->toggleable(), - - Tables\Columns\TextColumn::make('published_at') - ->label('发布时间') - ->dateTime('Y-m-d H:i') - ->sortable() - ->toggleable(), - - Tables\Columns\TextColumn::make('created_at') - ->label('创建时间') - ->dateTime('Y-m-d H:i') - ->sortable() - ->toggleable(isToggledHiddenByDefault: true), - ]) - ->filters([ - Tables\Filters\SelectFilter::make('status') - ->label('状态') - ->options([ - 'draft' => '草稿', - 'published' => '已发布', - 'archived' => '已归档', - ]), - - Tables\Filters\SelectFilter::make('category') - ->label('分类') - ->options(function () { - return SopTemplate::query() - ->whereNotNull('category') - ->distinct() - ->pluck('category', 'category') - ->toArray(); - }), - ]) - ->actions([ - Tables\Actions\ViewAction::make(), - Tables\Actions\EditAction::make(), - Tables\Actions\DeleteAction::make(), - ]) - ->bulkActions([ - Tables\Actions\BulkActionGroup::make([ - Tables\Actions\DeleteBulkAction::make(), - ]), - ]) - ->defaultSort('created_at', 'desc'); - } - - public static function getRelations(): array - { - return [ - // - ]; - } - - public static function getPages(): array - { - return [ - 'index' => Pages\ListSopTemplates::route('/'), - 'create' => Pages\CreateSopTemplate::route('/create'), - 'view' => Pages\ViewSopTemplate::route('/{record}'), - 'edit' => Pages\EditSopTemplate::route('/{record}/edit'), - ]; - } -} diff --git a/app/Filament/Resources/SopTemplateResource/Pages/CreateSopTemplate.php b/app/Filament/Resources/SopTemplateResource/Pages/CreateSopTemplate.php deleted file mode 100644 index 7c96b1f..0000000 --- a/app/Filament/Resources/SopTemplateResource/Pages/CreateSopTemplate.php +++ /dev/null @@ -1,23 +0,0 @@ -id(); - - return $data; - } - - protected function getRedirectUrl(): string - { - return $this->getResource()::getUrl('edit', ['record' => $this->getRecord()]); - } -} diff --git a/app/Filament/Resources/SopTemplateResource/Pages/EditSopTemplate.php b/app/Filament/Resources/SopTemplateResource/Pages/EditSopTemplate.php deleted file mode 100644 index 2cf9b43..0000000 --- a/app/Filament/Resources/SopTemplateResource/Pages/EditSopTemplate.php +++ /dev/null @@ -1,28 +0,0 @@ -schema([ - Infolists\Components\Section::make('基本信息') - ->schema([ - Infolists\Components\TextEntry::make('name') - ->label('模板名称'), - - Infolists\Components\TextEntry::make('description') - ->label('模板描述') - ->columnSpanFull(), - - Infolists\Components\TextEntry::make('category') - ->label('分类') - ->badge() - ->color('info'), - - Infolists\Components\TextEntry::make('tags') - ->label('标签') - ->badge() - ->separator(','), - - Infolists\Components\TextEntry::make('version') - ->label('版本号'), - - Infolists\Components\TextEntry::make('status') - ->label('状态') - ->badge() - ->color(fn (string $state): string => match ($state) { - 'draft' => 'gray', - 'published' => 'success', - 'archived' => 'warning', - }) - ->formatStateUsing(fn (string $state): string => match ($state) { - 'draft' => '草稿', - 'published' => '已发布', - 'archived' => '已归档', - default => $state, - }), - ]) - ->columns(2), - - Infolists\Components\Section::make('适用范围') - ->schema([ - Infolists\Components\TextEntry::make('applicable_departments') - ->label('适用部门') - ->badge() - ->separator(','), - - Infolists\Components\TextEntry::make('applicable_positions') - ->label('适用岗位') - ->badge() - ->separator(','), - ]) - ->columns(2), - - Infolists\Components\Section::make('其他信息') - ->schema([ - Infolists\Components\TextEntry::make('creator.name') - ->label('创建人'), - - Infolists\Components\TextEntry::make('published_at') - ->label('发布时间') - ->dateTime('Y-m-d H:i'), - - Infolists\Components\TextEntry::make('created_at') - ->label('创建时间') - ->dateTime('Y-m-d H:i'), - - Infolists\Components\TextEntry::make('updated_at') - ->label('更新时间') - ->dateTime('Y-m-d H:i'), - ]) - ->columns(2), - ]); - } -} diff --git a/app/Filament/Resources/TerminalResource.php b/app/Filament/Resources/TerminalResource.php index d0af04f..8bd78a3 100644 --- a/app/Filament/Resources/TerminalResource.php +++ b/app/Filament/Resources/TerminalResource.php @@ -68,11 +68,21 @@ class TerminalResource extends Resource ->placeholder('例如: 192.168.1.100') ->helperText('终端的IP地址'), + Forms\Components\TextInput::make('mac_address') + ->label('MAC地址') + ->maxLength(17) + ->placeholder('AA:BB:CC:DD:EE:FF') + ->helperText('终端的MAC地址,用于自动识别终端') + ->regex('/^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$/') + ->validationMessages([ + 'regex' => 'MAC地址格式不正确,应为 AA:BB:CC:DD:EE:FF', + ]), + Forms\Components\TextInput::make('station_id') ->label('线站ID') - ->numeric() - ->placeholder('请输入线站ID') - ->helperText('关联的生产线站ID'), + ->maxLength(50) + ->placeholder('例如: BL02U1') + ->helperText('关联的光束线/线站标识'), ]) ->columns(2), @@ -86,6 +96,24 @@ class TerminalResource extends Resource ->helperText('组态图的访问地址'), ]), + Forms\Components\Section::make('SCADA网关配置') + ->schema([ + Forms\Components\TextInput::make('scada_data_url') + ->label('SCADA数据查询URL') + ->url() + ->maxLength(500) + ->placeholder('http://gateway:8080/api/data') + ->helperText('OPC UA HTTP网关的数据查询地址'), + + Forms\Components\TextInput::make('scada_tags_url') + ->label('SCADA点位定义URL') + ->url() + ->maxLength(500) + ->placeholder('http://gateway:8080/api/tags') + ->helperText('OPC UA HTTP网关的点位定义查询地址'), + ]) + ->columns(2), + Forms\Components\Section::make('显示配置') ->schema([ Forms\Components\KeyValue::make('display_config') @@ -130,7 +158,7 @@ class TerminalResource extends Resource ->reorderableWithButtons() ->addActionLabel('添加知识库') ->reorderableWithDragAndDrop(false) - ->itemLabel(fn (array $state): ?string => + ->itemLabel(fn (array $state): ?string => \App\Models\KnowledgeBase::find($state['id'])?->name ?? '未选择' ) ->collapsed() @@ -139,6 +167,43 @@ class TerminalResource extends Resource ]) ->description('配置终端可以访问的知识库及其优先级'), + Forms\Components\Section::make('指引关联') + ->schema([ + Forms\Components\Repeater::make('guideAssociations') + ->label('关联指引') + ->relationship('guides') + ->schema([ + Forms\Components\Select::make('id') + ->label('指引') + ->options(\App\Models\Guide::where('status', 'published')->pluck('name', 'id')) + ->required() + ->searchable() + ->distinct() + ->disableOptionsWhenSelectedInSiblingRepeaterItems() + ->helperText('选择要关联的指引'), + + Forms\Components\TextInput::make('priority') + ->label('优先级') + ->numeric() + ->default(0) + ->required() + ->minValue(0) + ->helperText('数字越小优先级越高,0为最高优先级'), + ]) + ->columns(2) + ->reorderable() + ->reorderableWithButtons() + ->addActionLabel('添加指引') + ->reorderableWithDragAndDrop(false) + ->itemLabel(fn (array $state): ?string => + \App\Models\Guide::find($state['id'])?->name ?? '未选择' + ) + ->collapsed() + ->collapsible() + ->helperText('可以关联多个指引,并设置优先级。拖动或使用按钮调整顺序。'), + ]) + ->description('配置终端可以访问的操作指引及其优先级'), + Forms\Components\Section::make('AI提示词配置') ->schema([ Forms\Components\Grid::make(3) @@ -204,6 +269,13 @@ class TerminalResource extends Resource ->copyable() ->tooltip('点击复制'), + Tables\Columns\TextColumn::make('mac_address') + ->label('MAC地址') + ->searchable() + ->copyable() + ->placeholder('未设置') + ->toggleable(), + Tables\Columns\TextColumn::make('ip_address') ->label('IP地址') ->searchable() diff --git a/app/Filament/Resources/UserResource.php b/app/Filament/Resources/UserResource.php index dbe4daf..6e29908 100644 --- a/app/Filament/Resources/UserResource.php +++ b/app/Filament/Resources/UserResource.php @@ -48,7 +48,7 @@ class UserResource extends Resource 'system-setting' => ['name' => '系统设置', 'icon' => 'heroicon-o-cog-6-tooth'], 'activity-log' => ['name' => '操作日志', 'icon' => 'heroicon-o-clipboard-document-list'], 'terminal' => ['name' => '终端管理', 'icon' => 'heroicon-o-computer-desktop'], - 'sop-template' => ['name' => 'SOP模板', 'icon' => 'heroicon-o-document-text'], + 'guide' => ['name' => '操作指引', 'icon' => 'heroicon-o-book-open'], 'group' => ['name' => '分组管理', 'icon' => 'heroicon-o-user-group'], 'user' => ['name' => '用户管理', 'icon' => 'heroicon-o-users'], 'role' => ['name' => '角色管理', 'icon' => 'heroicon-o-shield-check'], @@ -172,7 +172,7 @@ class UserResource extends Resource ->dehydrateStateUsing(function ($state, $get) { // 收集所有模块的权限 $allPermissions = []; - $modules = ['document', 'system-setting', 'activity-log', 'terminal', 'sop-template', 'group', 'user', 'role']; + $modules = ['document', 'system-setting', 'activity-log', 'terminal', 'guide', 'group', 'user', 'role']; foreach ($modules as $module) { $modulePermissions = $get("permissions_{$module}") ?? []; diff --git a/app/Filament/Resources/UserResource/Pages/ViewUser.php b/app/Filament/Resources/UserResource/Pages/ViewUser.php index c682b65..b11d774 100644 --- a/app/Filament/Resources/UserResource/Pages/ViewUser.php +++ b/app/Filament/Resources/UserResource/Pages/ViewUser.php @@ -90,7 +90,7 @@ class ViewUser extends ViewRecord 'system-setting' => '⚙️ 系统设置', 'activity-log' => '📋 操作日志', 'terminal' => '🖥️ 终端管理', - 'sop-template' => '📝 SOP模板', + 'guide' => '📖 操作指引', 'group' => '👥 分组管理', 'user' => '👤 用户管理', 'role' => '🛡️ 角色管理', @@ -144,7 +144,7 @@ class ViewUser extends ViewRecord 'system-setting' => '⚙️ 系统设置', 'activity-log' => '📋 操作日志', 'terminal' => '🖥️ 终端管理', - 'sop-template' => '📝 SOP模板', + 'guide' => '📖 操作指引', 'group' => '👥 分组管理', 'user' => '👤 用户管理', 'role' => '🛡️ 角色管理', diff --git a/app/Http/Controllers/Api/TerminalApiController.php b/app/Http/Controllers/Api/TerminalApiController.php new file mode 100644 index 0000000..dea1658 --- /dev/null +++ b/app/Http/Controllers/Api/TerminalApiController.php @@ -0,0 +1,227 @@ +attributes->get('terminal'); + $terminal->load(['prompt', 'knowledgeBases']); + + // 渲染system prompt + $systemPrompt = ''; + if ($terminal->prompt && $terminal->prompt->prompt_template) { + $systemPrompt = $this->promptService->replaceVariables( + $terminal->prompt->prompt_template, + $terminal + ); + } + + // 获取终端关联的已发布指引数量 + $guideCount = $terminal->guides()->published()->count(); + + return response()->json([ + 'terminal' => [ + 'id' => $terminal->id, + 'name' => $terminal->name, + 'code' => $terminal->code, + 'station_id' => $terminal->station_id, + 'diagram_url' => $terminal->diagram_url, + 'scada_data_url' => $terminal->scada_data_url, + 'scada_tags_url' => $terminal->scada_tags_url, + 'display_config' => $terminal->display_config, + ], + 'system_prompt' => $systemPrompt, + 'guide_count' => $guideCount, + ]); + } + + /** + * GET /api/terminal/knowledge?query=xxx + * RAG知识搜索(由AI tool_call触发) + */ + public function knowledge(Request $request): JsonResponse + { + $request->validate([ + 'query' => 'required|string|max:500', + ]); + + $terminal = $request->attributes->get('terminal'); + $terminal->load('knowledgeBases'); + + $result = $this->knowledgeService->search($terminal, $request->input('query')); + + return response()->json($result); + } + + /** + * GET /api/terminal/guides?category=operation + * 已发布的指引列表 + */ + public function guides(Request $request): JsonResponse + { + $terminal = $request->attributes->get('terminal'); + $query = $terminal->guides()->published()->withCount('pages'); + + if ($category = $request->input('category')) { + $query->where('category', $category); + } + + $guides = $query->orderBy('name')->get()->map(fn(Guide $guide) => [ + 'id' => $guide->id, + 'name' => $guide->name, + 'description' => $guide->description, + 'category' => $guide->category, + 'tags' => $guide->tags, + 'page_count' => $guide->pages_count, + ]); + + return response()->json(['guides' => $guides]); + } + + /** + * POST /api/terminal/guides/pages + * 组合多个指引的页面,返回递归树形结构 + */ + public function guidePages(Request $request): JsonResponse + { + $request->validate([ + 'guide_ids' => 'required|array|min:1', + 'guide_ids.*' => 'integer|exists:guides,id', + ]); + + $terminal = $request->attributes->get('terminal'); + $accessibleIds = $terminal->guides()->published()->pluck('guides.id')->toArray(); + + $guideIds = $request->input('guide_ids'); + $pages = []; + + foreach ($guideIds as $guideId) { + if (!in_array($guideId, $accessibleIds)) { + continue; + } + + $guide = Guide::with( + $this->buildEagerLoadArray('trunkPages', 5) + )->find($guideId); + + if (!$guide) { + continue; + } + + foreach ($guide->trunkPages as $page) { + $pages = array_merge($pages, $this->flattenSequentialPages($page, $guide->name, $guide->id)); + } + } + + return response()->json([ + 'pages' => $pages, + 'total_pages' => count($pages), + ]); + } + + /** + * 将树形页面结构展平:顺序节点(无 options)平铺,分支节点保留嵌套 + */ + private function flattenSequentialPages(GuidePage $page, string $guideName, int $guideId): array + { + $data = [ + 'id' => $page->id, + 'guide_id' => $guideId, + 'guide_name' => $guideName, + 'page_number' => $page->page_number, + 'title' => $page->title, + 'html_url' => $page->html_url, + ]; + + if ($page->options && $page->branchChildren->isNotEmpty()) { + $data['options'] = $page->options; + $branches = []; + foreach ($page->branchChildren as $child) { + $branches[$child->branch_option][] = + $this->buildPageTree($child, $guideName, $guideId); + } + $data['branches'] = $branches; + return [$data]; + } + + if ($page->branchChildren->isNotEmpty()) { + $result = [$data]; + foreach ($page->branchChildren as $child) { + $result = array_merge($result, $this->flattenSequentialPages($child, $guideName, $guideId)); + } + return $result; + } + + return [$data]; + } + + private function buildPageTree(GuidePage $page, string $guideName, int $guideId): array + { + $data = [ + 'id' => $page->id, + 'guide_id' => $guideId, + 'guide_name' => $guideName, + 'page_number' => $page->page_number, + 'title' => $page->title, + 'html_url' => $page->html_url, + ]; + + if ($page->options && $page->branchChildren->isNotEmpty()) { + $data['options'] = $page->options; + $branches = []; + foreach ($page->branchChildren as $child) { + $branches[$child->branch_option][] = + $this->buildPageTree($child, $guideName, $guideId); + } + $data['branches'] = $branches; + } + + return $data; + } + + private function buildEagerLoadArray(string $base, int $depth): array + { + $loads = [$base => fn($q) => $q->orderBy('sort_order')]; + $current = $base; + for ($i = 0; $i < $depth; $i++) { + $current .= '.branchChildren'; + $loads[$current] = fn($q) => $q->orderBy('sort_order'); + } + return $loads; + } + + /** + * POST /api/terminal/heartbeat + * 终端心跳上报 + */ + public function heartbeat(Request $request): JsonResponse + { + $terminal = $request->attributes->get('terminal'); + + $terminal->update([ + 'is_online' => true, + 'last_online_at' => now(), + ]); + + return response()->json(['status' => 'ok']); + } +} diff --git a/app/Http/Middleware/IdentifyTerminal.php b/app/Http/Middleware/IdentifyTerminal.php new file mode 100644 index 0000000..9146d88 --- /dev/null +++ b/app/Http/Middleware/IdentifyTerminal.php @@ -0,0 +1,38 @@ +header('X-Terminal-MAC'); + + if (!$macHeader) { + return response()->json(['error' => 'Missing X-Terminal-MAC header'], 400); + } + + // HMI sends comma-separated MACs for all active interfaces; + // match if any one corresponds to a registered terminal + $macs = array_map('trim', explode(',', $macHeader)); + $terminal = Terminal::whereIn('mac_address', $macs)->first(); + + if (!$terminal) { + return response()->json(['error' => 'Terminal not registered'], 403); + } + + $request->attributes->set('terminal', $terminal); + + // Record IP address from header (for logging/diagnostics) + if ($ip = $request->header('X-Terminal-IP')) { + $request->attributes->set('terminal_ip', $ip); + } + + return $next($request); + } +} diff --git a/app/Models/Document.php b/app/Models/Document.php index 03f021a..9272335 100644 --- a/app/Models/Document.php +++ b/app/Models/Document.php @@ -32,6 +32,7 @@ class Document extends Model 'markdown_preview', 'conversion_status', 'conversion_error', + 'knowledge_base_id', ]; /** @@ -42,6 +43,14 @@ class Document extends Model return $this->belongsTo(Group::class); } + /** + * 获取文档所属的知识库 + */ + public function knowledgeBase(): BelongsTo + { + return $this->belongsTo(KnowledgeBase::class); + } + /** * 获取文档的上传者 */ @@ -120,6 +129,7 @@ class Document extends Model 'markdown_content' => $this->getMarkdownContent(), 'type' => $this->type, 'group_id' => $this->group_id, + 'knowledge_base_id' => $this->knowledge_base_id, 'uploaded_by' => $this->uploaded_by, 'created_at' => $this->created_at?->timestamp, ]; diff --git a/app/Models/Guide.php b/app/Models/Guide.php new file mode 100644 index 0000000..5f80f9a --- /dev/null +++ b/app/Models/Guide.php @@ -0,0 +1,75 @@ + 'array', + 'published_at' => 'datetime', + ]; + } + + public function pages() + { + return $this->hasMany(GuidePage::class)->orderBy('sort_order'); + } + + public function trunkPages() + { + return $this->hasMany(GuidePage::class) + ->where('parent_id', -1) + ->orderBy('sort_order'); + } + + public function creator() + { + return $this->belongsTo(User::class, 'created_by'); + } + + public function terminals() + { + return $this->belongsToMany(Terminal::class, 'terminal_guides') + ->withPivot('priority') + ->withTimestamps() + ->orderBy('priority'); + } + + public function scopePublished($query) + { + return $query->where('status', 'published'); + } + + public function scopeCategory($query, string $category) + { + return $query->where('category', $category); + } + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults() + ->logOnly(['name', 'description', 'category', 'status']) + ->logOnlyDirty() + ->setDescriptionForEvent(fn(string $eventName) => "指引已{$eventName}"); + } +} diff --git a/app/Models/GuidePage.php b/app/Models/GuidePage.php new file mode 100644 index 0000000..1b88d21 --- /dev/null +++ b/app/Models/GuidePage.php @@ -0,0 +1,58 @@ + 'array', + 'parent_id' => 'int', + ]; + + // filament-tree column name mapping + public function determineParentColumnName(): string + { + return 'parent_id'; + } + + public function determineOrderColumnName(): string + { + return 'sort_order'; + } + + public function determineTitleColumnName(): string + { + return 'title'; + } + + public function guide() + { + return $this->belongsTo(Guide::class); + } + + public function branchChildren() + { + return $this->hasMany(self::class, 'parent_id')->orderBy('sort_order'); + } + + public function parentPage() + { + return $this->belongsTo(self::class, 'parent_id'); + } +} diff --git a/app/Models/KnowledgeBase.php b/app/Models/KnowledgeBase.php index ae19946..81055c2 100644 --- a/app/Models/KnowledgeBase.php +++ b/app/Models/KnowledgeBase.php @@ -33,4 +33,14 @@ class KnowledgeBase extends Model ->withTimestamps() ->orderBy('priority'); } + + /** + * 获取知识库下的文档 + * + * @return \Illuminate\Database\Eloquent\Relations\HasMany + */ + public function documents() + { + return $this->hasMany(Document::class); + } } diff --git a/app/Models/SopInteractiveTask.php b/app/Models/SopInteractiveTask.php deleted file mode 100644 index 1c8323e..0000000 --- a/app/Models/SopInteractiveTask.php +++ /dev/null @@ -1,46 +0,0 @@ - - */ - protected $fillable = [ - 'sop_step_id', - 'task_type', - 'task_config', - 'validation_rules', - 'timeout_seconds', - 'is_required', - ]; - - /** - * 属性类型转换 - * - * @return array - */ - protected function casts(): array - { - return [ - 'task_config' => 'array', - 'validation_rules' => 'array', - 'is_required' => 'boolean', - ]; - } - - /** - * 获取任务所属的步骤 - * - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo - */ - public function step() - { - return $this->belongsTo(SopStep::class, 'sop_step_id'); - } -} diff --git a/app/Models/SopStep.php b/app/Models/SopStep.php deleted file mode 100644 index 5181873..0000000 --- a/app/Models/SopStep.php +++ /dev/null @@ -1,54 +0,0 @@ - - */ - protected $fillable = [ - 'sop_template_id', - 'step_number', - 'title', - 'content', - 'sort_order', - 'is_required', - ]; - - /** - * 属性类型转换 - * - * @return array - */ - protected function casts(): array - { - return [ - 'is_required' => 'boolean', - ]; - } - - /** - * 获取步骤所属的模板 - * - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo - */ - public function template() - { - return $this->belongsTo(SopTemplate::class, 'sop_template_id'); - } - - /** - * 获取步骤的交互任务列表 - * - * @return \Illuminate\Database\Eloquent\Relations\HasMany - */ - public function interactiveTasks() - { - return $this->hasMany(SopInteractiveTask::class); - } -} diff --git a/app/Models/SopTemplate.php b/app/Models/SopTemplate.php deleted file mode 100644 index 9035d42..0000000 --- a/app/Models/SopTemplate.php +++ /dev/null @@ -1,90 +0,0 @@ - - */ - protected $fillable = [ - 'name', - 'description', - 'category', - 'tags', - 'version', - 'status', - 'applicable_departments', - 'applicable_positions', - 'published_at', - 'created_by', - ]; - - /** - * 属性类型转换 - * - * @return array - */ - protected function casts(): array - { - return [ - 'tags' => 'array', - 'applicable_departments' => 'array', - 'applicable_positions' => 'array', - 'published_at' => 'datetime', - ]; - } - - /** - * 获取模板的步骤列表 - * - * @return \Illuminate\Database\Eloquent\Relations\HasMany - */ - public function steps() - { - return $this->hasMany(SopStep::class)->orderBy('sort_order'); - } - - /** - * 获取模板的版本历史 - * - * @return \Illuminate\Database\Eloquent\Relations\HasMany - */ - public function versions() - { - return $this->hasMany(SopTemplateVersion::class); - } - - /** - * 获取模板的创建者 - * - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo - */ - public function creator() - { - return $this->belongsTo(User::class, 'created_by'); - } - - /** - * 配置活动日志选项 - * - * @return \Spatie\Activitylog\LogOptions - */ - public function getActivitylogOptions(): LogOptions - { - return LogOptions::defaults() - ->logOnly(['name', 'description', 'category', 'status', 'version']) - ->logOnlyDirty() - ->setDescriptionForEvent(fn(string $eventName) => "SOP模板已{$eventName}"); - } -} diff --git a/app/Models/SopTemplateVersion.php b/app/Models/SopTemplateVersion.php deleted file mode 100644 index 218acc0..0000000 --- a/app/Models/SopTemplateVersion.php +++ /dev/null @@ -1,62 +0,0 @@ - - */ - protected $fillable = [ - 'sop_template_id', - 'version', - 'change_log', - 'content_snapshot', - 'created_by', - ]; - - /** - * 属性类型转换 - * - * @return array - */ - protected function casts(): array - { - return [ - 'content_snapshot' => 'array', - 'created_at' => 'datetime', - ]; - } - - /** - * 获取版本所属的模板 - * - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo - */ - public function template() - { - return $this->belongsTo(SopTemplate::class, 'sop_template_id'); - } - - /** - * 获取版本的创建者 - * - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo - */ - public function creator() - { - return $this->belongsTo(User::class, 'created_by'); - } -} diff --git a/app/Models/Terminal.php b/app/Models/Terminal.php index def5544..45aeef5 100644 --- a/app/Models/Terminal.php +++ b/app/Models/Terminal.php @@ -21,8 +21,11 @@ class Terminal extends Model 'name', 'code', 'ip_address', + 'mac_address', 'station_id', 'diagram_url', + 'scada_data_url', + 'scada_tags_url', 'display_config', 'is_online', 'last_online_at', @@ -55,6 +58,19 @@ class Terminal extends Model ->orderBy('priority'); } + /** + * 获取终端关联的指引 + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany + */ + public function guides() + { + return $this->belongsToMany(Guide::class, 'terminal_guides') + ->withPivot('priority') + ->withTimestamps() + ->orderBy('priority'); + } + /** * 获取终端的提示词配置 * diff --git a/app/Policies/GuidePolicy.php b/app/Policies/GuidePolicy.php new file mode 100644 index 0000000..e6f6828 --- /dev/null +++ b/app/Policies/GuidePolicy.php @@ -0,0 +1,81 @@ +can('guide.view'); + } + + /** + * 查看指引 + */ + public function view(User $user, Guide $guide): bool + { + return $user->can('guide.view'); + } + + /** + * 创建指引 + */ + public function create(User $user): bool + { + return $user->can('guide.create'); + } + + /** + * 更新指引 + */ + public function update(User $user, Guide $guide): bool + { + return $user->can('guide.update'); + } + + /** + * 删除指引 + */ + public function delete(User $user, Guide $guide): bool + { + return $user->can('guide.delete'); + } + + /** + * 发布指引 + */ + public function publish(User $user, Guide $guide): bool + { + return $user->can('guide.publish'); + } + + /** + * 归档指引 + */ + public function archive(User $user, Guide $guide): bool + { + return $user->can('guide.archive'); + } + + /** + * 恢复已删除的指引 + */ + public function restore(User $user, Guide $guide): bool + { + return $user->can('guide.delete'); + } + + /** + * 永久删除指引 + */ + public function forceDelete(User $user, Guide $guide): bool + { + return $user->can('guide.delete'); + } +} diff --git a/app/Policies/SopTemplatePolicy.php b/app/Policies/SopTemplatePolicy.php deleted file mode 100644 index 184b323..0000000 --- a/app/Policies/SopTemplatePolicy.php +++ /dev/null @@ -1,95 +0,0 @@ -can('sop-template.view'); - } - - /** - * 查看 SOP 模板 - */ - public function view(User $user, SopTemplate $sopTemplate): bool - { - return $user->can('sop-template.view'); - } - - /** - * 创建 SOP 模板 - */ - public function create(User $user): bool - { - return $user->can('sop-template.create'); - } - - /** - * 更新 SOP 模板 - */ - public function update(User $user, SopTemplate $sopTemplate): bool - { - // 首先检查权限 - if (!$user->can('sop-template.update')) { - return false; - } - - // 已发布的模板不能直接编辑 - if ($sopTemplate->status === 'published') { - return false; - } - - return true; - } - - /** - * 删除 SOP 模板 - */ - public function delete(User $user, SopTemplate $sopTemplate): bool - { - // 首先检查权限 - if (!$user->can('sop-template.delete')) { - return false; - } - - // 已发布的模板不能删除 - if ($sopTemplate->status === 'published') { - return false; - } - - return true; - } - - /** - * 发布 SOP 模板 - */ - public function publish(User $user, SopTemplate $sopTemplate): bool - { - // 首先检查权限 - if (!$user->can('sop-template.publish')) { - return false; - } - - return $sopTemplate->status === 'draft'; - } - - /** - * 归档 SOP 模板 - */ - public function archive(User $user, SopTemplate $sopTemplate): bool - { - // 首先检查权限 - if (!$user->can('sop-template.archive')) { - return false; - } - - return $sopTemplate->status === 'published'; - } -} diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 6414718..4df46ae 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -3,9 +3,9 @@ namespace App\Providers; use App\Models\Document; -use App\Models\SopTemplate; +use App\Models\Guide; use App\Observers\DocumentObserver; -use App\Policies\SopTemplatePolicy; +use App\Policies\GuidePolicy; use Carbon\Carbon; use Illuminate\Support\Facades\Gate; use Illuminate\Support\ServiceProvider; @@ -34,7 +34,7 @@ class AppServiceProvider extends ServiceProvider // 注册策略 Gate::policy(\App\Models\Document::class, \App\Policies\DocumentPolicy::class); Gate::policy(\App\Models\Terminal::class, \App\Policies\TerminalPolicy::class); - Gate::policy(SopTemplate::class, SopTemplatePolicy::class); + Gate::policy(Guide::class, GuidePolicy::class); Gate::policy(\Spatie\Permission\Models\Role::class, \App\Policies\RolePolicy::class); Gate::policy(\App\Models\User::class, \App\Policies\UserPolicy::class); Gate::policy(\App\Models\SystemSetting::class, \App\Policies\SystemSettingPolicy::class); diff --git a/app/Services/DocumentSearchService.php b/app/Services/DocumentSearchService.php index d12d447..bc71b2e 100644 --- a/app/Services/DocumentSearchService.php +++ b/app/Services/DocumentSearchService.php @@ -41,6 +41,10 @@ class DocumentSearchService $searchBuilder->where('uploaded_by', $filters['uploaded_by']); } + if (!empty($filters['knowledge_base_id'])) { + $searchBuilder->where('knowledge_base_id', $filters['knowledge_base_id']); + } + // 执行搜索并获取结果 $results = $searchBuilder->get(); @@ -103,6 +107,7 @@ class DocumentSearchService 'markdown_content' => $document->getMarkdownContent(), 'type' => $document->type, 'group_id' => $document->group_id, + 'knowledge_base_id' => $document->knowledge_base_id, 'uploaded_by' => $document->uploaded_by, 'created_at' => $document->created_at?->timestamp, 'updated_at' => $document->updated_at?->timestamp, diff --git a/app/Services/KnowledgeContextService.php b/app/Services/KnowledgeContextService.php new file mode 100644 index 0000000..23e5de9 --- /dev/null +++ b/app/Services/KnowledgeContextService.php @@ -0,0 +1,94 @@ +knowledgeBases->pluck('id')->toArray(); + + if (empty($knowledgeBaseIds)) { + return [ + 'context' => '', + 'sources' => [], + ]; + } + + try { + // 使用 Scout/Meilisearch 原生过滤(与 DocumentSearchService 一致) + $documents = Document::search($query) + ->whereIn('knowledge_base_id', $knowledgeBaseIds) + ->take(self::TOP_K) + ->get(); + } catch (\Exception $e) { + Log::warning('Knowledge search failed', [ + 'query' => $query, + 'error' => $e->getMessage(), + ]); + + return [ + 'context' => '', + 'sources' => [], + ]; + } + + if ($documents->isEmpty()) { + return [ + 'context' => '', + 'sources' => [], + ]; + } + + $context = ''; + $sources = []; + + foreach ($documents as $document) { + $snippet = $this->extractSnippet($document); + + if (mb_strlen($context) + mb_strlen($snippet) > self::MAX_CONTEXT_LENGTH) { + break; + } + + $context .= $snippet . "\n\n"; + $sources[] = [ + 'id' => $document->id, + 'title' => $document->title, + 'knowledge_base' => $document->knowledgeBase?->name, + ]; + } + + return [ + 'context' => trim($context), + 'sources' => $sources, + ]; + } + + /** + * 从文档中提取摘要片段 + */ + private function extractSnippet($document): string + { + $content = $document->markdown_preview ?? $document->description ?? ''; + + if (mb_strlen($content) <= 500) { + return "【{$document->title}】\n{$content}"; + } + + return "【{$document->title}】\n" . mb_substr($content, 0, 500) . '...'; + } +} diff --git a/app/Services/SopTemplateService.php b/app/Services/SopTemplateService.php deleted file mode 100644 index 73aa53d..0000000 --- a/app/Services/SopTemplateService.php +++ /dev/null @@ -1,222 +0,0 @@ - [ - 'name' => $template->name, - 'description' => $template->description, - 'category' => $template->category, - 'tags' => $template->tags, - 'version' => $template->version, - 'applicable_departments' => $template->applicable_departments, - 'applicable_positions' => $template->applicable_positions, - ], - 'steps' => $template->steps->map(function ($step) { - return [ - 'step_number' => $step->step_number, - 'title' => $step->title, - 'content' => $step->content, - 'sort_order' => $step->sort_order, - 'is_required' => $step->is_required, - 'interactive_tasks' => $step->interactiveTasks->map(function ($task) { - return [ - 'task_type' => $task->task_type, - 'task_config' => $task->task_config, - 'validation_rules' => $task->validation_rules, - 'timeout_seconds' => $task->timeout_seconds, - 'is_required' => $task->is_required, - ]; - })->toArray(), - ]; - })->toArray(), - 'exported_at' => now()->toIso8601String(), - 'exported_by' => auth()->user()?->name, - ]; - - return json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); - } - - /** - * 从 JSON 导入模板 - * - * @param string $json - * @return SopTemplate - * @throws ValidationException - */ - public function importFromJson(string $json): SopTemplate - { - $data = json_decode($json, true); - - if (json_last_error() !== JSON_ERROR_NONE) { - throw ValidationException::withMessages([ - 'file' => ['无效的 JSON 格式'], - ]); - } - - // 验证数据结构 - $validator = Validator::make($data, [ - 'template' => 'required|array', - 'template.name' => 'required|string|max:255', - 'template.version' => 'required|string|max:50', - 'steps' => 'required|array|min:1', - 'steps.*.step_number' => 'required|integer', - 'steps.*.title' => 'required|string|max:255', - ]); - - if ($validator->fails()) { - throw new ValidationException($validator); - } - - return DB::transaction(function () use ($data) { - // 创建模板 - $template = SopTemplate::create([ - 'name' => $data['template']['name'], - 'description' => $data['template']['description'] ?? null, - 'category' => $data['template']['category'] ?? null, - 'tags' => $data['template']['tags'] ?? [], - 'version' => $data['template']['version'], - 'status' => 'draft', - 'applicable_departments' => $data['template']['applicable_departments'] ?? [], - 'applicable_positions' => $data['template']['applicable_positions'] ?? [], - 'created_by' => auth()->id(), - ]); - - // 创建步骤 - foreach ($data['steps'] as $stepData) { - $step = $template->steps()->create([ - 'step_number' => $stepData['step_number'], - 'title' => $stepData['title'], - 'content' => $stepData['content'] ?? null, - 'sort_order' => $stepData['sort_order'] ?? $stepData['step_number'], - 'is_required' => $stepData['is_required'] ?? true, - ]); - - // 创建交互任务 - if (!empty($stepData['interactive_tasks'])) { - foreach ($stepData['interactive_tasks'] as $taskData) { - $step->interactiveTasks()->create([ - 'task_type' => $taskData['task_type'], - 'task_config' => $taskData['task_config'] ?? [], - 'validation_rules' => $taskData['validation_rules'] ?? [], - 'timeout_seconds' => $taskData['timeout_seconds'] ?? null, - 'is_required' => $taskData['is_required'] ?? true, - ]); - } - } - } - - return $template; - }); - } - - /** - * 发布模板 - * - * @param SopTemplate $template - * @param string|null $changeLog - * @return void - */ - public function publish(SopTemplate $template, ?string $changeLog = null): void - { - // 创建版本快照 - $this->createVersion($template, $changeLog); - - // 更新状态 - $template->update([ - 'status' => 'published', - 'published_at' => now(), - ]); - } - - /** - * 创建版本快照 - * - * @param SopTemplate $template - * @param string|null $changeLog - * @return SopTemplateVersion - */ - public function createVersion(SopTemplate $template, ?string $changeLog = null): SopTemplateVersion - { - return SopTemplateVersion::create([ - 'sop_template_id' => $template->id, - 'version' => $template->version, - 'change_log' => $changeLog ?? '版本快照', - 'content_snapshot' => [ - 'template' => $template->toArray(), - 'steps' => $template->steps->map(function ($step) { - return array_merge($step->toArray(), [ - 'interactive_tasks' => $step->interactiveTasks->toArray(), - ]); - })->toArray(), - ], - 'created_by' => auth()->id(), - 'created_at' => now(), - ]); - } - - /** - * 归档模板 - * - * @param SopTemplate $template - * @return void - */ - public function archive(SopTemplate $template): void - { - $template->update([ - 'status' => 'archived', - ]); - } - - /** - * 复制模板 - * - * @param SopTemplate $template - * @param string $newName - * @return SopTemplate - */ - public function duplicate(SopTemplate $template, string $newName): SopTemplate - { - return DB::transaction(function () use ($template, $newName) { - // 复制模板 - $newTemplate = $template->replicate(); - $newTemplate->name = $newName; - $newTemplate->status = 'draft'; - $newTemplate->published_at = null; - $newTemplate->created_by = auth()->id(); - $newTemplate->save(); - - // 复制步骤 - foreach ($template->steps as $step) { - $newStep = $step->replicate(); - $newStep->sop_template_id = $newTemplate->id; - $newStep->save(); - - // 复制交互任务 - foreach ($step->interactiveTasks as $task) { - $newTask = $task->replicate(); - $newTask->sop_step_id = $newStep->id; - $newTask->save(); - } - } - - return $newTemplate; - }); - } -} diff --git a/bootstrap/app.php b/bootstrap/app.php index c183276..9d0399a 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -7,11 +7,14 @@ use Illuminate\Foundation\Configuration\Middleware; return Application::configure(basePath: dirname(__DIR__)) ->withRouting( web: __DIR__.'/../routes/web.php', + api: __DIR__.'/../routes/api.php', commands: __DIR__.'/../routes/console.php', health: '/up', ) ->withMiddleware(function (Middleware $middleware): void { - // + $middleware->alias([ + 'identify.terminal' => \App\Http\Middleware\IdentifyTerminal::class, + ]); }) ->withExceptions(function (Exceptions $exceptions): void { // diff --git a/composer.json b/composer.json index 55b91ac..e6fa0b4 100644 --- a/composer.json +++ b/composer.json @@ -19,6 +19,7 @@ "maatwebsite/excel": "^3.1", "meilisearch/meilisearch-php": "^1.16", "phpoffice/phpword": "^1.4", + "solution-forest/filament-tree": "^2.0", "spatie/laravel-activitylog": "^4.12", "spatie/laravel-permission": "^6.24" }, diff --git a/composer.lock b/composer.lock index b36d322..387efe6 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "297bfa700eadd90d170fd39edd1c790d", + "content-hash": "316d29450fc96f2d79ed216639e6cfb2", "packages": [ { "name": "abdelhamiderrahmouni/filament-monaco-editor", @@ -6092,6 +6092,76 @@ ], "time": "2025-02-25T09:09:36+00:00" }, + { + "name": "solution-forest/filament-tree", + "version": "2.1.8", + "source": { + "type": "git", + "url": "https://github.com/solutionforest/filament-tree.git", + "reference": "de8b27c7c58f1e8c8e1a3081dff2e477b4327301" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/solutionforest/filament-tree/zipball/de8b27c7c58f1e8c8e1a3081dff2e477b4327301", + "reference": "de8b27c7c58f1e8c8e1a3081dff2e477b4327301", + "shasum": "" + }, + "require": { + "filament/filament": "^3.0", + "filament/support": "^3.0", + "php": "^8.1", + "spatie/laravel-package-tools": "^1.15.0" + }, + "require-dev": { + "laravel/pint": "^1.0", + "nunomaduro/collision": "^7.9", + "nunomaduro/larastan": "^2.0.1", + "orchestra/testbench": "^8.0", + "pestphp/pest": "^2.0", + "pestphp/pest-plugin-arch": "^2.0", + "pestphp/pest-plugin-laravel": "^2.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1.0", + "spatie/laravel-ray": "^1.26" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "SolutionForest\\FilamentTree\\FilamentTreeServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "SolutionForest\\FilamentTree\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Carly", + "email": "info@solutionforest.net", + "role": "Developer" + } + ], + "description": "This is a tree layout plugin for Filament Admin", + "homepage": "https://github.com/solution-forest/filament-tree", + "keywords": [ + "Solution Forest", + "filament-tree", + "laravel" + ], + "support": { + "issues": "https://github.com/solution-forest/filament-tree/issues", + "source": "https://github.com/solution-forest/filament-tree" + }, + "time": "2025-08-11T09:35:33+00:00" + }, { "name": "spatie/color", "version": "1.8.0", @@ -11593,16 +11663,16 @@ }, { "name": "laravel/pint", - "version": "v1.28.0", + "version": "v1.29.0", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "1feae84bf9c1649d99ba8f7b8193bf0f09f04cc9" + "reference": "bdec963f53172c5e36330f3a400604c69bf02d39" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/1feae84bf9c1649d99ba8f7b8193bf0f09f04cc9", - "reference": "1feae84bf9c1649d99ba8f7b8193bf0f09f04cc9", + "url": "https://api.github.com/repos/laravel/pint/zipball/bdec963f53172c5e36330f3a400604c69bf02d39", + "reference": "bdec963f53172c5e36330f3a400604c69bf02d39", "shasum": "" }, "require": { @@ -11619,8 +11689,8 @@ "laravel-zero/framework": "^12.0.5", "mockery/mockery": "^1.6.12", "nunomaduro/termwind": "^2.4.0", - "pestphp/pest": "^3.8.5", - "shipfastlabs/agent-detector": "^1.0.2" + "pestphp/pest": "^3.8.6", + "shipfastlabs/agent-detector": "^1.1.0" }, "bin": [ "builds/pint" @@ -11657,7 +11727,7 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2026-03-10T20:37:18+00:00" + "time": "2026-03-12T15:51:39+00:00" }, { "name": "laravel/sail", diff --git a/config/prompt_templates.php b/config/prompt_templates.php index 5ec3020..ad24ba6 100644 --- a/config/prompt_templates.php +++ b/config/prompt_templates.php @@ -14,37 +14,78 @@ return [ [ 'id' => 'general_assistant', 'name' => '通用助手', - 'description' => '适用于一般性问答和操作指导的AI助手', + 'description' => '同步辐射光束线站通用AI助手,集成知识库检索和交互式操作引导', 'category' => 'general', 'content' => <<<'TEMPLATE' -# AI助手角色定义 +# 角色 -你是一个专业的工业生产助手,为 {company_name} 的员工提供帮助。 +你是{station_id}光束线站的AI助手,运行在操作终端「{terminal_name}」上。你的使命是帮助用户安全、高效地完成光束线实验和操作。 -## 当前上下文 -- 用户:{user}({user_role}) -- 工作站:{station} -- 终端:{terminal_name} +## 当前会话上下文 + +- 用户:{user} +- 光束线站:{station_id} +- 操作终端:{terminal_name}({terminal_code}) - 时间:{time} - 班次:{shift} +- 可用知识库:{knowledge_bases} -## 你的职责 -1. 回答用户关于生产流程、设备操作的问题 -2. 提供安全操作指导 -3. 协助查找相关文档和资料 -4. 记录和反馈异常情况 +## 工具使用策略 -## 知识库 -你可以参考以下知识库:{knowledge_bases} +你有两个工具可以调用。**必须主动使用**,不要凭记忆回答专业问题。 -## 回答原则 -- 使用简洁、专业的语言 -- 优先考虑安全性 -- 如果不确定,建议咨询专业人员 -- 保持友好和耐心的态度 +### search_knowledge — 知识库检索 + +**何时调用**: +- 用户询问操作规程、设备参数、技术指标、安全规范 +- 需要确认具体数值(能量范围、分辨率、束斑尺寸等) +- 涉及标准流程或规章制度 +- 你不确定某个专业细节时 + +**使用要点**: +- 提取用户问题的核心概念作为搜索关键词,优先使用专业术语 +- 如果首次搜索结果不理想,换用同义词或上下位概念重新搜索 +- 回答时基于检索到的内容作答,注明信息来源 + +### show_guide — 交互式操作引导 + +**何时调用**: +- 用户需要分步操作指导(如"怎么换样品"、"如何调节能量") +- 遇到故障需要排查流程 +- 新用户需要入门引导 +- 任何涉及多步骤、有安全风险的操作 + +**使用要点**: +- 可以组合多个指引 ID 按执行顺序调用 +- 在 reason 中简要说明触发原因,帮助用户理解 +- 指引完成后,根据用户的选择结果提供针对性的后续建议 +- 如果用户在指引中选择了异常分支,主动追问详情并给出进一步处理建议 + +## 回答规范 + +### 安全准则(最高优先级) +- **辐射安全**:涉及进出实验大厅、打开光闸、联锁系统的操作,必须提醒安全要求 +- **真空安全**:涉及破真空、换窗片、样品装卸时,必须确认真空状态和操作顺序 +- **电气安全**:涉及高压设备、电源操作时,提醒断电和接地要求 +- **危险操作拦截**:如果用户描述的操作可能导致设备损坏或人身伤害,先给出警告,建议联系线站负责人确认后再操作 +- 如果你不确定某个操作是否安全,明确告知用户"建议联系线站工作人员确认" + +### 对话风格 +- 使用简洁专业的语言,避免冗长的铺垫 +- 对操作类问题,给出明确的步骤而非笼统建议 +- 对参数类问题,给出具体数值和单位 +- 如果问题超出你的知识范围,坦诚告知并建议联系线站负责人 +- 考虑用户角色:对经验丰富的操作员可以更简练,对访客和新用户需要更详细的解释 + +### 问题分类处理 +1. **快速查询**(参数、状态、简单事实)→ 先调用 search_knowledge 获取准确信息,直接回答 +2. **操作指导**(需要分步操作)→ 调用 show_guide 提供交互式引导 +3. **故障排查**(设备异常、报警处理)→ 先调用 search_knowledge 了解可能原因,再用 show_guide 引导排查流程 +4. **实验咨询**(方案设计、参数优化)→ 调用 search_knowledge 获取相关资料,结合专业知识给出建议 +5. **闲聊或非业务问题** → 简短友好地回应,引导回光束线相关话题 TEMPLATE ], - + [ 'id' => 'safety_focused', 'name' => '安全专员', @@ -78,7 +119,7 @@ TEMPLATE ⚠️ 安全提示:如有任何疑问,请立即停止操作并联系安全主管! TEMPLATE ], - + [ 'id' => 'troubleshooting', 'name' => '故障诊断', @@ -115,7 +156,7 @@ TEMPLATE 💡 提示:详细描述故障现象有助于快速定位问题 TEMPLATE ], - + [ 'id' => 'training_coach', 'name' => '培训教练', @@ -157,7 +198,7 @@ TEMPLATE 📚 学习提示:不要着急,每个人都有学习过程,慢慢来! TEMPLATE ], - + [ 'id' => 'quality_inspector', 'name' => '质量检查', diff --git a/config/scout.php b/config/scout.php index 5fd08a6..a856036 100644 --- a/config/scout.php +++ b/config/scout.php @@ -141,10 +141,10 @@ return [ 'key' => env('MEILISEARCH_KEY'), 'index-settings' => [ 'documents' => [ - 'filterableAttributes' => ['type', 'group_id', 'uploaded_by', 'conversion_status'], + 'filterableAttributes' => ['type', 'group_id', 'knowledge_base_id', 'uploaded_by', 'conversion_status'], 'sortableAttributes' => ['created_at', 'title', 'updated_at'], 'searchableAttributes' => ['title', 'description', 'markdown_content'], - 'displayedAttributes' => ['id', 'title', 'description', 'type', 'group_id', 'uploaded_by', 'created_at', 'updated_at'], + 'displayedAttributes' => ['id', 'title', 'description', 'type', 'group_id', 'knowledge_base_id', 'uploaded_by', 'created_at', 'updated_at'], ], ], ], diff --git a/database/factories/SopTemplateFactory.php b/database/factories/SopTemplateFactory.php deleted file mode 100644 index 8ffca3c..0000000 --- a/database/factories/SopTemplateFactory.php +++ /dev/null @@ -1,61 +0,0 @@ - - */ -class SopTemplateFactory extends Factory -{ - protected $model = SopTemplate::class; - - public function definition(): array - { - return [ - 'name' => fake()->randomElement([ - '设备启动操作规程', - '产品质检标准流程', - '安全生产检查清单', - '设备维护保养流程', - '应急处理操作指南', - ]) . '-' . fake()->numberBetween(1, 100), - 'description' => fake()->sentence(20), - 'category' => fake()->randomElement(['生产操作', '质量管理', '安全管理', '设备维护', '应急处理']), - 'tags' => fake()->randomElements(['标准作业', '安全', '质量', '效率', '培训'], fake()->numberBetween(1, 3)), - 'version' => '1.0.0', - 'status' => fake()->randomElement(['draft', 'published', 'archived']), - 'applicable_departments' => fake()->randomElements(['生产部', '质检部', '设备部', '安全部'], fake()->numberBetween(1, 2)), - 'applicable_positions' => fake()->randomElements(['操作员', '质检员', '班组长', '技术员'], fake()->numberBetween(1, 2)), - 'published_at' => fake()->optional(0.6)->dateTimeBetween('-6 months', 'now'), - 'created_by' => User::factory(), - ]; - } - - public function draft(): static - { - return $this->state(fn (array $attributes) => [ - 'status' => 'draft', - 'published_at' => null, - ]); - } - - public function published(): static - { - return $this->state(fn (array $attributes) => [ - 'status' => 'published', - 'published_at' => now(), - ]); - } - - public function archived(): static - { - return $this->state(fn (array $attributes) => [ - 'status' => 'archived', - 'published_at' => fake()->dateTimeBetween('-1 year', '-1 month'), - ]); - } -} diff --git a/database/migrations/2026_03_02_014623_create_terminals_table.php b/database/migrations/2026_03_02_014623_create_terminals_table.php index 378824d..886b046 100644 --- a/database/migrations/2026_03_02_014623_create_terminals_table.php +++ b/database/migrations/2026_03_02_014623_create_terminals_table.php @@ -16,7 +16,7 @@ return new class extends Migration $table->string('name')->comment('终端名称'); $table->string('code', 100)->unique()->comment('终端编码'); $table->string('ip_address', 45)->nullable()->comment('IP地址'); - $table->unsignedBigInteger('station_id')->nullable()->comment('线站ID'); + $table->string('station_id', 50)->nullable()->comment('线站ID'); $table->string('diagram_url', 500)->nullable()->comment('组态图URL'); $table->json('display_config')->nullable()->comment('显示配置'); $table->boolean('is_online')->default(false)->comment('在线状态'); diff --git a/database/migrations/2026_03_02_015106_create_sop_templates_table.php b/database/migrations/2026_03_02_015106_create_sop_templates_table.php deleted file mode 100644 index 8af29ed..0000000 --- a/database/migrations/2026_03_02_015106_create_sop_templates_table.php +++ /dev/null @@ -1,42 +0,0 @@ -id(); - $table->string('name')->comment('模板名称'); - $table->text('description')->nullable()->comment('模板描述'); - $table->string('category', 100)->nullable()->comment('分类'); - $table->json('tags')->nullable()->comment('标签'); - $table->string('version', 50)->default('1.0.0')->comment('版本号'); - $table->enum('status', ['draft', 'published', 'archived'])->default('draft')->comment('状态'); - $table->json('applicable_departments')->nullable()->comment('适用部门'); - $table->json('applicable_positions')->nullable()->comment('适用岗位'); - $table->timestamp('published_at')->nullable()->comment('发布时间'); - $table->unsignedBigInteger('created_by')->nullable()->comment('创建人'); - $table->timestamps(); - $table->softDeletes(); - - // 添加索引 - $table->index('status', 'idx_sop_templates_status'); - $table->index('category', 'idx_sop_templates_category'); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::dropIfExists('sop_templates'); - } -}; diff --git a/database/migrations/2026_03_02_015107_create_sop_steps_table.php b/database/migrations/2026_03_02_015107_create_sop_steps_table.php deleted file mode 100644 index 4967e77..0000000 --- a/database/migrations/2026_03_02_015107_create_sop_steps_table.php +++ /dev/null @@ -1,42 +0,0 @@ -id(); - $table->unsignedBigInteger('sop_template_id')->comment('模板ID'); - $table->integer('step_number')->comment('步骤序号'); - $table->string('title')->comment('步骤标题'); - $table->text('content')->nullable()->comment('步骤内容'); - $table->integer('sort_order')->default(0)->comment('排序'); - $table->boolean('is_required')->default(true)->comment('是否必需'); - $table->timestamps(); - - // 添加外键约束 - $table->foreign('sop_template_id') - ->references('id') - ->on('sop_templates') - ->onDelete('cascade'); - - // 添加索引 - $table->index(['sop_template_id', 'sort_order'], 'idx_template_sort'); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::dropIfExists('sop_steps'); - } -}; diff --git a/database/migrations/2026_03_02_015108_create_sop_interactive_tasks_table.php b/database/migrations/2026_03_02_015108_create_sop_interactive_tasks_table.php deleted file mode 100644 index 6b7caf5..0000000 --- a/database/migrations/2026_03_02_015108_create_sop_interactive_tasks_table.php +++ /dev/null @@ -1,39 +0,0 @@ -id(); - $table->unsignedBigInteger('sop_step_id')->comment('步骤ID'); - $table->enum('task_type', ['confirm', 'input', 'select', 'photo', 'scan'])->comment('任务类型'); - $table->json('task_config')->nullable()->comment('任务配置'); - $table->json('validation_rules')->nullable()->comment('验证规则'); - $table->integer('timeout_seconds')->nullable()->comment('超时时间'); - $table->boolean('is_required')->default(true)->comment('是否必需'); - $table->timestamps(); - - // 添加外键约束 - $table->foreign('sop_step_id') - ->references('id') - ->on('sop_steps') - ->onDelete('cascade'); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::dropIfExists('sop_interactive_tasks'); - } -}; diff --git a/database/migrations/2026_03_02_015109_create_sop_template_versions_table.php b/database/migrations/2026_03_02_015109_create_sop_template_versions_table.php deleted file mode 100644 index 822c98d..0000000 --- a/database/migrations/2026_03_02_015109_create_sop_template_versions_table.php +++ /dev/null @@ -1,41 +0,0 @@ -id(); - $table->unsignedBigInteger('sop_template_id')->comment('模板ID'); - $table->string('version', 50)->comment('版本号'); - $table->text('change_log')->nullable()->comment('变更说明'); - $table->json('content_snapshot')->nullable()->comment('内容快照'); - $table->unsignedBigInteger('created_by')->nullable()->comment('创建人'); - $table->timestamp('created_at')->nullable(); - - // 添加外键约束 - $table->foreign('sop_template_id') - ->references('id') - ->on('sop_templates') - ->onDelete('cascade'); - - // 添加索引 - $table->index(['sop_template_id', 'version'], 'idx_template_version'); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::dropIfExists('sop_template_versions'); - } -}; diff --git a/database/migrations/2026_03_12_051332_update_permissions_naming.php b/database/migrations/2026_03_12_051332_update_permissions_naming.php index 328aa63..fa7bf8e 100644 --- a/database/migrations/2026_03_12_051332_update_permissions_naming.php +++ b/database/migrations/2026_03_12_051332_update_permissions_naming.php @@ -24,8 +24,8 @@ return new class extends Migration // 终端管理 'terminal.viewAny' => 'terminal.view', - // SOP模板 - 'sop-template.viewAny' => 'sop-template.view', + // 操作指引 + 'guide.viewAny' => 'guide.view', // 分组管理 'group.viewAny' => 'group.view', @@ -78,7 +78,7 @@ return new class extends Migration 'system-setting.view', 'activity-log.view', 'terminal.view', - 'sop-template.view', + 'guide.view', 'group.view', 'user.view', 'role.view', diff --git a/database/migrations/2026_03_13_010100_create_guides_table.php b/database/migrations/2026_03_13_010100_create_guides_table.php new file mode 100644 index 0000000..1b5402b --- /dev/null +++ b/database/migrations/2026_03_13_010100_create_guides_table.php @@ -0,0 +1,60 @@ +id(); + $table->string('name')->comment('指引名称'); + $table->text('description')->nullable()->comment('指引描述'); + $table->string('category', 50)->default('operation')->comment('分类: operation/fault_handling/training'); + $table->json('tags')->nullable()->comment('标签'); + $table->string('status', 20)->default('draft')->comment('状态: draft/published/archived'); + $table->foreignId('created_by')->nullable()->constrained('users')->nullOnDelete(); + $table->timestamp('published_at')->nullable(); + $table->timestamps(); + $table->softDeletes(); + + $table->index('category'); + $table->index('status'); + }); + + Schema::create('guide_pages', function (Blueprint $table) { + $table->id(); + $table->foreignId('guide_id')->constrained()->cascadeOnDelete(); + $table->unsignedInteger('page_number')->comment('页码'); + $table->string('title')->comment('页面标题'); + $table->string('html_url', 500)->comment('HTML页面链接'); + $table->integer('parent_id')->default(-1); + $table->unsignedInteger('sort_order')->default(0)->comment('排序'); + $table->json('options')->nullable(); + $table->string('branch_option', 100)->nullable(); + $table->timestamps(); + + $table->index('parent_id'); + $table->index(['guide_id', 'sort_order']); + }); + + Schema::create('terminal_guides', function (Blueprint $table) { + $table->id(); + $table->foreignId('terminal_id')->constrained()->cascadeOnDelete(); + $table->foreignId('guide_id')->constrained()->cascadeOnDelete(); + $table->integer('priority')->default(0); + $table->timestamps(); + + $table->unique(['terminal_id', 'guide_id'], 'uk_terminal_guide'); + }); + } + + public function down(): void + { + Schema::dropIfExists('terminal_guides'); + Schema::dropIfExists('guide_pages'); + Schema::dropIfExists('guides'); + } +}; diff --git a/database/migrations/2026_03_13_010200_add_terminal_fields.php b/database/migrations/2026_03_13_010200_add_terminal_fields.php new file mode 100644 index 0000000..5d1d975 --- /dev/null +++ b/database/migrations/2026_03_13_010200_add_terminal_fields.php @@ -0,0 +1,27 @@ +string('mac_address', 17)->nullable()->unique()->after('ip_address') + ->comment('MAC地址 (AA:BB:CC:DD:EE:FF)'); + $table->string('scada_data_url', 500)->nullable()->after('diagram_url') + ->comment('OPC UA网关数据查询地址'); + $table->string('scada_tags_url', 500)->nullable()->after('scada_data_url') + ->comment('OPC UA网关点位定义查询地址'); + }); + } + + public function down(): void + { + Schema::table('terminals', function (Blueprint $table) { + $table->dropColumn(['mac_address', 'scada_data_url', 'scada_tags_url']); + }); + } +}; diff --git a/database/migrations/2026_03_13_060000_add_knowledge_base_id_to_documents_table.php b/database/migrations/2026_03_13_060000_add_knowledge_base_id_to_documents_table.php new file mode 100644 index 0000000..a37d9a6 --- /dev/null +++ b/database/migrations/2026_03_13_060000_add_knowledge_base_id_to_documents_table.php @@ -0,0 +1,29 @@ +foreignId('knowledge_base_id') + ->nullable() + ->after('group_id') + ->constrained('knowledge_bases') + ->nullOnDelete(); + + $table->index('knowledge_base_id'); + }); + } + + public function down(): void + { + Schema::table('documents', function (Blueprint $table) { + $table->dropForeign(['knowledge_base_id']); + $table->dropColumn('knowledge_base_id'); + }); + } +}; diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index 1cbde0a..12b24d1 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -33,7 +33,7 @@ class DatabaseSeeder extends Seeder 'email' => 'admin@example.com', 'password' => Hash::make('TRG}E^5BvPcbyErc'), ]); - + // 为管理员分配 super-admin 角色 $admin->assignRole('super-admin'); @@ -218,13 +218,13 @@ class DatabaseSeeder extends Seeder ]); $this->command->info('演示数据生成完成!'); - + // 9. 创建终端数据 $this->call(TerminalSeeder::class); - - // 10. 创建SOP模板数据 - $this->call(SopTemplateSeeder::class); - + + // 10. 创建操作指引数据 + $this->call(GuideSeeder::class); + $this->command->newLine(); $this->command->info('=== 生成的数据摘要 ==='); $this->command->info('用户数量: ' . User::count()); diff --git a/database/seeders/GuideSeeder.php b/database/seeders/GuideSeeder.php new file mode 100644 index 0000000..636094b --- /dev/null +++ b/database/seeders/GuideSeeder.php @@ -0,0 +1,219 @@ +command->info('开始创建操作指引数据...'); + + $admin = User::where('email', 'admin@example.com')->first(); + $terminals = Terminal::all(); + + // 1. 如何用光(带分支) + $guide1 = $this->createHowToUseBeamGuide($admin); + + // 2. 真空阀门故障处理 + $guide2 = $this->createVacuumValveIssueGuide($admin); + + // 3. 漏水报警处理 + $guide3 = $this->createWaterLeakAlarmGuide($admin); + + // 将所有指引关联到所有终端 + $this->command->info('关联指引到所有终端...'); + foreach ($terminals as $terminal) { + $terminal->guides()->attach([ + $guide1->id => ['priority' => 1], + $guide2->id => ['priority' => 2], + $guide3->id => ['priority' => 3], + ]); + } + + $this->command->info('操作指引数据创建完成!'); + $this->command->info(' - 指引数量: ' . Guide::count()); + $this->command->info(' - 指引页面数量: ' . GuidePage::count()); + $this->command->info(' - 关联终端数量: ' . $terminals->count()); + } + + private function createHowToUseBeamGuide(User $admin): Guide + { + $this->command->info('创建指引: 如何用光...'); + + $guide = Guide::create([ + 'name' => '如何用光', + 'description' => '光束线用光操作完整流程指引,包含前门12和后门两条路径', + 'category' => 'operation', + 'tags' => ['用光', '光闸', 'PS1', '光学棚屋'], + 'status' => 'published', + 'created_by' => $admin->id, + 'published_at' => now(), + ]); + + $baseUrl = self::BASE_URL . '/how-to-use-beam'; + + // 步骤1: 打开光子光闸 PS1(根节点) + $step1 = GuidePage::create([ + 'guide_id' => $guide->id, + 'page_number' => 1, + 'title' => '打开光子光闸 PS1', + 'html_url' => "{$baseUrl}/step-1.html", + 'parent_id' => -1, + 'sort_order' => 0, + ]); + + // 步骤2: 搜索光学棚屋(带选项:前门12 / 后门) + $step2 = GuidePage::create([ + 'guide_id' => $guide->id, + 'page_number' => 2, + 'title' => '搜索光学棚屋', + 'html_url' => "{$baseUrl}/step-2.html", + 'parent_id' => $step1->id, + 'sort_order' => 1, + 'options' => ['前门12', '后门'], + ]); + + // 步骤3a: 前门12路径 - 检查设备状态 + $step3a = GuidePage::create([ + 'guide_id' => $guide->id, + 'page_number' => 3, + 'title' => '前门12路径 - 检查设备状态', + 'html_url' => "{$baseUrl}/step-3a.html", + 'parent_id' => $step2->id, + 'sort_order' => 0, + 'branch_option' => '前门12', + ]); + + // 步骤3b: 后门路径 - 安全确认 + $step3b = GuidePage::create([ + 'guide_id' => $guide->id, + 'page_number' => 3, + 'title' => '后门路径 - 安全确认', + 'html_url' => "{$baseUrl}/step-3b.html", + 'parent_id' => $step2->id, + 'sort_order' => 1, + 'branch_option' => '后门', + ]); + + // 步骤4a: 前门12路径 - 打开实验站光闸 + GuidePage::create([ + 'guide_id' => $guide->id, + 'page_number' => 4, + 'title' => '前门12路径 - 打开实验站光闸', + 'html_url' => "{$baseUrl}/step-4a.html", + 'parent_id' => $step3a->id, + 'sort_order' => 0, + ]); + + // 步骤4b: 后门路径 - 设备检查 + GuidePage::create([ + 'guide_id' => $guide->id, + 'page_number' => 4, + 'title' => '后门路径 - 设备检查', + 'html_url' => "{$baseUrl}/step-4b.html", + 'parent_id' => $step3b->id, + 'sort_order' => 0, + ]); + + // 步骤5: 完成(根节点,最终汇合) + GuidePage::create([ + 'guide_id' => $guide->id, + 'page_number' => 5, + 'title' => '完成', + 'html_url' => "{$baseUrl}/step-5.html", + 'parent_id' => -1, + 'sort_order' => 1, + ]); + + return $guide; + } + + private function createVacuumValveIssueGuide(User $admin): Guide + { + $this->command->info('创建指引: 真空阀门故障处理...'); + + $guide = Guide::create([ + 'name' => '真空阀门故障处理', + 'description' => '真空阀门异常时的排查和处理流程', + 'category' => 'fault_handling', + 'tags' => ['真空', '阀门', '故障', '联锁', '气动'], + 'status' => 'published', + 'created_by' => $admin->id, + 'published_at' => now(), + ]); + + $baseUrl = self::BASE_URL . '/vacuum-valve-issue'; + $steps = [ + ['title' => '检查真空度', 'file' => 'step-1.html'], + ['title' => '检查联锁状态', 'file' => 'step-2.html'], + ['title' => '尝试手动复位', 'file' => 'step-3.html'], + ['title' => '检查气动系统', 'file' => 'step-4.html'], + ['title' => '联系维护人员', 'file' => 'step-5.html'], + ]; + + $parentId = -1; + foreach ($steps as $i => $step) { + $page = GuidePage::create([ + 'guide_id' => $guide->id, + 'page_number' => $i + 1, + 'title' => $step['title'], + 'html_url' => "{$baseUrl}/{$step['file']}", + 'parent_id' => $parentId, + 'sort_order' => $parentId === -1 ? $i : 0, + ]); + $parentId = $page->id; + } + + return $guide; + } + + private function createWaterLeakAlarmGuide(User $admin): Guide + { + $this->command->info('创建指引: 漏水报警处理...'); + + $guide = Guide::create([ + 'name' => '漏水报警处理', + 'description' => '漏水报警时的应急处理和复位流程', + 'category' => 'fault_handling', + 'tags' => ['漏水', '报警', '应急', '复位'], + 'status' => 'published', + 'created_by' => $admin->id, + 'published_at' => now(), + ]); + + $baseUrl = self::BASE_URL . '/water-leak-alarm'; + $steps = [ + ['title' => '确认报警位置', 'file' => 'step-1.html'], + ['title' => '搜索光学棚屋', 'file' => 'step-2.html'], + ['title' => '定位并处理漏水点', 'file' => 'step-3.html'], + ['title' => '复位报警', 'file' => 'step-4.html'], + ['title' => '完成', 'file' => 'step-5.html'], + ]; + + $parentId = -1; + foreach ($steps as $i => $step) { + $page = GuidePage::create([ + 'guide_id' => $guide->id, + 'page_number' => $i + 1, + 'title' => $step['title'], + 'html_url' => "{$baseUrl}/{$step['file']}", + 'parent_id' => $parentId, + 'sort_order' => $parentId === -1 ? $i : 0, + ]); + $parentId = $page->id; + } + + return $guide; + } +} diff --git a/database/seeders/PermissionSeeder.php b/database/seeders/PermissionSeeder.php index 0b76a6a..78549e4 100644 --- a/database/seeders/PermissionSeeder.php +++ b/database/seeders/PermissionSeeder.php @@ -40,13 +40,13 @@ class PermissionSeeder extends Seeder 'terminal.delete' => '删除终端', 'terminal.sync' => '同步终端配置', - // SOP模板权限 - 'sop-template.view' => '查看SOP模板', - 'sop-template.create' => '创建SOP', - 'sop-template.update' => '编辑SOP', - 'sop-template.delete' => '删除SOP', - 'sop-template.publish' => '发布SOP', - 'sop-template.archive' => '归档SOP', + // 操作指引权限 + 'guide.view' => '查看指引', + 'guide.create' => '创建指引', + 'guide.update' => '编辑指引', + 'guide.delete' => '删除指引', + 'guide.publish' => '发布指引', + 'guide.archive' => '归档指引', // 分组管理权限 'group.view' => '查看分组', @@ -129,13 +129,13 @@ class PermissionSeeder extends Seeder 'terminal.delete', 'terminal.sync', - // SOP模板 - 'sop-template.view', - 'sop-template.create', - 'sop-template.update', - 'sop-template.delete', - 'sop-template.publish', - 'sop-template.archive', + // 操作指引 + 'guide.view', + 'guide.create', + 'guide.update', + 'guide.delete', + 'guide.publish', + 'guide.archive', // 分组管理 'group.view', @@ -173,8 +173,8 @@ class PermissionSeeder extends Seeder // 终端管理(仅查看) 'terminal.view', - // SOP模板(仅查看) - 'sop-template.view', + // 操作指引(仅查看) + 'guide.view', // 分组管理(仅查看) 'group.view', diff --git a/database/seeders/SopTemplateSeeder.php b/database/seeders/SopTemplateSeeder.php deleted file mode 100644 index a3e70db..0000000 --- a/database/seeders/SopTemplateSeeder.php +++ /dev/null @@ -1,237 +0,0 @@ -command->info('开始创建SOP模板数据...'); - - // 获取或创建一个用户作为创建者 - $user = User::first(); - if (!$user) { - $this->command->warn('未找到用户,跳过SOP模板创建'); - return; - } - - // 1. 光束线开机流程 - $this->command->info('创建光束线开机流程...'); - $template1 = SopTemplate::create([ - 'name' => '光束线标准开机流程', - 'description' => '本流程规定了光束线开机前的检查项目、开机步骤和注意事项,确保光束线安全、正常启动。', - 'category' => '光束线操作', - 'tags' => ['标准作业', '开机流程', '安全'], - 'version' => '1.0.0', - 'status' => 'published', - 'applicable_departments' => ['BL02U1', 'BL07U', 'BL08U', 'BL13HB', 'BL13U', 'BL14B', 'BL14W', 'BL15U', 'BL16B', 'BL16U1'], - 'applicable_positions' => ['操作员', '值班员'], - 'published_at' => now()->subMonths(2), - 'created_by' => $user->id, - ]); - - $steps1 = [ - [ - 'step_number' => 1, - 'title' => '开机前安全检查', - 'content' => '

检查光束线周围环境,确保无障碍物和安全隐患。

  • 检查光束线外观是否完好
  • 检查安全联锁装置是否正常
  • 检查急停按钮是否正常
  • 确认辐射防护门关闭
', - 'sort_order' => 1, - 'is_required' => true, - ], - [ - 'step_number' => 2, - 'title' => '真空系统检查', - 'content' => '

检查真空系统状态。

  • 确认真空泵运行正常
  • 检查真空度读数
  • 检查真空阀门状态
', - 'sort_order' => 2, - 'is_required' => true, - ], - [ - 'step_number' => 3, - 'title' => '冷却水系统检查', - 'content' => '

检查冷却水系统。

  • 确认冷却水流量正常
  • 检查水温是否在正常范围
  • 检查冷却水压力
', - 'sort_order' => 3, - 'is_required' => true, - ], - [ - 'step_number' => 4, - 'title' => '启动光束线', - 'content' => '

按照正确顺序启动光束线。

  1. 打开控制系统
  2. 等待系统自检完成
  3. 启动束流
  4. 观察束流参数
', - 'sort_order' => 4, - 'is_required' => true, - ], - [ - 'step_number' => 5, - 'title' => '运行状态确认', - 'content' => '

确认光束线正常运行。

  • 检查束流强度
  • 检查束流位置
  • 检查各项参数是否在正常范围
', - 'sort_order' => 5, - 'is_required' => true, - ], - ]; - - foreach ($steps1 as $stepData) { - $step = SopStep::create(array_merge($stepData, ['sop_template_id' => $template1->id])); - - // 为第1步添加确认任务 - if ($stepData['step_number'] == 1) { - SopInteractiveTask::create([ - 'sop_step_id' => $step->id, - 'task_type' => 'confirm', - 'task_config' => [ - 'title' => '安全检查确认', - 'message' => '我已完成所有安全检查项目,确认无安全隐患', - ], - 'validation_rules' => [], - 'timeout_seconds' => 300, - 'is_required' => true, - ]); - } - - // 为第4步添加拍照任务 - if ($stepData['step_number'] == 4) { - SopInteractiveTask::create([ - 'sop_step_id' => $step->id, - 'task_type' => 'photo', - 'task_config' => [ - 'title' => '拍摄控制系统状态', - 'message' => '请拍摄控制系统界面照片', - 'min_photos' => 1, - 'max_photos' => 3, - ], - 'validation_rules' => [], - 'timeout_seconds' => 180, - 'is_required' => true, - ]); - } - } - - // 2. 用户实验准备流程 - $this->command->info('创建用户实验准备流程...'); - $template2 = SopTemplate::create([ - 'name' => '用户实验准备标准流程', - 'description' => '本流程规定了用户实验前的准备工作、样品安装和参数设置步骤。', - 'category' => '实验操作', - 'tags' => ['用户实验', '标准作业', '样品准备'], - 'version' => '1.0.0', - 'status' => 'published', - 'applicable_departments' => ['BL02U1', 'BL07U', 'BL08U', 'BL13HB', 'BL13U', 'BL14B', 'BL14W', 'BL15U', 'BL16B', 'BL16U1'], - 'applicable_positions' => ['操作员', '实验员'], - 'published_at' => now()->subMonth(), - 'created_by' => $user->id, - ]); - - $steps2 = [ - [ - 'step_number' => 1, - 'title' => '扫描用户机时单', - 'content' => '

使用扫码枪扫描用户机时单二维码,获取实验信息。

', - 'sort_order' => 1, - 'is_required' => true, - ], - [ - 'step_number' => 2, - 'title' => '样品安全检查', - 'content' => '

检查样品安全性。

  • 确认样品无放射性
  • 确认样品无毒性
  • 确认样品符合实验要求
', - 'sort_order' => 2, - 'is_required' => true, - ], - [ - 'step_number' => 3, - 'title' => '样品安装', - 'content' => '

将样品安装到样品台。

  • 调整样品位置
  • 固定样品
  • 对准光束中心
', - 'sort_order' => 3, - 'is_required' => true, - ], - [ - 'step_number' => 4, - 'title' => '实验参数设置', - 'content' => '

在控制系统中设置实验参数。

', - 'sort_order' => 4, - 'is_required' => true, - ], - ]; - - foreach ($steps2 as $stepData) { - $step = SopStep::create(array_merge($stepData, ['sop_template_id' => $template2->id])); - - // 为第1步添加扫码任务 - if ($stepData['step_number'] == 1) { - SopInteractiveTask::create([ - 'sop_step_id' => $step->id, - 'task_type' => 'scan', - 'task_config' => [ - 'title' => '扫描机时单二维码', - 'scan_type' => 'qrcode', - ], - 'validation_rules' => [ - 'pattern' => '^[A-Z0-9]{10,20}$', - ], - 'timeout_seconds' => 60, - 'is_required' => true, - ]); - } - - // 为第2步添加选择任务 - if ($stepData['step_number'] == 2) { - SopInteractiveTask::create([ - 'sop_step_id' => $step->id, - 'task_type' => 'select', - 'task_config' => [ - 'title' => '样品安全检查结果', - 'options' => ['通过', '不通过'], - ], - 'validation_rules' => [], - 'timeout_seconds' => 120, - 'is_required' => true, - ]); - } - - // 为第3步添加拍照任务 - if ($stepData['step_number'] == 3) { - SopInteractiveTask::create([ - 'sop_step_id' => $step->id, - 'task_type' => 'photo', - 'task_config' => [ - 'title' => '拍摄样品安装照片', - 'message' => '请拍摄样品安装完成后的照片', - 'min_photos' => 1, - 'max_photos' => 2, - ], - 'validation_rules' => [], - 'timeout_seconds' => 180, - 'is_required' => true, - ]); - } - } - - // 3. 创建一个草稿模板 - $this->command->info('创建草稿模板...'); - SopTemplate::create([ - 'name' => '光束线日常维护流程(草稿)', - 'description' => '光束线日常维护保养操作流程,包括清洁、检查、记录等内容。', - 'category' => '设备维护', - 'tags' => ['维护', '保养'], - 'version' => '0.1.0', - 'status' => 'draft', - 'applicable_departments' => ['BL02U1', 'BL07U', 'BL08U'], - 'applicable_positions' => ['维修工', '技术员'], - 'published_at' => null, - 'created_by' => $user->id, - ]); - - $this->command->info('SOP模板数据创建完成!'); - $this->command->newLine(); - $this->command->info('=== 生成的SOP模板摘要 ==='); - $this->command->info('总模板数量: ' . SopTemplate::count()); - $this->command->info(' - 已发布: ' . SopTemplate::where('status', 'published')->count()); - $this->command->info(' - 草稿: ' . SopTemplate::where('status', 'draft')->count()); - $this->command->info(' - 已归档: ' . SopTemplate::where('status', 'archived')->count()); - $this->command->info('总步骤数量: ' . SopStep::count()); - $this->command->info('总交互任务数量: ' . SopInteractiveTask::count()); - } -} diff --git a/database/seeders/TerminalSeeder.php b/database/seeders/TerminalSeeder.php index c3b5b2e..ad7613f 100644 --- a/database/seeders/TerminalSeeder.php +++ b/database/seeders/TerminalSeeder.php @@ -37,7 +37,7 @@ class TerminalSeeder extends Seeder 'code' => "SCREEN-{$beamline}", 'ip_address' => $ipAddress, 'station_id' => $beamline, - 'diagram_url' => "https://example.com/diagrams/{$beamline}.png", + 'diagram_url' => 'https://ssrf.9z.work/scada/demo.html', 'display_config' => [ 'resolution' => '3840x2160', 'refresh_rate' => 60, @@ -46,11 +46,11 @@ class TerminalSeeder extends Seeder 'touch_enabled' => true, ], 'is_online' => in_array($beamline, ['BL02U1', 'BL07U', 'BL08U', 'BL13U', 'BL15U']), - 'last_online_at' => in_array($beamline, ['BL02U1', 'BL07U', 'BL08U', 'BL13U', 'BL15U']) - ? now() + 'last_online_at' => in_array($beamline, ['BL02U1', 'BL07U', 'BL08U', 'BL13U', 'BL15U']) + ? now() : now()->subHours(rand(1, 24)), ]); - + // 为每个终端创建提示词 TerminalPrompt::create([ 'terminal_id' => $terminal->id, diff --git a/package-lock.json b/package-lock.json index 1beb370..21a8fb7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,9 +15,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", - "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.4.tgz", + "integrity": "sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==", "cpu": [ "ppc64" ], @@ -32,9 +32,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", - "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.4.tgz", + "integrity": "sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==", "cpu": [ "arm" ], @@ -49,9 +49,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", - "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.4.tgz", + "integrity": "sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==", "cpu": [ "arm64" ], @@ -66,9 +66,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", - "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.4.tgz", + "integrity": "sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==", "cpu": [ "x64" ], @@ -83,9 +83,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", - "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.4.tgz", + "integrity": "sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==", "cpu": [ "arm64" ], @@ -100,9 +100,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", - "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.4.tgz", + "integrity": "sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==", "cpu": [ "x64" ], @@ -117,9 +117,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", - "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.4.tgz", + "integrity": "sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==", "cpu": [ "arm64" ], @@ -134,9 +134,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", - "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.4.tgz", + "integrity": "sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==", "cpu": [ "x64" ], @@ -151,9 +151,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", - "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.4.tgz", + "integrity": "sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==", "cpu": [ "arm" ], @@ -168,9 +168,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", - "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.4.tgz", + "integrity": "sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==", "cpu": [ "arm64" ], @@ -185,9 +185,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", - "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.4.tgz", + "integrity": "sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==", "cpu": [ "ia32" ], @@ -202,9 +202,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", - "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.4.tgz", + "integrity": "sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==", "cpu": [ "loong64" ], @@ -219,9 +219,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", - "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.4.tgz", + "integrity": "sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==", "cpu": [ "mips64el" ], @@ -236,9 +236,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", - "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.4.tgz", + "integrity": "sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==", "cpu": [ "ppc64" ], @@ -253,9 +253,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", - "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.4.tgz", + "integrity": "sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==", "cpu": [ "riscv64" ], @@ -270,9 +270,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", - "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.4.tgz", + "integrity": "sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==", "cpu": [ "s390x" ], @@ -287,9 +287,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", - "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.4.tgz", + "integrity": "sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==", "cpu": [ "x64" ], @@ -304,9 +304,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", - "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.4.tgz", + "integrity": "sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==", "cpu": [ "arm64" ], @@ -321,9 +321,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", - "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.4.tgz", + "integrity": "sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==", "cpu": [ "x64" ], @@ -338,9 +338,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", - "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.4.tgz", + "integrity": "sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==", "cpu": [ "arm64" ], @@ -355,9 +355,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", - "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.4.tgz", + "integrity": "sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==", "cpu": [ "x64" ], @@ -372,9 +372,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", - "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.4.tgz", + "integrity": "sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==", "cpu": [ "arm64" ], @@ -389,9 +389,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", - "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.4.tgz", + "integrity": "sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==", "cpu": [ "x64" ], @@ -406,9 +406,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", - "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.4.tgz", + "integrity": "sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==", "cpu": [ "arm64" ], @@ -423,9 +423,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", - "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.4.tgz", + "integrity": "sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==", "cpu": [ "ia32" ], @@ -440,9 +440,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", - "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.4.tgz", + "integrity": "sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==", "cpu": [ "x64" ], @@ -1419,9 +1419,9 @@ } }, "node_modules/esbuild": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", - "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.4.tgz", + "integrity": "sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -1432,32 +1432,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.3", - "@esbuild/android-arm": "0.27.3", - "@esbuild/android-arm64": "0.27.3", - "@esbuild/android-x64": "0.27.3", - "@esbuild/darwin-arm64": "0.27.3", - "@esbuild/darwin-x64": "0.27.3", - "@esbuild/freebsd-arm64": "0.27.3", - "@esbuild/freebsd-x64": "0.27.3", - "@esbuild/linux-arm": "0.27.3", - "@esbuild/linux-arm64": "0.27.3", - "@esbuild/linux-ia32": "0.27.3", - "@esbuild/linux-loong64": "0.27.3", - "@esbuild/linux-mips64el": "0.27.3", - "@esbuild/linux-ppc64": "0.27.3", - "@esbuild/linux-riscv64": "0.27.3", - "@esbuild/linux-s390x": "0.27.3", - "@esbuild/linux-x64": "0.27.3", - "@esbuild/netbsd-arm64": "0.27.3", - "@esbuild/netbsd-x64": "0.27.3", - "@esbuild/openbsd-arm64": "0.27.3", - "@esbuild/openbsd-x64": "0.27.3", - "@esbuild/openharmony-arm64": "0.27.3", - "@esbuild/sunos-x64": "0.27.3", - "@esbuild/win32-arm64": "0.27.3", - "@esbuild/win32-ia32": "0.27.3", - "@esbuild/win32-x64": "0.27.3" + "@esbuild/aix-ppc64": "0.27.4", + "@esbuild/android-arm": "0.27.4", + "@esbuild/android-arm64": "0.27.4", + "@esbuild/android-x64": "0.27.4", + "@esbuild/darwin-arm64": "0.27.4", + "@esbuild/darwin-x64": "0.27.4", + "@esbuild/freebsd-arm64": "0.27.4", + "@esbuild/freebsd-x64": "0.27.4", + "@esbuild/linux-arm": "0.27.4", + "@esbuild/linux-arm64": "0.27.4", + "@esbuild/linux-ia32": "0.27.4", + "@esbuild/linux-loong64": "0.27.4", + "@esbuild/linux-mips64el": "0.27.4", + "@esbuild/linux-ppc64": "0.27.4", + "@esbuild/linux-riscv64": "0.27.4", + "@esbuild/linux-s390x": "0.27.4", + "@esbuild/linux-x64": "0.27.4", + "@esbuild/netbsd-arm64": "0.27.4", + "@esbuild/netbsd-x64": "0.27.4", + "@esbuild/openbsd-arm64": "0.27.4", + "@esbuild/openbsd-x64": "0.27.4", + "@esbuild/openharmony-arm64": "0.27.4", + "@esbuild/sunos-x64": "0.27.4", + "@esbuild/win32-arm64": "0.27.4", + "@esbuild/win32-ia32": "0.27.4", + "@esbuild/win32-x64": "0.27.4" } }, "node_modules/escalade": { diff --git a/public/css/solution-forest/filament-tree/filament-tree-min.css b/public/css/solution-forest/filament-tree/filament-tree-min.css new file mode 100644 index 0000000..a0aa861 --- /dev/null +++ b/public/css/solution-forest/filament-tree/filament-tree-min.css @@ -0,0 +1 @@ +.dd{max-width:600px;font-size:13px;line-height:20px}.dd,.dd-list{position:relative;display:block;margin:0;padding:0;list-style:none}.dd-list .dd-list{padding-left:30px}[dir=rtl] .dd-list .dd-list{padding-left:unset!important;padding-right:30px!important}.dd-empty,.dd-item,.dd-placeholder{display:block;position:relative;margin:0;padding:0;min-height:20px;font-size:13px;line-height:20px}.dd-item>button{position:relative;cursor:pointer;float:left;width:25px;height:20px;margin:5px 0;padding:0;white-space:nowrap;overflow:hidden;border:0;background:0 0;font-size:12px;line-height:1;text-align:center;font-weight:700}[dir=rtl] .dd-item>button{float:right}.dd-item>button:before{display:block;position:absolute;width:100%;text-align:center;text-indent:0}.dd-item>button.dd-expand:before{content:"+"}.dd-item>button.dd-collapse:before{content:"-"}.dd-collapsed .dd-collapse,.dd-collapsed .dd-list,.dd-expand{display:none}.dd-collapsed .dd-expand{display:block}.dd-empty,.dd-placeholder{margin:5px 0;padding:0;min-height:30px;background:#f2fbff;border:1px dashed #b6bcbf;box-sizing:border-box;-moz-box-sizing:border-box}.dd-empty{border:1px dashed #bbb;min-height:100px;background-color:#e5e5e5;background-size:60px 60px;background-position:0 0,30px 30px}.dd-dragel{position:absolute;pointer-events:none;z-index:9999}.dd-dragel>.dd-item .dd-handle{margin-top:0}.dd-dragel .dd-handle{box-shadow:2px 4px 6px 0 #0000001a}.dd-nochildren .dd-placeholder{display:none}.dd{max-width:none}.dd-handle{height:55px}.dd-item>button{height:45px}.btn-group{display:flex;gap:1px}.btn-group button{border-radius:0}[dir=ltr]{.btn-group button:first-of-type{border-top-left-radius:.5rem;border-bottom-left-radius:.5rem}.btn-group button:last-of-type{border-top-right-radius:.5rem;border-bottom-right-radius:.5rem}}[dir=rtl]{.btn-group button:first-of-type{border-top-right-radius:.5rem;border-bottom-right-radius:.5rem}.btn-group button:last-of-type{border-top-left-radius:.5rem;border-bottom-left-radius:.5rem}}.btn-group button:first-of-type{border-top-left-radius:.5rem;border-bottom-left-radius:.5rem;border-top-right-radius:0;border-bottom-right-radius:0}.btn-group button:last-of-type,[dir=rtl] .btn-group button:first-of-type{border-top-right-radius:.5rem;border-bottom-right-radius:.5rem;border-top-left-radius:0;border-bottom-left-radius:0}[dir=rtl] .btn-group button:last-of-type{border-top-left-radius:.5rem;border-bottom-left-radius:.5rem;border-top-right-radius:0;border-bottom-right-radius:0}.btn-group button:only-of-type{border-radius:.5rem!important}/*! tailwindcss v3.3.5 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border-width:0;border-style:solid;border-color:rgba(var(--gray-200),1)}:after,:before{--tw-content:""}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:var(--font-family),ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:rgba(var(--gray-400),1)}input::placeholder,textarea::placeholder{opacity:1;color:rgba(var(--gray-400),1)}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}[multiple],[type=date],[type=datetime-local],[type=email],[type=month],[type=number],[type=password],[type=search],[type=tel],[type=text],[type=time],[type=url],[type=week],input:where(:not([type])),select,textarea{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:rgba(var(--gray-500),var(--tw-border-opacity,1));border-width:1px;border-radius:0;padding:.5rem .75rem;font-size:1rem;line-height:1.5rem;--tw-shadow:0 0 #0000}[multiple]:focus,[type=date]:focus,[type=datetime-local]:focus,[type=email]:focus,[type=month]:focus,[type=number]:focus,[type=password]:focus,[type=search]:focus,[type=tel]:focus,[type=text]:focus,[type=time]:focus,[type=url]:focus,[type=week]:focus,input:where(:not([type])):focus,select:focus,textarea:focus{outline:2px solid #0000;outline-offset:2px;--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#2563eb;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);border-color:#2563eb}input::-moz-placeholder,textarea::-moz-placeholder{color:rgba(var(--gray-500),var(--tw-text-opacity,1));opacity:1}input::placeholder,textarea::placeholder{color:rgba(var(--gray-500),var(--tw-text-opacity,1));opacity:1}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-date-and-time-value{min-height:1.5em;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-meridiem-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-year-field{padding-top:0;padding-bottom:0}select{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='rgba(var(--gray-500), var(--tw-stroke-opacity, 1))' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3E%3C/svg%3E");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem;-webkit-print-color-adjust:exact;print-color-adjust:exact}[multiple],[size]:where(select:not([size="1"])){background-image:none;background-position:0 0;background-repeat:unset;background-size:initial;padding-right:.75rem;-webkit-print-color-adjust:unset;print-color-adjust:unset}[type=checkbox],[type=radio]{-webkit-appearance:none;-moz-appearance:none;appearance:none;padding:0;-webkit-print-color-adjust:exact;print-color-adjust:exact;display:inline-block;vertical-align:middle;background-origin:border-box;-webkit-user-select:none;-moz-user-select:none;user-select:none;flex-shrink:0;height:1rem;width:1rem;color:#2563eb;background-color:#fff;border-color:rgba(var(--gray-500),var(--tw-border-opacity,1));border-width:1px;--tw-shadow:0 0 #0000}[type=checkbox]{border-radius:0}[type=radio]{border-radius:100%}[type=checkbox]:focus,[type=radio]:focus{outline:2px solid #0000;outline-offset:2px;--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:2px;--tw-ring-offset-color:#fff;--tw-ring-color:#2563eb;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}[type=checkbox]:checked,[type=radio]:checked{border-color:#0000;background-color:currentColor;background-size:100% 100%;background-position:50%;background-repeat:no-repeat}[type=checkbox]:checked{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 16 16'%3E%3Cpath d='M12.207 4.793a1 1 0 0 1 0 1.414l-5 5a1 1 0 0 1-1.414 0l-2-2a1 1 0 0 1 1.414-1.414L6.5 9.086l4.293-4.293a1 1 0 0 1 1.414 0z'/%3E%3C/svg%3E")}[type=radio]:checked{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='3'/%3E%3C/svg%3E")}[type=checkbox]:checked:focus,[type=checkbox]:checked:hover,[type=checkbox]:indeterminate,[type=radio]:checked:focus,[type=radio]:checked:hover{border-color:#0000;background-color:currentColor}[type=checkbox]:indeterminate{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3E%3Cpath stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3E%3C/svg%3E");background-size:100% 100%;background-position:50%;background-repeat:no-repeat}[type=checkbox]:indeterminate:focus,[type=checkbox]:indeterminate:hover{border-color:#0000;background-color:currentColor}[type=file]{background:unset;border-color:inherit;border-width:0;border-radius:0;padding:0;font-size:unset;line-height:inherit}[type=file]:focus{outline:1px solid ButtonText;outline:1px auto -webkit-focus-ring-color}*,::backdrop,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.prose :where(.prose>ul>li>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ul>li>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose :where(.prose>ol>li>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ol>li>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose-sm :where(.prose-sm>ul>li>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em}.prose-sm :where(.prose-sm>ul>li>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.1428571em}.prose-sm :where(.prose-sm>ol>li>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em}.prose-sm :where(.prose-sm>ol>li>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.1428571em}.prose-base :where(.prose-base>ul>li>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose-base :where(.prose-base>ul>li>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose-base :where(.prose-base>ol>li>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose-base :where(.prose-base>ol>li>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose-lg :where(.prose-lg>ul>li>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.3333333em}.prose-lg :where(.prose-lg>ul>li>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.3333333em}.prose-lg :where(.prose-lg>ol>li>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.3333333em}.prose-lg :where(.prose-lg>ol>li>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.3333333em}.-m-0{margin:0}.-mr-2{margin-right:-.5rem}.-ms-0{margin-inline-start:0}.ml-1{margin-left:.25rem}.ml-4{margin-left:1rem}.mr-4{margin-right:1rem}.mt-0{margin-top:0}.h-1{height:.25rem}.h-\[100dvh\]{height:100dvh}.space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.75rem*var(--tw-space-x-reverse));margin-left:calc(.75rem*(1 - var(--tw-space-x-reverse)))}.rounded-l-lg{border-top-left-radius:.5rem;border-bottom-left-radius:.5rem}.border-r{border-right-width:1px}.border-danger-300{--tw-border-opacity:1;border-color:rgba(var(--danger-300),var(--tw-border-opacity))}.\!bg-gray-50,.\!bg-gray-700{--tw-bg-opacity:1!important}.bg-gray-400,.bg-gray-50{--tw-bg-opacity:1}.px-0{padding-left:0;padding-right:0}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-px{padding-left:1px;padding-right:1px}.py-0{padding-top:0;padding-bottom:0}.pl-3{padding-left:.75rem}.ring-danger-500{--tw-ring-opacity:1;--tw-ring-color:rgba(var(--danger-500),var(--tw-ring-opacity))}:is(.dark .dark\:prose-invert){--tw-prose-body:var(--tw-prose-invert-body);--tw-prose-headings:var(--tw-prose-invert-headings);--tw-prose-lead:var(--tw-prose-invert-lead);--tw-prose-links:var(--tw-prose-invert-links);--tw-prose-bold:var(--tw-prose-invert-bold);--tw-prose-counters:var(--tw-prose-invert-counters);--tw-prose-bullets:var(--tw-prose-invert-bullets);--tw-prose-hr:var(--tw-prose-invert-hr);--tw-prose-quotes:var(--tw-prose-invert-quotes);--tw-prose-quote-borders:var(--tw-prose-invert-quote-borders);--tw-prose-captions:var(--tw-prose-invert-captions);--tw-prose-kbd:var(--tw-prose-invert-kbd);--tw-prose-kbd-shadows:var(--tw-prose-invert-kbd-shadows);--tw-prose-code:var(--tw-prose-invert-code);--tw-prose-pre-code:var(--tw-prose-invert-pre-code);--tw-prose-pre-bg:var(--tw-prose-invert-pre-bg);--tw-prose-th-borders:var(--tw-prose-invert-th-borders);--tw-prose-td-borders:var(--tw-prose-invert-td-borders)}.before\:w-0:before{content:var(--tw-content);width:0}.focus\:border-primary-500:focus{--tw-border-opacity:1;border-color:rgba(var(--primary-500),var(--tw-border-opacity))}.focus\:ring-primary-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgba(var(--primary-500),var(--tw-ring-opacity))}.focus-visible\:ring-1:focus-visible,.focus-visible\:ring-2:focus-visible{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.disabled\:placeholder\:\[-webkit-text-fill-color\:theme\(colors\.gray\.400\)\]:disabled::-moz-placeholder,.disabled\:placeholder\:\[-webkit-text-fill-color\:theme\(colors\.gray\.400\)\]:disabled::placeholder{-webkit-text-fill-color:rgba(var(--gray-400),1)}.group\/item:focus-visible .group-focus-visible\/item\:underline,.group\/link:focus-visible .group-focus-visible\/link\:underline{text-decoration-line:underline}:is([dir=ltr] .ltr\:hidden){display:none}:is([dir=rtl] .rtl\:-ml-2){margin-left:-.5rem}:is([dir=rtl] .rtl\:ml-4){margin-left:1rem}:is([dir=rtl] .rtl\:mr-0){margin-right:0}:is([dir=rtl] .rtl\:mr-1){margin-right:.25rem}:is([dir=rtl] .rtl\:mr-4){margin-right:1rem}:is([dir=rtl] .rtl\:mr-auto){margin-right:auto}:is([dir=rtl] .rtl\:hidden){display:none}:is([dir=rtl] .rtl\:-translate-x-0){--tw-translate-x:-0px}:is([dir=rtl] .rtl\:-translate-x-0),:is([dir=rtl] .rtl\:-translate-x-5){transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}:is([dir=rtl] .rtl\:-translate-x-5){--tw-translate-x:-1.25rem}:is([dir=rtl] .rtl\:-translate-x-full){--tw-translate-x:-100%}:is([dir=rtl] .rtl\:-translate-x-full),:is([dir=rtl] .rtl\:translate-x-1\/2){transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}:is([dir=rtl] .rtl\:translate-x-1\/2){--tw-translate-x:50%}:is([dir=rtl] .rtl\:translate-x-1\/4){--tw-translate-x:25%}:is([dir=rtl] .rtl\:translate-x-1\/4),:is([dir=rtl] .rtl\:translate-x-full){transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}:is([dir=rtl] .rtl\:translate-x-full){--tw-translate-x:100%}:is([dir=rtl] .rtl\:rotate-180){--tw-rotate:180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}:is([dir=rtl] .rtl\:flex-row-reverse){flex-direction:row-reverse}:is([dir=rtl] .rtl\:space-x-reverse)>:not([hidden])~:not([hidden]){--tw-space-x-reverse:1}:is([dir=rtl] .rtl\:divide-x-reverse)>:not([hidden])~:not([hidden]){--tw-divide-x-reverse:1}:is([dir=rtl] .rtl\:rounded-l){border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}:is([dir=rtl] .rtl\:border-l){border-left-width:1px}:is([dir=rtl] .rtl\:border-r-0){border-right-width:0}:is(.dark .dark\:flex){display:flex}:is(.dark .dark\:hidden){display:none}:is(.dark .dark\:divide-white\/10)>:not([hidden])~:not([hidden]){border-color:#ffffff1a}:is(.dark .dark\:divide-white\/5)>:not([hidden])~:not([hidden]){border-color:#ffffff0d}:is(.dark .dark\:border-danger-400){--tw-border-opacity:1;border-color:rgba(var(--danger-400),var(--tw-border-opacity))}:is(.dark .dark\:border-gray-600){--tw-border-opacity:1;border-color:rgba(var(--gray-600),var(--tw-border-opacity))}:is(.dark .dark\:border-gray-700){--tw-border-opacity:1;border-color:rgba(var(--gray-700),var(--tw-border-opacity))}:is(.dark .dark\:border-primary-500){--tw-border-opacity:1;border-color:rgba(var(--primary-500),var(--tw-border-opacity))}:is(.dark .dark\:border-white\/10){border-color:#ffffff1a}:is(.dark .dark\:border-white\/5){border-color:#ffffff0d}:is(.dark .dark\:border-t-white\/10){border-top-color:#ffffff1a}:is(.dark .dark\:\!bg-gray-700){--tw-bg-opacity:1!important;background-color:rgba(var(--gray-700),var(--tw-bg-opacity))!important}:is(.dark .dark\:bg-custom-400\/10){background-color:rgba(var(--c-400),.1)}:is(.dark .dark\:bg-custom-500){--tw-bg-opacity:1;background-color:rgba(var(--c-500),var(--tw-bg-opacity))}:is(.dark .dark\:bg-custom-500\/20){background-color:rgba(var(--c-500),.2)}:is(.dark .dark\:bg-gray-400\/10){background-color:rgba(var(--gray-400),.1)}:is(.dark .dark\:bg-gray-500){--tw-bg-opacity:1;background-color:rgba(var(--gray-500),var(--tw-bg-opacity))}:is(.dark .dark\:bg-gray-500\/10){background-color:rgba(var(--gray-500),.1)}:is(.dark .dark\:bg-gray-500\/20){background-color:rgba(var(--gray-500),.2)}:is(.dark .dark\:bg-gray-600){--tw-bg-opacity:1;background-color:rgba(var(--gray-600),var(--tw-bg-opacity))}:is(.dark .dark\:bg-gray-700){--tw-bg-opacity:1;background-color:rgba(var(--gray-700),var(--tw-bg-opacity))}:is(.dark .dark\:bg-gray-800){--tw-bg-opacity:1;background-color:rgba(var(--gray-800),var(--tw-bg-opacity))}:is(.dark .dark\:bg-gray-900){--tw-bg-opacity:1;background-color:rgba(var(--gray-900),var(--tw-bg-opacity))}:is(.dark .dark\:bg-gray-900\/30){background-color:rgba(var(--gray-900),.3)}:is(.dark .dark\:bg-gray-950){--tw-bg-opacity:1;background-color:rgba(var(--gray-950),var(--tw-bg-opacity))}:is(.dark .dark\:bg-gray-950\/75){background-color:rgba(var(--gray-950),.75)}:is(.dark .dark\:bg-primary-400){--tw-bg-opacity:1;background-color:rgba(var(--primary-400),var(--tw-bg-opacity))}:is(.dark .dark\:bg-primary-500){--tw-bg-opacity:1;background-color:rgba(var(--primary-500),var(--tw-bg-opacity))}:is(.dark .dark\:bg-transparent){background-color:initial}:is(.dark .dark\:bg-white\/10){background-color:#ffffff1a}:is(.dark .dark\:bg-white\/5){background-color:#ffffff0d}:is(.dark .dark\:fill-current){fill:currentColor}:is(.dark .dark\:text-custom-300\/50){color:rgba(var(--c-300),.5)}:is(.dark .dark\:text-custom-400){--tw-text-opacity:1;color:rgba(var(--c-400),var(--tw-text-opacity))}:is(.dark .dark\:text-custom-400\/10){color:rgba(var(--c-400),.1)}:is(.dark .dark\:text-danger-400){--tw-text-opacity:1;color:rgba(var(--danger-400),var(--tw-text-opacity))}:is(.dark .dark\:text-danger-500){--tw-text-opacity:1;color:rgba(var(--danger-500),var(--tw-text-opacity))}:is(.dark .dark\:text-gray-200){--tw-text-opacity:1;color:rgba(var(--gray-200),var(--tw-text-opacity))}:is(.dark .dark\:text-gray-300\/50){color:rgba(var(--gray-300),.5)}:is(.dark .dark\:text-gray-400){--tw-text-opacity:1;color:rgba(var(--gray-400),var(--tw-text-opacity))}:is(.dark .dark\:text-gray-500){--tw-text-opacity:1;color:rgba(var(--gray-500),var(--tw-text-opacity))}:is(.dark .dark\:text-gray-700){--tw-text-opacity:1;color:rgba(var(--gray-700),var(--tw-text-opacity))}:is(.dark .dark\:text-gray-800){--tw-text-opacity:1;color:rgba(var(--gray-800),var(--tw-text-opacity))}:is(.dark .dark\:text-primary-400){--tw-text-opacity:1;color:rgba(var(--primary-400),var(--tw-text-opacity))}:is(.dark .dark\:text-primary-500){--tw-text-opacity:1;color:rgba(var(--primary-500),var(--tw-text-opacity))}:is(.dark .dark\:text-white){--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}:is(.dark .dark\:text-white\/5){color:#ffffff0d}:is(.dark .dark\:ring-custom-400\/30){--tw-ring-color:rgba(var(--c-400),0.3)}:is(.dark .dark\:ring-custom-500){--tw-ring-opacity:1;--tw-ring-color:rgba(var(--c-500),var(--tw-ring-opacity))}:is(.dark .dark\:ring-danger-400){--tw-ring-opacity:1;--tw-ring-color:rgba(var(--danger-400),var(--tw-ring-opacity))}:is(.dark .dark\:ring-danger-500){--tw-ring-opacity:1;--tw-ring-color:rgba(var(--danger-500),var(--tw-ring-opacity))}:is(.dark .dark\:ring-gray-400\/20){--tw-ring-color:rgba(var(--gray-400),0.2)}:is(.dark .dark\:ring-gray-50\/10){--tw-ring-color:rgba(var(--gray-50),0.1)}:is(.dark .dark\:ring-gray-700){--tw-ring-opacity:1;--tw-ring-color:rgba(var(--gray-700),var(--tw-ring-opacity))}:is(.dark .dark\:ring-gray-900){--tw-ring-opacity:1;--tw-ring-color:rgba(var(--gray-900),var(--tw-ring-opacity))}:is(.dark .dark\:ring-white\/10){--tw-ring-color:#ffffff1a}:is(.dark .dark\:ring-white\/20){--tw-ring-color:#fff3}:is(.dark .dark\:placeholder\:text-gray-500)::-moz-placeholder{--tw-text-opacity:1;color:rgba(var(--gray-500),var(--tw-text-opacity))}:is(.dark .dark\:placeholder\:text-gray-500)::placeholder{--tw-text-opacity:1;color:rgba(var(--gray-500),var(--tw-text-opacity))}:is(.dark .dark\:before\:bg-primary-500):before{content:var(--tw-content);--tw-bg-opacity:1;background-color:rgba(var(--primary-500),var(--tw-bg-opacity))}:is(.dark .dark\:checked\:bg-danger-500:checked){--tw-bg-opacity:1;background-color:rgba(var(--danger-500),var(--tw-bg-opacity))}:is(.dark .dark\:checked\:bg-primary-500:checked){--tw-bg-opacity:1;background-color:rgba(var(--primary-500),var(--tw-bg-opacity))}:is(.dark .dark\:focus-within\:bg-white\/5:focus-within){background-color:#ffffff0d}:is(.dark .dark\:hover\:bg-custom-400:hover){--tw-bg-opacity:1;background-color:rgba(var(--c-400),var(--tw-bg-opacity))}:is(.dark .dark\:hover\:bg-custom-400\/10:hover){background-color:rgba(var(--c-400),.1)}:is(.dark .dark\:hover\:bg-white\/10:hover){background-color:#ffffff1a}:is(.dark .dark\:hover\:bg-white\/5:hover){background-color:#ffffff0d}:is(.dark .dark\:hover\:text-custom-300:hover){--tw-text-opacity:1;color:rgba(var(--c-300),var(--tw-text-opacity))}:is(.dark .dark\:hover\:text-custom-300\/75:hover){color:rgba(var(--c-300),.75)}:is(.dark .dark\:hover\:text-gray-200:hover){--tw-text-opacity:1;color:rgba(var(--gray-200),var(--tw-text-opacity))}:is(.dark .dark\:hover\:text-gray-300\/75:hover){color:rgba(var(--gray-300),.75)}:is(.dark .dark\:hover\:text-gray-400:hover){--tw-text-opacity:1;color:rgba(var(--gray-400),var(--tw-text-opacity))}:is(.dark .dark\:hover\:ring-white\/20:hover){--tw-ring-color:#fff3}:is(.dark .dark\:focus\:ring-danger-500:focus){--tw-ring-opacity:1;--tw-ring-color:rgba(var(--danger-500),var(--tw-ring-opacity))}:is(.dark .dark\:focus\:ring-primary-500:focus){--tw-ring-opacity:1;--tw-ring-color:rgba(var(--primary-500),var(--tw-ring-opacity))}:is(.dark .dark\:checked\:focus\:ring-danger-400\/50:focus:checked){--tw-ring-color:rgba(var(--danger-400),0.5)}:is(.dark .dark\:checked\:focus\:ring-primary-400\/50:focus:checked){--tw-ring-color:rgba(var(--primary-400),0.5)}:is(.dark .dark\:focus-visible\:border-primary-500:focus-visible){--tw-border-opacity:1;border-color:rgba(var(--primary-500),var(--tw-border-opacity))}:is(.dark .dark\:focus-visible\:bg-custom-400\/10:focus-visible){background-color:rgba(var(--c-400),.1)}:is(.dark .dark\:focus-visible\:bg-white\/5:focus-visible){background-color:#ffffff0d}:is(.dark .dark\:focus-visible\:text-custom-300\/75:focus-visible){color:rgba(var(--c-300),.75)}:is(.dark .dark\:focus-visible\:text-gray-300\/75:focus-visible){color:rgba(var(--gray-300),.75)}:is(.dark .dark\:focus-visible\:text-gray-400:focus-visible){--tw-text-opacity:1;color:rgba(var(--gray-400),var(--tw-text-opacity))}:is(.dark .dark\:focus-visible\:ring-custom-400\/50:focus-visible){--tw-ring-color:rgba(var(--c-400),0.5)}:is(.dark .dark\:focus-visible\:ring-custom-500:focus-visible){--tw-ring-opacity:1;--tw-ring-color:rgba(var(--c-500),var(--tw-ring-opacity))}:is(.dark .dark\:focus-visible\:ring-primary-500:focus-visible){--tw-ring-opacity:1;--tw-ring-color:rgba(var(--primary-500),var(--tw-ring-opacity))}:is(.dark .dark\:disabled\:bg-transparent:disabled){background-color:initial}:is(.dark .dark\:disabled\:text-gray-400:disabled){--tw-text-opacity:1;color:rgba(var(--gray-400),var(--tw-text-opacity))}:is(.dark .dark\:disabled\:ring-white\/10:disabled){--tw-ring-color:#ffffff1a}:is(.dark .dark\:disabled\:\[-webkit-text-fill-color\:theme\(colors\.gray\.400\)\]:disabled){-webkit-text-fill-color:rgba(var(--gray-400),1)}:is(.dark .dark\:disabled\:placeholder\:\[-webkit-text-fill-color\:theme\(colors\.gray\.500\)\]:disabled)::-moz-placeholder,:is(.dark .dark\:disabled\:placeholder\:\[-webkit-text-fill-color\:theme\(colors\.gray\.500\)\]:disabled)::placeholder{-webkit-text-fill-color:rgba(var(--gray-500),1)}:is(.dark .dark\:disabled\:checked\:bg-gray-600:checked:disabled){--tw-bg-opacity:1;background-color:rgba(var(--gray-600),var(--tw-bg-opacity))}:is(.dark .group\/button:hover .dark\:group-hover\/button\:text-gray-400){--tw-text-opacity:1;color:rgba(var(--gray-400),var(--tw-text-opacity))}:is(.dark .group:hover .dark\:group-hover\:text-gray-200){--tw-text-opacity:1;color:rgba(var(--gray-200),var(--tw-text-opacity))}:is(.dark .group:hover .dark\:group-hover\:text-gray-400){--tw-text-opacity:1;color:rgba(var(--gray-400),var(--tw-text-opacity))}:is(.dark .group:focus-visible .dark\:group-focus-visible\:text-gray-200){--tw-text-opacity:1;color:rgba(var(--gray-200),var(--tw-text-opacity))}:is(.dark .group:focus-visible .dark\:group-focus-visible\:text-gray-400){--tw-text-opacity:1;color:rgba(var(--gray-400),var(--tw-text-opacity))}@media (min-width:640px){.sm\:py-1{padding-top:.25rem;padding-bottom:.25rem}.sm\:pt-1{padding-top:.25rem}}@media (min-width:1024px){:is([dir=rtl] .rtl\:lg\:-translate-x-0){--tw-translate-x:-0px}:is([dir=rtl] .rtl\:lg\:-translate-x-0),:is([dir=rtl] .rtl\:lg\:translate-x-full){transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}:is([dir=rtl] .rtl\:lg\:translate-x-full){--tw-translate-x:100%}:is(.dark .dark\:lg\:bg-transparent){background-color:initial}}:is(.dark .dark\:\[\&\.trix-active\]\:bg-white\/5.trix-active){background-color:#ffffff0d}:is(.dark .dark\:\[\&\.trix-active\]\:text-primary-400.trix-active){--tw-text-opacity:1;color:rgba(var(--primary-400),var(--tw-text-opacity))}:is(.dark .dark\:\[\&\:not\(\:has\(\.fi-ac-action\:focus\)\)\]\:focus-within\:ring-danger-500:focus-within:not(:has(.fi-ac-action:focus))){--tw-ring-opacity:1;--tw-ring-color:rgba(var(--danger-500),var(--tw-ring-opacity))}:is(.dark .dark\:\[\&\:not\(\:has\(\.fi-ac-action\:focus\)\)\]\:focus-within\:ring-primary-500:focus-within:not(:has(.fi-ac-action:focus))){--tw-ring-opacity:1;--tw-ring-color:rgba(var(--primary-500),var(--tw-ring-opacity))}:is(.dark .dark\:\[\&\:not\(\:nth-child\(1_of_\.fi-btn\)\)\]\:shadow-\[-1px_0_0_0_theme\(colors\.white\/20\%\)\]:not(:nth-child(1 of .fi-btn))){--tw-shadow:-1px 0 0 0 #fff3;--tw-shadow-colored:-1px 0 0 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}:is(.dark .\[\&\>\*\:first-child\]\:dark\:before\:bg-primary-500)>:first-child:before{content:var(--tw-content);--tw-bg-opacity:1;background-color:rgba(var(--primary-500),var(--tw-bg-opacity))}:is(.dark .\[\&_optgroup\]\:dark\:bg-gray-900) optgroup{--tw-bg-opacity:1;background-color:rgba(var(--gray-900),var(--tw-bg-opacity))}:is(.dark .\[\&_option\]\:dark\:bg-gray-900) option{--tw-bg-opacity:1;background-color:rgba(var(--gray-900),var(--tw-bg-opacity))}:checked+*>.\[\:checked\+\*\>\&\]\:text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}input:checked+.\[input\:checked\+\&\]\:bg-custom-600{--tw-bg-opacity:1;background-color:rgba(var(--c-600),var(--tw-bg-opacity))}input:checked+.\[input\:checked\+\&\]\:bg-gray-400{--tw-bg-opacity:1;background-color:rgba(var(--gray-400),var(--tw-bg-opacity))}input:checked+.\[input\:checked\+\&\]\:text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}input:checked+.\[input\:checked\+\&\]\:ring-0{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}input:checked+.\[input\:checked\+\&\]\:hover\:bg-custom-500:hover{--tw-bg-opacity:1;background-color:rgba(var(--c-500),var(--tw-bg-opacity))}input:checked+.\[input\:checked\+\&\]\:hover\:bg-gray-300:hover{--tw-bg-opacity:1;background-color:rgba(var(--gray-300),var(--tw-bg-opacity))}:is(.dark input:checked+.dark\:\[input\:checked\+\&\]\:bg-custom-500){--tw-bg-opacity:1;background-color:rgba(var(--c-500),var(--tw-bg-opacity))}:is(.dark input:checked+.dark\:\[input\:checked\+\&\]\:bg-gray-600){--tw-bg-opacity:1;background-color:rgba(var(--gray-600),var(--tw-bg-opacity))}:is(.dark input:checked+.dark\:\[input\:checked\+\&\]\:hover\:bg-custom-400:hover){--tw-bg-opacity:1;background-color:rgba(var(--c-400),var(--tw-bg-opacity))}:is(.dark input:checked+.dark\:\[input\:checked\+\&\]\:hover\:bg-gray-500:hover){--tw-bg-opacity:1;background-color:rgba(var(--gray-500),var(--tw-bg-opacity))}input:checked:focus-visible+.\[input\:checked\:focus-visible\+\&\]\:ring-custom-500\/50{--tw-ring-color:rgba(var(--c-500),0.5)}:is(.dark input:checked:focus-visible+.dark\:\[input\:checked\:focus-visible\+\&\]\:ring-custom-400\/50){--tw-ring-color:rgba(var(--c-400),0.5)}input:focus-visible+.\[input\:focus-visible\+\&\]\:z-10{z-index:10}input:focus-visible+.\[input\:focus-visible\+\&\]\:ring-2{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}input:focus-visible+.\[input\:focus-visible\+\&\]\:ring-gray-950\/10{--tw-ring-color:rgba(var(--gray-950),0.1)}:is(.dark input:focus-visible+.dark\:\[input\:focus-visible\+\&\]\:ring-white\/20){--tw-ring-color:#fff3} \ No newline at end of file diff --git a/public/guides/how-to-use-beam/step-1.html b/public/guides/how-to-use-beam/step-1.html new file mode 100644 index 0000000..b34cc78 --- /dev/null +++ b/public/guides/how-to-use-beam/step-1.html @@ -0,0 +1,84 @@ + + + + + + + 步骤 1 - 打开光子光闸 PS1 + + + + +
步骤 1: 打开光子光闸 PS1
+ +
+ PS1 控制面板 + + + + PS1 开关位置 + +
+ +
+

首先需要打开光子光闸 PS1。请问安全光闸 SS1 是否能打开?如果 SS1 无法打开,需要先检查人员安全系统(PSS)状态。

+
+ + + + diff --git a/public/guides/how-to-use-beam/step-2.html b/public/guides/how-to-use-beam/step-2.html new file mode 100644 index 0000000..009b152 --- /dev/null +++ b/public/guides/how-to-use-beam/step-2.html @@ -0,0 +1,92 @@ + + + + + + + 步骤 2 - 搜索光学棚屋 + + + + +
步骤 2: 搜索光学棚屋
+ +
+ 光学棚屋布局图 + + + + 前门12 + + 后门 + +
+ +
+

光学棚屋设置了 2 种搜索顺序,请选择您要使用的搜索路径:

+
    +
  • 前门12:从前门进入,适合常规实验
  • +
  • 后门:从后门进入,适合特殊设备维护
  • +
+
+ + + + diff --git a/public/guides/how-to-use-beam/step-3a.html b/public/guides/how-to-use-beam/step-3a.html new file mode 100644 index 0000000..188f20f --- /dev/null +++ b/public/guides/how-to-use-beam/step-3a.html @@ -0,0 +1,72 @@ + + + + + + + 步骤 3 - 前门12路径 - 检查设备状态 + + + + +
步骤 3: 前门12路径 - 检查设备状态
+ +
+ 前端设备 +
+ +
+

从前门12进入后,请检查以下设备状态:

+
    +
  1. 白光挡板是否正常
  2. +
  3. 单色器冷却水流量
  4. +
  5. 光束位置监测器读数
  6. +
+
+ + + diff --git a/public/guides/how-to-use-beam/step-3b.html b/public/guides/how-to-use-beam/step-3b.html new file mode 100644 index 0000000..c7b5500 --- /dev/null +++ b/public/guides/how-to-use-beam/step-3b.html @@ -0,0 +1,91 @@ + + + + + + + 步骤 3 - 后门路径 - 安全确认 + + + + +
步骤 3: 后门路径 - 安全确认
+ +
+ 后门安全区域 +
+ +
+

从后门进入需要额外的安全确认:

+
    +
  1. 确认后门区域无人员
  2. +
  3. 检查后门联锁状态
  4. +
  5. 通知控制室操作人员
  6. +
+
+ +
+ ⚠️ 注意 + 后门路径需要额外安全授权,请确保已通知控制室后再进入。 +
+ + + diff --git a/public/guides/how-to-use-beam/step-4a.html b/public/guides/how-to-use-beam/step-4a.html new file mode 100644 index 0000000..67252dd --- /dev/null +++ b/public/guides/how-to-use-beam/step-4a.html @@ -0,0 +1,65 @@ + + + + + + + 步骤 4 - 前门12路径 - 打开实验站光闸 + + + + +
步骤 4: 前门12路径 - 打开实验站光闸
+ +
+ SS2 控制面板 +
+ +
+

确认所有设备状态正常后,打开实验站光闸 SS2。观察光束强度是否达到预期值。

+
+ + + diff --git a/public/guides/how-to-use-beam/step-4b.html b/public/guides/how-to-use-beam/step-4b.html new file mode 100644 index 0000000..6db2ab6 --- /dev/null +++ b/public/guides/how-to-use-beam/step-4b.html @@ -0,0 +1,72 @@ + + + + + + + 步骤 4 - 后门路径 - 设备检查 + + + + +
步骤 4: 后门路径 - 设备检查
+ +
+ 后门区域设备 +
+ +
+

检查后门区域的关键设备:

+
    +
  1. 聚焦镜冷却系统
  2. +
  3. 狭缝位置
  4. +
  5. 光束诊断设备
  6. +
+
+ + + diff --git a/public/guides/how-to-use-beam/step-5.html b/public/guides/how-to-use-beam/step-5.html new file mode 100644 index 0000000..e98991d --- /dev/null +++ b/public/guides/how-to-use-beam/step-5.html @@ -0,0 +1,94 @@ + + + + + + + 步骤 5 - 完成 + + + + +
步骤 5: 完成
+ +
+
✓ 光束已成功送达实验站!
+
+

当前光束参数:

+ + + + +
能量12.66 keV
通量1.2×10¹² ph/s
光斑尺寸0.15×0.08 mm²
+

您现在可以开始实验了。

+
+
+ + + diff --git a/public/guides/vacuum-valve-issue/step-1.html b/public/guides/vacuum-valve-issue/step-1.html new file mode 100644 index 0000000..584d243 --- /dev/null +++ b/public/guides/vacuum-valve-issue/step-1.html @@ -0,0 +1,98 @@ + + + + + + + 步骤 1 - 检查真空度 + + + + +
步骤 1: 检查真空度
+ +
+ 真空计读数面板 + + + + 上游真空计 + + 下游真空计 + +
+ +
+

真空阀门无法打开通常是因为真空度不满足联锁条件。

+

请检查阀门两侧的真空度读数:

+
    +
  • 上游真空度应 < 1×10⁻⁷ mbar
  • +
  • 下游真空度应 < 1×10⁻⁶ mbar
  • +
+
+ + + diff --git a/public/guides/vacuum-valve-issue/step-2.html b/public/guides/vacuum-valve-issue/step-2.html new file mode 100644 index 0000000..34aad0c --- /dev/null +++ b/public/guides/vacuum-valve-issue/step-2.html @@ -0,0 +1,89 @@ + + + + + + + 步骤 2 - 检查联锁状态 + + + + +
步骤 2: 检查联锁状态
+ +
+ 阀门控制面板 + + + + 联锁指示区 + +
+ +
+

如果真空度满足要求但阀门仍无法打开,检查联锁系统:

+
    +
  1. 查看阀门控制面板的联锁指示灯
  2. +
  3. 确认相关设备(离子泵、分子泵)运行正常
  4. +
  5. 检查 EPS 系统是否有相关报警
  6. +
+
+ + + diff --git a/public/guides/vacuum-valve-issue/step-3.html b/public/guides/vacuum-valve-issue/step-3.html new file mode 100644 index 0000000..f6cb542 --- /dev/null +++ b/public/guides/vacuum-valve-issue/step-3.html @@ -0,0 +1,108 @@ + + + + + + + 步骤 3 - 尝试手动复位 + + + + +
步骤 3: 尝试手动复位
+ +
+ 阀门复位操作 + + + + 复位按钮 + +
+ +
+

如果联锁状态异常,尝试手动复位:

+
    +
  1. 按下阀门控制面板上的"复位"按钮
  2. +
  3. 等待 5 秒
  4. +
  5. 再次尝试打开阀门
  6. +
+
+ +
+ ⚠️ 重要提醒 + 复位前必须确认真空度满足要求!在真空度不达标时强行复位可能导致设备损坏。 +
+ + + diff --git a/public/guides/vacuum-valve-issue/step-4.html b/public/guides/vacuum-valve-issue/step-4.html new file mode 100644 index 0000000..e0f8e2e --- /dev/null +++ b/public/guides/vacuum-valve-issue/step-4.html @@ -0,0 +1,96 @@ + + + + + + + 步骤 4 - 检查气动系统 + + + + +
步骤 4: 检查气动系统
+ +
+ 气动系统 + + + + 气动控制单元 + +
+ +
+

如果复位后仍无法打开,检查气动系统:

+
    +
  1. 确认压缩空气供应正常(压力 > 5 bar
  2. +
  3. 检查气动管路是否有泄漏
  4. +
  5. 查看电磁阀是否工作(应有"咔哒"声)
  6. +
+
+ + + diff --git a/public/guides/vacuum-valve-issue/step-5.html b/public/guides/vacuum-valve-issue/step-5.html new file mode 100644 index 0000000..1c89311 --- /dev/null +++ b/public/guides/vacuum-valve-issue/step-5.html @@ -0,0 +1,123 @@ + + + + + + + 步骤 5 - 联系维护人员 + + + + +
步骤 5: 联系维护人员
+ +
+

如果以上步骤都无法解决问题,可能是阀门机械故障或控制系统故障。请联系维护人员。

+
+ +
+

维护人员联系方式

+
+ 真空系统负责人 + 内线 1234 +
+
+ 控制系统负责人 + 内线 5678 +
+
+ +
+

请记录以下信息以便维护人员排查:

+
    +
  • 故障时间
  • +
  • 真空度读数
  • +
  • 已执行的操作步骤
  • +
+
+ + + diff --git a/public/guides/water-leak-alarm/step-1.html b/public/guides/water-leak-alarm/step-1.html new file mode 100644 index 0000000..231b326 --- /dev/null +++ b/public/guides/water-leak-alarm/step-1.html @@ -0,0 +1,115 @@ + + + + + + + 步骤 1 - 确认报警位置 + + + + +
+ ⚠️ 紧急处理流程 +

水泄漏报警触发后应立即按照以下步骤处理,防止设备损坏。如遇大面积漏水,请先关闭主水阀并通知值班负责人。

+
+ +
步骤 1: 确认报警位置
+ +
+ EPS 机柜 + + + + + + + + + 水泄漏检测模块 + + 查看编号 + +
+ +
+

水泄漏报警已触发。首先需要确认漏水点的具体位置。

+

请查看设备保护系统(EPS)机柜上的水泄漏检测模块,记录显示的数字编号。

+
+ + + diff --git a/public/guides/water-leak-alarm/step-2.html b/public/guides/water-leak-alarm/step-2.html new file mode 100644 index 0000000..8fd2079 --- /dev/null +++ b/public/guides/water-leak-alarm/step-2.html @@ -0,0 +1,94 @@ + + + + + + + 步骤 2 - 搜索光学棚屋 + + + + +
步骤 2: 搜索光学棚屋
+ +
+ 光学棚屋布局图 + + + + 1-10 + + 11-20 + + 21-30 + +
+ +
+

根据检测模块显示的编号,进入光学棚屋找到对应的水泄漏检测线缆。

+

线缆编号对应位置:

+
    +
  • 1-10:单色器区域
  • +
  • 11-20:聚焦镜区域
  • +
  • 21-30:实验站区域
  • +
+
+ + + diff --git a/public/guides/water-leak-alarm/step-3.html b/public/guides/water-leak-alarm/step-3.html new file mode 100644 index 0000000..0f7b0d1 --- /dev/null +++ b/public/guides/water-leak-alarm/step-3.html @@ -0,0 +1,109 @@ + + + + + + + 步骤 3 - 定位并处理漏水点 + + + + +
步骤 3: 定位并处理漏水点
+ +
+ 漏水检测线缆 + + + + 检查此区域 + +
+ +
+

找到对应编号的线缆后,沿线缆检查漏水点:

+
    +
  1. 查看线缆周围是否有水渍
  2. +
  3. 检查附近的冷却水管接头
  4. +
  5. 用干布擦干水渍
  6. +
  7. 如果是管道漏水,需要关闭该区域冷却水阀门并通知维护人员
  8. +
+
+ +
+ ⚠️ 注意 + 如果是管道漏水,应立即关闭该区域冷却水阀门,并联系维护人员处理。 +
+ + + diff --git a/public/guides/water-leak-alarm/step-4.html b/public/guides/water-leak-alarm/step-4.html new file mode 100644 index 0000000..f4f6a13 --- /dev/null +++ b/public/guides/water-leak-alarm/step-4.html @@ -0,0 +1,108 @@ + + + + + + + 步骤 4 - 复位报警 + + + + +
步骤 4: 复位报警
+ +
+ EPS 复位面板 + + + + 复位按钮 + +
+ +
+

处理完漏水点后,返回 EPS 机柜:

+
    +
  1. 确认水泄漏检测模块指示灯已熄灭
  2. +
  3. 按下"复位"按钮
  4. +
  5. 观察系统是否恢复正常
  6. +
+
+ +
+ ⚠️ 注意 + 如果报警持续,说明漏水未完全处理,需要重新检查。 +
+ + + diff --git a/public/guides/water-leak-alarm/step-5.html b/public/guides/water-leak-alarm/step-5.html new file mode 100644 index 0000000..9f6d5d8 --- /dev/null +++ b/public/guides/water-leak-alarm/step-5.html @@ -0,0 +1,71 @@ + + + + + + + 步骤 5 - 完成 + + + + +
步骤 5: 完成
+ +
+
✓ 水泄漏报警已成功处理!
+
+

后续建议:

+
    +
  • 记录漏水位置和处理方法
  • +
  • 如果是管道问题,提交维护工单
  • +
  • 增加该区域的巡检频率
  • +
+
+
+ + + diff --git a/public/js/solution-forest/filament-tree/components/filament-tree-component.js b/public/js/solution-forest/filament-tree/components/filament-tree-component.js new file mode 100644 index 0000000..759b433 --- /dev/null +++ b/public/js/solution-forest/filament-tree/components/filament-tree-component.js @@ -0,0 +1,25 @@ +var Gi=Object.create;var On=Object.defineProperty;var Qi=Object.getOwnPropertyDescriptor;var Ji=Object.getOwnPropertyNames;var Ki=Object.getPrototypeOf,Zi=Object.prototype.hasOwnProperty;var er=(h,N)=>()=>(N||h((N={exports:{}}).exports,N),N.exports);var tr=(h,N,S,Y)=>{if(N&&typeof N=="object"||typeof N=="function")for(let k of Ji(N))!Zi.call(h,k)&&k!==S&&On(h,k,{get:()=>N[k],enumerable:!(Y=Qi(N,k))||Y.enumerable});return h};var Mn=(h,N,S)=>(S=h!=null?Gi(Ki(h)):{},tr(N||!h||!h.__esModule?On(S,"default",{value:h,enumerable:!0}):S,h));var at=er((Rn,bt)=>{(function(h,N){"use strict";typeof bt=="object"&&typeof bt.exports=="object"?bt.exports=h.document?N(h,!0):function(S){if(!S.document)throw new Error("jQuery requires a window with a document");return N(S)}:N(h)})(typeof window<"u"?window:Rn,function(h,N){"use strict";var S=[],Y=Object.getPrototypeOf,k=S.slice,W=S.flat?function(e){return S.flat.call(e)}:function(e){return S.concat.apply([],e)},I=S.push,E=S.indexOf,ce={},Ce=ce.toString,ae=ce.hasOwnProperty,Me=ae.toString,xt=Me.call(Object),O={},M=function(t){return typeof t=="function"&&typeof t.nodeType!="number"&&typeof t.item!="function"},Re=function(t){return t!=null&&t===t.window},P=h.document,Wn={type:!0,src:!0,nonce:!0,noModule:!0};function zt(e,t,n){n=n||P;var i,o,s=n.createElement("script");if(s.text=e,t)for(i in Wn)o=t[i]||t.getAttribute&&t.getAttribute(i),o&&s.setAttribute(i,o);n.head.appendChild(s).parentNode.removeChild(s)}function Ie(e){return e==null?e+"":typeof e=="object"||typeof e=="function"?ce[Ce.call(e)]||"object":typeof e}var Ut="3.7.1",Fn=/HTML$/i,r=function(e,t){return new r.fn.init(e,t)};r.fn=r.prototype={jquery:Ut,constructor:r,length:0,toArray:function(){return k.call(this)},get:function(e){return e==null?k.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=r.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return r.each(this,e)},map:function(e){return this.pushStack(r.map(this,function(t,n){return e.call(t,n,t)}))},slice:function(){return this.pushStack(k.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},even:function(){return this.pushStack(r.grep(this,function(e,t){return(t+1)%2}))},odd:function(){return this.pushStack(r.grep(this,function(e,t){return t%2}))},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(n>=0&&n0&&t-1 in e}function Q(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var Yn=S.pop,Bn=S.sort,$n=S.splice,U="[\\x20\\t\\r\\n\\f]",Ge=new RegExp("^"+U+"+|((?:^|[^\\\\])(?:\\\\.)*)"+U+"+$","g");r.contains=function(e,t){var n=t&&t.parentNode;return e===n||!!(n&&n.nodeType===1&&(e.contains?e.contains(n):e.compareDocumentPosition&&e.compareDocumentPosition(n)&16))};var zn=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g;function Un(e,t){return t?e==="\0"?"\uFFFD":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e}r.escapeSelector=function(e){return(e+"").replace(zn,Un)};var Te=P,Tt=I;(function(){var e,t,n,i,o,s=Tt,a,l,f,d,v,b=r.expando,g=0,x=0,H=gt(),B=gt(),R=gt(),Z=gt(),K=function(u,c){return u===c&&(o=!0),0},ye="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",ve="(?:\\\\[\\da-fA-F]{1,6}"+U+"?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+",F="\\["+U+"*("+ve+")(?:"+U+"*([*^$|!~]?=)"+U+`*(?:'((?:\\\\.|[^\\\\'])*)'|"((?:\\\\.|[^\\\\"])*)"|(`+ve+"))|)"+U+"*\\]",Pe=":("+ve+`)(?:\\((('((?:\\\\.|[^\\\\'])*)'|"((?:\\\\.|[^\\\\"])*)")|((?:\\\\.|[^\\\\()[\\]]|`+F+")*)|.*)\\)|)",$=new RegExp(U+"+","g"),J=new RegExp("^"+U+"*,"+U+"*"),rt=new RegExp("^"+U+"*([>+~]|"+U+")"+U+"*"),Xt=new RegExp(U+"|>"),me=new RegExp(Pe),ot=new RegExp("^"+ve+"$"),be={ID:new RegExp("^#("+ve+")"),CLASS:new RegExp("^\\.("+ve+")"),TAG:new RegExp("^("+ve+"|[*])"),ATTR:new RegExp("^"+F),PSEUDO:new RegExp("^"+Pe),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+U+"*(even|odd|(([+-]|)(\\d*)n|)"+U+"*(?:([+-]|)"+U+"*(\\d+)|))"+U+"*\\)|)","i"),bool:new RegExp("^(?:"+ye+")$","i"),needsContext:new RegExp("^"+U+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+U+"*((?:-\\d)?\\d*)"+U+"*\\)|)(?=[^-]|$)","i")},De=/^(?:input|select|textarea|button)$/i,ke=/^h\d$/i,fe=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,_t=/[+~]/,Ne=new RegExp("\\\\[\\da-fA-F]{1,6}"+U+"?|\\\\([^\\r\\n\\f])","g"),Se=function(u,c){var p="0x"+u.slice(1)-65536;return c||(p<0?String.fromCharCode(p+65536):String.fromCharCode(p>>10|55296,p&1023|56320))},Fi=function(){je()},Yi=vt(function(u){return u.disabled===!0&&Q(u,"fieldset")},{dir:"parentNode",next:"legend"});function Bi(){try{return a.activeElement}catch{}}try{s.apply(S=k.call(Te.childNodes),Te.childNodes),S[Te.childNodes.length].nodeType}catch{s={apply:function(c,p){Tt.apply(c,k.call(p))},call:function(c){Tt.apply(c,k.call(arguments,1))}}}function z(u,c,p,y){var m,C,T,A,w,X,L,q=c&&c.ownerDocument,_=c?c.nodeType:9;if(p=p||[],typeof u!="string"||!u||_!==1&&_!==9&&_!==11)return p;if(!y&&(je(c),c=c||a,f)){if(_!==11&&(w=fe.exec(u)))if(m=w[1]){if(_===9)if(T=c.getElementById(m)){if(T.id===m)return s.call(p,T),p}else return p;else if(q&&(T=q.getElementById(m))&&z.contains(c,T)&&T.id===m)return s.call(p,T),p}else{if(w[2])return s.apply(p,c.getElementsByTagName(u)),p;if((m=w[3])&&c.getElementsByClassName)return s.apply(p,c.getElementsByClassName(m)),p}if(!Z[u+" "]&&(!d||!d.test(u))){if(L=u,q=c,_===1&&(Xt.test(u)||rt.test(u))){for(q=_t.test(u)&&Wt(c.parentNode)||c,(q!=c||!O.scope)&&((A=c.getAttribute("id"))?A=r.escapeSelector(A):c.setAttribute("id",A=b)),X=st(u),C=X.length;C--;)X[C]=(A?"#"+A:":scope")+" "+yt(X[C]);L=X.join(",")}try{return s.apply(p,q.querySelectorAll(L)),p}catch{Z(u,!0)}finally{A===b&&c.removeAttribute("id")}}}return Pn(u.replace(Ge,"$1"),c,p,y)}function gt(){var u=[];function c(p,y){return u.push(p+" ")>t.cacheLength&&delete c[u.shift()],c[p+" "]=y}return c}function pe(u){return u[b]=!0,u}function Ue(u){var c=a.createElement("fieldset");try{return!!u(c)}catch{return!1}finally{c.parentNode&&c.parentNode.removeChild(c),c=null}}function $i(u){return function(c){return Q(c,"input")&&c.type===u}}function zi(u){return function(c){return(Q(c,"input")||Q(c,"button"))&&c.type===u}}function Hn(u){return function(c){return"form"in c?c.parentNode&&c.disabled===!1?"label"in c?"label"in c.parentNode?c.parentNode.disabled===u:c.disabled===u:c.isDisabled===u||c.isDisabled!==!u&&Yi(c)===u:c.disabled===u:"label"in c?c.disabled===u:!1}}function Oe(u){return pe(function(c){return c=+c,pe(function(p,y){for(var m,C=u([],p.length,c),T=C.length;T--;)p[m=C[T]]&&(p[m]=!(y[m]=p[m]))})})}function Wt(u){return u&&typeof u.getElementsByTagName<"u"&&u}function je(u){var c,p=u?u.ownerDocument||u:Te;return p==a||p.nodeType!==9||!p.documentElement||(a=p,l=a.documentElement,f=!r.isXMLDoc(a),v=l.matches||l.webkitMatchesSelector||l.msMatchesSelector,l.msMatchesSelector&&Te!=a&&(c=a.defaultView)&&c.top!==c&&c.addEventListener("unload",Fi),O.getById=Ue(function(y){return l.appendChild(y).id=r.expando,!a.getElementsByName||!a.getElementsByName(r.expando).length}),O.disconnectedMatch=Ue(function(y){return v.call(y,"*")}),O.scope=Ue(function(){return a.querySelectorAll(":scope")}),O.cssHas=Ue(function(){try{return a.querySelector(":has(*,:jqfake)"),!1}catch{return!0}}),O.getById?(t.filter.ID=function(y){var m=y.replace(Ne,Se);return function(C){return C.getAttribute("id")===m}},t.find.ID=function(y,m){if(typeof m.getElementById<"u"&&f){var C=m.getElementById(y);return C?[C]:[]}}):(t.filter.ID=function(y){var m=y.replace(Ne,Se);return function(C){var T=typeof C.getAttributeNode<"u"&&C.getAttributeNode("id");return T&&T.value===m}},t.find.ID=function(y,m){if(typeof m.getElementById<"u"&&f){var C,T,A,w=m.getElementById(y);if(w){if(C=w.getAttributeNode("id"),C&&C.value===y)return[w];for(A=m.getElementsByName(y),T=0;w=A[T++];)if(C=w.getAttributeNode("id"),C&&C.value===y)return[w]}return[]}}),t.find.TAG=function(y,m){return typeof m.getElementsByTagName<"u"?m.getElementsByTagName(y):m.querySelectorAll(y)},t.find.CLASS=function(y,m){if(typeof m.getElementsByClassName<"u"&&f)return m.getElementsByClassName(y)},d=[],Ue(function(y){var m;l.appendChild(y).innerHTML="",y.querySelectorAll("[selected]").length||d.push("\\["+U+"*(?:value|"+ye+")"),y.querySelectorAll("[id~="+b+"-]").length||d.push("~="),y.querySelectorAll("a#"+b+"+*").length||d.push(".#.+[+~]"),y.querySelectorAll(":checked").length||d.push(":checked"),m=a.createElement("input"),m.setAttribute("type","hidden"),y.appendChild(m).setAttribute("name","D"),l.appendChild(y).disabled=!0,y.querySelectorAll(":disabled").length!==2&&d.push(":enabled",":disabled"),m=a.createElement("input"),m.setAttribute("name",""),y.appendChild(m),y.querySelectorAll("[name='']").length||d.push("\\["+U+"*name"+U+"*="+U+`*(?:''|"")`)}),O.cssHas||d.push(":has"),d=d.length&&new RegExp(d.join("|")),K=function(y,m){if(y===m)return o=!0,0;var C=!y.compareDocumentPosition-!m.compareDocumentPosition;return C||(C=(y.ownerDocument||y)==(m.ownerDocument||m)?y.compareDocumentPosition(m):1,C&1||!O.sortDetached&&m.compareDocumentPosition(y)===C?y===a||y.ownerDocument==Te&&z.contains(Te,y)?-1:m===a||m.ownerDocument==Te&&z.contains(Te,m)?1:i?E.call(i,y)-E.call(i,m):0:C&4?-1:1)}),a}z.matches=function(u,c){return z(u,null,null,c)},z.matchesSelector=function(u,c){if(je(u),f&&!Z[c+" "]&&(!d||!d.test(c)))try{var p=v.call(u,c);if(p||O.disconnectedMatch||u.document&&u.document.nodeType!==11)return p}catch{Z(c,!0)}return z(c,a,null,[u]).length>0},z.contains=function(u,c){return(u.ownerDocument||u)!=a&&je(u),r.contains(u,c)},z.attr=function(u,c){(u.ownerDocument||u)!=a&&je(u);var p=t.attrHandle[c.toLowerCase()],y=p&&ae.call(t.attrHandle,c.toLowerCase())?p(u,c,!f):void 0;return y!==void 0?y:u.getAttribute(c)},z.error=function(u){throw new Error("Syntax error, unrecognized expression: "+u)},r.uniqueSort=function(u){var c,p=[],y=0,m=0;if(o=!O.sortStable,i=!O.sortStable&&k.call(u,0),Bn.call(u,K),o){for(;c=u[m++];)c===u[m]&&(y=p.push(m));for(;y--;)$n.call(u,p[y],1)}return i=null,u},r.fn.uniqueSort=function(){return this.pushStack(r.uniqueSort(k.apply(this)))},t=r.expr={cacheLength:50,createPseudo:pe,match:be,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(u){return u[1]=u[1].replace(Ne,Se),u[3]=(u[3]||u[4]||u[5]||"").replace(Ne,Se),u[2]==="~="&&(u[3]=" "+u[3]+" "),u.slice(0,4)},CHILD:function(u){return u[1]=u[1].toLowerCase(),u[1].slice(0,3)==="nth"?(u[3]||z.error(u[0]),u[4]=+(u[4]?u[5]+(u[6]||1):2*(u[3]==="even"||u[3]==="odd")),u[5]=+(u[7]+u[8]||u[3]==="odd")):u[3]&&z.error(u[0]),u},PSEUDO:function(u){var c,p=!u[6]&&u[2];return be.CHILD.test(u[0])?null:(u[3]?u[2]=u[4]||u[5]||"":p&&me.test(p)&&(c=st(p,!0))&&(c=p.indexOf(")",p.length-c)-p.length)&&(u[0]=u[0].slice(0,c),u[2]=p.slice(0,c)),u.slice(0,3))}},filter:{TAG:function(u){var c=u.replace(Ne,Se).toLowerCase();return u==="*"?function(){return!0}:function(p){return Q(p,c)}},CLASS:function(u){var c=H[u+" "];return c||(c=new RegExp("(^|"+U+")"+u+"("+U+"|$)"))&&H(u,function(p){return c.test(typeof p.className=="string"&&p.className||typeof p.getAttribute<"u"&&p.getAttribute("class")||"")})},ATTR:function(u,c,p){return function(y){var m=z.attr(y,u);return m==null?c==="!=":c?(m+="",c==="="?m===p:c==="!="?m!==p:c==="^="?p&&m.indexOf(p)===0:c==="*="?p&&m.indexOf(p)>-1:c==="$="?p&&m.slice(-p.length)===p:c==="~="?(" "+m.replace($," ")+" ").indexOf(p)>-1:c==="|="?m===p||m.slice(0,p.length+1)===p+"-":!1):!0}},CHILD:function(u,c,p,y,m){var C=u.slice(0,3)!=="nth",T=u.slice(-4)!=="last",A=c==="of-type";return y===1&&m===0?function(w){return!!w.parentNode}:function(w,X,L){var q,_,j,V,se,ee=C!==T?"nextSibling":"previousSibling",le=w.parentNode,xe=A&&w.nodeName.toLowerCase(),Ve=!L&&!A,te=!1;if(le){if(C){for(;ee;){for(j=w;j=j[ee];)if(A?Q(j,xe):j.nodeType===1)return!1;se=ee=u==="only"&&!se&&"nextSibling"}return!0}if(se=[T?le.firstChild:le.lastChild],T&&Ve){for(_=le[b]||(le[b]={}),q=_[u]||[],V=q[0]===g&&q[1],te=V&&q[2],j=V&&le.childNodes[V];j=++V&&j&&j[ee]||(te=V=0)||se.pop();)if(j.nodeType===1&&++te&&j===w){_[u]=[g,V,te];break}}else if(Ve&&(_=w[b]||(w[b]={}),q=_[u]||[],V=q[0]===g&&q[1],te=V),te===!1)for(;(j=++V&&j&&j[ee]||(te=V=0)||se.pop())&&!((A?Q(j,xe):j.nodeType===1)&&++te&&(Ve&&(_=j[b]||(j[b]={}),_[u]=[g,te]),j===w)););return te-=m,te===y||te%y===0&&te/y>=0}}},PSEUDO:function(u,c){var p,y=t.pseudos[u]||t.setFilters[u.toLowerCase()]||z.error("unsupported pseudo: "+u);return y[b]?y(c):y.length>1?(p=[u,u,"",c],t.setFilters.hasOwnProperty(u.toLowerCase())?pe(function(m,C){for(var T,A=y(m,c),w=A.length;w--;)T=E.call(m,A[w]),m[T]=!(C[T]=A[w])}):function(m){return y(m,0,p)}):y}},pseudos:{not:pe(function(u){var c=[],p=[],y=$t(u.replace(Ge,"$1"));return y[b]?pe(function(m,C,T,A){for(var w,X=y(m,null,A,[]),L=m.length;L--;)(w=X[L])&&(m[L]=!(C[L]=w))}):function(m,C,T){return c[0]=m,y(c,null,T,p),c[0]=null,!p.pop()}}),has:pe(function(u){return function(c){return z(u,c).length>0}}),contains:pe(function(u){return u=u.replace(Ne,Se),function(c){return(c.textContent||r.text(c)).indexOf(u)>-1}}),lang:pe(function(u){return ot.test(u||"")||z.error("unsupported lang: "+u),u=u.replace(Ne,Se).toLowerCase(),function(c){var p;do if(p=f?c.lang:c.getAttribute("xml:lang")||c.getAttribute("lang"))return p=p.toLowerCase(),p===u||p.indexOf(u+"-")===0;while((c=c.parentNode)&&c.nodeType===1);return!1}}),target:function(u){var c=h.location&&h.location.hash;return c&&c.slice(1)===u.id},root:function(u){return u===l},focus:function(u){return u===Bi()&&a.hasFocus()&&!!(u.type||u.href||~u.tabIndex)},enabled:Hn(!1),disabled:Hn(!0),checked:function(u){return Q(u,"input")&&!!u.checked||Q(u,"option")&&!!u.selected},selected:function(u){return u.parentNode&&u.parentNode.selectedIndex,u.selected===!0},empty:function(u){for(u=u.firstChild;u;u=u.nextSibling)if(u.nodeType<6)return!1;return!0},parent:function(u){return!t.pseudos.empty(u)},header:function(u){return ke.test(u.nodeName)},input:function(u){return De.test(u.nodeName)},button:function(u){return Q(u,"input")&&u.type==="button"||Q(u,"button")},text:function(u){var c;return Q(u,"input")&&u.type==="text"&&((c=u.getAttribute("type"))==null||c.toLowerCase()==="text")},first:Oe(function(){return[0]}),last:Oe(function(u,c){return[c-1]}),eq:Oe(function(u,c,p){return[p<0?p+c:p]}),even:Oe(function(u,c){for(var p=0;pc?y=c:y=p;--y>=0;)u.push(y);return u}),gt:Oe(function(u,c,p){for(var y=p<0?p+c:p;++y1?function(c,p,y){for(var m=u.length;m--;)if(!u[m](c,p,y))return!1;return!0}:u[0]}function Ui(u,c,p){for(var y=0,m=c.length;y-1&&(T[L]=!(A[L]=_))}}else j=mt(j===A?j.splice(ee,j.length):j),m?m(null,A,j,X):s.apply(A,j)})}function Bt(u){for(var c,p,y,m=u.length,C=t.relative[u[0].type],T=C||t.relative[" "],A=C?1:0,w=vt(function(q){return q===c},T,!0),X=vt(function(q){return E.call(c,q)>-1},T,!0),L=[function(q,_,j){var V=!C&&(j||_!=n)||((c=_).nodeType?w(q,_,j):X(q,_,j));return c=null,V}];A1&&Ft(L),A>1&&yt(u.slice(0,A-1).concat({value:u[A-2].type===" "?"*":""})).replace(Ge,"$1"),p,A0,y=u.length>0,m=function(C,T,A,w,X){var L,q,_,j=0,V="0",se=C&&[],ee=[],le=n,xe=C||y&&t.find.TAG("*",X),Ve=g+=le==null?1:Math.random()||.1,te=xe.length;for(X&&(n=T==a||T||X);V!==te&&(L=xe[V])!=null;V++){if(y&&L){for(q=0,!T&&L.ownerDocument!=a&&(je(L),A=!f);_=u[q++];)if(_(L,T||a,A)){s.call(w,L);break}X&&(g=Ve)}p&&((L=!_&&L)&&j--,C&&se.push(L))}if(j+=V,p&&V!==j){for(q=0;_=c[q++];)_(se,ee,T,A);if(C){if(j>0)for(;V--;)se[V]||ee[V]||(ee[V]=Yn.call(w));ee=mt(ee)}s.apply(w,ee),X&&!C&&ee.length>0&&j+c.length>1&&r.uniqueSort(w)}return X&&(g=Ve,n=le),se};return p?pe(m):m}function $t(u,c){var p,y=[],m=[],C=R[u+" "];if(!C){for(c||(c=st(u)),p=c.length;p--;)C=Bt(c[p]),C[b]?y.push(C):m.push(C);C=R(u,Vi(m,y)),C.selector=u}return C}function Pn(u,c,p,y){var m,C,T,A,w,X=typeof u=="function"&&u,L=!y&&st(u=X.selector||u);if(p=p||[],L.length===1){if(C=L[0]=L[0].slice(0),C.length>2&&(T=C[0]).type==="ID"&&c.nodeType===9&&f&&t.relative[C[1].type]){if(c=(t.find.ID(T.matches[0].replace(Ne,Se),c)||[])[0],c)X&&(c=c.parentNode);else return p;u=u.slice(C.shift().value.length)}for(m=be.needsContext.test(u)?0:C.length;m--&&(T=C[m],!t.relative[A=T.type]);)if((w=t.find[A])&&(y=w(T.matches[0].replace(Ne,Se),_t.test(C[0].type)&&Wt(c.parentNode)||c))){if(C.splice(m,1),u=y.length&&yt(C),!u)return s.apply(p,y),p;break}}return(X||$t(u,L))(y,c,!f,p,!c||_t.test(u)&&Wt(c.parentNode)||c),p}O.sortStable=b.split("").sort(K).join("")===b,je(),O.sortDetached=Ue(function(u){return u.compareDocumentPosition(a.createElement("fieldset"))&1}),r.find=z,r.expr[":"]=r.expr.pseudos,r.unique=r.uniqueSort,z.compile=$t,z.select=Pn,z.setDocument=je,z.tokenize=st,z.escape=r.escapeSelector,z.getText=r.text,z.isXML=r.isXMLDoc,z.selectors=r.expr,z.support=r.support,z.uniqueSort=r.uniqueSort})();var Xe=function(e,t,n){for(var i=[],o=n!==void 0;(e=e[t])&&e.nodeType!==9;)if(e.nodeType===1){if(o&&r(e).is(n))break;i.push(e)}return i},Vt=function(e,t){for(var n=[];e;e=e.nextSibling)e.nodeType===1&&e!==t&&n.push(e);return n},Gt=r.expr.match.needsContext,Qt=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function wt(e,t,n){return M(t)?r.grep(e,function(i,o){return!!t.call(i,o,i)!==n}):t.nodeType?r.grep(e,function(i){return i===t!==n}):typeof t!="string"?r.grep(e,function(i){return E.call(t,i)>-1!==n}):r.filter(t,e,n)}r.filter=function(e,t,n){var i=t[0];return n&&(e=":not("+e+")"),t.length===1&&i.nodeType===1?r.find.matchesSelector(i,e)?[i]:[]:r.find.matches(e,r.grep(t,function(o){return o.nodeType===1}))},r.fn.extend({find:function(e){var t,n,i=this.length,o=this;if(typeof e!="string")return this.pushStack(r(e).filter(function(){for(t=0;t1?r.uniqueSort(n):n},filter:function(e){return this.pushStack(wt(this,e||[],!1))},not:function(e){return this.pushStack(wt(this,e||[],!0))},is:function(e){return!!wt(this,typeof e=="string"&&Gt.test(e)?r(e):e||[],!1).length}});var Jt,Vn=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,Gn=r.fn.init=function(e,t,n){var i,o;if(!e)return this;if(n=n||Jt,typeof e=="string")if(e[0]==="<"&&e[e.length-1]===">"&&e.length>=3?i=[null,e,null]:i=Vn.exec(e),i&&(i[1]||!t))if(i[1]){if(t=t instanceof r?t[0]:t,r.merge(this,r.parseHTML(i[1],t&&t.nodeType?t.ownerDocument||t:P,!0)),Qt.test(i[1])&&r.isPlainObject(t))for(i in t)M(this[i])?this[i](t[i]):this.attr(i,t[i]);return this}else return o=P.getElementById(i[2]),o&&(this[0]=o,this.length=1),this;else return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);else{if(e.nodeType)return this[0]=e,this.length=1,this;if(M(e))return n.ready!==void 0?n.ready(e):e(r)}return r.makeArray(e,this)};Gn.prototype=r.fn,Jt=r(P);var Qn=/^(?:parents|prev(?:Until|All))/,Jn={children:!0,contents:!0,next:!0,prev:!0};r.fn.extend({has:function(e){var t=r(e,this),n=t.length;return this.filter(function(){for(var i=0;i-1:n.nodeType===1&&r.find.matchesSelector(n,e))){s.push(n);break}}return this.pushStack(s.length>1?r.uniqueSort(s):s)},index:function(e){return e?typeof e=="string"?E.call(r(e),this[0]):E.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(r.uniqueSort(r.merge(this.get(),r(e,t))))},addBack:function(e){return this.add(e==null?this.prevObject:this.prevObject.filter(e))}});function Kt(e,t){for(;(e=e[t])&&e.nodeType!==1;);return e}r.each({parent:function(e){var t=e.parentNode;return t&&t.nodeType!==11?t:null},parents:function(e){return Xe(e,"parentNode")},parentsUntil:function(e,t,n){return Xe(e,"parentNode",n)},next:function(e){return Kt(e,"nextSibling")},prev:function(e){return Kt(e,"previousSibling")},nextAll:function(e){return Xe(e,"nextSibling")},prevAll:function(e){return Xe(e,"previousSibling")},nextUntil:function(e,t,n){return Xe(e,"nextSibling",n)},prevUntil:function(e,t,n){return Xe(e,"previousSibling",n)},siblings:function(e){return Vt((e.parentNode||{}).firstChild,e)},children:function(e){return Vt(e.firstChild)},contents:function(e){return e.contentDocument!=null&&Y(e.contentDocument)?e.contentDocument:(Q(e,"template")&&(e=e.content||e),r.merge([],e.childNodes))}},function(e,t){r.fn[e]=function(n,i){var o=r.map(this,t,n);return e.slice(-5)!=="Until"&&(i=n),i&&typeof i=="string"&&(o=r.filter(i,o)),this.length>1&&(Jn[e]||r.uniqueSort(o),Qn.test(e)&&o.reverse()),this.pushStack(o)}});var he=/[^\x20\t\r\n\f]+/g;function Kn(e){var t={};return r.each(e.match(he)||[],function(n,i){t[i]=!0}),t}r.Callbacks=function(e){e=typeof e=="string"?Kn(e):r.extend({},e);var t,n,i,o,s=[],a=[],l=-1,f=function(){for(o=o||e.once,i=t=!0;a.length;l=-1)for(n=a.shift();++l-1;)s.splice(g,1),g<=l&&l--}),this},has:function(v){return v?r.inArray(v,s)>-1:s.length>0},empty:function(){return s&&(s=[]),this},disable:function(){return o=a=[],s=n="",this},disabled:function(){return!s},lock:function(){return o=a=[],!n&&!t&&(s=n=""),this},locked:function(){return!!o},fireWith:function(v,b){return o||(b=b||[],b=[v,b.slice?b.slice():b],a.push(b),t||f()),this},fire:function(){return d.fireWith(this,arguments),this},fired:function(){return!!i}};return d};function _e(e){return e}function ut(e){throw e}function Zt(e,t,n,i){var o;try{e&&M(o=e.promise)?o.call(e).done(t).fail(n):e&&M(o=e.then)?o.call(e,t,n):t.apply(void 0,[e].slice(i))}catch(s){n.apply(void 0,[s])}}r.extend({Deferred:function(e){var t=[["notify","progress",r.Callbacks("memory"),r.Callbacks("memory"),2],["resolve","done",r.Callbacks("once memory"),r.Callbacks("once memory"),0,"resolved"],["reject","fail",r.Callbacks("once memory"),r.Callbacks("once memory"),1,"rejected"]],n="pending",i={state:function(){return n},always:function(){return o.done(arguments).fail(arguments),this},catch:function(s){return i.then(null,s)},pipe:function(){var s=arguments;return r.Deferred(function(a){r.each(t,function(l,f){var d=M(s[f[4]])&&s[f[4]];o[f[1]](function(){var v=d&&d.apply(this,arguments);v&&M(v.promise)?v.promise().progress(a.notify).done(a.resolve).fail(a.reject):a[f[0]+"With"](this,d?[v]:arguments)})}),s=null}).promise()},then:function(s,a,l){var f=0;function d(v,b,g,x){return function(){var H=this,B=arguments,R=function(){var K,ye;if(!(v=f&&(g!==ut&&(H=void 0,B=[K]),b.rejectWith(H,B))}};v?Z():(r.Deferred.getErrorHook?Z.error=r.Deferred.getErrorHook():r.Deferred.getStackHook&&(Z.error=r.Deferred.getStackHook()),h.setTimeout(Z))}}return r.Deferred(function(v){t[0][3].add(d(0,v,M(l)?l:_e,v.notifyWith)),t[1][3].add(d(0,v,M(s)?s:_e)),t[2][3].add(d(0,v,M(a)?a:ut))}).promise()},promise:function(s){return s!=null?r.extend(s,i):i}},o={};return r.each(t,function(s,a){var l=a[2],f=a[5];i[a[1]]=l.add,f&&l.add(function(){n=f},t[3-s][2].disable,t[3-s][3].disable,t[0][2].lock,t[0][3].lock),l.add(a[3].fire),o[a[0]]=function(){return o[a[0]+"With"](this===o?void 0:this,arguments),this},o[a[0]+"With"]=l.fireWith}),i.promise(o),e&&e.call(o,o),o},when:function(e){var t=arguments.length,n=t,i=Array(n),o=k.call(arguments),s=r.Deferred(),a=function(l){return function(f){i[l]=this,o[l]=arguments.length>1?k.call(arguments):f,--t||s.resolveWith(i,o)}};if(t<=1&&(Zt(e,s.done(a(n)).resolve,s.reject,!t),s.state()==="pending"||M(o[n]&&o[n].then)))return s.then();for(;n--;)Zt(o[n],a(n),s.reject);return s.promise()}});var Zn=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;r.Deferred.exceptionHook=function(e,t){h.console&&h.console.warn&&e&&Zn.test(e.name)&&h.console.warn("jQuery.Deferred exception: "+e.message,e.stack,t)},r.readyException=function(e){h.setTimeout(function(){throw e})};var Et=r.Deferred();r.fn.ready=function(e){return Et.then(e).catch(function(t){r.readyException(t)}),this},r.extend({isReady:!1,readyWait:1,ready:function(e){(e===!0?--r.readyWait:r.isReady)||(r.isReady=!0,!(e!==!0&&--r.readyWait>0)&&Et.resolveWith(P,[r]))}}),r.ready.then=Et.then;function ft(){P.removeEventListener("DOMContentLoaded",ft),h.removeEventListener("load",ft),r.ready()}P.readyState==="complete"||P.readyState!=="loading"&&!P.documentElement.doScroll?h.setTimeout(r.ready):(P.addEventListener("DOMContentLoaded",ft),h.addEventListener("load",ft));var we=function(e,t,n,i,o,s,a){var l=0,f=e.length,d=n==null;if(Ie(n)==="object"){o=!0;for(l in n)we(e,t,l,n[l],!0,s,a)}else if(i!==void 0&&(o=!0,M(i)||(a=!0),d&&(a?(t.call(e,i),t=null):(d=t,t=function(v,b,g){return d.call(r(v),g)})),t))for(;l1,null,!0)},removeData:function(e){return this.each(function(){ie.remove(this,e)})}}),r.extend({queue:function(e,t,n){var i;if(e)return t=(t||"fx")+"queue",i=D.get(e,t),n&&(!i||Array.isArray(n)?i=D.access(e,t,r.makeArray(n)):i.push(n)),i||[]},dequeue:function(e,t){t=t||"fx";var n=r.queue(e,t),i=n.length,o=n.shift(),s=r._queueHooks(e,t),a=function(){r.dequeue(e,t)};o==="inprogress"&&(o=n.shift(),i--),o&&(t==="fx"&&n.unshift("inprogress"),delete s.stop,o.call(e,a,s)),!i&&s&&s.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return D.get(e,n)||D.access(e,n,{empty:r.Callbacks("once memory").add(function(){D.remove(e,[t+"queue",n])})})}}),r.fn.extend({queue:function(e,t){var n=2;return typeof e!="string"&&(t=e,e="fx",n--),arguments.length\x20\t\r\n\f]*)/i,sn=/^$|^module$|\/(?:java|ecma)script/i;(function(){var e=P.createDocumentFragment(),t=e.appendChild(P.createElement("div")),n=P.createElement("input");n.setAttribute("type","radio"),n.setAttribute("checked","checked"),n.setAttribute("name","t"),t.appendChild(n),O.checkClone=t.cloneNode(!0).cloneNode(!0).lastChild.checked,t.innerHTML="",O.noCloneChecked=!!t.cloneNode(!0).lastChild.defaultValue,t.innerHTML="",O.option=!!t.lastChild})();var ue={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};ue.tbody=ue.tfoot=ue.colgroup=ue.caption=ue.thead,ue.th=ue.td,O.option||(ue.optgroup=ue.option=[1,""]);function re(e,t){var n;return typeof e.getElementsByTagName<"u"?n=e.getElementsByTagName(t||"*"):typeof e.querySelectorAll<"u"?n=e.querySelectorAll(t||"*"):n=[],t===void 0||t&&Q(e,t)?r.merge([e],n):n}function Nt(e,t){for(var n=0,i=e.length;n-1){o&&o.push(s);continue}if(d=We(s),a=re(b.appendChild(s),"script"),d&&Nt(a),n)for(v=0;s=a[v++];)sn.test(s.type||"")&&n.push(s)}return b}var un=/^([^.]*)(?:\.(.+)|)/;function Ye(){return!0}function Be(){return!1}function St(e,t,n,i,o,s){var a,l;if(typeof t=="object"){typeof n!="string"&&(i=i||n,n=void 0);for(l in t)St(e,l,n,i,t[l],s);return e}if(i==null&&o==null?(o=n,i=n=void 0):o==null&&(typeof n=="string"?(o=i,i=void 0):(o=i,i=n,n=void 0)),o===!1)o=Be;else if(!o)return e;return s===1&&(a=o,o=function(f){return r().off(f),a.apply(this,arguments)},o.guid=a.guid||(a.guid=r.guid++)),e.each(function(){r.event.add(this,t,o,i,n)})}r.event={global:{},add:function(e,t,n,i,o){var s,a,l,f,d,v,b,g,x,H,B,R=D.get(e);if(Qe(e))for(n.handler&&(s=n,n=s.handler,o=s.selector),o&&r.find.matchesSelector(Le,o),n.guid||(n.guid=r.guid++),(f=R.events)||(f=R.events=Object.create(null)),(a=R.handle)||(a=R.handle=function(Z){return typeof r<"u"&&r.event.triggered!==Z.type?r.event.dispatch.apply(e,arguments):void 0}),t=(t||"").match(he)||[""],d=t.length;d--;)l=un.exec(t[d])||[],x=B=l[1],H=(l[2]||"").split(".").sort(),x&&(b=r.event.special[x]||{},x=(o?b.delegateType:b.bindType)||x,b=r.event.special[x]||{},v=r.extend({type:x,origType:B,data:i,handler:n,guid:n.guid,selector:o,needsContext:o&&r.expr.match.needsContext.test(o),namespace:H.join(".")},s),(g=f[x])||(g=f[x]=[],g.delegateCount=0,(!b.setup||b.setup.call(e,i,H,a)===!1)&&e.addEventListener&&e.addEventListener(x,a)),b.add&&(b.add.call(e,v),v.handler.guid||(v.handler.guid=n.guid)),o?g.splice(g.delegateCount++,0,v):g.push(v),r.event.global[x]=!0)},remove:function(e,t,n,i,o){var s,a,l,f,d,v,b,g,x,H,B,R=D.hasData(e)&&D.get(e);if(!(!R||!(f=R.events))){for(t=(t||"").match(he)||[""],d=t.length;d--;){if(l=un.exec(t[d])||[],x=B=l[1],H=(l[2]||"").split(".").sort(),!x){for(x in f)r.event.remove(e,x+t[d],n,i,!0);continue}for(b=r.event.special[x]||{},x=(i?b.delegateType:b.bindType)||x,g=f[x]||[],l=l[2]&&new RegExp("(^|\\.)"+H.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=s=g.length;s--;)v=g[s],(o||B===v.origType)&&(!n||n.guid===v.guid)&&(!l||l.test(v.namespace))&&(!i||i===v.selector||i==="**"&&v.selector)&&(g.splice(s,1),v.selector&&g.delegateCount--,b.remove&&b.remove.call(e,v));a&&!g.length&&((!b.teardown||b.teardown.call(e,H,R.handle)===!1)&&r.removeEvent(e,x,R.handle),delete f[x])}r.isEmptyObject(f)&&D.remove(e,"handle events")}},dispatch:function(e){var t,n,i,o,s,a,l=new Array(arguments.length),f=r.event.fix(e),d=(D.get(this,"events")||Object.create(null))[f.type]||[],v=r.event.special[f.type]||{};for(l[0]=f,t=1;t=1)){for(;d!==this;d=d.parentNode||this)if(d.nodeType===1&&!(e.type==="click"&&d.disabled===!0)){for(s=[],a={},n=0;n-1:r.find(o,this,null,[d]).length),a[o]&&s.push(i);s.length&&l.push({elem:d,handlers:s})}}return d=this,f\s*$/g;function fn(e,t){return Q(e,"table")&&Q(t.nodeType!==11?t:t.firstChild,"tr")&&r(e).children("tbody")[0]||e}function di(e){return e.type=(e.getAttribute("type")!==null)+"/"+e.type,e}function pi(e){return(e.type||"").slice(0,5)==="true/"?e.type=e.type.slice(5):e.removeAttribute("type"),e}function ln(e,t){var n,i,o,s,a,l,f;if(t.nodeType===1){if(D.hasData(e)&&(s=D.get(e),f=s.events,f)){D.remove(t,"handle events");for(o in f)for(n=0,i=f[o].length;n1&&typeof x=="string"&&!O.checkClone&&li.test(x))return e.each(function(B){var R=e.eq(B);H&&(t[0]=x.call(this,B,R.html())),$e(R,t,n,i)});if(b&&(o=an(t,e[0].ownerDocument,!1,e,i),s=o.firstChild,o.childNodes.length===1&&(o=s),s||i)){for(a=r.map(re(o,"script"),di),l=a.length;v0&&Nt(a,!f&&re(e,"script")),l},cleanData:function(e){for(var t,n,i,o=r.event.special,s=0;(n=e[s])!==void 0;s++)if(Qe(n)){if(t=n[D.expando]){if(t.events)for(i in t.events)o[i]?r.event.remove(n,i):r.removeEvent(n,i,t.handle);n[D.expando]=void 0}n[ie.expando]&&(n[ie.expando]=void 0)}}}),r.fn.extend({detach:function(e){return cn(this,e,!0)},remove:function(e){return cn(this,e)},text:function(e){return we(this,function(t){return t===void 0?r.text(this):this.empty().each(function(){(this.nodeType===1||this.nodeType===11||this.nodeType===9)&&(this.textContent=t)})},null,e,arguments.length)},append:function(){return $e(this,arguments,function(e){if(this.nodeType===1||this.nodeType===11||this.nodeType===9){var t=fn(this,e);t.appendChild(e)}})},prepend:function(){return $e(this,arguments,function(e){if(this.nodeType===1||this.nodeType===11||this.nodeType===9){var t=fn(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return $e(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return $e(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;(e=this[t])!=null;t++)e.nodeType===1&&(r.cleanData(re(e,!1)),e.textContent="");return this},clone:function(e,t){return e=e??!1,t=t??e,this.map(function(){return r.clone(this,e,t)})},html:function(e){return we(this,function(t){var n=this[0]||{},i=0,o=this.length;if(t===void 0&&n.nodeType===1)return n.innerHTML;if(typeof t=="string"&&!fi.test(t)&&!ue[(on.exec(t)||["",""])[1].toLowerCase()]){t=r.htmlPrefilter(t);try{for(;i=0&&(f+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-s-f-l-.5))||0),f+d}function bn(e,t,n){var i=dt(e),o=!O.boxSizingReliable()||n,s=o&&r.css(e,"boxSizing",!1,i)==="border-box",a=s,l=et(e,t,i),f="offset"+t[0].toUpperCase()+t.slice(1);if(At.test(l)){if(!n)return l;l="auto"}return(!O.boxSizingReliable()&&s||!O.reliableTrDimensions()&&Q(e,"tr")||l==="auto"||!parseFloat(l)&&r.css(e,"display",!1,i)==="inline")&&e.getClientRects().length&&(s=r.css(e,"boxSizing",!1,i)==="border-box",a=f in e,a&&(l=e[f])),l=parseFloat(l)||0,l+jt(e,t,n||(s?"border":"content"),a,i,l)+"px"}r.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=et(e,"opacity");return n===""?"1":n}}}},cssNumber:{animationIterationCount:!0,aspectRatio:!0,borderImageSlice:!0,columnCount:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,gridArea:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnStart:!0,gridRow:!0,gridRowEnd:!0,gridRowStart:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,scale:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeMiterlimit:!0,strokeOpacity:!0},cssProps:{},style:function(e,t,n,i){if(!(!e||e.nodeType===3||e.nodeType===8||!e.style)){var o,s,a,l=ge(t),f=Dt.test(t),d=e.style;if(f||(t=kt(l)),a=r.cssHooks[t]||r.cssHooks[l],n!==void 0){if(s=typeof n,s==="string"&&(o=Ke.exec(n))&&o[1]&&(n=nn(e,t,o),s="number"),n==null||n!==n)return;s==="number"&&!f&&(n+=o&&o[3]||(r.cssNumber[l]?"":"px")),!O.clearCloneStyle&&n===""&&t.indexOf("background")===0&&(d[t]="inherit"),(!a||!("set"in a)||(n=a.set(e,n,i))!==void 0)&&(f?d.setProperty(t,n):d[t]=n)}else return a&&"get"in a&&(o=a.get(e,!1,i))!==void 0?o:d[t]}},css:function(e,t,n,i){var o,s,a,l=ge(t),f=Dt.test(t);return f||(t=kt(l)),a=r.cssHooks[t]||r.cssHooks[l],a&&"get"in a&&(o=a.get(e,!0,n)),o===void 0&&(o=et(e,t,i)),o==="normal"&&t in vn&&(o=vn[t]),n===""||n?(s=parseFloat(o),n===!0||isFinite(s)?s||0:o):o}}),r.each(["height","width"],function(e,t){r.cssHooks[t]={get:function(n,i,o){if(i)return vi.test(r.css(n,"display"))&&(!n.getClientRects().length||!n.getBoundingClientRect().width)?dn(n,mi,function(){return bn(n,t,o)}):bn(n,t,o)},set:function(n,i,o){var s,a=dt(n),l=!O.scrollboxSize()&&a.position==="absolute",f=l||o,d=f&&r.css(n,"boxSizing",!1,a)==="border-box",v=o?jt(n,t,o,d,a):0;return d&&l&&(v-=Math.ceil(n["offset"+t[0].toUpperCase()+t.slice(1)]-parseFloat(a[t])-jt(n,t,"border",!1,a)-.5)),v&&(s=Ke.exec(i))&&(s[3]||"px")!=="px"&&(n.style[t]=i,i=r.css(n,t)),mn(n,i,v)}}}),r.cssHooks.marginLeft=pn(O.reliableMarginLeft,function(e,t){if(t)return(parseFloat(et(e,"marginLeft"))||e.getBoundingClientRect().left-dn(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),r.each({margin:"",padding:"",border:"Width"},function(e,t){r.cssHooks[e+t]={expand:function(n){for(var i=0,o={},s=typeof n=="string"?n.split(" "):[n];i<4;i++)o[e+Ee[i]+t]=s[i]||s[i-2]||s[0];return o}},e!=="margin"&&(r.cssHooks[e+t].set=mn)}),r.fn.extend({css:function(e,t){return we(this,function(n,i,o){var s,a,l={},f=0;if(Array.isArray(i)){for(s=dt(n),a=i.length;f1)}});function oe(e,t,n,i,o){return new oe.prototype.init(e,t,n,i,o)}r.Tween=oe,oe.prototype={constructor:oe,init:function(e,t,n,i,o,s){this.elem=e,this.prop=n,this.easing=o||r.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=i,this.unit=s||(r.cssNumber[n]?"":"px")},cur:function(){var e=oe.propHooks[this.prop];return e&&e.get?e.get(this):oe.propHooks._default.get(this)},run:function(e){var t,n=oe.propHooks[this.prop];return this.options.duration?this.pos=t=r.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):oe.propHooks._default.set(this),this}},oe.prototype.init.prototype=oe.prototype,oe.propHooks={_default:{get:function(e){var t;return e.elem.nodeType!==1||e.elem[e.prop]!=null&&e.elem.style[e.prop]==null?e.elem[e.prop]:(t=r.css(e.elem,e.prop,""),!t||t==="auto"?0:t)},set:function(e){r.fx.step[e.prop]?r.fx.step[e.prop](e):e.elem.nodeType===1&&(r.cssHooks[e.prop]||e.elem.style[kt(e.prop)]!=null)?r.style(e.elem,e.prop,e.now+e.unit):e.elem[e.prop]=e.now}}},oe.propHooks.scrollTop=oe.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},r.easing={linear:function(e){return e},swing:function(e){return .5-Math.cos(e*Math.PI)/2},_default:"swing"},r.fx=oe.prototype.init,r.fx.step={};var ze,pt,bi=/^(?:toggle|show|hide)$/,xi=/queueHooks$/;function Lt(){pt&&(P.hidden===!1&&h.requestAnimationFrame?h.requestAnimationFrame(Lt):h.setTimeout(Lt,r.fx.interval),r.fx.tick())}function xn(){return h.setTimeout(function(){ze=void 0}),ze=Date.now()}function ht(e,t){var n,i=0,o={height:e};for(t=t?1:0;i<4;i+=2-t)n=Ee[i],o["margin"+n]=o["padding"+n]=e;return t&&(o.opacity=o.width=e),o}function Cn(e,t,n){for(var i,o=(de.tweeners[t]||[]).concat(de.tweeners["*"]),s=0,a=o.length;s1)},removeAttr:function(e){return this.each(function(){r.removeAttr(this,e)})}}),r.extend({attr:function(e,t,n){var i,o,s=e.nodeType;if(!(s===3||s===8||s===2)){if(typeof e.getAttribute>"u")return r.prop(e,t,n);if((s!==1||!r.isXMLDoc(e))&&(o=r.attrHooks[t.toLowerCase()]||(r.expr.match.bool.test(t)?Tn:void 0)),n!==void 0){if(n===null){r.removeAttr(e,t);return}return o&&"set"in o&&(i=o.set(e,n,t))!==void 0?i:(e.setAttribute(t,n+""),n)}return o&&"get"in o&&(i=o.get(e,t))!==null?i:(i=r.find.attr(e,t),i??void 0)}},attrHooks:{type:{set:function(e,t){if(!O.radioValue&&t==="radio"&&Q(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,i=0,o=t&&t.match(he);if(o&&e.nodeType===1)for(;n=o[i++];)e.removeAttribute(n)}}),Tn={set:function(e,t,n){return t===!1?r.removeAttr(e,n):e.setAttribute(n,n),n}},r.each(r.expr.match.bool.source.match(/\w+/g),function(e,t){var n=tt[t]||r.find.attr;tt[t]=function(i,o,s){var a,l,f=o.toLowerCase();return s||(l=tt[f],tt[f]=a,a=n(i,o,s)!=null?f:null,tt[f]=l),a}});var wi=/^(?:input|select|textarea|button)$/i,Ei=/^(?:a|area)$/i;r.fn.extend({prop:function(e,t){return we(this,r.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each(function(){delete this[r.propFix[e]||e]})}}),r.extend({prop:function(e,t,n){var i,o,s=e.nodeType;if(!(s===3||s===8||s===2))return(s!==1||!r.isXMLDoc(e))&&(t=r.propFix[t]||t,o=r.propHooks[t]),n!==void 0?o&&"set"in o&&(i=o.set(e,n,t))!==void 0?i:e[t]=n:o&&"get"in o&&(i=o.get(e,t))!==null?i:e[t]},propHooks:{tabIndex:{get:function(e){var t=r.find.attr(e,"tabindex");return t?parseInt(t,10):wi.test(e.nodeName)||Ei.test(e.nodeName)&&e.href?0:-1}}},propFix:{for:"htmlFor",class:"className"}}),O.optSelected||(r.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),r.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){r.propFix[this.toLowerCase()]=this});function He(e){var t=e.match(he)||[];return t.join(" ")}function qe(e){return e.getAttribute&&e.getAttribute("class")||""}function Ht(e){return Array.isArray(e)?e:typeof e=="string"?e.match(he)||[]:[]}r.fn.extend({addClass:function(e){var t,n,i,o,s,a;return M(e)?this.each(function(l){r(this).addClass(e.call(this,l,qe(this)))}):(t=Ht(e),t.length?this.each(function(){if(i=qe(this),n=this.nodeType===1&&" "+He(i)+" ",n){for(s=0;s-1;)n=n.replace(" "+o+" "," ");a=He(n),i!==a&&this.setAttribute("class",a)}}):this):this.attr("class","")},toggleClass:function(e,t){var n,i,o,s,a=typeof e,l=a==="string"||Array.isArray(e);return M(e)?this.each(function(f){r(this).toggleClass(e.call(this,f,qe(this),t),t)}):typeof t=="boolean"&&l?t?this.addClass(e):this.removeClass(e):(n=Ht(e),this.each(function(){if(l)for(s=r(this),o=0;o-1)return!0;return!1}});var Ni=/\r/g;r.fn.extend({val:function(e){var t,n,i,o=this[0];return arguments.length?(i=M(e),this.each(function(s){var a;this.nodeType===1&&(i?a=e.call(this,s,r(this).val()):a=e,a==null?a="":typeof a=="number"?a+="":Array.isArray(a)&&(a=r.map(a,function(l){return l==null?"":l+""})),t=r.valHooks[this.type]||r.valHooks[this.nodeName.toLowerCase()],(!t||!("set"in t)||t.set(this,a,"value")===void 0)&&(this.value=a))})):o?(t=r.valHooks[o.type]||r.valHooks[o.nodeName.toLowerCase()],t&&"get"in t&&(n=t.get(o,"value"))!==void 0?n:(n=o.value,typeof n=="string"?n.replace(Ni,""):n??"")):void 0}}),r.extend({valHooks:{option:{get:function(e){var t=r.find.attr(e,"value");return t??He(r.text(e))}},select:{get:function(e){var t,n,i,o=e.options,s=e.selectedIndex,a=e.type==="select-one",l=a?null:[],f=a?s+1:o.length;for(s<0?i=f:i=a?s:0;i-1)&&(n=!0);return n||(e.selectedIndex=-1),s}}}}),r.each(["radio","checkbox"],function(){r.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=r.inArray(r(e).val(),t)>-1}},O.checkOn||(r.valHooks[this].get=function(e){return e.getAttribute("value")===null?"on":e.value})});var nt=h.location,wn={guid:Date.now()},qt=/\?/;r.parseXML=function(e){var t,n;if(!e||typeof e!="string")return null;try{t=new h.DOMParser().parseFromString(e,"text/xml")}catch{}return n=t&&t.getElementsByTagName("parsererror")[0],(!t||n)&&r.error("Invalid XML: "+(n?r.map(n.childNodes,function(i){return i.textContent}).join(` +`):e)),t};var En=/^(?:focusinfocus|focusoutblur)$/,Nn=function(e){e.stopPropagation()};r.extend(r.event,{trigger:function(e,t,n,i){var o,s,a,l,f,d,v,b,g=[n||P],x=ae.call(e,"type")?e.type:e,H=ae.call(e,"namespace")?e.namespace.split("."):[];if(s=b=a=n=n||P,!(n.nodeType===3||n.nodeType===8)&&!En.test(x+r.event.triggered)&&(x.indexOf(".")>-1&&(H=x.split("."),x=H.shift(),H.sort()),f=x.indexOf(":")<0&&"on"+x,e=e[r.expando]?e:new r.Event(x,typeof e=="object"&&e),e.isTrigger=i?2:3,e.namespace=H.join("."),e.rnamespace=e.namespace?new RegExp("(^|\\.)"+H.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,e.result=void 0,e.target||(e.target=n),t=t==null?[e]:r.makeArray(t,[e]),v=r.event.special[x]||{},!(!i&&v.trigger&&v.trigger.apply(n,t)===!1))){if(!i&&!v.noBubble&&!Re(n)){for(l=v.delegateType||x,En.test(l+x)||(s=s.parentNode);s;s=s.parentNode)g.push(s),a=s;a===(n.ownerDocument||P)&&g.push(a.defaultView||a.parentWindow||h)}for(o=0;(s=g[o++])&&!e.isPropagationStopped();)b=s,e.type=o>1?l:v.bindType||x,d=(D.get(s,"events")||Object.create(null))[e.type]&&D.get(s,"handle"),d&&d.apply(s,t),d=f&&s[f],d&&d.apply&&Qe(s)&&(e.result=d.apply(s,t),e.result===!1&&e.preventDefault());return e.type=x,!i&&!e.isDefaultPrevented()&&(!v._default||v._default.apply(g.pop(),t)===!1)&&Qe(n)&&f&&M(n[x])&&!Re(n)&&(a=n[f],a&&(n[f]=null),r.event.triggered=x,e.isPropagationStopped()&&b.addEventListener(x,Nn),n[x](),e.isPropagationStopped()&&b.removeEventListener(x,Nn),r.event.triggered=void 0,a&&(n[f]=a)),e.result}},simulate:function(e,t,n){var i=r.extend(new r.Event,n,{type:e,isSimulated:!0});r.event.trigger(i,null,t)}}),r.fn.extend({trigger:function(e,t){return this.each(function(){r.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return r.event.trigger(e,t,n,!0)}});var Si=/\[\]$/,Sn=/\r?\n/g,Ai=/^(?:submit|button|image|reset|file)$/i,Di=/^(?:input|select|textarea|keygen)/i;function Pt(e,t,n,i){var o;if(Array.isArray(t))r.each(t,function(s,a){n||Si.test(e)?i(e,a):Pt(e+"["+(typeof a=="object"&&a!=null?s:"")+"]",a,n,i)});else if(!n&&Ie(t)==="object")for(o in t)Pt(e+"["+o+"]",t[o],n,i);else i(e,t)}r.param=function(e,t){var n,i=[],o=function(s,a){var l=M(a)?a():a;i[i.length]=encodeURIComponent(s)+"="+encodeURIComponent(l??"")};if(e==null)return"";if(Array.isArray(e)||e.jquery&&!r.isPlainObject(e))r.each(e,function(){o(this.name,this.value)});else for(n in e)Pt(n,e[n],t,o);return i.join("&")},r.fn.extend({serialize:function(){return r.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=r.prop(this,"elements");return e?r.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!r(this).is(":disabled")&&Di.test(this.nodeName)&&!Ai.test(e)&&(this.checked||!Ze.test(e))}).map(function(e,t){var n=r(this).val();return n==null?null:Array.isArray(n)?r.map(n,function(i){return{name:t.name,value:i.replace(Sn,`\r +`)}}):{name:t.name,value:n.replace(Sn,`\r +`)}}).get()}});var ki=/%20/g,ji=/#.*$/,Li=/([?&])_=[^&]*/,Hi=/^(.*?):[ \t]*([^\r\n]*)$/mg,qi=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Pi=/^(?:GET|HEAD)$/,Oi=/^\/\//,An={},Ot={},Dn="*/".concat("*"),Mt=P.createElement("a");Mt.href=nt.href;function kn(e){return function(t,n){typeof t!="string"&&(n=t,t="*");var i,o=0,s=t.toLowerCase().match(he)||[];if(M(n))for(;i=s[o++];)i[0]==="+"?(i=i.slice(1)||"*",(e[i]=e[i]||[]).unshift(n)):(e[i]=e[i]||[]).push(n)}}function jn(e,t,n,i){var o={},s=e===Ot;function a(l){var f;return o[l]=!0,r.each(e[l]||[],function(d,v){var b=v(t,n,i);if(typeof b=="string"&&!s&&!o[b])return t.dataTypes.unshift(b),a(b),!1;if(s)return!(f=b)}),f}return a(t.dataTypes[0])||!o["*"]&&a("*")}function Rt(e,t){var n,i,o=r.ajaxSettings.flatOptions||{};for(n in t)t[n]!==void 0&&((o[n]?e:i||(i={}))[n]=t[n]);return i&&r.extend(!0,e,i),e}function Mi(e,t,n){for(var i,o,s,a,l=e.contents,f=e.dataTypes;f[0]==="*";)f.shift(),i===void 0&&(i=e.mimeType||t.getResponseHeader("Content-Type"));if(i){for(o in l)if(l[o]&&l[o].test(i)){f.unshift(o);break}}if(f[0]in n)s=f[0];else{for(o in n){if(!f[0]||e.converters[o+" "+f[0]]){s=o;break}a||(a=o)}s=s||a}if(s)return s!==f[0]&&f.unshift(s),n[s]}function Ri(e,t,n,i){var o,s,a,l,f,d={},v=e.dataTypes.slice();if(v[1])for(a in e.converters)d[a.toLowerCase()]=e.converters[a];for(s=v.shift();s;)if(e.responseFields[s]&&(n[e.responseFields[s]]=t),!f&&i&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),f=s,s=v.shift(),s){if(s==="*")s=f;else if(f!=="*"&&f!==s){if(a=d[f+" "+s]||d["* "+s],!a){for(o in d)if(l=o.split(" "),l[1]===s&&(a=d[f+" "+l[0]]||d["* "+l[0]],a)){a===!0?a=d[o]:d[o]!==!0&&(s=l[0],v.unshift(l[1]));break}}if(a!==!0)if(a&&e.throws)t=a(t);else try{t=a(t)}catch(b){return{state:"parsererror",error:a?b:"No conversion from "+f+" to "+s}}}}return{state:"success",data:t}}r.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:nt.href,type:"GET",isLocal:qi.test(nt.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Dn,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":r.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?Rt(Rt(e,r.ajaxSettings),t):Rt(r.ajaxSettings,e)},ajaxPrefilter:kn(An),ajaxTransport:kn(Ot),ajax:function(e,t){typeof e=="object"&&(t=e,e=void 0),t=t||{};var n,i,o,s,a,l,f,d,v,b,g=r.ajaxSetup({},t),x=g.context||g,H=g.context&&(x.nodeType||x.jquery)?r(x):r.event,B=r.Deferred(),R=r.Callbacks("once memory"),Z=g.statusCode||{},K={},ye={},ve="canceled",F={readyState:0,getResponseHeader:function($){var J;if(f){if(!s)for(s={};J=Hi.exec(o);)s[J[1].toLowerCase()+" "]=(s[J[1].toLowerCase()+" "]||[]).concat(J[2]);J=s[$.toLowerCase()+" "]}return J==null?null:J.join(", ")},getAllResponseHeaders:function(){return f?o:null},setRequestHeader:function($,J){return f==null&&($=ye[$.toLowerCase()]=ye[$.toLowerCase()]||$,K[$]=J),this},overrideMimeType:function($){return f==null&&(g.mimeType=$),this},statusCode:function($){var J;if($)if(f)F.always($[F.status]);else for(J in $)Z[J]=[Z[J],$[J]];return this},abort:function($){var J=$||ve;return n&&n.abort(J),Pe(0,J),this}};if(B.promise(F),g.url=((e||g.url||nt.href)+"").replace(Oi,nt.protocol+"//"),g.type=t.method||t.type||g.method||g.type,g.dataTypes=(g.dataType||"*").toLowerCase().match(he)||[""],g.crossDomain==null){l=P.createElement("a");try{l.href=g.url,l.href=l.href,g.crossDomain=Mt.protocol+"//"+Mt.host!=l.protocol+"//"+l.host}catch{g.crossDomain=!0}}if(g.data&&g.processData&&typeof g.data!="string"&&(g.data=r.param(g.data,g.traditional)),jn(An,g,t,F),f)return F;d=r.event&&g.global,d&&r.active++===0&&r.event.trigger("ajaxStart"),g.type=g.type.toUpperCase(),g.hasContent=!Pi.test(g.type),i=g.url.replace(ji,""),g.hasContent?g.data&&g.processData&&(g.contentType||"").indexOf("application/x-www-form-urlencoded")===0&&(g.data=g.data.replace(ki,"+")):(b=g.url.slice(i.length),g.data&&(g.processData||typeof g.data=="string")&&(i+=(qt.test(i)?"&":"?")+g.data,delete g.data),g.cache===!1&&(i=i.replace(Li,"$1"),b=(qt.test(i)?"&":"?")+"_="+wn.guid+++b),g.url=i+b),g.ifModified&&(r.lastModified[i]&&F.setRequestHeader("If-Modified-Since",r.lastModified[i]),r.etag[i]&&F.setRequestHeader("If-None-Match",r.etag[i])),(g.data&&g.hasContent&&g.contentType!==!1||t.contentType)&&F.setRequestHeader("Content-Type",g.contentType),F.setRequestHeader("Accept",g.dataTypes[0]&&g.accepts[g.dataTypes[0]]?g.accepts[g.dataTypes[0]]+(g.dataTypes[0]!=="*"?", "+Dn+"; q=0.01":""):g.accepts["*"]);for(v in g.headers)F.setRequestHeader(v,g.headers[v]);if(g.beforeSend&&(g.beforeSend.call(x,F,g)===!1||f))return F.abort();if(ve="abort",R.add(g.complete),F.done(g.success),F.fail(g.error),n=jn(Ot,g,t,F),!n)Pe(-1,"No Transport");else{if(F.readyState=1,d&&H.trigger("ajaxSend",[F,g]),f)return F;g.async&&g.timeout>0&&(a=h.setTimeout(function(){F.abort("timeout")},g.timeout));try{f=!1,n.send(K,Pe)}catch($){if(f)throw $;Pe(-1,$)}}function Pe($,J,rt,Xt){var me,ot,be,De,ke,fe=J;f||(f=!0,a&&h.clearTimeout(a),n=void 0,o=Xt||"",F.readyState=$>0?4:0,me=$>=200&&$<300||$===304,rt&&(De=Mi(g,F,rt)),!me&&r.inArray("script",g.dataTypes)>-1&&r.inArray("json",g.dataTypes)<0&&(g.converters["text script"]=function(){}),De=Ri(g,De,F,me),me?(g.ifModified&&(ke=F.getResponseHeader("Last-Modified"),ke&&(r.lastModified[i]=ke),ke=F.getResponseHeader("etag"),ke&&(r.etag[i]=ke)),$===204||g.type==="HEAD"?fe="nocontent":$===304?fe="notmodified":(fe=De.state,ot=De.data,be=De.error,me=!be)):(be=fe,($||!fe)&&(fe="error",$<0&&($=0))),F.status=$,F.statusText=(J||fe)+"",me?B.resolveWith(x,[ot,fe,F]):B.rejectWith(x,[F,fe,be]),F.statusCode(Z),Z=void 0,d&&H.trigger(me?"ajaxSuccess":"ajaxError",[F,g,me?ot:be]),R.fireWith(x,[F,fe]),d&&(H.trigger("ajaxComplete",[F,g]),--r.active||r.event.trigger("ajaxStop")))}return F},getJSON:function(e,t,n){return r.get(e,t,n,"json")},getScript:function(e,t){return r.get(e,void 0,t,"script")}}),r.each(["get","post"],function(e,t){r[t]=function(n,i,o,s){return M(i)&&(s=s||o,o=i,i=void 0),r.ajax(r.extend({url:n,type:t,dataType:s,data:i,success:o},r.isPlainObject(n)&&n))}}),r.ajaxPrefilter(function(e){var t;for(t in e.headers)t.toLowerCase()==="content-type"&&(e.contentType=e.headers[t]||"")}),r._evalUrl=function(e,t,n){return r.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,converters:{"text script":function(){}},dataFilter:function(i){r.globalEval(i,t,n)}})},r.fn.extend({wrapAll:function(e){var t;return this[0]&&(M(e)&&(e=e.call(this[0])),t=r(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){for(var n=this;n.firstElementChild;)n=n.firstElementChild;return n}).append(this)),this},wrapInner:function(e){return M(e)?this.each(function(t){r(this).wrapInner(e.call(this,t))}):this.each(function(){var t=r(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=M(e);return this.each(function(n){r(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(e){return this.parent(e).not("body").each(function(){r(this).replaceWith(this.childNodes)}),this}}),r.expr.pseudos.hidden=function(e){return!r.expr.pseudos.visible(e)},r.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},r.ajaxSettings.xhr=function(){try{return new h.XMLHttpRequest}catch{}};var Ii={0:200,1223:204},it=r.ajaxSettings.xhr();O.cors=!!it&&"withCredentials"in it,O.ajax=it=!!it,r.ajaxTransport(function(e){var t,n;if(O.cors||it&&!e.crossDomain)return{send:function(i,o){var s,a=e.xhr();if(a.open(e.type,e.url,e.async,e.username,e.password),e.xhrFields)for(s in e.xhrFields)a[s]=e.xhrFields[s];e.mimeType&&a.overrideMimeType&&a.overrideMimeType(e.mimeType),!e.crossDomain&&!i["X-Requested-With"]&&(i["X-Requested-With"]="XMLHttpRequest");for(s in i)a.setRequestHeader(s,i[s]);t=function(l){return function(){t&&(t=n=a.onload=a.onerror=a.onabort=a.ontimeout=a.onreadystatechange=null,l==="abort"?a.abort():l==="error"?typeof a.status!="number"?o(0,"error"):o(a.status,a.statusText):o(Ii[a.status]||a.status,a.statusText,(a.responseType||"text")!=="text"||typeof a.responseText!="string"?{binary:a.response}:{text:a.responseText},a.getAllResponseHeaders()))}},a.onload=t(),n=a.onerror=a.ontimeout=t("error"),a.onabort!==void 0?a.onabort=n:a.onreadystatechange=function(){a.readyState===4&&h.setTimeout(function(){t&&n()})},t=t("abort");try{a.send(e.hasContent&&e.data||null)}catch(l){if(t)throw l}},abort:function(){t&&t()}}}),r.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),r.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return r.globalEval(e),e}}}),r.ajaxPrefilter("script",function(e){e.cache===void 0&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),r.ajaxTransport("script",function(e){if(e.crossDomain||e.scriptAttrs){var t,n;return{send:function(i,o){t=r("