Files
evening_detective_frontend/src/components/PlaceBlock.vue
T
2026-07-24 14:02:45 +07:00

320 lines
8.7 KiB
Vue

<script setup lang="ts">
import { NDynamicInput, NInput, NInputGroup, NSpace, NSwitch, NText, NUpload, NUploadDragger } from 'naive-ui'
import type { PropType } from 'vue'
import { LockOutlined } from '@vicons/material'
import { Icon } from '@vicons/utils'
import { Key24Regular } from '@vicons/fluent'
import type { Application, Door, Key, Place } from '@/api/generated/crabs/evening_detective_server'
const props = defineProps({
newPlaceCode: {
type: String,
},
newPlaceName: {
type: String,
},
newPlaceText: {
type: String,
},
place: {
type: Object as PropType<Place>,
},
mode: {
type: String,
required: true,
},
})
const emit = defineEmits<{
'update:newPlaceCode': [value: string],
'update:newPlaceName': [value: string],
'update:newPlaceText': [value: string],
'update:mode': [value: string]
}>()
const updateCode = (code: string) => {
emit('update:newPlaceCode', code)
}
const updateName = (name: string) => {
emit('update:newPlaceName', name)
}
const updateText = (text: string) => {
emit('update:newPlaceText', text)
}
const updateMode = (mode: string) => {
emit('update:mode', mode)
}
function onCreateApplication(): Application {
return {
name: undefined,
image: undefined,
}
}
function onCreateDoor(): Door {
return {
code: undefined,
name: undefined,
keys: undefined,
}
}
function onCreateKey(): Key {
return {
name: undefined,
}
}
</script>
<template>
<div v-if="mode == 'mini-add'" class="place-block-hover show-editor-block" @click="updateMode('add')">
<p class="place-image-plus">+</p>
</div>
<div v-if="mode == 'add'" class="place-block">
<n-space vertical>
<n-input-group>
<n-input :style="{ width: '20%' }" :value="newPlaceCode" @update:value="updateCode" type="text"
placeholder="Ы-1" />
<n-input :style="{ width: '80%' }" :value="newPlaceName" @update:value="updateName" type="text"
placeholder="Дом №Ы" />
</n-input-group>
<n-input :value="newPlaceText" @update:value="updateText" type="textarea" :autosize="{ minRows: 3 }"
placeholder="В начале было слово..." />
<slot></slot>
</n-space>
</div>
<div v-if="mode == 'show-editor'" class="place-block-hover show-editor-block" @click="updateMode('edit')">
<p>[{{ props.place.code }}] - {{ props.place.name }}</p>
<div class="message-content">
<div v-if="props.place.image">
<div class="message-image-border">
<img :src="props.place.image" class="message-image">
</div>
</div>
{{ props.place.text }}
</div>
<div v-for="door in props.place.doors" v-bind:key="door.code" class="door-block">
{{ door.name }} <span v-for="key in door.keys" class="key-block">
<Icon class="lock-icon">
<LockOutlined />
</Icon> {{ key.name }}
</span>
</div>
<div v-for="application in props.place.applications" v-bind:key="application.name" class="application-block">
Приложение: {{ application.name }}
</div>
<p class="gray-block">
<span v-if="place.hidden">
Скрытая точка
</span>
<span v-for="key in place.keys" class="key-block">
<Icon class="lock-icon">
<Key24Regular />
</Icon> {{ key.name }}
</span>
</p>
</div>
<div v-if="mode == 'edit'" class="place-block">
<n-space vertical>
<n-input-group>
<n-input :style="{ width: '20%' }" v-model:value="props.place.code" type="text" placeholder="Ы-1" />
<n-input :style="{ width: '80%' }" v-model:value="props.place.name" type="text" placeholder="Дом №Ы" />
</n-input-group>
<n-input v-model:value="props.place.text" type="textarea" :autosize="{ minRows: 3 }"
placeholder="В начале было слово..." />
<p class="settings-header">Картинка</p>
<n-input v-model:value="props.place.image" type="text" placeholder="Не задано" disabled />
<n-upload directory-dnd :max="1" v-model:file-list="props.place.fileList">
<n-upload-dragger>
<n-text style="font-size: 16px">
Щелкните или перетащите файл в эту область для загрузки
</n-text>
</n-upload-dragger>
</n-upload>
<n-switch v-model:value="props.place.hidden">
<template #checked>
Скрытая точка
</template>
<template #unchecked>
Обычная точка
</template>
</n-switch>
Улики
<n-dynamic-input v-model:value="props.place.applications" :on-create="onCreateApplication">
<template #create-button-default> Добавить улику </template>
<template #default="{ value }">
<div style="display: flex; align-items: center; width: 100%">
<n-input v-model:value="value.name" type="text" placeholder="Название" />
</div>
</template>
</n-dynamic-input>
Действия
<n-dynamic-input v-model:value="props.place.doors" :on-create="onCreateDoor">
<template #create-button-default> Добавить действие </template>
<template #action="{ index, create, remove }">
<div style="display: flex; flex-direction: column;">
<n-button @click="() => create(index)" class="plus-minus-buttons">+</n-button>
<n-button disabled @click="() => remove(index)" class="plus-minus-buttons">-</n-button>
</div>
</template>
<template #default="{ value: door }">
<div style="display: flex; flex-direction: column; width: 100%">
<div class="door-add-block">
<n-input-group>
<n-input :style="{ width: '30%' }" v-model:value="door.code" type="text" placeholder="Код" />
<n-input :style="{ width: '70%' }" v-model:value="door.name" type="text" placeholder="Название" />
</n-input-group>
<p class="need-keys-text">Требуемые ключи</p>
<div class="door-field">
<n-dynamic-input v-model:value="door.keys" :on-create="onCreateKey">
<template #create-button-default> Добавить ключ </template>
<template #default="{ value: key }">
<div style="display: flex; align-items: center; width: 100%">
<n-input v-model:value="key.name" type="text" placeholder="Название" />
</div>
</template>
</n-dynamic-input>
</div>
</div>
</div>
</template>
</n-dynamic-input>
Получаемые ключи
<n-dynamic-input v-model:value="props.place.keys" :on-create="onCreateKey">
<template #create-button-default> Добавить ключ </template>
<template #default="{ value: key }">
<div style="display: flex; align-items: center; width: 100%">
<n-input v-model:value="key.name" type="text" placeholder="Название" />
</div>
</template>
</n-dynamic-input>
<div class="slot-block">
<slot></slot>
</div>
</n-space>
</div>
</template>
<style scoped>
.place-block-hover,
.place-block {
margin: 10px 0;
padding: 20px;
border-radius: 10px;
border: 1px solid #444;
}
.place-block-hover:hover {
color: #63e2b7;
}
.place-image-plus {
font-size: 30px;
display: flex;
justify-content: center;
align-items: center;
}
.show-editor-block {
cursor: pointer;
}
.message-content {
white-space: pre-wrap;
}
.gray-block {
margin-top: 5px;
color: #777;
}
.application-block {
margin-right: 5px;
color: burlywood;
}
.door-block {
display: inline-block;
margin: 10px 5px 10px 0;
padding: 5px 10px;
border-radius: 5px;
background-color: #222;
color: #eee;
}
.door-add-block {
padding: 10px;
border-radius: 5px;
background-color: #222;
color: #eee;
}
.key-block {
color: gold;
margin-left: 10px;
}
.lock-icon {
position: relative;
top: 2px;
}
.door-field {
margin-top: 10px;
}
.need-keys-text {
color: gold;
margin: 15px 0 0 0;
padding: 0;
}
.slot-block {
margin-top: 10px;
}
.plus-minus-buttons {
border: 1px solid #444;
padding: 0 13px;
margin: 0 0 10px 5px;
text-align: center;
color: #eee;
font-size: 25px;
border-radius: 40px;
}
.plus-minus-buttons:hover {
cursor: pointer;
color: #63e2b7;
}
.message-image-border {
width: 40%;
float: left;
padding: 7px;
margin-right: 15px;
background-image: url("@/assets/images/paper_white.jpg");
background-size: cover;
box-shadow: 0px 3px 15px rgb(98, 98, 98);
transform: rotate(-3deg);
}
.message-image {
width: 100%;
}
</style>