更新oa代码
This commit is contained in:
@@ -0,0 +1,609 @@
|
||||
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,
|
||||
};
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user