|
|
@@ -72,9 +72,39 @@ import { NzMessageService } from 'ng-zorro-antd/message';
|
|
|
stroke-linecap="round"
|
|
|
stroke-linejoin="round"
|
|
|
></polyline>
|
|
|
+ <line
|
|
|
+ *ngIf="hoveredDauIndex >= 0"
|
|
|
+ [attr.x1]="hoveredDauX"
|
|
|
+ [attr.y1]="16"
|
|
|
+ [attr.x2]="hoveredDauX"
|
|
|
+ [attr.y2]="164"
|
|
|
+ stroke="#91caff"
|
|
|
+ stroke-width="1"
|
|
|
+ stroke-dasharray="3 3"
|
|
|
+ ></line>
|
|
|
+ <circle
|
|
|
+ *ngIf="hoveredDauIndex >= 0"
|
|
|
+ [attr.cx]="hoveredDauX"
|
|
|
+ [attr.cy]="hoveredDauY"
|
|
|
+ r="4"
|
|
|
+ fill="#1677ff"
|
|
|
+ ></circle>
|
|
|
+ <rect
|
|
|
+ x="0"
|
|
|
+ y="0"
|
|
|
+ width="600"
|
|
|
+ height="180"
|
|
|
+ fill="transparent"
|
|
|
+ (mousemove)="onChartMouseMove($event)"
|
|
|
+ (mouseleave)="onChartMouseLeave()"
|
|
|
+ ></rect>
|
|
|
</svg>
|
|
|
</div>
|
|
|
|
|
|
+ <div class="chart-hover-tip" *ngIf="hoveredDauIndex >= 0">
|
|
|
+ {{ hoveredDauDate }}:{{ hoveredDauValue }}
|
|
|
+ </div>
|
|
|
+
|
|
|
<div class="chart-meta">
|
|
|
<span>{{ dauTrendStartDate }}</span>
|
|
|
<span>Max: {{ dauMax }}</span>
|
|
|
@@ -110,9 +140,18 @@ import { NzMessageService } from 'ng-zorro-antd/message';
|
|
|
<tbody>
|
|
|
<tr *ngFor="let work of worksTable.data">
|
|
|
<td>
|
|
|
- <div class="work-thumbnail" [style.backgroundImage]="'url(' + work.thumbnail + ')'" ></div>
|
|
|
+ <img
|
|
|
+ [src]="work.thumbnail"
|
|
|
+ class="work-thumbnail"
|
|
|
+ alt="作品缩略图"
|
|
|
+ (click)="openArtworkImage(work.thumbnail)"
|
|
|
+ />
|
|
|
+ </td>
|
|
|
+ <td>
|
|
|
+ <a [href]="getArtworkDetailUrl(work.resId)" target="_blank" rel="noopener noreferrer">
|
|
|
+ {{ work.name }}
|
|
|
+ </a>
|
|
|
</td>
|
|
|
- <td>{{ work.name }}</td>
|
|
|
<td>{{ work.areaCount || 0 }}</td>
|
|
|
<td>{{ work.clickRate * 100 | number: '1.1-2' }}%</td>
|
|
|
<td>
|
|
|
@@ -191,6 +230,12 @@ import { NzMessageService } from 'ng-zorro-antd/message';
|
|
|
font-size: 12px;
|
|
|
}
|
|
|
|
|
|
+ .chart-hover-tip {
|
|
|
+ margin-top: 6px;
|
|
|
+ color: #262626;
|
|
|
+ font-size: 12px;
|
|
|
+ }
|
|
|
+
|
|
|
.tab-subtitle {
|
|
|
margin-bottom: 10px;
|
|
|
color: #595959;
|
|
|
@@ -201,9 +246,9 @@ import { NzMessageService } from 'ng-zorro-antd/message';
|
|
|
width: 52px;
|
|
|
height: 52px;
|
|
|
border-radius: 4px;
|
|
|
- background-size: cover;
|
|
|
- background-position: center;
|
|
|
+ object-fit: cover;
|
|
|
border: 1px solid #f0f0f0;
|
|
|
+ cursor: zoom-in;
|
|
|
}
|
|
|
|
|
|
.empty-tip {
|
|
|
@@ -228,10 +273,19 @@ export class DashboardComponent implements OnInit {
|
|
|
dauMax = 0;
|
|
|
dauTrendStartDate = '';
|
|
|
dauTrendEndDate = '';
|
|
|
+ hoveredDauIndex = -1;
|
|
|
+ hoveredDauX = 0;
|
|
|
+ hoveredDauY = 0;
|
|
|
+ hoveredDauDate = '';
|
|
|
+ hoveredDauValue = 0;
|
|
|
|
|
|
artworkTabs: NewArtworkTab[] = [];
|
|
|
activeArtworkTabIndex = 0;
|
|
|
|
|
|
+ private readonly chartWidth = 600;
|
|
|
+ private readonly chartHeight = 180;
|
|
|
+ private readonly chartPadding = 16;
|
|
|
+
|
|
|
constructor(
|
|
|
private message: NzMessageService,
|
|
|
private dashboardService: DashboardService,
|
|
|
@@ -273,6 +327,48 @@ export class DashboardComponent implements OnInit {
|
|
|
.join(' ');
|
|
|
}
|
|
|
|
|
|
+ private getDauY(value: number): number {
|
|
|
+ const minVal = this.dauMin;
|
|
|
+ const maxVal = this.dauMax;
|
|
|
+ const span = Math.max(1, maxVal - minVal);
|
|
|
+ return this.chartHeight - this.chartPadding - ((value - minVal) / span) * (this.chartHeight - this.chartPadding * 2);
|
|
|
+ }
|
|
|
+
|
|
|
+ onChartMouseMove(event: MouseEvent): void {
|
|
|
+ if (this.dauTrendData.length === 0) return;
|
|
|
+
|
|
|
+ const rect = (event.target as SVGRectElement).getBoundingClientRect();
|
|
|
+ const localX = ((event.clientX - rect.left) / rect.width) * this.chartWidth;
|
|
|
+
|
|
|
+ let index = 0;
|
|
|
+ if (this.dauTrendData.length > 1) {
|
|
|
+ const stepX = (this.chartWidth - this.chartPadding * 2) / (this.dauTrendData.length - 1);
|
|
|
+ index = Math.round((localX - this.chartPadding) / stepX);
|
|
|
+ index = Math.max(0, Math.min(this.dauTrendData.length - 1, index));
|
|
|
+ this.hoveredDauX = this.chartPadding + index * stepX;
|
|
|
+ } else {
|
|
|
+ this.hoveredDauX = this.chartWidth / 2;
|
|
|
+ }
|
|
|
+
|
|
|
+ this.hoveredDauIndex = index;
|
|
|
+ this.hoveredDauDate = this.dauTrendLabels[index] || '';
|
|
|
+ this.hoveredDauValue = this.dauTrendData[index] || 0;
|
|
|
+ this.hoveredDauY = this.getDauY(this.hoveredDauValue);
|
|
|
+ }
|
|
|
+
|
|
|
+ onChartMouseLeave(): void {
|
|
|
+ this.hoveredDauIndex = -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ getArtworkDetailUrl(resId: string): string {
|
|
|
+ return `https://color2.jccytech.cn/app/zh/pages/detail/${resId}`;
|
|
|
+ }
|
|
|
+
|
|
|
+ openArtworkImage(imageUrl: string): void {
|
|
|
+ if (!imageUrl) return;
|
|
|
+ window.open(imageUrl, '_blank', 'noopener,noreferrer');
|
|
|
+ }
|
|
|
+
|
|
|
private applyDauData(dau: { today: number; trend: Array<{ date: string; dau: number }> }): void {
|
|
|
this.activeUsersToday = dau.today;
|
|
|
this.dauTrendLabels = dau.trend.map((item) => item.date);
|