|
|
@ -1,148 +1,136 @@ |
|
|
|
<template> |
|
|
|
<el-row> |
|
|
|
<!-- 数据总览卡片 --> |
|
|
|
<el-col :span="4" style="padding-right: 10px;"> |
|
|
|
<el-card class="center-card margin-bottom">数据总览</el-card> |
|
|
|
</el-col> |
|
|
|
<!-- 最后更新时间 --> |
|
|
|
<el-col :span="18" style="display: flex; align-items: center; font-size: 18px"> |
|
|
|
<div class="top"> |
|
|
|
<el-card style="width:20%" class="center-card">数据总览</el-card> |
|
|
|
<span class="text"> |
|
|
|
最后更新时间:{{ |
|
|
|
workDataUpdateTime && workDataUpdateTime !== '1970-01-01 08:00:00' ? workDataUpdateTime : '该地区暂无数据' |
|
|
|
}} |
|
|
|
</el-col> |
|
|
|
</el-row> |
|
|
|
</span> |
|
|
|
</div> |
|
|
|
|
|
|
|
<el-row :gutter="10"> |
|
|
|
<div class="card"> |
|
|
|
<!-- 第一个卡片 --> |
|
|
|
<el-col :span="6"> |
|
|
|
<el-card class="card-item"> |
|
|
|
<template #header> |
|
|
|
<div class="card-header"> |
|
|
|
<div class="card-title">当前金币余量</div> |
|
|
|
<div> |
|
|
|
<span style="font-weight: bold">{{ currentGold / 100 }}</span> 较前一日 |
|
|
|
{{ dailyChange / 100 }} |
|
|
|
<template v-if="dailyChange > 0"> |
|
|
|
<el-icon style="color:red"> |
|
|
|
<ArrowUpBold/> |
|
|
|
</el-icon> |
|
|
|
</template> |
|
|
|
<template v-else-if="dailyChange < 0"> |
|
|
|
<el-icon style="color:forestgreen"> |
|
|
|
<ArrowDownBold/> |
|
|
|
</el-icon> |
|
|
|
</template> |
|
|
|
<template v-else> |
|
|
|
<el-icon style="color:grey"> |
|
|
|
<SemiSelect/> |
|
|
|
</el-icon> |
|
|
|
</template> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</template> |
|
|
|
<el-card class="card-item"> |
|
|
|
<template #header> |
|
|
|
<div class="card-title">当前金币余量</div> |
|
|
|
<div> |
|
|
|
<div class="margin-bottom">永久金币:{{ currentPermanent / 100 }}</div> |
|
|
|
<div class="margin-bottom">免费金币:{{ currentFree / 100 }}</div> |
|
|
|
<div class="margin-bottom">[六月到期|{{ currentFreeJune / 100 }}] |
|
|
|
[十二月到期|{{ currentFreeDecember / 100 }}] |
|
|
|
</div> |
|
|
|
<div>任务金币:{{ currentTask / 100 }}</div> |
|
|
|
</div> |
|
|
|
</el-card> |
|
|
|
</el-col> |
|
|
|
|
|
|
|
<!-- 第二个卡片 --> |
|
|
|
<el-col :span="6"> |
|
|
|
<el-card class="card-item"> |
|
|
|
<div class="card-title">全年累计充值金币数</div> |
|
|
|
<div class="card-title">{{ yearlyRecharge / 100 }}</div> |
|
|
|
<div> </div> |
|
|
|
<div class="center-card">折合新币累计金额:{{ yearlyMoney / 100 }}</div> |
|
|
|
<template #footer> |
|
|
|
<el-col class="margin-bottom center-card">昨日新增:{{ recharge / 100 }}</el-col> |
|
|
|
<el-col class="margin-bottom center-card">其中充值:{{ money / 100 }}</el-col> |
|
|
|
</template> |
|
|
|
</el-card> |
|
|
|
</el-col> |
|
|
|
|
|
|
|
<!-- 第三个卡片 --> |
|
|
|
<el-col :span="6"> |
|
|
|
<el-card class="card-item"> |
|
|
|
<div class="card-title">全年累计消费金币数</div> |
|
|
|
<div class="card-title">{{ yearlyReduce / 100 }}</div> |
|
|
|
<div class="center-card">消费:{{ yearlyConsume / 100 }}</div> |
|
|
|
<div class="center-card">退款:{{ yearlyRefund / 100 }}</div> |
|
|
|
<template #footer> |
|
|
|
<div></div> |
|
|
|
|
|
|
|
<div class="margin-bottom center-card">昨日新增消费:{{ dailyConsume / 100 }}</div> |
|
|
|
<div class="margin-bottom center-card">昨日新增消耗:{{ dailyReduce / 100 }}</div> |
|
|
|
<div class="margin-bottom center-card">昨日新增退款:{{ dailyRefund / 100 }}</div> |
|
|
|
</template> |
|
|
|
</el-card> |
|
|
|
</el-col> |
|
|
|
|
|
|
|
<!-- 第四个卡片 --> |
|
|
|
<el-col :span="6"> |
|
|
|
<el-card class="card-item"> |
|
|
|
<el-col class="card-title">全年累计充值人头数</el-col> |
|
|
|
<el-col class="card-title">{{ yearlyRechargeNum }}</el-col> |
|
|
|
<el-col class="center-card">周同比:{{ sumWow }}% |
|
|
|
<template v-if="sumWow > 0"> |
|
|
|
<span style="font-weight: bold">{{ currentGold / 100 |
|
|
|
}}</span> 较前一日 |
|
|
|
{{ dailyChange / 100 }} |
|
|
|
<template v-if="dailyChange > 0"> |
|
|
|
<el-icon style="color:red"> |
|
|
|
<ArrowUpBold/> |
|
|
|
<ArrowUpBold /> |
|
|
|
</el-icon> |
|
|
|
</template> |
|
|
|
<template v-else-if="sumWow < 0"> |
|
|
|
<template v-else-if="dailyChange < 0"> |
|
|
|
<el-icon style="color:forestgreen"> |
|
|
|
<ArrowDownBold/> |
|
|
|
<ArrowDownBold /> |
|
|
|
</el-icon> |
|
|
|
</template> |
|
|
|
<template v-else> |
|
|
|
<el-icon style="color:grey"> |
|
|
|
<SemiSelect/> |
|
|
|
</el-icon> |
|
|
|
</template> |
|
|
|
</el-col> |
|
|
|
<el-col class="center-card">日环比:{{ sumDaily }}% |
|
|
|
<template v-if="sumDaily > 0"> |
|
|
|
<el-icon style="color:red"> |
|
|
|
<ArrowUpBold/> |
|
|
|
</el-icon> |
|
|
|
</template> |
|
|
|
<template v-else-if="sumDaily < 0"> |
|
|
|
<el-icon style="color:forestgreen"> |
|
|
|
<ArrowDownBold/> |
|
|
|
<SemiSelect /> |
|
|
|
</el-icon> |
|
|
|
</template> |
|
|
|
<template v-else> |
|
|
|
<el-icon style="color:grey"> |
|
|
|
<SemiSelect/> |
|
|
|
</el-icon> |
|
|
|
</template> |
|
|
|
</el-col> |
|
|
|
<template #footer> |
|
|
|
<el-col class="margin-bottom center-card">昨日充值人数:{{ ydayRechargeNum }}</el-col> |
|
|
|
<el-col class="margin-bottom center-card">其中首充:{{ firstRecharge }}</el-col> |
|
|
|
</div> |
|
|
|
</template> |
|
|
|
<div> |
|
|
|
<div class="margin-bottom">永久金币:{{ currentPermanent / 100 }}</div> |
|
|
|
<div class="margin-bottom">免费金币:{{ currentFree / 100 }}</div> |
|
|
|
<div class="margin-bottom">[六月到期|{{ currentFreeJune / 100 }}] |
|
|
|
[十二月到期|{{ currentFreeDecember / 100 }}] |
|
|
|
</div> |
|
|
|
<div>任务金币:{{ currentTask / 100 }}</div> |
|
|
|
</div> |
|
|
|
</el-card> |
|
|
|
|
|
|
|
<!-- 第二个卡片 --> |
|
|
|
<el-card class="card-item"> |
|
|
|
<div class="card-title">全年累计充值金币数</div> |
|
|
|
<div class="card-title">{{ yearlyRecharge / 100 }}</div> |
|
|
|
<div> </div> |
|
|
|
<div class="center-card">折合新币累计金额:{{ yearlyMoney / 100 }}</div> |
|
|
|
<template #footer> |
|
|
|
<el-col class="margin-bottom center-card">昨日新增:{{ recharge / 100 }}</el-col> |
|
|
|
<el-col class="margin-bottom center-card">其中充值:{{ money / 100 }}</el-col> |
|
|
|
</template> |
|
|
|
</el-card> |
|
|
|
|
|
|
|
<!-- 第三个卡片 --> |
|
|
|
<el-card class="card-item"> |
|
|
|
<div class="card-title">全年累计消费金币数</div> |
|
|
|
<div class="card-title">{{ yearlyReduce / 100 }}</div> |
|
|
|
<div class="center-card">消费:{{ yearlyConsume / 100 }}</div> |
|
|
|
<div class="center-card">退款:{{ yearlyRefund / 100 }}</div> |
|
|
|
<template #footer> |
|
|
|
<div></div> |
|
|
|
|
|
|
|
<div class="margin-bottom center-card">昨日新增消费:{{ dailyConsume / 100 }}</div> |
|
|
|
<div class="margin-bottom center-card">昨日新增消耗:{{ dailyReduce / 100 }}</div> |
|
|
|
<div class="margin-bottom center-card">昨日新增退款:{{ dailyRefund / 100 }}</div> |
|
|
|
</template> |
|
|
|
</el-card> |
|
|
|
|
|
|
|
<!-- 第四个卡片 --> |
|
|
|
<el-card class="card-item"> |
|
|
|
<el-col class="card-title">全年累计充值人头数</el-col> |
|
|
|
<el-col class="card-title">{{ yearlyRechargeNum }}</el-col> |
|
|
|
<el-col class="center-card">周同比:{{ sumWow }}% |
|
|
|
<template v-if="sumWow > 0"> |
|
|
|
<el-icon style="color:red"> |
|
|
|
<ArrowUpBold /> |
|
|
|
</el-icon> |
|
|
|
</template> |
|
|
|
</el-card> |
|
|
|
</el-col> |
|
|
|
</el-row> |
|
|
|
|
|
|
|
<el-card style="margin-top: 20px"> |
|
|
|
<el-row> |
|
|
|
<el-col> |
|
|
|
<el-tabs v-model="activeTab" @tab-change="handleTabChange"> |
|
|
|
<el-tab-pane label="金币充值" name="recharge"></el-tab-pane> |
|
|
|
<el-tab-pane label="金币消费" name="consume"></el-tab-pane> |
|
|
|
</el-tabs> |
|
|
|
</el-col> |
|
|
|
<div style="margin-top:5px;width:35vw">合计 |
|
|
|
<template v-else-if="sumWow < 0"> |
|
|
|
<el-icon style="color:forestgreen"> |
|
|
|
<ArrowDownBold /> |
|
|
|
</el-icon> |
|
|
|
</template> |
|
|
|
<template v-else> |
|
|
|
<el-icon style="color:grey"> |
|
|
|
<SemiSelect /> |
|
|
|
</el-icon> |
|
|
|
</template> |
|
|
|
</el-col> |
|
|
|
<el-col class="center-card">日环比:{{ sumDaily }}% |
|
|
|
<template v-if="sumDaily > 0"> |
|
|
|
<el-icon style="color:red"> |
|
|
|
<ArrowUpBold /> |
|
|
|
</el-icon> |
|
|
|
</template> |
|
|
|
<template v-else-if="sumDaily < 0"> |
|
|
|
<el-icon style="color:forestgreen"> |
|
|
|
<ArrowDownBold /> |
|
|
|
</el-icon> |
|
|
|
</template> |
|
|
|
<template v-else> |
|
|
|
<el-icon style="color:grey"> |
|
|
|
<SemiSelect /> |
|
|
|
</el-icon> |
|
|
|
</template> |
|
|
|
</el-col> |
|
|
|
<template #footer> |
|
|
|
<el-col class="margin-bottom center-card">昨日充值人数:{{ ydayRechargeNum }}</el-col> |
|
|
|
<el-col class="margin-bottom center-card">其中首充:{{ firstRecharge }}</el-col> |
|
|
|
</template> |
|
|
|
</el-card> |
|
|
|
</div> |
|
|
|
|
|
|
|
<div class="graph"> |
|
|
|
<el-card style="width:100%;"> |
|
|
|
<div> |
|
|
|
<el-tabs v-model="activeTab" @tab-change="handleTabChange"> |
|
|
|
<el-tab-pane label="金币充值" name="recharge"></el-tab-pane> |
|
|
|
<el-tab-pane label="金币消费" name="consume"></el-tab-pane> |
|
|
|
</el-tabs> |
|
|
|
</div> |
|
|
|
<div class="condition"> |
|
|
|
<div style="width:44%;background-color: antiquewhite;">合计 |
|
|
|
永久金币 {{ activeTab === 'recharge' ? sumRechargePermanent / 100 : sumConsumePermanent / 100 }} |
|
|
|
免费金币 {{ activeTab === 'recharge' ? sumRechargeFree / 100 : sumConsumeFree / 100 }} |
|
|
|
任务金币 {{ activeTab === 'recharge' ? sumRechargeTask / 100 : sumConsumeTask / 100 }} |
|
|
|
</div> |
|
|
|
<div @change="handleDatePickerChange" style="width:20vw"> |
|
|
|
<div @change="handleDatePickerChange" style="width:24%;background-color: aqua;"> |
|
|
|
<el-button @click="getYes()" :type="activeTimeRange === 'yes' ? 'primary' : ''">昨天 |
|
|
|
</el-button> |
|
|
|
<el-button @click="getToday()" :type="activeTimeRange === 'today' ? 'primary' : ''">今天 |
|
|
@ -154,26 +142,19 @@ |
|
|
|
<el-button @click="getYear()" :type="activeTimeRange === 'year' ? 'primary' : ''">本年 |
|
|
|
</el-button> |
|
|
|
</div> |
|
|
|
<div style="width:25vw"> |
|
|
|
<div style="width:31%;background-color: bisque;"> |
|
|
|
<el-date-picker v-model="dateRange" type="datetimerange" range-separator="→" start-placeholder="开始时间" |
|
|
|
end-placeholder="结束时间" format="YYYY-MM-DD HH:mm:ss" style="width:20vw" |
|
|
|
value-format="YYYY-MM-DD HH:mm:ss" |
|
|
|
:default-time="defaultTime" :disabled-date="disabledDate" |
|
|
|
/> |
|
|
|
end-placeholder="结束时间" format="YYYY-MM-DD HH:mm:ss" style="width:20vw" value-format="YYYY-MM-DD HH:mm:ss" |
|
|
|
:default-time="defaultTime" :disabled-date="disabledDate" /> |
|
|
|
<el-button type="primary" style="margin-left: 5px" @click="getChartData">查询</el-button> |
|
|
|
</div> |
|
|
|
</el-row> |
|
|
|
|
|
|
|
|
|
|
|
<el-row :gutter="20" style="margin-top: 20px"> |
|
|
|
<el-col :span="18"> |
|
|
|
<div class="bar"> |
|
|
|
<div ref="chartRef" style="width: 100%; height: 400px"></div> |
|
|
|
</div> |
|
|
|
</el-col> |
|
|
|
<el-col :span="6"> |
|
|
|
<el-card class="rank-card" style="width: 100%; height: 100%"> |
|
|
|
<div class="card-large margin-bottom">金币{{ activeTab === 'recharge' ? '充值' : '消费' }}排名</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<div class="graph-content"> |
|
|
|
<div ref="chartRef" class="left"></div> |
|
|
|
<div class="right"> |
|
|
|
<el-card> |
|
|
|
<div class="card-large">金币{{ activeTab === 'recharge' ? '充值' : '消费' }}排名</div> |
|
|
|
<el-select v-model="selectedType" style="width: 100%; margin-bottom: 15px"> |
|
|
|
<el-option label="全部类型" value="all"></el-option> |
|
|
|
<el-option label="永久金币" value="permanent"></el-option> |
|
|
@ -194,22 +175,22 @@ |
|
|
|
</el-table-column> |
|
|
|
</el-table> |
|
|
|
</el-card> |
|
|
|
</el-col> |
|
|
|
</el-row> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</el-card> |
|
|
|
</div> |
|
|
|
</template> |
|
|
|
|
|
|
|
<script setup> |
|
|
|
import * as echarts from 'echarts' |
|
|
|
import {ref, onMounted, nextTick, watch, onUnmounted} from 'vue' |
|
|
|
import { ref, onMounted, nextTick, watch, onUnmounted } from 'vue' |
|
|
|
import API from '@/util/http' |
|
|
|
import {ElMessage} from 'element-plus' |
|
|
|
import { ElMessage } from 'element-plus' |
|
|
|
import dayjs from 'dayjs'; |
|
|
|
import utc from 'dayjs-plugin-utc' |
|
|
|
|
|
|
|
dayjs.extend(utc) |
|
|
|
import {ArrowUpBold, ArrowDownBold, SemiSelect} from '@element-plus/icons-vue' |
|
|
|
import {marketMapping} from "@/utils/marketMap.js"; |
|
|
|
import { ArrowUpBold, ArrowDownBold, SemiSelect } from '@element-plus/icons-vue' |
|
|
|
import { marketMapping } from "@/utils/marketMap.js"; |
|
|
|
|
|
|
|
const defaultTime = [ |
|
|
|
new Date(2000, 1, 1, 0, 0, 0), |
|
|
@ -326,7 +307,7 @@ const getToday = function () { |
|
|
|
// 本周 |
|
|
|
const getWeek = function () { |
|
|
|
const today = dayjs() |
|
|
|
const startTime =((today.startOf('week').add(1, 'day')).format('YYYY-MM-DD HH:mm:ss')) |
|
|
|
const startTime = ((today.startOf('week').add(1, 'day')).format('YYYY-MM-DD HH:mm:ss')) |
|
|
|
const endTime = (today.endOf('week').add(1, 'day')).format('YYYY-MM-DD HH:mm:ss') |
|
|
|
// const endTime = today.add(1, 'week').startOf('week').add(1, 'day').format('YYYY-MM-DD HH:mm:ss') |
|
|
|
dateRange.value = [startTime, endTime] |
|
|
@ -629,7 +610,7 @@ const updateChart = (chartData) => { |
|
|
|
type: 'bar', |
|
|
|
stack: 'recharge', |
|
|
|
data: chartData.rechargePermanent, |
|
|
|
itemStyle: {color: '#5470c6'}, |
|
|
|
itemStyle: { color: '#5470c6' }, |
|
|
|
barWidth: 30 |
|
|
|
}, |
|
|
|
{ |
|
|
@ -637,7 +618,7 @@ const updateChart = (chartData) => { |
|
|
|
type: 'bar', |
|
|
|
stack: 'recharge', |
|
|
|
data: chartData.rechargeFree, |
|
|
|
itemStyle: {color: '#91cc75'}, |
|
|
|
itemStyle: { color: '#91cc75' }, |
|
|
|
barWidth: 30 |
|
|
|
}, |
|
|
|
{ |
|
|
@ -645,7 +626,7 @@ const updateChart = (chartData) => { |
|
|
|
type: 'bar', |
|
|
|
stack: 'recharge', |
|
|
|
data: chartData.rechargeTask, |
|
|
|
itemStyle: {color: '#fac858'}, |
|
|
|
itemStyle: { color: '#fac858' }, |
|
|
|
barWidth: 30 |
|
|
|
} |
|
|
|
] |
|
|
@ -657,7 +638,7 @@ const updateChart = (chartData) => { |
|
|
|
type: 'bar', |
|
|
|
stack: 'consume', |
|
|
|
data: chartData.consumePermanent, |
|
|
|
itemStyle: {color: '#5470c6'}, |
|
|
|
itemStyle: { color: '#5470c6' }, |
|
|
|
barWidth: 30 |
|
|
|
}, |
|
|
|
{ |
|
|
@ -665,7 +646,7 @@ const updateChart = (chartData) => { |
|
|
|
type: 'bar', |
|
|
|
stack: 'consume', |
|
|
|
data: chartData.consumeFree, |
|
|
|
itemStyle: {color: '#91cc75'}, |
|
|
|
itemStyle: { color: '#91cc75' }, |
|
|
|
barWidth: 30 |
|
|
|
}, |
|
|
|
{ |
|
|
@ -673,7 +654,7 @@ const updateChart = (chartData) => { |
|
|
|
type: 'bar', |
|
|
|
stack: 'consume', |
|
|
|
data: chartData.consumeTask, |
|
|
|
itemStyle: {color: '#fac858'}, |
|
|
|
itemStyle: { color: '#fac858' }, |
|
|
|
barWidth: 30 |
|
|
|
} |
|
|
|
] |
|
|
@ -754,7 +735,7 @@ const handleTabChange = () => { |
|
|
|
|
|
|
|
const getAdminData = async function () { |
|
|
|
try { |
|
|
|
const result = await API({url: '/admin/userinfo', data: {}}) |
|
|
|
const result = await API({ url: '/admin/userinfo', data: {} }) |
|
|
|
adminData.value = result |
|
|
|
console.log('用户信息', adminData.value) |
|
|
|
} catch (error) { |
|
|
@ -764,7 +745,7 @@ const getAdminData = async function () { |
|
|
|
// 获取卡片数据 |
|
|
|
const getCardData = async () => { |
|
|
|
try { |
|
|
|
const response = await API({url: '/workbench/getCard', data: {}}) |
|
|
|
const response = await API({ url: '/workbench/getCard', data: {} }) |
|
|
|
workDataUpdateTime.value = response.updateTime |
|
|
|
// 周同比 |
|
|
|
sumWow.value = response.sumWow.toFixed(2) |
|
|
@ -806,6 +787,56 @@ onUnmounted(() => { |
|
|
|
</script> |
|
|
|
|
|
|
|
<style scoped> |
|
|
|
.top { |
|
|
|
height: 5.5%; |
|
|
|
width:100%; |
|
|
|
display: flex; |
|
|
|
margin-bottom: 0.2%; |
|
|
|
.text{ |
|
|
|
margin-left:0.2%; |
|
|
|
width:50%; |
|
|
|
display: flex; |
|
|
|
align-items: center; |
|
|
|
font-size: 18px; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.card { |
|
|
|
height: 28%; |
|
|
|
display: flex; |
|
|
|
justify-content: center; |
|
|
|
} |
|
|
|
|
|
|
|
.graph { |
|
|
|
width: 100%; |
|
|
|
display: flex; |
|
|
|
height: 64%; |
|
|
|
background-color: aqua; |
|
|
|
|
|
|
|
.condition { |
|
|
|
width: 100%; |
|
|
|
height: 1%; |
|
|
|
display: flex; |
|
|
|
align-items: center; |
|
|
|
} |
|
|
|
|
|
|
|
.graph-content { |
|
|
|
flex: 1; |
|
|
|
height: auto; |
|
|
|
display: flex; |
|
|
|
|
|
|
|
.left { |
|
|
|
width: 70%; |
|
|
|
height: auto; |
|
|
|
} |
|
|
|
|
|
|
|
.right { |
|
|
|
flex: 1; |
|
|
|
padding: 0.5% 2%; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.center-card { |
|
|
|
display: flex; |
|
|
|
justify-content: center; |
|
|
@ -813,14 +844,16 @@ onUnmounted(() => { |
|
|
|
} |
|
|
|
|
|
|
|
.margin-bottom { |
|
|
|
margin-bottom: 5px; |
|
|
|
margin-bottom: 0.5%; |
|
|
|
} |
|
|
|
|
|
|
|
.card-item { |
|
|
|
height: 260px; |
|
|
|
width: 25%; |
|
|
|
height: 98%; |
|
|
|
display: flex; |
|
|
|
flex-direction: column; |
|
|
|
justify-content: center; |
|
|
|
margin-right: 0.25%; |
|
|
|
} |
|
|
|
|
|
|
|
.card-title { |
|
|
@ -831,10 +864,6 @@ onUnmounted(() => { |
|
|
|
align-items: center; |
|
|
|
} |
|
|
|
|
|
|
|
.rank-card { |
|
|
|
height: 500px; |
|
|
|
} |
|
|
|
|
|
|
|
.card-large { |
|
|
|
font-weight: bold; |
|
|
|
font-size: 16px; |
|
|
@ -842,13 +871,6 @@ onUnmounted(() => { |
|
|
|
margin-bottom: 15px; |
|
|
|
} |
|
|
|
|
|
|
|
.bar { |
|
|
|
background: white; |
|
|
|
border-radius: 8px; |
|
|
|
padding: 15px; |
|
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); |
|
|
|
} |
|
|
|
|
|
|
|
@keyframes spin { |
|
|
|
0% { |
|
|
|
transform: rotate(0deg); |
|
|
|