30 Commits

Author SHA1 Message Date
YunYouJun
21f74b3c48 chore: release v1.2.4 2025-01-24 11:40:38 +08:00
YunYouJun
5fa5944dff chore: fix ci node version 2025-01-24 11:40:02 +08:00
YunYouJun
1af9e36fc0 chore: upgrade readme 2025-01-24 11:36:35 +08:00
YunYouJun
65fea75825 chore: release v1.2.3 2025-01-24 11:35:46 +08:00
YunYouJun
5e97da4731 chore: upgrade deps & fix lint 2025-01-24 11:27:43 +08:00
George Chen
78fa350ced feat: use <button> to improve a11y and keyboard navigation experience (#74) 2025-01-12 01:35:19 +08:00
YunYouJun
11283f6f94 chore: fix lint ignore recipe.json 2024-12-30 02:04:51 +08:00
YunYouJun
e9b4d3ac38 chore: upgrade deps with nuxt 2024-12-30 02:02:12 +08:00
YunYouJun
7fe1f95d9d ci: fix nuxt lint 2024-09-15 18:26:51 +08:00
YunYouJun
86b565dfa9 ci: add lint-staged check 2024-09-15 18:14:52 +08:00
YunYouJun
41bdc3346f refactor: use nuxt compatiable 4 folder 2024-09-15 18:07:50 +08:00
YunYouJun
7a52b024dd ci: add vercel tag deplou 2024-09-15 17:45:11 +08:00
YunYouJun
10073b3a07 chore: fix ci lint/typecheck/test 2024-04-28 19:36:17 +08:00
YunYouJun
778594652c fix: ios qq 500 error 2024-04-28 19:18:31 +08:00
YunYouJun
b00b52cba8 chore: upgrade deps 2024-03-13 15:58:41 +08:00
YunYouJun
ab7f4e20f1 ci: use node lts 2024-01-28 06:57:17 +08:00
YunYouJun
d42e3cf65d chore: revert if commitSha buildDate 2024-01-27 03:46:30 +08:00
YunYouJun
f59c34499a test: fix relative path 2024-01-27 03:45:50 +08:00
YunYouJun
e741f5d5a0 chore: upgrade deps 2024-01-27 03:25:20 +08:00
KazariEX
99d7d660bb fix: div in span (#61) 2023-11-16 14:10:34 +08:00
YunYouJun
87025cd17e chore: release v1.2.2 2023-11-13 00:41:24 +08:00
YunYouJun
68a4b2dbeb chore: upgrade deps 2023-11-13 00:41:15 +08:00
YunYouJun
1ab402af8b feat: add random recipes localStorage 2023-11-13 00:31:29 +08:00
YunYouJun
b2cf053446 chore: update lock 2023-11-11 19:53:44 +08:00
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
115 changed files with 12218 additions and 9627 deletions

View File

@@ -23,7 +23,7 @@ jobs:
- name: Set node - name: Set node
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
node-version: 16.x node-version: lts/*
cache: pnpm cache: pnpm
- name: Install - name: Install
@@ -43,7 +43,7 @@ jobs:
- name: Set node - name: Set node
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
node-version: 16.x node-version: lts/*
cache: pnpm cache: pnpm
- name: Install - name: Install
@@ -60,7 +60,7 @@ jobs:
strategy: strategy:
matrix: matrix:
node-version: [16.x] node-version: [lts/*]
os: [ubuntu-latest] os: [ubuntu-latest]
fail-fast: false fail-fast: false

View File

@@ -17,10 +17,10 @@ jobs:
uses: pnpm/action-setup@v2 uses: pnpm/action-setup@v2
# after pnpm # after pnpm
- name: Use Node.js 16 - name: Use Node.js LTS
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
node-version: 16 node-version: lts/*
registry-url: https://registry.npmjs.org/ registry-url: https://registry.npmjs.org/
cache: pnpm cache: pnpm

35
.github/workflows/vercel.yml vendored Normal file
View File

@@ -0,0 +1,35 @@
name: Production Tag Deployment
env:
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
on:
push:
# Pattern matched against refs/tags
tags:
- '*' # Push events to every tag not containing /
jobs:
Deploy-Production:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install pnpm
uses: pnpm/action-setup@v2
# after pnpm
- name: Use Node.js LTS
uses: actions/setup-node@v3
with:
node-version: lts/*
registry-url: https://registry.npmjs.org/
cache: pnpm
- name: Install Vercel CLI
run: pnpm i -g vercel@latest
- name: Pull Vercel Environment Information
run: vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }}
- name: Build Project Artifacts
run: vercel build --prod --token=${{ secrets.VERCEL_TOKEN }}
- name: Deploy Project Artifacts to Vercel
run: vercel deploy --prebuilt --prod --token=${{ secrets.VERCEL_TOKEN }}

1
.gitignore vendored
View File

@@ -17,3 +17,4 @@ dist
.nuxt .nuxt
.env .env
.idea/ .idea/
.vercel

2
.npmrc
View File

@@ -1,3 +1,5 @@
shamefully-hoist=true shamefully-hoist=true
strict-peer-dependencies=false strict-peer-dependencies=false
shell-emulator=true shell-emulator=true
auto-install-peers=false
ignore-workspace-root-check=true

51
.vscode/settings.json vendored
View File

@@ -19,8 +19,6 @@
"*.css": "postcss" "*.css": "postcss"
}, },
// Enable the ESlint flat config support
"eslint.experimental.useFlatConfig": true,
// Disable the default formatter, use eslint instead // Disable the default formatter, use eslint instead
"prettier.enable": false, "prettier.enable": false,
"editor.formatOnSave": false, "editor.formatOnSave": false,
@@ -31,43 +29,17 @@
}, },
// Silent the stylistic rules in you IDE, but still auto fix them // Silent the stylistic rules in you IDE, but still auto fix them
"eslint.rules.customizations": [ "eslint.rules.customizations": [
{ { "rule": "style/*", "severity": "off" },
"rule": "style/*", { "rule": "*-indent", "severity": "off" },
"severity": "off" { "rule": "*-spacing", "severity": "off" },
}, { "rule": "*-spaces", "severity": "off" },
{ { "rule": "*-order", "severity": "off" },
"rule": "*-indent", { "rule": "*-dangle", "severity": "off" },
"severity": "off" { "rule": "*-newline", "severity": "off" },
}, { "rule": "*quotes", "severity": "off" },
{ { "rule": "*semi", "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 // Enable eslint for all supported languages
"eslint.validate": [ "eslint.validate": [
"javascript", "javascript",
@@ -80,5 +52,6 @@
"json", "json",
"jsonc", "jsonc",
"yaml" "yaml"
] ],
"typescript.tsdk": "node_modules/typescript/lib"
} }

View File

@@ -10,6 +10,7 @@
### 网页版本 ### 网页版本
- 网站链接:[cook.yunyoujun.cn](https://cook.yunyoujun.cn) - 网站链接:[cook.yunyoujun.cn](https://cook.yunyoujun.cn)
- 国内加速:[cook.yunle.fun](https://cook.yunle.fun)
- 备用:[cook.yyj.moe](https://cook.yyj.moe) - 备用:[cook.yyj.moe](https://cook.yyj.moe)
- 开发版:[cook.yunle.app](https://cook.yunle.app) - 开发版:[cook.yunle.app](https://cook.yunle.app)
@@ -73,8 +74,6 @@ docker stop cook
感谢以下小伙伴为本项目提供的数据支持和 QA 感谢以下小伙伴为本项目提供的数据支持和 QA
- [Runny](https://weibo.com/runny) - [Runny](https://weibo.com/runny)
- 山竹太凉
- leo
- 麒麟 - 麒麟
- 晴方啾 - 晴方啾
- 课代表阿伟 - 课代表阿伟

5
app.config.ts Normal file
View File

@@ -0,0 +1,5 @@
export default defineAppConfig({
theme: {
primaryColor: '#ababab',
},
})

View File

@@ -1,6 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { installPrompt } from './utils/pwa' import { useIndexedDB } from '~/composables/db'
import { appName } from '~/constants' import { appName } from '~/constants'
import { installPrompt } from './utils/pwa'
// https://nuxt.com/docs/api/composables/use-head // https://nuxt.com/docs/api/composables/use-head
useHead({ useHead({

View File

@@ -8,7 +8,10 @@ const props = defineProps({
const rStore = useRecipeStore() const rStore = useRecipeStore()
const { displayedRecipe } = storeToRefs(rStore) const { displayedRecipe } = storeToRefs(rStore)
const showBasketBtn = computed(async () => { /**
* Show basket button if there are recipes in the basket
*/
const showBasketBtn = computed(() => {
return displayedRecipe.value.length !== rStore.recipesLength && props.isVisible return displayedRecipe.value.length !== rStore.recipesLength && props.isVisible
}) })
</script> </script>

