add input form

This commit is contained in:
Владимир Фёдоров 2026-03-22 02:50:02 +07:00
parent 6b261804fd
commit f4964e6355
3 changed files with 124 additions and 112 deletions

View File

@ -1,6 +1,31 @@
<script setup lang="ts"> <script setup lang="ts">
import BeltBlock from './BeltBlock.vue'; import BeltBlock from './BeltBlock.vue';
import { apiLetsgo } from './client';
import MetalPlate from './MetalPlate.vue'; import MetalPlate from './MetalPlate.vue';
import HeaderText from './HeaderText.vue';
import { ref } from 'vue';
const place = ref("")
interface Props {
gameState: string
login: string
password: string
}
const props = withDefaults(defineProps<Props>(), {})
const isPlaceFilled = defineModel<boolean>();
async function addAction() {
isPlaceFilled.value = true
const placeValue = place.value.trim()
if (placeValue === "") {
place.value = ""
return
}
await apiLetsgo(props.login, props.password, placeValue)
place.value = ""
}
</script> </script>
<template> <template>
@ -9,7 +34,19 @@ import MetalPlate from './MetalPlate.vue';
<MetalPlate class="controller-metal controller-metal-left"></MetalPlate> <MetalPlate class="controller-metal controller-metal-left"></MetalPlate>
<MetalPlate class="controller-metal controller-metal-right"></MetalPlate> <MetalPlate class="controller-metal controller-metal-right"></MetalPlate>
<div class="center-block-custom"> <div class="center-block-custom">
<slot></slot> <form @submit.prevent="addAction">
<div class="controller">
<div class="game-input">
<div class="game-input-run-left"></div>
<input id="run" class="game-input-run" v-model="place" type="text" placeholder="Место назначения"
:disabled="props.gameState !== 'RUN'">
</div>
<div class="game-button-run-shadow"></div>
<button class="game-button-run" type="submit" :disabled="props.gameState !== 'RUN'">
<HeaderText>Поехали</HeaderText>
</button>
</div>
</form>
</div> </div>
</BeltBlock> </BeltBlock>
</div> </div>
@ -45,4 +82,78 @@ import MetalPlate from './MetalPlate.vue';
.controller-metal-right { .controller-metal-right {
right: -15px; right: -15px;
} }
.controller {
display: flex;
}
.game-input {
position: relative;
top: 14px;
left: 25px;
height: 50px;
width: calc(100% - 150px - 50px);
}
.game-input-run-left {
height: 50px;
width: 25px;
position: absolute;
left: 0px;
background-image: url("@/assets/input_left.png");
background-size: cover;
}
.game-input-run {
height: 100%;
width: 100%;
margin-left: 15px;
padding-left: 12px;
background-image: url("@/assets/input_center.png");
background-size: cover;
border: 0;
font-size: 18px;
font-family: a_OldTyper;
}
.game-input-run::placeholder {
color: #333333;
}
.game-input-run:focus {
border: 0;
outline: none;
}
.game-button-run-shadow {
position: absolute;
right: 10px;
top: -5px;
height: 80px;
width: 150px;
box-shadow: -5px 5px 10px black;
}
.game-button-run {
background-image: url("@/assets/button.png");
background-size: cover;
font-size: 1.5em;
position: absolute;
right: 10px;
top: -5px;
height: 80px;
width: 155px;
border: 0;
background-color: transparent;
margin: 0;
padding: 0;
}
.game-button-run:hover {
/* TODO */
}
.game-button-run:disabled {
/* TODO */
}
</style> </style>

View File

