/** * 部署详情页面 */ import { useState, useEffect } from 'react' import { View, Text, ScrollView } from '@tarojs/components' import Taro from '@tarojs/taro' import { AtButton, AtTag, AtActivityIndicator } from 'taro-ui' import { getDeploy, getDeployLogs, rollbackDeploy } from '../../../../../api/cicd' import type { Deploy, DeployStatus, DeployEnv } from '../../../../../types/cicd' import './deploy-detail.scss' // 状态映射 const STATUS_MAP: Record = { pending: { text: '等待中', color: '#FFC107' }, deploying: { text: '部署中', color: '#2196F3' }, success: { text: '部署成功', color: '#4CAF50' }, failed: { text: '部署失败', color: '#F44336' }, rollback: { text: '回滚中', color: '#FF9800' }, } // 环境映射 const ENV_MAP: Record = { development: { text: '开发环境', color: '#9E9E9E' }, staging: { text: '预发布环境', color: '#FF9800' }, production: { text: '生产环境', color: '#4CAF50' }, } export default function DeployDetail() { const [loading, setLoading] = useState(true) const [deploy, setDeploy] = useState({}) const [logs, setLogs] = useState('') const [activeTab, setActiveTab] = useState('info') const [deployId, setDeployId] = useState('') useEffect(() => { const pages = Taro.getCurrentPages() const current = pages[pages.length - 1] if (current?.options?.id) { setDeployId(current.options.id) } }, []) useEffect(() => { if (deployId) { fetchDeployDetail() } }, [deployId]) // 获取部署详情 const fetchDeployDetail = async () => { try { const res = await getDeploy(Number(deployId)) if (res.data) { setDeploy(res.data) } } catch (err) { console.error('获取部署详情失败', err) } finally { setLoading(false) } } // 获取部署日志 const fetchDeployLogs = async () => { try { const res = await getDeployLogs(Number(deployId)) if (res.data) { setLogs(res.data.logs || '') } } catch (err) { console.error('获取部署日志失败', err) } } // 切换 Tab useEffect(() => { if (activeTab === 'logs' && !logs) { fetchDeployLogs() } }, [activeTab]) // 回滚部署 const handleRollback = async () => { Taro.showModal({ title: '确认回滚', content: `确定要回滚到版本 ${deploy.previousVersion || '上一个版本'} 吗?`, success: async (res) => { if (res.confirm) { try { Taro.showLoading({ title: '回滚中...' }) await rollbackDeploy(Number(deployId)) Taro.showToast({ title: '回滚成功', icon: 'success' }) fetchDeployDetail() } catch (err) { Taro.showToast({ title: '回滚失败', icon: 'none' }) } finally { Taro.hideLoading() } } }, }) } // 格式化时间 const formatTime = (time?: string) => { if (!time) return '-' const date = new Date(time) return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')} ${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}:${String(date.getSeconds()).padStart(2, '0')}` } // 格式化时长 const formatDuration = (seconds?: number) => { if (!seconds) return '-' if (seconds < 60) return `${seconds} 秒` const mins = Math.floor(seconds / 60) const secs = seconds % 60 return `${mins} 分 ${secs} 秒` } if (loading) { return ( 加载中... ) } const statusInfo = STATUS_MAP[deploy.status || 'pending'] const envInfo = ENV_MAP[deploy.env || 'staging'] return ( {/* 部署状态卡片 */} v{deploy.version} {envInfo.text} {statusInfo.text} 等待 部署 完成 构建版本 #{deploy.buildNo} 部署人 {deploy.deployer} 开始时间 {formatTime(deploy.startTime)} 总耗时 {formatDuration(deploy.duration)} {deploy.previousVersion && ( 从 v{deploy.previousVersion} 回滚 )} {/* 操作按钮 */} {deploy.status === 'success' && ( 回滚到此版本 )} { Taro.navigateBack() }} > 返回列表 {/* Tab 切换 */} setActiveTab('info')} > 部署信息 setActiveTab('logs')} > 部署日志 {/* Tab 内容 */} {activeTab === 'info' && ( 基本信息 部署 ID {deploy.id} 项目 ID {deploy.websiteId} 构建 ID {deploy.buildId} 版本号 v{deploy.version} 时间信息 开始时间 {formatTime(deploy.startTime)} 结束时间 {formatTime(deploy.endTime)} 总耗时 {formatDuration(deploy.duration)} {deploy.previousVersion && ( 回滚信息 回滚源版本 v{deploy.previousVersion} 回滚至版本 v{deploy.version} )} )} {activeTab === 'logs' && ( {logs ? ( {logs.split('\n').map((line, index) => ( {line} ))} ) : ( 加载日志中... )} )} ) }