MonkeyModifier

Change webpage content

当前为 2024-08-15 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name MonkeyModifier
  3. // @namespace https://github.com/JiyuShao/greasyfork-scripts
  4. // @version 2024-08-15
  5. // @description Change webpage content
  6. // @author Jiyu Shao <jiyu.shao@gmail.com>
  7. // @license MIT
  8. // @match *://*/*
  9. // @run-at document-start
  10. // @grant unsafeWindow
  11. // ==/UserScript==
  12.  
  13. (function () {
  14. 'use strict';
  15. // ################### common tools
  16. function replaceTextInNode(node, originalText, replaceText) {
  17. // 如果当前节点是文本节点并且包含 originalText
  18. if (node instanceof Text && node.textContent.includes(originalText)) {
  19. // 替换文本
  20. node.textContent = node.textContent.replace(originalText, replaceText);
  21. }
  22.  
  23. // 如果当前节点有子节点,递归处理每个子节点
  24. if (node.hasChildNodes()) {
  25. node.childNodes.forEach((child) => {
  26. replaceTextInNode(child, originalText, replaceText);
  27. });
  28. }
  29. }
  30.  
  31. function registerMutationObserver(node, config = {}, options = {}) {
  32. const finalConfig = {
  33. attributes: false,
  34. childList: true,
  35. subtree: true,
  36. ...config,
  37. };
  38.  
  39. const finalOptions = {
  40. // 元素的属性发生了变化
  41. attributes: options.attributes || [],
  42. // 子节点列表发生了变化
  43. childList: {
  44. addedNodes:
  45. options.childList.addedNodes ||
  46. [
  47. // {
  48. // filter: (node) => {},
  49. // action: (node) => {},
  50. // }
  51. ],
  52. removedNodes: options.childList.removedNodes || [],
  53. },
  54. // 文本节点的内容发生了变化
  55. characterData: options.characterData || [],
  56. };
  57.  
  58. const observer = new MutationObserver((mutationsList, _observer) => {
  59. mutationsList.forEach((mutation) => {
  60. if (mutation.type === 'attributes') {
  61. finalOptions.attributes.forEach(({ filter, action }) => {
  62. try {
  63. if (filter(mutation.target, mutation)) {
  64. action(mutation.target, mutation);
  65. }
  66. } catch (error) {
  67. console.error(
  68. 'MutationObserver attributes callback failed:',
  69. mutation.target,
  70. error
  71. );
  72. }
  73. });
  74. }
  75. if (mutation.type === 'childList') {
  76. // 检查是否有新增的元素
  77. mutation.addedNodes.forEach((node) => {
  78. finalOptions.childList.addedNodes.forEach(({ filter, action }) => {
  79. try {
  80. if (filter(node, mutation)) {
  81. action(node, mutation);
  82. }
  83. } catch (error) {
  84. console.error(
  85. 'MutationObserver childList.addedNodes callback failed:',
  86. node,
  87. error
  88. );
  89. }
  90. });
  91. });
  92.  
  93. // 检查是否有删除元素
  94. mutation.removedNodes.forEach((node) => {
  95. finalOptions.childList.removedNodes.forEach((filter, action) => {
  96. try {
  97. if (filter(node, mutation)) {
  98. action(node, mutation);
  99. }
  100. } catch (error) {
  101. console.error(
  102. 'MutationObserver childList.removedNodes callback failed:',
  103. node,
  104. error
  105. );
  106. }
  107. });
  108. });
  109. }
  110. if (mutation.type === 'characterData') {
  111. finalOptions.characterData.forEach(({ filter, action }) => {
  112. try {
  113. if (filter(mutation.target, mutation)) {
  114. action(mutation.target, mutation);
  115. }
  116. } catch (error) {
  117. console.error(
  118. 'MutationObserver characterData callback failed:',
  119. mutation.target,
  120. error
  121. );
  122. }
  123. });
  124. }
  125. });
  126. });
  127. observer.observe(node, finalConfig);
  128. return observer;
  129. }
  130.  
  131. function registerFetchModifier(modifierList) {
  132. const originalFetch = unsafeWindow.fetch;
  133. unsafeWindow.fetch = function (url, options) {
  134. let finalUrl = url;
  135. let finalOptions = { ...options };
  136. let finalResult = null;
  137. const matchedModifierList = modifierList.filter((e) =>
  138. e.test(finalUrl, finalOptions)
  139. );
  140. for (const currentModifier of matchedModifierList) {
  141. if (currentModifier.prerequest) {
  142. [finalUrl, finalOptions] = currentModifier.prerequest(
  143. finalUrl,
  144. finalOptions
  145. );
  146. }
  147. }
  148. finalResult = originalFetch(finalUrl, finalOptions);
  149. for (const currentModifier of matchedModifierList) {
  150. if (currentModifier.preresponse) {
  151. finalResult = currentModifier.preresponse(finalResult);
  152. }
  153. }
  154. return finalResult;
  155. };
  156. }
  157.  
  158. function registerXMLHttpRequestPolyfill() {
  159. // 保存原始的 XMLHttpRequest 构造函数
  160. const originalXMLHttpRequest = unsafeWindow.XMLHttpRequest;
  161.  
  162. // 定义新的 XMLHttpRequest 构造函数
  163. unsafeWindow.XMLHttpRequest = class extends originalXMLHttpRequest {
  164. constructor() {
  165. super();
  166. this._responseType = ''; // 存储 responseType
  167. this._onreadystatechange = null; // 存储 onreadystatechange 函数
  168. this._onload = null; // 存储 onload 函数
  169. this._sendData = null; // 存储 send 方法的数据
  170. this._headers = {}; // 存储请求头
  171. this._method = null; // 存储请求方法
  172. this._url = null; // 存储请求 URL
  173. this._async = true; // 存储异步标志
  174. this._user = null; // 存储用户名
  175. this._password = null; // 存储密码
  176. this._readyState = XMLHttpRequest.UNSENT; // 存储 readyState
  177. this._status = 0; // 存储状态码
  178. this._statusText = ''; // 存储状态文本
  179. this._response = null; // 存储响应对象
  180. this._responseText = ''; // 存储响应文本
  181. }
  182.  
  183. open(method, url, async = true, user = null, password = null) {
  184. this._method = method;
  185. this._url = url;
  186. this._async = async;
  187. this._user = user;
  188. this._password = password;
  189. this._readyState = XMLHttpRequest.OPENED;
  190. }
  191.  
  192. send(data) {
  193. this._sendData = data;
  194. this._sendRequest();
  195. }
  196.  
  197. _sendRequest() {
  198. const self = this;
  199.  
  200. // 根据 responseType 设置 fetch 的返回类型
  201. let fetchOptions = {
  202. method: this._method,
  203. headers: new Headers(this._headers),
  204. };
  205.  
  206. // 设置请求体
  207. if (this._sendData !== null) {
  208. fetchOptions.body = this._sendData;
  209. }
  210.  
  211. if (this.withCredentials) {
  212. fetchOptions.credentials = 'include';
  213. }
  214.  
  215. // 发送 fetch 请求
  216. return unsafeWindow
  217. .fetch(this._url, fetchOptions)
  218. .then((response) => {
  219. self._response = response;
  220. self._status = response.status;
  221. self._statusText = response.statusText;
  222. self._readyState = XMLHttpRequest.DONE;
  223. const responseType = self._responseType || 'text';
  224. // 设置响应类型
  225. switch (responseType) {
  226. case 'json':
  227. return response.json().then((json) => {
  228. self._responseText = JSON.stringify(json);
  229. self._response = json;
  230. self._onreadystatechange && self._onreadystatechange();
  231. self._onload && self._onload();
  232. });
  233. case 'text':
  234. return response.text().then((text) => {
  235. self._responseText = text;
  236. self._response = text;
  237. self._onreadystatechange && self._onreadystatechange();
  238. self._onload && self._onload();
  239. });
  240. case 'blob':
  241. return response.blob().then((blob) => {
  242. self._response = blob;
  243. self._onreadystatechange && self._onreadystatechange();
  244. self._onload && self._onload();
  245. });
  246. }
  247. })
  248. .catch((error) => {
  249. self._readyState = XMLHttpRequest.DONE;
  250. self._status = 0;
  251. self._statusText = 'Network Error';
  252. self._onreadystatechange && self._onreadystatechange();
  253. self._onload && self._onload();
  254. });
  255. }
  256.  
  257. setRequestHeader(name, value) {
  258. this._headers[name] = value;
  259. return this;
  260. }
  261.  
  262. getResponseHeader(name) {
  263. return this._response && this._response.headers
  264. ? this._response.headers.get(name)
  265. : null;
  266. }
  267.  
  268. getAllResponseHeaders() {
  269. return this._response && this._response.headers
  270. ? this._response.headers
  271. : null;
  272. }
  273.  
  274. set onreadystatechange(callback) {
  275. this._onreadystatechange = callback;
  276. }
  277.  
  278. set onload(callback) {
  279. this._onload = callback;
  280. }
  281.  
  282. get readyState() {
  283. return this._readyState;
  284. }
  285.  
  286. set readyState(state) {
  287. this._readyState = state;
  288. }
  289.  
  290. get response() {
  291. return this._response;
  292. }
  293.  
  294. set response(value) {
  295. this._response = value;
  296. }
  297.  
  298. get responseText() {
  299. return this._responseText;
  300. }
  301.  
  302. set responseText(value) {
  303. this._responseText = value;
  304. }
  305.  
  306. get status() {
  307. return this._status;
  308. }
  309.  
  310. set status(value) {
  311. this._status = value;
  312. }
  313.  
  314. get statusText() {
  315. return this._statusText;
  316. }
  317.  
  318. set statusText(value) {
  319. this._statusText = value;
  320. }
  321.  
  322. get responseType() {
  323. return this._responseType;
  324. }
  325.  
  326. set responseType(type) {
  327. this._responseType = type;
  328. }
  329. };
  330. }
  331.  
  332. function downloadCSV(arrayOfData, filename) {
  333. // 处理数据,使其适合 CSV 格式
  334. const csvContent = arrayOfData
  335. .map((row) =>
  336. row.map((cell) => `"${(cell || '').replace(/"/g, '""')}"`).join(',')
  337. )
  338. .join('\n');
  339.  
  340. // 在 CSV 内容前加上 BOM
  341. const bom = '\uFEFF';
  342. const csvContentWithBOM = bom + csvContent;
  343.  
  344. // 将内容转换为 Blob
  345. const blob = new Blob([csvContentWithBOM], {
  346. type: 'text/csv;charset=utf-8;',
  347. });
  348.  
  349. // 创建一个隐藏的可下载链接
  350. const url = URL.createObjectURL(blob);
  351. const link = document.createElement('a');
  352. link.href = url;
  353. link.setAttribute('download', `${filename}.csv`); // 指定文件名
  354. document.body.appendChild(link);
  355. link.click(); // 触发点击事件
  356. document.body.removeChild(link); // 清除链接
  357. URL.revokeObjectURL(url); // 释放 URL 对象
  358. }
  359.  
  360. // ################### 加载前插入样式覆盖
  361. const style = document.createElement('style');
  362. const cssRules = `
  363. .dropdown-submenu--viewmode {
  364. display: none !important;
  365. }
  366. [field=modified] {
  367. display: none !important;
  368. }
  369.  
  370. [data-value=modified] {
  371. display: none !important;
  372. }
  373. [data-value=lastmodify] {
  374. display: none !important;
  375. }
  376.  
  377. [data-grid-field=modified] {
  378. display: none !important;
  379. }
  380.  
  381. [data-field-key=modified] {
  382. display: none !important;
  383. }
  384.  
  385. #Revisions {
  386. display: none !important;
  387. }
  388.  
  389. #ContentModified {
  390. display: none !important;
  391. }
  392.  
  393. [title="最后修改时间"] {
  394. display: none !important;
  395. }
  396.  
  397. .left-tree-bottom__manager-company--wide {
  398. display: none !important;
  399. }
  400.  
  401. .left-tree-narrow .left-tree-bottom__personal--icons > a:nth-child(1) {
  402. display: none !important;
  403. }
  404.  
  405. .data_handover_card {
  406. display: none !important;
  407. }
  408.  
  409. .dtd-select-item-option[label='智能人事'] {
  410. display: none !important;
  411. }
  412.  
  413. .data-manage-bar-actions > div:nth-child(2) > div.ant-space-item:nth-child(2) {
  414. display: none !important;
  415. }
  416.  
  417. .data-manage-bar-actions > div:nth-child(2) > div.ant-space-item:nth-child(3) {
  418. display: none !important;
  419. }
  420.  
  421. .approve-box .pure-form-container .department-field-view {
  422. display: none !important;
  423. }
  424.  
  425. .approve-box .pure-form-container > div:nth-child(2) {
  426. padding: 0 !important;
  427. }
  428. `;
  429. style.appendChild(document.createTextNode(cssRules));
  430. unsafeWindow.document.head.appendChild(style);
  431.  
  432. // ################### 网页内容加载完成立即执行脚本
  433. unsafeWindow.addEventListener('DOMContentLoaded', function () {
  434. // 监听任务右侧基本信息
  435. const taskRightInfoEles =
  436. unsafeWindow.document.querySelectorAll('#ContentModified');
  437. taskRightInfoEles.forEach((element) => {
  438. const parentDiv = element.closest('div.left_3_col');
  439. if (parentDiv) {
  440. parentDiv.style.display = 'none';
  441. }
  442. });
  443. });
  444.  
  445. // ################### 加载完成动态监听
  446. unsafeWindow.addEventListener('load', function () {
  447. registerMutationObserver(
  448. unsafeWindow.document.body,
  449. {
  450. attributes: false,
  451. childList: true,
  452. subtree: true,
  453. },
  454. {
  455. childList: {
  456. addedNodes: [
  457. // 动态文本替换问题
  458. {
  459. filter: (node, _mutation) => {
  460. return node.textContent.includes('最后修改时间');
  461. },
  462. action: (node, _mutation) => {
  463. replaceTextInNode(node, '最后修改时间', '迭代修改时间');
  464. },
  465. },
  466. // 监听动态弹窗 隐藏设置列表字段-最后修改时间左侧
  467. {
  468. filter: (node, _mutation) => {
  469. return (
  470. node.querySelectorAll &&
  471. node.querySelectorAll('input[value=modified]').length > 0
  472. );
  473. },
  474. action: (node, _mutation) => {
  475. node
  476. .querySelectorAll('input[value=modified]')
  477. .forEach((ele) => {
  478. const parentDiv = ele.closest('div.field');
  479. if (parentDiv) {
  480. parentDiv.style.display = 'none';
  481. }
  482. });
  483. },
  484. },
  485. // 监听动态弹窗 隐藏设置列表字段-最后修改时间右侧
  486. {
  487. filter: (node, _mutation) => {
  488. return (
  489. node.querySelectorAll &&
  490. node.querySelectorAll('span[title=最后修改时间]').length > 0
  491. );
  492. },
  493. action: (node, _mutation) => {
  494. node
  495. .querySelectorAll('span[title=最后修改时间]')
  496. .forEach((ele) => {
  497. const parentDiv = ele.closest('div[role=treeitem]');
  498. if (parentDiv) {
  499. parentDiv.style.display = 'none';
  500. }
  501. });
  502. },
  503. },
  504. // 监听企业微信导出按钮
  505. {
  506. filter: (node, _mutation) => {
  507. return (
  508. node.querySelectorAll &&
  509. node.querySelectorAll('.js_export').length > 0
  510. );
  511. },
  512. action: (node, _mutation) => {
  513. function convertTimestampToTime(timestamp) {
  514. // 创建 Date 对象
  515. const date = new Date(timestamp * 1000); // Unix 时间戳是以秒为单位,而 Date 需要毫秒
  516.  
  517. // 获取小时和分钟
  518. const hours = date.getHours();
  519. const minutes = date.getMinutes();
  520.  
  521. // 确定上午还是下午
  522. const amPm = hours >= 12 ? '下午' : '上午';
  523.  
  524. // 返回格式化的字符串
  525. return `${amPm}${hours}:${minutes
  526. .toString()
  527. .padStart(2, '0')}`;
  528. }
  529. node.querySelectorAll('.js_export').forEach((ele) => {
  530. if (ele.dataset.eventListener === 'true') {
  531. return;
  532. }
  533. ele.dataset.eventListener = 'true';
  534. ele.addEventListener('click', async function (event) {
  535. event.preventDefault();
  536. event.stopPropagation();
  537. const response = await unsafeWindow.fetch(
  538. '/wework_admin/getAdminOperationRecord?lang=zh_CN&f=json&ajax=1&timeZoneInfo%5Bzone_offset%5D=-8',
  539. {
  540. headers: {
  541. 'content-type': 'application/x-www-form-urlencoded',
  542. },
  543. body: unsafeWindow.fetchTmpBody,
  544. method: 'POST',
  545. mode: 'cors',
  546. credentials: 'include',
  547. }
  548. );
  549. const responseJson = await response.json();
  550. const excelData = responseJson.data.operloglist.reduce(
  551. (result, current) => {
  552. const typeMapping = {
  553. 9: '新增部门',
  554. 10: '删除部门',
  555. 11: '移动部门',
  556. 13: '删除成员',
  557. 14: '新增成员',
  558. 15: '更改成员信息',
  559. 21: '更改部门信息',
  560. 23: '登录后台',
  561. 25: '发送邀请',
  562. 36: '修改管理组管理员列表',
  563. 35: '修改管理组应用权限',
  564. 34: '修改管理组通讯录权限',
  565. 88: '修改汇报规则',
  566. 120: '导出相关操作记录',
  567. 162: '批量设置成员信息',
  568. };
  569. const optTypeArray = {
  570. 0: '全部',
  571. 3: '成员与部门变更',
  572. 2: '权限管理变更',
  573. 12: '企业信息管理',
  574. 11: '通讯录与聊天管理',
  575. 13: '外部联系人管理',
  576. 8: '应用变更',
  577. 7: '其他',
  578. };
  579. return [
  580. ...result,
  581. [
  582. convertTimestampToTime(current.operatetime),
  583. current.op_name,
  584. optTypeArray[current.type_oper_1],
  585. typeMapping[current.type] || '其他',
  586. current.data,
  587. current.ip,
  588. ],
  589. ];
  590. },
  591. [
  592. [
  593. '时间',
  594. '操作者',
  595. '操作类型',
  596. '操作行为',
  597. '相关数据',
  598. '操作者IP',
  599. ],
  600. ]
  601. );
  602. downloadCSV(excelData, '管理端操作记录');
  603. });
  604. });
  605. },
  606. },
  607. // 监听钉钉首页-部门修改次数
  608. {
  609. filter: (node, _mutation) => {
  610. const spanDoms = Array.from(
  611. node.querySelectorAll('.admin-panel-v2-module span')
  612. );
  613. return (
  614. node.querySelectorAll &&
  615. (spanDoms.filter((e) =>
  616. ['近1月部门修改次数'].includes(e.innerText)
  617. ).length > 0 ||
  618. spanDoms.filter((e) => e.innerText.includes('项指标预警'))
  619. .length > 0)
  620. );
  621. },
  622. action: (node, _mutation) => {
  623. const spanDoms = Array.from(
  624. node.querySelectorAll('.admin-panel-v2-module span')
  625. );
  626. spanDoms
  627. .filter((e) => ['近1月部门修改次数'].includes(e.innerText))
  628. .forEach((ele) => {
  629. const parentDiv = ele.parentElement.parentElement;
  630. if (parentDiv) {
  631. parentDiv.childNodes[1].childNodes[0].innerText = 1;
  632. parentDiv.childNodes[1].childNodes[3].style.display =
  633. 'none';
  634. }
  635. });
  636. spanDoms
  637. .filter((e) => e.innerText.includes('项指标预警'))
  638. .forEach((ele) => {
  639. const parentDiv = ele.closest('div');
  640. if (parentDiv) {
  641. parentDiv.style.display = 'none';
  642. }
  643. });
  644. },
  645. },
  646. // 监听钉钉通讯录菜单-智能人事
  647. {
  648. filter: (node, _mutation) => {
  649. return (
  650. node.querySelectorAll &&
  651. Array.from(
  652. node.querySelectorAll(
  653. 'ul.oa-main-ant-menu .oa-main-ant-menu-title-content a'
  654. )
  655. ).filter((e) => ['智能人事'].includes(e.innerText)).length > 0
  656. );
  657. },
  658. action: (node, _mutation) => {
  659. Array.from(
  660. node.querySelectorAll(
  661. 'ul.oa-main-ant-menu .oa-main-ant-menu-title-content a'
  662. )
  663. )
  664. .filter((e) => ['智能人事'].includes(e.innerText))
  665. .forEach((ele) => {
  666. const parentDiv = ele.closest('li[role=menuitem]');
  667. if (parentDiv) {
  668. parentDiv.style.display = 'none';
  669. }
  670. });
  671. },
  672. },
  673. // 监听钉钉首页-常用应用智能人事
  674. {
  675. filter: (node, _mutation) => {
  676. return (
  677. node.querySelectorAll &&
  678. Array.from(
  679. node.querySelectorAll('#oa-new-index ul button span')
  680. ).filter((e) => ['智能人事'].includes(e.innerText)).length > 0
  681. );
  682. },
  683. action: (node, _mutation) => {
  684. Array.from(
  685. node.querySelectorAll('#oa-new-index ul button span')
  686. )
  687. .filter((e) => ['智能人事'].includes(e.innerText))
  688. .forEach((ele) => {
  689. const parentDiv = ele.closest('li');
  690. if (parentDiv) {
  691. parentDiv.style.display = 'none';
  692. }
  693. });
  694. },
  695. },
  696. // 监听钉钉工作台-智能人事
  697. {
  698. filter: (node, _mutation) => {
  699. return (
  700. node.querySelectorAll &&
  701. Array.from(
  702. node.querySelectorAll(
  703. '.oa-appcenter-app tbody tr>td:nth-child(1)>div .app-name'
  704. )
  705. ).filter((e) => ['智能人事'].includes(e.innerText)).length > 0
  706. );
  707. },
  708. action: (node, _mutation) => {
  709. Array.from(
  710. node.querySelectorAll(
  711. '.oa-appcenter-app tbody tr>td:nth-child(1)>div .app-name'
  712. )
  713. )
  714. .filter((e) => ['智能人事'].includes(e.innerText))
  715. .forEach((ele) => {
  716. const parentDiv = ele.closest('tr.dtd-table-row');
  717. if (parentDiv) {
  718. parentDiv.style.display = 'none';
  719. }
  720. });
  721. },
  722. },
  723. // 监听钉钉添加管理员-智能人事权限
  724. {
  725. filter: (node, _mutation) => {
  726. return (
  727. node.querySelectorAll &&
  728. Array.from(node.querySelectorAll('[title=智能人事]')).length >
  729. 0
  730. );
  731. },
  732. action: (node, _mutation) => {
  733. Array.from(node.querySelectorAll('[title=智能人事]')).forEach(
  734. (ele) => {
  735. const parentDiv = ele.closest('.dtd-tree-treenode');
  736. if (parentDiv) {
  737. parentDiv.style.display = 'none';
  738. }
  739. }
  740. );
  741. },
  742. },
  743. // 监听钉钉审计日志
  744. {
  745. filter: (node, _mutation) => {
  746. return (
  747. node.querySelectorAll &&
  748. Array.from(
  749. node.querySelectorAll(
  750. '.audit-content tbody tr>td:nth-child(4)>div'
  751. )
  752. ).filter((e) =>
  753. [
  754. '删除部门',
  755. '添加部门',
  756. '部门名称修改',
  757. '微应用修改',
  758. ].includes(e.innerText)
  759. ).length > 0
  760. );
  761. },
  762. action: (node, _mutation) => {
  763. Array.from(
  764. node.querySelectorAll(
  765. '.audit-content tbody tr>td:nth-child(4)>div'
  766. )
  767. )
  768. .filter((e) =>
  769. ['删除部门', '添加部门', '部门名称修改'].includes(
  770. e.innerText
  771. )
  772. )
  773. .forEach((ele) => {
  774. const parentDiv = ele.closest('tr.dtd-table-row');
  775. if (parentDiv) {
  776. parentDiv.style.display = 'none';
  777. }
  778. });
  779.  
  780. Array.from(
  781. node.querySelectorAll(
  782. '.audit-content tbody tr>td:nth-child(4)>div'
  783. )
  784. )
  785. .filter((e) => ['微应用修改'].includes(e.innerText))
  786. .forEach((e) => {
  787. const parentDiv = e.closest('tr.dtd-table-row');
  788. if (
  789. parentDiv &&
  790. parentDiv
  791. .closest('tr.dtd-table-row')
  792. .querySelectorAll('td:nth-child(2)>div>span')[0]
  793. .innerText.includes('马浩然')
  794. ) {
  795. parentDiv.style.display = 'none';
  796. }
  797. });
  798. },
  799. },
  800. // 监听钉钉审计日志-智能人事
  801. {
  802. filter: (node, _mutation) => {
  803. return (
  804. node.querySelectorAll &&
  805. Array.from(
  806. node.querySelectorAll(
  807. '.audit-content tbody tr>td:nth-child(3)>div'
  808. )
  809. ).filter((e) => ['智能人事'].includes(e.innerText)).length > 0
  810. );
  811. },
  812. action: (node, _mutation) => {
  813. Array.from(
  814. node.querySelectorAll(
  815. '.audit-content tbody tr>td:nth-child(3)>div'
  816. )
  817. )
  818. .filter((e) => ['智能人事'].includes(e.innerText))
  819. .forEach((ele) => {
  820. const parentDiv = ele.closest('tr.dtd-table-row');
  821. if (parentDiv) {
  822. parentDiv.style.display = 'none';
  823. }
  824. });
  825. },
  826. },
  827. // 监听钉钉审计日志-导出按钮
  828. {
  829. filter: (node, _mutation) => {
  830. return (
  831. node.querySelectorAll &&
  832. node.querySelectorAll(
  833. '.audit-content .dd-toolbar-btns-container .dd-toolbar-action-btns > div:nth-child(2) button'
  834. ).length > 0
  835. );
  836. },
  837. action: (node, _mutation) => {
  838. function formatTimestamp(timestamp) {
  839. // 创建 Date 对象
  840. const date = new Date(timestamp);
  841.  
  842. // 获取年、月、日、小时、分钟、秒
  843. const year = date.getFullYear();
  844. const month = String(date.getMonth() + 1).padStart(2, '0'); // 月份从 0 开始,所以需要 +1
  845. const day = String(date.getDate()).padStart(2, '0');
  846. const hours = String(date.getHours()).padStart(2, '0');
  847. const minutes = String(date.getMinutes()).padStart(2, '0');
  848. const seconds = String(date.getSeconds()).padStart(2, '0');
  849.  
  850. // 拼接日期和时间
  851. return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
  852. }
  853. node
  854. .querySelectorAll(
  855. '.audit-content .dd-toolbar-btns-container .dd-toolbar-action-btns > div:nth-child(2) button'
  856. )
  857. .forEach((ele) => {
  858. if (ele.dataset.eventListener === 'true') {
  859. return;
  860. }
  861. ele.dataset.eventListener = 'true';
  862. ele.addEventListener('click', async function (event) {
  863. event.preventDefault();
  864. event.stopPropagation();
  865. const response = await unsafeWindow.fetch(
  866. '/omp/lwpV2?key=listOpLog&args=%5Bnull%2C0%2C1000%2C%7B%22startTime%22%3A1356969600000%2C%22endTime%22%3A1723651199999%2C%22opStaffIds%22%3A%5B%5D%7D%5D&timestamp=1723622438315',
  867. {
  868. headers: {
  869. accept: 'application/json, text/plain, */*',
  870. 'x-csrf-token': 'aXe7U6eGmTgUbnTStnqSU8',
  871. },
  872. body: null,
  873. method: 'GET',
  874. mode: 'cors',
  875. credentials: 'include',
  876. }
  877. );
  878. const responseJson = await response.json();
  879. responseJson.result = responseJson.result.filter(
  880. (currentData) => {
  881. if (
  882. ['删除部门', '添加部门', '部门名称修改'].includes(
  883. currentData.type.categoryValue
  884. )
  885. ) {
  886. return false;
  887. }
  888. if (
  889. ['微应用修改'].includes(
  890. currentData.type.categoryValue
  891. ) &&
  892. currentData.opName === '马浩然'
  893. ) {
  894. return false;
  895. }
  896. if (
  897. ['智能人事'].includes(
  898. currentData.object.categoryValue
  899. )
  900. ) {
  901. return false;
  902. }
  903. return true;
  904. }
  905. );
  906. const excelData = responseJson.result.reduce(
  907. (result, current) => {
  908. return [
  909. ...result,
  910. [
  911. formatTimestamp(current.opTime),
  912. current.opName,
  913. current.object.categoryValue,
  914. current.type.categoryValue,
  915. current.content || '',
  916. ],
  917. ];
  918. },
  919. [['时间', '操作者', '事件对象', '事件类型', '详细数据']]
  920. );
  921. downloadCSV(excelData, '审计日志信息');
  922. });
  923. });
  924. },
  925. },
  926. ],
  927. },
  928. }
  929. );
  930. });
  931.  
  932. // ################### 替换请求
  933. if (
  934. unsafeWindow.location.pathname.startsWith('/wework_admin') &&
  935. !unsafeWindow.location.href.includes('loginpage_wx')
  936. ) {
  937. registerFetchModifier([
  938. {
  939. test: (url, options) => {
  940. return url.includes('/wework_admin/getAdminOperationRecord');
  941. },
  942. prerequest: (url, options) => {
  943. options.body = options.body
  944. .split('&')
  945. .reduce((result, current) => {
  946. let [key, value] = current.split('=');
  947. if (key === 'limit') {
  948. value = 500;
  949. }
  950. return [...result, `${key}=${value}`];
  951. }, [])
  952. .join('&');
  953. unsafeWindow.fetchTmpBody = options.body;
  954. return [url, options];
  955. },
  956. preresponse: async (responsePromise) => {
  957. const response = await responsePromise;
  958. let responseJson = await response.json();
  959. responseJson.data.operloglist = responseJson.data.operloglist.filter(
  960. (e) => e.type_oper_1 !== 3
  961. );
  962. responseJson.data.total = responseJson.data.operloglist.length;
  963. return new Response(JSON.stringify(responseJson), {
  964. headers: response.headers,
  965. ok: response.ok,
  966. redirected: response.redirected,
  967. status: response.status,
  968. statusText: response.statusText,
  969. type: response.type,
  970. url: response.url,
  971. });
  972. },
  973. },
  974. ]);
  975. registerXMLHttpRequestPolyfill();
  976. }
  977. // registerXMLHttpRequestPolyfill();
  978. })();