251 lines
9.1 KiB
Vue
251 lines
9.1 KiB
Vue
<script setup lang="ts">
|
||
import type { FileUploadUploadEvent } from '~/node_modules/primevue/fileupload/FileUpload.d.ts'
|
||
import { Grid } from 'pathfinding'
|
||
|
||
const config = useRuntimeConfig()
|
||
const point_array = useState('point_array', () => [[]])
|
||
const chunk_size = useState('chunk_size', () => 8)
|
||
const threshold = useState('threshold', () => 20)
|
||
const grid_redraw = useState<boolean>('grid_redraw', () => false)
|
||
const active_point = useState<{ x: number, y: number } | undefined>('active_point', () => undefined)
|
||
const target_points = useState<{ x: number, y: number, type: string }[]>('target_points', () => [])
|
||
|
||
const loading = ref<boolean>(false)
|
||
|
||
const grid = ref<Grid>()
|
||
const startPoint = ref<number[]>()
|
||
const endPoint = ref<number[]>()
|
||
const startToEndPath = ref()
|
||
|
||
const selectFile = ref()
|
||
const loadingFile = ref(false)
|
||
const files = ref([])
|
||
|
||
const cw = 1920
|
||
const ch = 800
|
||
|
||
const title = ref()
|
||
|
||
const dataToState = async (data: any) => {
|
||
point_array.value = data.np_field
|
||
title.value = data.title
|
||
if (data.d_size) chunk_size.value = data.d_size
|
||
if (data.d_border) threshold.value = data.d_border
|
||
|
||
try {
|
||
const res = await fetch(`${config.public.apiBase}/api/floorplan/${data.id}/points`)
|
||
if (res.status !== 200) {
|
||
throw new Error()
|
||
}
|
||
const points = await res.json()
|
||
target_points.value = points.points
|
||
target_array.value = points.points
|
||
} catch (error) {
|
||
console.log(error)
|
||
target_points.value = []
|
||
target_array.value = []
|
||
}
|
||
}
|
||
|
||
const onUpload = async (event: FileUploadUploadEvent) => {
|
||
try {
|
||
const data = JSON.parse(event.xhr.response)
|
||
await dataToState(data.response)
|
||
} catch (error) {
|
||
console.log(error)
|
||
}
|
||
}
|
||
const selectFileEvent = async () => {
|
||
loadingFile.value = true
|
||
try {
|
||
const res = await fetch(`${config.public.apiBase}/api/floorplan/${selectFile.value}`)
|
||
if (res.status !== 200) {
|
||
throw new Error()
|
||
}
|
||
const data = await res.json()
|
||
await dataToState(data)
|
||
} catch (error) {
|
||
console.log(error)
|
||
}
|
||
loadingFile.value = false
|
||
}
|
||
|
||
const loadFiles = async () => {
|
||
try {
|
||
loadingFile.value = true
|
||
const res = await fetch(`${config.public.apiBase}/api/floorplan/`)
|
||
if (res.status !== 200) {
|
||
throw new Error()
|
||
}
|
||
const data = await res.json()
|
||
files.value = data
|
||
} catch (error) {
|
||
console.log(error)
|
||
}
|
||
loadingFile.value = false
|
||
}
|
||
|
||
const openBlocks = ref<string[]>([])
|
||
const isBlockOpen = (name: string) => {
|
||
return openBlocks.value.includes(name)
|
||
}
|
||
const toggleBlock = (name: string) => {
|
||
if (isBlockOpen(name)) {
|
||
openBlocks.value.splice(openBlocks.value.indexOf(name), 1)
|
||
} else {
|
||
openBlocks.value.push(name)
|
||
}
|
||
}
|
||
|
||
const fileBtnClick = async () => {
|
||
if (isBlockOpen('upload_file')) {
|
||
await loadFiles()
|
||
}
|
||
toggleBlock('upload_file')
|
||
}
|
||
|
||
const updateValues = async () => {
|
||
const res = await fetch(`${config.public.apiBase}/api/floorplan/${selectFile.value}`, {
|
||
method: 'PATCH',
|
||
headers: {
|
||
'Accept': 'application/json',
|
||
"Content-Type": "application/json",
|
||
},
|
||
body: JSON.stringify({
|
||
title: title.value,
|
||
d_size: chunk_size.value,
|
||
d_border: threshold.value,
|
||
})
|
||
})
|
||
const data = await res.json()
|
||
}
|
||
|
||
const target_array = ref<{ type: string, title: string, points?: { x: number, y: number } }[]>([])
|
||
const target_type = ref('')
|
||
const addTargetType = (name: string) => {
|
||
target_type.value = name
|
||
}
|
||
|
||
const savePoints = async () => {
|
||
const res = await fetch(`${config.public.apiBase}/api/floorplan/${selectFile.value}/points`, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Accept': 'application/json',
|
||
"Content-Type": "application/json",
|
||
},
|
||
body: JSON.stringify({
|
||
points: target_array.value
|
||
})
|
||
})
|
||
const data = await res.json()
|
||
}
|
||
onMounted(async () => {
|
||
await loadFiles()
|
||
})
|
||
watch(grid_redraw, () => {
|
||
setTimeout(() => {
|
||
loading.value = grid_redraw.value
|
||
}, 100)
|
||
})
|
||
watch(active_point, () => {
|
||
if (!active_point.value) return
|
||
|
||
const t_index = target_array.value.findIndex(el => el.type == target_type.value)
|
||
if (t_index !== -1) target_array.value.splice(t_index, 1)
|
||
|
||
const t = ({ type: target_type.value, title: target_type.value, points: active_point.value })
|
||
|
||
const t_state_index = target_points.value.findIndex(el => el.type == target_type.value)
|
||
if (t_state_index !== -1) target_points.value.splice(t_state_index, 1)
|
||
|
||
const t_state = ({ type: target_type.value, x: active_point.value.x, y: active_point.value.y })
|
||
|
||
target_array.value.push(t)
|
||
target_points.value.push(t_state)
|
||
|
||
target_type.value = ''
|
||
})
|
||
</script>
|
||
<template>
|
||
<div class="flex flex-col gap-4">
|
||
<Panel header="Выбор пресета">
|
||
<div class="flex align-center gap-2">
|
||
<Dropdown v-model="selectFile" placeholder="Выберите файл" :options="files" optionLabel="title"
|
||
optionValue="id" :loading="loadingFile" :disabled="!files.length || loadingFile"
|
||
@change="selectFileEvent" />
|
||
<Button @click="fileBtnClick()" :disabled="loadingFile">
|
||
<span style="display: contents;" v-if="isBlockOpen('upload_file')">
|
||
Выбрать план из загруженных
|
||
</span>
|
||
<span style="display: contents;" v-else>
|
||
Загрузить новый план
|
||
</span>
|
||
</Button>
|
||
</div>
|
||
</Panel>
|
||
<Panel header="Загрузка файла" v-if="isBlockOpen('upload_file')">
|
||
<FileUpload mode="basic" name="demo" :url="`${config.public.apiBase}/api/floorplan/`" accept="image/*"
|
||
:maxFileSize="10000000" @upload="onUpload" :auto="true" chooseLabel="Выберите файл"
|
||
:disabled="loading" />
|
||
</Panel>
|
||
|
||
<Panel header="Данные для координатной сетки">
|
||
<p>Рекомендуем использовать сетку с наиболее крупными ячейками</p>
|
||
<Divider />
|
||
<div class="flex align-middle items-end gap-2">
|
||
<div class="flex flex-col">
|
||
<label for="title">Название</label>
|
||
<InputText id="title" type="text" v-model="title" />
|
||
</div>
|
||
<div class="flex flex-col">
|
||
<label for="chunk">Значение дискретизации</label>
|
||
<InputNumber id="chunk_size" showButtons :min="0" :max="50" v-model="chunk_size"
|
||
:disabled="loading" />
|
||
</div>
|
||
<div class="flex flex-col">
|
||
<label for="threshold">Пороговое значение</label>
|
||
<InputNumber id="threshold" showButtons :min="0" :max="chunk_size * chunk_size" :disabled="loading"
|
||
v-model="threshold" />
|
||
</div>
|
||
<Button @click="updateValues">Сохранить данные</Button>
|
||
</div>
|
||
</Panel>
|
||
|
||
<Panel header="Данные о помещениях">
|
||
<div class="flex gap-2">
|
||
<div class="flex flex-col gap-2">
|
||
<Button @click="addTargetType('start')" :icon="target_type == 'start' ? 'pi pi-bullseye' : ''"
|
||
:label="`${!target_array.filter(el => el.type == 'start').length ?
|
||
`Добавить` : 'Изменить'} точку старта`"></Button>
|
||
<Button
|
||
@click="addTargetType('cabinet' + (target_array.filter(el => el.type.indexOf('cabinet') !== -1).length + 1))"
|
||
:icon="target_type.indexOf('cabinet') !== -1 ? 'pi pi-bullseye' : ''"
|
||
label="Добавить точку входа в кабинет"></Button>
|
||
</div>
|
||
<div class="flex flex-col">
|
||
<span v-if="active_point">
|
||
active point {{ active_point }}
|
||
</span>
|
||
<ul>
|
||
<li v-for="item in target_array">
|
||
{{ item.title }}
|
||
{{ item.type }}
|
||
{{ item.points }}
|
||
</li>
|
||
</ul>
|
||
<Button v-if="target_array.length > 0" @click="savePoints">
|
||
Сохранить данные
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
</Panel>
|
||
|
||
<Panel :header="loading ? `Идет отрисовка` : `Обработанный план здания`">
|
||
<div style=" max-width: 100%; overflow: auto; position: relative;">
|
||
<FloorplanCanvas :cw="cw" :ch="ch" />
|
||
<FloorplanSvg :cw="cw" :ch="ch" />
|
||
</div>
|
||
</Panel>
|
||
</div>
|
||
</template>
|