8.1 KiB
赛事中心赛程、直播、赔率组件实施计划
For agentic workers: REQUIRED SUB-SKILL: Use
executing-plansto implement this plan task-by-task. 本项目未授权子代理、自动测试、构建、提交或部署。
Goal: 在赛事中心当前页面内用一个通用组件切换赛程、直播和赔率内容,不再因点击三个 Tab 切换到世界杯主题组件。
Architecture: MatchCenterPanel.vue 只保留赛事中心外壳和世界杯专题入口,新建 MatchCenterTabsPanel.vue 管理通用筛选、三个 Tab、数据加载和内容渲染。后端新增通用直播与赔率接口,并复用现有直播地址解析、赔率盘口格式化和赛事匹配逻辑;世界杯专题接口保持兼容。
Tech Stack: PHP 8 / ThinkPHP、Vue 3 <script setup>、TypeScript、uni-app、SCSS。
Task 1: 通用直播查询
Files:
-
Modify:
server/app/common/service/match/MatchLiveService.php -
Modify:
server/app/api/controller/MatchController.php -
Step 1: 将世界杯直播查询抽成可传筛选条件的通用方法
在 MatchLiveService 增加:
public static function getLiveCards(int $sportType = 0, string $leagueName = '', bool $worldCupOnly = false): array
查询必须保留:
->join('match m', 'm.id = live.match_id')
->where(['m.is_show' => 1])
->where([['m.status', '<>', 2]])
MatchLive 模型已启用 SoftDelete,查询自动排除已软删除记录,不额外重复拼接 delete_time 条件。
条件规则:
if ($sportType > 0) {
$query->where('m.sport_type', $sportType);
}
if ($leagueName !== '') {
$query->where('m.league_name', $leagueName);
}
if ($worldCupOnly) {
$query->where(function ($query) {
$query->where('m.league_name', '世界杯')
->whereOr('m.competition_id', 61);
});
}
getWorldCupLiveCards() 改为调用:
return self::getLiveCards(0, '', true);
- Step 2: 新增公开接口
在 MatchController::$notNeedLogin 增加 liveCards,新增:
public function liveCards()
{
$sportType = $this->request->get('sport_type/d', 0);
$leagueName = trim($this->request->get('league_name/s', ''));
return $this->data([
'list' => MatchLiveService::getLiveCards($sportType, $leagueName),
]);
}
- Step 3: 保持世界杯接口响应兼容
worldCupLiveCards() 继续返回 season_id 和 list,不改变专题页字段结构。
Task 2: 通用赔率查询
Files:
-
Modify:
server/app/api/controller/MatchController.php -
Step 1: 抽取赔率列表构建方法
新增私有方法:
private function getOddsList(
string $showType,
string $sourceSite,
string $keyword,
int $includeSpecial,
int $limit,
int $sportType = 0,
string $leagueName = '',
bool $worldCupEventOnly = false
): array
sport_type 映射规则:
private function resolveOddsSportType(int $sportType): string
{
$map = [1 => 'FT'];
return $map[$sportType] ?? '';
}
当 sportType > 0 且无法映射时直接返回空数组。leagueName 非空时对 MatchOdds.league_name 做精确筛选。
- Step 2: 将赛事匹配扩展为通用匹配
将 buildWorldCupEventMap() 改为:
private function buildOddsEventMap(int $sportType = 0, string $leagueName = '', bool $worldCupOnly = false): array
通用查询以 MatchEvent::where('is_show', 1) 为基础;根据参数增加 sport_type、league_name 或世界杯条件,继续按标准化主客队键匹配。
- Step 3: 新增通用赔率接口并保持专题接口
在 MatchController::$notNeedLogin 增加 odds。
public function odds()
{
return $this->data([
'list' => $this->getOddsList(
$this->request->get('show_type/s', ''),
strtolower(trim($this->request->get('source_site/s', ''))),
trim($this->request->get('keyword/s', '')),
$this->request->get('include_special/d', 0),
min(max($this->request->get('limit/d', 120), 1), 300),
$this->request->get('sport_type/d', 0),
trim($this->request->get('league_name/s', ''))
),
]);
}
worldCupOdds() 调用同一方法并传 worldCupEventOnly=true,继续返回 season_id。
Task 3: 前端 API 类型与请求
Files:
-
Modify:
uniapp/src/api/match.ts -
Step 1: 将专题类型改成可复用类型
新增别名或通用接口:
export type MatchLiveCardItem = WorldCupLiveCardItem
export type MatchOddsMatchItem = WorldCupOddsMatchItem
- Step 2: 增加通用请求方法
export function getMatchLiveCards(data?: {
sport_type?: number
league_name?: string
}) {
return request.get({ url: '/match/liveCards', data }, { withToken: false })
}
export function getMatchOdds(data?: {
sport_type?: number
league_name?: string
show_type?: 'live' | 'today' | 'early'
source_site?: string
keyword?: string
include_special?: number
limit?: number
}) {
return request.get({ url: '/match/odds', data }, { withToken: false })
}
世界杯请求方法保持不变。
Task 4: 通用赛事中心 Tab 组件
Files:
-
Create:
uniapp/src/packages_match/components/MatchCenterTabsPanel.vue -
Modify:
uniapp/src/packages_match/components/MatchCenterPanel.vue -
Step 1: 创建组件状态
组件内部定义:
type MatchCenterTab = 'schedule' | 'live' | 'odds'
type OddsShowType = '' | 'live' | 'today' | 'early'
const activeTab = ref<MatchCenterTab>('schedule')
const currentSport = ref(0)
const currentLeague = ref('')
组件统一加载 getSportTypes()、getMatchLeagues(),运动和联赛分类只在赛程 Tab 显示并刷新赛程数据。
- Step 2: 迁移赛程列表
从 MatchCenterPanel.vue 迁移:
-
liveList、upcomingList、endedList -
已结束赛事分页
-
matchGroups -
分组折叠
-
fetchMatchList()、loadMore()、goDetail() -
当前运动和联赛筛选
-
现有
match-card列表模板与样式 -
Step 3: 增加直播内容
调用:
getMatchLiveCards()
直播卡复用世界杯直播卡现有的地址选择、主播信息、观看/关注人数格式化以及 H5/App 播放兼容逻辑,但不渲染世界杯主题头部。
- Step 4: 增加赔率内容和筛选
赔率请求:
getMatchOdds({
show_type: oddsShowType.value || undefined,
keyword: oddsKeyword.value || undefined,
include_special: 0,
limit: 300
})
保留:
-
全部/滚球盘/今日盘/早盘筛选
-
联赛/球队搜索输入框
-
不显示运动分类和联赛分类条
-
筛选、清空
-
独赢、让球、大小球盘口卡
-
匹配到站内赛事时进入比赛详情
-
Step 5: 精简赛事中心外壳
MatchCenterPanel.vue 只保留:
<view class="match-header__title">...</view>
<view class="topic-entry" @tap="openWorldCupCenter">...</view>
<MatchCenterTabsPanel />
删除原 topic-tabs 的世界杯视图切换事件。蓝色专题卡仍调用:
emit('open-worldcup', { tab: 'schedule', cid })
Task 5: 文档同步与人工验证建议
Files:
-
Modify:
业务进度管理.md -
Modify:
设计稿映射.md -
Step 1: 记录完成内容
在业务进度中增加:
-
赛事中心三个通用 Tab。
-
通用直播和赔率接口。
-
世界杯专题入口与专题页保持兼容。
-
本次未执行构建、测试和部署。
-
Step 2: 更新设计稿映射
将赛事中心备注更新为“赛程/直播/赔率在当前页面内容区切换,世界杯专题卡仍进入专题页”。
- Step 3: 建议验证命令,不自动执行
php -l server\app\api\controller\MatchController.php
php -l server\app\common\service\match\MatchLiveService.php
cd uniapp
npm run build:h5
建议浏览器人工验证:
- 赛事中心默认显示赛程。
- 三个 Tab 切换时 URL 和页面外壳不切换为世界杯主题。
- 直播按运动/联赛刷新。
- 赔率盘口和关键词筛选有效。
- 世界杯专题卡仍能进入专题页。