Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
- 修复领域数据自动更新中“天气过期”判定读取错误字段,天气有效期结束后会在每分钟检查时触发刷新。
- 合同高价规则入口改名为“二次确认设置”;入库页面没有合同时也显示设置按钮。
- 二次确认设置默认全局规则为 `-0`,未自定义时合同价格高于 MP 即触发二次确认。
- 现任高管详情面板改为插入到培训课程进度汇总块之后,定位不再依赖固定 CSS 类名和游戏文案。
- 修复切换现任高管后面板仍显示上一位高管数据的问题;切换后同步更新并重新插入到当前高管的培训汇总块之后。

## [1.33.2] - 2026-08-11

Expand Down
103 changes: 99 additions & 4 deletions src/features/executiveTrainingModule.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { registerExportInfo } from '../core/exportInfo.js';

const ExecutiveTrainingModule = (function () {
let panelRelocateTimer = null;
let currentPanelRenderTimer = null;

const OFFERS_URL = "/api/v2/companies/executives/my-offers/";
const NOTIFICATIONS_KEYWORD = "/game-notifications/";
Expand Down Expand Up @@ -64,6 +65,74 @@ import { registerExportInfo } from '../core/exportInfo.js';
const getCurrentExecPanelContainer = () =>
document.querySelector('#page .row > .col-lg-6') || null;

const getReactFiber = (el) => {
const key = Object.keys(el).find(k => k.startsWith('__reactFiber$') || k.startsWith('__reactInternalInstance$'));
return key ? el[key] : null;
};

const getHostNodeFromFiber = (fiber) => {
const queue = [fiber];
while (queue.length) {
const node = queue.pop();
if (!node) continue;
if (node.stateNode instanceof Element) return node.stateNode;
if (node.sibling) queue.push(node.sibling);
if (node.child) queue.push(node.child);
}
return null;
};

// 定位培训课程进度汇总块,不依赖 CSS 类名和游戏文案。
const getCurrentExecTrainingSummaryNode = (execId) => {
const page = document.getElementById('page');
if (!page || execId == null) return null;

const findKeyedTop = (rootFiber) => {
const queue = [rootFiber];
while (queue.length) {
const node = queue.pop();
if (!node) continue;
if (node.key === 'top') {
const host = getHostNodeFromFiber(node);
const hasProgress = !!(host && host.querySelector('[role="progressbar"]'));
if (hasProgress) {
return host;
}
}
if (node.sibling) queue.push(node.sibling);
if (node.child) queue.push(node.child);
}
return null;
};

for (const el of page.querySelectorAll('div')) {
const fiber = getReactFiber(el);
if (!fiber) continue;

let node = fiber;
while (node) {
const props = node.memoizedProps;
if (props && props.executive && String(props.executive.id) === String(execId)) {
const found = findKeyedTop(node);
if (found) return found;
break;
}
node = node.return;
}
}

const pageFound = findKeyedTop(getReactFiber(page));
return pageFound;
};

const clearCurrentPanelRenderTimer = () => {
if (currentPanelRenderTimer) {
clearInterval(currentPanelRenderTimer);
currentPanelRenderTimer = null;
}
};
window.addEventListener('pagehide', clearCurrentPanelRenderTimer, { once: true });

const ensureAgencyPanelRelocated = () => {
const panel = document.getElementById('sc-plugin-panel');
if (!panel) {
Expand All @@ -88,6 +157,7 @@ import { registerExportInfo } from '../core/exportInfo.js';
clearInterval(panelRelocateTimer);
panelRelocateTimer = null;
}
clearCurrentPanelRenderTimer();
}, { once: true });
};

Expand Down Expand Up @@ -124,10 +194,12 @@ import { registerExportInfo } from '../core/exportInfo.js';
function renderSkillPanel(data, isError = false, mode = 'agency') {
const targetContainer = mode === 'current' ? getCurrentExecPanelContainer() : getValidTargetContainer();
const panelId = mode === 'current' ? 'sc-current-exec-panel' : 'sc-plugin-panel';
if (!targetContainer || document.getElementById(panelId)) return;
const existingPanel = document.getElementById(panelId);
if (!targetContainer || (mode !== 'current' && existingPanel)) return;
if (mode === 'current') clearCurrentPanelRenderTimer();

const d14 = DM();
const panel = document.createElement('div');
const panel = existingPanel || document.createElement('div');
panel.id = panelId;
const baseStyle = `margin-top: 12px; padding: 12px; border-radius: 4px; font-family: sans-serif; font-size: 14px; background-color: ${d14 ? '#2c2c2c' : '#f2f2f2'}; border: 1px solid ${d14 ? '#555' : '#d1d1d1'}; color: ${d14 ? '#efefef' : '#333'};${mode === 'current' ? ' width:100%; box-sizing:border-box;' : ''}`;

Expand Down Expand Up @@ -273,7 +345,24 @@ import { registerExportInfo } from '../core/exportInfo.js';
panel.style = baseStyle;
panel.innerHTML = contentHtml;
if (mode === 'current') {
targetContainer.appendChild(panel);
panel.dataset.scExecId = data?.id != null ? String(data.id) : '';
const anchor = getCurrentExecTrainingSummaryNode(data?.id);
if (anchor) {
anchor.after(panel);
} else {
const startedAt = Date.now();
clearCurrentPanelRenderTimer();
currentPanelRenderTimer = setInterval(() => {
const retryAnchor = getCurrentExecTrainingSummaryNode(data?.id);
if (retryAnchor) {
clearCurrentPanelRenderTimer();
retryAnchor.after(panel);
} else if (Date.now() - startedAt > 5000) {
clearCurrentPanelRenderTimer();
if (targetContainer) targetContainer.appendChild(panel);
}
}, 200);
}
} else {
targetContainer.after(panel);
startAgencyPanelRelocateWatch();
Expand All @@ -297,7 +386,13 @@ import { registerExportInfo } from '../core/exportInfo.js';
} else {
const current = getCurrentExecRecord();
const execMatch = url.match(EXEC_API_REGEX);
if (current && execMatch && Number(execMatch[1]) === current.id && getCurrentExecPanelContainer()) {
const currentSlot = getCurrentExecSlot();
const detailPosition = d?.currentWorkHistory?.position;
const matchesSlot = currentSlot != null && (
detailPosition === currentSlot ||
(current && execMatch && Number(execMatch[1]) === Number(current.id))
);
if (execMatch && matchesSlot && getCurrentExecPanelContainer()) {
if (isExecutiveHistoryEnabled()) renderSkillPanel(d, false, 'current');
}
}
Expand Down
Loading