父节点
当前提交
4bdcaba30e

+ 287
- 82
app/Filament/Resources/EsgResource.php 查看文件

@@ -4,6 +4,7 @@ namespace App\Filament\Resources;
4 4
 
5 5
 use App\Filament\Resources\EsgResource\Pages;
6 6
 use App\Models\Esg;
7
+use Filament\Forms\Components\Fieldset;
7 8
 use Filament\Forms\Components\FileUpload;
8 9
 use Filament\Forms\Components\Grid;
9 10
 use Filament\Forms\Components\Group;
@@ -26,6 +27,8 @@ use Filament\Tables\Columns\TextColumn;
26 27
 use Filament\Tables\Table;
27 28
 use Illuminate\Database\Eloquent\Builder;
28 29
 use Illuminate\Database\Eloquent\SoftDeletingScope;
30
+use Malzariey\FilamentLexicalEditor\Enums\ToolbarItem;
31
+use Malzariey\FilamentLexicalEditor\FilamentLexicalEditor;
29 32
 use SolutionForest\FilamentTranslateField\Forms\Component\Translate;
30 33
 use Str;
31 34
 
@@ -40,6 +43,15 @@ class EsgResource extends Resource
40 43
 
41 44
     public static function form(Form $form): Form
42 45
     {
46
+        $editor_toolbar= [
47
+            ToolbarItem::UNDO, ToolbarItem::REDO, ToolbarItem::NORMAL,
48
+            ToolbarItem::H2, ToolbarItem::H3, ToolbarItem::H4, ToolbarItem::H5,
49
+            ToolbarItem::BULLET, ToolbarItem::NUMBERED, ToolbarItem::FONT_SIZE,
50
+            ToolbarItem::BOLD, ToolbarItem::ITALIC, ToolbarItem::UNDERLINE,
51
+            ToolbarItem::LINK, ToolbarItem::TEXT_COLOR, ToolbarItem::BACKGROUND_COLOR,
52
+            ToolbarItem::SUBSCRIPT, ToolbarItem::LOWERCASE, ToolbarItem::DIVIDER,
53
+            ToolbarItem::UPPERCASE, ToolbarItem::CLEAR, ToolbarItem::HR
54
+        ];
43 55
         return $form
44 56
             ->schema([
45 57
                 //
@@ -105,18 +117,19 @@ class EsgResource extends Resource
105 117
                                 5 => "影片",
106 118
                             ])->label("")->default(1)->Live(),
107 119
                             Group::make()->schema([
108
-                                Translate::make()->schema(fn (string $locale) => [
109
-                                    RichEditor::make('content.text_content')
110
-                                    ->fileAttachmentsDirectory('attachments')
111
-                                    ->fileAttachmentsVisibility('private')
112
-                                    ->disableToolbarButtons(['attachFiles'])
113
-                                    ->required($locale == "zh_TW")
114
-                                    ->validationMessages([
115
-                                        'required' => '請填寫文字內容',
116
-                                    ]),
117
-                                ])
118
-                                ->locales(["zh_TW", "en"])
119
-                                ->id(fn ($get) => "para_text_" . $get('item_key')),
120
+                                Section::make('純文字設定')->schema([
121
+                                    FilamentLexicalEditor::make('content.text_content_tw')
122
+                                        ->label('中文')
123
+                                        ->id(fn ($get) => "text_content_tw_" . $get('item_key') . "_" . uniqid())
124
+                                        ->enabledToolbars($editor_toolbar)
125
+                                        ->required()
126
+                                        ->columnSpanFull(),
127
+                                    FilamentLexicalEditor::make('content.text_content_en')
128
+                                        ->label('English')
129
+                                        ->id(fn ($get) => "text_content_en_" . $get('item_key') . "_" . uniqid())
130
+                                        ->enabledToolbars($editor_toolbar)
131
+                                        ->columnSpanFull(),
132
+                                ]),
120 133
                             ])->visible(fn (Get $get):bool => $get("type") == 1),
121 134
                             Group::make()->schema([
122 135
                                 Section::make('區塊文字設定')->schema([
@@ -130,18 +143,18 @@ class EsgResource extends Resource
130 143
                                                     $component->state(Str::random());
131 144
                                                 }
132 145
                                             }),
133
-                                        // 多語系內容
134
-                                        Translate::make()->schema(fn (string $locale) => [
135
-                                            RichEditor::make('block_content')
136
-                                                ->disableToolbarButtons(['attachFiles'])
137
-                                                ->required($locale == "zh_TW")
138
-                                                ->validationMessages([
139
-                                                    'required' => '請填寫文字內容',
140
-                                                ]),
141
-                                        ])
142
-                                        ->locales(["zh_TW", "en"])
143
-                                        ->id(fn ($get) => "text_block_" . $get('block_item_key')),
144
-
146
+                                        FilamentLexicalEditor::make('block_content_tw')
147
+                                            ->label('繁體中文內容')
148
+                                            ->id(fn ($get) => "block_content_tw_" . $get('block_item_key') . "_" . uniqid())
149
+                                            ->enabledToolbars($editor_toolbar)
150
+                                            ->required()
151
+                                            ->live(onBlur: true)
152
+                                            ->columnSpanFull(),
153
+                                        FilamentLexicalEditor::make('block_content_en')
154
+                                            ->label('English Content')
155
+                                            ->id(fn ($get) => "block_content_en_" . $get('block_item_key') . "_" . uniqid())
156
+                                            ->enabledToolbars($editor_toolbar)
157
+                                            ->columnSpanFull(),
145 158
                                     ])
146 159
                                     ->label("")
147 160
                                     ->addActionLabel('增加區塊')
@@ -241,79 +254,136 @@ class EsgResource extends Resource
241 254
                                                         $component->state(Str::random());
242 255
                                                     }
