This commit is contained in:
Stephen Zhou 2026-04-29 16:12:44 +08:00
parent da6fd82b6f
commit 930688c559
No known key found for this signature in database
7 changed files with 182 additions and 164 deletions

View File

@ -43,51 +43,37 @@ type DeployFormProps = {
onSubmit: (params: DeployFormSubmit) => void
}
type DisabledBindingControlProps = {
type RuntimeBindingGroupProps = {
label: string
placeholder: string
}
const DisabledBindingControl: FC<DisabledBindingControlProps> = ({ label, placeholder }) => (
<div className="flex items-center gap-2">
<span className="w-20 shrink-0 system-xs-medium text-text-secondary">{label}</span>
<button
type="button"
disabled
className="flex h-8 min-w-0 flex-1 cursor-not-allowed items-center justify-between rounded-lg border-[0.5px] border-components-input-border-active bg-components-input-bg-normal px-2 system-sm-medium text-text-quaternary opacity-60"
>
<span className="truncate">{placeholder}</span>
<span className="i-ri-arrow-down-s-line h-4 w-4 shrink-0 text-text-quaternary" />
</button>
</div>
)
type DisabledBindingGroupProps = {
label: string
placeholder: string
bindings: RuntimeBindingDisplay[]
isLoading: boolean
}
const DisabledBindingGroup: FC<DisabledBindingGroupProps> = ({ label, placeholder, bindings, isLoading }) => {
if (bindings.length === 0) {
return (
<DisabledBindingControl
label={label}
placeholder={isLoading ? placeholder : '—'}
/>
)
}
const RuntimeBindingGroup: FC<RuntimeBindingGroupProps> = ({ label, bindings, isLoading }) => {
const { t } = useTranslation('deployments')
return (
<div className="flex flex-col gap-2">
{bindings.map(binding => (
<DisabledBindingControl
key={`${binding.kind}-${runtimeBindingLabel(binding)}-${runtimeBindingValue(binding)}-${binding.valueType ?? ''}`}
label={runtimeBindingLabel(binding)}
placeholder={runtimeBindingValue(binding)}
/>
))}
<div className="flex items-start gap-3 border-t border-divider-subtle px-3 py-2.5 first:border-t-0">
<div className="w-36 shrink-0 system-xs-medium-uppercase text-text-tertiary">{label}</div>
<div className="flex min-w-0 flex-1 flex-col gap-1.5">
{isLoading
? <span className="system-sm-regular text-text-quaternary">{t('deployDrawer.loadingBindings')}</span>
: bindings.length === 0
? <span className="system-sm-regular text-text-quaternary">{t('deployDrawer.noBindingRequired')}</span>
: bindings.map(binding => (
<div
key={`${binding.kind}-${runtimeBindingLabel(binding)}-${runtimeBindingValue(binding)}-${binding.valueType ?? ''}`}
className="flex min-w-0 items-center justify-between gap-3"
>
<span className="min-w-0 truncate system-sm-medium text-text-secondary" title={runtimeBindingLabel(binding)}>
{runtimeBindingLabel(binding)}
</span>
<span className="max-w-[240px] truncate rounded-md bg-background-default px-2 py-0.5 font-mono system-xs-medium text-text-tertiary" title={runtimeBindingValue(binding)}>
{runtimeBindingValue(binding)}
</span>
</div>
))}
</div>
</div>
)
}
@ -214,35 +200,31 @@ export const DeployForm: FC<DeployFormProps> = ({
)}
</Field>
<div className="flex flex-col gap-4">
<div className="flex items-center justify-between gap-3">
<div className="system-xs-medium-uppercase text-text-tertiary">{t('deployDrawer.runtimeCredentials')}</div>
<span className="system-xs-regular text-text-quaternary">{t('deployDrawer.bindingsDisabled')}</span>
<div className="overflow-hidden rounded-xl border border-divider-subtle bg-background-default-subtle">
<div className="flex items-start justify-between gap-3 px-3 py-2.5">
<div className="flex min-w-0 flex-col gap-0.5">
<div className="system-xs-medium-uppercase text-text-tertiary">{t('deployDrawer.runtimeCredentials')}</div>
<span className="system-xs-regular text-text-quaternary">{t('deployDrawer.bindingsDisabled')}</span>
</div>
<span className="shrink-0 rounded-md bg-background-default px-2 py-0.5 system-xs-medium text-text-tertiary">
{t('deployDrawer.readOnly')}
</span>
</div>
<Field label={t('deployDrawer.modelCreds')}>
<DisabledBindingGroup
label={t('deployDrawer.modelCreds')}
placeholder={t('deployDrawer.defaultSelect')}
bindings={modelBindings}
isLoading={releasePreview.isFetching}
/>
</Field>
<Field label={t('deployDrawer.pluginCreds')}>
<DisabledBindingGroup
label={t('deployDrawer.pluginCreds')}
placeholder={t('deployDrawer.defaultSelect')}
bindings={pluginBindings}
isLoading={releasePreview.isFetching}
/>
</Field>
<Field label={t('deployDrawer.envVars')}>
<DisabledBindingGroup
label={t('deployDrawer.envVars')}
placeholder={t('deployDrawer.defaultSelect')}
bindings={envVarBindings}
isLoading={releasePreview.isFetching}
/>
</Field>
<RuntimeBindingGroup
label={t('deployDrawer.modelCreds')}
bindings={modelBindings}
isLoading={releasePreview.isFetching}
/>
<RuntimeBindingGroup
label={t('deployDrawer.pluginCreds')}
bindings={pluginBindings}
isLoading={releasePreview.isFetching}
/>
<RuntimeBindingGroup
label={t('deployDrawer.envVars')}
bindings={envVarBindings}
isLoading={releasePreview.isFetching}
/>
</div>
<div className="flex justify-end gap-2">

View File

@ -21,13 +21,14 @@ import {
environmentId,
environmentMode,
environmentName,
isUndeployedDeploymentRow,
releaseCommit,
releaseLabel,
} from '../utils'
import { DeploymentPanel } from './deploy-tab/deployment-panel'
import { DeploymentStatusSummary } from './deploy-tab/deployment-status-summary'
const GRID_TEMPLATE = 'lg:grid-cols-[1.2fr_0.8fr_1fr_auto]'
const GRID_TEMPLATE = 'lg:grid-cols-[minmax(180px,1fr)_minmax(140px,0.75fr)_minmax(180px,0.85fr)_240px]'
type DeployTabProps = {
instanceId: string
@ -41,14 +42,32 @@ const DeployTab: FC<DeployTabProps> = ({ instanceId: appId }) => {
const { environmentOptions } = useSourceApps()
const rows = useMemo(
() => appData?.environmentDeployments.data?.filter(row => row.environment?.id) ?? [],
[appData?.environmentDeployments.data],
)
const deployedRuntimeRows = useMemo(
() => deployedRows(appData?.environmentDeployments.data),
[appData?.environmentDeployments.data],
)
const deployedEnvIds = new Set(rows.map(row => environmentId(row.environment)))
const deployedEnvIds = new Set(deployedRuntimeRows.map(row => environmentId(row.environment)))
const availableEnvs = environmentOptions.filter(env => env.id && !deployedEnvIds.has(env.id))
const [expanded, setExpanded] = useState<string | null>(() => rows[0] ? environmentId(rows[0].environment) : null)
const toggle = (id: string) => setExpanded(prev => (prev === id ? null : id))
const expandableEnvIds = useMemo(
() => rows.filter(row => !isUndeployedDeploymentRow(row)).map(row => environmentId(row.environment)),
[rows],
)
const [expanded, setExpanded] = useState<string | null>()
const activeExpanded = expanded === undefined
? expandableEnvIds[0] ?? null
: expanded !== null && expandableEnvIds.includes(expanded)
? expanded
: null
const toggle = (id: string) => {
setExpanded((prev) => {
const current = prev === undefined ? expandableEnvIds[0] ?? null : prev
return current === id ? null : id
})
}
const [deployMenuOpen, setDeployMenuOpen] = useState(false)
return (
@ -126,39 +145,46 @@ const DeployTab: FC<DeployTabProps> = ({ instanceId: appId }) => {
<div>{t('deployTab.col.environment')}</div>
<div>{t('deployTab.col.currentRelease')}</div>
<div>{t('deployTab.col.status')}</div>
<div />
<div className="text-right">{t('deployTab.col.actions')}</div>
</div>
{rows.map((row) => {
const envId = environmentId(row.environment)
const isExpanded = expanded === envId
const isUndeployed = isUndeployedDeploymentRow(row)
const isExpanded = !isUndeployed && activeExpanded === envId
const status = deploymentStatus(row)
const release = activeRelease(row)
const actions = (
<div className="flex shrink-0 items-center gap-1" onClick={e => e.stopPropagation()}>
<Button size="small" variant="secondary" onClick={() => openDeployDrawer({ appId, environmentId: envId })}>
{status === 'ready' ? t('deployTab.deployOtherVersion') : t('deployTab.viewProgress')}
{isUndeployed
? t('deployDrawer.deploy')
: status === 'ready'
? t('deployTab.deployOtherVersion')
: t('deployTab.viewProgress')}
</Button>
<DropdownMenu modal={false}>
<DropdownMenuTrigger
aria-label={t('deployTab.moreActions')}
className="flex h-7 w-7 items-center justify-center rounded-md text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary"
>
<span className="i-ri-more-line h-4 w-4" />
</DropdownMenuTrigger>
<DropdownMenuContent placement="bottom-end" sideOffset={4} popupClassName="w-[200px]">
<DropdownMenuItem
className="gap-2 px-3"
onClick={() => undeployDeployment(appId, envId, deploymentId(row), status === 'deploying')}
{!isUndeployed && (
<DropdownMenu modal={false}>
<DropdownMenuTrigger
aria-label={t('deployTab.moreActions')}
className="flex h-7 w-7 items-center justify-center rounded-md text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary"
>
<span className="system-sm-regular text-text-destructive">
{status === 'deploying' ? t('deployTab.cancelDeployment') : t('deployTab.undeploy')}
</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<span className="i-ri-more-line h-4 w-4" />
</DropdownMenuTrigger>
<DropdownMenuContent placement="bottom-end" sideOffset={4} popupClassName="w-[200px]">
<DropdownMenuItem
className="gap-2 px-3"
onClick={() => undeployDeployment(appId, envId, deploymentId(row), status === 'deploying')}
>
<span className="system-sm-regular text-text-destructive">
{status === 'deploying' ? t('deployTab.cancelDeployment') : t('deployTab.undeploy')}
</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
)}
</div>
)
const chevron = (
const chevron = !isUndeployed && (
<span
className={cn(
'i-ri-arrow-down-s-line h-4 w-4 shrink-0 text-text-tertiary transition-transform',
@ -170,14 +196,14 @@ const DeployTab: FC<DeployTabProps> = ({ instanceId: appId }) => {
<div key={envId} className="border-b border-divider-subtle last:border-b-0">
<button
type="button"
onClick={() => toggle(envId)}
onClick={() => !isUndeployed && toggle(envId)}
className={cn(
'flex w-full flex-col gap-2 px-4 py-3 text-left hover:bg-state-base-hover',
'lg:grid lg:items-center lg:gap-4',
GRID_TEMPLATE,
)}
>
<div className="flex items-start justify-between gap-3 lg:block">
<div className="flex min-w-0 items-start justify-between gap-3 lg:block">
<div className="flex min-w-0 flex-col gap-0.5">
<span className="truncate system-sm-semibold text-text-primary">{environmentName(row.environment)}</span>
<div className="flex items-center gap-1.5 system-xs-regular text-text-tertiary">
@ -191,16 +217,16 @@ const DeployTab: FC<DeployTabProps> = ({ instanceId: appId }) => {
{chevron}
</div>
</div>
<div className="flex flex-wrap items-center gap-x-3 gap-y-1 lg:contents">
<div className="flex items-center gap-2">
<span className="font-mono system-sm-medium text-text-primary">{releaseLabel(release)}</span>
<span className="font-mono system-xs-regular text-text-tertiary">{releaseCommit(release)}</span>
</div>
<div>
<DeploymentStatusSummary row={row} />
</div>
<div className="flex min-w-0 items-center gap-2">
<span className="min-w-0 truncate font-mono system-sm-medium text-text-primary">{isUndeployed ? '—' : releaseLabel(release)}</span>
{!isUndeployed && (
<span className="shrink-0 font-mono system-xs-regular text-text-tertiary">{releaseCommit(release)}</span>
)}
</div>
<div className="hidden items-center justify-end gap-1 lg:flex">
<div className="min-w-0">
<DeploymentStatusSummary row={row} />
</div>
<div className="hidden min-w-0 items-center justify-end gap-1 lg:flex">
{actions}
{chevron}
</div>

View File

@ -4,14 +4,11 @@ import type { FC, ReactNode } from 'react'
import type { EnvironmentDeploymentRow } from '@/contract/console/deployments'
import { cn } from '@langgenius/dify-ui/cn'
import { useTranslation } from 'react-i18next'
import { HealthBadge, ModeBadge } from '../../components/status-badge'
import {
activeRelease,
deploymentId,
environmentBackend,
environmentHealth,
environmentMode,
environmentName,
formatDate,
isRuntimeEnvVarBinding,
isRuntimeModelBinding,
@ -28,9 +25,9 @@ type InfoBlockProps = {
}
const InfoBlock: FC<InfoBlockProps> = ({ title, children }) => (
<div className="flex flex-col gap-1.5">
<div className="system-xs-medium-uppercase text-text-tertiary">{title}</div>
<div className="flex flex-col gap-1">{children}</div>
<div className="min-w-0 rounded-lg bg-background-default px-3 py-2.5">
<div className="mb-2 system-xs-medium-uppercase text-text-tertiary">{title}</div>
<div className="flex flex-col gap-1.5">{children}</div>
</div>
)
@ -42,9 +39,9 @@ type InfoRowProps = {
}
const InfoRow: FC<InfoRowProps> = ({ label, value, mono, suffix }) => (
<div className="flex items-start gap-2 py-0.5">
<span className="w-24 shrink-0 system-xs-regular text-text-tertiary">{label}</span>
<span className={cn('min-w-0 flex-1 system-sm-regular break-words text-text-primary', mono && 'font-mono')}>
<div className="grid min-w-0 grid-cols-[120px_minmax(0,1fr)] items-start gap-2">
<span className="system-xs-regular text-text-tertiary">{label}</span>
<span className={cn('min-w-0 system-sm-regular break-words text-text-primary', mono && 'font-mono')}>
{value}
{suffix && <span className="system-xs-regular text-text-tertiary">{suffix}</span>}
</span>
@ -66,17 +63,8 @@ export const DeploymentPanel: FC<DeploymentPanelProps> = ({ row }) => {
const envVars = detailBindings.filter(isRuntimeEnvVarBinding)
return (
<div className="border-t border-divider-subtle bg-background-default-subtle px-6 py-4">
<div className="mb-3 flex items-center gap-2">
<span className="system-sm-semibold text-text-primary">
{environmentName(env)}
{' · '}
{releaseLabel(observed)}
</span>
<ModeBadge mode={environmentMode(env)} />
<HealthBadge health={environmentHealth(env)} />
</div>
<div className="grid grid-cols-1 gap-x-8 gap-y-4 md:grid-cols-2">
<div className="border-t border-divider-subtle bg-background-default-subtle px-4 py-3">
<div className="grid grid-cols-1 gap-3 xl:grid-cols-[minmax(0,1fr)_minmax(0,1fr)]">
<InfoBlock title={t('deployTab.panel.instanceInfo')}>
<InfoRow label={t('deployTab.panel.deploymentId')} value={deploymentId(row) || '—'} mono />
<InfoRow label={t('deployTab.panel.replicas')} value={row.detail?.replicas != null ? String(row.detail.replicas) : '—'} />
@ -88,8 +76,6 @@ export const DeploymentPanel: FC<DeploymentPanelProps> = ({ row }) => {
<InfoRow label={t('deployTab.panel.release')} value={releaseLabel(observed)} mono />
<InfoRow label={t('deployTab.panel.commit')} value={releaseCommit(observed)} mono />
<InfoRow label={t('deployTab.panel.createdAt')} value={formatDate(observed?.createdAt)} />
<InfoRow label={t('deployTab.panel.targetRelease')} value="—" mono />
<InfoRow label={t('deployTab.panel.failedRelease')} value="—" mono />
</InfoBlock>
<InfoBlock title={t('deployTab.panel.endpoints')}>
@ -97,43 +83,37 @@ export const DeploymentPanel: FC<DeploymentPanelProps> = ({ row }) => {
<InfoRow label={t('deployTab.panel.health')} value={endpoints?.health ?? '—'} mono />
</InfoBlock>
{modelCredentials.length > 0 && (
<InfoBlock title={t('deployTab.panel.modelCreds')}>
{modelCredentials.map(c => (
<InfoRow
key={`${c.kind}-${c.slot}-${c.label}-${c.displayName}-${c.displayValue}-${c.maskedValue}`}
label={runtimeBindingLabel(c)}
value={runtimeBindingValue(c)}
mono
/>
))}
</InfoBlock>
)}
{pluginCredentials.length > 0 && (
<InfoBlock title={t('deployTab.panel.pluginCreds')}>
{pluginCredentials.map(c => (
<InfoRow
key={`${c.kind}-${c.slot}-${c.label}-${c.displayName}-${c.displayValue}-${c.maskedValue}`}
label={runtimeBindingLabel(c)}
value={runtimeBindingValue(c)}
mono
/>
))}
</InfoBlock>
)}
{envVars.length > 0 && (
<InfoBlock title={t('deployTab.panel.envVars')}>
{envVars.map(v => (
<InfoRow
key={`${v.kind}-${v.slot}-${v.label}-${v.displayName}-${v.displayValue}`}
label={runtimeBindingLabel(v)}
value={runtimeBindingValue(v)}
mono
/>
))}
</InfoBlock>
{(modelCredentials.length > 0 || pluginCredentials.length > 0 || envVars.length > 0) && (
<div className="xl:col-span-2">
<InfoBlock title={t('deployTab.panel.runtimeBindings')}>
<div className="grid grid-cols-1 gap-2 lg:grid-cols-3">
{modelCredentials.map(c => (
<InfoRow
key={`${c.kind}-${c.slot}-${c.label}-${c.displayName}-${c.displayValue}-${c.maskedValue}`}
label={runtimeBindingLabel(c)}
value={runtimeBindingValue(c)}
mono
/>
))}
{pluginCredentials.map(c => (
<InfoRow
key={`${c.kind}-${c.slot}-${c.label}-${c.displayName}-${c.displayValue}-${c.maskedValue}`}
label={runtimeBindingLabel(c)}
value={runtimeBindingValue(c)}
mono
/>
))}
{envVars.map(v => (
<InfoRow
key={`${v.kind}-${v.slot}-${v.label}-${v.displayName}-${v.displayValue}`}
label={runtimeBindingLabel(v)}
value={runtimeBindingValue(v)}
mono
/>
))}
</div>
</InfoBlock>
</div>
)}
</div>

View File

@ -6,6 +6,7 @@ import { useTranslation } from 'react-i18next'
import {
activeRelease,
deploymentStatus,
isUndeployedDeploymentRow,
releaseLabel,
} from '../../utils'
@ -15,6 +16,15 @@ type DeploymentStatusSummaryProps = {
export const DeploymentStatusSummary: FC<DeploymentStatusSummaryProps> = ({ row }) => {
const { t } = useTranslation('deployments')
if (isUndeployedDeploymentRow(row)) {
return (
<span className="inline-flex items-center gap-1.5 system-sm-medium text-text-tertiary">
<span className="h-1.5 w-1.5 rounded-full bg-text-quaternary" />
{t('status.notDeployed')}
</span>
)
}
const status = deploymentStatus(row)
if (status === 'deploying') {

View File

@ -84,6 +84,9 @@ export const deploymentId = (row?: EnvironmentDeploymentRow) =>
export const activeRelease = (row?: EnvironmentDeploymentRow) => row?.currentRelease
export const isUndeployedDeploymentRow = (row?: EnvironmentDeploymentRow) =>
(row?.status?.toLowerCase() ?? '').includes('undeployed') || (!row?.id && !row?.currentRelease && !row?.detail)
export const deploymentStatus = (row: EnvironmentDeploymentRow): DeploymentUiStatus => {
const runtimeStatus = row.status?.toLowerCase() ?? ''
if (runtimeStatus.includes('deploying') || runtimeStatus.includes('pending'))
@ -94,7 +97,12 @@ export const deploymentStatus = (row: EnvironmentDeploymentRow): DeploymentUiSta
}
export const deployedRows = (rows?: EnvironmentDeploymentRow[]) =>
rows?.filter(row => row.environment?.id && (row.id || row.status || row.currentRelease || row.detail)) ?? []
rows?.filter((row) => {
const runtimeStatus = row.status?.toLowerCase() ?? ''
return row.environment?.id
&& !isUndeployedDeploymentRow(row)
&& (row.id || runtimeStatus || row.currentRelease || row.detail)
}) ?? []
export const accessModeToPermissionKey = (mode?: string): AccessPermissionKind => {
const normalized = mode?.toLowerCase() ?? ''

View File

@ -107,17 +107,19 @@
"createModal.selected": "Selected",
"createModal.sourceApp": "Source app (required)",
"createModal.title": "Create app instance",
"deployDrawer.bindingsDisabled": "Disabled until deployment binding options are available.",
"deployDrawer.bindingsDisabled": "Resolved from the release preview. Editing is not available yet.",
"deployDrawer.cancel": "Cancel",
"deployDrawer.defaultSelect": "Select...",
"deployDrawer.deploy": "Deploy",
"deployDrawer.description": "Create a new release from the current app YAML and deploy it to a target environment.",
"deployDrawer.envVars": "Environment variables",
"deployDrawer.existingReleaseHint": "This existing release will be deployed as-is. No new release will be created.",
"deployDrawer.loadingBindings": "Resolving...",
"deployDrawer.lockedHint": "Locked to current environment",
"deployDrawer.modelCreds": "Model credentials",
"deployDrawer.needsValidation": " (needs validation)",
"deployDrawer.newReleaseHint": "A new release will be created from the current app YAML.",
"deployDrawer.noBindingRequired": "Not required",
"deployDrawer.notFound": "Instance not found.",
"deployDrawer.noteLabel": "Release note (optional)",
"deployDrawer.notePlaceholder": "e.g. Ship onboarding copy tweak",
@ -125,6 +127,7 @@
"deployDrawer.promote": "Promote",
"deployDrawer.promoteDescription": "Deploy an existing release from the version history to a target environment.",
"deployDrawer.promoteTitle": "Promote release",
"deployDrawer.readOnly": "Read-only",
"deployDrawer.releaseLabel": "Release",
"deployDrawer.runtimeCredentials": "Runtime credentials",
"deployDrawer.secretPlaceholder": "secret",
@ -136,6 +139,7 @@
"deployDrawer.title": "Deploy to environment",
"deployDrawer.valuePlaceholder": "value",
"deployTab.cancelDeployment": "Cancel deployment",
"deployTab.col.actions": "Actions",
"deployTab.col.currentRelease": "Current release",
"deployTab.col.environment": "Environment",
"deployTab.col.status": "Status",
@ -161,6 +165,7 @@
"deployTab.panel.releaseInfo": "Release info",
"deployTab.panel.replicas": "Replicas",
"deployTab.panel.run": "Run",
"deployTab.panel.runtimeBindings": "Runtime bindings",
"deployTab.panel.runtimeMode": "Runtime mode",
"deployTab.panel.runtimeNote": "Runtime note",
"deployTab.panel.targetRelease": "Target release",
@ -258,6 +263,7 @@
"settings.updated": "Instance updated",
"status.deployFailed": "Deploy failed",
"status.deploying": "Deploying",
"status.notDeployed": "Not deployed",
"status.ready": "Ready",
"subtitle": "Deploy and manage your apps across environments.",
"tabs.access.description": "API keys and run-time access for this instance.",

View File

@ -107,17 +107,19 @@
"createModal.selected": "已选择",
"createModal.sourceApp": "源应用(必选)",
"createModal.title": "创建应用实例",
"deployDrawer.bindingsDisabled": "等待后端提供部署绑定选项后启用。",
"deployDrawer.bindingsDisabled": "来自发布预览的解析结果,暂不支持在这里编辑。",
"deployDrawer.cancel": "取消",
"deployDrawer.defaultSelect": "选择...",
"deployDrawer.deploy": "部署",
"deployDrawer.description": "基于当前应用 YAML 创建一个新的发布版本,并部署到目标环境。",
"deployDrawer.envVars": "环境变量",
"deployDrawer.existingReleaseHint": "将直接部署该已有发布版本,不会创建新的版本。",
"deployDrawer.loadingBindings": "解析中...",
"deployDrawer.lockedHint": "已锁定至当前环境",
"deployDrawer.modelCreds": "模型凭据",
"deployDrawer.needsValidation": "(待验证)",
"deployDrawer.newReleaseHint": "将基于当前应用 YAML 创建一个新的发布版本。",
"deployDrawer.noBindingRequired": "无需配置",
"deployDrawer.notFound": "未找到实例。",
"deployDrawer.noteLabel": "发布备注(可选)",
"deployDrawer.notePlaceholder": "例如:优化引导文案",
@ -125,6 +127,7 @@
"deployDrawer.promote": "推送",
"deployDrawer.promoteDescription": "从版本历史中选择一个已有发布版本,部署到目标环境。",
"deployDrawer.promoteTitle": "推送发布版本",
"deployDrawer.readOnly": "只读",
"deployDrawer.releaseLabel": "发布版本",
"deployDrawer.runtimeCredentials": "运行时凭据",
"deployDrawer.secretPlaceholder": "机密值",
@ -136,6 +139,7 @@
"deployDrawer.title": "部署到环境",
"deployDrawer.valuePlaceholder": "值",
"deployTab.cancelDeployment": "取消部署",
"deployTab.col.actions": "操作",
"deployTab.col.currentRelease": "当前发布",
"deployTab.col.environment": "环境",
"deployTab.col.status": "状态",
@ -161,6 +165,7 @@
"deployTab.panel.releaseInfo": "版本信息",
"deployTab.panel.replicas": "副本数",
"deployTab.panel.run": "运行",
"deployTab.panel.runtimeBindings": "运行时绑定",
"deployTab.panel.runtimeMode": "运行模式",
"deployTab.panel.runtimeNote": "运行时备注",
"deployTab.panel.targetRelease": "目标版本",
@ -258,6 +263,7 @@
"settings.updated": "实例已更新",
"status.deployFailed": "部署失败",
"status.deploying": "部署中",
"status.notDeployed": "未部署",
"status.ready": "就绪",
"subtitle": "在不同环境中部署和管理你的应用。",
"tabs.access.description": "该实例的 API 密钥和运行时访问设置。",