Initial commit: hardened DeerFlow factory

Vendored deer-flow upstream (bytedance/deer-flow) plus prompt-injection
hardening:

- New deerflow.security package: content_delimiter, html_cleaner,
  sanitizer (8 layers — invisible chars, control chars, symbols, NFC,
  PUA, tag chars, horizontal whitespace collapse with newline/tab
  preservation, length cap)
- New deerflow.community.searx package: web_search, web_fetch,
  image_search backed by a private SearX instance, every external
  string sanitized and wrapped in <<<EXTERNAL_UNTRUSTED_CONTENT>>>
  delimiters
- All native community web providers (ddg_search, tavily, exa,
  firecrawl, jina_ai, infoquest, image_search) replaced with hard-fail
  stubs that raise NativeWebToolDisabledError at import time, so a
  misconfigured tool.use path fails loud rather than silently falling
  back to unsanitized output
- Native client back-doors (jina_client.py, infoquest_client.py)
  stubbed too
- Native-tool tests quarantined under tests/_disabled_native/
  (collect_ignore_glob via local conftest.py)
- Sanitizer Layer 7 fix: only collapse horizontal whitespace, preserve
  newlines and tabs so list/table structure survives
- Hardened runtime config.yaml references only the searx-backed tools
- Factory overlay (backend/) kept in sync with deer-flow tree as a
  reference / source

See HARDENING.md for the full audit trail and verification steps.
This commit is contained in:
2026-04-12 14:23:57 +02:00
commit 6de0bf9f5b
889 changed files with 173052 additions and 0 deletions

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,4 @@
<link
rel="icon"
href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>⚽</text></svg>"
/>

View File

