chore: 基本服务器版本完成

This commit is contained in:
2025-11-12 08:23:41 +08:00
parent 22016ac339
commit 58ad47d124
9 changed files with 2084 additions and 490 deletions

408
data/config.json Normal file
View File

@@ -0,0 +1,408 @@
{
"individualRankings": [
{
"id": 1,
"name": "张三",
"score": 985,
"level": "SSS",
"avatar": "👑",
"department": "销售部",
"completedTasks": 48,
"bonus": 5000
},
{
"id": 2,
"name": "李四",
"score": 972,
"level": "SSS",
"avatar": "🥇",
"department": "技术部",
"completedTasks": 45,
"bonus": 3000
},
{
"id": 3,
"name": "王五",
"score": 958,
"level": "SS",
"avatar": "🥈",
"department": "市场部",
"completedTasks": 42,
"bonus": 2000
},
{
"id": 4,
"name": "赵六",
"score": 923,
"level": "SS",
"avatar": "🥉",
"department": "财务部",
"completedTasks": 40,
"bonus": 1500
},
{
"id": 5,
"name": "钱七",
"score": 897,
"level": "S",
"avatar": "⭐",
"department": "人力资源部",
"completedTasks": 38,
"bonus": 1000
},
{
"id": 6,
"name": "孙八",
"score": 876,
"level": "S",
"avatar": "⭐",
"department": "销售部",
"completedTasks": 36,
"bonus": 1000
},
{
"id": 7,
"name": "周九",
"score": 854,
"level": "A",
"avatar": "🔥",
"department": "技术部",
"completedTasks": 34,
"bonus": 800
},
{
"id": 8,
"name": "吴十",
"score": 832,
"level": "A",
"avatar": "🔥",
"department": "市场部",
"completedTasks": 32,
"bonus": 800
},
{
"id": 9,
"name": "郑十一",
"score": 810,
"level": "B",
"avatar": "⚡",
"department": "财务部",
"completedTasks": 30,
"bonus": 500
},
{
"id": 10,
"name": "王十二",
"score": 795,
"level": "B",
"avatar": "⚡",
"department": "人力资源部",
"completedTasks": 28,
"bonus": 500
},
{
"id": 11,
"name": "李十三",
"score": 782,
"level": "B",
"avatar": "⚡",
"department": "销售部",
"completedTasks": 26,
"bonus": 500
},
{
"id": 12,
"name": "张十四",
"score": 765,
"level": "B",
"avatar": "⚡",
"department": "技术部",
"completedTasks": 24,
"bonus": 500
},
{
"id": 13,
"name": "王十五",
"score": 748,
"level": "B",
"avatar": "⚡",
"department": "市场部",
"completedTasks": 22,
"bonus": 500
},
{
"id": 14,
"name": "赵十六",
"score": 732,
"level": "C",
"avatar": "🎯",
"department": "财务部",
"completedTasks": 20,
"bonus": 300
},
{
"id": 15,
"name": "钱十七",
"score": 715,
"level": "C",
"avatar": "🎯",
"department": "人力资源部",
"completedTasks": 18,
"bonus": 300
}
],
"teamRankings": [
{
"id": 1,
"name": "王者之师",
"totalScore": 4850,
"memberCount": 5,
"level": "SSS",
"leader": "张三",
"completedTasks": 210,
"bonus": 15000
},
{
"id": 2,
"name": "战无不胜",
"totalScore": 4680,
"memberCount": 5,
"level": "SS",
"leader": "李四",
"completedTasks": 198,
"bonus": 10000
},
{
"id": 3,
"name": "超越极限",
"totalScore": 4520,
"memberCount": 5,
"level": "SS",
"leader": "王五",
"completedTasks": 185,
"bonus": 8000
},
{
"id": 4,
"name": "精英战队",
"totalScore": 4280,
"memberCount": 5,
"level": "S",
"leader": "赵六",
"completedTasks": 172,
"bonus": 6000
},
{
"id": 5,
"name": "梦想之巅",
"totalScore": 4150,
"memberCount": 5,
"level": "S",
"leader": "钱七",
"completedTasks": 165,
"bonus": 6000
},
{
"id": 6,
"name": "无敌战队",
"totalScore": 3980,
"memberCount": 5,
"level": "A",
"leader": "孙八",
"completedTasks": 155,
"bonus": 4000
},
{
"id": 7,
"name": "冲锋陷阵",
"totalScore": 3850,
"memberCount": 5,
"level": "A",
"leader": "周九",
"completedTasks": 148,
"bonus": 4000
},
{
"id": 8,
"name": "锐不可当",
"totalScore": 3720,
"memberCount": 5,
"level": "A",
"leader": "吴十",
"completedTasks": 142,
"bonus": 4000
},
{
"id": 9,
"name": "同心协力",
"totalScore": 3600,
"memberCount": 5,
"level": "B",
"leader": "郑十一",
"completedTasks": 135,
"bonus": 2000
},
{
"id": 10,
"name": "众志成城",
"totalScore": 3480,
"memberCount": 5,
"level": "B",
"leader": "王十二",
"completedTasks": 128,
"bonus": 2000
},
{
"id": 11,
"name": "气势如虹",
"totalScore": 3350,
"memberCount": 5,
"level": "B",
"leader": "李十三",
"completedTasks": 122,
"bonus": 2000
},
{
"id": 12,
"name": "披荆斩棘",
"totalScore": 3220,
"memberCount": 5,
"level": "B",
"leader": "张十四",
"completedTasks": 115,
"bonus": 2000
},
{
"id": 13,
"name": "勇攀高峰",
"totalScore": 3100,
"memberCount": 5,
"level": "C",
"leader": "王十五",
"completedTasks": 108,
"bonus": 1000
},
{
"id": 14,
"name": "力争上游",
"totalScore": 2980,
"memberCount": 5,
"level": "C",
"leader": "赵十六",
"completedTasks": 102,
"bonus": 1000
},
{
"id": 15,
"name": "蓄势待发",
"totalScore": 2850,
"memberCount": 5,
"level": "C",
"leader": "钱十七",
"completedTasks": 95,
"bonus": 1000
}
],
"bonusRules": [
{
"rank": "1-3",
"individualBonus": "5000元, 3000元, 2000元",
"teamBonus": "15000元, 10000元, 8000元",
"description": "顶尖表现,高额奖励"
},
{
"rank": "4-6",
"individualBonus": "1500元, 1000元, 1000元",
"teamBonus": "6000元, 6000元",
"description": "优秀表现,丰厚激励"
},
{
"rank": "7-10",
"individualBonus": "800元, 800元, 500元, 500元",
"teamBonus": "无",
"description": "良好表现,基础奖励"
}
],
"systemUsers": [
{
"username": "admin",
"password": "admin123",
"role": "admin"
},
{
"username": "manager",
"password": "manager123",
"role": "manager"
}
],
"displayConfig": {
"individual": {
"showLevel": false,
"showDepartment": false,
"scoreColumn": {
"displayName": "签单金额",
"displayStyle": "amount"
},
"columnWidths": {
"rank": 60,
"avatar": 60,
"name": 1,
"score": 80,
"level": 80,
"department": 1,
"bonus": 80
}
},
"team": {
"showMemberCount": false,
"showLeader": false,
"totalScoreColumn": {
"displayName": "签单金额",
"displayStyle": "amount"
},
"columnWidths": {
"rank": 60,
"name": 1,
"score": 80,
"memberCount": 60,
"leader": 1,
"bonus": 80
}
}
},
"battleEndTime": {
"date": "2026-02-01",
"time": "23:59:59"
},
"drumConfig": {
"sound": {
"volume": 1,
"frequency1": 150,
"frequency2": 100,
"attackTime": 0.01,
"decayTime": 0.3,
"type1": "sine",
"type2": "triangle",
"enabled": true
},
"animation": {
"beatInterval": 200,
"beatScale": 1.3,
"beatTranslateY": -15,
"beatRotate": 5,
"idlePulseDuration": 2,
"beatDuration": 100,
"enabled": true
},
"pattern": {
"strongBeats": [
1,
4
],
"totalBeats": 4,
"accentMultiplier": 1.5,
"accentFrequencyOffset": 10,
"accentAnimation": 50
}
}
}

