您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
丸子的泛微ecology9-IT助手,让你维护泛微OA系统更加顺畅!
// ==UserScript== // @name 泛微ecology9-IT助手 // @namespace https://huahan.cc/ // @homepageURL https://huahan.cc/cn/index.php // @version 1.4.1 // @description 丸子的泛微ecology9-IT助手,让你维护泛微OA系统更加顺畅! // @author Oneszhang 丸子 // @match http*://*/wui/* // @match http*://*/spa/* // @match http*://*/security/monitor/* // @icon https://www.weaver.com.cn/img/favicon.ico // @grant none // @run-at document-end // @noframes // @license GPL-3.0 // ==/UserScript== (function() { 'use strict'; const config = { ecode: true, engineerNavigation: true, dataDictionary: true, iconLibrary: true, goFeedback: true, mobilePage: true, securityMonitor: true, cacheMonitor: true, selectList: true }; const targetHashPath = '#/workflowengine/path/pathSet/pathDetail/formManage/editField'; // 监控hash变化并触发事件 function checkHashAndTriggerEvent() { const currentHash = window.location.hash; if (currentHash.includes(targetHashPath)) { console.log('Target hash path detected:', currentHash); setupMutationObserver(); // 初始化MutationObserver appendIDToFieldPosition(); // 初次加载表格时添加ID到字段位置列后 } } // 监听哈希变化 window.addEventListener('hashchange', function() { checkHashAndTriggerEvent(); }); // 初次加载页面时检查一次哈希 checkHashAndTriggerEvent(); // 等待表格加载的函数 function waitForTableLoad() { const targetNode = document.querySelector('.wea-new-table'); if (!targetNode) { // console.log('Table container not found, checking again in 500ms.'); setTimeout(waitForTableLoad, 500); return; } const observer = new MutationObserver((mutations, obs) => { const tableHeader = document.querySelector('.ant-table thead tr'); const tableBodyRows = document.querySelectorAll('.ant-table tbody tr'); if (tableHeader && tableBodyRows.length > 0) { // console.log('Table structure found, executing code.'); appendIDToFieldPosition(); setupMutationObserver(); // 初始化MutationObserver,监控后续翻页等操作 obs.disconnect(); // 观察器不再需要,断开连接 } }); observer.observe(targetNode, { childList: true, subtree: true }); } // 调用等待表格加载的函数 waitForTableLoad(); // 将ID值附加到字段位置列值后的函数 function appendIDToFieldPosition() { const tableBodyRows = document.querySelectorAll('.ant-table tbody tr'); tableBodyRows.forEach((row) => { const fieldPositionTd = row.querySelector('td:nth-child(4)'); // 第四列为字段位置列 const idTd = row.querySelector('td:last-child'); // 最后一列为ID列 if (fieldPositionTd && idTd && !fieldPositionTd.innerText.includes('(')) { const idValue = idTd.getAttribute('stsdata'); fieldPositionTd.innerText += ` (${idValue})`; } }); // console.log('ID appended to Field Position column successfully.'); } // 设置MutationObserver监控翻页或数据变化 function setupMutationObserver() { const tableBody = document.querySelector('.ant-table tbody'); if (!tableBody) { // console.log('Table body not found for MutationObserver.'); return; } const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.type === 'childList' && mutation.addedNodes.length > 0) { // console.log('Table content changed, appending ID to Field Position column.'); appendIDToFieldPosition(); } }); }); observer.observe(tableBody, { childList: true, subtree: true }); console.log('MutationObserver set up successfully.'); } function addCssIfNotExists(url) { const urlWithoutQuery = url.split('?')[0]; const links = document.head.getElementsByTagName('link'); for (let i = 0; i < links.length; i++) { const linkHrefWithoutQuery = links[i].href.split('?')[0]; if (linkHrefWithoutQuery.includes(urlWithoutQuery)) { return; } } const linkElement = document.createElement('link'); linkElement.rel = 'stylesheet'; linkElement.href = url; document.head.appendChild(linkElement); } function checkPathhash(fullPath) { if (fullPath.includes('/spa/workflow/static4engine/engine.html#/main/workflowengine/form/selectList')) { antd.message.info("点击你想查看id的 选择框 即可查看~",10); } } function addStyle(css) { const style = document.createElement('style'); style.textContent = css; document.head.appendChild(style); } window.addEventListener('load', function() { // console.log("ccccccccc"); var pathname = window.location.pathname; var hash = window.location.hash; var fullPath = pathname + hash; checkPathhash(fullPath); }); function initSecurityMonitorFeatures() { // console.log("aaaaaaaaa"); var tableContainer = (document.querySelector(".listTable")? document.querySelector(".listTable").parentElement:(document.querySelector("iframe").contentDocument.querySelector(".listTable")? document.querySelector("iframe").contentDocument.querySelector(".listTable").parentElement:null)); var messageDiv = document.createElement("div"); messageDiv.style.textAlign = "left"; messageDiv.style.marginBottom = "10px"; messageDiv.style.fontSize = "14px"; messageDiv.style.color = "#333"; messageDiv.style.display = "flex"; messageDiv.style.justifyContent = "space-between"; messageDiv.style.alignItems = "center"; messageDiv.innerHTML = '<strong>提示:点击可疑次数、可疑时间可进行排序——泛微ecology9-IT助手</strong>'; tableContainer.parentElement.insertBefore(messageDiv, tableContainer); var checkboxContainer = document.createElement("div"); checkboxContainer.style.display = "flex"; checkboxContainer.style.gap = "10px"; messageDiv.appendChild(checkboxContainer); var hideIPCheckbox = createCheckbox("hideIPCheckbox", "隐藏已禁止IP", true); checkboxContainer.appendChild(hideIPCheckbox); var hideChinaIPCheckbox = createCheckbox("hideChinaIPCheckbox", "隐藏中国IP", true); checkboxContainer.appendChild(hideChinaIPCheckbox); var table = document.querySelector(".listTable")?document.querySelector(".listTable"):document.querySelector("iframe").contentDocument.querySelector(".listTable"); var headers = table.querySelectorAll("th"); var tbody = table.querySelector("tbody"); addSortFunction(headers[2], tbody, 3, 'number'); addSortFunction(headers[3], tbody, 4, 'date'); function toggleIPs() { var rows = tbody.querySelectorAll("tr"); rows.forEach(function(row) { var ipCell = row.querySelector("td:first-child span"); var locationCell = row.querySelector("td:nth-child(2)"); var isBanned = ipCell.style.backgroundColor === "rgb(240, 87, 87)"; var isChina = locationCell.textContent.includes("中国"); if ((isBanned && hideIPCheckbox.querySelector("input").checked) || (isChina && hideChinaIPCheckbox.querySelector("input").checked)) { row.style.display = "none"; } else { row.style.display = ""; } }); } hideIPCheckbox.querySelector("input").addEventListener("change", toggleIPs); hideChinaIPCheckbox.querySelector("input").addEventListener("change", toggleIPs); toggleIPs(); headers[2].click(); } function createCheckbox(id, labelText, checked) { var container = document.createElement("div"); var checkbox = document.createElement("input"); checkbox.type = "checkbox"; checkbox.id = id; checkbox.checked = checked; var label = document.createElement("label"); label.htmlFor = id; label.appendChild(document.createTextNode(labelText)); container.appendChild(checkbox); container.appendChild(label); return container; } function addSortFunction(header, tbody, colIndex, type) { header.style.cursor = "pointer"; header.addEventListener("click", function() { var rows = Array.from(tbody.querySelectorAll("tr")); var isAscending = header.classList.contains("asc"); rows.sort(function(a, b) { var aValue = getValue(a, colIndex, type); var bValue = getValue(b, colIndex, type); return isAscending ? aValue - bValue : bValue - aValue; }); tbody.innerHTML = ""; rows.forEach(function(row) { tbody.appendChild(row); }); header.classList.toggle("asc", !isAscending); header.classList.toggle("desc", isAscending); }); } function getValue(row, colIndex, type) { var cellValue = row.querySelector(`td:nth-child(${colIndex})`).innerText.trim(); if (type === 'number') { return parseInt(cellValue); } else if (type === 'date') { return new Date(cellValue).getTime(); } } function interceptNetworkRequests() { const originalXHROpen = XMLHttpRequest.prototype.open; XMLHttpRequest.prototype.open = function(method, url, ...args) { this.addEventListener('readystatechange', function() { // console.log("XHR",method,url); if (this.readyState === 4 && this.status === 200 && url.includes('/api/ec/dev/table/datas')) { const dataKey = new URLSearchParams(this.responseURL).get('dataKey'); if (dataKey) { displayDebugButton(dataKey); } } }); return originalXHROpen.apply(this, [method, url, ...args]); }; const originalFetch = window.fetch; window.fetch = function(resource, config) { // console.log("aabbb",resource,"bbaaa",config); if (config && config.method === 'POST' && config.headers) { const params = new URLSearchParams(config.body); const dataKey = params.get('dataKey'); if (dataKey) { displayDebugButton(dataKey); } } return originalFetch.apply(this, arguments) .then(response => { // console.log("response",response); if (response.url.includes('/api/ec/dev/table/datas') && response.status === 200) { response.clone().text().then(text => { const params = new URLSearchParams(text); const dataKey = params.get('dataKey'); if (dataKey) { displayDebugButton(dataKey); } }); }else if (response.url.includes('/api/workflow/formManage/publicselect/editselectItem') && response.status === 200) { response.clone().json().then(data => { displayTable(data.datas); }).catch(e => { console.error('Failed to parse response JSON', e); }); } return response; }); }; } function displayDebugButton(dataKey) { const debugButton = document.getElementById('debugButton'); debugButton.onclick = () => window.open(`/api/ec/dev/table/getxml?dataKey=${dataKey}`, '_blank'); debugButton.style.backgroundColor = '#2196F3'; debugButton.style.cursor = 'pointer'; let count = 0; const blinkInterval = setInterval(() => { debugButton.style.backgroundColor = count % 2 === 0 ? 'gray' : '#2196F3'; count++; if (count >= 10) { clearInterval(blinkInterval); } }, 400); setTimeout(() => { debugButton.onclick = null; debugButton.style.cursor = 'not-allowed'; debugButton.style.backgroundColor = 'gray'; }, 10000); } function makeDraggable(element) { var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; if (document.getElementById(element.id + "header")) { document.getElementById(element.id + "header").onmousedown = dragMouseDown; } else { element.onmousedown = dragMouseDown; } function dragMouseDown(e) { e = e || window.event; e.preventDefault(); pos3 = e.clientX; pos4 = e.clientY; document.onmouseup = closeDragElement; document.onmousemove = elementDrag; } function elementDrag(e) { e = e || window.event; e.preventDefault(); pos1 = pos3 - e.clientX; pos2 = pos4 - e.clientY; pos3 = e.clientX; pos4 = e.clientY; element.style.top = (element.offsetTop - pos2) + "px"; element.style.left = (element.offsetLeft - pos1) + "px"; } function closeDragElement() { document.onmouseup = null; document.onmousemove = null; } } function displayTable(datas) { if (!Array.isArray(datas)) { console.error('Expected an array of datas'); return; } const datasCopy = datas.slice(); const sortedById = datasCopy.sort((a, b) => a.id - b.id); const idToSid = {}; sortedById.forEach((data, index) => { idToSid[data.id] = index; }); const container = document.createElement('div'); container.className = 'onestool-draggable'; container.id = 'draggableContainer'; const closeButton = document.createElement('div'); closeButton.textContent = 'X'; closeButton.className = 'onestool-close'; closeButton.onclick = function() { container.remove(); }; container.appendChild(closeButton); const table = document.createElement('table'); table.className = 'onestool-table'; container.appendChild(table); const header = table.createTHead(); const headerRow = header.insertRow(); const headers = ['id', 'sid', 'optiontext', 'canel']; headers.forEach(text => { const cell = document.createElement('th'); cell.textContent = text; headerRow.appendChild(cell); }); const tbody = table.createTBody(); datas.forEach(data => { const row = tbody.insertRow(); headers.forEach(key => { const cell = row.insertCell(); cell.textContent = key === 'sid' ? idToSid[data.id] : data[key]; }); }); document.body.appendChild(container); makeDraggable(container); } function createButton(text, onClick) { const button = document.createElement('button'); button.textContent = text; button.onclick = onClick; Object.assign(button.style, { width: '100%', padding: '10px', marginBottom: '10px', backgroundColor: '#4CAF50', color: 'white', border: 'none', borderRadius: '5px', cursor: 'pointer' }); return button; } function createIconLibraryButton() { const button = document.createElement('button'); button.textContent = '图标库'; Object.assign(button.style, { width: '100%', padding: '10px', marginBottom: '10px', backgroundColor: '#4CAF50', color: 'white', border: 'none', borderRadius: '5px', cursor: 'pointer' }); button.addEventListener('click', showIcons); return button; } function showIcons() { const modal = document.createElement('div'); modal.className = 'onestool-icon-modal'; const tabContainer = document.createElement('div'); tabContainer.className = 'onestool-tab-container'; const weviconTab = createTabButton('Wevicon', true); const ecComiconTab = createTabButton('EcComicon', false); tabContainer.appendChild(weviconTab); tabContainer.appendChild(ecComiconTab); const iconContainer = document.createElement('div'); iconContainer.id = 'icon-container'; modal.appendChild(tabContainer); modal.appendChild(iconContainer); const closeButton = document.createElement('button'); closeButton.textContent = '关闭'; closeButton.className = 'onestool-close-button'; closeButton.addEventListener('click', () => document.body.removeChild(modal)); modal.appendChild(closeButton); document.body.appendChild(modal); loadIcons('wevicon'); weviconTab.addEventListener('click', () => { setActiveTab(weviconTab, ecComiconTab); loadIcons('wevicon'); }); ecComiconTab.addEventListener('click', () => { setActiveTab(ecComiconTab, weviconTab); loadIcons('ecComicon'); }); } function createTabButton(text, isActive) { const button = document.createElement('button'); button.textContent = text; button.className = `onestool-tab-button ${isActive ? 'active' : ''}`; return button; } function setActiveTab(activeTab, inactiveTab) { activeTab.classList.add('active'); inactiveTab.classList.remove('active'); } function loadIcons(iconType) { const url = iconType === 'wevicon' ? '/spa/theme/static/index.css' : '/cloudstore/resource/pc/com/v1/ecCom.min.css'; const iconRegex = iconType === 'wevicon' ? /\.wevicon-[^:]+:before\s*\{/g : /\.icon-[a-zA-Z0-9-]+:before\s*\{[^\}]*content\s*:\s*"\\[a-fA-F0-9]+"[^\}]*\}/g; fetch(url) .then(response => response.text()) .then(css => { let match; let iconsHtml = '<div class="onestool-icon-grid">'; while ((match = iconRegex.exec(css)) !== null) { const iconClass = match[0].split(':')[0].slice(1); iconsHtml += ` <div class="onestool-icon-item"> <i class="${iconClass} onestool-con-display"></i> <div class="onestool-icon-name">${iconClass.replace(iconType === 'wevicon' ? 'wevicon-' : 'icon-', '')}</div> </div> `; } iconsHtml += '</div>'; document.getElementById('icon-container').innerHTML = iconsHtml; }) .catch(error => { console.error('Failed to fetch icon CSS:', error); alert('Failed to load icon definitions.'); }); } function createInfoCard(userInfo, workflowInfo, modeInfo) { const card = document.createElement('div'); Object.assign(card.style, { position: 'fixed', bottom: '100px', right: '10px', width: '300px', padding: '20px', backgroundColor: '#f9f9f9', boxShadow: '0 4px 8px rgba(0, 0, 0, 0.1)', borderRadius: '10px', zIndex: '1000' }); const createSection = (title, data) => { const section = document.createElement('div'); const sectionTitle = document.createElement('h3'); sectionTitle.textContent = title; section.appendChild(sectionTitle); const sectionContent = document.createElement('div'); sectionContent.style.overflowY = 'auto'; if (data) { for (const [key, value] of Object.entries(data)) { const infoItem = document.createElement('p'); infoItem.textContent = `${key}: ${value}`; sectionContent.appendChild(infoItem); } } else { const infoItem = document.createElement('p'); infoItem.textContent = '无可用信息'; sectionContent.appendChild(infoItem); } section.appendChild(sectionContent); return section; }; if (userInfo) { card.appendChild(createSection('用户信息', { '部门ID.deptid': userInfo.deptid, '部门名称.deptname': userInfo.deptname, '分部ID.subcompanyid': userInfo.subcompanyid, '分部名称.subcompanyname': userInfo.subcompanyname, '用户语言.userLanguage': userInfo.userLanguage, '用户ID.userid': userInfo.userid, '用户名.username': userInfo.username })); } if (workflowInfo) { card.appendChild(createSection('流程信息', { '请求ID.requestid': workflowInfo.requestid, '流程ID.workflowid': workflowInfo.workflowid, '节点ID.nodeid': workflowInfo.nodeid, '表单ID.formid': workflowInfo.formid })); } if (modeInfo) { card.appendChild(createSection('建模信息', { '数据ID.billid': modeInfo.billid, '显示类型.type': modeInfo.type, '模块ID.modeId': modeInfo.modeId, '表单ID.formId': modeInfo.formId, '模块名称.modeName': modeInfo.modeName, '模块标题.modeTitle': modeInfo.modeTitle })); } if (!userInfo && !workflowInfo && !modeInfo) { card.appendChild(createSection('无可用信息', null)); } const closeButton = document.createElement('button'); closeButton.textContent = '关闭'; Object.assign(closeButton.style, { width: '100%', padding: '10px', backgroundColor: '#f44336', color: 'white', border: 'none', borderRadius: '5px', cursor: 'pointer', marginTop: '10px' }); closeButton.onclick = () => { document.body.removeChild(card); }; card.appendChild(closeButton); document.body.appendChild(card); } function main() { addCssIfNotExists('/spa/theme/static/index.css'); addCssIfNotExists('/cloudstore/resource/pc/com/v1/ecCom.min.css'); const togglecard = document.createElement('div'); Object.assign(togglecard.style, { position: 'fixed', bottom: '50px', right: '10px', width: '200px', padding: '20px', backgroundColor: '#f9f9f9', boxShadow: '0 4px 8px rgba(0, 0, 0, 0.1)', borderRadius: '10px', zIndex: '1000', display: 'none' }); if (config.ecode) { togglecard.appendChild(createButton('打开ecode', () => window.open('/ecode', '_blank'))); } if (config.mobilePage) { togglecard.appendChild(createButton('打开移动端', () => window.open('/spa/portal/static4mobilelogin/index.html', '_blank'))); } if (config.securityMonitor) { togglecard.appendChild(createButton('安全包监控', () => window.open('/security/monitor/Monitor.jsp', '_blank'))); } if (config.cacheMonitor) { togglecard.appendChild(createButton('SQL缓存管理', () => window.open('/commcache/cacheMonitor.jsp', '_blank'))); } if (config.selectList) { togglecard.appendChild(createButton('公共选框ID', () => window.open('/spa/workflow/static4engine/engine.html#/main/workflowengine/form/selectList', '_blank'))); } if (config.engineerNavigation) { togglecard.appendChild(createButton('工程师导航', () => window.open('https://huahan.cc', '_blank'))); } if (config.dataDictionary) { togglecard.appendChild(createButton('数据字典', () => window.open('https://oneszhang.com/E9Sql/', '_blank'))); } if (config.iconLibrary) { togglecard.appendChild(createIconLibraryButton()); } if (config.goFeedback) { togglecard.appendChild(createButton('留言反馈', () => window.open('https://oneszhang.com/onestool.html', '_blank'))); } if (window.location.pathname.includes('/security/monitor')) { if (document.getElementById('updateRulesForm')) { initSecurityMonitorFeatures(); } if (typeof window.resetbanner === 'function') { const originalResetBanner = window.resetbanner; window.resetbanner = function(objid) { originalResetBanner(objid); if (objid === 4) { const iframe = document.getElementById('iframeAlert'); iframe.addEventListener('load', function() { const iframeDoc = iframe.contentDocument || iframe.contentWindow.document; if (iframeDoc.querySelector('.listTable')) { initSecurityMonitorFeatures(); } }); } }; } } const allButtonContainer = document.createElement('div'); Object.assign(allButtonContainer.style, { position: 'fixed', bottom: '10px', right: '10px', display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: '1001' }); const toggleButton = document.createElement('div'); const toggleiconElement = document.createElement('i'); toggleiconElement.className = 'icon-New-Flow-menu'; toggleButton.appendChild(toggleiconElement); Object.assign(toggleButton.style, { width: '30px', height: '30px', backgroundColor: '#4CAF50', color: 'white', display: 'flex', alignItems: 'center', justifyContent: 'center', borderRadius: '50%', cursor: 'pointer', marginLeft: '10px' }); toggleButton.onclick = () => togglecard.style.display = togglecard.style.display === 'none' ? 'block' : 'none'; const debugButton = document.createElement('div'); const debugiconElement = document.createElement('i'); debugiconElement.className = 'wevicon-menu-2--5731'; debugButton.appendChild(debugiconElement); debugButton.id = 'debugButton'; Object.assign(debugButton.style, { width: '30px', height: '30px', backgroundColor: 'gray', color: 'white', display: 'flex', alignItems: 'center', justifyContent: 'center', borderRadius: '50%', cursor: 'not-allowed', marginLeft: '10px' }); const infoButton = document.createElement('div'); const infoiconElement = document.createElement('i'); infoiconElement.className = 'icon-coms02-Version'; infoButton.appendChild(infoiconElement); infoButton.id = 'infoButton'; Object.assign(infoButton.style, { width: '30px', height: '30px', backgroundColor: '#14b8a6', color: 'white', display: 'flex', alignItems: 'center', justifyContent: 'center', borderRadius: '50%', cursor: 'pointer', marginLeft: '10px' }); infoButton.onclick = () => { const userInfo = JSON.parse(localStorage.getItem('theme-account')); const workflowInfo = (typeof wfform !== 'undefined' && wfform !== null && typeof wfform.getBaseInfo === 'function') ? wfform.getBaseInfo() : null; const modeInfo = (typeof ModeForm !== 'undefined' && ModeForm !== null && typeof ModeForm.getCardUrlInfo === 'function') ? ModeForm.getCardUrlInfo() : null; createInfoCard(userInfo, workflowInfo, modeInfo); }; allButtonContainer.appendChild(debugButton); allButtonContainer.appendChild(infoButton); allButtonContainer.appendChild(toggleButton); document.body.appendChild(togglecard); document.body.appendChild(allButtonContainer); interceptNetworkRequests(); } addStyle(` .onestool-icon-button { position: fixed; left: 10px; bottom: 10px; z-index: 9999; padding: 10px 15px; background-color: #4CAF50; color: white; border: none; border-radius: 5px; cursor: pointer; font-size: 14px; transition: background-color 0.3s; } .onestool-icon-button:hover { background-color: #45a049; } .onestool-icon-modal { position: fixed; left: 50%; top: 50%; transform: translate(-50%, -50%); background-color: white; padding: 20px; border-radius: 8px; box-shadow: 0 4px 20px rgba(0,0,0,0.2); width: 90%; height: 90%; overflow: auto; z-index: 10000; } .onestool-icon-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(80px, 1fr)); gap: 10px; } .onestool-icon-item { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 5px; border: 1px solid #e0e0e0; border-radius: 5px; transition: box-shadow 0.3s; height: 80px; } .onestool-icon-item:hover { box-shadow: 0 2px 10px rgba(0,0,0,0.1); } .onestool-con-display { font-size: 24px; margin-bottom: 5px; } .onestool-icon-name { font-size: 10px; text-align: center; word-break: break-all; overflow: hidden; text-overflow: ellipsis; width: 100%; white-space: nowrap; } .onestool-close-button { position: absolute; right: 10px; top: 10px; background-color: #f44336; color: white; border: none; padding: 5px 10px; border-radius: 3px; cursor: pointer; transition: background-color 0.3s; } .onestool-close-button:hover { background-color: #d32f2f; } .onestool-tab-container { display: flex; justify-content: center; margin-bottom: 20px; } .onestool-tab-button { padding: 10px 20px; background-color: #f0f0f0; border: none; cursor: pointer; transition: background-color 0.3s; } .onestool-tab-button:hover { background-color: #e0e0e0; } .onestool-tab-button.active { background-color: #4CAF50; color: white; } .onestool-table { border-collapse: collapse; width: 100%; } .onestool-table, .onestool-table th, .onestool-table td { border: 1px solid black; padding: 5px; } .onestool-draggable { position: absolute; top: 10%; left: 10%; width: 80%; background: white; border: 1px solid black; z-index: 10000; } .onestool-close { cursor: pointer; position: absolute; top: 0; right: 0; padding: 5px 10px; } `); if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', main); } else { main(); } })();