惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

aimingoo的专栏
aimingoo的专栏
B
Blog RSS Feed
Recent Announcements
Recent Announcements
Vercel News
Vercel News
M
MIT News - Artificial intelligence
阮一峰的网络日志
阮一峰的网络日志
L
LangChain Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Microsoft Security Blog
Microsoft Security Blog
H
Help Net Security
T
The Blog of Author Tim Ferriss
Y
Y Combinator Blog
G
Google Developers Blog
罗磊的独立博客
爱范儿
爱范儿
宝玉的分享
宝玉的分享
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
酷 壳 – CoolShell
酷 壳 – CoolShell
博客园_首页
S
SegmentFault 最新的问题
WordPress大学
WordPress大学
月光博客
月光博客
人人都是产品经理
人人都是产品经理
Apple Machine Learning Research
Apple Machine Learning Research

V2EX

我用 AI 写代码,但终端管理反而成了累赘——于是我做了 codux [调研] 各位在公司都用什么 ide 和 agent 写代码? 老运维 share 一个运维平台 看到有公司考核 token 指标,很好奇大家上个月的 AI 账单是多少 GLM-Coding 调用持续报错: z.ai 的 Lite 套餐几乎无法使用,官方 Pro/Max 是否稳定? 现在还有什么渠道可以稳定安全地使用 Claude 吗? 上海漕河泾内推,本组有 2 个 hc,一个后端,一个前端,预算都是 20k 左右,不打卡,氛围好 如果 V2EX 上有一组不永久保存聊天记录(比如只保存 7 天或者 24 小时)的聊天室,那么会开启哪些有用或者有趣的可能? gemini cli 貌似挂了,一直返回 403 第一次在自媒体上赚到钱 收集了最近在使用的低价 GPT, Gemini,邮箱等 AI 会员的小店合集 讨论个大实话:现在企业还在说 AI 编程提效 20%, 30%的,真的太落后,没用懂 AI。因为包括很多前沿公司,已经狂奔到提效 200%-500%的情况 [招聘][远程][币安] 前端/后端/QA/iOS/Android 至少 3 年以上经验 目前有大量 HC 欢迎投递 Chatgpt Pro 用量用不完的可以开这些设置 面试的时候好像遇到钓鱼了,给各位避个坑 cursor 年续费 22 号到期, 自动续费是否还是老的计次套餐呢 被两件破事毁掉的一下午,琐碎的内耗消磨人的精力 使用 Planet 存储 Codex 的会话或者重要信息 如果业务部门领导不要你开发功能,而是要求你教会它用 claude code 开发功能,你会怎么做? 分享一个 MacOS 接绿联 CM818 USB 转 DP 转接器使用感受 我的 HR 朋友 10 年老 Java ,非全大专,大家帮忙看看简历 开源了一个 AI 口语练习工具,音素级发音评分,完全免费可自部署 V2EX 上有哪些你觉得很有趣、印象深刻的妹纸? 字节为啥不出个国内版 Vercel? 有在大马的朋友吗? 问个运营商问题 你们在有领导的公司大群发过的最大胆的消息是什么 公司裁员,目前没有工作。想试试摆摊,做一个移动鲜啤打酒车 我的硬盘 Memblaze Pblaze 5 Linux 下不识别,给 Linux 内核提交了补丁, AI 说有望被合并 只有我一个人觉得 codex 不好用?
人生 K 线图,不要码,直接本地运行
Guidoo · 2025-12-16 · via V2EX

在桌面建一个 index.html 文件 把下面的代码复制进去用 chrome 打开就行了。

