1 line
18 KiB
JSON
1 line
18 KiB
JSON
{"repo": "aaaa-zhen/petty", "files": {"petty.html": {"170a075a3c197e5fb51694bfb7cb4eb665586a6a": {"t": 1785809913.0674899, "n": 16606, "h": "1dea597a88b81824", "c": "<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>小题大做测量器</title>\n <script src=\"https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js\"></script>\n <script src=\"https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js\"></script>\n <script src=\"https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.23.5/babel.min.js\"></script>\n <script src=\"https://cdn.tailwindcss.com\"></script>\n <style>\n @keyframes fadeIn {\n from {\n opacity: 0;\n transform: translateY(20px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n }\n \n .animate-fadeIn {\n animation: fadeIn 0.5s ease-out;\n }\n \n .duration-1500 {\n transition-duration: 1.5s;\n }\n </style>\n</head>\n<body>\n <div id=\"root\"></div>\n\n <script type=\"text/babel\">\n const { useState } = React;\n\n // AlertCircle 图标组件\n const AlertCircle = ({ className }) => (\n <svg className={className} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"></circle>\n <line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"12\"></line>\n <line x1=\"12\" y1=\"16\" x2=\"12.01\" y2=\"16\"></line>\n </svg>\n );\n\n const PettinessMeter = () => {\n const [grievance, setGrievance] = useState('');\n const [loading, setLoading] = useState(false);\n const [result, setResult] = useState(null);\n const [error, setError] = useState('');\n const [displayScore, setDisplayScore] = useState(0);\n\n // 内置的 API 配置\n const API_URL = 'https://aihubmix.com/v1/messages';\n const API_KEY = 'sk-iouRBg8zeN5xCP8Z01935eD4EdE44a15A466Bb211a58E544';\n\n const exampleGrievances = [\n { label: \"呼吸声\", text: \"我室友呼吸声太大了\" },\n { label: \"开门礼貌\", text: \"我帮别人开门他们都不说谢谢\" },\n { label: \"微波鱼\", text: \"我同事总是用微波炉热鱼\" },\n { label: \"卫生纸\", text: \"他们把卫生纸装反了\" }\n ];\n\n const categories = [\n \"合理担忧\",\n \"正当抱怨\", \n \"开始小题大做\",\n \"相当小题大做\",\n \"小题大做巅峰\"\n ];\n\n // 调用 Anthropic API 分析抱怨\n const analyzeGrievance = async () => {\n if (!grievance.trim()) {\n setError('请输入您的抱怨内容!');\n return;\n }\n\n setLoading(true);\n setError('');\n\n try {\n const prompt = `请分析以下抱怨的\"小题大做\"程度,并给出0-100的分数以及建议。\n\n抱怨内容:\"${grievance}\"\n\n请以JSON格式返回结果,包含以下字段:\n- score: 0-100的整数分数(0表示完全合理,100表示极度小题大做)\n- category: 根据分数选择类别(0-20:\"合理担忧\", 21-40:\"正当抱怨\", 41-60:\"开始小题大做\", 61-80:\"相当小题大做\", 81-100:\"小题大做巅峰\")\n- judgment: 对这个抱怨的客观评价(30-50字)\n- advice: 给出的建议(30-50字)\n\n只返回JSON,不要其他内容。`;\n\n const response = await fetch(API_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': API_KEY,\n 'anthropic-version': '2023-06-01'\n },\n body: JSON.stringify({\n model: 'claude-sonnet-4-20250514',\n max_tokens: 300,\n messages: [{\n role: 'user',\n content: prompt\n }]\n })\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n throw new Error(errorData.error?.message || `API 调用失败: ${response.status}`);\n }\n\n const data = await response.json();\n const content = data.content[0].text;\n \n // 尝试解析JSON响应\n let analysisResult;\n try {\n analysisResult = JSON.parse(content);\n } catch (parseError) {\n // 如果直接解析失败,尝试提取JSON部分\n const jsonMatch = content.match(/\\{[\\s\\S]*\\}/);\n if (jsonMatch) {\n analysisResult = JSON.parse(jsonMatch[0]);\n } else {\n throw new Error('无法解析AI返回的分析结果');\n }\n }\n\n // 验证返回的数据结构\n if (!analysisResult.score || !analysisResult.category || !analysisResult.judgment || !analysisResult.advice) {\n throw new Error('AI返回的数据格式不完整');\n }\n\n setResult(analysisResult);\n animateScore(analysisResult.score);\n\n } catch (err) {\n console.error('API 调用错误:', err);\n setError(`分析失败: ${err.message}`);\n } finally {\n setLoading(false);\n }\n };\n\n const animateScore = (targetScore) => {\n const duration = 1500;\n const startTime = Date.now();\n const startScore = 0;\n\n const updateScore = () => {\n const now = Date.now();\n const progress = Math.min((now - startTime) / duration, 1);\n \n const easeOut = 1 - Math.pow(1 - progress, 3);\n const currentScore = Math.round(startScore + (targetScore - startScore) * easeOut);\n \n setDisplayScore(currentScore);\n \n if (progress < 1) {\n requestAnimationFrame(updateScore);\n }\n };\n \n requestAnimationFrame(updateScore);\n };\n\n const getGaugeFillDasharray = (score) => {\n const arcLength = 220;\n const fillLength = (score / 100) * arcLength;\n return `${fillLength} ${arcLength}`;\n };\n\n return (\n <div className=\"min-h-screen p-8\" style={{ backgroundColor: '#F4BE72' }}>\n <div className=\"max-w-2xl mx-auto\">\n <h1 className=\"text-5xl font-bold text-center mb-2\" style={{ color: '#45260C' }}>\n 你有多小题大做?\n </h1>\n <p className=\"text-center mb-8\" style={{ color: '#45260C' }}>\n 分享你的抱怨,让真正的AI来评判\n </p>\n\n <div className=\"bg-white rounded-2xl shadow-xl p-8\">\n {/* 仪表盘 */}\n <div className=\"flex flex-col items-center mb-8\">\n <div className=\"relative w-80 h-48 mb-4\">\n <svg viewBox=\"0 0 200 130\" className=\"w-full h-full\">\n <path\n d=\"M 30 100 A 70 70 0 0 1 170 100\"\n fill=\"none\"\n stroke=\"#E5E7EB\"\n strokeWidth=\"20\"\n strokeLinecap=\"round\"\n />\n \n <path\n d=\"M 30 100 A 70 70 0 0 1 170 100\"\n fill=\"none\"\n stroke=\"#E68600\"\n strokeWidth=\"20\"\n strokeLinecap=\"round\"\n strokeDasharray={getGaugeFillDasharray(displayScore)}\n style={{ transition: 'all 1.5s ease-out' }}\n />\n \n <text x=\"28\" y=\"125\" textAnchor=\"middle\" className=\"text-xs font-medium\" fill=\"#45260C\">0</text>\n <text x=\"100\" y=\"15\" textAnchor=\"middle\" className=\"text-xs font-medium\" fill=\"#45260C\">50</text>\n <text x=\"172\" y=\"125\" textAnchor=\"middle\" className=\"text-xs font-medium\" fill=\"#45260C\">100</text>\n </svg>\n \n <div className=\"absolute inset-0 flex items-center justify-center\">\n <div className=\"text-center mt-16\">\n <span className=\"text-5xl font-bold block\" style={{ color: '#E68600' }}>\n {result ? displayScore : 0}分\n </span>\n {result && (\n <span className=\"text-sm font-medium mt-1 block\" style={{ color: '#E68600' }}>\n {result.category}\n </span>\n )}\n </div>\n </div>\n </div>\n </div>\n\n {/* 输入区域 */}\n <div className=\"mb-6\">\n <label className=\"block text-lg font-semibold mb-3\" style={{ color: '#45260C' }}>\n 什么事情让你烦恼?\n </label>\n \n {!result ? (\n <>\n <textarea\n value={grievance}\n onChange={(e) => setGrievance(e.target.value)}\n placeholder=\"我室友把我留着的最后一片披萨吃了...\"\n className=\"w-full p-4 border-2 border-orange-200 rounded-lg focus:border-orange-500 focus:outline-none resize-none\"\n style={{ borderColor: 'rgba(230, 134, 0, 0.3)' }}\n rows={4}\n />\n \n <div className=\"mt-3\">\n <div className=\"flex flex-wrap gap-2\">\n {exampleGrievances.map((example, index) => (\n <button\n key={index}\n onClick={() => setGrievance(example.text)}\n className=\"px-3 py-1 text-sm border rounded-full transition-colors hover:bg-orange-50\"\n style={{ \n borderColor: '#E68600',\n color: '#45260C',\n backgroundColor: 'white'\n }}\n >\n {example.label}\n </button>\n ))}\n </div>\n </div>\n \n {error && (\n <div className=\"mt-3 flex items-center text-red-600\">\n <AlertCircle className=\"w-5 h-5 mr-2\" />\n <span>{error}</span>\n </div>\n )}\n\n <button\n onClick={analyzeGrievance}\n disabled={loading}\n className=\"mt-4 w-full py-4 rounded-lg font-bold text-white transition-all transform hover:scale-105\"\n style={{\n backgroundColor: loading ? '#F5A854' : '#E68600',\n }}\n >\n {loading ? '正在请求AI分析...' : '让AI分析我的小题大做程度!'}\n </button>\n </>\n ) : (\n <div className=\"bg-gray-50 rounded-lg p-4\">\n <p className=\"italic\" style={{ color: '#45260C' }}>\"{grievance}\"</p>\n </div>\n )}\n </div>\n\n {/* 结果区域 */}\n {result && (\n <div className=\"animate-fadeIn\">\n <div className=\"border-t border-gray-200 pt-6\">\n <div className=\"space-y-6\">\n <div>\n <p className=\"text-lg font-semibold mb-3\" style={{ color: '#45260C' }}>AI 分析结果</p>\n <div className=\"bg-orange-50 rounded-lg p-4\">\n <p style={{ color: '#45260C' }}>\n {result.judgment}\n </p>\n </div>\n </div>\n \n <div>\n <p className=\"text-lg font-semibold mb-3\" style={{ color: '#45260C' }}>AI 建议</p>\n <div className=\"bg-orange-100 rounded-lg p-4\">\n <p style={{ color: '#45260C' }}>\n {result.advice}\n </p>\n </div>\n </div>\n </div>\n\n <button\n onClick={() => {\n setGrievance('');\n setResult(null);\n setDisplayScore(0);\n setError('');\n }}\n className=\"mt-6 w-full py-3 border-2 rounded-lg font-semibold transition-colors hover:bg-orange-50\"\n style={{ \n borderColor: '#E68600', \n color: '#E68600' \n }}\n >\n 再试一个抱怨\n </button>\n </div>\n </div>\n )}\n </div>\n </div>\n </div>\n );\n };\n\n ReactDOM.render(<PettinessMeter />, document.getElementById('root'));\n </script>\n</body>\n</html>"}}}} |