145 lines
4.8 KiB
TypeScript
145 lines
4.8 KiB
TypeScript
export default defineContentScript({
|
|
matches: [
|
|
`*://${import.meta.env.WXT_PUBLIC_UCLASS_HOSTNAME}/*`,
|
|
`*://${import.meta.env.WXT_PUBLIC_INTRANET_HOSTNAME}/*`
|
|
],
|
|
|
|
world: 'MAIN',
|
|
|
|
runAt: 'document_start',
|
|
|
|
main() {
|
|
const uclassHostname = import.meta.env.WXT_PUBLIC_UCLASS_HOSTNAME;
|
|
const intranetHostname = import.meta.env.WXT_PUBLIC_INTRANET_HOSTNAME;
|
|
const intranetPort = import.meta.env.WXT_PUBLIC_INTRANET_PORT;
|
|
|
|
const isTargetPage =
|
|
window.location.hostname === uclassHostname ||
|
|
(window.location.hostname === intranetHostname && window.location.port === intranetPort)
|
|
|
|
if (!isTargetPage) {
|
|
return
|
|
}
|
|
|
|
console.log('[XHR Interceptor] 脚本已通过 WXT 成功注入。')
|
|
|
|
if (typeof XMLHttpRequest === 'undefined') {
|
|
return
|
|
}
|
|
|
|
const original_open = XMLHttpRequest.prototype.open
|
|
const original_send = XMLHttpRequest.prototype.send
|
|
|
|
interface PatchedXHR extends XMLHttpRequest {
|
|
_url?: string | URL
|
|
}
|
|
|
|
XMLHttpRequest.prototype.open = function (
|
|
this: PatchedXHR,
|
|
method: string,
|
|
url: string | URL,
|
|
...args: any[]
|
|
) {
|
|
this._url = url
|
|
return original_open.apply(this, arguments as any)
|
|
}
|
|
|
|
XMLHttpRequest.prototype.send = function (this: PatchedXHR, body) {
|
|
this.addEventListener('load', () => {
|
|
if (
|
|
this._url &&
|
|
typeof this._url === 'string' &&
|
|
this._url.includes('/lms/api//user/course/registList')
|
|
) {
|
|
try {
|
|
console.log('========== [WXT Main World] 检测到目标XHR请求 ==========')
|
|
console.log('请求URL:', this._url)
|
|
const responseData = JSON.parse(this.responseText)
|
|
console.log('响应JSON数据:', responseData)
|
|
|
|
// 检查响应数据是否是我们期望的格式
|
|
if (responseData && responseData.page && Array.isArray(responseData.page.content)) {
|
|
// 为课程对象定义一个更详细的类型
|
|
type Course = {
|
|
name: string
|
|
status: string
|
|
score: number | string
|
|
hour: number | string
|
|
cumulativeStudyCount: number
|
|
studyCount: number
|
|
[key: string]: any
|
|
}
|
|
|
|
const courses: Course[] = responseData.page.content
|
|
|
|
const cleanupAndApplyStyles = () => {
|
|
document.querySelectorAll('.chrome-ext-course-prefix').forEach((el) => el.remove())
|
|
document.querySelectorAll('[data-course-processed="true"]').forEach((el) => {
|
|
const htmlEl = el as HTMLElement
|
|
const title = htmlEl.querySelector<HTMLElement>('.title, .course-name, h3, h4') // 尝试一些常见的标题选择器
|
|
if (title) title.style.color = ''
|
|
htmlEl.removeAttribute('data-course-processed')
|
|
})
|
|
|
|
courses.forEach((course) => {
|
|
if (course && typeof course.name === 'string') {
|
|
const courseName = course.name.trim()
|
|
if (courseName === '') return
|
|
|
|
try {
|
|
const xpath = `//*[normalize-space(text())="${courseName}"]`
|
|
const titleElements: HTMLElement[] = []
|
|
const result = document.evaluate(
|
|
xpath,
|
|
document,
|
|
null,
|
|
XPathResult.ORDERED_NODE_ITERATOR_TYPE,
|
|
null,
|
|
)
|
|
let node: Node | null
|
|
while ((node = result.iterateNext())) {
|
|
if (node instanceof HTMLElement) {
|
|
titleElements.push(node)
|
|
}
|
|
}
|
|
|
|
titleElements.forEach((titleElement) => {
|
|
const parent = titleElement.parentElement
|
|
if (!parent) return
|
|
|
|
parent.dataset.courseProcessed = 'true'
|
|
|
|
const score = course.score ?? 'N/A'
|
|
const hour = course.hour ?? 'N/A'
|
|
const cumulativeStudyCount = course.cumulativeStudyCount ?? 'N/A'
|
|
const studyCount = course.studyCount ?? 'N/A'
|
|
const isVideo = cumulativeStudyCount / studyCount > 5 ? '大概率是文档' : '大概率是视频'
|
|
const prefixSpan = document.createElement('span')
|
|
prefixSpan.className = 'chrome-ext-course-prefix'
|
|
prefixSpan.textContent = `(${score}分-${hour}学时)-${isVideo}`
|
|
parent.insertBefore(prefixSpan, titleElement)
|
|
|
|
titleElement.style.color = course.status !== 'FINISH' ? 'red' : ''
|
|
})
|
|
} catch (e) {
|
|
console.error(`为课程 "${courseName}" 应用样式时出错:`, e)
|
|
}
|
|
}
|
|
})
|
|
console.log('样式和前缀已更新。')
|
|
}
|
|
setTimeout(cleanupAndApplyStyles, 1000)
|
|
} else {
|
|
console.log('[WXT Main World] 响应数据中未找到有效的课程列表 (response.page.content)')
|
|
}
|
|
console.log('======================================================')
|
|
} catch (e) {
|
|
console.error('[WXT Main World] 解析XHR响应失败:', e, '原始响应:', this.responseText)
|
|
}
|
|
}
|
|
})
|
|
return original_send.apply(this, arguments as any)
|
|
}
|
|
},
|
|
})
|