优化系统
This commit is contained in:
parent
f4244c09b9
commit
0cd0b9c705
121
pc/package-lock.json
generated
121
pc/package-lock.json
generated
@ -13,13 +13,15 @@
|
|||||||
"axios": "^1.13.1",
|
"axios": "^1.13.1",
|
||||||
"chart": "^0.1.2",
|
"chart": "^0.1.2",
|
||||||
"chart.js": "^4.5.1",
|
"chart.js": "^4.5.1",
|
||||||
|
"docx-preview": "^0.3.7",
|
||||||
"element-plus": "^2.11.7",
|
"element-plus": "^2.11.7",
|
||||||
"less": "^4.4.2",
|
"less": "^4.4.2",
|
||||||
"marked": "^16.4.1",
|
"marked": "^16.4.1",
|
||||||
"os": "^0.1.2",
|
"os": "^0.1.2",
|
||||||
"pinia": "^3.0.3",
|
"pinia": "^3.0.3",
|
||||||
"vue": "^3.5.22",
|
"vue": "^3.5.22",
|
||||||
"vue-router": "^4.6.3"
|
"vue-router": "^4.6.3",
|
||||||
|
"vue3-pdf-app": "^1.0.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^24.9.2",
|
"@types/node": "^24.9.2",
|
||||||
@ -2033,6 +2035,12 @@
|
|||||||
"url": "https://github.com/sponsors/mesqueeb"
|
"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": {
|
"node_modules/csstype": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz",
|
"resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz",
|
||||||
@ -2184,6 +2192,15 @@
|
|||||||
"node": ">=0.10"
|
"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": {
|
"node_modules/dom7": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmmirror.com/dom7/-/dom7-3.0.0.tgz",
|
"resolved": "https://registry.npmmirror.com/dom7/-/dom7-3.0.0.tgz",
|
||||||
@ -2927,6 +2944,12 @@
|
|||||||
"node": ">=0.10.0"
|
"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": {
|
"node_modules/immer": {
|
||||||
"version": "9.0.21",
|
"version": "9.0.21",
|
||||||
"resolved": "https://registry.npmmirror.com/immer/-/immer-9.0.21.tgz",
|
"resolved": "https://registry.npmmirror.com/immer/-/immer-9.0.21.tgz",
|
||||||
@ -2944,6 +2967,12 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/internal-slot": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmmirror.com/internal-slot/-/internal-slot-1.1.0.tgz",
|
"resolved": "https://registry.npmmirror.com/internal-slot/-/internal-slot-1.1.0.tgz",
|
||||||
@ -3356,6 +3385,18 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/less": {
|
||||||
"version": "4.4.2",
|
"version": "4.4.2",
|
||||||
"resolved": "https://registry.npmmirror.com/less/-/less-4.4.2.tgz",
|
"resolved": "https://registry.npmmirror.com/less/-/less-4.4.2.tgz",
|
||||||
@ -3382,6 +3423,15 @@
|
|||||||
"source-map": "~0.6.0"
|
"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": {
|
"node_modules/local-pkg": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmmirror.com/local-pkg/-/local-pkg-1.1.2.tgz",
|
"resolved": "https://registry.npmmirror.com/local-pkg/-/local-pkg-1.1.2.tgz",
|
||||||
@ -3767,6 +3817,12 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"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": {
|
"node_modules/parse-node-version": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmmirror.com/parse-node-version/-/parse-node-version-1.0.1.tgz",
|
"resolved": "https://registry.npmmirror.com/parse-node-version/-/parse-node-version-1.0.1.tgz",
|
||||||
@ -3916,6 +3972,12 @@
|
|||||||
"node": ">=6"
|
"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": {
|
"node_modules/proxy-from-env": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
"resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||||
@ -3946,6 +4008,27 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/readdirp": {
|
||||||
"version": "4.1.2",
|
"version": "4.1.2",
|
||||||
"resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-4.1.2.tgz",
|
"resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-4.1.2.tgz",
|
||||||
@ -4079,6 +4162,12 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"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": {
|
"node_modules/safe-push-apply": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmmirror.com/safe-push-apply/-/safe-push-apply-1.0.0.tgz",
|
"resolved": "https://registry.npmmirror.com/safe-push-apply/-/safe-push-apply-1.0.0.tgz",
|
||||||
@ -4569,6 +4658,12 @@
|
|||||||
"node": ">= 0.4"
|
"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": {
|
"node_modules/side-channel": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmmirror.com/side-channel/-/side-channel-1.1.0.tgz",
|
"resolved": "https://registry.npmmirror.com/side-channel/-/side-channel-1.1.0.tgz",
|
||||||
@ -4720,6 +4815,15 @@
|
|||||||
"node": ">= 0.4"
|
"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": {
|
"node_modules/string.prototype.trim": {
|
||||||
"version": "1.2.10",
|
"version": "1.2.10",
|
||||||
"resolved": "https://registry.npmmirror.com/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz",
|
"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": {
|
"node_modules/varint": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmmirror.com/varint/-/varint-6.0.0.tgz",
|
"resolved": "https://registry.npmmirror.com/varint/-/varint-6.0.0.tgz",
|
||||||
@ -5307,6 +5417,15 @@
|
|||||||
"vue": "^3.5.0"
|
"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": {
|
"node_modules/webpack-virtual-modules": {
|
||||||
"version": "0.6.2",
|
"version": "0.6.2",
|
||||||
"resolved": "https://registry.npmmirror.com/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz",
|
"resolved": "https://registry.npmmirror.com/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz",
|
||||||
|
|||||||
@ -14,13 +14,15 @@
|
|||||||
"axios": "^1.13.1",
|
"axios": "^1.13.1",
|
||||||
"chart": "^0.1.2",
|
"chart": "^0.1.2",
|
||||||
"chart.js": "^4.5.1",
|
"chart.js": "^4.5.1",
|
||||||
|
"docx-preview": "^0.3.7",
|
||||||
"element-plus": "^2.11.7",
|
"element-plus": "^2.11.7",
|
||||||
"less": "^4.4.2",
|
"less": "^4.4.2",
|
||||||
"marked": "^16.4.1",
|
"marked": "^16.4.1",
|
||||||
"os": "^0.1.2",
|
"os": "^0.1.2",
|
||||||
"pinia": "^3.0.3",
|
"pinia": "^3.0.3",
|
||||||
"vue": "^3.5.22",
|
"vue": "^3.5.22",
|
||||||
"vue-router": "^4.6.3"
|
"vue-router": "^4.6.3",
|
||||||
|
"vue3-pdf-app": "^1.0.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^24.9.2",
|
"@types/node": "^24.9.2",
|
||||||
|
|||||||
24
pc/src/api/dashboard.js
Normal file
24
pc/src/api/dashboard.js
Normal file
@ -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",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@ -113,3 +113,14 @@ export function addTag(data) {
|
|||||||
data,
|
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({
|
return request({
|
||||||
url: '/api/permissions/menus',
|
url: '/api/permissions/menus',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
|
params,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -108,7 +108,7 @@ const transformMenuData = (menus) => {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('原始菜单数据:', menus);
|
// console.log('原始菜单数据:', menus);
|
||||||
|
|
||||||
// 功能页面路径关键词,这些菜单不应该显示在侧边栏
|
// 功能页面路径关键词,这些菜单不应该显示在侧边栏
|
||||||
// 注意:只过滤真正的功能页面,不过滤包含这些关键词的父菜单
|
// 注意:只过滤真正的功能页面,不过滤包含这些关键词的父菜单
|
||||||
@ -146,17 +146,17 @@ const transformMenuData = (menus) => {
|
|||||||
.filter(menu => {
|
.filter(menu => {
|
||||||
// 只显示页面菜单,不显示API权限菜单
|
// 只显示页面菜单,不显示API权限菜单
|
||||||
if (menu.menuType !== 1 && menu.menuType !== undefined) {
|
if (menu.menuType !== 1 && menu.menuType !== undefined) {
|
||||||
console.log('过滤掉非页面菜单:', menu);
|
// console.log('过滤掉非页面菜单:', menu);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// 过滤掉功能页面(详情、新增、编辑、删除等)
|
// 过滤掉功能页面(详情、新增、编辑、删除等)
|
||||||
if (isFunctionPage(menu.path)) {
|
if (isFunctionPage(menu.path)) {
|
||||||
console.log('过滤掉功能页面:', menu.path);
|
// console.log('过滤掉功能页面:', menu.path);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// 过滤掉需要隐藏的子菜单(如分类管理、标签管理)
|
// 过滤掉需要隐藏的子菜单(如分类管理、标签管理)
|
||||||
if (isHiddenSubMenu(menu.path)) {
|
if (isHiddenSubMenu(menu.path)) {
|
||||||
console.log('过滤掉隐藏的子菜单:', menu.path);
|
// console.log('过滤掉隐藏的子菜单:', menu.path);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -172,7 +172,7 @@ const transformMenuData = (menus) => {
|
|||||||
children: []
|
children: []
|
||||||
}));
|
}));
|
||||||
|
|
||||||
console.log('过滤后的菜单数据:', allMenus);
|
// console.log('过滤后的菜单数据:', allMenus);
|
||||||
|
|
||||||
// 构建菜单映射表(只包含有效的页面菜单)
|
// 构建菜单映射表(只包含有效的页面菜单)
|
||||||
const menuMap = new Map();
|
const menuMap = new Map();
|
||||||
@ -205,7 +205,7 @@ const transformMenuData = (menus) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('构建后的菜单树:', rootMenus);
|
// console.log('构建后的菜单树:', rootMenus);
|
||||||
|
|
||||||
// 按 order 排序(确保排序正确)
|
// 按 order 排序(确保排序正确)
|
||||||
const sortMenus = (menus) => {
|
const sortMenus = (menus) => {
|
||||||
|
|||||||
@ -120,11 +120,13 @@ import { getKnowledgeDetail, createKnowledge, updateKnowledge, getCategoryList,
|
|||||||
import { ElMessage, ElMessageBox } from "element-plus";
|
import { ElMessage, ElMessageBox } from "element-plus";
|
||||||
import type { FormInstance, FormRules } from "element-plus";
|
import type { FormInstance, FormRules } from "element-plus";
|
||||||
import WangEditor from '@/views/components/WangEditor.vue';
|
import WangEditor from '@/views/components/WangEditor.vue';
|
||||||
|
import { useAuthStore } from '@/stores/auth';
|
||||||
|
|
||||||
const formRef = ref<FormInstance>();
|
const formRef = ref<FormInstance>();
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
const authStore = useAuthStore();
|
||||||
|
|
||||||
const formData = reactive<{
|
const formData = reactive<{
|
||||||
title: string;
|
title: string;
|
||||||
@ -186,17 +188,44 @@ watch(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 获取当前登录用户的显示名称
|
||||||
const getLoginUser = () => {
|
const getLoginUser = () => {
|
||||||
const userStr = localStorage.getItem("user");
|
const user = authStore.user;
|
||||||
if (userStr) {
|
if (!user) {
|
||||||
try {
|
// 如果 authStore 中没有,尝试从 localStorage 获取
|
||||||
const user = JSON.parse(userStr);
|
const userStr = localStorage.getItem("userInfo");
|
||||||
return user.username || user.name || user.userName || "";
|
if (userStr) {
|
||||||
} catch (e) {
|
try {
|
||||||
return "";
|
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 () => {
|
const fetchDetail = async () => {
|
||||||
|
|||||||
@ -154,7 +154,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, onMounted } from "vue";
|
import { ref, computed, onMounted, markRaw } from "vue";
|
||||||
import { Chart, registerables } from "chart.js";
|
import { Chart, registerables } from "chart.js";
|
||||||
import {
|
import {
|
||||||
Money,
|
Money,
|
||||||
@ -165,7 +165,11 @@ import {
|
|||||||
ArrowDown,
|
ArrowDown,
|
||||||
MoreFilled,
|
MoreFilled,
|
||||||
Plus,
|
Plus,
|
||||||
|
Document,
|
||||||
} from "@element-plus/icons-vue";
|
} from "@element-plus/icons-vue";
|
||||||
|
import { getKnowledgeCount } from "@/api/knowledge";
|
||||||
|
import { getPlatformStats, getTenantStats } from "@/api/dashboard";
|
||||||
|
import { useAuthStore } from "@/stores/auth";
|
||||||
|
|
||||||
Chart.register(...registerables);
|
Chart.register(...registerables);
|
||||||
|
|
||||||
@ -179,38 +183,111 @@ const currentDate = computed(() => {
|
|||||||
return `${month}月${day}日 ${weekday}`;
|
return `${month}月${day}日 ${weekday}`;
|
||||||
});
|
});
|
||||||
|
|
||||||
// 统计数据
|
// 统计数据(使用 markRaw 避免图标组件被响应式化)
|
||||||
const stats = ref([
|
const stats = ref([
|
||||||
{
|
{
|
||||||
label: "本月收入",
|
label: "知识库",
|
||||||
value: "¥ 35,800",
|
value: "0",
|
||||||
change: 10.4,
|
change: 0,
|
||||||
icon: Money,
|
icon: markRaw(Document),
|
||||||
type: "income",
|
type: "knowledge",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "新用户",
|
label: "新用户",
|
||||||
value: "842",
|
value: "0",
|
||||||
change: 3.7,
|
change: 0,
|
||||||
icon: User,
|
icon: markRaw(User),
|
||||||
type: "users",
|
type: "users",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "订单量",
|
label: "员工数",
|
||||||
value: "1,376",
|
value: "0",
|
||||||
change: -1.6,
|
change: 0,
|
||||||
icon: ShoppingCart,
|
icon: markRaw(ShoppingCart),
|
||||||
type: "orders",
|
type: "employees",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "活跃率",
|
label: "租户数",
|
||||||
value: "27.3%",
|
value: "0",
|
||||||
change: 2.2,
|
change: 0,
|
||||||
icon: TrendCharts,
|
icon: markRaw(TrendCharts),
|
||||||
type: "active",
|
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([
|
const tasks = ref([
|
||||||
{
|
{
|
||||||
@ -272,6 +349,13 @@ const handleTaskChange = (task: any) => {
|
|||||||
|
|
||||||
// 初始化图表
|
// 初始化图表
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
// 根据登录类型加载不同的统计数据
|
||||||
|
if (isEmployee.value) {
|
||||||
|
fetchTenantStats();
|
||||||
|
} else {
|
||||||
|
fetchPlatformStats();
|
||||||
|
}
|
||||||
|
|
||||||
// 折线图
|
// 折线图
|
||||||
const lineChartEl = document.getElementById("lineChart") as HTMLCanvasElement | null;
|
const lineChartEl = document.getElementById("lineChart") as HTMLCanvasElement | null;
|
||||||
if (!lineChartEl) {
|
if (!lineChartEl) {
|
||||||
@ -486,6 +570,18 @@ onMounted(() => {
|
|||||||
background: linear-gradient(135deg, #8b5cf6 0%, #a78bfa 100%);
|
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 {
|
.stat-content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
|||||||
@ -160,17 +160,19 @@ import {
|
|||||||
Select,
|
Select,
|
||||||
RefreshLeft,
|
RefreshLeft,
|
||||||
} from '@element-plus/icons-vue';
|
} from '@element-plus/icons-vue';
|
||||||
import { getRoleByTenantId } from '@/api/role';
|
import { getRoleByTenantId, getAllRoles } from '@/api/role';
|
||||||
import {
|
import {
|
||||||
getAllMenuPermissions,
|
getAllMenuPermissions,
|
||||||
getRolePermissions,
|
getRolePermissions,
|
||||||
assignRolePermissions,
|
assignRolePermissions,
|
||||||
} from '@/api/permission';
|
} from '@/api/permission';
|
||||||
|
import { useAuthStore } from '@/stores/auth';
|
||||||
|
|
||||||
// 角色相关
|
// 角色相关
|
||||||
const roleList = ref([]);
|
const roleList = ref([]);
|
||||||
const roleSearchQuery = ref('');
|
const roleSearchQuery = ref('');
|
||||||
const selectedRole = ref(null);
|
const selectedRole = ref(null);
|
||||||
|
const authStore = useAuthStore();
|
||||||
|
|
||||||
// 权限相关
|
// 权限相关
|
||||||
const allMenus = ref([]);
|
const allMenus = ref([]);
|
||||||
@ -200,33 +202,56 @@ const filteredRoles = computed(() => {
|
|||||||
|
|
||||||
// 获取当前租户ID
|
// 获取当前租户ID
|
||||||
const getCurrentTenantId = () => {
|
const getCurrentTenantId = () => {
|
||||||
|
if (authStore.user && authStore.user.tenant_id) {
|
||||||
|
return authStore.user.tenant_id;
|
||||||
|
}
|
||||||
const userInfo = JSON.parse(localStorage.getItem('userInfo') || '{}');
|
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 () => {
|
const loadRoles = async () => {
|
||||||
try {
|
try {
|
||||||
const tenantId = getCurrentTenantId();
|
// 如果是平台用户,使用 getAllRoles 获取所有角色(包括平台角色和租户角色)
|
||||||
const res = await getRoleByTenantId(tenantId);
|
// 如果是租户员工,使用 getRoleByTenantId 获取当前租户的角色
|
||||||
|
let res;
|
||||||
|
if (isPlatformUser()) {
|
||||||
|
res = await getAllRoles();
|
||||||
|
} else {
|
||||||
|
const tenantId = getCurrentTenantId();
|
||||||
|
res = await getRoleByTenantId(tenantId);
|
||||||
|
}
|
||||||
|
|
||||||
// 兼容两种响应格式:success 和 code
|
// 兼容两种响应格式:success 和 code
|
||||||
const isSuccess = res.success || res.code === 0;
|
const isSuccess = res.success || res.code === 0;
|
||||||
if (isSuccess && res.data) {
|
if (isSuccess && res.data) {
|
||||||
roleList.value = res.data;
|
roleList.value = Array.isArray(res.data) ? res.data : [];
|
||||||
} else {
|
} else {
|
||||||
ElMessage.warning(res.message || '未获取到角色数据');
|
ElMessage.warning(res.message || '未获取到角色数据');
|
||||||
|
roleList.value = [];
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('加载角色列表失败:', error);
|
console.error('加载角色列表失败:', error);
|
||||||
ElMessage.error('加载角色列表失败');
|
ElMessage.error('加载角色列表失败');
|
||||||
|
roleList.value = [];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 加载所有菜单权限
|
// 加载所有菜单权限(根据选中的角色过滤)
|
||||||
const loadAllMenus = async () => {
|
const loadAllMenus = async (roleId = null) => {
|
||||||
try {
|
try {
|
||||||
const res = await getAllMenuPermissions();
|
// 如果提供了roleId,传递给接口用于根据角色的default值过滤菜单
|
||||||
|
const params = roleId ? { roleId } : {};
|
||||||
|
const res = await getAllMenuPermissions(params);
|
||||||
|
|
||||||
if (res.success && res.data) {
|
if (res.success && res.data) {
|
||||||
allMenus.value = res.data;
|
allMenus.value = res.data;
|
||||||
@ -267,6 +292,8 @@ const buildMenuTree = () => {
|
|||||||
// 选择角色
|
// 选择角色
|
||||||
const selectRole = async (role) => {
|
const selectRole = async (role) => {
|
||||||
selectedRole.value = role;
|
selectedRole.value = role;
|
||||||
|
// 重新加载菜单列表(根据角色的default值过滤)
|
||||||
|
await loadAllMenus(role.roleId);
|
||||||
await loadRolePermissions(role.roleId);
|
await loadRolePermissions(role.roleId);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -96,7 +96,7 @@ import {
|
|||||||
ElMessageBox,
|
ElMessageBox,
|
||||||
} from "element-plus";
|
} from "element-plus";
|
||||||
import { Plus, View, Edit, Delete, Refresh } from "@element-plus/icons-vue";
|
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 { useAuthStore } from "@/stores/auth";
|
||||||
import RoleEditDialog from "./components/edit.vue";
|
import RoleEditDialog from "./components/edit.vue";
|
||||||
import RoleDetailDialog from "./components/detail.vue";
|
import RoleDetailDialog from "./components/detail.vue";
|
||||||
@ -124,13 +124,30 @@ const getCurrentTenantId = () => {
|
|||||||
if (userInfo) {
|
if (userInfo) {
|
||||||
try {
|
try {
|
||||||
const user = JSON.parse(userInfo);
|
const user = JSON.parse(userInfo);
|
||||||
return user.tenant_id || user.tenantId || null;
|
return user.tenant_id || user.tenantId || 0;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Failed to parse user info:', 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;
|
loading.value = true;
|
||||||
error.value = "";
|
error.value = "";
|
||||||
try {
|
try {
|
||||||
const tenantId = getCurrentTenantId();
|
// 如果是平台用户,使用 getAllRoles 获取所有角色(包括平台角色和租户角色)
|
||||||
const res = await getRoleByTenantId(tenantId);
|
// 如果是租户员工,使用 getRoleByTenantId 获取当前租户的角色
|
||||||
|
let res;
|
||||||
|
if (isPlatformUser()) {
|
||||||
|
res = await getAllRoles();
|
||||||
|
} else {
|
||||||
|
const tenantId = getCurrentTenantId();
|
||||||
|
res = await getRoleByTenantId(tenantId);
|
||||||
|
}
|
||||||
|
|
||||||
if (res.code === 0 && res.data) {
|
if (res.code === 0 && res.data) {
|
||||||
roles.value = Array.isArray(res.data) ? res.data : [];
|
roles.value = Array.isArray(res.data) ? res.data : [];
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -231,7 +231,7 @@ import {
|
|||||||
getUserInfo,
|
getUserInfo,
|
||||||
changePassword,
|
changePassword,
|
||||||
} from "@/api/user";
|
} from "@/api/user";
|
||||||
import { getRoleByTenantId } from "@/api/role";
|
import { getRoleByTenantId, getAllRoles } from "@/api/role";
|
||||||
import { getTenantDepartments } from "@/api/department";
|
import { getTenantDepartments } from "@/api/department";
|
||||||
import { getTenantPositions, getPositionsByDepartment } from "@/api/position";
|
import { getTenantPositions, getPositionsByDepartment } from "@/api/position";
|
||||||
import { useAuthStore } from "@/stores/auth";
|
import { useAuthStore } from "@/stores/auth";
|
||||||
@ -279,12 +279,37 @@ const getCurrentTenantId = () => {
|
|||||||
return 0;
|
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 () => {
|
const fetchRoles = async () => {
|
||||||
loadingRoles.value = true;
|
loadingRoles.value = true;
|
||||||
try {
|
try {
|
||||||
const tenantId = getCurrentTenantId();
|
// 如果是平台用户,使用 getAllRoles 获取所有角色(包括平台角色和租户角色)
|
||||||
const res = await getRoleByTenantId(tenantId);
|
// 如果是租户员工,使用 getRoleByTenantId 获取当前租户的角色
|
||||||
|
let res;
|
||||||
|
if (isPlatformUser()) {
|
||||||
|
res = await getAllRoles();
|
||||||
|
} else {
|
||||||
|
const tenantId = getCurrentTenantId();
|
||||||
|
res = await getRoleByTenantId(tenantId);
|
||||||
|
}
|
||||||
|
|
||||||
if (res.code === 0 && res.data) {
|
if (res.code === 0 && res.data) {
|
||||||
roleList.value = Array.isArray(res.data) ? res.data : [];
|
roleList.value = Array.isArray(res.data) ? res.data : [];
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -93,7 +93,7 @@ func (c *AuthController) Login() {
|
|||||||
"nickname": user.Nickname,
|
"nickname": user.Nickname,
|
||||||
"tenant_id": user.TenantId,
|
"tenant_id": user.TenantId,
|
||||||
"role": user.Role, // 角色ID
|
"role": user.Role, // 角色ID
|
||||||
"type": "user", // 标识是用户登录
|
"type": "user", // 标识是用户登录
|
||||||
}
|
}
|
||||||
} else if employee != nil {
|
} else if employee != nil {
|
||||||
// 员工登录
|
// 员工登录
|
||||||
@ -101,16 +101,16 @@ func (c *AuthController) Login() {
|
|||||||
usernameForToken = employee.EmployeeNo
|
usernameForToken = employee.EmployeeNo
|
||||||
tenantId = employee.TenantId
|
tenantId = employee.TenantId
|
||||||
userInfo = map[string]interface{}{
|
userInfo = map[string]interface{}{
|
||||||
"id": employee.Id,
|
"id": employee.Id,
|
||||||
"username": employee.EmployeeNo,
|
"username": employee.EmployeeNo,
|
||||||
"name": employee.Name,
|
"name": employee.Name,
|
||||||
"email": employee.Email,
|
"email": employee.Email,
|
||||||
"phone": employee.Phone,
|
"phone": employee.Phone,
|
||||||
"tenant_id": employee.TenantId,
|
"tenant_id": employee.TenantId,
|
||||||
"department_id": employee.DepartmentId,
|
"department_id": employee.DepartmentId,
|
||||||
"position_id": employee.PositionId,
|
"position_id": employee.PositionId,
|
||||||
"role": employee.Role, // 角色ID
|
"role": employee.Role, // 角色ID
|
||||||
"type": "employee", // 标识是员工登录
|
"type": "employee", // 标识是员工登录
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
c.Data["json"] = map[string]interface{}{
|
c.Data["json"] = map[string]interface{}{
|
||||||
@ -122,7 +122,11 @@ func (c *AuthController) Login() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 使用models包中的GenerateToken函数生成token
|
// 使用models包中的GenerateToken函数生成token
|
||||||
tokenString, err = models.GenerateToken(userId, usernameForToken, tenantId)
|
userType := "user"
|
||||||
|
if employee != nil {
|
||||||
|
userType = "employee"
|
||||||
|
}
|
||||||
|
tokenString, err = models.GenerateToken(userId, usernameForToken, tenantId, userType)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Data["json"] = map[string]interface{}{
|
c.Data["json"] = map[string]interface{}{
|
||||||
|
|||||||
108
server/controllers/dashboard.go
Normal file
108
server/controllers/dashboard.go
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"server/models"
|
||||||
|
|
||||||
|
"github.com/beego/beego/v2/client/orm"
|
||||||
|
beego "github.com/beego/beego/v2/server/web"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DashboardController 仪表盘控制器
|
||||||
|
type DashboardController struct {
|
||||||
|
beego.Controller
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPlatformStats 获取平台统计数据(平台用户使用)
|
||||||
|
// @router /api/dashboard/platform-stats [get]
|
||||||
|
func (c *DashboardController) GetPlatformStats() {
|
||||||
|
// 获取租户总数
|
||||||
|
tenants, err := models.GetTenantList()
|
||||||
|
tenantCount := int64(0)
|
||||||
|
if err == nil {
|
||||||
|
tenantCount = int64(len(tenants))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取知识库总数及增长率(所有租户)
|
||||||
|
knowledgeCount, currentMonthKnowledge, lastMonthKnowledge, knowledgeGrowthRate, _ := models.GetKnowledgeCountWithGrowth(0)
|
||||||
|
|
||||||
|
// 获取用户总数(所有租户的用户,使用简单查询)
|
||||||
|
o := orm.NewOrm()
|
||||||
|
var userCount int64
|
||||||
|
o.Raw("SELECT COUNT(*) FROM yz_users WHERE delete_time IS NULL").QueryRow(&userCount)
|
||||||
|
|
||||||
|
// 获取员工总数(所有租户的员工)
|
||||||
|
var employeeCount int64
|
||||||
|
o.Raw("SELECT COUNT(*) FROM yz_tenant_employees WHERE delete_time IS NULL").QueryRow(&employeeCount)
|
||||||
|
|
||||||
|
c.Data["json"] = map[string]interface{}{
|
||||||
|
"code": 0,
|
||||||
|
"message": "success",
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"tenantCount": tenantCount,
|
||||||
|
"userCount": userCount,
|
||||||
|
"knowledgeCount": map[string]interface{}{
|
||||||
|
"total": knowledgeCount,
|
||||||
|
"currentMonth": currentMonthKnowledge,
|
||||||
|
"lastMonth": lastMonthKnowledge,
|
||||||
|
"growthRate": knowledgeGrowthRate,
|
||||||
|
},
|
||||||
|
"employeeCount": employeeCount,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTenantStats 获取租户统计数据(租户员工使用)
|
||||||
|
// @router /api/dashboard/tenant-stats [get]
|
||||||
|
func (c *DashboardController) GetTenantStats() {
|
||||||
|
// 获取租户ID(从JWT token中获取)
|
||||||
|
tenantId := 0
|
||||||
|
if tenantIdVal, ok := c.Ctx.Input.GetData("tenantId").(int); ok && tenantIdVal > 0 {
|
||||||
|
if userType, ok := c.Ctx.Input.GetData("userType").(string); ok && userType == "employee" {
|
||||||
|
tenantId = tenantIdVal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if tenantId <= 0 {
|
||||||
|
c.Data["json"] = map[string]interface{}{
|
||||||
|
"code": 1,
|
||||||
|
"message": "无法获取租户信息",
|
||||||
|
"data": nil,
|
||||||
|
}
|
||||||
|
c.ServeJSON()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取知识库数量及增长率
|
||||||
|
knowledgeCount, currentMonthKnowledge, lastMonthKnowledge, knowledgeGrowthRate, _ := models.GetKnowledgeCountWithGrowth(tenantId)
|
||||||
|
|
||||||
|
// 获取员工数量(简单查询)
|
||||||
|
o := orm.NewOrm()
|
||||||
|
var employeeCount int64
|
||||||
|
o.Raw("SELECT COUNT(*) FROM yz_tenant_employees WHERE tenant_id = ? AND delete_time IS NULL", tenantId).QueryRow(&employeeCount)
|
||||||
|
|
||||||
|
// 获取部门数量
|
||||||
|
var departmentCount int64
|
||||||
|
o.Raw("SELECT COUNT(*) FROM yz_tenant_departments WHERE tenant_id = ? AND delete_time IS NULL", tenantId).QueryRow(&departmentCount)
|
||||||
|
|
||||||
|
// 获取职位数量
|
||||||
|
var positionCount int64
|
||||||
|
o.Raw("SELECT COUNT(*) FROM yz_tenant_positions WHERE tenant_id = ? AND delete_time IS NULL", tenantId).QueryRow(&positionCount)
|
||||||
|
|
||||||
|
c.Data["json"] = map[string]interface{}{
|
||||||
|
"code": 0,
|
||||||
|
"message": "success",
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"knowledgeCount": map[string]interface{}{
|
||||||
|
"total": knowledgeCount,
|
||||||
|
"currentMonth": currentMonthKnowledge,
|
||||||
|
"lastMonth": lastMonthKnowledge,
|
||||||
|
"growthRate": knowledgeGrowthRate,
|
||||||
|
},
|
||||||
|
"employeeCount": employeeCount,
|
||||||
|
"departmentCount": departmentCount,
|
||||||
|
"positionCount": positionCount,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
c.ServeJSON()
|
||||||
|
}
|
||||||
@ -58,6 +58,42 @@ func (c *KnowledgeController) List() {
|
|||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetCount 获取知识库数量及增长率
|
||||||
|
// @router /api/knowledge/count [get]
|
||||||
|
func (c *KnowledgeController) GetCount() {
|
||||||
|
// 获取租户ID(如果是员工登录,从JWT token中获取)
|
||||||
|
tenantId := 0
|
||||||
|
if tenantIdVal, ok := c.Ctx.Input.GetData("tenantId").(int); ok && tenantIdVal > 0 {
|
||||||
|
// 检查是否是员工登录(type === "employee")
|
||||||
|
if userType, ok := c.Ctx.Input.GetData("userType").(string); ok && userType == "employee" {
|
||||||
|
tenantId = tenantIdVal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
totalCount, currentMonthCount, lastMonthCount, growthRate, err := models.GetKnowledgeCountWithGrowth(tenantId)
|
||||||
|
if err != nil {
|
||||||
|
c.Data["json"] = map[string]interface{}{
|
||||||
|
"code": 1,
|
||||||
|
"message": "获取知识库数量失败: " + err.Error(),
|
||||||
|
"data": nil,
|
||||||
|
}
|
||||||
|
c.ServeJSON()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Data["json"] = map[string]interface{}{
|
||||||
|
"code": 0,
|
||||||
|
"message": "success",
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"count": totalCount,
|
||||||
|
"currentMonthCount": currentMonthCount,
|
||||||
|
"lastMonthCount": lastMonthCount,
|
||||||
|
"growthRate": growthRate,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
// Detail 获取知识详情
|
// Detail 获取知识详情
|
||||||
// @router /api/knowledge/detail [get]
|
// @router /api/knowledge/detail [get]
|
||||||
func (c *KnowledgeController) Detail() {
|
func (c *KnowledgeController) Detail() {
|
||||||
|
|||||||
@ -6,8 +6,8 @@ import (
|
|||||||
"server/models"
|
"server/models"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
beego "github.com/beego/beego/v2/server/web"
|
|
||||||
"github.com/beego/beego/v2/core/logs"
|
"github.com/beego/beego/v2/core/logs"
|
||||||
|
beego "github.com/beego/beego/v2/server/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PermissionController 权限管理控制器
|
// PermissionController 权限管理控制器
|
||||||
@ -16,8 +16,49 @@ type PermissionController struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetAllMenuPermissions 获取所有菜单权限列表(用于分配权限)
|
// GetAllMenuPermissions 获取所有菜单权限列表(用于分配权限)
|
||||||
|
// 根据当前登录用户的权限和选中角色的default值过滤菜单
|
||||||
|
// 如果提供了roleId参数,根据该角色的default值过滤菜单:
|
||||||
|
// - role.default=1(平台用户角色):只能分配default=1或default=0的菜单
|
||||||
|
// - role.default=2(租户用户角色):只能分配default=2或default=0的菜单
|
||||||
func (c *PermissionController) GetAllMenuPermissions() {
|
func (c *PermissionController) GetAllMenuPermissions() {
|
||||||
menus, err := models.GetAllMenuPermissions()
|
// 从JWT中获取用户ID和用户类型
|
||||||
|
userIdData := c.Ctx.Input.GetData("userId")
|
||||||
|
userTypeData := c.Ctx.Input.GetData("userType")
|
||||||
|
|
||||||
|
if userIdData == nil {
|
||||||
|
c.Data["json"] = map[string]interface{}{
|
||||||
|
"success": false,
|
||||||
|
"message": "未获取到用户信息",
|
||||||
|
}
|
||||||
|
c.ServeJSON()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
userId, ok := userIdData.(int)
|
||||||
|
if !ok {
|
||||||
|
c.Data["json"] = map[string]interface{}{
|
||||||
|
"success": false,
|
||||||
|
"message": "用户ID格式错误",
|
||||||
|
}
|
||||||
|
c.ServeJSON()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
userType := "user" // 默认为平台用户
|
||||||
|
if userTypeData != nil {
|
||||||
|
if ut, ok := userTypeData.(string); ok {
|
||||||
|
userType = ut
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取可选的roleId参数(用于根据角色的default值过滤菜单)
|
||||||
|
var roleId int
|
||||||
|
if roleIdParam, err := c.GetInt("roleId"); err == nil && roleIdParam > 0 {
|
||||||
|
roleId = roleIdParam
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据用户类型、权限和角色default值获取菜单列表
|
||||||
|
menus, err := models.GetAllMenuPermissionsForUser(userId, userType, roleId)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Data["json"] = map[string]interface{}{
|
c.Data["json"] = map[string]interface{}{
|
||||||
@ -261,4 +302,3 @@ func (c *PermissionController) CheckPermission() {
|
|||||||
|
|
||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -12,9 +12,33 @@ type RoleController struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetAllRoles 获取所有角色
|
// GetAllRoles 获取所有角色
|
||||||
|
// 根据当前登录用户的 tenant_id 和 userType 过滤角色
|
||||||
// @router /api/roles [get]
|
// @router /api/roles [get]
|
||||||
func (c *RoleController) GetAllRoles() {
|
func (c *RoleController) GetAllRoles() {
|
||||||
roles, err := models.GetAllRoles()
|
// 从JWT中获取租户ID和用户类型
|
||||||
|
tenantIdData := c.Ctx.Input.GetData("tenantId")
|
||||||
|
userTypeData := c.Ctx.Input.GetData("userType")
|
||||||
|
|
||||||
|
tenantId := 0
|
||||||
|
if tenantIdData != nil {
|
||||||
|
if tid, ok := tenantIdData.(int); ok {
|
||||||
|
tenantId = tid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果请求参数中有 tenant_id,优先使用请求参数
|
||||||
|
if requestTenantId, err := c.GetInt("tenant_id"); err == nil && requestTenantId > 0 {
|
||||||
|
tenantId = requestTenantId
|
||||||
|
}
|
||||||
|
|
||||||
|
userType := "user" // 默认为平台用户
|
||||||
|
if userTypeData != nil {
|
||||||
|
if ut, ok := userTypeData.(string); ok {
|
||||||
|
userType = ut
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
roles, err := models.GetAllRoles(tenantId, userType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Data["json"] = map[string]interface{}{
|
c.Data["json"] = map[string]interface{}{
|
||||||
"code": 1,
|
"code": 1,
|
||||||
@ -199,6 +223,21 @@ func (c *RoleController) CreateRole() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果请求中没有 tenant_id,从JWT中获取
|
||||||
|
if role.TenantId == 0 {
|
||||||
|
if tenantIdData := c.Ctx.Input.GetData("tenantId"); tenantIdData != nil {
|
||||||
|
if tid, ok := tenantIdData.(int); ok && tid > 0 {
|
||||||
|
role.TenantId = tid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取当前用户名(用于记录创建操作)
|
||||||
|
if username, ok := c.Ctx.Input.GetData("username").(string); ok && username != "" {
|
||||||
|
role.CreateBy = username
|
||||||
|
role.UpdateBy = username
|
||||||
|
}
|
||||||
|
|
||||||
// 检查角色代码是否已存在
|
// 检查角色代码是否已存在
|
||||||
existingRole, err := models.GetRoleByCode(role.RoleCode)
|
existingRole, err := models.GetRoleByCode(role.RoleCode)
|
||||||
if err == nil && existingRole != nil {
|
if err == nil && existingRole != nil {
|
||||||
@ -291,6 +330,20 @@ func (c *RoleController) UpdateRole() {
|
|||||||
// 设置角色ID
|
// 设置角色ID
|
||||||
role.RoleId = roleId
|
role.RoleId = roleId
|
||||||
|
|
||||||
|
// 如果请求中没有 tenant_id,从JWT中获取
|
||||||
|
if role.TenantId == 0 {
|
||||||
|
if tenantIdData := c.Ctx.Input.GetData("tenantId"); tenantIdData != nil {
|
||||||
|
if tid, ok := tenantIdData.(int); ok && tid > 0 {
|
||||||
|
role.TenantId = tid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取当前用户名(用于记录更新操作)
|
||||||
|
if username, ok := c.Ctx.Input.GetData("username").(string); ok && username != "" {
|
||||||
|
role.UpdateBy = username
|
||||||
|
}
|
||||||
|
|
||||||
err = models.UpdateRole(&role)
|
err = models.UpdateRole(&role)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Data["json"] = map[string]interface{}{
|
c.Data["json"] = map[string]interface{}{
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"server/models"
|
"server/models"
|
||||||
"server/services"
|
|
||||||
|
|
||||||
"github.com/beego/beego/v2/server/web"
|
"github.com/beego/beego/v2/server/web"
|
||||||
"github.com/beego/beego/v2/server/web/context"
|
"github.com/beego/beego/v2/server/web/context"
|
||||||
@ -61,17 +60,16 @@ func JWTAuthMiddleware() web.FilterFunc {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 将用户信息存储在上下文
|
// 将用户信息存储在上下文
|
||||||
ctx.Input.SetData("userId", claims.UserID)
|
ctx.Input.SetData("userId", claims.UserID)
|
||||||
ctx.Input.SetData("username", claims.Username)
|
ctx.Input.SetData("username", claims.Username)
|
||||||
ctx.Input.SetData("tenantId", claims.TenantId)
|
ctx.Input.SetData("tenantId", claims.TenantId)
|
||||||
|
|
||||||
// 判断用户类型:检查userId是否在员工表中
|
// 从token中获取用户类型(如果token中没有,则默认为"user")
|
||||||
// 如果userId在yz_tenant_employees表中存在,则为员工登录;否则为用户登录
|
userType := claims.UserType
|
||||||
userType := "user"
|
if userType == "" {
|
||||||
if services.IsEmployee(claims.UserID) {
|
userType = "user"
|
||||||
userType = "employee"
|
}
|
||||||
|
ctx.Input.SetData("userType", userType)
|
||||||
}
|
}
|
||||||
ctx.Input.SetData("userType", userType)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,11 +15,12 @@ type Claims struct {
|
|||||||
UserID int `json:"user_id"`
|
UserID int `json:"user_id"`
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
TenantId int `json:"tenant_id"` // 租户ID
|
TenantId int `json:"tenant_id"` // 租户ID
|
||||||
|
UserType string `json:"user_type"` // 用户类型:"user" 或 "employee"
|
||||||
jwt.RegisteredClaims
|
jwt.RegisteredClaims
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateToken 生成JWT token
|
// GenerateToken 生成JWT token
|
||||||
func GenerateToken(userID int, username string, tenantId int) (string, error) {
|
func GenerateToken(userID int, username string, tenantId int, userType string) (string, error) {
|
||||||
// 设置token过期时间
|
// 设置token过期时间
|
||||||
expirationTime := time.Now().Add(24 * time.Hour) // 24小时后过期
|
expirationTime := time.Now().Add(24 * time.Hour) // 24小时后过期
|
||||||
|
|
||||||
@ -28,6 +29,7 @@ func GenerateToken(userID int, username string, tenantId int) (string, error) {
|
|||||||
UserID: userID,
|
UserID: userID,
|
||||||
Username: username,
|
Username: username,
|
||||||
TenantId: tenantId,
|
TenantId: tenantId,
|
||||||
|
UserType: userType,
|
||||||
RegisteredClaims: jwt.RegisteredClaims{
|
RegisteredClaims: jwt.RegisteredClaims{
|
||||||
ExpiresAt: jwt.NewNumericDate(expirationTime),
|
ExpiresAt: jwt.NewNumericDate(expirationTime),
|
||||||
IssuedAt: jwt.NewNumericDate(time.Now()),
|
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||||
|
|||||||
@ -232,6 +232,111 @@ func GetAllKnowledge(page, pageSize int, status int8, categoryId int, share int8
|
|||||||
return knowledges, total, nil
|
return knowledges, total, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetKnowledgeCount 获取知识库数量(按租户)
|
||||||
|
func GetKnowledgeCount(tenantId int) (int64, error) {
|
||||||
|
o := orm.NewOrm()
|
||||||
|
whereSQL := "delete_time IS NULL"
|
||||||
|
params := []interface{}{}
|
||||||
|
|
||||||
|
// 如果tenantId > 0,添加租户过滤
|
||||||
|
if tenantId > 0 {
|
||||||
|
whereSQL += " AND tenant_id = ?"
|
||||||
|
params = append(params, tenantId)
|
||||||
|
}
|
||||||
|
|
||||||
|
var count int64
|
||||||
|
err := o.Raw("SELECT COUNT(*) FROM yz_knowledge WHERE "+whereSQL, params...).QueryRow(&count)
|
||||||
|
return count, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetKnowledgeCountWithGrowth 获取知识库总数量及增长率(按租户)
|
||||||
|
// 返回:总数量、本月新增数量、上个月新增数量、增长率
|
||||||
|
func GetKnowledgeCountWithGrowth(tenantId int) (int64, int64, int64, float64, error) {
|
||||||
|
o := orm.NewOrm()
|
||||||
|
|
||||||
|
// 获取当前月份的开始和结束时间
|
||||||
|
now := time.Now()
|
||||||
|
currentYear := now.Year()
|
||||||
|
currentMonth := int(now.Month())
|
||||||
|
|
||||||
|
// 当前月的开始时间
|
||||||
|
currentMonthStart := time.Date(currentYear, time.Month(currentMonth), 1, 0, 0, 0, 0, time.Local)
|
||||||
|
// 当前月的结束时间(下个月的第一天)
|
||||||
|
nextMonth := currentMonth + 1
|
||||||
|
nextYear := currentYear
|
||||||
|
if nextMonth > 12 {
|
||||||
|
nextMonth = 1
|
||||||
|
nextYear++
|
||||||
|
}
|
||||||
|
currentMonthEnd := time.Date(nextYear, time.Month(nextMonth), 1, 0, 0, 0, 0, time.Local)
|
||||||
|
|
||||||
|
// 上个月的时间范围
|
||||||
|
lastMonth := currentMonth - 1
|
||||||
|
lastYear := currentYear
|
||||||
|
if lastMonth < 1 {
|
||||||
|
lastMonth = 12
|
||||||
|
lastYear--
|
||||||
|
}
|
||||||
|
lastMonthStart := time.Date(lastYear, time.Month(lastMonth), 1, 0, 0, 0, 0, time.Local)
|
||||||
|
lastMonthEnd := currentMonthStart
|
||||||
|
|
||||||
|
// 构建查询条件
|
||||||
|
baseWhere := "delete_time IS NULL"
|
||||||
|
tenantFilter := ""
|
||||||
|
params := []interface{}{}
|
||||||
|
|
||||||
|
if tenantId > 0 {
|
||||||
|
tenantFilter = " AND tenant_id = ?"
|
||||||
|
params = append(params, tenantId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询总数量(所有未删除的知识库)
|
||||||
|
totalWhere := baseWhere + tenantFilter
|
||||||
|
var totalCount int64
|
||||||
|
totalParams := params
|
||||||
|
if len(totalParams) == 0 {
|
||||||
|
err := o.Raw("SELECT COUNT(*) FROM yz_knowledge WHERE " + totalWhere).QueryRow(&totalCount)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, 0, 0, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err := o.Raw("SELECT COUNT(*) FROM yz_knowledge WHERE "+totalWhere, totalParams...).QueryRow(&totalCount)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, 0, 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询本月新增数量(创建时间在当月范围内的)
|
||||||
|
currentWhere := baseWhere + " AND create_time >= ? AND create_time < ?" + tenantFilter
|
||||||
|
currentParams := append([]interface{}{currentMonthStart, currentMonthEnd}, params...)
|
||||||
|
|
||||||
|
var currentMonthCount int64
|
||||||
|
err := o.Raw("SELECT COUNT(*) FROM yz_knowledge WHERE "+currentWhere, currentParams...).QueryRow(¤tMonthCount)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, 0, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询上个月新增数量
|
||||||
|
lastWhere := baseWhere + " AND create_time >= ? AND create_time < ?" + tenantFilter
|
||||||
|
lastParams := append([]interface{}{lastMonthStart, lastMonthEnd}, params...)
|
||||||
|
|
||||||
|
var lastMonthCount int64
|
||||||
|
err = o.Raw("SELECT COUNT(*) FROM yz_knowledge WHERE "+lastWhere, lastParams...).QueryRow(&lastMonthCount)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, 0, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算增长率(本月新增相比上个月新增的增长率)
|
||||||
|
var growthRate float64
|
||||||
|
if lastMonthCount > 0 {
|
||||||
|
growthRate = float64(currentMonthCount-lastMonthCount) / float64(lastMonthCount) * 100
|
||||||
|
} else if currentMonthCount > 0 {
|
||||||
|
growthRate = 100.0 // 上个月为0,这个月有数据,增长100%
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalCount, currentMonthCount, lastMonthCount, growthRate, nil
|
||||||
|
}
|
||||||
|
|
||||||
// UpdateKnowledge 更新知识
|
// UpdateKnowledge 更新知识
|
||||||
func UpdateKnowledge(id int, k *Knowledge, tenantId int) error {
|
func UpdateKnowledge(id int, k *Knowledge, tenantId int) error {
|
||||||
o := orm.NewOrm()
|
o := orm.NewOrm()
|
||||||
|
|||||||
@ -40,6 +40,7 @@ type MenuPermission struct {
|
|||||||
MenuType int `json:"menu_type"` // 1: 页面菜单, 2: API接口
|
MenuType int `json:"menu_type"` // 1: 页面菜单, 2: API接口
|
||||||
Permission string `json:"permission"` // 权限标识
|
Permission string `json:"permission"` // 权限标识
|
||||||
ParentId int `json:"parent_id"`
|
ParentId int `json:"parent_id"`
|
||||||
|
Default int8 `json:"default"` // 默认可见性:0-全局,1-平台用户,2-租户用户
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -193,16 +194,232 @@ func GetRolePermissions(roleId int) (*RolePermission, error) {
|
|||||||
// 获取所有菜单权限列表(用于分配权限时展示,未删除的)
|
// 获取所有菜单权限列表(用于分配权限时展示,未删除的)
|
||||||
func GetAllMenuPermissions() ([]*MenuPermission, error) {
|
func GetAllMenuPermissions() ([]*MenuPermission, error) {
|
||||||
o := orm.NewOrm()
|
o := orm.NewOrm()
|
||||||
var menus []*MenuPermission
|
|
||||||
|
|
||||||
_, err := o.Raw("SELECT id as menu_id, name as menu_name, path, menu_type, permission, parent_id FROM yz_menus WHERE delete_time IS NULL ORDER BY parent_id, `order`").QueryRows(&menus)
|
// 查询菜单(菜单表没有default字段,直接使用0作为默认值)
|
||||||
|
var resultsWithoutDefault []struct {
|
||||||
|
MenuId int
|
||||||
|
MenuName string
|
||||||
|
Path string
|
||||||
|
MenuType int
|
||||||
|
Permission sql.NullString
|
||||||
|
ParentId int
|
||||||
|
}
|
||||||
|
_, err := o.Raw("SELECT id as menu_id, name as menu_name, path, menu_type, permission, parent_id FROM yz_menus WHERE delete_time IS NULL ORDER BY parent_id, `order`").QueryRows(&resultsWithoutDefault)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("获取菜单列表失败: %v", err)
|
return nil, fmt.Errorf("获取菜单列表失败: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 转换为MenuPermission结构,default字段设为0(全局可见)
|
||||||
|
menus := make([]*MenuPermission, 0, len(resultsWithoutDefault))
|
||||||
|
for _, r := range resultsWithoutDefault {
|
||||||
|
menu := &MenuPermission{
|
||||||
|
MenuId: r.MenuId,
|
||||||
|
MenuName: r.MenuName,
|
||||||
|
Path: r.Path,
|
||||||
|
MenuType: r.MenuType,
|
||||||
|
ParentId: r.ParentId,
|
||||||
|
Default: 0, // 默认值为0(全局可见),因为菜单表没有default字段
|
||||||
|
}
|
||||||
|
if r.Permission.Valid {
|
||||||
|
menu.Permission = r.Permission.String
|
||||||
|
} else {
|
||||||
|
menu.Permission = ""
|
||||||
|
}
|
||||||
|
menus = append(menus, menu)
|
||||||
|
}
|
||||||
|
|
||||||
return menus, nil
|
return menus, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetAllMenuPermissionsForUser 根据当前登录用户的权限获取可分配的菜单列表
|
||||||
|
// userType: "user" 表示平台用户(可以看到所有菜单),"employee" 表示租户员工
|
||||||
|
// roleId: 可选的角色ID,如果提供则根据该角色的default值过滤菜单
|
||||||
|
// 设计说明:
|
||||||
|
// - 平台用户:可以看到所有菜单,可以给任何角色分配任何菜单
|
||||||
|
// - 租户员工:在权限分配界面(提供roleId时)只能看到平台管理员已经分配给自己的菜单(包括父菜单)
|
||||||
|
// - 租户员工:在菜单显示时(不提供roleId时)只看到自己有权限的菜单
|
||||||
|
func GetAllMenuPermissionsForUser(userId int, userType string, roleId int) ([]*MenuPermission, error) {
|
||||||
|
o := orm.NewOrm()
|
||||||
|
|
||||||
|
// 如果提供了roleId,获取角色的default值用于过滤菜单
|
||||||
|
var roleDefault int8 = 0 // 0表示全局,不进行过滤
|
||||||
|
if roleId > 0 {
|
||||||
|
role, err := GetRoleById(roleId)
|
||||||
|
if err == nil && role != nil {
|
||||||
|
roleDefault = role.Default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是平台用户,返回所有菜单(根据roleDefault过滤)
|
||||||
|
if userType == "user" {
|
||||||
|
allMenus, err := GetAllMenuPermissions()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果roleDefault>0,根据角色的default值过滤菜单
|
||||||
|
if roleDefault > 0 {
|
||||||
|
filteredMenus := make([]*MenuPermission, 0)
|
||||||
|
for _, menu := range allMenus {
|
||||||
|
// 角色default=1(平台用户角色):只能分配default=1或default=0的菜单
|
||||||
|
// 角色default=2(租户用户角色):只能分配default=2或default=0的菜单
|
||||||
|
if menu.Default == 0 || menu.Default == roleDefault {
|
||||||
|
filteredMenus = append(filteredMenus, menu)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filteredMenus, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return allMenus, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是租户员工
|
||||||
|
if userType == "employee" {
|
||||||
|
// 获取员工信息
|
||||||
|
var employee Employee
|
||||||
|
err := o.Raw("SELECT * FROM yz_tenant_employees WHERE id = ? AND delete_time IS NULL", userId).QueryRow(&employee)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("员工不存在: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果员工没有角色,返回空列表
|
||||||
|
if employee.Role == 0 {
|
||||||
|
return []*MenuPermission{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取员工角色的菜单ID列表(这是平台管理员分配给该员工的菜单)
|
||||||
|
menuIds, err := GetRoleMenus(employee.Role)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("获取角色菜单失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有权限,返回空列表
|
||||||
|
if len(menuIds) == 0 {
|
||||||
|
return []*MenuPermission{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果提供了roleId(权限分配界面),需要包含父菜单
|
||||||
|
// 如果没有提供roleId(菜单显示),也需要包含父菜单(但这里已经在GetTenantMenus中处理了)
|
||||||
|
// 为了性能优化,一次性查询所有菜单的父子关系
|
||||||
|
type menuParent struct {
|
||||||
|
Id int
|
||||||
|
ParentId int
|
||||||
|
}
|
||||||
|
var allMenuParents []menuParent
|
||||||
|
_, err = o.Raw("SELECT id, parent_id FROM yz_menus WHERE delete_time IS NULL").QueryRows(&allMenuParents)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("获取菜单父子关系失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建菜单ID到父菜单ID的映射
|
||||||
|
menuParentMap := make(map[int]int)
|
||||||
|
for _, mp := range allMenuParents {
|
||||||
|
menuParentMap[mp.Id] = mp.ParentId
|
||||||
|
}
|
||||||
|
|
||||||
|
// 递归查找所有父菜单ID(使用内存中的映射,避免数据库查询)
|
||||||
|
parentIds := make(map[int]bool)
|
||||||
|
var findParents func(pid int)
|
||||||
|
findParents = func(pid int) {
|
||||||
|
if pid == 0 || parentIds[pid] {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
parentIds[pid] = true
|
||||||
|
if parentId, exists := menuParentMap[pid]; exists && parentId > 0 {
|
||||||
|
findParents(parentId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 为每个菜单查找其父菜单
|
||||||
|
for _, menuId := range menuIds {
|
||||||
|
if parentId, exists := menuParentMap[menuId]; exists && parentId > 0 {
|
||||||
|
findParents(parentId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 合并原始菜单ID和父菜单ID
|
||||||
|
allMenuIds := make(map[int]bool)
|
||||||
|
for _, id := range menuIds {
|
||||||
|
allMenuIds[id] = true
|
||||||
|
}
|
||||||
|
for pid := range parentIds {
|
||||||
|
allMenuIds[pid] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建IN查询的占位符和参数
|
||||||
|
finalMenuIds := make([]int, 0, len(allMenuIds))
|
||||||
|
for id := range allMenuIds {
|
||||||
|
finalMenuIds = append(finalMenuIds, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
placeholders := make([]string, len(finalMenuIds))
|
||||||
|
args := make([]interface{}, len(finalMenuIds))
|
||||||
|
for i, id := range finalMenuIds {
|
||||||
|
placeholders[i] = "?"
|
||||||
|
args[i] = id
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询菜单(包括父菜单)
|
||||||
|
type menuResult struct {
|
||||||
|
MenuId int
|
||||||
|
MenuName string
|
||||||
|
Path string
|
||||||
|
MenuType int
|
||||||
|
Permission sql.NullString
|
||||||
|
ParentId int
|
||||||
|
}
|
||||||
|
|
||||||
|
var results []menuResult
|
||||||
|
query := fmt.Sprintf("SELECT id as menu_id, name as menu_name, path, menu_type, permission, parent_id FROM yz_menus WHERE id IN (%s) AND delete_time IS NULL ORDER BY parent_id, `order`", strings.Join(placeholders, ","))
|
||||||
|
_, err = o.Raw(query, args...).QueryRows(&results)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("获取菜单列表失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 转换为MenuPermission结构
|
||||||
|
menus := make([]*MenuPermission, 0, len(results))
|
||||||
|
for _, r := range results {
|
||||||
|
menu := &MenuPermission{
|
||||||
|
MenuId: r.MenuId,
|
||||||
|
MenuName: r.MenuName,
|
||||||
|
Path: r.Path,
|
||||||
|
MenuType: r.MenuType,
|
||||||
|
ParentId: r.ParentId,
|
||||||
|
Default: 0, // 默认值为0(全局可见),因为菜单表没有default字段
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理permission字段
|
||||||
|
if r.Permission.Valid {
|
||||||
|
menu.Permission = r.Permission.String
|
||||||
|
} else {
|
||||||
|
menu.Permission = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
menus = append(menus, menu)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果roleDefault>0,根据角色的default值进一步过滤菜单
|
||||||
|
// 但由于菜单表没有default字段,所有菜单都是default=0,所以这里实际上不会过滤
|
||||||
|
if roleDefault > 0 {
|
||||||
|
filteredMenus := make([]*MenuPermission, 0)
|
||||||
|
for _, menu := range menus {
|
||||||
|
// 角色default=1(平台用户角色):只能分配default=1或default=0的菜单
|
||||||
|
// 角色default=2(租户用户角色):只能分配default=2或default=0的菜单
|
||||||
|
// 由于菜单表没有default字段,所有菜单都是default=0,所以所有菜单都可以分配
|
||||||
|
if menu.Default == 0 || menu.Default == roleDefault {
|
||||||
|
filteredMenus = append(filteredMenus, menu)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filteredMenus, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return menus, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 未知的用户类型,返回空列表
|
||||||
|
return []*MenuPermission{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// 为角色分配权限(菜单)- 更新JSON字段
|
// 为角色分配权限(菜单)- 更新JSON字段
|
||||||
func AssignRolePermissions(roleId int, menuIds []int, createBy string) error {
|
func AssignRolePermissions(roleId int, menuIds []int, createBy string) error {
|
||||||
o := orm.NewOrm()
|
o := orm.NewOrm()
|
||||||
|
|||||||
@ -173,7 +173,9 @@ func GetRoleById(roleId int) (*Role, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetAllRoles 获取所有角色(未删除的)
|
// GetAllRoles 获取所有角色(未删除的)
|
||||||
func GetAllRoles() ([]*Role, error) {
|
// tenantId: 租户ID,0表示所有租户
|
||||||
|
// userType: 用户类型,"user"表示平台用户,"employee"表示租户员工
|
||||||
|
func GetAllRoles(tenantId int, userType string) ([]*Role, error) {
|
||||||
o := orm.NewOrm()
|
o := orm.NewOrm()
|
||||||
var roles []*Role
|
var roles []*Role
|
||||||
|
|
||||||
@ -195,7 +197,33 @@ func GetAllRoles() ([]*Role, error) {
|
|||||||
UpdateBy string
|
UpdateBy string
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := o.Raw("SELECT role_id, tenant_id, `default`, role_code, role_name, description, CAST(IFNULL(menu_ids, '[]') AS CHAR) as menu_ids, status, sort_order, create_time, update_time, delete_time, create_by, update_by FROM yz_roles WHERE delete_time IS NULL ORDER BY sort_order ASC, role_id ASC").QueryRows(&results)
|
// 构建查询条件
|
||||||
|
var query string
|
||||||
|
var args []interface{}
|
||||||
|
|
||||||
|
// 如果是平台用户(user),可以看到所有角色
|
||||||
|
if userType == "user" {
|
||||||
|
query = "SELECT role_id, tenant_id, `default`, role_code, role_name, description, CAST(IFNULL(menu_ids, '[]') AS CHAR) as menu_ids, status, sort_order, create_time, update_time, delete_time, create_by, update_by FROM yz_roles WHERE delete_time IS NULL ORDER BY sort_order ASC, role_id ASC"
|
||||||
|
args = []interface{}{}
|
||||||
|
} else {
|
||||||
|
// 如果是租户员工(employee),根据 tenant_id 和 default 过滤
|
||||||
|
// 规则:
|
||||||
|
// 1. default=0: 全局角色,所有租户可见
|
||||||
|
// 2. default=1: 平台用户角色,租户员工不可见
|
||||||
|
// 3. default=2: 租户用户角色,只有对应租户可见
|
||||||
|
// 4. tenant_id=0: 全局角色
|
||||||
|
// 5. tenant_id=当前租户ID: 当前租户的角色
|
||||||
|
if tenantId > 0 {
|
||||||
|
query = "SELECT role_id, tenant_id, `default`, role_code, role_name, description, CAST(IFNULL(menu_ids, '[]') AS CHAR) as menu_ids, status, sort_order, create_time, update_time, delete_time, create_by, update_by FROM yz_roles WHERE delete_time IS NULL AND ((`default` = 0) OR (`default` = 2 AND (tenant_id = ? OR tenant_id = 0))) ORDER BY sort_order ASC, role_id ASC"
|
||||||
|
args = []interface{}{tenantId}
|
||||||
|
} else {
|
||||||
|
// tenantId=0,只返回全局角色(default=0)
|
||||||
|
query = "SELECT role_id, tenant_id, `default`, role_code, role_name, description, CAST(IFNULL(menu_ids, '[]') AS CHAR) as menu_ids, status, sort_order, create_time, update_time, delete_time, create_by, update_by FROM yz_roles WHERE delete_time IS NULL AND `default` = 0 ORDER BY sort_order ASC, role_id ASC"
|
||||||
|
args = []interface{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := o.Raw(query, args...).QueryRows(&results)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -273,6 +273,7 @@ func init() {
|
|||||||
|
|
||||||
// 知识库路由
|
// 知识库路由
|
||||||
beego.Router("/api/knowledge/list", &controllers.KnowledgeController{}, "get:List")
|
beego.Router("/api/knowledge/list", &controllers.KnowledgeController{}, "get:List")
|
||||||
|
beego.Router("/api/knowledge/count", &controllers.KnowledgeController{}, "get:GetCount")
|
||||||
beego.Router("/api/knowledge/detail", &controllers.KnowledgeController{}, "get:Detail")
|
beego.Router("/api/knowledge/detail", &controllers.KnowledgeController{}, "get:Detail")
|
||||||
beego.Router("/api/knowledge/create", &controllers.KnowledgeController{}, "post:Create")
|
beego.Router("/api/knowledge/create", &controllers.KnowledgeController{}, "post:Create")
|
||||||
beego.Router("/api/knowledge/update", &controllers.KnowledgeController{}, "post:Update")
|
beego.Router("/api/knowledge/update", &controllers.KnowledgeController{}, "post:Update")
|
||||||
@ -309,6 +310,10 @@ func init() {
|
|||||||
beego.Router("/api/permissions/user/menus", &controllers.PermissionController{}, "get:GetUserMenuTree")
|
beego.Router("/api/permissions/user/menus", &controllers.PermissionController{}, "get:GetUserMenuTree")
|
||||||
beego.Router("/api/permissions/check", &controllers.PermissionController{}, "get:CheckPermission")
|
beego.Router("/api/permissions/check", &controllers.PermissionController{}, "get:CheckPermission")
|
||||||
|
|
||||||
|
// 仪表盘路由
|
||||||
|
beego.Router("/api/dashboard/platform-stats", &controllers.DashboardController{}, "get:GetPlatformStats")
|
||||||
|
beego.Router("/api/dashboard/tenant-stats", &controllers.DashboardController{}, "get:GetTenantStats")
|
||||||
|
|
||||||
// 手动配置特殊路由(无法通过自动路由处理的)
|
// 手动配置特殊路由(无法通过自动路由处理的)
|
||||||
beego.Router("/api/allmenu", &controllers.MenuController{}, "get:GetAllMenus")
|
beego.Router("/api/allmenu", &controllers.MenuController{}, "get:GetAllMenus")
|
||||||
beego.Router("/api/program-categories/public", &controllers.ProgramCategoryController{}, "get:GetProgramCategoriesPublic")
|
beego.Router("/api/program-categories/public", &controllers.ProgramCategoryController{}, "get:GetProgramCategoriesPublic")
|
||||||
|
|||||||
Binary file not shown.
Loading…
Reference in New Issue
Block a user