Merge branch 'YunYouJun:main' into main
This commit is contained in:
@@ -13,6 +13,12 @@
|
|||||||
|
|
||||||
本项目初衷是方便特殊时期隔离在家而材料有限的小伙伴,因此菜谱材料会尽量限制在特定范围内。
|
本项目初衷是方便特殊时期隔离在家而材料有限的小伙伴,因此菜谱材料会尽量限制在特定范围内。
|
||||||
|
|
||||||
|
更多可参见 [来做菜 | 关于](https://cook.yunyoujun.cn/about)。
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
本项目支持 PWA,使用浏览器打开时,可将其添加到主屏幕以获得近原生 APP 的体验。
|
||||||
|
|
||||||
## 开发
|
## 开发
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
<link rel="apple-touch-icon" href="/pwa-192x192.png">
|
<link rel="apple-touch-icon" href="/pwa-192x192.png">
|
||||||
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#00aba9">
|
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#00aba9">
|
||||||
<meta name="msapplication-TileColor" content="#00aba9">
|
<meta name="msapplication-TileColor" content="#00aba9">
|
||||||
<meta name="theme-color" content="#ffffff">
|
|
||||||
<meta name="description" content="好的,今天我们来做菜!">
|
<meta name="description" content="好的,今天我们来做菜!">
|
||||||
<title>隔离食用手册</title>
|
<title>隔离食用手册</title>
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
22
package.json
22
package.json
@@ -1,6 +1,9 @@
|
|||||||
{
|
{
|
||||||
"private": true,
|
"private": true,
|
||||||
"packageManager": "pnpm@6.32.3",
|
"packageManager": "pnpm@6.32.3",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "npm run convert && vite-ssg build",
|
"build": "npm run convert && vite-ssg build",
|
||||||
"convert": "esno scripts/convert.ts",
|
"convert": "esno scripts/convert.ts",
|
||||||
@@ -19,12 +22,12 @@
|
|||||||
"pinia": "^2.0.13",
|
"pinia": "^2.0.13",
|
||||||
"prism-theme-vars": "^0.2.2",
|
"prism-theme-vars": "^0.2.2",
|
||||||
"vue": "^3.2.33",
|
"vue": "^3.2.33",
|
||||||
"vue-about-me": "^1.2.6",
|
"vue-about-me": "^1.2.7",
|
||||||
"vue-demi": "^0.12.5",
|
"vue-demi": "^0.12.5",
|
||||||
"vue-router": "^4.0.14"
|
"vue-router": "^4.0.14"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@antfu/eslint-config": "^0.20.6",
|
"@antfu/eslint-config": "^0.21.1",
|
||||||
"@iconify-json/fe": "^1.1.1",
|
"@iconify-json/fe": "^1.1.1",
|
||||||
"@iconify-json/gg": "^1.1.1",
|
"@iconify-json/gg": "^1.1.1",
|
||||||
"@iconify-json/ic": "^1.1.2",
|
"@iconify-json/ic": "^1.1.2",
|
||||||
@@ -36,26 +39,27 @@
|
|||||||
"consola": "^2.15.3",
|
"consola": "^2.15.3",
|
||||||
"critters": "^0.0.16",
|
"critters": "^0.0.16",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"eslint": "^8.13.0",
|
"eslint": "^8.14.0",
|
||||||
"esno": "^0.14.1",
|
"esno": "^0.14.1",
|
||||||
"https-localhost": "^4.7.1",
|
"https-localhost": "^4.7.1",
|
||||||
"markdown-it-link-attributes": "^4.0.0",
|
"markdown-it-link-attributes": "^4.0.0",
|
||||||
"markdown-it-prism": "^2.2.4",
|
"markdown-it-prism": "^2.2.4",
|
||||||
"pnpm": "^6.32.9",
|
"pnpm": "^6.32.10",
|
||||||
"sass": "^1.50.1",
|
"sass": "^1.51.0",
|
||||||
"star-markdown-css": "^0.3.3",
|
"star-markdown-css": "^0.3.3",
|
||||||
"typescript": "^4.6.3",
|
"typescript": "^4.6.3",
|
||||||
"unocss": "^0.31.6",
|
"unocss": "^0.31.17",
|
||||||
"unplugin-auto-import": "^0.7.1",
|
"unplugin-auto-import": "^0.7.1",
|
||||||
"unplugin-vue-components": "^0.19.3",
|
"unplugin-vue-components": "^0.19.3",
|
||||||
"vite": "^2.9.5",
|
"vite": "^2.9.6",
|
||||||
"vite-plugin-inspect": "^0.5.0",
|
"vite-plugin-inspect": "^0.5.0",
|
||||||
"vite-plugin-md": "^0.12.4",
|
"vite-plugin-md": "^0.13.0",
|
||||||
"vite-plugin-pages": "^0.23.0",
|
"vite-plugin-pages": "^0.23.0",
|
||||||
"vite-plugin-pwa": "^0.12.0",
|
"vite-plugin-pwa": "^0.12.0",
|
||||||
"vite-plugin-vue-layouts": "^0.6.0",
|
"vite-plugin-vue-layouts": "^0.6.0",
|
||||||
"vite-ssg": "^0.19.2",
|
"vite-ssg": "^0.19.2",
|
||||||
"vite-ssg-sitemap": "^0.2.4",
|
"vite-ssg-sitemap": "^0.2.4",
|
||||||
"vue-tsc": "^0.34.7"
|
"vue-toastification": "^2.0.0-rc.5",
|
||||||
|
"vue-tsc": "^0.34.10"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1318
pnpm-lock.yaml
generated
1318
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
10
src/App.vue
10
src/App.vue
@@ -1,11 +1,19 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { isDark } from '~/composables'
|
||||||
// https://github.com/vueuse/head
|
// https://github.com/vueuse/head
|
||||||
// you can use this to manipulate the document head in any components,
|
// you can use this to manipulate the document head in any components,
|
||||||
// they will be rendered correctly in the html results with vite-ssg
|
// they will be rendered correctly in the html results with vite-ssg
|
||||||
useHead({
|
useHead({
|
||||||
title: '隔离食用手册',
|
title: '隔离食用手册',
|
||||||
meta: [
|
meta: [
|
||||||
{ name: 'description', content: '好的,今天我们来做菜!' },
|
{
|
||||||
|
name: 'description',
|
||||||
|
content: '好的,今天我们来做菜!',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'theme-color',
|
||||||
|
content: computed(() => isDark.value ? '#121212' : '#fff'),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
1
src/components.d.ts
vendored
1
src/components.d.ts
vendored
@@ -17,6 +17,7 @@ declare module '@vue/runtime-core' {
|
|||||||
RouterView: typeof import('vue-router')['RouterView']
|
RouterView: typeof import('vue-router')['RouterView']
|
||||||
StapleTag: typeof import('./components/tags/StapleTag.vue')['default']
|
StapleTag: typeof import('./components/tags/StapleTag.vue')['default']
|
||||||
Switch: typeof import('./components/Switch.vue')['default']
|
Switch: typeof import('./components/Switch.vue')['default']
|
||||||
|
ToggleMode: typeof import('./components/ToggleMode.vue')['default']
|
||||||
ToolTag: typeof import('./components/tags/ToolTag.vue')['default']
|
ToolTag: typeof import('./components/tags/ToolTag.vue')['default']
|
||||||
VegetableTag: typeof import('./components/tags/VegetableTag.vue')['default']
|
VegetableTag: typeof import('./components/tags/VegetableTag.vue')['default']
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,47 @@ const copyright = {
|
|||||||
author: '云游君',
|
author: '云游君',
|
||||||
authorUrl: 'https://www.yunyoujun.cn',
|
authorUrl: 'https://www.yunyoujun.cn',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const links = [
|
||||||
|
{
|
||||||
|
type: 'github',
|
||||||
|
label: 'GitHub: YunYouJun',
|
||||||
|
href: 'https://github.com/YunYouJun',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'telegram',
|
||||||
|
label: 'Telegram Channel',
|
||||||
|
href: 'https://t.me/elpsycn',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'weibo',
|
||||||
|
label: '微博:机智的云游君',
|
||||||
|
href: 'http://weibo.com/jizhideyunyoujun',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'twitter',
|
||||||
|
label: 'Twitter: YunYouJun',
|
||||||
|
href: 'https://twitter.com/YunYouJun',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'wechat',
|
||||||
|
label: '微信公众号:云游君',
|
||||||
|
href: '/wechat',
|
||||||
|
target: '_self',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'bilibili',
|
||||||
|
label: '云游君Official',
|
||||||
|
href: 'https://space.bilibili.com/1579790',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'blog',
|
||||||
|
label: '博客:yunyoujun.cn',
|
||||||
|
href: 'http://www.yunyoujun.cn',
|
||||||
|
},
|
||||||
|
]
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<vue-about-me :is-dark="isDark" :copyright="copyright" />
|
<vue-about-me :is-dark="isDark" :copyright="copyright" :links="links" />
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useGtm } from '@gtm-support/vue-gtm'
|
import { useGtm } from '@gtm-support/vue-gtm'
|
||||||
import { isClient } from '@vueuse/core'
|
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
import Switch from './Switch.vue'
|
|
||||||
import type { StuffItem } from '~/data/food'
|
import type { StuffItem } from '~/data/food'
|
||||||
import { meat, staple, tools, vegetable } from '~/data/food'
|
import { meat, staple, tools, vegetable } from '~/data/food'
|
||||||
import recipeData from '~/data/recipe.json'
|
import recipeData from '~/data/recipe.json'
|
||||||
@@ -10,22 +8,25 @@ import type { Recipe } from '~/types'
|
|||||||
import { useRecipeStore } from '~/stores/recipe'
|
import { useRecipeStore } from '~/stores/recipe'
|
||||||
|
|
||||||
import { useInvisibleElement } from '~/composables/helper'
|
import { useInvisibleElement } from '~/composables/helper'
|
||||||
|
import { useEmojiAnimation } from '~/composables/animation'
|
||||||
|
|
||||||
const recipe = ref<Recipe>(recipeData as Recipe)
|
const recipe = ref<Recipe>(recipeData as Recipe)
|
||||||
|
|
||||||
const rStore = useRecipeStore()
|
const rStore = useRecipeStore()
|
||||||
const { strict, curTool } = storeToRefs(rStore)
|
const { curMode, curTool } = storeToRefs(rStore)
|
||||||
const curStuff = computed(() => rStore.selectedStuff)
|
const curStuff = computed(() => rStore.selectedStuff)
|
||||||
|
|
||||||
// 默认严格模式
|
// 默认严格模式
|
||||||
const displayedRecipe = computed(() => {
|
const displayedRecipe = computed(() => {
|
||||||
const recipes = recipe.value.filter((item) => {
|
if (curMode.value === 'strict') {
|
||||||
if (strict.value) {
|
return recipe.value.filter((item) => {
|
||||||
const stuffFlag = curStuff.value.every(stuff => item.stuff.includes(stuff))
|
const stuffFlag = curStuff.value.every(stuff => item.stuff.includes(stuff))
|
||||||
const toolFlag = item.tools?.includes(curTool.value)
|
const toolFlag = item.tools?.includes(curTool.value)
|
||||||
return curTool.value ? stuffFlag && toolFlag : stuffFlag
|
return curTool.value ? stuffFlag && toolFlag : stuffFlag
|
||||||
}
|
})
|
||||||
else {
|
}
|
||||||
|
else if (curMode.value === 'loose') {
|
||||||
|
return recipe.value.filter((item) => {
|
||||||
const stuffFlag = curStuff.value.some(stuff => item.stuff.includes(stuff))
|
const stuffFlag = curStuff.value.some(stuff => item.stuff.includes(stuff))
|
||||||
const toolFlag = item.tools?.includes(curTool.value)
|
const toolFlag = item.tools?.includes(curTool.value)
|
||||||
|
|
||||||
@@ -41,47 +42,20 @@ const displayedRecipe = computed(() => {
|
|||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
})
|
}
|
||||||
return recipes
|
// survival
|
||||||
|
else {
|
||||||
|
return recipe.value.filter((item) => {
|
||||||
|
const stuffFlag = item.stuff.every(stuff => curStuff.value.includes(stuff))
|
||||||
|
const toolFlag = item.tools?.includes(curTool.value)
|
||||||
|
return curTool.value ? stuffFlag && toolFlag : stuffFlag
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const { x, y } = usePointer()
|
|
||||||
|
|
||||||
const recipeBtn = ref<HTMLButtonElement>()
|
const recipeBtn = ref<HTMLButtonElement>()
|
||||||
|
const { playAnimation } = useEmojiAnimation(recipeBtn)
|
||||||
const { top, left } = useElementBounding(recipeBtn)
|
|
||||||
|
|
||||||
const playAnimation = (emoji: string) => {
|
|
||||||
if (!isClient)
|
|
||||||
return
|
|
||||||
|
|
||||||
// 单个 Vue 组件实现不适合创建多个元素和清除动画
|
|
||||||
const emojiEl = document.createElement('span')
|
|
||||||
emojiEl.style.position = 'fixed'
|
|
||||||
emojiEl.style.left = `${x.value}px`
|
|
||||||
emojiEl.style.top = `${y.value}px`
|
|
||||||
emojiEl.style.zIndex = '10'
|
|
||||||
emojiEl.style.transition = 'left .4s linear, top .4s cubic-bezier(0.5, -0.5, 1, 1)'
|
|
||||||
emojiEl.textContent = emoji
|
|
||||||
document.body.appendChild(emojiEl)
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
// 以防万一,按钮位置没检测出来,就不播放动画了
|
|
||||||
if (!top.value || !left.value) {
|
|
||||||
emojiEl.style.top = `${x.value}px`
|
|
||||||
emojiEl.style.left = `${y.value}px`
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
emojiEl.style.top = `${top.value}px`
|
|
||||||
emojiEl.style.left = `${left.value + 12}px`
|
|
||||||
}
|
|
||||||
}, 1)
|
|
||||||
|
|
||||||
emojiEl.ontransitionend = () => {
|
|
||||||
emojiEl.remove()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const gtm = useGtm()
|
const gtm = useGtm()
|
||||||
|
|
||||||
@@ -224,11 +198,18 @@ const { isVisible, show } = useInvisibleElement(recipePanel)
|
|||||||
</ToolTag>
|
</ToolTag>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div ref="recipePanel" m="2 t-4" p="2" class="transition shadow hover:shadow-md" bg="gray-400/8">
|
<div ref="recipePanel" m="2 t-4" p="2" class="relative transition shadow hover:shadow-md" bg="gray-400/8">
|
||||||
<h2 text="xl" font="bold" p="1">
|
<h2 text="xl" font="bold" p="1">
|
||||||
🍲 来看看组合出的菜谱吧!
|
🍲 来看看组合出的菜谱吧!
|
||||||
</h2>
|
</h2>
|
||||||
<Switch />
|
|
||||||
|
<!-- <div class="absolute left-5 top-5 icon-btn">
|
||||||
|
<div i-ri-compass-line />
|
||||||
|
</div> -->
|
||||||
|
|
||||||
|
<ToggleMode />
|
||||||
|
|
||||||
|
<!-- <Switch /> -->
|
||||||
<div p="2">
|
<div p="2">
|
||||||
<Transition mode="out-in">
|
<Transition mode="out-in">
|
||||||
<span v-if="!curStuff.length && !curTool" text="sm" p="2">
|
<span v-if="!curStuff.length && !curTool" text="sm" p="2">
|
||||||
|
|||||||
@@ -12,12 +12,12 @@ import { toggleDark } from '~/composables'
|
|||||||
<div i="ri-sun-line dark:ri-moon-line" />
|
<div i="ri-sun-line dark:ri-moon-line" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<RouterLink class="icon-btn mx-2 hover:text-blue-400" to="/about" title="关于">
|
<RouterLink class="icon-btn mx-2 hover:text-orange-400" to="/help" title="帮助">
|
||||||
<div i-ri-information-line />
|
<div i-ri-question-line />
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
|
|
||||||
<RouterLink class="icon-btn mx-2 hover:text-green-400" to="/wechat" title="微信公众号 - 云游君">
|
<RouterLink class="icon-btn mx-2 hover:text-blue-400" to="/about" title="关于">
|
||||||
<div i-ri-wechat-2-line />
|
<div i-ri-information-line />
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
|
|
||||||
<a class="icon-btn mx-2 hover:text-pink-400" rel="noreferrer" href="https://space.bilibili.com/1579790" target="_blank" title="BiliBili">
|
<a class="icon-btn mx-2 hover:text-pink-400" rel="noreferrer" href="https://space.bilibili.com/1579790" target="_blank" title="BiliBili">
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { storeToRefs } from 'pinia'
|
defineProps<{
|
||||||
import { useRecipeStore } from '~/stores/recipe'
|
strict: boolean
|
||||||
const rStore = useRecipeStore()
|
toggleStrict: (val: boolean) => void
|
||||||
const { strict } = storeToRefs(rStore)
|
}>()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="inline-flex justify-center items-center" m="t-2">
|
<div class="inline-flex justify-center items-center" m="t-2">
|
||||||
<span :class="!strict && 'text-orange-600'" font="bold" m="x-1" @click="strict = false">
|
<span :class="!strict && 'text-orange-600'" font="bold" m="x-1" @click="toggleStrict(false)">
|
||||||
模糊匹配
|
模糊匹配
|
||||||
</span>
|
</span>
|
||||||
<label m="x-1" class="switch">
|
<label m="x-1" class="switch">
|
||||||
<input v-model="strict" type="checkbox">
|
<input :modelValue="strict" type="checkbox" @update:modelValue="toggleStrict">
|
||||||
<span class="inline-flex justify-center items-center slider round" />
|
<span class="inline-flex justify-center items-center slider round" />
|
||||||
</label>
|
</label>
|
||||||
<span :class="strict && 'text-green-600'" font="bold" m="x-1" @click="strict = true">
|
<span :class="strict && 'text-green-600'" font="bold" m="x-1" @click="toggleStrict(true)">
|
||||||
精准匹配
|
精准匹配
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
34
src/components/ToggleMode.vue
Normal file
34
src/components/ToggleMode.vue
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import type { SearchMode } from '~/stores/recipe'
|
||||||
|
import { useRecipeStore } from '~/stores/recipe'
|
||||||
|
|
||||||
|
const rStore = useRecipeStore()
|
||||||
|
|
||||||
|
const searchModes: {
|
||||||
|
id: SearchMode
|
||||||
|
name: string
|
||||||
|
}[] = [{
|
||||||
|
id: 'loose',
|
||||||
|
name: '模糊匹配',
|
||||||
|
}, {
|
||||||
|
id: 'strict',
|
||||||
|
name: '严格匹配',
|
||||||
|
}, {
|
||||||
|
id: 'survival',
|
||||||
|
name: '生存模式',
|
||||||
|
}]
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
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)"
|
||||||
|
>
|
||||||
|
{{ mode.name }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
42
src/composables/animation.ts
Normal file
42
src/composables/animation.ts
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import { isClient } from '@vueuse/core'
|
||||||
|
import type { Ref } from 'vue'
|
||||||
|
|
||||||
|
export function useEmojiAnimation(recipeBtn: Ref<HTMLButtonElement | undefined>) {
|
||||||
|
const { x, y } = usePointer()
|
||||||
|
const { top, left } = useElementBounding(recipeBtn)
|
||||||
|
|
||||||
|
const playAnimation = (emoji: string) => {
|
||||||
|
if (!isClient)
|
||||||
|
return
|
||||||
|
|
||||||
|
// 单个 Vue 组件实现不适合创建多个元素和清除动画
|
||||||
|
const emojiEl = document.createElement('span')
|
||||||
|
emojiEl.style.position = 'fixed'
|
||||||
|
emojiEl.style.left = `${x.value}px`
|
||||||
|
emojiEl.style.top = `${y.value}px`
|
||||||
|
emojiEl.style.zIndex = '10'
|
||||||
|
emojiEl.style.transition = 'left .4s linear, top .4s cubic-bezier(0.5, -0.5, 1, 1)'
|
||||||
|
emojiEl.textContent = emoji
|
||||||
|
document.body.appendChild(emojiEl)
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
// 以防万一,按钮位置没检测出来,就不播放动画了
|
||||||
|
if (!top.value || !left.value) {
|
||||||
|
emojiEl.style.top = `${x.value}px`
|
||||||
|
emojiEl.style.left = `${y.value}px`
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
emojiEl.style.top = `${top.value}px`
|
||||||
|
emojiEl.style.left = `${left.value + 12}px`
|
||||||
|
}
|
||||||
|
}, 1)
|
||||||
|
|
||||||
|
emojiEl.ontransitionend = () => {
|
||||||
|
emojiEl.remove()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
playAnimation,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -179,7 +179,7 @@ export const tools: StuffItem[] = [
|
|||||||
icon: 'i-gg-smart-home-cooker',
|
icon: 'i-gg-smart-home-cooker',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '一口啥都能煮的大锅',
|
label: '一口能炒又能煮的大锅',
|
||||||
name: '一口大锅',
|
name: '一口大锅',
|
||||||
emoji: '',
|
emoji: '',
|
||||||
icon: 'i-mdi-pot-steam-outline',
|
icon: 'i-mdi-pot-steam-outline',
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { setupLayouts } from 'virtual:generated-layouts'
|
|||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
|
|
||||||
import '@unocss/reset/tailwind.css'
|
import '@unocss/reset/tailwind.css'
|
||||||
import './styles/main.css'
|
|
||||||
import './styles/css-vars.scss'
|
import './styles/css-vars.scss'
|
||||||
import './styles/index.scss'
|
import './styles/index.scss'
|
||||||
import 'uno.css'
|
import 'uno.css'
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ export const install: UserModule = ({ isClient, router }) => {
|
|||||||
if (!isClient)
|
if (!isClient)
|
||||||
return
|
return
|
||||||
|
|
||||||
router.isReady().then(async() => {
|
router.isReady().then(async () => {
|
||||||
const { registerSW } = await import('virtual:pwa-register')
|
const { registerSW } = await import('virtual:pwa-register')
|
||||||
registerSW({ immediate: true })
|
registerSW({ immediate: true })
|
||||||
})
|
})
|
||||||
|
|||||||
9
src/modules/toast.ts
Normal file
9
src/modules/toast.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import Toast from 'vue-toastification'
|
||||||
|
import type { UserModule } from '~/types'
|
||||||
|
|
||||||
|
import 'vue-toastification/dist/index.css'
|
||||||
|
|
||||||
|
export const install: UserModule = ({ app }) => {
|
||||||
|
// add google tag manager, and add GA4 in gtag
|
||||||
|
app.use(Toast)
|
||||||
|
}
|
||||||
@@ -11,7 +11,7 @@ title: 关于
|
|||||||
> 希望大家吃的开心!
|
> 希望大家吃的开心!
|
||||||
|
|
||||||
<div class="inline-flex justify-center items-center">
|
<div class="inline-flex justify-center items-center">
|
||||||
代码请见:<a class="inline-flex items-center justify-center" href="https://github.com/YunYouJun/cook" target="_blank">
|
代码仓库:<a class="inline-flex items-center justify-center" href="https://github.com/YunYouJun/cook" target="_blank">
|
||||||
<div m="r-1" inline-flex i-ri-github-line />YunYouJun/cook</a>
|
<div m="r-1" inline-flex i-ri-github-line />YunYouJun/cook</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -25,9 +25,6 @@ title: 关于
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
- 如果您发现了任何「故障」或希望有某个「新功能」,可前往 [Issues](https://github.com/YunYouJun/cook/issues)。
|
|
||||||
- 如果您有任何想要交流的内容,包括但不局限于建议/反馈/分享等,可前往 [Discussions](https://github.com/YunYouJun/cook/issues)。
|
|
||||||
|
|
||||||
## **致谢**
|
## **致谢**
|
||||||
|
|
||||||
感谢以下小伙伴为本项目提供的数据支持和 QA !
|
感谢以下小伙伴为本项目提供的数据支持和 QA !
|
||||||
@@ -39,8 +36,31 @@ title: 关于
|
|||||||
- 晴方啾
|
- 晴方啾
|
||||||
- 课代表阿伟
|
- 课代表阿伟
|
||||||
|
|
||||||
## **友情提示**
|
## **关于我**
|
||||||
|
|
||||||
点击首页最上方的大锅图标,可以清空所选食材和工具。(本来想当作彩蛋,但是感觉还挺实用的。)
|
Hello,我是云游君。
|
||||||
|
|
||||||
|
很高兴能在这里与你相遇,也很希望这个网站可以真的帮助到你。
|
||||||
|
|
||||||
|
同时,我也以我或许不值一提的脸面保证它会以免费开源的形式维护运营下去。
|
||||||
|
|
||||||
|
此外,我也会继续尝试做一些有趣或有用的东西,并分享给大家。
|
||||||
|
你也可以在这些地方找到我。
|
||||||
|
|
||||||
<AboutMe />
|
<AboutMe />
|
||||||
|
|
||||||
|
对了,给微信公众号「云游君」发送「做菜」也可以快速找到这个网址。
|
||||||
|
|
||||||
|
## [**赞助者**](https://sponsors.yunyoujun.cn)
|
||||||
|
|
||||||
|
也非常感谢至今以来的所有赞助者们!
|
||||||
|
|
||||||
|
如果觉得我的[小项目们](https://sponsors.yunyoujun.cn/projects)还算有趣的话,要不要考虑[赞助](https://sponsors.yunyoujun.cn/)我?
|
||||||
|
|
||||||
|
我会将其公开在[账簿](https://sponsors.yunyoujun.cn/account)中,并投入在周边的服务器、域名、CDN 等费用上。
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://sponsors.yunyoujun.cn">
|
||||||
|
<img src='https://cdn.jsdelivr.net/gh/YunYouJun/sponsors/public/sponsors.svg'/>
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|||||||
23
src/pages/help.md
Normal file
23
src/pages/help.md
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
title: 帮助
|
||||||
|
---
|
||||||
|
|
||||||
|
<h3 text="center" font="serif black">
|
||||||
|
使用帮助
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
- 故障/新功能反馈:[Issues](https://github.com/YunYouJun/cook/issues)
|
||||||
|
- 交流/建议/分享:[Discussions](https://github.com/YunYouJun/cook/issues)
|
||||||
|
|
||||||
|
## **模式说明**
|
||||||
|
|
||||||
|
- 模糊匹配:展示所有含当前选中任意食材的菜谱
|
||||||
|
- 精准匹配:展示所有含当前选中所有食材的菜谱
|
||||||
|
- 生存模式:展示当前选中食材可制作的所有菜谱
|
||||||
|
|
||||||
|
## **友情提示**
|
||||||
|
|
||||||
|
- 点击首页最上方的大锅图标,可清空所选食材和工具。
|
||||||
|
- 本项目支持 PWA,使用浏览器打开时,可将其添加到主屏幕以获得近原生 APP 的体验。
|
||||||
|
|
||||||
|
<br />
|
||||||
@@ -2,9 +2,14 @@ import { acceptHMRUpdate, defineStore } from 'pinia'
|
|||||||
|
|
||||||
const namespace = 'cook'
|
const namespace = 'cook'
|
||||||
|
|
||||||
export const useRecipeStore = defineStore('recipe', () => {
|
/**
|
||||||
const strict = useStorage(`${namespace}:strict`, false)
|
* survival: 生存模式
|
||||||
|
* strict: 严格
|
||||||
|
* loose: 模糊
|
||||||
|
*/
|
||||||
|
export type SearchMode = 'survival' | 'loose' | 'strict'
|
||||||
|
|
||||||
|
export const useRecipeStore = defineStore('recipe', () => {
|
||||||
const curStuff = useStorage(`${namespace}:stuff`, new Set<string>())
|
const curStuff = useStorage(`${namespace}:stuff`, new Set<string>())
|
||||||
// const curTools = ref(new Set<string>())
|
// const curTools = ref(new Set<string>())
|
||||||
const curTool = useStorage(`${namespace}:tool`, '')
|
const curTool = useStorage(`${namespace}:tool`, '')
|
||||||
@@ -13,6 +18,8 @@ export const useRecipeStore = defineStore('recipe', () => {
|
|||||||
// const selectedTools = computed(() => Array.from(curTools.value))
|
// const selectedTools = computed(() => Array.from(curTools.value))
|
||||||
// const selectedTools = ref('')
|
// const selectedTools = ref('')
|
||||||
|
|
||||||
|
const curMode = useStorage<SearchMode>(`${namespace}:mode`, 'loose')
|
||||||
|
|
||||||
function toggleStuff(name: string) {
|
function toggleStuff(name: string) {
|
||||||
if (!curStuff)
|
if (!curStuff)
|
||||||
return
|
return
|
||||||
@@ -33,6 +40,10 @@ export const useRecipeStore = defineStore('recipe', () => {
|
|||||||
// curTools.value.add(name)
|
// curTools.value.add(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setMode(mode: SearchMode) {
|
||||||
|
curMode.value = mode
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 重置
|
* 重置
|
||||||
*/
|
*/
|
||||||
@@ -43,12 +54,14 @@ export const useRecipeStore = defineStore('recipe', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
strict,
|
|
||||||
curTool,
|
curTool,
|
||||||
|
curMode,
|
||||||
selectedStuff,
|
selectedStuff,
|
||||||
|
|
||||||
toggleStuff,
|
toggleStuff,
|
||||||
toggleTools,
|
toggleTools,
|
||||||
reset,
|
reset,
|
||||||
|
setMode,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
38
src/styles/animation.scss
Normal file
38
src/styles/animation.scss
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/* we will explain what these classes do next! */
|
||||||
|
.v-enter-active,
|
||||||
|
.v-leave-active {
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v-enter-from,
|
||||||
|
.v-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// scrollbar
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
border-radius: 2px;
|
||||||
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
border-radius: 2px;
|
||||||
|
background-color: rgba(122, 122, 122, 0.3);
|
||||||
|
|
||||||
|
&:window-inactive {
|
||||||
|
background-color: rgba(122, 122, 122, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba(122, 122, 122, 0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background-color: rgba(122, 122, 122, 0.9);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,33 @@
|
|||||||
|
@import './animation.scss';
|
||||||
@import './markdown.scss';
|
@import './markdown.scss';
|
||||||
|
|
||||||
|
html,
|
||||||
|
body,
|
||||||
|
#app {
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark {
|
||||||
|
background: #121212;
|
||||||
|
}
|
||||||
|
|
||||||
|
#nprogress {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#nprogress .bar {
|
||||||
|
background: rgb(13,148,136);
|
||||||
|
opacity: 0.75;
|
||||||
|
position: fixed;
|
||||||
|
z-index: 1031;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
color: var(--c-text);
|
color: var(--c-text);
|
||||||
background-color: var(--c-bg);
|
background-color: var(--c-bg);
|
||||||
@@ -23,47 +51,11 @@ button {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hr {opacity: 0.1;}
|
||||||
|
|
||||||
.tag {
|
.tag {
|
||||||
margin: 4px;
|
margin: 4px;
|
||||||
padding: 2px 4px;
|
padding: 2px 4px;
|
||||||
// border: 1px solid var(--c-text);
|
// border: 1px solid var(--c-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* we will explain what these classes do next! */
|
|
||||||
.v-enter-active,
|
|
||||||
.v-leave-active {
|
|
||||||
transition: opacity 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.v-enter-from,
|
|
||||||
.v-leave-to {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// scrollbar
|
|
||||||
::-webkit-scrollbar {
|
|
||||||
width: 8px;
|
|
||||||
height: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar-track {
|
|
||||||
border-radius: 2px;
|
|
||||||
background-color: rgba(255, 255, 255, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb {
|
|
||||||
border-radius: 2px;
|
|
||||||
background-color: rgba(122, 122, 122, 0.3);
|
|
||||||
|
|
||||||
&:window-inactive {
|
|
||||||
background-color: rgba(122, 122, 122, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: rgba(122, 122, 122, 0.7);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
background-color: rgba(122, 122, 122, 0.9);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
html,
|
|
||||||
body,
|
|
||||||
#app {
|
|
||||||
height: 100%;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
html.dark {
|
|
||||||
background: #121212;
|
|
||||||
}
|
|
||||||
|
|
||||||
#nprogress {
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#nprogress .bar {
|
|
||||||
background: rgb(13,148,136);
|
|
||||||
opacity: 0.75;
|
|
||||||
position: fixed;
|
|
||||||
z-index: 1031;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 2px;
|
|
||||||
}
|
|
||||||
7
vercel.json
Normal file
7
vercel.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"rewrites": [
|
||||||
|
{ "source": "/about", "destination": "/index.html" },
|
||||||
|
{ "source": "/help", "destination": "/index.html" },
|
||||||
|
{ "source": "/wechat", "destination": "/index.html" }
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user