feat: Add analysis demo
This commit is contained in:
@@ -1,7 +1,40 @@
|
||||
<script setup lang="ts">
|
||||
import PanelGroup from './components/PanelGroup.vue'
|
||||
import { ElRow, ElCol, ElCard, ElSkeleton } from 'element-plus'
|
||||
import { Echart } from '@/components/Echart'
|
||||
import { pieOptions, barOptions, lineOptions } from './echarts-data'
|
||||
import { ref } from 'vue'
|
||||
|
||||
const loading = ref(true)
|
||||
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 1000)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<PanelGroup />
|
||||
<ElRow :gutter="20" justify="space-between">
|
||||
<ElCol :xl="10" :lg="10" :md="24" :sm="24" :xs="24">
|
||||
<ElCard shadow="hover" class="mb-20px">
|
||||
<ElSkeleton :loading="loading" animated>
|
||||
<Echart :options="pieOptions" :height="300" />
|
||||
</ElSkeleton>
|
||||
</ElCard>
|
||||
</ElCol>
|
||||
<ElCol :xl="14" :lg="14" :md="24" :sm="24" :xs="24">
|
||||
<ElCard shadow="hover" class="mb-20px">
|
||||
<ElSkeleton :loading="loading" animated>
|
||||
<Echart :options="barOptions" :height="300" />
|
||||
</ElSkeleton>
|
||||
</ElCard>
|
||||
</ElCol>
|
||||
<ElCol :span="24">
|
||||
<ElCard shadow="hover" class="mb-20px">
|
||||
<ElSkeleton :loading="loading" animated :rows="4">
|
||||
<Echart :options="lineOptions" :height="400" />
|
||||
</ElSkeleton>
|
||||
</ElCard>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
</template>
|
||||
|
||||
@@ -1,25 +1,154 @@
|
||||
<script setup lang="ts">
|
||||
import { ElRow, ElCol, ElCard } from 'element-plus'
|
||||
import { ElRow, ElCol, ElCard, ElSkeleton } from 'element-plus'
|
||||
import { CountTo } from '@/components/CountTo'
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { ref, reactive } from 'vue'
|
||||
import { getCountApi } from '@/api/dashboard/analysis'
|
||||
import type { AnalysisTotalTypes } from '@/api/dashboard/analysis/types'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const { getPrefixCls } = useDesign()
|
||||
|
||||
const prefixCls = getPrefixCls('panel')
|
||||
|
||||
const loading = ref(true)
|
||||
|
||||
let totalState = reactive<AnalysisTotalTypes>({
|
||||
users: 0,
|
||||
messages: 0,
|
||||
moneys: 0,
|
||||
shoppings: 0
|
||||
})
|
||||
|
||||
const getCount = async () => {
|
||||
const res = await getCountApi()
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
totalState = Object.assign(totalState, res?.data || {})
|
||||
}
|
||||
|
||||
getCount()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ElRow :gutter="20" class="v-panel">
|
||||
<ElCol :xl="6" :lg="6" :md="6" :sm="6" :xs="1">
|
||||
<ElCard shadow="hover">
|
||||
<div class="v-panel-item flex justify-between">
|
||||
<div>
|
||||
<div
|
||||
class="v-panel-item__icon v-panel-item__icon--peoples p-16px inline-block rounded-6px"
|
||||
>
|
||||
<Icon icon="svg-icon:peoples" :size="40" />
|
||||
<ElRow :gutter="20" justify="space-between" :class="prefixCls">
|
||||
<ElCol :xl="6" :lg="6" :md="12" :sm="12" :xs="24">
|
||||
<ElCard shadow="hover" class="mb-20px">
|
||||
<ElSkeleton :loading="loading" animated :rows="2">
|
||||
<template #default>
|
||||
<div :class="`${prefixCls}__item flex justify-between`">
|
||||
<div>
|
||||
<div
|
||||
:class="`${prefixCls}__item--icon ${prefixCls}__item--peoples p-16px inline-block rounded-6px`"
|
||||
>
|
||||
<Icon icon="svg-icon:peoples" :size="40" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col justify-between">
|
||||
<div :class="`${prefixCls}__item--text text-16px text-gray-500 text-right`">{{
|
||||
t('analysis.newUser')
|
||||
}}</div>
|
||||
<CountTo
|
||||
class="text-20px font-700 text-right"
|
||||
:start-val="0"
|
||||
:end-val="102400"
|
||||
:duration="2600"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col justify-between">
|
||||
<div class="v-panel-item__text">新增用户</div>
|
||||
<CountTo :start-val="0" :end-val="102400" :duration="2600" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</ElSkeleton>
|
||||
</ElCard>
|
||||
</ElCol>
|
||||
|
||||
<ElCol :xl="6" :lg="6" :md="12" :sm="12" :xs="24">
|
||||
<ElCard shadow="hover" class="mb-20px">
|
||||
<ElSkeleton :loading="loading" animated :rows="2">
|
||||
<template #default>
|
||||
<div :class="`${prefixCls}__item flex justify-between`">
|
||||
<div>
|
||||
<div
|
||||
:class="`${prefixCls}__item--icon ${prefixCls}__item--message p-16px inline-block rounded-6px`"
|
||||
>
|
||||
<Icon icon="svg-icon:message" :size="40" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col justify-between">
|
||||
<div :class="`${prefixCls}__item--text text-16px text-gray-500 text-right`">{{
|
||||
t('analysis.unreadInformation')
|
||||
}}</div>
|
||||
<CountTo
|
||||
class="text-20px font-700 text-right"
|
||||
:start-val="0"
|
||||
:end-val="81212"
|
||||
:duration="2600"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</ElSkeleton>
|
||||
</ElCard>
|
||||
</ElCol>
|
||||
|
||||
<ElCol :xl="6" :lg="6" :md="12" :sm="12" :xs="24">
|
||||
<ElCard shadow="hover" class="mb-20px">
|
||||
<ElSkeleton :loading="loading" animated :rows="2">
|
||||
<template #default>
|
||||
<div :class="`${prefixCls}__item flex justify-between`">
|
||||
<div>
|
||||
<div
|
||||
:class="`${prefixCls}__item--icon ${prefixCls}__item--money p-16px inline-block rounded-6px`"
|
||||
>
|
||||
<Icon icon="svg-icon:money" :size="40" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col justify-between">
|
||||
<div :class="`${prefixCls}__item--text text-16px text-gray-500 text-right`">{{
|
||||
t('analysis.transactionAmount')
|
||||
}}</div>
|
||||
<CountTo
|
||||
class="text-20px font-700 text-right"
|
||||
:start-val="0"
|
||||
:end-val="9280"
|
||||
:duration="2600"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</ElSkeleton>
|
||||
</ElCard>
|
||||
</ElCol>
|
||||
|
||||
<ElCol :xl="6" :lg="6" :md="12" :sm="12" :xs="24">
|
||||
<ElCard shadow="hover" class="mb-20px">
|
||||
<ElSkeleton :loading="loading" animated :rows="2">
|
||||
<template #default>
|
||||
<div :class="`${prefixCls}__item flex justify-between`">
|
||||
<div>
|
||||
<div
|
||||
:class="`${prefixCls}__item--icon ${prefixCls}__item--shopping p-16px inline-block rounded-6px`"
|
||||
>
|
||||
<Icon icon="svg-icon:shopping" :size="40" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col justify-between">
|
||||
<div :class="`${prefixCls}__item--text text-16px text-gray-500 text-right`">{{
|
||||
t('analysis.totalShopping')
|
||||
}}</div>
|
||||
<CountTo
|
||||
class="text-20px font-700 text-right"
|
||||
:start-val="0"
|
||||
:end-val="13600"
|
||||
:duration="2600"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</ElSkeleton>
|
||||
</ElCard>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
@@ -29,17 +158,41 @@ import { CountTo } from '@/components/CountTo'
|
||||
@prefix-cls: ~'@{namespace}-panel';
|
||||
|
||||
.@{prefix-cls} {
|
||||
&-item {
|
||||
&__item {
|
||||
&--peoples {
|
||||
color: #40c9c6;
|
||||
}
|
||||
|
||||
&--message {
|
||||
color: #36a3f7;
|
||||
}
|
||||
|
||||
&--money {
|
||||
color: #f4516c;
|
||||
}
|
||||
|
||||
&--shopping {
|
||||
color: #34bfa3;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
:deep(.v-icon) {
|
||||
:deep(.@{namespace}-icon) {
|
||||
color: #fff !important;
|
||||
}
|
||||
.@{prefix-cls}-item__icon {
|
||||
.@{prefix-cls}__item--icon {
|
||||
transition: all 0.38s ease-out;
|
||||
|
||||
&--peoples {
|
||||
background: #40c9c6;
|
||||
}
|
||||
}
|
||||
.@{prefix-cls}__item--peoples {
|
||||
background: #40c9c6;
|
||||
}
|
||||
.@{prefix-cls}__item--message {
|
||||
background: #36a3f7;
|
||||
}
|
||||
.@{prefix-cls}__item--money {
|
||||
background: #f4516c;
|
||||
}
|
||||
.@{prefix-cls}__item--shopping {
|
||||
background: #34bfa3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
import { EChartsOption } from 'echarts'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
export const lineOptions: EChartsOption = {
|
||||
title: {
|
||||
text: t('analysis.monthlySales'),
|
||||
left: 'center'
|
||||
},
|
||||
xAxis: {
|
||||
data: [
|
||||
t('analysis.january'),
|
||||
t('analysis.february'),
|
||||
t('analysis.march'),
|
||||
t('analysis.april'),
|
||||
t('analysis.may'),
|
||||
t('analysis.june'),
|
||||
t('analysis.july'),
|
||||
t('analysis.august'),
|
||||
t('analysis.september'),
|
||||
t('analysis.october'),
|
||||
t('analysis.november'),
|
||||
t('analysis.december')
|
||||
],
|
||||
boundaryGap: false,
|
||||
axisTick: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: 20,
|
||||
right: 20,
|
||||
bottom: 20,
|
||||
top: 80,
|
||||
containLabel: true
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross'
|
||||
},
|
||||
padding: [5, 10]
|
||||
},
|
||||
yAxis: {
|
||||
axisTick: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
data: [t('analysis.estimate'), t('analysis.actual')],
|
||||
top: 50
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: t('analysis.estimate'),
|
||||
smooth: true,
|
||||
type: 'line',
|
||||
data: [100, 120, 161, 134, 105, 160, 165, 114, 163, 185, 118, 123],
|
||||
animationDuration: 2800,
|
||||
animationEasing: 'cubicInOut'
|
||||
},
|
||||
{
|
||||
name: t('analysis.actual'),
|
||||
smooth: true,
|
||||
type: 'line',
|
||||
itemStyle: {},
|
||||
data: [120, 82, 91, 154, 162, 140, 145, 250, 134, 56, 99, 123],
|
||||
animationDuration: 2800,
|
||||
animationEasing: 'quadraticOut'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export const pieOptions: EChartsOption = {
|
||||
title: {
|
||||
text: t('analysis.userAccessSource'),
|
||||
left: 'center'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{a} <br/>{b} : {c} ({d}%)'
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
left: 'left',
|
||||
data: [
|
||||
t('analysis.directAccess'),
|
||||
t('analysis.mailMarketing'),
|
||||
t('analysis.allianceAdvertising'),
|
||||
t('analysis.videoAdvertising'),
|
||||
t('analysis.searchEngines')
|
||||
]
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: t('analysis.userAccessSource'),
|
||||
type: 'pie',
|
||||
radius: '55%',
|
||||
center: ['50%', '60%'],
|
||||
data: [
|
||||
{ value: 335, name: t('analysis.directAccess') },
|
||||
{ value: 310, name: t('analysis.mailMarketing') },
|
||||
{ value: 234, name: t('analysis.allianceAdvertising') },
|
||||
{ value: 135, name: t('analysis.videoAdvertising') },
|
||||
{ value: 1548, name: t('analysis.searchEngines') }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export const barOptions: EChartsOption = {
|
||||
title: {
|
||||
text: t('analysis.weeklyUserActivity'),
|
||||
left: 'center'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: 50,
|
||||
right: 20,
|
||||
bottom: 20
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: [
|
||||
t('analysis.monday'),
|
||||
t('analysis.tuesday'),
|
||||
t('analysis.wednesday'),
|
||||
t('analysis.thursday'),
|
||||
t('analysis.friday'),
|
||||
t('analysis.saturday'),
|
||||
t('analysis.sunday')
|
||||
],
|
||||
axisTick: {
|
||||
alignWithLabel: true
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: t('analysis.activeQuantity'),
|
||||
data: [13253, 34235, 26321, 12340, 24643, 1322, 1324],
|
||||
type: 'bar'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -5,6 +5,11 @@ import { LocaleDropdown } from '@/components/LocaleDropdown'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { underlineToHump } from '@/utils'
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
|
||||
const { getPrefixCls } = useDesign()
|
||||
|
||||
const prefixCls = getPrefixCls('login')
|
||||
|
||||
const appStore = useAppStore()
|
||||
|
||||
@@ -13,10 +18,13 @@ const { t } = useI18n()
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="v-login h-[100%] relative overflow-hidden <xl:bg-v-dark <sm:px-10px <xl:px-10px <md:px-10px"
|
||||
:class="prefixCls"
|
||||
class="h-[100%] relative overflow-hidden <xl:bg-v-dark <sm:px-10px <xl:px-10px <md:px-10px"
|
||||
>
|
||||
<div class="relative h-full flex mx-auto">
|
||||
<div class="v-login__left flex-1 bg-gray-500 bg-opacity-20 relative p-30px <xl:hidden">
|
||||
<div
|
||||
:class="`${prefixCls}__left flex-1 bg-gray-500 bg-opacity-20 relative p-30px <xl:hidden`"
|
||||
>
|
||||
<div class="flex items-center relative text-white">
|
||||
<img src="@/assets/imgs/logo.png" alt="" class="w-48px h-48px mr-10px" />
|
||||
<span class="text-20px font-bold">{{ underlineToHump(appStore.getTitle) }}</span>
|
||||
|
||||
Reference in New Issue
Block a user