UMU 增强

增强 UMU 的体验,可根据参与人数进行搜索,可模糊搜索,支持在新窗口打开页面从而不丢失当前浏览状态。

  1. // ==UserScript==
  2. // @name UMU 增强
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.0
  5. // @description 增强 UMU 的体验,可根据参与人数进行搜索,可模糊搜索,支持在新窗口打开页面从而不丢失当前浏览状态。
  6. // @author LinHQ
  7. // @match https://m.umu.cn/course/*
  8. // @icon https://m.umu.cn/favicon.ico
  9. // @grant GM_addStyle
  10. // @grant GM_xmlhttpRequest
  11. // @connect https://m.umu.cn/uapi/*
  12. // @require https://gf.qytechs.cn/scripts/429203-async-xmlhttprequest/code/Async%20xmlhttprequest.js?version=949226
  13. // ==/UserScript==
  14. 'use strict'
  15.  
  16. // 搜索关键字 ask
  17. let TYPES = new Map();
  18. TYPES.set(1,"survey");
  19. TYPES.set(2, "ask");
  20. TYPES.set(3, "discussion");
  21. TYPES.set(4, "photo");
  22. TYPES.set(5, "GAME");
  23. TYPES.set(6, "attendance");
  24. TYPES.set(7, "mini_course");
  25. TYPES.set(8, "raffle_drawing");
  26. TYPES.set(9, "registration");
  27. TYPES.set(10, "quiz");
  28. TYPES.set(11, "video");
  29. TYPES.set(12, "live");
  30. TYPES.set(13, "article");
  31. TYPES.set(14, "document");
  32. TYPES.set(16, "exercise/?surl=");
  33.  
  34. let GROUPID = new URL(window.location.href).searchParams.get('groupId');
  35.  
  36. // 获取第{cid}目录下的内容
  37. async function getChapters(cid, sessionCount) {
  38. let resp = await req({
  39. method: "POST",
  40. headers: {"Content-type": "application/x-www-form-urlencoded"},
  41. url: "https://m.umu.cn/uapi/v2/element/chapter-session",
  42. data: `t=${new Date().getTime()}&parent_id=${GROUPID}&chapter_id=${cid}&page=1&size=${sessionCount}&get_draft=0`
  43. });
  44. // 解析返回数据
  45. let li = JSON.parse(resp.responseText).data.list;
  46. li = li.map(l => {
  47. return {
  48. title: l.index + '.' + l.title,
  49. href: l.share_card_view.replace("element/share", `session/${TYPES.get(l.type)}`),
  50. people: parseInt(l.stat.finish_num),
  51. learned: l.extend.learn_status !== 0
  52. }
  53. });
  54. console.log(li);
  55. return li;
  56. }
  57.  
  58. // 获取第{id}页
  59. async function getPage(index) {
  60. let resp = await req({
  61. method: "POST",
  62. headers: {"Content-type": "application/x-www-form-urlencoded"},
  63. url: "https://m.umu.cn/uapi/v2/element/list",
  64. data: `t=${new Date().getTime()}&parent_id=${GROUPID}&page=${index}&size=15&get_draft=0`
  65. });
  66. return JSON.parse(resp.responseText).data;
  67. }
  68.  
  69. // 获取所有列表
  70. async function getIds() {
  71. let ids = [];
  72. let meta = await getPage(1);
  73. const pages = meta.page_info.total_page_num;
  74. for (let p = 1; p <= pages; p++) {
  75. let currentPage = await getPage(p);
  76. for (let dir of currentPage.list){
  77. let chapterIds = await getChapters(dir.id, parseInt(dir.session_count));
  78. ids = ids.concat(chapterIds);
  79. }
  80. }
  81. return ids;
  82. }
  83.  
  84. // 根据人数进行过滤
  85. function doFilter(filter, data) {
  86. console.log(data)
  87. // 【模式,值】
  88. let [mode, v] = filter.split(":");
  89. switch (mode) {
  90. case 's':
  91. return data.filter(e => e.title !== undefined && e.title.includes(v));
  92. case 'gt':
  93. return data.filter(e => e.people >= parseInt(v));
  94. case 'lt':
  95. return data.filter(e => e.people <= parseInt(v));
  96. case 'd':
  97. return data.filter(e => e.learned);
  98. case '!d':
  99. return data.filter(e => !e.learned);
  100. default:
  101. return [];
  102. }
  103. }
  104.  
  105. (function () {
  106. GM_addStyle(`#gm_box{
  107. position: fixed;
  108. z-index: 9999;
  109. top: 0vh;
  110. left: 0;
  111. border: 1px solid #a0c5fd;
  112. border-radius: 5px;
  113. height: 95vh;
  114. width: 3.4rem;
  115. display: flex;
  116. flex-flow: column;
  117. }
  118.  
  119. #gm_box input {
  120. margin: 10px;
  121. }
  122.  
  123. #gm_box .show{
  124. height: 90vh;
  125. width: 3.4rem;
  126. margin: 3px auto;
  127. overflow: auto;
  128. padding: 5px;
  129. }
  130.  
  131. #gm_box li {
  132. margin: 5px;
  133. border-left: 3px solid #fa541c;
  134. background: #e6e6e6;
  135. border-radius: 3px;
  136. padding: 3px;
  137. color: #114979;
  138. cursor: pointer;
  139. line-height: 1.2em;
  140. }
  141.  
  142. #gm_box li.learned {
  143. border-left: 3px solid #52c41a;
  144. }
  145.  
  146. #gm_box li.clicked {
  147. border-left: 3px solid #73289b;
  148. }
  149.  
  150. #gm_box .state {
  151. text-align: center;
  152. align-self: center;
  153. font-size: 0.7em;
  154. }
  155.  
  156. #gm_box .state span {
  157. color: #ff7875;
  158. }
  159. `);
  160. let container = document.createElement("div");
  161. container.id = "gm_box";
  162. container.innerHTML = `
  163. <input type="text" value="s:"/>
  164. <div class="state">Enter以开始搜索</div>
  165. <div class="show">
  166. <ol>
  167. </ol>
  168. </div>
  169. `;
  170. document.body.appendChild(container);
  171.  
  172. let reader = document.querySelector("#gm_box input");
  173. let li = [];
  174. reader.onkeypress = async (e) => {
  175. let ol = document.querySelector(".show>ol");
  176. let state = document.querySelector("div.state");
  177.  
  178. if (e.code === "Enter") {
  179. // 清除之前内容
  180. ol.innerHTML = "";
  181.  
  182. // 检查缓存
  183. if (li.length === 0) {
  184. state.textContent = "正在加载中,请稍后...";
  185. li = await getIds();
  186. }
  187.  
  188. let res = doFilter(e.target.value, li);
  189. // 展示结果
  190. state.textContent = `${res.length} 条结果`;
  191. for (let l of res) {
  192. let li = document.createElement("li");
  193. li.textContent = `${l.title}`;
  194. ol.appendChild(li);
  195. if (l.learned) li.classList.add("learned");
  196.  
  197. li.onclick = () => {
  198. // 点击变色
  199. li.classList.add("clicked");
  200. window.open(l.href)
  201. };
  202. }
  203. }
  204. };
  205.  
  206. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址