使用FullCalendar加载Outlook日历失败,登录后无渲染问题排查
问题诊断与修复方案
核心问题
- 未处理MSAL登录重定向回调:使用
loginRedirect完成登录后,页面重定向回网站时,MSAL需要处理回调来保存账户信息,但当前代码缺失这一步,导致msalInstance.getAllAccounts()始终返回空数组,重复触发登录流程后抛出interaction_in_progress错误。 - 登录成功后未自动触发日历渲染:重定向回调处理完成后,没有自动调用日历加载逻辑,导致登录返回后日历无生成动作。
- 函数作用域冲突:
getCalendarEvents、renderCalendar等核心函数定义在DOMContentLoaded回调内部,全局函数mostrarCalendarioOutlook无法访问这些函数,即使登录成功也无法执行后续流程。
具体修复步骤
1. 添加MSAL登录回调处理
初始化msalInstance后,立即处理登录重定向回调,确保账户信息正确保存并自动触发日历加载:
document.addEventListener('DOMContentLoaded', async function() { try { const msalConfig = { auth: { clientId: "Client_ID", authority: "https://login.microsoftonline.com/common", redirectUri: "redirect_Uri", popUp: true } }; msalInstance = new msal.PublicClientApplication(msalConfig); // 处理登录重定向回调 await msalInstance.handleRedirectPromise() .then(response => { if (response) { // 登录成功后自动加载日历 mostrarCalendarioOutlook(); } }) .catch(err => { console.error('回调处理失败:', err); }); // ... 其他函数定义 } catch (error) { console.error('初始化错误:', error); Swal.fire({ icon: 'error', title: '初始化失败', text: '加载配置时出现错误,请稍后重试。' }); } });
2. 调整函数作用域
将getCalendarEvents、renderCalendar等核心函数移到全局作用域,确保mostrarCalendarioOutlook能正常调用:
let msalInstance; const loginRequest = { scopes: ["User.Read", "Calendars.Read", "Calendars.Read.Shared"] }; // 全局定义核心函数 async function getCalendarEvents(accessToken) { console.log('进入getCalendarEvents'); const response = await fetch('https://graph.microsoft.com/v1.0/me/events', { headers: { 'Authorization': `Bearer ${accessToken}` } }); if (!response.ok) { const errorText = await response.text(); console.error(`获取日历事件失败: ${response.status} ${response.statusText}`, errorText); throw new Error('获取日历事件失败'); } const data = await response.json(); const events = []; function adjustToLocalTime(dateTime) { const date = new Date(dateTime); const timeZoneOffset = date.getTimezoneOffset() * 60000; return new Date(date.getTime() - timeZoneOffset).toISOString(); } const recurringEvents = data.value.filter(event => event.recurrence && event.recurrence.pattern); recurringEvents.forEach(event => { // 原循环逻辑不变 }); const singleEvents = data.value.filter(event => !event.recurrence); singleEvents.forEach(event => { events.push({ title: event.subject, start: adjustToLocalTime(event.start.dateTime), end: adjustToLocalTime(event.end.dateTime), allDay: event.isAllDay }); }); return events; } function renderCalendar(events) { console.log('进入renderCalendar'); const calendarEl = document.getElementById('calendar'); const calendar = new FullCalendar.Calendar(calendarEl, { // 原日历配置不变 }); calendar.render(); } async function mostrarCalendarioOutlook() { console.log('进入mostrarCalendarioOutlook'); try { if (!msalInstance) { throw new Error("msalInstance未初始化"); } const accounts = msalInstance.getAllAccounts(); if (accounts.length > 0) { console.log('检测到已登录账户'); const account = accounts[0]; const tokenResponse = await msalInstance.acquireTokenSilent({ account, scopes: ["User.Read", "Calendars.Read", "Calendars.Read.Shared"] }); const events = await getCalendarEvents(tokenResponse.accessToken); console.log('准备渲染日历'); renderCalendar(events); } else { console.log('未检测到登录账户'); // 检查是否有正在进行的交互,避免重复触发登录 if (!msalInstance.isInteractionInProgress()) { await msalInstance.loginRedirect(loginRequest); } } } catch (error) { handleError(error); } } // ... 其他全局函数(如handleError) document.addEventListener('DOMContentLoaded', async function() { try { const msalConfig = { auth: { clientId: "Client_ID", authority: "https://login.microsoftonline.com/common", redirectUri: "redirect_Uri", popUp: true } }; msalInstance = new msal.PublicClientApplication(msalConfig); // 处理登录回调 await msalInstance.handleRedirectPromise() .then(response => { if (response) { mostrarCalendarioOutlook(); } }) .catch(err => { console.error('回调处理失败:', err); }); // 保留仅在初始化时需要的函数 async function handleLogoutClick() { // 原注销逻辑不变 } } catch (error) { console.error('初始化错误:', error); Swal.fire({ icon: 'error', title: '初始化失败', text: '加载配置时出现错误,请稍后重试。' }); } });
3. 避免重复触发登录冲突
在mostrarCalendarioOutlook的else分支中,先检查是否有正在进行的交互,再执行登录跳转:
async function mostrarCalendarioOutlook() { console.log('进入mostrarCalendarioOutlook'); try { if (!msalInstance) { throw new Error("msalInstance未初始化"); } const accounts = msalInstance.getAllAccounts(); if (accounts.length > 0) { // 原登录后逻辑不变 } else { console.log('未检测到登录账户'); // 检查是否有交互在进行,防止重复触发登录 if (!msalInstance.isInteractionInProgress()) { await msalInstance.loginRedirect(loginRequest); } } } catch (error) { handleError(error); } }
额外验证点
- 确认Azure AD应用的
redirectUri与代码配置一致,且已添加到应用的重定向URI列表中。 - 检查应用是否已获得
Calendars.Read和Calendars.Read.Shared权限(管理员或用户已完成授权)。
内容的提问来源于stack exchange,提问作者Hugo Jiménez
相关产品推荐
相关产品推荐

