huaanglimeng/pages/index/index.js

446 lines
18 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.

// AI 配置 (建议后续迁移至后端以保证 API Key 安全)
const DEEPSEEK_API_KEY = 'sk-f659b4c3aa954baaa6cbdbd5cae0b7d1'; // 请在此处替换您的 API Key
const DEEPSEEK_BASE_URL = 'https://api.deepseek.com/chat/completions';
// --- Prompt 模板定义 ---
// 1. 基础版模板:平静、清晰、节制
const TAROT_PROMPT_BASE = `你是一位克制、温和、不制造依赖的塔罗解读者。
你的角色不是预测未来,而是陪伴当下的人理解此刻的状态。
【解读边界】
- 不给命令,不下结论,不使用“你应该”“结果是”“一定会”
- 不预测具体事件或时间,不制造焦虑或依赖
【语言风格】
- 平静、清晰、节制
- 使用“可能 / 正在 / 也许 / 可以感受到”
- 像是轻声说明,而不是说服或劝导,避免情绪渲染与神秘化表达
【解读结构】
1. 用 12 句话描绘这张牌当下呈现的状态氛围
2. 结合关键词与核心含义,解释它可能对应的内在状态
3. 用一句自然收口的话结束,不引导行动
【输出要求】
- 总字数 120180 字
- 只输出解读文本,不使用标题、编号、分段符号`;
// 2. 深夜版模板:更慢、更柔和、允许留白
const TAROT_PROMPT_NIGHT = `你是一位安静、克制、不制造依赖的塔罗解读者。
此刻的解读更像一次低声的陪伴,而不是解释或分析。
【解读边界】
- 不给建议,不下判断,不使用“你应该”“结果是”“接下来会”
- 不预测未来事件或变化,不放大情绪,不渲染痛苦
【语言风格】
- 更慢、更柔和
- 使用“也许 / 正在 / 像是 / 仿佛”
- 允许留白,不急着说清楚一切,文字像夜晚一样安静
【解读结构】
1. 先用一句贴近当下感受的描述开头
2. 围绕关键词展开,但重点放在“感受本身”
3. 用一句轻微、可停住的句子结束,不指向下一步
【输出要求】
- 总字数 100160 字
- 不使用感叹号,不使用列表、解释性语句
- 只输出解读文本`;
Page({
data: {
todayDate: '', // 用于存储今天的日期
// 1. 状态管理: idle, focusing, flipping, revealed
state: 'idle',
currentCard: null,
isRevealed: false,
explanation: '',
cardBackImage: '/images/beimian.png',
// 动画数据
deckAnimation: {},
cardAnimation: {},
infoAnimation: {},
guideAnimation: {},
// AI 相关
isAiLoading: false,
aiExplanation: '',
// 3. 前缀文案列表(随机抽取)
prefixList: [
"今天,这张牌提醒你:",
"此刻,宇宙传递的信息:",
"这张牌想告诉你:",
"请收下这份指引:"
],
// 2. 完整的 22 张大阿尔卡那牌组(包含逆位含义)
cardList: [
{
name: "愚者",
keyword: "新的开始",
meaning: "放下恐惧,勇敢迈出第一步,世界充满无限可能。",
reversedMeaning: "也许有些太冲动了?停下来检查一下背包,确保安全再出发。",
energy: "外放",
image: "/images/yuzhe.png"
},
{
name: "魔术师",
keyword: "创造力",
meaning: "你拥有实现目标所需的一切资源,现在是行动的时候。",
reversedMeaning: "你是否低估了自己的能力?或者在犹豫不决?相信自己,工具就在手边。",
energy: "外放",
image: "/images/moshushi.png"
},
{
name: "女祭司",
keyword: "直觉",
meaning: "倾听内心的声音,答案就在你潜意识的深处。",
reversedMeaning: "暂时听不见内心的声音了吗?别急着向外寻找,先让自己静下来。",
energy: "内敛",
image: "/images/nvjishi.png"
},
{
name: "皇后",
keyword: "丰饶",
meaning: "享受生活的美好,去创造、去关爱,收获也是一种能力。",
reversedMeaning: "是否忽略了对自己的关爱?或者有些过度保护?给彼此一点呼吸的空间。",
energy: "柔和",
image: "/images/nvhuang.png"
},
{
name: "皇帝",
keyword: "秩序",
meaning: "建立规则和结构,理性地掌控局面,承担起责任。",
reversedMeaning: "有时候太固执会让人疲惫,适度放权,弹性也是一种力量。",
energy: "坚实",
image: "/images/huangdi.png"
},
{
name: "教皇",
keyword: "指引",
meaning: "寻求传统智慧的帮助,或者成为他人的精神导师。",
reversedMeaning: "不必拘泥于陈规旧矩,试着打破常规,寻找适合你自己的那条路。",
energy: "庄重",
image: "/images/jiaohuang.png"
},
{
name: "恋人",
keyword: "选择",
meaning: "跟随你的心,在关系中寻找和谐,做出忠于自我的决定。",
reversedMeaning: "现在的关系是否有些失衡?先爱自己,才能更好地爱别人。",
energy: "柔和",
image: "/images/lianren.png"
},
{
name: "战车",
keyword: "意志",
meaning: "认准目标,克服相反的力量,胜利属于坚持到底的人。",
reversedMeaning: "感觉失去了方向控制?慢下来,重新调整导航,欲速则不达。",
energy: "激进",
image: "/images/zhanche.png"
},
{
name: "力量",
keyword: "勇气",
meaning: "真正的力量是温柔的坚持,用耐心驯服内心的野兽。",
reversedMeaning: "不要怀疑自己的韧性,面对内心的软弱并不是坏事,那是温柔的开始。",
energy: "柔和",
image: "/images/liliang.png"
},
{
name: "隐士",
keyword: "内省",
meaning: "暂时远离喧嚣,向内探索,寻找属于你自己的光。",
reversedMeaning: "`是不是一个人待太久了?试着走出洞穴,和外界建立一点连接吧。",
energy: "潜沉",
image: "/images/yinzhe.png"
},
{
name: "命运之轮",
keyword: "转折",
meaning: "改变是永恒的,顺势而为,抓住出现在你面前的机会。",
reversedMeaning: "运气暂时不在你这边,但没关系,低谷正是蓄力反弹的好时机。",
energy: "流转",
image: "/images/mingyunzhilun.png"
},
{
name: "正义",
keyword: "因果",
meaning: "诚实面对真相,所有的决定都会带来相应的结果。",
reversedMeaning: "即使结果不尽如人意,也要问心无愧。对自己诚实,比什么都重要。",
energy: "平衡",
image: "/images/zhengyi.png"
},
{
name: "倒吊人",
keyword: "换位",
meaning: "换一个角度看世界,有时候牺牲是为了更大的获得。",
reversedMeaning: "挣扎只会更累,不如彻底放松,换个角度看世界,也许心结就解开了。",
energy: "停滞",
image: "/images/daodiaoren.png"
},
{
name: "死神",
keyword: "新生",
meaning: "结束是为了新的开始,彻底告别过去,才能拥抱未来。",
reversedMeaning: "还在紧抓着过去不放吗?只有腾出双手,才能接住新的礼物。",
energy: "决绝",
image: "/images/sishen.png"
},
{
name: "节制",
keyword: "平衡",
meaning: "在极端之间寻找中庸之道,融合对立,通过耐心获得疗愈。",
reversedMeaning: "是不是有些失衡了?在这忙乱的世界里,找回你内心的节奏和中点。",
energy: "融合",
image: "/images/jiezhi.png"
},
{
name: "恶魔",
keyword: "束缚",
meaning: "觉察那些控制你的欲望或习惯,唯有觉知才能带来解脱。",
reversedMeaning: "别被眼前的诱惑蒙蔽,或者感觉被束缚。其实锁链是松的,你随时可以走。",
energy: "沉重",
image: "/images/emo.png"
},
{
name: "高塔",
keyword: "崩塌",
meaning: "不要害怕突如其来的剧变,它在摧毁虚假的根基,让你重建真实。",
reversedMeaning: "废墟中反而能看清地基。既然倒了,正好可以按照你真正想要的样子重建。",
energy: "剧烈",
image: "/images/ta.png"
},
{
name: "星星",
keyword: "希望",
meaning: "黑暗之后必有星光,保持信心,未来充满治愈与灵感。",
reversedMeaning: "暂时看不见星星也没关系,它们还在那里。给自己一点信心,黎明就在前方。",
energy: "明亮",
image: "/images/xingxing.png"
},
{
name: "月亮",
keyword: "幻象",
meaning: "直面内心的不安与迷茫,并非所有事情都如表象般真实。",
reversedMeaning: "恐惧只是长长的影子,别被它吓住。随着天亮,迷雾终会散去。",
energy: "幽静",
image: "/images/yueliang.png"
},
{
name: "太阳",
keyword: "喜悦",
meaning: "快乐、成功与活力,尽情地发光热,享受当下的幸福。",
reversedMeaning: "乌云暂时遮住了阳光,但这只是暂时的。保持乐观,你心里的光谁也偷不走。",
energy: "灼热",
image: "/images/taiyang.png"
},
{
name: "审判",
keyword: "觉醒",
meaning: "听到内心的召唤,回顾过往,做出决定,获得新生。",
reversedMeaning: "不要对他人的评价太敏感,也不要自我怀疑。如果你准备好了,就出发吧。",
energy: "回响",
image: "/images/shenpan.png"
},
{
name: "世界",
keyword: "圆满",
meaning: "一段旅程的完美终点,成就感与整合,准备开始新的循环。",
reversedMeaning: "机遇还没完全成熟,再耐心一点点。检查一下还有什么细节没完成?",
energy: "圆融",
meaning: "一段旅程的完美终点,成就感与整合,准备开始新的循环。",
reversedMeaning: "机遇还没完全成熟,再耐心一点点。检查一下还有什么细节没完成?",
image: "/images/shijie.png"
}
]
},
onLoad: function () {
// 获取当前日期
const now = new Date();
const year = now.getFullYear();
const month = (now.getMonth() + 1).toString().padStart(2, '0');
const day = now.getDate().toString().padStart(2, '0');
const dateStr = `${year}-${month}-${day}`;
this.setData({
todayDate: dateStr
});
this.startIdleAnimation();
console.log('塔罗牌(完整版 22 张 + 逆位文案)已经洗好了...')
},
// --- 待机呼吸动画 ---
startIdleAnimation: function () {
const animation = wx.createAnimation({
duration: 2000,
timingFunction: 'ease-in-out',
});
const next = () => {
if (this.data.state !== 'idle' && this.data.state !== 'focusing') return;
animation.translateY(4).step();
animation.translateY(0).step();
this.setData({
deckAnimation: animation.export()
});
};
next();
this.idleTimer = setInterval(next, 4000);
},
// --- 抽一张牌:进入 focusing 状态 ---
drawCard: function () {
if (this.data.state !== 'idle' && this.data.state !== 'revealed') return;
// 停止之前的呼吸动画计时器(如果有)
if (this.idleTimer) clearInterval(this.idleTimer);
this.setData({
state: 'focusing',
currentCard: null,
isRevealed: false,
explanation: ''
});
// 引导文案淡入
const guideAnim = wx.createAnimation({ duration: 500, timingFunction: 'ease' });
guideAnim.opacity(1).step();
this.setData({ guideAnimation: guideAnim.export() });
// 停顿 500-800ms 后进入 flipping
setTimeout(() => {
this.startFlipping();
}, 800);
},
// --- 开始翻牌流程 ---
startFlipping: function () {
const allCards = this.data.cardList;
const randomIndex = Math.floor(Math.random() * allCards.length);
const selected = Object.assign({}, allCards[randomIndex]);
selected.isReversed = Math.random() >= 0.5;
const prefixes = this.data.prefixList;
const randomPrefixIndex = Math.floor(Math.random() * prefixes.length);
const selectedPrefix = prefixes[randomPrefixIndex];
this.setData({
state: 'flipping',
currentCard: selected,
explanation: `${selectedPrefix}${selected.isReversed ? selected.reversedMeaning : selected.meaning}`
});
// 卡牌抬起并旋转的动画
const cardAnim = wx.createAnimation({
duration: 500,
timingFunction: 'ease-in-out'
});
// 轻微抬起 (Y -10~-15px) 和旋转 (≤ 3°)
cardAnim.translateY(-12).rotate(Math.random() * 6 - 3).step();
this.setData({ cardAnimation: cardAnim.export() });
// 翻转动画是在 WXML 中通过 CSS 类控制的,这里保持一致
setTimeout(() => {
this.setData({ isRevealed: true });
// 翻牌完成后留白 300-500ms
setTimeout(() => {
this.setData({ state: 'revealed' });
this.showInterpretation();
}, 500);
}, 500);
},
// --- 展示解读内容 ---
showInterpretation: function () {
const infoAnim = wx.createAnimation({
duration: 800,
timingFunction: 'ease'
});
infoAnim.opacity(1).translateY(0).step();
this.setData({ infoAnimation: infoAnim.export() });
// 异步获取 AI 解读
this.fetchAiInterpretation();
},
// --- 获取 AI 解读主逻辑 (DeepSeek 直接调用版) ---
fetchAiInterpretation: function () {
const card = this.data.currentCard;
if (!card) return;
this.setData({
isAiLoading: true,
aiExplanation: ''
});
// 1. 构建结构化 Prompt
const position = card.isReversed ? '逆位' : '正位';
const core = card.isReversed ? card.reversedMeaning : card.meaning;
// 当前版本统一使用基础解读 Prompt (TAROT_PROMPT_BASE)
const systemPrompt = TAROT_PROMPT_BASE;
const userPrompt = `请基于以下信息给出解读:
牌名:${card.name}
位置:${position}
关键词:${card.keyword}
一句话核心解读:${core}
能量倾向:${card.energy}`;
console.log("正在通过 DeepSeek 生成解读...");
// 2. 调用 DeepSeek API
wx.request({
url: DEEPSEEK_BASE_URL,
method: 'POST',
header: {
'Authorization': `Bearer ${DEEPSEEK_API_KEY}`,
'Content-Type': 'application/json'
},
data: {
model: "deepseek-chat",
messages: [
{ role: "system", content: systemPrompt },
{ role: "user", content: userPrompt }
],
stream: false,
temperature: 0.7
},
timeout: 15000,
success: (res) => {
if (res.data && res.data.choices && res.data.choices[0]) {
const aiResult = res.data.choices[0].message.content;
this.setData({
aiExplanation: aiResult,
isAiLoading: false
});
} else {
console.warn("DeepSeek 返回异常,执行回退");
this.handleAiFallback();
}
},
fail: (err) => {
console.error("DeepSeek 请求失败:", err);
this.handleAiFallback();
}
});
},
// --- AI 解读失败后的回退处理 ---
handleAiFallback: function () {
this.setData({
aiExplanation: this.data.explanation,
isAiLoading: false
});
},
})