优化系统
This commit is contained in:
Generated
+120
-1
@@ -13,13 +13,15 @@
|
||||
"axios": "^1.13.1",
|
||||
"chart": "^0.1.2",
|
||||
"chart.js": "^4.5.1",
|
||||
"docx-preview": "^0.3.7",
|
||||
"element-plus": "^2.11.7",
|
||||
"less": "^4.4.2",
|
||||
"marked": "^16.4.1",
|
||||
"os": "^0.1.2",
|
||||
"pinia": "^3.0.3",
|
||||
"vue": "^3.5.22",
|
||||
"vue-router": "^4.6.3"
|
||||
"vue-router": "^4.6.3",
|
||||
"vue3-pdf-app": "^1.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^24.9.2",
|
||||
@@ -2033,6 +2035,12 @@
|
||||
"url": "https://github.com/sponsors/mesqueeb"
|
||||
}
|
||||
},
|
||||
"node_modules/core-util-is": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz",
|
||||
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/csstype": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz",
|
||||
@@ -2184,6 +2192,15 @@
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/docx-preview": {
|
||||
"version": "0.3.7",
|
||||
"resolved": "https://registry.npmmirror.com/docx-preview/-/docx-preview-0.3.7.tgz",
|
||||
"integrity": "sha512-Lav69CTA/IYZPJTsKH7oYeoZjyg96N0wEJMNslGJnZJ+dMUZK85Lt5ASC79yUlD48ecWjuv+rkcmFt6EVPV0Xg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"jszip": ">=3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/dom7": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/dom7/-/dom7-3.0.0.tgz",
|
||||
@@ -2927,6 +2944,12 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/immediate": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmmirror.com/immediate/-/immediate-3.0.6.tgz",
|
||||
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/immer": {
|
||||
"version": "9.0.21",
|
||||
"resolved": "https://registry.npmmirror.com/immer/-/immer-9.0.21.tgz",
|
||||
@@ -2944,6 +2967,12 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/internal-slot": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/internal-slot/-/internal-slot-1.1.0.tgz",
|
||||
@@ -3356,6 +3385,18 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/jszip": {
|
||||
"version": "3.10.1",
|
||||
"resolved": "https://registry.npmmirror.com/jszip/-/jszip-3.10.1.tgz",
|
||||
"integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
|
||||
"license": "(MIT OR GPL-3.0-or-later)",
|
||||
"dependencies": {
|
||||
"lie": "~3.3.0",
|
||||
"pako": "~1.0.2",
|
||||
"readable-stream": "~2.3.6",
|
||||
"setimmediate": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/less": {
|
||||
"version": "4.4.2",
|
||||
"resolved": "https://registry.npmmirror.com/less/-/less-4.4.2.tgz",
|
||||
@@ -3382,6 +3423,15 @@
|
||||
"source-map": "~0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/lie": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/lie/-/lie-3.3.0.tgz",
|
||||
"integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"immediate": "~3.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/local-pkg": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/local-pkg/-/local-pkg-1.1.2.tgz",
|
||||
@@ -3767,6 +3817,12 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/pako": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmmirror.com/pako/-/pako-1.0.11.tgz",
|
||||
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
|
||||
"license": "(MIT AND Zlib)"
|
||||
},
|
||||
"node_modules/parse-node-version": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/parse-node-version/-/parse-node-version-1.0.1.tgz",
|
||||
@@ -3916,6 +3972,12 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/process-nextick-args": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/proxy-from-env": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
@@ -3946,6 +4008,27 @@
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/readable-stream": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.8.tgz",
|
||||
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
"isarray": "~1.0.0",
|
||||
"process-nextick-args": "~2.0.0",
|
||||
"safe-buffer": "~5.1.1",
|
||||
"string_decoder": "~1.1.1",
|
||||
"util-deprecate": "~1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/readable-stream/node_modules/isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/readdirp": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-4.1.2.tgz",
|
||||
@@ -4079,6 +4162,12 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/safe-push-apply": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/safe-push-apply/-/safe-push-apply-1.0.0.tgz",
|
||||
@@ -4569,6 +4658,12 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/setimmediate": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmmirror.com/setimmediate/-/setimmediate-1.0.5.tgz",
|
||||
"integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/side-channel": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/side-channel/-/side-channel-1.1.0.tgz",
|
||||
@@ -4720,6 +4815,15 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/string.prototype.trim": {
|
||||
"version": "1.2.10",
|
||||
"resolved": "https://registry.npmmirror.com/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz",
|
||||
@@ -5189,6 +5293,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/varint": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/varint/-/varint-6.0.0.tgz",
|
||||
@@ -5307,6 +5417,15 @@
|
||||
"vue": "^3.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vue3-pdf-app": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/vue3-pdf-app/-/vue3-pdf-app-1.0.3.tgz",
|
||||
"integrity": "sha512-qegWTIF4wYKiocZ3KreB70wRXhqSdXWbdERDyyKzT7d5PbjKbS9tD6vaKkCqh3PzTM84NyKPYrQ3iuwJb60YPQ==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"vue": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-virtual-modules": {
|
||||
"version": "0.6.2",
|
||||
"resolved": "https://registry.npmmirror.com/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz",
|
||||
|
||||
+3
-1
@@ -14,13 +14,15 @@
|
||||
"axios": "^1.13.1",
|
||||
"chart": "^0.1.2",
|
||||
"chart.js": "^4.5.1",
|
||||
"docx-preview": "^0.3.7",
|
||||
"element-plus": "^2.11.7",
|
||||
"less": "^4.4.2",
|
||||
"marked": "^16.4.1",
|
||||
"os": "^0.1.2",
|
||||
"pinia": "^3.0.3",
|
||||
"vue": "^3.5.22",
|
||||
"vue-router": "^4.6.3"
|
||||
"vue-router": "^4.6.3",
|
||||
"vue3-pdf-app": "^1.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^24.9.2",
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import request from "@/utils/request";
|
||||
|
||||
/**
|
||||
* 获取平台统计数据(平台用户使用)
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export function getPlatformStats() {
|
||||
return request({
|
||||
url: "/api/dashboard/platform-stats",
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取租户统计数据(租户员工使用)
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export function getTenantStats() {
|
||||
return request({
|
||||
url: "/api/dashboard/tenant-stats",
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
@@ -112,4 +112,15 @@ export function addTag(data) {
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取知识库数量
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export function getKnowledgeCount() {
|
||||
return request({
|
||||
url: "/api/knowledge/count",
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
@@ -2,11 +2,14 @@ import request from '@/utils/request';
|
||||
|
||||
/**
|
||||
* 获取所有菜单权限列表(用于分配权限)
|
||||
* @param {Object} params - 请求参数
|
||||
* @param {number} params.roleId - 可选的角色ID,用于根据角色的default值过滤菜单
|
||||
*/
|
||||
export function getAllMenuPermissions() {
|
||||
export function getAllMenuPermissions(params = {}) {
|
||||
return request({
|
||||
url: '/api/permissions/menus',
|
||||
method: 'get',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -108,7 +108,7 @@ const transformMenuData = (menus) => {
|
||||
return [];
|
||||
}
|
||||
|
||||
console.log('原始菜单数据:', menus);
|
||||
// console.log('原始菜单数据:', menus);
|
||||
|
||||
// 功能页面路径关键词,这些菜单不应该显示在侧边栏
|
||||
// 注意:只过滤真正的功能页面,不过滤包含这些关键词的父菜单
|
||||
@@ -146,17 +146,17 @@ const transformMenuData = (menus) => {
|
||||
.filter(menu => {
|
||||
// 只显示页面菜单,不显示API权限菜单
|
||||
if (menu.menuType !== 1 && menu.menuType !== undefined) {
|
||||
console.log('过滤掉非页面菜单:', menu);
|
||||
// console.log('过滤掉非页面菜单:', menu);
|
||||
return false;
|
||||
}
|
||||
// 过滤掉功能页面(详情、新增、编辑、删除等)
|
||||
if (isFunctionPage(menu.path)) {
|
||||
console.log('过滤掉功能页面:', menu.path);
|
||||
// console.log('过滤掉功能页面:', menu.path);
|
||||
return false;
|
||||
}
|
||||
// 过滤掉需要隐藏的子菜单(如分类管理、标签管理)
|
||||
if (isHiddenSubMenu(menu.path)) {
|
||||
console.log('过滤掉隐藏的子菜单:', menu.path);
|
||||
// console.log('过滤掉隐藏的子菜单:', menu.path);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -172,7 +172,7 @@ const transformMenuData = (menus) => {
|
||||
children: []
|
||||
}));
|
||||
|
||||
console.log('过滤后的菜单数据:', allMenus);
|
||||
// console.log('过滤后的菜单数据:', allMenus);
|
||||
|
||||
// 构建菜单映射表(只包含有效的页面菜单)
|
||||
const menuMap = new Map();
|
||||
@@ -205,7 +205,7 @@ const transformMenuData = (menus) => {
|
||||
}
|
||||
});
|
||||
|
||||
console.log('构建后的菜单树:', rootMenus);
|
||||
// console.log('构建后的菜单树:', rootMenus);
|
||||
|
||||
// 按 order 排序(确保排序正确)
|
||||
const sortMenus = (menus) => {
|
||||
|
||||
@@ -120,11 +120,13 @@ import { getKnowledgeDetail, createKnowledge, updateKnowledge, getCategoryList,
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import type { FormInstance, FormRules } from "element-plus";
|
||||
import WangEditor from '@/views/components/WangEditor.vue';
|
||||
import { useAuthStore } from '@/stores/auth';
|
||||
|
||||
const formRef = ref<FormInstance>();
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const authStore = useAuthStore();
|
||||
|
||||
const formData = reactive<{
|
||||
title: string;
|
||||
@@ -186,17 +188,44 @@ watch(
|
||||
}
|
||||
);
|
||||
|
||||
// 获取当前登录用户的显示名称
|
||||
const getLoginUser = () => {
|
||||
const userStr = localStorage.getItem("user");
|
||||
if (userStr) {
|
||||
try {
|
||||
const user = JSON.parse(userStr);
|
||||
return user.username || user.name || user.userName || "";
|
||||
} catch (e) {
|
||||
return "";
|
||||
const user = authStore.user;
|
||||
if (!user) {
|
||||
// 如果 authStore 中没有,尝试从 localStorage 获取
|
||||
const userStr = localStorage.getItem("userInfo");
|
||||
if (userStr) {
|
||||
try {
|
||||
const userInfo = JSON.parse(userStr);
|
||||
// 如果是员工登录,优先显示name
|
||||
if (userInfo.type === 'employee' && userInfo.name) {
|
||||
return userInfo.name;
|
||||
}
|
||||
// 如果是用户登录,优先显示nickname
|
||||
if (userInfo.nickname) {
|
||||
return userInfo.nickname;
|
||||
}
|
||||
// 最后显示username
|
||||
return userInfo.username || "";
|
||||
} catch (e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
return "";
|
||||
|
||||
// 如果是员工登录,优先显示name
|
||||
if (user.type === 'employee' && user.name) {
|
||||
return user.name;
|
||||
}
|
||||
|
||||
// 如果是用户登录,优先显示nickname
|
||||
if (user.nickname) {
|
||||
return user.nickname;
|
||||
}
|
||||
|
||||
// 最后显示username
|
||||
return user.username || "";
|
||||
};
|
||||
|
||||
const fetchDetail = async () => {
|
||||
|
||||
@@ -154,7 +154,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from "vue";
|
||||
import { ref, computed, onMounted, markRaw } from "vue";
|
||||
import { Chart, registerables } from "chart.js";
|
||||
import {
|
||||
Money,
|
||||
@@ -165,7 +165,11 @@ import {
|
||||
ArrowDown,
|
||||
MoreFilled,
|
||||
Plus,
|
||||
Document,
|
||||
} from "@element-plus/icons-vue";
|
||||
import { getKnowledgeCount } from "@/api/knowledge";
|
||||
import { getPlatformStats, getTenantStats } from "@/api/dashboard";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
|
||||
Chart.register(...registerables);
|
||||
|
||||
@@ -179,38 +183,111 @@ const currentDate = computed(() => {
|
||||
return `${month}月${day}日 ${weekday}`;
|
||||
});
|
||||
|
||||
// 统计数据
|
||||
// 统计数据(使用 markRaw 避免图标组件被响应式化)
|
||||
const stats = ref([
|
||||
{
|
||||
label: "本月收入",
|
||||
value: "¥ 35,800",
|
||||
change: 10.4,
|
||||
icon: Money,
|
||||
type: "income",
|
||||
label: "知识库",
|
||||
value: "0",
|
||||
change: 0,
|
||||
icon: markRaw(Document),
|
||||
type: "knowledge",
|
||||
},
|
||||
{
|
||||
label: "新用户",
|
||||
value: "842",
|
||||
change: 3.7,
|
||||
icon: User,
|
||||
value: "0",
|
||||
change: 0,
|
||||
icon: markRaw(User),
|
||||
type: "users",
|
||||
},
|
||||
{
|
||||
label: "订单量",
|
||||
value: "1,376",
|
||||
change: -1.6,
|
||||
icon: ShoppingCart,
|
||||
type: "orders",
|
||||
label: "员工数",
|
||||
value: "0",
|
||||
change: 0,
|
||||
icon: markRaw(ShoppingCart),
|
||||
type: "employees",
|
||||
},
|
||||
{
|
||||
label: "活跃率",
|
||||
value: "27.3%",
|
||||
change: 2.2,
|
||||
icon: TrendCharts,
|
||||
type: "active",
|
||||
label: "租户数",
|
||||
value: "0",
|
||||
change: 0,
|
||||
icon: markRaw(TrendCharts),
|
||||
type: "tenants",
|
||||
},
|
||||
]);
|
||||
|
||||
const authStore = useAuthStore();
|
||||
|
||||
// 判断是否为租户员工登录
|
||||
const isEmployee = computed(() => {
|
||||
return authStore.user?.type === 'employee';
|
||||
});
|
||||
|
||||
// 获取平台统计数据
|
||||
const fetchPlatformStats = async () => {
|
||||
try {
|
||||
const res = await getPlatformStats();
|
||||
if (res?.code === 0 || res?.success) {
|
||||
const data = res.data || {};
|
||||
|
||||
// 知识库
|
||||
if (data.knowledgeCount) {
|
||||
stats.value[0].value = data.knowledgeCount.total?.toString() || "0";
|
||||
stats.value[0].change = parseFloat((data.knowledgeCount.growthRate || 0).toFixed(1));
|
||||
}
|
||||
|
||||
// 用户数
|
||||
stats.value[1].label = "用户数";
|
||||
stats.value[1].value = (data.userCount || 0).toString();
|
||||
stats.value[1].change = 0;
|
||||
|
||||
// 员工数
|
||||
stats.value[2].label = "员工数";
|
||||
stats.value[2].value = (data.employeeCount || 0).toString();
|
||||
stats.value[2].change = 0;
|
||||
|
||||
// 租户数
|
||||
stats.value[3].label = "租户数";
|
||||
stats.value[3].value = (data.tenantCount || 0).toString();
|
||||
stats.value[3].change = 0;
|
||||
}
|
||||
} catch (e) {
|
||||
// 静默失败,不影响页面显示
|
||||
}
|
||||
};
|
||||
|
||||
// 获取租户统计数据
|
||||
const fetchTenantStats = async () => {
|
||||
try {
|
||||
const res = await getTenantStats();
|
||||
if (res?.code === 0 || res?.success) {
|
||||
const data = res.data || {};
|
||||
|
||||
// 知识库
|
||||
if (data.knowledgeCount) {
|
||||
stats.value[0].value = data.knowledgeCount.total?.toString() || "0";
|
||||
stats.value[0].change = parseFloat((data.knowledgeCount.growthRate || 0).toFixed(1));
|
||||
}
|
||||
|
||||
// 员工数
|
||||
stats.value[1].label = "员工数";
|
||||
stats.value[1].value = (data.employeeCount || 0).toString();
|
||||
stats.value[1].change = 0;
|
||||
|
||||
// 部门数
|
||||
stats.value[2].label = "部门数";
|
||||
stats.value[2].value = (data.departmentCount || 0).toString();
|
||||
stats.value[2].change = 0;
|
||||
|
||||
// 职位数
|
||||
stats.value[3].label = "职位数";
|
||||
stats.value[3].value = (data.positionCount || 0).toString();
|
||||
stats.value[3].change = 0;
|
||||
}
|
||||
} catch (e) {
|
||||
// 静默失败,不影响页面显示
|
||||
}
|
||||
};
|
||||
|
||||
// 任务列表
|
||||
const tasks = ref([
|
||||
{
|
||||
@@ -272,6 +349,13 @@ const handleTaskChange = (task: any) => {
|
||||
|
||||
// 初始化图表
|
||||
onMounted(() => {
|
||||
// 根据登录类型加载不同的统计数据
|
||||
if (isEmployee.value) {
|
||||
fetchTenantStats();
|
||||
} else {
|
||||
fetchPlatformStats();
|
||||
}
|
||||
|
||||
// 折线图
|
||||
const lineChartEl = document.getElementById("lineChart") as HTMLCanvasElement | null;
|
||||
if (!lineChartEl) {
|
||||
@@ -486,6 +570,18 @@ onMounted(() => {
|
||||
background: linear-gradient(135deg, #8b5cf6 0%, #a78bfa 100%);
|
||||
}
|
||||
|
||||
&.knowledge .stat-icon-wrapper {
|
||||
background: linear-gradient(135deg, #062da3 0%, #4f84ff 100%);
|
||||
}
|
||||
|
||||
&.employees .stat-icon-wrapper {
|
||||
background: linear-gradient(135deg, #10b981 0%, #34d399 100%);
|
||||
}
|
||||
|
||||
&.tenants .stat-icon-wrapper {
|
||||
background: linear-gradient(135deg, #8b5cf6 0%, #a78bfa 100%);
|
||||
}
|
||||
|
||||
.stat-content {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
|
||||
@@ -160,17 +160,19 @@ import {
|
||||
Select,
|
||||
RefreshLeft,
|
||||
} from '@element-plus/icons-vue';
|
||||
import { getRoleByTenantId } from '@/api/role';
|
||||
import { getRoleByTenantId, getAllRoles } from '@/api/role';
|
||||
import {
|
||||
getAllMenuPermissions,
|
||||
getRolePermissions,
|
||||
assignRolePermissions,
|
||||
} from '@/api/permission';
|
||||
import { useAuthStore } from '@/stores/auth';
|
||||
|
||||
// 角色相关
|
||||
const roleList = ref([]);
|
||||
const roleSearchQuery = ref('');
|
||||
const selectedRole = ref(null);
|
||||
const authStore = useAuthStore();
|
||||
|
||||
// 权限相关
|
||||
const allMenus = ref([]);
|
||||
@@ -200,33 +202,56 @@ const filteredRoles = computed(() => {
|
||||
|
||||
// 获取当前租户ID
|
||||
const getCurrentTenantId = () => {
|
||||
if (authStore.user && authStore.user.tenant_id) {
|
||||
return authStore.user.tenant_id;
|
||||
}
|
||||
const userInfo = JSON.parse(localStorage.getItem('userInfo') || '{}');
|
||||
return userInfo.tenant_id || 1;
|
||||
return userInfo.tenant_id || userInfo.tenantId || 0;
|
||||
};
|
||||
|
||||
// 判断是否为平台用户
|
||||
const isPlatformUser = () => {
|
||||
if (authStore.user && authStore.user.type) {
|
||||
return authStore.user.type === 'user';
|
||||
}
|
||||
const userInfo = JSON.parse(localStorage.getItem('userInfo') || '{}');
|
||||
return userInfo.type === 'user';
|
||||
};
|
||||
|
||||
// 加载角色列表
|
||||
const loadRoles = async () => {
|
||||
try {
|
||||
const tenantId = getCurrentTenantId();
|
||||
const res = await getRoleByTenantId(tenantId);
|
||||
// 如果是平台用户,使用 getAllRoles 获取所有角色(包括平台角色和租户角色)
|
||||
// 如果是租户员工,使用 getRoleByTenantId 获取当前租户的角色
|
||||
let res;
|
||||
if (isPlatformUser()) {
|
||||
res = await getAllRoles();
|
||||
} else {
|
||||
const tenantId = getCurrentTenantId();
|
||||
res = await getRoleByTenantId(tenantId);
|
||||
}
|
||||
|
||||
// 兼容两种响应格式:success 和 code
|
||||
const isSuccess = res.success || res.code === 0;
|
||||
if (isSuccess && res.data) {
|
||||
roleList.value = res.data;
|
||||
roleList.value = Array.isArray(res.data) ? res.data : [];
|
||||
} else {
|
||||
ElMessage.warning(res.message || '未获取到角色数据');
|
||||
roleList.value = [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载角色列表失败:', error);
|
||||
ElMessage.error('加载角色列表失败');
|
||||
roleList.value = [];
|
||||
}
|
||||
};
|
||||
|
||||
// 加载所有菜单权限
|
||||
const loadAllMenus = async () => {
|
||||
// 加载所有菜单权限(根据选中的角色过滤)
|
||||
const loadAllMenus = async (roleId = null) => {
|
||||
try {
|
||||
const res = await getAllMenuPermissions();
|
||||
// 如果提供了roleId,传递给接口用于根据角色的default值过滤菜单
|
||||
const params = roleId ? { roleId } : {};
|
||||
const res = await getAllMenuPermissions(params);
|
||||
|
||||
if (res.success && res.data) {
|
||||
allMenus.value = res.data;
|
||||
@@ -267,6 +292,8 @@ const buildMenuTree = () => {
|
||||
// 选择角色
|
||||
const selectRole = async (role) => {
|
||||
selectedRole.value = role;
|
||||
// 重新加载菜单列表(根据角色的default值过滤)
|
||||
await loadAllMenus(role.roleId);
|
||||
await loadRolePermissions(role.roleId);
|
||||
};
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ import {
|
||||
ElMessageBox,
|
||||
} from "element-plus";
|
||||
import { Plus, View, Edit, Delete, Refresh } from "@element-plus/icons-vue";
|
||||
import { getRoleByTenantId, deleteRole } from "@/api/role";
|
||||
import { getRoleByTenantId, getAllRoles, deleteRole } from "@/api/role";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
import RoleEditDialog from "./components/edit.vue";
|
||||
import RoleDetailDialog from "./components/detail.vue";
|
||||
@@ -124,13 +124,30 @@ const getCurrentTenantId = () => {
|
||||
if (userInfo) {
|
||||
try {
|
||||
const user = JSON.parse(userInfo);
|
||||
return user.tenant_id || user.tenantId || null;
|
||||
return user.tenant_id || user.tenantId || 0;
|
||||
} catch (e) {
|
||||
console.error('Failed to parse user info:', e);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return 0;
|
||||
};
|
||||
|
||||
// 判断是否为平台用户
|
||||
const isPlatformUser = () => {
|
||||
if (authStore.user && authStore.user.type) {
|
||||
return authStore.user.type === 'user';
|
||||
}
|
||||
const userInfo = localStorage.getItem('userInfo');
|
||||
if (userInfo) {
|
||||
try {
|
||||
const user = JSON.parse(userInfo);
|
||||
return user.type === 'user';
|
||||
} catch (e) {
|
||||
console.error('Failed to parse user info:', e);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// 判断是否为系统管理员角色(不允许删除)
|
||||
@@ -160,8 +177,16 @@ async function fetchRoles() {
|
||||
loading.value = true;
|
||||
error.value = "";
|
||||
try {
|
||||
const tenantId = getCurrentTenantId();
|
||||
const res = await getRoleByTenantId(tenantId);
|
||||
// 如果是平台用户,使用 getAllRoles 获取所有角色(包括平台角色和租户角色)
|
||||
// 如果是租户员工,使用 getRoleByTenantId 获取当前租户的角色
|
||||
let res;
|
||||
if (isPlatformUser()) {
|
||||
res = await getAllRoles();
|
||||
} else {
|
||||
const tenantId = getCurrentTenantId();
|
||||
res = await getRoleByTenantId(tenantId);
|
||||
}
|
||||
|
||||
if (res.code === 0 && res.data) {
|
||||
roles.value = Array.isArray(res.data) ? res.data : [];
|
||||
} else {
|
||||
|
||||
@@ -231,7 +231,7 @@ import {
|
||||
getUserInfo,
|
||||
changePassword,
|
||||
} from "@/api/user";
|
||||
import { getRoleByTenantId } from "@/api/role";
|
||||
import { getRoleByTenantId, getAllRoles } from "@/api/role";
|
||||
import { getTenantDepartments } from "@/api/department";
|
||||
import { getTenantPositions, getPositionsByDepartment } from "@/api/position";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
@@ -279,12 +279,37 @@ const getCurrentTenantId = () => {
|
||||
return 0;
|
||||
};
|
||||
|
||||
// 判断是否为平台用户
|
||||
const isPlatformUser = () => {
|
||||
if (authStore.user && authStore.user.type) {
|
||||
return authStore.user.type === 'user';
|
||||
}
|
||||
const userInfo = localStorage.getItem('userInfo');
|
||||
if (userInfo) {
|
||||
try {
|
||||
const user = JSON.parse(userInfo);
|
||||
return user.type === 'user';
|
||||
} catch (e) {
|
||||
console.error('Failed to parse user info:', e);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// 获取角色列表
|
||||
const fetchRoles = async () => {
|
||||
loadingRoles.value = true;
|
||||
try {
|
||||
const tenantId = getCurrentTenantId();
|
||||
const res = await getRoleByTenantId(tenantId);
|
||||
// 如果是平台用户,使用 getAllRoles 获取所有角色(包括平台角色和租户角色)
|
||||
// 如果是租户员工,使用 getRoleByTenantId 获取当前租户的角色
|
||||
let res;
|
||||
if (isPlatformUser()) {
|
||||
res = await getAllRoles();
|
||||
} else {
|
||||
const tenantId = getCurrentTenantId();
|
||||
res = await getRoleByTenantId(tenantId);
|
||||
}
|
||||
|
||||
if (res.code === 0 && res.data) {
|
||||
roleList.value = Array.isArray(res.data) ? res.data : [];
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user