feat: 祈福小助手万年历小程序第一期
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
const { birthInfoToBazi, analyzeElementBalance } = require('../../utils/core/index.js');
|
||||
|
||||
Page({
|
||||
data: {
|
||||
gender: 'male',
|
||||
birthDate: '1990-01-01',
|
||||
birthTime: '12:00',
|
||||
ziSect: 'lateZiNextDay',
|
||||
bazi: null,
|
||||
pillars: [],
|
||||
elements: [],
|
||||
elementAnalysis: {}
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
// 尝试从本地存储恢复
|
||||
const saved = wx.getStorageSync('lunar-bazi-input');
|
||||
if (saved) {
|
||||
this.setData(saved);
|
||||
}
|
||||
},
|
||||
|
||||
setGender(e) {
|
||||
this.setData({ gender: e.currentTarget.dataset.g });
|
||||
},
|
||||
|
||||
setZiSect(e) {
|
||||
this.setData({ ziSect: e.currentTarget.dataset.s });
|
||||
},
|
||||
|
||||
onDateChange(e) {
|
||||
this.setData({ birthDate: e.detail.value });
|
||||
},
|
||||
|
||||
onTimeChange(e) {
|
||||
this.setData({ birthTime: e.detail.value });
|
||||
},
|
||||
|
||||
calculate() {
|
||||
const { gender, birthDate, birthTime, ziSect } = this.data;
|
||||
const [year, month, day] = birthDate.split('-').map(Number);
|
||||
const [hour, minute] = birthTime.split(':').map(Number);
|
||||
|
||||
const bazi = birthInfoToBazi({ year, month, day, hour, minute, gender, ziSect });
|
||||
const elementAnalysis = analyzeElementBalance(bazi.eightChar);
|
||||
|
||||
const pillars = [
|
||||
{ label: '年柱', ...bazi.eightChar.yearPillar },
|
||||
{ label: '月柱', ...bazi.eightChar.monthPillar },
|
||||
{ label: '日柱', ...bazi.eightChar.dayPillar },
|
||||
{ label: '时柱', ...bazi.eightChar.hourPillar }
|
||||
];
|
||||
|
||||
const total = elementAnalysis.total || 1;
|
||||
const elements = [
|
||||
{ name: '木', count: elementAnalysis.wood, percent: Math.round(elementAnalysis.wood / total * 100) },
|
||||
{ name: '火', count: elementAnalysis.fire, percent: Math.round(elementAnalysis.fire / total * 100) },
|
||||
{ name: '土', count: elementAnalysis.earth, percent: Math.round(elementAnalysis.earth / total * 100) },
|
||||
{ name: '金', count: elementAnalysis.metal, percent: Math.round(elementAnalysis.metal / total * 100) },
|
||||
{ name: '水', count: elementAnalysis.water, percent: Math.round(elementAnalysis.water / total * 100) }
|
||||
];
|
||||
|
||||
this.setData({ bazi, pillars, elements, elementAnalysis });
|
||||
|
||||
// 保存输入
|
||||
wx.setStorageSync('lunar-bazi-input', { gender, birthDate, birthTime, ziSect });
|
||||
// 保存八字供运势页使用
|
||||
wx.setStorageSync('lunar-user-bazi', bazi);
|
||||
},
|
||||
|
||||
reset() {
|
||||
this.setData({ bazi: null });
|
||||
},
|
||||
|
||||
goFortune() {
|
||||
wx.switchTab({ url: '/pages/fortune/fortune' });
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"navigationBarTitleText": "八字排盘",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
<view class="container">
|
||||
<!-- 输入表单 -->
|
||||
<view class="card" wx:if="{{!bazi}}">
|
||||
<view class="section-title">输入出生信息</view>
|
||||
|
||||
<view class="form-item">
|
||||
<text class="form-label">性别</text>
|
||||
<view class="flex gap-2">
|
||||
<view class="btn-outline {{gender === 'male' ? 'active' : ''}}" bindtap="setGender" data-g="male">男</view>
|
||||
<view class="btn-outline {{gender === 'female' ? 'active' : ''}}" bindtap="setGender" data-g="female">女</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<text class="form-label">出生日期</text>
|
||||
<picker mode="date" value="{{birthDate}}" bindchange="onDateChange">
|
||||
<view class="picker-input">{{birthDate}}</view>
|
||||
</picker>
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<text class="form-label">出生时间</text>
|
||||
<picker mode="time" value="{{birthTime}}" bindchange="onTimeChange">
|
||||
<view class="picker-input">{{birthTime}}</view>
|
||||
</picker>
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<text class="form-label">子时流派</text>
|
||||
<view class="flex gap-2">
|
||||
<view class="btn-outline {{ziSect === 'lateZiNextDay' ? 'active' : ''}}" bindtap="setZiSect" data-s="lateZiNextDay">晚子时日柱算次日</view>
|
||||
<view class="btn-outline {{ziSect === 'lateZiSameDay' ? 'active' : ''}}" bindtap="setZiSect" data-s="lateZiSameDay">晚子时日柱算当日</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="btn-primary mt-2" bindtap="calculate">排盘</view>
|
||||
</view>
|
||||
|
||||
<!-- 八字结果 -->
|
||||
<block wx:if="{{bazi}}">
|
||||
<!-- 四柱 -->
|
||||
<view class="card">
|
||||
<view class="section-title">四柱八字</view>
|
||||
<view class="grid grid-cols-4 gap-2">
|
||||
<view class="pillar" wx:for="{{pillars}}" wx:key="label">
|
||||
<text class="pillar-label">{{item.label}}</text>
|
||||
<text class="pillar-ganzhi">{{item.ganzhi}}</text>
|
||||
<text class="pillar-ten">{{item.tenStar || '日主'}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 五行分析 -->
|
||||
<view class="card">
|
||||
<view class="section-title">五行分析</view>
|
||||
<view class="element-bar">
|
||||
<view class="element-item" wx:for="{{elements}}" wx:key="name">
|
||||
<text class="element-name">{{item.name}}</text>
|
||||
<view class="element-track">
|
||||
<view class="element-fill" style="width: {{item.percent}}%"></view>
|
||||
</view>
|
||||
<text class="element-count">{{item.count}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="mt-2 text-sm text-muted">
|
||||
日主:{{bazi.eightChar.dayMaster}} | dominant: {{elementAnalysis.dominant}} | weakest: {{elementAnalysis.weakest}}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 大运 -->
|
||||
<view class="card">
|
||||
<view class="section-title">大运</view>
|
||||
<scroll-view scroll-x class="scroll-x">
|
||||
<view class="flex gap-2">
|
||||
<view class="fortune-item" wx:for="{{bazi.decadeFortunes}}" wx:key="index">
|
||||
<text class="text-sm text-muted">{{item.startAge}}-{{item.endAge}}岁</text>
|
||||
<text class="text-base font-medium">{{item.ganzhi}}</text>
|
||||
<text class="text-xs text-muted">{{item.startYear}}-{{item.endYear}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<!-- 流年 -->
|
||||
<view class="card">
|
||||
<view class="section-title">流年</view>
|
||||
<scroll-view scroll-x class="scroll-x">
|
||||
<view class="flex gap-2">
|
||||
<view class="fortune-item" wx:for="{{bazi.annualFortunes}}" wx:key="year">
|
||||
<text class="text-sm text-muted">{{item.age}}岁</text>
|
||||
<text class="text-base font-medium">{{item.ganzhi}}</text>
|
||||
<text class="text-xs text-muted">{{item.year}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<!-- 操作 -->
|
||||
<view class="flex gap-2 mt-2">
|
||||
<view class="btn-outline flex-1 text-center" bindtap="reset">重新排盘</view>
|
||||
<view class="btn-primary flex-1 text-center" bindtap="goFortune">看运势</view>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
@@ -0,0 +1,107 @@
|
||||
.container {
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
margin-bottom: 16rpx;
|
||||
color: #C41E3A;
|
||||
}
|
||||
|
||||
.form-item {
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
font-size: 24rpx;
|
||||
color: #6B5E53;
|
||||
margin-bottom: 8rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.picker-input {
|
||||
padding: 20rpx;
|
||||
background: #FFFBF5;
|
||||
border-radius: 12rpx;
|
||||
border: 1rpx solid #E8D5C4;
|
||||
}
|
||||
|
||||
.btn-outline.active {
|
||||
background: #C41E3A;
|
||||
color: #FFFFFF;
|
||||
border-color: #C41E3A;
|
||||
}
|
||||
|
||||
.pillar {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 20rpx;
|
||||
background: #FFFBF5;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
.pillar-label {
|
||||
font-size: 20rpx;
|
||||
color: #9E8E7E;
|
||||
}
|
||||
|
||||
.pillar-ganzhi {
|
||||
font-size: 36rpx;
|
||||
font-weight: 700;
|
||||
color: #C41E3A;
|
||||
margin: 8rpx 0;
|
||||
}
|
||||
|
||||
.pillar-ten {
|
||||
font-size: 20rpx;
|
||||
color: #6B5E53;
|
||||
}
|
||||
|
||||
.element-bar {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.element-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.element-name {
|
||||
width: 60rpx;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.element-track {
|
||||
flex: 1;
|
||||
height: 16rpx;
|
||||
background: #E8D5C4;
|
||||
border-radius: 8rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.element-fill {
|
||||
height: 100%;
|
||||
background: #C41E3A;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.element-count {
|
||||
width: 60rpx;
|
||||
text-align: right;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.fortune-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 16rpx 24rpx;
|
||||
background: #FFFBF5;
|
||||
border-radius: 12rpx;
|
||||
min-width: 160rpx;
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
const { getMonthCalendar } = require('../../utils/core/index.js');
|
||||
|
||||
Page({
|
||||
data: {
|
||||
year: new Date().getFullYear(),
|
||||
month: new Date().getMonth() + 1,
|
||||
weekHeaders: ['日', '一', '二', '三', '四', '五', '六'],
|
||||
calendarDays: [],
|
||||
selectedDay: null,
|
||||
showMonthPicker: false,
|
||||
years: [],
|
||||
months: [1,2,3,4,5,6,7,8,9,10,11,12],
|
||||
pickerValue: [0, 0]
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
const years = [];
|
||||
for (let y = 1900; y <= 2100; y++) years.push(y);
|
||||
this.setData({ years });
|
||||
this.loadCalendar();
|
||||
},
|
||||
|
||||
loadCalendar() {
|
||||
const { year, month } = this.data;
|
||||
const weeks = getMonthCalendar(year, month);
|
||||
const calendarDays = [];
|
||||
weeks.forEach(week => {
|
||||
week.forEach(day => calendarDays.push(day));
|
||||
});
|
||||
this.setData({ calendarDays });
|
||||
},
|
||||
|
||||
prevMonth() {
|
||||
let { year, month } = this.data;
|
||||
month--;
|
||||
if (month < 1) { month = 12; year--; }
|
||||
this.setData({ year, month }, () => this.loadCalendar());
|
||||
},
|
||||
|
||||
nextMonth() {
|
||||
let { year, month } = this.data;
|
||||
month++;
|
||||
if (month > 12) { month = 1; year++; }
|
||||
this.setData({ year, month }, () => this.loadCalendar());
|
||||
},
|
||||
|
||||
showPicker() {
|
||||
const { year, month } = this.data;
|
||||
this.setData({
|
||||
showMonthPicker: true,
|
||||
pickerValue: [year - 1900, month - 1]
|
||||
});
|
||||
},
|
||||
|
||||
hidePicker() {
|
||||
this.setData({ showMonthPicker: false });
|
||||
},
|
||||
|
||||
pickerChange(e) {
|
||||
this.setData({ pickerValue: e.detail.value });
|
||||
},
|
||||
|
||||
confirmPicker() {
|
||||
const { pickerValue, years, months } = this.data;
|
||||
this.setData({
|
||||
year: years[pickerValue[0]],
|
||||
month: months[pickerValue[1]],
|
||||
showMonthPicker: false
|
||||
}, () => this.loadCalendar());
|
||||
},
|
||||
|
||||
selectDay(e) {
|
||||
const date = e.currentTarget.dataset.date;
|
||||
const day = this.data.calendarDays.find(d => d.solarDate === date);
|
||||
this.setData({ selectedDay: day });
|
||||
},
|
||||
|
||||
goDetail() {
|
||||
const { selectedDay } = this.data;
|
||||
if (selectedDay) {
|
||||
wx.navigateTo({ url: `/pages/day-detail/day-detail?date=${selectedDay.solarDate}` });
|
||||
}
|
||||
},
|
||||
|
||||
noop() {}
|
||||
});
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"navigationBarTitleText": "日历",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<view class="container">
|
||||
<!-- 年月选择器 -->
|
||||
<view class="card flex justify-between items-center">
|
||||
<view class="btn-ghost" bindtap="prevMonth">‹</view>
|
||||
<view class="text-xl font-bold" bindtap="showPicker">{{year}}年{{month}}月</view>
|
||||
<view class="btn-ghost" bindtap="nextMonth">›</view>
|
||||
</view>
|
||||
|
||||
<!-- 星期头 -->
|
||||
<view class="grid grid-cols-7 gap-1 mb-1">
|
||||
<view class="text-center text-sm text-muted" wx:for="{{weekHeaders}}" wx:key="*this">{{item}}</view>
|
||||
</view>
|
||||
|
||||
<!-- 日历网格 -->
|
||||
<view class="grid grid-cols-7 gap-1">
|
||||
<view
|
||||
class="day-cell {{item.isToday ? 'today' : ''}} {{item.solarMonth !== month ? 'other-month' : ''}}"
|
||||
wx:for="{{calendarDays}}"
|
||||
wx:key="solarDate"
|
||||
bindtap="selectDay"
|
||||
data-date="{{item.solarDate}}"
|
||||
>
|
||||
<text class="day-number">{{item.solarDay}}</text>
|
||||
<text class="day-lunar {{item.lunarFestival || item.solarFestival ? 'festival' : ''}}">
|
||||
{{item.lunarFestival || item.solarFestival || item.lunarDayName}}
|
||||
</text>
|
||||
<view class="day-dot" wx:if="{{item.isTermDay}}"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 选中日期详情 -->
|
||||
<view class="card mt-2" wx:if="{{selectedDay}}">
|
||||
<view class="flex justify-between items-center">
|
||||
<view>
|
||||
<text class="text-lg font-bold">{{selectedDay.solarDate}}</text>
|
||||
<text class="text-sm text-muted block">{{selectedDay.lunarMonthName}}{{selectedDay.lunarDayName}}</text>
|
||||
</view>
|
||||
<view class="btn-primary" bindtap="goDetail">详情</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 年月选择弹窗 -->
|
||||
<view class="picker-mask {{showMonthPicker ? 'show' : ''}}" bindtap="hidePicker">
|
||||
<view class="picker-panel" catchtap="noop">
|
||||
<view class="picker-header">
|
||||
<text bindtap="hidePicker">取消</text>
|
||||
<text class="font-bold">选择年月</text>
|
||||
<text bindtap="confirmPicker">确定</text>
|
||||
</view>
|
||||
<picker-view class="picker-view" value="{{pickerValue}}" bindchange="pickerChange">
|
||||
<picker-view-column>
|
||||
<view wx:for="{{years}}" wx:key="*this" class="picker-item">{{item}}年</view>
|
||||
</picker-view-column>
|
||||
<picker-view-column>
|
||||
<view wx:for="{{months}}" wx:key="*this" class="picker-item">{{item}}月</view>
|
||||
</picker-view-column>
|
||||
</picker-view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -0,0 +1,92 @@
|
||||
.container {
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
.day-cell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 12rpx 4rpx;
|
||||
background: #FFFFFF;
|
||||
border-radius: 12rpx;
|
||||
min-height: 100rpx;
|
||||
}
|
||||
|
||||
.day-cell.today {
|
||||
background: #C41E3A;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.day-cell.today .day-lunar {
|
||||
color: rgba(255,255,255,0.8);
|
||||
}
|
||||
|
||||
.day-cell.other-month {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.day-number {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.day-lunar {
|
||||
font-size: 20rpx;
|
||||
color: #9E8E7E;
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
|
||||
.day-lunar.festival {
|
||||
color: #C41E3A;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.day-dot {
|
||||
width: 8rpx;
|
||||
height: 8rpx;
|
||||
border-radius: 50%;
|
||||
background: #C41E3A;
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
|
||||
.picker-mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0,0,0,0.5);
|
||||
z-index: 100;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.picker-mask.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.picker-panel {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: #FFFFFF;
|
||||
border-radius: 24rpx 24rpx 0 0;
|
||||
}
|
||||
|
||||
.picker-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 24rpx;
|
||||
border-bottom: 1rpx solid #E8D5C4;
|
||||
}
|
||||
|
||||
.picker-view {
|
||||
height: 400rpx;
|
||||
}
|
||||
|
||||
.picker-item {
|
||||
text-align: center;
|
||||
line-height: 68rpx;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
const { getDayInfo, getAlmanacInfo } = require('../../utils/core/index.js');
|
||||
|
||||
Page({
|
||||
data: {
|
||||
date: '',
|
||||
dayInfo: {},
|
||||
almanac: {}
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
const date = options.date || new Date().toISOString().split('T')[0];
|
||||
this.setData({ date });
|
||||
this.loadData(date);
|
||||
},
|
||||
|
||||
loadData(dateStr) {
|
||||
const [year, month, day] = dateStr.split('-').map(Number);
|
||||
const dayInfo = getDayInfo(year, month, day);
|
||||
const almanac = getAlmanacInfo(year, month, day);
|
||||
this.setData({ dayInfo, almanac });
|
||||
},
|
||||
|
||||
addBookmark() {
|
||||
const bookmarks = wx.getStorageSync('lunar-bookmarks') || [];
|
||||
const { dayInfo } = this.data;
|
||||
const exists = bookmarks.some(b => b.date === dayInfo.solarDate);
|
||||
if (!exists) {
|
||||
bookmarks.push({
|
||||
date: dayInfo.solarDate,
|
||||
lunarDate: `${dayInfo.lunarMonthName}${dayInfo.lunarDayName}`,
|
||||
note: ''
|
||||
});
|
||||
wx.setStorageSync('lunar-bookmarks', bookmarks);
|
||||
wx.showToast({ title: '已收藏', icon: 'success' });
|
||||
} else {
|
||||
wx.showToast({ title: '已收藏过', icon: 'none' });
|
||||
}
|
||||
},
|
||||
|
||||
goBazi() {
|
||||
wx.navigateTo({ url: '/pages/bazi/bazi' });
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"navigationBarTitleText": "日详情",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
<view class="container">
|
||||
<!-- 日期头部 -->
|
||||
<view class="card header-card">
|
||||
<view class="text-center">
|
||||
<text class="text-3xl font-bold">{{dayInfo.solarDay}}</text>
|
||||
<text class="text-lg text-muted block">{{dayInfo.solarYear}}年{{dayInfo.solarMonth}}月 {{dayInfo.weekDay}}</text>
|
||||
</view>
|
||||
<view class="flex justify-center gap-2 mt-2">
|
||||
<text class="badge badge-festival" wx:if="{{dayInfo.lunarFestival}}">{{dayInfo.lunarFestival}}</text>
|
||||
<text class="badge badge-festival" wx:if="{{dayInfo.solarFestival}}">{{dayInfo.solarFestival}}</text>
|
||||
<text class="badge badge-outline" wx:if="{{dayInfo.solarTerm}}">{{dayInfo.solarTerm}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 农历信息 -->
|
||||
<view class="card">
|
||||
<view class="section-title">农历</view>
|
||||
<view class="grid grid-cols-2 gap-2">
|
||||
<view class="info-item">
|
||||
<text class="text-xs text-muted">农历日期</text>
|
||||
<text class="text-base font-medium">{{dayInfo.lunarYear}}年{{dayInfo.lunarMonthName}}{{dayInfo.lunarDayName}}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="text-xs text-muted">生肖</text>
|
||||
<text class="text-base font-medium">{{dayInfo.zodiac}}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="text-xs text-muted">干支</text>
|
||||
<text class="text-base font-medium">{{dayInfo.lunarYearGanzhi}}年 {{dayInfo.lunarMonthGanzhi}}月 {{dayInfo.lunarDayGanzhi}}日</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="text-xs text-muted">月相</text>
|
||||
<text class="text-base font-medium">{{dayInfo.moonPhase}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 宜忌 -->
|
||||
<view class="card">
|
||||
<view class="section-title">宜忌</view>
|
||||
<view class="flex gap-2">
|
||||
<view class="flex-1">
|
||||
<text class="text-lucky font-bold">宜</text>
|
||||
<view class="flex flex-wrap gap-1 mt-1">
|
||||
<text class="badge badge-lucky" wx:for="{{almanac.recommends}}" wx:key="*this">{{item}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex-1">
|
||||
<text class="text-unlucky font-bold">忌</text>
|
||||
<view class="flex flex-wrap gap-1 mt-1">
|
||||
<text class="badge badge-unlucky" wx:for="{{almanac.avoids}}" wx:key="*this">{{item}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 黄历详情 -->
|
||||
<view class="card">
|
||||
<view class="section-title">黄历</view>
|
||||
<view class="grid grid-cols-2 gap-2">
|
||||
<view class="info-item">
|
||||
<text class="text-xs text-muted">值神</text>
|
||||
<text class="text-base font-medium">{{almanac.duty}}({{almanac.dutyLuck === 'good' ? '吉' : almanac.dutyLuck === 'bad' ? '凶' : '平'}})</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="text-xs text-muted">黄道黑道</text>
|
||||
<text class="text-base font-medium">{{almanac.twelveStar.name}}({{almanac.twelveStar.ecliptic}})</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="text-xs text-muted">二十八宿</text>
|
||||
<text class="text-base font-medium">{{almanac.twentyEightStar.name}}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="text-xs text-muted">九星</text>
|
||||
<text class="text-base font-medium">{{almanac.nineStar.name}}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="text-xs text-muted">六曜</text>
|
||||
<text class="text-base font-medium">{{almanac.sixStar}}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="text-xs text-muted">小六壬</text>
|
||||
<text class="text-base font-medium">{{almanac.minorRen.name}}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="text-xs text-muted">纳音</text>
|
||||
<text class="text-base font-medium">{{almanac.nayin}}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="text-xs text-muted">胎神</text>
|
||||
<text class="text-base font-medium">{{almanac.fetus.position}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 冲煞 -->
|
||||
<view class="card">
|
||||
<view class="section-title">冲煞</view>
|
||||
<view class="grid grid-cols-2 gap-2">
|
||||
<view class="info-item">
|
||||
<text class="text-xs text-muted">冲</text>
|
||||
<text class="text-base font-medium">{{almanac.clash}}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="text-xs text-muted">害</text>
|
||||
<text class="text-base font-medium">{{almanac.harm}}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="text-xs text-muted">合</text>
|
||||
<text class="text-base font-medium">{{almanac.combine}}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="text-xs text-muted">煞方</text>
|
||||
<text class="text-base font-medium">{{almanac.evilDirection}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 彭祖百忌 -->
|
||||
<view class="card">
|
||||
<view class="section-title">彭祖百忌</view>
|
||||
<text class="text-base">{{almanac.pengZu}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 时辰黄历 -->
|
||||
<view class="card">
|
||||
<view class="section-title">时辰黄历</view>
|
||||
<view class="hour-list">
|
||||
<view class="hour-item" wx:for="{{almanac.hourDetails}}" wx:key="name">
|
||||
<view class="flex justify-between items-center">
|
||||
<text class="font-medium">{{item.name}}({{item.range}})</text>
|
||||
<text class="text-sm text-muted">{{item.ganzhi}}</text>
|
||||
</view>
|
||||
<view class="flex gap-2 mt-1">
|
||||
<text class="text-xs text-muted">值神:{{item.twelveStar}}</text>
|
||||
<text class="text-xs text-muted">九星:{{item.nineStar}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<view class="flex gap-2 mt-2">
|
||||
<view class="btn-outline flex-1 text-center" bindtap="addBookmark">收藏</view>
|
||||
<view class="btn-primary flex-1 text-center" bindtap="goBazi">排八字</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -0,0 +1,41 @@
|
||||
.container {
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
.header-card {
|
||||
background: linear-gradient(135deg, #C41E3A 0%, #8B1A2B 100%);
|
||||
color: #FFFFFF;
|
||||
border: none;
|
||||
padding: 40rpx 24rpx;
|
||||
}
|
||||
|
||||
.header-card .text-muted {
|
||||
color: rgba(255,255,255,0.7);
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
margin-bottom: 16rpx;
|
||||
color: #C41E3A;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
padding: 16rpx;
|
||||
background: #FFFBF5;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
.hour-list {
|
||||
max-height: 600rpx;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.hour-item {
|
||||
padding: 16rpx;
|
||||
border-bottom: 1rpx solid #E8D5C4;
|
||||
}
|
||||
|
||||
.hour-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
const { calculatePlumBlossom, calculateBoneWeight, HEAVEN_STEMS, EARTH_BRANCHES, LUNAR_MONTH_NAMES, LUNAR_DAY_NAMES, HOUR_NAMES } = require('../../utils/core/index.js');
|
||||
|
||||
Page({
|
||||
data: {
|
||||
plumDate: new Date().toISOString().split('T')[0],
|
||||
plumTime: '12:00',
|
||||
plumResult: null,
|
||||
yearGanzhiList: [],
|
||||
monthList: LUNAR_MONTH_NAMES,
|
||||
dayList: LUNAR_DAY_NAMES,
|
||||
hourList: HOUR_NAMES,
|
||||
boneYearIndex: 0,
|
||||
boneMonthIndex: 0,
|
||||
boneDayIndex: 0,
|
||||
boneHourIndex: 0,
|
||||
boneResult: null
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
const yearGanzhiList = [];
|
||||
for (let i = 0; i < 60; i++) {
|
||||
yearGanzhiList.push(HEAVEN_STEMS[i % 10] + EARTH_BRANCHES[i % 12]);
|
||||
}
|
||||
this.setData({ yearGanzhiList });
|
||||
},
|
||||
|
||||
onPlumDateChange(e) {
|
||||
this.setData({ plumDate: e.detail.value });
|
||||
},
|
||||
|
||||
onPlumTimeChange(e) {
|
||||
this.setData({ plumTime: e.detail.value });
|
||||
},
|
||||
|
||||
calcPlum() {
|
||||
const { plumDate, plumTime } = this.data;
|
||||
const [year, month, day] = plumDate.split('-').map(Number);
|
||||
const [hour] = plumTime.split(':').map(Number);
|
||||
const result = calculatePlumBlossom(year, month, day, hour);
|
||||
this.setData({ plumResult: result });
|
||||
},
|
||||
|
||||
onBoneYearChange(e) {
|
||||
this.setData({ boneYearIndex: e.detail.value });
|
||||
},
|
||||
|
||||
onBoneMonthChange(e) {
|
||||
this.setData({ boneMonthIndex: e.detail.value });
|
||||
},
|
||||
|
||||
onBoneDayChange(e) {
|
||||
this.setData({ boneDayIndex: e.detail.value });
|
||||
},
|
||||
|
||||
onBoneHourChange(e) {
|
||||
this.setData({ boneHourIndex: e.detail.value });
|
||||
},
|
||||
|
||||
calcBone() {
|
||||
const { boneYearIndex, boneMonthIndex, boneDayIndex, boneHourIndex } = this.data;
|
||||
const result = calculateBoneWeight(boneYearIndex, boneMonthIndex + 1, boneDayIndex + 1, boneHourIndex);
|
||||
this.setData({ boneResult: result });
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"navigationBarTitleText": "占卜",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
<view class="container">
|
||||
<!-- 梅花易数 -->
|
||||
<view class="card">
|
||||
<view class="section-title">梅花易数</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">起卦时间</text>
|
||||
<picker mode="date" value="{{plumDate}}" bindchange="onPlumDateChange">
|
||||
<view class="picker-input">{{plumDate}}</view>
|
||||
</picker>
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">时辰</text>
|
||||
<picker mode="time" value="{{plumTime}}" bindchange="onPlumTimeChange">
|
||||
<view class="picker-input">{{plumTime}}</view>
|
||||
</picker>
|
||||
</view>
|
||||
<view class="btn-primary" bindtap="calcPlum">起卦</view>
|
||||
</view>
|
||||
|
||||
<!-- 梅花易数结果 -->
|
||||
<view class="card" wx:if="{{plumResult}}">
|
||||
<view class="section-title">卦象</view>
|
||||
<view class="hexagram-display">
|
||||
<view class="trigram-row">
|
||||
<text class="trigram-symbol">{{plumResult.upperTrigram.symbol}}</text>
|
||||
<text class="trigram-name">{{plumResult.upperTrigram.name}}({{plumResult.upperTrigram.element}})</text>
|
||||
</view>
|
||||
<view class="trigram-row">
|
||||
<text class="trigram-symbol">{{plumResult.lowerTrigram.symbol}}</text>
|
||||
<text class="trigram-name">{{plumResult.lowerTrigram.name}}({{plumResult.lowerTrigram.element}})</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="mt-2">
|
||||
<text class="text-lg font-bold">{{plumResult.originalHexagram.name}}</text>
|
||||
<text class="text-sm text-muted block mt-1">第{{plumResult.changingLine}}爻动</text>
|
||||
</view>
|
||||
<view class="mt-2 p-2 bg-page rounded">
|
||||
<text class="text-sm">{{plumResult.originalHexagram.judgment}}</text>
|
||||
</view>
|
||||
<view class="mt-2 p-2 bg-page rounded">
|
||||
<text class="text-sm">{{plumResult.originalHexagram.image}}</text>
|
||||
</view>
|
||||
<view class="mt-2" wx:if="{{plumResult.transformedHexagram}}">
|
||||
<text class="text-base font-medium">变卦:{{plumResult.transformedHexagram.name}}</text>
|
||||
</view>
|
||||
<view class="mt-2 text-sm text-muted">
|
||||
体用关系:{{plumResult.relationship}}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 称骨算命 -->
|
||||
<view class="card mt-2">
|
||||
<view class="section-title">称骨算命</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">出生年份(干支)</text>
|
||||
<picker mode="selector" range="{{yearGanzhiList}}" value="{{boneYearIndex}}" bindchange="onBoneYearChange">
|
||||
<view class="picker-input">{{yearGanzhiList[boneYearIndex]}}</view>
|
||||
</picker>
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">农历月份</text>
|
||||
<picker mode="selector" range="{{monthList}}" value="{{boneMonthIndex}}" bindchange="onBoneMonthChange">
|
||||
<view class="picker-input">{{monthList[boneMonthIndex]}}</view>
|
||||
</picker>
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">农历日期</text>
|
||||
<picker mode="selector" range="{{dayList}}" value="{{boneDayIndex}}" bindchange="onBoneDayChange">
|
||||
<view class="picker-input">{{dayList[boneDayIndex]}}</view>
|
||||
</picker>
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">时辰</text>
|
||||
<picker mode="selector" range="{{hourList}}" value="{{boneHourIndex}}" bindchange="onBoneHourChange">
|
||||
<view class="picker-input">{{hourList[boneHourIndex]}}</view>
|
||||
</picker>
|
||||
</view>
|
||||
<view class="btn-primary" bindtap="calcBone">算命</view>
|
||||
</view>
|
||||
|
||||
<!-- 称骨结果 -->
|
||||
<view class="card" wx:if="{{boneResult}}">
|
||||
<view class="section-title">称骨结果</view>
|
||||
<view class="text-center">
|
||||
<text class="text-3xl font-bold">{{boneResult.totalLiang}}两{{boneResult.totalQian}}钱</text>
|
||||
<text class="badge {{boneResult.fortune.includes('上') ? 'badge-lucky' : boneResult.fortune.includes('下') ? 'badge-unlucky' : 'badge-outline'}} ml-2">{{boneResult.fortune}}</text>
|
||||
</view>
|
||||
<view class="mt-2 p-3 bg-page rounded">
|
||||
<text class="text-base">{{boneResult.interpretation}}</text>
|
||||
</view>
|
||||
<view class="grid grid-cols-4 gap-2 mt-2">
|
||||
<view class="text-center">
|
||||
<text class="text-xs text-muted">年</text>
|
||||
<text class="text-base font-medium block">{{boneResult.yearWeight}}钱</text>
|
||||
</view>
|
||||
<view class="text-center">
|
||||
<text class="text-xs text-muted">月</text>
|
||||
<text class="text-base font-medium block">{{boneResult.monthWeight}}钱</text>
|
||||
</view>
|
||||
<view class="text-center">
|
||||
<text class="text-xs text-muted">日</text>
|
||||
<text class="text-base font-medium block">{{boneResult.dayWeight}}钱</text>
|
||||
</view>
|
||||
<view class="text-center">
|
||||
<text class="text-xs text-muted">时</text>
|
||||
<text class="text-base font-medium block">{{boneResult.hourWeight}}钱</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -0,0 +1,52 @@
|
||||
.container {
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
margin-bottom: 16rpx;
|
||||
color: #C41E3A;
|
||||
}
|
||||
|
||||
.form-item {
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
font-size: 24rpx;
|
||||
color: #6B5E53;
|
||||
margin-bottom: 8rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.picker-input {
|
||||
padding: 20rpx;
|
||||
background: #FFFBF5;
|
||||
border-radius: 12rpx;
|
||||
border: 1rpx solid #E8D5C4;
|
||||
}
|
||||
|
||||
.hexagram-display {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 24rpx;
|
||||
background: #FFFBF5;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
|
||||
.trigram-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
margin: 8rpx 0;
|
||||
}
|
||||
|
||||
.trigram-symbol {
|
||||
font-size: 64rpx;
|
||||
}
|
||||
|
||||
.trigram-name {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
const { calculateDailyFortune } = require('../../utils/core/index.js');
|
||||
|
||||
Page({
|
||||
data: {
|
||||
hasBazi: false,
|
||||
fortune: null,
|
||||
scoreLevelText: ''
|
||||
},
|
||||
|
||||
onShow() {
|
||||
this.loadFortune();
|
||||
},
|
||||
|
||||
onPullDownRefresh() {
|
||||
this.loadFortune();
|
||||
wx.stopPullDownRefresh();
|
||||
},
|
||||
|
||||
loadFortune() {
|
||||
const bazi = wx.getStorageSync('lunar-user-bazi');
|
||||
if (!bazi) {
|
||||
this.setData({ hasBazi: false, fortune: null });
|
||||
return;
|
||||
}
|
||||
|
||||
const today = new Date();
|
||||
const fortune = calculateDailyFortune(bazi.eightChar, today);
|
||||
|
||||
const levelMap = {
|
||||
great: '大吉',
|
||||
good: '吉',
|
||||
fair: '平',
|
||||
poor: '凶',
|
||||
bad: '大凶'
|
||||
};
|
||||
|
||||
this.setData({
|
||||
hasBazi: true,
|
||||
fortune,
|
||||
scoreLevelText: levelMap[fortune.scoreLevel] || '平'
|
||||
});
|
||||
},
|
||||
|
||||
goBazi() {
|
||||
wx.switchTab({ url: '/pages/bazi/bazi' });
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"navigationBarTitleText": "每日运势",
|
||||
"enablePullDownRefresh": true
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<view class="container">
|
||||
<!-- 无八字提示 -->
|
||||
<view class="card text-center" wx:if="{{!hasBazi}}">
|
||||
<text class="text-lg text-muted">请先排八字以查看每日运势</text>
|
||||
<view class="btn-primary mt-2" bindtap="goBazi">去排八字</view>
|
||||
</view>
|
||||
|
||||
<!-- 运势内容 -->
|
||||
<block wx:if="{{hasBazi && fortune}}">
|
||||
<!-- 总分卡片 -->
|
||||
<view class="card score-card">
|
||||
<view class="text-center">
|
||||
<text class="score-value">{{fortune.overallScore}}</text>
|
||||
<text class="score-level">{{scoreLevelText}}</text>
|
||||
</view>
|
||||
<view class="text-sm text-muted text-center mt-1">{{fortune.date}} {{fortune.lunarDate}}</view>
|
||||
</view>
|
||||
|
||||
<!-- 幸运元素 -->
|
||||
<view class="card">
|
||||
<view class="section-title">幸运元素</view>
|
||||
<view class="grid grid-cols-2 gap-2">
|
||||
<view class="info-item">
|
||||
<text class="text-xs text-muted">幸运色</text>
|
||||
<text class="text-base font-medium">{{fortune.luckyMeta.colors.join('、')}}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="text-xs text-muted">幸运数字</text>
|
||||
<text class="text-base font-medium">{{fortune.luckyMeta.numbers.join('、')}}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="text-xs text-muted">幸运方位</text>
|
||||
<text class="text-base font-medium">{{fortune.luckyMeta.direction}}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="text-xs text-muted">五行</text>
|
||||
<text class="text-base font-medium">{{fortune.luckyMeta.element}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 四柱关系 -->
|
||||
<view class="card">
|
||||
<view class="section-title">四柱与今日关系</view>
|
||||
<view class="relation-list">
|
||||
<view class="relation-item" wx:for="{{fortune.pillarRelationships}}" wx:key="pillar">
|
||||
<view class="flex justify-between items-center">
|
||||
<text class="font-medium">{{item.pillarLabel}}</text>
|
||||
<text class="text-sm {{item.score >= 0 ? 'text-lucky' : 'text-unlucky'}}">{{item.score > 0 ? '+' : ''}}{{item.score}}</text>
|
||||
</view>
|
||||
<view class="text-sm text-muted mt-1">
|
||||
{{item.userGanzhi}} vs {{item.dayGanzhi}}
|
||||
</view>
|
||||
<view class="flex flex-wrap gap-1 mt-1">
|
||||
<text class="badge badge-outline" wx:if="{{item.stemTenStar}}">{{item.stemTenStar}}</text>
|
||||
<text class="badge badge-lucky" wx:if="{{item.stemCombine}}">天干合</text>
|
||||
<text class="badge badge-unlucky" wx:if="{{item.stemOpposite}}">天干冲</text>
|
||||
<text class="badge badge-lucky" wx:if="{{item.branchCombine}}">地支合</text>
|
||||
<text class="badge badge-unlucky" wx:if="{{item.branchOpposite}}">地支冲</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 建议 -->
|
||||
<view class="card">
|
||||
<view class="section-title">今日建议</view>
|
||||
<view class="suggestion-list">
|
||||
<view class="suggestion-item" wx:for="{{fortune.suggestions}}" wx:key="*this">
|
||||
<text class="text-base">{{item}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 分项得分 -->
|
||||
<view class="card">
|
||||
<view class="section-title">分项运势</view>
|
||||
<view class="grid grid-cols-2 gap-2">
|
||||
<view class="info-item text-center">
|
||||
<text class="text-xs text-muted">爱情</text>
|
||||
<text class="text-xl font-bold {{fortune.categoryScores.love >= 0 ? 'text-lucky' : 'text-unlucky'}}">{{fortune.categoryScores.love}}</text>
|
||||
</view>
|
||||
<view class="info-item text-center">
|
||||
<text class="text-xs text-muted">事业</text>
|
||||
<text class="text-xl font-bold {{fortune.categoryScores.career >= 0 ? 'text-lucky' : 'text-unlucky'}}">{{fortune.categoryScores.career}}</text>
|
||||
</view>
|
||||
<view class="info-item text-center">
|
||||
<text class="text-xs text-muted">财运</text>
|
||||
<text class="text-xl font-bold {{fortune.categoryScores.wealth >= 0 ? 'text-lucky' : 'text-unlucky'}}">{{fortune.categoryScores.wealth}}</text>
|
||||
</view>
|
||||
<view class="info-item text-center">
|
||||
<text class="text-xs text-muted">健康</text>
|
||||
<text class="text-xl font-bold {{fortune.categoryScores.health >= 0 ? 'text-lucky' : 'text-unlucky'}}">{{fortune.categoryScores.health}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
@@ -0,0 +1,47 @@
|
||||
.container {
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
.score-card {
|
||||
background: linear-gradient(135deg, #C41E3A 0%, #8B1A2B 100%);
|
||||
color: #FFFFFF;
|
||||
border: none;
|
||||
padding: 40rpx 24rpx;
|
||||
}
|
||||
|
||||
.score-value {
|
||||
font-size: 80rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.score-level {
|
||||
font-size: 32rpx;
|
||||
margin-left: 16rpx;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
margin-bottom: 16rpx;
|
||||
color: #C41E3A;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
padding: 16rpx;
|
||||
background: #FFFBF5;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
.relation-item {
|
||||
padding: 16rpx;
|
||||
border-bottom: 1rpx solid #E8D5C4;
|
||||
}
|
||||
|
||||
.relation-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.suggestion-item {
|
||||
padding: 12rpx 0;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
const { getTodayInfo, getAlmanacInfo } = require('../../utils/core/index.js');
|
||||
|
||||
Page({
|
||||
data: {
|
||||
dayInfo: {},
|
||||
almanac: {}
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
this.loadData();
|
||||
},
|
||||
|
||||
onPullDownRefresh() {
|
||||
this.loadData();
|
||||
wx.stopPullDownRefresh();
|
||||
},
|
||||
|
||||
loadData() {
|
||||
const dayInfo = getTodayInfo();
|
||||
const almanac = getAlmanacInfo(dayInfo.solarYear, dayInfo.solarMonth, dayInfo.solarDay);
|
||||
this.setData({ dayInfo, almanac });
|
||||
},
|
||||
|
||||
navTo(e) {
|
||||
const url = e.currentTarget.dataset.url;
|
||||
wx.navigateTo({ url });
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"navigationBarTitleText": "万年历",
|
||||
"enablePullDownRefresh": true,
|
||||
"backgroundTextStyle": "dark"
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
<view class="container">
|
||||
<!-- 顶部日期卡片 -->
|
||||
<view class="card date-card">
|
||||
<view class="flex justify-between items-center">
|
||||
<view>
|
||||
<text class="text-3xl font-bold">{{dayInfo.solarDay}}</text>
|
||||
<text class="text-lg text-muted ml-2">{{dayInfo.solarMonth}}月{{dayInfo.solarYear}}年</text>
|
||||
</view>
|
||||
<view class="text-right">
|
||||
<text class="text-base">{{dayInfo.weekDay}}</text>
|
||||
<text class="text-sm text-muted block">{{dayInfo.constellation}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="mt-2 flex items-center gap-2">
|
||||
<text class="badge badge-festival" wx:if="{{dayInfo.lunarFestival}}">{{dayInfo.lunarFestival}}</text>
|
||||
<text class="badge badge-festival" wx:if="{{dayInfo.solarFestival}}">{{dayInfo.solarFestival}}</text>
|
||||
<text class="badge badge-outline" wx:if="{{dayInfo.solarTerm}}">{{dayInfo.solarTerm}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 农历信息 -->
|
||||
<view class="card">
|
||||
<view class="flex justify-between items-center">
|
||||
<view>
|
||||
<text class="text-2xl font-bold font-chinese">{{dayInfo.lunarMonthName}}{{dayInfo.lunarDayName}}</text>
|
||||
<text class="text-sm text-muted block mt-1">{{dayInfo.lunarYearGanzhi}}年 {{dayInfo.lunarMonthGanzhi}}月 {{dayInfo.lunarDayGanzhi}}日</text>
|
||||
</view>
|
||||
<view class="text-right">
|
||||
<text class="text-lg font-medium">{{dayInfo.zodiac}}年</text>
|
||||
<text class="text-sm text-muted block">{{dayInfo.moonPhase}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 宜忌 -->
|
||||
<view class="card">
|
||||
<view class="flex gap-2 mb-2">
|
||||
<view class="flex-1">
|
||||
<text class="text-lucky font-bold text-lg">宜</text>
|
||||
<view class="flex flex-wrap gap-1 mt-1">
|
||||
<text class="badge badge-lucky" wx:for="{{almanac.recommends}}" wx:key="*this">{{item}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex-1">
|
||||
<text class="text-unlucky font-bold text-lg">忌</text>
|
||||
<view class="flex flex-wrap gap-1 mt-1">
|
||||
<text class="badge badge-unlucky" wx:for="{{almanac.avoids}}" wx:key="*this">{{item}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 黄历详情 -->
|
||||
<view class="card">
|
||||
<view class="grid grid-cols-2 gap-2">
|
||||
<view class="p-2 bg-page rounded">
|
||||
<text class="text-xs text-muted">值神</text>
|
||||
<text class="text-base font-medium block">{{almanac.duty}}</text>
|
||||
</view>
|
||||
<view class="p-2 bg-page rounded">
|
||||
<text class="text-xs text-muted">黄道</text>
|
||||
<text class="text-base font-medium block">{{almanac.twelveStar.name}}({{almanac.twelveStar.ecliptic}})</text>
|
||||
</view>
|
||||
<view class="p-2 bg-page rounded">
|
||||
<text class="text-xs text-muted">二十八宿</text>
|
||||
<text class="text-base font-medium block">{{almanac.twentyEightStar.name}}</text>
|
||||
</view>
|
||||
<view class="p-2 bg-page rounded">
|
||||
<text class="text-xs text-muted">六曜</text>
|
||||
<text class="text-base font-medium block">{{almanac.sixStar}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 快捷入口 -->
|
||||
<view class="grid grid-cols-4 gap-2 mt-2">
|
||||
<view class="quick-entry" bindtap="navTo" data-url="/pages/calendar/calendar">
|
||||
<image src="/images/calendar.png" class="quick-icon" mode="aspectFit"/>
|
||||
<text class="text-sm">日历</text>
|
||||
</view>
|
||||
<view class="quick-entry" bindtap="navTo" data-url="/pages/bazi/bazi">
|
||||
<image src="/images/bazi.png" class="quick-icon" mode="aspectFit"/>
|
||||
<text class="text-sm">八字</text>
|
||||
</view>
|
||||
<view class="quick-entry" bindtap="navTo" data-url="/pages/fortune/fortune">
|
||||
<image src="/images/fortune.png" class="quick-icon" mode="aspectFit"/>
|
||||
<text class="text-sm">运势</text>
|
||||
</view>
|
||||
<view class="quick-entry" bindtap="navTo" data-url="/pages/divination/divination">
|
||||
<image src="/images/divination.png" class="quick-icon" mode="aspectFit"/>
|
||||
<text class="text-sm">占卜</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -0,0 +1,31 @@
|
||||
.container {
|
||||
padding: 20rpx;
|
||||
padding-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.date-card {
|
||||
background: linear-gradient(135deg, #C41E3A 0%, #8B1A2B 100%);
|
||||
color: #FFFFFF;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.date-card .text-muted {
|
||||
color: rgba(255,255,255,0.7);
|
||||
}
|
||||
|
||||
.quick-entry {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 24rpx;
|
||||
background: #FFFFFF;
|
||||
border-radius: 16rpx;
|
||||
border: 1rpx solid #E8D5C4;
|
||||
}
|
||||
|
||||
.quick-icon {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
Page({
|
||||
data: {
|
||||
userInfo: null,
|
||||
weekStartDay: 0,
|
||||
ziHourSect: 'lateZiNextDay',
|
||||
solarTime: true,
|
||||
bookmarks: []
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
this.loadSettings();
|
||||
this.loadBookmarks();
|
||||
},
|
||||
|
||||
onShow() {
|
||||
this.loadBookmarks();
|
||||
},
|
||||
|
||||
loadSettings() {
|
||||
const settings = wx.getStorageSync('lunar-settings') || {};
|
||||
this.setData({
|
||||
weekStartDay: settings.weekStartDay || 0,
|
||||
ziHourSect: settings.ziHourSect || 'lateZiNextDay',
|
||||
solarTime: settings.solarTime !== false
|
||||
});
|
||||
},
|
||||
|
||||
saveSettings() {
|
||||
const { weekStartDay, ziHourSect, solarTime } = this.data;
|
||||
wx.setStorageSync('lunar-settings', { weekStartDay, ziHourSect, solarTime });
|
||||
},
|
||||
|
||||
loadBookmarks() {
|
||||
const bookmarks = wx.getStorageSync('lunar-bookmarks') || [];
|
||||
this.setData({ bookmarks });
|
||||
},
|
||||
|
||||
setWeekStart(e) {
|
||||
this.setData({ weekStartDay: Number(e.currentTarget.dataset.v) });
|
||||
this.saveSettings();
|
||||
},
|
||||
|
||||
setZiSect(e) {
|
||||
this.setData({ ziHourSect: e.currentTarget.dataset.v });
|
||||
this.saveSettings();
|
||||
},
|
||||
|
||||
onSolarTimeChange(e) {
|
||||
this.setData({ solarTime: e.detail.value });
|
||||
this.saveSettings();
|
||||
},
|
||||
|
||||
removeBookmark(e) {
|
||||
const date = e.currentTarget.dataset.date;
|
||||
let bookmarks = this.data.bookmarks.filter(b => b.date !== date);
|
||||
wx.setStorageSync('lunar-bookmarks', bookmarks);
|
||||
this.setData({ bookmarks });
|
||||
wx.showToast({ title: '已删除', icon: 'success' });
|
||||
},
|
||||
|
||||
login() {
|
||||
wx.getUserProfile({
|
||||
desc: '用于完善用户资料',
|
||||
success: (res) => {
|
||||
this.setData({ userInfo: res.userInfo });
|
||||
wx.setStorageSync('lunar-user-info', res.userInfo);
|
||||
wx.showToast({ title: '登录成功', icon: 'success' });
|
||||
},
|
||||
fail: () => {
|
||||
wx.showToast({ title: '登录失败', icon: 'none' });
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"navigationBarTitleText": "设置",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<view class="container">
|
||||
<!-- 用户信息 -->
|
||||
<view class="card">
|
||||
<view class="section-title">用户信息</view>
|
||||
<view class="flex items-center gap-2" wx:if="{{userInfo}}">
|
||||
<image src="{{userInfo.avatarUrl}}" class="avatar" mode="aspectFill"/>
|
||||
<view>
|
||||
<text class="text-base font-medium">{{userInfo.nickName}}</text>
|
||||
<text class="text-xs text-muted block">已登录</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="btn-primary" wx:else bindtap="login">微信登录</view>
|
||||
</view>
|
||||
|
||||
<!-- 偏好设置 -->
|
||||
<view class="card">
|
||||
<view class="section-title">偏好设置</view>
|
||||
|
||||
<view class="setting-item">
|
||||
<text class="setting-label">周起始日</text>
|
||||
<view class="flex gap-2">
|
||||
<view class="btn-outline {{weekStartDay === 0 ? 'active' : ''}}" bindtap="setWeekStart" data-v="0">周日</view>
|
||||
<view class="btn-outline {{weekStartDay === 1 ? 'active' : ''}}" bindtap="setWeekStart" data-v="1">周一</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="setting-item">
|
||||
<text class="setting-label">子时流派</text>
|
||||
<view class="flex gap-2">
|
||||
<view class="btn-outline {{ziHourSect === 'lateZiNextDay' ? 'active' : ''}}" bindtap="setZiSect" data-v="lateZiNextDay">晚子时算次日</view>
|
||||
<view class="btn-outline {{ziHourSect === 'lateZiSameDay' ? 'active' : ''}}" bindtap="setZiSect" data-v="lateZiSameDay">晚子时算当日</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="setting-item">
|
||||
<text class="setting-label">真太阳时校正</text>
|
||||
<switch checked="{{solarTime}}" bindchange="onSolarTimeChange" color="#C41E3A"/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 收藏管理 -->
|
||||
<view class="card">
|
||||
<view class="section-title">我的收藏</view>
|
||||
<view class="bookmark-list" wx:if="{{bookmarks.length > 0}}">
|
||||
<view class="bookmark-item" wx:for="{{bookmarks}}" wx:key="date">
|
||||
<view class="flex justify-between items-center">
|
||||
<view>
|
||||
<text class="text-base font-medium">{{item.date}}</text>
|
||||
<text class="text-xs text-muted block">{{item.lunarDate}}</text>
|
||||
</view>
|
||||
<view class="btn-ghost text-unlucky" bindtap="removeBookmark" data-date="{{item.date}}">删除</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="text-center text-muted" wx:else>
|
||||
<text>暂无收藏</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 关于 -->
|
||||
<view class="card">
|
||||
<view class="section-title">关于</view>
|
||||
<view class="text-sm text-muted">
|
||||
<text>万年历小程序 v1.0.0</text>
|
||||
<text class="block mt-1">提供农历、黄历、八字、运势、占卜等功能</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -0,0 +1,47 @@
|
||||
.container {
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
margin-bottom: 16rpx;
|
||||
color: #C41E3A;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.setting-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 20rpx 0;
|
||||
border-bottom: 1rpx solid #E8D5C4;
|
||||
}
|
||||
|
||||
.setting-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.setting-label {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.btn-outline.active {
|
||||
background: #C41E3A;
|
||||
color: #FFFFFF;
|
||||
border-color: #C41E3A;
|
||||
}
|
||||
|
||||
.bookmark-item {
|
||||
padding: 16rpx 0;
|
||||
border-bottom: 1rpx solid #E8D5C4;
|
||||
}
|
||||
|
||||
.bookmark-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
const { SOLAR_TERMS, SOLAR_TERM_DATES } = require('../../utils/core/index.js');
|
||||
|
||||
Page({
|
||||
data: {
|
||||
year: new Date().getFullYear(),
|
||||
terms: []
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
this.loadTerms();
|
||||
},
|
||||
|
||||
loadTerms() {
|
||||
const { year } = this.data;
|
||||
const today = new Date();
|
||||
const terms = [];
|
||||
|
||||
for (let m = 0; m < 12; m++) {
|
||||
for (let t = 0; t < 2; t++) {
|
||||
const termIndex = m * 2 + t;
|
||||
const day = SOLAR_TERM_DATES[m][t];
|
||||
const dateStr = `${year}-${String(m + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
|
||||
const termDate = new Date(year, m, day);
|
||||
terms.push({
|
||||
name: SOLAR_TERMS[termIndex],
|
||||
date: dateStr,
|
||||
isPast: termDate < today
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 按日期排序
|
||||
terms.sort((a, b) => new Date(a.date) - new Date(b.date));
|
||||
this.setData({ terms });
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"navigationBarTitleText": "二十四节气",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<view class="container">
|
||||
<view class="card">
|
||||
<view class="section-title">{{year}}年二十四节气</view>
|
||||
<view class="term-list">
|
||||
<view class="term-item" wx:for="{{terms}}" wx:key="name">
|
||||
<view class="flex justify-between items-center">
|
||||
<view>
|
||||
<text class="text-base font-medium">{{item.name}}</text>
|
||||
<text class="text-xs text-muted block">{{item.date}}</text>
|
||||
</view>
|
||||
<text class="badge {{item.isPast ? 'badge-outline' : 'badge-lucky'}}">{{item.isPast ? '已过' : '未来'}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -0,0 +1,24 @@
|
||||
.container {
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
margin-bottom: 16rpx;
|
||||
color: #C41E3A;
|
||||
}
|
||||
|
||||
.term-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.term-item {
|
||||
padding: 20rpx 0;
|
||||
border-bottom: 1rpx solid #E8D5C4;
|
||||
}
|
||||
|
||||
.term-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
Reference in New Issue
Block a user