项目初始化

This commit is contained in:
2026-03-27 17:38:01 +08:00
parent 1de218de1a
commit 34558bb1c2
5 changed files with 232 additions and 131 deletions

View File

@@ -17,4 +17,5 @@ export interface ChartInfo extends BasicModel<ChartInfo> {
remark?: string; // 运行时长
}
export const HostInfoData = () => defHttp.get<ChartInfo[]>({ url: adminPath + '/sys/analysis/getHostInfo' });
export const HostInfoData = () =>
defHttp.get<ChartInfo[]>({ url: adminPath + '/desktop/analysis/getHostInfo' });

View File

@@ -17,20 +17,24 @@
<div class="card-content">
<div class="note-overview">
<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__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__extra">总数</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 }">{{
item.finished
}}</div>
<div class="metric-item__extra">已完成</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>
@@ -45,15 +49,12 @@
<script setup lang="ts">
import { computed, nextTick, onMounted, onUnmounted, ref } from 'vue';
import * as echarts from 'echarts';
import dayjs from 'dayjs';
import { RefreshRight } from '@element-plus/icons-vue';
import { DictData, dictDataListData } from '@jeesite/core/api/sys/dictData';
import { MyNotes, myNotesListData } from '@jeesite/biz/api/biz/myNotes';
import { NoteInfo, NoteInfoData, ChartDataItem, NoteChartData } from '@jeesite/biz/api/biz/myWorkbench';
interface MetricCard {
key: string;
label: string;
value: number;
color: string;
total: number;
finished: number;
@@ -62,85 +63,51 @@
const noteCardRef = ref<HTMLElement>();
const chartRef = ref<HTMLElement>();
const loading = ref(false);
const noteList = ref<MyNotes[]>([]);
const typeDict = ref<DictData[]>([]);
const statusDict = ref<DictData[]>([]);
const statusGroups = [
{ key: 'pending', label: '待开始', color: '#F97316' },
{ key: 'processing', label: '进行中', color: '#3B82F6' },
{ key: 'finished', label: '已完成', color: '#10B981' },
];
const metricSource = ref<NoteInfo[]>([]);
const chartData = ref<ChartDataItem[]>([]);
const selectedMetricKey = ref('work');
const metricTypeGroups = [
{ key: 'work', label: '工作', color: '#3B82F6' },
{ key: 'life', label: '生活', color: '#10B981' },
{ key: 'study', label: '学习', color: '#F97316' },
{ key: 'other', label: '其他', color: '#8B5CF6' },
];
const metricCards = computed<MetricCard[]>(() => {
const colors = ['#3B82F6', '#10B981', '#F97316', '#8B5CF6', '#EC4899', '#06B6D4'];
return metricSource.value.map((item, index) => {
return {
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 resizeObserver: ResizeObserver | 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() {
return Array.from({ length: 12 }, (_, index) => `${index + 1}`);
}
function getNoteMonth(item: MyNotes) {
const dateValue = item.createTime || item.startTime || item.deadline || item.updateTime;
return dateValue ? dayjs(dateValue).format('M月') : '';
}
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 handleMetricClick(key: string) {
selectedMetricKey.value = key;
await getChartData();
nextTick(() => {
renderChart();
});
}
async function getList() {
loading.value = true;
try {
const res = await myNotesListData({ pageNum: 1, pageSize: 999 });
noteList.value = res?.list || [];
const [metricRes, metricChartRes] = await Promise.all([
NoteInfoData(),
NoteChartData({ type: selectedMetricKey.value }),
]);
metricSource.value = metricRes || [];
chartData.value = metricChartRes || [];
} catch (error) {
noteList.value = [];
metricSource.value = [];
chartData.value = [];
} finally {
loading.value = false;
nextTick(() => {
@@ -149,43 +116,56 @@
}
}
function buildChartData() {
const months = getMonthList();
const totals = months.map((month) => {
return statusGroups.reduce((sum, status) => {
return (
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;
}),
async function getChartData() {
loading.value = true;
try {
const reqParams = {
type: selectedMetricKey.value,
};
});
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({
name: '总数',
@@ -225,9 +205,8 @@
chartInstance = echarts.init(chartRef.value);
}
const { categories, series } = buildChartData();
const { categories, series, title } = buildChartData();
const isDark = document.documentElement.getAttribute('data-theme') === 'dark';
const totalSeries = series.find((item) => item.name === '总数');
if (totalSeries?.label) {
totalSeries.label.color = isDark ? '#cbd5e1' : '#475569';
@@ -237,10 +216,20 @@
grid: {
left: 12,
right: 12,
top: 40,
bottom: 6,
top: 52,
bottom: 10,
containLabel: true,
},
title: {
text: title,
left: 12,
top: 6,
textStyle: {
color: isDark ? '#e2e8f0' : '#334155',
fontSize: 13,
fontWeight: 600,
},
},
tooltip: {
trigger: 'axis',
backgroundColor: isDark ? 'rgba(20, 20, 20, 0.96)' : 'rgba(255, 255, 255, 0.96)',
@@ -254,11 +243,11 @@
},
},
legend: {
top: 4,
top: 6,
left: 'center',
selectedMode: true,
itemGap: 16,
data: statusGroups.map((item) => item.label),
data: ['待开始', '进行中', '已完成'],
textStyle: {
color: isDark ? '#e2e8f0' : '#475569',
},
@@ -306,9 +295,7 @@
}
onMounted(async () => {
await getDict();
await getList();
if (noteCardRef.value) {
resizeObserver = new ResizeObserver(() => {
resizeChart();
@@ -402,6 +389,19 @@
justify-content: space-between;
align-items: stretch;
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 {
display: flex;
@@ -425,14 +425,6 @@
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 {
font-size: 28px;
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 {
display: flex;
align-items: center;
@@ -457,6 +457,7 @@
font-size: 12px;
line-height: 16px;
text-align: center;
cursor: pointer;
}
}
@@ -490,18 +491,23 @@
}
.metric-item {
&--active {
box-shadow: 0 14px 30px rgb(37 99 235 / 20%);
outline-color: rgb(96 165 250);
}
&__pane {
background: linear-gradient(180deg, rgb(20, 20, 20) 0%, rgb(28 28 28) 100%);
box-shadow: 0 10px 24px rgb(0 0 0 / 24%);
}
&__extra {
&__extra,
&__label {
color: rgb(148 163 184);
}
&__label {
border-top-color: rgb(51 65 85);
color: rgb(148 163 184);
}
}
}
@@ -515,10 +521,6 @@
.note-metrics {
grid-template-rows: repeat(2, minmax(88px, 1fr));
}
.note-chart {
min-height: 0;
}
}
}
</style>