Skip to content

Commit c4d75cc

Browse files
committed
添加站点统计功能及优化前端展示
1 parent a7f1524 commit c4d75cc

File tree

8 files changed

+254
-71
lines changed

8 files changed

+254
-71
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ base_model:
5656

5757
```plaintext
5858
.
59-
├── app.py # Web应用主入口,Flask服务
59+
├── api/ # 适配Vercel的API服务
60+
│ ├── app.py # Web应用主入口,Flask服务
6061
├── main.py # 数据全流程自动化主入口
6162
├── requirements.txt # 依赖包列表
6263
├── vercel.json # Vercel部署配置
@@ -92,7 +93,6 @@ base_model:
9293
├── templates/ # Jinja2模板(index.html等)
9394
├── tool_utils/ # 工具类(数据库、日志、进度条等)
9495
├── logs/ # 日志输出目录
95-
├── .github/workflows/ # CI/CD配置
9696
└── ...
9797
```
9898

app.py renamed to api/app.py

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import json
2+
import os
23

34
import numpy as np
45
import torch
@@ -12,7 +13,12 @@
1213

1314
rich_logger = RichLogger()
1415
mysql_utils = MySQLUtils()
15-
app = Flask(__name__)
16+
17+
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
18+
TEMPLATE_DIR = os.path.join(BASE_DIR, 'templates')
19+
STATIC_DIR = os.path.join(BASE_DIR, 'static')
20+
21+
app = Flask(__name__, template_folder=TEMPLATE_DIR, static_folder=STATIC_DIR)
1622
CORS(app)
1723

1824
# ================== 数据加载 ==================
@@ -42,14 +48,31 @@
4248
rich_logger.error(f"模型加载错误: {e}")
4349
model = None # 设置为默认值或采取其他适当的错误处理措施
4450

51+
4552
# ================== 路由定义 ==================
4653
@app.route('/')
4754
def index():
4855
"""
49-
首页渲染
56+
首页渲染,并统计访问
5057
"""
58+
ip = request.remote_addr
59+
mysql_utils.record_visit(ip)
5160
return render_template('index.html')
5261

62+
63+
@app.route('/site_stats', methods=['GET'])
64+
def site_stats():
65+
"""
66+
获取站点访问次数和访问人数
67+
:return: JSON {visit_count, visitor_count}
68+
"""
69+
total, user = mysql_utils.get_site_stats()
70+
return jsonify({
71+
"visit_count": total,
72+
"visitor_count": user
73+
})
74+
75+
5376
@app.route('/query_hero', methods=['GET'])
5477
def query_hero():
5578
"""
@@ -58,6 +81,7 @@ def query_hero():
5881
"""
5982
return jsonify(hero_list)
6083

84+
6185
@app.route('/query_win_rate', methods=['GET'])
6286
def query_win_rate():
6387
"""
@@ -66,6 +90,7 @@ def query_win_rate():
6690
"""
6791
return jsonify(hero_win_rate)
6892

93+
6994
@app.route('/query_team', methods=['GET'])
7095
def query_team():
7196
"""
@@ -75,6 +100,7 @@ def query_team():
75100
data = team_list.get("data", team_list) if isinstance(team_list, dict) else team_list
76101
return jsonify(data)
77102

103+
78104
@app.route('/get_echarts_data', methods=['GET'])
79105
def get_heroes_data():
80106
"""
@@ -106,6 +132,7 @@ def get_heroes_data():
106132
}
107133
return jsonify(heroes_data)
108134

