fix(admin): move message row actions into a rightmost column
Add a dedicated operations column for viewing, marking read and deleting messages. Keep the message cell focused on its title, excerpt and ID; adjust column widths and empty-state colspan with regression coverage. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1902ba1e87
commit
479b324f95
3 files changed
+36
-18
No files matched your search
@@ -19,20 +19,21 @@
|
||||
.aksrt-messages-admin__danger.button:hover,
|
||||
.aksrt-messages-admin__danger.button:focus { color: #fff; background: var(--an-danger); border-color: var(--an-danger); }
|
||||
|
||||
.aksrt-messages-admin__table .column-title { width: 40%; }
|
||||
.aksrt-messages-admin__table .column-type { width: 12%; }
|
||||
.aksrt-messages-admin__table .column-user { width: 18%; }
|
||||
.aksrt-messages-admin__table .column-title { width: 32%; }
|
||||
.aksrt-messages-admin__table .column-type { width: 10%; }
|
||||
.aksrt-messages-admin__table .column-user { width: 16%; }
|
||||
.aksrt-messages-admin__table .column-read { width: 8%; }
|
||||
.aksrt-messages-admin__table .column-time { width: 14%; }
|
||||
.aksrt-messages-admin__table .column-time { width: 16%; }
|
||||
.aksrt-messages-admin__table .column-actions { width: 18%; text-align: right; }
|
||||
.aksrt-messages-admin__table td { vertical-align: middle; }
|
||||
.aksrt-messages-admin__table .row-title { display: inline; padding: 0; font-weight: 600; text-align: left; word-break: break-word; line-height: 1.5; }
|
||||
.aksrt-messages-admin__table .row-title { font-weight: 600; word-break: break-word; line-height: 1.5; }
|
||||
.aksrt-messages-admin__table tr.is-unread .row-title { font-weight: 700; }
|
||||
.aksrt-messages-admin__table tr.is-unread > th:first-child { box-shadow: inset 3px 0 0 var(--an-primary); }
|
||||
.aksrt-messages-admin__table tr.is-selected > td,
|
||||
.aksrt-messages-admin__table tr.is-selected > th { background: var(--an-primary-soft); }
|
||||
.aksrt-messages-admin__desc { margin-top: 3px; color: var(--an-text-2); word-break: break-word; }
|
||||
.aksrt-msg-row-actions { display: flex; flex-wrap: wrap; gap: 6px 12px; margin-top: 8px; }
|
||||
.aksrt-msg-row-actions button { padding: 0; font-size: 12px; text-decoration: none; }
|
||||
.aksrt-msg-row-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 6px 12px; }
|
||||
.aksrt-msg-row-actions button { padding: 0; font-size: 12px; text-decoration: none; white-space: nowrap; }
|
||||
|
||||
/* 类型徽章(可点击筛选) */
|
||||
.aksrt-msg-badge {
|
||||
|
||||
@@ -97,14 +97,14 @@ trait UsersMenusMessagesList {
|
||||
echo '</div><div class="aksrt-native-table-wrap">';
|
||||
echo '<table class="wp-list-table widefat fixed striped aksrt-messages-admin__table"><thead><tr>';
|
||||
echo '<td class="manage-column column-cb check-column"><label class="screen-reader-text" for="aksrt-msg-check-all">' . esc_html__('全选本页消息', 'aksrt') . '</label><input id="aksrt-msg-check-all" type="checkbox" data-aksrt-msg-check-all /></td>';
|
||||
foreach (['title' => __('消息', 'aksrt'), 'type' => __('类型', 'aksrt'), 'user' => __('收件人', 'aksrt'), 'read' => __('状态', 'aksrt'), 'time' => __('发送时间', 'aksrt')] as $column => $label) {
|
||||
foreach (['title' => __('消息', 'aksrt'), 'type' => __('类型', 'aksrt'), 'user' => __('收件人', 'aksrt'), 'read' => __('状态', 'aksrt'), 'time' => __('发送时间', 'aksrt'), 'actions' => __('操作', 'aksrt')] as $column => $label) {
|
||||
echo '<th scope="col" class="column-' . esc_attr($column) . '">' . esc_html($label) . '</th>';
|
||||
}
|
||||
echo '</tr></thead><tbody>';
|
||||
if ($items === []) {
|
||||
$hint = $user_missing ? __('没有找到该收件人。', 'aksrt')
|
||||
: ($this->message_filter_args($filters) === [] ? __('站内还没有任何通知。', 'aksrt') : __('当前筛选条件下没有消息。', 'aksrt'));
|
||||
echo NativeUi::empty_row(6, __('暂无消息', 'aksrt'), $hint); // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
echo NativeUi::empty_row(7, __('暂无消息', 'aksrt'), $hint); // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
}
|
||||
foreach ($items as $row) {
|
||||
$this->render_message_row($row, $filters);
|
||||
@@ -123,18 +123,11 @@ trait UsersMenusMessagesList {
|
||||
'<th scope="row" class="check-column"><label class="screen-reader-text" for="aksrt-msg-%1$d">%2$s</label><input id="aksrt-msg-%1$d" type="checkbox" name="ids[]" value="%1$d" data-aksrt-msg-check /></th>',
|
||||
$id, esc_html(sprintf(__('选择消息 #%d', 'aksrt'), $id))
|
||||
);
|
||||
echo '<td class="column-title"><button type="button" class="button-link row-title" data-aksrt-msg-view="' . esc_attr((string) $id) . '" aria-haspopup="dialog" aria-controls="aksrt-msg-detail">' . esc_html($title) . '</button>';
|
||||
echo '<td class="column-title"><strong class="row-title">' . esc_html($title) . '</strong>';
|
||||
if ($desc !== '' && $desc !== $title) {
|
||||
echo '<div class="aksrt-messages-admin__desc">' . esc_html(mb_strimwidth($desc, 0, 160, '…')) . '</div>';
|
||||
}
|
||||
echo '<div class="aksrt-messages-admin__sub"><code>#' . esc_html((string) $id) . '</code></div>';
|
||||
echo '<div class="aksrt-msg-row-actions">';
|
||||
printf('<button type="button" class="button-link" data-aksrt-msg-view="%d" aria-haspopup="dialog" aria-controls="aksrt-msg-detail">%s</button>', $id, esc_html__('查看', 'aksrt'));
|
||||
if ($unread) {
|
||||
printf('<button type="button" class="button-link" data-aksrt-msg-action="mark_read" data-id="%d">%s</button>', $id, esc_html__('标记已读', 'aksrt'));
|
||||
}
|
||||
printf('<button type="button" class="button-link button-link-delete" data-aksrt-msg-action="delete" data-id="%d">%s</button>', $id, esc_html__('删除', 'aksrt'));
|
||||
echo '</div>';
|
||||
$this->render_message_detail_template($row);
|
||||
echo '</td>';
|
||||
printf(
|
||||
@@ -156,7 +149,13 @@ trait UsersMenusMessagesList {
|
||||
esc_attr($ts ? gmdate('c', $ts) : ''), esc_html($this->message_time($created)),
|
||||
$ts ? '<div class="aksrt-messages-admin__sub">' . esc_html(sprintf(__('%s前', 'aksrt'), human_time_diff($ts))) . '</div>' : ''
|
||||
);
|
||||
echo '</tr>';
|
||||
echo '<td class="column-actions"><div class="aksrt-msg-row-actions">';
|
||||
printf('<button type="button" class="button-link" data-aksrt-msg-view="%d" aria-haspopup="dialog" aria-controls="aksrt-msg-detail">%s</button>', $id, esc_html__('查看', 'aksrt'));
|
||||
if ($unread) {
|
||||
printf('<button type="button" class="button-link" data-aksrt-msg-action="mark_read" data-id="%d">%s</button>', $id, esc_html__('标记已读', 'aksrt'));
|
||||
}
|
||||
printf('<button type="button" class="button-link button-link-delete" data-aksrt-msg-action="delete" data-id="%d">%s</button>', $id, esc_html__('删除', 'aksrt'));
|
||||
echo '</div></td></tr>';
|
||||
}
|
||||
|
||||
private function render_message_detail_template(array $row): void {
|
||||
|
||||
@@ -80,6 +80,24 @@ test('list toolbar is merged into the table card and inline confirmation is remo
|
||||
assert.match(empty, /当前筛选条件下没有消息/);
|
||||
});
|
||||
|
||||
test('message row actions live only in a dedicated rightmost operations column', () => {
|
||||
const { table, empty } = harness();
|
||||
assert.match(table, /<th scope="col" class="column-time">发送时间<\/th><th scope="col" class="column-actions">操作<\/th><\/tr>/);
|
||||
const rows = [...table.matchAll(/<tr class="is-(?:unread|read)">([\s\S]*?)<\/tr>/g)];
|
||||
assert.equal(rows.length, 2);
|
||||
rows.forEach(([, row]) => {
|
||||
const cells = [...row.matchAll(/<(?:th|td)\b[^>]*>[\s\S]*?<\/(?:th|td)>/g)].map(([cell]) => cell);
|
||||
assert.equal(cells.length, 7);
|
||||
assert.match(cells[1], /class="column-title"/);
|
||||
assert.doesNotMatch(cells[1], /<button|data-aksrt-msg-view|data-aksrt-msg-action/);
|
||||
assert.match(cells[5], /class="column-time"/);
|
||||
assert.match(cells[6], /class="column-actions"/);
|
||||
assert.match(cells[6], /data-aksrt-msg-view=/);
|
||||
assert.match(cells[6], /data-aksrt-msg-action="delete"/);
|
||||
});
|
||||
assert.match(empty, /colspan="7"/);
|
||||
});
|
||||
|
||||
test('details preserve full content, escape HTML and unsafe links, and never mark read', () => {
|
||||
const { table, cache_calls } = harness();
|
||||
assert.match(table, /<template id="aksrt-msg-detail-8">/);
|
||||
|
||||
Reference in new issue
Block a user