koishi-plugin-ets2-tools-tmp 3.0.6 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -57,15 +57,25 @@ module.exports = {
57
57
 
58
58
  if (response.code === 0 && response.data) {
59
59
  const enableAutoClock = response.data.enableAutoClock;
60
- this.logger.debug(`活动 "${activity.themeName}" V1自动打卡状态: ${enableAutoClock}`);
61
- return enableAutoClock === 1;
60
+ const serverId = response.data.serverId;
61
+ this.logger.debug(`活动 "${activity.themeName}" V1自动打卡状态: ${enableAutoClock}, serverId: ${serverId}`);
62
+
63
+ if (enableAutoClock === 1) {
64
+ if (serverId != null) {
65
+ return { status: 'set', message: '今日活动自动打卡已设置' };
66
+ } else {
67
+ return { status: 'no_server', message: '今日活动打卡服务器未设置' };
68
+ }
69
+ } else {
70
+ return { status: 'not_set', message: '今日活动打卡未设置' };
71
+ }
62
72
  } else {
63
73
  this.logger.error(`V1活动详情API返回错误: ${response.msg || '未知错误'} (代码: ${response.code || '无'})`);
64
- return false;
74
+ return { status: 'not_set', message: '今日活动打卡未设置' };
65
75
  }
66
76
  } catch (error) {
67
77
  this.logger.error(`检查V1活动 "${activity.themeName}" 自动打卡状态失败:`, error.message);
68
- return false;
78
+ return { status: 'not_set', message: '今日活动打卡未设置' };
69
79
  }
70
80
  }
71
81
  };
@@ -71,15 +71,25 @@ module.exports = {
71
71
 
72
72
  if (response.code === 200 && response.data) {
73
73
  const autoCheckInEnable = response.data.autoCheckInEnable;
74
- this.logger.debug(`活动 "${activity.themeName}" V2自动打卡状态: ${autoCheckInEnable}`);
75
- return autoCheckInEnable === 1;
74
+ const serverId = response.data.serverId;
75
+ this.logger.debug(`活动 "${activity.themeName}" V2自动打卡状态: ${autoCheckInEnable}, serverId: ${serverId}`);
76
+
77
+ if (autoCheckInEnable === 1) {
78
+ if (serverId != null) {
79
+ return { status: 'set', message: '今日活动自动打卡已设置' };
80
+ } else {
81
+ return { status: 'no_server', message: '今日活动打卡服务器未设置' };
82
+ }
83
+ } else {
84
+ return { status: 'not_set', message: '今日活动打卡未设置' };
85
+ }
76
86
  } else {
77
87
  this.logger.error(`V2活动详情API返回错误: ${response.msg || '未知错误'} (代码: ${response.code || '无'})`);
78
- return false;
88
+ return { status: 'not_set', message: '今日活动打卡未设置' };
79
89
  }
80
90
  } catch (error) {
81
91
  this.logger.error(`检查V2活动 "${activity.themeName}" 自动打卡状态失败:`, error.message);
82
- return false;
92
+ return { status: 'not_set', message: '今日活动打卡未设置' };
83
93
  }
84
94
  }
85
95
  };
