225 lines
6.1 KiB
Vue
225 lines
6.1 KiB
Vue
<script lang="ts" setup>
|
||
import { useGtm } from '@gtm-support/vue-gtm'
|
||
import { isClient } from '@vueuse/core'
|
||
import type { StuffItem } from '~/data/food'
|
||
import { meat, staple, tools, vegetable } from '~/data/food'
|
||
import recipeData from '~/data/recipe.json'
|
||
import type { Recipe } from '~/types'
|
||
import { useRecipeStore } from '~/stores/recipe'
|
||
|
||
import { useInvisibleElement } from '~/composables/helper'
|
||
|
||
const recipe = ref<Recipe>(recipeData as Recipe)
|
||
|
||
const rStore = useRecipeStore()
|
||
const curStuff = computed(() => rStore.selectedStuff)
|
||
const curTools = computed(() => rStore.selectedTools)
|
||
|
||
// 默认严格模式
|
||
const strict = ref(true)
|
||
const displayedRecipe = computed(() => {
|
||
return recipe.value.filter((item) => {
|
||
if (strict.value) {
|
||
const stuffFlag = curStuff.value.every(stuff => item.stuff.includes(stuff))
|
||
// const toolFlag = curTools.value.every(tool => item.tools?.includes(tool))
|
||
const toolFlag = curTools.value.some(tool => item.tools?.includes(tool))
|
||
return curTools.value.length ? stuffFlag && toolFlag : stuffFlag
|
||
}
|
||
else {
|
||
const stuffFlag = curStuff.value.some(stuff => item.stuff.includes(stuff))
|
||
const toolFlag = curTools.value.some(tool => item.tools?.includes(tool))
|
||
return stuffFlag || toolFlag
|
||
}
|
||
})
|
||
})
|
||
|
||
const { x, y } = usePointer()
|
||
|
||
const recipeBtn = ref<HTMLButtonElement>()
|
||
|
||
const { top, left } = useElementBounding(recipeBtn)
|
||
|
||
const playAnimation = (emoji: string) => {
|
||
if (!isClient)
|
||
return
|
||
|
||
// 单个 Vue 组件实现不适合创建多个元素和清除动画
|
||
const emojiEl = document.createElement('span')
|
||
emojiEl.style.position = 'fixed'
|
||
emojiEl.style.left = `${x.value}px`
|
||
emojiEl.style.top = `${y.value}px`
|
||
emojiEl.style.zIndex = '10'
|
||
emojiEl.style.transition = 'left .4s linear, top .4s cubic-bezier(0.5, -0.5, 1, 1)'
|
||
emojiEl.textContent = emoji
|
||
document.body.appendChild(emojiEl)
|
||
|
||
setTimeout(() => {
|
||
if (top.value)
|
||
emojiEl.style.top = `${top.value}px`
|
||
if (left.value)
|
||
emojiEl.style.left = `${left.value + 12}px`
|
||
}, 0)
|
||
|
||
emojiEl.ontransitionend = () => {
|
||
emojiEl.remove()
|
||
}
|
||
}
|
||
|
||
const gtm = useGtm()
|
||
|
||
const toggleStuff = (item: StuffItem, category = '', e?: Event) => {
|
||
rStore.toggleStuff(item.name)
|
||
|
||
if (curStuff.value.includes(item.name))
|
||
playAnimation(item.emoji)
|
||
|
||
gtm?.trackEvent({
|
||
event: 'click',
|
||
category: `${category}_${item.name}`,
|
||
action: 'click_stuff',
|
||
label: '食材',
|
||
})
|
||
}
|
||
|
||
/**
|
||
* toggle tool
|
||
* @param item
|
||
*/
|
||
const clickTool = (item: StuffItem) => {
|
||
const value = item.name
|
||
rStore.toggleTools(value)
|
||
|
||
gtm?.trackEvent({
|
||
event: 'click',
|
||
category: `tool_${value}`,
|
||
action: 'click_tool',
|
||
label: '工具',
|
||
})
|
||
}
|
||
|
||
const recipePanel = ref()
|
||
const { isVisible, show } = useInvisibleElement(recipePanel)
|
||
</script>
|
||
|
||
<template>
|
||
<Transition>
|
||
<button
|
||
v-if="displayedRecipe.length !== recipe.length && isVisible"
|
||
ref="recipeBtn"
|
||
class="cursor-pointer fixed inline-flex justify-center items-center rounded rounded-full shadow hover:shadow-md"
|
||
bg="green-50" w="10" h="10" bottom="4" right="4"
|
||
text="green-600"
|
||
@click="show"
|
||
>
|
||
<span v-if="displayedRecipe.length">
|
||
<div i-mdi-bowl-mix-outline />
|
||
</span>
|
||
<span v-else>
|
||
<div i-mdi-bowl-outline />
|
||
</span>
|
||
</button>
|
||
</Transition>
|
||
|
||
<h2 m="t-4" text="xl" font="bold" p="1">
|
||
🥘 先选一下食材
|
||
</h2>
|
||
<div>
|
||
<h2 opacity="90" text="base" font="bold" p="1">
|
||
🥬 菜菜们
|
||
</h2>
|
||
<VegetableTag
|
||
v-for="item, i in vegetable" :key="i"
|
||
:active="curStuff.includes(item.name)"
|
||
@click="() => toggleStuff(item, 'vegetable')"
|
||
>
|
||
<span v-if="item.emoji" class="inline-flex">{{ item.emoji }}</span>
|
||
<span v-else-if="item.image" class="inline-flex">
|
||
<img class="inline-flex" w="2" h="2" width="10" height="10" :src="item.image" :alt="item.name">
|
||
</span>
|
||
<span class="inline-flex" m="l-1">
|
||
{{
|
||
item.name
|
||
}}
|
||
</span>
|
||
</VegetableTag>
|
||
</div>
|
||
<div m="y-4">
|
||
<h2 opacity="90" text="base" font="bold" p="1">
|
||
🥩 肉肉们
|
||
</h2>
|
||
<MeatTag
|
||
v-for="item, i in meat" :key="i"
|
||
:active="curStuff.includes(item.name)"
|
||
@click="toggleStuff(item, 'meat')"
|
||
>
|
||
<span>{{ item.emoji }}</span>
|
||
<span m="l-1">
|
||
{{
|
||
item.name
|
||
}}
|
||
</span>
|
||
</MeatTag>
|
||
</div>
|
||
<div m="y-4">
|
||
<h2 opacity="90" text="base" font="bold" p="1">
|
||
🍚 一起下锅的主食(不选也行)
|
||
</h2>
|
||
<StapleTag
|
||
v-for="item, i in staple" :key="i"
|
||
:active="curStuff.includes(item.name)"
|
||
@click="toggleStuff(item, 'staple')"
|
||
>
|
||
<span>{{ item.emoji }}</span>
|
||
<span m="l-1">
|
||
{{
|
||
item.name
|
||
}}
|
||
</span>
|
||
</StapleTag>
|
||
</div>
|
||
<div m="t-4">
|
||
<h2 text="xl" font="bold" p="1">
|
||
🍳 再选一下厨具
|
||
</h2>
|
||
<ToolTag
|
||
v-for="item, i in tools" :key="i"
|
||
:active="curTools.includes(item.name)"
|
||
@click="clickTool(item)"
|
||
>
|
||
<span v-if="item.emoji" class="inline-flex">{{ item.emoji }}</span>
|
||
<span v-else-if="item.icon" class="inline-flex">
|
||
<div :class="item.icon" />
|
||
</span>
|
||
<span class="inline-flex" m="l-1">
|
||
{{
|
||
item.label || item.name
|
||
}}
|
||
</span>
|
||
</ToolTag>
|
||
</div>
|
||
|
||
<div ref="recipePanel" m="2 t-4" p="2" class="transition shadow hover:shadow-md" bg="gray-400/8">
|
||
<h2 text="xl" font="bold" p="1">
|
||
🍲 来看看组合出的菜谱吧!
|
||
</h2>
|
||
<Transition mode="out-in">
|
||
<div p="2">
|
||
<span v-if="!curStuff.length && !curTools.length" text="sm" p="2">
|
||
你要先选食材或工具哦~
|
||
</span>
|
||
|
||
<span v-else-if="displayedRecipe.length">
|
||
<DishTag v-for="item, i in displayedRecipe" :key="i" :dish="item" />
|
||
</span>
|
||
|
||
<span v-else text="sm">
|
||
还没有完美匹配的菜谱呢……
|
||
<br>
|
||
大胆尝试一下,或者<a href="#" @click="rStore.reset()">
|
||
<strong>换个组合</strong></a>?
|
||
</span>
|
||
</div>
|
||
</Transition>
|
||
</div>
|
||
</template>
|