From 717d26385053282a7d7ec76a5879a159e74f3eff Mon Sep 17 00:00:00 2001 From: Zhukj <3262118957> Date: Fri, 12 Dec 2025 14:24:45 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=88=A0=E9=99=A4=E6=88=98=E9=BC=93?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E4=BB=A3=E7=A0=81=20+=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=8A=A0=E8=BD=BD=E5=BC=82=E5=B8=B8=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/config.json | 31 --- package-lock.json | 281 +++++++++++++++++++++++++- package.json | 1 + src/data/mockData.js | 31 +-- src/services/configService.js | 37 +--- src/views/AdminPanel.vue | 227 +-------------------- src/views/BattleRanking.vue | 368 +--------------------------------- 7 files changed, 287 insertions(+), 689 deletions(-) diff --git a/data/config.json b/data/config.json index 9da7f4f..3064f90 100644 --- a/data/config.json +++ b/data/config.json @@ -950,36 +950,5 @@ "battleEndTime": { "date": "2026-02-08", "time": "00:00:00" - }, - "drumConfig": { - "sound": { - "volume": 1, - "frequency1": 150, - "frequency2": 100, - "attackTime": 0.01, - "decayTime": 0.3, - "type1": "sine", - "type2": "triangle", - "enabled": false - }, - "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 - } } } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 4dcff80..732131c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ }, "devDependencies": { "@vitejs/plugin-vue": "^6.0.1", + "nodemon": "^3.1.11", "sass": "^1.94.0", "vite": "^7.2.2" } @@ -1282,12 +1283,59 @@ "node": ">= 0.6" } }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/append-field": { "version": "1.0.0", "resolved": "https://registry.npmmirror.com/append-field/-/append-field-1.0.0.tgz", "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", "license": "MIT" }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/body-parser": { "version": "2.2.0", "resolved": "https://registry.npmmirror.com/body-parser/-/body-parser-2.2.0.tgz", @@ -1308,13 +1356,23 @@ "node": ">=18" } }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/braces": { "version": "3.0.3", "resolved": "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "license": "MIT", - "optional": true, "dependencies": { "fill-range": "^7.1.1" }, @@ -1393,6 +1451,13 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, "node_modules/concat-stream": { "version": "1.6.2", "resolved": "https://registry.npmmirror.com/concat-stream/-/concat-stream-1.6.2.tgz", @@ -1712,7 +1777,6 @@ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "license": "MIT", - "optional": true, "dependencies": { "to-regex-range": "^5.0.1" }, @@ -1816,6 +1880,19 @@ "node": ">= 0.4" } }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz", @@ -1828,6 +1905,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/has-symbols": { "version": "1.1.0", "resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz", @@ -1889,6 +1976,13 @@ "node": ">=0.10.0" } }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true, + "license": "ISC" + }, "node_modules/immutable": { "version": "5.1.4", "resolved": "https://registry.npmmirror.com/immutable/-/immutable-5.1.4.tgz", @@ -1911,13 +2005,25 @@ "node": ">= 0.10" } }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, "license": "MIT", - "optional": true, "engines": { "node": ">=0.10.0" } @@ -1928,7 +2034,6 @@ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "license": "MIT", - "optional": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -1942,7 +2047,6 @@ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, "license": "MIT", - "optional": true, "engines": { "node": ">=0.12.0" } @@ -2048,6 +2152,19 @@ "node": ">= 0.6" } }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/minimist": { "version": "1.2.8", "resolved": "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz", @@ -2172,6 +2289,96 @@ "license": "MIT", "optional": true }, + "node_modules/nodemon": { + "version": "3.1.11", + "resolved": "https://registry.npmmirror.com/nodemon/-/nodemon-3.1.11.tgz", + "integrity": "sha512-is96t8F/1//UHAjNPHpbsNY46ELPpftGUoSVNXwUfMk/qdjSylYrWSu1XavVTBOn526kFiOR733ATgNBCQyH0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/nodemon/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/nodemon/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz", @@ -2299,6 +2506,13 @@ "node": ">= 0.10" } }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmmirror.com/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true, + "license": "MIT" + }, "node_modules/qs": { "version": "6.14.0", "resolved": "https://registry.npmmirror.com/qs/-/qs-6.14.0.tgz", @@ -2494,6 +2708,19 @@ "@parcel/watcher": "^2.4.1" } }, + "node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/send": { "version": "1.2.0", "resolved": "https://registry.npmmirror.com/send/-/send-1.2.0.tgz", @@ -2609,6 +2836,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz", @@ -2650,6 +2890,19 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "license": "MIT" }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.15.tgz", @@ -2673,7 +2926,6 @@ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "license": "MIT", - "optional": true, "dependencies": { "is-number": "^7.0.0" }, @@ -2690,6 +2942,16 @@ "node": ">=0.6" } }, + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true, + "license": "ISC", + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, "node_modules/type-is": { "version": "2.0.1", "resolved": "https://registry.npmmirror.com/type-is/-/type-is-2.0.1.tgz", @@ -2710,6 +2972,13 @@ "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", "license": "MIT" }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true, + "license": "MIT" + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmmirror.com/unpipe/-/unpipe-1.0.0.tgz", diff --git a/package.json b/package.json index 1551644..38b9b3b 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ }, "devDependencies": { "@vitejs/plugin-vue": "^6.0.1", + "nodemon": "^3.1.11", "sass": "^1.94.0", "vite": "^7.2.2" } diff --git a/src/data/mockData.js b/src/data/mockData.js index a9416f7..8caaf80 100644 --- a/src/data/mockData.js +++ b/src/data/mockData.js @@ -12,8 +12,6 @@ import { saveDisplayConfig as saveDisplayConfigToConfig, getBattleEndTime, saveBattleEndTime as saveBattleEndTimeToConfig, - getDrumConfig, - saveDrumConfig as saveDrumConfigToConfig, getBonusRules, saveBonusRules as saveBonusRulesToConfig } from '../services/configService'; @@ -29,7 +27,7 @@ export let bonusRules = [ export let systemUsers = []; export let displayConfig = null; export let battleEndTime = { date: new Date().toISOString().split('T')[0], time: '00:00:00' }; -export let drumConfig = {}; + // 保存结束时间 export const saveBattleEndTime = async (endTime) => { @@ -59,30 +57,7 @@ export const saveDisplayConfig = async (config) => { return await saveDisplayConfigToConfig(config); }; -// 保存战鼓配置 -export const saveDrumConfig = async (config) => { - console.log('保存战鼓配置:', config); - - // 深度合并配置,确保嵌套对象(如sound、animation、pattern)的属性不会丢失 - drumConfig = { - ...drumConfig, - ...config, - sound: { - ...drumConfig.sound, - ...config.sound - }, - animation: { - ...drumConfig.animation, - ...config.animation - }, - pattern: { - ...drumConfig.pattern, - ...config.pattern - } - }; - - return await saveDrumConfigToConfig(drumConfig); -}; + // 保存奖金规则 export const saveBonusRules = async (rules) => { @@ -118,7 +93,7 @@ export const refreshData = async () => { systemUsers = await getSystemUsers(); displayConfig = await getDisplayConfig(); battleEndTime = await getBattleEndTime(); - drumConfig = await getDrumConfig(); + return true; } catch (error) { console.error('刷新数据失败:', error); diff --git a/src/services/configService.js b/src/services/configService.js index f114b56..65dee1a 100644 --- a/src/services/configService.js +++ b/src/services/configService.js @@ -1,5 +1,6 @@ // 配置文件API路径 -const CONFIG_API_URL = '/api/config'; +// 修复后(绝对路径,直接请求后端3000端口) +const CONFIG_API_URL = 'http://localhost:3000/api/config'; /** * 读取配置文件 @@ -106,21 +107,7 @@ const getDefaultConfig = () => ({ date: new Date().toISOString().split('T')[0], time: '00:00:00' }, - drumConfig: { - showDrum: false, // 控制战鼓的显示,默认不显示 - sound: { - volume: 1.0, - enabled: false, // 控制声音播放,默认不播放 - soundSrc: '' // 战鼓声音来源文件路径 - }, - animation: { - enabled: false - }, - pattern: { - strongBeats: [1], - totalBeats: 4 - } - }, + backgroundConfig: { useBackgroundImage: true, backgroundImage: '/battle-background.jpg', // 默认战旗背景图片 @@ -332,25 +319,7 @@ export const saveBattleEndTime = async (endTime) => { return await writeConfig(config); }; -/** - * 获取战鼓配置 - * @returns {Object} 战鼓配置 - */ -export const getDrumConfig = async () => { - const config = await readConfig(); - return config.drumConfig || getDefaultConfig().drumConfig; -}; -/** - * 保存战鼓配置 - * @param {Object} drumConfig 战鼓配置 - * @returns {boolean} 是否保存成功 - */ -export const saveDrumConfig = async (drumConfig) => { - const config = await readConfig(); - config.drumConfig = drumConfig; - return await writeConfig(config); -}; /** * 获取背景配置 diff --git a/src/views/AdminPanel.vue b/src/views/AdminPanel.vue index 8db4ade..205172b 100644 --- a/src/views/AdminPanel.vue +++ b/src/views/AdminPanel.vue @@ -655,172 +655,7 @@ - -
-

🥁 战鼓配置管理

- -
-

🔊 音效配置

-
-
- -
-
- -
-
- -
-
- -
-

🎵 第一个音调

-
- -
-
- -
-

🎵 第二个音调

-
- -
-
- -
-
-
- - -
-

🎬 动画配置

-
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
-
- - -
-

🎵 节拍模式配置

-
-
- -
-
- -
-
- -
-
- -
-
- -
-
-
-
@@ -954,8 +789,6 @@ import { saveDisplayConfig, battleEndTime, saveBattleEndTime, - drumConfig, - saveDrumConfig, refreshData, initializeData } from '../data/mockData.js'; @@ -983,8 +816,7 @@ const tabs = [ { key: 'bonus', label: '奖金设置' }, { key: 'config', label: '显示配置' }, { key: 'champion', label: '冠军Logo配置' }, - { key: 'endTime', label: '结束时间设置' }, - { key: 'drum', label: '战鼓配置' } + { key: 'endTime', label: '结束时间设置' } ]; // 冠军Logo配置 @@ -1011,18 +843,11 @@ onMounted(async () => { localDisplayConfig.value.crownPosition.top = '-100px'; } localBattleEndTime.value = { ...battleEndTime }; - localDrumConfig.value = { ...drumConfig }; // 初始化冠军Logo配置 if (displayConfig.championLogos) { championLogos.value = { ...displayConfig.championLogos }; } - - // 重新处理强拍位置 - if (localDrumConfig.value.pattern && localDrumConfig.value.pattern.strongBeats) { - localDrumConfig.value.pattern.strongBeatsStr = - localDrumConfig.value.pattern.strongBeats.join(',') || '1,4'; - } } catch (error) { console.error('初始化数据失败:', error); } @@ -1138,13 +963,6 @@ const handleRefreshData = () => { 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('数据刷新成功!'); @@ -1164,16 +982,6 @@ const localTeamRankings = ref([...teamRankings]); const localBonusRules = ref([...bonusRules]); const localDisplayConfig = ref({ ...displayConfig }); const localBattleEndTime = ref({ ...battleEndTime }); -// 初始化本地战鼓配置副本 -const localDrumConfig = ref({ ...drumConfig }); -// 添加强拍位置的字符串表示,用于输入框 -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 () => { @@ -1185,36 +993,12 @@ onMounted(async () => { 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 = () => { - try { - const beatsStr = localDrumConfig.value.pattern.strongBeatsStr; - if (!beatsStr) { - localDrumConfig.value.pattern.strongBeats = []; - return; - } - const beats = beatsStr.split(',') - .map(beat => parseInt(beat.trim())) - .filter(beat => !isNaN(beat) && beat > 0 && beat <= 8); - localDrumConfig.value.pattern.strongBeats = beats; - } catch (error) { - console.error('更新强拍位置失败:', error); - localDrumConfig.value.pattern.strongBeats = [1, 4]; - localDrumConfig.value.pattern.strongBeatsStr = '1,4'; - } -}; + // 对话框状态 const showAddIndividual = ref(false); @@ -1326,12 +1110,6 @@ const saveData = async () => { localIndividualRankings.value.sort((a, b) => b.score - a.score); localTeamRankings.value.sort((a, b) => b.totalScore - a.totalScore); - // 保存战鼓配置前,确保强拍位置数组是最新的 - updateStrongBeats(); - // 移除临时的字符串表示,避免保存到配置中 - const configToSave = { ...localDrumConfig.value }; - delete configToSave.pattern.strongBeatsStr; - // 导入必要的配置服务函数 const { readConfig, writeConfig } = await import('../services/configService'); @@ -1346,7 +1124,6 @@ const saveData = async () => { // 保存冠军Logo配置 currentConfig.displayConfig.championLogos = championLogos.value; currentConfig.battleEndTime = localBattleEndTime.value; - currentConfig.drumConfig = configToSave; // 一次性保存所有配置 const result = await writeConfig(currentConfig); diff --git a/src/views/BattleRanking.vue b/src/views/BattleRanking.vue index df79522..852120b 100644 --- a/src/views/BattleRanking.vue +++ b/src/views/BattleRanking.vue @@ -7,20 +7,7 @@ - -
-
- -
-
🥁
-
🥁
-
🏆
-
🥁
-
🥁
-
-
-
+
@@ -224,7 +211,6 @@ import { bonusRules, displayConfig, battleEndTime, - drumConfig, initializeData } from '../data/mockData.js'; import { readConfig } from '../services/configService.js'; @@ -692,17 +678,7 @@ const hours = ref(0); const minutes = ref(0); const seconds = ref(0); -// 战鼓动画状态 -const isBeating = ref(false); -let beatInterval = null; let countdownInterval = null; - -// 音频上下文和战鼓音效 -let audioContext = null; -const isPlayingSound = ref(false); - -// 战鼓位置状态 -const drumsPosition = ref({ x: 20, y: 20 }); // 倒计时位置状态已移除,直接在模板中使用固定位置 // 奖金设置模块位置状态 - 使用reactive存储实际定位值 const bonusPosition = reactive({ x: 'auto', y: 'auto' }); @@ -726,13 +702,7 @@ function throttle(func, limit) { }; } -// 开始拖动战鼓 -const startDrag = (e) => { - isDragging = true; - dragOffset.x = e.clientX - drumsPosition.value.x; - dragOffset.y = e.clientY - drumsPosition.value.y; - e.preventDefault(); -}; + // 开始拖动奖金模块(鼠标事件) const startBonusDrag = (e) => { @@ -792,10 +762,6 @@ const endTouch = (e) => { // 优化的拖动函数 - 使用节流减少更新频率(鼠标事件) const drag = throttle((e) => { - if (isDragging) { - drumsPosition.value.x = e.clientX - dragOffset.x; - drumsPosition.value.y = e.clientY - dragOffset.y; - } if (isBonusDragging) { // 计算新的位置 const newX = e.clientX - bonusDragOffset.x; @@ -881,237 +847,11 @@ const calculateCountdown = () => { } }; -// 初始化音频上下文 -const initAudioContext = () => { - if (!audioContext) { - audioContext = new (window.AudioContext || window.webkitAudioContext)(); - } -}; - -// 音频缓存,用于存储加载的MP3文件 -const audioBufferCache = ref({}); - -// 加载MP3文件到音频缓冲区 -const loadAudioFile = async (filePath) => { - try { - // 检查是否已缓存 - if (audioBufferCache.value[filePath]) { - return audioBufferCache.value[filePath]; - } - - // 加载音频文件 - const response = await fetch(filePath); - const arrayBuffer = await response.arrayBuffer(); - const audioBuffer = await audioContext.decodeAudioData(arrayBuffer); - - // 缓存音频缓冲区 - audioBufferCache.value[filePath] = audioBuffer; - return audioBuffer; - } catch (error) { - console.error('加载音频文件失败:', error); - return null; - } -}; - -// 播放战鼓音效 -const playDrumSound = async (isStrongBeat = false) => { - // 检查是否启用声音播放 - if (!audioContext || isPlayingSound.value || drumConfig?.sound?.enabled === false) return; - - isPlayingSound.value = true; - - try { - // 使用配置的音效参数 - const soundConfig = drumConfig?.sound || {}; - const patternConfig = drumConfig?.pattern || {}; - - // 检查是否配置了MP3文件路径 - if (soundConfig.soundSrc && soundConfig.soundSrc.trim() !== '') { - // 使用MP3文件播放 - const audioBuffer = await loadAudioFile(soundConfig.soundSrc); - - if (audioBuffer) { - // 创建音频源节点 - const source = audioContext.createBufferSource(); - const gainNode = audioContext.createGain(); - - // 连接节点 - source.buffer = audioBuffer; - source.connect(gainNode); - gainNode.connect(audioContext.destination); - - // 设置音量,支持强拍音量增强 - const baseVolume = soundConfig.volume || 1.0; - const accentMultiplier = patternConfig.accentMultiplier || 1.2; - const volume = isStrongBeat ? baseVolume * accentMultiplier : baseVolume; - gainNode.gain.value = volume; - - // 播放声音 - source.start(0); - - // 设置完成后重置播放状态 - setTimeout(() => { - isPlayingSound.value = false; - }, audioBuffer.duration * 1000 + 100); - - return; - } - } - - // 如果没有配置MP3文件或加载失败,回退到合成音效 - // 创建振荡器节点 - const oscillator = audioContext.createOscillator(); - const gainNode = audioContext.createGain(); - - // 连接节点 - oscillator.connect(gainNode); - gainNode.connect(audioContext.destination); - - // 设置战鼓音效参数,支持强拍 - const baseVolume = soundConfig.volume || 1.0; - // 使用pattern配置中的强拍音量倍数 - const accentMultiplier = patternConfig.accentMultiplier || 1.2; - const volume = isStrongBeat ? baseVolume * accentMultiplier : baseVolume; - - // 使用soundConfig中的type1 - oscillator.type = soundConfig.type1 || 'sine'; - - // 基础频率,支持强拍频率偏移 - const baseFrequency = soundConfig.frequency1 || 150; - const frequencyOffset = isStrongBeat ? (patternConfig.accentFrequencyOffset || 0) / 100 : 0; - const actualFrequency = baseFrequency * (1 + frequencyOffset); - - oscillator.frequency.setValueAtTime(actualFrequency, audioContext.currentTime); - // 频率渐变 - oscillator.frequency.exponentialRampToValueAtTime(actualFrequency * 0.5, audioContext.currentTime + 0.1); - - // 设置音量包络 - gainNode.gain.setValueAtTime(0, audioContext.currentTime); - gainNode.gain.linearRampToValueAtTime(volume, audioContext.currentTime + (soundConfig.attackTime || 0.01)); - gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + (soundConfig.decayTime || 0.3)); - - // 播放声音 - oscillator.start(); - oscillator.stop(audioContext.currentTime + (soundConfig.decayTime || 0.3)); - - // 双音调效果 - 始终使用 - const oscillator2 = audioContext.createOscillator(); - const gainNode2 = audioContext.createGain(); - - oscillator2.connect(gainNode2); - gainNode2.connect(audioContext.destination); - - oscillator2.type = soundConfig.type2 || 'triangle'; - oscillator2.frequency.setValueAtTime(soundConfig.frequency2 || 100, audioContext.currentTime); - - gainNode2.gain.setValueAtTime(0, audioContext.currentTime); - gainNode2.gain.linearRampToValueAtTime(volume * 0.8, audioContext.currentTime + (soundConfig.attackTime || 0.01)); - gainNode2.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + (soundConfig.decayTime || 0.3) + 0.2); - - oscillator2.start(); - oscillator2.stop(audioContext.currentTime + (soundConfig.decayTime || 0.3) + 0.2); - - // 设置完成后重置播放状态 - setTimeout(() => { - isPlayingSound.value = false; - }, (soundConfig.decayTime || 0.3) * 1000 + 150); - } catch (error) { - console.error('播放战鼓音效出错:', error); - isPlayingSound.value = false; - } -}; - -// 修改相关函数以支持异步 -const handleDrumClick = async () => { - // 如果音频上下文未初始化,初始化它 - if (!audioContext) { - initAudioContext(); - } - - // 如果音频上下文被暂停,恢复它 - if (audioContext.state === 'suspended') { - audioContext.resume(); - } - - // 触发战鼓动画和音效,使用配置的点击效果 - const animationConfig = drumConfig?.animation || {}; - isBeating.value = true; - await playDrumSound(true); // 点击总是强拍 - - setTimeout(() => { - isBeating.value = false; - }, animationConfig.clickBeatDuration || 250); -}; - -// 战鼓动画效果 -const startDrumAnimation = () => { - // 检查是否显示战鼓 - if (drumConfig?.showDrum === false) return; - - // 使用配置的动画和节拍参数 - const animationConfig = drumConfig?.animation || {}; - const patternConfig = drumConfig?.pattern || {}; - - // 检查是否启用动画 - if (animationConfig.enabled === false) return; - - let beatCount = 0; - // 使用配置的节拍间隔 - const interval = animationConfig.beatInterval || 200; - - beatInterval = setInterval(() => { - beatCount++; - // 使用配置的节拍模式和总拍数 - const totalBeats = patternConfig.totalBeats || 4; - const currentBeat = ((beatCount - 1) % totalBeats) + 1; - - // 根据节拍模式确定是否是强拍 - const strongBeats = patternConfig.strongBeats || [1, 4]; - const isStrongBeat = strongBeats.includes(currentBeat); - - // 执行动画和音效 - isBeating.value = true; - - // 根据是否是强拍播放音效 - playDrumSound(isStrongBeat); - - // 设置CSS变量,支持强拍动画增强 - const drums = document.querySelectorAll('.drum'); - drums.forEach(drum => { - // 使用配置的动画参数 - drum.style.setProperty('--drum-scale', isStrongBeat ? - (animationConfig.beatScale || 1.3) * (1 + (patternConfig.accentAnimation || 0) / 100) : - (animationConfig.beatScale || 1.3)); - drum.style.setProperty('--drum-translate-y', isStrongBeat ? - `${(animationConfig.beatTranslateY || -15) * (1 + (patternConfig.accentAnimation || 0) / 100)}px` : - `${animationConfig.beatTranslateY || -15}px`); - drum.style.setProperty('--drum-rotate', `${animationConfig.beatRotate || 5}deg`); - drum.style.setProperty('--drum-brightness', isStrongBeat ? '1.4' : '1.3'); - drum.style.setProperty('--drum-saturation', isStrongBeat ? '1.3' : '1.2'); - }); - - // 根据节拍类型设置持续时间 - const beatDuration = isStrongBeat - ? (animationConfig.beatDuration || 150) - : (animationConfig.beatDuration || 100); - - setTimeout(() => { - isBeating.value = false; - }, beatDuration); - }, interval); -}; - -// 已移至文件中异步版本的handleDrumClick函数 - // 跳转到管理员页面 const goToAdmin = () => { router.push('/admin'); }; -// 监听窗口点击事件,用于用户交互后初始化音频上下文 -document.addEventListener('click', initAudioContext, { once: true }); -document.addEventListener('touchstart', initAudioContext, { once: true }); - const handleResize = () => { // 计算并设置排名明细区域的最小高度,使其底部与视口对齐 const rankingsSection = document.querySelector('.rankings-section'); @@ -1156,7 +896,6 @@ onMounted(async () => { calculateCountdown(); countdownInterval = setInterval(calculateCountdown, 10); // 改为10ms更新一次以显示毫秒 - startDrumAnimation(); // 监听窗口大小变化,确保排名明细与底部对齐 window.addEventListener('resize', handleResize); handleResize(); // 初始调整 @@ -1187,7 +926,6 @@ const handleDisplayConfigChange = () => { onUnmounted(() => { if (countdownInterval) clearInterval(countdownInterval); - if (beatInterval) clearInterval(beatInterval); window.removeEventListener('resize', handleResize); // 移除拖放相关的事件监听 @@ -1198,12 +936,6 @@ onUnmounted(() => { document.removeEventListener('touchmove', touchMove); document.removeEventListener('touchend', endTouch); document.removeEventListener('touchcancel', endTouch); - - // 清理音频资源 - if (audioContext) { - audioContext.close(); - audioContext = null; - } }); @@ -1725,97 +1457,7 @@ onUnmounted(() => { } } -/* 战鼓部分 - 浮动并支持拖放 */ -.drums-section { - position: fixed; - left: 20px; - top: 20px; - padding: 20px; - background: rgba(255, 255, 255, 0.95); - border-radius: 20px; - cursor: move; - z-index: 1000; - box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15); - transition: box-shadow 0.3s ease; -} -.drums-section:hover { - box-shadow: 0 6px 30px rgba(0, 0, 0, 0.25); -} - -.drums-section:active { - box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3); -} - -.drums-container { - display: flex; - justify-content: center; -} - -.drums-animation { - display: flex; - align-items: center; - gap: 20px; - font-size: 3rem; -} - -.drum { - transition: transform 0.1s ease, filter 0.1s ease; - animation: idlePulse 2s infinite alternate; -} - -.drum.beating { - /* 使用CSS变量方便动态调整 */ - --drum-scale: 1.3; - --drum-translate-y: -15px; - --drum-rotate: 5deg; - --drum-brightness: 1.3; - --drum-saturation: 1.2; - - transform: scale(var(--drum-scale)) translateY(var(--drum-translate-y)) rotate(var(--drum-rotate)); - filter: brightness(var(--drum-brightness)) saturate(var(--drum-saturation)); - animation: drumBeat 0.1s ease-in-out; -} - -/* 战鼓闲置时的轻微脉动动画 */ -@keyframes idlePulse { - 0% { - transform: scale(1); - } - - 100% { - transform: scale(1.05); - } -} - -/* 增强跳动效果的关键帧动画 */ -@keyframes drumBeat { - 0% { - transform: scale(1); - } - - 50% { - transform: scale(var(--drum-scale, 1.3)) translateY(var(--drum-translate-y, -15px)) rotate(var(--drum-rotate, 5deg)); - } - - 100% { - transform: scale(1); - } -} - -.trophy { - animation: bounce 1s infinite alternate; -} - -@keyframes bounce { - from { - transform: translateY(0); - } - - to { - transform: translateY(-10px); - } -} /* 按钮样式 */ .btn-game-secondary { @@ -2320,11 +1962,7 @@ onUnmounted(() => { height: auto; } - /* 战鼓部分调整 */ - .drums-section { - transform: scale(0.8); - /* 缩小战鼓元素 */ - } + /* 2. 倒计时模块调整 - 移至冠军战区上方,缩小时间显示为一行 */ .timer-float {