koishi-plugin-ets2-tools-tmp 3.4.6 → 3.5.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.
Files changed (33) hide show
  1. package/lib/command/ets-app/changePoint/changePointV1.js +28 -7
  2. package/lib/command/ets-app/pointRanking/pointRanking.js +12 -5
  3. package/lib/command/ets-app/queryPoint/queryPoint.js +15 -1
  4. package/lib/command/ets-app/queryPoint/queryPointV1.js +21 -5
  5. package/lib/command/ets-app/resetPassword/resetPasswordV1.js +32 -3
  6. package/lib/command/tmpBind.js +41 -4
  7. package/lib/command/tmpDlcMap.js +1 -1
  8. package/lib/command/tmpFootprint.js +3 -2
  9. package/lib/command/tmpMileageRanking.js +74 -23
  10. package/lib/command/tmpPosition.js +3 -2
  11. package/lib/command/tmpQuery/tmpQuery.js +70 -1
  12. package/lib/command/tmpQuery/tmpQueryImg.js +4 -3
  13. package/lib/command/tmpQuery/tmpQueryText.js +4 -3
  14. package/lib/command/tmpServer/tmpServer.js +47 -3
  15. package/lib/command/tmpServer/tmpServerImg.js +1 -1
  16. package/lib/command/tmpTraffic/tmpTraffic.js +64 -3
  17. package/lib/command/tmpTraffic/tmpTrafficMap.js +2 -2
  18. package/lib/command/tmpVersion.js +81 -9
  19. package/lib/command/tmpVtcMileageRanking.js +75 -22
  20. package/lib/command/tmpVtcOnline/tmpVtcOnlineImg.js +1 -1
  21. package/lib/database/guildBind.js +150 -1
  22. package/lib/database/model.js +12 -0
  23. package/lib/index.js +139 -15
  24. package/lib/resource/command-suggest.html +222 -0
  25. package/lib/resource/tmp-version.html +292 -0
  26. package/lib/util/commandSuggest.js +140 -0
  27. package/lib/util/commandSuggestMiddleware.js +210 -0
  28. package/lib/util/localImageHost.js +219 -0
  29. package/lib/util/messageRender.js +234 -0
  30. package/lib/util/qqMarkdown.js +171 -0
  31. package/lib/util/qqMediaUpload.js +165 -0
  32. package/lib/util/qqOfficial.js +233 -0
  33. package/package.json +1 -1
package/lib/index.js CHANGED
@@ -10,6 +10,9 @@ const { ServerType } = require('./util/constant');
10
10
  const { MileageRankingType } = require('./util/constant');
11
11
  const { LeadRoleCallMessage, LeadRoleCallTime } = require('./util/constant');
12
12
  const tmpIdPicker = require('./util/tmpIdPicker');
13
+ const qqOfficial = require('./util/qqOfficial');
14
+ const commandSuggestMiddleware = require('./util/commandSuggestMiddleware');
15
+ const localImageHost = require('./util/localImageHost');
13
16
 
14
17
  const commands = {
15
18
  tmpQuery: require('./command/tmpQuery/tmpQuery'),
@@ -114,6 +117,18 @@ exports.Config = koishi_1.Schema.intersect([
114
117
  koishi_1.Schema.const(2).description('热力图')
115
118
  ]).default(1).description('路况信息展示方式'),
116
119
  }).description('路况查询配置'),
