Compare commits
27 Commits
master
...
feature/lo
Author | SHA1 | Date | |
---|---|---|---|
d6432ceb69 | |||
a72c6fcd2c | |||
afd5323ade | |||
f5bf2f01f5 | |||
292d913305 | |||
5aabaddc31 | |||
47f97cd291 | |||
605d60ec7e | |||
0274eb5407 | |||
2762a8b5f1 | |||
1977b791e5 | |||
874e2fc4dc | |||
1175524448 | |||
073321ed7a | |||
72c6bda35a | |||
4828d0bcd6 | |||
4c15c42ebc | |||
0537bdb86e | |||
79f1daf525 | |||
e4b562ed86 | |||
b588915cb2 | |||
2a85a09bc8 | |||
c6632b124c | |||
bf3b49e72b | |||
d44a793019 | |||
c3ccf8d73d | |||
04bf94df0c |
420
node_modules/.package-lock.json
generated
vendored
420
node_modules/.package-lock.json
generated
vendored
@ -930,6 +930,14 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/commander": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/commander/-/commander-7.2.0.tgz",
|
||||
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/convert-source-map": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-2.0.0.tgz",
|
||||
@ -986,6 +994,376 @@
|
||||
"resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz",
|
||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
|
||||
},
|
||||
"node_modules/d3": {
|
||||
"version": "7.9.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3/-/d3-7.9.0.tgz",
|
||||
"integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==",
|
||||
"dependencies": {
|
||||
"d3-array": "3",
|
||||
"d3-axis": "3",
|
||||
"d3-brush": "3",
|
||||
"d3-chord": "3",
|
||||
"d3-color": "3",
|
||||
"d3-contour": "4",
|
||||
"d3-delaunay": "6",
|
||||
"d3-dispatch": "3",
|
||||
"d3-drag": "3",
|
||||
"d3-dsv": "3",
|
||||
"d3-ease": "3",
|
||||
"d3-fetch": "3",
|
||||
"d3-force": "3",
|
||||
"d3-format": "3",
|
||||
"d3-geo": "3",
|
||||
"d3-hierarchy": "3",
|
||||
"d3-interpolate": "3",
|
||||
"d3-path": "3",
|
||||
"d3-polygon": "3",
|
||||
"d3-quadtree": "3",
|
||||
"d3-random": "3",
|
||||
"d3-scale": "4",
|
||||
"d3-scale-chromatic": "3",
|
||||
"d3-selection": "3",
|
||||
"d3-shape": "3",
|
||||
"d3-time": "3",
|
||||
"d3-time-format": "4",
|
||||
"d3-timer": "3",
|
||||
"d3-transition": "3",
|
||||
"d3-zoom": "3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-array": {
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmmirror.com/d3-array/-/d3-array-3.2.4.tgz",
|
||||
"integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
|
||||
"dependencies": {
|
||||
"internmap": "1 - 2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-axis": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-axis/-/d3-axis-3.0.0.tgz",
|
||||
"integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-brush": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-brush/-/d3-brush-3.0.0.tgz",
|
||||
"integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==",
|
||||
"dependencies": {
|
||||
"d3-dispatch": "1 - 3",
|
||||
"d3-drag": "2 - 3",
|
||||
"d3-interpolate": "1 - 3",
|
||||
"d3-selection": "3",
|
||||
"d3-transition": "3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-chord": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/d3-chord/-/d3-chord-3.0.1.tgz",
|
||||
"integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==",
|
||||
"dependencies": {
|
||||
"d3-path": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-color": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-color/-/d3-color-3.1.0.tgz",
|
||||
"integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-contour": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/d3-contour/-/d3-contour-4.0.2.tgz",
|
||||
"integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==",
|
||||
"dependencies": {
|
||||
"d3-array": "^3.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-delaunay": {
|
||||
"version": "6.0.4",
|
||||
"resolved": "https://registry.npmmirror.com/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
|
||||
"integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==",
|
||||
"dependencies": {
|
||||
"delaunator": "5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-dispatch": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
|
||||
"integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-drag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-drag/-/d3-drag-3.0.0.tgz",
|
||||
"integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==",
|
||||
"dependencies": {
|
||||
"d3-dispatch": "1 - 3",
|
||||
"d3-selection": "3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-dsv": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/d3-dsv/-/d3-dsv-3.0.1.tgz",
|
||||
"integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==",
|
||||
"dependencies": {
|
||||
"commander": "7",
|
||||
"iconv-lite": "0.6",
|
||||
"rw": "1"
|
||||
},
|
||||
"bin": {
|
||||
"csv2json": "bin/dsv2json.js",
|
||||
"csv2tsv": "bin/dsv2dsv.js",
|
||||
"dsv2dsv": "bin/dsv2dsv.js",
|
||||
"dsv2json": "bin/dsv2json.js",
|
||||
"json2csv": "bin/json2dsv.js",
|
||||
"json2dsv": "bin/json2dsv.js",
|
||||
"json2tsv": "bin/json2dsv.js",
|
||||
"tsv2csv": "bin/dsv2dsv.js",
|
||||
"tsv2json": "bin/dsv2json.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-ease": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/d3-ease/-/d3-ease-3.0.1.tgz",
|
||||
"integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-fetch": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/d3-fetch/-/d3-fetch-3.0.1.tgz",
|
||||
"integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==",
|
||||
"dependencies": {
|
||||
"d3-dsv": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-force": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-force/-/d3-force-3.0.0.tgz",
|
||||
"integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==",
|
||||
"dependencies": {
|
||||
"d3-dispatch": "1 - 3",
|
||||
"d3-quadtree": "1 - 3",
|
||||
"d3-timer": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-format": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-format/-/d3-format-3.1.0.tgz",
|
||||
"integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-geo": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/d3-geo/-/d3-geo-3.1.1.tgz",
|
||||
"integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==",
|
||||
"dependencies": {
|
||||
"d3-array": "2.5.0 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-hierarchy": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz",
|
||||
"integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-interpolate": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
|
||||
"integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
|
||||
"dependencies": {
|
||||
"d3-color": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-path": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-path/-/d3-path-3.1.0.tgz",
|
||||
"integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-polygon": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/d3-polygon/-/d3-polygon-3.0.1.tgz",
|
||||
"integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-quadtree": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/d3-quadtree/-/d3-quadtree-3.0.1.tgz",
|
||||
"integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-random": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/d3-random/-/d3-random-3.0.1.tgz",
|
||||
"integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-scale": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/d3-scale/-/d3-scale-4.0.2.tgz",
|
||||
"integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
|
||||
"dependencies": {
|
||||
"d3-array": "2.10.0 - 3",
|
||||
"d3-format": "1 - 3",
|
||||
"d3-interpolate": "1.2.0 - 3",
|
||||
"d3-time": "2.1.1 - 3",
|
||||
"d3-time-format": "2 - 4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-scale-chromatic": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz",
|
||||
"integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==",
|
||||
"dependencies": {
|
||||
"d3-color": "1 - 3",
|
||||
"d3-interpolate": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-selection": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-selection/-/d3-selection-3.0.0.tgz",
|
||||
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-shape": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-shape/-/d3-shape-3.2.0.tgz",
|
||||
"integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
|
||||
"dependencies": {
|
||||
"d3-path": "^3.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-time": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-time/-/d3-time-3.1.0.tgz",
|
||||
"integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
|
||||
"dependencies": {
|
||||
"d3-array": "2 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-time-format": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-time-format/-/d3-time-format-4.1.0.tgz",
|
||||
"integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
|
||||
"dependencies": {
|
||||
"d3-time": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-timer": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/d3-timer/-/d3-timer-3.0.1.tgz",
|
||||
"integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-transition": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/d3-transition/-/d3-transition-3.0.1.tgz",
|
||||
"integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==",
|
||||
"dependencies": {
|
||||
"d3-color": "1 - 3",
|
||||
"d3-dispatch": "1 - 3",
|
||||
"d3-ease": "1 - 3",
|
||||
"d3-interpolate": "1 - 3",
|
||||
"d3-timer": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"d3-selection": "2 - 3"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-zoom": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-zoom/-/d3-zoom-3.0.0.tgz",
|
||||
"integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==",
|
||||
"dependencies": {
|
||||
"d3-dispatch": "1 - 3",
|
||||
"d3-drag": "2 - 3",
|
||||
"d3-interpolate": "1 - 3",
|
||||
"d3-selection": "2 - 3",
|
||||
"d3-transition": "2 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.0.tgz",
|
||||
@ -1043,6 +1421,14 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/delaunator": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/delaunator/-/delaunator-5.0.1.tgz",
|
||||
"integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==",
|
||||
"dependencies": {
|
||||
"robust-predicates": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
@ -1440,6 +1826,17 @@
|
||||
"node": ">=18.18.0"
|
||||
}
|
||||
},
|
||||
"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==",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/immediate": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmmirror.com/immediate/-/immediate-3.0.6.tgz",
|
||||
@ -1450,6 +1847,14 @@
|
||||
"resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"node_modules/internmap": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/internmap/-/internmap-2.0.3.tgz",
|
||||
"integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/is-docker": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/is-docker/-/is-docker-3.0.0.tgz",
|
||||
@ -1893,6 +2298,11 @@
|
||||
"integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/robust-predicates": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/robust-predicates/-/robust-predicates-3.0.2.tgz",
|
||||
"integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg=="
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
"version": "4.40.1",
|
||||
"resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.40.1.tgz",
|
||||
@ -1944,11 +2354,21 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/rw": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmmirror.com/rw/-/rw-1.3.3.tgz",
|
||||
"integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ=="
|
||||
},
|
||||
"node_modules/safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||
},
|
||||
"node_modules/safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz",
|
||||
|
32
node_modules/.vite/deps/_metadata.json
generated
vendored
32
node_modules/.vite/deps/_metadata.json
generated
vendored
@ -1,43 +1,49 @@
|
||||
{
|
||||
"hash": "3498b3cb",
|
||||
"configHash": "9c7a641a",
|
||||
"lockfileHash": "c997fc3c",
|
||||
"browserHash": "38ceb684",
|
||||
"hash": "caa31cd8",
|
||||
"configHash": "8bd3ff55",
|
||||
"lockfileHash": "296e7a14",
|
||||
"browserHash": "748422e9",
|
||||
"optimized": {
|
||||
"axios": {
|
||||
"src": "../../axios/index.js",
|
||||
"file": "axios.js",
|
||||
"fileHash": "97de2fb4",
|
||||
"fileHash": "17dc0739",
|
||||
"needsInterop": false
|
||||
},
|
||||
"jszip": {
|
||||
"src": "../../jszip/dist/jszip.min.js",
|
||||
"file": "jszip.js",
|
||||
"fileHash": "3cd8a10a",
|
||||
"fileHash": "73a56b11",
|
||||
"needsInterop": true
|
||||
},
|
||||
"mitt": {
|
||||
"src": "../../mitt/dist/mitt.mjs",
|
||||
"file": "mitt.js",
|
||||
"fileHash": "72a77428",
|
||||
"needsInterop": false
|
||||
},
|
||||
"vue": {
|
||||
"src": "../../vue/dist/vue.runtime.esm-bundler.js",
|
||||
"file": "vue.js",
|
||||
"fileHash": "74f3ac3b",
|
||||
"fileHash": "dd9d6977",
|
||||
"needsInterop": false
|
||||
},
|
||||
"vue-router": {
|
||||
"src": "../../vue-router/dist/vue-router.mjs",
|
||||
"file": "vue-router.js",
|
||||
"fileHash": "d2d7abce",
|
||||
"fileHash": "1b493d5a",
|
||||
"needsInterop": false
|
||||
},
|
||||
"xlsx": {
|
||||
"src": "../../xlsx/xlsx.mjs",
|
||||
"file": "xlsx.js",
|
||||
"fileHash": "dbbdc859",
|
||||
"fileHash": "e65d0089",
|
||||
"needsInterop": false
|
||||
},
|
||||
"mitt": {
|
||||
"src": "../../mitt/dist/mitt.mjs",
|
||||
"file": "mitt.js",
|
||||
"fileHash": "ac6eb5ab",
|
||||
"d3": {
|
||||
"src": "../../d3/src/index.js",
|
||||
"file": "d3.js",
|
||||
"fileHash": "89f27bc8",
|
||||
"needsInterop": false
|
||||
}
|
||||
},
|
||||
|
10
node_modules/.vue-global-types/vue_3.5_0_0_0.d.ts
generated
vendored
10
node_modules/.vue-global-types/vue_3.5_0_0_0.d.ts
generated
vendored
@ -40,14 +40,6 @@ export {};
|
||||
expose?: (exposed: T) => void,
|
||||
}
|
||||
};
|
||||
type __VLS_NormalizeSlotReturns<S, R = NonNullable<S> extends (...args: any) => infer K ? K : any> = R extends any[] ? {
|
||||
[K in keyof R]: R[K] extends infer V
|
||||
? V extends Element ? V
|
||||
: V extends new (...args: any) => infer R ? ReturnType<__VLS_FunctionalComponent<R>>
|
||||
: V extends (...args: any) => infer R ? R
|
||||
: any
|
||||
: never
|
||||
} : R;
|
||||
type __VLS_IsFunction<T, K> = K extends keyof T
|
||||
? __VLS_IsAny<T[K]> extends false
|
||||
? unknown extends T[K]
|
||||
@ -113,7 +105,7 @@ export {};
|
||||
index: number,
|
||||
][];
|
||||
function __VLS_getSlotParameters<S, D extends S>(slot: S, decl?: D):
|
||||
__VLS_PickNotAny<NonNullable<D>, (...args: any) => any> extends (...args: infer P) => any ? P : any[];
|
||||
D extends (...args: infer P) => any ? P : any[];
|
||||
function __VLS_asFunctionalDirective<T>(dir: T): T extends import('vue').ObjectDirective
|
||||
? NonNullable<T['created' | 'beforeMount' | 'mounted' | 'beforeUpdate' | 'updated' | 'beforeUnmount' | 'unmounted']>
|
||||
: T extends (...args: any) => any
|
||||
|
421
package-lock.json
generated
421
package-lock.json
generated
@ -9,6 +9,7 @@
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"axios": "^1.9.0",
|
||||
"d3": "^7.9.0",
|
||||
"jszip": "^3.10.1",
|
||||
"process": "^0.11.10",
|
||||
"vue": "^3.5.13",
|
||||
@ -1579,6 +1580,14 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/commander": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/commander/-/commander-7.2.0.tgz",
|
||||
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/convert-source-map": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-2.0.0.tgz",
|
||||
@ -1635,6 +1644,376 @@
|
||||
"resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz",
|
||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
|
||||
},
|
||||
"node_modules/d3": {
|
||||
"version": "7.9.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3/-/d3-7.9.0.tgz",
|
||||
"integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==",
|
||||
"dependencies": {
|
||||
"d3-array": "3",
|
||||
"d3-axis": "3",
|
||||
"d3-brush": "3",
|
||||
"d3-chord": "3",
|
||||
"d3-color": "3",
|
||||
"d3-contour": "4",
|
||||
"d3-delaunay": "6",
|
||||
"d3-dispatch": "3",
|
||||
"d3-drag": "3",
|
||||
"d3-dsv": "3",
|
||||
"d3-ease": "3",
|
||||
"d3-fetch": "3",
|
||||
"d3-force": "3",
|
||||
"d3-format": "3",
|
||||
"d3-geo": "3",
|
||||
"d3-hierarchy": "3",
|
||||
"d3-interpolate": "3",
|
||||
"d3-path": "3",
|
||||
"d3-polygon": "3",
|
||||
"d3-quadtree": "3",
|
||||
"d3-random": "3",
|
||||
"d3-scale": "4",
|
||||
"d3-scale-chromatic": "3",
|
||||
"d3-selection": "3",
|
||||
"d3-shape": "3",
|
||||
"d3-time": "3",
|
||||
"d3-time-format": "4",
|
||||
"d3-timer": "3",
|
||||
"d3-transition": "3",
|
||||
"d3-zoom": "3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-array": {
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmmirror.com/d3-array/-/d3-array-3.2.4.tgz",
|
||||
"integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
|
||||
"dependencies": {
|
||||
"internmap": "1 - 2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-axis": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-axis/-/d3-axis-3.0.0.tgz",
|
||||
"integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-brush": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-brush/-/d3-brush-3.0.0.tgz",
|
||||
"integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==",
|
||||
"dependencies": {
|
||||
"d3-dispatch": "1 - 3",
|
||||
"d3-drag": "2 - 3",
|
||||
"d3-interpolate": "1 - 3",
|
||||
"d3-selection": "3",
|
||||
"d3-transition": "3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-chord": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/d3-chord/-/d3-chord-3.0.1.tgz",
|
||||
"integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==",
|
||||
"dependencies": {
|
||||
"d3-path": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-color": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-color/-/d3-color-3.1.0.tgz",
|
||||
"integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-contour": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/d3-contour/-/d3-contour-4.0.2.tgz",
|
||||
"integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==",
|
||||
"dependencies": {
|
||||
"d3-array": "^3.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-delaunay": {
|
||||
"version": "6.0.4",
|
||||
"resolved": "https://registry.npmmirror.com/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
|
||||
"integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==",
|
||||
"dependencies": {
|
||||
"delaunator": "5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-dispatch": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
|
||||
"integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-drag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-drag/-/d3-drag-3.0.0.tgz",
|
||||
"integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==",
|
||||
"dependencies": {
|
||||
"d3-dispatch": "1 - 3",
|
||||
"d3-selection": "3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-dsv": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/d3-dsv/-/d3-dsv-3.0.1.tgz",
|
||||
"integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==",
|
||||
"dependencies": {
|
||||
"commander": "7",
|
||||
"iconv-lite": "0.6",
|
||||
"rw": "1"
|
||||
},
|
||||
"bin": {
|
||||
"csv2json": "bin/dsv2json.js",
|
||||
"csv2tsv": "bin/dsv2dsv.js",
|
||||
"dsv2dsv": "bin/dsv2dsv.js",
|
||||
"dsv2json": "bin/dsv2json.js",
|
||||
"json2csv": "bin/json2dsv.js",
|
||||
"json2dsv": "bin/json2dsv.js",
|
||||
"json2tsv": "bin/json2dsv.js",
|
||||
"tsv2csv": "bin/dsv2dsv.js",
|
||||
"tsv2json": "bin/dsv2json.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-ease": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/d3-ease/-/d3-ease-3.0.1.tgz",
|
||||
"integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-fetch": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/d3-fetch/-/d3-fetch-3.0.1.tgz",
|
||||
"integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==",
|
||||
"dependencies": {
|
||||
"d3-dsv": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-force": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-force/-/d3-force-3.0.0.tgz",
|
||||
"integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==",
|
||||
"dependencies": {
|
||||
"d3-dispatch": "1 - 3",
|
||||
"d3-quadtree": "1 - 3",
|
||||
"d3-timer": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-format": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-format/-/d3-format-3.1.0.tgz",
|
||||
"integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-geo": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/d3-geo/-/d3-geo-3.1.1.tgz",
|
||||
"integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==",
|
||||
"dependencies": {
|
||||
"d3-array": "2.5.0 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-hierarchy": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz",
|
||||
"integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-interpolate": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
|
||||
"integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
|
||||
"dependencies": {
|
||||
"d3-color": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-path": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-path/-/d3-path-3.1.0.tgz",
|
||||
"integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-polygon": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/d3-polygon/-/d3-polygon-3.0.1.tgz",
|
||||
"integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-quadtree": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/d3-quadtree/-/d3-quadtree-3.0.1.tgz",
|
||||
"integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-random": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/d3-random/-/d3-random-3.0.1.tgz",
|
||||
"integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-scale": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/d3-scale/-/d3-scale-4.0.2.tgz",
|
||||
"integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
|
||||
"dependencies": {
|
||||
"d3-array": "2.10.0 - 3",
|
||||
"d3-format": "1 - 3",
|
||||
"d3-interpolate": "1.2.0 - 3",
|
||||
"d3-time": "2.1.1 - 3",
|
||||
"d3-time-format": "2 - 4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-scale-chromatic": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz",
|
||||
"integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==",
|
||||
"dependencies": {
|
||||
"d3-color": "1 - 3",
|
||||
"d3-interpolate": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-selection": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-selection/-/d3-selection-3.0.0.tgz",
|
||||
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-shape": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-shape/-/d3-shape-3.2.0.tgz",
|
||||
"integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
|
||||
"dependencies": {
|
||||
"d3-path": "^3.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-time": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-time/-/d3-time-3.1.0.tgz",
|
||||
"integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
|
||||
"dependencies": {
|
||||
"d3-array": "2 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-time-format": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-time-format/-/d3-time-format-4.1.0.tgz",
|
||||
"integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
|
||||
"dependencies": {
|
||||
"d3-time": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-timer": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/d3-timer/-/d3-timer-3.0.1.tgz",
|
||||
"integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-transition": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/d3-transition/-/d3-transition-3.0.1.tgz",
|
||||
"integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==",
|
||||
"dependencies": {
|
||||
"d3-color": "1 - 3",
|
||||
"d3-dispatch": "1 - 3",
|
||||
"d3-ease": "1 - 3",
|
||||
"d3-interpolate": "1 - 3",
|
||||
"d3-timer": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"d3-selection": "2 - 3"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-zoom": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-zoom/-/d3-zoom-3.0.0.tgz",
|
||||
"integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==",
|
||||
"dependencies": {
|
||||
"d3-dispatch": "1 - 3",
|
||||
"d3-drag": "2 - 3",
|
||||
"d3-interpolate": "1 - 3",
|
||||
"d3-selection": "2 - 3",
|
||||
"d3-transition": "2 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.0.tgz",
|
||||
@ -1692,6 +2071,14 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/delaunator": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/delaunator/-/delaunator-5.0.1.tgz",
|
||||
"integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==",
|
||||
"dependencies": {
|
||||
"robust-predicates": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
@ -2103,6 +2490,17 @@
|
||||
"node": ">=18.18.0"
|
||||
}
|
||||
},
|
||||
"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==",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/immediate": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmmirror.com/immediate/-/immediate-3.0.6.tgz",
|
||||
@ -2113,6 +2511,14 @@
|
||||
"resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"node_modules/internmap": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/internmap/-/internmap-2.0.3.tgz",
|
||||
"integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/is-docker": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/is-docker/-/is-docker-3.0.0.tgz",
|
||||
@ -2556,6 +2962,11 @@
|
||||
"integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/robust-predicates": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/robust-predicates/-/robust-predicates-3.0.2.tgz",
|
||||
"integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg=="
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
"version": "4.40.1",
|
||||
"resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.40.1.tgz",
|
||||
@ -2607,11 +3018,21 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/rw": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmmirror.com/rw/-/rw-1.3.3.tgz",
|
||||
"integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ=="
|
||||
},
|
||||
"node_modules/safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||
},
|
||||
"node_modules/safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz",
|
||||
|
@ -10,6 +10,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.9.0",
|
||||
"d3": "^7.9.0",
|
||||
"jszip": "^3.10.1",
|
||||
"process": "^0.11.10",
|
||||
"vue": "^3.5.13",
|
||||
|
545
public/AlliedAntiAirShip.xml
Normal file
545
public/AlliedAntiAirShip.xml
Normal file
@ -0,0 +1,545 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<AssetDeclaration xmlns="uri:ea.com:eala:asset" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xai="uri:ea.com:eala:asset:instance">
|
||||
<Tags></Tags>
|
||||
<Includes>
|
||||
<Include type="all" source="DATA:GlobalData/GlobalDefines.xml" />
|
||||
<Include
|
||||
type="all"
|
||||
source="ART:AUAntiAirShip_D.xml" />
|
||||
<Include
|
||||
type="all"
|
||||
source="ART:AUAntiAirShip_FX.w3x" />
|
||||
<Include
|
||||
type="all"
|
||||
source="ART:AUAntiAirShip_FPZ.w3x" />
|
||||
<Include
|
||||
type="all"
|
||||
source="ART:AUAntiAirShip_SKN.w3x" />
|
||||
<Include
|
||||
type="all"
|
||||
source="ART:FXGradient01.xml" />
|
||||
<Include
|
||||
type="all"
|
||||
source="ART:FXTracer.xml" />
|
||||
<Include
|
||||
type="all"
|
||||
source="ART:FXTracerHeroic.xml" />
|
||||
<!-- needed for temp laserEndParticleSystemFX? -->
|
||||
<Include
|
||||
type="all"
|
||||
source="ART:SUAntiVehicleVehicleTech3_FX.w3x" />
|
||||
<Include
|
||||
type="instance"
|
||||
source="ART:FXGrid_3.xml" />
|
||||
<Include
|
||||
type="instance"
|
||||
source="ART:FXHarpoonBeam.xml" />
|
||||
<!-- Base Object -->
|
||||
<Include
|
||||
type="instance"
|
||||
source="DATA:BaseObjects/BaseVehicle.xml" />
|
||||
</Includes>
|
||||
<!-- aka The Hydrofoil -->
|
||||
<GameObject
|
||||
id="AlliedAntiAirShip"
|
||||
inheritFrom="BaseVehicle"
|
||||
SelectPortrait="Portrait_AlliedAntiAirShip"
|
||||
ButtonImage="Button_AlliedAntiAirShip_on"
|
||||
Side="Allies"
|
||||
SubGroupPriority="440"
|
||||
EditorSorting="UNIT"
|
||||
HealthBoxHeightOffset="30"
|
||||
TransportSlotCount="10"
|
||||
BuildTime="10"
|
||||
CommandSet="AlliedAntiAirShipCommandSet"
|
||||
KindOf="SELECTABLE CAN_ATTACK CAN_CAST_REFLECTIONS SCORE VEHICLE SHIP CAN_BE_FAVORITE_UNIT"
|
||||
RadarPriority="UNIT"
|
||||
ProductionQueueType="WATERCRAFT"
|
||||
UnitCategory="VEHICLE"
|
||||
WeaponCategory="CANNON"
|
||||
VoicePriority="188"
|
||||
EditorName="AlliedAntiAirShip"
|
||||
Description="Desc:AlliedAntiAirShip"
|
||||
TypeDescription="Type:AlliedAntiAirShip"
|
||||
UnitIntro="Allied_Hydrofoil_UnitIntro">
|
||||
<DisplayName
|
||||
xai:joinAction="Replace" xmlns:xai="uri:ea.com:eala:asset:instance">Name:AlliedAntiAirShip</DisplayName>
|
||||
<ObjectResourceInfo>
|
||||
<BuildCost Account="=$ACCOUNT_ORE" Amount="900"/>
|
||||
</ObjectResourceInfo>
|
||||
<ArmorSet
|
||||
Armor="AlliedAntiAirShipArmor"
|
||||
DamageFX="VehicleDamageFX" />
|
||||
<LocomotorSet
|
||||
id="DefaultWaterLocomotorSet"
|
||||
Locomotor="AlliedAntiAirShipWaterLocomotor"
|
||||
Condition="NORMAL"
|
||||
Speed="125.0" />
|
||||
<LocomotorSet
|
||||
Locomotor="AlliedAntiAirShipWaterLocomotor_LeavingFactory"
|
||||
Condition="EXITING_PRODUCTION_STRUCTURE"
|
||||
Speed="125.0" />
|
||||
<SkirmishAIInformation
|
||||
UnitBuilderStandardCombatUnit="true" />
|
||||
<Draws>
|
||||
<ScriptedModelDraw
|
||||
id="ModuleTag_Draw"
|
||||
OkToChangeModelColor="true"
|
||||
InitialRecoilSpeed="0.1"
|
||||
MaxRecoilDistance="0.1"
|
||||
RecoilDamping="2.0"
|
||||
RecoilSettleSpeed="3.0"
|
||||
ExtraPublicBone="FX_Weapon_01 FX_Weapon_02" >
|
||||
<ModelConditionState
|
||||
ParseCondStateType="PARSE_DEFAULT"
|
||||
RetainSubObjects="true">
|
||||
<Model
|
||||
Name="AUAntiAirShip_SKN" />
|
||||
<WeaponFireFXBone
|
||||
WeaponSlotID="1"
|
||||
WeaponSlotType="PRIMARY_WEAPON"
|
||||
BoneName="FX_Weapon_01" />
|
||||
<WeaponRecoilBone
|
||||
WeaponSlotID="1"
|
||||
WeaponSlotType="PRIMARY_WEAPON"
|
||||
BoneName="FX_Weapon_01" />
|
||||
<WeaponLaunchBone
|
||||
WeaponSlotID="1"
|
||||
WeaponSlotType="PRIMARY_WEAPON"
|
||||
BoneName="FX_Weapon_01" />
|
||||
|
||||
<WeaponFireFXBone
|
||||
WeaponSlotID="1"
|
||||
WeaponSlotType="SECONDARY_WEAPON"
|
||||
BoneName="FX_Weapon_02" />
|
||||
<WeaponRecoilBone
|
||||
WeaponSlotID="1"
|
||||
WeaponSlotType="SECONDARY_WEAPON"
|
||||
BoneName="FX_Weapon_02" />
|
||||
<WeaponLaunchBone
|
||||
WeaponSlotID="1"
|
||||
WeaponSlotType="SECONDARY_WEAPON"
|
||||
BoneName="FX_Weapon_02" />
|
||||
<Turret
|
||||
TurretNameKey="turret"
|
||||
TurretPitch="Turret_Pitch"
|
||||
TurretID="1" />
|
||||
</ModelConditionState>
|
||||
|
||||
<ModelConditionState
|
||||
ParseCondStateType="PARSE_NORMAL"
|
||||
ConditionsYes="FORMATION_PREVIEW">
|
||||
<Model
|
||||
Name="AUAntiAirShip_SKN" />
|
||||
<Material
|
||||
ShaderName="FX_FormPreview.fx"
|
||||
TechniqueName="Default">
|
||||
<Constants>
|
||||
<Texture Name="SpecMap">
|
||||
<Value>FXGradient01</Value>
|
||||
</Texture>
|
||||
</Constants>
|
||||
</Material>
|
||||
</ModelConditionState>
|
||||
<ModelConditionState
|
||||
ParseCondStateType="PARSE_NORMAL"
|
||||
RetainSubObjects="true"
|
||||
ConditionsYes="REALLYDAMAGED">
|
||||
<Model
|
||||
Name="AUAntiAirShip_SKN" />
|
||||
<Texture
|
||||
Original="AUAntiAirShip"
|
||||
New="AUAntiAirShip_D" />
|
||||
</ModelConditionState>
|
||||
|
||||
<AnimationState
|
||||
ParseCondStateType="PARSE_DEFAULT">
|
||||
<Script>
|
||||
CurDrawableShowSubObjectPermanently("GUN")
|
||||
CurDrawableHideSubObjectPermanently("BEAM")
|
||||
</Script>
|
||||
<ParticleSysBone
|
||||
BoneName="None"
|
||||
FXParticleSystemTemplate="SmallShipWakeIdle"
|
||||
FollowBone="false" />
|
||||
</AnimationState>
|
||||
|
||||
<AnimationState
|
||||
ParseCondStateType="PARSE_NORMAL"
|
||||
ConditionsYes="MOVING">
|
||||
<ParticleSysBone
|
||||
BoneName="NONE"
|
||||
FXParticleSystemTemplate="AUHydrofoilWaterWake"
|
||||
FollowBone="false" />
|
||||
</AnimationState>
|
||||
<AnimationState
|
||||
ParseCondStateType="PARSE_NORMAL"
|
||||
ConditionsYes="WEAPONSTATE_TWO">
|
||||
<Script>
|
||||
CurDrawableHideSubObjectPermanently("GUN")
|
||||
CurDrawableShowSubObjectPermanently("BEAM")
|
||||
</Script>
|
||||
</AnimationState>
|
||||
</ScriptedModelDraw>
|
||||
|
||||
<ScriptedModelDraw
|
||||
id="ModuleTag_DrawZ"
|
||||
OkToChangeModelColor="true"
|
||||
InitialRecoilSpeed="0.1"
|
||||
MaxRecoilDistance="0.1"
|
||||
RecoilDamping="2.0"
|
||||
RecoilSettleSpeed="3.0"
|
||||
ExtraPublicBone="FX_Weapon_01 FX_Weapon_02">
|
||||
<ModelConditionState
|
||||
ParseCondStateType="PARSE_DEFAULT"
|
||||
RetainSubObjects="true">
|
||||
<Model
|
||||
Name="" />
|
||||
</ModelConditionState>
|
||||
|
||||
<ModelConditionState
|
||||
ParseCondStateType="PARSE_NORMAL"
|
||||
ConditionsYes="FORMATION_PREVIEW">
|
||||
<Model
|
||||
Name="AUAntiAirShip_FPZ" />
|
||||
</ModelConditionState>
|
||||
|
||||
<AnimationState
|
||||
ParseCondStateType="PARSE_DEFAULT">
|
||||
<Script>
|
||||
CurDrawableShowSubObjectPermanently("GUN")
|
||||
CurDrawableHideSubObjectPermanently("BEAM")
|
||||
</Script>
|
||||
</AnimationState>
|
||||
</ScriptedModelDraw>
|
||||
|
||||
<!-- Used in the Weapon Scramble Beam -->
|
||||
<LaserDraw
|
||||
id="ModuleTag_LaserDraw"
|
||||
Texture1_UTile="1"
|
||||
Texture1_VTile="1"
|
||||
Texture1_UScrollRate="0"
|
||||
Texture1_VScrollRate="0"
|
||||
Texture1_NumFrames="1"
|
||||
Texture1_FrameRate="30"
|
||||
Texture2_UTile="1"
|
||||
Texture2_VTile="1"
|
||||
Texture2_UScrollRate="0"
|
||||
Texture2_VScrollRate="1"
|
||||
Texture2_NumFrames="1"
|
||||
Texture2_FrameRate="30"
|
||||
LaserWidth="40"
|
||||
LaserStateID="1">
|
||||
<FXShader
|
||||
ShaderName="Laser.fx"
|
||||
TechniqueIndex="0">
|
||||
<Constants>
|
||||
<Texture
|
||||
Name="Texture1">
|
||||
<Value>FXGrid_3</Value>
|
||||
</Texture>
|
||||
<Texture
|
||||
Name="Texture2">
|
||||
<Value>FXInterlacedMask2</Value>
|
||||
</Texture>
|
||||
<Float Name="ColorEmissive">
|
||||
<Value>0.00000</Value>
|
||||
<Value>2.00000</Value>
|
||||
<Value>1.000000</Value>
|
||||
</Float>
|
||||
</Constants>
|
||||
</FXShader>
|
||||
</LaserDraw>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- Used for the Phalanx Gun -->
|
||||
<TracerModelDraw
|
||||
id="ModuleTag_TracerModelDraw"
|
||||
MinLength="10.0"
|
||||
MaxLength="25.0"
|
||||
Width="15.0"
|
||||
MinSpeed="22"
|
||||
MaxSpeed="32"
|
||||
SweepSpeed="3.0"
|
||||
SpreadAngle="5.0"
|
||||
MinTracersPerFrame="0.4"
|
||||
MaxTracersPerFrame="0.4"
|
||||
FrameLifeTime="25"
|
||||
WeaponSlotType="PRIMARY_WEAPON"
|
||||
Texture="FXTracer"
|
||||
UseAdditiveBlending="true" >
|
||||
<HeadColor
|
||||
r="1.0"
|
||||
g="1.0"
|
||||
b="1.0"
|
||||
a="1.0" />
|
||||
<TailColor
|
||||
r="1.0"
|
||||
g="0.75"
|
||||
b="0.65"
|
||||
a="0.0" />
|
||||
<ObjectStatusValidation
|
||||
ForbiddenStatus="WEAPON_UPGRADED_01 GENERIC_TOGGLE_STATE" />
|
||||
</TracerModelDraw>
|
||||
<TracerModelDraw
|
||||
id="ModuleTag_TracerModelDrawVeterancy"
|
||||
MinLength="10.0"
|
||||
MaxLength="25.0"
|
||||
Width="15.0"
|
||||
MinSpeed="30"
|
||||
MaxSpeed="30"
|
||||
SweepSpeed="0.5"
|
||||
SpreadAngle="0.5"
|
||||
MinTracersPerFrame="0.4"
|
||||
MaxTracersPerFrame="0.4"
|
||||
FrameLifeTime="35"
|
||||
WeaponSlotType="PRIMARY_WEAPON"
|
||||
Texture="FXTracerHeroic"
|
||||
UseAdditiveBlending="true" >
|
||||
<HeadColor
|
||||
r="1.0"
|
||||
g="0.0"
|
||||
b="0.0"
|
||||
a="1.0" />
|
||||
<TailColor
|
||||
r="1.0"
|
||||
g="0.75"
|
||||
b="0.65"
|
||||
a="0.0" />
|
||||
<ObjectStatusValidation
|
||||
RequiredStatus="WEAPON_UPGRADED_01"
|
||||
ForbiddenStatus="GENERIC_TOGGLE_STATE" />
|
||||
</TracerModelDraw>
|
||||
<!-- DRAW PARTICLES -->
|
||||
|
||||
<ScriptedModelDraw
|
||||
id="ModuleTag_Draw_FX"
|
||||
OkToChangeModelColor="true">
|
||||
<ModelConditionState
|
||||
ParseCondStateType="PARSE_DEFAULT">
|
||||
<Model
|
||||
Name="AUAntiAirShip_FX" />
|
||||
</ModelConditionState>
|
||||
<ModelConditionState
|
||||
ParseCondStateType="PARSE_NORMAL"
|
||||
ConditionsYes="DAMAGED">
|
||||
<Model
|
||||
Name="AUAntiAirShip_FX" />
|
||||
<ParticleSysBone
|
||||
BoneName="FX_BONE01"
|
||||
FXParticleSystemTemplate="VehicleDamageSmoke"
|
||||
FollowBone="true" />
|
||||
|
||||
</ModelConditionState>
|
||||
<ModelConditionState
|
||||
ParseCondStateType="PARSE_NORMAL"
|
||||
ConditionsYes="REALLYDAMAGED">
|
||||
<Model
|
||||
Name="AUAntiAirShip_FX" />
|
||||
<ParticleSysBone
|
||||
BoneName="FX_BONE01"
|
||||
FXParticleSystemTemplate="VehicleDamageSmoke"
|
||||
FollowBone="true" />
|
||||
<ParticleSysBone
|
||||
BoneName="FX_BONE01"
|
||||
FXParticleSystemTemplate="VehicleDamageFire"
|
||||
FollowBone="true" />
|
||||
<ParticleSysBone
|
||||
BoneName="FX_BONE01"
|
||||
FXParticleSystemTemplate="VehicleDamageFire02"
|
||||
FollowBone="true" />
|
||||
|
||||
</ModelConditionState>
|
||||
</ScriptedModelDraw>
|
||||
|
||||
|
||||
</Draws>
|
||||
<Behaviors>
|
||||
<WeaponSetUpdate
|
||||
id="ModuleTag_WeaponSetUpdate">
|
||||
<WeaponSlotTurret
|
||||
ID="1">
|
||||
<!-- This weapon is always around, but the weapon template itself prevents it
|
||||
from being able to be fired once it's upgraded. -->
|
||||
<Weapon
|
||||
Ordering="PRIMARY_WEAPON"
|
||||
Template="AlliedAntiAirShipPhalanxGun"
|
||||
ForbiddenObjectStatus="GENERIC_TOGGLE_STATE"
|
||||
/>
|
||||
<Weapon
|
||||
Ordering="SECONDARY_WEAPON"
|
||||
Template="AlliedAntiAirShipWeaponScrambler"
|
||||
ObjectStatus="GENERIC_TOGGLE_STATE"/>
|
||||
<TurretSettings
|
||||
TurretTurnRate="360"
|
||||
MinimumPitch="0d"
|
||||
AllowsPitch="true"
|
||||
TurretPitchRate="180"
|
||||
MinIdleScanTime="1.0s"
|
||||
MaxIdleScanTime="5.0s"
|
||||
MinIdleScanAngle="10.0"
|
||||
MaxIdleScanAngle="90.0"
|
||||
ComeToHaltJiggle="3d">
|
||||
<TurretAITargetChooserData
|
||||
IdleScanDelay="=$FAST_IDLE_SCAN_DELAY"
|
||||
CanAcquireDynamicIfAssignedOutOfRange="true" />
|
||||
</TurretSettings>
|
||||
</WeaponSlotTurret>
|
||||
</WeaponSetUpdate>
|
||||
<Physics
|
||||
id="ModuleTag_Physics" />
|
||||
<CreateObjectDie
|
||||
id="ModuleTag_CreateObjectDie"
|
||||
CreationList="AUAntiAirShip_Die_OCL">
|
||||
<DieMuxData
|
||||
DeathTypes="ALL"
|
||||
DeathTypesForbidden="FLOODED"/>
|
||||
</CreateObjectDie>
|
||||
<CreateObjectDie
|
||||
id="ModuleTag_CreateObjectDieWhole"
|
||||
CreationList="AUAntiAirShip_Die_OCL">
|
||||
<DieMuxData
|
||||
DeathTypes="FLOODED" />
|
||||
</CreateObjectDie>
|
||||
<DynamicsUpdate
|
||||
id="ModuleTag_DefaultDynamicsUpdate"
|
||||
xai:joinAction="Remove" />
|
||||
<DestroyDie
|
||||
id="ModuleTag_Die">
|
||||
<DieMuxData
|
||||
DeathTypes="ALL" />
|
||||
</DestroyDie>
|
||||
<FXListBehavior
|
||||
id="ModuleTag_FXList">
|
||||
<DieMuxData
|
||||
DeathTypes="ALL" />
|
||||
<Event
|
||||
Index="onDeath"
|
||||
FX="FX_ALL_HydrofoilDie" />
|
||||
</FXListBehavior>
|
||||
|
||||
<!-- The toggle for the Weapon Scrambler -->
|
||||
<SpecialPower
|
||||
id="ModuleTag_ActivateWeaponScrambler"
|
||||
SpecialPowerTemplate="SpecialPower_ToggleWeaponScrambler"
|
||||
UpdateModuleStartsAttack="true" />
|
||||
<ToggleStatusSpecialAbilityUpdate
|
||||
id="ModuleTag_ActivateWeaponScramblerUpdate"
|
||||
SpecialPowerTemplate="SpecialPower_ToggleWeaponScrambler"
|
||||
Options="RECONSTITUTE_STORED_COMMAND">
|
||||
|
||||
<ToggleState
|
||||
EnterStateSound="ALL_HydroFoil_ScramblerToggleOffMS">
|
||||
<SkirmishAiInfo
|
||||
ToggleHint="TOGGLE_DEFAULT">
|
||||
<StateWeapon
|
||||
Weapon="AlliedAntiAirShipPhalanxGun" />
|
||||
</SkirmishAiInfo>
|
||||
</ToggleState>
|
||||
|
||||
<ToggleState
|
||||
ObjectStatus="GENERIC_TOGGLE_STATE"
|
||||
ModelConditions="WEAPONSTATE_TWO"
|
||||
EnterStateSound="ALL_HydroFoil_ScramblerToggleOnMS">
|
||||
<SkirmishAiInfo
|
||||
ToggleHint="TOGGLE_LOCKDOWN"
|
||||
NeverUseInState="RETREAT">
|
||||
<StateWeapon
|
||||
Weapon="AlliedAntiAirShipWeaponScrambler" />
|
||||
</SkirmishAiInfo>
|
||||
</ToggleState>
|
||||
|
||||
</ToggleStatusSpecialAbilityUpdate>
|
||||
|
||||
<!-- The special power that is used by the weapon -->
|
||||
<SpecialPower
|
||||
id="ModuleTag_WeaponScrambler"
|
||||
SpecialPowerTemplate="SpecialPower_WeaponScrambler"
|
||||
TriggerFX="FX_None"
|
||||
UpdateModuleStartsAttack="false" />
|
||||
|
||||
<LaserState
|
||||
id="ModuleTag_LaserState"
|
||||
LaserId="1" >
|
||||
<LaserEndParticleSystem>AlliedHydroScrambler_Sparks</LaserEndParticleSystem>
|
||||
<LaserStartParticleSystem>AlliedHydroScrambler_Start</LaserStartParticleSystem>
|
||||
</LaserState>
|
||||
<StatusBitsUpgrade
|
||||
id="ModuleTag_VeterancyUpgrade"
|
||||
StatusToSet="WEAPON_UPGRADED_01">
|
||||
<TriggeredBy>Upgrade_Veterancy_HEROIC</TriggeredBy>
|
||||
</StatusBitsUpgrade>
|
||||
</Behaviors>
|
||||
<AI>
|
||||
<AIUpdate
|
||||
id="ModuleTag_AI"
|
||||
AutoAcquireEnemiesWhenIdle="YES"
|
||||
StateMachine="UnitAIStateMachine">
|
||||
<UnitAITargetChooserData
|
||||
CanPickDynamicTargets="false"/>
|
||||
</AIUpdate>
|
||||
</AI>
|
||||
<Body>
|
||||
<ActiveBody
|
||||
id="ModuleTag_02"
|
||||
MaxHealth="400" />
|
||||
</Body>
|
||||
<ClientBehaviors>
|
||||
<ModelConditionAudioLoopClientBehavior id="ModuleTag_ShrunkenVoice">
|
||||
<ModelConditionSound Sound="ALL_Hydrofoil_VoiceShrunken" RequiredFlags="SHRINK_EFFECT" />
|
||||
</ModelConditionAudioLoopClientBehavior>
|
||||
<ModelConditionSoundSelectorClientBehavior id="ModuleTag_VoiceAttackWeaponJammer">
|
||||
<Override RequiredFlags="WEAPONSTATE_TWO">
|
||||
<AudioArrayVoice>
|
||||
<AudioEntry Sound="ALL_Hydrofoil_VoiceAttackSpecial" AudioType="voiceAttack" />
|
||||
<AudioEntry Sound="ALL_Hydrofoil_VoiceSelectMS" AudioType="voiceSelectBattle" />
|
||||
</AudioArrayVoice>
|
||||
</Override>
|
||||
</ModelConditionSoundSelectorClientBehavior>
|
||||
<ModelConditionAudioLoopClientBehavior xai:joinAction="Replace" xmlns:xai="uri:ea.com:eala:asset:instance" id="ModuleTag_MagneticSatelliteSuckedAway">
|
||||
<ModelConditionSound Sound="SOV_MagneticSatellite_SuckedAwayWater" RequiredFlags="SUCKED_UP_HIGH" />
|
||||
</ModelConditionAudioLoopClientBehavior>
|
||||
</ClientBehaviors>
|
||||
<Geometry>
|
||||
<Shape
|
||||
Type="BOX"
|
||||
MajorRadius="30.0"
|
||||
MinorRadius="18.0"
|
||||
Height="20.0"
|
||||
ContactPointGeneration="VEHICLE"/>
|
||||
</Geometry>
|
||||
<AudioArrayVoice>
|
||||
<AudioEntry Sound="ALL_Hydrofoil_VoiceAttack" AudioType="voiceAttack" />
|
||||
<AudioEntry Sound="ALL_Hydrofoil_VoiceMoveAttack" AudioType="voiceAttackAfterMoving" />
|
||||
<AudioEntry Sound="ALL_Hydrofoil_VoiceCreate" AudioType="voiceCreated" />
|
||||
<AudioEntry Sound="ALL_Hydrofoil_VoiceMove" AudioType="voiceMove" />
|
||||
<AudioEntry Sound="ALL_Hydrofoil_VoiceRetreat" AudioType="voiceRetreatToCastle" />
|
||||
<AudioEntry Sound="ALL_Hydrofoil_VoiceSelectMS" AudioType="voiceSelect" />
|
||||
<AudioEntry Sound="ALL_Hydrofoil_VoiceSelectBattleMS" AudioType="voiceSelectBattle" />
|
||||
<AudioEntry Sound="ALL_Hydrofoil_VoiceSelectUnderFireMS" AudioType="voiceSelectUnderFire" />
|
||||
<!-- <NamedEntry Sound="ALL_Hydrofoil_VoiceAttackSpecial" Name="VoiceWeaponScrambler" /> oops plays on toggle -->
|
||||
</AudioArrayVoice>
|
||||
<AudioArraySound>
|
||||
<AudioEntry
|
||||
Sound="ALL_Hydrofoil_MoveStart"
|
||||
AudioType="soundMoveStart" />
|
||||
<AudioEntry
|
||||
Sound="VehicleCrush"
|
||||
AudioType="soundCrushing" />
|
||||
<AudioEntry
|
||||
Sound="Ship_Small_MoveLoopWater"
|
||||
AudioType="soundMoveLoop" />
|
||||
</AudioArraySound>
|
||||
<VisionInfo
|
||||
VisionRange="325"
|
||||
ShroudClearingRange="=$STANDARD_SHROUD_CLEAR" />
|
||||
<CrusherInfo
|
||||
id="id_CrusherInfo"
|
||||
CrusherLevel="1"
|
||||
CrushableLevel="20" />
|
||||
</GameObject>
|
||||
</AssetDeclaration>
|
1577
public/ButtonStateDataCommon.xml
Normal file
1577
public/ButtonStateDataCommon.xml
Normal file
File diff suppressed because it is too large
Load Diff
3253
public/Locomotor.xml
Normal file
3253
public/Locomotor.xml
Normal file
File diff suppressed because it is too large
Load Diff
2291
public/LogicCommand.xml
Normal file
2291
public/LogicCommand.xml
Normal file
File diff suppressed because it is too large
Load Diff
1097
public/LogicCommandSet.xml
Normal file
1097
public/LogicCommandSet.xml
Normal file
File diff suppressed because it is too large
Load Diff
1649
public/SpecialPowerTemplates.xml
Normal file
1649
public/SpecialPowerTemplates.xml
Normal file
File diff suppressed because it is too large
Load Diff
1088
public/UnitAbilityButtonTemplates.xml
Normal file
1088
public/UnitAbilityButtonTemplates.xml
Normal file
File diff suppressed because it is too large
Load Diff
64
src/api/record.js
Normal file
64
src/api/record.js
Normal file
@ -0,0 +1,64 @@
|
||||
import axiosInstance from './axiosConfig';
|
||||
|
||||
/**
|
||||
* 上传处理后的录像文件
|
||||
* 路由: /record/upload
|
||||
* 方法: POST
|
||||
* 需要admin权限
|
||||
* @param {file} file - 表单负载"file"上传
|
||||
* @returns {id<int>} - HTTP_202_ACCEPTED 录像id
|
||||
*/
|
||||
export const uploadRecord = async (file) => {
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
const response = await axiosInstance.post('/record/upload', formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
});
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取录像解析状态
|
||||
* 路由: /record/{id}
|
||||
* 方法: GET
|
||||
* 需要登录
|
||||
* @param {int} id - 录像id
|
||||
* @returns {id<int>} id - 录像id
|
||||
* @returns {status<string>} status - 状态processing 处理中success 处理成功fail 处理失败
|
||||
* @returns {data<json>} data - 录像数据仅当处理成功时有值
|
||||
*/
|
||||
export const getRecordStatus = async (id) => {
|
||||
try {
|
||||
const response = await axiosInstance.get(`/record/${id}`);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 获取单位信息
|
||||
* 路由: /unit
|
||||
* 方法: GET
|
||||
* 三个参数仅使用一个即可,如果传入多个优先选择上面的
|
||||
* @param {Object} params - 参数 { id, code, name }
|
||||
* @returns {id<string>} id
|
||||
* @returns {code<string>} code
|
||||
* @returns {name<string>} name
|
||||
*/
|
||||
export const unitInfo = async (params = {}) => {
|
||||
try {
|
||||
const response = await axiosInstance.get('/unit', { params });
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
throw error;
|
||||
}
|
||||
}
|
@ -3,239 +3,225 @@ import axiosInstance from './axiosConfig';
|
||||
/**
|
||||
* 添加赛事
|
||||
* @param {Object} tournamentData - 赛事数据
|
||||
* @param {string} tournamentData.name - 赛事名称
|
||||
* @param {string} tournamentData.format - 赛事类型(single, double, count)
|
||||
* @param {number} tournamentData.id - 数据库中id
|
||||
* @param {string} tournamentData.name - 名称
|
||||
* @param {string} tournamentData.format - 类型(single, double, count)
|
||||
* @param {string} tournamentData.organizer - 组织者
|
||||
* @param {string} tournamentData.qq_code - QQ号
|
||||
* @param {string} tournamentData.status - 状态(prepare, finish, starting)
|
||||
* @param {string} tournamentData.start_time - 开始时间(格式年/月/日,例2025/05/24)
|
||||
* @param {string} tournamentData.end_time - 结束时间(格式年/月/日,例2025/05/24)
|
||||
* @returns {Promise} 返回添加赛事的响应数据
|
||||
* @returns {Promise<Object>} 返回添加赛事的响应数据
|
||||
*/
|
||||
export const addTournament = async (tournamentData) => {
|
||||
try {
|
||||
const response = await axiosInstance.post('/tournament/add', tournamentData)
|
||||
return response.data
|
||||
const response = await axiosInstance.post('/tournament/add', tournamentData);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('添加赛事失败:', {
|
||||
status: error.response?.status,
|
||||
data: error.response?.data,
|
||||
message: error.message
|
||||
})
|
||||
throw error
|
||||
console.error('添加赛事失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取赛事列表
|
||||
* @returns {Promise} 返回赛事列表数据
|
||||
* @returns {Promise<Array<Object>>} 返回赛事列表数据
|
||||
*/
|
||||
export const getTournamentList = async () => {
|
||||
try {
|
||||
const response = await axiosInstance.get('/tournament/getlist')
|
||||
return response.data
|
||||
const response = await axiosInstance.get('/tournament/getlist');
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('获取赛事列表失败:', {
|
||||
status: error.response?.status,
|
||||
data: error.response?.data,
|
||||
message: error.message
|
||||
})
|
||||
throw error
|
||||
console.error('获取赛事列表失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// 更新赛事
|
||||
export const updateTournament = async (id, data) => {
|
||||
/**
|
||||
* 更新赛事
|
||||
* @param {number} id - 赛事ID
|
||||
* @param {Object} tournamentData - 赛事数据
|
||||
* @param {number} tournamentData.id - 数据库中id
|
||||
* @param {string} tournamentData.name - 名称
|
||||
* @param {string} tournamentData.format - 类型(single, double, count)
|
||||
* @param {string} tournamentData.organizer - 组织者
|
||||
* @param {string} tournamentData.qq_code - QQ号
|
||||
* @param {string} tournamentData.status - 状态(prepare, finish, starting)
|
||||
* @param {string} tournamentData.start_time - 开始时间(格式年/月/日,例2025/05/24)
|
||||
* @param {string} tournamentData.end_time - 结束时间(格式年/月/日,例2025/05/24)
|
||||
* @returns {Promise<Object>} 返回更新赛事的响应数据
|
||||
*/
|
||||
export const updateTournament = async (id, tournamentData) => {
|
||||
try {
|
||||
console.log('更新赛事,发送数据:', data)
|
||||
const response = await axiosInstance.put(`/tournament/update/${id}`, {
|
||||
name: data.name,
|
||||
format: data.format,
|
||||
organizer: data.organizer,
|
||||
qq_code: data.qq_code,
|
||||
start_time: data.start_time,
|
||||
end_time: data.end_time,
|
||||
status: data.status
|
||||
})
|
||||
return response.data
|
||||
const response = await axiosInstance.put(`/tournament/update/${id}`, tournamentData);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('更新赛事失败:', error)
|
||||
if (error.response) {
|
||||
console.error('错误详情:', {
|
||||
status: error.response.status,
|
||||
data: error.response.data,
|
||||
headers: error.response.headers,
|
||||
config: error.config
|
||||
})
|
||||
// 如果有详细的错误信息,抛出它
|
||||
if (error.response.data?.detail) {
|
||||
throw new Error(error.response.data.detail)
|
||||
}
|
||||
}
|
||||
throw error
|
||||
console.error('更新赛事失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 删除赛事
|
||||
/**
|
||||
* 删除赛事
|
||||
* @param {number} id - 赛事ID
|
||||
* @returns {Promise<Object>} 返回删除赛事的响应数据
|
||||
*/
|
||||
export const deleteTournament = async (id) => {
|
||||
try {
|
||||
const response = await axiosInstance.delete(`/tournament/delete/${id}`)
|
||||
return response.data
|
||||
const response = await axiosInstance.delete(`/tournament/delete/${id}`);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('删除赛事失败:', error)
|
||||
throw error
|
||||
console.error('删除赛事失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// 添加报名结果
|
||||
export const addSignUpResult = async (data) => {
|
||||
/**
|
||||
* 添加玩家报名
|
||||
* @param {Object} signupData - 报名数据
|
||||
* @param {number} signupData.id - 数据库中id
|
||||
* @param {string} signupData.type - 类型
|
||||
* @param {string} signupData.teamname - 队伍名称
|
||||
* @param {string} signupData.faction - 阵营(allied、soviet、empire、ob、voice、random)
|
||||
* @param {string} signupData.username - 用户名
|
||||
* @param {string} signupData.qq - QQ号
|
||||
* @returns {Promise<Object>} 返回添加报名的响应数据
|
||||
*/
|
||||
export const addSignUp = async (signupData) => {
|
||||
try {
|
||||
const response = await axiosInstance.post('/tournament/signup_result/add', {
|
||||
tournament_id: parseInt(data.tournament_id),
|
||||
tournament_name: data.tournament_name,
|
||||
team_name: data.team_name,
|
||||
sign_name: data.sign_name.trim(),
|
||||
win: '0',
|
||||
lose: '0',
|
||||
status: 'tie'
|
||||
})
|
||||
return response.data
|
||||
const response = await axiosInstance.post('/tournament/signup/add', signupData);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('请求错误:', error)
|
||||
if (error.response?.data?.detail) {
|
||||
throw new Error(error.response.data.detail)
|
||||
}
|
||||
throw error
|
||||
console.error('添加报名失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 获取参赛结果列表
|
||||
/**
|
||||
* 获取玩家报名列表
|
||||
* @returns {Promise<Array<Object>>} 返回报名列表数据
|
||||
*/
|
||||
export const getSignUpList = async () => {
|
||||
try {
|
||||
const response = await axiosInstance.get('/tournament/signup/getlist');
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('获取报名列表失败:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 更新玩家报名
|
||||
* @param {number} id - 报名ID
|
||||
* @param {Object} signupData - 报名数据
|
||||
* @param {number} signupData.id - 数据库中id
|
||||
* @param {string} signupData.type - 类型
|
||||
* @param {string} signupData.teamname - 队伍名称
|
||||
* @param {string} signupData.faction - 阵营(allied、soviet、empire、ob、voice、random)
|
||||
* @param {string} signupData.username - 用户名
|
||||
* @param {string} signupData.qq - QQ号
|
||||
* @returns {Promise<Object>} 返回更新报名的响应数据
|
||||
*/
|
||||
export const updateSignUp = async (id, signupData) => {
|
||||
try {
|
||||
const response = await axiosInstance.put(`/tournament/signup/update/${id}`, signupData);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('更新报名失败:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除玩家报名
|
||||
* @param {number} id - 报名ID
|
||||
* @returns {Promise<Object>} 返回删除报名的响应数据
|
||||
*/
|
||||
export const deleteSignUp = async (id) => {
|
||||
try {
|
||||
const response = await axiosInstance.delete(`/tournament/signup/delete/${id}`);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('删除报名失败:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 添加报名结果
|
||||
* @param {Object} resultData - 结果数据
|
||||
* @param {number} resultData.tournament_id - 赛事id(int)
|
||||
* @param {string} resultData.tournament_name - 赛事名称
|
||||
* @param {string} resultData.team_name - 队伍名称(可选)
|
||||
* @param {string} resultData.sign_name - 参赛人员名称
|
||||
* @param {string} resultData.win - 参赛人员胜利局数(str)
|
||||
* @param {string} resultData.lose - 参赛人员失败局数(str)
|
||||
* @param {string} resultData.status - 参赛人员对局状态(win,lose,tie)
|
||||
* @param {string} resultData.round - 轮数(str)
|
||||
* @param {string} resultData.rival_name - 对方name(str)
|
||||
* @returns {Promise<Object>} 返回添加报名结果的响应数据
|
||||
*/
|
||||
export const addSignUpResult = async (resultData) => {
|
||||
try {
|
||||
const response = await axiosInstance.post('/tournament/signup_result/add', resultData);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('添加报名结果失败:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取报名结果列表
|
||||
* @returns {Promise<Array<Object>>} 返回报名结果列表数据
|
||||
*/
|
||||
export const getSignUpResultList = async () => {
|
||||
try {
|
||||
const response = await axiosInstance.get('/tournament/signup_result/getlist')
|
||||
return response.data
|
||||
const response = await axiosInstance.get('/tournament/signup_result/getlist');
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('获取参赛结果列表失败:', {
|
||||
status: error.response?.status,
|
||||
data: error.response?.data,
|
||||
message: error.message
|
||||
})
|
||||
throw error
|
||||
console.error('获取报名结果列表失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 更新参赛结果
|
||||
export const updateSignUpResult = async (id, data) => {
|
||||
/**
|
||||
* 更新报名结果
|
||||
* @param {number} id - 结果ID
|
||||
* @param {Object} resultData - 结果数据
|
||||
* @param {number} resultData.tournament_id - 赛事id(int)
|
||||
* @param {string} resultData.tournament_name - 赛事名称
|
||||
* @param {string} resultData.team_name - 队伍名称(可选)
|
||||
* @param {string} resultData.sign_name - 参赛人员名称
|
||||
* @param {string} resultData.win - 参赛人员胜利局数(str)
|
||||
* @param {string} resultData.lose - 参赛人员失败局数(str)
|
||||
* @param {string} resultData.status - 参赛人员对局状态(win,lose,tie)
|
||||
* @param {string} resultData.round - 轮数(str)
|
||||
* @param {string} resultData.rival_name - 对方name(str)
|
||||
* @returns {Promise<Object>} 返回更新报名结果的响应数据
|
||||
*/
|
||||
export const updateSignUpResult = async (id, resultData) => {
|
||||
try {
|
||||
// // 更新报名信息 (这部分逻辑根据您的要求被注释掉)
|
||||
// console.log('更新报名信息...')
|
||||
// await axiosInstance.put(`/tournament/signup/update/${id}`, {
|
||||
// tournament_id: parseInt(data.tournament_id),
|
||||
// type: data.team_name ? 'teamname' : 'individual',
|
||||
// teamname: data.team_name || '',
|
||||
// faction: data.faction || 'random',
|
||||
// username: data.sign_name,
|
||||
// qq: data.qq || ''
|
||||
// })
|
||||
// console.log('报名信息更新成功')
|
||||
|
||||
// 更新报名结果
|
||||
console.log('更新报名结果...')
|
||||
await axiosInstance.put(`/tournament/signup_result/update/${id}`, {
|
||||
tournament_id: parseInt(data.tournament_id),
|
||||
tournament_name: data.tournament_name,
|
||||
team_name: data.team_name || null,
|
||||
sign_name: data.sign_name,
|
||||
win: data.win || '0',
|
||||
lose: data.lose || '0',
|
||||
status: data.status || 'tie'
|
||||
})
|
||||
console.log('报名结果更新成功')
|
||||
|
||||
return { success: true }
|
||||
const response = await axiosInstance.put(`/tournament/signup_result/update/${id}`, resultData);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('更新参赛结果失败:', {
|
||||
status: error.response?.status,
|
||||
data: error.response?.data,
|
||||
message: error.message
|
||||
})
|
||||
throw error
|
||||
console.error('更新报名结果失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 删除参赛选手
|
||||
/**
|
||||
* 删除报名结果
|
||||
* @param {number} id - 结果ID
|
||||
* @returns {Promise<Object>} 返回删除报名结果的响应数据
|
||||
*/
|
||||
export const deleteSignUpResult = async (id) => {
|
||||
try {
|
||||
// 删除报名结果
|
||||
console.log('删除报名结果...')
|
||||
await axiosInstance.delete(`/tournament/signup_result/delete/${id}`)
|
||||
console.log('报名结果删除成功')
|
||||
|
||||
// // 删除报名信息 (这部分逻辑根据您的要求被注释掉)
|
||||
// console.log('删除报名信息...')
|
||||
// await axiosInstance.delete(`/tournament/signup/delete/${id}`)
|
||||
// console.log('报名信息删除成功')
|
||||
|
||||
return { success: true }
|
||||
const response = await axiosInstance.delete(`/tournament/signup_result/delete/${id}`);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('删除参赛选手失败:', {
|
||||
status: error.response?.status,
|
||||
data: error.response?.data,
|
||||
message: error.message
|
||||
})
|
||||
throw error
|
||||
console.error('删除报名结果失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// 添加报名
|
||||
export const addSignUp = async (data) => {
|
||||
try {
|
||||
console.log('开始报名流程,数据:', data)
|
||||
|
||||
// 调用报名 API
|
||||
console.log('调用报名 API...')
|
||||
await axiosInstance.post('/tournament/signup/add', {
|
||||
tournament_id: data.id,
|
||||
type: data.type,
|
||||
teamname: data.team_name || '',
|
||||
faction: data.faction || 'random',
|
||||
username: data.sign_name,
|
||||
qq: data.qq || ''
|
||||
})
|
||||
console.log('报名 API 调用成功')
|
||||
|
||||
// 调用报名结果 API
|
||||
console.log('调用报名结果 API...')
|
||||
await axiosInstance.post('/tournament/signup_result/add', {
|
||||
tournament_id: data.id,
|
||||
tournament_name: data.tournament_name,
|
||||
team_name: data.team_name || null,
|
||||
sign_name: data.sign_name,
|
||||
win: '0',
|
||||
lose: '0',
|
||||
status: 'tie'
|
||||
})
|
||||
console.log('报名结果 API 调用成功')
|
||||
|
||||
return {
|
||||
signup: { success: true },
|
||||
result: { success: true }
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('报名请求错误:', {
|
||||
message: error.message,
|
||||
response: error.response?.data,
|
||||
status: error.response?.status,
|
||||
config: error.config
|
||||
})
|
||||
|
||||
// 如果是服务器返回的错误信息,直接使用
|
||||
if (error.response?.data?.detail) {
|
||||
throw new Error(error.response.data.detail)
|
||||
}
|
||||
// 其他错误,包装成更友好的错误信息
|
||||
throw new Error('报名失败,请检查网络连接后重试')
|
||||
}
|
||||
}
|
||||
};
|
BIN
src/assets/login_4.jpg
Normal file
BIN
src/assets/login_4.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 1024 KiB |
BIN
src/assets/login_5.jpg
Normal file
BIN
src/assets/login_5.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.3 MiB |
1263
src/components/DoubleEliminationBracket.vue
Normal file
1263
src/components/DoubleEliminationBracket.vue
Normal file
File diff suppressed because it is too large
Load Diff
@ -25,7 +25,7 @@ const props = defineProps({
|
||||
})
|
||||
const emit = defineEmits(['close', 'apply'])
|
||||
|
||||
function handleClose() {
|
||||
function handleClose() {
|
||||
emit('close')
|
||||
}
|
||||
function handleApply() {
|
||||
|
@ -12,7 +12,7 @@
|
||||
<div class="player-info">
|
||||
<div class="player-name">{{ item.username }}</div>
|
||||
<div class="player-faction">{{ item.faction }}</div>
|
||||
<div class="player-score">{{ item.score }}</div>
|
||||
<div class="player-score" :title="`积分: ${item.points}`">{{ item.score }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -31,7 +31,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { ref, onMounted, watch } from 'vue'
|
||||
import { getSignUpResultList } from '@/api/tournament'
|
||||
|
||||
const props = defineProps({
|
||||
@ -46,31 +46,77 @@ const rankData = ref([])
|
||||
const fetchRankData = async () => {
|
||||
try {
|
||||
const response = await getSignUpResultList()
|
||||
// 筛选当前赛事的玩家并按胜场数排序
|
||||
const results = response
|
||||
.filter(player => player.tournament_id === props.tournamentId)
|
||||
.map((player, index) => ({
|
||||
rank: index + 1,
|
||||
username: player.sign_name,
|
||||
faction: player.faction,
|
||||
win: parseInt(player.win) || 0,
|
||||
lose: parseInt(player.lose) || 0,
|
||||
score: `${player.win}胜${player.lose}负`
|
||||
}))
|
||||
.sort((a, b) => {
|
||||
// 首先按胜场数排序
|
||||
if (b.win !== a.win) {
|
||||
return b.win - a.win
|
||||
console.log('RankContestant 原始数据:', response)
|
||||
|
||||
// 筛选当前赛事的玩家
|
||||
const filteredPlayers = response.filter(player => player.tournament_id === props.tournamentId)
|
||||
console.log('RankContestant 筛选后数据:', filteredPlayers)
|
||||
|
||||
// 按玩家名称分组,计算总胜利数和总失败数
|
||||
const playerStats = {}
|
||||
|
||||
filteredPlayers.forEach((player, index) => {
|
||||
const playerName = player.sign_name
|
||||
const win = parseInt(player.win || 0)
|
||||
const lose = parseInt(player.lose || 0)
|
||||
const round = player.round || '0'
|
||||
|
||||
console.log(`RankContestant 处理第${index + 1}条数据:`, {
|
||||
id: player.id,
|
||||
sign_name: playerName,
|
||||
round: round,
|
||||
win: win,
|
||||
lose: lose,
|
||||
status: player.status
|
||||
})
|
||||
|
||||
if (!playerStats[playerName]) {
|
||||
playerStats[playerName] = {
|
||||
username: playerName,
|
||||
faction: player.faction || player.team_name || '',
|
||||
totalWin: 0,
|
||||
totalLose: 0,
|
||||
totalPoints: 0,
|
||||
rounds: []
|
||||
}
|
||||
// 如果胜场数相同,则按负场数排序(负场数少的排前面)
|
||||
return a.lose - b.lose
|
||||
}
|
||||
|
||||
playerStats[playerName].totalWin += win
|
||||
playerStats[playerName].totalLose += lose
|
||||
playerStats[playerName].rounds.push(round)
|
||||
|
||||
// 计算积分(胜场数)
|
||||
playerStats[playerName].totalPoints = playerStats[playerName].totalWin
|
||||
})
|
||||
|
||||
console.log('RankContestant 分组后统计:', playerStats)
|
||||
|
||||
// 转换为数组并按总胜利数排序
|
||||
const sortedPlayers = Object.values(playerStats)
|
||||
.sort((a, b) => {
|
||||
// 首先按总胜利数排序
|
||||
if (b.totalWin !== a.totalWin) {
|
||||
return b.totalWin - a.totalWin
|
||||
}
|
||||
// 如果胜利数相同,按失败数排序(失败数少的排名靠前)
|
||||
return a.totalLose - b.totalLose
|
||||
})
|
||||
.map((player, index) => ({
|
||||
...player,
|
||||
rank: index + 1
|
||||
}))
|
||||
id: index + 1, // 使用索引作为ID
|
||||
rank: (index + 1).toString(),
|
||||
username: player.username,
|
||||
faction: player.faction,
|
||||
win: player.totalWin,
|
||||
lose: player.totalLose,
|
||||
points: player.totalPoints,
|
||||
status: '',
|
||||
bracket_type: 'winners',
|
||||
score: `${player.totalWin}胜${player.totalLose}负 (${player.totalPoints.toFixed(1)}分)`,
|
||||
rounds: player.rounds.join(',')
|
||||
}))
|
||||
|
||||
rankData.value = results
|
||||
console.log('RankContestant 最终排名结果:', sortedPlayers)
|
||||
rankData.value = sortedPlayers
|
||||
} catch (error) {
|
||||
console.error('获取排名数据失败:', error)
|
||||
}
|
||||
@ -79,6 +125,13 @@ const fetchRankData = async () => {
|
||||
onMounted(() => {
|
||||
fetchRankData()
|
||||
})
|
||||
|
||||
// 当比赛ID变化时重新获取数据
|
||||
watch(() => props.tournamentId, () => {
|
||||
if (props.tournamentId) {
|
||||
fetchRankData()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
File diff suppressed because it is too large
Load Diff
112
src/components/backend/AdminChangesPwd.vue
Normal file
112
src/components/backend/AdminChangesPwd.vue
Normal file
@ -0,0 +1,112 @@
|
||||
<template>
|
||||
<div class="temp-privilege-form">
|
||||
<h2>管理员使用qq发送修改密码邮件</h2>
|
||||
<form @submit.prevent="handleSubmit">
|
||||
<div class="form-group">
|
||||
<label for="uuid">用户qq:</label>
|
||||
<input id="uuid" v-model="qq_code" type="text" placeholder="请输入用户qq" required />
|
||||
</div>
|
||||
<button class="submit-btn" type="submit" :disabled="loading">提交</button>
|
||||
</form>
|
||||
<div v-if="msg" :class="{'success-msg': msgType==='success', 'error-msg': msgType==='error'}">{{ msg }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import {getUserByInfo,requestResetPassword} from '@/api/login'
|
||||
const qq_code = ref('')
|
||||
const loading = ref(false)
|
||||
const msg = ref('')
|
||||
const msgType = ref('')
|
||||
|
||||
|
||||
async function handleSubmit() {
|
||||
msg.value = ''
|
||||
msgType.value = ''
|
||||
loading.value = true
|
||||
try {
|
||||
//api
|
||||
const user = await getUserByInfo({qq_code:qq_code.value})
|
||||
console.log(user)
|
||||
await requestResetPassword(user.uuid)
|
||||
msg.value = '发送成功!'
|
||||
msgType.value = 'success'
|
||||
qq_code.value = ''
|
||||
|
||||
} catch (e) {
|
||||
msg.value = '发送失败请稍后再试'
|
||||
msgType.value = 'error'
|
||||
}
|
||||
loading.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.temp-privilege-form {
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 32px 24px 24px 24px;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.06);
|
||||
max-width: 420px;
|
||||
margin: 40px auto 0 auto;
|
||||
}
|
||||
.temp-privilege-form h2 {
|
||||
margin-bottom: 24px;
|
||||
color: #2563eb;
|
||||
text-align: center;
|
||||
}
|
||||
.form-group {
|
||||
margin-bottom: 18px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.form-group label {
|
||||
margin-bottom: 6px;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
.form-group input,
|
||||
.form-group select {
|
||||
padding: 8px 10px;
|
||||
border: 1px solid #d1d5db;
|
||||
border-radius: 4px;
|
||||
font-size: 15px;
|
||||
outline: none;
|
||||
transition: border 0.2s;
|
||||
}
|
||||
.form-group input:focus,
|
||||
.form-group select:focus {
|
||||
border-color: #2563eb;
|
||||
}
|
||||
.custom-exp-input {
|
||||
margin-top: 8px;
|
||||
}
|
||||
.submit-btn {
|
||||
width: 100%;
|
||||
background: #2563eb;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 10px 0;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
.submit-btn:disabled {
|
||||
background: #a5b4fc;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.success-msg {
|
||||
color: #22c55e;
|
||||
text-align: center;
|
||||
margin-top: 18px;
|
||||
}
|
||||
.error-msg {
|
||||
color: #ef4444;
|
||||
text-align: center;
|
||||
margin-top: 18px;
|
||||
}
|
||||
</style>
|
@ -86,8 +86,10 @@
|
||||
<span class="error-message" v-if="usernameError">{{ usernameError }}</span>
|
||||
</div>
|
||||
<div class="login-button">
|
||||
<button type="submit" :disabled="isSubmitting">
|
||||
{{ isSubmitting ? '提交中...' : '重置密码' }}
|
||||
<button type="submit" :disabled="isSubmitting || cooldown > 0">
|
||||
<template v-if="isSubmitting">提交中...</template>
|
||||
<template v-else-if="cooldown > 0">请稍候 ({{ cooldown }}s)</template>
|
||||
<template v-else>重置密码</template>
|
||||
</button>
|
||||
</div>
|
||||
<div class="register-link">
|
||||
@ -111,7 +113,7 @@
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import {getCaptcha, forgetPassword, requestResetPassword, getUserByInfo} from '../api/login'
|
||||
import {getCaptcha, requestResetPassword, getUserByInfo} from '../api/login'
|
||||
import ErrorDialog from './ErrorDialog.vue'
|
||||
import SuccessDialog from './SuccessDialog.vue'
|
||||
|
||||
@ -132,6 +134,8 @@ const uuidError = ref('')
|
||||
|
||||
// 状态
|
||||
const isSubmitting = ref(false)
|
||||
const cooldown = ref(0)
|
||||
let cooldownTimer = null
|
||||
|
||||
// 错误弹窗相关
|
||||
const showError = ref(false)
|
||||
@ -218,17 +222,23 @@ const uuid_handleForgetPassword = async () => {
|
||||
if (!uuid_validateForm()) {
|
||||
return
|
||||
}
|
||||
//console.log(uuid.value);
|
||||
const user = await getUserByInfo({qq_code:username.value})
|
||||
console.log(user)
|
||||
await requestResetPassword(user.uuid)
|
||||
isSubmitting.value = true
|
||||
cooldown.value = 60
|
||||
cooldownTimer = setInterval(() => {
|
||||
if (cooldown.value > 0) {
|
||||
cooldown.value--
|
||||
} else {
|
||||
clearInterval(cooldownTimer)
|
||||
cooldownTimer = null
|
||||
}
|
||||
}, 1000)
|
||||
}catch ( error){
|
||||
showErrorMessage(error.message || '不是正确的uuid')
|
||||
}finally {
|
||||
isSubmitting.value = false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -16,7 +16,8 @@ const routes = [
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
redirect: '/maps'
|
||||
name: 'Home',
|
||||
component: () => import('@/views/index/Home.vue')
|
||||
},
|
||||
{
|
||||
path: 'demands',
|
||||
@ -48,33 +49,47 @@ const routes = [
|
||||
path: 'weapon-match',
|
||||
name: 'WeaponMatch',
|
||||
component: () => import('@/views/weapon/WeaponMatch.vue'),
|
||||
meta: { requiresAuth: true, requiredPrivilege: ['lv-admin','lv-mod'] }
|
||||
// meta: { requiresAuth: true, requiredPrivilege: ['lv-admin','lv-mod'] }
|
||||
meta: { requiresAuth: true}
|
||||
},
|
||||
{
|
||||
path: 'configEditor',
|
||||
name: 'ConfigEditor',
|
||||
component: () => import('@/views/index/ConfigEditor.vue'),
|
||||
// meta: { requiresAuth: true, requiredPrivilege: ['lv-admin','lv-mod'] }
|
||||
meta: { requiresAuth: true}
|
||||
},
|
||||
{
|
||||
path: 'competition',
|
||||
name: 'Competition',
|
||||
component: () => import('@/views/index/Competition.vue'),
|
||||
component: () => import('@/views/competition/Competition.vue'),
|
||||
// meta: { requiresAuth: true, requiredPrivilege: ['lv-admin','lv-competitor'] }
|
||||
meta: { requiresAuth: true}
|
||||
},
|
||||
{
|
||||
path: 'competition/add',
|
||||
name: 'AddCompetition',
|
||||
component: () => import('@/views/index/AddContestant.vue'),
|
||||
component: () => import('@/views/competition/AddContestant.vue'),
|
||||
meta: { requiresAuth: true }
|
||||
},
|
||||
{
|
||||
path: 'competition/detail',
|
||||
name: 'CompetitionDetail',
|
||||
component: () => import('@/views/index/CompetitionDetail.vue'),
|
||||
component: () => import('@/views/competition/CompetitionDetail.vue'),
|
||||
meta: { requiresAuth: true }
|
||||
},
|
||||
{
|
||||
path: 'competition/signup',
|
||||
name: 'CompetitionSignUp',
|
||||
component: () => import('@/views/index/CompetitionSignUp.vue'),
|
||||
component: () => import('@/views/competition/CompetitionSignUp.vue'),
|
||||
meta: { requiresAuth: true }
|
||||
},
|
||||
// {
|
||||
// path: 'competition',
|
||||
// name: 'Competition',
|
||||
// component: () => import('@/views/competition/Competition.vue'),
|
||||
// meta: { requiresAuth: true}
|
||||
// },
|
||||
{
|
||||
path: 'editors-maps',
|
||||
name: 'EditorsMaps',
|
||||
@ -89,13 +104,13 @@ const routes = [
|
||||
path: 'PIC2TGA',
|
||||
name: 'PIC2TGA',
|
||||
component: () => import('@/views/index/PIC2TGA.vue'),
|
||||
meta: { requiredPrivilege: ['lv-admin','lv-mod','lv-map','lv-competitor'] }
|
||||
// meta: { requiredPrivilege: ['lv-admin','lv-mod','lv-map','lv-competitor'] }
|
||||
},
|
||||
{
|
||||
path: 'terrainGenerate',
|
||||
name: 'TerrainGenerate',
|
||||
component: () => import('@/views/index/TerrainGenerate.vue'),
|
||||
meta: { requiredPrivilege: ['lv-admin','lv-mod','lv-map','lv-competitor'] }
|
||||
// meta: { requiredPrivilege: ['lv-admin','lv-mod','lv-map','lv-competitor'] }
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -163,7 +178,7 @@ router.beforeEach(async (to, from, next) => {
|
||||
// 登录校验:如果页面需要登录但本地没有有效 token,则跳转登录页
|
||||
if (requiresAuth && !hasValidToken()) {
|
||||
return next({
|
||||
path: '/maps',
|
||||
path: '/',
|
||||
query: { redirect: to.fullPath }
|
||||
})
|
||||
}
|
||||
@ -174,7 +189,7 @@ router.beforeEach(async (to, from, next) => {
|
||||
if (!user || !hasPrivilegeWithTemp(user, requiredPrivilege)) {
|
||||
// 通过事件总线通知弹窗
|
||||
eventBus.emit('no-privilege')
|
||||
return next({ path: '/maps', replace: true })
|
||||
return next({ path: '/', replace: true })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -98,7 +98,6 @@ export const loginSuccess = (accessToken, userId) => {
|
||||
if (userId) {
|
||||
localStorage.setItem('user_id', userId);
|
||||
}
|
||||
|
||||
// 设置登录标志
|
||||
justLoggedIn.value = true;
|
||||
|
||||
|
@ -55,9 +55,9 @@
|
||||
{{ loading ? '重置中...' : '重置密码' }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="back-link">
|
||||
<a @click.prevent="goToHome">返回首页</a>
|
||||
</div>
|
||||
<!-- <div class="back-link">-->
|
||||
<!-- <a @click.prevent="goToHome">返回登录页</a>-->
|
||||
<!-- </div>-->
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@ -86,11 +86,14 @@ import SuccessDialog from '@/components/SuccessDialog.vue'
|
||||
import loginBg from '@/assets/login_1.jpg'
|
||||
import loginBg1 from '@/assets/login_2.jpg'
|
||||
import loginBg3 from '@/assets/login_3.jpg'
|
||||
import loginBg4 from '@/assets/login_4.jpg'
|
||||
import loginBg5 from '@/assets/login_5.jpg'
|
||||
import {hasValidToken, logoutUser} from "@/utils/jwt.js";
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
const images = [loginBg, loginBg1, loginBg3]
|
||||
const images = [loginBg, loginBg1, loginBg3,loginBg4, loginBg5]
|
||||
const randomIndex = Math.floor(Math.random() * images.length)
|
||||
const bgImg = ref(images[randomIndex])
|
||||
|
||||
@ -149,7 +152,17 @@ const handleResetPassword = async () => {
|
||||
// 调用重置密码API
|
||||
await resetPassword(token, newPassword.value)
|
||||
|
||||
showSuccessMessage('密码重置成功!请使用新密码登录。')
|
||||
// 密码重置成功后显示提示并跳转到登录页
|
||||
showSuccessMessage('密码重置成功!')
|
||||
|
||||
// 延迟跳转,让用户看到成功提示
|
||||
setTimeout(() => {
|
||||
if (hasValidToken()) {
|
||||
logoutUser()
|
||||
}
|
||||
router.push('/backend/login')
|
||||
}, 1500)
|
||||
|
||||
} catch (error) {
|
||||
console.error('重置密码失败:', error)
|
||||
const errorMessage = error.response?.data?.detail || error.response?.data?.message || error.message || '重置密码失败,请重试'
|
||||
@ -165,7 +178,11 @@ const handleSuccessClose = () => {
|
||||
}
|
||||
|
||||
const goToHome = () => {
|
||||
router.push('/')
|
||||
if (hasValidToken()) {
|
||||
logoutUser()
|
||||
}
|
||||
router.push('/backend/login')
|
||||
// router.push('/')
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
@ -27,6 +27,9 @@
|
||||
<li :class="{active: currentAdminView === 'admin-edit-user-privilege'}">
|
||||
<a @click="selectAdminView('admin-edit-user-privilege')">管理员修改用户权限</a>
|
||||
</li>
|
||||
<li :class="{active: currentAdminView === 'admin-edit-user-password'}">
|
||||
<a @click="selectAdminView('admin-edit-user-password')">管理员把你密码扬了</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
@ -71,6 +74,7 @@
|
||||
<AdminEditUserPrivilege v-if="currentAdminView === 'admin-edit-user-privilege'" />
|
||||
<AffairManagement v-if="currentAdminView === 'affair-management'" />
|
||||
<TempPrivilegeReview v-if="currentAdminView === 'permission-review'" />
|
||||
<AdminChangesPwd v-if="currentAdminView === 'admin-edit-user-password'" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -83,6 +87,7 @@ import { getUserInfo } from '@/utils/jwt'
|
||||
import AdminEditUserPrivilege from '@/components/backend/AdminEditUserPrivilege.vue'
|
||||
import AffairManagement from '@/components/backend/AffairManagement.vue'
|
||||
import TempPrivilegeReview from '@/components/backend/TempPrivilegeReview.vue'
|
||||
import AdminChangesPwd from '@/components/backend/AdminChangesPwd.vue'
|
||||
|
||||
const router = useRouter()
|
||||
const hasToken = ref(false)
|
||||
|
@ -37,11 +37,13 @@ import { hasValidToken } from '@/utils/jwt'
|
||||
import loginBg from '@/assets/login_1.jpg'
|
||||
import loginBg1 from '@/assets/login_2.jpg'
|
||||
import loginBg3 from '@/assets/login_3.jpg'
|
||||
import loginBg4 from '@/assets/login_4.jpg'
|
||||
import loginBg5 from '@/assets/login_5.jpg'
|
||||
import ForgetModule from "@/components/forget_module.vue";
|
||||
import LoginModule from '@/components/login_module.vue'
|
||||
import RegisterModule from '@/components/register_module.vue'
|
||||
|
||||
const images = [loginBg, loginBg1,loginBg3]
|
||||
const images = [loginBg, loginBg1,loginBg3, loginBg4,loginBg5]
|
||||
const randomIndex = Math.floor(Math.random() * images.length)
|
||||
const bgImg = ref(images[randomIndex])
|
||||
|
||||
|
@ -3,61 +3,62 @@
|
||||
<div class="page-header">
|
||||
<h1>添加新赛事</h1>
|
||||
<div class="header-actions">
|
||||
<button class="btn-excel" @click="handleExcelImport">
|
||||
<i class="fas fa-file-excel"></i>
|
||||
通过表格添加
|
||||
</button>
|
||||
<!-- <button class="btn-excel" @click="handleExcelImport">-->
|
||||
<!-- <i class="fas fa-file-excel"></i>-->
|
||||
<!-- 通过表格添加-->
|
||||
<!-- </button>-->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Excel导入弹窗 -->
|
||||
<div v-if="showExcelDialog" class="excel-dialog-overlay">
|
||||
<div class="excel-dialog">
|
||||
<h3>通过Excel导入赛事信息</h3>
|
||||
<div class="excel-upload-area" @click="triggerFileInput" @dragover.prevent @drop.prevent="handleFileDrop"
|
||||
:class="{ 'is-dragover': isDragover }">
|
||||
<input type="file" ref="fileInput" accept=".xlsx" @change="handleFileSelect" style="display: none">
|
||||
<div class="upload-content">
|
||||
<i class="fas fa-file-excel"></i>
|
||||
<p>点击或拖拽Excel文件到此处</p>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="eventPreviewData.length > 0" class="preview-table">
|
||||
<h4>赛事信息表预览</h4>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th v-for="h in eventPreviewHeaders" :key="h">{{ h }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(item, index) in eventPreviewData" :key="index">
|
||||
<td v-for="h in eventPreviewHeaders" :key="h">{{ item[h] }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div v-if="playerPreviewData.length > 0" class="preview-table">
|
||||
<h4>选手报名表预览</h4>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th v-for="h in playerPreviewHeaders" :key="h">{{ h }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(item, index) in playerPreviewData" :key="index">
|
||||
<td v-for="h in playerPreviewHeaders" :key="h">{{ item[h] }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="dialog-actions">
|
||||
<button class="confirm-btn" @click="confirmImport" :disabled="eventPreviewData.length === 0">确认导入</button>
|
||||
<button class="cancel-btn" @click="closeExcelDialog">取消</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- <!– Excel导入弹窗 –>-->
|
||||
<!-- <div v-if="showExcelDialog" class="excel-dialog-overlay">-->
|
||||
<!-- <div class="excel-dialog">-->
|
||||
<!-- <h3>通过Excel导入赛事信息</h3>-->
|
||||
<!-- <div class="excel-upload-area" @click="triggerFileInput" @dragover.prevent @drop.prevent="handleFileDrop"-->
|
||||
<!-- :class="{ 'is-dragover': isDragover }">-->
|
||||
<!-- <input type="file" ref="fileInput" accept=".xlsx" @change="handleFileSelect" style="display: none">-->
|
||||
<!-- <div class="upload-content">-->
|
||||
<!-- <i class="fas fa-file-excel"></i>-->
|
||||
<!-- <p>点击或拖拽Excel文件到此处</p>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div v-if="eventPreviewData.length > 0" class="preview-table">-->
|
||||
<!-- <h4>赛事信息表预览</h4>-->
|
||||
<!-- <table>-->
|
||||
<!-- <thead>-->
|
||||
<!-- <tr>-->
|
||||
<!-- <th v-for="h in eventPreviewHeaders" :key="h">{{ h }}</th>-->
|
||||
<!-- </tr>-->
|
||||
<!-- </thead>-->
|
||||
<!-- <tbody>-->
|
||||
<!-- <tr v-for="(item, index) in eventPreviewData" :key="index">-->
|
||||
<!-- <td v-for="h in eventPreviewHeaders" :key="h">{{ item[h] }}</td>-->
|
||||
<!-- </tr>-->
|
||||
<!-- </tbody>-->
|
||||
<!-- </table>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div v-if="playerPreviewData.length > 0" class="preview-table">-->
|
||||
<!-- <h4>选手报名表预览</h4>-->
|
||||
<!-- <table>-->
|
||||
<!-- <thead>-->
|
||||
<!-- <tr>-->
|
||||
<!-- <th v-for="h in playerPreviewHeaders" :key="h">{{ h }}</th>-->
|
||||
<!-- </tr>-->
|
||||
<!-- </thead>-->
|
||||
<!-- <tbody>-->
|
||||
<!-- <tr v-for="(item, index) in playerPreviewData" :key="index">-->
|
||||
<!-- <td v-for="h in playerPreviewHeaders" :key="h">{{ item[h] }}</td>-->
|
||||
<!-- </tr>-->
|
||||
<!-- </tbody>-->
|
||||
<!-- </table>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="dialog-actions">-->
|
||||
<!-- <button class="confirm-btn" @click="confirmImport" :disabled="eventPreviewData.length === 0">确认导入</button>-->
|
||||
<!-- <button class="cancel-btn" @click="closeExcelDialog">取消</button>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
|
||||
<form @submit.prevent="handleSubmit" class="contest-form">
|
||||
<div class="form-group">
|
||||
@ -100,14 +101,28 @@
|
||||
<button type="button" class="cancel-btn" @click="handleCancel">取消</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<!-- 对话框组件 -->
|
||||
<SuccessDialog
|
||||
:visible="successDialog.visible"
|
||||
:message="successDialog.message"
|
||||
@close="successDialog.visible = false"
|
||||
/>
|
||||
<ErrorDialog
|
||||
:visible="errorDialog.visible"
|
||||
:message="errorDialog.message"
|
||||
@close="errorDialog.visible = false"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { addTournament, addSignUp, getTournamentList } from '@/api/tournament'
|
||||
import { addTournament, addSignUp, getTournamentList } from '@/api/tournament.js'
|
||||
import * as XLSX from 'xlsx'
|
||||
import SuccessDialog from '@/components/SuccessDialog.vue'
|
||||
import ErrorDialog from '@/components/ErrorDialog.vue'
|
||||
|
||||
const router = useRouter()
|
||||
const showExcelDialog = ref(false)
|
||||
@ -129,6 +144,10 @@ const formData = ref({
|
||||
description: ''
|
||||
})
|
||||
|
||||
// Dialog state
|
||||
const successDialog = ref({ visible: false, message: '' })
|
||||
const errorDialog = ref({ visible: false, message: '' })
|
||||
|
||||
// Excel赛事信息字段与表单字段的映射
|
||||
const excelFieldMap = {
|
||||
'赛事名称': 'name',
|
||||
@ -190,11 +209,11 @@ const handleSubmit = async () => {
|
||||
// 调用API添加赛事
|
||||
await addTournament(tournamentData)
|
||||
|
||||
alert('添加赛事成功!')
|
||||
successDialog.value = { visible: true, message: '添加赛事成功!' }
|
||||
router.push('/competition')
|
||||
} catch (error) {
|
||||
console.error('提交失败:', error)
|
||||
alert(error.response?.data?.message || '添加赛事失败,请重试')
|
||||
errorDialog.value = { visible: true, message: error.response?.data?.message || '添加赛事失败,请重试' }
|
||||
}
|
||||
}
|
||||
|
||||
@ -310,7 +329,7 @@ const processExcelFile = (file) => {
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
alert('Excel文件格式错误,请检查文件内容')
|
||||
errorDialog.value = { visible: true, message: 'Excel文件格式错误,请检查文件内容' }
|
||||
}
|
||||
}
|
||||
reader.readAsArrayBuffer(file)
|
||||
@ -340,7 +359,7 @@ const confirmImport = async () => {
|
||||
|
||||
try {
|
||||
await addTournament(tournamentData)
|
||||
alert('赛事导入成功!')
|
||||
successDialog.value = { visible: true, message: '赛事导入成功!' }
|
||||
|
||||
const tournamentList = await getTournamentList()
|
||||
console.log('获取到的赛事列表:', tournamentList)
|
||||
@ -377,16 +396,16 @@ const confirmImport = async () => {
|
||||
await addSignUp(signUpData)
|
||||
} catch (error) {
|
||||
console.error('报名失败:', error)
|
||||
alert(`报名失败: ${row['队伍名称或者个人参赛名称']},${error.message}`)
|
||||
errorDialog.value = { visible: true, message: `报名失败: ${row['队伍名称或者个人参赛名称']},${error.message}` }
|
||||
}
|
||||
}
|
||||
alert('选手报名表导入完成!')
|
||||
successDialog.value = { visible: true, message: '选手报名表导入完成!' }
|
||||
}
|
||||
|
||||
router.push('/competition')
|
||||
} catch (error) {
|
||||
console.error('导入失败:', error)
|
||||
alert(error.response?.data?.message || '赛事导入失败,请重试')
|
||||
errorDialog.value = { visible: true, message: error.response?.data?.message || '赛事导入失败,请重试' }
|
||||
}
|
||||
closeExcelDialog()
|
||||
}
|
@ -110,7 +110,7 @@
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { getTournamentList } from '@/api/tournament'
|
||||
import { getTournamentList } from '@/api/tournament.js'
|
||||
|
||||
const router = useRouter()
|
||||
|
@ -24,14 +24,14 @@
|
||||
{{ statusMap[competition.status] }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="edit-controls" v-if="isOrganizer">
|
||||
<!-- <div class="edit-controls" v-if="isOrganizer">
|
||||
<button class="edit-mode-btn" @click="toggleEditMode">
|
||||
{{ isEditMode ? '退出编辑' : '编辑对阵图' }}
|
||||
</button>
|
||||
<button v-if="isEditMode" class="save-btn" @click="saveChanges" :disabled="isSaving">
|
||||
{{ isSaving ? '保存中...' : '保存修改' }}
|
||||
</button>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
|
||||
<!-- 报名玩家列表 -->
|
||||
@ -46,6 +46,8 @@
|
||||
<tr>
|
||||
<th>玩家名称</th>
|
||||
<th>队伍名称</th>
|
||||
<th>阵营</th>
|
||||
<th>QQ</th>
|
||||
<th>胜场</th>
|
||||
<th>负场</th>
|
||||
<th>状态</th>
|
||||
@ -55,12 +57,14 @@
|
||||
<tbody>
|
||||
<tr v-for="player in registeredPlayers" :key="player.id">
|
||||
<td>{{ player.sign_name }}</td>
|
||||
<td>{{ player.team_name || '个人' }}</td>
|
||||
<td>{{ player.team_name === ' ' ? '个人' : player.team_name}}</td>
|
||||
<td>{{ formatFaction(player.faction) }}</td>
|
||||
<td>{{ player.qq || '-' }}</td>
|
||||
<td>{{ player.win }}</td>
|
||||
<td>{{ player.lose }}</td>
|
||||
<td>{{ player.status }}</td>
|
||||
<td class="action-buttons">
|
||||
<button class="edit-player-btn" @click="handleEditPlayer(player)" >修改</button>
|
||||
<!-- <button class="edit-player-btn" @click="handleEditPlayer(player)" >修改</button>-->
|
||||
<button class="remove-btn" @click="handleRemovePlayer(player.id)">移除</button>
|
||||
</td>
|
||||
</tr>
|
||||
@ -75,10 +79,13 @@
|
||||
v-if="competition.status === 'finish'"
|
||||
:tournament-id="parseInt(route.query.id)"
|
||||
/>
|
||||
<DoubleEliminationBracket
|
||||
v-if="competition.format === '双败淘汰' && competition.status === 'starting'"
|
||||
:tournament-id="parseInt(route.query.id)"
|
||||
/>
|
||||
<tournament-bracket
|
||||
v-if="competition.status === 'starting'"
|
||||
v-else-if="competition.status === 'starting'"
|
||||
:tournament-id="parseInt(route.query.id)"
|
||||
@refreshPlayers="fetchRegisteredPlayers"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@ -209,6 +216,18 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 对话框组件 -->
|
||||
<SuccessDialog
|
||||
:visible="successDialog.visible"
|
||||
:message="successDialog.message"
|
||||
@close="successDialog.visible = false"
|
||||
/>
|
||||
<ErrorDialog
|
||||
:visible="errorDialog.visible"
|
||||
:message="errorDialog.message"
|
||||
@close="errorDialog.visible = false"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -217,15 +236,20 @@ import { ref, onMounted, computed } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import TournamentBracket from '@/components/TournamentBracket.vue'
|
||||
import RankContestant from '@/components/RankContestant.vue'
|
||||
import DoubleEliminationBracket from '@/components/DoubleEliminationBracket.vue'
|
||||
import {
|
||||
getTournamentList,
|
||||
updateTournament,
|
||||
deleteTournament,
|
||||
getSignUpList,
|
||||
getSignUpResultList,
|
||||
updateSignUpResult,
|
||||
deleteSignUpResult
|
||||
} from '@/api/tournament'
|
||||
import { getStoredUser } from '@/utils/jwt'
|
||||
deleteSignUpResult,
|
||||
deleteSignUp
|
||||
} from '@/api/tournament.js'
|
||||
import { getStoredUser } from '@/utils/jwt.js'
|
||||
import SuccessDialog from '@/components/SuccessDialog.vue'
|
||||
import ErrorDialog from '@/components/ErrorDialog.vue'
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
@ -243,15 +267,6 @@ const statusMap = {
|
||||
'finish': '已结束'
|
||||
}
|
||||
|
||||
// const factionMap = {
|
||||
// 'allied': '盟军',
|
||||
// 'soviet': '苏联',
|
||||
// 'empire': '帝国',
|
||||
// 'ob': 'OB',
|
||||
// 'voice': '解说',
|
||||
// 'random': '随机'
|
||||
// }
|
||||
|
||||
// 状态管理
|
||||
const competition = ref({
|
||||
name: '',
|
||||
@ -290,6 +305,10 @@ const playerEditForm = ref({ // 玩家编辑表单
|
||||
tournament_id: ''
|
||||
})
|
||||
|
||||
// Dialog state
|
||||
const successDialog = ref({ visible: false, message: '' })
|
||||
const errorDialog = ref({ visible: false, message: '' })
|
||||
|
||||
// 格式化日期
|
||||
const formatDate = (date) => {
|
||||
if (!date) return ''
|
||||
@ -301,10 +320,18 @@ const formatType = (type) => {
|
||||
return formatMap[type] || type
|
||||
}
|
||||
|
||||
// // 格式化阵营
|
||||
// const formatFaction = (faction) => {
|
||||
// return factionMap[faction] || faction
|
||||
// }
|
||||
// 格式化阵营
|
||||
const formatFaction = (faction) => {
|
||||
const factionMap = {
|
||||
'allied': '盟军',
|
||||
'soviet': '苏联',
|
||||
'empire': '帝国',
|
||||
'ob': 'OB',
|
||||
'voice': '解说',
|
||||
'random': '随机'
|
||||
}
|
||||
return factionMap[faction] || faction
|
||||
}
|
||||
|
||||
// 返回列表
|
||||
const handleBack = () => {
|
||||
@ -374,12 +401,23 @@ const handleUpdate = async () => {
|
||||
}
|
||||
|
||||
await updateTournament(tournamentId, updateData)
|
||||
alert('更新成功!')
|
||||
|
||||
// 如果赛事名称发生变化,更新所有玩家的tournament_name
|
||||
if (editForm.value.name !== competition.value.name) {
|
||||
await updateAllPlayersTournamentName(tournamentId, editForm.value.name)
|
||||
}
|
||||
|
||||
// 如果状态变为"进行中",为所有玩家生成对手
|
||||
if (selectedStatus.value === 'starting') {
|
||||
await generateOpponentsForAllPlayers(tournamentId)
|
||||
}
|
||||
|
||||
successDialog.value = { visible: true, message: '更新成功!' }
|
||||
closeEditDialog()
|
||||
fetchTournamentDetail() // 刷新数据
|
||||
} catch (error) {
|
||||
console.error('更新失败:', error)
|
||||
alert(error.response?.data?.message || '更新失败,请重试')
|
||||
errorDialog.value = { visible: true, message: error.response?.data?.message || '更新失败,请重试' }
|
||||
} finally {
|
||||
isUpdating.value = false
|
||||
}
|
||||
@ -389,13 +427,52 @@ const handleUpdate = async () => {
|
||||
const handleDelete = async () => {
|
||||
try {
|
||||
isDeleting.value = true
|
||||
const tournamentId = route.query.id
|
||||
const tournamentId = parseInt(route.query.id)
|
||||
|
||||
// 先获取该赛事的所有报名玩家
|
||||
const [signupList, resultList] = await Promise.all([
|
||||
getSignUpList(),
|
||||
getSignUpResultList()
|
||||
])
|
||||
|
||||
// 筛选当前赛事的报名信息
|
||||
const tournamentSignups = signupList.filter(signup =>
|
||||
signup.tournament_id === tournamentId
|
||||
)
|
||||
|
||||
// 筛选当前赛事的结果信息
|
||||
const tournamentResults = resultList.filter(result =>
|
||||
result.tournament_id === tournamentId
|
||||
)
|
||||
|
||||
console.log(`准备删除赛事 ${tournamentId},共有 ${tournamentSignups.length} 个报名记录,${tournamentResults.length} 个结果记录`)
|
||||
|
||||
// 删除所有报名玩家
|
||||
const deletePromises = []
|
||||
|
||||
// 删除所有报名信息
|
||||
for (const signup of tournamentSignups) {
|
||||
deletePromises.push(deleteSignUp(signup.id))
|
||||
}
|
||||
|
||||
// 删除所有报名结果
|
||||
for (const result of tournamentResults) {
|
||||
deletePromises.push(deleteSignUpResult(result.id))
|
||||
}
|
||||
|
||||
// 等待所有玩家删除操作完成
|
||||
if (deletePromises.length > 0) {
|
||||
await Promise.all(deletePromises)
|
||||
console.log('所有报名玩家删除完成')
|
||||
}
|
||||
|
||||
// 最后删除赛事
|
||||
await deleteTournament(tournamentId)
|
||||
alert('删除成功!')
|
||||
successDialog.value = { visible: true, message: '删除成功!' }
|
||||
router.push('/competition')
|
||||
} catch (error) {
|
||||
console.error('删除失败:', error)
|
||||
alert(error.response?.data?.message || '删除失败,请重试')
|
||||
errorDialog.value = { visible: true, message: error.response?.data?.message || '删除失败,请重试' }
|
||||
} finally {
|
||||
isDeleting.value = false
|
||||
showDeleteConfirm.value = false
|
||||
@ -444,12 +521,12 @@ const fetchTournamentDetail = async () => {
|
||||
}
|
||||
} else {
|
||||
console.log('未找到赛事')
|
||||
alert('未找到赛事信息')
|
||||
errorDialog.value = { visible: true, message: '未找到赛事信息' }
|
||||
router.push('/competition')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取赛事详情失败:', error)
|
||||
alert('获取赛事详情失败,请重试')
|
||||
errorDialog.value = { visible: true, message: '获取赛事详情失败,请重试' }
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
@ -459,15 +536,42 @@ const fetchTournamentDetail = async () => {
|
||||
const fetchRegisteredPlayers = async () => {
|
||||
try {
|
||||
const tournamentId = parseInt(route.query.id)
|
||||
const response = await getSignUpResultList()
|
||||
// 调试日志
|
||||
console.log('报名玩家原始数据:', response)
|
||||
|
||||
// 同时获取报名信息和结果信息
|
||||
const [signupList, resultList] = await Promise.all([
|
||||
getSignUpList(),
|
||||
getSignUpResultList()
|
||||
])
|
||||
|
||||
console.log('报名信息原始数据:', signupList)
|
||||
console.log('结果信息原始数据:', resultList)
|
||||
console.log('当前赛事ID:', tournamentId)
|
||||
// 只保留 tournament_id 等于当前赛事 id 的玩家
|
||||
registeredPlayers.value = response.filter(player =>
|
||||
player.tournament_id === tournamentId
|
||||
|
||||
// 筛选当前赛事的报名信息
|
||||
const tournamentSignups = signupList.filter(signup =>
|
||||
signup.tournament_id === tournamentId
|
||||
)
|
||||
console.log('筛选后的玩家数据:', registeredPlayers.value)
|
||||
|
||||
// 筛选当前赛事的结果信息
|
||||
const tournamentResults = resultList.filter(result =>
|
||||
result.tournament_id === tournamentId
|
||||
)
|
||||
|
||||
// 合并报名信息和结果信息,添加阵营信息,只取轮数为0的玩家
|
||||
registeredPlayers.value = tournamentResults
|
||||
.filter(result => result.round === '0' || result.round === 0 || !result.round)
|
||||
.map(result => {
|
||||
// 查找对应的报名信息
|
||||
const signup = tournamentSignups.find(s => s.username === result.sign_name)
|
||||
|
||||
return {
|
||||
...result,
|
||||
faction: signup?.faction || 'random', // 添加阵营信息
|
||||
qq: signup?.qq || '' // 添加QQ信息
|
||||
}
|
||||
})
|
||||
|
||||
console.log('合并后的玩家数据:', registeredPlayers.value)
|
||||
} catch (error) {
|
||||
console.error('获取报名玩家列表失败:', error)
|
||||
}
|
||||
@ -478,12 +582,38 @@ const handleRemovePlayer = async (playerId) => {
|
||||
if (!confirm('确定要移除该玩家吗?')) return
|
||||
|
||||
try {
|
||||
await deleteSignUpResult(playerId)
|
||||
// 获取玩家信息,用于查找对应的报名记录
|
||||
const player = registeredPlayers.value.find(p => p.id === playerId)
|
||||
if (!player) {
|
||||
throw new Error('未找到玩家信息')
|
||||
}
|
||||
|
||||
// 获取报名列表,查找对应的报名记录
|
||||
const signupList = await getSignUpList()
|
||||
const signupRecord = signupList.find(s =>
|
||||
s.tournament_id === parseInt(route.query.id) &&
|
||||
s.username === player.sign_name
|
||||
)
|
||||
|
||||
// 同时删除报名信息和报名结果
|
||||
const deletePromises = []
|
||||
|
||||
// 删除报名结果
|
||||
deletePromises.push(deleteSignUpResult(playerId))
|
||||
|
||||
// 如果找到对应的报名记录,也删除报名信息
|
||||
if (signupRecord) {
|
||||
deletePromises.push(deleteSignUp(signupRecord.id))
|
||||
}
|
||||
|
||||
// 等待所有删除操作完成
|
||||
await Promise.all(deletePromises)
|
||||
|
||||
await fetchRegisteredPlayers() // 刷新列表
|
||||
alert('移除成功!')
|
||||
successDialog.value = { visible: true, message: '移除成功!' }
|
||||
} catch (error) {
|
||||
console.error('移除玩家失败:', error)
|
||||
alert(error.response?.data?.message || '移除失败,请重试')
|
||||
errorDialog.value = { visible: true, message: error.response?.data?.message || '移除失败,请重试' }
|
||||
}
|
||||
}
|
||||
|
||||
@ -525,7 +655,14 @@ const handleStatusUpdate = async () => {
|
||||
|
||||
console.log('更新赛事状态,发送数据:', updateData)
|
||||
await updateTournament(tournamentId, updateData)
|
||||
alert('状态更新成功!')
|
||||
|
||||
// 如果状态变为"进行中",为所有玩家生成对手
|
||||
if (selectedStatus.value === 'starting') {
|
||||
console.log('状态变为进行中,开始生成对手...')
|
||||
await generateOpponentsForAllPlayers(tournamentId)
|
||||
}
|
||||
|
||||
successDialog.value = { visible: true, message: '状态更新成功!' }
|
||||
closeStatusDialog()
|
||||
fetchTournamentDetail() // 刷新数据
|
||||
} catch (error) {
|
||||
@ -535,7 +672,7 @@ const handleStatusUpdate = async () => {
|
||||
response: error.response?.data,
|
||||
status: error.response?.status
|
||||
})
|
||||
alert(error.message || '状态更新失败,请重试')
|
||||
errorDialog.value = { visible: true, message: error.message || '状态更新失败,请重试' }
|
||||
} finally {
|
||||
isUpdating.value = false
|
||||
}
|
||||
@ -586,7 +723,7 @@ const handlePlayerUpdate = async () => {
|
||||
await updateSignUpResult(playerEditForm.value.id, updateData)
|
||||
await fetchRegisteredPlayers() // 刷新列表
|
||||
closePlayerEditDialog()
|
||||
alert('更新成功!')
|
||||
successDialog.value = { visible: true, message: '更新成功!' }
|
||||
} catch (error) {
|
||||
console.error('更新玩家信息失败:', error)
|
||||
console.error('错误详情:', {
|
||||
@ -594,7 +731,7 @@ const handlePlayerUpdate = async () => {
|
||||
response: error.response?.data,
|
||||
status: error.response?.status
|
||||
})
|
||||
alert(error.response?.data?.detail || error.message || '更新失败,请重试')
|
||||
errorDialog.value = { visible: true, message: error.response?.data?.detail || error.message || '更新失败,请重试' }
|
||||
} finally {
|
||||
isUpdating.value = false
|
||||
}
|
||||
@ -606,37 +743,246 @@ const saveChanges = async () => {
|
||||
isSaving.value = true
|
||||
// TODO: 调用保存对阵图的API
|
||||
// await saveTournamentBracket(route.query.id, tournamentRounds.value)
|
||||
alert('保存成功!')
|
||||
successDialog.value = { visible: true, message: '保存成功!' }
|
||||
isEditMode.value = false
|
||||
} catch (error) {
|
||||
console.error('保存失败:', error)
|
||||
alert('保存失败,请重试')
|
||||
errorDialog.value = { visible: true, message: '保存失败,请重试' }
|
||||
} finally {
|
||||
isSaving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 更新所有玩家的赛事名称
|
||||
const updateAllPlayersTournamentName = async (tournamentId, newTournamentName) => {
|
||||
try {
|
||||
console.log('开始更新所有玩家的赛事名称...')
|
||||
|
||||
// 获取当前赛事的所有玩家
|
||||
const response = await getSignUpResultList()
|
||||
const players = response.filter(player => player.tournament_id == tournamentId)
|
||||
|
||||
console.log(`找到 ${players.length} 个玩家需要更新赛事名称`)
|
||||
|
||||
if (players.length === 0) {
|
||||
console.log('没有找到需要更新的玩家')
|
||||
return
|
||||
}
|
||||
|
||||
// 批量更新所有玩家的tournament_name
|
||||
const updatePromises = players.map(player =>
|
||||
updateSignUpResult(player.id, {
|
||||
...player,
|
||||
tournament_name: newTournamentName
|
||||
})
|
||||
)
|
||||
|
||||
// 等待所有更新完成
|
||||
await Promise.all(updatePromises)
|
||||
console.log('所有玩家的赛事名称更新完成')
|
||||
|
||||
} catch (error) {
|
||||
console.error('更新玩家赛事名称失败:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// 为所有玩家生成对手
|
||||
const generateOpponentsForAllPlayers = async (tournamentId) => {
|
||||
try {
|
||||
console.log('=== 开始为所有玩家生成对手 ===')
|
||||
console.log('当前赛事ID:', tournamentId)
|
||||
|
||||
// 获取当前赛事的所有玩家
|
||||
const response = await getSignUpResultList()
|
||||
console.log('获取到的所有结果数据:', response)
|
||||
|
||||
const players = response.filter(player => player.tournament_id == tournamentId)
|
||||
console.log('筛选后的玩家数据:', players)
|
||||
console.log(`找到 ${players.length} 个玩家需要生成对手`)
|
||||
|
||||
if (players.length === 0) {
|
||||
console.log('没有找到需要生成对手的玩家')
|
||||
return
|
||||
}
|
||||
|
||||
// 显示原始玩家信息
|
||||
console.log('=== 原始玩家信息 ===')
|
||||
players.forEach((player, index) => {
|
||||
console.log(`玩家${index + 1}:`, {
|
||||
id: player.id,
|
||||
sign_name: player.sign_name,
|
||||
team_name: player.team_name,
|
||||
tournament_id: player.tournament_id,
|
||||
tournament_name: player.tournament_name
|
||||
})
|
||||
})
|
||||
|
||||
// 随机打乱玩家顺序
|
||||
const shuffledPlayers = [...players].sort(() => Math.random() - 0.5)
|
||||
console.log('=== 随机打乱后的玩家顺序 ===')
|
||||
shuffledPlayers.forEach((player, index) => {
|
||||
console.log(`第${index + 1}位:`, player.sign_name)
|
||||
})
|
||||
|
||||
// 生成对手配对
|
||||
const updatePromises = []
|
||||
const pairs = []
|
||||
|
||||
console.log('=== 开始生成配对 ===')
|
||||
for (let i = 0; i < shuffledPlayers.length; i += 2) {
|
||||
if (i + 1 < shuffledPlayers.length) {
|
||||
// 有配对的情况
|
||||
const player1 = shuffledPlayers[i]
|
||||
const player2 = shuffledPlayers[i + 1]
|
||||
|
||||
console.log(`配对 ${i/2 + 1}: ${player1.sign_name} vs ${player2.sign_name}`)
|
||||
pairs.push({
|
||||
player1: player1.sign_name,
|
||||
player2: player2.sign_name,
|
||||
type: '对战'
|
||||
})
|
||||
|
||||
// 更新player1的对手信息
|
||||
const updateData1 = {
|
||||
...player1,
|
||||
rival_name: player2.sign_name,
|
||||
win: '0',
|
||||
lose: '0',
|
||||
status: 'tie',
|
||||
round: '0'
|
||||
}
|
||||
console.log(`更新 ${player1.sign_name} 的对手信息:`, updateData1)
|
||||
updatePromises.push(updateSignUpResult(player1.id, updateData1))
|
||||
|
||||
// 更新player2的对手信息
|
||||
const updateData2 = {
|
||||
...player2,
|
||||
rival_name: player1.sign_name,
|
||||
win: '0',
|
||||
lose: '0',
|
||||
status: 'tie',
|
||||
round: '0'
|
||||
}
|
||||
console.log(`更新 ${player2.sign_name} 的对手信息:`, updateData2)
|
||||
updatePromises.push(updateSignUpResult(player2.id, updateData2))
|
||||
} else {
|
||||
// 最后一个玩家轮空
|
||||
const player = shuffledPlayers[i]
|
||||
console.log(`轮空: ${player.sign_name}`)
|
||||
pairs.push({
|
||||
player1: player.sign_name,
|
||||
player2: null,
|
||||
type: '轮空'
|
||||
})
|
||||
|
||||
const updateData = {
|
||||
...player,
|
||||
rival_name: '轮空',
|
||||
win: '0',
|
||||
lose: '0',
|
||||
status: 'tie',
|
||||
round: '0'
|
||||
}
|
||||
console.log(`更新 ${player.sign_name} 的轮空信息:`, updateData)
|
||||
updatePromises.push(updateSignUpResult(player.id, updateData))
|
||||
}
|
||||
}
|
||||
|
||||
console.log('=== 配对结果总结 ===')
|
||||
pairs.forEach((pair, index) => {
|
||||
if (pair.type === '对战') {
|
||||
console.log(`配对${index + 1}: ${pair.player1} vs ${pair.player2}`)
|
||||
} else {
|
||||
console.log(`配对${index + 1}: ${pair.player1} (轮空)`)
|
||||
}
|
||||
})
|
||||
|
||||
console.log(`总共需要更新 ${updatePromises.length} 个玩家记录`)
|
||||
|
||||
// 等待所有更新完成
|
||||
console.log('=== 开始批量更新 ===')
|
||||
await Promise.all(updatePromises)
|
||||
console.log('=== 所有玩家的对手生成完成 ===')
|
||||
|
||||
} catch (error) {
|
||||
console.error('生成对手失败:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// 获取最终结果
|
||||
const fetchFinalResults = async () => {
|
||||
try {
|
||||
const tournamentId = parseInt(route.query.id)
|
||||
const response = await getSignUpResultList()
|
||||
// 筛选当前赛事的玩家并按胜场数排序
|
||||
const results = response
|
||||
.filter(player => player.tournament_id === tournamentId)
|
||||
|
||||
console.log('原始API数据:', response)
|
||||
|
||||
// 筛选当前赛事的所有轮次数据
|
||||
const tournamentData = response.filter(player => player.tournament_id === tournamentId)
|
||||
|
||||
console.log('筛选后的赛事数据:', tournamentData)
|
||||
console.log('数据条数:', tournamentData.length)
|
||||
|
||||
// 按玩家名称分组,计算总胜利数和总失败数
|
||||
const playerStats = {}
|
||||
|
||||
tournamentData.forEach((player, index) => {
|
||||
const playerName = player.sign_name
|
||||
const win = parseInt(player.win || 0)
|
||||
const lose = parseInt(player.lose || 0)
|
||||
const round = player.round || '0'
|
||||
|
||||
console.log(`处理第${index + 1}条数据:`, {
|
||||
id: player.id,
|
||||
sign_name: playerName,
|
||||
round: round,
|
||||
win: win,
|
||||
lose: lose,
|
||||
status: player.status
|
||||
})
|
||||
|
||||
if (!playerStats[playerName]) {
|
||||
playerStats[playerName] = {
|
||||
username: playerName,
|
||||
qq: player.qq || '',
|
||||
faction: player.faction || 'random',
|
||||
totalWin: 0,
|
||||
totalLose: 0,
|
||||
rounds: []
|
||||
}
|
||||
}
|
||||
|
||||
playerStats[playerName].totalWin += win
|
||||
playerStats[playerName].totalLose += lose
|
||||
playerStats[playerName].rounds.push(round)
|
||||
})
|
||||
|
||||
console.log('分组后的玩家统计:', playerStats)
|
||||
|
||||
// 转换为数组并按总胜利数排序
|
||||
const results = Object.values(playerStats)
|
||||
.sort((a, b) => {
|
||||
// 首先按总胜利数排序
|
||||
if (b.totalWin !== a.totalWin) {
|
||||
return b.totalWin - a.totalWin
|
||||
}
|
||||
// 如果胜利数相同,按失败数排序(失败数少的排名靠前)
|
||||
return a.totalLose - b.totalLose
|
||||
})
|
||||
.map((player, index) => ({
|
||||
rank: index + 1,
|
||||
username: player.sign_name,
|
||||
username: player.username,
|
||||
qq: player.qq,
|
||||
faction: player.faction,
|
||||
score: `${player.win}胜${player.lose}负`
|
||||
score: `${player.totalWin}胜${player.totalLose}负`,
|
||||
rounds: player.rounds.join(',')
|
||||
}))
|
||||
.sort((a, b) => {
|
||||
const aScore = parseInt(a.score.split('胜')[0])
|
||||
const bScore = parseInt(b.score.split('胜')[0])
|
||||
return bScore - aScore
|
||||
})
|
||||
|
||||
console.log('最终排名结果:', results)
|
||||
finalResults.value = results
|
||||
} catch (error) {
|
||||
console.error('获取最终结果失败:', error)
|
@ -124,13 +124,27 @@
|
||||
<button class="btn-submit" @click="handleSubmit">提交报名</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 对话框组件 -->
|
||||
<SuccessDialog
|
||||
:visible="successDialog.visible"
|
||||
:message="successDialog.message"
|
||||
@close="successDialog.visible = false"
|
||||
/>
|
||||
<ErrorDialog
|
||||
:visible="errorDialog.visible"
|
||||
:message="errorDialog.message"
|
||||
@close="errorDialog.visible = false"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, watch } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { addSignUp } from '@/api/tournament'
|
||||
import {addSignUp, addSignUpResult} from '@/api/tournament.js'
|
||||
import SuccessDialog from '@/components/SuccessDialog.vue'
|
||||
import ErrorDialog from '@/components/ErrorDialog.vue'
|
||||
|
||||
defineOptions({
|
||||
name: 'CompetitionSignUp'
|
||||
@ -158,6 +172,10 @@ const signupForm = ref({
|
||||
qq: ''
|
||||
})
|
||||
|
||||
// Dialog state
|
||||
const successDialog = ref({ visible: false, message: '' })
|
||||
const errorDialog = ref({ visible: false, message: '' })
|
||||
|
||||
const formatDate = (date) => {
|
||||
if (!date) return ''
|
||||
// 将年/月/日格式转换为年-月-日格式显示
|
||||
@ -190,12 +208,12 @@ const handleSubmit = async () => {
|
||||
// 根据报名类型验证必填字段
|
||||
if (signupForm.value.type === 'teamname') {
|
||||
if (!signupForm.value.teamName || !signupForm.value.username) {
|
||||
alert('请填写完整的队伍信息')
|
||||
errorDialog.value = { visible: true, message: '请填写完整的队伍信息' }
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if (!signupForm.value.username) {
|
||||
alert('请填写完整的个人信息')
|
||||
errorDialog.value = { visible: true, message: '请填写完整的个人信息' }
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -203,61 +221,67 @@ const handleSubmit = async () => {
|
||||
// 验证 sign_name
|
||||
const signName = signupForm.value.username.trim()
|
||||
if (!signName) {
|
||||
alert('参赛人员名称不能为空')
|
||||
errorDialog.value = { visible: true, message: '参赛人员名称不能为空' }
|
||||
return
|
||||
}
|
||||
if (signName.length < 2) {
|
||||
alert('参赛人员名称至少需要2个字符')
|
||||
errorDialog.value = { visible: true, message: '参赛人员名称至少需要2个字符' }
|
||||
return
|
||||
}
|
||||
if (signName.length > 20) {
|
||||
alert('参赛人员名称不能超过20个字符')
|
||||
errorDialog.value = { visible: true, message: '参赛人员名称不能超过20个字符' }
|
||||
return
|
||||
}
|
||||
// 只允许中文、英文、数字和下划线
|
||||
if (!/^[\u4e00-\u9fa5a-zA-Z0-9_]+$/.test(signName)) {
|
||||
alert('参赛人员名称只能包含中文、英文、数字和下划线')
|
||||
errorDialog.value = { visible: true, message: '参赛人员名称只能包含中文、英文、数字和下划线' }
|
||||
return
|
||||
}
|
||||
// 不允许纯数字
|
||||
if (/^\d+$/.test(signName)) {
|
||||
alert('参赛人员名称不能为纯数字')
|
||||
errorDialog.value = { visible: true, message: '参赛人员名称不能为纯数字' }
|
||||
return
|
||||
}
|
||||
// 不允许纯下划线
|
||||
if (/^_+$/.test(signName)) {
|
||||
alert('参赛人员名称不能为纯下划线')
|
||||
errorDialog.value = { visible: true, message: '参赛人员名称不能为纯下划线' }
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
// 确保所有必需字段都存在
|
||||
if (!competitionInfo.value.id || !competitionInfo.value.name) {
|
||||
alert('比赛信息不完整,请返回重试')
|
||||
errorDialog.value = { visible: true, message: '比赛信息不完整,请返回重试' }
|
||||
return
|
||||
}
|
||||
|
||||
const submitData = {
|
||||
id: parseInt(competitionInfo.value.id),
|
||||
tournament_name: competitionInfo.value.name,
|
||||
tournament_id: parseInt(competitionInfo.value.id),
|
||||
type: signupForm.value.type,
|
||||
team_name: signupForm.value.type === 'teamname' ? signupForm.value.teamName : '',
|
||||
sign_name: signName,
|
||||
teamname: signupForm.value.type === 'teamname' ? signupForm.value.teamName : ' ',
|
||||
username: signupForm.value.username,
|
||||
faction: signupForm.value.faction,
|
||||
qq_code: String(competitionInfo.value.qq_code) // 确保 qq_code 是字符串类型
|
||||
qq: signupForm.value.qq
|
||||
}
|
||||
const signupResultData = {
|
||||
tournament_id: parseInt(competitionInfo.value.id),
|
||||
tournament_name: competitionInfo.value.name,
|
||||
team_name: signupForm.value.type === 'teamname' ? signupForm.value.teamName : ' ',
|
||||
sign_name: signupForm.value.username,
|
||||
win: '0',
|
||||
lose: '0',
|
||||
status: 'tie',
|
||||
round: '0',
|
||||
rival_name: ''
|
||||
}
|
||||
|
||||
console.log('提交的报名数据:', submitData)
|
||||
const result = await addSignUp(submitData)
|
||||
console.log('报名结果:', result)
|
||||
|
||||
if (result.signup && result.result) {
|
||||
alert('报名成功!')
|
||||
const resultSignup = await addSignUpResult(signupResultData)
|
||||
console.log('提交的报名数据:', submitData, signupResultData)
|
||||
console.log('报名结果:', result, resultSignup)
|
||||
successDialog.value = { visible: true, message: '报名成功!' }
|
||||
setTimeout(() => {
|
||||
router.push('/competition')
|
||||
} else {
|
||||
console.error('报名结果不完整:', result)
|
||||
throw new Error('报名数据不完整,请重试')
|
||||
}
|
||||
}, 1500)
|
||||
} catch (error) {
|
||||
console.error('报名失败:', error)
|
||||
console.error('错误详情:', {
|
||||
@ -268,13 +292,13 @@ const handleSubmit = async () => {
|
||||
|
||||
// 显示具体的错误信息
|
||||
if (error.message.includes('返回数据为空')) {
|
||||
alert('服务器返回数据为空,请稍后重试')
|
||||
errorDialog.value = { visible: true, message: '服务器返回数据为空,请稍后重试' }
|
||||
} else if (error.message.includes('数据不完整')) {
|
||||
alert('报名数据不完整,请重试')
|
||||
errorDialog.value = { visible: true, message: '报名数据不完整,请重试' }
|
||||
} else if (error.message.includes('网络连接')) {
|
||||
alert('网络连接失败,请检查网络后重试')
|
||||
errorDialog.value = { visible: true, message: '网络连接失败,请检查网络后重试' }
|
||||
} else {
|
||||
alert(error.message || '报名失败,请稍后重试')
|
||||
errorDialog.value = { visible: true, message: error.message || '报名失败,请稍后重试' }
|
||||
}
|
||||
}
|
||||
}
|
@ -324,11 +324,13 @@ function handlePasswordChangeError(errorMessage) {
|
||||
<div class="app">
|
||||
<nav class="navbar">
|
||||
<div class="nav-container">
|
||||
<div class="nav-brand">红色警戒3数据分析中心</div>
|
||||
<a href="/" class="nav-brand">红色警戒3数据分析中心</a>
|
||||
<!-- <div class="nav-brand">红色警戒3数据分析中心</div>-->
|
||||
<button class="mobile-menu-toggle" @click="toggleMobileMenu">
|
||||
<i class="fas fa-bars"></i>
|
||||
</button>
|
||||
<div class="nav-left" :class="{ active: showMobileMenu }">
|
||||
<a href="/" class="nav-link">首页</a>
|
||||
<!-- 地图 一级菜单 -->
|
||||
<div class="nav-dropdown">
|
||||
<span class="nav-link">地图与作者推荐</span>
|
||||
@ -343,41 +345,16 @@ function handlePasswordChangeError(errorMessage) {
|
||||
<span class="nav-link">地形与纹理</span>
|
||||
<div class="dropdown-content">
|
||||
<router-link to="/terrain" class="nav-link">地形图列表</router-link>
|
||||
<!-- <router-link v-if="isLoggedIn" to="/terrainGenerate" class="nav-link" @click.prevent="handleNavClick('/terrainGenerate', ['lv-admin','lv-mod','lv-map','lv-competitor'])">地形纹理合成工具</router-link>-->
|
||||
<router-link to="/terrainGenerate" class="nav-link" @click.prevent="handleNavClick('/terrainGenerate', ['lv-admin','lv-mod','lv-map','lv-competitor'])">地形纹理合成工具</router-link>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <template v-if="isLoggedIn">-->
|
||||
<!-- <!– 在线工具 一级菜单 –>-->
|
||||
<!-- <div class="nav-dropdown">-->
|
||||
<!-- <span class="nav-link">在线工具</span>-->
|
||||
<!-- <div class="dropdown-content">-->
|
||||
<!-- <router-link to="/weapon-match" class="nav-link" @click.prevent="handleNavClick('/weapon-match', ['lv-admin','lv-mod'])">Weapon 匹配</router-link>-->
|
||||
<!-- <router-link to="/PIC2TGA" class="nav-link" @click.prevent="handleNavClick('/PIC2TGA', ['lv-admin','lv-mod','lv-map','lv-competitor'])">在线转tga工具</router-link>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- <!– 赛事信息 一级菜单 –>-->
|
||||
<!-- <div class="nav-dropdown">-->
|
||||
<!-- <span class="nav-link">赛事信息</span>-->
|
||||
<!-- <div class="dropdown-content">-->
|
||||
<!--<!– <router-link to="/competition" class="nav-link" @click.prevent="handleNavClick('/competition', ['lv-admin','lv-competitor'])">赛程信息</router-link>–>-->
|
||||
<!-- <router-link to="/competition" class="nav-link">赛程信息</router-link>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- <!– 公共信息区 一级菜单 –>-->
|
||||
<!-- <div class="nav-dropdown">-->
|
||||
<!-- <span class="nav-link">公共信息区</span>-->
|
||||
<!-- <div class="dropdown-content">-->
|
||||
<!-- <router-link to="/demands" class="nav-link">办事大厅</router-link>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </template>-->
|
||||
<!-- 需要登陆才能访问,如果没有登陆,则点击跳转到登陆页面-->
|
||||
<!-- 如果登陆了,才能执行权限判断-->
|
||||
<div class="nav-dropdown">
|
||||
<span class="nav-link">在线工具</span>
|
||||
<div class="dropdown-content">
|
||||
<router-link to="/weapon-match" class="nav-link" @click.prevent="handleNavClick('/weapon-match', ['lv-admin','lv-mod'])">Weapon 匹配</router-link>
|
||||
<router-link to="/configEditor" class="nav-link" @click.prevent="handleNavClick('/configEditor', ['lv-admin','lv-mod'])">Config.xml 编辑器</router-link>
|
||||
<router-link to="/PIC2TGA" class="nav-link" @click.prevent="handleNavClick('/PIC2TGA', ['lv-admin','lv-mod','lv-map','lv-competitor'])">在线转tga工具</router-link>
|
||||
</div>
|
||||
</div>
|
||||
@ -386,7 +363,7 @@ function handlePasswordChangeError(errorMessage) {
|
||||
<span class="nav-link">赛事信息</span>
|
||||
<div class="dropdown-content">
|
||||
<!-- <router-link to="/competition" class="nav-link" @click.prevent="handleNavClick('/competition', ['lv-admin','lv-competitor'])">赛程信息</router-link>-->
|
||||
<router-link to="/competition" class="nav-link" @click.prevent="handleNavClick('/competition')">赛程信息</router-link>
|
||||
<router-link to="competition" class="nav-link" @click.prevent="handleNavClick('/competition')">赛程信息</router-link>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 公共信息区 一级菜单 -->
|
||||
@ -439,9 +416,15 @@ function handlePasswordChangeError(errorMessage) {
|
||||
<img src="../assets/logo.png" class="footer-logo">
|
||||
<span class="footer-title">红色警戒3数据分析中心</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="footer-bottom">
|
||||
<p>Byz解忧杂货铺</p>
|
||||
<div>
|
||||
<p>Byz解忧杂货铺</p>
|
||||
</div>
|
||||
<p class="beian">
|
||||
<a href="https://beian.miit.gov.cn/" rel="noreferrer" target="_blank">注册号 : 京ICP备2025120142号-1</a>
|
||||
</p>
|
||||
<p class="beian">
|
||||
<img src="../assets/备案图标.png" alt="公安备案图标" class="police-icon"/>
|
||||
<a href="https://beian.mps.gov.cn/#/query/webSearch?code=11010802045768" rel="noreferrer" target="_blank">京公网安备11010802045768号</a>
|
||||
@ -453,12 +436,12 @@ function handlePasswordChangeError(errorMessage) {
|
||||
:message="errorDialogMessage"
|
||||
@close="errorDialogVisible = false"
|
||||
/>
|
||||
<PrivilegeRequestDialog
|
||||
:visible="privilegeDialogVisible"
|
||||
:privilegeName="privilegeDialogName"
|
||||
@close="privilegeDialogVisible = false"
|
||||
@apply="handlePrivilegeApply"
|
||||
/>
|
||||
<!-- <PrivilegeRequestDialog-->
|
||||
<!-- :visible="privilegeDialogVisible"-->
|
||||
<!-- :privilegeName="privilegeDialogName"-->
|
||||
<!-- @close="privilegeDialogVisible = false"-->
|
||||
<!-- @apply="handlePrivilegeApply"-->
|
||||
<!-- />-->
|
||||
<SuccessDialog
|
||||
:visible="successDialog.visible"
|
||||
:message="successDialog.message"
|
||||
|
1783
src/views/index/ConfigEditor.vue
Normal file
1783
src/views/index/ConfigEditor.vue
Normal file
File diff suppressed because it is too large
Load Diff
1073
src/views/index/Home.vue
Normal file
1073
src/views/index/Home.vue
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user