13 Commits
ai ... v1.2.3

Author SHA1 Message Date
YunYouJun
65fea75825 chore: release v1.2.3 2025-01-24 11:35:46 +08:00
YunYouJun
5e97da4731 chore: upgrade deps & fix lint 2025-01-24 11:27:43 +08:00
George Chen
78fa350ced feat: use <button> to improve a11y and keyboard navigation experience (#74) 2025-01-12 01:35:19 +08:00
YunYouJun
11283f6f94 chore: fix lint ignore recipe.json 2024-12-30 02:04:51 +08:00
YunYouJun
e9b4d3ac38 chore: upgrade deps with nuxt 2024-12-30 02:02:12 +08:00
YunYouJun
7fe1f95d9d ci: fix nuxt lint 2024-09-15 18:26:51 +08:00
YunYouJun
86b565dfa9 ci: add lint-staged check 2024-09-15 18:14:52 +08:00
YunYouJun
41bdc3346f refactor: use nuxt compatiable 4 folder 2024-09-15 18:07:50 +08:00
YunYouJun
7a52b024dd ci: add vercel tag deplou 2024-09-15 17:45:11 +08:00
YunYouJun
10073b3a07 chore: fix ci lint/typecheck/test 2024-04-28 19:36:17 +08:00
YunYouJun
778594652c fix: ios qq 500 error 2024-04-28 19:18:31 +08:00
YunYouJun
b00b52cba8 chore: upgrade deps 2024-03-13 15:58:41 +08:00
YunYouJun
ab7f4e20f1 ci: use node lts 2024-01-28 06:57:17 +08:00
125 changed files with 11594 additions and 11177 deletions

View File

@@ -1,2 +0,0 @@
SD_API_BASE_URL=
OPENAI_API_KEY=

View File

@@ -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
View 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
View File

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

2
.npmrc
View File

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

48
.vscode/settings.json vendored
View File

@@ -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",

View File

@@ -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({

View File

@@ -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>

View File

@@ -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)

View File

@@ -25,7 +25,7 @@ const filteredRecipes = computedAsync(async () => {
<template>
<YlfIconButton
absolute right-3 top-4
absolute right-4 top-4
class="icon-btn hover:text-yellow-400 !outline-none"
text-xl
title="切换" @click="openModal"
@@ -89,9 +89,9 @@ const filteredRecipes = computedAsync(async () => {
@click="keyword = ''"
/>
</div>
<div op="70" ml-2 inline-flex cursor-pointer text-base @click="closeModal">
<button op="70" ml-2 inline-flex cursor-pointer text-base @click="closeModal">
取消
</div>
</button>
</DialogTitle>
<div flex="~ col grow" overflow="auto" class="mt-2" text-xs>
<DishTag v-for="item, i in filteredRecipes" :key="i" :dish="item" />

View File

@@ -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()

View File

@@ -0,0 +1,29 @@
<template>
<FAQItem :default-open="true" title="关于">
<div text-left>
<ul>
<li>
它诞生于 2022 4 时值疫情风控期间希望能帮助期间的伙伴根据现有食材寻找到合适的菜谱故原名隔离食用手册
</li>
<li>
如今那个时期已离我们远去故去掉隔离二字但也很高兴能在这里继续与你相遇希望它能继续发光发热在日常生活中帮助到大家
</li>
<li>
<div class="inline-flex items-center justify-center">
代码仓库<a class="inline-flex items-center justify-center" href="https://github.com/YunYouJun/cook" target="_blank">
<div m="r-1" i-ri-github-line inline-flex />YunYouJun/cook</a>
</div>
</li>
<li>
<div class="inline-flex items-center justify-center">
菜谱视频来源
<a class="inline-flex items-center text-sm text-blue-600 dark:text-blue-400" href="https://docs.qq.com/sheet/DQk1vdkhFV0twQVNS" target="_blank">
<div m="r-1" i-ri-bilibili-line inline-flex />
<span class="inline-flex">隔离食用手册大全</span>
</a>
</div>
</li>
</ul>
</div>
</FAQItem>
</template>

View File

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

View File

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

View File

@@ -1,9 +1,9 @@
<script lang="ts" setup>
import type { DbRecipeItem } from '~/utils/db'
import { tools } from '~/data/food'
import type { RecipeItem } from '~/types'
import { getEmojisFromStuff } from '~/utils'
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

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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()

View File

@@ -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)

View File

@@ -1,6 +1,6 @@
import type { DbRecipeItem } from '~/utils/db'
import { useStorage } from '@vueuse/core'
import { namespace } from '~/constants'
import type { DbRecipeItem } from '~/utils/db'
/**
*

View File

@@ -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 { defaultSettings } from '../../utils/settings'
export const useAppStore = defineStore('app', () => {
const deferredPrompt = ref<Event | any>()

View File

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

View File

@@ -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'

View File

@@ -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 = '/'

View File

@@ -7,78 +7,63 @@ export const vegetable: StuffItem[] = [
{
name: '土豆',
emoji: '🥔',
en: 'potato',
},
{
name: '胡萝卜',
emoji: '🥕',
en: 'carrot',
},
{
name: '花菜',
emoji: '🥦',
en: 'cauliflower',
},
{
name: '白萝卜',
emoji: '🥣',
en: 'radish',
},
{
name: '西葫芦',
emoji: '🥒',
en: 'zucchini',
},
{
name: '番茄',
emoji: '🍅',
alias: '西红柿',
en: 'tomato',
},
{
name: '芹菜',
emoji: '🥬',
en: 'celery',
},
{
name: '黄瓜',
emoji: '🥒',
en: 'cucumber',
},
{
name: '洋葱',
emoji: '🧅',
en: 'onion',
},
{
name: '莴笋',
emoji: '🎍',
en: 'lettuce',
},
{
name: '菌菇',
emoji: '🍄',
en: 'mushroom',
},
{
name: '茄子',
emoji: '🍆',
en: 'eggplant',
},
{
name: '豆腐',
emoji: '🍲',
en: 'tofu',
},
{
name: '包菜',
emoji: '🥗',
en: 'cabbage',
},
{
name: '白菜',
emoji: '🥬',
en: 'cabbage',
},
]
@@ -89,52 +74,42 @@ export const meat: StuffItem[] = [
{
name: '午餐肉',
emoji: '🥓',
en: 'bacon',
},
{
name: '香肠',
emoji: '🌭',
en: 'sausage',
},
{
name: '腊肠',
emoji: '🌭',
en: 'sausage',
},
{
name: '鸡肉',
emoji: '🐤',
en: 'chicken',
},
{
name: '猪肉',
emoji: '🐷',
en: 'pork',
},
{
name: '鸡蛋',
emoji: '🥚',
en: 'egg',
},
{
name: '虾',
emoji: '🦐',
en: 'shrimp',
},
{
name: '牛肉',
emoji: '🐮',
en: 'beef',
},
{
name: '骨头',
emoji: '🦴',
en: 'bone',
},
{
name: '鱼Todo',
emoji: '🐟',
en: 'fish',
},
]
@@ -145,22 +120,18 @@ export const staple: StuffItem[] = [
{
name: '面食',
emoji: '🍝',
en: 'noodles',
},
{
name: '面包',
emoji: '🍞',
en: 'bread',
},
{
name: '米',
emoji: '🍚',
en: 'rice',
},
{
name: '方便面',
emoji: '🍜',
en: 'instant noodles',
},
]

1
app/data/recipe.json Normal file

File diff suppressed because one or more lines are too long

View File

@@ -1,7 +1,7 @@
<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 left-3 top-5 />
<DarkToggle absolute left-4 top-4 />
<SearchRecipe />
<TheBottomMenu fixed bottom-0 left-0 right-0 />
</main>

View File

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

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

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

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

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

View File

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

View File

@@ -26,7 +26,5 @@
// cook custom
:root {
--cook-bottom-menu-padding-bottom: 20px;
--cook-bottom-menu-height: calc(
64px + var(--cook-bottom-menu-padding-bottom)
);
--cook-bottom-menu-height: calc(64px + var(--cook-bottom-menu-padding-bottom));
}

View File

@@ -58,7 +58,8 @@ hr {
opacity: 0.1;
}
.tag {
.tag,
.tagSize {
margin: 4px;
padding: 2px 4px;
// border: 1px solid var(--c-text);

View File

@@ -66,9 +66,4 @@ export interface StuffItem {
*
*/
label?: string
/**
* , for ai keyword
* @example 'potato'
*/
en?: string
}

View File

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

View File

@@ -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) => {

View File

@@ -1,178 +0,0 @@
<script lang="ts" setup>
import type { StuffItem } from '~/types'
import { meat, staple, vegetable } from '~/data/food'
import { useEmojiAnimation } from '~/composables/animation'
import type { AIRecipeInfo } from '~/packages/ai/src'
import { generateRecipeInfo, getRecipeImage } from '~/utils/api'
const rStore = useRecipeStore()
const curStuff = computed(() => rStore.selectedStuff)
const recipeBtnRef = ref<HTMLButtonElement>()
const { playAnimation } = useEmojiAnimation(recipeBtnRef)
const gtm = useGtm()
const recipePanelRef = ref()
const { isVisible, show } = useInvisibleElement(recipePanelRef)
function toggleStuff(item: StuffItem, category = '', _e?: Event) {
rStore.toggleStuff(item.name)
if (curStuff.value.includes(item.name))
playAnimation(item.emoji)
gtm?.trackEvent({
event: 'click',
category: `${category}_${item.name}`,
action: 'click_stuff',
label: '食材',
})
gtm?.trackEvent({
event: 'click_stuff',
action: item.name,
})
}
// cook recipe
const cooking = ref(false)
const recipeImg = ref('')
const aiRecipeInfo = ref<AIRecipeInfo>({
名称: '名称',
介绍: '介绍',
})
async function cook() {
cooking.value = true
const foods = rStore.selectedStuff
// reset
aiRecipeInfo.value = ({
名称: '起名中...',
介绍: '正在思考怎么介绍...',
})
recipeImg.value = ''
// generate
const [info, img] = await Promise.all([generateRecipeInfo(foods), getRecipeImage(foods)])
aiRecipeInfo.value = info
recipeImg.value = img
cooking.value = false
}
</script>
<template>
<div>
<h2 m="t-4" text="xl" font="bold" p="1">
🥘 先选一下食材
</h2>
<div>
<h2 opacity="90" text="base" font="bold" p="1">
🥬 菜菜们
</h2>
<div>
<VegetableTag
v-for="item, i in vegetable" :key="i"
:active="curStuff.includes(item.name)"
@click="toggleStuff(item, 'vegetable')"
>
<span v-if="item.emoji" class="inline-flex">{{ item.emoji }}</span>
<span v-else-if="item.image" class="inline-flex">
<img class="inline-flex" w="2" h="2" width="10" height="10" :src="item.image" :alt="item.name">
</span>
<span class="inline-flex" m="l-1">{{ item.name }}</span>
</VegetableTag>
</div>
</div>
<div m="y-4">
<h2 opacity="90" text="base" font="bold" p="1">
🥩 肉肉们
</h2>
<div>
<MeatTag
v-for="item, i in meat" :key="i"
:active="curStuff.includes(item.name)"
@click="toggleStuff(item, 'meat')"
>
<span>{{ item.emoji }}</span>
<span m="l-1">{{ item.name }}</span>
</MeatTag>
</div>
</div>
<div m="y-4">
<h2 opacity="90" text="base" font="bold" p="1">
🍚 主食
</h2>
<div>
<StapleTag
v-for="item, i in staple" :key="i"
:active="curStuff.includes(item.name)"
@click="toggleStuff(item, 'staple')"
>
<span>{{ item.emoji }}</span>
<span m="l-1">{{ item.name }}</span>
</StapleTag>
</div>
</div>
<!-- <div m="t-4">
<h2 text="xl" font="bold" p="1">
🍳 再选一下厨具
</h2>
<div>
<ToolTag
v-for="item, i in tools" :key="i"
:active="curTool === item.name"
@click="rStore.clickTool(item)"
>
<span v-if="item.emoji" class="inline-flex">
{{ item.emoji }}
</span>
<span v-else-if="item.icon" class="inline-flex">
<div :class="item.icon" />
</span>
<span class="inline-flex" m="l-1">{{ item.label || item.name }}</span>
</ToolTag>
</div>
</div> -->
<Transition>
<BasketButton ref="recipeBtnRef" :is-visible="isVisible" @click="show" />
</Transition>
<button
m-auto
flex items-center justify-center
class="rounded bg-yellow px-4 py-2 text-orange-900 font-black shadow hover:shadow-md active:shadow-inset"
@click="cook()"
>
<div v-if="cooking" class="mr-2 inline-flex" i-svg-spinners:clock />
<span>做黑暗料理 🥘</span>
</button>
<div
class="recipe-panel relative shadow transition"
m="x-2 y-4" p="2"
bg="gray-400/8"
>
<div text="xl" font="bold" p="1">
{{ aiRecipeInfo['名称'] }}
</div>
<div class="cook-recipes text-center" p="2">
<img
v-if="recipeImg"
class="m-auto w-25 rounded shadow transition hover:shadow-md"
:src="recipeImg"
alt="recipes"
>
<div v-else class="m-auto h-25 w-25 rounded bg-gray shadow transition hover:shadow-md" />
</div>
<div>
{{ aiRecipeInfo['介绍'] }}
</div>
</div>
</div>
</template>

View File

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

View File

@@ -1,6 +0,0 @@
<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 left-3 top-4 />
</main>
</template>

View File

@@ -1,6 +1,6 @@
import process from 'node:process'
import { pwa } from './config/pwa'
import { appDescription } from './constants/index'
import { pwa } from './app/config/pwa'
import { appDescription } from './app/constants/index'
Object.assign(process.env, {
VITE_COMMIT_REF: process.env.CF_PAGES_COMMIT_SHA || '',
@@ -9,29 +9,50 @@ Object.assign(process.env, {
// add build time to env
import.meta.env.VITE_APP_BUILD_TIME = new Date().getTime().toString()
export default defineNuxtConfig({
ssr: false,
modules: [
'@vueuse/nuxt',
'@unocss/nuxt',
'@pinia/nuxt',
'@nuxt/test-utils/module',
'@nuxtjs/color-mode',
'@vite-pwa/nuxt',
'@nuxt/eslint',
'@nuxt/test-utils/module',
'@zadigetvoltaire/nuxt-gtm',
'@yunlefun/vue/nuxt',
// fix QQ in iOS, Done
// See https://github.com/unjs/ofetch/pull/366
// 'nuxt-fix-ofetch',
],
ssr: false,
components: [
{ path: '~/components', pathPrefix: false },
],
experimental: {
// when using generate, payload js assets included in sw precache manifest
// but missing on offline, disabling extraction it until fixed
payloadExtraction: false,
// inlineSSRStyles: false,
renderJsonPayloads: true,
typedPages: true,
devtools: {
enabled: true,
},
app: {
head: {
viewport: 'width=device-width,initial-scale=1',
link: [
{ rel: 'icon', type: 'image/svg+xml', href: '/favicon.svg' },
{ rel: 'apple-touch-icon', href: '/apple-touch-icon.png' },
],
meta: [
{ name: 'viewport', content: 'width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no' },
{ name: 'description', content: appDescription },
{ name: 'apple-mobile-web-app-status-bar-style', content: 'black-translucent' },
{ name: 'theme-color', media: '(prefers-color-scheme: light)', content: 'white' },
{ name: 'theme-color', media: '(prefers-color-scheme: dark)', content: '#222222' },
],
},
},
css: [
@@ -44,6 +65,25 @@ export default defineNuxtConfig({
classSuffix: '',
},
future: {
compatibilityVersion: 4,
},
features: {
// For UnoCSS
inlineStyles: false,
},
experimental: {
// when using generate, payload js assets included in sw precache manifest
// but missing on offline, disabling extraction it until fixed
payloadExtraction: false,
renderJsonPayloads: true,
typedPages: true,
},
compatibilityDate: '2024-08-14',
nitro: {
esbuild: {
options: {
@@ -57,32 +97,18 @@ export default defineNuxtConfig({
},
},
app: {
head: {
viewport: 'width=device-width,initial-scale=1',
link: [
{ rel: 'icon', type: 'image/svg+xml', href: '/favicon.svg' },
{ rel: 'apple-touch-icon', href: '/apple-touch-icon.png' },
],
meta: [
{ name: 'viewport', content: 'width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no' },
{ name: 'description', content: appDescription },
{ name: 'apple-mobile-web-app-status-bar-style', content: 'black-translucent' },
],
eslint: {
config: {
standalone: false,
nuxt: {
sortConfigKeys: true,
},
},
},
components: [
{ path: '~/components', pathPrefix: false },
],
gtm: {
id: 'GTM-5FJSV46',
},
pwa,
devtools: {
enabled: true,
},
})

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