120
+ tmpMileageRanking: koishi_1.Schema.object({
121
+ type: koishi_1.Schema.union([
122
+ koishi_1.Schema.const(1).description('文字'),
123
+ koishi_1.Schema.const(2).description('图片')
124
+ ]).default(2).description('里程排行榜展示方式')
125
+ }).description('里程排行榜配置'),
126
+ tmpVtcMileageRanking: koishi_1.Schema.object({
127
+ type: koishi_1.Schema.union([
128
+ koishi_1.Schema.const(1).description('文字'),
129
+ koishi_1.Schema.const(2).description('图片')
130
+ ]).default(2).description('VTC里程排行榜展示方式')
131
+ }).description('VTC里程排行榜配置'),
117
132
  tmpServer: koishi_1.Schema.object({
118
133
  type: koishi_1.Schema.union([
119
134
  koishi_1.Schema.const(1).description('文字'),
@@ -126,6 +141,12 @@ exports.Config = koishi_1.Schema.intersect([
126
141
  koishi_1.Schema.const(2).description('图片')
127
142
  ]).default(1).description('车队在线成员展示方式')
128
143
  }).description('车队在线成员查询'),
144
+ tmpVersion: koishi_1.Schema.object({
145
+ type: koishi_1.Schema.union([
146
+ koishi_1.Schema.const(1).description('文字'),
147
+ koishi_1.Schema.const(2).description('图片')
148
+ ]).default(2).description('tmp版本展示方式')
149
+ }).description('tmp版本查询配置'),
129
150
 
130
151
  tmpVersionCheck: koishi_1.Schema.object({
131
152
  checkInterval: koishi_1.Schema.number().description("版本检查间隔(分钟)").default(30),
@@ -215,10 +236,105 @@ exports.Config = koishi_1.Schema.intersect([
215
236
  logMessageSending: koishi_1.Schema.boolean().description("记录消息发送详情").default(false)
216
237
  }).description("开发者选项")
217
238
  }).description("活动查询配置")
218
- }).description('功能配置')
239
+ }).description('功能配置'),
240
+ koishi_1.Schema.object({
241
+ commandSuggest: koishi_1.Schema.object({
242
+ enable: koishi_1.Schema.boolean().description("启用「猜你想问」指令纠错提示(打错指令时给出相近指令建议)").default(true),
243
+ type: koishi_1.Schema.union([
244
+ koishi_1.Schema.const(1).description('文字'),
245
+ koishi_1.Schema.const(2).description('图片')
246
+ ]).default(1).description('「猜你想问」提示的展示方式(图片模式下官方机器人会发图片并在下方附带按钮)'),
247
+ maxDistance: koishi_1.Schema.number().description("最大编辑距离:用户输入与指令名的字符差异上限,越大越宽松(默认按长度自适应,此项留空即自适应)")
248
+ }).description("指令纠错配置"),
249
+ qqOfficial: koishi_1.Schema.object({
250
+ enable: koishi_1.Schema.boolean().description("启用 QQ 官方机器人增强:文本用 markdown 发送、附带交互按钮(仅对 qq/qqguild 适配器生效,onebot 不受影响)").default(true),
251
+ showQueryButtons: koishi_1.Schema.boolean().description("在查询类结果下方附加常用查询按钮(仅官方机器人)").default(true),
252
+ publicBaseUrl: koishi_1.Schema.string()
253
+ .role('link')
254
+ .description("Koishi 对外公网地址(如 https://bd.114512.xyz:5155)。填写后图片会存为本地文件并以公网 URL 发送,不受 QQ 官方 100 张图片限制,且手机端可正常显示;留空则使用 QQ 官方素材上传")
255
+ .default('')
256
+ }).description("QQ 官方机器人配置")
257
+ }).description("通用与适配器配置")
219
258
  ]);
220
259
 
221
260
 
