feat: 添加 UniApp 核心源码文件

- App.vue 应用入口
- algorithms/ 算法模块(黄历、紫微斗数、运势、日历等)
- services/ 业务服务层
- utils/ 工具函数(storage、exportImport)
- 配置文件(manifest.json、pages.json、vite.config.ts)
- H5 入口 index.html
This commit is contained in:
张翔
2026-04-29 21:15:55 +08:00
parent 330c3af227
commit 0c9a4ac96b
20 changed files with 3576 additions and 0 deletions
@@ -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<SearchTemplate[]>(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<ReportTemplate[]> {
const stored = storage.get<ReportTemplate[]>(this.templatesKey)
if (stored && stored.length > 0) return stored
return DEFAULT_TEMPLATES
}
async getTemplateById(id: string): Promise<ReportTemplate | null> {
const templates = await this.getTemplates()
return templates.find(t => t.id === id) ?? null
}
async saveTemplate(template: ReportTemplate): Promise<void> {
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<boolean> {
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<GeneratedReport | null> {
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