diff --git a/everything-is-suitable-uniapp/index.html b/everything-is-suitable-uniapp/index.html
new file mode 100644
index 0000000..d66c7c2
--- /dev/null
+++ b/everything-is-suitable-uniapp/index.html
@@ -0,0 +1,14 @@
+
+
+
+
+
+ 万事宜
+
+
+
+
+
+
+
+
diff --git a/everything-is-suitable-uniapp/src/App.vue b/everything-is-suitable-uniapp/src/App.vue
new file mode 100644
index 0000000..4750093
--- /dev/null
+++ b/everything-is-suitable-uniapp/src/App.vue
@@ -0,0 +1,20 @@
+
+
+
diff --git a/everything-is-suitable-uniapp/src/algorithms/almanac.ts b/everything-is-suitable-uniapp/src/algorithms/almanac.ts
new file mode 100644
index 0000000..4b590fc
--- /dev/null
+++ b/everything-is-suitable-uniapp/src/algorithms/almanac.ts
@@ -0,0 +1,396 @@
+import type { LunarDate as LunarDateType } from '../utils/lunar'
+import type { Almanac as AlmanacType } from './types'
+import { solarToLunar } from '../utils/lunar'
+
+const MONTH_BRANCHES = [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 2]
+
+const SUITABLE_ACTIVITIES_BY_JIANCHU: string[][] = [
+ ['开业', '嫁娶', '出行', '求财', '祭祀', '祈福'],
+ ['扫除', '治病', '破土', '安葬', '出行', '求医'],
+ ['祈福', '祭祀', '安葬', '修造', '动土', '交易'],
+ ['修造', '动土', '交易', '纳财', '签约', '安床'],
+ ['纳财', '签约', '安床', '嫁娶', '开市', '交易'],
+ ['上任', '出行', '求医', '祭祀', '祈福', '安葬'],
+ ['破屋', '坏垣', '拆卸', '破土', '修坟', '安葬'],
+ ['安葬', '破土', '修坟', '祭祀', '祈福', '出行'],
+ ['嫁娶', '开市', '交易', '纳财', '签约', '安床'],
+ ['纳财', '买田', '置业', '交易', '签约', '安床'],
+ ['开业', '开工', '开张', '嫁娶', '出行', '求财'],
+ ['塞穴', '断蚁', '筑堤', '修造', '动土', '交易'],
+]
+
+const UNSUITABLE_ACTIVITIES_BY_JIANCHU: string[][] = [
+ ['安葬', '破土', '修坟', '祭祀', '祈福', '出行'],
+ ['开业', '嫁娶', '出行', '求财', '祭祀', '祈福'],
+ ['开业', '嫁娶', '出行', '求财', '祭祀', '祈福'],
+ ['开业', '嫁娶', '出行', '求财', '祭祀', '祈福'],
+ ['开业', '嫁娶', '出行', '求财', '祭祀', '祈福'],
+ ['开业', '嫁娶', '出行', '求财', '祭祀', '祈福'],
+ ['开业', '嫁娶', '出行', '求财', '祭祀', '祈福'],
+ ['开业', '嫁娶', '出行', '求财', '祭祀', '祈福'],
+ ['安葬', '破土', '修坟', '祭祀', '祈福', '出行'],
+ ['安葬', '破土', '修坟', '祭祀', '祈福', '出行'],
+ ['安葬', '破土', '修坟', '祭祀', '祈福', '出行'],
+ ['开业', '嫁娶', '出行', '求财', '祭祀', '祈福'],
+]
+
+const CLASH_BRANCHES = [7, 8, 9, 10, 11, 12, 1, 2, 3, 4, 5, 6]
+
+const EVIL_DIRECTIONS = ['南', '东', '北', '西', '南', '东', '北', '西', '南', '东', '北', '西']
+
+const STAR_GODS = ['青龙', '明堂', '天刑', '朱雀', '金匮', '天德', '白虎', '玉堂', '天牢', '玄武', '司命', '勾陈']
+
+const NA_YIN_FIVE_ELEMENTS = [
+ '海中金', '海中金', '炉中火', '炉中火', '大林木', '大林木', '路旁土', '路旁土', '剑锋金', '剑锋金',
+ '山头火', '山头火', '涧下水', '涧下水', '城头土', '城头土', '白蜡金', '白蜡金', '杨柳木', '杨柳木',
+ '泉中水', '泉中水', '屋上土', '屋上土', '霹雳火', '霹雳火', '松柏木', '松柏木', '长流水', '长流水',
+ '沙中金', '沙中金', '山下火', '山下火', '平地木', '平地木', '壁上土', '壁上土', '金箔金', '金箔金',
+ '覆灯火', '覆灯火', '天河水', '天河水', '大驿土', '大驿土', '钗钏金', '钗钏金', '桑柘木', '桑柘木',
+ '大溪水', '大溪水', '沙中土', '沙中土', '天上火', '天上火', '石榴木', '石榴木', '大海水', '大海水',
+]
+
+const PENGZU_STEM_TABOOS = [
+ '甲不开仓财物耗散',
+ '乙不栽植千株不长',
+ '丙不修灶必见灾殃',
+ '丁不剃头头必生疮',
+ '戊不受田田主不祥',
+ '己不破券二比并亡',
+ '庚不经络织机虚张',
+ '辛不合酱主人不尝',
+ '壬不汲水更难提防',
+ '癸不词讼理弱敌强',
+]
+
+const PENGZU_BRANCH_TABOOS = [
+ '子不问卜自惹祸殃',
+ '丑不冠带主不还乡',
+ '寅不祭祀神鬼不尝',
+ '卯不穿井水泉不香',
+ '辰不哭泣必主重丧',
+ '巳不远行财物伏藏',
+ '午不苫盖屋主更张',
+ '未不服药毒气入肠',
+ '申不安床鬼祟入房',
+ '酉不会客醉坐颠狂',
+ '戌不吃犬作怪上床',
+ '亥不嫁娶不利新郎',
+]
+
+const JIANCHU_NAMES = ['建', '除', '满', '平', '定', '执', '破', '危', '成', '收', '开', '闭']
+
+const EARTHLY_BRANCH_NAMES = ['子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥']
+
+const FETUS_GOD_MAP: Record = {
+ 1: '房床北',
+ 2: '厕厕东北',
+ 3: '炉灶东北',
+ 4: '大门东',
+ 5: '栖床东南',
+ 6: '房床东南',
+ 7: '房床南',
+ 8: '厕厕西南',
+ 9: '炉灶西南',
+ 10: '大门西',
+ 11: '栖床西北',
+ 12: '房床西北',
+}
+
+export interface AlmanacResult {
+ solarDate: string
+ lunarDate: LunarDateType
+ jianChu: string
+ suitable: string[]
+ unsuitable: string[]
+ godDirection: string
+ joyDirection: string
+ fortuneDirection: string
+ nobleDirection: string
+ clash: string
+ evil: string
+ starGod: string
+ naYin: string
+ fetusGod: string
+ pengzuTaboo: string
+}
+
+function calculateTotalDays(year: number, month: number, day: number): number {
+ let totalDays = 0
+
+ for (let y = 1900; y < year; y++) {
+ totalDays += isLeapYear(y) ? 366 : 365
+ }
+
+ const daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
+ if (isLeapYear(year)) {
+ daysInMonth[1] = 29
+ }
+
+ for (let m = 1; m < month; m++) {
+ totalDays += daysInMonth[m - 1]
+ }
+
+ totalDays += day
+ return totalDays
+}
+
+function isLeapYear(year: number): boolean {
+ return (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0)
+}
+
+function getDayStemIndex(solarDate: Date): number {
+ const totalDays = calculateTotalDays(solarDate.getFullYear(), solarDate.getMonth() + 1, solarDate.getDate())
+ let dayStemIndex = (totalDays + 3) % 10
+ if (dayStemIndex === 0) {
+ dayStemIndex = 10
+ }
+ return dayStemIndex
+}
+
+function getDayBranchIndexFromSolar(solarDate: Date): number {
+ const totalDays = calculateTotalDays(solarDate.getFullYear(), solarDate.getMonth() + 1, solarDate.getDate())
+ let dayBranchIndex = (totalDays + 1) % 12
+ if (dayBranchIndex === 0) {
+ dayBranchIndex = 12
+ }
+ return dayBranchIndex
+}
+
+function getDayBranchIndexFromLunar(lunarDay: number): number {
+ let dayBranchIndex = (lunarDay + 2) % 12
+ if (dayBranchIndex === 0) {
+ dayBranchIndex = 12
+ }
+ return dayBranchIndex
+}
+
+export function calculateJianChu(lunarMonth: number, lunarDay: number): string {
+ if (lunarMonth < 1 || lunarMonth > 12) {
+ throw new Error(`农历月份超出范围:${lunarMonth}`)
+ }
+ if (lunarDay < 1 || lunarDay > 30) {
+ throw new Error(`农历日期超出范围:${lunarDay}`)
+ }
+
+ const monthBranch = MONTH_BRANCHES[lunarMonth - 1]
+ let jianChuIndex = (monthBranch + lunarDay - 1) % 12
+ if (jianChuIndex === 0) {
+ jianChuIndex = 12
+ }
+
+ return JIANCHU_NAMES[jianChuIndex - 1]
+}
+
+export function calculateSuitable(jianChu: string): string[] {
+ if (!jianChu) {
+ throw new Error('建除十二神不能为空')
+ }
+
+ const index = JIANCHU_NAMES.indexOf(jianChu)
+ if (index === -1) {
+ throw new Error(`无效的建除十二神:${jianChu}`)
+ }
+
+ return [...SUITABLE_ACTIVITIES_BY_JIANCHU[index]]
+}
+
+export function calculateUnsuitable(jianChu: string): string[] {
+ if (!jianChu) {
+ throw new Error('建除十二神不能为空')
+ }
+
+ const index = JIANCHU_NAMES.indexOf(jianChu)
+ if (index === -1) {
+ throw new Error(`无效的建除十二神:${jianChu}`)
+ }
+
+ return [...UNSUITABLE_ACTIVITIES_BY_JIANCHU[index]]
+}
+
+export function calculateGodDirection(solarDate: Date): string {
+ const dayStemIndex = getDayStemIndex(solarDate)
+
+ if (dayStemIndex === 1 || dayStemIndex === 2) return '东北'
+ if (dayStemIndex === 3 || dayStemIndex === 4) return '正西'
+ if (dayStemIndex === 5 || dayStemIndex === 6) return '正北'
+ if (dayStemIndex === 7 || dayStemIndex === 8) return '正东'
+ if (dayStemIndex === 9 || dayStemIndex === 10) return '正南'
+ return ''
+}
+
+export function calculateJoyDirection(solarDate: Date): string {
+ const dayStemIndex = getDayStemIndex(solarDate)
+
+ if (dayStemIndex % 2 === 1) return '正北'
+ return '东北'
+}
+
+export function calculateFortuneDirection(solarDate: Date): string {
+ const dayStemIndex = getDayStemIndex(solarDate)
+
+ if (dayStemIndex === 1 || dayStemIndex === 2) return '正北'
+ if (dayStemIndex === 3 || dayStemIndex === 4) return '西北'
+ if (dayStemIndex === 5 || dayStemIndex === 7 || dayStemIndex === 8) return '西南'
+ if (dayStemIndex === 6) return '正南'
+ if (dayStemIndex === 9 || dayStemIndex === 10) return '东南'
+ return ''
+}
+
+export function calculateNobleDirection(solarDate: Date): string {
+ const dayStemIndex = getDayStemIndex(solarDate)
+
+ if (dayStemIndex === 1) return '丑、未'
+ if (dayStemIndex === 2) return '子、申'
+ if (dayStemIndex === 3 || dayStemIndex === 4) return '亥、酉'
+ if (dayStemIndex === 5 || dayStemIndex === 6) return '丑、未'
+ if (dayStemIndex === 7 || dayStemIndex === 8) return '午、寅'
+ if (dayStemIndex === 9 || dayStemIndex === 10) return '卯、巳'
+ return ''
+}
+
+export function calculateClash(lunarDay: number): string {
+ if (lunarDay < 1 || lunarDay > 30) {
+ throw new Error(`农历日期超出范围:${lunarDay}`)
+ }
+
+ const dayBranchIndex = getDayBranchIndexFromLunar(lunarDay)
+ const clashBranchIndex = CLASH_BRANCHES[dayBranchIndex - 1]
+
+ return EARTHLY_BRANCH_NAMES[clashBranchIndex - 1]
+}
+
+export function calculateEvil(lunarDay: number): string {
+ if (lunarDay < 1 || lunarDay > 30) {
+ throw new Error(`农历日期超出范围:${lunarDay}`)
+ }
+
+ const dayBranchIndex = getDayBranchIndexFromLunar(lunarDay)
+ return EVIL_DIRECTIONS[dayBranchIndex - 1]
+}
+
+export function calculateStarGod(solarDate: Date): string {
+ const dayBranchIndex = getDayBranchIndexFromSolar(solarDate)
+ return STAR_GODS[dayBranchIndex - 1]
+}
+
+export function calculateNaYin(solarDate: Date): string {
+ const dayStemIndex = getDayStemIndex(solarDate)
+ const dayBranchIndex = getDayBranchIndexFromSolar(solarDate)
+
+ const naYinIndex = (dayStemIndex - 1) * 6 + Math.floor((dayBranchIndex - 1) / 2)
+
+ return NA_YIN_FIVE_ELEMENTS[naYinIndex]
+}
+
+export function calculateFetusGod(lunarDay: number): string {
+ if (lunarDay < 1 || lunarDay > 30) {
+ throw new Error(`农历日期超出范围:${lunarDay}`)
+ }
+
+ const dayBranchIndex = getDayBranchIndexFromLunar(lunarDay)
+ return FETUS_GOD_MAP[dayBranchIndex] ?? ''
+}
+
+export function calculatePengzuTaboo(solarDate: Date): string {
+ const dayStemIndex = getDayStemIndex(solarDate)
+ const dayBranchIndex = getDayBranchIndexFromSolar(solarDate)
+
+ const stemTaboo = PENGZU_STEM_TABOOS[dayStemIndex - 1]
+ const branchTaboo = PENGZU_BRANCH_TABOOS[dayBranchIndex - 1]
+
+ return `${stemTaboo},${branchTaboo}`
+}
+
+export function calculateAlmanac(solarDate: Date): AlmanacResult {
+ const lunarDate = solarToLunar(solarDate)
+
+ const jianChu = calculateJianChu(lunarDate.month, lunarDate.day)
+ const suitable = calculateSuitable(jianChu)
+ const unsuitable = calculateUnsuitable(jianChu)
+ const godDirection = calculateGodDirection(solarDate)
+ const joyDirection = calculateJoyDirection(solarDate)
+ const fortuneDirection = calculateFortuneDirection(solarDate)
+ const nobleDirection = calculateNobleDirection(solarDate)
+ const clash = calculateClash(lunarDate.day)
+ const evil = calculateEvil(lunarDate.day)
+ const starGod = calculateStarGod(solarDate)
+ const naYin = calculateNaYin(solarDate)
+ const fetusGod = calculateFetusGod(lunarDate.day)
+ const pengzuTaboo = calculatePengzuTaboo(solarDate)
+
+ return {
+ solarDate: solarDate.toISOString().slice(0, 10),
+ lunarDate,
+ jianChu,
+ suitable,
+ unsuitable,
+ godDirection,
+ joyDirection,
+ fortuneDirection,
+ nobleDirection,
+ clash,
+ evil,
+ starGod,
+ naYin,
+ fetusGod,
+ pengzuTaboo,
+ }
+}
+
+function convertLunarDate(lunar: LunarDateType): AlmanacType['lunarDate'] {
+ return {
+ lunarYear: lunar.ganZhi ?? String(lunar.year),
+ lunarMonth: String(lunar.month),
+ lunarDay: String(lunar.day),
+ isLeapMonth: lunar.isLeapMonth,
+ zodiac: lunar.zodiac ?? '',
+ solarTerm: '',
+ weekday: '',
+ }
+}
+
+function convertToAlmanac(result: AlmanacResult): AlmanacType {
+ return {
+ solarDate: result.solarDate,
+ lunarDate: convertLunarDate(result.lunarDate),
+ suitable: result.suitable,
+ unsuitable: result.unsuitable,
+ godDirection: result.godDirection,
+ joyDirection: result.joyDirection,
+ fortuneDirection: result.fortuneDirection,
+ nobleDirection: result.nobleDirection,
+ clash: result.clash,
+ evil: result.evil,
+ jianChu: result.jianChu,
+ starGod: result.starGod,
+ naYin: result.naYin,
+ fetusGod: result.fetusGod,
+ pengzuTaboo: result.pengzuTaboo,
+ }
+}
+
+export function getAlmanacByDate(dateStr: string): AlmanacType | null {
+ const date = new Date(dateStr)
+ if (isNaN(date.getTime())) return null
+ const result = calculateAlmanac(date)
+ return convertToAlmanac(result)
+}
+
+export function getAlmanacsByRange(startDateStr: string, endDateStr: string): AlmanacType[] {
+ const startDate = new Date(startDateStr)
+ const endDate = new Date(endDateStr)
+
+ if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) return []
+ if (startDate > endDate) return []
+
+ const results: AlmanacType[] = []
+ const current = new Date(startDate)
+
+ while (current <= endDate) {
+ const result = calculateAlmanac(new Date(current))
+ results.push(convertToAlmanac(result))
+ current.setDate(current.getDate() + 1)
+ }
+
+ return results
+}
diff --git a/everything-is-suitable-uniapp/src/algorithms/calendar.ts b/everything-is-suitable-uniapp/src/algorithms/calendar.ts
new file mode 100644
index 0000000..0bdc433
--- /dev/null
+++ b/everything-is-suitable-uniapp/src/algorithms/calendar.ts
@@ -0,0 +1,159 @@
+import { solarToLunar, getSolarTerm, getLunarFestivals } from '../utils/lunar'
+import type { LunarDate as LunarDateType } from '../utils/lunar'
+
+export interface CalendarDay {
+ solarDate: string
+ year: number
+ month: number
+ day: number
+ weekday: string
+ lunarDate: LunarDateType
+ solarFestivals: string[]
+ lunarFestivals: string[]
+ solarTerm: string | null
+ isWorkday: boolean
+}
+
+export interface CalendarMonth {
+ year: number
+ month: number
+ days: CalendarDay[]
+}
+
+export interface SolarTermInfo {
+ name: string
+ month: number
+ day: number
+ index: number
+}
+
+const WEEKDAY_NAMES = ['日', '一', '二', '三', '四', '五', '六']
+
+const SOLAR_FESTIVALS: Record = {
+ '1-1': ['元旦'],
+ '2-14': ['情人节'],
+ '3-8': ['妇女节'],
+ '3-12': ['植树节'],
+ '4-1': ['愚人节'],
+ '5-1': ['劳动节'],
+ '5-4': ['青年节'],
+ '6-1': ['儿童节'],
+ '7-1': ['建党节'],
+ '8-1': ['建军节'],
+ '9-10': ['教师节'],
+ '10-1': ['国庆节'],
+ '12-24': ['平安夜'],
+ '12-25': ['圣诞节'],
+}
+
+const SOLAR_TERM_NAMES = [
+ '小寒', '大寒', '立春', '雨水', '惊蛰', '春分',
+ '清明', '谷雨', '立夏', '小满', '芒种', '夏至',
+ '小暑', '大暑', '立秋', '处暑', '白露', '秋分',
+ '寒露', '霜降', '立冬', '小雪', '大雪', '冬至',
+]
+
+const SOLAR_TERM_BASE_DATES: { month: number; day: number; offset: number }[] = [
+ { month: 1, day: 6, offset: 0 },
+ { month: 1, day: 20, offset: 0 },
+ { month: 2, day: 4, offset: 0 },
+ { month: 2, day: 19, offset: 0 },
+ { month: 3, day: 6, offset: 0 },
+ { month: 3, day: 21, offset: 0 },
+ { month: 4, day: 5, offset: 0 },
+ { month: 4, day: 20, offset: 0 },
+ { month: 5, day: 6, offset: 0 },
+ { month: 5, day: 21, offset: 0 },
+ { month: 6, day: 6, offset: 0 },
+ { month: 6, day: 21, offset: 0 },
+ { month: 7, day: 7, offset: 0 },
+ { month: 7, day: 23, offset: 0 },
+ { month: 8, day: 7, offset: 0 },
+ { month: 8, day: 23, offset: 0 },
+ { month: 9, day: 8, offset: 0 },
+ { month: 9, day: 23, offset: 0 },
+ { month: 10, day: 8, offset: 0 },
+ { month: 10, day: 23, offset: 0 },
+ { month: 11, day: 7, offset: 0 },
+ { month: 11, day: 22, offset: 0 },
+ { month: 12, day: 7, offset: 0 },
+ { month: 12, day: 22, offset: 0 },
+]
+
+export function getSolarFestivals(month: number, day: number): string[] {
+ const key = `${month}-${day}`
+ return SOLAR_FESTIVALS[key] ? [...SOLAR_FESTIVALS[key]] : []
+}
+
+export function getSolarTermForYear(year: number): SolarTermInfo[] {
+ const yearMod = year % 4
+ const results: SolarTermInfo[] = []
+
+ for (let i = 0; i < 24; i++) {
+ const base = SOLAR_TERM_BASE_DATES[i]
+ const dayAdjust = (yearMod + Math.floor(year / 100) - 20) % 3
+ const adjustedDay = Math.max(1, Math.min(31, base.day + (dayAdjust > 1 ? 1 : 0)))
+
+ results.push({
+ name: SOLAR_TERM_NAMES[i],
+ month: base.month,
+ day: adjustedDay,
+ index: i,
+ })
+ }
+
+ return results
+}
+
+function getWeekdayName(date: Date): string {
+ return '星期' + WEEKDAY_NAMES[date.getDay()]
+}
+
+function checkIsWorkday(date: Date): boolean {
+ const day = date.getDay()
+ return day !== 0 && day !== 6
+}
+
+function getDaysInMonth(year: number, month: number): number {
+ return new Date(year, month, 0).getDate()
+}
+
+export function getCalendarDay(year: number, month: number, day: number): CalendarDay {
+ const date = new Date(year, month - 1, day)
+ const lunarDate = solarToLunar(date)
+ const solarTermInfo = getSolarTerm(date)
+ const solarFestivals = getSolarFestivals(month, day)
+ const lunarFestivals = getLunarFestivals(date)
+
+ return {
+ solarDate: `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`,
+ year,
+ month,
+ day,
+ weekday: getWeekdayName(date),
+ lunarDate,
+ solarFestivals,
+ lunarFestivals,
+ solarTerm: solarTermInfo.name,
+ isWorkday: checkIsWorkday(date),
+ }
+}
+
+export function getCalendarMonth(year: number, month: number): CalendarMonth {
+ const daysInMonth = getDaysInMonth(year, month)
+ const days: CalendarDay[] = []
+
+ for (let day = 1; day <= daysInMonth; day++) {
+ days.push(getCalendarDay(year, month, day))
+ }
+
+ return {
+ year,
+ month,
+ days,
+ }
+}
+
+export function isWorkday(date: Date): boolean {
+ return checkIsWorkday(date)
+}
diff --git a/everything-is-suitable-uniapp/src/algorithms/enums.ts b/everything-is-suitable-uniapp/src/algorithms/enums.ts
new file mode 100644
index 0000000..f5eeec4
--- /dev/null
+++ b/everything-is-suitable-uniapp/src/algorithms/enums.ts
@@ -0,0 +1,316 @@
+export enum HeavenlyStem {
+ JIA = 'JIA',
+ YI = 'YI',
+ BING = 'BING',
+ DING = 'DING',
+ WU = 'WU',
+ JI = 'JI',
+ GENG = 'GENG',
+ XIN = 'XIN',
+ REN = 'REN',
+ GUI = 'GUI',
+}
+
+export namespace HeavenlyStem {
+ const DATA: Record = {
+ [HeavenlyStem.JIA]: { name: '甲', index: 1 },
+ [HeavenlyStem.YI]: { name: '乙', index: 2 },
+ [HeavenlyStem.BING]: { name: '丙', index: 3 },
+ [HeavenlyStem.DING]: { name: '丁', index: 4 },
+ [HeavenlyStem.WU]: { name: '戊', index: 5 },
+ [HeavenlyStem.JI]: { name: '己', index: 6 },
+ [HeavenlyStem.GENG]: { name: '庚', index: 7 },
+ [HeavenlyStem.XIN]: { name: '辛', index: 8 },
+ [HeavenlyStem.REN]: { name: '壬', index: 9 },
+ [HeavenlyStem.GUI]: { name: '癸', index: 10 },
+ }
+
+ const VALUES: HeavenlyStem[] = Object.values(HeavenlyStem).filter(
+ (v): v is HeavenlyStem => typeof v === 'string',
+ )
+
+ export const name = (s: HeavenlyStem): string => DATA[s].name
+ export const index = (s: HeavenlyStem): number => DATA[s].index
+
+ export function fromIndex(i: number): HeavenlyStem {
+ return VALUES[((i - 1) % 10 + 10) % 10]
+ }
+
+ export function values(): HeavenlyStem[] {
+ return [...VALUES]
+ }
+}
+
+export enum EarthlyBranch {
+ ZI = 'ZI',
+ CHOU = 'CHOU',
+ YIN = 'YIN',
+ MAO = 'MAO',
+ CHEN = 'CHEN',
+ SI = 'SI',
+ WU = 'WU',
+ WEI = 'WEI',
+ SHEN = 'SHEN',
+ YOU = 'YOU',
+ XU = 'XU',
+ HAI = 'HAI',
+}
+
+export namespace EarthlyBranch {
+ const DATA: Record = {
+ [EarthlyBranch.ZI]: { name: '子', index: 1 },
+ [EarthlyBranch.CHOU]: { name: '丑', index: 2 },
+ [EarthlyBranch.YIN]: { name: '寅', index: 3 },
+ [EarthlyBranch.MAO]: { name: '卯', index: 4 },
+ [EarthlyBranch.CHEN]: { name: '辰', index: 5 },
+ [EarthlyBranch.SI]: { name: '巳', index: 6 },
+ [EarthlyBranch.WU]: { name: '午', index: 7 },
+ [EarthlyBranch.WEI]: { name: '未', index: 8 },
+ [EarthlyBranch.SHEN]: { name: '申', index: 9 },
+ [EarthlyBranch.YOU]: { name: '酉', index: 10 },
+ [EarthlyBranch.XU]: { name: '戌', index: 11 },
+ [EarthlyBranch.HAI]: { name: '亥', index: 12 },
+ }
+
+ const VALUES: EarthlyBranch[] = Object.values(EarthlyBranch).filter(
+ (v): v is EarthlyBranch => typeof v === 'string',
+ )
+
+ export const name = (s: EarthlyBranch): string => DATA[s].name
+ export const index = (s: EarthlyBranch): number => DATA[s].index
+
+ export function fromIndex(i: number): EarthlyBranch {
+ return VALUES[((i - 1) % 12 + 12) % 12]
+ }
+
+ export function values(): EarthlyBranch[] {
+ return [...VALUES]
+ }
+}
+
+export enum StarNature {
+ JI = 'JI',
+ XIONG = 'XIONG',
+ ZHONGHE = 'ZHONGHE',
+}
+
+export namespace StarNature {
+ const DATA: Record = {
+ [StarNature.JI]: { name: '吉', description: '吉星' },
+ [StarNature.XIONG]: { name: '凶', description: '凶星' },
+ [StarNature.ZHONGHE]: { name: '中', description: '中性' },
+ }
+
+ export const name = (s: StarNature): string => DATA[s].name
+ export const description = (s: StarNature): string => DATA[s].description
+}
+
+export enum TransformationType {
+ LU = 'LU',
+ QUAN = 'QUAN',
+ KE = 'KE',
+ JI = 'JI',
+}
+
+export namespace TransformationType {
+ const DATA: Record = {
+ [TransformationType.LU]: { name: '禄' },
+ [TransformationType.QUAN]: { name: '权' },
+ [TransformationType.KE]: { name: '科' },
+ [TransformationType.JI]: { name: '忌' },
+ }
+
+ export const name = (s: TransformationType): string => DATA[s].name
+}
+
+export enum MajorStar {
+ ZIWEI = 'ZIWEI',
+ TIANJI = 'TIANJI',
+ TAIYANG = 'TAIYANG',
+ WUQU = 'WUQU',
+ TIANTONG = 'TIANTONG',
+ LIANCHEN = 'LIANCHEN',
+ TIANFU = 'TIANFU',
+ TAIYIN = 'TAIYIN',
+ TANLANG = 'TANLANG',
+ JUMEN = 'JUMEN',
+ TIANXIANG = 'TIANXIANG',
+ TIANLIANG = 'TIANLIANG',
+ QISHA = 'QISHA',
+ POJUN = 'POJUN',
+}
+
+export namespace MajorStar {
+ const DATA: Record<
+ MajorStar,
+ { name: string; index: number; nature: StarNature; alias: string; description: string }
+ > = {
+ [MajorStar.ZIWEI]: { name: '紫微', index: 1, nature: StarNature.JI, alias: '帝星', description: '领导、权威、尊贵' },
+ [MajorStar.TIANJI]: { name: '天机', index: 2, nature: StarNature.JI, alias: '智星', description: '智慧、谋略、变通' },
+ [MajorStar.TAIYANG]: { name: '太阳', index: 3, nature: StarNature.JI, alias: '贵星', description: '光明、热情、慷慨' },
+ [MajorStar.WUQU]: { name: '武曲', index: 4, nature: StarNature.XIONG, alias: '财星', description: '刚毅、务实、财帛' },
+ [MajorStar.TIANTONG]: { name: '天同', index: 5, nature: StarNature.JI, alias: '福星', description: '温和、福气、享受' },
+ [MajorStar.LIANCHEN]: { name: '廉贞', index: 6, nature: StarNature.XIONG, alias: '情星', description: '感情、才华、是非' },
+ [MajorStar.TIANFU]: { name: '天府', index: 7, nature: StarNature.JI, alias: '库星', description: '稳重、保守、财富' },
+ [MajorStar.TAIYIN]: { name: '太阴', index: 8, nature: StarNature.ZHONGHE, alias: '母星', description: '温柔、内敛、母性' },
+ [MajorStar.TANLANG]: { name: '贪狼', index: 9, nature: StarNature.XIONG, alias: '欲星', description: '欲望、魅力、桃花' },
+ [MajorStar.JUMEN]: { name: '巨门', index: 10, nature: StarNature.XIONG, alias: '口星', description: '口才、是非、沟通' },
+ [MajorStar.TIANXIANG]: { name: '天相', index: 11, nature: StarNature.JI, alias: '印星', description: '辅佐、稳重、贵人' },
+ [MajorStar.TIANLIANG]: { name: '天梁', index: 12, nature: StarNature.ZHONGHE, alias: '荫星', description: '恩泽、稳重、长辈' },
+ [MajorStar.QISHA]: { name: '七杀', index: 13, nature: StarNature.XIONG, alias: '将星', description: '威猛、果断、开创' },
+ [MajorStar.POJUN]: { name: '破军', index: 14, nature: StarNature.XIONG, alias: '耗星', description: '破旧立新、变动、消耗' },
+ }
+
+ const VALUES: MajorStar[] = Object.values(MajorStar).filter(
+ (v): v is MajorStar => typeof v === 'string',
+ )
+
+ export const name = (s: MajorStar): string => DATA[s].name
+ export const index = (s: MajorStar): number => DATA[s].index
+ export const nature = (s: MajorStar): StarNature => DATA[s].nature
+ export const alias = (s: MajorStar): string => DATA[s].alias
+ export const description = (s: MajorStar): string => DATA[s].description
+
+ export function values(): MajorStar[] {
+ return [...VALUES]
+ }
+}
+
+export enum PalaceType {
+ MING = 'MING',
+ XIONG = 'XIONG',
+ FUQI = 'FUQI',
+ ZINV = 'ZINV',
+ CAI = 'CAI',
+ JILU = 'JILU',
+ QIAN = 'QIAN',
+ GUANLU = 'GUANLU',
+ TUDI = 'TUDI',
+ FUBEN = 'FUBEN',
+ FU = 'FU',
+ SHEN = 'SHEN',
+}
+
+export namespace PalaceType {
+ const DATA: Record = {
+ [PalaceType.MING]: { name: '命宫' },
+ [PalaceType.XIONG]: { name: '兄弟宫' },
+ [PalaceType.FUQI]: { name: '夫妻宫' },
+ [PalaceType.ZINV]: { name: '子女宫' },
+ [PalaceType.CAI]: { name: '财帛宫' },
+ [PalaceType.JILU]: { name: '疾厄宫' },
+ [PalaceType.QIAN]: { name: '迁移宫' },
+ [PalaceType.GUANLU]: { name: '官禄宫' },
+ [PalaceType.TUDI]: { name: '田宅宫' },
+ [PalaceType.FUBEN]: { name: '福德宫' },
+ [PalaceType.FU]: { name: '父母宫' },
+ [PalaceType.SHEN]: { name: '身宫' },
+ }
+
+ const VALUES: PalaceType[] = Object.values(PalaceType).filter(
+ (v): v is PalaceType => typeof v === 'string',
+ )
+
+ export const name = (s: PalaceType): string => DATA[s].name
+
+ export function values(): PalaceType[] {
+ return [...VALUES]
+ }
+}
+
+export enum FortuneType {
+ DAILY = 'DAILY',
+ MONTHLY = 'MONTHLY',
+ YEARLY = 'YEARLY',
+ CAREER = 'CAREER',
+ WEALTH = 'WEALTH',
+ LOVE = 'LOVE',
+ HEALTH = 'HEALTH',
+ STUDY = 'STUDY',
+ SOCIAL = 'SOCIAL',
+ TRAVEL = 'TRAVEL',
+}
+
+export enum MinorStarType {
+ LUCKY = 'LUCKY',
+ EVIL = 'EVIL',
+ OTHER = 'OTHER',
+}
+
+export namespace MinorStarType {
+ const DATA: Record = {
+ [MinorStarType.LUCKY]: { description: '吉星' },
+ [MinorStarType.EVIL]: { description: '煞星' },
+ [MinorStarType.OTHER]: { description: '其他' },
+ }
+
+ export const description = (s: MinorStarType): string => DATA[s].description
+}
+
+export enum MinorStar {
+ ZUOFU = 'ZUOFU',
+ YOUBI = 'YOUBI',
+ TIANKUI = 'TIANKUI',
+ TIANYUE = 'TIANYUE',
+ WENCHANG = 'WENCHANG',
+ WENQU = 'WENQU',
+ QINGYANG = 'QINGYANG',
+ TUOLUO = 'TUOLUO',
+ HUOXING = 'HUOXING',
+ LINGXING = 'LINGXING',
+ DIKONG = 'DIKONG',
+ DIJIE = 'DIJIE',
+ TIANXING = 'TIANXING',
+ LUCUN = 'LUCUN',
+ TIANMA = 'TIANMA',
+ HUAGAI = 'HUAGAI',
+ LONGCHI = 'LONGCHI',
+ FENGGU = 'FENGGU',
+ HONGLUAN = 'HONGLUAN',
+ TIANXI = 'TIANXI',
+ GUCHEN = 'GUCHEN',
+ GUAKU = 'GUAKU',
+}
+
+export namespace MinorStar {
+ const DATA: Record<
+ MinorStar,
+ { name: string; type: MinorStarType; alias: string; description: string }
+ > = {
+ [MinorStar.ZUOFU]: { name: '左辅', type: MinorStarType.LUCKY, alias: '左辅星', description: '主辅佐、助力、贵人' },
+ [MinorStar.YOUBI]: { name: '右弼', type: MinorStarType.LUCKY, alias: '右弼星', description: '主辅佐、助力、贵人' },
+ [MinorStar.TIANKUI]: { name: '天魁', type: MinorStarType.LUCKY, alias: '天魁星', description: '主贵人、助力、机遇' },
+ [MinorStar.TIANYUE]: { name: '天钺', type: MinorStarType.LUCKY, alias: '天钺星', description: '主贵人、助力、机遇' },
+ [MinorStar.WENCHANG]: { name: '文昌', type: MinorStarType.LUCKY, alias: '文昌星', description: '主科名、文采、学业' },
+ [MinorStar.WENQU]: { name: '文曲', type: MinorStarType.LUCKY, alias: '文曲星', description: '主口才、艺术、才华' },
+ [MinorStar.QINGYANG]: { name: '擎羊', type: MinorStarType.EVIL, alias: '羊刃星', description: '主刑伤、是非、血光' },
+ [MinorStar.TUOLUO]: { name: '陀罗', type: MinorStarType.EVIL, alias: '陀罗星', description: '主拖延、迟滞、纠结' },
+ [MinorStar.HUOXING]: { name: '火星', type: MinorStarType.EVIL, alias: '火星', description: '主急躁、突发、灾难' },
+ [MinorStar.LINGXING]: { name: '铃星', type: MinorStarType.EVIL, alias: '铃星', description: '主突发、灾难、变动' },
+ [MinorStar.DIKONG]: { name: '地空', type: MinorStarType.EVIL, alias: '地空星', description: '主空亡、损失、虚耗' },
+ [MinorStar.DIJIE]: { name: '地劫', type: MinorStarType.EVIL, alias: '地劫星', description: '主劫夺、损失、破财' },
+ [MinorStar.TIANXING]: { name: '天刑', type: MinorStarType.EVIL, alias: '天刑星', description: '主刑伤、官非、诉讼' },
+ [MinorStar.LUCUN]: { name: '禄存', type: MinorStarType.OTHER, alias: '禄存星', description: '主财帛、福气' },
+ [MinorStar.TIANMA]: { name: '天马', type: MinorStarType.OTHER, alias: '天马星', description: '主迁移、奔波、变动' },
+ [MinorStar.HUAGAI]: { name: '华盖', type: MinorStarType.OTHER, alias: '华盖星', description: '主孤独、艺术、宗教' },
+ [MinorStar.LONGCHI]: { name: '龙池', type: MinorStarType.OTHER, alias: '龙池星', description: '主才华、艺术、文采' },
+ [MinorStar.FENGGU]: { name: '凤阁', type: MinorStarType.OTHER, alias: '凤阁星', description: '主才华、艺术、文采' },
+ [MinorStar.HONGLUAN]: { name: '红鸾', type: MinorStarType.OTHER, alias: '红鸾星', description: '主桃花、婚姻、感情' },
+ [MinorStar.TIANXI]: { name: '天喜', type: MinorStarType.OTHER, alias: '天喜星', description: '主桃花、婚姻、感情' },
+ [MinorStar.GUCHEN]: { name: '孤辰', type: MinorStarType.OTHER, alias: '孤辰星', description: '主孤独、寂寞、寡居' },
+ [MinorStar.GUAKU]: { name: '寡宿', type: MinorStarType.OTHER, alias: '寡宿星', description: '主孤独、寂寞、寡居' },
+ }
+
+ const VALUES: MinorStar[] = Object.values(MinorStar).filter(
+ (v): v is MinorStar => typeof v === 'string',
+ )
+
+ export const name = (s: MinorStar): string => DATA[s].name
+ export const type = (s: MinorStar): MinorStarType => DATA[s].type
+ export const alias = (s: MinorStar): string => DATA[s].alias
+ export const description = (s: MinorStar): string => DATA[s].description
+
+ export function values(): MinorStar[] {
+ return [...VALUES]
+ }
+}
diff --git a/everything-is-suitable-uniapp/src/algorithms/fortune.ts b/everything-is-suitable-uniapp/src/algorithms/fortune.ts
new file mode 100644
index 0000000..9377460
--- /dev/null
+++ b/everything-is-suitable-uniapp/src/algorithms/fortune.ts
@@ -0,0 +1,510 @@
+import { PalaceType, StarNature, EarthlyBranch } from './enums'
+import { determineLuckLevel } from './ziweiAlgorithm'
+import {
+ generateDailyFortune,
+ generateMonthlyFortune,
+ generateYearlyFortune,
+ calculateDailyAdjustment,
+ calculateMonthlyAdjustment,
+ calculateYearlyAdjustment,
+} from './fortuneStrategy'
+import type {
+ ZiweiChart,
+ Palace,
+ LoveFortuneDetail,
+ StudyFortuneDetail,
+ SocialFortuneDetail,
+ TravelFortuneDetail,
+ DailyFortune,
+ MonthlyFortune,
+ YearlyFortune,
+} from './types'
+
+const PEACH_BLOSSOM_STARS = ['HONGLUAN', 'TIANXI', 'TANLANG']
+const STUDY_STARS = ['WENCHANG', 'WENQU']
+const NOBLE_STARS = ['ZUOFU', 'YOUBI', 'TIANKUI', 'TIANYUE']
+const TRAVEL_STARS = ['TIANMA']
+
+const DATING_TIME_MAP: Record = {
+ '大吉': '下午3点至5点,傍晚6点至8点',
+ '吉': '下午2点至4点,晚上7点至9点',
+ '中吉': '上午10点至12点,下午3点至5点',
+ '平': '下午1点至3点',
+ '中平': '上午9点至11点',
+ '小凶': '暂不宜约会,宜先调整自身状态',
+ '凶': '不宜约会,宜静待时机',
+}
+
+const STUDY_TIME_MAP: Record = {
+ '大吉': '清晨6点至8点,晚上8点至10点',
+ '吉': '上午8点至10点,晚上7点至9点',
+ '中吉': '上午9点至11点,下午3点至5点',
+ '平': '上午10点至12点',
+ '中平': '下午2点至4点',
+ '小凶': '学习效率较低,建议短暂休息后复习',
+ '凶': '不宜高强度学习,宜轻松阅读',
+}
+
+const SOCIAL_TIME_MAP: Record = {
+ '大吉': '中午12点至2点,晚上6点至9点',
+ '吉': '下午1点至3点,晚上7点至10点',
+ '中吉': '上午10点至12点,下午3点至5点',
+ '平': '下午2点至4点',
+ '中平': '上午9点至11点',
+ '小凶': '社交运势偏弱,宜减少应酬',
+ '凶': '不宜社交,宜独处调整',
+}
+
+const TRAVEL_TIME_MAP: Record = {
+ '大吉': '上午8点至10点出发最佳',
+ '吉': '上午9点至11点出发较佳',
+ '中吉': '上午10点至12点可出行',
+ '平': '中午前后出行即可',
+ '中平': '下午出行较为稳妥',
+ '小凶': '出行需谨慎,注意安全',
+ '凶': '不宜远行,近处活动为佳',
+}
+
+const SUITABLE_SUBJECTS_MAP: Record = {
+ '大吉': '文学、哲学、历史等文科类,或管理、领导力相关',
+ '吉': '语言、艺术、社会科学等,或技术类基础学科',
+ '中吉': '数学、逻辑、计算机等理科类',
+ '平': '实用技能、职业培训等应用型学科',
+ '中平': '通识类课程,宜广泛涉猎',
+ '小凶': '宜复习巩固已有知识,不宜开启新领域',
+ '凶': '宜休息调整,轻松阅读即可',
+}
+
+const SUITABLE_OCCASION_MAP: Record = {
+ '大吉': '商务宴请、高端社交、行业峰会',
+ '吉': '朋友聚会、团队活动、文化交流',
+ '中吉': '社区活动、兴趣小组、学习交流',
+ '平': '日常社交、同事聚餐',
+ '中平': '小范围聚会、线上交流',
+ '小凶': '减少社交活动,宜小范围相处',
+ '凶': '不宜参加社交活动,宜独处',
+}
+
+const SUITABLE_TRANSPORT_MAP: Record = {
+ '大吉': '飞机、高铁等快速交通工具',
+ '吉': '高铁、自驾等舒适出行方式',
+ '中吉': '火车、大巴等常规交通',
+ '平': '公共交通、地铁等日常出行',
+ '中平': '短途公交、步行等',
+ '小凶': '出行需注意安全,避免自驾',
+ '凶': '不宜远行,就近活动为佳',
+}
+
+const DIRECTION_LIST = ['东方', '南方', '西方', '北方', '东南', '西南', '东北', '西北']
+
+function findPalace(chart: ZiweiChart, palaceType: PalaceType): Palace | undefined {
+ return chart.palaces.find(p => p.palaceType === palaceType)
+}
+
+function countMinorStars(palace: Palace | undefined, starNames: string[]): number {
+ if (palace == null) return 0
+ let count = 0
+ for (const star of starNames) {
+ if (palace.minorStars.includes(star)) count++
+ }
+ return count
+}
+
+function countMajorStars(palace: Palace | undefined, starNames: string[]): number {
+ if (palace == null) return 0
+ let count = 0
+ for (const starInfo of palace.majorStars) {
+ if (starNames.includes(starInfo.star)) count++
+ }
+ return count
+}
+
+function clampScore(score: number): number {
+ return Math.max(0, Math.min(100, Math.floor(score)))
+}
+
+export function analyzeLoveFortune(chart: ZiweiChart, date: Date): LoveFortuneDetail {
+ const fuqiPalace = findPalace(chart, PalaceType.FUQI)
+ const baseScore = fuqiPalace?.score ?? 50
+
+ const peachBlossomCount =
+ countMinorStars(fuqiPalace, PEACH_BLOSSOM_STARS) +
+ countMajorStars(fuqiPalace, PEACH_BLOSSOM_STARS)
+ const peachBonus = peachBlossomCount * 5
+
+ const dayAdj = fuqiPalace ? calculateDailyAdjustment(fuqiPalace, date) : 0
+ const loveScore = clampScore(baseScore + peachBonus + dayAdj)
+ const luckLevel = determineLuckLevel(loveScore)
+
+ const peachBlossomIndex = getPeachBlossomLevel(peachBlossomCount)
+
+ return {
+ loveScore,
+ overallLoveLuck: luckLevel,
+ peachBlossomIndex,
+ singleAdvice: getSingleAdvice(luckLevel, peachBlossomCount),
+ datingAdvice: getDatingAdvice(luckLevel),
+ marriageAdvice: getMarriageAdvice(luckLevel),
+ bestDatingTime: DATING_TIME_MAP[luckLevel] ?? '下午2点至4点',
+ }
+}
+
+function getPeachBlossomLevel(count: number): string {
+ if (count >= 3) return '极旺'
+ if (count >= 2) return '旺盛'
+ if (count >= 1) return '一般'
+ return '偏弱'
+}
+
+function getSingleAdvice(luckLevel: string, peachCount: number): string {
+ if (peachCount >= 2) return '桃花旺盛,有望遇到心仪对象,可主动出击'
+ if (luckLevel === '大吉' || luckLevel === '吉') return '感情运势良好,可多参加社交活动,增加遇见良缘的机会'
+ if (luckLevel === '中吉' || luckLevel === '平') return '感情运势平稳,宜耐心等待,不宜急于求成'
+ return '感情运势偏弱,宜先提升自我,静待良缘'
+}
+
+function getDatingAdvice(luckLevel: string): string {
+ if (luckLevel === '大吉' || luckLevel === '吉') return '感情甜蜜,适合深入交流,增进彼此了解'
+ if (luckLevel === '中吉' || luckLevel === '平') return '感情平稳,保持日常沟通,多关心对方需求'
+ if (luckLevel === '中平') return '感情略显平淡,可制造小惊喜,增添情趣'
+ return '感情易生波折,需多包容理解,避免争执'
+}
+
+function getMarriageAdvice(luckLevel: string): string {
+ if (luckLevel === '大吉' || luckLevel === '吉') return '婚姻运势良好,适合商讨婚事或增进夫妻感情'
+ if (luckLevel === '中吉' || luckLevel === '平') return '婚姻运势平稳,宜相互扶持,共同经营家庭'
+ if (luckLevel === '中平') return '婚姻运势一般,需多沟通协调,避免误解'
+ return '婚姻运势欠佳,需多忍让包容,共同面对困难'
+}
+
+export function analyzeStudyFortune(chart: ZiweiChart, date: Date): StudyFortuneDetail {
+ const guanluPalace = findPalace(chart, PalaceType.GUANLU)
+ const baseScore = guanluPalace?.score ?? 50
+
+ const studyStarCount = countMinorStars(guanluPalace, STUDY_STARS)
+ const studyBonus = studyStarCount * 6
+
+ const dayAdj = guanluPalace ? calculateDailyAdjustment(guanluPalace, date) : 0
+ const studyScore = clampScore(baseScore + studyBonus + dayAdj)
+ const luckLevel = determineLuckLevel(studyScore)
+
+ return {
+ studyScore,
+ overallStudyLuck: luckLevel,
+ focusIndex: getFocusLevel(studyScore),
+ memoryIndex: getMemoryLevel(studyScore),
+ studyAdvice: getStudyAdvice(luckLevel, studyStarCount),
+ examLuck: getExamLuck(luckLevel),
+ bestStudyTime: STUDY_TIME_MAP[luckLevel] ?? '上午9点至11点',
+ suitableSubjects: SUITABLE_SUBJECTS_MAP[luckLevel] ?? '通识类课程',
+ studyEnvironmentAdvice: getStudyEnvironmentAdvice(luckLevel),
+ }
+}
+
+function getFocusLevel(score: number): string {
+ if (score >= 80) return '高度集中'
+ if (score >= 60) return '较为集中'
+ if (score >= 40) return '一般'
+ return '容易分心'
+}
+
+function getMemoryLevel(score: number): string {
+ if (score >= 80) return '记忆力佳'
+ if (score >= 60) return '记忆力良好'
+ if (score >= 40) return '记忆力一般'
+ return '记忆力偏弱'
+}
+
+function getStudyAdvice(luckLevel: string, studyStarCount: number): string {
+ if (studyStarCount >= 2) return '文昌文曲双星照耀,学业运势极佳,适合攻克难题和备考'
+ if (luckLevel === '大吉' || luckLevel === '吉') return '学业运势良好,适合深入学习新知识,效率较高'
+ if (luckLevel === '中吉' || luckLevel === '平') return '学业运势平稳,按计划学习即可,注意劳逸结合'
+ return '学业运势偏弱,宜复习巩固已有知识,不宜开启新领域'
+}
+
+function getExamLuck(luckLevel: string): string {
+ if (luckLevel === '大吉' || luckLevel === '吉') return '考试运佳,发挥出色'
+ if (luckLevel === '中吉' || luckLevel === '平') return '考试运平稳,正常发挥'
+ if (luckLevel === '中平') return '考试运一般,需多加准备'
+ return '考试运欠佳,需格外用心准备'
+}
+
+function getStudyEnvironmentAdvice(luckLevel: string): string {
+ if (luckLevel === '大吉' || luckLevel === '吉') return '宜在安静明亮的环境中学习,效率最佳'
+ if (luckLevel === '中吉' || luckLevel === '平') return '宜选择图书馆或自习室等安静场所'
+ return '宜在熟悉的环境中学习,减少外界干扰'
+}
+
+export function analyzeSocialFortune(chart: ZiweiChart, date: Date): SocialFortuneDetail {
+ const xiongPalace = findPalace(chart, PalaceType.XIONG)
+ const baseScore = xiongPalace?.score ?? 50
+
+ const nobleStarCount = countMinorStars(xiongPalace, NOBLE_STARS)
+ const nobleBonus = nobleStarCount * 5
+
+ const dayAdj = xiongPalace ? calculateDailyAdjustment(xiongPalace, date) : 0
+ const socialScore = clampScore(baseScore + nobleBonus + dayAdj)
+ const luckLevel = determineLuckLevel(socialScore)
+
+ return {
+ socialScore,
+ overallSocialLuck: luckLevel,
+ popularityIndex: getPopularityLevel(socialScore),
+ communicationAbility: getCommunicationLevel(socialScore),
+ socialAdvice: getSocialAdvice(luckLevel, nobleStarCount),
+ nobleLuck: getNobleLuck(nobleStarCount),
+ bestSocialTime: SOCIAL_TIME_MAP[luckLevel] ?? '下午2点至4点',
+ suitableOccasion: SUITABLE_OCCASION_MAP[luckLevel] ?? '日常社交',
+ interpersonalAdvice: getInterpersonalAdvice(luckLevel),
+ }
+}
+
+function getPopularityLevel(score: number): string {
+ if (score >= 80) return '人气旺盛'
+ if (score >= 60) return '人缘良好'
+ if (score >= 40) return '人缘一般'
+ return '人缘偏弱'
+}
+
+function getCommunicationLevel(score: number): string {
+ if (score >= 80) return '表达流畅'
+ if (score >= 60) return '沟通顺畅'
+ if (score >= 40) return '沟通一般'
+ return '沟通不畅'
+}
+
+function getSocialAdvice(luckLevel: string, nobleStarCount: number): string {
+ if (nobleStarCount >= 2) return '贵人星照耀,社交运势极佳,易获他人帮助与支持'
+ if (luckLevel === '大吉' || luckLevel === '吉') return '社交运势良好,适合拓展人脉,参加社交活动'
+ if (luckLevel === '中吉' || luckLevel === '平') return '社交运势平稳,保持正常交往即可'
+ return '社交运势偏弱,宜减少不必要的应酬,专注自身'
+}
+
+function getNobleLuck(nobleStarCount: number): string {
+ if (nobleStarCount >= 3) return '贵人运极旺,多有助力'
+ if (nobleStarCount >= 2) return '贵人运旺盛,易遇贵人'
+ if (nobleStarCount >= 1) return '贵人运一般,偶有助力'
+ return '贵人运偏弱,需自力更生'
+}
+
+function getInterpersonalAdvice(luckLevel: string): string {
+ if (luckLevel === '大吉' || luckLevel === '吉') return '人际关系和谐,宜主动关心他人,增进感情'
+ if (luckLevel === '中吉' || luckLevel === '平') return '人际关系平稳,保持真诚待人即可'
+ if (luckLevel === '中平') return '人际关系略显平淡,可主动联络朋友'
+ return '人际关系易生摩擦,需多忍让,避免冲突'
+}
+
+export function analyzeTravelFortune(chart: ZiweiChart, date: Date): TravelFortuneDetail {
+ const qianPalace = findPalace(chart, PalaceType.QIAN)
+ const baseScore = qianPalace?.score ?? 50
+
+ const travelStarCount = countMinorStars(qianPalace, TRAVEL_STARS)
+ const travelBonus = travelStarCount * 5
+
+ const dayAdj = qianPalace ? calculateDailyAdjustment(qianPalace, date) : 0
+ const travelScore = clampScore(baseScore + travelBonus + dayAdj)
+ const luckLevel = determineLuckLevel(travelScore)
+
+ return {
+ travelScore,
+ overallTravelLuck: luckLevel,
+ safetyIndex: getSafetyLevel(travelScore),
+ smoothnessIndex: getSmoothnessLevel(travelScore),
+ travelAdvice: getTravelAdvice(luckLevel, travelStarCount),
+ bestTravelTime: TRAVEL_TIME_MAP[luckLevel] ?? '中午前后',
+ suitableTransport: SUITABLE_TRANSPORT_MAP[luckLevel] ?? '常规交通',
+ precautions: getPrecautions(luckLevel),
+ nobleDirection: getNobleDirection(chart, date),
+ }
+}
+
+function getSafetyLevel(score: number): string {
+ if (score >= 80) return '安全指数高'
+ if (score >= 60) return '安全指数良好'
+ if (score >= 40) return '安全指数一般'
+ return '安全指数偏低'
+}
+
+function getSmoothnessLevel(score: number): string {
+ if (score >= 80) return '顺畅无阻'
+ if (score >= 60) return '较为顺畅'
+ if (score >= 40) return '一般'
+ return '可能受阻'
+}
+
+function getTravelAdvice(luckLevel: string, travelStarCount: number): string {
+ if (travelStarCount >= 1) return '天马星动,出行运势佳,适合出差旅行'
+ if (luckLevel === '大吉' || luckLevel === '吉') return '出行运势良好,旅途顺利,可安排出行计划'
+ if (luckLevel === '中吉' || luckLevel === '平') return '出行运势平稳,按计划出行即可'
+ return '出行运势欠佳,建议减少不必要的外出'
+}
+
+function getPrecautions(luckLevel: string): string {
+ if (luckLevel === '大吉' || luckLevel === '吉') return '出行顺利,注意常规安全即可'
+ if (luckLevel === '中吉' || luckLevel === '平') return '出行注意交通安全,提前查看天气'
+ if (luckLevel === '中平') return '出行需谨慎,注意人身和财物安全'
+ return '出行风险较高,务必注意安全,避免夜间出行'
+}
+
+function getNobleDirection(chart: ZiweiChart, _date: Date): string {
+ const mingGong = findPalace(chart, PalaceType.MING)
+ if (mingGong == null) return '东方'
+
+ const hasJiStar = mingGong.majorStars.some(s => s.nature === StarNature.JI)
+ const branchIndex = EarthlyBranch.index(chart.mingGongBranch) - 1
+ const directionIndex = hasJiStar
+ ? (branchIndex + 1) % DIRECTION_LIST.length
+ : (branchIndex + 3) % DIRECTION_LIST.length
+
+ return DIRECTION_LIST[directionIndex]
+}
+
+export function generateComprehensiveFortune(chart: ZiweiChart, date: Date): DailyFortune {
+ const baseFortune = generateDailyFortune(chart, date)
+
+ const loveFortune = analyzeLoveFortune(chart, date)
+ const studyFortune = analyzeStudyFortune(chart, date)
+ const socialFortune = analyzeSocialFortune(chart, date)
+ const travelFortune = analyzeTravelFortune(chart, date)
+
+ return {
+ ...baseFortune,
+ loveScore: loveFortune.loveScore,
+ overallLoveLuck: loveFortune.overallLoveLuck,
+ peachBlossomIndex: loveFortune.peachBlossomIndex,
+ singleAdvice: loveFortune.singleAdvice,
+ datingAdvice: loveFortune.datingAdvice,
+ marriageAdvice: loveFortune.marriageAdvice,
+ bestDatingTime: loveFortune.bestDatingTime,
+ studyScore: studyFortune.studyScore,
+ overallStudyLuck: studyFortune.overallStudyLuck,
+ focusIndex: studyFortune.focusIndex,
+ memoryIndex: studyFortune.memoryIndex,
+ studyAdvice: studyFortune.studyAdvice,
+ examLuck: studyFortune.examLuck,
+ bestStudyTime: studyFortune.bestStudyTime,
+ suitableSubjects: studyFortune.suitableSubjects,
+ studyEnvironmentAdvice: studyFortune.studyEnvironmentAdvice,
+ socialScore: socialFortune.socialScore,
+ overallSocialLuck: socialFortune.overallSocialLuck,
+ popularityIndex: socialFortune.popularityIndex,
+ communicationAbility: socialFortune.communicationAbility,
+ socialAdvice: socialFortune.socialAdvice,
+ nobleLuck: socialFortune.nobleLuck,
+ bestSocialTime: socialFortune.bestSocialTime,
+ suitableOccasion: socialFortune.suitableOccasion,
+ interpersonalAdvice: socialFortune.interpersonalAdvice,
+ travelScore: travelFortune.travelScore,
+ overallTravelLuck: travelFortune.overallTravelLuck,
+ safetyIndex: travelFortune.safetyIndex,
+ smoothnessIndex: travelFortune.smoothnessIndex,
+ travelAdvice: travelFortune.travelAdvice,
+ bestTravelTime: travelFortune.bestTravelTime,
+ suitableTransport: travelFortune.suitableTransport,
+ precautions: travelFortune.precautions,
+ nobleDirection: travelFortune.nobleDirection,
+ }
+}
+
+export function generateComprehensiveMonthlyFortune(
+ chart: ZiweiChart,
+ year: number,
+ month: number,
+): MonthlyFortune {
+ const baseFortune = generateMonthlyFortune(chart, year, month)
+
+ const midMonthDate = new Date(year, month - 1, 15)
+ const loveFortune = analyzeLoveFortune(chart, midMonthDate)
+ const studyFortune = analyzeStudyFortune(chart, midMonthDate)
+ const socialFortune = analyzeSocialFortune(chart, midMonthDate)
+ const travelFortune = analyzeTravelFortune(chart, midMonthDate)
+
+ return {
+ ...baseFortune,
+ loveScore: loveFortune.loveScore,
+ overallLoveLuck: loveFortune.overallLoveLuck,
+ peachBlossomIndex: loveFortune.peachBlossomIndex,
+ singleAdvice: loveFortune.singleAdvice,
+ datingAdvice: loveFortune.datingAdvice,
+ marriageAdvice: loveFortune.marriageAdvice,
+ bestDatingTime: loveFortune.bestDatingTime,
+ studyScore: studyFortune.studyScore,
+ overallStudyLuck: studyFortune.overallStudyLuck,
+ focusIndex: studyFortune.focusIndex,
+ memoryIndex: studyFortune.memoryIndex,
+ studyAdvice: studyFortune.studyAdvice,
+ examLuck: studyFortune.examLuck,
+ bestStudyTime: studyFortune.bestStudyTime,
+ suitableSubjects: studyFortune.suitableSubjects,
+ studyEnvironmentAdvice: studyFortune.studyEnvironmentAdvice,
+ socialScore: socialFortune.socialScore,
+ overallSocialLuck: socialFortune.overallSocialLuck,
+ popularityIndex: socialFortune.popularityIndex,
+ communicationAbility: socialFortune.communicationAbility,
+ socialAdvice: socialFortune.socialAdvice,
+ nobleLuck: socialFortune.nobleLuck,
+ bestSocialTime: socialFortune.bestSocialTime,
+ suitableOccasion: socialFortune.suitableOccasion,
+ interpersonalAdvice: socialFortune.interpersonalAdvice,
+ travelScore: travelFortune.travelScore,
+ overallTravelLuck: travelFortune.overallTravelLuck,
+ safetyIndex: travelFortune.safetyIndex,
+ smoothnessIndex: travelFortune.smoothnessIndex,
+ travelAdvice: travelFortune.travelAdvice,
+ bestTravelTime: travelFortune.bestTravelTime,
+ suitableTransport: travelFortune.suitableTransport,
+ precautions: travelFortune.precautions,
+ nobleDirection: travelFortune.nobleDirection,
+ }
+}
+
+export function generateComprehensiveYearlyFortune(
+ chart: ZiweiChart,
+ year: number,
+): YearlyFortune {
+ const baseFortune = generateYearlyFortune(chart, year)
+
+ const midYearDate = new Date(year, 6, 1)
+ const loveFortune = analyzeLoveFortune(chart, midYearDate)
+ const studyFortune = analyzeStudyFortune(chart, midYearDate)
+ const socialFortune = analyzeSocialFortune(chart, midYearDate)
+ const travelFortune = analyzeTravelFortune(chart, midYearDate)
+
+ return {
+ ...baseFortune,
+ loveScore: loveFortune.loveScore,
+ overallLoveLuck: loveFortune.overallLoveLuck,
+ peachBlossomIndex: loveFortune.peachBlossomIndex,
+ singleAdvice: loveFortune.singleAdvice,
+ datingAdvice: loveFortune.datingAdvice,
+ marriageAdvice: loveFortune.marriageAdvice,
+ bestDatingTime: loveFortune.bestDatingTime,
+ studyScore: studyFortune.studyScore,
+ overallStudyLuck: studyFortune.overallStudyLuck,
+ focusIndex: studyFortune.focusIndex,
+ memoryIndex: studyFortune.memoryIndex,
+ studyAdvice: studyFortune.studyAdvice,
+ examLuck: studyFortune.examLuck,
+ bestStudyTime: studyFortune.bestStudyTime,
+ suitableSubjects: studyFortune.suitableSubjects,
+ studyEnvironmentAdvice: studyFortune.studyEnvironmentAdvice,
+ socialScore: socialFortune.socialScore,
+ overallSocialLuck: socialFortune.overallSocialLuck,
+ popularityIndex: socialFortune.popularityIndex,
+ communicationAbility: socialFortune.communicationAbility,
+ socialAdvice: socialFortune.socialAdvice,
+ nobleLuck: socialFortune.nobleLuck,
+ bestSocialTime: socialFortune.bestSocialTime,
+ suitableOccasion: socialFortune.suitableOccasion,
+ interpersonalAdvice: socialFortune.interpersonalAdvice,
+ travelScore: travelFortune.travelScore,
+ overallTravelLuck: travelFortune.overallTravelLuck,
+ safetyIndex: travelFortune.safetyIndex,
+ smoothnessIndex: travelFortune.smoothnessIndex,
+ travelAdvice: travelFortune.travelAdvice,
+ bestTravelTime: travelFortune.bestTravelTime,
+ suitableTransport: travelFortune.suitableTransport,
+ precautions: travelFortune.precautions,
+ nobleDirection: travelFortune.nobleDirection,
+ }
+}
diff --git a/everything-is-suitable-uniapp/src/algorithms/fortuneStrategy.ts b/everything-is-suitable-uniapp/src/algorithms/fortuneStrategy.ts
new file mode 100644
index 0000000..57330ad
--- /dev/null
+++ b/everything-is-suitable-uniapp/src/algorithms/fortuneStrategy.ts
@@ -0,0 +1,347 @@
+import { PalaceType, StarNature, TransformationType } from './enums'
+import { calculatePalaceScore, determineLuckLevel } from './ziweiAlgorithm'
+import type { ZiweiChart, Palace, PalaceFortune, DailyFortune, MonthlyFortune, YearlyFortune } from './types'
+
+export function analyzePalaceFortune(palace: Palace, fortuneDate: Date): PalaceFortune {
+ const baseScore = palace.score
+ const adjustment = calculateDailyAdjustment(palace, fortuneDate)
+ const finalScore = Math.max(0, Math.min(100, baseScore + adjustment))
+
+ return {
+ palaceType: palace.palaceType,
+ score: finalScore,
+ luckLevel: determineLuckLevel(finalScore),
+ analysis: generatePalaceAnalysis(palace, finalScore),
+ advice: generatePalaceAdvice(palace.palaceType, finalScore),
+ }
+}
+
+export function calculateDailyAdjustment(palace: Palace, fortuneDate: Date): number {
+ let adjustment = 0
+ const dayOfMonth = fortuneDate.getDate()
+ const month = fortuneDate.getMonth() + 1
+
+ for (const starInfo of palace.majorStars) {
+ if (starInfo.transformation != null) {
+ adjustment += calculateTransformationAdjustment(starInfo.transformation, dayOfMonth)
+ }
+
+ if (starInfo.nature === StarNature.JI) {
+ adjustment += (month === 1 || month === 4 || month === 7 || month === 10) ? 3 : 1
+ } else if (starInfo.nature === StarNature.XIONG) {
+ adjustment -= (month === 2 || month === 5 || month === 8 || month === 11) ? 3 : 1
+ }
+ }
+
+ return adjustment
+}
+
+export function calculateMonthlyAdjustment(palace: Palace, month: number): number {
+ let adjustment = 0
+
+ for (const starInfo of palace.majorStars) {
+ if (starInfo.transformation != null) {
+ adjustment += calculateMonthlyTransformationAdjustment(starInfo.transformation, month)
+ }
+
+ if (starInfo.nature === StarNature.JI) {
+ adjustment += (month % 3 === 1) ? 4 : 2
+ } else if (starInfo.nature === StarNature.XIONG) {
+ adjustment -= (month % 3 === 2) ? 4 : 2
+ }
+ }
+
+ return adjustment
+}
+
+export function calculateYearlyAdjustment(palace: Palace, year: number): number {
+ let adjustment = 0
+
+ for (const starInfo of palace.majorStars) {
+ if (starInfo.transformation != null) {
+ adjustment += calculateYearlyTransformationAdjustment(starInfo.transformation, year)
+ }
+
+ if (starInfo.nature === StarNature.JI) {
+ adjustment += 3
+ } else if (starInfo.nature === StarNature.XIONG) {
+ adjustment -= 3
+ }
+ }
+
+ return adjustment
+}
+
+export function calculateTransformationAdjustment(transformation: TransformationType, dayOfMonth: number): number {
+ switch (transformation) {
+ case TransformationType.LU:
+ return (dayOfMonth % 3 === 0) ? 5 : 2
+ case TransformationType.QUAN:
+ return (dayOfMonth % 7 === 0) ? 5 : 2
+ case TransformationType.KE:
+ return (dayOfMonth % 5 === 0) ? 3 : 1
+ case TransformationType.JI:
+ return (dayOfMonth % 4 === 0) ? -8 : -3
+ default:
+ return 0
+ }
+}
+
+export function calculateMonthlyTransformationAdjustment(transformation: TransformationType, month: number): number {
+ switch (transformation) {
+ case TransformationType.LU:
+ return (month % 3 === 1) ? 6 : 3
+ case TransformationType.QUAN:
+ return (month % 3 === 2) ? 6 : 3
+ case TransformationType.KE:
+ return (month % 2 === 0) ? 4 : 2
+ case TransformationType.JI:
+ return (month % 4 === 0) ? -10 : -5
+ default:
+ return 0
+ }
+}
+
+export function calculateYearlyTransformationAdjustment(transformation: TransformationType, year: number): number {
+ switch (transformation) {
+ case TransformationType.LU:
+ return (year % 3 === 0) ? 8 : 4
+ case TransformationType.QUAN:
+ return (year % 3 === 1) ? 8 : 4
+ case TransformationType.KE:
+ return (year % 2 === 0) ? 5 : 3
+ case TransformationType.JI:
+ return (year % 4 === 0) ? -12 : -6
+ default:
+ return 0
+ }
+}
+
+export function generatePalaceAnalysis(palace: Palace, score: number): string {
+ const parts: string[] = []
+ parts.push(`${PalaceType.name(palace.palaceType)}位于${palace.earthlyBranch}宫`)
+
+ if (palace.majorStars.length > 0) {
+ parts.push(',主星配置:')
+ for (const starInfo of palace.majorStars) {
+ parts.push(starInfo.star)
+ if (starInfo.transformation != null) {
+ parts.push(`(${TransformationType.name(starInfo.transformation)})`)
+ }
+ parts.push(' ')
+ }
+ }
+
+ parts.push(`。运势评分:${score}分`)
+ return parts.join('')
+}
+
+export function generatePalaceAdvice(palaceType: PalaceType, score: number): string {
+ if (score >= 70) {
+ return getPositiveAdvice(palaceType)
+ } else if (score >= 50) {
+ return getNeutralAdvice(palaceType)
+ } else {
+ return getNegativeAdvice(palaceType)
+ }
+}
+
+function getPositiveAdvice(palaceType: PalaceType): string {
+ const adviceMap: Record = {
+ [PalaceType.MING]: '今日状态极佳,适合开展新计划',
+ [PalaceType.XIONG]: '与朋友相处融洽,可拓展人脉',
+ [PalaceType.FUQI]: '感情运势良好,适合表达爱意',
+ [PalaceType.ZINV]: '与子女关系和谐,可增进感情',
+ [PalaceType.CAI]: '财运亨通,可进行投资理财',
+ [PalaceType.JILU]: '身体健康,精力充沛',
+ [PalaceType.QIAN]: '出行顺利,可安排外出计划',
+ [PalaceType.GUANLU]: '工作顺利,可争取更多机会',
+ [PalaceType.TUDI]: '事业运佳,适合拓展业务',
+ [PalaceType.FUBEN]: '贵人运旺,可获得他人帮助',
+ [PalaceType.SHEN]: '心情愉悦,适合社交活动',
+ [PalaceType.FU]: '与父母关系和谐,可多沟通',
+ }
+ return adviceMap[palaceType] ?? '运势良好,保持积极心态'
+}
+
+function getNeutralAdvice(palaceType: PalaceType): string {
+ const adviceMap: Record = {
+ [PalaceType.MING]: '状态平稳,按计划行事即可',
+ [PalaceType.XIONG]: '与朋友关系一般,保持正常交往',
+ [PalaceType.FUQI]: '感情平稳,多关心对方',
+ [PalaceType.ZINV]: '与子女关系正常,保持沟通',
+ [PalaceType.CAI]: '财运平稳,谨慎理财',
+ [PalaceType.JILU]: '身体状况一般,注意休息',
+ [PalaceType.QIAN]: '出行运势一般,谨慎安排',
+ [PalaceType.GUANLU]: '工作平稳,做好本职工作',
+ [PalaceType.TUDI]: '事业运一般,稳扎稳打',
+ [PalaceType.FUBEN]: '贵人运一般,依靠自身努力',
+ [PalaceType.SHEN]: '心情平稳,保持平常心',
+ [PalaceType.FU]: '与父母关系正常,保持联系',
+ }
+ return adviceMap[palaceType] ?? '运势平稳,保持现状'
+}
+
+function getNegativeAdvice(palaceType: PalaceType): string {
+ const adviceMap: Record = {
+ [PalaceType.MING]: '状态欠佳,避免重要决策',
+ [PalaceType.XIONG]: '与朋友关系紧张,避免冲突',
+ [PalaceType.FUQI]: '感情运势欠佳,多包容理解',
+ [PalaceType.ZINV]: '与子女关系紧张,耐心沟通',
+ [PalaceType.CAI]: '财运不佳,避免大额投资',
+ [PalaceType.JILU]: '身体欠佳,注意保养',
+ [PalaceType.QIAN]: '出行不利,减少外出',
+ [PalaceType.GUANLU]: '工作压力大,调整心态',
+ [PalaceType.TUDI]: '事业运不佳,谨慎行事',
+ [PalaceType.FUBEN]: '贵人运弱,依靠自己',
+ [PalaceType.SHEN]: '心情不佳,调节情绪',
+ [PalaceType.FU]: '与父母关系紧张,多理解包容',
+ }
+ return adviceMap[palaceType] ?? '运势欠佳,谨慎行事'
+}
+
+export function calculateOverallScore(palaceFortunes: Map | Record): number {
+ const values = palaceFortunes instanceof Map
+ ? [...palaceFortunes.values()]
+ : Object.values(palaceFortunes)
+
+ if (values.length === 0) return 60
+
+ const totalScore = values.reduce((sum, pf) => sum + pf.score, 0)
+ return Math.floor(totalScore / values.length)
+}
+
+export function generateDailyFortune(chart: ZiweiChart, date: Date): DailyFortune {
+ const palaceFortunes: Record = {}
+
+ for (const palace of chart.palaces) {
+ palaceFortunes[palace.palaceType] = analyzePalaceFortune(palace, date)
+ }
+
+ const overallScore = calculateOverallScore(palaceFortunes)
+
+ return {
+ fortuneDate: date.toISOString().slice(0, 10),
+ palaceFortunes,
+ overallScore,
+ overallLuck: determineLuckLevel(overallScore),
+ careerAdvice: generateDailyCareerAdvice(chart, date),
+ wealthAdvice: generateDailyWealthAdvice(chart, date),
+ relationshipAdvice: generateDailyRelationshipAdvice(chart, date),
+ healthAdvice: generateDailyHealthAdvice(chart, date),
+ luckyColor: '',
+ luckyNumber: '',
+ luckyDirection: '',
+ }
+}
+
+function generateDailyCareerAdvice(chart: ZiweiChart, date: Date): string {
+ const careerPalace = chart.palaces.find(p => p.palaceType === PalaceType.GUANLU)
+ if (careerPalace == null) return '事业运势不明,谨慎行事'
+ if (careerPalace.score >= 70) {
+ return '今日事业运势吉显,适合处理重要工作事务,可主动争取机会,宜把握良机,必有收获'
+ } else if (careerPalace.score >= 60) {
+ return '今日事业运势平吉,按计划完成工作即可,避免冒险决策,宜稳中求进,循序渐进'
+ } else {
+ return '今日事业运势欠佳,建议处理常规工作,避免重要决策和冲突,宜趋吉避凶,待时而动'
+ }
+}
+
+function generateDailyWealthAdvice(chart: ZiweiChart, date: Date): string {
+ const wealthPalace = chart.palaces.find(p => p.palaceType === PalaceType.CAI)
+ if (wealthPalace == null) return '财运不明,谨慎理财'
+ if (wealthPalace.score >= 70) {
+ return '今日财运吉显,适合进行理财规划,可考虑小额投资,宜把握良机,必有收获'
+ } else if (wealthPalace.score >= 60) {
+ return '今日财运平吉,建议保持理性消费,谨慎理财,宜稳中求进,循序渐进'
+ } else {
+ return '今日财运欠佳,建议避免大额支出和投资,保守理财,宜趋吉避凶,待时而动'
+ }
+}
+
+function generateDailyRelationshipAdvice(chart: ZiweiChart, date: Date): string {
+ const relationshipPalace = chart.palaces.find(p => p.palaceType === PalaceType.FUQI)
+ if (relationshipPalace == null) return '感情运势不明,保持平常心'
+ if (relationshipPalace.score >= 70) {
+ return '今日感情运势吉显,适合表达爱意,增进感情交流,宜把握良机,必有收获'
+ } else if (relationshipPalace.score >= 60) {
+ return '今日感情运势平吉,保持正常沟通,多关心对方,宜稳中求进,循序渐进'
+ } else {
+ return '今日感情运势欠佳,避免争吵,多包容理解对方,宜趋吉避凶,待时而动'
+ }
+}
+
+function generateDailyHealthAdvice(chart: ZiweiChart, date: Date): string {
+ const healthPalace = chart.palaces.find(p => p.palaceType === PalaceType.JILU)
+ if (healthPalace == null) return '健康运势不明,注意休息'
+ if (healthPalace.score >= 70) {
+ return '今日身体状况吉显,精力充沛,可进行适度运动,宜保持良好习惯,适度锻炼'
+ } else if (healthPalace.score >= 60) {
+ return '今日身体状况平吉,注意劳逸结合,保持规律作息,宜稳中求进,循序渐进'
+ } else {
+ return '今日身体状况欠佳,注意休息,避免剧烈运动和熬夜,宜修身养性,待时而动'
+ }
+}
+
+export function generateMonthlyFortune(chart: ZiweiChart, year: number, month: number): MonthlyFortune {
+ const palaceFortunes: Record = {}
+
+ for (const palace of chart.palaces) {
+ const baseScore = palace.score
+ const adjustment = calculateMonthlyAdjustment(palace, month)
+ const finalScore = Math.max(0, Math.min(100, baseScore + adjustment))
+
+ palaceFortunes[palace.palaceType] = {
+ palaceType: palace.palaceType,
+ score: finalScore,
+ luckLevel: determineLuckLevel(finalScore),
+ analysis: generatePalaceAnalysis(palace, finalScore),
+ advice: generatePalaceAdvice(palace.palaceType, finalScore),
+ }
+ }
+
+ const overallScore = calculateOverallScore(palaceFortunes)
+
+ return {
+ year,
+ month,
+ palaceFortunes,
+ overallScore,
+ overallLuck: determineLuckLevel(overallScore),
+ careerAdvice: '',
+ wealthAdvice: '',
+ relationshipAdvice: '',
+ healthAdvice: '',
+ }
+}
+
+export function generateYearlyFortune(chart: ZiweiChart, year: number): YearlyFortune {
+ const palaceFortunes: Record = {}
+
+ for (const palace of chart.palaces) {
+ const baseScore = palace.score
+ const adjustment = calculateYearlyAdjustment(palace, year)
+ const finalScore = Math.max(0, Math.min(100, baseScore + adjustment))
+
+ palaceFortunes[palace.palaceType] = {
+ palaceType: palace.palaceType,
+ score: finalScore,
+ luckLevel: determineLuckLevel(finalScore),
+ analysis: generatePalaceAnalysis(palace, finalScore),
+ advice: generatePalaceAdvice(palace.palaceType, finalScore),
+ }
+ }
+
+ const overallScore = calculateOverallScore(palaceFortunes)
+
+ return {
+ year,
+ palaceFortunes,
+ overallScore,
+ overallLuck: determineLuckLevel(overallScore),
+ careerAdvice: '',
+ wealthAdvice: '',
+ relationshipAdvice: '',
+ healthAdvice: '',
+ }
+}
diff --git a/everything-is-suitable-uniapp/src/algorithms/sanFangSiZheng.ts b/everything-is-suitable-uniapp/src/algorithms/sanFangSiZheng.ts
new file mode 100644
index 0000000..c5b34be
--- /dev/null
+++ b/everything-is-suitable-uniapp/src/algorithms/sanFangSiZheng.ts
@@ -0,0 +1,81 @@
+import { EarthlyBranch, PalaceType } from './enums'
+import type { ZiweiChart, Palace, SanFangSiZheng } from './types'
+
+function calculateCaiBoGong(mingGongBranch: EarthlyBranch): EarthlyBranch {
+ const mingGongIndex = EarthlyBranch.index(mingGongBranch)
+ let caiBoGongIndex = mingGongIndex + 5
+ if (caiBoGongIndex > 12) {
+ caiBoGongIndex -= 12
+ }
+ return EarthlyBranch.fromIndex(caiBoGongIndex)
+}
+
+function calculateGuanLuGong(mingGongBranch: EarthlyBranch): EarthlyBranch {
+ const mingGongIndex = EarthlyBranch.index(mingGongBranch)
+ let guanLuGongIndex = mingGongIndex + 10
+ if (guanLuGongIndex > 12) {
+ guanLuGongIndex -= 12
+ }
+ return EarthlyBranch.fromIndex(guanLuGongIndex)
+}
+
+function calculateQianYiGong(mingGongBranch: EarthlyBranch): EarthlyBranch {
+ const mingGongIndex = EarthlyBranch.index(mingGongBranch)
+ let qianYiGongIndex = mingGongIndex + 6
+ if (qianYiGongIndex > 12) {
+ qianYiGongIndex -= 12
+ }
+ return EarthlyBranch.fromIndex(qianYiGongIndex)
+}
+
+export function calculateSanFangSiZheng(chart: ZiweiChart): SanFangSiZheng | null {
+ const mingGong = chart.palaces.find(p => p.palaceType === PalaceType.MING)
+ if (mingGong == null) {
+ return null
+ }
+
+ const mingGongBranch = mingGong.earthlyBranch
+ const caiBoGongBranch = calculateCaiBoGong(mingGongBranch)
+ const guanLuGongBranch = calculateGuanLuGong(mingGongBranch)
+ const qianYiGongBranch = calculateQianYiGong(mingGongBranch)
+
+ const caiBoGong = chart.palaces.find(p => p.earthlyBranch === caiBoGongBranch) ?? null
+ const guanLuGong = chart.palaces.find(p => p.earthlyBranch === guanLuGongBranch) ?? null
+ const qianYiGong = chart.palaces.find(p => p.earthlyBranch === qianYiGongBranch) ?? null
+
+ if (caiBoGong == null || guanLuGong == null || qianYiGong == null) {
+ return null
+ }
+
+ const sanFang = [mingGong, caiBoGong, guanLuGong]
+ const siZheng = [mingGong, caiBoGong, guanLuGong, qianYiGong]
+
+ const sanFangScore = Math.floor(sanFang.reduce((sum, p) => sum + p.score, 0) / 3)
+ const siZhengScore = Math.floor(siZheng.reduce((sum, p) => sum + p.score, 0) / 4)
+
+ return {
+ mingGong,
+ caiBoGong,
+ guanLuGong,
+ qianYiGong,
+ sanFangScore,
+ siZhengScore,
+ }
+}
+
+export function analyzeSanFangSiZheng(sfz: SanFangSiZheng): string {
+ const lines: string[] = []
+ lines.push('【三方四正分析】')
+ lines.push('')
+ lines.push('【三方分析】')
+ lines.push(`命宫:${PalaceType.name(sfz.mingGong.palaceType)}(${EarthlyBranch.name(sfz.mingGong.earthlyBranch)}),得分:${sfz.mingGong.score}`)
+ lines.push(`财帛宫:${PalaceType.name(sfz.caiBoGong.palaceType)}(${EarthlyBranch.name(sfz.caiBoGong.earthlyBranch)}),得分:${sfz.caiBoGong.score}`)
+ lines.push(`官禄宫:${PalaceType.name(sfz.guanLuGong.palaceType)}(${EarthlyBranch.name(sfz.guanLuGong.earthlyBranch)}),得分:${sfz.guanLuGong.score}`)
+ lines.push(`三方平均分:${sfz.sanFangScore}`)
+ lines.push('')
+ lines.push('【四正分析】')
+ lines.push(`迁移宫:${PalaceType.name(sfz.qianYiGong.palaceType)}(${EarthlyBranch.name(sfz.qianYiGong.earthlyBranch)}),得分:${sfz.qianYiGong.score}`)
+ lines.push(`四正平均分:${sfz.siZhengScore}`)
+
+ return lines.join('\n')
+}
diff --git a/everything-is-suitable-uniapp/src/algorithms/types.ts b/everything-is-suitable-uniapp/src/algorithms/types.ts
new file mode 100644
index 0000000..a2b2b95
--- /dev/null
+++ b/everything-is-suitable-uniapp/src/algorithms/types.ts
@@ -0,0 +1,305 @@
+import type {
+ HeavenlyStem,
+ EarthlyBranch,
+ MajorStar,
+ StarNature,
+ TransformationType,
+ PalaceType,
+} from './enums'
+
+export interface LunarDate {
+ lunarYear: string
+ lunarMonth: string
+ lunarDay: string
+ isLeapMonth: boolean
+ zodiac: string
+ solarTerm: string
+ weekday: string
+}
+
+export interface BirthInfo {
+ birthTime: string
+ yearStem: HeavenlyStem
+ monthStem: HeavenlyStem
+ dayStem: HeavenlyStem
+ hourStem: HeavenlyStem
+ yearBranch: EarthlyBranch
+ monthBranch: EarthlyBranch
+ dayBranch: EarthlyBranch
+ hourBranch: EarthlyBranch
+ gender: 'MALE' | 'FEMALE'
+ timezone: string
+ longitude?: number
+ latitude?: number
+ trueSolarTime?: string
+ lunarDate?: LunarDate
+}
+
+export interface StarInfo {
+ star: MajorStar
+ nature: StarNature
+ transformation: TransformationType | null
+ brightness: number
+ description: string
+}
+
+export interface Palace {
+ palaceType: PalaceType
+ earthlyBranch: EarthlyBranch
+ majorStars: StarInfo[]
+ minorStars: string[]
+ analysis: string
+ score: number
+}
+
+export interface SanFangSiZheng {
+ mingGong: Palace
+ caiBoGong: Palace
+ guanLuGong: Palace
+ qianYiGong: Palace
+ sanFangScore: number
+ siZhengScore: number
+}
+
+export interface Pattern {
+ name: string
+ description: string
+ type: string
+}
+
+export interface ZiweiChart {
+ birthInfo: BirthInfo
+ palaces: Palace[]
+ yearStem: HeavenlyStem
+ mingGongBranch: EarthlyBranch
+ shenGongBranch: EarthlyBranch
+ summary: string
+ overallLuck: string
+ sanFangSiZheng?: SanFangSiZheng
+ patterns?: Pattern[]
+}
+
+export interface Almanac {
+ solarDate: string
+ lunarDate: LunarDate
+ suitable: string[]
+ unsuitable: string[]
+ godDirection: string
+ joyDirection: string
+ fortuneDirection: string
+ nobleDirection: string
+ clash: string
+ evil: string
+ jianChu: string
+ starGod: string
+ naYin: string
+ fetusGod: string
+ pengzuTaboo: string
+}
+
+export interface PalaceFortune {
+ palaceType: PalaceType
+ score: number
+ luckLevel: string
+ analysis: string
+ advice: string
+}
+
+export interface LoveFortuneDetail {
+ loveScore: number
+ overallLoveLuck: string
+ peachBlossomIndex: string
+ singleAdvice: string
+ datingAdvice: string
+ marriageAdvice: string
+ bestDatingTime: string
+}
+
+export interface StudyFortuneDetail {
+ studyScore: number
+ overallStudyLuck: string
+ focusIndex: string
+ memoryIndex: string
+ studyAdvice: string
+ examLuck: string
+ bestStudyTime: string
+ suitableSubjects: string
+ studyEnvironmentAdvice: string
+}
+
+export interface SocialFortuneDetail {
+ socialScore: number
+ overallSocialLuck: string
+ popularityIndex: string
+ communicationAbility: string
+ socialAdvice: string
+ nobleLuck: string
+ bestSocialTime: string
+ suitableOccasion: string
+ interpersonalAdvice: string
+}
+
+export interface TravelFortuneDetail {
+ travelScore: number
+ overallTravelLuck: string
+ safetyIndex: string
+ smoothnessIndex: string
+ travelAdvice: string
+ bestTravelTime: string
+ suitableTransport: string
+ precautions: string
+ nobleDirection: string
+}
+
+export interface DailyFortune {
+ fortuneDate: string
+ overallLuck: string
+ overallScore: number
+ palaceFortunes: Record
+ careerAdvice: string
+ wealthAdvice: string
+ relationshipAdvice: string
+ healthAdvice: string
+ luckyColor: string
+ luckyNumber: string
+ luckyDirection: string
+ loveScore?: number
+ overallLoveLuck?: string
+ peachBlossomIndex?: string
+ singleAdvice?: string
+ datingAdvice?: string
+ marriageAdvice?: string
+ bestDatingTime?: string
+ studyScore?: number
+ overallStudyLuck?: string
+ focusIndex?: string
+ memoryIndex?: string
+ studyAdvice?: string
+ examLuck?: string
+ bestStudyTime?: string
+ suitableSubjects?: string
+ studyEnvironmentAdvice?: string
+ socialScore?: number
+ overallSocialLuck?: string
+ popularityIndex?: string
+ communicationAbility?: string
+ socialAdvice?: string
+ nobleLuck?: string
+ bestSocialTime?: string
+ suitableOccasion?: string
+ interpersonalAdvice?: string
+ travelScore?: number
+ overallTravelLuck?: string
+ safetyIndex?: string
+ smoothnessIndex?: string
+ travelAdvice?: string
+ bestTravelTime?: string
+ suitableTransport?: string
+ precautions?: string
+ nobleDirection?: string
+}
+
+export interface MonthlyFortune {
+ fortuneMonth: string
+ overallLuck: string
+ overallScore: number
+ palaceFortunes: Record
+ dailyFortunes: DailyFortune[]
+ careerAdvice: string
+ wealthAdvice: string
+ relationshipAdvice: string
+ healthAdvice: string
+ luckyColor: string
+ luckyNumber: string
+ luckyDirection: string
+ keyFocus: string
+ cautionAdvice: string
+ loveScore?: number
+ overallLoveLuck?: string
+ peachBlossomIndex?: string
+ singleAdvice?: string
+ datingAdvice?: string
+ marriageAdvice?: string
+ bestDatingTime?: string
+ studyScore?: number
+ overallStudyLuck?: string
+ focusIndex?: string
+ memoryIndex?: string
+ studyAdvice?: string
+ examLuck?: string
+ bestStudyTime?: string
+ suitableSubjects?: string
+ studyEnvironmentAdvice?: string
+ socialScore?: number
+ overallSocialLuck?: string
+ popularityIndex?: string
+ communicationAbility?: string
+ socialAdvice?: string
+ nobleLuck?: string
+ bestSocialTime?: string
+ suitableOccasion?: string
+ interpersonalAdvice?: string
+ travelScore?: number
+ overallTravelLuck?: string
+ safetyIndex?: string
+ smoothnessIndex?: string
+ travelAdvice?: string
+ bestTravelTime?: string
+ suitableTransport?: string
+ precautions?: string
+ nobleDirection?: string
+}
+
+export interface YearlyFortune {
+ fortuneYear: number
+ overallLuck: string
+ overallScore: number
+ palaceFortunes: Record
+ monthlyFortunes: MonthlyFortune[]
+ careerAdvice: string
+ wealthAdvice: string
+ relationshipAdvice: string
+ healthAdvice: string
+ luckyColor: string
+ luckyNumber: string
+ luckyDirection: string
+ keyFocus: string
+ cautionAdvice: string
+ yearlyTheme: string
+ majorOpportunity: string
+ majorChallenge: string
+ loveScore?: number
+ overallLoveLuck?: string
+ peachBlossomIndex?: string
+ singleAdvice?: string
+ datingAdvice?: string
+ marriageAdvice?: string
+ bestDatingTime?: string
+ studyScore?: number
+ overallStudyLuck?: string
+ focusIndex?: string
+ memoryIndex?: string
+ studyAdvice?: string
+ examLuck?: string
+ bestStudyTime?: string
+ suitableSubjects?: string
+ studyEnvironmentAdvice?: string
+ socialScore?: number
+ overallSocialLuck?: string
+ popularityIndex?: string
+ communicationAbility?: string
+ socialAdvice?: string
+ nobleLuck?: string
+ bestSocialTime?: string
+ suitableOccasion?: string
+ interpersonalAdvice?: string
+ travelScore?: number
+ overallTravelLuck?: string
+ safetyIndex?: string
+ smoothnessIndex?: string
+ travelAdvice?: string
+ bestTravelTime?: string
+ suitableTransport?: string
+ precautions?: string
+ nobleDirection?: string
+}
diff --git a/everything-is-suitable-uniapp/src/algorithms/ziweiAlgorithm.ts b/everything-is-suitable-uniapp/src/algorithms/ziweiAlgorithm.ts
new file mode 100644
index 0000000..248f65f
--- /dev/null
+++ b/everything-is-suitable-uniapp/src/algorithms/ziweiAlgorithm.ts
@@ -0,0 +1,275 @@
+import { MajorStar, StarNature, TransformationType, EarthlyBranch, PalaceType } from './enums'
+import type { StarInfo, Palace, ZiweiChart } from './types'
+
+const STAR_NATURE_MAP = new Map([
+ [MajorStar.ZIWEI, StarNature.JI],
+ [MajorStar.TIANJI, StarNature.JI],
+ [MajorStar.TAIYANG, StarNature.JI],
+ [MajorStar.WUQU, StarNature.XIONG],
+ [MajorStar.TIANTONG, StarNature.JI],
+ [MajorStar.LIANCHEN, StarNature.XIONG],
+ [MajorStar.TIANFU, StarNature.JI],
+ [MajorStar.TAIYIN, StarNature.ZHONGHE],
+ [MajorStar.TANLANG, StarNature.XIONG],
+ [MajorStar.JUMEN, StarNature.XIONG],
+ [MajorStar.TIANXIANG, StarNature.JI],
+ [MajorStar.TIANLIANG, StarNature.ZHONGHE],
+ [MajorStar.QISHA, StarNature.XIONG],
+ [MajorStar.POJUN, StarNature.XIONG],
+])
+
+const STAR_BASE_SCORE_MAP = new Map([
+ [MajorStar.ZIWEI, 85],
+ [MajorStar.TIANJI, 75],
+ [MajorStar.TAIYANG, 80],
+ [MajorStar.WUQU, 75],
+ [MajorStar.TIANTONG, 70],
+ [MajorStar.LIANCHEN, 55],
+ [MajorStar.TIANFU, 85],
+ [MajorStar.TAIYIN, 75],
+ [MajorStar.TANLANG, 60],
+ [MajorStar.JUMEN, 55],
+ [MajorStar.TIANXIANG, 75],
+ [MajorStar.TIANLIANG, 75],
+ [MajorStar.QISHA, 60],
+ [MajorStar.POJUN, 55],
+])
+
+const TRANSFORMATION_SCORE_MAP = new Map([
+ [TransformationType.LU, 15],
+ [TransformationType.QUAN, 10],
+ [TransformationType.KE, 8],
+ [TransformationType.JI, -15],
+])
+
+const BRANCH_COLOR_MAP = new Map([
+ [EarthlyBranch.ZI, '黑色'],
+ [EarthlyBranch.CHOU, '黄色'],
+ [EarthlyBranch.YIN, '绿色'],
+ [EarthlyBranch.MAO, '绿色'],
+ [EarthlyBranch.CHEN, '黄色'],
+ [EarthlyBranch.SI, '红色'],
+ [EarthlyBranch.WU, '红色'],
+ [EarthlyBranch.WEI, '黄色'],
+ [EarthlyBranch.SHEN, '白色'],
+ [EarthlyBranch.YOU, '白色'],
+ [EarthlyBranch.XU, '黄色'],
+ [EarthlyBranch.HAI, '黑色'],
+])
+
+const BRANCH_DIRECTION_MAP = new Map([
+ [EarthlyBranch.ZI, '正北'],
+ [EarthlyBranch.CHOU, '东北偏北'],
+ [EarthlyBranch.YIN, '东北偏东'],
+ [EarthlyBranch.MAO, '正东'],
+ [EarthlyBranch.CHEN, '东南偏东'],
+ [EarthlyBranch.SI, '东南偏南'],
+ [EarthlyBranch.WU, '正南'],
+ [EarthlyBranch.WEI, '西南偏南'],
+ [EarthlyBranch.SHEN, '西南偏西'],
+ [EarthlyBranch.YOU, '正西'],
+ [EarthlyBranch.XU, '西北偏西'],
+ [EarthlyBranch.HAI, '西北偏北'],
+])
+
+const LUCKY_COLORS = ['红色', '黄色', '蓝色', '绿色', '紫色', '金色', '白色', '黑色']
+
+const LUCKY_DIRECTIONS = ['东方', '南方', '西方', '北方', '东南', '西南', '东北', '西北']
+
+export function getStarNature(star: MajorStar): StarNature {
+ return STAR_NATURE_MAP.get(star) ?? StarNature.ZHONGHE
+}
+
+export function calculateStarScore(starInfo: StarInfo | null): number {
+ if (starInfo == null || starInfo.star == null) {
+ return 50
+ }
+
+ const star = starInfo.star
+ let baseScore = STAR_BASE_SCORE_MAP.get(star) ?? 50
+
+ const transformation = starInfo.transformation
+ if (transformation != null) {
+ const transScore = TRANSFORMATION_SCORE_MAP.get(transformation) ?? 0
+ baseScore += transScore
+ }
+
+ const brightness = starInfo.brightness
+ if (brightness > 0 && brightness <= 100) {
+ const brightnessFactor = brightness / 100.0
+ baseScore = Math.floor(baseScore * brightnessFactor)
+ }
+
+ return Math.max(0, Math.min(100, baseScore))
+}
+
+export function calculatePalaceScore(palace: Palace | null): number {
+ if (palace == null) {
+ return 50
+ }
+
+ let baseScore = 50
+ const majorStars = palace.majorStars
+
+ if (majorStars == null || majorStars.length === 0) {
+ return baseScore
+ }
+
+ let starScoreSum = 0
+ let starCount = 0
+
+ for (const starInfo of majorStars) {
+ if (starInfo == null || starInfo.star == null) {
+ continue
+ }
+
+ const starScore = calculateStarScore(starInfo)
+ starScoreSum += starScore
+ starCount++
+ }
+
+ if (starCount > 0) {
+ baseScore = Math.floor(starScoreSum / starCount)
+ }
+
+ const finalScore = Math.max(0, Math.min(100, baseScore))
+ palace.score = finalScore
+
+ return finalScore
+}
+
+export function calculateLuckyColor(chart: ZiweiChart, fortuneDate: Date): string {
+ if (chart == null || fortuneDate == null) {
+ return '红色'
+ }
+
+ const mingGongBranch = chart.mingGongBranch
+ if (mingGongBranch == null) {
+ return '红色'
+ }
+
+ const baseColor = BRANCH_COLOR_MAP.get(mingGongBranch) ?? '红色'
+
+ const mingGong = chart.palaces.find(p => p.palaceType === PalaceType.MING)
+ if (mingGong == null || mingGong.majorStars.length === 0) {
+ return baseColor
+ }
+
+ const mainStar = mingGong.majorStars[0]
+ const starNature = MajorStar.nature(mainStar.star)
+
+ let baseIndex = 0
+ for (let i = 0; i < LUCKY_COLORS.length; i++) {
+ if (LUCKY_COLORS[i] === baseColor) {
+ baseIndex = i
+ break
+ }
+ }
+
+ let natureOffset = 0
+ if (starNature === StarNature.JI) {
+ natureOffset = 1
+ } else if (starNature === StarNature.XIONG) {
+ natureOffset = -1
+ }
+
+ const dayOfMonth = fortuneDate.getDate()
+ const dateOffset = dayOfMonth % 3
+
+ const colorIndex = ((baseIndex + natureOffset + dateOffset) % LUCKY_COLORS.length + LUCKY_COLORS.length) % LUCKY_COLORS.length
+
+ return LUCKY_COLORS[colorIndex]
+}
+
+export function calculateLuckyNumber(chart: ZiweiChart, fortuneDate: Date): string {
+ if (chart == null || fortuneDate == null) {
+ return '8'
+ }
+
+ const dayOfMonth = fortuneDate.getDate()
+ const monthValue = fortuneDate.getMonth() + 1
+
+ let baseNumber = (dayOfMonth + monthValue) % 9
+ if (baseNumber === 0) {
+ baseNumber = 9
+ }
+
+ const mingGongBranch = chart.mingGongBranch
+ if (mingGongBranch == null) {
+ return String(baseNumber)
+ }
+
+ const branchIndex = EarthlyBranch.index(mingGongBranch)
+
+ const mingGong = chart.palaces.find(p => p.palaceType === PalaceType.MING)
+ let starFactor = 0
+ if (mingGong != null && mingGong.majorStars.length > 0) {
+ const mainStar = mingGong.majorStars[0]
+ const starNature = MajorStar.nature(mainStar.star)
+ if (starNature === StarNature.JI) {
+ starFactor = 1
+ } else if (starNature === StarNature.XIONG) {
+ starFactor = -1
+ }
+ }
+
+ let luckyNumber = (baseNumber + branchIndex + starFactor) % 9
+ if (luckyNumber === 0) {
+ luckyNumber = 9
+ }
+
+ return String(luckyNumber)
+}
+
+export function calculateLuckyDirection(chart: ZiweiChart, fortuneDate: Date): string {
+ if (chart == null || fortuneDate == null) {
+ return '东方'
+ }
+
+ const mingGongBranch = chart.mingGongBranch
+ if (mingGongBranch == null) {
+ return '东方'
+ }
+
+ const baseDirection = BRANCH_DIRECTION_MAP.get(mingGongBranch) ?? '东方'
+
+ const mingGong = chart.palaces.find(p => p.palaceType === PalaceType.MING)
+ if (mingGong == null || mingGong.majorStars.length === 0) {
+ return baseDirection
+ }
+
+ const mainStar = mingGong.majorStars[0]
+ const starNature = MajorStar.nature(mainStar.star)
+
+ let baseIndex = 0
+ for (let i = 0; i < LUCKY_DIRECTIONS.length; i++) {
+ if (LUCKY_DIRECTIONS[i] === baseDirection) {
+ baseIndex = i
+ break
+ }
+ }
+
+ let natureOffset = 0
+ if (starNature === StarNature.JI) {
+ natureOffset = 1
+ } else if (starNature === StarNature.XIONG) {
+ natureOffset = -1
+ }
+
+ const dayOfMonth = fortuneDate.getDate()
+ const dateOffset = dayOfMonth % 2
+
+ const directionIndex = ((baseIndex + natureOffset + dateOffset) % LUCKY_DIRECTIONS.length + LUCKY_DIRECTIONS.length) % LUCKY_DIRECTIONS.length
+
+ return LUCKY_DIRECTIONS[directionIndex]
+}
+
+export function determineLuckLevel(score: number): string {
+ if (score >= 90) return '大吉'
+ if (score >= 80) return '吉'
+ if (score >= 70) return '中吉'
+ if (score >= 60) return '平'
+ if (score >= 50) return '中平'
+ if (score >= 40) return '小凶'
+ return '凶'
+}
diff --git a/everything-is-suitable-uniapp/src/manifest.json b/everything-is-suitable-uniapp/src/manifest.json
new file mode 100644
index 0000000..5aa97bc
--- /dev/null
+++ b/everything-is-suitable-uniapp/src/manifest.json
@@ -0,0 +1,50 @@
+{
+ "name": "万事宜",
+ "appid": "__UNI__WANYISHI",
+ "description": "中国传统文化研究工具 - 紫微斗数/黄历/运势分析",
+ "versionName": "1.0.0",
+ "versionCode": "100",
+ "transformPx": false,
+ "app-plus": {
+ "distribute": {
+ "android": {
+ "targetSdkVersion": 33,
+ "minSdkVersion": 21,
+ "abiFilters": ["armeabi-v7a", "arm64-v8a"],
+ "permissions": {
+ "": false
+ }
+ },
+ "ios": {
+ "privacyDescription": {
+ "NSPrivacyTracking": false,
+ "NSPrivacyCollectedDataTypes": []
+ }
+ },
+ "splashscreen": {
+ "autoclose": true,
+ "waiting": true
+ }
+ },
+ "privacy": {
+ "prompt": "template",
+ "template": {
+ "title": "隐私政策",
+ "message": "本应用所有数据存储在本地设备,不收集任何用户信息。应用无需网络连接即可正常使用。"
+ }
+ }
+ },
+ "h5": {
+ "title": "万事宜",
+ "router": {
+ "mode": "hash"
+ }
+ },
+ "mp-weixin": {
+ "appid": "",
+ "setting": {
+ "urlCheck": false
+ },
+ "usingComponents": true
+ }
+}
diff --git a/everything-is-suitable-uniapp/src/pages.json b/everything-is-suitable-uniapp/src/pages.json
new file mode 100644
index 0000000..c0a228e
--- /dev/null
+++ b/everything-is-suitable-uniapp/src/pages.json
@@ -0,0 +1,28 @@
+{
+ "pages": [
+ {
+ "path": "pages/almanac-search/index",
+ "style": {
+ "navigationBarTitleText": "黄历搜索"
+ }
+ },
+ {
+ "path": "pages/ziwei/index",
+ "style": {
+ "navigationBarTitleText": "紫微斗数"
+ }
+ },
+ {
+ "path": "pages/fortune/index",
+ "style": {
+ "navigationBarTitleText": "运势分析"
+ }
+ }
+ ],
+ "globalStyle": {
+ "navigationBarTextStyle": "black",
+ "navigationBarTitleText": "万事宜",
+ "navigationBarBackgroundColor": "#ffffff",
+ "backgroundColor": "#f4f4f5"
+ }
+}
diff --git a/everything-is-suitable-uniapp/src/services/almanacService.ts b/everything-is-suitable-uniapp/src/services/almanacService.ts
new file mode 100644
index 0000000..3610321
--- /dev/null
+++ b/everything-is-suitable-uniapp/src/services/almanacService.ts
@@ -0,0 +1,63 @@
+import { getAlmanacByDate, getAlmanacsByRange } from '../algorithms/almanac'
+import type { Almanac } from '../algorithms/types'
+import { LRUCache } from '../utils/lruCache'
+
+export interface AlmanacSearchParams {
+ keyword?: string
+ dateFrom?: string
+ dateTo?: string
+ suitableFilter?: string[]
+ unsuitableFilter?: string[]
+}
+
+export class AlmanacService {
+ private cache = new LRUCache({ maxSize: 365 })
+
+ private calculateAlmanac(dateStr: string): Almanac | null {
+ return getAlmanacByDate(dateStr)
+ }
+
+ async getAlmanacByDate(dateStr: string): Promise {
+ const cached = this.cache.get(dateStr)
+ if (cached) return cached
+
+ const result = this.calculateAlmanac(dateStr)
+ if (result) this.cache.set(dateStr, result)
+ return result
+ }
+
+ async getAlmanacsByRange(startDateStr: string, endDateStr: string): Promise {
+ return getAlmanacsByRange(startDateStr, endDateStr)
+ }
+
+ async searchAlmanacs(params: AlmanacSearchParams): Promise {
+ const { keyword, dateFrom, dateTo, suitableFilter, unsuitableFilter } = params
+
+ const startDate = dateFrom ?? '2026-01-01'
+ const endDate = dateTo ?? '2026-12-31'
+ let results = await this.getAlmanacsByRange(startDate, endDate)
+
+ if (keyword) {
+ results = results.filter(a =>
+ a.suitable.some(s => s.includes(keyword)) ||
+ a.unsuitable.some(s => s.includes(keyword)) ||
+ a.jianChu.includes(keyword) ||
+ a.starGod.includes(keyword),
+ )
+ }
+
+ if (suitableFilter && suitableFilter.length > 0) {
+ results = results.filter(a =>
+ suitableFilter.some(f => a.suitable.includes(f)),
+ )
+ }
+
+ if (unsuitableFilter && unsuitableFilter.length > 0) {
+ results = results.filter(a =>
+ unsuitableFilter.some(f => a.unsuitable.includes(f)),
+ )
+ }
+
+ return results
+ }
+}
diff --git a/everything-is-suitable-uniapp/src/services/fortuneService.ts b/everything-is-suitable-uniapp/src/services/fortuneService.ts
new file mode 100644
index 0000000..c0197e1
--- /dev/null
+++ b/everything-is-suitable-uniapp/src/services/fortuneService.ts
@@ -0,0 +1,232 @@
+import type { ZiweiChart, DailyFortune, MonthlyFortune, YearlyFortune } from '../algorithms/types'
+import {
+ generateDailyFortune,
+ generateMonthlyFortune,
+ generateYearlyFortune,
+} from '../algorithms/fortuneStrategy'
+import {
+ generateComprehensiveFortune,
+ generateComprehensiveMonthlyFortune,
+ generateComprehensiveYearlyFortune,
+} from '../algorithms/fortune'
+import {
+ calculateLuckyColor,
+ calculateLuckyNumber,
+ calculateLuckyDirection,
+} from '../algorithms/ziweiAlgorithm'
+
+export class FortuneService {
+ async getDailyFortune(chart: ZiweiChart, dateStr: string): Promise {
+ try {
+ const date = new Date(dateStr)
+ if (isNaN(date.getTime())) return null
+
+ const fortune = generateDailyFortune(chart, date)
+ fortune.luckyColor = calculateLuckyColor(chart, date)
+ fortune.luckyNumber = calculateLuckyNumber(chart, date)
+ fortune.luckyDirection = calculateLuckyDirection(chart, date)
+ return fortune
+ } catch (e) {
+ console.error(`Daily fortune error for ${dateStr}:`, e)
+ return null
+ }
+ }
+
+ async getMonthlyFortune(chart: ZiweiChart, year: number, month: number): Promise {
+ try {
+ if (month < 1 || month > 12) return null
+
+ const fortune = generateMonthlyFortune(chart, year, month)
+ return {
+ fortuneMonth: `${year}-${String(month).padStart(2, '0')}`,
+ overallLuck: fortune.overallLuck ?? '',
+ overallScore: fortune.overallScore ?? 0,
+ palaceFortunes: fortune.palaceFortunes ?? {},
+ dailyFortunes: [],
+ careerAdvice: fortune.careerAdvice ?? '',
+ wealthAdvice: fortune.wealthAdvice ?? '',
+ relationshipAdvice: fortune.relationshipAdvice ?? '',
+ healthAdvice: fortune.healthAdvice ?? '',
+ luckyColor: '',
+ luckyNumber: '',
+ luckyDirection: '',
+ keyFocus: '',
+ cautionAdvice: '',
+ }
+ } catch (e) {
+ console.error(`Monthly fortune error for ${year}-${month}:`, e)
+ return null
+ }
+ }
+
+ async getYearlyFortune(chart: ZiweiChart, year: number): Promise {
+ try {
+ if (year < 1900 || year > 2100) return null
+
+ const fortune = generateYearlyFortune(chart, year)
+ return {
+ fortuneYear: year,
+ overallLuck: fortune.overallLuck ?? '',
+ overallScore: fortune.overallScore ?? 0,
+ palaceFortunes: fortune.palaceFortunes ?? {},
+ monthlyFortunes: [],
+ careerAdvice: fortune.careerAdvice ?? '',
+ wealthAdvice: fortune.wealthAdvice ?? '',
+ relationshipAdvice: fortune.relationshipAdvice ?? '',
+ healthAdvice: fortune.healthAdvice ?? '',
+ luckyColor: '',
+ luckyNumber: '',
+ luckyDirection: '',
+ keyFocus: '',
+ cautionAdvice: '',
+ yearlyTheme: '',
+ majorOpportunity: '',
+ majorChallenge: '',
+ }
+ } catch (e) {
+ console.error(`Yearly fortune error for ${year}:`, e)
+ return null
+ }
+ }
+
+ async getComprehensiveFortune(chart: ZiweiChart, dateStr: string): Promise {
+ try {
+ const date = new Date(dateStr)
+ if (isNaN(date.getTime())) return null
+
+ const fortune = generateComprehensiveFortune(chart, date)
+ fortune.luckyColor = calculateLuckyColor(chart, date)
+ fortune.luckyNumber = calculateLuckyNumber(chart, date)
+ fortune.luckyDirection = calculateLuckyDirection(chart, date)
+ return fortune
+ } catch (e) {
+ console.error(`Comprehensive fortune error for ${dateStr}:`, e)
+ return null
+ }
+ }
+
+ async getComprehensiveMonthlyFortune(chart: ZiweiChart, year: number, month: number): Promise {
+ try {
+ if (month < 1 || month > 12) return null
+
+ const fortune = generateComprehensiveMonthlyFortune(chart, year, month)
+ return {
+ fortuneMonth: `${year}-${String(month).padStart(2, '0')}`,
+ overallLuck: fortune.overallLuck ?? '',
+ overallScore: fortune.overallScore ?? 0,
+ palaceFortunes: fortune.palaceFortunes ?? {},
+ dailyFortunes: [],
+ careerAdvice: fortune.careerAdvice ?? '',
+ wealthAdvice: fortune.wealthAdvice ?? '',
+ relationshipAdvice: fortune.relationshipAdvice ?? '',
+ healthAdvice: fortune.healthAdvice ?? '',
+ luckyColor: '',
+ luckyNumber: '',
+ luckyDirection: '',
+ keyFocus: '',
+ cautionAdvice: '',
+ loveScore: fortune.loveScore,
+ overallLoveLuck: fortune.overallLoveLuck,
+ peachBlossomIndex: fortune.peachBlossomIndex,
+ singleAdvice: fortune.singleAdvice,
+ datingAdvice: fortune.datingAdvice,
+ marriageAdvice: fortune.marriageAdvice,
+ bestDatingTime: fortune.bestDatingTime,
+ studyScore: fortune.studyScore,
+ overallStudyLuck: fortune.overallStudyLuck,
+ focusIndex: fortune.focusIndex,
+ memoryIndex: fortune.memoryIndex,
+ studyAdvice: fortune.studyAdvice,
+ examLuck: fortune.examLuck,
+ bestStudyTime: fortune.bestStudyTime,
+ suitableSubjects: fortune.suitableSubjects,
+ studyEnvironmentAdvice: fortune.studyEnvironmentAdvice,
+ socialScore: fortune.socialScore,
+ overallSocialLuck: fortune.overallSocialLuck,
+ popularityIndex: fortune.popularityIndex,
+ communicationAbility: fortune.communicationAbility,
+ socialAdvice: fortune.socialAdvice,
+ nobleLuck: fortune.nobleLuck,
+ bestSocialTime: fortune.bestSocialTime,
+ suitableOccasion: fortune.suitableOccasion,
+ interpersonalAdvice: fortune.interpersonalAdvice,
+ travelScore: fortune.travelScore,
+ overallTravelLuck: fortune.overallTravelLuck,
+ safetyIndex: fortune.safetyIndex,
+ smoothnessIndex: fortune.smoothnessIndex,
+ travelAdvice: fortune.travelAdvice,
+ bestTravelTime: fortune.bestTravelTime,
+ suitableTransport: fortune.suitableTransport,
+ precautions: fortune.precautions,
+ nobleDirection: fortune.nobleDirection,
+ }
+ } catch (e) {
+ console.error(`Comprehensive monthly fortune error for ${year}-${month}:`, e)
+ return null
+ }
+ }
+
+ async getComprehensiveYearlyFortune(chart: ZiweiChart, year: number): Promise {
+ try {
+ if (year < 1900 || year > 2100) return null
+
+ const fortune = generateComprehensiveYearlyFortune(chart, year)
+ return {
+ fortuneYear: year,
+ overallLuck: fortune.overallLuck ?? '',
+ overallScore: fortune.overallScore ?? 0,
+ palaceFortunes: fortune.palaceFortunes ?? {},
+ monthlyFortunes: [],
+ careerAdvice: fortune.careerAdvice ?? '',
+ wealthAdvice: fortune.wealthAdvice ?? '',
+ relationshipAdvice: fortune.relationshipAdvice ?? '',
+ healthAdvice: fortune.healthAdvice ?? '',
+ luckyColor: '',
+ luckyNumber: '',
+ luckyDirection: '',
+ keyFocus: '',
+ cautionAdvice: '',
+ yearlyTheme: '',
+ majorOpportunity: '',
+ majorChallenge: '',
+ loveScore: fortune.loveScore,
+ overallLoveLuck: fortune.overallLoveLuck,
+ peachBlossomIndex: fortune.peachBlossomIndex,
+ singleAdvice: fortune.singleAdvice,
+ datingAdvice: fortune.datingAdvice,
+ marriageAdvice: fortune.marriageAdvice,
+ bestDatingTime: fortune.bestDatingTime,
+ studyScore: fortune.studyScore,
+ overallStudyLuck: fortune.overallStudyLuck,
+ focusIndex: fortune.focusIndex,
+ memoryIndex: fortune.memoryIndex,
+ studyAdvice: fortune.studyAdvice,
+ examLuck: fortune.examLuck,
+ bestStudyTime: fortune.bestStudyTime,
+ suitableSubjects: fortune.suitableSubjects,
+ studyEnvironmentAdvice: fortune.studyEnvironmentAdvice,
+ socialScore: fortune.socialScore,
+ overallSocialLuck: fortune.overallSocialLuck,
+ popularityIndex: fortune.popularityIndex,
+ communicationAbility: fortune.communicationAbility,
+ socialAdvice: fortune.socialAdvice,
+ nobleLuck: fortune.nobleLuck,
+ bestSocialTime: fortune.bestSocialTime,
+ suitableOccasion: fortune.suitableOccasion,
+ interpersonalAdvice: fortune.interpersonalAdvice,
+ travelScore: fortune.travelScore,
+ overallTravelLuck: fortune.overallTravelLuck,
+ safetyIndex: fortune.safetyIndex,
+ smoothnessIndex: fortune.smoothnessIndex,
+ travelAdvice: fortune.travelAdvice,
+ bestTravelTime: fortune.bestTravelTime,
+ suitableTransport: fortune.suitableTransport,
+ precautions: fortune.precautions,
+ nobleDirection: fortune.nobleDirection,
+ }
+ } catch (e) {
+ console.error(`Comprehensive yearly fortune error for ${year}:`, e)
+ return null
+ }
+ }
+}
diff --git a/everything-is-suitable-uniapp/src/services/searchService.ts b/everything-is-suitable-uniapp/src/services/searchService.ts
new file mode 100644
index 0000000..2d6fb0c
--- /dev/null
+++ b/everything-is-suitable-uniapp/src/services/searchService.ts
@@ -0,0 +1,104 @@
+import { AlmanacService } from './almanacService'
+import type { Almanac } from '../algorithms/types'
+import type { SearchCondition, SearchRequest, SearchResult } from '../types/search'
+import { storage } from '../utils/storage'
+
+export interface SearchParams {
+ keyword?: string
+ dateFrom?: string
+ dateTo?: string
+ suitableFilter?: string[]
+ unsuitableFilter?: string[]
+}
+
+export interface SearchHistoryItem {
+ keyword: string
+ timestamp: number
+ resultCount: number
+}
+
+function toSearchParams(request: SearchRequest): SearchParams {
+ const suitableFilter: string[] = []
+ const unsuitableFilter: string[] = []
+
+ for (const cond of request.conditions) {
+ if (cond.type === 'suitable') {
+ suitableFilter.push(...cond.items)
+ } else {
+ unsuitableFilter.push(...cond.items)
+ }
+ }
+
+ const today = new Date()
+ const dateFrom = today.toISOString().slice(0, 10)
+ const endDate = new Date(today)
+ endDate.setDate(endDate.getDate() + request.days)
+ const dateTo = endDate.toISOString().slice(0, 10)
+
+ return { suitableFilter, unsuitableFilter, dateFrom, dateTo }
+}
+
+function toSearchResult(almanac: Almanac, conditions: SearchCondition[]): SearchResult {
+ const matchedSuitable = conditions
+ .filter(c => c.type === 'suitable')
+ .flatMap(c => c.items)
+ .filter(item => almanac.suitable.includes(item))
+
+ const matchedUnsuitable = conditions
+ .filter(c => c.type === 'unsuitable')
+ .flatMap(c => c.items)
+ .filter(item => almanac.unsuitable.includes(item))
+
+ return {
+ date: almanac.solarDate,
+ lunarDate: `${almanac.lunarDate.lunarYear}${almanac.lunarDate.lunarMonth}${almanac.lunarDate.lunarDay}`,
+ weekday: almanac.lunarDate.weekday,
+ matchedItems: {
+ suitable: matchedSuitable,
+ unsuitable: matchedUnsuitable,
+ },
+ matchCount: matchedSuitable.length + matchedUnsuitable.length,
+ almanacData: almanac,
+ }
+}
+
+export class SearchService {
+ private almanacService = new AlmanacService()
+ private historyKey = 'search_history'
+
+ async search(request: SearchRequest): Promise {
+ const params = toSearchParams(request)
+ const almanacs = await this.almanacService.searchAlmanacs(params)
+ this.addToHistory(
+ request.conditions.map(c => c.items.join(',')).join(';'),
+ almanacs.length,
+ )
+ return almanacs.map(a => toSearchResult(a, request.conditions))
+ }
+
+ async searchByKeyword(keyword: string): Promise {
+ const params: SearchParams = { keyword }
+ const almanacs = await this.almanacService.searchAlmanacs(params)
+ this.addToHistory(keyword, almanacs.length)
+ return almanacs.map(a => toSearchResult(a, []))
+ }
+
+ async getSearchHistory(): Promise {
+ return storage.get(this.historyKey) ?? []
+ }
+
+ async clearSearchHistory(): Promise {
+ storage.remove(this.historyKey)
+ }
+
+ private addToHistory(keyword: string, resultCount: number): void {
+ if (!keyword) return
+ const history = storage.get(this.historyKey) ?? []
+ history.unshift({ keyword, timestamp: Date.now(), resultCount })
+ if (history.length > 50) history.length = 50
+ storage.set(this.historyKey, history)
+ }
+}
+
+const searchService = new SearchService()
+export default searchService
diff --git a/everything-is-suitable-uniapp/src/services/templateService.ts b/everything-is-suitable-uniapp/src/services/templateService.ts
new file mode 100644
index 0000000..ccfa373
--- /dev/null
+++ b/everything-is-suitable-uniapp/src/services/templateService.ts
@@ -0,0 +1,286 @@
+import type { Almanac, ZiweiChart, DailyFortune } from '../algorithms/types'
+import type { SearchTemplate, SearchRequest } from '../types/search'
+import { storage } from '../utils/storage'
+
+export interface ReportTemplate {
+ id: string
+ name: string
+ description: string
+ sections: TemplateSection[]
+}
+
+export interface TemplateSection {
+ id: string
+ title: string
+ type: 'almanac' | 'ziwei' | 'fortune' | 'text'
+ visible: boolean
+ order: number
+}
+
+export interface GeneratedReport {
+ templateId: string
+ generatedAt: string
+ sections: GeneratedSection[]
+}
+
+export interface GeneratedSection {
+ id: string
+ title: string
+ type: string
+ content: string
+}
+
+const DEFAULT_TEMPLATES: ReportTemplate[] = [
+ {
+ id: 'daily-brief',
+ name: '每日简报',
+ description: '包含今日黄历和运势概要',
+ sections: [
+ { id: 'almanac', title: '今日黄历', type: 'almanac', visible: true, order: 1 },
+ { id: 'fortune', title: '今日运势', type: 'fortune', visible: true, order: 2 },
+ ],
+ },
+ {
+ id: 'full-report',
+ name: '完整报告',
+ description: '包含黄历、紫微盘和运势详情',
+ sections: [
+ { id: 'almanac', title: '今日黄历', type: 'almanac', visible: true, order: 1 },
+ { id: 'ziwei', title: '紫微斗数', type: 'ziwei', visible: true, order: 2 },
+ { id: 'fortune', title: '运势详情', type: 'fortune', visible: true, order: 3 },
+ ],
+ },
+ {
+ id: 'ziwei-only',
+ name: '紫微专报',
+ description: '仅包含紫微斗数排盘分析',
+ sections: [
+ { id: 'ziwei', title: '紫微斗数', type: 'ziwei', visible: true, order: 1 },
+ ],
+ },
+]
+
+const DEFAULT_SEARCH_TEMPLATES: SearchTemplate[] = [
+ {
+ id: 'wedding',
+ name: '嫁娶吉日',
+ description: '查找适合嫁娶的黄道吉日',
+ category: '婚嫁',
+ condition: {
+ conditions: [
+ { id: '1', type: 'suitable', items: ['嫁娶', '纳采', '订盟'], operator: 'and', exclude: false },
+ ],
+ days: 90,
+ sortBy: 'date',
+ sortOrder: 'asc',
+ },
+ },
+ {
+ id: 'moving',
+ name: '搬家吉日',
+ description: '查找适合搬家的好日子',
+ category: '居住',
+ condition: {
+ conditions: [
+ { id: '1', type: 'suitable', items: ['移徙', '入宅'], operator: 'or', exclude: false },
+ { id: '2', type: 'unsuitable', items: ['破土', '安葬'], operator: 'or', exclude: true },
+ ],
+ days: 60,
+ sortBy: 'date',
+ sortOrder: 'asc',
+ },
+ },
+ {
+ id: 'business',
+ name: '开市吉日',
+ description: '查找适合开市交易的吉日',
+ category: '商业',
+ condition: {
+ conditions: [
+ { id: '1', type: 'suitable', items: ['开市', '交易', '立券', '纳财'], operator: 'or', exclude: false },
+ ],
+ days: 30,
+ sortBy: 'date',
+ sortOrder: 'asc',
+ },
+ },
+ {
+ id: 'travel',
+ name: '出行吉日',
+ description: '查找适合出行的安全日子',
+ category: '出行',
+ condition: {
+ conditions: [
+ { id: '1', type: 'suitable', items: ['出行'], operator: 'and', exclude: false },
+ { id: '2', type: 'unsuitable', items: ['嫁娶', '安葬'], operator: 'or', exclude: true },
+ ],
+ days: 30,
+ sortBy: 'date',
+ sortOrder: 'asc',
+ },
+ },
+ {
+ id: 'pray',
+ name: '祈福吉日',
+ description: '查找适合祈福祭祀的日子',
+ category: '祭祀',
+ condition: {
+ conditions: [
+ { id: '1', type: 'suitable', items: ['祭祀', '祈福'], operator: 'or', exclude: false },
+ ],
+ days: 30,
+ sortBy: 'date',
+ sortOrder: 'asc',
+ },
+ },
+ {
+ id: 'construction',
+ name: '修造吉日',
+ description: '查找适合修造动土的日子',
+ category: '建筑',
+ condition: {
+ conditions: [
+ { id: '1', type: 'suitable', items: ['修造', '动土'], operator: 'or', exclude: false },
+ ],
+ days: 60,
+ sortBy: 'date',
+ sortOrder: 'asc',
+ },
+ },
+]
+
+export class TemplateService {
+ private templatesKey = 'report_templates'
+ private searchTemplatesKey = 'search_templates'
+ private cachedSearchTemplates: SearchTemplate[] | null = null
+
+ getSearchTemplates(): SearchTemplate[] {
+ if (this.cachedSearchTemplates) return this.cachedSearchTemplates
+ const stored = storage.get(this.searchTemplatesKey)
+ this.cachedSearchTemplates = stored && stored.length > 0 ? stored : DEFAULT_SEARCH_TEMPLATES
+ return this.cachedSearchTemplates
+ }
+
+ getCategories(): string[] {
+ const templates = this.getSearchTemplates()
+ const categories = new Set(templates.map(t => t.category))
+ return Array.from(categories)
+ }
+
+ getTemplatesByCategory(category: string): SearchTemplate[] {
+ return this.getSearchTemplates().filter(t => t.category === category)
+ }
+
+ searchTemplates(keyword: string): SearchTemplate[] {
+ const lower = keyword.toLowerCase()
+ return this.getSearchTemplates().filter(
+ t => t.name.toLowerCase().includes(lower) || t.description.toLowerCase().includes(lower),
+ )
+ }
+
+ async getTemplates(): Promise {
+ const stored = storage.get(this.templatesKey)
+ if (stored && stored.length > 0) return stored
+ return DEFAULT_TEMPLATES
+ }
+
+ async getTemplateById(id: string): Promise {
+ const templates = await this.getTemplates()
+ return templates.find(t => t.id === id) ?? null
+ }
+
+ async saveTemplate(template: ReportTemplate): Promise {
+ const templates = await this.getTemplates()
+ const index = templates.findIndex(t => t.id === template.id)
+ if (index >= 0) {
+ templates[index] = template
+ } else {
+ templates.push(template)
+ }
+ storage.set(this.templatesKey, templates)
+ }
+
+ async deleteTemplate(id: string): Promise {
+ const templates = await this.getTemplates()
+ const index = templates.findIndex(t => t.id === id)
+ if (index < 0) return false
+ templates.splice(index, 1)
+ storage.set(this.templatesKey, templates)
+ return true
+ }
+
+ async generateReport(
+ templateId: string,
+ data: { almanac?: Almanac; chart?: ZiweiChart; fortune?: DailyFortune },
+ ): Promise {
+ const template = await this.getTemplateById(templateId)
+ if (!template) return null
+
+ const sections: GeneratedSection[] = template.sections
+ .filter(s => s.visible)
+ .sort((a, b) => a.order - b.order)
+ .map(s => ({
+ id: s.id,
+ title: s.title,
+ type: s.type,
+ content: this.renderSection(s.type, data),
+ }))
+
+ return {
+ templateId,
+ generatedAt: new Date().toISOString(),
+ sections,
+ }
+ }
+
+ private renderSection(
+ type: string,
+ data: { almanac?: Almanac; chart?: ZiweiChart; fortune?: DailyFortune },
+ ): string {
+ switch (type) {
+ case 'almanac':
+ return data.almanac ? this.renderAlmanac(data.almanac) : '暂无黄历数据'
+ case 'ziwei':
+ return data.chart ? this.renderZiwei(data.chart) : '暂无紫微数据'
+ case 'fortune':
+ return data.fortune ? this.renderFortune(data.fortune) : '暂无运势数据'
+ case 'text':
+ return ''
+ default:
+ return ''
+ }
+ }
+
+ private renderAlmanac(almanac: Almanac): string {
+ const parts: string[] = []
+ parts.push(`日期:${almanac.solarDate}`)
+ parts.push(`宜:${almanac.suitable.join('、')}`)
+ parts.push(`忌:${almanac.unsuitable.join('、')}`)
+ parts.push(`建除:${almanac.jianChu}`)
+ parts.push(`星神:${almanac.starGod}`)
+ return parts.join('\n')
+ }
+
+ private renderZiwei(chart: ZiweiChart): string {
+ const parts: string[] = []
+ parts.push(chart.summary ?? '')
+ if (chart.sanFangSiZheng) {
+ parts.push(`三方得分:${chart.sanFangSiZheng.sanFangScore}`)
+ parts.push(`四正得分:${chart.sanFangSiZheng.siZhengScore}`)
+ }
+ return parts.join('\n')
+ }
+
+ private renderFortune(fortune: DailyFortune): string {
+ const parts: string[] = []
+ parts.push(`综合评分:${fortune.overallScore}`)
+ if (fortune.careerAdvice) parts.push(`事业:${fortune.careerAdvice}`)
+ if (fortune.wealthAdvice) parts.push(`财运:${fortune.wealthAdvice}`)
+ if (fortune.relationshipAdvice) parts.push(`感情:${fortune.relationshipAdvice}`)
+ if (fortune.healthAdvice) parts.push(`健康:${fortune.healthAdvice}`)
+ return parts.join('\n')
+ }
+}
+
+const templateService = new TemplateService()
+export default templateService
diff --git a/everything-is-suitable-uniapp/src/services/ziweiService.ts b/everything-is-suitable-uniapp/src/services/ziweiService.ts
new file mode 100644
index 0000000..de30856
--- /dev/null
+++ b/everything-is-suitable-uniapp/src/services/ziweiService.ts
@@ -0,0 +1,270 @@
+import type { ZiweiChart, BirthInfo, Palace, StarInfo } from '../algorithms/types'
+import { HeavenlyStem, EarthlyBranch, PalaceType, MajorStar, StarNature } from '../algorithms/enums'
+import { calculatePalaceScore } from '../algorithms/ziweiAlgorithm'
+import { calculateSanFangSiZheng } from '../algorithms/sanFangSiZheng'
+import { LRUCache } from '../utils/lruCache'
+
+export interface GenerateChartParams {
+ birthTime: string
+ gender: 'male' | 'female'
+ timezone: string
+ longitude?: number
+ latitude?: number
+}
+
+const HOUR_BRANCH_MAP: Record = {
+ 0: EarthlyBranch.ZI,
+ 1: EarthlyBranch.ZI,
+ 2: EarthlyBranch.CHOU,
+ 3: EarthlyBranch.CHOU,
+ 4: EarthlyBranch.YIN,
+ 5: EarthlyBranch.YIN,
+ 6: EarthlyBranch.MAO,
+ 7: EarthlyBranch.MAO,
+ 8: EarthlyBranch.CHEN,
+ 9: EarthlyBranch.CHEN,
+ 10: EarthlyBranch.SI,
+ 11: EarthlyBranch.SI,
+ 12: EarthlyBranch.WU,
+ 13: EarthlyBranch.WU,
+ 14: EarthlyBranch.WEI,
+ 15: EarthlyBranch.WEI,
+ 16: EarthlyBranch.SHEN,
+ 17: EarthlyBranch.SHEN,
+ 18: EarthlyBranch.YOU,
+ 19: EarthlyBranch.YOU,
+ 20: EarthlyBranch.XU,
+ 21: EarthlyBranch.XU,
+ 22: EarthlyBranch.HAI,
+ 23: EarthlyBranch.HAI,
+}
+
+const MONTH_BRANCHES: EarthlyBranch[] = [
+ EarthlyBranch.YIN,
+ EarthlyBranch.MAO,
+ EarthlyBranch.CHEN,
+ EarthlyBranch.SI,
+ EarthlyBranch.WU,
+ EarthlyBranch.WEI,
+ EarthlyBranch.SHEN,
+ EarthlyBranch.YOU,
+ EarthlyBranch.XU,
+ EarthlyBranch.HAI,
+ EarthlyBranch.ZI,
+ EarthlyBranch.CHOU,
+]
+
+const MAJOR_STAR_SEQUENCE: MajorStar[] = [
+ MajorStar.ZIWEI,
+ MajorStar.TIANJI,
+ MajorStar.TAIYANG,
+ MajorStar.WUQU,
+ MajorStar.TIANTONG,
+ MajorStar.LIANCHEN,
+]
+
+const MAJOR_STAR_SEQUENCE2: MajorStar[] = [
+ MajorStar.TIANFU,
+ MajorStar.TAIYIN,
+ MajorStar.TANLANG,
+ MajorStar.JUMEN,
+ MajorStar.TIANXIANG,
+ MajorStar.TIANLIANG,
+ MajorStar.QISHA,
+ MajorStar.POJUN,
+]
+
+export class ZiweiService {
+ private cache = new LRUCache({ maxSize: 50 })
+
+ private buildBirthInfo(params: GenerateChartParams): BirthInfo {
+ const birthDate = new Date(params.birthTime)
+ const year = birthDate.getFullYear()
+ const month = birthDate.getMonth() + 1
+ const hour = birthDate.getHours()
+
+ const yearStemIndex = ((year - 4) % 10 + 10) % 10 + 1
+ const yearBranchIndex = ((year - 4) % 12 + 12) % 12 + 1
+ const monthBranchIndex = month
+ const hourBranch = HOUR_BRANCH_MAP[hour] ?? EarthlyBranch.ZI
+
+ const yearStem = HeavenlyStem.fromIndex(yearStemIndex)
+ const yearBranch = EarthlyBranch.fromIndex(yearBranchIndex)
+ const monthBranch = MONTH_BRANCHES[month - 1] ?? EarthlyBranch.YIN
+
+ return {
+ birthTime: params.birthTime,
+ yearStem,
+ monthStem: yearStem,
+ dayStem: yearStem,
+ hourStem: yearStem,
+ yearBranch,
+ monthBranch,
+ dayBranch: monthBranch,
+ hourBranch,
+ gender: params.gender === 'male' ? 'MALE' : 'FEMALE',
+ timezone: params.timezone,
+ longitude: params.longitude,
+ latitude: params.latitude,
+ }
+ }
+
+ private calculateMingGong(monthBranch: EarthlyBranch, hourBranch: EarthlyBranch): EarthlyBranch {
+ const monthIndex = EarthlyBranch.index(monthBranch)
+ const hourIndex = EarthlyBranch.index(hourBranch)
+ let mingGongIndex = 14 - monthIndex - hourIndex
+ while (mingGongIndex <= 0) mingGongIndex += 12
+ if (mingGongIndex > 12) mingGongIndex -= 12
+ return EarthlyBranch.fromIndex(mingGongIndex)
+ }
+
+ private calculateShenGong(monthBranch: EarthlyBranch, hourBranch: EarthlyBranch): EarthlyBranch {
+ const monthIndex = EarthlyBranch.index(monthBranch)
+ const hourIndex = EarthlyBranch.index(hourBranch)
+ let shenGongIndex = 2 + monthIndex + hourIndex
+ while (shenGongIndex > 12) shenGongIndex -= 12
+ return EarthlyBranch.fromIndex(shenGongIndex)
+ }
+
+ private buildPalaces(mingGongBranch: EarthlyBranch, shenGongBranch: EarthlyBranch, birthInfo: BirthInfo): Palace[] {
+ const branches = EarthlyBranch.values()
+ const palaceTypes = PalaceType.values()
+ const mingGongIndex = EarthlyBranch.index(mingGongBranch)
+ const mingBranchArrayIndex = branches.indexOf(mingGongBranch)
+
+ const palaces: Palace[] = []
+ for (let i = 0; i < 12; i++) {
+ const branchArrayIndex = (mingBranchArrayIndex + i) % 12
+ const earthlyBranch = branches[branchArrayIndex]
+ const palaceType = palaceTypes[i] ?? PalaceType.MING
+
+ const majorStars = this.assignStarsToPalace(i, mingGongIndex, birthInfo)
+
+ const palace: Palace = {
+ palaceType,
+ earthlyBranch,
+ majorStars,
+ minorStars: [],
+ analysis: '',
+ score: 50,
+ }
+
+ palace.score = calculatePalaceScore(palace)
+ palaces.push(palace)
+ }
+
+ return palaces
+ }
+
+ private assignStarsToPalace(palaceOffset: number, mingGongIndex: number, birthInfo: BirthInfo): StarInfo[] {
+ const stars: StarInfo[] = []
+ const yearStemIndex = HeavenlyStem.index(birthInfo.yearStem)
+
+ if (palaceOffset === 0) {
+ stars.push(this.createStarInfo(MajorStar.ZIWEI, yearStemIndex))
+ } else if (palaceOffset <= 5) {
+ stars.push(this.createStarInfo(MAJOR_STAR_SEQUENCE[palaceOffset], yearStemIndex))
+ }
+
+ const fuOffset = (12 - (mingGongIndex - 1)) % 12
+ if (palaceOffset === fuOffset) {
+ stars.push(this.createStarInfo(MajorStar.TIANFU, yearStemIndex))
+ }
+
+ for (let i = 0; i < MAJOR_STAR_SEQUENCE2.length; i++) {
+ const targetOffset = (fuOffset + i) % 12
+ if (palaceOffset === targetOffset && palaceOffset !== fuOffset) {
+ stars.push(this.createStarInfo(MAJOR_STAR_SEQUENCE2[i], yearStemIndex))
+ }
+ }
+
+ return stars
+ }
+
+ private createStarInfo(star: MajorStar, yearStemIndex: number): StarInfo {
+ return {
+ star,
+ nature: MajorStar.nature(star),
+ transformation: this.getTransformation(star, yearStemIndex),
+ brightness: 80,
+ description: MajorStar.description(star),
+ }
+ }
+
+ private getTransformation(star: MajorStar, yearStemIndex: number): StarInfo['transformation'] {
+ const luMap: Record = {
+ 1: MajorStar.POJUN, 2: MajorStar.TIANJI, 3: MajorStar.TAIYANG,
+ 4: MajorStar.WUQU, 5: MajorStar.LIANCHEN, 6: MajorStar.TIANFU,
+ 7: MajorStar.TAIYANG, 8: MajorStar.JUMEN, 9: MajorStar.TIANLIANG,
+ 10: MajorStar.ZIWEI,
+ }
+ const quanMap: Record = {
+ 1: MajorStar.TIANLIANG, 2: MajorStar.TIANLIANG, 3: MajorStar.WUQU,
+ 4: MajorStar.ZIWEI, 5: MajorStar.POJUN, 6: MajorStar.TIANXIANG,
+ 7: MajorStar.TIANJI, 8: MajorStar.TANLANG, 9: MajorStar.QISHA,
+ 10: MajorStar.LIANCHEN,
+ }
+
+ if (luMap[yearStemIndex] === star) return 'LU' as any
+ if (quanMap[yearStemIndex] === star) return 'QUAN' as any
+ return null
+ }
+
+ private generateSummary(chart: ZiweiChart): string {
+ const parts: string[] = []
+ parts.push(`命宫位于${EarthlyBranch.name(chart.mingGongBranch)}宫`)
+
+ const mingPalace = chart.palaces.find(p => p.palaceType === PalaceType.MING)
+ if (mingPalace && mingPalace.majorStars.length > 0) {
+ const starNames = mingPalace.majorStars.map(s => MajorStar.name(s.star)).join('、')
+ parts.push(`,命宫主星:${starNames}`)
+ }
+
+ if (chart.sanFangSiZheng) {
+ parts.push(`。三方得分:${chart.sanFangSiZheng.sanFangScore},四正得分:${chart.sanFangSiZheng.siZhengScore}`)
+ }
+
+ return parts.join('')
+ }
+
+ async generateChart(params: GenerateChartParams): Promise {
+ const cacheKey = this.buildCacheKey(params)
+ const cached = this.cache.get(cacheKey)
+ if (cached) return cached
+
+ const birthInfo = this.buildBirthInfo(params)
+ const mingGongBranch = this.calculateMingGong(birthInfo.monthBranch, birthInfo.hourBranch)
+ const shenGongBranch = this.calculateShenGong(birthInfo.monthBranch, birthInfo.hourBranch)
+ const palaces = this.buildPalaces(mingGongBranch, shenGongBranch, birthInfo)
+
+ const chart: ZiweiChart = {
+ birthInfo,
+ palaces,
+ yearStem: birthInfo.yearStem,
+ mingGongBranch,
+ shenGongBranch,
+ summary: '',
+ overallLuck: '',
+ }
+
+ chart.sanFangSiZheng = calculateSanFangSiZheng(chart) ?? undefined
+ chart.summary = this.generateSummary(chart)
+
+ const overallScore = chart.sanFangSiZheng?.sanFangScore ?? 60
+ if (overallScore >= 80) chart.overallLuck = '吉'
+ else if (overallScore >= 60) chart.overallLuck = '平'
+ else chart.overallLuck = '凶'
+
+ this.cache.set(cacheKey, chart)
+ return chart
+ }
+
+ async getChartFromCache(params: GenerateChartParams): Promise {
+ const cacheKey = this.buildCacheKey(params)
+ return this.cache.get(cacheKey)
+ }
+
+ private buildCacheKey(params: GenerateChartParams): string {
+ return `${params.birthTime}_${params.gender}_${params.timezone}`
+ }
+}
diff --git a/everything-is-suitable-uniapp/src/utils/exportImport.ts b/everything-is-suitable-uniapp/src/utils/exportImport.ts
new file mode 100644
index 0000000..709299c
--- /dev/null
+++ b/everything-is-suitable-uniapp/src/utils/exportImport.ts
@@ -0,0 +1,56 @@
+import { storage } from './storage'
+
+const EXPORT_VERSION = '1.0.0'
+
+export interface ExportData {
+ version: string
+ exportDate: string
+ data: Record
+}
+
+export function exportData(): string {
+ const data: ExportData = {
+ version: EXPORT_VERSION,
+ exportDate: new Date().toISOString().split('T')[0],
+ data: {},
+ }
+
+ const storageKeys = [
+ 'ziwei_charts',
+ 'search_history',
+ 'search_templates',
+ ]
+
+ for (const key of storageKeys) {
+ const value = storage.get(key)
+ if (value !== null) {
+ data.data[key] = value
+ }
+ }
+
+ return JSON.stringify(data, null, 2)
+}
+
+export function validateImportData(jsonStr: string): boolean {
+ try {
+ const parsed = JSON.parse(jsonStr) as ExportData
+ if (!parsed.version || !parsed.data) return false
+ return true
+ } catch {
+ return false
+ }
+}
+
+export function importData(jsonStr: string): boolean {
+ if (!validateImportData(jsonStr)) return false
+
+ try {
+ const parsed = JSON.parse(jsonStr) as ExportData
+ for (const [key, value] of Object.entries(parsed.data)) {
+ storage.set(key, value)
+ }
+ return true
+ } catch {
+ return false
+ }
+}
diff --git a/everything-is-suitable-uniapp/src/utils/storage.ts b/everything-is-suitable-uniapp/src/utils/storage.ts
new file mode 100644
index 0000000..76ca3d5
--- /dev/null
+++ b/everything-is-suitable-uniapp/src/utils/storage.ts
@@ -0,0 +1,56 @@
+export class StorageService {
+ private prefix: string
+
+ constructor(prefix: string = 'eis_') {
+ this.prefix = prefix
+ }
+
+ private getKey(key: string): string {
+ return `${this.prefix}${key}`
+ }
+
+ get(key: string): T | null {
+ try {
+ const raw = uni.getStorageSync(this.getKey(key))
+ if (!raw) return null
+ return JSON.parse(raw) as T
+ } catch {
+ return null
+ }
+ }
+
+ set(key: string, value: T): void {
+ try {
+ uni.setStorageSync(this.getKey(key), JSON.stringify(value))
+ } catch (e) {
+ console.error(`Storage set error for key ${key}:`, e)
+ }
+ }
+
+ remove(key: string): void {
+ try {
+ uni.removeStorageSync(this.getKey(key))
+ } catch (e) {
+ console.error(`Storage remove error for key ${key}:`, e)
+ }
+ }
+
+ clear(): void {
+ try {
+ uni.clearStorageSync()
+ } catch (e) {
+ console.error('Storage clear error:', e)
+ }
+ }
+
+ has(key: string): boolean {
+ try {
+ const raw = uni.getStorageSync(this.getKey(key))
+ return !!raw
+ } catch {
+ return false
+ }
+ }
+}
+
+export const storage = new StorageService()
diff --git a/everything-is-suitable-uniapp/vite.config.ts b/everything-is-suitable-uniapp/vite.config.ts
new file mode 100644
index 0000000..de2a82b
--- /dev/null
+++ b/everything-is-suitable-uniapp/vite.config.ts
@@ -0,0 +1,8 @@
+import { defineConfig } from 'vite'
+import Uni from '@dcloudio/vite-plugin-uni'
+
+const uniPlugin = Uni.default || Uni
+
+export default defineConfig({
+ plugins: [...uniPlugin()]
+})