🍦

欢迎来到 POGO 噗菓

给自己取一个昵称,开始你的冰淇淋之旅吧

POGO噗菓

城市漫游地图

popo

日记

点击日期进入二级页面

2026.07

心情日历

日期默认没有冰淇淋图标。上传照片保存后,才会显示对应贴纸。

写日记

选口味、拍照片、取名字、写几句

选择冰淇淋口味

上传冰淇淋照片

📷 点击上传冰淇淋照片

上传后会自动裁剪成圆角贴纸

给这支冰淇淋取个名字

今天的日记

店铺

自己添加店铺名字、图片,再进行评分

0 家

添加新店铺

店铺名字
地点
特别推荐口味
店铺图片

我的店铺

店铺评分

给你自己添加的店铺打分

Shop

这是你添加的店铺,可以在这里评分和写打卡笔记。

评分

还没有评分

打卡笔记

💬 社区留言板

分享你的打卡体验,和其他冰淇淋爱好者交流

它的店铺

来自社区分享的店铺

Shop

💬 社区留言板

调整取景
拖动调整图片位置
* * (注意要在 app.js 之前加载) */ // ============ 配置 ============ const SUPABASE_URL = 'https://xchdbremnjxlqmvwcvzm.supabase.co'; const SUPABASE_ANON_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InhjaGRicmVtbmp4bHFtdndjdnptIiwicm9sZSI6ImFub24iLCJpYXQiOjE3ODMwOTU1ODEsImV4cCI6MjA5ODY3MTU4MX0.1KOd9WnVd7vehX7o6wqp5eeWBmmhgHh8-y2M-K18Yiw'; // ============ 初始化客户端 ============ let _supabase = null; /** * 获取 Supabase 客户端实例 * 需要先在页面中引入 @supabase/supabase-js CDN */ function getSupabase() { if (_supabase) return _supabase; // 检查是否已加载 supabase-js if (typeof window.supabase === 'undefined' && typeof window.createClient === 'undefined') { console.warn('[Supabase] supabase-js 未加载,跨设备同步不可用'); return null; } try { const createClient = window.supabase?.createClient || window.createClient; _supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY); console.log('[Supabase] 客户端初始化成功'); return _supabase; } catch (e) { console.error('[Supabase] 初始化失败:', e); return null; } } /** 检查 Supabase 是否可用 */ function isSupabaseReady() { return SUPABASE_URL !== 'https://YOUR_PROJECT.supabase.co' && getSupabase() !== null; } // ============ 店铺同步 ============ /** * 获取所有社区店铺(不同设备共享) * @returns {Promise} 店铺列表 */ async function supabaseGetShops() { const sb = getSupabase(); if (!sb) return []; try { const { data, error } = await sb .from('community_shops') .select('*') .order('created_at', { ascending: false }); if (error) throw error; return data || []; } catch (e) { console.warn('[Supabase] 获取店铺失败:', e.message); return []; } } /** * 上传店铺到 Supabase(让其他设备能看到) * @param {Object} shop - { id, name, location, recommendFlavor, imageUrl, ownerNick } */ async function supabaseAddShop(shop) { const sb = getSupabase(); if (!sb) return false; try { const { error } = await sb.from('community_shops').insert({ id: shop.id, name: shop.name, location: shop.location || '', recommend_flavor: shop.recommendFlavor || '', image_url: shop.imageUrl || shop.image || '', owner_nick: shop.ownerNick || '匿名用户' }); if (error) throw error; console.log('[Supabase] 店铺上传成功:', shop.name); return true; } catch (e) { console.warn('[Supabase] 店铺上传失败:', e.message); return false; } } /** * 删除社区店铺 * @param {string} shopId - 店铺 ID */ async function supabaseDeleteShop(shopId) { const sb = getSupabase(); if (!sb) return false; try { const { error } = await sb.from('community_shops').delete().eq('id', shopId); if (error) throw error; console.log('[Supabase] 店铺删除成功:', shopId); return true; } catch (e) { console.warn('[Supabase] 店铺删除失败:', e.message); return false; } } // ============ 论坛留言同步 ============ /** * 获取某店铺的所有论坛留言(跨设备共享) * @param {string} shopId - 店铺 ID * @returns {Promise} 留言列表(按时间倒序) */ async function supabaseGetForum(shopId) { const sb = getSupabase(); if (!sb) return []; try { const { data, error } = await sb .from('community_forum') .select('*') .eq('shop_id', shopId) .order('created_at', { ascending: false }); if (error) throw error; return (data || []).map(p => ({ nick: p.nick, text: p.text, star: p.star || 0, time: new Date(p.created_at).getTime() })); } catch (e) { console.warn('[Supabase] 获取留言失败:', e.message); return []; } } /** * 发布论坛留言(所有人都能看到) * @param {string} shopId - 店铺 ID * @param {Object} post - { nick, text, star } */ async function supabaseAddForumPost(shopId, post) { const sb = getSupabase(); if (!sb) return false; try { const { error } = await sb.from('community_forum').insert({ shop_id: shopId, nick: post.nick, text: post.text, star: post.star || 0 }); if (error) throw error; console.log('[Supabase] 留言发布成功'); return true; } catch (e) { console.warn('[Supabase] 留言发布失败:', e.message); return false; } } // ============ 评分打卡同步 ============ /** * 获取某店铺的所有用户评分(聚合) * @param {string} shopId - 店铺 ID * @returns {Promise} { average, total, checkins: [] } */ async function supabaseGetCheckins(shopId) { const sb = getSupabase(); if (!sb) return { average: 0, total: 0, checkins: [] }; try { const { data, error } = await sb .from('community_checkins') .select('*') .eq('shop_id', shopId) .order('created_at', { ascending: false }); if (error) throw error; const checkins = data || []; const total = checkins.length; const sum = checkins.reduce((s, c) => s + (c.rating || 0), 0); const average = total > 0 ? Math.round(sum / total * 10) / 10 : 0; return { average, total, checkins: checkins.map(c => ({ nick: c.nick, rating: c.rating, note: c.note || '', time: new Date(c.created_at).getTime() })) }; } catch (e) { console.warn('[Supabase] 获取评分失败:', e.message); return { average: 0, total: 0, checkins: [] }; } } /** * 提交评分打卡(其他用户可看到) * @param {string} shopId - 店铺 ID * @param {Object} checkin - { nick, rating, note } */ async function supabaseAddCheckin(shopId, checkin) { const sb = getSupabase(); if (!sb) return false; try { const { error } = await sb.from('community_checkins').insert({ shop_id: shopId, nick: checkin.nick, rating: checkin.rating, note: checkin.note || '' }); if (error) throw error; console.log('[Supabase] 评分打卡成功'); return true; } catch (e) { console.warn('[Supabase] 评分打卡失败:', e.message); return false; } } // ============ 图片上传(可选,用 Supabase Storage) ============ /** * 上传图片到 Supabase Storage * 需要先在 Supabase 创建一个名为 "shop-images" 的 public bucket * @param {File} file - 图片文件 * @returns {Promise} 图片公开 URL,失败返回 null */ async function supabaseUploadImage(file) { const sb = getSupabase(); if (!sb) return null; try { const ext = file.name.split('.').pop() || 'jpg'; const fileName = `shop_${Date.now()}_${Math.random().toString(36).slice(2, 8)}.${ext}`; const { error: uploadError } = await sb.storage .from('shop-images') .upload(fileName, file); if (uploadError) throw uploadError; const { data } = sb.storage.from('shop-images').getPublicUrl(fileName); console.log('[Supabase] 图片上传成功:', data.publicUrl); return data.publicUrl; } catch (e) { console.warn('[Supabase] 图片上传失败:', e.message); return null; } } // ============ 便捷封装:自动回退本地 ============ /** * 获取社区店铺(先尝试 Supabase,失败回退本地缓存) */ async function fetchCommunityShops() { if (isSupabaseReady()) { const shops = await supabaseGetShops(); if (shops.length > 0) { // 缓存到本地 try { localStorage.setItem('pogo_community_shops_cache', JSON.stringify(shops)); } catch (e) {} return shops; } } // 回退到本地缓存 try { const cache = JSON.parse(localStorage.getItem('pogo_community_shops_cache') || '[]'); return cache; } catch (e) { return []; } } /** * 获取论坛留言(先尝试 Supabase,失败回退本地) */ async function fetchForumPosts(shopId) { if (isSupabaseReady()) { const posts = await supabaseGetForum(shopId); if (posts.length > 0) return posts; } // 回退到本地 try { const data = JSON.parse(localStorage.getItem('pogo_fresh_forum') || '{}'); return data[shopId] || []; } catch (e) { return []; } } /** * 发布留言(同时写入 Supabase 和本地) */ async function postForumMessage(shopId, post) { // 先写本地 try { const data = JSON.parse(localStorage.getItem('pogo_fresh_forum') || '{}'); if (!data[shopId]) data[shopId] = []; data[shopId].push({ ...post, time: Date.now() }); localStorage.setItem('pogo_fresh_forum', JSON.stringify(data)); } catch (e) {} // 再写 Supabase if (isSupabaseReady()) { await supabaseAddForumPost(shopId, post); } } // ============ 实时订阅(可选高级功能) ============ /** * 订阅论坛留言变化(新留言实时推送) * @param {string} shopId - 店铺 ID * @param {Function} callback - 回调函数 (newPosts) => void * @returns {Object|null} 订阅对象(用于取消订阅) */ function subscribeForum(shopId, callback) { const sb = getSupabase(); if (!sb) return null; try { const subscription = sb .channel(`forum:${shopId}`) .on('postgres_changes', { event: 'INSERT', schema: 'public', table: 'community_forum', filter: `shop_id=eq.${shopId}` }, (payload) => { console.log('[Supabase] 收到新留言:', payload.new); // 重新拉取完整列表 supabaseGetForum(shopId).then(callback); } ) .subscribe(); console.log('[Supabase] 已订阅论坛:', shopId); return subscription; } catch (e) { console.warn('[Supabase] 订阅失败:', e.message); return null; } } /** * 取消订阅 */ function unsubscribeForum(subscription) { if (subscription && typeof subscription.unsubscribe === 'function') { subscription.unsubscribe(); console.log('[Supabase] 已取消订阅'); } }
html2.link ·粘贴 HTML,一键生成链接
举报