feat(阶段四): 创建 SOP 模板资源和页面
- 创建 SopTemplateResource 资源类 - 实现模板列表、创建、编辑、查看页面 - 添加步骤编辑器(Repeater 组件) - 支持富文本编辑步骤内容 - 支持拖拽排序步骤 - 添加状态筛选和分类筛选 - 显示步骤数统计
This commit is contained in:
259
app/Filament/Resources/SopTemplateResource.php
Normal file
259
app/Filament/Resources/SopTemplateResource.php
Normal file
@@ -0,0 +1,259 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources;
|
||||
|
||||
use App\Filament\Resources\SopTemplateResource\Pages;
|
||||
use App\Models\SopTemplate;
|
||||
use Filament\Forms;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Tables;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
class SopTemplateResource extends Resource
|
||||
{
|
||||
protected static ?string $model = SopTemplate::class;
|
||||
|
||||
protected static ?string $navigationIcon = 'heroicon-o-document-text';
|
||||
|
||||
protected static ?string $navigationLabel = 'SOP模板';
|
||||
|
||||
protected static ?string $modelLabel = 'SOP模板';
|
||||
|
||||
protected static ?string $pluralModelLabel = 'SOP模板';
|
||||
|
||||
protected static ?int $navigationSort = 4;
|
||||
|
||||
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'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\SopTemplateResource\Pages;
|
||||
|
||||
use App\Filament\Resources\SopTemplateResource;
|
||||
use Filament\Resources\Pages\CreateRecord;
|
||||
|
||||
class CreateSopTemplate extends CreateRecord
|
||||
{
|
||||
protected static string $resource = SopTemplateResource::class;
|
||||
|
||||
protected function mutateFormDataBeforeCreate(array $data): array
|
||||
{
|
||||
$data['created_by'] = auth()->id();
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
protected function getRedirectUrl(): string
|
||||
{
|
||||
return $this->getResource()::getUrl('edit', ['record' => $this->getRecord()]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\SopTemplateResource\Pages;
|
||||
|
||||
use App\Filament\Actions\ArchiveSopTemplateAction;
|
||||
use App\Filament\Actions\ExportSopTemplateAction;
|
||||
use App\Filament\Actions\PreviewSopTemplateAction;
|
||||
use App\Filament\Actions\PublishSopTemplateAction;
|
||||
use App\Filament\Resources\SopTemplateResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\EditRecord;
|
||||
|
||||
class EditSopTemplate extends EditRecord
|
||||
{
|
||||
protected static string $resource = SopTemplateResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
PreviewSopTemplateAction::make(),
|
||||
ExportSopTemplateAction::make(),
|
||||
PublishSopTemplateAction::make(),
|
||||
ArchiveSopTemplateAction::make(),
|
||||
Actions\ViewAction::make(),
|
||||
Actions\DeleteAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\SopTemplateResource\Pages;
|
||||
|
||||
use App\Filament\Actions\ExportSopTemplateAction;
|
||||
use App\Filament\Actions\ImportSopTemplateAction;
|
||||
use App\Filament\Resources\SopTemplateResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
|
||||
class ListSopTemplates extends ListRecords
|
||||
{
|
||||
protected static string $resource = SopTemplateResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
ImportSopTemplateAction::make(),
|
||||
Actions\CreateAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\SopTemplateResource\Pages;
|
||||
|
||||
use App\Filament\Resources\SopTemplateResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\ViewRecord;
|
||||
use Filament\Infolists;
|
||||
use Filament\Infolists\Infolist;
|
||||
|
||||
class ViewSopTemplate extends ViewRecord
|
||||
{
|
||||
protected static string $resource = SopTemplateResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\EditAction::make(),
|
||||
];
|
||||
}
|
||||
|
||||
public function infolist(Infolist $infolist): Infolist
|
||||
{
|
||||
return $infolist
|
||||
->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),
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user