TAPD多项目任务管理,实现BUG统计及自动评分

1.页面文件:index.html

<!DOCTYPE html>

<html lang="zh">

<head>

  <meta charset="UTF-8" />

  <title>TAPD Bug 评估</title>

  <style>

    body { font-family: sans-serif; padding: 20px; }

    table { border-collapse: collapse; width: 100%; margin-top: 20px; }

    th, td { border: 1px solid #ccc; padding: 8px; text-align: left; }

    input[type="number"] { width: 60px; }

  </style>

</head>

<body>

  <h2>Bug 评估计算器</h2>

  <label for="month">选择月份:</label>

  <input type="month" id="month" />

  <div id="fixerInputs" style="margin-top: 20px;">

    <!-- 动态渲染 fixer 和输入框 -->

  </div>

  <button id="calculateBtn">计算得分</button>

  <div id="resultArea">

    <!-- 显示结果 -->

  </div>

  <script src="assessmentScore.js" type="module"></script>

</body>

</html>

2.主要计算:assessmentScore.js

// 默认当前月

// document.getElementById('month').value = new Date().toISOString().slice(0, 7);

// 设置默认日期为上一个月

const monthInput = document.getElementById('month');

const now = new Date();

const lastMonth = new Date(now.getFullYear(), now.getMonth() - 1, 1); // 上一个月

const formatted = `${lastMonth.getFullYear()}-${String(lastMonth.getMonth() + 1).padStart(2, '0')}`;

monthInput.value = formatted;

const fixerRatioMap = {}; // 存储用户输入

let fixerList = [];      // 所有人名

let rawBugData = [];      // 所有原始数据

// 获取数据 & 渲染输入框

(async () => {

  rawBugData = await fetchBugData();

  fixerList = getFixers(rawBugData, getSelectedMonth());

  renderFixerInputs(fixerList);

})();

function getSelectedMonth() {

  return document.getElementById('month').value;

}

function getFixers(data, month) {

  const fixers = new Set();

  data.forEach(item => {

    const created = item.Bug.created;

    const fixer = item.Bug.fixer;

    if (created.startsWith(month) && fixer) {

      fixers.add(fixer);

    }

  });

  return Array.from(fixers);

}

function renderFixerInputs(fixers) {

  const container = document.getElementById('fixerInputs');

  container.innerHTML = '<h3>输入任务占比(0–100%)</h3><table><tr><th>姓名</th><th>任务占比%</th></tr>' +

    fixers.map(name => `

      <tr>

        <td>${name}</td>

        <td><input type="number" min="0" max="100" data-fixer="${name}" /></td>

      </tr>

    `).join('') + '</table>';

}

// 绑定点击事件

document.getElementById('calculateBtn').addEventListener('click', () => {

  // 读取输入框

  document.querySelectorAll('input[data-fixer]').forEach(input => {

    const fixer = input.dataset.fixer;

    const val = parseFloat(input.value);

    fixerRatioMap[fixer] = isNaN(val) ? null : val / 100;

  });

  const month = getSelectedMonth();

  const result = calculateScores(rawBugData, month, fixerRatioMap);

  renderResult(result);

});

function renderResult(data) {

  const html = `

    <h3>评估结果(总分:33.25分)</h3>

    <table>

      <tr><th>Fixer</th><th>normal</th><th>serious</th><th>fatal</th><th>bugScore</th><th>得分</th></tr>

      ${data.map(d => `

        <tr>

          <td>${d.fixer}</td>

          <td>${d.normal}</td>

          <td>${d.serious}</td>

          <td>${d.fatal}</td>

          <td>${d.bugScore}</td>

          <td>${d.score}</td>

        </tr>

      `).join('')}

    </table>

  `;

  document.getElementById('resultArea').innerHTML = html;

}

// === 以下是你已有逻辑的调整版 ===

async function fetchBugData() {

  const token = 'iUXZdUZJTrJfmoM0';

  const projectArray = [

    { name: 'xxx1', workspace_id: 'xx1', conf_id: 'x1' },

    { name: 'xxx2', workspace_id: 'xx2', conf_id: 'x2' },

    { name: xxx3', workspace_id: 'xx3', conf_id: 'x3' }

  ];

  let merged = [];

  for (let i = 1; i <= 10; i++) {

    const promises = projectArray.map(item =>

      fetch('/api/tapd', {

        method: 'POST',

        headers: { 'Content-Type': 'application/json' },

        body: JSON.stringify({

          workspace_id: item.workspace_id,

          conf_id: item.conf_id,

          sort_name: "",

          confIdType: "URL",

          order: "desc",

          perpage: 50,

          page: i,

          selected_workspace_ids: "",

          query_token: "",

          location: "/bugtrace/bugreports/my_view",

          target: `${item.workspace_id}/bug/normal`,

          entity_types: ["bug"],

          use_scene: "bug_list",

          return_url: `https://www.tapd.cn/tapd_fe/${item.workspace_id}/bug/list?confId=${item.conf_id}`,

          identifier: "app_for_list_tools,app_for_list_operation",

          dsc_token: token

        })

      }).then(res => res.json().then(r => r?.data?.bugs_list_ret?.data?.bugs_list || []))

    );

    const res = await Promise.all(promises);

    merged = merged.concat(...res);

  }

  return merged;

}

function calculateScores(data, time, ratioMap = {}) {

  const filtered = data.filter(item => item.Bug.created.startsWith(time));

  const fixerStats = {};

  filtered.forEach(item => {

    const { fixer, severity } = item.Bug;

    const sev = severity?.toLowerCase();

    if (!fixer || !['normal', 'serious', 'fatal'].includes(sev)) return;

    fixerStats[fixer] ||= { normal: 0, serious: 0, fatal: 0, bugScore: 0 };

    const scoreMap = { normal: 1, serious: 3, fatal: 5 };

    fixerStats[fixer][sev]++;

    fixerStats[fixer].bugScore += scoreMap[sev];

  });

  return Object.entries(fixerStats).map(([fixer, stats]) => {

    const ratio = typeof ratioMap[fixer] === 'number' ? ratioMap[fixer] : 0.25;

    const x = stats.bugScore;

    const score = (100 - x * (1 - ratio)) * 35 * 0.95 / 100;

    return {

      fixer,

      ...stats,

      score: score.toFixed(2),

    };

  });

}

3.处理跨域问题:proxy.js

import express from 'express';

import fetch from 'node-fetch';

const app = express();

const PORT = 3000;

app.use(express.static('public'));

app.use(express.json());

app.post('/api/tapd', async (req, res) => {

  try {

    const response = await fetch('https://www.tapd.cn/api/aggregation/bug_aggregation/get_bugs_list', {

      method: 'POST',

      headers: {

        'Content-Type': 'application/json',

        'Cookie': 't_u=196aae59xxxxxxxxxxxxx;',

        'Referer': 'https://www.tapd.cn/'

      },

      body: JSON.stringify(req.body)

    });

    const data = await response.json();

    res.json(data);

  } catch (err) {

    res.status(500).json({ error: '代理请求失败', detail: err.message });

  }

});

app.listen(PORT, () => {

  console.log(`✅ 代理服务运行中:http://localhost:${PORT}`);

});

4.自行安装对应的依赖:package.json文件

{

  "name": "project-folder",

  "version": "1.0.0",

  "type": "module",

  "main": "index.js",

  "scripts": {

    "test": "echo \"Error: no test specified\" && exit 1"

  },

  "keywords": [],

  "author": "wang",

  "license": "ISC",

  "description": "",

  "dependencies": {

    "express": "^5.1.0",

    "node-fetch": "^3.3.2"

  }

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容