This commit is contained in:
949036910@qq.com
2026-05-30 18:45:08 +08:00
commit 16b9a007a6
150 changed files with 26852 additions and 0 deletions

View File

@@ -0,0 +1,158 @@
<script setup>
import { computed, ref, watch } from "vue";
import TitleLineStyleEditor from "./TitleLineStyleEditor.vue";
import { syncTitleLinesFromReference } from "../../utils/subtitleTemplateDraft.js";
const config = defineModel("config", { type: Object, required: true });
const props = defineProps({
referenceTitle: { type: String, default: "" },
});
const titleConfig = computed({
get: () => config.value.titleSubtitleConfig,
set: (v) => {
config.value.titleSubtitleConfig = v;
},
});
const enabled = computed({
get: () => titleConfig.value?.enabled !== false,
set: (v) => {
titleConfig.value.enabled = v;
},
});
const openLineIndex = ref(0);
const displayTitle = computed(
() => props.referenceTitle?.trim() || titleConfig.value?.sourceTitle || "(暂无标题,请先在步骤 04 生成)",
);
function reloadFromReference() {
syncTitleLinesFromReference(
{ config: config.value },
props.referenceTitle || titleConfig.value?.sourceTitle,
);
}
const linePreviews = computed(() => titleConfig.value?.lines || []);
const maxCharMarks = [8, 12, 15, 20];
function markLeft(value) {
return `${((value - 8) / (20 - 8)) * 100}%`;
}
watch(
() => titleConfig.value?.maxCharsPerLine,
() => {
reloadFromReference();
},
);
</script>
<template>
<div class="title-subtitle-settings space-y-4 rounded-lg border border-slate-600/40 bg-slate-800/30 p-4">
<div class="flex items-center gap-3">
<Checkbox v-model="enabled" binary />
<span class="font-medium text-slate-100">标题字幕</span>
</div>
<div v-if="enabled" class="space-y-4">
<div>
<div class="mb-2 text-xs text-slate-400">
当前标题自动读取步骤 04标题标签关键词生成结果
</div>
<div class="flex flex-wrap items-center gap-2 rounded bg-slate-900/50 p-3">
<div class="flex-1 text-sm text-slate-200">{{ displayTitle }}</div>
<Button label="重新加载" size="small" severity="secondary" @click="reloadFromReference" />
</div>
</div>
<div class="space-y-4">
<div class="flex items-center gap-3">
<div class="w-28 shrink-0 text-sm text-slate-300">每行最大字符数</div>
<div class="relative flex-1 pb-5">
<Slider
v-model="titleConfig.maxCharsPerLine"
:min="8"
:max="20"
:step="1"
class="w-full"
/>
<div class="pointer-events-none absolute inset-x-0 top-6 text-xs text-slate-500">
<span
v-for="mark in maxCharMarks"
:key="mark"
class="absolute -translate-x-1/2"
:style="{ left: markLeft(mark) }"
>
{{ mark }}
</span>
</div>
</div>
</div>
<div class="flex items-center gap-3">
<div class="w-28 shrink-0 text-sm text-slate-300">行间距%</div>
<InputNumber
v-model="titleConfig.lineSpacingPercent"
:min="3"
:max="15"
class="w-32"
/>
<span class="text-xs text-slate-500">相对于视频高度</span>
</div>
<div class="flex items-center gap-3">
<div class="w-28 shrink-0 text-sm text-slate-300">顶部边距%</div>
<InputNumber
v-model="titleConfig.topMarginPercent"
:min="2"
:max="20"
class="w-32"
/>
<span class="text-xs text-slate-500">相对于视频高度</span>
</div>
</div>
<div v-if="linePreviews.length">
<div class="mb-2 text-xs text-slate-400">分行预览 {{ linePreviews.length }} </div>
<div class="space-y-1">
<div
v-for="(line, i) in linePreviews"
:key="i"
class="rounded bg-slate-900/40 px-2 py-1 text-sm text-slate-300"
>
<span class="text-slate-500">{{ i + 1 }}</span>{{ line.text }}
</div>
</div>
</div>
<div>
<div class="mb-2 text-sm font-medium text-slate-200">每行样式配置</div>
<div class="space-y-2">
<div
v-for="(line, index) in titleConfig.lines"
:key="index"
class="overflow-hidden rounded-lg border border-slate-600/50"
>
<button
type="button"
class="flex w-full items-center justify-between bg-slate-900/50 px-3 py-2 text-left text-sm text-slate-200 hover:bg-slate-900/70"
@click="openLineIndex = openLineIndex === index ? -1 : index"
>
<span>{{ index + 1 }}{{ line.text || "(空)" }}</span>
<i
class="pi text-xs"
:class="openLineIndex === index ? 'pi-chevron-up' : 'pi-chevron-down'"
/>
</button>
<div v-show="openLineIndex === index" class="border-t border-slate-600/40 bg-white p-3">
<TitleLineStyleEditor v-model:line="titleConfig.lines[index]" />
</div>
</div>
</div>
</div>
</div>
</div>
</template>