change
This commit is contained in:
@@ -0,0 +1,252 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
<script src="${base!}/assets/platform/plugins/echarts/echarts.min.js" nonce="${cspNonce!}"></script>
|
||||
<style>
|
||||
#sub-app-container-main-content { background: #f4f6f8; }
|
||||
.spread-statistics { width: 100%; }
|
||||
.spread-statistics .filter-card { margin-bottom: 12px; }
|
||||
.spread-statistics .metrics-card { margin-bottom: 18px; border: 0; border-radius: 12px; }
|
||||
.spread-statistics .metrics-card > .el-card__body { padding: 24px; }
|
||||
.spread-statistics .stat-grid { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 18px; }
|
||||
.spread-statistics .metric-tile { min-width: 0; min-height: 108px; padding: 20px; display: flex; align-items: center; gap: 16px; box-sizing: border-box; border: 1px solid #e8edf3; border-radius: 13px; background: #fff; box-shadow: 0 3px 14px rgba(15,23,42,.06); transition: transform .25s, box-shadow .25s; }
|
||||
.spread-statistics .metric-tile:hover { transform: translateY(-2px); box-shadow: 0 8px 22px rgba(15,23,42,.11); }
|
||||
.spread-statistics .metric-icon { width: 58px; height: 58px; flex: 0 0 58px; display: flex; align-items: center; justify-content: center; color: #fff; font-size: 27px; border-radius: 14px; }
|
||||
.spread-statistics .metric-icon.purple { background: linear-gradient(135deg,#667eea,#764ba2); }
|
||||
.spread-statistics .metric-icon.green { background: linear-gradient(135deg,#43e97b,#38f9d7); }
|
||||
.spread-statistics .metric-icon.orange { background: linear-gradient(135deg,#f7971e,#ffd200); }
|
||||
.spread-statistics .metric-icon.blue { background: linear-gradient(135deg,#4facfe,#00d7ef); }
|
||||
.spread-statistics .metric-icon.pink { background: linear-gradient(135deg,#f093fb,#f5576c); }
|
||||
.spread-statistics .metric-info { min-width: 0; }
|
||||
.spread-statistics .stat-label { margin-bottom: 6px; overflow: hidden; color: #8492a6; font-size: 15px; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.spread-statistics .stat-value { color: #17233d; font-size: 31px; font-weight: 700; line-height: 1; }
|
||||
.spread-statistics .chart-grid { display: grid; grid-template-columns: minmax(360px, 1fr) minmax(0, 2fr); gap: 14px; }
|
||||
.spread-statistics .chart-card { min-width: 0; border: 0; border-radius: 11px; }
|
||||
.spread-statistics .chart-card > .el-card__header { padding: 26px 26px 18px; font-size: 18px; border-bottom: 1px solid #e4e7ed; }
|
||||
.spread-statistics .chart-card > .el-card__body { padding: 8px 24px 18px; }
|
||||
.spread-statistics .chart-box { width: 100%; height: 410px; }
|
||||
.spread-statistics .top-table-card { margin-top: 14px; border: 0; border-radius: 11px; }
|
||||
.spread-statistics .top-table-card > .el-card__header { padding: 20px 24px; font-size: 17px; }
|
||||
@media(max-width: 1199px) { .spread-statistics .stat-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
|
||||
@media(max-width: 980px) { .spread-statistics .stat-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } .spread-statistics .chart-grid { grid-template-columns: 1fr; } }
|
||||
@media(max-width: 560px) { .spread-statistics .stat-grid { grid-template-columns: 1fr; } .spread-statistics .metrics-card > .el-card__body { padding: 12px; } }
|
||||
</style>
|
||||
<div id="app" class="spread-statistics" v-cloak>
|
||||
<guava ref="guava">
|
||||
<el-card class="filter-card" shadow="never">
|
||||
<search :is-search-button="false">
|
||||
<search-item label="所属栏目">
|
||||
<el-cascader v-model="query.categoryId" :options="categoryOptions"
|
||||
:props="{value:'id',label:'name',children:'children',emitPath:false,checkStrictly:true}"
|
||||
clearable filterable style="width: 100%"></el-cascader>
|
||||
</search-item>
|
||||
<search-item label="发布状态">
|
||||
<el-select v-model="query.isPublished" clearable placeholder="全部">
|
||||
<el-option label="草稿" :value="0"></el-option>
|
||||
<el-option label="已发布" :value="1"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<search-item label="创建时间">
|
||||
<el-date-picker v-model="query.createTime" type="datetimerange"
|
||||
value-format="yyyy-MM-dd HH:mm:ss" start-placeholder="开始时间"
|
||||
end-placeholder="结束时间" style="width: 100%"></el-date-picker>
|
||||
</search-item>
|
||||
<div class="search-query">
|
||||
<el-button type="primary" size="medium" icon="el-icon-search" @click="loadAll">查询</el-button>
|
||||
<el-button size="medium" @click="resetQuery">重置</el-button>
|
||||
</div>
|
||||
</search>
|
||||
</el-card>
|
||||
|
||||
<el-card class="metrics-card" shadow="never" v-loading="loading">
|
||||
<div class="stat-grid">
|
||||
<div class="metric-tile">
|
||||
<div class="metric-icon purple"><i class="el-icon-folder-opened"></i></div>
|
||||
<div class="metric-info"><div class="stat-label">栏目总数</div><div class="stat-value">{{summary.categoryCount || 0}}</div></div>
|
||||
</div>
|
||||
<div class="metric-tile">
|
||||
<div class="metric-icon purple"><i class="el-icon-document"></i></div>
|
||||
<div class="metric-info"><div class="stat-label">内容总数</div><div class="stat-value">{{summary.contentCount || 0}}</div></div>
|
||||
</div>
|
||||
<div class="metric-tile">
|
||||
<div class="metric-icon green"><i class="el-icon-circle-check"></i></div>
|
||||
<div class="metric-info"><div class="stat-label">已发布</div><div class="stat-value">{{summary.publishedCount || 0}}</div></div>
|
||||
</div>
|
||||
<div class="metric-tile">
|
||||
<div class="metric-icon orange"><i class="el-icon-time"></i></div>
|
||||
<div class="metric-info"><div class="stat-label">未发布</div><div class="stat-value">{{unpublishedCount}}</div></div>
|
||||
</div>
|
||||
<div class="metric-tile">
|
||||
<div class="metric-icon blue"><i class="el-icon-view"></i></div>
|
||||
<div class="metric-info"><div class="stat-label">总访问量</div><div class="stat-value">{{summary.viewCount || 0}}</div></div>
|
||||
</div>
|
||||
<div class="metric-tile">
|
||||
<div class="metric-icon pink"><i class="el-icon-data-line"></i></div>
|
||||
<div class="metric-info"><div class="stat-label">平均访问量</div><div class="stat-value">{{summary.avgViewCount || 0}}</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<div class="chart-grid">
|
||||
<el-card class="chart-card" shadow="never">
|
||||
<div slot="header"><b>栏目访问量占比</b></div>
|
||||
<div ref="categoryChart" class="chart-box"></div>
|
||||
</el-card>
|
||||
<el-card class="chart-card" shadow="never">
|
||||
<div slot="header"><b>新闻内容访问量 Top10</b></div>
|
||||
<div ref="topChart" class="chart-box"></div>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<el-card class="top-table-card" shadow="never" v-loading="loading">
|
||||
<div slot="header"><b>新闻内容访问量 Top10</b></div>
|
||||
<el-table :data="topData" stripe border style="width: 100%">
|
||||
<el-table-column type="index" label="序号" width="70" align="center"></el-table-column>
|
||||
<el-table-column prop="categoryName" label="栏目" min-width="120" align="center"></el-table-column>
|
||||
<el-table-column prop="title" label="标题" min-width="260" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="summary" label="摘要" min-width="260" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="unionName" label="所属工会" min-width="150" show-overflow-tooltip align="center"></el-table-column>
|
||||
<el-table-column label="发布时间" width="170" align="center">
|
||||
<template slot-scope="{row}">{{formatTime(row.publishedAt)}}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="viewCount" label="访问量" width="100" align="center"></el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</guava>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
data() {
|
||||
return {
|
||||
apiBase: "/platform/spread/statistics",
|
||||
loading: false,
|
||||
query: {categoryId: "", isPublished: "", createTime: []},
|
||||
categoryOptions: [],
|
||||
summary: {},
|
||||
topData: [],
|
||||
categoryData: [],
|
||||
topChart: null,
|
||||
categoryChart: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 未发布数量由内容总数减去已发布数量得到。
|
||||
unpublishedCount() {
|
||||
return Math.max(Number(this.summary.contentCount || 0) - Number(this.summary.publishedCount || 0), 0)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 将时间范围拆分成统计接口参数。
|
||||
buildParams() {
|
||||
return {
|
||||
categoryId: this.query.categoryId,
|
||||
isPublished: this.query.isPublished,
|
||||
beginTime: this.query.createTime && this.query.createTime.length ? this.query.createTime[0] : "",
|
||||
endTime: this.query.createTime && this.query.createTime.length ? this.query.createTime[1] : ""
|
||||
}
|
||||
},
|
||||
// 加载栏目筛选树。
|
||||
loadCategories() {
|
||||
return this.$axios.post(this.apiBase + "/categoryTree").then(resp => {
|
||||
if (resp.code === 0) this.categoryOptions = resp.data || []
|
||||
})
|
||||
},
|
||||
// 并行加载卡片、排行和栏目统计,减少页面等待时间。
|
||||
loadAll() {
|
||||
this.loading = true
|
||||
const params = this.buildParams()
|
||||
Promise.all([
|
||||
this.$axios.post(this.apiBase + "/summary", params),
|
||||
this.$axios.post(this.apiBase + "/top10", params),
|
||||
this.$axios.post(this.apiBase + "/categoryData", params)
|
||||
]).then(values => {
|
||||
this.summary = values[0].code === 0 ? (values[0].data || {}) : {}
|
||||
this.topData = values[1].code === 0 ? (values[1].data || []) : []
|
||||
this.categoryData = values[2].code === 0 ? (values[2].data || []) : []
|
||||
this.$nextTick(this.renderCharts)
|
||||
}).finally(() => this.loading = false)
|
||||
},
|
||||
// 清空筛选并重新统计。
|
||||
resetQuery() {
|
||||
this.query = {categoryId: "", isPublished: "", createTime: []}
|
||||
this.loadAll()
|
||||
},
|
||||
// 创建或刷新两个统计图表。
|
||||
renderCharts() {
|
||||
if (!window.echarts) return
|
||||
if (!this.topChart) this.topChart = echarts.init(this.$refs.topChart)
|
||||
if (!this.categoryChart) this.categoryChart = echarts.init(this.$refs.categoryChart)
|
||||
this.topChart.setOption({
|
||||
color: ["#5b8ff9"],
|
||||
tooltip: {trigger: "axis", axisPointer: {type: "shadow"}},
|
||||
grid: {left: 58, right: 28, top: 42, bottom: 82},
|
||||
xAxis: {
|
||||
type: "category",
|
||||
data: this.topData.map(item => item.title),
|
||||
axisTick: {show: false},
|
||||
axisLine: {lineStyle: {color: "#cfd5df"}},
|
||||
axisLabel: {
|
||||
interval: 0,
|
||||
rotate: 24,
|
||||
color: "#606266",
|
||||
formatter: value => value.length > 12 ? value.slice(0, 12) + "…" : value
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: "value",
|
||||
minInterval: 1,
|
||||
axisLine: {show: false},
|
||||
axisTick: {show: false},
|
||||
splitLine: {lineStyle: {color: "#dfe3e8", type: "dashed"}}
|
||||
},
|
||||
series: [{
|
||||
type: "bar",
|
||||
barMaxWidth: 44,
|
||||
itemStyle: {color: "#5b8ff9"},
|
||||
data: this.topData.map(item => item.viewCount || 0)
|
||||
}]
|
||||
}, true)
|
||||
this.categoryChart.setOption({
|
||||
color: ["#5b8ff9", "#5ad8a6", "#6f86ad", "#f6bd16", "#e8684a", "#9270ca"],
|
||||
tooltip: {trigger: "item", formatter: "{b}<br/>访问量:{c}<br/>占比:{d}%"},
|
||||
series: [{
|
||||
type: "pie",
|
||||
radius: ["35%", "64%"],
|
||||
center: ["50%", "56%"],
|
||||
avoidLabelOverlap: true,
|
||||
itemStyle: {borderColor: "#fff", borderWidth: 3, borderRadius: 10},
|
||||
label: {color: "#303133", formatter: "{b}: {d}%"},
|
||||
labelLine: {length: 16, length2: 18},
|
||||
data: this.categoryData.map(item => ({name: item.categoryName, value: item.viewCount || 0}))
|
||||
}]
|
||||
}, true)
|
||||
},
|
||||
// 格式化 Top10 表格的发布时间。
|
||||
formatTime(value) {
|
||||
return value ? this.$moment(Number(value)).format("YYYY-MM-DD HH:mm:ss") : ""
|
||||
},
|
||||
// 浏览器尺寸变化时重算图表尺寸。
|
||||
resizeCharts() {
|
||||
if (this.topChart) this.topChart.resize()
|
||||
if (this.categoryChart) this.categoryChart.resize()
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
window.addEventListener("resize", this.resizeCharts)
|
||||
// 图表容器挂载完成后自动执行首次查询,避免必须点击“查询”才显示数据。
|
||||
this.$nextTick(() => {
|
||||
this.loadCategories().then(() => this.loadAll())
|
||||
})
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener("resize", this.resizeCharts)
|
||||
if (this.topChart) this.topChart.dispose()
|
||||
if (this.categoryChart) this.categoryChart.dispose()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
Reference in New Issue
Block a user