243 256
                                                 }),
244
-                                            Translate::make()->schema(fn (string $locale) => [
245
-                                                Grid::make(1)  // 改用 Grid 來支援動態 columns
246
-                                                    ->schema([
247
-                                                        Radio::make('align1')
248
-                                                            ->label('對齊')
249
-                                                            ->options([
250
-                                                                1 => '置左',  // 改為數字鍵值
251
-                                                                2 => '置中',
252
-                                                                3 => '置右'
253
-                                                            ])
254
-                                                        ->default(1),
255
-                                                        RichEditor::make('col1')
256
-                                                            ->label('第1欄')
257
-                                                            ->disableToolbarButtons(['attachFiles'])
258
-                                                            ->required($locale == "zh_TW")
259
-                                                            ->validationMessages([
260
-                                                                'required' => '請填寫',
261
-                                                            ]),
262
-                                                        // TextInput::make('col2')->label('第2欄')->required(),
263
-                                                        Radio::make('align2')
257
+                                            Grid::make(1)  // 改用 Grid 來支援動態 columns
258
+                                                ->schema([
259
+                                                    Section::make("第一欄")->schema([
260
+                                                        Fieldset::make('中文')->schema([
261
+                                                            Radio::make('align1_tw')
262
+                                                                ->label('對齊')
263
+                                                                ->options([
264
+                                                                    1 => '置左',  // 改為數字鍵值
265
+                                                                    2 => '置中',
266
+                                                                    3 => '置右'
267
+                                                                ])
268
+                                                            ->default(1)->inline(),
269
+                                                            FilamentLexicalEditor::make('col1_tw')
270
+                                                                ->id(fn ($get) => "col1_tw_" . $get('simple_table_rows_key') . "_" . uniqid())
271
+                                                                ->enabledToolbars($editor_toolbar)
272
+                                                                ->required()
273
+                                                                ->columnSpanFull(),
274
+                                                        ]),
275
+                                                        Fieldset::make('英文')->schema([
276
+                                                            Radio::make('align1_en')
264 277
                                                             ->label('對齊')
265 278
                                                             ->options([
266 279
                                                                 1 => '置左',  // 改為數字鍵值
267 280
                                                                 2 => '置中',
268 281
                                                                 3 => '置右'
269 282
                                                             ])
283
+                                                            ->default(1)->inline(),
284
+                                                            FilamentLexicalEditor::make('col1_en')
285
+                                                                ->id(fn ($get) => "col1_en_" . $get('simple_table_rows_key') . "_" . uniqid())
286
+                                                                ->enabledToolbars($editor_toolbar)
287
+                                                                ->columnSpanFull(),
288
+                                                        ]),
289
+                                                    ]),
290
+                                                    Section::make("第二欄")->schema([
291
+                                                        Fieldset::make('中文')->schema([
292
+                                                            Radio::make('align2_tw')
293
+                                                                ->label('對齊')
294
+                                                                ->options([
295
+                                                                    1 => '置左',  // 改為數字鍵值
296
+                                                                    2 => '置中',
297
+                                                                    3 => '置右'
298
+                                                                ])
299
+                                                            ->default(1)->inline(),
300
+                                                            FilamentLexicalEditor::make('col2_tw')
301
+                                                                ->id(fn ($get) => "col2_tw_" . $get('simple_table_rows_key') . "_" . uniqid())
302
+                                                                ->enabledToolbars($editor_toolbar)
303
+                                                                ->required()
304
+                                                                ->columnSpanFull(),
305
+                                                        ]),
306
+                                                        Fieldset::make('英文')->schema([
307
+                                                            Radio::make('align2_en')
308
+                                                                ->label('對齊')
309
+                                                                ->options([
310
+                                                                    1 => '置左',  // 改為數字鍵值
311
+                                                                    2 => '置中',
312
+                                                                    3 => '置右'
313
+                                                                ])
314
+                                                            ->default(1)->inline(),
315
+                                                            FilamentLexicalEditor::make('col2_en')
316
+                                                                ->id(fn ($get) => "col2_en_" . $get('simple_table_rows_key') . "_" . uniqid())
317
+                                                                ->enabledToolbars($editor_toolbar)
318
+                                                                ->columnSpanFull(),
319
+                                                        ]),
320
+                                                    ]),
321
+                                                    Section::make("第三欄")->schema([
322
+                                                        Fieldset::make('中文')->schema([
323
+                                                            Radio::make('align3_tw')
324
+                                                                ->label('對齊')
325
+                                                                ->options([
326
+                                                                    1 => '置左',  // 改為數字鍵值
327
+                                                                    2 => '置中',
328
+                                                                    3 => '置右'
329
+                                                                ])
270 330
                                                             ->default(1),
271
-                                                        RichEditor::make('col2')
272
-                                                            ->label('第2欄')
273
-                                                            ->disableToolbarButtons(['attachFiles'])
274
-                                                            ->required($locale == "zh_TW")
275
-                                                            ->validationMessages([
276
-                                                                'required' => '請填寫',
277
-                                                            ]),
278
-                                                        Radio::make('align3')
279
-                                                            ->label('對齊')
280
-                                                            ->options([
281
-                                                                1 => '置左',  // 改為數字鍵值
282
-                                                                2 => '置中',
283
-                                                                3 => '置右'
284
-                                                            ])
285
-                                                            ->default(1)
286
-                                                            ->visible(fn (Get $get) => intval($get('../../../content.column_count')) >= 3),
287
-                                                        RichEditor::make('col3')
288
-                                                            ->label('第3欄')
289
-                                                            ->disableToolbarButtons(['attachFiles'])
290
-                                                            ->visible(fn (Get $get) => intval($get('../../../content.column_count')) >= 3)
291
-                                                            ->required($locale == "zh_TW" && fn (Get $get)=> intval($get("../../../content.column_count")) >= 3)
292
-                                                            ->validationMessages([
293
-                                                                'required' => '請填寫',
331
+                                                            FilamentLexicalEditor::make('col3_tw')
332
+                                                                ->id(fn ($get) => "col3_tw_" . $get('simple_table_rows_key') . "_" . uniqid())
333
+                                                                ->enabledToolbars($editor_toolbar)
334
+                                                                ->required()
335
+                                                                ->columnSpanFull(),
336
+                                                        ]),
337
+                                                        Fieldset::make('英文')->schema([
338
+                                                            Radio::make('align3_en')
339
+                                                                ->label('對齊')
340
+                                                                ->options([
341
+                                                                    1 => '置左',  // 改為數字鍵值
342
+                                                                    2 => '置中',
343
+                                                                    3 => '置右'
344
+                                                                ])
345
+                                                            ->default(1)->inline(),
346
+                                                            FilamentLexicalEditor::make('col3_en')
347
+                                                                ->id(fn ($get) => "col3_en_" . $get('simple_table_rows_key') . "_" . uniqid())
348
+                                                                ->enabledToolbars($editor_toolbar)
349
+                                                                ->columnSpanFull(),
294 350
                                                             ]),
295
-                                                            Radio::make('align4')
351
+                                                    ])->visible(fn (Get $get) => intval($get('../../../content.column_count')) >= 3),
352
+                                                    Section::make("第四欄")->schema([
353
+                                                        Fieldset::make('中文')->schema([
354
+                                                            Radio::make('align4_tw')
296 355
                                                                 ->label('對齊')
297 356
                                                                 ->options([
298 357
                                                                     1 => '置左',  // 改為數字鍵值
299 358
                                                                     2 => '置中',
300 359
                                                                     3 => '置右'
301 360
                                                                 ])
302
-                                                                ->default(1)
303
-                                                                ->visible(fn (Get $get) => intval($get('../../../content.column_count')) >= 4),
304
-                                                            RichEditor::make('col4')
305
-                                                                ->label('第4欄')
306
-                                                                ->disableToolbarButtons(['attachFiles'])
307
-                                                                ->visible(fn (Get $get) => intval($get('../../../content.column_count')) >= 4)
308
-                                                                ->required($locale == "zh_TW" && fn (Get $get)=> intval($get("../../../content.column_count")) >= 4)
309
-                                                                ->validationMessages([
310
-                                                                    'required' => '請填寫',
311
-                                                                ]),
312
-                                                    ])
313
-                                                    ->reactive()  // 加入響應式
314
-                                            ])->locales(["zh_TW", "en"]),
361
+                                                            ->default(1)->inline(),
362
+                                                            FilamentLexicalEditor::make('col4_tw')
363
+                                                                ->id(fn ($get) => "col4_tw_" . $get('simple_table_rows_key') . "_" . uniqid())
364
+                                                                ->enabledToolbars($editor_toolbar)
365
+                                                                ->required()
366
+                                                                ->columnSpanFull(),
367
+                                                        ]),
368
+                                                        Fieldset::make('英文')->schema([
369
+                                                            Radio::make('align4_en')
370
+                                                                ->label('對齊')
371
+                                                                ->options([
372
+                                                                    1 => '置左',  // 改為數字鍵值
373
+                                                                    2 => '置中',
374
+                                                                    3 => '置右'
375
+                                                                ])
376
+                                                            ->default(1)->inline(),
377
+                                                            FilamentLexicalEditor::make('col4_en')
378
+                                                                ->id(fn ($get) => "col4_en_" . $get('simple_table_rows_key') . "_" . uniqid())
379
+                                                                ->enabledToolbars($editor_toolbar)
380
+                                                                ->columnSpanFull(),
381
+                                                        ]),
382
+                                                    ])->visible(fn (Get $get) => intval($get('../../../content.column_count')) >= 4),
383
+                                                ])
384
+                                                ->reactive()  // 加入響應式
315 385
                                         ])
