sa2kit 2.0.0 → 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/CollisionBalls-BpHufX3H.d.mts +41 -0
- package/dist/CollisionBalls-BpHufX3H.d.ts +41 -0
- package/dist/ConfigService-BxK06xP6.d.mts +262 -0
- package/dist/ConfigService-BxK06xP6.d.ts +262 -0
- package/dist/UniversalFileService-BpvbZitV.d.mts +139 -0
- package/dist/UniversalFileService-GsP6D3Rc.d.ts +139 -0
- package/dist/audioDetection/index.d.mts +449 -0
- package/dist/audioDetection/index.d.ts +449 -0
- package/dist/audioDetection/index.js +1244 -0
- package/dist/audioDetection/index.js.map +1 -0
- package/dist/audioDetection/index.mjs +1227 -0
- package/dist/audioDetection/index.mjs.map +1 -0
- package/dist/auth/legacy/core/index.d.mts +42 -0
- package/dist/auth/legacy/core/index.d.ts +42 -0
- package/dist/auth/legacy/core/index.js +242 -0
- package/dist/auth/legacy/core/index.js.map +1 -0
- package/dist/auth/legacy/core/index.mjs +226 -0
- package/dist/auth/legacy/core/index.mjs.map +1 -0
- package/dist/auth/legacy/db/index.d.mts +5 -0
- package/dist/auth/legacy/db/index.d.ts +5 -0
- package/dist/auth/legacy/db/index.js +261 -0
- package/dist/auth/legacy/db/index.js.map +1 -0
- package/dist/auth/legacy/db/index.mjs +250 -0
- package/dist/auth/legacy/db/index.mjs.map +1 -0
- package/dist/auth/legacy/index.d.mts +5 -0
- package/dist/auth/legacy/index.d.ts +5 -0
- package/dist/auth/legacy/index.js +1107 -0
- package/dist/auth/legacy/index.js.map +1 -0
- package/dist/auth/legacy/index.mjs +1086 -0
- package/dist/auth/legacy/index.mjs.map +1 -0
- package/dist/auth/legacy/logic/index.d.mts +9 -0
- package/dist/auth/legacy/logic/index.d.ts +9 -0
- package/dist/auth/legacy/logic/index.js +194 -0
- package/dist/auth/legacy/logic/index.js.map +1 -0
- package/dist/auth/legacy/logic/index.mjs +187 -0
- package/dist/auth/legacy/logic/index.mjs.map +1 -0
- package/dist/auth/legacy/miniapp/index.d.mts +5 -0
- package/dist/auth/legacy/miniapp/index.d.ts +5 -0
- package/dist/auth/legacy/miniapp/index.js +506 -0
- package/dist/auth/legacy/miniapp/index.js.map +1 -0
- package/dist/auth/legacy/miniapp/index.mjs +487 -0
- package/dist/auth/legacy/miniapp/index.mjs.map +1 -0
- package/dist/auth/legacy/routes/index.d.mts +53 -0
- package/dist/auth/legacy/routes/index.d.ts +53 -0
- package/dist/auth/legacy/routes/index.js +278 -0
- package/dist/auth/legacy/routes/index.js.map +1 -0
- package/dist/auth/legacy/routes/index.mjs +271 -0
- package/dist/auth/legacy/routes/index.mjs.map +1 -0
- package/dist/auth/legacy/schema/index.d.mts +401 -0
- package/dist/auth/legacy/schema/index.d.ts +401 -0
- package/dist/auth/legacy/schema/index.js +50 -0
- package/dist/auth/legacy/schema/index.js.map +1 -0
- package/dist/auth/legacy/schema/index.mjs +44 -0
- package/dist/auth/legacy/schema/index.mjs.map +1 -0
- package/dist/auth/legacy/server/index.d.mts +13 -0
- package/dist/auth/legacy/server/index.d.ts +13 -0
- package/dist/auth/legacy/server/index.js +21 -0
- package/dist/auth/legacy/server/index.js.map +1 -0
- package/dist/auth/legacy/server/index.mjs +19 -0
- package/dist/auth/legacy/server/index.mjs.map +1 -0
- package/dist/auth/legacy/services/index.d.mts +40 -0
- package/dist/auth/legacy/services/index.d.ts +40 -0
- package/dist/auth/legacy/services/index.js +258 -0
- package/dist/auth/legacy/services/index.js.map +1 -0
- package/dist/auth/legacy/services/index.mjs +252 -0
- package/dist/auth/legacy/services/index.mjs.map +1 -0
- package/dist/auth/legacy/ui/miniapp/index.d.mts +10 -0
- package/dist/auth/legacy/ui/miniapp/index.d.ts +10 -0
- package/dist/auth/legacy/ui/miniapp/index.js +298 -0
- package/dist/auth/legacy/ui/miniapp/index.js.map +1 -0
- package/dist/auth/legacy/ui/miniapp/index.mjs +290 -0
- package/dist/auth/legacy/ui/miniapp/index.mjs.map +1 -0
- package/dist/auth/legacy/ui/web/index.d.mts +22 -0
- package/dist/auth/legacy/ui/web/index.d.ts +22 -0
- package/dist/auth/legacy/ui/web/index.js +899 -0
- package/dist/auth/legacy/ui/web/index.js.map +1 -0
- package/dist/auth/legacy/ui/web/index.mjs +889 -0
- package/dist/auth/legacy/ui/web/index.mjs.map +1 -0
- package/dist/auth/legacy/web/index.d.mts +5 -0
- package/dist/auth/legacy/web/index.d.ts +5 -0
- package/dist/auth/legacy/web/index.js +1107 -0
- package/dist/auth/legacy/web/index.js.map +1 -0
- package/dist/auth/legacy/web/index.mjs +1086 -0
- package/dist/auth/legacy/web/index.mjs.map +1 -0
- package/dist/auth/rn/index.d.mts +64 -0
- package/dist/auth/rn/index.d.ts +64 -0
- package/dist/auth/rn/index.js +765 -0
- package/dist/auth/rn/index.js.map +1 -0
- package/dist/auth/rn/index.mjs +754 -0
- package/dist/auth/rn/index.mjs.map +1 -0
- package/dist/base-api-client-ACKKt13v.d.mts +277 -0
- package/dist/base-api-client-ACKKt13v.d.ts +277 -0
- package/dist/boothVaultService-Cn4WPhjg.d.mts +83 -0
- package/dist/boothVaultService-Cn4WPhjg.d.ts +83 -0
- package/dist/business/index.d.mts +6 -0
- package/dist/business/index.d.ts +6 -0
- package/dist/business/index.js +1682 -0
- package/dist/business/index.js.map +1 -0
- package/dist/business/index.mjs +1675 -0
- package/dist/business/index.mjs.map +1 -0
- package/dist/calendar/index.d.mts +1325 -0
- package/dist/calendar/index.d.ts +1325 -0
- package/dist/calendar/index.js +5964 -0
- package/dist/calendar/index.js.map +1 -0
- package/dist/calendar/index.mjs +5878 -0
- package/dist/calendar/index.mjs.map +1 -0
- package/dist/components/index.d.mts +405 -0
- package/dist/components/index.d.ts +405 -0
- package/dist/components/index.js +2516 -0
- package/dist/components/index.js.map +1 -0
- package/dist/components/index.mjs +2396 -0
- package/dist/components/index.mjs.map +1 -0
- package/dist/drizzle-schema-BNhqj2AZ.d.mts +1114 -0
- package/dist/drizzle-schema-BNhqj2AZ.d.ts +1114 -0
- package/dist/festivalCard/index.d.mts +75 -0
- package/dist/festivalCard/index.d.ts +75 -0
- package/dist/festivalCard/index.js +1492 -0
- package/dist/festivalCard/index.js.map +1 -0
- package/dist/festivalCard/index.mjs +1475 -0
- package/dist/festivalCard/index.mjs.map +1 -0
- package/dist/festivalCard/server/index.d.mts +120 -0
- package/dist/festivalCard/server/index.d.ts +120 -0
- package/dist/festivalCard/server/index.js +272 -0
- package/dist/festivalCard/server/index.js.map +1 -0
- package/dist/festivalCard/server/index.mjs +265 -0
- package/dist/festivalCard/server/index.mjs.map +1 -0
- package/dist/festivalCardService-CZomuQ4E.d.mts +80 -0
- package/dist/festivalCardService-CZomuQ4E.d.ts +80 -0
- package/dist/index-1Ag7IBXN.d.ts +144 -0
- package/dist/index-DNKZ7-R_.d.mts +184 -0
- package/dist/index-DNKZ7-R_.d.ts +184 -0
- package/dist/index-DSel44Ke.d.mts +93 -0
- package/dist/index-DSel44Ke.d.ts +93 -0
- package/dist/index-DdeZSeTJ.d.mts +144 -0
- package/dist/index-DrPcMJPc.d.mts +250 -0
- package/dist/index-DrPcMJPc.d.ts +250 -0
- package/dist/index.d.mts +5333 -0
- package/dist/index.d.ts +5333 -0
- package/dist/index.js +18809 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +18533 -0
- package/dist/index.mjs.map +1 -0
- package/dist/mikuContest/ui/web/index.d.mts +2 -0
- package/dist/mikuContest/ui/web/index.d.ts +2 -0
- package/dist/mikuContest/ui/web/index.js +353 -0
- package/dist/mikuContest/ui/web/index.js.map +1 -0
- package/dist/mikuContest/ui/web/index.mjs +343 -0
- package/dist/mikuContest/ui/web/index.mjs.map +1 -0
- package/dist/mikuFireworks3D/index.d.mts +268 -0
- package/dist/mikuFireworks3D/index.d.ts +268 -0
- package/dist/mikuFireworks3D/index.js +1267 -0
- package/dist/mikuFireworks3D/index.js.map +1 -0
- package/dist/mikuFireworks3D/index.mjs +1228 -0
- package/dist/mikuFireworks3D/index.mjs.map +1 -0
- package/dist/mikuFusionGame/index.d.mts +117 -0
- package/dist/mikuFusionGame/index.d.ts +117 -0
- package/dist/mikuFusionGame/index.js +1208 -0
- package/dist/mikuFusionGame/index.js.map +1 -0
- package/dist/mikuFusionGame/index.mjs +1195 -0
- package/dist/mikuFusionGame/index.mjs.map +1 -0
- package/dist/mmd/admin/index.d.mts +487 -0
- package/dist/mmd/admin/index.d.ts +487 -0
- package/dist/mmd/admin/index.js +1058 -0
- package/dist/mmd/admin/index.js.map +1 -0
- package/dist/mmd/admin/index.mjs +1027 -0
- package/dist/mmd/admin/index.mjs.map +1 -0
- package/dist/mmd/index.d.mts +2467 -0
- package/dist/mmd/index.d.ts +2467 -0
- package/dist/mmd/index.js +10119 -0
- package/dist/mmd/index.js.map +1 -0
- package/dist/mmd/index.mjs +10028 -0
- package/dist/mmd/index.mjs.map +1 -0
- package/dist/mmd/server/index.d.mts +139 -0
- package/dist/mmd/server/index.d.ts +139 -0
- package/dist/mmd/server/index.js +424 -0
- package/dist/mmd/server/index.js.map +1 -0
- package/dist/mmd/server/index.mjs +404 -0
- package/dist/mmd/server/index.mjs.map +1 -0
- package/dist/music/index.d.mts +74 -0
- package/dist/music/index.d.ts +74 -0
- package/dist/music/index.js +830 -0
- package/dist/music/index.js.map +1 -0
- package/dist/music/index.mjs +809 -0
- package/dist/music/index.mjs.map +1 -0
- package/dist/music/server/index.d.mts +1 -0
- package/dist/music/server/index.d.ts +1 -0
- package/dist/music/server/index.js +194 -0
- package/dist/music/server/index.js.map +1 -0
- package/dist/music/server/index.mjs +182 -0
- package/dist/music/server/index.mjs.map +1 -0
- package/dist/navigation/index.d.mts +93 -0
- package/dist/navigation/index.d.ts +93 -0
- package/dist/navigation/index.js +453 -0
- package/dist/navigation/index.js.map +1 -0
- package/dist/navigation/index.mjs +443 -0
- package/dist/navigation/index.mjs.map +1 -0
- package/dist/portfolio/index.d.mts +66 -0
- package/dist/portfolio/index.d.ts +66 -0
- package/dist/portfolio/index.js +736 -0
- package/dist/portfolio/index.js.map +1 -0
- package/dist/portfolio/index.mjs +724 -0
- package/dist/portfolio/index.mjs.map +1 -0
- package/dist/qqbot/server/index.d.mts +216 -0
- package/dist/qqbot/server/index.d.ts +216 -0
- package/dist/qqbot/server/index.js +394 -0
- package/dist/qqbot/server/index.js.map +1 -0
- package/dist/qqbot/server/index.mjs +385 -0
- package/dist/qqbot/server/index.mjs.map +1 -0
- package/dist/qqbot/ui/web/index.d.mts +10 -0
- package/dist/qqbot/ui/web/index.d.ts +10 -0
- package/dist/qqbot/ui/web/index.js +105 -0
- package/dist/qqbot/ui/web/index.js.map +1 -0
- package/dist/qqbot/ui/web/index.mjs +99 -0
- package/dist/qqbot/ui/web/index.mjs.map +1 -0
- package/dist/screenReceiver/index.d.mts +86 -0
- package/dist/screenReceiver/index.d.ts +86 -0
- package/dist/screenReceiver/index.js +281 -0
- package/dist/screenReceiver/index.js.map +1 -0
- package/dist/screenReceiver/index.mjs +273 -0
- package/dist/screenReceiver/index.mjs.map +1 -0
- package/dist/testYourself/admin/index.d.mts +58 -0
- package/dist/testYourself/admin/index.d.ts +58 -0
- package/dist/testYourself/admin/index.js +1009 -0
- package/dist/testYourself/admin/index.js.map +1 -0
- package/dist/testYourself/admin/index.mjs +1002 -0
- package/dist/testYourself/admin/index.mjs.map +1 -0
- package/dist/testYourself/index.d.mts +53 -0
- package/dist/testYourself/index.d.ts +53 -0
- package/dist/testYourself/index.js +2551 -0
- package/dist/testYourself/index.js.map +1 -0
- package/dist/testYourself/index.mjs +2531 -0
- package/dist/testYourself/index.mjs.map +1 -0
- package/dist/testYourself/server/index.d.mts +1029 -0
- package/dist/testYourself/server/index.d.ts +1029 -0
- package/dist/testYourself/server/index.js +825 -0
- package/dist/testYourself/server/index.js.map +1 -0
- package/dist/testYourself/server/index.mjs +816 -0
- package/dist/testYourself/server/index.mjs.map +1 -0
- package/dist/types-BTiaMsBz.d.mts +292 -0
- package/dist/types-DyG3ZV9V.d.mts +270 -0
- package/dist/types-DyG3ZV9V.d.ts +270 -0
- package/dist/types-ERmJyjx8.d.ts +292 -0
- package/dist/types-HorDyIRv.d.mts +303 -0
- package/dist/types-HorDyIRv.d.ts +303 -0
- package/dist/vocaloidBooth/index.d.mts +64 -0
- package/dist/vocaloidBooth/index.d.ts +64 -0
- package/dist/vocaloidBooth/index.js +376 -0
- package/dist/vocaloidBooth/index.js.map +1 -0
- package/dist/vocaloidBooth/index.mjs +362 -0
- package/dist/vocaloidBooth/index.mjs.map +1 -0
- package/dist/vocaloidBooth/server/index.d.mts +111 -0
- package/dist/vocaloidBooth/server/index.d.ts +111 -0
- package/dist/vocaloidBooth/server/index.js +247 -0
- package/dist/vocaloidBooth/server/index.js.map +1 -0
- package/dist/vocaloidBooth/server/index.mjs +237 -0
- package/dist/vocaloidBooth/server/index.mjs.map +1 -0
- package/dist/vocaloidBooth/web/index.d.mts +3 -0
- package/dist/vocaloidBooth/web/index.d.ts +3 -0
- package/dist/vocaloidBooth/web/index.js +376 -0
- package/dist/vocaloidBooth/web/index.js.map +1 -0
- package/dist/vocaloidBooth/web/index.mjs +362 -0
- package/dist/vocaloidBooth/web/index.mjs.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var React4 = require('react');
|
|
4
|
+
|
|
5
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
6
|
+
|
|
7
|
+
var React4__default = /*#__PURE__*/_interopDefault(React4);
|
|
8
|
+
|
|
9
|
+
// src/mikuContest/ui/web/components/MikuContestDashboard.tsx
|
|
10
|
+
var MikuContestDashboard = ({ snapshot }) => {
|
|
11
|
+
return /* @__PURE__ */ React4__default.default.createElement("div", null, /* @__PURE__ */ React4__default.default.createElement("h2", null, snapshot.contest.name), /* @__PURE__ */ React4__default.default.createElement("p", null, snapshot.contest.theme), /* @__PURE__ */ React4__default.default.createElement("p", null, "\u6295\u7A3F\u6570\uFF1A", snapshot.submissions.length), /* @__PURE__ */ React4__default.default.createElement("p", null, "\u516C\u544A\u6570\uFF1A", snapshot.announcements.length), /* @__PURE__ */ React4__default.default.createElement("ul", null, snapshot.leaderboard.map((item) => /* @__PURE__ */ React4__default.default.createElement("li", { key: item.submissionId }, "#", item.rank, " ", item.title, " - ", item.voteCount, "\u7968"))));
|
|
12
|
+
};
|
|
13
|
+
var MikuContestDashboard_default = MikuContestDashboard;
|
|
14
|
+
|
|
15
|
+
// src/mikuContest/service/api/client.ts
|
|
16
|
+
var toQueryString = (filter) => {
|
|
17
|
+
if (!filter) return "";
|
|
18
|
+
const params = new URLSearchParams();
|
|
19
|
+
if (filter.status) params.set("status", filter.status);
|
|
20
|
+
if (filter.type) params.set("type", filter.type);
|
|
21
|
+
if (filter.authorId) params.set("authorId", filter.authorId);
|
|
22
|
+
if (filter.authorKeyword) params.set("authorKeyword", filter.authorKeyword);
|
|
23
|
+
if (filter.titleKeyword) params.set("titleKeyword", filter.titleKeyword);
|
|
24
|
+
const query = params.toString();
|
|
25
|
+
return query ? `?${query}` : "";
|
|
26
|
+
};
|
|
27
|
+
var unwrap = (result) => {
|
|
28
|
+
if (!result.success || result.data === void 0) {
|
|
29
|
+
throw new Error(result.error || "\u8BF7\u6C42\u5931\u8D25");
|
|
30
|
+
}
|
|
31
|
+
return result.data;
|
|
32
|
+
};
|
|
33
|
+
var createMikuContestApiClient = (basePath, requester) => {
|
|
34
|
+
return {
|
|
35
|
+
async getSnapshot() {
|
|
36
|
+
const result = await requester(`${basePath}/contest`, { method: "GET" });
|
|
37
|
+
return unwrap(result);
|
|
38
|
+
},
|
|
39
|
+
async updateContestConfig(patch) {
|
|
40
|
+
const result = await requester(`${basePath}/contest`, {
|
|
41
|
+
method: "PATCH",
|
|
42
|
+
body: patch
|
|
43
|
+
});
|
|
44
|
+
return unwrap(result);
|
|
45
|
+
},
|
|
46
|
+
async createSubmission(input, mode = "web") {
|
|
47
|
+
const result = await requester(`${basePath}/submissions`, {
|
|
48
|
+
method: "POST",
|
|
49
|
+
body: { payload: input, mode }
|
|
50
|
+
});
|
|
51
|
+
return unwrap(result);
|
|
52
|
+
},
|
|
53
|
+
async listSubmissions(filter) {
|
|
54
|
+
const result = await requester(
|
|
55
|
+
`${basePath}/submissions${toQueryString(filter)}`,
|
|
56
|
+
{ method: "GET" }
|
|
57
|
+
);
|
|
58
|
+
return unwrap(result);
|
|
59
|
+
},
|
|
60
|
+
async reviewSubmission(input) {
|
|
61
|
+
const result = await requester(`${basePath}/submissions/review`, {
|
|
62
|
+
method: "POST",
|
|
63
|
+
body: input
|
|
64
|
+
});
|
|
65
|
+
return unwrap(result);
|
|
66
|
+
},
|
|
67
|
+
async vote(input) {
|
|
68
|
+
const result = await requester(`${basePath}/votes`, {
|
|
69
|
+
method: "POST",
|
|
70
|
+
body: input
|
|
71
|
+
});
|
|
72
|
+
return unwrap(result);
|
|
73
|
+
},
|
|
74
|
+
async setVoterRestriction(input) {
|
|
75
|
+
const result = await requester(`${basePath}/admin/voter-restrictions`, {
|
|
76
|
+
method: "POST",
|
|
77
|
+
body: input
|
|
78
|
+
});
|
|
79
|
+
return unwrap(result);
|
|
80
|
+
},
|
|
81
|
+
async resetVotes(input) {
|
|
82
|
+
const result = await requester(
|
|
83
|
+
`${basePath}/admin/votes/reset`,
|
|
84
|
+
{
|
|
85
|
+
method: "POST",
|
|
86
|
+
body: input
|
|
87
|
+
}
|
|
88
|
+
);
|
|
89
|
+
return unwrap(result);
|
|
90
|
+
},
|
|
91
|
+
async exportSubmissions(filter) {
|
|
92
|
+
const response = await fetch(`${basePath}/admin/submissions/export${toQueryString(filter)}`);
|
|
93
|
+
if (!response.ok) {
|
|
94
|
+
throw new Error(`\u5BFC\u51FA\u5931\u8D25: ${response.status}`);
|
|
95
|
+
}
|
|
96
|
+
return response.arrayBuffer();
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
// src/mikuContest/service/web/client.ts
|
|
102
|
+
var defaultRequester = (options) => {
|
|
103
|
+
const baseUrl = options.baseUrl || "";
|
|
104
|
+
const commonHeaders = options.headers || {};
|
|
105
|
+
return async (url, requestOptions) => {
|
|
106
|
+
const response = await fetch(`${baseUrl}${url}`, {
|
|
107
|
+
method: requestOptions?.method || "GET",
|
|
108
|
+
headers: {
|
|
109
|
+
"Content-Type": "application/json",
|
|
110
|
+
...commonHeaders
|
|
111
|
+
},
|
|
112
|
+
body: requestOptions?.body ? JSON.stringify(requestOptions.body) : void 0
|
|
113
|
+
});
|
|
114
|
+
const json = await response.json();
|
|
115
|
+
return json;
|
|
116
|
+
};
|
|
117
|
+
};
|
|
118
|
+
var createMikuContestWebClient = (options = {}) => {
|
|
119
|
+
const basePath = options.basePath || "/api/miku-contest";
|
|
120
|
+
return createMikuContestApiClient(basePath, defaultRequester(options));
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
// src/mikuContest/ui/web/pages/MikuContestAudiencePage.tsx
|
|
124
|
+
var MikuContestAudiencePage = ({
|
|
125
|
+
client,
|
|
126
|
+
voterId,
|
|
127
|
+
title = "\u89C2\u4F17\u6295\u7968\u533A"
|
|
128
|
+
}) => {
|
|
129
|
+
const api = React4.useMemo(() => client || createMikuContestWebClient(), [client]);
|
|
130
|
+
const [snapshot, setSnapshot] = React4.useState(null);
|
|
131
|
+
const [loading, setLoading] = React4.useState(false);
|
|
132
|
+
const [error, setError] = React4.useState(null);
|
|
133
|
+
const approvedWorks = React4.useMemo(() => {
|
|
134
|
+
if (!snapshot) return [];
|
|
135
|
+
return snapshot.submissions.filter((item) => item.status === "approved");
|
|
136
|
+
}, [snapshot]);
|
|
137
|
+
const loadSnapshot = async () => {
|
|
138
|
+
setLoading(true);
|
|
139
|
+
setError(null);
|
|
140
|
+
try {
|
|
141
|
+
const data = await api.getSnapshot();
|
|
142
|
+
setSnapshot(data);
|
|
143
|
+
} catch (e) {
|
|
144
|
+
setError(e.message);
|
|
145
|
+
} finally {
|
|
146
|
+
setLoading(false);
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
React4.useEffect(() => {
|
|
150
|
+
void loadSnapshot();
|
|
151
|
+
}, []);
|
|
152
|
+
const handleVote = async (submission) => {
|
|
153
|
+
if (!snapshot) return;
|
|
154
|
+
try {
|
|
155
|
+
await api.vote({
|
|
156
|
+
contestId: snapshot.contest.id,
|
|
157
|
+
submissionId: submission.id,
|
|
158
|
+
voterId
|
|
159
|
+
});
|
|
160
|
+
await loadSnapshot();
|
|
161
|
+
} catch (e) {
|
|
162
|
+
setError(e.message);
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
return /* @__PURE__ */ React4__default.default.createElement("section", null, /* @__PURE__ */ React4__default.default.createElement("h2", null, title), /* @__PURE__ */ React4__default.default.createElement("button", { onClick: () => void loadSnapshot(), disabled: loading }, loading ? "\u5237\u65B0\u4E2D..." : "\u5237\u65B0\u6570\u636E"), error ? /* @__PURE__ */ React4__default.default.createElement("p", { style: { color: "crimson" } }, "\u9519\u8BEF\uFF1A", error) : null, !snapshot ? null : /* @__PURE__ */ React4__default.default.createElement(React4__default.default.Fragment, null, /* @__PURE__ */ React4__default.default.createElement("p", null, "\u8D5B\u4E8B\uFF1A", snapshot.contest.name, "\uFF5C\u4E3B\u9898\uFF1A", snapshot.contest.theme), /* @__PURE__ */ React4__default.default.createElement("p", null, "\u5DF2\u8FC7\u5BA1\u4F5C\u54C1\uFF1A", approvedWorks.length, "\uFF5C\u6BCF\u65E5\u4E0A\u9650\uFF1A", snapshot.contest.votingRules.maxVotesPerDay), /* @__PURE__ */ React4__default.default.createElement("ul", null, approvedWorks.map((work) => /* @__PURE__ */ React4__default.default.createElement("li", { key: work.id }, /* @__PURE__ */ React4__default.default.createElement("strong", null, work.title), "\uFF08", work.authorNickname, "\uFF09- \u5F53\u524D ", work.voteCount, " \u7968", " ", /* @__PURE__ */ React4__default.default.createElement("button", { onClick: () => void handleVote(work) }, "\u6295\u7968"))))));
|
|
166
|
+
};
|
|
167
|
+
var MikuContestAudiencePage_default = MikuContestAudiencePage;
|
|
168
|
+
var workTypes = ["visual", "video", "text", "audio"];
|
|
169
|
+
var MikuContestArtistPage = ({
|
|
170
|
+
client,
|
|
171
|
+
authorId,
|
|
172
|
+
authorNickname,
|
|
173
|
+
title = "\u753B\u5E08\u6295\u7A3F\u533A"
|
|
174
|
+
}) => {
|
|
175
|
+
const api = React4.useMemo(() => client || createMikuContestWebClient(), [client]);
|
|
176
|
+
const [snapshot, setSnapshot] = React4.useState(null);
|
|
177
|
+
const [mySubmissions, setMySubmissions] = React4.useState([]);
|
|
178
|
+
const [submitting, setSubmitting] = React4.useState(false);
|
|
179
|
+
const [loading, setLoading] = React4.useState(false);
|
|
180
|
+
const [error, setError] = React4.useState(null);
|
|
181
|
+
const [titleInput, setTitleInput] = React4.useState("");
|
|
182
|
+
const [descInput, setDescInput] = React4.useState("");
|
|
183
|
+
const [coverImage, setCoverImage] = React4.useState("");
|
|
184
|
+
const [workType, setWorkType] = React4.useState("visual");
|
|
185
|
+
const loadData = async () => {
|
|
186
|
+
setLoading(true);
|
|
187
|
+
setError(null);
|
|
188
|
+
try {
|
|
189
|
+
const [contest, mine] = await Promise.all([
|
|
190
|
+
api.getSnapshot(),
|
|
191
|
+
api.listSubmissions({ authorId })
|
|
192
|
+
]);
|
|
193
|
+
setSnapshot(contest);
|
|
194
|
+
setMySubmissions(mine);
|
|
195
|
+
} catch (e) {
|
|
196
|
+
setError(e.message);
|
|
197
|
+
} finally {
|
|
198
|
+
setLoading(false);
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
React4.useEffect(() => {
|
|
202
|
+
void loadData();
|
|
203
|
+
}, []);
|
|
204
|
+
const submitWork = async () => {
|
|
205
|
+
if (!snapshot) return;
|
|
206
|
+
const payload = {
|
|
207
|
+
contestId: snapshot.contest.id,
|
|
208
|
+
authorId,
|
|
209
|
+
authorNickname,
|
|
210
|
+
title: titleInput,
|
|
211
|
+
description: descInput,
|
|
212
|
+
type: workType,
|
|
213
|
+
tags: ["web"],
|
|
214
|
+
content: {
|
|
215
|
+
coverImage,
|
|
216
|
+
images: coverImage ? [coverImage] : void 0
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
setSubmitting(true);
|
|
220
|
+
setError(null);
|
|
221
|
+
try {
|
|
222
|
+
await api.createSubmission(payload, "web");
|
|
223
|
+
setTitleInput("");
|
|
224
|
+
setDescInput("");
|
|
225
|
+
setCoverImage("");
|
|
226
|
+
await loadData();
|
|
227
|
+
} catch (e) {
|
|
228
|
+
setError(e.message);
|
|
229
|
+
} finally {
|
|
230
|
+
setSubmitting(false);
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
return /* @__PURE__ */ React4__default.default.createElement("section", null, /* @__PURE__ */ React4__default.default.createElement("h2", null, title), /* @__PURE__ */ React4__default.default.createElement("button", { onClick: () => void loadData(), disabled: loading }, loading ? "\u5237\u65B0\u4E2D..." : "\u5237\u65B0\u6570\u636E"), error ? /* @__PURE__ */ React4__default.default.createElement("p", { style: { color: "crimson" } }, "\u9519\u8BEF\uFF1A", error) : null, /* @__PURE__ */ React4__default.default.createElement("div", null, /* @__PURE__ */ React4__default.default.createElement("h3", null, "\u65B0\u5EFA\u6295\u7A3F"), /* @__PURE__ */ React4__default.default.createElement("input", { value: titleInput, onChange: (e) => setTitleInput(e.target.value), placeholder: "\u4F5C\u54C1\u6807\u9898" }), /* @__PURE__ */ React4__default.default.createElement("br", null), /* @__PURE__ */ React4__default.default.createElement("textarea", { value: descInput, onChange: (e) => setDescInput(e.target.value), placeholder: "\u4F5C\u54C1\u7B80\u4ECB" }), /* @__PURE__ */ React4__default.default.createElement("br", null), /* @__PURE__ */ React4__default.default.createElement("input", { value: coverImage, onChange: (e) => setCoverImage(e.target.value), placeholder: "\u5C01\u9762 URL" }), /* @__PURE__ */ React4__default.default.createElement("br", null), /* @__PURE__ */ React4__default.default.createElement("select", { value: workType, onChange: (e) => setWorkType(e.target.value) }, workTypes.map((item) => /* @__PURE__ */ React4__default.default.createElement("option", { value: item, key: item }, item))), /* @__PURE__ */ React4__default.default.createElement("button", { onClick: () => void submitWork(), disabled: submitting || !snapshot }, submitting ? "\u63D0\u4EA4\u4E2D..." : "\u63D0\u4EA4\u7A3F\u4EF6")), /* @__PURE__ */ React4__default.default.createElement("div", null, /* @__PURE__ */ React4__default.default.createElement("h3", null, "\u6211\u7684\u6295\u7A3F\uFF08", mySubmissions.length, "\uFF09"), /* @__PURE__ */ React4__default.default.createElement("ul", null, mySubmissions.map((item) => /* @__PURE__ */ React4__default.default.createElement("li", { key: item.id }, item.title, "\uFF5C\u72B6\u6001\uFF1A", item.status, "\uFF5C\u7968\u6570\uFF1A", item.voteCount, item.rejectReason ? `\uFF5C\u9A73\u56DE\uFF1A${item.rejectReason}` : "")))));
|
|
234
|
+
};
|
|
235
|
+
var MikuContestArtistPage_default = MikuContestArtistPage;
|
|
236
|
+
var MikuContestAdminPage = ({
|
|
237
|
+
client,
|
|
238
|
+
adminId,
|
|
239
|
+
title = "\u7BA1\u7406\u5458\u9762\u677F"
|
|
240
|
+
}) => {
|
|
241
|
+
const api = React4.useMemo(() => client || createMikuContestWebClient(), [client]);
|
|
242
|
+
const [snapshot, setSnapshot] = React4.useState(null);
|
|
243
|
+
const [submissions, setSubmissions] = React4.useState([]);
|
|
244
|
+
const [loading, setLoading] = React4.useState(false);
|
|
245
|
+
const [error, setError] = React4.useState(null);
|
|
246
|
+
const [voterId, setVoterId] = React4.useState("");
|
|
247
|
+
const loadData = async () => {
|
|
248
|
+
setLoading(true);
|
|
249
|
+
setError(null);
|
|
250
|
+
try {
|
|
251
|
+
const [contest, list] = await Promise.all([api.getSnapshot(), api.listSubmissions()]);
|
|
252
|
+
setSnapshot(contest);
|
|
253
|
+
setSubmissions(list);
|
|
254
|
+
} catch (e) {
|
|
255
|
+
setError(e.message);
|
|
256
|
+
} finally {
|
|
257
|
+
setLoading(false);
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
React4.useEffect(() => {
|
|
261
|
+
void loadData();
|
|
262
|
+
}, []);
|
|
263
|
+
const review = async (item, action) => {
|
|
264
|
+
try {
|
|
265
|
+
await api.reviewSubmission({
|
|
266
|
+
submissionId: item.id,
|
|
267
|
+
reviewerId: adminId,
|
|
268
|
+
action,
|
|
269
|
+
rejectReason: action === "reject" ? "\u7BA1\u7406\u5458\u9A73\u56DE" : void 0
|
|
270
|
+
});
|
|
271
|
+
await loadData();
|
|
272
|
+
} catch (e) {
|
|
273
|
+
setError(e.message);
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
const toggleVoting = async (enabled) => {
|
|
277
|
+
if (!snapshot) return;
|
|
278
|
+
try {
|
|
279
|
+
await api.updateContestConfig({
|
|
280
|
+
toggles: {
|
|
281
|
+
...snapshot.contest.toggles,
|
|
282
|
+
votingEnabled: enabled
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
await loadData();
|
|
286
|
+
} catch (e) {
|
|
287
|
+
setError(e.message);
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
const setRestriction = async (banned) => {
|
|
291
|
+
if (!snapshot || !voterId.trim()) return;
|
|
292
|
+
try {
|
|
293
|
+
await api.setVoterRestriction({
|
|
294
|
+
voterId: voterId.trim(),
|
|
295
|
+
banned,
|
|
296
|
+
reason: banned ? "\u7BA1\u7406\u5458\u624B\u52A8\u5C01\u7981" : "\u7BA1\u7406\u5458\u89E3\u9664\u5C01\u7981",
|
|
297
|
+
operatorId: adminId
|
|
298
|
+
});
|
|
299
|
+
setVoterId("");
|
|
300
|
+
} catch (e) {
|
|
301
|
+
setError(e.message);
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
const resetVotesByVoter = async () => {
|
|
305
|
+
if (!voterId.trim()) return;
|
|
306
|
+
try {
|
|
307
|
+
await api.resetVotes({ voterId: voterId.trim() });
|
|
308
|
+
setVoterId("");
|
|
309
|
+
await loadData();
|
|
310
|
+
} catch (e) {
|
|
311
|
+
setError(e.message);
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
const exportExcel = async () => {
|
|
315
|
+
try {
|
|
316
|
+
const data = await api.exportSubmissions();
|
|
317
|
+
const blob = new Blob([data], {
|
|
318
|
+
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
|
319
|
+
});
|
|
320
|
+
const url = URL.createObjectURL(blob);
|
|
321
|
+
const a = document.createElement("a");
|
|
322
|
+
a.href = url;
|
|
323
|
+
a.download = "miku-submissions.xlsx";
|
|
324
|
+
a.click();
|
|
325
|
+
URL.revokeObjectURL(url);
|
|
326
|
+
} catch (e) {
|
|
327
|
+
setError(e.message);
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
return /* @__PURE__ */ React4__default.default.createElement("section", null, /* @__PURE__ */ React4__default.default.createElement("h2", null, title), /* @__PURE__ */ React4__default.default.createElement("button", { onClick: () => void loadData(), disabled: loading }, loading ? "\u5237\u65B0\u4E2D..." : "\u5237\u65B0\u6570\u636E"), error ? /* @__PURE__ */ React4__default.default.createElement("p", { style: { color: "crimson" } }, "\u9519\u8BEF\uFF1A", error) : null, /* @__PURE__ */ React4__default.default.createElement("div", null, /* @__PURE__ */ React4__default.default.createElement("h3", null, "\u8D5B\u4E8B\u5F00\u5173"), /* @__PURE__ */ React4__default.default.createElement("button", { onClick: () => void toggleVoting(true), disabled: !snapshot }, "\u5F00\u542F\u6295\u7968"), /* @__PURE__ */ React4__default.default.createElement("button", { onClick: () => void toggleVoting(false), disabled: !snapshot }, "\u5173\u95ED\u6295\u7968")), /* @__PURE__ */ React4__default.default.createElement("div", null, /* @__PURE__ */ React4__default.default.createElement("h3", null, "\u6295\u7A3F\u5BA1\u6838\uFF08", submissions.length, "\uFF09"), /* @__PURE__ */ React4__default.default.createElement("ul", null, submissions.map((item) => /* @__PURE__ */ React4__default.default.createElement("li", { key: item.id }, /* @__PURE__ */ React4__default.default.createElement("strong", null, item.title), "\uFF5C\u4F5C\u8005\uFF1A", item.authorNickname, "\uFF5C\u72B6\u6001\uFF1A", item.status, "\uFF5C\u7968\u6570\uFF1A", item.voteCount, item.status === "pending" ? /* @__PURE__ */ React4__default.default.createElement(React4__default.default.Fragment, null, " ", /* @__PURE__ */ React4__default.default.createElement("button", { onClick: () => void review(item, "approve") }, "\u901A\u8FC7"), /* @__PURE__ */ React4__default.default.createElement("button", { onClick: () => void review(item, "reject") }, "\u9A73\u56DE")) : null)))), /* @__PURE__ */ React4__default.default.createElement("div", null, /* @__PURE__ */ React4__default.default.createElement("h3", null, "\u6295\u7968\u98CE\u63A7"), /* @__PURE__ */ React4__default.default.createElement("input", { value: voterId, onChange: (e) => setVoterId(e.target.value), placeholder: "voterId" }), /* @__PURE__ */ React4__default.default.createElement("button", { onClick: () => void setRestriction(true), disabled: !snapshot }, "\u5C01\u7981\u6295\u7968"), /* @__PURE__ */ React4__default.default.createElement("button", { onClick: () => void setRestriction(false), disabled: !snapshot }, "\u89E3\u9664\u5C01\u7981"), /* @__PURE__ */ React4__default.default.createElement("button", { onClick: () => void resetVotesByVoter() }, "\u6E05\u96F6\u8BE5\u7528\u6237\u7968\u6570")), /* @__PURE__ */ React4__default.default.createElement("div", null, /* @__PURE__ */ React4__default.default.createElement("h3", null, "\u5BFC\u51FA"), /* @__PURE__ */ React4__default.default.createElement("button", { onClick: () => void exportExcel() }, "\u5BFC\u51FA\u6295\u7A3F Excel")));
|
|
331
|
+
};
|
|
332
|
+
var MikuContestAdminPage_default = MikuContestAdminPage;
|
|
333
|
+
|
|
334
|
+
// src/mikuContest/ui/web/pages/MikuContestPage.tsx
|
|
335
|
+
var MikuContestPage = ({
|
|
336
|
+
defaultView = "audience",
|
|
337
|
+
viewerVoterId = "viewer-demo",
|
|
338
|
+
artistId = "artist-demo",
|
|
339
|
+
artistNickname = "Demo \u753B\u5E08",
|
|
340
|
+
adminId = "admin-demo"
|
|
341
|
+
}) => {
|
|
342
|
+
const [view, setView] = React4.useState(defaultView);
|
|
343
|
+
return /* @__PURE__ */ React4__default.default.createElement("div", null, /* @__PURE__ */ React4__default.default.createElement("h1", null, "Miku Contest"), /* @__PURE__ */ React4__default.default.createElement("div", null, /* @__PURE__ */ React4__default.default.createElement("button", { onClick: () => setView("audience") }, "\u89C2\u4F17\u7AEF"), /* @__PURE__ */ React4__default.default.createElement("button", { onClick: () => setView("artist") }, "\u753B\u5E08\u7AEF"), /* @__PURE__ */ React4__default.default.createElement("button", { onClick: () => setView("admin") }, "\u7BA1\u7406\u5458\u7AEF")), view === "audience" ? /* @__PURE__ */ React4__default.default.createElement(MikuContestAudiencePage_default, { voterId: viewerVoterId }) : null, view === "artist" ? /* @__PURE__ */ React4__default.default.createElement(MikuContestArtistPage_default, { authorId: artistId, authorNickname: artistNickname }) : null, view === "admin" ? /* @__PURE__ */ React4__default.default.createElement(MikuContestAdminPage_default, { adminId }) : null);
|
|
344
|
+
};
|
|
345
|
+
var MikuContestPage_default = MikuContestPage;
|
|
346
|
+
|
|
347
|
+
exports.MikuContestAdminPage = MikuContestAdminPage_default;
|
|
348
|
+
exports.MikuContestArtistPage = MikuContestArtistPage_default;
|
|
349
|
+
exports.MikuContestAudiencePage = MikuContestAudiencePage_default;
|
|
350
|
+
exports.MikuContestDashboard = MikuContestDashboard_default;
|
|
351
|
+
exports.MikuContestPage = MikuContestPage_default;
|
|
352
|
+
//# sourceMappingURL=index.js.map
|
|
353
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/mikuContest/ui/web/components/MikuContestDashboard.tsx","../../../../src/mikuContest/service/api/client.ts","../../../../src/mikuContest/service/web/client.ts","../../../../src/mikuContest/ui/web/pages/MikuContestAudiencePage.tsx","../../../../src/mikuContest/ui/web/pages/MikuContestArtistPage.tsx","../../../../src/mikuContest/ui/web/pages/MikuContestAdminPage.tsx","../../../../src/mikuContest/ui/web/pages/MikuContestPage.tsx"],"names":["React","useMemo","useState","useEffect"],"mappings":"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","file":"index.js","sourcesContent":["import React from 'react';\nimport type { MikuContestStateSnapshot } from '../../../types';\n\nexport interface MikuContestDashboardProps {\n snapshot: MikuContestStateSnapshot;\n}\n\nconst MikuContestDashboard: React.FC<MikuContestDashboardProps> = ({ snapshot }) => {\n return (\n <div>\n <h2>{snapshot.contest.name}</h2>\n <p>{snapshot.contest.theme}</p>\n <p>投稿数:{snapshot.submissions.length}</p>\n <p>公告数:{snapshot.announcements.length}</p>\n <ul>\n {snapshot.leaderboard.map((item) => (\n <li key={item.submissionId}>\n #{item.rank} {item.title} - {item.voteCount}票\n </li>\n ))}\n </ul>\n </div>\n );\n};\n\nexport default MikuContestDashboard;\n","import type {\n CreateMikuSubmissionInput,\n MikuContestConfig,\n MikuContestStateSnapshot,\n MikuSubmission,\n MikuSubmissionFilter,\n MikuVoterRestriction,\n ResetMikuVotesInput,\n ReviewMikuSubmissionInput,\n SetMikuVoterRestrictionInput,\n VoteMikuSubmissionInput,\n} from '../../types';\n\nexport interface MikuContestApiClient {\n getSnapshot(): Promise<MikuContestStateSnapshot>;\n updateContestConfig(patch: Partial<MikuContestConfig>): Promise<MikuContestConfig>;\n createSubmission(input: CreateMikuSubmissionInput, mode?: 'web' | 'miniapp'): Promise<MikuSubmission>;\n listSubmissions(filter?: MikuSubmissionFilter): Promise<MikuSubmission[]>;\n reviewSubmission(input: ReviewMikuSubmissionInput): Promise<MikuSubmission>;\n vote(input: VoteMikuSubmissionInput): Promise<MikuSubmission>;\n setVoterRestriction(input: SetMikuVoterRestrictionInput): Promise<MikuVoterRestriction>;\n resetVotes(input: ResetMikuVotesInput): Promise<{ removedVotes: number; affectedSubmissions: string[] }>;\n exportSubmissions(filter?: MikuSubmissionFilter): Promise<ArrayBuffer>;\n}\n\nexport type HttpMethod = 'GET' | 'POST' | 'PATCH';\n\nexport interface Requester {\n <T>(url: string, options?: { method?: HttpMethod; body?: unknown }): Promise<T>;\n}\n\nconst toQueryString = (filter?: MikuSubmissionFilter): string => {\n if (!filter) return '';\n const params = new URLSearchParams();\n if (filter.status) params.set('status', filter.status);\n if (filter.type) params.set('type', filter.type);\n if (filter.authorId) params.set('authorId', filter.authorId);\n if (filter.authorKeyword) params.set('authorKeyword', filter.authorKeyword);\n if (filter.titleKeyword) params.set('titleKeyword', filter.titleKeyword);\n const query = params.toString();\n return query ? `?${query}` : '';\n};\n\ninterface ApiEnvelope<T> {\n success: boolean;\n data?: T;\n error?: string;\n}\n\nconst unwrap = <T>(result: ApiEnvelope<T>): T => {\n if (!result.success || result.data === undefined) {\n throw new Error(result.error || '请求失败');\n }\n return result.data;\n};\n\nexport const createMikuContestApiClient = (\n basePath: string,\n requester: Requester,\n): MikuContestApiClient => {\n return {\n async getSnapshot() {\n const result = await requester<ApiEnvelope<MikuContestStateSnapshot>>(`${basePath}/contest`, { method: 'GET' });\n return unwrap(result);\n },\n async updateContestConfig(patch) {\n const result = await requester<ApiEnvelope<MikuContestConfig>>(`${basePath}/contest`, {\n method: 'PATCH',\n body: patch,\n });\n return unwrap(result);\n },\n async createSubmission(input, mode = 'web') {\n const result = await requester<ApiEnvelope<MikuSubmission>>(`${basePath}/submissions`, {\n method: 'POST',\n body: { payload: input, mode },\n });\n return unwrap(result);\n },\n async listSubmissions(filter) {\n const result = await requester<ApiEnvelope<MikuSubmission[]>>(\n `${basePath}/submissions${toQueryString(filter)}`,\n { method: 'GET' },\n );\n return unwrap(result);\n },\n async reviewSubmission(input) {\n const result = await requester<ApiEnvelope<MikuSubmission>>(`${basePath}/submissions/review`, {\n method: 'POST',\n body: input,\n });\n return unwrap(result);\n },\n async vote(input) {\n const result = await requester<ApiEnvelope<MikuSubmission>>(`${basePath}/votes`, {\n method: 'POST',\n body: input,\n });\n return unwrap(result);\n },\n async setVoterRestriction(input) {\n const result = await requester<ApiEnvelope<MikuVoterRestriction>>(`${basePath}/admin/voter-restrictions`, {\n method: 'POST',\n body: input,\n });\n return unwrap(result);\n },\n async resetVotes(input) {\n const result = await requester<ApiEnvelope<{ removedVotes: number; affectedSubmissions: string[] }>>(\n `${basePath}/admin/votes/reset`,\n {\n method: 'POST',\n body: input,\n },\n );\n return unwrap(result);\n },\n async exportSubmissions(filter) {\n const response = await fetch(`${basePath}/admin/submissions/export${toQueryString(filter)}`);\n if (!response.ok) {\n throw new Error(`导出失败: ${response.status}`);\n }\n return response.arrayBuffer();\n },\n };\n};\n","import { createMikuContestApiClient, type Requester } from '../api';\n\nexport interface MikuContestWebClientOptions {\n baseUrl?: string;\n basePath?: string;\n headers?: Record<string, string>;\n}\n\nconst defaultRequester = (options: MikuContestWebClientOptions): Requester => {\n const baseUrl = options.baseUrl || '';\n const commonHeaders = options.headers || {};\n\n return async <T>(url: string, requestOptions?: { method?: 'GET' | 'POST' | 'PATCH'; body?: unknown }): Promise<T> => {\n const response = await fetch(`${baseUrl}${url}`, {\n method: requestOptions?.method || 'GET',\n headers: {\n 'Content-Type': 'application/json',\n ...commonHeaders,\n },\n body: requestOptions?.body ? JSON.stringify(requestOptions.body) : undefined,\n });\n\n const json = (await response.json()) as T;\n return json;\n };\n};\n\nexport const createMikuContestWebClient = (options: MikuContestWebClientOptions = {}) => {\n const basePath = options.basePath || '/api/miku-contest';\n return createMikuContestApiClient(basePath, defaultRequester(options));\n};\n","import React, { useEffect, useMemo, useState } from 'react';\nimport { createMikuContestWebClient } from '../../../service/web';\nimport type { MikuContestApiClient } from '../../../service/api';\nimport type { MikuContestStateSnapshot, MikuSubmission } from '../../../types';\n\nexport interface MikuContestAudiencePageProps {\n client?: Pick<MikuContestApiClient, 'getSnapshot' | 'vote'>;\n voterId: string;\n title?: string;\n}\n\nconst MikuContestAudiencePage: React.FC<MikuContestAudiencePageProps> = ({\n client,\n voterId,\n title = '观众投票区',\n}) => {\n const api = useMemo(() => client || createMikuContestWebClient(), [client]);\n\n const [snapshot, setSnapshot] = useState<MikuContestStateSnapshot | null>(null);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const approvedWorks = useMemo(() => {\n if (!snapshot) return [];\n return snapshot.submissions.filter((item) => item.status === 'approved');\n }, [snapshot]);\n\n const loadSnapshot = async () => {\n setLoading(true);\n setError(null);\n try {\n const data = await api.getSnapshot();\n setSnapshot(data);\n } catch (e) {\n setError((e as Error).message);\n } finally {\n setLoading(false);\n }\n };\n\n useEffect(() => {\n void loadSnapshot();\n }, []);\n\n const handleVote = async (submission: MikuSubmission) => {\n if (!snapshot) return;\n try {\n await api.vote({\n contestId: snapshot.contest.id,\n submissionId: submission.id,\n voterId,\n });\n await loadSnapshot();\n } catch (e) {\n setError((e as Error).message);\n }\n };\n\n return (\n <section>\n <h2>{title}</h2>\n <button onClick={() => void loadSnapshot()} disabled={loading}>\n {loading ? '刷新中...' : '刷新数据'}\n </button>\n {error ? <p style={{ color: 'crimson' }}>错误:{error}</p> : null}\n\n {!snapshot ? null : (\n <>\n <p>\n 赛事:{snapshot.contest.name}|主题:{snapshot.contest.theme}\n </p>\n <p>\n 已过审作品:{approvedWorks.length}|每日上限:{snapshot.contest.votingRules.maxVotesPerDay}\n </p>\n <ul>\n {approvedWorks.map((work) => (\n <li key={work.id}>\n <strong>{work.title}</strong>({work.authorNickname})- 当前 {work.voteCount} 票{' '}\n <button onClick={() => void handleVote(work)}>投票</button>\n </li>\n ))}\n </ul>\n </>\n )}\n </section>\n );\n};\n\nexport default MikuContestAudiencePage;\n","import React, { useEffect, useMemo, useState } from 'react';\nimport { createMikuContestWebClient } from '../../../service/web';\nimport type { MikuContestApiClient } from '../../../service/api';\nimport type { CreateMikuSubmissionInput, MikuContestStateSnapshot, MikuSubmission, MikuWorkType } from '../../../types';\n\nexport interface MikuContestArtistPageProps {\n client?: Pick<MikuContestApiClient, 'getSnapshot' | 'createSubmission' | 'listSubmissions'>;\n authorId: string;\n authorNickname: string;\n title?: string;\n}\n\nconst workTypes: MikuWorkType[] = ['visual', 'video', 'text', 'audio'];\n\nconst MikuContestArtistPage: React.FC<MikuContestArtistPageProps> = ({\n client,\n authorId,\n authorNickname,\n title = '画师投稿区',\n}) => {\n const api = useMemo(() => client || createMikuContestWebClient(), [client]);\n\n const [snapshot, setSnapshot] = useState<MikuContestStateSnapshot | null>(null);\n const [mySubmissions, setMySubmissions] = useState<MikuSubmission[]>([]);\n const [submitting, setSubmitting] = useState(false);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const [titleInput, setTitleInput] = useState('');\n const [descInput, setDescInput] = useState('');\n const [coverImage, setCoverImage] = useState('');\n const [workType, setWorkType] = useState<MikuWorkType>('visual');\n\n const loadData = async () => {\n setLoading(true);\n setError(null);\n try {\n const [contest, mine] = await Promise.all([\n api.getSnapshot(),\n api.listSubmissions({ authorId }),\n ]);\n setSnapshot(contest);\n setMySubmissions(mine);\n } catch (e) {\n setError((e as Error).message);\n } finally {\n setLoading(false);\n }\n };\n\n useEffect(() => {\n void loadData();\n }, []);\n\n const submitWork = async () => {\n if (!snapshot) return;\n const payload: CreateMikuSubmissionInput = {\n contestId: snapshot.contest.id,\n authorId,\n authorNickname,\n title: titleInput,\n description: descInput,\n type: workType,\n tags: ['web'],\n content: {\n coverImage,\n images: coverImage ? [coverImage] : undefined,\n },\n };\n\n setSubmitting(true);\n setError(null);\n try {\n await api.createSubmission(payload, 'web');\n setTitleInput('');\n setDescInput('');\n setCoverImage('');\n await loadData();\n } catch (e) {\n setError((e as Error).message);\n } finally {\n setSubmitting(false);\n }\n };\n\n return (\n <section>\n <h2>{title}</h2>\n <button onClick={() => void loadData()} disabled={loading}>\n {loading ? '刷新中...' : '刷新数据'}\n </button>\n {error ? <p style={{ color: 'crimson' }}>错误:{error}</p> : null}\n\n <div>\n <h3>新建投稿</h3>\n <input value={titleInput} onChange={(e) => setTitleInput(e.target.value)} placeholder=\"作品标题\" />\n <br />\n <textarea value={descInput} onChange={(e) => setDescInput(e.target.value)} placeholder=\"作品简介\" />\n <br />\n <input value={coverImage} onChange={(e) => setCoverImage(e.target.value)} placeholder=\"封面 URL\" />\n <br />\n <select value={workType} onChange={(e) => setWorkType(e.target.value as MikuWorkType)}>\n {workTypes.map((item) => (\n <option value={item} key={item}>\n {item}\n </option>\n ))}\n </select>\n <button onClick={() => void submitWork()} disabled={submitting || !snapshot}>\n {submitting ? '提交中...' : '提交稿件'}\n </button>\n </div>\n\n <div>\n <h3>我的投稿({mySubmissions.length})</h3>\n <ul>\n {mySubmissions.map((item) => (\n <li key={item.id}>\n {item.title}|状态:{item.status}|票数:{item.voteCount}\n {item.rejectReason ? `|驳回:${item.rejectReason}` : ''}\n </li>\n ))}\n </ul>\n </div>\n </section>\n );\n};\n\nexport default MikuContestArtistPage;\n","import React, { useEffect, useMemo, useState } from 'react';\nimport { createMikuContestWebClient } from '../../../service/web';\nimport type { MikuContestApiClient } from '../../../service/api';\nimport type { MikuContestStateSnapshot, MikuSubmission } from '../../../types';\n\nexport interface MikuContestAdminPageProps {\n client?: Pick<\n MikuContestApiClient,\n | 'getSnapshot'\n | 'listSubmissions'\n | 'reviewSubmission'\n | 'setVoterRestriction'\n | 'resetVotes'\n | 'exportSubmissions'\n | 'updateContestConfig'\n >;\n adminId: string;\n title?: string;\n}\n\nconst MikuContestAdminPage: React.FC<MikuContestAdminPageProps> = ({\n client,\n adminId,\n title = '管理员面板',\n}) => {\n const api = useMemo(() => client || createMikuContestWebClient(), [client]);\n\n const [snapshot, setSnapshot] = useState<MikuContestStateSnapshot | null>(null);\n const [submissions, setSubmissions] = useState<MikuSubmission[]>([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [voterId, setVoterId] = useState('');\n\n const loadData = async () => {\n setLoading(true);\n setError(null);\n try {\n const [contest, list] = await Promise.all([api.getSnapshot(), api.listSubmissions()]);\n setSnapshot(contest);\n setSubmissions(list);\n } catch (e) {\n setError((e as Error).message);\n } finally {\n setLoading(false);\n }\n };\n\n useEffect(() => {\n void loadData();\n }, []);\n\n const review = async (item: MikuSubmission, action: 'approve' | 'reject') => {\n try {\n await api.reviewSubmission({\n submissionId: item.id,\n reviewerId: adminId,\n action,\n rejectReason: action === 'reject' ? '管理员驳回' : undefined,\n });\n await loadData();\n } catch (e) {\n setError((e as Error).message);\n }\n };\n\n const toggleVoting = async (enabled: boolean) => {\n if (!snapshot) return;\n try {\n await api.updateContestConfig({\n toggles: {\n ...snapshot.contest.toggles,\n votingEnabled: enabled,\n },\n });\n await loadData();\n } catch (e) {\n setError((e as Error).message);\n }\n };\n\n const setRestriction = async (banned: boolean) => {\n if (!snapshot || !voterId.trim()) return;\n try {\n await api.setVoterRestriction({\n voterId: voterId.trim(),\n banned,\n reason: banned ? '管理员手动封禁' : '管理员解除封禁',\n operatorId: adminId,\n });\n setVoterId('');\n } catch (e) {\n setError((e as Error).message);\n }\n };\n\n const resetVotesByVoter = async () => {\n if (!voterId.trim()) return;\n try {\n await api.resetVotes({ voterId: voterId.trim() });\n setVoterId('');\n await loadData();\n } catch (e) {\n setError((e as Error).message);\n }\n };\n\n const exportExcel = async () => {\n try {\n const data = await api.exportSubmissions();\n const blob = new Blob([data], {\n type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = 'miku-submissions.xlsx';\n a.click();\n URL.revokeObjectURL(url);\n } catch (e) {\n setError((e as Error).message);\n }\n };\n\n return (\n <section>\n <h2>{title}</h2>\n <button onClick={() => void loadData()} disabled={loading}>\n {loading ? '刷新中...' : '刷新数据'}\n </button>\n {error ? <p style={{ color: 'crimson' }}>错误:{error}</p> : null}\n\n <div>\n <h3>赛事开关</h3>\n <button onClick={() => void toggleVoting(true)} disabled={!snapshot}>\n 开启投票\n </button>\n <button onClick={() => void toggleVoting(false)} disabled={!snapshot}>\n 关闭投票\n </button>\n </div>\n\n <div>\n <h3>投稿审核({submissions.length})</h3>\n <ul>\n {submissions.map((item) => (\n <li key={item.id}>\n <strong>{item.title}</strong>|作者:{item.authorNickname}|状态:{item.status}|票数:{item.voteCount}\n {item.status === 'pending' ? (\n <>\n {' '}\n <button onClick={() => void review(item, 'approve')}>通过</button>\n <button onClick={() => void review(item, 'reject')}>驳回</button>\n </>\n ) : null}\n </li>\n ))}\n </ul>\n </div>\n\n <div>\n <h3>投票风控</h3>\n <input value={voterId} onChange={(e) => setVoterId(e.target.value)} placeholder=\"voterId\" />\n <button onClick={() => void setRestriction(true)} disabled={!snapshot}>\n 封禁投票\n </button>\n <button onClick={() => void setRestriction(false)} disabled={!snapshot}>\n 解除封禁\n </button>\n <button onClick={() => void resetVotesByVoter()}>清零该用户票数</button>\n </div>\n\n <div>\n <h3>导出</h3>\n <button onClick={() => void exportExcel()}>导出投稿 Excel</button>\n </div>\n </section>\n );\n};\n\nexport default MikuContestAdminPage;\n","import React, { useState } from 'react';\nimport MikuContestAudiencePage from './MikuContestAudiencePage';\nimport MikuContestArtistPage from './MikuContestArtistPage';\nimport MikuContestAdminPage from './MikuContestAdminPage';\n\ntype MikuContestView = 'audience' | 'artist' | 'admin';\n\nexport interface MikuContestPageProps {\n defaultView?: MikuContestView;\n viewerVoterId?: string;\n artistId?: string;\n artistNickname?: string;\n adminId?: string;\n}\n\nconst MikuContestPage: React.FC<MikuContestPageProps> = ({\n defaultView = 'audience',\n viewerVoterId = 'viewer-demo',\n artistId = 'artist-demo',\n artistNickname = 'Demo 画师',\n adminId = 'admin-demo',\n}) => {\n const [view, setView] = useState<MikuContestView>(defaultView);\n\n return (\n <div>\n <h1>Miku Contest</h1>\n <div>\n <button onClick={() => setView('audience')}>观众端</button>\n <button onClick={() => setView('artist')}>画师端</button>\n <button onClick={() => setView('admin')}>管理员端</button>\n </div>\n\n {view === 'audience' ? <MikuContestAudiencePage voterId={viewerVoterId} /> : null}\n {view === 'artist' ? <MikuContestArtistPage authorId={artistId} authorNickname={artistNickname} /> : null}\n {view === 'admin' ? <MikuContestAdminPage adminId={adminId} /> : null}\n </div>\n );\n};\n\nexport default MikuContestPage;\n"]}
|