Вынес Запчасти в боковое меню

This commit is contained in:
Ilia Miheev
2026-06-02 22:59:06 +03:00
parent 9db8220fdb
commit 5272c1ddf7
7 changed files with 241 additions and 11 deletions
+6 -1
View File
@@ -1,8 +1,12 @@
<script setup>
import { ref } from "vue";
import { ref, provide } from "vue";
import DroneParts from "@/components/DroneParts.vue";
import MyDropMenu from "@/components/MyDropMenu.vue";
const showDroneParts = ref(false);
const dropMenu = ref(null);
provide('dropMenu', dropMenu);
</script>
<template>
@@ -14,6 +18,7 @@ const showDroneParts = ref(false);
<img src="/images/drone.png" alt="menu" class="btn-icon" />
</button>
<MyDropMenu v-if="!showDroneParts" ref="dropMenu" />
<DroneParts v-if="showDroneParts" @back="showDroneParts = false" />
<router-view v-if="!showDroneParts" />
</template>
+48 -5
View File
@@ -22,6 +22,25 @@
text-color="#ffffff"
active-text-color="#23aae3"
>
<el-sub-menu index="parts">
<template #title>
<span class="menu-content__group-title">Запчасти</span>
</template>
<el-menu-item
v-for="part in parts"
:key="part.id"
:index="`part-${part.id}`"
class="menu-list-item"
@click="openPart(part)"
>
<ListCard
:title="part.name"
:subtitle="part.category"
:image="part.image"
:active="isPartActive(part.id)"
/>
</el-menu-item>
</el-sub-menu>
<el-sub-menu index="terms">
<template #title>
<span class="menu-content__group-title">Термины</span>
@@ -30,7 +49,7 @@
v-for="(item, index) in informationForDirectory"
:key="item.title"
:index="`term-${index}`"
class="terms-menu-item"
class="menu-list-item"
@click="openInfoCard(item)"
>
<ListCard
@@ -63,12 +82,18 @@
</template>
<script setup>
import { ref } from 'vue';
import { ref, onMounted } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import MyDialog from "@/components/MyDialog.vue";
import CustomButton from '@/components/UI/CustomButton.vue'
import ListCard from '@/components/UI/ListCard.vue'
import informationForDirectory from "../data/informationForDirectory";
import { useEventListener } from "@vueuse/core";
import { usePartsCatalog } from '@/composables/usePartsCatalog';
const route = useRoute()
const router = useRouter()
const { parts, loadParts } = usePartsCatalog()
const isOpen = ref(false);
const isModalVisible = ref(false);
@@ -79,6 +104,17 @@ function getSubtitle(body) {
return body.length > 72 ? `${body.slice(0, 72)}` : body
}
function isPartActive(partId) {
return route.name === 'topic'
&& route.params.source === 'parts'
&& Number(route.params.id) === partId
}
function openPart(part) {
isOpen.value = false
router.push({ name: 'topic', params: { source: 'parts', id: part.id } })
}
function handleOkBtn() {
isModalVisible.value = false
isOpen.value = true
@@ -106,6 +142,10 @@ const handleKeydown = (event) => {
};
useEventListener("keydown", handleKeydown);
onMounted(() => {
loadParts()
})
defineExpose({ open: () => (isOpen.value = true), close: closeMenu });
</script>
@@ -147,7 +187,8 @@ h1 {
background-color: rgba(255, 255, 255, 0.1);
}
.terms-menu-item {
.terms-menu-item,
.menu-list-item {
height: auto;
line-height: normal;
padding: 0;
@@ -155,12 +196,14 @@ h1 {
white-space: normal;
}
.terms-menu-item :deep(.list-card) {
.terms-menu-item :deep(.list-card),
.menu-list-item :deep(.list-card) {
margin: 0;
width: 100%;
}
.terms-menu-item.is-active {
.terms-menu-item.is-active,
.menu-list-item.is-active {
background-color: transparent !important;
}
+36
View File
@@ -0,0 +1,36 @@
import { ref } from 'vue'
const parts = ref([])
let loadPromise = null
export function usePartsCatalog() {
async function loadParts() {
if (parts.value.length) {
return parts.value
}
if (!loadPromise) {
loadPromise = fetch('/droneComponents/components.json')
.then((response) => (response.ok ? response.json() : []))
.then((data) => {
parts.value = data
return data
})
.catch(() => {
parts.value = []
return []
})
.finally(() => {
loadPromise = null
})
}
return loadPromise
}
function getPartById(id) {
return parts.value.find((part) => part.id === id) ?? null
}
return { parts, loadParts, getPartById }
}
+22
View File
@@ -0,0 +1,22 @@
import { usePartsCatalog } from '@/composables/usePartsCatalog'
export async function resolveTopic(source, id) {
if (source === 'parts') {
const { loadParts, getPartById } = usePartsCatalog()
await loadParts()
const part = getPartById(Number(id))
if (!part) {
return null
}
return {
title: part.name,
subtitle: part.category,
image: part.image,
description: part.description,
}
}
return null
}
+5
View File
@@ -14,6 +14,11 @@ const router = createRouter({
name: 'preview',
component: () => import('@/views/PreviewView.vue'),
},
{
path: '/topic/:source/:id',
name: 'topic',
component: () => import('@/views/TopicView.vue'),
},
],
})
+3 -5
View File
@@ -1,10 +1,9 @@
<script setup>
import { ref, onMounted, h } from "vue";
import { ref, onMounted, h, inject } from "vue";
import { useRouter } from "vue-router";
import izitoast from "@/shared/izitoast";
import { useEventListener } from "@vueuse/core";
import MyDialog from "@/components/MyDialog.vue";
import MyDropMenu from "@/components/MyDropMenu.vue";
import copyToclipboard from "@/shared/copyToclipboard";
import ContextMenu from "@imengyu/vue3-context-menu";
import emergencyAndNavigate from '@/shablons/emergency_and_navigate.js'
@@ -24,7 +23,7 @@ const numbers = ref([]);
const clickCount = ref(1);
const listOfTrips = ref([]);
const isModalVisible = ref(false);
const dropMenu = ref(null)
const dropMenu = inject('dropMenu')
onMounted(() => {
for (let i = 0; i < 100; i++) {
@@ -172,7 +171,7 @@ const handleKeydown = (event) => {
saveResult();
} else if (event.ctrlKey && event.code === "KeyN") {
event.preventDefault();
dropMenu.value.open();
dropMenu.value?.open();
} else if (event.code === "Delete") {
event.preventDefault();
cleanResult();
@@ -371,7 +370,6 @@ function onContextMenu(e, item) {
Превью
</n-tooltip>
</div>
<MyDropMenu ref="dropMenu" />
<MyDialog v-model:show="isModalVisible" class="dialogWindow" :is-success=Boolean(listOfTrips.length)>
<template #header>
<strong v-if="listOfTrips.length">Код готов!</strong>
+121
View File
@@ -0,0 +1,121 @@
<script setup>
import { ref, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { resolveTopic } from '@/composables/useTopicResolver'
const route = useRoute()
const router = useRouter()
const topic = ref(null)
async function loadTopic() {
topic.value = await resolveTopic(route.params.source, route.params.id)
if (!topic.value) {
router.replace({ name: 'planner' })
}
}
watch(
() => [route.params.source, route.params.id],
loadTopic,
{ immediate: true }
)
function goBack() {
router.push({ name: 'planner' })
}
</script>
<template>
<div v-if="topic" class="topic-view scroll-strip">
<article class="topic-view__card">
<button class="topic-view__back" @click="goBack">
Назад
</button>
<div class="topic-view__image">
<img :src="topic.image" :alt="topic.title" />
</div>
<div class="topic-view__header">
<p v-if="topic.subtitle" class="topic-view__subtitle">{{ topic.subtitle }}</p>
<h1 class="topic-view__title">{{ topic.title }}</h1>
</div>
<p class="topic-view__description">{{ topic.description }}</p>
</article>
</div>
</template>
<style scoped>
.topic-view {
min-height: 100vh;
padding: 40px 20px;
box-sizing: border-box;
}
.topic-view__back {
margin-bottom: 24px;
padding: 10px 18px;
background: linear-gradient(135deg, #ff6b6b, #c92a2a);
border: none;
border-radius: 10px;
color: white;
font-weight: bold;
cursor: pointer;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
transition: transform 0.2s;
}
.topic-view__back:hover {
transform: scale(1.02);
}
.topic-view__card {
max-width: 800px;
margin: 0 auto;
background: rgba(0, 0, 0, 0.5);
border-radius: 24px;
padding: 30px;
backdrop-filter: blur(5px);
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.topic-view__image {
text-align: center;
margin-bottom: 30px;
}
.topic-view__image img {
max-width: 100%;
max-height: 350px;
border-radius: 20px;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);
border: 1px solid rgba(255, 255, 255, 0.2);
}
.topic-view__header {
text-align: center;
margin-bottom: 24px;
}
.topic-view__subtitle {
margin: 0 0 8px;
font-size: 14px;
color: rgba(255, 255, 255, 0.65);
}
.topic-view__title {
margin: 0;
font-size: 28px;
color: #fff;
}
.topic-view__description {
margin: 0;
font-size: 16px;
line-height: 1.6;
color: rgba(255, 255, 255, 0.85);
white-space: pre-wrap;
}
</style>