feat: 优化

This commit is contained in:
2026-01-18 18:36:37 +08:00
parent 265ee3a453
commit f5bccf8da4
11 changed files with 1435 additions and 252 deletions

View File

@@ -1,41 +1,44 @@
<script setup>
<script setup lang="ts">
import { ref, watch } from 'vue'
import { EditOutlined, CopyOutlined } from '@ant-design/icons-vue'
import { message } from 'ant-design-vue'
import ChatMessageRenderer from '@/components/ChatMessageRenderer.vue'
import ChatMessageRendererV2 from '@/components/ChatMessageRendererV2.vue'
import { ChatMessageApi } from '@/api/chat'
import { streamChat } from '@/utils/streamChat'
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
mergedText: {
type: String,
default: '',
},
textCount: {
type: Number,
default: 0,
},
const props = withDefaults(defineProps<{
visible: boolean
mergedText: string
textCount: number
}>(), {
visible: false,
mergedText: '',
textCount: 0,
})
const emit = defineEmits(['update:visible', 'copy', 'save', 'use'])
const emit = defineEmits<{
'update:visible': [value: boolean]
'copy': [text: string]
'save': [text: string]
'use': [text: string]
}>()
const batchPrompt = ref('')
const batchPromptEditMode = ref(false)
const batchPromptGenerating = ref(false)
const hasGenerated = ref(false)
function resetModal() {
batchPrompt.value = ''
batchPromptEditMode.value = false
batchPromptGenerating.value = false
hasGenerated.value = false
}
watch(() => props.visible, (newVal) => {
if (newVal && props.mergedText && !hasGenerated.value) {
generateBatchPrompt()
} else if (!newVal) {
batchPrompt.value = ''
batchPromptEditMode.value = false
batchPromptGenerating.value = false
hasGenerated.value = false
resetModal()
}
})
@@ -47,47 +50,61 @@ watch(() => props.mergedText, (newVal) => {
async function generateBatchPrompt() {
if (!props.mergedText || hasGenerated.value) return
hasGenerated.value = true
try {
batchPromptGenerating.value = true
const createPayload = { roleId: 20 }
console.debug('createChatConversationMy payload(batch):', createPayload)
const conversationResp = await ChatMessageApi.createChatConversationMy(createPayload)
let conversationId = null
if (conversationResp?.data) {
conversationId = typeof conversationResp.data === 'object' ? conversationResp.data.id : conversationResp.data
}
if (!conversationId) {
throw new Error('创建对话失败:未获取到 conversationId')
}
const conversationId = await createConversation()
const aiContent = await streamChat({
conversationId,
content: props.mergedText,
onUpdate: (fullText) => {
onUpdate: (fullText: string) => {
batchPrompt.value = fullText
},
enableTypewriter: true,
typewriterSpeed: 10,
typewriterBatchSize: 2
typewriterBatchSize: 2,
onComplete: () => {},
onError: (error: Error) => {
console.error('流式聊天错误:', error)
},
enableContext: false,
enableWebSearch: false,
timeout: 180000,
attachmentUrls: []
})
if (aiContent && aiContent !== batchPrompt.value) {
batchPrompt.value = aiContent
}
message.success(`批量分析完成:已基于 ${props.textCount} 个视频的文案生成综合提示词`)
} catch (aiError) {
console.error('AI生成失败:', aiError)
} catch (error) {
console.error('AI生成失败:', error)
message.error('AI生成失败请稍后重试')
} finally {
batchPromptGenerating.value = false
hasGenerated.value = false
}
}
async function createConversation() {
const createPayload = { roleId: 20 }
const conversationResp = await ChatMessageApi.createChatConversationMy(createPayload)
const conversationId = conversationResp?.data
? (typeof conversationResp.data === 'object' ? conversationResp.data.id : conversationResp.data)
: null
if (!conversationId) {
throw new Error('创建对话失败:未获取到 conversationId')
}
return conversationId
}
function handleClose() {
emit('update:visible', false)
}
@@ -112,131 +129,86 @@ function handleUse() {
:width="800"
:maskClosable="false"
:keyboard="false"
@cancel="handleClose">
@cancel="handleClose"
>
<div class="batch-prompt-modal">
<!-- 内容显示模式 -->
<div v-if="!batchPromptEditMode" class="batch-prompt-display">
<ChatMessageRenderer
<ChatMessageRendererV2
:content="batchPrompt"
:is-streaming="batchPromptGenerating"
/>
</div>
<a-textarea
<!-- 编辑模式 -->
<a-textarea
v-else
v-model:value="batchPrompt"
:rows="15"
placeholder="内容将在这里显示..." />
v-model:value="batchPrompt"
:rows="15"
placeholder="内容将在这里显示..."
/>
</div>
<template #footer>
<a-space>
<a-button size="small" :title="batchPromptEditMode ? '取消编辑' : '编辑'" @click="batchPromptEditMode = !batchPromptEditMode">
<template #icon>
<EditOutlined />
</template>
</a-button>
<a-button size="small" title="复制" @click="handleCopy">
<template #icon>
<CopyOutlined />
</template>
</a-button>
<a-button size="small" title="保存提示词" @click="handleSave" :disabled="!batchPrompt.trim()">
保存提示词
</a-button>
<a-button @click="handleClose">取消</a-button>
<a-button
type="primary"
:disabled="batchPromptGenerating || !batchPrompt.trim()"
@click="handleUse">去创作</a-button>
</a-space>
<div class="footer-actions">
<div class="left-actions">
<a-button type="text" @click="batchPromptEditMode = !batchPromptEditMode">
{{ batchPromptEditMode ? '取消编辑' : '编辑' }}
</a-button>
<a-button type="text" @click="handleCopy">复制</a-button>
<a-button
type="text"
@click="handleSave"
:disabled="!batchPrompt.trim()"
>
保存提示词
</a-button>
</div>
<div class="right-actions">
<a-button @click="handleClose">取消</a-button>
<a-button
type="primary"
:disabled="batchPromptGenerating || !batchPrompt.trim()"
@click="handleUse"
>
去创作
</a-button>
</div>
</div>
</template>
</a-modal>
</template>
<style scoped>
<style scoped lang="less">
.batch-prompt-modal {
min-height: 200px;
.batch-prompt-display {
min-height: 300px;
max-height: 500px;
overflow-y: auto;
padding: 24px;
border: 1px solid var(--color-border);
border-radius: 8px;
background: var(--color-surface);
}
}
.batch-prompt-display {
min-height: 300px;
max-height: 500px;
overflow-y: auto;
padding: 12px;
background: #0d0d0d;
border: 1px solid var(--color-border);
border-radius: 6px;
line-height: 1.6;
}
.footer-actions {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
.batch-prompt-display :deep(h1) {
font-size: 18px;
font-weight: 600;
margin: 12px 0;
color: var(--color-text);
}
.left-actions {
display: flex;
gap: 8px;
}
.batch-prompt-display :deep(h2) {
font-size: 16px;
font-weight: 600;
margin: 16px 0 8px 0;
color: var(--color-text);
}
.batch-prompt-display :deep(h3) {
font-size: 14px;
font-weight: 600;
margin: 12px 0 6px 0;
color: var(--color-text-secondary);
}
.batch-prompt-display :deep(p) {
margin: 8px 0;
color: var(--color-text-secondary);
}
.batch-prompt-display :deep(ul),
.batch-prompt-display :deep(ol) {
margin: 8px 0;
padding-left: 20px;
}
.batch-prompt-display :deep(li) {
margin: 4px 0;
color: var(--color-text-secondary);
}
.batch-prompt-display :deep(strong) {
font-weight: 600;
color: var(--color-text);
}
.batch-prompt-display :deep(code) {
background: #1a1a1a;
padding: 2px 6px;
border-radius: 4px;
font-family: 'Courier New', monospace;
font-size: 12px;
color: #e11d48;
}
.batch-prompt-display :deep(pre) {
background: #1a1a1a;
padding: 12px;
border-radius: 6px;
overflow-x: auto;
margin: 8px 0;
}
.batch-prompt-display :deep(pre code) {
background: transparent;
padding: 0;
}
.batch-prompt-display :deep(blockquote) {
border-left: 3px solid var(--color-primary);
padding-left: 12px;
margin: 8px 0;
color: var(--color-text-secondary);
.right-actions {
display: flex;
gap: 8px;
}
}
</style>

View File

@@ -1,6 +1,6 @@
<script setup>
import { CopyOutlined, SaveOutlined } from '@ant-design/icons-vue'
import ChatMessageRenderer from '@/components/ChatMessageRenderer.vue'
import ChatMessageRendererV2 from '@/components/ChatMessageRendererV2.vue'
const props = defineProps({
record: {
@@ -26,7 +26,6 @@ function handleCreateContent() {
<template>
<div class="expanded-content">
<!-- 未分析的行显示提示 -->
<div v-if="!record.transcriptions && !record.prompt" class="no-analysis-tip">
<a-empty description="该视频尚未分析">
<template #image>
@@ -42,10 +41,8 @@ function handleCreateContent() {
</a-button>
</a-empty>
</div>
<!-- 已分析的行显示内容 -->
<div v-else class="two-col">
<!-- 左侧原配音内容 -->
<section class="col left-col">
<div class="sub-title">原配音</div>
<div class="transcript-box" v-if="record.transcriptions">
@@ -54,44 +51,43 @@ function handleCreateContent() {
<div v-else class="no-transcript">暂无转写文本请先点击"分析"获取</div>
</section>
<!-- 右侧提示词 -->
<section class="col right-col">
<div class="sub-title">提示词</div>
<div class="prompt-display-wrapper">
<ChatMessageRenderer
<ChatMessageRendererV2
:content="record.prompt || ''"
:is-streaming="record._analyzing || false"
/>
<div v-if="!record.prompt" class="no-prompt">暂无提示词</div>
</div>
<div class="right-actions">
<a-space>
<a-button
size="small"
type="text"
<a-button
size="small"
type="text"
class="copy-btn"
:title="'复制'"
title="复制"
@click="handleCopy">
<template #icon>
<CopyOutlined />
</template>
</a-button>
<a-button
<a-button
v-if="record.prompt"
size="small"
type="text"
size="small"
type="text"
class="save-server-btn"
:title="'保存'"
title="保存"
@click="handleSaveToServer">
<template #icon>
<SaveOutlined />
</template>
保存
</a-button>
<a-button
type="dashed"
<a-button
type="dashed"
:disabled="!record.prompt || record._analyzing"
@click="handleCreateContent">基于提示词去创作</a-button>
</a-space>
@@ -177,7 +173,6 @@ function handleCreateContent() {
opacity: 0.8;
}
.no-analysis-tip {
padding: var(--space-8) var(--space-5);
text-align: center;