Compare commits
No commits in common. "6d6a6cd03ad9f999d5abd3e8289c0643a8f2ed43" and "b7eb16fb2f1571976ceb796d8443b987ce4bf152" have entirely different histories.
6d6a6cd03a
...
b7eb16fb2f
@ -5,7 +5,7 @@
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
||||
<title>红色警戒3工具平台</title>
|
||||
<title>红色警戒3数据分析中心</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
440
node_modules/.package-lock.json
generated
vendored
440
node_modules/.package-lock.json
generated
vendored
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "untitled2",
|
||||
"version": "V.1.0.0",
|
||||
"version": "0.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
@ -54,7 +54,6 @@
|
||||
"resolved": "https://registry.npmmirror.com/@babel/core/-/core-7.27.1.tgz",
|
||||
"integrity": "sha512-IaaGWsQqfsQWVLqMn9OB92MNN7zukfVA4s7KKAI0KfrrDsZ0yhi5uV4baBuLuN7n3vsZpwP8asPPcVwApxvjBQ==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@ampproject/remapping": "^2.2.0",
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
@ -840,7 +839,6 @@
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"caniuse-lite": "^1.0.30001716",
|
||||
"electron-to-chromium": "^1.5.149",
|
||||
@ -932,14 +930,6 @@
|
||||
"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",
|
||||
@ -996,377 +986,6 @@
|
||||
"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==",
|
||||
"peer": true,
|
||||
"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",
|
||||
@ -1424,14 +1043,6 @@
|
||||
"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",
|
||||
@ -1829,17 +1440,6 @@
|
||||
"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",
|
||||
@ -1850,14 +1450,6 @@
|
||||
"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",
|
||||
@ -2049,18 +1641,6 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/marked": {
|
||||
"version": "17.0.0",
|
||||
"resolved": "https://registry.npmjs.org/marked/-/marked-17.0.0.tgz",
|
||||
"integrity": "sha512-KkDYEWEEiYJw/KC+DVm1zzlpMQSMIu6YRltkcCvwheCp8HWPXCk9JwOmHJKBlGfzcpzcIt6x3sMnTsRm/51oDg==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"marked": "bin/marked.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/math-intrinsics": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||
@ -2313,17 +1893,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",
|
||||
"integrity": "sha512-C5VvvgCCyfyotVITIAv+4efVytl5F7wt+/I2i9q9GZcEXW9BP52YYOXC58igUi+LFZVHukErIIqQSWwv/M3WRw==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/estree": "1.0.7"
|
||||
},
|
||||
@ -2370,21 +1944,11 @@
|
||||
"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",
|
||||
@ -2592,7 +2156,6 @@
|
||||
"resolved": "https://registry.npmmirror.com/vite/-/vite-6.3.4.tgz",
|
||||
"integrity": "sha512-BiReIiMS2fyFqbqNT/Qqt4CVITDU9M9vE+DKcVAsB+ZV0wvTKd+3hMbkpxz1b+NmEDMegpVbisKiAZOnvO92Sw==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.25.0",
|
||||
"fdir": "^6.4.4",
|
||||
@ -2750,7 +2313,6 @@
|
||||
"version": "3.5.13",
|
||||
"resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.13.tgz",
|
||||
"integrity": "sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@vue/compiler-dom": "3.5.13",
|
||||
"@vue/compiler-sfc": "3.5.13",
|
||||
|
||||
46
node_modules/.vite/deps/_metadata.json
generated
vendored
46
node_modules/.vite/deps/_metadata.json
generated
vendored
@ -1,64 +1,52 @@
|
||||
{
|
||||
"hash": "f6d37eef",
|
||||
"configHash": "4eee8ee7",
|
||||
"lockfileHash": "17b35e94",
|
||||
"browserHash": "38da36af",
|
||||
"hash": "3498b3cb",
|
||||
"configHash": "9c7a641a",
|
||||
"lockfileHash": "c997fc3c",
|
||||
"browserHash": "38ceb684",
|
||||
"optimized": {
|
||||
"axios": {
|
||||
"src": "../../axios/index.js",
|
||||
"file": "axios.js",
|
||||
"fileHash": "a5a07628",
|
||||
"needsInterop": false
|
||||
},
|
||||
"d3": {
|
||||
"src": "../../d3/src/index.js",
|
||||
"file": "d3.js",
|
||||
"fileHash": "828cdd5e",
|
||||
"fileHash": "97de2fb4",
|
||||
"needsInterop": false
|
||||
},
|
||||
"jszip": {
|
||||
"src": "../../jszip/dist/jszip.min.js",
|
||||
"file": "jszip.js",
|
||||
"fileHash": "c0e06a93",
|
||||
"fileHash": "3cd8a10a",
|
||||
"needsInterop": true
|
||||
},
|
||||
"mitt": {
|
||||
"src": "../../mitt/dist/mitt.mjs",
|
||||
"file": "mitt.js",
|
||||
"fileHash": "8e22dbfe",
|
||||
"needsInterop": false
|
||||
},
|
||||
"vue": {
|
||||
"src": "../../vue/dist/vue.runtime.esm-bundler.js",
|
||||
"file": "vue.js",
|
||||
"fileHash": "7a7fc2f0",
|
||||
"fileHash": "74f3ac3b",
|
||||
"needsInterop": false
|
||||
},
|
||||
"vue-router": {
|
||||
"src": "../../vue-router/dist/vue-router.mjs",
|
||||
"file": "vue-router.js",
|
||||
"fileHash": "58c64081",
|
||||
"fileHash": "d2d7abce",
|
||||
"needsInterop": false
|
||||
},
|
||||
"xlsx": {
|
||||
"src": "../../xlsx/xlsx.mjs",
|
||||
"file": "xlsx.js",
|
||||
"fileHash": "d95fb4c8",
|
||||
"fileHash": "dbbdc859",
|
||||
"needsInterop": false
|
||||
},
|
||||
"marked": {
|
||||
"src": "../../marked/lib/marked.esm.js",
|
||||
"file": "marked.js",
|
||||
"fileHash": "a5f98500",
|
||||
"mitt": {
|
||||
"src": "../../mitt/dist/mitt.mjs",
|
||||
"file": "mitt.js",
|
||||
"fileHash": "ac6eb5ab",
|
||||
"needsInterop": false
|
||||
}
|
||||
},
|
||||
"chunks": {
|
||||
"chunk-YBGSFZ7G": {
|
||||
"file": "chunk-YBGSFZ7G.js"
|
||||
"chunk-U3LI7FBV": {
|
||||
"file": "chunk-U3LI7FBV.js"
|
||||
},
|
||||
"chunk-FM7WUVZV": {
|
||||
"file": "chunk-FM7WUVZV.js"
|
||||
"chunk-5FUTL2UF": {
|
||||
"file": "chunk-5FUTL2UF.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
2
node_modules/.vite/deps/axios.js
generated
vendored
2
node_modules/.vite/deps/axios.js
generated
vendored
@ -1,6 +1,6 @@
|
||||
import {
|
||||
__export
|
||||
} from "./chunk-FM7WUVZV.js";
|
||||
} from "./chunk-5FUTL2UF.js";
|
||||
|
||||
// node_modules/axios/lib/helpers/bind.js
|
||||
function bind(fn, thisArg) {
|
||||
|
||||
2
node_modules/.vite/deps/axios.js.map
generated
vendored
2
node_modules/.vite/deps/axios.js.map
generated
vendored
File diff suppressed because one or more lines are too long
21
node_modules/.vite/deps/chunk-5FUTL2UF.js
generated
vendored
Normal file
21
node_modules/.vite/deps/chunk-5FUTL2UF.js
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
||||
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
||||
}) : x)(function(x) {
|
||||
if (typeof require !== "undefined") return require.apply(this, arguments);
|
||||
throw Error('Dynamic require of "' + x + '" is not supported');
|
||||
});
|
||||
var __commonJS = (cb, mod) => function __require2() {
|
||||
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
||||
};
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, { get: all[name], enumerable: true });
|
||||
};
|
||||
|
||||
export {
|
||||
__require,
|
||||
__commonJS,
|
||||
__export
|
||||
};
|
||||
7
node_modules/.vite/deps/chunk-5FUTL2UF.js.map
generated
vendored
Normal file
7
node_modules/.vite/deps/chunk-5FUTL2UF.js.map
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"version": 3,
|
||||
"sources": [],
|
||||
"sourcesContent": [],
|
||||
"mappings": "",
|
||||
"names": []
|
||||
}
|
||||
12542
node_modules/.vite/deps/chunk-U3LI7FBV.js
generated
vendored
Normal file
12542
node_modules/.vite/deps/chunk-U3LI7FBV.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
7
node_modules/.vite/deps/chunk-U3LI7FBV.js.map
generated
vendored
Normal file
7
node_modules/.vite/deps/chunk-U3LI7FBV.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
2
node_modules/.vite/deps/jszip.js
generated
vendored
2
node_modules/.vite/deps/jszip.js
generated
vendored
@ -1,7 +1,7 @@
|
||||
import {
|
||||
__commonJS,
|
||||
__require
|
||||
} from "./chunk-FM7WUVZV.js";
|
||||
} from "./chunk-5FUTL2UF.js";
|
||||
|
||||
// node_modules/jszip/dist/jszip.min.js
|
||||
var require_jszip_min = __commonJS({
|
||||
|
||||
2
node_modules/.vite/deps/jszip.js.map
generated
vendored
2
node_modules/.vite/deps/jszip.js.map
generated
vendored
File diff suppressed because one or more lines are too long
2
node_modules/.vite/deps/mitt.js
generated
vendored
2
node_modules/.vite/deps/mitt.js
generated
vendored
@ -1,4 +1,4 @@
|
||||
import "./chunk-FM7WUVZV.js";
|
||||
import "./chunk-5FUTL2UF.js";
|
||||
|
||||
// node_modules/mitt/dist/mitt.mjs
|
||||
function mitt_default(n) {
|
||||
|
||||
4
node_modules/.vite/deps/vue-router.js
generated
vendored
4
node_modules/.vite/deps/vue-router.js
generated
vendored
@ -16,8 +16,8 @@ import {
|
||||
unref,
|
||||
watch,
|
||||
watchEffect
|
||||
} from "./chunk-YBGSFZ7G.js";
|
||||
import "./chunk-FM7WUVZV.js";
|
||||
} from "./chunk-U3LI7FBV.js";
|
||||
import "./chunk-5FUTL2UF.js";
|
||||
|
||||
// node_modules/@vue/devtools-api/lib/esm/env.js
|
||||
function getDevtoolsGlobalHook() {
|
||||
|
||||
2
node_modules/.vite/deps/vue-router.js.map
generated
vendored
2
node_modules/.vite/deps/vue-router.js.map
generated
vendored
File diff suppressed because one or more lines are too long
4
node_modules/.vite/deps/vue.js
generated
vendored
4
node_modules/.vite/deps/vue.js
generated
vendored
@ -168,8 +168,8 @@ import {
|
||||
withMemo,
|
||||
withModifiers,
|
||||
withScopeId
|
||||
} from "./chunk-YBGSFZ7G.js";
|
||||
import "./chunk-FM7WUVZV.js";
|
||||
} from "./chunk-U3LI7FBV.js";
|
||||
import "./chunk-5FUTL2UF.js";
|
||||
export {
|
||||
BaseTransition,
|
||||
BaseTransitionPropsValidators,
|
||||
|
||||
2
node_modules/.vite/deps/xlsx.js
generated
vendored
2
node_modules/.vite/deps/xlsx.js
generated
vendored
@ -1,4 +1,4 @@
|
||||
import "./chunk-FM7WUVZV.js";
|
||||
import "./chunk-5FUTL2UF.js";
|
||||
|
||||
// node_modules/xlsx/xlsx.mjs
|
||||
var XLSX = {};
|
||||
|
||||
2
node_modules/.vite/deps/xlsx.js.map
generated
vendored
2
node_modules/.vite/deps/xlsx.js.map
generated
vendored
File diff suppressed because one or more lines are too long
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,6 +40,14 @@ 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]
|
||||
@ -105,7 +113,7 @@ export {};
|
||||
index: number,
|
||||
][];
|
||||
function __VLS_getSlotParameters<S, D extends S>(slot: S, decl?: D):
|
||||
D extends (...args: infer P) => any ? P : any[];
|
||||
__VLS_PickNotAny<NonNullable<D>, (...args: any) => any> 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
|
||||
|
||||
444
package-lock.json
generated
444
package-lock.json
generated
@ -1,17 +1,15 @@
|
||||
{
|
||||
"name": "untitled2",
|
||||
"version": "V.1.0.0",
|
||||
"version": "0.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "untitled2",
|
||||
"version": "V.1.0.0",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"axios": "^1.9.0",
|
||||
"d3": "^7.9.0",
|
||||
"jszip": "^3.10.1",
|
||||
"marked": "^17.0.0",
|
||||
"process": "^0.11.10",
|
||||
"vue": "^3.5.13",
|
||||
"vue-router": "^4.5.1",
|
||||
@ -74,7 +72,6 @@
|
||||
"resolved": "https://registry.npmmirror.com/@babel/core/-/core-7.27.1.tgz",
|
||||
"integrity": "sha512-IaaGWsQqfsQWVLqMn9OB92MNN7zukfVA4s7KKAI0KfrrDsZ0yhi5uV4baBuLuN7n3vsZpwP8asPPcVwApxvjBQ==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@ampproject/remapping": "^2.2.0",
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
@ -1491,7 +1488,6 @@
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"caniuse-lite": "^1.0.30001716",
|
||||
"electron-to-chromium": "^1.5.149",
|
||||
@ -1583,14 +1579,6 @@
|
||||
"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",
|
||||
@ -1647,377 +1635,6 @@
|
||||
"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==",
|
||||
"peer": true,
|
||||
"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",
|
||||
@ -2075,14 +1692,6 @@
|
||||
"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",
|
||||
@ -2494,17 +2103,6 @@
|
||||
"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",
|
||||
@ -2515,14 +2113,6 @@
|
||||
"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",
|
||||
@ -2714,18 +2304,6 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/marked": {
|
||||
"version": "17.0.0",
|
||||
"resolved": "https://registry.npmjs.org/marked/-/marked-17.0.0.tgz",
|
||||
"integrity": "sha512-KkDYEWEEiYJw/KC+DVm1zzlpMQSMIu6YRltkcCvwheCp8HWPXCk9JwOmHJKBlGfzcpzcIt6x3sMnTsRm/51oDg==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"marked": "bin/marked.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/math-intrinsics": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||
@ -2978,17 +2556,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",
|
||||
"integrity": "sha512-C5VvvgCCyfyotVITIAv+4efVytl5F7wt+/I2i9q9GZcEXW9BP52YYOXC58igUi+LFZVHukErIIqQSWwv/M3WRw==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/estree": "1.0.7"
|
||||
},
|
||||
@ -3035,21 +2607,11 @@
|
||||
"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",
|
||||
@ -3257,7 +2819,6 @@
|
||||
"resolved": "https://registry.npmmirror.com/vite/-/vite-6.3.4.tgz",
|
||||
"integrity": "sha512-BiReIiMS2fyFqbqNT/Qqt4CVITDU9M9vE+DKcVAsB+ZV0wvTKd+3hMbkpxz1b+NmEDMegpVbisKiAZOnvO92Sw==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.25.0",
|
||||
"fdir": "^6.4.4",
|
||||
@ -3415,7 +2976,6 @@
|
||||
"version": "3.5.13",
|
||||
"resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.13.tgz",
|
||||
"integrity": "sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@vue/compiler-dom": "3.5.13",
|
||||
"@vue/compiler-sfc": "3.5.13",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "untitled2",
|
||||
"version": "V.1.0.1",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
@ -10,9 +10,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.9.0",
|
||||
"d3": "^7.9.0",
|
||||
"jszip": "^3.10.1",
|
||||
"marked": "^17.0.0",
|
||||
"process": "^0.11.10",
|
||||
"vue": "^3.5.13",
|
||||
"vue-router": "^4.5.1",
|
||||
|
||||
@ -1,545 +0,0 @@
|
||||
<?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>
|
||||
File diff suppressed because it is too large
Load Diff
3253
public/Locomotor.xml
3253
public/Locomotor.xml
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,8 +1,7 @@
|
||||
import axios from 'axios';
|
||||
import { logoutUser } from '../utils/jwt'; // logoutUser会处理清除存储和重定向
|
||||
const API_BASE_URL = 'http://zybdatasupport.online:8000'
|
||||
// const API_BASE_URL = 'http://110.42.61.148/'
|
||||
// const API_BASE_URL = 'https://api.zybdatasupport.online';
|
||||
|
||||
const API_BASE_URL = 'https://api.zybdatasupport.online';
|
||||
//const API_BASE_URL = 'http://hk.zybdatasupport.online:8000/';
|
||||
|
||||
const axiosInstance = axios.create({
|
||||
|
||||
@ -1,16 +0,0 @@
|
||||
import axiosInstance from './axiosConfig'
|
||||
|
||||
/**
|
||||
* 获取后端版本信息
|
||||
* GET /info
|
||||
* 基于 axiosInstance 的 baseURL:`http://zybdatasupport.online:8000`
|
||||
* 返回后端提供的原始 JSON 数据
|
||||
*/
|
||||
export const getBackendInfo = async () => {
|
||||
try {
|
||||
const { data } = await axiosInstance.get('/info')
|
||||
return data
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
@ -1,64 +0,0 @@
|
||||
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,225 +3,239 @@ import axiosInstance from './axiosConfig';
|
||||
/**
|
||||
* 添加赛事
|
||||
* @param {Object} tournamentData - 赛事数据
|
||||
* @param {number} tournamentData.id - 数据库中id
|
||||
* @param {string} tournamentData.name - 名称
|
||||
* @param {string} tournamentData.format - 类型(single, double, count)
|
||||
* @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>} 返回添加赛事的响应数据
|
||||
* @returns {Promise} 返回添加赛事的响应数据
|
||||
*/
|
||||
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('添加赛事失败:', error);
|
||||
throw error;
|
||||
console.error('添加赛事失败:', {
|
||||
status: error.response?.status,
|
||||
data: error.response?.data,
|
||||
message: error.message
|
||||
})
|
||||
throw error
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取赛事列表
|
||||
* @returns {Promise<Array<Object>>} 返回赛事列表数据
|
||||
* @returns {Promise} 返回赛事列表数据
|
||||
*/
|
||||
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('获取赛事列表失败:', error);
|
||||
throw error;
|
||||
console.error('获取赛事列表失败:', {
|
||||
status: error.response?.status,
|
||||
data: error.response?.data,
|
||||
message: error.message
|
||||
})
|
||||
throw error
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新赛事
|
||||
* @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) => {
|
||||
|
||||
// 更新赛事
|
||||
export const updateTournament = async (id, data) => {
|
||||
try {
|
||||
const response = await axiosInstance.put(`/tournament/update/${id}`, tournamentData);
|
||||
return response.data;
|
||||
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
|
||||
} catch (error) {
|
||||
console.error('更新赛事失败:', error);
|
||||
throw 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
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除赛事
|
||||
* @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
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加玩家报名
|
||||
* @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) => {
|
||||
|
||||
// 添加报名结果
|
||||
export const addSignUpResult = async (data) => {
|
||||
try {
|
||||
const response = await axiosInstance.post('/tournament/signup/add', signupData);
|
||||
return response.data;
|
||||
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
|
||||
} catch (error) {
|
||||
console.error('添加报名失败:', error);
|
||||
throw error;
|
||||
console.error('请求错误:', error)
|
||||
if (error.response?.data?.detail) {
|
||||
throw new Error(error.response.data.detail)
|
||||
}
|
||||
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('获取报名结果列表失败:', error);
|
||||
throw error;
|
||||
console.error('获取参赛结果列表失败:', {
|
||||
status: error.response?.status,
|
||||
data: error.response?.data,
|
||||
message: error.message
|
||||
})
|
||||
throw error
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新报名结果
|
||||
* @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) => {
|
||||
// 更新参赛结果
|
||||
export const updateSignUpResult = async (id, data) => {
|
||||
try {
|
||||
const response = await axiosInstance.put(`/tournament/signup_result/update/${id}`, resultData);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('更新报名结果失败:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
// // 更新报名信息 (这部分逻辑根据您的要求被注释掉)
|
||||
// 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('报名结果更新成功')
|
||||
|
||||
/**
|
||||
* 删除报名结果
|
||||
* @param {number} id - 结果ID
|
||||
* @returns {Promise<Object>} 返回删除报名结果的响应数据
|
||||
*/
|
||||
return { success: true }
|
||||
} catch (error) {
|
||||
console.error('更新参赛结果失败:', {
|
||||
status: error.response?.status,
|
||||
data: error.response?.data,
|
||||
message: error.message
|
||||
})
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// 删除参赛选手
|
||||
export const deleteSignUpResult = async (id) => {
|
||||
try {
|
||||
const response = await axiosInstance.delete(`/tournament/signup_result/delete/${id}`);
|
||||
return response.data;
|
||||
// 删除报名结果
|
||||
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 }
|
||||
} catch (error) {
|
||||
console.error('删除报名结果失败:', error);
|
||||
throw error;
|
||||
console.error('删除参赛选手失败:', {
|
||||
status: error.response?.status,
|
||||
data: error.response?.data,
|
||||
message: error.message
|
||||
})
|
||||
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('报名失败,请检查网络连接后重试')
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1024 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 2.3 MiB |
@ -1,19 +0,0 @@
|
||||
### 后端
|
||||
## V.2025.07.30.1
|
||||
|
||||
1、修改服务器地址
|
||||
|
||||
|
||||
------
|
||||
### 前端
|
||||
|
||||
## v.1.0.1
|
||||
1、谢谢你gemini3 pro,改了下赛事样式
|
||||
|
||||
|
||||
## v.1.0.0
|
||||
|
||||
1、添加版本信息页面
|
||||
|
||||
2、修改服务器地址
|
||||
|
||||
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" :title="`积分: ${item.points}`">{{ item.score }}</div>
|
||||
<div class="player-score">{{ item.score }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -31,7 +31,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, watch } from 'vue'
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { getSignUpResultList } from '@/api/tournament'
|
||||
|
||||
const props = defineProps({
|
||||
@ -46,77 +46,31 @@ const rankData = ref([])
|
||||
const fetchRankData = async () => {
|
||||
try {
|
||||
const response = await getSignUpResultList()
|
||||
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: []
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
// 筛选当前赛事的玩家并按胜场数排序
|
||||
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.totalWin !== a.totalWin) {
|
||||
return b.totalWin - a.totalWin
|
||||
// 首先按胜场数排序
|
||||
if (b.win !== a.win) {
|
||||
return b.win - a.win
|
||||
}
|
||||
// 如果胜利数相同,按失败数排序(失败数少的排名靠前)
|
||||
return a.totalLose - b.totalLose
|
||||
// 如果胜场数相同,则按负场数排序(负场数少的排前面)
|
||||
return a.lose - b.lose
|
||||
})
|
||||
.map((player, index) => ({
|
||||
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(',')
|
||||
}))
|
||||
...player,
|
||||
rank: index + 1
|
||||
}))
|
||||
|
||||
console.log('RankContestant 最终排名结果:', sortedPlayers)
|
||||
rankData.value = sortedPlayers
|
||||
rankData.value = results
|
||||
} catch (error) {
|
||||
console.error('获取排名数据失败:', error)
|
||||
}
|
||||
@ -125,13 +79,6 @@ 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
@ -1,112 +0,0 @@
|
||||
<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,10 +86,8 @@
|
||||
<span class="error-message" v-if="usernameError">{{ usernameError }}</span>
|
||||
</div>
|
||||
<div class="login-button">
|
||||
<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 type="submit" :disabled="isSubmitting">
|
||||
{{ isSubmitting ? '提交中...' : '重置密码' }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="register-link">
|
||||
@ -113,7 +111,7 @@
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import {getCaptcha, requestResetPassword, getUserByInfo} from '../api/login'
|
||||
import {getCaptcha, forgetPassword, requestResetPassword, getUserByInfo} from '../api/login'
|
||||
import ErrorDialog from './ErrorDialog.vue'
|
||||
import SuccessDialog from './SuccessDialog.vue'
|
||||
|
||||
@ -134,8 +132,6 @@ const uuidError = ref('')
|
||||
|
||||
// 状态
|
||||
const isSubmitting = ref(false)
|
||||
const cooldown = ref(0)
|
||||
let cooldownTimer = null
|
||||
|
||||
// 错误弹窗相关
|
||||
const showError = ref(false)
|
||||
@ -222,23 +218,17 @@ 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,8 +16,7 @@ const routes = [
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'Home',
|
||||
component: () => import('@/views/index/Home.vue')
|
||||
redirect: '/maps'
|
||||
},
|
||||
{
|
||||
path: 'demands',
|
||||
@ -49,47 +48,33 @@ const routes = [
|
||||
path: 'weapon-match',
|
||||
name: 'WeaponMatch',
|
||||
component: () => import('@/views/weapon/WeaponMatch.vue'),
|
||||
// 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}
|
||||
meta: { requiresAuth: true, requiredPrivilege: ['lv-admin','lv-mod'] }
|
||||
},
|
||||
{
|
||||
path: 'competition',
|
||||
name: 'Competition',
|
||||
component: () => import('@/views/competition/Competition.vue'),
|
||||
component: () => import('@/views/index/Competition.vue'),
|
||||
// meta: { requiresAuth: true, requiredPrivilege: ['lv-admin','lv-competitor'] }
|
||||
meta: { requiresAuth: true}
|
||||
},
|
||||
{
|
||||
path: 'competition/add',
|
||||
name: 'AddCompetition',
|
||||
component: () => import('@/views/competition/AddContestant.vue'),
|
||||
component: () => import('@/views/index/AddContestant.vue'),
|
||||
meta: { requiresAuth: true }
|
||||
},
|
||||
{
|
||||
path: 'competition/detail',
|
||||
name: 'CompetitionDetail',
|
||||
component: () => import('@/views/competition/CompetitionDetail.vue'),
|
||||
component: () => import('@/views/index/CompetitionDetail.vue'),
|
||||
meta: { requiresAuth: true }
|
||||
},
|
||||
{
|
||||
path: 'competition/signup',
|
||||
name: 'CompetitionSignUp',
|
||||
component: () => import('@/views/competition/CompetitionSignUp.vue'),
|
||||
component: () => import('@/views/index/CompetitionSignUp.vue'),
|
||||
meta: { requiresAuth: true }
|
||||
},
|
||||
// {
|
||||
// path: 'competition',
|
||||
// name: 'Competition',
|
||||
// component: () => import('@/views/competition/Competition.vue'),
|
||||
// meta: { requiresAuth: true}
|
||||
// },
|
||||
{
|
||||
path: 'editors-maps',
|
||||
name: 'EditorsMaps',
|
||||
@ -104,18 +89,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'] }
|
||||
},
|
||||
{
|
||||
path: 'version',
|
||||
name: 'VersionInfo',
|
||||
component: () => import('@/views/index/VersionInfo.vue')
|
||||
meta: { requiredPrivilege: ['lv-admin','lv-mod','lv-map','lv-competitor'] }
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -183,7 +163,7 @@ router.beforeEach(async (to, from, next) => {
|
||||
// 登录校验:如果页面需要登录但本地没有有效 token,则跳转登录页
|
||||
if (requiresAuth && !hasValidToken()) {
|
||||
return next({
|
||||
path: '/',
|
||||
path: '/maps',
|
||||
query: { redirect: to.fullPath }
|
||||
})
|
||||
}
|
||||
@ -194,11 +174,11 @@ router.beforeEach(async (to, from, next) => {
|
||||
if (!user || !hasPrivilegeWithTemp(user, requiredPrivilege)) {
|
||||
// 通过事件总线通知弹窗
|
||||
eventBus.emit('no-privilege')
|
||||
return next({ path: '/', replace: true })
|
||||
return next({ path: '/maps', replace: true })
|
||||
}
|
||||
}
|
||||
|
||||
return next();
|
||||
});
|
||||
|
||||
export default router
|
||||
export default router
|
||||
@ -98,6 +98,7 @@ 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,14 +86,11 @@ 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,loginBg4, loginBg5]
|
||||
const images = [loginBg, loginBg1, loginBg3]
|
||||
const randomIndex = Math.floor(Math.random() * images.length)
|
||||
const bgImg = ref(images[randomIndex])
|
||||
|
||||
@ -152,17 +149,7 @@ const handleResetPassword = async () => {
|
||||
// 调用重置密码API
|
||||
await resetPassword(token, newPassword.value)
|
||||
|
||||
// 密码重置成功后显示提示并跳转到登录页
|
||||
showSuccessMessage('密码重置成功!')
|
||||
|
||||
// 延迟跳转,让用户看到成功提示
|
||||
setTimeout(() => {
|
||||
if (hasValidToken()) {
|
||||
logoutUser()
|
||||
}
|
||||
router.push('/backend/login')
|
||||
}, 1500)
|
||||
|
||||
showSuccessMessage('密码重置成功!请使用新密码登录。')
|
||||
} catch (error) {
|
||||
console.error('重置密码失败:', error)
|
||||
const errorMessage = error.response?.data?.detail || error.response?.data?.message || error.message || '重置密码失败,请重试'
|
||||
@ -178,11 +165,7 @@ const handleSuccessClose = () => {
|
||||
}
|
||||
|
||||
const goToHome = () => {
|
||||
if (hasValidToken()) {
|
||||
logoutUser()
|
||||
}
|
||||
router.push('/backend/login')
|
||||
// router.push('/')
|
||||
router.push('/')
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
@ -27,9 +27,6 @@
|
||||
<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>
|
||||
@ -74,7 +71,6 @@
|
||||
<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>
|
||||
@ -87,7 +83,6 @@ 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,13 +37,11 @@ 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, loginBg4,loginBg5]
|
||||
const images = [loginBg, loginBg1,loginBg3]
|
||||
const randomIndex = Math.floor(Math.random() * images.length)
|
||||
const bgImg = ref(images[randomIndex])
|
||||
|
||||
|
||||
@ -1,974 +0,0 @@
|
||||
<template>
|
||||
<div class="competition-detail-container">
|
||||
<!-- 顶部横幅 -->
|
||||
<div class="detail-header">
|
||||
<div class="header-content">
|
||||
<div class="title-section">
|
||||
<button class="back-btn" @click="goBack">
|
||||
<i class="fas fa-arrow-left"></i>
|
||||
</button>
|
||||
<h1>{{ competition.name }}</h1>
|
||||
<span class="status-badge" :class="competition.status">
|
||||
{{ formatStatus(competition.status) }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="info-grid">
|
||||
<div class="info-item">
|
||||
<i class="fas fa-trophy"></i>
|
||||
<span>赛制: {{ competition.format }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<i class="fas fa-user-tie"></i>
|
||||
<span>举办方: {{ competition.organizer }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<i class="fas fa-calendar-alt"></i>
|
||||
<span>时间: {{ formatDate(competition.start_time) }} - {{ formatDate(competition.end_time) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 主要内容区 -->
|
||||
<div class="main-content">
|
||||
<!-- 侧边导航/标签页 -->
|
||||
<div class="tabs-nav">
|
||||
<button
|
||||
v-for="tab in tabs"
|
||||
:key="tab.id"
|
||||
class="tab-btn"
|
||||
:class="{ active: currentTab === tab.id }"
|
||||
@click="currentTab = tab.id"
|
||||
>
|
||||
<i :class="tab.icon"></i>
|
||||
{{ tab.name }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 标签页内容 -->
|
||||
<div class="tab-content">
|
||||
<!-- 概览 -->
|
||||
<div v-if="currentTab === 'overview'" class="tab-pane fade-in">
|
||||
<div class="overview-card">
|
||||
<h2>赛事公告</h2>
|
||||
<div class="markdown-body" v-html="renderedDescription"></div>
|
||||
</div>
|
||||
|
||||
<div class="overview-card" v-if="competition.status === 'finish'">
|
||||
<h2>最终排名</h2>
|
||||
<RankContestant :tournamentId="competition.id" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 对阵图 -->
|
||||
<div v-if="currentTab === 'bracket'" class="tab-pane fade-in">
|
||||
<div v-if="competition.status === 'prepare'" class="empty-state">
|
||||
<i class="fas fa-hourglass-start"></i>
|
||||
<p>比赛尚未开始,请等待组织者生成对阵图</p>
|
||||
<button v-if="isOrganizer" class="btn-primary" @click="startTournament">
|
||||
开始比赛并生成对阵
|
||||
</button>
|
||||
</div>
|
||||
<div v-else class="bracket-wrapper">
|
||||
<TournamentBracket
|
||||
v-if="competition.format === '单败淘汰'"
|
||||
:tournamentId="competition.id"
|
||||
@refreshPlayers="fetchRegisteredPlayers"
|
||||
/>
|
||||
<DoubleEliminationBracket
|
||||
v-else-if="competition.format === '双败淘汰'"
|
||||
:tournamentId="competition.id"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 参赛人员 -->
|
||||
<div v-if="currentTab === 'participants'" class="tab-pane fade-in">
|
||||
<div class="participants-header">
|
||||
<h2>参赛选手 ({{ registeredPlayers.length }})</h2>
|
||||
<div class="actions" v-if="isOrganizer && competition.status === 'prepare'">
|
||||
<button class="btn-secondary" @click="shufflePlayers" title="随机打乱顺序">
|
||||
<i class="fas fa-random"></i> 随机种子
|
||||
</button>
|
||||
<button class="btn-primary" @click="openAddPlayerModal">
|
||||
<i class="fas fa-plus"></i> 添加选手
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="participants-list">
|
||||
<div v-for="(player, index) in registeredPlayers" :key="player.id" class="participant-card">
|
||||
<div class="player-rank">种子 #{{ index + 1 }}</div>
|
||||
<div class="player-info">
|
||||
<div class="player-name">{{ player.sign_name }}</div>
|
||||
<div class="player-team" v-if="player.team_name">{{ player.team_name }}</div>
|
||||
</div>
|
||||
<div class="player-actions" v-if="isOrganizer && competition.status === 'prepare'">
|
||||
<button class="icon-btn" @click="movePlayerUp(index)" :disabled="index === 0" title="上移">
|
||||
<i class="fas fa-arrow-up"></i>
|
||||
</button>
|
||||
<button class="icon-btn" @click="movePlayerDown(index)" :disabled="index === registeredPlayers.length - 1" title="下移">
|
||||
<i class="fas fa-arrow-down"></i>
|
||||
</button>
|
||||
<button class="icon-btn delete" @click="confirmRemovePlayer(player)">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 设置 (仅组织者) -->
|
||||
<div v-if="currentTab === 'settings' && isOrganizer" class="tab-pane fade-in">
|
||||
<div class="settings-card">
|
||||
<h2>赛事管理</h2>
|
||||
<form @submit.prevent="handleUpdateTournament">
|
||||
<div class="setting-group">
|
||||
<label>赛事名称</label>
|
||||
<input v-model="editingCompetition.name" type="text" required class="form-input" />
|
||||
</div>
|
||||
|
||||
<div class="setting-group">
|
||||
<label>举办方</label>
|
||||
<input v-model="editingCompetition.organizer" type="text" required class="form-input" />
|
||||
</div>
|
||||
|
||||
<div class="setting-group">
|
||||
<label>开始时间</label>
|
||||
<input v-model="editingCompetition.start_time" type="date" class="form-input" />
|
||||
</div>
|
||||
|
||||
<div class="setting-group">
|
||||
<label>结束时间</label>
|
||||
<input v-model="editingCompetition.end_time" type="date" class="form-input" />
|
||||
</div>
|
||||
|
||||
<div class="setting-group">
|
||||
<label>赛事状态</label>
|
||||
<select v-model="editingCompetition.status" class="form-select">
|
||||
<option value="prepare">筹备中</option>
|
||||
<option value="starting">进行中</option>
|
||||
<option value="finish">已结束</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="setting-actions">
|
||||
<button type="submit" class="btn-primary">
|
||||
<i class="fas fa-save"></i> 保存修改
|
||||
</button>
|
||||
<button type="button" class="btn-danger" @click="confirmDeleteTournament">
|
||||
<i class="fas fa-trash-alt"></i> 删除赛事
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</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 v-if="showDeleteConfirm" class="modal-overlay">
|
||||
<div class="modal-content">
|
||||
<h3>确认删除</h3>
|
||||
<p>确定要删除该赛事吗?此操作不可恢复。</p>
|
||||
<div class="modal-actions">
|
||||
<button class="btn-secondary" @click="showDeleteConfirm = false">取消</button>
|
||||
<button class="btn-danger" @click="handleDeleteTournament">确认删除</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 添加选手弹窗 -->
|
||||
<div v-if="showAddPlayerModal" class="modal-overlay">
|
||||
<div class="modal-content">
|
||||
<h3>添加选手</h3>
|
||||
<div class="form-group">
|
||||
<label>选手名称</label>
|
||||
<input v-model="newPlayerName" placeholder="请输入选手名称" class="form-input" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>QQ号 (可选)</label>
|
||||
<input v-model="newPlayerQQ" placeholder="请输入选手QQ号" class="form-input" />
|
||||
</div>
|
||||
<div class="modal-actions">
|
||||
<button class="btn-secondary" @click="showAddPlayerModal = false">取消</button>
|
||||
<button class="btn-primary" @click="handleAddPlayer">添加</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, computed, watch } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { marked } from 'marked'
|
||||
import {
|
||||
getTournamentList,
|
||||
updateTournament,
|
||||
deleteTournament,
|
||||
getSignUpList,
|
||||
deleteSignUp,
|
||||
addSignUpResult,
|
||||
getSignUpResultList,
|
||||
updateSignUpResult,
|
||||
addSignUp
|
||||
} from '@/api/tournament'
|
||||
import { getStoredUser } from '@/utils/jwt.js'
|
||||
import TournamentBracket from '@/components/TournamentBracket.vue'
|
||||
import DoubleEliminationBracket from '@/components/DoubleEliminationBracket.vue'
|
||||
import RankContestant from '@/components/RankContestant.vue'
|
||||
import SuccessDialog from '@/components/SuccessDialog.vue'
|
||||
import ErrorDialog from '@/components/ErrorDialog.vue'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
// 状态变量
|
||||
const competition = ref({})
|
||||
const editingCompetition = ref({}) // 用于编辑的副本
|
||||
const registeredPlayers = ref([])
|
||||
const isOrganizer = ref(false)
|
||||
const isLoading = ref(false)
|
||||
const currentTab = ref('overview')
|
||||
const showDeleteConfirm = ref(false)
|
||||
const showAddPlayerModal = ref(false)
|
||||
const newPlayerName = ref('')
|
||||
const newPlayerQQ = ref('')
|
||||
|
||||
// 弹窗状态
|
||||
const successDialog = ref({ visible: false, message: '' })
|
||||
const errorDialog = ref({ visible: false, message: '' })
|
||||
|
||||
// 标签页配置
|
||||
const tabs = [
|
||||
{ id: 'overview', name: '概览', icon: 'fas fa-info-circle' },
|
||||
{ id: 'bracket', name: '对阵图', icon: 'fas fa-sitemap' },
|
||||
{ id: 'participants', name: '参赛人员', icon: 'fas fa-users' },
|
||||
{ id: 'settings', name: '设置', icon: 'fas fa-cog' }
|
||||
]
|
||||
|
||||
// Markdown 渲染
|
||||
const renderedDescription = computed(() => {
|
||||
return competition.value.description ? marked(competition.value.description) : '暂无描述'
|
||||
})
|
||||
|
||||
// 初始化
|
||||
onMounted(async () => {
|
||||
await fetchTournamentDetail()
|
||||
})
|
||||
|
||||
// 监听 competition 变化,更新 editingCompetition
|
||||
watch(competition, (newVal) => {
|
||||
if (newVal) {
|
||||
editingCompetition.value = { ...newVal }
|
||||
// 格式化日期以适应 input[type="date"]
|
||||
if (editingCompetition.value.start_time) {
|
||||
editingCompetition.value.start_time = formatDateForInput(editingCompetition.value.start_time)
|
||||
}
|
||||
if (editingCompetition.value.end_time) {
|
||||
editingCompetition.value.end_time = formatDateForInput(editingCompetition.value.end_time)
|
||||
}
|
||||
}
|
||||
}, { deep: true })
|
||||
|
||||
// 返回上一页
|
||||
const goBack = () => {
|
||||
router.push('/competition')
|
||||
}
|
||||
|
||||
// 获取赛事详情
|
||||
const fetchTournamentDetail = async () => {
|
||||
try {
|
||||
isLoading.value = true
|
||||
const tournamentId = parseInt(route.query.id)
|
||||
const data = await getTournamentList()
|
||||
const tournament = data.find(item => item.id === tournamentId)
|
||||
|
||||
if (tournament) {
|
||||
competition.value = {
|
||||
...tournament,
|
||||
format: formatType(tournament.format)
|
||||
}
|
||||
|
||||
// 权限判断
|
||||
const user = getStoredUser()
|
||||
if (user && user.qq_code && tournament.qq_code) {
|
||||
isOrganizer.value = String(user.qq_code) === String(tournament.qq_code)
|
||||
}
|
||||
|
||||
// 获取报名玩家
|
||||
await fetchRegisteredPlayers()
|
||||
} else {
|
||||
errorDialog.value = { visible: true, message: '未找到赛事信息' }
|
||||
router.push('/competition')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取赛事详情失败:', error)
|
||||
errorDialog.value = { visible: true, message: '获取赛事详情失败' }
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 获取报名玩家列表
|
||||
const fetchRegisteredPlayers = async () => {
|
||||
try {
|
||||
const res = await getSignUpList()
|
||||
// 过滤出当前赛事的报名者
|
||||
registeredPlayers.value = res.filter(item =>
|
||||
String(item.tournament_id) === String(competition.value.id)
|
||||
)
|
||||
} catch (error) {
|
||||
console.error('获取报名列表失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 格式化赛制
|
||||
const formatType = (type) => {
|
||||
const map = {
|
||||
'single_elimination': '单败淘汰',
|
||||
'double_elimination': '双败淘汰',
|
||||
'round_robin': '循环赛'
|
||||
}
|
||||
return map[type] || type
|
||||
}
|
||||
|
||||
// 格式化日期 (用于显示)
|
||||
const formatDate = (dateStr) => {
|
||||
if (!dateStr) return '待定'
|
||||
return new Date(dateStr).toLocaleDateString()
|
||||
}
|
||||
|
||||
// 格式化日期 (用于 input type="date")
|
||||
const formatDateForInput = (dateStr) => {
|
||||
if (!dateStr) return ''
|
||||
const date = new Date(dateStr)
|
||||
const year = date.getFullYear()
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(date.getDate()).padStart(2, '0')
|
||||
return `${year}-${month}-${day}`
|
||||
}
|
||||
|
||||
// 格式化状态
|
||||
const formatStatus = (status) => {
|
||||
const map = {
|
||||
'prepare': '筹备中',
|
||||
'starting': '进行中',
|
||||
'finish': '已结束'
|
||||
}
|
||||
return map[status] || status
|
||||
}
|
||||
|
||||
// 随机打乱选手
|
||||
const shufflePlayers = () => {
|
||||
registeredPlayers.value.sort(() => Math.random() - 0.5)
|
||||
successDialog.value = { visible: true, message: '选手顺序已随机打乱' }
|
||||
}
|
||||
|
||||
// 上移选手
|
||||
const movePlayerUp = (index) => {
|
||||
if (index > 0) {
|
||||
const temp = registeredPlayers.value[index]
|
||||
registeredPlayers.value[index] = registeredPlayers.value[index - 1]
|
||||
registeredPlayers.value[index - 1] = temp
|
||||
}
|
||||
}
|
||||
|
||||
// 下移选手
|
||||
const movePlayerDown = (index) => {
|
||||
if (index < registeredPlayers.value.length - 1) {
|
||||
const temp = registeredPlayers.value[index]
|
||||
registeredPlayers.value[index] = registeredPlayers.value[index + 1]
|
||||
registeredPlayers.value[index + 1] = temp
|
||||
}
|
||||
}
|
||||
|
||||
// 打开添加选手弹窗
|
||||
const openAddPlayerModal = () => {
|
||||
newPlayerName.value = ''
|
||||
newPlayerQQ.value = ''
|
||||
showAddPlayerModal.value = true
|
||||
}
|
||||
|
||||
// 添加选手
|
||||
const handleAddPlayer = async () => {
|
||||
if (!newPlayerName.value.trim()) {
|
||||
errorDialog.value = { visible: true, message: '请输入选手名称' }
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await addSignUp({
|
||||
tournament_id: competition.value.id,
|
||||
tournament_name: competition.value.name,
|
||||
sign_name: newPlayerName.value,
|
||||
qq: newPlayerQQ.value || '', // 关联QQ
|
||||
contact_info: ''
|
||||
})
|
||||
await fetchRegisteredPlayers()
|
||||
showAddPlayerModal.value = false
|
||||
successDialog.value = { visible: true, message: '添加成功' }
|
||||
} catch (error) {
|
||||
console.error('添加选手失败:', error)
|
||||
errorDialog.value = { visible: true, message: '添加选手失败' }
|
||||
}
|
||||
}
|
||||
|
||||
// 确认删除选手
|
||||
const confirmRemovePlayer = async (player) => {
|
||||
if (confirm(`确定要移除选手 ${player.sign_name} 吗?`)) {
|
||||
try {
|
||||
await deleteSignUp(player.id)
|
||||
await fetchRegisteredPlayers()
|
||||
successDialog.value = { visible: true, message: '移除成功' }
|
||||
} catch (error) {
|
||||
console.error('移除选手失败:', error)
|
||||
errorDialog.value = { visible: true, message: '移除选手失败' }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 开始比赛
|
||||
const startTournament = async () => {
|
||||
if (registeredPlayers.value.length < 2) {
|
||||
errorDialog.value = { visible: true, message: '至少需要2名选手才能开始比赛' }
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
// 1. 更新赛事状态
|
||||
await updateTournament(competition.value.id, {
|
||||
...competition.value,
|
||||
status: 'starting'
|
||||
})
|
||||
competition.value.status = 'starting'
|
||||
|
||||
// 2. 生成对阵
|
||||
await generateOpponentsForAllPlayers()
|
||||
|
||||
successDialog.value = { visible: true, message: '比赛已开始,对阵图生成中...' }
|
||||
currentTab.value = 'bracket'
|
||||
} catch (error) {
|
||||
console.error('开始比赛失败:', error)
|
||||
errorDialog.value = { visible: true, message: '开始比赛失败' }
|
||||
}
|
||||
}
|
||||
|
||||
// 生成标准种子顺序
|
||||
const getStandardBracketOrder = (n) => {
|
||||
if (n <= 0) return [];
|
||||
let rounds = Math.ceil(Math.log2(n));
|
||||
let bracket = [1, 2];
|
||||
for (let i = 1; i < rounds; i++) {
|
||||
let nextBracket = [];
|
||||
let sum = Math.pow(2, i + 1) + 1;
|
||||
for (let j = 0; j < bracket.length; j++) {
|
||||
nextBracket.push(bracket[j]);
|
||||
nextBracket.push(sum - bracket[j]);
|
||||
}
|
||||
bracket = nextBracket;
|
||||
}
|
||||
return bracket;
|
||||
}
|
||||
|
||||
// 生成对阵逻辑 (基于当前列表顺序作为种子顺序)
|
||||
const generateOpponentsForAllPlayers = async () => {
|
||||
const players = registeredPlayers.value
|
||||
const totalPlayers = players.length
|
||||
|
||||
// 计算需要的总槽位数(最近的2的幂)
|
||||
const nextPowerOf2 = Math.pow(2, Math.ceil(Math.log2(totalPlayers)))
|
||||
|
||||
// 获取标准种子顺序
|
||||
const order = getStandardBracketOrder(totalPlayers)
|
||||
|
||||
// 遍历种子顺序,每两个一组生成对阵
|
||||
for (let i = 0; i < order.length; i += 2) {
|
||||
const seedA = order[i]
|
||||
const seedB = order[i+1]
|
||||
|
||||
// 注意:seed是1-based,数组是0-based
|
||||
const pA = players[seedA - 1]
|
||||
const pB = players[seedB - 1]
|
||||
|
||||
if (pA && pB) {
|
||||
// pA vs pB
|
||||
await createMatchRecord(pA, pB.sign_name)
|
||||
await createMatchRecord(pB, pA.sign_name)
|
||||
} else if (pA && !pB) {
|
||||
// pA vs 轮空
|
||||
await createMatchRecord(pA, '轮空')
|
||||
} else if (!pA && pB) {
|
||||
// pB vs 轮空 (理论上不应该发生,因为我们按顺序填充)
|
||||
await createMatchRecord(pB, '轮空')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 创建比赛记录辅助函数
|
||||
const createMatchRecord = async (player, rivalName) => {
|
||||
try {
|
||||
await addSignUpResult({
|
||||
tournament_id: competition.value.id,
|
||||
tournament_name: competition.value.name,
|
||||
team_name: player.team_name || null,
|
||||
sign_name: player.sign_name,
|
||||
win: '0',
|
||||
lose: '0',
|
||||
status: 'tie',
|
||||
round: '0',
|
||||
rival_name: rivalName,
|
||||
qq_code: player.qq || '' // 关联QQ
|
||||
})
|
||||
} catch (error) {
|
||||
console.error(`创建比赛记录失败 (${player.sign_name} vs ${rivalName}):`, error)
|
||||
}
|
||||
}
|
||||
|
||||
// 更新赛事信息
|
||||
const handleUpdateTournament = async () => {
|
||||
try {
|
||||
// 反向映射 format
|
||||
const formatMap = {
|
||||
'单败淘汰': 'single_elimination',
|
||||
'双败淘汰': 'double_elimination',
|
||||
'循环赛': 'round_robin'
|
||||
}
|
||||
const rawFormat = formatMap[editingCompetition.value.format] || editingCompetition.value.format
|
||||
|
||||
const updateData = {
|
||||
...editingCompetition.value,
|
||||
format: rawFormat,
|
||||
// 确保日期格式正确 (YYYY/MM/DD)
|
||||
start_time: editingCompetition.value.start_time ? editingCompetition.value.start_time.replace(/-/g, '/') : '',
|
||||
end_time: editingCompetition.value.end_time ? editingCompetition.value.end_time.replace(/-/g, '/') : ''
|
||||
}
|
||||
|
||||
await updateTournament(competition.value.id, updateData)
|
||||
|
||||
// 更新本地状态
|
||||
competition.value = { ...editingCompetition.value }
|
||||
successDialog.value = { visible: true, message: '赛事信息已更新' }
|
||||
} catch (error) {
|
||||
console.error('更新赛事失败:', error)
|
||||
errorDialog.value = { visible: true, message: '更新赛事失败' }
|
||||
}
|
||||
}
|
||||
|
||||
// 确认删除赛事
|
||||
const confirmDeleteTournament = () => {
|
||||
showDeleteConfirm.value = true
|
||||
}
|
||||
|
||||
// 删除赛事
|
||||
const handleDeleteTournament = async () => {
|
||||
try {
|
||||
await deleteTournament(competition.value.id)
|
||||
showDeleteConfirm.value = false
|
||||
router.push('/competition')
|
||||
} catch (error) {
|
||||
console.error('删除赛事失败:', error)
|
||||
errorDialog.value = { visible: true, message: '删除赛事失败' }
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.competition-detail-container {
|
||||
min-height: 100vh;
|
||||
background: #f5f7fa;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.detail-header {
|
||||
background: white;
|
||||
padding: 20px 40px;
|
||||
box-shadow: 0 2px 12px rgba(0,0,0,0.05);
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.header-content {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.title-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.back-btn {
|
||||
border: none;
|
||||
background: none;
|
||||
font-size: 20px;
|
||||
color: #606266;
|
||||
cursor: pointer;
|
||||
padding: 5px;
|
||||
border-radius: 50%;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.back-btn:hover {
|
||||
background: #f0f2f5;
|
||||
color: #409EFF;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
padding: 4px 12px;
|
||||
border-radius: 20px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.status-badge.prepare { background: #e6a23c; color: white; }
|
||||
.status-badge.starting { background: #67c23a; color: white; }
|
||||
.status-badge.finish { background: #909399; color: white; }
|
||||
|
||||
.info-grid {
|
||||
display: flex;
|
||||
gap: 30px;
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
max-width: 1200px;
|
||||
margin: 20px auto;
|
||||
width: 100%;
|
||||
gap: 20px;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.tabs-nav {
|
||||
width: 200px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.tab-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 12px 20px;
|
||||
border: none;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
color: #606266;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
text-align: left;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.tab-btn:hover {
|
||||
background: #f5f7fa;
|
||||
color: #409EFF;
|
||||
}
|
||||
|
||||
.tab-btn.active {
|
||||
background: #409EFF;
|
||||
color: white;
|
||||
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.3);
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
flex: 1;
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
padding: 30px;
|
||||
min-height: 600px;
|
||||
box-shadow: 0 2px 12px rgba(0,0,0,0.05);
|
||||
}
|
||||
|
||||
.tab-pane {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.fade-in {
|
||||
animation: fadeIn 0.3s ease;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
.overview-card {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.overview-card h2 {
|
||||
font-size: 18px;
|
||||
color: #303133;
|
||||
margin-bottom: 15px;
|
||||
border-left: 4px solid #409EFF;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 400px;
|
||||
color: #909399;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.empty-state i {
|
||||
font-size: 48px;
|
||||
color: #dcdfe6;
|
||||
}
|
||||
|
||||
.bracket-wrapper {
|
||||
height: 100%;
|
||||
min-height: 600px;
|
||||
}
|
||||
|
||||
.participants-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.participants-header h2 {
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.participants-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.participant-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 15px;
|
||||
background: #f9fafc;
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 8px;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.participant-card:hover {
|
||||
border-color: #c6e2ff;
|
||||
background: #ecf5ff;
|
||||
}
|
||||
|
||||
.player-rank {
|
||||
width: 80px;
|
||||
font-weight: bold;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.player-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.player-name {
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.player-team {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.player-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.icon-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border: none;
|
||||
background: white;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
color: #606266;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.3s;
|
||||
border: 1px solid #dcdfe6;
|
||||
}
|
||||
|
||||
.icon-btn:hover:not(:disabled) {
|
||||
color: #409EFF;
|
||||
border-color: #c6e2ff;
|
||||
}
|
||||
|
||||
.icon-btn:disabled {
|
||||
color: #c0c4cc;
|
||||
cursor: not-allowed;
|
||||
background: #f5f7fa;
|
||||
}
|
||||
|
||||
.icon-btn.delete:hover {
|
||||
color: #F56C6C;
|
||||
border-color: #fbc4c4;
|
||||
}
|
||||
|
||||
.settings-card {
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.setting-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.setting-group label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
color: #606266;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.form-input, .form-select {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
transition: border-color 0.3s;
|
||||
}
|
||||
|
||||
.form-input:focus, .form-select:focus {
|
||||
border-color: #409EFF;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.setting-actions {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.btn-primary, .btn-secondary, .btn-danger {
|
||||
padding: 8px 16px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: #409EFF;
|
||||
color: white;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: #66b1ff;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: white;
|
||||
border: 1px solid #dcdfe6;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
border-color: #409EFF;
|
||||
color: #409EFF;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background: #F56C6C;
|
||||
color: white;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.btn-danger:hover {
|
||||
background: #f78989;
|
||||
}
|
||||
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
top: 0; left: 0; right: 0; bottom: 0;
|
||||
background: rgba(0,0,0,0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 2000;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background: white;
|
||||
width: 400px;
|
||||
padding: 30px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 20px rgba(0,0,0,0.15);
|
||||
}
|
||||
|
||||
.modal-content h3 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 20px;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.modal-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.form-group input {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
||||
@ -324,13 +324,11 @@ function handlePasswordChangeError(errorMessage) {
|
||||
<div class="app">
|
||||
<nav class="navbar">
|
||||
<div class="nav-container">
|
||||
<a href="/" class="nav-brand">红色警戒3工具平台</a>
|
||||
<!-- <div class="nav-brand">红色警戒3数据分析中心</div>-->
|
||||
<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>
|
||||
@ -345,16 +343,41 @@ 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>
|
||||
@ -363,7 +386,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>
|
||||
<!-- 公共信息区 一级菜单 -->
|
||||
@ -373,12 +396,8 @@ function handlePasswordChangeError(errorMessage) {
|
||||
<router-link to="/demands" class="nav-link" @click.prevent="handleNavClick('/demands')">办事大厅</router-link>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 版本更新 -->
|
||||
<div class="nav-dropdown">
|
||||
<router-link to="/version" class="nav-link">版本信息</router-link>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="nav-right" :class="{ active: showMobileMenu }">
|
||||
<router-link v-if="!isLoggedIn" to="/backend/login" class="nav-link login-btn">
|
||||
<i class="fas fa-user"></i>
|
||||
@ -418,17 +437,11 @@ function handlePasswordChangeError(errorMessage) {
|
||||
<div class="footer-top">
|
||||
<div class="footer-brand">
|
||||
<img src="../assets/logo.png" class="footer-logo">
|
||||
<span class="footer-title">红色警戒3工具平台</span>
|
||||
<span class="footer-title">红色警戒3数据分析中心</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="footer-bottom">
|
||||
<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>Byz解忧杂货铺</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>
|
||||
@ -440,12 +453,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"
|
||||
|
||||
@ -3,62 +3,61 @@
|
||||
<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">
|
||||
@ -101,28 +100,14 @@
|
||||
<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.js'
|
||||
import { addTournament, addSignUp, getTournamentList } from '@/api/tournament'
|
||||
import * as XLSX from 'xlsx'
|
||||
import SuccessDialog from '@/components/SuccessDialog.vue'
|
||||
import ErrorDialog from '@/components/ErrorDialog.vue'
|
||||
|
||||
const router = useRouter()
|
||||
const showExcelDialog = ref(false)
|
||||
@ -144,10 +129,6 @@ const formData = ref({
|
||||
description: ''
|
||||
})
|
||||
|
||||
// Dialog state
|
||||
const successDialog = ref({ visible: false, message: '' })
|
||||
const errorDialog = ref({ visible: false, message: '' })
|
||||
|
||||
// Excel赛事信息字段与表单字段的映射
|
||||
const excelFieldMap = {
|
||||
'赛事名称': 'name',
|
||||
@ -209,11 +190,11 @@ const handleSubmit = async () => {
|
||||
// 调用API添加赛事
|
||||
await addTournament(tournamentData)
|
||||
|
||||
successDialog.value = { visible: true, message: '添加赛事成功!' }
|
||||
alert('添加赛事成功!')
|
||||
router.push('/competition')
|
||||
} catch (error) {
|
||||
console.error('提交失败:', error)
|
||||
errorDialog.value = { visible: true, message: error.response?.data?.message || '添加赛事失败,请重试' }
|
||||
alert(error.response?.data?.message || '添加赛事失败,请重试')
|
||||
}
|
||||
}
|
||||
|
||||
@ -329,7 +310,7 @@ const processExcelFile = (file) => {
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
errorDialog.value = { visible: true, message: 'Excel文件格式错误,请检查文件内容' }
|
||||
alert('Excel文件格式错误,请检查文件内容')
|
||||
}
|
||||
}
|
||||
reader.readAsArrayBuffer(file)
|
||||
@ -359,7 +340,7 @@ const confirmImport = async () => {
|
||||
|
||||
try {
|
||||
await addTournament(tournamentData)
|
||||
successDialog.value = { visible: true, message: '赛事导入成功!' }
|
||||
alert('赛事导入成功!')
|
||||
|
||||
const tournamentList = await getTournamentList()
|
||||
console.log('获取到的赛事列表:', tournamentList)
|
||||
@ -396,16 +377,16 @@ const confirmImport = async () => {
|
||||
await addSignUp(signUpData)
|
||||
} catch (error) {
|
||||
console.error('报名失败:', error)
|
||||
errorDialog.value = { visible: true, message: `报名失败: ${row['队伍名称或者个人参赛名称']},${error.message}` }
|
||||
alert(`报名失败: ${row['队伍名称或者个人参赛名称']},${error.message}`)
|
||||
}
|
||||
}
|
||||
successDialog.value = { visible: true, message: '选手报名表导入完成!' }
|
||||
alert('选手报名表导入完成!')
|
||||
}
|
||||
|
||||
router.push('/competition')
|
||||
} catch (error) {
|
||||
console.error('导入失败:', error)
|
||||
errorDialog.value = { visible: true, message: error.response?.data?.message || '赛事导入失败,请重试' }
|
||||
alert(error.response?.data?.message || '赛事导入失败,请重试')
|
||||
}
|
||||
closeExcelDialog()
|
||||
}
|
||||
@ -34,7 +34,6 @@
|
||||
</div>
|
||||
<select v-model="filterStatus" @change="handleFilter" class="filter-select">
|
||||
<option value="all">全部状态</option>
|
||||
<option value="prepare">筹备中</option>
|
||||
<option value="ongoing">进行中</option>
|
||||
<option value="finished">已结束</option>
|
||||
</select>
|
||||
@ -50,60 +49,57 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 加载状态 -->
|
||||
<div v-if="isLoading" class="loading-container">
|
||||
<div class="loading-spinner"></div>
|
||||
<p>正在加载赛程数据...</p>
|
||||
</div>
|
||||
<div class="table-container" :class="{ 'loading': isLoading }">
|
||||
<table class="competition-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>序号</th>
|
||||
<th>赛程名称</th>
|
||||
<th>开始时间</th>
|
||||
<th>结束时间</th>
|
||||
<th>状态</th>
|
||||
<th>组织者</th>
|
||||
<th>QQ号</th>
|
||||
<th>赛制类型</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(competition, index) in filteredCompetitions"
|
||||
:key="index"
|
||||
class="competition-row"
|
||||
@click="handleView(competition)">
|
||||
<td>{{ index + 1 }}</td>
|
||||
<td class="competition-name">{{ competition.name }}</td>
|
||||
<td>{{ formatDate(competition.start_time) }}</td>
|
||||
<td>{{ formatDate(competition.end_time) }}</td>
|
||||
<td>
|
||||
<span :class="['status-tag', competition.status]">
|
||||
{{ competition.status === 'prepare' ? '筹备中' :
|
||||
competition.status === 'starting' ? '进行中' : '已结束' }}
|
||||
</span>
|
||||
</td>
|
||||
<td>{{ competition.organizer }}</td>
|
||||
<td>{{ competition.qq_code }}</td>
|
||||
<td>{{ competition.format === 'single' ? '单败淘汰' :
|
||||
competition.format === 'double' ? '双败淘汰' : '积分赛' }}</td>
|
||||
<td class="action-cell">
|
||||
<button class="action-btn view" @click.stop="handleSignUp(competition)" :disabled="competition.status === 'finish'">
|
||||
报名
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- 赛程卡片网格 -->
|
||||
<div v-else class="competition-grid">
|
||||
<div
|
||||
v-for="(competition, index) in filteredCompetitions"
|
||||
:key="index"
|
||||
class="competition-card"
|
||||
@click="handleView(competition)"
|
||||
>
|
||||
<div class="card-header">
|
||||
<div class="status-badge" :class="competition.status">
|
||||
{{ getStatusText(competition.status) }}
|
||||
</div>
|
||||
<div class="format-badge">{{ competition.format === 'single' ? '单败' : competition.format === 'double' ? '双败' : '积分' }}</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<h3 class="competition-title">{{ competition.name }}</h3>
|
||||
|
||||
<div class="info-row">
|
||||
<i class="fas fa-user-tie"></i>
|
||||
<span>主办方:{{ competition.organizer }}</span>
|
||||
</div>
|
||||
|
||||
<div class="info-row">
|
||||
<i class="fab fa-qq"></i>
|
||||
<span>QQ:{{ competition.qq_code }}</span>
|
||||
</div>
|
||||
|
||||
<div class="info-row time-row">
|
||||
<i class="far fa-calendar-alt"></i>
|
||||
<span>{{ formatDate(competition.start_time) }} - {{ formatDate(competition.end_time) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-footer">
|
||||
<button
|
||||
class="action-btn view"
|
||||
@click.stop="handleSignUp(competition)"
|
||||
:disabled="competition.status === 'finish'"
|
||||
:class="{ 'disabled': competition.status === 'finish' }"
|
||||
>
|
||||
{{ competition.status === 'finish' ? '已结束' : '立即报名' }}
|
||||
</button>
|
||||
</div>
|
||||
<!-- 加载状态 -->
|
||||
<div v-if="isLoading" class="loading-overlay">
|
||||
<i class="fas fa-spinner fa-spin"></i>
|
||||
<span>加载中...</span>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 空状态显示 -->
|
||||
<div v-if="filteredCompetitions.length === 0" class="empty-state">
|
||||
<div v-else-if="filteredCompetitions.length === 0" class="empty-state">
|
||||
<i class="fas fa-calendar-times"></i>
|
||||
<p>暂无赛程信息</p>
|
||||
</div>
|
||||
@ -114,7 +110,7 @@
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { getTournamentList } from '@/api/tournament.js'
|
||||
import { getTournamentList } from '@/api/tournament'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
@ -139,13 +135,9 @@ const filteredCompetitions = computed(() => {
|
||||
|
||||
// 状态过滤
|
||||
if (filterStatus.value !== 'all') {
|
||||
if (filterStatus.value === 'ongoing') {
|
||||
result = result.filter(comp => comp.status === 'starting')
|
||||
} else if (filterStatus.value === 'finished') {
|
||||
result = result.filter(comp => comp.status === 'finish')
|
||||
} else if (filterStatus.value === 'prepare') {
|
||||
result = result.filter(comp => comp.status === 'prepare')
|
||||
}
|
||||
result = result.filter(comp =>
|
||||
filterStatus.value === 'ongoing' ? comp.status === 'starting' : comp.status === 'finish'
|
||||
)
|
||||
}
|
||||
|
||||
return result
|
||||
@ -153,19 +145,9 @@ const filteredCompetitions = computed(() => {
|
||||
|
||||
// 方法
|
||||
const formatDate = (date) => {
|
||||
if (!date) return ''
|
||||
return date.replace(/\//g, '-')
|
||||
}
|
||||
|
||||
const getStatusText = (status) => {
|
||||
const map = {
|
||||
'prepare': '筹备中',
|
||||
'starting': '进行中',
|
||||
'finish': '已结束'
|
||||
}
|
||||
return map[status] || status
|
||||
}
|
||||
|
||||
const handleSearch = () => {
|
||||
// 搜索时重置过滤
|
||||
}
|
||||
@ -231,74 +213,60 @@ refreshCompetitions()
|
||||
|
||||
<style scoped>
|
||||
.competition-page {
|
||||
padding: 24px;
|
||||
padding: 16px;
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
min-height: 100vh;
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
margin-bottom: 24px;
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.page-header h1 {
|
||||
font-size: 28px;
|
||||
font-size: 22px;
|
||||
color: #1a237e;
|
||||
margin: 0 0 8px 0;
|
||||
font-weight: 600;
|
||||
margin: 0 0 6px 0;
|
||||
}
|
||||
|
||||
.header-subtitle {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.action-bar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 24px;
|
||||
margin-bottom: 16px;
|
||||
flex-wrap: wrap;
|
||||
gap: 16px;
|
||||
background: white;
|
||||
padding: 16px;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.left-actions,
|
||||
.right-actions {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
position: relative;
|
||||
width: 240px;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.search-box input {
|
||||
padding: 6px 10px 6px 28px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
font-size: 13px;
|
||||
width: 100%;
|
||||
padding: 8px 12px 8px 32px;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.search-box input:focus {
|
||||
border-color: #409EFF;
|
||||
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
|
||||
outline: none;
|
||||
max-width: 220px;
|
||||
}
|
||||
|
||||
.search-icon {
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
left: 8px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: #999;
|
||||
@ -306,259 +274,215 @@ refreshCompetitions()
|
||||
}
|
||||
|
||||
.filter-select {
|
||||
padding: 8px 12px;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
padding: 6px 10px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
font-size: 13px;
|
||||
background: white;
|
||||
min-width: 120px;
|
||||
min-width: 100px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.filter-select:focus {
|
||||
border-color: #409EFF;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.btn-common {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 8px 16px;
|
||||
font-size: 14px;
|
||||
gap: 5px;
|
||||
padding: 6px 12px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
border-radius: 6px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #b6d2ff;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.btn-gradient {
|
||||
background: linear-gradient(135deg, #71eaeb 0%, #416bdf 100%);
|
||||
background: linear-gradient(90deg, #71eaeb 0%, #416bdf 100%);
|
||||
color: white;
|
||||
box-shadow: 0 4px 12px rgba(65, 107, 223, 0.2);
|
||||
border: none;
|
||||
}
|
||||
|
||||
.btn-gradient:hover {
|
||||
background: linear-gradient(135deg, #416bdf 0%, #71eaeb 100%);
|
||||
background: linear-gradient(90deg, #416bdf 0%, #71eaeb 100%);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 6px 16px rgba(65, 107, 223, 0.3);
|
||||
}
|
||||
|
||||
.btn-light {
|
||||
background: #f0f7ff;
|
||||
background: white;
|
||||
color: #2563eb;
|
||||
border: 1px solid #dbeafe;
|
||||
}
|
||||
|
||||
.btn-light:hover {
|
||||
background: #dbeafe;
|
||||
background: #f5f7fa;
|
||||
border-color: #2563eb;
|
||||
}
|
||||
|
||||
/* 网格布局 */
|
||||
.competition-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.competition-card {
|
||||
.table-container {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
border: 1px solid #f0f0f0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 1px 8px rgba(0, 0, 0, 0.05);
|
||||
overflow-x: auto;
|
||||
margin-bottom: 20px;
|
||||
position: relative;
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
.competition-card:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.1);
|
||||
border-color: #e0e0e0;
|
||||
.competition-table {
|
||||
width: 100%;
|
||||
min-width: 800px;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #f5f5f5;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
padding: 4px 10px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.status-badge.prepare { background-color: #fff7e6; color: #fa8c16; border: 1px solid #ffd591; }
|
||||
.status-badge.starting { background-color: #f6ffed; color: #52c41a; border: 1px solid #b7eb8f; }
|
||||
.status-badge.finish { background-color: #f5f5f5; color: #8c8c8c; border: 1px solid #d9d9d9; }
|
||||
|
||||
.format-badge {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
background: #e6f7ff;
|
||||
padding: 2px 8px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid #bae7ff;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
padding: 20px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.competition-title {
|
||||
margin: 0 0 16px 0;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #1a237e;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.info-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 8px;
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.info-row i {
|
||||
width: 16px;
|
||||
text-align: center;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.time-row {
|
||||
margin-top: 12px;
|
||||
padding-top: 12px;
|
||||
border-top: 1px dashed #f0f0f0;
|
||||
.competition-table th,
|
||||
.competition-table td {
|
||||
padding: 12px;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.card-footer {
|
||||
padding: 16px;
|
||||
border-top: 1px solid #f5f5f5;
|
||||
text-align: center;
|
||||
.competition-table th {
|
||||
background-color: #f8f9fa;
|
||||
font-weight: 600;
|
||||
color: #1a237e;
|
||||
}
|
||||
|
||||
.competition-row {
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.competition-row:hover {
|
||||
background-color: #f0f7ff;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.competition-name {
|
||||
font-weight: 500;
|
||||
color: #1a237e;
|
||||
}
|
||||
|
||||
.status-tag {
|
||||
display: inline-block;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.status-tag.prepare { background-color: #e6a23c; color: #fff; }
|
||||
.status-tag.starting { background-color: #67c23a; color: #fff; }
|
||||
.status-tag.finish { background-color: #909399; color: #fff; }
|
||||
|
||||
.action-cell {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border-radius: 6px;
|
||||
padding: 5px 10px;
|
||||
border-radius: 4px;
|
||||
background: linear-gradient(90deg, #71eaeb 0%, #416bdf 100%);
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
font-size: 13px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.action-btn:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.action-btn.disabled {
|
||||
background: #f5f5f5;
|
||||
color: #999;
|
||||
cursor: not-allowed;
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
grid-column: 1 / -1;
|
||||
padding: 60px;
|
||||
padding: 30px;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
color: #909399;
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.empty-state i {
|
||||
font-size: 48px;
|
||||
margin-bottom: 16px;
|
||||
color: #d9d9d9;
|
||||
}
|
||||
|
||||
.loading-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 60px;
|
||||
color: #409EFF;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 3px solid #f3f3f3;
|
||||
border-top: 3px solid #409EFF;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.error-message {
|
||||
background-color: #fef0f0;
|
||||
color: #f56c6c;
|
||||
padding: 12px 16px;
|
||||
border-radius: 8px;
|
||||
padding: 10px 14px;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 20px;
|
||||
border: 1px solid #fde2e2;
|
||||
gap: 8px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.retry-btn {
|
||||
margin-left: auto;
|
||||
padding: 4px 12px;
|
||||
padding: 4px 10px;
|
||||
font-size: 12px;
|
||||
background: #f56c6c;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.loading-overlay {
|
||||
position: absolute;
|
||||
top: 0; left: 0; right: 0; bottom: 0;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
color: #409EFF;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.table-container.loading {
|
||||
opacity: 0.6;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.btn-common:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.action-btn:disabled {
|
||||
background: #e0e0e0 !important;
|
||||
color: #b0b0b0 !important;
|
||||
cursor: not-allowed;
|
||||
border: none;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.competition-page {
|
||||
padding: 16px;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.action-bar {
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.left-actions, .right-actions {
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
width: 100%;
|
||||
.table-container {
|
||||
margin: 0 -12px;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.filter-select {
|
||||
|
||||
.competition-table th, .competition-table td {
|
||||
padding: 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.search-box input, .filter-select {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.status-tag {
|
||||
font-size: 10px;
|
||||
padding: 2px 5px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
1258
src/views/index/CompetitionDetail.vue
Normal file
1258
src/views/index/CompetitionDetail.vue
Normal file
File diff suppressed because it is too large
Load Diff
@ -124,27 +124,13 @@
|
||||
<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, addSignUpResult} from '@/api/tournament.js'
|
||||
import SuccessDialog from '@/components/SuccessDialog.vue'
|
||||
import ErrorDialog from '@/components/ErrorDialog.vue'
|
||||
import { addSignUp } from '@/api/tournament'
|
||||
|
||||
defineOptions({
|
||||
name: 'CompetitionSignUp'
|
||||
@ -172,10 +158,6 @@ const signupForm = ref({
|
||||
qq: ''
|
||||
})
|
||||
|
||||
// Dialog state
|
||||
const successDialog = ref({ visible: false, message: '' })
|
||||
const errorDialog = ref({ visible: false, message: '' })
|
||||
|
||||
const formatDate = (date) => {
|
||||
if (!date) return ''
|
||||
// 将年/月/日格式转换为年-月-日格式显示
|
||||
@ -208,12 +190,12 @@ const handleSubmit = async () => {
|
||||
// 根据报名类型验证必填字段
|
||||
if (signupForm.value.type === 'teamname') {
|
||||
if (!signupForm.value.teamName || !signupForm.value.username) {
|
||||
errorDialog.value = { visible: true, message: '请填写完整的队伍信息' }
|
||||
alert('请填写完整的队伍信息')
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if (!signupForm.value.username) {
|
||||
errorDialog.value = { visible: true, message: '请填写完整的个人信息' }
|
||||
alert('请填写完整的个人信息')
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -221,67 +203,61 @@ const handleSubmit = async () => {
|
||||
// 验证 sign_name
|
||||
const signName = signupForm.value.username.trim()
|
||||
if (!signName) {
|
||||
errorDialog.value = { visible: true, message: '参赛人员名称不能为空' }
|
||||
alert('参赛人员名称不能为空')
|
||||
return
|
||||
}
|
||||
if (signName.length < 2) {
|
||||
errorDialog.value = { visible: true, message: '参赛人员名称至少需要2个字符' }
|
||||
alert('参赛人员名称至少需要2个字符')
|
||||
return
|
||||
}
|
||||
if (signName.length > 20) {
|
||||
errorDialog.value = { visible: true, message: '参赛人员名称不能超过20个字符' }
|
||||
alert('参赛人员名称不能超过20个字符')
|
||||
return
|
||||
}
|
||||
// 只允许中文、英文、数字和下划线
|
||||
if (!/^[\u4e00-\u9fa5a-zA-Z0-9_]+$/.test(signName)) {
|
||||
errorDialog.value = { visible: true, message: '参赛人员名称只能包含中文、英文、数字和下划线' }
|
||||
alert('参赛人员名称只能包含中文、英文、数字和下划线')
|
||||
return
|
||||
}
|
||||
// 不允许纯数字
|
||||
if (/^\d+$/.test(signName)) {
|
||||
errorDialog.value = { visible: true, message: '参赛人员名称不能为纯数字' }
|
||||
alert('参赛人员名称不能为纯数字')
|
||||
return
|
||||
}
|
||||
// 不允许纯下划线
|
||||
if (/^_+$/.test(signName)) {
|
||||
errorDialog.value = { visible: true, message: '参赛人员名称不能为纯下划线' }
|
||||
alert('参赛人员名称不能为纯下划线')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
// 确保所有必需字段都存在
|
||||
if (!competitionInfo.value.id || !competitionInfo.value.name) {
|
||||
errorDialog.value = { visible: true, message: '比赛信息不完整,请返回重试' }
|
||||
alert('比赛信息不完整,请返回重试')
|
||||
return
|
||||
}
|
||||
|
||||
const submitData = {
|
||||
tournament_id: parseInt(competitionInfo.value.id),
|
||||
type: signupForm.value.type,
|
||||
teamname: signupForm.value.type === 'teamname' ? signupForm.value.teamName : ' ',
|
||||
username: signupForm.value.username,
|
||||
faction: signupForm.value.faction,
|
||||
qq: signupForm.value.qq
|
||||
}
|
||||
const signupResultData = {
|
||||
tournament_id: parseInt(competitionInfo.value.id),
|
||||
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: ''
|
||||
type: signupForm.value.type,
|
||||
team_name: signupForm.value.type === 'teamname' ? signupForm.value.teamName : '',
|
||||
sign_name: signName,
|
||||
faction: signupForm.value.faction,
|
||||
qq_code: String(competitionInfo.value.qq_code) // 确保 qq_code 是字符串类型
|
||||
}
|
||||
|
||||
console.log('提交的报名数据:', submitData)
|
||||
const result = await addSignUp(submitData)
|
||||
const resultSignup = await addSignUpResult(signupResultData)
|
||||
console.log('提交的报名数据:', submitData, signupResultData)
|
||||
console.log('报名结果:', result, resultSignup)
|
||||
successDialog.value = { visible: true, message: '报名成功!' }
|
||||
setTimeout(() => {
|
||||
console.log('报名结果:', result)
|
||||
|
||||
if (result.signup && result.result) {
|
||||
alert('报名成功!')
|
||||
router.push('/competition')
|
||||
}, 1500)
|
||||
} else {
|
||||
console.error('报名结果不完整:', result)
|
||||
throw new Error('报名数据不完整,请重试')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('报名失败:', error)
|
||||
console.error('错误详情:', {
|
||||
@ -292,13 +268,13 @@ const handleSubmit = async () => {
|
||||
|
||||
// 显示具体的错误信息
|
||||
if (error.message.includes('返回数据为空')) {
|
||||
errorDialog.value = { visible: true, message: '服务器返回数据为空,请稍后重试' }
|
||||
alert('服务器返回数据为空,请稍后重试')
|
||||
} else if (error.message.includes('数据不完整')) {
|
||||
errorDialog.value = { visible: true, message: '报名数据不完整,请重试' }
|
||||
alert('报名数据不完整,请重试')
|
||||
} else if (error.message.includes('网络连接')) {
|
||||
errorDialog.value = { visible: true, message: '网络连接失败,请检查网络后重试' }
|
||||
alert('网络连接失败,请检查网络后重试')
|
||||
} else {
|
||||
errorDialog.value = { visible: true, message: error.message || '报名失败,请稍后重试' }
|
||||
alert(error.message || '报名失败,请稍后重试')
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -74,7 +74,7 @@
|
||||
error: null,
|
||||
currentPage: 1,
|
||||
itemsPerPage: 100,
|
||||
apiBaseUrl: 'http://zybdatasupport.online:8000',
|
||||
apiBaseUrl: 'https://api.zybdatasupport.online',
|
||||
categoryList: [],
|
||||
selectedCategory: '全部',
|
||||
};
|
||||
|
||||
@ -1,103 +0,0 @@
|
||||
<template>
|
||||
<div class="version-page">
|
||||
<div class="page-header">
|
||||
<h1>版本信息</h1>
|
||||
<div class="header-subtitle">
|
||||
<span>当前前端应用版本:{{ appVersion }}</span>
|
||||
<br />
|
||||
<span>当前后端应用版本:V.{{ backendVersion }}</span>
|
||||
<h3>联系邮箱</h3>
|
||||
<ul>
|
||||
<li v-for="(email, name) in contactEmails" :key="name">
|
||||
{{ name }}: {{ email }}
|
||||
</li>
|
||||
<li>kuangisa: 1549184870@qq.com</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<h2>更新日志</h2>
|
||||
<div class="md-content" v-html="mdHtml"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import pkg from '../../../package.json'
|
||||
import { marked } from 'marked'
|
||||
import versionMd from '@/assets/version.md?raw'
|
||||
import { getBackendInfo } from '@/api/info'
|
||||
import { ref, onMounted } from 'vue'
|
||||
|
||||
const backendVersion = ref('')
|
||||
const contactEmails = ref({})
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
const info = await getBackendInfo()
|
||||
backendVersion.value = info.version || '未知'
|
||||
if (info.contact && info.contact.email) {
|
||||
try {
|
||||
const emailString = info.contact.email.replace(/'/g, '"')
|
||||
contactEmails.value = JSON.parse(emailString)
|
||||
} catch (e) {
|
||||
contactEmails.value = {}
|
||||
}
|
||||
}
|
||||
} catch (_) {
|
||||
backendVersion.value = '获取失败'
|
||||
}
|
||||
})
|
||||
|
||||
const appVersion = pkg.version || '未知'
|
||||
const mdHtml = marked.parse(versionMd || '')
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-header {
|
||||
margin-bottom: 24px;
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.page-header h1 {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.header-subtitle {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.header-subtitle span {
|
||||
display: inline-block;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.header-subtitle h3 {
|
||||
margin-top: 16px;
|
||||
margin-bottom: 8px;
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.header-subtitle ul {
|
||||
padding-left: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.header-subtitle li {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.md-content {
|
||||
line-height: 1.8;
|
||||
}
|
||||
.md-content h1, .md-content h2, .md-content h3 {
|
||||
margin: 12px 0 8px;
|
||||
}
|
||||
.md-content p {
|
||||
margin: 8px 0;
|
||||
}
|
||||
.md-content ul, .md-content ol {
|
||||
padding-left: 20px;
|
||||
}
|
||||
</style>
|
||||
Loading…
x
Reference in New Issue
Block a user