dev #90
1
app.vue
1
app.vue
|
@ -6,6 +6,5 @@ import '@/assets/main.scss'
|
||||||
<Header />
|
<Header />
|
||||||
<NuxtPage />
|
<NuxtPage />
|
||||||
<Footer />
|
<Footer />
|
||||||
<Modal />
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -27,21 +27,18 @@ const cameraStat = reactive({
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<Loader />
|
<Loader />
|
||||||
<TresCanvas clear-color="#e2e8f0">
|
<Scene :canvasProps="{ clearColor: '#e2e8f0' }">
|
||||||
<TresPerspectiveCamera v-bind="cameraStat" ref="camera" />
|
<TresPerspectiveCamera v-bind="cameraStat" ref="camera" />
|
||||||
<OrbitControls v-bind="controlsState" make-default />
|
<OrbitControls v-bind="controlsState" make-default />
|
||||||
<Suspense>
|
<Suspense>
|
||||||
<ModelSmoothCamera />
|
<ModelSmoothCamera />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
<Suspense>
|
|
||||||
<ModelEnv />
|
|
||||||
</Suspense>
|
|
||||||
<TresGroup>
|
<TresGroup>
|
||||||
<Suspense>
|
<Suspense>
|
||||||
<ModelParametric />
|
<ModelParametric />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</TresGroup>
|
</TresGroup>
|
||||||
</TresCanvas>
|
</Scene>
|
||||||
</ClientOnly>
|
</ClientOnly>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
|
@ -19,6 +19,7 @@ const section_count = use_section_count()
|
||||||
const extra_section = use_extra_section()
|
const extra_section = use_extra_section()
|
||||||
const total_length = use_total_length()
|
const total_length = use_total_length()
|
||||||
const min_length = use_min_length()
|
const min_length = use_min_length()
|
||||||
|
const globalFenceType = useGlobalFenceType()
|
||||||
|
|
||||||
if (!pillar_color.value) {
|
if (!pillar_color.value) {
|
||||||
const r = Math.floor(Math.random() * predefPillarColors.length)
|
const r = Math.floor(Math.random() * predefPillarColors.length)
|
||||||
|
@ -205,7 +206,8 @@ const calc_table = computed(() => {
|
||||||
:goto_cam="new Vector3(1, 2, -1)" />
|
:goto_cam="new Vector3(1, 2, -1)" />
|
||||||
<label for="pillar_topper">Колпак столба</label>
|
<label for="pillar_topper">Колпак столба</label>
|
||||||
<DropdownPicker type="topper" :cb="setPillarTopper" name="pillar_topper"
|
<DropdownPicker type="topper" :cb="setPillarTopper" name="pillar_topper"
|
||||||
:pattern="getTopper(pillar_topper)" :disabled="remove_pillar"
|
:pattern="getTopper(pillar_topper, globalFenceType?.type == 'aristo' ? 'aristoToppers' : 'baseToppers')"
|
||||||
|
:disabled="remove_pillar"
|
||||||
:goto_target="new Vector3(fence_section * -0.5, lamelles_count * lamelle_height, 0)"
|
:goto_target="new Vector3(fence_section * -0.5, lamelles_count * lamelle_height, 0)"
|
||||||
:goto_cam="new Vector3(-1, 2, 1)" />
|
:goto_cam="new Vector3(-1, 2, 1)" />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,24 +1,39 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { getFilename as getPattern, patterns } from '../pattern';
|
import { getFilename as getPattern, patterns } from '../pattern';
|
||||||
import { getFilename as getTopper, toppers } from '../topper';
|
import { getFilename as getTopper, baseToppers, aristoToppers } from '../topper';
|
||||||
|
|
||||||
|
const globalFenceType = useGlobalFenceType()
|
||||||
const props = defineProps(['cb', 'patterns']);
|
const props = defineProps(['cb', 'patterns']);
|
||||||
type pType = {
|
type pType = {
|
||||||
list: typeof patterns | typeof toppers,
|
list: typeof patterns | typeof baseToppers,
|
||||||
func: any
|
func: any
|
||||||
|
type: string | null
|
||||||
}
|
}
|
||||||
const p = {
|
const p = reactive({
|
||||||
list: [],
|
list: [],
|
||||||
|
type: null,
|
||||||
func: () => { }
|
func: () => { }
|
||||||
} as pType
|
} as pType)
|
||||||
|
|
||||||
if (props.patterns == 'pattern') {
|
if (props.patterns == 'pattern') {
|
||||||
p.list = patterns;
|
p.list = patterns;
|
||||||
p.func = getPattern
|
p.func = getPattern
|
||||||
} else if (props.patterns == 'topper') {
|
} else if (props.patterns == 'topper') {
|
||||||
p.list = toppers
|
p.list = baseToppers
|
||||||
p.func = getTopper
|
p.func = getTopper
|
||||||
}
|
}
|
||||||
|
watch(globalFenceType, () => {
|
||||||
|
if (props.patterns == 'topper') {
|
||||||
|
if (globalFenceType.value?.type == 'standart') {
|
||||||
|
p.list = baseToppers
|
||||||
|
p.type = 'toppers'
|
||||||
|
}
|
||||||
|
if (globalFenceType.value?.type == 'aristo') {
|
||||||
|
p.list = aristoToppers
|
||||||
|
p.type = 'aristoToppers'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<template v-for="item in p.list">
|
<template v-for="item in p.list">
|
||||||
|
|
|
@ -17,22 +17,6 @@ const explosion_state = use_explosion_state()
|
||||||
const toggleExpState = () => {
|
const toggleExpState = () => {
|
||||||
explosion_state.value = !explosion_state.value
|
explosion_state.value = !explosion_state.value
|
||||||
}
|
}
|
||||||
const back_light = ref()
|
|
||||||
const secondary_light = ref()
|
|
||||||
const loadAll = async () => {
|
|
||||||
const { scene: back } = await useGLTF('/models_light/back_exp.glb')
|
|
||||||
const { scene: secondary } = await useGLTF('/models_light/secondary_exp.glb')
|
|
||||||
|
|
||||||
const k = 0.03
|
|
||||||
|
|
||||||
back_light.value = back.children[0]
|
|
||||||
back_light.value.intensity = back_light.value.intensity * k
|
|
||||||
back_light.value.shadow.bias = -0.02
|
|
||||||
|
|
||||||
secondary_light.value = secondary.children[0]
|
|
||||||
secondary_light.value.intensity = secondary_light.value.intensity * k
|
|
||||||
secondary_light.value.shadow.bias = -0.01
|
|
||||||
}
|
|
||||||
|
|
||||||
const changeDistance = (v = 1) => {
|
const changeDistance = (v = 1) => {
|
||||||
if (camera.value && controls.value) {
|
if (camera.value && controls.value) {
|
||||||
|
@ -41,9 +25,6 @@ const changeDistance = (v = 1) => {
|
||||||
camera.value.position.normalize().multiplyScalar(r)
|
camera.value.position.normalize().multiplyScalar(r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onMounted(() => {
|
|
||||||
loadAll()
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="h-96 relative">
|
<div class="h-96 relative">
|
||||||
|
@ -53,14 +34,11 @@ onMounted(() => {
|
||||||
Загрузка 3D модели
|
Загрузка 3D модели
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<TresCanvas height="600">
|
<Scene :canvasProps="{ height: '600' }">
|
||||||
<TresPerspectiveCamera :position="[-7, 2, 4]" ref="camera" />
|
<TresPerspectiveCamera :position="[-7, 2, 4]" ref="camera" />
|
||||||
<OrbitControls v-bind="controlsState" ref="controls" make-default />
|
<OrbitControls v-bind="controlsState" ref="controls" make-default />
|
||||||
<ModelEnv />
|
|
||||||
<Suspense>
|
|
||||||
<ModelDiagram />
|
<ModelDiagram />
|
||||||
</Suspense>
|
</Scene>
|
||||||
</TresCanvas>
|
|
||||||
</ClientOnly>
|
</ClientOnly>
|
||||||
<div class="canvas-icons">
|
<div class="canvas-icons">
|
||||||
<a href="#" @click.prevent="toggleExpState">
|
<a href="#" @click.prevent="toggleExpState">
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { getName as getTopperName } from './topper';
|
||||||
|
|
||||||
const config = useRuntimeConfig()
|
const config = useRuntimeConfig()
|
||||||
const apiBase = config.public.apiBase
|
const apiBase = config.public.apiBase
|
||||||
const { data: calculatorData } = await apiFetch<ApiCalcType>(`calculator/5/`)
|
const props = defineProps(['calcData'])
|
||||||
const isModalOpen = useState('modal_open', () => false)
|
const isModalOpen = useState('modal_open', () => false)
|
||||||
|
|
||||||
const lamelle_height = useState<number>('lamelle_height')
|
const lamelle_height = useState<number>('lamelle_height')
|
||||||
|
@ -122,12 +122,14 @@ const total_colors = computed(() => {
|
||||||
`Столбы ${pillar_color.value} ${getColorNameFromRal(pillar_color.value)}`,
|
`Столбы ${pillar_color.value} ${getColorNameFromRal(pillar_color.value)}`,
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
const total_txt = computed(() => {
|
const total_txt = computed(() => {
|
||||||
if (!calculatorData.value) return
|
const calculatorData = props.calcData as ApiCalcType
|
||||||
const pillar = parseFloat(calculatorData.value.pillar)
|
if (!calculatorData) return
|
||||||
const pillar_base = parseFloat(calculatorData.value.pillar_base)
|
const pillar = parseFloat(calculatorData.pillar)
|
||||||
const lamelles_block = parseFloat(calculatorData.value.lamelles_block)
|
const pillar_base = parseFloat(calculatorData.pillar_base)
|
||||||
const { discount } = calculatorData.value
|
const lamelles_block = parseFloat(calculatorData.lamelles_block)
|
||||||
|
const { discount } = calculatorData
|
||||||
|
|
||||||
const sections = section_count.value as number
|
const sections = section_count.value as number
|
||||||
const extra_m = extra_section.value as number * 0.001
|
const extra_m = extra_section.value as number * 0.001
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { useGLTF } from '@tresjs/cientos'
|
||||||
import { getColorHexFromRal } from '../ral';
|
import { getColorHexFromRal } from '../ral';
|
||||||
|
|
||||||
const explosion_state = use_explosion_state()
|
const explosion_state = use_explosion_state()
|
||||||
|
const globalFenceType = useGlobalFenceType()
|
||||||
|
|
||||||
const k = 1.5
|
const k = 1.5
|
||||||
const targetExplosion = {
|
const targetExplosion = {
|
||||||
|
@ -14,24 +15,67 @@ const targetExplosion = {
|
||||||
stolb: [0 * k, 0 * k, 1 * k],
|
stolb: [0 * k, 0 * k, 1 * k],
|
||||||
verh: [0 * k, 0.25 * k, 0 * k],
|
verh: [0 * k, 0.25 * k, 0 * k],
|
||||||
}
|
}
|
||||||
|
const { scene: standart_kosynka } = await useGLTF('/models_exp/kosynka.glb')
|
||||||
|
const { scene: standart_krepleniye_planok } = await useGLTF('/models_exp/krepleniye_planok.glb')
|
||||||
|
const { scene: standart_osnova_stolba } = await useGLTF('/models_exp/osnova_stolba.glb')
|
||||||
|
const { scene: standart_planki } = await useGLTF('/models_exp/planki.glb')
|
||||||
|
const { scene: standart_stolb } = await useGLTF('/models_exp/stolb.glb')
|
||||||
|
const { scene: standart_verh } = await useGLTF('/models_exp/verh.glb')
|
||||||
|
|
||||||
|
const { scene: aristo_kosynka } = await useGLTF('/models_aristo_exp/kosynka.glb')
|
||||||
|
const { scene: aristo_krepleniye_planok } = await useGLTF('/models_aristo_exp/krepleniye_planok.glb')
|
||||||
|
const { scene: aristo_osnova_stolba } = await useGLTF('/models_aristo_exp/osnova_stolba.glb')
|
||||||
|
const { scene: aristo_planki } = await useGLTF('/models_aristo_exp/planki.glb')
|
||||||
|
const { scene: aristo_stolb } = await useGLTF('/models_aristo_exp/stolb.glb')
|
||||||
|
const { scene: aristo_verh } = await useGLTF('/models_aristo_exp/verh.glb')
|
||||||
|
|
||||||
|
const kosynka = ref()
|
||||||
|
const krepleniye_planok = ref()
|
||||||
|
const osnova_stolba = ref()
|
||||||
|
const planki = ref()
|
||||||
|
const stolb = ref()
|
||||||
|
const verh = ref()
|
||||||
|
|
||||||
|
const setModels = () => {
|
||||||
|
const t = globalFenceType.value?.type || 'aristo'
|
||||||
|
if (t == 'aristo') {
|
||||||
|
kosynka.value = aristo_kosynka
|
||||||
|
krepleniye_planok.value = aristo_krepleniye_planok
|
||||||
|
osnova_stolba.value = aristo_osnova_stolba
|
||||||
|
planki.value = aristo_planki
|
||||||
|
stolb.value = aristo_stolb
|
||||||
|
verh.value = aristo_verh
|
||||||
|
}
|
||||||
|
if (t == "standart") {
|
||||||
|
kosynka.value = standart_kosynka
|
||||||
|
krepleniye_planok.value = standart_krepleniye_planok
|
||||||
|
osnova_stolba.value = standart_osnova_stolba
|
||||||
|
planki.value = standart_planki
|
||||||
|
stolb.value = standart_stolb
|
||||||
|
verh.value = standart_verh
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setModels()
|
||||||
|
|
||||||
|
watch(() => globalFenceType.value?.type, (t) => {
|
||||||
|
setModels()
|
||||||
|
set_material(planki.value, getColorHexFromRal(lamelle_color.value));
|
||||||
|
[stolb, verh, krepleniye_planok].map(el => set_material(el.value, getColorHexFromRal(pillar_color.value)))
|
||||||
|
})
|
||||||
|
|
||||||
const { scene: kosynka } = await useGLTF('/models_exp/kosynka.glb')
|
|
||||||
const { scene: krepleniye_planok } = await useGLTF('/models_exp/krepleniye_planok.glb')
|
|
||||||
const { scene: osnova_stolba } = await useGLTF('/models_exp/osnova_stolba.glb')
|
|
||||||
const { scene: planki } = await useGLTF('/models_exp/planki.glb')
|
|
||||||
const { scene: stolb } = await useGLTF('/models_exp/stolb.glb')
|
|
||||||
const { scene: verh } = await useGLTF('/models_exp/verh.glb')
|
|
||||||
|
|
||||||
const lamelle_color = use_lamelle_color()
|
const lamelle_color = use_lamelle_color()
|
||||||
const pillar_color = use_pillar_color()
|
const pillar_color = use_pillar_color()
|
||||||
|
|
||||||
set_material(planki, getColorHexFromRal(lamelle_color.value));
|
set_material(planki.value, getColorHexFromRal(lamelle_color.value));
|
||||||
[stolb, verh, krepleniye_planok].map(el => set_material(el, getColorHexFromRal(pillar_color.value)))
|
[stolb, verh, krepleniye_planok].map(el => set_material(el.value, getColorHexFromRal(pillar_color.value)))
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<Suspense>
|
<Suspense>
|
||||||
<TresGroup :position-y="-3.5" :scale="2">
|
<TresGroup :position-y="!globalFenceType || globalFenceType.type == 'aristo' ? -2 : -3.5" :scale="2"
|
||||||
|
:key="globalFenceType?.type">
|
||||||
<ModelItem :model="kosynka" :target="explosion_state ? targetExplosion.kosynka : [0, 0, 0]" />
|
<ModelItem :model="kosynka" :target="explosion_state ? targetExplosion.kosynka : [0, 0, 0]" />
|
||||||
<ModelItem :model="krepleniye_planok"
|
<ModelItem :model="krepleniye_planok"
|
||||||
:target="explosion_state ? targetExplosion.krepleniye_planok : [0, 0, 0]" />
|
:target="explosion_state ? targetExplosion.krepleniye_planok : [0, 0, 0]" />
|
||||||
|
|
|
@ -32,7 +32,7 @@ onMounted(async () => {
|
||||||
const newEnvMap = exrCubeRenderTarget ? exrCubeRenderTarget.texture : null;
|
const newEnvMap = exrCubeRenderTarget ? exrCubeRenderTarget.texture : null;
|
||||||
|
|
||||||
scene.value.environment = newEnvMap
|
scene.value.environment = newEnvMap
|
||||||
scene.value.environmentIntensity = 1
|
scene.value.environmentIntensity = 1.25
|
||||||
scene.value.environmentRotation.z = 0.25
|
scene.value.environmentRotation.z = 0.25
|
||||||
result.renderTarget.texture.dispose();
|
result.renderTarget.texture.dispose();
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { BufferGeometry, Matrix4, Mesh, Object3D, Vector3 } from 'three';
|
import { BufferGeometry, Group, Matrix4, Mesh, Object3D, Vector3 } from 'three';
|
||||||
import { getColorHexFromRal } from '../ral';
|
import { getColorHexFromRal } from '../ral';
|
||||||
|
|
||||||
const props = defineProps(['index', 'models', 'last_element', 'first_element'])
|
const props = defineProps(['index', 'models', 'last_element', 'first_element'])
|
||||||
|
@ -16,7 +16,6 @@ const pillar_pattern = use_pattern()
|
||||||
const pillar_topper = use_topper()
|
const pillar_topper = use_topper()
|
||||||
const lamelle_color = use_lamelle_color()
|
const lamelle_color = use_lamelle_color()
|
||||||
|
|
||||||
const lSize = lamelle_height.value
|
|
||||||
const pillar_size = 104 * 0.001
|
const pillar_size = 104 * 0.001
|
||||||
|
|
||||||
const pillar_one_pos = ref()
|
const pillar_one_pos = ref()
|
||||||
|
@ -80,7 +79,7 @@ const instanced_fixing_two_el = [
|
||||||
const lamelleMatrix = (i: number) => {
|
const lamelleMatrix = (i: number) => {
|
||||||
const scale_x = (((extra.value as number) || fence_section.value) * 9.9)
|
const scale_x = (((extra.value as number) || fence_section.value) * 9.9)
|
||||||
const pos_x = pillar_size * 0.5
|
const pos_x = pillar_size * 0.5
|
||||||
const pos_y = (lSize * i)
|
const pos_y = (lamelle_height.value * i)
|
||||||
const pos_z = 0.022 * scale_koef
|
const pos_z = 0.022 * scale_koef
|
||||||
return new Matrix4().fromArray([
|
return new Matrix4().fromArray([
|
||||||
scale_x, 0, 0, 0,
|
scale_x, 0, 0, 0,
|
||||||
|
@ -91,7 +90,7 @@ const lamelleMatrix = (i: number) => {
|
||||||
}
|
}
|
||||||
const fixingOneMatrix = (i: number) => {
|
const fixingOneMatrix = (i: number) => {
|
||||||
const pos_x = pillar_one_pos.value + pillar_size * 0.66
|
const pos_x = pillar_one_pos.value + pillar_size * 0.66
|
||||||
const pos_y = (lSize * i) + 0.01 * scale_koef;
|
const pos_y = (lamelle_height.value * i) + 0.01 * scale_koef;
|
||||||
const pos_z = 0.022 * scale_koef
|
const pos_z = 0.022 * scale_koef
|
||||||
return new Matrix4().fromArray([
|
return new Matrix4().fromArray([
|
||||||
1, 0, 0, 0,
|
1, 0, 0, 0,
|
||||||
|
@ -102,7 +101,7 @@ const fixingOneMatrix = (i: number) => {
|
||||||
}
|
}
|
||||||
const fixingTwoMatrix = (i: number) => {
|
const fixingTwoMatrix = (i: number) => {
|
||||||
const pos_x = pillar_two_pos.value - pillar_size * 0.66
|
const pos_x = pillar_two_pos.value - pillar_size * 0.66
|
||||||
const pos_y = (lSize * i) + 0.01 * scale_koef;
|
const pos_y = (lamelle_height.value * i) + 0.01 * scale_koef;
|
||||||
const pos_z = 0.022 * scale_koef
|
const pos_z = 0.022 * scale_koef
|
||||||
return new Matrix4().fromArray([
|
return new Matrix4().fromArray([
|
||||||
1, 0, 0, 0,
|
1, 0, 0, 0,
|
||||||
|
@ -130,19 +129,35 @@ const setBraceCount = () => {
|
||||||
setBraceCount()
|
setBraceCount()
|
||||||
watch(lamelles_count, setBraceCount)
|
watch(lamelles_count, setBraceCount)
|
||||||
|
|
||||||
|
const calculateAddScale = (model: Mesh, k = 1): number => {
|
||||||
|
if (!model.geometry.boundingBox) {
|
||||||
|
model.geometry.computeBoundingBox(); // Вычисляем boundingBox, если он не был вычислен ранее
|
||||||
|
}
|
||||||
|
|
||||||
|
const boundingBoxHeight = Math.abs(
|
||||||
|
model.geometry.boundingBox!.max.y - model.geometry.boundingBox!.min.y
|
||||||
|
);
|
||||||
|
|
||||||
|
if (boundingBoxHeight === 0) {
|
||||||
|
console.warn('Bounding box height is zero. Returning default scale of 1.');
|
||||||
|
return 1; // Возвращаем 1 при делении на ноль
|
||||||
|
}
|
||||||
|
|
||||||
|
return (lamelle_height.value * k) / boundingBoxHeight;
|
||||||
|
};
|
||||||
|
|
||||||
const pillar = ref<Mesh[]>([])
|
const pillar = ref<Mesh[]>([])
|
||||||
const setPillar = () => {
|
const setPillar = () => {
|
||||||
const top = props.models.pillar_top.children[0];
|
const top = props.models.pillar_top.children[0];
|
||||||
top.position.setComponent(1, lSize * lamelles_count.value + lamelles_count.value * 0.0001 * scale_koef);
|
top.position.setComponent(1, lamelle_height.value * lamelles_count.value * scale_koef);
|
||||||
|
|
||||||
const pillar_outer = props.models.pillar_center.children[0];
|
const pillar_outer = props.models.pillar_center.children[0];
|
||||||
pillar_outer.scale.setComponent(1, lamelles_count.value);
|
pillar_outer.scale.setComponent(1, lamelles_count.value * calculateAddScale(pillar_outer));
|
||||||
|
|
||||||
const pillar_inner = props.models.pillar_inner.children[0];
|
const pillar_inner = props.models.pillar_inner.children[0];
|
||||||
pillar_inner.scale.setComponent(1, lamelles_count.value);
|
pillar_inner.scale.setComponent(1, lamelles_count.value * calculateAddScale(pillar_inner));
|
||||||
|
|
||||||
const bottom = props.models.pillar_bottom.children[0];
|
const bottom = props.models.pillar_bottom.children[0];
|
||||||
bottom.position.setComponent(1, lSize * -0.5);
|
|
||||||
|
|
||||||
let arr = [top, pillar_outer, pillar_inner, bottom]
|
let arr = [top, pillar_outer, pillar_inner, bottom]
|
||||||
arr.map(el => {
|
arr.map(el => {
|
||||||
|
@ -151,48 +166,50 @@ const setPillar = () => {
|
||||||
set_material(
|
set_material(
|
||||||
{ children: [arr[2]] },
|
{ children: [arr[2]] },
|
||||||
getColorHexFromRal(pillar_color.value),
|
getColorHexFromRal(pillar_color.value),
|
||||||
{ pattern: pillar_pattern.value, count: lamelles_count.value },
|
{ pattern: pillar_pattern.value, count: lamelles_count.value * calculateAddScale(pillar_inner) },
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
pillar.value = arr.map(el => el.clone())
|
pillar.value = arr.map(el => el.clone())
|
||||||
}
|
}
|
||||||
setPillar()
|
setPillar()
|
||||||
watch([pillar_pattern, pillar_color, pillar_topper, fence_section, lamelles_count], setPillar)
|
watch([pillar_pattern, pillar_color, pillar_topper, fence_section, lamelles_count, lamelle_height], setPillar)
|
||||||
|
|
||||||
const fastening = ref<Object3D[]>([])
|
const fastening = ref<Object3D[]>([])
|
||||||
const setFastening = () => {
|
const setFastening = () => {
|
||||||
const top_one = props.models.fixing.clone().children[0];
|
const top_one = props.models.fixing.clone().children[0];
|
||||||
top_one.position.set(
|
top_one.position.set(
|
||||||
pillar_one_pos.value + pillar_size * 0.66,
|
pillar_one_pos.value + pillar_size * 0.66,
|
||||||
lamelles_count.value * lSize - 0.015 * scale_koef,
|
lamelles_count.value * lamelle_height.value - 0.015 * scale_koef,
|
||||||
0.025 * scale_koef
|
0.025 * scale_koef
|
||||||
)
|
)
|
||||||
|
|
||||||
const top_two = props.models.fixing.clone().children[0];
|
const top_two = props.models.fixing.clone().children[0];
|
||||||
top_two.position.set(
|
top_two.position.set(
|
||||||
pillar_two_pos.value - pillar_size * 0.66,
|
pillar_two_pos.value - pillar_size * 0.66,
|
||||||
lamelles_count.value * lSize - 0.01 * scale_koef,
|
lamelles_count.value * lamelle_height.value - 0.01 * scale_koef,
|
||||||
0.025 * scale_koef
|
0.025 * scale_koef
|
||||||
)
|
)
|
||||||
|
|
||||||
const v = ((extra.value as number) || fence_section.value) * 10
|
const v = ((extra.value as number) || fence_section.value) * 10
|
||||||
|
|
||||||
const top = props.models.fastening_top.clone().children[0];
|
const top = props.models.fastening_top.clone().children[0];
|
||||||
top.position.set(
|
top.position.set(
|
||||||
pillar_size * 0.5,
|
pillar_size * 0.5,
|
||||||
lamelles_count.value * lSize - 0.0275 * scale_koef,
|
lamelles_count.value * lamelle_height.value -0.0275 * scale_koef,
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
top.scale.setComponent(0, v);
|
top.scale.setComponent(0, v);
|
||||||
|
|
||||||
|
let c = 0.0019 * scale_koef
|
||||||
const side_one = props.models.fastening_side.clone().children[0];
|
const side_one = props.models.fastening_side.clone().children[0];
|
||||||
side_one.name = 'side_one'
|
side_one.name = 'side_one'
|
||||||
side_one.position.set(pillar_one_pos.value, 0, 0.002 * scale_koef);
|
side_one.position.set(pillar_one_pos.value, 0, c);
|
||||||
side_one.scale.set(1, lamelles_count.value, 1)
|
side_one.scale.set(1, lamelles_count.value * calculateAddScale(side_one), 1)
|
||||||
|
|
||||||
const side_two = props.models.fastening_side.clone().children[0];
|
const side_two = props.models.fastening_side.clone().children[0];
|
||||||
side_two.name = 'side_two'
|
side_two.name = 'side_two'
|
||||||
side_two.scale.set(-1, lamelles_count.value, -1)
|
side_two.scale.set(-1, lamelles_count.value * calculateAddScale(side_two), -1)
|
||||||
side_two.position.set(pillar_two_pos.value, 0, -0.005 * scale_koef);
|
side_two.position.set(pillar_two_pos.value, 0, c);
|
||||||
|
|
||||||
let arr = [top_one, top_two, top, side_one, side_two];
|
let arr = [top_one, top_two, top, side_one, side_two];
|
||||||
[top, side_one, side_two, ...braces.value].map(el => {
|
[top, side_one, side_two, ...braces.value].map(el => {
|
||||||
|
@ -201,7 +218,7 @@ const setFastening = () => {
|
||||||
fastening.value = arr.map(el => el.clone())
|
fastening.value = arr.map(el => el.clone())
|
||||||
}
|
}
|
||||||
setFastening()
|
setFastening()
|
||||||
watch([pillar_color, lamelles_count, pillar_one_pos, pillar_two_pos], setFastening)
|
watch([pillar_color, pillar_one_pos, pillar_two_pos, lamelles_count, lamelle_height], setFastening)
|
||||||
|
|
||||||
const setLamellesColor = () => {
|
const setLamellesColor = () => {
|
||||||
if (instanced_lamelle.value) {
|
if (instanced_lamelle.value) {
|
||||||
|
@ -214,6 +231,7 @@ watch([instanced_lamelle, lamelle_color], setLamellesColor)
|
||||||
watch([
|
watch([
|
||||||
instanced_lamelle,
|
instanced_lamelle,
|
||||||
lamelles_count,
|
lamelles_count,
|
||||||
|
lamelle_height,
|
||||||
fence_section,
|
fence_section,
|
||||||
], () => {
|
], () => {
|
||||||
const translationVector = new Vector3(0, 20, 20)
|
const translationVector = new Vector3(0, 20, 20)
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { Object3D, Vector3 } from 'three';
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
import { useGLTF, } from '@tresjs/cientos'
|
import { useGLTF, } from '@tresjs/cientos'
|
||||||
import type { OrbitControlsProps } from '@tresjs/cientos/dist/core/controls/OrbitControls.vue.js';
|
import type { OrbitControlsProps } from '@tresjs/cientos/dist/core/controls/OrbitControls.vue.js';
|
||||||
import { getModel, toppers, type toppersIds } from '../topper';
|
import { getModel, allToppers, baseToppers, aristoToppers, type toppersIds } from '../topper';
|
||||||
|
|
||||||
const section_count = use_section_count()
|
const section_count = use_section_count()
|
||||||
const extra_section = use_extra_section()
|
const extra_section = use_extra_section()
|
||||||
|
@ -20,10 +20,36 @@ const goto_target = use_goto_target()
|
||||||
const { scene, controls, camera } = useTresContext()
|
const { scene, controls, camera } = useTresContext()
|
||||||
const { seek, seekAll } = useSeek()
|
const { seek, seekAll } = useSeek()
|
||||||
|
|
||||||
const topper_models = {} as { [key: toppersIds]: Object3D }
|
const globalFenceType = useGlobalFenceType()
|
||||||
|
|
||||||
|
const top = ref(null)
|
||||||
|
const pillar_top = ref()
|
||||||
|
const pillar_center = ref(null)
|
||||||
|
const pillar_bottom = ref(null)
|
||||||
|
const pillar_inner = ref(null)
|
||||||
|
const pillar_brace = ref(null)
|
||||||
|
const fastening_top = ref(null)
|
||||||
|
const fastening_side = ref(null)
|
||||||
|
const fixing = ref(null)
|
||||||
|
const lamelle = ref(null)
|
||||||
|
|
||||||
|
const total = ref(0)
|
||||||
|
const size = ref(0)
|
||||||
|
const count = ref(0)
|
||||||
|
|
||||||
|
const min_for_square = 12;
|
||||||
|
|
||||||
|
const loadAll = async () => {
|
||||||
|
const topper_models = {
|
||||||
|
baseToppers: {} as { [key: toppersIds]: Object3D },
|
||||||
|
aristoToppers: {} as { [key: toppersIds]: Object3D },
|
||||||
|
}
|
||||||
|
for (const key of Object.keys(allToppers)) {
|
||||||
|
let toppers = allToppers[key]
|
||||||
for await (const element of toppers) {
|
for await (const element of toppers) {
|
||||||
const { scene } = await useGLTF(getModel(element.id))
|
const { scene } = await useGLTF(getModel(element.id, key))
|
||||||
topper_models[element.id] = scene
|
topper_models[key][element.id] = scene
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const { scene: model_pillar_center } = await useGLTF('/models_one/pillar/center.glb')
|
const { scene: model_pillar_center } = await useGLTF('/models_one/pillar/center.glb')
|
||||||
|
@ -36,26 +62,34 @@ const { scene: model_fastening_side } = await useGLTF('/models_one/fastening/sid
|
||||||
const { scene: model_fixing } = await useGLTF('/models_one/fixing.glb');
|
const { scene: model_fixing } = await useGLTF('/models_one/fixing.glb');
|
||||||
|
|
||||||
const { scene: top_model } = await useGLTF('/models_one/top_100.glb', { draco: true })
|
const { scene: top_model } = await useGLTF('/models_one/top_100.glb', { draco: true })
|
||||||
const { scene: lamelle_model } = await useGLTF('/models_one/lamel_100.glb', { draco: true });
|
// const { scene: lamelle_model } = await useGLTF('/models_one/lamel_100.glb', { draco: true });
|
||||||
|
|
||||||
const top = ref(top_model)
|
const { scene: lamelle_model_standart } = await useGLTF('/models_one/lamel_100.glb', { draco: true });
|
||||||
const pillar_top = ref(topper_models[pillar_topper.value])
|
const { scene: lamelle_model_aristo } = await useGLTF('/models_one/lamel_100_aristo.glb', { draco: true });
|
||||||
const pillar_center = ref(model_pillar_center)
|
|
||||||
const pillar_bottom = ref(model_pillar_bottom)
|
|
||||||
const pillar_inner = ref(model_pillar_inner)
|
|
||||||
const pillar_brace = ref(model_pillar_brace)
|
|
||||||
const fastening_top = ref(model_fastening_top)
|
|
||||||
const fastening_side = ref(model_fastening_side)
|
|
||||||
const fixing = ref(model_fixing)
|
|
||||||
const lamelle = ref(lamelle_model)
|
|
||||||
|
|
||||||
watch(pillar_topper, async () => {
|
top.value = top_model
|
||||||
pillar_top.value = topper_models[pillar_topper.value]
|
pillar_center.value = model_pillar_center
|
||||||
})
|
pillar_bottom.value = model_pillar_bottom
|
||||||
|
pillar_inner.value = model_pillar_inner
|
||||||
|
pillar_brace.value = model_pillar_brace
|
||||||
|
fastening_top.value = model_fastening_top
|
||||||
|
fastening_side.value = model_fastening_side
|
||||||
|
fixing.value = model_fixing
|
||||||
|
lamelle.value = lamelle_model_standart
|
||||||
|
|
||||||
const total = ref((section_count.value + ~~(!!extra_section.value)))
|
const setPillarTopper = () => {
|
||||||
const size = ref(Math.ceil(total.value / 4))
|
let key = 'baseToppers'
|
||||||
const count = ref((total.value >= 4) ? size.value : total.value)
|
if (globalFenceType.value?.type == 'aristo') {
|
||||||
|
key = 'aristoToppers'
|
||||||
|
}
|
||||||
|
pillar_top.value = topper_models[key][pillar_topper.value]
|
||||||
|
}
|
||||||
|
setPillarTopper()
|
||||||
|
watch(pillar_topper, setPillarTopper)
|
||||||
|
|
||||||
|
total.value = (section_count.value + ~~(!!extra_section.value))
|
||||||
|
size.value = Math.ceil(total.value / 4)
|
||||||
|
count.value = (total.value >= 4) ? size.value : total.value
|
||||||
|
|
||||||
watch(() => [section_count.value, extra_section.value], () => {
|
watch(() => [section_count.value, extra_section.value], () => {
|
||||||
total.value = (section_count.value + ~~(!!extra_section.value))
|
total.value = (section_count.value + ~~(!!extra_section.value))
|
||||||
|
@ -100,7 +134,7 @@ const setTarget = (smooth = false) => {
|
||||||
goto_cam.value = new Vector3(f, f, f)
|
goto_cam.value = new Vector3(f, f, f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
watch([lamelles_count, fence_section], () => {
|
watch([lamelles_count, fence_section, lamelle_height], () => {
|
||||||
setTarget()
|
setTarget()
|
||||||
open_calc.value = []
|
open_calc.value = []
|
||||||
})
|
})
|
||||||
|
@ -110,13 +144,32 @@ watch(open_calc, () => {
|
||||||
setTarget(true)
|
setTarget(true)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const min_for_square = 12;
|
|
||||||
|
|
||||||
setTarget()
|
setTarget()
|
||||||
|
|
||||||
|
const setLamelleType = () => {
|
||||||
|
if (globalFenceType.value?.type == 'standart') {
|
||||||
|
lamelle.value = lamelle_model_standart
|
||||||
|
lamelle_height.value = 0.115
|
||||||
|
pillar_topper.value = 0
|
||||||
|
setPillarTopper()
|
||||||
|
}
|
||||||
|
if (globalFenceType.value?.type == 'aristo') {
|
||||||
|
lamelle.value = lamelle_model_aristo
|
||||||
|
lamelle_height.value = 0.196
|
||||||
|
pillar_topper.value = 0
|
||||||
|
setPillarTopper()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setLamelleType()
|
||||||
|
watch(() => globalFenceType.value?.id, setLamelleType)
|
||||||
|
}
|
||||||
|
onMounted(async () => { await loadAll() })
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<TresGroup name="base">
|
<TresGroup name="base">
|
||||||
<template v-for="line in (total >= min_for_square) ? 4 : 1" :key="`${line}_${count}`">
|
<template v-for="line in (total >= min_for_square) ? 4 : 1"
|
||||||
|
:key="`${globalFenceType?.id ?? 5}_${line}_${count}`">
|
||||||
<ModelLine :models="{
|
<ModelLine :models="{
|
||||||
top,
|
top,
|
||||||
pillar_center, pillar_top, pillar_bottom, pillar_inner,
|
pillar_center, pillar_top, pillar_bottom, pillar_inner,
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
<script setup>
|
||||||
|
const props = defineProps({
|
||||||
|
canvasProps: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
cameraProps: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({}),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const container = ref(null);
|
||||||
|
const isIntersecting = ref(true);
|
||||||
|
let observer;
|
||||||
|
|
||||||
|
// Вычисляем renderMode на основе видимости
|
||||||
|
const renderMode = computed(() => (isIntersecting.value ? 'always' : 'never'));
|
||||||
|
const startObserver = () => {
|
||||||
|
observer = new IntersectionObserver(
|
||||||
|
(entries) => {
|
||||||
|
entries.forEach((entry) => {
|
||||||
|
isIntersecting.value = entry.isIntersecting;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
{ threshold: 0.1 } // Настройте порог видимости по вашему усмотрению
|
||||||
|
);
|
||||||
|
|
||||||
|
if (container.value) {
|
||||||
|
observer.observe(container.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onMounted(async () => {
|
||||||
|
await nextTick(); // Ждём завершения рендеринга
|
||||||
|
startObserver(); // Запускаем IntersectionObserver
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
if (observer) {
|
||||||
|
observer.disconnect();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div ref="container" class="h-full">
|
||||||
|
<Suspense>
|
||||||
|
<TresCanvas v-bind="canvasProps" :render-mode="renderMode" :key="renderMode">
|
||||||
|
<ModelEnv />
|
||||||
|
<Suspense>
|
||||||
|
<slot />
|
||||||
|
</Suspense>
|
||||||
|
</TresCanvas>
|
||||||
|
</Suspense>
|
||||||
|
</div>
|
||||||
|
</template>
|
|
@ -1,24 +1,63 @@
|
||||||
export const toppers = [
|
interface Topper {
|
||||||
|
id: number; // ID элемента
|
||||||
|
name: string; // Название
|
||||||
|
filename: string; // Имя файла
|
||||||
|
model: string; // Модель
|
||||||
|
}
|
||||||
|
|
||||||
|
export const baseToppers = [
|
||||||
{ name: 'Ровный', filename: 'icon_stolb_verh_3.svg', model: 'top' },
|
{ name: 'Ровный', filename: 'icon_stolb_verh_3.svg', model: 'top' },
|
||||||
{ name: 'Вершина 1', filename: 'icon_stolb_verh_2.svg', model: 'decor1' },
|
{ name: 'Вершина 1', filename: 'icon_stolb_verh_2.svg', model: 'decor1' },
|
||||||
{ name: 'Вершина 2', filename: 'icon_stolb_verh_1.svg', model: 'decor2' },
|
{ name: 'Вершина 2', filename: 'icon_stolb_verh_1.svg', model: 'decor2' },
|
||||||
].map((el, i) => Object.assign(el, { id: i }))
|
].map((el, i) => Object.assign(el, { id: i }))
|
||||||
|
|
||||||
export const getFilename = (id: toppersIds) => {
|
export const aristoToppers = [
|
||||||
const el = toppers.find(el => el.id == id)
|
{ name: 'Колпак', filename: 'icon_stolb_verh_1.svg', model: 'aristo1' },
|
||||||
|
].map((el, i) => Object.assign(el, { id: i }))
|
||||||
|
|
||||||
|
export const allToppers = {
|
||||||
|
'baseToppers': baseToppers,
|
||||||
|
'aristoToppers': aristoToppers,
|
||||||
|
} as { [key: string]: Topper[] }
|
||||||
|
|
||||||
|
function getToppersByType(type: string | null): any {
|
||||||
|
if (!type) type = 'NONE'
|
||||||
|
// Проверяем, существует ли указанный тип в объекте allToppers
|
||||||
|
if (allToppers[type]) {
|
||||||
|
return allToppers[type];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Если типа нет, берем первый доступный элемент из allToppers
|
||||||
|
const keys = Object.keys(allToppers);
|
||||||
|
if (keys.length > 0) {
|
||||||
|
const firstKey = keys[0];
|
||||||
|
return allToppers[firstKey];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Если объект allToppers пустой, возвращаем значение по умолчанию (например, пустой массив)
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
export const getFilename = (id: toppersIds, type: string | null) => {
|
||||||
|
let toppers = getToppersByType(type)
|
||||||
|
|
||||||
|
const el = toppers.find((el: Topper) => el.id == id)
|
||||||
if (!el || !el.filename) return undefined
|
if (!el || !el.filename) return undefined
|
||||||
return `/topper/${el?.filename}`
|
return `/topper/${el?.filename}`
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getName = (id: toppersIds) => {
|
export const getName = (id: toppersIds, type: string | null) => {
|
||||||
const el = toppers.find(el => el.id == id)
|
let toppers = getToppersByType(type)
|
||||||
|
|
||||||
|
const el = toppers.find((el: Topper) => el.id == id)
|
||||||
if (!el || !el.filename) return undefined
|
if (!el || !el.filename) return undefined
|
||||||
return el.name
|
return el.name
|
||||||
}
|
}
|
||||||
export const getModel = (id: toppersIds) => {
|
export const getModel = (id: toppersIds, type: string | null) => {
|
||||||
const el = toppers.find(el => el.id == id)
|
let toppers = getToppersByType(type)
|
||||||
|
|
||||||
|
let el = toppers.find((el: Topper) => el.id == id)
|
||||||
if (!el || !el.filename) return undefined
|
if (!el || !el.filename) return undefined
|
||||||
return `/models_one/pillar/topper/${el?.model}.glb`
|
return `/models_one/pillar/topper/${el?.model}.glb`
|
||||||
}
|
}
|
||||||
|
|
||||||
export type toppersIds = typeof toppers[number]['id']
|
export type toppersIds = typeof allToppers[string][number]['id']
|
|
@ -0,0 +1,7 @@
|
||||||
|
interface calc {
|
||||||
|
id: number
|
||||||
|
type: 'standart' | 'aristo'
|
||||||
|
calc: ApiCalcType
|
||||||
|
}
|
||||||
|
export const useGlobalFenceType = () => useState<calc | null>('fence-global-type', () => null)
|
||||||
|
|
|
@ -32,7 +32,7 @@ if(route.path !== '/404') {
|
||||||
<h1>Вы ищете страницу, которой не существует. Вернитесь на главную страницу сайта</h1>
|
<h1>Вы ищете страницу, которой не существует. Вернитесь на главную страницу сайта</h1>
|
||||||
<p>Извините, но мы не можем найти запрашиваемую страницу. К сожалению, мы не можем помочь вам с
|
<p>Извините, но мы не можем найти запрашиваемую страницу. К сожалению, мы не можем помочь вам с
|
||||||
покупкой забора здесь.</p>
|
покупкой забора здесь.</p>
|
||||||
<p class="hidden">
|
<p>
|
||||||
<code>
|
<code>
|
||||||
{{ error?.message }}
|
{{ error?.message }}
|
||||||
</code>
|
</code>
|
||||||
|
|
|
@ -10,6 +10,9 @@ export default defineNuxtConfig({
|
||||||
htmlAttrs: {
|
htmlAttrs: {
|
||||||
lang: 'ru',
|
lang: 'ru',
|
||||||
},
|
},
|
||||||
|
link: [
|
||||||
|
{ rel: 'icon', type: 'image/svg+xml', href: '/tabler--fence-filled.svg' },
|
||||||
|
]
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ssr: true,
|
ssr: true,
|
||||||
|
@ -25,7 +28,9 @@ export default defineNuxtConfig({
|
||||||
],
|
],
|
||||||
runtimeConfig: {
|
runtimeConfig: {
|
||||||
public: {
|
public: {
|
||||||
apiBase: 'https://mns.kustarshina.ru/kp',
|
// apiBase: process.env.mode == 'DEVELOPMENT' ? "http://localhost:8000" : "https://mns.kustarshina.ru/kp",
|
||||||
|
apiBase: process.env.mode == 'DEVELOPMENT' ? "http://mns.dev.kustarshina.ru" : "https://mns.kustarshina.ru/kp",
|
||||||
|
// apiBase: 'http://localhost:8000',
|
||||||
imgBase: 'https://mns.kustarshina.ru',
|
imgBase: 'https://mns.kustarshina.ru',
|
||||||
baseUrl: '',
|
baseUrl: '',
|
||||||
yandexMetrika: {
|
yandexMetrika: {
|
||||||
|
|
|
@ -7,6 +7,8 @@ import { marked } from 'marked';
|
||||||
|
|
||||||
import og_img from '/og_img.png'
|
import og_img from '/og_img.png'
|
||||||
|
|
||||||
|
const globalFenceType = useGlobalFenceType()
|
||||||
|
|
||||||
const { data: seoData } = await apiFetch<ApiKpType>(`kp/1/`)
|
const { data: seoData } = await apiFetch<ApiKpType>(`kp/1/`)
|
||||||
useSeoMeta({
|
useSeoMeta({
|
||||||
title: seoData.value?.title,
|
title: seoData.value?.title,
|
||||||
|
@ -21,7 +23,10 @@ const { data: menuData } = await apiFetch<ApiMenuType>(`menu/1/?ordering=order`)
|
||||||
const pagesData = menuData.value ? menuData.value.pages : []
|
const pagesData = menuData.value ? menuData.value.pages : []
|
||||||
|
|
||||||
const { data: reviewsData } = await apiFetch<ApiReviewsType[]>(`review/`)
|
const { data: reviewsData } = await apiFetch<ApiReviewsType[]>(`review/`)
|
||||||
const { data: calculatorData } = await apiFetch(`calculator/5/`)
|
|
||||||
|
const { data: calculators } = await apiFetch<ApiCalcType[]>('calculator/')
|
||||||
|
const openTab = ref<number>()
|
||||||
|
if (calculators.value?.length) openTab.value = calculators.value[0].id
|
||||||
|
|
||||||
const about = pagesData.find(el => el.slug == 'about')
|
const about = pagesData.find(el => el.slug == 'about')
|
||||||
const reviews = pagesData.find(el => el.slug == 'clients')
|
const reviews = pagesData.find(el => el.slug == 'clients')
|
||||||
|
@ -37,16 +42,32 @@ const aboutText = computed(() => marked.parse(about?.content || ''))
|
||||||
const deliveryText = computed(() => delivery?.content.split('[col]').map(el => marked.parse(el || '')))
|
const deliveryText = computed(() => delivery?.content.split('[col]').map(el => marked.parse(el || '')))
|
||||||
const advantagesText = computed(() => {
|
const advantagesText = computed(() => {
|
||||||
let c = advantages?.content || ''
|
let c = advantages?.content || ''
|
||||||
Object.entries(calculatorData.value || {}).map(item => {
|
Object.entries(calculators.value?.find(el => el.id == openTab.value) || {}).map(item => {
|
||||||
c = c.replaceAll(`[${item[0]}]`, roubleSign.format(item[1]))
|
c = c.replaceAll(`[${item[0]}]`, roubleSign.format(item[1] as number))
|
||||||
})
|
})
|
||||||
return marked.parse(c)
|
return marked.parse(c)
|
||||||
})
|
})
|
||||||
|
|
||||||
const { data: advData } = await apiFetch<ApiAdvantageType[]>(`advantage/`)
|
const { data: advData } = await apiFetch<ApiAdvantageType[]>(`advantage/`)
|
||||||
|
|
||||||
|
watch(openTab, () => {
|
||||||
|
if (!openTab.value) return
|
||||||
|
if (!calculators.value) return
|
||||||
|
let type = 'standart'
|
||||||
|
if (openTab.value == 6) {
|
||||||
|
type = 'aristo'
|
||||||
|
}
|
||||||
|
const v = {
|
||||||
|
id: openTab.value as number,
|
||||||
|
type: type as "standart" | "aristo",
|
||||||
|
calc: calculators.value.find(el => el.id == openTab.value) as ApiCalcType,
|
||||||
|
}
|
||||||
|
globalFenceType.value = v
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
|
<Modal :calcData="(calculators || []).find(el => el.id == openTab)" />
|
||||||
<div class="siteblock bg-white" :id="about?.slug" v-if="about">
|
<div class="siteblock bg-white" :id="about?.slug" v-if="about">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1 class="siteblock-title">{{ about?.title }}</h1>
|
<h1 class="siteblock-title">{{ about?.title }}</h1>
|
||||||
|
@ -92,6 +113,24 @@ const { data: advData } = await apiFetch<ApiAdvantageType[]>(`advantage/`)
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="siteblock bg-white" :id="advantages?.slug">
|
<div class="siteblock bg-white" :id="advantages?.slug">
|
||||||
|
<div class="flex col-span-full gap-2 justify-center">
|
||||||
|
<template v-for="item in calculators">
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<label class="rounded p-2 cursor-pointer flex gap-2" :for="item.id.toString()">
|
||||||
|
<input type="radio" :id="item.id.toString()" :value="item.id" v-model="openTab"
|
||||||
|
:checked="openTab == item.id">
|
||||||
|
<span :class="[
|
||||||
|
'text-2xl font-bold bg-clip-text text-transparent drop-shadow-xs',
|
||||||
|
openTab == item.id
|
||||||
|
? 'bg-gradient-to-r from-yellow-400 via-orange-500 to-red-600'
|
||||||
|
: 'bg-gradient-to-r from-slate-400 to-slate-600'
|
||||||
|
]">
|
||||||
|
{{ item.title }}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
<div class="container gap-4">
|
<div class="container gap-4">
|
||||||
<div class="col-span-full xl:col-span-8">
|
<div class="col-span-full xl:col-span-8">
|
||||||
<ExpDiagram />
|
<ExpDiagram />
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="currentColor" d="M19 17v3a1 1 0 0 1-1 1h-4a1 1 0 0 1-1-1v-3zm-8 0v3a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1v-3zm9-5a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1zM8.707 3.293l2 2A1 1 0 0 1 11 6v5H5V6a1 1 0 0 1 .293-.707l2-2a1 1 0 0 1 1.414 0m8 0l2 2A1 1 0 0 1 19 6v5h-6V6a1 1 0 0 1 .293-.707l2-2a1 1 0 0 1 1.414 0"/></svg>
|
After Width: | Height: | Size: 424 B |
|
@ -3,6 +3,7 @@ export async function apiFetch<T>(path: string) {
|
||||||
const apiBase = config.public.apiBase
|
const apiBase = config.public.apiBase
|
||||||
const headers = new Headers()
|
const headers = new Headers()
|
||||||
headers.set('Referer', config.public.baseUrl)
|
headers.set('Referer', config.public.baseUrl)
|
||||||
|
|
||||||
return useFetch<T>(`${apiBase}/${path}`, {
|
return useFetch<T>(`${apiBase}/${path}`, {
|
||||||
baseURL: config.public.baseUrl,
|
baseURL: config.public.baseUrl,
|
||||||
headers,
|
headers,
|
||||||
|
|
Loading…
Reference in New Issue