Explorar o código

style(admin): compact member detail actions and audit reason

彭峰 hai 1 mes
pai
achega
b84e680e81

+ 3 - 5
projects/xiaoshu-admin/src/app/pages/operations-user-detail.component.html

@@ -1,7 +1,5 @@
-<header class="page-header detail-actions"><button type="button" (click)="refreshBusiness()" [disabled]="loading()"><lucide-icon [img]="icons.RefreshCw" [size]="17" />刷新</button></header>
-
 @if (loading() && !user()) { <div class="state"><lucide-icon class="spin" [img]="icons.LoaderCircle" [size]="28" />正在加载会员资料…</div> }
 @if (loading() && !user()) { <div class="state"><lucide-icon class="spin" [img]="icons.LoaderCircle" [size]="28" />正在加载会员资料…</div> }
-@else if (error() && !user()) { <div class="state error" role="alert">{{ error() }}</div> }
+@else if (error() && !user()) { <div class="state error" role="alert"><span>{{ error() }}</span><button type="button" (click)="refreshBusiness()" [disabled]="loading()"><lucide-icon [img]="icons.RefreshCw" [size]="17" />重新加载</button></div> }
 @else { @if (user(); as detail) {
 @else { @if (user(); as detail) {
   <section class="profile-summary" aria-label="会员资料与操作">
   <section class="profile-summary" aria-label="会员资料与操作">
     <div class="avatar"><lucide-icon [img]="icons.UserRound" [size]="28" /></div>
     <div class="avatar"><lucide-icon [img]="icons.UserRound" [size]="28" /></div>
@@ -10,7 +8,7 @@
       <p>会员 ID {{ detail.userId || '—' }}@if (detail.username && detail.username !== detail.displayName) { · 账号 {{ detail.username }} }</p>
       <p>会员 ID {{ detail.userId || '—' }}@if (detail.username && detail.username !== detail.displayName) { · 账号 {{ detail.username }} }</p>
       <div class="profile-facts"><span>{{ detail.learningStatusLabel }}</span><span>{{ detail.parentDisplayName || '未绑定门店' }}</span><span>{{ detail.registeredAt | date:'yyyy-MM-dd' }} 注册</span></div>
       <div class="profile-facts"><span>{{ detail.learningStatusLabel }}</span><span>{{ detail.parentDisplayName || '未绑定门店' }}</span><span>{{ detail.registeredAt | date:'yyyy-MM-dd' }} 注册</span></div>
     </div>
     </div>
-    <nav class="quick-actions" aria-label="会员常用操作"><button type="button" (click)="selectTab('balances')"><lucide-icon [img]="icons.WalletCards" [size]="17" />{{ detail.permissions.canManage ? '充值课时' : '查看课时' }}</button><button type="button" (click)="selectTab('courses')"><lucide-icon [img]="icons.BookOpen" [size]="17" />{{ detail.permissions.canEdit ? '按分类开课' : '查看课程' }}</button><a class="action-link primary" routerLink="/admin/schedule" [queryParams]="{studentId:detail.userId}"><lucide-icon [img]="icons.CalendarDays" [size]="17" />为该学员排课</a></nav>
+    <nav class="quick-actions" aria-label="会员常用操作"><button class="profile-refresh" type="button" (click)="refreshBusiness()" [disabled]="loading()"><lucide-icon [img]="icons.RefreshCw" [class.spin]="loading()" [size]="17" />刷新</button><button type="button" (click)="selectTab('balances')"><lucide-icon [img]="icons.WalletCards" [size]="17" />{{ detail.permissions.canManage ? '充值课时' : '查看课时' }}</button><button type="button" (click)="selectTab('courses')"><lucide-icon [img]="icons.BookOpen" [size]="17" />{{ detail.permissions.canEdit ? '按分类开课' : '查看课程' }}</button><a class="action-link primary" routerLink="/admin/schedule" [queryParams]="{studentId:detail.userId}"><lucide-icon [img]="icons.CalendarDays" [size]="17" />为该学员排课</a></nav>
   </section>
   </section>
   @if (detail.writeReady === false) { <p class="notice" role="status">{{ detail.readOnlyReason }}</p> }
   @if (detail.writeReady === false) { <p class="notice" role="status">{{ detail.readOnlyReason }}</p> }
   @if (message()) { <p class="notice" role="status">{{ message() }}</p> } @if (error()) { <p class="notice error" role="alert">{{ error() }}</p> }
   @if (message()) { <p class="notice" role="status">{{ message() }}</p> } @if (error()) { <p class="notice error" role="alert">{{ error() }}</p> }
@@ -26,7 +24,7 @@
   </nav>
   </nav>
 
 
   @if (sectionLoading()) { <div class="section-loading"><lucide-icon class="spin" [img]="icons.LoaderCircle" [size]="22" />正在读取业务记录…</div> }
   @if (sectionLoading()) { <div class="section-loading"><lucide-icon class="spin" [img]="icons.LoaderCircle" [size]="22" />正在读取业务记录…</div> }
-  @if ((tab()==='overview'||tab()==='audit') && (detail.permissions.canEdit||detail.permissions.canManage)) { <section class="reason-bar"><label><span>本次操作原因 <b aria-hidden="true">*</b></span><input [ngModel]="reason()" (ngModelChange)="reason.set($event)" minlength="2" maxlength="500" placeholder="保存资料、变更账号或添加备注前填写,至少 2 个字" /></label><small>操作原因随本次修改记入审计日志</small></section> }
+  @if ((tab()==='overview'||tab()==='audit') && (detail.permissions.canEdit||detail.permissions.canManage)) { <section class="reason-bar"><label><span>本次操作原因 <b aria-hidden="true">*</b></span><input [ngModel]="reason()" (ngModelChange)="reason.set($event)" minlength="2" maxlength="500" aria-describedby="operation-reason-hint" placeholder="填写修改原因,至少 2 个字" /></label><small id="operation-reason-hint">保存资料、变更账号或添加备注时必填,记入审计日志</small></section> }
   @switch (tab()) {
   @switch (tab()) {
     @case ('overview') {
     @case ('overview') {
       <div class="detail-grid">
       <div class="detail-grid">

+ 9 - 8
projects/xiaoshu-admin/src/app/pages/operations-user-detail.component.scss

@@ -1,10 +1,6 @@
 @use '../components/member-controls' as member;
 @use '../components/member-controls' as member;
 :host { display:block; color:#263733; font-size:14px; line-height:1.5; }
 :host { display:block; color:#263733; font-size:14px; line-height:1.5; }
 @include member.controls;
 @include member.controls;
-.page-header { display:flex; align-items:center; justify-content:space-between; gap:18px; margin-bottom:16px; }
-.page-header > button { flex-shrink:0; white-space:nowrap; }
-.page-header a { display:inline-flex; align-items:center; gap:6px; color:#526e63; font-size:13px; text-decoration:none; }
-.page-header a:hover { text-decoration:underline; }
 .profile-summary { display:flex; align-items:center; flex-wrap:wrap; gap:18px; padding:24px; border-radius:14px; color:#fff; background:linear-gradient(120deg,#163b58,#176953); }
 .profile-summary { display:flex; align-items:center; flex-wrap:wrap; gap:18px; padding:24px; border-radius:14px; color:#fff; background:linear-gradient(120deg,#163b58,#176953); }
 .avatar { display:grid; flex:none; place-items:center; width:50px; height:50px; border-radius:12px; background:#ffffff24; }
 .avatar { display:grid; flex:none; place-items:center; width:50px; height:50px; border-radius:12px; background:#ffffff24; }
 .profile-identity { flex:1 1 320px; min-width:0; }
 .profile-identity { flex:1 1 320px; min-width:0; }
@@ -14,6 +10,8 @@
 .profile-facts { display:flex; flex-wrap:wrap; gap:8px; margin-top:12px; }
 .profile-facts { display:flex; flex-wrap:wrap; gap:8px; margin-top:12px; }
 .profile-facts span { padding:4px 9px; border:1px solid #ffffff40; border-radius:999px; color:#e0eee8; font-size:12px; }
 .profile-facts span { padding:4px 9px; border:1px solid #ffffff40; border-radius:999px; color:#e0eee8; font-size:12px; }
 .quick-actions { display:flex; flex-wrap:wrap; gap:10px; margin-left:auto; }
 .quick-actions { display:flex; flex-wrap:wrap; gap:10px; margin-left:auto; }
+.quick-actions .profile-refresh { color:#e0eee8; border-color:#ffffff50; background:transparent; }
+.quick-actions .profile-refresh:hover { background:#ffffff18; }
 .quick-actions > * { white-space:nowrap; }
 .quick-actions > * { white-space:nowrap; }
 .status,.learning-status { display:inline-flex; padding:5px 10px; border-radius:999px; color:#17654e; background:#e6f5ee; font-size:12px; font-weight:600; white-space:nowrap; }
 .status,.learning-status { display:inline-flex; padding:5px 10px; border-radius:999px; color:#17654e; background:#e6f5ee; font-size:12px; font-weight:600; white-space:nowrap; }
 .status.disabled { color:#923b44; background:#ffe7e9; }
 .status.disabled { color:#923b44; background:#ffe7e9; }
@@ -73,9 +71,11 @@
 .timeline strong { font-size:14px; }
 .timeline strong { font-size:14px; }
 .timeline time { display:block; margin-top:4px; color:#697c72; font-size:12px; }
 .timeline time { display:block; margin-top:4px; color:#697c72; font-size:12px; }
 .timeline p { margin:8px 0 0; color:#53645f; font-size:14px; line-height:1.7; overflow-wrap:anywhere; }
 .timeline p { margin:8px 0 0; color:#53645f; font-size:14px; line-height:1.7; overflow-wrap:anywhere; }
-.reason-bar { display:flex; flex-wrap:wrap; align-items:end; gap:12px; margin-bottom:18px; padding:14px 16px; border:1px solid #dce6e0; border-radius:10px; background:#f3f8f5; }
-.reason-bar label { flex:1 1 320px; }
-.reason-bar small { padding-bottom:10px; color:#63786b; }
+.reason-bar { display:flex; flex-wrap:wrap; align-items:center; gap:8px 16px; margin-bottom:16px; padding:10px 14px; border:1px solid #dce6e0; border-radius:10px; background:#f3f8f5; }
+.reason-bar label { display:flex; align-items:center; gap:12px; flex:1 1 480px; min-width:0; }
+.reason-bar label > span { flex-shrink:0; white-space:nowrap; }
+.reason-bar input { flex:1; min-width:0; }
+.reason-bar small { color:#63786b; font-size:12px; }
 .reason-bar b { color:#a33f48; }
 .reason-bar b { color:#a33f48; }
 .pagination { display:flex; flex-wrap:wrap; align-items:center; justify-content:end; gap:12px; margin:16px 0; color:#60756a; font-size:13px; }
 .pagination { display:flex; flex-wrap:wrap; align-items:center; justify-content:end; gap:12px; margin:16px 0; color:#60756a; font-size:13px; }
 .pagination>span { margin-right:auto; }
 .pagination>span { margin-right:auto; }
@@ -86,4 +86,5 @@
 @media(max-width:1100px) { .detail-grid,.audit-grid{grid-template-columns:1fr}.profile-editor{grid-row:auto}.risk-panel{grid-column:auto} }
 @media(max-width:1100px) { .detail-grid,.audit-grid{grid-template-columns:1fr}.profile-editor{grid-row:auto}.risk-panel{grid-column:auto} }
 @media(max-width:650px) { .profile-summary{padding:18px;gap:14px}.avatar{display:none}.profile-identity{flex-basis:100%}.profile-title h1{font-size:24px}.quick-actions{width:100%;margin-left:0}.tabs button{flex:0 0 auto}.form-grid,.account-facts{grid-template-columns:1fr}.metric-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.panel{padding:16px}.panel>header{flex-wrap:wrap}.date-filters{grid-template-columns:repeat(2,minmax(0,1fr))}.quick-actions>*{flex:1 1 auto}.pagination{gap:8px} }
 @media(max-width:650px) { .profile-summary{padding:18px;gap:14px}.avatar{display:none}.profile-identity{flex-basis:100%}.profile-title h1{font-size:24px}.quick-actions{width:100%;margin-left:0}.tabs button{flex:0 0 auto}.form-grid,.account-facts{grid-template-columns:1fr}.metric-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.panel{padding:16px}.panel>header{flex-wrap:wrap}.date-filters{grid-template-columns:repeat(2,minmax(0,1fr))}.quick-actions>*{flex:1 1 auto}.pagination{gap:8px} }
 
 
-.page-header.detail-actions { justify-content: flex-end; }
+
+@media(max-width:650px) { .reason-bar label { display:grid; gap:6px; flex-basis:100%; } .reason-bar { gap:6px; } }