mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-10 17:26:38 +08:00
[优化] 界面优化
This commit is contained in:
@@ -1,395 +1,384 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { marked } from 'marked';
|
import {marked} from 'marked';
|
||||||
import { useEffect, useState } from 'react';
|
import {useEffect, useState} from 'react';
|
||||||
|
|
||||||
import { EmptyState } from '@/components/feedback/empty-state';
|
import {EmptyState} from '@/components/feedback/empty-state';
|
||||||
import { ErrorState } from '@/components/feedback/error-state';
|
import {ErrorState} from '@/components/feedback/error-state';
|
||||||
import { LoadingState } from '@/components/feedback/loading-state';
|
import {LoadingState} from '@/components/feedback/loading-state';
|
||||||
import { AppCard } from '@/components/ui/app-card';
|
import {AppCard} from '@/components/ui/app-card';
|
||||||
import { AppModal } from '@/components/ui/app-modal';
|
import {AppModal} from '@/components/ui/app-modal';
|
||||||
import { StatusBadge } from '@/components/ui/status-badge';
|
import {StatusBadge} from '@/components/ui/status-badge';
|
||||||
import type {
|
import type {LatestReleaseInfo, ReleaseChannel, UploadedServerBinaryInfo,} from '@/features/update/types';
|
||||||
LatestReleaseInfo,
|
|
||||||
ReleaseChannel,
|
|
||||||
UploadedServerBinaryInfo,
|
|
||||||
} from '@/features/update/types';
|
|
||||||
import {
|
import {
|
||||||
PrimaryButton,
|
PrimaryButton,
|
||||||
ResourceField,
|
ResourceField,
|
||||||
ResourceInput,
|
ResourceInput,
|
||||||
SecondaryButton,
|
SecondaryButton,
|
||||||
} from '@/features/shared/components/resource-primitives';
|
} from '@/features/shared/components/resource-primitives';
|
||||||
import { formatDateTime, formatRelativeTime } from '@/lib/utils/date';
|
import {formatDateTime, formatRelativeTime} from '@/lib/utils/date';
|
||||||
|
|
||||||
interface VersionUpgradeModalProps {
|
interface VersionUpgradeModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
currentVersion: string;
|
currentVersion: string;
|
||||||
frontendVersion: string;
|
frontendVersion: string;
|
||||||
release: LatestReleaseInfo | null | undefined;
|
release: LatestReleaseInfo | null | undefined;
|
||||||
selectedChannel: ReleaseChannel;
|
selectedChannel: ReleaseChannel;
|
||||||
uploadedBinary: UploadedServerBinaryInfo | null;
|
uploadedBinary: UploadedServerBinaryInfo | null;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
releaseErrorMessage?: string;
|
releaseErrorMessage?: string;
|
||||||
manualStatusMessage?: string;
|
manualStatusMessage?: string;
|
||||||
manualErrorMessage?: string;
|
manualErrorMessage?: string;
|
||||||
canUpgrade: boolean;
|
canUpgrade: boolean;
|
||||||
isChecking: boolean;
|
isChecking: boolean;
|
||||||
isUpgrading: boolean;
|
isUpgrading: boolean;
|
||||||
isUploadingBinary: boolean;
|
isUploadingBinary: boolean;
|
||||||
isConfirmingManualUpgrade: boolean;
|
isConfirmingManualUpgrade: boolean;
|
||||||
onChannelChange: (channel: ReleaseChannel) => void;
|
onChannelChange: (channel: ReleaseChannel) => void;
|
||||||
onCheck: () => void;
|
onCheck: () => void;
|
||||||
onUpgrade: () => void;
|
onUpgrade: () => void;
|
||||||
onUploadBinary: (file: File) => void;
|
onUploadBinary: (file: File) => void;
|
||||||
onConfirmManualUpgrade: () => void;
|
onConfirmManualUpgrade: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getUpgradeBadge(release: LatestReleaseInfo | null | undefined) {
|
function getUpgradeBadge(release: LatestReleaseInfo | null | undefined) {
|
||||||
if (!release) {
|
if (!release) {
|
||||||
return { label: '未检查', variant: 'info' as const };
|
return {label: '未检查', variant: 'info' as const};
|
||||||
}
|
}
|
||||||
if (release.in_progress) {
|
if (release.in_progress) {
|
||||||
return { label: '升级中', variant: 'warning' as const };
|
return {label: '升级中', variant: 'warning' as const};
|
||||||
}
|
}
|
||||||
if (release.has_update) {
|
if (release.has_update) {
|
||||||
return { label: '可升级', variant: 'warning' as const };
|
return {label: '可升级', variant: 'warning' as const};
|
||||||
}
|
}
|
||||||
return { label: '最新', variant: 'success' as const };
|
return {label: '最新', variant: 'success' as const};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function VersionUpgradeModal({
|
export function VersionUpgradeModal({
|
||||||
isOpen,
|
isOpen,
|
||||||
onClose,
|
onClose,
|
||||||
currentVersion,
|
currentVersion,
|
||||||
frontendVersion,
|
frontendVersion,
|
||||||
release,
|
release,
|
||||||
selectedChannel,
|
selectedChannel,
|
||||||
uploadedBinary,
|
uploadedBinary,
|
||||||
isLoading,
|
isLoading,
|
||||||
releaseErrorMessage,
|
releaseErrorMessage,
|
||||||
manualStatusMessage,
|
manualStatusMessage,
|
||||||
manualErrorMessage,
|
manualErrorMessage,
|
||||||
canUpgrade,
|
canUpgrade,
|
||||||
isChecking,
|
isChecking,
|
||||||
isUpgrading,
|
isUpgrading,
|
||||||
isUploadingBinary,
|
isUploadingBinary,
|
||||||
isConfirmingManualUpgrade,
|
isConfirmingManualUpgrade,
|
||||||
onChannelChange,
|
onChannelChange,
|
||||||
onCheck,
|
onCheck,
|
||||||
onUpgrade,
|
onUpgrade,
|
||||||
onUploadBinary,
|
onUploadBinary,
|
||||||
onConfirmManualUpgrade,
|
onConfirmManualUpgrade,
|
||||||
}: VersionUpgradeModalProps) {
|
}: VersionUpgradeModalProps) {
|
||||||
const upgradeBadge = getUpgradeBadge(release);
|
const upgradeBadge = getUpgradeBadge(release);
|
||||||
const [selectedBinary, setSelectedBinary] = useState<File | null>(null);
|
const [selectedBinary, setSelectedBinary] = useState<File | null>(null);
|
||||||
const selectedChannelLabel =
|
const selectedChannelLabel =
|
||||||
selectedChannel === 'preview' ? '预览版' : '正式版';
|
selectedChannel === 'preview' ? '预览版' : '正式版';
|
||||||
const toggleChannel = () => {
|
const toggleChannel = () => {
|
||||||
onChannelChange(selectedChannel === 'preview' ? 'stable' : 'preview');
|
onChannelChange(selectedChannel === 'preview' ? 'stable' : 'preview');
|
||||||
};
|
};
|
||||||
const canConfirmManualUpgrade = Boolean(
|
const canConfirmManualUpgrade = Boolean(
|
||||||
uploadedBinary?.ready_to_upgrade && uploadedBinary.upload_token,
|
uploadedBinary?.ready_to_upgrade && uploadedBinary.upload_token,
|
||||||
);
|
);
|
||||||
const showConfirmManualUpgradeAction = canConfirmManualUpgrade;
|
const showConfirmManualUpgradeAction = canConfirmManualUpgrade;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isOpen) {
|
if (!isOpen) {
|
||||||
setSelectedBinary(null);
|
setSelectedBinary(null);
|
||||||
}
|
}
|
||||||
}, [isOpen]);
|
}, [isOpen]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppModal
|
<AppModal
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
title="版本"
|
title="版本"
|
||||||
description="默认检查正式版更新;你也可以手动检查 preview 发布并选择升级,或上传 Server 二进制确认升级。升级开始后服务会短暂重启。"
|
description="默认检查正式版更新;你也可以手动检查 preview 发布并选择升级,或上传 Server 二进制确认升级。升级开始后服务会短暂重启。"
|
||||||
size="lg"
|
size="lg"
|
||||||
>
|
>
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<div className="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
|
<div className="grid gap-4 md:grid-cols-2 xl:grid-cols-2">
|
||||||
<AppCard title="前端版本">
|
|
||||||
<p className="text-sm font-medium text-[var(--foreground-primary)]">
|
<AppCard title="服务端版本">
|
||||||
{frontendVersion}
|
<div className="flex flex-wrap items-center gap-3">
|
||||||
</p>
|
<p className="text-sm font-medium text-[var(--foreground-primary)]">
|
||||||
</AppCard>
|
{currentVersion || 'unknown'}
|
||||||
<AppCard title="服务端版本">
|
</p>
|
||||||
<div className="flex flex-wrap items-center gap-3">
|
<StatusBadge
|
||||||
<p className="text-sm font-medium text-[var(--foreground-primary)]">
|
label={upgradeBadge.label}
|
||||||
{currentVersion || 'unknown'}
|
variant={upgradeBadge.variant}
|
||||||
</p>
|
/>
|
||||||
<StatusBadge
|
</div>
|
||||||
label={upgradeBadge.label}
|
</AppCard>
|
||||||
variant={upgradeBadge.variant}
|
|
||||||
/>
|
<AppCard title="最新版本">
|
||||||
</div>
|
<div className="space-y-4">
|
||||||
</AppCard>
|
<div className="flex flex-wrap items-center gap-3">
|
||||||
<AppCard title="最新版本">
|
<p className="text-sm font-medium text-[var(--foreground-primary)]">
|
||||||
<div className="space-y-4">
|
{release?.tag_name || '未检查'}
|
||||||
<div className="flex flex-wrap items-center gap-3">
|
</p>
|
||||||
<p className="text-sm font-medium text-[var(--foreground-primary)]">
|
{canUpgrade ? (
|
||||||
{release?.tag_name || '未检查'}
|
<div className="flex flex-wrap items-center justify-between gap-3">
|
||||||
</p>
|
<div className="flex flex-wrap items-center gap-2">
|
||||||
<StatusBadge
|
<StatusBadge
|
||||||
label={selectedChannelLabel}
|
label={selectedChannelLabel}
|
||||||
variant={selectedChannel === 'preview' ? 'warning' : 'info'}
|
variant={
|
||||||
/>
|
selectedChannel === 'preview' ? 'warning' : 'info'
|
||||||
</div>
|
}
|
||||||
{canUpgrade ? (
|
onClick={toggleChannel}
|
||||||
<div className="flex flex-wrap items-center justify-between gap-3">
|
disabled={isChecking || isUpgrading || isUploadingBinary}
|
||||||
<div className="flex flex-wrap items-center gap-2">
|
/>
|
||||||
<span className="text-xs text-[var(--foreground-secondary)]">
|
</div>
|
||||||
点击标签切换通道
|
</div>
|
||||||
</span>
|
) : null}
|
||||||
<StatusBadge
|
</div>
|
||||||
label={selectedChannelLabel}
|
<SecondaryButton
|
||||||
variant={
|
type="button"
|
||||||
selectedChannel === 'preview' ? 'warning' : 'info'
|
onClick={onCheck}
|
||||||
}
|
disabled={isChecking || isUpgrading || isUploadingBinary}
|
||||||
onClick={toggleChannel}
|
className="w-full md:w-auto"
|
||||||
disabled={isChecking || isUpgrading || isUploadingBinary}
|
>
|
||||||
/>
|
{isChecking ? '检查中...' : '检查'}
|
||||||
</div>
|
</SecondaryButton>
|
||||||
<SecondaryButton
|
</div>
|
||||||
type="button"
|
</AppCard>
|
||||||
onClick={onCheck}
|
|
||||||
disabled={isChecking || isUpgrading || isUploadingBinary}
|
|
||||||
className="w-full md:w-auto"
|
|
||||||
>
|
|
||||||
{isChecking ? '检查中...' : '检查'}
|
|
||||||
</SecondaryButton>
|
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
</AppCard>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{isLoading ? <LoadingState /> : null}
|
{isLoading ? <LoadingState/> : null}
|
||||||
{!isLoading && releaseErrorMessage ? (
|
{!isLoading && releaseErrorMessage ? (
|
||||||
<ErrorState title="版本检查失败" description={releaseErrorMessage} />
|
<ErrorState title="版本检查失败" description={releaseErrorMessage}/>
|
||||||
) : null}
|
|
||||||
{!isLoading && !releaseErrorMessage && !release ? (
|
|
||||||
<EmptyState
|
|
||||||
title={`尚未检查${selectedChannelLabel}`}
|
|
||||||
description={`点击“检查${selectedChannelLabel}”后会在这里展示对应 GitHub Release 信息。`}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
{!isLoading && !releaseErrorMessage && release ? (
|
|
||||||
<AppCard
|
|
||||||
title={`GitHub ${selectedChannelLabel} · ${release.tag_name}`}
|
|
||||||
description={
|
|
||||||
release.published_at
|
|
||||||
? `发布时间:${formatRelativeTime(release.published_at)} · ${formatDateTime(release.published_at)}`
|
|
||||||
: '未提供发布时间'
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div className="space-y-4">
|
|
||||||
<div className="flex flex-wrap items-center gap-3">
|
|
||||||
<StatusBadge
|
|
||||||
label={release.has_update ? '发现新版本' : '已经是最新版本'}
|
|
||||||
variant={release.has_update ? 'warning' : 'success'}
|
|
||||||
/>
|
|
||||||
{release.prerelease ? (
|
|
||||||
<StatusBadge label="Preview 发布" variant="warning" />
|
|
||||||
) : (
|
|
||||||
<StatusBadge label="正式发布" variant="info" />
|
|
||||||
)}
|
|
||||||
{!release.upgrade_supported ? (
|
|
||||||
<StatusBadge
|
|
||||||
label="当前平台不支持自动升级"
|
|
||||||
variant="danger"
|
|
||||||
/>
|
|
||||||
) : null}
|
) : null}
|
||||||
{release.in_progress ? (
|
{!isLoading && !releaseErrorMessage && !release ? (
|
||||||
<StatusBadge label="升级任务执行中" variant="warning" />
|
<EmptyState
|
||||||
) : null}
|
title={`尚未检查${selectedChannelLabel}`}
|
||||||
</div>
|
description={`点击“检查${selectedChannelLabel}”后会在这里展示对应 GitHub Release 信息。`}
|
||||||
<div
|
|
||||||
className="prose prose-sm max-w-none text-[var(--foreground-primary)] [&_a]:text-[var(--brand-primary)]"
|
|
||||||
dangerouslySetInnerHTML={{
|
|
||||||
__html: marked.parse(
|
|
||||||
release.body || '暂无更新说明',
|
|
||||||
) as string,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<a
|
|
||||||
href={release.html_url}
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
className="text-sm font-medium text-[var(--brand-primary)] transition hover:opacity-80"
|
|
||||||
>
|
|
||||||
查看发布详情
|
|
||||||
</a>
|
|
||||||
{canUpgrade ? (
|
|
||||||
<div className="flex justify-end">
|
|
||||||
<PrimaryButton
|
|
||||||
type="button"
|
|
||||||
onClick={onUpgrade}
|
|
||||||
disabled={
|
|
||||||
!release.has_update ||
|
|
||||||
release.in_progress ||
|
|
||||||
isUpgrading ||
|
|
||||||
!release.upgrade_supported ||
|
|
||||||
isUploadingBinary ||
|
|
||||||
isConfirmingManualUpgrade
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{isUpgrading
|
|
||||||
? '升级中...'
|
|
||||||
: release.in_progress
|
|
||||||
? '升级中...'
|
|
||||||
: selectedChannel === 'preview'
|
|
||||||
? '升级到预览版'
|
|
||||||
: '升级到正式版'}
|
|
||||||
</PrimaryButton>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
</AppCard>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
{canUpgrade ? (
|
|
||||||
<AppCard
|
|
||||||
title="手动升级"
|
|
||||||
description="上传服务端二进制后,服务端会先检查版本,再由你确认是否执行升级。"
|
|
||||||
>
|
|
||||||
<div className="space-y-4">
|
|
||||||
<ResourceField
|
|
||||||
label="服务端二进制"
|
|
||||||
hint="支持上传已编译好的当前平台 Server 可执行文件。"
|
|
||||||
>
|
|
||||||
<ResourceInput
|
|
||||||
type="file"
|
|
||||||
onChange={(event) => {
|
|
||||||
const file = event.target.files?.[0] ?? null;
|
|
||||||
setSelectedBinary(file);
|
|
||||||
}}
|
|
||||||
disabled={isUploadingBinary || isConfirmingManualUpgrade}
|
|
||||||
/>
|
|
||||||
</ResourceField>
|
|
||||||
|
|
||||||
<div className="flex justify-end">
|
|
||||||
{showConfirmManualUpgradeAction ? (
|
|
||||||
<PrimaryButton
|
|
||||||
type="button"
|
|
||||||
onClick={onConfirmManualUpgrade}
|
|
||||||
disabled={
|
|
||||||
!canConfirmManualUpgrade ||
|
|
||||||
isConfirmingManualUpgrade ||
|
|
||||||
isUploadingBinary ||
|
|
||||||
isUpgrading
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{isConfirmingManualUpgrade ? '升级中...' : '升级'}
|
|
||||||
</PrimaryButton>
|
|
||||||
) : (
|
|
||||||
<PrimaryButton
|
|
||||||
type="button"
|
|
||||||
onClick={() => {
|
|
||||||
if (selectedBinary) {
|
|
||||||
onUploadBinary(selectedBinary);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
disabled={
|
|
||||||
!selectedBinary ||
|
|
||||||
isUploadingBinary ||
|
|
||||||
isConfirmingManualUpgrade
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{isUploadingBinary ? '上传检查中...' : '上传并检查'}
|
|
||||||
</PrimaryButton>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{manualErrorMessage ? (
|
|
||||||
<ErrorState
|
|
||||||
title="手动升级检查失败"
|
|
||||||
description={manualErrorMessage}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
{!manualErrorMessage && manualStatusMessage ? (
|
|
||||||
<div className="rounded-2xl border border-[var(--status-warning-border)] bg-[var(--status-warning-soft)] px-4 py-3 text-sm text-[var(--status-warning-foreground)]">
|
|
||||||
{manualStatusMessage}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
{uploadedBinary ? (
|
|
||||||
<div className="space-y-4 rounded-3xl border border-[var(--border-default)] bg-[var(--surface-elevated)] p-4">
|
|
||||||
<div className="flex flex-wrap items-center gap-3">
|
|
||||||
<StatusBadge
|
|
||||||
label={
|
|
||||||
uploadedBinary.ready_to_upgrade
|
|
||||||
? '可确认升级'
|
|
||||||
: uploadedBinary.has_update
|
|
||||||
? '待确认'
|
|
||||||
: '不可升级'
|
|
||||||
}
|
|
||||||
variant={
|
|
||||||
uploadedBinary.ready_to_upgrade ? 'warning' : 'info'
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
{!uploadedBinary.upgrade_supported ? (
|
) : null}
|
||||||
<StatusBadge
|
{!isLoading && !releaseErrorMessage && release ? (
|
||||||
label="当前版本不支持手动升级"
|
<AppCard
|
||||||
variant="danger"
|
title={`GitHub ${selectedChannelLabel} · ${release.tag_name}`}
|
||||||
/>
|
description={
|
||||||
) : null}
|
release.published_at
|
||||||
</div>
|
? `发布时间:${formatRelativeTime(release.published_at)} · ${formatDateTime(release.published_at)}`
|
||||||
|
: '未提供发布时间'
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="flex flex-wrap items-center gap-3">
|
||||||
|
<StatusBadge
|
||||||
|
label={release.has_update ? '发现新版本' : '已经是最新版本'}
|
||||||
|
variant={release.has_update ? 'warning' : 'success'}
|
||||||
|
/>
|
||||||
|
{release.prerelease ? (
|
||||||
|
<StatusBadge label="Preview 发布" variant="warning"/>
|
||||||
|
) : (
|
||||||
|
<StatusBadge label="正式发布" variant="info"/>
|
||||||
|
)}
|
||||||
|
{!release.upgrade_supported ? (
|
||||||
|
<StatusBadge
|
||||||
|
label="当前平台不支持自动升级"
|
||||||
|
variant="danger"
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
{release.in_progress ? (
|
||||||
|
<StatusBadge label="升级任务执行中" variant="warning"/>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="prose prose-sm max-w-none text-[var(--foreground-primary)] [&_a]:text-[var(--brand-primary)]"
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: marked.parse(
|
||||||
|
release.body || '暂无更新说明',
|
||||||
|
) as string,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<a
|
||||||
|
href={release.html_url}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
className="text-sm font-medium text-[var(--brand-primary)] transition hover:opacity-80"
|
||||||
|
>
|
||||||
|
查看发布详情
|
||||||
|
</a>
|
||||||
|
{canUpgrade ? (
|
||||||
|
<div className="flex justify-end">
|
||||||
|
|
||||||
<div className="grid gap-4 md:grid-cols-2">
|
<PrimaryButton
|
||||||
<div>
|
type="button"
|
||||||
<p className="text-xs text-[var(--foreground-secondary)]">
|
onClick={onUpgrade}
|
||||||
文件名
|
disabled={
|
||||||
</p>
|
!release.has_update ||
|
||||||
<p className="mt-1 text-sm font-medium text-[var(--foreground-primary)]">
|
release.in_progress ||
|
||||||
{uploadedBinary.file_name}
|
isUpgrading ||
|
||||||
</p>
|
!release.upgrade_supported ||
|
||||||
</div>
|
isUploadingBinary ||
|
||||||
<div>
|
isConfirmingManualUpgrade
|
||||||
<p className="text-xs text-[var(--foreground-secondary)]">
|
}
|
||||||
上传时间
|
>
|
||||||
</p>
|
{isUpgrading
|
||||||
<p className="mt-1 text-sm font-medium text-[var(--foreground-primary)]">
|
? '升级中...'
|
||||||
{uploadedBinary.uploaded_at
|
: release.in_progress
|
||||||
? formatDateTime(uploadedBinary.uploaded_at)
|
? '升级中...'
|
||||||
: '未知'}
|
: selectedChannel === 'preview'
|
||||||
</p>
|
? '升级到预览版'
|
||||||
</div>
|
: '升级到正式版'}
|
||||||
<div>
|
</PrimaryButton>
|
||||||
<p className="text-xs text-[var(--foreground-secondary)]">
|
</div>
|
||||||
当前版本
|
) : null}
|
||||||
</p>
|
</div>
|
||||||
<p className="mt-1 text-sm font-medium text-[var(--foreground-primary)]">
|
</AppCard>
|
||||||
{uploadedBinary.current_version}
|
) : null}
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p className="text-xs text-[var(--foreground-secondary)]">
|
|
||||||
上传版本
|
|
||||||
</p>
|
|
||||||
<p className="mt-1 text-sm font-medium text-[var(--foreground-primary)]">
|
|
||||||
{uploadedBinary.detected_version}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p className="text-sm text-[var(--foreground-secondary)]">
|
{canUpgrade ? (
|
||||||
{uploadedBinary.comparison_message}
|
<AppCard
|
||||||
</p>
|
title="手动升级"
|
||||||
</div>
|
description="上传服务端二进制后,服务端会先检查版本,再由你确认是否执行升级。"
|
||||||
) : (
|
>
|
||||||
<EmptyState
|
<div className="space-y-4">
|
||||||
title="尚未上传升级包"
|
<ResourceField
|
||||||
description="上传后会在这里展示识别出的版本信息,并允许你确认升级。"
|
label="服务端二进制"
|
||||||
/>
|
hint="支持上传已编译好的当前平台 Server 可执行文件。"
|
||||||
)}
|
>
|
||||||
|
<ResourceInput
|
||||||
|
type="file"
|
||||||
|
onChange={(event) => {
|
||||||
|
const file = event.target.files?.[0] ?? null;
|
||||||
|
setSelectedBinary(file);
|
||||||
|
}}
|
||||||
|
disabled={isUploadingBinary || isConfirmingManualUpgrade}
|
||||||
|
/>
|
||||||
|
</ResourceField>
|
||||||
|
|
||||||
|
<div className="flex justify-end">
|
||||||
|
{showConfirmManualUpgradeAction ? (
|
||||||
|
<PrimaryButton
|
||||||
|
type="button"
|
||||||
|
onClick={onConfirmManualUpgrade}
|
||||||
|
disabled={
|
||||||
|
!canConfirmManualUpgrade ||
|
||||||
|
isConfirmingManualUpgrade ||
|
||||||
|
isUploadingBinary ||
|
||||||
|
isUpgrading
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{isConfirmingManualUpgrade ? '升级中...' : '升级'}
|
||||||
|
</PrimaryButton>
|
||||||
|
) : (
|
||||||
|
<PrimaryButton
|
||||||
|
type="button"
|
||||||
|
onClick={() => {
|
||||||
|
if (selectedBinary) {
|
||||||
|
onUploadBinary(selectedBinary);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
disabled={
|
||||||
|
!selectedBinary ||
|
||||||
|
isUploadingBinary ||
|
||||||
|
isConfirmingManualUpgrade
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{isUploadingBinary ? '上传检查中...' : '上传并检查'}
|
||||||
|
</PrimaryButton>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{manualErrorMessage ? (
|
||||||
|
<ErrorState
|
||||||
|
title="手动升级检查失败"
|
||||||
|
description={manualErrorMessage}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{!manualErrorMessage && manualStatusMessage ? (
|
||||||
|
<div
|
||||||
|
className="rounded-2xl border border-[var(--status-warning-border)] bg-[var(--status-warning-soft)] px-4 py-3 text-sm text-[var(--status-warning-foreground)]">
|
||||||
|
{manualStatusMessage}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{uploadedBinary ? (
|
||||||
|
<div
|
||||||
|
className="space-y-4 rounded-3xl border border-[var(--border-default)] bg-[var(--surface-elevated)] p-4">
|
||||||
|
<div className="flex flex-wrap items-center gap-3">
|
||||||
|
<StatusBadge
|
||||||
|
label={
|
||||||
|
uploadedBinary.ready_to_upgrade
|
||||||
|
? '可确认升级'
|
||||||
|
: uploadedBinary.has_update
|
||||||
|
? '待确认'
|
||||||
|
: '不可升级'
|
||||||
|
}
|
||||||
|
variant={
|
||||||
|
uploadedBinary.ready_to_upgrade ? 'warning' : 'info'
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
{!uploadedBinary.upgrade_supported ? (
|
||||||
|
<StatusBadge
|
||||||
|
label="当前版本不支持手动升级"
|
||||||
|
variant="danger"
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid gap-4 md:grid-cols-2">
|
||||||
|
<div>
|
||||||
|
<p className="text-xs text-[var(--foreground-secondary)]">
|
||||||
|
文件名
|
||||||
|
</p>
|
||||||
|
<p className="mt-1 text-sm font-medium text-[var(--foreground-primary)]">
|
||||||
|
{uploadedBinary.file_name}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-xs text-[var(--foreground-secondary)]">
|
||||||
|
上传时间
|
||||||
|
</p>
|
||||||
|
<p className="mt-1 text-sm font-medium text-[var(--foreground-primary)]">
|
||||||
|
{uploadedBinary.uploaded_at
|
||||||
|
? formatDateTime(uploadedBinary.uploaded_at)
|
||||||
|
: '未知'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-xs text-[var(--foreground-secondary)]">
|
||||||
|
当前版本
|
||||||
|
</p>
|
||||||
|
<p className="mt-1 text-sm font-medium text-[var(--foreground-primary)]">
|
||||||
|
{uploadedBinary.current_version}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-xs text-[var(--foreground-secondary)]">
|
||||||
|
上传版本
|
||||||
|
</p>
|
||||||
|
<p className="mt-1 text-sm font-medium text-[var(--foreground-primary)]">
|
||||||
|
{uploadedBinary.detected_version}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p className="text-sm text-[var(--foreground-secondary)]">
|
||||||
|
{uploadedBinary.comparison_message}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<EmptyState
|
||||||
|
title="尚未上传升级包"
|
||||||
|
description="上传后会在这里展示识别出的版本信息,并允许你确认升级。"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</AppCard>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
</AppCard>
|
</AppModal>
|
||||||
) : null}
|
);
|
||||||
</div>
|
|
||||||
</AppModal>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user