@@ -0,0 +1,385 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>江苏城市足球联赛2025赛季 | 苏超联赛第一季</title>
<link rel="stylesheet" href="css/style.css" />
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"
/>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700;800;900&family=Oswald:wght@300;400;500;600;700&family=Inter:wght@300;400;500;600;700&display=swap"
rel="stylesheet"
/>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css"
/>
<link
rel="icon"
href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>⚽</text></svg>"
/>
</head>
<body>
<!-- 加载动画 -->
<div class="loader">
<div class="loader-content">
<div class="football"></div>
<div class="loader-text">加载中...</div>
</div>
</div>
<!-- 导航栏 -->
<nav class="navbar">
<div class="container">
<div class="nav-brand">
<div class="logo">
<div class="logo-ball"></div>
<span class="logo-text">苏超联赛</span>
</div>
<div class="league-name">江苏城市足球联赛2025赛季</div>
</div>
<div class="nav-menu">
<a href="#home" class="nav-link active">首页</a>
<a href="#teams" class="nav-link">球队</a>
<a href="#fixtures" class="nav-link">赛程</a>
<a href="#standings" class="nav-link">积分榜</a>
<a href="#stats" class="nav-link">数据</a>
<a href="#news" class="nav-link">新闻</a>
</div>
<div class="nav-actions">
<button class="btn-theme-toggle">
<i class="fas fa-moon"></i>
</button>
<button class="btn-menu-toggle">
<i class="fas fa-bars"></i>
</button>
</div>
</div>
</nav>
<!-- 主内容区 -->
<main>
<!-- 英雄区域 -->
<section id="home" class="hero">
<div class="hero-background">
<div class="hero-gradient"></div>
<div class="hero-pattern"></div>
<div class="hero-ball-animation"></div>
</div>
<div class="container">
<div class="hero-content">
<div class="hero-badge">
<span class="badge-season">2025赛季</span>
<span class="badge-league">苏超联赛第一季</span>
</div>
<h1 class="hero-title">
<span class="title-line">江苏城市</span>
<span class="title-line highlight">足球联赛</span>
</h1>
<p class="hero-subtitle">
江苏省首个城市间职业足球联赛汇集12支精英球队点燃2025赛季战火
</p>
<div class="hero-stats">
<div class="stat-item">
<div class="stat-number">12</div>
<div class="stat-label">参赛球队</div>
</div>
<div class="stat-item">
<div class="stat-number">132</div>
<div class="stat-label">场比赛</div>
</div>
<div class="stat-item">
<div class="stat-number">26</div>
<div class="stat-label">比赛周</div>
</div>
<div class="stat-item">
<div class="stat-number">1</div>
<div class="stat-label">冠军荣耀</div>
</div>
</div>
<div class="hero-actions">
<a href="#fixtures" class="btn btn-primary">
<i class="fas fa-calendar-alt"></i>
查看赛程
</a>
<a href="#standings" class="btn btn-secondary">
<i class="fas fa-trophy"></i>
积分榜
</a>
</div>
</div>
<div class="hero-visual">
<div class="stadium-visual">
<div class="stadium-field"></div>
<div class="stadium-stands"></div>
<div class="stadium-players">
<div class="player player-1"></div>
<div class="player player-2"></div>
<div class="player player-3"></div>
</div>
<div class="stadium-ball"></div>
</div>
</div>
</div>
<div class="hero-scroll">
<div class="scroll-indicator">
<div class="scroll-line"></div>
</div>
</div>
</section>
<!-- 下一场比赛 -->
<section class="next-match">
<div class="container">
<div class="section-header">
<h2 class="section-title">下一场比赛</h2>
<div class="section-subtitle">即将开始的精彩对决</div>
</div>
<div class="match-card">
<div class="match-date">
<div class="match-day">周六</div>
<div class="match-date-number">25</div>
<div class="match-month">一月</div>
<div class="match-time">19:30</div>
</div>
<div class="match-teams">
<div class="team team-home">
<div class="team-logo logo-nanjing"></div>
<div class="team-name">南京城联</div>
<div class="team-record">8胜 3平 2负</div>
</div>
<div class="match-vs">
<div class="vs-text">VS</div>
<div class="match-info">
<div class="match-venue">南京奥体中心</div>
<div class="match-round">第12轮</div>
</div>
</div>
<div class="team team-away">
<div class="team-logo logo-suzhou"></div>
<div class="team-name">苏州雄狮</div>
<div class="team-record">7胜 4平 2负</div>
</div>
</div>
<div class="match-actions">
<button class="btn btn-outline">
<i class="fas fa-bell"></i>
设置提醒
</button>
<button class="btn btn-primary">
<i class="fas fa-ticket-alt"></i>
购票
</button>
</div>
</div>
</div>
</section>
<!-- 球队展示 -->
<section id="teams" class="teams-section">
<div class="container">
<div class="section-header">
<h2 class="section-title">参赛球队</h2>
<div class="section-subtitle">12支城市代表队的荣耀之战</div>
</div>
<div class="teams-grid">
<!-- 球队卡片将通过JS动态生成 -->
</div>
</div>
</section>
<!-- 积分榜 -->
<section id="standings" class="standings-section">
<div class="container">
<div class="section-header">
<h2 class="section-title">积分榜</h2>
<div class="section-subtitle">2025赛季实时排名</div>
</div>
<div class="standings-container">
<div class="standings-table">
<table>
<thead>
<tr>
<th>排名</th>
<th>球队</th>
<th>场次</th>
<th></th>
<th></th>
<th></th>
<th>进球</th>
<th>失球</th>
<th>净胜球</th>
<th>积分</th>
</tr>
</thead>
<tbody>
<!-- 积分榜数据将通过JS动态生成 -->
</tbody>
</table>
</div>
</div>
</div>
</section>
<!-- 赛程表 -->
<section id="fixtures" class="fixtures-section">
<div class="container">
<div class="section-header">
<h2 class="section-title">赛程表</h2>
<div class="section-subtitle">2025赛季完整赛程</div>
</div>
<div class="fixtures-tabs">
<div class="tabs">
<button class="tab active" data-round="all">全部赛程</button>
<button class="tab" data-round="next">即将比赛</button>
<button class="tab" data-round="recent">最近赛果</button>
</div>
<div class="fixtures-list">
<!-- 赛程数据将通过JS动态生成 -->
</div>
</div>
</div>
</section>
<!-- 数据统计 -->
<section id="stats" class="stats-section">
<div class="container">
<div class="section-header">
<h2 class="section-title">数据统计</h2>
<div class="section-subtitle">球员与球队数据排行榜</div>
</div>
<div class="stats-tabs">
<div class="stats-tab-nav">
<button class="stats-tab active" data-tab="scorers">
射手榜
</button>
<button class="stats-tab" data-tab="assists">助攻榜</button>
<button class="stats-tab" data-tab="teams">球队数据</button>
</div>
<div class="stats-content">
<div class="stats-tab-content active" id="scorers">
<!-- 射手榜数据 -->
</div>
<div class="stats-tab-content" id="assists">
<!-- 助攻榜数据 -->
</div>
<div class="stats-tab-content" id="teams">
<!-- 球队数据 -->
</div>
</div>
</div>
</div>
</section>
<!-- 新闻动态 -->
<section id="news" class="news-section">
<div class="container">
<div class="section-header">
<h2 class="section-title">新闻动态</h2>
<div class="section-subtitle">联赛最新资讯</div>
</div>
<div class="news-grid">
<!-- 新闻卡片将通过JS动态生成 -->
</div>
</div>
</section>
<!-- 底部 -->
<footer class="footer">
<div class="container">
<div class="footer-content">
<div class="footer-brand">
<div class="logo">
<div class="logo-ball"></div>
<span class="logo-text">苏超联赛</span>
</div>
<div class="footer-description">
江苏城市足球联赛2025赛季官方网站
</div>
<div class="footer-social">
<a href="#" class="social-link"><i class="fab fa-weibo"></i></a>
<a href="#" class="social-link"
><i class="fab fa-weixin"></i
></a>
<a href="#" class="social-link"
><i class="fab fa-douyin"></i
></a>
<a href="#" class="social-link"
><i class="fab fa-bilibili"></i
></a>
</div>
</div>
<div class="footer-links">
<div class="footer-column">
<h3 class="footer-title">联赛信息</h3>
<a href="#" class="footer-link">关于联赛</a>
<a href="#" class="footer-link">联赛章程</a>
<a href="#" class="footer-link">组织机构</a>
<a href="#" class="footer-link">合作伙伴</a>
</div>
<div class="footer-column">
<h3 class="footer-title">球迷服务</h3>
<a href="#" class="footer-link">票务信息</a>
<a href="#" class="footer-link">球迷社区</a>
<a href="#" class="footer-link">官方商店</a>
<a href="#" class="footer-link">联系我们</a>
</div>
<div class="footer-column">
<h3 class="footer-title">媒体中心</h3>
<a href="#" class="footer-link">新闻发布</a>
<a href="#" class="footer-link">媒体资料</a>
<a href="#" class="footer-link">采访申请</a>
<a href="#" class="footer-link">摄影图库</a>
</div>
</div>
</div>
<div class="footer-bottom">
<div class="copyright">
&copy; 2025 江苏城市足球联赛. 保留所有权利.
</div>
<div class="footer-legal">
<a href="#" class="legal-link">隐私政策</a>
<a href="#" class="legal-link">使用条款</a>
<a href="#" class="legal-link">Cookie政策</a>
</div>
</div>
</div>
</footer>
</main>
<!-- JavaScript文件 -->
<script src="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js"></script>
<script src="js/data.js"></script>
<script src="js/main.js"></script>
</body>
</html>

