feat: implement frontend-backend configuration linkage

- Create public config API for frontend consumption
- Add configuration fetching to homepage
- Implement module show/hide logic based on config
- Add support for Services items filtering
- Add support for Products featured products and pricing display
- Add support for News display count, categories, and sort order
- Fix table name from 'configs' to 'siteConfig' in API route
- Update type definitions for proper TypeScript support
This commit is contained in:
张翔
2026-03-13 13:11:20 +08:00
parent f93f802427
commit 4fdfc2d8b4
100 changed files with 894 additions and 316 deletions
+27 -5
View File
@@ -38,6 +38,7 @@ export default function UsersPage() {
const [showEditModal, setShowEditModal] = useState(false);
const [selectedUser, setSelectedUser] = useState<User | null>(null);
const [saving, setSaving] = useState(false);
const [deletingUserId, setDeletingUserId] = useState<string | null>(null);
const [formData, setFormData] = useState({
email: '',
@@ -94,20 +95,32 @@ export default function UsersPage() {
};
const handleDelete = async (userId: string) => {
if (!confirm('确定要删除此用户吗?')) {
if (deletingUserId) {
console.log('删除操作正在进行中,请勿重复点击');
return;
}
if (!confirm('确定要删除此用户吗?此操作不可恢复。')) {
return;
}
try {
setDeletingUserId(userId);
const res = await fetch(`/api/admin/users/${userId}`, {
method: 'DELETE'
});
if (res.ok) {
await fetchUsers();
} else {
const data = await res.json();
alert(data.error || '删除失败');
}
} catch (error) {
console.error('删除用户失败:', error);
alert('删除失败,请稍后重试');
} finally {
setDeletingUserId(null);
}
};
@@ -196,7 +209,8 @@ export default function UsersPage() {
</td>
<td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<button
onClick={() => {
onClick={(e) => {
e.stopPropagation();
setSelectedUser(user);
setFormData({
email: user.email,
@@ -211,10 +225,18 @@ export default function UsersPage() {
<Edit className="h-4 w-4 inline" />
</button>
<button
onClick={() => handleDelete(user.id)}
className="text-red-600 hover:text-red-800"
onClick={(e) => {
e.stopPropagation();
handleDelete(user.id);
}}
disabled={deletingUserId === user.id}
className="text-red-600 hover:text-red-800 disabled:opacity-50 disabled:cursor-not-allowed"
>
<Trash2 className="h-4 w-4 inline" />
{deletingUserId === user.id ? (
<Loader2 className="h-4 w-4 inline animate-spin" />
) : (
<Trash2 className="h-4 w-4 inline" />
)}
</button>
</td>
</tr>