mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-13 03:13:41 +08:00
polish(approval-grants-ui): show granter name, fix note cell rendering, tighten layout
This commit is contained in:
parent
b34de4e078
commit
5f2adf15f6
@ -15,6 +15,7 @@ import vip.mate.approval.grant.repository.ApprovalGrantMapper;
|
||||
import vip.mate.approval.grant.repository.ApprovalResolutionLogMapper;
|
||||
import vip.mate.approval.grant.service.ApprovalGrantService;
|
||||
import vip.mate.auth.model.UserEntity;
|
||||
import vip.mate.auth.repository.UserMapper;
|
||||
import vip.mate.auth.service.AuthService;
|
||||
import vip.mate.common.result.R;
|
||||
import vip.mate.exception.MateClawException;
|
||||
@ -57,6 +58,7 @@ public class ApprovalGrantController {
|
||||
private final ApprovalGrantMapper grantMapper;
|
||||
private final ApprovalResolutionLogMapper resolutionMapper;
|
||||
private final AuthService authService;
|
||||
private final UserMapper userMapper;
|
||||
private final WorkspaceService workspaceService;
|
||||
|
||||
// ─── Create ─────────────────────────────────────────────────────────
|
||||
@ -139,7 +141,39 @@ public class ApprovalGrantController {
|
||||
wrapper.eq(ApprovalGrant::getGrantedBy, actorId);
|
||||
}
|
||||
Page<ApprovalGrant> pageObj = new Page<>(boundedPage, boundedSize);
|
||||
return R.ok(grantMapper.selectPage(pageObj, wrapper));
|
||||
IPage<ApprovalGrant> result = grantMapper.selectPage(pageObj, wrapper);
|
||||
fillGranterNames(result.getRecords());
|
||||
return R.ok(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch-loads the display name (nickname → username fallback) for every
|
||||
* unique {@code grantedBy} id on the page and writes it into the entity's
|
||||
* transient {@code grantedByName} field. One round-trip via
|
||||
* {@code selectBatchIds} rather than N queries; the field stays null when
|
||||
* the source user has since been deleted.
|
||||
*/
|
||||
private void fillGranterNames(java.util.List<ApprovalGrant> records) {
|
||||
if (records == null || records.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
java.util.Set<Long> userIds = new java.util.HashSet<>();
|
||||
for (ApprovalGrant g : records) {
|
||||
if (g.getGrantedBy() != null) userIds.add(g.getGrantedBy());
|
||||
}
|
||||
if (userIds.isEmpty()) return;
|
||||
java.util.Map<Long, String> idToName = userMapper.selectBatchIds(userIds).stream()
|
||||
.collect(java.util.stream.Collectors.toMap(
|
||||
UserEntity::getId,
|
||||
u -> u.getNickname() != null && !u.getNickname().isEmpty()
|
||||
? u.getNickname()
|
||||
: u.getUsername(),
|
||||
(a, b) -> a));
|
||||
for (ApprovalGrant g : records) {
|
||||
if (g.getGrantedBy() != null) {
|
||||
g.setGrantedByName(idToName.get(g.getGrantedBy()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Active summary (chip "(N)") ────────────────────────────────────
|
||||
|
||||
@ -52,6 +52,15 @@ public class ApprovalGrant {
|
||||
|
||||
private Long grantedBy;
|
||||
|
||||
/**
|
||||
* Display name of the granter (nickname → username). Not persisted; the
|
||||
* controller fills it in after {@code selectPage} by batch-loading the
|
||||
* touched user ids so the UI doesn't need a separate /users call for a
|
||||
* snowflake → name lookup. Null when the user no longer exists.
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private String grantedByName;
|
||||
|
||||
private LocalDateTime grantedAt;
|
||||
|
||||
private Integer revoked;
|
||||
|
||||
@ -1043,6 +1043,8 @@ export interface ApprovalGrant {
|
||||
grantKind: GrantKind
|
||||
expireAt: string | null
|
||||
grantedBy: string
|
||||
/** Display name (nickname → username) of the granter; null if the user was deleted. */
|
||||
grantedByName?: string | null
|
||||
grantedAt: string
|
||||
revoked: number
|
||||
revokedBy: string | null
|
||||
|
||||
@ -104,22 +104,35 @@
|
||||
</td>
|
||||
<td>{{ t(`approval.grant.kind.${kindI18nKey(row.grantKind)}`) }}</td>
|
||||
<td class="muted">{{ formatDate(row.expireAt) }}</td>
|
||||
<td>{{ row.grantedBy }}</td>
|
||||
<td class="note-cell" :title="row.note || ''">{{ row.note }}</td>
|
||||
<td>
|
||||
<span v-if="row.grantedByName" :title="`#${row.grantedBy}`">{{ row.grantedByName }}</span>
|
||||
<span v-else class="muted">#{{ row.grantedBy }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="note-cell" :title="row.note || ''">{{ row.note }}</span>
|
||||
</td>
|
||||
<td class="col-actions">
|
||||
<button
|
||||
v-if="row.revoked === 0"
|
||||
class="action-btn danger"
|
||||
class="row-action-btn row-action-btn--danger"
|
||||
:title="t('approval.grant.revokeBtn')"
|
||||
@click="confirmRevoke(row)"
|
||||
>
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none"
|
||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none"
|
||||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="3 6 5 6 21 6"/>
|
||||
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>
|
||||
</svg>
|
||||
<span>{{ t('approval.grant.revokeBtn') }}</span>
|
||||
</button>
|
||||
<span v-else class="muted">{{ t('common.revoked') }}</span>
|
||||
<span v-else class="revoked-pill">
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none"
|
||||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="9"/>
|
||||
<line x1="5.6" y1="5.6" x2="18.4" y2="18.4"/>
|
||||
</svg>
|
||||
{{ t('common.revoked') }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@ -610,7 +623,19 @@ onMounted(loadGrants)
|
||||
overflow-x: auto;
|
||||
}
|
||||
.rules-table {
|
||||
min-width: 900px;
|
||||
min-width: 1100px; /* enough headroom so the kind / granter / date columns
|
||||
don't collapse into vertical-text mode on a narrow viewport */
|
||||
}
|
||||
/* Prevent narrow columns from wrapping into stacked Chinese glyphs. */
|
||||
.rules-table th,
|
||||
.rules-table td {
|
||||
white-space: nowrap;
|
||||
}
|
||||
/* Note cell is the one cell where wrapping is fine — it's already
|
||||
ellipsis-truncated by .note-cell. Keep this explicit so adding nowrap to
|
||||
the wider scope above doesn't suppress the ellipsis. */
|
||||
.rules-table td .note-cell {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Pagination row — right-aligned beneath the table. McPagination provides
|
||||
|
||||
Loading…
Reference in New Issue
Block a user