From c4ab592fd5a723d5cfd4d60eb74e98fe93eb6b1a Mon Sep 17 00:00:00 2001 From: lizhuoran <625237490@qq.com> Date: Mon, 9 Mar 2026 13:24:02 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E9=98=B6=E6=AE=B5=E5=9B=9B):=20=E5=88=9B?= =?UTF-8?q?=E5=BB=BA=20SOP=20=E6=A8=A1=E6=9D=BF=E8=B5=84=E6=BA=90=E5=92=8C?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 创建 SopTemplateResource 资源类 - 实现模板列表、创建、编辑、查看页面 - 添加步骤编辑器(Repeater 组件) - 支持富文本编辑步骤内容 - 支持拖拽排序步骤 - 添加状态筛选和分类筛选 - 显示步骤数统计 --- .../Resources/SopTemplateResource.php | 259 ++++++++++++++++++ .../Pages/CreateSopTemplate.php | 23 ++ .../Pages/EditSopTemplate.php | 28 ++ .../Pages/ListSopTemplates.php | 22 ++ .../Pages/ViewSopTemplate.php | 99 +++++++ 5 files changed, 431 insertions(+) create mode 100644 app/Filament/Resources/SopTemplateResource.php create mode 100644 app/Filament/Resources/SopTemplateResource/Pages/CreateSopTemplate.php create mode 100644 app/Filament/Resources/SopTemplateResource/Pages/EditSopTemplate.php create mode 100644 app/Filament/Resources/SopTemplateResource/Pages/ListSopTemplates.php create mode 100644 app/Filament/Resources/SopTemplateResource/Pages/ViewSopTemplate.php diff --git a/app/Filament/Resources/SopTemplateResource.php b/app/Filament/Resources/SopTemplateResource.php new file mode 100644 index 0000000..c5d91ff --- /dev/null +++ b/app/Filament/Resources/SopTemplateResource.php @@ -0,0 +1,259 @@ +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 new file mode 100644 index 0000000..7c96b1f --- /dev/null +++ b/app/Filament/Resources/SopTemplateResource/Pages/CreateSopTemplate.php @@ -0,0 +1,23 @@ +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 new file mode 100644 index 0000000..2cf9b43 --- /dev/null +++ b/app/Filament/Resources/SopTemplateResource/Pages/EditSopTemplate.php @@ -0,0 +1,28 @@ +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), + ]); + } +}