<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>React 渲染与提交学习实例</title>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
</head>
<body class="bg-gradient-to-br from-blue-50 to-indigo-100 min-h-screen">
<div id="root" class="container mx-auto px-4 py-8"></div>
<script type="text/babel">
const { useState, useEffect } = React;
// 学习记录项组件
const LearningItem = ({ item, onDelete, onEdit }) => {
const [isEditing, setIsEditing] = useState(false);
const [editValue, setEditValue] = useState(item.text);
const handleSave = () => {
if (editValue.trim()) {
onEdit(item.id, editValue);
setIsEditing(false);
}
};
return (
<div className="bg-white rounded-xl shadow-md p-4 mb-3 transition-all duration-300 hover:shadow-lg">
<div className="flex items-start">
<div className="flex-1">
{isEditing ? (
<div className="flex flex-col space-y-2">
<input
type="text"
value={editValue}
onChange={(e) => setEditValue(e.target.value)}
className="border-b-2 border-indigo-300 focus:border-indigo-500 outline-none pb-1 w-full"
autoFocus
/>
<div className="flex space-x-2 pt-2">
<button
onClick={handleSave}
className="px-3 py-1 bg-green-500 text-white rounded-lg text-sm hover:bg-green-600 transition-colors"
>
保存
</button>
<button
onClick={() => setIsEditing(false)}
className="px-3 py-1 bg-gray-300 text-gray-700 rounded-lg text-sm hover:bg-gray-400 transition-colors"
>
取消
</button>
</div>
</div>
) : (
<div>
<p className="text-gray-800">{item.text}</p>
<p className="text-xs text-gray-500 mt-1">
<i className="far fa-clock mr-1"></i>
{new Date(item.timestamp).toLocaleString('zh-CN')}
</p>
</div>
)}
</div>
{!isEditing && (
<div className="flex space-x-2 ml-2">
<button
onClick={() => setIsEditing(true)}
className="text-blue-500 hover:text-blue-700 transition-colors"
title="编辑"
>
<i className="fas fa-edit"></i>
</button>
<button
onClick={() => onDelete(item.id)}
className="text-red-500 hover:text-red-700 transition-colors"
title="删除"
>
<i className="fas fa-trash-alt"></i>
</button>
</div>
)}
</div>
</div>
);
};
// 主应用组件
const App = () => {
const [inputValue, setInputValue] = useState('');
const [items, setItems] = useState([]);
const [filter, setFilter] = useState('all');
const [searchTerm, setSearchTerm] = useState('');
// 从本地存储加载数据
useEffect(() => {
const savedItems = localStorage.getItem('learningItems');
if (savedItems) {
setItems(JSON.parse(savedItems));
}
}, []);
// 保存数据到本地存储
useEffect(() => {
localStorage.setItem('learningItems', JSON.stringify(items));
}, [items]);
// 提交新学习记录
const handleSubmit = (e) => {
e.preventDefault();
if (inputValue.trim()) {
const newItem = {
id: Date.now(),
text: inputValue.trim(),
timestamp: new Date().toISOString()
};
setItems([newItem, ...items]);
setInputValue('');
}
};
// 删除学习记录
const handleDelete = (id) => {
setItems(items.filter(item => item.id !== id));
};
// 编辑学习记录
const handleEdit = (id, newText) => {
setItems(items.map(item =>
item.id === id ? {...item, text: newText} : item
));
};
// 清空所有记录
const handleClearAll = () => {
if (window.confirm('确定要清空所有学习记录吗?')) {
setItems([]);
}
};
// 过滤和搜索逻辑
const filteredItems = items.filter(item => {
const matchesSearch = item.text.toLowerCase().includes(searchTerm.toLowerCase());
return matchesSearch;
});
const completedCount = items.length;
const todayCount = items.filter(item =>
new Date(item.timestamp).toDateString() === new Date().toDateString()
).length;
return (
<div className="max-w-2xl mx-auto">
{/* 头部 */}
<header className="text-center mb-10">
<h1 className="text-4xl font-bold text-indigo-700 mb-2">
<i className="fas fa-book-reader mr-3"></i>学习记录系统
</h1>
<p className="text-gray-600">记录你的每一个学习瞬间</p>
</header>
{/* 统计卡片 */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-8">
<div className="bg-gradient-to-r from-indigo-500 to-purple-600 rounded-xl p-5 text-white shadow-lg">
<div className="flex items-center">
<i className="fas fa-tasks text-3xl mr-4"></i>
<div>
<p className="text-sm opacity-80">总计记录</p>
<p className="text-3xl font-bold">{completedCount}</p>
</div>
</div>
</div>
<div className="bg-gradient-to-r from-green-500 to-blue-600 rounded-xl p-5 text-white shadow-lg">
<div className="flex items-center">
<i className="fas fa-calendar-day text-3xl mr-4"></i>
<div>
<p className="text-sm opacity-80">今日新增</p>
<p className="text-3xl font-bold">{todayCount}</p>
</div>
</div>
</div>
</div>
{/* 输入表单 */}
<div className="bg-white rounded-2xl shadow-xl p-6 mb-8">
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<label htmlFor="learningInput" className="block text-lg font-medium text-gray-700 mb-2">
添加新的学习记录
</label>
<div className="flex space-x-2">
<input
id="learningInput"
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
placeholder="今天学到了什么?"
className="flex-1 px-4 py-3 border-2 border-gray-200 rounded-xl focus:border-indigo-500 focus:outline-none transition-colors"
/>
<button
type="submit"
disabled={!inputValue.trim()}
className={`px-6 py-3 rounded-xl font-semibold transition-all ${
inputValue.trim()
? 'bg-indigo-600 text-white hover:bg-indigo-700 transform hover:-translate-y-0.5 shadow-md hover:shadow-lg'
: 'bg-gray-300 text-gray-500 cursor-not-allowed'
}`}
>
<i className="fas fa-plus-circle mr-2"></i>提交
</button>
</div>
</div>
</form>
</div>
{/* 控制面板 */}
<div className="bg-white rounded-2xl shadow-xl p-6 mb-8">
<div className="flex flex-col md:flex-row md:items-center md:justify-between space-y-4 md:space-y-0">
<div className="flex items-center space-x-4">
<span className="text-gray-700 font-medium">搜索:</span>
<div className="relative">
<input
type="text"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="查找学习记录..."
className="pl-10 pr-4 py-2 border-2 border-gray-200 rounded-lg focus:border-indigo-500 focus:outline-none w-full md:w-64"
/>
<i className="fas fa-search absolute left-3 top-3 text-gray-400"></i>
</div>
</div>
<div className="flex items-center space-x-4">
<span className="text-gray-700 font-medium">操作:</span>
<button
onClick={handleClearAll}
disabled={items.length === 0}
className={`px-4 py-2 rounded-lg flex items-center ${
items.length > 0
? 'bg-red-500 text-white hover:bg-red-600'
: 'bg-gray-300 text-gray-500 cursor-not-allowed'
}`}
>
<i className="fas fa-trash-alt mr-2"></i>清空全部
</button>
</div>
</div>
</div>
{/* 学习记录列表 */}
<div className="bg-white rounded-2xl shadow-xl p-6">
<div className="flex items-center justify-between mb-6">
<h2 className="text-2xl font-bold text-gray-800">
<i className="fas fa-list mr-2"></i>学习记录列表
</h2>
<span className="bg-indigo-100 text-indigo-800 px-3 py-1 rounded-full text-sm">
共 {filteredItems.length} 条记录
</span>
</div>
{filteredItems.length === 0 ? (
<div className="text-center py-12">
<i className="fas fa-inbox text-5xl text-gray-300 mb-4"></i>
<h3 className="text-xl font-medium text-gray-500 mb-2">暂无学习记录</h3>
<p className="text-gray-400">
{items.length === 0
? '开始添加你的第一条学习记录吧!'
: '没有找到匹配的记录'}
</p>
</div>
) : (
<div className="space-y-3">
{filteredItems.map(item => (
<LearningItem
key={item.id}
item={item}
onDelete={handleDelete}
onEdit={handleEdit}
/>
))}
</div>
)}
</div>
{/* 页脚 */}
<footer className="mt-12 text-center text-gray-500 text-sm">
<p>© 2026 学习记录系统 - 记录成长的每一步</p>
</footer>
</div>
);
};
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
</script>
</body>
</html>

浙公网安备 33010602011771号