fix: 页面优化,工具栏显示
This commit is contained in:
parent
0e94beb477
commit
5ba6c9f746
|
|
@ -399,10 +399,44 @@ const clear = () => {
|
||||||
hideMentionPanel()
|
hideMentionPanel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 供父组件持久化:参考图模式的 HTML + 素材列表 */
|
||||||
|
const getDraftState = () => ({
|
||||||
|
html: editorRef.value ? editorRef.value.innerHTML : '',
|
||||||
|
mentionList: mentionImageList.value.map(({ url, refNo, mediaType }) => ({
|
||||||
|
url,
|
||||||
|
refNo,
|
||||||
|
mediaType
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
const applyDraftState = (draft) => {
|
||||||
|
hideMentionPanel()
|
||||||
|
savedSelectionRange.value = null
|
||||||
|
if (!draft || typeof draft !== 'object') {
|
||||||
|
clear()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mentionImageList.value = Array.isArray(draft.mentionList)
|
||||||
|
? draft.mentionList.map((x) => ({
|
||||||
|
url: x.url,
|
||||||
|
refNo: Number(x.refNo) || 0,
|
||||||
|
mediaType: x.mediaType || 'image'
|
||||||
|
}))
|
||||||
|
: []
|
||||||
|
if (editorRef.value) {
|
||||||
|
editorRef.value.innerHTML = typeof draft.html === 'string' ? draft.html : ''
|
||||||
|
}
|
||||||
|
nextTick(() => {
|
||||||
|
emit('text-change', getPlainText())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
getContentItems,
|
getContentItems,
|
||||||
getPlainText,
|
getPlainText,
|
||||||
clear
|
clear,
|
||||||
|
getDraftState,
|
||||||
|
applyDraftState
|
||||||
})
|
})
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|
|
||||||
|
|
@ -86,8 +86,8 @@
|
||||||
<div class="rich-editor-container">
|
<div class="rich-editor-container">
|
||||||
<VideoRichEditor
|
<VideoRichEditor
|
||||||
ref="videoRichEditor"
|
ref="videoRichEditor"
|
||||||
:show-toolbar="videoMode !== 'text-to-video'"
|
:show-toolbar="videoMode === 'image-reference'"
|
||||||
@text-change="bumpEditorVisualTick"
|
@text-change="onVideoEditorTextChange"
|
||||||
:placeholder="
|
:placeholder="
|
||||||
$t('common.textVideoPlaceholder') ||
|
$t('common.textVideoPlaceholder') ||
|
||||||
'描述画面与动态,例如:阳光下的女孩在海边起舞…'
|
'描述画面与动态,例如:阳光下的女孩在海边起舞…'
|
||||||
|
|
@ -223,6 +223,8 @@
|
||||||
<script>
|
<script>
|
||||||
import VideoRichEditor from '@/components/VideoRichEditor.vue'
|
import VideoRichEditor from '@/components/VideoRichEditor.vue'
|
||||||
|
|
||||||
|
const REFERENCE_DRAFT_STORAGE_KEY = 'portalVideoGen_referenceDraft_v1'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'VideoGen',
|
name: 'VideoGen',
|
||||||
data() {
|
data() {
|
||||||
|
|
@ -254,7 +256,9 @@ export default {
|
||||||
selectedRatio: '',
|
selectedRatio: '',
|
||||||
selectedDuration: null,
|
selectedDuration: null,
|
||||||
selectedResolution: '',
|
selectedResolution: '',
|
||||||
maxPollAttempts: 40,
|
// 每 30 秒查询任务状态,最多 30 次(约 15 分钟)
|
||||||
|
maxPollAttempts: 30,
|
||||||
|
pollIntervalMs: 30000,
|
||||||
taskRows: []
|
taskRows: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -301,6 +305,40 @@ export default {
|
||||||
this.editorVisualTick++
|
this.editorVisualTick++
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onVideoEditorTextChange() {
|
||||||
|
this.bumpEditorVisualTick()
|
||||||
|
if (this.videoMode === 'image-reference') {
|
||||||
|
this.saveReferenceDraftFromEditor()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
saveReferenceDraftFromEditor() {
|
||||||
|
const ed = this.$refs.videoRichEditor
|
||||||
|
if (!ed || typeof ed.getDraftState !== 'function') return
|
||||||
|
try {
|
||||||
|
const draft = ed.getDraftState()
|
||||||
|
localStorage.setItem(REFERENCE_DRAFT_STORAGE_KEY, JSON.stringify(draft))
|
||||||
|
} catch (_) {
|
||||||
|
/* ignore quota / private mode */
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
restoreReferenceDraftToEditor() {
|
||||||
|
const ed = this.$refs.videoRichEditor
|
||||||
|
if (!ed || typeof ed.applyDraftState !== 'function') return
|
||||||
|
try {
|
||||||
|
const raw = localStorage.getItem(REFERENCE_DRAFT_STORAGE_KEY)
|
||||||
|
if (!raw) {
|
||||||
|
ed.clear?.()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const draft = JSON.parse(raw)
|
||||||
|
ed.applyDraftState(draft)
|
||||||
|
} catch (_) {
|
||||||
|
ed.clear?.()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/** 编辑器内插入的参考图 URL(与 getContentItems 一致) */
|
/** 编辑器内插入的参考图 URL(与 getContentItems 一致) */
|
||||||
firstReferenceImageUrlFromEditor() {
|
firstReferenceImageUrlFromEditor() {
|
||||||
const items = this.$refs.videoRichEditor?.getContentItems?.() || []
|
const items = this.$refs.videoRichEditor?.getContentItems?.() || []
|
||||||
|
|
@ -313,11 +351,18 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
pickVideoMode(m) {
|
pickVideoMode(m) {
|
||||||
|
if (this.videoMode === 'image-reference') {
|
||||||
|
this.saveReferenceDraftFromEditor()
|
||||||
|
}
|
||||||
this.videoMode = m
|
this.videoMode = m
|
||||||
this.firstUrl = ''
|
this.firstUrl = ''
|
||||||
this.lastUrl = ''
|
this.lastUrl = ''
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.$refs.videoRichEditor?.clear?.()
|
if (m === 'image-reference') {
|
||||||
|
this.restoreReferenceDraftToEditor()
|
||||||
|
} else {
|
||||||
|
this.$refs.videoRichEditor?.clear?.()
|
||||||
|
}
|
||||||
this.bumpEditorVisualTick()
|
this.bumpEditorVisualTick()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
@ -498,7 +543,9 @@ export default {
|
||||||
this.interval = setInterval(() => {
|
this.interval = setInterval(() => {
|
||||||
attempts++
|
attempts++
|
||||||
if (attempts > this.maxPollAttempts) {
|
if (attempts > this.maxPollAttempts) {
|
||||||
this.$message.warning('视频生成超时')
|
this.$message.warning(
|
||||||
|
'等待结果超时:任务可能仍在云端生成,可稍后在「我的视频任务」刷新或向客服提供任务 ID'
|
||||||
|
)
|
||||||
this.destroyInterval()
|
this.destroyInterval()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -507,12 +554,30 @@ export default {
|
||||||
url: `api/portal/video/tasks/${videoId}`,
|
url: `api/portal/video/tasks/${videoId}`,
|
||||||
method: 'GET'
|
method: 'GET'
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
if (res.code == 200 && res.data.status === 'succeeded') {
|
if (res.code != 200) {
|
||||||
this.videoUrl = res.data.content?.video_url || res.data.video_url
|
|
||||||
this.destroyInterval()
|
this.destroyInterval()
|
||||||
|
this.$message.error(res.msg || '查询生成状态失败')
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
const st = res.data != null && res.data.status != null ? String(res.data.status).toLowerCase().trim() : ''
|
||||||
|
if (st === 'succeeded') {
|
||||||
|
this.videoUrl = res.data.content?.video_url || res.data.video_url
|
||||||
|
if (!this.videoUrl) {
|
||||||
|
this.$message.warning('任务已完成但未返回视频地址,请稍后重试或联系管理员')
|
||||||
|
}
|
||||||
|
this.destroyInterval()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (st === 'failed' || st === 'cancelled' || st === 'canceled') {
|
||||||
|
this.destroyInterval()
|
||||||
|
this.$message.error(
|
||||||
|
res.data.error?.message || res.data.message || '视频生成失败或已取消'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
/* 单次网络错误不停止轮询,避免偶发抖动 */
|
||||||
})
|
})
|
||||||
}, 3000)
|
}, this.pollIntervalMs)
|
||||||
},
|
},
|
||||||
|
|
||||||
destroyInterval() {
|
destroyInterval() {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue