130 lines
5.0 KiB
Vue
130 lines
5.0 KiB
Vue
<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 }} ₽/г</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 }}°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>
|