Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cb61ab90fd | ||
|
|
7b849b7e0a | ||
|
|
5045f7f91b | ||
|
|
0ec5a4d51e | ||
|
|
6095d97b2a | ||
|
|
13b6ad6047 | ||
|
|
190c05918b | ||
|
|
42c244573c | ||
|
|
21f74b3c48 | ||
|
|
5fa5944dff | ||
|
|
1af9e36fc0 | ||
|
|
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 |
1
.env.example
Normal file
1
.env.example
Normal file
@@ -0,0 +1 @@
|
||||
APPLE_DEVELOPMENT_TEAM=
|
||||
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
|
||||
|
||||
|
||||
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
@@ -17,10 +17,10 @@ jobs:
|
||||
uses: pnpm/action-setup@v2
|
||||
|
||||
# after pnpm
|
||||
- name: Use Node.js 16
|
||||
- name: Use Node.js LTS
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
node-version: lts/*
|
||||
registry-url: https://registry.npmjs.org/
|
||||
cache: pnpm
|
||||
|
||||
|
||||
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
|
||||
|
||||
51
.vscode/settings.json
vendored
51
.vscode/settings.json
vendored
@@ -19,8 +19,6 @@
|
||||
"*.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,
|
||||
@@ -31,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",
|
||||
@@ -80,5 +52,6 @@
|
||||
"json",
|
||||
"jsonc",
|
||||
"yaml"
|
||||
]
|
||||
],
|
||||
"typescript.tsdk": "node_modules/typescript/lib"
|
||||
}
|
||||
|
||||
@@ -9,9 +9,10 @@
|
||||
|
||||
### 网页版本
|
||||
|
||||
- 网站链接:[cook.yunyoujun.cn](https://cook.yunyoujun.cn)
|
||||
- 备用:[cook.yyj.moe](https://cook.yyj.moe)
|
||||
- 开发版:[cook.yunle.app](https://cook.yunle.app)
|
||||
- 网站链接(Cloudflare):[cook.yunyoujun.cn](https://cook.yunyoujun.cn)
|
||||
- 国内加速(腾讯云):[cook.yunle.fun](https://cook.yunle.fun)
|
||||
- 备用(Netlify):[cook.yyj.moe](https://cook.yyj.moe)
|
||||
- 开发版(Vercel):[cook.yunle.app](https://cook.yunle.app)
|
||||
|
||||
### 小程序版本
|
||||
|
||||
@@ -73,8 +74,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({
|
||||
@@ -22,13 +22,6 @@ onBeforeMount(() => {
|
||||
<span m="l-1" class="inline-flex" style="margin-top: 1px;">B 站</span>
|
||||
</a>
|
||||
</div>
|
||||
<div mt-2>
|
||||
本站点由
|
||||
<a color="#F6821F" href="https://www.cloudflare-cn.com/" target="_blank" title="Cloudflare" border="b-1 dashed">
|
||||
<span>Cloudflare</span>
|
||||
</a>
|
||||
提供 CDN 支持
|
||||
</div>
|
||||
<div m="t-2" opacity="80" class="flex items-center justify-center">
|
||||
©️ <a href="https://github.com/YunYouJun/cook" target="_blank">Cook</a>
|
||||
<div text="xs" m="x-1" i-ri-cloud-line />
|
||||
@@ -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)
|
||||
@@ -12,7 +12,8 @@ const curStuff = computed(() => rStore.selectedStuff)
|
||||
const recipeBtnRef = ref<HTMLButtonElement>()
|
||||
const { playAnimation } = useEmojiAnimation(recipeBtnRef)
|
||||
|
||||
const gtm = useGtm()
|
||||
const { proxy } = useScriptGoogleTagManager()
|
||||
|
||||
const recipePanelRef = ref()
|
||||
const { isVisible, show } = useInvisibleElement(recipePanelRef)
|
||||
|
||||
@@ -22,13 +23,13 @@ function toggleStuff(item: StuffItem, category = '', _e?: Event) {
|
||||
if (curStuff.value.includes(item.name))
|
||||
playAnimation(item.emoji)
|
||||
|
||||
gtm?.trackEvent({
|
||||
proxy.dataLayer.push({
|
||||
event: 'click',
|
||||
category: `${category}_${item.name}`,
|
||||
action: 'click_stuff',
|
||||
label: '食材',
|
||||
})
|
||||
gtm?.trackEvent({
|
||||
proxy.dataLayer.push({
|
||||
event: 'click_stuff',
|
||||
action: item.name,
|
||||
})
|
||||
@@ -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
|
||||
@@ -5,22 +5,23 @@ const { random, randomRecipes } = useRandomRecipe(count)
|
||||
|
||||
<template>
|
||||
<div inline-flex m="y-3">
|
||||
<button rounded-full p-2 btn @click="dec()">
|
||||
<button btn rounded-full p-2 @click="dec()">
|
||||
<div i-carbon-subtract />
|
||||
</button>
|
||||
<div font="mono" w="15" m-auto inline-block>
|
||||
{{ count }}
|
||||
</div>
|
||||
<button rounded-full p-2 btn @click="inc()">
|
||||
<button btn rounded-full p-2 @click="inc()">
|
||||
<div i-carbon-add />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<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">
|
||||
<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>随机一下</div>
|
||||
</button>
|
||||
<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 {
|
||||
@@ -21,7 +21,7 @@ const searchModes: {
|
||||
<template>
|
||||
<div>
|
||||
<button
|
||||
v-for="mode in searchModes" :key="mode.id" class="rounded px-2 tag"
|
||||
v-for="mode in searchModes" :key="mode.id" class="tag rounded px-2"
|
||||
:bg="mode.id === rStore.curMode ? 'orange-500 dark:orange-600 opacity-100' : 'orange-300 opacity-20'"
|
||||
:text="mode.id === rStore.curMode ? 'orange-100' : 'orange-800 dark:orange-200'"
|
||||
@click="rStore.setMode(mode.id)"
|
||||
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()
|
||||
13
app/components/footer/CdnSupport.vue
Normal file
13
app/components/footer/CdnSupport.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<script lang="ts" setup>
|
||||
// migrate to 腾讯云 EdgeOne
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div mt-2>
|
||||
本站点由
|
||||
<a color="#F6821F" href="https://www.cloudflare-cn.com/" target="_blank" title="Cloudflare" border="b-1 dashed">
|
||||
<span>Cloudflare</span>
|
||||
</a>
|
||||
提供 CDN 支持
|
||||
</div>
|
||||
</template>
|
||||
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 date = import.meta.env.VITE_APP_BUILD_DATE
|
||||
const buildDate = (new Date(date)).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,25 +1,31 @@
|
||||
<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<{
|
||||
dish: RecipeItem | DbRecipeItem
|
||||
}>()
|
||||
|
||||
const gtm = useGtm()
|
||||
const { proxy } = useScriptGoogleTagManager()
|
||||
|
||||
function triggerGtm(val: string) {
|
||||
gtm?.trackEvent({
|
||||
function triggerGtm(dish: RecipeItem) {
|
||||
recipeHistories.value.push({
|
||||
recipe: dish,
|
||||
time: Date.now(),
|
||||
})
|
||||
|
||||
proxy.dataLayer.push({
|
||||
event: 'click',
|
||||
category: `dish_${val}`,
|
||||
category: `dish_${dish.name}`,
|
||||
action: 'click_recipe',
|
||||
label: '跳转菜谱',
|
||||
})
|
||||
gtm?.trackEvent({
|
||||
proxy.dataLayer.push({
|
||||
event: 'click_dish',
|
||||
action: val,
|
||||
action: dish.name,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -31,16 +37,16 @@ const dishLabel = computed(() => {
|
||||
|
||||
<template>
|
||||
<a
|
||||
:href="dish.link || `https://www.bilibili.com/video/${dish.bv}`" target="_blank" class="dish-tag rounded tag" p="x-2"
|
||||
:href="dish.link || `https://www.bilibili.com/video/${dish.bv}`" target="_blank" class="dish-tag tag rounded" 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
|
||||
class="meat-tag rounded tag" p="x-2"
|
||||
<button
|
||||
class="meat-tag tag rounded" 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
|
||||
class="rounded tag" p="x-2" border="~ yellow-200 dark:yellow-800"
|
||||
<button
|
||||
class="tag rounded" 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
|
||||
class="rounded tag" p="x-2"
|
||||
<button
|
||||
class="tag rounded" 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
|
||||
class="vegetable-tag rounded tag" p="x-2"
|
||||
<button
|
||||
class="vegetable-tag tag rounded" 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,7 +21,9 @@ export function useRandomRecipe(total: Ref<number>) {
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
random()
|
||||
// 如果没有随机菜谱,就生成一次
|
||||
if (randomRecipes.value.length <= 0)
|
||||
random()
|
||||
})
|
||||
|
||||
return {
|
||||
@@ -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,9 @@
|
||||
import { acceptHMRUpdate, defineStore } from 'pinia'
|
||||
import type { RecipeItem, StuffItem } from '~/types'
|
||||
import { useStorage } from '@vueuse/core'
|
||||
import { acceptHMRUpdate, defineStore } from 'pinia'
|
||||
import { computed, onMounted, ref, watch } from 'vue'
|
||||
import { useGtm } from '@gtm-support/vue-gtm'
|
||||
import { db } from '../../utils/db'
|
||||
import { useAppStore } from './app'
|
||||
import type { RecipeItem, StuffItem } from '~/types'
|
||||
|
||||
const namespace = 'cook'
|
||||
|
||||
@@ -16,7 +15,7 @@ const namespace = 'cook'
|
||||
export type SearchMode = 'survival' | 'loose' | 'strict'
|
||||
|
||||
export const useRecipeStore = defineStore('recipe', () => {
|
||||
const gtm = useGtm()
|
||||
const { proxy } = useScriptGoogleTagManager()
|
||||
const { settings } = useAppStore()
|
||||
|
||||
/**
|
||||
@@ -78,8 +77,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,6 +113,9 @@ export const useRecipeStore = defineStore('recipe', () => {
|
||||
}).toArray()
|
||||
}
|
||||
|
||||
if (keyword.value)
|
||||
result = result.filter(item => item.name.includes(keyword.value))
|
||||
|
||||
isSearching.value = false
|
||||
return result
|
||||
}
|
||||
@@ -135,13 +135,13 @@ export const useRecipeStore = defineStore('recipe', () => {
|
||||
const value = item.name
|
||||
toggleTools(value)
|
||||
|
||||
gtm?.trackEvent({
|
||||
proxy.dataLayer.push({
|
||||
event: 'click',
|
||||
category: `tool_${value}`,
|
||||
action: 'click_tool',
|
||||
label: '工具',
|
||||
})
|
||||
gtm?.trackEvent({
|
||||
proxy.dataLayer.push({
|
||||
event: 'click_tool',
|
||||
action: item.name,
|
||||
})
|
||||
@@ -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'
|
||||
@@ -7,4 +7,9 @@ export const links = {
|
||||
* 兔小巢反馈
|
||||
*/
|
||||
feedback: 'https://support.qq.com/product/507827',
|
||||
|
||||
/**
|
||||
* changelog
|
||||
*/
|
||||
changelog: 'https://docs.yunyoujun.cn/projects/cook/changelog',
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
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
@@ -6,9 +6,9 @@ By default, `default.vue` will be used unless an alternative is specified in the
|
||||
|
||||
```html
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
layout: 'home',
|
||||
})
|
||||
definePageMeta({
|
||||
layout: 'home',
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
@@ -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>
|
||||
@@ -9,7 +9,7 @@
|
||||
</div>
|
||||
<div>菜谱消失了</div>
|
||||
<div>
|
||||
<NuxtLink text-sm btn m="3 t8" to="/">
|
||||
<NuxtLink btn text-sm m="3 t8" to="/">
|
||||
返回主页
|
||||
</NuxtLink>
|
||||
</div>
|
||||
@@ -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>
|
||||
@@ -18,6 +18,10 @@ definePageMeta({
|
||||
text-left
|
||||
>
|
||||
<YlfForm>
|
||||
<YlfFormItem label="暗色模式">
|
||||
<YlfSwitch v-model="app.settings.keepLocalData" />
|
||||
</YlfFormItem>
|
||||
|
||||
<YlfFormItem label="离开网页后保留选中数据">
|
||||
<YlfSwitch v-model="app.settings.keepLocalData" />
|
||||
</YlfFormItem>
|
||||
@@ -13,19 +13,29 @@ 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" />
|
||||
</YlfForm>
|
||||
|
||||
<YlfForm>
|
||||
<YlfFormItem icon="i-ri-settings-line" label="设置" to="/settings" />
|
||||
<YlfFormItem icon="i-ri-file-history-line" label="更新日志" :to="links.changelog" target="_blank" />
|
||||
</YlfForm>
|
||||
|
||||
<YlfForm>
|
||||
<YlfFormItem icon="i-ri-article-line" label="自定义菜谱" to="/cookbooks/" />
|
||||
<YlfFormItem icon="i-ri-settings-line" label="设置" to="/settings" />
|
||||
</YlfForm>
|
||||
|
||||
<!-- <YlfForm>
|
||||
<YlfFormItem icon="i-ri-article-line" label="自定义菜谱 TODO" to="/cookbooks/" />
|
||||
</YlfForm> -->
|
||||
|
||||
<YlfForm>
|
||||
<YlfFormItem icon="i-ri-question-line" label="帮助" to="/help" />
|
||||
<YlfFormItem icon="i-ri-information-line" label="关于" to="/help" />
|
||||
@@ -12,7 +12,6 @@ body,
|
||||
background: var(--c-bg);
|
||||
}
|
||||
|
||||
|
||||
input:focus {
|
||||
outline: none;
|
||||
}
|
||||
@@ -22,7 +21,7 @@ input:focus {
|
||||
}
|
||||
|
||||
#nprogress .bar {
|
||||
background: rgb(13,148,136);
|
||||
background: rgb(13, 148, 136);
|
||||
opacity: 0.75;
|
||||
position: fixed;
|
||||
z-index: 1031;
|
||||
@@ -37,7 +36,7 @@ html {
|
||||
background-color: var(--c-bg);
|
||||
|
||||
scroll-behavior: smooth;
|
||||
|
||||
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
@@ -48,18 +47,20 @@ a {
|
||||
|
||||
button {
|
||||
outline: none;
|
||||
|
||||
|
||||
&:focus,
|
||||
&:active {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
hr {opacity: 0.1;}
|
||||
hr {
|
||||
opacity: 0.1;
|
||||
}
|
||||
|
||||
.tag {
|
||||
.tag,
|
||||
.tagSize {
|
||||
margin: 4px;
|
||||
padding: 2px 4px;
|
||||
// border: 1px solid var(--c-text);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ blockquote {
|
||||
border-left: 0.25em solid #ddd;
|
||||
padding: 0 1em;
|
||||
color: #777;
|
||||
quotes: '\\201C''\\201D''\\2018''\\2019';
|
||||
quotes: '\\201C' '\\201D' '\\2018' '\\2019';
|
||||
}
|
||||
|
||||
ol {
|
||||
@@ -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
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user