@@ -182,10 +182,15 @@ function ($problem_id) {
182182 },
183183 function ($ type , $ problem_id ) {
184184 if ($ type == '+ ' ) {
185+ $ max_order = DB ::selectFirst ([
186+ "select max(problem_order) as max_order from lists_problems " ,
187+ "where " , ["list_id " => UOJList::info ('id ' )],
188+ ]);
189+ $ next_order = ($ max_order ['max_order ' ] ?? 0 ) + 1 ;
185190 DB ::insert ([
186191 "insert into lists_problems " ,
187- DB ::bracketed_fields (["list_id " , "problem_id " ]),
188- "values " , DB ::tuple ([UOJList::info ('id ' ), $ problem_id ]),
192+ DB ::bracketed_fields (["list_id " , "problem_id " , " problem_order " ]),
193+ "values " , DB ::tuple ([UOJList::info ('id ' ), $ problem_id, $ next_order ]),
189194 ]);
190195 } else if ($ type == '- ' ) {
191196 DB ::delete ([
@@ -204,6 +209,92 @@ function ($type, $problem_id) {
204209 );
205210 $ problems_form ->runAtServer ();
206211
212+ // Reorder form
213+ $ current_order = DB ::selectAll ([
214+ "select problem_id from lists_problems " ,
215+ "where " , ["list_id " => UOJList::info ('id ' )],
216+ "order by problem_order asc, problem_id asc " ,
217+ ]);
218+ $ current_order_str = implode ("\n" , array_map (fn ($ x ) => $ x ['problem_id ' ], $ current_order ));
219+
220+ $ reorder_form = new UOJForm ('reorder_problems ' );
221+ $ reorder_form ->addTextArea ('problem_order ' , [
222+ 'label ' => '题目顺序 ' ,
223+ 'input_class ' => 'form-control font-monospace ' ,
224+ 'default_value ' => $ current_order_str ,
225+ 'validator_php ' => function ($ str , &$ vdata ) {
226+ $ ids = [];
227+ foreach (explode ("\n" , $ str ) as $ line ) {
228+ $ line = trim ($ line );
229+ if ($ line === '' ) continue ;
230+ if (!validateUInt ($ line )) {
231+ return "无效题号: {$ line }" ;
232+ }
233+ $ ids [] = intval ($ line );
234+ }
235+
236+ // Get all problem IDs in current list
237+ $ current_ids = array_map (
238+ fn ($ x ) => intval ($ x ['problem_id ' ]),
239+ DB ::selectAll ([
240+ "select problem_id from lists_problems " ,
241+ "where " , ["list_id " => UOJList::info ('id ' )],
242+ ])
243+ );
244+
245+ sort ($ current_ids );
246+ $ sorted_ids = $ ids ;
247+ sort ($ sorted_ids );
248+
249+ if ($ sorted_ids !== $ current_ids ) {
250+ return '题目列表与当前题单中的题目不一致,请确保包含所有题目且无多余题目 ' ;
251+ }
252+
253+ if (count ($ ids ) !== count (array_unique ($ ids ))) {
254+ return '存在重复的题号 ' ;
255+ }
256+
257+ $ vdata ['order ' ] = $ ids ;
258+ return '' ;
259+ },
260+ 'help ' => '每行一个题号,按照从上到下的顺序排列。 ' ,
261+ ]);
262+ $ reorder_form ->handle = function ($ vdata ) {
263+ $ order = 1 ;
264+ foreach ($ vdata ['order ' ] as $ problem_id ) {
265+ DB ::update ([
266+ "update lists_problems " ,
267+ "set " , ["problem_order " => $ order ],
268+ "where " , [
269+ "list_id " => UOJList::info ('id ' ),
270+ "problem_id " => $ problem_id ,
271+ ],
272+ ]);
273+ $ order ++;
274+ }
275+ dieWithJsonData (['status ' => 'success ' , 'message ' => '排序更新成功 ' ]);
276+ };
277+ $ reorder_form ->setAjaxSubmit (<<<EOD
278+ function(res) {
279+ if (res.status === 'success') {
280+ $('#reorder-result-alert')
281+ .html('排序更新成功!')
282+ .addClass('alert-success')
283+ .removeClass('alert-danger')
284+ .show();
285+ location.reload();
286+ } else {
287+ $('#reorder-result-alert')
288+ .html('排序更新失败。' + (res.message || ''))
289+ .removeClass('alert-success')
290+ .addClass('alert-danger')
291+ .show();
292+ }
293+ }
294+ EOD );
295+ $ reorder_form ->config ['submit_button ' ]['text ' ] = '更新排序 ' ;
296+ $ reorder_form ->runAtServer ();
297+
207298 $ n_problems = DB ::selectCount ([
208299 "select count(*) " ,
209300 "from lists_problems " ,
@@ -258,12 +349,13 @@ function ($type, $problem_id) {
258349 <div class="card-body">
259350 <?php
260351 echoLongTable (
261- ['problem_id ' ],
352+ ['problem_id ' , ' problem_order ' ],
262353 "lists_problems " ,
263354 ["list_id " => UOJList::info ('id ' )],
264- "order by problem_id asc " ,
355+ "order by problem_order asc, problem_id asc " ,
265356 <<<EOD
266357 <tr>
358+ <th class="text-center" style="width:3em">#</th>
267359 <th class="text-center" style="width:5em">ID</th>
268360 <th>标题</th>
269361 </tr>
@@ -272,6 +364,7 @@ function ($row) {
272364 $ problem = UOJProblem::query ($ row ['problem_id ' ]);
273365
274366 echo HTML ::tag_begin ('tr ' );
367+ echo HTML ::tag ('td ' , ['class ' => 'text-center text-muted ' ], $ row ['problem_order ' ]);
275368 echo HTML ::tag ('td ' , ['class ' => 'text-center ' ], $ problem ->info ['id ' ]);
276369 echo HTML ::tag_begin ('td ' );
277370 echo $ problem ->getLink (['with ' => 'none ' ]);
@@ -292,7 +385,25 @@ function ($row) {
292385 );
293386 ?>
294387
388+ <hr>
389+
295390 <?php $ problems_form ->printHTML () ?>
391+
392+ <hr>
393+
394+ <div id="reorder-result-alert" class="alert" role="alert" style="display: none"></div>
395+ <div class="row">
396+ <div class="col-md-8">
397+ <?php $ reorder_form ->printHTML () ?>
398+ </div>
399+ <div class="col-md-4">
400+ <h5>排序说明</h5>
401+ <ul class="mb-0">
402+ <li>每行一个题号,按照期望的展示顺序排列。</li>
403+ <li>必须包含题单中所有题目,不能多也不能少。</li>
404+ </ul>
405+ </div>
406+ </div>
296407 </div>
297408 </div>
298409 <?php endif ?>
0 commit comments