@@ -4,6 +4,7 @@ import { registerExportInfo } from '../core/exportInfo.js';
44
55 const ExecutiveTrainingModule = ( function ( ) {
66 let panelRelocateTimer = null ;
7+ let currentPanelRenderTimer = null ;
78
89 const OFFERS_URL = "/api/v2/companies/executives/my-offers/" ;
910 const NOTIFICATIONS_KEYWORD = "/game-notifications/" ;
@@ -64,6 +65,74 @@ import { registerExportInfo } from '../core/exportInfo.js';
6465 const getCurrentExecPanelContainer = ( ) =>
6566 document . querySelector ( '#page .row > .col-lg-6' ) || null ;
6667
68+ const getReactFiber = ( el ) => {
69+ const key = Object . keys ( el ) . find ( k => k . startsWith ( '__reactFiber$' ) || k . startsWith ( '__reactInternalInstance$' ) ) ;
70+ return key ? el [ key ] : null ;
71+ } ;
72+
73+ const getHostNodeFromFiber = ( fiber ) => {
74+ const queue = [ fiber ] ;
75+ while ( queue . length ) {
76+ const node = queue . pop ( ) ;
77+ if ( ! node ) continue ;
78+ if ( node . stateNode instanceof Element ) return node . stateNode ;
79+ if ( node . sibling ) queue . push ( node . sibling ) ;
80+ if ( node . child ) queue . push ( node . child ) ;
81+ }
82+ return null ;
83+ } ;
84+
85+ // 定位培训课程进度汇总块,不依赖 CSS 类名和游戏文案。
86+ const getCurrentExecTrainingSummaryNode = ( execId ) => {
87+ const page = document . getElementById ( 'page' ) ;
88+ if ( ! page || execId == null ) return null ;
89+
90+ const findKeyedTop = ( rootFiber ) => {
91+ const queue = [ rootFiber ] ;
92+ while ( queue . length ) {
93+ const node = queue . pop ( ) ;
94+ if ( ! node ) continue ;
95+ if ( node . key === 'top' ) {
96+ const host = getHostNodeFromFiber ( node ) ;
97+ const hasProgress = ! ! ( host && host . querySelector ( '[role="progressbar"]' ) ) ;
98+ if ( hasProgress ) {
99+ return host ;
100+ }
101+ }
102+ if ( node . sibling ) queue . push ( node . sibling ) ;
103+ if ( node . child ) queue . push ( node . child ) ;
104+ }
105+ return null ;
106+ } ;
107+
108+ for ( const el of page . querySelectorAll ( 'div' ) ) {
109+ const fiber = getReactFiber ( el ) ;
110+ if ( ! fiber ) continue ;
111+
112+ let node = fiber ;
113+ while ( node ) {
114+ const props = node . memoizedProps ;
115+ if ( props && props . executive && String ( props . executive . id ) === String ( execId ) ) {
116+ const found = findKeyedTop ( node ) ;
117+ if ( found ) return found ;
118+ break ;
119+ }
120+ node = node . return ;
121+ }
122+ }
123+
124+ const pageFound = findKeyedTop ( getReactFiber ( page ) ) ;
125+ return pageFound ;
126+ } ;
127+
128+ const clearCurrentPanelRenderTimer = ( ) => {
129+ if ( currentPanelRenderTimer ) {
130+ clearInterval ( currentPanelRenderTimer ) ;
131+ currentPanelRenderTimer = null ;
132+ }
133+ } ;
134+ window . addEventListener ( 'pagehide' , clearCurrentPanelRenderTimer , { once : true } ) ;
135+
67136 const ensureAgencyPanelRelocated = ( ) => {
68137 const panel = document . getElementById ( 'sc-plugin-panel' ) ;
69138 if ( ! panel ) {
@@ -88,6 +157,7 @@ import { registerExportInfo } from '../core/exportInfo.js';
88157 clearInterval ( panelRelocateTimer ) ;
89158 panelRelocateTimer = null ;
90159 }
160+ clearCurrentPanelRenderTimer ( ) ;
91161 } , { once : true } ) ;
92162 } ;
93163
@@ -124,10 +194,12 @@ import { registerExportInfo } from '../core/exportInfo.js';
124194 function renderSkillPanel ( data , isError = false , mode = 'agency' ) {
125195 const targetContainer = mode === 'current' ? getCurrentExecPanelContainer ( ) : getValidTargetContainer ( ) ;
126196 const panelId = mode === 'current' ? 'sc-current-exec-panel' : 'sc-plugin-panel' ;
127- if ( ! targetContainer || document . getElementById ( panelId ) ) return ;
197+ const existingPanel = document . getElementById ( panelId ) ;
198+ if ( ! targetContainer || ( mode !== 'current' && existingPanel ) ) return ;
199+ if ( mode === 'current' ) clearCurrentPanelRenderTimer ( ) ;
128200
129201 const d14 = DM ( ) ;
130- const panel = document . createElement ( 'div' ) ;
202+ const panel = existingPanel || document . createElement ( 'div' ) ;
131203 panel . id = panelId ;
132204 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;' : '' } ` ;
133205
@@ -273,7 +345,24 @@ import { registerExportInfo } from '../core/exportInfo.js';
273345 panel . style = baseStyle ;
274346 panel . innerHTML = contentHtml ;
275347 if ( mode === 'current' ) {
276- targetContainer . appendChild ( panel ) ;
348+ panel . dataset . scExecId = data ?. id != null ? String ( data . id ) : '' ;
349+ const anchor = getCurrentExecTrainingSummaryNode ( data ?. id ) ;
350+ if ( anchor ) {
351+ anchor . after ( panel ) ;
352+ } else {
353+ const startedAt = Date . now ( ) ;
354+ clearCurrentPanelRenderTimer ( ) ;
355+ currentPanelRenderTimer = setInterval ( ( ) => {
356+ const retryAnchor = getCurrentExecTrainingSummaryNode ( data ?. id ) ;
357+ if ( retryAnchor ) {
358+ clearCurrentPanelRenderTimer ( ) ;
359+ retryAnchor . after ( panel ) ;
360+ } else if ( Date . now ( ) - startedAt > 5000 ) {
361+ clearCurrentPanelRenderTimer ( ) ;
362+ if ( targetContainer ) targetContainer . appendChild ( panel ) ;
363+ }
364+ } , 200 ) ;
365+ }
277366 } else {
278367 targetContainer . after ( panel ) ;
279368 startAgencyPanelRelocateWatch ( ) ;
@@ -297,7 +386,14 @@ import { registerExportInfo } from '../core/exportInfo.js';
297386 } else {
298387 const current = getCurrentExecRecord ( ) ;
299388 const execMatch = url . match ( EXEC_API_REGEX ) ;
300- if ( current && execMatch && Number ( execMatch [ 1 ] ) === current . id && getCurrentExecPanelContainer ( ) ) {
389+ const currentSlot = getCurrentExecSlot ( ) ;
390+ const detailPosition = d ?. currentWorkHistory ?. position ;
391+ const detailSlot = detailPosition != null ? SLOT_MAP [ detailPosition ] : null ;
392+ const matchesSlot = currentSlot != null && (
393+ detailSlot === currentSlot ||
394+ ( current && execMatch && Number ( execMatch [ 1 ] ) === Number ( current . id ) )
395+ ) ;
396+ if ( execMatch && matchesSlot && getCurrentExecPanelContainer ( ) ) {
301397 if ( isExecutiveHistoryEnabled ( ) ) renderSkillPanel ( d , false , 'current' ) ;
302398 }
303399 }
0 commit comments