V1.3: 新增塔罗知识模块 - 支持本地+外链混合内容

This commit is contained in:
huanglimeng 2026-02-08 21:17:12 +08:00
parent 27b5c0a3ad
commit af8c17cf72
20 changed files with 746 additions and 1 deletions

View File

@ -1,7 +1,11 @@
{
"pages": [
"pages/home/home",
"pages/index/index"
"pages/index/index",
"pages/knowledge/index",
"pages/knowledge/list",
"pages/knowledge/article",
"pages/webview/index"
],
"window": {
"navigationBarTitleText": "我的第一个小程序"

View File

@ -3,5 +3,11 @@ Page({
wx.navigateTo({
url: '/pages/index/index'
})
},
goToKnowledge: function () {
wx.navigateTo({
url: '/pages/knowledge/index'
})
}
})

View File

@ -16,6 +16,14 @@
</view>
</view>
<!-- 知识模块入口 -->
<view class="direction-card" bindtap="goToKnowledge" hover-class="card-hover">
<view class="card-icon">📚</view>
<view class="card-content">
<text class="card-title">学点塔罗</text>
<text class="card-subtitle">了解塔罗基础知识</text>
</view>
</view>
</view>
</view>

View File

@ -0,0 +1,33 @@
const { getArticleById } = require('../../utils/knowledgeData');
Page({
data: {
article: null
},
onLoad(options) {
const id = options.id;
const article = getArticleById(id);
if (article) {
this.setData({
article: article
});
} else {
wx.showToast({
title: '文章不存在',
icon: 'none'
});
setTimeout(() => {
wx.navigateBack();
}, 1500);
}
},
// 去占卜
goToDivination() {
wx.navigateTo({
url: '/pages/index/index'
});
}
});

View File

@ -0,0 +1,5 @@
{
"navigationBarTitleText": "文章详情",
"navigationBarBackgroundColor": "#1a1a2e",
"navigationBarTextStyle": "white"
}

View File

@ -0,0 +1,16 @@
<view class="container" wx:if="{{article}}">
<view class="article-header">
<text class="article-title">{{article.title}}</text>
<text class="article-summary">{{article.summary}}</text>
</view>
<view class="article-content">
<text class="content-text">{{article.content}}</text>
</view>
<view class="bottom-action">
<button class="divination-btn" bindtap="goToDivination" hover-class="btn-hover">
去占卜
</button>
</view>
</view>

View File

@ -0,0 +1,74 @@
.container {
min-height: 100vh;
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
padding: 40rpx 30rpx 120rpx;
}
.article-header {
margin-bottom: 50rpx;
}
.article-title {
display: block;
font-size: 44rpx;
font-weight: 600;
color: #fff;
line-height: 1.4;
margin-bottom: 24rpx;
}
.article-summary {
display: block;
font-size: 28rpx;
color: rgba(255, 255, 255, 0.7);
line-height: 1.6;
padding: 20rpx 24rpx;
background: rgba(255, 255, 255, 0.08);
border-radius: 12rpx;
border-left: 4rpx solid rgba(255, 255, 255, 0.3);
}
.article-content {
background: rgba(255, 255, 255, 0.05);
backdrop-filter: blur(10px);
border-radius: 16rpx;
padding: 40rpx 32rpx;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.content-text {
display: block;
font-size: 30rpx;
color: rgba(255, 255, 255, 0.9);
line-height: 1.8;
white-space: pre-wrap;
}
.bottom-action {
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: 20rpx 30rpx;
background: linear-gradient(to top, #1a1a2e 80%, transparent);
}
.divination-btn {
width: 100%;
height: 88rpx;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 44rpx;
color: #fff;
font-size: 32rpx;
font-weight: 500;
display: flex;
align-items: center;
justify-content: center;
border: none;
box-shadow: 0 8rpx 24rpx rgba(102, 126, 234, 0.3);
}
.btn-hover {
opacity: 0.8;
transform: scale(0.98);
}

21
pages/knowledge/index.js Normal file
View File

@ -0,0 +1,21 @@
const { getCategories } = require('../../utils/knowledgeData');
Page({
data: {
categories: []
},
onLoad() {
this.setData({
categories: getCategories()
});
},
// 点击分类卡片
goToList(e) {
const category = e.currentTarget.dataset.category;
wx.navigateTo({
url: `/pages/knowledge/list?category=${category}`
});
}
});

View File

@ -0,0 +1,5 @@
{
"navigationBarTitleText": "塔罗知识",
"navigationBarBackgroundColor": "#1a1a2e",
"navigationBarTextStyle": "white"
}

View File

@ -0,0 +1,19 @@
<view class="container">
<view class="header">
<text class="page-title">塔罗知识</text>
</view>
<view class="category-list">
<view
class="category-card"
wx:for="{{categories}}"
wx:key="id"
bindtap="goToList"
data-category="{{item.id}}"
hover-class="card-hover"
>
<text class="category-icon">{{item.icon}}</text>
<text class="category-name">{{item.name}}</text>
</view>
</view>
</view>

View File

@ -0,0 +1,50 @@
.container {
min-height: 100vh;
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
padding: 40rpx 30rpx;
}
.header {
text-align: center;
margin-bottom: 60rpx;
}
.page-title {
font-size: 48rpx;
font-weight: 600;
color: #fff;
letter-spacing: 4rpx;
}
.category-list {
display: flex;
flex-direction: column;
gap: 30rpx;
}
.category-card {
background: rgba(255, 255, 255, 0.08);
backdrop-filter: blur(10px);
border-radius: 20rpx;
padding: 50rpx 40rpx;
display: flex;
align-items: center;
gap: 30rpx;
border: 1px solid rgba(255, 255, 255, 0.1);
transition: all 0.3s ease;
}
.card-hover {
background: rgba(255, 255, 255, 0.12);
transform: translateY(-4rpx);
}
.category-icon {
font-size: 60rpx;
}
.category-name {
font-size: 36rpx;
color: #fff;
font-weight: 500;
}

49
pages/knowledge/list.js Normal file
View File

@ -0,0 +1,49 @@
const { getArticlesByCategory, getCategories } = require('../../utils/knowledgeData');
Page({
data: {
category: '',
categoryName: '',
articles: []
},
onLoad(options) {
const category = options.category || 'beginner';
const categories = getCategories();
const categoryInfo = categories.find(c => c.id === category);
this.setData({
category: category,
categoryName: categoryInfo ? categoryInfo.name : '文章列表',
articles: getArticlesByCategory(category)
});
},
// 点击文章
goToArticle(e) {
const id = e.currentTarget.dataset.id;
const articles = this.data.articles;
const article = articles.find(a => a.id === id);
if (!article) {
wx.showToast({
title: '文章不存在',
icon: 'none'
});
return;
}
// 根据文章类型跳转
if (article.type === 'web') {
// 外链文章 → 跳转到 webview
wx.navigateTo({
url: `/pages/webview/index?url=${encodeURIComponent(article.url)}`
});
} else {
// 本地文章 → 跳转到 article 详情页
wx.navigateTo({
url: `/pages/knowledge/article?id=${id}`
});
}
}
});

View File

@ -0,0 +1,5 @@
{
"navigationBarTitleText": "文章列表",
"navigationBarBackgroundColor": "#1a1a2e",
"navigationBarTextStyle": "white"
}

19
pages/knowledge/list.wxml Normal file
View File

@ -0,0 +1,19 @@
<view class="container">
<view class="header">
<text class="page-title">{{categoryName}}</text>
</view>
<view class="article-list">
<view
class="article-item"
wx:for="{{articles}}"
wx:key="id"
bindtap="goToArticle"
data-id="{{item.id}}"
hover-class="item-hover"
>
<text class="article-title">{{item.title}}</text>
<text class="article-summary">{{item.summary}}</text>
</view>
</view>
</view>

52
pages/knowledge/list.wxss Normal file
View File

@ -0,0 +1,52 @@
.container {
min-height: 100vh;
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
padding: 40rpx 30rpx;
}
.header {
text-align: center;
margin-bottom: 50rpx;
}
.page-title {
font-size: 44rpx;
font-weight: 600;
color: #fff;
letter-spacing: 2rpx;
}
.article-list {
display: flex;
flex-direction: column;
gap: 24rpx;
}
.article-item {
background: rgba(255, 255, 255, 0.08);
backdrop-filter: blur(10px);
border-radius: 16rpx;
padding: 36rpx 32rpx;
border: 1px solid rgba(255, 255, 255, 0.1);
transition: all 0.3s ease;
}
.item-hover {
background: rgba(255, 255, 255, 0.12);
transform: translateX(8rpx);
}
.article-title {
display: block;
font-size: 32rpx;
color: #fff;
font-weight: 500;
margin-bottom: 16rpx;
}
.article-summary {
display: block;
font-size: 26rpx;
color: rgba(255, 255, 255, 0.6);
line-height: 1.6;
}

21
pages/webview/index.js Normal file
View File

@ -0,0 +1,21 @@
Page({
data: {
url: ''
},
onLoad(options) {
if (options.url) {
this.setData({
url: decodeURIComponent(options.url)
});
} else {
wx.showToast({
title: 'URL参数缺失',
icon: 'none'
});
setTimeout(() => {
wx.navigateBack();
}, 1500);
}
}
});

5
pages/webview/index.json Normal file
View File

@ -0,0 +1,5 @@
{
"navigationBarTitleText": "文章详情",
"navigationBarBackgroundColor": "#1a1a2e",
"navigationBarTextStyle": "white"
}

1
pages/webview/index.wxml Normal file
View File

@ -0,0 +1 @@
<web-view src="{{url}}"></web-view>

2
pages/webview/index.wxss Normal file
View File

@ -0,0 +1,2 @@
/* webview 页面样式 */
/* web-view 组件会自动占满整个页面,无需额外样式 */

350
utils/knowledgeData.js Normal file
View File

@ -0,0 +1,350 @@
// 塔罗知识模块 - 本地静态数据 V1.5
// 分类beginner(新手必看), inspiration(提问灵感), tips(塔罗小知识)
// 支持混合内容type: 'local'(本地文章) | 'web'(外链文章)
const knowledgeData = [
// ========== 新手必看 ==========
{
id: 'beginner_001',
category: 'beginner',
title: '什么是塔罗牌?',
summary: '了解塔罗牌的起源、结构和基本用途',
type: 'local',
content: `塔罗牌是一套由78张牌组成的占卜工具起源于15世纪的欧洲。
它分为两部分
大阿尔卡那22代表人生重大主题和转折点
小阿尔卡那56代表日常生活的细节和事件
塔罗牌不是用来"算命"而是一面镜子帮助你看清内心深处的想法和感受
它通过象征性的图像引导你思考问题的不同角度从而做出更明智的选择`,
url: ''
},
{
id: 'beginner_002',
category: 'beginner',
title: '塔罗占卜准吗?',
summary: '理解塔罗占卜的本质和正确心态',
type: 'local',
content: `塔罗牌不是"预测未来"的工具,而是"探索可能性"的方式。
它的准确性取决于
1. 你的问题是否清晰具体
2. 你是否真诚面对自己的内心
3. 你是否愿意从牌面中获得启发
塔罗牌反映的是当下的能量和趋势未来始终掌握在你自己手中
把塔罗当作一个智慧的朋友它会给你建议但最终的决定权在你`,
url: ''
},
{
id: 'beginner_003',
category: 'beginner',
title: '如何正确提问?',
summary: '掌握提问技巧,让占卜更有意义',
type: 'local',
content: `好的问题决定了占卜的质量。
避免的问题类型
是非题"他喜欢我吗?"
时间预测"我什么时候能升职?"
替他人占卜"他在想什么?"
推荐的问题方式
"在这段关系中,我需要注意什么?"
"如果我选择换工作,可能会面临哪些挑战?"
"我该如何改善目前的困境?"
记住"如何"比问"会不会"更有价值`,
url: ''
},
{
id: 'beginner_004',
category: 'beginner',
title: '正位和逆位是什么意思?',
summary: '理解牌面方向的含义',
type: 'local',
content: `塔罗牌在占卜时可能以两种方向出现:
正位
牌面图案正向朝上
通常代表该牌的直接含义
能量流畅显现明显
逆位
牌面图案倒置
可能代表能量受阻内化或过度
需要更深入的反思
逆位不等于"坏"它只是提醒你
这个能量可能被压抑延迟或需要从另一个角度理解`,
url: ''
},
{
id: 'web_002',
category: 'beginner',
title: '塔罗牌入门指南',
summary: '系统化的塔罗学习路径',
type: 'web',
content: '',
url: 'http://www.90000li.com/tarot/2/999d793fce9f4f3782c9b24769232942.html'
},
// ========== 提问灵感 ==========
{
id: 'inspiration_001',
category: 'inspiration',
title: '关于感情的提问',
summary: '如何更好地探索情感问题',
type: 'local',
content: `感情类问题是塔罗占卜中最常见的主题。
优质提问示例
"在这段关系中,我需要学习什么?"
"我该如何处理目前的情感困惑?"
"这段关系对我的成长有什么意义?"
"如何改善我们之间的沟通?"
避免问
"他/她喜欢我吗?"过于简单
"我们会结婚吗?"预测结果
"他/她现在在想什么?"替他人占卜
把焦点放在自己身上问题会更有力量`,
url: ''
},
{
id: 'inspiration_002',
category: 'inspiration',
title: '关于事业的提问',
summary: '探索职业发展和工作选择',
type: 'local',
content: `事业问题需要更具体的角度。
优质提问示例
"如果我选择这份工作,可能面临哪些挑战?"
"我该如何提升目前的工作表现?"
"这个项目的潜在风险是什么?"
"我的职业发展方向应该关注什么?"
避免问
"我会升职吗?"是非题
"我什么时候能找到工作?"时间预测
把问题聚焦在"如何做得更好"而不是"会不会成功"`,
url: ''
},
{
id: 'inspiration_003',
category: 'inspiration',
title: '关于个人成长的提问',
summary: '用塔罗探索内在自我',
type: 'local',
content: `塔罗牌最擅长的,就是帮助你认识自己。
优质提问示例
"我目前最需要关注的内在课题是什么?"
"如何突破当前的心理困境?"
"我的潜在优势是什么?"
"这段经历想教会我什么?"
这类问题没有"对错"只有"看见"
通过塔罗你可以
发现盲点
理解情绪
找到方向
获得力量`,
url: ''
},
{
id: 'inspiration_004',
category: 'inspiration',
title: '关于决策的提问',
summary: '在选择中寻找方向',
type: 'local',
content: `面对选择时,塔罗可以帮你看清不同路径。
优质提问示例
"选择A和选择B分别会带来什么影响"
"做这个决定时,我需要考虑什么?"
"这个选择对我的长期发展有何意义?"
建议使用"对比牌阵"
左边选择A的可能结果
右边选择B的可能结果
中间核心建议
记住塔罗给建议但决定权永远在你手中`,
url: ''
},
{
id: 'web_003',
category: 'inspiration',
title: '塔罗占卜实战技巧',
summary: '提升占卜准确度的方法',
type: 'web',
content: '',
url: 'http://www.90000li.com/tarot/2/999d793fce9f4f3782c9b24769232942.html'
},
// ========== 塔罗小知识 ==========
{
id: 'tips_001',
category: 'tips',
title: '为什么要"洗牌"',
summary: '了解洗牌的意义和能量连接',
type: 'local',
content: `洗牌不只是打乱顺序,它是一个能量连接的过程。
洗牌的作用
1. 清除上一次占卜的能量残留
2. 让你的意识与牌建立连接
3. 创造一个专注的仪式感
在洗牌时
保持内心平静
专注于你的问题
感受手中牌的重量
这个过程帮助你进入"占卜状态"让解读更准确`,
url: ''
},
{
id: 'tips_002',
category: 'tips',
title: '同一个问题可以占卜多次吗?',
summary: '理解占卜频率和心态',
type: 'local',
content: `不建议短时间内反复占卜同一个问题。
原因
频繁占卜会让能量混乱
你可能只是在寻找"想要的答案"
过度依赖会削弱你的判断力
建议
同一问题至少间隔1-2
如果情况有明显变化可以重新占卜
把塔罗当作参考而不是唯一依据
记住塔罗是工具不是拐杖`,
url: ''
},
{
id: 'tips_003',
category: 'tips',
title: '什么时候不适合占卜?',
summary: '选择合适的占卜时机',
type: 'local',
content: `并非所有时刻都适合占卜。
不适合占卜的情况
情绪极度激动或崩溃时
只想听"好消息"
已经有明确答案只是不想接受时
身体极度疲惫时
最佳占卜时机
内心相对平静
真诚想要获得启发
愿意接受任何答案
精神状态良好
占卜需要一颗开放的心`,
url: ''
},
{
id: 'tips_004',
category: 'tips',
title: '塔罗牌需要"养牌"吗?',
summary: '关于塔罗牌保养的真相',
type: 'local',
content: `网上有很多"养牌"的说法,但其实没那么复杂。
真正重要的
保持牌面清洁
避免潮湿和暴晒
用心对待你的牌
不必要的仪式
不需要"开光"
不需要特定时间才能用
不需要复杂的净化仪式
塔罗牌是工具你的诚意比仪式更重要
用心使用它自然会成为你的好伙伴`,
url: ''
},
{
id: 'tips_005',
category: 'tips',
title: '塔罗牌可以借给别人吗?',
summary: '关于塔罗牌的使用边界',
type: 'local',
content: `这是个人选择,没有绝对的对错。
传统观点
塔罗牌是私人物品不宜外借
避免能量混杂
现代观点
如果你不介意可以分享
用后可以简单整理一下
建议
如果你很在意就不要借
如果借出后感觉不舒服可以重新洗牌
最重要的是尊重自己的感受
你的感受才是最重要的边界`,
url: ''
},
{
id: 'web_001',
category: 'tips',
title: '塔罗牌基础知识介绍',
summary: '来自外部知识库的深度解析',
type: 'web',
content: '',
url: 'http://www.90000li.com/tarot/2/999d793fce9f4f3782c9b24769232942.html'
},
{
id: 'web_004',
category: 'tips',
title: '大阿卡那与小阿卡那的奥秘',
summary: '探索大阿卡那与小阿卡那的神秘世界',
type: 'web',
content: '',
url: 'http://www.90000li.com/tarot/null/4ae0c88e9c34499485250ec9bfa58098.html'
}
];
// 获取所有分类
function getCategories() {
return [
{ id: 'beginner', name: '新手必看', icon: '📖' },
{ id: 'inspiration', name: '提问灵感', icon: '💡' },
{ id: 'tips', name: '塔罗小知识', icon: '✨' }
];
}
// 根据分类获取文章列表
function getArticlesByCategory(category) {
return knowledgeData.filter(item => item.category === category);
}
// 根据ID获取文章详情
function getArticleById(id) {
return knowledgeData.find(item => item.id === id);
}
module.exports = {
knowledgeData,
getCategories,
getArticlesByCategory,
getArticleById
};