feat: Implement warnings for incompatible food combinations (#82)
* feat: 添加食物相克检测功能及相关数据支持 * chore: 修改 .gitignore 设置,并删除 recipe.json 的 git 引用 * style: 格式化 incompatible-foods.ts 文件中的代码,修改缩紧为 2 * fix: 修复导入语句,确保正确使用 Vue 的 ref 和 computed * refactor: 优化相克检测逻辑,使用 Set 提高性能 * refactor: 移除不必要的 IncompatibleRule 类型定义 * fix: 添加缺失字段检查,确保不完整数据不会被处理 * style: 使用 eslint 优化代码格式
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
import type { StuffItem } from '~/types'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useEmojiAnimation } from '~/composables/animation'
|
||||
import { useIncompatibleFoods } from '~/composables/incompatible-foods'
|
||||
|
||||
import { meat, staple, tools, vegetable } from '~/data/food'
|
||||
|
||||
@@ -9,6 +10,9 @@ const rStore = useRecipeStore()
|
||||
const { curTool } = storeToRefs(rStore)
|
||||
const curStuff = computed(() => rStore.selectedStuff)
|
||||
|
||||
// 食物相克检测
|
||||
const { warningMessage, hasWarning, checkIncompatibility } = useIncompatibleFoods()
|
||||
|
||||
const recipeBtnRef = ref<HTMLButtonElement>()
|
||||
const { playAnimation } = useEmojiAnimation(recipeBtnRef)
|
||||
|
||||
@@ -17,6 +21,18 @@ const { proxy } = useScriptGoogleTagManager()
|
||||
const recipePanelRef = ref()
|
||||
const { isVisible, show } = useInvisibleElement(recipePanelRef)
|
||||
|
||||
// 监听食材变化,自动检测相克
|
||||
watch(curStuff, (newIngredients) => {
|
||||
checkIncompatibility(newIngredients)
|
||||
}, { deep: true })
|
||||
|
||||
// 页面初始化时也检查一次(处理已有选择的情况)
|
||||
onMounted(() => {
|
||||
if (curStuff.value.length > 0) {
|
||||
checkIncompatibility(curStuff.value)
|
||||
}
|
||||
})
|
||||
|
||||
function toggleStuff(item: StuffItem, category = '', _e?: Event) {
|
||||
rStore.toggleStuff(item.name)
|
||||
|
||||
@@ -41,6 +57,35 @@ function toggleStuff(item: StuffItem, category = '', _e?: Event) {
|
||||
<h2 m="t-4" text="xl" font="bold" p="1">
|
||||
🥘 先选一下食材
|
||||
</h2>
|
||||
|
||||
<!-- 食物相克警告提示 -->
|
||||
<Transition name="incompatible-warning">
|
||||
<div
|
||||
v-if="hasWarning"
|
||||
class="incompatible-warning-box"
|
||||
m="b-4" p="4"
|
||||
border="~ 2 red-300 dark:red-600 rounded-xl"
|
||||
text="red-800 dark:red-200 sm"
|
||||
shadow="lg"
|
||||
relative="~"
|
||||
overflow="hidden"
|
||||
>
|
||||
<div flex="~ items-start gap-3">
|
||||
<div text="2xl" flex="shrink-0" class="animate-pulse">
|
||||
🚨
|
||||
</div>
|
||||
<div flex="1 col gap-1">
|
||||
<div font="bold" text="base">
|
||||
食物相克警告!
|
||||
</div>
|
||||
<div leading="relaxed" whitespace="pre-line">
|
||||
{{ warningMessage }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<div>
|
||||
<h2 opacity="90" text="base" font="bold" p="1">
|
||||
🥬 菜菜们
|
||||
|
||||
88
app/composables/incompatible-foods.ts
Normal file
88
app/composables/incompatible-foods.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import type { IncompatibleRule } from '~/types'
|
||||
import { computed, onMounted, readonly, ref } from 'vue'
|
||||
import incompatibleFoodsData from '~/data/incompatible-foods.json'
|
||||
|
||||
/**
|
||||
* 食物相克检测 composable
|
||||
*/
|
||||
export function useIncompatibleFoods() {
|
||||
// 用于存储从 JSON 加载的相克规则
|
||||
const incompatibleRules = ref<IncompatibleRule[]>([])
|
||||
// 用于存储并显示给用户的警告信息
|
||||
const warningMessage = ref<string>('')
|
||||
// 加载状态
|
||||
const isLoading = ref(true)
|
||||
|
||||
/**
|
||||
* 在组件挂载后,加载食物相克数据
|
||||
*/
|
||||
onMounted(() => {
|
||||
try {
|
||||
// 直接使用导入的数据
|
||||
incompatibleRules.value = incompatibleFoodsData as IncompatibleRule[]
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Failed to load incompatible foods data:', error)
|
||||
}
|
||||
finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* 核心检测函数:检查当前选择的食材是否存在相克组合
|
||||
* @param ingredients - 当前已选的食材列表
|
||||
*/
|
||||
const checkIncompatibility = (ingredients: string[]) => {
|
||||
// 重置警告信息
|
||||
warningMessage.value = ''
|
||||
|
||||
// 如果食材少于2个或规则还没加载完成,无需检测
|
||||
if (ingredients.length < 2 || isLoading.value) {
|
||||
return
|
||||
}
|
||||
|
||||
const foundRules: IncompatibleRule[] = []
|
||||
|
||||
const ingredientSet = new Set(ingredients)
|
||||
|
||||
for (const rule of incompatibleRules.value) {
|
||||
// 检查规则中的两种食物是否都存在于我们的食材 Set 中
|
||||
if (ingredientSet.has(rule.foodA) && ingredientSet.has(rule.foodB)) {
|
||||
foundRules.push(rule)
|
||||
}
|
||||
}
|
||||
|
||||
// 如果找到相克组合,生成警告信息
|
||||
if (foundRules.length > 0) {
|
||||
if (foundRules.length === 1) {
|
||||
const rule = foundRules[0]!
|
||||
warningMessage.value
|
||||
= `🚨 危险组合!\n`
|
||||
+ `【${rule.foodA}】+ 【${rule.foodB}】= 有毒?!\n`
|
||||
+ `${rule.reason}\n`
|
||||
+ `换个搭配会更安全哦~`
|
||||
}
|
||||
else {
|
||||
const warnings = foundRules.map(rule =>
|
||||
`【${rule.foodA}】+ 【${rule.foodB}】(${rule.reason})\n`,
|
||||
).join('')
|
||||
warningMessage.value
|
||||
= `🚨 发现 ${foundRules.length} 个危险组合!\n`
|
||||
+ `${warnings}`
|
||||
+ `建议调整搭配哦~`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 计算属性:是否有警告信息
|
||||
const hasWarning = computed(() => Boolean(warningMessage.value))
|
||||
|
||||
return {
|
||||
incompatibleRules: readonly(incompatibleRules),
|
||||
warningMessage: readonly(warningMessage),
|
||||
hasWarning,
|
||||
isLoading: readonly(isLoading),
|
||||
checkIncompatibility,
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './incompatible-foods'
|
||||
export * from './store'
|
||||
|
||||
// others is auto exported
|
||||
|
||||
6
app/data/incompatible-foods.csv
Normal file
6
app/data/incompatible-foods.csv
Normal file
@@ -0,0 +1,6 @@
|
||||
foodA,foodB,reason
|
||||
番茄,黄瓜,黄瓜中含有维生素C分解酶,会破坏番茄中的维生素C,营养流失严重
|
||||
牛奶,韭菜,牛奶与韭菜同食会影响钙的吸收,降低营养价值
|
||||
土豆,番茄,土豆会产生大量的盐酸,番茄在较强的酸性环境中会产生不溶于水的沉淀
|
||||
白萝卜,胡萝卜,白萝卜中的维生素C会被胡萝卜中的抗坏血酸酵素破坏
|
||||
芹菜,黄瓜,芹菜中的维生素C会被黄瓜中的维生素C分解酶破坏
|
||||
|
File diff suppressed because one or more lines are too long
8
app/types/incompatible-foods.ts
Normal file
8
app/types/incompatible-foods.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* 食物相克规则
|
||||
*/
|
||||
export interface IncompatibleRule {
|
||||
foodA: string
|
||||
foodB: string
|
||||
reason: string
|
||||
}
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from './cookbook'
|
||||
export * from './incompatible-foods'
|
||||
export * from './recipe'
|
||||
|
||||
Reference in New Issue
Block a user