Files
what2eat/app.js
T
2026-07-26 17:14:33 +08:00

178 lines
9.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const meals = [
{ name: "番茄牛腩饭", icon: "🍅", flavor: "rich", budget: "mid", group: true, note: "酸甜开胃 · 热乎下饭" },
{ name: "麻辣香锅", icon: "🌶️", flavor: "spicy", budget: "mid", group: true, note: "自由搭配 · 快乐翻倍" },
{ name: "鸡丝凉面", icon: "🍜", flavor: "fresh", budget: "low", group: false, note: "清爽不腻 · 午后轻盈" },
{ name: "海南鸡饭", icon: "🍗", flavor: "light", budget: "mid", group: false, note: "鲜嫩咸香 · 稳稳满足" },
{ name: "寿喜烧", icon: "🍲", flavor: "rich", budget: "high", group: true, note: "甜咸浓郁 · 犒劳一下" },
{ name: "酸菜鱼", icon: "🐟", flavor: "spicy", budget: "high", group: true, note: "酸辣鲜香 · 超级下饭" },
{ name: "牛肉粉", icon: "🥣", flavor: "spicy", budget: "low", group: false, note: "暖胃满足 · 快速回血" },
{ name: "轻食沙拉", icon: "🥗", flavor: "light", budget: "mid", group: false, note: "清新脆爽 · 没有负担" },
{ name: "咖喱猪排饭", icon: "🍛", flavor: "rich", budget: "mid", group: false, note: "酥脆浓香 · 能量满格" },
{ name: "小笼包", icon: "🥟", flavor: "light", budget: "low", group: true, note: "一口爆汁 · 简单满足" },
{ name: "韩式烤肉", icon: "🥩", flavor: "rich", budget: "high", group: true, note: "焦香多汁 · 适合聚餐" },
{ name: "重庆小面", icon: "🍜", flavor: "spicy", budget: "low", group: false, note: "麻辣带劲 · 唤醒味蕾" },
{ name: "日式拉面", icon: "🍥", flavor: "rich", budget: "mid", group: false, note: "汤浓面弹 · 暖心满足" },
{ name: "广式烧腊饭", icon: "🍖", flavor: "rich", budget: "mid", group: false, note: "蜜香油润 · 经典不出错" },
{ name: "云南米线", icon: "🍜", flavor: "fresh", budget: "low", group: false, note: "鲜香顺滑 · 嗦粉快乐" },
{ name: "泰式冬阴功", icon: "🦐", flavor: "spicy", budget: "high", group: true, note: "酸辣醒胃 · 南洋风味" },
{ name: "椰子鸡火锅", icon: "🥥", flavor: "light", budget: "high", group: true, note: "清甜鲜嫩 · 聚餐友好" },
{ name: "东北铁锅炖", icon: "🍲", flavor: "rich", budget: "high", group: true, note: "热气腾腾 · 越吃越香" },
{ name: "披萨拼盘", icon: "🍕", flavor: "rich", budget: "mid", group: true, note: "口味任选 · 分享方便" },
{ name: "潮汕牛肉火锅", icon: "🥩", flavor: "fresh", budget: "high", group: true, note: "鲜切现涮 · 团队人气王" },
{ name: "越南牛肉河粉", icon: "🌿", flavor: "fresh", budget: "mid", group: false, note: "清鲜有层次 · 舒服耐吃" },
{ name: "菌菇焖饭", icon: "🍄", flavor: "light", budget: "low", group: false, note: "菌香温润 · 素食也满足" },
{ name: "新疆大盘鸡", icon: "🍗", flavor: "spicy", budget: "high", group: true, note: "香辣量足 · 多人更合适" },
{ name: "粤式点心", icon: "🫖", flavor: "light", budget: "mid", group: true, note: "多款分享 · 慢慢吃聊" }
];
const KEYS = {
favorites: "whats2eat-favorites",
history: "whats2eat-history"
};
const state = { flavor: "all", budget: "all", mode: "solo", current: null, activeList: "favorites" };
const $ = (selector) => document.querySelector(selector);
const $$ = (selector) => document.querySelectorAll(selector);
const readStore = (key) => JSON.parse(localStorage.getItem(key) || "[]");
const writeStore = (key, value) => localStorage.setItem(key, JSON.stringify(value));
const mealByName = (name) => meals.find((meal) => meal.name === name);
function bindExclusiveButtons(selector, stateKey, onChange) {
$$(selector).forEach((button) => {
button.addEventListener("click", () => {
$$(selector).forEach((item) => {
item.classList.remove("active");
item.setAttribute("aria-pressed", "false");
});
button.classList.add("active");
button.setAttribute("aria-pressed", "true");
state[stateKey] = button.dataset.value || button.dataset.mode;
if (onChange) onChange();
});
});
}
function setModeCopy() {
const isTeam = state.mode === "team";
$("#heroSubtitle").textContent = isTeam
? "少数服从运气,找一个大家都愿意去的答案。"
: "一个人的午饭,也值得认真决定。";
$("#modeBadge").textContent = isTeam ? "同事聚餐推荐" : "一人食推荐";
$("#status").textContent = isTeam ? "聚餐模式会优先推荐适合分享的选择。" : "一人食模式会优先推荐方便又满足的选择。";
}
function filteredMeals() {
let pool = meals.filter((meal) =>
(state.flavor === "all" || meal.flavor === state.flavor) &&
(state.budget === "all" || meal.budget === state.budget) &&
(state.mode !== "team" || meal.group)
);
if (pool.length > 1 && state.current) pool = pool.filter((meal) => meal.name !== state.current.name);
return pool;
}
function pickMeal() {
const pool = filteredMeals();
if (!pool.length) {
$("#status").textContent = "这个组合暂时没有合适选项,试试放宽一个条件吧。";
return;
}
const meal = pool[Math.floor(Math.random() * pool.length)];
const card = $("#resultCard");
card.classList.remove("shuffling");
void card.offsetWidth;
card.classList.add("shuffling");
setTimeout(() => showResult(meal, true), 260);
}
function showResult(meal, addToHistory) {
state.current = meal;
$("#resultIcon").textContent = meal.icon;
$("#resultKicker").textContent = state.mode === "team" ? "大家今天就吃" : "今天就吃";
$("#resultName").textContent = meal.name;
$("#resultMeta").textContent = meal.note;
$("#status").textContent = `答案是「${meal.name}」,不要再纠结啦!`;
updateSaveButton(readStore(KEYS.favorites).includes(meal.name));
if (addToHistory) {
const history = readStore(KEYS.history).filter((name) => name !== meal.name);
history.unshift(meal.name);
writeStore(KEYS.history, history.slice(0, 12));
if (state.activeList === "history") renderList();
}
}
function updateSaveButton(saved) {
$("#saveBtn").classList.toggle("saved", saved);
$("#saveBtn").setAttribute("aria-pressed", String(saved));
$("#saveBtn").textContent = saved ? "♥ 已收藏" : "♡ 收藏";
}
function toggleFavorite() {
if (!state.current) {
$("#status").textContent = "先抽一个午饭答案,再收藏它吧。";
return;
}
const favorites = readStore(KEYS.favorites);
const index = favorites.indexOf(state.current.name);
if (index >= 0) favorites.splice(index, 1);
else favorites.unshift(state.current.name);
writeStore(KEYS.favorites, favorites);
updateSaveButton(index < 0);
renderList();
}
function renderList() {
const names = readStore(KEYS[state.activeList]);
$("#favoriteCount").textContent = readStore(KEYS.favorites).length;
$("#clearHistory").hidden = state.activeList !== "history" || names.length === 0;
$("#mealList").innerHTML = names.length
? names.map((name) => {
const meal = mealByName(name);
if (!meal) return "";
const action = state.activeList === "favorites"
? `<button class="remove-item" data-remove="${meal.name}" aria-label="取消收藏 ${meal.name}">×</button>`
: "";
return `<article class="meal-item">
<span class="meal-item-icon" aria-hidden="true">${meal.icon}</span>
<span><strong>${meal.name}</strong><small>${meal.note}</small></span>
${action}
</article>`;
}).join("")
: `<div class="empty">${state.activeList === "favorites" ? "还没有收藏,遇到心动午饭就点一下 ♡" : "还没有抽取记录,今天的第一顿会是什么?"}</div>`;
$$("[data-remove]").forEach((button) => {
button.addEventListener("click", () => {
writeStore(KEYS.favorites, readStore(KEYS.favorites).filter((name) => name !== button.dataset.remove));
if (state.current?.name === button.dataset.remove) updateSaveButton(false);
renderList();
});
});
}
function switchList(listName, shouldScroll = false) {
state.activeList = listName;
$$(".tab").forEach((tab) => {
const active = tab.dataset.list === listName;
tab.classList.toggle("active", active);
tab.setAttribute("aria-selected", String(active));
});
renderList();
if (shouldScroll) $(".collection").scrollIntoView({ behavior: "smooth", block: "start" });
}
bindExclusiveButtons("#flavorChips .chip", "flavor", () => $("#status").textContent = "口味收到,随时可以开奖。");
bindExclusiveButtons("#budgetChips .chip", "budget", () => $("#status").textContent = "预算收到,随时可以开奖。");
bindExclusiveButtons(".mode", "mode", setModeCopy);
$("#decideBtn").addEventListener("click", pickMeal);
$("#againBtn").addEventListener("click", pickMeal);
$("#saveBtn").addEventListener("click", toggleFavorite);
$$(".tab").forEach((tab) => tab.addEventListener("click", () => switchList(tab.dataset.list)));
$$("[data-open-list]").forEach((button) => button.addEventListener("click", () => switchList(button.dataset.openList, true)));
$("#clearHistory").addEventListener("click", () => {
writeStore(KEYS.history, []);
renderList();
});
setModeCopy();
renderList();