Убрал прошлый функционал Запчастей

This commit is contained in:
Ilia Miheev
2026-06-02 23:32:44 +03:00
parent 5272c1ddf7
commit 25a0014d21
2 changed files with 2 additions and 494 deletions
+2 -42
View File
@@ -1,26 +1,15 @@
<script setup> <script setup>
import { ref, provide } from "vue"; import { ref, provide } from "vue";
import DroneParts from "@/components/DroneParts.vue";
import MyDropMenu from "@/components/MyDropMenu.vue"; import MyDropMenu from "@/components/MyDropMenu.vue";
const showDroneParts = ref(false);
const dropMenu = ref(null); const dropMenu = ref(null);
provide('dropMenu', dropMenu); provide('dropMenu', dropMenu);
</script> </script>
<template> <template>
<button <MyDropMenu ref="dropMenu" />
v-if="!showDroneParts" <router-view />
@click="showDroneParts = true"
class="show-parts-btn"
>
<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> </template>
<style> <style>
@@ -36,33 +25,4 @@ body {
font-size: 18px; font-size: 18px;
margin: 0; margin: 0;
} }
.show-parts-btn {
position: fixed;
top: 20px;
right: 20px;
z-index: 9999;
width: 50px;
height: 50px;
padding: 0;
background: rgba(12, 12, 12, 0.034);
border: none;
cursor: pointer;
box-shadow: 0 4px 15px rgba(0,0,0,0.3);
transition: transform 0.2s;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.btn-icon {
width: 90%;
height: 90%;
object-fit: cover;
}
.show-parts-btn:hover {
transform: scale(1.05);
}
</style> </style>
-452
View File
@@ -1,452 +0,0 @@
<template>
<div class="drone-parts-container">
<!-- Кнопка возврата в левом верхнем углу -->
<button class="back-btn" @click="$emit('back')">
Вернуться к маршруту
</button>
<!-- Левая панель - список компонентов -->
<div class="parts-list scroll-strip">
<ListCard
v-for="part in parts"
:key="part.id"
:title="part.name"
:subtitle="part.category"
:image="part.image"
:active="selectedPart?.id === part.id"
@click="selectPart(part)"
/>
</div>
<!-- Правая часть - детали -->
<div class="main-area">
<div v-if="selectedPart" class="part-detail">
<div class="detail-image">
<img :src="selectedPart.image" :alt="selectedPart.name" />
</div>
<div class="detail-info">
<h2>{{ selectedPart.name }}</h2>
<p class="description">{{ selectedPart.description }}</p>
</div>
<div class="comments-section">
<div class="comments-header">
<h3>📝 Мой комментарий</h3>
</div>
<div v-if="myComment" class="existing-comment">
<div class="comment-text">{{ myComment.text }}</div>
<div class="comment-meta">
<span class="comment-date">{{ myComment.date }}</span>
<button class="edit-comment" @click="editComment"> Редактировать</button>
<button class="delete-comment" @click="deleteComment">🗑 Удалить</button>
</div>
</div>
<div v-else class="add-comment">
<textarea
v-model="commentText"
:placeholder="`Мой комментарий к «${selectedPart.name}»...`"
rows="3"
></textarea>
<div class="comment-actions">
<button class="save-btn" @click="saveComment" :disabled="!commentText.trim()">
💾 Сохранить комментарий
</button>
</div>
</div>
</div>
</div>
<div v-else class="empty-state">
<div class="empty-icon">🛸</div>
<h3>Выберите компонент дрона</h3>
<p>Нажмите на любой компонент из списка слева</p>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import ListCard from '@/components/UI/ListCard.vue'
const emit = defineEmits(['back'])
let db = null
const openDB = () => {
return new Promise((resolve, reject) => {
const request = indexedDB.open('DroneCommentsDB', 1)
request.onerror = () => reject(request.error)
request.onsuccess = () => resolve(request.result)
request.onupgradeneeded = (event) => {
const database = event.target.result
if (!database.objectStoreNames.contains('comments')) {
const store = database.createObjectStore('comments', { keyPath: 'id', autoIncrement: true })
store.createIndex('part_id', 'part_id', { unique: false })
}
}
})
}
const saveCommentToDB = async (partId, text) => {
return new Promise((resolve, reject) => {
const transaction = db.transaction(['comments'], 'readwrite')
const store = transaction.objectStore('comments')
const comment = {
part_id: partId,
user_id: 'me',
text: text,
date: new Date().toLocaleString('ru-RU'),
timestamp: Date.now()
}
const request = store.add(comment)
request.onsuccess = () => resolve(request.result)
request.onerror = () => reject(request.error)
})
}
const loadCommentFromDB = async (partId) => {
return new Promise((resolve, reject) => {
const transaction = db.transaction(['comments'], 'readonly')
const store = transaction.objectStore('comments')
const index = store.index('part_id')
const request = index.getAll(partId)
request.onsuccess = () => {
const comments = request.result
const myComment = comments.length > 0 ? comments[comments.length - 1] : null
resolve(myComment)
}
request.onerror = () => reject(request.error)
})
}
const updateCommentInDB = async (id, newText) => {
return new Promise((resolve, reject) => {
const transaction = db.transaction(['comments'], 'readwrite')
const store = transaction.objectStore('comments')
const request = store.get(id)
request.onsuccess = () => {
const comment = request.result
if (comment) {
comment.text = newText
comment.date = new Date().toLocaleString('ru-RU')
comment.timestamp = Date.now()
const updateRequest = store.put(comment)
updateRequest.onsuccess = () => resolve()
updateRequest.onerror = () => reject(updateRequest.error)
} else {
reject('Comment not found')
}
}
request.onerror = () => reject(request.error)
})
}
const deleteCommentFromDB = async (id) => {
return new Promise((resolve, reject) => {
const transaction = db.transaction(['comments'], 'readwrite')
const store = transaction.objectStore('comments')
const request = store.delete(id)
request.onsuccess = () => resolve()
request.onerror = () => reject(request.error)
})
}
const parts = ref([])
const selectedPart = ref(null)
const myComment = ref(null)
const commentText = ref('')
const dbReady = ref(false)
const loadParts = async () => {
try {
const response = await fetch('/droneComponents/components.json')
if (response.ok) {
parts.value = await response.json()
} else {
console.error('Файл components.json не найден')
parts.value = []
}
} catch (error) {
console.error('Ошибка загрузки компонентов:', error)
parts.value = []
}
}
const selectPart = async (part) => {
selectedPart.value = part
if (dbReady.value) {
const comment = await loadCommentFromDB(part.id)
myComment.value = comment
commentText.value = ''
}
}
// ========== КОММЕНТАРИИ ==========
const saveComment = async () => {
if (!commentText.value.trim() || !selectedPart.value) return
try {
await saveCommentToDB(selectedPart.value.id, commentText.value.trim())
const comment = await loadCommentFromDB(selectedPart.value.id)
myComment.value = comment
commentText.value = ''
} catch (error) {
console.error('Ошибка сохранения:', error)
}
}
const editComment = () => {
if (myComment.value) {
commentText.value = myComment.value.text
myComment.value = null
}
}
const deleteComment = async () => {
if (!confirm('Удалить комментарий?')) return
try {
await deleteCommentFromDB(myComment.value.id)
myComment.value = null
commentText.value = ''
} catch (error) {
console.error('Ошибка удаления:', error)
}
}
onMounted(async () => {
try {
db = await openDB()
dbReady.value = true
await loadParts()
} catch (error) {
console.error('Ошибка открытия IndexedDB:', error)
}
})
</script>
<style scoped>
.drone-parts-container {
display: flex;
height: 100vh;
width: 100%;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
position: relative;
}
/* Кнопка возврата */
.back-btn {
position: fixed;
top: 20px;
right: 20px;
z-index: 9999;
padding: 12px 20px;
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;
}
.back-btn:hover {
transform: scale(1.02);
}
/* Левая панель с отступом сверху, чтобы не перекрывалась кнопкой */
.parts-list {
width: 300px;
height: 100%;
background: rgba(0, 0, 0, 0.4);
backdrop-filter: blur(10px);
border-right: 1px solid rgba(255, 255, 255, 0.1);
margin-top: 0;
}
/* Правая область */
.main-area {
flex: 1;
overflow-y: auto;
padding: 20px;
}
.part-detail {
max-width: 800px;
margin: 0 auto;
background: rgba(0, 0, 0, 0.5);
border-radius: 24px;
padding: 30px;
backdrop-filter: blur(5px);
}
.detail-image {
text-align: center;
margin-bottom: 30px;
}
.detail-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);
}
.detail-info h2 {
font-size: 28px;
color: #fff;
margin-bottom: 15px;
text-align: center;
}
.description {
font-size: 16px;
line-height: 1.6;
color: rgba(255, 255, 255, 0.85);
text-align: center;
margin-bottom: 30px;
white-space: pre-wrap;
}
.comments-section {
border-top: 1px solid rgba(255, 255, 255, 0.2);
padding-top: 25px;
}
.comments-header h3 {
font-size: 18px;
color: #fff;
margin: 0 0 20px 0;
}
.existing-comment {
background: rgba(35, 170, 227, 0.15);
border-radius: 12px;
padding: 15px;
border-left: 3px solid #23aae3;
}
.comment-text {
color: #fff;
font-size: 15px;
line-height: 1.5;
margin-bottom: 10px;
white-space: pre-wrap;
}
.comment-meta {
display: flex;
gap: 15px;
align-items: center;
font-size: 11px;
color: rgba(255, 255, 255, 0.5);
}
.comment-meta button {
background: none;
border: none;
cursor: pointer;
font-size: 12px;
padding: 4px 8px;
border-radius: 6px;
transition: 0.2s;
}
.edit-comment {
color: #23aae3;
}
.edit-comment:hover {
background: rgba(35, 170, 227, 0.2);
}
.delete-comment {
color: #ff6b6b;
}
.delete-comment:hover {
background: rgba(255, 107, 107, 0.2);
}
.add-comment textarea {
width: 100%;
padding: 12px;
background: rgba(255, 255, 255, 0.08);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 12px;
color: #fff;
font-size: 14px;
font-family: inherit;
resize: vertical;
}
.add-comment textarea:focus {
outline: none;
border-color: #23aae3;
}
.add-comment textarea::placeholder {
color: rgba(255, 255, 255, 0.4);
}
.comment-actions {
margin-top: 10px;
}
.save-btn {
background: linear-gradient(135deg, #23aae3, #1a6d8f);
border: none;
padding: 10px 24px;
border-radius: 24px;
color: white;
font-size: 14px;
cursor: pointer;
transition: 0.2s;
}
.save-btn:hover:not(:disabled) {
transform: scale(1.02);
box-shadow: 0 2px 10px rgba(35, 170, 227, 0.4);
}
.save-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
text-align: center;
}
.empty-icon {
font-size: 80px;
margin-bottom: 20px;
opacity: 0.5;
}
.empty-state h3 {
font-size: 24px;
color: #fff;
margin-bottom: 10px;
}
.empty-state p {
color: rgba(255, 255, 255, 0.6);
}
</style>