View File

@@ -0,0 +1,808 @@
// 江苏城市足球联赛2025赛季 - 数据文件
const leagueData = {
// 联赛信息
leagueInfo: {
name: "江苏城市足球联赛",
season: "2025赛季",
alias: "苏超联赛第一季",
teamsCount: 12,
totalMatches: 132,
weeks: 26,
startDate: "2025-03-01",
endDate: "2025-10-31",
},
// 参赛球队
teams: [
{
id: 1,
name: "南京城联",
city: "南京",
shortName: "NJL",
colors: ["#dc2626", "#ef4444"],
founded: 2020,
stadium: "南京奥体中心",
capacity: 62000,
manager: "张伟",
captain: "李明",
},
{
id: 2,
name: "苏州雄狮",
city: "苏州",
shortName: "SZS",
colors: ["#059669", "#10b981"],
founded: 2019,
stadium: "苏州奥林匹克体育中心",
capacity: 45000,
manager: "王强",
captain: "陈浩",
},
{
id: 3,
name: "无锡太湖",
city: "无锡",
shortName: "WXT",
colors: ["#3b82f6", "#60a5fa"],
founded: 2021,
stadium: "无锡体育中心",
capacity: 32000,
manager: "赵刚",
captain: "刘洋",
},
{
id: 4,
name: "常州龙城",
city: "常州",
shortName: "CZL",
colors: ["#7c3aed", "#8b5cf6"],
founded: 2022,
stadium: "常州奥林匹克体育中心",
capacity: 38000,
manager: "孙磊",
captain: "周涛",
},
{
id: 5,
name: "镇江金山",
city: "镇江",
shortName: "ZJJ",
colors: ["#f59e0b", "#fbbf24"],
founded: 2020,
stadium: "镇江体育会展中心",
capacity: 28000,
manager: "吴斌",
captain: "郑军",
},
{
id: 6,
name: "扬州运河",
city: "扬州",
shortName: "YZY",
colors: ["#ec4899", "#f472b6"],
founded: 2021,
stadium: "扬州体育公园",
capacity: 35000,
manager: "钱勇",
captain: "王磊",
},
{
id: 7,
name: "南通江海",
city: "南通",
shortName: "NTJ",
colors: ["#0ea5e9", "#38bdf8"],
founded: 2022,
stadium: "南通体育会展中心",
capacity: 32000,
manager: "冯超",
captain: "张勇",
},
{
id: 8,
name: "徐州楚汉",
city: "徐州",
shortName: "XZC",
colors: ["#84cc16", "#a3e635"],
founded: 2019,
stadium: "徐州奥体中心",
capacity: 42000,
manager: "陈明",
captain: "李强",
},
{
id: 9,
name: "淮安运河",
city: "淮安",
shortName: "HAY",
colors: ["#f97316", "#fb923c"],
founded: 2021,
stadium: "淮安体育中心",
capacity: 30000,
manager: "周伟",
captain: "吴刚",
},
{
id: 10,
name: "盐城黄海",
city: "盐城",
shortName: "YCH",
colors: ["#06b6d4", "#22d3ee"],
founded: 2020,
stadium: "盐城体育中心",
capacity: 32000,
manager: "郑涛",
captain: "孙明",
},
{
id: 11,
name: "泰州凤城",
city: "泰州",
shortName: "TZF",
colors: ["#8b5cf6", "#a78bfa"],
founded: 2022,
stadium: "泰州体育公园",
capacity: 28000,
manager: "王刚",
captain: "陈涛",
},
{
id: 12,
name: "宿迁西楚",
city: "宿迁",
shortName: "SQC",
colors: ["#10b981", "#34d399"],
founded: 2021,
stadium: "宿迁体育中心",
capacity: 26000,
manager: "李伟",
captain: "张刚",
},
],
// 积分榜数据
standings: [
{
rank: 1,
teamId: 1,
played: 13,
won: 8,
drawn: 3,
lost: 2,
goalsFor: 24,
goalsAgainst: 12,
goalDifference: 12,
points: 27,
},
{
rank: 2,
teamId: 2,
played: 13,
won: 7,
drawn: 4,
lost: 2,
goalsFor: 22,
goalsAgainst: 14,
goalDifference: 8,
points: 25,
},
{
rank: 3,
teamId: 8,
played: 13,
won: 7,
drawn: 3,
lost: 3,
goalsFor: 20,
goalsAgainst: 15,
goalDifference: 5,
points: 24,
},
{
rank: 4,
teamId: 3,
played: 13,
won: 6,
drawn: 4,
lost: 3,
goalsFor: 18,
goalsAgainst: 14,
goalDifference: 4,
points: 22,
},
{
rank: 5,
teamId: 4,
played: 13,
won: 6,
drawn: 3,
lost: 4,
goalsFor: 19,
goalsAgainst: 16,
goalDifference: 3,
points: 21,
},
{
rank: 6,
teamId: 6,
played: 13,
won: 5,
drawn: 5,
lost: 3,
goalsFor: 17,
goalsAgainst: 15,
goalDifference: 2,
points: 20,
},
{
rank: 7,
teamId: 5,
played: 13,
won: 5,
drawn: 4,
lost: 4,
goalsFor: 16,
goalsAgainst: 15,
goalDifference: 1,
points: 19,
},
{
rank: 8,
teamId: 7,
played: 13,
won: 4,
drawn: 5,
lost: 4,
goalsFor: 15,
goalsAgainst: 16,
goalDifference: -1,
points: 17,
},
{
rank: 9,
teamId: 10,
played: 13,
won: 4,
drawn: 4,
lost: 5,
goalsFor: 14,
goalsAgainst: 17,
goalDifference: -3,
points: 16,
},
{
rank: 10,
teamId: 9,
played: 13,
won: 3,
drawn: 5,
lost: 5,
goalsFor: 13,
goalsAgainst: 18,
goalDifference: -5,
points: 14,
},
{
rank: 11,
teamId: 11,
played: 13,
won: 2,
drawn: 4,
lost: 7,
goalsFor: 11,
goalsAgainst: 20,
goalDifference: -9,
points: 10,
},
{
rank: 12,
teamId: 12,
played: 13,
won: 1,
drawn: 3,
lost: 9,
goalsFor: 9,
goalsAgainst: 24,
goalDifference: -15,
points: 6,
},
],
// 赛程数据
fixtures: [
{
id: 1,
round: 1,
date: "2025-03-01",
time: "15:00",
homeTeamId: 1,
awayTeamId: 2,
venue: "南京奥体中心",
status: "completed",
homeScore: 2,
awayScore: 1,
},
{
id: 2,
round: 1,
date: "2025-03-01",
time: "15:00",
homeTeamId: 3,
awayTeamId: 4,
venue: "无锡体育中心",
status: "completed",
homeScore: 1,
awayScore: 1,
},
{
id: 3,
round: 1,
date: "2025-03-02",
time: "19:30",
homeTeamId: 5,
awayTeamId: 6,
venue: "镇江体育会展中心",
status: "completed",
homeScore: 0,
awayScore: 2,
},
{
id: 4,
round: 1,
date: "2025-03-02",
time: "19:30",
homeTeamId: 7,
awayTeamId: 8,
venue: "南通体育会展中心",
status: "completed",
homeScore: 1,
awayScore: 3,
},
{
id: 5,
round: 1,
date: "2025-03-03",
time: "15:00",
homeTeamId: 9,
awayTeamId: 10,
venue: "淮安体育中心",
status: "completed",
homeScore: 2,
awayScore: 2,
},
{
id: 6,
round: 1,
date: "2025-03-03",
time: "15:00",
homeTeamId: 11,
awayTeamId: 12,
venue: "泰州体育公园",
status: "completed",
homeScore: 1,
awayScore: 0,
},
{
id: 7,
round: 2,
date: "2025-03-08",
time: "15:00",
homeTeamId: 2,
awayTeamId: 3,
venue: "苏州奥林匹克体育中心",
status: "completed",
homeScore: 2,
awayScore: 0,
},
{
id: 8,
round: 2,
date: "2025-03-08",
time: "15:00",
homeTeamId: 4,
awayTeamId: 5,
venue: "常州奥林匹克体育中心",
status: "completed",
homeScore: 3,
awayScore: 1,
},
{
id: 9,
round: 2,
date: "2025-03-09",
time: "19:30",
homeTeamId: 6,
awayTeamId: 7,
venue: "扬州体育公园",
status: "completed",
homeScore: 1,
awayScore: 1,
},
{
id: 10,
round: 2,
date: "2025-03-09",
time: "19:30",
homeTeamId: 8,
awayTeamId: 9,
venue: "徐州奥体中心",
status: "completed",
homeScore: 2,
awayScore: 0,
},
{
id: 11,
round: 2,
date: "2025-03-10",
time: "15:00",
homeTeamId: 10,
awayTeamId: 11,
venue: "盐城体育中心",
status: "completed",
homeScore: 1,
awayScore: 0,
},
{
id: 12,
round: 2,
date: "2025-03-10",
time: "15:00",
homeTeamId: 12,
awayTeamId: 1,
venue: "宿迁体育中心",
status: "completed",
homeScore: 0,
awayScore: 3,
},
{
id: 13,
round: 12,
date: "2025-05-24",
time: "19:30",
homeTeamId: 1,
awayTeamId: 2,
venue: "南京奥体中心",
status: "scheduled",
},
{
id: 14,
round: 12,
date: "2025-05-24",
time: "15:00",
homeTeamId: 3,
awayTeamId: 4,
venue: "无锡体育中心",
status: "scheduled",
},
{
id: 15,
round: 12,
date: "2025-05-25",
time: "19:30",
homeTeamId: 5,
awayTeamId: 6,
venue: "镇江体育会展中心",
status: "scheduled",
},
{
id: 16,
round: 12,
date: "2025-05-25",
time: "15:00",
homeTeamId: 7,
awayTeamId: 8,
venue: "南通体育会展中心",
status: "scheduled",
},
{
id: 17,
round: 12,
date: "2025-05-26",
time: "19:30",
homeTeamId: 9,
awayTeamId: 10,
venue: "淮安体育中心",
status: "scheduled",
},
{
id: 18,
round: 12,
date: "2025-05-26",
time: "15:00",
homeTeamId: 11,
awayTeamId: 12,
venue: "泰州体育公园",
status: "scheduled",
},
],
// 球员数据
players: {
scorers: [
{
rank: 1,
playerId: 101,
name: "张伟",
teamId: 1,
goals: 12,
assists: 4,
matches: 13,
minutes: 1170,
},
{
rank: 2,
playerId: 102,
name: "李明",
teamId: 1,
goals: 8,
assists: 6,
matches: 13,
minutes: 1170,
},
{
rank: 3,
playerId: 201,
name: "王强",
teamId: 2,
goals: 7,
assists: 5,
matches: 13,
minutes: 1170,
},
{
rank: 4,
playerId: 301,
name: "赵刚",
teamId: 3,
goals: 6,
assists: 3,
matches: 13,
minutes: 1170,
},
{
rank: 5,
playerId: 801,
name: "陈明",
teamId: 8,
goals: 6,
assists: 2,
matches: 13,
minutes: 1170,
},
{
rank: 6,
playerId: 401,
name: "孙磊",
teamId: 4,
goals: 5,
assists: 4,
matches: 13,
minutes: 1170,
},
{
rank: 7,
playerId: 601,
name: "钱勇",
teamId: 6,
goals: 5,
assists: 3,
matches: 13,
minutes: 1170,
},
{
rank: 8,
playerId: 501,
name: "吴斌",
teamId: 5,
goals: 4,
assists: 5,
matches: 13,
minutes: 1170,
},
{
rank: 9,
playerId: 701,
name: "冯超",
teamId: 7,
goals: 4,
assists: 3,
matches: 13,
minutes: 1170,
},
{
rank: 10,
playerId: 1001,
name: "郑涛",
teamId: 10,
goals: 3,
assists: 2,
matches: 13,
minutes: 1170,
},
],
assists: [
{
rank: 1,
playerId: 102,
name: "李明",
teamId: 1,
assists: 6,
goals: 8,
matches: 13,
minutes: 1170,
},
{
rank: 2,
playerId: 501,
name: "吴斌",
teamId: 5,
assists: 5,
goals: 4,
matches: 13,
minutes: 1170,
},
{
rank: 3,
playerId: 201,
name: "王强",
teamId: 2,
assists: 5,
goals: 7,
matches: 13,
minutes: 1170,
},
{
rank: 4,
playerId: 401,
name: "孙磊",
teamId: 4,
assists: 4,
goals: 5,
matches: 13,
minutes: 1170,
},
{
rank: 5,
playerId: 101,
name: "张伟",
teamId: 1,
assists: 4,
goals: 12,
matches: 13,
minutes: 1170,
},
{
rank: 6,
playerId: 301,
name: "赵刚",
teamId: 3,
assists: 3,
goals: 6,
matches: 13,
minutes: 1170,
},
{
rank: 7,
playerId: 601,
name: "钱勇",
teamId: 6,
assists: 3,
goals: 5,
matches: 13,
minutes: 1170,
},
{
rank: 8,
playerId: 701,
name: "冯超",
teamId: 7,
assists: 3,
goals: 4,
matches: 13,
minutes: 1170,
},
{
rank: 9,
playerId: 901,
name: "周伟",
teamId: 9,
assists: 3,
goals: 2,
matches: 13,
minutes: 1170,
},
{
rank: 10,
playerId: 1101,
name: "王刚",
teamId: 11,
assists: 2,
goals: 1,
matches: 13,
minutes: 1170,
},
],
},
// 新闻数据
news: [
{
id: 1,
title: "南京城联主场力克苏州雄狮,继续领跑积分榜",
excerpt:
"在昨晚进行的第12轮焦点战中南京城联凭借张伟的梅开二度主场2-1战胜苏州雄狮继续以2分优势领跑积分榜。",
category: "比赛战报",
date: "2025-05-25",
imageColor: "#dc2626",
},
{
id: 2,
title: "联赛最佳球员揭晓张伟当选4月最佳",
excerpt:
"江苏城市足球联赛官方宣布南京城联前锋张伟凭借出色的表现当选4月份联赛最佳球员。",
category: "官方公告",
date: "2025-05-20",
imageColor: "#3b82f6",
},
{
id: 3,
title: "徐州楚汉签下前国脚李强,实力大增",
excerpt:
"徐州楚汉俱乐部官方宣布,与前国家队中场李强签约两年,这位经验丰富的老将将提升球队中场实力。",
category: "转会新闻",
date: "2025-05-18",
imageColor: "#84cc16",
},
{
id: 4,
title: "联赛半程总结:竞争激烈,多队有望争冠",
excerpt:
"随着联赛进入半程积分榜前六名球队分差仅7分本赛季冠军争夺异常激烈多支球队都有机会问鼎。",
category: "联赛动态",
date: "2025-05-15",
imageColor: "#f59e0b",
},
{
id: 5,
title: "球迷互动日:各俱乐部将举办开放训练",
excerpt:
"为感谢球迷支持,各俱乐部将在本周末举办球迷开放日,球迷可近距离观看球队训练并与球员互动。",
category: "球迷活动",
date: "2025-05-12",
imageColor: "#ec4899",
},
{
id: 6,
title: "技术统计:联赛进球数创历史新高",
excerpt:
"本赛季前13轮共打进176球场均2.77球,创下联赛历史同期最高进球纪录,进攻足球成为主流。",
category: "数据统计",
date: "2025-05-10",
imageColor: "#0ea5e9",
},
],
};
// 工具函数根据ID获取球队信息
function getTeamById(teamId) {
return leagueData.teams.find((team) => team.id === teamId);
}
// 工具函数:格式化日期
function formatDate(dateString) {
const date = new Date(dateString);
const options = { weekday: "short", month: "short", day: "numeric" };
return date.toLocaleDateString("zh-CN", options);
}
// 工具函数:格式化时间
function formatTime(timeString) {
return timeString;
}
// 导出数据
if (typeof module !== "undefined" && module.exports) {
module.exports = leagueData;
}