效果如下:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AI 命理 - 人生 K 线图</title>
    
    <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
    <link rel="stylesheet" href="https://unpkg.com/element-plus/dist/index.css" />
    <script src="https://unpkg.com/element-plus/dist/index.full.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/echarts.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/lunar.js"></script>

    <style>
        :root { --bg-color: #1a1a1a; --card-bg: #252525; --text-color: #e0e0e0; --gold: #d4af37; --accent: #409eff; }
        body { margin: 0; background-color: var(--bg-color); color: var(--text-color); font-family: 'PingFang SC', sans-serif; }
        .container { max-width: 1200px; margin: 0 auto; padding: 20px; }
        .header { text-align: center; margin-bottom: 20px; border-bottom: 1px solid #333; padding-bottom: 20px; }
        .header h1 { color: var(--gold); margin: 0; letter-spacing: 2px; }
        
        .config-panel { background: #331f00; border: 1px solid #5c4e2a; padding: 15px; border-radius: 8px; margin-bottom: 20px; }
        .control-panel { background: var(--card-bg); padding: 20px; border-radius: 8px; margin-bottom: 20px; box-shadow: 0 4px 12px rgba(0,0,0,0.3); }
        
        .bazi-result { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; text-align: center; margin-top: 20px; padding: 15px; background: #333; border-radius: 4px; }
        .pillar-box .ganzhi { font-size: 24px; color: var(--gold); font-weight: bold; }
        
        .chart-container { background: var(--card-bg); padding: 20px; border-radius: 8px; height: 550px; width: 100%; margin-bottom: 20px; position: relative; }
        
        .report-section { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-top: 20px; }
        @media (max-width: 768px) { .report-section { grid-template-columns: 1fr; } }
        .analysis-card { background: var(--card-bg); padding: 20px; border-radius: 8px; }
        
        .loading-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.85); z-index: 999; display: flex; flex-direction: column; justify-content: center; align-items: center; color: var(--gold); }
        .loading-text { margin-top: 15px; font-size: 16px; color: #fff; }
        .loading-sub { margin-top: 5px; font-size: 12px; color: #888; }
        
        /* 滚动条美化 */
        ::-webkit-scrollbar { width: 8px; height: 8px; }
        ::-webkit-scrollbar-thumb { background: #444; border-radius: 4px; }
        ::-webkit-scrollbar-track { background: #1a1a1a; }
    </style>
</head>
<body>
    <div id="app">
        <div class="container">
            <header class="header">
                <h1>AI 命理 - 人生 K 线图</h1>
                <p style="font-size: 12px; color: #666; margin-top: 5px;">Powered by Generative AI</p>
            </header>

            <div class="config-panel">
                <h4 style="margin:0 0 10px 0; color:var(--gold)">🛠️ 模型配置</h4>
                <el-form :inline="true" size="small">
                    <el-form-item label="API Base URL">
                        <el-input v-model="apiConfig.baseUrl" placeholder="例如 https://api.openai.com/v1" style="width: 250px"></el-input>
                    </el-form-item>
                    <el-form-item label="API Key">
                        <el-input v-model="apiConfig.apiKey" type="password" show-password placeholder="sk-..." style="width: 200px"></el-input>
                    </el-form-item>
                    <el-form-item label="模型名称">
                        <el-input v-model="apiConfig.model" placeholder="gpt-4o-mini / deepseek-chat" style="width: 150px"></el-input>
                    </el-form-item>
                </el-form>
            </div>

            <div class="control-panel">
                <el-form :inline="true" size="large">
                    <el-form-item label="出生日期">
                        <el-date-picker v-model="input.date" type="datetime" placeholder="选择出生时间" format="YYYY-MM-DD HH:mm"></el-date-picker>
                    </el-form-item>
                    <el-form-item label="性别">
                        <el-radio-group v-model="input.gender">
                            <el-radio-button label="1">男</el-radio-button>
                            <el-radio-button label="0">女</el-radio-button>
                        </el-radio-group>
                    </el-form-item>
                    <el-form-item>
                        <el-button type="primary" color="#d4af37" @click="handleGenerate" :disabled="loading">
                            {{ loading ? '正在连接天机...' : '启动真实推演' }}
                        </el-button>
                    </el-form-item>
                </el-form>

                <div v-if="baziData.year" class="bazi-result">
                    <div class="pillar-box"><div style="font-size:12px;color:#888">年柱</div><div class="ganzhi">{{ baziData.year }}</div></div>
                    <div class="pillar-box"><div style="font-size:12px;color:#888">月柱</div><div class="ganzhi">{{ baziData.month }}</div></div>
                    <div class="pillar-box"><div style="font-size:12px;color:#888">日柱</div><div class="ganzhi">{{ baziData.day }}</div></div>
                    <div class="pillar-box"><div style="font-size:12px;color:#888">时柱</div><div class="ganzhi">{{ baziData.time }}</div></div>
                    <div style="grid-column: span 4; margin-top: 10px; color: #ccc; font-size: 14px;">
                        {{ baziData.description }}
                    </div>
                </div>
            </div>

            <div v-show="chartVisible" class="chart-container" id="lifeChart"></div>

            <div v-if="reportData" class="report-section">
                <div class="analysis-card">
                    <h3 style="color:var(--gold); border-bottom:1px solid #444; padding-bottom:10px">
                        {{ reportData.patternType }} <span style="font-size:12px; float:right">格局评分: {{ reportData.baseLevelScore }}</span>
                    </h3>
                    <p style="color:#ccc; line-height:1.6; font-size: 15px;">{{ reportData.summary }}</p>
                    <el-divider border-style="dashed"></el-divider>
                    <div style="display:grid; grid-template-columns: 1fr 1fr; gap: 10px; font-size:14px;">
                        <div>🍀 喜神: <span style="color:var(--gold)">{{ (reportData.suggestions?.favorableDirections || []).join('、') }}</span></div>
                        <div>🎨 幸运色: <span style="color:var(--gold)">{{ (reportData.suggestions?.favorableColors || []).join('、') }}</span></div>
                        <div>🔢 数字: <span style="color:var(--gold)">{{ (reportData.suggestions?.favorableNumbers || []).join('、') }}</span></div>
                        <div>🤝 贵人: <span style="color:var(--gold)">{{ (reportData.suggestions?.noblePeople || []).join('、') }}</span></div>
                    </div>
                </div>
                <div class="analysis-card">
                    <h3 style="color:var(--gold); border-bottom:1px solid #444; padding-bottom:10px">深度剖析</h3>
                    <el-collapse accordion>
                        <el-collapse-item title="💰 财富机缘" name="1">{{ reportData.wealthAnalysis }}</el-collapse-item>
                        <el-collapse-item title="🚀 事业官运" name="2">{{ reportData.industryAnalysis }}</el-collapse-item>
                        <el-collapse-item title="❤️ 婚姻情感" name="3">{{ reportData.marriageAnalysis }}</el-collapse-item>
                        <el-collapse-item title="🏥 健康保养" name="4">{{ reportData.healthAnalysis }}</el-collapse-item>
                        <el-collapse-item title="🏠 六亲眷属" name="5">{{ reportData.familyAnalysis }}</el-collapse-item>
                    </el-collapse>
                </div>
            </div>
        </div>

        <div v-if="loading" class="loading-overlay">
            <div class="el-loading-spinner" style="margin-bottom: 20px;">
                <svg class="circular" viewBox="25 25 50 50"><circle class="path" cx="50" cy="50" r="20" fill="none"></circle></svg>
            </div>
            <div class="loading-text">正在沟通大模型 API...</div>
            <div class="loading-sub">生成 80 年数据量较大,请耐心等待 (约 20-60 秒)</div>
        </div>
    </div>

    <script>
        const { createApp, ref, reactive, nextTick } = Vue;

        createApp({
            setup() {
                const loading = ref(false);
                const chartVisible = ref(false);
                
                // 默认配置 (为了方便演示,预填了一些通用地址,但 Key 留空)
                const apiConfig = reactive({
                    baseUrl: localStorage.getItem('bazi_api_url') || 'https://api.openai.com/v1',
                    apiKey: localStorage.getItem('bazi_api_key') || '',
                    model: localStorage.getItem('bazi_model') || 'gpt-4o-mini'
                });

                const input = reactive({
                    date: new Date('1995-06-15 08:30'),
                    gender: '1'
                });

                const baziData = reactive({ year: '', month: '', day: '', time: '', description: '' });
                const reportData = ref(null);
                let myChart = null;

                // 1. 八字排盘
                const calculateBazi = () => {
                    const solar = Solar.fromDate(input.date);
                    const lunar = solar.getLunar();
                    const baZi = lunar.getEightChar();
                    baziData.year = baZi.getYear();
                    baziData.month = baZi.getMonth();
                    baziData.day = baZi.getDay();
                    baziData.time = baZi.getTime();
                    
                    const gender = input.gender === '1' ? 1 : 0;
                    const yun = baZi.getYun(gender);
                    baziData.description = `农历:${lunar.toString()} | ${yun.getStartYear()}岁起运 | ${input.gender === '1'?'乾造':'坤造'}`;
                    
                    return { baZi, yun, genderText: input.gender === '1'?'男':'女' };
                };

                // 2. 构建 Prompt
                const buildPrompt = (baziInfo) => {
                    return `你是一位世界顶级的八字命理大师。请根据以下信息进行流年推算:
                    性别:${baziInfo.genderText}
                    出生公历:${input.date.toLocaleString()}
                    八字:${baziInfo.baZi.toString()}
                    
                    任务:请生成该用户 1 岁 到 80 岁 的每一年的运势评分与简批。
                    
                    要求:
                    1. 返回严格的 JSON 格式,不要包含 Markdown 代码块标记(如 \`\`\`json )。
                    2. JSON 结构必须严格包含:
                       {
                         "patternType": "格局名称",
                         "baseLevelScore": 基础分(0-100),
                         "summary": "总评(100 字内)",
                         "wealthAnalysis": "财运分析",
                         "industryAnalysis": "事业分析",
                         "marriageAnalysis": "婚姻分析",
                         "healthAnalysis": "健康分析",
                         "familyAnalysis": "家庭分析",
                         "suggestions": { "favorableDirections": [], "favorableColors": [], "favorableNumbers": [], "noblePeople": [] },
                         "chartPoints": [
                            { "age": 1, "year": 1996, "daYun": "大运名", "score": 总分, "reason": "简短流年批语(30 字以内)", "scores": { "total": 分, "wealth": 分, "career": 分, "marriage": 分, "health": 分, "family": 分 } },
                            ... (一直到 80 岁)
                         ]
                       }
                    3. 评分逻辑:请务必根据八字喜用神、流年干支冲克关系进行科学打分,体现出人生的起伏波动,不要全部都是高分。
                    4. 流年批语(reason)要言简意赅,点出吉凶关键。
                    `;
                };

                // 3. 调用 LLM
                const callLLM = async (prompt) => {
                    if (!apiConfig.apiKey) throw new Error("请输入 API Key");

                    const response = await fetch(`${apiConfig.baseUrl}/chat/completions`, {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/json',
                            'Authorization': `Bearer ${apiConfig.apiKey}`
                        },
                        body: JSON.stringify({
                            model: apiConfig.model,
                            messages: [
                                { role: "system", content: "你是一个只输出 JSON 数据的八字算命程序。" },
                                { role: "user", content: prompt }
                            ],
                            temperature: 0.7
                        })
                    });

                    if (!response.ok) {
                        const err = await response.text();
                        throw new Error(`API 请求失败: ${response.status} - ${err}`);
                    }

                    const data = await response.json();
                    let content = data.choices[0].message.content;
                    
                    // 清洗数据:防止模型返回 Markdown 格式
                    content = content.replace(/```json/g, '').replace(/```/g, '').trim();
                    
                    return JSON.parse(content);
                };

                // 4. 渲染图表
                const renderChart = (data) => {
                    const chartDom = document.getElementById('lifeChart');
                    if (myChart) myChart.dispose();
                    myChart = echarts.init(chartDom);
                    
                    const option = {
                        backgroundColor: 'transparent',
                        title: { text: '人生八十年运势全景', left: 'center', textStyle: { color: '#d4af37' }, top: 10 },
                        tooltip: { 
                            trigger: 'axis',
                            backgroundColor: 'rgba(30,30,30,0.95)',
                            borderColor: '#d4af37',
                            textStyle: { color: '#fff' },
                            formatter: (params) => {
                                const p = data.chartPoints[params[0].dataIndex];
                                return `<div style="width:200px">
                                    <div style="color:#d4af37;font-weight:bold">${p.age}岁 (${p.year}) ${p.daYun}运</div>
                                    <div style="margin:5px 0;font-size:12px">${p.reason}</div>
                                    <div style="border-top:1px solid #555;padding-top:5px;display:flex;justify-content:space-between;font-size:12px">
                                        <span>💰${p.scores.wealth}</span><span>💼${p.scores.career}</span>
                                        <span>❤️${p.scores.marriage}</span><span>🏥${p.scores.health}</span>
                                    </div>
                                </div>`;
                            }
                        },
                        grid: { left: '3%', right: '4%', bottom: '10%', top: '15%', containLabel: true },
                        xAxis: { 
                            type: 'category', 
                            data: data.chartPoints.map(p => p.age),
                            axisLabel: { interval: 9, color: '#888' } 
                        },
                        yAxis: { type: 'value', min: 20, max: 100, splitLine: { lineStyle: { color: '#333' } } },
                        dataZoom: [{ type: 'inside', start: 0, end: 100 }, { start: 0, end: 100, bottom: 0 }],
                        series: [
                            {
                                name: '总运', type: 'line', smooth: 0.3,
                                data: data.chartPoints.map(p => p.scores.total),
                                lineStyle: { width: 3, color: '#d4af37' },
                                itemStyle: { color: '#d4af37' },
                                areaStyle: { color: new echarts.graphic.LinearGradient(0,0,0,1,[{offset:0,color:'rgba(212,175,55,0.4)'},{offset:1,color:'rgba(212,175,55,0)'}]) },
                                markLine: { data: [{ yAxis: 60, lineStyle: { color: '#666', type: 'dashed' } }] }
                            }
                        ]
                    };
                    myChart.setOption(option);
                    window.addEventListener('resize', () => myChart && myChart.resize());
                };

                const handleGenerate = async () => {
                    // 保存配置到本地,方便下次使用
                    localStorage.setItem('bazi_api_url', apiConfig.baseUrl);
                    localStorage.setItem('bazi_api_key', apiConfig.apiKey);
                    localStorage.setItem('bazi_model', apiConfig.model);

                    try {
                        loading.value = true;
                        chartVisible.value = false;
                        reportData.value = null;

                        const baziInfo = calculateBazi();
                        const prompt = buildPrompt(baziInfo);
                        
                        console.log("发送给 AI 的 Prompt:", prompt);
                        
                        // 真实调用
                        const result = await callLLM(prompt);
                        console.log("AI 返回数据:", result);
                        
                        reportData.value = result;
                        chartVisible.value = true;
                        await nextTick();
                        renderChart(result);

                    } catch (e) {
                        console.error(e);
                        alert(`错误: ${e.message}`);
                    } finally {
                        loading.value = false;
                    }
                };

                return { input, apiConfig, loading, baziData, reportData, chartVisible, handleGenerate };
            }
        }).use(ElementPlus).mount('#app');
    </script>
</body>
</html>