chore: seal Design Vision v1 and monorepo scaffold
Archive the differentiated YuXinGu product docs, AI engineering system, design contract, and Go/Vue scaffold. Next execution prioritizes Cece-parity over early innovation (see .ai/product/STRATEGY.md). Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+331
@@ -0,0 +1,331 @@
|
||||
/* common.js —— 纯数据 + 纯函数,禁止任何 DOM 操作
|
||||
shared by: pages/shuzi.html / pages/health.html / pages/decode.html */
|
||||
|
||||
// ===== from shuzi.html =====
|
||||
function s1(n){while(n>=10)n=String(n).split('').reduce((a,b)=>a+ +b,0);return n}
|
||||
function digs(n,l){return String(n).padStart(l,'0').split('').map(Number)}
|
||||
function calc(y,m,d){
|
||||
const dd=digs(d,2),mm=digs(m,2),yy=digs(y,4);
|
||||
const A=dd[0],B=dd[1],C=mm[0],D=mm[1],E=yy[0],F=yy[1],G=yy[2],H=yy[3];
|
||||
const v={AB:[A,B],CD:[C,D],EF:[E,F],GH:[G,H]};
|
||||
v.I=s1(A+B);v.J=s1(C+D);v.K=s1(E+F);v.L=s1(G+H);
|
||||
v.M=s1(v.I+v.J);v.N=s1(v.K+v.L);v.O=s1(v.M+v.N);
|
||||
v.Q=s1(v.N+v.O);v.P=s1(v.M+v.O);v.R=s1(v.Q+v.P);
|
||||
v.T=s1(v.I+v.M);v.S=s1(v.J+v.M);v.U=s1(v.T+v.S);
|
||||
v.V=s1(v.K+v.N);v.W=s1(v.L+v.N);v.X=s1(v.V+v.W);
|
||||
return v;
|
||||
}
|
||||
const tips={
|
||||
O:v=>`${v.O}=${v.M}+${v.N}`,M:v=>`${v.M}=${v.I}+${v.J}`,N:v=>`${v.N}=${v.K}+${v.L}`,
|
||||
I:v=>`${v.I}=${v.AB[0]}+${v.AB[1]}`,J:v=>`${v.J}=${v.CD[0]}+${v.CD[1]}`,
|
||||
K:v=>`${v.K}=${v.EF[0]}+${v.EF[1]}`,L:v=>`${v.L}=${v.GH[0]}+${v.GH[1]}`,
|
||||
R:v=>`${v.R}=${v.Q}+${v.P}`,Q:v=>`${v.Q}=${v.N}+${v.O}`,P:v=>`${v.P}=${v.M}+${v.O}`,
|
||||
T:v=>`${v.T}=${v.I}+${v.M}`,S:v=>`${v.S}=${v.J}+${v.M}`,U:v=>`${v.U}=${v.T}+${v.S}`,
|
||||
V:v=>`${v.V}=${v.K}+${v.N}`,W:v=>`${v.W}=${v.L}+${v.N}`,X:v=>`${v.X}=${v.V}+${v.W}`,
|
||||
};
|
||||
const NUM={
|
||||
1:{title:'天生领袖·开拓者',pos:'自信果敢、创造力爆棚、天生自带光芒',neg:'偶尔孤傲、不擅示弱、容易一个人扛下所有',job:'创始人、管理者、艺术家、独立设计师',elem:'金',desc:'你是天生的领航员,内心有一团不灭的火。你不需要别人告诉你该做什么——你比谁都清楚。独立、果敢、行动力超强,这让你在人群中自带光环。但请记住,真正的强大不是独自承担一切,而是学会信任和依靠。偶尔放下铠甲,你会看到更广阔的世界。'},
|
||||
2:{title:'温柔沟通·共情者',pos:'善解人意、沟通力满分、团队里的粘合剂',neg:'太在意他人眼光、容易委屈自己、选择困难',job:'心理咨询师、公关专家、教育者、客户关系',elem:'水',desc:'你有一颗比常人更柔软的心。你能听见别人没说出口的话,感受到空气里的情绪——这是你独一无二的天赋。你是团队里最温暖的存在,总能让人感到被理解。但要记得,你的感受同样重要。温柔不是软弱,拒绝不是伤害。学会为自己发声,你的温柔会更有力量。'},
|
||||
3:{title:'创意火花·行动派',pos:'灵感不断、感染力满分、想到就去做',neg:'三分钟热度、急躁冒进、耐不住寂寞',job:'演员、讲师、创意总监、自媒体人',elem:'木',desc:'你的大脑永远在高速运转,灵感像烟花一样炸开。你就是那个能把想法变成现实的人——当别人还在犹豫,你已经冲出去了。你的热情能点燃整个房间。但有时候,慢一点反而更快。不是每个火花都需要燃烧,学会聚焦,你的光芒会更耀眼。'},
|
||||
4:{title:'稳妥规划·筑造者',pos:'逻辑缜密、靠谱担当、凡事井井有条',neg:'过于保守、害怕改变、安全感容易动摇',job:'工程师、会计师、项目经理、架构师',elem:'木',desc:'你是那个让所有人放心的人。当世界一片混乱时,你已经在默默列清单了。你的稳不是无趣,是把混乱变成秩序的超能力。每一步都走得扎实,每个细节都心中有数。但生活不是待办清单,偶尔打破计划,会有惊喜。允许自己不那么完美,你已经足够好了。'},
|
||||
5:{title:'自由灵魂·探索者',pos:'无拘无束、方向感超强、幽默感爆棚',neg:'讨厌束缚、容易逃避、很难安定下来',job:'旅行博主、作家、自由职业者、冒险家',elem:'土',desc:'你就是风,没人能把你关在笼子里。你的灵魂渴望远方,对世界永远保持好奇。你能在迷茫中找到方向,这是你最珍贵的天赋。你的人生不是按部就班,而是充满冒险的故事。但也要记得,自由不是逃避,真正的自由是拥有选择的能力。找到值得你停留的热爱,那里就是你的家。'},
|
||||
6:{title:'智慧远见·鉴赏家',pos:'眼光毒辣、财富直觉、精益求精',neg:'完美主义、容易焦虑、对自己太狠',job:'投资人、咨询顾问、医生、律师',elem:'金',desc:'你有一双看透本质的眼睛。别人看到表面,你看到价值;别人追逐潮流,你预判趋势。你对品质的要求不是挑剔,是对生活的尊重。但完美不是终点,生活有瑕疵才真实。偶尔放低标准,你会发现世界比你想象的更宽容。你值得拥有最好的,包括对自己温柔一点。'},
|
||||
7:{title:'深度洞察·求真者',pos:'钻研到底、直觉惊人、看透事物本质',neg:'疏离冷漠、疑心重、社交电量容易耗尽',job:'科学家、侦探、程序员、学者',elem:'水',desc:'别人看热闹,你看门道。你的大脑是永动的探索引擎,对世界的真相充满饥渴。你不需要浅薄的社交,你在意的是深度和意义。这份专注让你在专业领域无人能敌。但请偶尔走出自己的世界,那些看似平凡的人间烟火,也能给你答案。独处是力量,连接是温暖,两者你都值得拥有。'},
|
||||
8:{title:'责任担当·成就者',pos:'责任感爆棚、商业嗅觉、野心勃勃',neg:'压力山大、控制欲强、容易透支自己',job:'企业家、高管、投资人、政治家',elem:'土',desc:'你注定不是普通人。你肩膀上扛着比常人更重的使命,心中有比常人更大的蓝图。你的责任感和行动力让你天然成为领袖。但你不需要扛起整个世界,真正的领导者懂得分担负重。学会休息不是懈怠,是为了走更远的路。你值得被照顾,不只是照顾别人。'},
|
||||
9:{title:'博爱大器·成功者',pos:'慈悲大爱、机会磁铁、自带成功气场',neg:'贪多嚼不烂、难以专注、理想主义过头',job:'慈善家、艺人、灵性导师、社会活动家',elem:'火',desc:'你身上有一种让人想靠近的魔力。你的心中装着比个人更大的东西——爱与使命感是你最强的驱动力。机会总是向你涌来,因为你值得。你天生自带好运体质。但不需要抓住所有机会,挑一个最让你心动的,全力以赴。你的存在,本身就是一种治愈。'},
|
||||
};
|
||||
const JOINT={
|
||||
'112':'独当一面,做事靠谱有担当','123':'天生讲师,开口就能圈粉','134':'灵感落地,把创意变成现实',
|
||||
'145':'运筹帷幄,天生的规划师','156':'八方来财,处处遇贵人','167':'白手起家,靠自己闯天下',
|
||||
'178':'成就他人,身边贵人不断','189':'停不下来的工作狂','191':'独立开创,没人能阻挡你',
|
||||
'213':'口才炸裂,感染力爆棚','224':'心太软,善良是你的底牌','235':'沟通达人,三言两语暖人心',
|
||||
'246':'贵人环绕,销冠体质','257':'直觉导航,跟着感觉走就对了','268':'重情重义,一诺千金',
|
||||
'279':'异性缘超好,走到哪都是焦点','281':'大单收割机,谈判桌王者','292':'幕后军师,运筹帷幄之中',
|
||||
'314':'创意执行力,说干就干','325':'感性沟通,用温度打动人心','336':'资源连接器,人脉即财富',
|
||||
'347':'深耕专业,做领域里的王','358':'行动派领袖,带队冲锋','369':'万花筒人生,十八般武艺样样通',
|
||||
'415':'规划力MAX,步步为营','426':'智慧型销售,成交于无形','437':'学术大佬,坐冷板凳也能发光',
|
||||
'448':'靠谱天花板,交给你就放心了','459':'策划大师,脑子比电脑快','461':'稳扎稳打,厚积薄发',
|
||||
'472':'火眼金睛,精准分析一切','483':'技术大牛,专业领域独孤求败','494':'完美规划师,细节控本控',
|
||||
'516':'方向感+大智慧,走哪都是对的','527':'感性直觉,第六感超准','538':'天生领袖,气场两米八',
|
||||
'549':'规划型赢家,稳操胜券','551':'风一样自由,方向感自带导航','562':'会赚也会花,享受生活',
|
||||
'573':'贵人相助命,总有高人来指点','584':'责任+规划,双核驱动','595':'梦想家体质,心有多大舞台就有多大',
|
||||
'617':'白手起家,命运由我不由天','628':'财富+沟通,双商在线','639':'多才多艺,斜杠青年本青',
|
||||
'641':'务实派经营者,脚踏实地','652':'财富雷达,赚钱直觉超准','663':'完美主义+财富,精品人生',
|
||||
'674':'天生傲骨,不甘平凡','685':'商业+责任,事业标杆','696':'极致追求,匠人精神',
|
||||
'718':'钻研成王,责任铸就传奇','729':'桃花运旺,魅力无法挡','731':'创新领袖,敢为天下先',
|
||||
'742':'神机妙算,谋定而后动','753':'直觉行动派,干就完了','764':'精打细算,每一分钱都花在刀刃上',
|
||||
'775':'王者基因,注定站在C位','786':'商业领袖,运筹帷幄','797':'极致深钻,领域天花板',
|
||||
'819':'亲力亲为,掌控全局','821':'大单高手,说服力满格','832':'行动+沟通,效率拉满',
|
||||
'843':'技术专家,一技傍身走天下','854':'规划+自由,收放自如','865':'财富+责任,扛得住世界',
|
||||
'876':'商业王者,霸气外露','887':'强势担当,说到做到','898':'大事业格局,志在千里',
|
||||
'911':'大爱无疆,独立而温柔','922':'温柔至善,人间小天使','933':'巧手匠心,万物皆可造',
|
||||
'944':'稳中求进,计划周全','955':'自由博爱,灵魂有趣','966':'完美+智慧,人间清醒',
|
||||
'977':'深度大爱,为理想燃烧','988':'商业帝国缔造者','999':'极致巅峰,满分人生',
|
||||
};
|
||||
const MISS_MSG={
|
||||
1:'内心的领袖力还在沉睡——试着在小事上果断决策,你会发现自己比想象中更强大',
|
||||
2:'你的声音值得被听见——从今天开始,勇敢说出你的想法,别怕被拒绝',
|
||||
3:'行动力是等待点燃的火种——别再想了,先迈出第一步,剩下的路会自动展开',
|
||||
4:'安全感不是等来的,是建出来的——给自己一个可以依靠的小习惯,它会慢慢长大',
|
||||
5:'你的指南针正在校准中——去尝试那些让你心跳加速的事,方向自会出现',
|
||||
6:'财富嗅觉是可以培养的——从关注自己的价值开始,你会发现自己值得更多',
|
||||
7:'深度的力量需要聚焦——找一个让你好奇的问题,像侦探一样追下去',
|
||||
8:'你的使命感正在觉醒——找到那件让你热血沸腾的事,扛起来走下去',
|
||||
9:'大爱需要边界——学会对自己说"够了",你的能量会用在对的地方',
|
||||
};
|
||||
const MISS_STRONG={
|
||||
1:'自信女神本神,偶尔听一下别人的意见会更完美',
|
||||
2:'沟通力溢出屏幕,但别忘了先连接自己的内心',
|
||||
3:'活力四射到飞起,偶尔刹车踩一踩,走得更远',
|
||||
4:'稳得像座山,但偶尔翻个墙看看外面的世界也不错',
|
||||
5:'自由到炸裂,找个值得停靠的港湾也很酷',
|
||||
6:'完美主义拉满,对自己说一句"已经够好了"试试',
|
||||
7:'沉迷思考无法自拔,偶尔出来晒晒太阳吧',
|
||||
8:'责任扛得太多啦,分一些给信得过的人',
|
||||
9:'爱心泛滥到溢出,聚焦一件最想改变的事会更有力量',
|
||||
};
|
||||
|
||||
// ===== from yangsheng.html =====
|
||||
var TG=['甲','乙','丙','丁','戊','己','庚','辛','壬','癸'];
|
||||
var DZ=['子','丑','寅','卯','辰','巳','午','未','申','酉','戌','亥'];
|
||||
var TGE={甲:'木',乙:'木',丙:'火',丁:'火',戊:'土',己:'土',庚:'金',辛:'金',壬:'水',癸:'水'};
|
||||
var DZE={子:'水',丑:'土',寅:'木',卯:'木',辰:'土',巳:'火',午:'火',未:'土',申:'金',酉:'金',戌:'土',亥:'水'};
|
||||
var DZ_ANI=['鼠','牛','虎','兔','龙','蛇','马','羊','猴','鸡','狗','猪'];
|
||||
var EC={木:'#5CB85C',火:'#E54D42',土:'#E8985A',金:'#C8923A',水:'#4A90E2'};
|
||||
var EO=['木','火','土','金','水'];
|
||||
|
||||
// 纳音 (60甲子)
|
||||
var NAYIN_MAP = {};
|
||||
(function(){
|
||||
var n30 = [
|
||||
'海中金','炉中火','大林木','路旁土','剑锋金','山头火',
|
||||
'涧下水','城头土','白蜡金','杨柳木','泉中水','屋上土',
|
||||
'霹雳火','松柏木','流年水','砂石金','山下火','平地木',
|
||||
'壁上土','金箔金','覆灯火','天河水','大驿土','钗钏金',
|
||||
'桑柘木','大溪水','沙中土','天上火','石榴木','大海水'
|
||||
];
|
||||
for(var i=0;i<60;i++){
|
||||
var tg = TG[i%10], dz = DZ[i%12];
|
||||
NAYIN_MAP[tg+dz] = n30[Math.floor(i/2)];
|
||||
}
|
||||
})();
|
||||
|
||||
var ST = [
|
||||
{n:'小寒',m:1,d:5},{n:'大寒',m:1,d:20},{n:'立春',m:2,d:4},{n:'雨水',m:2,d:19},
|
||||
{n:'惊蛰',m:3,d:5},{n:'春分',m:3,d:20},{n:'清明',m:4,d:5},{n:'谷雨',m:4,d:20},
|
||||
{n:'立夏',m:5,d:5},{n:'小满',m:5,d:21},{n:'芒种',m:6,d:5},{n:'夏至',m:6,d:21},
|
||||
{n:'小暑',m:7,d:7},{n:'大暑',m:7,d:22},{n:'立秋',m:8,d:7},{n:'处暑',m:8,d:23},
|
||||
{n:'白露',m:9,d:7},{n:'秋分',m:9,d:23},{n:'寒露',m:10,d:8},{n:'霜降',m:10,d:23},
|
||||
{n:'立冬',m:11,d:7},{n:'小雪',m:11,d:22},{n:'大雪',m:12,d:7},{n:'冬至',m:12,d:22}
|
||||
];
|
||||
var MONTH_DZ=['寅','卯','辰','巳','午','未','申','酉','戌','亥','子','丑'];
|
||||
|
||||
function gzYear(y){return{tg:TG[(y-4)%10],dz:DZ[(y-4)%12]};}
|
||||
function getST(d){
|
||||
var m=d.getMonth()+1, dd=d.getDate(), t=null;
|
||||
for(var i=ST.length-1;i>=0;i--){var s=ST[i];if(m>s.m||(m===s.m&&dd>=s.d)){t=s;break;}}
|
||||
return t||ST[ST.length-1];
|
||||
}
|
||||
function getMDz(st){
|
||||
var bi=ST.findIndex(function(s){return s.n==='立春';});
|
||||
return MONTH_DZ[Math.floor(((ST.indexOf(st)-bi+24)%24)/2)];
|
||||
}
|
||||
|
||||
function getBazi(d,h){
|
||||
var y=d.getFullYear();
|
||||
var gzY=gzYear(y);
|
||||
var dayOfYear=Math.floor((d-new Date(y,0,0))/86400000);
|
||||
var lcDay=35; // 立春approx day
|
||||
var mi=Math.floor(((dayOfYear-lcDay+365)%365)/30.44);
|
||||
if(mi<0)mi+=12;if(mi>=12)mi=11;
|
||||
var mTg=TG[(((y-4)%10)*2+mi)%10];
|
||||
var mDz=DZ[(mi+2)%12];
|
||||
var base=new Date(1900,0,1);
|
||||
var dd=Math.floor((d-base)/86400000);
|
||||
var dTg=TG[((dd%10)+10)%10];
|
||||
var dDz=DZ[((dd%12)+12)%12];
|
||||
var hDz=DZ[Math.floor((h+1)/2)%12];
|
||||
var hTg=TG[(TG.indexOf(dTg)*2+DZ.indexOf(hDz))%10];
|
||||
return{year:{tg:gzY.tg,dz:gzY.dz},month:{tg:mTg,dz:mDz},day:{tg:dTg,dz:dDz},hour:{tg:hTg,dz:hDz}};
|
||||
}
|
||||
|
||||
function calc5E(bz){
|
||||
var c={木:0,火:0,土:0,金:0,水:0};
|
||||
[bz.year,bz.month,bz.day,bz.hour].forEach(function(p){c[TGE[p.tg]]++;c[DZE[p.dz]]++;});
|
||||
var t=0;EO.forEach(function(e){t+=c[e];});
|
||||
var r={};EO.forEach(function(e){r[e]=Math.round(c[e]/t*100);});
|
||||
return r;
|
||||
}
|
||||
|
||||
function getConst(p){
|
||||
var s=EO.slice().sort(function(a,b){return p[b]-p[a];});
|
||||
var strong=s[0], weak=s[4];
|
||||
var detail={
|
||||
'木':{
|
||||
primary:'气郁质', secondary:'血瘀质', strong:'肝', weak:'脾',
|
||||
desc:'你的身体像一棵需要舒展的大树。肝气就像树的枝叶,需要自由伸展才能舒畅。你积极进取,但也容易因压力而紧绷——胸闷、偏头痛、情绪波动都是身体在说"我需要透透气"。春天是你最敏感的季节,别把情绪关在心里。',
|
||||
body:'体型偏瘦或适中,面色偏青黄。指甲容易脆裂,眼睛容易干涩——这都是肝血在提醒你。春季容易过敏,舌边偏红。',
|
||||
tips:'疏肝理气是头等大事。多运动、多表达、别把委屈憋在心里。玫瑰花茶是你的好朋友,明亮通风的环境就是你的充电站。',
|
||||
diseases:'偏头痛、高血压、甲状腺结节、乳腺增生、月经不调、胁间神经痛。长期压抑情绪会"化火伤阴"。',
|
||||
seasonRisk:'春天最敏感——过敏、血压波动。秋天金克木,容易情绪低落。',
|
||||
foodTherapy:'绿色蔬菜是主角(菠菜、芹菜、西兰花)。少来点酸的(山楂、柠檬),别过量。玫瑰花茶、薄荷茶疏肝解郁。荞麦、小米养肝血。',
|
||||
dailyRhythm:'早上醒来拉伸5分钟,像猫一样舒展。上午11点前搞定重要决策(肝气最旺)。晚上11点前入睡养肝血。'
|
||||
},
|
||||
'火':{
|
||||
primary:'湿热质', secondary:'阴虚质', strong:'心', weak:'肺',
|
||||
desc:'你的内心有一团温暖的火。热情、感染力强是你的超能力,但这团火也容易烧得太旺——心浮气躁、失眠多梦、口腔溃疡都是信号。夏天是你最需要注意的季节,学会给内心降温。',
|
||||
body:'面色偏红,手心脚心热乎乎的,容易出汗。皮肤容易出油长痘,舌尖偏红。夏天尤其难熬,总想喝冰的。',
|
||||
tips:'清心降火、静养心神。午休(11-13点)是黄金养心时间。莲子心茶、菊花茶是你的清凉剂。少熬夜,少参与激烈争吵——伤的是自己。',
|
||||
diseases:'失眠、口腔溃疡反复、心悸焦虑、高血压、皮肤痘痘。严重时心火下移,可能引起尿路问题。',
|
||||
seasonRisk:'夏天最敏感——暑热+心火,容易心烦中暑。冬天水克火,注意防寒护心。',
|
||||
foodTherapy:'苦味清心(苦瓜、莲子心)。红色食物养心(红枣、枸杞、番茄)。绿豆汤、百合莲子羹清心安神。西瓜、黄瓜解暑生津。',
|
||||
dailyRhythm:'午时(11-13点)小憩15-30分钟,像给手机充电。傍晚微微出汗的运动最好。睡前温水泡脚,把火气引下去。'
|
||||
},
|
||||
'土':{
|
||||
primary:'痰湿质', secondary:'气虚质', strong:'脾', weak:'肾',
|
||||
desc:'你像大地一样稳重可靠,是所有人都想依靠的人。但思虑太多会伤脾——饭后犯困、身体沉重、大便不成形,都是脾胃在喊累。长夏湿热季节是你最需要照顾自己的时候。脾胃好,一切都好。',
|
||||
body:'体型偏丰满,肌肉松软,腹部容易囤积。面色偏黄,舌头胖大。梅雨季会特别不舒服,头重脚轻。',
|
||||
tips:'健脾祛湿是核心功课。吃饭七分饱,细嚼慢咽——这不是规矩,是爱自己。山药薏米粥、陈皮茶都是好帮手。别老坐着,动起来帮脾胃运转。',
|
||||
diseases:'肥胖、高血脂、脂肪肝、慢性胃炎、消化不良、眩晕、湿疹。',
|
||||
seasonRisk:'长夏(7-8月)最难熬——湿热交蒸,浑身沉重。春天肝气犯脾,容易胃痛腹泻。',
|
||||
foodTherapy:'甘淡的食物最养脾(山药、茯苓、薏米)。黄色食物是脾胃的好朋友(小米、南瓜、玉米)。陈皮理气,生姜暖中。冰的、甜的、腻的——少碰。',
|
||||
dailyRhythm:'早餐7-9点必须吃好吃饱(胃经当令)。饭后散步10分钟助消化。晚餐早一点、少一点。'
|
||||
},
|
||||
'金':{
|
||||
primary:'阴虚质', secondary:'气郁质', strong:'肺', weak:'肝',
|
||||
desc:'你像一把精密的乐器,条理分明、追求完美。但肺为"娇脏",怕燥怕干——皮肤干燥、秋冬咳嗽、喉咙不适都是它在提醒你。秋天是你最要呵护自己的季节,给自己多一点滋润。',
|
||||
body:'偏瘦,皮肤容易干燥起皮,嘴唇干裂。手心脚心热,晚上容易盗汗。秋天特别敏感,容易便秘。',
|
||||
tips:'滋阴润肺是关键。白色食物是你的好朋友(银耳、百合、雪梨)。辛辣烧烤油炸——能躲就躲。早睡比什么都养阴,腹式呼吸能帮你增强肺力。',
|
||||
diseases:'慢性咽炎、干咳、支气管炎、皮肤干燥、便秘、过敏性鼻炎、荨麻疹。',
|
||||
seasonRisk:'秋天最敏感——燥邪伤肺,干咳咽痒。夏天火克金,心肺都要护。',
|
||||
foodTherapy:'白色食物润肺(银耳、百合、山药、雪梨、莲藕)。蜂蜜、麦冬、沙参滋阴润燥。菊花、薄荷清肺。远离辛辣、烧烤、烟酒。',
|
||||
dailyRhythm:'清晨(5-7点大肠经)排清宿便。上午深呼吸5分钟。午后吃个梨或喝银耳羹。晚上9点前入睡养肺阴。保持房间湿度50%-60%。'
|
||||
},
|
||||
'水':{
|
||||
primary:'阳虚质', secondary:'痰湿质', strong:'肾', weak:'心',
|
||||
desc:'你像深潭里的水,沉静而有深度。智慧是你的底色,但阳气不足会让你畏寒怕冷、腰膝酸软、缺乏安全感。肾是先天之本,冬天是你最需要温暖的季节——对自己好一点,从保暖开始。',
|
||||
body:'体型偏丰满或有浮肿感,面色偏白或晦暗。手脚冰凉,晚上起夜多,头发容易掉。腰膝冷痛是常见信号。',
|
||||
tips:'温阳补肾是首要任务。多晒太阳,特别是晒后背——那是你的"阳气充电板"。艾灸关元、命门、腰要暖、脚要热。黑豆、核桃、肉桂都是你的好朋友。',
|
||||
diseases:'慢性肾炎、甲减、骨质疏松、腰椎问题、耳鸣耳聋、不孕不育。寒湿导致关节痛。',
|
||||
seasonRisk:'冬天最难熬——寒气伤阳,浑身发冷。长夏湿气困肾,水肿加重。',
|
||||
foodTherapy:'黑色食物补肾(黑豆、黑芝麻、黑米、桑葚)。核桃、山药、枸杞温补肾阳。海带、紫菜适量。冰镇饮料、生冷瓜果——戒了。',
|
||||
dailyRhythm:'早晨7-9点晒太阳15分钟补阳气。上午搓腰眼至发热。下午5-7点(肾经当令)休息养肾。晚上热水泡脚至微微出汗。冬天早睡晚起。'
|
||||
}
|
||||
};
|
||||
return detail[strong];
|
||||
}
|
||||
|
||||
function getEmotion(strongEl){
|
||||
var m={
|
||||
'木':{
|
||||
strength:['创造力','决策力','学习能力','进取心','目标感'],
|
||||
risk:['焦虑','易怒','急躁','偏头痛倾向','经前紧张(女)'],
|
||||
advice:'遇事深呼吸三秒再回应。每日快走30分钟疏解肝气。学习正念冥想。',
|
||||
color:'绿'
|
||||
},
|
||||
'火':{
|
||||
strength:['热情','感染力','表达力','社交能力','行动力'],
|
||||
risk:['失眠','焦躁','心神不宁','多梦','舌尖溃疡'],
|
||||
advice:'午时(11-13点)小憩养心。少刷手机,睡前温水泡脚。练习书法静心。',
|
||||
color:'红'
|
||||
},
|
||||
'土':{
|
||||
strength:['稳重','包容','耐心','执行力','可信赖'],
|
||||
risk:['思虑过度','脾胃不适','优柔寡断','过度操心'],
|
||||
advice:'饭后散步10分钟助运化。学会说"不",减轻负担。培养一个动手爱好。',
|
||||
color:'黄'
|
||||
},
|
||||
'金':{
|
||||
strength:['条理','决断','专注','毅力','原则性'],
|
||||
risk:['悲伤','忧愁','呼吸道敏感','皮肤干燥','便秘'],
|
||||
advice:'练习腹式呼吸强肺。多与朋友交流倾诉。保持环境湿度。',
|
||||
color:'白'
|
||||
},
|
||||
'水':{
|
||||
strength:['智慧','洞察','冷静','适应力','深度思考'],
|
||||
risk:['恐惧','孤独感','腰膝酸软','听力下降','记忆力减退'],
|
||||
advice:'冬季注意腰腹保暖。练习站桩固肾。多听音乐舒缓恐惧。',
|
||||
color:'黑'
|
||||
}
|
||||
};
|
||||
return m[strongEl];
|
||||
}
|
||||
|
||||
function getWYLQ(year){
|
||||
var gz=gzYear(year);
|
||||
var tgI=TG.indexOf(gz.tg);
|
||||
var zy=TGE[gz.tg]; // 中运
|
||||
var tg2=tgI%2===0; // 太过
|
||||
var dzI=DZ.indexOf(gz.dz);
|
||||
var sitianMap={'子':'午','丑':'未','寅':'申','卯':'酉','辰':'戌','巳':'亥','午':'子','未':'丑','申':'寅','酉':'卯','戌':'辰','亥':'巳'};
|
||||
var st=DZ[DZ.indexOf(sitianMap[gz.dz])];
|
||||
var zq=st;
|
||||
var stepNum=Math.floor((new Date().getMonth())/2);
|
||||
var zhuqi=['厥阴风木','少阴君火','少阳相火','太阴湿土','阳明燥金','太阳寒水'];
|
||||
var keqiOrder = [];
|
||||
var stIdx=DZ.indexOf(st);
|
||||
for(var k=0;k<6;k++) keqiOrder.push(DZ[(stIdx+k)%12]);
|
||||
return{zhongyun:zy,taiguo:tg2,sitian:st,zaiquan:zq,zhuqi:zhuqi,keqiOrder:keqiOrder,stepNum:stepNum,yearTg:gz.tg,yearDz:gz.dz};
|
||||
}
|
||||
|
||||
function getCurAnalysis(){
|
||||
var now=new Date();
|
||||
var w=getWYLQ(now.getFullYear());
|
||||
var term=getST(now);
|
||||
var mDz=getMDz(term);
|
||||
var m=DZE[mDz];
|
||||
var seMap={3:'木',4:'木',5:'火',6:'火',7:'土',8:'土',9:'金',10:'金',11:'水',12:'水',1:'水',2:'木'};
|
||||
var se=seMap[now.getMonth()+1];
|
||||
var ww={};
|
||||
EO.forEach(function(e){
|
||||
var i=EO.indexOf(e), si=EO.indexOf(se);
|
||||
if(e===se)ww[e]='旺';
|
||||
else if(e===EO[(si+1)%5])ww[e]='相';
|
||||
else if(e===EO[(si+4)%5])ww[e]='休';
|
||||
else if(e===EO[(si+2)%5])ww[e]='囚';
|
||||
else ww[e]='死';
|
||||
});
|
||||
var step=Math.floor((now.getMonth())/2);
|
||||
return{date:now,year:now.getFullYear(),term:term,monthDz:mDz,monthEl:m,seasonEl:se,waxWane:ww,wylq:w,step:step};
|
||||
}
|
||||
|
||||
// 子午流注 12 时辰
|
||||
var meridian=[
|
||||
{h:1,n:'丑时',o:'肝经',act:'熟睡养肝血',tip:'深度睡眠黄金期'},
|
||||
{h:3,n:'寅时',o:'肺经',act:'深睡养肺气',tip:'哮喘易发时段'},
|
||||
{h:5,n:'卯时',o:'大肠经',act:'起床排便',tip:'最佳排便时间'},
|
||||
{h:7,n:'辰时',o:'胃经',act:'吃早餐',tip:'胃气最旺时进食'},
|
||||
{h:9,n:'巳时',o:'脾经',act:'工作学习',tip:'脾运化最佳'},
|
||||
{h:11,n:'午时',o:'心经',act:'午餐小憩',tip:'养心阴黄金30分钟'},
|
||||
{h:13,n:'未时',o:'小肠经',act:'消化吸收',tip:'多喝水助排浊'},
|
||||
{h:15,n:'申时',o:'膀胱经',act:'学习记忆',tip:'记忆力最佳时段'},
|
||||
{h:17,n:'酉时',o:'肾经',act:'休息养肾',tip:'不宜剧烈运动'},
|
||||
{h:19,n:'戌时',o:'心包经',act:'放松散步',tip:'保持心情愉快'},
|
||||
{h:21,n:'亥时',o:'三焦经',act:'准备入睡',tip:'泡脚听轻音乐'},
|
||||
{h:23,n:'子时',o:'胆经',act:'深度睡眠',tip:'必须睡觉养胆气'}
|
||||
];
|
||||
|
||||
// 五行-时令风险库(25种组合)
|
||||
var riskDB={
|
||||
'木_火':{t:'心火偏旺',d:'木生火,夏季火旺叠加先天木盛,心火易亢。木火相煽,热扰心神。',sy:['失眠多梦','焦躁易怒','口干心烦','舌尖红','小便黄'],act:'午时小憩养心,睡前温水泡脚。喝莲子心茶清心火。减少咖啡和激烈运动。按揉神门穴、内关穴。'},
|
||||
'木_木':{t:'肝气过亢',d:'春木逢木,肝气太过。木旺生风,上扰清窍。',sy:['头晕目眩','偏头痛','血压波动','眼干眼胀'],act:'每日快走30分钟疏肝。喝菊花枸杞茶清肝明目。按揉太冲穴、行间穴。避免熬夜和情绪激动。'},
|
||||
'木_土':{t:'肝木克土',d:'先天木旺克制脾土,脾胃运化受阻。',sy:['食欲不振','腹胀嗳气','大便不调','疲倦乏力'],act:'少食多餐,温热饮食。饭前喝一小碗小米粥护脾胃。饭后散步10分钟。按揉足三里、中脘穴。避免生气时进食。'},
|
||||
'木_金':{t:'木侮金',d:'木气过盛反侮肺金。',sy:['干咳少痰','咽喉不适','皮肤过敏'],act:'保持室内湿度,喝蜂蜜梨水润肺。练习腹式呼吸。避免辛辣和干燥环境。'},
|
||||
'木_水':{t:'子盗母气',d:'木旺耗水,肾水暗亏。',sy:['腰膝酸软','耳鸣','脱发增多'],act:'多吃黑芝麻、核桃补肾。搓腰眼至发热。减少熬夜,保证子时(23点前)入睡。'},
|
||||
'火_火':{t:'心火亢盛',d:'火上加火,两阳相劫,耗伤心阴。',sy:['心悸怔忡','盗汗','烦躁不宁','口腔溃疡频发','面赤'],act:'务必午休养心阴。绿豆百合汤清心安神。避免烈日下活动和辛辣食物。睡前温水泡脚引火下行。按揉神门、涌泉穴。'},
|
||||
'火_木':{t:'木火刑金',d:'火得木生更旺,反侮肺金。',sy:['干咳','鼻衄','皮肤干燥','便秘'],act:'桑叶菊花茶清肺润燥。多吃梨、银耳、蜂蜜。保持环境湿润。避免烧烤和烟酒。'},
|
||||
'火_土':{t:'火生土亢',d:'火旺生土过度,湿热内生。',sy:['口苦口臭','大便黏滞','皮肤油腻长痘','头重身困'],act:'薏米赤小豆汤祛湿热。少甜食油腻。保持皮肤清洁干爽。荷叶茶清热利湿。'},
|
||||
'火_水':{t:'火盛水涸',d:'心火亢盛下劫肾水,心肾不交。',sy:['心烦失眠','腰膝酸软','耳鸣','记忆力下降','夜尿频'],act:'黄连阿胶汤类方交通心肾。睡前热水泡脚至微汗。按揉涌泉、太溪穴。避免熬夜和过度用脑。'},
|
||||
'火_金':{t:'火克金',d:'心火乘肺金,肺阴受损。',sy:['干咳无痰','咽干喉痛','皮肤粗糙','毛发干枯'],act:'麦冬沙参茶润肺生津。银耳百合羹每周3次。腹式深呼吸养肺。居室保持50%湿度。'},
|
||||
'土_火':{t:'火生土壅',d:'火旺生土,湿热蕴脾。',sy:['身重困倦','头蒙如裹','口黏不爽','大便溏滞'],act:'薏米茯苓粥健脾祛湿。室内通风除湿。少思虑多运动。按揉阴陵泉、丰隆穴。'},
|
||||
'土_土':{t:'脾湿壅盛',d:'土气过旺,湿浊内停。',sy:['身体沉重','腹胀满闷','痰多','浮肿'],act:'陈皮薏仁茶理气化湿。控制食量七分饱。每天快走40分钟。按揉足三里、天枢穴。'},
|
||||
'土_水':{t:'脾土克水',d:'土盛克水,肾阳受损。',sy:['水肿','小便不利','腰冷痛','畏寒'],act:'茯苓山药粥健脾利水。艾灸关元、命门温阳。避免久坐久卧。'},
|
||||
'土_木':{t:'土虚木乘',d:'湿土困脾,肝木乘虚而入。',sy:['胃痛','反酸','情绪波动时加重'],act:'饭前少量生姜水暖胃。少食多餐减轻脾胃负担。保持情绪稳定,饭后不生气。'},
|
||||
'水_火':{t:'水火不济',d:'夏季火旺克水,先天水盛之人肾水受制,心肾不交。',sy:['心烦不得眠','五心烦热','腰膝酸软','健忘','耳鸣加重'],act:'交泰丸类方交通心肾。睡前搓涌泉穴100次。下午5-7点(肾经当令)休息。少吃生冷,多晒太阳补阳。'},
|
||||
'水_水':{t:'寒湿内盛',d:'水气过旺,寒湿困阻。',sy:['畏寒肢冷','关节疼痛','水肿','小便清长'],act:'艾灸关元、命门、足三里。生姜红糖水温中散寒。多晒太阳尤其后背。冬季泡温泉。忌冰饮冷食。'},
|
||||
'水_木':{t:'水不涵木',d:'水亏不能养木,虚风内动。',sy:['头晕','手抖','失眠'],act:'枸杞菊花茶滋水涵木。早睡养肾阴。减少用眼过度。按揉太冲、太溪穴。'},
|
||||
'水_金':{t:'金不生水',d:'金被火克不能生水,肾水枯竭。',sy:['腰酸','脱发','牙齿松动','骨关节退变'],act:'黑豆核桃粥补肾填精。西洋参麦冬茶气阴双补。搓腰眼每日100次。适当负重运动强骨。'},
|
||||
'金_火':{t:'肺燥津伤',d:'火克金,肺阴受损。燥热伤津,肺失肃降。',sy:['干咳无痰或少痰','咽干鼻燥','皮肤干痒','大便秘结','声音嘶哑'],act:'川贝雪梨汤润肺止咳。室内加湿器保持50%-60%湿度。腹式深呼吸每次5分钟。避免辛辣油炸烧烤。麦冬沙参泡水代茶。'},
|
||||
'金_金':{t:'肺气壅滞',d:'金气过盛,肺失宣降。',sy:['胸闷气短','咳嗽','皮肤干燥脱屑'],act:'白萝卜蜂蜜水宣肺化痰。户外深呼吸新鲜空气。适度有氧运动微汗。按揉太渊、列缺穴。'},
|
||||
'金_木':{t:'金乘肝木',d:'肺金过盛抑制肝木疏泄。',sy:['情绪抑郁','胸胁胀痛','经前不适(女)'],act:'玫瑰花薄荷茶疏肝解郁。多与朋友交流倾诉。快走或瑜伽舒畅气机。按揉太冲、期门穴。'},
|
||||
};
|
||||
+210
@@ -0,0 +1,210 @@
|
||||
/* ---------- 工具 ---------- */
|
||||
function qp(k){return new URLSearchParams(location.search).get(k);}
|
||||
function strongestEl(p){return EO.reduce((a,b)=>p[b]>p[a]?b:a,EO[0]);}
|
||||
function esc(s){return (s||'').replace(/</g,'<');}
|
||||
|
||||
/* ---------- 精简 SVG 三角图(核心数字:I,J,K,L → M,N → O) ---------- */
|
||||
function miniPyramid(v){
|
||||
const base=[v.I,v.J,v.K,v.L], mid=[v.M,v.N], apex=v.O;
|
||||
const CW=42,CH=40,GY=8,W=CW*4+GY*3,cx=W/2;
|
||||
function cell(x,y,n,hl){
|
||||
return '<rect x="'+x+'" y="'+y+'" width="'+CW+'" height="'+CH+'" rx="8" fill="'+(hl?'#C8923A':'#fff8ef')+'" stroke="#e0c48a"/><text x="'+(x+CW/2)+'" y="'+(y+CH/2+5)+'" text-anchor="middle" font-size="17" font-weight="700" fill="'+(hl?'#fff':'#8a6d3b')+'">'+(n!=null?n:'')+'</text>';
|
||||
}
|
||||
var s='<svg width="'+W+'" height="'+(CH*3+GY*2)+'" xmlns="http://www.w3.org/2000/svg">';
|
||||
s+=cell(cx-CW/2,0,apex,true);
|
||||
s+=cell(cx-CW-GY/2,CH+GY,mid[0])+cell(cx+GY/2,CH+GY,mid[1]);
|
||||
base.forEach(function(n,i){ s+=cell(i*(CW+GY),(CH+GY)*2,n); });
|
||||
s+='</svg>';
|
||||
return s;
|
||||
}
|
||||
|
||||
/* ---------- 今日锦囊 ---------- */
|
||||
var SEASON_EL={0:'水',1:'水',2:'木',3:'木',4:'木',5:'火',6:'火',7:'火',8:'土',9:'金',10:'金',11:'水'};
|
||||
var YI_JI={
|
||||
木:{yi:'早睡养肝血 · 舒展拉伸 · 绿色蔬菜',ji:'久坐 · 生闷气 · 熬夜'},
|
||||
火:{yi:'午休15分钟 · 菊花/莲子 · 平心静气',ji:'暴晒 · 辛辣油腻 · 过度兴奋'},
|
||||
土:{yi:'规律三餐 · 健脾山药 · 饭后散步',ji:'思虑过度 · 生冷 · 暴饮暴食'},
|
||||
金:{yi:'滋润养肺 · 白色食物 · 深呼吸',ji:'干燥受凉 · 悲忧 · 剧烈耗气'},
|
||||
水:{yi:'早卧晚起 · 温补黑色食物 · 护腰腹',ji:'受寒 · 过劳 · 惊恐紧张'}
|
||||
};
|
||||
function todaySolar(){
|
||||
var now=new Date(),m=now.getMonth()+1,d=now.getDate();
|
||||
var cur=ST[0];
|
||||
for(var i=0;i<ST.length;i++){ var t=ST[i];if(m>t.m||(m===t.m&&d>=t.d)) cur=t; }
|
||||
return cur;
|
||||
}
|
||||
|
||||
/* ---------- 主渲染 ---------- */
|
||||
function render(){
|
||||
var y=+qp('y'),m=+qp('m'),d=+qp('d');
|
||||
var box=document.getElementById('report');
|
||||
if(!y||!m||!d||y<1900||y>2100){
|
||||
box.innerHTML='<div class="err">缺少有效生日参数,请从首页重新进入 🙏</div>';return;
|
||||
}
|
||||
document.getElementById('disc').style.display='block';
|
||||
|
||||
// === 数字引擎 ===
|
||||
var v=calc(y,m,d);
|
||||
var mainNum=v.O;
|
||||
var person=NUM[mainNum]||{title:'',pos:'',neg:'',job:'',desc:''};
|
||||
|
||||
// === 养生引擎(无时辰,默认午时12点) ===
|
||||
var bz=getBazi(new Date(y,m-1,d),12);
|
||||
var p5=calc5E(bz);
|
||||
var strong=strongestEl(p5);
|
||||
var cst=getConst(p5);
|
||||
var emo=getEmotion(strong);
|
||||
|
||||
// === 今日锦囊 ===
|
||||
var st=todaySolar();
|
||||
var sEl=SEASON_EL[new Date().getMonth()];
|
||||
var yj=YI_JI[sEl];
|
||||
|
||||
var paid=localStorage.getItem('jiema_paid')==='1';
|
||||
|
||||
box.innerHTML=
|
||||
'<!-- Part1 数字性格 -->'+
|
||||
'<div class="card">'+
|
||||
'<span class="tag tag-num">✨ 数字性格密码</span>'+
|
||||
'<div class="hex">'+mainNum+'</div>'+
|
||||
'<div class="p-title">'+mainNum+'号人 · '+esc(person.title)+'</div>'+
|
||||
'<div class="p-oneline">'+esc((person.desc||'').split(/[。.]/)[0])+'</div>'+
|
||||
'<div class="pyr">'+miniPyramid(v)+'</div>'+
|
||||
'<div class="lock '+(paid?'unlocked':'')+'" id="lock-num">'+
|
||||
'<div class="blur">'+
|
||||
'<b>天赋优势:</b>'+esc(person.pos)+'<br>'+
|
||||
'<b>人生课题:</b>'+esc(person.neg)+'<br>'+
|
||||
'<b>适合方向:</b>'+esc(person.job)+'<br>'+
|
||||
'<b>能量解读:</b>'+esc(person.desc)+
|
||||
'</div>'+
|
||||
'<div class="cover">'+
|
||||
'<div class="lk-t">🔒 解锁你的完整密码</div>'+
|
||||
'<div class="lk-d">天赋优势 · 人生课题 · 适合方向 · 12组联合码深度解读</div>'+
|
||||
'<button class="unlock-btn" onclick="unlock()">¥39.9 立即解锁 →</button>'+
|
||||
'</div>'+
|
||||
'</div>'+
|
||||
'</div>'+
|
||||
|
||||
'<!-- Part2 五行体质 -->'+
|
||||
'<div class="card">'+
|
||||
'<span class="tag tag-ys">🌿 五行体质画像</span>'+
|
||||
'<div class="ys-type">'+esc(cst.primary)+'</div>'+
|
||||
'<div class="ys-dir">核心养护:养'+esc(cst.strong)+' · 需呵护'+esc(cst.weak)+'</div>'+
|
||||
'<div class="bars">'+
|
||||
EO.map(function(e){return '<div class="bar-row"><span class="lab" style="color:'+EC[e]+'">'+e+'</span><div class="bar-track"><div class="bar-fill" style="width:'+p5[e]+'%;background:'+EC[e]+'"></div></div><span class="val">'+p5[e]+'%</span></div>';}).join('')+
|
||||
'</div>'+
|
||||
'<div class="p-oneline">🎭 情绪画像:'+esc((emo.strength||[]).join(' · '))+'</div>'+
|
||||
'<div class="lock '+(paid?'unlocked':'')+'" id="lock-ys">'+
|
||||
'<div class="blur">'+
|
||||
'<b>体质特征:</b>'+esc(cst.body)+'<br>'+
|
||||
'<b>调养要点:</b>'+esc(cst.tips)+'<br>'+
|
||||
'<b>易感倾向:</b>'+esc(cst.diseases)+'<br>'+
|
||||
'<b>季节风险:</b>'+esc(cst.seasonRisk)+'<br>'+
|
||||
'<b>食疗方向:</b>'+esc(cst.foodTherapy)+'<br>'+
|
||||
'<b>作息节律:</b>'+esc(cst.dailyRhythm)+
|
||||
'</div>'+
|
||||
'<div class="cover">'+
|
||||
'<div class="lk-t">🔒 解锁五行体质深度分析</div>'+
|
||||
'<div class="lk-d">脏腑画像 · 易感倾向 · 食疗 · 运动作息方案</div>'+
|
||||
'<button class="unlock-btn" onclick="unlock()">¥39.9 立即解锁 →</button>'+
|
||||
'</div>'+
|
||||
'</div>'+
|
||||
'</div>'+
|
||||
|
||||
'<!-- Part3 今日养生锦囊(免费) -->'+
|
||||
'<div class="card jn">'+
|
||||
'<span class="tag tag-ys">🎯 今日养生锦囊</span>'+
|
||||
'<div class="st">当前节气:'+st.n+' · 时令 '+sEl+' 当令</div>'+
|
||||
'<h4>今日调养建议</h4>'+
|
||||
'<div class="row"><b>宜:</b>'+yj.yi+'</div>'+
|
||||
'<div class="row"><b>忌:</b>'+yj.ji+'</div>'+
|
||||
'</div>'+
|
||||
|
||||
'<!-- 深度入口 -->'+
|
||||
'<div class="deep">'+
|
||||
'<a class="d-num" href="shuzi.html?y='+y+'&m='+m+'&d='+d+'">完整数字报告 →</a>'+
|
||||
'<a class="d-ys" href="health.html?y='+y+'&m='+m+'&d='+d+'&h=12">完整养生方案 →</a>'+
|
||||
'</div>'+
|
||||
|
||||
'<!-- 契合度(免费给分数) -->'+
|
||||
'<div class="card">'+
|
||||
'<span class="tag tag-num">💞 测测你和 TA 的契合度</span>'+
|
||||
'<div class="mini-form">'+
|
||||
'<input type="number" id="tY" placeholder="年"><span>年</span>'+
|
||||
'<input type="number" id="tM" placeholder="月"><span>月</span>'+
|
||||
'<input type="number" id="tD" placeholder="日"><span>日</span>'+
|
||||
'<button onclick="matchTest('+y+','+m+','+d+')">测算</button>'+
|
||||
'</div>'+
|
||||
'<div id="matchOut"></div>'+
|
||||
'</div>'+
|
||||
|
||||
'<!-- 分享 -->'+
|
||||
'<div class="card">'+
|
||||
'<span class="tag tag-num">🦋 分享你的身心密码</span>'+
|
||||
'<button class="share-btn" onclick="genShare(\''+mainNum+'号人 · '+esc(person.title)+'\',\''+esc(cst.primary)+'\',\'养'+esc(cst.strong)+'\')">📱 生成分享卡片</button>'+
|
||||
'</div>';
|
||||
}
|
||||
|
||||
/* ---------- 付费墙 ---------- */
|
||||
function unlock(){
|
||||
document.getElementById('payModal').classList.add('on');
|
||||
}
|
||||
function doUnlock(){
|
||||
localStorage.setItem('jiema_paid','1');
|
||||
document.querySelectorAll('.lock').forEach(function(el){el.classList.add('unlocked');});
|
||||
document.getElementById('payModal').classList.remove('on');
|
||||
}
|
||||
|
||||
/* ---------- 契合度(免费仅分数,深度引导付费/双人报告) ---------- */
|
||||
function matchTest(y1,m1,d1){
|
||||
var y=+document.getElementById('tY').value,
|
||||
m=+document.getElementById('tM').value,
|
||||
d=+document.getElementById('tD').value;
|
||||
if(!y||!m||!d){alert('请输入 TA 的完整生日');return;}
|
||||
var a=calc(y1,m1,d1).O, b=calc(y,m,d).O;
|
||||
var diff=Math.abs(a-b);
|
||||
var score=92-diff*6+((a+b)%3)*3;
|
||||
score=Math.max(60,Math.min(98,score));
|
||||
document.getElementById('matchOut').innerHTML=
|
||||
'<div class="match-score">'+score+' 分</div>'+
|
||||
'<div class="lock" style="margin-top:8px">'+
|
||||
'<div class="blur">你们在沟通节奏、能量互补、相处课题上的详细解读……</div>'+
|
||||
'<div class="cover">'+
|
||||
'<div class="lk-t">🔒 解锁完整契合度解读</div>'+
|
||||
'<div class="lk-d">关系能量图 · 相处建议 · 沟通指南</div>'+
|
||||
'<button class="unlock-btn" onclick="alert(\'双人契合度报告 ¥129(模拟)\')">¥129 查看完整解读 →</button>'+
|
||||
'</div>'+
|
||||
'</div>';
|
||||
}
|
||||
|
||||
/* ---------- 分享卡片(纯 canvas,无外部库) ---------- */
|
||||
function genShare(numTitle,constType,ysKey){
|
||||
var c=document.getElementById('shareCanvas'),g=c.getContext('2d');
|
||||
var W=c.width,H=c.height;
|
||||
var grd=g.createLinearGradient(0,0,0,H);
|
||||
grd.addColorStop(0,'#FF9F80');grd.addColorStop(1,'#FFC8B5');
|
||||
g.fillStyle=grd;g.fillRect(0,0,W,H);
|
||||
g.fillStyle='#fff';g.textAlign='center';
|
||||
g.font='bold 40px sans-serif';g.fillText('愈心解码',W/2,90);
|
||||
g.font='20px sans-serif';g.fillText('一个生日,读懂身与心',W/2,128);
|
||||
// 卡片
|
||||
g.fillStyle='rgba(255,255,255,.95)';
|
||||
roundRect(g,60,180,W-120,380,24);g.fill();
|
||||
g.fillStyle='#8a6d3b';g.font='bold 46px sans-serif';
|
||||
g.fillText(numTitle,W/2,290);
|
||||
g.fillStyle='#5CB85C';g.font='bold 40px sans-serif';
|
||||
g.fillText(constType,W/2,390);
|
||||
g.fillStyle='#666';g.font='24px sans-serif';
|
||||
g.fillText('养生重点:'+ysKey,W/2,460);
|
||||
g.fillStyle='#E54D42';g.font='20px sans-serif';
|
||||
g.fillText('▶ 扫码解锁你的身心密码',W/2,530);
|
||||
g.fillStyle='#fff';g.font='18px sans-serif';
|
||||
g.fillText('愈心谷 · 愈见魔方,遇见自己',W/2,H-40);
|
||||
document.getElementById('shareModal').classList.add('on');
|
||||
}
|
||||
function roundRect(g,x,y,w,h,r){
|
||||
g.beginPath();g.moveTo(x+r,y);g.arcTo(x+w,y,x+w,y+h,r);
|
||||
g.arcTo(x+w,y+h,x,y+h,r);g.arcTo(x,y+h,x,y,r);g.arcTo(x,y,x+w,y,r);g.closePath();
|
||||
}
|
||||
|
||||
render();
|
||||
+431
@@ -0,0 +1,431 @@
|
||||
// ===================== REPORTS =====================
|
||||
function wkReport(p,con,emo,cur){
|
||||
var s=EO.slice().sort(function(a,b){return p[b]-p[a];});
|
||||
var st=s[0], se=cur.seasonEl, ww=cur.waxWane;
|
||||
var om={木:'肝',火:'心',土:'脾',金:'肺',水:'肾'};
|
||||
var os={};
|
||||
EO.forEach(function(e){
|
||||
var b=p[e],mod=0;
|
||||
if(ww[e]==='旺')mod=20;else if(ww[e]==='相')mod=10;else if(ww[e]==='休')mod=-5;else if(ww[e]==='囚')mod=-15;else mod=-25;
|
||||
os[om[e]]=Math.max(1,Math.min(5,Math.round((b+mod)/20)));
|
||||
});
|
||||
|
||||
// Element interaction
|
||||
var gen=EO[(EO.indexOf(st)+1)%5], ke=EO[(EO.indexOf(st)+2)%5], beiGen=EO[(EO.indexOf(st)+4)%5], beiKe=EO[(EO.indexOf(st)+3)%5];
|
||||
var interaction={gen:gen, ke:ke, beiGen:beiGen, beiKe:beiKe};
|
||||
|
||||
// Risks - comprehensive
|
||||
var risks=[];
|
||||
var riskDB={
|
||||
'木_火':{t:'心火偏旺',d:'木生火,夏季火旺叠加先天木盛,心火易亢。木火相煽,热扰心神。',sy:['失眠多梦','焦躁易怒','口干心烦','舌尖红','小便黄'],act:'午时小憩养心,睡前温水泡脚。喝莲子心茶清心火。减少咖啡和激烈运动。按揉神门穴、内关穴。'},
|
||||
'木_木':{t:'肝气过亢',d:'春木逢木,肝气太过。木旺生风,上扰清窍。',sy:['头晕目眩','偏头痛','血压波动','眼干眼胀'],act:'每日快走30分钟疏肝。喝菊花枸杞茶清肝明目。按揉太冲穴、行间穴。避免熬夜和情绪激动。'},
|
||||
'木_土':{t:'肝木克土',d:'先天木旺克制脾土,脾胃运化受阻。'+(ww['土']==='囚'?'当前土气被困,脾胃尤为脆弱。':''),sy:['食欲不振','腹胀嗳气','大便不调','疲倦乏力'],act:'少食多餐,温热饮食。饭前喝一小碗小米粥护脾胃。饭后散步10分钟。按揉足三里、中脘穴。避免生气时进食。'},
|
||||
'木_金':{t:'木侮金',d:'木气过盛反侮肺金。',sy:['干咳少痰','咽喉不适','皮肤过敏'],act:'保持室内湿度,喝蜂蜜梨水润肺。练习腹式呼吸。避免辛辣和干燥环境。'},
|
||||
'木_水':{t:'子盗母气',d:'木旺耗水,肾水暗亏。',sy:['腰膝酸软','耳鸣','脱发增多'],act:'多吃黑芝麻、核桃补肾。搓腰眼至发热。减少熬夜,保证子时(23点前)入睡。'},
|
||||
'火_火':{t:'心火亢盛',d:'火上加火,两阳相劫,耗伤心阴。',sy:['心悸怔忡','盗汗','烦躁不宁','口腔溃疡频发','面赤'],act:'务必午休养心阴。绿豆百合汤清心安神。避免烈日下活动和辛辣食物。睡前温水泡脚引火下行。按揉神门、涌泉穴。'},
|
||||
'火_木':{t:'木火刑金',d:'火得木生更旺,反侮肺金。',sy:['干咳','鼻衄','皮肤干燥','便秘'],act:'桑叶菊花茶清肺润燥。多吃梨、银耳、蜂蜜。保持环境湿润。避免烧烤和烟酒。'},
|
||||
'火_土':{t:'火生土亢',d:'火旺生土过度,湿热内生。',sy:['口苦口臭','大便黏滞','皮肤油腻长痘','头重身困'],act:'薏米赤小豆汤祛湿热。少甜食油腻。保持皮肤清洁干爽。荷叶茶清热利湿。'},
|
||||
'火_水':{t:'火盛水涸',d:'心火亢盛下劫肾水,心肾不交。',sy:['心烦失眠','腰膝酸软','耳鸣','记忆力下降','夜尿频'],act:'黄连阿胶汤类方交通心肾。睡前热水泡脚至微汗。按揉涌泉、太溪穴。避免熬夜和过度用脑。'},
|
||||
'火_金':{t:'火克金',d:'心火乘肺金,肺阴受损。',sy:['干咳无痰','咽干喉痛','皮肤粗糙','毛发干枯'],act:'麦冬沙参茶润肺生津。银耳百合羹每周3次。腹式深呼吸养肺。居室保持50%湿度。'},
|
||||
'土_火':{t:'火生土壅',d:'火旺生土,湿热蕴脾。',sy:['身重困倦','头蒙如裹','口黏不爽','大便溏滞'],act:'薏米茯苓粥健脾祛湿。室内通风除湿。少思虑多运动。按揉阴陵泉、丰隆穴。'},
|
||||
'土_土':{t:'脾湿壅盛',d:'土气过旺,湿浊内停。',sy:['身体沉重','腹胀满闷','痰多','浮肿'],act:'陈皮薏仁茶理气化湿。控制食量七分饱。每天快走40分钟。按揉足三里、天枢穴。'},
|
||||
'土_水':{t:'脾土克水',d:'土盛克水,肾阳受损。',sy:['水肿','小便不利','腰冷痛','畏寒'],act:'茯苓山药粥健脾利水。艾灸关元、命门温阳。避免久坐久卧。'},
|
||||
'土_木':{t:'土虚木乘',d:'湿土困脾,肝木乘虚而入。',sy:['胃痛','反酸','情绪波动时加重'],act:'饭前少量生姜水暖胃。少食多餐减轻脾胃负担。保持情绪稳定,饭后不生气。'},
|
||||
'水_火':{t:'水火不济',d:'夏季火旺克水,先天水盛之人肾水受制,心肾不交。',sy:['心烦不得眠','五心烦热','腰膝酸软','健忘','耳鸣加重'],act:'交泰丸类方交通心肾。睡前搓涌泉穴100次。下午5-7点(肾经当令)休息。少吃生冷,多晒太阳补阳。'},
|
||||
'水_水':{t:'寒湿内盛',d:'水气过旺,寒湿困阻。',sy:['畏寒肢冷','关节疼痛','水肿','小便清长'],act:'艾灸关元、命门、足三里。生姜红糖水温中散寒。多晒太阳尤其后背。冬季泡温泉。忌冰饮冷食。'},
|
||||
'水_木':{t:'水不涵木',d:'水亏不能养木,虚风内动。',sy:['头晕','手抖','失眠'],act:'枸杞菊花茶滋水涵木。早睡养肾阴。减少用眼过度。按揉太冲、太溪穴。'},
|
||||
'水_金':{t:'金不生水',d:'金被火克不能生水,肾水枯竭。',sy:['腰酸','脱发','牙齿松动','骨关节退变'],act:'黑豆核桃粥补肾填精。西洋参麦冬茶气阴双补。搓腰眼每日100次。适当负重运动强骨。'},
|
||||
'金_火':{t:'肺燥津伤',d:'火克金,肺阴受损。燥热伤津,肺失肃降。',sy:['干咳无痰或少痰','咽干鼻燥','皮肤干痒','大便秘结','声音嘶哑'],act:'川贝雪梨汤润肺止咳。室内加湿器保持50%-60%湿度。腹式深呼吸每次5分钟。避免辛辣油炸烧烤。麦冬沙参泡水代茶。'},
|
||||
'金_金':{t:'肺气壅滞',d:'金气过盛,肺失宣降。',sy:['胸闷气短','咳嗽','皮肤干燥脱屑'],act:'白萝卜蜂蜜水宣肺化痰。户外深呼吸新鲜空气。适度有氧运动微汗。按揉太渊、列缺穴。'},
|
||||
'金_木':{t:'金乘肝木',d:'肺金过盛抑制肝木疏泄。',sy:['情绪抑郁','胸胁胀痛','经前不适(女)'],act:'玫瑰花薄荷茶疏肝解郁。多与朋友交流倾诉。快走或瑜伽舒畅气机。按揉太冲、期门穴。'},
|
||||
};
|
||||
var rk=riskDB[st+'_'+se] || riskDB[st+'_'+st];
|
||||
if(rk) risks.push(rk);
|
||||
// Add a second risk based on beiKe
|
||||
var rk2=riskDB[beiKe+'_'+se];
|
||||
if(rk2 && rk2.t!==(rk?rk.t:'')) risks.push(rk2);
|
||||
if(risks.length===0) risks.push({t:'整体协调',d:'当前时空与先天体质较为协调,继续保持良好生活习惯即可。',sy:['保持规律作息','适度运动','均衡饮食']});
|
||||
|
||||
// Diet
|
||||
var dietDB={
|
||||
'火':{rec:['绿豆','苦瓜','莲子','百合','冬瓜','西瓜','黄瓜','梨','荸荠','藕'],avoid:['烧烤','火锅','烈酒','辣椒','羊肉','榴莲','荔枝','龙眼','咖喱','大蒜'],reason:'清热泻火,养阴生津。避免辛温助热之品。',tea:'莲子心竹叶茶:莲子心3g + 淡竹叶5g,沸水冲泡,清心降火。菊花枸杞茶:菊花5朵 + 枸杞10粒,清肝明目。'},
|
||||
'木':{rec:['菠菜','芹菜','西兰花','绿茶','山楂','柠檬','佛手','薄荷','荞麦'],avoid:['油腻厚味','甜食','酒精','咖啡过量','麻辣'],reason:'疏肝理气,养血柔肝。避免壅滞气机。',tea:'玫瑰花佛手茶:玫瑰花5朵 + 佛手干3片,疏肝解郁。薄荷柠檬茶:鲜薄荷叶5片 + 柠檬2片,舒畅气机。'},
|
||||
'土':{rec:['山药','薏米','茯苓','红枣','小米','南瓜','白扁豆','陈皮','生姜','莲子'],avoid:['生冷食物','冰饮','甜腻','肥甘厚味','牛奶过量'],reason:'健脾祛湿,温运中焦。避免寒凉伤脾。',tea:'陈皮薏仁茶:陈皮5g + 炒薏米15g,理气化湿。生姜红枣茶:生姜3片 + 红枣5枚,温中健脾。'},
|
||||
'金':{rec:['银耳','百合','蜂蜜','梨','白萝卜','豆腐','杏仁','麦冬','沙参','莲藕'],avoid:['辛辣','烧烤','油炸','烟酒','干果'],reason:'滋阴润肺,生津润燥。避免辛燥伤阴。',tea:'麦冬沙参茶:麦冬10g + 沙参10g,润肺生津。蜂蜜雪梨水:雪梨1个切块+蜂蜜,煮水代茶。'},
|
||||
'水':{rec:['黑豆','核桃','黑芝麻','枸杞','山药','羊肉(冬季)','韭菜','肉桂','生姜'],avoid:['寒凉食物','冰镇饮品','生冷瓜果过量'],reason:'温补肾阳,固摄精气。避免寒凉伤阳。',tea:'桂圆枸杞茶:桂圆5颗 + 枸杞15粒 + 生姜2片,温补肾阳。黑豆核桃露:黑豆30g + 核桃3颗打浆煮饮。'}
|
||||
};
|
||||
var dt=se==='土'&&st==='火'?dietDB['火']:dietDB[se];
|
||||
var dt2=dietDB[beiKe];
|
||||
|
||||
// Exercise
|
||||
var exDB={
|
||||
'火':{rec:['八段锦','游泳','太极','瑜伽冥想','快走','站桩'],avoid:['高强度熬汗','马拉松','HIIT','烈日下运动'],reason:'微汗为度,汗为心之液,过汗伤心阴。'},
|
||||
'木':{rec:['快走','慢跑','太极','瑜伽','伸展运动','舞蹈'],avoid:['过度竞技运动','压抑性运动'],reason:'运动疏肝,舒畅气机。避免过量压抑肝气。'},
|
||||
'土':{rec:['散步','八段锦','太极','轻度力量训练'],avoid:['长时间久坐','饭后立即剧烈运动'],reason:'适度运动助脾运化,过劳伤脾。'},
|
||||
'金':{rec:['深呼吸练习','太极','瑜伽','游泳','慢跑'],avoid:['干燥环境中运动','过度出汗'],reason:'养肺气,润皮毛。避免燥邪伤肺。'},
|
||||
'水':{rec:['站桩','太极','散步','温和瑜伽','八段锦'],avoid:['冬季晨练过早','大汗淋漓','冷水泳'],reason:'固肾藏精,微汗养阳。避免过耗阳气。'}
|
||||
};
|
||||
var ex=exDB[se];
|
||||
|
||||
// Sleep
|
||||
var slDB={
|
||||
'火':{bt:'22:30',wt:'06:00',focus:'23:00前入睡养胆清心。午时(11-13点)小憩15-30分钟养心阴。'},
|
||||
'木':{bt:'22:30',wt:'06:30',focus:'23:00前入睡养肝血。睡前温水泡脚引火下行。避免睡前思虑。'},
|
||||
'土':{bt:'22:00',wt:'06:30',focus:'早睡养脾。晚餐宜早宜少,睡前3小时不进食。'},
|
||||
'金':{bt:'21:30',wt:'06:00',focus:'早卧早起养肺气。保持卧室湿度,避免干燥。'},
|
||||
'水':{bt:'21:30',wt:'07:00',focus:'早睡晚起养肾阳。睡前热水泡脚至微汗。注意腰腹保暖。'}
|
||||
};
|
||||
var sl=slDB[se];
|
||||
|
||||
// Acupoints
|
||||
var acuDB={
|
||||
'火':['神门','内关','涌泉','百会'],
|
||||
'木':['太冲','行间','期门','肝俞'],
|
||||
'土':['足三里','中脘','脾俞','阴陵泉'],
|
||||
'金':['太渊','尺泽','肺俞','列缺'],
|
||||
'水':['太溪','关元','命门','肾俞']
|
||||
};
|
||||
var acu=acuDB[beiKe]||acuDB[st];
|
||||
|
||||
// Quotes
|
||||
var quotes=[
|
||||
{t:'春夏养阳,秋冬养阴。',e:'当前属'+({'木':'春','火':'夏','土':'长夏','金':'秋','水':'冬'}[se]||se)+'季,应顺应阳气生长之势,避免过度耗散,保持规律作息。'},
|
||||
{t:'正气存内,邪不可干。',e:'保持内在平衡是抵御外邪的根本。当前注意调和阴阳,顺应天时变化。'},
|
||||
{t:'上古之人,其知道者,法于阴阳,和于术数。',e:'养生之道在于遵循自然规律。饮食有节,起居有常,不妄作劳。'},
|
||||
{t:'虚邪贼风,避之有时,恬惔虚无,真气从之。',e:'顺应四时避开外邪,内心平和则正气自足。当前时令尤需注意。'},
|
||||
{t:'阴平阳秘,精神乃治。',e:'阴阳平衡是健康的根本。当前'+se+'气偏盛,注意调和阴阳,不过不不及。'}
|
||||
];
|
||||
var q=quotes[Math.floor(Math.random()*quotes.length)];
|
||||
|
||||
return{organScores:os,risks:risks,diet:dt,diet2:dt2,exercise:ex,sleep:sl,acupoints:acu,quote:q,interaction:interaction,term:cur.term,se:se,strongest:st,wylq:cur.wylq,step:cur.step,waxWane:ww};
|
||||
}
|
||||
|
||||
function dyReport(p,con,emo,cur){
|
||||
var wk=wkReport(p,con,emo,cur);
|
||||
var h=new Date().getHours();
|
||||
var meridian=[
|
||||
{h:1,n:'丑时',o:'肝经',act:'熟睡养肝血',tip:'深度睡眠黄金期'},
|
||||
{h:3,n:'寅时',o:'肺经',act:'深睡养肺气',tip:'哮喘易发时段'},
|
||||
{h:5,n:'卯时',o:'大肠经',act:'起床排便',tip:'最佳排便时间'},
|
||||
{h:7,n:'辰时',o:'胃经',act:'吃早餐',tip:'胃气最旺时进食'},
|
||||
{h:9,n:'巳时',o:'脾经',act:'工作学习',tip:'脾运化最佳'},
|
||||
{h:11,n:'午时',o:'心经',act:'午餐小憩',tip:'养心阴黄金30分钟'},
|
||||
{h:13,n:'未时',o:'小肠经',act:'消化吸收',tip:'多喝水助排浊'},
|
||||
{h:15,n:'申时',o:'膀胱经',act:'学习记忆',tip:'记忆力最佳时段'},
|
||||
{h:17,n:'酉时',o:'肾经',act:'休息养肾',tip:'不宜剧烈运动'},
|
||||
{h:19,n:'戌时',o:'心包经',act:'放松散步',tip:'保持心情愉快'},
|
||||
{h:21,n:'亥时',o:'三焦经',act:'准备入睡',tip:'泡脚听轻音乐'},
|
||||
{h:23,n:'子时',o:'胆经',act:'深度睡眠',tip:'必须睡觉养胆气'}
|
||||
];
|
||||
var currM=meridian.find(function(m){return Math.abs(m.h-h)<=1;})||meridian[0];
|
||||
var scores=[
|
||||
{l:'睡眠',v:Math.floor(Math.random()*16+80),c:'#5CB85C'},
|
||||
{l:'饮食',v:Math.floor(Math.random()*21+65),c:'#E8985A'},
|
||||
{l:'情绪',v:Math.floor(Math.random()*26+60),c:'#E54D42'},
|
||||
{l:'运动',v:Math.floor(Math.random()*16+80),c:'#4A90E2'}
|
||||
];
|
||||
var yiDB={
|
||||
'火':['喝菊花茶','午休15分钟','散步','听轻音乐','温水泡脚','深呼吸'],
|
||||
'木':['喝玫瑰花茶','快走30分钟','和朋友聊天','拉伸运动','看绿色植物'],
|
||||
'土':['喝陈皮水','饭后散步','吃温热食物','按摩足三里','少食多餐'],
|
||||
'金':['喝蜂蜜水','腹式呼吸','吃梨或银耳','早睡','室内加湿'],
|
||||
'水':['喝姜枣茶','晒太阳','热水泡脚','搓腰眼','穿暖不贪凉']
|
||||
};
|
||||
var jiDB={
|
||||
'火':['熬夜','暴饮暴食','情绪激动','过量咖啡','麻辣火锅','烈日暴晒'],
|
||||
'木':['压抑情绪','过量饮酒','熬夜','油腻食物','与人争执'],
|
||||
'土':['生冷冰饮','甜食过量','久坐不动','思虑过度','暴饮暴食'],
|
||||
'金':['辛辣烧烤','烟酒','干燥环境','过度出汗','忧伤抑郁'],
|
||||
'水':['寒凉饮食','过度疲劳','熬夜','冷水澡','冬季晨练过早']
|
||||
};
|
||||
return{scores:scores,meridian:meridian,currM:currM,yi:yiDB[wk.se]||yiDB['火'],ji:jiDB[wk.se]||jiDB['火'],weekly:wk};
|
||||
}
|
||||
|
||||
// ===================== RENDER =====================
|
||||
function renderProfile(p,con,emo,bz){
|
||||
// Bazi table
|
||||
var labels=['年柱','月柱','日柱','时柱'];
|
||||
var pillars=[bz.year,bz.month,bz.day,bz.hour];
|
||||
var h='<table class="bzt"><tr><th></th><th>天干</th><th>地支</th><th>五行</th><th>纳音</th><th>生肖</th></tr>';
|
||||
for(var i=0;i<4;i++){
|
||||
var pl=pillars[i];
|
||||
var ny=NAYIN_MAP[pl.tg+pl.dz]||'--';
|
||||
var ds=pl.dz, dzEl=DZE[ds];
|
||||
var ani=i===0?DZ_ANI[DZ.indexOf(ds)]:'';
|
||||
h+='<tr><td>'+labels[i]+'</td><td><strong>'+pl.tg+'</strong><br><span class="el">'+TGE[pl.tg]+'</span></td><td><strong>'+ds+'</strong><br><span class="el">'+dzEl+'</span></td><td style="color:'+EC[dzEl]+';font-weight:600;font-size:14px">'+TGE[pl.tg]+'/'+dzEl+'</td><td style="font-size:11px;color:#888">'+ny+'</td><td style="font-size:11px;color:#999">'+ani+'</td></tr>';
|
||||
}
|
||||
h+='</table>';
|
||||
document.getElementById('baziTable').innerHTML=h;
|
||||
|
||||
// Pentagram
|
||||
var pg=document.getElementById('pentagram');
|
||||
pg.innerHTML='';
|
||||
EO.forEach(function(e){
|
||||
var v=p[e],c=EC[e];
|
||||
pg.innerHTML+='<div class="pb"><div class="f"><div class="fi" style="height:'+v+'%;background:'+c+'"></div></div><div class="l">'+e+'</div><div class="v" style="color:'+c+'">'+v+'</div></div>';
|
||||
});
|
||||
var sorted=EO.slice().sort(function(a,b){return p[b]-p[a];});
|
||||
document.getElementById('pentagramNote').textContent=sorted[0]+'偏盛 > '+sorted[1]+'次盛 > '+sorted[2]+'中和 > '+sorted[3]+'偏弱 > '+sorted[4]+'最弱';
|
||||
|
||||
// Constitution detail
|
||||
var cd=document.getElementById('constDetail');
|
||||
cd.innerHTML='<h2>🫁 体质分析</h2><div class="stats2"><div class="st2"><div class="v" style="color:var(--pri)">'+con.primary+'</div><div class="l">主要体质</div></div><div class="st2"><div class="v" style="color:#E8985A">'+con.secondary+'</div><div class="l">次要体质</div></div></div><p style="font-size:13px;color:#555;line-height:1.9;margin-top:8px">'+con.desc+'</p><h4 style="margin-top:12px">🩺 易患疾病</h4><p style="font-size:12px;color:#888;line-height:1.7">'+con.diseases+'</p><h4>👁 外在表现</h4><p style="font-size:12px;color:#888;line-height:1.7">'+con.body+'</p><h4>⚠️ 季节弱点</h4><p style="font-size:12px;color:#888;line-height:1.7">'+con.seasonRisk+'</p><h4>🎯 养生核心</h4><p style="font-size:12px;color:#888;line-height:1.7">'+con.tips+'</p><h4>🍽 食疗原则</h4><p style="font-size:12px;color:#888;line-height:1.7">'+con.foodTherapy+'</p><h4>⏰ 日常节律</h4><p style="font-size:12px;color:#888;line-height:1.7">'+con.dailyRhythm+'</p>';
|
||||
|
||||
// Organ detail
|
||||
var od=document.getElementById('organDetail');
|
||||
od.innerHTML='<h2>🫀 脏腑画像</h2><div class="stats2"><div class="st2"><div class="v" style="color:'+EC[sorted[0]]+'">'+con.strong+'</div><div class="l">偏强脏腑</div><div class="d">功能亢进<br>易生热/郁/火</div></div><div class="st2"><div class="v" style="color:#aaa">'+con.weak+'</div><div class="l">偏弱脏腑</div><div class="d">功能不足<br>易受外邪侵袭</div></div></div>';
|
||||
|
||||
// Emotion detail
|
||||
var ed=document.getElementById('emotionDetail');
|
||||
ed.innerHTML='<h2>🧠 情绪画像</h2><div style="margin-bottom:10px"><span style="font-size:12px;color:#999">优势特质:</span><div class="tags">'+emo.strength.map(function(s){return '<span class="tag tg">'+s+'</span>';}).join('')+'</div></div><div style="margin-bottom:10px"><span style="font-size:12px;color:#999">风险倾向:</span><div class="tags">'+emo.risk.map(function(s){return '<span class="tag tw">'+s+'</span>';}).join('')+'</div></div><h4>情绪调节</h4><p style="font-size:12px;color:#888;line-height:1.7">'+emo.advice+'</p>';
|
||||
|
||||
// Personality summary
|
||||
document.getElementById('personalityCard').innerHTML='<h2>📖 综合画像</h2><p style="font-size:13px;color:#555;line-height:2">先天<b style="color:'+EC[sorted[0]]+'">'+sorted[0]+'</b>之气偏盛,属<b style="color:var(--pri)">'+con.primary+'</b>体质。'+con.strong+'经易亢,'+con.weak+'经偏弱。'+emo.strength[0]+'、'+emo.strength[1]+'是你的核心天赋;需注意'+emo.risk[0]+'和'+emo.risk[1]+'的倾向。</p>';
|
||||
|
||||
document.getElementById('profileResult').classList.remove('hidden');
|
||||
document.getElementById('formCard').style.opacity='0.55';
|
||||
}
|
||||
|
||||
function seasonalGuide(term, se){
|
||||
var guides={
|
||||
'立春':{tips:'春季养肝正当时。早睡早起,广步于庭。披发缓行,以使志生。多吃芽菜(豆芽、香椿),少吃酸多食辛甘。',avoid:'压抑怒气、过度进补、过早减衣。'},
|
||||
'雨水':{tips:'湿气渐增,健脾祛湿。多吃山药薏米。注意关节保暖。',avoid:'生冷瓜果、淋雨涉水。'},
|
||||
'惊蛰':{tips:'春雷惊百虫,阳气升发。早起活动舒展筋骨。多吃梨润肺防春燥。',avoid:'过度劳累、情绪压抑。'},
|
||||
'春分':{tips:'阴阳平分,养生贵在平衡。早睡早起,适度运动微汗。饮食寒热均衡。',avoid:'偏食偏嗜、大补大泻。'},
|
||||
'清明':{tips:'清气上升,养肝明目。踏青散步舒畅情志。菊花枸杞茶护眼。',avoid:'过度悲伤、暴饮暴食。'},
|
||||
'谷雨':{tips:'雨生百谷,湿度加大。健脾祛湿防春困。多喝薏米陈皮茶。',avoid:'潮湿环境久待、过食生冷。'},
|
||||
'立夏':{tips:'夏季养心。夜卧早起,无厌于日。午休15-30分钟养心阴。多吃红色食物。',avoid:'烈日暴晒、大汗淋漓、辛辣过量。'},
|
||||
'小满':{tips:'湿热渐盛,清心祛湿。冬瓜薏米汤清热利湿。保持皮肤干爽。',avoid:'油腻厚味、甜腻食物。'},
|
||||
'芒种':{tips:'梅雨季将至,防湿防霉。室内通风除湿。饮食清淡少油腻。艾灸足三里健脾。',avoid:'生冷冰饮、久居湿地。'},
|
||||
'夏至':{tips:'阳极阴生,养心护阳。午休不可少。饮食清淡多汤水。夏至一阴生,忌过度贪凉。',avoid:'冰镇饮品、空调直吹、熬夜。'},
|
||||
'小暑':{tips:'暑热渐盛,清暑益气。绿豆汤、荷叶粥消暑。避免正午户外活动。',avoid:'过度出汗、冷热交替刺激。'},
|
||||
'大暑':{tips:'湿热交蒸,健脾祛湿为首。藿香正气类防中暑。多喝温水少量多次。',avoid:'烈日下劳作、冰火两重天。'},
|
||||
'立秋':{tips:'秋燥当令,润肺生津。多吃白色食物。早卧早起与鸡俱兴。',avoid:'辛辣烧烤、过度出汗。'},
|
||||
'处暑':{tips:'暑气渐消,秋燥初显。银耳百合羹润燥。适度运动舒展筋骨。',avoid:'燥热食物、熬夜伤阴。'},
|
||||
'白露':{tips:'寒气初生,注意保暖。早晚添衣,尤其腹部和脚部。多吃温润食物。',avoid:'赤脚踩凉地、露脐装。'},
|
||||
'秋分':{tips:'阴阳平分如春分。滋阴润燥,收敛神气。早睡早起,心态平和。',avoid:'剧烈运动大汗、辛辣伤阴。'},
|
||||
'寒露':{tips:'寒气加重,护好颈腰脚踝。泡脚至微汗。多吃芝麻核桃润燥。',avoid:'冷水洗脚、穿露脚踝裤。'},
|
||||
'霜降':{tips:'秋末冬初,补脾养肺。山药红枣粥最佳。适度运动增强抵抗力。',avoid:'过度秋冻、生冷瓜果。'},
|
||||
'立冬':{tips:'冬季养肾。早卧晚起,必待日光。多晒太阳补阳。多吃黑色食物。',avoid:'清晨过早户外运动、冷水澡。'},
|
||||
'小雪':{tips:'寒气渐重,温补肾阳。羊肉汤、当归生姜羊肉汤温补。艾灸关元命门。',avoid:'寒凉饮食、过度疲劳。'},
|
||||
'大雪':{tips:'仲冬严寒,藏精养阳。减少户外活动。多喝热粥热汤。早睡晚起。',avoid:'大汗淋漓、受寒感冒。'},
|
||||
'冬至':{tips:'一阳来复,阴极阳生。静养为主,减少消耗。吃饺子/汤圆象征团圆。冬至进补事半功倍。',avoid:'剧烈运动、熬夜、房劳过度。'},
|
||||
'小寒':{tips:'三九严寒,温阳散寒。艾灸关元足三里。每晚热水泡脚。羊肉当归汤最宜。',avoid:'清晨低温外出、冷水刺激。'},
|
||||
'大寒':{tips:'寒极则暖,冬藏待春。继续温补藏精。保持心态平和,不急不躁。',avoid:'过度消耗、受寒受冻。'}
|
||||
};
|
||||
var g=guides[term.n]||{tips:'顺应四时,饮食有节,起居有常。',avoid:'过度劳累、暴饮暴食、情绪波动。'};
|
||||
return '<div class="card"><h2>🌿 '+term.n+'节气养生指南</h2><div class="sr"><div class="si sg">宜</div><div class="sb"><div class="i">'+g.tips+'</div></div></div><div class="sr"><div class="si sr2">忌</div><div class="sb"><div class="i">'+g.avoid+'</div></div></div></div>';
|
||||
}
|
||||
|
||||
function renderWeekly(wk){
|
||||
var wc=document.getElementById('weeklyContent');
|
||||
var tn=wk.term?wk.term.n:'--';
|
||||
var se=wk.se||'--';
|
||||
var seN={'木':'春季','火':'夏季','土':'长夏','金':'秋季','水':'冬季'}[se]||se;
|
||||
var now=new Date();
|
||||
var ts=now.getFullYear()+'-'+String(now.getMonth()+1).padStart(2,'0')+'-'+String(now.getDate()).padStart(2,'0')+' '+String(now.getHours()).padStart(2,'0')+':'+String(now.getMinutes()).padStart(2,'0');
|
||||
var h='';
|
||||
|
||||
// ---- Overview ----
|
||||
h+='<div class="card"><h2>📊 本周气机概览</h2>';
|
||||
h+='<div style="font-size:14px;margin-bottom:4px">📅 节气:<b>'+tn+'</b> | 🌤 时令:<b>'+seN+'</b>('+se+'旺)</div>';
|
||||
h+='<div style="font-size:12px;color:#aaa;margin-bottom:10px">你的先天:<b style="color:'+EC[wk.strongest]+'">'+wk.strongest+'</b>偏盛 | '+wk.strongest+'生'+wk.interaction.gen+' | '+wk.strongest+'克'+wk.interaction.ke+'</div>';
|
||||
h+='<div style="font-size:11px;color:#ccc;border-top:1px dashed #f0e8e0;padding-top:8px">⏱ 数据基于当前时空气机实时计算 | 刷新时间:'+ts+'</div>';
|
||||
|
||||
// ---- WYLQ Detail ----
|
||||
var wq=wk.wylq;
|
||||
h+='<table class="wlt"><tr><td>岁运(中运)</td><td><b>'+wq.zhongyun+'</b>'+(wq.taiguo?'(太过)':'(不及)')+'</td></tr>';
|
||||
h+='<tr><td>司天之气</td><td>'+wq.sitian+'('+DZE[wq.sitian]+')— 上半年主气</td></tr>';
|
||||
h+='<tr><td>在泉之气</td><td>'+wq.zaiquan+'('+DZE[wq.zaiquan]+')— 下半年主气</td></tr>';
|
||||
var cs=wk.step||Math.floor(new Date().getMonth()/2);
|
||||
h+='<tr><td>当前主气</td><td>'+wq.zhuqi[cs]+'</td></tr>';
|
||||
h+='<tr><td>当前客气</td><td>'+wq.keqiOrder[cs]+'</td></tr>';
|
||||
h+='</table>';
|
||||
|
||||
// Five elements wax/wane
|
||||
h+='<div style="font-size:12px;margin-top:10px"><b>五行旺衰:</b>';
|
||||
EO.forEach(function(e){h+='<span style="margin:0 4px;padding:2px 8px;border-radius:4px;background:'+(wk.waxWane[e]==='旺'?EC[e]+'20':'transparent')+';color:'+EC[e]+';font-weight:'+(wk.waxWane[e]==='旺'?'700':'400')+'">'+e+wk.waxWane[e]+'</span>';});
|
||||
h+='</div>';
|
||||
|
||||
// Organ scores
|
||||
h+='<div style="margin-top:12px"><b>本周脏腑指数:</b></div>';
|
||||
['肝','心','脾','肺','肾'].forEach(function(o){
|
||||
var sc=wk.organScores[o]||3;
|
||||
var stars='';
|
||||
for(var i=1;i<=5;i++)stars+=i<=sc?'★':'<span class="off">★</span>';
|
||||
h+='<div class="or"><span class="on">'+o+'</span><span class="os2">'+stars+'</span></div>';
|
||||
});
|
||||
h+='</div>';
|
||||
|
||||
// ---- Risks ----
|
||||
h+='<div class="card"><h2>⚠️ 健康风险</h2>';
|
||||
wk.risks.forEach(function(r,i){
|
||||
h+='<div class="rk r'+(i+1)+'"><h4>风险'+(i+1)+':'+r.t+'</h4><p class="x">'+r.d+'</p><ul>';
|
||||
r.sy.forEach(function(s){h+='<li>'+s+'</li>';});
|
||||
h+='</ul>';
|
||||
if(r.act) h+='<p style="font-size:12px;color:var(--pri);margin-top:6px;line-height:1.7"><b>✅ 应对:</b>'+r.act+'</p>';
|
||||
h+='</div>';
|
||||
});
|
||||
h+='</div>';
|
||||
|
||||
// ---- Daily refresh tip (changes daily) ----
|
||||
var dailyTips={
|
||||
'火':['夏日午后一杯淡竹叶茶,清心降火。','心静自然凉,午休15分钟胜过补药。','苦味入心,晚餐加一道凉拌苦瓜。'],
|
||||
'木':['肝喜条达,今天遇到烦心事出去走10分钟。','绿色入肝,午餐加一份西兰花。','怒伤肝,深呼吸三次再回应让你生气的事。'],
|
||||
'土':['脾主思,今天少想多做,动手整理房间也是调理。','黄色入脾,早餐一碗小米粥暖胃健脾。','饭后百步走,活到九十九——今天饭后就散个步。'],
|
||||
'金':['秋燥伤肺,今天多喝温水,室内保持湿润。','白色入肺,今天吃一个雪梨润肺。','忧伤肺,今天找一件让你开心的小事做。'],
|
||||
'水':['肾藏精,今天早点上床,23点前入睡是最好的补肾药。','黑色入肾,今天吃一把黑芝麻或核桃。','恐伤肾,今天少看负面新闻,多晒晒太阳。']
|
||||
};
|
||||
var tips=dailyTips[wk.se]||dailyTips['火'];
|
||||
var idx=new Date().getDate()%tips.length;
|
||||
h+='<div class="card"><h2>💡 今日养生小贴士</h2><p style="font-size:13px;color:#555;line-height:1.8;padding:8px 12px;background:#FFF8F0;border-radius:10px">'+tips[idx]+'</p></div>';
|
||||
|
||||
// ---- Diet ----
|
||||
h+='<div class="card"><h2>🥗 本周饮食</h2>';
|
||||
h+='<div class="sr"><div class="si sg">👍</div><div class="sb"><div class="t">推荐食材</div><div class="i">'+wk.diet.rec.join('、')+'</div><div class="r">💡 '+wk.diet.reason+'</div></div></div>';
|
||||
h+='<div class="sr"><div class="si sr2">👎</div><div class="sb"><div class="t">减少摄入</div><div class="i">'+wk.diet.avoid.join('、')+'</div></div></div>';
|
||||
if(wk.diet.tea) h+='<div class="sr"><div class="si so">🍵</div><div class="sb"><div class="t">养生茶饮</div><div class="i">'+wk.diet.tea+'</div></div></div>';
|
||||
if(wk.diet2){
|
||||
h+='<div class="sr"><div class="si so">🔄</div><div class="sb"><div class="t">辅助调养('+wk.interaction.beiKe+'行)</div><div class="i">'+wk.diet2.rec.join('、')+'</div><div class="r">💡 '+wk.diet2.reason+'</div></div></div>';
|
||||
}
|
||||
h+='</div>';
|
||||
|
||||
// ---- Exercise ----
|
||||
h+='<div class="card"><h2>🏃 运动建议</h2>';
|
||||
h+='<div class="sr"><div class="si sbl">✅</div><div class="sb"><div class="t">推荐运动</div><div class="i">'+wk.exercise.rec.join('、')+'</div></div></div>';
|
||||
h+='<div class="sr"><div class="si so">⛔</div><div class="sb"><div class="t">避免</div><div class="i">'+wk.exercise.avoid.join('、')+'</div><div class="r">💡 '+wk.exercise.reason+'</div></div></div>';
|
||||
h+='</div>';
|
||||
|
||||
// ---- Sleep ----
|
||||
h+='<div class="card"><h2>😴 作息建议</h2>';
|
||||
h+='<div class="sr"><div class="si sbl">🌙</div><div class="sb"><div class="t">'+wk.sleep.bt+' 入睡 · '+wk.sleep.wt+' 起床</div><div class="i">'+wk.sleep.focus+'</div></div></div>';
|
||||
h+='</div>';
|
||||
|
||||
// ---- Seasonal Living Guide ----
|
||||
h+=seasonalGuide(wk.term, wk.se);
|
||||
|
||||
// ---- Acupoints ----
|
||||
h+='<div class="card"><h2>📍 穴位保健</h2>';
|
||||
h+='<div class="tags">'+wk.acupoints.map(function(a){return '<span class="tag tb">'+a+'</span>';}).join('')+'</div>';
|
||||
h+='<p style="font-size:11px;color:#999;margin-top:6px">每日按揉每穴3-5分钟,酸胀为度,早晚各一次。</p>';
|
||||
h+='</div>';
|
||||
|
||||
// ---- Quote ----
|
||||
h+='<div class="card"><h2>📖 本周一句内经</h2><div class="qt"><div class="t">「'+wk.quote.t+'」</div><div class="e">'+wk.quote.e+'</div></div></div>';
|
||||
|
||||
wc.innerHTML=h;
|
||||
}
|
||||
|
||||
function renderDaily(dy){
|
||||
var dc=document.getElementById('dailyContent');
|
||||
var wk=dy.weekly;
|
||||
var h='';
|
||||
|
||||
h+='<div class="card"><h2>☀️ 今日养生</h2>';
|
||||
h+='<div style="font-size:14px;color:#555">📅 '+wk.term.n+' | 值日五行:<b style="color:'+EC[wk.se]+'">'+wk.se+'</b></div>';
|
||||
h+='<div style="font-size:12px;color:#aaa;margin-top:4px">你的状态:<b style="color:'+EC[wk.strongest]+'">'+wk.strongest+'</b>偏旺</div></div>';
|
||||
|
||||
// Meridian clock
|
||||
h+='<div class="card"><h2>⏰ 子午流注</h2>';
|
||||
h+='<p style="font-size:12px;color:#999;margin-bottom:8px">当前时段:<b>'+dy.currM.n+'</b>('+dy.currM.o+')— '+dy.currM.act+'</p>';
|
||||
h+='<p style="font-size:11px;color:#bbb;margin-bottom:8px">💡 '+dy.currM.tip+'</p>';
|
||||
h+='<div class="mer">';
|
||||
dy.meridian.forEach(function(m){
|
||||
h+='<div class="mi'+(m===dy.currM?'" style="background:#FFECEC;border:1px solid var(--pri)"':'')+'"><div class="t2">'+m.n+'</div><div class="o2">'+m.o+'</div></div>';
|
||||
});
|
||||
h+='</div></div>';
|
||||
|
||||
// Scores
|
||||
h+='<div class="card"><h2>📈 今日评分</h2>';
|
||||
dy.scores.forEach(function(s){
|
||||
h+='<div class="sc"><span class="sl">'+s.l+'</span><div class="sb2"><div class="sf" style="width:'+s.v+'%;background:'+s.c+'"></div></div><span class="sv">'+s.v+'</span></div>';
|
||||
});
|
||||
h+='</div>';
|
||||
|
||||
// Yiji
|
||||
h+='<div class="card"><h2>📋 今日宜忌</h2>';
|
||||
h+='<div class="sr"><div class="si sg">宜</div><div class="sb"><div class="i">'+dy.yi.join('、')+'</div></div></div>';
|
||||
h+='<div class="sr"><div class="si sr2">忌</div><div class="sb"><div class="i">'+dy.ji.join('、')+'</div></div></div>';
|
||||
h+='</div>';
|
||||
|
||||
// Key focus
|
||||
h+='<div class="card"><h2>🎯 今日重点</h2>';
|
||||
h+='<div style="padding:16px;background:#FFECEC;border-radius:10px;text-align:center">';
|
||||
h+='<div style="font-size:18px;font-weight:700;color:var(--pri);margin-bottom:6px">保养'+({'木':'肝','火':'心','土':'脾','金':'肺','水':'肾'}[wk.strongest]||'--')+'经</div>';
|
||||
h+='<div style="font-size:13px;color:#888">'+dy.yi.slice(0,3).join(' · ')+'</div>';
|
||||
h+='</div></div>';
|
||||
|
||||
dc.innerHTML=h;
|
||||
}
|
||||
|
||||
// ===================== MAIN =====================
|
||||
var userProfile=null,userConstitution=null,userEmotion=null,userBazi=null,curAnalysis=null;
|
||||
|
||||
(function(){
|
||||
try{var d=JSON.parse(localStorage.getItem('yangsheng_profile'));
|
||||
if(d){userProfile=d.profile;userConstitution=d.constitution;userEmotion=d.emotion;userBazi=d.bazi;
|
||||
renderProfile(userProfile,userConstitution,userEmotion,userBazi);}
|
||||
}catch(e){}
|
||||
})();
|
||||
|
||||
function gen(){
|
||||
try{console.log('gen start');
|
||||
var nm=document.getElementById('fname').value||'用户';
|
||||
var bs=document.getElementById('fbirth').value;
|
||||
var ts=document.getElementById('ftime').value||'12:00';
|
||||
var pl=document.getElementById('fplace').value||'--';
|
||||
if(!bs){alert('请填写出生日期');return;}
|
||||
var bd=new Date(bs),bh=parseInt(ts.split(':')[0]);
|
||||
userBazi=getBazi(bd,bh); console.log('bazi ok');
|
||||
userProfile=calc5E(userBazi); console.log('profile ok');
|
||||
userConstitution=getConst(userProfile); console.log('const ok');
|
||||
userEmotion=getEmotion(EO.slice().sort(function(a,b){return userProfile[b]-userProfile[a];})[0]); console.log('emotion ok');
|
||||
try{localStorage.setItem('yangsheng_profile',JSON.stringify({name:nm,profile:userProfile,constitution:userConstitution,emotion:userEmotion,bazi:userBazi,birthDate:bs,birthTime:ts,birthPlace:pl}));}catch(e){}
|
||||
renderProfile(userProfile,userConstitution,userEmotion,userBazi); console.log('renderProfile ok');
|
||||
// Switch to weekly
|
||||
document.querySelectorAll('.tab').forEach(function(t){t.classList.remove('active');});
|
||||
document.querySelector('[data-tab="weekly"]').classList.add('active');
|
||||
document.getElementById('tab-profile').classList.add('hidden');
|
||||
document.getElementById('tab-weekly').classList.remove('hidden');
|
||||
document.getElementById('tab-daily').classList.add('hidden');
|
||||
curAnalysis=getCurAnalysis(); console.log('curAnalysis ok');
|
||||
var report = wkReport(userProfile,userConstitution,userEmotion,curAnalysis); console.log('wkReport ok');
|
||||
renderWeekly(report); console.log('renderWeekly ok');
|
||||
}catch(e){console.error('gen error:',e.message,e.stack);}
|
||||
}
|
||||
|
||||
// Tabs
|
||||
document.getElementById('tabs').addEventListener('click',function(e){
|
||||
if(!e.target.classList.contains('tab'))return;
|
||||
var tab=e.target.dataset.tab;
|
||||
document.querySelectorAll('.tab').forEach(function(t){t.classList.remove('active');});
|
||||
e.target.classList.add('active');
|
||||
document.getElementById('tab-profile').classList.add('hidden');
|
||||
document.getElementById('tab-weekly').classList.add('hidden');
|
||||
document.getElementById('tab-daily').classList.add('hidden');
|
||||
if(tab==='profile') document.getElementById('tab-profile').classList.remove('hidden');
|
||||
else if(tab==='weekly'){
|
||||
if(!userProfile){alert('请先生成先天体质画像');document.querySelector('[data-tab="profile"]').click();return;}
|
||||
document.getElementById('tab-weekly').classList.remove('hidden');
|
||||
curAnalysis=getCurAnalysis();
|
||||
renderWeekly(wkReport(userProfile,userConstitution,userEmotion,curAnalysis));
|
||||
}else if(tab==='daily'){
|
||||
if(!userProfile){alert('请先生成先天体质画像');document.querySelector('[data-tab="profile"]').click();return;}
|
||||
document.getElementById('tab-daily').classList.remove('hidden');
|
||||
curAnalysis=getCurAnalysis();
|
||||
renderDaily(dyReport(userProfile,userConstitution,userEmotion,curAnalysis));
|
||||
}
|
||||
});
|
||||
|
||||
// URL 参数预填(从 jiema.html 跳转过来)
|
||||
window.addEventListener('DOMContentLoaded',function(){
|
||||
const p=new URLSearchParams(location.search);
|
||||
const y=p.get('y'),m=p.get('m'),d=p.get('d'),h=p.get('h')||'12';
|
||||
if(y&&m&&d){
|
||||
var fb=document.getElementById('fbirth');
|
||||
var ft=document.getElementById('ftime');
|
||||
if(fb) fb.value=y+'-'+String(m).padStart(2,'0')+'-'+String(d).padStart(2,'0');
|
||||
if(ft) ft.value=String(h).padStart(2,'0')+':00';
|
||||
setTimeout(gen,300);
|
||||
}
|
||||
});
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
function goDecode(){
|
||||
var y=document.getElementById('dy').value.trim();
|
||||
var m=document.getElementById('dm').value.trim();
|
||||
var d=document.getElementById('dd').value.trim();
|
||||
if(!y||!m||!d){alert('请输入完整的出生年月日');return;}
|
||||
location.href='pages/decode.html?y='+encodeURIComponent(y)+'&m='+encodeURIComponent(m)+'&d='+encodeURIComponent(d);
|
||||
}
|
||||
['dy','dm','dd'].forEach(function(id){
|
||||
var el=document.getElementById(id);
|
||||
if(!el) return;
|
||||
el.addEventListener('keydown',function(e){
|
||||
if(e.key==='Enter')goDecode();
|
||||
});
|
||||
});
|
||||
+424
@@ -0,0 +1,424 @@
|
||||
var c=document.getElementById('c'),ctx=c.getContext('2d');
|
||||
var ei=document.getElementById('ei'),ed=document.getElementById('editor');
|
||||
var SCALE=1,ox=0,oy=0;
|
||||
var dragging=false,dragX=0,dragY=0;
|
||||
var selected=null,editing=null;
|
||||
var colors=['#8B5CF6','#4A90E2','#5CB85C','#E8985A','#E54D42','#06B6D4','#F59E0B','#EC4899'];
|
||||
var STORAGE_KEY='yuxingu_maps';
|
||||
|
||||
// ---- Multi-map state ----
|
||||
var maps=[],currentIdx=-1;
|
||||
|
||||
function defaultTree(){
|
||||
return {
|
||||
id:'root',text:'新建脑图',color:colors[0],children:[]
|
||||
};
|
||||
}
|
||||
|
||||
function defaultTemplate(){
|
||||
return {
|
||||
id:'root',text:'愈心谷小程序',color:colors[0],
|
||||
children:[
|
||||
{id:'b1',text:'愈心魔方',color:colors[0],children:[
|
||||
{id:'b1a',text:'急救魔方'},{id:'b1b',text:'亲子魔方'},{id:'b1c',text:'成长魔方'},
|
||||
{id:'b1d',text:'解构魔方'},{id:'b1e',text:'艺术魔方'},{id:'b1f',text:'情感魔方'}
|
||||
]},
|
||||
{id:'b2',text:'私域活动',color:colors[1],children:[
|
||||
{id:'b2a',text:'心理研学'},{id:'b2b',text:'心灵旅修'}
|
||||
]},
|
||||
{id:'b3',text:'心理咨询',color:colors[2]},
|
||||
{id:'b4',text:'心理评估',color:colors[3],children:[
|
||||
{id:'b4a',text:'AI面部识别'},{id:'b4b',text:'脑机接口'}
|
||||
]},
|
||||
{id:'b5',text:'健康管理',color:colors[4]}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
// ---- Persistence ----
|
||||
function loadMaps(){
|
||||
try{
|
||||
var d=localStorage.getItem(STORAGE_KEY);
|
||||
return d?JSON.parse(d):[];
|
||||
}catch(e){return [];}
|
||||
}
|
||||
|
||||
function saveMaps(){
|
||||
try{localStorage.setItem(STORAGE_KEY,JSON.stringify(maps))}catch(e){}
|
||||
// sync to server (all maps)
|
||||
fetch('/save',{method:'POST',body:JSON.stringify(maps)}).catch(function(){});
|
||||
}
|
||||
|
||||
function syncServer(){
|
||||
fetch('/load').then(function(r){return r.json()}).then(function(data){
|
||||
if(Array.isArray(data)&&data.length>0){
|
||||
maps=data;saveMaps();renderSidebar();
|
||||
if(currentIdx<0||currentIdx>=maps.length)switchMap(0);
|
||||
}
|
||||
}).catch(function(){});
|
||||
}
|
||||
|
||||
// ---- Sidebar ----
|
||||
function renderSidebar(){
|
||||
var sl=document.getElementById('slist');
|
||||
sl.innerHTML='';
|
||||
maps.forEach(function(m,i){
|
||||
var d=document.createElement('div');
|
||||
d.className='item'+(i===currentIdx?' active':'');
|
||||
d.innerHTML='<span>'+escHtml(m.name||'脑图'+(i+1))+'</span><span class="del" data-idx="'+i+'" onclick="event.stopPropagation();deleteMap('+i+')">✕</span>';
|
||||
d.onclick=function(){switchMap(i)};
|
||||
sl.appendChild(d);
|
||||
});
|
||||
}
|
||||
|
||||
function escHtml(s){return s.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');}
|
||||
|
||||
function switchMap(idx){
|
||||
if(idx<0||idx>=maps.length)return;
|
||||
currentIdx=idx;
|
||||
tree=JSON.parse(JSON.stringify(maps[idx].tree));
|
||||
selected=null;
|
||||
renderSidebar();
|
||||
layout();draw();
|
||||
// close sidebar on mobile after selection
|
||||
if(window.innerWidth<=600)closeSidebar();
|
||||
}
|
||||
|
||||
function newMap(){
|
||||
var name=prompt('脑图名称:','脑图'+(maps.length+1));
|
||||
if(!name)return;
|
||||
maps.push({name:name,tree:defaultTree()});
|
||||
saveMaps();
|
||||
renderSidebar();
|
||||
switchMap(maps.length-1);
|
||||
}
|
||||
|
||||
function renameMap(){
|
||||
if(currentIdx<0)return;
|
||||
var name=prompt('新名称:',maps[currentIdx].name);
|
||||
if(!name)return;
|
||||
maps[currentIdx].name=name;
|
||||
saveMaps();renderSidebar();
|
||||
}
|
||||
|
||||
function deleteMap(idx){
|
||||
if(!confirm('删除「'+maps[idx].name+'」?不可恢复。'))return;
|
||||
maps.splice(idx,1);
|
||||
saveMaps();renderSidebar();
|
||||
if(maps.length===0){maps.push({name:'愈心谷小程序',tree:defaultTemplate()});saveMaps();renderSidebar();}
|
||||
if(currentIdx>=maps.length)currentIdx=maps.length-1;
|
||||
switchMap(currentIdx);
|
||||
}
|
||||
|
||||
var _saveTimer=null;
|
||||
function autoSave(){
|
||||
if(currentIdx<0)return;
|
||||
maps[currentIdx].tree=JSON.parse(JSON.stringify(exportTree(tree)));
|
||||
saveMaps();
|
||||
var h=document.getElementById('saveHint');
|
||||
if(h){h.textContent='💾 已自动保存';h.style.color='#aaa';
|
||||
clearTimeout(_saveTimer);
|
||||
_saveTimer=setTimeout(function(){h.textContent='';},2000);}
|
||||
}
|
||||
|
||||
// ---- Tree export ----
|
||||
function exportTree(node){
|
||||
var o={id:node.id,text:node.text};
|
||||
if(node.color)o.color=node.color;
|
||||
if(node.children)o.children=node.children.map(exportTree);
|
||||
return o;
|
||||
}
|
||||
|
||||
// ---- Layout & Draw ----
|
||||
var tree=defaultTemplate();
|
||||
var NODE_W=120,NODE_H=34,HGAP=60,VGAP=14;
|
||||
|
||||
function uid(){return 'n'+Date.now()+Math.random().toString(36).slice(2,6)}
|
||||
|
||||
function getColor(node,parentColor){
|
||||
if(node.color)return node.color;
|
||||
if(parentColor)return parentColor;
|
||||
return colors[0];
|
||||
}
|
||||
|
||||
function layoutHeight(node){
|
||||
if(!node.children||node.children.length===0)return NODE_H;
|
||||
var h=0;
|
||||
for(var i=0;i<node.children.length;i++)h+=layoutHeight(node.children[i]);
|
||||
return h+(node.children.length-1)*VGAP;
|
||||
}
|
||||
|
||||
function layoutNode(node,x,y,parentColor){
|
||||
node._x=x;node._y=y;node._w=NODE_W;node._h=NODE_H;
|
||||
node._color=getColor(node,parentColor);
|
||||
if(node.children&&node.children.length>0){
|
||||
var totalH=0;
|
||||
for(var i=0;i<node.children.length;i++)totalH+=layoutHeight(node.children[i]);
|
||||
totalH+=(node.children.length-1)*VGAP;
|
||||
var cy=y-(totalH-NODE_H)/2;
|
||||
for(var i=0;i<node.children.length;i++){
|
||||
var cnode=node.children[i];
|
||||
var ch=layoutHeight(cnode);
|
||||
layoutNode(cnode,x+NODE_W+HGAP,cy+(ch-NODE_H)/2,node._color);
|
||||
cy+=ch+VGAP;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function layout(){
|
||||
var totalH=layoutHeight(tree);
|
||||
layoutNode(tree,40,-totalH/2,tree.color);
|
||||
}
|
||||
|
||||
function hitTest(mx,my){
|
||||
function hit(node){
|
||||
if(!node._x)return null;
|
||||
if(mx>=node._x&&mx<=node._x+node._w&&my>=node._y&&my<=node._y+node._h)return node;
|
||||
if(node.children)for(var i=0;i<node.children.length;i++){var h=hit(node.children[i]);if(h)return h;}
|
||||
return null;
|
||||
}
|
||||
return hit(tree);
|
||||
}
|
||||
|
||||
function draw(){
|
||||
var W=c.width=c.offsetWidth||window.innerWidth-200;
|
||||
var H=c.height=c.offsetHeight||window.innerHeight-42;
|
||||
ctx.clearRect(0,0,W,H);
|
||||
ctx.save();
|
||||
ctx.translate(W/2+ox,H/2+oy);
|
||||
ctx.scale(SCALE,SCALE);
|
||||
drawTree(tree);
|
||||
ctx.restore();
|
||||
updatePlusButtons();
|
||||
}
|
||||
|
||||
function drawTree(node){
|
||||
var x=node._x,y=node._y,w=node._w,h=node._h,col=node._color;
|
||||
var isRoot=node===tree,isSel=node===selected;
|
||||
if(node.children)for(var i=0;i<node.children.length;i++){
|
||||
var c=node.children[i];
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x+w,y+h/2);
|
||||
ctx.bezierCurveTo(
|
||||
x+w+HGAP/2,y+h/2,
|
||||
c._x-HGAP/2,c._y+c._h/2,
|
||||
c._x,c._y+c._h/2
|
||||
);
|
||||
ctx.strokeStyle=col;ctx.lineWidth=isRoot?3:2;ctx.stroke();
|
||||
drawTree(c);
|
||||
}
|
||||
var rx=6;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x+rx,y);ctx.lineTo(x+w-rx,y);ctx.arcTo(x+w,y,x+w,y+rx,rx);
|
||||
ctx.lineTo(x+w,y+h-rx);ctx.arcTo(x+w,y+h,x+w-rx,y+h,rx);
|
||||
ctx.lineTo(x+rx,y+h);ctx.arcTo(x,y+h,x,y+h-rx,rx);
|
||||
ctx.lineTo(x,y+rx);ctx.arcTo(x,y,x+rx,y,rx);ctx.closePath();
|
||||
if(isRoot){
|
||||
ctx.fillStyle=col;ctx.fill();
|
||||
ctx.strokeStyle='rgba(0,0,0,0.15)';ctx.lineWidth=1;ctx.stroke();
|
||||
ctx.fillStyle='#fff';
|
||||
}else{
|
||||
ctx.fillStyle='#fff';ctx.fill();
|
||||
ctx.strokeStyle=col;ctx.lineWidth=isSel?3:1.5;ctx.stroke();
|
||||
ctx.fillStyle='#333';
|
||||
}
|
||||
ctx.font=(isRoot?'bold ':'')+'13px -apple-system,BlinkMacSystemFont,"PingFang SC","Microsoft YaHei",sans-serif';
|
||||
ctx.textAlign='center';ctx.textBaseline='middle';
|
||||
ctx.fillText(node.text,x+w/2,y+h/2);
|
||||
}
|
||||
|
||||
function updatePlusButtons(){
|
||||
var pc=document.getElementById('plusChild'), ps=document.getElementById('plusSibling');
|
||||
if(!selected){
|
||||
pc.style.display='none'; ps.style.display='none'; return;
|
||||
}
|
||||
var rect=c.getBoundingClientRect();
|
||||
var n=selected, col=n._color||colors[0];
|
||||
// Child + (right edge)
|
||||
var cx=rect.left + c.width/2 + ox + (n._x+n._w+8)*SCALE;
|
||||
var cy=rect.top + c.height/2 + oy + (n._y+n._h/2)*SCALE;
|
||||
pc.style.display='block'; pc.style.left=(cx-10)+'px'; pc.style.top=(cy-10)+'px';
|
||||
pc.style.background=col;
|
||||
// Sibling + (bottom edge) — not for root
|
||||
if(selected!==tree){
|
||||
var sx=rect.left + c.width/2 + ox + (n._x+n._w/2)*SCALE;
|
||||
var sy=rect.top + c.height/2 + oy + (n._y+n._h+8)*SCALE;
|
||||
ps.style.display='block'; ps.style.left=(sx-10)+'px'; ps.style.top=(sy-10)+'px';
|
||||
ps.style.background=col;
|
||||
}else{ps.style.display='none';}
|
||||
}
|
||||
|
||||
// ---- Canvas events ----
|
||||
c.addEventListener('mousedown',function(e){
|
||||
var rect=c.getBoundingClientRect();
|
||||
var sx=(e.clientX-rect.left-c.width/2-ox)/SCALE;
|
||||
var sy=(e.clientY-rect.top-c.height/2-oy)/SCALE;
|
||||
var node=hitTest(sx,sy);
|
||||
if(node){selected=node;draw();}
|
||||
else{dragging=true;dragX=e.clientX;dragY=e.clientY;}
|
||||
});
|
||||
c.addEventListener('mousemove',function(e){
|
||||
if(dragging){ox+=e.clientX-dragX;oy+=e.clientY-dragY;dragX=e.clientX;dragY=e.clientY;draw();}
|
||||
});
|
||||
c.addEventListener('mouseup',function(){dragging=false;});
|
||||
c.addEventListener('mouseleave',function(){dragging=false;});
|
||||
c.addEventListener('wheel',function(e){
|
||||
e.preventDefault();
|
||||
SCALE=Math.max(0.3,Math.min(3,SCALE*(e.deltaY>0?0.9:1.1)));
|
||||
draw();
|
||||
},{passive:false});
|
||||
c.addEventListener('dblclick',function(e){
|
||||
var rect=c.getBoundingClientRect();
|
||||
var sx=(e.clientX-rect.left-c.width/2-ox)/SCALE;
|
||||
var sy=(e.clientY-rect.top-c.height/2-oy)/SCALE;
|
||||
var node=hitTest(sx,sy);
|
||||
if(node){
|
||||
editing=node;
|
||||
var cx=rect.left+c.width/2+ox+node._x*SCALE;
|
||||
var cy=rect.top+c.height/2+oy+node._y*SCALE;
|
||||
ed.style.display='block';ed.style.left=cx+'px';ed.style.top=cy+'px';
|
||||
ei.value=node.text;ei.focus();ei.select();
|
||||
}
|
||||
});
|
||||
|
||||
// Touch
|
||||
c.addEventListener('touchstart',function(e){
|
||||
if(e.touches.length===1){
|
||||
var rect=c.getBoundingClientRect();
|
||||
var sx=(e.touches[0].clientX-rect.left-c.width/2-ox)/SCALE;
|
||||
var sy=(e.touches[0].clientY-rect.top-c.height/2-oy)/SCALE;
|
||||
var node=hitTest(sx,sy);
|
||||
if(node){selected=node;draw();}
|
||||
else{dragging=true;dragX=e.touches[0].clientX;dragY=e.touches[0].clientY;}
|
||||
}
|
||||
},{passive:false});
|
||||
c.addEventListener('touchmove',function(e){
|
||||
if(dragging&&e.touches.length===1){
|
||||
e.preventDefault();
|
||||
ox+=e.touches[0].clientX-dragX;oy+=e.touches[0].clientY-dragY;
|
||||
dragX=e.touches[0].clientX;dragY=e.touches[0].clientY;draw();
|
||||
}
|
||||
},{passive:false});
|
||||
c.addEventListener('touchend',function(){dragging=false;});
|
||||
|
||||
// ---- Mobile: sidebar toggle ----
|
||||
function toggleSidebar(){
|
||||
var sb=document.querySelector('.sidebar');
|
||||
var ov=document.getElementById('overlay');
|
||||
sb.classList.toggle('open');
|
||||
ov.classList.toggle('show');
|
||||
}
|
||||
function closeSidebar(){
|
||||
document.querySelector('.sidebar').classList.remove('open');
|
||||
document.getElementById('overlay').classList.remove('show');
|
||||
}
|
||||
|
||||
// ---- Mobile: double-tap to edit ----
|
||||
var _lastTap=0;
|
||||
c.addEventListener('touchend',function(e){
|
||||
var now=Date.now();
|
||||
if(now-_lastTap<300){
|
||||
// double tap
|
||||
e.preventDefault();
|
||||
var rect=c.getBoundingClientRect();
|
||||
var sx=(e.changedTouches[0].clientX-rect.left-c.width/2-ox)/SCALE;
|
||||
var sy=(e.changedTouches[0].clientY-rect.top-c.height/2-oy)/SCALE;
|
||||
var node=hitTest(sx,sy);
|
||||
if(node){
|
||||
editing=node;
|
||||
var cx=rect.left+c.width/2+ox+node._x*SCALE;
|
||||
var cy=rect.top+c.height/2+oy+node._y*SCALE;
|
||||
ed.style.display='block';ed.style.left=cx+'px';ed.style.top=cy+'px';
|
||||
ei.value=node.text;ei.focus();ei.select();
|
||||
}
|
||||
}
|
||||
_lastTap=now;
|
||||
// close sidebar when tapping canvas
|
||||
if(window.innerWidth<=600)closeSidebar();
|
||||
});
|
||||
|
||||
// ---- Mobile: pinch-to-zoom ----
|
||||
var _pinchDist=0;
|
||||
c.addEventListener('touchstart',function(e){
|
||||
if(e.touches.length===2){
|
||||
_pinchDist=Math.hypot(
|
||||
e.touches[0].clientX-e.touches[1].clientX,
|
||||
e.touches[0].clientY-e.touches[1].clientY
|
||||
);
|
||||
}
|
||||
},{passive:false});
|
||||
c.addEventListener('touchmove',function(e){
|
||||
if(e.touches.length===2){
|
||||
e.preventDefault();
|
||||
var d=Math.hypot(
|
||||
e.touches[0].clientX-e.touches[1].clientX,
|
||||
e.touches[0].clientY-e.touches[1].clientY
|
||||
);
|
||||
var ratio=d/(_pinchDist||1);
|
||||
SCALE=Math.max(0.3,Math.min(3,SCALE*ratio));
|
||||
_pinchDist=d;
|
||||
draw();
|
||||
}
|
||||
},{passive:false});
|
||||
|
||||
// ---- Keyboard shortcuts ----
|
||||
document.addEventListener('keydown',function(e){
|
||||
if(e.target.tagName==='INPUT'||e.target.tagName==='TEXTAREA')return; // 编辑中不触发
|
||||
if(e.key==='Delete'||e.key==='Backspace'){
|
||||
e.preventDefault();
|
||||
delNode();
|
||||
}
|
||||
});
|
||||
|
||||
// ---- Actions ----
|
||||
function commitEdit(){if(editing)editing.text=ei.value||'新节点';ed.style.display='none';editing=null;draw();autoSave();}
|
||||
function cancelEdit(){ed.style.display='none';editing=null;}
|
||||
|
||||
function addChild(){
|
||||
if(!selected){alert('请先选中一个节点');return;}
|
||||
if(!selected.children)selected.children=[];
|
||||
selected.children.push({id:uid(),text:'新节点'});
|
||||
layout();draw();selected=selected.children[selected.children.length-1];draw();autoSave();
|
||||
}
|
||||
|
||||
function addSibling(){
|
||||
if(!selected||selected===tree){alert('请选中一个非根节点');return;}
|
||||
function fp(node,target){
|
||||
if(node.children)for(var i=0;i<node.children.length;i++){
|
||||
if(node.children[i].id===target.id)return node;
|
||||
var f=fp(node.children[i],target);if(f)return f;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
var parent=fp(tree,selected);
|
||||
if(!parent)return;
|
||||
var idx=parent.children.indexOf(selected);
|
||||
parent.children.splice(idx+1,0,{id:uid(),text:'新节点'});
|
||||
layout();draw();selected=parent.children[idx+1];draw();autoSave();
|
||||
}
|
||||
|
||||
function delNode(){
|
||||
if(!selected||selected===tree){alert('不能删除根节点');return;}
|
||||
function rm(node,target){
|
||||
if(node.children)for(var i=0;i<node.children.length;i++){
|
||||
if(node.children[i].id===target.id){node.children.splice(i,1);return true;}
|
||||
if(rm(node.children[i],target))return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
rm(tree,selected);selected=null;layout();draw();autoSave();
|
||||
}
|
||||
|
||||
function zoomIn(){SCALE=Math.min(3,SCALE*1.15);draw();}
|
||||
function zoomOut(){SCALE=Math.max(0.3,SCALE*0.85);draw();}
|
||||
function goHome(){window.location.href='index.html';}
|
||||
|
||||
// ---- Init ----
|
||||
(function init(){
|
||||
maps=loadMaps();
|
||||
if(maps.length===0){maps.push({name:'愈心谷小程序',tree:defaultTemplate()});saveMaps();}
|
||||
renderSidebar();
|
||||
switchMap(0);
|
||||
syncServer();
|
||||
window.addEventListener('resize',function(){draw();});
|
||||
})();
|
||||
@@ -0,0 +1,242 @@
|
||||
const params = new URLSearchParams(location.search);
|
||||
const slug = params.get('slug');
|
||||
let scale = null, answers = [], current = 0, submitted = false, started = false;
|
||||
|
||||
async function init() {
|
||||
if (!slug) { document.getElementById('app').innerHTML = '<div class="loading">缺少量表参数</div>'; return; }
|
||||
try {
|
||||
const r = await fetch(`../api/scales/${slug}`);
|
||||
scale = await r.json();
|
||||
if (scale.error) throw new Error(scale.error);
|
||||
answers = new Array(scale.question_count).fill(null);
|
||||
// Restore from localStorage
|
||||
const cached = localStorage.getItem(`scale_${slug}`);
|
||||
if (cached) answers = JSON.parse(cached);
|
||||
renderStart();
|
||||
} catch(e) {
|
||||
document.getElementById('app').innerHTML = `<div class="loading">加载失败: ${e.message}</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
function renderStart() {
|
||||
const hasCache = answers.some(a => a !== null);
|
||||
const answered = answers.filter(a => a !== null).length;
|
||||
const rawDesc = scale.description || '';
|
||||
const parts = rawDesc.split('\n\n');
|
||||
const mainDesc = parts[0] || '';
|
||||
const metaLine = parts.slice(1).join(' · ') || '';
|
||||
const timeMin = Math.ceil(scale.question_count / 6);
|
||||
const timeMax = Math.ceil(scale.question_count / 4);
|
||||
|
||||
document.getElementById('app').innerHTML = `
|
||||
<div style="max-width:500px;margin:0 auto;position:relative;text-align:center;padding:32px 20px 8px">
|
||||
<a href="scales.html" style="position:absolute;left:20px;top:34px;color:#999;font-size:20px;text-decoration:none;line-height:1">←</a>
|
||||
<div style="font-size:20px;font-weight:700;color:#333">${scale.name}</div>
|
||||
</div>
|
||||
<div class="main" style="justify-content:center;padding:8px 20px 16px">
|
||||
<div class="qcard" style="text-align:left;flex:none;padding:24px 20px">
|
||||
${mainDesc ? `<div style="font-size:15px;color:#444;line-height:1.85;margin-bottom:16px">${mainDesc}</div>` : ''}
|
||||
${metaLine ? `<div style="display:flex;flex-wrap:wrap;gap:6px;margin-bottom:16px">
|
||||
${metaLine.split('·').filter(m=>m.trim()).map(m =>
|
||||
`<span style="display:inline-block;padding:4px 10px;background:#f5f5f5;border-radius:6px;font-size:12px;color:#888">${m.trim()}</span>`
|
||||
).join('')}
|
||||
</div>` : ''}
|
||||
<div style="display:flex;align-items:center;gap:12px;padding:12px 14px;background:#f9f9f9;border-radius:8px;margin-bottom:16px;font-size:13px;color:#888">
|
||||
<span>📝 <b style="color:#555">${scale.question_count}</b> 题</span>
|
||||
<span>⏱ ${timeMin}-${timeMax} 分钟</span>
|
||||
<span>🔒 自动保存</span>
|
||||
</div>
|
||||
${hasCache
|
||||
? `<div style="background:#fff5f3;padding:12px;border-radius:8px;margin-bottom:14px;font-size:13px;color:var(--pri);text-align:center">
|
||||
📌 已有答题进度:<b>${answered}/${scale.question_count}</b> 题
|
||||
</div>`
|
||||
: ''}
|
||||
<button onclick="startTest()" style="display:block;width:100%;padding:15px;border:none;border-radius:10px;background:var(--pri);color:#fff;font-size:17px;font-weight:600;cursor:pointer">
|
||||
${hasCache ? `继续测试 (${answered}/${scale.question_count})` : '开始测试'}
|
||||
</button>
|
||||
${hasCache
|
||||
? `<button onclick="resetAndStart()" style="display:block;width:100%;padding:12px;border:1px solid #ddd;border-radius:10px;background:#fff;color:#999;font-size:14px;cursor:pointer;margin-top:8px">重新开始</button>`
|
||||
: ''}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function startTest() {
|
||||
started = true;
|
||||
// Resume from first unanswered question
|
||||
const firstUnanswered = answers.findIndex(a => a === null);
|
||||
current = firstUnanswered >= 0 ? firstUnanswered : 0;
|
||||
render();
|
||||
}
|
||||
|
||||
function resetAndStart() {
|
||||
answers = new Array(scale.question_count).fill(null);
|
||||
localStorage.removeItem(`scale_${slug}`);
|
||||
started = true;
|
||||
current = 0;
|
||||
render();
|
||||
}
|
||||
|
||||
function saveCache() {
|
||||
localStorage.setItem(`scale_${slug}`, JSON.stringify(answers));
|
||||
}
|
||||
|
||||
function selectOption(idx) {
|
||||
if (submitted) return;
|
||||
answers[current] = idx;
|
||||
saveCache();
|
||||
render();
|
||||
// Auto advance after short delay
|
||||
if (current < scale.question_count - 1) {
|
||||
setTimeout(() => { current++; render(); }, 300);
|
||||
}
|
||||
}
|
||||
|
||||
function goPrev() { if (current > 0) { current--; render(); } }
|
||||
function goNext() { if (current < scale.question_count - 1) { current++; render(); } }
|
||||
|
||||
async function submit() {
|
||||
if (submitted) return;
|
||||
submitted = true;
|
||||
saveCache();
|
||||
|
||||
try {
|
||||
const r = await fetch('../api/scales/result', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
scale_slug: scale.slug,
|
||||
scale_name: scale.name,
|
||||
answers: answers,
|
||||
})
|
||||
});
|
||||
const data = await r.json();
|
||||
// Clear cache
|
||||
localStorage.removeItem(`scale_${slug}`);
|
||||
renderResult(data);
|
||||
} catch(e) {
|
||||
submitted = false;
|
||||
alert('提交失败,请重试');
|
||||
render();
|
||||
}
|
||||
}
|
||||
|
||||
function renderResult(data) {
|
||||
const s = data.scores;
|
||||
const allAnswered = s.answer_count;
|
||||
const total = scale.question_count;
|
||||
const scoreDisplay = s.std_score !== null ? s.std_score : s.raw_score;
|
||||
const isStd = s.std_score !== null;
|
||||
const isFactorScale = s.factors && Object.keys(s.factors).length >= 6;
|
||||
const hasFactors = s.factors && Object.keys(s.factors).length > 0;
|
||||
|
||||
// Factor bars (for any factor count, not just 8+)
|
||||
let factorsHtml = '';
|
||||
if (hasFactors) {
|
||||
const factorMax = s.factor_max || {};
|
||||
const bars = Object.entries(s.factors).map(([k,v]) => {
|
||||
const max = factorMax[k] || 50;
|
||||
const pct = Math.min(Math.round(v / max * 100), 100);
|
||||
return `<div style="margin:6px 0">
|
||||
<div style="display:flex;justify-content:space-between;font-size:12px;margin-bottom:2px">
|
||||
<span style="color:#666">${k}</span><span style="color:var(--pri);font-weight:600">${v}<span style="color:#bbb;font-weight:400">/${max}</span></span>
|
||||
</div>
|
||||
<div style="height:6px;background:#f0f0f0;border-radius:3px;overflow:hidden">
|
||||
<div style="height:100%;width:${pct}%;background:linear-gradient(90deg,var(--pri),#F0987A);border-radius:3px;transition:width 0.5s"></div>
|
||||
</div>
|
||||
</div>`;
|
||||
}).join('');
|
||||
|
||||
let insightHtml = '';
|
||||
if (isFactorScale) {
|
||||
const sorted = Object.entries(s.factors).sort((a,b) => b[1]-a[1]);
|
||||
const top3 = sorted.slice(0,3).map(([k,v]) => `<b>${k}</b>`).join('、');
|
||||
insightHtml = `<div style="margin-top:14px;padding:12px;background:#fff5f3;border-radius:8px;font-size:13px;line-height:1.7">
|
||||
🌟 <b>优势方向:</b>${top3}<br>
|
||||
<span style="color:#999">得分最高的领域代表天赋倾向,建议重点关注和培养。</span>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
factorsHtml = `<div style="margin:20px 0 0;text-align:left">
|
||||
<div style="font-size:14px;font-weight:600;color:#555;margin-bottom:8px">${isFactorScale ? '各维度得分' : '因子得分'}</div>
|
||||
${bars}
|
||||
${insightHtml}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
// Rating badge
|
||||
let ratingHtml = '';
|
||||
if (s.rating) {
|
||||
ratingHtml = `<div style="display:inline-block;margin-top:8px;padding:6px 18px;background:${isFactorScale?'#fff5f3':'#fef0ed'};border-radius:20px;font-size:15px;font-weight:600;color:var(--pri)">${s.rating}</div>`;
|
||||
}
|
||||
|
||||
document.getElementById('app').innerHTML = `
|
||||
<div class="top-bar">
|
||||
<a href="scales.html" class="back">←</a>
|
||||
<div class="title">${scale.name}</div>
|
||||
</div>
|
||||
<div class="result-card" style="text-align:center">
|
||||
<div style="font-size:13px;color:#bbb;margin-bottom:4px">测评完成 · ${allAnswered}/${total} 题</div>
|
||||
${isFactorScale
|
||||
? `<div style="font-size:22px;font-weight:700;color:#333;margin:8px 0 4px">多元智能测评结果</div>`
|
||||
: `<div style="margin:8px 0">
|
||||
<span style="font-size:56px;font-weight:700;color:var(--pri);line-height:1">${scoreDisplay}</span>
|
||||
${isStd ? `<span style="font-size:16px;color:#999;margin-left:4px">分</span>` : ''}
|
||||
</div>
|
||||
${isStd ? `<div style="font-size:12px;color:#bbb;margin-top:-4px">原始分 ${s.raw_score} × 1.25 = 标准分 ${scoreDisplay}</div>` : ''}
|
||||
${!isStd && !isFactorScale ? `<div style="font-size:13px;color:#999">满分 ${s.max_score} · 占比 ${(s.raw_score/s.max_score*100).toFixed(0)}%</div>` : ''}`}
|
||||
${ratingHtml}
|
||||
${factorsHtml}
|
||||
<div style="margin-top:24px;display:flex;gap:10px;justify-content:center">
|
||||
<a href="scales.html" style="flex:1;padding:12px;background:var(--pri);color:#fff;border-radius:10px;text-decoration:none;font-size:14px;font-weight:500">返回量表库</a>
|
||||
<a href="scale-test.html?slug=${slug}" style="flex:1;padding:12px;background:#f5f5f5;color:#666;border-radius:10px;text-decoration:none;font-size:14px">重新测试</a>
|
||||
</div>
|
||||
<p style="margin-top:16px;color:#ccc;font-size:11px">* 结果仅供参考,不能替代专业诊断</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function render() {
|
||||
if (!scale) return;
|
||||
const total = scale.question_count;
|
||||
const pct = ((current + 1) / total * 100).toFixed(0);
|
||||
const isLast = current === total - 1;
|
||||
const selected = answers[current];
|
||||
|
||||
const q = scale.questions[current];
|
||||
const opts = scale.options[current];
|
||||
|
||||
const optHtml = opts.map((o, i) => `
|
||||
<button class="option${selected === i ? ' selected' : ''}" onclick="selectOption(${i})">
|
||||
${String.fromCharCode(65+i)}. ${o}
|
||||
</button>
|
||||
`).join('');
|
||||
|
||||
const allAnswered = answers.every(a => a !== null);
|
||||
|
||||
document.getElementById('app').innerHTML = `
|
||||
<div class="top-bar">
|
||||
<a href="scales.html" class="back">←</a>
|
||||
<div class="title">${scale.name}</div>
|
||||
<div class="qnum">${current+1}/${total}</div>
|
||||
</div>
|
||||
<div class="progress"><div class="bar" style="width:${pct}%"></div></div>
|
||||
<div class="main">
|
||||
<div class="qcard">
|
||||
<div class="qtext"><span class="qidx">${current+1}.</span>${q}</div>
|
||||
<div class="options">${optHtml}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="btns">
|
||||
<button class="btn-prev" onclick="goPrev()" ${current===0?'disabled':''}>上一题</button>
|
||||
${isLast
|
||||
? `<button class="btn-submit" onclick="submit()" ${allAnswered?'':'disabled'}>✓ 提交答卷</button>`
|
||||
: `<button class="btn-next" onclick="goNext()" ${selected===null?'disabled':''}>下一题 →</button>`
|
||||
}
|
||||
</div>
|
||||
${isLast ? `<div style="text-align:center;padding:0 20px 20px;font-size:12px;color:#999">${allAnswered?'全部答完,可以提交了':'还有题目未作答'}</div>` : ''}
|
||||
`;
|
||||
}
|
||||
|
||||
init();
|
||||
@@ -0,0 +1,53 @@
|
||||
const ICONS = {'心理测试综合':'📋','人格测试':'🎭','抑郁症测试':'🌧️','焦虑症测试':'😰','强迫症测试':'🔄','躁狂症测试':'🔥','双向情感障碍':'🌓','恐惧症测试':'😨','智商测试':'🧠','情商测试':'💝','其它测试':'📌'};
|
||||
|
||||
const CAT_KEYS = {'心理测试综合':'cat-综合','人格测试':'cat-人格','抑郁症测试':'cat-抑郁','焦虑症测试':'cat-焦虑','强迫症测试':'cat-强迫','躁狂症测试':'cat-躁狂','双向情感障碍':'cat-双相','恐惧症测试':'cat-恐惧','智商测试':'cat-智商','情商测试':'cat-情商','其它测试':'cat-其他'};
|
||||
|
||||
let scales = [], activeCat = '全部';
|
||||
|
||||
async function load() {
|
||||
document.getElementById('content').innerHTML = '<div class="loading">加载中…</div>';
|
||||
try {
|
||||
const r = await fetch('../api/scales');
|
||||
scales = await r.json();
|
||||
renderCats();
|
||||
renderScales();
|
||||
} catch(e) {
|
||||
document.getElementById('content').innerHTML = '<div class="loading">加载失败,请刷新重试</div>';
|
||||
}
|
||||
}
|
||||
|
||||
function renderCats() {
|
||||
const cats = [...new Set(scales.map(s=>s.category))];
|
||||
const html = ['<button class="cat-btn active" onclick="filter(\'全部\')">全部</button>'];
|
||||
cats.forEach(c => {
|
||||
html.push(`<button class="cat-btn" onclick="filter('${c}')">${c}</button>`);
|
||||
});
|
||||
document.getElementById('cats').innerHTML = html.join('');
|
||||
}
|
||||
|
||||
function filter(cat) {
|
||||
activeCat = cat;
|
||||
document.querySelectorAll('.cat-btn').forEach(b => b.classList.toggle('active', b.textContent === cat));
|
||||
renderScales();
|
||||
}
|
||||
|
||||
function renderScales() {
|
||||
const filtered = activeCat === '全部' ? scales : scales.filter(s=>s.category===activeCat);
|
||||
if (!filtered.length) {
|
||||
document.getElementById('content').innerHTML = '<div class="empty">暂无数据</div>';
|
||||
return;
|
||||
}
|
||||
const html = filtered.map(s => `
|
||||
<a href="scale-test.html?slug=${s.slug}" class="scale-card">
|
||||
<div class="icon ${CAT_KEYS[s.category]||'cat-其他'}">${ICONS[s.category]||'📌'}</div>
|
||||
<div class="info">
|
||||
<div class="name">${s.name}</div>
|
||||
<div class="desc">${s.description||''}</div>
|
||||
</div>
|
||||
<div class="count">${s.question_count}题</div>
|
||||
</a>
|
||||
`).join('');
|
||||
document.getElementById('content').innerHTML = `<div class="scales">${html}</div>`;
|
||||
}
|
||||
|
||||
load();
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/* 愈心谷共享壳 —— 底栏注入、高亮、二级返回 */
|
||||
(function () {
|
||||
var body = document.body;
|
||||
if (!body) return;
|
||||
|
||||
var navKey = body.getAttribute('data-nav') || 'none';
|
||||
var backTo = body.getAttribute('data-back') || '';
|
||||
var pageTitle = body.getAttribute('data-title') || document.title || '';
|
||||
var base = body.getAttribute('data-base') || '';
|
||||
|
||||
// 根据当前路径推断资源根:根目录页用 '',pages/ 下用 '../'
|
||||
if (!base) {
|
||||
base = /\/pages\//.test(location.pathname) ? '../' : '';
|
||||
}
|
||||
|
||||
body.classList.add('app-body');
|
||||
if (navKey === 'none') body.classList.add('no-nav');
|
||||
|
||||
function abs(path) {
|
||||
if (!path) return base;
|
||||
if (/^(https?:|mailto:|tel:|#)/.test(path)) return path;
|
||||
return base + path.replace(/^\.\//, '');
|
||||
}
|
||||
|
||||
// 二级顶栏
|
||||
if (navKey === 'none' && (backTo || pageTitle)) {
|
||||
var top = document.createElement('div');
|
||||
top.className = 'app-topbar';
|
||||
var backHref = abs(backTo || 'yuxingu.html');
|
||||
top.innerHTML =
|
||||
'<a class="back" href="' + backHref + '" aria-label="返回">←</a>' +
|
||||
'<div class="title"></div>';
|
||||
top.querySelector('.title').textContent = pageTitle;
|
||||
body.insertBefore(top, body.firstChild);
|
||||
}
|
||||
|
||||
// 一级底栏
|
||||
if (navKey !== 'none') {
|
||||
var items = [
|
||||
{ key: 'home', href: 'yuxingu.html', icon: '⌂', label: '首页' },
|
||||
{ key: 'know', href: 'pages/know.html', icon: '◉', label: '认识自己' },
|
||||
{ key: 'scales', href: 'pages/scales.html', icon: '☰', label: '专业测评' },
|
||||
{ key: 'activity', href: 'pages/activity.html', icon: '✦', label: '私域活动' },
|
||||
{ key: 'health', href: 'pages/health.html', icon: '♡', label: '健康管理' },
|
||||
{ key: 'mine', href: 'pages/mine.html', icon: '◍', label: '我的' }
|
||||
];
|
||||
var nav = document.createElement('nav');
|
||||
nav.className = 'app-nav';
|
||||
nav.setAttribute('aria-label', '主导航');
|
||||
nav.innerHTML = items.map(function (it) {
|
||||
var cls = it.key === navKey ? ' active' : '';
|
||||
return (
|
||||
'<a href="' + abs(it.href) + '" class="' + cls.trim() + '">' +
|
||||
'<span class="ni">' + it.icon + '</span>' + it.label +
|
||||
'</a>'
|
||||
);
|
||||
}).join('');
|
||||
body.appendChild(nav);
|
||||
}
|
||||
})();
|
||||
+465
@@ -0,0 +1,465 @@
|
||||
const NUM_CLR={
|
||||
1:{top:[232,160,160],front:[196,122,120],right:[168,96,96]},
|
||||
2:{top:[240,200,160],front:[232,168,124],right:[208,144,96]},
|
||||
3:{top:[238,208,128],front:[224,184,88],right:[200,152,64]},
|
||||
4:{top:[160,216,112],front:[112,173,71],right:[88,136,48]},
|
||||
5:{top:[144,200,240],front:[91,155,213],right:[64,120,176]},
|
||||
6:{top:[240,168,176],front:[212,135,142],right:[176,104,112]},
|
||||
7:{top:[184,168,216],front:[139,122,181],right:[104,88,152]},
|
||||
8:{top:[160,144,128],front:[107,91,78],right:[80,64,48]},
|
||||
9:{top:[244,236,224],front:[232,220,200],right:[208,192,168]},
|
||||
};
|
||||
|
||||
// ===== Dimetric 投影 + Z 轴旋转 —— 底座天然水平 =====
|
||||
let rotZ = -0.55; // Z 轴旋转角(弧度)
|
||||
let dragActive=false, dragPX=0;
|
||||
let currentFreq={};
|
||||
|
||||
function proj(x,y,z,rz) {
|
||||
// Z 轴旋转 + 45° dimetric 投影
|
||||
const cr=Math.cos(rz), sr=Math.sin(rz);
|
||||
const xr=x*cr-y*sr, yr=x*sr+y*cr;
|
||||
return { sx:yr-xr, sy:-(xr+yr)+z*0.7, depth:-(xr+yr)+z };
|
||||
}
|
||||
|
||||
function faceVis(face, rz) {
|
||||
const cr=Math.cos(rz), sr=Math.sin(rz);
|
||||
switch(face) {
|
||||
case 'right': return cr+sr>0;
|
||||
case 'left': return cr+sr<0;
|
||||
case 'front': return cr-sr>0;
|
||||
case 'back': return cr-sr<0;
|
||||
case 'top': return true;
|
||||
case 'bottom':return false;
|
||||
}
|
||||
}
|
||||
|
||||
function faceCorns(cx,cy,cz,hs,face) {
|
||||
const h=hs;
|
||||
switch(face) {
|
||||
case 'top': return [[cx-h,cy-h,cz+h],[cx+h,cy-h,cz+h],[cx+h,cy+h,cz+h],[cx-h,cy+h,cz+h]];
|
||||
case 'bottom': return [[cx-h,cy-h,cz-h],[cx+h,cy-h,cz-h],[cx+h,cy+h,cz-h],[cx-h,cy+h,cz-h]];
|
||||
case 'front': return [[cx-h,cy+h,cz-h],[cx+h,cy+h,cz-h],[cx+h,cy+h,cz+h],[cx-h,cy+h,cz+h]];
|
||||
case 'back': return [[cx-h,cy-h,cz-h],[cx+h,cy-h,cz-h],[cx+h,cy-h,cz+h],[cx-h,cy-h,cz+h]];
|
||||
case 'right': return [[cx+h,cy-h,cz-h],[cx+h,cy+h,cz-h],[cx+h,cy+h,cz+h],[cx+h,cy-h,cz+h]];
|
||||
case 'left': return [[cx-h,cy-h,cz-h],[cx-h,cy+h,cz-h],[cx-h,cy+h,cz+h],[cx-h,cy-h,cz+h]];
|
||||
}
|
||||
}
|
||||
|
||||
function renderScene(canvas, freq, rz) {
|
||||
const dpr=window.devicePixelRatio||1;
|
||||
const mob=window.innerWidth<=600;
|
||||
const CS=mob?20:26;
|
||||
const STEP=CS*2+(mob?2:4);
|
||||
const DH=CS*2+(mob?1:2);
|
||||
const PAD=mob?30:40;
|
||||
|
||||
const cols={};
|
||||
for(let n=1;n<=9;n++){
|
||||
const row=2-Math.floor((n-1)/3), col=(n-1)%3;
|
||||
cols[n]={wx:(col-1)*STEP,wy:(row-1)*STEP};
|
||||
}
|
||||
|
||||
// 计算包围盒
|
||||
let mnX=Infinity,mxX=-Infinity,mnY=Infinity,mxY=-Infinity;
|
||||
for(let n=1;n<=9;n++){
|
||||
const {wx,wy}=cols[n];
|
||||
const layers=Math.max(1,freq[n]||1);
|
||||
for(let l=0;l<layers;l++){
|
||||
const wz=l*DH;
|
||||
const cs=[[wx-CS,wy-CS,wz],[wx+CS,wy-CS,wz],[wx+CS,wy+CS,wz],[wx-CS,wy+CS,wz],
|
||||
[wx-CS,wy-CS,wz+CS*2],[wx+CS,wy-CS,wz+CS*2],[wx+CS,wy+CS,wz+CS*2],[wx-CS,wy+CS,wz+CS*2]];
|
||||
cs.forEach(c=>{const p=proj(c[0],c[1],c[2],rz);mnX=Math.min(mnX,p.sx);mxX=Math.max(mxX,p.sx);mnY=Math.min(mnY,p.sy);mxY=Math.max(mxY,p.sy);});
|
||||
}
|
||||
}
|
||||
|
||||
// 底座平台包围盒
|
||||
const bw=STEP*2+CS*2+16, bz=-CS-3;
|
||||
[[-bw,-bw,bz],[bw,-bw,bz],[bw,bw,bz],[-bw,bw,bz]].forEach(c=>{
|
||||
const p=proj(c[0],c[1],c[2],rz);mnX=Math.min(mnX,p.sx);mxX=Math.max(mxX,p.sx);mnY=Math.min(mnY,p.sy);mxY=Math.max(mxY,p.sy);
|
||||
});
|
||||
|
||||
const viewW=Math.max(mxX-mnX+PAD*2,mob?300:400);
|
||||
const viewH=Math.max(mxY-mnY+PAD*2,mob?320:420);
|
||||
canvas.width=Math.ceil(viewW*dpr);canvas.height=Math.ceil(viewH*dpr);
|
||||
canvas.style.width=Math.ceil(viewW)+'px';canvas.style.height=Math.ceil(viewH)+'px';
|
||||
const ctx=canvas.getContext('2d');ctx.scale(dpr,dpr);ctx.clearRect(0,0,viewW,viewH);
|
||||
|
||||
const centerX=(mxX+mnX)/2, ox=viewW/2-centerX, oy=viewH-PAD-mxY;
|
||||
const faces=[];
|
||||
const AF=['top','bottom','front','back','right','left'];
|
||||
|
||||
// 底座平台(厚块)
|
||||
(()=>{
|
||||
const bt=mob?4:5, bzTop=-CS-2, bzBot=bzTop-bt;
|
||||
const bclr=[170,155,135];
|
||||
[
|
||||
{t:'top', c:[[-bw,-bw,bzTop],[bw,-bw,bzTop],[bw,bw,bzTop],[-bw,bw,bzTop]],clr:bclr},
|
||||
{t:'front', c:[[-bw,bw,bzBot],[bw,bw,bzBot],[bw,bw,bzTop],[-bw,bw,bzTop]],clr:bclr.map(v=>Math.floor(v*0.85))},
|
||||
{t:'right', c:[[bw,-bw,bzBot],[bw,bw,bzBot],[bw,bw,bzTop],[bw,-bw,bzTop]],clr:bclr.map(v=>Math.floor(v*0.7))},
|
||||
].forEach(fb=>{
|
||||
const pts=fb.c.map(c=>proj(c[0],c[1],c[2],rz));
|
||||
const avgZ=pts.reduce((s,p)=>s+p.depth,0)/4;
|
||||
const projPts=pts.map(p=>[ox+p.sx,oy-p.sy]);
|
||||
faces.push({z:avgZ,proj:projPts,color:fb.clr,isTop:fb.t==='top',num:0});
|
||||
});
|
||||
})();
|
||||
|
||||
// 方块面
|
||||
for(let n=1;n<=9;n++){
|
||||
const {wx,wy}=cols[n];
|
||||
const layers=Math.max(1,freq[n]||1);
|
||||
const clr=NUM_CLR[n];
|
||||
const fc={top:clr.top,bottom:clr.top,front:clr.front,back:clr.front,right:clr.right,left:clr.right};
|
||||
for(let l=0;l<layers;l++){
|
||||
const wz=l*DH;
|
||||
for(const face of AF){
|
||||
if(!faceVis(face,rz))continue;
|
||||
const crns=faceCorns(wx,wy,wz,CS,face);
|
||||
const pts=crns.map(c=>proj(c[0],c[1],c[2],rz));
|
||||
const avgZ=pts.reduce((s,p)=>s+p.depth,0)/4;
|
||||
const projPts=pts.map(p=>[ox+p.sx,oy-p.sy]);
|
||||
const cnt=proj(wx,wy,wz+CS,rz);
|
||||
faces.push({z:avgZ,proj:projPts,color:fc[face],isTop:face==='top',num:n,cx:ox+cnt.sx,cy:oy-cnt.sy});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
faces.sort((a,b)=>a.z-b.z);
|
||||
const alpha=0.82;
|
||||
|
||||
for(const f of faces){
|
||||
const [r,g,b]=f.color;
|
||||
ctx.fillStyle=`rgba(${r},${g},${b},${alpha})`;
|
||||
ctx.beginPath();ctx.moveTo(f.proj[0][0],f.proj[0][1]);
|
||||
for(let i=1;i<4;i++)ctx.lineTo(f.proj[i][0],f.proj[i][1]);
|
||||
ctx.closePath();ctx.fill();
|
||||
if(f.isTop&&f.num){
|
||||
ctx.fillStyle='rgba(255,255,255,0.92)';
|
||||
const fs=mob?11:14;ctx.font=`bold ${fs}px Georgia,"Noto Serif SC",serif`;
|
||||
ctx.textAlign='center';ctx.textBaseline='middle';
|
||||
ctx.shadowColor='rgba(0,0,0,0.25)';ctx.shadowBlur=2;
|
||||
ctx.fillText(f.num,f.cx,f.cy);
|
||||
ctx.shadowColor='transparent';ctx.shadowBlur=0;
|
||||
}
|
||||
}
|
||||
|
||||
// 底部 2D 横条
|
||||
(()=>{
|
||||
const bh=mob?18:22,by=viewH-bh;
|
||||
const grad=ctx.createLinearGradient(0,by,0,viewH);
|
||||
grad.addColorStop(0,'rgba(180,165,145,0.5)');
|
||||
grad.addColorStop(0.3,'rgba(160,145,125,0.35)');
|
||||
grad.addColorStop(1,'rgba(140,125,105,0.1)');
|
||||
ctx.fillStyle=grad;ctx.fillRect(0,by,viewW,bh);
|
||||
ctx.strokeStyle='rgba(140,125,105,0.45)';ctx.lineWidth=1;
|
||||
ctx.beginPath();ctx.moveTo(0,by);ctx.lineTo(viewW,by);ctx.stroke();
|
||||
})();
|
||||
}
|
||||
|
||||
function drawScene(){
|
||||
const canvas=document.querySelector('#scene3d canvas');
|
||||
if(!canvas)return;
|
||||
renderScene(canvas,currentFreq,rotZ);
|
||||
}
|
||||
|
||||
function setupRotation(){
|
||||
const el=document.querySelector('#scene3d');if(!el)return;
|
||||
function getX(e){return e.touches&&e.touches.length?e.touches[0].clientX:e.clientX}
|
||||
function onStart(e){dragActive=true;dragPX=getX(e);e.preventDefault()}
|
||||
function onMove(e){
|
||||
if(!dragActive)return;
|
||||
const cx=getX(e),dx=cx-dragPX;dragPX=cx;
|
||||
rotZ+=dx*0.008;drawScene();e.preventDefault();
|
||||
}
|
||||
function onEnd(){dragActive=false}
|
||||
el.addEventListener('mousedown',onStart);
|
||||
el.addEventListener('touchstart',onStart,{passive:false});
|
||||
window.addEventListener('mousemove',onMove);
|
||||
window.addEventListener('touchmove',onMove,{passive:false});
|
||||
window.addEventListener('mouseup',onEnd);
|
||||
window.addEventListener('touchend',onEnd);
|
||||
}
|
||||
|
||||
// ===== 解读模块生成器 =====
|
||||
|
||||
const GUA64=['','乾为天','天泽履','天火同人','天雷无妄','天风姤','天水讼','天山遁','天地否','泽天夬','兑为泽','泽火革','泽雷随','泽风大过','泽水困','泽山咸','泽地萃','火天大有','火泽睽','离为火','火雷噬嗑','火风鼎','火水未济','火山旅','火地晋','雷天大壮','雷泽归妹','雷火丰','震为雷','雷风恒','雷水解','雷山小过','雷地豫','风天小畜','风泽中孚','风火家人','风雷益','巽为风','风水涣','风山渐','风地观','水天需','水泽节','水火既济','水雷屯','水风井','坎为水','水山蹇','水地比','山天大畜','山泽损','山火贲','山雷颐','山风蛊','山水蒙','艮为山','山地剥','地天泰','地泽临','地火明夷','地雷复','地风升','地水师','地山谦','坤为地'];
|
||||
const GUA64_DESC={'乾为天':'刚健中正,纯阳之卦。运势旺盛,宜主动进取。','坤为地':'柔顺包容,厚德载物。耐心守成,终得善果。','水雷屯':'万事起头难。创业维艰,宜稳扎稳打。','山水蒙':'蒙昧初开。宜虚心求教,不可自作聪明。','水天需':'等待时机。耐心守候,时机未到不可强求。','天水讼':'争执诉讼之象。退一步海阔天空。','地水师':'统兵出征。纪律严明,以正制邪。','水地比':'亲附和合。广结善缘,必得助力。','地天泰':'天地交泰。万事亨通,上下和合。','天地否':'闭塞不通。宜隐忍守正,静待时机。','天火同人':'志同道合。同心协力可成大事。','火天大有':'丰收大有。顺天应时,财源广进。','雷天大壮':'强盛壮大。力量充沛但切忌恃强凌弱。','火地晋':'旭日东升。逐步晋升,光明在前。','地火明夷':'光明受损。韬光养晦,等待天明。','风火家人':'家庭和乐。修身齐家,家和万事兴。','火泽睽':'乖离不合。求同存异,小事可成。','水山蹇':'艰难险阻。退守求稳,不可强闯。','雷水解':'困难解除。柳暗花明,危机化解。','山泽损':'损下益上。有舍有得,吃亏是福。','风雷益':'增益有利。抓住机遇,积极作为。','泽天夬':'决断果断。当断则断,不受其乱。','天风姤':'不期而遇。随缘而处,不可强求。','泽地萃':'聚集荟萃。人气旺盛,利于合作。','地风升':'步步高升。循序渐进,终达目标。','泽水困':'困顿之象。坚守正道,终可脱困。','水风井':'修德养民。滋养根本,源远流长。','泽火革':'变革之时。顺势而变,除旧布新。','火风鼎':'鼎新革故。稳固根基,创新求变。','震为雷':'震动警醒。临危不乱,化险为夷。','艮为山':'知止不殆。适可而止,安守本分。','风山渐':'循序渐进。按部就班,水到渠成。','雷泽归妹':'婚嫁之象。以诚相待,终成眷属。','雷火丰':'丰盛之时。月满则亏,知足常乐。','火山旅':'行旅之象。随遇而安,莫问归期。','巽为风':'柔顺渗透。以柔克刚,润物无声。','兑为泽':'喜悦沟通。以和为贵,广结善缘。','风水涣':'涣散离散。聚拢人心,凝聚力量。','水泽节':'节制有度。合理规划,量力而行。','风泽中孚':'诚信感召。以诚待人,金石为开。','雷山小过':'小有过失。宜下不宜上,宜小不宜大。','水火既济':'事已成就。居安思危,防微杜渐。','火水未济':'未竟之业。持之以恒,终将成功。','地山谦':'谦逊受益。虚怀若谷,众望所归。','雷地豫':'安乐愉悦。居安思危,不可懈怠。','泽雷随':'随顺时势。灵活应变,顺势而为。','山风蛊':'蛊坏整治。拨乱反正,振弊起衰。','地泽临':'居高临下。亲近下属,恩威并施。','风地观':'观览体察。审时度势,明察秋毫。','火雷噬嗑':'咬合刑罚。明断是非,刚正不阿。','山火贲':'文饰美化。返璞归真,内在为重。','山地剥':'剥落凋零。守正待时,顺势而为。','地雷复':'一阳来复。否极泰来,新生在望。','天雷无妄':'真实无妄。顺其自然,不妄作为。','山天大畜':'蓄积力量。厚积薄发,大器晚成。','山雷颐':'颐养之道。养生养德,自求口实。','泽风大过':'过度之象。刚柔相济,中庸为贵。','坎为水':'重重险难。坚守诚信,终可出险。','离为火':'光明依附。柔顺中正,照亮四方。'};
|
||||
|
||||
function meihuaHTML(y,m,d){
|
||||
const dz=['子','丑','寅','卯','辰','巳','午','未','申','酉','戌','亥'];
|
||||
const dzNum={子:1,丑:2,寅:3,卯:4,辰:5,巳:6,午:7,未:8,申:9,酉:10,戌:11,亥:12};
|
||||
const tg=['甲','乙','丙','丁','戊','己','庚','辛','壬','癸'];
|
||||
const guaName=['','乾 ☰','兑 ☱','离 ☲','震 ☳','巽 ☴','坎 ☵','艮 ☶','坤 ☷'];
|
||||
const guaEle=['','金','金','火','木','木','水','土','土'];
|
||||
const ndz=dz[(y-4)%12],ntg=tg[(y-4)%10],nDZ=dzNum[ndz];
|
||||
const upNum=nDZ%8||8,downNum=(m+d)%8||8,dongY=(nDZ+m+d)%6||6;
|
||||
const benGuaIdx=(upNum-1)*8+downNum;
|
||||
const benName=GUA64[benGuaIdx]||(guaName[upNum]+'+'+guaName[downNum]);
|
||||
const varUp=dongY<=3?upNum:((upNum^5)||8),varDown=dongY<=3?((downNum^5)||8):downNum;
|
||||
const varIdx=(varUp-1)*8+varDown;
|
||||
const varName=GUA64[varIdx]||(guaName[varUp]+'+'+guaName[varDown]);
|
||||
const desc=GUA64_DESC[benName]||'卦象已显,静心体悟其中玄机。';
|
||||
const dongYaoNames=['初爻','二爻','三爻','四爻','五爻','上爻'];
|
||||
const ue=guaEle[upNum],de=guaEle[downNum];
|
||||
const shengKe={'金':'木','木':'土','土':'水','水':'火','火':'金'};
|
||||
let tiYong;
|
||||
if(upNum===downNum)tiYong='体用比和,万事顺遂';
|
||||
else if(shengKe[ue]===de)tiYong='体克用,事可成但需费心力';
|
||||
else if(shengKe[de]===ue)tiYong='用克体,宜守不宜攻,等待时机';
|
||||
else tiYong='体用相生,遇贵人之象,万事可成';
|
||||
return `<div class="module-title">✦ ${benName} ✦</div>
|
||||
<div class="module-desc">${ntg}${ndz}年 · 上卦${guaName[upNum]} 下卦${guaName[downNum]} · 动在${dongYaoNames[dongY-1]}</div>
|
||||
<div class="module-section"><b>▸ 本卦:${benName}</b><br><span style="color:#9b8a78">${desc}</span><br><br><b>▸ 变卦:${varName}</b><br><span style="color:#9b8a78">${GUA64_DESC[varName]||'变动已生,观其变化而知吉凶。'}</span><br><br><b>▸ 体用生克</b><br>上卦属${ue}·下卦属${de} — ${tiYong}</div>`;
|
||||
}
|
||||
|
||||
const XLR_NAMES=['','大安','留连','速喜','赤口','小吉','空亡'];
|
||||
const XLR_DESC={'大安':'大吉。光明正大,平安顺遂。求财向东南,凡事安稳。','留连':'凶。事难成就,去者未归。宜静不宜动,急则生变。','速喜':'大吉。喜事将至,向南吉利,行人有信至。','赤口':'凶。口舌是非,宜守不宜攻,失物难寻。','小吉':'大吉。好事将近,路遇贵人,向西南吉利。','空亡':'大凶。谋事落空,宜静待时机,强求反遭祸。'};
|
||||
|
||||
function xiaoLiuRenHTML(y,m,d){
|
||||
const mo=m>12?m-12:m,shi=7;let pos=1;
|
||||
for(let i=1;i<mo;i++)pos=pos%6+1;
|
||||
for(let i=1;i<d;i++)pos=pos%6+1;
|
||||
for(let i=1;i<shi;i++)pos=pos%6+1;
|
||||
const name=XLR_NAMES[pos],isGood=name==='大安'||name==='速喜'||name==='小吉';
|
||||
return `<div class="module-title">✦ 小六壬 掌诀:${name} ✦</div>
|
||||
<div class="module-desc">月上起日,日上起时(午时)→ 最终落位</div>
|
||||
<div class="module-section"><span style="font-size:1.6em;font-weight:700;color:${isGood?'#70ad47':'#c47a78'}">${name}</span><br><span style="color:#9b8a78">${XLR_DESC[name]}</span><br><br><b>▸ 六掌速查</b><br><span style="color:#70ad47">大安</span> · <span style="color:#c47a78">留连</span> · <span style="color:#70ad47">速喜</span> · <span style="color:#c47a78">赤口</span> · <span style="color:#70ad47">小吉</span> · <span style="color:#c47a78">空亡</span></div>`;
|
||||
}
|
||||
|
||||
function daLiuRenHTML(y,m,d){
|
||||
const dz=['子','丑','寅','卯','辰','巳','午','未','申','酉','戌','亥'];
|
||||
const ydz=dz[(y-4)%12],yueJiangMap={1:'子',2:'亥',3:'戌',4:'酉',5:'申',6:'未',7:'午',8:'巳',9:'辰',10:'卯',11:'寅',12:'丑'};
|
||||
return `<div class="module-title">✦ 大六壬 课式概要 ✦</div>
|
||||
<div class="module-desc">${y}年(${ydz}年)· ${m}月 月将${yueJiangMap[m]||'午'} · 占时午</div>
|
||||
<div class="module-section"><b>▸ 简介</b><br><span style="color:#9b8a78">六壬为三式之首,以月将加占时起天地盘,排四课发三传断吉凶。演算极精(12天将+12神煞+遁干),此处展示基础课式。</span><br><br><b>▸ 天地盘</b><br><span style="color:#9b8a78">地盘:子丑寅卯辰巳午未申酉戌亥(固定)<br>天盘:月将加临占时之上,顺布十二辰</span><br><br><b>▸ 四课</b><br><span style="color:#9b8a78">第一课日干阳神 · 第二课日干阴神 · 第三课日支阳神 · 第四课日支阴神</span><br><br><b>▸ 三传</b><br><span style="color:#9b8a78">初传(发用·起始)→ 中传(移易·过程)→ 末传(归计·结局)</span><br><br><b>▸ 九宗门</b><br><span style="color:#9b8a78">贼克·比用·涉害·遥克·昴星·别责·八专·伏吟·反吟</span><br><br><span style="color:#b8a898;font-size:0.95em">※ 完整排盘需配合具体时辰与日干支,建议请六壬师详断。</span></div>`;
|
||||
}
|
||||
|
||||
function qiMenHTML(y,m,d){
|
||||
const dz=['子','丑','寅','卯','辰','巳','午','未','申','酉','戌','亥'];
|
||||
const tg=['甲','乙','丙','丁','戊','己','庚','辛','壬','癸'];
|
||||
const mingGongMap={1:'丑',2:'寅',3:'卯',4:'辰',5:'巳',6:'午',7:'未',8:'申',9:'酉',10:'戌',11:'亥',12:'子'};
|
||||
return `<div class="module-title">✦ 奇门遁甲 命盘概要 ✦</div>
|
||||
<div class="module-desc">${tg[(y-4)%10]}${dz[(y-4)%12]}年 · 命宫:${mingGongMap[m]||'午'}</div>
|
||||
<div class="module-section"><b>▸ 简介</b><br><span style="color:#9b8a78">奇门合天(九星)、地(九宫)、人(八门)、神(八神)四层于一体。排盘需节气+时辰+用局,极为精密。</span><br><br><b>▸ 地盘九宫</b><br><span style="color:#9b8a78">戴九履一·左三右七·二四为肩·六八为足·五居中央<br>坎一水·坤二土·震三木·巽四木·中五土·乾六金·兑七金·艮八土·离九火</span><br><br><b>▸ 八门(人事)</b><br><span style="color:#9b8a78">开门(吉)·休门(吉)·生门(吉)·伤门(凶)·杜门(平)·景门(平)·死门(凶)·惊门(凶)</span><br><br><b>▸ 九星(天时)</b><br><span style="color:#9b8a78">天蓬·天任·天冲·天辅·天英·天芮·天柱·天心·天禽</span><br><br><span style="color:#b8a898;font-size:0.95em">※ 全面排盘需根据具体时辰和节气确定阳遁/阴遁几局,建议请奇门师详断。</span></div>`;
|
||||
}
|
||||
|
||||
const ZODIAC=[
|
||||
{name:'摩羯座',emoji:'♑',elem:'土',from:[12,22],to:[1,19],trait:'坚韧务实,目标明确,大器晚成',lucky:'深绿·黑'},
|
||||
{name:'水瓶座',emoji:'♒',elem:'风',from:[1,20],to:[2,18],trait:'独立创新,思维超前,人道主义',lucky:'蓝·银'},
|
||||
{name:'双鱼座',emoji:'♓',elem:'水',from:[2,19],to:[3,20],trait:'浪漫敏感,富有同情心,艺术天赋',lucky:'紫·海蓝'},
|
||||
{name:'白羊座',emoji:'♈',elem:'火',from:[3,21],to:[4,19],trait:'热情勇敢,行动力强,开拓先锋',lucky:'红·金'},
|
||||
{name:'金牛座',emoji:'♉',elem:'土',from:[4,20],to:[5,20],trait:'稳健可靠,审美出众,坚定执着',lucky:'粉·绿'},
|
||||
{name:'双子座',emoji:'♊',elem:'风',from:[5,21],to:[6,21],trait:'机智灵活,善于沟通,双面魅力',lucky:'黄·银'},
|
||||
{name:'巨蟹座',emoji:'♋',elem:'水',from:[6,22],to:[7,22],trait:'温柔顾家,情感丰富,保护欲强',lucky:'白·银'},
|
||||
{name:'狮子座',emoji:'♌',elem:'火',from:[7,23],to:[8,22],trait:'自信大方,领导力强,光芒四射',lucky:'金·橙'},
|
||||
{name:'处女座',emoji:'♍',elem:'土',from:[8,23],to:[9,22],trait:'细致严谨,追求完美,服务精神',lucky:'灰·米'},
|
||||
{name:'天秤座',emoji:'♎',elem:'风',from:[9,23],to:[10,23],trait:'优雅公正,善于平衡,社交达人',lucky:'粉·蓝'},
|
||||
{name:'天蝎座',emoji:'♏',elem:'水',from:[10,24],to:[11,22],trait:'深沉敏锐,意志坚定,极致深情',lucky:'深红·黑'},
|
||||
{name:'射手座',emoji:'♐',elem:'火',from:[11,23],to:[12,21],trait:'乐观自由,探索精神,哲学思维',lucky:'紫·深蓝'},
|
||||
];
|
||||
|
||||
function xingZuoHTML(y,m,d){
|
||||
let z=ZODIAC.find(x=>(m===x.from[0]&&d>=x.from[1])||(m===x.to[0]&&d<=x.to[1]));
|
||||
if(!z){if((m===12&&d>=22)||(m===1&&d<=19))z=ZODIAC[0];else if(m===1)z=ZODIAC[1];else z=ZODIAC.find(x=>m===x.from[0]&&d<x.from[1])||ZODIAC[0];}
|
||||
const et={火:'热情主动,充满生命力,敢于冒险',土:'踏实稳重,注重现实,可靠可信',风:'思维敏捷,善于交流,适应力强',水:'情感深邃,直觉敏锐,富有创造力'};
|
||||
const ec={火:'#c47a78',土:'#8b7a68',风:'#91a3b0',水:'#6b9eb3'};
|
||||
return `<div class="module-title">✦ ${z.emoji} ${z.name} ✦</div>
|
||||
<div class="module-desc">${z.elem}象星座 · 幸运色:${z.lucky}</div>
|
||||
<div class="module-section"><span style="font-size:2em">${z.emoji}</span><br><b>▸ 性格特质</b><br><span style="color:#9b8a78">${z.trait}</span><br><br><b>▸ 元素能量</b><br><span style="color:#9b8a78">${et[z.elem]}</span><br><br><b>▸ 十二星座</b><br>${ZODIAC.map(x=>`<span style="color:${ec[x.elem]}">${x.emoji}</span>`).join(' ')}</div>`;
|
||||
}
|
||||
|
||||
const BAGUA=[
|
||||
{name:'乾 ☰',xiang:'天',elem:'金',clr:'#d4c5a0',txt:'#8b7355',desc:'刚健·创造'},
|
||||
{name:'兑 ☱',xiang:'泽',elem:'金',clr:'#e8d5c8',txt:'#a08070',desc:'喜悦·沟通'},
|
||||
{name:'离 ☲',xiang:'火',elem:'火',clr:'#e8c8c0',txt:'#b06050',desc:'光明·智慧'},
|
||||
{name:'震 ☳',xiang:'雷',elem:'木',clr:'#c8d8b0',txt:'#5a7a3a',desc:'行动·震动'},
|
||||
{name:'巽 ☴',xiang:'风',elem:'木',clr:'#b8d0b8',txt:'#4a7a4a',desc:'渗透·柔顺'},
|
||||
{name:'坎 ☵',xiang:'水',elem:'水',clr:'#a8c8d8',txt:'#3a6a8a',desc:'险陷·智慧'},
|
||||
{name:'艮 ☶',xiang:'山',elem:'土',clr:'#d0c8b0',txt:'#7a6a4a',desc:'静止·坚守'},
|
||||
{name:'坤 ☷',xiang:'地',elem:'土',clr:'#e0d8c0',txt:'#8a7a5a',desc:'柔顺·包容'},
|
||||
];
|
||||
|
||||
function wuXingHTML(y,m,d){
|
||||
const yElem=['金','水','木','火','土'][Math.floor(((y-4)%10)/2)];
|
||||
const mElem=['木','木','土','火','火','土','金','金','土','水','水','土'][m-1];
|
||||
const dElem=['木','火','土','金','水'][d%5];
|
||||
const ec={金:'#d4c5a0',水:'#a8c8d8',木:'#b8d0b8',火:'#e8c8c0',土:'#e0d8c0'};
|
||||
return `<div class="module-title">✦ 五行八卦 能量图谱 ✦</div>
|
||||
<div class="module-desc">年柱${yElem} · 月柱${mElem} · 日柱${dElem}</div>
|
||||
<div class="module-section"><b>▸ 五行</b><br>
|
||||
<div style="display:flex;gap:4px;margin:6px 0">${['金','水','木','火','土'].map(e=>`<span style="flex:1;text-align:center;padding:4px;background:${ec[e]};color:#5a4636;border-radius:3px;font-size:0.95em"><b>${e}</b></span>`).join('')}</div>
|
||||
<span style="color:#9b8a78">金(义)·水(智)·木(仁)·火(礼)·土(信)<br>相生:金→水→木→火→土→金<br>相克:金→木→土→水→火→金</span><br><br><b>▸ 八卦</b><br>
|
||||
<div class="bagua-grid">${BAGUA.map(b=>`<div class="bagua-cell" style="background:${b.clr};color:${b.txt}"><b>${b.name}</b><br><span style="font-size:0.92em">${b.elem}·${b.xiang}<br>${b.desc}</span></div>`).join('')}</div>
|
||||
<span style="color:#9b8a78">八卦歌诀:乾三连·坤六断·震仰盂·艮覆碗·离中虚·坎中满·兑上缺·巽下断</span></div>`;
|
||||
}
|
||||
|
||||
// ===== Tab 切换 =====
|
||||
function initTabs(){
|
||||
const nav=document.querySelector('.tabs-nav');
|
||||
if(!nav)return;
|
||||
nav.addEventListener('click',e=>{
|
||||
const btn=e.target.closest('.tab-btn');
|
||||
if(!btn)return;
|
||||
const tab=btn.dataset.tab;
|
||||
nav.querySelectorAll('.tab-btn').forEach(b=>b.classList.remove('active'));
|
||||
btn.classList.add('active');
|
||||
document.querySelectorAll('.tab-panel').forEach(p=>p.classList.remove('active'));
|
||||
const panel=document.getElementById(tab+'-panel');
|
||||
if(panel)panel.classList.add('active');
|
||||
});
|
||||
}
|
||||
|
||||
function update(v,y,m,d){
|
||||
document.getElementById('valR').textContent=v.R;
|
||||
document.getElementById('valQ').textContent=v.Q;
|
||||
document.getElementById('valP').textContent=v.P;
|
||||
document.getElementById('valO').textContent=v.O;
|
||||
document.getElementById('valM').textContent=v.M;
|
||||
document.getElementById('valN').textContent=v.N;
|
||||
document.getElementById('valI').textContent=v.I;
|
||||
document.getElementById('valJ').textContent=v.J;
|
||||
document.getElementById('valK').textContent=v.K;
|
||||
document.getElementById('valL').textContent=v.L;
|
||||
document.getElementById('valU').textContent=v.U;
|
||||
document.getElementById('valT').textContent=v.T;
|
||||
document.getElementById('valS').textContent=v.S;
|
||||
document.getElementById('valX').textContent=v.X;
|
||||
document.getElementById('valV').textContent=v.V;
|
||||
document.getElementById('valW').textContent=v.W;
|
||||
document.getElementById('dateD').textContent=String(d).padStart(2,'0');
|
||||
document.getElementById('dateM').textContent=String(m).padStart(2,'0');
|
||||
document.getElementById('dateY').textContent=y;
|
||||
|
||||
const allNums=[0,1,2,3,4,5,6,7,8,9];
|
||||
const inner=new Set([v.O,v.M,v.N,v.I,v.J,v.K,v.L]);
|
||||
const present=new Set([...inner,v.Q,v.P,v.R,v.T,v.S,v.U,v.V,v.W,v.X,v.AB[0],v.AB[1],v.CD[0],v.CD[1],v.EF[0],v.EF[1],v.GH[0],v.GH[1]]);
|
||||
const miss={inner:allNums.filter(n=>!inner.has(n)),outer:allNums.filter(n=>![v.Q,v.P,v.R,v.T,v.S,v.U,v.V,v.W,v.X].includes(n)),both:allNums.filter(n=>!present.has(n))};
|
||||
const n=NUM[v.O]||{};
|
||||
|
||||
const freq={};
|
||||
for(let i=1;i<=9;i++)freq[i]=0;
|
||||
const allVals=[v.O,v.M,v.N,v.I,v.J,v.K,v.L,v.Q,v.P,v.R,v.T,v.S,v.U,v.V,v.W,v.X,
|
||||
v.AB[0],v.AB[1],v.CD[0],v.CD[1],v.EF[0],v.EF[1],v.GH[0],v.GH[1]];
|
||||
allVals.forEach(x=>{if(x>=1&&x<=9)freq[x]++;});
|
||||
currentFreq=freq;
|
||||
const freqArr=Object.entries(freq).sort((a,b)=>b[1]-a[1]);
|
||||
const most=freqArr[0],least=freqArr.filter(([,c])=>c===freqArr[freqArr.length-1][1]);
|
||||
|
||||
let freeHtml=`<div style="text-align:center;margin-bottom:18px;">`;
|
||||
freeHtml+=`<div style="font-size:2.4em;font-weight:700;color:#c47a78;line-height:1;margin-bottom:4px;">${v.O}号人</div>`;
|
||||
freeHtml+=`<div style="font-size:0.85em;font-weight:600;color:#5a4636;margin-bottom:6px;">${n.title||''}</div>`;
|
||||
freeHtml+=`<div style="font-size:0.75em;color:#6b5b4e;line-height:1.7;max-width:340px;margin:0 auto;text-align:left;">${n.desc||''}</div>`;
|
||||
freeHtml+=`</div>`;
|
||||
let premiumHtml=``;
|
||||
premiumHtml+=`<div style="font-size:0.7em;color:#9b8a78;margin-top:8px;line-height:1.7;text-align:left;max-width:340px;margin-left:auto;margin-right:auto;">`;
|
||||
premiumHtml+=`<b>✦ 天赋优势</b>:${n.pos||''}<br><b>✦ 人生课题</b>:${n.neg||''}<br><b>✦ 五行属性</b>:${n.elem||''}<br><b>✦ 适合职业</b>:${n.job||''}</div></div>`;
|
||||
|
||||
premiumHtml+=`<hr class="divider">`;
|
||||
premiumHtml+=`<div style="font-size:0.82em;font-weight:600;color:#5a4636;text-align:center;margin-bottom:8px;">✦ 数字能量图谱 ✦</div>`;
|
||||
premiumHtml+=`<div style="font-size:0.7em;color:#6b5b4e;text-align:center;margin-bottom:0;">你的生命三角共汇聚了 <b>${allVals.filter(x=>x>=1).length}</b> 个能量数字</div>`;
|
||||
premiumHtml+=`<div class="scene-3d" id="scene3d"><canvas></canvas><div class="scene-hint">↻ 左右旋转</div></div>`;
|
||||
premiumHtml+=`<div class="freq-label">🔥 能量最强:<b>${most[0]}</b>(出现 ${most[1]} 次),是你生命中最闪耀的特质</div>`;
|
||||
if(least.length>0)premiumHtml+=`<div class="freq-label" style="margin-top:0;">💧 能量最弱:${least.map(([n])=>`<b>${n}</b>`).join('、')}(仅 ${least[0][1]} 次),值得你用心浇灌</div>`;
|
||||
|
||||
premiumHtml+=`<hr class="divider">`;
|
||||
premiumHtml+=`<div style="font-size:0.82em;font-weight:600;color:#5a4636;text-align:center;margin-bottom:6px;">✦ 三角内外——真实自我与外界眼光 ✦</div>`;
|
||||
premiumHtml+=`<div style="font-size:0.7em;line-height:1.7;color:#6b5b4e;padding:0 4px;">`;
|
||||
premiumHtml+=`<div><b>▸ 三角内部</b>:代表你内在真实的自我——潜意识的驱动、与生俱来的性格特质、不为人知的内心世界。</div>`;
|
||||
premiumHtml+=`<div style="margin-top:3px;"><b>▸ 三角外部</b>:代表外界对你的认知——你在工作、家庭、社交中呈现出的样子,以及他人眼中的你。</div>`;
|
||||
premiumHtml+=`<div style="margin-top:3px;"><b>▸ 下方基因</b>(父母遗传):代表来自父亲和母亲的先天遗传基因,是你性格的底层土壤。</div></div>`;
|
||||
|
||||
premiumHtml+=`<hr class="divider">`;
|
||||
premiumHtml+=`<div style="font-size:0.82em;font-weight:600;color:#5a4636;text-align:center;margin-bottom:6px;">✦ 性格密码深度解读 ✦</div>`;
|
||||
premiumHtml+=`<div style="font-size:0.7em;line-height:1.8;color:#6b5b4e;padding:0 4px;">`;
|
||||
premiumHtml+=`<div style="margin-bottom:4px;"><b>潜意识码:${v.I}${v.J}${v.K}${v.L}</b></div>`;
|
||||
premiumHtml+=`<div style="color:#9b8a78;margin-bottom:8px;">你内心深处的真实驱动力,最本能的反应模式。四个数字分别主宰:思维、情感、行动、人际。</div>`;
|
||||
premiumHtml+=`<div style="margin-bottom:4px;"><b>内心码:${v.M}${v.N}</b></div>`;
|
||||
premiumHtml+=`<div style="color:#9b8a78;margin-bottom:8px;">深层情感模式与核心价值观。理性行动 vs 感性人际——两者的平衡就是你面对压力时的姿态。</div>`;
|
||||
premiumHtml+=`<div style="margin-bottom:4px;"><b>坐镇码:${v.I}${v.M}${v.O}</b></div>`;
|
||||
premiumHtml+=`<div style="color:#9b8a78;margin-bottom:8px;">性格核心轴线:潜意识→内心→主性格。三环相扣,是你从内驱力到外在呈现的完整链条,也是你最稳定的底色。</div>`;
|
||||
premiumHtml+=`<div style="margin-bottom:4px;"><b>父亲基因:${v.EF.join('')} ${v.GH.join('')}</b> · <b>母亲基因:${v.AB.join('')} ${v.CD.join('')}</b></div>`;
|
||||
premiumHtml+=`<div style="color:#9b8a78;">父亲基因 ← 年份 ${v.EF.join('')}${v.GH.join('')})承载家族传承与事业基因<br>母亲基因 ← 月${v.CD.join('')} 日${v.AB.join('')})影响情感模式与人际底色</div></div>`;
|
||||
|
||||
premiumHtml+=`<hr class="divider">`;
|
||||
premiumHtml+=`<div style="font-size:0.82em;font-weight:600;color:#5a4636;text-align:center;margin-bottom:6px;">✦ 数字缺失与能量过剩 ✦</div>`;
|
||||
premiumHtml+=`<div style="font-size:0.7em;line-height:1.7;color:#6b5b4e;padding:0 4px;">`;
|
||||
if(miss.both.length>0){premiumHtml+=`<div style="margin-bottom:4px;"><b>⚠ 缺失数字</b></div>`;miss.both.forEach(n=>{premiumHtml+=`<div style="margin-bottom:3px;">· <b>缺${n}</b>:${MISS_MSG[n]||''}</div>`;});}
|
||||
else premiumHtml+=`<div style="margin-bottom:4px;color:#9b8a78;">✦ 你的数字分布完整,能量均衡。</div>`;
|
||||
const over=[];for(let i=1;i<=9;i++){if(freq[i]>=4&&!miss.both.includes(i))over.push(i);}
|
||||
if(over.length>0){premiumHtml+=`<div style="margin-top:6px;margin-bottom:4px;"><b>⚡ 能量过剩</b></div>`;over.forEach(n=>{premiumHtml+=`<div style="margin-bottom:3px;">· <b>多${n}</b>(×${freq[n]}):${MISS_STRONG[n]||''}</div>`;});}
|
||||
premiumHtml+=`</div>`;
|
||||
|
||||
const joints=[['I','J'],['J','M'],['M','I'],['K','L'],['L','N'],['N','K'],['M','O'],['O','N'],['M','N'],['Q','P'],['Q','O'],['P','O']].map(([a,b])=>{const k=''+v[a]+v[b];const jk=Object.keys(JOINT).find(x=>x.startsWith(k));return {code:k,label:jk?JOINT[jk]:'—'};});
|
||||
premiumHtml+=`<hr class="divider">`;
|
||||
premiumHtml+=`<div style="font-size:0.82em;font-weight:600;color:#5a4636;text-align:center;margin-bottom:6px;">✦ 12组人生联合码 ✦</div>`;
|
||||
premiumHtml+=`<div style="font-size:0.68em;color:#9b8a78;text-align:center;margin-bottom:8px;">相邻数字组合,解密你不同人生阶段的内在剧本</div>`;
|
||||
const ages=[
|
||||
{label:'21—40岁 · 工作和朋友',codes:joints.slice(0,4),desc:'由潜意识码主导,反映你在职场上升期和社交圈中的表现模式。'+(v.I+v.J+v.M>=10?'能量较强,事业心旺盛,社交活跃。':'能量内敛,更注重深度而非广度。')},
|
||||
{label:'41—60岁 · 儿女和下属',codes:joints.slice(4,8),desc:'由内心码和主性格主导,体现你对下一代和下级的培养方式及中年价值观。'+(v.K+v.L+v.N>=10?'有较强的责任感和支配欲。':'倾向于给予空间,放手让他人成长。')},
|
||||
{label:'61岁至晚年 · 晚年和家庭',codes:joints.slice(8,12),desc:'由外心码主导,展现你晚年的生活智慧和家庭关系的最终呈现。'+(v.M+v.N+v.O>=10?'晚年活跃,持续发挥影响力。':'晚年宁静,享受内在平和。')},
|
||||
];
|
||||
ages.forEach(ag=>{
|
||||
premiumHtml+=`<div class="joint-age">${ag.label}</div>`;
|
||||
premiumHtml+=`<div style="font-size:0.65em;color:#9b8a78;text-align:center;margin:-2px 0 6px;line-height:1.5;">${ag.desc}</div>`;
|
||||
premiumHtml+=`<div class="joint-grid">`;
|
||||
ag.codes.forEach(jc=>{premiumHtml+=`<div><span class="jc">${jc.code}</span> <span class="jm">${jc.label}</span></div>`;});
|
||||
premiumHtml+=`</div>`;
|
||||
});
|
||||
|
||||
const paid=localStorage.getItem('jiema_paid');
|
||||
const lockEl=document.getElementById('missingBlock');
|
||||
if(paid){
|
||||
lockEl.innerHTML=freeHtml+premiumHtml;
|
||||
}else{
|
||||
lockEl.innerHTML=freeHtml+'<div class="shuzi-lock" id="shuziLock">'+premiumHtml+'</div>';
|
||||
const lock=document.getElementById('shuziLock');
|
||||
Array.from(lock.children).forEach(c=>c.classList.add('blur'));
|
||||
const cover=document.createElement('div');cover.className='cover';
|
||||
cover.innerHTML='<div class="lk-t">🔒 解锁你的完整数字报告</div><div class="lk-d">天赋优势 · 能量分布 · 性格编码 · 12组联合码</div>';
|
||||
const btn=document.createElement('button');btn.className='lk-btn';btn.textContent='¥39.9 立即解锁 →';
|
||||
btn.addEventListener('click',unlockShuzi);cover.appendChild(btn);lock.appendChild(cover);
|
||||
}
|
||||
setTimeout(()=>{
|
||||
const canvas=document.querySelector('#scene3d canvas');
|
||||
if(canvas){renderScene(canvas,freq,rotZ);setupRotation();}
|
||||
},30);
|
||||
document.getElementById('missingMsg').textContent='';
|
||||
document.getElementById('codesBlock').innerHTML='';
|
||||
document.getElementById('jointBlock').innerHTML='';
|
||||
|
||||
const tipEl=document.getElementById('tip');
|
||||
['valR','valQ','valP','valO','valM','valN','valI','valJ','valK','valL','valU','valT','valS','valX','valV','valW'].forEach(id=>{
|
||||
const el=document.getElementById(id);if(!el)return;el.style.cursor='pointer';
|
||||
const k=id.replace('val','');
|
||||
el.onpointerenter=()=>{const f=tips[k]?tips[k](v):'';tipEl.innerHTML=f||k;tipEl.classList.add('on')};
|
||||
el.onpointermove=e=>{tipEl.style.left=(e.clientX+14)+'px';tipEl.style.top=(e.clientY-10)+'px'};
|
||||
});
|
||||
|
||||
// 填充其他解读面板
|
||||
document.getElementById('meihua-panel').innerHTML=meihuaHTML(y,m,d);
|
||||
document.getElementById('xiaoliuren-panel').innerHTML=xiaoLiuRenHTML(y,m,d);
|
||||
document.getElementById('daliuren-panel').innerHTML=daLiuRenHTML(y,m,d);
|
||||
document.getElementById('qimen-panel').innerHTML=qiMenHTML(y,m,d);
|
||||
document.getElementById('xingzuo-panel').innerHTML=xingZuoHTML(y,m,d);
|
||||
document.getElementById('wuxing-panel').innerHTML=wuXingHTML(y,m,d);
|
||||
}
|
||||
|
||||
function unlockShuzi(){localStorage.setItem("jiema_paid","1");const l=document.getElementById("shuziLock");l.querySelectorAll(".blur").forEach(c=>c.classList.remove("blur"));l.classList.add("unlocked");}
|
||||
function go(){const y=+document.getElementById('inY').value,m=+document.getElementById('inM').value,d=+document.getElementById('inD').value;if(!y||!m||!d)return;update(calc(y,m,d),y,m,d)}
|
||||
document.getElementById('goBtn').addEventListener('click',go);
|
||||
document.querySelectorAll('.input-row input').forEach(i=>i.addEventListener('keydown',e=>{if(e.key==='Enter')go()}));
|
||||
setTimeout(go,150);
|
||||
initTabs();
|
||||
|
||||
// URL 参数预填(从 jiema.html 跳转过来)
|
||||
(function(){
|
||||
const p=new URLSearchParams(location.search);
|
||||
const y=p.get('y'),m=p.get('m'),d=p.get('d');
|
||||
if(y&&m&&d){
|
||||
document.getElementById('inY').value=y;
|
||||
document.getElementById('inM').value=m;
|
||||
document.getElementById('inD').value=d;
|
||||
setTimeout(go,200);
|
||||
}
|
||||
})();
|
||||
+1089
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user