850
package-lock.json generated
View File

@@ -8,6 +8,8 @@
"name": "vs100",
"version": "0.0.0",
"dependencies": {
"cors": "^2.8.5",
"express": "^5.1.0",
"vue": "^3.5.24",
"vue-router": "^4.6.3"
},
@@ -955,12 +957,190 @@
"integrity": "sha512-9cwHL2EsJBdi8NY22pngYYWzkTDhld6fAD6jlaeloNGciNSJL6bLpbxVgXl96X00Jtc6YWQv96YA/0sxex/k1A==",
"license": "MIT"
},
"node_modules/accepts": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/accepts/-/accepts-2.0.0.tgz",
"integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
"license": "MIT",
"dependencies": {
"mime-types": "^3.0.0",
"negotiator": "^1.0.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/body-parser": {
"version": "2.2.0",
"resolved": "https://registry.npmmirror.com/body-parser/-/body-parser-2.2.0.tgz",
"integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==",
"license": "MIT",
"dependencies": {
"bytes": "^3.1.2",
"content-type": "^1.0.5",
"debug": "^4.4.0",
"http-errors": "^2.0.0",
"iconv-lite": "^0.6.3",
"on-finished": "^2.4.1",
"qs": "^6.14.0",
"raw-body": "^3.0.0",
"type-is": "^2.0.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/bytes": {
"version": "3.1.2",
"resolved": "https://registry.npmmirror.com/bytes/-/bytes-3.1.2.tgz",
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/call-bind-apply-helpers": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/call-bound": {
"version": "1.0.4",
"resolved": "https://registry.npmmirror.com/call-bound/-/call-bound-1.0.4.tgz",
"integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.2",
"get-intrinsic": "^1.3.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/content-disposition": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/content-disposition/-/content-disposition-1.0.0.tgz",
"integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==",
"license": "MIT",
"dependencies": {
"safe-buffer": "5.2.1"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/content-type": {
"version": "1.0.5",
"resolved": "https://registry.npmmirror.com/content-type/-/content-type-1.0.5.tgz",
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cookie": {
"version": "0.7.2",
"resolved": "https://registry.npmmirror.com/cookie/-/cookie-0.7.2.tgz",
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cookie-signature": {
"version": "1.2.2",
"resolved": "https://registry.npmmirror.com/cookie-signature/-/cookie-signature-1.2.2.tgz",
"integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==",
"license": "MIT",
"engines": {
"node": ">=6.6.0"
}
},
"node_modules/cors": {
"version": "2.8.5",
"resolved": "https://registry.npmmirror.com/cors/-/cors-2.8.5.tgz",
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
"license": "MIT",
"dependencies": {
"object-assign": "^4",
"vary": "^1"
},
"engines": {
"node": ">= 0.10"
}
},
"node_modules/csstype": {
"version": "3.1.3",
"resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
"license": "MIT"
},
"node_modules/debug": {
"version": "4.4.3",
"resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz",
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/depd": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz",
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz",
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.1",
"es-errors": "^1.3.0",
"gopd": "^1.2.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
"license": "MIT"
},
"node_modules/encodeurl": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/encodeurl/-/encodeurl-2.0.0.tgz",
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/entities": {
"version": "4.5.0",
"resolved": "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz",
@@ -973,6 +1153,36 @@
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/es-define-property": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz",
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-errors": {
"version": "1.3.0",
"resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-object-atoms": {
"version": "1.1.1",
"resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/esbuild": {
"version": "0.25.12",
"resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.25.12.tgz",
@@ -1015,12 +1225,69 @@
"@esbuild/win32-x64": "0.25.12"
}
},
"node_modules/escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
"license": "MIT"
},
"node_modules/estree-walker": {
"version": "2.0.2",
"resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
"license": "MIT"
},
"node_modules/etag": {
"version": "1.8.1",
"resolved": "https://registry.npmmirror.com/etag/-/etag-1.8.1.tgz",
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/express": {
"version": "5.1.0",
"resolved": "https://registry.npmmirror.com/express/-/express-5.1.0.tgz",
"integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==",
"license": "MIT",
"dependencies": {
"accepts": "^2.0.0",
"body-parser": "^2.2.0",
"content-disposition": "^1.0.0",
"content-type": "^1.0.5",
"cookie": "^0.7.1",
"cookie-signature": "^1.2.1",
"debug": "^4.4.0",
"encodeurl": "^2.0.0",
"escape-html": "^1.0.3",
"etag": "^1.8.1",
"finalhandler": "^2.1.0",
"fresh": "^2.0.0",
"http-errors": "^2.0.0",
"merge-descriptors": "^2.0.0",
"mime-types": "^3.0.0",
"on-finished": "^2.4.1",
"once": "^1.4.0",
"parseurl": "^1.3.3",
"proxy-addr": "^2.0.7",
"qs": "^6.14.0",
"range-parser": "^1.2.1",
"router": "^2.2.0",
"send": "^1.1.0",
"serve-static": "^2.2.0",
"statuses": "^2.0.1",
"type-is": "^2.0.1",
"vary": "^1.1.2"
},
"engines": {
"node": ">= 18"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/fdir": {
"version": "6.5.0",
"resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz",
@@ -1039,6 +1306,41 @@
}
}
},
"node_modules/finalhandler": {
"version": "2.1.0",
"resolved": "https://registry.npmmirror.com/finalhandler/-/finalhandler-2.1.0.tgz",
"integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==",
"license": "MIT",
"dependencies": {
"debug": "^4.4.0",
"encodeurl": "^2.0.0",
"escape-html": "^1.0.3",
"on-finished": "^2.4.1",
"parseurl": "^1.3.3",
"statuses": "^2.0.1"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/forwarded": {
"version": "0.2.0",
"resolved": "https://registry.npmmirror.com/forwarded/-/forwarded-0.2.0.tgz",
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/fresh": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/fresh/-/fresh-2.0.0.tgz",
"integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz",
@@ -1054,6 +1356,146 @@
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-intrinsic": {
"version": "1.3.0",
"resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.2",
"es-define-property": "^1.0.1",
"es-errors": "^1.3.0",
"es-object-atoms": "^1.1.1",
"function-bind": "^1.1.2",
"get-proto": "^1.0.1",
"gopd": "^1.2.0",
"has-symbols": "^1.1.0",
"hasown": "^2.0.2",
"math-intrinsics": "^1.1.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz",
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
"license": "MIT",
"dependencies": {
"dunder-proto": "^1.0.1",
"es-object-atoms": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz",
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-symbols": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz",
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/http-errors": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/http-errors/-/http-errors-2.0.0.tgz",
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
"license": "MIT",
"dependencies": {
"depd": "2.0.0",
"inherits": "2.0.4",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"toidentifier": "1.0.1"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/http-errors/node_modules/statuses": {
"version": "2.0.1",
"resolved": "https://registry.npmmirror.com/statuses/-/statuses-2.0.1.tgz",
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"license": "ISC"
},
"node_modules/ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmmirror.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
"license": "MIT",
"engines": {
"node": ">= 0.10"
}
},
"node_modules/is-promise": {
"version": "4.0.0",
"resolved": "https://registry.npmmirror.com/is-promise/-/is-promise-4.0.0.tgz",
"integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
"license": "MIT"
},
"node_modules/magic-string": {
"version": "0.30.21",
"resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz",
@@ -1063,6 +1505,63 @@
"@jridgewell/sourcemap-codec": "^1.5.5"
}
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/media-typer": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/media-typer/-/media-typer-1.1.0.tgz",
"integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/merge-descriptors": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
"integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==",
"license": "MIT",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/mime-db": {
"version": "1.54.0",
"resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.54.0.tgz",
"integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "3.0.1",
"resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-3.0.1.tgz",
"integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
"license": "MIT",
"dependencies": {
"mime-db": "^1.54.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
"node_modules/nanoid": {
"version": "3.3.11",
"resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz",
@@ -1081,6 +1580,76 @@
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/negotiator": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/negotiator/-/negotiator-1.0.0.tgz",
"integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/object-inspect": {
"version": "1.13.4",
"resolved": "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.13.4.tgz",
"integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/on-finished": {
"version": "2.4.1",
"resolved": "https://registry.npmmirror.com/on-finished/-/on-finished-2.4.1.tgz",
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
"license": "MIT",
"dependencies": {
"ee-first": "1.1.1"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"license": "ISC",
"dependencies": {
"wrappy": "1"
}
},
"node_modules/parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmmirror.com/parseurl/-/parseurl-1.3.3.tgz",
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/path-to-regexp": {
"version": "8.3.0",
"resolved": "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-8.3.0.tgz",
"integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==",
"license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz",
@@ -1128,6 +1697,74 @@
"node": "^10 || ^12 || >=14"
}
},
"node_modules/proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmmirror.com/proxy-addr/-/proxy-addr-2.0.7.tgz",
"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
"license": "MIT",
"dependencies": {
"forwarded": "0.2.0",
"ipaddr.js": "1.9.1"
},
"engines": {
"node": ">= 0.10"
}
},
"node_modules/qs": {
"version": "6.14.0",
"resolved": "https://registry.npmmirror.com/qs/-/qs-6.14.0.tgz",
"integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
"license": "BSD-3-Clause",
"dependencies": {
"side-channel": "^1.1.0"
},
"engines": {
"node": ">=0.6"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmmirror.com/range-parser/-/range-parser-1.2.1.tgz",
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/raw-body": {
"version": "3.0.1",
"resolved": "https://registry.npmmirror.com/raw-body/-/raw-body-3.0.1.tgz",
"integrity": "sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==",
"license": "MIT",
"dependencies": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
"iconv-lite": "0.7.0",
"unpipe": "1.0.0"
},
"engines": {
"node": ">= 0.10"
}
},
"node_modules/raw-body/node_modules/iconv-lite": {
"version": "0.7.0",
"resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.7.0.tgz",
"integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==",
"license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
},
"engines": {
"node": ">=0.10.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/rollup": {
"version": "4.53.2",
"resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.53.2.tgz",
@@ -1170,6 +1807,163 @@
"fsevents": "~2.3.2"
}
},
"node_modules/router": {
"version": "2.2.0",
"resolved": "https://registry.npmmirror.com/router/-/router-2.2.0.tgz",
"integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==",
"license": "MIT",
"dependencies": {
"debug": "^4.4.0",
"depd": "^2.0.0",
"is-promise": "^4.0.0",
"parseurl": "^1.3.3",
"path-to-regexp": "^8.0.0"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT"
},
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"license": "MIT"
},
"node_modules/send": {
"version": "1.2.0",
"resolved": "https://registry.npmmirror.com/send/-/send-1.2.0.tgz",
"integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==",
"license": "MIT",
"dependencies": {
"debug": "^4.3.5",
"encodeurl": "^2.0.0",
"escape-html": "^1.0.3",
"etag": "^1.8.1",
"fresh": "^2.0.0",
"http-errors": "^2.0.0",
"mime-types": "^3.0.1",
"ms": "^2.1.3",
"on-finished": "^2.4.1",
"range-parser": "^1.2.1",
"statuses": "^2.0.1"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/serve-static": {
"version": "2.2.0",
"resolved": "https://registry.npmmirror.com/serve-static/-/serve-static-2.2.0.tgz",
"integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==",
"license": "MIT",
"dependencies": {
"encodeurl": "^2.0.0",
"escape-html": "^1.0.3",
"parseurl": "^1.3.3",
"send": "^1.2.0"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/setprototypeof": {
"version": "1.2.0",
"resolved": "https://registry.npmmirror.com/setprototypeof/-/setprototypeof-1.2.0.tgz",
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
"license": "ISC"
},
"node_modules/side-channel": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/side-channel/-/side-channel-1.1.0.tgz",
"integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"object-inspect": "^1.13.3",
"side-channel-list": "^1.0.0",
"side-channel-map": "^1.0.1",
"side-channel-weakmap": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/side-channel-list": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/side-channel-list/-/side-channel-list-1.0.0.tgz",
"integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"object-inspect": "^1.13.3"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/side-channel-map": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/side-channel-map/-/side-channel-map-1.0.1.tgz",
"integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
"license": "MIT",
"dependencies": {
"call-bound": "^1.0.2",
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.5",
"object-inspect": "^1.13.3"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/side-channel-weakmap": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
"integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
"license": "MIT",
"dependencies": {
"call-bound": "^1.0.2",
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.5",
"object-inspect": "^1.13.3",
"side-channel-map": "^1.0.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz",
@@ -1179,6 +1973,15 @@
"node": ">=0.10.0"
}
},
"node_modules/statuses": {
"version": "2.0.2",
"resolved": "https://registry.npmmirror.com/statuses/-/statuses-2.0.2.tgz",
"integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/tinyglobby": {
"version": "0.2.15",
"resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.15.tgz",
@@ -1196,6 +1999,47 @@
"url": "https://github.com/sponsors/SuperchupuDev"
}
},
"node_modules/toidentifier": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/toidentifier/-/toidentifier-1.0.1.tgz",
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
"license": "MIT",
"engines": {
"node": ">=0.6"
}
},
"node_modules/type-is": {
"version": "2.0.1",
"resolved": "https://registry.npmmirror.com/type-is/-/type-is-2.0.1.tgz",
"integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
"license": "MIT",
"dependencies": {
"content-type": "^1.0.5",
"media-typer": "^1.1.0",
"mime-types": "^3.0.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/vary": {
"version": "1.1.2",
"resolved": "https://registry.npmmirror.com/vary/-/vary-1.1.2.tgz",
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/vite": {
"version": "7.2.2",
"resolved": "https://registry.npmmirror.com/vite/-/vite-7.2.2.tgz",
@@ -1306,6 +2150,12 @@
"peerDependencies": {
"vue": "^3.5.0"
}
},
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"license": "ISC"
}
}
}

View File

@@ -6,9 +6,12 @@
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
"preview": "vite preview",
"start": "node server.js"
},
"dependencies": {
"cors": "^2.8.5",
"express": "^5.1.0",
"vue": "^3.5.24",
"vue-router": "^4.6.3"
},

77
server.js Normal file
View File

@@ -0,0 +1,77 @@
import express from 'express';
import cors from 'cors';
import path from 'path';
import fs from 'fs';
import { fileURLToPath } from 'url';
// 获取当前文件的目录路径
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const app = express();
const PORT = 3000;
const CONFIG_FILE_PATH = path.join(__dirname, 'data', 'config.json');
// 中间件
app.use(cors());
app.use(express.json());
// 静态文件服务Vue应用
app.use(express.static(path.join(__dirname, 'dist')));
// API: 获取配置数据
app.get('/api/config', (req, res) => {
try {
const configData = fs.readFileSync(CONFIG_FILE_PATH, 'utf8');
res.json(JSON.parse(configData));
} catch (error) {
console.error('读取配置文件失败:', error);
res.status(500).json({ error: '读取配置文件失败' });
}
});
// API: 保存配置数据
app.post('/api/config', (req, res) => {
try {
fs.writeFileSync(CONFIG_FILE_PATH, JSON.stringify(req.body, null, 2), 'utf8');
res.json({ success: true });
} catch (error) {
console.error('保存配置文件失败:', error);
res.status(500).json({ error: '保存配置文件失败' });
}
});
// 处理Vue Router历史模式 - 使用正则表达式代替通配符
app.get(/^((?!\/api).)*$/, (req, res) => {
res.sendFile(path.join(__dirname, 'dist', 'index.html'));
});
// 启动服务器并监听错误
const server = app.listen(PORT, () => {
console.log(`服务器运行在 http://localhost:${PORT}`);
console.log('服务器已成功启动,可以访问 http://localhost:3000');
console.log('API端点: GET/POST /api/config');
});
// 监听服务器错误
server.on('error', (error) => {
console.error('服务器错误:', error);
if (error.code === 'EADDRINUSE') {
console.error(`端口 ${PORT} 已被占用,请尝试其他端口。`);
}
});
// 监听SIGINT信号Ctrl+C
process.on('SIGINT', () => {
console.log('正在关闭服务器...');
server.close(() => {
console.log('服务器已关闭');
process.exit(0);
});
});
// 确保服务器持续运行
setInterval(() => {
// 保持服务器活动的空操作
}, 60000); // 每分钟执行一次

View File

@@ -1,450 +1,155 @@
// 模拟数据 - 个人排名
export const individualRankings = [
{
id: 1,
name: '张三',
score: 985,
level: 'SSS',
avatar: '👑',
department: '销售部',
completedTasks: 48,
bonus: 5000
},
{
id: 2,
name: '李四',
score: 972,
level: 'SSS',
avatar: '🥇',
department: '技术部',
completedTasks: 45,
bonus: 3000
},
{
id: 3,
name: '王五',
score: 958,
level: 'SS',
avatar: '🥈',
department: '市场部',
completedTasks: 42,
bonus: 2000
},
{
id: 4,
name: '赵六',
score: 923,
level: 'SS',
avatar: '🥉',
department: '财务部',
completedTasks: 40,
bonus: 1500
},
{
id: 5,
name: '钱七',
score: 897,
level: 'S',
avatar: '⭐',
department: '人力资源部',
completedTasks: 38,
bonus: 1000
},
{
id: 6,
name: '孙八',
score: 876,
level: 'S',
avatar: '⭐',
department: '销售部',
completedTasks: 36,
bonus: 1000
},
{
id: 7,
name: '周九',
score: 854,
level: 'A',
avatar: '🔥',
department: '技术部',
completedTasks: 34,
bonus: 800
},
{
id: 8,
name: '吴十',
score: 832,
level: 'A',
avatar: '🔥',
department: '市场部',
completedTasks: 32,
bonus: 800
},
{
id: 9,
name: '郑十一',
score: 810,
level: 'B',
avatar: '⚡',
department: '财务部',
completedTasks: 30,
bonus: 500
},
{
id: 10,
name: '王十二',
score: 795,
level: 'B',
avatar: '⚡',
department: '人力资源部',
completedTasks: 28,
bonus: 500
},
{
id: 11,
name: '李十三',
score: 782,
level: 'B',
avatar: '⚡',
department: '销售部',
completedTasks: 26,
bonus: 500
},
{
id: 12,
name: '张十四',
score: 765,
level: 'B',
avatar: '⚡',
department: '技术部',
completedTasks: 24,
bonus: 500
},
{
id: 13,
name: '王十五',
score: 748,
level: 'B',
avatar: '⚡',
department: '市场部',
completedTasks: 22,
bonus: 500
},
{
id: 14,
name: '赵十六',
score: 732,
level: 'C',
avatar: '🎯',
department: '财务部',
completedTasks: 20,
bonus: 300
},
{
id: 15,
name: '钱十七',
score: 715,
level: 'C',
avatar: '🎯',
department: '人力资源部',
completedTasks: 18,
bonus: 300
}
import {
getIndividualRankings,
saveIndividualRankings as saveIndividualRankingsToConfig,
getTeamRankings,
saveTeamRankings as saveTeamRankingsToConfig,
getSystemUsers,
saveSystemUsers as saveSystemUsersToConfig,
addSystemUser as addSystemUserToConfig,
deleteSystemUser as deleteSystemUserToConfig,
updateSystemUser as updateSystemUserToConfig,
getDisplayConfig,
saveDisplayConfig as saveDisplayConfigToConfig,
getBattleEndTime,
saveBattleEndTime as saveBattleEndTimeToConfig,
getDrumConfig,
saveDrumConfig as saveDrumConfigToConfig,
getBonusRules,
saveBonusRules as saveBonusRulesToConfig
} from '../services/configService';
// 初始化数据
export let individualRankings = getIndividualRankings();
export let teamRankings = getTeamRankings();
export let bonusRules = getBonusRules() || [
{ rank: '1-3', description: '前三名', individualBonus: '¥10000, ¥8000, ¥5000', teamBonus: '¥50000, ¥30000, ¥20000' },
{ rank: '4-10', description: '四至十名', individualBonus: '¥3000/人', teamBonus: '¥10000/队' },
{ rank: '11-20', description: '十一至二十名', individualBonus: '¥1000/人', teamBonus: '¥5000/队' }
];
// 模拟数据 - 战队排名
export const teamRankings = [
{
id: 1,
name: '王者之师',
totalScore: 4850,
memberCount: 5,
level: 'SSS',
leader: '张三',
completedTasks: 210,
bonus: 15000
},
{
id: 2,
name: '战无不胜',
totalScore: 4680,
memberCount: 5,
level: 'SS',
leader: '李四',
completedTasks: 198,
bonus: 10000
},
{
id: 3,
name: '超越极限',
totalScore: 4520,
memberCount: 5,
level: 'SS',
leader: '王五',
completedTasks: 185,
bonus: 8000
},
{
id: 4,
name: '精英战队',
totalScore: 4280,
memberCount: 5,
level: 'S',
leader: '赵六',
completedTasks: 172,
bonus: 6000
},
{
id: 5,
name: '梦想之巅',
totalScore: 4150,
memberCount: 5,
level: 'S',
leader: '钱七',
completedTasks: 165,
bonus: 6000
},
{
id: 6,
name: '无敌战队',
totalScore: 3980,
memberCount: 5,
level: 'A',
leader: '孙八',
completedTasks: 155,
bonus: 4000
},
{
id: 7,
name: '冲锋陷阵',
totalScore: 3850,
memberCount: 5,
level: 'A',
leader: '周九',
completedTasks: 148,
bonus: 4000
},
{
id: 8,
name: '锐不可当',
totalScore: 3720,
memberCount: 5,
level: 'A',
leader: '吴十',
completedTasks: 142,
bonus: 4000
},
{
id: 9,
name: '同心协力',
totalScore: 3600,
memberCount: 5,
level: 'B',
leader: '郑十一',
completedTasks: 135,
bonus: 2000
},
{
id: 10,
name: '众志成城',
totalScore: 3480,
memberCount: 5,
level: 'B',
leader: '王十二',
completedTasks: 128,
bonus: 2000
},
{
id: 11,
name: '气势如虹',
totalScore: 3350,
memberCount: 5,
level: 'B',
leader: '李十三',
completedTasks: 122,
bonus: 2000
},
{
id: 12,
name: '披荆斩棘',
totalScore: 3220,
memberCount: 5,
level: 'B',
leader: '张十四',
completedTasks: 115,
bonus: 2000
},
{
id: 13,
name: '勇攀高峰',
totalScore: 3100,
memberCount: 5,
level: 'C',
leader: '王十五',
completedTasks: 108,
bonus: 1000
},
{
id: 14,
name: '力争上游',
totalScore: 2980,
memberCount: 5,
level: 'C',
leader: '赵十六',
completedTasks: 102,
bonus: 1000
},
{
id: 15,
name: '蓄势待发',
totalScore: 2850,
memberCount: 5,
level: 'C',
leader: '钱十七',
completedTasks: 95,
bonus: 1000
}
];
// 奖金设置说明
export const bonusRules = [
{
rank: '1-3',
individualBonus: '5000元, 3000元, 2000元',
teamBonus: '15000元, 10000元, 8000元',
description: '顶尖表现,高额奖励'
},
{
rank: '4-6',
individualBonus: '1500元, 1000元, 1000元',
teamBonus: '6000元, 6000元',
description: '优秀表现,丰厚激励'
},
{
rank: '7-10',
individualBonus: '800元, 800元, 500元, 500元',
teamBonus: '无',
description: '良好表现,基础奖励'
}
];
// 系统用户(用于后台登录)
export const systemUsers = [
{
username: 'admin',
password: 'admin123',
role: 'admin'
},
{
username: 'manager',
password: 'manager123',
role: 'manager'
}
];
// 显示配置
export const displayConfig = {
// 个人排名显示配置
individual: {
showLevel: false, // 显示等级列
showDepartment: false, // 显示部门列
scoreColumn: {
displayName: '签单金额', // 列显示名称
displayStyle: 'amount' // 显示样式: 'amount'(金额) 或 'number'(普通数字)
},
columnWidths: {
rank: 60, // 排名列宽度
avatar: 60, // 头像列宽度
name: 1, // 姓名列宽度1表示自动填充
score: 80, // 分数列宽度
level: 80, // 等级列宽度
department: 1, // 部门列宽度1表示自动填充
bonus: 80 // 奖金列宽度
}
},
// 战队排名显示配置
team: {
showMemberCount: false, // 显示人数列
showLeader: false, // 显示队长列
totalScoreColumn: {
displayName: '签单金额', // 列显示名称
displayStyle: 'amount' // 显示样式: 'amount'(金额) 或 'number'(普通数字)
},
columnWidths: {
rank: 60, // 排名列宽度
name: 1, // 战队名列宽度1表示自动填充
score: 80, // 分数列宽度
memberCount: 60, // 人数列宽度
leader: 1, // 队长列宽度1表示自动填充
bonus: 80 // 奖金列宽度
}
}
};
// 结束时间配置(精确到秒)
export let battleEndTime = {
date: '2026-02-01',
time: '23:59:59'
};
// 战鼓参数配置
export let drumConfig = {
// 音效参数
sound: {
volume: 1.0, // 音量 0-1
frequency1: 150, // 第一个音调频率
frequency2: 100, // 第二个音调频率
attackTime: 0.01, // 起音时间
decayTime: 0.3, // 衰减时间
type1: 'sine', // 第一个振荡器类型
type2: 'triangle' // 第二个振荡器类型
},
// 动画参数
animation: {
beatInterval: 200, // 节拍间隔(毫秒)
beatScale: 1.3, // 跳动缩放比例
beatTranslateY: -15, // 跳动上下位移
beatRotate: 5, // 跳动旋转角度
idlePulseDuration: 2, // 闲置脉动持续时间
beatDuration: 100 // 单次跳动持续时间
},
// 节拍模式
pattern: {
strongBeats: [1, 4], // 强拍位置1-4拍
totalBeats: 4 // 每小节总拍数
}
};
export let systemUsers = getSystemUsers();
export let displayConfig = getDisplayConfig();
export let battleEndTime = getBattleEndTime();
export let drumConfig = getDrumConfig();
// 保存结束时间
export const saveBattleEndTime = (endTime) => {
battleEndTime = endTime;
export const saveBattleEndTime = async (endTime) => {
battleEndTime = { ...endTime };
console.log('保存结束时间:', battleEndTime);
return await saveBattleEndTimeToConfig(endTime);
};
// 保存数据的方法(模拟本地存储)
export const saveIndividualRankings = (data) => {
// 这里只是模拟实际项目中可以考虑使用localStorage或后端API
// 保存个人排名数据
export const saveIndividualRankings = async (data) => {
individualRankings = [...data];
console.log('保存个人排名数据:', data);
// 在真实环境中可以调用API保存数据
return await saveIndividualRankingsToConfig(data);
};
export const saveTeamRankings = (data) => {
// 这里只是模拟实际项目中可以考虑使用localStorage或后端API
// 保存战队排名数据
export const saveTeamRankings = async (data) => {
teamRankings = [...data];
console.log('保存战队排名数据:', data);
// 在真实环境中可以调用API保存数据
return await saveTeamRankingsToConfig(data);
};
// 保存显示配置
export const saveDisplayConfig = (config) => {
// 这里只是模拟实际项目中可以考虑使用localStorage或后端API
export const saveDisplayConfig = async (config) => {
displayConfig = { ...config };
console.log('保存显示配置:', config);
// 在真实环境中可以调用API保存数据
return await saveDisplayConfigToConfig(config);
};
// 保存战鼓配置
export const saveDrumConfig = (config) => {
// 这里只是模拟实际项目中可以考虑使用localStorage或后端API
export const saveDrumConfig = async (config) => {
console.log('保存战鼓配置:', config);
drumConfig = { ...drumConfig, ...config };
// 在真实环境中可以调用API保存数据
return await saveDrumConfigToConfig(drumConfig);
};
// 保存奖金规则
export const saveBonusRules = async (rules) => {
bonusRules = [...rules];
console.log('保存奖金规则:', rules);
return await saveBonusRulesToConfig(rules);
};
// 验证用户登录
export const validateUser = (username, password) => {
return systemUsers.find(user => user.username === username && user.password === password);
};
// 初始化数据(异步版本,用于应用启动时加载数据)
export const initializeData = async () => {
try {
await refreshData();
console.log('数据初始化成功');
} catch (error) {
console.error('数据初始化失败:', error);
}
};
// 刷新数据
export const refreshData = async () => {
try {
individualRankings = await getIndividualRankings();
teamRankings = await getTeamRankings();
bonusRules = await getBonusRules() || [
{ rank: '1-3', description: '前三名', individualBonus: '¥10000, ¥8000, ¥5000', teamBonus: '¥50000, ¥30000, ¥20000' },
{ rank: '4-10', description: '四至十名', individualBonus: '¥3000/人', teamBonus: '¥10000/队' },
{ rank: '11-20', description: '十一至二十名', individualBonus: '¥1000/人', teamBonus: '¥5000/队' }
];
systemUsers = await getSystemUsers();
displayConfig = await getDisplayConfig();
battleEndTime = await getBattleEndTime();
drumConfig = await getDrumConfig();
return true;
} catch (error) {
console.error('刷新数据失败:', error);
return false;
}
};
// 添加系统用户
export const addSystemUser = async (user) => {
try {
const result = await addSystemUserToConfig(user);
if (result) {
// 刷新用户列表
systemUsers = await getSystemUsers();
}
return result;
} catch (error) {
console.error('添加用户失败:', error);
throw error;
}
};
// 删除系统用户
export const deleteSystemUser = async (userId) => {
try {
const result = await deleteSystemUserToConfig(userId);
if (result) {
// 刷新用户列表
systemUsers = await getSystemUsers();
}
return result;
} catch (error) {
console.error('删除用户失败:', error);
throw error;
}
};
// 更新系统用户
export const updateSystemUser = async (userId, updatedData) => {
try {
const result = await updateSystemUserToConfig(userId, updatedData);
if (result) {
// 刷新用户列表
systemUsers = await getSystemUsers();
}
return result;
} catch (error) {
console.error('更新用户失败:', error);
throw error;
}
};

View File

@@ -0,0 +1,325 @@
// 配置文件API路径
const CONFIG_API_URL = '/api/config';
/**
* 读取配置文件
* @returns {Object} 配置数据
*/
export const readConfig = async () => {
try {
const response = await fetch(CONFIG_API_URL);
if (response.ok) {
return await response.json();
} else if (response.status === 404) {
// 配置文件不存在,使用默认配置
console.log('配置文件不存在,使用默认配置');
return getDefaultConfig();
}
throw new Error(`获取配置失败: ${response.status}`);
} catch (error) {
console.error('读取配置失败:', error);
return getDefaultConfig();
}
};
/**
* 写入配置文件
* @param {Object} config 新的配置数据
* @returns {boolean} 是否写入成功
*/
export const writeConfig = async (config) => {
try {
const response = await fetch(CONFIG_API_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(config)
});
if (response.ok) {
const result = await response.json();
return result.success;
}
throw new Error(`保存配置失败: ${response.status}`);
} catch (error) {
console.error('写入配置失败:', error);
return false;
}
};
// 同步版本的readConfig用于向后兼容
export const getConfig = async () => {
return await readConfig();
};
/**
* 获取默认配置(用于兜底)
* @returns {Object} 默认配置
*/
const getDefaultConfig = () => ({
individualRankings: [],
teamRankings: [],
bonusRules: [],
systemUsers: [],
displayConfig: {
individual: {
showLevel: false,
showDepartment: false,
scoreColumn: {
displayName: '分数',
displayStyle: 'number'
},
columnWidths: {}
},
team: {
showMemberCount: false,
showLeader: false,
totalScoreColumn: {
displayName: '总分',
displayStyle: 'number'
},
columnWidths: {}
}
},
battleEndTime: {
date: new Date().toISOString().split('T')[0],
time: '23:59:59'
},
drumConfig: {
sound: {
volume: 1.0,
enabled: false
},
animation: {
enabled: false
},
pattern: {
strongBeats: [1],
totalBeats: 4
}
}
});
/**
* 获取个人排名数据
* @returns {Array} 个人排名数组
*/
export const getIndividualRankings = async () => {
const config = await readConfig();
return config.individualRankings || [];
};
/**
* 保存个人排名数据
* @param {Array} rankings 个人排名数组
* @returns {boolean} 是否保存成功
*/
export const saveIndividualRankings = async (rankings) => {
const config = await readConfig();
config.individualRankings = rankings;
return await writeConfig(config);
};
/**
* 获取战队排名数据
* @returns {Array} 战队排名数组
*/
export const getTeamRankings = async () => {
const config = await readConfig();
return config.teamRankings || [];
};
/**
* 保存战队排名数据
* @param {Array} rankings 战队排名数组
* @returns {boolean} 是否保存成功
*/
export const saveTeamRankings = async (rankings) => {
const config = await readConfig();
config.teamRankings = rankings;
return await writeConfig(config);
};
/**
* 获取奖金规则
* @returns {Array} 奖金规则数组
*/
export const getBonusRules = async () => {
const config = await readConfig();
return config.bonusRules || [];
};
/**
* 保存奖金规则
* @param {Array} rules 奖金规则数组
* @returns {boolean} 是否保存成功
*/
export const saveBonusRules = async (rules) => {
const config = await readConfig();
config.bonusRules = rules;
return await writeConfig(config);
};
/**
* 获取系统用户
* @returns {Array} 系统用户数组
*/
export const getSystemUsers = async () => {
const config = await readConfig();
return config.systemUsers || [];
};
/**
* 保存系统用户
* @param {Array} users 系统用户数组
* @returns {boolean} 是否保存成功
*/
export const saveSystemUsers = async (users) => {
const config = await readConfig();
config.systemUsers = users;
return await writeConfig(config);
};
/**
* 添加用户
* @param {Object} user 用户信息
* @returns {boolean} 是否添加成功
*/
export const addSystemUser = async (user) => {
try {
const users = await getSystemUsers();
// 检查用户名是否已存在
const existingUser = users.find(u => u.username === user.username);
if (existingUser) {
throw new Error('用户名已存在');
}
users.push({
id: Date.now(),
...user
});
return await saveSystemUsers(users);
} catch (error) {
console.error('添加用户失败:', error);
throw error;
}
};
/**
* 删除用户
* @param {number} userId 用户ID
* @returns {boolean} 是否删除成功
*/
export const deleteSystemUser = async (userId) => {
try {
let users = await getSystemUsers();
// 确保至少保留一个管理员用户
if (users.length <= 1) {
throw new Error('系统至少需要保留一个管理员用户');
}
users = users.filter(u => u.id !== userId);
return await saveSystemUsers(users);
} catch (error) {
console.error('删除用户失败:', error);
throw error;
}
};
/**
* 更新用户信息
* @param {number} userId 用户ID
* @param {Object} updatedData 更新的数据
* @returns {boolean} 是否更新成功
*/
export const updateSystemUser = async (userId, updatedData) => {
try {
const users = await getSystemUsers();
const userIndex = users.findIndex(u => u.id === userId);
if (userIndex === -1) {
throw new Error('用户不存在');
}
// 如果要更新用户名,检查是否与其他用户冲突
if (updatedData.username && updatedData.username !== users[userIndex].username) {
const existingUser = users.find(u => u.id !== userId && u.username === updatedData.username);
if (existingUser) {
throw new Error('用户名已存在');
}
}
users[userIndex] = {
...users[userIndex],
...updatedData
};
return await saveSystemUsers(users);
} catch (error) {
console.error('更新用户失败:', error);
throw error;
}
};
/**
* 获取显示配置
* @returns {Object} 显示配置
*/
export const getDisplayConfig = async () => {
const config = await readConfig();
return config.displayConfig || {};
};
/**
* 保存显示配置
* @param {Object} displayConfig 显示配置
* @returns {boolean} 是否保存成功
*/
export const saveDisplayConfig = async (displayConfig) => {
const config = await readConfig();
config.displayConfig = displayConfig;
return await writeConfig(config);
};
/**
* 获取战斗结束时间
* @returns {Object} 结束时间配置
*/
export const getBattleEndTime = async () => {
const config = await readConfig();
return config.battleEndTime || {};
};
/**
* 保存战斗结束时间
* @param {Object} endTime 结束时间配置
* @returns {boolean} 是否保存成功
*/
export const saveBattleEndTime = async (endTime) => {
const config = await readConfig();
config.battleEndTime = endTime;
return await writeConfig(config);
};
/**
* 获取战鼓配置
* @returns {Object} 战鼓配置
*/
export const getDrumConfig = async () => {
const config = await readConfig();
return config.drumConfig || {};
};
/**
* 保存战鼓配置
* @param {Object} drumConfig 战鼓配置
* @returns {boolean} 是否保存成功
*/
export const saveDrumConfig = async (drumConfig) => {
const config = await readConfig();
config.drumConfig = drumConfig;
return await writeConfig(config);
};

View File

@@ -0,0 +1,47 @@
import { getConfig, writeConfig } from './configService.js';
// 测试配置服务功能
const testConfigService = async () => {
try {
console.log('开始测试配置服务...');
// 读取配置
const config = await getConfig();
console.log('成功读取配置:', {
hasIndividualRankings: config.individualRankings?.length > 0,
hasTeamRankings: config.teamRankings?.length > 0,
hasSystemUsers: config.systemUsers?.length > 0,
hasDisplayConfig: !!config.displayConfig,
hasBattleEndTime: !!config.battleEndTime,
hasDrumConfig: !!config.drumConfig
});
// 写入配置(添加一个小的修改然后恢复)
const testKey = 'test_timestamp';
const originalValue = config[testKey];
config[testKey] = Date.now();
await writeConfig(config);
console.log('成功写入配置修改');
// 验证修改已保存
const updatedConfig = await getConfig();
console.log('修改验证成功:', updatedConfig[testKey] === config[testKey]);
// 恢复原始状态
if (originalValue === undefined) {
delete updatedConfig[testKey];
} else {
updatedConfig[testKey] = originalValue;
}
await writeConfig(updatedConfig);
console.log('成功恢复原始配置');
return { success: true };
} catch (error) {
console.error('配置服务测试失败:', error);
return { success: false, error: error.message };
}
};
export default testConfigService;

View File

@@ -35,9 +35,10 @@
<div class="top-nav">
<h1 class="nav-title">📊 百人大战管理系统</h1>
<div class="nav-actions">
<span class="welcome-text">欢迎您{{ currentUser.username }}</span>
<button @click="logout" class="logout-btn">退出登录</button>
</div>
<span class="welcome-text">欢迎您{{ currentUser.username }}</span>
<button @click="handleRefreshData" class="refresh-btn" style="margin-right: 10px;">刷新数据</button>
<button @click="logout" class="logout-btn">退出登录</button>
</div>
</div>
<!-- 功能选项卡 -->
@@ -319,6 +320,44 @@
</div>
</div>
<!-- 奖金设置 -->
<div v-if="currentTab === 'bonus'" class="bonus-config-content">
<h2 class="config-title">🎯 奖金设置</h2>
<div class="config-section">
<h3>🏆 奖金规则配置</h3>
<div class="bonus-rules-list">
<div
v-for="(rule, index) in localBonusRules"
:key="index"
class="bonus-rule-item"
>
<div class="rule-form">
<div class="form-group">
<label>名次范围:</label>
<input v-model="rule.rank" type="text" class="form-input" placeholder="如: 1-3">
</div>
<div class="form-group">
<label>规则描述:</label>
<input v-model="rule.description" type="text" class="form-input" placeholder="如: 前三名">
</div>
<div class="form-group">
<label>个人奖励:</label>
<input v-model="rule.individualBonus" type="text" class="form-input" placeholder="如: ¥10000, ¥8000, ¥5000">
</div>
<div class="form-group">
<label>团队奖励:</label>
<input v-model="rule.teamBonus" type="text" class="form-input" placeholder="如: ¥50000, ¥30000, ¥20000">
</div>
<div class="form-actions">
<button @click="deleteBonusRule(index)" class="delete-btn">🗑 删除</button>
</div>
</div>
</div>
<button @click="addBonusRule" class="add-btn" style="margin-top: 20px;"> 添加奖金规则</button>
</div>
</div>
</div>
<!-- 战鼓配置 -->
<div v-if="currentTab === 'drum'" class="drum-config-content">
<h2 class="config-title">🥁 战鼓配置管理</h2>
@@ -652,20 +691,24 @@
</template>
<script setup>
import { ref, reactive } from 'vue';
import { ref, reactive, onMounted } from 'vue';
import { useRouter } from 'vue-router';
import {
individualRankings,
teamRankings,
systemUsers,
bonusRules,
saveIndividualRankings,
saveTeamRankings,
saveBonusRules,
displayConfig,
saveDisplayConfig,
battleEndTime,
saveBattleEndTime,
drumConfig,
saveDrumConfig
saveDrumConfig,
refreshData,
initializeData
} from '../data/mockData.js';
const router = useRouter();
@@ -688,21 +731,80 @@ const currentUser = ref({});
const tabs = [
{ key: 'individual', label: '个人排名' },
{ key: 'team', label: '战队排名' },
{ key: 'bonus', label: '奖金设置' },
{ key: 'config', label: '显示配置' },
{ key: 'endTime', label: '结束时间设置' },
{ key: 'drum', label: '战鼓配置' }
];
// 刷新数据
const handleRefreshData = () => {
try {
const success = refreshData();
// 重新加载本地数据副本
localIndividualRankings.value = [...individualRankings];
localTeamRankings.value = [...teamRankings];
localBonusRules.value = [...bonusRules];
localDisplayConfig.value = {...displayConfig};
localBattleEndTime.value = {...battleEndTime};
localDrumConfig.value = {...drumConfig};
// 重新处理强拍位置
if (localDrumConfig.value.pattern && localDrumConfig.value.pattern.strongBeats) {
localDrumConfig.value.pattern.strongBeatsStr =
localDrumConfig.value.pattern.strongBeats.join(',') || '1,4';
}
if (success) {
alert('数据刷新成功!');
} else {
alert('数据刷新失败,请重试。');
}
} catch (error) {
console.error('数据刷新失败:', error);
alert('数据刷新失败,请重试。');
}
};
const currentTab = ref('individual');
// 本地数据副本
const localIndividualRankings = ref([...individualRankings]);
const localTeamRankings = ref([...teamRankings]);
const localBonusRules = ref([...bonusRules]);
const localDisplayConfig = ref({...displayConfig});
const localBattleEndTime = ref({...battleEndTime});
// 初始化本地战鼓配置副本
const localDrumConfig = ref({...drumConfig});
// 添加强拍位置的字符串表示,用于输入框
localDrumConfig.value.pattern.strongBeatsStr = localDrumConfig.value.pattern.strongBeats?.join(',') || '1,4';
if (localDrumConfig.value.pattern && localDrumConfig.value.pattern.strongBeats) {
localDrumConfig.value.pattern.strongBeatsStr = localDrumConfig.value.pattern.strongBeats.join(',') || '1,4';
} else {
localDrumConfig.value.pattern = localDrumConfig.value.pattern || {};
localDrumConfig.value.pattern.strongBeats = [1, 4];
localDrumConfig.value.pattern.strongBeatsStr = '1,4';
}
// 组件挂载时初始化数据
onMounted(async () => {
try {
await initializeData();
// 重新加载本地数据副本
localIndividualRankings.value = [...individualRankings];
localTeamRankings.value = [...teamRankings];
localBonusRules.value = [...bonusRules];
localDisplayConfig.value = {...displayConfig};
localBattleEndTime.value = {...battleEndTime};
localDrumConfig.value = {...drumConfig};
// 重新处理强拍位置
if (localDrumConfig.value.pattern && localDrumConfig.value.pattern.strongBeats) {
localDrumConfig.value.pattern.strongBeatsStr =
localDrumConfig.value.pattern.strongBeats.join(',') || '1,4';
}
} catch (error) {
console.error('初始化数据失败:', error);
}
});
// 更新强拍位置数组
const updateStrongBeats = () => {
@@ -783,24 +885,45 @@ const goBack = () => {
};
// 保存数据
const saveData = () => {
// 按分数重新排序
localIndividualRankings.value.sort((a, b) => b.score - a.score);
localTeamRankings.value.sort((a, b) => b.totalScore - a.totalScore);
const saveData = async () => {
try {
// 按分数重新排序
localIndividualRankings.value.sort((a, b) => b.score - a.score);
localTeamRankings.value.sort((a, b) => b.totalScore - a.totalScore);
// 调用保存方法
saveIndividualRankings(localIndividualRankings.value);
saveTeamRankings(localTeamRankings.value);
saveDisplayConfig(localDisplayConfig.value);
saveBattleEndTime(localBattleEndTime.value);
// 保存战鼓配置前,确保强拍位置数组是最新的
updateStrongBeats();
// 移除临时的字符串表示,避免保存到配置中
const configToSave = {...localDrumConfig.value};
delete configToSave.pattern.strongBeatsStr;
saveDrumConfig(configToSave);
// 保存战鼓配置前,确保强拍位置数组是最新的
updateStrongBeats();
// 移除临时的字符串表示,避免保存到配置中
const configToSave = {...localDrumConfig.value};
delete configToSave.pattern.strongBeatsStr;
alert('数据保存成功!');
// 导入必要的配置服务函数
const { readConfig, writeConfig } = await import('../services/configService');
// 一次性读取、修改并保存所有配置,避免竞态条件
const currentConfig = await readConfig();
// 更新所有配置项
currentConfig.individualRankings = localIndividualRankings.value;
currentConfig.teamRankings = localTeamRankings.value;
currentConfig.bonusRules = localBonusRules.value;
currentConfig.displayConfig = localDisplayConfig.value;
currentConfig.battleEndTime = localBattleEndTime.value;
currentConfig.drumConfig = configToSave;
// 一次性保存所有配置
const result = await writeConfig(currentConfig);
// 检查保存是否成功
if (result) {
alert('数据保存成功!');
} else {
alert('数据保存失败,请检查网络连接后重试。');
}
} catch (error) {
console.error('保存数据时发生错误:', error);
alert('保存数据失败,请重试。');
}
};
// 添加/编辑个人
@@ -904,6 +1027,23 @@ const deleteTeam = (id) => {
);
}
};
// 添加奖金规则
const addBonusRule = () => {
localBonusRules.value.push({
rank: '',
description: '',
individualBonus: '',
teamBonus: ''
});
};
// 删除奖金规则
const deleteBonusRule = (index) => {
if (confirm('确定要删除这条奖金规则吗?')) {
localBonusRules.value.splice(index, 1);
}
};
</script>
<style scoped>

View File

@@ -42,7 +42,7 @@
<h2>🎯 奖金设置</h2>
<div class="bonus-rules-row">
<div
v-for="(rule, index) in bonusRules"
v-for="(rule, index) in displayBonusRules"
:key="index"
class="bonus-rule-item"
>
@@ -69,9 +69,9 @@
<span class="rank-col">排名</span>
<span class="avatar-col">头像</span>
<span class="name-col">姓名</span>
<span class="score-col">{{ displayConfig.individual.scoreColumn.displayName }}</span>
<span v-if="displayConfig.individual.showLevel" class="level-col">等级</span>
<span v-if="displayConfig.individual.showDepartment" class="dept-col">部门</span>
<span class="score-col">{{ localDisplayConfig.individual?.scoreColumn?.displayName || '得分' }}</span>
<span v-if="localDisplayConfig.individual?.showLevel" class="level-col">等级</span>
<span v-if="localDisplayConfig.individual?.showDepartment" class="dept-col">部门</span>
<span class="bonus-col">奖金</span>
</div>
<div
@@ -87,9 +87,9 @@
<span class="rank-col">{{ index + 1 }}</span>
<span class="avatar-col">{{ item.avatar }}</span>
<span class="name-col">{{ item.name }}</span>
<span class="score-col">{{ displayConfig.individual.scoreColumn.displayStyle === 'amount' ? '¥' + item.score : item.score }}</span>
<span v-if="displayConfig.individual.showLevel" class="level-col" :class="`level-${item.level}`">{{ item.level }}</span>
<span v-if="displayConfig.individual.showDepartment" class="dept-col">{{ item.department }}</span>
<span class="score-col">{{ localDisplayConfig.individual?.scoreColumn?.displayStyle === 'amount' ? '¥' + item.score : item.score }}</span>
<span v-if="localDisplayConfig.individual?.showLevel" class="level-col" :class="`level-${item.level}`">{{ item.level }}</span>
<span v-if="localDisplayConfig.individual?.showDepartment" class="dept-col">{{ item.department }}</span>
<span class="bonus-col">¥{{ item.bonus }}</span>
</div>
</div>
@@ -102,9 +102,9 @@
<div class="table-header" :style="{ 'grid-template-columns': teamGridTemplate }">
<span class="rank-col">排名</span>
<span class="name-col">战队名称</span>
<span class="score-col">{{ displayConfig.team.totalScoreColumn.displayName }}</span>
<span v-if="displayConfig.team.showMemberCount" class="member-col">人数</span>
<span v-if="displayConfig.team.showLeader" class="leader-col">队长</span>
<span class="score-col">{{ localDisplayConfig.team?.totalScoreColumn?.displayName || '总分' }}</span>
<span v-if="localDisplayConfig.team?.showMemberCount" class="member-col">人数</span>
<span v-if="localDisplayConfig.team?.showLeader" class="leader-col">队长</span>
<span class="bonus-col">奖金</span>
</div>
<div
@@ -119,9 +119,9 @@
>
<span class="rank-col">{{ index + 1 }}</span>
<span class="name-col">{{ item.name }}</span>
<span class="score-col">{{ displayConfig.team.totalScoreColumn.displayStyle === 'amount' ? '¥' + item.totalScore : item.totalScore }}</span>
<span v-if="displayConfig.team.showMemberCount" class="member-col">{{ item.memberCount }}</span>
<span v-if="displayConfig.team.showLeader" class="leader-col">{{ item.leader }}</span>
<span class="score-col">{{ localDisplayConfig.team?.totalScoreColumn?.displayStyle === 'amount' ? '¥' + item.totalScore : item.totalScore }}</span>
<span v-if="localDisplayConfig.team?.showMemberCount" class="member-col">{{ item.memberCount }}</span>
<span v-if="localDisplayConfig.team?.showLeader" class="leader-col">{{ item.leader }}</span>
<span class="bonus-col">¥{{ item.bonus }}</span>
</div>
</div>
@@ -147,11 +147,49 @@ import {
bonusRules,
displayConfig,
battleEndTime,
drumConfig
drumConfig,
initializeData
} from '../data/mockData.js';
// 创建本地显示配置的副本,确保深拷贝
const localDisplayConfig = ref(JSON.parse(JSON.stringify(displayConfig)));
// 创建本地显示配置的副本,确保深拷贝并提供默认值
const createDefaultDisplayConfig = () => ({
individual: {
scoreColumn: {
displayName: '得分',
displayStyle: 'number'
},
showLevel: true,
showDepartment: true,
columnWidths: {}
},
team: {
totalScoreColumn: {
displayName: '总分',
displayStyle: 'number'
},
showMemberCount: true,
showLeader: true,
columnWidths: {}
}
});
// 创建默认奖金规则
const createDefaultBonusRules = () => [
{ rank: '1-3', description: '前三名', individualBonus: '¥10000, ¥8000, ¥5000', teamBonus: '¥50000, ¥30000, ¥20000' },
{ rank: '4-10', description: '四至十名', individualBonus: '¥3000/人', teamBonus: '¥10000/队' },
{ rank: '11-20', description: '十一至二十名', individualBonus: '¥1000/人', teamBonus: '¥5000/队' }
];
const localDisplayConfig = ref(
displayConfig ? JSON.parse(JSON.stringify(displayConfig)) : createDefaultDisplayConfig()
);
// 确保奖金规则有默认值
const displayBonusRules = computed(() => {
return Array.isArray(bonusRules) && bonusRules.length > 0
? bonusRules
: createDefaultBonusRules();
});
const router = useRouter();
@@ -438,16 +476,6 @@ const goToAdmin = () => {
router.push('/admin');
};
onMounted(() => {
calculateCountdown();
countdownInterval = setInterval(calculateCountdown, 1000);
// 延迟初始化音频上下文,避免浏览器自动播放限制
setTimeout(() => {
// 不自动初始化音频,等待用户交互后再初始化
}, 1000);
startDrumAnimation();
});
// 监听窗口点击事件,用于用户交互后初始化音频上下文
document.addEventListener('click', initAudioContext, { once: true });
document.addEventListener('touchstart', initAudioContext, { once: true });
@@ -465,7 +493,18 @@ const handleResize = () => {
}
};
onMounted(() => {
onMounted(async () => {
try {
// 异步初始化数据
await initializeData();
// 更新本地显示配置
localDisplayConfig.value = displayConfig ? JSON.parse(JSON.stringify(displayConfig)) : createDefaultDisplayConfig();
} catch (error) {
console.error('初始化数据失败:', error);
// 使用默认配置
localDisplayConfig.value = createDefaultDisplayConfig();
}
calculateCountdown();
countdownInterval = setInterval(calculateCountdown, 10); // 改为10ms更新一次以显示毫秒
startDrumAnimation();