diff --git a/package-lock.json b/package-lock.json
index 195cf74..4611661 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14,6 +14,7 @@
"chart": "^0.1.2",
"chart.js": "^4.5.1",
"docx-preview": "^0.3.7",
+ "echarts": "^6.0.0",
"element-plus": "^2.11.7",
"less": "^4.4.2",
"marked": "^16.4.1",
@@ -2269,6 +2270,22 @@
"node": ">= 0.4"
}
},
+ "node_modules/echarts": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmmirror.com/echarts/-/echarts-6.0.0.tgz",
+ "integrity": "sha512-Tte/grDQRiETQP4xz3iZWSvoHrkCQtwqd6hs+mifXcjrCuo2iKWbajFObuLJVBlDIJlOzgQPd1hsaKt/3+OMkQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "2.3.0",
+ "zrender": "6.0.0"
+ }
+ },
+ "node_modules/echarts/node_modules/tslib": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz",
+ "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==",
+ "license": "0BSD"
+ },
"node_modules/element-plus": {
"version": "2.11.7",
"resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.11.7.tgz",
@@ -5642,6 +5659,21 @@
"engines": {
"node": ">=0.8"
}
+ },
+ "node_modules/zrender": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmmirror.com/zrender/-/zrender-6.0.0.tgz",
+ "integrity": "sha512-41dFXEEXuJpNecuUQq6JlbybmnHaqqpGlbH1yxnA5V9MMP4SbohSVZsJIwz+zdjQXSSlR1Vc34EgH1zxyTDvhg==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "tslib": "2.3.0"
+ }
+ },
+ "node_modules/zrender/node_modules/tslib": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz",
+ "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==",
+ "license": "0BSD"
}
}
}
diff --git a/package.json b/package.json
index 1093c02..a825a6c 100644
--- a/package.json
+++ b/package.json
@@ -15,6 +15,7 @@
"chart": "^0.1.2",
"chart.js": "^4.5.1",
"docx-preview": "^0.3.7",
+ "echarts": "^6.0.0",
"element-plus": "^2.11.7",
"less": "^4.4.2",
"marked": "^16.4.1",
diff --git a/src/api/analytics.js b/src/api/analytics.js
new file mode 100644
index 0000000..7a7f133
--- /dev/null
+++ b/src/api/analytics.js
@@ -0,0 +1,18 @@
+// 数据统计相关API
+import request from "@/utils/request";
+
+// 获取内容统计
+export function getContentStats() {
+ return request({
+ url: "/admin/contentstats",
+ method: "get",
+ });
+}
+
+// 获取用户统计
+export function getUserStats() {
+ return request({
+ url: "/admin/usersstats",
+ method: "get",
+ });
+}
\ No newline at end of file
diff --git a/src/components/CommonAside.vue b/src/components/CommonAside.vue
index 39ff58c..4ee6ef0 100644
--- a/src/components/CommonAside.vue
+++ b/src/components/CommonAside.vue
@@ -27,7 +27,7 @@
:default-active="route.path"
>
-
{{ isCollapse ? '管理' : '管理后台' }}
+ {{ isCollapse ? '管理' : '云泽管理后台' }}
diff --git a/src/router/dynamicRoutes.js b/src/router/dynamicRoutes.js
index c67f0be..f7ec466 100644
--- a/src/router/dynamicRoutes.js
+++ b/src/router/dynamicRoutes.js
@@ -2,12 +2,17 @@ import { createComponentLoader } from '@/utils/pathResolver';
import { h, resolveComponent as resolveVueComponent } from 'vue';
// 递归转换嵌套菜单为嵌套路由
-export function convertMenusToRoutes(menus) {
+export function convertMenusToRoutes(menus, parentPath = '') {
if (!menus || menus.length === 0) return [];
return menus.map(menu => {
+ // 拼接完整的路由路径(处理相对路径)
+ const fullPath = menu.path ?
+ (menu.path.startsWith('/') ? menu.path : `${parentPath}/${menu.path}`)
+ : '';
+
const route = {
- path: menu.path || '',
+ path: fullPath || menu.path || '',
name: `menu_${menu.id}`,
meta: {
title: menu.title,
@@ -32,32 +37,21 @@ export function convertMenusToRoutes(menus) {
route.component = () => import('@/views/404/404.vue');
}
- // 2. 递归子路由
+ // 2. 递归子路由(传递当前完整路径作为父路径)
if (menu.children && menu.children.length > 0) {
- route.children = convertMenusToRoutes(menu.children);
+ route.children = convertMenusToRoutes(menu.children, fullPath);
// 目录节点添加重定向,防止点击父菜单页面空白
const firstChild = menu.children[0];
if (firstChild && firstChild.path) {
- route.redirect = firstChild.path;
+ // 计算第一个子路由的完整路径
+ const childFullPath = firstChild.path.startsWith('/') ?
+ firstChild.path :
+ `${fullPath}/${firstChild.path}`;
+ route.redirect = childFullPath;
}
}
return route;
});
}
-
-// 查找第一个有效的子路由
-function findFirstValidRoute(children) {
- if (!children || children.length === 0) return null;
- for (const child of children) {
- if (child.path && (child.component_path || (child.children && child.children.length > 0))) {
- return child;
- }
- if (child.children && child.children.length > 0) {
- const grandChild = findFirstValidRoute(child.children);
- if (grandChild) return grandChild;
- }
- }
- return null;
-}
diff --git a/src/views/analytics/content/index.vue b/src/views/analytics/content/index.vue
new file mode 100644
index 0000000..4639152
--- /dev/null
+++ b/src/views/analytics/content/index.vue
@@ -0,0 +1,245 @@
+
+
+
+
+
+ {{ card.label }}
+ {{ card.count.toLocaleString() }}
+
+ 昨日
+
+ {{ card.trend >= 0 ? "+" : "" }}{{ card.trend }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ row.cate || '未分类' }}
+
+
+
+
+
+
+ {{ row.publish_date ? row.publish_date.slice(0, 16).replace('T', ' ') : '-' }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/analytics/users/index.vue b/src/views/analytics/users/index.vue
new file mode 100644
index 0000000..c646712
--- /dev/null
+++ b/src/views/analytics/users/index.vue
@@ -0,0 +1,196 @@
+
+
+
+
+
+
+
+
+
+
+
{{ item.title }}
+
{{ item.value.toLocaleString() }}
+
+ {{ item.isUp ? '↑' : '↓' }} {{ item.percentage }}%
+ 较上月
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/views/apps/cms/articles/index.vue b/src/views/apps/cms/articles/index.vue
index 4d6839a..f6f7917 100644
--- a/src/views/apps/cms/articles/index.vue
+++ b/src/views/apps/cms/articles/index.vue
@@ -116,7 +116,7 @@
width="100"
align="center"
/>
-
+