forked from zhongyidian/super_video
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
151 lines
4.3 KiB
Vue
151 lines
4.3 KiB
Vue
<script lang="ts" setup>
|
||
import { useRoute, useRouter } from 'vue-router'
|
||
import { Video } from '@/components'
|
||
import { ImgButton } from '@/components/Button'
|
||
import PracticeRatingModal, {
|
||
type PracticeRatingItem,
|
||
} from '@/components/PracticeRatingModal'
|
||
import { IMAGE_PATHS, getPublicPath, getVideoPoster } from '@/utils'
|
||
import { useInterviewStore } from '@/stores/modules/interview'
|
||
import { computed, ref } from 'vue'
|
||
import { GuideSteps, GuideStepsItem } from '@/components/Guide'
|
||
|
||
const router = useRouter()
|
||
const route = useRoute()
|
||
const store = useInterviewStore()
|
||
|
||
const guideKey = 'interview-practice-video-rating'
|
||
const showGuide = ref(!store.guideShown[guideKey])
|
||
|
||
const handleRatingGuideComplete = () => {
|
||
store.markGuideShown(guideKey)
|
||
}
|
||
|
||
const props = defineProps({
|
||
path: {
|
||
type: String,
|
||
required: true,
|
||
},
|
||
})
|
||
|
||
type PracticeRatingScores = Record<string, number>
|
||
|
||
const workKey = computed<string>(() => decodeURIComponent(props.path))
|
||
|
||
const applyScores = (
|
||
items: PracticeRatingItem[],
|
||
scores: PracticeRatingScores,
|
||
): PracticeRatingItem[] =>
|
||
items.map((item) => ({
|
||
...item,
|
||
score: scores[item.title] ?? item.score,
|
||
}))
|
||
|
||
const toScores = (items: PracticeRatingItem[]): PracticeRatingScores =>
|
||
Object.fromEntries(items.map((item) => [item.title, item.score]))
|
||
|
||
/** 评估项目列表 */
|
||
const defaultEvaluationItems: PracticeRatingItem[] = [
|
||
{
|
||
title: '人物表达',
|
||
description: '能清晰介绍人物身份、特点或故事,让观众认识人物',
|
||
score: 0,
|
||
},
|
||
{
|
||
title: '素材拍摄',
|
||
description: '画面包含必要人物镜头、环境镜头与细节素材,内容较丰富',
|
||
score: 0,
|
||
},
|
||
{
|
||
title: '镜头语言',
|
||
description: '合理使用移动镜头、特写、跟拍等方式增强表现力',
|
||
score: 0,
|
||
},
|
||
{
|
||
title: '剪辑结构',
|
||
description: '结构完整清晰,段落顺畅,内容有起承转合',
|
||
score: 0,
|
||
},
|
||
{
|
||
title: '音画关系',
|
||
description: '画面与配音/环境音基本匹配,能辅助理解内容',
|
||
score: 0,
|
||
},
|
||
{
|
||
title: '综合观感',
|
||
description: '作品完整,有真实感与感染力,具备观看吸引力',
|
||
score: 0,
|
||
},
|
||
]
|
||
|
||
const evaluationItems = ref<PracticeRatingItem[]>(
|
||
applyScores(
|
||
defaultEvaluationItems,
|
||
store.getPracticeWorkRating(workKey.value),
|
||
),
|
||
)
|
||
|
||
const handleSaveEvaluation = (items: PracticeRatingItem[]): void => {
|
||
store.setPracticeWorkRating(workKey.value, toScores(items))
|
||
}
|
||
|
||
const handleVideoEnded = (): void => {
|
||
const watchKey = route.query.watchKey
|
||
if (typeof watchKey === 'string' && watchKey) {
|
||
store.markPracticeVideoWatched(watchKey)
|
||
}
|
||
}
|
||
|
||
/** 保存弹窗 */
|
||
const showModal = ref<boolean>(false)
|
||
|
||
/** 返回上一页 */
|
||
const handleBack = (): void => {
|
||
router.back()
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<div class="relative h-full">
|
||
<ImgButton
|
||
class="absolute right-0 top-0 z-50"
|
||
:image="IMAGE_PATHS.COMPONENTS.BACK"
|
||
@click="handleBack"
|
||
/>
|
||
<ImgButton
|
||
class="btn-1 absolute right-0 top-1/2 -translate-y-1/2 z-50"
|
||
image="vlog/practice/评分"
|
||
@click="showModal = true"
|
||
/>
|
||
<Video
|
||
:src="getPublicPath(decodeURIComponent(props.path))"
|
||
:poster="getVideoPoster(getPublicPath(decodeURIComponent(props.path)))"
|
||
:volume="1"
|
||
:muted="false"
|
||
:controls="true"
|
||
:loop="false"
|
||
@ended="handleVideoEnded"
|
||
/>
|
||
|
||
<PracticeRatingModal
|
||
:visible="showModal"
|
||
v-model:items="evaluationItems"
|
||
background-image="interview/practice/人物访谈短片创作-作品评分表"
|
||
top-tip="评分时要关注6个维度哦!分别是人物表达、素材拍摄、镜头语言、剪辑结构、音画关系、综合观感每个维度满分5分,能打1-5分,记得对照作品表现来打分呀~"
|
||
bottom-tip="己评价的实战作品,从封面入口就能直接看到具体评分!打了多少分一目了然,不用再点进详情页翻找啦~"
|
||
@save="handleSaveEvaluation"
|
||
@close="showModal = false"
|
||
/>
|
||
<GuideSteps
|
||
:visible="showGuide"
|
||
highlight-selector=".btn-1"
|
||
position-offset-y="85%"
|
||
@complete="handleRatingGuideComplete"
|
||
>
|
||
<GuideStepsItem audio-src="/audio/py_6_008.MP3">
|
||
点击【评分】,为你喜爱的作品送上五星好评。
|
||
</GuideStepsItem>
|
||
</GuideSteps>
|
||
</div>
|
||
</template>
|