feat: 工具卡片精简、移除token统计、消息时间显示
1. 聊天消息工具卡片只显示工具名+调用内容,不显示执行结果(工作空间工具页签已有完整结果) 2. 移除所有token统计相关代码:顶栏统计、消息内token/耗时显示、历史记录中的token显示 3. 每条消息显示具体时间(年月日 时分秒),formatTime 已含秒数
This commit is contained in:
@@ -184,13 +184,8 @@ export function appendMessageDOM(msg: ChatMessage, index: number): void {
|
|||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const stats: string[] = [];
|
if (msg.timestamp) {
|
||||||
const statClasses: string[] = [];
|
contentHtml += `<div class="msg-stats"><span>${formatTime(msg.timestamp)}</span></div>`;
|
||||||
if (msg.timestamp) { stats.push(formatTime(msg.timestamp)); statClasses.push(''); }
|
|
||||||
if (msg.eval_count != null && msg.eval_count > 0) { stats.push(`${msg.eval_count} tokens`); statClasses.push('stat-tokens'); }
|
|
||||||
if (msg.total_duration != null && msg.total_duration > 0) { stats.push(`${(msg.total_duration / 1e9).toFixed(1)}s`); statClasses.push('stat-duration'); }
|
|
||||||
if (stats.length > 0) {
|
|
||||||
contentHtml += `<div class="msg-stats">${stats.map((s, i) => `<span${statClasses[i] ? ` class="${statClasses[i]}"` : ''}>${s}</span>`).join(' · ')}</div>`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
div.innerHTML = `
|
div.innerHTML = `
|
||||||
@@ -246,96 +241,6 @@ function renderToolCallCard(tc: ToolCallRecord): string {
|
|||||||
if (args.selector) paramsHtml += `<div class="tool-param">选择器: <code>${escapeHtml(String(args.selector))}</code></div>`;
|
if (args.selector) paramsHtml += `<div class="tool-param">选择器: <code>${escapeHtml(String(args.selector))}</code></div>`;
|
||||||
if (args.action) paramsHtml += `<div class="tool-param">操作: <code>${escapeHtml(String(args.action))}</code></div>`;
|
if (args.action) paramsHtml += `<div class="tool-param">操作: <code>${escapeHtml(String(args.action))}</code></div>`;
|
||||||
|
|
||||||
let resultHtml = '';
|
|
||||||
if (tc.result) {
|
|
||||||
if (tc.result.success) {
|
|
||||||
const r = tc.result as Record<string, unknown>;
|
|
||||||
if (tc.name === 'read_file' && r.content) {
|
|
||||||
const content = String(r.content);
|
|
||||||
const lines = content.split('\n');
|
|
||||||
const preview = lines.slice(0, 20).join('\n');
|
|
||||||
resultHtml = `<pre class="tool-result-content">${escapeHtml(preview)}${lines.length > 20 ? `\n... (共 ${r.lines || lines.length} 行)` : ''}</pre>`;
|
|
||||||
resultHtml += `<div class="tool-result-meta">📄 ${(r.size as number || 0) > 0 ? formatFileSize(r.size as number) : ''} · ${r.lines || '?'} 行</div>`;
|
|
||||||
} else if (tc.name === 'list_directory' && r.entries) {
|
|
||||||
const entries = r.entries as Array<{ name: string; type: string; size: number | null }>;
|
|
||||||
resultHtml = entries.slice(0, 30).map(e =>
|
|
||||||
`<div class="tool-result-entry">${e.type === 'directory' ? '📁' : '📄'} ${escapeHtml(e.name)}${e.size != null ? ` (${formatFileSize(e.size)})` : ''}</div>`
|
|
||||||
).join('');
|
|
||||||
if (entries.length > 30) resultHtml += `<div class="tool-result-entry">... 共 ${(r.total as number) || entries.length} 项</div>`;
|
|
||||||
} else if (tc.name === 'search_files' && r.results) {
|
|
||||||
const results = r.results as Array<{ path: string; matches: Array<{ line: number; text: string }> }>;
|
|
||||||
resultHtml = results.slice(0, 10).map(file =>
|
|
||||||
file.matches.slice(0, 3).map(m =>
|
|
||||||
`<div class="tool-result-entry">📄 ${escapeHtml(file.path)}:${m.line} ${escapeHtml(m.text).slice(0, 80)}</div>`
|
|
||||||
).join('')
|
|
||||||
).join('');
|
|
||||||
resultHtml += `<div class="tool-result-meta">共 ${r.total_files || '?'} 个文件,${r.total_matches || '?'} 处匹配</div>`;
|
|
||||||
} else if (tc.name === 'write_file') {
|
|
||||||
resultHtml = `<div class="tool-result-entry">✅ 已写入 ${escapeHtml(String(r.path || ''))} (${formatFileSize(r.bytesWritten as number || 0)})</div>`;
|
|
||||||
} else if (tc.name === 'create_directory') {
|
|
||||||
resultHtml = `<div class="tool-result-entry">✅ 已创建 ${escapeHtml(String(r.path || ''))}</div>`;
|
|
||||||
} else if (tc.name === 'delete_file') {
|
|
||||||
resultHtml = `<div class="tool-result-entry">✅ 已删除 ${escapeHtml(String(r.path || ''))}</div>`;
|
|
||||||
} else if (tc.name === 'run_command') {
|
|
||||||
const stdout = String(r.stdout || '').slice(0, 1000);
|
|
||||||
const stderr = String(r.stderr || '').slice(0, 500);
|
|
||||||
if (stdout) resultHtml += `<pre class="tool-result-content">${escapeHtml(stdout)}</pre>`;
|
|
||||||
if (stderr) resultHtml += `<pre class="tool-result-content tool-result-stderr">${escapeHtml(stderr)}</pre>`;
|
|
||||||
resultHtml += `<div class="tool-result-meta">exit: ${r.exitCode || 0} · ${r.duration || 0}ms</div>`;
|
|
||||||
} else if (tc.name === 'web_search' && r.results) {
|
|
||||||
const results = r.results as Array<{ title: string; url: string; snippet: string }>;
|
|
||||||
resultHtml = results.slice(0, 5).map((item, i) =>
|
|
||||||
`<div class="tool-result-entry">[${i + 1}] ${escapeHtml(item.title)}<br><code>${escapeHtml(item.url)}</code></div>`
|
|
||||||
).join('');
|
|
||||||
resultHtml += `<div class="tool-result-meta">共 ${r.total || results.length} 条结果</div>`;
|
|
||||||
} else if (tc.name === 'web_fetch') {
|
|
||||||
const content = String(r.content || '').slice(0, 500);
|
|
||||||
resultHtml = `<pre class="tool-result-content">${escapeHtml(content)}${(r.content as string || '').length > 500 ? '\n...' : ''}</pre>`;
|
|
||||||
resultHtml += `<div class="tool-result-meta">${r.length || content.length} chars</div>`;
|
|
||||||
} else if (tc.name === 'git' && r.action) {
|
|
||||||
if (r.commits) {
|
|
||||||
resultHtml = (r.commits as Array<{ hash: string; message: string }>).slice(0, 10).map(c =>
|
|
||||||
`<div class="tool-result-entry"><code>${escapeHtml(c.hash)}</code> ${escapeHtml(c.message)}</div>`
|
|
||||||
).join('');
|
|
||||||
} else if (r.branches) {
|
|
||||||
resultHtml = (r.branches as Array<{ name: string; current: boolean }>).map(b =>
|
|
||||||
`<div class="tool-result-entry">${b.current ? '→ ' : ''}${escapeHtml(b.name)}</div>`
|
|
||||||
).join('');
|
|
||||||
} else {
|
|
||||||
resultHtml = `<div class="tool-result-entry">✅ ${r.action} 完成</div>`;
|
|
||||||
}
|
|
||||||
} else if (tc.name === 'memory_search' && r.results) {
|
|
||||||
const results = r.results as Array<{ type: string; content: string }>;
|
|
||||||
resultHtml = results.slice(0, 5).map(m =>
|
|
||||||
`<div class="tool-result-entry">${m.type === 'rule' ? '📏' : m.type === 'preference' ? '⚙️' : '📌'} ${escapeHtml(m.content).slice(0, 80)}</div>`
|
|
||||||
).join('');
|
|
||||||
} else if (tc.name === 'session_list' && r.sessions) {
|
|
||||||
const sessions = r.sessions as Array<{ title: string; messageCount: number }>;
|
|
||||||
resultHtml = sessions.slice(0, 5).map(s =>
|
|
||||||
`<div class="tool-result-entry">💬 ${escapeHtml(s.title)}</div>`
|
|
||||||
).join('');
|
|
||||||
} else if (tc.name?.startsWith('browser_')) {
|
|
||||||
if (tc.name === 'browser_screenshot' && r.png) {
|
|
||||||
resultHtml = `<div class="tool-result-entry">📸 截图已生成</div>`;
|
|
||||||
} else if (tc.name === 'browser_extract') {
|
|
||||||
const text = String(r.text || '').slice(0, 300);
|
|
||||||
resultHtml = `<pre class="tool-result-content">${escapeHtml(text)}</pre>`;
|
|
||||||
resultHtml += `<div class="tool-result-meta">链接: ${(r.links as unknown[])?.length || 0} 个</div>`;
|
|
||||||
} else if (r.title) {
|
|
||||||
resultHtml = `<div class="tool-result-entry">📄 ${escapeHtml(r.title)}</div>`;
|
|
||||||
} else if (r.result) {
|
|
||||||
resultHtml = `<pre class="tool-result-content">${escapeHtml(String(r.result).slice(0, 300))}</pre>`;
|
|
||||||
} else {
|
|
||||||
resultHtml = `<div class="tool-result-entry">✅ 完成</div>`;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
resultHtml = `<div class="tool-result-entry">✅ 操作成功</div>`;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
resultHtml = `<div class="tool-result-entry tool-result-error">❌ ${escapeHtml(tc.result.error || '未知错误')}</div>`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return `<div class="tool-call-card tool-call-${tc.status}">
|
return `<div class="tool-call-card tool-call-${tc.status}">
|
||||||
<div class="tool-call-header">
|
<div class="tool-call-header">
|
||||||
<span class="tool-call-icon">${icon}</span>
|
<span class="tool-call-icon">${icon}</span>
|
||||||
@@ -343,7 +248,6 @@ function renderToolCallCard(tc: ToolCallRecord): string {
|
|||||||
<span class="tool-call-status">${status}</span>
|
<span class="tool-call-status">${status}</span>
|
||||||
</div>
|
</div>
|
||||||
${paramsHtml ? `<div class="tool-call-params">${paramsHtml}</div>` : ''}
|
${paramsHtml ? `<div class="tool-call-params">${paramsHtml}</div>` : ''}
|
||||||
${resultHtml ? `<div class="tool-call-result">${resultHtml}</div>` : ''}
|
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -403,19 +307,6 @@ export function updateLastAssistantMessage(content: string, think: string | null
|
|||||||
thinkBlock.querySelector('pre')!.textContent = think;
|
thinkBlock.querySelector('pre')!.textContent = think;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stats) {
|
|
||||||
let statsDiv = lastMsg.querySelector('.msg-stats');
|
|
||||||
if (!statsDiv) {
|
|
||||||
statsDiv = document.createElement('div');
|
|
||||||
statsDiv.className = 'msg-stats';
|
|
||||||
lastMsg.querySelector('.msg-body')!.appendChild(statsDiv);
|
|
||||||
}
|
|
||||||
const parts: string[] = [];
|
|
||||||
if (stats.eval_count != null && stats.eval_count > 0) parts.push(`<span class="stat-tokens">${stats.eval_count} tokens</span>`);
|
|
||||||
if (stats.total_duration != null && stats.total_duration > 0) parts.push(`<span class="stat-duration">${(stats.total_duration / 1e9).toFixed(1)}s</span>`);
|
|
||||||
if (parts.length) statsDiv.innerHTML = parts.join(' · ');
|
|
||||||
}
|
|
||||||
|
|
||||||
scrollToBottom();
|
scrollToBottom();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -507,18 +398,6 @@ export function enableAutoScroll(): void {
|
|||||||
if (scrollBtnEl) scrollBtnEl.style.display = 'none';
|
if (scrollBtnEl) scrollBtnEl.style.display = 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 统计当前会话总 token 消耗并更新 header 显示 */
|
|
||||||
export function updateTotalTokens(): void {
|
|
||||||
const session = state.get<ChatSession | null>(KEYS.CURRENT_SESSION);
|
|
||||||
const el = document.querySelector('#totalTokensValue');
|
|
||||||
if (!el) return;
|
|
||||||
if (!session) { el.textContent = '0'; return; }
|
|
||||||
const total = session.messages
|
|
||||||
.filter(m => m.role === 'assistant' && m.eval_count)
|
|
||||||
.reduce((sum, m) => sum + (m.eval_count || 0), 0);
|
|
||||||
el.textContent = total > 0 ? total.toLocaleString() : '0';
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── 导出功能 ──
|
// ── 导出功能 ──
|
||||||
|
|
||||||
async function nativeSaveFile(defaultName: string, content: string): Promise<void> {
|
async function nativeSaveFile(defaultName: string, content: string): Promise<void> {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
import { state, KEYS } from '../state/state.js';
|
import { state, KEYS } from '../state/state.js';
|
||||||
import { debounce, escapeHtml, formatTime } from '../utils/utils.js';
|
import { debounce, escapeHtml, formatTime } from '../utils/utils.js';
|
||||||
import { exportAsMarkdown, exportAsHtml, exportAsTxt, renderMessages, clearMessages, updateTotalTokens } from './chat-area.js';
|
import { exportAsMarkdown, exportAsHtml, exportAsTxt, renderMessages, clearMessages } from './chat-area.js';
|
||||||
import { clearToolCardsExternal, clearTerminalExternal } from './workspace-panel.js';
|
import { clearToolCardsExternal, clearTerminalExternal } from './workspace-panel.js';
|
||||||
import { showConfirm } from './prompt-modal.js';
|
import { showConfirm } from './prompt-modal.js';
|
||||||
import { ChatDB } from '../db/chat-db.js';
|
import { ChatDB } from '../db/chat-db.js';
|
||||||
@@ -186,7 +186,6 @@ async function loadHistorySession(sessionId: string): Promise<void> {
|
|||||||
|
|
||||||
clearMessages();
|
clearMessages();
|
||||||
renderMessages();
|
renderMessages();
|
||||||
updateTotalTokens();
|
|
||||||
closeHistoryModal();
|
closeHistoryModal();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { detectLanguage, truncate, escapeHtml, formatSize } from '../utils/utils
|
|||||||
import { getSelectedModel, isThinkEnabled, isVisionAvailable, isToolCallingSupported } from './model-bar.js';
|
import { getSelectedModel, isThinkEnabled, isVisionAvailable, isToolCallingSupported } from './model-bar.js';
|
||||||
import {
|
import {
|
||||||
renderMessages, clearMessagesDOM, appendAssistantPlaceholder, appendSystemMessage,
|
renderMessages, clearMessagesDOM, appendAssistantPlaceholder, appendSystemMessage,
|
||||||
updateLastAssistantMessage, clearMessages, safeMarkdown, enableAutoScroll, updateTotalTokens,
|
updateLastAssistantMessage, clearMessages, safeMarkdown, enableAutoScroll,
|
||||||
appendToolCallCardToPlaceholder, updateToolCallCardInPlaceholder, resetCurrentPlaceholder
|
appendToolCallCardToPlaceholder, updateToolCallCardInPlaceholder, resetCurrentPlaceholder
|
||||||
} from './chat-area.js';
|
} from './chat-area.js';
|
||||||
import { showToast } from './toast.js';
|
import { showToast } from './toast.js';
|
||||||
@@ -371,7 +371,6 @@ async function handleRetry(): Promise<void> {
|
|||||||
}
|
}
|
||||||
renderMessages();
|
renderMessages();
|
||||||
await saveCurrentSession();
|
await saveCurrentSession();
|
||||||
updateTotalTokens();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -424,7 +423,6 @@ async function handleUndo(): Promise<void> {
|
|||||||
|
|
||||||
clearMessagesDOM();
|
clearMessagesDOM();
|
||||||
renderMessages();
|
renderMessages();
|
||||||
updateTotalTokens();
|
|
||||||
showToast('已撤销上一轮对话', 'success');
|
showToast('已撤销上一轮对话', 'success');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -503,7 +501,6 @@ async function handleCompress(): Promise<void> {
|
|||||||
|
|
||||||
clearMessagesDOM();
|
clearMessagesDOM();
|
||||||
renderMessages();
|
renderMessages();
|
||||||
updateTotalTokens();
|
|
||||||
|
|
||||||
logInfo(`上下文压缩完成: ${middle.length} 条 → 1 条摘要`);
|
logInfo(`上下文压缩完成: ${middle.length} 条 → 1 条摘要`);
|
||||||
showToast(`已压缩 ${middle.length} 条消息为摘要`, 'success');
|
showToast(`已压缩 ${middle.length} 条消息为摘要`, 'success');
|
||||||
@@ -754,7 +751,6 @@ export async function sendMessage(): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await saveCurrentSession();
|
await saveCurrentSession();
|
||||||
updateTotalTokens();
|
|
||||||
|
|
||||||
// ── 自动提取记忆(非阻塞)──
|
// ── 自动提取记忆(非阻塞)──
|
||||||
if (isMemoryEnabled() && freshSession.messages.length >= 10) {
|
if (isMemoryEnabled() && freshSession.messages.length >= 10) {
|
||||||
@@ -1096,7 +1092,6 @@ async function sendMessageWithAgentLoop(text: string, currentSession: ChatSessio
|
|||||||
}
|
}
|
||||||
renderMessages();
|
renderMessages();
|
||||||
await saveCurrentSession();
|
await saveCurrentSession();
|
||||||
updateTotalTokens();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@@ -35,7 +35,6 @@
|
|||||||
<line x1="12" y1="17" x2="12.01" y2="17"/>
|
<line x1="12" y1="17" x2="12.01" y2="17"/>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<span class="total-tokens" id="totalTokens" title="当前会话总 token 消耗"><svg class="token-icon" viewBox="0 0 16 16" fill="none"><circle cx="8" cy="8" r="7" stroke="currentColor" stroke-width="1.2"/><path d="M5.2 5h5.6v1.5H9.2v6.2H6.8V6.5H5.2V5z" fill="currentColor"/></svg> <span id="totalTokensValue">0</span></span>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="conn-status pending" id="connStatus" title="连接状态">
|
<div class="conn-status pending" id="connStatus" title="连接状态">
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import { initToast, showToast } from './components/toast.js';
|
|||||||
import { initLightbox, closeLightbox } from './components/lightbox.js';
|
import { initLightbox, closeLightbox } from './components/lightbox.js';
|
||||||
import { initHeader, checkConnection } from './components/header.js';
|
import { initHeader, checkConnection } from './components/header.js';
|
||||||
import { initModelBar, loadModels, setSelectedModel } from './components/model-bar.js';
|
import { initModelBar, loadModels, setSelectedModel } from './components/model-bar.js';
|
||||||
import { initChatArea, renderMessages, clearMessages, enableAutoScroll, updateTotalTokens } from './components/chat-area.js';
|
import { initChatArea, renderMessages, clearMessages, enableAutoScroll } from './components/chat-area.js';
|
||||||
import { initInputArea } from './components/input-area.js';
|
import { initInputArea } from './components/input-area.js';
|
||||||
import { initSettingsModal, closeSettingsModal } from './components/settings-modal.js';
|
import { initSettingsModal, closeSettingsModal } from './components/settings-modal.js';
|
||||||
import { initHistoryModal, closeHistoryModal } from './components/history-modal.js';
|
import { initHistoryModal, closeHistoryModal } from './components/history-modal.js';
|
||||||
@@ -279,7 +279,6 @@ async function startNewSession(): Promise<void> {
|
|||||||
clearMessages();
|
clearMessages();
|
||||||
enableAutoScroll();
|
enableAutoScroll();
|
||||||
renderMessages();
|
renderMessages();
|
||||||
updateTotalTokens();
|
|
||||||
|
|
||||||
// ── 重置工作空间到空闲状态 ──
|
// ── 重置工作空间到空闲状态 ──
|
||||||
clearToolCardsExternal();
|
clearToolCardsExternal();
|
||||||
@@ -403,7 +402,6 @@ async function init(): Promise<void> {
|
|||||||
state.set('heartbeatInterval', heartbeatInterval);
|
state.set('heartbeatInterval', heartbeatInterval);
|
||||||
|
|
||||||
(document.querySelector('#chatInput') as HTMLElement)?.focus();
|
(document.querySelector('#chatInput') as HTMLElement)?.focus();
|
||||||
updateTotalTokens();
|
|
||||||
|
|
||||||
logSuccess('应用初始化完成');
|
logSuccess('应用初始化完成');
|
||||||
|
|
||||||
|
|||||||
@@ -374,28 +374,6 @@ html, body {
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.total-tokens {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 4px;
|
|
||||||
margin-left: 8px;
|
|
||||||
padding: 3px 10px;
|
|
||||||
font-size: 11px;
|
|
||||||
font-weight: 500;
|
|
||||||
color: var(--caution);
|
|
||||||
background: var(--caution-bg);
|
|
||||||
border: 1px solid rgba(212, 160, 60, 0.15);
|
|
||||||
border-radius: 20px;
|
|
||||||
white-space: nowrap;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.token-icon {
|
|
||||||
width: 14px;
|
|
||||||
height: 14px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
color: var(--caution);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ═══ 模型选择栏 ═══ */
|
/* ═══ 模型选择栏 ═══ */
|
||||||
.model-bar {
|
.model-bar {
|
||||||
@@ -1024,14 +1002,6 @@ html, body {
|
|||||||
color: var(--text-tertiary);
|
color: var(--text-tertiary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.msg-stats .stat-tokens {
|
|
||||||
color: var(--caution);
|
|
||||||
}
|
|
||||||
|
|
||||||
.msg-stats .stat-duration {
|
|
||||||
color: var(--accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ═══ 输入区域 ═══ */
|
/* ═══ 输入区域 ═══ */
|
||||||
.input-area {
|
.input-area {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export function generateId(): string {
|
|||||||
export function formatTime(ts: number): string {
|
export function formatTime(ts: number): string {
|
||||||
const d = new Date(ts);
|
const d = new Date(ts);
|
||||||
const pad = (n: number): string => String(n).padStart(2, '0');
|
const pad = (n: number): string => String(n).padStart(2, '0');
|
||||||
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
|
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 截断文本 */
|
/** 截断文本 */
|
||||||
|
|||||||
Reference in New Issue
Block a user