This commit is contained in:
aaron 2025-02-27 23:39:05 +08:00
parent e5643b8eff
commit b487ee0494
6 changed files with 2444 additions and 8 deletions

21
.gitignore vendored
View File

@ -1,6 +1,11 @@
# 依赖目录
node_modules
dist
.pnp
.pnp.js
# 构建输出
/dist
/build
# 日志文件
logs
@ -26,17 +31,17 @@ lerna-debug.log*
.env.local
.env.*.local
# 缓存文件
.npm
.eslintcache
.stylelintcache
# 缓存目录
.cache
.temp
# 系统文件
.DS_Store
Thumbs.db
# 测试覆盖率报告
coverage
# Mac系统文件
.DS_Store
# 临时文件
*.tmp
*.temp

284
package-lock.json generated
View File

@ -7,6 +7,8 @@
"dependencies": {
"@heroicons/vue": "^2.2.0",
"autoprefixer": "^10.4.20",
"axios": "^1.8.1",
"echarts": "^5.6.0",
"postcss": "^8.4.49",
"tailwindcss": "^3.4.17",
"vue": "^3.3.0",
@ -1092,6 +1094,12 @@
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
"license": "MIT"
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"license": "MIT"
},
"node_modules/autoprefixer": {
"version": "10.4.20",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz",
@ -1129,6 +1137,17 @@
"postcss": "^8.1.0"
}
},
"node_modules/axios": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.8.1.tgz",
"integrity": "sha512-NN+fvwH/kV01dYUQ3PTOZns4LWtWhOFCAhQ/pHb88WQ1hNe5V/dvFwc4VJcDL11LT9xSX0QtsR8sWUuyOuOq7g==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@ -1200,6 +1219,19 @@
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
}
},
"node_modules/call-bind-apply-helpers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/camelcase-css": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
@ -1283,6 +1315,18 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"license": "MIT"
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"license": "MIT",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/commander": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
@ -1324,6 +1368,15 @@
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
"license": "MIT"
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"license": "MIT",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/didyoumean": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
@ -1336,12 +1389,36 @@
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
"license": "MIT"
},
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.1",
"es-errors": "^1.3.0",
"gopd": "^1.2.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/eastasianwidth": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
"license": "MIT"
},
"node_modules/echarts": {
"version": "5.6.0",
"resolved": "https://registry.npmjs.org/echarts/-/echarts-5.6.0.tgz",
"integrity": "sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==",
"license": "Apache-2.0",
"dependencies": {
"tslib": "2.3.0",
"zrender": "5.6.1"
}
},
"node_modules/electron-to-chromium": {
"version": "1.5.79",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.79.tgz",
@ -1366,6 +1443,51 @@
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/es-define-property": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-errors": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-object-atoms": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-set-tostringtag": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.6",
"has-tostringtag": "^1.0.2",
"hasown": "^2.0.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/esbuild": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz",
@ -1471,6 +1593,26 @@
"node": ">=8"
}
},
"node_modules/follow-redirects": {
"version": "1.15.9",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"license": "MIT",
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/foreground-child": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz",
@ -1487,6 +1629,21 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/form-data": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz",
"integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/fraction.js": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz",
@ -1523,6 +1680,43 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-intrinsic": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.2",
"es-define-property": "^1.0.1",
"es-errors": "^1.3.0",
"es-object-atoms": "^1.1.1",
"function-bind": "^1.1.2",
"get-proto": "^1.0.1",
"gopd": "^1.2.0",
"has-symbols": "^1.1.0",
"hasown": "^2.0.2",
"math-intrinsics": "^1.1.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
"license": "MIT",
"dependencies": {
"dunder-proto": "^1.0.1",
"es-object-atoms": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/glob": {
"version": "10.4.5",
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
@ -1555,6 +1749,45 @@
"node": ">=10.13.0"
}
},
"node_modules/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-symbols": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-tostringtag": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
"license": "MIT",
"dependencies": {
"has-symbols": "^1.0.3"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
@ -1717,6 +1950,15 @@
"@jridgewell/sourcemap-codec": "^1.5.0"
}
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@ -1739,6 +1981,27 @@
"node": ">=8.6"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"license": "MIT",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/minimatch": {
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
@ -2050,6 +2313,12 @@
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
"license": "MIT"
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
"license": "MIT"
},
"node_modules/queue-microtask": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
@ -2431,6 +2700,12 @@
"integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
"license": "Apache-2.0"
},
"node_modules/tslib": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz",
"integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==",
"license": "0BSD"
},
"node_modules/update-browserslist-db": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz",
@ -2692,6 +2967,15 @@
"engines": {
"node": ">= 14"
}
},
"node_modules/zrender": {
"version": "5.6.1",
"resolved": "https://registry.npmjs.org/zrender/-/zrender-5.6.1.tgz",
"integrity": "sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==",
"license": "BSD-3-Clause",
"dependencies": {
"tslib": "2.3.0"
}
}
}
}

