add icons and scroll

This commit is contained in:
2026-08-01 20:56:40 +07:00
parent f8b4b6c6a8
commit d84539f1d0
2 changed files with 156 additions and 5 deletions
+111 -1
View File
@@ -4,12 +4,24 @@ import MetalPlate from './MetalPlate.vue';
import HeaderText from './HeaderText.vue';
import { ref, type PropType } from 'vue';
import { Footsteps } from '@vicons/ionicons5'
import { ContactMailRound } from '@vicons/material'
import { Icon } from '@vicons/utils'
const code = ref('')
const props = defineProps({
addAction: {
type: Function as PropType<(param: string) => void>,
required: true,
required: true
},
allPlacesCount: {
type: Number,
required: true
},
newPlacesCount: {
type: Number,
required: true
}
})
@@ -18,6 +30,24 @@ async function addClickButton() {
code.value = ''
}
function getTextForCounter(count: number): string[] {
count = count % 100
const number1 = Math.floor(count / 10) % 10
const number2 = count % 10
let text1 = ''
let text2 = ''
if (number1 > 0) {
text1 = String(number1)
}
if (number2 > 0 || number1 > 0) {
text2 = String(number2)
}
return [text1, text2]
}
</script>
<template>
@@ -25,8 +55,31 @@ async function addClickButton() {
<BeltBlock class="input-form">
<MetalPlate class="controller-metal controller-metal-left"></MetalPlate>
<MetalPlate class="controller-metal controller-metal-right"></MetalPlate>
<div class="center-block-700">
<div class="controller">
<div class="counters-block">
<div class="counter-block">
<div class="places-text">
<Icon class="icon-for-counter">
<Footsteps />
</Icon>
</div>
<div v-for="n in getTextForCounter(allPlacesCount)" class="places-text places-text-number">{{ n }}</div>
</div>
<div class="counter-block">
<div class="places-text">
<Icon class="icon-for-counter">
<ContactMailRound />
</Icon>
</div>
<div v-for="n in getTextForCounter(newPlacesCount)" class="places-text places-text-number">{{ n }}</div>
</div>
</div>
<div class="game-input">
<input id="run" class="game-input-run" v-model="code" type="text" placeholder="Место назначения">
</div>
@@ -135,6 +188,63 @@ async function addClickButton() {
cursor: pointer;
}
.new-places-label,
.all-places-label {
position: absolute;
height: 48px;
}
.new-places-label {
top: 14px;
left: 85px;
width: 40px;
}
.all-places-label {
top: 14px;
left: 15px;
width: 60px;
}
.places-text {
background-color: #d1cebd;
border: 1px solid #9e9982;
color: #111;
font-size: 1.4em;
font-family: 'font_old_typer';
border-radius: 7px;
margin: 0 1px;
display: inline-block;
text-align: center;
background-image: url("@/assets/images/paper.jpg");
background-size: cover;
box-shadow: 0px 0px 5px black;
}
.places-text-number {
width: 18px;
}
.counters-block {
position: absolute;
top: -40px;
left: 23px;
margin: 20px 10px 20px 5px;
}
.counter-block {
display: inline-block;
margin: 5px;
}
.icon-for-counter {
position: relative;
top: 3px;
margin: 0 4px;
}
@media (min-width: 1025px) {
.center-block-700 {
width: 700px;
+45 -4
View File
@@ -4,7 +4,7 @@ import type { Place, Story } from '@/api/generated/crabs/evening_detective_serve
import HeaderMenu from '@/components/HeaderMenu.vue'
import { NCard, NFlex, NInput, NModal, NSpace, NText, NUpload, NUploadDragger, NAlert, NTag, NButton } from 'naive-ui'
import { useMessage } from 'naive-ui';
import { ref } from 'vue';
import { nextTick, ref } from 'vue';
import { useRoute } from 'vue-router';
import PlaceBlock from '@/components/PlaceBlock.vue'
import GameInputForm from '@/components/GameInputForm.vue'
@@ -16,12 +16,23 @@ const route = useRoute()
const teamId = route.params.id
const password = route.query.password
const newPlacesCount = ref(0)
const scrollContainer = ref<HTMLDivElement | null>();
const story = ref<AdvancedStory>({
places: []
})
const code = ref('')
const isAtBottom = () => {
const container = scrollContainer.value;
if (!container) return false;
return container.scrollHeight - container.scrollTop - container.clientHeight < 1;
};
async function getStory() {
const res = await client.GetTeamStory({
id: Number(teamId),
@@ -31,6 +42,15 @@ async function getStory() {
message.error(res.error!)
return
}
const wasAtBottom = isAtBottom();
if (wasAtBottom) {
newPlacesCount.value = 0
}
const oldCount = story.value.places.length
story.value = {
places: res.story!.places?.map((item) => {
return {
@@ -39,6 +59,19 @@ async function getStory() {
}
}) || []
}
const newCount = story.value.places.length
if (newCount > oldCount && oldCount != 0) {
newPlacesCount.value += newCount - oldCount
}
await nextTick();
if (wasAtBottom && oldCount != 0) {
const container = scrollContainer.value;
if (!container) return false;
container.scrollTop = container.scrollHeight;
}
}
getStory()
@@ -54,6 +87,9 @@ async function addAction(newCode: string) {
}
await getStory()
code.value = ''
await nextTick();
newPlacesCount.value = 0
}
type AdvancedStory = {
@@ -68,13 +104,14 @@ type AdvancedPlace = {
useSimplePolling(() => {
getStory()
}, 2000);
</script>
<template>
<div class="center-block-custom bottom-margin">
<div class="center-block-custom bottom-margin smooth-scroll" ref="scrollContainer">
<div class="width700">
<GameInputForm :addAction="addAction"></GameInputForm>
<GameInputForm :addAction="addAction" :allPlacesCount="story.places.length"
:newPlacesCount="newPlacesCount"></GameInputForm>
<PlaceBlock :place="advancedPlace.place" v-model:mode="advancedPlace.mode"
v-for="advancedPlace in story.places" v-bind:key="advancedPlace.place.code" :addAction="addAction">
@@ -90,6 +127,10 @@ useSimplePolling(() => {
height: calc(100dvh - 70px - 76px);
}
.smooth-scroll {
scroll-behavior: smooth;
}
@media (width >=1024px) {
.bottom-margin {
height: calc(100dvh - 70px - 76px);