From 4120f37c94e26d9b2a2347ee84df368fba958a64 Mon Sep 17 00:00:00 2001
From: "949036910@qq.com" <>
Date: Sun, 31 May 2026 22:19:51 +0800
Subject: [PATCH] 11
---
.../workflow/MixCutSettingPanel.vue | 23 ++++
src/components/workflow/Step03VideoEdit.vue | 15 +--
.../workflow/Step05SubtitleMusic.vue | 5 +-
src/services/mixCutSettings.js | 109 +++++++++++++++++-
src/services/subtitleBgmGenerate.js | 4 +-
src/services/talkingVideoGenerate.js | 21 +++-
src/services/videoEditProcess.js | 5 +-
src/stores/workflow.js | 2 +-
8 files changed, 167 insertions(+), 17 deletions(-)
diff --git a/src/components/workflow/MixCutSettingPanel.vue b/src/components/workflow/MixCutSettingPanel.vue
index d3e3767..af4f125 100644
--- a/src/components/workflow/MixCutSettingPanel.vue
+++ b/src/components/workflow/MixCutSettingPanel.vue
@@ -8,11 +8,13 @@ import {
findMappingForLine,
loadMixCutSubtitleRecords,
openSubtitleFileAndParse,
+ persistSubtitleRecordsToMixCut,
pickLocalMixCutMaterial,
pickMaterialFolder,
prepareMixCutSettingsForSave,
removeMaterialFromSubtitleLine,
} from "../../services/mixCutSettings.js";
+import { revealLocalFileInFolder } from "../../services/fileExport.js";
import {
createDefaultGlobalDisplay,
createDefaultMixCutSettings,
@@ -120,6 +122,10 @@ async function loadExistingSubtitleLines() {
if (result.srtPath) subtitleFilePath.value = result.srtPath;
settings.value.records = result.records;
if (result.srtPath) settings.value.subtitleFilePath = result.srtPath;
+ await persistSubtitleRecordsToMixCut(props.store, {
+ records: result.records,
+ srtPath: result.srtPath,
+ });
const msg =
result.message ||
`字幕加载成功,共 ${result.records.length} 条`;
@@ -131,6 +137,19 @@ async function loadExistingSubtitleLines() {
}
async function onOpenSubtitleFile() {
+ const currentPath = subtitleFilePath.value || props.store.subtitleSrtPath;
+ if (currentPath) {
+ try {
+ await revealLocalFileInFolder(currentPath);
+ } catch (err) {
+ showFeedback(
+ "error",
+ err instanceof Error ? err.message : "打开文件夹失败",
+ );
+ }
+ return;
+ }
+
const result = await openSubtitleFileAndParse();
if (!result.message && !result.ok) return;
if (!result.ok) {
@@ -141,6 +160,10 @@ async function onOpenSubtitleFile() {
subtitleFilePath.value = result.srtPath || "";
settings.value.records = result.records;
settings.value.subtitleFilePath = result.srtPath || "";
+ await persistSubtitleRecordsToMixCut(props.store, {
+ records: result.records,
+ srtPath: result.srtPath,
+ });
showFeedback("success", `已加载 ${result.records.length} 行字幕`);
}
diff --git a/src/components/workflow/Step03VideoEdit.vue b/src/components/workflow/Step03VideoEdit.vue
index fa77d34..d90e18b 100644
--- a/src/components/workflow/Step03VideoEdit.vue
+++ b/src/components/workflow/Step03VideoEdit.vue
@@ -12,8 +12,9 @@ const {
greenScreen,
videoProcessing,
greenScreenBackgroundPath,
- generatedVideoSrc,
generatedVideoPath,
+ processedVideoSrc,
+ processedVideoPath,
} = storeToRefs(workflow);
const feedback = ref({ severity: "", message: "" });
@@ -56,12 +57,12 @@ async function onPickPipMaterial() {
}
async function onOpenVideoInExplorer() {
- if (!generatedVideoPath.value) {
- feedback.value = { severity: "error", message: "暂无视频文件" };
+ if (!processedVideoPath.value) {
+ feedback.value = { severity: "error", message: "暂无编辑后的视频文件" };
return;
}
try {
- await revealLocalFileInFolder(generatedVideoPath.value);
+ await revealLocalFileInFolder(processedVideoPath.value);
} catch (err) {
feedback.value = {
severity: "error",
@@ -146,15 +147,15 @@ async function onOpenVideoInExplorer() {
size="small"
severity="secondary"
text
- :disabled="!generatedVideoPath"
+ :disabled="!processedVideoPath"
aria-label="在资源管理器中打开"
@click="onOpenVideoInExplorer"
/>
diff --git a/src/components/workflow/Step05SubtitleMusic.vue b/src/components/workflow/Step05SubtitleMusic.vue
index bf0f2ac..ab8870f 100644
--- a/src/components/workflow/Step05SubtitleMusic.vue
+++ b/src/components/workflow/Step05SubtitleMusic.vue
@@ -14,6 +14,7 @@ const {
subtitleTemplateId,
subtitleBgmGenerating,
generatedVideoPath,
+ processedVideoPath,
subtitlePreviewVideoPath,
subtitlePreviewVideoSrc,
bgmPreviewSrc,
@@ -22,7 +23,9 @@ const {
const feedback = ref({ severity: "", message: "" });
const templateDialogVisible = ref(false);
-const hasSourceVideo = computed(() => Boolean(generatedVideoPath.value));
+const hasSourceVideo = computed(() =>
+ Boolean(processedVideoPath.value || generatedVideoPath.value),
+);
const hasSubtitleVideoPreview = computed(() =>
Boolean(subtitlePreviewVideoSrc.value),
);
diff --git a/src/services/mixCutSettings.js b/src/services/mixCutSettings.js
index ac67f36..01b8c05 100644
--- a/src/services/mixCutSettings.js
+++ b/src/services/mixCutSettings.js
@@ -70,6 +70,108 @@ async function readTextFile(filePath) {
* @param {number} durationSec
* @returns {Array<{ text: string, start: number, end: number }>}
*/
+/**
+ * @param {Array