@@ -620,13 +620,18 @@ class ActivityService {
620
620
 
621
621
  for (const activity of this.todayActivities) {
622
622
  try {
623
- const autoClockEnabled = await this.checkAutoClock(activity);
623
+ const autoClockResult = await this.checkAutoClock(activity);
624
624
 
625
- const message = autoClockEnabled
626
- ? `今日活动自动打卡已设置`
627
- : `请注意,今日活动打卡未设置。\n 活动名称: ${activity.themeName || '未知活动'}`;
625
+ let message;
626
+ if (autoClockResult.status === 'set') {
627
+ message = `今日活动自动打卡已设置`;
628
+ } else if (autoClockResult.status === 'no_server') {
629
+ message = `请注意,今日活动打卡服务器未设置。\n 活动名称: ${activity.themeName || '未知活动'}`;
630
+ } else {
631
+ message = `请注意,今日活动打卡未设置。\n 活动名称: ${activity.themeName || '未知活动'}`;
632
+ }
628
633
 
629
- this.logger.message(`自动打卡检查: "${activity.themeName || '未知活动'}" - ${autoClockEnabled ? "已设置" : "未设置"}`);
634
+ this.logger.message(`自动打卡检查: "${activity.themeName || '未知活动'}" - ${autoClockResult.message}`);
630
635
 
631
636
  for (const groupId of this.cfg.admin.groups) {
632
637
  try {
@@ -68,7 +68,7 @@ module.exports = async (ctx, session, serverType, tmpId, date) => {
68
68
  }
69
69
 
70
70
  if (date === 'tenday') {
71
- startTime = dayjs().subtract(10, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss');
71
+ startTime = dayjs().subtract(7, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss');
72
72
  endTime = dayjs().endOf('day').format('YYYY-MM-DD HH:mm:ss');
73
73
  }
74
74
 
@@ -98,8 +98,8 @@ module.exports = async (ctx, session, serverType, tmpId, date) => {
98
98
  if (date === 'today') {
99
99
  return `今日暂无数据`;
100
100
  }
101
- if (date === 'tenday') {
102
- return `近十天暂无数据`;
101
+ if (date === 'sevenday') {
102
+ return `近七日暂无数据`;
103
103
  }
104
104
  if (date === 'lastMonth') {
105
105
  return `上月暂无数据`;
@@ -1,4 +1,6 @@
1
1
  const dayjs = require('dayjs');
2
+ const dayjsRelativeTime = require('dayjs/plugin/relativeTime');
3
+ const dayjsLocaleZhCn = require('dayjs/locale/zh-cn');
2
4
  const guildBind = require('../../database/guildBind');
3
5
  const truckyAppApi = require('../../api/truckyAppApi');
4
6
  const evmOpenApi = require('../../api/evmOpenApi');
@@ -6,6 +8,8 @@ const baiduTranslate = require('../../util/baiduTranslate');
6
8
  const { resolve } = require("path");
7
9
  const common = require("../../util/common");
8
10
  const { segment } = require("koishi");
11
+ dayjs.extend(dayjsRelativeTime);
12
+ dayjs.locale(dayjsLocaleZhCn);
9
13
  /**
10
14
  * 用户组
11
15
  */
@@ -21,6 +25,7 @@ const userGroup = {
21
25
  * 查询玩家信息
22
26
  */
23
27
  module.exports = async (ctx, cfg, session, tmpId) => {
28
+ const { vtcId } = cfg.tmpActivityService?.api || {};
24
29
  if (!ctx.puppeteer) {
25
30
  return '未启用 puppeteer 服务';
26
31
  }
@@ -50,27 +55,36 @@ module.exports = async (ctx, cfg, session, tmpId) => {
50
55
  data.tmpId = playerInfo.data.tmpId;
51
56
  data.name = playerInfo.data.name;
52
57
  data.steamId = playerInfo.data.steamId;
53
- data.registerDate = dayjs(playerInfo.data.registerTime).format('YYYY年MM月DD日');
58
+ let registerDate = dayjs(playerInfo.data.registerTime);
59
+ data.registerDate = registerDate.format('YYYY年MM月DD日');
60
+ data.registerDays = dayjs().diff(registerDate, 'day');
54
61
  data.avatarUrl = playerInfo.data.avatarUrl;
55
62
  data.groupColor = playerInfo.data.groupColor;
56
63
  data.groupName = (userGroup[playerInfo.data.groupName] || playerInfo.data.groupName);
57
64
  data.isJoinVtc = playerInfo.data.isJoinVtc;
58
65
  data.vtcName = playerInfo.data.vtcName;
59
66
  data.vtcRole = playerInfo.data.vtcRole;
67
+ data.vtcHistory = playerInfo.data.vtcHistory || [];
60
68
  data.isSponsor = playerInfo.data.isSponsor;
61
69
  data.sponsorAmount = playerInfo.data.sponsorAmount;
62
70
  data.sponsorCumulativeAmount = playerInfo.data.sponsorCumulativeAmount;
63
71
  data.sponsorHide = playerInfo.data.sponsorHide;
72
+ data.mileage = playerInfo.data.mileage;
73
+ data.todayMileage = playerInfo.data.todayMileage;
64
74
  data.isOnline = false;
75
+ data.onlineStatus = '离线';
65
76
  if (playerMapInfo && !playerMapInfo.error) {
66
77
  data.isOnline = playerMapInfo.data.online;
67
78
  if (data.isOnline) {
79
+ data.onlineStatus = '在线';
68
80
  data.onlineServerName = playerMapInfo.data.serverDetails.name;
69
81
  data.onlineCountry = await baiduTranslate(ctx, cfg, playerMapInfo.data.location.poi.country);
70
82
  data.onlineCity = await baiduTranslate(ctx, cfg, playerMapInfo.data.location.poi.realName);
71
83
  data.onlineX = playerMapInfo.data.x;
72
84
  data.onlineY = playerMapInfo.data.y;
73
85
  data.onlineMapType = playerMapInfo.data.serverDetails.id === 50 ? 'promods' : 'ets';
86
+ } else if (playerInfo.data.lastOnlineTime) {
87
+ data.lastOnlineTime = dayjs(playerInfo.data.lastOnlineTime).fromNow(false);
74
88
  }
75
89
  }
76
90
  data.isBan = playerInfo.data.isBan;
@@ -79,10 +93,86 @@ module.exports = async (ctx, cfg, session, tmpId) => {
79
93
  data.banReasonZh = playerInfo.data.banReasonZh;
80
94
  data.banCount = playerInfo.data.banCount;
81
95
  data.banHide = playerInfo.data.banHide;
96
+ // 查询VTC积分(仅主群/管理群显示)
97
+ data.rewardPoints = 0;
98
+ if (playerInfo.data.isJoinVtc && cfg.commands?.mainSettings) {
99
+ const mainGroups = cfg.tmpActivityService?.mainGroup?.groups || [];
100
+ const adminGroups = cfg.tmpActivityService?.admin?.groups || [];
101
+ const allowedGroups = [...new Set([...mainGroups, ...adminGroups])];
102
+ const currentGroupId = session.channelId || '';
103
+ const isInAllowedGroup = allowedGroups.some(g => currentGroupId.includes(g));
104
+
105
+ if (isInAllowedGroup && playerInfo.data.vtcId == vtcId) {
106
+ const { url, token, logOutput, platformVersion } = cfg.mainSettings?.settings || {};
107
+ const platform = (platformVersion || "v1").toLowerCase();
108
+ try {
109
+ if (platform === "v2") {
110
+ const baseUrl = url;
111
+ const userInfoUrl = `https://${baseUrl}/members/get?token=${token}&tmpId=${tmpId}`;
112
+ if (logOutput) {
113
+ ctx.logger.info(`[TMP_BOT] tmpQueryImg:开始查询TmpID ${tmpId} 的V2.0积分`);
114
+ ctx.logger.info(`[TMP_BOT] 请求V2.0用户信息: ${userInfoUrl}`);
115
+ }
116
+ const userInfoResponse = await ctx.http.get(userInfoUrl);
117
+ if (logOutput) {
118
+ ctx.logger.info(`[TMP_BOT] V2.0用户信息响应: ${JSON.stringify(userInfoResponse)}`);
119
+ }
120
+ if (userInfoResponse.code === 200 && userInfoResponse.data) {
121
+ data.rewardPoints = userInfoResponse.data.point || 0;
122
+ }
123
+ } else {
124
+ if (logOutput) {
125
+ ctx.logger.info(`[TMP_BOT] tmpQueryImg:开始查询TmpID ${tmpId} 的V1.0积分`);
126
+ }
127
+ const userInfoUrl = `https://${url}/api/user/info/list?token=${token}&page=0&limit=7&tmpId=${tmpId}&tmpName=&teamId=&qq=&state=0&teamRole=`;
128
+ if (logOutput) {
129
+ ctx.logger.info(`[TMP_BOT] 请求V1.0用户信息: ${userInfoUrl}`);
130
+ }
131
+ const userInfoResponse = await ctx.http.post(userInfoUrl);
132
+ if (logOutput) {
133
+ ctx.logger.info(`[TMP_BOT] V1.0用户信息响应: ${JSON.stringify(userInfoResponse)}`);
134
+ }
135
+ const userList = userInfoResponse.page?.list || [];
136
+ const userInfo = userList[0];
137
+ data.rewardPoints = userInfo.rewardPoints || 0;
138
+ }
139
+ } catch (error) {
140
+ ctx.logger.error(`积分查询过程出错: ${error}`);
141
+ }
142
+ }
143
+ }
144
+ // 查询Steam游戏时长
145
+ data.ets2GameTime = null;
146
+ data.atsGameTime = null;
147
+ if (cfg.commands?.tmpQueryGameTime) {
148
+ try {
149
+ const steamApiKey = cfg.steamApi?.key;
150
+ if (steamApiKey) {
151
+ const steamId = playerInfo.data.steamId;
152
+ const url = `https://api.114512.xyz/steam/IPlayerService/GetOwnedGames/v1?key=${steamApiKey}&steamid=${steamId}&appids_filter[0]=227300&appids_filter[1]=270880&include_played_free_games=1`;
153
+ const response = await ctx.http.get(url);
154
+ if (response.response && response.response.game_count > 0) {
155
+ for (const game of response.response.games) {
156
+ const playtimeMinutes = game.playtime_forever;
157
+ const hours = Math.floor(playtimeMinutes / 60);
158
+ const minutes = playtimeMinutes % 60;
159
+ const playtime = `${hours}小时${minutes}分钟`;
160
+ if (game.appid === 227300) {
161
+ data.ets2GameTime = playtime;
162
+ } else if (game.appid === 270880) {
163
+ data.atsGameTime = playtime;
164
+ }
165
+ }
166
+ }
167
+ }
168
+ } catch (error) {
169
+ ctx.logger.error(`查询Steam游戏时长出错: ${error}`);
170
+ }
171
+ }
82
172
  let page;
83
173
  try {
84
174
  page = await ctx.puppeteer.page();
85
- await page.setViewport({ width: 1000, height: 1000 });
175
+ await page.setViewport({ width: 520, height: 1000 });
86
176
  await page.goto(`file:///${resolve(__dirname, '../../resource/query.html')}`);
87
177
  await page.evaluate(`init(${JSON.stringify(data)})`);
88
178
  await common.sleep(100);
@@ -81,7 +81,14 @@ module.exports = async (ctx, cfg, session, tmpId) => {
81
81
  }
82
82
  message += '\n🚚车队角色: ' + playerInfo.data.vtcRole;
83
83
  if (cfg.commands?.mainSettings) {
84
- if (playerInfo.data.vtcId == vtcId) {
84
+ // 仅在主群或管理群中显示车队积分
85
+ const mainGroups = cfg.tmpActivityService?.mainGroup?.groups || [];
86
+ const adminGroups = cfg.tmpActivityService?.admin?.groups || [];
87
+ const allowedGroups = [...new Set([...mainGroups, ...adminGroups])];
88
+ const currentGroupId = session.channelId || '';
89
+ const isInAllowedGroup = allowedGroups.some(g => currentGroupId.includes(g));
90
+
91
+ if (isInAllowedGroup && playerInfo.data.vtcId == vtcId) {
85
92
  const { url, token, logOutput, platformVersion } = cfg.mainSettings?.settings || {};
86
93
  const platform = (platformVersion || "v1").toLowerCase();
87
94
  try {
package/lib/index.js CHANGED
@@ -312,15 +312,15 @@ function registerBaseCommands(ctx, cfg) {
312
312
  if (cfg.commands?.tmpFootprint) {
313
313
  ctx.command('近十日足迹 [tmpId:string]')
314
314
  .usage("查询ETS服务器今日足迹")
315
- .example("近十日足迹")
316
- .example("近十日足迹 12345")
317
- .action(async ({ session }, tmpId) => await commands.tmpFootprint(ctx, session, ServerType.ets, tmpId, 'tenday'));
315
+ .example("近七日足迹")
316
+ .example("近七日足迹 12345")
317
+ .action(async ({ session }, tmpId) => await commands.tmpFootprint(ctx, session, ServerType.ets, tmpId, 'sevenday'));
318
318
 
319
- ctx.command('近十日足迹p [tmpId:string]')
319
+ ctx.command('近七日足迹p [tmpId:string]')
320
320
  .usage("查询Promods服务器今日足迹")
321
- .example("近十日足迹p")
322
- .example("近十日足迹p 12345")
323
- .action(async ({ session }, tmpId) => await commands.tmpFootprint(ctx, session, ServerType.promods, tmpId, 'tenday'));
321
+ .example("近七日足迹p")
322
+ .example("近七日足迹p 12345")
323
+ .action(async ({ session }, tmpId) => await commands.tmpFootprint(ctx, session, ServerType.promods, tmpId, 'sevenday'));
324
324
 
325
325
  ctx.command('昨日足迹 [tmpId:string]')
326
326
  .usage("查询ETS服务器今日足迹")
@@ -1,8 +1,8 @@
1
1
  <!DOCTYPE html>
2
- <html lang="en">
2
+ <html lang="zh-CN">
3
3
  <head>
4
4
  <meta charset="UTF-8">
5
- <title>Title</title>
5
+ <title>Player Info Card</title>
6
6
  <link href="./package/leaflet/leaflet.min.css" rel="stylesheet">
7
7
  <script src="./package/leaflet/leaflet.min.js"></script>
8
8
  <style>
@@ -12,350 +12,757 @@
12
12
  font-weight: normal;
13
13
  font-style: normal;
14
14
  }
15
- body {
16
- font-family: 'segui-emj', 'sans-serif';
15
+
16
+ * {
17
+ margin: 0;
18
+ padding: 0;
19
+ box-sizing: border-box;
17
20
  }
18
21
 
19
- .border {
20
- border: 1px solid red;
22
+ body {
23
+ font-family: 'segui-emj', 'Segoe UI', -apple-system, sans-serif;
24
+ background: #080c20;
25
+ -webkit-font-smoothing: antialiased;
21
26
  }
22
27
 
23
28
  #container {
24
- padding-bottom: 16px;
29
+ width: 480px;
30
+ background: linear-gradient(160deg, #0a0e27 0%, #151338 30%, #0d1a3a 60%, #080c20 100%);
31
+ padding: 0 0 16px;
32
+ overflow: hidden;
33
+ position: relative;
25
34
  }
26
35
 
27
- .form-box {
28
- display: flex;
29
- flex-wrap: wrap;
36
+ /* Ambient glow orbs */
37
+ .ambient-orb {
38
+ position: absolute;
39
+ border-radius: 50%;
40
+ pointer-events: none;
41
+ filter: blur(60px);
42
+ z-index: 0;
30
43
  }
31
- .form-box .form-item {
32
- width: 50%;
44
+
45
+ .ambient-orb.orb1 {
46
+ top: -40px;
47
+ right: -30px;
48
+ width: 180px;
49
+ height: 180px;
50
+ background: rgba(99, 102, 241, 0.18);
33
51
  }
34
- .form-box .form-item.full {
35
- width: 100%;
52
+
53
+ .ambient-orb.orb2 {
54
+ top: 200px;
55
+ left: -50px;
56
+ width: 140px;
57
+ height: 140px;
58
+ background: rgba(168, 85, 247, 0.12);
36
59
  }
37
60
 
38
- #container {
39
- width: 400px;
40
- background: linear-gradient(135deg, #1f2f54, #0f2c2a);
41
- overflow: hidden;
61
+ .ambient-orb.orb3 {
62
+ bottom: 80px;
63
+ right: -20px;
64
+ width: 120px;
65
+ height: 120px;
66
+ background: rgba(59, 130, 246, 0.1);
42
67
  }
43
68
 
69
+ /* ===== HEADER ===== */
44
70
  .header {
45
- height: 100px;
46
- background-color: rgba(0, 0, 0, .1);
71
+ padding: 24px 24px 18px;
47
72
  display: flex;
48
73
  align-items: center;
49
- padding: 0 26px;
50
- box-shadow: 0 0 16px rgba(0, 0, 0, .4);
74
+ gap: 16px;
75
+ position: relative;
76
+ z-index: 1;
77
+ background: linear-gradient(180deg, rgba(139, 92, 246, 0.08) 0%, transparent 100%);
78
+ border-bottom: 1px solid rgba(255, 255, 255, 0.04);
51
79
  }
52
- .header .avatar {
53
- width: 64px;
54
- height: 64px;
80
+
81
+ .avatar-wrapper {
82
+ position: relative;
83
+ flex-shrink: 0;
55
84
  }
56
- .header .info {
85
+
86
+ .avatar {
87
+ width: 72px;
88
+ height: 72px;
89
+ border-radius: 50%;
90
+ border: 2.5px solid rgba(139, 92, 246, 0.5);
91
+ box-shadow:
92
+ 0 0 20px rgba(139, 92, 246, 0.35),
93
+ 0 0 40px rgba(139, 92, 246, 0.15),
94
+ inset 0 0 12px rgba(139, 92, 246, 0.1);
95
+ object-fit: cover;
96
+ }
97
+
98
+ .online-dot {
99
+ position: absolute;
100
+ bottom: 2px;
101
+ right: 2px;
102
+ width: 14px;
103
+ height: 14px;
104
+ border-radius: 50%;
105
+ border: 2.5px solid #0a0e27;
106
+ z-index: 2;
107
+ }
108
+
109
+ .online-dot.online {
110
+ background: #22c55e;
111
+ box-shadow: 0 0 8px rgba(34, 197, 94, 0.6);
112
+ }
113
+
114
+ .online-dot.offline {
115
+ background: #6b7280;
116
+ }
117
+
118
+ .header-info {
57
119
  flex: 1;
58
- padding-left: 16px;
120
+ min-width: 0;
121
+ }
122
+
123
+ .player-name {
124
+ font-size: 20px;
125
+ font-weight: 700;
126
+ color: #f0f0ff;
127
+ white-space: nowrap;
128
+ overflow: hidden;
129
+ text-overflow: ellipsis;
130
+ text-shadow: 0 0 20px rgba(139, 92, 246, 0.3);
59
131
  }
60
- .header .info .name {
61
- color: #b0c7ff;
62
- font-size: 18px;
132
+
133
+ .player-meta {
134
+ display: flex;
135
+ align-items: center;
136
+ gap: 8px;
137
+ margin-top: 6px;
138
+ flex-wrap: wrap;
139
+ }
140
+
141
+ .tmp-id {
142
+ font-size: 13px;
143
+ color: #94a3b8;
144
+ font-weight: 500;
145
+ }
146
+
147
+ .group-badge {
148
+ font-size: 11px;
63
149
  font-weight: 600;
150
+ padding: 2px 8px;
151
+ border-radius: 10px;
152
+ color: #fff;
153
+ text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
154
+ letter-spacing: 0.3px;
64
155
  }
65
- .header .info .tmp-id {
66
- color: #aaaaaa;
67
- font-size: 14px;
68
- margin-top: 4px;
156
+
157
+ .status-badge {
158
+ font-size: 11px;
159
+ font-weight: 600;
160
+ padding: 2px 8px;
161
+ border-radius: 10px;
162
+ letter-spacing: 0.3px;
163
+ }
164
+
165
+ .status-badge.online {
166
+ background: rgba(34, 197, 94, 0.2);
167
+ color: #4ade80;
168
+ border: 1px solid rgba(34, 197, 94, 0.3);
169
+ }
170
+
171
+ .status-badge.offline {
172
+ background: rgba(107, 114, 128, 0.2);
173
+ color: #9ca3af;
174
+ border: 1px solid rgba(107, 114, 128, 0.3);
69
175
  }
70
- .header .info .group {
71
- color: #ffffff;
176
+
177
+ /* ===== GLASS SECTIONS ===== */
178
+ .section {
179
+ margin: 10px 16px;
180
+ background: linear-gradient(135deg, rgba(255, 255, 255, 0.06) 0%, rgba(255, 255, 255, 0.02) 100%);
181
+ border: 1px solid rgba(255, 255, 255, 0.08);
182
+ border-radius: 16px;
183
+ padding: 14px 16px;
184
+ backdrop-filter: blur(20px) saturate(1.2);
185
+ -webkit-backdrop-filter: blur(20px) saturate(1.2);
186
+ position: relative;
187
+ z-index: 1;
188
+ box-shadow:
189
+ 0 4px 16px rgba(0, 0, 0, 0.15),
190
+ 0 1px 2px rgba(255, 255, 255, 0.05) inset;
191
+ overflow: hidden;
192
+ }
193
+
194
+ /* Glass highlight on top */
195
+ .section::before {
196
+ content: '';
197
+ position: absolute;
198
+ top: 0;
199
+ left: 0;
200
+ right: 0;
201
+ height: 1px;
202
+ background: linear-gradient(90deg, transparent 5%, rgba(255, 255, 255, 0.12) 50%, transparent 95%);
203
+ z-index: 2;
204
+ }
205
+
206
+ .section-title {
72
207
  font-size: 12px;
73
- padding: 2px 4px;
74
- margin-left: 6px;
75
- border-radius: 4px;
76
- text-shadow: 0 4px 4px rgba(0, 0, 0, 0.8),
77
- 0 -4px 4px rgba(0, 0, 0, 0.8);
208
+ font-weight: 700;
209
+ color: #c4b5fd;
210
+ text-transform: uppercase;
211
+ letter-spacing: 1.2px;
212
+ margin-bottom: 10px;
213
+ display: flex;
214
+ align-items: center;
215
+ gap: 6px;
216
+ position: relative;
217
+ z-index: 1;
78
218
  }
79
219
 
80
- .details-container {
81
- padding: 16px 20px 0 20px;
220
+ .section-title::after {
221
+ content: '';
222
+ flex: 1;
223
+ height: 1px;
224
+ background: linear-gradient(90deg, rgba(139, 92, 246, 0.4), rgba(139, 92, 246, 0.05), transparent);
225
+ margin-left: 8px;
82
226
  }
83
- .details-container .form-item {
84
- background-color: rgba(0, 0, 0, 0.2);
85
- box-sizing: border-box;
227
+
228
+ /* ===== INFO ROWS ===== */
229
+ .info-row {
230
+ display: flex;
231
+ align-items: center;
232
+ justify-content: space-between;
233
+ padding: 5px 0;
234
+ position: relative;
235
+ z-index: 1;
86
236
  }
87
- .details-container .form-item:nth-child(1) {
88
- margin-top: 0;
237
+
238
+ .info-row + .info-row {
239
+ border-top: 1px solid rgba(255, 255, 255, 0.04);
89
240
  }
90
- .details-container .form-item .label {
91
- color: #dddddd;
92
- background-color: rgba(0, 0, 0, 0.2);
93
- padding: 4px 8px;
241
+
242
+ .info-label {
243
+ font-size: 13px;
244
+ color: #94a3b8;
245
+ display: flex;
246
+ align-items: center;
247
+ gap: 6px;
248
+ flex-shrink: 0;
94
249
  }
95
- .details-container .form-item .value {
96
- color: #ffffff;
97
- padding: 4px 8px;
250
+
251
+ .info-value {
252
+ font-size: 13px;
253
+ color: #e2e8f0;
254
+ text-align: right;
255
+ word-break: break-all;
256
+ max-width: 60%;
98
257
  }
99
258
 
100
- .ban-container {
101
- margin: 16px 20px 0 20px;
102
- background-color: rgba(227, 92, 92, 0.3);
259
+ .info-value.gold {
260
+ color: #fbbf24;
261
+ font-weight: 600;
262
+ text-shadow: 0 0 8px rgba(251, 191, 36, 0.3);
103
263
  }
104
- .ban-header {
105
- width: 100%;
106
- height: 30px;
107
- box-sizing: border-box;
108
- padding: 0 12px;
109
- color: #ffffff;
110
- font-size: 14px;
111
- line-height: 30px;
112
- background-color: rgba(227, 92, 92, 0.6);
264
+
265
+ .info-value.green {
266
+ color: #4ade80;
113
267
  }
114
- .ban-body {
115
- padding: 12px 16px;
268
+
269
+ .info-value.red {
270
+ color: #f87171;
116
271
  }
117
- .ban-body .ban-hidden {
118
- color: #cccccc;
119
- font-size: 14px;
120
- font-style: italic;
121
- text-align: center;
272
+
273
+ .info-value.dim {
274
+ color: #6b7280;
275
+ font-size: 12px;
122
276
  }
123
- .ban-body .form-item {
277
+
278
+ /* ===== VTC HISTORY ===== */
279
+ .vtc-history-item {
280
+ background: linear-gradient(135deg, rgba(255, 255, 255, 0.04) 0%, rgba(255, 255, 255, 0.01) 100%);
281
+ border: 1px solid rgba(255, 255, 255, 0.06);
282
+ border-radius: 10px;
283
+ padding: 10px 12px;
124
284
  margin-top: 8px;
285
+ backdrop-filter: blur(10px);
286
+ -webkit-backdrop-filter: blur(10px);
287
+ position: relative;
288
+ z-index: 1;
125
289
  }
126
- .ban-body .form-item:nth-child(1),
127
- .ban-body .form-item:nth-child(2) {
128
- margin-top: 0;
290
+
291
+ .vtc-history-item + .vtc-history-item {
292
+ margin-top: 6px;
129
293
  }
130
- .ban-body .form-item .label {
131
- color: #f3f3f3;
132
- font-size: 12px;
294
+
295
+ .vtc-history-name {
296
+ font-size: 13px;
297
+ color: #fbbf24;
298
+ font-weight: 600;
299
+ margin-bottom: 4px;
133
300
  }
134
- .ban-body .form-item .value {
135
- color: #eeeeee;
136
- font-size: 14px;
137
- word-wrap: break-word;
138
- word-break: break-all;
301
+
302
+ .vtc-history-dates {
303
+ font-size: 11px;
304
+ color: #64748b;
305
+ }
306
+
307
+ /* ===== BAN SECTION ===== */
308
+ .section.ban-section {
309
+ background: linear-gradient(135deg, rgba(239, 68, 68, 0.1) 0%, rgba(239, 68, 68, 0.03) 100%);
310
+ border-color: rgba(239, 68, 68, 0.15);
311
+ }
312
+
313
+ .section.ban-section .section-title {
314
+ color: #f87171;
139
315
  }
140
316
 
141
- .position-container {
142
- margin: 16px 20px;
317
+ .section.ban-section .section-title::after {
318
+ background: linear-gradient(90deg, rgba(248, 113, 113, 0.4), rgba(248, 113, 113, 0.05), transparent);
319
+ }
320
+
321
+ .ban-hidden-text {
322
+ color: #6b7280;
323
+ font-size: 13px;
324
+ font-style: italic;
325
+ text-align: center;
326
+ padding: 4px 0;
143
327
  }
144
- .position-container .position-header {
328
+
329
+ /* ===== MAP SECTION ===== */
330
+ .section.map-section {
331
+ padding: 0;
332
+ overflow: hidden;
333
+ }
334
+
335
+ .map-section .section-title {
336
+ padding: 14px 16px 0;
337
+ margin-bottom: 10px;
338
+ }
339
+
340
+ .map-section #map {
145
341
  width: 100%;
146
- height: 30px;
147
- box-sizing: border-box;
148
- padding: 0 12px;
149
- color: #ffffff;
342
+ height: 180px;
343
+ border-radius: 0 0 16px 16px;
344
+ }
345
+
346
+ /* ===== SPONSOR SECTION ===== */
347
+ .section.sponsor-section {
348
+ background: linear-gradient(135deg, rgba(250, 204, 21, 0.08) 0%, rgba(250, 204, 21, 0.02) 100%);
349
+ border-color: rgba(250, 204, 21, 0.15);
350
+ }
351
+
352
+ .section.sponsor-section .section-title {
353
+ color: #facc15;
354
+ }
355
+
356
+ .section.sponsor-section .section-title::after {
357
+ background: linear-gradient(90deg, rgba(250, 204, 21, 0.4), rgba(250, 204, 21, 0.05), transparent);
358
+ }
359
+
360
+ /* ===== GAME TIME SECTION ===== */
361
+ .section.game-section {
362
+ background: linear-gradient(135deg, rgba(59, 130, 246, 0.08) 0%, rgba(59, 130, 246, 0.02) 100%);
363
+ border-color: rgba(59, 130, 246, 0.15);
364
+ }
365
+
366
+ .section.game-section .section-title {
367
+ color: #60a5fa;
368
+ }
369
+
370
+ .section.game-section .section-title::after {
371
+ background: linear-gradient(90deg, rgba(96, 165, 250, 0.4), rgba(96, 165, 250, 0.05), transparent);
372
+ }
373
+
374
+ /* ===== MILEAGE SECTION ===== */
375
+ .section.mileage-section {
376
+ background: linear-gradient(135deg, rgba(16, 185, 129, 0.08) 0%, rgba(16, 185, 129, 0.02) 100%);
377
+ border-color: rgba(16, 185, 129, 0.15);
378
+ }
379
+
380
+ .section.mileage-section .section-title {
381
+ color: #34d399;
382
+ }
383
+
384
+ .section.mileage-section .section-title::after {
385
+ background: linear-gradient(90deg, rgba(52, 211, 153, 0.4), rgba(52, 211, 153, 0.05), transparent);
386
+ }
387
+
388
+ /* ===== GLASS BAR (ban count, last online) ===== */
389
+ .glass-bar {
390
+ margin: 10px 16px;
391
+ display: flex;
392
+ align-items: center;
393
+ justify-content: space-between;
394
+ padding: 10px 16px;
395
+ background: linear-gradient(135deg, rgba(255, 255, 255, 0.05) 0%, rgba(255, 255, 255, 0.02) 100%);
396
+ border: 1px solid rgba(255, 255, 255, 0.07);
397
+ border-radius: 12px;
398
+ backdrop-filter: blur(16px) saturate(1.1);
399
+ -webkit-backdrop-filter: blur(16px) saturate(1.1);
400
+ position: relative;
401
+ z-index: 1;
402
+ box-shadow:
403
+ 0 2px 8px rgba(0, 0, 0, 0.1),
404
+ 0 1px 1px rgba(255, 255, 255, 0.04) inset;
405
+ }
406
+
407
+ .glass-bar::before {
408
+ content: '';
409
+ position: absolute;
410
+ top: 0;
411
+ left: 0;
412
+ right: 0;
413
+ height: 1px;
414
+ background: linear-gradient(90deg, transparent 10%, rgba(255, 255, 255, 0.08) 50%, transparent 90%);
415
+ }
416
+
417
+ .glass-bar .info-label {
418
+ font-size: 13px;
419
+ }
420
+
421
+ .glass-bar .info-value {
150
422
  font-size: 14px;
151
- line-height: 30px;
152
- background-color: rgba(0, 0, 0, 0.4);
423
+ font-weight: 700;
153
424
  }
154
- .position-container #map {
155
- width: 100%;
156
- height: 150px;
157
- background-color: rgba(0, 0, 0, 0.25);
425
+
426
+ .glass-bar .info-value.has-bans {
427
+ color: #f87171;
428
+ text-shadow: 0 0 6px rgba(248, 113, 113, 0.3);
429
+ }
430
+
431
+ .glass-bar .info-value.no-bans {
432
+ color: #4ade80;
158
433
  }
159
434
  </style>
160
435
  </head>
161
436
  <body>
162
437
  <div id="container">
438
+ <!-- Ambient light orbs -->
439
+ <div class="ambient-orb orb1"></div>
440
+ <div class="ambient-orb orb2"></div>
441
+ <div class="ambient-orb orb3"></div>
442
+
443
+ <!-- Header -->
163
444
  <div class="header">
164
- <img class="avatar" id="tmp-avatar" src="" alt="avatar"/>
165
- <div class="info">
166
- <div class="name" id="tmp-name">-</div>
167
- <div class="tmp-id" id="tmp-id"></div>
445
+ <div class="avatar-wrapper">
446
+ <img class="avatar" id="tmp-avatar" src="" alt="avatar"/>
447
+ <div class="online-dot" id="online-dot"></div>
168
448
  </div>
169
- </div>
170
- <div class="details-container">
171
- <div class="form-box">
172
- <div class="form-item full">
173
- <div class="label">🎮SteamID</div>
174
- <div class="value" id="tmp-steam-id"></div>
175
- </div>
176
- <div class="form-item full">
177
- <div class="label">📑注册日期</div>
178
- <div class="value" id="tmp-register-date"></div>
179
- </div>
180
- <div class="form-item full" id="tmp-vtc-box">
181
- <div class="label">🚚所属车队</div>
182
- <div class="value" id="tmp-vtc-name"></div>
183
- </div>
184
- <div class="form-item full" id="tmp-vtc-role-box">
185
- <div class="label">🚚车队角色</div>
186
- <div class="value" id="tmp-vtc-role"></div>
187
- </div>
188
- <div class="form-item" id="tmp-sponsor-box">
189
- <div class="label">🎁赞助用户</div>
190
- <div class="value" id="tmp-sponsor-amount"></div>
191
- </div>
192
- <div class="form-item" id="tmp-sponsor-cumulative-box">
193
- <div class="label">🎁累计赞助</div>
194
- <div class="value" id="tmp-sponsor-cumulative"></div>
449
+ <div class="header-info">
450
+ <div class="player-name" id="tmp-name">-</div>
451
+ <div class="player-meta">
452
+ <span class="tmp-id" id="tmp-id"></span>
453
+ <span class="group-badge" id="group-badge"></span>
454
+ <span class="status-badge" id="status-badge"></span>
195
455
  </div>
196
456
  </div>
197
457
  </div>
198
- <div class="position-container" id="position-box">
199
- <div class="position-header" id="position-title"></div>
458
+
459
+ <!-- Basic Info -->
460
+ <div class="section">
461
+ <div class="section-title">📋 基本信息</div>
462
+ <div class="info-row">
463
+ <div class="info-label">🆔 SteamID</div>
464
+ <div class="info-value" id="tmp-steam-id"></div>
465
+ </div>
466
+ <div class="info-row">
467
+ <div class="info-label">📑 注册日期</div>
468
+ <div class="info-value" id="tmp-register-date"></div>
469
+ </div>
470
+ <div class="info-row">
471
+ <div class="info-label">💼 所属分组</div>
472
+ <div class="info-value" id="tmp-group-name"></div>
473
+ </div>
474
+ </div>
475
+
476
+ <!-- VTC Section (only if isJoinVtc) -->
477
+ <div class="section" id="vtc-section" style="display:none;">
478
+ <div class="section-title">🚚 车队信息</div>
479
+ <div class="info-row">
480
+ <div class="info-label">🚚 所属车队</div>
481
+ <div class="info-value gold" id="tmp-vtc-name"></div>
482
+ </div>
483
+ <div class="info-row">
484
+ <div class="info-label">🚚 车队角色</div>
485
+ <div class="info-value" id="tmp-vtc-role"></div>
486
+ </div>
487
+ <div id="vtc-history-container"></div>
488
+ <div class="info-row" id="reward-points-row" style="display:none;">
489
+ <div class="info-label">⭐ 当前车队积分</div>
490
+ <div class="info-value gold" id="tmp-reward-points"></div>
491
+ </div>
492
+ </div>
493
+
494
+ <!-- Online Status -->
495
+ <div class="section" id="status-section">
496
+ <div class="section-title">📶 在线状态</div>
497
+ <div class="info-row">
498
+ <div class="info-label">📶 状态</div>
499
+ <div class="info-value" id="tmp-online-status"></div>
500
+ </div>
501
+ <div class="info-row" id="online-location-row" style="display:none;">
502
+ <div class="info-label">🌍 线上位置</div>
503
+ <div class="info-value" id="tmp-online-location"></div>
504
+ </div>
505
+ </div>
506
+
507
+ <!-- Last Online (only when offline) -->
508
+ <div class="glass-bar" id="last-online-bar" style="display:none;">
509
+ <div class="info-label">📶 上次在线</div>
510
+ <div class="info-value dim" id="tmp-last-online"></div>
511
+ </div>
512
+
513
+ <!-- Map Section (only when online) -->
514
+ <div class="section map-section" id="map-section" style="display:none;">
515
+ <div class="section-title" id="map-title">🗺️ 位置地图</div>
200
516
  <div id="map"></div>
201
517
  </div>
202
- <div class="ban-container" id="ban-box">
203
- <div class="ban-header">封禁中</div>
204
- <div class="ban-body">
205
- <div class="ban-hidden" id="ban-hidden-box">玩家隐藏信息</div>
206
- <div class="form-box" id="ban-info-box">
207
- <div class="form-item">
208
- <div class="label">截止时间</div>
209
- <div class="value" id="ban-until"></div>
210
- </div>
211
- <div class="form-item">
212
- <div class="label">封禁次数</div>
213
- <div class="value" id="ban-count"></div>
214
- </div>
215
- <div class="form-item full">
216
- <div class="label">原因</div>
217
- <div class="value" id="ban-reason"></div>
218
- </div>
219
- </div>
518
+
519
+ <!-- Ban Section (only when banned) -->
520
+ <div class="section ban-section" id="ban-section" style="display:none;">
521
+ <div class="section-title">🚫 封禁信息</div>
522
+ <div id="ban-content"></div>
523
+ </div>
524
+
525
+ <!-- Ban Count (always shown) -->
526
+ <div class="glass-bar" id="ban-count-bar">
527
+ <div class="info-label">🚫 封禁次数</div>
528
+ <div class="info-value" id="tmp-ban-count"></div>
529
+ </div>
530
+
531
+ <!-- Mileage Section -->
532
+ <div class="section mileage-section" id="mileage-section" style="display:none;">
533
+ <div class="section-title">🚩 里程信息</div>
534
+ <div class="info-row" id="mileage-row" style="display:none;">
535
+ <div class="info-label">🚩 历史里程</div>
536
+ <div class="info-value" id="tmp-mileage"></div>
537
+ </div>
538
+ <div class="info-row" id="today-mileage-row" style="display:none;">
539
+ <div class="info-label">🚩 今日里程</div>
540
+ <div class="info-value" id="tmp-today-mileage"></div>
541
+ </div>
542
+ </div>
543
+
544
+ <!-- Game Time Section -->
545
+ <div class="section game-section" id="game-section" style="display:none;">
546
+ <div class="section-title">🎮 游戏时长</div>
547
+ <div class="info-row" id="ets2-time-row" style="display:none;">
548
+ <div class="info-label">🎮 欧卡游戏时长</div>
549
+ <div class="info-value" id="tmp-ets2-time"></div>
550
+ </div>
551
+ <div class="info-row" id="ats-time-row" style="display:none;">
552
+ <div class="info-label">🎮 美卡游戏时长</div>
553
+ <div class="info-value" id="tmp-ats-time"></div>
554
+ </div>
555
+ </div>
556
+
557
+ <!-- Sponsor Section -->
558
+ <div class="section sponsor-section" id="sponsor-section" style="display:none;">
559
+ <div class="section-title">🌟 赞助信息</div>
560
+ <div class="info-row">
561
+ <div class="info-label">🌟 Patreon支持者</div>
562
+ <div class="info-value" id="tmp-sponsor-status"></div>
563
+ </div>
564
+ <div class="info-row">
565
+ <div class="info-label">💰 当前赞助金额</div>
566
+ <div class="info-value" id="tmp-sponsor-amount"></div>
567
+ </div>
568
+ <div class="info-row">
569
+ <div class="info-label">💰 全部赞助金额</div>
570
+ <div class="info-value" id="tmp-sponsor-cumulative"></div>
220
571
  </div>
221
572
  </div>
222
573
  </div>
574
+
223
575
  <script>
224
576
  let mapConfig = {
225
577
  ets: {
226
578
  tileUrl: 'https://ets-map.oss-cn-beijing.aliyuncs.com/ets2/05102019/{z}/{x}/{y}.png',
227
- multipliers: {
228
- x: 71282,
229
- y: 56532
230
- },
231
- breakpoints: {
232
- uk: {
233
- x: -31056.8,
234
- y: -5832.867
235
- }
236
- },
237
- bounds: {
238
- y: 131072,
239
- x: 131072
240
- },
579
+ multipliers: { x: 71282, y: 56532 },
580
+ breakpoints: { uk: { x: -31056.8, y: -5832.867 } },
581
+ bounds: { y: 131072, x: 131072 },
241
582
  maxZoom: 8,
242
583
  minZoom: 2,
243
- // 游戏地转地图坐标
244
584
  calculateMapCoordinate (x, y) {
245
- return [
246
- x / 1.325928 + mapConfig.ets.multipliers.x,
247
- y / 1.325928 + mapConfig.ets.multipliers.y
248
- ];
585
+ return [x / 1.325928 + mapConfig.ets.multipliers.x, y / 1.325928 + mapConfig.ets.multipliers.y];
249
586
  }
250
587
  },
251
588
  promods: {
252
589
  tileUrl: 'https://ets-map.oss-cn-beijing.aliyuncs.com/promods/05102019/{z}/{x}/{y}.png',
253
- multipliers: {
254
- x: 51953,
255
- y: 76024
256
- },
257
- breakpoints: {
258
- uk: {
259
- x: -31056.8,
260
- y: -5832.867
261
- }
262
- },
263
- bounds: {
264
- y: 131072,
265
- x: 131072
266
- },
590
+ multipliers: { x: 51953, y: 76024 },
591
+ breakpoints: { uk: { x: -31056.8, y: -5832.867 } },
592
+ bounds: { y: 131072, x: 131072 },
267
593
  maxZoom: 8,
268
594
  minZoom: 2,
269
- // 游戏地转地图坐标
270
595
  calculateMapCoordinate (x, y) {
271
- return [
272
- x / 2.598541 + mapConfig.promods.multipliers.x,
273
- y / 2.598541 + mapConfig.promods.multipliers.y
274
- ]
596
+ return [x / 2.598541 + mapConfig.promods.multipliers.x, y / 2.598541 + mapConfig.promods.multipliers.y];
275
597
  }
276
598
  }
277
599
  }
278
600
 
601
+ function formatMileage(val) {
602
+ if (!val) return null;
603
+ if (val > 1000) return (val / 1000).toFixed(1) + ' 公里';
604
+ return val + ' 米';
605
+ }
606
+
279
607
  function init(data) {
280
- document.getElementById('tmp-name').innerText = data.name
281
- document.getElementById('tmp-avatar').src = data.avatarUrl
282
- document.getElementById('tmp-id').innerHTML = `TMP#${data.tmpId} <span class="group" style="background-color: #${data.groupColor}">${data.groupName}</span>`
283
- document.getElementById('tmp-steam-id').innerText = data.steamId
284
- document.getElementById('tmp-register-date').innerText = data.registerDate
285
- if (data.isJoinVtc) {
286
- document.getElementById('tmp-vtc-name').innerText = data.vtcName
287
- document.getElementById('tmp-vtc-role').innerText = data.vtcRole
608
+ // Header
609
+ document.getElementById('tmp-avatar').src = data.avatarUrl;
610
+ document.getElementById('tmp-name').innerText = data.name;
611
+ document.getElementById('tmp-id').innerText = 'TMP#' + data.tmpId;
612
+ document.getElementById('group-badge').innerText = data.groupName;
613
+ document.getElementById('group-badge').style.backgroundColor = '#' + data.groupColor;
614
+
615
+ // Online dot & status badge
616
+ const dot = document.getElementById('online-dot');
617
+ const badge = document.getElementById('status-badge');
618
+ if (data.isOnline) {
619
+ dot.className = 'online-dot online';
620
+ badge.className = 'status-badge online';
621
+ badge.innerText = '在线';
288
622
  } else {
289
- document.getElementById('tmp-vtc-box').remove()
290
- document.getElementById('tmp-vtc-role-box').remove()
623
+ dot.className = 'online-dot offline';
624
+ badge.className = 'status-badge offline';
625
+ badge.innerText = '离线';
291
626
  }
292
- if (data.isSponsor && !data.sponsorHide) {
293
- document.getElementById('tmp-sponsor-amount').innerText = '$' + Math.floor(data.sponsorAmount / 100)
294
- document.getElementById('tmp-sponsor-cumulative').innerText = '$' + Math.floor(data.sponsorCumulativeAmount / 100)
295
- } else {
296
- document.getElementById('tmp-sponsor-box').remove()
297
- document.getElementById('tmp-sponsor-cumulative-box').remove()
627
+
628
+ // Basic Info
629
+ document.getElementById('tmp-steam-id').innerText = data.steamId;
630
+ document.getElementById('tmp-register-date').innerText = data.registerDate + ' (' + data.registerDays + '天)';
631
+ document.getElementById('tmp-group-name').innerText = data.groupName;
632
+
633
+ // VTC Section
634
+ if (data.isJoinVtc) {
635
+ document.getElementById('vtc-section').style.display = '';
636
+ document.getElementById('tmp-vtc-name').innerText = data.vtcName;
637
+ document.getElementById('tmp-vtc-role').innerText = data.vtcRole;
638
+
639
+ if (data.vtcHistory && data.vtcHistory.length > 0) {
640
+ const historyContainer = document.getElementById('vtc-history-container');
641
+ data.vtcHistory.forEach(function(vtc) {
642
+ const item = document.createElement('div');
643
+ item.className = 'vtc-history-item';
644
+ item.innerHTML =
645
+ '<div class="vtc-history-name">📜 ' + vtc.vtcName + '</div>' +
646
+ '<div class="vtc-history-dates">加入: ' + formatDate(vtc.joinDate) + ' · 离开: ' + formatDate(vtc.quitDate) + '</div>';
647
+ historyContainer.appendChild(item);
648
+ });
649
+ }
650
+
651
+ if (data.rewardPoints > 0) {
652
+ document.getElementById('reward-points-row').style.display = '';
653
+ document.getElementById('tmp-reward-points').innerText = data.rewardPoints;
654
+ }
298
655
  }
656
+
657
+ // Online Status Section
658
+ const statusEl = document.getElementById('tmp-online-status');
299
659
  if (data.isOnline) {
300
- document.getElementById('position-title').innerText = `${data.onlineServerName} · ${data.onlineCountry}-${data.onlineCity}`
301
- map(data.onlineMapType, data.onlineX, data.onlineY)
660
+ statusEl.innerHTML = '<span style="color:#4ade80;">在线 🟢</span> <span style="color:#94a3b8;font-size:12px;">(' + data.onlineServerName + ')</span>';
661
+ if (data.onlineCountry && data.onlineCity) {
662
+ document.getElementById('online-location-row').style.display = '';
663
+ document.getElementById('tmp-online-location').innerText = data.onlineCountry + ' - ' + data.onlineCity;
664
+ }
302
665
  } else {
303
- document.getElementById('position-box').remove()
666
+ statusEl.innerHTML = '<span style="color:#9ca3af;">离线 ⚫</span>';
667
+ }
668
+
669
+ // Last Online
670
+ if (!data.isOnline && data.lastOnlineTime) {
671
+ document.getElementById('last-online-bar').style.display = '';
672
+ document.getElementById('tmp-last-online').innerText = data.lastOnlineTime;
304
673
  }
674
+
675
+ // Map
676
+ if (data.isOnline && data.onlineX !== undefined) {
677
+ document.getElementById('map-section').style.display = '';
678
+ if (data.onlineCountry && data.onlineCity) {
679
+ document.getElementById('map-title').innerText = '🗺️ ' + data.onlineCountry + ' - ' + data.onlineCity;
680
+ }
681
+ map(data.onlineMapType, data.onlineX, data.onlineY);
682
+ }
683
+
684
+ // Ban Section
305
685
  if (data.isBan) {
686
+ document.getElementById('ban-section').style.display = '';
687
+ const banContent = document.getElementById('ban-content');
306
688
  if (data.banHide) {
307
- document.getElementById('ban-info-box').remove()
689
+ banContent.innerHTML = '<div class="ban-hidden-text">玩家隐藏了封禁信息</div>';
308
690
  } else {
309
- document.getElementById('ban-hidden-box').remove()
310
- document.getElementById('ban-until').innerText = (data.banUntil || '永久')
311
- document.getElementById('ban-count').innerText = (data.banCount || 0) + '次'
312
- document.getElementById('ban-reason').innerText = data.banReasonZh || data.banReason
691
+ let until = '永久';
692
+ if (data.banUntil) until = formatDate(data.banUntil);
693
+ banContent.innerHTML =
694
+ '<div class="info-row"><div class="info-label">截止时间</div><div class="info-value red">' + until + '</div></div>' +
695
+ '<div class="info-row"><div class="info-label">封禁原因</div><div class="info-value red">' + (data.banReasonZh || data.banReason) + '</div></div>';
313
696
  }
314
- } else {
315
- document.getElementById('ban-box').remove()
697
+ }
698
+
699
+ // Ban Count
700
+ const banCountEl = document.getElementById('tmp-ban-count');
701
+ const banCount = data.banCount || 0;
702
+ banCountEl.innerText = banCount + '次';
703
+ banCountEl.className = banCount > 0 ? 'info-value has-bans' : 'info-value no-bans';
704
+
705
+ // Mileage
706
+ const mileage = formatMileage(data.mileage);
707
+ const todayMileage = formatMileage(data.todayMileage);
708
+ if (mileage || todayMileage) {
709
+ document.getElementById('mileage-section').style.display = '';
710
+ if (mileage) {
711
+ document.getElementById('mileage-row').style.display = '';
712
+ document.getElementById('tmp-mileage').innerText = mileage;
713
+ }
714
+ if (todayMileage) {
715
+ document.getElementById('today-mileage-row').style.display = '';
716
+ document.getElementById('tmp-today-mileage').innerText = todayMileage;
717
+ }
718
+ }
719
+
720
+ // Game Time
721
+ if (data.ets2GameTime || data.atsGameTime) {
722
+ document.getElementById('game-section').style.display = '';
723
+ if (data.ets2GameTime) {
724
+ document.getElementById('ets2-time-row').style.display = '';
725
+ document.getElementById('tmp-ets2-time').innerText = data.ets2GameTime;
726
+ }
727
+ if (data.atsGameTime) {
728
+ document.getElementById('ats-time-row').style.display = '';
729
+ document.getElementById('tmp-ats-time').innerText = data.atsGameTime;
730
+ }
731
+ }
732
+
733
+ // Sponsor
734
+ if (data.isSponsor && !data.sponsorHide) {
735
+ document.getElementById('sponsor-section').style.display = '';
736
+ document.getElementById('tmp-sponsor-status').innerHTML = '<span style="color:#facc15;">是</span>';
737
+ const currentAmount = data.sponsorAmount == 'null' ? '0' : (data.sponsorAmount / 100).toFixed(0);
738
+ const cumulativeAmount = data.sponsorCumulativeAmount == 'null' ? '0' : (data.sponsorCumulativeAmount / 100).toFixed(0);
739
+ document.getElementById('tmp-sponsor-amount').innerText = '$' + currentAmount;
740
+ document.getElementById('tmp-sponsor-cumulative').innerText = '$' + cumulativeAmount;
316
741
  }
317
742
  }
318
743
 
744
+ function formatDate(dateStr) {
745
+ if (!dateStr) return '-';
746
+ const d = new Date(dateStr);
747
+ return d.getFullYear() + '年' + String(d.getMonth() + 1).padStart(2, '0') + '月' + String(d.getDate()).padStart(2, '0') + '日 ' + String(d.getHours()).padStart(2, '0') + ':' + String(d.getMinutes()).padStart(2, '0');
748
+ }
749
+
319
750
  function map(mapType, x, y) {
320
- // 定义地图
321
- let map = L.map('map', {
322
- attributionControl: false,
323
- crs: L.CRS.Simple,
324
- zoomControl: false
325
- });
326
-
327
- // 边界
751
+ let map = L.map('map', { attributionControl: false, crs: L.CRS.Simple, zoomControl: false });
328
752
  let bounds = L.latLngBounds(
329
753
  map.unproject([0, mapConfig[mapType].bounds.y], mapConfig[mapType].maxZoom),
330
754
  map.unproject([mapConfig[mapType].bounds.x, 0], mapConfig[mapType].maxZoom)
331
755
  );
332
-
333
- // 瓦片地图
334
756
  L.tileLayer(mapConfig[mapType].tileUrl, {
335
- minZoom: 2,
336
- maxZoom: 10,
337
- maxNativeZoom: 8,
338
- tileSize: 512,
339
- bounds: bounds,
340
- reuseTiles: true
757
+ minZoom: 2, maxZoom: 10, maxNativeZoom: 8, tileSize: 512, bounds: bounds, reuseTiles: true
341
758
  }).addTo(map);
342
- map.setMaxBounds(
343
- new L.LatLngBounds(
344
- map.unproject([0, mapConfig[mapType].bounds.y], mapConfig[mapType].maxZoom),
345
- map.unproject([mapConfig[mapType].bounds.x, 0], mapConfig[mapType].maxZoom)
346
- )
347
- );
348
-
759
+ map.setMaxBounds(new L.LatLngBounds(
760
+ map.unproject([0, mapConfig[mapType].bounds.y], mapConfig[mapType].maxZoom),
761
+ map.unproject([mapConfig[mapType].bounds.x, 0], mapConfig[mapType].maxZoom)
762
+ ));
349
763
  L.circleMarker(map.unproject(mapConfig[mapType].calculateMapCoordinate(x, y), 8), {
350
- color: '#2f2f2f', // 标记点边框颜色
351
- weight: 2, // 标记点边框大小
352
- fillColor: '#1cb715', // 标记点填充颜色
353
- fillOpacity: 1, // 标记点填充不透明度(0到1之间的值)
354
- radius: 5, // 标记点半径(以像素为单位)
355
- zIndex: 1000
764
+ color: '#2f2f2f', weight: 2, fillColor: '#1cb715', fillOpacity: 1, radius: 5, zIndex: 1000
356
765
  }).addTo(map);
357
-
358
- // 设置中心点
359
766
  map.setView(map.unproject(mapConfig[mapType].calculateMapCoordinate(x, y), 8), 5);
360
767
  }
361
768
  </script>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-ets2-tools-tmp",
3
3
  "description": "欧卡2 TruckersMP信息查询、车队平台查询及活动提醒",
4
- "version": "3.0.6",
4
+ "version": "3.1.0",
5
5
  "contributors": [
6
6
  "opwop <slhp1013@qq.com>",
7
7
  "bot_actions <168329908@qq.com>"