统计分析

This commit is contained in:
Soutfairy 2024-11-26 11:35:23 +08:00
parent 0f3a5de20e
commit 14948c4357
9 changed files with 244 additions and 1 deletions

63
package-lock.json generated
View File

@ -13,6 +13,7 @@
"@wangeditor/editor": "^5.1.23",
"@wangeditor/editor-for-vue": "^5.1.12",
"axios": "^1.6.8",
"chart.js": "^4.4.6",
"countup.js": "^2.8.0",
"cropperjs": "^1.6.1",
"echarts": "^5.5.0",
@ -23,6 +24,7 @@
"js-table2excel": "^1.1.2",
"jsplumb": "^2.15.6",
"mitt": "^3.0.1",
"mockjs": "^1.1.0",
"nprogress": "^0.2.0",
"pinia": "^2.1.7",
"print-js": "^1.6.0",
@ -39,6 +41,8 @@
"vue-router": "^4.3.0"
},
"devDependencies": {
"@types/chart.js": "^2.9.41",
"@types/mockjs": "^1.0.10",
"@types/node": "^20.11.28",
"@types/nprogress": "^0.2.3",
"@types/sortablejs": "^1.15.8",
@ -412,6 +416,11 @@
"version": "1.4.15",
"license": "MIT"
},
"node_modules/@kurkle/color": {
"version": "0.3.4",
"resolved": "https://registry.npmmirror.com/@kurkle/color/-/color-0.3.4.tgz",
"integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w=="
},
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"dev": true,
@ -472,6 +481,15 @@
"version": "0.0.7",
"license": "MIT"
},
"node_modules/@types/chart.js": {
"version": "2.9.41",
"resolved": "https://registry.npmmirror.com/@types/chart.js/-/chart.js-2.9.41.tgz",
"integrity": "sha512-3dvkDvueckY83UyUXtJMalYoH6faOLkWQoaTlJgB4Djde3oORmNP0Jw85HtzTuXyliUHcdp704s0mZFQKio/KQ==",
"dev": true,
"dependencies": {
"moment": "^2.10.2"
}
},
"node_modules/@types/estree": {
"version": "1.0.5",
"dev": true,
@ -497,6 +515,12 @@
"@types/lodash": "*"
}
},
"node_modules/@types/mockjs": {
"version": "1.0.10",
"resolved": "https://registry.npmmirror.com/@types/mockjs/-/mockjs-1.0.10.tgz",
"integrity": "sha512-SXgrhajHG7boLv6oU93CcmdDm0HYRiceuz6b+7z+/2lCJPTWDv0V5YiwFHT2ejE4bQqgSXQiVPQYPWv7LGsK1g==",
"dev": true
},
"node_modules/@types/node": {
"version": "20.11.28",
"dev": true,
@ -1245,6 +1269,17 @@
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/chart.js": {
"version": "4.4.6",
"resolved": "https://registry.npmmirror.com/chart.js/-/chart.js-4.4.6.tgz",
"integrity": "sha512-8Y406zevUPbbIBA/HRk33khEmQPk5+cxeflWE/2rx1NJsjVWMPw/9mSP9rxHP5eqi6LNoPBVMfZHxbwLSgldYA==",
"dependencies": {
"@kurkle/color": "^0.3.0"
},
"engines": {
"pnpm": ">=8"
}
},
"node_modules/chokidar": {
"version": "3.5.3",
"dev": true,
@ -1359,6 +1394,14 @@
"node": ">= 0.8"
}
},
"node_modules/commander": {
"version": "12.1.0",
"resolved": "https://registry.npmmirror.com/commander/-/commander-12.1.0.tgz",
"integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
"engines": {
"node": ">=18"
}
},
"node_modules/compute-scroll-into-view": {
"version": "1.0.20",
"license": "MIT"
@ -2538,6 +2581,26 @@
"mkdirp": "bin/cmd.js"
}
},
"node_modules/mockjs": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/mockjs/-/mockjs-1.1.0.tgz",
"integrity": "sha512-eQsKcWzIaZzEZ07NuEyO4Nw65g0hdWAyurVol1IPl1gahRwY+svqzfgfey8U8dahLwG44d6/RwEzuK52rSa/JQ==",
"dependencies": {
"commander": "*"
},
"bin": {
"random": "bin/random"
}
},
"node_modules/moment": {
"version": "2.30.1",
"resolved": "https://registry.npmmirror.com/moment/-/moment-2.30.1.tgz",
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
"dev": true,
"engines": {
"node": "*"
}
},
"node_modules/ms": {
"version": "2.1.2",
"dev": true,

View File

@ -14,6 +14,7 @@
"@wangeditor/editor": "^5.1.23",
"@wangeditor/editor-for-vue": "^5.1.12",
"axios": "^1.6.8",
"chart.js": "^4.4.6",
"countup.js": "^2.8.0",
"cropperjs": "^1.6.1",
"echarts": "^5.5.0",
@ -24,6 +25,7 @@
"js-table2excel": "^1.1.2",
"jsplumb": "^2.15.6",
"mitt": "^3.0.1",
"mockjs": "^1.1.0",
"nprogress": "^0.2.0",
"pinia": "^2.1.7",
"print-js": "^1.6.0",
@ -40,6 +42,8 @@
"vue-router": "^4.3.0"
},
"devDependencies": {
"@types/chart.js": "^2.9.41",
"@types/mockjs": "^1.0.10",
"@types/node": "^20.11.28",
"@types/nprogress": "^0.2.3",
"@types/sortablejs": "^1.15.8",

39
src/api/mock/index.ts Normal file
View File

@ -0,0 +1,39 @@
// src/api/mock/index.ts
import { Random } from 'mockjs';
interface StatisticsData {
todayVisits: number;
newUsers: number;
currentMonthSales: number;
currentMonthOrders: number;
}
interface YearlySalesData {
monthlySales: number[];
}
// 模拟统计数据接口
export const getStatisticsData = async (): Promise<StatisticsData> => {
return new Promise((resolve) => {
// 模拟 API 延迟
setTimeout(() => {
resolve({
todayVisits: Random.integer(500, 1500),
newUsers: Random.integer(20, 100),
currentMonthSales: Random.integer(3000, 15000),
currentMonthOrders: Random.integer(100, 500),
});
}, 1000);
});
};
// 模拟年度销售数据接口
export const getYearlySalesData = async (): Promise<YearlySalesData> => {
return new Promise((resolve) => {
// 模拟 API 延迟
setTimeout(() => {
const monthlySales = Array.from({ length: 12 }, () => Random.integer(5000, 20000));
resolve({ monthlySales });
}, 1000);
});
};

View File

@ -13,6 +13,7 @@ export default {
article: 'article',
message: 'message',
order: 'order',
statistics: 'statistics',
articleDetail: 'articleDetail',
addArticle: 'addArticle',
editArticle: 'editArticle',

View File

@ -13,6 +13,7 @@ export default {
article: '文章管理',
message: '留言管理',
order: '订单管理',
statistics: '统计分析',
articleDetail: '文章详情',
addArticle: '新增文章',
editArticle: '编辑文章',

View File

@ -13,6 +13,7 @@ export default {
article: '文章管理',
message: '聯言管理',
order: '訂單管理',
statistics: '統計分析',
articleDetail: '文章詳情',
addArticle: '文章新增',
editArticle: '文章編輯',

View File

@ -295,6 +295,21 @@ export const dynamicRoutes: Array<RouteRecordRaw> = [
icon: 'iconfont icon-shuju',
},
},
{
path: '/statistics',
name: 'statistics',
component: () => import('/@/views/statistics/index.vue'),
meta: {
title: 'message.router.statistics',
isLink: '',
isHide: false,
isKeepAlive: true,
isAffix: false,
isIframe: false,
roles: ['admin', 'common'],
icon: 'iconfont icon-ico_shuju',
},
},
{
path: '/video',
name: 'video',

1
src/types/chartjs.d.ts vendored Normal file
View File

@ -0,0 +1 @@
declare module 'chart.js';

View File

@ -0,0 +1,118 @@
<template>
<div class="statistics">
<h1>统计分析</h1>
<div class="cards">
<div class="card" v-for="(item, index) in statistics" :key="index">
<h2>{{ item.title }}</h2>
<p class="val">{{ item.value }}</p>
</div>
</div>
<div class="chart">
<h2>全年销售额分析图</h2>
<canvas ref="salesChart"></canvas>
</div>
</div>
</template>
<script lang="ts">
import { ref, onMounted } from 'vue';
import { Chart, registerables } from 'chart.js';
import { getStatisticsData, getYearlySalesData } from '/@/api/mock';
//
interface Statistic {
title: string;
value: string | number;
}
Chart.register(...registerables);
export default {
name: 'Statistics',
setup() {
const statistics = ref<Statistic[]>([]);
const salesChart = ref<HTMLCanvasElement | null>(null);
const fetchStatistics = async () => {
const data = await getStatisticsData();
statistics.value = [
{ title: '今日访问量', value: '+' + data.todayVisits },
{ title: '新增用户', value: '+' + data.newUsers },
{ title: '当月销售额', value: data.currentMonthSales },
{ title: '当月订单量', value: data.currentMonthOrders },
];
};
const fetchYearlySales = async () => {
if (salesChart.value) {
const ctx = salesChart.value.getContext('2d');
const data = await getYearlySalesData();
new Chart(ctx!, {
type: 'line',
data: {
labels: [
'一月', '二月', '三月', '四月', '五月', '六月',
'七月', '八月', '九月', '十月', '十一月', '十二月'
],
datasets: [{
label: '全年销售额',
data: data.monthlySales,
borderColor: 'rgba(75, 192, 192, 1)',
backgroundColor: 'rgba(75, 192, 192, 0.2)',
borderWidth: 1,
}]
},
options: {
responsive: true,
scales: {
y: {
beginAtZero: true
}
}
}
});
}
};
onMounted(async () => {
await fetchStatistics();
await fetchYearlySales();
});
return {
statistics,
salesChart
};
}
};
</script>
<style scoped>
.val {
margin-top: 10px;
}
.statistics {
padding: 20px;
}
.cards {
display: flex;
gap: 20px;
margin-bottom: 20px;
}
.card {
flex: 1;
border: 1px solid #ccc;
padding: 20px;
border-radius: 5px;
text-align: center;
}
.chart {
margin-top: 20px;
}
</style>