详情页开发

接下来开发详情页(detail 页面),页面 UI 效果图如图12-6所示。

Figure P332 47328
Figure 1. 图12-6 详情页效果图

在 views 目录下新建 detail 文件目录,由于详情页逻辑复杂一些,我们将详情页拆分成 movieinfo.vue、movieactors.vue、moviecomments.vue 三个组件,其目录结构如图12-7所示。

image 2024 02 26 17 27 24 490
Figure 2. 图12-7 详情页目录结构

电影基本信息组件

在 detail 目录下新建 detail.vue,用来作为父组件,其核心代码如下:

<div class="detail-container">
    <div class="left-content">
        <movieinfo />// 基本信息
        <movieactors />// 演员信息
        <moviecomments />// 评论信息
    </div>
</div>

该组件主要为父组件的壳子,用于设置基本的样式,没有其他逻辑。

在 detail 目录下新建 movieinfo.vue,用来作为电影信息组件,其核心代码如下:

setup(){
    const store = Vuex.useStore()
    let detailData = ref({})
    let actors = reactive({
        orgin:[],
        short:[],
        isShowMore: true
    })
    let rate = reactive({
        list:[],
        betterList:[]
    })
    const route = useRoute()
    let id = computed(() => route.query.id);
    onMounted(async () => {
        // 获取数据
        detailData.value = await service.get(configapi.detail(id.value),{})
        // 设置当前电影的title,为了通知其他组件,所以放在Vuex的Store中
        store.commit('setTitle',detailData.value.title)
        actors.orgin = detailData.value.actors||[]
        actors.short = actors.orgin.slice(0,3)
        actors.isShowMore = actors.orgin.length > 3
        // 渲染评分高低排名
        let rateData = await service.get(configapi.rate(id.value),{})
        rate.list = dealRateData(rateData)
        rate.betterList = rateData.type_ranks||[]
    });
    // 格式化评分数据
    const dealRateData = (rateData)=>{
        // 100%的最大宽度
        let maxwidth = 70
        let list = []
        for (let i = 0 ; i < rateData.stats.length ; i++) {
            let r = rateData.stats[i].toFixed(3)*100
            // 以此通过评分计算百分比宽度
            list.push({
                index: i+1,
                count:r,
                width: r*maxwidth/100
            })
        }
        return list.reverse()// 从高到低排列
    }
    // 展开所有演员名字
    const expand = ()=>{
        actors.short = actors.orgin
        actors.isShowMore = false
    }
    return {
        detailData,
        actors,
        expand,
        rate
    }
},

其主要业务逻辑如下:

  • 获取基本信息数据,对将数据分别进行处理,供基本内容展示和评分排名使用。

  • 在获取到标题后,利用 Vuex 通知其他需要标题的组件。

  • 在基本信息中,如果演员数据太长,则默认只取前三个名字,当单击展开时,再将所有名字平铺出来。

  • 在评分排名渲染时,设置一个最大宽度,根据每种区间得分的占比分别设置不同的宽度。

电影演员信息组件

在 detail 目录下新建 movieactors.vue,用来作为电影演员图片列表组件,其核心代码如下:

setup(){
    const store = Vuex.useStore()
    // 从Vuex的Store中获取title
    const title = computed(() => store.state.detailTitle);
    let detailData = reactive({
        list:[]
    })
    const route = useRoute()
    let id = computed(() => route.query.id);
    // 获取演员图片列表数据
    onMounted(async () => {
        let data = await service.get(configapi.actors(id.value))
        detailData.list = data.directors.concat(data.actors)
    });
    return {
        detailData,
        title
    }
}

其主要业务逻辑如下:

  • 获取演员图片列表数据。

  • 将 title 从 Vuex 的 Store 中取出来并展示。

电影评论信息组件

在 detail 目录下新建 moviecomments.vue,用来作为电影评论列表组件,其核心代码如下:

setup(){
    const store = Vuex.useStore()
    // 从Vuex的Store中获取title
    const title = computed(() => store.state.detailTitle);
    // 获取用户自己发票的评论数据
    const incommentList = computed(() => store.state.commentList);
    let detailData = reactive({
        list:[]
    })
    const route = useRoute()
    // 从url上获取id参数
    let id = computed(() => route.query.id);
    onMounted(async () => {
        let data = await service.get(configapi.comments(id.value),{
            start:0,
            count:20
        })
        // 将后端获取的数据和用户发票的数据进行组合
        detailData.list = incommentList.value.concat(data.reviews || [])
    });
    return {
        detailData,
        title
    }
}

其主要业务逻辑如下:

  • 根据 url 上的参数 id 获取评论列表数据。

  • 根据数据渲染并展示评论列表。

  • 当有用户自己发表的数据时,将数据进行合并。

在该组件中,单击发表评论跳转到发表界面。