- enforce localhost:5174 for wheel campaign client URL in local development - lock vite dev server ports using strictPort on admin (5173) and client (5174) - sanitize .env.example and remove hardcoded staging domain - configure root dev orchestration and docker multi-stage build context
104 lines
4.1 KiB
Vue
104 lines
4.1 KiB
Vue
<script setup lang="ts">
|
|
import { toFarsiDigits, WheelDesignProps } from '@lucky-wheel/shared-types';
|
|
import { useWheelFromProps } from '../../composables/useWheelBase';
|
|
|
|
const props = defineProps<WheelDesignProps>();
|
|
const emit = defineEmits<{ (e: 'spin-finished'): void }>();
|
|
|
|
const { wheelStyle, pointerStyle, getSlicePath, getTextRotation, pegs } = useWheelFromProps(
|
|
props,
|
|
() => emit('spin-finished'),
|
|
{ radius: 138, pegRadius: 143 },
|
|
);
|
|
|
|
const woodFallback = ['#8B5A2B', '#A0522D', '#6B4226', '#C4A484', '#8B5A2B', '#5D4037'];
|
|
</script>
|
|
|
|
<template>
|
|
<div class="relative w-full max-w-[340px] xs:max-w-[380px] sm:max-w-[420px] aspect-square mx-auto select-none">
|
|
<!-- Rustic wooden bezel -->
|
|
<div
|
|
class="absolute inset-0 rounded-full p-3 shadow-[0_12px_30px_rgba(60,30,10,0.55)]"
|
|
style="background: linear-gradient(145deg, #c9a36a 0%, #6b3f1e 40%, #3e2412 100%)"
|
|
>
|
|
<div
|
|
class="w-full h-full rounded-full border-[3px] border-[#c9a36a] relative overflow-hidden"
|
|
style="background: radial-gradient(circle at 40% 35%, #5c3a1e 0%, #2a160c 70%)"
|
|
>
|
|
<svg class="w-full h-full" :style="wheelStyle" viewBox="0 0 300 300">
|
|
<!-- Wood grain rings -->
|
|
<circle cx="150" cy="150" r="136" fill="none" stroke="#4a2c14" stroke-width="8" opacity="0.35" />
|
|
<circle cx="150" cy="150" r="120" fill="none" stroke="#7a4a22" stroke-width="2" opacity="0.25" />
|
|
|
|
<g v-for="(seg, idx) in segments" :key="seg.id">
|
|
<path
|
|
:d="getSlicePath(idx)"
|
|
:fill="seg.color || woodFallback[idx % woodFallback.length]"
|
|
stroke="#3e2723"
|
|
stroke-width="1.5"
|
|
/>
|
|
<g :transform="getTextRotation(idx)">
|
|
<text
|
|
x="150"
|
|
y="45"
|
|
text-anchor="middle"
|
|
fill="#fff8e7"
|
|
font-size="11"
|
|
font-weight="700"
|
|
class="wheel-slice-text font-sans"
|
|
>
|
|
{{ toFarsiDigits(seg.title) }}
|
|
</text>
|
|
<image v-if="seg.image" :href="seg.image" x="138" y="54" width="24" height="24" />
|
|
</g>
|
|
</g>
|
|
|
|
<!-- Metal bolts -->
|
|
<g v-for="(peg, pIdx) in pegs" :key="pIdx">
|
|
<circle :cx="peg.x" :cy="peg.y" r="4" fill="#2c1810" stroke="#8d6e63" stroke-width="0.8" />
|
|
<circle :cx="peg.x" :cy="peg.y" r="2.6" fill="url(#woodBolt)" />
|
|
<line
|
|
:x1="peg.x - 1.6"
|
|
:y1="peg.y"
|
|
:x2="peg.x + 1.6"
|
|
:y2="peg.y"
|
|
stroke="#5d4037"
|
|
stroke-width="0.7"
|
|
/>
|
|
</g>
|
|
|
|
<circle cx="150" cy="150" r="30" fill="url(#woodHub)" stroke="#d7ccc8" stroke-width="2" />
|
|
<circle cx="150" cy="150" r="18" fill="#3e2723" stroke="#a1887f" stroke-width="1.5" />
|
|
<text x="150" y="156" text-anchor="middle" font-size="14" fill="#efebe9">🪵</text>
|
|
|
|
<defs>
|
|
<radialGradient id="woodBolt" cx="35%" cy="30%" r="70%">
|
|
<stop offset="0%" stop-color="#efebe9" />
|
|
<stop offset="50%" stop-color="#9e9e9e" />
|
|
<stop offset="100%" stop-color="#424242" />
|
|
</radialGradient>
|
|
<radialGradient id="woodHub" cx="40%" cy="35%" r="65%">
|
|
<stop offset="0%" stop-color="#bcaaa4" />
|
|
<stop offset="100%" stop-color="#5d4037" />
|
|
</radialGradient>
|
|
</defs>
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="absolute -top-3 left-1/2 -translate-x-1/2 z-20 pointer-events-none drop-shadow-lg">
|
|
<div :style="pointerStyle" class="w-9 h-[44px] origin-[50%_12px] will-change-transform">
|
|
<svg width="36" height="44" viewBox="0 0 36 44" fill="none">
|
|
<path
|
|
d="M18 44L5 14C3 9 7 3 12 3H24C29 3 33 9 31 14L18 44Z"
|
|
fill="#8d6e63"
|
|
stroke="#efebe9"
|
|
stroke-width="1.5"
|
|
/>
|
|
<circle cx="18" cy="12" r="4.5" fill="#5d4037" stroke="#d7ccc8" stroke-width="1.2" />
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|