261
+
262
+ /**
263
+ * 收集本插件注册的指令名,供「猜你想问」使用。
264
+ * 只取指令定义里第一个空格前的部分(去掉参数说明)。
265
+ */
266
+ function collectCommandNames(cfg) {
267
+ const flags = cfg.commands || {};
268
+ const list = [];
269
+ const add = (enabled, defs) => {
270
+ if (!enabled) return;
271
+ for (const def of defs) list.push(String(def).split(' ')[0]);
272
+ };
273
+
274
+ add(flags.tmpQuery, ['查询']);
275
+ add(flags.tmpServer, ['美卡服务器', '欧卡服务器']);
276
+ add(flags.tmpBind, ['绑定', '我的绑定', '设为默认', '解绑']);
277
+ add(flags.tmpTraffic, ['路况']);
278
+ add(flags.tmpPosition, ['定位']);
279
+ add(flags.tmpVersion, ['tmp版本']);
280
+ add(flags.tmpDlcMap, ['地图dlc价格']);
281
+ add(flags.tmpMileageRanking, ['里程排行榜', '今日里程排行榜']);
282
+ add(flags.tmpVtcMileageRanking, ['vtc里程排行榜', 'vtc今日里程排行榜']);
283
+ add(flags.pointRanking, ['积分排行']);
284
+ add(flags.tmpVtc, ['vtc查询']);
285
+ add(flags.tmpVtcOnline, ['车队在线成员']);
286
+ add(flags.tmpFootprint, [
287
+ '近一年足迹', '近一年足迹p', '近一年足迹美',
288
+ '近一个月足迹美', '近七日足迹美', '昨日足迹美', '今日足迹美',
289
+ '近一个月足迹', '近一个月足迹p', '近七日足迹', '近七日足迹p',
290
+ '昨日足迹', '昨日足迹p', '今日足迹', '今日足迹p',
291
+ ]);
292
+ add(flags.resetPassword, ['重置密码']);
293
+ add(flags.mainSettings, ['查询积分']);
294
+ add(flags.changePoint, ['积分修改']);
295
+
296
+ // 无开关、始终注册的指令
297
+ list.push('规则查询');
298
+
299
+ return list;
300
+ }
301
+
302
+ /**
303
+ * 注册「猜你想问」中间件。
304
+ */
305
+ /**
306
+ * 注册本地图片托管路由(仅配置了公网地址时启用)。
307
+ */
308
+ function setupLocalImageHost(ctx, cfg) {
309
+ try {
310
+ const baseUrl = localImageHost.getPublicBaseUrl(cfg);
311
+ if (!baseUrl) {
312
+ if (cfg.debugMode) ctx.logger.debug('[本地图片] 未配置 qqOfficial.publicBaseUrl,图片将由 QQ 官方素材接口上传');
313
+ return;
314
+ }
315
+ const dir = localImageHost.registerStaticRoute(ctx, cfg);
316
+ if (dir) {
317
+ ctx.logger.info(`[本地图片] 已启用,访问地址: ${baseUrl}${localImageHost.ROUTE_PREFIX}/,存储目录: ${dir}`);
318
+ } else {
319
+ ctx.logger.warn('[本地图片] 未找到 server 服务,图片将回退为 QQ 官方素材上传');
320
+ }
321
+ } catch (error) {
322
+ ctx.logger.error('[本地图片] 初始化失败:', error.message);
323
+ }
324
+ }
325
+
326
+ function registerCommandSuggest(ctx, cfg) {
327
+ try {
328
+ const names = collectCommandNames(cfg);
329
+ commandSuggestMiddleware.register(ctx, cfg, names);
330
+ if (cfg.debugMode) {
331
+ ctx.logger.debug(`[猜你想问] 已启用,候选指令 ${names.length} 个`);
332
+ }
333
+ } catch (error) {
334
+ ctx.logger.error("[猜你想问] 初始化失败:", error.message);
335
+ }
336
+ }
337
+
222
338
  function logDisabledCommands(ctx, cfg) {
223
339
  const commandFlags = cfg.commands || {};
224
340
  const enabled = [];
@@ -259,11 +375,11 @@ function registerBaseCommands(ctx, cfg) {
259
375
  if (cfg.commands?.tmpServer) {
260
376
  ctx.command('美卡服务器')
261
377
  .usage("查询美国卡车模拟器TMP服务器信息")
262
- .action(async () => await commands.tmpServer(ctx, cfg, 'ATS'));
378
+ .action(async ({ session }) => await commands.tmpServer(ctx, cfg, session, 'ATS'));
263
379
 
264
380
  ctx.command('欧卡服务器')
265
381
  .usage("查询欧洲卡车模拟2TMP服务器信息")
266
- .action(async () => await commands.tmpServer(ctx, cfg, 'ETS2'));
382
+ .action(async ({ session }) => await commands.tmpServer(ctx, cfg, session, 'ETS2'));
267
383
  }
268
384
 
269
385
  if (cfg.commands?.tmpQuery) {
@@ -274,7 +390,7 @@ function registerBaseCommands(ctx, cfg) {
274
390
  ctx.command('我的绑定')
275
391
  .usage("查看已绑定的全部TMP编号(★ 为默认绑定)")
276
392
  .action(async ({ session }) => {
277
- const bindings = await guildBind.list(ctx.database, session.platform, session.userId);
393
+ const bindings = await guildBind.listWithUnion(ctx.database, session.platform, session.userId, qqOfficial.buildIdentity(session));
278
394
  if (bindings.length === 0) {
279
395
  return '还没有绑定编号,使用「绑定 tmpId」添加';
280
396
  }
@@ -297,9 +413,10 @@ function registerBaseCommands(ctx, cfg) {
297
413
  if (!index || !index.trim()) {
298
414
  return '请输入要设为默认的序号(序号见「我的绑定」)';
299
415
  }
300
- await guildBind.refreshNames(ctx, await guildBind.list(ctx.database, session.platform, session.userId));
301
- const { row, changed, total } = await guildBind.setDefaultByIndex(
302
- ctx, ctx.database, session.platform, session.userId, index.trim()
416
+ const bindings = await guildBind.listWithUnion(ctx.database, session.platform, session.userId, qqOfficial.buildIdentity(session));
417
+ await guildBind.refreshNames(ctx, bindings);
418
+ const { row, changed, total } = await guildBind.setDefaultByIndexFromRows(
419
+ ctx.database, bindings, index.trim()
303
420
  );
304
421
  if (!row) {
305
422
  if (!total) {
@@ -316,7 +433,7 @@ function registerBaseCommands(ctx, cfg) {
316
433
  .example("解绑 2")
317
434
  .example("解绑 all")
318
435
  .action(async ({ session }, index) => {
319
- const bindings = await guildBind.list(ctx.database, session.platform, session.userId);
436
+ const bindings = await guildBind.listWithUnion(ctx.database, session.platform, session.userId, qqOfficial.buildIdentity(session));
320
437
  if (bindings.length === 0) {
321
438
  return '还没有绑定编号';
322
439
  }
@@ -324,8 +441,9 @@ function registerBaseCommands(ctx, cfg) {
324
441
  return '请输入要解绑的序号或 all(序号见「我的绑定」)';
325
442
  }
326
443
  if (/^all$/i.test(index.trim())) {
327
- await guildBind.removeAll(ctx.database, session.platform, session.userId);
328
- return `已删除全部 ${bindings.length} 个绑定`;
444
+ // 用「已查到的行」删除:跨群时 user_id 是对方群的 openid,不能再按 (platform,user_id) 反查
445
+ const removed = await guildBind.removeAllFromRows(ctx.database, bindings);
446
+ return `已删除全部 ${removed} 个绑定`;
329
447
  }
330
448
  const idx = parseInt(index, 10);
331
449
  if (isNaN(idx) || idx < 1 || idx > bindings.length) {
@@ -334,7 +452,7 @@ function registerBaseCommands(ctx, cfg) {
334
452
  // 到这一步才需要显示名字(成功文案 / 顶替提示)→ 此时刷新
335
453
  await guildBind.refreshNames(ctx, bindings);
336
454
  const row = bindings[idx - 1];
337
- const { promoted } = await guildBind.remove(ctx.database, session.platform, session.userId, row.id);
455
+ const { promoted } = await guildBind.removeFromRows(ctx.database, bindings, row.id);
338
456
  let msg = `已解绑 ${idx}. ${row.tmp_name || '未知'} (${row.tmp_id})`;
339
457
  if (promoted) {
340
458
  msg += `\n默认绑定已自动切换到 ${promoted.tmp_name || '未知'} (${promoted.tmp_id})`;
@@ -347,7 +465,7 @@ function registerBaseCommands(ctx, cfg) {
347
465
  ctx.command('路况 <serverName>')
348
466
  .usage("查询欧洲卡车模拟2服务器路况")
349
467
  .example("路况 - s1")
350
- .action(async ({ session }, serverName) => await commands.tmpTraffic(ctx, cfg, serverName));
468
+ .action(async ({ session }, serverName) => await commands.tmpTraffic(ctx, cfg, session, serverName));
351
469
  }
352
470
 
353
471
  if (cfg.commands?.tmpPosition) {
@@ -359,7 +477,7 @@ function registerBaseCommands(ctx, cfg) {
359
477
  if (cfg.commands?.tmpVersion) {
360
478
  ctx.command('tmp版本')
361
479
  .usage("查询TruckersMP支持的游戏版本")
362
- .action(async () => await commands.tmpVersion(ctx));
480
+ .action(async ({ session }) => await commands.tmpVersion(ctx, cfg, session));
363
481
  }
364
482
 
365
483
  if (cfg.commands?.tmpDlcMap) {
@@ -371,11 +489,11 @@ function registerBaseCommands(ctx, cfg) {
371
489
  if (cfg.commands?.tmpMileageRanking) {
372
490
  ctx.command('里程排行榜 [target]')
373
491
  .usage("查询欧洲卡车模拟2里程排行榜(可 @某人 查看对方的排名)")
374
- .action(async ({ session }, target) => await commands.tmpMileageRanking(ctx, session, MileageRankingType.total, target));
492
+ .action(async ({ session }, target) => await commands.tmpMileageRanking(ctx, cfg, session, MileageRankingType.total, target));
375
493
 
376
494
  ctx.command('今日里程排行榜 [target]')
377
495
  .usage("查询欧洲卡车模拟2今日里程排行榜(可 @某人 查看对方的排名)")
378
- .action(async ({ session }, target) => await commands.tmpMileageRanking(ctx, session, MileageRankingType.today, target));
496
+ .action(async ({ session }, target) => await commands.tmpMileageRanking(ctx, cfg, session, MileageRankingType.today, target));
379
497
  }
380
498
 
381
499
  if (cfg.commands?.tmpVtcMileageRanking) {
@@ -533,6 +651,12 @@ function apply(ctx, cfg) {
533
651
  // 多绑定选择器中间件(捕获用户回复的序号,完成"列出绑定→序号选择"交互)
534
652
  tmpIdPicker.register(ctx);
535
653
 
654
+ // 「猜你想问」中间件:指令打错时给出提示(只提示不执行)
655
+ registerCommandSuggest(ctx, cfg);
656
+
657
+ // 本地图片托管:配置了公网地址时,图片以本地文件 + 公网 URL 提供
658
+ setupLocalImageHost(ctx, cfg);
659
+
536
660
  try {
537
661
  model(ctx);
538
662
  if (cfg.debugMode) {
@@ -0,0 +1,222 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>猜你想问</title>
6
+ <style>
7
+ * { margin: 0; padding: 0; box-sizing: border-box; }
8
+
9
+ body {
10
+ font-family: 'Segoe UI', 'Microsoft YaHei', sans-serif;
11
+ background: #06060b;
12
+ -webkit-font-smoothing: antialiased;
13
+ }
14
+
15
+ #container {
16
+ width: 560px;
17
+ background: #0a0a12;
18
+ position: relative;
19
+ overflow: hidden;
20
+ padding: 22px 24px 24px;
21
+ }
22
+
23
+ .grid-bg {
24
+ position: absolute;
25
+ inset: 0;
26
+ background-image:
27
+ linear-gradient(rgba(0, 200, 255, 0.03) 1px, transparent 1px),
28
+ linear-gradient(90deg, rgba(0, 200, 255, 0.03) 1px, transparent 1px);
29
+ background-size: 26px 26px;
30
+ pointer-events: none;
31
+ z-index: 0;
32
+ }
33
+
34
+ .glow-top {
35
+ position: absolute;
36
+ top: -180px;
37
+ left: 50%;
38
+ transform: translateX(-50%);
39
+ width: 620px;
40
+ height: 360px;
41
+ background: radial-gradient(ellipse at center, rgba(0, 200, 255, 0.16) 0%, rgba(0, 200, 255, 0.04) 45%, transparent 70%);
42
+ pointer-events: none;
43
+ z-index: 0;
44
+ }
45
+
46
+ .header {
47
+ position: relative;
48
+ z-index: 2;
49
+ display: flex;
50
+ align-items: center;
51
+ gap: 13px;
52
+ padding-bottom: 16px;
53
+ border-bottom: 1px solid rgba(0, 200, 255, 0.1);
54
+ }
55
+
56
+ .header-badge {
57
+ width: 44px;
58
+ height: 44px;
59
+ flex-shrink: 0;
60
+ border-radius: 13px;
61
+ display: flex;
62
+ align-items: center;
63
+ justify-content: center;
64
+ font-size: 22px;
65
+ background: linear-gradient(145deg, rgba(0, 200, 255, 0.22), rgba(0, 200, 255, 0.06));
66
+ border: 1px solid rgba(0, 200, 255, 0.32);
67
+ box-shadow: 0 0 18px rgba(0, 200, 255, 0.22);
68
+ }
69
+
70
+ .header-title {
71
+ font-size: 19px;
72
+ font-weight: 800;
73
+ color: #eaf6ff;
74
+ letter-spacing: 1.2px;
75
+ }
76
+
77
+ .header-title em {
78
+ font-style: normal;
79
+ color: #00c8ff;
80
+ text-shadow: 0 0 14px rgba(0, 200, 255, 0.55);
81
+ }
82
+
83
+ .header-sub {
84
+ margin-top: 3px;
85
+ font-size: 10px;
86
+ letter-spacing: 3px;
87
+ color: #3d5570;
88
+ text-transform: uppercase;
89
+ }
90
+
91
+ .hint {
92
+ position: relative;
93
+ z-index: 2;
94
+ margin-top: 16px;
95
+ font-size: 12.5px;
96
+ color: #7d8b9c;
97
+ letter-spacing: 0.4px;
98
+ }
99
+
100
+ .list {
101
+ position: relative;
102
+ z-index: 2;
103
+ margin-top: 11px;
104
+ display: flex;
105
+ flex-direction: column;
106
+ gap: 9px;
107
+ }
108
+
109
+ .item {
110
+ position: relative;
111
+ display: flex;
112
+ align-items: center;
113
+ gap: 12px;
114
+ padding: 13px 15px;
115
+ border-radius: 12px;
116
+ background: rgba(0, 255, 157, 0.05);
117
+ border: 1px solid rgba(0, 255, 157, 0.26);
118
+ box-shadow: inset 0 0 20px rgba(0, 255, 157, 0.06);
119
+ }
120
+
121
+ .item::before {
122
+ content: '';
123
+ position: absolute;
124
+ left: 0;
125
+ top: 7px;
126
+ bottom: 7px;
127
+ width: 3px;
128
+ border-radius: 0 3px 3px 0;
129
+ background: linear-gradient(180deg, #00ff88, #00c8ff);
130
+ box-shadow: 0 0 10px rgba(0, 255, 136, 0.6);
131
+ }
132
+
133
+ .item-idx {
134
+ flex-shrink: 0;
135
+ width: 22px;
136
+ height: 22px;
137
+ border-radius: 7px;
138
+ display: flex;
139
+ align-items: center;
140
+ justify-content: center;
141
+ font-size: 11px;
142
+ font-weight: 800;
143
+ color: #04121a;
144
+ background: linear-gradient(135deg, #00ff88, #00c8ff);
145
+ }
146
+
147
+ .item-name {
148
+ font-size: 15.5px;
149
+ font-weight: 700;
150
+ color: #eaf6ff;
151
+ letter-spacing: 0.4px;
152
+ word-break: break-all;
153
+ }
154
+
155
+ .footer {
156
+ position: relative;
157
+ z-index: 2;
158
+ margin-top: 18px;
159
+ padding-top: 12px;
160
+ border-top: 1px solid rgba(0, 200, 255, 0.08);
161
+ font-size: 10px;
162
+ color: #3d5570;
163
+ letter-spacing: 2.4px;
164
+ text-align: center;
165
+ text-transform: uppercase;
166
+ }
167
+ </style>
168
+ </head>
169
+ <body>
170
+ <div id="container">
171
+ <div class="grid-bg"></div>
172
+ <div class="glow-top"></div>
173
+
174
+ <div class="header">
175
+ <div class="header-badge">💡</div>
176
+ <div>
177
+ <div class="header-title">猜你<em>想问</em></div>
178
+ <div class="header-sub">Did You Mean</div>
179
+ </div>
180
+ </div>
181
+
182
+ <div class="hint" id="hint">没有找到这条指令,你是不是想用:</div>
183
+ <div class="list" id="list"></div>
184
+
185
+ <div class="footer">TMP-BOT · Command Suggest</div>
186
+ </div>
187
+
188
+ <script>
189
+ function esc(s) {
190
+ return String(s == null ? '' : s)
191
+ .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
192
+ .replace(/"/g, '&quot;').replace(/'/g, '&#39;');
193
+ }
194
+
195
+ /**
196
+ * @param {{ names?: string[], input?: string }} data
197
+ */
198
+ function setData(data) {
199
+ data = data || {};
200
+ var names = Array.isArray(data.names) ? data.names : [];
201
+ var hintEl = document.getElementById('hint');
202
+ var listEl = document.getElementById('list');
203
+
204
+ if (data.input) {
205
+ hintEl.textContent = '没有找到「' + data.input + '」,你是不是想用:';
206
+ }
207
+
208
+ if (names.length === 0) {
209
+ listEl.innerHTML = '<div class="item"><div class="item-name">-</div></div>';
210
+ return;
211
+ }
212
+
213
+ listEl.innerHTML = names.map(function (name, i) {
214
+ return '<div class="item">' +
215
+ '<div class="item-idx">' + (i + 1) + '</div>' +
216
+ '<div class="item-name">' + esc(name) + '</div>' +
217
+ '</div>';
218
+ }).join('');
219
+ }
220
+ </script>
221
+ </body>
222
+ </html>