Microsoft TFS 助手开发文档

引言

引用

[fixed] BUG:20230104日历存在超出星期日的bug

每天写日报,感觉很烦躁,所以写了个helper
⚠️ 仅在TFS2013测试通过,因为公司用的就是这个版本

技术栈

  • html css js
  • jQuery (复用,无需引入)

功能

  • 获取当前月的日报情况,可以直观的看到哪天的日报没有写
  • 填写日报时,自动填写一些常规的文本
  • 简化项目选择器,通过简称赋值到项目框
  • 可以自由拖拽标题栏,以至移动TFS助手窗口的位置

截图

日报信息
这里的日报信息来源为页面表格中的信息,通过document.getElementsByClassName("tbTileContent");获取到所有人的日报信息,根据姓名筛选出自己的工作报告。

显示详细日志
当鼠标指向日报时,会更加详细的展示那天做了什么。

TFS助手缩小
TFS助手缩小时的样子,如果主要目的并不是查看日报历史信息,可以缩小,节省页面空间。

日报标题
本地日报

我们公司要求日报格式为【yyyymmdd-内容】所以分割起来就很容易了,我每天有本地记录日报的习惯,因此复制粘贴过来就好

项目选择
在代码中做好映射关系,简称和TFS中的项目关系,在这里输入简称即可

自动化填报
自动把一些改填写的填上,剩下的自己手动添加,或者修改代码,根据标题中的内容智能分析出技术栈

难点&解决

信息

浏览器的CORB(没错,并不是CORS)策略,以至于页面无法注入进来,最后是通过jQuery的load和ajax注入到页面中的。

这里需要一个本地的WEB服务器,nginx,iis,apacheHttp都可以,因为TFS助手只用到了原生的js,不需要高级语言的介入,而tfs中默认引入了jQuery,注入后的代码复用了tfs的jQuery。

上面的那段代码放到书签里,在tfs的页面中打开这个书签,就可以注入到页面中了。
当然, 注意端口号。

代码

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<style>
    .calendar {
        opacity: .99;
        z-index: 99999999999999;
        border: 2px solid #0066aa;
        position: absolute;
        display: inline-block;
        background-color: rgba(210, 210, 210, .8);
    }


    .t_head {
        border: 1px solid blue;
        background-color: #53ace7;
        color: white;
    }

    .t_body {
        z-index: 100;
    }

    .day_div {
        width: 80px;
        height: 80px;
    }

    .now_day_div {
        text-shadow: 0 0 10px black
    }

    .dayLog {
        z-index: 1000;
        display: block;
        overflow: hidden;
        white-space: nowrap;
        text-overflow: ellipsis;
    }

    .dayLog:hover {
        position: relative;
        z-index: 10000;
        color: yellow;
        font-weight: bold;
        background-color: rgba(0, 0, 0, .8);
        width: 150px;

        border: 3px solid black;
        overflow: visible;
        word-wrap: normal;
        text-overflow: auto;
        white-space: normal;
    }
</style>

<body>
    <div class="calendar">
        <div id='title_bar' style="width: 100%; height:25px; background-color: #007acc">
            <span style=" font-size:16px; margin-right:8px; cursor:pointer">TFS助手</span>
            <span style="float:right; font-size:18px; margin-right:8px; cursor:pointer" onclick="closeDialog()">x</span>
        </div>
        <div>
            <button onclick="getDayLog()">获取日报信息</button>
            <button onclick="displayCal()">隐藏/显示日历</button>
            <button onclick="setTFS()">自动化填报</button>
        </div>
        <table class="t_table" style=" display: none;" cellspacing="0" cellpadding="0" border="1">
            <tr>
                <th class="t_head">星期一</th>
                <th class="t_head">星期二</th>
                <th class="t_head">星期三</th>
                <th class="t_head">星期四</th>
                <th class="t_head">星期五</th>
                <th class="t_head">星期六</th>
                <th class="t_head">星期日</th>
            </tr>
            <tbody id="data-view">
                <tr id="line1"></tr>
                <tr id="line2"></tr>
                <tr id="line3"></tr>
                <tr id="line4"></tr>
                <tr id="line5"></tr>
                <tr id="line6"></tr>
            </tbody>
        </table>

        <div id="message" style="background-color: #333333;color:darkorange"></div>
    </div>
