P6. 对局列表和排行榜功能

P6. 对局列表和排行榜功能

    • [0 概述](#0 概述)
    • [1 对局列表功能](#1 对局列表功能)
      • [1.1 分页配置](#1.1 分页配置)
      • [1.2 后端按页获取对局列表接口](#1.2 后端按页获取对局列表接口)
      • [1.3 前端展示传回来的对局列表](#1.3 前端展示传回来的对局列表)
      • [1.4 录像回放功能](#1.4 录像回放功能)
        • [1.4.1 录像回放的流程](#1.4.1 录像回放的流程)
        • [1.4.2 录像回放的实现](#1.4.2 录像回放的实现)
      • [1.5 前端分页展示](#1.5 前端分页展示)
    • [2 排行榜功能](#2 排行榜功能)
      • [2.1 排行榜的实现](#2.1 排行榜的实现)

0 概述

  • 本节主要介绍了如何实现对局列表和排行榜 ,对局列表包含对局录像的回放的实现最重要的是掌握怎么写分页功能

1 对局列表功能

1.1 分页配置

由于对局数量很多,不可能放在同一页上展示,因此要实现分页功能。后端的 Mybatis 分页配置:

MybatisConfig:

java 复制代码
@Configuration
public class MybatisConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

1.2 后端按页获取对局列表接口

service.impl

需要传入参数 page,表示当前要获取的是哪一页的列表。

通过 MyBatis 提供的 API 来实现分页查询的功能,查询的页面按最新对局顺序排列。

java 复制代码
@Override
public JSONObject getRecordList(Integer page) {
    IPage<Record> recordIPage = new Page<>(page, 8); // 每页展示8条
    QueryWrapper<Record> queryWrapper = new QueryWrapper<>();

    queryWrapper.orderByDesc("id");
    List<Record> records = recordMapper.selectPage(recordIPage, queryWrapper).getRecords();

    List<JSONObject> items = new ArrayList<>();
    for (Record record : records) {
        JSONObject item = new JSONObject();

        User userA = userMapper.selectById(record.getAId());
        User userB = userMapper.selectById(record.getBId());

        item.put("a_photo", userA.getPhoto());
        item.put("a_username", userA.getUsername());
        item.put("b_photo", userB.getPhoto());
        item.put("b_username", userB.getUsername());
        item.put("record", record);

        String result = "Draw";
        if ("A".equals(record.getLoser())) result = "B Win";
        else if ("B".equals(record.getLoser())) result = "A Win";
        item.put("result", result);

        items.add(item);
    }

    JSONObject resp = new JSONObject();
    resp.put("records", items);
    resp.put("records_count", recordMapper.selectCount(null));

    return resp;
}

1.3 前端展示传回来的对局列表

写一个表格来展示每个对局的信息,先获取一页的信息进行调试:

vue 复制代码
<template>
    <ContentField>
        <table class="table table-striped table-hover" style="text-align: center;">
            <thead>
                <tr>
                    <th>A</th>
                    <th>B</th>
                    <th>对战结果</th>
                    <th>对战时间</th>
                    <th>操作</th>
                </tr>
            </thead>
            <tbody>
                <tr v-for="record in records" :key="record.record.id">
                    <td>s
                        <img :src="record.a_photo" alt="" class="record-user-photo">
                        &nbsp;
                        <span class="record-user-username">{{ record.a_username }}</span>
                    </td>
                    <td>
                        <img :src="record.b_photo" alt="" class="record-user-photo">
                        &nbsp;
                        <span class="record-user-username">{{ record.b_username }}</span>
                    </td>
                    <td>{{ record.result }}</td>
                    <td>{{ record.record.createtime }}</td>
                    <td>
                        <button type="button" class="btn btn-secondary">查看录像</button>
                    </td>
                </tr>
            </tbody>
        </table>
    </ContentField>
</template>

<script>
import ContentField from '../../components/ContentField.vue'
import { useStore } from 'vuex';
import { ref } from 'vue';
import $ from 'jquery';

export default {
    components: {
        ContentField
    },
    setup() {
        const store = useStore();
        let records = ref([]);
        let current_page = 1;
        let total_records = 0;
        console.log(total_records);

        const pull_page = page => {
            current_page = page;
            $.ajax({
                url: "http://127.0.0.1:3000/record/getlist/",
                data: {
                    page,
                },
                type: "get",
                headers: {
                    Authorization: "Bearer " + store.state.user.token,
                },
                success(resp) {
                    records.value = resp.records;
                    total_records = resp.records_count;
                },
                error(resp) {
                    console.log(resp);
                }
            })
        }

        pull_page(current_page);

        return {
            records,
        }
    }
}
</script>

<style scoped>
img.record-user-photo {
    width: 4vh;
    border-radius: 50%;
}
</style>

1.4 录像回放功能

1.4.1 录像回放的流程

首先要明确录像回放需要哪些参数,将这些参数从后端获取后保存在前端。之后要明确前端在哪展示录像,以及如何展示录像。

  • 后端传来的参数已在 1.2 节中说明,并且前端保存在 records 数组中,records 中的每个 record.record 包含了录像的具体信息,包括地图、双方玩家的操作信息、败者等。
  • 前端需要开一个全局变量 record.js 进行保存,并且决定在 PlayGround 中进行录像回放,因此需要一个变量记录当前是在 pk 还是在播放 record。另外,它们共用的 GameMap.js 也需要根据是 record 还是 pk 进行相应的判断,如果是 pk 那就是前几节实现的,如果是 record 则要播放录像。
1.4.2 录像回放的实现
  • 首先在前端 record.js 记录全局变量,在点击"播放录像"按钮之后,应该将对应录像的信息保存到 Game, Record,并且跳转到新的页面播放对应录像,因此需要添加对应的路由(路由中带上对应录像的 id):

    javascript 复制代码
    path: '/record/:recordId/',
    name: 'record_content',
    component: RecordContentView,
    meta: {
      requestAuth: true,
    }
    javascript 复制代码
    const open_record_content = recordId => {
        for (const record of records.value) {
            if (record.record.id === recordId) {
                store.commit("updateIsRecord", true);
                store.commit("updateGame", {
                    map: stringTo2D(record.record.map),
                    a_id: record.record.aid,
                    a_sx: record.record.asx,
                    a_sy: record.record.asy,
                    b_id: record.record.bid,
                    b_sx: record.record.bsx,
                    b_sy: record.record.bsy,
                });
                store.commit("updateSteps", {
                    a_steps: record.record.asteps,
                    b_steps: record.record.bsteps,
                });
                store.commit("updateRecordLoser", record.record.loser);
                router.push({
                    name: "record_content",
                    params: {
                        recordId
                    }
                })
                break;
            }
        }
    }
  • GameMap.js 中实现录像回放:

    javascript 复制代码
    add_listening_events() {
        if (this.store.state.record.is_record) {
            let k = 0;
    
            const a_steps = this.store.state.record.a_steps;
            const b_steps = this.store.state.record.b_steps;
            const loser = this.store.state.record.record_loser;
            const [snake0, snake1] = this.snakes;
    
            const interval_id = setInterval(() => {
                if (k >= a_steps.length - 1) {
                    if (loser === "all" || loser === "A") {
                        snake0.status = "die";
                    }
                    if (loser === "all" || loser === "B") {
                        snake1.status = "die";
                    }
                    clearInterval(interval_id);
                } else {
                    snake0.set_direction(parseInt(a_steps[k]));
                    snake1.set_direction(parseInt(b_steps[k]));
                }
                k ++ ;
            }, 300);
        } else {
            /* pk页面的操作 */
        }
    }

1.5 前端分页展示

分页要满足以下几点:

(1) 当前页高亮,并且返回当前页的对局列表。

(2) 展示前面2页和后面2页的信息,如果页数不存在就不展示。

(3) 点击对应编号之后会跳转到对应的页面。

vue 复制代码
<template>
    <ContentField>
        <nav aria-label="...">
            <ul class="pagination" style="float: right;">
                <li class="page-item" @click="click_page(-2)">
                    <a class="page-link" href="#">前一页</a>
                </li>
                <li :class="'page-item ' + page.is_active" v-for="page in pages" 
                    :key="page.number" @click="click_page(page.number)">
                    <a class="page-link" href="#">{{ page.number }}</a>
                </li>
                <li class="page-item" @click="click_page(-1)">
                    <a class="page-link" href="#">后一页</a>
                </li>
            </ul>
        </nav>
    
    </ContentField>
</template>

<script>
export default {
    setup() {
        let current_page = 1;
        let total_records = 0;
        let pages = ref([]); // pages 记录当前要展示的所有页面

        const click_page = page => {
            if (page === -2) page = current_page - 1;
            else if (page === -1) page = current_page + 1;
            let max_pages = parseInt(Math.ceil(total_records / 8));

            if (page >= 1 && page <= max_pages) {
                pull_page(page);
            }
        }

        const udpate_pages = () => {
            let max_pages = parseInt(Math.ceil(total_records / 8)); 
            // 除的是每页展示多少个录像
            let new_pages = [];
            for (let i = current_page - 2; i <= current_page + 2; i ++ ) {
                if (i >= 1 && i <= max_pages) {
                    new_pages.push({
                        number: i,
                        is_active: i === current_page ? "active" : "",
                    });
                }
            }
            pages.value = new_pages;
        }
		
        /* 每次 pull_page 之后都要记得 update_pages */
        
        return {
            pages,
            click_page
        }
    }
}
</script>

2 排行榜功能

2.1 排行榜的实现

排行榜相对就简单多得多了,和对局列表功能相同,就是查出来用户列表,再在前端展示即可(前端和上面的差不多,不赘述了)。

java 复制代码
@Override
public JSONObject getRanklist(Integer page) {
    IPage<User> userIPage = new Page<>(page, 8);
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.orderByDesc("rating");

    List<User> users = userMapper.selectPage(userIPage, queryWrapper).getRecords();

    JSONObject resp = new JSONObject();
    for (User user : users) user.setPassword(""); // 记得传回去之前要把密码清空

    resp.put("users", users);
    resp.put("users_count", userMapper.selectCount(null));

    return resp;
}
相关推荐
bpmf_fff11 分钟前
二九(vue2-05)、父子通信v-model、sync、ref、¥nextTick、自定义指令、具名插槽、作用域插槽、综合案例 - 商品列表
vue
哑巴语天雨14 分钟前
React+Vite项目框架
前端·react.js·前端框架
初遇你时动了情27 分钟前
react 项目打包二级目 使用BrowserRouter 解决页面刷新404 找不到路由
前端·javascript·react.js
乔峰不是张无忌3301 小时前
【HTML】动态闪烁圣诞树+雪花+音效
前端·javascript·html·圣诞树
鸿蒙自习室1 小时前
鸿蒙UI开发——组件滤镜效果
开发语言·前端·javascript
m0_748250741 小时前
高性能Web网关:OpenResty 基础讲解
前端·openresty
机器之心1 小时前
图学习新突破:一个统一框架连接空域和频域
人工智能·后端
前端没钱1 小时前
从 Vue 迈向 React:平滑过渡与关键注意点全解析
前端·vue.js·react.js
NoneCoder2 小时前
CSS系列(29)-- Scroll Snap详解
前端·css
无言非影2 小时前
vtie项目中使用到了TailwindCSS,如何打包成一个单独的CSS文件(优化、压缩)
前端·css