'use client' import { useState, useEffect } from 'react' import { useSession } from 'next-auth/react' import apiClient from '@/lib/api-client' import FunctionFormModal from './FunctionFormModal' import AlertDialog from '@/components/ui/AlertDialog' import ConfirmDialog from '@/components/ui/ConfirmDialog' interface SystemFunction { id: number code: string name: string function_type: number // 1=NODE, 2=FUNCTION upper_function_id: number order: number function_icon: string module_code: string | null module_functions: string[] description: string is_mana: boolean is_active: boolean } interface ParentFunction { id: number code: string name: string } export default function SystemFunctionsPage() { const { data: session } = useSession() const [allFunctions, setAllFunctions] = useState([]) // 所有資料 const [functions, setFunctions] = useState([]) // 當前頁面顯示的資料 const [parentFunctions, setParentFunctions] = useState([]) const [loading, setLoading] = useState(true) const [showModal, setShowModal] = useState(false) const [editingFunction, setEditingFunction] = useState(null) // 對話框狀態 const [alertDialog, setAlertDialog] = useState<{ isOpen: boolean title: string message: string type: 'info' | 'warning' | 'error' | 'success' }>({ isOpen: false, title: '', message: '', type: 'info', }) const [confirmDialog, setConfirmDialog] = useState<{ isOpen: boolean title: string message: string type: 'info' | 'warning' | 'error' | 'success' onConfirm: () => void }>({ isOpen: false, title: '', message: '', type: 'warning', onConfirm: () => {}, }) // 篩選條件 const [filterType, setFilterType] = useState('') const [filterActive, setFilterActive] = useState('') const [filterParent, setFilterParent] = useState('') // 分頁與排序 const [currentPage, setCurrentPage] = useState(1) const [pageSize, setPageSize] = useState(5) const [sortField, setSortField] = useState<'order' | 'id'>('order') const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('asc') // 載入功能列表 const loadFunctions = async () => { try { setLoading(true) console.log('[SystemFunctions] Loading functions...') // 建立查詢參數 - 不使用 URLSearchParams,直接建立 query string let queryParts = ['page_size=100'] // 後端限制最大 100 if (filterType) { queryParts.push(`function_type=${filterType}`) } // is_active 特殊處理:空字串表示不篩選,所以不加這個參數 if (filterActive) { queryParts.push(`is_active=${filterActive}`) } else { // 不傳 is_active 參數,讓後端使用預設值或不篩選 // 為了顯示所有資料(包含停用),我們明確傳 is_active=None 或不傳 } if (filterParent) { queryParts.push(`upper_function_id=${filterParent}`) } const queryString = queryParts.join('&') console.log('[SystemFunctions] Query:', queryString) const response: any = await apiClient.get(`/system-functions?${queryString}`) console.log('[SystemFunctions] Response:', response) console.log('[SystemFunctions] Items count:', response.items?.length || 0) setAllFunctions(response.items || []) setCurrentPage(1) // 重置到第一頁 } catch (error) { console.error('[SystemFunctions] Failed to load functions:', error) setAlertDialog({ isOpen: true, title: '載入失敗', message: `載入系統功能失敗: ${(error as any)?.message || '未知錯誤'}`, type: 'error', }) } finally { setLoading(false) } } // 載入上層功能選項 (用於篩選) const loadParentFunctions = async () => { try { const response: any = await apiClient.get('/system-functions?function_type=1&page_size=100') const parents = response.items || [] setParentFunctions([{ id: 0, code: 'root', name: '根層 (無上層)' }, ...parents]) } catch (error) { console.error('Failed to load parent functions:', error) } } useEffect(() => { if (session) { loadParentFunctions() loadFunctions() } }, [session]) // 當篩選條件改變時重新載入 useEffect(() => { if (session) { loadFunctions() } }, [filterType, filterActive, filterParent]) // 排序和分頁處理 useEffect(() => { // 排序 const sorted = [...allFunctions].sort((a, b) => { const aValue = a[sortField] const bValue = b[sortField] if (sortDirection === 'asc') { return aValue > bValue ? 1 : -1 } else { return aValue < bValue ? 1 : -1 } }) // 分頁 const startIndex = (currentPage - 1) * pageSize const endIndex = startIndex + pageSize const paginated = sorted.slice(startIndex, endIndex) setFunctions(paginated) }, [allFunctions, currentPage, pageSize, sortField, sortDirection]) // 排序處理 const handleSort = (field: 'order' | 'id') => { if (sortField === field) { // 切換排序方向 setSortDirection(sortDirection === 'asc' ? 'desc' : 'asc') } else { // 新欄位,預設升序 setSortField(field) setSortDirection('asc') } } // 計算總頁數 const totalPages = Math.ceil(allFunctions.length / pageSize) // 切換頁碼 const handlePageChange = (page: number) => { if (page >= 1 && page <= totalPages) { setCurrentPage(page) } } // 產生頁碼按鈕 const renderPageNumbers = () => { const pages = [] const maxVisiblePages = 5 let startPage = Math.max(1, currentPage - Math.floor(maxVisiblePages / 2)) let endPage = Math.min(totalPages, startPage + maxVisiblePages - 1) if (endPage - startPage < maxVisiblePages - 1) { startPage = Math.max(1, endPage - maxVisiblePages + 1) } for (let i = startPage; i <= endPage; i++) { pages.push( ) } return pages } // 新增功能 const handleCreate = () => { setEditingFunction(null) setShowModal(true) } // 編輯功能 const handleEdit = (func: SystemFunction) => { setEditingFunction(func) setShowModal(true) } // 刪除功能 (軟刪除) const handleDelete = (id: number) => { setConfirmDialog({ isOpen: true, title: '確認刪除', message: '確定要刪除此功能嗎?刪除後將無法復原。', type: 'warning', onConfirm: async () => { try { await apiClient.delete(`/system-functions/${id}`) loadFunctions() setAlertDialog({ isOpen: true, title: '刪除成功', message: '功能已成功刪除', type: 'success', }) } catch (error) { console.error('Failed to delete function:', error) setAlertDialog({ isOpen: true, title: '刪除失敗', message: '刪除功能時發生錯誤,請稍後再試', type: 'error', }) } finally { setConfirmDialog({ ...confirmDialog, isOpen: false }) } }, }) } // 切換啟用狀態 const handleToggleActive = async (func: SystemFunction) => { try { await apiClient.patch(`/system-functions/${func.id}`, { is_active: !func.is_active, edit_by: 1 // TODO: 從 session 取得當前用戶 ID }) loadFunctions() } catch (error) { console.error('Failed to toggle active status:', error) setAlertDialog({ isOpen: true, title: '操作失敗', message: '切換狀態失敗,請稍後再試', type: 'error', }) } } if (loading) { return (
載入中...
) } return (
{/* Header */}

🎯 系統功能設定

管理系統功能列表,包含功能代碼、名稱、圖示等設定

{/* 篩選條件 */}
{(filterType || filterActive || filterParent) && (
)}
{/* 每頁筆數選擇 */}
共 {allFunctions.length} 筆資料
{/* Table */}
{functions.map((func) => ( ))}
ID 圖示 功能代碼 功能名稱 handleSort('order')} >
順序 {sortField === 'order' && ( {sortDirection === 'asc' ? '↑' : '↓'} )}
啟用 操作
{func.id} {func.function_icon} {func.code} {func.name} {func.order}
{allFunctions.length === 0 && !loading && (
尚無功能資料
)} {/* 分頁控制 */} {allFunctions.length > 0 && (
顯示第 {(currentPage - 1) * pageSize + 1} - {Math.min(currentPage * pageSize, allFunctions.length)} 筆, 共 {allFunctions.length} 筆
{/* 第一頁 */} {/* 上一頁 */} {/* 頁碼 */} {renderPageNumbers()} {/* 下一頁 */} {/* 最後一頁 */} {/* 跳頁 */}
跳至 { const page = Number(e.target.value) if (page >= 1 && page <= totalPages) { handlePageChange(page) } }} className="w-16 px-2 py-1 border border-gray-300 rounded text-center text-sm text-gray-900 focus:outline-none focus:ring-2 focus:ring-blue-500" />
)} {/* Modal */} {showModal && ( setShowModal(false)} onSave={(isEdit: boolean) => { setShowModal(false) loadFunctions() setAlertDialog({ isOpen: true, title: '儲存成功', message: `功能已成功${isEdit ? '更新' : '新增'}`, type: 'success', }) }} /> )} {/* Alert Dialog */} setAlertDialog({ ...alertDialog, isOpen: false })} /> {/* Confirm Dialog */} setConfirmDialog({ ...confirmDialog, isOpen: false })} />
) }