View File

@@ -1,10 +1,10 @@
<script lang="ts" setup> <script lang="ts" setup>
import { storeToRefs } from 'pinia'
import type { StuffItem } from '~/types' import type { StuffItem } from '~/types'
import { meat, staple, tools, vegetable } from '~/data/food' import { storeToRefs } from 'pinia'
import { useEmojiAnimation } from '~/composables/animation' import { useEmojiAnimation } from '~/composables/animation'
import { meat, staple, tools, vegetable } from '~/data/food'
const rStore = useRecipeStore() const rStore = useRecipeStore()
const { curTool } = storeToRefs(rStore) const { curTool } = storeToRefs(rStore)
const curStuff = computed(() => rStore.selectedStuff) const curStuff = computed(() => rStore.selectedStuff)

View File

@@ -10,7 +10,7 @@ defineProps<{
<template> <template>
<Disclosure v-slot="{ open }" :default-open="defaultOpen" 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 text-blue-900 font-medium hover:bg-blue-200 focus:outline-none focus-visible:ring focus-visible:ring-blue-500 focus-visible:ring-opacity-75"
> >
<span>{{ title }}</span> <span>{{ title }}</span>
<div <div

View File

@@ -16,11 +16,12 @@ const { random, randomRecipes } = useRandomRecipe(count)
</button> </button>
</div> </div>
<div v-show="randomRecipes.length > 0"> <button cursor-pointer class="inline-flex inline-flex items-center justify-center rounded-md border-none bg-blue-600 px-3 py-1.5 text-sm text-white font-semibold leading-6 shadow-sm hover:bg-blue-500 focus-visible:outline-2 focus-visible:outline-blue-600 focus-visible:outline-offset-2 focus-visible:outline" @click="random">
<button cursor-pointer class="inline-flex inline-flex items-center justify-center rounded-md border-none bg-blue-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-blue-500 focus-visible:outline-2 focus-visible:outline-blue-600 focus-visible:outline-offset-2 focus-visible:outline" @click="random">
<div class="transition" hover="text-blue-500" i-ri-refresh-line mr-1 inline-flex /> <div class="transition" hover="text-blue-500" i-ri-refresh-line mr-1 inline-flex />
<div>随机一下</div> <div>随机一下</div>
</button> </button>
<div v-show="randomRecipes.length > 0">
<div m="t-8" flex="~ col"> <div m="t-8" flex="~ col">
<template v-for="recipe, i in randomRecipes" :key="i"> <template v-for="recipe, i in randomRecipes" :key="i">
<DishTag v-if="recipe" :dish="recipe" /> <DishTag v-if="recipe" :dish="recipe" />

View File

@@ -56,7 +56,7 @@ const showTooltip = computed(() => !selectedStuff.value.length && !curTool.value
<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="text-blue-600 font-bold dark:text-blue-400" href="https://docs.qq.com/sheet/DQk1vdkhFV0twQVNS?tab=uykkic" target="_blank">这里</a>
<span>反馈新的菜谱</span> <span>反馈新的菜谱</span>
</div> </div>
</div> </div>

View File

@@ -43,22 +43,22 @@ defineProps<{
left: 0; left: 0;
right: 0; right: 0;
bottom: 0; bottom: 0;
-webkit-transition: .4s; -webkit-transition: 0.4s;
transition: .4s; transition: 0.4s;
} }
$size: 20px; $size: 20px;
.slider:before { .slider:before {
position: absolute; position: absolute;
content: ""; content: '';
height: $size; height: $size;
width: $size; width: $size;
left: 4px; left: 4px;
bottom: 4px; bottom: 4px;
background-color: white; background-color: white;
-webkit-transition: .4s; -webkit-transition: 0.4s;
transition: .4s; transition: 0.4s;
} }
input:checked + .slider { input:checked + .slider {

View File

@@ -25,7 +25,7 @@ const filteredRecipes = computedAsync(async () => {
<template> <template>
<YlfIconButton <YlfIconButton
absolute right-3 top-5 absolute right-4 top-4
class="icon-btn hover:text-yellow-400 !outline-none" class="icon-btn hover:text-yellow-400 !outline-none"
text-xl text-xl
title="切换" @click="openModal" title="切换" @click="openModal"
@@ -44,7 +44,7 @@ const filteredRecipes = computedAsync(async () => {
leave-from="opacity-100" leave-from="opacity-100"
leave-to="opacity-0" leave-to="opacity-0"
> >
<div class="fixed inset-0" /> <div class="fixed inset-0 bg-black/10" />
</TransitionChild> </TransitionChild>
<div class="fixed inset-0 overflow-y-auto"> <div class="fixed inset-0 overflow-y-auto">
@@ -62,7 +62,7 @@ const filteredRecipes = computedAsync(async () => {
> >
<DialogPanel <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" class="h-full max-w-xl w-full transform overflow-hidden bg-white p-4 text-left align-middle shadow-xl transition-all dark:bg-dark-600"
md="mt-4 rounded-2xl" md="rounded-2xl"
overflow="auto" overflow="auto"
flex="~ col" flex="~ col"
> >
@@ -89,9 +89,9 @@ const filteredRecipes = computedAsync(async () => {
@click="keyword = ''" @click="keyword = ''"
/> />
</div> </div>
<div ml-2 inline-flex op="70" text-base @click="closeModal"> <button op="70" ml-2 inline-flex cursor-pointer text-base @click="closeModal">
取消 取消
</div> </button>
</DialogTitle> </DialogTitle>
<div flex="~ col grow" overflow="auto" class="mt-2" text-xs> <div flex="~ col grow" overflow="auto" class="mt-2" text-xs>
<DishTag v-for="item, i in filteredRecipes" :key="i" :dish="item" /> <DishTag v-for="item, i in filteredRecipes" :key="i" :dish="item" />

View File

@@ -1,5 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import VueAboutMe from 'vue-about-me' import { VueAboutMe } from 'vue-about-me'
import 'vue-about-me/style.css' import 'vue-about-me/style.css'
const color = useColorMode() const color = useColorMode()

View File

@@ -0,0 +1,29 @@
<template>
<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>
</template>

View File

@@ -1,9 +1,9 @@
<script lang="ts" setup> <script lang="ts" setup>
import pkg from '~/package.json' import pkg from '~/../package.json'
const commitSha = (import.meta.env.VITE_COMMIT_REF || '').slice(0, 7) const commitSha = (import.meta.env.VITE_COMMIT_REF || '').slice(0, 7)
const now = import.meta.env.VITE_APP_BUILD_TIME const now = import.meta.env.VITE_APP_BUILD_TIME
const buildDate = (new Date(Number.parseInt(now) * 1000)).toLocaleDateString() const buildDate = (new Date(Number.parseInt(now))).toLocaleDateString()
</script> </script>
<template> <template>

View File

@@ -40,7 +40,9 @@ defineProps<{
font-size: 0.8rem; font-size: 0.8rem;
} }
tr, th, td { tr,
th,
td {
border: 1px solid black; border: 1px solid black;
} }
</style> </style>

View File

@@ -0,0 +1,11 @@
<template>
<div
class="tagSize pureTag rounded"
p="x-2"
border="~ yellow-200 dark:yellow-800"
bg="yellow-300 opacity-20"
text="yellow-800 dark:yellow-200"
>
<slot />
</div>
</template>

View File

@@ -1,9 +1,9 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { DbRecipeItem } from '~/utils/db'
import { tools } from '~/data/food'
import type { RecipeItem } from '~/types' import type { RecipeItem } from '~/types'
import { getEmojisFromStuff } from '~/utils' import type { DbRecipeItem } from '~/utils/db'
import { recipeHistories } from '~/composables/store/history' import { recipeHistories } from '~/composables/store/history'
import { tools } from '~/data/food'
import { getEmojisFromStuff } from '~/utils'
const props = defineProps<{ const props = defineProps<{
dish: RecipeItem | DbRecipeItem dish: RecipeItem | DbRecipeItem
@@ -42,11 +42,11 @@ const dishLabel = computed(() => {
bg="blue-300 opacity-20" bg="blue-300 opacity-20"
@click="triggerGtm(dish)" @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" text="sm blue-700 dark:blue-200">
{{ dishLabel }} {{ dishLabel }}
</span> </span>
<span v-for="tool, i in tools" :key="i" inline-flex> <template v-for="tool, i in tools">
<div v-if="dish.tools?.includes(tool.name)" :class="tool.icon" /> <span v-if="dish.tools?.includes(tool.name)" :key="i" :class="tool.icon" />
</span> </template>
</a> </a>
</template> </template>

View File

@@ -5,12 +5,12 @@ defineProps<{
</script> </script>
<template> <template>
<span <button
class="meat-tag rounded tag" p="x-2" class="meat-tag rounded tag" p="x-2"
border="~ red-200 dark:red-800" border="~ red-200 dark:red-800"
:bg="active ? 'red-500 opacity-90' : 'red-300 opacity-20'" :bg="active ? 'red-500 opacity-90' : 'red-300 opacity-20'"
:text="active ? 'red-100' : 'red-800 dark:red-200'" :text="active ? 'red-100' : 'red-800 dark:red-200'"
> >
<slot /> <slot />
</span> </button>
</template> </template>

View File

@@ -5,11 +5,11 @@ defineProps<{
</script> </script>
<template> <template>
<span <button
class="rounded tag" p="x-2" border="~ yellow-200 dark:yellow-800" class="rounded tag" p="x-2" border="~ yellow-200 dark:yellow-800"
:bg="active ? 'yellow-500 dark:yellow-600 opacity-100' : 'yellow-300 opacity-20'" :bg="active ? 'yellow-500 dark:yellow-600 opacity-100' : 'yellow-300 opacity-20'"
:text="active ? 'yellow-100' : 'yellow-800 dark:yellow-200'" :text="active ? 'yellow-100' : 'yellow-800 dark:yellow-200'"
> >
<slot /> <slot />
</span> </button>
</template> </template>

View File

@@ -5,12 +5,12 @@ defineProps<{
</script> </script>
<template> <template>
<span <button
class="rounded tag" p="x-2" class="rounded tag" p="x-2"
border="~ stone-200 dark:stone-600" border="~ stone-200 dark:stone-600"
:bg="active ? 'stone-600 opacity-100' : 'stone-300 opacity-5'" :bg="active ? 'stone-600 opacity-100' : 'stone-300 opacity-5'"
:text="active ? 'stone-100' : 'stone-800 dark:stone-200'" :text="active ? 'stone-100' : 'stone-800 dark:stone-200'"
> >
<slot /> <slot />
</span> </button>
</template> </template>

View File

@@ -5,12 +5,12 @@ defineProps<{
</script> </script>
<template> <template>
<span <button
class="vegetable-tag rounded tag" p="x-2" class="vegetable-tag rounded tag" p="x-2"
border="~ green-200 dark:green-800" border="~ green-200 dark:green-800"
:bg="active ? 'green-600 opacity-90' : 'green-300 opacity-20'" :bg="active ? 'green-600 opacity-90' : 'green-300 opacity-20'"
:text="active ? 'green-100' : 'green-800 dark:green-200'" :text="active ? 'green-100' : 'green-800 dark:green-200'"
> >
<slot /> <slot />
</span> </button>
</template> </template>

View File

@@ -1,5 +1,5 @@
import { isClient } from '@vueuse/core'
import type { Ref } from 'vue' import type { Ref } from 'vue'
import { isClient } from '@vueuse/core'
export function useEmojiAnimation(recipeBtn: Ref<HTMLButtonElement | undefined>) { export function useEmojiAnimation(recipeBtn: Ref<HTMLButtonElement | undefined>) {
const { x, y } = usePointer() const { x, y } = usePointer()

View File

@@ -1,5 +1,6 @@
import { useStorage } from '@vueuse/core' import { useStorage } from '@vueuse/core'
import { lastDbUpdated, namespace } from '~/constants' import { lastDbUpdated, namespace } from '~/constants'
import { db, initDb } from '~/utils/db'
export function useIndexedDB() { export function useIndexedDB() {
const dbUpdated = useStorage(`${namespace}:lastDbUpdated`, lastDbUpdated) const dbUpdated = useStorage(`${namespace}:lastDbUpdated`, lastDbUpdated)

View File

@@ -1,11 +1,13 @@
import type { DbRecipeItem } from '~/utils/db' import type { DbRecipeItem } from '~/utils/db'
import { useStorage } from '@vueuse/core'
import { namespace } from '~/constants'
/** /**
* *
* @param total * @param total
*/ */
export function useRandomRecipe(total: Ref<number>) { export function useRandomRecipe(total: Ref<number>) {
const randomRecipes = ref<(DbRecipeItem | undefined)[]>([]) const randomRecipes = useStorage<(DbRecipeItem | undefined)[]>(`${namespace}:random:recipes`, [])
async function random() { async function random() {
const length = await db.recipes.count() const length = await db.recipes.count()
const randomArr = generateRandomArray(length, total.value) const randomArr = generateRandomArray(length, total.value)
@@ -19,6 +21,8 @@ export function useRandomRecipe(total: Ref<number>) {
}) })
onMounted(() => { onMounted(() => {
// 如果没有随机菜谱,就生成一次
if (randomRecipes.value.length <= 0)
random() random()
}) })

View File

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

View File

@@ -1,6 +1,6 @@
import type { RecipeItem } from '~/types'
import { useStorage } from '@vueuse/core' import { useStorage } from '@vueuse/core'
import { namespace } from '~/constants' import { namespace } from '~/constants'
import type { RecipeItem } from '~/types'
export interface RecipeHistoryItem { export interface RecipeHistoryItem {
recipe: RecipeItem recipe: RecipeItem

View File

@@ -1,10 +1,10 @@
import { acceptHMRUpdate, defineStore } from 'pinia' import type { RecipeItem, StuffItem } from '~/types'
import { useStorage } from '@vueuse/core'
import { computed, onMounted, ref, watch } from 'vue'
import { useGtm } from '@gtm-support/vue-gtm' import { useGtm } from '@gtm-support/vue-gtm'
import { useStorage } from '@vueuse/core'
import { acceptHMRUpdate, defineStore } from 'pinia'
import { computed, onMounted, ref, watch } from 'vue'
import { db } from '../../utils/db' import { db } from '../../utils/db'
import { useAppStore } from './app' import { useAppStore } from './app'
import type { RecipeItem, StuffItem } from '~/types'
const namespace = 'cook' const namespace = 'cook'
@@ -78,8 +78,6 @@ export const useRecipeStore = defineStore('recipe', () => {
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,6 +114,9 @@ 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
} }

View File

@@ -1,5 +1,5 @@
import process from 'node:process'
import type { ModuleOptions } from '@vite-pwa/nuxt' import type { ModuleOptions } from '@vite-pwa/nuxt'
import process from 'node:process'
import { appDescription, appName } from '../constants/index' import { appDescription, appName } from '../constants/index'
const scope = '/' const scope = '/'

View File

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

@@ -296,7 +296,7 @@ BBQ烟熏手撕猪肉,猪肉,BV1DV411x7SH,复杂,,烤,烤箱,
名古屋鸡翅,鸡肉,BV1ET4y1A7Xd,普通,日式,,一口大锅, 名古屋鸡翅,鸡肉,BV1ET4y1A7Xd,普通,日式,,一口大锅,
日式炖白萝,白萝卜,BV17b411B7H1,简单,日式,,一口大锅, 日式炖白萝,白萝卜,BV17b411B7H1,简单,日式,,一口大锅,
炸虾天妇罗,,BV1e5411t7LY,困难,日式,,一口大锅, 炸虾天妇罗,,BV1e5411t7LY,困难,日式,,一口大锅,
可乐饼,土豆、洋葱、肉、鸡蛋,BV1yW411Q7sa,普通,日式菜,,一口大锅, 可乐饼,土豆、洋葱、肉、鸡蛋,BV17x411U75q,普通,日式菜,,一口大锅,
清炒莴笋丝,莴笋、胡萝卜,BV1qK411H7RL,简单,爽口,,一口大锅, 清炒莴笋丝,莴笋、胡萝卜,BV1qK411H7RL,简单,爽口,,一口大锅,
莴笋泡菜,莴笋,BV1h741127rS,简单,爽口,泡菜,一口大锅, 莴笋泡菜,莴笋,BV1h741127rS,简单,爽口,泡菜,一口大锅,
口蘑汤,菌菇,BV1e64y1h776,简单,,煎、炖,一口大锅, 口蘑汤,菌菇,BV1e64y1h776,简单,,煎、炖,一口大锅,
@@ -597,3 +597,4 @@ biangbiang面,面食,BV1844y157GL,简单,,油泼,一口大锅,
电饭煲番茄牛肉焖饭,番茄、牛肉、米,BV1Bv411C7X3,普通,,,一口大锅、电饭煲, 电饭煲番茄牛肉焖饭,番茄、牛肉、米,BV1Bv411C7X3,普通,,,一口大锅、电饭煲,
电饭煲排骨土豆焖饭,猪肉、土豆、米,BV1Bv411C7X3,普通,,,一口大锅、电饭煲, 电饭煲排骨土豆焖饭,猪肉、土豆、米,BV1Bv411C7X3,普通,,,一口大锅、电饭煲,
米布丁,米、鸡蛋,BV1hr4y1k7A5,简单,,,一口大锅、电饭煲, 米布丁,米、鸡蛋,BV1hr4y1k7A5,简单,,,一口大锅、电饭煲,
麻婆豆腐,豆腐,BV1it4y1X75m,简单,,,一口大锅,
1 name stuff bv difficulty tags methods tools
296 名古屋鸡翅 鸡肉 BV1ET4y1A7Xd 普通 日式 一口大锅
297 日式炖白萝 白萝卜 BV17b411B7H1 简单 日式 一口大锅
298 炸虾天妇罗 BV1e5411t7LY 困难 日式 一口大锅
299 可乐饼 土豆、洋葱、肉、鸡蛋 BV1yW411Q7sa BV17x411U75q 普通 日式菜 一口大锅
300 清炒莴笋丝 莴笋、胡萝卜 BV1qK411H7RL 简单 爽口 一口大锅
301 莴笋泡菜 莴笋 BV1h741127rS 简单 爽口 泡菜 一口大锅
302 口蘑汤 菌菇 BV1e64y1h776 简单 煎、炖 一口大锅
597 电饭煲番茄牛肉焖饭 番茄、牛肉、米 BV1Bv411C7X3 普通 一口大锅、电饭煲
598 电饭煲排骨土豆焖饭 猪肉、土豆、米 BV1Bv411C7X3 普通 一口大锅、电饭煲
599 米布丁 米、鸡蛋 BV1hr4y1k7A5 简单 一口大锅、电饭煲
600 麻婆豆腐 豆腐 BV1it4y1X75m 简单 一口大锅

1
app/data/recipe.json Normal file

File diff suppressed because one or more lines are too long

View File

@@ -6,9 +6,9 @@ By default, `default.vue` will be used unless an alternative is specified in the
```html ```html
<script setup lang="ts"> <script setup lang="ts">
definePageMeta({ definePageMeta({
layout: 'home', layout: 'home',
}) })
</script> </script>
``` ```

View File

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

View File

@@ -1,4 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import { defaultCookbook } from '~/utils'
definePageMeta({ definePageMeta({
layout: 'child', layout: 'child',
title: '自定义菜谱', title: '自定义菜谱',

141
app/pages/help.vue Normal file
View File

@@ -0,0 +1,141 @@
<template>
<div>
<div class="w-full">
<CommonHeader>
帮助
</CommonHeader>
<InstallPwa />
<FeedbackActions />
<div class="mx-auto max-w-md w-full rounded-2xl p-2" text-left>
<FAQItem title="未来计划?">
计划增加新功能如自定义菜谱与使用其他用户分享的菜谱
</FAQItem>
<FAQItem title="什么是模式?">
<ul>
<li><b>模糊匹配</b>展示所有含当前选中任意食材的菜谱</li>
<li><b>精准匹配</b>展示所有含当前选中所有食材的菜谱</li>
<li><b>生存模式</b>展示当前选中食材即可制作的所有菜谱</li>
</ul>
</FAQItem>
<FAQItem title="如何快速清空所选食材和工具?">
<div inline-flex items-center justify-center>
点击顶部 <div i-mdi-pot-steam-outline mx-1 inline-block /> 图标即可
</div>
</FAQItem>
<FAQItem title="是否有微信小程序?">
因不可抗力小程序因跳转 B 站视频而被判定为导流违规下架
将不再提供小程序版本
<br>
<br>
搜索微信公众号<b>云游君</b>并发送<b>做菜</b>也可以快速找到本网站
</FAQItem>
<FAQItem title="是否有 APP?">
<b>暂时没有开发 APP 的计划</b>
<br>
但我们正在优化 <b>PWA</b> 的体验以便您可以直接将本站添加到桌面并享受<b>类似 APP 的体验</b>
<br>
你可以使用浏览器打开点击上方的<b>安装到桌面</b>或在菜单中点击<b>添加到主屏幕</b>
</FAQItem>
<FAQItem title="未来是否会收费?">
该项目将以免费开源的形式运营
<br>
您可以考虑赞助本项目以支持我们的开发
我会将其投入在周边的服务器域名CDN 等费用上
<ul mt-1>
<li>
<a href="https://afdian.net/a/yunyoujun" target="_blank">爱发电赞助</a>
</li>
<li>
<a href="https://sponsors.yunyoujun.cn/" target="_blank">我要直接打钱</a>
</li>
</ul>
</FAQItem>
<FAQItem title="页面无法点击、资源加载失败?">
<blockquote>
试试无痕模式是否正常
</blockquote>
<br>
<ol>
<li>
<b>清除 Cookie</b>
<ol>
<li>
点击浏览器网址前方的 🔒 图标
</li>
<li>
点击Cookie并清除
</li>
</ol>
</li>
<li>
<b>强制刷新缓存</b>
<ul>
<li>Windows: <code>Ctrl + F5</code></li>
<li>macOS: <code>Cmd + Shift + R</code></li>
</ul>
</li>
</ol>
</FAQItem>
<hr h="1" my="4" bg-black>
<HelpAbout />
<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>
晴方啾
</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>
<BaseFooter mt-4 />
</div>
</template>

24
app/pages/index.vue Normal file
View File

@@ -0,0 +1,24 @@
<script lang="ts" setup>
const rStore = useRecipeStore()
</script>
<template>
<div>
<div text-4xl>
<button
class="cursor-pointer transition active:text-green-800 hover:(text-green-600)"
title="重置"
@click="rStore.reset"
>
<div v-if="rStore.selectedStuff.length" i-mdi-pot-steam-outline />
<div v-else i-mdi-pot-mix-outline />
</button>
</div>
<p text="sm" m="b-4">
好的今天我们来做菜
</p>
<ChooseFood />
<SimpleCopyright />
</div>
</template>

View File

@@ -16,21 +16,21 @@ function clearAllHistory() {
<template> <template>
<div pt-2> <div pt-2>
<div <button
text="blue-900 dark:blue-200" text="blue-900 dark:blue-200"
bg="blue-300 op-20 hover:(blue-800 op-20) dark:hover:(blue-200 op-20)" 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" 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" @click="clearAllHistory"
> >
<div i-ri-eraser-line /> <span i-ri-eraser-line />
<span class="ml-1">清空记录</span> <span class="ml-1">清空记录</span>
</div> </button>
<div flex="~ col"> <div flex="~ col">
<div v-for="history in recipeHistories" :key="history.recipe.name" mt-2> <div v-for="history in recipeHistories" :key="history.recipe.name" mt-2>
<StapleTag :active="false"> <DateTag>
{{ dayjs(history.time).format('YYYY-MM-DD HH:mm:ss') }} {{ dayjs(history.time).format('YYYY-MM-DD HH:mm:ss') }}
</StapleTag> </DateTag>
<DishTag :dish="history.recipe" /> <DishTag :dish="history.recipe" />
</div> </div>
</div> </div>

View File

@@ -8,17 +8,17 @@ import { links } from '~/constants'
我的 我的
</CommonHeader> </CommonHeader>
<div mt-4 gap="3" grid="~ cols-3" px-2>
<YlfIconItem to="/recipes/history" icon="i-ri-history-line" label="历史记录" />
<YlfIconItem to="/recipes/collect" icon="i-ri-star-line" label="我的收藏" />
<YlfIconItem to="/cookbooks" icon="i-ri-article-line" label="自定义菜谱" />
</div>
<div <div
class="mx-auto max-w-md w-full" class="mx-auto max-w-md w-full"
px-2 px-2
text-left 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>
<YlfForm> <YlfForm>
<YlfFormItem icon="i-ri-feedback-line" label="立即反馈" :to="links.feedback" target="_blank" /> <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" /> <YlfFormItem icon="i-ri-mail-send-line" label="立即投稿" :to="links.contribute" target="_blank" />

View File

@@ -12,7 +12,6 @@ body,
background: var(--c-bg); background: var(--c-bg);
} }
input:focus { input:focus {
outline: none; outline: none;
} }
@@ -22,7 +21,7 @@ input:focus {
} }
#nprogress .bar { #nprogress .bar {
background: rgb(13,148,136); background: rgb(13, 148, 136);
opacity: 0.75; opacity: 0.75;
position: fixed; position: fixed;
z-index: 1031; z-index: 1031;
@@ -55,11 +54,13 @@ button {
} }
} }
hr {opacity: 0.1;} hr {
opacity: 0.1;
}
.tag { .tag,
.tagSize {
margin: 4px; margin: 4px;
padding: 2px 4px; padding: 2px 4px;
// border: 1px solid var(--c-text); // border: 1px solid var(--c-text);
} }

View File

@@ -10,7 +10,7 @@ blockquote {
border-left: 0.25em solid #ddd; border-left: 0.25em solid #ddd;
padding: 0 1em; padding: 0 1em;
color: #777; color: #777;
quotes: '\\201C''\\201D''\\2018''\\2019'; quotes: '\\201C' '\\201D' '\\2018' '\\2019';
} }
ol { ol {

View File

@@ -1,8 +1,8 @@
import type { Table } from 'dexie' import type { Table } from 'dexie'
import Dexie from 'dexie'
import type { RecipeItem } from '~/types' import type { RecipeItem } from '~/types'
import Dexie from 'dexie'
export interface DbRecipeItem extends RecipeItem { export interface DbRecipeItem extends RecipeItem {
id?: number id?: number
} }

View File

@@ -1,5 +1,7 @@
import { meat, staple, vegetable } from '~/data/food' import { meat, staple, vegetable } from '~/data/food'
export * from './cookbook'
const foodItems = [...vegetable, ...meat, ...staple] const foodItems = [...vegetable, ...meat, ...staple]
const foodEmojiMap = new Map() const foodEmojiMap = new Map()
foodItems.forEach((item) => { foodItems.forEach((item) => {

File diff suppressed because one or more lines are too long

View File

@@ -1,7 +1,17 @@
// @ts-check
import antfu from '@antfu/eslint-config' import antfu from '@antfu/eslint-config'
import unocss from '@unocss/eslint-config/flat' import nuxt from './.nuxt/eslint.config.mjs'
export default antfu( export default nuxt(
{}, antfu(
unocss, {
unocss: true,
formatters: true,
},
{
ignores: [
'app/data/*.json',
],
},
),
) )

Some files were not shown because too many files have changed in this diff Show More