Size
8.8 KB
Version
1.0.1
Created
Jan 21, 2026
Updated
14 days ago
1// ==UserScript==
2// @name Extension for dev.hundsun.com
3// @description A new extension
4// @version 1.0.1
5// @match https://*.dev.hundsun.com/*
6// @icon https://dev.hundsun.com/frameV2/logo.ico
7// ==/UserScript==
8(function() {
9 'use strict';
10
11 // 工具函数:等待元素出现
12 function waitForElement(selector, timeout = 10000) {
13 return new Promise((resolve, reject) => {
14 const startTime = Date.now();
15
16 const checkElement = () => {
17 const element = document.querySelector(selector);
18 if (element) {
19 console.log(`找到元素: ${selector}`);
20 resolve(element);
21 } else if (Date.now() - startTime > timeout) {
22 console.error(`等待元素超时: ${selector}`);
23 reject(new Error(`等待元素超时: ${selector}`));
24 } else {
25 setTimeout(checkElement, 100);
26 }
27 };
28
29 checkElement();
30 });
31 }
32
33 // 工具函数:延迟
34 function delay(ms) {
35 return new Promise(resolve => setTimeout(resolve, ms));
36 }
37
38 // 主要功能:处理单个需求
39 async function processStory(storyElement, index, total) {
40 try {
41 console.log(`\n========== 处理第 ${index + 1}/${total} 个需求 ==========`);
42
43 // 1. 点击需求标题
44 const titleElement = storyElement.querySelector('.title.link.ellipsis');
45 if (!titleElement) {
46 console.error('未找到需求标题元素');
47 return;
48 }
49
50 const storyTitle = titleElement.textContent.trim();
51 console.log(`需求标题: ${storyTitle}`);
52
53 titleElement.click();
54 console.log('已点击需求标题');
55
56 // 2. 等待详情窗口出现
57 await delay(1000);
58 const detailWindow = await waitForElement('.storyDetails-arrow-Tab', 5000);
59 console.log('详情窗口已打开');
60
61 // 3. 找到并点击"任务安排"tab(第二个tab)
62 const tabs = document.querySelectorAll('.tab_wrap .tab.detail');
63 console.log(`找到 ${tabs.length} 个tab`);
64
65 if (tabs.length < 2) {
66 console.error('未找到任务安排tab');
67 return;
68 }
69
70 const taskTab = tabs[1]; // 第二个tab是"任务"
71 console.log(`点击tab: ${taskTab.textContent.trim()}`);
72 taskTab.click();
73
74 // 4. 等待任务列表加载
75 await delay(1500);
76
77 // 5. 获取已安排的任务列表
78 const taskList = document.querySelectorAll('.storyDetails-arrow-Tab .task-item, .storyDetails-arrow-Tab .el-table__row');
79 console.log(`当前需求已安排任务数量: ${taskList.length}`);
80
81 // 6. 查找并点击批量创建图标
82 const batchCreateIcon = document.querySelector('.storyDetails-arrow-Tab .icon.iconfontcustom.stack-create-wrapper[title="批量创建"]');
83
84 if (!batchCreateIcon) {
85 console.warn('未找到批量创建图标,尝试其他选择器...');
86 // 尝试其他可能的选择器
87 const alternativeIcon = document.querySelector('.storyDetails-arrow-Tab span[title="批量创建"]');
88 if (alternativeIcon) {
89 console.log('找到批量创建图标(备用选择器)');
90 alternativeIcon.click();
91 } else {
92 console.error('无法找到批量创建图标');
93 return;
94 }
95 } else {
96 console.log('找到批量创建图标,准备点击');
97 batchCreateIcon.click();
98 }
99
100 // 7. 等待批量创建窗口弹出
101 await delay(1000);
102 const batchCreateWindow = await waitForElement('.el-dialog__wrapper, .batch-create-dialog', 3000);
103 console.log('批量任务创建窗口已弹出');
104
105 // 8. 等待用户操作后关闭窗口
106 await delay(2000);
107
108 // 9. 关闭详情窗口
109 const closeBtn = document.querySelector('.storyDetails-arrow-Tab .close-btn, .storyDetails-arrow-Tab .el-icon-close');
110 if (closeBtn) {
111 closeBtn.click();
112 console.log('已关闭详情窗口');
113 }
114
115 await delay(500);
116
117 } catch (error) {
118 console.error('处理需求时出错:', error);
119 }
120 }
121
122 // 主要功能:一键任务拆解
123 async function oneClickTaskBreakdown() {
124 try {
125 console.log('========== 开始一键任务拆解 ==========');
126
127 // 1. 获取所有需求列表
128 const storyElements = document.querySelectorAll('.el-table__body .el-table__row');
129
130 if (storyElements.length === 0) {
131 alert('未找到需求列表,请确保页面已加载完成');
132 console.error('未找到需求列表');
133 return;
134 }
135
136 console.log(`找到 ${storyElements.length} 个需求`);
137
138 // 2. 遍历处理每个需求
139 for (let i = 0; i < storyElements.length; i++) {
140 await processStory(storyElements[i], i, storyElements.length);
141
142 // 每处理完一个需求,等待一段时间
143 if (i < storyElements.length - 1) {
144 console.log('等待处理下一个需求...');
145 await delay(1000);
146 }
147 }
148
149 console.log('========== 一键任务拆解完成 ==========');
150 alert('一键任务拆解完成!');
151
152 } catch (error) {
153 console.error('一键任务拆解出错:', error);
154 alert('一键任务拆解出错: ' + error.message);
155 }
156 }
157
158 // 添加按钮样式
159 TM_addStyle(`
160 .task-breakdown-btn {
161 margin-left: 10px;
162 padding: 9px 15px;
163 background-color: #67C23A;
164 color: white;
165 border: none;
166 border-radius: 4px;
167 cursor: pointer;
168 font-size: 12px;
169 transition: background-color 0.3s;
170 }
171
172 .task-breakdown-btn:hover {
173 background-color: #85CE61;
174 }
175
176 .task-breakdown-btn:active {
177 background-color: #5DAF34;
178 }
179
180 .task-breakdown-btn:disabled {
181 background-color: #A0CFFF;
182 cursor: not-allowed;
183 }
184 `);
185
186 // 初始化函数
187 function init() {
188 console.log('一键任务拆解扩展已加载');
189
190 // 等待页面加载完成
191 const checkAndAddButton = () => {
192 const createBtnWrapper = document.querySelector('.d-button.big-btn');
193
194 if (createBtnWrapper && !document.querySelector('.task-breakdown-btn')) {
195 console.log('找到创建需求按钮,添加一键任务拆解按钮');
196
197 // 创建一键任务拆解按钮
198 const breakdownBtn = document.createElement('button');
199 breakdownBtn.className = 'task-breakdown-btn';
200 breakdownBtn.textContent = '一键任务拆解';
201 breakdownBtn.title = '遍历所有需求并打开批量任务创建窗口';
202
203 // 绑定点击事件
204 breakdownBtn.addEventListener('click', async () => {
205 breakdownBtn.disabled = true;
206 breakdownBtn.textContent = '处理中...';
207
208 try {
209 await oneClickTaskBreakdown();
210 } finally {
211 breakdownBtn.disabled = false;
212 breakdownBtn.textContent = '一键任务拆解';
213 }
214 });
215
216 // 将按钮插入到创建需求按钮旁边
217 createBtnWrapper.parentElement.insertBefore(breakdownBtn, createBtnWrapper.nextSibling);
218
219 console.log('一键任务拆解按钮已添加');
220 } else if (!createBtnWrapper) {
221 // 如果还没找到,继续等待
222 setTimeout(checkAndAddButton, 500);
223 }
224 };
225
226 // 开始检查并添加按钮
227 if (document.readyState === 'loading') {
228 document.addEventListener('DOMContentLoaded', checkAndAddButton);
229 } else {
230 checkAndAddButton();
231 }
232 }
233
234 // 启动
235 init();
236})();