243 lines
5.7 KiB
Vue
243 lines
5.7 KiB
Vue
<script setup>
|
||
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 { 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 emit = defineEmits(['update:visible', 'copy', 'save', 'use'])
|
||
|
||
const batchPrompt = ref('')
|
||
const batchPromptEditMode = ref(false)
|
||
const batchPromptGenerating = ref(false)
|
||
const hasGenerated = ref(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
|
||
}
|
||
})
|
||
|
||
watch(() => props.mergedText, (newVal) => {
|
||
if (props.visible && newVal && !hasGenerated.value) {
|
||
generateBatchPrompt()
|
||
}
|
||
})
|
||
|
||
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 aiContent = await streamChat({
|
||
conversationId,
|
||
content: props.mergedText,
|
||
onUpdate: (fullText) => {
|
||
batchPrompt.value = fullText
|
||
},
|
||
enableTypewriter: true,
|
||
typewriterSpeed: 10,
|
||
typewriterBatchSize: 2
|
||
})
|
||
|
||
if (aiContent && aiContent !== batchPrompt.value) {
|
||
batchPrompt.value = aiContent
|
||
}
|
||
|
||
message.success(`批量分析完成:已基于 ${props.textCount} 个视频的文案生成综合提示词`)
|
||
} catch (aiError) {
|
||
console.error('AI生成失败:', aiError)
|
||
message.error('AI生成失败,请稍后重试')
|
||
} finally {
|
||
batchPromptGenerating.value = false
|
||
}
|
||
}
|
||
|
||
function handleClose() {
|
||
emit('update:visible', false)
|
||
}
|
||
|
||
function handleCopy() {
|
||
emit('copy', batchPrompt.value)
|
||
}
|
||
|
||
function handleSave() {
|
||
emit('save', batchPrompt.value)
|
||
}
|
||
|
||
function handleUse() {
|
||
emit('use', batchPrompt.value)
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<a-modal
|
||
:open="visible"
|
||
title="综合分析结果"
|
||
:width="800"
|
||
:maskClosable="false"
|
||
:keyboard="false"
|
||
@cancel="handleClose">
|
||
<div class="batch-prompt-modal">
|
||
<div v-if="!batchPromptEditMode" class="batch-prompt-display">
|
||
<ChatMessageRenderer
|
||
:content="batchPrompt"
|
||
:is-streaming="batchPromptGenerating"
|
||
/>
|
||
</div>
|
||
<a-textarea
|
||
v-else
|
||
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>
|
||
</template>
|
||
</a-modal>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.batch-prompt-modal {
|
||
min-height: 200px;
|
||
}
|
||
|
||
.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;
|
||
}
|
||
|
||
.batch-prompt-display :deep(h1) {
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
margin: 12px 0;
|
||
color: var(--color-text);
|
||
}
|
||
|
||
.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);
|
||
}
|
||
</style>
|
||
|