refactor: 修复知识库和操作指引

This commit is contained in:
2026-03-13 14:32:37 +08:00
parent bbe8e60646
commit 58f42de9df
88 changed files with 3387 additions and 2472 deletions

View File

@@ -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('全部类型'),
])

View File

@@ -61,7 +61,7 @@ class ViewActivityLog extends ViewRecord
'Document' => '文档',
'Group' => '分组',
'Terminal' => '终端',
'SopTemplate' => 'SOP模板',
'Guide' => '操作指引',
default => $className,
};
}),

View File

@@ -0,0 +1,204 @@
<?php
namespace App\Filament\Resources;
use App\Filament\Resources\GuideResource\Pages;
use App\Models\Guide;
use App\Models\Terminal;
use Filament\Forms;
use Filament\Forms\Form;
use Filament\Resources\Resource;
use Filament\Tables;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
class GuideResource extends Resource
{
protected static ?string $model = Guide::class;
protected static ?string $navigationIcon = 'heroicon-o-book-open';
protected static ?string $navigationLabel = '操作指引';
protected static ?string $modelLabel = '指引';
protected static ?string $pluralModelLabel = '指引';
protected static ?int $navigationSort = 3;
protected static ?string $navigationGroup = '业务管理';
public static function shouldRegisterNavigation(): bool
{
return auth()->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'),
];
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace App\Filament\Resources\GuideResource\Pages;
use App\Filament\Resources\GuideResource;
use Filament\Resources\Pages\CreateRecord;
class CreateGuide extends CreateRecord
{
protected static string $resource = GuideResource::class;
protected function mutateFormDataBeforeCreate(array $data): array
{
$data['created_by'] = auth()->id();
if ($data['status'] === 'published') {
$data['published_at'] = now();
}
return $data;
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace App\Filament\Resources\GuideResource\Pages;
use App\Filament\Resources\GuideResource;
use Filament\Actions;
use Filament\Resources\Pages\EditRecord;
class EditGuide extends EditRecord
{
protected static string $resource = GuideResource::class;
protected function getHeaderActions(): array
{
return [
\Filament\Actions\Action::make('managePages')
->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;
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace App\Filament\Resources\GuideResource\Pages;
use App\Filament\Resources\GuideResource;
use Filament\Actions;
use Filament\Resources\Pages\ListRecords;
class ListGuides extends ListRecords
{
protected static string $resource = GuideResource::class;
protected function getHeaderActions(): array
{
return [
Actions\CreateAction::make()
->label('创建指引'),
];
}
}

View File

@@ -0,0 +1,88 @@
<?php
namespace App\Filament\Resources\GuideResource\Pages;
use App\Filament\Resources\GuideResource;
use App\Models\Guide;
use App\Models\GuidePage;
use Filament\Forms;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
use SolutionForest\FilamentTree\Actions;
use SolutionForest\FilamentTree\Resources\Pages\TreePage;
class ManageGuidePages extends TreePage
{
protected static string $resource = GuideResource::class;
protected ?string $treeTitle = '指引页面';
protected bool $enableTreeTitle = true;
protected static string $model = GuidePage::class;
protected function getTreeQuery(): Builder
{
return GuidePage::query()
->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'));
}
}

View File

@@ -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}") ?? [];

View File

@@ -80,7 +80,7 @@ class ViewRole extends ViewRecord
'system-setting' => '⚙️ 系统设置',
'activity-log' => '📋 操作日志',
'terminal' => '🖥️ 终端管理',
'sop-template' => '📝 SOP模板',
'guide' => '📖 操作指引',
'group' => '👥 分组管理',
'user' => '👤 用户管理',
'role' => '🛡️ 角色管理',

View File

@@ -1,269 +0,0 @@
<?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 = 1;
protected static ?string $navigationGroup = '业务管理';
/**
* 控制导航菜单是否显示
*/
public static function shouldRegisterNavigation(): bool
{
return auth()->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'),
];
}
}

View File

@@ -1,23 +0,0 @@
<?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()]);
}
}

View File

@@ -1,28 +0,0 @@
<?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(),
];
}
}

View File

@@ -1,22 +0,0 @@
<?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(),
];
}
}

View File

@@ -1,99 +0,0 @@
<?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),
]);
}
}

View File

@@ -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()

View File

@@ -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}") ?? [];

View File

@@ -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' => '🛡️ 角色管理',