您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
try to take over the world!
// ==UserScript== // @name jira任务板 // @namespace http://tampermonkey.net/ // @version 0.1 // @description try to take over the world! // @author You // @require https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js // @require https://cdn.bootcss.com/lodash.js/4.17.15/lodash.min.js // @require https://cdn.bootcss.com/moment.js/2.24.0/moment.min.js // @match http://jira.dotfashion.cn/* // @grant GM_addStyle // ==/UserScript== (function() { 'use strict'; const pmNameKey = 'customfield_10300' const frontEndConfigOptions = [ { title: '前端开始日期', key: 'customfield_11811', }, { title: '前端结束日期', key: 'customfield_11788', }, ] const issueConfigOptions = [ { title: '联调日期', key: 'customfield_12557', }, { title: '测试开始日期', key: 'customfield_11806', }, { title: '测试结束日期', key: 'customfield_11792', }, { title: '验收日期', key: 'customfield_11790', }, { title: '计划上线日期', key: 'customfield_10203', }, { title: '实际上线日期', key: 'customfield_10813', }, ] const columns = [ { title: '主需求号', key: 'linkKey', render: value => `<a href="http://jira.dotfashion.cn/browse/${value}" target="_blank">${value || ''}</a>`, }, { title: '个人需求号', key: 'key', render: value => `<a href="http://jira.dotfashion.cn/browse/${value}" target="_blank">${value || ''}</a>`, }, { title: '需求摘要', key: 'linkSummary', render: (value, item) => value || item.summary || '', }, { title: '产品经理', key: 'pmName', }, ].concat(frontEndConfigOptions.concat(issueConfigOptions).map(item => Object.assign({}, item, { class: value => { if (value) { const diff = moment(moment().format('YYYY-MM-DD')) - moment(value) const className = diff > 0 ? 'jira-m-done' : diff === 0 ? 'jira-m-active' : 'jira-m-wait' return className } return '' }, }))) const tableTitleTemp = $(`<tr class="table-title">` + columns.map(item => `<th class="table-item">${item.title}</th>`).join('') + `</tr>`) $(document).ready(function() { GM_addStyle(` .jira-m-wait { background-color: #dfe1e5; color: #42526e; } .jira-m-active { background: #0052cc; color: #fff; } .jira-m-done { background-color: #e3fcef; color: #064; } .jira-panel-icon { position: fixed; z-index: 101; left: 40px; bottom: 40px; width: 80px; height: 80px; border-radius: 50%; background: #0747a6; color: #fff; display: none; align-items: center; justify-content: center; cursor: pointer; } .jira-panel-container { position: fixed; width: 100%; height: 100%; left: 0; top: 0; z-index: 100; display: none } .jira-panel-mask { position: absolute; z-index: -1; width: 100%; height: 100%; left: 0; top: 0; background: rgba(0,0,0,.33); } .jira-panel-modal-wrap { width: 100%; height: 100%; overflow: auto; } .jira-panel-modal { width: 90%; margin: 50px auto; background: #fff; border: 1px solid #cecece; border-radius: 8px; } .jira-panel-modal-title { text-indent: 1em; line-height: 2.8em; border-bottom: 1px solid #cecece; } .jira-panel-modal-body { padding: 35px; } .jira-panel-modal-body-content { overflow: auto; max-height: 400px; } .jira-panel-modal-table .table-item { min-width: 60px; max-width: 250px; padding: 8px; line-height: 2em; text-align: center; } .jira-panel-modal-table .table-title { font-weight: 600; background: rgba(0,0,0,.15); } `) const panelIconTemp = $(`<div class="jira-panel-icon" id="panelIcon">JIRA面板</div>`) const panelContainerTemp = $(` <div class="jira-panel-container"> <div class="jira-panel-mask" /> <div class="jira-panel-modal-wrap"> <div class="jira-panel-modal"> <div class="jira-panel-modal-title">JIRA任务-${moment().format('YYYY-MM-DD')}</div> <div class="jira-panel-modal-body"> <div class="jira-panel-modal-body-content"> <table class="jira-panel-modal-table" id="panelTable" border="1" cellspacing="0" /> </div> </div> </div> </div> </div> `) $('body').append(panelIconTemp) $('body').append(panelContainerTemp) panelIconTemp.click(() => panelContainerTemp.slideToggle()) fetch('/rest/api/2/myself') .then(res => res.json()) .then(res => { const jql = encodeURIComponent(`project = MLB AND assignee in (${res.name}) order by created DESC`) return fetch(`/rest/api/2/search?jql=${jql}`) }) .then(res => res.json()) .then(res => { const data = res.issues.map(item => { const { key, fields } = item const frontEndConfig = _.pick(fields, frontEndConfigOptions.map(item => item.key)) const linkOptions = fields.issuelinks.length ? fields.issuelinks[0].outwardIssue : {} return Object.assign({}, { key, summary: fields.summary, linkKey: linkOptions.key, linkSummary: linkOptions.fields && linkOptions.fields.summary, linkApi: linkOptions.self, }, frontEndConfig) }) return Promise.all(data.map(item => { if (item.linkApi) { return fetch(item.linkApi).then(res => res.json()) } return Promise.resolve(null) })).then(res => data.map((item, index) => { const fields = res[index] ? res[index].fields : {} const pmName = fields[pmNameKey] && fields[pmNameKey].name return Object.assign({ pmName }, item, _.pick(fields, issueConfigOptions.map(child => child.key))) })) }).then(res => { const rowHtmlStr = res.map(item => { const childs = columns.map(sub => { const keyValue = item[sub.key] const value = typeof sub.render === 'function' ? sub.render(keyValue, item) : keyValue const className = typeof sub.class === 'function' ? ` ${sub.class(keyValue, item)}` : '' return `<td class="table-item${className}">${value || ''}</td>` }) return `<tr class="table-row">${childs.join('')}</tr>` }).join('') $('#panelTable').append(tableTitleTemp) $('#panelTable').append($(rowHtmlStr)) $('#panelIcon').css('display', 'flex').fadeIn() }) }); })();