View File

@@ -0,0 +1,645 @@
// 江苏城市足球联赛2025赛季 - 主JavaScript文件
document.addEventListener("DOMContentLoaded", function () {
// 初始化加载动画
initLoader();
// 初始化主题切换
initThemeToggle();
// 初始化导航菜单
initNavigation();
// 初始化滚动监听
initScrollSpy();
// 渲染球队卡片
renderTeams();
// 渲染积分榜
renderStandings();
// 渲染赛程表
renderFixtures();
// 渲染数据统计
renderStats();
// 渲染新闻动态
renderNews();
// 初始化标签页切换
initTabs();
// 初始化移动端菜单
initMobileMenu();
});
// 加载动画
function initLoader() {
const loader = document.querySelector(".loader");
// 模拟加载延迟
setTimeout(() => {
loader.classList.add("loaded");
// 动画结束后隐藏loader
setTimeout(() => {
loader.style.display = "none";
}, 300);
}, 1500);
}
// 主题切换
function initThemeToggle() {
const themeToggle = document.querySelector(".btn-theme-toggle");
const themeIcon = themeToggle.querySelector("i");
// 检查本地存储的主题偏好
const savedTheme = localStorage.getItem("theme") || "light";
document.documentElement.setAttribute("data-theme", savedTheme);
updateThemeIcon(savedTheme);
themeToggle.addEventListener("click", () => {
const currentTheme = document.documentElement.getAttribute("data-theme");
const newTheme = currentTheme === "light" ? "dark" : "light";
document.documentElement.setAttribute("data-theme", newTheme);
localStorage.setItem("theme", newTheme);
updateThemeIcon(newTheme);
// 添加切换动画
themeToggle.style.transform = "scale(0.9)";
setTimeout(() => {
themeToggle.style.transform = "";
}, 150);
});
function updateThemeIcon(theme) {
if (theme === "dark") {
themeIcon.className = "fas fa-sun";
} else {
themeIcon.className = "fas fa-moon";
}
}
}
// 导航菜单
function initNavigation() {
const navLinks = document.querySelectorAll(".nav-link");
navLinks.forEach((link) => {
link.addEventListener("click", function (e) {
e.preventDefault();
const targetId = this.getAttribute("href");
const targetSection = document.querySelector(targetId);
if (targetSection) {
// 更新活动链接
navLinks.forEach((l) => l.classList.remove("active"));
this.classList.add("active");
// 平滑滚动到目标区域
window.scrollTo({
top: targetSection.offsetTop - 80,
behavior: "smooth",
});
// 如果是移动端,关闭菜单
const navMenu = document.querySelector(".nav-menu");
if (navMenu.classList.contains("active")) {
navMenu.classList.remove("active");
}
}
});
});
}
// 滚动监听
function initScrollSpy() {
const sections = document.querySelectorAll("section[id]");
const navLinks = document.querySelectorAll(".nav-link");
window.addEventListener("scroll", () => {
let current = "";
sections.forEach((section) => {
const sectionTop = section.offsetTop;
const sectionHeight = section.clientHeight;
if (scrollY >= sectionTop - 100) {
current = section.getAttribute("id");
}
});
navLinks.forEach((link) => {
link.classList.remove("active");
if (link.getAttribute("href") === `#${current}`) {
link.classList.add("active");
}
});
});
}
// 渲染球队卡片
function renderTeams() {
const teamsGrid = document.querySelector(".teams-grid");
if (!teamsGrid) return;
teamsGrid.innerHTML = "";
leagueData.teams.forEach((team) => {
const teamCard = document.createElement("div");
teamCard.className = "team-card";
// 获取球队统计数据
const standing = leagueData.standings.find((s) => s.teamId === team.id);
teamCard.innerHTML = `
<div class="team-card-logo" style="background: linear-gradient(135deg, ${team.colors[0]} 0%, ${team.colors[1]} 100%);">
${team.shortName}
</div>
<h3 class="team-card-name">${team.name}</h3>
<div class="team-card-city">${team.city}</div>
<div class="team-card-stats">
<div class="team-stat">
<div class="team-stat-value">${standing ? standing.rank : "-"}</div>
<div class="team-stat-label">排名</div>
</div>
<div class="team-stat">
<div class="team-stat-value">${standing ? standing.points : "0"}</div>
<div class="team-stat-label">积分</div>
</div>
<div class="team-stat">
<div class="team-stat-value">${standing ? standing.goalDifference : "0"}</div>
<div class="team-stat-label">净胜球</div>
</div>
</div>
`;
teamCard.addEventListener("click", () => {
// 这里可以添加点击跳转到球队详情页的功能
alert(`查看 ${team.name} 的详细信息`);
});
teamsGrid.appendChild(teamCard);
});
}
// 渲染积分榜
function renderStandings() {
const standingsTable = document.querySelector(".standings-table tbody");
if (!standingsTable) return;
standingsTable.innerHTML = "";
leagueData.standings.forEach((standing) => {
const team = getTeamById(standing.teamId);
const row = document.createElement("tr");
// 根据排名添加特殊样式
if (standing.rank <= 4) {
row.classList.add("champions-league");
} else if (standing.rank <= 6) {
row.classList.add("europa-league");
} else if (standing.rank >= 11) {
row.classList.add("relegation");
}
row.innerHTML = `
<td>${standing.rank}</td>
<td>
<div style="display: flex; align-items: center; gap: 0.5rem;">
<div class="team-logo-small" style="width: 24px; height: 24px; border-radius: 50%; background: linear-gradient(135deg, ${team.colors[0]} 0%, ${team.colors[1]} 100%);"></div>
${team.name}
</div>
</td>
<td>${standing.played}</td>
<td>${standing.won}</td>
<td>${standing.drawn}</td>
<td>${standing.lost}</td>
<td>${standing.goalsFor}</td>
<td>${standing.goalsAgainst}</td>
<td>${standing.goalDifference > 0 ? "+" : ""}${standing.goalDifference}</td>
<td><strong>${standing.points}</strong></td>
`;
standingsTable.appendChild(row);
});
}
// 渲染赛程表
function renderFixtures() {
const fixturesList = document.querySelector(".fixtures-list");
if (!fixturesList) return;
fixturesList.innerHTML = "";
// 按轮次分组
const fixturesByRound = {};
leagueData.fixtures.forEach((fixture) => {
if (!fixturesByRound[fixture.round]) {
fixturesByRound[fixture.round] = [];
}
fixturesByRound[fixture.round].push(fixture);
});
// 渲染所有赛程
Object.keys(fixturesByRound)
.sort((a, b) => a - b)
.forEach((round) => {
const roundHeader = document.createElement("div");
roundHeader.className = "fixture-round-header";
roundHeader.innerHTML = `<h3>第${round}轮</h3>`;
fixturesList.appendChild(roundHeader);
fixturesByRound[round].forEach((fixture) => {
const homeTeam = getTeamById(fixture.homeTeamId);
const awayTeam = getTeamById(fixture.awayTeamId);
const fixtureItem = document.createElement("div");
fixtureItem.className = "fixture-item";
const date = new Date(fixture.date);
const dayNames = [
"周日",
"周一",
"周二",
"周三",
"周四",
"周五",
"周六",
];
const dayName = dayNames[date.getDay()];
let scoreHtml = "";
let statusText = "";
if (fixture.status === "completed") {
scoreHtml = `
<div class="fixture-score-value">${fixture.homeScore} - ${fixture.awayScore}</div>
<div class="fixture-score-status">已结束</div>
`;
} else if (fixture.status === "scheduled") {
scoreHtml = `
<div class="fixture-score-value">VS</div>
<div class="fixture-score-status">${fixture.time}</div>
`;
} else {
scoreHtml = `
<div class="fixture-score-value">-</div>
<div class="fixture-score-status">待定</div>
`;
}
fixtureItem.innerHTML = `
<div class="fixture-date">
<div class="fixture-day">${dayName}</div>
<div class="fixture-time">${formatDate(fixture.date)}</div>
</div>
<div class="fixture-teams">
<div class="fixture-team home">
<div class="fixture-team-name">${homeTeam.name}</div>
<div class="fixture-team-logo" style="background: linear-gradient(135deg, ${homeTeam.colors[0]} 0%, ${homeTeam.colors[1]} 100%);"></div>
</div>
<div class="fixture-vs">VS</div>
<div class="fixture-team away">
<div class="fixture-team-logo" style="background: linear-gradient(135deg, ${awayTeam.colors[0]} 0%, ${awayTeam.colors[1]} 100%);"></div>
<div class="fixture-team-name">${awayTeam.name}</div>
</div>
</div>
<div class="fixture-score">
${scoreHtml}
</div>
`;
fixturesList.appendChild(fixtureItem);
});
});
}
// 渲染数据统计
function renderStats() {
renderScorers();
renderAssists();
renderTeamStats();
}
function renderScorers() {
const scorersContainer = document.querySelector("#scorers");
if (!scorersContainer) return;
scorersContainer.innerHTML = `
<table class="stats-table">
<thead>
<tr>
<th class="stats-rank">排名</th>
<th class="stats-player">球员</th>
<th class="stats-team">球队</th>
<th class="stats-value">进球</th>
<th class="stats-value">助攻</th>
<th class="stats-value">出场</th>
</tr>
</thead>
<tbody>
${leagueData.players.scorers
.map((player) => {
const team = getTeamById(player.teamId);
return `
<tr>
<td class="stats-rank">${player.rank}</td>
<td class="stats-player">${player.name}</td>
<td class="stats-team">${team.name}</td>
<td class="stats-value">${player.goals}</td>
<td class="stats-value">${player.assists}</td>
<td class="stats-value">${player.matches}</td>
</tr>
`;
})
.join("")}
</tbody>
</table>
`;
}
function renderAssists() {
const assistsContainer = document.querySelector("#assists");
if (!assistsContainer) return;
assistsContainer.innerHTML = `
<table class="stats-table">
<thead>
<tr>
<th class="stats-rank">排名</th>
<th class="stats-player">球员</th>
<th class="stats-team">球队</th>
<th class="stats-value">助攻</th>
<th class="stats-value">进球</th>
<th class="stats-value">出场</th>
</tr>
</thead>
<tbody>
${leagueData.players.assists
.map((player) => {
const team = getTeamById(player.teamId);
return `
<tr>
<td class="stats-rank">${player.rank}</td>
<td class="stats-player">${player.name}</td>
<td class="stats-team">${team.name}</td>
<td class="stats-value">${player.assists}</td>
<td class="stats-value">${player.goals}</td>
<td class="stats-value">${player.matches}</td>
</tr>
`;
})
.join("")}
</tbody>
</table>
`;
}
function renderTeamStats() {
const teamStatsContainer = document.querySelector("#teams");
if (!teamStatsContainer) return;
// 计算球队统计数据
const teamStats = leagueData.standings
.map((standing) => {
const team = getTeamById(standing.teamId);
const goalsPerGame = (standing.goalsFor / standing.played).toFixed(2);
const concededPerGame = (standing.goalsAgainst / standing.played).toFixed(
2,
);
return {
rank: standing.rank,
team: team.name,
goalsFor: standing.goalsFor,
goalsAgainst: standing.goalsAgainst,
goalDifference: standing.goalDifference,
goalsPerGame,
concededPerGame,
cleanSheets: Math.floor(Math.random() * 5), // 模拟数据
};
})
.sort((a, b) => a.rank - b.rank);
teamStatsContainer.innerHTML = `
<table class="stats-table">
<thead>
<tr>
<th class="stats-rank">排名</th>
<th class="stats-player">球队</th>
<th class="stats-value">进球</th>
<th class="stats-value">失球</th>
<th class="stats-value">净胜球</th>
<th class="stats-value">场均进球</th>
<th class="stats-value">场均失球</th>
<th class="stats-value">零封</th>
</tr>
</thead>
<tbody>
${teamStats
.map(
(stat) => `
<tr>
<td class="stats-rank">${stat.rank}</td>
<td class="stats-player">${stat.team}</td>
<td class="stats-value">${stat.goalsFor}</td>
<td class="stats-value">${stat.goalsAgainst}</td>
<td class="stats-value">${stat.goalDifference > 0 ? "+" : ""}${stat.goalDifference}</td>
<td class="stats-value">${stat.goalsPerGame}</td>
<td class="stats-value">${stat.concededPerGame}</td>
<td class="stats-value">${stat.cleanSheets}</td>
</tr>
`,
)
.join("")}
</tbody>
</table>
`;
}
// 渲染新闻动态
function renderNews() {
const newsGrid = document.querySelector(".news-grid");
if (!newsGrid) return;
newsGrid.innerHTML = "";
leagueData.news.forEach((newsItem) => {
const newsCard = document.createElement("div");
newsCard.className = "news-card";
const date = new Date(newsItem.date);
const formattedDate = date.toLocaleDateString("zh-CN", {
year: "numeric",
month: "long",
day: "numeric",
});
newsCard.innerHTML = `
<div class="news-card-image" style="background: linear-gradient(135deg, ${newsItem.imageColor} 0%, ${darkenColor(newsItem.imageColor, 20)} 100%);"></div>
<div class="news-card-content">
<span class="news-card-category">${newsItem.category}</span>
<h3 class="news-card-title">${newsItem.title}</h3>
<p class="news-card-excerpt">${newsItem.excerpt}</p>
<div class="news-card-meta">
<span class="news-card-date">
<i class="far fa-calendar"></i>
${formattedDate}
</span>
<span class="news-card-read-more">阅读更多 →</span>
</div>
</div>
`;
newsCard.addEventListener("click", () => {
alert(`查看新闻: ${newsItem.title}`);
});
newsGrid.appendChild(newsCard);
});
}
// 初始化标签页切换
function initTabs() {
// 赛程标签页
const fixtureTabs = document.querySelectorAll(".fixtures-tabs .tab");
const fixtureItems = document.querySelectorAll(".fixture-item");
fixtureTabs.forEach((tab) => {
tab.addEventListener("click", () => {
// 更新活动标签
fixtureTabs.forEach((t) => t.classList.remove("active"));
tab.classList.add("active");
const roundFilter = tab.getAttribute("data-round");
// 这里可以根据筛选条件显示不同的赛程
// 由于时间关系,这里只是简单的演示
console.log(`筛选赛程: ${roundFilter}`);
});
});
// 数据统计标签页
const statsTabs = document.querySelectorAll(".stats-tab");
const statsContents = document.querySelectorAll(".stats-tab-content");
statsTabs.forEach((tab) => {
tab.addEventListener("click", () => {
const tabId = tab.getAttribute("data-tab");
// 更新活动标签
statsTabs.forEach((t) => t.classList.remove("active"));
tab.classList.add("active");
// 显示对应内容
statsContents.forEach((content) => {
content.classList.remove("active");
if (content.id === tabId) {
content.classList.add("active");
}
});
});
});
}
// 初始化移动端菜单
function initMobileMenu() {
const menuToggle = document.querySelector(".btn-menu-toggle");
const navMenu = document.querySelector(".nav-menu");
if (menuToggle && navMenu) {
menuToggle.addEventListener("click", () => {
navMenu.classList.toggle("active");
// 更新菜单图标
const icon = menuToggle.querySelector("i");
if (navMenu.classList.contains("active")) {
icon.className = "fas fa-times";
} else {
icon.className = "fas fa-bars";
}
});
// 点击菜单外区域关闭菜单
document.addEventListener("click", (e) => {
if (!navMenu.contains(e.target) && !menuToggle.contains(e.target)) {
navMenu.classList.remove("active");
menuToggle.querySelector("i").className = "fas fa-bars";
}
});
}
}
// 工具函数:加深颜色
function darkenColor(color, percent) {
const num = parseInt(color.replace("#", ""), 16);
const amt = Math.round(2.55 * percent);
const R = (num >> 16) - amt;
const G = ((num >> 8) & 0x00ff) - amt;
const B = (num & 0x0000ff) - amt;
return (
"#" +
(
0x1000000 +
(R < 255 ? (R < 1 ? 0 : R) : 255) * 0x10000 +
(G < 255 ? (G < 1 ? 0 : G) : 255) * 0x100 +
(B < 255 ? (B < 1 ? 0 : B) : 255)
)
.toString(16)
.slice(1)
);
}
// 工具函数:格式化日期(简写)
function formatDate(dateString) {
const date = new Date(dateString);
const month = date.getMonth() + 1;
const day = date.getDate();
return `${month}${day}`;
}
// 工具函数根据ID获取球队信息
function getTeamById(teamId) {
return leagueData.teams.find((team) => team.id === teamId);
}
// 添加一些交互效果
document.addEventListener("DOMContentLoaded", () => {
// 为所有按钮添加点击效果
const buttons = document.querySelectorAll(".btn");
buttons.forEach((button) => {
button.addEventListener("mousedown", () => {
button.style.transform = "scale(0.95)";
});
button.addEventListener("mouseup", () => {
button.style.transform = "";
});
button.addEventListener("mouseleave", () => {
button.style.transform = "";
});
});
// 为卡片添加悬停效果
const cards = document.querySelectorAll(".team-card, .news-card");
cards.forEach((card) => {
card.addEventListener("mouseenter", () => {
card.style.transition = "transform 0.3s ease, box-shadow 0.3s ease";
});
});
});