feat(admin): open ticket creation in a dialog on the list page
Move "代客提交工单" back into a native <dialog> on the ticket list while the ticket workspace stays a full page. Closing asks before discarding input, ignores backdrop clicks, resets the form and returns focus; an uncertain create result keeps the form locked behind "重新加载". Remove the ?view=new create page route and its styles. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d86a85e6a6
commit
54cc5301c3
9 files changed
+169
-65
No files matched your search
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* 工单管理:列表、处理工作区(?id=)与代客提交(?view=new)三个页内视图。
|
||||
* 工单管理:列表(含代客提交弹窗)与处理工作区(?id=)两个页内视图。
|
||||
* 写操作统一使用 TicketRestAdmin;此处只负责服务端渲染。
|
||||
*
|
||||
* @package AKSRT
|
||||
@@ -46,6 +46,7 @@ trait UsersMenusTicketsCenter {
|
||||
'uncertain' => __('连接中断,提交结果尚未确认。请先重新加载核对,避免重复提交。', 'aksrt'),
|
||||
'required' => __('请填写内容,不能只包含空格。', 'aksrt'),
|
||||
'tooLong' => __('插入后将超过字数限制,请先精简回复内容。', 'aksrt'),
|
||||
'discard' => __('有尚未保存的内容。关闭后将丢弃,是否继续?', 'aksrt'),
|
||||
],
|
||||
]);
|
||||
}
|
||||
@@ -55,12 +56,9 @@ trait UsersMenusTicketsCenter {
|
||||
$filters = $this->ticket_filters($_GET);
|
||||
$paged = max(1, absint($_GET['paged'] ?? 1));
|
||||
$id = absint($_GET['id'] ?? 0);
|
||||
$view = isset($_GET['view']) && is_scalar($_GET['view']) ? sanitize_key((string) wp_unslash($_GET['view'])) : '';
|
||||
NativeUi::page_open('aksrt-tickets-admin');
|
||||
if ($id > 0) {
|
||||
$this->render_ticket_detail_page($id, $this->ticket_context($filters, $paged));
|
||||
} elseif ($view === 'new') {
|
||||
$this->render_ticket_create_page($this->ticket_context($filters, $paged));
|
||||
} else {
|
||||
$this->render_ticket_list_page($filters, $paged);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
/** 代客提交工单(?view=new):独立页内表单,创建成功后跳转到新工单的工作区。@package AKSRT */
|
||||
/** 代客提交工单:列表页上的原生 <dialog>,创建成功后跳转到新工单的工作区。@package AKSRT */
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
|
||||
@@ -10,40 +10,42 @@ defined('ABSPATH') || exit;
|
||||
|
||||
trait UsersMenusTicketsCreate {
|
||||
|
||||
private function render_ticket_create_page(array $context): void {
|
||||
$back = $this->ticket_page_url($context);
|
||||
NativeUi::header(
|
||||
__('代客提交工单', 'aksrt'),
|
||||
__('为指定用户创建工单,创建后由你跟进,用户会收到通知。', 'aksrt'),
|
||||
'dashicons-plus-alt2',
|
||||
[['label' => __('返回工单列表', 'aksrt'), 'url' => $back, 'icon' => 'dashicons-arrow-left-alt']]
|
||||
);
|
||||
private const TICKET_CREATE_DIALOG = 'aksrt-ticket-create-dialog';
|
||||
|
||||
private function render_ticket_create_dialog(): void {
|
||||
$types = TicketTypeService::all();
|
||||
NativeUi::card_open('', '', false, 'aksrt-tickets-admin__create');
|
||||
echo '<dialog id="' . esc_attr(self::TICKET_CREATE_DIALOG) . '" class="aksrt-ticket-dialog" aria-labelledby="aksrt-ticket-create-title" aria-describedby="aksrt-ticket-create-description">';
|
||||
echo $types === [] ? '<div class="aksrt-ticket-dialog__frame">' : $this->ticket_form_open('create', 0, 'aksrt-ticket-dialog__frame'); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<header class="aksrt-ticket-dialog__head"><div><h2 id="aksrt-ticket-create-title" tabindex="-1" autofocus>' . esc_html__('代客提交工单', 'aksrt') . '</h2>';
|
||||
echo '<p id="aksrt-ticket-create-description">' . esc_html__('为指定用户创建工单,创建后由你跟进,用户会收到通知。', 'aksrt') . '</p></div>';
|
||||
echo '<button type="button" class="aksrt-ticket-dialog__close" data-aksrt-dialog-close aria-label="' . esc_attr__('关闭弹窗', 'aksrt') . '"><span class="dashicons dashicons-no-alt" aria-hidden="true"></span></button></header>';
|
||||
echo '<div class="aksrt-ticket-dialog__body">' . $this->ticket_feedback(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
if ($types === []) {
|
||||
echo NativeUi::empty_state(__('尚未配置工单部门', 'aksrt'), __('请先添加部门,再为用户创建工单。', 'aksrt')); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<p><a class="button" href="' . esc_url(admin_url('admin.php?page=aksrt-settings#/users/ticket')) . '">' . esc_html__('前往工单配置', 'aksrt') . '</a></p>';
|
||||
NativeUi::card_close();
|
||||
return;
|
||||
} else {
|
||||
echo '<div class="aksrt-native-form-grid">';
|
||||
echo '<label class="aksrt-native-field"><span class="aksrt-native-field__label">' . esc_html__('用户 ID', 'aksrt') . '</span><input type="number" name="user_id" min="1" step="1" required placeholder="' . esc_attr__('填写工单归属用户的 ID', 'aksrt') . '"></label>';
|
||||
echo '<label class="aksrt-native-field"><span class="aksrt-native-field__label">' . esc_html__('工单部门', 'aksrt') . '</span><select name="ticket_type_id" required><option value="">' . esc_html__('请选择部门', 'aksrt') . '</option>';
|
||||
foreach ($types as $type) {
|
||||
echo '<option value="' . (int) $type['id'] . '">' . esc_html((string) $type['name']) . '</option>';
|
||||
}
|
||||
echo '</select></label>';
|
||||
echo '<label class="aksrt-native-field is-wide"><span class="aksrt-native-field__label">' . esc_html__('工单标题', 'aksrt') . '</span><input type="text" name="title" maxlength="' . TicketSettings::title_max() . '" required placeholder="' . esc_attr__('简要说明用户遇到的问题', 'aksrt') . '"></label>';
|
||||
if (TicketSettings::order_link_enabled()) {
|
||||
echo '<label class="aksrt-native-field is-wide"><span class="aksrt-native-field__label">' . esc_html__('关联订单(可选)', 'aksrt') . '</span><input type="text" name="order_number" placeholder="' . esc_attr__('填写订单号', 'aksrt') . '"></label>';
|
||||
}
|
||||
echo '<label class="aksrt-native-field is-wide"><span class="aksrt-native-field__label">' . esc_html__('问题描述', 'aksrt') . '</span><textarea id="aksrt-ticket-create-content" name="content" rows="7" maxlength="' . TicketSettings::content_max() . '" required data-aksrt-ticket-editor aria-describedby="aksrt-ticket-create-count" placeholder="' . esc_attr__('描述问题、发生时间和已尝试的处理方式', 'aksrt') . '"></textarea>';
|
||||
echo '<span id="aksrt-ticket-create-count" class="aksrt-ticket-counter" data-aksrt-ticket-counter="aksrt-ticket-create-content">0 / ' . TicketSettings::content_max() . '</span></label></div>';
|
||||
}
|
||||
echo $this->ticket_feedback(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo $this->ticket_form_open('create', 0, 'aksrt-tickets-admin__create-form'); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<div class="aksrt-native-form-grid">';
|
||||
echo '<label class="aksrt-native-field"><span class="aksrt-native-field__label">' . esc_html__('用户 ID', 'aksrt') . '</span><input type="number" name="user_id" min="1" step="1" required placeholder="' . esc_attr__('填写工单归属用户的 ID', 'aksrt') . '"></label>';
|
||||
echo '<label class="aksrt-native-field"><span class="aksrt-native-field__label">' . esc_html__('工单部门', 'aksrt') . '</span><select name="ticket_type_id" required><option value="">' . esc_html__('请选择部门', 'aksrt') . '</option>';
|
||||
foreach ($types as $type) {
|
||||
echo '<option value="' . (int) $type['id'] . '">' . esc_html((string) $type['name']) . '</option>';
|
||||
echo '</div><footer class="aksrt-ticket-dialog__foot">';
|
||||
if ($types !== []) {
|
||||
echo '<span class="aksrt-ticket-hint">' . esc_html__('新工单状态为「已回复」', 'aksrt') . '</span>';
|
||||
}
|
||||
echo '</select></label>';
|
||||
echo '<label class="aksrt-native-field is-wide"><span class="aksrt-native-field__label">' . esc_html__('工单标题', 'aksrt') . '</span><input type="text" name="title" maxlength="' . TicketSettings::title_max() . '" required placeholder="' . esc_attr__('简要说明用户遇到的问题', 'aksrt') . '"></label>';
|
||||
if (TicketSettings::order_link_enabled()) {
|
||||
echo '<label class="aksrt-native-field is-wide"><span class="aksrt-native-field__label">' . esc_html__('关联订单(可选)', 'aksrt') . '</span><input type="text" name="order_number" placeholder="' . esc_attr__('填写订单号', 'aksrt') . '"></label>';
|
||||
echo '<button type="button" class="button" data-aksrt-dialog-close>' . esc_html__('取消', 'aksrt') . '</button>';
|
||||
if ($types !== []) {
|
||||
echo '<button type="submit" class="button button-primary">' . esc_html__('创建工单并通知用户', 'aksrt') . '</button>';
|
||||
}
|
||||
echo '<label class="aksrt-native-field is-wide"><span class="aksrt-native-field__label">' . esc_html__('问题描述', 'aksrt') . '</span><textarea id="aksrt-ticket-create-content" name="content" rows="8" maxlength="' . TicketSettings::content_max() . '" required data-aksrt-ticket-editor aria-describedby="aksrt-ticket-create-count" placeholder="' . esc_attr__('描述问题、发生时间和已尝试的处理方式', 'aksrt') . '"></textarea>';
|
||||
echo '<span id="aksrt-ticket-create-count" class="aksrt-ticket-counter" data-aksrt-ticket-counter="aksrt-ticket-create-content">0 / ' . TicketSettings::content_max() . '</span></label></div>';
|
||||
echo '<div class="aksrt-native-form-foot"><button type="submit" class="button button-primary">' . esc_html__('创建工单并通知用户', 'aksrt') . '</button>';
|
||||
echo '<a class="button" href="' . esc_url($back) . '">' . esc_html__('取消', 'aksrt') . '</a>';
|
||||
echo '<span class="aksrt-ticket-hint">' . esc_html__('新工单状态为「已回复」', 'aksrt') . '</span></div></form>';
|
||||
NativeUi::card_close();
|
||||
echo '</footer>' . ($types === [] ? '</div>' : '</form>') . '</dialog>';
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ defined('ABSPATH') || exit;
|
||||
trait UsersMenusTicketsList {
|
||||
|
||||
private function render_ticket_list_page(array $filters, int $paged): void {
|
||||
// 先查询并夹取页码,页头「代客提交」链接才能带上实际页码。
|
||||
// 先查询并夹取页码,创建成功后跳转的工作区链接才能带上实际页码。
|
||||
[$result, $paged] = $this->ticket_list_result($filters, $paged);
|
||||
NativeUi::header(
|
||||
__('工单管理', 'aksrt'),
|
||||
@@ -21,7 +21,7 @@ trait UsersMenusTicketsList {
|
||||
[
|
||||
[
|
||||
'label' => __('代客提交工单', 'aksrt'),
|
||||
'url' => $this->ticket_page_url(['view' => 'new'] + $this->ticket_context($filters, $paged)),
|
||||
'dialog' => self::TICKET_CREATE_DIALOG,
|
||||
'primary' => true,
|
||||
'icon' => 'dashicons-plus-alt2',
|
||||
],
|
||||
@@ -33,6 +33,7 @@ trait UsersMenusTicketsList {
|
||||
]
|
||||
);
|
||||
$this->render_ticket_list($filters, $paged, $result);
|
||||
$this->render_ticket_create_dialog();
|
||||
}
|
||||
|
||||
/** 越界页码夹到末页,避免处理最后一条工单后停在空页。返回 [查询结果, 实际页码]。 */
|
||||
|
||||
Reference in new issue
Block a user