Sfoglia il codice sorgente

使用dayjs格式化时间
显示格式为"YYYY-MM-DD HH:mm:ss"
保持表格其他功能和样式不变
确保时间显示清晰易读

yourname 6 mesi fa
parent
commit
e1b9f6fd74
3 ha cambiato i file con 110 aggiunte e 20 eliminazioni
  1. 2 1
      HISTORY.md
  2. 86 10
      client/migrations/migrations_app.tsx
  3. 22 9
      server/routes_migrations.ts

+ 2 - 1
HISTORY.md

@@ -1,2 +1,3 @@
 2025.05.13 0.1.0
-首页添加了迁移管理入口按钮, 无需登录即可访问
+首页添加了迁移管理入口按钮, 无需登录即可访问
+打开迁移管理页面时,将迁移历史读取出来

+ 86 - 10
client/migrations/migrations_app.tsx

@@ -1,10 +1,12 @@
 import React, { useState } from 'react';
 import { createRoot } from 'react-dom/client';
-import { Button, Space, Alert, Spin, Typography } from 'antd';
+import { Button, Space, Alert, Spin, Typography, Table } from 'antd';
 import axios from 'axios';
+import dayjs from 'dayjs';
 import {
   QueryClient,
   QueryClientProvider,
+  useQuery,
 } from '@tanstack/react-query';
 
 const { Title } = Typography;
@@ -18,10 +20,26 @@ interface MigrationResponse {
   failedResult?: any;
 }
 
+interface MigrationHistory {
+  id: string;
+  name: string;
+  status: string;
+  timestamp: string;
+  batch: string;
+}
+
 const MigrationsApp: React.FC = () => {
   const [loading, setLoading] = useState(false);
   const [migrationResult, setMigrationResult] = useState<MigrationResponse | null>(null);
 
+  const { data: historyData, isLoading: isHistoryLoading, error: historyError } = useQuery({
+    queryKey: ['migrations-history'],
+    queryFn: async () => {
+      const response = await axios.get('/api/migrations/history');
+      return response.data.history;
+    }
+  });
+
   const runMigrations = async () => {
     try {
       setLoading(true);
@@ -40,13 +58,43 @@ const MigrationsApp: React.FC = () => {
     }
   };
 
+  const columns = [
+    {
+      title: '迁移名称',
+      dataIndex: 'name',
+      key: 'name',
+      sorter: (a: MigrationHistory, b: MigrationHistory) => a.name.localeCompare(b.name),
+    },
+    {
+      title: '批次',
+      dataIndex: 'batch',
+      key: 'batch',
+    },
+    {
+      title: '状态',
+      dataIndex: 'status',
+      key: 'status',
+      render: (status: string) => (
+        <span style={{ color: status === 'completed' ? 'green' : 'red' }}>
+          {status === 'completed' ? '已完成' : '失败'}
+        </span>
+      )
+    },
+    {
+      title: '时间',
+      dataIndex: 'timestamp',
+      key: 'timestamp',
+      render: (timestamp: string) => dayjs(timestamp).format('YYYY-MM-DD HH:mm:ss')
+    },
+  ];
+
   return (
     <div className="p-4">
       <Title level={3}>数据库迁移管理</Title>
       
       <Space direction="vertical" size="middle" style={{ width: '100%' }}>
-        <Button 
-          type="primary" 
+        <Button
+          type="primary"
           onClick={runMigrations}
           loading={loading}
           disabled={loading}
@@ -58,13 +106,13 @@ const MigrationsApp: React.FC = () => {
 
         {migrationResult && (
           migrationResult.success ? (
-            <Alert 
-              message="迁移成功" 
-              type="success" 
-              showIcon 
+            <Alert
+              message="迁移成功"
+              type="success"
+              showIcon
             />
           ) : (
-            <Alert 
+            <Alert
               message="迁移失败"
               description={
                 <>
@@ -76,11 +124,39 @@ const MigrationsApp: React.FC = () => {
                   )}
                 </>
               }
-              type="error" 
-              showIcon 
+              type="error"
+              showIcon
             />
           )
         )}
+
+        <Title level={4}>迁移历史记录</Title>
+        
+        {isHistoryLoading ? (
+          <Spin tip="加载历史记录中..." />
+        ) : historyError ? (
+          <Alert
+            message="加载历史记录失败"
+            description={historyError.message}
+            type="error"
+            showIcon
+          />
+        ) : (
+          <Table
+            columns={columns}
+            dataSource={historyData}
+            rowKey="id"
+            pagination={{
+              pageSize: 10,
+              showSizeChanger: true,
+              pageSizeOptions: ['10', '20', '50', '100'],
+              showTotal: (total) => `共 ${total} 条记录`,
+            }}
+            bordered
+            className="migration-history-table"
+            style={{ marginTop: 16 }}
+          />
+        )}
       </Space>
     </div>
   );

+ 22 - 9
server/routes_migrations.ts

@@ -7,20 +7,15 @@ import debug from "debug";
 const log = {
   api: debug("api:migrations"),
 };
-// 初始化数据库
-const initDatabase = async (apiClient: APIClient) => {
-    log.api('正在执行数据库迁移...')
-    const migrationsResult = await apiClient.database.executeLiveMigrations(migrations)
-    // log.app('数据库迁移完成 %O',migrationsResult)
-    log.api('数据库迁移完成')
-    return migrationsResult
-}
+
 export function createMigrationsRoutes(withAuth: WithAuth) {
   const migrationsRoutes = new Hono<{ Variables: Variables }>()
 
   migrationsRoutes.get('/', async (c) => {
     const apiClient = c.get('apiClient')
-    const migrationsResult = await initDatabase(apiClient)
+    log.api('正在执行数据库迁移...')
+    const migrationsResult = await apiClient.database.executeLiveMigrations(migrations)
+    // log.app('数据库迁移完成 %O',migrationsResult)
     
     const failedResult = migrationsResult?.find((migration) => migration.status === 'failed')
     if (failedResult) {
@@ -31,5 +26,23 @@ export function createMigrationsRoutes(withAuth: WithAuth) {
     return c.json({ success: true })
   })
 
+
+  migrationsRoutes.get('/history', async (c) => {
+    const apiClient = c.get('apiClient')
+    log.api('正在执行数据库迁移...')
+    const MIRGRATIONS_TABLE = 'knex_migrations'
+    const hasTable = await apiClient.database.schema.hasTable(MIRGRATIONS_TABLE);
+
+    let history = []
+
+    if(hasTable)
+      history = await apiClient.database.table(MIRGRATIONS_TABLE).orderBy('id', 'desc')
+    
+    return c.json({
+      success: true,
+      history
+    })
+  })
+
   return migrationsRoutes
 }