Files
everything-is-suitable/everything-is-suitable-uniapp/e2e/performance/monitor.config.ts
T
张翔 08ea5fbe98 feat(admin): 添加用户管理相关文件
添加用户管理视图、API和状态管理文件
2026-03-28 14:37:29 +08:00

270 lines
8.3 KiB
TypeScript

export interface PerformanceThresholds {
pageLoadTime: number;
firstContentfulPaint: number;
largestContentfulPaint: number;
timeToInteractive: number;
cumulativeLayoutShift: number;
firstInputDelay: number;
fps: number;
frameTime: number;
animationDuration: number;
}
export const PERFORMANCE_THRESHOLDS: PerformanceThresholds = {
pageLoadTime: 2000,
firstContentfulPaint: 1000,
largestContentfulPaint: 1500,
timeToInteractive: 2000,
cumulativeLayoutShift: 0.1,
firstInputDelay: 100,
fps: 30,
frameTime: 33.33,
animationDuration: 500,
};
export interface PerformanceMetrics {
pageLoadTime: number;
firstContentfulPaint: number;
largestContentfulPaint: number;
timeToInteractive: number;
cumulativeLayoutShift: number;
firstInputDelay: number;
fps: number;
frameTime: number;
droppedFrames: number;
totalFrames: number;
animationDuration: number;
}
export interface PerformanceReport {
timestamp: string;
url: string;
platform: string;
metrics: PerformanceMetrics;
thresholds: PerformanceThresholds;
passed: boolean;
issues: PerformanceIssue[];
}
export interface PerformanceIssue {
type: 'page-load' | 'animation' | 'rendering';
metric: string;
actual: number;
expected: number;
severity: 'critical' | 'warning' | 'info';
message: string;
}
export class PerformanceMonitor {
private metrics: PerformanceMetrics;
private thresholds: PerformanceThresholds;
private issues: PerformanceIssue[];
constructor(thresholds?: Partial<PerformanceThresholds>) {
this.thresholds = { ...PERFORMANCE_THRESHOLDS, ...thresholds };
this.metrics = this.initializeMetrics();
this.issues = [];
}
private initializeMetrics(): PerformanceMetrics {
return {
pageLoadTime: 0,
firstContentfulPaint: 0,
largestContentfulPaint: 0,
timeToInteractive: 0,
cumulativeLayoutShift: 0,
firstInputDelay: 0,
fps: 0,
frameTime: 0,
droppedFrames: 0,
totalFrames: 0,
animationDuration: 0,
};
}
public setMetric(key: keyof PerformanceMetrics, value: number): void {
this.metrics[key] = value;
}
public getMetric(key: keyof PerformanceMetrics): number {
return this.metrics[key];
}
public getMetrics(): PerformanceMetrics {
return { ...this.metrics };
}
public checkThresholds(): void {
this.issues = [];
if (this.metrics.pageLoadTime > this.thresholds.pageLoadTime) {
this.issues.push({
type: 'page-load',
metric: 'pageLoadTime',
actual: this.metrics.pageLoadTime,
expected: this.thresholds.pageLoadTime,
severity: 'critical',
message: `页面加载时间 ${this.metrics.pageLoadTime}ms 超过阈值 ${this.thresholds.pageLoadTime}ms`,
});
}
if (this.metrics.firstContentfulPaint > this.thresholds.firstContentfulPaint) {
this.issues.push({
type: 'page-load',
metric: 'firstContentfulPaint',
actual: this.metrics.firstContentfulPaint,
expected: this.thresholds.firstContentfulPaint,
severity: 'warning',
message: `首次内容绘制 ${this.metrics.firstContentfulPaint}ms 超过阈值 ${this.thresholds.firstContentfulPaint}ms`,
});
}
if (this.metrics.largestContentfulPaint > this.thresholds.largestContentfulPaint) {
this.issues.push({
type: 'page-load',
metric: 'largestContentfulPaint',
actual: this.metrics.largestContentfulPaint,
expected: this.thresholds.largestContentfulPaint,
severity: 'warning',
message: `最大内容绘制 ${this.metrics.largestContentfulPaint}ms 超过阈值 ${this.thresholds.largestContentfulPaint}ms`,
});
}
if (this.metrics.timeToInteractive > this.thresholds.timeToInteractive) {
this.issues.push({
type: 'page-load',
metric: 'timeToInteractive',
actual: this.metrics.timeToInteractive,
expected: this.thresholds.timeToInteractive,
severity: 'warning',
message: `可交互时间 ${this.metrics.timeToInteractive}ms 超过阈值 ${this.thresholds.timeToInteractive}ms`,
});
}
if (this.metrics.cumulativeLayoutShift > this.thresholds.cumulativeLayoutShift) {
this.issues.push({
type: 'rendering',
metric: 'cumulativeLayoutShift',
actual: this.metrics.cumulativeLayoutShift,
expected: this.thresholds.cumulativeLayoutShift,
severity: 'warning',
message: `累积布局偏移 ${this.metrics.cumulativeLayoutShift} 超过阈值 ${this.thresholds.cumulativeLayoutShift}`,
});
}
if (this.metrics.firstInputDelay > this.thresholds.firstInputDelay) {
this.issues.push({
type: 'page-load',
metric: 'firstInputDelay',
actual: this.metrics.firstInputDelay,
expected: this.thresholds.firstInputDelay,
severity: 'warning',
message: `首次输入延迟 ${this.metrics.firstInputDelay}ms 超过阈值 ${this.thresholds.firstInputDelay}ms`,
});
}
if (this.metrics.fps < this.thresholds.fps) {
this.issues.push({
type: 'animation',
metric: 'fps',
actual: this.metrics.fps,
expected: this.thresholds.fps,
severity: 'critical',
message: `动画帧率 ${this.metrics.fps}fps 低于阈值 ${this.thresholds.fps}fps`,
});
}
if (this.metrics.frameTime > this.thresholds.frameTime) {
this.issues.push({
type: 'animation',
metric: 'frameTime',
actual: this.metrics.frameTime,
expected: this.thresholds.frameTime,
severity: 'warning',
message: `帧时间 ${this.metrics.frameTime}ms 超过阈值 ${this.thresholds.frameTime}ms`,
});
}
}
public getIssues(): PerformanceIssue[] {
return [...this.issues];
}
public hasCriticalIssues(): boolean {
return this.issues.some(issue => issue.severity === 'critical');
}
public hasWarnings(): boolean {
return this.issues.some(issue => issue.severity === 'warning');
}
public generateReport(url: string, platform: string): PerformanceReport {
this.checkThresholds();
return {
timestamp: new Date().toISOString(),
url,
platform,
metrics: this.getMetrics(),
thresholds: this.thresholds,
passed: !this.hasCriticalIssues(),
issues: this.getIssues(),
};
}
public reset(): void {
this.metrics = this.initializeMetrics();
this.issues = [];
}
}
export const createPerformanceMonitor = (thresholds?: Partial<PerformanceThresholds>): PerformanceMonitor => {
return new PerformanceMonitor(thresholds);
};
export const generatePerformanceReportSummary = (reports: PerformanceReport[]): string => {
const totalReports = reports.length;
const passedReports = reports.filter(report => report.passed).length;
const failedReports = totalReports - passedReports;
const totalIssues = reports.reduce((sum, report) => sum + report.issues.length, 0);
const criticalIssues = reports.reduce((sum, report) => sum + report.issues.filter(issue => issue.severity === 'critical').length, 0);
const warningIssues = reports.reduce((sum, report) => sum + report.issues.filter(issue => issue.severity === 'warning').length, 0);
const avgPageLoadTime = reports.reduce((sum, report) => sum + report.metrics.pageLoadTime, 0) / totalReports;
const avgFPS = reports.reduce((sum, report) => sum + report.metrics.fps, 0) / totalReports;
return `
性能测试报告摘要
================
测试时间: ${new Date().toISOString()}
测试数量: ${totalReports}
通过数量: ${passedReports}
失败数量: ${failedReports}
通过率: ${((passedReports / totalReports) * 100).toFixed(2)}%
问题统计
--------
总问题数: ${totalIssues}
严重问题: ${criticalIssues}
警告问题: ${warningIssues}
性能指标
--------
平均页面加载时间: ${avgPageLoadTime.toFixed(2)}ms
平均动画帧率: ${avgFPS.toFixed(2)}fps
详细信息
--------
${reports.map((report, index) => `
报告 ${index + 1}: ${report.url}
- 平台: ${report.platform}
- 状态: ${report.passed ? '通过' : '失败'}
- 页面加载时间: ${report.metrics.pageLoadTime}ms
- 动画帧率: ${report.metrics.fps}fps
- 问题数: ${report.issues.length}
${report.issues.length > 0 ? ` 问题详情:\n${report.issues.map(issue => ` - [${issue.severity.toUpperCase()}] ${issue.message}`).join('\n')}` : ''}
`).join('\n')}
`;
};