Browse Source

feat:心增首页数据大屏

main
zhj 2 months ago
parent
commit
9c9490e44f
  1. 2
      src/router/index.js
  2. 114
      src/views/dashboard/LineAreaChart.vue
  3. 58
      src/views/dashboard/LineChart.vue
  4. 82
      src/views/dashboard/OptionBar.vue
  5. 33
      src/views/dashboard/PieChart.vue
  6. 146
      src/views/dashboard/commonChart.vue
  7. 137
      src/views/index.vue
  8. 0
      src/views/index_v.vue

2
src/router/index.js

@ -68,7 +68,7 @@ export const constantRoutes = [
children: [ children: [
{ {
path: 'index', path: 'index',
component: () => import('@/views/index'), component: () => import('@/views/index.vue'),
name: 'Index', name: 'Index',
meta: { title: '首页', icon: 'dashboard', affix: true } meta: { title: '首页', icon: 'dashboard', affix: true }
} }

114
src/views/dashboard/LineAreaChart.vue

@ -0,0 +1,114 @@
<template>
<div :class="className" :style="{height:height,width:width}" />
</template>
<script>
import * as echarts from 'echarts'
require('echarts/theme/macarons') // echarts theme
import resize from './mixins/resize'
export default {
mixins: [resize],
props: {
className: {
type: String,
default: 'chart'
},
width: {
type: String,
default: '100%'
},
height: {
type: String,
default: '350px'
},
autoResize: {
type: Boolean,
default: true
},
chartData: {
type: Object,
required: true
}
},
data() {
return {
chart: null
}
},
watch: {
chartData: {
deep: true,
handler(val) {
this.setOptions(val)
}
}
},
mounted() {
this.$nextTick(() => {
this.initChart()
})
},
beforeDestroy() {
if (!this.chart) {
return
}
this.chart.dispose()
this.chart = null
},
methods: {
initChart() {
if(this.chart)
this.chart.dispose()
this.chart = echarts.init(this.$el, 'macarons')
this.setOptions(this.chartData)
},
setOptions(data=[]) {
let series = []
data.data.forEach((item,idx) => {
series.push({
name: data.legend[idx],
smooth: true,
type: 'line',
data: item,
areaStyle: {},
animationDuration: 2800,
animationEasing: 'cubicInOut'
})
})
this.chart.setOption({
xAxis: {
data: data.xAxis,
boundaryGap: false,
axisTick: {
show: false
}
},
grid: {
left: 15,
right: 35,
bottom: 20,
top: 30,
containLabel: true
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross'
},
padding: [5, 10]
},
yAxis: {
axisTick: {
show: false
}
},
legend: {
data: data.legend
},
series: series
})
}
}
}
</script>

58
src/views/dashboard/LineChart.vue

@ -61,18 +61,29 @@ export default {
this.chart = echarts.init(this.$el, 'macarons') this.chart = echarts.init(this.$el, 'macarons')
this.setOptions(this.chartData) this.setOptions(this.chartData)
}, },
setOptions({ expectedData, actualData } = {}) { setOptions(data={}) {
let series = []
data.data.forEach((item,idx) => {
series.push({
name: data.legend[idx],
smooth: true,
type: 'line',
data: item,
animationDuration: 2800,
animationEasing: idx%2 === 0 ? 'cubicInOut': 'quadraticOut'
})
})
this.chart.setOption({ this.chart.setOption({
xAxis: { xAxis: {
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], data: data.xAxis,
boundaryGap: false, boundaryGap: false,
axisTick: { axisTick: {
show: false show: false
} }
}, },
grid: { grid: {
left: 10, left: 15,
right: 10, right: 35,
bottom: 20, bottom: 20,
top: 30, top: 30,
containLabel: true containLabel: true
@ -90,44 +101,9 @@ export default {
} }
}, },
legend: { legend: {
data: ['expected', 'actual'] data: data.legend
},
series: [{
name: 'expected', itemStyle: {
normal: {
color: '#FF005A',
lineStyle: {
color: '#FF005A',
width: 2
}
}
}, },
smooth: true, series: series
type: 'line',
data: expectedData,
animationDuration: 2800,
animationEasing: 'cubicInOut'
},
{
name: 'actual',
smooth: true,
type: 'line',
itemStyle: {
normal: {
color: '#3888fa',
lineStyle: {
color: '#3888fa',
width: 2
},
areaStyle: {
color: '#f3f8ff'
}
}
},
data: actualData,
animationDuration: 2800,
animationEasing: 'quadraticOut'
}]
}) })
} }
} }