316
-                                        ->id(fn ($get) => "simple_table_" . $get('simple_table_key'))
386
+                                        ->id(fn ($get) => "simple_table_" . $get('simple_table_rows_key'))
317 387
                                         ->addActionLabel('新增列')
318 388
                                         ->reorderableWithButtons()
319 389
                                         ->minItems(1)
@@ -369,6 +439,141 @@ class EsgResource extends Resource
369 439
                         ->reorderableWithButtons()
370 440
                         ->orderColumn('order')
371 441
                         ->cloneable()
442
+                        ->mutateRelationshipDataBeforeFillUsing(function (array $data): array {
443
+                            switch($data["type"]){
444
+                                case "1":
445
+                                    if (!empty($data["content"]['text_content'])) {
446
+                                        $content = is_string($data["content"]['text_content'])
447
+                                            ? json_decode($data["content"]['text_content'], true)
448
+                                            : $data["content"]['text_content'];
449
+
450
+                                        if (is_array($content)) {
451
+                                            $data["content"]['text_content_tw'] = $content['zh_TW'] ?? '';
452
+                                            $data["content"]['text_content_en'] = $content['en'] ?? '';
453
+                                        }
454
+                                    }
455
+                                    break;
456
+                                case "2":
457
+                                    if (!empty($data["content"]['text_blocks']) && count($data["content"]['text_blocks']) > 0) {
458
+                                        foreach ($data["content"]['text_blocks'] as $block_index => $block){
459
+                                            $content = is_string($block['block_content'])
460
+                                                ? json_decode($block['block_content'], true)
461
+                                                : $block['block_content'];
462
+
463
+                                            if (is_array($content)) {
464
+                                                $data["content"]['text_blocks'][$block_index]["block_content_tw"] = $content['zh_TW'] ?? '';
465
+                                                $data["content"]['text_blocks'][$block_index]["block_content_en"] = $content['en'] ?? '';
466
+                                            }
467
+                                        }
468
+                                    }
469
+                                    break;
470
+                                case "3":
471
+                                    if (!empty($data["content"]['simple_table_rows']) && count($data["content"]['simple_table_rows']) > 0) {
472
+                                        foreach ($data["content"]['simple_table_rows'] as $row_key => $row) {
473
+                                            $flatRow = [];
474
+
475
+                                            foreach ($row as $key => $value) {
476
+                                                if (is_array($value) && isset($value['zh_TW'], $value['en'])) {
477
+                                                    // 多语言字段,分离为两个字段
478
+                                                    $flatRow[$key . '_tw'] = $value['zh_TW'];
479
+                                                    $flatRow[$key . '_en'] = $value['en'];
480
+                                                } else {
481
+                                                    // 非多语言字段,保持原样
482
+                                                    $flatRow[$key] = $value;
483
+                                                }
484
+                                            }
485
+
486
+                                            $data["content"]['simple_table_rows'][$row_key] = $flatRow;
487
+                                        }
488
+                                    }
489
+                                    break;
490
+                            }
491
+                            return $data;
492
+                        })
493
+                        ->mutateRelationshipDataBeforeSaveUsing(function (array $data): array {
494
+                            switch($data["type"]){
495
+                                case "1":
496
+                                    $data["content"]["text_content"] = ["zh_TW" => $data["content"]["text_content_tw"] ?? "" , "en" => $data["content"]["text_content_en"] ?? ""];
497
+                                    // 移除臨時的分離欄位,只保留合併的 JSON 欄位
498
+                                    unset($data["content"]["text_content_tw"]);
499
+                                    unset($data["content"]["text_content_en"]);
500
+
501
+                                    // 清理 HTML 標籤(如果需要)
502
+                                    foreach ($data['content']['text_content'] as $locale => $content) {
503
+                                        \Log::info($content);
504
+                                        \Log::info(stripslashes($content));
505
+                                        $data['content']['text_content'][$locale] = stripslashes($content);
506
+                                    }
507
+                                    break;
508
+                                case "2":
509
+                                    // 確保有 text_blocks
510
+                                    if (!isset($data['content']['text_blocks'])) {
511
+                                        $data['content']['text_blocks'] = [];
512
+                                    }
513
+
514
+                                    // 處理每個文字區塊
515
+                                    foreach ($data['content']['text_blocks'] as $index => &$block) {
516
+                                        // 確保區塊有必要的欄位
517
+                                        $block['block_item_key'] = $block['block_item_key'] ?? Str::random();
518
+                                        $block['order'] = $block['order'] ?? ($index + 1);
519
+
520
+                                        $block["block_content"] = ["zh_TW" => $block["block_content_tw"] ?? "" , "en" => $block["block_content_en"] ?? ""];
521
+                                        // 移除臨時的分離欄位,只保留合併的 JSON 欄位
522
+                                        unset($block["block_content_tw"]);
523
+                                        unset($block["block_content_en"]);
524
+                                        // 清理區塊內容
525
+                                        if (isset($block['block_content'])) {
526
+                                            foreach ($block['block_content'] as $locale => $content) {
527
+                                                $block['block_content'][$locale] = stripslashes($content);
528
+                                            }
529
+                                        }
530
+                                    }
531
+                                    break;
532
+                                case "3":
533
+                                    // 確保欄數設定
534
+                                    $columnCount = $data['content']['column_count'] ?? 2;
535
+
536
+                                    // 確保表頭存在
537
+                                    if (!isset($data['content']['headers'])) {
538
+                                        $data['content']['headers'] = ['zh_TW' => [], 'en' => []];
539
+                                    }
540
+
541
+                                    // 確保表格資料存在
542
+                                    if (!isset($data['content']['simple_table_rows'])) {
543
+                                        $data['content']['simple_table_rows'] = [];
544
+                                    }
545
+
546
+                                    // 處理表格資料,確保欄數一致
547
+                                    foreach ($data['content']['simple_table_rows'] as $rowIndex => &$row) {
548
+                                        // 確保每行都有正確的欄數
549
+                                        for ($i = 1; $i <= $columnCount; $i++) {
550
+                                            $row["col{$i}"] = ["zh_TW" => $row["col{$i}_tw"] ?? "" , "en" => $row["col{$i}_en"] ?? ""];
551
+                                            foreach ($row["col{$i}"] as $locale => $content) {
552
+                                                $row["col{$i}"][$locale] = $content;
553
+                                            }
554
+                                            unset($row["col{$i}_tw"]);
555
+                                            unset($row["col{$i}_en"]);
556
+                                            $row["align{$i}"] = ["zh_TW" => $row["align{$i}_tw"] ?? "" , "en" => $row["align{$i}_en"] ?? ""];
557
+                                            foreach ($row["align{$i}"] as $locale => $content) {
558
+                                                $row["align{$i}"][$locale] = $content;
559
+                                            }
560
+                                            unset($row["align{$i}_tw"]);
561
+                                            unset($row["align{$i}_en"]);
562
+                                        }
563
+
564
+                                        // 移除多餘的欄位
565
+                                        for ($i = $columnCount + 1; $i <= 4; $i++) {
566
+                                            unset($row["col{$i}_tw"]);
567
+                                            unset($row["col{$i}_en"]);
568
+                                            unset($row["align{$i}_tw"]);
569
+                                            unset($row["align{$i}_en"]);
570
+                                        }
571
+                                    }
572
+                                    break;
573
+                            }
574
+                            \Log::info($data);
575
+                            return $data;
576
+                        })
372 577
                     ])->columnSpanFull(),
