import { CopyOutlined, DeleteOutlined, DownloadOutlined, EditOutlined, EyeOutlined, SyncOutlined, UploadOutlined } from '@ant-design/icons'; import { PageContainer, ProTable, type ActionType, type ProColumns } from '@ant-design/pro-components'; import { history, useLocation } from '@umijs/max'; import { useBoolean } from 'ahooks'; import { Button, message, Modal, Popconfirm, Space, Tabs, Tooltip } from 'antd'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; import { oneDark } from 'react-syntax-highlighter/dist/esm/styles/prism'; import type { TableQuery, TableVO } from '@/api/tool/gen/types'; import { batchGenCode, delTable, getDataNames, listTable, previewTable, synchDb } from '@/api/tool/gen'; import EllipsisText from '@/components/common/EllipsisText'; import { useDateRangeQuery } from '@/hooks/useDateRangeQuery'; import { useUserStore } from '@/stores/userStore'; import { saveValidatedBlob } from '@/utils/download'; import { hasPermi } from '@/utils/permission'; import { toPageQuery, toTableData } from '@/utils/ruoyi'; import ImportTableModal from './components/ImportTableModal'; function previewName(path: string) { const fileName = path.substring(path.lastIndexOf('/') + 1); return fileName.includes('.ftl') ? fileName.substring(0, fileName.indexOf('.ftl')) : fileName; } function previewLanguage(name: string) { const ext = name.toLowerCase().split('.').pop(); const languageMap: Record = { java: 'java', xml: 'xml', ts: 'typescript', tsx: 'tsx', vue: 'vue', sql: 'sql', json: 'json', yml: 'yaml', yaml: 'yaml', js: 'javascript', jsx: 'jsx', html: 'html', css: 'css', less: 'less' }; return (ext && languageMap[ext]) || 'text'; } export default function ToolGenPage() { const location = useLocation(); const actionRef = useRef(undefined); const userInfo = useUserStore(state => state.userInfo); const [selectedRows, setSelectedRows] = useState([]); const [dataNames, setDataNames] = useState([]); const [previewOpen, { setTrue: openPreviewModal, setFalse: closePreviewModal }] = useBoolean(false); const [previewData, setPreviewData] = useState>({}); const [previewActive, setPreviewActive] = useState(''); const [importOpen, { setTrue: openImportModal, setFalse: closeImportModal }] = useBoolean(false); const [currentDataName, setCurrentDataName] = useState(); const [currentPage, setCurrentPage] = useState( () => Number(new URLSearchParams(location.search).get('pageNum')) || 1 ); const { applyDateRange } = useDateRangeQuery(); const canCode = hasPermi(userInfo, ['tool:gen:code']); const canImport = hasPermi(userInfo, ['tool:gen:import']); const canEdit = hasPermi(userInfo, ['tool:gen:edit']); const canRemove = hasPermi(userInfo, ['tool:gen:remove']); const canPreview = hasPermi(userInfo, ['tool:gen:preview']); const selectedIds = selectedRows.map(item => item.tableId).filter(Boolean); const selectedOne = selectedRows.length === 1 ? selectedRows[0] : undefined; const previewItems = useMemo(() => Object.entries(previewData), [previewData]); const loadDataNames = useCallback(async () => { const res = await getDataNames(); const names = res.data || []; setDataNames(names); return names; }, []); const openEditTable = (tableId?: string | number) => { if (!tableId) return; history.push(`/tool/gen-edit/index/${tableId}?pageNum=${currentPage}`); }; useEffect(() => { loadDataNames(); }, [loadDataNames]); const handleGenTable = async (row?: TableVO) => { const rows = row ? [row] : selectedRows; if (!rows.length) { message.error('请选择要生成的数据'); return; } const tableIdStr = rows.map(item => item.tableId).join(','); const blob = await batchGenCode(tableIdStr); await saveValidatedBlob(blob, 'ruoyi.zip'); }; const handlePreview = async (row: TableVO) => { const res = await previewTable(row.tableId); const data = res.data || {}; setPreviewData(data); setPreviewActive(Object.keys(data)[0] ? previewName(Object.keys(data)[0]) : ''); openPreviewModal(); }; const copyPreview = async (value: string) => { await navigator.clipboard.writeText(value); message.success('复制成功'); }; const handleSynchDb = async (row: TableVO) => { await synchDb(row.tableId); message.success('同步成功'); actionRef.current?.reload(); }; const handleDelete = async (row?: TableVO) => { const ids = row?.tableId || selectedIds; if (!ids || (Array.isArray(ids) && ids.length === 0)) return; await delTable(ids); message.success('删除成功'); setSelectedRows([]); actionRef.current?.reloadAndRest?.(); }; const openImportTable = async () => { await loadDataNames(); openImportModal(); }; const columns: ProColumns[] = [ { title: '数据源', dataIndex: 'dataName', valueType: 'select', fieldProps: { allowClear: true, showSearch: true, options: [{ label: '全部', value: '' }, ...dataNames.map(item => ({ label: item, value: item }))] }, width: 150, render: (_, row) => }, { title: '表名称', dataIndex: 'tableName', width: 180, render: (_, row) => }, { title: '表描述', dataIndex: 'tableComment', width: 180, render: (_, row) => }, { title: '实体', dataIndex: 'className', search: false, width: 180, render: (_, row) => }, { title: '创建时间', dataIndex: 'createTime', valueType: 'dateTime', search: false, width: 170 }, { title: '创建时间', dataIndex: 'dateRange', valueType: 'dateRange', hideInTable: true, search: { transform: value => ({ dateRange: value }) } }, { title: '更新时间', dataIndex: 'updateTime', valueType: 'dateTime', search: false, width: 170 }, { title: '操作', valueType: 'option', width: 230, fixed: 'right', render: (_, row) => ( {canPreview && ( ), canImport && ( ), canEdit && ( ), canRemove && ( handleDelete()}> ) ]} /> ({ key: previewName(key), label: previewName(key), children: (
{value}
) }))} />
{ closeImportModal(); actionRef.current?.reloadAndRest?.(); }} /> ); }