将 Namely 平台接入 Vue 3 项目,意味着不仅要完成接口对接,还要处理数据缓存、权限标识、环境隔离等工程化问题。Namely 是面向中型企业的 HR 与福利管理 SaaS 平台,提供员工档案、福利计划、资格规则、入离职事件等 REST API。本文假定团队已经拥有 Namely 的开发者账号与客户端凭证,并采用 Vue 3 + TypeScript + Pinia + Axios 的技术栈。

一、理解 Namely 平台与 API 边界
Namely 的 API 采用 OAuth 2.0 授权码流程或客户端凭证模式,所有请求需要在 Authorization 头中携带 Bearer Token。常见的端点包括 /api/v1/profiles 获取员工列表、/api/v1/benefits/plans 获取福利计划、/api/v1/benefits/eligibility 查询资格状态。接口返回 JSON 结构,分页通过 page 和 per_page 参数控制。
在工程化之前,应当先阅读 Namely 开放文档,确认字段命名和嵌套关系。以员工对象为例,一个 profile 可能包含 first_name、last_name、email、job_title、salary 等字段。福利资格接口则可能返回 plan_id、effective_date、status 等。前端要做的不是简单请求这些端点,而是把它们抽象成稳定的领域模型,例如 Employee、BenefitPlan、EligibilityResult,避免页面组件直接依赖原始 JSON。
下面是一个最基础的 API 调用示例,使用 Axios 直接获取员工列表。后续章节会在此基础上引入拦截器和类型封装。
import axios from 'axios';
const client = axios.create({
baseURL: 'https://api.namely.com',
headers: {
'Authorization': `Bearer ${import.meta.env.VITE_NAMELY_TOKEN}`,
'Accept': 'application/json'
}
});
export async function fetchProfiles(page = 1) {
const { data } = await client.get('/api/v1/profiles', {
params: { page, per_page: 50 }
});
return data.profiles;
}
注意这里直接把 Token 放进环境变量,只适合本地开发。生产环境必须通过后端代理或安全存储获取短期 Token。
二、基于 Composition API 的分层架构设计
Vue 3 的 Composition API 让逻辑复用变得更自然。我们可以将 Namely 集成拆成三层:api 层负责 HTTP 请求、错误重试与参数规范;store 层负责全局状态、缓存有效期与数据一致性;composables 层面向组件提供响应式数据与操作方法。
api 层建议按资源拆分文件,例如 namely/profiles.ts、namely/benefits.ts。每个模块只暴露类型安全的函数,返回业务对象而不是 AxiosResponse。store 层使用 Pinia,定义 employees、benefitPlans、eligibility 三个 store,每个 store 内部维护 loading、error、lastUpdated 元数据,方便组件判断是否命中缓存。
composables 层通过 useNamelyEmployees 等函数组合 store 与 api。组件只调用 composable,不直接接触 Axios 或 Pinia 的细节。这样当 Namely 的版本升级或接口字段变化时,只需修改 api 层映射函数。
// src/composables/useNamelyEmployees.ts
import { computed, ref } from 'vue';
import { useEmployeeStore } from '@/stores/employeeStore';
import { fetchProfiles } from '@/api/namely/profiles';
export function useNamelyEmployees() {
const store = useEmployeeStore();
const loading = ref(false);
async function load(page = 1) {
loading.value = true;
try {
const employees = await fetchProfiles(page);
store.setEmployees(employees);
} finally {
loading.value = false;
}
}
const activeEmployees = computed(() =>
store.employees.filter((emp) => emp.status === 'active')
);
return { load, activeEmployees, loading };
}
这份代码中,泛型函数若要在代码块中展示,需要将 <T> 转义,不过示例中没有使用泛型。实际开发时可返回 Promise<Employee[]> 以增强类型推导。
分层架构的价值在于可测试性。团队可以用 Mock Service Worker 拦截 api 层请求,编写组件测试时无需真的连接 Namely。同时 store 层可以单独做单元测试,验证缓存过期和错误重置逻辑。
三、Token 管理与敏感数据脱敏策略
HR 数据包含社保号码、薪资、家庭住址等敏感信息。前端不应无条件拉取所有字段。首先建议后端代理 Namely 的 OAuth 令牌刷新,前端只持有短期访问令牌。若必须在前端处理刷新,可使用 Axios 响应拦截器捕获 401 状态码,然后调用刷新端点并重放原始请求。
下面的拦截器示例简化了刷新流程,重点是展示请求重放机制。需要注意的是,刷新令牌不应暴露在浏览器中,这段代码仅用于演示拦截器结构。
import axios from 'axios';
const client = axios.create({ baseURL: 'https://api.namely.com' });
let isRefreshing = false;
let pendingQueue = [];
client.interceptors.response.use(
(response) => response,
async (error) => {
const original = error.config;
if (error.response?.status === 401 && !original._retry) {
original._retry = true;
if (isRefreshing) {
return new Promise((resolve, reject) => {
pendingQueue.push({ resolve, reject });
});
}
isRefreshing = true;
try {
const refreshToken = sessionStorage.getItem('namely_refresh');
const { data } = await axios.post('https://auth.ipipp.com/refresh', { refreshToken });
sessionStorage.setItem('namely_access', data.accessToken);
pendingQueue.forEach((item) => item.resolve(client(original)));
pendingQueue = [];
return client(original);
} catch (refreshError) {
pendingQueue.forEach((item) => item.reject(refreshError));
pendingQueue = [];
throw refreshError;
} finally {
isRefreshing = false;
}
}
return Promise.reject(error);
}
);
示例中的 auth.ipipp.com 仅作为演示地址。脱敏方面,可以在组件层的计算属性中对敏感字段做掩码,例如显示薪资范围而不是精确数字。对于前端完全不使用的字段,应通过 API 查询参数或后端字段白名单进行裁剪。
四、环境配置与跨域代理实践
本地开发时,浏览器直接请求 Namely API 会遇到跨域限制。如果 Namely 未开放 CORS,可以通过 Vite 的开发服务器进行代理。在 vite.config.ts 中配置 proxy,把 /namely-api 前缀转发到真实 API 地址,并且可以重写路径。
这种做法的好处是前端代码中只写相对路径,切换环境时通过环境变量控制 API 基础地址。生产环境则由 Nginx 或后端网关承担同样的代理职责,避免将 Namely 域名直接暴露给客户端。
// vite.config.ts
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
export default defineConfig({
plugins: [vue()],
server: {
proxy: {
'/namely-api': {
target: 'https://api.namely.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/namely-api/, '')
}
}
}
});
在 .env.development 中设置 VITE_NAMELY_BASE_URL=/namely-api,在 .env.production 中设置 VITE_NAMELY_BASE_URL=https://api.namely.com。代码中 Axios 的 baseURL 统一读取该变量,从而保证环境一致性。
五、目录结构示例与联调技巧
合理的目录结构能降低 HR 模块的维护成本。建议将 Namely 相关代码集中在 src/modules/namely 下,内部再按功能拆分。例如 profiles、benefits、compliance 三个子模块,每个子模块拥有自己的 api、store、components 和 tests。
联调阶段优先使用 Namely 沙箱环境,避免误改真实员工数据。可以准备一份种子数据 JSON,当沙箱不可用时用 Mock Service Worker 模拟接口响应。针对分页和筛选参数,编写一个可复用的 usePagination composable,统一管理 page、perPage、total 与加载更多逻辑。
调试时,在浏览器 DevTools 的 Network 面板中过滤 namely-api,查看请求头中 Authorization 是否携带正确的 Bearer Token。如果响应体出现 HTML 登录页,通常意味着 Token 过期且刷新失败,需要检查 sessionStorage 中的 refresh token 是否存在。
最后需要强调,HR 与福利管理对数据准确性要求很高。前端工程化不只是引入状态管理和代码分包,还要设计清楚错误边界:当 Namely 返回 429 限流时,应退避重试并提示用户;当部分员工数据缺失时,不能阻塞整个看板渲染。通过以上分层与配置策略,Vue 3 项目可以在保持灵活性的同时,稳定支撑 HR 团队的日常操作。