373 578
                 ])
374 579
                 ->columnSpanFull()

+ 5
- 0
app/Filament/Resources/EsgResource/Pages/EditEsg.php 查看文件

@@ -26,7 +26,11 @@ class EditEsg extends EditRecord
26 26
 
27 27
         // ✅ 使用 Trait 方法載入並格式化 paragraphs
28 28
         $data['paragraphs'] = $this->loadAndFormatParagraphs($this->record);
29
+        return $data;
30
+    }
29 31
 
32
+    protected function mutateFormDataBeforeSave(array $data): array
33
+    {
30 34
         return $data;
31 35
     }
32 36
 
@@ -35,6 +39,7 @@ class EditEsg extends EditRecord
35 39
      */
36 40
     protected function handleRecordUpdate(Model $record, array $data): Model
37 41
     {
42
+
38 43
         // ✅ 使用 Trait 方法處理表單中的 paragraphs
39 44
         $processedParagraphs = $this->processFormParagraphs();
40 45
         // 更新主記錄

+ 55
- 24
app/Filament/Resources/NewsResource.php 查看文件

@@ -7,8 +7,11 @@ use App\Models\News;
7 7
 use App\Models\NewsCategory;
8 8
 use Filament\Forms\Components\Actions\Action;
9 9
 use Filament\Forms\Components\Group;
10
+use Filament\Forms\Components\Hidden;
10 11
 use Filament\Forms\Components\Repeater;
11 12
 use Filament\Forms\Components\RichEditor;
13
+use Filament\Forms\Components\Tabs;
14
+use Filament\Forms\Components\Tabs\Tab;
12 15
 use Filament\Forms\Components\TextInput;
13 16
 use Filament\Forms\Components\Textarea;
14 17
 use Filament\Forms\Components\Select;
@@ -19,6 +22,7 @@ use Filament\Forms\Components\DatePicker;
19 22
 use Filament\Forms\Components\FileUpload;
20 23
 use Filament\Forms\Get;
21 24
 use Filament\Forms\Form;
25
+use Filament\Forms\Set;
22 26
 use Filament\Resources\Resource;
23 27
 use Filament\Tables;
24 28
 use Filament\Tables\Columns\IconColumn;
@@ -29,6 +33,8 @@ use Filament\Tables\Table;
29 33
 use Illuminate\Database\Eloquent\Builder;
30 34
 use Illuminate\Support\Facades\DB;
31 35
 use Illuminate\Support\Str;
36
+use Malzariey\FilamentLexicalEditor\Enums\ToolbarItem;
37
+use Malzariey\FilamentLexicalEditor\FilamentLexicalEditor;
32 38
 use SolutionForest\FilamentTranslateField\Forms\Component\Translate;
33 39
 use RalphJSmit\Filament\SEO\SEO;
34 40
 
@@ -142,32 +148,33 @@ class NewsResource extends Resource
142 148
                             ])