82
src/views/dashboard/OptionBar.vue

@ -0,0 +1,82 @@
<template>
<div style="display: flex; justify-content: space-between; width: 100%">
<el-radio-group @input="handleDateTypeChg" v-model="dataType" size="small">
<el-radio-button label="day">按日</el-radio-button>
<el-radio-button label="week">按周</el-radio-button>
<el-radio-button label="month">按月</el-radio-button>
<el-radio-button label="year">按年</el-radio-button>
</el-radio-group>
<el-date-picker
@change="datePeriodChg"
v-model="datePeriodCp"
:type="datePeriodType"
align="right"
unlink-panels
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="yyyy-MM-dd"
>
</el-date-picker>
</div>
</template>
<script>
import * as echarts from 'echarts'
require('echarts/theme/macarons') // echarts theme
import resize from './mixins/resize'
export default {
mixins: [resize],
props: {
datePeriod: {
type: Array,
default: ()=>{[]}
}
},
mounted() {
if (!this.datePeriodCp || this.datePeriodCp.length === 0) {
if(!this.datePeriodCp){
this.datePeriodCp = []
}
let bgDate = new Date();
bgDate.setDate(bgDate.getDate() - 7);
this.datePeriodCp.push(bgDate);
this.datePeriodCp.push(new Date());
}
},
watch:{
datePeriod:{
deep: true,
immediate: true,
handler(newVal, oldVal){
this.datePeriodCp = newVal;
}
}
},
data() {
return {
dataType: 'day',
datePeriodType: 'daterange',
valueFormat: 'yyyy-MM-dd',
datePeriodCp: null
}
},
methods: {
handleDateTypeChg(type) {
if (type === 'month' || type === 'year') {
this.datePeriodType = 'monthrange'
this.valueFormat = 'yyyy-MM'
} else {
this.datePeriodType = 'daterange'
this.valueFormat = 'yyyy-MM-dd'
}
this.$emit('change', this.dataType, this.datePeriodCp)
},
datePeriodChg(date) {
this.$emit('change', this.dataType, this.datePeriodCp)
}
}
}
</script>

33
src/views/dashboard/PieChart.vue

