diff --git a/frontend/src/components/OrgPermissionTree.test.js b/frontend/src/components/OrgPermissionTree.test.js
index 08689a5..93a3a90 100644
--- a/frontend/src/components/OrgPermissionTree.test.js
+++ b/frontend/src/components/OrgPermissionTree.test.js
@@ -24,3 +24,21 @@ describe("OrgPermissionTree hierarchy lines", () => {
assert.doesNotMatch(source, /background:\s*var\(--tree-connector-color\)/);
});
});
+
+describe("OrgPermissionTree employee list", () => {
+ it("shows each employee organization path with full-value hover text", () => {
+ assert.match(source, /
组织路径<\/th>/);
+ assert.match(source, /employeeOrganizationPath\(employee\)/);
+ assert.match(source, /class="org-path-value"\s+:title="employeeOrganizationPath\(employee\)"/);
+ assert.match(source, /class="org-path-tail"\s+:title="employeeOrganizationPath\(employee\)"/);
+ assert.match(source, /function employeeOrganizationTail\(employee\)/);
+ assert.doesNotMatch(source, /org-path-pill/);
+ assert.match(source, / | {
+ assert.match(source, / | \s*/);
+ assert.match(source, /\.employee-actions\s*\{[\s\S]*?display:\s*inline-flex;/);
+ assert.doesNotMatch(source, /\.action-row\s*\{[^}]*display:\s*flex;/);
+ });
+});
diff --git a/frontend/src/components/OrgPermissionTree.vue b/frontend/src/components/OrgPermissionTree.vue
index c883a96..a0615de 100644
--- a/frontend/src/components/OrgPermissionTree.vue
+++ b/frontend/src/components/OrgPermissionTree.vue
@@ -54,9 +54,19 @@
+
+
+
+
+
+
+
+
+
| 姓名 |
+ 组织路径 |
电话号码 / 账号 |
岗位 |
角色 |
@@ -67,6 +77,10 @@
| {{ employeeName(employee) }} |
+
+ {{ employeeOrganizationPath(employee) }}
+ {{ employeeOrganizationTail(employee) }}
+ |
{{ employeePhone(employee) }} |
{{ employeePosition(employee) }} |
{{ employeeRoles(employee) }} |
@@ -74,12 +88,14 @@
-
-
+
+
+
+
|
- | {{ selectedEmployees.length ? "没有符合条件的人员" : "当前节点暂无人员" }} |
+ {{ selectedEmployees.length ? "没有符合条件的人员" : "当前节点暂无人员" }} |
@@ -237,7 +253,7 @@ const filteredEmployees = computed(() => {
return selectedEmployees.value;
}
return selectedEmployees.value.filter((employee) =>
- [employeeName(employee), employeePhone(employee), employee.employee_code, employee.username]
+ [employeeName(employee), employeeOrganizationPath(employee), employeePhone(employee), employee.employee_code, employee.username]
.filter(Boolean)
.join(" ")
.toLowerCase()
@@ -393,6 +409,15 @@ function employeePosition(employee) {
return employee?.job_title || employee?.position || employee?.post_name || employee?.title || "未设置岗位";
}
+function employeeOrganizationPath(employee) {
+ return employee?.organization_path || employee?.parent_node_label || "未设置组织";
+}
+
+function employeeOrganizationTail(employee) {
+ const pathParts = Array.isArray(employee?.organization_path_parts) ? employee.organization_path_parts.filter(Boolean) : [];
+ return pathParts.length ? pathParts[pathParts.length - 1] : employeeOrganizationPath(employee);
+}
+
function employeeRoles(employee) {
if (Array.isArray(employee?.role_names) && employee.role_names.length) {
return employee.role_names.join("、");
@@ -764,9 +789,23 @@ function removeEmployee(employee) {
}
.action-row {
- display: flex;
- flex-wrap: wrap;
+ overflow: visible;
+ white-space: nowrap;
+}
+
+.employee-actions {
+ display: inline-flex;
+ align-items: center;
+ justify-content: flex-start;
gap: 8px;
+ min-width: max-content;
+}
+
+.employee-actions .ghost-button {
+ flex: 0 0 auto;
+ min-height: 30px;
+ padding: 0 10px;
+ font-size: 12px;
}
.empty-row {
@@ -779,6 +818,67 @@ function removeEmployee(employee) {
table-layout: fixed;
}
+.org-employee-table .employee-col-name {
+ width: 10%;
+}
+
+.org-employee-table .employee-col-path {
+ width: 30%;
+}
+
+.org-employee-table .employee-col-phone {
+ width: 13%;
+}
+
+.org-employee-table .employee-col-position {
+ width: 10%;
+}
+
+.org-employee-table .employee-col-role {
+ width: 11%;
+}
+
+.org-employee-table .employee-col-status {
+ width: 7%;
+}
+
+.org-employee-table .employee-col-action {
+ width: 19%;
+}
+
+.org-path-cell {
+ min-width: 0;
+}
+
+.org-path-value,
+.org-path-tail {
+ display: block;
+ max-width: 100%;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.org-path-value {
+ color: #334155;
+ font-size: 13px;
+ font-weight: 700;
+ line-height: 1.35;
+}
+
+.org-path-tail {
+ width: fit-content;
+ max-width: 100%;
+ margin-top: 3px;
+ border-radius: 999px;
+ padding: 2px 7px;
+ background: #eef6ff;
+ color: #2563eb;
+ font-size: 11px;
+ font-weight: 750;
+ line-height: 1.2;
+}
+
:deep(.org-tree-branch) {
position: relative;
display: flex;
diff --git a/frontend/src/styles/main.css b/frontend/src/styles/main.css
index e1a9c91..300ecd6 100644
--- a/frontend/src/styles/main.css
+++ b/frontend/src/styles/main.css
@@ -12062,32 +12062,37 @@ body .system-permission-page .tree-layout-card .org-employee-table td {
body .system-permission-page .tree-layout-card .org-employee-table th:nth-child(1),
body .system-permission-page .tree-layout-card .org-employee-table td:nth-child(1) {
- width: 13% !important;
+ width: 10% !important;
}
body .system-permission-page .tree-layout-card .org-employee-table th:nth-child(2),
body .system-permission-page .tree-layout-card .org-employee-table td:nth-child(2) {
- width: 20% !important;
+ width: 30% !important;
}
body .system-permission-page .tree-layout-card .org-employee-table th:nth-child(3),
body .system-permission-page .tree-layout-card .org-employee-table td:nth-child(3) {
- width: 14% !important;
+ width: 13% !important;
}
body .system-permission-page .tree-layout-card .org-employee-table th:nth-child(4),
body .system-permission-page .tree-layout-card .org-employee-table td:nth-child(4) {
- width: 17% !important;
+ width: 10% !important;
}
body .system-permission-page .tree-layout-card .org-employee-table th:nth-child(5),
body .system-permission-page .tree-layout-card .org-employee-table td:nth-child(5) {
- width: 12% !important;
+ width: 11% !important;
}
body .system-permission-page .tree-layout-card .org-employee-table th:nth-child(6),
body .system-permission-page .tree-layout-card .org-employee-table td:nth-child(6) {
- width: 24% !important;
+ width: 7% !important;
+}
+
+body .system-permission-page .tree-layout-card .org-employee-table th:nth-child(7),
+body .system-permission-page .tree-layout-card .org-employee-table td:nth-child(7) {
+ width: 19% !important;
}
body .system-permission-page .tree-layout-card .org-employee-table .action-row {
@@ -12096,9 +12101,17 @@ body .system-permission-page .tree-layout-card .org-employee-table .action-row {
white-space: nowrap !important;
}
+body .system-permission-page .tree-layout-card .org-employee-table .employee-actions {
+ display: inline-flex !important;
+ align-items: center !important;
+ justify-content: flex-start !important;
+ gap: 8px !important;
+ min-width: max-content !important;
+}
+
body .system-permission-page .tree-layout-card .org-employee-table .action-row button {
min-height: 30px !important;
- padding: 0 8px !important;
+ padding: 0 10px !important;
font-size: 12px !important;
}
diff --git a/frontend/src/utils/orgTreeEmployees.js b/frontend/src/utils/orgTreeEmployees.js
index 1e49faa..cde0274 100644
--- a/frontend/src/utils/orgTreeEmployees.js
+++ b/frontend/src/utils/orgTreeEmployees.js
@@ -2,7 +2,7 @@ export function collectOrganizationEmployees(node) {
const employees = [];
const seenKeys = new Set();
- collectFromNode(node, node, employees, seenKeys);
+ collectFromNode(node, node, employees, seenKeys, []);
return employees;
}
@@ -10,40 +10,48 @@ export function countOrganizationEmployees(node) {
return collectOrganizationEmployees(node).length;
}
-function collectFromNode(node, parentNode, employees, seenKeys) {
+function collectFromNode(node, parentNode, employees, seenKeys, pathParts) {
if (!node) {
return;
}
if (isEmployeeNode(node)) {
- appendEmployee(node, parentNode, employees, seenKeys);
+ appendEmployee(node, parentNode, employees, seenKeys, pathParts);
return;
}
+ const currentPathParts = [...pathParts, nodeLabel(node)].filter(Boolean);
+
for (const employee of Array.isArray(node.employees) ? node.employees : []) {
- appendEmployee(employee, node, employees, seenKeys);
+ appendEmployee(employee, node, employees, seenKeys, currentPathParts);
}
for (const child of Array.isArray(node.children) ? node.children : []) {
if (isOrganizationNode(child)) {
- collectFromNode(child, child, employees, seenKeys);
+ collectFromNode(child, child, employees, seenKeys, currentPathParts);
} else if (isEmployeeNode(child)) {
- appendEmployee(child, node, employees, seenKeys);
+ appendEmployee(child, node, employees, seenKeys, currentPathParts);
}
}
}
-function appendEmployee(employee, parentNode, employees, seenKeys) {
+function appendEmployee(employee, parentNode, employees, seenKeys, pathParts) {
const key = employeeKey(employee);
if (!key || seenKeys.has(key)) {
return;
}
+ const organizationPathParts = Array.isArray(employee.organization_path_parts) && employee.organization_path_parts.length
+ ? employee.organization_path_parts
+ : pathParts;
+
seenKeys.add(key);
employees.push({
...employee,
parent_node_id: employee.parent_node_id ?? nodeId(parentNode),
- parent_node_label: employee.parent_node_label ?? nodeLabel(parentNode)
+ parent_node_label: employee.parent_node_label ?? nodeLabel(parentNode),
+ organization_path_parts: organizationPathParts,
+ organization_path: employee.organization_path || organizationPathParts.join(" / ")
});
}
diff --git a/frontend/src/utils/orgTreeEmployees.test.js b/frontend/src/utils/orgTreeEmployees.test.js
index 258cc85..a51cea1 100644
--- a/frontend/src/utils/orgTreeEmployees.test.js
+++ b/frontend/src/utils/orgTreeEmployees.test.js
@@ -40,6 +40,9 @@ describe("organization tree employee aggregation", () => {
assert.deepEqual(employees.map((employee) => employee.employee_name), ["直属主管", "张三", "李四", "王五"]);
assert.equal(employees[1].parent_node_id, 11);
assert.equal(employees[1].parent_node_label, "旭升加工部");
+ assert.deepEqual(employees[1].organization_path_parts, ["旭升车间", "旭升加工部"]);
+ assert.equal(employees[1].organization_path, "旭升车间 / 旭升加工部");
+ assert.equal(employees[3].organization_path, "旭升车间 / 旭升加工部 / 冲压小组");
assert.equal(countOrganizationEmployees(branch), 4);
});
diff --git a/frontend/src/views/SystemPermissionView.test.js b/frontend/src/views/SystemPermissionView.test.js
index f5d9a0a..f893e06 100644
--- a/frontend/src/views/SystemPermissionView.test.js
+++ b/frontend/src/views/SystemPermissionView.test.js
@@ -46,3 +46,28 @@ describe("SystemPermissionView maintenance scrolling", () => {
assert.match(mainStyles, /\.system-permission-page\.system-permission-page--maintenance\s+\.system-permission-maintenance-card\s*\{[\s\S]*?overflow:\s*visible;/);
});
});
+
+describe("SystemPermissionView organization employee table columns", () => {
+ it("keeps the organization employee table aligned after adding the organization path column", () => {
+ assert.match(mainStyles, /\.tree-layout-card\s+\.org-employee-table th:nth-child\(7\),[\s\S]*?td:nth-child\(7\)\s*\{[\s\S]*?width:\s*19%\s*!important;/);
+ assert.match(mainStyles, /\.tree-layout-card\s+\.org-employee-table \.action-row\s*\{[\s\S]*?display:\s*table-cell\s*!important;/);
+ assert.match(mainStyles, /\.tree-layout-card\s+\.org-employee-table \.employee-actions\s*\{[\s\S]*?display:\s*inline-flex\s*!important;[\s\S]*?min-width:\s*max-content\s*!important;/);
+ });
+});
+
+describe("SystemPermissionView authorize drawer", () => {
+ it("uses card-style role selection instead of a native multi-select", () => {
+ const authorizeStart = source.indexOf('v-if="showAuthorizeDrawer"');
+ const authorizeEnd = source.indexOf('v-if="showRoleDrawer"', authorizeStart);
+ const authorizeSource = source.slice(authorizeStart, authorizeEnd);
+
+ assert.doesNotMatch(authorizeSource, /
|