28 Commits

Author SHA1 Message Date
YunYouJun
37bab9269c chore: release v1.2.1 2023-11-11 19:53:07 +08:00
YunYouJun
44f9b40ee5 feat: add 麻婆豆腐, close #55 2023-11-11 19:51:51 +08:00
YunYouJun
4ddd5ac2d2 chore: fix data updated time 2023-11-11 19:46:44 +08:00
YunYouJun
9df47e977e chore: fix 可乐饼 link 2023-11-11 19:43:43 +08:00
KazariEX
5fb76f24dc fix: keyword filter (#57) 2023-11-07 15:33:16 +08:00
YunYouJun
014f129b8e chore: fix style details cursor size 2023-11-07 02:18:49 +08:00
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
YunYouJun
5ab297cc47 chore: release v1.1.8 2023-11-06 14:34:40 +08:00
YunYouJun
7f87c47320 fix: icon padding bottom & remove useless recipe 2023-11-06 14:34:19 +08:00
YunYouJun
dd2de09325 ci: add check for dev 2023-11-06 02:30:08 +08:00
YunYouJun
e55d9a59c3 chore: fix lint 2023-11-06 02:28:14 +08:00
YunYouJun
0232595083 test: fix nuxt vitest 2023-11-06 02:26:45 +08:00
YunYouJun
d3ab8fb2c1 fix: add css var safe padding bottom 2023-11-06 02:00:56 +08:00
YunYouJun
85c8f4fcd1 chore: update simple copyright 2023-11-06 01:46:37 +08:00
YunYouJun
f2f878e2f4 chore: add simple copyright 2023-11-06 01:19:43 +08:00
YunYouJun
246a65599a fix: lint & add no scalable 2023-11-06 00:36:09 +08:00
YunYouJun
203ab27496 chore: upgrade deps & fix lint 2023-11-05 21:57:05 +08:00
YunYouJun
e861c5bd65 chore: fix typecheck 2023-08-20 05:02:20 +08:00
YunYouJun
94a9ff5007 feat: show custom cookbook 2023-08-20 04:59:14 +08:00
YunYouJun
98123b0039 feat: add settings page 2023-08-20 02:28:00 +08:00
YunYouJun
95ce2c4dac chore: fix readme & title 2023-08-03 23:19:37 +08:00
YunYouJun
1bd0a6721e chore: release before upload 2023-07-30 21:23:14 +08:00
73 changed files with 4598 additions and 3668 deletions

View File

@@ -1,2 +0,0 @@
dist
public

View File

@@ -1,7 +0,0 @@
{
"extends": [
"@antfu",
"@unocss"
],
"ignorePatterns": "*.json"
}

View File

@@ -4,10 +4,12 @@ on:
push: push:
branches: branches:
- main - main
- dev
pull_request: pull_request:
branches: branches:
- main - main
- dev
jobs: jobs:
lint: lint:

View File

@@ -28,10 +28,6 @@ jobs:
run: pnpm install run: pnpm install
- run: npm run generate --if-present - run: npm run generate --if-present
- uses: actions/upload-artifact@v3
with:
name: Cook Dist
path: .output/public/
- name: Release - name: Release
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v1
@@ -39,6 +35,11 @@ jobs:
with: with:
files: .output/public/ files: .output/public/
- uses: actions/upload-artifact@v3
with:
name: Cook Dist
path: .output/public/
- run: npx changelogithub # or changelogithub@0.12 if ensure the stable result - run: npx changelogithub # or changelogithub@0.12 if ensure the stable result
env: env:
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}

3
.gitignore vendored
View File

@@ -1,5 +1,5 @@
# auto generate # auto generate
src/data/recipe.json data/recipe.json
.DS_Store .DS_Store
.vite-ssg-dist .vite-ssg-dist
@@ -7,6 +7,7 @@ src/data/recipe.json
*.local *.local
esbuild-kit esbuild-kit
tsx-*
# nuxt # nuxt
node_modules node_modules

85
.vscode/settings.json vendored
View File

@@ -1,11 +1,84 @@
{ {
"cSpell.words": ["Vitesse", "Vite", "unocss", "vitest", "vueuse", "pinia", "demi", "antfu", "iconify", "intlify", "vitejs", "unplugin", "pnpm"], "cSpell.words": [
"prettier.enable": false, "antfu",
"editor.codeActionsOnSave": { "demi",
"source.fixAll.eslint": true, "iconify",
}, "intlify",
"nuxi",
"pinia",
"pnpm",
"unocss",
"unplugin",
"Vite",
"vitejs",
"Vitesse",
"vitest",
"vueuse"
],
"files.associations": { "files.associations": {
"*.css": "postcss", "*.css": "postcss"
}, },
// Enable the ESlint flat config support
"eslint.experimental.useFlatConfig": true,
// Disable the default formatter, use eslint instead
"prettier.enable": false,
"editor.formatOnSave": false, "editor.formatOnSave": false,
// Auto fix
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit",
"source.organizeImports": "never"
},
// Silent the stylistic rules in you IDE, but still auto fix them
"eslint.rules.customizations": [
{
"rule": "style/*",
"severity": "off"
},
{
"rule": "*-indent",
"severity": "off"
},
{
"rule": "*-spacing",
"severity": "off"
},
{
"rule": "*-spaces",
"severity": "off"
},
{
"rule": "*-order",
"severity": "off"
},
{
"rule": "*-dangle",
"severity": "off"
},
{
"rule": "*-newline",
"severity": "off"
},
{
"rule": "*quotes",
"severity": "off"
},
{
"rule": "*semi",
"severity": "off"
}
],
// Enable eslint for all supported languages
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"vue",
"html",
"markdown",
"json",
"jsonc",
"yaml"
]
} }

View File

@@ -1,6 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import { isClient } from '@vueuse/core' import { isClient } from '@vueuse/core'
import pkg from '~/package.json'
const displayICP = ref(true) const displayICP = ref(true)
@@ -8,24 +7,11 @@ onBeforeMount(() => {
if (isClient) if (isClient)
displayICP.value = ['cook.yunyoujun.cn', 'localhost', '127.0.0.1'].includes(window.location.hostname) displayICP.value = ['cook.yunyoujun.cn', 'localhost', '127.0.0.1'].includes(window.location.hostname)
}) })
const commitSha = (import.meta.env.VITE_COMMIT_REF || '').slice(0, 7)
const now = import.meta.env.VITE_APP_BUILD_TIME
const buildDate = (new Date(Number.parseInt(now) * 1000)).toLocaleDateString()
</script> </script>
<template> <template>
<div p="4 t-2" class="flex flex-col items-center justify-center" text="sm"> <div p="4 t-2" class="flex flex-col items-center justify-center" text="sm">
<div v-if="commitSha && buildDate" mb-2> <CurrentVersion />
<span>
当前版本 v{{ pkg.version }}{{ buildDate }}:
</span>
<span>
<a border="b-1 dashed" :href="`https://github.com/YunYouJun/cook/commit/${commitSha}`" target="_blank" alt="Cook | GitHub Commit">
{{ commitSha }}
</a>
</span>
</div>
<a v-if="displayICP" opacity="80" class="flex" href="https://beian.miit.gov.cn/" target="_blank"> <a v-if="displayICP" opacity="80" class="flex" href="https://beian.miit.gov.cn/" target="_blank">
苏ICP备17038157号 苏ICP备17038157号
</a> </a>

View File

