EsgResource.php 47KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711
  1. <?php
  2. namespace App\Filament\Resources;
  3. use App\Filament\Resources\EsgResource\Pages;
  4. use App\Models\Esg;
  5. use Filament\Forms\Components\Fieldset;
  6. use Filament\Forms\Components\FileUpload;
  7. use Filament\Forms\Components\Grid;
  8. use Filament\Forms\Components\Group;
  9. use Filament\Forms\Components\Placeholder;
  10. use Filament\Forms\Components\Radio;
  11. use Filament\Forms\Components\Repeater;
  12. use Filament\Forms\Components\RichEditor;
  13. use Filament\Forms\Components\Section;
  14. use Filament\Forms\Components\Select;
  15. use Filament\Forms\Components\Tabs;
  16. use Filament\Forms\Components\Tabs\Tab;
  17. use Filament\Forms\Components\Textarea;
  18. use Filament\Forms\Components\TextInput;
  19. use Filament\Forms\Components\Toggle;
  20. use Filament\Forms\Form;
  21. use Filament\Forms\Get;
  22. use Filament\Resources\Resource;
  23. use Filament\Tables;
  24. use Filament\Tables\Columns\TextColumn;
  25. use Filament\Tables\Table;
  26. use Illuminate\Database\Eloquent\Builder;
  27. use Illuminate\Database\Eloquent\SoftDeletingScope;
  28. use Malzariey\FilamentLexicalEditor\Enums\ToolbarItem;
  29. use Malzariey\FilamentLexicalEditor\FilamentLexicalEditor;
  30. use SolutionForest\FilamentTranslateField\Forms\Component\Translate;
  31. use Filament\Forms\Components\Hidden;
  32. use Str;
  33. class EsgResource extends Resource
  34. {
  35. protected static ?string $model = Esg::class;
  36. protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack';
  37. protected static ?string $modelLabel = "ESG 管理";
  38. protected static ?string $navigationGroup = 'ESG 上稿內容管理';
  39. protected static ?string $navigationLabel = "ESG 管理";
  40. public static function form(Form $form): Form
  41. {
  42. $editor_toolbar= [
  43. ToolbarItem::UNDO, ToolbarItem::REDO, ToolbarItem::NORMAL,
  44. ToolbarItem::H2, ToolbarItem::H3, ToolbarItem::H4, ToolbarItem::H5,
  45. ToolbarItem::BULLET, ToolbarItem::NUMBERED, ToolbarItem::FONT_SIZE,
  46. ToolbarItem::BOLD, ToolbarItem::ITALIC, ToolbarItem::UNDERLINE,
  47. ToolbarItem::LINK, ToolbarItem::TEXT_COLOR, ToolbarItem::BACKGROUND_COLOR,
  48. ToolbarItem::SUBSCRIPT, ToolbarItem::LOWERCASE, ToolbarItem::DIVIDER,
  49. ToolbarItem::UPPERCASE, ToolbarItem::CLEAR, ToolbarItem::HR, ToolbarItem::FONT_FAMILY
  50. ];
  51. return $form
  52. ->schema([
  53. //
  54. Tabs::make("")->tabs([
  55. Tab::make('基本資訊')->schema([
  56. TextInput::make('keyword')->label("關聯字詞")->required()
  57. ->validationMessages([
  58. 'required' => '請填寫關聯字詞',
  59. ]),
  60. Translate::make()->schema(fn (string $locale) => [
  61. TextInput::make('title')
  62. ->label("標題")
  63. ->columnSpan(1)
  64. ->required($locale == "zh_TW")
  65. ->validationMessages([
  66. 'required' => '請填寫標題',
  67. ])
  68. ])->locales(["zh_TW", "en"])
  69. ->columnSpanFull()->columns(3),
  70. ]),
  71. Tab::make('Banner 設定')->schema([
  72. FileUpload::make('banner_pc')->label("Banner (PC)")
  73. ->disk("public")
  74. ->helperText('桌機版建議上傳圖片比例 4 : 1,寬1920px 高480px,檔案大小限制為1MB以下')
  75. // ->helperText('建議寬高限制為:1280*720px,檔案大小限制為1M以下')->maxSize('1024')
  76. ->directory("esg")->required()
  77. ->validationMessages([
  78. 'required' => '請上傳圖片',
  79. ]),
  80. FileUpload::make('banner_mobile')->label("Banner (Mobile)")
  81. ->disk("public")
  82. ->helperText('手機版建議上傳圖片比例 4 : 1,寬1920px 高480px,檔案大小限制為1MB以下')
  83. // ->helperText('建議寬高限制為:600*896px,檔案大小限制為1M以下')->maxSize('1024')
  84. ->directory("esg")->required()
  85. ->validationMessages([
  86. 'required' => '請上傳圖片',
  87. ]),
  88. Translate::make()->schema(fn (string $locale) => [
  89. TextInput::make('banner_alt')
  90. ->label("Banner 圖片註釋")
  91. ->required($locale == "zh_TW")
  92. ->validationMessages([
  93. 'required' => '請填寫圖片註釋',
  94. ]),
  95. Textarea::make("description")->rows(5)->columnSpanFull()->label("短文"),
  96. ])->locales(["zh_TW", "en"])
  97. ->columnSpanFull(),
  98. ]),
  99. Tab::make("段落設計")->schema([
  100. //1: 純文字 2:區塊文字 3:表格 4:影片 or 圖片 5:左右圖文
  101. Repeater::make("paragraphs")->label("")->schema([
  102. TextInput::make('item_key')
  103. ->default(fn () => Str::random())
  104. ->hidden()
  105. ->afterStateHydrated(function (TextInput $component, $state) {
  106. if (empty($state)) {
  107. $component->state(Str::random());
  108. }
  109. }),
  110. Radio::make("type")->options([
  111. 1 => "純文字",
  112. 2 => "區塊文字",
  113. 3 => "表格",
  114. 4 => "圖片",
  115. 5 => "影片",
  116. ])->label("")->default(1)->Live(),
  117. Group::make()->schema([
  118. Section::make('純文字設定')->schema([
  119. FilamentLexicalEditor::make('content.text_content_tw')
  120. ->label('中文')
  121. ->label(new \Illuminate\Support\HtmlString('中文  <a href="' . asset('images/tool-bar.png') . '", target="_blank">工具列說明</a>'))
  122. ->id(fn ($get) => "text_content_tw_" . $get('item_key') . "_" . uniqid())
  123. ->enabledToolbars($editor_toolbar)
  124. ->required()
  125. ->columnSpanFull(),
  126. FilamentLexicalEditor::make('content.text_content_en')
  127. ->label('English')
  128. ->id(fn ($get) => "text_content_en_" . $get('item_key') . "_" . uniqid())
  129. ->enabledToolbars($editor_toolbar)
  130. ->columnSpanFull(),
  131. ]),
  132. ])->visible(fn (Get $get):bool => $get("type") == 1),
  133. Group::make()->schema([
  134. Section::make('區塊文字設定')->schema([
  135. // 文字區塊重複器
  136. Repeater::make("content.text_blocks")->schema([
  137. TextInput::make('block_item_key')
  138. ->default(fn () => Str::random())
  139. ->hidden()
  140. ->afterStateHydrated(function (TextInput $component, $state) {
  141. if (empty($state)) {
  142. $component->state(Str::random());
  143. }
  144. }),
  145. FilamentLexicalEditor::make('block_content_tw')
  146. // ->label('繁體中文內容')
  147. ->label(new \Illuminate\Support\HtmlString('繁體中文內容  <a href="' . asset('images/tool-bar.png') . '", target="_blank">工具列說明</a>'))
  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(),
  158. ])
  159. ->label("")
  160. ->addActionLabel('增加區塊')
  161. ->orderColumn('order')
  162. ->reorderableWithButtons()
  163. ->cloneable()
  164. ->minItems(1)
  165. ->maxItems(20)
  166. ->defaultItems(1),
  167. ]),
  168. ])->visible(fn (Get $get):bool => $get('type') == 2),
  169. Group::make()->schema([
  170. Section::make('表格設定')->schema([
  171. Hidden::make('content.is_card')
  172. ->default('0'),
  173. /*
  174. Radio::make('content.is_card')
  175. ->label('手機板卡片顯示')
  176. ->options([
  177. '1' => '是', // 改為數字鍵值
  178. '0' => '否'
  179. ])
  180. ->default('1')
  181. ->inline(),
  182. */
  183. // 表格資料
  184. Placeholder::make('table_builder_info')->label("")->content('請先設定表格欄數,然後填入表格資料'),
  185. Grid::make(3)->schema([
  186. Radio::make('content.column_count')
  187. ->label('欄數')
  188. ->options([
  189. 2 => '2欄', // 改為數字鍵值
  190. 3 => '3欄',
  191. 4 => '4欄'
  192. ])
  193. ->default(2)
  194. ->inline()
  195. ->live()
  196. ->reactive(), // ✅ 添加 reactive()
  197. ]),
  198. Translate::make()->schema(fn (string $locale) => [
  199. Grid::make(2) // 改用 Grid 來支援動態 columns
  200. ->schema([
  201. TextInput::make('content.head1')->label('標題第1欄')->required()
  202. ->validationMessages([
  203. 'required' => '請填寫',
  204. ]),
  205. Radio::make('content.head_align1')
  206. ->label('對齊')
  207. ->options([
  208. 1 => '置左', // 改為數字鍵值
  209. 2 => '置中',
  210. 3 => '置右'
  211. ])
  212. ->default(1),
  213. TextInput::make('content.head2')->label('標題第2欄')->required()
  214. ->validationMessages([
  215. 'required' => '請填寫',
  216. ]),
  217. Radio::make('content.head_align2')
  218. ->label('對齊')
  219. ->options([
  220. 1 => '置左', // 改為數字鍵值
  221. 2 => '置中',
  222. 3 => '置右'
  223. ])
  224. ->default(1),
  225. TextInput::make('content.head3')->label('標題第3欄')
  226. ->visible(fn (Get $get) => intval($get('content.column_count')) >= 3)->columnSpan(1),
  227. Radio::make('content.head_align3')
  228. ->label('對齊')
  229. ->options([
  230. 1 => '置左', // 改為數字鍵值
  231. 2 => '置中',
  232. 3 => '置右'
  233. ])
  234. ->default(1)
  235. ->visible(fn (Get $get) => intval($get('content.column_count')) >= 3)->columnSpan(1),
  236. TextInput::make('content.head4')->label('標題第4欄')
  237. ->visible(fn (Get $get) => intval($get('content.column_count')) >= 4)->columnSpan(1),
  238. Radio::make('content.head_align4')
  239. ->label('對齊')
  240. ->options([
  241. 1 => '置左', // 改為數字鍵值
  242. 2 => '置中',
  243. 3 => '置右'
  244. ])
  245. ->default(1)
  246. ->visible(fn (Get $get) => intval($get('content.column_count')) >= 4)->columnSpan(1),
  247. ])
  248. ->reactive() // 加入響應式
  249. ])->locales(["zh_TW", "en"]),
  250. Repeater::make('content.simple_table_rows')
  251. ->label('表格資料')
  252. ->schema([
  253. TextInput::make('simple_table_rows_key')
  254. ->default(fn () => Str::random())
  255. ->hidden()
  256. ->afterStateHydrated(function (TextInput $component, $state) {
  257. if (empty($state)) {
  258. $component->state(Str::random());
  259. }
  260. }),
  261. Grid::make(1) // 改用 Grid 來支援動態 columns
  262. ->schema([
  263. Section::make("第一欄")->schema([
  264. Fieldset::make('中文')->schema([
  265. Radio::make('align1_tw')
  266. ->label('對齊')
  267. ->options([
  268. 1 => '置左', // 改為數字鍵值
  269. 2 => '置中',
  270. 3 => '置右'
  271. ])
  272. ->default(1)->inline(),
  273. FilamentLexicalEditor::make('col1_tw')
  274. ->id(fn ($get) => "col1_tw_" . $get('simple_table_rows_key') . "_" . uniqid())
  275. ->label(new \Illuminate\Support\HtmlString('<a href="' . asset('images/tool-bar.png') . '", target="_blank">工具列說明</a>'))
  276. ->enabledToolbars($editor_toolbar)
  277. ->required()
  278. ->columnSpanFull(),
  279. ]),
  280. Fieldset::make('英文')->schema([
  281. Radio::make('align1_en')
  282. ->label('對齊')
  283. ->options([
  284. 1 => '置左', // 改為數字鍵值
  285. 2 => '置中',
  286. 3 => '置右'
  287. ])
  288. ->default(1)->inline(),
  289. FilamentLexicalEditor::make('col1_en')
  290. ->id(fn ($get) => "col1_en_" . $get('simple_table_rows_key') . "_" . uniqid())
  291. ->enabledToolbars($editor_toolbar)
  292. ->columnSpanFull(),
  293. ]),
  294. ]),
  295. Section::make("第二欄")->schema([
  296. Fieldset::make('中文')->schema([
  297. Radio::make('align2_tw')
  298. ->label('對齊')
  299. ->options([
  300. 1 => '置左', // 改為數字鍵值
  301. 2 => '置中',
  302. 3 => '置右'
  303. ])
  304. ->default(1)->inline(),
  305. FilamentLexicalEditor::make('col2_tw')
  306. ->id(fn ($get) => "col2_tw_" . $get('simple_table_rows_key') . "_" . uniqid())
  307. ->label(new \Illuminate\Support\HtmlString('<a href="' . asset('images/tool-bar.png') . '", target="_blank">工具列說明</a>'))
  308. ->enabledToolbars($editor_toolbar)
  309. ->required()
  310. ->columnSpanFull(),
  311. ]),
  312. Fieldset::make('英文')->schema([
  313. Radio::make('align2_en')
  314. ->label('對齊')
  315. ->options([
  316. 1 => '置左', // 改為數字鍵值
  317. 2 => '置中',
  318. 3 => '置右'
  319. ])
  320. ->default(1)->inline(),
  321. FilamentLexicalEditor::make('col2_en')
  322. ->id(fn ($get) => "col2_en_" . $get('simple_table_rows_key') . "_" . uniqid())
  323. ->enabledToolbars($editor_toolbar)
  324. ->columnSpanFull(),
  325. ]),
  326. ]),
  327. Section::make("第三欄")->schema([
  328. Fieldset::make('中文')->schema([
  329. Radio::make('align3_tw')
  330. ->label('對齊')
  331. ->options([
  332. 1 => '置左', // 改為數字鍵值
  333. 2 => '置中',
  334. 3 => '置右'
  335. ])
  336. ->default(1),
  337. FilamentLexicalEditor::make('col3_tw')
  338. ->id(fn ($get) => "col3_tw_" . $get('simple_table_rows_key') . "_" . uniqid())
  339. ->label(new \Illuminate\Support\HtmlString('<a href="' . asset('images/tool-bar.png') . '", target="_blank">工具列說明</a>'))
  340. ->enabledToolbars($editor_toolbar)
  341. ->required()
  342. ->columnSpanFull(),
  343. ]),
  344. Fieldset::make('英文')->schema([
  345. Radio::make('align3_en')
  346. ->label('對齊')
  347. ->options([
  348. 1 => '置左', // 改為數字鍵值
  349. 2 => '置中',
  350. 3 => '置右'
  351. ])
  352. ->default(1)->inline(),
  353. FilamentLexicalEditor::make('col3_en')
  354. ->id(fn ($get) => "col3_en_" . $get('simple_table_rows_key') . "_" . uniqid())
  355. ->enabledToolbars($editor_toolbar)
  356. ->columnSpanFull(),
  357. ]),
  358. ])->visible(fn (Get $get) => intval($get('../../../content.column_count')) >= 3),
  359. Section::make("第四欄")->schema([
  360. Fieldset::make('中文')->schema([
  361. Radio::make('align4_tw')
  362. ->label('對齊')
  363. ->options([
  364. 1 => '置左', // 改為數字鍵值
  365. 2 => '置中',
  366. 3 => '置右'
  367. ])
  368. ->default(1)->inline(),
  369. FilamentLexicalEditor::make('col4_tw')
  370. ->id(fn ($get) => "col4_tw_" . $get('simple_table_rows_key') . "_" . uniqid())
  371. ->label(new \Illuminate\Support\HtmlString('<a href="' . asset('images/tool-bar.png') . '", target="_blank">工具列說明</a>'))
  372. ->enabledToolbars($editor_toolbar)
  373. ->required()
  374. ->columnSpanFull(),
  375. ]),
  376. Fieldset::make('英文')->schema([
  377. Radio::make('align4_en')
  378. ->label('對齊')
  379. ->options([
  380. 1 => '置左', // 改為數字鍵值
  381. 2 => '置中',
  382. 3 => '置右'
  383. ])
  384. ->default(1)->inline(),
  385. FilamentLexicalEditor::make('col4_en')
  386. ->id(fn ($get) => "col4_en_" . $get('simple_table_rows_key') . "_" . uniqid())
  387. ->enabledToolbars($editor_toolbar)
  388. ->columnSpanFull(),
  389. ]),
  390. ])->visible(fn (Get $get) => intval($get('../../../content.column_count')) >= 4),
  391. ])
  392. ->reactive() // 加入響應式
  393. ])
  394. ->id(fn ($get) => "simple_table_" . $get('simple_table_rows_key'))
  395. ->addActionLabel('新增列')
  396. ->reorderableWithButtons()
  397. ->minItems(1)
  398. ]),
  399. ])->visible(fn (Get $get):bool => $get('type') == 3),
  400. Group::make()->schema([
  401. Section::make('')->schema([
  402. Repeater::make("content.multiple_images")->schema([
  403. TextInput::make('para_img_item_key')
  404. ->default(fn () => Str::random())
  405. ->hidden()
  406. ->afterStateHydrated(function (TextInput $component, $state) {
  407. if (empty($state)) {
  408. $component->state(Str::random());
  409. }
  410. }),
  411. Translate::make()->schema(fn (string $locale) => [
  412. TextInput::make('image_alt')->label("圖片註文")
  413. ])->locales(["zh_TW", "en"])
  414. ->id(fn ($get) => "para_img_mul_" . $get('para_img_item_key')),
  415. FileUpload::make('image_url')->label("")->disk("public")
  416. ->helperText('檔案大小限制為1MB以下')
  417. // ->helperText('建議寬高限制為:1080*675px,檔案大小限制為1M以下')->maxSize('1024')
  418. ->directory("esg/paragraphPhoto")
  419. ->maxFiles(10)
  420. ->required()
  421. ->validationMessages([
  422. 'required' => '請上傳圖片',
  423. ]),
  424. ])
  425. ->addActionLabel('新增')
  426. ->label("")
  427. ->orderColumn('order')
  428. ])
  429. ])->visible(fn (Get $get):bool => $get("type") == 4),
  430. Group::make()->schema([
  431. Section::make("")->schema([
  432. FileUpload::make('content.video_img')->label("影片底圖")
  433. ->disk("public")
  434. ->helperText('檔案大小限制為1MB以下')
  435. ->directory("esg/paragraphVideo"),
  436. Translate::make()->schema(fn (string $locale) => [
  437. TextInput::make('content.link')->label("網址")->required()
  438. ->validationMessages([
  439. 'required' => '請附上連結',
  440. ])
  441. ])->locales(["zh_TW", "en"]),
  442. Translate::make()->schema(fn (string $locale) => [
  443. TextInput::make('content.video_alt')->label("影片註文"),
  444. ])->locales(["zh_TW", "en"])
  445. ]),
  446. ])->visible(fn (Get $get):bool => $get("type") == 5),
  447. ])
  448. ->relationship("paragraphs")
  449. ->addActionLabel('新增段落')
  450. ->collapsible()
  451. ->reorderableWithButtons()
  452. ->orderColumn('order')
  453. ->cloneable()
  454. ->mutateRelationshipDataBeforeFillUsing(function (array $data): array {
  455. switch($data["type"]){
  456. case "1":
  457. if (!empty($data["content"]['text_content'])) {
  458. $content = is_string($data["content"]['text_content'])
  459. ? json_decode($data["content"]['text_content'], true)
  460. : $data["content"]['text_content'];
  461. if (is_array($content)) {
  462. $data["content"]['text_content_tw'] = $content['zh_TW'] ?? '';
  463. $data["content"]['text_content_en'] = $content['en'] ?? '';
  464. }
  465. }
  466. break;
  467. case "2":
  468. if (!empty($data["content"]['text_blocks']) && count($data["content"]['text_blocks']) > 0) {
  469. foreach ($data["content"]['text_blocks'] as $block_index => $block){
  470. $content = is_string($block['block_content'])
  471. ? json_decode($block['block_content'], true)
  472. : $block['block_content'];
  473. if (is_array($content)) {
  474. $data["content"]['text_blocks'][$block_index]["block_content_tw"] = $content['zh_TW'] ?? '';
  475. $data["content"]['text_blocks'][$block_index]["block_content_en"] = $content['en'] ?? '';
  476. }
  477. }
  478. }
  479. break;
  480. case "3":
  481. if (!empty($data["content"]['simple_table_rows']) && count($data["content"]['simple_table_rows']) > 0) {
  482. foreach ($data["content"]['simple_table_rows'] as $row_key => $row) {
  483. $flatRow = [];
  484. foreach ($row as $key => $value) {
  485. if (is_array($value) && isset($value['zh_TW'], $value['en'])) {
  486. // 多语言字段,分离为两个字段
  487. $flatRow[$key . '_tw'] = $value['zh_TW'];
  488. $flatRow[$key . '_en'] = $value['en'];
  489. } else {
  490. // 非多语言字段,保持原样
  491. $flatRow[$key] = $value;
  492. }
  493. }
  494. $data["content"]['simple_table_rows'][$row_key] = $flatRow;
  495. }
  496. }
  497. break;
  498. }
  499. return $data;
  500. })
  501. ->mutateRelationshipDataBeforeSaveUsing(function (array $data): array {
  502. switch($data["type"]){
  503. case "1":
  504. $data["content"]["text_content"] = ["zh_TW" => $data["content"]["text_content_tw"] ?? "" , "en" => $data["content"]["text_content_en"] ?? ""];
  505. // 移除臨時的分離欄位,只保留合併的 JSON 欄位
  506. unset($data["content"]["text_content_tw"]);
  507. unset($data["content"]["text_content_en"]);
  508. // 清理 HTML 標籤(如果需要)
  509. foreach ($data['content']['text_content'] as $locale => $content) {
  510. $data['content']['text_content'][$locale] = stripslashes($content);
  511. }
  512. break;
  513. case "2":
  514. // 確保有 text_blocks
  515. if (!isset($data['content']['text_blocks'])) {
  516. $data['content']['text_blocks'] = [];
  517. }
  518. // 處理每個文字區塊
  519. foreach ($data['content']['text_blocks'] as $index => &$block) {
  520. // 確保區塊有必要的欄位
  521. $block['block_item_key'] = $block['block_item_key'] ?? Str::random();
  522. $block['order'] = $block['order'] ?? ($index + 1);
  523. $block["block_content"] = ["zh_TW" => $block["block_content_tw"] ?? "" , "en" => $block["block_content_en"] ?? ""];
  524. // 移除臨時的分離欄位,只保留合併的 JSON 欄位
  525. unset($block["block_content_tw"]);
  526. unset($block["block_content_en"]);
  527. // 清理區塊內容
  528. if (isset($block['block_content'])) {
  529. foreach ($block['block_content'] as $locale => $content) {
  530. $block['block_content'][$locale] = stripslashes($content);
  531. }
  532. }
  533. }
  534. break;
  535. case "3":
  536. // 確保欄數設定
  537. $columnCount = $data['content']['column_count'] ?? 2;
  538. // 確保表頭存在
  539. if (!isset($data['content']['headers'])) {
  540. $data['content']['headers'] = ['zh_TW' => [], 'en' => []];
  541. }
  542. // 確保表格資料存在
  543. if (!isset($data['content']['simple_table_rows'])) {
  544. $data['content']['simple_table_rows'] = [];
  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. for ($i = $columnCount + 1; $i <= 4; $i++) {
  565. unset($row["col{$i}_tw"]);
  566. unset($row["col{$i}_en"]);
  567. unset($row["align{$i}_tw"]);
  568. unset($row["align{$i}_en"]);
  569. }
  570. }
  571. break;
  572. }
  573. return $data;
  574. })
  575. ->mutateRelationshipDataBeforeCreateUsing(function (array $data): array {
  576. switch($data["type"]){
  577. case "1":
  578. $data["content"]["text_content"] = ["zh_TW" => $data["content"]["text_content_tw"] ?? "" , "en" => $data["content"]["text_content_en"] ?? ""];
  579. // 移除臨時的分離欄位,只保留合併的 JSON 欄位
  580. unset($data["content"]["text_content_tw"]);
  581. unset($data["content"]["text_content_en"]);
  582. // 清理 HTML 標籤(如果需要)
  583. foreach ($data['content']['text_content'] as $locale => $content) {
  584. $data['content']['text_content'][$locale] = stripslashes($content);
  585. }
  586. break;
  587. case "2":
  588. // 確保有 text_blocks
  589. if (!isset($data['content']['text_blocks'])) {
  590. $data['content']['text_blocks'] = [];
  591. }
  592. // 處理每個文字區塊
  593. foreach ($data['content']['text_blocks'] as $index => &$block) {
  594. // 確保區塊有必要的欄位
  595. $block['block_item_key'] = $block['block_item_key'] ?? Str::random();
  596. $block['order'] = $block['order'] ?? ($index + 1);
  597. $block["block_content"] = ["zh_TW" => $block["block_content_tw"] ?? "" , "en" => $block["block_content_en"] ?? ""];
  598. // 移除臨時的分離欄位,只保留合併的 JSON 欄位
  599. unset($block["block_content_tw"]);
  600. unset($block["block_content_en"]);
  601. // 清理區塊內容
  602. if (isset($block['block_content'])) {
  603. foreach ($block['block_content'] as $locale => $content) {
  604. $block['block_content'][$locale] = stripslashes($content);
  605. }
  606. }
  607. }
  608. break;
  609. case "3":
  610. // 確保欄數設定
  611. $columnCount = $data['content']['column_count'] ?? 2;
  612. // 確保表頭存在
  613. if (!isset($data['content']['headers'])) {
  614. $data['content']['headers'] = ['zh_TW' => [], 'en' => []];
  615. }
  616. // 確保表格資料存在
  617. if (!isset($data['content']['simple_table_rows'])) {
  618. $data['content']['simple_table_rows'] = [];
  619. }
  620. // 處理表格資料,確保欄數一致
  621. foreach ($data['content']['simple_table_rows'] as $rowIndex => &$row) {
  622. // 確保每行都有正確的欄數
  623. for ($i = 1; $i <= $columnCount; $i++) {
  624. $row["col{$i}"] = ["zh_TW" => $row["col{$i}_tw"] ?? "" , "en" => $row["col{$i}_en"] ?? ""];
  625. foreach ($row["col{$i}"] as $locale => $content) {
  626. $row["col{$i}"][$locale] = $content;
  627. }
  628. unset($row["col{$i}_tw"]);
  629. unset($row["col{$i}_en"]);
  630. $row["align{$i}"] = ["zh_TW" => $row["align{$i}_tw"] ?? "" , "en" => $row["align{$i}_en"] ?? ""];
  631. foreach ($row["align{$i}"] as $locale => $content) {
  632. $row["align{$i}"][$locale] = $content;
  633. }
  634. unset($row["align{$i}_tw"]);
  635. unset($row["align{$i}_en"]);
  636. }
  637. // 移除多餘的欄位
  638. for ($i = $columnCount + 1; $i <= 4; $i++) {
  639. unset($row["col{$i}_tw"]);
  640. unset($row["col{$i}_en"]);
  641. unset($row["align{$i}_tw"]);
  642. unset($row["align{$i}_en"]);
  643. }
  644. }
  645. break;
  646. }
  647. \Log::info($data);
  648. return $data;
  649. })
  650. ])->columnSpanFull(),
  651. ])
  652. ->columnSpanFull()
  653. ]);
  654. }
  655. public static function table(Table $table): Table
  656. {
  657. return $table
  658. ->columns([
  659. //
  660. TextColumn::make("title")->label("標題"),
  661. ])
  662. ->filters([
  663. //
  664. ])
  665. ->actions([
  666. Tables\Actions\EditAction::make(),
  667. ]);
  668. }
  669. public static function getRelations(): array
  670. {
  671. return [
  672. //
  673. ];
  674. }
  675. public static function getPages(): array
  676. {
  677. return [
  678. 'index' => Pages\ListEsg::route('/'),
  679. 'create' => Pages\CreateEsg::route('/create'),
  680. 'edit' => Pages\EditEsg::route('/{record}/edit'),
  681. ];
  682. }
  683. }