migrations_app.tsx 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. import React, { useState } from 'react';
  2. import { createRoot } from 'react-dom/client';
  3. import { Button, Space, Alert, Spin, Typography, Table } from 'antd';
  4. import axios from 'axios';
  5. import dayjs from 'dayjs';
  6. import {
  7. QueryClient,
  8. QueryClientProvider,
  9. useQuery,
  10. } from '@tanstack/react-query';
  11. const { Title } = Typography;
  12. // 创建QueryClient实例
  13. const queryClient = new QueryClient();
  14. interface MigrationResponse {
  15. success: boolean;
  16. error?: string;
  17. failedResult?: any;
  18. }
  19. interface MigrationHistory {
  20. id: string;
  21. name: string;
  22. status: string;
  23. timestamp: string;
  24. batch: string;
  25. }
  26. const MigrationsApp: React.FC = () => {
  27. const [loading, setLoading] = useState(false);
  28. const [migrationResult, setMigrationResult] = useState<MigrationResponse | null>(null);
  29. const { data: historyData, isLoading: isHistoryLoading, error: historyError } = useQuery({
  30. queryKey: ['migrations-history'],
  31. queryFn: async () => {
  32. const response = await axios.get('/api/migrations/history');
  33. return response.data.history;
  34. }
  35. });
  36. const runMigrations = async () => {
  37. try {
  38. setLoading(true);
  39. setMigrationResult(null);
  40. const response = await axios.get('/api/migrations');
  41. setMigrationResult(response.data);
  42. } catch (error: any) {
  43. setMigrationResult({
  44. success: false,
  45. error: error.response?.data?.error || '数据库迁移失败',
  46. failedResult: error.response?.data?.failedResult
  47. });
  48. } finally {
  49. setLoading(false);
  50. }
  51. };
  52. const columns = [
  53. {
  54. title: '迁移名称',
  55. dataIndex: 'name',
  56. key: 'name',
  57. sorter: (a: MigrationHistory, b: MigrationHistory) => a.name.localeCompare(b.name),
  58. },
  59. {
  60. title: '批次',
  61. dataIndex: 'batch',
  62. key: 'batch',
  63. },
  64. {
  65. title: '状态',
  66. dataIndex: 'status',
  67. key: 'status',
  68. render: (status: string) => (
  69. <span style={{ color: status === 'completed' ? 'green' : 'red' }}>
  70. {status === 'completed' ? '已完成' : '失败'}
  71. </span>
  72. )
  73. },
  74. {
  75. title: '时间',
  76. dataIndex: 'migration_time',
  77. key: 'migration_time',
  78. render: (migration_time: string) => dayjs(migration_time).format('YYYY-MM-DD HH:mm:ss')
  79. },
  80. ];
  81. return (
  82. <div className="p-4">
  83. <Title level={3}>数据库迁移管理</Title>
  84. <Space direction="vertical" size="middle" style={{ width: '100%' }}>
  85. <Button
  86. type="primary"
  87. onClick={runMigrations}
  88. loading={loading}
  89. disabled={loading}
  90. >
  91. 执行迁移
  92. </Button>
  93. {loading && <Spin tip="迁移执行中..." />}
  94. {migrationResult && (
  95. migrationResult.success ? (
  96. <Alert
  97. message="迁移成功"
  98. type="success"
  99. showIcon
  100. />
  101. ) : (
  102. <Alert
  103. message="迁移失败"
  104. description={
  105. <>
  106. <p>{migrationResult.error}</p>
  107. {migrationResult.failedResult && (
  108. <pre style={{ marginTop: 10 }}>
  109. {JSON.stringify(migrationResult.failedResult, null, 2)}
  110. </pre>
  111. )}
  112. </>
  113. }
  114. type="error"
  115. showIcon
  116. />
  117. )
  118. )}
  119. <Title level={4}>迁移历史记录</Title>
  120. {isHistoryLoading ? (
  121. <Spin tip="加载历史记录中..." />
  122. ) : historyError ? (
  123. <Alert
  124. message="加载历史记录失败"
  125. description={historyError.message}
  126. type="error"
  127. showIcon
  128. />
  129. ) : (
  130. <Table
  131. columns={columns}
  132. dataSource={historyData}
  133. rowKey="id"
  134. pagination={{
  135. pageSize: 10,
  136. showSizeChanger: true,
  137. pageSizeOptions: ['10', '20', '50', '100'],
  138. showTotal: (total) => `共 ${total} 条记录`,
  139. }}
  140. bordered
  141. className="migration-history-table"
  142. style={{ marginTop: 16 }}
  143. />
  144. )}
  145. </Space>
  146. </div>
  147. );
  148. };
  149. // 渲染应用
  150. const root = createRoot(document.getElementById('root') as HTMLElement);
  151. root.render(
  152. <QueryClientProvider client={queryClient}>
  153. <MigrationsApp />
  154. </QueryClientProvider>
  155. );