知识库增加租户隔离
This commit is contained in:
@@ -8,6 +8,14 @@ export function getAllMenus() {
|
||||
});
|
||||
}
|
||||
|
||||
// 获取租户菜单(根据角色权限)
|
||||
export function getTenantMenus(roleId) {
|
||||
return request({
|
||||
url: `/api/menus/tenant/${roleId}`,
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
// 更新菜单状态
|
||||
export function updateMenuStatus(menuId, status) {
|
||||
return request({
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
import { ref, computed, onMounted, onUnmounted } from 'vue';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import { useAllDataStore } from '@/stores';
|
||||
import { getAllMenus } from '@/api/menu';
|
||||
import { getAllMenus, getTenantMenus } from '@/api/menu';
|
||||
import MenuTreeItem from './MenuTreeItem.vue';
|
||||
|
||||
const emit = defineEmits(['menu-click']);
|
||||
@@ -196,8 +196,20 @@ const transformMenuData = (menus) => {
|
||||
const fetchMenus = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
// 直接从接口获取菜单数据,不使用缓存
|
||||
const res = await getAllMenus();
|
||||
// 获取用户信息,判断登录类型
|
||||
const userInfo = JSON.parse(localStorage.getItem('userInfo') || '{}');
|
||||
const loginType = userInfo.type; // "user" 或 "employee"
|
||||
const roleId = userInfo.role; // 角色ID
|
||||
|
||||
let res;
|
||||
if (loginType === "employee" && roleId) {
|
||||
// 员工登录,使用getTenantMenus接口
|
||||
res = await getTenantMenus(roleId);
|
||||
} else {
|
||||
// 用户登录,使用getAllMenus接口
|
||||
res = await getAllMenus();
|
||||
}
|
||||
|
||||
if (res && res.success && res.data) {
|
||||
const menuData = res.data;
|
||||
// 转换并排序菜单数据
|
||||
|
||||
@@ -62,7 +62,7 @@ import { useRouter, useRoute } from "vue-router";
|
||||
import { useAllDataStore } from "@/stores";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
import { User, SwitchButton, Sunny, Moon, Refresh } from '@element-plus/icons-vue';
|
||||
import { getAllMenus } from '@/api/menu';
|
||||
import { getAllMenus, getTenantMenus } from '@/api/menu';
|
||||
import { ElMessage } from 'element-plus';
|
||||
|
||||
const router = useRouter();
|
||||
@@ -111,7 +111,20 @@ function saveMenuToCache(menus: Menu[]) {
|
||||
// 从API加载菜单
|
||||
async function loadMenuFromAPI(updateCache = true) {
|
||||
try {
|
||||
const res = await getAllMenus();
|
||||
// 获取用户信息,判断登录类型
|
||||
const userInfo = JSON.parse(localStorage.getItem('userInfo') || '{}');
|
||||
const loginType = userInfo.type; // "user" 或 "employee"
|
||||
const roleId = userInfo.role; // 角色ID
|
||||
|
||||
let res;
|
||||
if (loginType === "employee" && roleId) {
|
||||
// 员工登录,使用getTenantMenus接口
|
||||
res = await getTenantMenus(roleId);
|
||||
} else {
|
||||
// 用户登录,使用getAllMenus接口
|
||||
res = await getAllMenus();
|
||||
}
|
||||
|
||||
const menus = res.data || [];
|
||||
if (updateCache && menus.length > 0) {
|
||||
saveMenuToCache(menus);
|
||||
|
||||
+16
-3
@@ -71,9 +71,22 @@ export async function loadAndAddDynamicRoutes() {
|
||||
// 创建加载 Promise
|
||||
routesLoadingPromise = (async () => {
|
||||
try {
|
||||
// 直接从 API 获取菜单数据
|
||||
const { getAllMenus } = await import("@/api/menu");
|
||||
const res = await getAllMenus();
|
||||
// 获取用户信息,判断登录类型
|
||||
const userInfo = JSON.parse(localStorage.getItem('userInfo') || '{}');
|
||||
const loginType = userInfo.type; // "user" 或 "employee"
|
||||
const roleId = userInfo.role; // 角色ID
|
||||
|
||||
// 根据登录类型选择不同的菜单接口
|
||||
const { getAllMenus, getTenantMenus } = await import("@/api/menu");
|
||||
let res;
|
||||
|
||||
if (loginType === "employee" && roleId) {
|
||||
// 员工登录,使用getTenantMenus接口
|
||||
res = await getTenantMenus(roleId);
|
||||
} else {
|
||||
// 用户登录,使用getAllMenus接口
|
||||
res = await getAllMenus();
|
||||
}
|
||||
|
||||
if (res && res.success && res.data) {
|
||||
// 添加动态路由
|
||||
|
||||
@@ -27,10 +27,6 @@
|
||||
<h3 class="info-title">基本信息</h3>
|
||||
<el-divider />
|
||||
<div class="info-content">
|
||||
<div class="info-item">
|
||||
<span class="info-label">标题:</span>
|
||||
<span class="info-value">{{ formData.title }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">分类:</span>
|
||||
<el-tag size="small" type="info">{{ formData.category }}</el-tag>
|
||||
@@ -190,7 +186,6 @@ function handleDelete() {
|
||||
|
||||
<style scoped lang="less">
|
||||
.knowledge-detail {
|
||||
padding: 24px;
|
||||
min-height: 100%;
|
||||
background-color: var(--el-bg-color-page);
|
||||
}
|
||||
|
||||
@@ -519,7 +519,6 @@ onMounted(() => {
|
||||
|
||||
<style lang="less" scoped>
|
||||
.knowledge-home {
|
||||
padding: 24px;
|
||||
min-height: 100%;
|
||||
background-color: var(--el-bg-color-page);
|
||||
}
|
||||
@@ -806,7 +805,6 @@ onMounted(() => {
|
||||
// 响应式
|
||||
@media (max-width: 768px) {
|
||||
.knowledge-home {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.search-section {
|
||||
|
||||
@@ -41,6 +41,11 @@
|
||||
align="center"
|
||||
min-width="200"
|
||||
/>
|
||||
<el-table-column prop="role" label="角色" width="150" align="center">
|
||||
<template #default="scope">
|
||||
<span>{{ scope.row.roleName || '未分配' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="department" label="部门" width="150" align="center">
|
||||
<template #default="scope">
|
||||
<span>{{ scope.row.departmentName || '未分配' }}</span>
|
||||
@@ -160,6 +165,27 @@
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="角色">
|
||||
<el-select
|
||||
v-model="form.role"
|
||||
placeholder="请选择角色"
|
||||
style="width: 100%"
|
||||
:loading="loadingRoles"
|
||||
clearable
|
||||
>
|
||||
<el-option
|
||||
v-for="role in roleList"
|
||||
:key="role.roleId"
|
||||
:label="role.roleName"
|
||||
:value="role.roleId"
|
||||
>
|
||||
<span>{{ role.roleName }}</span>
|
||||
<span style="color: #8492a6; font-size: 13px; margin-left: 8px;">
|
||||
({{ role.roleCode }})
|
||||
</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="开户行">
|
||||
<el-input v-model="form.bank_name" placeholder="请输入工资卡开户行" />
|
||||
</el-form-item>
|
||||
@@ -231,6 +257,7 @@ import {
|
||||
} from "@/api/employee";
|
||||
import { getTenantDepartments, getDepartmentInfo } from "@/api/department";
|
||||
import { getTenantPositions, getPositionsByDepartment, getPositionInfo } from "@/api/position";
|
||||
import { getRoleByTenantId } from "@/api/role";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
|
||||
interface Employee {
|
||||
@@ -241,6 +268,8 @@ interface Employee {
|
||||
email: string;
|
||||
department: string;
|
||||
position: string;
|
||||
role: number;
|
||||
roleName?: string;
|
||||
status: number;
|
||||
createTime: string;
|
||||
tenant_id: number;
|
||||
@@ -258,6 +287,8 @@ const departmentTree = ref<any[]>([]);
|
||||
const loadingDepartments = ref(false);
|
||||
const positionList = ref<any[]>([]);
|
||||
const loadingPositions = ref(false);
|
||||
const roleList = ref<any[]>([]);
|
||||
const loadingRoles = ref(false);
|
||||
const loading = ref(false);
|
||||
|
||||
// 获取当前登录用户的租户ID
|
||||
@@ -271,7 +302,7 @@ const getCurrentTenantId = () => {
|
||||
const user = JSON.parse(userInfo);
|
||||
return user.tenant_id || user.tenantId || 0;
|
||||
} catch (e) {
|
||||
console.error('Failed to parse user info:', e);
|
||||
console.error('解析用户信息失败:', e);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@@ -311,6 +342,14 @@ const fetchEmployees = async () => {
|
||||
positionName = posInfo ? posInfo.name : '';
|
||||
}
|
||||
|
||||
// 查找角色名称
|
||||
let roleName = '';
|
||||
const roleId = item.role || null;
|
||||
if (roleId) {
|
||||
const roleInfo = roleList.value.find(r => r.roleId === roleId);
|
||||
roleName = roleInfo ? roleInfo.roleName : '';
|
||||
}
|
||||
|
||||
// 处理入职时间
|
||||
const createTime = item.create_time || item.createTime || null;
|
||||
|
||||
@@ -324,6 +363,8 @@ const fetchEmployees = async () => {
|
||||
departmentName: departmentName,
|
||||
position_id: positionId,
|
||||
positionName: positionName,
|
||||
role: roleId,
|
||||
roleName: roleName,
|
||||
bankName: item.bank_name || item.bankName || '',
|
||||
bankAccount: item.bank_account || item.bankAccount || '',
|
||||
status: item.status || 1,
|
||||
@@ -515,10 +556,36 @@ const handleDepartmentChange = (departmentId: number | null) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 获取角色列表
|
||||
const fetchRoles = async () => {
|
||||
loadingRoles.value = true;
|
||||
try {
|
||||
const tenantId = getCurrentTenantId();
|
||||
const res = await getRoleByTenantId(tenantId);
|
||||
|
||||
// 兼容接口返回的数据结构
|
||||
if (res?.data && Array.isArray(res.data)) {
|
||||
roleList.value = res.data;
|
||||
} else if (res?.data?.data && Array.isArray(res.data.data)) {
|
||||
roleList.value = res.data.data;
|
||||
} else if (Array.isArray(res)) {
|
||||
roleList.value = res;
|
||||
} else {
|
||||
roleList.value = [];
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('获取角色列表失败:', error);
|
||||
roleList.value = [];
|
||||
} finally {
|
||||
loadingRoles.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
await Promise.all([
|
||||
fetchDepartments(),
|
||||
fetchPositions(),
|
||||
fetchRoles(),
|
||||
]);
|
||||
fetchEmployees();
|
||||
});
|
||||
@@ -540,6 +607,7 @@ const form = ref<any>({
|
||||
email: "",
|
||||
department_id: null,
|
||||
position_id: null,
|
||||
role: null,
|
||||
bank_name: "",
|
||||
bank_account: "",
|
||||
status: 1,
|
||||
@@ -592,6 +660,7 @@ const handleAddEmployee = () => {
|
||||
email: "",
|
||||
department_id: null,
|
||||
position_id: null,
|
||||
role: null,
|
||||
bank_name: "",
|
||||
bank_account: "",
|
||||
status: 1,
|
||||
@@ -629,6 +698,7 @@ const handleEdit = async (employee: Employee) => {
|
||||
position_id: data.position_id ? Number(data.position_id) : null,
|
||||
bank_name: data.bank_name || data.bankName || '',
|
||||
bank_account: data.bank_account || data.bankAccount || '',
|
||||
role: data.role || null,
|
||||
status: data.status || 1,
|
||||
tenant_id: data.tenant_id || tenantId,
|
||||
};
|
||||
@@ -799,6 +869,7 @@ const submitForm = async () => {
|
||||
email: form.value.email,
|
||||
department_id: form.value.department_id || 0,
|
||||
position_id: form.value.position_id || 0,
|
||||
role: form.value.role || 0,
|
||||
bank_name: form.value.bank_name || '',
|
||||
bank_account: form.value.bank_account || '',
|
||||
status: form.value.status,
|
||||
@@ -821,22 +892,21 @@ const submitForm = async () => {
|
||||
fetchEmployees();
|
||||
} else {
|
||||
// 构建提交数据
|
||||
const tenantId = getCurrentTenantId();
|
||||
const submitData: any = {
|
||||
tenant_id: tenantId || form.value.tenant_id || 0,
|
||||
employee_no: form.value.employeeNo,
|
||||
name: form.value.name,
|
||||
phone: form.value.phone,
|
||||
email: form.value.email,
|
||||
phone: form.value.phone || '',
|
||||
email: form.value.email || '',
|
||||
department_id: form.value.department_id || 0,
|
||||
position_id: form.value.position_id || 0,
|
||||
role: form.value.role || 0,
|
||||
bank_name: form.value.bank_name || '',
|
||||
bank_account: form.value.bank_account || '',
|
||||
status: form.value.status,
|
||||
status: form.value.status || 1,
|
||||
};
|
||||
|
||||
if (form.value.tenant_id) {
|
||||
submitData.tenant_id = form.value.tenant_id;
|
||||
}
|
||||
|
||||
await addEmployee(submitData);
|
||||
ElMessage.success({
|
||||
message: "添加成功",
|
||||
|
||||
@@ -3,7 +3,7 @@ import { ref, onMounted } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
import { login } from "@/api/login";
|
||||
import { getAllMenus } from "@/api/menu";
|
||||
import { getAllMenus, getTenantMenus } from "@/api/menu";
|
||||
|
||||
const router = useRouter();
|
||||
const authStore = useAuthStore();
|
||||
@@ -77,7 +77,20 @@ const handleLogin = async () => {
|
||||
|
||||
// 登录成功后缓存菜单
|
||||
try {
|
||||
const menuRes = await getAllMenus();
|
||||
const userInfo = res.data.user || {};
|
||||
const loginType = userInfo.type; // "user" 或 "employee"
|
||||
const roleId = userInfo.role; // 角色ID
|
||||
|
||||
let menuRes;
|
||||
// 判断是租户登录(员工)还是用户登录
|
||||
if (loginType === "employee" && roleId) {
|
||||
// 员工登录,使用getTenantMenus接口,根据角色权限过滤菜单
|
||||
menuRes = await getTenantMenus(roleId);
|
||||
} else {
|
||||
// 用户登录,使用getAllMenus接口(获取所有菜单)
|
||||
menuRes = await getAllMenus();
|
||||
}
|
||||
|
||||
if (menuRes && menuRes.data && menuRes.data.length > 0) {
|
||||
localStorage.setItem('menu_cache', JSON.stringify(menuRes.data));
|
||||
}
|
||||
|
||||
@@ -232,6 +232,8 @@ import {
|
||||
changePassword,
|
||||
} from "@/api/user";
|
||||
import { getRoleByTenantId } from "@/api/role";
|
||||
import { getTenantDepartments } from "@/api/department";
|
||||
import { getTenantPositions, getPositionsByDepartment } from "@/api/position";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
|
||||
interface User {
|
||||
|
||||
Reference in New Issue
Block a user