@ -3,20 +3,18 @@ import { ref, nextTick, watch, onMounted } from 'vue';
import { useRoute, useRouter } from 'vue-router'; import { useRoute, useRouter } from 'vue-router';
import GameHeader from './GameHeader.vue'; import GameHeader from './GameHeader.vue';
import type { Action, Door, Team } from './models'; import type { Action, Door, Team } from './models';
import { apiGetGame, apiGetTeam, apiLetsgo } from './client'; import { apiGetGame, apiGetTeam } from './client';
import { UnauthorizedError } from './UnauthorizedError'; import { UnauthorizedError } from './UnauthorizedError';
import WelcomeGameBlock from './WelcomeGameBlock.vue'; import WelcomeGameBlock from './WelcomeGameBlock.vue';
import HeaderText from './HeaderText.vue';
import MessageCloud from './MessageCloud.vue'; import MessageCloud from './MessageCloud.vue';
import GameInputForm from './GameInputForm.vue'; import GameInputForm from './GameInputForm.vue';
const router = useRouter(); const router = useRouter();
const route = useRoute(); const route = useRoute();
const inputPlace = ref(false) const isPlaceFilled = ref(false)
const login = ref("") const login = ref("")
const password = ref("") const password = ref("")
const place = ref("")
const team = ref<Team>({ name: "", actions: [] }) const team = ref<Team>({ name: "", actions: [] })
const actions = ref<Action[]>([]) const actions = ref<Action[]>([])
const scrollContainer = ref<HTMLDivElement | null>(); const scrollContainer = ref<HTMLDivElement | null>();
@ -58,17 +56,6 @@ async function getTeam() {
} }
} }
async function addAction() {
inputPlace.value = true
const placeValue = place.value.trim()
if (placeValue === "") {
place.value = ""
return
}
await apiLetsgo(login.value, password.value, placeValue)
place.value = ""
}
const scrollToBottom = async (behavior: ScrollBehavior = 'smooth'): Promise<void> => { const scrollToBottom = async (behavior: ScrollBehavior = 'smooth'): Promise<void> => {
await nextTick(); await nextTick();
if (scrollContainer.value) { if (scrollContainer.value) {
@ -96,11 +83,11 @@ async function getGame() {
// Автоматическая прокрутка при изменении items // Автоматическая прокрутка при изменении items
watch(actions, () => { watch(actions, () => {
if (inputPlace.value === false) { if (isPlaceFilled.value === false) {
return return
} }
scrollToBottom(); scrollToBottom();
inputPlace.value = false isPlaceFilled.value = false
}, { deep: true }); }, { deep: true });
let intervalId = 0 let intervalId = 0
@ -135,21 +122,7 @@ onMounted(() => {
<GameHeader></GameHeader> <GameHeader></GameHeader>
<GameInputForm> <GameInputForm v-model="isPlaceFilled" :gameState="gameState" :login="login" :password="password"></GameInputForm>
<form @submit.prevent="addAction">
<div class="controller">
<div class="game-input">
<div class="game-input-run-left"></div>
<input id="run" class="game-input-run" v-model="place" type="text" placeholder="Место назначения"
:disabled="gameState !== 'RUN'">
</div>
<div class="game-button-run-shadow"></div>
<button class="game-button-run" type="submit" :disabled="gameState !== 'RUN'">
<HeaderText>Поехали</HeaderText>
</button>
</div>
</form>
</GameInputForm>
<!-- Действия --> <!-- Действия -->
<div class="messages-block" ref="scrollContainer"> <div class="messages-block" ref="scrollContainer">
@ -161,7 +134,8 @@ onMounted(() => {
</div> </div>
<div v-else> <div v-else>
<div v-for="action in team.actions" :key="action.id"> <div v-for="action in team.actions" :key="action.id">
<MessageCloud :action="action" :gameState="gameState" :login="login" :password="password"></MessageCloud> <MessageCloud v-model="isPlaceFilled" :action="action" :gameState="gameState" :login="login"
:password="password"></MessageCloud>
</div> </div>
</div> </div>
</div> </div>
@ -178,8 +152,6 @@ onMounted(() => {
background-color: gray; background-color: gray;
} }
.game-input-form-shadow { .game-input-form-shadow {
height: 90px; height: 90px;
width: 120%; width: 120%;
@ -234,78 +206,4 @@ onMounted(() => {
/* Добавляем троеточие */ /* Добавляем троеточие */
font-size: medium; font-size: medium;
} }
.controller {
display: flex;
}
.game-button-run {
background-image: url("@/assets/button.png");
background-size: cover;
font-size: 1.5em;
position: absolute;
right: 10px;
top: -5px;
height: 80px;
width: 155px;
border: 0;
background-color: transparent;
margin: 0;
padding: 0;
}
.game-button-run-shadow {
position: absolute;
right: 10px;
top: -5px;
height: 80px;
width: 150px;
box-shadow: -5px 5px 10px black;
}
.game-button-run:hover {
/* TODO */
}
.game-button-run:disabled {
/* TODO */
}
.game-input {
position: relative;
top: 14px;
left: 25px;
height: 50px;
width: calc(100% - 150px - 50px);
}
.game-input-run-left {
height: 50px;
width: 25px;
position: absolute;
left: 0px;
background-image: url("@/assets/input_left.png");
background-size: cover;
}
.game-input-run {
height: 100%;
width: 100%;
margin-left: 15px;
padding-left: 12px;
background-image: url("@/assets/input_center.png");
background-size: cover;
border: 0;
font-size: 18px;
font-family: a_OldTyper;
}
.game-input-run::placeholder {
color: #333333;
}
.game-input-run:focus {
border: 0;
outline: none;
}
</style> </style>

View File

@ -10,12 +10,15 @@ interface Props {
} }
const props = withDefaults(defineProps<Props>(), {}) const props = withDefaults(defineProps<Props>(), {})
const isPlaceFilled = defineModel<boolean>();
function clickCollapse() { function clickCollapse() {
// eslint-disable-next-line vue/no-mutating-props // eslint-disable-next-line vue/no-mutating-props
props.action.isOpen = !props.action.isOpen props.action.isOpen = !props.action.isOpen
} }
async function letsgo(place: string) { async function letsgo(place: string) {
isPlaceFilled.value = true
await apiLetsgo(props.login, props.password, place) await apiLetsgo(props.login, props.password, place)
} }
</script> </script>
@ -33,12 +36,12 @@ async function letsgo(place: string) {
<img v-bind:src="props.action.image" class="message-image" /> <img v-bind:src="props.action.image" class="message-image" />
</div>{{ props.action.text }} </div>{{ props.action.text }}
</div> </div>
<hr class="hr" v-if="props.action.buttons?.length"/> <hr class="hr" v-if="props.action.buttons?.length" />
<button v-for="door in props.action.buttons" :key="door.code" class="button-dialog" v-on:click="letsgo(door.code)" <button v-for="door in props.action.buttons" :key="door.code" class="button-dialog" v-on:click="letsgo(door.code)"
:disabled="gameState !== 'RUN' || !door.show"> :disabled="gameState !== 'RUN' || !door.show">
{{ door.name }} {{ door.name }}
</button> </button>
<hr class="hr" v-if="props.action.applications.length"/> <hr class="hr" v-if="props.action.applications.length" />
<div class="message-footer" v-for="application in props.action.applications" :key="application.name"> <div class="message-footer" v-for="application in props.action.applications" :key="application.name">
{{ application.number }} Приложение: {{ application.name }} {{ application.number }} Приложение: {{ application.name }}
</div> </div>