dsh-plugin-daily-report 1.0.1 → 1.0.3
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/README.md +12 -6
- package/client.js +638 -40
- package/index.js +245 -17
- package/lib/ai.js +48 -9
- package/lib/collect.js +34 -9
- package/lib/formats.js +190 -0
- package/lib/report.js +3 -2
- package/package.json +1 -1
package/client.js
CHANGED
|
@@ -20,6 +20,8 @@ window.__ModuleLoader__.load({
|
|
|
20
20
|
"range.thisMonth": "本月",
|
|
21
21
|
"range.lastMonth": "上月",
|
|
22
22
|
"range.last7": "最近 7 天",
|
|
23
|
+
"range.yesterday": "昨天",
|
|
24
|
+
"range.thisWeek": "本周",
|
|
23
25
|
"range.days": "天",
|
|
24
26
|
"range.total": "共",
|
|
25
27
|
"git.title": "Git 仓库",
|
|
@@ -32,6 +34,15 @@ window.__ModuleLoader__.load({
|
|
|
32
34
|
"git.empty": "还没有 Git 仓库。",
|
|
33
35
|
"git.mine": "仅统计我的提交",
|
|
34
36
|
"git.mineHint": "按各仓库 git config user.email / user.name 过滤",
|
|
37
|
+
"git.excludeMerges": "排除 merge commit",
|
|
38
|
+
"git.authorEmails": "作者邮箱(可选)",
|
|
39
|
+
"git.authorEmailsHint": "留空自动读取各仓库 git config;多个邮箱用逗号分隔",
|
|
40
|
+
"test.button": "测试数据源",
|
|
41
|
+
"test.testing": "测试中…",
|
|
42
|
+
"test.failed": "测试数据源失败",
|
|
43
|
+
"test.ok": "正常",
|
|
44
|
+
cancel: "取消",
|
|
45
|
+
retry: "重试",
|
|
35
46
|
"scan.open": "扫描目录",
|
|
36
47
|
"scan.title": "从目录批量添加",
|
|
37
48
|
"scan.desc": "扫描目录下最多 6 层的 Git 仓库,已存在的会自动跳过。",
|
|
@@ -57,13 +68,35 @@ window.__ModuleLoader__.load({
|
|
|
57
68
|
"ai.model": "当前模型:",
|
|
58
69
|
"ai.unavailable": "当前没有可用的默认模型。",
|
|
59
70
|
"ai.cost": "会增加生成时间和模型 token 消耗。",
|
|
71
|
+
"ai.template": "分析风格",
|
|
72
|
+
"ai.template.concise": "简洁",
|
|
73
|
+
"ai.template.detailed": "详细",
|
|
74
|
+
"ai.template.manager": "管理汇报",
|
|
75
|
+
"ai.modelSelect": "模型",
|
|
76
|
+
"ai.defaultModel": "默认模型",
|
|
77
|
+
"schedule.title": "自动生成",
|
|
78
|
+
"schedule.desc": "在 DSH 运行期间按设定时间自动生成报告,并保存到本地历史目录。",
|
|
79
|
+
"schedule.enable": "启用自动生成",
|
|
80
|
+
"schedule.daily": "每天",
|
|
81
|
+
"schedule.weekly": "每周一",
|
|
82
|
+
"schedule.time": "生成时间",
|
|
83
|
+
"schedule.hint": "自动生成使用当前保存的数据源、AI 设置和格式。",
|
|
84
|
+
"datasource.title": "数据源配置",
|
|
85
|
+
"datasource.desc": "统一管理 Git 仓库与本地文档,生成时合并到同一份日报。",
|
|
86
|
+
"datasource.git": "Git 仓库",
|
|
87
|
+
"datasource.docs": "本地文档",
|
|
60
88
|
"progress.start": "正在创建生成任务…",
|
|
61
89
|
"progress.done": "生成完成,正在下载…",
|
|
62
90
|
"progress.preview": "生成完成,请预览并编辑后导出 XLSX。",
|
|
63
91
|
"progress.hint": "AI 分析需要调用模型,耗时通常比普通汇总更长;请保持页面打开。",
|
|
64
92
|
optional: "可选",
|
|
65
93
|
remove: "移除",
|
|
66
|
-
generate: "
|
|
94
|
+
generate: "生成日报",
|
|
95
|
+
"format.label": "格式",
|
|
96
|
+
"format.xlsx": "XLSX 工作簿",
|
|
97
|
+
"format.markdown": "Markdown 文档",
|
|
98
|
+
"format.csv": "CSV 表格",
|
|
99
|
+
"format.html": "HTML 网页",
|
|
67
100
|
generating: "正在收集并生成…",
|
|
68
101
|
"save.saving": "正在保存设置…",
|
|
69
102
|
"save.savingShort": "正在保存…",
|
|
@@ -85,7 +118,7 @@ window.__ModuleLoader__.load({
|
|
|
85
118
|
"result.ai.failed": "AI 分析未完成",
|
|
86
119
|
"preview.title": "生成结果预览",
|
|
87
120
|
"preview.desc": "先检查并编辑每日 AI 摘要、补充非代码工作,再导出 XLSX。",
|
|
88
|
-
"preview.export": "导出
|
|
121
|
+
"preview.export": "导出",
|
|
89
122
|
"preview.expandAll": "全部展开",
|
|
90
123
|
"preview.collapseAll": "全部收起",
|
|
91
124
|
"preview.exporting": "正在导出…",
|
|
@@ -128,6 +161,8 @@ window.__ModuleLoader__.load({
|
|
|
128
161
|
"range.thisMonth": "This month",
|
|
129
162
|
"range.lastMonth": "Last month",
|
|
130
163
|
"range.last7": "Last 7 days",
|
|
164
|
+
"range.yesterday": "Yesterday",
|
|
165
|
+
"range.thisWeek": "This week",
|
|
131
166
|
"range.days": "days",
|
|
132
167
|
"range.total": "Total",
|
|
133
168
|
"git.title": "Git repositories",
|
|
@@ -140,6 +175,15 @@ window.__ModuleLoader__.load({
|
|
|
140
175
|
"git.empty": "No repositories yet.",
|
|
141
176
|
"git.mine": "Only my commits",
|
|
142
177
|
"git.mineHint": "Filter by each repository's git config user.email / user.name",
|
|
178
|
+
"git.excludeMerges": "Exclude merge commits",
|
|
179
|
+
"git.authorEmails": "Author emails (optional)",
|
|
180
|
+
"git.authorEmailsHint": "Leave empty to read each repository's git config; separate multiple emails with commas",
|
|
181
|
+
"test.button": "Test sources",
|
|
182
|
+
"test.testing": "Testing…",
|
|
183
|
+
"test.failed": "Could not test data sources",
|
|
184
|
+
"test.ok": "OK",
|
|
185
|
+
cancel: "Cancel",
|
|
186
|
+
retry: "Retry",
|
|
143
187
|
"scan.open": "Scan directory",
|
|
144
188
|
"scan.title": "Add repositories from a directory",
|
|
145
189
|
"scan.desc": "Scans up to 6 levels deep for Git repositories and skips ones already added.",
|
|
@@ -165,13 +209,35 @@ window.__ModuleLoader__.load({
|
|
|
165
209
|
"ai.model": "Current model: ",
|
|
166
210
|
"ai.unavailable": "No default model is available.",
|
|
167
211
|
"ai.cost": "This adds generation time and model token usage.",
|
|
212
|
+
"ai.template": "Analysis style",
|
|
213
|
+
"ai.template.concise": "Concise",
|
|
214
|
+
"ai.template.detailed": "Detailed",
|
|
215
|
+
"ai.template.manager": "Manager summary",
|
|
216
|
+
"ai.modelSelect": "Model",
|
|
217
|
+
"ai.defaultModel": "Default model",
|
|
218
|
+
"schedule.title": "Scheduled generation",
|
|
219
|
+
"schedule.desc": "While DSH is running, generate a report at the configured time and save it to the local history folder.",
|
|
220
|
+
"schedule.enable": "Enable scheduled generation",
|
|
221
|
+
"schedule.daily": "Daily",
|
|
222
|
+
"schedule.weekly": "Every Monday",
|
|
223
|
+
"schedule.time": "Time",
|
|
224
|
+
"schedule.hint": "Scheduled runs use the saved sources, AI settings and format.",
|
|
225
|
+
"datasource.title": "Data sources",
|
|
226
|
+
"datasource.desc": "Manage Git repositories and local documents together; both feed the same report.",
|
|
227
|
+
"datasource.git": "Git repositories",
|
|
228
|
+
"datasource.docs": "Local documents",
|
|
168
229
|
"progress.start": "Creating the report job…",
|
|
169
230
|
"progress.done": "Done. Preparing the download…",
|
|
170
231
|
"progress.preview": "Generated. Review, edit, then export the XLSX.",
|
|
171
232
|
"progress.hint": "AI analysis calls a model, so it usually takes longer than a plain digest. Keep this page open.",
|
|
172
233
|
optional: "optional",
|
|
173
234
|
remove: "Remove",
|
|
174
|
-
generate: "Generate
|
|
235
|
+
generate: "Generate report",
|
|
236
|
+
"format.label": "Format",
|
|
237
|
+
"format.xlsx": "XLSX workbook",
|
|
238
|
+
"format.markdown": "Markdown document",
|
|
239
|
+
"format.csv": "CSV table",
|
|
240
|
+
"format.html": "HTML page",
|
|
175
241
|
generating: "Collecting and generating…",
|
|
176
242
|
"save.saving": "Saving settings…",
|
|
177
243
|
"save.savingShort": "Saving…",
|
|
@@ -193,7 +259,7 @@ window.__ModuleLoader__.load({
|
|
|
193
259
|
"result.ai.failed": "AI analysis did not complete",
|
|
194
260
|
"preview.title": "Review before export",
|
|
195
261
|
"preview.desc": "Check and edit each day's AI summary, add non-code work, then export the XLSX.",
|
|
196
|
-
"preview.export": "Export
|
|
262
|
+
"preview.export": "Export",
|
|
197
263
|
"preview.expandAll": "Expand all",
|
|
198
264
|
"preview.collapseAll": "Collapse all",
|
|
199
265
|
"preview.exporting": "Exporting…",
|
|
@@ -258,6 +324,19 @@ window.__ModuleLoader__.load({
|
|
|
258
324
|
return { start: formatDate(start), end: formatDate(now) };
|
|
259
325
|
}
|
|
260
326
|
|
|
327
|
+
function yesterdayRange() {
|
|
328
|
+
const now = new Date();
|
|
329
|
+
const yesterday = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1);
|
|
330
|
+
return { start: formatDate(yesterday), end: formatDate(yesterday) };
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function thisWeekRange() {
|
|
334
|
+
const now = new Date();
|
|
335
|
+
const mondayOffset = (now.getDay() + 6) % 7;
|
|
336
|
+
const monday = new Date(now.getFullYear(), now.getMonth(), now.getDate() - mondayOffset);
|
|
337
|
+
return { start: formatDate(monday), end: formatDate(now) };
|
|
338
|
+
}
|
|
339
|
+
|
|
261
340
|
function daysBetween(start, end) {
|
|
262
341
|
if (!start || !end) return 0;
|
|
263
342
|
const first = new Date(start + "T00:00:00");
|
|
@@ -286,10 +365,22 @@ window.__ModuleLoader__.load({
|
|
|
286
365
|
};
|
|
287
366
|
})
|
|
288
367
|
: [];
|
|
289
|
-
return {
|
|
368
|
+
return {
|
|
369
|
+
git,
|
|
370
|
+
docs,
|
|
371
|
+
mineOnly: source.mineOnly !== false,
|
|
372
|
+
excludeMerges: source.excludeMerges !== false,
|
|
373
|
+
authorEmails: typeof source.authorEmails === "string" ? source.authorEmails : "",
|
|
374
|
+
aiTemplate: ["concise", "detailed", "manager"].includes(source.aiTemplate) ? source.aiTemplate : "concise",
|
|
375
|
+
aiProvider: typeof source.aiProvider === "string" ? source.aiProvider : "",
|
|
376
|
+
aiModel: typeof source.aiModel === "string" ? source.aiModel : "",
|
|
377
|
+
scheduleEnabled: source.scheduleEnabled === true,
|
|
378
|
+
scheduleFrequency: source.scheduleFrequency === "weekly" ? "weekly" : "daily",
|
|
379
|
+
scheduleTime: typeof source.scheduleTime === "string" ? source.scheduleTime : "18:30",
|
|
380
|
+
};
|
|
290
381
|
}
|
|
291
382
|
|
|
292
|
-
function cleanSettings(settings, aiEnabled, range) {
|
|
383
|
+
function cleanSettings(settings, aiEnabled, range, format) {
|
|
293
384
|
return {
|
|
294
385
|
git: settings.git
|
|
295
386
|
.map((item) => ({
|
|
@@ -306,6 +397,15 @@ window.__ModuleLoader__.load({
|
|
|
306
397
|
.filter((item) => item.location.length > 0),
|
|
307
398
|
aiEnabled: aiEnabled !== false,
|
|
308
399
|
mineOnly: settings.mineOnly !== false,
|
|
400
|
+
excludeMerges: settings.excludeMerges !== false,
|
|
401
|
+
authorEmails: (settings.authorEmails || "").trim(),
|
|
402
|
+
aiTemplate: ["concise", "detailed", "manager"].includes(settings.aiTemplate) ? settings.aiTemplate : "concise",
|
|
403
|
+
aiProvider: (settings.aiProvider || "").trim(),
|
|
404
|
+
aiModel: (settings.aiModel || "").trim(),
|
|
405
|
+
scheduleEnabled: settings.scheduleEnabled === true,
|
|
406
|
+
scheduleFrequency: settings.scheduleFrequency === "weekly" ? "weekly" : "daily",
|
|
407
|
+
scheduleTime: /^\d{2}:\d{2}$/.test(settings.scheduleTime) ? settings.scheduleTime : "18:30",
|
|
408
|
+
format: FORMAT_OPTIONS.includes(format) ? format : "xlsx",
|
|
309
409
|
range:
|
|
310
410
|
range && typeof range.start === "string" && typeof range.end === "string"
|
|
311
411
|
? { start: range.start, end: range.end }
|
|
@@ -339,6 +439,23 @@ window.__ModuleLoader__.load({
|
|
|
339
439
|
}
|
|
340
440
|
}
|
|
341
441
|
|
|
442
|
+
const FORMAT_OPTIONS = ["xlsx", "markdown", "csv", "html"];
|
|
443
|
+
|
|
444
|
+
function formatLabel(format, t) {
|
|
445
|
+
if (format === "markdown") return t("format.markdown", "Markdown");
|
|
446
|
+
if (format === "csv") return t("format.csv", "CSV");
|
|
447
|
+
if (format === "html") return t("format.html", "HTML");
|
|
448
|
+
return t("format.xlsx", "XLSX");
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
function formatExtension(format) {
|
|
452
|
+
return format === "markdown" ? "md" : format;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
function formatShortLabel(format) {
|
|
456
|
+
return format === "markdown" ? "Markdown" : format.toUpperCase();
|
|
457
|
+
}
|
|
458
|
+
|
|
342
459
|
function useTranslate(ctx) {
|
|
343
460
|
const [, force] = React.useState(0);
|
|
344
461
|
React.useEffect(() => {
|
|
@@ -359,8 +476,20 @@ window.__ModuleLoader__.load({
|
|
|
359
476
|
".dsdr-hero-copy { flex: 1; min-width: 0; }",
|
|
360
477
|
".dsdr-hero-action { display: flex; flex: none; flex-direction: column; align-items: flex-end; gap: 5px; margin-left: auto; }",
|
|
361
478
|
".dsdr-hero-buttons { display: flex; align-items: center; justify-content: flex-end; flex-wrap: wrap; gap: 8px; }",
|
|
362
|
-
".dsdr-save-btn { min-height: 38px; }",
|
|
363
|
-
".dsdr-
|
|
479
|
+
".dsdr-save-btn { min-height: 38px; background: #3b4046; border-color: #3b4046; color: #ffffff; font-weight: 600; }",
|
|
480
|
+
".dsdr-save-btn:hover { background: #292d31; border-color: #292d31; }",
|
|
481
|
+
".dsdr-save-btn:disabled { background: #666b70; border-color: #666b70; color: #ffffff; opacity: .8; }",
|
|
482
|
+
".dsdr-generate { min-height: 38px; padding: 9px 16px; font-size: 13px; white-space: nowrap; }",
|
|
483
|
+
".dsdr-btn-with-icon { display: inline-flex; align-items: center; justify-content: center; gap: 6px; }",
|
|
484
|
+
".dsdr-split { position: relative; display: inline-flex; align-items: stretch; }",
|
|
485
|
+
".dsdr-split .dsdr-generate { border-radius: 8px 0 0 8px; }",
|
|
486
|
+
".dsdr-split-toggle { min-height: 38px; padding: 0 9px; border-radius: 0 8px 8px 0; border-left: 1px solid rgba(255, 255, 255, .32); }",
|
|
487
|
+
".dsdr-menu { position: absolute; top: calc(100% + 6px); right: 0; z-index: 30; min-width: 190px; padding: 5px; border: 1px solid var(--dsw-alias-border-l2); border-radius: 10px; background: var(--dsw-alias-bg-overlay); box-shadow: 0 10px 28px rgba(0, 0, 0, .2); }",
|
|
488
|
+
".dsdr-menu-title { padding: 5px 8px 4px; color: var(--dsw-alias-label-secondary); font-size: 11px; line-height: 16px; }",
|
|
489
|
+
".dsdr-menu-item { display: flex; align-items: center; justify-content: space-between; gap: 8px; width: 100%; padding: 7px 8px; border: 0; border-radius: 7px; background: transparent; color: var(--dsw-alias-label-primary); font: inherit; font-size: 12px; line-height: 18px; text-align: left; cursor: pointer; }",
|
|
490
|
+
".dsdr-menu-item:hover { background: var(--dsw-alias-bg-layer-2); }",
|
|
491
|
+
".dsdr-menu-item.is-active { background: var(--dsw-alias-bg-layer-2); font-weight: 600; }",
|
|
492
|
+
".dsdr-menu-check { width: 14px; color: #146b5a; text-align: center; }",
|
|
364
493
|
".dsdr-save { color: var(--dsw-alias-label-secondary); font-size: 11px; line-height: 16px; }",
|
|
365
494
|
".dsdr-mark { flex: none; width: 42px; height: 42px; border-radius: 11px; display: grid; place-items: center; background: var(--dsw-alias-bg-layer-2); border: 1px solid var(--dsw-alias-border-l1); }",
|
|
366
495
|
".dsdr-title { margin: 0; font-size: 22px; line-height: 30px; font-weight: 600; }",
|
|
@@ -368,7 +497,15 @@ window.__ModuleLoader__.load({
|
|
|
368
497
|
".dsdr-card { border: 1px solid var(--dsw-alias-border-l1); border-radius: 12px; background: var(--dsw-alias-bg-layer-1); padding: 16px 18px; }",
|
|
369
498
|
".dsdr-card-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; margin-bottom: 12px; }",
|
|
370
499
|
".dsdr-card-title { font-size: 14px; line-height: 22px; font-weight: 600; }",
|
|
500
|
+
".dsdr-ai-title { display: inline-block; font-size: 15px; font-weight: 700; letter-spacing: .03em; background: linear-gradient(100deg, #0f766e 0%, #06b6d4 22%, #3b82f6 46%, #8b5cf6 70%, #0f766e 100%); background-size: 320% 100%; -webkit-background-clip: text; background-clip: text; color: transparent; -webkit-text-fill-color: transparent; filter: drop-shadow(0 0 7px rgba(20, 184, 166, .22)); animation: dsdr-ai-gradient 5s ease-in-out infinite; }",
|
|
501
|
+
"@keyframes dsdr-ai-gradient { 0% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } 100% { background-position: 0% 50%; } }",
|
|
502
|
+
"@media (prefers-reduced-motion: reduce) { .dsdr-ai-title { animation: none; } }",
|
|
371
503
|
".dsdr-card-desc { margin-top: 2px; color: var(--dsw-alias-label-secondary); font-size: 12px; line-height: 18px; }",
|
|
504
|
+
".dsdr-title-with-info { display: flex; align-items: center; gap: 6px; min-width: 0; }",
|
|
505
|
+
".dsdr-info { position: relative; display: inline-flex; flex: none; align-items: center; justify-content: center; width: 15px; height: 15px; border: 1px solid var(--dsw-alias-border-l2); border-radius: 50%; color: var(--dsw-alias-label-secondary); font-family: Georgia, serif; font-size: 10px; font-style: italic; font-weight: 700; line-height: 1; cursor: help; }",
|
|
506
|
+
".dsdr-info:hover, .dsdr-info:focus-visible { border-color: #146b5a; color: #146b5a; outline: none; }",
|
|
507
|
+
".dsdr-info::after { content: attr(data-tip); position: absolute; top: calc(100% + 8px); left: 50%; z-index: 80; width: max-content; max-width: 280px; padding: 7px 9px; border: 1px solid var(--dsw-alias-border-l2); border-radius: 7px; background: var(--dsw-alias-bg-overlay); color: var(--dsw-alias-label-primary); box-shadow: 0 7px 20px rgba(0, 0, 0, .2); font-family: inherit; font-size: 11px; font-style: normal; font-weight: 400; line-height: 17px; text-align: left; white-space: normal; opacity: 0; visibility: hidden; transform: translate(-50%, -3px); transition: opacity .15s ease, transform .15s ease, visibility .15s ease; pointer-events: none; }",
|
|
508
|
+
".dsdr-info:hover::after, .dsdr-info:focus-visible::after { opacity: 1; visibility: visible; transform: translate(-50%, 0); }",
|
|
372
509
|
".dsdr-range { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) auto; gap: 10px; align-items: end; }",
|
|
373
510
|
".dsdr-field { display: flex; flex-direction: column; gap: 4px; min-width: 0; }",
|
|
374
511
|
".dsdr-label { color: var(--dsw-alias-label-secondary); font-size: 12px; line-height: 18px; }",
|
|
@@ -380,9 +517,9 @@ window.__ModuleLoader__.load({
|
|
|
380
517
|
".dsdr-btn:hover { background: var(--dsw-alias-bg-layer-1); border-color: var(--dsw-alias-brand-primary); }",
|
|
381
518
|
".dsdr-btn:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary); outline-offset: 1px; }",
|
|
382
519
|
".dsdr-btn:disabled { opacity: .7; cursor: not-allowed; }",
|
|
383
|
-
".dsdr-btn-primary { background: #
|
|
384
|
-
".dsdr-btn-primary:hover { background: #
|
|
385
|
-
".dsdr-btn-primary:disabled { background: #
|
|
520
|
+
".dsdr-btn-primary { background: #146b5a; border-color: #146b5a; color: #ffffff; font-weight: 600; box-shadow: 0 2px 6px rgba(0, 0, 0, .18); }",
|
|
521
|
+
".dsdr-btn-primary:hover { background: #0f5547; border-color: #0f5547; opacity: 1; }",
|
|
522
|
+
".dsdr-btn-primary:disabled { background: #6b8a82; border-color: #6b8a82; color: #ffffff; opacity: .8; }",
|
|
386
523
|
".dsdr-btn-danger { border-color: transparent; background: transparent; color: var(--dsw-alias-state-error-primary); font-weight: 600; }",
|
|
387
524
|
".dsdr-btn-danger:hover { background: var(--dsw-alias-bg-layer-2); border-color: transparent; }",
|
|
388
525
|
".dsdr-check { display: inline-flex; align-items: center; gap: 7px; color: var(--dsw-alias-label-primary); font-size: 12px; line-height: 20px; cursor: pointer; white-space: nowrap; }",
|
|
@@ -416,6 +553,16 @@ window.__ModuleLoader__.load({
|
|
|
416
553
|
".dsdr-manual-remove { border: 0; background: transparent; color: var(--dsw-alias-state-error-primary); font: inherit; font-size: 14px; line-height: 16px; padding: 0 1px; cursor: pointer; }",
|
|
417
554
|
".dsdr-ai-text { margin-top: 5px; color: var(--dsw-alias-label-primary); font-size: 12px; line-height: 20px; white-space: pre-wrap; }",
|
|
418
555
|
".dsdr-filter-row { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 12px; margin: 0 0 10px; }",
|
|
556
|
+
".dsdr-datasource-tabs { margin-bottom: -16px; border-bottom-left-radius: 0; border-bottom-right-radius: 0; }",
|
|
557
|
+
".dsdr-datasource-panel { border-top-left-radius: 0; border-top-right-radius: 0; }",
|
|
558
|
+
".dsdr-tabs { display: flex; align-items: center; gap: 4px; margin-top: 12px; border-bottom: 1px solid var(--dsw-alias-border-l1); }",
|
|
559
|
+
".dsdr-tab { display: inline-flex; align-items: center; gap: 6px; padding: 7px 12px 8px; border: 0; border-bottom: 2px solid transparent; background: transparent; color: var(--dsw-alias-label-secondary); font: inherit; font-size: 12px; line-height: 18px; cursor: pointer; }",
|
|
560
|
+
".dsdr-tab:hover { color: var(--dsw-alias-label-primary); }",
|
|
561
|
+
".dsdr-tab.is-active { color: var(--dsw-alias-label-primary); border-bottom-color: #146b5a; font-weight: 600; }",
|
|
562
|
+
".dsdr-tab-count { display: inline-flex; min-width: 17px; height: 17px; align-items: center; justify-content: center; padding: 0 5px; border-radius: 999px; background: var(--dsw-alias-bg-layer-2); color: var(--dsw-alias-label-secondary); font-size: 10px; line-height: 17px; }",
|
|
563
|
+
".dsdr-filter-author { flex: 1 1 280px; min-width: 220px; }",
|
|
564
|
+
".dsdr-ai-options { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 10px; margin-top: 10px; }",
|
|
565
|
+
".dsdr-test-results { display: flex; flex-direction: column; gap: 5px; margin: 0 0 10px; padding: 8px 10px; border: 1px solid var(--dsw-alias-border-l1); border-radius: 8px; background: var(--dsw-alias-bg-layer-2); }",
|
|
419
566
|
".dsdr-scan { display: flex; flex-direction: column; gap: 7px; margin: 0 0 12px; padding: 12px; border: 1px dashed var(--dsw-alias-border-l2); border-radius: 10px; background: var(--dsw-alias-bg-layer-2); }",
|
|
420
567
|
".dsdr-scan-title { font-size: 13px; line-height: 20px; font-weight: 600; }",
|
|
421
568
|
".dsdr-scan-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 8px; align-items: end; }",
|
|
@@ -423,6 +570,7 @@ window.__ModuleLoader__.load({
|
|
|
423
570
|
".dsdr-icon-btn { width: 32px; height: 32px; padding: 0; display: grid; place-items: center; line-height: 1; font-size: 18px; }",
|
|
424
571
|
".dsdr-hint { color: var(--dsw-alias-label-secondary); font-size: 12px; line-height: 18px; }",
|
|
425
572
|
".dsdr-error { border: 1px solid var(--dsw-alias-state-error-primary); border-radius: 9px; color: var(--dsw-alias-state-error-primary); background: transparent; padding: 9px 12px; font-size: 12px; line-height: 18px; white-space: pre-wrap; }",
|
|
573
|
+
".dsdr-error-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; }",
|
|
426
574
|
".dsdr-notice { color: var(--dsw-alias-label-secondary); font-size: 12px; line-height: 18px; }",
|
|
427
575
|
".dsdr-ok { color: var(--dsw-alias-state-success-primary); }",
|
|
428
576
|
".dsdr-warn { color: var(--dsw-alias-state-warn-primary); }",
|
|
@@ -443,7 +591,7 @@ window.__ModuleLoader__.load({
|
|
|
443
591
|
".dsdr-code { font-family: ui-monospace, SFMono-Regular, Consolas, monospace; font-size: 11px; }",
|
|
444
592
|
".dsdr-footer { color: var(--dsw-alias-label-secondary); font-size: 11px; line-height: 17px; }",
|
|
445
593
|
".dsdr-spacer { flex: 1; }",
|
|
446
|
-
"@media (max-width: 780px) { .dsdr-page { padding: 20px 16px 40px; } .dsdr-hero-action { flex: 1 0 100%; width: 100%; align-items: stretch; margin-left: 0; } .dsdr-hero-buttons { width: 100%; } .dsdr-hero-buttons .dsdr-btn { flex: 1; } .dsdr-generate { width: 100%; } .dsdr-range { grid-template-columns: 1fr 1fr; } .dsdr-range .dsdr-actions { grid-column: 1 / -1; } .dsdr-stats { grid-template-columns: 1fr 1fr; } .dsdr-scan-row { grid-template-columns: 1fr; } .dsdr-manual-add { grid-template-columns: 1fr; } .dsdr-source, .dsdr-source-docs { grid-template-columns: 1fr; } .dsdr-source .dsdr-icon-btn { justify-self: end; } }",
|
|
594
|
+
"@media (max-width: 780px) { .dsdr-page { padding: 20px 16px 40px; } .dsdr-hero-action { flex: 1 0 100%; width: 100%; align-items: stretch; margin-left: 0; } .dsdr-hero-buttons { width: 100%; } .dsdr-hero-buttons .dsdr-btn { flex: 1; } .dsdr-split { flex: 1; } .dsdr-split .dsdr-generate { flex: 1; } .dsdr-generate { width: 100%; } .dsdr-ai-options { grid-template-columns: 1fr; } .dsdr-range { grid-template-columns: 1fr 1fr; } .dsdr-range .dsdr-actions { grid-column: 1 / -1; } .dsdr-stats { grid-template-columns: 1fr 1fr; } .dsdr-scan-row { grid-template-columns: 1fr; } .dsdr-manual-add { grid-template-columns: 1fr; } .dsdr-source, .dsdr-source-docs { grid-template-columns: 1fr; } .dsdr-source .dsdr-icon-btn { justify-self: end; } }",
|
|
447
595
|
].join(NL);
|
|
448
596
|
|
|
449
597
|
function installStyles(ctx) {
|
|
@@ -488,12 +636,84 @@ window.__ModuleLoader__.load({
|
|
|
488
636
|
);
|
|
489
637
|
}
|
|
490
638
|
|
|
639
|
+
function SaveIcon(props) {
|
|
640
|
+
const size = Number(props && props.size) > 0 ? Number(props.size) : 15;
|
|
641
|
+
return h(
|
|
642
|
+
"svg",
|
|
643
|
+
{
|
|
644
|
+
viewBox: "0 0 24 24",
|
|
645
|
+
width: size,
|
|
646
|
+
height: size,
|
|
647
|
+
fill: "none",
|
|
648
|
+
stroke: "currentColor",
|
|
649
|
+
strokeWidth: 1.8,
|
|
650
|
+
strokeLinecap: "round",
|
|
651
|
+
strokeLinejoin: "round",
|
|
652
|
+
"aria-hidden": true,
|
|
653
|
+
},
|
|
654
|
+
h("path", { d: "M5 3.5h11l3.5 3.5v13.5H5z" }),
|
|
655
|
+
h("path", { d: "M8 3.5v5h7v-5M8 20.5v-6h8v6" }),
|
|
656
|
+
);
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
function GenerateIcon(props) {
|
|
660
|
+
const size = Number(props && props.size) > 0 ? Number(props.size) : 15;
|
|
661
|
+
return h(
|
|
662
|
+
"svg",
|
|
663
|
+
{
|
|
664
|
+
viewBox: "0 0 24 24",
|
|
665
|
+
width: size,
|
|
666
|
+
height: size,
|
|
667
|
+
fill: "none",
|
|
668
|
+
stroke: "currentColor",
|
|
669
|
+
strokeWidth: 1.8,
|
|
670
|
+
strokeLinecap: "round",
|
|
671
|
+
strokeLinejoin: "round",
|
|
672
|
+
"aria-hidden": true,
|
|
673
|
+
},
|
|
674
|
+
h("path", { d: "M6 3.5h8l4 4v13H6z" }),
|
|
675
|
+
h("path", { d: "M14 3.5v4h4M9 12h6M9 15.5h4" }),
|
|
676
|
+
h("path", { d: "M18.5 16.5v4M16.5 18.5h4" }),
|
|
677
|
+
);
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
function ChevronIcon(props) {
|
|
681
|
+
const size = Number(props && props.size) > 0 ? Number(props.size) : 12;
|
|
682
|
+
return h(
|
|
683
|
+
"svg",
|
|
684
|
+
{
|
|
685
|
+
viewBox: "0 0 24 24",
|
|
686
|
+
width: size,
|
|
687
|
+
height: size,
|
|
688
|
+
fill: "none",
|
|
689
|
+
stroke: "currentColor",
|
|
690
|
+
strokeWidth: 2,
|
|
691
|
+
strokeLinecap: "round",
|
|
692
|
+
strokeLinejoin: "round",
|
|
693
|
+
"aria-hidden": true,
|
|
694
|
+
},
|
|
695
|
+
h("path", { d: "M6 9l6 6 6-6" }),
|
|
696
|
+
);
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
function InfoDot(props) {
|
|
700
|
+
return h("span", {
|
|
701
|
+
className: "dsdr-info",
|
|
702
|
+
role: "img",
|
|
703
|
+
tabIndex: 0,
|
|
704
|
+
"aria-label": props.text,
|
|
705
|
+
"data-tip": props.text,
|
|
706
|
+
}, "i");
|
|
707
|
+
}
|
|
708
|
+
|
|
491
709
|
function DailyReportView(props) {
|
|
492
710
|
const ctx = props.ctx;
|
|
493
711
|
const t = useTranslate(ctx);
|
|
494
712
|
const [settings, setSettings] = React.useState({ git: [], docs: [] });
|
|
495
713
|
const [range, setRange] = React.useState(currentMonthRange);
|
|
496
|
-
const [
|
|
714
|
+
const [exportFormat, setExportFormat] = React.useState("xlsx");
|
|
715
|
+
const [formatMenuOpen, setFormatMenuOpen] = React.useState(false);
|
|
716
|
+
const [aiInfo, setAiInfo] = React.useState({ available: false, provider: "", model: "", models: [] });
|
|
497
717
|
const [useAi, setUseAi] = React.useState(true);
|
|
498
718
|
const [loading, setLoading] = React.useState(true);
|
|
499
719
|
const [loadError, setLoadError] = React.useState("");
|
|
@@ -514,11 +734,17 @@ window.__ModuleLoader__.load({
|
|
|
514
734
|
const [manualDate, setManualDate] = React.useState("");
|
|
515
735
|
const [manualText, setManualText] = React.useState("");
|
|
516
736
|
const [expandedPreviewDays, setExpandedPreviewDays] = React.useState({});
|
|
737
|
+
const [testingSources, setTestingSources] = React.useState(false);
|
|
738
|
+
const [testResults, setTestResults] = React.useState(null);
|
|
739
|
+
const [testError, setTestError] = React.useState("");
|
|
740
|
+
const [dataSourceTab, setDataSourceTab] = React.useState("git");
|
|
517
741
|
const firstSave = React.useRef(true);
|
|
518
742
|
const resultRef = React.useRef(null);
|
|
519
743
|
const pollAbortRef = React.useRef(null);
|
|
520
744
|
const saveAbortRef = React.useRef(null);
|
|
521
745
|
const saveTimerRef = React.useRef(null);
|
|
746
|
+
const formatMenuRef = React.useRef(null);
|
|
747
|
+
const jobIdRef = React.useRef("");
|
|
522
748
|
const sessionId = typeof props.sessionId === "string" ? props.sessionId : "";
|
|
523
749
|
const localeIsEnglish = currentLocaleId(ctx).toLowerCase().startsWith("en");
|
|
524
750
|
|
|
@@ -535,6 +761,17 @@ window.__ModuleLoader__.load({
|
|
|
535
761
|
};
|
|
536
762
|
}, []);
|
|
537
763
|
|
|
764
|
+
React.useEffect(() => {
|
|
765
|
+
if (!formatMenuOpen) return;
|
|
766
|
+
const close = (event) => {
|
|
767
|
+
if (formatMenuRef.current && !formatMenuRef.current.contains(event.target)) {
|
|
768
|
+
setFormatMenuOpen(false);
|
|
769
|
+
}
|
|
770
|
+
};
|
|
771
|
+
document.addEventListener("mousedown", close);
|
|
772
|
+
return () => document.removeEventListener("mousedown", close);
|
|
773
|
+
}, [formatMenuOpen]);
|
|
774
|
+
|
|
538
775
|
React.useEffect(() => {
|
|
539
776
|
let alive = true;
|
|
540
777
|
const query = sessionId.length > 0 ? "?sessionId=" + encodeURIComponent(sessionId) : "";
|
|
@@ -547,11 +784,16 @@ window.__ModuleLoader__.load({
|
|
|
547
784
|
if (!alive) return;
|
|
548
785
|
setSettings(normalizeClientSettings(payload && payload.settings));
|
|
549
786
|
setUseAi(!(payload && payload.settings && payload.settings.aiEnabled === false));
|
|
787
|
+
const savedFormat = payload && payload.settings && payload.settings.format;
|
|
788
|
+
if (typeof savedFormat === "string" && FORMAT_OPTIONS.includes(savedFormat)) {
|
|
789
|
+
setExportFormat(savedFormat);
|
|
790
|
+
}
|
|
550
791
|
const nextAi = payload && payload.ai ? payload.ai : { available: false, provider: "", model: "" };
|
|
551
792
|
setAiInfo({
|
|
552
793
|
available: nextAi.available === true,
|
|
553
794
|
provider: typeof nextAi.provider === "string" ? nextAi.provider : "",
|
|
554
795
|
model: typeof nextAi.model === "string" ? nextAi.model : "",
|
|
796
|
+
models: Array.isArray(nextAi.models) ? nextAi.models : [],
|
|
555
797
|
});
|
|
556
798
|
const savedRange = payload && payload.settings && payload.settings.range;
|
|
557
799
|
if (savedRange && typeof savedRange.start === "string" && typeof savedRange.end === "string") {
|
|
@@ -590,7 +832,7 @@ window.__ModuleLoader__.load({
|
|
|
590
832
|
saveTimerRef.current = null;
|
|
591
833
|
}
|
|
592
834
|
};
|
|
593
|
-
}, [settings, useAi, range, loading]);
|
|
835
|
+
}, [settings, useAi, range, exportFormat, loading]);
|
|
594
836
|
|
|
595
837
|
function updateGit(index, patch) {
|
|
596
838
|
setSettings((previous) => ({
|
|
@@ -637,7 +879,7 @@ window.__ModuleLoader__.load({
|
|
|
637
879
|
const response = await fetch(API + "/state", {
|
|
638
880
|
method: "PUT",
|
|
639
881
|
headers: { "content-type": "application/json" },
|
|
640
|
-
body: JSON.stringify({ settings: cleanSettings(settings, useAi, range) }),
|
|
882
|
+
body: JSON.stringify({ settings: cleanSettings(settings, useAi, range, exportFormat) }),
|
|
641
883
|
signal: controller.signal,
|
|
642
884
|
});
|
|
643
885
|
if (!response.ok) throw new Error("save-failed");
|
|
@@ -658,6 +900,52 @@ window.__ModuleLoader__.load({
|
|
|
658
900
|
persistSettings();
|
|
659
901
|
}
|
|
660
902
|
|
|
903
|
+
async function testSources() {
|
|
904
|
+
if (testingSources) return;
|
|
905
|
+
setTestingSources(true);
|
|
906
|
+
setTestError("");
|
|
907
|
+
setTestResults(null);
|
|
908
|
+
try {
|
|
909
|
+
const response = await fetch(API + "/test-sources", {
|
|
910
|
+
method: "POST",
|
|
911
|
+
headers: { "content-type": "application/json", accept: "application/json" },
|
|
912
|
+
body: JSON.stringify({ settings: cleanSettings(settings, useAi, range, exportFormat), sessionId }),
|
|
913
|
+
});
|
|
914
|
+
if (!response.ok) {
|
|
915
|
+
let payload = null;
|
|
916
|
+
try {
|
|
917
|
+
payload = await response.json();
|
|
918
|
+
} catch {
|
|
919
|
+
payload = null;
|
|
920
|
+
}
|
|
921
|
+
throw new Error(errorMessageFromPayload(payload));
|
|
922
|
+
}
|
|
923
|
+
const payload = await response.json();
|
|
924
|
+
setTestResults(Array.isArray(payload.sources) ? payload.sources : []);
|
|
925
|
+
} catch (err) {
|
|
926
|
+
setTestError(err && err.message ? err.message : t("test.failed", "测试数据源失败"));
|
|
927
|
+
} finally {
|
|
928
|
+
setTestingSources(false);
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
async function cancelGeneration() {
|
|
933
|
+
const jobId = jobIdRef.current;
|
|
934
|
+
if (jobId.length === 0) return;
|
|
935
|
+
try {
|
|
936
|
+
await fetch(API + "/job/cancel", {
|
|
937
|
+
method: "POST",
|
|
938
|
+
headers: { "content-type": "application/json" },
|
|
939
|
+
body: JSON.stringify({ jobId }),
|
|
940
|
+
});
|
|
941
|
+
} catch {
|
|
942
|
+
// The local abort below still stops the UI polling.
|
|
943
|
+
}
|
|
944
|
+
if (pollAbortRef.current) pollAbortRef.current.abort();
|
|
945
|
+
setProgress(null);
|
|
946
|
+
setGenerating(false);
|
|
947
|
+
}
|
|
948
|
+
|
|
661
949
|
function toggleScan() {
|
|
662
950
|
const next = !showScan;
|
|
663
951
|
setShowScan(next);
|
|
@@ -821,7 +1109,7 @@ window.__ModuleLoader__.load({
|
|
|
821
1109
|
const response = await fetch(API + "/job/export", {
|
|
822
1110
|
method: "POST",
|
|
823
1111
|
headers: { "content-type": "application/json", accept: "application/octet-stream" },
|
|
824
|
-
body: JSON.stringify({ jobId: previewJobId, edits: previewEdits() }),
|
|
1112
|
+
body: JSON.stringify({ jobId: previewJobId, edits: previewEdits(), format: exportFormat }),
|
|
825
1113
|
});
|
|
826
1114
|
if (!response.ok) {
|
|
827
1115
|
let payload = null;
|
|
@@ -837,7 +1125,7 @@ window.__ModuleLoader__.load({
|
|
|
837
1125
|
const filenameHeader = response.headers.get("x-report-filename");
|
|
838
1126
|
const filename = filenameHeader
|
|
839
1127
|
? decodeURIComponent(filenameHeader)
|
|
840
|
-
: "日报汇总_" + preview.range.start + "_" + preview.range.end + ".
|
|
1128
|
+
: "日报汇总_" + preview.range.start + "_" + preview.range.end + "." + formatExtension(exportFormat);
|
|
841
1129
|
const blob = await response.blob();
|
|
842
1130
|
const url = window.URL.createObjectURL(blob);
|
|
843
1131
|
const next = Object.assign({}, summary, { filename, url, size: blob.size });
|
|
@@ -919,9 +1207,12 @@ window.__ModuleLoader__.load({
|
|
|
919
1207
|
});
|
|
920
1208
|
}
|
|
921
1209
|
|
|
922
|
-
async function generate() {
|
|
1210
|
+
async function generate(formatOverride) {
|
|
923
1211
|
if (generating) return;
|
|
924
|
-
const
|
|
1212
|
+
const format = FORMAT_OPTIONS.includes(formatOverride) ? formatOverride : exportFormat;
|
|
1213
|
+
setExportFormat(format);
|
|
1214
|
+
setFormatMenuOpen(false);
|
|
1215
|
+
const cleaned = cleanSettings(settings, useAi, range, format);
|
|
925
1216
|
if (cleaned.git.length === 0 && cleaned.docs.length === 0) {
|
|
926
1217
|
setError(t("error.noSources", "请至少添加一个 Git 仓库或本地文档。"));
|
|
927
1218
|
return;
|
|
@@ -971,6 +1262,7 @@ window.__ModuleLoader__.load({
|
|
|
971
1262
|
const started = await startResponse.json();
|
|
972
1263
|
const jobId = started && typeof started.jobId === "string" ? started.jobId : "";
|
|
973
1264
|
if (jobId.length === 0) throw new Error(t("error.generate", "生成失败"));
|
|
1265
|
+
jobIdRef.current = jobId;
|
|
974
1266
|
while (true) {
|
|
975
1267
|
if (controller.signal.aborted) {
|
|
976
1268
|
const aborted = new Error("aborted");
|
|
@@ -1032,6 +1324,7 @@ window.__ModuleLoader__.load({
|
|
|
1032
1324
|
setError(err && err.message ? err.message : t("error.generate", "生成失败"));
|
|
1033
1325
|
} finally {
|
|
1034
1326
|
if (pollAbortRef.current === controller) pollAbortRef.current = null;
|
|
1327
|
+
jobIdRef.current = "";
|
|
1035
1328
|
setGenerating(false);
|
|
1036
1329
|
}
|
|
1037
1330
|
}
|
|
@@ -1080,16 +1373,66 @@ window.__ModuleLoader__.load({
|
|
|
1080
1373
|
"button",
|
|
1081
1374
|
{
|
|
1082
1375
|
type: "button",
|
|
1083
|
-
className: "dsdr-btn dsdr-save-btn",
|
|
1376
|
+
className: "dsdr-btn dsdr-save-btn dsdr-btn-with-icon",
|
|
1084
1377
|
onClick: saveSettingsNow,
|
|
1085
1378
|
disabled: saveState === "saving",
|
|
1086
1379
|
},
|
|
1087
|
-
|
|
1380
|
+
h(SaveIcon, { size: 15 }),
|
|
1381
|
+
h("span", null, saveState === "saving" ? t("save.savingShort", "正在保存…") : t("save.button", "保存设置")),
|
|
1088
1382
|
),
|
|
1089
1383
|
h(
|
|
1090
|
-
"
|
|
1091
|
-
{
|
|
1092
|
-
|
|
1384
|
+
"div",
|
|
1385
|
+
{ className: "dsdr-split", ref: formatMenuRef },
|
|
1386
|
+
h(
|
|
1387
|
+
"button",
|
|
1388
|
+
{
|
|
1389
|
+
type: "button",
|
|
1390
|
+
className: "dsdr-btn dsdr-btn-primary dsdr-generate dsdr-btn-with-icon",
|
|
1391
|
+
onClick: () => generate(),
|
|
1392
|
+
disabled: generating,
|
|
1393
|
+
},
|
|
1394
|
+
h(GenerateIcon, { size: 15 }),
|
|
1395
|
+
h("span", null, generating ? t("generating", "正在收集并生成…") : t("generate", "生成日报")),
|
|
1396
|
+
),
|
|
1397
|
+
h(
|
|
1398
|
+
"button",
|
|
1399
|
+
{
|
|
1400
|
+
type: "button",
|
|
1401
|
+
className: "dsdr-btn dsdr-btn-primary dsdr-split-toggle",
|
|
1402
|
+
onClick: () => setFormatMenuOpen((open) => !open),
|
|
1403
|
+
disabled: generating,
|
|
1404
|
+
"aria-haspopup": "menu",
|
|
1405
|
+
"aria-expanded": formatMenuOpen,
|
|
1406
|
+
"aria-label": t("format.label", "格式"),
|
|
1407
|
+
},
|
|
1408
|
+
h(ChevronIcon, { size: 12 }),
|
|
1409
|
+
),
|
|
1410
|
+
formatMenuOpen
|
|
1411
|
+
? h(
|
|
1412
|
+
"div",
|
|
1413
|
+
{ className: "dsdr-menu", role: "menu" },
|
|
1414
|
+
h("div", { className: "dsdr-menu-title" }, t("format.label", "格式")),
|
|
1415
|
+
FORMAT_OPTIONS.map((format) =>
|
|
1416
|
+
h(
|
|
1417
|
+
"button",
|
|
1418
|
+
{
|
|
1419
|
+
type: "button",
|
|
1420
|
+
role: "menuitemradio",
|
|
1421
|
+
"aria-checked": exportFormat === format,
|
|
1422
|
+
className: "dsdr-menu-item" + (exportFormat === format ? " is-active" : ""),
|
|
1423
|
+
key: format,
|
|
1424
|
+
onClick: () => {
|
|
1425
|
+
setExportFormat(format);
|
|
1426
|
+
setFormatMenuOpen(false);
|
|
1427
|
+
generate(format);
|
|
1428
|
+
},
|
|
1429
|
+
},
|
|
1430
|
+
h("span", null, formatLabel(format, t)),
|
|
1431
|
+
h("span", { className: "dsdr-menu-check" }, exportFormat === format ? "✓" : ""),
|
|
1432
|
+
),
|
|
1433
|
+
),
|
|
1434
|
+
)
|
|
1435
|
+
: null,
|
|
1093
1436
|
),
|
|
1094
1437
|
),
|
|
1095
1438
|
saveText ? h("div", { className: "dsdr-save" }, saveText) : null,
|
|
@@ -1104,7 +1447,12 @@ window.__ModuleLoader__.load({
|
|
|
1104
1447
|
"div",
|
|
1105
1448
|
{ className: "dsdr-progress-head" },
|
|
1106
1449
|
h("span", null, (progress && progress.message) || t("generating", "正在收集并生成…")),
|
|
1107
|
-
h(
|
|
1450
|
+
h(
|
|
1451
|
+
"div",
|
|
1452
|
+
{ className: "dsdr-actions" },
|
|
1453
|
+
h("span", { className: "dsdr-progress-value" }, progress && !progress.indeterminate ? Math.round(Number(progress.percent) || 0) + "%" : "…"),
|
|
1454
|
+
h("button", { type: "button", className: "dsdr-btn", onClick: cancelGeneration }, t("cancel", "取消")),
|
|
1455
|
+
),
|
|
1108
1456
|
),
|
|
1109
1457
|
h(
|
|
1110
1458
|
"div",
|
|
@@ -1119,7 +1467,14 @@ window.__ModuleLoader__.load({
|
|
|
1119
1467
|
: null,
|
|
1120
1468
|
|
|
1121
1469
|
loadError ? h("div", { className: "dsdr-error" }, loadError) : null,
|
|
1122
|
-
error
|
|
1470
|
+
error
|
|
1471
|
+
? h(
|
|
1472
|
+
"div",
|
|
1473
|
+
{ className: "dsdr-error dsdr-error-row", role: "alert" },
|
|
1474
|
+
h("span", null, error),
|
|
1475
|
+
h("button", { type: "button", className: "dsdr-btn", onClick: () => generate() }, t("retry", "重试")),
|
|
1476
|
+
)
|
|
1477
|
+
: null,
|
|
1123
1478
|
|
|
1124
1479
|
preview
|
|
1125
1480
|
? h(
|
|
@@ -1131,8 +1486,12 @@ window.__ModuleLoader__.load({
|
|
|
1131
1486
|
h(
|
|
1132
1487
|
"div",
|
|
1133
1488
|
null,
|
|
1134
|
-
h(
|
|
1135
|
-
|
|
1489
|
+
h(
|
|
1490
|
+
"div",
|
|
1491
|
+
{ className: "dsdr-title-with-info" },
|
|
1492
|
+
h("div", { className: "dsdr-card-title" }, t("preview.title", "生成结果预览")),
|
|
1493
|
+
h(InfoDot, { text: t("preview.desc", "先检查并编辑每日 AI 摘要、补充非代码工作,再导出 XLSX。") }),
|
|
1494
|
+
),
|
|
1136
1495
|
),
|
|
1137
1496
|
h(
|
|
1138
1497
|
"div",
|
|
@@ -1150,7 +1509,7 @@ window.__ModuleLoader__.load({
|
|
|
1150
1509
|
h(
|
|
1151
1510
|
"button",
|
|
1152
1511
|
{ type: "button", className: "dsdr-btn dsdr-btn-primary", onClick: exportPreview, disabled: exporting || regenerating.length > 0 },
|
|
1153
|
-
exporting ? t("preview.exporting", "正在导出…") : t("preview.export", "导出
|
|
1512
|
+
exporting ? t("preview.exporting", "正在导出…") : t("preview.export", "导出") + " " + formatShortLabel(exportFormat),
|
|
1154
1513
|
),
|
|
1155
1514
|
),
|
|
1156
1515
|
),
|
|
@@ -1288,8 +1647,12 @@ window.__ModuleLoader__.load({
|
|
|
1288
1647
|
h(
|
|
1289
1648
|
"div",
|
|
1290
1649
|
null,
|
|
1291
|
-
h(
|
|
1292
|
-
|
|
1650
|
+
h(
|
|
1651
|
+
"div",
|
|
1652
|
+
{ className: "dsdr-title-with-info" },
|
|
1653
|
+
h("div", { className: "dsdr-card-title" }, t("range.title", "汇总时间范围")),
|
|
1654
|
+
h(InfoDot, { text: t("range.desc", "默认当月月初到月底;可改为任意日期范围。") }),
|
|
1655
|
+
),
|
|
1293
1656
|
),
|
|
1294
1657
|
h("div", { className: "dsdr-hint" }, rangeDays > 0 ? t("range.total", "共") + " " + rangeDays + " " + t("range.days", "天") : ""),
|
|
1295
1658
|
),
|
|
@@ -1323,6 +1686,8 @@ window.__ModuleLoader__.load({
|
|
|
1323
1686
|
h(
|
|
1324
1687
|
"div",
|
|
1325
1688
|
{ className: "dsdr-actions" },
|
|
1689
|
+
h("button", { type: "button", className: "dsdr-btn", onClick: () => setRange(yesterdayRange()) }, t("range.yesterday", "昨天")),
|
|
1690
|
+
h("button", { type: "button", className: "dsdr-btn", onClick: () => setRange(thisWeekRange()) }, t("range.thisWeek", "本周")),
|
|
1326
1691
|
h("button", { type: "button", className: "dsdr-btn", onClick: () => setRange(currentMonthRange()) }, t("range.thisMonth", "本月")),
|
|
1327
1692
|
h("button", { type: "button", className: "dsdr-btn", onClick: () => setRange(lastMonthRange()) }, t("range.lastMonth", "上月")),
|
|
1328
1693
|
h("button", { type: "button", className: "dsdr-btn", onClick: () => setRange(last7Range()) }, t("range.last7", "最近 7 天")),
|
|
@@ -1339,8 +1704,12 @@ window.__ModuleLoader__.load({
|
|
|
1339
1704
|
h(
|
|
1340
1705
|
"div",
|
|
1341
1706
|
null,
|
|
1342
|
-
h(
|
|
1343
|
-
|
|
1707
|
+
h(
|
|
1708
|
+
"div",
|
|
1709
|
+
{ className: "dsdr-title-with-info" },
|
|
1710
|
+
h("div", { className: "dsdr-card-title dsdr-ai-title" }, t("ai.title", "AI 分析")),
|
|
1711
|
+
h(InfoDot, { text: t("ai.desc", "使用当前默认模型分析提交说明、代码变更和文档摘要,生成按天简要总结。") }),
|
|
1712
|
+
),
|
|
1344
1713
|
),
|
|
1345
1714
|
h(
|
|
1346
1715
|
"label",
|
|
@@ -1358,9 +1727,66 @@ window.__ModuleLoader__.load({
|
|
|
1358
1727
|
"div",
|
|
1359
1728
|
{ className: "dsdr-hint" },
|
|
1360
1729
|
aiInfo.available
|
|
1361
|
-
? t("ai.model", "当前模型:") +
|
|
1730
|
+
? t("ai.model", "当前模型:") +
|
|
1731
|
+
(settings.aiModel ? (settings.aiProvider ? settings.aiProvider + "/" : "") + settings.aiModel : aiInfo.provider + "/" + aiInfo.model) +
|
|
1732
|
+
" · " +
|
|
1733
|
+
t("ai.cost", "会增加生成时间和模型 token 消耗。")
|
|
1362
1734
|
: t("ai.unavailable", "当前没有可用的默认模型。"),
|
|
1363
1735
|
),
|
|
1736
|
+
h(
|
|
1737
|
+
"div",
|
|
1738
|
+
{ className: "dsdr-ai-options" },
|
|
1739
|
+
h(
|
|
1740
|
+
"div",
|
|
1741
|
+
{ className: "dsdr-field" },
|
|
1742
|
+
h("label", { className: "dsdr-label", htmlFor: "dsdr-ai-template" }, t("ai.template", "分析风格")),
|
|
1743
|
+
h(
|
|
1744
|
+
"select",
|
|
1745
|
+
{
|
|
1746
|
+
id: "dsdr-ai-template",
|
|
1747
|
+
className: "dsdr-input",
|
|
1748
|
+
value: settings.aiTemplate || "concise",
|
|
1749
|
+
onChange: (event) => setSettings((previous) => ({ ...previous, aiTemplate: event.target.value })),
|
|
1750
|
+
},
|
|
1751
|
+
h("option", { value: "concise" }, t("ai.template.concise", "简洁")),
|
|
1752
|
+
h("option", { value: "detailed" }, t("ai.template.detailed", "详细")),
|
|
1753
|
+
h("option", { value: "manager" }, t("ai.template.manager", "管理汇报")),
|
|
1754
|
+
),
|
|
1755
|
+
),
|
|
1756
|
+
h(
|
|
1757
|
+
"div",
|
|
1758
|
+
{ className: "dsdr-field" },
|
|
1759
|
+
h("label", { className: "dsdr-label", htmlFor: "dsdr-ai-model" }, t("ai.modelSelect", "模型")),
|
|
1760
|
+
aiInfo.models && aiInfo.models.length > 0
|
|
1761
|
+
? h(
|
|
1762
|
+
"select",
|
|
1763
|
+
{
|
|
1764
|
+
id: "dsdr-ai-model",
|
|
1765
|
+
className: "dsdr-input",
|
|
1766
|
+
value: settings.aiModel || "",
|
|
1767
|
+
onChange: (event) => {
|
|
1768
|
+
const selected = aiInfo.models.find((model) => model.id === event.target.value);
|
|
1769
|
+
setSettings((previous) => ({
|
|
1770
|
+
...previous,
|
|
1771
|
+
aiProvider: selected ? selected.provider : "",
|
|
1772
|
+
aiModel: selected ? selected.id : "",
|
|
1773
|
+
}));
|
|
1774
|
+
},
|
|
1775
|
+
},
|
|
1776
|
+
h("option", { value: "" }, t("ai.defaultModel", "默认模型") + "(" + aiInfo.provider + "/" + aiInfo.model + ")"),
|
|
1777
|
+
aiInfo.models.map((model) =>
|
|
1778
|
+
h("option", { value: model.id, key: model.provider + "/" + model.id }, model.name || model.id),
|
|
1779
|
+
),
|
|
1780
|
+
)
|
|
1781
|
+
: h("input", {
|
|
1782
|
+
id: "dsdr-ai-model",
|
|
1783
|
+
className: "dsdr-input",
|
|
1784
|
+
value: settings.aiModel || "",
|
|
1785
|
+
placeholder: t("ai.defaultModel", "默认模型"),
|
|
1786
|
+
onChange: (event) => setSettings((previous) => ({ ...previous, aiProvider: "", aiModel: event.target.value })),
|
|
1787
|
+
}),
|
|
1788
|
+
),
|
|
1789
|
+
),
|
|
1364
1790
|
),
|
|
1365
1791
|
|
|
1366
1792
|
h(
|
|
@@ -1372,8 +1798,150 @@ window.__ModuleLoader__.load({
|
|
|
1372
1798
|
h(
|
|
1373
1799
|
"div",
|
|
1374
1800
|
null,
|
|
1375
|
-
h(
|
|
1376
|
-
|
|
1801
|
+
h(
|
|
1802
|
+
"div",
|
|
1803
|
+
{ className: "dsdr-title-with-info" },
|
|
1804
|
+
h("div", { className: "dsdr-card-title" }, t("schedule.title", "自动生成")),
|
|
1805
|
+
h(InfoDot, { text: t("schedule.desc", "在 DSH 运行期间按设定时间自动生成报告,并保存到本地历史目录。") }),
|
|
1806
|
+
),
|
|
1807
|
+
),
|
|
1808
|
+
h(
|
|
1809
|
+
"label",
|
|
1810
|
+
{ className: "dsdr-check" },
|
|
1811
|
+
h("input", {
|
|
1812
|
+
type: "checkbox",
|
|
1813
|
+
checked: settings.scheduleEnabled === true,
|
|
1814
|
+
onChange: (event) => setSettings((previous) => ({ ...previous, scheduleEnabled: event.target.checked })),
|
|
1815
|
+
}),
|
|
1816
|
+
h("span", null, t("schedule.enable", "启用自动生成")),
|
|
1817
|
+
),
|
|
1818
|
+
),
|
|
1819
|
+
h(
|
|
1820
|
+
"div",
|
|
1821
|
+
{ className: "dsdr-ai-options" },
|
|
1822
|
+
h(
|
|
1823
|
+
"div",
|
|
1824
|
+
{ className: "dsdr-field" },
|
|
1825
|
+
h("label", { className: "dsdr-label", htmlFor: "dsdr-schedule-frequency" }, t("schedule.title", "自动生成")),
|
|
1826
|
+
h(
|
|
1827
|
+
"select",
|
|
1828
|
+
{
|
|
1829
|
+
id: "dsdr-schedule-frequency",
|
|
1830
|
+
className: "dsdr-input",
|
|
1831
|
+
value: settings.scheduleFrequency || "daily",
|
|
1832
|
+
onChange: (event) => setSettings((previous) => ({ ...previous, scheduleFrequency: event.target.value })),
|
|
1833
|
+
},
|
|
1834
|
+
h("option", { value: "daily" }, t("schedule.daily", "每天")),
|
|
1835
|
+
h("option", { value: "weekly" }, t("schedule.weekly", "每周一")),
|
|
1836
|
+
),
|
|
1837
|
+
),
|
|
1838
|
+
h(
|
|
1839
|
+
"div",
|
|
1840
|
+
{ className: "dsdr-field" },
|
|
1841
|
+
h("label", { className: "dsdr-label", htmlFor: "dsdr-schedule-time" }, t("schedule.time", "生成时间")),
|
|
1842
|
+
h("input", {
|
|
1843
|
+
id: "dsdr-schedule-time",
|
|
1844
|
+
className: "dsdr-input",
|
|
1845
|
+
type: "time",
|
|
1846
|
+
value: settings.scheduleTime || "18:30",
|
|
1847
|
+
onChange: (event) => setSettings((previous) => ({ ...previous, scheduleTime: event.target.value })),
|
|
1848
|
+
}),
|
|
1849
|
+
),
|
|
1850
|
+
),
|
|
1851
|
+
h("div", { className: "dsdr-hint" }, t("schedule.hint", "自动生成使用当前保存的数据源、AI 设置和格式。")),
|
|
1852
|
+
),
|
|
1853
|
+
|
|
1854
|
+
h(
|
|
1855
|
+
"section",
|
|
1856
|
+
{ className: "dsdr-card dsdr-datasource-tabs" },
|
|
1857
|
+
h(
|
|
1858
|
+
"div",
|
|
1859
|
+
{ className: "dsdr-card-head" },
|
|
1860
|
+
h(
|
|
1861
|
+
"div",
|
|
1862
|
+
null,
|
|
1863
|
+
h(
|
|
1864
|
+
"div",
|
|
1865
|
+
{ className: "dsdr-title-with-info" },
|
|
1866
|
+
h("div", { className: "dsdr-card-title" }, t("datasource.title", "数据源配置")),
|
|
1867
|
+
h(InfoDot, { text: t("datasource.desc", "统一管理 Git 仓库与本地文档,生成时合并到同一份日报。") }),
|
|
1868
|
+
),
|
|
1869
|
+
),
|
|
1870
|
+
h(
|
|
1871
|
+
"button",
|
|
1872
|
+
{ type: "button", className: "dsdr-btn", onClick: testSources, disabled: testingSources },
|
|
1873
|
+
testingSources ? t("test.testing", "测试中…") : t("test.button", "测试数据源"),
|
|
1874
|
+
),
|
|
1875
|
+
),
|
|
1876
|
+
h(
|
|
1877
|
+
"div",
|
|
1878
|
+
{ className: "dsdr-tabs", role: "tablist" },
|
|
1879
|
+
h(
|
|
1880
|
+
"button",
|
|
1881
|
+
{
|
|
1882
|
+
type: "button",
|
|
1883
|
+
role: "tab",
|
|
1884
|
+
"aria-selected": dataSourceTab === "git",
|
|
1885
|
+
className: "dsdr-tab" + (dataSourceTab === "git" ? " is-active" : ""),
|
|
1886
|
+
onClick: () => setDataSourceTab("git"),
|
|
1887
|
+
},
|
|
1888
|
+
t("datasource.git", "Git 仓库"),
|
|
1889
|
+
h("span", { className: "dsdr-tab-count" }, String(settings.git.length)),
|
|
1890
|
+
),
|
|
1891
|
+
h(
|
|
1892
|
+
"button",
|
|
1893
|
+
{
|
|
1894
|
+
type: "button",
|
|
1895
|
+
role: "tab",
|
|
1896
|
+
"aria-selected": dataSourceTab === "docs",
|
|
1897
|
+
className: "dsdr-tab" + (dataSourceTab === "docs" ? " is-active" : ""),
|
|
1898
|
+
onClick: () => setDataSourceTab("docs"),
|
|
1899
|
+
},
|
|
1900
|
+
t("datasource.docs", "本地文档"),
|
|
1901
|
+
h("span", { className: "dsdr-tab-count" }, String(settings.docs.length)),
|
|
1902
|
+
),
|
|
1903
|
+
),
|
|
1904
|
+
testError ? h("div", { className: "dsdr-error" }, testError) : null,
|
|
1905
|
+
testResults && testResults.length > 0
|
|
1906
|
+
? h(
|
|
1907
|
+
"div",
|
|
1908
|
+
{ className: "dsdr-test-results" },
|
|
1909
|
+
testResults.map((source, index) =>
|
|
1910
|
+
h(
|
|
1911
|
+
"div",
|
|
1912
|
+
{ className: "dsdr-row", key: String(index) },
|
|
1913
|
+
h("span", { className: "dsdr-dot " + (source.status || "ok") }),
|
|
1914
|
+
h(
|
|
1915
|
+
"div",
|
|
1916
|
+
null,
|
|
1917
|
+
h("span", { className: "dsdr-source-name" }, source.label || source.location || "—"),
|
|
1918
|
+
h("span", { className: "dsdr-source-meta" }, " · " + (source.status === "error" ? t("status.error", "失败") : t("test.ok", "正常"))),
|
|
1919
|
+
h("div", { className: "dsdr-source-meta" }, source.message || ""),
|
|
1920
|
+
),
|
|
1921
|
+
),
|
|
1922
|
+
),
|
|
1923
|
+
)
|
|
1924
|
+
: null,
|
|
1925
|
+
),
|
|
1926
|
+
|
|
1927
|
+
h(
|
|
1928
|
+
"section",
|
|
1929
|
+
{
|
|
1930
|
+
className: "dsdr-card dsdr-datasource-panel",
|
|
1931
|
+
style: { display: dataSourceTab === "git" ? undefined : "none" },
|
|
1932
|
+
},
|
|
1933
|
+
h(
|
|
1934
|
+
"div",
|
|
1935
|
+
{ className: "dsdr-card-head" },
|
|
1936
|
+
h(
|
|
1937
|
+
"div",
|
|
1938
|
+
null,
|
|
1939
|
+
h(
|
|
1940
|
+
"div",
|
|
1941
|
+
{ className: "dsdr-title-with-info" },
|
|
1942
|
+
h("div", { className: "dsdr-card-title" }, t("git.title", "Git 仓库")),
|
|
1943
|
+
h(InfoDot, { text: t("git.desc", "支持远程地址或本地仓库;多个仓库、多个分支会合并到同一份日报。") }),
|
|
1944
|
+
),
|
|
1377
1945
|
),
|
|
1378
1946
|
h(
|
|
1379
1947
|
"div",
|
|
@@ -1395,7 +1963,30 @@ window.__ModuleLoader__.load({
|
|
|
1395
1963
|
}),
|
|
1396
1964
|
h("span", null, t("git.mine", "仅统计我的提交")),
|
|
1397
1965
|
),
|
|
1398
|
-
h(
|
|
1966
|
+
h(
|
|
1967
|
+
"label",
|
|
1968
|
+
{ className: "dsdr-check" },
|
|
1969
|
+
h("input", {
|
|
1970
|
+
type: "checkbox",
|
|
1971
|
+
checked: settings.excludeMerges !== false,
|
|
1972
|
+
onChange: (event) => setSettings((previous) => ({ ...previous, excludeMerges: event.target.checked })),
|
|
1973
|
+
}),
|
|
1974
|
+
h("span", null, t("git.excludeMerges", "排除 merge commit")),
|
|
1975
|
+
),
|
|
1976
|
+
settings.mineOnly !== false
|
|
1977
|
+
? h(
|
|
1978
|
+
"div",
|
|
1979
|
+
{ className: "dsdr-field dsdr-filter-author" },
|
|
1980
|
+
h("label", { className: "dsdr-label", htmlFor: "dsdr-author-emails" }, t("git.authorEmails", "作者邮箱(可选)")),
|
|
1981
|
+
h("input", {
|
|
1982
|
+
id: "dsdr-author-emails",
|
|
1983
|
+
className: "dsdr-input",
|
|
1984
|
+
value: settings.authorEmails || "",
|
|
1985
|
+
placeholder: t("git.authorEmailsHint", "留空自动读取各仓库 git config;多个邮箱用逗号分隔"),
|
|
1986
|
+
onChange: (event) => setSettings((previous) => ({ ...previous, authorEmails: event.target.value })),
|
|
1987
|
+
}),
|
|
1988
|
+
)
|
|
1989
|
+
: null,
|
|
1399
1990
|
),
|
|
1400
1991
|
showScan
|
|
1401
1992
|
? h(
|
|
@@ -1478,15 +2069,22 @@ window.__ModuleLoader__.load({
|
|
|
1478
2069
|
|
|
1479
2070
|
h(
|
|
1480
2071
|
"section",
|
|
1481
|
-
{
|
|
2072
|
+
{
|
|
2073
|
+
className: "dsdr-card dsdr-datasource-panel",
|
|
2074
|
+
style: { display: dataSourceTab === "docs" ? undefined : "none" },
|
|
2075
|
+
},
|
|
1482
2076
|
h(
|
|
1483
2077
|
"div",
|
|
1484
2078
|
{ className: "dsdr-card-head" },
|
|
1485
2079
|
h(
|
|
1486
2080
|
"div",
|
|
1487
2081
|
null,
|
|
1488
|
-
h(
|
|
1489
|
-
|
|
2082
|
+
h(
|
|
2083
|
+
"div",
|
|
2084
|
+
{ className: "dsdr-title-with-info" },
|
|
2085
|
+
h("div", { className: "dsdr-card-title" }, t("docs.title", "本地文档")),
|
|
2086
|
+
h(InfoDot, { text: t("docs.desc", "文件或目录;目录会递归扫描 md、txt、docx、html 等笔记文件。") }),
|
|
2087
|
+
),
|
|
1490
2088
|
),
|
|
1491
2089
|
h(
|
|
1492
2090
|
"div",
|