Sql Query Manager güncellemeleri
This commit is contained in:
parent
79c72430ab
commit
a746fea95e
5 changed files with 1841 additions and 22 deletions
|
|
@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore;
|
||||||
namespace Sozsoft.Platform.Migrations
|
namespace Sozsoft.Platform.Migrations
|
||||||
{
|
{
|
||||||
[DbContext(typeof(PlatformDbContext))]
|
[DbContext(typeof(PlatformDbContext))]
|
||||||
[Migration("20260225082054_Initial")]
|
[Migration("20260301173343_Initial")]
|
||||||
partial class Initial
|
partial class Initial
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|
@ -13,7 +13,7 @@ import type {
|
||||||
} from '@/proxy/sql-query-manager/models'
|
} from '@/proxy/sql-query-manager/models'
|
||||||
import { SqlObjectType } from '@/proxy/sql-query-manager/models'
|
import { SqlObjectType } from '@/proxy/sql-query-manager/models'
|
||||||
import { sqlObjectManagerService } from '@/services/sql-query-manager.service'
|
import { sqlObjectManagerService } from '@/services/sql-query-manager.service'
|
||||||
import { FaDatabase, FaPlay, FaSave, FaSyncAlt, FaCloudUploadAlt, FaCode } from 'react-icons/fa'
|
import { FaDatabase, FaPlay, FaSave, FaCloudUploadAlt, FaCode, FaTable } from 'react-icons/fa'
|
||||||
import { FaCheckCircle } from 'react-icons/fa'
|
import { FaCheckCircle } from 'react-icons/fa'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
import SqlObjectExplorer from './components/SqlObjectExplorer'
|
import SqlObjectExplorer from './components/SqlObjectExplorer'
|
||||||
|
|
@ -21,6 +21,7 @@ import SqlEditor, { SqlEditorRef } from './components/SqlEditor'
|
||||||
import SqlResultsGrid from './components/SqlResultsGrid'
|
import SqlResultsGrid from './components/SqlResultsGrid'
|
||||||
import SqlObjectProperties from './components/SqlObjectProperties'
|
import SqlObjectProperties from './components/SqlObjectProperties'
|
||||||
import TemplateDialog from './components/TemplateDialog'
|
import TemplateDialog from './components/TemplateDialog'
|
||||||
|
import TableDesignerDialog from './components/TableDesignerDialog'
|
||||||
import { Splitter } from '@/components/codeLayout/Splitter'
|
import { Splitter } from '@/components/codeLayout/Splitter'
|
||||||
import { Helmet } from 'react-helmet'
|
import { Helmet } from 'react-helmet'
|
||||||
import { useStoreState } from '@/store/store'
|
import { useStoreState } from '@/store/store'
|
||||||
|
|
@ -72,11 +73,18 @@ const SqlQueryManager = () => {
|
||||||
isExistingObject: false,
|
isExistingObject: false,
|
||||||
})
|
})
|
||||||
const [showTemplateDialog, setShowTemplateDialog] = useState(false)
|
const [showTemplateDialog, setShowTemplateDialog] = useState(false)
|
||||||
|
const [showTableDesignerDialog, setShowTableDesignerDialog] = useState(false)
|
||||||
|
const [designTableData, setDesignTableData] = useState<{ schemaName: string; tableName: string } | null>(null)
|
||||||
const [showTemplateConfirmDialog, setShowTemplateConfirmDialog] = useState(false)
|
const [showTemplateConfirmDialog, setShowTemplateConfirmDialog] = useState(false)
|
||||||
const [pendingTemplate, setPendingTemplate] = useState<{ content: string; type: string } | null>(
|
const [pendingTemplate, setPendingTemplate] = useState<{ content: string; type: string } | null>(
|
||||||
null,
|
null,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const handleDesignTable = (schemaName: string, tableName: string) => {
|
||||||
|
setDesignTableData({ schemaName, tableName })
|
||||||
|
setShowTableDesignerDialog(true)
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadDataSources()
|
loadDataSources()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
@ -802,16 +810,6 @@ GO`,
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<Button
|
|
||||||
size="sm"
|
|
||||||
variant="solid"
|
|
||||||
color="orange-500"
|
|
||||||
icon={<FaCode />}
|
|
||||||
onClick={() => setShowTemplateDialog(true)}
|
|
||||||
className="shadow-sm"
|
|
||||||
>
|
|
||||||
{translate('::App.Platform.Templates')}
|
|
||||||
</Button>
|
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="solid"
|
variant="solid"
|
||||||
|
|
@ -880,6 +878,11 @@ GO`,
|
||||||
selectedObject={state.selectedObject}
|
selectedObject={state.selectedObject}
|
||||||
onTemplateSelect={handleTemplateSelect}
|
onTemplateSelect={handleTemplateSelect}
|
||||||
onShowTableColumns={handleShowTableColumns}
|
onShowTableColumns={handleShowTableColumns}
|
||||||
|
onDesignTable={handleDesignTable}
|
||||||
|
onNewTable={() => {
|
||||||
|
setDesignTableData(null)
|
||||||
|
setShowTableDesignerDialog(true)
|
||||||
|
}}
|
||||||
refreshTrigger={state.refreshTrigger}
|
refreshTrigger={state.refreshTrigger}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -972,6 +975,20 @@ GO`,
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Table Designer Dialog */}
|
||||||
|
<TableDesignerDialog
|
||||||
|
isOpen={showTableDesignerDialog}
|
||||||
|
onClose={() => {
|
||||||
|
setShowTableDesignerDialog(false)
|
||||||
|
setDesignTableData(null)
|
||||||
|
}}
|
||||||
|
dataSource={state.selectedDataSource}
|
||||||
|
initialTableData={designTableData}
|
||||||
|
onDeployed={() => {
|
||||||
|
setState((prev) => ({ ...prev, refreshTrigger: prev.refreshTrigger + 1 }))
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
{/* Template Dialog */}
|
{/* Template Dialog */}
|
||||||
<TemplateDialog
|
<TemplateDialog
|
||||||
isOpen={showTemplateDialog}
|
isOpen={showTemplateDialog}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import {
|
||||||
FaEdit,
|
FaEdit,
|
||||||
FaTrash,
|
FaTrash,
|
||||||
FaTable,
|
FaTable,
|
||||||
|
FaPlus,
|
||||||
} from 'react-icons/fa'
|
} from 'react-icons/fa'
|
||||||
import type {
|
import type {
|
||||||
SqlFunctionDto,
|
SqlFunctionDto,
|
||||||
|
|
@ -42,6 +43,8 @@ interface SqlObjectExplorerProps {
|
||||||
selectedObject: SqlObject | null
|
selectedObject: SqlObject | null
|
||||||
onTemplateSelect?: (template: string, templateType: string) => void
|
onTemplateSelect?: (template: string, templateType: string) => void
|
||||||
onShowTableColumns?: (schemaName: string, tableName: string) => void
|
onShowTableColumns?: (schemaName: string, tableName: string) => void
|
||||||
|
onDesignTable?: (schemaName: string, tableName: string) => void
|
||||||
|
onNewTable?: () => void
|
||||||
refreshTrigger?: number
|
refreshTrigger?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -51,6 +54,8 @@ const SqlObjectExplorer = ({
|
||||||
selectedObject,
|
selectedObject,
|
||||||
onTemplateSelect,
|
onTemplateSelect,
|
||||||
onShowTableColumns,
|
onShowTableColumns,
|
||||||
|
onDesignTable,
|
||||||
|
onNewTable,
|
||||||
refreshTrigger,
|
refreshTrigger,
|
||||||
}: SqlObjectExplorerProps) => {
|
}: SqlObjectExplorerProps) => {
|
||||||
const { translate } = useLocalization()
|
const { translate } = useLocalization()
|
||||||
|
|
@ -506,6 +511,22 @@ const SqlObjectExplorer = ({
|
||||||
className="fixed z-50 bg-white dark:bg-gray-800 shadow-lg rounded border border-gray-200 dark:border-gray-700 py-1"
|
className="fixed z-50 bg-white dark:bg-gray-800 shadow-lg rounded border border-gray-200 dark:border-gray-700 py-1"
|
||||||
style={{ top: contextMenu.y, left: contextMenu.x }}
|
style={{ top: contextMenu.y, left: contextMenu.x }}
|
||||||
>
|
>
|
||||||
|
{contextMenu.node?.id?.startsWith('table-') && (
|
||||||
|
<button
|
||||||
|
className="w-full px-4 py-2 text-left hover:bg-gray-100 dark:hover:bg-gray-700 text-sm"
|
||||||
|
onClick={() => {
|
||||||
|
const tableData = contextMenu.node?.data as any
|
||||||
|
if (tableData && onDesignTable) {
|
||||||
|
onDesignTable(tableData.schemaName, tableData.tableName)
|
||||||
|
}
|
||||||
|
setContextMenu({ show: false, x: 0, y: 0, node: null })
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FaTable className="inline mr-2 text-teal-600" />
|
||||||
|
Design
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
{contextMenu.node?.type === 'object' && contextMenu.node?.objectType && contextMenu.node?.data?.isCustom && (
|
{contextMenu.node?.type === 'object' && contextMenu.node?.objectType && contextMenu.node?.data?.isCustom && (
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
|
|
@ -547,6 +568,83 @@ const SqlObjectExplorer = ({
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{contextMenu.node?.type === 'folder' && (
|
{contextMenu.node?.type === 'folder' && (
|
||||||
|
<>
|
||||||
|
{/* Tables folder */}
|
||||||
|
{contextMenu.node.id === 'tables' && (
|
||||||
|
<button
|
||||||
|
className="w-full px-4 py-2 text-left hover:bg-gray-100 dark:hover:bg-gray-700 text-sm"
|
||||||
|
onClick={() => {
|
||||||
|
onNewTable?.()
|
||||||
|
setContextMenu({ show: false, x: 0, y: 0, node: null })
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FaPlus className="inline mr-2 text-teal-600" />
|
||||||
|
New Table
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Stored Procedures folder */}
|
||||||
|
{contextMenu.node.id === 'procedures' && (
|
||||||
|
<button
|
||||||
|
className="w-full px-4 py-2 text-left hover:bg-gray-100 dark:hover:bg-gray-700 text-sm"
|
||||||
|
onClick={() => {
|
||||||
|
onTemplateSelect?.('', 'create-procedure')
|
||||||
|
setContextMenu({ show: false, x: 0, y: 0, node: null })
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FaPlus className="inline mr-2 text-green-600" />
|
||||||
|
New Stored Procedure
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Views folder */}
|
||||||
|
{contextMenu.node.id === 'views' && (
|
||||||
|
<button
|
||||||
|
className="w-full px-4 py-2 text-left hover:bg-gray-100 dark:hover:bg-gray-700 text-sm"
|
||||||
|
onClick={() => {
|
||||||
|
onTemplateSelect?.('', 'create-view')
|
||||||
|
setContextMenu({ show: false, x: 0, y: 0, node: null })
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FaPlus className="inline mr-2 text-purple-600" />
|
||||||
|
New View
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Functions folder */}
|
||||||
|
{contextMenu.node.id === 'functions' && (
|
||||||
|
<button
|
||||||
|
className="w-full px-4 py-2 text-left hover:bg-gray-100 dark:hover:bg-gray-700 text-sm"
|
||||||
|
onClick={() => {
|
||||||
|
onTemplateSelect?.('', 'create-scalar-function')
|
||||||
|
setContextMenu({ show: false, x: 0, y: 0, node: null })
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FaPlus className="inline mr-2 text-red-500" />
|
||||||
|
New Function
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Queries folder */}
|
||||||
|
{contextMenu.node.id === 'queries' && (
|
||||||
|
<button
|
||||||
|
className="w-full px-4 py-2 text-left hover:bg-gray-100 dark:hover:bg-gray-700 text-sm"
|
||||||
|
onClick={() => {
|
||||||
|
onTemplateSelect?.('', 'select')
|
||||||
|
setContextMenu({ show: false, x: 0, y: 0, node: null })
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FaPlus className="inline mr-2 text-yellow-600" />
|
||||||
|
New Query
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Separator */}
|
||||||
|
{contextMenu.node.id !== 'root' && (
|
||||||
|
<div className="my-1 border-t border-gray-100 dark:border-gray-700" />
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Refresh — all folders */}
|
||||||
<button
|
<button
|
||||||
className="w-full px-4 py-2 text-left hover:bg-gray-100 dark:hover:bg-gray-700 text-sm"
|
className="w-full px-4 py-2 text-left hover:bg-gray-100 dark:hover:bg-gray-700 text-sm"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|
@ -557,6 +655,7 @@ const SqlObjectExplorer = ({
|
||||||
<FaSyncAlt className="inline mr-2" />
|
<FaSyncAlt className="inline mr-2" />
|
||||||
{translate('::App.Platform.Refresh')}
|
{translate('::App.Platform.Refresh')}
|
||||||
</button>
|
</button>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
1703
ui/src/views/sqlQueryManager/components/TableDesignerDialog.tsx
Normal file
1703
ui/src/views/sqlQueryManager/components/TableDesignerDialog.tsx
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue