add screens

This commit is contained in:
2026-09-10 03:39:25 +07:00
parent 907360e7cc
commit 625f7d87fd
47 changed files with 226 additions and 49 deletions
@@ -518,6 +518,15 @@ export type GetScreenStateRsp = {
state: ScreenState | undefined;
};
export type GetGameScreensReq = {
gameId: number | undefined;
};
export type GetGameScreensRsp = {
error: string | undefined;
screens: ScreenState[] | undefined;
};
export type BindScreenReq = {
screenId: string | undefined;
accessToken: string | undefined;
@@ -537,6 +546,15 @@ export type UnbindScreenRsp = {
error: string | undefined;
};
export type SetScreenSoundReq = {
screenId: string | undefined;
soundEnabled: boolean | undefined;
};
export type SetScreenSoundRsp = {
error: string | undefined;
};
export type SetScreenModeReq = {
gameId: number | undefined;
mode: ScreenMode | undefined;
@@ -830,10 +848,12 @@ export interface EveningDetectiveServer {
CreateScreenSession(request: CreateScreenSessionReq): Promise<CreateScreenSessionRsp>;
// Получить состояние экрана по его токену. Вызывается самим экраном.
GetScreenState(request: GetScreenStateReq): Promise<GetScreenStateRsp>;
GetGameScreens(request: GetGameScreensReq): Promise<GetGameScreensRsp>;
// Привязать экран к игре. Требует авторизацию организатора, автора или администратора.
BindScreen(request: BindScreenReq): Promise<BindScreenRsp>;
// Отвязать экран от игры. Требует авторизацию организатора, автора или администратора.
UnbindScreen(request: UnbindScreenReq): Promise<UnbindScreenRsp>;
SetScreenSound(request: SetScreenSoundReq): Promise<SetScreenSoundRsp>;
// Изменить общий режим показа всех экранов игры.
SetScreenMode(request: SetScreenModeReq): Promise<SetScreenModeRsp>;
// Отправить общую команду экранам игры.
@@ -1701,6 +1721,26 @@ export function createEveningDetectiveServerClient(
method: "GetScreenState",
}) as Promise<GetScreenStateRsp>;
},
GetGameScreens(request) { // eslint-disable-line @typescript-eslint/no-unused-vars
if (!request.gameId) {
throw new Error("missing required field request.gameId");
}
const path = `api/games/${request.gameId}/screens`; // eslint-disable-line quotes
const body = null;
const queryParams: string[] = [];
let uri = path;
if (queryParams.length > 0) {
uri += `?${queryParams.join("&")}`
}
return handler({
path: uri,
method: "GET",
body,
}, {
service: "EveningDetectiveServer",
method: "GetGameScreens",
}) as Promise<GetGameScreensRsp>;
},
BindScreen(request) { // eslint-disable-line @typescript-eslint/no-unused-vars
if (!request.screenId) {
throw new Error("missing required field request.screenId");
@@ -1741,6 +1781,26 @@ export function createEveningDetectiveServerClient(
method: "UnbindScreen",
}) as Promise<UnbindScreenRsp>;
},
SetScreenSound(request) { // eslint-disable-line @typescript-eslint/no-unused-vars
if (!request.screenId) {
throw new Error("missing required field request.screenId");
}
const path = `api/screens/${request.screenId}/sound`; // eslint-disable-line quotes
const body = JSON.stringify(request);
const queryParams: string[] = [];
let uri = path;
if (queryParams.length > 0) {
uri += `?${queryParams.join("&")}`
}
return handler({
path: uri,
method: "POST",
body,
}, {
service: "EveningDetectiveServer",
method: "SetScreenSound",
}) as Promise<SetScreenSoundRsp>;
},
SetScreenMode(request) { // eslint-disable-line @typescript-eslint/no-unused-vars
if (!request.gameId) {
throw new Error("missing required field request.gameId");
+78
View File
@@ -13,6 +13,7 @@ import type {
Application,
Game,
Question,
ScreenState,
Team,
TeamAnswers,
} from '@/api/generated/crabs/evening_detective_server'
@@ -52,6 +53,7 @@ const game = ref<Game>({
scenario: undefined,
teams: undefined,
})
const screens = ref<ScreenState[]>([])
async function getGame(id: number) {
const res = await client.GetGame({ id: id })
@@ -89,6 +91,33 @@ async function getGame(id: number) {
}
getGame(Number(gameId))
async function getGameScreens() {
const res = await client.GetGameScreens({ gameId: Number(gameId) })
if (res.error != '') {
return
}
screens.value = res.screens || []
}
getGameScreens()
async function setSoundScreen(screen: ScreenState) {
const res = await client.SetScreenSound({ screenId: screen.id, soundEnabled: true })
if (res.error != '') {
message.error(res.error!)
return
}
await getGameScreens()
}
async function unbindScreen(screen: ScreenState) {
const res = await client.UnbindScreen({ screenId: screen.id })
if (res.error != '') {
message.error(res.error!)
return
}
await getGameScreens()
}
async function toTeamStory(id: number, password: string, newTab: boolean = false) {
const uri = '/team-story/' + id + '?password=' + password
if (newTab) {
@@ -250,6 +279,7 @@ async function openQR(url: string, text: string) {
useSimplePolling(() => {
getGame(Number(gameId))
getGameScreens()
}, 2000)
async function resetGame() {
@@ -477,6 +507,22 @@ useSimplePolling(() => {
</n-flex>
</div>
<div v-if="screens.length > 0" class="screens-block">
<p class="screens-title">Экраны</p>
<div v-for="screen in screens" :key="screen.id" class="screen-row">
<div>
<div>Экран {{ screen.id }}</div>
<div class="screen-status">{{ screen.status }}</div>
<div v-if="screen.error" class="screen-error">{{ screen.error }}</div>
</div>
<n-flex justify="end">
<n-tag v-if="screen.soundEnabled" type="success">Со звуком</n-tag>
<n-button v-else size="small" @click="setSoundScreen(screen)">Назначить звук</n-button>
<n-button size="small" type="error" ghost @click="unbindScreen(screen)">Отвязать</n-button>
</n-flex>
</div>
</div>
<div class="team-block team-block-hover" @click="showAddTeamModal = true">
<p class="place-image-plus">+</p>
</div>
@@ -823,4 +869,36 @@ useSimplePolling(() => {
.answers-score-input {
width: 110px;
}
.screens-block {
background-color: #222;
border-radius: 10px;
margin: 12px 0;
padding: 16px;
}
.screens-title {
font-size: 18px;
font-weight: 600;
margin-bottom: 12px;
}
.screen-row {
align-items: center;
border-top: 1px solid rgb(255 255 255 / 10%);
display: flex;
gap: 12px;
justify-content: space-between;
padding: 12px 0;
}
.screen-status {
color: #aaa;
font-size: 13px;
}
.screen-error {
color: #f2a2a2;
font-size: 13px;
}
</style>