@@ -18,7 +18,8 @@ const showBasketBtn = computed(async () => {
v-show="showBasketBtn" v-show="showBasketBtn"
class="fixed z-9 inline-flex cursor-pointer items-center justify-center rounded rounded-full shadow hover:shadow-md" class="fixed z-9 inline-flex cursor-pointer items-center justify-center rounded rounded-full shadow hover:shadow-md"
bg="green-50 dark:green-900" w="10" h="10" bg="green-50 dark:green-900" w="10" h="10"
bottom="18" right="4" bottom="22"
right="4"
text="green-600 dark:green-300" text="green-600 dark:green-300"
> >
<span v-if="displayedRecipe.length > 0"> <span v-if="displayedRecipe.length > 0">

View File

@@ -1,6 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import { storeToRefs } from 'pinia' import { storeToRefs } from 'pinia'
import type { StuffItem } from '~/data/food' import type { StuffItem } from '~/types'
import { meat, staple, tools, vegetable } from '~/data/food' import { meat, staple, tools, vegetable } from '~/data/food'
import { useEmojiAnimation } from '~/composables/animation' import { useEmojiAnimation } from '~/composables/animation'

View File

@@ -3,11 +3,12 @@ import { Disclosure, DisclosureButton, DisclosurePanel } from '@headlessui/vue'
defineProps<{ defineProps<{
title: string title: string
defaultOpen?: boolean
}>() }>()
</script> </script>
<template> <template>
<Disclosure v-slot="{ open }" as="div" class="mt-2"> <Disclosure v-slot="{ open }" :default-open="defaultOpen" as="div" class="mt-2">
<DisclosureButton <DisclosureButton
class="w-full flex justify-between rounded-lg bg-blue-100 px-4 py-2 text-left text-sm font-medium text-blue-900 hover:bg-blue-200 focus:outline-none focus-visible:ring focus-visible:ring-blue-500 focus-visible:ring-opacity-75" class="w-full flex justify-between rounded-lg bg-blue-100 px-4 py-2 text-left text-sm font-medium text-blue-900 hover:bg-blue-200 focus:outline-none focus-visible:ring focus-visible:ring-blue-500 focus-visible:ring-opacity-75"
> >

View File

@@ -29,37 +29,35 @@ const showTooltip = computed(() => !selectedStuff.value.length && !curTool.value
<SearchFoodInput v-if="showSearchInput" /> <SearchFoodInput v-if="showSearchInput" />
<Transition mode="out-in"> <Transition mode="out-in">
<div class="cook-filter-recipes"> <span v-if="showTooltip" text="sm" p="2">
<span v-if="showTooltip" text="sm" p="2"> 你要先选食材或工具哦
你要先选食材或工具哦 </span>
</span>
<div <div
v-else-if="rStore.isSearching" v-else-if="rStore.isSearching"
relative flex items-center justify-center p-6 relative flex items-center justify-center p-6
text-xl text-xl
> >
<div class="magnifying-glass" i-ri-search-line inline-flex /> <div class="magnifying-glass" i-ri-search-line inline-flex />
</div> </div>
<div v-else-if="rStore.displayedRecipe.length"> <div v-else-if="rStore.displayedRecipe.length">
<DishTag v-for="item, i in rStore.displayedRecipe" :key="i" :dish="item" /> <DishTag v-for="item, i in rStore.displayedRecipe" :key="i" :dish="item" />
</div> </div>
<div v-else text="sm"> <div v-else text="sm">
<span>还没有完美匹配的菜谱呢</span> <span>还没有完美匹配的菜谱呢</span>
<br> <br>
<span>大胆尝试一下或者</span> <span>大胆尝试一下或者</span>
<a href="#" @click="rStore.reset()"> <a href="#" @click="rStore.reset()">
<strong>换个组合</strong> <strong>换个组合</strong>
</a> </a>
<span></span> <span></span>
<br> <br>
<div m="t-1"> <div m="t-1">
<span>欢迎来</span> <span>欢迎来</span>
<a class="font-bold text-blue-600 dark:text-blue-400" href="https://docs.qq.com/sheet/DQk1vdkhFV0twQVNS?tab=uykkic" target="_blank">这里</a> <a class="font-bold text-blue-600 dark:text-blue-400" href="https://docs.qq.com/sheet/DQk1vdkhFV0twQVNS?tab=uykkic" target="_blank">这里</a>
<span>反馈新的菜谱</span> <span>反馈新的菜谱</span>
</div>
</div> </div>
</div> </div>
</Transition> </Transition>

View File

@@ -1,6 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { BottomMenuItem } from '@yunlefun/vue' import type { BottomMenuItem } from '@yunlefun/vue'
import { ref } from 'vue'
const items: BottomMenuItem[] = [ const items: BottomMenuItem[] = [
{ {
@@ -38,20 +37,20 @@ const items: BottomMenuItem[] = [
const route = useRoute() const route = useRoute()
const router = useRouter() const router = useRouter()
const active = ref(route.path)
function onClick(item: BottomMenuItem) { function onClick(item: BottomMenuItem) {
active.value = item.to || '' // router.push(item.to || '/')
router.push(item.to || '/') router.replace(item.to || '/')
} }
</script> </script>
<template> <template>
<YlfBottomMenu shadow-2xl> <YlfBottomMenu shadow-2xl pb="$cook-bottom-menu-padding-bottom">
<YlfBottomMenuItem <YlfBottomMenuItem
v-for="item in items" v-for="item in items"
:key="item.to" :key="item.to"
:item="item" :item="item"
:active="active === item.to" :active="route.path === item.to"
class="pt-3"
@click="onClick" @click="onClick"
/> />
</YlfBottomMenu> </YlfBottomMenu>

View File

@@ -0,0 +1,13 @@
<script lang="ts" setup>
const router = useRouter()
function back() {
router.back()
}
</script>
<template>
<YlfIconButton
icon="i-ri-arrow-left-s-line"
@click="back"
/>
</template>

View File

@@ -15,7 +15,11 @@ function toggleDark() {
</script> </script>
<template> <template>
<button class="mx-2 icon-btn hover:text-yellow-400 !outline-none" title="切换" @click="toggleDark()"> <YlfIconButton
class="icon-btn hover:text-yellow-400 !outline-none"
text-xl
title="切换" @click="toggleDark()"
>
<div i="ri-sun-line dark:ri-moon-line" /> <div i="ri-sun-line dark:ri-moon-line" />
</button> </YlfIconButton>
</template> </template>

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 bg-black/10" />
</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="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 op="70" ml-2 inline-flex cursor-pointer 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

@@ -1,18 +1,18 @@
<template> <template>
<div> <div>
<NuxtLink class="mx-2 icon-btn hover:text-orange-400" to="/help" title="帮助"> <NuxtLink class="icon-btn mx-2 hover:text-orange-400" to="/help" title="帮助">
<div i-ri-question-line /> <div i-ri-question-line />
</NuxtLink> </NuxtLink>
<NuxtLink class="mx-2 icon-btn hover:text-blue-400" to="/about" title="关于"> <NuxtLink class="icon-btn mx-2 hover:text-blue-400" to="/about" title="关于">
<div i-ri-information-line /> <div i-ri-information-line />
</NuxtLink> </NuxtLink>
<a class="mx-2 icon-btn hover:text-pink-400" rel="noreferrer" href="https://space.bilibili.com/1579790" target="_blank" title="BiliBili"> <a class="icon-btn mx-2 hover:text-pink-400" rel="noreferrer" href="https://space.bilibili.com/1579790" target="_blank" title="BiliBili">
<div i-ri-bilibili-line /> <div i-ri-bilibili-line />
</a> </a>
<a class="hover:text-black-400 mx-2 icon-btn" rel="noreferrer" href="https://github.com/YunYouJun/cook" target="_blank" title="GitHub"> <a class="icon-btn hover:text-black-400 mx-2" rel="noreferrer" href="https://github.com/YunYouJun/cook" target="_blank" title="GitHub">
<div i-ri-github-line /> <div i-ri-github-line />
</a> </a>
</div> </div>

View File

@@ -0,0 +1,31 @@
<script lang="ts" setup>
import type { Cookbook } from '~/types'
defineProps<{
cookbook: Cookbook
}>()
const showDetail = ref(false)
</script>
<template>
<button
class="bg-$c-bg-alt"
h-36 w-full inline-flex cursor-pointer items-center justify-center shadow
@click="showDetail = true"
>
<slot />
</button>
<CookbookDetail
v-if="showDetail"
absolute bottom-17 left-2 right-2 top-2 z-1 overflow-hidden shadow
:cookbook="cookbook"
>
<YlfIconButton
icon="i-ri-close-line"
class="absolute right-2 top-2"
@click="showDetail = false"
/>
</CookbookDetail>
</template>

View File

@@ -0,0 +1,27 @@
<script lang="ts" setup>
import type { Cookbook } from '~/types'
const props = defineProps<{
cookbook: Cookbook
}>()
const recipes = ref<Cookbook['recipes']>(props.cookbook.recipes)
onMounted(async () => {
recipes.value = ((await import('../../data/recipe.json')).default) as unknown as Cookbook['recipes']
})
</script>
<template>
<div class="bg-$c-bg-alt" flex="~ col">
<h3 mt-4 font-bold>
{{ cookbook.title }}
</h3>
<sub op="90" my-3>
{{ cookbook.description }}
</sub>
<div mx-auto mt-2 p-0 border="1px" overflow-y="scroll">
<RecipeTable h="full" :recipes="recipes" />
</div>
<slot />
</div>
</template>

View File

@@ -0,0 +1,18 @@
<script lang="ts" setup>
definePageMeta({
layout: 'child',
title: '新建食谱书',
})
</script>
<template>
<NuxtLink
class="bg-$c-bg-alt"
h-36 w-full inline-flex cursor-pointer items-center justify-center shadow
to="/cookbooks/new"
>
<slot>
<div i-ri-add-line />
</slot>
</NuxtLink>
</template>

View File

@@ -0,0 +1,20 @@
<script lang="ts" setup>
import pkg from '~/package.json'
const commitSha = (import.meta.env.VITE_COMMIT_REF || '').slice(0, 7)
const now = import.meta.env.VITE_APP_BUILD_TIME
const buildDate = (new Date(Number.parseInt(now) * 1000)).toLocaleDateString()
</script>
<template>
<div v-if="commitSha && buildDate" mb-2 text-sm>
<span>
当前版本 v{{ pkg.version }}{{ buildDate }}:
</span>
<span>
<a border="b-1 dashed" :href="`https://github.com/YunYouJun/cook/commit/${commitSha}`" target="_blank" alt="Cook | GitHub Commit">
{{ commitSha }}
</a>
</span>
</div>
</template>

View File

@@ -0,0 +1,22 @@
<template>
<div text="center sm" my-3>
<CurrentVersion />
<div flex="~" items-center justify-center gap="2">
<a
href="https://github.com/YunYouJun/cook" target="_blank"
class="inline-flex items-center justify-center"
>
<div i-ri-github-line mr-1 />
<span>Code</span>
</a>
by
<a
href="https://www.bilibili.com/opus/649847454294868008" target="_blank"
class="inline-flex items-center justify-center"
>
<div i-ri-bilibili-line mr-1 class="text-pink-400" />
<span>云游君</span>
</a>
</div>
</div>
</template>

View File

@@ -0,0 +1,46 @@
<script lang="ts" setup>
import type { Recipes } from '~/types'
defineProps<{
recipes: Recipes
}>()
</script>
<template>
<table
class="recipe-table bg-$c-bg"
overflow="auto" h="full"
>
<thead>
<tr>
<th />
<th>
名称
</th>
<th>
工具
</th>
<th>
材料
</th>
</tr>
</thead>
<tbody>
<RecipeTableItem
v-for="(recipe, i) in recipes"
:key="recipe.name"
:index="i" :recipe="recipe"
/>
</tbody>
</table>
</template>
<style lang="scss">
.recipe-table {
font-size: 0.8rem;
}
tr, th, td {
border: 1px solid black;
}
</style>

View File

@@ -0,0 +1,31 @@
<script lang="ts" setup>
import type { RecipeItem } from '~/types'
defineProps<{
index: number
recipe: RecipeItem
}>()
</script>
<template>
<tr>
<td>
{{ index }}
</td>
<td>
<a
class="text-blue-500" font-bold
:href="recipe.link || `https://www.bilibili.com/video/${recipe.bv}`"
target="_blank"
>
{{ recipe.name }}
</a>
</td>
<td>
{{ recipe.tools.join('、') }}
</td>
<td>
{{ recipe.stuff.join('、') }}
</td>
</tr>
</template>

View File

@@ -1,8 +1,9 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { DbRecipeItem } from 'utils/db' import type { DbRecipeItem } from '~/utils/db'
import { tools } from '~/data/food' import { tools } from '~/data/food'
import type { RecipeItem } from '~/types' import type { RecipeItem } from '~/types'
import { getEmojisFromStuff } from '~/utils' import { getEmojisFromStuff } from '~/utils'
import { recipeHistories } from '~/composables/store/history'
const props = defineProps<{ const props = defineProps<{
dish: RecipeItem | DbRecipeItem dish: RecipeItem | DbRecipeItem
@@ -10,16 +11,21 @@ const props = defineProps<{
const gtm = useGtm() const gtm = useGtm()
function triggerGtm(val: string) { function triggerGtm(dish: RecipeItem) {
recipeHistories.value.push({
recipe: dish,
time: Date.now(),
})
gtm?.trackEvent({ gtm?.trackEvent({
event: 'click', event: 'click',
category: `dish_${val}`, category: `dish_${dish.name}`,
action: 'click_recipe', action: 'click_recipe',
label: '跳转菜谱', label: '跳转菜谱',
}) })
gtm?.trackEvent({ gtm?.trackEvent({
event: 'click_dish', 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" :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" border="~ blue-200 dark:blue-800"
bg="blue-300 opacity-20" 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"> <span m="r-1" class="inline-flex items-center justify-center" text="sm blue-700 dark:blue-200">
{{ dishLabel }} {{ dishLabel }}

View File

@@ -0,0 +1,24 @@
<template>
<div class="ylf-form" flex="~ col" rounded-md>
<slot />
</div>
</template>
<style lang="scss">
.ylf-form {
background-color: var(--ylf-c-bg-alt);
border: 1px solid var(--ylf-c-border);
margin: 10px 0;
overflow: hidden;
.ylf-form-item {
border-bottom: 1px solid var(--ylf-c-border);
&:last-child {
border-bottom: none;
}
}
}
</style>

View File

@@ -0,0 +1,33 @@
<script lang="ts" setup>
import { NuxtLink } from '#components'
defineProps<{
icon?: string
label?: string
/**
* Router link
*/
to?: string
}>()
</script>
<template>
<component
:is="to ? NuxtLink : 'div'"
:to="to"
class="ylf-form-item"
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-sm" inline-flex items-center justify-center>
<div v-if="icon" :class="icon" mr-2 inline-flex />
<span>{{ label }}</span>
</div>
<div inline-flex>
<slot>
<div v-if="to" i-ri-arrow-right-s-line />
</slot>
</div>
</component>
</template>

View File

@@ -0,0 +1,16 @@
<script lang="ts" setup>
defineProps<{
icon?: string
}>()
</script>
<template>
<button
class="ylf-icon-button hover:(bg-blue-300 bg-opacity-20)"
h-10 w-10 inline-flex items-center justify-center rounded-full
>
<slot>
<div v-if="icon" text-xl :class="icon" />
</slot>
</button>
</template>

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,28 @@
<script lang="ts" setup>
import { Switch } from '@headlessui/vue'
defineProps<{
modelValue: boolean
}>()
const emit = defineEmits(['update:modelValue'])
function updateModelValue(value: boolean) {
emit('update:modelValue', value)
}
</script>
<template>
<Switch
:model-value="modelValue"
:class="modelValue ? 'bg-blue-600' : 'bg-gray'"
class="relative h-6 w-11 inline-flex shrink-0 cursor-pointer border-2 border-transparent rounded-full transition-colors duration-200 ease-in-out focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75"
@update:model-value="updateModelValue"
>
<span class="sr-only">Use setting</span>
<span
aria-hidden="true"
:class="modelValue ? 'translate-x-5' : 'translate-x-0'"
class="pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow-lg ring-0 transition duration-200 ease-in-out"
/>
</Switch>
</template>

View File

@@ -4,7 +4,6 @@ import { isClient, useElementBounding } from '@vueuse/core'
/** /**
* trigger show invisible element * trigger show invisible element
* @param target * @param target
* @returns
*/ */
export function useInvisibleElement(target: MaybeComputedElementRef<HTMLElement>) { export function useInvisibleElement(target: MaybeComputedElementRef<HTMLElement>) {
const { top } = useElementBounding(target) const { top } = useElementBounding(target)

View File

@@ -1,9 +1,8 @@
import type { DbRecipeItem } from 'utils/db' import type { DbRecipeItem } from '~/utils/db'
/** /**
* 随机几道菜 * 随机几道菜
* @param total * @param total
* @returns
*/ */
export function useRandomRecipe(total: Ref<number>) { export function useRandomRecipe(total: Ref<number>) {
const randomRecipes = ref<(DbRecipeItem | undefined)[]>([]) const randomRecipes = ref<(DbRecipeItem | undefined)[]>([])

View File

@@ -1,10 +1,17 @@
import { acceptHMRUpdate, defineStore } from 'pinia' import { acceptHMRUpdate, defineStore } from 'pinia'
import { useStorage } from '@vueuse/core'
import { ref } from 'vue'
import { defaultSettings } from '~/utils/settings'
import { namespace } from '~/constants'
export const useAppStore = defineStore('app', () => { export const useAppStore = defineStore('app', () => {
const deferredPrompt = ref<Event | any>() const deferredPrompt = ref<Event | any>()
const settings = useStorage(`${namespace}:settings`, defaultSettings)
return { return {
deferredPrompt, deferredPrompt,
settings,
} }
}) })

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

@@ -2,9 +2,9 @@ import { acceptHMRUpdate, defineStore } from 'pinia'
import { useStorage } from '@vueuse/core' import { useStorage } from '@vueuse/core'
import { computed, onMounted, ref, watch } from 'vue' import { computed, onMounted, ref, watch } from 'vue'
import { useGtm } from '@gtm-support/vue-gtm' import { useGtm } from '@gtm-support/vue-gtm'
import type { RecipeItem } from 'types'
import type { StuffItem } from '../../data/food'
import { db } from '../../utils/db' import { db } from '../../utils/db'
import { useAppStore } from './app'
import type { RecipeItem, StuffItem } from '~/types'
const namespace = 'cook' const namespace = 'cook'
@@ -17,6 +17,7 @@ export type SearchMode = 'survival' | 'loose' | 'strict'
export const useRecipeStore = defineStore('recipe', () => { export const useRecipeStore = defineStore('recipe', () => {
const gtm = useGtm() const gtm = useGtm()
const { settings } = useAppStore()
/** /**
* 搜索关键字 * 搜索关键字
@@ -24,10 +25,10 @@ export const useRecipeStore = defineStore('recipe', () => {
const keyword = ref('') const keyword = ref('')
// can not exported // can not exported
const curStuff = useStorage(`${namespace}:stuff`, new Set<string>()) const curStuff = settings.keepLocalData ? useStorage(`${namespace}:stuff`, new Set<string>()) : ref(new Set<string>())
// const curTools = ref(new Set<string>()) // const curTools = ref(new Set<string>())
const curTool = useStorage(`${namespace}:tool`, '') const curTool = settings.keepLocalData ? useStorage(`${namespace}:tool`, '') : ref('')
const curMode = useStorage<SearchMode>(`${namespace}:mode`, 'loose') const curMode = settings.keepLocalData ? useStorage<SearchMode>(`${namespace}:mode`, 'loose') : ref<SearchMode>('loose')
const selectedStuff = computed(() => Array.from(curStuff.value)) const selectedStuff = computed(() => Array.from(curStuff.value))
// const selectedTools = computed(() => Array.from(curTools.value)) // const selectedTools = computed(() => Array.from(curTools.value))
@@ -73,13 +74,10 @@ export const useRecipeStore = defineStore('recipe', () => {
const isSearching = ref(false) const isSearching = ref(false)
/** /**
* 搜索菜谱 * 搜索菜谱
* @returns
*/ */
async function searchRecipes() { async function searchRecipes() {
isSearching.value = true isSearching.value = true
let result: RecipeItem[] = [] let result: RecipeItem[] = []
if (keyword.value)
result = await db.recipes.filter(item => item.name.includes(keyword.value)).toArray()
if (curMode.value === 'strict') { if (curMode.value === 'strict') {
result = await db.recipes.filter((item) => { result = await db.recipes.filter((item) => {
@@ -116,14 +114,18 @@ export const useRecipeStore = defineStore('recipe', () => {
}).toArray() }).toArray()
} }
if (keyword.value)
result = result.filter(item => item.name.includes(keyword.value))
isSearching.value = false isSearching.value = false
return result return result
} }
// 默认严格模式 // 默认严格模式
const displayedRecipe = ref<RecipeItem[]>([]) const displayedRecipe = ref<RecipeItem[]>([])
watch([keyword, curStuff, curTool, curMode], async () => { // fix curStuff watch
displayedRecipe.value = await searchRecipes() watch(() => [keyword.value, selectedStuff.value, curTool.value, curMode.value], async () => {
displayedRecipe.value = [...(await searchRecipes())]
}) })
/** /**

View File

@@ -1,7 +1,7 @@
export const appName = '隔离食用手册' export const appName = '食用手册'
export const appDescription = '好的,今天我们来做菜!' export const appDescription = '好的,今天我们来做菜!'
export const namespace = 'cook' export const namespace = 'cook'
export const lastDbUpdated = '2022-07-27 03:05:02' export const lastDbUpdated = '2023-11-11 19:51:02'
export * from './links' export * from './links'

View File

@@ -1,29 +1,4 @@
export interface StuffItem { import type { StuffItem } from '../types'
/**
* 食材名称
*/
name: string
/**
* 例如:🥔
*/
emoji: string
/**
* 图片链接
*/
image?: string
/**
* 别名,譬如:西红柿/番茄
*/
alias?: string
/**
* 图标名称
*/
icon?: string
/**
* 显示标签
*/
label?: string
}
/** /**
* 素菜 * 素菜

View File

@@ -184,10 +184,8 @@ BBQ烟熏手撕猪肉,猪肉,BV1DV411x7SH,复杂,,烤,烤箱,
微波炉版土豆浓汤,土豆、胡萝卜,BV1HL411E7MM,简单,早饭,微波加热,微波炉, 微波炉版土豆浓汤,土豆、胡萝卜,BV1HL411E7MM,简单,早饭,微波加热,微波炉,
微波炉版煲仔饭,米、香肠、鸡蛋,BV145411f7R4,简单,主食,微波加热,微波炉, 微波炉版煲仔饭,米、香肠、鸡蛋,BV145411f7R4,简单,主食,微波加热,微波炉,
微波炉版燕麦粥,,BV1cF411Y7pd,,主食,微波加热,微波炉, 微波炉版燕麦粥,,BV1cF411Y7pd,,主食,微波加热,微波炉,
微波炉版抱蛋水饺,面食、鸡蛋,BV1zL411x7CU,,,,微波炉,
微波炉版叉烧肉,猪肉、米,BV1Hi4y1s7Sc,,,微波加热,微波炉, 微波炉版叉烧肉,猪肉、米,BV1Hi4y1s7Sc,,,微波加热,微波炉,
微波炉版蛋炒饭,米、鸡蛋、香肠,BV1T34y147qU,简单,,微波加热,微波炉, 微波炉版蛋炒饭,米、鸡蛋、香肠,BV1T34y147qU,简单,,微波加热,微波炉,
微波炉版番茄炒蛋,鸡蛋、番茄、米,BV1zL411x7CU,,,,微波炉,
微波炉版番茄鸡蛋汤,番茄、鸡蛋,BV1qx411n7QF,,,,微波炉, 微波炉版番茄鸡蛋汤,番茄、鸡蛋,BV1qx411n7QF,,,,微波炉,
微波炉版番茄肉盒,番茄、猪肉、青椒,BV194411R7aH,,,微波加热,微波炉, 微波炉版番茄肉盒,番茄、猪肉、青椒,BV194411R7aH,,,微波加热,微波炉,
微波炉版肥牛饭,牛肉、洋葱、鸡蛋、米,BV1gh411Y7TU,,,,微波炉, 微波炉版肥牛饭,牛肉、洋葱、鸡蛋、米,BV1gh411Y7TU,,,,微波炉,
@@ -207,13 +205,10 @@ BBQ烟熏手撕猪肉,猪肉,BV1DV411x7SH,复杂,,烤,烤箱,
微波炉版牛奶炖饭,,BV1W7411c7As,简单,,微波加热,微波炉, 微波炉版牛奶炖饭,,BV1W7411c7As,简单,,微波加热,微波炉,
微波炉版日式嫩蒸鸡胸肉,鸡肉,BV1xu41197tU,,,,微波炉, 微波炉版日式嫩蒸鸡胸肉,鸡肉,BV1xu41197tU,,,,微波炉,
微波炉版烧烤茄子,茄子,BV19x411H7ig,,,,微波炉, 微波炉版烧烤茄子,茄子,BV19x411H7ig,,,,微波炉,
微波炉版蔬菜烘蛋,鸡蛋、猪肉、胡萝卜,BV1zL411x7CU,,,,微波炉,
微波炉版酸辣土豆丝,土豆,BV1zL411x7CU,,,,微波炉,
微波炉版蒜香烤茄子,茄子,BV19x411H7ig,简单,,微波加热,微波炉, 微波炉版蒜香烤茄子,茄子,BV19x411H7ig,简单,,微波加热,微波炉,
微波炉版蒜香琵琶腿,鸡肉,BV1qx411777p,,,,微波炉, 微波炉版蒜香琵琶腿,鸡肉,BV1qx411777p,,,,微波炉,
微波炉版吐司杯,鸡蛋、面包,BV1CS4y1k7Xi,,,微波加热,微波炉, 微波炉版吐司杯,鸡蛋、面包,BV1CS4y1k7Xi,,,微波加热,微波炉,
微波炉版五花肉,猪肉、米,BV1hL4y137Zk,,,微波加热,微波炉, 微波炉版五花肉,猪肉、米,BV1hL4y137Zk,,,微波加热,微波炉,
微波炉版鲜虾面,面食、虾、胡萝卜、方便面,BV1zL411x7CU,,,,微波炉,
微波炉版香干,豆腐,BV1F44y1T7sQ,,,微波加热,微波炉, 微波炉版香干,豆腐,BV1F44y1T7sQ,,,微波加热,微波炉,
微波炉版香辣豆腐,豆腐、米,BV1bT4y1o7dv,,,,微波炉, 微波炉版香辣豆腐,豆腐、米,BV1bT4y1o7dv,,,,微波炉,
微波炉版香嫩鸡胸肉,鸡肉,BV1xu41197tU,,,,微波炉, 微波炉版香嫩鸡胸肉,鸡肉,BV1xu41197tU,,,,微波炉,
@@ -222,8 +217,6 @@ BBQ烟熏手撕猪肉,猪肉,BV1DV411x7SH,复杂,,烤,烤箱,
微波炉版照烧鸡腿饭,米、鸡肉、胡萝卜、花菜,BV16Z4y1V774,,,,微波炉, 微波炉版照烧鸡腿饭,米、鸡肉、胡萝卜、花菜,BV16Z4y1V774,,,,微波炉,
微波炉版蒸蛋羹,鸡蛋,BV19T4y1D7Zd,,,,微波炉, 微波炉版蒸蛋羹,鸡蛋,BV19T4y1D7Zd,,,,微波炉,
微波炉版煮米饭,,BV193411W7nb,,,,微波炉, 微波炉版煮米饭,,BV193411W7nb,,,,微波炉,
微波炉版孜然豆腐丁,豆腐,BV1zL411x7CU,,,,微波炉,
豉油鸡翅,鸡肉,BV1vz4y1d775,普通,茶餐厅,,一口大锅,
水煮肉片,猪肉、芹菜、莴笋,BV1ZZ4y1379N,普通,川菜,,一口大锅, 水煮肉片,猪肉、芹菜、莴笋,BV1ZZ4y1379N,普通,川菜,,一口大锅,
脆口黄瓜,黄瓜,BV1Tb4y1X7ow,简单,脆口,凉拌,一口大锅, 脆口黄瓜,黄瓜,BV1Tb4y1X7ow,简单,脆口,凉拌,一口大锅,
白萝卜汤,白萝卜,BV1HJ411L7xA,简单,单一食材,,一口大锅, 白萝卜汤,白萝卜,BV1HJ411L7xA,简单,单一食材,,一口大锅,
@@ -303,7 +296,7 @@ BBQ烟熏手撕猪肉,猪肉,BV1DV411x7SH,复杂,,烤,烤箱,
名古屋鸡翅,鸡肉,BV1ET4y1A7Xd,普通,日式,,一口大锅, 名古屋鸡翅,鸡肉,BV1ET4y1A7Xd,普通,日式,,一口大锅,
日式炖白萝,白萝卜,BV17b411B7H1,简单,日式,,一口大锅, 日式炖白萝,白萝卜,BV17b411B7H1,简单,日式,,一口大锅,
炸虾天妇罗,,BV1e5411t7LY,困难,日式,,一口大锅, 炸虾天妇罗,,BV1e5411t7LY,困难,日式,,一口大锅,
可乐饼,土豆、洋葱、肉、鸡蛋,BV1yW411Q7sa,普通,日式菜,,一口大锅, 可乐饼,土豆、洋葱、肉、鸡蛋,BV17x411U75q,普通,日式菜,,一口大锅,
清炒莴笋丝,莴笋、胡萝卜,BV1qK411H7RL,简单,爽口,,一口大锅, 清炒莴笋丝,莴笋、胡萝卜,BV1qK411H7RL,简单,爽口,,一口大锅,
莴笋泡菜,莴笋,BV1h741127rS,简单,爽口,泡菜,一口大锅, 莴笋泡菜,莴笋,BV1h741127rS,简单,爽口,泡菜,一口大锅,
口蘑汤,菌菇,BV1e64y1h776,简单,,煎、炖,一口大锅, 口蘑汤,菌菇,BV1e64y1h776,简单,,煎、炖,一口大锅,
@@ -604,3 +597,4 @@ biangbiang面,面食,BV1844y157GL,简单,,油泼,一口大锅,
电饭煲番茄牛肉焖饭,番茄、牛肉、米,BV1Bv411C7X3,普通,,,一口大锅、电饭煲, 电饭煲番茄牛肉焖饭,番茄、牛肉、米,BV1Bv411C7X3,普通,,,一口大锅、电饭煲,
电饭煲排骨土豆焖饭,猪肉、土豆、米,BV1Bv411C7X3,普通,,,一口大锅、电饭煲, 电饭煲排骨土豆焖饭,猪肉、土豆、米,BV1Bv411C7X3,普通,,,一口大锅、电饭煲,
米布丁,米、鸡蛋,BV1hr4y1k7A5,简单,,,一口大锅、电饭煲, 米布丁,米、鸡蛋,BV1hr4y1k7A5,简单,,,一口大锅、电饭煲,
麻婆豆腐,豆腐,BV1it4y1X75m,简单,,,一口大锅,
1 name stuff bv difficulty tags methods tools
184 微波炉版土豆浓汤 土豆、胡萝卜 BV1HL411E7MM 简单 早饭 微波加热 微波炉
185 微波炉版煲仔饭 米、香肠、鸡蛋 BV145411f7R4 简单 主食 微波加热 微波炉
186 微波炉版燕麦粥 BV1cF411Y7pd 主食 微波加热 微波炉
微波炉版抱蛋水饺 面食、鸡蛋 BV1zL411x7CU 微波炉
187 微波炉版叉烧肉 猪肉、米 BV1Hi4y1s7Sc 微波加热 微波炉
188 微波炉版蛋炒饭 米、鸡蛋、香肠 BV1T34y147qU 简单 微波加热 微波炉
微波炉版番茄炒蛋 鸡蛋、番茄、米 BV1zL411x7CU 微波炉
189 微波炉版番茄鸡蛋汤 番茄、鸡蛋 BV1qx411n7QF 微波炉
190 微波炉版番茄肉盒 番茄、猪肉、青椒 BV194411R7aH 微波加热 微波炉
191 微波炉版肥牛饭 牛肉、洋葱、鸡蛋、米 BV1gh411Y7TU 微波炉
205 微波炉版牛奶炖饭 BV1W7411c7As 简单 微波加热 微波炉
206 微波炉版日式嫩蒸鸡胸肉 鸡肉 BV1xu41197tU 微波炉
207 微波炉版烧烤茄子 茄子 BV19x411H7ig 微波炉
微波炉版蔬菜烘蛋 鸡蛋、猪肉、胡萝卜 BV1zL411x7CU 微波炉
微波炉版酸辣土豆丝 土豆 BV1zL411x7CU 微波炉
208 微波炉版蒜香烤茄子 茄子 BV19x411H7ig 简单 微波加热 微波炉
209 微波炉版蒜香琵琶腿 鸡肉 BV1qx411777p 微波炉
210 微波炉版吐司杯 鸡蛋、面包 BV1CS4y1k7Xi 微波加热 微波炉
211 微波炉版五花肉 猪肉、米 BV1hL4y137Zk 微波加热 微波炉
微波炉版鲜虾面 面食、虾、胡萝卜、方便面 BV1zL411x7CU 微波炉
212 微波炉版香干 豆腐 BV1F44y1T7sQ 微波加热 微波炉
213 微波炉版香辣豆腐 豆腐、米 BV1bT4y1o7dv 微波炉
214 微波炉版香嫩鸡胸肉 鸡肉 BV1xu41197tU 微波炉
217 微波炉版照烧鸡腿饭 米、鸡肉、胡萝卜、花菜 BV16Z4y1V774 微波炉
218 微波炉版蒸蛋羹 鸡蛋 BV19T4y1D7Zd 微波炉
219 微波炉版煮米饭 BV193411W7nb 微波炉
微波炉版孜然豆腐丁 豆腐 BV1zL411x7CU 微波炉
豉油鸡翅 鸡肉 BV1vz4y1d775 普通 茶餐厅 一口大锅
220 水煮肉片 猪肉、芹菜、莴笋 BV1ZZ4y1379N 普通 川菜 一口大锅
221 脆口黄瓜 黄瓜 BV1Tb4y1X7ow 简单 脆口 凉拌 一口大锅
222 白萝卜汤 白萝卜 BV1HJ411L7xA 简单 单一食材 一口大锅
296 名古屋鸡翅 鸡肉 BV1ET4y1A7Xd 普通 日式 一口大锅
297 日式炖白萝 白萝卜 BV17b411B7H1 简单 日式 一口大锅
298 炸虾天妇罗 BV1e5411t7LY 困难 日式 一口大锅
299 可乐饼 土豆、洋葱、肉、鸡蛋 BV1yW411Q7sa BV17x411U75q 普通 日式菜 一口大锅
300 清炒莴笋丝 莴笋、胡萝卜 BV1qK411H7RL 简单 爽口 一口大锅
301 莴笋泡菜 莴笋 BV1h741127rS 简单 爽口 泡菜 一口大锅
302 口蘑汤 菌菇 BV1e64y1h776 简单 煎、炖 一口大锅
597 电饭煲番茄牛肉焖饭 番茄、牛肉、米 BV1Bv411C7X3 普通 一口大锅、电饭煲
598 电饭煲排骨土豆焖饭 猪肉、土豆、米 BV1Bv411C7X3 普通 一口大锅、电饭煲
599 米布丁 米、鸡蛋 BV1hr4y1k7A5 简单 一口大锅、电饭煲
600 麻婆豆腐 豆腐 BV1it4y1X75m 简单 一口大锅

File diff suppressed because one or more lines are too long

7
eslint.config.js Normal file
View File

@@ -0,0 +1,7 @@
import antfu from '@antfu/eslint-config'
import unocss from '@unocss/eslint-config/flat'
export default antfu(
{},
unocss,
)

22
layouts/child.vue Normal file
View File

@@ -0,0 +1,22 @@
<script lang="ts" setup>
defineProps<{
title?: string
}>()
const route = useRoute()
</script>
<template>
<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 font="bold">
{{ route.meta.title }}
</h2>
<DarkToggle mr-3 />
</div>
<slot />
<TheBottomMenu fixed bottom-0 left-0 right-0 />
</main>
</template>

View File

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

View File

@@ -15,6 +15,7 @@ export default defineNuxtConfig({
'@pinia/nuxt', '@pinia/nuxt',
'@nuxtjs/color-mode', '@nuxtjs/color-mode',
'@vite-pwa/nuxt', '@vite-pwa/nuxt',
'nuxt-vitest',
'@zadigetvoltaire/nuxt-gtm', '@zadigetvoltaire/nuxt-gtm',
@@ -26,7 +27,6 @@ export default defineNuxtConfig({
// but missing on offline, disabling extraction it until fixed // but missing on offline, disabling extraction it until fixed
payloadExtraction: false, payloadExtraction: false,
inlineSSRStyles: false, inlineSSRStyles: false,
renderJsonPayloads: true,
typedPages: true, typedPages: true,
}, },
@@ -48,7 +48,7 @@ export default defineNuxtConfig({
}, },
prerender: { prerender: {
crawlLinks: false, crawlLinks: false,
routes: ['/'], routes: ['/', '/random', '/help', '/user', '/404', '/settings'],
ignore: ['/hi'], ignore: ['/hi'],
}, },
}, },
@@ -61,7 +61,7 @@ export default defineNuxtConfig({
{ rel: 'apple-touch-icon', href: '/apple-touch-icon.png' }, { rel: 'apple-touch-icon', href: '/apple-touch-icon.png' },
], ],
meta: [ meta: [
{ name: 'viewport', content: 'width=device-width, initial-scale=1' }, { name: 'viewport', content: 'width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no' },
{ name: 'description', content: appDescription }, { name: 'description', content: appDescription },
{ name: 'apple-mobile-web-app-status-bar-style', content: 'black-translucent' }, { name: 'apple-mobile-web-app-status-bar-style', content: 'black-translucent' },
], ],

View File

@@ -1,14 +1,16 @@
{ {
"type": "module",
"version": "1.2.1",
"private": true, "private": true,
"version": "1.1.6", "packageManager": "pnpm@8.10.2",
"packageManager": "pnpm@8.6.10",
"engines": { "engines": {
"node": ">=14" "node": ">=14"
}, },
"scripts": { "scripts": {
"build": "npm run convert && cross-env VITE_APP_BUILD_TIME=$(date +%s) nuxi build", "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", "convert": "tsx scripts/convert.ts",
"dev": "cross-env VITE_APP_BUILD_TIME=$(date +%s) nuxi dev", "dev": "cross-env VITE_APP_BUILD_TIME=$(date +%s) nuxi dev --host",
"dev:pwa": "VITE_PLUGIN_PWA=true nuxi dev", "dev:pwa": "VITE_PLUGIN_PWA=true nuxi dev",
"generate": "nuxi generate", "generate": "nuxi generate",
"start:generate": "npx serve .output/public", "start:generate": "npx serve .output/public",
@@ -21,43 +23,47 @@
"typecheck": "vue-tsc --noEmit" "typecheck": "vue-tsc --noEmit"
}, },
"dependencies": { "dependencies": {
"dayjs": "^1.11.10",
"vue-about-me": "^1.2.7" "vue-about-me": "^1.2.7"
}, },
"devDependencies": { "devDependencies": {
"@antfu/eslint-config": "^0.39.8", "@antfu/eslint-config": "^1.1.0",
"@headlessui/vue": "^1.7.15", "@headlessui/vue": "^1.7.16",
"@iconify-json/carbon": "^1.1.18", "@iconify-json/carbon": "^1.1.21",
"@iconify-json/fe": "^1.1.6", "@iconify-json/fe": "^1.1.7",
"@iconify-json/gg": "^1.1.5", "@iconify-json/gg": "^1.1.6",
"@iconify-json/ic": "^1.1.13", "@iconify-json/ic": "^1.1.14",
"@iconify-json/mdi": "^1.1.53", "@iconify-json/mdi": "^1.1.55",
"@iconify-json/ri": "^1.1.10", "@iconify-json/ri": "^1.1.12",
"@iconify-json/twemoji": "^1.1.11", "@iconify-json/twemoji": "^1.1.12",
"@nuxt/devtools": "^0.7.1", "@nuxt/devtools": "^1.0.1",
"@nuxtjs/color-mode": "^3.3.0", "@nuxtjs/color-mode": "^3.3.0",
"@pinia/nuxt": "^0.4.11", "@pinia/nuxt": "^0.5.1",
"@pinia/testing": "^0.1.3", "@pinia/testing": "^0.1.3",
"@unocss/eslint-config": "^0.54.0", "@unocss/eslint-config": "^0.57.3",
"@unocss/nuxt": "^0.54.0", "@unocss/nuxt": "^0.57.3",
"@vite-pwa/nuxt": "^0.1.0", "@vite-pwa/nuxt": "^0.2.1",
"@vue/test-utils": "^2.4.1", "@vue/test-utils": "^2.4.1",
"@vueuse/nuxt": "^10.2.1", "@vueuse/nuxt": "^10.6.0",
"@yunlefun/vue": "^0.0.7", "@yunlefun/vue": "0.0.8-beta.4",
"@zadigetvoltaire/nuxt-gtm": "^0.0.13", "@zadigetvoltaire/nuxt-gtm": "^0.0.13",
"bumpp": "^9.2.0",
"consola": "^3.2.3", "consola": "^3.2.3",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"dexie": "^3.2.4", "dexie": "^3.2.4",
"eslint": "^8.46.0", "eslint": "^8.53.0",
"fake-indexeddb": "^4.0.2", "fake-indexeddb": "^5.0.1",
"happy-dom": "^12.10.3",
"jsdom": "^22.1.0", "jsdom": "^22.1.0",
"nuxt": "^3.6.5", "nuxt": "^3.8.1",
"pinia": "^2.1.6", "nuxt-vitest": "^0.11.3",
"sass": "^1.64.1", "pinia": "^2.1.7",
"sass": "^1.69.5",
"star-markdown-css": "^0.4.2", "star-markdown-css": "^0.4.2",
"tsx": "^3.12.7", "tsx": "^4.1.1",
"typescript": "^5.1.6", "typescript": "^5.2.2",
"unocss": "^0.54.0", "unocss": "^0.57.3",
"vitest": "^0.33.0", "vitest": "^0.34.6",
"vue-tsc": "^1.8.8" "vue-tsc": "^1.8.22"
} }
} }

View File

@@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
const router = useRouter() // const router = useRouter()
</script> </script>
<template> <template>
@@ -9,9 +9,9 @@ const router = useRouter()
</div> </div>
<div>菜谱消失了</div> <div>菜谱消失了</div>
<div> <div>
<button text-sm btn m="3 t8" @click="router.back()"> <NuxtLink text-sm btn m="3 t8" to="/">
返回 返回主页
</button> </NuxtLink>
</div> </div>
</main> </main>
</template> </template>

5
pages/about.vue Normal file
View File

@@ -0,0 +1,5 @@
<template>
<div>
关于
</div>
</template>

22
pages/cookbooks/index.vue Normal file
View File

@@ -0,0 +1,22 @@
<script lang="ts" setup>
definePageMeta({
layout: 'child',
title: '自定义菜谱',
})
</script>
<template>
<div>
<h3>
开发中敬请期待
</h3>
<div grid="~ cols-3" gap="4" p="4">
<CookbookCard :cookbook="defaultCookbook">
默认菜谱
</CookbookCard>
<NewCookbookCard />
</div>
</div>
</template>

5
pages/cookbooks/new.vue Normal file
View File

@@ -0,0 +1,5 @@
<template>
<div>
新建 Cookbook
</div>
</template>

View File

@@ -9,6 +9,10 @@
<FeedbackActions /> <FeedbackActions />
<div class="mx-auto max-w-md w-full rounded-2xl p-2" text-left> <div class="mx-auto max-w-md w-full rounded-2xl p-2" text-left>
<FAQItem title="未来计划?">
计划增加新功能如自定义菜谱与使用其他用户分享的菜谱
</FAQItem>
<FAQItem title="什么是模式?"> <FAQItem title="什么是模式?">
<ul> <ul>
<li><b>模糊匹配</b>展示所有含当前选中任意食材的菜谱</li> <li><b>模糊匹配</b>展示所有含当前选中任意食材的菜谱</li>
@@ -80,6 +84,87 @@
</li> </li>
</ol> </ol>
</FAQItem> </FAQItem>
<hr h="1" my="4" bg-black>
<FAQItem :default-open="true" title="关于">
<div text-left>
<ul>
<li>
它诞生于 2022 4 时值疫情风控期间希望能帮助期间的伙伴根据现有食材寻找到合适的菜谱故原名隔离食用手册
</li>
<li>
如今那个时期已离我们远去故去掉隔离二字但也很高兴能在这里继续与你相遇希望它能继续发光发热在日常生活中帮助到大家
</li>
<li>
<div class="inline-flex items-center justify-center">
代码仓库<a class="inline-flex items-center justify-center" href="https://github.com/YunYouJun/cook" target="_blank">
<div m="r-1" i-ri-github-line inline-flex />YunYouJun/cook</a>
</div>
</li>
<li>
<div class="inline-flex items-center justify-center">
菜谱视频来源
<a class="inline-flex items-center text-sm text-blue-600 dark:text-blue-400" href="https://docs.qq.com/sheet/DQk1vdkhFV0twQVNS" target="_blank">
<div m="r-1" i-ri-bilibili-line inline-flex />
<span class="inline-flex">隔离食用手册大全</span>
</a>
</div>
</li>
</ul>
</div>
</FAQItem>
<FAQItem title="关于我">
<div text-left>
我的个人微信公众号云游君会分享一些生活和写的<a href="https://sponsors.yunyoujun.cn/projects" target="_blank">
小玩具们
</a>
<a inline-flex py-4 href="https://cdn.yunyoujun.cn/img/about/white-qrcode-and-search.jpg" target="_blank">
<img src="https://cdn.yunyoujun.cn/img/about/white-qrcode-and-search.jpg">
</a>
</div>
<AboutMe />
</FAQItem>
<FAQItem title="致谢">
<p>
感谢以下小伙伴为本项目提供的数据支持和 QA
</p>
<ul mt-2 text-left text-sm>
<li>
<a href="https://weibo.com/runny" target="_blank">Runny</a>
</li>
<li>
山竹太凉
</li>
<li>
leo
</li>
<li>
麒麟
</li>
<li>
晴方啾
</li>
<li>
课代表阿伟
</li>
</ul>
</FAQItem>
<FAQItem title="赞助者们">
<div>
感谢至今以来所有的<a href="https://afdian.net/a/yunyoujun" class="text-purple" target="_blank">赞助者</a>你们的支持是我持续维护和开发新项目的动力
</div>
<div pt-2>
<a href="https://sponsors.yunyoujun.cn" target="_blank">
<img src="https://sponsors.yunyoujun.cn/sponsors.svg">
</a>
</div>
</FAQItem>
</div> </div>
</div> </div>
<BaseFooter mt-4 /> <BaseFooter mt-4 />

View File

@@ -18,4 +18,5 @@ const rStore = useRecipeStore()
</p> </p>
<ChooseFood /> <ChooseFood />
<SimpleCopyright />
</template> </template>

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>

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

@@ -0,0 +1,12 @@
<script lang="ts" setup>
definePageMeta({
layout: 'child',
title: '菜谱 - ?',
})
</script>
<template>
<div>
asd
</div>
</template>

5
pages/recipes/new.vue Normal file
View File

@@ -0,0 +1,5 @@
<template>
<div>
新建 Recipe
</div>
</template>

View File

@@ -1,7 +1,28 @@
<script lang="ts" setup>
const app = useAppStore()
definePageMeta({
layout: 'child',
})
</script>
<template> <template>
<div> <div>
<button> <CommonHeader>
设置 设置
</button> </CommonHeader>
<div
class="mx-auto max-w-md w-full"
px-2
text-left
>
<YlfForm>
<YlfFormItem label="离开网页后保留选中数据">
<YlfSwitch v-model="app.settings.keepLocalData" />
</YlfFormItem>
<YlfFormItem label="更多设置,敬请期待" />
</YlfForm>
</div>
</div> </div>
</template> </template>

View File

@@ -1,83 +1,41 @@
<script lang="ts" setup>
import { links } from '~/constants'
</script>
<template> <template>
<div px-2> <div>
<CommonHeader> <CommonHeader>
我的 我的
</CommonHeader> </CommonHeader>
<FeedbackActions /> <div
class="mx-auto max-w-md w-full"
px-2
text-left
>
<div mt-2 gap="3" grid="~ cols-3">
<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 rounded-2xl p-2" text-left> <YlfForm>
<p my-6 text-left text-base> <YlfFormItem icon="i-ri-feedback-line" label="立即反馈" :to="links.feedback" target="_blank" />
很高兴能在这里与你相遇也很希望这个网站可以真的帮助到你 <YlfFormItem icon="i-ri-mail-send-line" label="立即投稿" :to="links.contribute" target="_blank" />
</p> </YlfForm>
<FAQItem title="关于"> <YlfForm>
<div text-left> <YlfFormItem icon="i-ri-settings-line" label="设置" to="/settings" />
<ul> </YlfForm>
<li>
<div class="inline-flex items-center justify-center">
代码仓库<a class="inline-flex items-center justify-center" href="https://github.com/YunYouJun/cook" target="_blank">
<div m="r-1" i-ri-github-line inline-flex />YunYouJun/cook</a>
</div>
</li>
<li>
<div class="inline-flex items-center justify-center">
菜谱视频来源
<a class="inline-flex items-center text-sm text-blue-600 dark:text-blue-400" href="https://docs.qq.com/sheet/DQk1vdkhFV0twQVNS" target="_blank">
<div m="r-1" i-ri-bilibili-line inline-flex />
<span class="inline-flex">隔离食用手册大全</span>
</a>
</div>
</li>
</ul>
</div>
</FAQItem>
<FAQItem title="关于我"> <!-- <YlfForm>
<div text-left> <YlfFormItem icon="i-ri-article-line" label="自定义菜谱 TODO" to="/cookbooks/" />
我的个人微信公众号云游君会分享一些生活和写的<a href="https://sponsors.yunyoujun.cn/projects" target="_blank"> </YlfForm> -->
小玩具们
</a>
<a inline-flex py-4 href="https://cdn.yunyoujun.cn/img/about/white-qrcode-and-search.jpg" target="_blank"> <YlfForm>
<img src="https://cdn.yunyoujun.cn/img/about/white-qrcode-and-search.jpg"> <YlfFormItem icon="i-ri-question-line" label="帮助" to="/help" />
</a> <YlfFormItem icon="i-ri-information-line" label="关于" to="/help" />
</div> </YlfForm>
<AboutMe />
</FAQItem>
<FAQItem title="致谢">
<p>
感谢以下小伙伴为本项目提供的数据支持和 QA
</p>
<ul mt-2 text-left text-sm>
<li>
<a href="https://weibo.com/runny" target="_blank">Runny</a>
</li>
<li>
山竹太凉
</li>
<li>
leo
</li>
<li>
麒麟
</li>
<li>
晴方啾
</li>
<li>
课代表阿伟
</li>
</ul>
</FAQItem>
<FAQItem title="赞助者们">
<a href="https://sponsors.yunyoujun.cn" target="_blank">
<img src="https://sponsors.yunyoujun.cn/sponsors.svg">
</a>
</FAQItem>
</div> </div>
<BaseFooter mt-4 /> <BaseFooter mt-4 />

6961
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

10
shims.d.ts vendored
View File

@@ -9,13 +9,15 @@ declare interface Window {
// with vite-plugin-vue-markdown, markdowns can be treat as Vue components // with vite-plugin-vue-markdown, markdowns can be treat as Vue components
declare module '*.md' { declare module '*.md' {
import { type DefineComponent } from 'vue' import type { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
const component: DefineComponent<object, object, any>
export default component export default component
} }
declare module '*.vue' { declare module '*.vue' {
import { type DefineComponent } from 'vue' import type { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
const component: DefineComponent<object, object, any>
export default component export default component
} }

View File

@@ -2,11 +2,29 @@
--c-primary: #5fc178; --c-primary: #5fc178;
--c-text: #333; --c-text: #333;
--c-bg: #fff; --c-bg: white;
--c-bg-alt: #f9fbfd;
} }
.dark { .dark {
--c-text: #fafafa; --c-text: #fafafa;
--c-bg: #121212; --c-bg: #121212;
--c-bg-alt: #333;
}
// ylf
:root {
--ylf-c-bg-alt: var(--c-bg-alt);
--ylf-c-border: #eaeaea;
}
.dark {
--ylf-c-border: #666;
}
// cook custom
:root {
--cook-bottom-menu-padding-bottom: 20px;
--cook-bottom-menu-height: calc(64px + var(--cook-bottom-menu-padding-bottom));
} }

View File

@@ -8,11 +8,10 @@ body,
height: 100%; height: 100%;
margin: 0; margin: 0;
padding: 0; padding: 0;
background: var(--c-bg);
} }
html.dark {
background: #121212;
}
input:focus { input:focus {
outline: none; outline: none;

View File

@@ -5,7 +5,7 @@ import { describe, it } from 'vitest'
// import { createTestingPinia } from '@pinia/testing' // import { createTestingPinia } from '@pinia/testing'
// import ChooseFood from '../src/components/ChooseFood.vue' // import ChooseFood from '../src/components/ChooseFood.vue'
describe('ChooseFood.vue', () => { describe('chooseFood.vue', () => {
it('should render', async () => { it('should render', async () => {
// const pinia = createTestingPinia({ // const pinia = createTestingPinia({
// createSpy: vi.fn, // createSpy: vi.fn,

22
types/cookbook.ts Normal file
View File

@@ -0,0 +1,22 @@
import type { Recipes } from './recipe'
export interface Cookbook {
/**
* 菜谱 ID自定义唯一标识符
*/
id: string
cover?: string
/**
* 菜谱名称
*/
title: string
description: string
author: string | string[]
/**
* 菜谱
*/
recipes: Recipes
createdAt: string
updatedAt: string
}

View File

@@ -1 +1,2 @@
export * from './cookbook'
export * from './recipe' export * from './recipe'

View File

@@ -40,3 +40,30 @@ export interface RecipeItem {
} }
export type Recipes = RecipeItem[] export type Recipes = RecipeItem[]
export interface StuffItem {
/**
* 食材名称
*/
name: string
/**
* 例如:🥔
*/
emoji: string
/**
* 图片链接
*/
image?: string
/**
* 别名,譬如:西红柿/番茄
*/
alias?: string
/**
* 图标名称
*/
icon?: string
/**
* 显示标签
*/
label?: string
}

View File

@@ -21,7 +21,6 @@ export default defineConfig({
shortcuts: [ shortcuts: [
['tag', 'text-sm cursor-pointer inline-flex justify-center items-center transition shadow hover:shadow-md'], ['tag', 'text-sm cursor-pointer inline-flex justify-center items-center transition shadow hover:shadow-md'],
['btn', 'text-sm px-4 py-1 rounded inline-block bg-blue-600 text-white cursor-pointer hover:bg-blue-700 disabled:cursor-default disabled:bg-gray-600 disabled:opacity-50'], ['btn', 'text-sm px-4 py-1 rounded inline-block bg-blue-600 text-white cursor-pointer hover:bg-blue-700 disabled:cursor-default disabled:bg-gray-600 disabled:opacity-50'],
['icon-btn', 'text-[0.9em] inline-block cursor-pointer select-none opacity-75 transition duration-200 ease-in-out hover:opacity-100 hover:text-blue-600'],
], ],
presets: [ presets: [
presetUno(), presetUno(),

11
utils/cookbook.ts Normal file
View File

@@ -0,0 +1,11 @@
import type { Cookbook } from '~/types'
export const defaultCookbook: Cookbook = {
id: 'default',
title: '默认菜谱',
description: '记录了一些特殊时期常用的菜谱',
author: [''],
recipes: [],
updatedAt: '',
createdAt: '2021-04-04',
}

View File

@@ -9,7 +9,6 @@ foodItems.forEach((item) => {
/** /**
* get emojis from stuff name array * get emojis from stuff name array
* @param stuff * @param stuff
* @returns
*/ */
export function getEmojisFromStuff(stuff: string[]) { export function getEmojisFromStuff(stuff: string[]) {
const emojis: string[] = stuff.map(name => foodEmojiMap.get(name)).filter(item => !!item) const emojis: string[] = stuff.map(name => foodEmojiMap.get(name)).filter(item => !!item)

View File

@@ -3,7 +3,6 @@ import { isClient } from '@vueuse/core'
/** /**
* - https://web.dev/customize-install/#detect-install * - https://web.dev/customize-install/#detect-install
* - [Trigger installation from your PWA](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/How_to/Trigger_install_prompt) * - [Trigger installation from your PWA](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/How_to/Trigger_install_prompt)
* @returns
*/ */
export function installPrompt() { export function installPrompt() {
if (!isClient) if (!isClient)

View File

@@ -1,7 +1,5 @@
/** /**
* 生成随机数组 * 生成随机数组
* @param recipes
* @returns
*/ */
export function generateRandomArray(length: number, total = 1) { export function generateRandomArray(length: number, total = 1) {
const randomArr: number[] = [] const randomArr: number[] = []

10
utils/settings.ts Normal file
View File

@@ -0,0 +1,10 @@
export interface UserSettings {
/**
* 保留本地数据
*/
keepLocalData: boolean
}
export const defaultSettings: UserSettings = {
keepLocalData: true,
}

View File

@@ -4,10 +4,16 @@ export default defineConfig({
test: { test: {
include: ['test/**/*.test.ts'], include: ['test/**/*.test.ts'],
environment: 'jsdom', environment: 'jsdom',
deps: { server: {
inline: ['@vue', '@vueuse', 'vue-demi'], deps: {
inline: ['@vue', '@vueuse', 'vue-demi'],
},
}, },
setupFiles: ['test/setup.ts'], setupFiles: ['test/setup.ts'],
alias: {
'~': './',
},
}, },
}) })

10
vitest.nuxt.config.ts Normal file
View File

@@ -0,0 +1,10 @@
import { defineVitestConfig } from 'nuxt-vitest/config'
export default defineVitestConfig({
test: {
include: ['test/**/*.test.ts'],
environment: 'nuxt',
setupFiles: ['test/setup.ts'],
},
})