</body>
<script>
    try {
        $("body")
        console.log("[#] 存在jQuery")
    }
    catch (e) {
        console.log("[#] 不存在jQuery")
        var oHead = document.getElementsByTagName("HEAD").item(0);
        var oScript = document.createElement("script");
        oScript.type = "text/javascript";
        oScript.src = "dayLog.js";
        oHead.appendChild(oScript);
    }

</script>

</html>

<!--

最后的存在jQuery与否是为了判断是否在tfs页面,如果不在,就导入dayLog.js,如果存在,也就是在TFS页面中了,注入时不能使用script标签,也不能通过document.write()写入内容,因此只能出此下策。

let dialogMoveFlag = false;
let mousePointX = 0;
let yourName = "姓名"
let mousePointY = 0;
let cal = document.getElementById("data-view")
let point_day = 1;
let data = [
]
let now = new Date()
let now_day = now.getDate();
let template = '<td class="t_body "><div id="day{day}" class="day_div " style="text-align:center"><span >{day}<span></div></td>';
function message(text, code = 1) {
    if (code === 1) {
        document.getElementById("message").innerHTML = "[#] 信息:" + text;
    } else if (code === 0) {
        document.getElementById("message").innerHTML = "[x] 错误:" + text;
    }
}
function getCountDays() {
    let curDate = new Date(now);
    var curMonth = now.getMonth();
    curDate.setMonth(curMonth);
    curDate.setDate(0);
    return curDate.getDate();
}
function displayCal() {
    obj = document.getElementsByClassName("t_table")[0]
    if (obj.style.display == "none") {
        obj.style.display = "block"
        message("显示日历")
    } else {
        obj.style.display = "none"
        message("隐藏日历")
    }
}
function setCol() {
    let line = 1;
    let isFirst = true;
    let finalDayWeek = 0;
    if (isFirst) {
        let a = new Date(now.setDate(1)).getDay()
        if (a == 0) {
            for (let i = 0; i < 6; i++) {
                document.getElementById("line" + line).innerHTML += '<td ></td>'
            }
        } else {
            for (let i = 0; i < a - 1; i++) {
                document.getElementById("line" + line).innerHTML += '<td ></td>'
            }
        }
    }
    for (let i = 1; i <= getCountDays(); i++) {
        let a = new Date(now.setDate(i)).getDay()
        document.getElementById("line" + line).innerHTML += template.replaceAll("{day}", i < 10 ? "0" + i : i);
        if (a == 0) {
            line++;
        }
        finalDayWeek = 7 - a
    }
    for (let i = 0; i < finalDayWeek; i++) {
        document.getElementById("line" + line).innerHTML += '<td ></td>'
    }

    document.getElementById("day" + (now_day < 10 ? "0" + now_day : now_day)).parentNode.className += "now_day_div";
}
function setTFS() {
    try {
        if ($("input").length < 8) {
            message("自动化填报失败,尚未发现输入框", 0)
            return 0;
        }
        let task = prompt("今日任务, 自动拼接年月")
        let org = prompt("[简称]-全称\n[[简称]-全称\n[简称]-全称\nnull-自己选择")
        switch (org) {
            case "*简称*": $("input")[8].value = $("input")[8].title = '*全称*'; break;
            case "*简称*": $("input")[8].value = $("input")[8].title = '*全称*'; break;
            case "*简称*": $("input")[8].value = $("input")[8].title = '*全称*'; break;
            default: break;
        }
        $("input")[8].focus()
        if (task !== null) {
            let dateOfNow = new Date()
            task = dateOfNow.getUTCFullYear() + "" + (dateOfNow.getMonth() + 1) + task
        }
        $($("input")[3]).removeClass("watermark")
        $("input")[3].value = $("input")[3].title = task
        $("input")[3].focus()
        $("input")[9].value = $("input")[9].title = '技术栈';
        $("input")[9].focus()
        $("input")[10].value = $("input")[10].title = 100;
        $("input")[10].focus()
        $("input")[11].value = $("input")[11].title = 8;
        $("input")[11].focus()
        $("input")[12].value = $("input")[12].title = 8;
        $("input")[12].focus()
        $("input")[13].value = $("input")[13].title = 8;
        $("input")[13].focus()
        $($($("body")[0]).find("iframe")[0]).contents().find('body').get(0).innerHTML = task
        let invalidItem = $(".invalid")
        for (let i = 0; i < invalidItem.length; i++) {
            $(invalidItem[i]).removeClass("invalid")
        }
        $($($($("body")[0]).find("iframe")[0]).contents().find('body').get(0)).removeClass("invalid")
        $($($($("body")[0]).find("iframe")[0]).contents().find('body').get(0)).focus()
        message("自动化填报成功")
    } catch (e) {

        console.error(e)
        message("自动化填报失败" + e, 0)
    }
}
function setData() {
    dayLogItems = document.getElementsByClassName("dayLog")
    for (let i = dayLogItems.length - 1; i >= 0; i--) {
        dayLogItems[i].parentNode.removeChild(dayLogItems[i])
    }
    for (let i = 0; i < data.length; i++) {
        let day_log_item = data[i].split('-')
        let log_day = day_log_item[0].substr(6, 2)
        let log_item = day_log_item[1]

        document.getElementById("day" + log_day).innerHTML += "<div class='dayLog' id='day_i" + log_day + "'>" + log_item + "</div>"
    }
    obj = document.getElementsByClassName("t_table")[0]
    obj.style.display = "block"
}
title_bar.addEventListener('mousedown', function (e) {
    dialogMoveFlag = true;
    mousePointX = document.getElementsByClassName("calendar")[0].style.left.replaceAll("px", "") - e.x;
    mousePointY = document.getElementsByClassName("calendar")[0].style.top.replaceAll("px", "") - e.y;
    // console.log("down", mousePointX, mousePointY)
})