143 149
                         ])->visible(fn (Get $get):bool => $get("paragraph_type") == 1),
144 150
                         Group::make()->schema([
145
-                            Translate::make()->schema(fn (string $locale) => [
146
-                                RichEditor::make('text_content')
147
-                                ->toolbarButtons([
148
-                                    'blockquote',
149
-                                    'bold',
150
-                                    'bulletList',
151
-                                    'h2',
152
-                                    'h3',
153
-                                    'italic',
154
-                                    'link',
155
-                                    'orderedList',
156
-                                    'redo',
157
-                                    'strike',
158
-                                    'underline',
159
-                                    'undo',
151
+                            FilamentLexicalEditor::make('text_content_tw')
152
+                                ->label('繁體中文內容')
153
+                                ->id(fn ($get) => "text_content_tw_" . $get('item_key') . "_" . uniqid())
154
+                                ->enabledToolbars([
155
+                                    ToolbarItem::UNDO, ToolbarItem::REDO, ToolbarItem::NORMAL,
156
+                                    ToolbarItem::H2, ToolbarItem::H3, ToolbarItem::H4, ToolbarItem::H5,
157
+                                    ToolbarItem::BULLET, ToolbarItem::NUMBERED, ToolbarItem::FONT_SIZE,
158
+                                    ToolbarItem::BOLD, ToolbarItem::ITALIC, ToolbarItem::UNDERLINE,
159
+                                    ToolbarItem::LINK, ToolbarItem::TEXT_COLOR, ToolbarItem::BACKGROUND_COLOR,
160
+                                    ToolbarItem::SUBSCRIPT, ToolbarItem::LOWERCASE, ToolbarItem::DIVIDER,
161
+                                    ToolbarItem::UPPERCASE, ToolbarItem::CLEAR, ToolbarItem::HR
160 162
                                 ])
161
-                                ->disableToolbarButtons([
162
-                                    'blockquote',
163
-                                    'strike',
164
-                                    'attachFiles',
163
+                                ->live(onBlur: true)
164
+                                ->columnSpanFull(),
165
+                            FilamentLexicalEditor::make('text_content_en')
166
+                                ->label('English Content')
167
+                                ->id(fn ($get) => "text_content_en_" . $get('item_key') . "_" . uniqid())
168
+                                ->enabledToolbars([
169
+                                    ToolbarItem::UNDO, ToolbarItem::REDO, ToolbarItem::NORMAL,
170
+                                    ToolbarItem::H2, ToolbarItem::H3, ToolbarItem::H4, ToolbarItem::H5,
171
+                                    ToolbarItem::BULLET, ToolbarItem::NUMBERED, ToolbarItem::FONT_SIZE,
172
+                                    ToolbarItem::BOLD, ToolbarItem::ITALIC, ToolbarItem::UNDERLINE,
173
+                                    ToolbarItem::LINK, ToolbarItem::TEXT_COLOR, ToolbarItem::BACKGROUND_COLOR,
174
+                                    ToolbarItem::SUBSCRIPT, ToolbarItem::LOWERCASE, ToolbarItem::DIVIDER,
175
+                                    ToolbarItem::UPPERCASE, ToolbarItem::CLEAR, ToolbarItem::HR
165 176
                                 ])
166
-                                ->fileAttachmentsDirectory('attachments')
167
-                                ->fileAttachmentsVisibility('private'),
168
-                            ])
169
-                            ->locales(["zh_TW", "en"])
170
-                            ->id(fn ($get) => "para_text_" . $get('item_key')),
177
+                                ->columnSpanFull(),
171 178
                         ])->visible(fn (Get $get):bool => $get("paragraph_type") == 2),
172 179
                             Group::make()->schema([
173 180
                                 Section::make("")->schema([
@@ -201,6 +208,30 @@ class NewsResource extends Resource
201 208
                     ->reorderableWithButtons()
202 209
                     ->orderColumn('order')
203 210
                     ->cloneable()
211
+                    ->mutateRelationshipDataBeforeFillUsing(function (array $data): array {
212
+
213
+                        if ($data['paragraph_type'] == 2 && !empty($data['text_content'])) {
214
+                            $content = is_string($data['text_content'])
215
+                                ? json_decode($data['text_content'], true)
216
+                                : $data['text_content'];
217
+
218
+                            if (is_array($content)) {
219
+                                $data['text_content_tw'] = $content['zh_TW'] ?? '';
220
+                                $data['text_content_en'] = $content['en'] ?? '';
221
+                            }
222
+                        }
223
+
224
+                        return $data;
225
+                    })
226
+                    ->mutateRelationshipDataBeforeSaveUsing(function (array $data): array{
227
+                        if ($data['paragraph_type'] == 2) {
228
+                            $data["text_content"] = ["zh_TW" => $data["text_content_tw"], "en" => $data["text_content_en"]];
229
+                            // 移除臨時的分離欄位,只保留合併的 JSON 欄位
230
+                            unset($data['text_content_tw']);
231
+                            unset($data['text_content_en']);
232
+                        }
233
+                        return $data;
234
+                    })
204 235
                 ]),
205 236
             ]);
206 237
     }

+ 1
- 1
app/Http/Controllers/Api/NewsController.php 查看文件

@@ -185,7 +185,7 @@ class NewsController extends Controller
185 185
                 case "text":
186 186
                     $paragraphs[] = [
187 187
                         "type" => $paragraph->contentType(),
188
-                        "content" => nl2br($paragraph->getTranslation("text_content", $locate, false)),
188
+                        "content" => nl2br($paragraph->getTextContent($locate)),
189 189
                     ];
190 190
                     break;
191 191
                 case "image":

+ 29
- 1
app/Models/NewsParagraph.php 查看文件

@@ -20,7 +20,10 @@ class NewsParagraph extends Model
20 20
     protected $guarded = ['id'];
21 21
     public $timestamps = false;
22 22
     protected $appends = ['paragraph_video_img', 'paragraph_video_type', 'paragraph_video_url'];
23
-    public $translatable = ['text_content', 'video_img_alt'];
23
+    public $translatable = ['video_img_alt'];
24
+    protected $casts = [
25
+        'text_content' => 'array',         // JSON 轉陣列
26
+    ];
24 27
 
25 28
     public function news(){
26 29
         return $this->belongsTo(News::class);
@@ -63,4 +66,29 @@ class NewsParagraph extends Model
63 66
             get: fn ($value) => ($this->video_type == 2) ? Storage::disk('public')->url($this->video_url) : $this->link,
64 67
         );
65 68
     }
69
+    /**
70
+     * 取得特定語言的文字內容
71
+     */
72
+    public function getTextContent($locale = 'zh_TW'): string
73
+    {
74
+        if ($this->paragraph_type != 2) {
75
+            return '';
76
+        }
77
+
78
+        $content = $this->text_content;
79
+
80
+        if (is_array($content)) {
81
+            return $content[$locale] ?? '';
82
+        }
83
+
84
+        if (is_string($content)) {
85
+            $decoded = json_decode($content, true);
86
+            return $decoded[$locale] ?? '';
87
+        }
88
+
89
+        return '';
90
+    }
91
+
92
+
93
+
66 94
 }

+ 23
- 44
app/Traits/HandlesEsgParagraphs.php 查看文件

@@ -16,8 +16,6 @@ trait HandlesEsgParagraphs
16 16
         $formattedParagraphs = [];
17 17
 
18 18
         foreach ($paragraphs as $paragraph) {
19
-            \Log::info("格式化段落:", $paragraph);
20
-
21 19
             $formatted = [
22 20
                 'id' => $paragraph['id'],
23 21
                 'type' => $paragraph['type'],
@@ -50,8 +48,6 @@ trait HandlesEsgParagraphs
50 48
             }
51 49
 
52 50
             $formattedParagraphs[] = $formatted;
53
-
54
-            \Log::info("格式化完成:", $formatted);
55 51
         }
56 52
 
57 53
         return $formattedParagraphs;
@@ -157,8 +153,6 @@ trait HandlesEsgParagraphs
157 153
      */
158 154
     protected function preprocessParagraphs(array $paragraphs): array
159 155
     {
160
-        \Log::info("=== 開始預處理 Paragraphs ===");
161
-
162 156
         foreach ($paragraphs as $index => &$paragraph) {
163 157
             \Log::info("處理段落 {$index}:", $paragraph);
164 158
 
@@ -199,12 +193,10 @@ trait HandlesEsgParagraphs
199 193
      */
200 194
     protected function preprocessTextParagraph(array $paragraph): array
201 195
     {
202
-        \Log::info("處理純文字段落");
203
-
204
-        // 確保文字內容結構正確
205
-        if (!isset($paragraph['content']['text_content'])) {
206
-            $paragraph['content']['text_content'] = ['zh_TW' => '', 'en' => ''];
207
-        }
196
+        $paragraph["content"]["text_content"] = ["zh_TW" => $paragraph["content"]["text_content_tw"] ?? "" , "en" => $paragraph["content"]["text_content_en"] ?? ""];
197
+        // 移除臨時的分離欄位,只保留合併的 JSON 欄位
198
+        unset($paragraph["content"]["text_content_tw"]);
199
+        unset($paragraph["content"]["text_content_en"]);
208 200
 
209 201
         // 清理 HTML 標籤(如果需要)
210 202
         foreach ($paragraph['content']['text_content'] as $locale => $content) {
@@ -219,8 +211,6 @@ trait HandlesEsgParagraphs
219 211
      */
220 212
     protected function preprocessBlockParagraph(array $paragraph): array
221 213
     {
222
-        \Log::info("處理區塊文字段落");
223
-
224 214
         // 確保有 text_blocks
225 215
         if (!isset($paragraph['content']['text_blocks'])) {
226 216
             $paragraph['content']['text_blocks'] = [];
@@ -232,6 +222,10 @@ trait HandlesEsgParagraphs
232 222
             $block['block_item_key'] = $block['block_item_key'] ?? Str::random();
233 223
             $block['order'] = $block['order'] ?? ($index + 1);
234 224
 
225
+            $block["block_content"] = ["zh_TW" => $block["block_content_tw"] ?? "" , "en" => $block["block_content_en"] ?? ""];
226
+            // 移除臨時的分離欄位,只保留合併的 JSON 欄位
227
+            unset($block["block_content_tw"]);
228
+            unset($block["block_content_en"]);
235 229
             // 清理區塊內容
236 230
             if (isset($block['block_content'])) {
237 231
                 foreach ($block['block_content'] as $locale => $content) {
@@ -248,7 +242,6 @@ trait HandlesEsgParagraphs
248 242
      */
249 243
     protected function preprocessTableParagraph(array $paragraph): array
250 244
     {
251
-        \Log::info("處理表格段落");
252 245
 
253 246
         // 確保欄數設定
254 247
         $columnCount = $paragraph['content']['column_count'] ?? 2;
@@ -265,22 +258,22 @@ trait HandlesEsgParagraphs
265 258
 
266 259
         // 處理表格資料,確保欄數一致
267 260
         foreach ($paragraph['content']['simple_table_rows'] as $rowIndex => &$row) {
268
-            foreach (['zh_TW', 'en'] as $locale) {
269
-                if (!isset($row[$locale])) {
270
-                    $row[$locale] = [];
271
-                }
272
-
273
-                // 確保每行都有正確的欄數
274
-                for ($i = 1; $i <= $columnCount; $i++) {
275
-                    if (!isset($row[$locale]["col{$i}"])) {
276
-                        $row[$locale]["col{$i}"] = '';
277
-                    }
278
-                }
261
+            // 確保每行都有正確的欄數
262
+            for ($i = 1; $i <= $columnCount; $i++) {
263
+                $row["col{$i}"] = ["zh_TW" => $row["col{$i}_tw"] ?? "" , "en" => $row["col{$i}_en"] ?? ""];
264
+                unset($block["block_content_tw"]);
265
+                unset($block["block_content_en"]);
266
+                $row["align{$i}"] = ["zh_TW" => $row["align{$i}_tw"] ?? "" , "en" => $row["align{$i}_en"] ?? ""];
267
+                unset($row["align{$i}_tw"]);
268
+                unset($row["align{$i}_en"]);
269
+            }
279 270
 
280
-                // 移除多餘的欄位
281
-                for ($i = $columnCount + 1; $i <= 4; $i++) {
282
-                    unset($row[$locale]["col{$i}"]);
283
-                }
271
+            // 移除多餘的欄位
272
+            for ($i = $columnCount + 1; $i <= 4; $i++) {
273
+                unset($row["col{$i}_tw"]);
274
+                unset($row["col{$i}_en"]);
275
+                unset($row["align{$i}_tw"]);
276
+                unset($row["align{$i}_en"]);
284 277
             }
285 278
         }
286 279
 
@@ -318,8 +311,6 @@ trait HandlesEsgParagraphs
318 311
      */
319 312
     protected function updateParagraphs(Model $record, array $paragraphs): void
320 313
     {
321
-        \Log::info("=== 開始更新 Paragraphs 關聯 ===");
322
-
323 314
         // 獲取現有的段落 IDs
324 315
         $existingIds = collect($paragraphs)
325 316
             ->pluck('id')
@@ -332,8 +323,6 @@ trait HandlesEsgParagraphs
332 323
             ->whereNotIn('id', $existingIds)
333 324
             ->delete();
334 325
 
335
-        \Log::info("已刪除不存在的段落");
336
-
337 326
         // 更新或建立段落
338 327
         foreach ($paragraphs as $paragraphData) {
339 328
             if (isset($paragraphData['id'])) {
@@ -345,7 +334,6 @@ trait HandlesEsgParagraphs
345 334
                         'order' => $paragraphData['order'],
346 335
                         'content' => $paragraphData['content'],
347 336
                     ]);
348
-                    \Log::info("更新段落 ID: {$paragraphData['id']}");
349 337
                 }
350 338
             } else {
351 339
                 // 建立新段落
@@ -354,11 +342,8 @@ trait HandlesEsgParagraphs
354 342
                     'order' => $paragraphData['order'],
355 343
                     'content' => $paragraphData['content'],
356 344
                 ]);
357
-                \Log::info("建立新段落 ID: {$newParagraph->id}");
358 345
             }
359 346
         }
360
-
361
-        \Log::info("=== Paragraphs 關聯更新完成 ===");
362 347
     }
363 348
 
364 349
     /**
@@ -366,18 +351,13 @@ trait HandlesEsgParagraphs
366 351
      */
367 352
     protected function createParagraphs(Model $record, array $paragraphs): void
368 353
     {
369
-        \Log::info("=== 開始建立 Paragraphs 關聯 ===");
370
-
371 354
         foreach ($paragraphs as $paragraphData) {
372 355
             $newParagraph = $record->paragraphs()->create([
373 356
                 'type' => $paragraphData['type'],
374 357
                 'order' => $paragraphData['order'],
375 358
                 'content' => $paragraphData['content'],
376 359
             ]);
377
-            \Log::info("建立新段落 ID: {$newParagraph->id}");
378 360
         }
379
-
380
-        \Log::info("=== Paragraphs 關聯建立完成 ===");
381 361
     }
382 362
 
383 363
     /**
@@ -429,7 +409,6 @@ trait HandlesEsgParagraphs
429 409
     protected function processFormParagraphs(): ?array
430 410
     {
431 411
         $formState = $this->form->getState();
432
-
433 412
         if (!isset($formState['paragraphs'])) {
434 413
             return null;
435 414
         }

+ 1
- 0
composer.json 查看文件

@@ -13,6 +13,7 @@
13 13
         "filament/spatie-laravel-translatable-plugin": "^3.2",
14 14
         "laravel/framework": "^12.0",
15 15
         "laravel/tinker": "^2.10.1",
16
+        "malzariey/filament-lexical-editor": "^1.1",
16 17
         "pboivin/filament-peek": "^2.0",
17 18
         "solution-forest/filament-translate-field": "^1.4"
18 19
     },

+ 76
- 1
composer.lock 查看文件

@@ -4,7 +4,7 @@
4 4
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
5 5
         "This file is @generated automatically"
6 6
     ],
7
-    "content-hash": "4dd77b45cb10dba97a9548c348c2a50d",
7
+    "content-hash": "e9061dad4ffa638a18cda5e044ab8b4f",
8 8
     "packages": [
9 9
         {
10 10
             "name": "anourvalar/eloquent-serialize",
@@ -3276,6 +3276,81 @@
3276 3276
             "time": "2025-04-12T22:26:52+00:00"
3277 3277
         },
3278 3278
         {
3279
+            "name": "malzariey/filament-lexical-editor",
3280
+            "version": "1.1",
3281
+            "source": {
3282
+                "type": "git",
3283
+                "url": "https://github.com/malzariey/filament-lexical-editor.git",
3284
+                "reference": "df8d9ffcfc078fdafaa85a4265fdf03d835d5b27"
3285
+            },
3286
+            "dist": {
3287
+                "type": "zip",
3288
+                "url": "https://api.github.com/repos/malzariey/filament-lexical-editor/zipball/df8d9ffcfc078fdafaa85a4265fdf03d835d5b27",
3289
+                "reference": "df8d9ffcfc078fdafaa85a4265fdf03d835d5b27",
3290
+                "shasum": ""
3291
+            },
3292
+            "require": {
3293
+                "filament/filament": "^3.0",
3294
+                "filament/forms": "^3.0",
3295
+                "php": "^8.1",
3296
+                "spatie/laravel-package-tools": "^1.15.0"
3297
+            },
3298
+            "require-dev": {
3299
+                "nunomaduro/collision": "^7.9",
3300
+                "orchestra/testbench": "^8.0",
3301
+                "pestphp/pest": "^2.1",
3302
+                "pestphp/pest-plugin-arch": "^2.0",
3303
+                "pestphp/pest-plugin-laravel": "^2.0"
3304
+            },
3305
+            "type": "library",
3306
+            "extra": {
3307
+                "laravel": {
3308
+                    "providers": [
3309
+                        "Malzariey\\FilamentLexicalEditor\\FilamentLexicalEditorServiceProvider"
3310
+                    ]
3311
+                }
3312
+            },
3313
+            "autoload": {
3314
+                "psr-4": {
3315
+                    "Malzariey\\FilamentLexicalEditor\\": "src/"
3316
+                }
3317
+            },
3318
+            "notification-url": "https://packagist.org/downloads/",
3319
+            "license": [
3320
+                "MIT"
3321
+            ],
3322
+            "authors": [
3323
+                {
3324
+                    "name": "Majid Al Zariey",
3325
+                    "email": "malzariey@hajarco.com",
3326
+                    "role": "Developer"
3327
+                }
3328
+            ],
3329
+            "description": "Implementation of meta's lexical editor in FilamentPHP, a modern, extensible text editor framework.",
3330
+            "homepage": "https://github.com/malzariey/filament-lexical-editor",
3331
+            "keywords": [
3332
+                "Malzariey",
3333
+                "editor",
3334
+                "filaemntPHP",
3335
+                "filament-lexical-editor",
3336
+                "laravel",
3337
+                "lexical",
3338
+                "rich-text",
3339
+                "text-editor"
3340
+            ],
3341
+            "support": {
3342
+                "issues": "https://github.com/malzariey/filament-lexical-editor/issues",
3343
+                "source": "https://github.com/malzariey/filament-lexical-editor"
3344
+            },
3345
+            "funding": [
3346
+                {
3347
+                    "url": "https://github.com/malzariey",
3348
+                    "type": "github"
3349
+                }
3350
+            ],
3351
+            "time": "2025-04-30T11:06:41+00:00"
3352
+        },
3353
+        {
3279 3354
             "name": "masterminds/html5",
3280 3355
             "version": "2.9.0",
3281 3356
             "source": {

+ 2
- 0
public/css/malzariey/filament-lexical-editor/filament-lexical-editor-styles.css
文件差异内容过多而无法显示
查看文件


+ 44
- 0
public/js/malzariey/filament-lexical-editor/components/lexical-component.js
文件差异内容过多而无法显示
查看文件