您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
hack pingcode
当前为
// ==UserScript== // @name pingcodeHelper // @namespace http://tampermonkey.net/ // @version 3.11 // @description hack pingcode // @author Amos // @match https://onetoken.pingcode.com/* // @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw== // @grant GM_registerMenuCommand // ==/UserScript== /* globals jQuery, $, waitForKeyElements */ GM_registerMenuCommand('Archive tasks done before 2 months', function() { get_work_items().then(items=>{ let longSetTasks=[] for(let item of items){ if(item.state_type===3||item.state_type===4){ // console.log('checking',(Date.now()-item.updated_at*1000)/24/3600000,item) if(Date.now()-item.updated_at*1000>24*60*3600*1000){ longSetTasks.push(item) } } } console.log(longSetTasks) // let toArchiveItems=longSetTasks.slice(0,2) // console.log(toArchiveItems) archiveItems(longSetTasks).then(()=>{ alert(`共归档${longSetTasks.length}个任务`) }).catch(e=>{ alert(e) }) }) }, 'r'); function get_work_items(pageSize=1000,pageIndex=0){ return new Promise((resolve,reject)=>{ $.ajax({ url: 'https://onetoken.pingcode.com/api/agile/work-items', dataType: "json", data: {ps:pageSize,pi:pageIndex}, async: true, cache: false, timeout: 30000, success: (res)=> { if(res.code===200){ let data=res.data let dataList=data.value if(data.page_index<data.page_count-1){ get_work_items(pageSize,pageIndex+1).then(items=>{ let concatList=dataList.concat(items) resolve(concatList) }) } else { resolve(dataList) } } else{ reject() } }, error: (request, status, error)=> { reject(error) }, type: "GET" }); }) } function archiveItems(items,index=0){ return new Promise((resolve,reject)=>{ if(items.length===0){ resolve() return } let item=items[index] $.ajax({ url: `https://onetoken.pingcode.com/api/agile/work-items/${item._id}/archive`, dataType: "json", data: {}, async: true, cache: false, timeout: 10000, success: function (data) { if(index<items.length-1){ archiveItems(items,index+1).then(res=>{ resolve() }) } else { resolve() } }, error: function (request, status, error) { reject(index) }, type: "PUT" }); }) } // $.ajax({ // url: 'https://onetoken.pingcode.com/api/agile/work-items/60e29290793b014b8ffbdeba/archive', // dataType: "json", // data: {}, // async: true, // cache: false, // timeout: 30000, // success: function (data) { // // my success stuff // }, // error: function (request, status, error) { // // my error stuff // }, // type: "PUT" // }); var pageURLCheckTimer = setInterval( function () { if (this.lastPathStr !== location.pathname || this.lastQueryStr !== location.search || this.lastPathStr === null || this.lastQueryStr === null ) { this.lastPathStr = location.pathname; this.lastQueryStr = location.search; gmMain(); } }, 222 ); function gmMain() { setTimeout(function () { let x = document.querySelector("#app-host-container > app-agile-root > app-agile-actual-root > agile-global > agile-global-query-detail > thy-header > div.layout-header-content > div > thy-nav > a.styx-secondary-nav-link.nav-link > span") if(x){ console.log(x); console.log(x.textContent.trim()); document.title = x.textContent.trim(); } }, 3000); } function refreshUnread(){ let authorization = localStorage.getItem('authorization') if(authorization){ fetch('https://iris.pingcode.com/api/iris/notifications/n-unreads?t=1636023609632',{headers:{authorization: authorization}}) .then(res => res.json()) .then(res=>{ if(res.code===200){ let unreadSize=res.data.value let currentTitle=document.head.querySelector('title').textContent if(currentTitle.startsWith('(')){ let endIndex = currentTitle.indexOf(')') if(endIndex>=0){ let newTitle=`(${unreadSize})${currentTitle.substring(endIndex+1)}` document.head.querySelector('title').textContent=newTitle } } } }) } } function hideYearJump(){ let prevYear=document.querySelector(".thy-calendar-prev-year-btn") let nextYear=document.querySelector(".thy-calendar-next-year-btn") if(prevYear){ prevYear.style.display='none' } if(nextYear){ nextYear.style.display='none' } } function bestEffortUUID() { let ts = new Date().getTime() let hexDigits = '0123456789abcdef' let uuid = ts + '-' for (let i = 0; i < 8; i++) { uuid += hexDigits.substr(Math.floor(Math.random() * 0x10), 1) } return uuid } (function() { 'use strict'; // let showFinished=true //let btn=null let cnt=0 changePriorityWidth() setInterval(()=>{ shortcutContainerAdjustment() addHideChildrenLogic() cnt++ if(cnt%60===0){ refreshUnread() } addArchiveChildrenLogic() hideYearJump() addCreateChildrenLogic() },1000) // type 4 -task // https://onetoken.pingcode.com/api/agile/work-item POST // due: {date: 1647964799, with_time: 0} function createSubTask(parentId,projectId,title,type,due){ let data = {parent_id:parentId,project_id:projectId,title,due,type} $.ajax({ url: `https://onetoken.pingcode.com/api/agile/work-item`, dataType: "json", contentType: "application/json; charset=utf-8", data:JSON.stringify(data), async: true, cache: false, timeout: 30000, success: (res)=> { }, error: (request, status, error)=> { console.log(error) }, type: "POST" }); } function addCreateBtn(agileDetail,id){ if(agileDetail){ let itemListParent = agileDetail.querySelector('.sub-work-item-list') if(itemListParent){ console.log('adding btn') if(agileDetail.querySelector('#createChildrenBtn')===null){ let obj={btn:null,showFinished:true} obj.btn = document.createElement('div') obj.btn.textContent='批量创建子任务' obj.btn.id='createChildrenBtn' obj.btn.style='color:#aaa;cursor:pointer' agileDetail.insertBefore(obj.btn, agileDetail.firstChild) obj.btn.addEventListener('click',()=>{ $.ajax({ url: `https://onetoken.pingcode.com/api/agile/work-items/${id}`, dataType: "json", async: true, cache: false, timeout: 30000, success: (res)=> { if(res.code===200){ let data=res.data let itemData=data.value console.log(itemData) createSubTask(id,itemData.project_id,'产品方案',4,itemData.due) createSubTask(id,itemData.project_id,'前端开发',4,itemData.due) createSubTask(id,itemData.project_id,'后端开发',4,itemData.due) createSubTask(id,itemData.project_id,'测试',4,itemData.due) createSubTask(id,itemData.project_id,'产品验收',4,itemData.due) alert('已批量生成子任务,请刷新后查看') } else{ console.log('code is not 200, but',res.code) } }, error: (request, status, error)=> { console.log(error) }, type: "GET" }); }) } } } } function getAgileDetail(){ let allAgileDetails = document.querySelectorAll('.agile-work-item-detail-work-item') let agileDetail=null if(allAgileDetails.length>0){ agileDetail=allAgileDetails[allAgileDetails.length-1] } else{ let allAgileDetailChildren = document.querySelectorAll('.agile-work-item-detail-children') if(allAgileDetailChildren.length>0){ agileDetail=allAgileDetailChildren[allAgileDetailChildren.length-1] } } return agileDetail } function addCreateChildrenLogic(){ console.log('start add create children logic') let agileDetail=getAgileDetail() if(agileDetail===null||agileDetail.querySelector('#createChildrenBtn')!==null){ console.log('btn is not Null',agileDetail,agileDetail?agileDetail.querySelector('#createChildrenBtn'):null) return } let containers = document.querySelectorAll('.thy-dialog-container') if(containers.length>0){ let currentContainer = containers[containers.length-1] let statusEl = currentContainer.querySelector('.thy-label-emboss-status') if(statusEl){ let taskType=statusEl .querySelector('.font-size-sm')?.innerHTML if(taskType!=='用户故事'){ return } if(!currentContainer.querySelector('thy-list-item')){ console.log('hasNoChild') let currentId = currentContainer.id.split('-')[0] addCreateBtn(agileDetail,currentId) } } } } function addArchiveChildrenLogic(){ let agileDetail=getAgileDetail() if(agileDetail===null||agileDetail.querySelector('#archiveBtn')!==null){ return } let containers = document.querySelectorAll('.thy-dialog-container') if(containers.length>0){ let currentContainer = containers[containers.length-1] let currentId = currentContainer.id.split('-')[0] $.ajax({ url: `https://onetoken.pingcode.com/api/agile/work-items/${currentId}`, dataType: "json", async: true, cache: false, timeout: 30000, success: (res)=> { if(res.code===200){ let data=res.data let itemData=data.value if(itemData.child_ids.length>=40){ addArchiveBtn(itemData.child_ids,agileDetail,15) } else if(itemData.child_ids.length>=15){ addArchiveBtn(itemData.child_ids,agileDetail,30) } } else{ console.log('code is not 200, but',res.code) } }, error: (request, status, error)=> { console.log(error) }, type: "GET" }); } } function dateFormat(fmt, date) { let ret; const opt = { "Y+": date.getFullYear().toString(), // 年 "m+": (date.getMonth() + 1).toString(), // 月 "d+": date.getDate().toString(), // 日 "H+": date.getHours().toString(), // 时 "M+": date.getMinutes().toString(), // 分 "S+": date.getSeconds().toString() // 秒 }; for (let k in opt) { ret = new RegExp("(" + k + ")").exec(fmt); if (ret) { fmt = fmt.replace(ret[1], (ret[1].length == 1) ? (opt[k]) : (opt[k].padStart(ret[1].length, "0"))) }; }; return fmt; } function addArchiveBtn(idList,agileDetail,cutoffDays){ if(agileDetail){ let itemListParent = agileDetail.querySelector('.sub-work-item-list') if(itemListParent){ let itemList = itemListParent.querySelectorAll('.work-items-list-item') if(itemList.length>0){ console.log('adding btn') if(agileDetail.querySelector('#archiveBtn')===null){ let obj={btn:null,showFinished:true} obj.btn = document.createElement('div') obj.btn.textContent=`归档${cutoffDays}天前的子任务` obj.btn.id='archiveBtn' obj.btn.style='color:#aaa;cursor:pointer' agileDetail.insertBefore(obj.btn, agileDetail.firstChild) obj.btn.addEventListener('click',()=>{ getItemList(idList).then(workItems=>{ let toArchive = [] let cutoffTime = Date.now()-cutoffDays*24*3600*1000 workItems.forEach(item=>{ let isWorkEnd = item.state_type===3||item.state_type===4 let itemLastUpdated = item.updated_at*1000 if(isWorkEnd&&itemLastUpdated<cutoffTime){ toArchive.push(item) } }) if(toArchive.length>0){ showArchiveList(agileDetail,toArchive) } else { alert('无可归档任务') } }) }) } } } } } function showArchiveList(agileDetail,toArchive){ let popupWin = document.querySelector('#pingcode-archive-table') if(popupWin){ document.body.removeChild(popupWin) } popupWin = document.createElement('div') popupWin.style = "background-color:gray;position: absolute;top: 100px;left: 400px;z-index:10000;height:500px; overflow:auto;padding:10px;" let titleEl = document.createElement('h4') titleEl.innerText=`确定归档以下${toArchive.length}个任务?` popupWin.appendChild(titleEl) let notifyRoot = document.createElement('div') popupWin.appendChild(notifyRoot) notifyRoot.style="height:400px; overflow:auto;" let table = document.createElement('table') table.id = 'pingcode-archive-table' table.style='border:1px' table.cellspacing="0" table.cellpadding="1" table.border="1" let headerEl = document.createElement('tr') let idHeadEl = document.createElement('td') idHeadEl.innerText = '编号' headerEl.appendChild(idHeadEl) let titleHeadEl = document.createElement('td') titleHeadEl.innerText = '标题' headerEl.appendChild(titleHeadEl) let stateHeadEl = document.createElement('td') stateHeadEl.innerText = '状态' headerEl.appendChild(stateHeadEl) let lastUpdatedHeadEl = document.createElement('td') lastUpdatedHeadEl.innerText = '最后更新' headerEl.appendChild(lastUpdatedHeadEl) table.appendChild(headerEl) notifyRoot.appendChild(table) document.body.appendChild(popupWin) let btnDiv = document.createElement('div') btnDiv.style='margin-top:10px;' popupWin.appendChild(btnDiv) let confirmBtn = document.createElement('button') confirmBtn.innerText='确定' let cancelBtn = document.createElement('button') cancelBtn.style='margin-left:15px;' cancelBtn.innerText='取消' btnDiv.appendChild(confirmBtn) btnDiv.appendChild(cancelBtn) cancelBtn.addEventListener('click',()=>{ document.body.removeChild(popupWin) }) confirmBtn.addEventListener('click',()=>{ archiveItems(toArchive).then(()=>{ alert(`成功归档${toArchive.length}个任务`) }).catch(size=>{ alert(`成功归档${size}个任务,失败${toArchive.length-size}个`) }).finally(()=>{ let archiveBtn = agileDetail.querySelector('#archiveBtn') if(archiveBtn){ agileDetail.removeChild(archiveBtn) } }) // console.log(archiveItems) document.body.removeChild(popupWin) }) toArchive.forEach(item=>{ let trEl = document.createElement('tr') table.appendChild(trEl) let identifierEl = document.createElement('td') identifierEl.innerText = item.whole_identifier trEl.appendChild(identifierEl) let titleEl = document.createElement('td') titleEl.innerText = item.title trEl.appendChild(titleEl) let stateEl = document.createElement('td') stateEl.innerText = item.state_name trEl.appendChild(stateEl) let lastUpdatedEl = document.createElement('td') lastUpdatedEl.innerText = dateFormat('YYYY-mm-dd HH:MM',new Date(item.updated_at*1000)) trEl.appendChild(lastUpdatedEl) }) } function getItemList(idList){ let promises = [] idList.forEach(id=>{ promises.push(getWorkItem(id)) }) return Promise.all(promises) } function getWorkItem(id){ return new Promise((resolve,reject)=>{ $.ajax({ url: `https://onetoken.pingcode.com/api/agile/work-items/${id}`, dataType: "json", async: true, cache: false, timeout: 30000, success: (res)=> { if(res.code===200){ let data=res.data let itemData=data.value itemData.state_name = data.references.lookups.states[0].name resolve(itemData) } else{ reject() } }, error: (request, status, error)=> { reject(error) }, type: "GET" }); }) } function checkIfArchive(id,cutoffDays){ let cutoffTime = Date.now()-cutoffDays*24*3600*1000 $.ajax({ url: `https://onetoken.pingcode.com/api/agile/work-items/${id}`, dataType: "json", async: true, cache: false, timeout: 30000, success: (res)=> { if(res.code===200){ let data=res.data let itemData=data.value let isWorkEnd = itemData.state_type===3||itemData.state_type===4 let itemLastUpdated = itemData.updated_at*1000 if(isWorkEnd&&itemLastUpdated<cutoffTime){ $.ajax({ url: `https://onetoken.pingcode.com/api/agile/work-items/${id}/archive`, dataType: "json", data: {}, async: true, cache: false, timeout: 10000, success: function (data) { }, error: function (request, status, error) { console.log(error) }, type: "PUT" }); } else { console.log('skip',id) } } else{ console.log(res.code) } }, error: (request, status, error)=> { console.log(error) }, type: "GET" }); } function addHideChildrenLogic(){ let agileDetail = getAgileDetail() if(agileDetail){ let itemListParent = agileDetail.querySelector('.sub-work-item-list') if(itemListParent){ let itemList = itemListParent.querySelectorAll('.work-items-list-item') if(itemList.length>0){ if(agileDetail.querySelector('#displayBtn')===null){ let obj={btn:null,showFinished:true} obj.btn = document.createElement('span') obj.btn.textContent=obj.showFinished?'隐藏已完成':'显示已完成' obj.btn.id='displayBtn' obj.btn.style='color:#aaa;cursor:pointer' agileDetail.insertBefore(obj.btn, agileDetail.firstChild) obj.btn.addEventListener('click',()=>{ toggleShowFinished(agileDetail,obj) }) } } } } } function changePriorityWidth(){ var sheet = document.createElement('style') sheet.innerHTML = ".agile .agile-work-items-list-item-container .work-item-priority {width: 8px;}"; document.body.appendChild(sheet); } function isEnd(workItem){ let status=workItem.querySelector('.flexible-text-container').textContent return status==='关闭'||status==='已完成' } function shortcutContainerAdjustment(){ let shortcutContainer=document.querySelector('.shortcut-container') if(shortcutContainer){ let tableContent = document.querySelector('.styx-table-content') if(tableContent){ if(shortcutContainer.offsetHeight>0){ tableContent.style='margin-bottom:28px' } else{ tableContent.style='margin-bottom:0px' } } } } function toggleShowFinished(agileDetail,obj){ obj.showFinished=!obj.showFinished obj.btn.textContent=obj.showFinished?'隐藏已完成':'显示已完成' if(obj.showFinished){ let workItems=agileDetail.querySelectorAll('.work-item-info') for(let item of workItems){ item.parentElement.parentElement.style='display:block' } } else { let workItems=agileDetail.querySelectorAll('.work-item-info') for(let item of workItems){ let status=item.querySelector('.flexible-text-container').textContent if(status==='关闭'||status==='已完成'||status==='已修复'||status==='已拒绝'){ item.parentElement.parentElement.style='display:none' } } } } // Your code here... })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址