From 140f40c168e99a61e2306f0b90f6c3870ddff2a7 Mon Sep 17 00:00:00 2001 From: ApplePine Date: Sun, 20 Jul 2025 11:41:29 +0800 Subject: [PATCH] =?UTF-8?q?initial:=20=E5=88=9D=E5=A7=8B=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在系统课程列表界面读取发送请求,获取课程信息 - 在课程列表界面为课程卡片补充提示信息(包含是否已经学习过、学时、学分、课程类型等) --- .env.example | 3 + .gitignore | 32 ++++++++ README.md | 124 ++++++++++++++++++++++++++++++ assets/vue.svg | 1 + entrypoints/background.ts | 3 + entrypoints/content.ts | 144 +++++++++++++++++++++++++++++++++++ entrypoints/popup/App.vue | 10 +++ entrypoints/popup/index.html | 13 ++++ entrypoints/popup/main.ts | 5 ++ entrypoints/popup/style.css | 80 +++++++++++++++++++ package.json | 25 ++++++ public/icon/128.png | Bin 0 -> 3074 bytes public/icon/16.png | Bin 0 -> 559 bytes public/icon/32.png | Bin 0 -> 916 bytes public/icon/48.png | Bin 0 -> 1334 bytes public/icon/96.png | Bin 0 -> 2366 bytes public/wxt.svg | 15 ++++ tsconfig.json | 3 + wxt.config.ts | 6 ++ 19 files changed, 464 insertions(+) create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 README.md create mode 100644 assets/vue.svg create mode 100644 entrypoints/background.ts create mode 100644 entrypoints/content.ts create mode 100644 entrypoints/popup/App.vue create mode 100644 entrypoints/popup/index.html create mode 100644 entrypoints/popup/main.ts create mode 100644 entrypoints/popup/style.css create mode 100644 package.json create mode 100644 public/icon/128.png create mode 100644 public/icon/16.png create mode 100644 public/icon/32.png create mode 100644 public/icon/48.png create mode 100644 public/icon/96.png create mode 100644 public/wxt.svg create mode 100644 tsconfig.json create mode 100644 wxt.config.ts diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..9fe4f60 --- /dev/null +++ b/.env.example @@ -0,0 +1,3 @@ +WXT_PUBLIC_UCLASS_HOSTNAME="xxx.xxxx.cn" +WXT_PUBLIC_INTRANET_HOSTNAME="192.168.1.7" +WXT_PUBLIC_INTRANET_PORT="1234" \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b1e2d61 --- /dev/null +++ b/.gitignore @@ -0,0 +1,32 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +.output +stats.html +stats-*.json +.wxt +web-ext.config.ts + +# Editor directories and files +.vscode/ +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +# lock file +bun.lock + +# env +.env \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..a218c58 --- /dev/null +++ b/README.md @@ -0,0 +1,124 @@ +# 金蝶课程系统助手 + +一个专为企业金蝶课程系统设计的浏览器扩展插件,能够为课程视频卡片添加智能提示信息,帮助用户更好地了解课程学习状态。 + +## ✨ 功能特性 + +- 🎯 **智能课程识别**:自动识别页面中的课程列表 +- 📊 **学习状态显示**:显示课程的学习状态(已完成/未完成) +- ⏱️ **学时学分信息**:显示课程的学分和学时信息 +- 🎬 **内容类型识别**:智能判断课程是视频还是文档类型 +- 🎨 **视觉提示**:未完成课程以红色标识,已完成课程保持原色 +- 🔄 **实时更新**:页面刷新后自动重新应用样式和信息 + +## 🚀 安装方法 + +### 方法一:从源码构建(推荐) + +1. 克隆项目到本地: +```bash +git clone https://github.com/App1ePine/kingdeeCourseBot.git +cd kingdeeCourseBot +``` + +2. 安装依赖: +```bash +npm install +``` + +3. 配置环境变量: + - 将 `.env.example` 文件复制并重命名为 `.env` + - 根据您的实际环境修改 `.env` 文件中的配置值 + +4. 构建扩展: +```bash +npm run build +``` + +5. 在浏览器中加载扩展: + - **Chrome/Edge**: 打开 `chrome://extensions/`,开启"开发者模式",点击"加载已解压的扩展程序",选择 `dist` 文件夹 + - **Firefox**: 打开 `about:debugging#/runtime/this-firefox`,点击"加载临时附加组件",选择 `dist` 文件夹中的 `manifest.json` + +### 方法二:开发模式 + +```bash +npm run dev +``` + +## 📖 使用说明 + +1. 安装扩展后,访问金蝶课程系统 +2. 插件会自动检测课程列表页面 +3. 每个课程卡片前会显示格式为 `(学分-学时)-内容类型` 的提示信息 +4. 未完成的课程标题会以红色显示 +5. 已完成的课程保持原色 + +## 🛠️ 技术栈 + +- **框架**: [WXT](https://wxt.dev/) - 现代化的浏览器扩展开发框架 +- **前端**: Vue 3 + JavaScript +- **构建工具**: Vite +- **语言**: JavaScript + +## 🔧 开发 + +### 环境要求 + +- Node.js 16+ +- npm 或 yarn + +### 开发命令 + +```bash +# 开发模式(Chrome) +npm run dev + +# 开发模式(Firefox) +npm run dev:firefox + +# 构建生产版本 +npm run build + +# 构建 Firefox 版本 +npm run build:firefox + +# 打包扩展 +npm run zip + +# 类型检查 +npm run compile +``` + +### 项目结构 + +``` +kingdeeCourseBot/ +├── entrypoints/ +│ ├── background.ts # 后台脚本 +│ ├── content.ts # 内容脚本 +│ └── popup/ # 弹出窗口 +├── components/ # Vue 组件 +├── assets/ # 静态资源 +├── public/ # 公共资源 +└── wxt.config.ts # WXT 配置文件 +``` + +## ⚙️ 配置 + +插件通过环境变量配置目标网站。项目根目录包含 `.env.example` 文件,您需要: + +1. 将 `.env.example` 文件复制并重命名为 `.env` +2. 在 `.env` 文件中配置以下环境变量: + +```bash +# 金蝶课程系统主机名 +WXT_PUBLIC_UCLASS_HOSTNAME=your-uclass-hostname.com + +# 内网主机名 +WXT_PUBLIC_INTRANET_HOSTNAME=your-intranet-hostname.com + +# 内网端口 +WXT_PUBLIC_INTRANET_PORT=8080 +``` + +根据实际情况使用公网域名 或内网IP,请根据您的实际环境修改这些配置值。 diff --git a/assets/vue.svg b/assets/vue.svg new file mode 100644 index 0000000..ca8129c --- /dev/null +++ b/assets/vue.svg @@ -0,0 +1 @@ + diff --git a/entrypoints/background.ts b/entrypoints/background.ts new file mode 100644 index 0000000..45e9a33 --- /dev/null +++ b/entrypoints/background.ts @@ -0,0 +1,3 @@ +export default defineBackground(() => { + console.log('kingdeeCourseAutoPlayer 已启动', { id: browser.runtime.id }); +}) diff --git a/entrypoints/content.ts b/entrypoints/content.ts new file mode 100644 index 0000000..7293db5 --- /dev/null +++ b/entrypoints/content.ts @@ -0,0 +1,144 @@ +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('.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) + } + }, +}) diff --git a/entrypoints/popup/App.vue b/entrypoints/popup/App.vue new file mode 100644 index 0000000..d6b279d --- /dev/null +++ b/entrypoints/popup/App.vue @@ -0,0 +1,10 @@ + + + + + diff --git a/entrypoints/popup/index.html b/entrypoints/popup/index.html new file mode 100644 index 0000000..5a2184e --- /dev/null +++ b/entrypoints/popup/index.html @@ -0,0 +1,13 @@ + + + + + + Default Popup Title + + + +
+ + + diff --git a/entrypoints/popup/main.ts b/entrypoints/popup/main.ts new file mode 100644 index 0000000..f8c23e4 --- /dev/null +++ b/entrypoints/popup/main.ts @@ -0,0 +1,5 @@ +import { createApp } from 'vue'; +import './style.css'; +import App from './App.vue'; + +createApp(App).mount('#app'); diff --git a/entrypoints/popup/style.css b/entrypoints/popup/style.css new file mode 100644 index 0000000..7294765 --- /dev/null +++ b/entrypoints/popup/style.css @@ -0,0 +1,80 @@ +:root { + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} +a:hover { + color: #535bf2; +} + +body { + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; +} +button:hover { + border-color: #646cff; +} +button:focus, +button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; +} + +.card { + padding: 2em; +} + +#app { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #747bff; + } + button { + background-color: #f9f9f9; + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..1c22c68 --- /dev/null +++ b/package.json @@ -0,0 +1,25 @@ +{ + "name": "kingdee-course-bot", + "description": "kingdee course bot", + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "wxt", + "dev:firefox": "wxt -b firefox", + "build": "wxt build", + "build:firefox": "wxt build -b firefox", + "zip": "wxt zip", + "zip:firefox": "wxt zip -b firefox", + "compile": "vue-tsc --noEmit", + "postinstall": "wxt prepare" + }, + "dependencies": { + "vue": "^3.5.13" + }, + "devDependencies": { + "@wxt-dev/module-vue": "^1.0.2", + "typescript": "5.6.3", + "vue-tsc": "^2.2.10", + "wxt": "^0.20.6" + } +} diff --git a/public/icon/128.png b/public/icon/128.png new file mode 100644 index 0000000000000000000000000000000000000000..9e35d130796978714468fa149241172744bf3992 GIT binary patch literal 3074 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 Q9RL6T07*qoM6N<$g0^9zvj6}9 literal 0 HcmV?d00001 diff --git a/public/icon/16.png b/public/icon/16.png new file mode 100644 index 0000000000000000000000000000000000000000..cd09f8cfbc020b5a077c6c491e2be5f1cb75fb50 GIT binary patch literal 559 zcmV+~0?_@5P)J*;soIYk(IETZ~(bMj$o8vnQ?Z`K&P@byKnRdgL9BcAPx)vHta#e(k_EN+QtB~a;#B7vY4-MPwd z0I*M%0Ii$vMXQ0J*IDiEx*HeHO#2~|QUWDPMwmjovlOSF45Is5Q{nqwq^gh#fRPv6 zS}@*-M+8d*2rWit4ITS_^ytbtKuJ{`srD|`y4ycdB$KV&w?aSmH!}7OlPlMt4AEQp zhZ%K%Z3q@VTdrAraF5Y9z3|}q+%#iwp}*e_69qRYzn6K?6mgj zj?N1`|7Fpj-nU)JS}?UmCFPhx`KK{AMERWeC5F}p!vOUOl x{o{41KG|W@*}t!GLho_Yiov7vl{n4;-T?Ii%lIRLgKym_bV-2v#fCmVkAh`iih%Om#5EqsU zEB$;ulbPxHNiqxxepRVTcfam_)BWD}zDCH1!#lJ1=QkYzLKTQg1TaFtD>Sfey|Nw1 z3Cv>jc4%%Q$sQXQ)5Zy*b+&%lh8&pdK=X%LqOt7p-_p#5NNihWyASt-m}fv^KK5X9 zbks(t8*DKHMv44r@nJitenlQlcBQ64mJYAwD3KR8Dmyf5Q6f}^(*cw^x1qBd8s%9kw)PqOF+ zc-#S zwQjUnP-RT=n$z;oa{$o^ej$o%)Kw)I9tuf6Q`Tn+#^dIVS&7sFkK3u|69@r)NF$HD zn5?fe?w80Fs5B&yA2ly4ut2Ew4a9w|>vbXq1Q}Wd!xyk%#42Pl;Q%w^j@J4G3&z1W zvl_P0Dd_YlpU67vy~AJ_#T7gd5&C5&WPNcQtPjvH(26c7^r`6R;v+X!sKdcrNe*yU zu%IoZ&tmCi;5IA?s^k;m?l}i#BnCL@`+N#*F3DTVC`^hA)cVg?aqWB(jFA(tHZEp< zwF)31XomugC@(-5{jAQilHIq;7k$02kY(b8Kv-7EiXJ9J%oAhjFm3N<5ujX#EH9TW9YBdNqdt0000<{22)0j@ZAli#Ip&84}hAsEPnZ0vICT zIil>g)=nMB5eRp{-n;rUl5nIZozTq{LL5B**?A2)AhQ9D%laV^98KF_fMq|gbh*W_VK z1jmlNdOFzMnWnR6z&J3c=k!HD!pQmK(ypfUD~#Df9-3~ld-VB_oA?C8H;_0Hc_u)( zkPLQNszn=p+I^HX5MyYO{Sc@2!+9Vp?Ft~F`P!l-lx{XHv`YA9z_Otk!1`zN$>8z6qBU3z6!!)@Cylpu z&2$r4dr z!Ph&1n@}}yZ|UZmlwLZ3yn({y_xC54U@7EOHs zmS9Ox%wtj5-FXeFu?9FB4(G0#0YAVJ!7g(VX(}eN5NByC!#oOv_#o0Wj5RxTHdK#0}VOB}LiJ$VTSLE0$n_c4LN3lA@FuMfFbX^Typ_M);{*DcI)i6`WkWsC0j9uXW3O zVuOZ!LPg1b(kgf{rMV>i4+)t4j0{Pu7zjk3{An}TrkIRbNlQWFk|JO4^rG#EB2j-C z$|~4rDvhoAGr1@OO2Pi^N;kHK|71{qA96S36HTf1nb0gBaFKb-q<8q)Wk`U5Mv3!J zsKJ!Llmig|{?`gPQPn>@48Ai~??4K2FD)WqrXqmSJFT^`-buopHnKdR2ti1B(Eeq* sU%9M=0p6t5!3M|Pz3;IZ)jLV}0)HJVZ}+VOtN;K207*qoM6N<$f?ddKj{pDw literal 0 HcmV?d00001 diff --git a/public/icon/96.png b/public/icon/96.png new file mode 100644 index 0000000000000000000000000000000000000000..c28ad52d56fc409195f52f57c447442326d94b6b GIT binary patch literal 2366 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 literal 0 HcmV?d00001 diff --git a/public/wxt.svg b/public/wxt.svg new file mode 100644 index 0000000..0e76320 --- /dev/null +++ b/public/wxt.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..008bc3c --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./.wxt/tsconfig.json" +} diff --git a/wxt.config.ts b/wxt.config.ts new file mode 100644 index 0000000..55fbc4a --- /dev/null +++ b/wxt.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from 'wxt'; + +// See https://wxt.dev/api/config.html +export default defineConfig({ + modules: ['@wxt-dev/module-vue'], +});