mirror of
https://github.com/A-Minos/nonebot-plugin-tetris-stats.git
synced 2026-03-05 05:36:54 +08:00
* 🚧 查数据图初版测试 Co-authored-by: C1ystal <m17687496044@163.com> Co-authored-by: C29H25N3O5 <michaelgu495@gmail.com> * 🙈 添加一些 ignore 文件 * 🎨 格式化代码 * 🐛 修复格式化导致的样式爆炸 * 💄 优化曲线图观感 * 💄 将雷达图的指示器名称旋转显示 * 💄 查数据图第二版 Co-authored-by: C29H25N3O5 <michaelgu495@gmail.com> * ✏️ 修复 typo * 💄 把用户头像文件的引用放到 html 里 * 💄 账户绑定图第一版 Co-authored-by: C1ystal <m17687496044@163.com> Co-authored-by: C29H25N3O5 <michaelgu495@gmail.com> * 🚧 模板化测试 * ➕ 添加依赖 fastapi * ✨ 通过 FastAPI 提供静态文件 * ➕ 添加依赖 jinja2 * 💄 更新数据图模板 (#291) * feat(template): show actual value * feat(template): add user avatar * feat(template): fix radar * feat(style): fix name container width fixed caused display misplacement * feat(style): fix vs value wrap display * feat(template): make check data length in template * feat(template): update radar data * feat(jinja): update data * fix(template): fix typo * feat(style): prevent sign too long * feat(template): turn off echarts animation * chore(deps): add identicon.js * fix(template): fix typo * 🙈 更新.gitignore * 🏗️ 大部分重构为 flex 布局 --------- Co-authored-by: shoucandanghehe <wallfjjd@gmail.com> Co-authored-by: 呵呵です <51957264+shoucandanghehe@users.noreply.github.com> * ➕ 添加依赖 nonebot_plugin_userinfo * ✨ 通过 FastAPI 托管渲染后的模板 * ✨ 新增头像 api 使用 playwright 生成 * ✨ 修正模板资源文件引用路径 被托管后的正确路径 * 💄 将绑定图模板化 * 💄 重命名变量 * 🚚 重命名资源 * ✨ 使用 jinja2 渲染模板 * ✨ 使用 playwright 渲染网页 * 🩹 渲染模板时对 IO 进行一些额外处理 * ➕ 添加依赖 pillow * 🚚 修改托管页面的路由路径 * 💬 优化绑定图文案 * ✨ 新增获取自身网络位置的方法 * 🍱 更新 unknown.svg * ✨ 新增获取用户头像的方法 * ✨ 绑定消息使用图片回复 * ✨ 为 identicon api 添加缓存 * 🔥 删除旧文件 * 🚚 重命名模板 * 📄 添加字体 License * 🙈 更新.gitignore --------- Co-authored-by: C1ystal <m17687496044@163.com> Co-authored-by: C29H25N3O5 <michaelgu495@gmail.com> Co-authored-by: 渣渣120 <WOSHIZHAZHA120@qq.com>
353 lines
14 KiB
HTML
353 lines
14 KiB
HTML
<!doctype html>
|
||
|
||
<html>
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<title></title>
|
||
<link href="../../static/css/data.css" rel="stylesheet" />
|
||
</head>
|
||
|
||
<body>
|
||
<div id="main-content">
|
||
<span class="big-title">Account&Rankings</span>
|
||
<div id="account-box">
|
||
<div id="info-box">
|
||
<div class="flex-gap"></div>
|
||
<div class="box-shadow box-rounded-corners" id="user-info-box">
|
||
<img id="user-avatar" src="{{user_avatar}}" />
|
||
<div id="user-name">{{user_name}}</div>
|
||
<div id="user-sign">“{{user_sign}}”</div>
|
||
</div>
|
||
<div class="flex-gap"></div>
|
||
<div class="box-shadow box-rounded-corners" id="game-info-box">
|
||
<div id="game-type-box">
|
||
<img id="game-logo" src="../../static/static/logo/{{game_type}}.svg" />
|
||
<span id="game-name">{{game_type}}</span>
|
||
</div>
|
||
<div id="game-info-dividing-line"></div>
|
||
<div id="ranking-info-box">
|
||
<span id="ranking-title">Ranking</span>
|
||
<span id="ranking">{{ranking}}</span>
|
||
<span id="rd">±{{rd}}</span>
|
||
</div>
|
||
</div>
|
||
<div class="flex-gap"></div>
|
||
</div>
|
||
<div class="chart-shadow box-rounded-corners" id="TR-curve-chart">
|
||
<span id="TR-title">Tetra Rating (TR)</span>
|
||
<img id="rank-icon" src="../../static/static/rank/{{rank}}.svg" />
|
||
<span id="TR" style="display: flex; align-items: flex-end"
|
||
>{{TR}}
|
||
<p style="font-size: 30px; font-weight: 400; line-height: 47px">(#{{global_rank}})</p>
|
||
</span>
|
||
</div>
|
||
</div>
|
||
<span class="big-title">Multiplayer Stats</span>
|
||
<div id="multiplayer-box">
|
||
<div class="flex-gap"></div>
|
||
<div id="multiplayer-data-box">
|
||
<div class="multiplayer-data small-data-box box-shadow box-rounded-corners" id="lpm-box">
|
||
<span class="big-data-value" id="lpm-value">{{lpm}}</span>
|
||
<span class="small-data-value" id="pps-value">{{pps}} pps</span>
|
||
</div>
|
||
<div class="multiplayer-data small-data-box box-shadow box-rounded-corners" id="apm-box">
|
||
<span class="big-data-value" id="apm-value">{{apm}}</span>
|
||
<span class="small-data-value" id="apl-value">x{{apl}}</span>
|
||
</div>
|
||
<div class="multiplayer-data small-data-box box-shadow box-rounded-corners" id="adpm-box">
|
||
<span class="big-data-value" id="adpm-value">{{adpm}}</span>
|
||
<span class="small-data-value" id="adpl-value">x{{adpl}}</span>
|
||
<span class="small-data-value" id="vs-value">{{vs}} vs</span>
|
||
</div>
|
||
</div>
|
||
<div class="flex-gap"></div>
|
||
<div class="chart-shadow box-rounded-corners" id="radar-chart"></div>
|
||
<div class="flex-gap"></div>
|
||
</div>
|
||
<span class="big-title">Singleplayer Stats</span>
|
||
<div id="singleplayer-box">
|
||
<div class="flex-gap"></div>
|
||
<div class="small-data-box box-shadow box-rounded-corners" id="sprint-box">
|
||
<span class="big-data-value" id="sprint-value">{{sprint}}</span>
|
||
</div>
|
||
<div class="flex-gap"></div>
|
||
<div class="small-data-box box-shadow box-rounded-corners" id="blitz-box">
|
||
<span class="big-data-value" id="blitz-value">{{blitz}}</span>
|
||
</div>
|
||
<div class="flex-gap"></div>
|
||
</div>
|
||
<div id="footer">Powered by<br />Nonebot2 x nonebot-plugin-tetris-stats</div>
|
||
</div>
|
||
</body>
|
||
|
||
<script src="../../static/js/echarts.js"></script>
|
||
|
||
<script>
|
||
var data = {{data}}
|
||
|
||
// 曲线图
|
||
var lineChartDom = document.getElementById('TR-curve-chart');
|
||
var lineChart = echarts.init(lineChartDom, null, { renderer: 'svg' });
|
||
var option;
|
||
|
||
/** @type EChartsOption */
|
||
option = {
|
||
animation: false,
|
||
grid: {
|
||
left: '-5%',
|
||
bottom: '17%',
|
||
width: '90%',
|
||
height: '70%',
|
||
},
|
||
|
||
xAxis: {
|
||
type: 'time',
|
||
minInterval: 3600 * 48 * 1000,
|
||
axisTick: {
|
||
show: false,
|
||
},
|
||
axisLine: {
|
||
show: false,
|
||
},
|
||
axisLabel: {
|
||
formatter: function (value, index) {
|
||
var date = new Date(value);
|
||
var lst;
|
||
var ret;
|
||
function format_date() {
|
||
return new Intl.DateTimeFormat('en-US', {
|
||
month: '2-digit',
|
||
day: '2-digit',
|
||
})
|
||
.format(date)
|
||
.split('/');
|
||
}
|
||
switch (index) {
|
||
case 0:
|
||
case 6:
|
||
ret = '';
|
||
break;
|
||
default:
|
||
lst = format_date();
|
||
if (index === 5) {
|
||
ret = '{last_month|' + lst[0] + '}\n{last_day|' + lst[1] + '}';
|
||
break;
|
||
}
|
||
ret = '{month|' + lst[0] + '}\n{day|' + lst[1] + '}';
|
||
}
|
||
return ret;
|
||
},
|
||
rich: {
|
||
month: {
|
||
fontFamily: 'CabinetGrotesk-Variable',
|
||
fontSize: 13,
|
||
fontWeight: '400',
|
||
color: 'rgba(255, 255, 255, 0.6)',
|
||
},
|
||
day: {
|
||
fontFamily: 'CabinetGrotesk-Variable',
|
||
fontSize: 20,
|
||
fontWeight: '800',
|
||
color: 'rgba(255, 255, 255, 0.6)',
|
||
},
|
||
last_month: {
|
||
fontFamily: 'CabinetGrotesk-Variable',
|
||
fontSize: 13,
|
||
fontWeight: '400',
|
||
color: '#373533',
|
||
backgroundColor: '#FAFAFA',
|
||
borderRadius: 6,
|
||
padding: [-10, 0, 10, 0],
|
||
width: 36,
|
||
height: 37,
|
||
lineHeight: 32,
|
||
},
|
||
last_day: {
|
||
fontFamily: 'CabinetGrotesk-Variable',
|
||
fontSize: 20,
|
||
fontWeight: '800',
|
||
color: '#373533',
|
||
padding: [-18, 0, 0, 0],
|
||
lineHeight: 0,
|
||
},
|
||
},
|
||
},
|
||
zlevel: 1,
|
||
},
|
||
|
||
yAxis: {
|
||
type: 'value',
|
||
interval: {{split_value}},
|
||
position: 'right',
|
||
splitLine: {
|
||
show: false,
|
||
},
|
||
axisLine: {
|
||
show: false,
|
||
},
|
||
axisLabel: {
|
||
align: 'right',
|
||
formatter: function (value, index) {
|
||
return '{value|' + value.toLocaleString() + '}';
|
||
},
|
||
rich: {
|
||
value: {
|
||
fontFamily: 'CabinetGrotesk-Variable',
|
||
fontSize: 15,
|
||
fontWeight: '500',
|
||
color: 'rgba(255, 255, 255, 0.6)',
|
||
},
|
||
},
|
||
},
|
||
offset: 70,
|
||
max: {{value_max+offset}},
|
||
min: {{value_min-offset}},
|
||
},
|
||
series: [
|
||
{
|
||
// 10天的数据,最后一天只要第一条 (时间戳最少要多1ms)
|
||
data: data,
|
||
type: 'line',
|
||
smooth: true,
|
||
symbol: function (value, params) {
|
||
if (params.dataIndex === data.length - 1) {
|
||
return 'image://../../static/static/data/point.svg';
|
||
}
|
||
return 'none';
|
||
},
|
||
symbolSize: 75,
|
||
symbolOffset: [0.79, 0],
|
||
lineStyle: {
|
||
color: '#FAFAFA99',
|
||
},
|
||
areaStyle: {
|
||
color: {
|
||
type: 'linear',
|
||
x: 0,
|
||
y: 0,
|
||
x2: 0,
|
||
y2: 1,
|
||
colorStops: [
|
||
{
|
||
offset: 0,
|
||
color: 'rgba(250, 250, 250, 0.3)',
|
||
},
|
||
{
|
||
offset: 1,
|
||
color: 'rgba(250, 250, 250, 0)',
|
||
},
|
||
],
|
||
global: false,
|
||
},
|
||
},
|
||
markLine: {
|
||
data: [
|
||
{
|
||
xAxis: 'max',
|
||
y: 300,
|
||
},
|
||
],
|
||
label: {
|
||
show: false,
|
||
},
|
||
lineStyle: {
|
||
color: '#FAFAFA',
|
||
width: 3,
|
||
type: 'dashed',
|
||
cap: 'round',
|
||
},
|
||
symbol: 'none',
|
||
animation: false,
|
||
},
|
||
z: 5,
|
||
},
|
||
],
|
||
};
|
||
option && lineChart.setOption(option);
|
||
</script>
|
||
|
||
<script>
|
||
// 雷达图
|
||
var radarChartDom = document.getElementById('radar-chart');
|
||
var radarChart = echarts.init(radarChartDom, null, { renderer: 'svg' });
|
||
var option;
|
||
|
||
option = {
|
||
animation: false,
|
||
radar: [
|
||
{
|
||
indicator: [
|
||
{ name: 'PPS' },
|
||
{ name: 'APP', nameRotate: 60 },
|
||
{ name: 'DSPP', nameRotate: -60 },
|
||
{ name: 'OR' },
|
||
{ name: 'CI', nameRotate: 60 },
|
||
{ name: 'GE', nameRotate: -60 },
|
||
],
|
||
center: ['50%', '50%'],
|
||
radius: '65%',
|
||
startAngle: 90,
|
||
splitNumber: 4,
|
||
shape: 'circle',
|
||
silent: true,
|
||
axisName: {
|
||
color: '#FAFAFA',
|
||
fontFamily: 'CabinetGrotesk-Variable',
|
||
fontSize: 15,
|
||
fontWeight: '800',
|
||
},
|
||
splitArea: {
|
||
show: false,
|
||
},
|
||
axisLine: {
|
||
lineStyle: {
|
||
color: 'rgba(250, 250, 250, 0.3)',
|
||
},
|
||
},
|
||
axisLabel: {
|
||
show: true,
|
||
rotate: 0,
|
||
margin: -1,
|
||
fontFamily: 'CabinetGrotesk-Variable',
|
||
fontSize: 7,
|
||
fontWeight: '800',
|
||
color: '#FFFFFF',
|
||
},
|
||
splitLine: {
|
||
lineStyle: {
|
||
color: 'rgba(250, 250, 250, 0.3)',
|
||
},
|
||
},
|
||
},
|
||
],
|
||
series: [
|
||
{
|
||
type: 'radar',
|
||
symbol: 'none',
|
||
label: {
|
||
show: true,
|
||
},
|
||
emphasis: {
|
||
disabled: true,
|
||
},
|
||
lineStyle: {
|
||
color: '#FAFAFA',
|
||
width: 2.5,
|
||
shadowBlur: 20,
|
||
shadowColor: 'rgba(250, 250, 250, 1)',
|
||
},
|
||
areaStyle: {
|
||
color: 'rgba(250, 250, 250, 0.45)',
|
||
},
|
||
data: [
|
||
{
|
||
value: [{{pps}}, {{app}}, {{dspp}}, {{OR}}, {{ci}}, {{ge}}],
|
||
},
|
||
],
|
||
},
|
||
],
|
||
};
|
||
option && radarChart.setOption(option);
|
||
</script>
|
||
</html>
|