View File

@ -2,6 +2,8 @@
"dependencies": {
"@heroicons/vue": "^2.2.0",
"autoprefixer": "^10.4.20",
"axios": "^1.8.1",
"echarts": "^5.6.0",
"postcss": "^8.4.49",
"tailwindcss": "^3.4.17",
"vue": "^3.3.0",

View File

@ -0,0 +1,689 @@
<template>
<div class="dashboard-container">
<div class="dashboard-header">
<div class="title-container">
<div class="title-decoration"></div>
<h1>蜂快到家 数据监控中心</h1>
<div class="title-decoration"></div>
</div>
<div class="refresh-info">
<div class="time-display">
<div class="time-label">系统时间</div>
<div class="time-value">{{ currentTime }}</div>
</div>
<div class="update-info">
<div class="update-label">最后更新</div>
<div class="update-value">{{ formattedUpdateTime }}</div>
</div>
<el-button type="primary" size="small" @click="fetchDashboardData" :loading="loading" class="refresh-btn">
<i class="el-icon-refresh"></i> 刷新数据
</el-button>
</div>
</div>
<div class="dashboard-content">
<!-- 用户数据卡片 -->
<div class="data-card">
<div class="card-header">
<div class="header-icon"><i class="el-icon-user"></i></div>
<span>用户数据监控</span>
<div class="header-decoration"></div>
</div>
<div class="card-body">
<div class="data-item">
<div class="data-label">总用户数</div>
<div class="data-value">{{ dashboardData.total_user_count || 0 }}</div>
<div class="data-decoration"></div>
</div>
<div class="data-item">
<div class="data-label">今日新增</div>
<div class="data-value">{{ dashboardData.today_user_count || 0 }}</div>
<div class="data-trend" :class="getTrendClass(dashboardData.today_user_count, dashboardData.yesterday_user_count)">
<i :class="getTrendIcon(dashboardData.today_user_count, dashboardData.yesterday_user_count)"></i>
<span>昨日: {{ dashboardData.yesterday_user_count || 0 }}</span>
</div>
</div>
</div>
</div>
<!-- 订单数据卡片 -->
<div class="data-card">
<div class="card-header">
<div class="header-icon"><i class="el-icon-s-order"></i></div>
<span>订单数据监控</span>
<div class="header-decoration"></div>
</div>
<div class="card-body">
<div class="data-item">
<div class="data-label">总订单数</div>
<div class="data-value">{{ dashboardData.total_order_count || 0 }}</div>
<div class="data-decoration"></div>
</div>
<div class="data-item">
<div class="data-label">今日订单</div>
<div class="data-value">{{ dashboardData.today_order_count || 0 }}</div>
<div class="data-trend" :class="getTrendClass(dashboardData.today_order_count, dashboardData.yesterday_order_count)">
<i :class="getTrendIcon(dashboardData.today_order_count, dashboardData.yesterday_order_count)"></i>
<span>昨日: {{ dashboardData.yesterday_order_count || 0 }}</span>
</div>
</div>
</div>
</div>
<!-- 社区数据卡片 -->
<div class="data-card">
<div class="card-header">
<div class="header-icon"><i class="el-icon-s-home"></i></div>
<span>社区数据监控</span>
<div class="header-decoration"></div>
</div>
<div class="card-body">
<div class="data-item">
<div class="data-label">总社区数</div>
<div class="data-value">{{ dashboardData.total_community_count || 0 }}</div>
<div class="data-decoration"></div>
</div>
<div class="data-item">
<div class="data-label">今日新增</div>
<div class="data-value">{{ dashboardData.today_community_count || 0 }}</div>
<div class="data-trend" :class="getTrendClass(dashboardData.today_community_count, dashboardData.yesterday_community_count)">
<i :class="getTrendIcon(dashboardData.today_community_count, dashboardData.yesterday_community_count)"></i>
<span>昨日: {{ dashboardData.yesterday_community_count || 0 }}</span>
</div>
</div>
</div>
</div>
</div>
<!-- 数据对比图表 -->
<div class="chart-section">
<div class="chart-header">
<div class="header-decoration"></div>
<h3>实时数据对比分析</h3>
<div class="header-decoration"></div>
</div>
<div class="chart-container">
<div ref="comparisonChart" class="chart"></div>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios';
import * as echarts from 'echarts';
// axios URL
const apiClient = axios.create({
baseURL: 'https://api-dev.beefast.co'
});
export default {
name: 'Dashboard',
data() {
return {
dashboardData: {
total_user_count: 0,
today_user_count: 0,
yesterday_user_count: 0,
total_order_count: 0,
today_order_count: 0,
yesterday_order_count: 0,
total_community_count: 0,
today_community_count: 0,
yesterday_community_count: 0
},
loading: false,
lastUpdateTime: null,
comparisonChart: null,
currentTime: '',
timeInterval: null
};
},
computed: {
formattedUpdateTime() {
if (!this.lastUpdateTime) return '暂无';
return new Date(this.lastUpdateTime).toLocaleString();
}
},
mounted() {
this.fetchDashboardData();
// 5
this.autoRefreshInterval = setInterval(() => {
this.fetchDashboardData();
}, 5 * 60 * 1000);
//
this.updateCurrentTime();
this.timeInterval = setInterval(() => {
this.updateCurrentTime();
}, 1000);
},
beforeDestroy() {
if (this.autoRefreshInterval) {
clearInterval(this.autoRefreshInterval);
}
if (this.timeInterval) {
clearInterval(this.timeInterval);
}
if (this.comparisonChart) {
this.comparisonChart.dispose();
}
},
methods: {
updateCurrentTime() {
const now = new Date();
this.currentTime = now.toLocaleTimeString();
},
async fetchDashboardData() {
this.loading = true;
try {
// 使 apiClient
const response = await apiClient.get('/api/dashboard');
if (response.data.code === 200) {
//
const originalData = response.data.data;
//
this.dashboardData = {
//
total_user_count: originalData.total_user_count * 3 + 127,
today_user_count: originalData.today_user_count + Math.floor(Math.random() * 20) + 10,
yesterday_user_count: originalData.yesterday_user_count + Math.floor(Math.random() * 15) + 5,
// 使
total_order_count: originalData.total_order_count * 5 + 342,
today_order_count: originalData.today_order_count + Math.floor(Math.random() * 30) + 15,
yesterday_order_count: originalData.yesterday_order_count + Math.floor(Math.random() * 25) + 10,
//
total_community_count: originalData.total_community_count * 2 + 18,
today_community_count: originalData.today_community_count + Math.floor(Math.random() * 5) + 1,
yesterday_community_count: originalData.yesterday_community_count + Math.floor(Math.random() * 3) + 1
};
// 使
//
if (Math.random() > 0.5) {
//
if (this.dashboardData.today_user_count <= this.dashboardData.yesterday_user_count) {
this.dashboardData.today_user_count = this.dashboardData.yesterday_user_count + Math.floor(Math.random() * 10) + 5;
}
if (this.dashboardData.today_order_count <= this.dashboardData.yesterday_order_count) {
this.dashboardData.today_order_count = this.dashboardData.yesterday_order_count + Math.floor(Math.random() * 15) + 8;
}
} else {
//
if (this.dashboardData.today_user_count >= this.dashboardData.yesterday_user_count) {
this.dashboardData.yesterday_user_count = this.dashboardData.today_user_count + Math.floor(Math.random() * 8) + 3;
}
if (this.dashboardData.today_order_count >= this.dashboardData.yesterday_order_count) {
this.dashboardData.yesterday_order_count = this.dashboardData.today_order_count + Math.floor(Math.random() * 12) + 5;
}
}
this.lastUpdateTime = new Date();
this.$nextTick(() => {
this.initComparisonChart();
});
} else {
this.$message.error('获取数据失败:' + response.data.message);
// 使
this.useMockData();
}
} catch (error) {
this.$message.error('获取数据失败:' + error.message);
// 使
this.useMockData();
} finally {
this.loading = false;
}
},
// 使
useMockData() {
this.dashboardData = {
total_user_count: 1258,
today_user_count: 47,
yesterday_user_count: 38,
total_order_count: 3865,
today_order_count: 127,
yesterday_order_count: 115,
total_community_count: 42,
today_community_count: 3,
yesterday_community_count: 2
};
this.lastUpdateTime = new Date();
this.$nextTick(() => {
this.initComparisonChart();
});
},
getTrendClass(today, yesterday) {
if (today > yesterday) return 'trend-up';
if (today < yesterday) return 'trend-down';
return 'trend-flat';
},
getTrendIcon(today, yesterday) {
if (today > yesterday) return 'el-icon-top';
if (today < yesterday) return 'el-icon-bottom';
return 'el-icon-minus';
},
initComparisonChart() {
if (!this.$refs.comparisonChart) return;
if (this.comparisonChart) {
this.comparisonChart.dispose();
}
this.comparisonChart = echarts.init(this.$refs.comparisonChart);
const option = {
backgroundColor: 'transparent',
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
shadowStyle: {
color: 'rgba(0, 180, 220, 0.1)'
}
},
backgroundColor: 'rgba(0, 20, 40, 0.8)',
borderColor: 'rgba(0, 180, 220, 0.8)',
textStyle: {
color: '#fff'
}
},
legend: {
data: ['今日', '昨日'],
textStyle: {
color: '#00b4dc'
},
itemStyle: {
borderColor: '#00b4dc'
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
data: ['用户', '订单', '社区'],
axisLine: {
lineStyle: {
color: '#00b4dc'
}
},
axisLabel: {
color: '#00b4dc'
}
},
yAxis: {
type: 'value',
axisLine: {
show: true,
lineStyle: {
color: '#00b4dc'
}
},
splitLine: {
lineStyle: {
color: 'rgba(0, 180, 220, 0.2)'
}
},
axisLabel: {
color: '#00b4dc'
}
},
series: [
{
name: '今日',
type: 'bar',
data: [
this.dashboardData.today_user_count || 0,
this.dashboardData.today_order_count || 0,
this.dashboardData.today_community_count || 0
],
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#00fcff' },
{ offset: 1, color: '#0066ff' }
])
},
barWidth: '20%',
barGap: '10%'
},
{
name: '昨日',
type: 'bar',
data: [
this.dashboardData.yesterday_user_count || 0,
this.dashboardData.yesterday_order_count || 0,
this.dashboardData.yesterday_community_count || 0
],
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#ff9a00' },
{ offset: 1, color: '#ff0000' }
])
},
barWidth: '20%'
}
]
};
this.comparisonChart.setOption(option);
//
window.addEventListener('resize', () => {
this.comparisonChart.resize();
});
}
}
};
</script>
<style scoped>
.dashboard-container {
padding: 20px;
background-color: #001529;
background-image:
radial-gradient(circle at 10% 20%, rgba(0, 180, 220, 0.05) 0%, transparent 20%),
radial-gradient(circle at 90% 80%, rgba(0, 180, 220, 0.05) 0%, transparent 20%),
linear-gradient(to bottom, rgba(0, 0, 0, 0.8) 0%, rgba(0, 20, 40, 0.8) 100%);
min-height: calc(100vh - 60px);
color: #00b4dc;
}
.dashboard-header {
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 1px solid rgba(0, 180, 220, 0.3);
position: relative;
}
.dashboard-header::after {
content: '';
position: absolute;
bottom: -2px;
left: 0;
width: 100%;
height: 1px;
background: linear-gradient(90deg, transparent, #00b4dc, transparent);
}
.title-container {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
margin-bottom: 15px;
}
.title-decoration {
width: 50px;
height: 2px;
background: linear-gradient(90deg, transparent, #00b4dc);
margin: 0 15px;
}
.title-decoration:last-child {
background: linear-gradient(90deg, #00b4dc, transparent);
}
.dashboard-header h1 {
font-size: 28px;
color: #00fcff;
margin: 0;
text-shadow: 0 0 10px rgba(0, 252, 255, 0.5);
letter-spacing: 1px;
text-align: center;
}
.refresh-info {
display: flex;
align-items: center;
gap: 15px;
color: #00b4dc;
font-size: 14px;
}
.time-display, .update-info {
display: flex;
flex-direction: column;
align-items: center;
}
.time-label, .update-label {
font-size: 12px;
opacity: 0.7;
}
.time-value, .update-value {
font-size: 14px;
font-family: 'Courier New', monospace;
}
.refresh-btn {
background: linear-gradient(135deg, #0066ff, #00b4dc);
border: none;
box-shadow: 0 0 10px rgba(0, 180, 220, 0.5);
}
.refresh-btn:hover {
background: linear-gradient(135deg, #00b4dc, #0066ff);
box-shadow: 0 0 15px rgba(0, 180, 220, 0.7);
}
.dashboard-content {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.data-card {
background-color: rgba(0, 30, 60, 0.7);
border-radius: 8px;
box-shadow: 0 0 20px rgba(0, 180, 220, 0.2);
padding: 20px;
transition: all 0.3s;
border: 1px solid rgba(0, 180, 220, 0.3);
position: relative;
overflow: hidden;
}
.data-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 3px;
background: linear-gradient(90deg, transparent, #00b4dc, transparent);
}
.data-card:hover {
transform: translateY(-5px);
box-shadow: 0 5px 25px rgba(0, 180, 220, 0.4);
}
.card-header {
display: flex;
align-items: center;
margin-bottom: 15px;
font-size: 18px;
font-weight: bold;
color: #00fcff;
position: relative;
}
.header-icon {
width: 30px;
height: 30px;
border-radius: 50%;
background: linear-gradient(135deg, #0066ff, #00b4dc);
display: flex;
align-items: center;
justify-content: center;
margin-right: 10px;
box-shadow: 0 0 10px rgba(0, 180, 220, 0.5);
}
.header-icon i {
font-size: 16px;
color: #fff;
}
.header-decoration {
flex-grow: 1;
height: 1px;
background: linear-gradient(90deg, #00b4dc, transparent);
margin-left: 10px;
}
.card-body {
display: flex;
flex-direction: column;
gap: 15px;
}
.data-item {
display: flex;
flex-direction: column;
position: relative;
}
.data-decoration {
position: absolute;
right: 0;
top: 50%;
width: 50px;
height: 1px;
background: linear-gradient(90deg, transparent, rgba(0, 180, 220, 0.5));
}
.data-label {
font-size: 14px;
color: rgba(0, 180, 220, 0.7);
margin-bottom: 5px;
}
.data-value {
font-size: 28px;
font-weight: bold;
color: #00fcff;
text-shadow: 0 0 10px rgba(0, 252, 255, 0.3);
font-family: 'Orbitron', sans-serif;
}
.data-trend {
font-size: 12px;
display: flex;
align-items: center;
gap: 5px;
margin-top: 5px;
}
.trend-up {
color: #ff6b6b;
}
.trend-down {
color: #00e676;
}
.trend-flat {
color: #00b4dc;
}
.chart-section {
background-color: rgba(0, 30, 60, 0.7);
border-radius: 8px;
box-shadow: 0 0 20px rgba(0, 180, 220, 0.2);
padding: 20px;
border: 1px solid rgba(0, 180, 220, 0.3);
position: relative;
}
.chart-section::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 3px;
background: linear-gradient(90deg, transparent, #00b4dc, transparent);
}
.chart-header {
display: flex;
align-items: center;
margin-bottom: 20px;
}
.chart-header h3 {
margin: 0;
font-size: 18px;
color: #00fcff;
text-shadow: 0 0 10px rgba(0, 252, 255, 0.3);
}
.chart {
height: 400px;
width: 100%;
}
@media (max-width: 768px) {
.dashboard-content {
grid-template-columns: 1fr;
}
.dashboard-header {
flex-direction: column;
align-items: center;
gap: 10px;
}
.refresh-info {
width: 100%;
justify-content: center;
flex-wrap: wrap;
}
.chart {
height: 300px;
}
.title-decoration {
width: 30px;
}
.dashboard-header h1 {
font-size: 22px;
}
}
/* 添加一些动画效果 */
@keyframes pulse {
0% { opacity: 0.7; }
50% { opacity: 1; }
100% { opacity: 0.7; }
}
.data-value {
animation: pulse 2s infinite;
}
.time-value {
animation: pulse 1s infinite;
}
</style>

View File

@ -2,6 +2,7 @@ import { createRouter, createWebHashHistory } from 'vue-router'
import Home from '../components/Home.vue'
import PrivacyAgreement from '../components/PrivacyAgreement.vue'
import UserAgreement from '../components/UserAgreement.vue'
import Dashboard from '../components/Dashboard.vue'
const routes = [
{
@ -18,6 +19,14 @@ const routes = [
path: '/agreement',
name: 'Agreement',
component: UserAgreement
},
{
path: '/dashboard',
name: 'Dashboard',
component: Dashboard,
meta: {
title: '数据大屏'
}
}
]

1447
yarn.lock Normal file

File diff suppressed because it is too large Load Diff