@ -21,6 +21,10 @@ export default {
height: { height: {
type: String, type: String,
default: '300px' default: '300px'
},
chartData:{
type: Object,
required: true
} }
}, },
data() { data() {
@ -42,32 +46,35 @@ export default {
}, },
methods: { methods: {
initChart() { initChart() {
if(this.chart) this.chart.dispose()
this.chart = echarts.init(this.$el, 'macarons') this.chart = echarts.init(this.$el, 'macarons')
this.setOptions(this.chartData)
},
setOptions(data={}) {
let seriesData = []
data.data[0].forEach((item,idx) => {
seriesData.push({value: item, name: data.legend[idx]})
})
this.chart.setOption({ this.chart.setOption({
title: {
text: '交易类型图',
left: 'center'
},
tooltip: { tooltip: {
trigger: 'item', trigger: 'item',
formatter: '{a} <br/>{b} : {c} ({d}%)' formatter: '{b} : {c} ({d}%)'
}, },
legend: { legend: {
left: 'center', left: 'center',
bottom: '10', bottom: '10',
data: ['Industries', 'Technology', 'Forex', 'Gold', 'Forecasts'] data: data.legend
}, },
series: [ series: [
{ {
name: 'WEEKLY WRITE ARTICLES',
type: 'pie', type: 'pie',
roseType: 'radius',
radius: [15, 95], radius: [15, 95],
center: ['50%', '38%'], center: ['50%', '50%'],
data: [ data: seriesData,
{ value: 320, name: 'Industries' },
{ value: 240, name: 'Technology' },
{ value: 149, name: 'Forex' },
{ value: 100, name: 'Gold' },
{ value: 59, name: 'Forecasts' }
],
animationEasing: 'cubicInOut', animationEasing: 'cubicInOut',
animationDuration: 2600 animationDuration: 2600
} }

146
src/views/dashboard/commonChart.vue

@ -0,0 +1,146 @@
<template>
<div :class="className" :style="{height:height,width:width}" />
</template>
<script>
import * as echarts from 'echarts'
require('echarts/theme/macarons') // echarts theme
import resize from './mixins/resize'
export default {
mixins: [resize],
props: {
className: {
type: String,
default: 'chart'
},
width: {
type: String,
default: '100%'
},
height: {
type: String,
default: '350px'
},
autoResize: {
type: Boolean,
default: true
},
chartData: {
type: Object,
required: true
}
},
data() {
return {
chart: null
}
},
watch: {
chartData: {
deep: true,
handler(val) {
this.setOptions(val)
}
}
},
mounted() {
this.$nextTick(() => {
this.initChart()
})
},
beforeDestroy() {
if (!this.chart) {
return
}
this.chart.dispose()
this.chart = null
},
methods: {
initChart() {
if(this.chart) this.chart.dispose()
this.chart = echarts.init(this.$el, 'macarons')
this.setOptions(this.chartData)
},
setOptions(data={}) {
let series = []
let tooltip = {}
let grid = {}
let xAxis = {}
let yAxis = {}
data.data.forEach((item,idx) => {
series.push({
name: data.legend[idx],
smooth: true,
type: data.type,
barWidth: data.type==='bar' && data.barWidth ? data.barWidth : 0,
data: item,
animationDuration: 2800,
animationEasing: idx%2 === 0 ? 'cubicInOut': 'quadraticOut'
})
})
if(data.type === 'line'){
tooltip = {
trigger: 'axis',
axisPointer: {
type: 'cross'
},
padding: [5, 10]
}
grid = { left: 15, right: 35, bottom: 20, top: 30, containLabel: true
}
xAxis = {
data: data.xAxis,
boundaryGap: false,
axisTick: {
show: false
}
}
yAxis={
axisTick: {
show: false
}
}
}else if(data.type === 'bar'){
tooltip = {
trigger: 'axis',
axisPointer: { //
type: 'shadow' // 线'line' | 'shadow'
}
}
grid = {
top: 10,
left: '2%',
right: '2%',
bottom: '3%',
containLabel: true
}
xAxis = [{
type: 'category',
data: data.xAxis,
axisTick: {
alignWithLabel: true
}
}]
yAxis = [{
type: 'value',
axisTick: {
show: false
}
}]
}
this.chart.setOption({
xAxis: xAxis,
grid: grid,
tooltip: tooltip,
yAxis: yAxis,
legend: {
data: data.legend
},
series: series
})
}
}
}
</script>

137
src/views/index.vue

@ -5,20 +5,149 @@
<h2>{{ $t('index.title') }}</h2> <h2>{{ $t('index.title') }}</h2>
</el-col> </el-col>
</el-row> </el-row>
<el-divider /> <el-divider/>
<el-row :gutter="16">
<el-col v-for="item in indicators" :md="4" :sm="4" :xs="24">
<el-card shadow="always">
<div style="display: flex; flex-direction: column; justify-content: center; align-items: center">
<el-statistic
group-separator=","
:precision="item.precision"
:value="item.value"
:title="item.name"
>
<template slot="formatter">
<count-to :start-val="0" :end-val="item.value" :decimals="item.precision" :duration="3000"
style="font-size: 25px"/>
</template>
</el-statistic>
</div>
</el-card>
</el-col>
</el-row>
<el-row :gutter="16" style="margin-top: 20px">
<el-col :md="12" :sm="12" :xs="24">
<option-bar @change="(type, period)=>{this.handleDateChg(type, period, 'biz')}"></option-bar>
<common-chart ref="lineChart" :chart-data="chartData.biz"/>
</el-col>
<el-col :md="12" :sm="12" :xs="24">
<option-bar @change="(type, period)=>{this.handleDateChg(type, period, 'user')}"></option-bar>
<common-chart ref="barChart" :chart-data="chartData.user"/>
</el-col>
</el-row>
<el-row :gutter="16" style="margin-top: 20px">
<el-col :md="12" :sm="12" :xs="24">
<option-bar @change="(type, period)=>{this.handleDateChg(type, period, 'currency')}"></option-bar>
<line-area-chart ref="lineAreaChart" :chart-data="chartData.currency"></line-area-chart>
</el-col>
<el-col :md="12" :sm="12" :xs="24">
<option-bar @change="(type, period)=>{this.handleDateChg(type, period, 'bizType')}"></option-bar>
<pie-chart ref="pieChart" :chart-data="chartData.bizType"></pie-chart>
</el-col>
</el-row>
</div> </div>
</template> </template>
<script> <script>
import LineChart from "@/views/dashboard/LineChart.vue";
import CountTo from 'vue-count-to'
import CommonChart from "@/views/dashboard/commonChart.vue";
import BarChart from "@/views/dashboard/BarChart.vue";
import PieChart from "@/views/dashboard/PieChart.vue";
import LineAreaChart from "@/views/dashboard/LineAreaChart.vue";
import OptionBar from "@/views/dashboard/OptionBar.vue";
export default { export default {
name: "Index", name: "Index",
components: {OptionBar, LineAreaChart, PieChart, BarChart, CommonChart, LineChart, CountTo},
data() { data() {
return { return {
// //
version: "4.8.2", indicators: [
{
name: '交易额',
value: 1829932.227,
precision: 2
},
{
name: '交易笔数',
value: 2932,
precision: 0
},
{
name: '用户数量',
value: 1427,
precision: 0
}, {
name: '今日交易额',
value: 4329,
precision: 2
},
{
name: '今日交易笔数',
value: 23,
precision: 0
},
{
name: '今日活跃用户数',
value: 93,
precision: 0
},
],
chartData: {
biz: {
type: 'line',
data: [[244, 308, 136, 155, 321, 83, 287], [123, 215, 301, 167, 254, 98, 290], [188, 245, 112, 310, 155, 203, 137], [128, 273, 94, 316, 205, 177, 89]],
xAxis: ['2025/05/01', '2025/05/02', '2025/05/03', '2025/05/04', '2025/05/05', '2025/05/06', '2025/05/07'],
legend: ['转账', '提现', '收款', '付款']
},
user: {
type: 'bar',
barWidth: '60%',
data: [[234, 145, 312, 95, 186, 273, 168, 231, 190]],
xAxis: ['2025/05/01', '2025/05/02', '2025/05/03', '2025/05/04', '2025/05/05', '2025/05/06', '2025/05/07', '2025/05/08', '2025/05/09'],
legend: ['活跃用户']
},
currency: {
type: 'line',
data: [[34, 145, 212, 225, 236, 243, 268]],
xAxis: ['2025/05/01', '2025/05/02', '2025/05/03', '2025/05/04', '2025/05/05', '2025/05/06', '2025/05/07'],
legend: ['交易本币']
},
bizType: {
data: [[587, 129, 364, 528]],
legend: ['转账', '提现', '收款', '付款']
}
}
}; };
}, },
methods: { methods: {
handleDateChg(type, period, name) {
for (let j = 0; j < this.chartData[name].data.length; j++) {
let arr = []
for (let i = 0; i < this.chartData[name].data[0].length; i++) {
arr.push(Math.floor(Math.random() * 100));
}
this.chartData[name].data[j] = arr
}
switch (name) {
case 'biz':
this.$refs.lineChart.initChart()
break;
case 'user':
this.$refs.barChart.initChart()
break;
case 'currency':
this.$refs.lineAreaChart.initChart();
break;
case 'bizType':
this.$refs.pieChart.initChart()
break;
default:
break;
}
},
goTarget(href) { goTarget(href) {
window.open(href, "_blank"); window.open(href, "_blank");
}, },
@ -28,18 +157,22 @@ export default {
<style scoped lang="scss"> <style scoped lang="scss">
.home { .home {
background-color: #F5F7F9;
blockquote { blockquote {
padding: 10px 20px; padding: 10px 20px;
margin: 0 0 20px; margin: 0 0 20px;
font-size: 17.5px; font-size: 17.5px;
border-left: 5px solid #eee; border-left: 5px solid #eee;
} }
hr { hr {
margin-top: 20px; margin-top: 20px;
margin-bottom: 20px; margin-bottom: 20px;
border: 0; border: 0;
border-top: 1px solid #eee; border-top: 1px solid #eee;
} }
.col-item { .col-item {
margin-bottom: 20px; margin-bottom: 20px;
} }

0
src/views/index_v1.vue → src/views/index_v.vue

Loading…
Cancel
Save