|
|
@@ -15,7 +15,7 @@ import { StatusBadgeComponent } from '../../../components/domain/status-badge/st
|
|
|
import { Agent } from '../../../core/api/models';
|
|
|
import { DeliverablesService } from '../../../core/deliverables.service';
|
|
|
import { DeviceService } from '../../../core/device.service';
|
|
|
-import { fmtBalance, fmtPercent, fmtTokens, isMatrixReady, usageOf } from '../../../core/format';
|
|
|
+import { agentStatusText, fmtBalance, fmtPercent, fmtTokens, isMatrixReady, usageOf } from '../../../core/format';
|
|
|
import { fmtDateTime } from '../../../core/time';
|
|
|
import { LiveService } from '../../../core/live.service';
|
|
|
|
|
|
@@ -83,27 +83,47 @@ import { LiveService } from '../../../core/live.service';
|
|
|
<!-- 档案头 -->
|
|
|
<ui-card>
|
|
|
<div class="hero">
|
|
|
- <ui-avatar [name]="a.name" size="lg" />
|
|
|
- <div class="hero-body">
|
|
|
- <div class="hero-line">
|
|
|
- <h1>{{ a.name }}</h1>
|
|
|
- <app-status-badge [agent]="a" />
|
|
|
+ <!-- 第一行:头像 + 主信息 + 操作(🔴 头像独立列,不参与挤压) -->
|
|
|
+ <div class="hero-main">
|
|
|
+ <ui-avatar [name]="a.name" size="lg" class="hero-avatar" />
|
|
|
+ <div class="hero-body">
|
|
|
+ <div class="hero-line">
|
|
|
+ <h1 class="ellipsis" [title]="a.name">{{ a.name }}</h1>
|
|
|
+ <app-status-badge [agent]="a" />
|
|
|
+ </div>
|
|
|
+ <p class="hero-specialty ellipsis">{{ a.specialty || '数字生命' }}</p>
|
|
|
+ </div>
|
|
|
+ <div class="hero-action">
|
|
|
+ @if (ready(a)) {
|
|
|
+ <ui-button variant="primary" icon="fa-solid fa-comment" (click)="openChat(a)">
|
|
|
+ 进入会话
|
|
|
+ </ui-button>
|
|
|
+ } @else {
|
|
|
+ <!-- AC-09.2:禁用态 + 内联说明,不用原生 alert -->
|
|
|
+ <ui-button variant="primary" icon="fa-solid fa-comment-slash" [disabled]="true">
|
|
|
+ 尚未接入会话
|
|
|
+ </ui-button>
|
|
|
+ }
|
|
|
</div>
|
|
|
- <p class="hero-specialty">{{ a.specialty || '数字生命' }}</p>
|
|
|
- <p class="hero-owner">归属 {{ a.owner?.name || '未指派' }}</p>
|
|
|
</div>
|
|
|
- <div class="hero-action">
|
|
|
- @if (ready(a)) {
|
|
|
- <ui-button variant="primary" icon="fa-solid fa-comment" (click)="openChat(a)">
|
|
|
- 进入会话
|
|
|
- </ui-button>
|
|
|
- } @else {
|
|
|
- <!-- AC-09.2:禁用态 + 内联说明,不用原生 alert -->
|
|
|
- <ui-button variant="primary" icon="fa-solid fa-comment-slash" [disabled]="true">
|
|
|
- 尚未接入会话
|
|
|
- </ui-button>
|
|
|
+
|
|
|
+ <!-- 第二行:身份元信息(🔴 独立于主信息行,窄屏也不挤) -->
|
|
|
+ <dl class="hero-meta">
|
|
|
+ <div class="meta-item">
|
|
|
+ <dt>归属</dt>
|
|
|
+ <dd class="ellipsis">{{ a.owner?.name || '未指派' }}</dd>
|
|
|
+ </div>
|
|
|
+ <div class="meta-item">
|
|
|
+ <dt>状态</dt>
|
|
|
+ <dd class="ellipsis">{{ statusText(a) }}</dd>
|
|
|
+ </div>
|
|
|
+ @if (a.currentTask) {
|
|
|
+ <div class="meta-item meta-item--wide">
|
|
|
+ <dt>当前任务</dt>
|
|
|
+ <dd class="ellipsis" [title]="a.currentTask">{{ a.currentTask }}</dd>
|
|
|
+ </div>
|
|
|
}
|
|
|
- </div>
|
|
|
+ </dl>
|
|
|
</div>
|
|
|
|
|
|
@if (!ready(a)) {
|
|
|
@@ -184,6 +204,8 @@ import { LiveService } from '../../../core/live.service';
|
|
|
`,
|
|
|
styles: [
|
|
|
`
|
|
|
+ @use '../../../core/styles/breakpoints' as bp;
|
|
|
+
|
|
|
:host {
|
|
|
display: block;
|
|
|
}
|
|
|
@@ -198,41 +220,115 @@ import { LiveService } from '../../../core/live.service';
|
|
|
padding: var(--sp-3);
|
|
|
}
|
|
|
|
|
|
+ /*
|
|
|
+ * 🔴 档案头改为「两行结构」(2026-10-20 修复头像挤压)
|
|
|
+ *
|
|
|
+ * 原实现:.hero 一行 flex + flex-wrap,塞 头像 + 文字块(min-width 200px) + 按钮。
|
|
|
+ * 页面 narrow 上限 980px,减侧栏与内边距后不足 900px,768~1000px 区间必然挤压:
|
|
|
+ * 头像被压扁、文字换行、按钮掉行。
|
|
|
+ *
|
|
|
+ * 新结构:
|
|
|
+ * 第一行 .hero-main:grid [头像 4rem][文字 1fr][操作 auto]
|
|
|
+ * 第二行 .hero-meta:元信息,窄屏自动折为单列
|
|
|
+ * 头像用固定列宽,永不参与伸缩。
|
|
|
+ */
|
|
|
.hero {
|
|
|
- display: flex;
|
|
|
+ display: grid;
|
|
|
+ gap: var(--sp-4);
|
|
|
+ }
|
|
|
+ .hero-main {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: auto minmax(0, 1fr) auto;
|
|
|
align-items: center;
|
|
|
gap: var(--sp-4);
|
|
|
- flex-wrap: wrap;
|
|
|
+ }
|
|
|
+ /* 头像:固定尺寸,禁止被 flex/grid 压缩 */
|
|
|
+ .hero-avatar {
|
|
|
+ flex-shrink: 0;
|
|
|
+ align-self: center;
|
|
|
}
|
|
|
.hero-body {
|
|
|
- flex: 1;
|
|
|
- min-width: 200px;
|
|
|
display: grid;
|
|
|
gap: var(--sp-1);
|
|
|
+ min-width: 0; /* 🔴 允许子项收缩,让 ellipsis 生效 */
|
|
|
}
|
|
|
.hero-line {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
gap: var(--sp-3);
|
|
|
- flex-wrap: wrap;
|
|
|
+ min-width: 0;
|
|
|
}
|
|
|
.hero-line h1 {
|
|
|
margin: 0;
|
|
|
font-size: var(--fs-26);
|
|
|
+ line-height: 1.2;
|
|
|
color: var(--ink);
|
|
|
+ min-width: 0;
|
|
|
}
|
|
|
.hero-specialty {
|
|
|
margin: 0;
|
|
|
font-size: var(--fs-13);
|
|
|
color: var(--ink-2);
|
|
|
+ min-width: 0;
|
|
|
+ }
|
|
|
+ .hero-action {
|
|
|
+ flex-shrink: 0;
|
|
|
+ white-space: nowrap;
|
|
|
}
|
|
|
- .hero-owner {
|
|
|
+
|
|
|
+ /* 元信息行:归属 / 状态 / 当前任务 */
|
|
|
+ .hero-meta {
|
|
|
+ display: flex;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ gap: var(--sp-2) var(--sp-5);
|
|
|
margin: 0;
|
|
|
+ padding-top: var(--sp-3);
|
|
|
+ border-top: 1px solid var(--edge);
|
|
|
+ }
|
|
|
+ .meta-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: baseline;
|
|
|
+ gap: var(--sp-2);
|
|
|
+ min-width: 0;
|
|
|
+ max-width: 100%;
|
|
|
+ }
|
|
|
+ .meta-item--wide {
|
|
|
+ flex: 1 1 100%; /* 当前任务独占一行,长文本也不会挤压前面的项 */
|
|
|
+ }
|
|
|
+ .meta-item dt {
|
|
|
+ flex-shrink: 0;
|
|
|
font-size: var(--fs-11);
|
|
|
color: var(--muted);
|
|
|
}
|
|
|
- .hero-action {
|
|
|
- flex-shrink: 0;
|
|
|
+ .meta-item dd {
|
|
|
+ margin: 0;
|
|
|
+ font-size: var(--fs-12);
|
|
|
+ color: var(--ink-2);
|
|
|
+ min-width: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 🔴 窄屏(<1150 右栏让位区间):操作按钮独立成行,避免与文字抢空间 */
|
|
|
+ @include bp.dk-below-4col {
|
|
|
+ .hero-main {
|
|
|
+ grid-template-columns: auto minmax(0, 1fr);
|
|
|
+ grid-template-areas:
|
|
|
+ 'avatar body'
|
|
|
+ 'action action';
|
|
|
+ row-gap: var(--sp-3);
|
|
|
+ }
|
|
|
+ .hero-avatar {
|
|
|
+ grid-area: avatar;
|
|
|
+ }
|
|
|
+ .hero-body {
|
|
|
+ grid-area: body;
|
|
|
+ }
|
|
|
+ .hero-action {
|
|
|
+ grid-area: action;
|
|
|
+ justify-self: stretch;
|
|
|
+ }
|
|
|
+ .hero-action :is(ui-button, button) {
|
|
|
+ width: 100%;
|
|
|
+ }
|
|
|
}
|
|
|
.alert-hold {
|
|
|
margin-top: var(--sp-4);
|
|
|
@@ -366,6 +462,8 @@ export class DkAgentPage implements OnInit {
|
|
|
});
|
|
|
|
|
|
readonly dt = fmtDateTime;
|
|
|
+ /** 状态中文文案(模板用;文案由 core/format 统一给出,不在组件里拼) */
|
|
|
+ readonly statusText = agentStatusText;
|
|
|
readonly pctText = (v?: number) => fmtPercent(v);
|
|
|
readonly tokens = (a: Agent) => fmtTokens(usageOf(a).totalTokens);
|
|
|
readonly balance = (a: Agent) => fmtBalance(a.balance);
|