import { defineStore } from 'pinia'; import { ref, computed } from 'vue'; import { getTenantDepartments, getDepartmentInfo } from '@/api/department'; import { getTenantPositions, getPositionsByDepartment } from '@/api/position'; import { getRoleByTenantId } from '@/api/role'; import { getOABaseData } from '@/api/oa'; import { useAuthStore } from '@/stores/auth'; /** * OA 基础数据 Store * 统一管理部门、职位、角色等基础数据,并提供缓存机制 */ export const useOAStore = defineStore('oa', () => { // ========== 状态定义 ========== const departments = ref([]); // 部门列表(扁平结构) const departmentTree = ref([]); // 部门树结构 const positions = ref([]); // 职位列表 const roles = ref([]); // 角色列表 // 缓存时间戳(毫秒) const cacheTime = 5 * 60 * 1000; // 5分钟缓存 const departmentsCacheTime = ref(0); const positionsCacheTime = ref(0); const rolesCacheTime = ref(0); // 加载状态 const loadingDepartments = ref(false); const loadingPositions = ref(false); const loadingRoles = ref(false); // ========== 工具函数 ========== const authStore = useAuthStore(); // 获取当前租户ID const getCurrentTenantId = () => { if (authStore.user && authStore.user.tenant_id) { return authStore.user.tenant_id; } const userInfo = localStorage.getItem('userInfo'); if (userInfo) { try { const user = JSON.parse(userInfo); return user.tenant_id || user.tenantId || 0; } catch (e) { console.error('Failed to parse user info:', e); } } return 0; }; // 检查缓存是否有效 const isCacheValid = (cacheTimeValue) => { return Date.now() - cacheTimeValue < cacheTime; }; // 构建部门树 const buildDepartmentTree = (deptList, tenantId) => { if (!deptList || deptList.length === 0) { return []; } // 先按tenant_id过滤 let filteredList = deptList; if (tenantId !== undefined && tenantId !== null) { filteredList = deptList.filter((dept) => { const deptTenantId = dept.tenant_id || dept.tenantId; return deptTenantId === tenantId || deptTenantId === 0; }); } const tree = []; const map = new Map(); // 第一遍:创建所有节点的映射 filteredList.forEach((dept) => { if (!dept.id) return; map.set(dept.id, { ...dept, children: [], }); }); // 第二遍:构建树结构 filteredList.forEach((dept) => { if (!dept.id) return; const node = map.get(dept.id); if (!node) return; const parentId = dept.parent_id || 0; if (parentId === 0 || parentId === null || parentId === undefined) { tree.push(node); } else { const parent = map.get(parentId); if (parent) { parent.children.push(node); } else { tree.push(node); } } }); // 排序 const sortTree = (nodes) => { nodes.sort((a, b) => (a.sort_order || 0) - (b.sort_order || 0)); nodes.forEach((node) => { if (node.children && node.children.length > 0) { sortTree(node.children); } }); }; sortTree(tree); return tree; }; // 解析接口返回的数据 const parseResponse = (res) => { if (Array.isArray(res)) { return res; } else if (res?.data && Array.isArray(res.data)) { return res.data; } else if (res?.data?.data && Array.isArray(res.data.data)) { return res.data.data; } else if (res?.data) { return res.data; } return []; }; // ========== 部门相关方法 ========== /** * 获取部门列表(带缓存) * @param {boolean} forceRefresh 是否强制刷新,忽略缓存 * @returns {Promise} */ const fetchDepartments = async (forceRefresh = false) => { // 如果缓存有效且不强制刷新,直接返回缓存数据 if (!forceRefresh && isCacheValid(departmentsCacheTime.value) && departments.value.length > 0) { return departments.value; } // 如果正在加载,等待加载完成 if (loadingDepartments.value) { return new Promise((resolve) => { const checkInterval = setInterval(() => { if (!loadingDepartments.value) { clearInterval(checkInterval); resolve(departments.value); } }, 100); }); } loadingDepartments.value = true; try { const tenantId = getCurrentTenantId(); const res = await getTenantDepartments(tenantId); const deptList = parseResponse(res); departments.value = deptList; departmentTree.value = buildDepartmentTree(deptList, tenantId); departmentsCacheTime.value = Date.now(); return deptList; } catch (error) { console.error('获取部门列表失败:', error); // 如果出错且没有缓存数据,返回空数组 if (departments.value.length === 0) { departments.value = []; departmentTree.value = []; } throw error; } finally { loadingDepartments.value = false; } }; /** * 获取部门树(计算属性) */ const getDepartmentTree = computed(() => departmentTree.value); /** * 根据ID获取部门信息 */ const getDepartmentById = (id) => { const findInTree = (nodes) => { for (const node of nodes) { if (node.id === id) return node; if (node.children && node.children.length > 0) { const found = findInTree(node.children); if (found) return found; } } return null; }; return findInTree(departmentTree.value) || departments.value.find(d => d.id === id); }; // ========== 职位相关方法 ========== /** * 获取职位列表(带缓存) * @param {number|null} departmentId 部门ID,如果提供则只获取该部门的职位 * @param {boolean} forceRefresh 是否强制刷新 * @returns {Promise} */ const fetchPositions = async (departmentId = null, forceRefresh = false) => { // 如果指定了部门ID,先尝试从已有数据中过滤 if (departmentId) { // 如果已加载所有职位且缓存有效,直接过滤返回 // 注意:即使某个部门没有职位,只要已加载所有职位,也应该直接返回空数组 if (!forceRefresh && isCacheValid(positionsCacheTime.value)) { // 如果已加载所有职位(positions.value.length > 0 或已加载过),直接过滤 const filteredPositions = positions.value.filter((pos) => { return Number(pos.department_id) === Number(departmentId); }); // 直接返回过滤结果(即使为空数组,也说明该部门确实没有职位) return filteredPositions; } // 如果缓存中没有数据或需要刷新,请求该部门的职位 try { loadingPositions.value = true; const res = await getPositionsByDepartment(departmentId); const posList = parseResponse(res); // 将获取到的职位合并到职位列表中(去重) if (posList && posList.length > 0) { const uniquePositions = new Map(); // 先添加现有职位 positions.value.forEach((pos) => { uniquePositions.set(Number(pos.id), pos); }); // 再添加新获取的职位 posList.forEach((pos) => { const posId = Number(pos.id); if (posId && !uniquePositions.has(posId)) { uniquePositions.set(posId, pos); } }); positions.value = Array.from(uniquePositions.values()); } // 更新缓存时间(即使返回空数组也要更新,表示已查询过) positionsCacheTime.value = Date.now(); return posList || []; } catch (error) { console.error('获取职位列表失败:', error); throw error; } finally { loadingPositions.value = false; } } // 如果缓存有效且不强制刷新,直接返回缓存数据 if (!forceRefresh && isCacheValid(positionsCacheTime.value) && positions.value.length > 0) { return positions.value; } // 如果正在加载,等待加载完成 if (loadingPositions.value) { return new Promise((resolve) => { const checkInterval = setInterval(() => { if (!loadingPositions.value) { clearInterval(checkInterval); resolve(positions.value); } }, 100); }); } loadingPositions.value = true; try { const tenantId = getCurrentTenantId(); const res = await getTenantPositions(tenantId); const posList = parseResponse(res); // 去重 const uniquePositions = new Map(); posList.forEach((pos) => { const posId = Number(pos.id); if (posId && !uniquePositions.has(posId)) { uniquePositions.set(posId, pos); } }); positions.value = Array.from(uniquePositions.values()); positionsCacheTime.value = Date.now(); return positions.value; } catch (error) { console.error('获取职位列表失败:', error); if (positions.value.length === 0) { positions.value = []; } throw error; } finally { loadingPositions.value = false; } }; /** * 根据ID获取职位信息 */ const getPositionById = (id) => { return positions.value.find(p => p.id === id); }; // ========== 角色相关方法 ========== /** * 获取角色列表(带缓存) * @param {boolean} forceRefresh 是否强制刷新 * @returns {Promise} */ const fetchRoles = async (forceRefresh = false) => { // 如果缓存有效且不强制刷新,直接返回缓存数据 if (!forceRefresh && isCacheValid(rolesCacheTime.value) && roles.value.length > 0) { return roles.value; } // 如果正在加载,等待加载完成 if (loadingRoles.value) { return new Promise((resolve) => { const checkInterval = setInterval(() => { if (!loadingRoles.value) { clearInterval(checkInterval); resolve(roles.value); } }, 100); }); } loadingRoles.value = true; try { const tenantId = getCurrentTenantId(); const res = await getRoleByTenantId(tenantId); // 兼容接口返回的数据结构 if (res?.data && Array.isArray(res.data)) { roles.value = res.data; } else if (res?.data?.data && Array.isArray(res.data.data)) { roles.value = res.data.data; } else if (Array.isArray(res)) { roles.value = res; } else { roles.value = []; } rolesCacheTime.value = Date.now(); return roles.value; } catch (error) { console.error('获取角色列表失败:', error); if (roles.value.length === 0) { roles.value = []; } throw error; } finally { loadingRoles.value = false; } }; /** * 根据ID获取角色信息 */ const getRoleById = (id) => { return roles.value.find(r => r.roleId === id || r.id === id); }; // ========== 批量获取方法 ========== /** * 批量获取所有基础数据(用于页面初始化) * 使用合并接口,一次性获取所有数据,减少网络请求次数 * @param {boolean} forceRefresh 是否强制刷新 * @returns {Promise} */ const fetchAllBaseData = async (forceRefresh = false) => { // 如果缓存有效且不强制刷新,直接返回 if (!forceRefresh && isCacheValid(departmentsCacheTime.value) && isCacheValid(positionsCacheTime.value) && isCacheValid(rolesCacheTime.value) && departments.value.length > 0 && positions.value.length > 0 && roles.value.length > 0) { return; } // 如果正在加载,等待加载完成 if (loadingDepartments.value || loadingPositions.value || loadingRoles.value) { return new Promise((resolve) => { const checkInterval = setInterval(() => { if (!loadingDepartments.value && !loadingPositions.value && !loadingRoles.value) { clearInterval(checkInterval); resolve(); } }, 100); }); } // 设置加载状态 loadingDepartments.value = true; loadingPositions.value = true; loadingRoles.value = true; try { const tenantId = getCurrentTenantId(); // 调用合并接口,一次性获取所有数据 const res = await getOABaseData(tenantId); // 解析返回的数据 let baseData; if (res?.data) { if (res.data.data) { baseData = res.data.data; } else { baseData = res.data; } } else { baseData = res; } // 更新部门数据 if (baseData.departments && Array.isArray(baseData.departments)) { departments.value = baseData.departments; departmentTree.value = buildDepartmentTree(baseData.departments, tenantId); departmentsCacheTime.value = Date.now(); } // 更新职位数据 if (baseData.positions && Array.isArray(baseData.positions)) { // 去重 const uniquePositions = new Map(); baseData.positions.forEach((pos) => { const posId = Number(pos.id); if (posId && !uniquePositions.has(posId)) { uniquePositions.set(posId, pos); } }); positions.value = Array.from(uniquePositions.values()); positionsCacheTime.value = Date.now(); } // 更新角色数据 if (baseData.roles && Array.isArray(baseData.roles)) { roles.value = baseData.roles; rolesCacheTime.value = Date.now(); } } catch (error) { console.error('批量获取基础数据失败:', error); // 如果合并接口失败,回退到分别请求 console.warn('合并接口失败,回退到分别请求'); try { await Promise.all([ fetchDepartments(forceRefresh), fetchPositions(null, forceRefresh), fetchRoles(forceRefresh), ]); } catch (fallbackError) { console.error('回退请求也失败:', fallbackError); throw fallbackError; } } finally { loadingDepartments.value = false; loadingPositions.value = false; loadingRoles.value = false; } }; // ========== 缓存管理方法 ========== /** * 清除所有缓存 */ const clearCache = () => { departmentsCacheTime.value = 0; positionsCacheTime.value = 0; rolesCacheTime.value = 0; }; /** * 清除部门缓存 */ const clearDepartmentsCache = () => { departmentsCacheTime.value = 0; }; /** * 清除职位缓存 */ const clearPositionsCache = () => { positionsCacheTime.value = 0; }; /** * 清除角色缓存 */ const clearRolesCache = () => { rolesCacheTime.value = 0; }; /** * 临时添加部门到列表(用于显示不在当前租户列表中的部门) */ const addTemporaryDepartment = (deptData) => { if (!deptData || !deptData.id) return; // 检查是否已存在 const exists = departments.value.some((dept) => dept.id === deptData.id); if (!exists) { departments.value.push(deptData); // 重新构建部门树 const tenantId = getCurrentTenantId(); departmentTree.value = buildDepartmentTree(departments.value, tenantId); } }; /** * 临时添加职位到列表(用于显示不在当前租户列表中的职位) */ const addTemporaryPosition = (posData) => { if (!posData || !posData.id) return; // 检查是否已存在 const exists = positions.value.some((pos) => pos.id === posData.id); if (!exists) { positions.value.push(posData); } }; /** * 临时添加角色到列表(用于显示不在当前租户列表中的角色) */ const addTemporaryRole = (roleData) => { if (!roleData) return; // 获取角色ID(兼容不同的字段名) const roleId = roleData.roleId || roleData.id || roleData.role_id; if (!roleId) return; // 检查是否已存在 const exists = roles.value.some((role) => { const rId = role.roleId || role.id || role.role_id; return Number(rId) === Number(roleId); }); if (!exists) { roles.value.push(roleData); } }; /** * 刷新指定数据(清除缓存并重新获取) */ const refreshDepartments = () => fetchDepartments(true); const refreshPositions = () => fetchPositions(null, true); const refreshRoles = () => fetchRoles(true); const refreshAll = () => fetchAllBaseData(true); return { // 状态 departments, departmentTree, positions, roles, loadingDepartments, loadingPositions, loadingRoles, // 计算属性 getDepartmentTree, // 方法 fetchDepartments, fetchPositions, fetchRoles, fetchAllBaseData, getDepartmentById, getPositionById, getRoleById, // 缓存管理 clearCache, clearDepartmentsCache, clearPositionsCache, clearRolesCache, refreshDepartments, refreshPositions, refreshRoles, refreshAll, // 临时数据添加 addTemporaryDepartment, addTemporaryPosition, addTemporaryRole, }; });