style: fix prettier formatting in 3 frontend files
CI/CD Pipeline / Build Production Runtime Images (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Staging E2E Tests (push) Failing after 26h54m44s
CI/CD Pipeline / Build & Push Staging (Watchtower auto-deploy) (push) Failing after 27h0m5s
CI/CD Pipeline / Frontend Lint (push) Failing after 27h1m2s
CI/CD Pipeline / Validate Code Quality And Tests (push) Failing after 27h1m2s
CI/CD Pipeline / Build Production Runtime Images (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Staging E2E Tests (push) Failing after 26h54m44s
CI/CD Pipeline / Build & Push Staging (Watchtower auto-deploy) (push) Failing after 27h0m5s
CI/CD Pipeline / Frontend Lint (push) Failing after 27h1m2s
CI/CD Pipeline / Validate Code Quality And Tests (push) Failing after 27h1m2s
- AssetLibrary.tsx - GeneratePage.tsx - VoiceLibrary.tsx This fixes the CI Frontend Lint job failure.
This commit is contained in:
@@ -220,7 +220,15 @@ const AssetCard: React.FC<{
|
||||
onDiagnose: () => void;
|
||||
onPlay: () => void;
|
||||
onDelete: () => void;
|
||||
}> = ({ asset, selected, diagnosing, onToggle, onDiagnose, onPlay, onDelete }) => (
|
||||
}> = ({
|
||||
asset,
|
||||
selected,
|
||||
diagnosing,
|
||||
onToggle,
|
||||
onDiagnose,
|
||||
onPlay,
|
||||
onDelete,
|
||||
}) => (
|
||||
<div
|
||||
className={`xx-asset-card${selected ? " xx-asset-card-selected" : ""}`}
|
||||
onClick={onToggle}
|
||||
@@ -264,10 +272,7 @@ const AssetCard: React.FC<{
|
||||
cancelText="取消"
|
||||
okButtonProps={{ danger: true }}
|
||||
>
|
||||
<span
|
||||
className="xx-asset-delete"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<span className="xx-asset-delete" onClick={(e) => e.stopPropagation()}>
|
||||
<DeleteOutlined />
|
||||
</span>
|
||||
</Popconfirm>
|
||||
|
||||
@@ -25,7 +25,11 @@ import {
|
||||
} from "@ant-design/icons";
|
||||
import type { AssetItem } from "@/api/assets";
|
||||
import { getAssets, getAssetLibraries } from "@/api/assets";
|
||||
import { createEditPlan, generateEditPlan, updateEditPlan } from "@/api/editPlans";
|
||||
import {
|
||||
createEditPlan,
|
||||
generateEditPlan,
|
||||
updateEditPlan,
|
||||
} from "@/api/editPlans";
|
||||
import { getEditingTemplates } from "@/api/editingPlanner";
|
||||
import { getTitles } from "@/api/titles";
|
||||
import apiClient from "@/api/client";
|
||||
@@ -655,26 +659,46 @@ const GeneratePage: React.FC = () => {
|
||||
const translateError = (msg: string): string => {
|
||||
if (!msg) return "生成失败,请检查网络后重试或联系管理员";
|
||||
// 状态机相关错误
|
||||
if (msg.includes("editing") || msg.includes("draft") || msg.includes("状态")) {
|
||||
if (
|
||||
msg.includes("editing") ||
|
||||
msg.includes("draft") ||
|
||||
msg.includes("状态")
|
||||
) {
|
||||
return "正在准备生成,请稍候再试";
|
||||
}
|
||||
// 参数校验错误
|
||||
if (msg.includes("template_id") || msg.includes("not found") || msg.includes("不存在")) {
|
||||
if (
|
||||
msg.includes("template_id") ||
|
||||
msg.includes("not found") ||
|
||||
msg.includes("不存在")
|
||||
) {
|
||||
return "所选模板或素材不可用,请重新选择";
|
||||
}
|
||||
if (msg.includes("asset") && (msg.includes("not found") || msg.includes("missing"))) {
|
||||
if (
|
||||
msg.includes("asset") &&
|
||||
(msg.includes("not found") || msg.includes("missing"))
|
||||
) {
|
||||
return "素材数据异常,请返回素材库重新检查";
|
||||
}
|
||||
// 网络/超时
|
||||
if (msg.includes("timeout") || msg.includes("network") || msg.includes("ECONN")) {
|
||||
if (
|
||||
msg.includes("timeout") ||
|
||||
msg.includes("network") ||
|
||||
msg.includes("ECONN")
|
||||
) {
|
||||
return "网络连接超时,请检查网络后重试";
|
||||
}
|
||||
// 配额/限制
|
||||
if (msg.includes("quota") || msg.includes("limit") || msg.includes("exceed")) {
|
||||
if (
|
||||
msg.includes("quota") ||
|
||||
msg.includes("limit") ||
|
||||
msg.includes("exceed")
|
||||
) {
|
||||
return "已达到生成次数上限,请稍后再试或联系客服";
|
||||
}
|
||||
// 兜底:返回原始消息(如果已经是中文人话)或默认提示
|
||||
if (msg.length > 0 && msg.length < 100 && !msg.includes("{")) return msg;
|
||||
if (msg.length > 0 && msg.length < 100 && !msg.includes("{"))
|
||||
return msg;
|
||||
return "生成失败,请稍后重试或联系管理员";
|
||||
};
|
||||
const finalMsg = translateError(rawError);
|
||||
|
||||
@@ -45,11 +45,7 @@ import {
|
||||
toVoiceClone,
|
||||
type VoiceClone,
|
||||
} from "@/api/voiceClone";
|
||||
import {
|
||||
synthesizeSpeech,
|
||||
getTTSJobStatus,
|
||||
saveTtsToLibrary,
|
||||
} from "@/api/tts";
|
||||
import { synthesizeSpeech, getTTSJobStatus, saveTtsToLibrary } from "@/api/tts";
|
||||
import {
|
||||
uploadAssetDirect,
|
||||
getAssetLibraries,
|
||||
@@ -673,7 +669,9 @@ const VoiceLibrary: React.FC = () => {
|
||||
const [ttsVoiceId, setTtsVoiceId] = useState<string>("");
|
||||
const [ttsSpeed, setTtsSpeed] = useState(1.0);
|
||||
const [ttsJobId, setTtsJobId] = useState<string | null>(null);
|
||||
const [ttsStatus, setTtsStatus] = useState<"idle" | "synthesizing" | "done" | "error">("idle");
|
||||
const [ttsStatus, setTtsStatus] = useState<
|
||||
"idle" | "synthesizing" | "done" | "error"
|
||||
>("idle");
|
||||
const [ttsAudioUrl, setTtsAudioUrl] = useState<string | null>(null);
|
||||
const [ttsError, setTtsError] = useState<string | null>(null);
|
||||
const ttsTimerRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
||||
@@ -1222,7 +1220,14 @@ const VoiceLibrary: React.FC = () => {
|
||||
width={520}
|
||||
maskClosable={uploadProgress === null}
|
||||
>
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: 16, padding: "8px 0" }}>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: 16,
|
||||
padding: "8px 0",
|
||||
}}
|
||||
>
|
||||
{/* 拖拽上传区 */}
|
||||
<Upload.Dragger
|
||||
accept="audio/*"
|
||||
@@ -1239,30 +1244,54 @@ const VoiceLibrary: React.FC = () => {
|
||||
showUploadList={false}
|
||||
disabled={uploadProgress !== null}
|
||||
>
|
||||
<p style={{ fontSize: 32, color: "var(--primary-color)", marginBottom: 8 }}>
|
||||
<p
|
||||
style={{
|
||||
fontSize: 32,
|
||||
color: "var(--primary-color)",
|
||||
marginBottom: 8,
|
||||
}}
|
||||
>
|
||||
<UploadOutlined />
|
||||
</p>
|
||||
<p style={{ fontSize: 14, fontWeight: 500, margin: "0 0 4px" }}>
|
||||
点击或拖拽音频文件到此处
|
||||
</p>
|
||||
<p style={{ fontSize: 12, color: "var(--text-secondary)", margin: 0 }}>
|
||||
<p
|
||||
style={{
|
||||
fontSize: 12,
|
||||
color: "var(--text-secondary)",
|
||||
margin: 0,
|
||||
}}
|
||||
>
|
||||
支持 MP3、WAV、AAC、FLAC 等格式,最大 200MB
|
||||
</p>
|
||||
</Upload.Dragger>
|
||||
|
||||
{/* 已选文件信息 */}
|
||||
{uploadFile && (
|
||||
<div style={{
|
||||
padding: "10px 12px",
|
||||
background: "var(--bg-secondary)",
|
||||
borderRadius: 8,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 10,
|
||||
}}>
|
||||
<SoundOutlined style={{ fontSize: 18, color: "var(--primary-color)" }} />
|
||||
<div
|
||||
style={{
|
||||
padding: "10px 12px",
|
||||
background: "var(--bg-secondary)",
|
||||
borderRadius: 8,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 10,
|
||||
}}
|
||||
>
|
||||
<SoundOutlined
|
||||
style={{ fontSize: 18, color: "var(--primary-color)" }}
|
||||
/>
|
||||
<div style={{ flex: 1, minWidth: 0 }}>
|
||||
<div style={{ fontSize: 13, fontWeight: 500, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
|
||||
<div
|
||||
style={{
|
||||
fontSize: 13,
|
||||
fontWeight: 500,
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
whiteSpace: "nowrap",
|
||||
}}
|
||||
>
|
||||
{uploadFile.name}
|
||||
</div>
|
||||
<div style={{ fontSize: 11, color: "var(--text-secondary)" }}>
|
||||
@@ -1275,33 +1304,49 @@ const VoiceLibrary: React.FC = () => {
|
||||
{/* 上传进度 */}
|
||||
{uploadProgress !== null && (
|
||||
<div style={{ textAlign: "center", padding: "8px 0" }}>
|
||||
<div style={{ fontSize: 22, fontWeight: 700, color: "var(--primary-color)" }}>
|
||||
<div
|
||||
style={{
|
||||
fontSize: 22,
|
||||
fontWeight: 700,
|
||||
color: "var(--primary-color)",
|
||||
}}
|
||||
>
|
||||
{uploadProgress}%
|
||||
</div>
|
||||
<div style={{ fontSize: 12, color: "var(--text-secondary)" }}>
|
||||
{uploadProgress < 100 ? "上传中..." : "处理中..."}
|
||||
</div>
|
||||
<div style={{
|
||||
height: 4,
|
||||
background: "var(--bg-tertiary)",
|
||||
borderRadius: 2,
|
||||
marginTop: 8,
|
||||
overflow: "hidden",
|
||||
}}>
|
||||
<div style={{
|
||||
height: "100%",
|
||||
width: `${uploadProgress}%`,
|
||||
background: "var(--primary-color)",
|
||||
<div
|
||||
style={{
|
||||
height: 4,
|
||||
background: "var(--bg-tertiary)",
|
||||
borderRadius: 2,
|
||||
transition: "width 0.3s ease",
|
||||
}} />
|
||||
marginTop: 8,
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
height: "100%",
|
||||
width: `${uploadProgress}%`,
|
||||
background: "var(--primary-color)",
|
||||
borderRadius: 2,
|
||||
transition: "width 0.3s ease",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 名称 */}
|
||||
<div>
|
||||
<div style={{ fontSize: 13, color: "var(--text-secondary)", marginBottom: 6 }}>
|
||||
<div
|
||||
style={{
|
||||
fontSize: 13,
|
||||
color: "var(--text-secondary)",
|
||||
marginBottom: 6,
|
||||
}}
|
||||
>
|
||||
素材名称
|
||||
</div>
|
||||
<input
|
||||
@@ -1325,7 +1370,13 @@ const VoiceLibrary: React.FC = () => {
|
||||
|
||||
{/* 性别选择 */}
|
||||
<div>
|
||||
<div style={{ fontSize: 13, color: "var(--text-secondary)", marginBottom: 6 }}>
|
||||
<div
|
||||
style={{
|
||||
fontSize: 13,
|
||||
color: "var(--text-secondary)",
|
||||
marginBottom: 6,
|
||||
}}
|
||||
>
|
||||
音色性别
|
||||
</div>
|
||||
<div style={{ display: "flex", gap: 8 }}>
|
||||
@@ -1340,8 +1391,14 @@ const VoiceLibrary: React.FC = () => {
|
||||
padding: "6px 0",
|
||||
borderRadius: 8,
|
||||
border: `1px solid ${uploadGender === g ? "var(--primary-color)" : "var(--border-color)"}`,
|
||||
background: uploadGender === g ? "var(--primary-soft)" : "transparent",
|
||||
color: uploadGender === g ? "var(--primary-color)" : "var(--text-secondary)",
|
||||
background:
|
||||
uploadGender === g
|
||||
? "var(--primary-soft)"
|
||||
: "transparent",
|
||||
color:
|
||||
uploadGender === g
|
||||
? "var(--primary-color)"
|
||||
: "var(--text-secondary)",
|
||||
fontSize: 13,
|
||||
fontWeight: uploadGender === g ? 600 : 400,
|
||||
cursor: uploadProgress !== null ? "not-allowed" : "pointer",
|
||||
@@ -1356,7 +1413,13 @@ const VoiceLibrary: React.FC = () => {
|
||||
|
||||
{/* 描述 */}
|
||||
<div>
|
||||
<div style={{ fontSize: 13, color: "var(--text-secondary)", marginBottom: 6 }}>
|
||||
<div
|
||||
style={{
|
||||
fontSize: 13,
|
||||
color: "var(--text-secondary)",
|
||||
marginBottom: 6,
|
||||
}}
|
||||
>
|
||||
音色描述(可选)
|
||||
</div>
|
||||
<textarea
|
||||
@@ -1382,7 +1445,14 @@ const VoiceLibrary: React.FC = () => {
|
||||
</div>
|
||||
|
||||
{/* 操作按钮 */}
|
||||
<div style={{ display: "flex", justifyContent: "flex-end", gap: 10, paddingTop: 4 }}>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "flex-end",
|
||||
gap: 10,
|
||||
paddingTop: 4,
|
||||
}}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
@@ -1422,20 +1492,24 @@ const VoiceLibrary: React.FC = () => {
|
||||
description: uploadDesc.trim(),
|
||||
});
|
||||
}}
|
||||
disabled={!uploadFile || !uploadName.trim() || uploadProgress !== null}
|
||||
disabled={
|
||||
!uploadFile || !uploadName.trim() || uploadProgress !== null
|
||||
}
|
||||
style={{
|
||||
padding: "8px 20px",
|
||||
borderRadius: 8,
|
||||
border: "none",
|
||||
background: (!uploadFile || !uploadName.trim() || uploadProgress !== null)
|
||||
? "var(--text-tertiary)"
|
||||
: "var(--primary-color)",
|
||||
background:
|
||||
!uploadFile || !uploadName.trim() || uploadProgress !== null
|
||||
? "var(--text-tertiary)"
|
||||
: "var(--primary-color)",
|
||||
color: "#fff",
|
||||
fontSize: 13,
|
||||
fontWeight: 600,
|
||||
cursor: (!uploadFile || !uploadName.trim() || uploadProgress !== null)
|
||||
? "not-allowed"
|
||||
: "pointer",
|
||||
cursor:
|
||||
!uploadFile || !uploadName.trim() || uploadProgress !== null
|
||||
? "not-allowed"
|
||||
: "pointer",
|
||||
}}
|
||||
>
|
||||
{uploadProgress !== null ? "上传中..." : "开始上传"}
|
||||
@@ -1461,10 +1535,23 @@ const VoiceLibrary: React.FC = () => {
|
||||
footer={null}
|
||||
width={560}
|
||||
>
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: 16, padding: "8px 0" }}>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: 16,
|
||||
padding: "8px 0",
|
||||
}}
|
||||
>
|
||||
{/* 文本输入 */}
|
||||
<div>
|
||||
<div style={{ fontSize: 13, color: "var(--text-secondary)", marginBottom: 6 }}>
|
||||
<div
|
||||
style={{
|
||||
fontSize: 13,
|
||||
color: "var(--text-secondary)",
|
||||
marginBottom: 6,
|
||||
}}
|
||||
>
|
||||
输入文本
|
||||
</div>
|
||||
<textarea
|
||||
@@ -1487,14 +1574,27 @@ const VoiceLibrary: React.FC = () => {
|
||||
lineHeight: 1.6,
|
||||
}}
|
||||
/>
|
||||
<div style={{ fontSize: 11, color: "var(--text-tertiary)", textAlign: "right", marginTop: 4 }}>
|
||||
<div
|
||||
style={{
|
||||
fontSize: 11,
|
||||
color: "var(--text-tertiary)",
|
||||
textAlign: "right",
|
||||
marginTop: 4,
|
||||
}}
|
||||
>
|
||||
{ttsText.length}/2000
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 音色选择 */}
|
||||
<div>
|
||||
<div style={{ fontSize: 13, color: "var(--text-secondary)", marginBottom: 6 }}>
|
||||
<div
|
||||
style={{
|
||||
fontSize: 13,
|
||||
color: "var(--text-secondary)",
|
||||
marginBottom: 6,
|
||||
}}
|
||||
>
|
||||
选择音色
|
||||
</div>
|
||||
<select
|
||||
@@ -1522,7 +1622,13 @@ const VoiceLibrary: React.FC = () => {
|
||||
|
||||
{/* 语速 */}
|
||||
<div>
|
||||
<div style={{ fontSize: 13, color: "var(--text-secondary)", marginBottom: 6 }}>
|
||||
<div
|
||||
style={{
|
||||
fontSize: 13,
|
||||
color: "var(--text-secondary)",
|
||||
marginBottom: 6,
|
||||
}}
|
||||
>
|
||||
语速:{ttsSpeed.toFixed(1)}x
|
||||
</div>
|
||||
<input
|
||||
@@ -1534,7 +1640,14 @@ const VoiceLibrary: React.FC = () => {
|
||||
onChange={(e) => setTtsSpeed(parseFloat(e.target.value))}
|
||||
style={{ width: "100%", accentColor: "var(--primary-color)" }}
|
||||
/>
|
||||
<div style={{ display: "flex", justifyContent: "space-between", fontSize: 11, color: "var(--text-tertiary)" }}>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
fontSize: 11,
|
||||
color: "var(--text-tertiary)",
|
||||
}}
|
||||
>
|
||||
<span>0.5x</span>
|
||||
<span>1.0x</span>
|
||||
<span>2.0x</span>
|
||||
@@ -1551,15 +1664,17 @@ const VoiceLibrary: React.FC = () => {
|
||||
padding: "10px 0",
|
||||
borderRadius: 8,
|
||||
border: "none",
|
||||
background: (ttsStatus === "synthesizing" || !ttsText.trim())
|
||||
? "var(--text-tertiary)"
|
||||
: "var(--primary-color)",
|
||||
background:
|
||||
ttsStatus === "synthesizing" || !ttsText.trim()
|
||||
? "var(--text-tertiary)"
|
||||
: "var(--primary-color)",
|
||||
color: "#fff",
|
||||
fontSize: 14,
|
||||
fontWeight: 600,
|
||||
cursor: (ttsStatus === "synthesizing" || !ttsText.trim())
|
||||
? "not-allowed"
|
||||
: "pointer",
|
||||
cursor:
|
||||
ttsStatus === "synthesizing" || !ttsText.trim()
|
||||
? "not-allowed"
|
||||
: "pointer",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
@@ -1572,28 +1687,38 @@ const VoiceLibrary: React.FC = () => {
|
||||
|
||||
{/* 错误提示 */}
|
||||
{ttsError && (
|
||||
<div style={{
|
||||
padding: "10px 12px",
|
||||
background: "var(--error-soft, #fff2f0)",
|
||||
borderRadius: 8,
|
||||
color: "var(--error-color, #ff4d4f)",
|
||||
fontSize: 13,
|
||||
}}>
|
||||
<div
|
||||
style={{
|
||||
padding: "10px 12px",
|
||||
background: "var(--error-soft, #fff2f0)",
|
||||
borderRadius: 8,
|
||||
color: "var(--error-color, #ff4d4f)",
|
||||
fontSize: 13,
|
||||
}}
|
||||
>
|
||||
{ttsError}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 合成结果 */}
|
||||
{ttsStatus === "done" && ttsAudioUrl && (
|
||||
<div style={{
|
||||
padding: 12,
|
||||
background: "var(--bg-secondary)",
|
||||
borderRadius: 8,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: 10,
|
||||
}}>
|
||||
<div style={{ fontSize: 13, fontWeight: 500, color: "var(--success-color, #52c41a)" }}>
|
||||
<div
|
||||
style={{
|
||||
padding: 12,
|
||||
background: "var(--bg-secondary)",
|
||||
borderRadius: 8,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: 10,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
fontSize: 13,
|
||||
fontWeight: 500,
|
||||
color: "var(--success-color, #52c41a)",
|
||||
}}
|
||||
>
|
||||
✅ 合成完成
|
||||
</div>
|
||||
<audio controls src={ttsAudioUrl} style={{ width: "100%" }} />
|
||||
|
||||
Reference in New Issue
Block a user