Files
filam3d/frontend/src/components/MaterialPicker.vue
2026-03-22 14:26:45 +03:00

130 lines
5.0 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="card">
<div class="mb-4 flex items-center justify-between">
<h2 class="text-lg font-semibold text-gray-900">2. Выберите материал</h2>
<button @click="$emit('openAdvisor')" class="btn-secondary !py-1.5 !px-3 !text-xs">
<svg class="mr-1.5 h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M9.813 15.904L9 18.75l-.813-2.846a4.5 4.5 0 00-3.09-3.09L2.25 12l2.846-.813a4.5 4.5 0 003.09-3.09L9 5.25l.813 2.846a4.5 4.5 0 003.09 3.09L15.75 12l-2.846.813a4.5 4.5 0 00-3.09 3.09z" />
</svg>
Помочь выбрать
</button>
</div>
<div v-for="(label, cat) in categories" :key="cat" class="mb-5 last:mb-0">
<h3 class="mb-2.5 text-xs font-semibold uppercase tracking-wider text-gray-500">{{ label }}</h3>
<div class="grid grid-cols-1 gap-2.5 sm:grid-cols-2 lg:grid-cols-3">
<button
v-for="mat in materialsByCategory(cat)"
:key="mat.id"
@click="selectMaterial(mat)"
:class="[
'flex flex-col rounded-lg border-2 p-3.5 text-left transition-all',
store.materialId === mat.id
? 'border-primary-500 bg-primary-50 ring-1 ring-primary-500'
: 'border-gray-200 hover:border-gray-300 hover:bg-gray-50',
]"
>
<div class="flex items-center justify-between">
<span class="text-sm font-semibold text-gray-900">{{ mat.name }}</span>
<span class="text-xs font-medium text-gray-500">{{ mat.price_per_gram }} &#8381;/г</span>
</div>
<p class="mt-1 text-xs leading-relaxed text-gray-500">{{ mat.description }}</p>
<!-- Color palette -->
<div v-if="mat.color_options && mat.color_options.length" class="mt-2 flex flex-wrap gap-1">
<span
v-for="c in mat.color_options"
:key="c.hex || c"
class="h-4 w-4 rounded-full border border-gray-300"
:style="{ backgroundColor: c.hex || c }"
:title="c.name || c"
></span>
</div>
<div class="mt-2 flex flex-wrap gap-1.5">
<span v-if="mat.properties.food_safe" class="inline-flex items-center rounded-full bg-green-100 px-2 py-0.5 text-[10px] font-medium text-green-700">
Food safe
</span>
<span class="inline-flex items-center rounded-full bg-gray-100 px-2 py-0.5 text-[10px] font-medium text-gray-600">
{{ mat.properties.max_temp_c }}&deg;C
</span>
<span class="inline-flex items-center rounded-full bg-gray-100 px-2 py-0.5 text-[10px] font-medium text-gray-600">
{{ strengthLabel(mat.properties.strength) }}
</span>
</div>
</button>
</div>
</div>
<!-- Color selection (shown when material is selected) -->
<div v-if="selectedMaterial && selectedMaterial.color_options && selectedMaterial.color_options.length" class="mt-5 rounded-lg border border-gray-200 bg-gray-50 p-4">
<h3 class="mb-3 text-sm font-semibold text-gray-700">Выберите цвет</h3>
<div class="flex flex-wrap gap-2">
<button
v-for="c in selectedMaterial.color_options"
:key="c.hex || c"
@click="selectColor(c)"
:class="[
'flex items-center gap-2 rounded-lg border-2 px-3 py-1.5 text-xs font-medium transition-all',
store.color === (c.name || c)
? 'border-primary-500 bg-white ring-1 ring-primary-500'
: 'border-gray-200 bg-white hover:border-gray-300',
]"
>
<span
class="h-5 w-5 rounded-full border border-gray-300 flex-shrink-0"
:style="{ backgroundColor: c.hex || c }"
></span>
<span class="text-gray-700">{{ c.name || c }}</span>
</button>
</div>
</div>
</div>
</template>
<script setup>
import { computed, onMounted } from 'vue'
import { useCalculatorStore } from '../stores/calculator'
import { useMaterialsStore } from '../stores/materials'
defineEmits(['openAdvisor'])
const store = useCalculatorStore()
const materialsStore = useMaterialsStore()
const { categories } = materialsStore
onMounted(() => materialsStore.fetchMaterials())
function materialsByCategory(cat) {
return materialsStore.materials.filter((m) => m.category === cat)
}
const selectedMaterial = computed(() => {
if (!store.materialId) return null
return materialsStore.materials.find((m) => m.id === store.materialId)
})
function selectMaterial(mat) {
store.materialId = mat.id
store.color = null
store.result = null
}
function selectColor(c) {
store.color = c.name || c
store.result = null
}
const strengthLabels = {
low: 'Низкая',
medium: 'Средняя',
high: 'Высокая',
very_high: 'Очень высокая',
extreme: 'Экстремальная',
}
function strengthLabel(val) {
return strengthLabels[val] || val
}
</script>