perf(admin): toggle the ticket process form instead of reloading the page
Expand and cancel were full admin navigations (action/id query round trip), which is why they felt slow. Pre-render each pending ticket's process form as a hidden colspan=6 row and swap list row <-> form via a small delegated jQuery handler: expand hides the row (info stays single-sourced), collapse brings it back, only one form is open at a time, and the no-JS action=process links keep working as a fallback.
This commit is contained in:
@@ -218,23 +218,62 @@ trait UsersMenusTickets {
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach ($tickets as $row) {
|
foreach ($tickets as $row) {
|
||||||
$is_processing = $process_action === 'process'
|
$process_key = (string) $row['kind'] . '-' . (string) $row['id'];
|
||||||
&& $process_id === (string) $row['kind'] . '-' . (string) $row['id'];
|
$expanded = $process_action === 'process' && $process_id === $process_key && $row['status'] === 'pending';
|
||||||
if ($is_processing) {
|
$this->render_ticket_row($row, $page_url, $expanded);
|
||||||
// 被处理工单:行信息由处理表单承载,不再重复渲染列表行——
|
|
||||||
// 此前 row + 表单把同一工单信息展示两次,且表单裸嵌 <tbody>
|
if ($row['status'] !== 'pending') {
|
||||||
// 属无效 HTML,被浏览器 foster-parent 后那次多余的行信息
|
continue;
|
||||||
// 错位到“确认提交”下方。合法包裹为 colspan=6 单元格。
|
|
||||||
echo '<tr><td colspan="6">';
|
|
||||||
$this->render_ticket_process_form($row, $action_url);
|
|
||||||
echo '</td></tr>';
|
|
||||||
} else {
|
|
||||||
$this->render_ticket_row($row, $page_url);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 展开与收起走 JS 切换(无整页刷新,即点即达):展开时隐藏列表行、
|
||||||
|
// 显示处理表单(信息只出现一次);取消时换回。无 JS 环境由立即处理
|
||||||
|
// 链接(action=process)整页兜底,直链进入时表单默认展开。
|
||||||
|
$hidden_attr = $expanded ? '' : ' style="display:none;"';
|
||||||
|
echo '<tr data-aksrt-ticket-process="' . esc_attr($process_key) . '"' . $hidden_attr . '><td colspan="6">';
|
||||||
|
$this->render_ticket_process_form($row, $action_url);
|
||||||
|
echo '</td></tr>';
|
||||||
}
|
}
|
||||||
|
|
||||||
echo '</tbody></table></div>';
|
echo '</tbody></table></div>';
|
||||||
|
|
||||||
|
// 行内展开/收起的轻量交互(管理员环境必有 jQuery)。
|
||||||
|
echo '<script>
|
||||||
|
(function ($) {
|
||||||
|
function processRow(key) {
|
||||||
|
return $("[data-aksrt-ticket-process=\\"" + key + "\\"]");
|
||||||
|
}
|
||||||
|
function sourceRow(key) {
|
||||||
|
return $("[data-aksrt-ticket-row=\\"" + key + "\\"]");
|
||||||
|
}
|
||||||
|
$(document).on("click", "[data-aksrt-ticket-toggle]", function (e) {
|
||||||
|
var key = $(this).attr("data-aksrt-ticket-toggle");
|
||||||
|
var $row = processRow(key);
|
||||||
|
if (!$row.length) return;
|
||||||
|
e.preventDefault();
|
||||||
|
var show = !$row.is(":visible");
|
||||||
|
// 一次只展开一个:先收起其它展开的表单并换回其列表行。
|
||||||
|
$("[data-aksrt-ticket-process]:visible").not($row).each(function () {
|
||||||
|
$(this).hide();
|
||||||
|
sourceRow($(this).attr("data-aksrt-ticket-process")).show();
|
||||||
|
});
|
||||||
|
if (show) {
|
||||||
|
sourceRow(key).hide();
|
||||||
|
$row.show();
|
||||||
|
} else {
|
||||||
|
$row.hide();
|
||||||
|
sourceRow(key).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$(document).on("click", "[data-aksrt-ticket-cancel]", function (e) {
|
||||||
|
var key = $(this).attr("data-aksrt-ticket-cancel");
|
||||||
|
e.preventDefault();
|
||||||
|
processRow(key).hide();
|
||||||
|
sourceRow(key).show();
|
||||||
|
});
|
||||||
|
})(jQuery);
|
||||||
|
</script>';
|
||||||
|
|
||||||
$this->ticket_pagenavi($total, self::TICKETS_PER_PAGE);
|
$this->ticket_pagenavi($total, self::TICKETS_PER_PAGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,11 +298,12 @@ trait UsersMenusTickets {
|
|||||||
echo '</span></div><br class="clear"></div>';
|
echo '</span></div><br class="clear"></div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
private function render_ticket_row(array $row, string $page_url): void {
|
private function render_ticket_row(array $row, string $page_url, bool $hidden = false): void {
|
||||||
$kind = $row['kind'];
|
$kind = $row['kind'];
|
||||||
$status = $row['status'];
|
$status = $row['status'];
|
||||||
$user_id = (int) $row['user_id'];
|
$user_id = (int) $row['user_id'];
|
||||||
$url = add_query_arg(['action' => 'process', 'id' => $kind . '-' . $row['id']], $page_url);
|
$process_key = $kind . '-' . (string) $row['id'];
|
||||||
|
$url = add_query_arg(['action' => 'process', 'id' => $process_key], $page_url);
|
||||||
|
|
||||||
// 状态列(对齐参考主题:待处理→立即处理按钮 / 已批准 / 处理完成 / 已拒绝 / 正在处理)
|
// 状态列(对齐参考主题:待处理→立即处理按钮 / 已批准 / 处理完成 / 已拒绝 / 正在处理)
|
||||||
$is_processing = isset($_GET['action']) && sanitize_key((string) wp_unslash($_GET['action'])) === 'process'
|
$is_processing = isset($_GET['action']) && sanitize_key((string) wp_unslash($_GET['action'])) === 'process'
|
||||||
@@ -271,7 +311,8 @@ trait UsersMenusTickets {
|
|||||||
if ($is_processing) {
|
if ($is_processing) {
|
||||||
$status_html = '<span style="color:#fb4444;">' . esc_html__('正在处理', 'aksrt') . '</span>';
|
$status_html = '<span style="color:#fb4444;">' . esc_html__('正在处理', 'aksrt') . '</span>';
|
||||||
} elseif ($status === 'pending') {
|
} elseif ($status === 'pending') {
|
||||||
$status_html = '<a class="button" href="' . esc_url($url) . '">' . esc_html__('立即处理', 'aksrt') . '</a>';
|
// JS 切换展开(无整页刷新);无 JS 时 href 整页兜底。
|
||||||
|
$status_html = '<a class="button" data-aksrt-ticket-toggle="' . esc_attr($process_key) . '" href="' . esc_url($url) . '">' . esc_html__('立即处理', 'aksrt') . '</a>';
|
||||||
} elseif ($status === 'resolved') {
|
} elseif ($status === 'resolved') {
|
||||||
$status_html = $kind === 'appeal'
|
$status_html = $kind === 'appeal'
|
||||||
? '<span style="color:#16a60a;">' . esc_html__('已批准', 'aksrt') . '</span>'
|
? '<span style="color:#16a60a;">' . esc_html__('已批准', 'aksrt') . '</span>'
|
||||||
@@ -300,7 +341,7 @@ trait UsersMenusTickets {
|
|||||||
|
|
||||||
$desc_html = $kind === 'report' ? $this->ticket_report_desc_html($row['payload']) : $this->ticket_appeal_desc_html($row['payload'], (int) $row['id']);
|
$desc_html = $kind === 'report' ? $this->ticket_report_desc_html($row['payload']) : $this->ticket_appeal_desc_html($row['payload'], (int) $row['id']);
|
||||||
|
|
||||||
echo '<tr>';
|
echo '<tr' . ($hidden ? ' style="display:none;"' : '') . ' data-aksrt-ticket-row="' . esc_attr($process_key) . '">';
|
||||||
echo '<td>' . $status_html . '</td>';
|
echo '<td>' . $status_html . '</td>';
|
||||||
echo '<td>' . $type_html . '</td>';
|
echo '<td>' . $type_html . '</td>';
|
||||||
echo '<td>' . $user_html . '</td>';
|
echo '<td>' . $user_html . '</td>';
|
||||||
@@ -489,8 +530,8 @@ trait UsersMenusTickets {
|
|||||||
|
|
||||||
echo '<tr><th></th><td><p>';
|
echo '<tr><th></th><td><p>';
|
||||||
echo '<button type="submit" class="button button-primary process-submit">' . esc_html__('确认提交', 'aksrt') . '</button> ';
|
echo '<button type="submit" class="button button-primary process-submit">' . esc_html__('确认提交', 'aksrt') . '</button> ';
|
||||||
// 取消:回到未展开状态(去掉 action/id 参数的当前列表,保留筛选)。
|
// 取消:收起表单、换回列表行(JS 切换;无 JS 时整页回列表兜底)。
|
||||||
echo '<a class="button" href="' . esc_url(remove_query_arg(['action', 'id'])) . '">' . esc_html__('取消', 'aksrt') . '</a>';
|
echo '<a class="button" data-aksrt-ticket-cancel="' . esc_attr($row['kind'] . '-' . (string) $row['id']) . '" href="' . esc_url(remove_query_arg(['action', 'id'])) . '">' . esc_html__('取消', 'aksrt') . '</a>';
|
||||||
echo '</p></td></tr>';
|
echo '</p></td></tr>';
|
||||||
echo '</tbody></table></form>';
|
echo '</tbody></table></form>';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -179,14 +179,17 @@ test('native ban page mirrors the reference theme layout', () => {
|
|||||||
assert.match(tickets, /name="action" value="' \. esc_attr\(\$kind === 'report' \? 'aksrt_users_update_report' : 'aksrt_users_process_appeal'\)/)
|
assert.match(tickets, /name="action" value="' \. esc_attr\(\$kind === 'report' \? 'aksrt_users_update_report' : 'aksrt_users_process_appeal'\)/)
|
||||||
assert.match(usersMenus, /admin_post_aksrt_users_update_report/)
|
assert.match(usersMenus, /admin_post_aksrt_users_update_report/)
|
||||||
assert.match(usersMenus, /admin_post_aksrt_users_process_appeal/)
|
assert.match(usersMenus, /admin_post_aksrt_users_process_appeal/)
|
||||||
// 内联处理视图(参考主题方向):被处理工单以 colspan=6 合法单元格展开
|
// 内联处理视图(参考主题方向):表单以合法的 colspan=6 单元格随待处理行
|
||||||
// 表单,且不再渲染其列表行——此前 row + 表单重复展示同一工单信息,
|
// 预渲染;展开/取消走 JS 行↔表单互换(无整页刷新),信息只出现一次,
|
||||||
// 表单裸嵌 tbody 被 foster-parent 后多余行信息错位到“确认提交”下方。
|
// 不再裸嵌 tbody 被 foster-parent 错位。
|
||||||
assert.match(tickets, /<tr><td colspan="6">';\s*\$this->render_ticket_process_form/)
|
assert.match(tickets, /<td colspan="6">';\s*\$this->render_ticket_process_form/)
|
||||||
assert.match(tickets, /if \(\$is_processing\) \{[\s\S]*?render_ticket_process_form\(\$row, \$action_url\)[\s\S]*?\} else \{\s*\$this->render_ticket_row/)
|
assert.match(tickets, /data-aksrt-ticket-toggle="/)
|
||||||
// 展开的处理表单提供“取消”按钮(确认提交右侧),可收起回到列表。
|
assert.match(tickets, /data-aksrt-ticket-cancel="/)
|
||||||
|
assert.match(tickets, /data-aksrt-ticket-row="/)
|
||||||
|
assert.match(tickets, /e\.preventDefault\(\);/)
|
||||||
|
// 展开的处理表单提供“取消”按钮(确认提交右侧)。
|
||||||
assert.match(tickets, /class="button button-primary process-submit"/)
|
assert.match(tickets, /class="button button-primary process-submit"/)
|
||||||
assert.match(tickets, /<a class="button" href="' \. esc_url\(remove_query_arg\(\['action', 'id'\]\)\) . '">' \. esc_html__\('取消', 'aksrt'\)/)
|
assert.match(tickets, /<a class="button" data-aksrt-ticket-cancel="' \. esc_attr\(\$row\['kind'\] \. '-' \. \(string\) \$row\['id'\]\) . '" href="' \. esc_url\(remove_query_arg\(\['action', 'id'\]\)\) . '">' \. esc_html__\('取消', 'aksrt'\)/)
|
||||||
|
|
||||||
// 分页对齐参考主题 zibpay_admin_pagenavi(每页 20 条)
|
// 分页对齐参考主题 zibpay_admin_pagenavi(每页 20 条)
|
||||||
assert.match(tickets, /TICKETS_PER_PAGE = 20/)
|
assert.match(tickets, /TICKETS_PER_PAGE = 20/)
|
||||||
|
|||||||
Reference in New Issue
Block a user