135+
109136
@app.route('/predict', methods=['POST'])
110137
def predict():
111138
"""
@@ -169,5 +196,6 @@ def predict():
169196
rich_logger.error(f"预测错误: {e}")
170197
return jsonify({"error": str(e)}), 400
171198

199+
172200
if __name__ == '__main__':
173-
app.run(debug=True)
201+
app.run()

static/css/style.css

Lines changed: 133 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ html {
44
}
55
/* 设置body的基本样式和布局 */
66
body {
7-
font-family: Arial, sans-serif; /* 设置字体 */
7+
font-family: 'ShanHaiJiGuMingKe', '思源黑体', '微软雅黑', '幼圆', '华文中宋', Arial, sans-serif;
88
background-color: #f4f4f4; /* 设置背景颜色 */
99
color: #333; /* 设置文本颜色 */
1010
margin: 0; /* 去除边距 */
@@ -24,7 +24,8 @@ body::before {
2424
left: 0; /* 左部对齐 */
2525
width: 100%; /* 宽度占满 */
2626
height: 100%; /* 高度占满 */
27-
background: url('../images/background.jpg') no-repeat center center fixed; /* 设置背景图片 */
27+
background: url("../images/background.jpg") no-repeat center center
28+
fixed; /* 设置背景图片 */
2829
background-size: cover; /* 图片等比填充 */
2930
opacity: 0.6; /* 设置透明度 */
3031
z-index: -1; /* 设置层级,确保在所有内容之下 */
@@ -33,9 +34,9 @@ body::before {
3334
/* 顶栏样式定义 */
3435
.topbar {
3536
width: 100%; /* 宽度占满 */
36-
display: flex; /* 使用弹性布局 */
37-
justify-content: space-between; /* 两端对齐 */
38-
align-items: center; /* 中心垂直对齐 */
37+
display: flex;
38+
justify-content: space-between;
39+
align-items: center;
3940
background-color: rgba(255, 255, 255, 0.6); /* 半透明白色背景 */
4041
padding: 10px 20px; /* 内边距 */
4142
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); /* 阴影效果 */
@@ -48,13 +49,13 @@ body::before {
4849
}
4950

5051
/* 顶栏中的标题样式 */
51-
.title {
52-
font-family: 幼圆, serif;
53-
font-size: 24px; /* 字号 */
54-
font-weight: bold; /* 加粗 */
55-
flex: 1; /* 占据剩余空间 */
56-
display: flex; /* 使用弹性布局进行垂直居中 */
57-
align-items: center; /* 垂直居中 */
52+
.topbar .title {
53+
flex: 1;
54+
display: flex;
55+
align-items: center;
56+
font-family: 'ShanHaiJiGuMingKe', '思源黑体', '微软雅黑', '幼圆', '华文中宋', Arial, sans-serif;
57+
font-size: 24px;
58+
font-weight: bold;
5859
}
5960

6061
/* 顶栏中的作者图标样式 */
@@ -217,7 +218,8 @@ body::before {
217218
align-items: flex-end; /* 靠右对齐 */
218219
margin-bottom: 10px; /* 底部外边距 */
219220
margin-right: 2%; /* 右边距 */
220-
text-align: right; /* 文本右对齐 */ }
221+
text-align: right; /* 文本右对齐 */
222+
}
221223
/* 玩家布局样式 */
222224
.team-column#right-team .player {
223225
display: flex; /* 使用弹性布局 */
@@ -267,9 +269,9 @@ body::before {
267269
}
268270
/* 结果展示 p 样式 */
269271
.footer p {
270-
font-size: 26px; /* 字号 */
271-
font-family: "华文中宋", serif; /* 字体设置 */
272-
margin: 5px 0; /* 顶部和底部外边距 */
272+
font-size: 26px;
273+
font-family: 'ShanHaiJiGuMingKe', '思源黑体', '微软雅黑', '幼圆', '华文中宋', Arial, sans-serif;
274+
margin: 5px 0;
273275
}
274276
/* 预测按钮样式 */
275277
#predict-btn {
@@ -313,15 +315,30 @@ body::before {
313315
}
314316
/* A 队胜率 p 样式 */
315317
.A-win-rate p {
316-
font-size: 25px !important; /* 字号 */
317-
font-family: "幼圆", serif !important; /* 字体设置 */
318-
font-weight: bolder; /* 加粗 */
318+
font-size: 25px !important;
319+
font-family: 'ShanHaiJiGuMingKe', '思源黑体', '微软雅黑', '幼圆', '华文中宋', Arial, sans-serif !important;
320+
font-weight: bolder;
319321
}
320322
/* B 队胜率 p 样式 */
321323
.B-win-rate p {
322-
font-size: 25px !important; /* 字号 */
323-
font-family: "幼圆", serif !important; /* 字体设置 */
324-
font-weight: bolder; /* 加粗 */
324+
font-size: 25px !important;
325+
font-family: 'ShanHaiJiGuMingKe', '思源黑体', '微软雅黑', '幼圆', '华文中宋', Arial, sans-serif !important;
326+
font-weight: bolder;
327+
}
328+
.team-selection {
329+
margin-bottom: 20px;
330+
}
331+
.team {
332+
display: flex;
333+
justify-content: space-between;
334+
align-items: flex-start;
335+
}
336+
#win-rate-chart {
337+
margin: 0 30px;
338+
flex: 1;
339+
}
340+
.footer {
341+
margin-top: 30px;
325342
}
326343
/* Echarts 图表样式 */
327344
#win-rate-chart {
@@ -332,4 +349,97 @@ body::before {
332349
background-color: rgba(255, 255, 255, 0.5); /* 半透明背景 */
333350
border-radius: 10px; /* 圆角 */
334351
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); /* 阴影效果 */
335-
}
352+
}
353+
354+
.site-stats-header {
355+
flex: 1;
356+
display: flex;
357+
align-items: center;
358+
justify-content: center;
359+
font-size: 20px;
360+
color: #666;
361+
background: none;
362+
border-radius: 8px;
363+
padding: 5px 10px;
364+
box-shadow: none;
365+
height: 30px;
366+
margin: 0 20px;
367+
font-family: 'ShanHaiJiGuMingKe', '思源黑体', '微软雅黑', '幼圆', '华文中宋', Arial, sans-serif;
368+
}
369+
370+
.site-stats-header span {
371+
margin-right: 20px;
372+
display: flex;
373+
align-items: center;
374+
}
375+
376+
.site-stats-header img {
377+
vertical-align: middle;
378+
margin-right: 5px;
379+
}
380+
381+
/* 移除底部 site-stats 的样式 */
382+
.site-stats {
383+
display: none;
384+
}
385+
386+
.topbar-right {
387+
flex: 1;
388+
display: flex;
389+
align-items: center;
390+
justify-content: flex-end;
391+
}
392+
#current-time {
393+
margin-right: 0;
394+
}
395+
396+
@font-face {
397+
font-family: 'ShanHaiJiGuMingKe';
398+
src: url('../font/ShanHaiJiGuMingKe(YaZhiBan).woff2') format('woff2');
399+
font-display: swap;
400+
}
401+
402+
html, body {
403+
font-family: 'ShanHaiJiGuMingKe', '思源黑体', '微软雅黑', '幼圆', '华文中宋', Arial, sans-serif;
404+
}
405+
406+
.stats-badge {
407+
display: inline-flex;
408+
align-items: center;
409+
background: none;
410+
color: #444;
411+
border-radius: 8px;
412+
padding: 2px 12px 2px 8px;
413+
font-size: 20px;
414+
font-family: 'ShanHaiJiGuMingKe', '思源黑体', '微软雅黑', '幼圆', '华文中宋', Arial, sans-serif;
415+
margin-right: 18px;
416+
font-weight: 500;
417+
box-shadow: none;
418+
}
419+
.stats-badge:last-child {
420+
margin-right: 0;
421+
}
422+
.stats-badge svg {
423+
display: inline-block;
424+
width: 28px !important;
425+
height: 28px !important;
426+
}
427+
.stats-number {
428+
font-weight: bold;
429+
margin-left: 8px;
430+
background: linear-gradient(
431+
270deg,
432+
#ff5858, #ffb347, #f9f871, #7be495, #4fc3f7, #a084ee, #ff5858, #ffb347, #f9f871, #7be495, #4fc3f7, #a084ee, #ff5858
433+
);
434+
background-size: 1200% 1200%;
435+
-webkit-background-clip: text;
436+
-webkit-text-fill-color: transparent;
437+
background-clip: text;
438+
color: transparent;
439+
animation: rainbow-flash 12s linear infinite;
440+
text-shadow: 0 1px 2px rgba(0,0,0,0.12);
441+
}
442+
@keyframes rainbow-flash {
443+
0% { background-position: 0% 50%; }
444+
100% { background-position: 100% 50%; }
445+
}
1.29 MB
Binary file not shown.

static/js/main.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@ function init() {
88
initializeChart();
99
loadDataAndInitialize();
1010
initializePredictionButton();
11+
updateSiteStats();
12+
}
13+
14+
function updateSiteStats() {
15+
$.get('/site_stats', function(data) {
16+
$('#site-visit-count').text(data.visit_count);
17+
$('#site-visitor-count').text(data.visitor_count);
18+
});
1119
}
1220

1321
function updateTimeEverySecond() {

templates/index.html

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,19 @@
1414
<body>
1515
<div class="topbar">
1616
<div class="title"><img id="author" src="../static/images/avatar.png" alt="Author">英雄联盟胜率预测</div>
17-
<div id="current-time"></div>
17+
<div class="site-stats-header">
18+
<span class="stats-badge">
19+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" style="margin-right: 5px; vertical-align: middle;"><rect x="3" y="10" width="3" height="8" fill="#4FC3F7"/><rect x="9" y="6" width="3" height="12" fill="#1976D2"/><rect x="15" y="3" width="3" height="15" fill="#81C784"/></svg>
20+
<span style="margin-right: 3px;">本站访问次数</span><span id="site-visit-count" class="stats-number">--</span>
21+
</span>
22+
<span class="stats-badge">
23+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" style="margin-right: 5px; vertical-align: middle;"><circle cx="7" cy="12" r="3" fill="#FFD600"/><circle cx="17" cy="12" r="3" fill="#4CAF50"/><ellipse cx="12" cy="17" rx="7" ry="3" fill="#90CAF9"/></svg>
24+
<span style="margin-right: 3px;">本站访问人数</span><span id="site-visitor-count" class="stats-number">--</span>
25+
</span>
26+
</div>
27+
<div class="topbar-right">
28+
<div id="current-time"></div>
29+
</div>
1830
</div>
1931
<div class="container">
2032
<div class="selection-container">
@@ -83,10 +95,10 @@ <h3>🅱队阵容选择</h3>
8395
</div>
8496
<div class="footer">
8597
<p>获胜方:
86-
<span id="winning-team-container">
87-
<span id="winning-team-name"></span>
88-
<img id="winning-team-logo" src="" alt="队伍Logo" style="display: none; width: 30px; height: 30px; vertical-align: middle; margin-left: 10px;">
89-
</span>
98+
<span id="winning-team-container">
99+
<span id="winning-team-name"></span>
100+
<img id="winning-team-logo" src="" alt="队伍Logo" style="display: none; width: 30px; height: 30px; vertical-align: middle; margin-left: 10px;">
101+
</span>
90102
</p>
91103
<div id="result" class="result">
92104
<div class="win-rates">
@@ -99,7 +111,6 @@ <h3>🅱队阵容选择</h3>
99111
</div>
100112
<button id="predict-btn">预测结果</button>
101113
</div>
102-
103114
</div>
104115
</div>
105116
<script src="{{ url_for('static', filename='js/main.js') }}"></script>

0 commit comments

Comments
 (0)