项目初始化
This commit is contained in:
@@ -0,0 +1,22 @@
|
|||||||
|
package com.jeesite.modules.apps.Module;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class NoteInfo implements Serializable {
|
||||||
|
private String key;
|
||||||
|
private String label;
|
||||||
|
private Long value01;
|
||||||
|
private Long value02;
|
||||||
|
|
||||||
|
public NoteInfo(){}
|
||||||
|
|
||||||
|
public NoteInfo(String key,String label,Long value01,Long value02){
|
||||||
|
this.key = key;
|
||||||
|
this.label = label;
|
||||||
|
this.value01 = value01;
|
||||||
|
this.value02 = value02;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
@RequestMapping(value = "${adminPath}/sys/analysis")
|
@RequestMapping(value = "${adminPath}/desktop/analysis")
|
||||||
public class SysAnalysisController {
|
public class SysAnalysisController {
|
||||||
|
|
||||||
@RequestMapping(value = "getHostInfo")
|
@RequestMapping(value = "getHostInfo")
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
package com.jeesite.modules.apps.web;
|
||||||
|
|
||||||
|
import com.jeesite.modules.apps.Module.ChartDataItem;
|
||||||
|
import com.jeesite.modules.apps.Module.NoteInfo;
|
||||||
|
import com.jeesite.modules.biz.entity.MyNotes;
|
||||||
|
import com.jeesite.modules.biz.service.MyNotesService;
|
||||||
|
import com.jeesite.modules.sys.entity.DictData;
|
||||||
|
import com.jeesite.modules.sys.utils.DictUtils;
|
||||||
|
import com.jeesite.modules.utils.DateUtils;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
@RequestMapping(value = "${adminPath}/desktop/workbench")
|
||||||
|
public class SysWorkbenchController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private MyNotesService myNotesService;
|
||||||
|
|
||||||
|
@RequestMapping(value = "getNoteInfo")
|
||||||
|
@ResponseBody
|
||||||
|
public List<NoteInfo> getNoteInfo() {
|
||||||
|
List<NoteInfo> noteInfos = new ArrayList<>();
|
||||||
|
List<DictData> dictDataList = DictUtils.getDictList("note_type");
|
||||||
|
List<MyNotes> myNotesList = myNotesService.findList(new MyNotes());
|
||||||
|
Map<String, Long> totalMap = myNotesList.stream()
|
||||||
|
.collect(Collectors.groupingBy(MyNotes::getType, Collectors.counting()));
|
||||||
|
Map<String, Long> ustatusMap = myNotesList.stream()
|
||||||
|
.filter(note -> note.getUstatus().equals("done"))
|
||||||
|
.collect(Collectors.groupingBy(MyNotes::getType, Collectors.counting()));
|
||||||
|
for (DictData dict : dictDataList) {
|
||||||
|
String type = dict.getDictValue();
|
||||||
|
Long value01 = totalMap.getOrDefault(type, 0L);
|
||||||
|
Long value02 = ustatusMap.getOrDefault(type, 0L);
|
||||||
|
noteInfos.add(new NoteInfo(dict.getDictValue(), dict.getDictLabel(), value01, value02));
|
||||||
|
}
|
||||||
|
return noteInfos;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@RequestMapping(value = "getNoteChart")
|
||||||
|
@ResponseBody
|
||||||
|
public List<ChartDataItem> getNoteChart(MyNotes myNotes) {
|
||||||
|
List<MyNotes> myNotesList = myNotesService.findList(myNotes);
|
||||||
|
Map<String, ChartDataItem> chartDataMap = myNotesList.stream()
|
||||||
|
.collect(Collectors.groupingBy(
|
||||||
|
note -> Optional.of(DateUtils.getMonth(note.getCreateTime())).orElse(""),
|
||||||
|
Collectors.collectingAndThen(Collectors.toList(), list -> {
|
||||||
|
String month = list.stream()
|
||||||
|
.findFirst()
|
||||||
|
.map(MyNotes::getCreateTime)
|
||||||
|
.map(DateUtils::getMonth)
|
||||||
|
.orElse("");
|
||||||
|
Long todoCount = list.stream().filter(n -> n.getUstatus().equals("todo")).count();
|
||||||
|
Long doingCount = list.stream().filter(n -> n.getUstatus().equals("doing")).count();
|
||||||
|
Long doneCount = list.stream().filter(n -> n.getUstatus().equals("done")).count();
|
||||||
|
ChartDataItem item = new ChartDataItem();
|
||||||
|
item.setAxisName(month);
|
||||||
|
item.setValue01(String.valueOf(todoCount));
|
||||||
|
item.setValue02(String.valueOf(doingCount));
|
||||||
|
item.setValue03(String.valueOf(doneCount));
|
||||||
|
return item;
|
||||||
|
})
|
||||||
|
));
|
||||||
|
return chartDataMap.values().stream()
|
||||||
|
.sorted(Comparator.comparing(ChartDataItem::getAxisName))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -17,4 +17,5 @@ export interface ChartInfo extends BasicModel<ChartInfo> {
|
|||||||
remark?: string; // 运行时长
|
remark?: string; // 运行时长
|
||||||
}
|
}
|
||||||
|
|
||||||
export const HostInfoData = () => defHttp.get<ChartInfo[]>({ url: adminPath + '/sys/analysis/getHostInfo' });
|
export const HostInfoData = () =>
|
||||||
|
defHttp.get<ChartInfo[]>({ url: adminPath + '/desktop/analysis/getHostInfo' });
|
||||||
|
|||||||
@@ -17,20 +17,24 @@
|
|||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<div class="note-overview">
|
<div class="note-overview">
|
||||||
<div class="note-metrics">
|
<div class="note-metrics">
|
||||||
<div v-for="item in metricCards" :key="item.key" class="metric-item">
|
<div
|
||||||
|
v-for="item in metricCards"
|
||||||
|
:key="item.key"
|
||||||
|
:class="['metric-item', { 'metric-item--active': selectedMetricKey === item.key }]"
|
||||||
|
>
|
||||||
<div class="metric-item__main">
|
<div class="metric-item__main">
|
||||||
<div class="metric-item__pane metric-item__pane--left">
|
<div class="metric-item__pane">
|
||||||
<div class="metric-item__value" :style="{ color: item.color }">{{ item.total }}</div>
|
<div class="metric-item__value" :style="{ color: item.color }">{{ item.total }}</div>
|
||||||
<div class="metric-item__extra">总数</div>
|
<div class="metric-item__extra">总数</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="metric-item__pane metric-item__pane--right">
|
<div class="metric-item__pane">
|
||||||
<div class="metric-item__value metric-item__value--small" :style="{ color: item.color }">{{
|
<div class="metric-item__value metric-item__value--small" :style="{ color: item.color }">{{
|
||||||
item.finished
|
item.finished
|
||||||
}}</div>
|
}}</div>
|
||||||
<div class="metric-item__extra">已完成</div>
|
<div class="metric-item__extra">已完成</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="metric-item__label">{{ item.label }}</div>
|
<div class="metric-item__label" @click="handleMetricClick(item.key)">{{ item.label }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -45,15 +49,12 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, nextTick, onMounted, onUnmounted, ref } from 'vue';
|
import { computed, nextTick, onMounted, onUnmounted, ref } from 'vue';
|
||||||
import * as echarts from 'echarts';
|
import * as echarts from 'echarts';
|
||||||
import dayjs from 'dayjs';
|
|
||||||
import { RefreshRight } from '@element-plus/icons-vue';
|
import { RefreshRight } from '@element-plus/icons-vue';
|
||||||
import { DictData, dictDataListData } from '@jeesite/core/api/sys/dictData';
|
import { NoteInfo, NoteInfoData, ChartDataItem, NoteChartData } from '@jeesite/biz/api/biz/myWorkbench';
|
||||||
import { MyNotes, myNotesListData } from '@jeesite/biz/api/biz/myNotes';
|
|
||||||
|
|
||||||
interface MetricCard {
|
interface MetricCard {
|
||||||
key: string;
|
key: string;
|
||||||
label: string;
|
label: string;
|
||||||
value: number;
|
|
||||||
color: string;
|
color: string;
|
||||||
total: number;
|
total: number;
|
||||||
finished: number;
|
finished: number;
|
||||||
@@ -62,85 +63,51 @@
|
|||||||
const noteCardRef = ref<HTMLElement>();
|
const noteCardRef = ref<HTMLElement>();
|
||||||
const chartRef = ref<HTMLElement>();
|
const chartRef = ref<HTMLElement>();
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const noteList = ref<MyNotes[]>([]);
|
const metricSource = ref<NoteInfo[]>([]);
|
||||||
const typeDict = ref<DictData[]>([]);
|
const chartData = ref<ChartDataItem[]>([]);
|
||||||
const statusDict = ref<DictData[]>([]);
|
const selectedMetricKey = ref('work');
|
||||||
const statusGroups = [
|
|
||||||
{ key: 'pending', label: '待开始', color: '#F97316' },
|
|
||||||
{ key: 'processing', label: '进行中', color: '#3B82F6' },
|
|
||||||
{ key: 'finished', label: '已完成', color: '#10B981' },
|
|
||||||
];
|
|
||||||
|
|
||||||
const metricTypeGroups = [
|
const metricCards = computed<MetricCard[]>(() => {
|
||||||
{ key: 'work', label: '工作', color: '#3B82F6' },
|
const colors = ['#3B82F6', '#10B981', '#F97316', '#8B5CF6', '#EC4899', '#06B6D4'];
|
||||||
{ key: 'life', label: '生活', color: '#10B981' },
|
return metricSource.value.map((item, index) => {
|
||||||
{ key: 'study', label: '学习', color: '#F97316' },
|
return {
|
||||||
{ key: 'other', label: '其他', color: '#8B5CF6' },
|
key: item.key,
|
||||||
];
|
label: item.label,
|
||||||
|
color: colors[index % colors.length],
|
||||||
|
total: Number(item.value01 || 0),
|
||||||
|
finished: Number(item.value02 || 0),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
let chartInstance: echarts.ECharts | null = null;
|
let chartInstance: echarts.ECharts | null = null;
|
||||||
let resizeObserver: ResizeObserver | null = null;
|
let resizeObserver: ResizeObserver | null = null;
|
||||||
let themeObserver: MutationObserver | null = null;
|
let themeObserver: MutationObserver | null = null;
|
||||||
|
|
||||||
const metricCards = computed<MetricCard[]>(() => {
|
|
||||||
return metricTypeGroups.map((group) => ({
|
|
||||||
key: group.key,
|
|
||||||
label: group.label,
|
|
||||||
color: group.color,
|
|
||||||
value: noteList.value.filter((item) => matchTypeGroup(item.type, group.label)).length,
|
|
||||||
total: noteList.value.filter((item) => matchTypeGroup(item.type, group.label)).length,
|
|
||||||
finished: noteList.value.filter((item) => matchTypeGroup(item.type, group.label) && item.ustatus === '1').length,
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
function getDictLabel(dictList: DictData[], value?: string) {
|
|
||||||
return dictList.find((item) => item.dictValue === value)?.dictLabelRaw || value || '-';
|
|
||||||
}
|
|
||||||
|
|
||||||
function matchTypeGroup(typeValue: string | undefined, targetLabel: string) {
|
|
||||||
return getDictLabel(typeDict.value, typeValue) === targetLabel;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getStatusGroup(item: MyNotes) {
|
|
||||||
const label = getDictLabel(statusDict.value, item.ustatus);
|
|
||||||
if (label.includes('完成')) return '已完成';
|
|
||||||
if (label.includes('进行')) return '进行中';
|
|
||||||
if (label.includes('开始')) return '待开始';
|
|
||||||
if (item.ustatus === '1') return '已完成';
|
|
||||||
if (item.ustatus === '2') return '进行中';
|
|
||||||
return '待开始';
|
|
||||||
}
|
|
||||||
|
|
||||||
function getMonthList() {
|
function getMonthList() {
|
||||||
return Array.from({ length: 12 }, (_, index) => `${index + 1}月`);
|
return Array.from({ length: 12 }, (_, index) => `${index + 1}月`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNoteMonth(item: MyNotes) {
|
async function handleMetricClick(key: string) {
|
||||||
const dateValue = item.createTime || item.startTime || item.deadline || item.updateTime;
|
selectedMetricKey.value = key;
|
||||||
return dateValue ? dayjs(dateValue).format('M月') : '';
|
await getChartData();
|
||||||
}
|
nextTick(() => {
|
||||||
|
renderChart();
|
||||||
async function getDict() {
|
});
|
||||||
try {
|
|
||||||
const [typeRes, statusRes] = await Promise.all([
|
|
||||||
dictDataListData({ dictType: 'note_type' }),
|
|
||||||
dictDataListData({ dictType: 'note_status' }),
|
|
||||||
]);
|
|
||||||
typeDict.value = typeRes || [];
|
|
||||||
statusDict.value = statusRes || [];
|
|
||||||
} catch (error) {
|
|
||||||
typeDict.value = [];
|
|
||||||
statusDict.value = [];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getList() {
|
async function getList() {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
const res = await myNotesListData({ pageNum: 1, pageSize: 999 });
|
const [metricRes, metricChartRes] = await Promise.all([
|
||||||
noteList.value = res?.list || [];
|
NoteInfoData(),
|
||||||
|
NoteChartData({ type: selectedMetricKey.value }),
|
||||||
|
]);
|
||||||
|
metricSource.value = metricRes || [];
|
||||||
|
chartData.value = metricChartRes || [];
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
noteList.value = [];
|
metricSource.value = [];
|
||||||
|
chartData.value = [];
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
@@ -149,43 +116,56 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildChartData() {
|
async function getChartData() {
|
||||||
const months = getMonthList();
|
loading.value = true;
|
||||||
const totals = months.map((month) => {
|
try {
|
||||||
return statusGroups.reduce((sum, status) => {
|
const reqParams = {
|
||||||
return (
|
type: selectedMetricKey.value,
|
||||||
sum +
|
|
||||||
noteList.value.filter((item) => getNoteMonth(item) === month && getStatusGroup(item) === status.label).length
|
|
||||||
);
|
|
||||||
}, 0);
|
|
||||||
});
|
|
||||||
|
|
||||||
const series: echarts.BarSeriesOption[] = statusGroups.map((group) => {
|
|
||||||
return {
|
|
||||||
name: group.label,
|
|
||||||
type: 'bar',
|
|
||||||
stack: 'total',
|
|
||||||
barWidth: '24%',
|
|
||||||
emphasis: {
|
|
||||||
focus: 'series',
|
|
||||||
},
|
|
||||||
label: {
|
|
||||||
show: true,
|
|
||||||
position: 'inside',
|
|
||||||
formatter: ({ value }) => (Number(value) > 0 ? `${value}` : ''),
|
|
||||||
color: '#ffffff',
|
|
||||||
fontSize: 11,
|
|
||||||
},
|
|
||||||
itemStyle: {
|
|
||||||
color: group.color,
|
|
||||||
borderRadius: 0,
|
|
||||||
},
|
|
||||||
data: months.map((month) => {
|
|
||||||
return noteList.value.filter((item) => getNoteMonth(item) === month && getStatusGroup(item) === group.label)
|
|
||||||
.length;
|
|
||||||
}),
|
|
||||||
};
|
};
|
||||||
});
|
const metricRes = await NoteChartData(reqParams);
|
||||||
|
chartData.value = metricRes || [];
|
||||||
|
} catch (error) {
|
||||||
|
chartData.value = [];
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildChartData() {
|
||||||
|
const statusGroups = [
|
||||||
|
{ label: '待开始', color: '#F97316', field: 'value01' },
|
||||||
|
{ label: '进行中', color: '#3B82F6', field: 'value02' },
|
||||||
|
{ label: '已完成', color: '#10B981', field: 'value03' },
|
||||||
|
] as const;
|
||||||
|
const months = chartData.value.map((item) => item.axisName || '-');
|
||||||
|
const pendingData = chartData.value.map((item) => Number(item.value01 || 0));
|
||||||
|
const processingData = chartData.value.map((item) => Number(item.value02 || 0));
|
||||||
|
const finishedData = chartData.value.map((item) => Number(item.value03 || 0));
|
||||||
|
const totals = chartData.value.map(
|
||||||
|
(item) => Number(item.value01 || 0) + Number(item.value02 || 0) + Number(item.value03 || 0),
|
||||||
|
);
|
||||||
|
|
||||||
|
const series: echarts.BarSeriesOption[] = statusGroups.map((group, groupIndex) => ({
|
||||||
|
name: group.label,
|
||||||
|
type: 'bar',
|
||||||
|
stack: 'total',
|
||||||
|
barWidth: '24%',
|
||||||
|
emphasis: {
|
||||||
|
focus: 'series',
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
position: 'inside',
|
||||||
|
formatter: ({ value }) => (Number(value) > 0 ? `${value}` : ''),
|
||||||
|
color: '#ffffff',
|
||||||
|
fontSize: 11,
|
||||||
|
},
|
||||||
|
itemStyle: {
|
||||||
|
color: group.color,
|
||||||
|
borderRadius: 0,
|
||||||
|
},
|
||||||
|
data: [pendingData, processingData, finishedData][groupIndex] || [],
|
||||||
|
}));
|
||||||
|
|
||||||
series.push({
|
series.push({
|
||||||
name: '总数',
|
name: '总数',
|
||||||
@@ -225,9 +205,8 @@
|
|||||||
chartInstance = echarts.init(chartRef.value);
|
chartInstance = echarts.init(chartRef.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { categories, series } = buildChartData();
|
const { categories, series, title } = buildChartData();
|
||||||
const isDark = document.documentElement.getAttribute('data-theme') === 'dark';
|
const isDark = document.documentElement.getAttribute('data-theme') === 'dark';
|
||||||
|
|
||||||
const totalSeries = series.find((item) => item.name === '总数');
|
const totalSeries = series.find((item) => item.name === '总数');
|
||||||
if (totalSeries?.label) {
|
if (totalSeries?.label) {
|
||||||
totalSeries.label.color = isDark ? '#cbd5e1' : '#475569';
|
totalSeries.label.color = isDark ? '#cbd5e1' : '#475569';
|
||||||
@@ -237,10 +216,20 @@
|
|||||||
grid: {
|
grid: {
|
||||||
left: 12,
|
left: 12,
|
||||||
right: 12,
|
right: 12,
|
||||||
top: 40,
|
top: 52,
|
||||||
bottom: 6,
|
bottom: 10,
|
||||||
containLabel: true,
|
containLabel: true,
|
||||||
},
|
},
|
||||||
|
title: {
|
||||||
|
text: title,
|
||||||
|
left: 12,
|
||||||
|
top: 6,
|
||||||
|
textStyle: {
|
||||||
|
color: isDark ? '#e2e8f0' : '#334155',
|
||||||
|
fontSize: 13,
|
||||||
|
fontWeight: 600,
|
||||||
|
},
|
||||||
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'axis',
|
trigger: 'axis',
|
||||||
backgroundColor: isDark ? 'rgba(20, 20, 20, 0.96)' : 'rgba(255, 255, 255, 0.96)',
|
backgroundColor: isDark ? 'rgba(20, 20, 20, 0.96)' : 'rgba(255, 255, 255, 0.96)',
|
||||||
@@ -254,11 +243,11 @@
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
top: 4,
|
top: 6,
|
||||||
left: 'center',
|
left: 'center',
|
||||||
selectedMode: true,
|
selectedMode: true,
|
||||||
itemGap: 16,
|
itemGap: 16,
|
||||||
data: statusGroups.map((item) => item.label),
|
data: ['待开始', '进行中', '已完成'],
|
||||||
textStyle: {
|
textStyle: {
|
||||||
color: isDark ? '#e2e8f0' : '#475569',
|
color: isDark ? '#e2e8f0' : '#475569',
|
||||||
},
|
},
|
||||||
@@ -306,9 +295,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await getDict();
|
|
||||||
await getList();
|
await getList();
|
||||||
|
|
||||||
if (noteCardRef.value) {
|
if (noteCardRef.value) {
|
||||||
resizeObserver = new ResizeObserver(() => {
|
resizeObserver = new ResizeObserver(() => {
|
||||||
resizeChart();
|
resizeChart();
|
||||||
@@ -402,6 +389,19 @@
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
|
transition:
|
||||||
|
transform 0.2s ease,
|
||||||
|
box-shadow 0.2s ease,
|
||||||
|
border-color 0.2s ease;
|
||||||
|
|
||||||
|
&--active {
|
||||||
|
box-shadow: 0 12px 28px rgb(59 130 246 / 18%);
|
||||||
|
outline: 1px solid rgb(147 197 253);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
&__main {
|
&__main {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -425,14 +425,6 @@
|
|||||||
box-shadow: 0 8px 24px rgb(148 163 184 / 14%);
|
box-shadow: 0 8px 24px rgb(148 163 184 / 14%);
|
||||||
}
|
}
|
||||||
|
|
||||||
&__extra {
|
|
||||||
margin-top: 8px;
|
|
||||||
color: rgb(100 116 139);
|
|
||||||
font-size: 12px;
|
|
||||||
line-height: 16px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__value {
|
&__value {
|
||||||
font-size: 28px;
|
font-size: 28px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
@@ -444,6 +436,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__extra {
|
||||||
|
margin-top: 8px;
|
||||||
|
color: rgb(100 116 139);
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 16px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
&__label {
|
&__label {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -457,6 +457,7 @@
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 16px;
|
line-height: 16px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -490,18 +491,23 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.metric-item {
|
.metric-item {
|
||||||
|
&--active {
|
||||||
|
box-shadow: 0 14px 30px rgb(37 99 235 / 20%);
|
||||||
|
outline-color: rgb(96 165 250);
|
||||||
|
}
|
||||||
|
|
||||||
&__pane {
|
&__pane {
|
||||||
background: linear-gradient(180deg, rgb(20, 20, 20) 0%, rgb(28 28 28) 100%);
|
background: linear-gradient(180deg, rgb(20, 20, 20) 0%, rgb(28 28 28) 100%);
|
||||||
box-shadow: 0 10px 24px rgb(0 0 0 / 24%);
|
box-shadow: 0 10px 24px rgb(0 0 0 / 24%);
|
||||||
}
|
}
|
||||||
|
|
||||||
&__extra {
|
&__extra,
|
||||||
|
&__label {
|
||||||
color: rgb(148 163 184);
|
color: rgb(148 163 184);
|
||||||
}
|
}
|
||||||
|
|
||||||
&__label {
|
&__label {
|
||||||
border-top-color: rgb(51 65 85);
|
border-top-color: rgb(51 65 85);
|
||||||
color: rgb(148 163 184);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -515,10 +521,6 @@
|
|||||||
.note-metrics {
|
.note-metrics {
|
||||||
grid-template-rows: repeat(2, minmax(88px, 1fr));
|
grid-template-rows: repeat(2, minmax(88px, 1fr));
|
||||||
}
|
}
|
||||||
|
|
||||||
.note-chart {
|
|
||||||
min-height: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user