fix(web): restore V21 workspace Chinese copy
This commit is contained in:
@@ -476,31 +476,31 @@ const ProjectAssets: React.FC = () => {
|
||||
};
|
||||
|
||||
const assetPill = (asset: AssetItem) => {
|
||||
if (asset.mime_type?.startsWith('audio')) return { text: '??', cls: 'audio' };
|
||||
if (asset.mime_type?.startsWith('image')) return { text: '??', cls: 'video' };
|
||||
if (String(asset.metadata?.review_status || '') === 'pending_review') return { text: '???', cls: 'warn' };
|
||||
if (String(asset.metadata?.review_status || '') === 'rejected') return { text: '???', cls: 'bad' };
|
||||
return { text: asset.status === 'ready' ? '??' : '??', cls: asset.status === 'ready' ? 'ok' : 'video' };
|
||||
if (asset.mime_type?.startsWith('audio')) return { text: '音频', cls: 'audio' };
|
||||
if (asset.mime_type?.startsWith('image')) return { text: '图片', cls: 'video' };
|
||||
if (String(asset.metadata?.review_status || '') === 'pending_review') return { text: '待复核', cls: 'warn' };
|
||||
if (String(asset.metadata?.review_status || '') === 'rejected') return { text: '已驳回', cls: 'bad' };
|
||||
return { text: asset.status === 'ready' ? '就绪' : '导入中', cls: asset.status === 'ready' ? 'ok' : 'video' };
|
||||
};
|
||||
|
||||
const assetMeta = (asset: AssetItem) => {
|
||||
const duration = Number(asset.metadata?.duration_seconds || asset.metadata?.duration || 0);
|
||||
const durationText = duration ? `${Math.round(duration)}s` : asset.mime_type?.startsWith('audio') ? '????' : '9:16';
|
||||
const classification = String(asset.metadata?.classification || '???');
|
||||
const review = String(asset.metadata?.review_status || asset.status || '???');
|
||||
return `${durationText} ? ${classification} ? ${review}`;
|
||||
const durationText = duration ? `${Math.round(duration)}s` : asset.mime_type?.startsWith('audio') ? '音频素材' : '9:16';
|
||||
const classification = String(asset.metadata?.classification || '未分类');
|
||||
const review = String(asset.metadata?.review_status || asset.status || '未复核');
|
||||
return `${durationText} · ${classification} · ${review}`;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="xx-page-card">
|
||||
<div className="xx-page-head">
|
||||
<div>
|
||||
<h1>???</h1>
|
||||
<p>?????? 9:16 ??????????????????</p>
|
||||
<h1>素材库</h1>
|
||||
<p>上传和管理 9:16 视频、图片、配音素材,系统会自动分类并给出剪辑可用度。</p>
|
||||
</div>
|
||||
<div className="xx-action-row">
|
||||
<Button icon={<PlusOutlined />} onClick={() => setCreateLibraryOpen(true)}>?????</Button>
|
||||
<Button className="xx-btn-primary" type="primary" disabled={!workspaceId || !libraryId} onClick={() => document.getElementById('asset-upload-dropzone')?.scrollIntoView({ behavior: 'smooth', block: 'center' })}>????</Button>
|
||||
<Button icon={<PlusOutlined />} onClick={() => setCreateLibraryOpen(true)}>新建素材库</Button>
|
||||
<Button className="xx-btn-primary" type="primary" disabled={!workspaceId || !libraryId} onClick={() => document.getElementById('asset-upload-dropzone')?.scrollIntoView({ behavior: 'smooth', block: 'center' })}>上传素材</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -511,48 +511,48 @@ const ProjectAssets: React.FC = () => {
|
||||
style={{ width: 160 }}
|
||||
value={smartViewFilter}
|
||||
options={[
|
||||
{ label: '????', value: 'all' },
|
||||
{ label: '????', value: 'recommended' },
|
||||
{ label: '????', value: 'needs_attention' },
|
||||
{ label: '?????', value: 'high_risk' },
|
||||
{ label: '?????', value: 'unclassified' },
|
||||
{ label: '????', value: 'recent' },
|
||||
{ label: '?????', value: 'unused' },
|
||||
{ label: '?????', value: 'used' },
|
||||
{ label: '?????', value: 'pending_review' },
|
||||
{ label: '????', value: 'voice' },
|
||||
{ label: '全部素材', value: 'all' },
|
||||
{ label: '推荐可用', value: 'recommended' },
|
||||
{ label: '需要处理', value: 'needs_attention' },
|
||||
{ label: '高风险素材', value: 'high_risk' },
|
||||
{ label: '未分类素材', value: 'unclassified' },
|
||||
{ label: '最近上传', value: 'recent' },
|
||||
{ label: '未使用素材', value: 'unused' },
|
||||
{ label: '已使用素材', value: 'used' },
|
||||
{ label: '待复核素材', value: 'pending_review' },
|
||||
{ label: '配音素材', value: 'voice' },
|
||||
]}
|
||||
onChange={setSmartViewFilter}
|
||||
/>
|
||||
<Select style={{ width: 170 }} value={batchMode} options={batchModeOptions} onChange={(value) => setBatchMode(value)} />
|
||||
<Select style={{ width: 240 }} placeholder="?????" value={libraryId || undefined} options={libraryOptions} onChange={setLibraryId} loading={librariesQuery.isLoading} />
|
||||
<Button icon={<TagsOutlined />} disabled={!selectedRowKeys.length || batchProgress.running} onClick={handleBatchClassification}>???? ({selectedRowKeys.length})</Button>
|
||||
<Button icon={<ReloadOutlined />} onClick={() => { librariesQuery.refetch(); assetsQuery.refetch(); diagnosisQuery.refetch(); }}>??</Button>
|
||||
<Select style={{ width: 240 }} placeholder="选择素材库" value={libraryId || undefined} options={libraryOptions} onChange={setLibraryId} loading={librariesQuery.isLoading} />
|
||||
<Button icon={<TagsOutlined />} disabled={!selectedRowKeys.length || batchProgress.running} onClick={handleBatchClassification}>批量分类 ({selectedRowKeys.length})</Button>
|
||||
<Button icon={<ReloadOutlined />} onClick={() => { librariesQuery.refetch(); assetsQuery.refetch(); diagnosisQuery.refetch(); }}>刷新</Button>
|
||||
</div>
|
||||
|
||||
{!workspaceId && <Alert style={{ marginBottom: 16 }} type="warning" showIcon message="???????????" description="???????????????????????????????????" />}
|
||||
{librariesQuery.isError && <Alert style={{ marginBottom: 16 }} type="error" showIcon message="???????" />}
|
||||
{assetsQuery.isError && <Alert style={{ marginBottom: 16 }} type="error" showIcon message="????????" />}
|
||||
{ingestJobQuery.isError && <Alert style={{ marginBottom: 16 }} type="error" showIcon message="????????" />}
|
||||
{classificationJobQuery.isError && <Alert style={{ marginBottom: 16 }} type="error" showIcon message="????????" />}
|
||||
{ingestJobQuery.data && <Alert style={{ marginBottom: 16 }} type={ingestJobQuery.data.status === 'failed' ? 'error' : 'info'} message={`???????${ingestJobQuery.data.status}`} description={ingestJobQuery.data.error_message || `?? ID: ${ingestJobQuery.data.id}`} />}
|
||||
{classificationJobQuery.data && <Alert style={{ marginBottom: 16 }} type={classificationJobQuery.data.status === 'failed' ? 'error' : 'success'} message={`???????${classificationJobQuery.data.status}`} description={classificationJobQuery.data.error_message || `?????${classificationJobQuery.data.classification || '???'}`} />}
|
||||
{!workspaceId && <Alert style={{ marginBottom: 16 }} type="warning" showIcon message="缺少工作空间信息" description="请从项目详情页进入素材库,系统需要确认当前工作空间。" />}
|
||||
{librariesQuery.isError && <Alert style={{ marginBottom: 16 }} type="error" showIcon message="素材库加载失败" />}
|
||||
{assetsQuery.isError && <Alert style={{ marginBottom: 16 }} type="error" showIcon message="素材加载失败" />}
|
||||
{ingestJobQuery.isError && <Alert style={{ marginBottom: 16 }} type="error" showIcon message="导入任务加载失败" />}
|
||||
{classificationJobQuery.isError && <Alert style={{ marginBottom: 16 }} type="error" showIcon message="分类任务加载失败" />}
|
||||
{ingestJobQuery.data && <Alert style={{ marginBottom: 16 }} type={ingestJobQuery.data.status === 'failed' ? 'error' : 'info'} message={`导入任务:${ingestJobQuery.data.status}`} description={ingestJobQuery.data.error_message || `任务 ID: ${ingestJobQuery.data.id}`} />}
|
||||
{classificationJobQuery.data && <Alert style={{ marginBottom: 16 }} type={classificationJobQuery.data.status === 'failed' ? 'error' : 'success'} message={`分类任务:${classificationJobQuery.data.status}`} description={classificationJobQuery.data.error_message || `分类结果:${classificationJobQuery.data.classification || '未返回'}`} />}
|
||||
|
||||
{batchProgress.total > 0 && (
|
||||
<div className="xx-section-block">
|
||||
<div className="xx-section-title"><h3>??????</h3></div>
|
||||
<div className="xx-section-title"><h3>批量分类进度</h3></div>
|
||||
<Progress percent={batchPercent} status={batchProgress.running ? 'active' : batchFailedIds.length > 0 ? 'exception' : 'success'} />
|
||||
<Space size="large" wrap><span>???{batchProgress.total}</span><span>????{batchProgress.submitted}</span><span>????{batchProgress.completed}</span><span>???{batchProgress.failed}</span><span>???{batchProgress.skipped}</span></Space>
|
||||
<Space size="large" wrap><span>总数 {batchProgress.total}</span><span>已提交 {batchProgress.submitted}</span><span>已完成 {batchProgress.completed}</span><span>失败 {batchProgress.failed}</span><span>跳过 {batchProgress.skipped}</span></Space>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{diagnosisQuery.isError && <Alert style={{ marginBottom: 16 }} type="error" showIcon message="????????" />}
|
||||
{diagnosisQuery.isError && <Alert style={{ marginBottom: 16 }} type="error" showIcon message="素材诊断加载失败" />}
|
||||
{diagnosisQuery.data && (
|
||||
<div className="xx-diagnosis-strip">
|
||||
<div className="xx-kpi-card"><b>{diagnosisQuery.data.readiness_score}</b><span>{diagnosisQuery.data.readiness_label}</span></div>
|
||||
<Space size={[8, 8]} wrap>
|
||||
<Tag color="blue">??? {diagnosisQuery.data.total_assets}</Tag><Tag color="green">Ready {diagnosisQuery.data.ready_assets}</Tag><Tag color="purple">?? {diagnosisQuery.data.video_assets}</Tag><Tag color="orange">?? {diagnosisQuery.data.image_assets}</Tag><Tag color="cyan">?? {diagnosisQuery.data.voice_assets}</Tag><Tag color="gold">????? {diagnosisQuery.data.estimated_video_count}</Tag>
|
||||
{diagnosisQuery.data.smart_views.map((item) => <Tag key={item.key} color={smartViewFilter === item.key ? 'blue' : undefined} style={{ cursor: 'pointer' }} onClick={() => setSmartViewFilter(item.key)}>{item.label}?{item.count}</Tag>)}
|
||||
<Tag color="blue">总数 {diagnosisQuery.data.total_assets}</Tag><Tag color="green">Ready {diagnosisQuery.data.ready_assets}</Tag><Tag color="purple">视频 {diagnosisQuery.data.video_assets}</Tag><Tag color="orange">图片 {diagnosisQuery.data.image_assets}</Tag><Tag color="cyan">配音 {diagnosisQuery.data.voice_assets}</Tag><Tag color="gold">预计成片 {diagnosisQuery.data.estimated_video_count}</Tag>
|
||||
{diagnosisQuery.data.smart_views.map((item) => <Tag key={item.key} color={smartViewFilter === item.key ? 'blue' : undefined} style={{ cursor: 'pointer' }} onClick={() => setSmartViewFilter(item.key)}>{item.label}:{item.count}</Tag>)}
|
||||
</Space>
|
||||
</div>
|
||||
)}
|
||||
@@ -560,8 +560,8 @@ const ProjectAssets: React.FC = () => {
|
||||
<div id="asset-upload-dropzone">
|
||||
<Dragger name="file" multiple customRequest={customUpload} showUploadList={false} disabled={!workspaceId || !libraryId} style={{ marginBottom: 24 }}>
|
||||
<p className="ant-upload-drag-icon"><InboxOutlined /></p>
|
||||
<p className="ant-upload-text">????????????????</p>
|
||||
<p className="ant-upload-hint">?????????????????????? {uploading ? `(${activeUploadCount} ??????)` : '????????'}</p>
|
||||
<p className="ant-upload-text">点击或拖拽素材到这里上传</p>
|
||||
<p className="ant-upload-hint">支持视频、图片、配音素材批量上传 {uploading ? `(${activeUploadCount} 个文件上传中)` : '上传后自动导入并分类'}</p>
|
||||
</Dragger>
|
||||
</div>
|
||||
|
||||
@@ -575,25 +575,25 @@ const ProjectAssets: React.FC = () => {
|
||||
const isAutoClassifying = asset.metadata?.auto_classification === 'queued';
|
||||
return (
|
||||
<article className={`xx-vertical-card ${selected ? 'selected' : ''}`} key={asset.id} onClick={() => setSelectedRowKeys((keys) => selected ? keys.filter((key) => key !== asset.id) : [...keys, asset.id])}>
|
||||
<div className={`xx-vertical-thumb ${assetTone(asset, index)}`}><button className="xx-mini-play" type="button">?</button></div>
|
||||
<div className={`xx-vertical-thumb ${assetTone(asset, index)}`}><button className="xx-mini-play" type="button">▶</button></div>
|
||||
<div className="xx-vertical-row"><b title={asset.name}>{asset.name}</b><span className={`xx-pill ${pill.cls}`}>{pill.text}</span></div>
|
||||
<p className="xx-vertical-meta">{assetMeta(asset)}</p>
|
||||
<div style={{ marginTop: 8 }}>{renderClassification(asset)}</div>
|
||||
<div className="xx-action-row" style={{ marginTop: 10 }} onClick={(event) => event.stopPropagation()}>
|
||||
<Button size="small" icon={<TagsOutlined />} loading={isCurrentClassifying && classificationMutation.isPending} disabled={isAutoClassifying || (!!classifyingAssetId && classifyingAssetId !== asset.id) || batchProgress.running} onClick={() => { setClassifyingAssetId(asset.id); classificationMutation.mutate({ workspace_id: asset.workspace_id, project_id: asset.project_id, asset_id: asset.id }); }}>{isAutoClassifying ? '???' : hasClassification ? '???' : '??'}</Button>
|
||||
<Button size="small" disabled={reviewMutation.isPending} onClick={() => reviewMutation.mutate({ assetId: asset.id, reviewStatus: 'approved' })}>??</Button>
|
||||
<Button size="small" danger disabled={reviewMutation.isPending} onClick={() => reviewMutation.mutate({ assetId: asset.id, reviewStatus: 'rejected' })}>??</Button>
|
||||
<Button size="small" icon={<TagsOutlined />} loading={isCurrentClassifying && classificationMutation.isPending} disabled={isAutoClassifying || (!!classifyingAssetId && classifyingAssetId !== asset.id) || batchProgress.running} onClick={() => { setClassifyingAssetId(asset.id); classificationMutation.mutate({ workspace_id: asset.workspace_id, project_id: asset.project_id, asset_id: asset.id }); }}>{isAutoClassifying ? '排队中' : hasClassification ? '重分类' : '分类'}</Button>
|
||||
<Button size="small" disabled={reviewMutation.isPending} onClick={() => reviewMutation.mutate({ assetId: asset.id, reviewStatus: 'approved' })}>通过</Button>
|
||||
<Button size="small" danger disabled={reviewMutation.isPending} onClick={() => reviewMutation.mutate({ assetId: asset.id, reviewStatus: 'rejected' })}>驳回</Button>
|
||||
</div>
|
||||
</article>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
) : <div className="xx-empty-state"><div className="xx-empty-icon">+</div><b>?????</b><span>??????????? 9:16 ???</span></div>}
|
||||
) : <div className="xx-empty-state"><div className="xx-empty-icon">+</div><b>暂无素材</b><span>上传素材后会显示为 9:16 卡片。</span></div>}
|
||||
|
||||
<Modal title="?????" open={createLibraryOpen} onCancel={() => setCreateLibraryOpen(false)} onOk={() => form.submit()} confirmLoading={createLibraryMutation.isPending} okButtonProps={{ disabled: !workspaceId }}>
|
||||
<Modal title="新建素材库" open={createLibraryOpen} onCancel={() => setCreateLibraryOpen(false)} onOk={() => form.submit()} confirmLoading={createLibraryMutation.isPending} okButtonProps={{ disabled: !workspaceId }}>
|
||||
<Form form={form} layout="vertical" onFinish={(values) => createLibraryMutation.mutate({ workspace_id: workspaceId, project_id: projectId, name: values.name, kind: values.kind })}>
|
||||
<Form.Item label="?????" name="name" rules={[{ required: true, message: '????????' }]}><Input placeholder="????????" /></Form.Item>
|
||||
<Form.Item label="?????" name="kind" initialValue="video" rules={[{ required: true }]}><Select options={[{ label: '???', value: 'video' }, { label: '???', value: 'voice' }, { label: '???', value: 'image' }]} /></Form.Item>
|
||||
<Form.Item label="素材库名称" name="name" rules={[{ required: true, message: '请输入素材库名称' }]}><Input placeholder="例如:产品口播素材" /></Form.Item>
|
||||
<Form.Item label="素材类型" name="kind" initialValue="video" rules={[{ required: true }]}><Select options={[{ label: '视频', value: 'video' }, { label: '配音', value: 'voice' }, { label: '图片', value: 'image' }]} /></Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
</div>
|
||||
|
||||
@@ -180,11 +180,11 @@ const ProjectGeneration: React.FC = () => {
|
||||
<div className="xx-page-card">
|
||||
<div className="xx-page-head">
|
||||
<div>
|
||||
<h1>????</h1>
|
||||
<p>????????????????????????????</p>
|
||||
<h1>剪辑参数</h1>
|
||||
<p>选择素材、标题、模板和配音,先生成剪辑计划,再确认发起视频生成。</p>
|
||||
</div>
|
||||
<Button className="xx-btn-primary" type="primary" onClick={() => form.submit()} loading={generationMutation.isPending} disabled={!canSubmitGeneration || !editPlan}>
|
||||
???????
|
||||
确认计划并生成
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -193,31 +193,31 @@ const ProjectGeneration: React.FC = () => {
|
||||
style={{ marginBottom: 16 }}
|
||||
type="error"
|
||||
showIcon
|
||||
message="?????????"
|
||||
description="?????????????????????????????"
|
||||
message="缺少工作空间信息"
|
||||
description="请从项目详情页进入视频剪辑,系统需要确认当前工作空间和用户权限。"
|
||||
/>
|
||||
)}
|
||||
|
||||
{librariesQuery.isError && <Alert style={{ marginBottom: 16 }} type="error" showIcon message="???????" />}
|
||||
{taskQuery.isError && <Alert style={{ marginBottom: 16 }} type="error" showIcon message="????????" />}
|
||||
{resultsQuery.isError && <Alert style={{ marginBottom: 16 }} type="error" showIcon message="????????" />}
|
||||
{librariesQuery.isError && <Alert style={{ marginBottom: 16 }} type="error" showIcon message="素材库加载失败" />}
|
||||
{taskQuery.isError && <Alert style={{ marginBottom: 16 }} type="error" showIcon message="生成任务加载失败" />}
|
||||
{resultsQuery.isError && <Alert style={{ marginBottom: 16 }} type="error" showIcon message="生成结果加载失败" />}
|
||||
|
||||
{diagnosisQuery.data && (
|
||||
<Alert
|
||||
style={{ marginBottom: 16 }}
|
||||
type={hasCriticalGap ? 'error' : diagnosisQuery.data.gaps.length ? 'warning' : 'success'}
|
||||
showIcon
|
||||
message={`??????${diagnosisQuery.data.readiness_score} / ${diagnosisQuery.data.readiness_label}`}
|
||||
message={`素材就绪度:${diagnosisQuery.data.readiness_score} / ${diagnosisQuery.data.readiness_label}`}
|
||||
description={
|
||||
diagnosisQuery.data.gaps.length
|
||||
? diagnosisQuery.data.gaps.map((gap) => `${gap.message}?${gap.recommendation}`).join('?')
|
||||
: '????????????????'
|
||||
? diagnosisQuery.data.gaps.map((gap) => `${gap.message}:${gap.recommendation}`).join(';')
|
||||
: '素材状态良好,可以继续生成剪辑计划。'
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
{diagnosisQuery.isError && (
|
||||
<Alert style={{ marginBottom: 16 }} type="error" showIcon message="????????" description="????????????????????" />
|
||||
<Alert style={{ marginBottom: 16 }} type="error" showIcon message="素材诊断加载失败" description="请稍后刷新页面重试。" />
|
||||
)}
|
||||
|
||||
<div className="xx-generation-layout">
|
||||
@@ -238,29 +238,29 @@ const ProjectGeneration: React.FC = () => {
|
||||
}
|
||||
>
|
||||
<div className="xx-section-block">
|
||||
<div className="xx-section-title"><h3>????</h3><span className="xx-hint">????+B-roll</span></div>
|
||||
<Form.Item label="???" name="asset_library_id" rules={[{ required: true, message: '??????' }]}>
|
||||
<Select options={assetLibraryOptions} placeholder="????/?????" loading={librariesQuery.isLoading} />
|
||||
<div className="xx-section-title"><h3>素材与画面</h3><span className="xx-hint">主素材 + B-roll</span></div>
|
||||
<Form.Item label="素材库" name="asset_library_id" rules={[{ required: true, message: '请选择素材库' }]}>
|
||||
<Select options={assetLibraryOptions} placeholder="选择视频/图片素材库" loading={librariesQuery.isLoading} />
|
||||
</Form.Item>
|
||||
</div>
|
||||
|
||||
<div className="xx-section-block">
|
||||
<div className="xx-section-title"><h3>?????</h3></div>
|
||||
<Form.Item label="????" name="title_id">
|
||||
<Select allowClear options={titleOptions} placeholder="?????????" loading={titlesQuery.isLoading} />
|
||||
<div className="xx-section-title"><h3>标题与模板</h3></div>
|
||||
<Form.Item label="标题" name="title_id">
|
||||
<Select allowClear options={titleOptions} placeholder="选择标题库中的标题" loading={titlesQuery.isLoading} />
|
||||
</Form.Item>
|
||||
<Form.Item label="????" name="template_id">
|
||||
<Select allowClear options={templateOptions} placeholder="??????" loading={templatesQuery.isLoading} />
|
||||
<Form.Item label="剪辑模板" name="template_id">
|
||||
<Select allowClear options={templateOptions} placeholder="选择剪辑模板" loading={templatesQuery.isLoading} />
|
||||
</Form.Item>
|
||||
</div>
|
||||
|
||||
<div className="xx-section-block">
|
||||
<div className="xx-section-title"><h3>??</h3></div>
|
||||
<Form.Item label="???" name="voice_library_id">
|
||||
<Select allowClear options={voiceLibraryOptions} placeholder="?????" loading={librariesQuery.isLoading} />
|
||||
<div className="xx-section-title"><h3>配音与策略</h3></div>
|
||||
<Form.Item label="配音库" name="voice_library_id">
|
||||
<Select allowClear options={voiceLibraryOptions} placeholder="选择配音库" loading={librariesQuery.isLoading} />
|
||||
</Form.Item>
|
||||
<Form.Item label="?? ID" name="strategy_id">
|
||||
<Input placeholder="????? default-strategy" />
|
||||
<Form.Item label="策略 ID" name="strategy_id">
|
||||
<Input placeholder="可选,例如 default-strategy" />
|
||||
</Form.Item>
|
||||
</div>
|
||||
</Form>
|
||||
@@ -268,10 +268,10 @@ const ProjectGeneration: React.FC = () => {
|
||||
|
||||
<aside className="xx-plan-panel">
|
||||
<div className="xx-preview-phone">
|
||||
<span className="xx-play">?</span>
|
||||
<div>3 ???????30 ?????</div>
|
||||
<span className="xx-play">▶</span>
|
||||
<div>3 段素材 · 约 30 秒成片</div>
|
||||
</div>
|
||||
<h3>??????</h3>
|
||||
<h3>剪辑计划预览</h3>
|
||||
{editPlan ? (
|
||||
<>
|
||||
<Typography.Paragraph>{editPlan.summary}</Typography.Paragraph>
|
||||
@@ -280,7 +280,7 @@ const ProjectGeneration: React.FC = () => {
|
||||
dataSource={editPlan.clips}
|
||||
renderItem={(clip) => (
|
||||
<List.Item>
|
||||
<Typography.Text>{clip.sequence}. {clip.asset_name} / {Math.round(clip.duration)}? / {clip.reason}</Typography.Text>
|
||||
<Typography.Text>{clip.sequence}. {clip.asset_name} / {Math.round(clip.duration)}秒 / {clip.reason}</Typography.Text>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
@@ -288,8 +288,8 @@ const ProjectGeneration: React.FC = () => {
|
||||
) : (
|
||||
<div className="xx-empty-state" style={{ padding: 18 }}>
|
||||
<div className="xx-empty-icon">AI</div>
|
||||
<b>???????</b>
|
||||
<span>?????????????????</span>
|
||||
<b>还没有剪辑计划</b>
|
||||
<span>先选择素材库和模板,生成计划后再确认生成视频。</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="xx-action-row" style={{ marginTop: 16 }}>
|
||||
@@ -297,7 +297,7 @@ const ProjectGeneration: React.FC = () => {
|
||||
onClick={() => {
|
||||
const values = form.getFieldsValue();
|
||||
if (!values.asset_library_id) {
|
||||
message.warning('???????');
|
||||
message.warning('请先选择素材库');
|
||||
return;
|
||||
}
|
||||
editPlanMutation.mutate({
|
||||
@@ -313,10 +313,10 @@ const ProjectGeneration: React.FC = () => {
|
||||
loading={editPlanMutation.isPending}
|
||||
disabled={!canSubmitGeneration}
|
||||
>
|
||||
??????
|
||||
重新生成计划
|
||||
</Button>
|
||||
<Button className="xx-btn-primary" type="primary" onClick={() => form.submit()} loading={generationMutation.isPending} disabled={!canSubmitGeneration || !editPlan}>
|
||||
???????
|
||||
确认计划并生成
|
||||
</Button>
|
||||
</div>
|
||||
</aside>
|
||||
@@ -326,19 +326,19 @@ const ProjectGeneration: React.FC = () => {
|
||||
<Alert
|
||||
style={{ marginTop: 16 }}
|
||||
type={taskQuery.data.status === 'failed' ? 'error' : taskQuery.data.status === 'completed' ? 'success' : 'info'}
|
||||
message={`?????${humanizeGenerationStatus(taskQuery.data.status)}`}
|
||||
description={taskQuery.data.status === 'failed' ? failedReason : `?? ${taskQuery.data.progress}% / ??? ${taskQuery.data.result_count}`}
|
||||
message={`生成状态:${humanizeGenerationStatus(taskQuery.data.status)}`}
|
||||
description={taskQuery.data.status === 'failed' ? failedReason : `进度 ${taskQuery.data.progress}% / 结果 ${taskQuery.data.result_count}`}
|
||||
/>
|
||||
)}
|
||||
|
||||
{resultsQuery.data?.length ? (
|
||||
<div className="xx-section-block" style={{ marginTop: 16 }}>
|
||||
<div className="xx-section-title"><h3>??????</h3></div>
|
||||
<div className="xx-section-title"><h3>生成结果</h3></div>
|
||||
<Space direction="vertical">
|
||||
{resultsQuery.data.map((item) => (
|
||||
<Space key={item.id}>
|
||||
<Typography.Text>{item.name}</Typography.Text>
|
||||
<Button type="link" onClick={() => openSignedDownload(item.id)}>??/??</Button>
|
||||
<Button type="link" onClick={() => openSignedDownload(item.id)}>预览/下载</Button>
|
||||
</Space>
|
||||
))}
|
||||
</Space>
|
||||
|
||||
Reference in New Issue
Block a user