feat: 首页样式同步完成
This commit is contained in:
6
nkebao/postcss.config.js
Normal file
6
nkebao/postcss.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
@@ -1,3 +1,7 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
|
||||
@@ -1,5 +1,242 @@
|
||||
import React from 'react';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { Chart, registerables } from 'chart.js';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Smartphone, Users, Activity, Bell } from 'lucide-react';
|
||||
Chart.register(...registerables);
|
||||
|
||||
export default function Home() {
|
||||
return <div>首页</div>;
|
||||
const chartRef = useRef<HTMLCanvasElement>(null);
|
||||
const chartInstance = useRef<Chart | null>(null);
|
||||
const navigate = useNavigate();
|
||||
|
||||
// 统计数据状态
|
||||
const [stats, setStats] = useState({
|
||||
totalDevices: 0,
|
||||
onlineDevices: 0,
|
||||
totalWechatAccounts: 0,
|
||||
onlineWechatAccounts: 0,
|
||||
});
|
||||
|
||||
// 业务场景数据
|
||||
const scenarioFeatures = [
|
||||
{
|
||||
id: "douyin",
|
||||
name: "抖音获客",
|
||||
icon: "https://hebbkx1anhila5yf.public.blob.vercel-storage.com/image-QR8ManuDplYTySUJsY4mymiZkDYnQ9.png",
|
||||
color: "bg-blue-100 text-blue-600",
|
||||
value: 156,
|
||||
growth: 12,
|
||||
},
|
||||
{
|
||||
id: "xiaohongshu",
|
||||
name: "小红书获客",
|
||||
icon: "https://hebbkx1anhila5yf.public.blob.vercel-storage.com/image-yvnMxpoBUzcvEkr8DfvHgPHEo1kmQ3.png",
|
||||
color: "bg-red-100 text-red-600",
|
||||
value: 89,
|
||||
growth: 8,
|
||||
},
|
||||
{
|
||||
id: "gongzhonghao",
|
||||
name: "公众号获客",
|
||||
icon: "https://hebbkx1anhila5yf.public.blob.vercel-storage.com/image-Gsg0CMf5tsZb41mioszdjqU1WmsRxW.png",
|
||||
color: "bg-green-100 text-green-600",
|
||||
value: 234,
|
||||
growth: 15,
|
||||
},
|
||||
{
|
||||
id: "haibao",
|
||||
name: "海报获客",
|
||||
icon: "https://hebbkx1anhila5yf.public.blob.vercel-storage.com/image-x92XJgXy4MI7moNYlA1EAes2FqDxMH.png",
|
||||
color: "bg-orange-100 text-orange-600",
|
||||
value: 167,
|
||||
growth: 10,
|
||||
},
|
||||
];
|
||||
|
||||
// 获取统计数据
|
||||
useEffect(() => {
|
||||
const fetchStats = async () => {
|
||||
try {
|
||||
// 这里可以调用实际的API
|
||||
// const response = await fetch('/api/stats');
|
||||
// const data = await response.json();
|
||||
|
||||
// 模拟数据
|
||||
setStats({
|
||||
totalDevices: 42,
|
||||
onlineDevices: 35,
|
||||
totalWechatAccounts: 42,
|
||||
onlineWechatAccounts: 35,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('获取统计数据失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
fetchStats();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (chartRef.current) {
|
||||
if (chartInstance.current) chartInstance.current.destroy();
|
||||
chartInstance.current = new Chart(chartRef.current, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
|
||||
datasets: [
|
||||
{
|
||||
label: '获客数量',
|
||||
data: [120, 150, 180, 200, 230, 210, 190],
|
||||
backgroundColor: 'rgba(59, 130, 246, 0.2)',
|
||||
borderColor: 'rgba(59, 130, 246, 1)',
|
||||
borderWidth: 2,
|
||||
tension: 0.3,
|
||||
pointRadius: 4,
|
||||
pointBackgroundColor: 'rgba(59, 130, 246, 1)',
|
||||
pointHoverRadius: 6,
|
||||
},
|
||||
],
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false,
|
||||
},
|
||||
tooltip: {
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.9)',
|
||||
titleColor: '#333',
|
||||
bodyColor: '#666',
|
||||
borderColor: '#ddd',
|
||||
borderWidth: 1,
|
||||
padding: 10,
|
||||
displayColors: false,
|
||||
callbacks: {
|
||||
label: (context) => `获客数量: ${context.parsed.y}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
grid: {
|
||||
display: false,
|
||||
},
|
||||
},
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
grid: {
|
||||
color: 'rgba(0, 0, 0, 0.05)',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
return () => {
|
||||
if (chartInstance.current) chartInstance.current.destroy();
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleDevicesClick = () => {
|
||||
navigate('/devices');
|
||||
};
|
||||
|
||||
const handleWechatClick = () => {
|
||||
navigate('/wechat-accounts');
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex-1 overflow-y-auto pb-16 bg-gray-50">
|
||||
<header className="sticky top-0 z-10 bg-white border-b">
|
||||
<div className="flex justify-between items-center p-4">
|
||||
<h1 className="text-xl font-semibold text-blue-600">存客宝</h1>
|
||||
<button className="p-2 hover:bg-gray-100 rounded-full">
|
||||
<Bell className="h-5 w-5 text-gray-600" />
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div className="p-4 space-y-6">
|
||||
{/* 统计卡片 */}
|
||||
<div className="grid grid-cols-3 gap-4">
|
||||
<div
|
||||
className="p-4 bg-white hover:shadow-lg transition-all cursor-pointer rounded shadow"
|
||||
onClick={handleDevicesClick}
|
||||
>
|
||||
<div className="flex flex-col">
|
||||
<span className="text-sm text-gray-500 mb-2">设备数量</span>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-2xl font-bold text-blue-600">{stats.totalDevices}</span>
|
||||
<Smartphone className="w-6 h-6 text-blue-600" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="p-4 bg-white hover:shadow-lg transition-all cursor-pointer rounded shadow"
|
||||
onClick={handleWechatClick}
|
||||
>
|
||||
<div className="flex flex-col">
|
||||
<span className="text-sm text-gray-500 mb-2">微信号数量</span>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-2xl font-bold text-blue-600">{stats.totalWechatAccounts}</span>
|
||||
<Users className="w-6 h-6 text-blue-600" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-4 bg-white rounded shadow">
|
||||
<div className="flex flex-col">
|
||||
<span className="text-sm text-gray-500 mb-2">在线微信号</span>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<span className="text-2xl font-bold text-blue-600">{stats.onlineWechatAccounts}</span>
|
||||
<Activity className="w-6 h-6 text-blue-600" />
|
||||
</div>
|
||||
<div className="w-full bg-gray-200 rounded-full h-1">
|
||||
<div
|
||||
className="bg-blue-600 h-1 rounded-full"
|
||||
style={{ width: `${(stats.onlineWechatAccounts / stats.totalWechatAccounts) * 100}%` }}
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 场景获客统计 */}
|
||||
<div className="p-4 bg-white rounded shadow">
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<h2 className="text-lg font-semibold">场景获客统计</h2>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
{scenarioFeatures
|
||||
.sort((a, b) => b.value - a.value)
|
||||
.map((scenario) => (
|
||||
<div
|
||||
key={scenario.id}
|
||||
className="block flex-1 cursor-pointer"
|
||||
onClick={() => navigate(`/scenarios/${scenario.id}`)}
|
||||
>
|
||||
<div className="flex flex-col items-center text-center space-y-2">
|
||||
<div className={`w-12 h-12 rounded-full ${scenario.color} flex items-center justify-center`}>
|
||||
<img src={scenario.icon || "/placeholder.svg"} alt={scenario.name} className="w-6 h-6" />
|
||||
</div>
|
||||
<div className="text-sm font-medium">{scenario.value}</div>
|
||||
<div className="text-xs text-gray-500 whitespace-nowrap overflow-hidden text-ellipsis w-full">
|
||||
{scenario.name}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 每日获客趋势 */}
|
||||
<div className="p-4 bg-white rounded shadow">
|
||||
<h2 className="text-lg font-semibold mb-4">每日获客趋势</h2>
|
||||
<div className="w-full h-64 relative">
|
||||
<canvas ref={chartRef} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
10
nkebao/tailwind.config.js
Normal file
10
nkebao/tailwind.config.js
Normal file
@@ -0,0 +1,10 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: [
|
||||
"./src/**/*.{js,jsx,ts,tsx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
Reference in New Issue
Block a user