chandao4 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +10 -0
- package/CHANGELOG.md +21 -0
- package/LICENSE +21 -0
- package/README.md +184 -0
- package/dist/commands/bug.d.ts +3 -0
- package/dist/commands/bug.js +373 -0
- package/dist/commands/config.d.ts +2 -0
- package/dist/commands/config.js +38 -0
- package/dist/commands/login.d.ts +3 -0
- package/dist/commands/login.js +83 -0
- package/dist/commands/product.d.ts +3 -0
- package/dist/commands/product.js +41 -0
- package/dist/commands/project.d.ts +3 -0
- package/dist/commands/project.js +70 -0
- package/dist/commands/task.d.ts +3 -0
- package/dist/commands/task.js +445 -0
- package/dist/config/config.d.ts +17 -0
- package/dist/config/config.js +216 -0
- package/dist/config/defaults.d.ts +5 -0
- package/dist/config/defaults.js +23 -0
- package/dist/core/api-client.d.ts +20 -0
- package/dist/core/api-client.js +127 -0
- package/dist/core/auth.d.ts +44 -0
- package/dist/core/auth.js +244 -0
- package/dist/core/errors.d.ts +17 -0
- package/dist/core/errors.js +61 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +125 -0
- package/dist/services/bug.service.d.ts +59 -0
- package/dist/services/bug.service.js +232 -0
- package/dist/services/product.service.d.ts +12 -0
- package/dist/services/product.service.js +43 -0
- package/dist/services/project.service.d.ts +18 -0
- package/dist/services/project.service.js +35 -0
- package/dist/services/task.service.d.ts +55 -0
- package/dist/services/task.service.js +254 -0
- package/dist/types/api.d.ts +31 -0
- package/dist/types/api.js +3 -0
- package/dist/types/config.d.ts +18 -0
- package/dist/types/config.js +3 -0
- package/dist/types/models.d.ts +65 -0
- package/dist/types/models.js +33 -0
- package/dist/utils/format.d.ts +38 -0
- package/dist/utils/format.js +201 -0
- package/dist/utils/prompt.d.ts +12 -0
- package/dist/utils/prompt.js +154 -0
- package/dist/utils/validators.d.ts +14 -0
- package/dist/utils/validators.js +42 -0
- package/package.json +63 -0
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Task 业务逻辑 - JSON API
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.TaskService = void 0;
|
|
5
|
+
class TaskService {
|
|
6
|
+
client;
|
|
7
|
+
constructor(client) {
|
|
8
|
+
this.client = client;
|
|
9
|
+
}
|
|
10
|
+
/** 获取当前用户的任务列表 */
|
|
11
|
+
async getMyTasks() {
|
|
12
|
+
const data = await this.client.getJson('/my-task.json');
|
|
13
|
+
const tasks = [];
|
|
14
|
+
if (data.tasks) {
|
|
15
|
+
for (const [id, t] of Object.entries(data.tasks)) {
|
|
16
|
+
tasks.push(this.mapTask({ ...t, id }));
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
tasks.sort((a, b) => b.id - a.id);
|
|
20
|
+
let total = tasks.length;
|
|
21
|
+
if (data.summary) {
|
|
22
|
+
const m = data.summary.match(/共\s*(\d+)\s*个/);
|
|
23
|
+
if (m)
|
|
24
|
+
total = parseInt(m[1], 10);
|
|
25
|
+
}
|
|
26
|
+
return { tasks, total };
|
|
27
|
+
}
|
|
28
|
+
/** 获取任务列表 */
|
|
29
|
+
async getList(projectId) {
|
|
30
|
+
const data = await this.client.getJson(`/project-task-${projectId}.json`);
|
|
31
|
+
const tasks = [];
|
|
32
|
+
if (data.tasks) {
|
|
33
|
+
for (const [id, t] of Object.entries(data.tasks)) {
|
|
34
|
+
tasks.push(this.mapTask({ ...t, id }));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
tasks.sort((a, b) => b.id - a.id);
|
|
38
|
+
let total = tasks.length;
|
|
39
|
+
if (data.summary) {
|
|
40
|
+
const m = data.summary.match(/共\s*(\d+)\s*个/);
|
|
41
|
+
if (m)
|
|
42
|
+
total = parseInt(m[1], 10);
|
|
43
|
+
}
|
|
44
|
+
return { tasks, total };
|
|
45
|
+
}
|
|
46
|
+
/** 获取任务详情 */
|
|
47
|
+
async getDetail(taskId) {
|
|
48
|
+
const data = await this.client.getJson(`/task-view-${taskId}.json`);
|
|
49
|
+
if (!data.task)
|
|
50
|
+
return null;
|
|
51
|
+
return this.mapTask(data.task);
|
|
52
|
+
}
|
|
53
|
+
mapTask(t) {
|
|
54
|
+
return {
|
|
55
|
+
id: parseInt(t.id, 10),
|
|
56
|
+
name: t.name || '',
|
|
57
|
+
execution: parseInt(t.project, 10) || 0,
|
|
58
|
+
module: parseInt(t.module, 10) || 0,
|
|
59
|
+
type: t.type || '',
|
|
60
|
+
priority: parseInt(t.pri, 10) || 3,
|
|
61
|
+
status: t.status || 'wait',
|
|
62
|
+
assignedTo: t.assignedTo || '',
|
|
63
|
+
estimate: parseFloat(t.estimate) || 0,
|
|
64
|
+
consumed: parseFloat(t.consumed) || 0,
|
|
65
|
+
left: parseFloat(t.left) || 0,
|
|
66
|
+
deadline: t.deadline !== '0000-00-00' ? t.deadline : undefined,
|
|
67
|
+
estStarted: t.estStarted !== '0000-00-00' ? t.estStarted : undefined,
|
|
68
|
+
openedDate: t.openedDate || '',
|
|
69
|
+
desc: t.desc || '',
|
|
70
|
+
detailFields: this.buildDetailFields(t),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
buildDetailFields(t) {
|
|
74
|
+
const f = {};
|
|
75
|
+
const priLabels = ['', '紧急', '高', '中', '低'];
|
|
76
|
+
if (t.pri)
|
|
77
|
+
f['优先级'] = priLabels[parseInt(t.pri, 10)] || t.pri;
|
|
78
|
+
if (t.status)
|
|
79
|
+
f['状态'] = t.status;
|
|
80
|
+
if (t.type)
|
|
81
|
+
f['类型'] = t.type;
|
|
82
|
+
if (t.assignedTo)
|
|
83
|
+
f['指派给'] = t.assignedTo;
|
|
84
|
+
if (t.openedBy)
|
|
85
|
+
f['创建者'] = t.openedBy;
|
|
86
|
+
if (t.openedDate)
|
|
87
|
+
f['创建日期'] = t.openedDate;
|
|
88
|
+
if (t.estimate)
|
|
89
|
+
f['预估工时'] = `${t.estimate}h`;
|
|
90
|
+
if (t.consumed)
|
|
91
|
+
f['已消耗'] = `${t.consumed}h`;
|
|
92
|
+
if (t.left)
|
|
93
|
+
f['剩余'] = `${t.left}h`;
|
|
94
|
+
if (t.deadline && t.deadline !== '0000-00-00')
|
|
95
|
+
f['截止日期'] = t.deadline;
|
|
96
|
+
if (t.desc)
|
|
97
|
+
f['描述'] = t.desc;
|
|
98
|
+
return f;
|
|
99
|
+
}
|
|
100
|
+
/** 检查操作是否成功(通过 resp 或实际状态验证) */
|
|
101
|
+
isSuccess(resp) {
|
|
102
|
+
if (resp === null || resp === undefined)
|
|
103
|
+
return false;
|
|
104
|
+
if (typeof resp === 'object') {
|
|
105
|
+
if (resp.result === 'success' || resp.status === 'success')
|
|
106
|
+
return true;
|
|
107
|
+
// JS locate string 被 JSON 包装后 data 是字符串
|
|
108
|
+
if (resp.data && typeof resp.data === 'string' && resp.data.includes('location'))
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
// die(js::locate(...)) 返回原始 JS 字符串
|
|
112
|
+
if (typeof resp === 'string' && resp.includes('location'))
|
|
113
|
+
return true;
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
async create(projectId, task) {
|
|
117
|
+
const data = {
|
|
118
|
+
project: String(projectId),
|
|
119
|
+
name: task.name,
|
|
120
|
+
type: task.type || 'devel',
|
|
121
|
+
pri: String(task.priority || 3),
|
|
122
|
+
'assignedTo[]': task.assignedTo || '',
|
|
123
|
+
estStarted: task.estStarted || new Date().toISOString().split('T')[0],
|
|
124
|
+
};
|
|
125
|
+
if (task.estimate)
|
|
126
|
+
data.estimate = String(task.estimate);
|
|
127
|
+
data.deadline = task.deadline || data.estStarted;
|
|
128
|
+
if (task.desc)
|
|
129
|
+
data.desc = task.desc;
|
|
130
|
+
const resp = await this.client.postJson(`/task-create-${projectId}.json`, data);
|
|
131
|
+
if (resp?.result === 'success' || resp?.status === 'success') {
|
|
132
|
+
const taskId = resp?.data ? parseInt(String(resp.data), 10) : 0;
|
|
133
|
+
if (taskId > 0) {
|
|
134
|
+
return this.getDetail(taskId);
|
|
135
|
+
}
|
|
136
|
+
const { tasks } = await this.getList(projectId);
|
|
137
|
+
if (tasks.length > 0) {
|
|
138
|
+
const byName = tasks.find(t => t.name === task.name);
|
|
139
|
+
if (byName)
|
|
140
|
+
return byName;
|
|
141
|
+
return tasks[0];
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (resp?.message) {
|
|
145
|
+
console.error('创建失败:', resp.message);
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
async update(taskId, updates) {
|
|
151
|
+
const current = await this.getDetail(taskId);
|
|
152
|
+
if (!current) {
|
|
153
|
+
console.error('更新失败: 任务不存在');
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
const data = {};
|
|
157
|
+
if (updates.name)
|
|
158
|
+
data.name = String(updates.name);
|
|
159
|
+
if (updates.status)
|
|
160
|
+
data.status = String(updates.status);
|
|
161
|
+
if (updates.priority !== undefined)
|
|
162
|
+
data.pri = String(updates.priority);
|
|
163
|
+
if (updates.assignedTo)
|
|
164
|
+
data.assignedTo = String(updates.assignedTo);
|
|
165
|
+
if (updates.consumed !== undefined)
|
|
166
|
+
data.consumed = String(updates.consumed);
|
|
167
|
+
if (updates.left !== undefined)
|
|
168
|
+
data.left = String(updates.left);
|
|
169
|
+
if (updates.desc)
|
|
170
|
+
data.desc = String(updates.desc);
|
|
171
|
+
data.estimate = updates.estimate !== undefined
|
|
172
|
+
? String(updates.estimate)
|
|
173
|
+
: String(current.estimate || 0);
|
|
174
|
+
data.estStarted = String(updates.estStarted || current.estStarted || '');
|
|
175
|
+
data.deadline = String(updates.deadline || current.deadline || '');
|
|
176
|
+
if (!data.estStarted || data.estStarted === '')
|
|
177
|
+
data.estStarted = new Date().toISOString().split('T')[0];
|
|
178
|
+
if (!data.deadline || data.deadline === '')
|
|
179
|
+
data.deadline = data.estStarted;
|
|
180
|
+
await this.client.postJson(`/task-edit-${taskId}.json`, data);
|
|
181
|
+
return this.getDetail(taskId);
|
|
182
|
+
}
|
|
183
|
+
/** 取消任务 */
|
|
184
|
+
async cancel(taskId, comment) {
|
|
185
|
+
// comment 字段确保 $_POST 非空,禅道以此判断是否执行取消操作
|
|
186
|
+
const data = { comment: comment || '' };
|
|
187
|
+
await this.client.postJson(`/task-cancel-${taskId}.json`, data);
|
|
188
|
+
const task = await this.getDetail(taskId);
|
|
189
|
+
if (task && task.status !== 'cancel') {
|
|
190
|
+
console.error('取消失败: 服务端未能更新任务状态,当前状态为', task.status);
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
return task;
|
|
194
|
+
}
|
|
195
|
+
/** 开始任务(wait/pause → doing) */
|
|
196
|
+
async start(taskId, options) {
|
|
197
|
+
const data = { comment: options?.comment || '' };
|
|
198
|
+
if (options?.consumed !== undefined)
|
|
199
|
+
data.consumed = String(options.consumed);
|
|
200
|
+
if (options?.left !== undefined)
|
|
201
|
+
data.left = String(options.left);
|
|
202
|
+
if (options?.assignedTo)
|
|
203
|
+
data.assignedTo = options.assignedTo;
|
|
204
|
+
await this.client.postJson(`/task-start-${taskId}.json`, data);
|
|
205
|
+
const task = await this.getDetail(taskId);
|
|
206
|
+
if (task && task.status !== 'doing') {
|
|
207
|
+
console.error('开始失败: 服务端未能更新任务状态,当前状态为', task.status);
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
return task;
|
|
211
|
+
}
|
|
212
|
+
/** 完成任务(doing → done) */
|
|
213
|
+
async finish(taskId, options) {
|
|
214
|
+
const data = { left: '0' };
|
|
215
|
+
if (options?.consumed !== undefined)
|
|
216
|
+
data.consumed = String(options.consumed);
|
|
217
|
+
if (options?.comment)
|
|
218
|
+
data.comment = options.comment;
|
|
219
|
+
await this.client.postJson(`/task-finish-${taskId}.json`, data);
|
|
220
|
+
const task = await this.getDetail(taskId);
|
|
221
|
+
if (task && task.status !== 'done') {
|
|
222
|
+
console.error('完成失败: 服务端未能更新任务状态,当前状态为', task.status);
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
return task;
|
|
226
|
+
}
|
|
227
|
+
/** 关闭任务(done/cancel → closed) */
|
|
228
|
+
async close(taskId, comment) {
|
|
229
|
+
const data = { comment: comment || '' };
|
|
230
|
+
await this.client.postJson(`/task-close-${taskId}.json`, data);
|
|
231
|
+
const task = await this.getDetail(taskId);
|
|
232
|
+
if (task && task.status !== 'closed') {
|
|
233
|
+
console.error('关闭失败: 服务端未能更新任务状态,当前状态为', task.status);
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
return task;
|
|
237
|
+
}
|
|
238
|
+
/** 激活任务(cancel/closed → wait) */
|
|
239
|
+
async activate(taskId, options) {
|
|
240
|
+
const data = { comment: options?.comment || '' };
|
|
241
|
+
if (options?.left !== undefined)
|
|
242
|
+
data.left = String(options.left);
|
|
243
|
+
await this.client.postJson(`/task-activate-${taskId}.json`, data);
|
|
244
|
+
return this.getDetail(taskId);
|
|
245
|
+
}
|
|
246
|
+
async delete(taskId, projectId) {
|
|
247
|
+
const pid = projectId || 0;
|
|
248
|
+
const resp = await this.client.getJson(`/task-delete-${pid}-${taskId}-yes.json`);
|
|
249
|
+
if (resp?.result !== 'success' && resp?.message) {
|
|
250
|
+
console.error('删除失败:', resp.message);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
exports.TaskService = TaskService;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Bug, Task, Product, Execution, User } from './models';
|
|
2
|
+
export interface ZentaoListResponse<T> {
|
|
3
|
+
page: number;
|
|
4
|
+
total: number;
|
|
5
|
+
limit: number;
|
|
6
|
+
bugs?: T[];
|
|
7
|
+
tasks?: T[];
|
|
8
|
+
products?: T[];
|
|
9
|
+
executions?: T[];
|
|
10
|
+
users?: T[];
|
|
11
|
+
}
|
|
12
|
+
export interface ZentaoItemResponse<T> {
|
|
13
|
+
data: T;
|
|
14
|
+
}
|
|
15
|
+
export interface ZentaoErrorResponse {
|
|
16
|
+
errcode: number;
|
|
17
|
+
errmsg: string;
|
|
18
|
+
}
|
|
19
|
+
export type BugListResponse = ZentaoListResponse<Bug>;
|
|
20
|
+
export type BugDetailResponse = ZentaoItemResponse<Bug>;
|
|
21
|
+
export type TaskListResponse = ZentaoListResponse<Task>;
|
|
22
|
+
export type TaskDetailResponse = ZentaoItemResponse<Task>;
|
|
23
|
+
export type ProductListResponse = ZentaoListResponse<Product>;
|
|
24
|
+
export type ProductDetailResponse = ZentaoItemResponse<Product>;
|
|
25
|
+
export type ExecutionListResponse = ZentaoListResponse<Execution>;
|
|
26
|
+
export type UserListResponse = ZentaoListResponse<User>;
|
|
27
|
+
export interface LoginResult {
|
|
28
|
+
success: boolean;
|
|
29
|
+
zentaosid?: string;
|
|
30
|
+
error?: string;
|
|
31
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface ServerConfig {
|
|
2
|
+
url: string;
|
|
3
|
+
username?: string;
|
|
4
|
+
password?: string;
|
|
5
|
+
code?: string;
|
|
6
|
+
token?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface OutputConfig {
|
|
9
|
+
format: 'table' | 'json';
|
|
10
|
+
color: boolean;
|
|
11
|
+
}
|
|
12
|
+
export interface ZentaoConfig {
|
|
13
|
+
server: ServerConfig;
|
|
14
|
+
output: OutputConfig;
|
|
15
|
+
}
|
|
16
|
+
export interface RuntimeConfig extends ZentaoConfig {
|
|
17
|
+
authMode: 'session' | 'apikey';
|
|
18
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export interface Bug {
|
|
2
|
+
id: number;
|
|
3
|
+
title: string;
|
|
4
|
+
product: number;
|
|
5
|
+
module: number;
|
|
6
|
+
severity: number;
|
|
7
|
+
priority: number;
|
|
8
|
+
status: string;
|
|
9
|
+
type: string;
|
|
10
|
+
assignedTo: string;
|
|
11
|
+
openedBy: string;
|
|
12
|
+
resolvedBy?: string;
|
|
13
|
+
steps?: string;
|
|
14
|
+
deadline?: string;
|
|
15
|
+
openedDate: string;
|
|
16
|
+
resolvedDate?: string;
|
|
17
|
+
/** HTML 页面提取的所有详情字段 */
|
|
18
|
+
detailFields?: Record<string, string>;
|
|
19
|
+
}
|
|
20
|
+
export interface Task {
|
|
21
|
+
id: number;
|
|
22
|
+
name: string;
|
|
23
|
+
execution: number;
|
|
24
|
+
module: number;
|
|
25
|
+
type: string;
|
|
26
|
+
priority: number;
|
|
27
|
+
status: string;
|
|
28
|
+
assignedTo: string;
|
|
29
|
+
estimate: number;
|
|
30
|
+
consumed: number;
|
|
31
|
+
left: number;
|
|
32
|
+
deadline?: string;
|
|
33
|
+
estStarted?: string;
|
|
34
|
+
openedDate: string;
|
|
35
|
+
/** 描述 */
|
|
36
|
+
desc?: string;
|
|
37
|
+
/** HTML 页面提取的所有详情字段 */
|
|
38
|
+
detailFields?: Record<string, string>;
|
|
39
|
+
}
|
|
40
|
+
export interface Product {
|
|
41
|
+
id: number;
|
|
42
|
+
name: string;
|
|
43
|
+
code: string;
|
|
44
|
+
status: string;
|
|
45
|
+
}
|
|
46
|
+
export interface Execution {
|
|
47
|
+
id: number;
|
|
48
|
+
name: string;
|
|
49
|
+
code: string;
|
|
50
|
+
product: number;
|
|
51
|
+
type: string;
|
|
52
|
+
status: string;
|
|
53
|
+
begin: string;
|
|
54
|
+
end?: string;
|
|
55
|
+
}
|
|
56
|
+
export interface User {
|
|
57
|
+
id: number;
|
|
58
|
+
account: string;
|
|
59
|
+
realname: string;
|
|
60
|
+
role: string;
|
|
61
|
+
}
|
|
62
|
+
export declare const BUG_STATUS_MAP: Record<string, string>;
|
|
63
|
+
export declare const BUG_SEVERITY_MAP: Record<number, string>;
|
|
64
|
+
export declare const BUG_PRIORITY_MAP: Record<number, string>;
|
|
65
|
+
export declare const TASK_STATUS_MAP: Record<string, string>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// 业务模型类型定义
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.TASK_STATUS_MAP = exports.BUG_PRIORITY_MAP = exports.BUG_SEVERITY_MAP = exports.BUG_STATUS_MAP = void 0;
|
|
5
|
+
// Bug 状态常量
|
|
6
|
+
exports.BUG_STATUS_MAP = {
|
|
7
|
+
active: '激活',
|
|
8
|
+
resolved: '已解决',
|
|
9
|
+
closed: '已关闭',
|
|
10
|
+
};
|
|
11
|
+
// Bug 严重程度
|
|
12
|
+
exports.BUG_SEVERITY_MAP = {
|
|
13
|
+
1: '致命',
|
|
14
|
+
2: '严重',
|
|
15
|
+
3: '一般',
|
|
16
|
+
4: '建议',
|
|
17
|
+
};
|
|
18
|
+
// Bug 优先级
|
|
19
|
+
exports.BUG_PRIORITY_MAP = {
|
|
20
|
+
1: '紧急',
|
|
21
|
+
2: '高',
|
|
22
|
+
3: '中',
|
|
23
|
+
4: '低',
|
|
24
|
+
};
|
|
25
|
+
// Task 状态常量
|
|
26
|
+
exports.TASK_STATUS_MAP = {
|
|
27
|
+
wait: '未开始',
|
|
28
|
+
doing: '进行中',
|
|
29
|
+
done: '已完成',
|
|
30
|
+
pause: '已暂停',
|
|
31
|
+
cancel: '已取消',
|
|
32
|
+
closed: '已关闭',
|
|
33
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Bug, Task, Product, Execution } from '../types/models';
|
|
2
|
+
import { Project } from '../services/project.service';
|
|
3
|
+
/**
|
|
4
|
+
* 格式化 Bug 列表为表格
|
|
5
|
+
*/
|
|
6
|
+
export declare function formatBugTable(bugs: Bug[]): string;
|
|
7
|
+
/**
|
|
8
|
+
* 格式化 Bug 详情
|
|
9
|
+
*/
|
|
10
|
+
export declare function formatBugDetail(bug: Bug): string;
|
|
11
|
+
/**
|
|
12
|
+
* 格式化 Task 列表为表格
|
|
13
|
+
*/
|
|
14
|
+
export declare function formatTaskTable(tasks: Task[]): string;
|
|
15
|
+
/**
|
|
16
|
+
* 格式化 Task 详情
|
|
17
|
+
*/
|
|
18
|
+
export declare function formatTaskDetail(task: Task): string;
|
|
19
|
+
/**
|
|
20
|
+
* 格式化 Product 列表
|
|
21
|
+
*/
|
|
22
|
+
export declare function formatProductTable(products: Product[]): string;
|
|
23
|
+
/**
|
|
24
|
+
* 格式化 Execution 列表
|
|
25
|
+
*/
|
|
26
|
+
export declare function formatExecutionTable(executions: Execution[]): string;
|
|
27
|
+
/**
|
|
28
|
+
* 格式化 Project 列表
|
|
29
|
+
*/
|
|
30
|
+
export declare function formatProjectTable(projects: Project[]): string;
|
|
31
|
+
export declare function formatBugStatus(status: string): string;
|
|
32
|
+
export declare function formatBugSeverity(severity: number): string;
|
|
33
|
+
export declare function formatBugPriority(priority: number): string;
|
|
34
|
+
export declare function formatTaskStatus(status: string): string;
|
|
35
|
+
/**
|
|
36
|
+
* JSON 输出
|
|
37
|
+
*/
|
|
38
|
+
export declare function formatJson(data: unknown): string;
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// 表格和颜色输出格式化
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.formatBugTable = formatBugTable;
|
|
8
|
+
exports.formatBugDetail = formatBugDetail;
|
|
9
|
+
exports.formatTaskTable = formatTaskTable;
|
|
10
|
+
exports.formatTaskDetail = formatTaskDetail;
|
|
11
|
+
exports.formatProductTable = formatProductTable;
|
|
12
|
+
exports.formatExecutionTable = formatExecutionTable;
|
|
13
|
+
exports.formatProjectTable = formatProjectTable;
|
|
14
|
+
exports.formatBugStatus = formatBugStatus;
|
|
15
|
+
exports.formatBugSeverity = formatBugSeverity;
|
|
16
|
+
exports.formatBugPriority = formatBugPriority;
|
|
17
|
+
exports.formatTaskStatus = formatTaskStatus;
|
|
18
|
+
exports.formatJson = formatJson;
|
|
19
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
20
|
+
const cli_table3_1 = __importDefault(require("cli-table3"));
|
|
21
|
+
const models_1 = require("../types/models");
|
|
22
|
+
/**
|
|
23
|
+
* 格式化 Bug 列表为表格
|
|
24
|
+
*/
|
|
25
|
+
function formatBugTable(bugs) {
|
|
26
|
+
const table = new cli_table3_1.default({
|
|
27
|
+
head: ['ID', '标题', '状态', '严重程度', '优先级', '指派给'],
|
|
28
|
+
colWidths: [8, 40, 10, 10, 8, 12],
|
|
29
|
+
wordWrap: true,
|
|
30
|
+
});
|
|
31
|
+
for (const bug of bugs) {
|
|
32
|
+
table.push([
|
|
33
|
+
String(bug.id),
|
|
34
|
+
bug.title,
|
|
35
|
+
formatBugStatus(bug.status),
|
|
36
|
+
formatBugSeverity(bug.severity),
|
|
37
|
+
formatBugPriority(bug.priority),
|
|
38
|
+
bug.assignedTo || '-',
|
|
39
|
+
]);
|
|
40
|
+
}
|
|
41
|
+
return table.toString();
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* 格式化 Bug 详情
|
|
45
|
+
*/
|
|
46
|
+
function formatBugDetail(bug) {
|
|
47
|
+
const lines = [chalk_1.default.bold(`\nBug #${bug.id}: ${bug.title}\n`)];
|
|
48
|
+
const fields = bug.detailFields || {};
|
|
49
|
+
const labels = Object.keys(fields);
|
|
50
|
+
if (labels.length > 0) {
|
|
51
|
+
const maxLen = Math.max(...labels.map(l => l.length));
|
|
52
|
+
for (const [label, value] of Object.entries(fields)) {
|
|
53
|
+
if (!value || value === '-' || value === '0000-00-00' || value === '0000-00-00 00:00:00')
|
|
54
|
+
continue;
|
|
55
|
+
const display = label === '重现步骤' ? value.substring(0, 200) + (value.length > 200 ? '...' : '') : value;
|
|
56
|
+
lines.push(`${chalk_1.default.gray(label.padEnd(maxLen + 1))} ${display}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return lines.join('\n');
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* 格式化 Task 列表为表格
|
|
63
|
+
*/
|
|
64
|
+
function formatTaskTable(tasks) {
|
|
65
|
+
const table = new cli_table3_1.default({
|
|
66
|
+
head: ['ID', '名称', '状态', '优先级', '指派给', '预估(h)', '已消耗(h)'],
|
|
67
|
+
colWidths: [8, 35, 10, 8, 12, 10, 10],
|
|
68
|
+
wordWrap: true,
|
|
69
|
+
});
|
|
70
|
+
for (const task of tasks) {
|
|
71
|
+
table.push([
|
|
72
|
+
String(task.id),
|
|
73
|
+
task.name,
|
|
74
|
+
formatTaskStatus(task.status),
|
|
75
|
+
formatBugPriority(task.priority),
|
|
76
|
+
task.assignedTo || '-',
|
|
77
|
+
String(task.estimate || 0),
|
|
78
|
+
String(task.consumed || 0),
|
|
79
|
+
]);
|
|
80
|
+
}
|
|
81
|
+
return table.toString();
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* 格式化 Task 详情
|
|
85
|
+
*/
|
|
86
|
+
function formatTaskDetail(task) {
|
|
87
|
+
const lines = [chalk_1.default.bold(`\n任务 #${task.id}: ${task.name}\n`)];
|
|
88
|
+
const fields = task.detailFields || {};
|
|
89
|
+
const labels = Object.keys(fields);
|
|
90
|
+
if (labels.length > 0) {
|
|
91
|
+
const maxLen = Math.max(...labels.map(l => l.length));
|
|
92
|
+
for (const [label, value] of Object.entries(fields)) {
|
|
93
|
+
if (!value || value === '-' || value === '0000-00-00' || value === '0000-00-00 00:00:00' || value === '暂无')
|
|
94
|
+
continue;
|
|
95
|
+
lines.push(`${chalk_1.default.gray(label.padEnd(maxLen + 1))} ${value}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return lines.join('\n');
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* 格式化 Product 列表
|
|
102
|
+
*/
|
|
103
|
+
function formatProductTable(products) {
|
|
104
|
+
const table = new cli_table3_1.default({
|
|
105
|
+
head: ['ID', '名称', '代号', '状态'],
|
|
106
|
+
colWidths: [8, 30, 20, 10],
|
|
107
|
+
});
|
|
108
|
+
for (const p of products) {
|
|
109
|
+
table.push([
|
|
110
|
+
String(p.id),
|
|
111
|
+
p.name,
|
|
112
|
+
p.code || '-',
|
|
113
|
+
p.status || '-',
|
|
114
|
+
]);
|
|
115
|
+
}
|
|
116
|
+
return table.toString();
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* 格式化 Execution 列表
|
|
120
|
+
*/
|
|
121
|
+
function formatExecutionTable(executions) {
|
|
122
|
+
const table = new cli_table3_1.default({
|
|
123
|
+
head: ['ID', '名称', '类型', '状态', '开始日期', '结束日期'],
|
|
124
|
+
colWidths: [8, 25, 10, 10, 14, 14],
|
|
125
|
+
});
|
|
126
|
+
for (const e of executions) {
|
|
127
|
+
table.push([
|
|
128
|
+
String(e.id),
|
|
129
|
+
e.name,
|
|
130
|
+
e.type || '-',
|
|
131
|
+
e.status || '-',
|
|
132
|
+
e.begin || '-',
|
|
133
|
+
e.end || '-',
|
|
134
|
+
]);
|
|
135
|
+
}
|
|
136
|
+
return table.toString();
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* 格式化 Project 列表
|
|
140
|
+
*/
|
|
141
|
+
function formatProjectTable(projects) {
|
|
142
|
+
const table = new cli_table3_1.default({
|
|
143
|
+
head: ['ID', '名称', '代号', '状态', '开始日期', '结束日期'],
|
|
144
|
+
colWidths: [8, 30, 20, 10, 14, 14],
|
|
145
|
+
});
|
|
146
|
+
for (const p of projects) {
|
|
147
|
+
table.push([
|
|
148
|
+
String(p.id),
|
|
149
|
+
p.name,
|
|
150
|
+
p.code || '-',
|
|
151
|
+
p.status || '-',
|
|
152
|
+
p.begin || '-',
|
|
153
|
+
p.end || '-',
|
|
154
|
+
]);
|
|
155
|
+
}
|
|
156
|
+
return table.toString();
|
|
157
|
+
}
|
|
158
|
+
// 状态格式化函数
|
|
159
|
+
function formatBugStatus(status) {
|
|
160
|
+
const label = models_1.BUG_STATUS_MAP[status] || status;
|
|
161
|
+
switch (status) {
|
|
162
|
+
case 'active': return chalk_1.default.red(label);
|
|
163
|
+
case 'resolved': return chalk_1.default.green(label);
|
|
164
|
+
case 'closed': return chalk_1.default.gray(label);
|
|
165
|
+
default: return label;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
function formatBugSeverity(severity) {
|
|
169
|
+
const label = models_1.BUG_SEVERITY_MAP[severity] || String(severity);
|
|
170
|
+
if (severity <= 2)
|
|
171
|
+
return chalk_1.default.red(label);
|
|
172
|
+
if (severity === 3)
|
|
173
|
+
return chalk_1.default.yellow(label);
|
|
174
|
+
return chalk_1.default.gray(label);
|
|
175
|
+
}
|
|
176
|
+
function formatBugPriority(priority) {
|
|
177
|
+
const label = models_1.BUG_PRIORITY_MAP[priority] || String(priority);
|
|
178
|
+
if (priority === 1)
|
|
179
|
+
return chalk_1.default.red(label);
|
|
180
|
+
if (priority === 2)
|
|
181
|
+
return chalk_1.default.yellow(label);
|
|
182
|
+
return chalk_1.default.green(label);
|
|
183
|
+
}
|
|
184
|
+
function formatTaskStatus(status) {
|
|
185
|
+
const label = models_1.TASK_STATUS_MAP[status] || status;
|
|
186
|
+
switch (status) {
|
|
187
|
+
case 'doing': return chalk_1.default.blue(label);
|
|
188
|
+
case 'done': return chalk_1.default.green(label);
|
|
189
|
+
case 'closed': return chalk_1.default.gray(label);
|
|
190
|
+
case 'pause': return chalk_1.default.yellow(label);
|
|
191
|
+
case 'cancel': return chalk_1.default.red(label);
|
|
192
|
+
case 'wait': return chalk_1.default.cyan(label);
|
|
193
|
+
default: return label;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* JSON 输出
|
|
198
|
+
*/
|
|
199
|
+
function formatJson(data) {
|
|
200
|
+
return JSON.stringify(data, null, 2);
|
|
201
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 询问用户确认
|
|
3
|
+
*/
|
|
4
|
+
export declare function confirm(message: string): Promise<boolean>;
|
|
5
|
+
/**
|
|
6
|
+
* 询问用户输入
|
|
7
|
+
*/
|
|
8
|
+
export declare function prompt(message: string, defaultValue?: string): Promise<string>;
|
|
9
|
+
/**
|
|
10
|
+
* 询问密码(不回显)
|
|
11
|
+
*/
|
|
12
|
+
export declare function promptPassword(message: string): Promise<string>;
|