15 Commits

Author SHA1 Message Date
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
69 changed files with 4008 additions and 3504 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

68
.vscode/settings.json vendored
View File

@@ -1,11 +1,69 @@
{ {
"cSpell.words": ["Vitesse", "Vite", "unocss", "vitest", "vueuse", "pinia", "demi", "antfu", "iconify", "intlify", "vitejs", "unplugin", "pnpm"], "cSpell.words": ["Vitesse", "Vite", "unocss", "vitest", "vueuse", "pinia", "demi", "antfu", "iconify", "intlify", "vitejs", "unplugin", "pnpm"],
"prettier.enable": false,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true,
},
"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

@@ -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,5 +1,5 @@
<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'

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-3
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="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,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

@@ -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,7 +74,6 @@ 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

View File

@@ -1,4 +1,4 @@
export const appName = '隔离食用手册' export const appName = '食用手册'
export const appDescription = '好的,今天我们来做菜!' export const appDescription = '好的,今天我们来做菜!'
export const namespace = 'cook' export const namespace = 'cook'

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,7 +217,6 @@ BBQ烟熏手撕猪肉,猪肉,BV1DV411x7SH,复杂,,烤,烤箱,
微波炉版照烧鸡腿饭,米、鸡肉、胡萝卜、花菜,BV16Z4y1V774,,,,微波炉, 微波炉版照烧鸡腿饭,米、鸡肉、胡萝卜、花菜,BV16Z4y1V774,,,,微波炉,
微波炉版蒸蛋羹,鸡蛋,BV19T4y1D7Zd,,,,微波炉, 微波炉版蒸蛋羹,鸡蛋,BV19T4y1D7Zd,,,,微波炉,
微波炉版煮米饭,,BV193411W7nb,,,,微波炉, 微波炉版煮米饭,,BV193411W7nb,,,,微波炉,
微波炉版孜然豆腐丁,豆腐,BV1zL411x7CU,,,,微波炉,
豉油鸡翅,鸡肉,BV1vz4y1d775,普通,茶餐厅,,一口大锅, 豉油鸡翅,鸡肉,BV1vz4y1d775,普通,茶餐厅,,一口大锅,
水煮肉片,猪肉、芹菜、莴笋,BV1ZZ4y1379N,普通,川菜,,一口大锅, 水煮肉片,猪肉、芹菜、莴笋,BV1ZZ4y1379N,普通,川菜,,一口大锅,
脆口黄瓜,黄瓜,BV1Tb4y1X7ow,简单,脆口,凉拌,一口大锅, 脆口黄瓜,黄瓜,BV1Tb4y1X7ow,简单,脆口,凉拌,一口大锅,
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 微波炉
220 豉油鸡翅 鸡肉 BV1vz4y1d775 普通 茶餐厅 一口大锅
221 水煮肉片 猪肉、芹菜、莴笋 BV1ZZ4y1379N 普通 川菜 一口大锅
222 脆口黄瓜 黄瓜 BV1Tb4y1X7ow 简单 脆口 凉拌 一口大锅

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>
{{ route.meta.title }}
</h2>
<DarkToggle mr-3 />
</div>
<slot />
<TheBottomMenu fixed bottom-0 left-0 right-0 />
</main>
</template>

View File

@@ -1,7 +1,7 @@
<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 right-3 top-5 />
<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,15 @@
{ {
"type": "module",
"version": "1.1.8",
"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",
"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",
@@ -24,40 +25,42 @@
"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.0",
"@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.2",
"@unocss/nuxt": "^0.54.0", "@unocss/nuxt": "^0.57.2",
"@vite-pwa/nuxt": "^0.1.0", "@vite-pwa/nuxt": "^0.1.1",
"@vue/test-utils": "^2.4.1", "@vue/test-utils": "^2.4.1",
"@vueuse/nuxt": "^10.2.1", "@vueuse/nuxt": "^10.5.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",
"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.0",
"pinia": "^2.1.6", "nuxt-vitest": "^0.11.2",
"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": "^3.14.0",
"typescript": "^5.1.6", "typescript": "^5.2.2",
"unocss": "^0.54.0", "unocss": "^0.57.2",
"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/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,35 @@
<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
>
<YlfForm>
<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" />
</YlfForm>
<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-settings-line" label="设置" to="/settings" />
很高兴能在这里与你相遇也很希望这个网站可以真的帮助到你 </YlfForm>
</p>
<FAQItem title="关于"> <YlfForm>
<div text-left> <YlfFormItem icon="i-ri-article-line" label="自定义菜谱" to="/cookbooks/" />
<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-question-line" label="帮助" to="/help" />
我的个人微信公众号云游君会分享一些生活和写的<a href="https://sponsors.yunyoujun.cn/projects" target="_blank"> <YlfFormItem icon="i-ri-information-line" label="关于" to="/help" />
小玩具们 </YlfForm>
</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="赞助者们">
<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 />

6447
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
} }

1
src/data/recipe.json Normal file

File diff suppressed because one or more lines are too long

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'],
},
})