MonkeyModifier

Change webpage content

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

  1. // ==UserScript==
  2. // @name MonkeyModifier
  3. // @namespace https://github.com/JiyuShao/greasyfork-scripts
  4. // @version 2024-08-14
  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. return createPropertyTracker(this);
  182. }
  183.  
  184. open(method, url, async = true, user = null, password = null) {
  185. this._method = method;
  186. this._url = url;
  187. this._async = async;
  188. this._user = user;
  189. this._password = password;
  190. this._readyState = XMLHttpRequest.OPENED;
  191. }
  192.  
  193. send(data) {
  194. this._sendData = data;
  195. this._sendRequest();
  196. }
  197.  
  198. _sendRequest() {
  199. const self = this;
  200.  
  201. // 根据 responseType 设置 fetch 的返回类型
  202. let fetchOptions = {
  203. method: this._method,
  204. headers: new Headers(this._headers),
  205. };
  206.  
  207. // 设置请求体
  208. if (this._sendData !== null) {
  209. fetchOptions.body = this._sendData;
  210. }
  211.  
  212. if (this.withCredentials) {
  213. fetchOptions.credentials = 'include';
  214. }
  215.  
  216. // 发送 fetch 请求
  217. unsafeWindow
  218. .fetch(this._url, fetchOptions)
  219. .then((response) => {
  220. self._response = response;
  221. self._status = response.status;
  222. self._statusText = response.statusText;
  223. self._readyState = XMLHttpRequest.DONE;
  224. const responseType = self._responseType || 'text';
  225. // 设置响应类型
  226. switch (responseType) {
  227. case 'json':
  228. return response.json().then((json) => {
  229. self._responseText = JSON.stringify(json);
  230. self._response = json;
  231. self._onreadystatechange && self._onreadystatechange();
  232. self._onload && self._onload();
  233. });
  234. case 'text':
  235. return response.text().then((text) => {
  236. self._responseText = text;
  237. self._response = text;
  238. self._onreadystatechange && self._onreadystatechange();
  239. self._onload && self._onload();
  240. });
  241. case 'blob':
  242. return response.blob().then((blob) => {
  243. self._response = blob;
  244. self._onreadystatechange && self._onreadystatechange();
  245. self._onload && self._onload();
  246. });
  247. }
  248. })
  249. .catch((error) => {
  250. self._readyState = XMLHttpRequest.DONE;
  251. self._status = 0;
  252. self._statusText = 'Network Error';
  253. self._onreadystatechange && self._onreadystatechange();
  254. self._onload && self._onload();
  255. });
  256. }
  257.  
  258. setRequestHeader(name, value) {
  259. this._headers[name] = value;
  260. return this;
  261. }
  262.  
  263. getResponseHeader(name) {
  264. return this._response && this._response.headers
  265. ? this._response.headers.get(name)
  266. : null;
  267. }
  268.  
  269. getAllResponseHeaders() {
  270. return this._response && this._response.headers
  271. ? this._response.headers
  272. : null;
  273. }
  274.  
  275. set onreadystatechange(callback) {
  276. this._onreadystatechange = callback;
  277. }
  278.  
  279. set onload(callback) {
  280. this._onload = callback;
  281. }
  282.  
  283. get readyState() {
  284. return this._readyState;
  285. }
  286.  
  287. set readyState(state) {
  288. this._readyState = state;
  289. }
  290.  
  291. get response() {
  292. return this._response;
  293. }
  294.  
  295. set response(value) {
  296. this._response = value;
  297. }
  298.  
  299. get responseText() {
  300. return this._responseText;
  301. }
  302.  
  303. set responseText(value) {
  304. this._responseText = value;
  305. }
  306.  
  307. get status() {
  308. return this._status;
  309. }
  310.  
  311. set status(value) {
  312. this._status = value;
  313. }
  314.  
  315. get statusText() {
  316. return this._statusText;
  317. }
  318.  
  319. set statusText(value) {
  320. this._statusText = value;
  321. }
  322.  
  323. get responseType() {
  324. return this._responseType;
  325. }
  326.  
  327. set responseType(type) {
  328. this._responseType = type;
  329. }
  330. };
  331. }
  332.  
  333. function downloadCSV(arrayOfData, filename) {
  334. // 处理数据,使其适合 CSV 格式
  335. const csvContent = arrayOfData
  336. .map((row) =>
  337. row.map((cell) => `"${(cell || '').replace(/"/g, '""')}"`).join(',')
  338. )
  339. .join('\n');
  340.  
  341. // 在 CSV 内容前加上 BOM
  342. const bom = '\uFEFF';
  343. const csvContentWithBOM = bom + csvContent;
  344.  
  345. // 将内容转换为 Blob
  346. const blob = new Blob([csvContentWithBOM], {
  347. type: 'text/csv;charset=utf-8;',
  348. });
  349.  
  350. // 创建一个隐藏的可下载链接
  351. const url = URL.createObjectURL(blob);
  352. const link = document.createElement('a');
  353. link.href = url;
  354. link.setAttribute('download', `${filename}.csv`); // 指定文件名
  355. document.body.appendChild(link);
  356. link.click(); // 触发点击事件
  357. document.body.removeChild(link); // 清除链接
  358. URL.revokeObjectURL(url); // 释放 URL 对象
  359. }
  360.  
  361. function createPropertyTracker(target) {
  362. if (!unsafeWindow.PropertyTracker) {
  363. unsafeWindow.PropertyTracker = new Map();
  364. }
  365.  
  366. const currentPropertyAccesses = new Set();
  367. unsafeWindow.PropertyTracker.set(target, currentPropertyAccesses);
  368.  
  369. const handler = {
  370. get: function (target, prop, receiver) {
  371. currentPropertyAccesses.add(prop);
  372. return Reflect.get(target, prop, receiver);
  373. },
  374. };
  375.  
  376. return target;
  377. }
  378. // ################### 加载前插入样式覆盖
  379. const style = document.createElement('style');
  380. const cssRules = `
  381. .dropdown-submenu--viewmode {
  382. display: none !important;
  383. }
  384. [field=modified] {
  385. display: none !important;
  386. }
  387.  
  388. [data-value=modified] {
  389. display: none !important;
  390. }
  391. [data-value=lastmodify] {
  392. display: none !important;
  393. }
  394.  
  395. [data-grid-field=modified] {
  396. display: none !important;
  397. }
  398.  
  399. [data-field-key=modified] {
  400. display: none !important;
  401. }
  402.  
  403. #Revisions {
  404. display: none !important;
  405. }
  406.  
  407. #ContentModified {
  408. display: none !important;
  409. }
  410.  
  411. [title="最后修改时间"] {
  412. display: none !important;
  413. }
  414.  
  415. .left-tree-bottom__manager-company--wide {
  416. display: none !important;
  417. }
  418.  
  419. .left-tree-narrow .left-tree-bottom__personal--icons > a:nth-child(1) {
  420. display: none !important;
  421. }
  422.  
  423. .data_handover_card {
  424. display: none !important;
  425. }
  426.  
  427. .dtd-select-item-option[label='智能人事'] {
  428. display: none !important;
  429. }
  430. `;
  431. style.appendChild(document.createTextNode(cssRules));
  432. unsafeWindow.document.head.appendChild(style);
  433.  
  434. // ################### 网页内容加载完成立即执行脚本
  435. unsafeWindow.addEventListener('DOMContentLoaded', function () {
  436. // 监听任务右侧基本信息
  437. const taskRightInfoEles =
  438. unsafeWindow.document.querySelectorAll('#ContentModified');
  439. taskRightInfoEles.forEach((element) => {
  440. const parentDiv = element.closest('div.left_3_col');
  441. if (parentDiv) {
  442. parentDiv.style.display = 'none';
  443. }
  444. });
  445. });
  446.  
  447. // ################### 加载完成动态监听
  448. unsafeWindow.addEventListener('load', function () {
  449. registerMutationObserver(
  450. unsafeWindow.document.body,
  451. {
  452. attributes: false,
  453. childList: true,
  454. subtree: true,
  455. },
  456. {
  457. childList: {
  458. addedNodes: [
  459. // 动态文本替换问题
  460. {
  461. filter: (node, _mutation) => {
  462. return node.textContent.includes('最后修改时间');
  463. },
  464. action: (node, _mutation) => {
  465. replaceTextInNode(node, '最后修改时间', '迭代修改时间');
  466. },
  467. },
  468. // 监听动态弹窗 隐藏设置列表字段-最后修改时间左侧
  469. {
  470. filter: (node, _mutation) => {
  471. return (
  472. node.querySelectorAll &&
  473. node.querySelectorAll('input[value=modified]').length > 0
  474. );
  475. },
  476. action: (node, _mutation) => {
  477. node
  478. .querySelectorAll('input[value=modified]')
  479. .forEach((ele) => {
  480. const parentDiv = ele.closest('div.field');
  481. if (parentDiv) {
  482. parentDiv.style.display = 'none';
  483. }
  484. });
  485. },
  486. },
  487. // 监听动态弹窗 隐藏设置列表字段-最后修改时间右侧
  488. {
  489. filter: (node, _mutation) => {
  490. return (
  491. node.querySelectorAll &&
  492. node.querySelectorAll('span[title=最后修改时间]').length > 0
  493. );
  494. },
  495. action: (node, _mutation) => {
  496. node
  497. .querySelectorAll('span[title=最后修改时间]')
  498. .forEach((ele) => {
  499. const parentDiv = ele.closest('div[role=treeitem]');
  500. if (parentDiv) {
  501. parentDiv.style.display = 'none';
  502. }
  503. });
  504. },
  505. },
  506. // 监听企业微信导出按钮
  507. {
  508. filter: (node, _mutation) => {
  509. return (
  510. node.querySelectorAll &&
  511. node.querySelectorAll('.js_export').length > 0
  512. );
  513. },
  514. action: (node, _mutation) => {
  515. function convertTimestampToTime(timestamp) {
  516. // 创建 Date 对象
  517. const date = new Date(timestamp * 1000); // Unix 时间戳是以秒为单位,而 Date 需要毫秒
  518.  
  519. // 获取小时和分钟
  520. const hours = date.getHours();
  521. const minutes = date.getMinutes();
  522.  
  523. // 确定上午还是下午
  524. const amPm = hours >= 12 ? '下午' : '上午';
  525.  
  526. // 返回格式化的字符串
  527. return `${amPm}${hours}:${minutes
  528. .toString()
  529. .padStart(2, '0')}`;
  530. }
  531. node.querySelectorAll('.js_export').forEach((ele) => {
  532. if (ele.dataset.eventListener === 'true') {
  533. return;
  534. }
  535. ele.dataset.eventListener = 'true';
  536. ele.addEventListener('click', async function (event) {
  537. event.preventDefault();
  538. event.stopPropagation();
  539. const response = await unsafeWindow.fetch(
  540. '/wework_admin/getAdminOperationRecord?lang=zh_CN&f=json&ajax=1&timeZoneInfo%5Bzone_offset%5D=-8',
  541. {
  542. headers: {
  543. 'content-type': 'application/x-www-form-urlencoded',
  544. },
  545. body: unsafeWindow.fetchTmpBody,
  546. method: 'POST',
  547. mode: 'cors',
  548. credentials: 'include',
  549. }
  550. );
  551. const responseJson = await response.json();
  552. const excelData = responseJson.data.operloglist.reduce(
  553. (result, current) => {
  554. const typeMapping = {
  555. 9: '新增部门',
  556. 10: '删除部门',
  557. 11: '移动部门',
  558. 13: '删除成员',
  559. 14: '新增成员',
  560. 15: '更改成员信息',
  561. 21: '更改部门信息',
  562. 23: '登录后台',
  563. 25: '发送邀请',
  564. 36: '修改管理组管理员列表',
  565. 35: '修改管理组应用权限',
  566. 34: '修改管理组通讯录权限',
  567. 88: '修改汇报规则',
  568. 120: '导出相关操作记录',
  569. 162: '批量设置成员信息',
  570. };
  571. const optTypeArray = {
  572. 0: '全部',
  573. 3: '成员与部门变更',
  574. 2: '权限管理变更',
  575. 12: '企业信息管理',
  576. 11: '通讯录与聊天管理',
  577. 13: '外部联系人管理',
  578. 8: '应用变更',
  579. 7: '其他',
  580. };
  581. return [
  582. ...result,
  583. [
  584. convertTimestampToTime(current.operatetime),
  585. current.op_name,
  586. optTypeArray[current.type_oper_1],
  587. typeMapping[current.type] || '其他',
  588. current.data,
  589. current.ip,
  590. ],
  591. ];
  592. },
  593. [
  594. [
  595. '时间',
  596. '操作者',
  597. '操作类型',
  598. '操作行为',
  599. '相关数据',
  600. '操作者IP',
  601. ],
  602. ]
  603. );
  604. downloadCSV(excelData, '管理端操作记录');
  605. });
  606. });
  607. },
  608. },
  609. // 监听钉钉通讯录菜单-智能人事
  610. {
  611. filter: (node, _mutation) => {
  612. return (
  613. node.querySelectorAll &&
  614. Array.from(
  615. node.querySelectorAll(
  616. 'ul.oa-main-ant-menu .oa-main-ant-menu-title-content a'
  617. )
  618. ).filter((e) => ['智能人事'].includes(e.innerText)).length > 0
  619. );
  620. },
  621. action: (node, _mutation) => {
  622. Array.from(
  623. node.querySelectorAll(
  624. 'ul.oa-main-ant-menu .oa-main-ant-menu-title-content a'
  625. )
  626. )
  627. .filter((e) => ['智能人事'].includes(e.innerText))
  628. .forEach((ele) => {
  629. const parentDiv = ele.closest('li[role=menuitem]');
  630. if (parentDiv) {
  631. parentDiv.style.display = 'none';
  632. }
  633. });
  634. },
  635. },
  636. // 监听钉钉首页-常用应用智能人事
  637. {
  638. filter: (node, _mutation) => {
  639. return (
  640. node.querySelectorAll &&
  641. Array.from(
  642. node.querySelectorAll('#oa-new-index ul button span')
  643. ).filter((e) => ['智能人事'].includes(e.innerText)).length > 0
  644. );
  645. },
  646. action: (node, _mutation) => {
  647. Array.from(
  648. node.querySelectorAll('#oa-new-index ul button span')
  649. )
  650. .filter((e) => ['智能人事'].includes(e.innerText))
  651. .forEach((ele) => {
  652. const parentDiv = ele.closest('li');
  653. if (parentDiv) {
  654. parentDiv.style.display = 'none';
  655. }
  656. });
  657. },
  658. },
  659. // 监听钉钉工作台-智能人事
  660. {
  661. filter: (node, _mutation) => {
  662. return (
  663. node.querySelectorAll &&
  664. Array.from(
  665. node.querySelectorAll(
  666. '.oa-appcenter-app tbody tr>td:nth-child(1)>div .app-name'
  667. )
  668. ).filter((e) => ['智能人事'].includes(e.innerText)).length > 0
  669. );
  670. },
  671. action: (node, _mutation) => {
  672. Array.from(
  673. node.querySelectorAll(
  674. '.oa-appcenter-app tbody tr>td:nth-child(1)>div .app-name'
  675. )
  676. )
  677. .filter((e) => ['智能人事'].includes(e.innerText))
  678. .forEach((ele) => {
  679. const parentDiv = ele.closest('tr.dtd-table-row');
  680. if (parentDiv) {
  681. parentDiv.style.display = 'none';
  682. }
  683. });
  684. },
  685. },
  686. // 监听钉钉添加管理员-智能人事权限
  687. {
  688. filter: (node, _mutation) => {
  689. return (
  690. node.querySelectorAll &&
  691. Array.from(node.querySelectorAll('[title=智能人事]')).length >
  692. 0
  693. );
  694. },
  695. action: (node, _mutation) => {
  696. Array.from(node.querySelectorAll('[title=智能人事]')).forEach(
  697. (ele) => {
  698. const parentDiv = ele.closest('.dtd-tree-treenode');
  699. if (parentDiv) {
  700. parentDiv.style.display = 'none';
  701. }
  702. }
  703. );
  704. },
  705. },
  706. // 监听钉钉审计日志
  707. {
  708. filter: (node, _mutation) => {
  709. return (
  710. node.querySelectorAll &&
  711. Array.from(
  712. node.querySelectorAll(
  713. '.audit-content tbody tr>td:nth-child(4)>div'
  714. )
  715. ).filter((e) =>
  716. [
  717. '删除部门',
  718. '添加部门',
  719. '部门名称修改',
  720. '微应用修改',
  721. ].includes(e.innerText)
  722. ).length > 0
  723. );
  724. },
  725. action: (node, _mutation) => {
  726. Array.from(
  727. node.querySelectorAll(
  728. '.audit-content tbody tr>td:nth-child(4)>div'
  729. )
  730. )
  731. .filter((e) =>
  732. ['删除部门', '添加部门', '部门名称修改'].includes(
  733. e.innerText
  734. )
  735. )
  736. .forEach((ele) => {
  737. const parentDiv = ele.closest('tr.dtd-table-row');
  738. if (parentDiv) {
  739. parentDiv.style.display = 'none';
  740. }
  741. });
  742.  
  743. Array.from(
  744. node.querySelectorAll(
  745. '.audit-content tbody tr>td:nth-child(4)>div'
  746. )
  747. )
  748. .filter((e) => ['微应用修改'].includes(e.innerText))
  749. .forEach((e) => {
  750. const parentDiv = e.closest('tr.dtd-table-row');
  751. if (
  752. parentDiv &&
  753. parentDiv
  754. .closest('tr.dtd-table-row')
  755. .querySelectorAll('td:nth-child(2)>div>span')[0]
  756. .innerText.includes('马浩然')
  757. ) {
  758. parentDiv.style.display = 'none';
  759. }
  760. });
  761. },
  762. },
  763. // 监听钉钉审计日志-智能人事
  764. {
  765. filter: (node, _mutation) => {
  766. return (
  767. node.querySelectorAll &&
  768. Array.from(
  769. node.querySelectorAll(
  770. '.audit-content tbody tr>td:nth-child(3)>div'
  771. )
  772. ).filter((e) => ['智能人事'].includes(e.innerText)).length > 0
  773. );
  774. },
  775. action: (node, _mutation) => {
  776. Array.from(
  777. node.querySelectorAll(
  778. '.audit-content tbody tr>td:nth-child(3)>div'
  779. )
  780. )
  781. .filter((e) => ['智能人事'].includes(e.innerText))
  782. .forEach((ele) => {
  783. const parentDiv = ele.closest('tr.dtd-table-row');
  784. if (parentDiv) {
  785. parentDiv.style.display = 'none';
  786. }
  787. });
  788. },
  789. },
  790. // 监听钉钉审计日志-导出按钮
  791. {
  792. filter: (node, _mutation) => {
  793. return (
  794. node.querySelectorAll &&
  795. node.querySelectorAll(
  796. '.audit-content .dd-toolbar-btns-container .dd-toolbar-action-btns > div:nth-child(2) button'
  797. ).length > 0
  798. );
  799. },
  800. action: (node, _mutation) => {
  801. function formatTimestamp(timestamp) {
  802. // 创建 Date 对象
  803. const date = new Date(timestamp);
  804.  
  805. // 获取年、月、日、小时、分钟、秒
  806. const year = date.getFullYear();
  807. const month = String(date.getMonth() + 1).padStart(2, '0'); // 月份从 0 开始,所以需要 +1
  808. const day = String(date.getDate()).padStart(2, '0');
  809. const hours = String(date.getHours()).padStart(2, '0');
  810. const minutes = String(date.getMinutes()).padStart(2, '0');
  811. const seconds = String(date.getSeconds()).padStart(2, '0');
  812.  
  813. // 拼接日期和时间
  814. return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
  815. }
  816. node
  817. .querySelectorAll(
  818. '.audit-content .dd-toolbar-btns-container .dd-toolbar-action-btns > div:nth-child(2) button'
  819. )
  820. .forEach((ele) => {
  821. if (ele.dataset.eventListener === 'true') {
  822. return;
  823. }
  824. ele.dataset.eventListener = 'true';
  825. ele.addEventListener('click', async function (event) {
  826. event.preventDefault();
  827. event.stopPropagation();
  828. const response = await unsafeWindow.fetch(
  829. '/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',
  830. {
  831. headers: {
  832. accept: 'application/json, text/plain, */*',
  833. 'x-csrf-token': 'aXe7U6eGmTgUbnTStnqSU8',
  834. },
  835. body: null,
  836. method: 'GET',
  837. mode: 'cors',
  838. credentials: 'include',
  839. }
  840. );
  841. const responseJson = await response.json();
  842. responseJson.result = responseJson.result.filter(
  843. (currentData) => {
  844. if (
  845. ['删除部门', '添加部门', '部门名称修改'].includes(
  846. currentData.type.categoryValue
  847. )
  848. ) {
  849. return false;
  850. }
  851. if (
  852. ['微应用修改'].includes(
  853. currentData.type.categoryValue
  854. ) &&
  855. currentData.opName === '马浩然'
  856. ) {
  857. return false;
  858. }
  859. if (
  860. ['智能人事'].includes(
  861. currentData.object.categoryValue
  862. )
  863. ) {
  864. return false;
  865. }
  866. return true;
  867. }
  868. );
  869. const excelData = responseJson.result.reduce(
  870. (result, current) => {
  871. return [
  872. ...result,
  873. [
  874. formatTimestamp(current.opTime),
  875. current.opName,
  876. current.object.categoryValue,
  877. current.type.categoryValue,
  878. current.content || '',
  879. ],
  880. ];
  881. },
  882. [['时间', '操作者', '事件对象', '事件类型', '详细数据']]
  883. );
  884. downloadCSV(excelData, '审计日志信息');
  885. });
  886. });
  887. },
  888. },
  889. ],
  890. },
  891. }
  892. );
  893. });
  894.  
  895. // ################### 替换请求
  896. if (
  897. unsafeWindow.location.pathname.startsWith('/wework_admin') &&
  898. !unsafeWindow.location.href.includes('loginpage_wx')
  899. ) {
  900. registerFetchModifier([
  901. {
  902. test: (url, options) => {
  903. return url.includes('/wework_admin/getAdminOperationRecord');
  904. },
  905. prerequest: (url, options) => {
  906. options.body = options.body
  907. .split('&')
  908. .reduce((result, current) => {
  909. let [key, value] = current.split('=');
  910. if (key === 'limit') {
  911. value = 500;
  912. }
  913. return [...result, `${key}=${value}`];
  914. }, [])
  915. .join('&');
  916. unsafeWindow.fetchTmpBody = options.body;
  917. return [url, options];
  918. },
  919. preresponse: async (responsePromise) => {
  920. const response = await responsePromise;
  921. let responseJson = await response.json();
  922. responseJson.data.operloglist = responseJson.data.operloglist.filter(
  923. (e) => e.type_oper_1 !== 3
  924. );
  925. responseJson.data.total = responseJson.data.operloglist.length;
  926. return new Response(JSON.stringify(responseJson), {
  927. headers: response.headers,
  928. ok: response.ok,
  929. redirected: response.redirected,
  930. status: response.status,
  931. statusText: response.statusText,
  932. type: response.type,
  933. url: response.url,
  934. });
  935. },
  936. },
  937. ]);
  938. registerXMLHttpRequestPolyfill();
  939. }
  940. // registerXMLHttpRequestPolyfill();
  941. })();