7 Commits

Author SHA1 Message Date
YunYouJun
2949c45839 chore: release v1.2.0 2023-11-07 02:00:11 +08:00
YunYouJun
81586158d5 chore: fix lint 2023-11-07 02:00:03 +08:00
YunYouJun
bfb6ea3e14 feat: add history 2023-11-07 01:59:29 +08:00
YunYouJun
7d26f9ba84 feat: add search keyword 2023-11-07 01:06:30 +08:00
YunYouJun
9f12401922 chore: release v1.1.9 2023-11-07 00:25:00 +08:00
YunYouJun
69c689df67 fix: curStuff watch & remove useless data 2023-11-07 00:20:18 +08:00
YunYouJun
8ca8c4aac8 fix: curStuff watch 2023-11-07 00:12:56 +08:00
15 changed files with 430 additions and 307 deletions

17
.vscode/settings.json vendored
View File

@@ -1,5 +1,20 @@
{
"cSpell.words": ["Vitesse", "Vite", "unocss", "vitest", "vueuse", "pinia", "demi", "antfu", "iconify", "intlify", "vitejs", "unplugin", "pnpm"],
"cSpell.words": [
"antfu",
"demi",
"iconify",
"intlify",
"nuxi",
"pinia",
"pnpm",
"unocss",
"unplugin",
"Vite",
"vitejs",
"Vitesse",
"vitest",
"vueuse"
],
"files.associations": {
"*.css": "postcss"
},

View File

@@ -0,0 +1,105 @@
<script setup lang="ts">
import { Dialog, DialogPanel, DialogTitle, TransitionChild, TransitionRoot } from '@headlessui/vue'
import { db } from '~/utils/db'
const isOpen = ref(false)
function closeModal() {
isOpen.value = false
}
function openModal() {
isOpen.value = true
}
const keyword = ref('')
async function getFilterRecipes(keyword: string) {
return db.recipes.filter((recipe) => {
return recipe.name.includes(keyword)
}).toArray()
}
const filteredRecipes = computedAsync(async () => {
return await getFilterRecipes(keyword.value)
})
</script>
<template>
<YlfIconButton
absolute right-3 top-5
class="icon-btn hover:text-yellow-400 !outline-none"
text-xl
title="切换" @click="openModal"
>
<div i="ri-search-line" />
</YlfIconButton>
<TransitionRoot appear :show="isOpen" as="template">
<Dialog as="div" class="relative z-10" @close="closeModal">
<TransitionChild
as="template"
enter="duration-300 ease-out"
enter-from="opacity-0"
enter-to="opacity-100"
leave="duration-200 ease-in"
leave-from="opacity-100"
leave-to="opacity-0"
>
<div class="fixed inset-0" />
</TransitionChild>
<div class="fixed inset-0 overflow-y-auto">
<div
class="h-full flex justify-center text-center"
>
<TransitionChild
as="template"
enter="duration-300 ease-out"
enter-from="opacity-0 scale-95"
enter-to="opacity-100 scale-100"
leave="duration-200 ease-in"
leave-from="opacity-100 scale-100"
leave-to="opacity-0 scale-95"
>
<DialogPanel
class="h-full max-w-xl w-full transform overflow-hidden bg-white p-4 text-left align-middle shadow-xl transition-all dark:bg-dark-600"
md="mt-4 rounded-2xl"
overflow="auto"
flex="~ col"
>
<DialogTitle
as="h3"
class="flex items-center justify-center text-lg font-medium leading-6"
>
<div relative inline-flex flex="grow">
<div
i-ri-search-line
class="absolute left-3 top-2 cursor-pointer text-gray-400"
/>
<input
v-model="keyword"
type="text"
class="w-full rounded-full bg-transparent text-sm focus:outline-none focus:ring-1 focus:ring-blue-500 placeholder-gray-400"
border="~ rounded-full gray-300 op-50 focus:border-blue-500"
placeholder="搜索菜谱"
autofocus py-2 pl-10 pr-3
>
<div
v-if="keyword" i-ri-close-line
class="absolute right-3 top-2 cursor-pointer text-gray-400"
@click="keyword = ''"
/>
</div>
<div ml-2 inline-flex op="70" text-base @click="closeModal">
取消
</div>
</DialogTitle>
<div flex="~ col grow" overflow="auto" class="mt-2" text-xs>
<DishTag v-for="item, i in filteredRecipes" :key="i" :dish="item" />
</div>
</DialogPanel>
</TransitionChild>
</div>
</div>
</Dialog>
</TransitionRoot>
</template>

View File

@@ -3,6 +3,7 @@ import type { DbRecipeItem } from '~/utils/db'
import { tools } from '~/data/food'
import type { RecipeItem } from '~/types'
import { getEmojisFromStuff } from '~/utils'
import { recipeHistories } from '~/composables/store/history'
const props = defineProps<{
dish: RecipeItem | DbRecipeItem
@@ -10,16 +11,21 @@ const props = defineProps<{
const gtm = useGtm()
function triggerGtm(val: string) {
function triggerGtm(dish: RecipeItem) {
recipeHistories.value.push({
recipe: dish,
time: Date.now(),
})
gtm?.trackEvent({
event: 'click',
category: `dish_${val}`,
category: `dish_${dish.name}`,
action: 'click_recipe',
label: '跳转菜谱',
})
gtm?.trackEvent({
event: 'click_dish',
action: val,
action: dish.name,
})
}
@@ -34,7 +40,7 @@ const dishLabel = computed(() => {
:href="dish.link || `https://www.bilibili.com/video/${dish.bv}`" target="_blank" class="dish-tag rounded tag" p="x-2"
border="~ blue-200 dark:blue-800"
bg="blue-300 opacity-20"
@click="triggerGtm(dish.name)"
@click="triggerGtm(dish)"
>
<span m="r-1" class="inline-flex items-center justify-center" text="sm blue-700 dark:blue-200">
{{ dishLabel }}

View File

@@ -16,11 +16,11 @@ defineProps<{
:is="to ? NuxtLink : 'div'"
:to="to"
class="ylf-form-item"
w-full flex cursor-pointer items-center justify-between p-3
w-full flex cursor-pointer items-center justify-between p-2
hover:bg-gray-100
dark:hover:bg-dark-400
>
<div v-if="label" class="text-md" inline-flex items-center justify-center>
<div v-if="label" class="text-sm" inline-flex items-center justify-center>
<div v-if="icon" :class="icon" mr-2 inline-flex />
<span>{{ label }}</span>
</div>

View File

@@ -0,0 +1,22 @@
<script lang="ts" setup>
defineProps<{
icon: string
label: string
to: string
}>()
</script>
<template>
<NuxtLink
:to="to"
flex="~ col"
border="~ solid dark:$ylf-c-border"
bg="$ylf-c-bg-alt"
class="inline-flex items-center justify-center rounded-md px-4 py-2 text-sm font-medium decoration-none hover-bg-gray-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-blue-500 dark:hover:bg-dark-400"
>
<div :class="icon" inline-flex text-lg />
<div mt-2 inline-flex text-xs>
{{ label }}
</div>
</NuxtLink>
</template>

View File

@@ -0,0 +1,10 @@
import { useStorage } from '@vueuse/core'
import { namespace } from '~/constants'
import type { RecipeItem } from '~/types'
export interface RecipeHistoryItem {
recipe: RecipeItem
time: number
}
export const recipeHistories = useStorage<RecipeHistoryItem[]>(`${namespace}:history`, [])

View File

@@ -122,8 +122,9 @@ export const useRecipeStore = defineStore('recipe', () => {
// 默认严格模式
const displayedRecipe = ref<RecipeItem[]>([])
watch([keyword, curStuff, curTool, curMode], async () => {
displayedRecipe.value = await searchRecipes()
// fix curStuff watch
watch(() => [keyword.value, selectedStuff.value, curTool.value, curMode.value], async () => {
displayedRecipe.value = [...(await searchRecipes())]
})
/**

View File

@@ -217,7 +217,6 @@ BBQ烟熏手撕猪肉,猪肉,BV1DV411x7SH,复杂,,烤,烤箱,
微波炉版照烧鸡腿饭,米、鸡肉、胡萝卜、花菜,BV16Z4y1V774,,,,微波炉,
微波炉版蒸蛋羹,鸡蛋,BV19T4y1D7Zd,,,,微波炉,
微波炉版煮米饭,,BV193411W7nb,,,,微波炉,
豉油鸡翅,鸡肉,BV1vz4y1d775,普通,茶餐厅,,一口大锅,
水煮肉片,猪肉、芹菜、莴笋,BV1ZZ4y1379N,普通,川菜,,一口大锅,
脆口黄瓜,黄瓜,BV1Tb4y1X7ow,简单,脆口,凉拌,一口大锅,
白萝卜汤,白萝卜,BV1HJ411L7xA,简单,单一食材,,一口大锅,
1 name stuff bv difficulty tags methods tools
217 微波炉版照烧鸡腿饭 米、鸡肉、胡萝卜、花菜 BV16Z4y1V774 微波炉
218 微波炉版蒸蛋羹 鸡蛋 BV19T4y1D7Zd 微波炉
219 微波炉版煮米饭 BV193411W7nb 微波炉
豉油鸡翅 鸡肉 BV1vz4y1d775 普通 茶餐厅 一口大锅
220 水煮肉片 猪肉、芹菜、莴笋 BV1ZZ4y1379N 普通 川菜 一口大锅
221 脆口黄瓜 黄瓜 BV1Tb4y1X7ow 简单 脆口 凉拌 一口大锅
222 白萝卜汤 白萝卜 BV1HJ411L7xA 简单 单一食材 一口大锅

View File

@@ -10,7 +10,7 @@ const route = useRoute()
<main class="text-center text-gray-700 dark:text-gray-200" p="t-5 b-15">
<div flex items-center justify-between>
<BackBtn ml-3 />
<h2 flex items-center justify-center text-lg>
<h2 flex items-center justify-center text-lg font="bold">
{{ route.meta.title }}
</h2>
<DarkToggle mr-3 />

View File

@@ -1,7 +1,8 @@
<template>
<main class="cook-main text-center text-gray-700 dark:text-gray-200" p="t-8 b-$cook-bottom-menu-height">
<slot />
<DarkToggle absolute right-3 top-5 />
<DarkToggle absolute left-3 top-5 />
<SearchRecipe />
<TheBottomMenu fixed bottom-0 left-0 right-0 />
</main>
</template>

View File

@@ -1,6 +1,6 @@
{
"type": "module",
"version": "1.1.8",
"version": "1.2.0",
"private": true,
"packageManager": "pnpm@8.10.2",
"engines": {
@@ -8,6 +8,7 @@
},
"scripts": {
"build": "npm run convert && cross-env VITE_APP_BUILD_TIME=$(date +%s) nuxi build",
"build:static": "npm run convert && cross-env VITE_APP_BUILD_TIME=$(date +%s) nuxi generate",
"convert": "tsx scripts/convert.ts",
"dev": "cross-env VITE_APP_BUILD_TIME=$(date +%s) nuxi dev --host",
"dev:pwa": "VITE_PLUGIN_PWA=true nuxi dev",
@@ -22,6 +23,7 @@
"typecheck": "vue-tsc --noEmit"
},
"dependencies": {
"dayjs": "^1.11.10",
"vue-about-me": "^1.2.7"
},
"devDependencies": {
@@ -52,8 +54,8 @@
"fake-indexeddb": "^5.0.1",
"happy-dom": "^12.10.3",
"jsdom": "^22.1.0",
"nuxt": "^3.8.0",
"nuxt-vitest": "^0.11.2",
"nuxt": "^3.8.1",
"nuxt-vitest": "^0.11.3",
"pinia": "^2.1.7",
"sass": "^1.69.5",
"star-markdown-css": "^0.4.2",

12
pages/recipes/collect.vue Normal file
View File

@@ -0,0 +1,12 @@
<script lang="ts" setup>
definePageMeta({
layout: 'child',
title: '我的收藏',
})
</script>
<template>
<div>
施工中...
</div>
</template>

38
pages/recipes/history.vue Normal file
View File

@@ -0,0 +1,38 @@
<script lang="ts" setup>
import dayjs from 'dayjs'
import { recipeHistories } from '~/composables/store/history'
definePageMeta({
layout: 'child',
title: '历史记录',
})
// todo
// clear one history
function clearAllHistory() {
recipeHistories.value = []
}
</script>
<template>
<div pt-2>
<div
text="blue-900 dark:blue-200"
bg="blue-300 op-20 hover:(blue-800 op-20) dark:hover:(blue-200 op-20)"
class="inline-flex items-center justify-center border border-transparent rounded-md px-4 py-2 text-sm font-medium focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-blue-500"
@click="clearAllHistory"
>
<div i-ri-eraser-line />
<span class="ml-1">清空记录</span>
</div>
<div flex="~ col">
<div v-for="history in recipeHistories" :key="history.recipe.name" mt-2>
<StapleTag :active="false">
{{ dayjs(history.time).format('YYYY-MM-DD HH:mm:ss') }}
</StapleTag>
<DishTag :dish="history.recipe" />
</div>
</div>
</div>
</template>

View File

@@ -8,6 +8,12 @@ import { links } from '~/constants'
我的
</CommonHeader>
<div mt-4 gap="3" grid="~ cols-3" px-2>
<YlfIconItem to="/recipes/history" icon="i-ri-history-line" label="历史记录" />
<YlfIconItem to="/recipes/collect" icon="i-ri-star-line" label="我的收藏" />
<YlfIconItem to="/cookbooks" icon="i-ri-article-line" label="自定义菜谱" />
</div>
<div
class="mx-auto max-w-md w-full"
px-2
@@ -22,9 +28,9 @@ import { links } from '~/constants'
<YlfFormItem icon="i-ri-settings-line" label="设置" to="/settings" />
</YlfForm>
<YlfForm>
<YlfFormItem icon="i-ri-article-line" label="自定义菜谱" to="/cookbooks/" />
</YlfForm>
<!-- <YlfForm>
<YlfFormItem icon="i-ri-article-line" label="自定义菜谱 TODO" to="/cookbooks/" />
</YlfForm> -->
<YlfForm>
<YlfFormItem icon="i-ri-question-line" label="帮助" to="/help" />

484
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff