feat: optimize tarot functionality, navigation, ritual experience and standardized spreads
This commit is contained in:
parent
2b7fc07969
commit
a9b583fd37
3
app.json
3
app.json
|
|
@ -1,8 +1,7 @@
|
|||
{
|
||||
"pages": [
|
||||
"pages/home/home",
|
||||
"pages/index/index",
|
||||
"pages/zodiac/index"
|
||||
"pages/index/index"
|
||||
],
|
||||
"window": {
|
||||
"navigationBarTitleText": "我的第一个小程序"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,341 @@
|
|||
Page({
|
||||
data: {
|
||||
selectedZodiac: null,
|
||||
currentFortune: null, // { keyword, today, lucky, notice, match, action, summary }
|
||||
isMatchExpanded: false, // 控制关系提示的展开状态
|
||||
|
||||
// 12星座数据 - 增加 summary 字段
|
||||
zodiacList: [
|
||||
{
|
||||
name: '白羊座',
|
||||
symbol: '♈',
|
||||
range: '3.21 – 4.19',
|
||||
fortunes: [
|
||||
{
|
||||
keyword: '开启',
|
||||
today: '感觉能量在一点点回升,像温柔的晨光,正如适合开启一个小计划。',
|
||||
lucky: '试着做一件从未做过的小事,哪怕只是换条路漫步回家。',
|
||||
notice: '稍微慢下来也没关系,深呼吸,给自己一点缓冲的时间。',
|
||||
action: '也许可以尝试一项简单的运动,或者把那个想了很久的小决定做了。',
|
||||
match: '狮子座',
|
||||
summary: '整体氛围积极向上,适宜迈出新的一步。'
|
||||
},
|
||||
{
|
||||
keyword: '直觉',
|
||||
today: '直觉敏锐的一天,内心的声音会温柔地指引你去对的地方。',
|
||||
lucky: '流一点汗,让身体的能量自由流动起来。',
|
||||
notice: '倾听也是一种力量,试着先听听别人的想法。',
|
||||
action: '相信你的第一反应,今天不需要过度分析,跟着感觉走就好。',
|
||||
match: '射手座',
|
||||
summary: '这是一个依靠感性而非逻辑的时刻,信任直觉。'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: '金牛座',
|
||||
symbol: '♉',
|
||||
range: '4.20 – 5.20',
|
||||
fortunes: [
|
||||
{
|
||||
keyword: '安稳',
|
||||
today: '享受当下的安稳就好,物质的触感能带给你实实在在的安全感。',
|
||||
lucky: '一杯温热的茶,或是一顿用心准备的晚餐,都是很好的滋养。',
|
||||
notice: '柔软一点,你会发现世界比想象中更宽阔。',
|
||||
action: '花点时间整理一下钱包或书桌,有序的环境会让你感到平静。',
|
||||
match: '处女座',
|
||||
summary: '平稳是今日的主基调,无需向外过多索求。'
|
||||
},
|
||||
{
|
||||
keyword: '秩序',
|
||||
today: '按部就班也很好,秩序感会让你感到踏实和放松。',
|
||||
lucky: '在整理收纳中梳理思绪,享受物品归位的快乐。',
|
||||
notice: '试着松开手,有时候放手反而能拥有更多。',
|
||||
action: '去做一次舒缓的按摩,或者仅仅是静静地听一段白噪音。',
|
||||
match: '摩羯座',
|
||||
summary: '在规则与秩序中能找到内心的宁静。'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: '双子座',
|
||||
symbol: '♊',
|
||||
range: '5.21 – 6.21',
|
||||
fortunes: [
|
||||
{
|
||||
keyword: '灵感',
|
||||
today: '思维很活跃,灵感像小火花一样在闪烁。',
|
||||
lucky: '翻翻新书的目录,或和陌生人简单聊几句。',
|
||||
notice: '试着一次只做一件事情,专注会让你更舒服。',
|
||||
action: '随身带一个小本子,记录下今天那些有趣的小念头。',
|
||||
match: '水瓶座',
|
||||
summary: '思维跳跃的一天,由于灵感的迸发而显得生动。'
|
||||
},
|
||||
{
|
||||
keyword: '表达',
|
||||
today: '沟通的桥梁已经架好,适合真实而轻松地表达自己。',
|
||||
lucky: '记录下脑海中一闪而过的念头,它们很珍贵。',
|
||||
notice: '有时候稍微沉默一下,给自己留点空间。',
|
||||
action: '给许久未见的老朋友发个信息,简单的问候就很好。',
|
||||
match: '天秤座',
|
||||
summary: '适宜交流与分享,信息的流动会带来新的机会。'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: '巨蟹座',
|
||||
symbol: '♋',
|
||||
range: '6.22 – 7.22',
|
||||
fortunes: [
|
||||
{
|
||||
keyword: '疗愈',
|
||||
today: '情感细腻丰富,适合向内探索,疗愈旧日的伤口。',
|
||||
lucky: '去水边走走,水的包容会让你感到平静。',
|
||||
notice: '保护好自己的情绪边界,不用对所有人的话都太在意。',
|
||||
action: '今晚给自己做一顿暖胃的食物,或者泡一个舒服的热水澡。',
|
||||
match: '双鱼座',
|
||||
summary: '向内关注自我,是一个适合自我修复的温和日子。'
|
||||
},
|
||||
{
|
||||
keyword: '关怀',
|
||||
today: '温柔是你的铠甲,你对他人的关怀也会回流到自己身上。',
|
||||
lucky: '给家人打个电话,或者照顾一株植物。',
|
||||
notice: '过去已去,当下才是最真实的,试着活在此时此刻。',
|
||||
action: '整理一下旧照片,保留快乐的记忆就好。',
|
||||
match: '天蝎座',
|
||||
summary: '被温柔的情绪包围,付出与接受都自然发生。'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: '狮子座',
|
||||
symbol: '♌',
|
||||
range: '7.23 – 8.22',
|
||||
fortunes: [
|
||||
{
|
||||
keyword: '舞台',
|
||||
today: '自信的光芒很美,你本身就是舞台的中心。',
|
||||
lucky: '穿一件喜欢的亮色衣服,或者大方地接受别人的赞美。',
|
||||
notice: '真正的王者懂得示弱的艺术,柔软也是一种力量。',
|
||||
action: '对着镜子里的自己微笑,肯定自己的一个优点。',
|
||||
match: '白羊座',
|
||||
summary: '光芒外露,适宜展示自我,但也需张弛有度。'
|
||||
},
|
||||
{
|
||||
keyword: '创造',
|
||||
today: '创造力在涌动,与其等待机会,不如自己创造一个小舞台。',
|
||||
lucky: '像孩子一样去玩耍,画画或者唱歌都很棒。',
|
||||
notice: '保持谦逊,你会看到更多不一样的风景。',
|
||||
action: '尝试一种新的娱乐方式,或者去一个从未去过的店。',
|
||||
match: '射手座',
|
||||
summary: '创造力充沛,是表达个性和释放天性的好时机。'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: '处女座',
|
||||
symbol: '♍',
|
||||
range: '8.23 – 9.22',
|
||||
fortunes: [
|
||||
{
|
||||
keyword: '清晰',
|
||||
today: '逻辑很清晰,能看透事物的本质,混乱将离你而去。',
|
||||
lucky: '列一张简单的清单,每划掉一项都是一种治愈。',
|
||||
notice: '对自己宽容一点,完美是一个方向,而不是必须到达的终点。',
|
||||
action: '清理手机里无用的截图和APP,数字极简也能带来清爽。',
|
||||
match: '金牛座',
|
||||
summary: '条理清晰,事半功倍,适合处理复杂的细节事务。'
|
||||
},
|
||||
{
|
||||
keyword: '服务',
|
||||
today: '助人的愿望很美好,你的细心能帮到很多人。',
|
||||
lucky: '做一次深度的清洁,无论是环境还是心灵。',
|
||||
notice: '偶尔也要抬头看看森林,不要总是盯着细节看。',
|
||||
action: '在这个周末,尝试“一次只做一件事”,体验专注的快乐。',
|
||||
match: '摩羯座',
|
||||
summary: '在付出中获得满足感,但也需注意微观与宏观的平衡。'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: '天秤座',
|
||||
symbol: '♎',
|
||||
range: '9.23 – 10.23',
|
||||
fortunes: [
|
||||
{
|
||||
keyword: '平衡',
|
||||
today: '追求和谐与美,在平衡中找到内心的宁静。',
|
||||
lucky: '去美术馆逛逛,或者欣赏一首优美的乐曲。',
|
||||
notice: '不要为了取悦他人而委屈自己,温和地拒绝也是可以的。',
|
||||
action: '更换一下手机或电脑的壁纸,换成一张让你觉得美的图片。',
|
||||
match: '双子座',
|
||||
summary: '适宜在审美与艺术中寻找平衡,享受片刻的优雅。'
|
||||
},
|
||||
{
|
||||
keyword: '连接',
|
||||
today: '人际关系很顺畅,适合化解误会,建立新的连接。',
|
||||
lucky: '买一束花插在瓶中,为生活增添一点仪式感。',
|
||||
notice: '犹豫不决时,抛硬币也是一种把命运交给天意的智慧。',
|
||||
action: '主动赞美身边的一个人,真诚的赞美会让大家都很开心。',
|
||||
match: '水瓶座',
|
||||
summary: '良性的互动会带来好运,人际关系是今日的重点。'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: '天蝎座',
|
||||
symbol: '♏',
|
||||
range: '10.24 – 11.22',
|
||||
fortunes: [
|
||||
{
|
||||
keyword: '洞察',
|
||||
today: '洞察力很强,能看穿表象,直达事物的核心。',
|
||||
lucky: '在日记本里写下秘密,或者进行一次深刻的冥想。',
|
||||
notice: '放下执念,学会像蛇蜕皮一样重生。',
|
||||
action: '读一本悬疑小说或者看一部深度电影,满足你的探索欲。',
|
||||
match: '巨蟹座',
|
||||
summary: '直觉与洞察力并存,适合深入思考而非浅尝辄止。'
|
||||
},
|
||||
{
|
||||
keyword: '坚定',
|
||||
today: '意志力坚定,现在是攻克难题的好时机。',
|
||||
lucky: '独处,享受一个人的高质量时光。',
|
||||
notice: '试着交付一点信任,你会发现没有那么多需要防备的事。',
|
||||
action: '尝试与一个信任的人进行一次深度谈话,不设防的那种。',
|
||||
match: '双鱼座'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: '射手座',
|
||||
symbol: '♐',
|
||||
range: '11.23 – 12.21',
|
||||
fortunes: [
|
||||
{
|
||||
keyword: '自由',
|
||||
today: '视野开阔,心向远方,自由的感觉真好。',
|
||||
lucky: '规划一次未来的旅行,或者看一部异国电影。',
|
||||
notice: '承诺之前先想一下,不要因为一时兴起而许下诺言。',
|
||||
action: '去户外走走,甚至只是去楼下公园转一圈,看看天空。',
|
||||
match: '白羊座',
|
||||
summary: '心向自由,适合拓展视野,不宜被琐事困住眼界。'
|
||||
},
|
||||
{
|
||||
keyword: '乐观',
|
||||
today: '乐观的精神很有感染力,你是身边人的开心果。',
|
||||
lucky: '去户外奔跑,去接触大自然和新鲜空气。',
|
||||
notice: '检查一下随身物品,细心一点能避免小麻烦。',
|
||||
action: '学习一个全新的冷知识,或者尝试一种异国料理。',
|
||||
match: '狮子座',
|
||||
summary: '积极乐观的一天,行动力是好运的来源。'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: '摩羯座',
|
||||
symbol: '♑',
|
||||
range: '12.22 – 1.19',
|
||||
fortunes: [
|
||||
{
|
||||
keyword: '踏实',
|
||||
today: '脚踏实地,每一步都算数,成就感源于自律。',
|
||||
lucky: '制定一个长期的目标,并迈出第一步。',
|
||||
notice: '不要把自己逼得太紧,工作之余也要学会“虚度时光”。',
|
||||
action: '复盘一下本周的计划,把完成的事项画上大大的勾。',
|
||||
match: '金牛座',
|
||||
summary: '耕耘即有收获,适合专注于当下脚踏实地的积累。'
|
||||
},
|
||||
{
|
||||
keyword: '权威',
|
||||
today: '权威感提升,你的专业度会得到大家的认可。',
|
||||
lucky: '向一位长辈或导师请教,汲取传统的智慧。',
|
||||
notice: '给生活加点糖,不用总是那么严肃。',
|
||||
action: '给自己泡一杯好茶或好咖啡,在工作间隙享受片刻宁静。',
|
||||
match: '处女座',
|
||||
summary: '专业与稳重是今日的通行证,但也需适度放松。'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: '水瓶座',
|
||||
symbol: '♒',
|
||||
range: '1.20 – 2.18',
|
||||
fortunes: [
|
||||
{
|
||||
keyword: '创意',
|
||||
today: '特立独行的一天,你的怪念头也许就是天才的创意。',
|
||||
lucky: '尝试一种新的科技产品,或者研究一个小众领域。',
|
||||
notice: '给身边人一点温度,理智之外也需要温情。',
|
||||
action: '去浏览一些前沿科技或艺术资讯,给大脑“充充电”。',
|
||||
match: '双子座',
|
||||
summary: '思维活跃且独特,适合进行创造性的脑力活动。'
|
||||
},
|
||||
{
|
||||
keyword: '理想',
|
||||
today: '为了理想而战,你的视野超越了当下,看向未来。',
|
||||
lucky: '参加一个社群活动,寻找志同道合的伙伴。',
|
||||
notice: '保持开放的心态,固执己见和坚持真理只有一线之隔。',
|
||||
action: '换一条平时不走的路去上班/回家,观察沿途的新风景。',
|
||||
match: '天秤座',
|
||||
summary: '目光长远,适合规划未来,不宜纠结于眼前的小节。'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: '双鱼座',
|
||||
symbol: '♓',
|
||||
range: '2.19 – 3.20',
|
||||
fortunes: [
|
||||
{
|
||||
keyword: '灵感',
|
||||
today: '梦幻与现实的边界模糊,灵感如潮水般涌来。',
|
||||
lucky: '听海浪的声音,或者在睡前读一首诗。',
|
||||
notice: '勇敢面对现实问题,比躲进幻想更有效。',
|
||||
action: '听一首纯音乐,闭上眼睛想象一个完美的场景。',
|
||||
match: '巨蟹座',
|
||||
summary: '感受力极强的一天,适合艺术创作与精神探索。'
|
||||
},
|
||||
{
|
||||
keyword: '同理心',
|
||||
today: '同理心爆棚,你能轻易感知他人的悲喜。',
|
||||
lucky: '做一件善事,哪怕只是给流浪猫一点食物。',
|
||||
notice: '学会自我净化,不要做情绪的海绵。',
|
||||
action: '写下一件让你感到焦虑的事,然后把纸条撕碎扔掉。',
|
||||
match: '天蝎座',
|
||||
summary: '情感流动充沛,在关怀他人的同时也需照顾好自己。'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// 选择星座
|
||||
selectZodiac: function (e) {
|
||||
const index = e.currentTarget.dataset.index;
|
||||
const zodiac = this.data.zodiacList[index];
|
||||
|
||||
const randomIdx = Math.floor(Math.random() * zodiac.fortunes.length);
|
||||
const fortuneData = zodiac.fortunes[randomIdx];
|
||||
|
||||
this.setData({
|
||||
selectedZodiac: zodiac,
|
||||
currentFortune: fortuneData,
|
||||
isMatchExpanded: false // 重置展开状态
|
||||
});
|
||||
},
|
||||
|
||||
// 切换关系提示展开状态
|
||||
toggleMatch: function () {
|
||||
this.setData({
|
||||
isMatchExpanded: !this.data.isMatchExpanded
|
||||
});
|
||||
},
|
||||
|
||||
// 返回列表
|
||||
backToList: function () {
|
||||
this.setData({
|
||||
selectedZodiac: null,
|
||||
currentFortune: null,
|
||||
isMatchExpanded: false // 重置展开状态
|
||||
});
|
||||
}
|
||||
})
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"navigationBarTitleText": "星座运势"
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
<view class="container">
|
||||
|
||||
<!-- 状态1: 星座列表 -->
|
||||
<view class="zodiac-grid" wx:if="{{!selectedZodiac}}">
|
||||
<view class="header-tip">选择你的星座</view>
|
||||
<view class="grid-container">
|
||||
<block wx:for="{{zodiacList}}" wx:key="name">
|
||||
<view class="zodiac-item" bindtap="selectZodiac" data-index="{{index}}">
|
||||
<text class="zodiac-symbol">{{item.symbol}}</text>
|
||||
<text class="zodiac-name">{{item.name}}</text>
|
||||
<text class="zodiac-range">{{item.range}}</text>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 状态2: 运势展示 -->
|
||||
<view class="result-container" wx:if="{{selectedZodiac}}">
|
||||
<view class="result-header">
|
||||
<text class="result-symbol">{{selectedZodiac.symbol}}</text>
|
||||
<text class="today-title">今日指引 · {{selectedZodiac.name}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 三段式卡片 -->
|
||||
<view class="fortune-card">
|
||||
|
||||
<!-- 0. 今日关键词 (NEW) -->
|
||||
<view class="keyword-card">
|
||||
<text class="keyword-label">✨ 今日关键词</text>
|
||||
<text class="keyword-text">{{currentFortune.keyword}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 1. 今日运势 -->
|
||||
<view class="section primary-section">
|
||||
<text class="section-label">✨ 今日运势</text>
|
||||
<text class="section-content">{{currentFortune.today}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 2. 幸运提示 -->
|
||||
<view class="section">
|
||||
<text class="section-label">🍀 幸运提示</text>
|
||||
<text class="section-content">{{currentFortune.lucky}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 3. 注意事项 -->
|
||||
<view class="section">
|
||||
<text class="section-label">💡 注意事项</text>
|
||||
<text class="section-content">{{currentFortune.notice}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 4. 今日行动建议 (NEW) -->
|
||||
<view class="section last-section">
|
||||
<text class="section-label">🌱 今日行动建议</text>
|
||||
<text class="section-content">{{currentFortune.action}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 5. 关系提示 (可展开) -->
|
||||
<view class="status-divider"></view> <!-- 分隔线 -->
|
||||
<view class="match-module" bindtap="toggleMatch">
|
||||
<text class="match-trigger">{{isMatchExpanded ? '收起关系提示' : '🌸 今日关系提示(点击查看)'}}</text>
|
||||
|
||||
<view class="match-content" wx:if="{{isMatchExpanded}}">
|
||||
<text class="match-intro">今天,与你能量更顺的星座是:</text>
|
||||
<text class="match-name">{{currentFortune.match}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
||||
<!-- 总体结论 (收束模块) -->
|
||||
<view class="summary-text">{{currentFortune.summary}}</view>
|
||||
|
||||
<view class="action-area" bindtap="backToList">
|
||||
<text class="back-btn">⬅ 选其他星座</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
|
@ -0,0 +1,247 @@
|
|||
/* 保持与整体风格一致的暗色背景 */
|
||||
page {
|
||||
background-color: #1a1a2e;
|
||||
height: 100%;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 30px 20px;
|
||||
box-sizing: border-box;
|
||||
min-height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* --- 列表状态样式 --- */
|
||||
.header-tip {
|
||||
font-size: 20px;
|
||||
margin-bottom: 30px;
|
||||
text-align: center;
|
||||
opacity: 0.9;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.grid-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.zodiac-item {
|
||||
width: 30%; /* 三列布局 */
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
margin-bottom: 20px;
|
||||
padding: 20px 0;
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.zodiac-symbol {
|
||||
font-size: 28px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.zodiac-name {
|
||||
font-size: 14px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.zodiac-range {
|
||||
font-size: 10px;
|
||||
opacity: 0.5;
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
/* --- 结果状态样式 --- */
|
||||
.result-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-top: 20px;
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.result-header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-bottom: 30px;
|
||||
animation: fadeIn 0.8s ease;
|
||||
}
|
||||
|
||||
.result-symbol {
|
||||
font-size: 50px;
|
||||
margin-bottom: 10px;
|
||||
color: #e94560;
|
||||
}
|
||||
|
||||
.today-title {
|
||||
font-size: 18px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.fortune-card {
|
||||
width: 100%;
|
||||
padding: 25px;
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
border-radius: 16px;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 40px;
|
||||
border: 1px solid rgba(233, 69, 96, 0.3);
|
||||
animation: slideUp 0.8s ease;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* 0. 关键词卡片样式 */
|
||||
.keyword-card {
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
border-radius: 12px;
|
||||
padding: 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-bottom: 25px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.keyword-label {
|
||||
font-size: 12px;
|
||||
color: #a29bfe;
|
||||
margin-bottom: 5px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.keyword-text {
|
||||
font-size: 32px; /* 进一步加大关键词字号 */
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
letter-spacing: 4px;
|
||||
margin-top: 5px;
|
||||
text-shadow: 0 2px 10px rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
/* 三段式内容的样式 */
|
||||
.section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 25px;
|
||||
border-bottom: 1px dashed rgba(255, 255, 255, 0.1); /* 分隔线 */
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
|
||||
.last-section {
|
||||
margin-bottom: 0;
|
||||
border-bottom: none;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.section-label {
|
||||
font-size: 14px;
|
||||
color: #a29bfe; /* 浅紫色标签 */
|
||||
margin-bottom: 8px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.section-content {
|
||||
font-size: 14px; /* 二级信息字号调小 */
|
||||
line-height: 1.6;
|
||||
color: #fff;
|
||||
opacity: 0.75; /* 二级信息透明度降低 */
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
/* 一级信息强调样式 */
|
||||
.primary-section .section-content {
|
||||
font-size: 17px;
|
||||
opacity: 1;
|
||||
font-weight: 500;
|
||||
line-height: 1.7;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.primary-section .section-label {
|
||||
font-size: 15px;
|
||||
color: #e94560; /* 稍微强调标题颜色 */
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.back-btn {
|
||||
font-size: 14px;
|
||||
opacity: 0.6;
|
||||
text-decoration: underline;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
/* 简单的动效 */
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes slideUp {
|
||||
from { opacity: 0; transform: translateY(20px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
/* 关系模块样式 */
|
||||
.status-divider {
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
.match-module {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 10px 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.match-trigger {
|
||||
font-size: 14px;
|
||||
color: #fbaceb; /* 淡淡的粉色 */
|
||||
opacity: 0.8;
|
||||
text-decoration: underline;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.match-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-top: 15px;
|
||||
animation: fadeIn 0.5s ease;
|
||||
}
|
||||
|
||||
.match-intro {
|
||||
font-size: 12px;
|
||||
color: #fff;
|
||||
opacity: 0.6;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.match-name {
|
||||
font-size: 18px;
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* 总体结论样式 */
|
||||
.summary-text {
|
||||
font-size: 13px;
|
||||
color: rgba(255, 255, 255, 0.5); /* 中性灰 */
|
||||
margin-top: -20px; /* 调整间距 */
|
||||
margin-bottom: 30px;
|
||||
text-align: center;
|
||||
max-width: 80%;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
|
@ -3,11 +3,5 @@ Page({
|
|||
wx.navigateTo({
|
||||
url: '/pages/index/index'
|
||||
})
|
||||
},
|
||||
|
||||
goToZodiac: function () {
|
||||
wx.navigateTo({
|
||||
url: '/pages/zodiac/index'
|
||||
})
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -16,14 +16,6 @@
|
|||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 星座入口 -->
|
||||
<view class="direction-card" bindtap="goToZodiac" hover-class="card-hover">
|
||||
<view class="card-icon">♒</view>
|
||||
<view class="card-content">
|
||||
<text class="card-title">星座运势</text>
|
||||
<text class="card-subtitle">解析星体给你的启示</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</view>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@
|
|||
const DEEPSEEK_API_KEY = 'sk-f659b4c3aa954baaa6cbdbd5cae0b7d1'; // 请在此处替换您的 API Key
|
||||
const DEEPSEEK_BASE_URL = 'https://api.deepseek.com/chat/completions';
|
||||
|
||||
// --- 数据导入 ---
|
||||
const { minorArcana } = require('../../data/tarot/index');
|
||||
|
||||
// --- Prompt 模板定义 ---
|
||||
|
||||
// 1. 基础版模板:平静、清晰、节制
|
||||
|
|
@ -45,19 +48,95 @@ const TAROT_PROMPT_NIGHT = `你是一位安静、克制、不制造依赖的塔
|
|||
3. 用一句轻微、可停住的句子结束,不指向下一步
|
||||
|
||||
【输出要求】
|
||||
- 总字数 100–160 字
|
||||
- 不使用感叹号,不使用列表、解释性语句
|
||||
- 只输出解读文本`;
|
||||
|
||||
// 3. 增强版模板:结构化深度解读
|
||||
const TAROT_PROMPT_ENHANCED = `你是一位专业、温和且具有洞察力的塔罗解读者。
|
||||
你的任务是根据选定的牌阵,为用户提供有深度、有层次且具有陪伴感的解读。
|
||||
|
||||
【解读边界】
|
||||
- 不预测具体事件、不给绝对结论、不使用恐吓性语言
|
||||
- 保持温和的引导口气,像是在进行一场心灵对话
|
||||
|
||||
【解读结构】
|
||||
请严格按照以下 JSON 格式输出解读内容,不要包含任何其他说明性文字:
|
||||
{
|
||||
"theme": "用一句话概括本次解读的核心主题",
|
||||
"status": "描述用户当下的情绪或处境状态",
|
||||
"influence": "分析当前局面背后的潜在影响因素(内在或外在)",
|
||||
"advice": "给出具体、温和且具有操作性的行动建议",
|
||||
"positions": [
|
||||
{
|
||||
"posName": "位置名称",
|
||||
"posMeaning": "由你结合牌面对该位置的详细解读(约 60-100 字)"
|
||||
}
|
||||
]
|
||||
}`;
|
||||
|
||||
// --- 牌阵定义 ---
|
||||
const SPREADS = [
|
||||
{
|
||||
"id": "one_card_guidance",
|
||||
"name": "单张指引",
|
||||
"cardCount": 1,
|
||||
"positions": ["当下的指引"],
|
||||
"description": "为你此刻的状态提供一个温和而清晰的方向提示。",
|
||||
"aiSchema": ["core_theme", "current_state", "action_advice"]
|
||||
},
|
||||
{
|
||||
"id": "three_time_flow",
|
||||
"name": "过去 · 现在 · 未来",
|
||||
"cardCount": 3,
|
||||
"positions": ["过去", "现在", "未来"],
|
||||
"description": "帮助你理解事情的发展过程与可能走向。",
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"]
|
||||
},
|
||||
{
|
||||
"id": "three_problem_solution",
|
||||
"name": "问题 · 阻碍 · 建议",
|
||||
"cardCount": 3,
|
||||
"positions": ["问题核心", "当前阻碍", "行动建议"],
|
||||
"description": "聚焦关键问题,找出当下最可行的应对方式。",
|
||||
"aiSchema": ["core_theme", "current_state", "action_advice"]
|
||||
},
|
||||
{
|
||||
"id": "five_situation_analysis",
|
||||
"name": "现状分析",
|
||||
"cardCount": 5,
|
||||
"positions": ["现状", "内在因素", "外在影响", "行动方向", "可能结果"],
|
||||
"description": "从内外层面拆解局势,明确下一步行动。",
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"]
|
||||
},
|
||||
{
|
||||
"id": "two_choice_decision",
|
||||
"name": "二选一抉择",
|
||||
"cardCount": 4,
|
||||
"positions": ["选择A的发展", "选择A的结果", "选择B的发展", "选择B的结果"],
|
||||
"description": "对比两种选择的潜在走向,辅助理性决策。",
|
||||
"aiSchema": ["core_theme", "potential_influence", "action_advice"]
|
||||
},
|
||||
{
|
||||
"id": "relationship_spread",
|
||||
"name": "关系洞察",
|
||||
"cardCount": 5,
|
||||
"positions": ["你的位置", "对方的位置", "关系现状", "隐藏影响", "未来趋势"],
|
||||
"description": "理解一段关系中的互动模式与发展方向。",
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"]
|
||||
}
|
||||
];
|
||||
|
||||
Page({
|
||||
data: {
|
||||
todayDate: '', // 用于存储今天的日期
|
||||
// 1. 状态管理: idle, focusing, flipping, revealed
|
||||
state: 'idle',
|
||||
currentCard: null,
|
||||
isRevealed: false,
|
||||
explanation: '',
|
||||
// 1. 状态管理: spread_select, idle, shuffling, drawing, flipping, revealed
|
||||
state: 'spread_select', // 初始进入牌阵选择
|
||||
selectedSpread: null,
|
||||
drawnCardIndices: [], // 用户选中的牌索引
|
||||
drawnCards: [], // 实际抽到的牌对象
|
||||
revealedCount: 0, // 已翻开的数量
|
||||
|
||||
cardBackImage: '/images/beimian.png',
|
||||
spreads: SPREADS,
|
||||
|
||||
// 动画数据
|
||||
deckAnimation: {},
|
||||
|
|
@ -253,11 +332,12 @@ Page({
|
|||
meaning: "一段旅程的完美终点,成就感与整合,准备开始新的循环。",
|
||||
reversedMeaning: "机遇还没完全成熟,再耐心一点点。检查一下还有什么细节没完成?",
|
||||
energy: "圆融",
|
||||
meaning: "一段旅程的完美终点,成就感与整合,准备开始新的循环。",
|
||||
reversedMeaning: "机遇还没完全成熟,再耐心一点点。检查一下还有什么细节没完成?",
|
||||
image: "/images/shijie.png"
|
||||
}
|
||||
]
|
||||
],
|
||||
// 合并大牌和小牌
|
||||
allCards: [],
|
||||
aiResult: null
|
||||
},
|
||||
|
||||
onLoad: function () {
|
||||
|
|
@ -269,95 +349,78 @@ Page({
|
|||
const dateStr = `${year}-${month}-${day}`;
|
||||
|
||||
this.setData({
|
||||
todayDate: dateStr
|
||||
todayDate: dateStr,
|
||||
allCards: [...this.data.cardList, ...minorArcana]
|
||||
});
|
||||
|
||||
this.startIdleAnimation();
|
||||
console.log('塔罗牌(完整版 22 张 + 逆位文案)已经洗好了...')
|
||||
console.log(`塔罗牌已就位,共 ${this.data.allCards.length} 张。`)
|
||||
},
|
||||
|
||||
// --- 待机呼吸动画 ---
|
||||
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);
|
||||
|
||||
// --- 1. 选择牌阵 ---
|
||||
selectSpread: function (e) {
|
||||
const id = e.currentTarget.dataset.id;
|
||||
const spread = this.data.spreads.find(s => s.id === id);
|
||||
this.setData({
|
||||
state: 'focusing',
|
||||
currentCard: null,
|
||||
isRevealed: false,
|
||||
explanation: ''
|
||||
selectedSpread: spread,
|
||||
state: 'shuffling'
|
||||
});
|
||||
|
||||
// 引导文案淡入
|
||||
const guideAnim = wx.createAnimation({ duration: 500, timingFunction: 'ease' });
|
||||
guideAnim.opacity(1).step();
|
||||
this.setData({ guideAnimation: guideAnim.export() });
|
||||
|
||||
// 停顿 500-800ms 后进入 flipping
|
||||
// 模拟洗牌感应 2.5s
|
||||
setTimeout(() => {
|
||||
this.startFlipping();
|
||||
}, 800);
|
||||
this.setData({ state: 'drawing' });
|
||||
}, 2500);
|
||||
},
|
||||
|
||||
// --- 开始翻牌流程 ---
|
||||
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;
|
||||
// --- 2. 抽牌逻辑 ---
|
||||
onCardTap: function (e) {
|
||||
const index = e.currentTarget.dataset.index;
|
||||
let { drawnCardIndices, selectedSpread } = this.data;
|
||||
|
||||
const prefixes = this.data.prefixList;
|
||||
const randomPrefixIndex = Math.floor(Math.random() * prefixes.length);
|
||||
const selectedPrefix = prefixes[randomPrefixIndex];
|
||||
if (drawnCardIndices.includes(index)) {
|
||||
drawnCardIndices = drawnCardIndices.filter(i => i !== index);
|
||||
} else if (drawnCardIndices.length < selectedSpread.cardCount) {
|
||||
drawnCardIndices.push(index);
|
||||
}
|
||||
|
||||
this.setData({ drawnCardIndices });
|
||||
},
|
||||
|
||||
// --- 3. 确认开启 ---
|
||||
confirmDraw: function () {
|
||||
const { drawnCardIndices, allCards } = this.data;
|
||||
const drawnCards = drawnCardIndices.map(() => {
|
||||
const randomIndex = Math.floor(Math.random() * allCards.length);
|
||||
const card = Object.assign({}, allCards[randomIndex]);
|
||||
card.isReversed = Math.random() >= 0.5;
|
||||
// 如果这张牌没有图片(如小牌),使用背面图作为占位符,或保持原有逻辑
|
||||
if (!card.image) {
|
||||
card.image = this.data.cardBackImage;
|
||||
}
|
||||
return card;
|
||||
});
|
||||
|
||||
this.setData({
|
||||
drawnCards,
|
||||
state: 'flipping',
|
||||
currentCard: selected,
|
||||
explanation: `${selectedPrefix}${selected.isReversed ? selected.reversedMeaning : selected.meaning}`
|
||||
revealedCount: 0
|
||||
});
|
||||
},
|
||||
|
||||
// 卡牌抬起并旋转的动画
|
||||
const cardAnim = wx.createAnimation({
|
||||
duration: 500,
|
||||
timingFunction: 'ease-in-out'
|
||||
});
|
||||
// --- 4. 逐一翻牌 ---
|
||||
revealNext: function (e) {
|
||||
const index = e.currentTarget.dataset.index;
|
||||
if (index === this.data.revealedCount) {
|
||||
const nextCount = index + 1;
|
||||
this.setData({
|
||||
revealedCount: nextCount
|
||||
});
|
||||
|
||||
// 轻微抬起 (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(() => {
|
||||
// 如果全部翻开,触发解读
|
||||
if (nextCount === this.data.selectedSpread.cardCount) {
|
||||
this.setData({ state: 'revealed' });
|
||||
this.showInterpretation();
|
||||
}, 500);
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// --- 展示解读内容 ---
|
||||
|
|
@ -373,33 +436,25 @@ Page({
|
|||
this.fetchAiInterpretation();
|
||||
},
|
||||
|
||||
// --- 获取 AI 解读主逻辑 (DeepSeek 直接调用版) ---
|
||||
// --- 获取 AI 解读主逻辑 ---
|
||||
fetchAiInterpretation: function () {
|
||||
const card = this.data.currentCard;
|
||||
if (!card) return;
|
||||
const { drawnCards, selectedSpread } = this.data;
|
||||
if (!drawnCards.length) return;
|
||||
|
||||
this.setData({
|
||||
isAiLoading: true,
|
||||
aiExplanation: ''
|
||||
aiResult: null
|
||||
});
|
||||
|
||||
// 1. 构建结构化 Prompt
|
||||
const position = card.isReversed ? '逆位' : '正位';
|
||||
const core = card.isReversed ? card.reversedMeaning : card.meaning;
|
||||
// 1. 构建卡牌信息
|
||||
const cardDetails = drawnCards.map((card, index) => {
|
||||
return `位置[${selectedSpread.positions[index]}]:${card.name} (${card.isReversed ? '逆位' : '正位'}),关键词:${card.keyword || (card.keywords ? card.keywords.join(',') : '')}`;
|
||||
}).join('\n');
|
||||
|
||||
// 当前版本统一使用基础解读 Prompt (TAROT_PROMPT_BASE)
|
||||
const systemPrompt = TAROT_PROMPT_BASE;
|
||||
const userPrompt = `牌阵:${selectedSpread.name}\n${cardDetails}`;
|
||||
|
||||
const userPrompt = `请基于以下信息给出解读:
|
||||
牌名:${card.name}
|
||||
位置:${position}
|
||||
关键词:${card.keyword}
|
||||
一句话核心解读:${core}
|
||||
能量倾向:${card.energy}`;
|
||||
console.log("正在获取深度解读...");
|
||||
|
||||
console.log("正在通过 DeepSeek 生成解读...");
|
||||
|
||||
// 2. 调用 DeepSeek API
|
||||
wx.request({
|
||||
url: DEEPSEEK_BASE_URL,
|
||||
method: 'POST',
|
||||
|
|
@ -410,37 +465,82 @@ Page({
|
|||
data: {
|
||||
model: "deepseek-chat",
|
||||
messages: [
|
||||
{ role: "system", content: systemPrompt },
|
||||
{ role: "system", content: TAROT_PROMPT_ENHANCED },
|
||||
{ role: "user", content: userPrompt }
|
||||
],
|
||||
stream: false,
|
||||
temperature: 0.7
|
||||
response_format: { type: "json_object" }
|
||||
},
|
||||
timeout: 15000,
|
||||
timeout: 20000,
|
||||
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
|
||||
});
|
||||
try {
|
||||
const aiResult = JSON.parse(res.data.choices[0].message.content);
|
||||
this.setData({
|
||||
aiResult,
|
||||
isAiLoading: false
|
||||
});
|
||||
} catch (e) {
|
||||
console.error("JSON 解析失败", e);
|
||||
this.handleAiFallback();
|
||||
}
|
||||
} else {
|
||||
console.warn("DeepSeek 返回异常,执行回退");
|
||||
this.handleAiFallback();
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error("DeepSeek 请求失败:", err);
|
||||
console.error("请求失败", err);
|
||||
this.handleAiFallback();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// --- AI 解读失败后的回退处理 ---
|
||||
handleAiFallback: function () {
|
||||
// 构建一个基础的回退对象
|
||||
const fallback = {
|
||||
theme: "能量感应受阻",
|
||||
status: "当前思绪可能较为杂乱",
|
||||
influence: "外界干扰或网络波动",
|
||||
advice: "建议深呼吸,稍后再次尝试开启心灵对话",
|
||||
positions: this.data.drawnCards.map((card, index) => ({
|
||||
posName: this.data.selectedSpread.positions[index],
|
||||
posMeaning: `${card.name}${card.isReversed ? '(逆位)' : '(正位)'}:${card.isReversed ? (card.reversedMeaning || card.description) : (card.meaning || card.description)}`
|
||||
}))
|
||||
};
|
||||
this.setData({
|
||||
aiExplanation: this.data.explanation,
|
||||
aiResult: fallback,
|
||||
isAiLoading: false
|
||||
});
|
||||
},
|
||||
})
|
||||
|
||||
resetSpread: function () {
|
||||
this.setData({
|
||||
state: 'spread_select',
|
||||
selectedSpread: null,
|
||||
drawnCardIndices: [],
|
||||
drawnCards: [],
|
||||
revealedCount: 0,
|
||||
aiResult: null
|
||||
});
|
||||
},
|
||||
|
||||
// --- 导航优化:统一返回逻辑 ---
|
||||
handleBack: function () {
|
||||
const pages = getCurrentPages();
|
||||
if (pages.length > 1) {
|
||||
wx.navigateBack({
|
||||
delta: 1
|
||||
});
|
||||
} else {
|
||||
// 兜底逻辑:如果是在第一层,则跳转回首页
|
||||
wx.switchTab({
|
||||
url: '/pages/home/home'
|
||||
}).catch(() => {
|
||||
// 如果不是 tabBar,则使用 reLaunch
|
||||
wx.reLaunch({
|
||||
url: '/pages/home/home'
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"navigationBarTitleText": "塔罗指引",
|
||||
"usingComponents": {}
|
||||
}
|
||||
|
|
@ -1,76 +1,141 @@
|
|||
<view class="container">
|
||||
<view class="title-area">
|
||||
<text class="app-title">塔罗 · 今日指引</text>
|
||||
<text class="app-subtitle">📅 今日指引 · {{todayDate}}</text>
|
||||
<text class="instruction" style="font-size: 14px; margin-top: 5px; opacity: 0.8; color: #ccc;">当你需要一个答案时</text>
|
||||
</view>
|
||||
|
||||
<!-- 标题区域 -->
|
||||
<view class="header">
|
||||
<text class="app-title">🔮 神秘塔罗 🔮</text>
|
||||
<!-- 自定义返回按钮 -->
|
||||
<view class="nav-back" bindtap="handleBack">
|
||||
<text class="back-icon">⇠</text>
|
||||
<text class="back-text">返回</text>
|
||||
</view>
|
||||
|
||||
<!-- 1. 待机与引导状态 -->
|
||||
<view class="intro" wx:if="{{state === 'idle' || state === 'focusing'}}">
|
||||
<view class="deck-container" animation="{{deckAnimation}}" bindtap="drawCard">
|
||||
<image class="card-deck" src="{{cardBackImage}}" mode="widthFix"></image>
|
||||
<text class="tap-hint" wx:if="{{state === 'idle'}}">点击开始抽牌</text>
|
||||
<!-- 1. 牌阵选择状态 -->
|
||||
<view class="spread-select-area" wx:if="{{state === 'spread_select'}}">
|
||||
<view class="title-area">
|
||||
<text class="app-title">选择你的牌阵</text>
|
||||
<text class="instruction">请根据你的困惑选择一个适合的牌阵</text>
|
||||
</view>
|
||||
|
||||
<view class="guide-text" animation="{{guideAnimation}}" style="opacity: 0;">
|
||||
<text class="instruction">请在心中默念你的问题</text>
|
||||
<view class="spread-list">
|
||||
<view class="spread-item" wx:for="{{spreads}}" wx:key="id" bindtap="selectSpread" data-id="{{item.id}}" hover-class="spread-hover">
|
||||
<text class="spread-name">{{item.name}}</text>
|
||||
<text class="spread-desc">{{item.description}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 2. 抽牌与结果展示状态 -->
|
||||
<view class="card-display" wx:if="{{state === 'flipping' || state === 'revealed'}}">
|
||||
<view class="flip-scene" animation="{{cardAnimation}}">
|
||||
<view class="flip-card {{isRevealed ? 'flipped' : ''}}">
|
||||
<!-- A. 牌背 -->
|
||||
<view class="card-face face-back">
|
||||
<image class="card-image" src="{{cardBackImage}}" mode="widthFix"></image>
|
||||
</view>
|
||||
<!-- 2. 洗牌状态 (展示动画) -->
|
||||
<view class="ritual-area" wx:if="{{state === 'shuffling'}}">
|
||||
<view class="shuffling-container">
|
||||
<image class="shuffling-card" wx:for="{{[1,2,3,4,5]}}" wx:key="*this" src="{{cardBackImage}}" mode="aspectFill" style="animation-delay: {{index * 0.2}}s"></image>
|
||||
</view>
|
||||
<text class="ritual-hint">正在感应你的能量,请保持内心平静...</text>
|
||||
</view>
|
||||
|
||||
<!-- B. 牌面 -->
|
||||
<view class="card-face face-front">
|
||||
<image class="card-image {{currentCard.isReversed ? 'reversed' : ''}}" src="{{currentCard.image}}" mode="widthFix"></image>
|
||||
<!-- 3. 抽牌状态 (展开牌面供选择) -->
|
||||
<view class="drawing-area" wx:if="{{state === 'drawing'}}">
|
||||
<view class="title-area">
|
||||
<text class="app-title">请挑选 {{selectedSpread.cardCount}} 张牌</text>
|
||||
<text class="instruction">凭直觉选出触动你的卡牌</text>
|
||||
</view>
|
||||
<!-- 抽牌槽位(仅占位符) -->
|
||||
<view class="slots-container {{selectedSpread.id}}">
|
||||
<view class="slot-item" wx:for="{{selectedSpread.cardCount}}" wx:key="index">
|
||||
<view class="slot-placeholder">
|
||||
<text class="slot-num">{{index + 1}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 解读区域:翻牌完成 (revealed) 后显示 -->
|
||||
<view class="card-info" animation="{{infoAnimation}}" style="opacity: 0; transform: translateY(20px);">
|
||||
<!-- 1. 牌名与位置 -->
|
||||
<view class="info-header">
|
||||
<text class="card-name">{{currentCard.name}}</text>
|
||||
<text class="card-pos">{{currentCard.isReversed ? '· 逆位' : '· 正位'}}</text>
|
||||
<!-- 牌堆:卡牌会直接从这里飞到上方槽位 -->
|
||||
<view class="fan-deck">
|
||||
<view
|
||||
class="fan-card {{helper.isSelected(drawnCardIndices, index) ? 'is-selected' : ''}}"
|
||||
wx:for="{{20}}"
|
||||
wx:key="*this"
|
||||
style="transform: {{helper.getTransform(drawnCardIndices, index, selectedSpread.cardCount)}};"
|
||||
bindtap="onCardTap"
|
||||
data-index="{{index}}"
|
||||
>
|
||||
<image class="card-back-sm" src="{{cardBackImage}}" mode="aspectFill"></image>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 2. 关键词 (权重最高) -->
|
||||
<view class="keyword-area">
|
||||
<text class="keyword-tag">{{currentCard.keyword}}</text>
|
||||
<!-- 简单辅助逻辑 -->
|
||||
<wxs module="helper">
|
||||
module.exports.isSelected = function(indices, currentIdx) {
|
||||
return indices.indexOf(currentIdx) !== -1;
|
||||
};
|
||||
|
||||
module.exports.getTransform = function(indices, currentIdx, count) {
|
||||
var pos = indices.indexOf(currentIdx);
|
||||
if (pos !== -1) {
|
||||
var itemWidth = 140 + 20;
|
||||
var centerOffset = (pos - (count - 1) / 2) * itemWidth;
|
||||
// 位移到上方槽位 (向上位移以对准 slots-container)
|
||||
return 'translate(' + centerOffset + 'rpx, -450rpx) rotate(0deg)';
|
||||
} else {
|
||||
// 保持在牌堆中的放射状
|
||||
return 'rotate(' + ((currentIdx - 10) * 8) + 'deg)';
|
||||
}
|
||||
};
|
||||
</wxs>
|
||||
<view class="draw-count">{{drawnCardIndices.length}} / {{selectedSpread.cardCount}}</view>
|
||||
<button class="confirm-draw-btn" wx:if="{{drawnCardIndices.length === selectedSpread.cardCount}}" bindtap="confirmDraw">开启解读</button>
|
||||
</view>
|
||||
|
||||
<!-- 4. 翻牌与结果展示状态 -->
|
||||
<view class="result-area" wx:if="{{state === 'flipping' || state === 'revealed'}}">
|
||||
<!-- 卡牌阵列 -->
|
||||
<view class="spread-display {{selectedSpread.id}}">
|
||||
<view class="card-slot" wx:for="{{drawnCards}}" wx:key="index">
|
||||
<text class="pos-name">{{selectedSpread.positions[index]}}</text>
|
||||
<view class="flip-scene" bindtap="revealNext" data-index="{{index}}">
|
||||
<view class="flip-card {{index < revealedCount ? 'flipped' : ''}}">
|
||||
<view class="card-face face-back">
|
||||
<image class="card-image" src="{{cardBackImage}}" mode="widthFix"></image>
|
||||
</view>
|
||||
<view class="card-face face-front">
|
||||
<image class="card-image {{item.isReversed ? 'reversed' : ''}}" src="{{item.image}}" mode="widthFix"></image>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<text class="card-name-sm" wx:if="{{index < revealedCount}}">{{item.name}}{{item.isReversed ? '(逆)' : ''}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 3. 一句话核心解读 (判词) -->
|
||||
<view class="core-meaning">
|
||||
<text>{{currentCard.isReversed ? '警告:' : '启示:'}}{{currentCard.isReversed ? currentCard.reversedMeaning : currentCard.meaning}}</text>
|
||||
<!-- 解读区域 -->
|
||||
<view class="interpretation-area" wx:if="{{revealedCount === selectedSpread.cardCount}}" animation="{{infoAnimation}}">
|
||||
<view class="loading-ai" wx:if="{{isAiLoading}}">
|
||||
<text class="loading-dot">···</text>
|
||||
<text>正在深度解读能量流向...</text>
|
||||
</view>
|
||||
|
||||
<!-- 4. 详细解释文本 (权重最低) -->
|
||||
<view class="detail-explanation">
|
||||
<view class="loading-ai" wx:if="{{isAiLoading}}">
|
||||
<text class="loading-dot">···</text>
|
||||
<text>AI 正在感应心灵能量...</text>
|
||||
<view class="structured-result" wx:else>
|
||||
<view class="theme-box">
|
||||
<text class="label">核心主题</text>
|
||||
<text class="content">{{aiResult.theme}}</text>
|
||||
</view>
|
||||
<text class="explanation-text" wx:else>{{aiExplanation || explanation}}</text>
|
||||
</view>
|
||||
|
||||
<view class="info-grid">
|
||||
<view class="info-item">
|
||||
<text class="label">当前状态</text>
|
||||
<text class="content">{{aiResult.status}}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">潜在影响</text>
|
||||
<text class="content">{{aiResult.influence}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="pos-meanings">
|
||||
<view class="pos-meaning-item" wx:for="{{aiResult.positions}}" wx:key="posName">
|
||||
<text class="pos-title">{{item.posName}}</text>
|
||||
<text class="pos-text">{{item.posMeaning}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="advice-box">
|
||||
<text class="label">行动建议</text>
|
||||
<text class="content">{{aiResult.advice}}</text>
|
||||
</view>
|
||||
|
||||
<button class="reset-btn" bindtap="resetSpread">重新开启</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 3. 抽卡按钮:点击触发 drawCard -->
|
||||
<button class="draw-btn" bindtap="drawCard" type="primary">
|
||||
{{currentCard ? '重抽一张' : '✨ 抽一张塔罗牌'}}
|
||||
</button>
|
||||
|
||||
</view>
|
||||
</view>
|
||||
|
|
|
|||
|
|
@ -1,278 +1,440 @@
|
|||
/* 让整个页面背景变黑,制造神秘感 */
|
||||
page {
|
||||
background-color: #1a1a2e; /*以此颜色为底 */
|
||||
min-height: 100%; /* 使用 min-height 允许滚动 */
|
||||
box-sizing: border-box;
|
||||
background-color: #0f0f1b;
|
||||
color: #e0e0e0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
}
|
||||
|
||||
.container {
|
||||
min-height: 100vh;
|
||||
background-color: #0f0f1b;
|
||||
padding: 40rpx 30rpx 100rpx;
|
||||
box-sizing: border-box;
|
||||
color: #e0e0e0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
/* 去掉 justify-content: center,防止内容过多时顶部被裁切 */
|
||||
min-height: 100%;
|
||||
padding: 15px; /* 减小内边距 */
|
||||
padding-bottom: 40px; /* 底部预留空间,防止按钮太靠底 */
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* 标题区域 */
|
||||
.title-area {
|
||||
/* 自定义返回按钮样式 */
|
||||
.nav-back {
|
||||
position: absolute;
|
||||
top: 40rpx;
|
||||
left: 30rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-bottom: 20px; /* 减小间距 */
|
||||
margin-top: 10px;
|
||||
z-index: 100;
|
||||
padding: 10rpx 20rpx;
|
||||
border-radius: 30rpx;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.back-icon {
|
||||
font-size: 32rpx;
|
||||
margin-right: 8rpx;
|
||||
color: #c9a0dc;
|
||||
}
|
||||
|
||||
.back-text {
|
||||
font-size: 26rpx;
|
||||
color: #c9a0dc;
|
||||
letter-spacing: 2rpx;
|
||||
}
|
||||
|
||||
/* 标题通用样式 */
|
||||
.title-area {
|
||||
text-align: center;
|
||||
margin-top: 40rpx;
|
||||
margin-bottom: 60rpx;
|
||||
}
|
||||
|
||||
.app-title {
|
||||
font-size: 24px; /* 减小字号 */
|
||||
color: #e94560;
|
||||
font-weight: bold;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.app-subtitle {
|
||||
font-size: 44rpx;
|
||||
font-weight: 600;
|
||||
color: #c9a0dc;
|
||||
letter-spacing: 4rpx;
|
||||
display: block;
|
||||
font-size: 16px; /* 减小字号 */
|
||||
color: #fff;
|
||||
margin-bottom: 5px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* 引导区域 */
|
||||
.intro {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.deck-container {
|
||||
position: relative;
|
||||
width: 160px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.card-deck {
|
||||
width: 100%;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 10px 30px rgba(0,0,0,0.5);
|
||||
}
|
||||
|
||||
.tap-hint {
|
||||
position: absolute;
|
||||
bottom: -25px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
color: #e94560;
|
||||
font-size: 12px;
|
||||
opacity: 0.8;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.guide-text {
|
||||
margin-top: 20px;
|
||||
height: 40px;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.instruction {
|
||||
display: block;
|
||||
color: #fff;
|
||||
font-size: 16px;
|
||||
letter-spacing: 1px;
|
||||
font-weight: 300;
|
||||
font-size: 26rpx;
|
||||
color: #8a8a9d;
|
||||
letter-spacing: 1rpx;
|
||||
}
|
||||
|
||||
/* 卡牌展示出的框框 */
|
||||
.card-display {
|
||||
/* 1. 牌阵选择列表 */
|
||||
.spread-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
background-color: #16213e;
|
||||
padding: 15px; /* 减小内边距 */
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 0 15px rgba(233, 69, 96, 0.3);
|
||||
margin-bottom: 20px; /* 减小间距 */
|
||||
gap: 30rpx;
|
||||
}
|
||||
|
||||
/* --- 翻牌动画核心样式 --- */
|
||||
|
||||
/* 1. 场景容器:提供 3D 景深 */
|
||||
.flip-scene {
|
||||
width: 200px;
|
||||
height: 333px;
|
||||
perspective: 1000px;
|
||||
margin-bottom: 20px;
|
||||
position: relative;
|
||||
.spread-item {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border: 1px solid rgba(201, 160, 220, 0.2);
|
||||
padding: 40rpx;
|
||||
border-radius: 20rpx;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
/* 2. 翻转实体:包含正反两面,执行旋转 */
|
||||
.flip-card {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
transition: transform 0.5s ease-in-out;
|
||||
transform-style: preserve-3d;
|
||||
.spread-hover {
|
||||
background: rgba(201, 160, 220, 0.1);
|
||||
transform: translateY(-4rpx);
|
||||
border-color: rgba(201, 160, 220, 0.5);
|
||||
}
|
||||
|
||||
/* 翻转状态 */
|
||||
.flip-card.flipped {
|
||||
transform: rotateY(180deg);
|
||||
.spread-name {
|
||||
font-size: 34rpx;
|
||||
font-weight: 500;
|
||||
color: #c9a0dc;
|
||||
display: block;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
/* 3. 正反面公共样式 */
|
||||
.card-face {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
backface-visibility: hidden;
|
||||
.spread-desc {
|
||||
font-size: 24rpx;
|
||||
color: #6a6a7d;
|
||||
}
|
||||
|
||||
/* 2. 洗牌仪式感 */
|
||||
.ritual-area {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 背面 */
|
||||
.face-back {
|
||||
background-color: #16213e;
|
||||
transform: rotateY(0deg);
|
||||
z-index: 2;
|
||||
.shuffling-container {
|
||||
position: relative;
|
||||
width: 200rpx;
|
||||
height: 300rpx;
|
||||
margin-bottom: 80rpx;
|
||||
}
|
||||
|
||||
/* 正面 */
|
||||
.face-front {
|
||||
background-color: #16213e;
|
||||
transform: rotateY(180deg);
|
||||
}
|
||||
|
||||
/* 图片填满容器 */
|
||||
.card-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* 提示点击的文字 */
|
||||
.hint-text {
|
||||
.shuffling-card {
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
background-color: rgba(0,0,0,0.5);
|
||||
padding: 5px 10px;
|
||||
border-radius: 15px;
|
||||
z-index: 3;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 12rpx;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.5);
|
||||
animation: shuffle 1s infinite alternate ease-in-out;
|
||||
}
|
||||
|
||||
/* --- 文字信息层级样式 --- */
|
||||
.card-info {
|
||||
@keyframes shuffle {
|
||||
from { transform: translateX(-40rpx) rotate(-5deg); }
|
||||
to { transform: translateX(40rpx) rotate(5deg); }
|
||||
}
|
||||
|
||||
.ritual-hint {
|
||||
font-size: 28rpx;
|
||||
color: #c9a0dc;
|
||||
opacity: 0.8;
|
||||
animation: breathe 2s infinite ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes breathe {
|
||||
0%, 100% { opacity: 0.4; }
|
||||
50% { opacity: 1; }
|
||||
}
|
||||
|
||||
/* 3. 抽牌区域 (槽位 + 扇形) */
|
||||
.drawing-area {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 0 10px;
|
||||
padding-top: 40rpx;
|
||||
}
|
||||
|
||||
.info-header {
|
||||
margin-bottom: 15px;
|
||||
.slots-container {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.card-name {
|
||||
color: #fff;
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.card-pos {
|
||||
color: #e94560;
|
||||
font-size: 14px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.keyword-area {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.keyword-tag {
|
||||
background: rgba(233, 69, 96, 0.15);
|
||||
color: #e94560;
|
||||
padding: 6px 16px;
|
||||
border-radius: 20px;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
border: 1px solid rgba(233, 69, 96, 0.3);
|
||||
}
|
||||
|
||||
.core-meaning {
|
||||
color: #fff;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
margin-bottom: 15px;
|
||||
line-height: 1.6;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.detail-explanation {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 20px;
|
||||
justify-content: center;
|
||||
gap: 20rpx;
|
||||
height: 240rpx;
|
||||
margin-bottom: 60rpx;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.explanation-text {
|
||||
color: #aaa;
|
||||
font-size: 13px;
|
||||
text-align: justify;
|
||||
line-height: 1.5;
|
||||
.slot-item {
|
||||
width: 140rpx;
|
||||
height: 220rpx;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
|
||||
/* 按钮美化 */
|
||||
.draw-btn {
|
||||
margin-top: 40px;
|
||||
width: 80% !important;
|
||||
background-color: #e94560 !important;
|
||||
border-radius: 25px !important;
|
||||
font-weight: bold !important;
|
||||
box-shadow: 0 4px 15px rgba(233, 69, 96, 0.3);
|
||||
font-size: 16px !important;
|
||||
padding: 8px 0 !important;
|
||||
.slot-placeholder {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 2rpx dashed rgba(201, 160, 220, 0.3);
|
||||
border-radius: 12rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* 逆位时的图片样式 */
|
||||
.card-image.reversed {
|
||||
transform: rotate(180deg);
|
||||
transition: transform 0.5s;
|
||||
.slot-num {
|
||||
font-size: 40rpx;
|
||||
color: rgba(201, 160, 220, 0.2);
|
||||
font-family: serif;
|
||||
}
|
||||
|
||||
/* AI 加载状态 */
|
||||
.slot-card-wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
animation: flyInSlot 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) both;
|
||||
}
|
||||
|
||||
.slot-card-back {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 12rpx;
|
||||
box-shadow: 0 8rpx 20rpx rgba(0,0,0,0.6);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
@keyframes flyInSlot {
|
||||
from {
|
||||
transform: translateY(400rpx) scale(0.8) rotate(10deg);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform: translateY(0) scale(1) rotate(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.fan-deck {
|
||||
position: relative;
|
||||
height: 380rpx;
|
||||
width: 100%;
|
||||
margin-top: 20rpx;
|
||||
perspective: 1000px;
|
||||
overflow: visible; /* 允许卡牌飞出容器 */
|
||||
}
|
||||
|
||||
.fan-card {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
bottom: 0;
|
||||
width: 150rpx;
|
||||
height: 260rpx;
|
||||
margin-left: -75rpx;
|
||||
transform-origin: bottom center;
|
||||
transition: transform 0.6s cubic-bezier(0.19, 1, 0.22, 1);
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.card-back-sm {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 10rpx;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.5);
|
||||
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.fan-card.is-selected {
|
||||
z-index: 100;
|
||||
pointer-events: auto; /* 允许点击已抽出的牌返回 */
|
||||
}
|
||||
|
||||
.draw-count {
|
||||
text-align: center;
|
||||
font-size: 36rpx;
|
||||
color: #c9a0dc;
|
||||
margin-top: 60rpx;
|
||||
}
|
||||
|
||||
.confirm-draw-btn {
|
||||
margin: 40rpx auto;
|
||||
background: #c9a0dc;
|
||||
color: #1a1a2e;
|
||||
border-radius: 40rpx;
|
||||
font-weight: 600;
|
||||
width: 300rpx;
|
||||
}
|
||||
|
||||
/* 4. 翻牌结果展示 */
|
||||
.spread-display {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 30rpx;
|
||||
padding: 40rpx 0;
|
||||
}
|
||||
|
||||
/* 牌阵特定布局 */
|
||||
.spread-display.three_time_flow,
|
||||
.spread-display.three_problem_solution,
|
||||
.spread-display.two_choice_decision {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.card-slot {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 200rpx;
|
||||
}
|
||||
|
||||
.pos-name {
|
||||
font-size: 22rpx;
|
||||
color: #8a8a9d;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.flip-scene {
|
||||
width: 180rpx;
|
||||
height: 300rpx;
|
||||
perspective: 1000rpx;
|
||||
}
|
||||
|
||||
.flip-card {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
transition: transform 0.6s;
|
||||
transform-style: preserve-3d;
|
||||
}
|
||||
|
||||
.flip-card.flipped {
|
||||
transform: rotateY(180deg);
|
||||
}
|
||||
|
||||
.card-face {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
backface-visibility: hidden;
|
||||
border-radius: 12rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.face-back { background: #2a2a3e; }
|
||||
.face-front { background: #1a1a2e; transform: rotateY(180deg); }
|
||||
|
||||
.card-image { width: 100%; height: 100%; }
|
||||
.card-image.reversed { transform: rotate(180deg); }
|
||||
|
||||
.card-name-sm {
|
||||
margin-top: 16rpx;
|
||||
font-size: 24rpx;
|
||||
color: #c9a0dc;
|
||||
}
|
||||
|
||||
/* 5. 深度解读区域 */
|
||||
.interpretation-area {
|
||||
margin-top: 60rpx;
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border-radius: 24rpx;
|
||||
padding: 40rpx;
|
||||
}
|
||||
|
||||
.theme-box {
|
||||
text-align: center;
|
||||
margin-bottom: 60rpx;
|
||||
background: rgba(201, 160, 220, 0.08);
|
||||
padding: 30rpx;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
|
||||
.label {
|
||||
display: block;
|
||||
font-size: 20rpx;
|
||||
color: #c9a0dc;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 4rpx;
|
||||
margin-bottom: 16rpx;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.theme-box .content {
|
||||
font-size: 34rpx;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.info-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 30rpx;
|
||||
margin-bottom: 60rpx;
|
||||
}
|
||||
|
||||
.info-item .content {
|
||||
font-size: 26rpx;
|
||||
line-height: 1.6;
|
||||
color: #b0b0cf;
|
||||
}
|
||||
|
||||
.pos-meanings {
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.05);
|
||||
padding-top: 40rpx;
|
||||
margin-bottom: 60rpx;
|
||||
}
|
||||
|
||||
.pos-meaning-item {
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.pos-title {
|
||||
font-size: 24rpx;
|
||||
color: #c9a0dc;
|
||||
display: block;
|
||||
margin-bottom: 12rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.pos-text {
|
||||
font-size: 26rpx;
|
||||
color: #b0b0cf;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.advice-box {
|
||||
background: linear-gradient(135deg, rgba(201, 160, 220, 0.1), rgba(201, 160, 220, 0.03));
|
||||
padding: 40rpx;
|
||||
border-radius: 20rpx;
|
||||
border-left: 4rpx solid #c9a0dc;
|
||||
}
|
||||
|
||||
.advice-box .content {
|
||||
font-size: 28rpx;
|
||||
color: #ffffff;
|
||||
font-weight: 500;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.reset-btn {
|
||||
margin-top: 80rpx;
|
||||
background: transparent;
|
||||
color: #c9a0dc;
|
||||
border: 1px solid rgba(201, 160, 220, 0.4);
|
||||
font-size: 24rpx;
|
||||
border-radius: 40rpx;
|
||||
width: 240rpx;
|
||||
}
|
||||
|
||||
/* AI Loading */
|
||||
.loading-ai {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
color: #666;
|
||||
font-size: 12px;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
@keyframes flash {
|
||||
0%, 100% { opacity: 0.3; }
|
||||
50% { opacity: 1; }
|
||||
padding: 80rpx 0;
|
||||
color: #c9a0dc;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
.loading-dot {
|
||||
font-size: 24px;
|
||||
color: #e94560;
|
||||
margin-bottom: 5px;
|
||||
font-size: 60rpx;
|
||||
margin-bottom: 30rpx;
|
||||
animation: flash 1.5s infinite;
|
||||
}
|
||||
|
||||
@keyframes flash {
|
||||
0%, 100% { opacity: 0.3; transform: scale(0.9); }
|
||||
50% { opacity: 1; transform: scale(1.1); }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
},
|
||||
"compileType": "miniprogram",
|
||||
"libVersion": "3.14.0",
|
||||
"appid": "wx16a275637dbd4603",
|
||||
"appid": "wx3ab13b1be648f0da",
|
||||
"projectname": "my-first-mini-program",
|
||||
"simulatorPluginLibVersion": {},
|
||||
"packOptions": {
|
||||
|
|
|
|||
|
|
@ -16,6 +16,6 @@
|
|||
"compileHotReLoad": true,
|
||||
"checkInvalidKey": true,
|
||||
"ignoreDevUnusedFiles": true,
|
||||
"bigPackageSizeSupport": false
|
||||
"bigPackageSizeSupport": true
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue