V1.6: Zodiac Share Logic, Back Button Fix, Points UI Polish
This commit is contained in:
parent
9292e8dc0f
commit
2b532c0116
54
app.js
54
app.js
|
|
@ -1,5 +1,53 @@
|
|||
App({
|
||||
onLaunch() {
|
||||
console.log('小程序启动啦!')
|
||||
|
||||
onShow: function (options) {
|
||||
|
||||
// 检查是否从分享卡片进入 (场景值 1007, 1008)
|
||||
if (options && (options.scene === 1007 || options.scene === 1008)) {
|
||||
|
||||
console.log('[App] 用户通过分享卡片进入');
|
||||
|
||||
// 获取来源用户ID(保留结构,暂不使用)
|
||||
const fromUserId = options.query && options.query.fromUserId;
|
||||
if (fromUserId) {
|
||||
console.log('[App] 来源用户ID:', fromUserId);
|
||||
}
|
||||
|
||||
// 只做欢迎提示,不发积分
|
||||
wx.showToast({
|
||||
title: '欢迎进入今日命运场 ✨',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
onLaunch: function () {
|
||||
|
||||
console.log('小程序启动啦!');
|
||||
|
||||
// 检查隐私协议同意状态
|
||||
const agreed = wx.getStorageSync('privacyAgreed');
|
||||
|
||||
if (!agreed) {
|
||||
|
||||
wx.onAppRoute((res) => {
|
||||
|
||||
const isAgreed = wx.getStorageSync('privacyAgreed');
|
||||
if (isAgreed) return;
|
||||
|
||||
if (res.path !== 'pages/privacy-agree/privacy-agree' &&
|
||||
res.path !== 'pages/privacy/privacy') {
|
||||
|
||||
wx.reLaunch({
|
||||
url: '/pages/privacy-agree/privacy-agree'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
wx.reLaunch({
|
||||
url: '/pages/privacy-agree/privacy-agree'
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
11
app.json
11
app.json
|
|
@ -1,15 +1,22 @@
|
|||
{
|
||||
"pages": [
|
||||
"pages/home/home",
|
||||
"pages/zodiac/index",
|
||||
"pages/index/index",
|
||||
"pages/knowledge/index",
|
||||
"pages/knowledge/list",
|
||||
"pages/knowledge/article",
|
||||
"pages/webview/index",
|
||||
"pages/privacy/privacy"
|
||||
"pages/privacy/privacy",
|
||||
"pages/privacy-agree/privacy-agree",
|
||||
"pages/settings/settings",
|
||||
"pages/user-agreement/user-agreement"
|
||||
],
|
||||
"window": {
|
||||
"navigationBarTitleText": "我的第一个小程序"
|
||||
"navigationBarTitleText": "塔罗星语",
|
||||
"navigationBarBackgroundColor": "#1a1a2e",
|
||||
"navigationBarTextStyle": "white",
|
||||
"backgroundColor": "#1a1a2e"
|
||||
},
|
||||
"style": "v2",
|
||||
"sitemapLocation": "sitemap.json"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,105 @@
|
|||
Component({
|
||||
properties: {
|
||||
spread: { type: Object, value: null },
|
||||
cards: { type: Array, value: [] },
|
||||
revealedCount: { type: Number, value: 0 },
|
||||
cardWidth: { type: Number, value: 160 } // 安全默认值 160
|
||||
},
|
||||
|
||||
data: {
|
||||
displayList: [] // 统一渲染列表 (merged with layout info)
|
||||
},
|
||||
|
||||
lifetimes: {
|
||||
attached() {
|
||||
this.updateLayout();
|
||||
}
|
||||
},
|
||||
|
||||
observers: {
|
||||
'spread, cards, cardWidth': function (spread, cards, cardWidth) {
|
||||
this.updateLayout();
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
updateLayout() {
|
||||
const { spread, cards, cardWidth } = this.data;
|
||||
if (!spread || !spread.layout || !cards || cards.length === 0) return;
|
||||
|
||||
const layoutType = spread.layout.type;
|
||||
|
||||
// 1. Grid 处理 (简单网格布局,不涉及复杂计算)
|
||||
if (layoutType === 'grid') {
|
||||
const list = cards.map((c, i) => ({
|
||||
...c,
|
||||
_posName: spread.positions[i],
|
||||
_seq: i + 1,
|
||||
_index: i,
|
||||
_style: '' // 无内联样式,交给 grid CSS
|
||||
}));
|
||||
this.setData({ displayList: list });
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. Celtic Cross (静止百分比布局 - 600x900rpx 容器适配)
|
||||
if (layoutType === 'celtic') {
|
||||
// 容器: 600rpx x 900rpx
|
||||
// 卡牌: 140rpx x 210rpx
|
||||
// 安全水平间距: 140/600 = 23.3% -> 中心间距需 > 23.3%
|
||||
// 安全垂直间距: 210/900 = 23.3% -> 中心间距需 > 23.3%
|
||||
|
||||
// 中轴 X: 38% (228rpx) - 左移给右侧列留空间
|
||||
// 左牌 X: 38% - 25% = 13% (78rpx) - 安全
|
||||
// 右牌 X: 38% + 25% = 63% (378rpx) - 安全
|
||||
// 右侧列 X: 85% (510rpx) - 离右牌 22% = 132rpx > 140rpx 略挤,调整到 87%
|
||||
|
||||
const positions = [
|
||||
{ left: 38, top: 47 }, // 1 现状
|
||||
{ left: 38, top: 47, rotate: 'rotate(90deg)' }, // 2 阻碍 (重叠)
|
||||
{ left: 38, top: 73 }, // 3 根基 (47+26=73)
|
||||
{ left: 13, top: 47 }, // 4 过去
|
||||
{ left: 38, top: 21 }, // 5 可能性 (47-26=21)
|
||||
{ left: 63, top: 47 }, // 6 近期未来
|
||||
|
||||
// 右侧列 X: 87%, 纵向间距 ~22% of 900 = 198rpx > 210rpx 略挤
|
||||
// 调整为 4 张牌均匀分布在 10%-90% 之间
|
||||
// 间距: (90-10)/3 = 26.7% = 240rpx > 210rpx ✓
|
||||
{ left: 87, top: 15 }, // 7 你的态度
|
||||
{ left: 87, top: 38 }, // 8 外部影响 (15+23=38)
|
||||
{ left: 87, top: 61 }, // 9 希望与恐惧 (38+23=61)
|
||||
{ left: 87, top: 84 }, // 10 最终结果 (61+23=84)
|
||||
];
|
||||
|
||||
// Merge Layout + Card Data
|
||||
const list = cards.map((c, i) => {
|
||||
const pos = positions[i] || {};
|
||||
// 直接生成内联样式,移除所有动态计算
|
||||
const style = `left: ${pos.left}%; top: ${pos.top}%; transform: translate(-50%, -50%) ${pos.rotate || ''};`;
|
||||
|
||||
return {
|
||||
...c,
|
||||
_posName: spread.positions[i],
|
||||
_seq: i + 1,
|
||||
_index: i,
|
||||
_style: style
|
||||
};
|
||||
});
|
||||
|
||||
this.setData({ displayList: list });
|
||||
}
|
||||
},
|
||||
|
||||
// 移除多余的 calcCelticLayout 方法,直接在 updateLayout 中处理
|
||||
handleCardTap(e) {
|
||||
const idx = e.currentTarget.dataset.index;
|
||||
if (idx === undefined) return;
|
||||
|
||||
if (idx === this.data.revealedCount) {
|
||||
this.triggerEvent('reveal', { index: idx });
|
||||
} else if (idx < this.data.revealedCount) {
|
||||
this.triggerEvent('viewDetail', { index: idx });
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"component": true,
|
||||
"usingComponents": {}
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
<!-- components/spread-container/spread-container.wxml -->
|
||||
|
||||
<!-- 1. 卡牌单元模板 -->
|
||||
<template name="card-item">
|
||||
<view class="card-slot" style="{{extraStyle}}" bindtap="handleCardTap" data-index="{{card._index}}">
|
||||
|
||||
<!-- 位置信息 -->
|
||||
<view class="pos-info">
|
||||
<text class="pos-name">{{card._posName}}</text>
|
||||
<!-- <text class="pos-seq">NO.{{card._seq}}</text> -->
|
||||
</view>
|
||||
|
||||
<!-- 翻牌场景 -->
|
||||
<view class="flip-scene">
|
||||
<view class="flip-card {{card._index < revealedCount ? 'flipped' : ''}}">
|
||||
<!-- 背面 -->
|
||||
<view class="card-face face-back">
|
||||
<image class="card-image" src="/images/beimian.png" mode="aspectFit" />
|
||||
</view>
|
||||
<!-- 正面 -->
|
||||
<view class="card-face face-front">
|
||||
<image
|
||||
class="card-image {{card.isReversed ? 'reversed' : ''}}"
|
||||
src="{{card.image}}"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 牌名称 (翻开后显示) -->
|
||||
<text class="card-name-sm" wx:if="{{card._index < revealedCount}}">{{card.name}}</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
|
||||
<view class="spread-wrapper {{spread.layout.type === 'celtic' ? 'celtic-mode' : ''}}">
|
||||
|
||||
<!-- =========================================
|
||||
布局 1: Grid (网格)
|
||||
直接遍历 displayList,无特殊定位
|
||||
========================================= -->
|
||||
<view class="layout-grid" wx:if="{{spread.layout.type === 'grid'}}">
|
||||
<block wx:for="{{displayList}}" wx:key="_index">
|
||||
<template is="card-item" data="{{card: item, revealedCount: revealedCount, extraStyle: ''}}" />
|
||||
</block>
|
||||
</view>
|
||||
|
||||
|
||||
<!-- =========================================
|
||||
布局 2: Celtic Cross (舞台模式 - 600x900rpx)
|
||||
使用 displayList 中的 _style 进行绝对定位
|
||||
========================================= -->
|
||||
<view class="spread-container" wx:if="{{spread.layout.type === 'celtic'}}">
|
||||
<block wx:for="{{displayList}}" wx:key="_index">
|
||||
<view class="stage-card" style="{{item._style}}" bindtap="handleCardTap" data-index="{{item._index}}">
|
||||
<!-- 复用 card-item 内部结构,但不使用 template 以避免 style 冲突 -->
|
||||
<view class="card-slot">
|
||||
<view class="pos-info">
|
||||
<text class="pos-name">{{item._posName}}</text>
|
||||
</view>
|
||||
<view class="flip-scene">
|
||||
<view class="flip-card {{item._index < revealedCount ? 'flipped' : ''}}">
|
||||
<view class="card-face face-back">
|
||||
<image class="card-image" src="/images/beimian.png" mode="aspectFit" />
|
||||
</view>
|
||||
<view class="card-face face-front">
|
||||
<image
|
||||
class="card-image {{item.isReversed ? 'reversed' : ''}}"
|
||||
src="{{item.image}}"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<text class="card-name-sm" wx:if="{{item._index < revealedCount}}">{{item.name}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
|
@ -0,0 +1,165 @@
|
|||
/* components/spread-container/spread-container.wxss */
|
||||
|
||||
/* 通用容器 */
|
||||
.spread-wrapper {
|
||||
width: 100%;
|
||||
padding: 0 40rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* =========================================
|
||||
卡牌单元 (Card Item)
|
||||
========================================= */
|
||||
.card-slot {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
/* 默认尺寸,grid 布局使用 */
|
||||
width: 124rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
/* 翻牌场景 */
|
||||
.flip-scene {
|
||||
width: 100%;
|
||||
/* 保持 160:260 比例 -> 1:1.625 */
|
||||
/* 或 124:200 -> 1:1.61 */
|
||||
aspect-ratio: 2/3;
|
||||
perspective: 1000rpx;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.flip-card {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
transform-style: preserve-3d;
|
||||
transition: transform 0.6s;
|
||||
}
|
||||
|
||||
.flip-card.flipped {
|
||||
transform: rotateY(180deg);
|
||||
}
|
||||
|
||||
.card-face {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
backface-visibility: hidden;
|
||||
border-radius: 12rpx;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.face-front {
|
||||
transform: rotateY(180deg);
|
||||
}
|
||||
|
||||
.card-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.card-image.reversed {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
/* 文字信息 */
|
||||
.pos-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.pos-name {
|
||||
font-size: 22rpx;
|
||||
color: #ddd;
|
||||
line-height: 1.2;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.pos-seq {
|
||||
font-size: 18rpx;
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
.card-name-sm {
|
||||
font-size: 20rpx;
|
||||
color: #fff;
|
||||
margin-top: 8rpx;
|
||||
text-align: center;
|
||||
max-width: 120%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
/* =========================================
|
||||
布局 1: Grid (网格)
|
||||
========================================= */
|
||||
.layout-grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 30rpx;
|
||||
padding: 40rpx 0;
|
||||
}
|
||||
|
||||
/* =========================================
|
||||
布局系统 2: Celtic Cross (从零重构 - 绝对锁定)
|
||||
========================================= */
|
||||
|
||||
/* 1. 全屏垂直水平居中包装器 (仅 celtic 模式) */
|
||||
.spread-wrapper.celtic-mode {
|
||||
width: 100%;
|
||||
/* 高度设为 100vh 可能会导致不可滚动,这里视情况调整 */
|
||||
/* 用户虽然要求 100vh,但在小程序组件中可能会溢出,建议 min-height */
|
||||
min-height: 80vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* 2. 核心容器 (600x900rpx) */
|
||||
.spread-container {
|
||||
position: relative;
|
||||
width: 600rpx;
|
||||
height: 900rpx;
|
||||
/* 调试辅助: border: 1px dashed rgba(255,255,255,0.2); */
|
||||
}
|
||||
|
||||
/* 3. 卡牌通用样式 (绝对定位 + 居中) */
|
||||
.stage-card {
|
||||
position: absolute;
|
||||
transform: translate(-50%, -50%); /* 核心锚点居中 */
|
||||
z-index: 10;
|
||||
width: 140rpx; /* 适配 600rpx 容器的推荐尺寸 */
|
||||
display: flex;
|
||||
flex-direction: column; /* 确保文字在下方 */
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
/* 复写卡牌槽样式以匹配新容器 */
|
||||
.spread-container .card-slot {
|
||||
width: 100%; /* 跟随 stage-card */
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.spread-container .flip-scene {
|
||||
width: 100%;
|
||||
height: 210rpx; /* 强制高度 (140 * 1.5) */
|
||||
background: rgba(255, 255, 255, 0.1); /* 调试背景,确保占位可见 */
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.spread-container .card-face {
|
||||
background: #1a1a2e; /* 默认深色背景,防图挂 */
|
||||
border: 1px solid rgba(255,255,255,0.2); /* 边框 */
|
||||
}
|
||||
|
|
@ -2,15 +2,28 @@ const { getPoints, checkDailyReward, canWatchAd, getTodayAdCount, rewardFromAd,
|
|||
const { getDailyAdvice } = require('../../utils/dailyAdvice');
|
||||
const { getDailyArticle, getCategories } = require('../../utils/knowledgeData');
|
||||
|
||||
|
||||
/**
|
||||
* 🔧 开发模式配置
|
||||
* 🔧 广告功能开关 (上线前配置)
|
||||
*
|
||||
* DEV_MODE = true: 使用模拟广告(开发测试)
|
||||
* DEV_MODE = false: 使用真实广告(正式上线)
|
||||
* 1. ENABLE_AD (总开关):
|
||||
* - true: 开启广告功能
|
||||
* - false: 关闭广告功能 (隐藏按钮, 点击提示"即将上线")
|
||||
*
|
||||
* ⚠️ 上线前务必改为 false!
|
||||
* 2. DEV_MODE (开发模式):
|
||||
* - true: 使用模拟广告 (不拉起微信广告, 直接发奖励)
|
||||
* - false: 使用真实广告 (拉起微信广告组件)
|
||||
*
|
||||
* ⚠️ 上线前:
|
||||
* - 确保已开通流量主并填入真实 ID
|
||||
* - 将 DEV_MODE 改为 false
|
||||
* - 将 ENABLE_AD 改为 true
|
||||
*/
|
||||
const DEV_MODE = true; // 👈 开发时设为 true,上线前改为 false
|
||||
const ENABLE_AD = false; // 👈 暂时关闭广告功能
|
||||
const DEV_MODE = true; // 👈 开发测试模式
|
||||
|
||||
// 积分系统开关 (即便 ENABLE_AD 为 true, 如果此开关为 false, UI 也不显示积分)
|
||||
const ENABLE_POINTS_UI = true;
|
||||
|
||||
Page({
|
||||
data: {
|
||||
|
|
@ -24,7 +37,7 @@ Page({
|
|||
categoryName: '',
|
||||
type: 'local'
|
||||
},
|
||||
energyVisible: false,
|
||||
showAdButton: ENABLE_AD, // 控制广告按钮显示
|
||||
knowledgeVisible: false
|
||||
},
|
||||
|
||||
|
|
@ -36,14 +49,29 @@ Page({
|
|||
this.initRewardedAd();
|
||||
},
|
||||
|
||||
// ... (keeping other methods)
|
||||
|
||||
// 跳转到星座模块
|
||||
goToZodiac: function () {
|
||||
wx.navigateTo({
|
||||
url: '/pages/zodiac/index'
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 初始化激励视频广告
|
||||
* ⚠️ 需要在微信公众平台开通流量主并获取广告位ID
|
||||
*/
|
||||
initRewardedAd: function () {
|
||||
// 🔧 开发模式:跳过真实广告初始化
|
||||
// 1. 如果广告功能关闭,直接返回
|
||||
if (!ENABLE_AD) {
|
||||
console.log('[广告系统] 🔧 广告功能已关闭 (ENABLE_AD = false)');
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 如果是开发模式,使用模拟广告,不需要初始化真实组件
|
||||
if (DEV_MODE) {
|
||||
console.log('[广告系统] 🔧 开发模式:使用模拟广告');
|
||||
console.log('[广告系统] 🔧 开发模式:使用模拟广告 (DEV_MODE = true)');
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -56,6 +84,7 @@ Page({
|
|||
try {
|
||||
// 创建激励视频广告实例
|
||||
// ⚠️ TODO: 替换为你的真实广告位ID
|
||||
// 只有在非开发模式且开启广告时才创建
|
||||
this.rewardedAd = wx.createRewardedVideoAd({
|
||||
adUnitId: 'adunit-xxxxxxxxxxxxxxxx' // 请替换为你的广告位ID
|
||||
});
|
||||
|
|
@ -122,22 +151,24 @@ Page({
|
|||
},
|
||||
|
||||
onShow: function () {
|
||||
// 检查每日登录奖励
|
||||
const rewardResult = checkDailyReward();
|
||||
if (ENABLE_POINTS_UI) {
|
||||
// 检查每日登录奖励
|
||||
const rewardResult = checkDailyReward();
|
||||
|
||||
// 如果获得了奖励,显示提示
|
||||
if (rewardResult.rewarded) {
|
||||
wx.showToast({
|
||||
title: rewardResult.message,
|
||||
icon: 'success',
|
||||
duration: 2000
|
||||
// 如果获得了奖励,显示提示
|
||||
if (rewardResult.rewarded) {
|
||||
wx.showToast({
|
||||
title: rewardResult.message,
|
||||
icon: 'success',
|
||||
duration: 2000
|
||||
});
|
||||
}
|
||||
|
||||
// 刷新积分显示
|
||||
this.setData({
|
||||
currentPoints: getPoints()
|
||||
});
|
||||
}
|
||||
|
||||
// 刷新积分显示
|
||||
this.setData({
|
||||
currentPoints: getPoints()
|
||||
});
|
||||
},
|
||||
|
||||
// 加载每日内容
|
||||
|
|
@ -213,6 +244,13 @@ Page({
|
|||
});
|
||||
},
|
||||
|
||||
// 跳转到星座模块
|
||||
goToZodiac: function () {
|
||||
wx.navigateTo({
|
||||
url: '/pages/zodiac/index'
|
||||
});
|
||||
},
|
||||
|
||||
// 跳转到文章详情
|
||||
goToArticle: function () {
|
||||
const article = this.data.dailyArticle;
|
||||
|
|
@ -238,61 +276,40 @@ Page({
|
|||
}, 300);
|
||||
},
|
||||
|
||||
// 跳转到隐私政策
|
||||
goToPrivacy: function () {
|
||||
// 跳转到设置页
|
||||
goToSettings: function () {
|
||||
wx.navigateTo({
|
||||
url: '/pages/privacy/privacy'
|
||||
url: '/pages/settings/settings'
|
||||
});
|
||||
},
|
||||
|
||||
// 显示积分说明
|
||||
// 显示积分说明 (切换为自定义浮层)
|
||||
showPointsInfo: function () {
|
||||
try {
|
||||
console.log('[首页] 点击积分徽章');
|
||||
const remaining = AD_REWARD_CONFIG.DAILY_LIMIT - getTodayAdCount();
|
||||
const canWatch = canWatchAd();
|
||||
this.setData({
|
||||
pointsVisible: true
|
||||
});
|
||||
},
|
||||
|
||||
console.log('[首页] 剩余广告次数:', remaining);
|
||||
console.log('[首页] 是否可以观看:', canWatch);
|
||||
console.log('[首页] 当前积分:', this.data.currentPoints);
|
||||
|
||||
const lines = [
|
||||
'当前积分:' + this.data.currentPoints,
|
||||
'',
|
||||
'每次占卜会消耗积分,不同牌阵消耗不同。',
|
||||
'每日首次登录可获得 +3 积分奖励。',
|
||||
'',
|
||||
'看广告可获得积分(今日剩余 ' + remaining + ' 次)'
|
||||
];
|
||||
|
||||
const content = lines.join('\n');
|
||||
console.log('[首页] 弹窗内容:', content);
|
||||
|
||||
console.log('[首页] 准备调用 wx.showModal');
|
||||
wx.showModal({
|
||||
title: '积分说明',
|
||||
content: content,
|
||||
confirmText: canWatch ? '看广告' : '知道了',
|
||||
cancelText: '关闭',
|
||||
success: (res) => {
|
||||
console.log('[首页] 弹窗回调:', res);
|
||||
if (res.confirm && canWatch) {
|
||||
this.watchAdForPoints();
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('[首页] 弹窗失败:', err);
|
||||
}
|
||||
});
|
||||
console.log('[首页] wx.showModal 已调用');
|
||||
} catch (error) {
|
||||
console.error('[首页] showPointsInfo 错误:', error);
|
||||
}
|
||||
// 关闭积分说明
|
||||
hidePointsInfo: function () {
|
||||
this.setData({
|
||||
pointsVisible: false
|
||||
});
|
||||
},
|
||||
|
||||
// 观看广告获取积分(支持开发模式)
|
||||
watchAdForPoints: function () {
|
||||
// 检查是否还有观看次数
|
||||
// 1. 全局开关检查
|
||||
if (!ENABLE_AD) {
|
||||
wx.showToast({
|
||||
title: '功能即将上线',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 检查是否还有观看次数
|
||||
if (!canWatchAd()) {
|
||||
wx.showToast({
|
||||
title: '今日广告次数已用完',
|
||||
|
|
|
|||
|
|
@ -1,16 +1,37 @@
|
|||
<view class="tarot-table">
|
||||
|
||||
<!-- 顶部栏 (已移除) -->
|
||||
|
||||
<!-- 右上角积分徽章 -->
|
||||
<view class="points-badge" bindtap="showPointsInfo">
|
||||
<view class="points-badge" bindtap="showPointsInfo" style="top: 100rpx;">
|
||||
<text class="badge-icon">🎯</text>
|
||||
<text class="badge-value">{{currentPoints}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 星座能量入口 (放在主牌堆上方) -->
|
||||
<view class="zodiac-entry" bindtap="goToZodiac">
|
||||
<view class="zodiac-content">
|
||||
<text class="zodiac-title">今日星象能量</text>
|
||||
<text class="zodiac-subtitle">看看今天宇宙给你的提示</text>
|
||||
</view>
|
||||
<view class="zodiac-icon">🌑</view>
|
||||
</view>
|
||||
|
||||
<!-- 中央主牌堆 -->
|
||||
<view class="main-deck" bindtap="goToTarot" hover-class="deck-hover">
|
||||
<view class="deck-cards">
|
||||
<view class="card-stack card-1"></view>
|
||||
<view class="card-stack card-2"></view>
|
||||
<view class="card-stack card-3"></view>
|
||||
<view class="card-stack card-1">
|
||||
<view class="card-bg"></view>
|
||||
</view>
|
||||
<view class="card-stack card-2">
|
||||
<view class="card-bg"></view>
|
||||
</view>
|
||||
<view class="card-stack card-3">
|
||||
<view class="card-bg"></view>
|
||||
<view class="card-pattern">
|
||||
<view class="card-symbol">✡</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<text class="deck-title">开始抽牌</text>
|
||||
</view>
|
||||
|
|
@ -41,9 +62,9 @@
|
|||
<text class="drawer-icon">🎯</text>
|
||||
<text class="drawer-text">积分说明</text>
|
||||
</view>
|
||||
<view class="drawer-item" bindtap="goToPrivacy" hover-class="drawer-hover">
|
||||
<text class="drawer-icon">🔒</text>
|
||||
<text class="drawer-text">隐私政策</text>
|
||||
<view class="drawer-item" bindtap="goToSettings" hover-class="drawer-hover">
|
||||
<text class="drawer-icon">⚙️</text>
|
||||
<text class="drawer-text">更多设置</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
|
@ -56,6 +77,43 @@
|
|||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 积分说明浮层 -->
|
||||
<view class="overlay {{pointsVisible ? 'show' : ''}}" bindtap="hidePointsInfo">
|
||||
<view class="overlay-content points-content" catchtap="stopPropagation">
|
||||
<view class="points-header">
|
||||
<text class="points-big-icon">🎯</text>
|
||||
<text class="points-big-value">{{currentPoints}}</text>
|
||||
<text class="points-label">当前积分</text>
|
||||
</view>
|
||||
|
||||
<view class="points-rules">
|
||||
<view class="rule-item">
|
||||
<text class="rule-icon">📅</text>
|
||||
<view class="rule-text">
|
||||
<text class="rule-title">每日登录</text>
|
||||
<text class="rule-desc">自动获得 +3 积分</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="rule-item">
|
||||
<text class="rule-icon">🎁</text>
|
||||
<view class="rule-text">
|
||||
<text class="rule-title">分享给朋友</text>
|
||||
<text class="rule-desc">获得 +10 积分 (每日限3次)</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="rule-item">
|
||||
<text class="rule-icon">🔮</text>
|
||||
<view class="rule-text">
|
||||
<text class="rule-title">消耗规则</text>
|
||||
<text class="rule-desc">不同牌阵消耗不同积分</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="overlay-close" bindtap="hidePointsInfo">知道了</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 知识卡半屏 -->
|
||||
<view class="panel {{knowledgeVisible ? 'show' : ''}}" bindtap="hideKnowledgePanel">
|
||||
<view class="panel-content" catchtap="stopPropagation">
|
||||
|
|
|
|||
|
|
@ -10,14 +10,20 @@ page {
|
|||
height: 100vh;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
justify-content: flex-start;
|
||||
padding-top: 120rpx;
|
||||
gap: 40px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* ========== 顶部栏 (已移除) ========== */
|
||||
|
||||
/* ========== 右上角积分徽章 ========== */
|
||||
.points-badge {
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
top: 30px; /* Restore original position */
|
||||
right: 20px;
|
||||
background: linear-gradient(135deg, rgba(233, 69, 96, 0.25), rgba(233, 69, 96, 0.15));
|
||||
border: 1px solid rgba(233, 69, 96, 0.5);
|
||||
|
|
@ -31,6 +37,56 @@ page {
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* ========== 星座入口 ========== */
|
||||
.zodiac-entry {
|
||||
width: 80%;
|
||||
max-width: 320px;
|
||||
background: linear-gradient(90deg, #2a2a3e, #16213e);
|
||||
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||
border-radius: 16px;
|
||||
padding: 15px 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
|
||||
z-index: 10;
|
||||
animation: float 6s ease-in-out infinite;
|
||||
margin-top: 120rpx;
|
||||
}
|
||||
|
||||
.entry-hover {
|
||||
transform: scale(0.98);
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.zodiac-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.zodiac-title {
|
||||
color: #fff;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.zodiac-subtitle {
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.zodiac-icon {
|
||||
font-size: 28px;
|
||||
filter: drop-shadow(0 0 10px rgba(255, 255, 255, 0.3));
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0%, 100% { transform: translateY(0); }
|
||||
50% { transform: translateY(-8px); }
|
||||
}
|
||||
|
||||
.badge-icon {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
|
@ -61,10 +117,79 @@ page {
|
|||
position: absolute;
|
||||
width: 180px;
|
||||
height: 260px;
|
||||
background: linear-gradient(135deg, #e94560 0%, #8b3a62 50%, #4a1942 100%);
|
||||
/* background moved to .card-bg */
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5);
|
||||
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.6);
|
||||
transition: all 0.3s ease;
|
||||
/* overflow: hidden; Removed to allow glow to show outside */
|
||||
}
|
||||
|
||||
.card-bg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(135deg, #4a1942 0%, #8b3a62 50%, #e94560 100%);
|
||||
border-radius: 12px;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
/* 模糊光晕描边 */
|
||||
.card-stack::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: -2px;
|
||||
left: -2px;
|
||||
right: -2px;
|
||||
bottom: -2px;
|
||||
z-index: -1;
|
||||
background: linear-gradient(135deg, #7a5cff, #005bea);
|
||||
border-radius: 14px;
|
||||
filter: blur(2px); /* 微调模糊度 */
|
||||
opacity: 1; /* 增强可见度 */
|
||||
}
|
||||
|
||||
/* Card Pattern Overlay */
|
||||
.card-stack::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: 10px;
|
||||
border: 1px solid rgba(255, 215, 0, 0.2);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.card-pattern {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-image: radial-gradient(circle at center, rgba(255,255,255,0.1) 0%, transparent 60%);
|
||||
}
|
||||
|
||||
.card-symbol {
|
||||
font-size: 200rpx; /* 最大化视局冲击 */
|
||||
font-weight: bold;
|
||||
background: linear-gradient(180deg, #FFD700 10%, #E0C3FC 50%, #8A2BE2 90%); /* 金色-淡紫-深紫渐变,增加神秘感 */
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
color: transparent;
|
||||
filter: drop-shadow(0 0 20rpx rgba(255, 215, 0, 0.5)) drop-shadow(0 0 50rpx rgba(138, 43, 226, 0.7)); /* 静态综合光晕 */
|
||||
animation: pulseMystic 4s infinite ease-in-out;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
line-height: 1;
|
||||
will-change: transform, opacity; /* 硬件加速优化 */
|
||||
}
|
||||
|
||||
@keyframes pulseMystic {
|
||||
0%, 100% { transform: scale(1); opacity: 0.85; }
|
||||
50% { transform: scale(1.15); opacity: 1; }
|
||||
}
|
||||
|
||||
.card-1 {
|
||||
|
|
@ -113,8 +238,8 @@ page {
|
|||
position: absolute;
|
||||
width: 90px;
|
||||
height: 120px;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||
background: rgba(255, 255, 255, 0.08); /* Semi-transparent white */
|
||||
border: 1px solid rgba(255, 255, 255, 0.15); /* Light border */
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
|
@ -124,6 +249,8 @@ page {
|
|||
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.3);
|
||||
transition: all 0.3s ease;
|
||||
z-index: 5;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.card-hover {
|
||||
|
|
@ -132,15 +259,11 @@ page {
|
|||
}
|
||||
|
||||
.energy-card {
|
||||
left: 30px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
left: 10px;
|
||||
}
|
||||
|
||||
.knowledge-card {
|
||||
right: 30px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
.card-icon {
|
||||
|
|
@ -163,7 +286,7 @@ page {
|
|||
background: rgba(0, 0, 0, 0.3);
|
||||
backdrop-filter: blur(10px);
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||
padding: 15px 20px 25px;
|
||||
padding: 8px 10px 15px;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
z-index: 20;
|
||||
|
|
@ -173,8 +296,8 @@ page {
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 10px 15px;
|
||||
gap: 2px;
|
||||
padding: 4px 8px;
|
||||
border-radius: 12px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
|
@ -347,3 +470,95 @@ page {
|
|||
background: rgba(255, 255, 255, 0.1);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* 积分浮层特有样式 */
|
||||
.points-header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-bottom: 25px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.points-big-icon {
|
||||
font-size: 40px;
|
||||
margin-bottom: 5px;
|
||||
filter: drop-shadow(0 0 10px rgba(233, 69, 96, 0.4));
|
||||
animation: float 4s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.points-big-value {
|
||||
font-size: 48px;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
text-shadow: 0 0 20px rgba(233, 69, 96, 0.6);
|
||||
line-height: 1;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.points-label {
|
||||
font-size: 14px;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
.points-rules {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
border-radius: 12px;
|
||||
padding: 15px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 25px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.rule-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.rule-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.rule-icon {
|
||||
font-size: 24px;
|
||||
margin-right: 15px;
|
||||
width: 30px;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.rule-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
|
||||
.rule-title {
|
||||
color: #ffffff !important;
|
||||
font-size: 15px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 4px;
|
||||
text-align: left;
|
||||
text-shadow: 0 1px 2px rgba(0,0,0,0.8);
|
||||
}
|
||||
|
||||
.rule-desc {
|
||||
color: #eeeeee !important;
|
||||
font-size: 13px;
|
||||
text-align: left;
|
||||
opacity: 0.9;
|
||||
text-shadow: 0 1px 2px rgba(0,0,0,0.5);
|
||||
}
|
||||
|
||||
|
||||
.points-content {
|
||||
width: 70% !important;
|
||||
max-width: 300px !important;
|
||||
padding: 20px !important;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,7 +64,20 @@ const TAROT_PROMPT_NIGHT = `你是一位安静、克制、不制造依赖的塔
|
|||
// 3. 增强版模板:解读稳定器 + 高关联性结构化解读
|
||||
const TAROT_PROMPT_ENHANCED = `📏 解读稳定规则(高优先级执行)
|
||||
|
||||
你必须保持解读风格稳定、具体且一致,不允许出现质量波动。
|
||||
【合规红线 - 必须遵守】
|
||||
1. **娱乐属性**:明确告知用户内容仅供参考,不构成现实决策依据。
|
||||
2. **严禁迷信**:禁止使用“注定”、“必然”、“灾祸”、“鬼神”等封建迷信词汇。
|
||||
3. **禁止决策建议**:严禁提供任何医疗、法律、投资理财方面的具体建议。
|
||||
4. **正向引导**:解读必须积极向上,侧重心理疏导和个人成长,避免制造焦虑。
|
||||
|
||||
1. **结构化输出**:必须严格遵循JSON格式,包含 theme, status, influence, advice, positions (数组)。
|
||||
2. **字数控制**:
|
||||
- 核心主题:15-20字(精炼一句话)
|
||||
- 单个位置解读:60-100字(深度但不过长)每张牌2句以内
|
||||
- 当前状态:2~3句
|
||||
- 发展趋势:2~3句
|
||||
- 行动建议:3~5条短句
|
||||
• 禁止:超长段落、大段心理学科普、冗长比喻
|
||||
|
||||
【一、解读长度控制】
|
||||
• 整体长度:400~700 字(不少于 300 字,不超过 900 字)
|
||||
|
|
@ -168,7 +181,8 @@ const SPREADS = [
|
|||
"positions": ["当下的指引"],
|
||||
"description": "为你此刻的状态提供一个温和而清晰的方向提示",
|
||||
"tags": ["综合"],
|
||||
"aiSchema": ["core_theme", "current_state", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "three_time_flow",
|
||||
|
|
@ -178,7 +192,8 @@ const SPREADS = [
|
|||
"positions": ["过去", "现在", "未来"],
|
||||
"description": "帮助你理解事情的发展过程与可能走向",
|
||||
"tags": ["综合", "决策"],
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "three_problem_solution",
|
||||
|
|
@ -188,7 +203,8 @@ const SPREADS = [
|
|||
"positions": ["问题核心", "当前阻碍", "行动建议"],
|
||||
"description": "聚焦关键问题,找出当下最可行的应对方式",
|
||||
"tags": ["决策"],
|
||||
"aiSchema": ["core_theme", "current_state", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "love_spark",
|
||||
|
|
@ -198,7 +214,8 @@ const SPREADS = [
|
|||
"positions": ["你的感受", "对方的感受", "关系潜力"],
|
||||
"description": "探索彼此的真实感受与这段关系的可能性",
|
||||
"tags": ["爱情"],
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "two_choice_decision",
|
||||
|
|
@ -208,7 +225,8 @@ const SPREADS = [
|
|||
"positions": ["选择A的发展", "选择A的结果", "选择B的发展", "选择B的结果"],
|
||||
"description": "对比两种选择的潜在走向,辅助理性决策",
|
||||
"tags": ["决策"],
|
||||
"aiSchema": ["core_theme", "potential_influence", "action_advice"]
|
||||
"aiSchema": ["core_theme", "potential_influence", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "relationship_healing",
|
||||
|
|
@ -218,7 +236,8 @@ const SPREADS = [
|
|||
"positions": ["问题根源", "你的责任", "对方的责任", "修复方向"],
|
||||
"description": "深入理解关系裂痕,找到和解与修复的路径",
|
||||
"tags": ["爱情"],
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "five_situation_analysis",
|
||||
|
|
@ -228,7 +247,8 @@ const SPREADS = [
|
|||
"positions": ["现状", "内在因素", "外在影响", "行动方向", "可能结果"],
|
||||
"description": "从内外层面拆解局势,明确下一步行动",
|
||||
"tags": ["事业", "综合"],
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "relationship_spread",
|
||||
|
|
@ -238,7 +258,8 @@ const SPREADS = [
|
|||
"positions": ["你的位置", "对方的位置", "关系现状", "隐藏影响", "未来趋势"],
|
||||
"description": "理解一段关系中的互动模式与发展方向",
|
||||
"tags": ["爱情"],
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "destiny_connection",
|
||||
|
|
@ -248,7 +269,8 @@ const SPREADS = [
|
|||
"positions": ["前世因缘", "今生相遇", "情感纽带", "考验挑战", "缘分走向"],
|
||||
"description": "探索两人之间的灵性连接与命运安排",
|
||||
"tags": ["爱情"],
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "career_breakthrough",
|
||||
|
|
@ -258,7 +280,8 @@ const SPREADS = [
|
|||
"positions": ["当前困境", "优势资源", "潜在机会", "需要克服", "突破方向"],
|
||||
"description": "识别职场瓶颈,找到突破与晋升的关键",
|
||||
"tags": ["事业"],
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "timeline_spread",
|
||||
|
|
@ -268,7 +291,8 @@ const SPREADS = [
|
|||
"positions": ["远古根源", "过去影响", "当下状态", "近期发展", "未来趋势"],
|
||||
"description": "追溯事件的时间线,看清发展脉络",
|
||||
"tags": ["综合"],
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "diamond_spread",
|
||||
|
|
@ -278,7 +302,8 @@ const SPREADS = [
|
|||
"positions": ["问题本质", "过去原因", "未来发展", "外部资源", "最佳行动"],
|
||||
"description": "多角度剖析问题,找到解决之道",
|
||||
"tags": ["决策"],
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "career_planning",
|
||||
|
|
@ -288,7 +313,8 @@ const SPREADS = [
|
|||
"positions": ["当前位置", "核心优势", "发展方向", "潜在障碍", "贵人助力", "长期目标"],
|
||||
"description": "全面规划职业发展路径,明确长期目标",
|
||||
"tags": ["事业"],
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "wealth_analysis",
|
||||
|
|
@ -298,7 +324,8 @@ const SPREADS = [
|
|||
"positions": ["财务现状", "收入来源", "支出模式", "投资机会", "财务风险", "财富增长"],
|
||||
"description": "深入分析财务状况,找到财富增长的路径",
|
||||
"tags": ["事业"],
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "spiritual_guidance",
|
||||
|
|
@ -308,7 +335,8 @@ const SPREADS = [
|
|||
"positions": ["当前能量", "内在阻碍", "潜在天赋", "灵性课题", "指导建议", "未来机遇", "最高指引"],
|
||||
"description": "深入探索内在世界,获得灵性层面的启发",
|
||||
"tags": ["深度"],
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "horseshoe_spread",
|
||||
|
|
@ -318,7 +346,8 @@ const SPREADS = [
|
|||
"positions": ["过去", "现在", "未来", "你的态度", "他人影响", "障碍", "最终结果"],
|
||||
"description": "全面了解情况的来龙去脉与未来走向",
|
||||
"tags": ["综合"],
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "celtic_cross",
|
||||
|
|
@ -328,7 +357,8 @@ const SPREADS = [
|
|||
"positions": ["现状", "挑战", "根基", "过去", "可能性", "近期未来", "你的态度", "外部影响", "希望与恐惧", "最终结果"],
|
||||
"description": "最经典的综合牌阵,深度解析生命议题",
|
||||
"tags": ["深度"],
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "tree_of_life",
|
||||
|
|
@ -338,7 +368,8 @@ const SPREADS = [
|
|||
"positions": ["王冠", "智慧", "理解", "慈悲", "严厉", "美丽", "胜利", "荣耀", "基础", "王国"],
|
||||
"description": "基于卡巴拉生命之树的深度灵性探索",
|
||||
"tags": ["深度"],
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
// 新增爱情牌阵
|
||||
{
|
||||
|
|
@ -349,7 +380,8 @@ const SPREADS = [
|
|||
"positions": ["核心关系", "你的状态", "对方状态", "未来发展"],
|
||||
"description": "适合恋人关系 & 互动解析",
|
||||
"tags": ["爱情"],
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "new_love",
|
||||
|
|
@ -359,7 +391,8 @@ const SPREADS = [
|
|||
"positions": ["当前状态", "遇见契机", "发展方向"],
|
||||
"description": "适合新的感情 & 遇见新欢,为单身的你或刚分手的你带来新爱提示",
|
||||
"tags": ["爱情"],
|
||||
"aiSchema": ["core_theme", "current_state", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "relationship_basic",
|
||||
|
|
@ -369,7 +402,8 @@ const SPREADS = [
|
|||
"positions": ["你的想法", "对方想法", "关系走向"],
|
||||
"description": "适合梳理关系 & 拉近距离,回溯结识初衷,梳理人际关系,拉近彼此距离",
|
||||
"tags": ["爱情"],
|
||||
"aiSchema": ["core_theme", "current_state", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "love_tree_spread",
|
||||
|
|
@ -379,7 +413,8 @@ const SPREADS = [
|
|||
"positions": ["核心问题", "过去影响", "现在状况", "未来趋势", "最终结果"],
|
||||
"description": "适合深度求爱 & 寻找症结,解析前因后果,回溯爱情过往,探究本源核心",
|
||||
"tags": ["爱情"],
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "love_cross_spread",
|
||||
|
|
@ -389,7 +424,8 @@ const SPREADS = [
|
|||
"positions": ["你的状态", "对方状态", "过去影响", "现在情况", "未来发展"],
|
||||
"description": "适合解析两性关系 & 爱情状况,基于洞悉彼此关系中的情感状况并分析结果",
|
||||
"tags": ["爱情"],
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "mr_right",
|
||||
|
|
@ -399,7 +435,8 @@ const SPREADS = [
|
|||
"positions": ["内心期待", "现实阻碍", "理想对象", "行动建议", "遇见时机"],
|
||||
"description": "单身探索有缘人 & 合适对象,探索内心的想法,改善外在的行为,遇到适合的人",
|
||||
"tags": ["爱情"],
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "search_lover",
|
||||
|
|
@ -409,7 +446,8 @@ const SPREADS = [
|
|||
"positions": ["当前状态", "优势特质", "改进方向", "寻找方式", "目标确定"],
|
||||
"description": "适合寻找意中人 & 有缘人,睁全宫中人的愿景,帮助自己确定目标",
|
||||
"tags": ["爱情"],
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "inspiration_correspondence",
|
||||
|
|
@ -419,7 +457,8 @@ const SPREADS = [
|
|||
"positions": ["情感现状", "内心需求", "对方感受", "互动方式", "发展建议", "最终走向"],
|
||||
"description": "适合情感困惑 & 人际关系,增长解析指缘联接或情感互动,帮您交融状态",
|
||||
"tags": ["爱情"],
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "gypsy_spread",
|
||||
|
|
@ -429,7 +468,8 @@ const SPREADS = [
|
|||
"positions": ["你的想法", "对方想法", "过去经历", "现在状况", "未来发展"],
|
||||
"description": "适合情侣分析 & 关系延展,探索彼此内心想法,找到合适的相处方式",
|
||||
"tags": ["爱情"],
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "venus_spread",
|
||||
|
|
@ -439,7 +479,8 @@ const SPREADS = [
|
|||
"positions": ["你的状态", "对方状态", "关系开始", "当前情况", "核心问题", "最终结果", "外部影响", "内在感受"],
|
||||
"description": "适合爱情发展 & 判断走向,全面的爱情解析牌阵,深入分析两性关系",
|
||||
"tags": ["爱情"],
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "lover_tree",
|
||||
|
|
@ -449,7 +490,8 @@ const SPREADS = [
|
|||
"positions": ["你的行为", "对方行为", "你的想法", "对方想法", "你的感受", "对方感受", "关系走向"],
|
||||
"description": "适合探究对方心理 & 行为模式,对比情侣间的行为差异,进而根据事实做出判断",
|
||||
"tags": ["爱情"],
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "marriage_spread",
|
||||
|
|
@ -459,7 +501,8 @@ const SPREADS = [
|
|||
"positions": ["婚姻基础", "你的期望", "对方期望", "核心问题", "外部影响", "内在感受", "未来走向"],
|
||||
"description": "适合婚姻状况 & 内在剖析,针对婚姻状况和期望进行解析,通过细致分析把握婚姻走向",
|
||||
"tags": ["爱情"],
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
// 新增事业牌阵
|
||||
{
|
||||
|
|
@ -470,7 +513,8 @@ const SPREADS = [
|
|||
"positions": ["问题核心", "左侧因素", "右侧因素"],
|
||||
"description": "适合财富探索 & 问题解析,清楚审视整个问题的来龙去脉,进而做出正确决策",
|
||||
"tags": ["事业", "财富"],
|
||||
"aiSchema": ["core_theme", "current_state", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "success_star",
|
||||
|
|
@ -480,7 +524,8 @@ const SPREADS = [
|
|||
"positions": ["当前位置", "行动方向", "成功目标"],
|
||||
"description": "适合走位辨路 & 累积行动,锁定目标破除阻碍,告诉星光,心有明月再出发",
|
||||
"tags": ["事业"],
|
||||
"aiSchema": ["core_theme", "current_state", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "career_pyramid",
|
||||
|
|
@ -490,7 +535,8 @@ const SPREADS = [
|
|||
"positions": ["核心优势", "需要改进", "外部机会", "未来发展"],
|
||||
"description": "适合解析财富运势 & 提升财商,明晰自我的优缺点,帮助自己除弊趋利在事业中获得优势",
|
||||
"tags": ["事业"],
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "wealth_tree",
|
||||
|
|
@ -500,7 +546,8 @@ const SPREADS = [
|
|||
"positions": ["财务基础", "收入来源", "支出状况", "当前状态", "未来趋势"],
|
||||
"description": "适合事业发展 & 状态评估,通过对财富的梳理,健全适合自己的财务模式",
|
||||
"tags": ["事业", "财富"],
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "x_opportunity",
|
||||
|
|
@ -510,7 +557,8 @@ const SPREADS = [
|
|||
"positions": ["当前时机", "有利因素", "不利因素", "行动建议", "结果预测"],
|
||||
"description": "适合时机捕捉 & 临场决策,以事物处理时机为主线轴,牵挂问题解决的成功概率",
|
||||
"tags": ["事业", "决策"],
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "job_interview",
|
||||
|
|
@ -520,7 +568,8 @@ const SPREADS = [
|
|||
"positions": ["自我状态", "优势展现", "需要注意", "面试官印象", "录用可能"],
|
||||
"description": "适合解析应聘面试 & 求职状况,洞察对方需求与自我留言,有效提高面试成功率",
|
||||
"tags": ["事业"],
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "work_problems",
|
||||
|
|
@ -530,7 +579,8 @@ const SPREADS = [
|
|||
"positions": ["问题核心", "上级态度", "同事关系", "个人能力", "外部环境", "解决方案"],
|
||||
"description": "适合聚焦目标 & 优化路径,当你明确自己的目标,全世界都会为你让路",
|
||||
"tags": ["事业"],
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "turbulent_finances",
|
||||
|
|
@ -540,7 +590,8 @@ const SPREADS = [
|
|||
"positions": ["财务起点", "第一波动", "稳定期", "第二波动", "调整期", "未来趋势"],
|
||||
"description": "适合借力打力 & 多空对比,追问本质,结合正反两方面的分析,做出理智判断",
|
||||
"tags": ["事业", "财富"],
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "desired_job",
|
||||
|
|
@ -550,7 +601,8 @@ const SPREADS = [
|
|||
"positions": ["理想工作", "个人优势", "需要提升", "寻找方向", "关键因素", "实现可能"],
|
||||
"description": "适合评估工作前景 & 关联因素,剖析工作前景,理清相关因素,给出明确的思路",
|
||||
"tags": ["事业"],
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
// 新增决策牌阵
|
||||
{
|
||||
|
|
@ -561,7 +613,8 @@ const SPREADS = [
|
|||
"positions": ["选项A", "选项B", "建议方向"],
|
||||
"description": "适合评测选项 & 做出抉择,在两难的处境中给出建议,选出适合自己的抉择",
|
||||
"tags": ["决策"],
|
||||
"aiSchema": ["core_theme", "current_state", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "three_options",
|
||||
|
|
@ -571,7 +624,8 @@ const SPREADS = [
|
|||
"positions": ["当前状况", "参考方向一", "参考方向二"],
|
||||
"description": "提供参考方向 & 行动指向,遇到3个选择时,给你提供参考的方向",
|
||||
"tags": ["决策"],
|
||||
"aiSchema": ["core_theme", "current_state", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "daily_tree",
|
||||
|
|
@ -581,7 +635,8 @@ const SPREADS = [
|
|||
"positions": ["今日核心", "需要注意", "有利因素", "行动建议"],
|
||||
"description": "适长当日难题 & 当日解决,天天没烦恼,解决每天遇到的问题",
|
||||
"tags": ["综合"],
|
||||
"aiSchema": ["core_theme", "current_state", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "weigh_two",
|
||||
|
|
@ -591,7 +646,8 @@ const SPREADS = [
|
|||
"positions": ["当前状况", "选项A优势", "选项B优势", "选项A劣势", "选项B劣势"],
|
||||
"description": "主要用于抉择 & 判断,二选一,适用于二种情况选择其中一种",
|
||||
"tags": ["决策"],
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "comparing_choices",
|
||||
|
|
@ -601,7 +657,8 @@ const SPREADS = [
|
|||
"positions": ["选项A现状", "选项A发展", "选项A结果", "选项B现状", "选项B发展", "选项B结果"],
|
||||
"description": "适合判断情势 & 决定方向,多层面聚焦两个方向,更多的线索更多的细节",
|
||||
"tags": ["决策"],
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "weigh_three",
|
||||
|
|
@ -611,7 +668,8 @@ const SPREADS = [
|
|||
"positions": ["当前状况", "选项A优势", "选项B优势", "选项A劣势", "选项C优势", "选项C劣势", "选项B劣势"],
|
||||
"description": "适合事情抉择 & 选择评估,对利弊关系进行分析判断,权衡利弊,做出最终的选择",
|
||||
"tags": ["决策"],
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
// 新增深度探索牌阵
|
||||
{
|
||||
|
|
@ -622,7 +680,8 @@ const SPREADS = [
|
|||
"positions": ["身体", "心智", "灵性", "整体状态"],
|
||||
"description": "适合自我探索 & 了解自己,透彻审视自我,更直切明了自身成长路径",
|
||||
"tags": ["成长", "综合"],
|
||||
"aiSchema": ["core_theme", "current_state", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "four_elements",
|
||||
|
|
@ -632,7 +691,8 @@ const SPREADS = [
|
|||
"positions": ["感性", "理性", "物质", "行动"],
|
||||
"description": "适合问题探索 & 多方解析,从感性、理性、物质、行动四方面分析,进而接近实质",
|
||||
"tags": ["探索", "综合"],
|
||||
"aiSchema": ["core_theme", "current_state", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "self_discovery",
|
||||
|
|
@ -642,7 +702,8 @@ const SPREADS = [
|
|||
"positions": ["核心自我", "潜能", "优势", "需要提升"],
|
||||
"description": "适合认识自我 & 提升潜能,开发自身潜力,提高对自己的认识",
|
||||
"tags": ["探索", "成长"],
|
||||
"aiSchema": ["core_theme", "current_state", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "know_yourself",
|
||||
|
|
@ -652,7 +713,8 @@ const SPREADS = [
|
|||
"positions": ["核心自我", "思想", "行为", "情感", "环境影响"],
|
||||
"description": "适合自我剖析 & 境况认知,人无时无刻不受环境的影响,在各种关系的交织中成长",
|
||||
"tags": ["成长", "综合"],
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "your_breakthrough",
|
||||
|
|
@ -662,7 +724,8 @@ const SPREADS = [
|
|||
"positions": ["当前困境", "阻碍因素", "突破点", "行动方向", "预期结果"],
|
||||
"description": "适合打破僵环 & 突破自我,识别固定模式,突破往复循环",
|
||||
"tags": ["成长"],
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "elemental_cross",
|
||||
|
|
@ -672,7 +735,8 @@ const SPREADS = [
|
|||
"positions": ["核心问题", "火元素", "水元素", "风元素", "土元素", "解决方案"],
|
||||
"description": "主要用于带入问题 & 解决问题,根据现实情况全面运用四元素能提升自己",
|
||||
"tags": ["探索", "综合"],
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "wheel_spread",
|
||||
|
|
@ -682,7 +746,8 @@ const SPREADS = [
|
|||
"positions": ["中心", "过去", "现在", "未来", "机会", "挑战", "结果"],
|
||||
"description": "适合专注自我 & 探寻改变,有时走出循环照顾的舒适圈,有所改变时,才能成长",
|
||||
"tags": ["成长"],
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "seven_planets",
|
||||
|
|
@ -692,7 +757,8 @@ const SPREADS = [
|
|||
"positions": ["核心", "月亮", "水星", "金星", "太阳", "火星", "木星"],
|
||||
"description": "适合审视目前状态 & 了解自己,综合分析当下状况,全面的了解自己",
|
||||
"tags": ["成长", "综合"],
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
},
|
||||
{
|
||||
"id": "dream_mirror",
|
||||
|
|
@ -702,7 +768,8 @@ const SPREADS = [
|
|||
"positions": ["梦境表象", "潜意识", "情感", "符号", "启示", "行动", "整合"],
|
||||
"description": "适合分析梦境 & 获得启迪,梦像一面镜子,解读梦境,是理解自己的渠道",
|
||||
"tags": ["探索"],
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"]
|
||||
"aiSchema": ["core_theme", "current_state", "potential_influence", "action_advice"],
|
||||
layout: { type: 'grid' }
|
||||
}
|
||||
];
|
||||
|
||||
|
|
@ -739,17 +806,29 @@ Page({
|
|||
// AI 解读相关
|
||||
aiResult: null,
|
||||
isAiLoading: false,
|
||||
aiLoadingText: '正在抽取牌面…', // 动态加载提示
|
||||
aiLoadingText: '正在深度解读...', // 动态加载提示
|
||||
spreadStory: '',
|
||||
|
||||
// 3. 前缀文案列表(随机抽取)
|
||||
prefixList: [
|
||||
"今天,这张牌提醒你:",
|
||||
"此刻,宇宙传递的信息:",
|
||||
"这张牌想告诉你:",
|
||||
"请收下这份指引:"
|
||||
"透过层层迷雾,牌面显示...",
|
||||
"连接此刻的能量,这张牌意味着...",
|
||||
"这是一个特别的信号...",
|
||||
"如同命运的低语...",
|
||||
"在潜意识的深处..."
|
||||
],
|
||||
|
||||
// 4. 分享引导文案
|
||||
randomGuidanceText: "",
|
||||
guidanceList: [
|
||||
"这组牌很少见",
|
||||
"你的能量状态正在转折",
|
||||
"这个答案值得记录",
|
||||
"一次直抵内心的对话",
|
||||
"命运在这一刻给予了回应"
|
||||
],
|
||||
|
||||
|
||||
// 2. 完整的 22 张大阿尔卡那牌组(包含逆位含义)
|
||||
cardList: [
|
||||
{
|
||||
|
|
@ -1206,7 +1285,7 @@ Page({
|
|||
|
||||
// --- 4. 逐一翻牌 ---
|
||||
revealNext: function (e) {
|
||||
const index = e.currentTarget.dataset.index;
|
||||
const index = (e.detail && e.detail.index !== undefined) ? e.detail.index : e.currentTarget.dataset.index;
|
||||
if (index === this.data.revealedCount) {
|
||||
const nextCount = index + 1;
|
||||
this.setData({
|
||||
|
|
@ -1215,12 +1294,34 @@ Page({
|
|||
|
||||
// 如果全部翻开,触发解读
|
||||
if (nextCount === this.data.selectedSpread.cardCount) {
|
||||
this.setData({ state: 'revealed' });
|
||||
// 随机生成分享引导文案
|
||||
// Note: guidanceList needs to be defined in data or elsewhere for this to work.
|
||||
// For now, assuming it exists or will be added.
|
||||
const guidanceIdx = Math.floor(Math.random() * (this.data.guidanceList ? this.data.guidanceList.length : 1));
|
||||
const guidanceText = this.data.guidanceList ? this.data.guidanceList[guidanceIdx] : '分享你的塔罗解读,获得更多指引!';
|
||||
|
||||
this.setData({
|
||||
state: 'revealed',
|
||||
randomGuidanceText: guidanceText // 设置引导文案
|
||||
});
|
||||
this.showInterpretation();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 查看已翻开牌的详情
|
||||
viewCardDetail: function (e) {
|
||||
const index = (e.detail && e.detail.index !== undefined) ? e.detail.index : e.currentTarget.dataset.index;
|
||||
const card = this.data.drawnCards[index];
|
||||
if (card) {
|
||||
wx.showToast({
|
||||
title: card.name + (card.isReversed ? '(逆)' : ''),
|
||||
icon: 'none',
|
||||
duration: 1500
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// --- 展示解读内容 ---
|
||||
showInterpretation: function () {
|
||||
const infoAnim = wx.createAnimation({
|
||||
|
|
@ -1242,6 +1343,7 @@ Page({
|
|||
this.setData({
|
||||
isAiLoading: true,
|
||||
aiResult: null,
|
||||
aiStreamText: '',
|
||||
aiLoadingText: '正在抽取牌面…'
|
||||
});
|
||||
|
||||
|
|
@ -1305,11 +1407,7 @@ ${cardMeanings}
|
|||
|
||||
// 5. 处理结果
|
||||
if (result.status === 'blocked') {
|
||||
wx.showToast({
|
||||
title: result.message,
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
wx.showToast({ title: result.message, icon: 'none', duration: 2000 });
|
||||
this.setData({ isAiLoading: false });
|
||||
return;
|
||||
}
|
||||
|
|
@ -1326,14 +1424,69 @@ ${cardMeanings}
|
|||
|
||||
// 8. 如果是 fallback,显示提示
|
||||
if (result.status === 'fallback' && result.error) {
|
||||
wx.showToast({
|
||||
title: 'AI 解读失败,已使用备用解读',
|
||||
icon: 'none',
|
||||
duration: 3000
|
||||
});
|
||||
wx.showToast({ title: 'AI 解读失败,已使用备用解读', icon: 'none', duration: 3000 });
|
||||
}
|
||||
},
|
||||
|
||||
// --- 打字机效果:逐字显示 AI 解读 ---
|
||||
typewriterReveal: function (data) {
|
||||
// 初始化空的 aiResult 结构,先占位
|
||||
const emptyResult = {
|
||||
theme: '',
|
||||
status: '',
|
||||
influence: '',
|
||||
advice: '',
|
||||
positions: (data.positions || []).map(p => ({
|
||||
posName: p.posName,
|
||||
posMeaning: ''
|
||||
}))
|
||||
};
|
||||
this.setData({ aiResult: emptyResult });
|
||||
|
||||
// 把所有需要打字机显示的字段排成队列
|
||||
const fields = [
|
||||
{ key: 'aiResult.theme', text: data.theme || '' },
|
||||
{ key: 'aiResult.status', text: data.status || '' },
|
||||
{ key: 'aiResult.influence', text: data.influence || '' },
|
||||
{ key: 'aiResult.advice', text: data.advice || '' },
|
||||
];
|
||||
|
||||
// 加入每个位置的解读
|
||||
(data.positions || []).forEach((pos, i) => {
|
||||
fields.push({
|
||||
key: `aiResult.positions[${i}].posMeaning`,
|
||||
text: pos.posMeaning || ''
|
||||
});
|
||||
});
|
||||
|
||||
// 逐字段、逐字符地显示
|
||||
let fieldIndex = 0;
|
||||
|
||||
const revealNextField = () => {
|
||||
if (fieldIndex >= fields.length) return; // 全部完成
|
||||
|
||||
const field = fields[fieldIndex];
|
||||
const fullText = field.text;
|
||||
let charIndex = 0;
|
||||
|
||||
// 每个字符间隔 25ms(约 40字/秒)
|
||||
const timer = setInterval(() => {
|
||||
charIndex++;
|
||||
const partial = fullText.slice(0, charIndex);
|
||||
this.setData({ [field.key]: partial });
|
||||
|
||||
if (charIndex >= fullText.length) {
|
||||
clearInterval(timer);
|
||||
fieldIndex++;
|
||||
// 字段间停顿 150ms 再开始下一个字段
|
||||
setTimeout(revealNextField, 150);
|
||||
}
|
||||
}, 25);
|
||||
};
|
||||
|
||||
revealNextField();
|
||||
},
|
||||
|
||||
handleAiFallback: function () {
|
||||
// 构建一个基础的回退对象
|
||||
const fallback = {
|
||||
|
|
@ -1401,11 +1554,30 @@ ${cardMeanings}
|
|||
wx.navigateBack({
|
||||
delta: 1
|
||||
});
|
||||
} else {
|
||||
// 其他状态直接返回主页
|
||||
wx.navigateBack({
|
||||
delta: 1
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 用户点击右上角分享
|
||||
*/
|
||||
onShareAppMessage: function () {
|
||||
const titles = [
|
||||
"我刚抽到一组很准的塔罗…",
|
||||
"这次塔罗结果让我有点意外",
|
||||
"今天的塔罗指引比我想象深",
|
||||
"这是我此刻的能量状态✨",
|
||||
"这里有一个答案,也许你也需要"
|
||||
];
|
||||
const randomTitle = titles[Math.floor(Math.random() * titles.length)];
|
||||
|
||||
// 模拟生成 fromUserId (实际应从用户信息获取)
|
||||
// 这里简单使用一个随机字符串代替,或者保留为空等待后续接入用户系统
|
||||
const mockUserId = 'user_' + Math.random().toString(36).substr(2, 9);
|
||||
|
||||
return {
|
||||
title: randomTitle,
|
||||
path: `/pages/home/home?fromUserId=${mockUserId}`,
|
||||
imageUrl: '/images/share-cover.png' // 建议后续添加一张通用的分享图
|
||||
};
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
{
|
||||
"navigationBarTitleText": "塔罗指引",
|
||||
"usingComponents": {}
|
||||
"usingComponents": {
|
||||
"spread-container": "/components/spread-container/spread-container"
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
<text class="back-icon">⇠</text>
|
||||
<text class="back-text">返回</text>
|
||||
</view>
|
||||
|
||||
<!-- 1. 牌阵选择状态 - 卡片式V2 -->
|
||||
<view class="spread-select-area" wx:if="{{state === 'spread_select'}}">
|
||||
<!-- 顶部标题区域 -->
|
||||
|
|
@ -47,7 +48,7 @@
|
|||
<!-- 底部信息 -->
|
||||
<view class="card-footer">
|
||||
<text class="card-count">{{item.cardCount}} 张牌</text>
|
||||
<text class="card-cost">消耗 {{item.cost}} 积分</text>
|
||||
<text class="card-cost">需 {{item.cost}} 积分</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -145,33 +146,42 @@
|
|||
module.exports.getTransform = function(indices, currentIdx, count) {
|
||||
var pos = indices.indexOf(currentIdx);
|
||||
if (pos !== -1) {
|
||||
// 每排最多5张牌
|
||||
var maxPerRow = 5;
|
||||
var row = Math.floor(pos / maxPerRow); // 第几排(0或1)
|
||||
var col = pos % maxPerRow; // 该排的第几个
|
||||
var cardsInRow = (row === 0) ? Math.min(count, maxPerRow) : (count - maxPerRow);
|
||||
var row = Math.floor(pos / maxPerRow);
|
||||
var col = pos % maxPerRow;
|
||||
|
||||
// 槽位:宽100rpx + gap 20rpx = 120rpx 间距
|
||||
var slotWidth = 100;
|
||||
var gap = 20;
|
||||
var slotSpacing = slotWidth + gap;
|
||||
var cardsRemaining = count - (row * maxPerRow);
|
||||
var cardsInRow = (cardsRemaining > maxPerRow) ? maxPerRow : cardsRemaining;
|
||||
|
||||
// 计算该排槽位的水平居中偏移
|
||||
// 从该排中间位置开始计算
|
||||
var centerOffset = (col - (cardsInRow - 1) / 2) * slotSpacing;
|
||||
// Container Calculation
|
||||
var rowCount = Math.ceil(count / maxPerRow);
|
||||
var contentHeight = (rowCount * 160) + ((rowCount - 1) * 20);
|
||||
if (contentHeight < 240) { contentHeight = 240; } // Handle min-height: 240rpx
|
||||
|
||||
// 垂直偏移计算
|
||||
// 第一排槽位:向上 -520rpx(已验证接近正确)
|
||||
// 第二排:需要减少向上的距离
|
||||
// 槽位高度160 + gap 20 = 180,但实际可能需要微调
|
||||
var firstRowY = -520;
|
||||
var secondRowOffset = 200; // 增加偏移量,让第二排更接近fan-deck
|
||||
var yOffset = firstRowY + (row * secondRowOffset); // row=0: -520, row=1: -320
|
||||
// Distance from Slot Container Top to Fan Deck Bottom
|
||||
// FanHeight(380) + FanMarginTop(20) + ContainerMarginBottom(60) + ContainerHeight
|
||||
var distToFanBottom = 380 + 20 + 60 + contentHeight;
|
||||
|
||||
// Determine Slot Center Y from Container Top
|
||||
// Row 0 starts at 0. Center is 80.
|
||||
// Row 1 starts at 180. Center is 260.
|
||||
var slotCenterY = (row * 180) + 80;
|
||||
|
||||
// Target Y relative to Fan Bottom (0)
|
||||
// SlotCenter is above FanBottom, so negative.
|
||||
var targetY = -(distToFanBottom - slotCenterY);
|
||||
|
||||
// Adjust for Card Visual Center (Transform Origin: Bottom Center)
|
||||
// Card Height 260 -> Scaled 0.65 -> 169
|
||||
// Visual Center is 84.5 from bottom.
|
||||
// We want visual center at targetY.
|
||||
// Origin (Bottom) needs to be at targetY + 84.5
|
||||
var yOffset = targetY + 84.5;
|
||||
|
||||
var centerOffset = (col - (cardsInRow - 1) / 2) * 120;
|
||||
|
||||
// 位移并缩小到0.65
|
||||
return 'translate(' + centerOffset + 'rpx, ' + yOffset + 'rpx) scale(0.65) rotate(0deg)';
|
||||
} else {
|
||||
// 保持在牌堆中的放射状
|
||||
return 'rotate(' + ((currentIdx - 10) * 8) + 'deg)';
|
||||
}
|
||||
};
|
||||
|
|
@ -184,25 +194,25 @@
|
|||
<!-- 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]}}(第{{index + 1}}张)</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>
|
||||
<!-- 卡牌阵列 (Unified Layout System) -->
|
||||
<spread-container
|
||||
spread="{{selectedSpread}}"
|
||||
cards="{{drawnCards}}"
|
||||
revealed-count="{{revealedCount}}"
|
||||
bind:reveal="revealNext"
|
||||
bind:viewDetail="viewCardDetail"
|
||||
/>
|
||||
|
||||
<!-- 翻牌提示 -->
|
||||
<view class="flip-hint" wx:if="{{state === 'flipping' && revealedCount < selectedSpread.cardCount}}">
|
||||
<text class="flip-hint-icon">✦</text>
|
||||
<text class="flip-hint-text">依次点击牌面,逐一揭示</text>
|
||||
<text class="flip-hint-icon">✦</text>
|
||||
</view>
|
||||
|
||||
<!-- 解读区域 -->
|
||||
<view class="interpretation-area" wx:if="{{revealedCount === selectedSpread.cardCount}}" animation="{{infoAnimation}}">
|
||||
<!-- 加载中 -->
|
||||
<view class="loading-ai" wx:if="{{isAiLoading}}">
|
||||
<text class="loading-dot">···</text>
|
||||
<text>{{aiLoadingText}}</text>
|
||||
|
|
@ -243,6 +253,20 @@
|
|||
<text class="content">{{aiResult.advice}}</text>
|
||||
</view>
|
||||
|
||||
<view class="ai-warning">
|
||||
<text>⚠ 塔罗映照潜意识的微光,最终的选择始终在你手中。</text>
|
||||
</view>
|
||||
|
||||
<!-- 🌟 新增:分享引导模块 -->
|
||||
<view class="share-guidance">
|
||||
<view class="guidance-text">
|
||||
<text>“{{randomGuidanceText}}”</text>
|
||||
</view>
|
||||
<button class="share-btn" open-type="share">
|
||||
<text>分享这次塔罗</text>
|
||||
</button>
|
||||
</view>
|
||||
|
||||
<button class="reset-btn" bindtap="resetSpread">重新开启</button>
|
||||
</view>
|
||||
</view>
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -5,6 +5,10 @@ Page({
|
|||
article: null
|
||||
},
|
||||
|
||||
handleBack() {
|
||||
wx.navigateBack({ delta: 1 });
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
const id = options.id;
|
||||
const article = getArticleById(id);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
<view class="container" wx:if="{{article}}">
|
||||
<view class="nav-back" bindtap="handleBack">
|
||||
<text class="back-icon">⇠</text>
|
||||
<text class="back-text">返回</text>
|
||||
</view>
|
||||
<view class="article-header">
|
||||
<text class="article-title">{{article.title}}</text>
|
||||
<text class="article-summary">{{article.summary}}</text>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,32 @@
|
|||
.container {
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
||||
padding: 40rpx 30rpx 120rpx;
|
||||
padding: 140rpx 30rpx 120rpx;
|
||||
}
|
||||
|
||||
/* 自定义返回按钮样式 */
|
||||
.nav-back {
|
||||
position: absolute;
|
||||
top: 40rpx;
|
||||
left: 30rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
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;
|
||||
}
|
||||
|
||||
.article-header {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,13 @@ Page({
|
|||
});
|
||||
},
|
||||
|
||||
// 返回上一页
|
||||
handleBack() {
|
||||
wx.navigateBack({
|
||||
delta: 1
|
||||
});
|
||||
},
|
||||
|
||||
// 点击分类卡片
|
||||
goToList(e) {
|
||||
const category = e.currentTarget.dataset.category;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
<view class="container">
|
||||
<view class="nav-back" bindtap="handleBack">
|
||||
<text class="back-icon">⇠</text>
|
||||
<text class="back-text">返回</text>
|
||||
</view>
|
||||
<view class="header">
|
||||
<text class="page-title">塔罗知识</text>
|
||||
</view>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,32 @@
|
|||
.container {
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
||||
padding: 40rpx 30rpx;
|
||||
padding: 140rpx 30rpx;
|
||||
}
|
||||
|
||||
/* 自定义返回按钮样式 */
|
||||
.nav-back {
|
||||
position: absolute;
|
||||
top: 40rpx;
|
||||
left: 30rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
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;
|
||||
}
|
||||
|
||||
.header {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,10 @@ Page({
|
|||
articles: []
|
||||
},
|
||||
|
||||
handleBack() {
|
||||
wx.navigateBack({ delta: 1 });
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
const category = options.category || 'beginner';
|
||||
const categories = getCategories();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
<view class="container">
|
||||
<view class="nav-back" bindtap="handleBack">
|
||||
<text class="back-icon">⇠</text>
|
||||
<text class="back-text">返回</text>
|
||||
</view>
|
||||
<view class="header">
|
||||
<text class="page-title">{{categoryName}}</text>
|
||||
</view>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,32 @@
|
|||
.container {
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
||||
padding: 40rpx 30rpx;
|
||||
padding: 140rpx 30rpx;
|
||||
}
|
||||
|
||||
/* 自定义返回按钮样式 */
|
||||
.nav-back {
|
||||
position: absolute;
|
||||
top: 40rpx;
|
||||
left: 30rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
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;
|
||||
}
|
||||
|
||||
.header {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
Page({
|
||||
data: {
|
||||
},
|
||||
onLoad: function (options) {
|
||||
}
|
||||
})
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"navigationBarTitleText": "我的"
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
<view class="container">
|
||||
<text>Mine Page</text>
|
||||
</view>
|
||||
|
|
@ -0,0 +1 @@
|
|||
/* pages/mine/mine.wxss */
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
Page({
|
||||
data: {
|
||||
show: false
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
setTimeout(() => {
|
||||
this.setData({ show: true })
|
||||
}, 100)
|
||||
},
|
||||
|
||||
handleAgree() {
|
||||
wx.setStorageSync('privacyAgreed', true)
|
||||
|
||||
wx.reLaunch({
|
||||
url: '/pages/home/home'
|
||||
})
|
||||
}
|
||||
})
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"navigationBarTitleText": "隐私保护提示",
|
||||
"navigationStyle": "default"
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<view class="overlay">
|
||||
<view class="modal {{show ? 'modal-show' : ''}}">
|
||||
|
||||
<view class="title">🔮 神秘仪式开启前</view>
|
||||
|
||||
<scroll-view scroll-y class="content">
|
||||
<view class="text">
|
||||
<view>在揭开塔罗的神秘面纱之前,</view>
|
||||
<view>请您阅读并同意《隐私政策》。</view>
|
||||
<view style="height: 20rpx;"></view>
|
||||
<view>我们仅在必要范围内收集信息,</view>
|
||||
<view>用于生成解读指引与积分功能。</view>
|
||||
</view>
|
||||
|
||||
<navigator url="/pages/privacy/privacy" class="link">
|
||||
查看完整《隐私政策》
|
||||
</navigator>
|
||||
</scroll-view>
|
||||
|
||||
<button class="agree-btn" bindtap="handleAgree">
|
||||
✨ 同意并开启指引
|
||||
</button>
|
||||
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
/* 整体遮罩背景 */
|
||||
page {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
background: radial-gradient(circle at top, #1b1b3a, #0d0d1f);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* 弹窗卡片 */
|
||||
.modal {
|
||||
width: 75%;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
backdrop-filter: blur(20rpx);
|
||||
border-radius: 30rpx;
|
||||
padding: 50rpx 40rpx;
|
||||
opacity: 0;
|
||||
transform: translateY(40rpx);
|
||||
transition: all 0.6s cubic-bezier(0.19, 1, 0.22, 1);
|
||||
box-shadow: 0 0 40rpx rgba(122, 92, 255, 0.2);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
/* 动画显示 */
|
||||
.modal-show {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
/* 标题 */
|
||||
.title {
|
||||
font-size: 38rpx;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
text-align: center;
|
||||
margin-bottom: 40rpx;
|
||||
text-shadow: 0 2rpx 10rpx rgba(122, 92, 255, 0.5);
|
||||
}
|
||||
|
||||
/* 内容区域 */
|
||||
.content {
|
||||
height: 300rpx;
|
||||
margin-bottom: 50rpx;
|
||||
}
|
||||
|
||||
/* 文本 */
|
||||
.text {
|
||||
font-size: 28rpx;
|
||||
color: #ddddff;
|
||||
line-height: 1.8;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 链接 */
|
||||
.link {
|
||||
color: #b499ff;
|
||||
margin-top: 30rpx;
|
||||
display: block;
|
||||
text-align: center;
|
||||
text-decoration: underline;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
/* 按钮 */
|
||||
.agree-btn {
|
||||
background: linear-gradient(135deg, #7a5cff, #b499ff);
|
||||
color: #ffffff;
|
||||
border-radius: 60rpx;
|
||||
font-size: 32rpx;
|
||||
padding: 10rpx 0;
|
||||
box-shadow: 0 8rpx 30rpx rgba(122, 92, 255, 0.4);
|
||||
font-weight: bold;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.agree-btn:active {
|
||||
transform: scale(0.96);
|
||||
box-shadow: 0 4rpx 15rpx rgba(122, 92, 255, 0.4);
|
||||
}
|
||||
|
|
@ -5,135 +5,190 @@
|
|||
</view>
|
||||
|
||||
<scroll-view class="privacy-content" scroll-y>
|
||||
|
||||
<view class="section">
|
||||
<text class="section-title">引言</text>
|
||||
<text class="section-text">欢迎使用我们的塔罗占卜小程序。我们非常重视您的隐私保护和个人信息安全。本隐私政策将帮助您了解我们如何收集、使用、存储和保护您的信息。</text>
|
||||
<text class="section-text">
|
||||
欢迎使用我们的塔罗占卜小程序。本小程序由【XXX】运营。
|
||||
我们非常重视您的隐私保护和个人信息安全。
|
||||
在您使用本小程序前,请您仔细阅读并充分理解本隐私政策。
|
||||
当您点击“同意”并开始使用本小程序时,即表示您已阅读、
|
||||
理解并同意本政策的全部内容。
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<view class="section">
|
||||
<text class="section-title">一、信息收集</text>
|
||||
<text class="section-text">为了向您提供更好的服务,我们可能会收集以下信息:</text>
|
||||
<text class="section-title">一、我们收集的信息</text>
|
||||
<text class="section-text">
|
||||
为向您提供塔罗解读服务,我们可能会收集以下信息:
|
||||
</text>
|
||||
|
||||
<view class="list-item">
|
||||
<text class="bullet">•</text>
|
||||
<text class="list-text">您输入的占卜问题和相关内容</text>
|
||||
<text class="list-text">您输入的占卜问题内容</text>
|
||||
</view>
|
||||
|
||||
<view class="list-item">
|
||||
<text class="bullet">•</text>
|
||||
<text class="list-text">积分记录和使用情况</text>
|
||||
<text class="list-text">积分记录与使用情况</text>
|
||||
</view>
|
||||
|
||||
<view class="list-item">
|
||||
<text class="bullet">•</text>
|
||||
<text class="list-text">广告观看记录(仅用于积分奖励统计)</text>
|
||||
<text class="list-text">广告观看记录(仅用于积分奖励统计)</text>
|
||||
</view>
|
||||
|
||||
<view class="list-item">
|
||||
<text class="bullet">•</text>
|
||||
<text class="list-text">设备信息(用于优化服务体验)</text>
|
||||
<text class="list-text">设备基础信息(用于保障运行及优化体验)</text>
|
||||
</view>
|
||||
<text class="section-note">注: 所有信息仅存储在您的设备本地,我们不会上传到服务器。</text>
|
||||
|
||||
<text class="section-note">
|
||||
注:除使用第三方AI服务生成解读外,
|
||||
所有数据默认仅存储在您的设备本地,
|
||||
我们不会主动上传至自有服务器。
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<view class="section">
|
||||
<text class="section-title">二、信息使用</text>
|
||||
<text class="section-text">我们收集的信息将用于:</text>
|
||||
<text class="section-title">二、信息的使用目的</text>
|
||||
|
||||
<view class="list-item">
|
||||
<text class="bullet">•</text>
|
||||
<text class="list-text">提供塔罗占卜解读服务</text>
|
||||
<text class="list-text">为您生成塔罗牌解读内容</text>
|
||||
</view>
|
||||
|
||||
<view class="list-item">
|
||||
<text class="bullet">•</text>
|
||||
<text class="list-text">管理积分系统和奖励机制</text>
|
||||
<text class="list-text">管理积分系统与奖励机制</text>
|
||||
</view>
|
||||
|
||||
<view class="list-item">
|
||||
<text class="bullet">•</text>
|
||||
<text class="list-text">改进和优化用户体验</text>
|
||||
<text class="list-text">优化产品功能与用户体验</text>
|
||||
</view>
|
||||
|
||||
<view class="list-item">
|
||||
<text class="bullet">•</text>
|
||||
<text class="list-text">提供个性化的内容推荐</text>
|
||||
<text class="list-text">实现广告奖励功能</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="section">
|
||||
<text class="section-title">三、信息存储</text>
|
||||
<text class="section-text">您的所有数据均通过微信小程序的本地存储功能保存在您的设备上。我们不会将您的个人信息上传至任何外部服务器。数据的保存和删除完全由您控制。</text>
|
||||
<text class="section-title">三、信息存储与删除</text>
|
||||
<text class="section-text">
|
||||
您的塔罗问题、积分记录等数据默认通过微信小程序
|
||||
本地存储功能保存在您的设备中。
|
||||
您可以通过删除小程序清除全部本地数据。
|
||||
我们不会在服务器长期保存您的个人历史记录。
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<view class="section">
|
||||
<text class="section-title">四、第三方服务</text>
|
||||
<text class="section-text">为了提供完整的服务功能,我们使用了以下第三方服务:</text>
|
||||
<text class="section-title">四、第三方服务说明</text>
|
||||
<text class="section-text">
|
||||
为实现部分功能,我们可能会使用以下第三方服务:
|
||||
</text>
|
||||
|
||||
<view class="list-item">
|
||||
<text class="bullet">•</text>
|
||||
<text class="list-text">DeepSeek AI: 用于生成塔罗牌解读内容。您的问题将被发送至DeepSeek API进行处理,但不会包含任何可识别您身份的信息。</text>
|
||||
<text class="list-text">
|
||||
DeepSeek AI 服务:用于生成塔罗解读内容。
|
||||
您输入的占卜问题文本将发送至 DeepSeek API 接口进行处理,
|
||||
仅用于生成解读结果,不会包含您的昵称、头像、
|
||||
手机号等可识别身份信息。
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<view class="list-item">
|
||||
<text class="bullet">•</text>
|
||||
<text class="list-text">微信激励视频广告: 用于积分奖励功能。广告服务由微信官方提供,遵循微信隐私政策。</text>
|
||||
<text class="list-text">
|
||||
微信激励视频广告:用于积分奖励功能。
|
||||
广告服务由微信官方提供,数据处理遵循微信平台相关隐私政策。
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="section">
|
||||
<text class="section-title">五、用户权利</text>
|
||||
<text class="section-text">您拥有以下权利:</text>
|
||||
|
||||
<view class="list-item">
|
||||
<text class="bullet">•</text>
|
||||
<text class="list-text">随时查看和管理您的积分记录</text>
|
||||
<text class="list-text">查看和管理您的积分记录</text>
|
||||
</view>
|
||||
|
||||
<view class="list-item">
|
||||
<text class="bullet">•</text>
|
||||
<text class="list-text">通过删除小程序清除所有本地数据</text>
|
||||
<text class="list-text">选择是否观看广告获取奖励</text>
|
||||
</view>
|
||||
|
||||
<view class="list-item">
|
||||
<text class="bullet">•</text>
|
||||
<text class="list-text">选择是否观看广告获取积分</text>
|
||||
<text class="list-text">删除小程序以清除本地数据</text>
|
||||
</view>
|
||||
|
||||
<view class="list-item">
|
||||
<text class="bullet">•</text>
|
||||
<text class="list-text">随时停止使用我们的服务</text>
|
||||
<text class="list-text">随时停止使用本服务</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="section">
|
||||
<text class="section-title">六、信息安全</text>
|
||||
<text class="section-text">我们采取合理的安全措施保护您的信息:</text>
|
||||
|
||||
<view class="list-item">
|
||||
<text class="bullet">•</text>
|
||||
<text class="list-text">使用HTTPS加密传输与AI服务的通信</text>
|
||||
<text class="list-text">与AI接口通信采用HTTPS加密传输</text>
|
||||
</view>
|
||||
|
||||
<view class="list-item">
|
||||
<text class="bullet">•</text>
|
||||
<text class="list-text">不收集任何可直接识别您身份的信息</text>
|
||||
<text class="list-text">不收集敏感身份识别信息</text>
|
||||
</view>
|
||||
|
||||
<view class="list-item">
|
||||
<text class="bullet">•</text>
|
||||
<text class="list-text">遵循微信小程序平台的安全规范</text>
|
||||
<text class="list-text">遵循微信小程序平台安全规范</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="section">
|
||||
<text class="section-title">七、未成年人保护</text>
|
||||
<text class="section-text">我们非常重视未成年人的隐私保护。如果您是未成年人,请在监护人的陪同下使用本小程序。</text>
|
||||
<text class="section-text">
|
||||
如您为未成年人,请在监护人指导下阅读本政策并使用本服务。
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<view class="section">
|
||||
<text class="section-title">八、政策更新</text>
|
||||
<text class="section-text">我们可能会不定期更新本隐私政策。更新后的政策将在小程序内公布,并在页面顶部标注更新日期。继续使用我们的服务即表示您同意更新后的隐私政策。</text>
|
||||
<text class="section-title">八、免责声明</text>
|
||||
<text class="section-text">
|
||||
本小程序提供的塔罗解读内容仅供娱乐和参考,
|
||||
不构成任何现实决策建议或结果承诺。
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<view class="section">
|
||||
<text class="section-title">九、联系我们</text>
|
||||
<text class="section-text">如果您对本隐私政策有任何疑问、意见或建议,欢迎通过以下方式联系我们:</text>
|
||||
<text class="section-text">
|
||||
本小程序运营者:XXX
|
||||
</text>
|
||||
|
||||
<view class="list-item">
|
||||
<text class="bullet">•</text>
|
||||
<text class="list-text">在小程序内提供反馈</text>
|
||||
<text class="list-text">联系邮箱:xxx@xxx.com</text>
|
||||
</view>
|
||||
|
||||
<view class="list-item">
|
||||
<text class="bullet">•</text>
|
||||
<text class="list-text">通过微信公众平台留言</text>
|
||||
<text class="list-text">反馈方式:小程序内反馈入口</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="footer">
|
||||
<text class="footer-text">感谢您信任并使用我们的服务!</text>
|
||||
<text class="footer-text">
|
||||
感谢您信任并使用我们的服务!
|
||||
</text>
|
||||
</view>
|
||||
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@
|
|||
.privacy-content {
|
||||
height: calc(100vh - 280rpx);
|
||||
padding: 0 30rpx;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.section {
|
||||
|
|
@ -40,6 +42,8 @@
|
|||
padding: 32rpx;
|
||||
margin-bottom: 24rpx;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
|
||||
Page({
|
||||
clearCache() {
|
||||
wx.showModal({
|
||||
title: '确认清除',
|
||||
content: '将清除所有本地数据,包括积分记录,是否继续?',
|
||||
success(res) {
|
||||
if (res.confirm) {
|
||||
try {
|
||||
wx.clearStorageSync()
|
||||
wx.showToast({
|
||||
title: '已清除',
|
||||
icon: 'none'
|
||||
})
|
||||
// Consider re-initializing necessary keys if needed, or just let app.js handle it on next launch
|
||||
} catch (e) {
|
||||
wx.showToast({
|
||||
title: '清除失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
revokePrivacy() {
|
||||
wx.showModal({
|
||||
title: '撤销隐私授权',
|
||||
content: '撤销后将重置隐私协议状态,下次启动需重新同意,是否继续?',
|
||||
success(res) {
|
||||
if (res.confirm) {
|
||||
try {
|
||||
wx.removeStorageSync('privacyAgreed')
|
||||
wx.showToast({
|
||||
title: '已撤销',
|
||||
icon: 'none'
|
||||
})
|
||||
// 延迟跳转,确保提示显示
|
||||
setTimeout(() => {
|
||||
wx.reLaunch({
|
||||
url: '/pages/privacy-agree/privacy-agree'
|
||||
})
|
||||
}, 1500)
|
||||
} catch (e) {
|
||||
wx.showToast({
|
||||
title: '操作失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"navigationBarTitleText": "设置",
|
||||
"navigationStyle": "default",
|
||||
"navigationBarBackgroundColor": "#0d0d1f",
|
||||
"navigationBarTextStyle": "white"
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
|
||||
<view class="settings-container">
|
||||
|
||||
<!-- 功能区 -->
|
||||
<view class="group">
|
||||
<view class="group-title">账户与数据</view>
|
||||
|
||||
<view class="item" bindtap="clearCache">
|
||||
<text>清除本地数据</text>
|
||||
<text>></text>
|
||||
</view>
|
||||
|
||||
<view class="item" bindtap="revokePrivacy">
|
||||
<text>撤销隐私授权</text>
|
||||
<text>></text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 协议区 -->
|
||||
<view class="group">
|
||||
<view class="group-title">协议与说明</view>
|
||||
|
||||
<navigator url="/pages/privacy/privacy" class="item">
|
||||
<text>隐私政策</text>
|
||||
<text>></text>
|
||||
</navigator>
|
||||
|
||||
<navigator url="/pages/user-agreement/user-agreement" class="item">
|
||||
<text>用户协议</text>
|
||||
<text>></text>
|
||||
</navigator>
|
||||
|
||||
<view class="item">
|
||||
<text>当前版本</text>
|
||||
<text>v1.0.0</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
|
||||
.settings-container {
|
||||
padding: 30rpx;
|
||||
background: #0d0d1f;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.group {
|
||||
margin-bottom: 40rpx;
|
||||
background: rgba(255,255,255,0.06);
|
||||
border-radius: 20rpx;
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
|
||||
.group-title {
|
||||
font-size: 24rpx;
|
||||
color: #b499ff;
|
||||
margin-bottom: 10rpx;
|
||||
padding-left: 10rpx;
|
||||
padding-top: 20rpx;
|
||||
}
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 30rpx 10rpx;
|
||||
border-bottom: 1rpx solid rgba(255,255,255,0.05);
|
||||
color: #ffffff;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
Page({
|
||||
data: {
|
||||
|
||||
},
|
||||
onLoad(options) {
|
||||
|
||||
}
|
||||
})
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"navigationBarTitleText": "用户协议"
|
||||
}
|
||||
|
|
@ -0,0 +1,156 @@
|
|||
<view class="agreement-container">
|
||||
<view class="agreement-header">
|
||||
<text class="agreement-title">用户服务协议</text>
|
||||
<text class="update-date">更新日期:2026年2月14日</text>
|
||||
</view>
|
||||
|
||||
<scroll-view class="agreement-content" scroll-y>
|
||||
|
||||
<view class="section">
|
||||
<text class="section-title">一、协议说明</text>
|
||||
<text class="section-text">
|
||||
欢迎使用本塔罗占卜小程序。本小程序由【XXX运营者名称】开发和运营。
|
||||
在您使用本服务前,请您仔细阅读本协议全部内容。
|
||||
当您点击“同意”或实际使用本服务,即表示您已阅读、
|
||||
理解并同意受本协议约束。
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<view class="section">
|
||||
<text class="section-title">二、服务内容</text>
|
||||
<text class="section-text">
|
||||
本小程序为娱乐性质的塔罗解读服务,
|
||||
通过人工智能技术生成塔罗牌解读内容。
|
||||
所有解读内容仅供娱乐和参考,
|
||||
不构成任何现实决策建议、投资建议、
|
||||
医疗建议或法律建议。
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<view class="section">
|
||||
<text class="section-title">三、用户行为规范</text>
|
||||
|
||||
<view class="list-item">
|
||||
<text class="bullet">•</text>
|
||||
<text class="list-text">不得利用本服务发布违法、违规内容</text>
|
||||
</view>
|
||||
|
||||
<view class="list-item">
|
||||
<text class="bullet">•</text>
|
||||
<text class="list-text">不得利用本服务进行欺诈或恶意行为</text>
|
||||
</view>
|
||||
|
||||
<view class="list-item">
|
||||
<text class="bullet">•</text>
|
||||
<text class="list-text">不得尝试破坏系统安全或恶意攻击接口</text>
|
||||
</view>
|
||||
|
||||
<view class="list-item">
|
||||
<text class="bullet">•</text>
|
||||
<text class="list-text">不得批量抓取或复制生成内容用于商业用途</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="section">
|
||||
<text class="section-title">四、AI内容免责声明(重要提示)</text>
|
||||
|
||||
<text class="section-text">
|
||||
本小程序提供的塔罗解读内容由人工智能系统自动生成,
|
||||
基于算法模型对塔罗象征意义进行语言表达,
|
||||
不代表真实占卜师的专业判断。
|
||||
</text>
|
||||
|
||||
<text class="section-text">
|
||||
所有解读结果仅供娱乐和文化参考,
|
||||
不构成任何形式的现实建议,
|
||||
包括但不限于投资建议、医疗建议、
|
||||
心理咨询建议、法律建议或情感决策建议。
|
||||
</text>
|
||||
|
||||
<text class="section-text">
|
||||
人工智能生成内容可能存在理解偏差、
|
||||
信息不完整或表达不准确的情况,
|
||||
我们不对解读结果的准确性、完整性、
|
||||
时效性或适用性作任何保证。
|
||||
</text>
|
||||
|
||||
<text class="section-text">
|
||||
因依赖或使用解读内容所产生的任何直接或间接后果,
|
||||
包括经济损失、决策失误或其他损害,
|
||||
均由用户自行承担责任。
|
||||
</text>
|
||||
|
||||
<text class="section-text">
|
||||
如您对生成内容存在疑问,
|
||||
请理性判断并结合现实情况作出决策。
|
||||
</text>
|
||||
|
||||
<view class="section-note">
|
||||
本服务不提供预测未来、保证结果或改变现实结果的能力。
|
||||
塔罗解读仅作为象征性表达与自我思考工具。
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="section">
|
||||
<text class="section-title">五、积分与广告机制</text>
|
||||
<text class="section-text">
|
||||
本小程序采用积分机制提供服务。
|
||||
用户可通过观看微信官方激励视频广告获得积分奖励。
|
||||
积分仅用于本小程序内使用,不可兑换现金或转让。
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<view class="section">
|
||||
<text class="section-title">六、知识产权</text>
|
||||
<text class="section-text">
|
||||
本小程序的界面设计、功能结构、代码、品牌标识等
|
||||
均归【XXX运营者名称】所有。
|
||||
未经许可,不得擅自复制、传播或用于商业用途。
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<view class="section">
|
||||
<text class="section-title">七、责任限制</text>
|
||||
<text class="section-text">
|
||||
因使用本服务所产生的任何直接或间接损失,
|
||||
包括但不限于决策失误、投资损失等,
|
||||
运营方不承担任何法律责任。
|
||||
如因不可抗力或系统维护导致服务中断,
|
||||
我们不承担由此产生的损失。
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<view class="section">
|
||||
<text class="section-title">八、协议修改</text>
|
||||
<text class="section-text">
|
||||
我们有权根据法律法规或运营需要对本协议进行修改。
|
||||
修改后的协议将在小程序内公布。
|
||||
您继续使用本服务即视为接受更新后的协议内容。
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<view class="section">
|
||||
<text class="section-title">九、联系我们</text>
|
||||
<text class="section-text">
|
||||
如您对本协议有任何疑问或建议,请联系:
|
||||
</text>
|
||||
|
||||
<view class="list-item">
|
||||
<text class="bullet">•</text>
|
||||
<text class="list-text">运营者名称:【XXX运营者名称】</text>
|
||||
</view>
|
||||
|
||||
<view class="list-item">
|
||||
<text class="bullet">•</text>
|
||||
<text class="list-text">联系邮箱:【xxx@xxx.com】</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="footer">
|
||||
<text class="footer-text">
|
||||
感谢您理性使用本服务,愿塔罗为您带来启发。
|
||||
</text>
|
||||
</view>
|
||||
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
/* 协议页面样式 - 优化阅读体验 */
|
||||
|
||||
|
||||
.agreement-container {
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
padding-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.agreement-header {
|
||||
text-align: center;
|
||||
padding: 60rpx 40rpx;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
margin-bottom: 20rpx;
|
||||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
|
||||
margin: 40rpx 30rpx 30rpx;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
.agreement-title {
|
||||
display: block;
|
||||
font-size: 40rpx;
|
||||
font-weight: bold;
|
||||
color: #2d3748;
|
||||
margin-bottom: 16rpx;
|
||||
letter-spacing: 2rpx;
|
||||
}
|
||||
|
||||
.update-date {
|
||||
display: block;
|
||||
font-size: 24rpx;
|
||||
color: #a0aec0;
|
||||
}
|
||||
|
||||
.agreement-content {
|
||||
height: calc(100vh - 240rpx);
|
||||
padding: 0 30rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.section {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border-radius: 16rpx;
|
||||
padding: 40rpx;
|
||||
margin-bottom: 30rpx;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.03);
|
||||
}
|
||||
|
||||
.section-title {
|
||||
display: block;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #2d3748;
|
||||
margin-bottom: 24rpx;
|
||||
position: relative;
|
||||
padding-left: 20rpx;
|
||||
}
|
||||
|
||||
.section-title::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 8rpx;
|
||||
bottom: 8rpx;
|
||||
width: 8rpx;
|
||||
background: #667eea;
|
||||
border-radius: 4rpx;
|
||||
}
|
||||
|
||||
.section-text {
|
||||
display: block;
|
||||
font-size: 28rpx;
|
||||
color: #4a5568;
|
||||
line-height: 1.8;
|
||||
text-align: justify;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.list-item {
|
||||
display: flex;
|
||||
margin-bottom: 16rpx;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.bullet {
|
||||
color: #667eea;
|
||||
font-size: 24rpx; /* Smaller bullet for subtleness */
|
||||
margin-right: 16rpx;
|
||||
margin-top: 6rpx; /* Align with text */
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.list-text {
|
||||
flex: 1;
|
||||
font-size: 28rpx;
|
||||
color: #4a5568;
|
||||
line-height: 1.8;
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
.footer {
|
||||
text-align: center;
|
||||
padding: 60rpx 40rpx;
|
||||
margin-top: 40rpx;
|
||||
}
|
||||
|
||||
.footer-text {
|
||||
display: block;
|
||||
font-size: 26rpx;
|
||||
color: #a0aec0;
|
||||
letter-spacing: 1rpx;
|
||||
}
|
||||
|
|
@ -1,326 +1,78 @@
|
|||
const { getZodiacList } = require('../../utils/zodiacData');
|
||||
const { fetchZodiacFortune, fetchDailyOverview } = require('../../utils/zodiacAI');
|
||||
const { checkShareReward } = require('../../utils/pointsManager');
|
||||
|
||||
Page({
|
||||
data: {
|
||||
selectedZodiac: null,
|
||||
currentFortune: null, // { keyword, today, lucky, notice, match, action, summary }
|
||||
isMatchExpanded: false, // 控制关系提示的展开状态
|
||||
currentFortune: null,
|
||||
isLoading: false,
|
||||
isMatchExpanded: false,
|
||||
zodiacList: [],
|
||||
dailyOverview: null // 新增:星象概览
|
||||
},
|
||||
|
||||
// 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: '情感流动充沛,在关怀他人的同时也需照顾好自己。'
|
||||
}
|
||||
]
|
||||
onLoad: async function () {
|
||||
this.setData({
|
||||
zodiacList: getZodiacList()
|
||||
});
|
||||
|
||||
// 加载星象概览
|
||||
const overview = await fetchDailyOverview();
|
||||
this.setData({ dailyOverview: overview });
|
||||
},
|
||||
|
||||
// 返回上一页或列表
|
||||
goBack: function () {
|
||||
console.log('[Zodiac] goBack triggered');
|
||||
if (this.data.selectedZodiac) {
|
||||
// 如果在详情页,返回列表
|
||||
this.setData({
|
||||
selectedZodiac: null
|
||||
});
|
||||
} else {
|
||||
// 如果在列表页,尝试返回上一页
|
||||
const pages = getCurrentPages();
|
||||
if (pages.length > 1) {
|
||||
wx.navigateBack({
|
||||
delta: 1
|
||||
});
|
||||
} else {
|
||||
// 如果是直接打开(如分享进入),则返回首页
|
||||
wx.reLaunch({
|
||||
url: '/pages/home/home'
|
||||
});
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
// 选择星座
|
||||
selectZodiac: function (e) {
|
||||
selectZodiac: async 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];
|
||||
|
||||
// 1. 设置加载状态 & 选中星座
|
||||
this.setData({
|
||||
selectedZodiac: zodiac,
|
||||
currentFortune: fortuneData,
|
||||
isMatchExpanded: false // 重置展开状态
|
||||
currentFortune: null, // 清空旧数据
|
||||
isLoading: true, // 开启 Loading
|
||||
isMatchExpanded: false
|
||||
});
|
||||
|
||||
// 2. 获取运势 (AI 或 缓存)
|
||||
try {
|
||||
const fortuneData = await fetchZodiacFortune(zodiac.name);
|
||||
|
||||
// 3. 渲染数据
|
||||
this.setData({
|
||||
currentFortune: fortuneData,
|
||||
isLoading: false
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('获取运势失败', err);
|
||||
this.setData({ isLoading: false });
|
||||
wx.showToast({ title: '网络小差,请重试', icon: 'none' });
|
||||
}
|
||||
},
|
||||
|
||||
// 切换关系提示展开状态
|
||||
|
|
@ -335,7 +87,48 @@ Page({
|
|||
this.setData({
|
||||
selectedZodiac: null,
|
||||
currentFortune: null,
|
||||
isLoading: false,
|
||||
isMatchExpanded: false // 重置展开状态
|
||||
});
|
||||
},
|
||||
|
||||
// 跳转回塔罗页面
|
||||
goToTarot: function () {
|
||||
wx.navigateTo({
|
||||
url: '/pages/index/index'
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 用户点击右上角分享
|
||||
*/
|
||||
onShareAppMessage: function () {
|
||||
const titles = [
|
||||
"我的今日星象运势竟然是...",
|
||||
"宇宙刚刚给我发了一条星语指引 ✨",
|
||||
"准到离谱!快来看看你的今日星象",
|
||||
"这是我此刻的星象能量状态 🌟",
|
||||
"为你抽取了一份宇宙星象指引"
|
||||
];
|
||||
const randomTitle = titles[Math.floor(Math.random() * titles.length)];
|
||||
|
||||
// 尝试发放分享奖励
|
||||
const rewardResult = checkShareReward();
|
||||
if (rewardResult.success) {
|
||||
wx.showToast({
|
||||
title: rewardResult.message,
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
}
|
||||
|
||||
// 模拟生成 fromUserId
|
||||
const mockUserId = 'user_' + Math.random().toString(36).substr(2, 9);
|
||||
|
||||
return {
|
||||
title: randomTitle,
|
||||
path: `/pages/zodiac/index?fromUserId=${mockUserId}`,
|
||||
imageUrl: '/images/share-cover.png' // 使用统一的分享图或后续专用图
|
||||
};
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,7 +1,24 @@
|
|||
<view class="container">
|
||||
<!-- 自定义返回按钮 (仿塔罗页风格) -->
|
||||
<view class="nav-back" bindtap="goBack">
|
||||
<text class="back-icon">⇠</text>
|
||||
<text class="back-text">返回</text>
|
||||
</view>
|
||||
|
||||
<!-- 状态1: 星座列表 -->
|
||||
<view class="zodiac-grid" wx:if="{{!selectedZodiac}}">
|
||||
|
||||
<!-- 今日星象概览 (新增) -->
|
||||
<view class="daily-overview" wx:if="{{dailyOverview}}">
|
||||
<view class="overview-title">今日星象概览</view>
|
||||
<view class="overview-content">{{dailyOverview.content}}</view>
|
||||
<view class="overview-tags">
|
||||
<block wx:for="{{dailyOverview.tags}}" wx:key="*this">
|
||||
<text class="tag-item">{{item}}</text>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="header-tip">选择你的星座</view>
|
||||
<view class="grid-container">
|
||||
<block wx:for="{{zodiacList}}" wx:key="name">
|
||||
|
|
@ -14,64 +31,117 @@
|
|||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 状态2: 运势展示 -->
|
||||
<view class="result-container" wx:if="{{selectedZodiac}}">
|
||||
<!-- 状态2: 加载中 -->
|
||||
<view class="loading-container" wx:if="{{selectedZodiac && isLoading}}">
|
||||
<view class="loading-symbol">{{selectedZodiac.symbol}}</view>
|
||||
<view class="loading-text">正在接收宇宙讯息...</view>
|
||||
</view>
|
||||
|
||||
<!-- 状态3: 运势展示 -->
|
||||
<view class="result-container" wx:elif="{{selectedZodiac && !isLoading}}">
|
||||
<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">
|
||||
<!-- 1. 关键词与核心能量 -->
|
||||
<view class="keyword-section">
|
||||
<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 class="core-energy">
|
||||
<text class="energy-label">🌙 今日核心能量</text>
|
||||
<text class="energy-text">{{currentFortune.core_energy}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="divider"></view>
|
||||
|
||||
<!-- 2. 运势指数 -->
|
||||
<view class="section-block">
|
||||
<text class="section-title">📊 运势指数</text>
|
||||
<view class="rating-grid">
|
||||
<view class="rating-item">
|
||||
<text class="rating-label">综合</text>
|
||||
<view class="stars">
|
||||
<block wx:for="{{[1,2,3,4,5]}}" wx:key="*this">
|
||||
<text class="star">{{item <= currentFortune.ratings.overall ? '★' : '☆'}}</text>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
<view class="rating-item">
|
||||
<text class="rating-label">爱情</text>
|
||||
<view class="stars">
|
||||
<block wx:for="{{[1,2,3,4,5]}}" wx:key="*this">
|
||||
<text class="star">{{item <= currentFortune.ratings.love ? '★' : '☆'}}</text>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
<view class="rating-item">
|
||||
<text class="rating-label">事业</text>
|
||||
<view class="stars">
|
||||
<block wx:for="{{[1,2,3,4,5]}}" wx:key="*this">
|
||||
<text class="star">{{item <= currentFortune.ratings.career ? '★' : '☆'}}</text>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
<view class="rating-item">
|
||||
<text class="rating-label">财运</text>
|
||||
<view class="stars">
|
||||
<block wx:for="{{[1,2,3,4,5]}}" wx:key="*this">
|
||||
<text class="star">{{item <= currentFortune.ratings.wealth ? '★' : '☆'}}</text>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 3. 详细运势 -->
|
||||
<view class="section-block">
|
||||
<text class="section-title">❤️ 感情运势</text>
|
||||
<text class="section-content">{{currentFortune.fortune_text.love}}</text>
|
||||
</view>
|
||||
|
||||
<view class="section-block">
|
||||
<text class="section-title">💼 事业运势</text>
|
||||
<text class="section-content">{{currentFortune.fortune_text.career}}</text>
|
||||
</view>
|
||||
|
||||
<view class="section-block">
|
||||
<text class="section-title">💰 财运提醒</text>
|
||||
<text class="section-content">{{currentFortune.fortune_text.wealth}}</text>
|
||||
</view>
|
||||
|
||||
<view class="divider"></view>
|
||||
|
||||
<!-- 4. 幸运元素 -->
|
||||
<view class="section-block">
|
||||
<text class="section-title">🎨 幸运元素</text>
|
||||
<view class="lucky-grid">
|
||||
<view class="lucky-item">颜色:<text class="highlight">{{currentFortune.lucky_elements.color}}</text></view>
|
||||
<view class="lucky-item">数字:<text class="highlight">{{currentFortune.lucky_elements.number}}</text></view>
|
||||
<view class="lucky-item">时间:<text class="highlight">{{currentFortune.lucky_elements.time}}</text></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 5. 行动建议 -->
|
||||
<view class="section-block last-section">
|
||||
<text class="section-title">🌿 今日行动建议</text>
|
||||
<text class="section-content">{{currentFortune.action}}</text>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
||||
<!-- 总体结论 (收束模块) -->
|
||||
<view class="summary-text">{{currentFortune.summary}}</view>
|
||||
|
||||
<view class="action-area" bindtap="backToList">
|
||||
<text class="back-btn">⬅ 选其他星座</text>
|
||||
<view class="action-area">
|
||||
<view class="back-btn" bindtap="backToList">⬅ 选其他星座</view>
|
||||
<button class="share-btn" open-type="share">✨ 分享今日指引</button>
|
||||
<view class="verify-btn" bindtap="goToTarot">前往塔罗验证能量</view>
|
||||
</view>
|
||||
|
||||
<view class="disclaimer">
|
||||
<text>内容仅供娱乐与参考,不构成现实决策依据</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
|
|
|||
|
|
@ -2,24 +2,119 @@
|
|||
page {
|
||||
background-color: #1a1a2e;
|
||||
height: 100%;
|
||||
color: #fff;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 30px 20px;
|
||||
padding: 100rpx 40rpx 60rpx; /* Increased top padding to avoid overlap */
|
||||
box-sizing: border-box;
|
||||
min-height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* 自定义返回按钮样式 (同塔罗页) */
|
||||
.nav-back {
|
||||
position: absolute;
|
||||
top: 30rpx;
|
||||
left: 30rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
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;
|
||||
}
|
||||
|
||||
/* --- 加载状态样式 --- */
|
||||
.loading-container {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding-bottom: 100rpx;
|
||||
}
|
||||
|
||||
.loading-symbol {
|
||||
font-size: 60px;
|
||||
color: #e94560;
|
||||
margin-bottom: 20px;
|
||||
animation: breathe 2s infinite alternate;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
font-size: 16px;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
@keyframes breathe {
|
||||
0% { transform: scale(0.9); opacity: 0.7; text-shadow: 0 0 10px rgba(233, 69, 96, 0.3); }
|
||||
100% { transform: scale(1.1); opacity: 1; text-shadow: 0 0 25px rgba(233, 69, 96, 0.8); }
|
||||
}
|
||||
|
||||
/* --- 列表状态样式 --- */
|
||||
.daily-overview {
|
||||
background: rgba(255, 255, 255, 0.05); /* Unified dark theme background */
|
||||
border-radius: 12px;
|
||||
padding: 15px; /* Reduced padding */
|
||||
margin-bottom: 20px; /* Reduced margin */
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
animation: slideUp 0.6s ease;
|
||||
}
|
||||
|
||||
.overview-title {
|
||||
font-size: 15px; /* SLightly smaller */
|
||||
font-weight: bold;
|
||||
color: #fff; /* White text */
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.overview-content {
|
||||
font-size: 12px;
|
||||
color: rgba(255, 255, 255, 0.8); /* Light grey text */
|
||||
line-height: 1.5;
|
||||
margin-bottom: 10px;
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
.overview-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tag-item {
|
||||
font-size: 10px;
|
||||
background-color: rgba(255, 255, 255, 0.1); /* Darker tag bg */
|
||||
color: #a29bfe; /* Light purple tag text */
|
||||
padding: 3px 8px;
|
||||
border-radius: 10px;
|
||||
font-weight: 500;
|
||||
border: 1px solid rgba(162, 155, 254, 0.3);
|
||||
}
|
||||
|
||||
.header-tip {
|
||||
font-size: 20px;
|
||||
margin-bottom: 30px;
|
||||
font-size: 16px; /* Smaller header */
|
||||
margin-bottom: 15px; /* Reduced margin */
|
||||
text-align: center;
|
||||
opacity: 0.9;
|
||||
color: #fff;
|
||||
opacity: 1;
|
||||
letter-spacing: 1px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.grid-container {
|
||||
|
|
@ -30,31 +125,35 @@ page {
|
|||
|
||||
.zodiac-item {
|
||||
width: 30%; /* 三列布局 */
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
margin-bottom: 20px;
|
||||
padding: 20px 0;
|
||||
border-radius: 12px;
|
||||
background-color: rgba(255, 255, 255, 0.08);
|
||||
margin-bottom: 12px; /* Reduced margin */
|
||||
padding: 12px 0; /* Reduced padding */
|
||||
border-radius: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.zodiac-symbol {
|
||||
font-size: 28px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 24px; /* Smaller symbol */
|
||||
margin-bottom: 4px;
|
||||
color: rgb(240, 240, 255);
|
||||
text-shadow: 0 0 10px rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.zodiac-name {
|
||||
font-size: 14px;
|
||||
opacity: 0.8;
|
||||
font-size: 12px; /* Smaller name */
|
||||
color: #fff;
|
||||
opacity: 1;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.zodiac-range {
|
||||
font-size: 10px;
|
||||
opacity: 0.5;
|
||||
margin-top: 3px;
|
||||
font-size: 9px; /* Smaller range */
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
/* --- 结果状态样式 --- */
|
||||
|
|
@ -83,7 +182,11 @@ page {
|
|||
|
||||
.today-title {
|
||||
font-size: 18px;
|
||||
opacity: 0.8;
|
||||
color: #fff;
|
||||
opacity: 1;
|
||||
font-weight: 500;
|
||||
letter-spacing: 2px;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.fortune-card {
|
||||
|
|
@ -99,84 +202,194 @@ page {
|
|||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* 0. 关键词卡片样式 */
|
||||
.keyword-card {
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
border-radius: 12px;
|
||||
padding: 15px;
|
||||
/* --- 新版结果展示样式 --- */
|
||||
.keyword-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-bottom: 25px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.keyword-label {
|
||||
font-size: 12px;
|
||||
color: #a29bfe;
|
||||
margin-bottom: 5px;
|
||||
opacity: 0.8;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.keyword-text {
|
||||
font-size: 32px; /* 进一步加大关键词字号 */
|
||||
font-size: 36px;
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
letter-spacing: 4px;
|
||||
margin-top: 5px;
|
||||
text-shadow: 0 2px 10px rgba(0,0,0,0.3);
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
/* 三段式内容的样式 */
|
||||
.section {
|
||||
.core-energy {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
padding: 10px 15px;
|
||||
border-radius: 8px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.energy-label {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
color: #fbaceb;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.energy-text {
|
||||
font-size: 14px;
|
||||
color: #fff;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.divider {
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.section-block {
|
||||
margin-bottom: 25px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
display: block;
|
||||
font-size: 15px;
|
||||
color: #e94560;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.section-content {
|
||||
font-size: 14px;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
line-height: 1.6;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
/* 评分网格 */
|
||||
.rating-grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.rating-item {
|
||||
width: 48%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
background: rgba(0,0,0,0.2);
|
||||
padding: 8px 10px;
|
||||
border-radius: 6px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.rating-label {
|
||||
font-size: 12px;
|
||||
color: rgba(255,255,255,0.7);
|
||||
}
|
||||
|
||||
.stars {
|
||||
font-size: 12px;
|
||||
color: #ffd700;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
/* 幸运元素 */
|
||||
.lucky-grid {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 25px;
|
||||
border-bottom: 1px dashed rgba(255, 255, 255, 0.1); /* 分隔线 */
|
||||
padding-bottom: 15px;
|
||||
gap: 8px;
|
||||
background: rgba(0,0,0,0.2);
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.lucky-item {
|
||||
font-size: 14px;
|
||||
color: rgba(255,255,255,0.8);
|
||||
}
|
||||
|
||||
.lucky-item .highlight {
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.last-section {
|
||||
margin-bottom: 0;
|
||||
border-bottom: none;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.section-label {
|
||||
font-size: 14px;
|
||||
color: #a29bfe; /* 浅紫色标签 */
|
||||
margin-bottom: 8px;
|
||||
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;
|
||||
}
|
||||
|
||||
.section-content {
|
||||
font-size: 14px; /* 二级信息字号调小 */
|
||||
line-height: 1.6;
|
||||
.action-area {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
margin-bottom: 30px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.verify-btn {
|
||||
background: linear-gradient(135deg, #e94560, #8b3a62);
|
||||
color: #fff;
|
||||
opacity: 0.75; /* 二级信息透明度降低 */
|
||||
text-align: left;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
padding: 12px 30px;
|
||||
border-radius: 25px;
|
||||
box-shadow: 0 4px 15px rgba(233, 69, 96, 0.4);
|
||||
letter-spacing: 1px;
|
||||
animation: pulseBtn 2s infinite;
|
||||
}
|
||||
|
||||
/* 一级信息强调样式 */
|
||||
.primary-section .section-content {
|
||||
font-size: 17px;
|
||||
opacity: 1;
|
||||
font-weight: 500;
|
||||
line-height: 1.7;
|
||||
letter-spacing: 0.5px;
|
||||
.share-btn {
|
||||
background: linear-gradient(135deg, #7a5cff, #005bea); /* 不同的蓝紫色渐变 */
|
||||
color: #fff;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
padding: 12px 30px;
|
||||
border-radius: 25px;
|
||||
box-shadow: 0 4px 15px rgba(122, 92, 255, 0.4);
|
||||
letter-spacing: 1px;
|
||||
width: auto !important;
|
||||
margin-bottom: 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.primary-section .section-label {
|
||||
font-size: 15px;
|
||||
color: #e94560; /* 稍微强调标题颜色 */
|
||||
opacity: 1;
|
||||
@keyframes pulseBtn {
|
||||
0% { transform: scale(1); box-shadow: 0 4px 15px rgba(233, 69, 96, 0.4); }
|
||||
50% { transform: scale(1.05); box-shadow: 0 6px 20px rgba(233, 69, 96, 0.6); }
|
||||
100% { transform: scale(1); box-shadow: 0 4px 15px rgba(233, 69, 96, 0.4); }
|
||||
}
|
||||
|
||||
.back-btn {
|
||||
font-size: 14px;
|
||||
opacity: 0.6;
|
||||
color: #c9a0dc; /* Light purple to match theme but be visible */
|
||||
opacity: 0.9;
|
||||
text-decoration: underline;
|
||||
padding: 10px;
|
||||
padding: 10px; /* Increased padding */
|
||||
}
|
||||
|
||||
/* 简单的动效 */
|
||||
|
|
@ -194,7 +407,7 @@ page {
|
|||
.status-divider {
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
|
|
@ -208,10 +421,11 @@ page {
|
|||
|
||||
.match-trigger {
|
||||
font-size: 14px;
|
||||
color: #fbaceb; /* 淡淡的粉色 */
|
||||
opacity: 0.8;
|
||||
color: #ffccff; /* Brighter pink/purple */
|
||||
opacity: 1;
|
||||
text-decoration: underline;
|
||||
margin-bottom: 5px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.match-content {
|
||||
|
|
@ -233,15 +447,22 @@ page {
|
|||
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;
|
||||
}
|
||||
|
||||
/* 免责声明 */
|
||||
.disclaimer {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
padding-bottom: 30px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.disclaimer text {
|
||||
font-size: 11px;
|
||||
color: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
/* 免责声明样式 */
|
||||
.disclaimer {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
padding: 20rpx 0;
|
||||
margin-top: 20rpx;
|
||||
border-top: 1rpx solid rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.disclaimer text {
|
||||
font-size: 20rpx;
|
||||
color: rgba(255, 255, 255, 0.3);
|
||||
letter-spacing: 1rpx;
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
const fs = require('fs');
|
||||
const path = 'e:\\I code\\pages\\index\\index.js';
|
||||
|
||||
try {
|
||||
let content = fs.readFileSync(path, 'utf8');
|
||||
|
||||
// Define old and new function
|
||||
// We use a simple string replace for the function head and body start
|
||||
const oldFn = ` revealNext: function (e) {
|
||||
const index = e.currentTarget.dataset.index;`;
|
||||
|
||||
const newFn = ` revealNext: function (e) {
|
||||
const index = (e.detail && e.detail.index !== undefined) ? e.detail.index : e.currentTarget.dataset.index;`;
|
||||
|
||||
if (content.indexOf(oldFn) === -1) {
|
||||
console.log('Could not find exact function match. Trying flexible match...');
|
||||
// regex fallback
|
||||
content = content.replace(
|
||||
/revealNext:\s*function\s*\(e\)\s*\{\s*const\s*index\s*=\s*e\.currentTarget\.dataset\.index;/g,
|
||||
`revealNext: function (e) {
|
||||
const index = (e.detail && e.detail.index !== undefined) ? e.detail.index : e.currentTarget.dataset.index;`
|
||||
);
|
||||
} else {
|
||||
content = content.replace(oldFn, newFn);
|
||||
}
|
||||
|
||||
// Add viewCardDetail if missing
|
||||
if (content.indexOf('viewCardDetail:') === -1) {
|
||||
const insertPoint = ` }
|
||||
},
|
||||
|
||||
// --- 展示解读内容 ---`;
|
||||
|
||||
// Find the end of revealNext and insert viewCardDetail
|
||||
// revealNext ends, usually followed by showInterpretation
|
||||
const showInterp = ` // --- 展示解读内容 ---`;
|
||||
|
||||
const newMethod = ` // 查看已翻开牌的详情
|
||||
viewCardDetail: function(e) {
|
||||
const index = (e.detail && e.detail.index !== undefined) ? e.detail.index : e.currentTarget.dataset.index;
|
||||
const card = this.data.drawnCards[index];
|
||||
if (card) {
|
||||
wx.showToast({
|
||||
title: card.name + (card.isReversed ? '(逆)' : ''),
|
||||
icon: 'none',
|
||||
duration: 1500
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// --- 展示解读内容 ---`;
|
||||
|
||||
content = content.replace(showInterp, newMethod);
|
||||
}
|
||||
|
||||
fs.writeFileSync(path, content, 'utf8');
|
||||
console.log('Successfully updated index.js functions');
|
||||
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
const fs = require('fs');
|
||||
const path = 'e:\\I code\\pages\\index\\index.js';
|
||||
|
||||
try {
|
||||
let content = fs.readFileSync(path, 'utf8');
|
||||
|
||||
// Regex to match aiSchema: [...] and append layout
|
||||
// We match "aiSchema": [ ... ] (multiline)
|
||||
// And escape the replacement curlies
|
||||
const regex = /("aiSchema":\s*\[[^\]]*\])/g;
|
||||
|
||||
let newContent = content.replace(regex, (match) => {
|
||||
return match + ',\n layout: { type: \'grid\' }';
|
||||
});
|
||||
|
||||
// Special handling for Celtic Cross
|
||||
// Find the generated text for celtic_cross and replace grid with specific layout
|
||||
// We need to look for "id": "celtic_cross" ... layout: { type: 'grid' }
|
||||
|
||||
const celticLayout = `layout: {
|
||||
type: 'celtic',
|
||||
areas: [
|
||||
{ area: 'center', rotate: 0 },
|
||||
{ area: 'center', rotate: 90 },
|
||||
{ area: 'bottom' },
|
||||
{ area: 'left' },
|
||||
{ area: 'top' },
|
||||
{ area: 'right' },
|
||||
{ area: 'side' },
|
||||
{ area: 'side' },
|
||||
{ area: 'side' },
|
||||
{ area: 'side' }
|
||||
]
|
||||
}`;
|
||||
|
||||
// Use a regex that finds the celtic_cross object and its layout
|
||||
// Look for id: celtic_cross, then scan until layout: { type: 'grid' }
|
||||
// This is tricky with regex.
|
||||
// Let's just replacing the specific string unique to celtic cross context if possible.
|
||||
|
||||
// Easier: Split by "id": "celtic_cross"
|
||||
const parts = newContent.split('"id": "celtic_cross"');
|
||||
if (parts.length > 1) {
|
||||
// part[1] starts after celtic_cross.
|
||||
// Find the first layout: { type: 'grid' } in part[1] and replace it.
|
||||
parts[1] = parts[1].replace("layout: { type: 'grid' }", celticLayout);
|
||||
newContent = parts.join('"id": "celtic_cross"');
|
||||
}
|
||||
|
||||
fs.writeFileSync(path, newContent, 'utf8');
|
||||
console.log('Successfully updated index.js');
|
||||
|
||||
} catch (e) {
|
||||
console.error('Error:', e);
|
||||
}
|
||||
|
|
@ -8,16 +8,12 @@ let isRequesting = false;
|
|||
|
||||
/**
|
||||
* 安全的 JSON 解析
|
||||
* @param {string} text - 待解析的文本
|
||||
* @returns {Object} 解析结果
|
||||
*/
|
||||
function safeJSONParse(text) {
|
||||
try {
|
||||
return JSON.parse(text);
|
||||
} catch (e) {
|
||||
console.log('AI_JSON_FALLBACK: 首次解析失败,尝试提取 JSON 代码块');
|
||||
|
||||
// 尝试提取 JSON 代码块(可能被包裹在 ```json ``` 中)
|
||||
const jsonMatch = text.match(/```json\s*([\s\S]*?)\s*```/) || text.match(/```\s*([\s\S]*?)\s*```/);
|
||||
if (jsonMatch && jsonMatch[1]) {
|
||||
try {
|
||||
|
|
@ -26,20 +22,12 @@ function safeJSONParse(text) {
|
|||
console.error('AI_JSON_FALLBACK: JSON 代码块解析也失败');
|
||||
}
|
||||
}
|
||||
|
||||
// 完全失败,返回 fallback 状态
|
||||
return {
|
||||
status: 'fallback_text',
|
||||
raw: text
|
||||
};
|
||||
return { status: 'fallback_text', raw: text };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成兜底解读内容
|
||||
* @param {Array} drawnCards - 抽到的牌
|
||||
* @param {Object} spread - 牌阵信息
|
||||
* @returns {Object} 兜底解读
|
||||
*/
|
||||
function generateFallbackInterpretation(drawnCards, spread) {
|
||||
const positions = drawnCards.map((card, index) => ({
|
||||
|
|
@ -58,8 +46,6 @@ function generateFallbackInterpretation(drawnCards, spread) {
|
|||
|
||||
/**
|
||||
* 执行单次 AI 请求
|
||||
* @param {Object} config - 请求配置
|
||||
* @returns {Promise} 请求结果
|
||||
*/
|
||||
function executeRequest(config) {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
|
@ -87,9 +73,6 @@ function executeRequest(config) {
|
|||
|
||||
/**
|
||||
* 带重试的请求执行
|
||||
* @param {Object} config - 请求配置
|
||||
* @param {number} retryCount - 当前重试次数
|
||||
* @returns {Promise} 请求结果
|
||||
*/
|
||||
async function executeWithRetry(config, retryCount = 0) {
|
||||
try {
|
||||
|
|
@ -104,8 +87,6 @@ async function executeWithRetry(config, retryCount = 0) {
|
|||
|
||||
if (shouldRetry) {
|
||||
console.log(`AI_REQUEST_RETRY: 第 ${retryCount + 1} 次重试`);
|
||||
|
||||
// 等待 1500ms 后重试
|
||||
await new Promise(resolve => setTimeout(resolve, 1500));
|
||||
return executeWithRetry(config, retryCount + 1);
|
||||
}
|
||||
|
|
@ -116,8 +97,6 @@ async function executeWithRetry(config, retryCount = 0) {
|
|||
|
||||
/**
|
||||
* 安全的 AI 请求(主函数)
|
||||
* @param {Object} payload - 请求参数
|
||||
* @returns {Promise<Object>} 请求结果
|
||||
*/
|
||||
async function safeAIRequest(payload) {
|
||||
const {
|
||||
|
|
@ -165,20 +144,17 @@ async function safeAIRequest(payload) {
|
|||
timeout: 90000
|
||||
});
|
||||
|
||||
// 清除超时定时器
|
||||
clearTimeout(timeoutTimer);
|
||||
clearTimeout(progressTimer1);
|
||||
clearTimeout(progressTimer2);
|
||||
|
||||
// 5. 检查手动超时
|
||||
if (manualTimeout) {
|
||||
throw new Error('Manual timeout');
|
||||
}
|
||||
|
||||
// 6. 安全解析 JSON
|
||||
// 5. 安全解析 JSON
|
||||
const parsedResult = safeJSONParse(rawResponse);
|
||||
|
||||
// 7. 检查是否是 fallback 状态
|
||||
if (parsedResult.status === 'fallback_text') {
|
||||
console.log('AI_JSON_FALLBACK: 使用兜底解读');
|
||||
isRequesting = false;
|
||||
|
|
@ -199,14 +175,12 @@ async function safeAIRequest(payload) {
|
|||
} catch (err) {
|
||||
console.error('AI_REQUEST_FAILED:', err);
|
||||
|
||||
// 清除定时器
|
||||
clearTimeout(timeoutTimer);
|
||||
clearTimeout(progressTimer1);
|
||||
clearTimeout(progressTimer2);
|
||||
|
||||
isRequesting = false;
|
||||
|
||||
// 返回兜底解读
|
||||
return {
|
||||
status: 'fallback',
|
||||
data: generateFallbackInterpretation(drawnCards, spread),
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ const STORAGE_KEY_POINTS = 'tarot_points';
|
|||
const STORAGE_KEY_LAST_LOGIN = 'tarot_last_login';
|
||||
const STORAGE_KEY_AD_DATE = 'tarot_ad_date';
|
||||
const STORAGE_KEY_AD_COUNT = 'tarot_ad_count';
|
||||
const STORAGE_KEY_SHARE_DATE = 'tarot_share_date'; // 分享奖励日期
|
||||
const STORAGE_KEY_SHARE_COUNT = 'tarot_share_count'; // 新增:分享奖励计数
|
||||
const DEFAULT_POINTS = 20; // 新用户默认积分
|
||||
const DAILY_REWARD = 3; // 每日登录奖励
|
||||
|
||||
|
|
@ -25,6 +27,12 @@ const AD_REWARD_CONFIG = {
|
|||
DAILY_LIMIT: 3 // 每日上限次数
|
||||
};
|
||||
|
||||
// 分享奖励配置
|
||||
const SHARE_REWARD_CONFIG = {
|
||||
REWARD_POINTS: 10, // 分享奖励积分 (改为 10)
|
||||
DAILY_LIMIT: 3 // 每日上限次数 (新增)
|
||||
};
|
||||
|
||||
/**
|
||||
* 初始化积分系统
|
||||
* 如果用户是首次使用,设置默认积分
|
||||
|
|
@ -282,6 +290,56 @@ function rewardFromAd() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查并发放分享奖励
|
||||
* @returns {object} { success: boolean, message: string, points: number }
|
||||
*/
|
||||
function checkShareReward() {
|
||||
try {
|
||||
const today = new Date().toDateString();
|
||||
const lastShareDate = wx.getStorageSync(STORAGE_KEY_SHARE_DATE);
|
||||
let shareCount = wx.getStorageSync(STORAGE_KEY_SHARE_COUNT) || 0;
|
||||
|
||||
// 如果日期变化,重置计数
|
||||
if (lastShareDate !== today) {
|
||||
shareCount = 0;
|
||||
wx.setStorageSync(STORAGE_KEY_SHARE_DATE, today);
|
||||
}
|
||||
|
||||
// 检查每日上限
|
||||
if (shareCount >= SHARE_REWARD_CONFIG.DAILY_LIMIT) {
|
||||
return {
|
||||
success: false,
|
||||
message: '今日分享奖励已达上限',
|
||||
points: 0
|
||||
};
|
||||
}
|
||||
|
||||
// 发放奖励
|
||||
const rewardPoints = SHARE_REWARD_CONFIG.REWARD_POINTS;
|
||||
const newPoints = addPoints(rewardPoints, 'share_reward');
|
||||
|
||||
// 更新计数
|
||||
shareCount++;
|
||||
wx.setStorageSync(STORAGE_KEY_SHARE_COUNT, shareCount);
|
||||
|
||||
console.log(`[积分系统] 分享奖励 +${rewardPoints} 积分 (今日第 ${shareCount}/${SHARE_REWARD_CONFIG.DAILY_LIMIT} 次)`);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `分享奖励 +${rewardPoints} 积分`,
|
||||
points: rewardPoints
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('[积分系统] 分享奖励检查失败:', error);
|
||||
return {
|
||||
success: false,
|
||||
message: '奖励发放失败',
|
||||
points: 0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
initPoints,
|
||||
getPoints,
|
||||
|
|
@ -292,7 +350,10 @@ module.exports = {
|
|||
// 广告奖励相关
|
||||
getTodayAdCount,
|
||||
canWatchAd,
|
||||
getTodayAdCount,
|
||||
canWatchAd,
|
||||
rewardFromAd,
|
||||
checkShareReward, // 导出新函数
|
||||
// 常量导出
|
||||
POINT_SOURCE,
|
||||
AD_REWARD_CONFIG
|
||||
|
|
|
|||
|
|
@ -0,0 +1,244 @@
|
|||
const { getZodiacList } = require('./zodiacData');
|
||||
|
||||
// AI 配置
|
||||
const API_KEY = 'sk-f659b4c3aa954baaa6cbdbd5cae0b7d1';
|
||||
const BASE_URL = 'https://api.deepseek.com/chat/completions';
|
||||
|
||||
/**
|
||||
* 获取今日日期字符串 (YYYY-MM-DD)
|
||||
*/
|
||||
function getTodayStr() {
|
||||
const now = new Date();
|
||||
return `${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate()}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建系统 Prompt
|
||||
*/
|
||||
function buildPrompt(zodiacName) {
|
||||
return `你是一位温暖、疗愈且富有洞察力的星座运势师。
|
||||
请根据今天的日期,为【${zodiacName}】生成一份专属运势。
|
||||
|
||||
【合规要求 - 极重要】
|
||||
1. 语言风格:温柔、诗意、治愈,仅供娱乐和心理参考。
|
||||
2. 禁止绝对化:不要使用“注定”、“一定会”、“必然”等词汇,使用“可能”、“倾向于”、“建议”等委婉表达。
|
||||
3. 禁止迷信承诺:严禁承诺具体的发财、升职、脱单结果。
|
||||
4. 禁止现实决策:严禁给出具体的医疗、法律、投资理财建议。
|
||||
5. 核心基调:通过细微的生活洞察,给人力量和抚慰,侧重心理状态疏导。
|
||||
|
||||
【输出结构】
|
||||
请严格按照以下 JSON 格式输出,不要包含 markdown 标记:
|
||||
{
|
||||
"keyword": "今日关键词 (2个字)",
|
||||
"core_energy": "核心能量 (15-20字,一句话概括)",
|
||||
"ratings": {
|
||||
"overall": 4, // 1-5的整数
|
||||
"love": 3,
|
||||
"career": 5,
|
||||
"wealth": 3
|
||||
},
|
||||
"fortune_text": {
|
||||
"love": "感情运势 (30-50字)",
|
||||
"career": "事业运势 (30-50字)",
|
||||
"wealth": "财运提醒 (30-50字)"
|
||||
},
|
||||
"lucky_elements": {
|
||||
"color": "颜色",
|
||||
"number": "数字",
|
||||
"time": "吉时 (如 15:00-17:00)"
|
||||
},
|
||||
"action": "行动建议 (20-30字)"
|
||||
}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全解析 JSON
|
||||
*/
|
||||
function safeJSONParse(text) {
|
||||
try {
|
||||
const cleanText = text.replace(/```json/g, '').replace(/```/g, '').trim();
|
||||
return JSON.parse(cleanText);
|
||||
} catch (e) {
|
||||
console.error('JSON解析失败:', e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取本地兜底数据 (适配新格式)
|
||||
*/
|
||||
function getLocalFallback(zodiacName) {
|
||||
const list = getZodiacList();
|
||||
const target = list.find(z => z.name === zodiacName);
|
||||
|
||||
if (target && target.fortunes && target.fortunes.length > 0) {
|
||||
const randomIdx = Math.floor(Math.random() * target.fortunes.length);
|
||||
const oldData = target.fortunes[randomIdx];
|
||||
|
||||
// 适配旧数据到新格式
|
||||
return {
|
||||
keyword: oldData.keyword,
|
||||
core_energy: oldData.today,
|
||||
ratings: {
|
||||
overall: 4,
|
||||
love: 3,
|
||||
career: 4,
|
||||
wealth: 3
|
||||
},
|
||||
fortune_text: {
|
||||
love: oldData.summary, // 暂用 summary 填充
|
||||
career: oldData.today,
|
||||
wealth: oldData.notice
|
||||
},
|
||||
lucky_elements: {
|
||||
color: "今日专属色",
|
||||
number: "7",
|
||||
time: "09:00-11:00"
|
||||
},
|
||||
action: oldData.action
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取星座运势 (优先缓存 -> AI -> 本地兜底)
|
||||
* @param {string} zodiacName 星座名称
|
||||
* @returns {Promise<object>} 运势数据对象
|
||||
*/
|
||||
async function fetchZodiacFortune(zodiacName) {
|
||||
const today = getTodayStr();
|
||||
const cacheKey = `zodiac_fortune_${zodiacName}_${today}`;
|
||||
|
||||
// 1. 检查缓存
|
||||
try {
|
||||
const cached = wx.getStorageSync(cacheKey);
|
||||
if (cached) {
|
||||
console.log(`[ZodiacAI] Hit cache for ${zodiacName}`);
|
||||
return cached;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('读取缓存失败:', e);
|
||||
}
|
||||
|
||||
console.log(`[ZodiacAI] Fetching AI for ${zodiacName}...`);
|
||||
|
||||
// 2. 调用 AI 接口
|
||||
try {
|
||||
const res = await new Promise((resolve, reject) => {
|
||||
wx.request({
|
||||
url: BASE_URL,
|
||||
method: 'POST',
|
||||
header: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${API_KEY}`
|
||||
},
|
||||
data: {
|
||||
model: "deepseek-chat",
|
||||
messages: [
|
||||
{ role: "system", content: buildPrompt(zodiacName) },
|
||||
{ role: "user", content: `今天是 ${today},请解读。` }
|
||||
],
|
||||
temperature: 1.3 // 稍微提高创造性
|
||||
},
|
||||
timeout: 30000,
|
||||
success: resolve,
|
||||
fail: reject
|
||||
});
|
||||
});
|
||||
|
||||
if (res.statusCode === 200 && res.data.choices && res.data.choices[0]) {
|
||||
const content = res.data.choices[0].message.content;
|
||||
const parsedData = safeJSONParse(content);
|
||||
|
||||
if (parsedData) {
|
||||
// 3. 写入缓存 (成功才缓存)
|
||||
wx.setStorageSync(cacheKey, parsedData);
|
||||
return parsedData;
|
||||
}
|
||||
}
|
||||
console.warn('[ZodiacAI] AI response invalid, using fallback.');
|
||||
|
||||
} catch (err) {
|
||||
console.error('[ZodiacAI] Request failed:', err);
|
||||
}
|
||||
|
||||
// 4. 失败兜底 (返回本地数据)
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取今日星象概览 (每日更新一次)
|
||||
*/
|
||||
async function fetchDailyOverview() {
|
||||
const today = getTodayStr();
|
||||
const cacheKey = `daily_overview_${today}`;
|
||||
|
||||
// 1. 检查缓存
|
||||
try {
|
||||
const cached = wx.getStorageSync(cacheKey);
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('读取缓存失败:', e);
|
||||
}
|
||||
|
||||
const prompt = `你是一位专业占星师。请根据今天日期,生成一段通用的【今日星象概览】。
|
||||
【风格要求】
|
||||
- 客观、简练、有指导意义
|
||||
- 描述今天的月亮位置、主要行星相位(如水星三合木星等)及其对大众的影响
|
||||
|
||||
【输出格式】
|
||||
严格 JSON 格式:
|
||||
{
|
||||
"content": "星象描述 (40-60字)",
|
||||
"tags": ["标签1", "标签2", "标签3"] // 3个短标签,如:利于社交、适合学习、谨慎决策
|
||||
}`;
|
||||
|
||||
// 2. 调用 AI
|
||||
try {
|
||||
const res = await new Promise((resolve, reject) => {
|
||||
wx.request({
|
||||
url: BASE_URL,
|
||||
method: 'POST',
|
||||
header: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${API_KEY}`
|
||||
},
|
||||
data: {
|
||||
model: "deepseek-chat",
|
||||
messages: [
|
||||
{ role: "system", content: prompt },
|
||||
{ role: "user", content: `今天是 ${today}。` }
|
||||
],
|
||||
temperature: 1.0
|
||||
},
|
||||
timeout: 30000,
|
||||
success: resolve,
|
||||
fail: reject
|
||||
});
|
||||
});
|
||||
|
||||
if (res.statusCode === 200 && res.data.choices) {
|
||||
const parsed = safeJSONParse(res.data.choices[0].message.content);
|
||||
if (parsed) {
|
||||
wx.setStorageSync(cacheKey, parsed);
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('星象概览获取失败:', err);
|
||||
}
|
||||
|
||||
// 兜底数据
|
||||
return {
|
||||
content: "今日月亮能量温和,适合处理内心的情绪。水星状态良好,沟通效率较高。",
|
||||
tags: ["平稳", "沟通顺畅", "宜内省"]
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
fetchZodiacFortune,
|
||||
fetchDailyOverview
|
||||
};
|
||||
|
|
@ -0,0 +1,322 @@
|
|||
const 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: '情感流动充沛,在关怀他人的同时也需照顾好自己。'
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
// 获取所有星座列表
|
||||
function getZodiacList() {
|
||||
return zodiacList;
|
||||
}
|
||||
|
||||
// 获取今日建议(随机)
|
||||
function getDailySuggestion() {
|
||||
// 简单实现:随机返回一个星座的一个运势
|
||||
const zIdx = Math.floor(Math.random() * zodiacList.length);
|
||||
const fIdx = Math.floor(Math.random() * zodiacList[zIdx].fortunes.length);
|
||||
return {
|
||||
zodiac: zodiacList[zIdx],
|
||||
fortune: zodiacList[zIdx].fortunes[fIdx]
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getZodiacList,
|
||||
getDailySuggestion
|
||||
};
|
||||
Loading…
Reference in New Issue