```json
{
"summary": "我将为图表组件添加真实测试数据,使用SVG绘制四种不同类型的图表(柱状图、折线图、饼图和混合图),展示完整的可视化效果。",
"widget": {
"type": "custom-template",
"isAPI": false,
"fullWidth": true,
"html": "<div class=\"bg-white rounded-xl shadow-md border border-solid border-gray-200 p-6\">\n <div class=\"flex flex-col md:flex-row justify-between items-start md:items-center mb-6 gap-4\">\n <div>\n <h2 class=\"text-2xl font-bold text-gray-800\">2023年销售数据</h2>\n <p class=\"text-gray-500\">单位:万元</p>\n </div>\n \n <div class=\"flex flex-wrap gap-2\">\n <div class=\"inline-flex rounded-md shadow-sm\" role=\"group\">\n <button type=\"button\" class=\"px-3 py-2 text-sm font-medium rounded-l-lg border border-solid border-indigo-500 bg-indigo-50 text-indigo-700 hover:bg-indigo-100 focus:z-10 focus:ring-2 focus:ring-indigo-500 chart-type-btn active\" data-type=\"bar\">\n <svg class=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z\"></path>\n </svg>\n </button>\n <button type=\"button\" class=\"px-3 py-2 text-sm font-medium border-t border-b border-solid border-gray-300 bg-white text-gray-700 hover:bg-gray-50 focus:z-10 focus:ring-2 focus:ring-indigo-500 chart-type-btn\" data-type=\"line\">\n <svg class=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M7 12l5-5m0 0l5 5m-5-5v12m10-5a9 9 0 11-18 0 9 9 0 0118 0z\"></path>\n </svg>\n </button>\n <button type=\"button\" class=\"px-3 py-2 text-sm font-medium border border-solid border-gray-300 bg-white text-gray-700 hover:bg-gray-50 focus:z-10 focus:ring-2 focus:ring-indigo-500 chart-type-btn\" data-type=\"pie\">\n <svg class=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M11 3.055A9.001 9.001 0 1020.945 13H11V3.055z\"></path>\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M20.488 9H15V3.512A9.025 9.025 0 0120.488 9z\"></path>\n </svg>\n </button>\n <button type=\"button\" class=\"px-3 py-2 text-sm font-medium rounded-r-lg border border-solid border-gray-300 bg-white text-gray-700 hover:bg-gray-50 focus:z-10 focus:ring-2 focus:ring-indigo-500 chart-type-btn\" data-type=\"mixed\">\n <svg class=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 6h16M4 10h16M4 14h16M4 18h16\"></path>\n </svg>\n </button>\n </div>\n </div>\n </div>\n \n <div class=\"relative h-96 bg-gray-50 rounded-lg border border-solid border-gray-200 overflow-hidden\" id=\"chart-container\">\n <!-- 默认显示柱状图 -->\n <svg class=\"w-full h-full\" viewBox=\"0 0 800 400\" preserveAspectRatio=\"xMidYMid meet\">\n <!-- X轴 -->\n <line x1=\"50\" y1=\"350\" x2=\"750\" y2=\"350\" stroke=\"#6b7280\" stroke-width=\"1\" />\n \n <!-- Y轴 -->\n <line x1=\"50\" y1=\"350\" x2=\"50\" y2=\"50\" stroke=\"#6b7280\" stroke-width=\"1\" />\n \n <!-- Y轴刻度 -->\n <text x=\"30\" y=\"60\" font-size=\"12\" fill=\"#6b7280\" text-anchor=\"end\">120</text>\n <text x=\"30\" y=\"110\" font-size=\"12\" fill=\"#6b7280\" text-anchor=\"end\">90</text>\n <text x=\"30\" y=\"160\" font-size=\"12\" fill=\"#6b7280\" text-anchor=\"end\">60</text>\n <text x=\"30\" y=\"210\" font-size=\"12\" fill=\"#6b7280\" text-anchor=\"end\">30</text>\n <text x=\"30\" y=\"260\" font-size=\"12\" fill=\"#6b7280\" text-anchor=\"end\">0</text>\n \n <!-- 月份标签 -->\n <text x=\"100\" y=\"370\" font-size=\"12\" fill=\"#6b7280\" text-anchor=\"middle\">1月</text>\n <text x=\"200\" y=\"370\" font-size=\"12\" fill=\"#6b7280\" text-anchor=\"middle\">2月</text>\n <text x=\"300\" y=\"370\" font-size=\"12\" fill=\"#6b7280\" text-anchor=\"middle\">3月</text>\n <text x=\"400\" y=\"370\" font-size=\"12\" fill=\"#6b7280\" text-anchor=\"middle\">4月</text>\n <text x=\"500\" y=\"370\" font-size=\"12\" fill=\"#6b7280\" text-anchor=\"middle\">5月</text>\n <text x=\"600\" y=\"370\" font-size=\"12\" fill=\"#6b7280\" text-anchor=\"middle\">6月</text>\n <text x=\"700\" y=\"370\" font-size=\"12\" fill=\"#6b7280\" text-anchor=\"middle\">7月</text>\n \n <!-- 柱状图数据 -->\n <rect x=\"80\" y=\"230\" width=\"40\" height=\"120\" fill=\"#4f46e5\" rx=\"2\" />\n <rect x=\"180\" y=\"270\" width=\"40\" height=\"80\" fill=\"#4f46e5\" rx=\"2\" />\n <rect x=\"280\" y=\"190\" width=\"40\" height=\"160\" fill=\"#4f46e5\" rx=\"2\" />\n <rect x=\"380\" y=\"150\" width=\"40\" height=\"200\" fill=\"#4f46e5\" rx=\"2\" />\n <rect x=\"480\" y=\"110\" width=\"40\" height=\"240\" fill=\"#4f46e5\" rx=\"2\" />\n <rect x=\"580\" y=\"170\" width=\"40\" height=\"180\" fill=\"#4f46e5\" rx=\"2\" />\n <rect x=\"680\" y=\"210\" width=\"40\" height=\"140\" fill=\"#4f46e5\" rx=\"2\" />\n \n <!-- 数据标签 -->\n <text x=\"100\" y=\"220\" font-size=\"12\" fill=\"#374151\" text-anchor=\"middle\">80</text>\n <text x=\"200\" y=\"260\" font-size=\"12\" fill=\"#374151\" text-anchor=\"middle\">60</text>\n <text x=\"300\" y=\"180\" font-size=\"12\" fill=\"#374151\" text-anchor=\"middle\">100</text>\n <text x=\"400\" y=\"140\" font-size=\"12\" fill=\"#374151\" text-anchor=\"middle\">120</text>\n <text x=\"500\" y=\"100\" font-size=\"12\" fill=\"#374151\" text-anchor=\"middle\">140</text>\n <text x=\"600\" y=\"160\" font-size=\"12\" fill=\"#374151\" text-anchor=\"middle\">110</text>\n <text x=\"700\" y=\"200\" font-size=\"12\" fill=\"#374151\" text-anchor=\"middle\">90</text>\n </svg>\n </div>\n \n <div class=\"mt-4 flex flex-wrap justify-between items-center\">\n <div class=\"flex space-x-2\">\n <span class=\"inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800\">\n 数据点: 7\n </span>\n <span class=\"inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800\">\n 更新于: 2023-07-31\n </span>\n </div>\n \n <div class=\"flex items-center space-x-4 mt-2 sm:mt-0\">\n <div class=\"flex items-center\">\n <span class=\"text-sm text-gray-500 mr-2\">时间范围:</span>\n <div class=\"relative\">\n <select class=\"appearance-none bg-white border border-solid border-gray-300 rounded-md pl-3 pr-8 py-1 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500\">\n <option selected>2023上半年</option>\n <option>2022全年</option>\n <option>近三年</option>\n </select>\n </div>\n </div>\n </div>\n </div>\n \n <script>\n const chartData = {\n bar: {\n title: '2023年月销售额(柱状图)',\n data: [80, 60, 100, 120, 140, 110, 90],\n labels: ['1月', '2月', '3月', '4月', '5月', '6月', '7月'],\n color: '#4f46e5'\n },\n line: {\n title: '2023年销售增长率(折线图)',\n data: [5, 15, 8, 20, 25, 18, 10],\n labels: ['1月', '2月', '3月', '4月', '5月', '6月', '7月'],\n color: '#10b981'\n },\n pie: {\n title: '2023年产品销售额占比(饼图)',\n data: [35, 25, 20, 15, 5],\n labels: ['产品A', '产品B', '产品C', '产品D', '产品E'],\n colors: ['#4f46e5', '#818cf8', '#c7d2fe', '#e0e7ff', '#f5f3ff']\n },\n mixed: {\n title: '2023年销售数据对比(混合图)',\n barData: [80, 60, 100, 120, 140, 110, 90],\n lineData: [5, 15, 8, 20, 25, 18, 10],\n labels: ['1月', '2月', '3月', '4月', '5月', '6月', '7月'],\n barColor: '#4f46e5',\n lineColor: '#10b981'\n }\n };\n \n function renderBarChart() {\n const { data, labels, color } = chartData.bar;\n const maxValue = Math.max(...data);\n const svg = document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\");\n svg.setAttribute(\"viewBox\", \"0 0 800 400\");\n svg.setAttribute(\"preserveAspectRatio\", \"xMidYMid meet\");\n svg.classList.add(\"w-full\", \"h-full\");\n \n // 添加X轴和Y轴\n svg.innerHTML = `\n <line x1=\"50\" y1=\"350\" x2=\"750\" y2=\"350\" stroke=\"#6b7280\" stroke-width=\"1\" />\n <line x1=\"50\" y1=\"350\" x2=\"50\" y2=\"50\" stroke=\"#6b7280\" stroke-width=\"1\" />\n <text x=\"30\" y=\"60\" font-size=\"12\" fill=\"#6b7280\" text-anchor=\"end\">${maxValue}</text>\n <text x=\"30\" y=\"210\" font-size=\"12\" fill=\"#6b7280\" text-anchor=\"end\">${Math.floor(maxValue/2)}</text>\n <text x=\"30\" y=\"350\" font-size=\"12\" fill=\"#6b7280\" text-anchor=\"end\">0</text>\n `;\n \n // 添加柱子和标签\n labels.forEach((label, i) => {\n const x = 100 + i * 100;\n const height = (data[i] / maxValue) * 280;\n const y = 350 - height;\n \n svg.innerHTML += `\n <rect x=\"${x-20}\" y=\"${y}\" width=\"40\" height=\"${height}\" fill=\"${color}\" rx=\"2\" />\n <text x=\"${x}\" y=\"370\" font-size=\"12\" fill=\"#6b7280\" text-anchor=\"middle\">${label}</text>\n <text x=\"${x}\" y=\"${y-10}\" font-size=\"12\" fill=\"#374151\" text-anchor=\"middle\">${data[i]}</text>\n `;\n });\n \n document.getElementById('chart-container').innerHTML = '';\n document.getElementById('chart-container').appendChild(svg);\n }\n \n function renderLineChart() {\n const { data, labels, color } = chartData.line;\n const maxValue = Math.max(...data);\n const points = data.map((value, i) => {\n const x = 100 + i * 100;\n const y = 350 - (value / maxValue) * 300;\n return `${x},${y}`;\n }).join(' ');\n \n const svg = document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\");\n svg.setAttribute(\"viewBox\", \"0 0 800 400\");\n svg.setAttribute(\"preserveAspectRatio\", \"xMidYMid meet\");\n svg.classList.add(\"w-full\", \"h-full\");\n \n svg.innerHTML = `\n <line x1=\"50\" y1=\"350\" x2=\"750\" y2=\"350\" stroke=\"#6b7280\" stroke-width=\"1\" />\n <line x1=\"50\" y1=\"350\" x2=\"50\" y2=\"50\" stroke=\"#6b7280\" stroke-width=\"1\" />\n <text x=\"30\" y=\"60\" font-size=\"12\" fill=\"#6b7280\" text-anchor=\"end\">${maxValue}%</text>\n <text x=\"30\" y=\"210\" font-size=\"12\" fill=\"#6b7280\" text-anchor=\"end\">${Math.floor(maxValue/2)}%</text>\n <text x=\"30\" y=\"350\" font-size=\"12\" fill=\"#6b7280\" text-anchor=\"end\">0%</text>\n \n <polyline points=\"${points}\" fill=\"none\" stroke=\"${color}\" stroke-width=\"3\" stroke-linejoin=\"round\" />\n `;\n \n // 添加数据点和标签\n data.forEach((value, i) => {\n const x = 100 + i * 100;\n const y = 350 - (value / maxValue) * 300;\n \n svg.innerHTML += `\n <circle cx=\"${x}\" cy=\"${y}\" r=\"4\" fill=\"${color}\" stroke=\"white\" stroke-width=\"2\" />\n <text x=\"${x}\" y=\"${y-15}\" font-size=\"12\" fill=\"#374151\" text-anchor=\"middle\">${value}%</text>\n <text x=\"${x}\" y=\"370\" font-size=\"12\" fill=\"#6b7280\" text-anchor=\"middle\">${labels[i]}</text>\n `;\n });\n \n document.getElementById('chart-container').innerHTML = '';\n document.getElementById('chart-container').appendChild(svg);\n }\n \n function renderPieChart() {\n const { data, labels, colors } = chartData.pie;\n const total = data.reduce((sum, value) => sum + value, 0);\n let cumulativeAngle = 0;\n \n const svg = document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\");\n svg.setAttribute(\"viewBox\", \"0 0 800 400\");\n svg.setAttribute(\"preserveAspectRatio\", \"xMidYMid meet\");\n svg.classList.add(\"w-full\", \"h-full\");\n \n // 添加饼图\n data.forEach((value, i) => {\n const percentage = value / total;\n const angle = percentage * 360;\n const largeArcFlag = angle > 180 ? 1 : 0;\n \n const x1 = 400 + 150 * Math.cos(cumulativeAngle * Math.PI / 180);\n const y1 = 200 + 150 * Math.sin(cumulativeAngle * Math.PI / 180);\n const x2 = 400 + 150 * Math.cos((cumulativeAngle + angle) * Math.PI / 180);\n const y2 = 200 + 150 * Math.sin((cumulativeAngle + angle) * Math.PI / 180);\n \n svg.innerHTML += `\n <path d=\"M400,200 L${x1},${y1} A150,150 0 ${largeArcFlag},1 ${x2},${y2} Z\" fill=\"${colors[i]}\" />\n `;\n \n // 添加图例\n const legendX = 600;\n const legendY = 100 + i * 30;\n svg.innerHTML += `\n <rect x=\"${legendX}\" y=\"${legendY-10}\" width=\"12\" height=\"12\" fill=\"${colors[i]}\" rx=\"2\" />\n <text x=\"${legendX+20}\" y=\"${legendY}\" font-size=\"12\" fill=\"#374151\">${labels[i]} (${Math.round(percentage*100)}%)</text>\n `;\n \n cumulativeAngle += angle;\n });\n \n // 添加中心圆\n svg.innerHTML += `<circle cx=\"400\" cy=\"200\" r=\"60\" fill=\"white\" />`;\n svg.innerHTML += `<text x=\"400\" y=\"205\" font-size=\"14\" fill=\"#4f46e5\" text-anchor=\"middle\" font-weight=\"bold\">总销售额</text>`;\n svg.innerHTML += `<text x=\"400\" y=\"225\" font-size=\"12\" fill=\"#6b7280\" text-anchor=\"middle\">${total}万元</text>`;\n \n document.getElementById('chart-container').innerHTML = '';\n document.getElementById('chart-container').appendChild(svg);\n }\n \n function renderMixedChart() {\n const { barData, lineData, labels, barColor, lineColor } = chartData.mixed;\n const maxBarValue = Math.max(...barData);\n const maxLineValue = Math.max(...lineData);\n \n const svg = document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\");\n svg.setAttribute(\"viewBox\", \"0 0 800 400\");\n svg.setAttribute(\"preserveAspectRatio\", \"xMidYMid meet\");\n svg.classList.add(\"w-full\", \"h-full\");\n \n // 添加轴和刻度\n svg.innerHTML = `\n <line x1=\"50\" y1=\"350\" x2=\"750\" y2=\"350\" stroke=\"#6b7280\" stroke-width=\"1\" />\n <line x1=\"50\" y1=\"350\" x2=\"50\" y2=\"50\" stroke=\"#6b7280\" stroke-width=\"1\" />\n \n <!-- 左侧Y轴(柱状图) -->\n <text x=\"30\" y=\"60\" font-size=\"12\" fill=\"#6b7280\" text-anchor=\"end\">${maxBarValue}</text>\n <text x=\"30\" y=\"210\" font-size=\"12\" fill=\"#6b7280\" text-anchor=\"end\">${Math.floor(maxBarValue/2)}</text>\n <text x=\"30\" y=\"350\" font-size=\"12\" fill=\"#6b7280\" text-anchor=\"end\">0</text>\n \n <!-- 右侧Y轴(折线图) -->\n <line x1=\"750\" y1=\"350\" x2=\"750\" y2=\"50\" stroke=\"#6b7280\" stroke-width=\"1\" />\n <text x=\"770\" y=\"60\" font-size=\"12\" fill=\"#6b7280\" text-anchor=\"start\">${maxLineValue}%</text>\n <text x=\"770\" y=\"210\" font-size=\"12\" fill=\"#6b7280\" text-anchor=\"start\">${Math.floor(maxLineValue/2)}%</text>\n <text x=\"770\" y=\"350\" font-size=\"12\" fill=\"#6b7280\" text-anchor=\"start\">0%</text>\n `;\n \n // 添加柱状图\n barData.forEach((value, i) => {\n const x = 100 + i * 100;\n const height = (value / maxBarValue) * 280;\n const y = 350 - height;\n \n svg.innerHTML += `\n <rect x=\"${x-20}\" y=\"${y}\" width=\"40\" height=\"${height}\" fill=\"${barColor}\" rx=\"2\" />\n <text x=\"${x}\" y=\"${y-10}\" font-size=\"12\" fill=\"#374151\" text-anchor=\"middle\">${value}</text>\n `;\n });\n \n // 添加折线图\n const linePoints = lineData.map((value, i) => {\n const x = 100 + i * 100;\n const y = 350 - (value / maxLineValue) * 300;\n return `${x},${y}`;\n }).join(' ');\n \n svg.innerHTML += `\n <polyline points=\"${linePoints}\" fill=\"none\" stroke=\"${lineColor}\" stroke-width=\"3\" stroke-linejoin=\"round\" />\n `;\n \n // 添加折线图数据点和月份标签\n lineData.forEach((value, i) => {\n const x = 100 + i * 100;\n const y = 350 - (value / maxLineValue) * 300;\n \n svg.innerHTML += `\n <circle cx=\"${x}\" cy=\"${y}\" r=\"4\" fill=\"${lineColor}\" stroke=\"white\" stroke-width=\"2\" />\n <text x=\"${x}\" y=\"${y-15}\" font-size=\"12\" fill=\"#374151\" text-anchor=\"middle\">${value}%</text>\n <text x=\"${x}\" y=\"370\" font-size=\"12\" fill=\"#6b7280\" text-anchor=\"middle\">${labels[i]}</text>\n `;\n });\n \n // 添加图例\n svg.innerHTML += `\n <rect x=\"600\" y=\"30\" width=\"12\" height=\"12\" fill=\"${barColor}\" rx=\"2\" />\n <text x=\"620\" y=\"40\" font-size=\"12\" fill=\"#374151\">销售额(万元)</text>\n \n <circle cx=\"600\" cy=\"60\" r=\"6\" fill=\"${lineColor}\" stroke=\"white\" stroke-width=\"2\" />\n <text x=\"620\" y=\"60\" font-size=\"12\" fill=\"#374151\">增长率(%)</text>\n `;\n \n document.getElementById('chart-container').innerHTML = '';\n document.getElementById('chart-container').appendChild(svg);\n }\n \n // 初始化显示柱状图\n renderBarChart();\n \n // 添加按钮点击事件\n document.querySelectorAll('.chart-type-btn').forEach(btn => {\n btn.addEventListener('click', function() {\n // 更新按钮状态\n document.querySelectorAll('.chart-type-btn').forEach(b => {\n b.classList.remove('bg-indigo-50', 'text-indigo-700', 'border-indigo-500', 'active');\n b.classList.add('bg-white', 'text-gray-700', 'border-gray-300');\n });\n \n this.classList.add('bg-indigo-50', 'text-indigo-700', 'border-indigo-500', 'active');\n this.classList.remove('bg-white', 'text-gray-700', 'border-gray-300');\n \n // 根据类型渲染对应图表\n const chartType = this.getAttribute('data-type');\n switch(chartType) {\n case 'bar':\n renderBarChart();\n break;\n case 'line':\n renderLineChart();\n break;\n case 'pie':\n renderPieChart();\n break;\n case 'mixed':\n renderMixedChart();\n break;\n }\n });\n });\n </script>\n</div>"
},
"next": [
"添加图表动画",
"实现数据更新",
"优化响应式",
"添加导出功能",
"添加工具提示"
]
}
```
角色
assistant
content
total_tokens
14717