|
89 | 89 | container.addEventListener('click', (e) => { |
90 | 90 | const target = e.target; |
91 | 91 |
|
| 92 | + // 处理复制令牌按钮 |
| 93 | + if (target.classList.contains('btn-copy-token')) { |
| 94 | + const tokenHash = target.dataset.token; |
| 95 | + if (tokenHash) copyTokenToClipboard(tokenHash); |
| 96 | + return; |
| 97 | + } |
| 98 | + |
92 | 99 | // 处理编辑按钮 |
93 | 100 | if (target.classList.contains('btn-edit')) { |
94 | 101 | const row = target.closest('tr'); |
|
154 | 161 | <th style="text-align: center;">${t('tokens.table.streamAvg')}</th> |
155 | 162 | <th style="text-align: center;">${t('tokens.table.nonStreamAvg')}</th> |
156 | 163 | <th>${t('tokens.table.lastUsed')}</th> |
157 | | - <th style="width: 200px;">${t('tokens.table.actions')}</th> |
| 164 | + <th style="width: 260px;">${t('tokens.table.actions')}</th> |
158 | 165 | </tr> |
159 | 166 | </thead> |
160 | 167 | `; |
|
217 | 224 | const nonStreamAvgHtml = buildResponseTimeHtml(token.non_stream_avg_rt, token.non_stream_count); |
218 | 225 |
|
219 | 226 | // 使用模板引擎渲染 |
| 227 | + const maskedToken = token.token.length > 8 |
| 228 | + ? token.token.substring(0, 4) + '****' + token.token.slice(-4) |
| 229 | + : token.token; |
| 230 | + |
220 | 231 | return TemplateEngine.render('tpl-token-row', { |
221 | 232 | id: token.id, |
222 | 233 | description: token.description, |
223 | 234 | token: token.token, |
| 235 | + maskedToken: maskedToken, |
224 | 236 | statusClass: status.class, |
225 | 237 | createdAt: createdAt, |
226 | 238 | createdLabel: t('tokens.createdSuffix'), |
|
425 | 437 | const streamAvgHtml = buildResponseTimeHtml(token.stream_avg_ttfb, token.stream_count); |
426 | 438 | const nonStreamAvgHtml = buildResponseTimeHtml(token.non_stream_avg_rt, token.non_stream_count); |
427 | 439 |
|
| 440 | + const maskedToken = token.token.length > 8 |
| 441 | + ? token.token.substring(0, 4) + '****' + token.token.slice(-4) |
| 442 | + : token.token; |
| 443 | + |
428 | 444 | return ` |
429 | 445 | <tr data-token-id="${token.id}"> |
430 | 446 | <td style="font-weight: 500;">${escapeHtml(token.description)}</td> |
431 | 447 | <td> |
432 | | - <div><span class="token-display token-display-${status.class}">${escapeHtml(token.token)}</span></div> |
| 448 | + <div><span class="token-display token-display-${status.class}">${escapeHtml(maskedToken)}</span></div> |
433 | 449 | <div style="font-size: 12px; color: var(--neutral-500); margin-top: 4px;">${createdAt}${t('tokens.createdSuffix')} · ${expiresAt}</div> |
434 | 450 | </td> |
435 | 451 | <td style="text-align: center;">${callsHtml}</td> |
|
440 | 456 | <td style="text-align: center;">${streamAvgHtml}</td> |
441 | 457 | <td style="text-align: center;">${nonStreamAvgHtml}</td> |
442 | 458 | <td style="color: var(--neutral-600);">${lastUsed}</td> |
443 | | - <td> |
| 459 | + <td style="white-space: nowrap;"> |
| 460 | + <button class="btn-copy-token btn btn-secondary" style="padding: 4px 12px; font-size: 13px; margin-right: 4px;" data-token="${escapeHtml(token.token)}">${t('common.copy')}</button> |
444 | 461 | <button class="btn btn-secondary btn-edit" style="padding: 4px 12px; font-size: 13px; margin-right: 4px;">${t('common.edit')}</button> |
445 | 462 | <button class="btn btn-danger btn-delete" style="padding: 4px 12px; font-size: 13px;">${t('common.delete')}</button> |
446 | 463 | </td> |
|
520 | 537 | const textarea = document.getElementById('newTokenValue'); |
521 | 538 | textarea.select(); |
522 | 539 | document.execCommand('copy'); |
523 | | - |
| 540 | + |
524 | 541 | window.showNotification(t('tokens.msg.copySuccess'), 'success'); |
525 | 542 | } |
526 | 543 |
|
| 544 | + function copyTokenToClipboard(hash) { |
| 545 | + navigator.clipboard.writeText(hash).then(() => { |
| 546 | + window.showNotification(t('tokens.msg.copySuccess'), 'success'); |
| 547 | + }).catch(() => { |
| 548 | + // fallback |
| 549 | + const textarea = document.createElement('textarea'); |
| 550 | + textarea.value = hash; |
| 551 | + textarea.style.position = 'fixed'; |
| 552 | + textarea.style.opacity = '0'; |
| 553 | + document.body.appendChild(textarea); |
| 554 | + textarea.select(); |
| 555 | + document.execCommand('copy'); |
| 556 | + document.body.removeChild(textarea); |
| 557 | + window.showNotification(t('tokens.msg.copySuccess'), 'success'); |
| 558 | + }); |
| 559 | + } |
| 560 | + |
527 | 561 | function closeTokenResultModal() { |
528 | 562 | document.getElementById('tokenResultModal').style.display = 'none'; |
529 | 563 | document.getElementById('newTokenValue').value = ''; |
|
0 commit comments