wukong-gitlog-cli 1.0.42 → 1.0.44
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/CHANGELOG.md +14 -0
- package/package.json +15 -13
- package/src/app/serveAction.mjs +6 -1
- package/src/cli/createCommand.mjs +31 -0
- package/src/index.mjs +98 -69
- package/src/serve/startServer.mjs +16 -2
- package/src/utils/checkUpdate.mjs +135 -0
- package/web/app.js +39 -0
- package/web/index.html +44 -36
- package/web/index2.html +159 -0
- package/web/static/i18n/en.js +29 -0
- package/web/static/i18n/zh.js +29 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
### [1.0.44](https://github.com/tomatobybike/wukong-gitlog-cli/compare/v1.0.43...v1.0.44) (2026-02-14)
|
|
6
|
+
|
|
7
|
+
### [1.0.43](https://github.com/tomatobybike/wukong-gitlog-cli/compare/v1.0.42...v1.0.43) (2026-02-13)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Features
|
|
11
|
+
|
|
12
|
+
* 🎸 autoCheckUpdate ([d377012](https://github.com/tomatobybike/wukong-gitlog-cli/commit/d3770123f10c574f0f5ea592ef58e71bfdd8edc1))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
* 🐛 update version ([840216d](https://github.com/tomatobybike/wukong-gitlog-cli/commit/840216d4f553e5d78f84e42f76386056553d1282))
|
|
18
|
+
|
|
5
19
|
### [1.0.42](https://github.com/tomatobybike/wukong-gitlog-cli/compare/v1.0.41...v1.0.42) (2026-02-13)
|
|
6
20
|
|
|
7
21
|
### [1.0.41](https://github.com/tomatobybike/wukong-gitlog-cli/compare/v1.0.40...v1.0.41) (2026-02-12)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wukong-gitlog-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.44",
|
|
4
4
|
"description": "Advanced Git commit log exporter with Excel/JSON/TXT output, grouping, stats and CLI.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"git",
|
|
@@ -97,35 +97,37 @@
|
|
|
97
97
|
"#utils/*": "./src/utils/*"
|
|
98
98
|
},
|
|
99
99
|
"dependencies": {
|
|
100
|
-
"@inquirer/prompts": "
|
|
100
|
+
"@inquirer/prompts": "8.2.0",
|
|
101
|
+
"boxen": "8.0.1",
|
|
101
102
|
"chalk": "5.6.2",
|
|
102
|
-
"commander": "
|
|
103
|
+
"commander": "14.0.3",
|
|
103
104
|
"date-fns": "4.1.0",
|
|
104
|
-
"date-holidays": "
|
|
105
|
+
"date-holidays": "3.26.8",
|
|
105
106
|
"dayjs": "1.11.19",
|
|
106
|
-
"dotenv": "17.
|
|
107
|
+
"dotenv": "17.3.1",
|
|
107
108
|
"exceljs": "4.4.0",
|
|
108
109
|
"fs-extra": "11.3.3",
|
|
109
|
-
"
|
|
110
|
+
"is-online": "12.0.2",
|
|
111
|
+
"ora": "9.3.0",
|
|
110
112
|
"os-locale": "^8.0.0",
|
|
111
|
-
"string-width": "
|
|
113
|
+
"string-width": "8.1.1",
|
|
112
114
|
"wukong-profiler": "^1.0.11",
|
|
113
|
-
"wukong-progress": "^0.1.
|
|
115
|
+
"wukong-progress": "^0.1.3"
|
|
114
116
|
},
|
|
115
117
|
"devDependencies": {
|
|
116
|
-
"@trivago/prettier-plugin-sort-imports": "
|
|
118
|
+
"@trivago/prettier-plugin-sort-imports": "6.0.2",
|
|
117
119
|
"eslint": "8.57.1",
|
|
118
120
|
"eslint-config-airbnb-base": "15.0.0",
|
|
119
121
|
"eslint-config-prettier": "10.1.8",
|
|
120
122
|
"eslint-import-resolver-alias": "1.1.2",
|
|
121
123
|
"eslint-plugin-import": "2.32.0",
|
|
122
|
-
"eslint-plugin-prettier": "5.5.
|
|
124
|
+
"eslint-plugin-prettier": "5.5.5",
|
|
123
125
|
"eslint-plugin-simple-import-sort": "12.1.1",
|
|
124
126
|
"husky": "^9.1.7",
|
|
125
127
|
"lint-staged": "^16.2.7",
|
|
126
|
-
"prettier": "3.
|
|
127
|
-
"prettier-plugin-packagejson": "
|
|
128
|
-
"sort-package-json": "3.
|
|
128
|
+
"prettier": "3.8.1",
|
|
129
|
+
"prettier-plugin-packagejson": "3.0.0",
|
|
130
|
+
"sort-package-json": "3.6.1",
|
|
129
131
|
"standard-version": "9.5.0"
|
|
130
132
|
},
|
|
131
133
|
"packageManager": "yarn@1.22.22"
|
package/src/app/serveAction.mjs
CHANGED
|
@@ -48,7 +48,12 @@ export async function serveAction(rawOpts = {}) {
|
|
|
48
48
|
for (let i = 0; i < maxTries; i++) {
|
|
49
49
|
try {
|
|
50
50
|
// eslint-disable-next-line no-await-in-loop
|
|
51
|
-
server = await startServer(
|
|
51
|
+
server = await startServer({
|
|
52
|
+
port,
|
|
53
|
+
outputDir: dir,
|
|
54
|
+
data,
|
|
55
|
+
lang: rawOpts.lang
|
|
56
|
+
})
|
|
52
57
|
break
|
|
53
58
|
} catch (err) {
|
|
54
59
|
// 端口被占用,尝试下一个端口;其它错误抛出
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export function createCommand(program, {
|
|
2
|
+
name,
|
|
3
|
+
description,
|
|
4
|
+
optionsBuilder,
|
|
5
|
+
handler,
|
|
6
|
+
autoUpdate,
|
|
7
|
+
lang
|
|
8
|
+
}) {
|
|
9
|
+
const cmd = program.command(name).description(description)
|
|
10
|
+
|
|
11
|
+
// 注入 options
|
|
12
|
+
if (optionsBuilder) {
|
|
13
|
+
optionsBuilder(cmd)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
cmd.action(async (cmdOpts, command) => {
|
|
17
|
+
const globalOpts = command.parent?.opts?.() || {}
|
|
18
|
+
const finalOpts = { ...globalOpts, ...cmdOpts,lang }
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
await handler(finalOpts)
|
|
22
|
+
} finally {
|
|
23
|
+
if (autoUpdate) {
|
|
24
|
+
// 不阻塞 CLI
|
|
25
|
+
autoUpdate().catch(() => {})
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
return cmd
|
|
31
|
+
}
|
package/src/index.mjs
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from 'commander'
|
|
3
|
+
import fs from 'fs'
|
|
4
|
+
import path from 'path'
|
|
5
|
+
import { fileURLToPath } from 'url'
|
|
3
6
|
|
|
4
7
|
import { runGitPreflight, showGitInfo } from '#src/domain/git/index.mjs'
|
|
5
8
|
|
|
@@ -11,6 +14,7 @@ import { journalAction } from './app/journalAction.mjs'
|
|
|
11
14
|
import { overtimeAction } from './app/overtimeAction.mjs'
|
|
12
15
|
import { serveAction } from './app/serveAction.mjs'
|
|
13
16
|
import { versionAction } from './app/versionAction.mjs'
|
|
17
|
+
import { createCommand } from './cli/createCommand.mjs'
|
|
14
18
|
import {
|
|
15
19
|
addAnalysisOptions,
|
|
16
20
|
addGitSourceOptions,
|
|
@@ -21,9 +25,38 @@ import {
|
|
|
21
25
|
} from './cli/defineOptions.mjs'
|
|
22
26
|
import { initI18n, t } from './i18n/index.mjs'
|
|
23
27
|
import { loadRcConfig } from './infra/configStore.mjs'
|
|
28
|
+
import { checkUpdateWithPatch } from './utils/checkUpdate.mjs'
|
|
24
29
|
|
|
25
30
|
// 引入加载器
|
|
26
31
|
|
|
32
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
33
|
+
const __dirname = path.dirname(__filename)
|
|
34
|
+
|
|
35
|
+
const pkgPath = path.resolve(__dirname, '../package.json')
|
|
36
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'))
|
|
37
|
+
|
|
38
|
+
const autoCheckUpdate = async () => {
|
|
39
|
+
// 避免 CI 打印更新提示
|
|
40
|
+
if (process.env.CI) return
|
|
41
|
+
// CLI 更新提示只在 global 安装时出现
|
|
42
|
+
if (process.env.npm_config_global !== 'true') return
|
|
43
|
+
if (!process.stdout.isTTY) return
|
|
44
|
+
|
|
45
|
+
// === CLI 主逻辑完成后提示更新 ===
|
|
46
|
+
// await checkUpdateWithPatch({
|
|
47
|
+
// pkg: {
|
|
48
|
+
// name: pkg.name,
|
|
49
|
+
// version: pkg.version
|
|
50
|
+
// },
|
|
51
|
+
// // force:true
|
|
52
|
+
// })
|
|
53
|
+
|
|
54
|
+
// 放到下一个 tick
|
|
55
|
+
setTimeout(() => {
|
|
56
|
+
checkUpdateWithPatch({ pkg }).catch(() => {})
|
|
57
|
+
}, 0)
|
|
58
|
+
}
|
|
59
|
+
|
|
27
60
|
const main = async () => {
|
|
28
61
|
// --- 第一步:参数预检 (Pre-flight) ---
|
|
29
62
|
const args = process.argv.slice(2)
|
|
@@ -37,6 +70,8 @@ const main = async () => {
|
|
|
37
70
|
// 这一步必须在定义子命令描述之前完成!
|
|
38
71
|
const finalLang = await initI18n(userLang)
|
|
39
72
|
|
|
73
|
+
|
|
74
|
+
|
|
40
75
|
// ---------------------------------------------------------
|
|
41
76
|
// 2. 环境准备
|
|
42
77
|
// ---------------------------------------------------------
|
|
@@ -67,85 +102,79 @@ const main = async () => {
|
|
|
67
102
|
|
|
68
103
|
// === 命令: Init ===
|
|
69
104
|
|
|
70
|
-
program
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
105
|
+
createCommand(program, {
|
|
106
|
+
name: 'init',
|
|
107
|
+
description: t('cmds.init'),
|
|
108
|
+
optionsBuilder: (cmd) => {
|
|
109
|
+
cmd.option('-f, --force', t('options.force'))
|
|
110
|
+
},
|
|
111
|
+
handler: initAction,
|
|
112
|
+
autoUpdate: autoCheckUpdate
|
|
113
|
+
})
|
|
79
114
|
|
|
80
115
|
// # 命令: Analyze 核心分析(默认) 分析 git 提交记录 (最全参数)
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
addOutputOptions(analyzeCmd)
|
|
94
|
-
addPerformanceOptions(analyzeCmd)
|
|
116
|
+
createCommand(program, {
|
|
117
|
+
name: 'analyze',
|
|
118
|
+
description: t('cmds.analyze'),
|
|
119
|
+
optionsBuilder: (cmd) => {
|
|
120
|
+
addGitSourceOptions(cmd)
|
|
121
|
+
addAnalysisOptions(cmd)
|
|
122
|
+
addOutputOptions(cmd)
|
|
123
|
+
addPerformanceOptions(cmd)
|
|
124
|
+
},
|
|
125
|
+
handler: analyzeAction,
|
|
126
|
+
autoUpdate: autoCheckUpdate
|
|
127
|
+
})
|
|
95
128
|
|
|
96
129
|
// # 命令: Overtime 加班文化分析
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
130
|
+
createCommand(program, {
|
|
131
|
+
name: 'overtime',
|
|
132
|
+
description: t('cmds.overtime'),
|
|
133
|
+
optionsBuilder: (cmd) => {
|
|
134
|
+
addGitSourceOptions(cmd)
|
|
135
|
+
addAnalysisOptions(cmd)
|
|
136
|
+
},
|
|
137
|
+
handler: overtimeAction,
|
|
138
|
+
autoUpdate: autoCheckUpdate
|
|
139
|
+
})
|
|
107
140
|
|
|
108
141
|
// # 命令: Export (专注导出) 导出(excel / csv / json)
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
})
|
|
120
|
-
addGitSourceOptions(exportCmd)
|
|
121
|
-
addOutputOptions(exportCmd)
|
|
142
|
+
createCommand(program, {
|
|
143
|
+
name: 'export',
|
|
144
|
+
description: t('cmds.export'),
|
|
145
|
+
optionsBuilder: (cmd) => {
|
|
146
|
+
addGitSourceOptions(cmd)
|
|
147
|
+
addOutputOptions(cmd)
|
|
148
|
+
},
|
|
149
|
+
handler: exportAction,
|
|
150
|
+
autoUpdate: autoCheckUpdate
|
|
151
|
+
})
|
|
122
152
|
|
|
123
153
|
// === 命令: Journal (日报) ===
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
addPerformanceOptions(journalCmd)
|
|
154
|
+
createCommand(program, {
|
|
155
|
+
name: 'journal',
|
|
156
|
+
description: t('cmds.journal'),
|
|
157
|
+
optionsBuilder: (cmd) => {
|
|
158
|
+
addGitSourceOptions(cmd)
|
|
159
|
+
addPerformanceOptions(cmd)
|
|
160
|
+
},
|
|
161
|
+
handler: journalAction,
|
|
162
|
+
autoUpdate: autoCheckUpdate
|
|
163
|
+
})
|
|
136
164
|
|
|
137
165
|
// === 命令: Serve (Web服务) ===
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
166
|
+
createCommand(program, {
|
|
167
|
+
name: 'serve',
|
|
168
|
+
description: 'Start web server',
|
|
169
|
+
optionsBuilder: (cmd) => {
|
|
170
|
+
addServeOptions(cmd)
|
|
171
|
+
},
|
|
172
|
+
handler: serveAction,
|
|
173
|
+
autoUpdate: autoCheckUpdate,
|
|
174
|
+
lang:finalLang
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
await program.parseAsync(process.argv)
|
|
149
178
|
|
|
150
179
|
// const opts = program.opts()
|
|
151
180
|
// console.log('✅ Cli Opts:', opts)
|
|
@@ -51,7 +51,7 @@ function findPkgRoot(currentDir) {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
// eslint-disable-next-line default-param-last
|
|
54
|
-
export function startServer(port = 3000, outputDir) {
|
|
54
|
+
export function startServer({ port = 3000, outputDir, data,lang }) {
|
|
55
55
|
// 解析包根目录,确保 web 资源在全局安装后也能找到
|
|
56
56
|
const __filename = fileURLToPath(import.meta.url)
|
|
57
57
|
const __dirname = path.dirname(__filename)
|
|
@@ -64,7 +64,6 @@ export function startServer(port = 3000, outputDir) {
|
|
|
64
64
|
? path.resolve(outputDir)
|
|
65
65
|
: path.resolve(process.cwd(), 'output-wukong')
|
|
66
66
|
|
|
67
|
-
|
|
68
67
|
// warn if web directory or data directory doesn't exist
|
|
69
68
|
if (!fs.existsSync(webRoot)) {
|
|
70
69
|
console.warn(
|
|
@@ -113,6 +112,21 @@ export function startServer(port = 3000, outputDir) {
|
|
|
113
112
|
'Content-Type',
|
|
114
113
|
mime.get(ext) || 'application/octet-stream'
|
|
115
114
|
)
|
|
115
|
+
|
|
116
|
+
// 👇 只对 index.html 注入
|
|
117
|
+
if (pathname === '/index.html') {
|
|
118
|
+
let html = fs.readFileSync(fileLocal, 'utf8')
|
|
119
|
+
|
|
120
|
+
html = html.replace(
|
|
121
|
+
`window.__LANG__ = '__LANG__'`,
|
|
122
|
+
`window.__LANG__ = "${lang}"`
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
res.setHeader('Content-Type', 'text/html; charset=utf-8')
|
|
126
|
+
res.end(html)
|
|
127
|
+
return
|
|
128
|
+
}
|
|
129
|
+
|
|
116
130
|
const stream = fs.createReadStream(fileLocal)
|
|
117
131
|
stream.pipe(res)
|
|
118
132
|
return
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import boxen from 'boxen'
|
|
2
|
+
import fs from 'fs'
|
|
3
|
+
import isOnline from 'is-online'
|
|
4
|
+
import os from 'os'
|
|
5
|
+
import path from 'path'
|
|
6
|
+
import semver from 'semver'
|
|
7
|
+
|
|
8
|
+
import { colors } from './colors.mjs'
|
|
9
|
+
|
|
10
|
+
const CONFIG_DIR = path.join(os.homedir(), '.config', 'configstore')
|
|
11
|
+
|
|
12
|
+
const getCacheFile = (pkg) =>
|
|
13
|
+
path.join(CONFIG_DIR, `update-notifier-${pkg.name}.json`)
|
|
14
|
+
|
|
15
|
+
async function fetchLatestVersion(pkgName, timeout = 1500) {
|
|
16
|
+
const url = `https://registry.npmjs.org/${pkgName}/latest`
|
|
17
|
+
const controller = new AbortController()
|
|
18
|
+
const timer = setTimeout(() => controller.abort(), timeout)
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
const res = await fetch(url, { signal: controller.signal })
|
|
22
|
+
clearTimeout(timer)
|
|
23
|
+
if (!res.ok) throw new Error('fetch fail')
|
|
24
|
+
const data = await res.json()
|
|
25
|
+
return data.version
|
|
26
|
+
} catch (e) {
|
|
27
|
+
clearTimeout(timer)
|
|
28
|
+
// 超时或其他错误都返回 null,防止程序卡死
|
|
29
|
+
return null
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* 格式化升级提示信息
|
|
35
|
+
*/
|
|
36
|
+
export function formatUpdateMessage(current, latest, name) {
|
|
37
|
+
const arrow = colors.dim('→')
|
|
38
|
+
const currentVer = colors.dim(current)
|
|
39
|
+
const latestVer = colors.green(latest)
|
|
40
|
+
|
|
41
|
+
const message =
|
|
42
|
+
`${colors.dim('Update available')}` +
|
|
43
|
+
` ${currentVer} ${arrow} ${latestVer}\n` +
|
|
44
|
+
`${colors.dim('Run')} ${colors.cyanish(`npm i -g ${name}`)} ${colors.dim(' to update')}`
|
|
45
|
+
|
|
46
|
+
return boxen(message, {
|
|
47
|
+
padding: 1,
|
|
48
|
+
margin: 1,
|
|
49
|
+
borderStyle: 'round',
|
|
50
|
+
borderColor: 'yellow'
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* 检查更新(带缓存 + 支持 patch 更新)
|
|
56
|
+
*/
|
|
57
|
+
export async function checkUpdateWithPatch({
|
|
58
|
+
pkg,
|
|
59
|
+
interval = 24 * 60 * 60 * 1000,
|
|
60
|
+
// interval = 6 * 1000,
|
|
61
|
+
force = false
|
|
62
|
+
} = {}) {
|
|
63
|
+
const online = await isOnline()
|
|
64
|
+
console.log('online', online)
|
|
65
|
+
if (!online) return null
|
|
66
|
+
const now = Date.now()
|
|
67
|
+
const CACHE_FILE = getCacheFile(pkg)
|
|
68
|
+
|
|
69
|
+
let cache = {}
|
|
70
|
+
if (fs.existsSync(CACHE_FILE)) {
|
|
71
|
+
try {
|
|
72
|
+
cache = JSON.parse(fs.readFileSync(CACHE_FILE, 'utf8')) || {}
|
|
73
|
+
// eslint-disable-next-line no-empty
|
|
74
|
+
} catch {}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// console.log('now - cache.lastCheck', now - cache.lastCheck, interval)
|
|
78
|
+
if (!force && cache.lastCheck && now - cache.lastCheck < interval) {
|
|
79
|
+
return cache.updateInfo || null
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const latest = await fetchLatestVersion(pkg.name)
|
|
83
|
+
if (!latest) {
|
|
84
|
+
// console.log('无法获取最新版本')
|
|
85
|
+
return null
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (!semver.valid(pkg.version) || !semver.valid(latest)) {
|
|
89
|
+
return null
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
if (semver.lt(pkg.version, latest)) {
|
|
94
|
+
// 构造 update 对象,兼容 update-notifier
|
|
95
|
+
const updateInfo = {
|
|
96
|
+
current: pkg.version,
|
|
97
|
+
latest,
|
|
98
|
+
type: semver.diff(pkg.version, latest) || 'patch',
|
|
99
|
+
name: pkg.name,
|
|
100
|
+
// 这里官方还会有 message 字段
|
|
101
|
+
// 用官方的默认消息格式生成,方便notify打印
|
|
102
|
+
message: `\nUpdate available ${pkg.version} → ${latest}\nRun npm i -g ${pkg.name} to update\n`
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// 缓存
|
|
106
|
+
try {
|
|
107
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true })
|
|
108
|
+
fs.writeFileSync(
|
|
109
|
+
CACHE_FILE,
|
|
110
|
+
JSON.stringify(
|
|
111
|
+
{
|
|
112
|
+
lastCheck: now,
|
|
113
|
+
updateInfo
|
|
114
|
+
},
|
|
115
|
+
null,
|
|
116
|
+
2
|
|
117
|
+
)
|
|
118
|
+
)
|
|
119
|
+
} catch (e) {
|
|
120
|
+
console.error('缓存写入失败:', e)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const { current } = updateInfo
|
|
124
|
+
console.log(formatUpdateMessage(current, latest, pkg.name))
|
|
125
|
+
return updateInfo
|
|
126
|
+
}
|
|
127
|
+
// 无更新,刷新缓存时间
|
|
128
|
+
try {
|
|
129
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true })
|
|
130
|
+
fs.writeFileSync(CACHE_FILE, JSON.stringify({ lastCheck: now }, null, 2))
|
|
131
|
+
} catch (e) {
|
|
132
|
+
console.error('缓存写入失败:', e)
|
|
133
|
+
}
|
|
134
|
+
return null
|
|
135
|
+
}
|
package/web/app.js
CHANGED
|
@@ -3,6 +3,45 @@
|
|
|
3
3
|
/* eslint-disable no-restricted-globals */
|
|
4
4
|
/* global echarts */
|
|
5
5
|
|
|
6
|
+
/* ---------------------- 动态加载语言 */
|
|
7
|
+
|
|
8
|
+
import zh from './static/i18n/zh.js'
|
|
9
|
+
import en from './static/i18n/en.js'
|
|
10
|
+
|
|
11
|
+
function resolveLang() {
|
|
12
|
+
// CLI 注入优先
|
|
13
|
+
if (window.__LANG__) return window.__LANG__
|
|
14
|
+
|
|
15
|
+
// 浏览器语言
|
|
16
|
+
if (navigator.language.startsWith('zh')) return 'zh'
|
|
17
|
+
|
|
18
|
+
return 'en'
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const lang = resolveLang()
|
|
22
|
+
|
|
23
|
+
const messages = {
|
|
24
|
+
zh,
|
|
25
|
+
en
|
|
26
|
+
}[lang] || zh
|
|
27
|
+
|
|
28
|
+
function applyI18n() {
|
|
29
|
+
document.querySelectorAll('[data-i18n]').forEach(el => {
|
|
30
|
+
const key = el.dataset.i18n
|
|
31
|
+
// eslint-disable-next-line no-param-reassign
|
|
32
|
+
el.textContent = messages[key] || key
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
document.querySelectorAll('[data-i18n-placeholder]').forEach(el => {
|
|
36
|
+
const key = el.dataset.i18nPlaceholder
|
|
37
|
+
// eslint-disable-next-line no-param-reassign
|
|
38
|
+
el.placeholder = messages[key] || key
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
applyI18n()
|
|
43
|
+
|
|
44
|
+
/* ---------------------- */
|
|
6
45
|
// 1. 定义一个存储所有实例的数组
|
|
7
46
|
const chartInstances = []
|
|
8
47
|
const formatDate = (d) => new Date(d).toLocaleString()
|
package/web/index.html
CHANGED
|
@@ -3,29 +3,34 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="utf-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
6
|
-
<title>
|
|
6
|
+
<title data-i18n="dashboardTitle">Wukong Gitlog 加班分析看板</title>
|
|
7
7
|
<link rel="stylesheet" href="./static/style.css" />
|
|
8
|
+
<script>
|
|
9
|
+
window.__LANG__ = '__LANG__'
|
|
10
|
+
</script>
|
|
8
11
|
<script src="./static/echarts.min.js"></script>
|
|
9
12
|
</head>
|
|
10
13
|
<body>
|
|
11
14
|
<header>
|
|
12
|
-
<h1
|
|
15
|
+
<h1 data-i18n="dashboardTitle"></h1>
|
|
13
16
|
<div id="samplingInfo" class="sampling-info"></div>
|
|
14
17
|
</header>
|
|
15
18
|
<main id="main">
|
|
16
19
|
<section id="chartsHalf">
|
|
17
20
|
<div class="chart-card">
|
|
18
|
-
<h2>下班时间 vs 工作时间提交占比</h2>
|
|
21
|
+
<!-- <h2>下班时间 vs 工作时间提交占比</h2> -->
|
|
22
|
+
<h2 data-i18n="outsideVsInside"></h2>
|
|
23
|
+
|
|
19
24
|
<div id="outsideVsInsideChart" class="echart"></div>
|
|
20
25
|
</div>
|
|
21
26
|
<div class="chart-card" id="kpiCard">
|
|
22
|
-
<h2>关键指标</h2>
|
|
27
|
+
<h2 data-i18n="kpi">关键指标</h2>
|
|
23
28
|
<div id="kpiContent"></div>
|
|
24
29
|
</div>
|
|
25
30
|
</section>
|
|
26
31
|
<section id="charts">
|
|
27
32
|
<div class="chart-card">
|
|
28
|
-
<h2>每小时加班分布
|
|
33
|
+
<h2 data-i18n="hourlyOvertime">每小时加班分布(小时 -> 提交数)</h2>
|
|
29
34
|
<div
|
|
30
35
|
id="hourlyOvertimeChart"
|
|
31
36
|
data-title="每小时加班分布"
|
|
@@ -34,7 +39,7 @@
|
|
|
34
39
|
</div>
|
|
35
40
|
|
|
36
41
|
<div class="chart-card">
|
|
37
|
-
<h2>按日提交趋势(次数)</h2>
|
|
42
|
+
<h2 data-i18n="dailyTrend">按日提交趋势(次数)</h2>
|
|
38
43
|
<div
|
|
39
44
|
id="dailyTrendChart"
|
|
40
45
|
data-title="按日提交趋势(次数)"
|
|
@@ -42,7 +47,7 @@
|
|
|
42
47
|
></div>
|
|
43
48
|
</div>
|
|
44
49
|
<div class="chart-card">
|
|
45
|
-
<h2>按日提交趋势(次数)</h2>
|
|
50
|
+
<h2 data-i18n="dailyTrend">按日提交趋势(次数)</h2>
|
|
46
51
|
<div
|
|
47
52
|
id="dailyTrendChartDog"
|
|
48
53
|
data-title="按日提交趋势"
|
|
@@ -50,7 +55,7 @@
|
|
|
50
55
|
></div>
|
|
51
56
|
</div>
|
|
52
57
|
<div class="chart-card">
|
|
53
|
-
<h2>每日最晚提交时间(小时)</h2>
|
|
58
|
+
<h2 data-i18n="latestHourDaily">每日最晚提交时间(小时)</h2>
|
|
54
59
|
<div
|
|
55
60
|
id="latestHourDailyChart"
|
|
56
61
|
data-title="每日最晚提交时间(小时)"
|
|
@@ -58,7 +63,7 @@
|
|
|
58
63
|
></div>
|
|
59
64
|
</div>
|
|
60
65
|
<div class="chart-card">
|
|
61
|
-
<h2>每日超过下班的小时数</h2>
|
|
66
|
+
<h2 data-i18n="dailySeverity">每日超过下班的小时数</h2>
|
|
62
67
|
<div
|
|
63
68
|
id="dailySeverityChart"
|
|
64
69
|
data-title="每日超过下班的小时数"
|
|
@@ -67,7 +72,7 @@
|
|
|
67
72
|
</div>
|
|
68
73
|
|
|
69
74
|
<div class="chart-card">
|
|
70
|
-
<h2>每周趋势(加班占比)</h2>
|
|
75
|
+
<h2 data-i18n="weeklyTrend">每周趋势(加班占比)</h2>
|
|
71
76
|
<div
|
|
72
77
|
id="weeklyTrendChart"
|
|
73
78
|
data-title="每周趋势(加班占比)"
|
|
@@ -75,7 +80,7 @@
|
|
|
75
80
|
></div>
|
|
76
81
|
</div>
|
|
77
82
|
<div class="chart-card">
|
|
78
|
-
<h2>每月趋势(加班占比)</h2>
|
|
83
|
+
<h2 data-i18n="monthlyTrend">每月趋势(加班占比)</h2>
|
|
79
84
|
<div
|
|
80
85
|
id="monthlyTrendChart"
|
|
81
86
|
data-title="每月趋势(加班占比)"
|
|
@@ -93,10 +98,8 @@
|
|
|
93
98
|
</div>
|
|
94
99
|
</section>
|
|
95
100
|
|
|
96
|
-
|
|
97
|
-
|
|
98
101
|
<div class="chart-card">
|
|
99
|
-
<h2>开发者 Changed 工作量趋势(行数)</h2>
|
|
102
|
+
<h2 data-i18n="authorChanges">开发者 Changed 工作量趋势(行数)</h2>
|
|
100
103
|
|
|
101
104
|
<div id="tabs" class="tabs">
|
|
102
105
|
<button data-type="daily" class="active">按日</button>
|
|
@@ -107,7 +110,7 @@
|
|
|
107
110
|
</div>
|
|
108
111
|
|
|
109
112
|
<div class="chart-card">
|
|
110
|
-
<h2>开发者 加班最晚趋势(小时)</h2>
|
|
113
|
+
<h2 data-i18n="authorLatestOvertime">开发者 加班最晚趋势(小时)</h2>
|
|
111
114
|
<div class="tabs" id="tabsLatestOvertime">
|
|
112
115
|
<button data-type="daily" class="active">按日</button>
|
|
113
116
|
<button data-type="weekly">按周</button>
|
|
@@ -117,7 +120,7 @@
|
|
|
117
120
|
</div>
|
|
118
121
|
|
|
119
122
|
<div class="chart-card">
|
|
120
|
-
<h2>开发者 加班趋势(次数)</h2>
|
|
123
|
+
<h2 data-i18n="authorOvertime">开发者 加班趋势(次数)</h2>
|
|
121
124
|
<div class="tabs" id="tabsOvertime">
|
|
122
125
|
<button data-type="daily" class="active">按日</button>
|
|
123
126
|
<button data-type="weekly">按周</button>
|
|
@@ -134,7 +137,7 @@
|
|
|
134
137
|
</div>
|
|
135
138
|
|
|
136
139
|
<div class="chart-card">
|
|
137
|
-
<h2>开发者 午休最晚提交(小时)</h2>
|
|
140
|
+
<h2 data-i18n="authorLunch">开发者 午休最晚提交(小时)</h2>
|
|
138
141
|
<div class="tabs" id="tabsLunch">
|
|
139
142
|
<button data-type="daily" class="active">按日</button>
|
|
140
143
|
<button data-type="weekly">按周</button>
|
|
@@ -149,7 +152,7 @@
|
|
|
149
152
|
|
|
150
153
|
<!-- 午休 分析 -->
|
|
151
154
|
<div class="chart-card">
|
|
152
|
-
<h2>开发者 累计午休工作时长</h2>
|
|
155
|
+
<h2 data-i18n="totalLunchTime">开发者 累计午休工作时长</h2>
|
|
153
156
|
<div class="tabs" id="tabsTotalLunchTime">
|
|
154
157
|
<button data-type="daily" class="active">按日</button>
|
|
155
158
|
<button data-type="weekly">按周</button>
|
|
@@ -161,18 +164,18 @@
|
|
|
161
164
|
|
|
162
165
|
<section class="chartsHalf">
|
|
163
166
|
<div class="chart-card">
|
|
164
|
-
<h2>开发者 午休工作总时长</h2>
|
|
167
|
+
<h2 data-i18n="totalLunchTime">开发者 午休工作总时长</h2>
|
|
165
168
|
<div id="authorTotalLunchTimeRank" class="rank-list"></div>
|
|
166
169
|
</div>
|
|
167
170
|
<div class="chart-card">
|
|
168
|
-
<h2>开发者 午休工作总时长</h2>
|
|
171
|
+
<h2 data-i18n="totalLunchTimeAll">开发者 午休工作总时长</h2>
|
|
169
172
|
<div id="authorTotalLunchTimeRankSummary" style="height: 600px"></div>
|
|
170
173
|
</div>
|
|
171
174
|
</section>
|
|
172
175
|
<!-- 午休 分析 End -->
|
|
173
176
|
|
|
174
177
|
<div class="chart-card">
|
|
175
|
-
<h2>开发者 累计加班时长</h2>
|
|
178
|
+
<h2 data-i18n="totalOvertime">开发者 累计加班时长</h2>
|
|
176
179
|
<div class="tabs" id="tabsTotalOvertime">
|
|
177
180
|
<button data-type="daily" class="active">按日</button>
|
|
178
181
|
<button data-type="weekly">按周</button>
|
|
@@ -184,18 +187,18 @@
|
|
|
184
187
|
|
|
185
188
|
<section class="chartsHalf">
|
|
186
189
|
<div class="chart-card">
|
|
187
|
-
<h2>开发者 加班总时长</h2>
|
|
190
|
+
<h2 data-i18n="totalOvertimeAll">开发者 加班总时长</h2>
|
|
188
191
|
<div id="authorTotalOvertimeRank" class="rank-list"></div>
|
|
189
192
|
</div>
|
|
190
193
|
<div class="chart-card">
|
|
191
|
-
<h2>开发者 加班总时长</h2>
|
|
194
|
+
<h2 data-i18n="totalOvertimeAll">开发者 加班总时长</h2>
|
|
192
195
|
<div id="authorTotalOvertimeRankSummary" style="height: 600px"></div>
|
|
193
196
|
</div>
|
|
194
197
|
</section>
|
|
195
198
|
|
|
196
199
|
<!-- 开发者 累计提交次数 -->
|
|
197
200
|
<div class="chart-card">
|
|
198
|
-
<h2>开发者 累计提交次数</h2>
|
|
201
|
+
<h2 data-i18n="totalCommits">开发者 累计提交次数</h2>
|
|
199
202
|
<div class="tabs" id="tabsTotalCommits">
|
|
200
203
|
<button data-type="daily" class="active">按日</button>
|
|
201
204
|
<button data-type="weekly">按周</button>
|
|
@@ -207,19 +210,19 @@
|
|
|
207
210
|
|
|
208
211
|
<section class="chartsHalf">
|
|
209
212
|
<div class="chart-card">
|
|
210
|
-
<h2>开发者 提交总次数</h2>
|
|
213
|
+
<h2 data-i18n="totalCommitsCount">开发者 提交总次数</h2>
|
|
211
214
|
<div id="authorTotalCommitsRank" class="rank-list"></div>
|
|
212
215
|
</div>
|
|
213
216
|
<div class="chart-card">
|
|
214
|
-
<h2>开发者 提交总次数</h2>
|
|
217
|
+
<h2 data-i18n="totalCommitsCount">开发者 提交总次数</h2>
|
|
215
218
|
<div id="authorTotalCommitsRankSummary" style="height: 600px"></div>
|
|
216
219
|
</div>
|
|
217
220
|
</section>
|
|
218
221
|
<!-- 开发者 累计提交次数 End -->
|
|
219
222
|
|
|
220
|
-
|
|
223
|
+
<!-- 开发者 累计提交Changed -->
|
|
221
224
|
<div class="chart-card" id="totalCommitsChangedCard">
|
|
222
|
-
<h2>开发者 累计提交Changed
|
|
225
|
+
<h2 data-i18n="totalCommitsChanged">开发者 累计提交Changed</h2>
|
|
223
226
|
<div class="tabs" id="tabsTotalCommitsChanged">
|
|
224
227
|
<button data-type="daily" class="active">按日</button>
|
|
225
228
|
<button data-type="weekly">按周</button>
|
|
@@ -231,12 +234,15 @@
|
|
|
231
234
|
|
|
232
235
|
<section class="chartsHalf" id="totalCommitsChangedRankSection">
|
|
233
236
|
<div class="chart-card">
|
|
234
|
-
<h2>开发者 提交Changed 总次数</h2>
|
|
237
|
+
<h2 data-i18n="totalCommitsChangedAll">开发者 提交Changed 总次数</h2>
|
|
235
238
|
<div id="authorTotalCommitsChangedRank" class="rank-list"></div>
|
|
236
239
|
</div>
|
|
237
240
|
<div class="chart-card">
|
|
238
|
-
<h2>开发者 提交Changed 总次数</h2>
|
|
239
|
-
<div
|
|
241
|
+
<h2 data-i18n="totalCommitsChangedAll">开发者 提交Changed 总次数</h2>
|
|
242
|
+
<div
|
|
243
|
+
id="authorTotalCommitsChangedRankSummary"
|
|
244
|
+
style="height: 600px"
|
|
245
|
+
></div>
|
|
240
246
|
</div>
|
|
241
247
|
</section>
|
|
242
248
|
<!-- 开发者 累计提交Changed End -->
|
|
@@ -250,18 +256,20 @@
|
|
|
250
256
|
</div> -->
|
|
251
257
|
|
|
252
258
|
<section class="table-card">
|
|
253
|
-
<h2>提交清单</h2>
|
|
259
|
+
<h2 data-i18n="commitsList">提交清单</h2>
|
|
254
260
|
<div id="tableControls">
|
|
255
261
|
<input
|
|
256
262
|
id="searchInput"
|
|
257
263
|
type="search"
|
|
258
264
|
placeholder="搜索作者/信息/Hash/Date"
|
|
265
|
+
data-i18n-placeholder="searchPlaceholder"
|
|
266
|
+
|
|
259
267
|
/>
|
|
260
268
|
<input type="date" id="startDate" />
|
|
261
269
|
<span>~</span>
|
|
262
270
|
<input type="date" id="endDate" />
|
|
263
|
-
<button id="clearDate">清除</button>
|
|
264
|
-
<label for="pageSize">每页显示</label>
|
|
271
|
+
<button id="clearDate" data-i18n="clear">清除</button>
|
|
272
|
+
<label for="pageSize" data-i18n="per">每页显示</label>
|
|
265
273
|
<select id="pageSize">
|
|
266
274
|
<option value="10">10</option>
|
|
267
275
|
<option value="20">20</option>
|
|
@@ -270,9 +278,9 @@
|
|
|
270
278
|
</select>
|
|
271
279
|
<label id="commitsTotal"></label>
|
|
272
280
|
<div class="pager">
|
|
273
|
-
<button id="prevPage">上一页</button>
|
|
281
|
+
<button id="prevPage" data-i18n="prevPage">上一页</button>
|
|
274
282
|
<span id="pageInfo"></span>
|
|
275
|
-
<button id="nextPage">下一页</button>
|
|
283
|
+
<button id="nextPage" data-i18n="nextPage">下一页</button>
|
|
276
284
|
</div>
|
|
277
285
|
</div>
|
|
278
286
|
<table id="commitsTable">
|
package/web/index2.html
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="zh">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<title data-i18n="dashboardTitle">Wukong Gitlog 加班分析看板</title>
|
|
6
|
+
</head>
|
|
7
|
+
|
|
8
|
+
<body>
|
|
9
|
+
<h1 data-i18n="dashboardTitle">
|
|
10
|
+
Wukong Gitlog 加班分析看板
|
|
11
|
+
</h1>
|
|
12
|
+
|
|
13
|
+
<!-- KPI -->
|
|
14
|
+
<section>
|
|
15
|
+
<h2 data-i18n="kpi">关键指标</h2>
|
|
16
|
+
<div id="kpi-container"></div>
|
|
17
|
+
</section>
|
|
18
|
+
|
|
19
|
+
<!-- 下班时间 vs 工作时间 -->
|
|
20
|
+
<section>
|
|
21
|
+
<h2 data-i18n="outsideVsInside">
|
|
22
|
+
下班时间 vs 工作时间提交占比
|
|
23
|
+
</h2>
|
|
24
|
+
<div id="ratio-chart"></div>
|
|
25
|
+
</section>
|
|
26
|
+
|
|
27
|
+
<!-- 每小时加班 -->
|
|
28
|
+
<section>
|
|
29
|
+
<h2 data-i18n="hourlyOvertime">
|
|
30
|
+
每小时加班分布 (小时 → 提交数)
|
|
31
|
+
</h2>
|
|
32
|
+
<div id="hourly-chart"></div>
|
|
33
|
+
</section>
|
|
34
|
+
|
|
35
|
+
<!-- 每日趋势 -->
|
|
36
|
+
<section>
|
|
37
|
+
<h2 data-i18n="dailyTrend">
|
|
38
|
+
按日提交趋势(次数)
|
|
39
|
+
</h2>
|
|
40
|
+
<div id="daily-chart"></div>
|
|
41
|
+
</section>
|
|
42
|
+
|
|
43
|
+
<!-- 最晚提交时间 -->
|
|
44
|
+
<section>
|
|
45
|
+
<h2 data-i18n="latestHourDaily">
|
|
46
|
+
每日最晚提交时间(小时)
|
|
47
|
+
</h2>
|
|
48
|
+
<div id="latest-hour-chart"></div>
|
|
49
|
+
</section>
|
|
50
|
+
|
|
51
|
+
<!-- 每日加班严重度 -->
|
|
52
|
+
<section>
|
|
53
|
+
<h2 data-i18n="dailySeverity">
|
|
54
|
+
每日超过下班的小时数
|
|
55
|
+
</h2>
|
|
56
|
+
<div id="severity-chart"></div>
|
|
57
|
+
</section>
|
|
58
|
+
|
|
59
|
+
<!-- 每周趋势 -->
|
|
60
|
+
<section>
|
|
61
|
+
<h2 data-i18n="weeklyTrend">
|
|
62
|
+
每周趋势(加班占比)
|
|
63
|
+
</h2>
|
|
64
|
+
<div id="weekly-chart"></div>
|
|
65
|
+
</section>
|
|
66
|
+
|
|
67
|
+
<!-- 每月趋势 -->
|
|
68
|
+
<section>
|
|
69
|
+
<h2 data-i18n="monthlyTrend">
|
|
70
|
+
每月趋势(加班占比)
|
|
71
|
+
</h2>
|
|
72
|
+
<div id="monthly-chart"></div>
|
|
73
|
+
</section>
|
|
74
|
+
|
|
75
|
+
<!-- 作者工作量 -->
|
|
76
|
+
<section>
|
|
77
|
+
<h2 data-i18n="authorChanges">
|
|
78
|
+
开发者 Changed 工作量趋势(行数)
|
|
79
|
+
</h2>
|
|
80
|
+
<div id="author-changes-chart"></div>
|
|
81
|
+
</section>
|
|
82
|
+
|
|
83
|
+
<!-- 作者最晚加班 -->
|
|
84
|
+
<section>
|
|
85
|
+
<h2 data-i18n="authorLatestOvertime">
|
|
86
|
+
开发者 加班最晚趋势(小时)
|
|
87
|
+
</h2>
|
|
88
|
+
<div id="author-latest-hour-chart"></div>
|
|
89
|
+
</section>
|
|
90
|
+
|
|
91
|
+
<!-- 作者加班次数 -->
|
|
92
|
+
<section>
|
|
93
|
+
<h2 data-i18n="authorOvertime">
|
|
94
|
+
开发者 加班趋势(次数)
|
|
95
|
+
</h2>
|
|
96
|
+
<div id="author-overtime-chart"></div>
|
|
97
|
+
</section>
|
|
98
|
+
|
|
99
|
+
<!-- 作者午休 -->
|
|
100
|
+
<section>
|
|
101
|
+
<h2 data-i18n="authorLunch">
|
|
102
|
+
开发者 午休最晚提交(小时)
|
|
103
|
+
</h2>
|
|
104
|
+
<div id="author-lunch-chart"></div>
|
|
105
|
+
</section>
|
|
106
|
+
|
|
107
|
+
<!-- 累计统计 -->
|
|
108
|
+
<section>
|
|
109
|
+
<h2 data-i18n="totalLunchTime">
|
|
110
|
+
开发者 累计午休工作时长
|
|
111
|
+
</h2>
|
|
112
|
+
<div id="total-lunch"></div>
|
|
113
|
+
</section>
|
|
114
|
+
|
|
115
|
+
<section>
|
|
116
|
+
<h2 data-i18n="totalOvertime">
|
|
117
|
+
开发者 累计加班时长
|
|
118
|
+
</h2>
|
|
119
|
+
<div id="total-overtime"></div>
|
|
120
|
+
</section>
|
|
121
|
+
|
|
122
|
+
<section>
|
|
123
|
+
<h2 data-i18n="totalCommits">
|
|
124
|
+
开发者 累计提交次数
|
|
125
|
+
</h2>
|
|
126
|
+
<div id="total-commits"></div>
|
|
127
|
+
</section>
|
|
128
|
+
|
|
129
|
+
<section>
|
|
130
|
+
<h2 data-i18n="totalCommitsChanged">
|
|
131
|
+
开发者 累计提交Changed
|
|
132
|
+
</h2>
|
|
133
|
+
<div id="total-changed"></div>
|
|
134
|
+
</section>
|
|
135
|
+
|
|
136
|
+
<!-- 提交清单 -->
|
|
137
|
+
<section>
|
|
138
|
+
<h2 data-i18n="commitsList">提交清单</h2>
|
|
139
|
+
|
|
140
|
+
<input
|
|
141
|
+
type="text"
|
|
142
|
+
data-i18n-placeholder="searchPlaceholder"
|
|
143
|
+
placeholder="搜索作者/信息/Hash/Date"
|
|
144
|
+
/>
|
|
145
|
+
|
|
146
|
+
<div id="commit-table"></div>
|
|
147
|
+
|
|
148
|
+
<div class="pagination">
|
|
149
|
+
<button data-i18n="prevPage">上一页</button>
|
|
150
|
+
<button data-i18n="nextPage">下一页</button>
|
|
151
|
+
</div>
|
|
152
|
+
</section>
|
|
153
|
+
|
|
154
|
+
<script>
|
|
155
|
+
window.__LANG__ = "__LANG__"
|
|
156
|
+
</script>
|
|
157
|
+
<script type="module" src="./app.js"></script>
|
|
158
|
+
</body>
|
|
159
|
+
</html>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
dashboardTitle: 'Wukong Gitlog Overtime Dashboard',
|
|
3
|
+
outsideVsInside: 'After-hours vs Working-hours Commit Ratio',
|
|
4
|
+
kpi: 'Key Metrics',
|
|
5
|
+
hourlyOvertime: 'Hourly Overtime Distribution (Hours → Commits)',
|
|
6
|
+
dailyTrend: 'Daily Commit Trend (Times)',
|
|
7
|
+
latestHourDaily: 'Latest Commit Hour Per Day',
|
|
8
|
+
dailySeverity: 'Hours Exceeding Off-work Time Per Day',
|
|
9
|
+
weeklyTrend: 'Weekly Trend (Overtime Ratio)',
|
|
10
|
+
monthlyTrend: 'Monthly Trend (Overtime Ratio)',
|
|
11
|
+
authorChanges: 'Developer Changed Workload Trend (Lines)',
|
|
12
|
+
authorLatestOvertime: 'Developer Latest Overtime Trend (Hour)',
|
|
13
|
+
authorOvertime: 'Developer Overtime Trend (Times)',
|
|
14
|
+
authorLunch: 'Developer Latest Lunch Commit (Hour)',
|
|
15
|
+
totalLunchTime: 'Developer Total Lunch Working Time',
|
|
16
|
+
totalLunchTimeAll: "Total All Lunch Working Time",
|
|
17
|
+
totalOvertime: 'Developer Total Overtime',
|
|
18
|
+
totalOvertimeAll: "Total All Overtime",
|
|
19
|
+
totalCommits: 'Developer Total Commits',
|
|
20
|
+
totalCommitsCount: 'Total All Commits',
|
|
21
|
+
totalCommitsChanged: 'Developer Total Commits Changed',
|
|
22
|
+
totalCommitsChangedAll: 'Total Commits Changed All',
|
|
23
|
+
commitsList: 'Commit List',
|
|
24
|
+
searchPlaceholder: 'Author/Message/Hash/Date',
|
|
25
|
+
per: ' ',
|
|
26
|
+
clear: 'Clear',
|
|
27
|
+
prevPage: 'Previous',
|
|
28
|
+
nextPage: 'Next'
|
|
29
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
dashboardTitle: "Wukong Gitlog 加班分析看板",
|
|
3
|
+
outsideVsInside: "下班时间 vs 工作时间提交占比",
|
|
4
|
+
kpi: "关键指标",
|
|
5
|
+
hourlyOvertime: "每小时加班分布 (小时 → 提交数)",
|
|
6
|
+
dailyTrend: "按日提交趋势(次数)",
|
|
7
|
+
latestHourDaily: "每日最晚提交时间(小时)",
|
|
8
|
+
dailySeverity: "每日超过下班的小时数",
|
|
9
|
+
weeklyTrend: "每周趋势(加班占比)",
|
|
10
|
+
monthlyTrend: "每月趋势(加班占比)",
|
|
11
|
+
authorChanges: "开发者 Changed 工作量趋势(行数)",
|
|
12
|
+
authorLatestOvertime: "开发者 加班最晚趋势(小时)",
|
|
13
|
+
authorOvertime: "开发者 加班趋势(次数)",
|
|
14
|
+
authorLunch: "开发者 午休最晚提交(小时)",
|
|
15
|
+
totalLunchTime: "开发者 累计午休工作时长",
|
|
16
|
+
totalLunchTimeAll: "开发者 午休工作总时长",
|
|
17
|
+
totalOvertime: "开发者 累计加班时长",
|
|
18
|
+
totalOvertimeAll: "开发者 加班总时长",
|
|
19
|
+
totalCommits: "开发者 累计提交次数",
|
|
20
|
+
totalCommitsCount: "开发者 提交总次数",
|
|
21
|
+
totalCommitsChanged: "开发者 累计提交Changed",
|
|
22
|
+
totalCommitsChangedAll: '开发者 提交Changed 总次数',
|
|
23
|
+
commitsList: "提交清单",
|
|
24
|
+
searchPlaceholder: "搜索作者/信息/Hash/Date",
|
|
25
|
+
per: '每页显示',
|
|
26
|
+
clear: '清除',
|
|
27
|
+
prevPage: "上一页",
|
|
28
|
+
nextPage: "下一页"
|
|
29
|
+
}
|