title_bar.addEventListener('mousemove', function (e) {

    if (dialogMoveFlag) {
        document.getElementsByClassName("calendar")[0].style.left = mousePointX + e.x + "px";
        document.getElementsByClassName("calendar")[0].style.top = mousePointY + e.y + "px";
        // console.log("move", document.getElementsByClassName("calendar")[0].style.left, document.getElementsByClassName("calendar")[0].style.top)
    }
})

document.addEventListener('mouseup', function (e) {
    // console.log("up")
    dialogMoveFlag = false;
})

function closeDialog() {
    document.getElementsByClassName("calendar")[0].style = 'display:none'
}

function getDayLog() {
    let b = document.getElementsByClassName("tbTileContent");
    data = []
    for (let i = 0; i < b.length; i++) {
        if (b[i].innerText.indexOf(yourName) == b[i].innerText.length - 2)
            data.push(b[i].innerText)
    }
    data.sort();
    setData()
    message("重新获取日报信息成功")

}
function init() {
    setCol();

}

init()

结语

虽然吧,这个代码很垃圾,我敢肯定有好多css是可以去掉的,js部分有冗余的代码,但是能用,并且效果看起来还不错,肝到了凌晨3点半,不优化了,如果TFS模板有变动,这个助手可能就不太好用了,但是原理是相通的主要是$("input")[n] 这里的n是第几个,算好就行,id每打开一次就会变化,我感觉只能这么做了。

以上

睡觉

评论 0

还没有评论,来说点什么吧。

提交后需要审核才会公开。