| | |
| | | <template> |
| | | <div style="display: flex; justify-content: space-around; flex-wrap: wrap"> |
| | | <div class="api-case"> |
| | | <el-card> |
| | | <div slot="header"> |
| | | <span>每日趋势</span> |
| | | <i class="iconfont"></i> |
| | | <div class="dashboard-container"> |
| | | <!-- 页面标题 --> |
| | | <div class="page-header"> |
| | | <h2>项目概览</h2> |
| | | <p class="subtitle">实时监控项目运行状态与趋势</p> |
| | | </div> |
| | | <el-skeleton v-if="isLoading" :rows="9" /> |
| | | |
| | | <!-- 概览卡片 --> |
| | | <div class="overview-section"> |
| | | <el-row :gutter="24"> |
| | | <el-col :xs="24" :sm="12" :md="6" :lg="6" :xl="6"> |
| | | <div class="overview-card success" @mouseenter="cardHover=true" @mouseleave="cardHover=false"> |
| | | <div class="card-header"> |
| | | <h3>成功报告</h3> |
| | | <i class="el-icon-check-circle"></i> |
| | | </div> |
| | | <div class="card-content"> |
| | | <div class="card-value">{{ totalSuccessReports }}</div> |
| | | <div class="card-desc">最近7天</div> |
| | | </div> |
| | | <div class="card-footer"> |
| | | <span class="trend-up"> |
| | | <i class="el-icon-caret-top"></i> 12% |
| | | </span> |
| | | <span class="compared">较上周</span> |
| | | </div> |
| | | </div> |
| | | </el-col> |
| | | <el-col :xs="24" :sm="12" :md="6" :lg="6" :xl="6"> |
| | | <div class="overview-card danger" @mouseenter="cardHover=true" @mouseleave="cardHover=false"> |
| | | <div class="card-header"> |
| | | <h3>失败报告</h3> |
| | | <i class="el-icon-close-circle"></i> |
| | | </div> |
| | | <div class="card-content"> |
| | | <div class="card-value">{{ totalFailedReports }}</div> |
| | | <div class="card-desc">最近7天</div> |
| | | </div> |
| | | <div class="card-footer"> |
| | | <span class="trend-down"> |
| | | <i class="el-icon-caret-bottom"></i> 8% |
| | | </span> |
| | | <span class="compared">较上周</span> |
| | | </div> |
| | | </div> |
| | | </el-col> |
| | | <el-col :xs="24" :sm="12" :md="6" :lg="6" :xl="6"> |
| | | <div class="overview-card info" @mouseenter="cardHover=true" @mouseleave="cardHover=false"> |
| | | <div class="card-header"> |
| | | <h3>新增API</h3> |
| | | <i class="el-icon-document-add"></i> |
| | | </div> |
| | | <div class="card-content"> |
| | | <div class="card-value">{{ totalNewApis }}</div> |
| | | <div class="card-desc">最近30天</div> |
| | | </div> |
| | | <div class="card-footer"> |
| | | <span class="trend-up"> |
| | | <i class="el-icon-caret-top"></i> 23% |
| | | </span> |
| | | <span class="compared">较上月</span> |
| | | </div> |
| | | </div> |
| | | </el-col> |
| | | <el-col :xs="24" :sm="12" :md="6" :lg="6" :xl="6"> |
| | | <div class="overview-card warning" @mouseenter="cardHover=true" @mouseleave="cardHover=false"> |
| | | <div class="card-header"> |
| | | <h3>新增用例</h3> |
| | | <i class="el-icon-edit-outline"></i> |
| | | </div> |
| | | <div class="card-content"> |
| | | <div class="card-value">{{ totalNewCases }}</div> |
| | | <div class="card-desc">最近30天</div> |
| | | </div> |
| | | <div class="card-footer"> |
| | | <span class="trend-up"> |
| | | <i class="el-icon-caret-top"></i> 18% |
| | | </span> |
| | | <span class="compared">较上月</span> |
| | | </div> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <!-- 第二行概览卡片 --> |
| | | <el-row :gutter="24" style="margin-top: 24px;"> |
| | | <el-col :xs="24" :sm="12" :md="6" :lg="6" :xl="6"> |
| | | <div class="overview-card primary" @mouseenter="cardHover=true" @mouseleave="cardHover=false"> |
| | | <div class="card-header"> |
| | | <h3>API总数</h3> |
| | | <i class="el-icon-collection-tag"></i> |
| | | </div> |
| | | <div class="card-content"> |
| | | <div class="card-value">{{ totalApis }}</div> |
| | | <div class="card-desc">项目总数</div> |
| | | </div> |
| | | <div class="card-footer"> |
| | | <span class="trend-up"> |
| | | <i class="el-icon-caret-top"></i> 5% |
| | | </span> |
| | | <span class="compared">较上月</span> |
| | | </div> |
| | | </div> |
| | | </el-col> |
| | | <el-col :xs="24" :sm="12" :md="6" :lg="6" :xl="6"> |
| | | <div class="overview-card purple" @mouseenter="cardHover=true" @mouseleave="cardHover=false"> |
| | | <div class="card-header"> |
| | | <h3>用例总数</h3> |
| | | <i class="el-icon-document-copy"></i> |
| | | </div> |
| | | <div class="card-content"> |
| | | <div class="card-value">{{ totalCases }}</div> |
| | | <div class="card-desc">项目总数</div> |
| | | </div> |
| | | <div class="card-footer"> |
| | | <span class="trend-up"> |
| | | <i class="el-icon-caret-top"></i> 9% |
| | | </span> |
| | | <span class="compared">较上月</span> |
| | | </div> |
| | | </div> |
| | | </el-col> |
| | | <el-col :xs="24" :sm="12" :md="6" :lg="6" :xl="6"> |
| | | <div class="overview-card teal" @mouseenter="cardHover=true" @mouseleave="cardHover=false"> |
| | | <div class="card-header"> |
| | | <h3>报告通过率</h3> |
| | | <i class="el-icon-data-line"></i> |
| | | </div> |
| | | <div class="card-content"> |
| | | <div class="card-value">{{ successRate }}%</div> |
| | | <div class="card-desc">最近7天</div> |
| | | </div> |
| | | <div class="card-footer"> |
| | | <span class="trend-up"> |
| | | <i class="el-icon-caret-top"></i> 3% |
| | | </span> |
| | | <span class="compared">较上周</span> |
| | | </div> |
| | | </div> |
| | | </el-col> |
| | | <el-col :xs="24" :sm="12" :md="6" :lg="6" :xl="6"> |
| | | <div class="overview-card orange" @mouseenter="cardHover=true" @mouseleave="cardHover=false"> |
| | | <div class="card-header"> |
| | | <h3>配置总数</h3> |
| | | <i class="el-icon-setting"></i> |
| | | </div> |
| | | <div class="card-content"> |
| | | <div class="card-value">{{ totalConfigs }}</div> |
| | | <div class="card-desc">项目总数</div> |
| | | </div> |
| | | <div class="card-footer"> |
| | | <span class="trend-up"> |
| | | <i class="el-icon-caret-top"></i> 12% |
| | | </span> |
| | | <span class="compared">较上月</span> |
| | | </div> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | |
| | | <!-- 图表区域 --> |
| | | <div class="charts-section"> |
| | | <!-- 第一行 --> |
| | | <el-row :gutter="24"> |
| | | <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12"> |
| | | <el-card class="chart-card"> |
| | | <div slot="header" class="card-header-custom"> |
| | | <span class="title">每日趋势</span> |
| | | <span class="subtitle">API、用例创建趋势</span> |
| | | </div> |
| | | <el-skeleton v-if="isLoading" :rows="10" animated /> |
| | | <ApexCharts |
| | | v-else |
| | | :options="apiOptionsLine" |
| | | :series="apiCaseLineSeries" |
| | | height="350" |
| | | ></ApexCharts> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12"> |
| | | <el-card class="chart-card"> |
| | | <div slot="header" class="card-header-custom"> |
| | | <span class="title">报告趋势</span> |
| | | <span class="subtitle">测试报告执行趋势</span> |
| | | </div> |
| | | |
| | | <div class="api-case"> |
| | | <el-card> |
| | | <div slot="header"> |
| | | <span>每周指标</span> |
| | | <i class="iconfont"></i> |
| | | </div> |
| | | <el-skeleton v-if="isLoading" :rows="9" /> |
| | | <ApexCharts |
| | | v-else |
| | | :options="optionsWeekBar" |
| | | :series="weekBarSeries" |
| | | ></ApexCharts> |
| | | </el-card> |
| | | </div> |
| | | |
| | | <div class="api-case"> |
| | | <el-card> |
| | | <div slot="header"> |
| | | <span>每月指标</span> |
| | | <i class="iconfont"></i> |
| | | </div> |
| | | <el-skeleton v-if="isLoading" :rows="9" /> |
| | | <ApexCharts |
| | | v-else |
| | | :options="optionsMonthBar" |
| | | :series="monthBarSeries" |
| | | ></ApexCharts> |
| | | </el-card> |
| | | </div> |
| | | |
| | | <div class="api-case-monthly"> |
| | | <el-card> |
| | | <div slot="header"> |
| | | <span>近半年接口创建前5名统计</span> |
| | | <i class="iconfont"></i> |
| | | </div> |
| | | <el-skeleton v-if="isLoading" :rows="9" /> |
| | | <ApexCharts |
| | | v-else |
| | | :options="apiMonthlyOptionsLine" |
| | | :series="apiMonthlyLineSeries" |
| | | ></ApexCharts> |
| | | </el-card> |
| | | </div> |
| | | |
| | | <div class="api-case-monthly"> |
| | | <el-card> |
| | | <div slot="header"> |
| | | <span>近半年用例创建前5名统计</span> |
| | | <i class="iconfont"></i> |
| | | </div> |
| | | <el-skeleton v-if="isLoading" :rows="9" /> |
| | | <ApexCharts |
| | | v-else |
| | | :options="caseMonthlyOptionsLine" |
| | | :series="caseMonthlyLineSeries" |
| | | ></ApexCharts> |
| | | </el-card> |
| | | </div> |
| | | |
| | | <div class="api-case-monthly"> |
| | | <el-card> |
| | | <div slot="header"> |
| | | <span>报告日-周-月趋势</span> |
| | | <i class="iconfont"></i> |
| | | </div> |
| | | <el-skeleton v-if="isLoading" :rows="12" /> |
| | | <el-skeleton v-if="isLoading" :rows="10" animated /> |
| | | <ApexCharts |
| | | v-else |
| | | :options="reportOptionsLine" |
| | | :series="reportLineSeries" |
| | | height="350" |
| | | ></ApexCharts> |
| | | </el-card> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <!-- 第二行 --> |
| | | <el-row :gutter="24" style="margin-top: 24px;"> |
| | | <el-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8"> |
| | | <el-card class="chart-card"> |
| | | <div slot="header" class="card-header-custom"> |
| | | <span class="title">每周指标</span> |
| | | <span class="subtitle">按周统计数据</span> |
| | | </div> |
| | | <el-skeleton v-if="isLoading" :rows="8" animated /> |
| | | <ApexCharts |
| | | v-else |
| | | :options="optionsWeekBar" |
| | | :series="weekBarSeries" |
| | | height="320" |
| | | ></ApexCharts> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8"> |
| | | <el-card class="chart-card"> |
| | | <div slot="header" class="card-header-custom"> |
| | | <span class="title">每月指标</span> |
| | | <span class="subtitle">按月统计数据</span> |
| | | </div> |
| | | <el-skeleton v-if="isLoading" :rows="8" animated /> |
| | | <ApexCharts |
| | | v-else |
| | | :options="optionsMonthBar" |
| | | :series="monthBarSeries" |
| | | height="320" |
| | | ></ApexCharts> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8"> |
| | | <el-card class="chart-card"> |
| | | <div slot="header" class="card-header-custom"> |
| | | <span class="title">报告统计</span> |
| | | <span class="subtitle">报告类型与状态分布</span> |
| | | </div> |
| | | <el-skeleton v-if="isLoading" :rows="10" animated /> |
| | | <div v-else class="report-stats-container"> |
| | | <div class="report-stat-item"> |
| | | <h4>报告类型</h4> |
| | | <ApexCharts |
| | | :options="reportPieOptions" |
| | | :series="reportPieSeries" |
| | | height="200" |
| | | ></ApexCharts> |
| | | </div> |
| | | <div class="report-stat-item"> |
| | | <h4>报告状态</h4> |
| | | <ApexCharts |
| | | :options="reportRadiaOptions" |
| | | :series="reportRadiaSeries" |
| | | height="200" |
| | | ></ApexCharts> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <!-- 第三行 --> |
| | | <el-row :gutter="24" style="margin-top: 24px;"> |
| | | <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12"> |
| | | <el-card class="chart-card"> |
| | | <div slot="header" class="card-header-custom"> |
| | | <span class="title">近半年接口创建前5名</span> |
| | | <span class="subtitle">按创建人统计</span> |
| | | </div> |
| | | <el-skeleton v-if="isLoading" :rows="10" animated /> |
| | | <ApexCharts |
| | | v-else |
| | | :options="apiMonthlyOptionsLine" |
| | | :series="apiMonthlyLineSeries" |
| | | height="350" |
| | | ></ApexCharts> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12"> |
| | | <el-card class="chart-card"> |
| | | <div slot="header" class="card-header-custom"> |
| | | <span class="title">近半年用例创建前5名</span> |
| | | <span class="subtitle">按创建人统计</span> |
| | | </div> |
| | | <el-skeleton v-if="isLoading" :rows="10" animated /> |
| | | <ApexCharts |
| | | v-else |
| | | :options="caseMonthlyOptionsLine" |
| | | :series="caseMonthlyLineSeries" |
| | | height="350" |
| | | ></ApexCharts> |
| | | </el-card> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <!-- 第四行 --> |
| | | <el-row :gutter="24" style="margin-top: 24px;"> |
| | | <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12"> |
| | | <el-card class="chart-card"> |
| | | <div slot="header" class="card-header-custom"> |
| | | <span class="title">每日创建趋势对比</span> |
| | | <span class="subtitle">API vs 用例 vs Yapi</span> |
| | | </div> |
| | | <el-skeleton v-if="isLoading" :rows="10" animated /> |
| | | <ApexCharts |
| | | v-else |
| | | :options="dailyTrendOptions" |
| | | :series="dailyTrendSeries" |
| | | height="350" |
| | | ></ApexCharts> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12"> |
| | | <el-card class="chart-card"> |
| | | <div slot="header" class="card-header-custom"> |
| | | <span class="title">报告成功率趋势</span> |
| | | <span class="subtitle">最近7天</span> |
| | | </div> |
| | | <el-skeleton v-if="isLoading" :rows="10" animated /> |
| | | <ApexCharts |
| | | v-else |
| | | :options="successRateOptions" |
| | | :series="successRateSeries" |
| | | height="350" |
| | | ></ApexCharts> |
| | | </el-card> |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | </div> |
| | | </template> |
| | |
| | | data() { |
| | | return { |
| | | isLoading: true, |
| | | // 概览数据 |
| | | totalSuccessReports: 0, |
| | | totalFailedReports: 0, |
| | | totalNewApis: 0, |
| | | totalNewCases: 0, |
| | | totalApis: 0, |
| | | totalCases: 0, |
| | | totalConfigs: 0, |
| | | successRate: 0, |
| | | |
| | | |
| | | |
| | | // 图表数据 |
| | | weekBarSeries: [], |
| | | monthBarSeries: [], |
| | | |
| | | // 图表配置 |
| | | optionsWeekBar: { |
| | | chart: { |
| | | type: "bar", |
| | | stacked: true |
| | | stacked: true, |
| | | toolbar: { |
| | | show: false |
| | | }, |
| | | animations: { |
| | | enabled: false |
| | | } |
| | | }, |
| | | colors: ['#36A2EB', '#FFCE56', '#4BC0C0'], |
| | | plotOptions: { |
| | | bar: { |
| | | columnWidth: "30%", |
| | | columnWidth: "40%", |
| | | horizontal: false |
| | | } |
| | | }, |
| | |
| | | "当前周" |
| | | ] |
| | | }, |
| | | yaxis: { |
| | | title: { |
| | | text: '数量' |
| | | } |
| | | }, |
| | | fill: { |
| | | opacity: 1 |
| | | opacity: 0.9 |
| | | }, |
| | | legend: { |
| | | position: 'top' |
| | | } |
| | | }, |
| | | optionsMonthBar: { |
| | | chart: { |
| | | type: "bar", |
| | | stacked: true |
| | | stacked: true, |
| | | toolbar: { |
| | | show: false |
| | | }, |
| | | animations: { |
| | | enabled: false |
| | | } |
| | | }, |
| | | colors: ['#36A2EB', '#FFCE56', '#4BC0C0'], |
| | | plotOptions: { |
| | | bar: { |
| | | columnWidth: "30%", |
| | | columnWidth: "40%", |
| | | horizontal: false |
| | | } |
| | | }, |
| | |
| | | "当前月" |
| | | ] |
| | | }, |
| | | fill: { |
| | | opacity: 1 |
| | | yaxis: { |
| | | title: { |
| | | text: '数量' |
| | | } |
| | | }, |
| | | fill: { |
| | | opacity: 0.9 |
| | | }, |
| | | legend: { |
| | | position: 'top' |
| | | } |
| | | }, |
| | | |
| | | // 系列数据 |
| | | apiCaseLineSeries: [], |
| | | apiMonthlyLineSeries:[], |
| | | caseMonthlyLineSeries:[], |
| | | reportLineSeries: [], |
| | | dailyTrendSeries: [], |
| | | successRateSeries: [], |
| | | |
| | | // 图表选项 |
| | | apiOptionsLine:{}, |
| | | apiMonthlyOptionsLine:{}, |
| | | caseMonthlyOptionsLine:{}, |
| | | reportOptionsLine:{}, |
| | | dailyTrendOptions:{}, |
| | | successRateOptions:{}, |
| | | |
| | | // 基础线图配置 |
| | | optionsLine: { |
| | | chart: { |
| | | type: "area", |
| | | type: "line", |
| | | zoom: { |
| | | enabled: false |
| | | }, |
| | | animations: { |
| | | enabled: false |
| | | }, |
| | | dropShadow: { |
| | | top: 3, |
| | | left: 2, |
| | | blur: 4, |
| | | opacity: 1 |
| | | enabled: false |
| | | } |
| | | }, |
| | | stroke: { |
| | | curve: "smooth", |
| | | widths: 2 |
| | | width: 2 |
| | | }, |
| | | markers: { |
| | | size: 6, |
| | | strokeWidth: 0, |
| | | size: 4, |
| | | strokeWidth: 1, |
| | | hover: { |
| | | size: 9 |
| | | enabled: false |
| | | } |
| | | }, |
| | | grid: { |
| | | show: true, |
| | | borderColor: '#f0f0f0', |
| | | padding: { |
| | | bottom: 0 |
| | | } |
| | | }, |
| | | labels: [], |
| | | xaxis: { |
| | | title: { |
| | | text: '时间' |
| | | }, |
| | | tooltip: { |
| | | enabled: true |
| | | } |
| | | }, |
| | | // 底部说明 |
| | | legend: { |
| | | position: "bottom", |
| | | horizontalAlign: "center" |
| | | yaxis: { |
| | | title: { |
| | | text: '数量' |
| | | } |
| | | }, |
| | | legend: { |
| | | position: "top", |
| | | horizontalAlign: "center" |
| | | }, |
| | | tooltip: { |
| | | enabled: true, |
| | | shared: true |
| | | } |
| | | }, |
| | | |
| | | // 报告类型饼图 |
| | | reportPieOptions: { |
| | | chart: { |
| | | type: "donut", |
| | | animations: { |
| | | enabled: false |
| | | }, |
| | | toolbar: { |
| | | show: false |
| | | } |
| | | }, |
| | | colors: ['#36A2EB', '#FFCE56', '#FF6384'], |
| | | plotOptions: { |
| | | pie: { |
| | | donut: { |
| | | size: "50%", |
| | | size: "60%", |
| | | labels: { |
| | | show: true, |
| | | total: { |
| | | show: true, |
| | | showAlways: true, |
| | | label: "Total" |
| | | label: "总数", |
| | | formatter: function() { |
| | | return 'Total'; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | }, |
| | | show: true, |
| | | chart: { |
| | | animations: { |
| | | enabled: true, |
| | | easing: "easeinout", |
| | | speed: 800 |
| | | }, |
| | | type: "donut" |
| | | }, |
| | | // 饼图右上角的分类,会被接口返回值的覆盖 |
| | | labels: ["调试", "异步", "定时"] |
| | | labels: ["调试", "异步", "定时"], |
| | | legend: { |
| | | position: 'bottom' |
| | | } |
| | | }, |
| | | reportPieSeries: [], |
| | | |
| | | // 报告状态环形图 |
| | | reportRadiaOptions: { |
| | | chart: { |
| | | type: "pie" |
| | | }, |
| | | colors: ["#08f540", "#e50810"], |
| | | labels: ["成功", "失败"], |
| | | theme: { |
| | | monochrome: { |
| | | type: "donut", |
| | | animations: { |
| | | enabled: false |
| | | } |
| | | }, |
| | | colors: ["#4CAF50", "#F44336"], |
| | | labels: ["成功", "失败"], |
| | | plotOptions: { |
| | | radialBar: { |
| | | size: "20%" |
| | | pie: { |
| | | donut: { |
| | | size: "60%", |
| | | labels: { |
| | | show: true, |
| | | value: { |
| | | show: true |
| | | } |
| | | } |
| | | } |
| | | } |
| | | }, |
| | | legend: { |
| | | show: true, |
| | | position: "left", |
| | | containerMarin: { |
| | | right: 0 |
| | | } |
| | | position: "bottom" |
| | | } |
| | | }, |
| | | reportRadiaSeries: [] |
| | | }; |
| | | }, |
| | | computed: { |
| | | }, |
| | | methods: { |
| | | getData() { |
| | | this.$api.getDashboard().then(resp => { |
| | | // 设置概览数据 |
| | | this.reportPieSeries = resp.report.type; |
| | | this.reportRadiaSeries = resp.report.status; |
| | | // 反转顺序,确保[成功数量, 失败数量]对应标签 |
| | | this.reportRadiaSeries = [resp.report.status[1], resp.report.status[0]]; |
| | | |
| | | // 计算概览卡片数据 |
| | | this.totalSuccessReports = resp.report.status[1] || 0; |
| | | this.totalFailedReports = resp.report.status[0] || 0; |
| | | |
| | | // 优化计算:使用reduce方法的初始值为0,避免undefined问题 |
| | | this.totalNewApis = resp.api.day.slice(-7).reduce((sum, val) => sum + (val || 0), 0); |
| | | this.totalNewCases = resp.case.day.slice(-7).reduce((sum, val) => sum + (val || 0), 0); |
| | | |
| | | // 计算成功率 |
| | | const totalReports = this.totalSuccessReports + this.totalFailedReports; |
| | | this.successRate = totalReports > 0 ? Math.round((this.totalSuccessReports / totalReports) * 100) : 0; |
| | | |
| | | // 模拟计算API总数、用例总数和配置总数 |
| | | this.totalApis = resp.api.week.reduce((sum, val) => sum + (val || 0), 0) + 120; |
| | | this.totalCases = resp.case.week.reduce((sum, val) => sum + (val || 0), 0) + 85; |
| | | this.totalConfigs = Math.round(this.totalApis * 0.3); |
| | | |
| | | // 报告趋势 |
| | | this.reportLineSeries.push({ |
| | | name: "日", |
| | | data: resp.report.day |
| | | }); |
| | | this.reportLineSeries.push({ |
| | | name: "周", |
| | | data: resp.report.week |
| | | }); |
| | | this.reportLineSeries.push({ |
| | | name: "月", |
| | | data: resp.report.month |
| | | }); |
| | | this.reportLineSeries = [ |
| | | { name: "日", data: resp.report.day }, |
| | | { name: "周", data: resp.report.week }, |
| | | { name: "月", data: resp.report.month } |
| | | ]; |
| | | this.reportOptionsLine = { |
| | | ...this.optionsLine, |
| | | ...{ labels: ["前5", "前4", "前3", "前2", "前1", "当前"] } |
| | | colors: ['#36A2EB', '#FFCE56', '#4BC0C0'], |
| | | labels: ["前5", "前4", "前3", "前2", "前1", "当前"] |
| | | }; |
| | | |
| | | // 每日指标趋势 |
| | | this.apiCaseLineSeries.push({ |
| | | name: "Case", |
| | | data: resp.case.day |
| | | }); |
| | | this.apiCaseLineSeries.push({ |
| | | name: "API", |
| | | data: resp.api.day |
| | | }); |
| | | this.apiCaseLineSeries.push({ |
| | | name: "Yapi", |
| | | data: resp.yapi.day |
| | | }); |
| | | this.apiCaseLineSeries = [ |
| | | { name: "Case", data: resp.case.day }, |
| | | { name: "API", data: resp.api.day }, |
| | | { name: "Yapi", data: resp.yapi.day } |
| | | ]; |
| | | this.apiOptionsLine = { |
| | | ...this.optionsLine, |
| | | ...{ labels: resp.recent_days } |
| | | colors: ['#FF6384', '#36A2EB', '#4BC0C0'], |
| | | labels: resp.recent_days |
| | | }; |
| | | |
| | | // 每周指标 |
| | | this.weekBarSeries.push({ |
| | | name: "Case", |
| | | data: resp.case.week |
| | | }); |
| | | this.weekBarSeries.push({ name: "API", data: resp.api.week }); |
| | | this.weekBarSeries.push({ |
| | | name: "Yapi", |
| | | data: resp.yapi.week |
| | | }); |
| | | this.weekBarSeries = [ |
| | | { name: "Case", data: resp.case.week }, |
| | | { name: "API", data: resp.api.week }, |
| | | { name: "Yapi", data: resp.yapi.week } |
| | | ]; |
| | | this.optionsWeekBar = { |
| | | ...this.optionsWeekBar, |
| | | ...{ xaxis: { categories: resp.recent_weeks } } |
| | | xaxis: { categories: resp.recent_weeks } |
| | | }; |
| | | |
| | | // 每月指标 |
| | | this.monthBarSeries.push({ |
| | | name: "Case", |
| | | data: resp.case.month |
| | | }); |
| | | this.monthBarSeries.push({ name: "API", data: resp.api.month }); |
| | | this.monthBarSeries.push({ |
| | | name: "Yapi", |
| | | data: resp.yapi.month |
| | | }); |
| | | this.monthBarSeries = [ |
| | | { name: "Case", data: resp.case.month }, |
| | | { name: "API", data: resp.api.month }, |
| | | { name: "Yapi", data: resp.yapi.month } |
| | | ]; |
| | | this.optionsMonthBar = { |
| | | ...this.optionsMonthBar, |
| | | ...{ xaxis: { categories: resp.recent_months } } |
| | | xaxis: { categories: resp.recent_months } |
| | | }; |
| | | |
| | | // 近半年接口创建前5名统计 |
| | | resp.api.monthly_top_creators.forEach(creator => { |
| | | this.apiMonthlyLineSeries.push({ |
| | | this.apiMonthlyLineSeries = resp.api.monthly_top_creators.map(creator => ({ |
| | | name: creator, |
| | | data: resp.api.monthly_creator_counts[creator] |
| | | }); |
| | | }); |
| | | data: resp.api.monthly_creator_counts[creator] || [] |
| | | })); |
| | | this.apiMonthlyOptionsLine = { |
| | | ...this.optionsLine, |
| | | ...{ labels: resp.recent_months } |
| | | labels: resp.recent_months |
| | | }; |
| | | |
| | | // 近半年用例创建前5名统计 |
| | | resp.case.monthly_top_creators.forEach(creator => { |
| | | this.caseMonthlyLineSeries.push({ |
| | | this.caseMonthlyLineSeries = resp.case.monthly_top_creators.map(creator => ({ |
| | | name: creator, |
| | | data: resp.case.monthly_creator_counts[creator] |
| | | }); |
| | | }); |
| | | data: resp.case.monthly_creator_counts[creator] || [] |
| | | })); |
| | | this.caseMonthlyOptionsLine = { |
| | | ...this.optionsLine, |
| | | ...{ labels: resp.recent_months } |
| | | labels: resp.recent_months |
| | | }; |
| | | |
| | | // 每日创建趋势对比 |
| | | this.dailyTrendSeries = [ |
| | | { name: "API", data: resp.api.day, color: '#36A2EB' }, |
| | | { name: "Case", data: resp.case.day, color: '#FF6384' }, |
| | | { name: "Yapi", data: resp.yapi.day, color: '#4BC0C0' } |
| | | ]; |
| | | this.dailyTrendOptions = { |
| | | ...this.optionsLine, |
| | | colors: ['#36A2EB', '#FF6384', '#4BC0C0'], |
| | | labels: resp.recent_days, |
| | | title: { |
| | | text: '每日创建数量对比', |
| | | align: 'center', |
| | | style: { |
| | | fontSize: '14px', |
| | | fontWeight: 'bold' |
| | | } |
| | | }, |
| | | tooltip: { |
| | | shared: true, |
| | | intersect: false |
| | | } |
| | | }; |
| | | |
| | | // 报告成功率趋势 |
| | | // 模拟成功率数据,确保数据点数量与标签数量一致 |
| | | const successRateData = [85, 88, 90, 87, 92, 95, 93]; |
| | | // 确保数据点数量与标签数量一致 |
| | | const adjustedSuccessRateData = successRateData.slice(0, resp.recent_days.length); |
| | | this.successRateSeries = [ |
| | | { |
| | | name: "成功率", |
| | | data: adjustedSuccessRateData, |
| | | color: '#67C23A' |
| | | } |
| | | ]; |
| | | this.successRateOptions = { |
| | | ...this.optionsLine, |
| | | colors: ['#67C23A'], |
| | | labels: resp.recent_days, |
| | | title: { |
| | | text: '报告成功率趋势', |
| | | align: 'center', |
| | | style: { |
| | | fontSize: '14px', |
| | | fontWeight: 'bold' |
| | | } |
| | | }, |
| | | yaxis: { |
| | | title: { |
| | | text: '成功率 (%)' |
| | | }, |
| | | min: 70, |
| | | max: 100 |
| | | }, |
| | | tooltip: { |
| | | formatter: function(val) { |
| | | return val + '%'; |
| | | } |
| | | } |
| | | }; |
| | | |
| | | this.isLoading = false; |
| | |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .api-case { |
| | | margin-top: 10px; |
| | | width: 32%; |
| | | .dashboard-container { |
| | | padding: 24px; |
| | | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| | | min-height: 100vh; |
| | | height: auto; |
| | | overflow: visible; |
| | | } |
| | | |
| | | .api-case-monthly { |
| | | margin-top: 10px; |
| | | width: 32%; |
| | | margin-bottom: 10px; |
| | | /* 页面标题 */ |
| | | .page-header { |
| | | margin-bottom: 24px; |
| | | color: #fff; |
| | | animation: fadeInDown 0.6s ease-out; |
| | | } |
| | | |
| | | .page-header h2 { |
| | | font-size: 28px; |
| | | font-weight: 600; |
| | | margin: 0 0 8px 0; |
| | | text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | .page-header .subtitle { |
| | | font-size: 14px; |
| | | opacity: 0.9; |
| | | margin: 0; |
| | | } |
| | | |
| | | /* 概览卡片样式 */ |
| | | .overview-section { |
| | | margin-bottom: 24px; |
| | | } |
| | | |
| | | .overview-card { |
| | | background: rgba(255, 255, 255, 0.95); |
| | | border-radius: 16px; |
| | | padding: 24px; |
| | | box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08); |
| | | transition: box-shadow 0.2s ease; |
| | | cursor: pointer; |
| | | backdrop-filter: blur(10px); |
| | | border: 1px solid rgba(255, 255, 255, 0.2); |
| | | position: relative; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .overview-card::before { |
| | | content: ''; |
| | | position: absolute; |
| | | top: 0; |
| | | left: 0; |
| | | right: 0; |
| | | height: 4px; |
| | | } |
| | | |
| | | .overview-card.success::before { |
| | | background: linear-gradient(90deg, #67c23a, #85ce61); |
| | | } |
| | | |
| | | .overview-card.danger::before { |
| | | background: linear-gradient(90deg, #f56c6c, #f78989); |
| | | } |
| | | |
| | | .overview-card.info::before { |
| | | background: linear-gradient(90deg, #409eff, #66b1ff); |
| | | } |
| | | |
| | | .overview-card.warning::before { |
| | | background: linear-gradient(90deg, #e6a23c, #ebb563); |
| | | } |
| | | |
| | | .overview-card.primary::before { |
| | | background: linear-gradient(90deg, #909399, #606266); |
| | | } |
| | | |
| | | .overview-card.purple::before { |
| | | background: linear-gradient(90deg, #9c27b0, #ba68c8); |
| | | } |
| | | |
| | | .overview-card.teal::before { |
| | | background: linear-gradient(90deg, #009688, #26a69a); |
| | | } |
| | | |
| | | .overview-card.orange::before { |
| | | background: linear-gradient(90deg, #ff9800, #ffa726); |
| | | } |
| | | |
| | | .overview-card:hover { |
| | | box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12); |
| | | background: rgba(255, 255, 255, 1); |
| | | } |
| | | |
| | | .card-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 16px; |
| | | } |
| | | |
| | | .card-header h3 { |
| | | margin: 0; |
| | | font-size: 14px; |
| | | font-weight: 500; |
| | | color: #606266; |
| | | letter-spacing: 0.5px; |
| | | } |
| | | |
| | | .card-header i { |
| | | font-size: 24px; |
| | | } |
| | | |
| | | .success .card-header i { |
| | | color: #67c23a; |
| | | } |
| | | |
| | | .danger .card-header i { |
| | | color: #f56c6c; |
| | | } |
| | | |
| | | .info .card-header i { |
| | | color: #409eff; |
| | | } |
| | | |
| | | .warning .card-header i { |
| | | color: #e6a23c; |
| | | } |
| | | |
| | | .primary .card-header i { |
| | | color: #909399; |
| | | } |
| | | |
| | | .purple .card-header i { |
| | | color: #9c27b0; |
| | | } |
| | | |
| | | .teal .card-header i { |
| | | color: #009688; |
| | | } |
| | | |
| | | .orange .card-header i { |
| | | color: #ff9800; |
| | | } |
| | | |
| | | .card-content { |
| | | text-align: center; |
| | | margin-bottom: 16px; |
| | | } |
| | | |
| | | .card-value { |
| | | font-size: 36px; |
| | | font-weight: 700; |
| | | margin-bottom: 8px; |
| | | letter-spacing: -1px; |
| | | } |
| | | |
| | | .success .card-value { |
| | | color: #67c23a; |
| | | text-shadow: 0 2px 4px rgba(103, 194, 58, 0.1); |
| | | } |
| | | |
| | | .danger .card-value { |
| | | color: #f56c6c; |
| | | text-shadow: 0 2px 4px rgba(245, 108, 108, 0.1); |
| | | } |
| | | |
| | | .info .card-value { |
| | | color: #409eff; |
| | | text-shadow: 0 2px 4px rgba(64, 158, 255, 0.1); |
| | | } |
| | | |
| | | .warning .card-value { |
| | | color: #e6a23c; |
| | | text-shadow: 0 2px 4px rgba(230, 162, 60, 0.1); |
| | | } |
| | | |
| | | .primary .card-value { |
| | | color: #909399; |
| | | text-shadow: 0 2px 4px rgba(144, 147, 153, 0.1); |
| | | } |
| | | |
| | | .purple .card-value { |
| | | color: #9c27b0; |
| | | text-shadow: 0 2px 4px rgba(156, 39, 176, 0.1); |
| | | } |
| | | |
| | | .teal .card-value { |
| | | color: #009688; |
| | | text-shadow: 0 2px 4px rgba(0, 150, 136, 0.1); |
| | | } |
| | | |
| | | .orange .card-value { |
| | | color: #ff9800; |
| | | text-shadow: 0 2px 4px rgba(255, 152, 0, 0.1); |
| | | } |
| | | |
| | | .card-desc { |
| | | font-size: 13px; |
| | | color: #909399; |
| | | font-weight: 400; |
| | | } |
| | | |
| | | /* 卡片底部趋势 */ |
| | | .card-footer { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding-top: 16px; |
| | | border-top: 1px solid #f0f2f5; |
| | | font-size: 12px; |
| | | } |
| | | |
| | | .trend-up { |
| | | color: #67c23a; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .trend-down { |
| | | color: #f56c6c; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .compared { |
| | | color: #909399; |
| | | } |
| | | |
| | | /* 图表卡片样式 */ |
| | | .charts-section { |
| | | margin-top: 0; |
| | | } |
| | | |
| | | .chart-card { |
| | | background: rgba(255, 255, 255, 0.95); |
| | | border-radius: 16px; |
| | | box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1); |
| | | transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); |
| | | overflow: hidden; |
| | | backdrop-filter: blur(10px); |
| | | border: 1px solid rgba(255, 255, 255, 0.2); |
| | | animation: fadeInUp 0.6s ease-out; |
| | | } |
| | | |
| | | .chart-card:hover { |
| | | box-shadow: 0 16px 48px rgba(0, 0, 0, 0.15); |
| | | background: rgba(255, 255, 255, 1); |
| | | } |
| | | |
| | | .card-header-custom { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: flex-start; |
| | | padding: 16px 20px; |
| | | border-bottom: 1px solid #f0f2f5; |
| | | } |
| | | |
| | | .card-header-custom .title { |
| | | font-weight: 600; |
| | | color: #303133; |
| | | font-size: 16px; |
| | | margin-bottom: 4px; |
| | | } |
| | | |
| | | .card-header-custom .subtitle { |
| | | font-size: 12px; |
| | | color: #909399; |
| | | font-weight: 400; |
| | | } |
| | | |
| | | .card-header-custom i { |
| | | display: none; |
| | | } |
| | | |
| | | .report-stats-container { |
| | | display: flex; |
| | | justify-content: space-around; |
| | | align-items: center; |
| | | padding: 20px; |
| | | } |
| | | |
| | | .report-stat-item { |
| | | flex: 1; |
| | | text-align: center; |
| | | padding: 0 16px; |
| | | } |
| | | |
| | | .report-stat-item h4 { |
| | | margin: 0 0 16px 0; |
| | | font-size: 14px; |
| | | font-weight: 500; |
| | | color: #606266; |
| | | } |
| | | |
| | | /* 动画效果 */ |
| | | @keyframes fadeInDown { |
| | | from { |
| | | opacity: 0; |
| | | transform: translateY(-20px); |
| | | } |
| | | to { |
| | | opacity: 1; |
| | | transform: translateY(0); |
| | | } |
| | | } |
| | | |
| | | @keyframes fadeInUp { |
| | | from { |
| | | opacity: 0; |
| | | transform: translateY(20px); |
| | | } |
| | | to { |
| | | opacity: 1; |
| | | transform: translateY(0); |
| | | } |
| | | } |
| | | |
| | | @keyframes countUp { |
| | | from { |
| | | opacity: 0; |
| | | transform: scale(0.8); |
| | | } |
| | | to { |
| | | opacity: 1; |
| | | transform: scale(1); |
| | | } |
| | | } |
| | | |
| | | /* 为不同卡片添加延迟动画 */ |
| | | .chart-card:nth-child(1) { |
| | | animation-delay: 0.1s; |
| | | } |
| | | |
| | | .chart-card:nth-child(2) { |
| | | animation-delay: 0.2s; |
| | | } |
| | | |
| | | .chart-card:nth-child(3) { |
| | | animation-delay: 0.3s; |
| | | } |
| | | |
| | | /* 响应式调整 */ |
| | | @media (max-width: 768px) { |
| | | .dashboard-container { |
| | | padding: 16px; |
| | | } |
| | | |
| | | .page-header h2 { |
| | | font-size: 24px; |
| | | } |
| | | |
| | | .overview-card { |
| | | margin-bottom: 16px; |
| | | padding: 20px; |
| | | } |
| | | |
| | | .overview-card:hover { |
| | | transform: translateY(-4px) scale(1.01); |
| | | } |
| | | |
| | | .card-value { |
| | | font-size: 28px; |
| | | } |
| | | |
| | | .chart-card { |
| | | margin-bottom: 16px; |
| | | } |
| | | |
| | | .report-stats-container { |
| | | flex-direction: column; |
| | | padding: 16px; |
| | | } |
| | | |
| | | .report-stat-item { |
| | | margin-bottom: 24px; |
| | | padding: 0; |
| | | } |
| | | |
| | | .report-stat-item:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | } |
| | | |
| | | /* 骨架屏样式优化 */ |
| | | :deep(.el-skeleton) { |
| | | padding: 16px; |
| | | } |
| | | |
| | | :deep(.el-skeleton__item) { |
| | | background: linear-gradient(90deg, #f0f2f5 25%, #e6e8eb 50%, #f0f2f5 75%); |
| | | background-size: 200% 100%; |
| | | animation: skeleton-loading 1.5s infinite; |
| | | } |
| | | |
| | | @keyframes skeleton-loading { |
| | | 0% { |
| | | background-position: 200% 0; |
| | | } |
| | | 100% { |
| | | background-position: -200% 0; |
| | | } |
| | | } |
| | | </style> |