Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
65fea75825 | ||
|
|
5e97da4731 | ||
|
|
78fa350ced | ||
|
|
11283f6f94 | ||
|
|
e9b4d3ac38 | ||
|
|
7fe1f95d9d | ||
|
|
86b565dfa9 | ||
|
|
41bdc3346f | ||
|
|
7a52b024dd | ||
|
|
10073b3a07 | ||
|
|
778594652c | ||
|
|
b00b52cba8 | ||
|
|
ab7f4e20f1 | ||
|
|
d42e3cf65d | ||
|
|
f59c34499a | ||
|
|
e741f5d5a0 | ||
|
|
99d7d660bb | ||
|
|
87025cd17e | ||
|
|
68a4b2dbeb | ||
|
|
1ab402af8b | ||
|
|
b2cf053446 | ||
|
|
37bab9269c | ||
|
|
44f9b40ee5 | ||
|
|
4ddd5ac2d2 | ||
|
|
9df47e977e | ||
|
|
5fb76f24dc | ||
|
|
014f129b8e | ||
|
|
2949c45839 | ||
|
|
81586158d5 | ||
|
|
bfb6ea3e14 | ||
|
|
7d26f9ba84 | ||
|
|
9f12401922 | ||
|
|
69c689df67 | ||
|
|
8ca8c4aac8 |
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
- name: Set node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16.x
|
||||
node-version: lts/*
|
||||
cache: pnpm
|
||||
|
||||
- name: Install
|
||||
@@ -43,7 +43,7 @@ jobs:
|
||||
- name: Set node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16.x
|
||||
node-version: lts/*
|
||||
cache: pnpm
|
||||
|
||||
- name: Install
|
||||
@@ -60,7 +60,7 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [16.x]
|
||||
node-version: [lts/*]
|
||||
os: [ubuntu-latest]
|
||||
fail-fast: false
|
||||
|
||||
|
||||
24
.github/workflows/vercel.yml
vendored
Normal file
24
.github/workflows/vercel.yml
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
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 Vercel CLI
|
||||
run: npm install --global 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
1
.gitignore
vendored
@@ -17,3 +17,4 @@ dist
|
||||
.nuxt
|
||||
.env
|
||||
.idea/
|
||||
.vercel
|
||||
|
||||
2
.npmrc
2
.npmrc
@@ -1,3 +1,5 @@
|
||||
shamefully-hoist=true
|
||||
strict-peer-dependencies=false
|
||||
shell-emulator=true
|
||||
auto-install-peers=false
|
||||
ignore-workspace-root-check=true
|
||||
|
||||
68
.vscode/settings.json
vendored
68
.vscode/settings.json
vendored
@@ -1,11 +1,24 @@
|
||||
{
|
||||
"cSpell.words": ["Vitesse", "Vite", "unocss", "vitest", "vueuse", "pinia", "demi", "antfu", "iconify", "intlify", "vitejs", "unplugin", "pnpm"],
|
||||
"cSpell.words": [
|
||||
"antfu",
|
||||
"demi",
|
||||
"iconify",
|
||||
"intlify",
|
||||
"nuxi",
|
||||
"pinia",
|
||||
"pnpm",
|
||||
"unocss",
|
||||
"unplugin",
|
||||
"Vite",
|
||||
"vitejs",
|
||||
"Vitesse",
|
||||
"vitest",
|
||||
"vueuse"
|
||||
],
|
||||
"files.associations": {
|
||||
"*.css": "postcss"
|
||||
},
|
||||
|
||||
// Enable the ESlint flat config support
|
||||
"eslint.experimental.useFlatConfig": true,
|
||||
// Disable the default formatter, use eslint instead
|
||||
"prettier.enable": false,
|
||||
"editor.formatOnSave": false,
|
||||
@@ -16,43 +29,17 @@
|
||||
},
|
||||
// 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"
|
||||
}
|
||||
{ "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",
|
||||
@@ -65,5 +52,6 @@
|
||||
"json",
|
||||
"jsonc",
|
||||
"yaml"
|
||||
]
|
||||
],
|
||||
"typescript.tsdk": "node_modules/typescript/lib"
|
||||
}
|
||||
|
||||
@@ -73,8 +73,6 @@ docker stop cook
|
||||
感谢以下小伙伴为本项目提供的数据支持和 QA !
|
||||
|
||||
- [Runny](https://weibo.com/runny)
|
||||
- 山竹太凉
|
||||
- leo
|
||||
- 麒麟
|
||||
- 晴方啾
|
||||
- 课代表阿伟
|
||||
|
||||
5
app.config.ts
Normal file
5
app.config.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export default defineAppConfig({
|
||||
theme: {
|
||||
primaryColor: '#ababab',
|
||||
},
|
||||
})
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { installPrompt } from './utils/pwa'
|
||||
import { useIndexedDB } from '~/composables/db'
|
||||
import { appName } from '~/constants'
|
||||
import { installPrompt } from './utils/pwa'
|
||||
|
||||
// https://nuxt.com/docs/api/composables/use-head
|
||||
useHead({
|
||||
@@ -8,7 +8,10 @@ const props = defineProps({
|
||||
const rStore = useRecipeStore()
|
||||
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
|
||||
})
|
||||
</script>
|
||||
@@ -1,10 +1,10 @@
|
||||
<script lang="ts" setup>
|
||||
import { storeToRefs } from 'pinia'
|
||||
import type { StuffItem } from '~/types'
|
||||
import { meat, staple, tools, vegetable } from '~/data/food'
|
||||
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useEmojiAnimation } from '~/composables/animation'
|
||||
|
||||
import { meat, staple, tools, vegetable } from '~/data/food'
|
||||
|
||||
const rStore = useRecipeStore()
|
||||
const { curTool } = storeToRefs(rStore)
|
||||
const curStuff = computed(() => rStore.selectedStuff)
|
||||
@@ -10,7 +10,7 @@ defineProps<{
|
||||
<template>
|
||||
<Disclosure v-slot="{ open }" :default-open="defaultOpen" as="div" class="mt-2">
|
||||
<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>
|
||||
<div
|
||||
@@ -16,11 +16,12 @@ const { random, randomRecipes } = useRandomRecipe(count)
|
||||
</button>
|
||||
</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 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">
|
||||
<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">
|
||||
<div class="transition" hover="text-blue-500" i-ri-refresh-line mr-1 inline-flex />
|
||||
<div>随机一下</div>
|
||||
</button>
|
||||
|
||||
<div v-show="randomRecipes.length > 0">
|
||||
<div m="t-8" flex="~ col">
|
||||
<template v-for="recipe, i in randomRecipes" :key="i">
|
||||
<DishTag v-if="recipe" :dish="recipe" />
|
||||
@@ -56,7 +56,7 @@ const showTooltip = computed(() => !selectedStuff.value.length && !curTool.value
|
||||
<br>
|
||||
<div m="t-1">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
@@ -43,22 +43,22 @@ defineProps<{
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
-webkit-transition: .4s;
|
||||
transition: .4s;
|
||||
-webkit-transition: 0.4s;
|
||||
transition: 0.4s;
|
||||
}
|
||||
|
||||
$size: 20px;
|
||||
|
||||
.slider:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
content: '';
|
||||
height: $size;
|
||||
width: $size;
|
||||
left: 4px;
|
||||
bottom: 4px;
|
||||
background-color: white;
|
||||
-webkit-transition: .4s;
|
||||
transition: .4s;
|
||||
-webkit-transition: 0.4s;
|
||||
transition: 0.4s;
|
||||
}
|
||||
|
||||
input:checked + .slider {
|
||||
105
app/components/common/SearchRecipe.vue
Normal file
105
app/components/common/SearchRecipe.vue
Normal 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-4 top-4
|
||||
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>
|
||||
<button op="70" ml-2 inline-flex cursor-pointer text-base @click="closeModal">
|
||||
取消
|
||||
</button>
|
||||
</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>
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import VueAboutMe from 'vue-about-me'
|
||||
import { VueAboutMe } from 'vue-about-me'
|
||||
import 'vue-about-me/style.css'
|
||||
|
||||
const color = useColorMode()
|
||||
29
app/components/help/HelpAbout.vue
Normal file
29
app/components/help/HelpAbout.vue
Normal 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>
|
||||
@@ -1,9 +1,9 @@
|
||||
<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 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>
|
||||
|
||||
<template>
|
||||
@@ -40,7 +40,9 @@ defineProps<{
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
tr, th, td {
|
||||
tr,
|
||||
th,
|
||||
td {
|
||||
border: 1px solid black;
|
||||
}
|
||||
</style>
|
||||
11
app/components/tags/DateTag.vue
Normal file
11
app/components/tags/DateTag.vue
Normal 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>
|
||||
@@ -1,7 +1,8 @@
|
||||
<script lang="ts" setup>
|
||||
import type { DbRecipeItem } from '~/utils/db'
|
||||
import { tools } from '~/data/food'
|
||||
import type { RecipeItem } from '~/types'
|
||||
import type { DbRecipeItem } from '~/utils/db'
|
||||
import { recipeHistories } from '~/composables/store/history'
|
||||
import { tools } from '~/data/food'
|
||||
import { getEmojisFromStuff } from '~/utils'
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -10,16 +11,21 @@ const props = defineProps<{
|
||||
|
||||
const gtm = useGtm()
|
||||
|
||||
function triggerGtm(val: string) {
|
||||
function triggerGtm(dish: RecipeItem) {
|
||||
recipeHistories.value.push({
|
||||
recipe: dish,
|
||||
time: Date.now(),
|
||||
})
|
||||
|
||||
gtm?.trackEvent({
|
||||
event: 'click',
|
||||
category: `dish_${val}`,
|
||||
category: `dish_${dish.name}`,
|
||||
action: 'click_recipe',
|
||||
label: '跳转菜谱',
|
||||
})
|
||||
gtm?.trackEvent({
|
||||
event: 'click_dish',
|
||||
action: val,
|
||||
action: dish.name,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -34,13 +40,13 @@ const dishLabel = computed(() => {
|
||||
:href="dish.link || `https://www.bilibili.com/video/${dish.bv}`" target="_blank" class="dish-tag rounded tag" p="x-2"
|
||||
border="~ blue-200 dark:blue-800"
|
||||
bg="blue-300 opacity-20"
|
||||
@click="triggerGtm(dish.name)"
|
||||
@click="triggerGtm(dish)"
|
||||
>
|
||||
<span m="r-1" class="inline-flex items-center justify-center" text="sm blue-700 dark:blue-200">
|
||||
<span m="r-1" text="sm blue-700 dark:blue-200">
|
||||
{{ dishLabel }}
|
||||
</span>
|
||||
<span v-for="tool, i in tools" :key="i" inline-flex>
|
||||
<div v-if="dish.tools?.includes(tool.name)" :class="tool.icon" />
|
||||
</span>
|
||||
<template v-for="tool, i in tools">
|
||||
<span v-if="dish.tools?.includes(tool.name)" :key="i" :class="tool.icon" />
|
||||
</template>
|
||||
</a>
|
||||
</template>
|
||||
@@ -5,12 +5,12 @@ defineProps<{
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span
|
||||
<button
|
||||
class="meat-tag rounded tag" p="x-2"
|
||||
border="~ red-200 dark:red-800"
|
||||
:bg="active ? 'red-500 opacity-90' : 'red-300 opacity-20'"
|
||||
:text="active ? 'red-100' : 'red-800 dark:red-200'"
|
||||
>
|
||||
<slot />
|
||||
</span>
|
||||
</button>
|
||||
</template>
|
||||
@@ -5,11 +5,11 @@ defineProps<{
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span
|
||||
<button
|
||||
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'"
|
||||
:text="active ? 'yellow-100' : 'yellow-800 dark:yellow-200'"
|
||||
>
|
||||
<slot />
|
||||
</span>
|
||||
</button>
|
||||
</template>
|
||||
@@ -5,12 +5,12 @@ defineProps<{
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span
|
||||
<button
|
||||
class="rounded tag" p="x-2"
|
||||
border="~ stone-200 dark:stone-600"
|
||||
:bg="active ? 'stone-600 opacity-100' : 'stone-300 opacity-5'"
|
||||
:text="active ? 'stone-100' : 'stone-800 dark:stone-200'"
|
||||
>
|
||||
<slot />
|
||||
</span>
|
||||
</button>
|
||||
</template>
|
||||
@@ -5,12 +5,12 @@ defineProps<{
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span
|
||||
<button
|
||||
class="vegetable-tag rounded tag" p="x-2"
|
||||
border="~ green-200 dark:green-800"
|
||||
:bg="active ? 'green-600 opacity-90' : 'green-300 opacity-20'"
|
||||
:text="active ? 'green-100' : 'green-800 dark:green-200'"
|
||||
>
|
||||
<slot />
|
||||
</span>
|
||||
</button>
|
||||
</template>
|
||||
@@ -16,11 +16,11 @@ defineProps<{
|
||||
:is="to ? NuxtLink : 'div'"
|
||||
:to="to"
|
||||
class="ylf-form-item"
|
||||
w-full flex cursor-pointer items-center justify-between p-3
|
||||
w-full flex cursor-pointer items-center justify-between p-2
|
||||
hover:bg-gray-100
|
||||
dark:hover:bg-dark-400
|
||||
>
|
||||
<div v-if="label" class="text-md" inline-flex items-center justify-center>
|
||||
<div v-if="label" class="text-sm" inline-flex items-center justify-center>
|
||||
<div v-if="icon" :class="icon" mr-2 inline-flex />
|
||||
<span>{{ label }}</span>
|
||||
</div>
|
||||
22
app/components/ylf/YlfIconItem.vue
Normal file
22
app/components/ylf/YlfIconItem.vue
Normal 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>
|
||||
@@ -1,5 +1,5 @@
|
||||
import { isClient } from '@vueuse/core'
|
||||
import type { Ref } from 'vue'
|
||||
import { isClient } from '@vueuse/core'
|
||||
|
||||
export function useEmojiAnimation(recipeBtn: Ref<HTMLButtonElement | undefined>) {
|
||||
const { x, y } = usePointer()
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useStorage } from '@vueuse/core'
|
||||
import { lastDbUpdated, namespace } from '~/constants'
|
||||
import { db, initDb } from '~/utils/db'
|
||||
|
||||
export function useIndexedDB() {
|
||||
const dbUpdated = useStorage(`${namespace}:lastDbUpdated`, lastDbUpdated)
|
||||
@@ -1,11 +1,13 @@
|
||||
import type { DbRecipeItem } from '~/utils/db'
|
||||
import { useStorage } from '@vueuse/core'
|
||||
import { namespace } from '~/constants'
|
||||
|
||||
/**
|
||||
* 随机几道菜
|
||||
* @param total
|
||||
*/
|
||||
export function useRandomRecipe(total: Ref<number>) {
|
||||
const randomRecipes = ref<(DbRecipeItem | undefined)[]>([])
|
||||
const randomRecipes = useStorage<(DbRecipeItem | undefined)[]>(`${namespace}:random:recipes`, [])
|
||||
async function random() {
|
||||
const length = await db.recipes.count()
|
||||
const randomArr = generateRandomArray(length, total.value)
|
||||
@@ -19,6 +21,8 @@ export function useRandomRecipe(total: Ref<number>) {
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
// 如果没有随机菜谱,就生成一次
|
||||
if (randomRecipes.value.length <= 0)
|
||||
random()
|
||||
})
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { acceptHMRUpdate, defineStore } from 'pinia'
|
||||
import { useStorage } from '@vueuse/core'
|
||||
import { acceptHMRUpdate, defineStore } from 'pinia'
|
||||
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', () => {
|
||||
const deferredPrompt = ref<Event | any>()
|
||||
10
app/composables/store/history.ts
Normal file
10
app/composables/store/history.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import type { RecipeItem } from '~/types'
|
||||
import { useStorage } from '@vueuse/core'
|
||||
import { namespace } from '~/constants'
|
||||
|
||||
export interface RecipeHistoryItem {
|
||||
recipe: RecipeItem
|
||||
time: number
|
||||
}
|
||||
|
||||
export const recipeHistories = useStorage<RecipeHistoryItem[]>(`${namespace}:history`, [])
|
||||
@@ -1,10 +1,10 @@
|
||||
import { acceptHMRUpdate, defineStore } from 'pinia'
|
||||
import { useStorage } from '@vueuse/core'
|
||||
import { computed, onMounted, ref, watch } from 'vue'
|
||||
import type { RecipeItem, StuffItem } from '~/types'
|
||||
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 { useAppStore } from './app'
|
||||
import type { RecipeItem, StuffItem } from '~/types'
|
||||
|
||||
const namespace = 'cook'
|
||||
|
||||
@@ -78,8 +78,6 @@ export const useRecipeStore = defineStore('recipe', () => {
|
||||
async function searchRecipes() {
|
||||
isSearching.value = true
|
||||
let result: RecipeItem[] = []
|
||||
if (keyword.value)
|
||||
result = await db.recipes.filter(item => item.name.includes(keyword.value)).toArray()
|
||||
|
||||
if (curMode.value === 'strict') {
|
||||
result = await db.recipes.filter((item) => {
|
||||
@@ -116,14 +114,18 @@ export const useRecipeStore = defineStore('recipe', () => {
|
||||
}).toArray()
|
||||
}
|
||||
|
||||
if (keyword.value)
|
||||
result = result.filter(item => item.name.includes(keyword.value))
|
||||
|
||||
isSearching.value = false
|
||||
return result
|
||||
}
|
||||
|
||||
// 默认严格模式
|
||||
const displayedRecipe = ref<RecipeItem[]>([])
|
||||
watch([keyword, curStuff, curTool, curMode], async () => {
|
||||
displayedRecipe.value = await searchRecipes()
|
||||
// fix curStuff watch
|
||||
watch(() => [keyword.value, selectedStuff.value, curTool.value, curMode.value], async () => {
|
||||
displayedRecipe.value = [...(await searchRecipes())]
|
||||
})
|
||||
|
||||
/**
|
||||
@@ -1,5 +1,5 @@
|
||||
import process from 'node:process'
|
||||
import type { ModuleOptions } from '@vite-pwa/nuxt'
|
||||
import process from 'node:process'
|
||||
import { appDescription, appName } from '../constants/index'
|
||||
|
||||
const scope = '/'
|
||||
@@ -2,6 +2,6 @@ export const appName = '食用手册'
|
||||
export const appDescription = '好的,今天我们来做菜!'
|
||||
|
||||
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'
|
||||
@@ -217,7 +217,6 @@ BBQ烟熏手撕猪肉,猪肉,BV1DV411x7SH,复杂,,烤,烤箱,
|
||||
微波炉版照烧鸡腿饭,米、鸡肉、胡萝卜、花菜,BV16Z4y1V774,,,,微波炉,
|
||||
微波炉版蒸蛋羹,鸡蛋,BV19T4y1D7Zd,,,,微波炉,
|
||||
微波炉版煮米饭,米,BV193411W7nb,,,,微波炉,
|
||||
豉油鸡翅,鸡肉,BV1vz4y1d775,普通,茶餐厅,煮,一口大锅,
|
||||
水煮肉片,猪肉、芹菜、莴笋,BV1ZZ4y1379N,普通,川菜,炒,一口大锅,
|
||||
脆口黄瓜,黄瓜,BV1Tb4y1X7ow,简单,脆口,凉拌,一口大锅,
|
||||
白萝卜汤,白萝卜,BV1HJ411L7xA,简单,单一食材,煮,一口大锅,
|
||||
@@ -297,7 +296,7 @@ BBQ烟熏手撕猪肉,猪肉,BV1DV411x7SH,复杂,,烤,烤箱,
|
||||
名古屋鸡翅,鸡肉,BV1ET4y1A7Xd,普通,日式,炸,一口大锅,
|
||||
日式炖白萝,白萝卜,BV17b411B7H1,简单,日式,炖,一口大锅,
|
||||
炸虾天妇罗,虾,BV1e5411t7LY,困难,日式,炸,一口大锅,
|
||||
可乐饼,土豆、洋葱、肉、鸡蛋,BV1yW411Q7sa,普通,日式菜,炸,一口大锅,
|
||||
可乐饼,土豆、洋葱、肉、鸡蛋,BV17x411U75q,普通,日式菜,炸,一口大锅,
|
||||
清炒莴笋丝,莴笋、胡萝卜,BV1qK411H7RL,简单,爽口,炒,一口大锅,
|
||||
莴笋泡菜,莴笋,BV1h741127rS,简单,爽口,泡菜,一口大锅,
|
||||
口蘑汤,菌菇,BV1e64y1h776,简单,汤,煎、炖,一口大锅,
|
||||
@@ -598,3 +597,4 @@ biangbiang面,面食,BV1844y157GL,简单,,油泼,一口大锅,
|
||||
电饭煲番茄牛肉焖饭,番茄、牛肉、米,BV1Bv411C7X3,普通,,,一口大锅、电饭煲,
|
||||
电饭煲排骨土豆焖饭,猪肉、土豆、米,BV1Bv411C7X3,普通,,,一口大锅、电饭煲,
|
||||
米布丁,米、鸡蛋,BV1hr4y1k7A5,简单,,,一口大锅、电饭煲,
|
||||
麻婆豆腐,豆腐,BV1it4y1X75m,简单,,,一口大锅,
|
||||
|
1
app/data/recipe.json
Normal file
1
app/data/recipe.json
Normal file
File diff suppressed because one or more lines are too long
@@ -10,7 +10,7 @@ const route = useRoute()
|
||||
<main class="text-center text-gray-700 dark:text-gray-200" p="t-5 b-15">
|
||||
<div flex items-center justify-between>
|
||||
<BackBtn ml-3 />
|
||||
<h2 flex items-center justify-center text-lg>
|
||||
<h2 flex items-center justify-center text-lg font="bold">
|
||||
{{ route.meta.title }}
|
||||
</h2>
|
||||
<DarkToggle mr-3 />
|
||||
@@ -1,7 +1,8 @@
|
||||
<template>
|
||||
<main class="cook-main text-center text-gray-700 dark:text-gray-200" p="t-8 b-$cook-bottom-menu-height">
|
||||
<slot />
|
||||
<DarkToggle absolute right-3 top-5 />
|
||||
<DarkToggle absolute left-4 top-4 />
|
||||
<SearchRecipe />
|
||||
<TheBottomMenu fixed bottom-0 left-0 right-0 />
|
||||
</main>
|
||||
</template>
|
||||
@@ -1,4 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
import { defaultCookbook } from '~/utils'
|
||||
|
||||
definePageMeta({
|
||||
layout: 'child',
|
||||
title: '自定义菜谱',
|
||||
141
app/pages/help.vue
Normal file
141
app/pages/help.vue
Normal 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
24
app/pages/index.vue
Normal 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>
|
||||
12
app/pages/recipes/collect.vue
Normal file
12
app/pages/recipes/collect.vue
Normal file
@@ -0,0 +1,12 @@
|
||||
<script lang="ts" setup>
|
||||
definePageMeta({
|
||||
layout: 'child',
|
||||
title: '我的收藏',
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
施工中...
|
||||
</div>
|
||||
</template>
|
||||
38
app/pages/recipes/history.vue
Normal file
38
app/pages/recipes/history.vue
Normal 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>
|
||||
<button
|
||||
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"
|
||||
>
|
||||
<span i-ri-eraser-line />
|
||||
<span class="ml-1">清空记录</span>
|
||||
</button>
|
||||
|
||||
<div flex="~ col">
|
||||
<div v-for="history in recipeHistories" :key="history.recipe.name" mt-2>
|
||||
<DateTag>
|
||||
{{ dayjs(history.time).format('YYYY-MM-DD HH:mm:ss') }}
|
||||
</DateTag>
|
||||
<DishTag :dish="history.recipe" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -13,6 +13,12 @@ import { links } from '~/constants'
|
||||
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>
|
||||
|
||||
<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" />
|
||||
@@ -22,9 +28,9 @@ import { links } from '~/constants'
|
||||
<YlfFormItem icon="i-ri-settings-line" label="设置" to="/settings" />
|
||||
</YlfForm>
|
||||
|
||||
<YlfForm>
|
||||
<YlfFormItem icon="i-ri-article-line" label="自定义菜谱" to="/cookbooks/" />
|
||||
</YlfForm>
|
||||
<!-- <YlfForm>
|
||||
<YlfFormItem icon="i-ri-article-line" label="自定义菜谱 TODO" to="/cookbooks/" />
|
||||
</YlfForm> -->
|
||||
|
||||
<YlfForm>
|
||||
<YlfFormItem icon="i-ri-question-line" label="帮助" to="/help" />
|
||||
@@ -12,7 +12,6 @@ body,
|
||||
background: var(--c-bg);
|
||||
}
|
||||
|
||||
|
||||
input:focus {
|
||||
outline: none;
|
||||
}
|
||||
@@ -55,11 +54,13 @@ button {
|
||||
}
|
||||
}
|
||||
|
||||
hr {opacity: 0.1;}
|
||||
hr {
|
||||
opacity: 0.1;
|
||||
}
|
||||
|
||||
.tag {
|
||||
.tag,
|
||||
.tagSize {
|
||||
margin: 4px;
|
||||
padding: 2px 4px;
|
||||
// border: 1px solid var(--c-text);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { Table } from 'dexie'
|
||||
import Dexie from 'dexie'
|
||||
|
||||
import type { RecipeItem } from '~/types'
|
||||
|
||||
import Dexie from 'dexie'
|
||||
|
||||
export interface DbRecipeItem extends RecipeItem {
|
||||
id?: number
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
import { meat, staple, vegetable } from '~/data/food'
|
||||
|
||||
export * from './cookbook'
|
||||
|
||||
const foodItems = [...vegetable, ...meat, ...staple]
|
||||
const foodEmojiMap = new Map()
|
||||
foodItems.forEach((item) => {
|
||||
File diff suppressed because one or more lines are too long
@@ -1,7 +1,17 @@
|
||||
// @ts-check
|
||||
import antfu from '@antfu/eslint-config'
|
||||
import unocss from '@unocss/eslint-config/flat'
|
||||
import nuxt from './.nuxt/eslint.config.mjs'
|
||||
|
||||
export default antfu(
|
||||
{},
|
||||
unocss,
|
||||
export default nuxt(
|
||||
antfu(
|
||||
{
|
||||
unocss: true,
|
||||
formatters: true,
|
||||
},
|
||||
{
|
||||
ignores: [
|
||||
'app/data/*.json',
|
||||
],
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
[build.environment]
|
||||
NODE_VERSION = "16"
|
||||
|
||||
[build]
|
||||
publish = "dist"
|
||||
command = "pnpm run build"
|
||||
|
||||
[build.environment]
|
||||
NODE_VERSION = "16"
|
||||
|
||||
[[redirects]]
|
||||
from = "/*"
|
||||
to = "/index.html"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user