@composy/log-viewer-core 0.0.1 → 0.0.2
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.
- package/CHANGELOG.md +52 -0
- package/README.md +175 -37
- package/dist/index.js +614 -299
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +548 -1
- package/dist/index.min.js.map +1 -0
- package/es/.dts-output-manifest.json +1 -0
- package/es/.dts.tsbuildinfo +1 -0
- package/es/core/EventEmitter.d.ts +0 -1
- package/es/core/EventEmitter.js +13 -9
- package/es/core/EventEmitter.js.map +1 -1
- package/es/core/LogViewer.d.ts +14 -11
- package/es/core/LogViewer.js +243 -64
- package/es/core/LogViewer.js.map +1 -1
- package/es/filters/FilterEngine.d.ts +0 -1
- package/es/filters/FilterEngine.js +31 -26
- package/es/filters/FilterEngine.js.map +1 -1
- package/es/index.d.ts +7 -7
- package/es/index.js +3 -3
- package/es/index.js.map +1 -1
- package/es/renderers/DiffRenderer.d.ts +0 -1
- package/es/renderers/DiffRenderer.js +13 -3
- package/es/renderers/DiffRenderer.js.map +1 -1
- package/es/renderers/TimelineRenderer.d.ts +7 -3
- package/es/renderers/TimelineRenderer.js +56 -22
- package/es/renderers/TimelineRenderer.js.map +1 -1
- package/es/styles/index.d.ts +1 -5
- package/es/styles/index.js +167 -131
- package/es/styles/index.js.map +1 -1
- package/es/types/index.d.ts +4 -1
- package/es/types/index.js +1 -1
- package/es/types/index.js.map +1 -1
- package/es/utils/index.d.ts +1 -2
- package/es/utils/index.js +54 -6
- package/es/utils/index.js.map +1 -1
- package/esm/core/EventEmitter.js +13 -9
- package/esm/core/EventEmitter.js.map +1 -1
- package/esm/core/LogViewer.js +243 -64
- package/esm/core/LogViewer.js.map +1 -1
- package/esm/filters/FilterEngine.js +31 -26
- package/esm/filters/FilterEngine.js.map +1 -1
- package/esm/index.js +2 -2
- package/esm/index.js.map +1 -1
- package/esm/renderers/DiffRenderer.js +13 -3
- package/esm/renderers/DiffRenderer.js.map +1 -1
- package/esm/renderers/TimelineRenderer.js +56 -22
- package/esm/renderers/TimelineRenderer.js.map +1 -1
- package/esm/styles/index.js +167 -131
- package/esm/styles/index.js.map +1 -1
- package/esm/types/index.js +1 -1
- package/esm/types/index.js.map +1 -1
- package/esm/utils/index.js +54 -6
- package/esm/utils/index.js.map +1 -1
- package/lib/core/EventEmitter.cjs +13 -9
- package/lib/core/EventEmitter.cjs.map +1 -1
- package/{esm/core/EventEmitter.d.ts → lib/core/EventEmitter.d.cts} +0 -1
- package/lib/core/LogViewer.cjs +247 -68
- package/lib/core/LogViewer.cjs.map +1 -1
- package/{esm/core/LogViewer.d.ts → lib/core/LogViewer.d.cts} +14 -11
- package/lib/filters/FilterEngine.cjs +31 -26
- package/lib/filters/FilterEngine.cjs.map +1 -1
- package/lib/filters/{FilterEngine.d.ts → FilterEngine.d.cts} +0 -1
- package/lib/index.cjs +3 -3
- package/lib/index.cjs.map +1 -1
- package/{esm/index.d.ts → lib/index.d.cts} +7 -7
- package/lib/renderers/DiffRenderer.cjs +13 -3
- package/lib/renderers/DiffRenderer.cjs.map +1 -1
- package/lib/renderers/{DiffRenderer.d.ts → DiffRenderer.d.cts} +0 -1
- package/lib/renderers/TimelineRenderer.cjs +55 -21
- package/lib/renderers/TimelineRenderer.cjs.map +1 -1
- package/lib/renderers/{TimelineRenderer.d.ts → TimelineRenderer.d.cts} +7 -3
- package/lib/styles/index.cjs +167 -131
- package/lib/styles/index.cjs.map +1 -1
- package/{esm/styles/index.d.ts → lib/styles/index.d.cts} +1 -5
- package/lib/types/index.cjs +1 -1
- package/lib/types/index.cjs.map +1 -1
- package/{esm/types/index.d.ts → lib/types/index.d.cts} +4 -1
- package/lib/utils/index.cjs +54 -6
- package/lib/utils/index.cjs.map +1 -1
- package/{esm/utils/index.d.ts → lib/utils/index.d.cts} +1 -2
- package/package.json +15 -9
- package/es/core/EventEmitter.d.ts.map +0 -1
- package/es/core/LogViewer.d.ts.map +0 -1
- package/es/filters/FilterEngine.d.ts.map +0 -1
- package/es/index.d.ts.map +0 -1
- package/es/renderers/DiffRenderer.d.ts.map +0 -1
- package/es/renderers/TimelineRenderer.d.ts.map +0 -1
- package/es/styles/index.d.ts.map +0 -1
- package/es/types/index.d.ts.map +0 -1
- package/es/utils/index.d.ts.map +0 -1
- package/esm/core/EventEmitter.d.ts.map +0 -1
- package/esm/core/LogViewer.d.ts.map +0 -1
- package/esm/filters/FilterEngine.d.ts +0 -26
- package/esm/filters/FilterEngine.d.ts.map +0 -1
- package/esm/index.d.ts.map +0 -1
- package/esm/renderers/DiffRenderer.d.ts +0 -18
- package/esm/renderers/DiffRenderer.d.ts.map +0 -1
- package/esm/renderers/TimelineRenderer.d.ts +0 -22
- package/esm/renderers/TimelineRenderer.d.ts.map +0 -1
- package/esm/styles/index.d.ts.map +0 -1
- package/esm/types/index.d.ts.map +0 -1
- package/esm/utils/index.d.ts.map +0 -1
- package/lib/core/EventEmitter.d.ts +0 -11
- package/lib/core/LogViewer.d.ts +0 -46
- package/lib/index.d.ts +0 -26
- package/lib/styles/index.d.ts +0 -9
- package/lib/types/index.d.ts +0 -113
- package/lib/utils/index.d.ts +0 -18
package/dist/index.min.js
CHANGED
|
@@ -1 +1,548 @@
|
|
|
1
|
-
(function(global,factory){typeof exports==="object"&&typeof module!=="undefined"?factory(exports):typeof define==="function"&&define.amd?define(["exports"],factory):(global=typeof globalThis!=="undefined"?globalThis:global||self,factory(global.LDesignLogViewer={}));})(this,function(exports){Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const ACTION_LABELS={create:"新增",update:"修改",delete:"删除",query:"查询",login:"登录",logout:"登出",export:"导出",import:"导入",approve:"审批",reject:"驳回",other:"其他"};const ACTION_COLORS={create:"#52c41a",update:"#1890ff",delete:"#ff4d4f",query:"#8c8c8c",login:"#722ed1",logout:"#eb2f96",export:"#fa8c16",import:"#13c2c2",approve:"#52c41a",reject:"#ff4d4f",other:"#d9d9d9"};var EventEmitter=class{constructor(){this.listeners=new Map();}on(event,callback){if(!this.listeners.has(event))this.listeners.set(event,new Set());this.listeners.get(event).add(callback);return()=>this.off(event,callback);}off(event,callback){this.listeners.get(event)?.delete(callback);}emit(event,...args){this.listeners.get(event)?.forEach((cb)=>{try{cb(...args);}catch(e){console.error(`[LogViewer]Event handler error(${event}):`,e);}});}removeAllListeners(event){if(event)this.listeners.delete(event);else this.listeners.clear();}};var FilterEngine=class{filter(entries,config){let result=entries;if(config.user){const keyword=config.user.toLowerCase();result=result.filter((e)=>e.user.toLowerCase().includes(keyword)||e.userId&&e.userId.toLowerCase().includes(keyword));}if(config.action){const actions=Array.isArray(config.action)?config.action:[config.action];result=result.filter((e)=>actions.includes(e.action));}if(config.module){const mod=config.module.toLowerCase();result=result.filter((e)=>e.module.toLowerCase().includes(mod));}if(config.ip){const ip=config.ip.toLowerCase();result=result.filter((e)=>e.ip&&e.ip.toLowerCase().includes(ip));}if(config.timeRange){const[start,end]=config.timeRange;const startTime=new Date(start).getTime();const endTime=new Date(end).getTime();result=result.filter((e)=>{const t=new Date(e.timestamp).getTime();return t>=startTime&&t<=endTime;});}if(config.keyword){const kw=config.keyword.toLowerCase();result=result.filter((e)=>{return[e.user,e.action,e.module,e.detail||"",e.ip||"",e.id].some((t)=>t.toLowerCase().includes(kw));});}return result;}getUniqueUsers(entries){return[...new Set(entries.map((e)=>e.user))].sort();}getUniqueModules(entries){return[...new Set(entries.map((e)=>e.module))].sort();}getUniqueActions(entries){return[...new Set(entries.map((e)=>e.action))].sort();}};let _uid=0;function uid(prefix="lv"){return `${prefix}-${++_uid}-${Math.random().toString(36).slice(2,8)}`;}function formatDate(value,fmt="YYYY-MM-DD HH:mm:ss"){const d=value instanceof Date?value:new Date(value);if(isNaN(d.getTime()))return String(value);const pad=(n)=>String(n).padStart(2,"0");const year=String(d.getFullYear());const month=pad(d.getMonth()+1);const day=pad(d.getDate());const hours=pad(d.getHours());const minutes=pad(d.getMinutes());const seconds=pad(d.getSeconds());return fmt.replace("YYYY",year).replace("MM",month).replace("DD",day).replace("HH",hours).replace("mm",minutes).replace("ss",seconds);}function formatRelativeTime(value){const d=value instanceof Date?value:new Date(value);const diff=Date.now()-d.getTime();if(diff<6e4)return "刚刚";if(diff<36e5)return `${Math.floor(diff/6e4)}\u5206\u949F\u524D`;if(diff<864e5)return `${Math.floor(diff/36e5)}\u5C0F\u65F6\u524D`;if(diff<6048e5)return `${Math.floor(diff/864e5)}\u5929\u524D`;return formatDate(d);}function escapeHtml(str){const map={"&":"&","<":"<",">":">","\"":""","'":"'"};return str.replace(/[&<>"']/g,(c)=>map[c]||c);}function debounce(fn,delay){let timer=null;return((...args)=>{if(timer)clearTimeout(timer);timer=setTimeout(()=>fn(...args),delay);});}function deepClone(obj){if(obj===null||typeof obj!=="object")return obj;try{return JSON.parse(JSON.stringify(obj));}catch{return obj;}}function formatValue(val){if(val===null||val===void 0)return "-";if(typeof val==="object")try{return JSON.stringify(val,null,2);}catch{return String(val);}return String(val);}const ANOMALY_LABELS={"off-hours":"非工作时间",sensitive:"敏感操作","high-frequency":"高频操作",none:""};var TimelineRenderer=class{render(entries,expandedIds){const container=document.createElement("div");container.className="lv-timeline";if(entries.length===0){container.innerHTML=`<div class="lv-empty"><div class="lv-empty-icon">--</div><div>\u6682\u65E0\u65E5\u5FD7\u8BB0\u5F55</div></div>`;container.className="";return container;}for(const entry of entries)container.appendChild(this.renderItem(entry,expandedIds.has(entry.id)));return container;}renderItem(entry,expanded){const item=document.createElement("div");item.className="lv-timeline-item";item.setAttribute("data-id",entry.id);const isAnomaly=entry.anomaly&&entry.anomaly!=="none";const dot=document.createElement("div");dot.className=`lv-timeline-dot${isAnomaly?" lv-timeline-dot--anomaly":""}`;item.appendChild(dot);const card=document.createElement("div");card.className=`lv-timeline-card${isAnomaly?" lv-timeline-card--anomaly":""}`;const header=document.createElement("div");header.className="lv-timeline-header";const time=document.createElement("span");time.className="lv-timeline-time";time.textContent=formatDate(entry.timestamp);header.appendChild(time);const user=document.createElement("span");user.className="lv-timeline-user";user.textContent=entry.user;header.appendChild(user);const actionTag=document.createElement("span");actionTag.className="lv-action-tag";actionTag.textContent=ACTION_LABELS[entry.action]||entry.action;actionTag.style.backgroundColor=ACTION_COLORS[entry.action]||"#8c8c8c";header.appendChild(actionTag);const moduleBadge=document.createElement("span");moduleBadge.className="lv-timeline-module";moduleBadge.textContent=entry.module;header.appendChild(moduleBadge);if(entry.ip){const ip=document.createElement("span");ip.className="lv-timeline-ip";ip.textContent=entry.ip;header.appendChild(ip);}if(isAnomaly){const badge=document.createElement("span");badge.className="lv-anomaly-badge";badge.textContent=ANOMALY_LABELS[entry.anomaly];header.appendChild(badge);}card.appendChild(header);if(entry.detail){const detail=document.createElement("div");detail.className="lv-timeline-detail";detail.textContent=entry.detail;card.appendChild(detail);}if(entry.changes&&entry.changes.length>0){const hint=document.createElement("div");hint.className="lv-timeline-expand-hint";hint.textContent=expanded?"[收起变更详情]":`[\u67E5\u770B\u53D8\u66F4\u8BE6\u60C5(${entry.changes.length}\u4E2A\u5B57\u6BB5)]`;card.appendChild(hint);}item.appendChild(card);return item;}renderPagination(page,pageSize,total){const container=document.createElement("div");container.className="lv-pagination";const totalPages=Math.ceil(total/pageSize);if(totalPages<=1)return container;const prev=document.createElement("button");prev.className="lv-pagination-btn";prev.textContent="<";prev.disabled=page<=1;prev.setAttribute("data-page",String(page-1));container.appendChild(prev);const maxVisible=7;let startPage=Math.max(1,page-Math.floor(maxVisible/2));const endPage=Math.min(totalPages,startPage+maxVisible-1);if(endPage-startPage<maxVisible-1)startPage=Math.max(1,endPage-maxVisible+1);for(let i=startPage;i<=endPage;i++){const btn=document.createElement("button");btn.className=`lv-pagination-btn${i===page?" lv-pagination-btn--active":""}`;btn.textContent=String(i);btn.setAttribute("data-page",String(i));container.appendChild(btn);}const next=document.createElement("button");next.className="lv-pagination-btn";next.textContent=">";next.disabled=page>=totalPages;next.setAttribute("data-page",String(page+1));container.appendChild(next);const info=document.createElement("span");info.className="lv-pagination-info";info.textContent=`\u5171 ${total}\u6761`;container.appendChild(info);return container;}};var DiffRenderer=class{renderDetail(entry){const container=document.createElement("div");container.className="lv-detail";if(entry.changes&&entry.changes.length>0)container.appendChild(this.renderDiffTable(entry.changes));if(entry.metadata&&Object.keys(entry.metadata).length>0){const meta=document.createElement("div");meta.className="lv-metadata";meta.textContent=JSON.stringify(entry.metadata,null,2);container.appendChild(meta);}return container;}renderDiffTable(changes){const table=document.createElement("table");table.className="lv-diff-table";const thead=document.createElement("thead");thead.innerHTML=`<tr><th>\u5B57\u6BB5</th><th>\u53D8\u66F4\u524D</th><th>\u53D8\u66F4\u540E</th></tr>`;table.appendChild(thead);const tbody=document.createElement("tbody");for(const change of changes){const tr=document.createElement("tr");const tdField=document.createElement("td");tdField.textContent=change.label||change.field;tr.appendChild(tdField);const tdOld=document.createElement("td");tdOld.className="lv-diff-old";tdOld.textContent=formatValue(change.oldValue);tr.appendChild(tdOld);const tdNew=document.createElement("td");tdNew.className="lv-diff-new";tdNew.textContent=formatValue(change.newValue);tr.appendChild(tdNew);tbody.appendChild(tr);}table.appendChild(tbody);return table;}};const STYLE_ID="ldesign-log-viewer-styles";function injectStyles(){if(typeof document==="undefined")return;if(document.getElementById(STYLE_ID))return;const style=document.createElement("style");style.id=STYLE_ID;style.textContent=CSS_TEXT;document.head.appendChild(style);}const CSS_TEXT=` .lv-root{--lv-bg:#ffffff;--lv-bg-secondary:#f7f8fa;--lv-bg-hover:#f0f3ff;--lv-border:#ebedf0;--lv-text:#1a1a2e;--lv-text-secondary:#666;--lv-text-muted:#999;--lv-primary:#4f6ef7;--lv-primary-light:#edf0ff;--lv-shadow:rgba(0,0,0,0.04);--lv-timeline-line:#e0e0e0;--lv-timeline-dot:#4f6ef7;--lv-diff-add-bg:#f0fff4;--lv-diff-add-text:#22863a;--lv-diff-del-bg:#fff5f5;--lv-diff-del-text:#cb2431;--lv-anomaly-bg:#fff2e8;--lv-anomaly-border:#ffbb96;--lv-radius:8px;--lv-font:-apple-system,BlinkMacSystemFont,'Segoe UI','PingFang SC','Hiragino Sans GB','Microsoft YaHei',sans-serif;--lv-font-mono:'Fira Code','SF Mono',Consolas,monospace;}.lv-root[data-theme="dark"]{--lv-bg:#1a1a2e;--lv-bg-secondary:#16213e;--lv-bg-hover:#1e2a4a;--lv-border:#2a2a4a;--lv-text:#e0e0e0;--lv-text-secondary:#a0a0b0;--lv-text-muted:#666680;--lv-primary:#6b85f9;--lv-primary-light:#252545;--lv-shadow:rgba(0,0,0,0.2);--lv-timeline-line:#333355;--lv-timeline-dot:#6b85f9;--lv-diff-add-bg:#0d2818;--lv-diff-add-text:#85e89d;--lv-diff-del-bg:#2d1214;--lv-diff-del-text:#f97583;--lv-anomaly-bg:#2d1a0a;--lv-anomaly-border:#8b5e3c;}.lv-root{font-family:var(--lv-font);font-size:14px;color:var(--lv-text);background:var(--lv-bg);line-height:1.6;}.lv-filter{display:flex;flex-wrap:wrap;gap:10px;padding:16px;background:var(--lv-bg);border:1px solid var(--lv-border);border-radius:var(--lv-radius);margin-bottom:16px;align-items:center;}.lv-filter-item{display:flex;align-items:center;gap:6px;font-size:13px;}.lv-filter-label{color:var(--lv-text-secondary);font-size:12px;white-space:nowrap;}.lv-filter-input{padding:5px 10px;border:1px solid var(--lv-border);border-radius:6px;font-size:13px;background:var(--lv-bg);color:var(--lv-text);outline:none;transition:border-color 0.2s;min-width:120px;}.lv-filter-input:focus{border-color:var(--lv-primary);}.lv-filter-select{padding:5px 10px;border:1px solid var(--lv-border);border-radius:6px;font-size:13px;background:var(--lv-bg);color:var(--lv-text);outline:none;cursor:pointer;}.lv-filter-btn{display:inline-flex;align-items:center;gap:4px;padding:5px 14px;font-size:13px;border:1px solid var(--lv-border);border-radius:6px;background:var(--lv-bg);color:var(--lv-text);cursor:pointer;transition:all 0.15s;}.lv-filter-btn:hover{border-color:var(--lv-primary);color:var(--lv-primary);}.lv-filter-btn--primary{background:var(--lv-primary);border-color:var(--lv-primary);color:#fff;}.lv-filter-btn--primary:hover{opacity:0.85;}.lv-timeline{position:relative;padding-left:28px;}.lv-timeline::before{content:'';position:absolute;left:9px;top:0;bottom:0;width:2px;background:var(--lv-timeline-line);}.lv-timeline-item{position:relative;padding:0 0 20px 0;}.lv-timeline-dot{position:absolute;left:-25px;top:6px;width:12px;height:12px;border-radius:50%;background:var(--lv-bg);border:2px solid var(--lv-timeline-dot);z-index:1;}.lv-timeline-dot--anomaly{border-color:#ff4d4f;background:#fff1f0;}.lv-timeline-card{background:var(--lv-bg);border:1px solid var(--lv-border);border-radius:var(--lv-radius);padding:14px 18px;cursor:pointer;transition:all 0.2s;box-shadow:0 1px 3px var(--lv-shadow);}.lv-timeline-card:hover{border-color:var(--lv-primary);box-shadow:0 2px 8px var(--lv-shadow);}.lv-timeline-card--anomaly{border-left:3px solid var(--lv-anomaly-border);background:var(--lv-anomaly-bg);}.lv-timeline-header{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:4px;}.lv-timeline-time{font-size:12px;color:var(--lv-text-muted);font-family:var(--lv-font-mono);}.lv-timeline-user{font-weight:600;font-size:14px;}.lv-action-tag{display:inline-block;padding:1px 8px;font-size:12px;border-radius:4px;font-weight:500;color:#fff;}.lv-timeline-module{font-size:12px;color:var(--lv-text-secondary);background:var(--lv-bg-secondary);padding:1px 8px;border-radius:4px;}.lv-timeline-ip{font-size:12px;color:var(--lv-text-muted);font-family:var(--lv-font-mono);}.lv-anomaly-badge{font-size:11px;padding:1px 6px;border-radius:3px;background:#fff2e8;color:#d46b08;border:1px solid #ffd591;}.lv-timeline-detail{font-size:13px;color:var(--lv-text-secondary);margin-top:6px;}.lv-timeline-expand-hint{font-size:12px;color:var(--lv-primary);margin-top:6px;cursor:pointer;}.lv-detail{margin-top:12px;padding-top:12px;border-top:1px dashed var(--lv-border);animation:lv-slide-down 0.2s ease;}@keyframes lv-slide-down{from{opacity:0;transform:translateY(-8px);}to{opacity:1;transform:translateY(0);}}.lv-diff-table{width:100%;border-collapse:collapse;font-size:13px;border:1px solid var(--lv-border);border-radius:6px;overflow:hidden;}.lv-diff-table th{background:var(--lv-bg-secondary);padding:8px 12px;text-align:left;font-weight:600;font-size:12px;color:var(--lv-text-secondary);border-bottom:1px solid var(--lv-border);}.lv-diff-table td{padding:8px 12px;border-bottom:1px solid var(--lv-border);vertical-align:top;}.lv-diff-table tr:last-child td{border-bottom:none;}.lv-diff-old{background:var(--lv-diff-del-bg);color:var(--lv-diff-del-text);text-decoration:line-through;font-family:var(--lv-font-mono);font-size:12px;}.lv-diff-new{background:var(--lv-diff-add-bg);color:var(--lv-diff-add-text);font-family:var(--lv-font-mono);font-size:12px;}.lv-metadata{margin-top:10px;background:var(--lv-bg-secondary);border-radius:6px;padding:10px 14px;font-size:12px;font-family:var(--lv-font-mono);white-space:pre-wrap;word-break:break-all;color:var(--lv-text-secondary);}.lv-pagination{display:flex;align-items:center;justify-content:center;gap:6px;padding:16px 0;}.lv-pagination-btn{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border:1px solid var(--lv-border);border-radius:6px;background:var(--lv-bg);color:var(--lv-text);font-size:13px;cursor:pointer;transition:all 0.15s;}.lv-pagination-btn:hover{border-color:var(--lv-primary);color:var(--lv-primary);}.lv-pagination-btn--active{background:var(--lv-primary);border-color:var(--lv-primary);color:#fff;}.lv-pagination-btn:disabled{opacity:0.4;cursor:not-allowed;}.lv-pagination-info{font-size:13px;color:var(--lv-text-secondary);margin:0 8px;}.lv-empty{text-align:center;padding:48px 16px;color:var(--lv-text-muted);font-size:14px;}.lv-empty-icon{font-size:48px;margin-bottom:12px;opacity:0.3;}`;var LogViewer=class{constructor(config){this.emitter=new EventEmitter();this.filterEngine=new FilterEngine();this.timelineRenderer=new TimelineRenderer();this.diffRenderer=new DiffRenderer();this.data=[];this.filteredData=[];this.filter={};this.pagination={page:1,pageSize:20};this.theme="light";this.expandedIds=new Set();this.timelineEl=null;this.paginationEl=null;this.destroyed=false;this.config=config;const container=typeof config.container==="string"?document.querySelector(config.container):config.container;if(!container)throw new Error(`[LogViewer]Container not found:${config.container}`);injectStyles();this.root=document.createElement("div");this.root.className="lv-root";container.appendChild(this.root);this.theme=config.theme||"light";this.root.setAttribute("data-theme",this.theme);if(config.pageSize)this.pagination.pageSize=config.pageSize;if(config.pagination)Object.assign(this.pagination,config.pagination);if(config.filter)this.filter=deepClone(config.filter);if(config.data)this.data=[...config.data];this.applyFilterAndRender();this.bindEvents();}getData(){return[...this.data];}setData(entries){this.data=[...entries];this.pagination.page=1;this.applyFilterAndRender();this.emitter.emit("data:change",this.data);}appendData(entries){this.data.push(...entries);this.applyFilterAndRender();this.emitter.emit("data:change",this.data);}getFilter(){return deepClone(this.filter);}setFilter(filter){this.filter=deepClone(filter);this.pagination.page=1;this.applyFilterAndRender();this.emitter.emit("filter:change",this.filter);this.config.onFilterChange?.(this.filter);}resetFilter(){this.filter={};this.pagination.page=1;this.applyFilterAndRender();this.emitter.emit("filter:change",this.filter);}getTheme(){return this.theme;}setTheme(theme){this.theme=theme;this.root.setAttribute("data-theme",theme);this.emitter.emit("theme:change",theme);this.config.onThemeChange?.(theme);}expandEntry(id){this.expandedIds.add(id);const entry=this.data.find((e)=>e.id===id);if(entry)this.emitter.emit("entry:expand",entry);this.renderTimeline();}collapseEntry(id){this.expandedIds.delete(id);const entry=this.data.find((e)=>e.id===id);if(entry)this.emitter.emit("entry:collapse",entry);this.renderTimeline();}collapseAll(){this.expandedIds.clear();this.renderTimeline();}goToPage(page){const totalPages=Math.ceil(this.filteredData.length/this.pagination.pageSize);if(page<1||page>totalPages)return;this.pagination.page=page;this.emitter.emit("page:change",page);this.renderTimeline();}getPagination(){return{...this.pagination,total:this.filteredData.length};}on(event,callback){return this.emitter.on(event,callback);}refresh(){this.applyFilterAndRender();}destroy(){if(this.destroyed)return;this.destroyed=true;this.emitter.removeAllListeners();this.root.remove();}applyFilterAndRender(){this.filteredData=this.filterEngine.filter(this.data,this.filter);this.filteredData.sort((a,b)=>new Date(b.timestamp).getTime()-new Date(a.timestamp).getTime());this.pagination.total=this.filteredData.length;this.renderTimeline();}getPageData(){const{page,pageSize}=this.pagination;const start=(page-1)*pageSize;return this.filteredData.slice(start,start+pageSize);}renderTimeline(){if(this.destroyed)return;const pageData=this.getPageData();const newTimeline=this.timelineRenderer.render(pageData,this.expandedIds);for(const entry of pageData)if(this.expandedIds.has(entry.id)){const itemEl=newTimeline.querySelector(`[data-id="${entry.id}"].lv-timeline-card`);if(itemEl)itemEl.appendChild(this.diffRenderer.renderDetail(entry));}if(this.timelineEl)this.root.removeChild(this.timelineEl);this.timelineEl=newTimeline;this.root.appendChild(this.timelineEl);const newPagination=this.timelineRenderer.renderPagination(this.pagination.page,this.pagination.pageSize,this.filteredData.length);if(this.paginationEl)this.root.removeChild(this.paginationEl);this.paginationEl=newPagination;this.root.appendChild(this.paginationEl);}bindEvents(){this.root.addEventListener("click",(e)=>{const target=e.target;const card=target.closest(".lv-timeline-card");if(card){const id=card.closest(".lv-timeline-item")?.getAttribute("data-id");if(!id)return;const entry=this.filteredData.find((en)=>en.id===id);if(!entry)return;if(target.classList.contains("lv-timeline-expand-hint")){if(this.expandedIds.has(id))this.collapseEntry(id);else this.expandEntry(id);return;}this.emitter.emit("entry:click",entry);this.config.onEntryClick?.(entry);}const pageBtn=target.closest(".lv-pagination-btn");if(pageBtn&&!pageBtn.disabled){const page=parseInt(pageBtn.getAttribute("data-page")||"1",10);this.goToPage(page);}});}};const VERSION="0.1.0";function createLogViewer(config){return new LogViewer(config);}exports.ACTION_COLORS=ACTION_COLORS;exports.ACTION_LABELS=ACTION_LABELS;exports.DiffRenderer=DiffRenderer;exports.EventEmitter=EventEmitter;exports.FilterEngine=FilterEngine;exports.LogViewer=LogViewer;exports.TimelineRenderer=TimelineRenderer;exports.VERSION=VERSION;exports.createLogViewer=createLogViewer;exports.debounce=debounce;exports.deepClone=deepClone;exports.escapeHtml=escapeHtml;exports.formatDate=formatDate;exports.formatRelativeTime=formatRelativeTime;exports.formatValue=formatValue;exports.injectStyles=injectStyles;exports.uid=uid;});
|
|
1
|
+
/*!
|
|
2
|
+
* ***********************************
|
|
3
|
+
* @composy/log-viewer-core v0.0.1 *
|
|
4
|
+
* Built with rollup *
|
|
5
|
+
* Build time: 2024-10-12 17:29:20 *
|
|
6
|
+
* Build mode: production *
|
|
7
|
+
* Minified: No *
|
|
8
|
+
* ***********************************
|
|
9
|
+
*/
|
|
10
|
+
(function(e, t) {
|
|
11
|
+
"object" == typeof exports && "undefined" != typeof module ? t(exports) : "function" == typeof define && define.amd ? define(["exports"], t) : t((e = "undefined" != typeof globalThis ? globalThis : e || self).LDesignLogViewerCore = {});
|
|
12
|
+
})(this, function(exports) {
|
|
13
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
14
|
+
const e = {
|
|
15
|
+
create: "新增",
|
|
16
|
+
update: "修改",
|
|
17
|
+
delete: "删除",
|
|
18
|
+
query: "查询",
|
|
19
|
+
login: "登录",
|
|
20
|
+
logout: "登出",
|
|
21
|
+
export: "导出",
|
|
22
|
+
import: "导入",
|
|
23
|
+
approve: "审批",
|
|
24
|
+
reject: "驳回",
|
|
25
|
+
other: "其他"
|
|
26
|
+
};
|
|
27
|
+
var t = class {
|
|
28
|
+
constructor() {
|
|
29
|
+
this.listeners = /* @__PURE__ */ new Map();
|
|
30
|
+
}
|
|
31
|
+
on(e, t) {
|
|
32
|
+
return e && "function" == typeof t ? (this.listeners.has(e) || this.listeners.set(e, /* @__PURE__ */ new Set()), this.listeners.get(e).add(t), () => this.off(e, t)) : () => {};
|
|
33
|
+
}
|
|
34
|
+
off(e, t) {
|
|
35
|
+
const n = this.listeners.get(e);
|
|
36
|
+
n && (n.delete(t), 0 === n.size && this.listeners.delete(e));
|
|
37
|
+
}
|
|
38
|
+
emit(e, ...t) {
|
|
39
|
+
const n = this.listeners.get(e);
|
|
40
|
+
if (n) for (const r of [...n]) try {
|
|
41
|
+
r(...t);
|
|
42
|
+
} catch (t) {
|
|
43
|
+
console.error(`[LogViewer] Event handler error (${e}):`, t);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
removeAllListeners(e) {
|
|
47
|
+
e ? this.listeners.delete(e) : this.listeners.clear();
|
|
48
|
+
}
|
|
49
|
+
}, n = class {
|
|
50
|
+
filter(e, t) {
|
|
51
|
+
let n = Array.isArray(e) ? e.slice() : [];
|
|
52
|
+
const r = t || {};
|
|
53
|
+
if (r.user?.trim()) {
|
|
54
|
+
const e = r.user.trim().toLowerCase();
|
|
55
|
+
n = n.filter((t) => t.user?.toLowerCase().includes(e) || t.userId?.toLowerCase().includes(e));
|
|
56
|
+
}
|
|
57
|
+
if (r.action) {
|
|
58
|
+
const e = Array.isArray(r.action) ? r.action : [r.action];
|
|
59
|
+
e.length > 0 && (n = n.filter((t) => e.includes(t.action)));
|
|
60
|
+
}
|
|
61
|
+
if (r.module?.trim()) {
|
|
62
|
+
const e = r.module.trim().toLowerCase();
|
|
63
|
+
n = n.filter((t) => t.module?.toLowerCase().includes(e));
|
|
64
|
+
}
|
|
65
|
+
if (r.ip?.trim()) {
|
|
66
|
+
const e = r.ip.trim().toLowerCase();
|
|
67
|
+
n = n.filter((t) => t.ip?.toLowerCase().includes(e));
|
|
68
|
+
}
|
|
69
|
+
if (r.timeRange) {
|
|
70
|
+
const [e, t] = r.timeRange, a = new Date(e).getTime(), i = new Date(t).getTime();
|
|
71
|
+
if (Number.isFinite(a) && Number.isFinite(i)) {
|
|
72
|
+
const e = Math.min(a, i), t = Math.max(a, i);
|
|
73
|
+
n = n.filter((n) => {
|
|
74
|
+
const r = new Date(n.timestamp).getTime();
|
|
75
|
+
return Number.isFinite(r) && r >= e && r <= t;
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (r.keyword?.trim()) {
|
|
80
|
+
const e = r.keyword.trim().toLowerCase();
|
|
81
|
+
n = n.filter((t) => [
|
|
82
|
+
t.user || "",
|
|
83
|
+
t.action,
|
|
84
|
+
t.module || "",
|
|
85
|
+
t.detail || "",
|
|
86
|
+
t.ip || "",
|
|
87
|
+
t.id
|
|
88
|
+
].some((t) => t.toLowerCase().includes(e)));
|
|
89
|
+
}
|
|
90
|
+
return n;
|
|
91
|
+
}
|
|
92
|
+
getUniqueUsers(e) {
|
|
93
|
+
return [...new Set(e.map((e) => e.user).filter(Boolean))].sort();
|
|
94
|
+
}
|
|
95
|
+
getUniqueModules(e) {
|
|
96
|
+
return [...new Set(e.map((e) => e.module).filter(Boolean))].sort();
|
|
97
|
+
}
|
|
98
|
+
getUniqueActions(e) {
|
|
99
|
+
return [...new Set(e.map((e) => e.action))].sort();
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
let r = 0;
|
|
103
|
+
function a(e, t = "YYYY-MM-DD HH:mm:ss") {
|
|
104
|
+
const n = e instanceof Date ? e : new Date(e);
|
|
105
|
+
if (Number.isNaN(n.getTime())) return String(e);
|
|
106
|
+
const r = (e) => String(e).padStart(2, "0"), a = String(n.getFullYear()), i = r(n.getMonth() + 1), o = r(n.getDate()), l = r(n.getHours()), d = r(n.getMinutes()), s = r(n.getSeconds());
|
|
107
|
+
return t.replace("YYYY", a).replace("MM", i).replace("DD", o).replace("HH", l).replace("mm", d).replace("ss", s);
|
|
108
|
+
}
|
|
109
|
+
function i(e) {
|
|
110
|
+
if (null === e || "object" != typeof e) return e;
|
|
111
|
+
if ("function" == typeof structuredClone) try {
|
|
112
|
+
return structuredClone(e);
|
|
113
|
+
} catch {}
|
|
114
|
+
const t = /* @__PURE__ */ new WeakMap(), n = (e) => {
|
|
115
|
+
if (null === e || "object" != typeof e) return e;
|
|
116
|
+
if (t.has(e)) return t.get(e);
|
|
117
|
+
if (e instanceof Date) return new Date(e.getTime());
|
|
118
|
+
if (e instanceof RegExp) return new RegExp(e.source, e.flags);
|
|
119
|
+
if (e instanceof Map) {
|
|
120
|
+
const r = /* @__PURE__ */ new Map();
|
|
121
|
+
return t.set(e, r), e.forEach((e, t) => r.set(n(t), n(e))), r;
|
|
122
|
+
}
|
|
123
|
+
if (e instanceof Set) {
|
|
124
|
+
const r = /* @__PURE__ */ new Set();
|
|
125
|
+
return t.set(e, r), e.forEach((e) => r.add(n(e))), r;
|
|
126
|
+
}
|
|
127
|
+
if (Array.isArray(e)) {
|
|
128
|
+
const r = [];
|
|
129
|
+
return t.set(e, r), e.forEach((e) => r.push(n(e))), r;
|
|
130
|
+
}
|
|
131
|
+
const r = Object.create(Object.getPrototypeOf(e));
|
|
132
|
+
t.set(e, r);
|
|
133
|
+
for (const t of Reflect.ownKeys(e)) {
|
|
134
|
+
const a = Object.getOwnPropertyDescriptor(e, t);
|
|
135
|
+
a && "value" in a && Object.defineProperty(r, t, {
|
|
136
|
+
...a,
|
|
137
|
+
value: n(a.value)
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
return r;
|
|
141
|
+
};
|
|
142
|
+
try {
|
|
143
|
+
return n(e);
|
|
144
|
+
} catch {
|
|
145
|
+
return e;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
function o(e) {
|
|
149
|
+
if (null == e) return "-";
|
|
150
|
+
if ("object" == typeof e) try {
|
|
151
|
+
return JSON.stringify(e, (e, t) => "bigint" == typeof t ? `${t}n` : t, 2);
|
|
152
|
+
} catch {
|
|
153
|
+
return String(e);
|
|
154
|
+
}
|
|
155
|
+
return String(e);
|
|
156
|
+
}
|
|
157
|
+
var l = class {
|
|
158
|
+
renderDetail(e) {
|
|
159
|
+
const t = document.createElement("div");
|
|
160
|
+
if (t.className = "lv-detail", e.changes && e.changes.length > 0 && t.appendChild(this.renderDiffTable(e.changes)), e.metadata && Object.keys(e.metadata).length > 0) {
|
|
161
|
+
const n = document.createElement("div");
|
|
162
|
+
n.className = "lv-metadata", n.textContent = o(e.metadata), t.appendChild(n);
|
|
163
|
+
}
|
|
164
|
+
return t;
|
|
165
|
+
}
|
|
166
|
+
renderDiffTable(e) {
|
|
167
|
+
const t = document.createElement("table");
|
|
168
|
+
t.className = "lv-diff-table";
|
|
169
|
+
const n = document.createElement("thead"), r = document.createElement("tr");
|
|
170
|
+
for (const e of [
|
|
171
|
+
"字段",
|
|
172
|
+
"变更前",
|
|
173
|
+
"变更后"
|
|
174
|
+
]) {
|
|
175
|
+
const t = document.createElement("th");
|
|
176
|
+
t.textContent = e, r.appendChild(t);
|
|
177
|
+
}
|
|
178
|
+
n.appendChild(r), t.appendChild(n);
|
|
179
|
+
const a = document.createElement("tbody");
|
|
180
|
+
for (const t of e) {
|
|
181
|
+
const e = document.createElement("tr"), n = document.createElement("td");
|
|
182
|
+
n.textContent = t.label || t.field, e.appendChild(n);
|
|
183
|
+
const r = document.createElement("td");
|
|
184
|
+
r.className = "lv-diff-old", r.textContent = o(t.oldValue), e.appendChild(r);
|
|
185
|
+
const i = document.createElement("td");
|
|
186
|
+
i.className = "lv-diff-new", i.textContent = o(t.newValue), e.appendChild(i), a.appendChild(e);
|
|
187
|
+
}
|
|
188
|
+
return t.appendChild(a), t;
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
const d = {
|
|
192
|
+
"off-hours": "非工作时间",
|
|
193
|
+
sensitive: "敏感操作",
|
|
194
|
+
"high-frequency": "高频操作",
|
|
195
|
+
none: ""
|
|
196
|
+
}, s = {
|
|
197
|
+
"off-hours": "Off hours",
|
|
198
|
+
sensitive: "Sensitive",
|
|
199
|
+
"high-frequency": "High frequency",
|
|
200
|
+
none: ""
|
|
201
|
+
}, c = {
|
|
202
|
+
create: "Create",
|
|
203
|
+
update: "Update",
|
|
204
|
+
delete: "Delete",
|
|
205
|
+
query: "Query",
|
|
206
|
+
login: "Login",
|
|
207
|
+
logout: "Logout",
|
|
208
|
+
export: "Export",
|
|
209
|
+
import: "Import",
|
|
210
|
+
approve: "Approve",
|
|
211
|
+
reject: "Reject",
|
|
212
|
+
other: "Other"
|
|
213
|
+
};
|
|
214
|
+
var p = class {
|
|
215
|
+
render(e, t, n = {}) {
|
|
216
|
+
const r = document.createElement("div");
|
|
217
|
+
if (r.className = "lv-timeline", 0 === e.length) {
|
|
218
|
+
const e = document.createElement("div");
|
|
219
|
+
e.className = "lv-empty-icon", e.textContent = "--";
|
|
220
|
+
const t = document.createElement("div");
|
|
221
|
+
return t.textContent = "en-US" === n.locale ? "No log entries" : "暂无日志记录", r.append(e, t), r.className = "", r;
|
|
222
|
+
}
|
|
223
|
+
for (const a of e) r.appendChild(this.renderItem(a, t.has(a.id), n));
|
|
224
|
+
return r;
|
|
225
|
+
}
|
|
226
|
+
renderItem(t, n, r = {}) {
|
|
227
|
+
const i = document.createElement("div");
|
|
228
|
+
i.className = "lv-timeline-item", i.setAttribute("data-id", t.id);
|
|
229
|
+
const o = !1 !== r.showAnomalyBadge && t.anomaly && "none" !== t.anomaly, l = document.createElement("div");
|
|
230
|
+
l.className = "lv-timeline-dot" + (o ? " lv-timeline-dot--anomaly" : ""), i.appendChild(l);
|
|
231
|
+
const p = document.createElement("div");
|
|
232
|
+
p.className = "lv-timeline-card" + (o ? " lv-timeline-card--anomaly" : "");
|
|
233
|
+
const v = document.createElement("div");
|
|
234
|
+
v.className = "lv-timeline-header";
|
|
235
|
+
const m = document.createElement("span");
|
|
236
|
+
m.className = "lv-timeline-time", m.textContent = a(t.timestamp), v.appendChild(m);
|
|
237
|
+
const f = document.createElement("span");
|
|
238
|
+
f.className = "lv-timeline-user", f.textContent = t.user, v.appendChild(f);
|
|
239
|
+
const h = document.createElement("span");
|
|
240
|
+
h.className = `lv-action-tag lv-action-tag--${Object.keys(e).includes(t.action) ? t.action : "other"}`, h.textContent = ("en-US" === r.locale ? c[t.action] : e[t.action]) || t.action, v.appendChild(h);
|
|
241
|
+
const u = document.createElement("span");
|
|
242
|
+
if (u.className = "lv-timeline-module", u.textContent = t.module, v.appendChild(u), t.ip) {
|
|
243
|
+
const e = document.createElement("span");
|
|
244
|
+
e.className = "lv-timeline-ip", e.textContent = t.ip, v.appendChild(e);
|
|
245
|
+
}
|
|
246
|
+
if (!1 !== r.showAnomalyBadge && o) {
|
|
247
|
+
const e = document.createElement("span");
|
|
248
|
+
e.className = "lv-anomaly-badge", e.textContent = ("en-US" === r.locale ? s[t.anomaly] : d)[t.anomaly] || t.anomaly, v.appendChild(e);
|
|
249
|
+
}
|
|
250
|
+
if (p.appendChild(v), t.detail) {
|
|
251
|
+
const e = document.createElement("div");
|
|
252
|
+
e.className = "lv-timeline-detail", e.textContent = t.detail, p.appendChild(e);
|
|
253
|
+
}
|
|
254
|
+
if (!1 !== r.showDiff && t.changes && t.changes.length > 0) {
|
|
255
|
+
const e = document.createElement("button");
|
|
256
|
+
e.type = "button", e.className = "lv-timeline-expand-hint", e.setAttribute("aria-expanded", String(n)), e.textContent = n ? "[ 收起变更详情 ]" : `[ 查看变更详情 (${t.changes.length} 个字段) ]`, p.appendChild(e);
|
|
257
|
+
}
|
|
258
|
+
return i.appendChild(p), i;
|
|
259
|
+
}
|
|
260
|
+
renderPagination(e, t, n) {
|
|
261
|
+
const r = document.createElement("div");
|
|
262
|
+
r.className = "lv-pagination";
|
|
263
|
+
const a = Number.isFinite(t) && t > 0 ? Math.floor(t) : 1, i = Number.isFinite(n) && n > 0 ? Math.floor(n) : 0, o = Number.isFinite(e) && e > 0 ? Math.floor(e) : 1, l = Math.ceil(i / a);
|
|
264
|
+
if (l <= 1) return r;
|
|
265
|
+
const d = document.createElement("button");
|
|
266
|
+
d.type = "button", d.className = "lv-pagination-btn", d.textContent = "<", d.disabled = o <= 1, d.setAttribute("aria-label", "Previous page"), d.setAttribute("data-page", String(o - 1)), r.appendChild(d);
|
|
267
|
+
let s = Math.max(1, o - Math.floor(3.5));
|
|
268
|
+
const c = Math.min(l, s + 7 - 1);
|
|
269
|
+
c - s < 6 && (s = Math.max(1, c - 7 + 1));
|
|
270
|
+
for (let e = s; e <= c; e++) {
|
|
271
|
+
const t = document.createElement("button");
|
|
272
|
+
t.type = "button", t.className = "lv-pagination-btn" + (e === o ? " lv-pagination-btn--active" : ""), t.textContent = String(e), t.setAttribute("data-page", String(e)), e === o && t.setAttribute("aria-current", "page"), r.appendChild(t);
|
|
273
|
+
}
|
|
274
|
+
const p = document.createElement("button");
|
|
275
|
+
p.type = "button", p.className = "lv-pagination-btn", p.textContent = ">", p.disabled = o >= l, p.setAttribute("aria-label", "Next page"), p.setAttribute("data-page", String(o + 1)), r.appendChild(p);
|
|
276
|
+
const v = document.createElement("span");
|
|
277
|
+
return v.className = "lv-pagination-info", v.textContent = `共 ${i} 条`, r.appendChild(v), r;
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
const v = "ldesign-log-viewer-styles";
|
|
281
|
+
function m() {
|
|
282
|
+
if ("undefined" == typeof document) return;
|
|
283
|
+
if (document.getElementById(v)) return;
|
|
284
|
+
const e = document.createElement("style");
|
|
285
|
+
e.id = v, e.textContent = f, document.head.appendChild(e);
|
|
286
|
+
}
|
|
287
|
+
const f = "\n/* ===== CSS Variables ===== */\n.lv-root {\n --lv-bg: var(--ld-white);\n --lv-bg-secondary: var(--ld-bg-surface-soft);\n --lv-bg-hover: var(--ld-primary-50);\n --lv-border: var(--ld-border);\n --lv-text: var(--ld-text-primary);\n --lv-text-secondary: var(--ld-text-secondary);\n --lv-text-muted: var(--ld-text-muted);\n --lv-primary: var(--ld-primary-500);\n --lv-primary-light: var(--ld-primary-50);\n --lv-shadow: color-mix(in srgb, var(--ld-text-primary) 4%, transparent);\n --lv-timeline-line: var(--ld-border);\n --lv-timeline-dot: var(--ld-primary-500);\n --lv-diff-add-bg: var(--ld-success-50);\n --lv-diff-add-text: var(--ld-success-700);\n --lv-diff-del-bg: var(--ld-danger-50);\n --lv-diff-del-text: var(--ld-danger-700);\n --lv-anomaly-bg: var(--ld-warning-50);\n --lv-anomaly-border: var(--ld-warning-300);\n --lv-radius: var(--ld-radius-md);\n --lv-font: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif;\n --lv-font-mono: 'Fira Code', 'SF Mono', Consolas, monospace;\n}\n\n.lv-root[data-theme=\"dark\"] {\n --lv-bg: var(--ld-bg-surface);\n --lv-bg-secondary: var(--ld-bg-surface-soft);\n --lv-bg-hover: var(--ld-primary-50);\n --lv-border: var(--ld-border-strong);\n --lv-text: var(--ld-text-primary);\n --lv-text-secondary: var(--ld-text-secondary);\n --lv-text-muted: var(--ld-text-muted);\n --lv-primary: var(--ld-primary-400);\n --lv-primary-light: var(--ld-primary-50);\n --lv-shadow: color-mix(in srgb, var(--ld-text-primary) 20%, transparent);\n --lv-timeline-line: var(--ld-border-strong);\n --lv-timeline-dot: var(--ld-primary-400);\n --lv-diff-add-bg: var(--ld-success-50);\n --lv-diff-add-text: var(--ld-success-700);\n --lv-diff-del-bg: var(--ld-danger-50);\n --lv-diff-del-text: var(--ld-danger-700);\n --lv-anomaly-bg: var(--ld-warning-50);\n --lv-anomaly-border: var(--ld-warning-700);\n}\n\n/* ===== Root ===== */\n.lv-root {\n font-family: var(--lv-font);\n font-size: var(--ld-px-14);\n color: var(--lv-text);\n background: var(--lv-bg);\n line-height: 1.6;\n}\n\n/* ===== Filter Bar ===== */\n.lv-filter {\n display: flex;\n flex-wrap: wrap;\n gap: var(--ld-px-10);\n padding: var(--ld-px-16);\n background: var(--lv-bg);\n border: var(--ld-px-1) solid var(--lv-border);\n border-radius: var(--lv-radius);\n margin-bottom: var(--ld-px-16);\n align-items: center;\n}\n\n.lv-filter-item {\n display: flex;\n align-items: center;\n gap: var(--ld-px-6);\n font-size: var(--ld-px-13);\n}\n\n.lv-filter-label {\n color: var(--lv-text-secondary);\n font-size: var(--ld-px-12);\n white-space: nowrap;\n}\n\n.lv-filter-input {\n padding: var(--ld-px-5) var(--ld-px-10);\n border: var(--ld-px-1) solid var(--lv-border);\n border-radius: var(--ld-px-6);\n font-size: var(--ld-px-13);\n background: var(--lv-bg);\n color: var(--lv-text);\n outline: none;\n transition: border-color 0.2s;\n min-width: var(--ld-px-120);\n}\n\n.lv-filter-input:focus {\n border-color: var(--lv-primary);\n}\n\n.lv-filter-select {\n padding: var(--ld-px-5) var(--ld-px-10);\n border: var(--ld-px-1) solid var(--lv-border);\n border-radius: var(--ld-px-6);\n font-size: var(--ld-px-13);\n background: var(--lv-bg);\n color: var(--lv-text);\n outline: none;\n cursor: pointer;\n}\n\n.lv-filter-btn {\n display: inline-flex;\n align-items: center;\n gap: var(--ld-px-4);\n padding: var(--ld-px-5) var(--ld-px-14);\n font-size: var(--ld-px-13);\n border: var(--ld-px-1) solid var(--lv-border);\n border-radius: var(--ld-px-6);\n background: var(--lv-bg);\n color: var(--lv-text);\n cursor: pointer;\n transition: all 0.15s;\n}\n\n.lv-filter-btn:hover {\n border-color: var(--lv-primary);\n color: var(--lv-primary);\n}\n\n.lv-filter-btn--primary {\n background: var(--lv-primary);\n border-color: var(--lv-primary);\n color: var(--ld-white);\n}\n\n.lv-filter-btn--primary:hover {\n opacity: 0.85;\n}\n\n/* ===== Timeline ===== */\n.lv-timeline {\n position: relative;\n padding-left: var(--ld-px-28);\n}\n\n.lv-timeline::before {\n content: '';\n position: absolute;\n left: var(--ld-px-9);\n top: 0;\n bottom: 0;\n width: var(--ld-px-2);\n background: var(--lv-timeline-line);\n}\n\n.lv-timeline-item {\n position: relative;\n padding: 0 0 var(--ld-px-20) 0;\n}\n\n.lv-timeline-dot {\n position: absolute;\n left: calc(-1 * var(--ld-px-25));\n top: var(--ld-px-6);\n width: var(--ld-px-12);\n height: var(--ld-px-12);\n border-radius: 50%;\n background: var(--lv-bg);\n border: var(--ld-px-2) solid var(--lv-timeline-dot);\n z-index: 1;\n}\n\n.lv-timeline-dot--anomaly {\n border-color: var(--ld-danger-500);\n background: var(--ld-danger-50);\n}\n\n.lv-timeline-card {\n background: var(--lv-bg);\n border: var(--ld-px-1) solid var(--lv-border);\n border-radius: var(--lv-radius);\n padding: var(--ld-px-14) var(--ld-px-18);\n cursor: pointer;\n transition: all 0.2s;\n box-shadow: 0 var(--ld-px-1) var(--ld-px-3) var(--lv-shadow);\n}\n\n.lv-timeline-card:hover {\n border-color: var(--lv-primary);\n box-shadow: 0 var(--ld-px-2) var(--ld-px-8) var(--lv-shadow);\n}\n\n.lv-timeline-card--anomaly {\n border-left: var(--ld-px-3) solid var(--lv-anomaly-border);\n background: var(--lv-anomaly-bg);\n}\n\n.lv-timeline-header {\n display: flex;\n align-items: center;\n gap: var(--ld-px-10);\n flex-wrap: wrap;\n margin-bottom: var(--ld-px-4);\n}\n\n.lv-timeline-time {\n font-size: var(--ld-px-12);\n color: var(--lv-text-muted);\n font-family: var(--lv-font-mono);\n}\n\n.lv-timeline-user {\n font-weight: 600;\n font-size: var(--ld-px-14);\n}\n\n.lv-action-tag {\n display: inline-block;\n padding: var(--ld-px-1) var(--ld-px-8);\n font-size: var(--ld-px-12);\n border-radius: var(--ld-px-4);\n font-weight: 500;\n color: var(--ld-white);\n}\n\n.lv-action-tag--create,\n.lv-action-tag--approve {\n background: var(--ld-success-500);\n}\n\n.lv-action-tag--update,\n.lv-action-tag--login {\n background: var(--ld-primary-500);\n}\n\n.lv-action-tag--delete,\n.lv-action-tag--reject {\n background: var(--ld-danger-500);\n}\n\n.lv-action-tag--query,\n.lv-action-tag--other {\n background: var(--ld-text-secondary);\n}\n\n.lv-action-tag--logout {\n background: var(--ld-primary-700);\n}\n\n.lv-action-tag--export {\n background: var(--ld-warning-500);\n}\n\n.lv-action-tag--import {\n background: var(--ld-success-500);\n}\n\n.lv-timeline-module {\n font-size: var(--ld-px-12);\n color: var(--lv-text-secondary);\n background: var(--lv-bg-secondary);\n padding: var(--ld-px-1) var(--ld-px-8);\n border-radius: var(--ld-px-4);\n}\n\n.lv-timeline-ip {\n font-size: var(--ld-px-12);\n color: var(--lv-text-muted);\n font-family: var(--lv-font-mono);\n}\n\n.lv-anomaly-badge {\n font-size: var(--ld-px-11);\n padding: var(--ld-px-1) var(--ld-px-6);\n border-radius: var(--ld-px-3);\n background: var(--ld-warning-50);\n color: var(--ld-warning-700);\n border: var(--ld-px-1) solid var(--ld-warning-300);\n}\n\n.lv-timeline-detail {\n font-size: var(--ld-px-13);\n color: var(--lv-text-secondary);\n margin-top: var(--ld-px-6);\n}\n\n.lv-timeline-expand-hint {\n border: 0;\n padding: 0;\n background: transparent;\n font: inherit;\n font-size: var(--ld-px-12);\n color: var(--lv-primary);\n margin-top: var(--ld-px-6);\n cursor: pointer;\n}\n\n/* ===== Detail Panel ===== */\n.lv-detail {\n margin-top: var(--ld-px-12);\n padding-top: var(--ld-px-12);\n border-top: var(--ld-px-1) dashed var(--lv-border);\n animation: lv-slide-down 0.2s ease;\n}\n\n@keyframes lv-slide-down {\n from { opacity: 0; transform: translateY(calc(-1 * var(--ld-px-8))); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n/* ===== Diff Table ===== */\n.lv-diff-table {\n width: 100%;\n border-collapse: collapse;\n font-size: var(--ld-px-13);\n border: var(--ld-px-1) solid var(--lv-border);\n border-radius: var(--ld-px-6);\n overflow: hidden;\n}\n\n.lv-diff-table th {\n background: var(--lv-bg-secondary);\n padding: var(--ld-px-8) var(--ld-px-12);\n text-align: left;\n font-weight: 600;\n font-size: var(--ld-px-12);\n color: var(--lv-text-secondary);\n border-bottom: var(--ld-px-1) solid var(--lv-border);\n}\n\n.lv-diff-table td {\n padding: var(--ld-px-8) var(--ld-px-12);\n border-bottom: var(--ld-px-1) solid var(--lv-border);\n vertical-align: top;\n}\n\n.lv-diff-table tr:last-child td {\n border-bottom: none;\n}\n\n.lv-diff-old {\n background: var(--lv-diff-del-bg);\n color: var(--lv-diff-del-text);\n text-decoration: line-through;\n font-family: var(--lv-font-mono);\n font-size: var(--ld-px-12);\n}\n\n.lv-diff-new {\n background: var(--lv-diff-add-bg);\n color: var(--lv-diff-add-text);\n font-family: var(--lv-font-mono);\n font-size: var(--ld-px-12);\n}\n\n/* ===== Metadata ===== */\n.lv-metadata {\n margin-top: var(--ld-px-10);\n background: var(--lv-bg-secondary);\n border-radius: var(--ld-px-6);\n padding: var(--ld-px-10) var(--ld-px-14);\n font-size: var(--ld-px-12);\n font-family: var(--lv-font-mono);\n white-space: pre-wrap;\n word-break: break-all;\n color: var(--lv-text-secondary);\n}\n\n/* ===== Pagination ===== */\n.lv-pagination {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--ld-px-6);\n padding: var(--ld-px-16) 0;\n}\n\n.lv-pagination-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: var(--ld-px-32);\n height: var(--ld-px-32);\n border: var(--ld-px-1) solid var(--lv-border);\n border-radius: var(--ld-px-6);\n background: var(--lv-bg);\n color: var(--lv-text);\n font-size: var(--ld-px-13);\n cursor: pointer;\n transition: all 0.15s;\n}\n\n.lv-pagination-btn:hover {\n border-color: var(--lv-primary);\n color: var(--lv-primary);\n}\n\n.lv-pagination-btn--active {\n background: var(--lv-primary);\n border-color: var(--lv-primary);\n color: var(--ld-white);\n}\n\n.lv-pagination-btn:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n\n.lv-pagination-info {\n font-size: var(--ld-px-13);\n color: var(--lv-text-secondary);\n margin: 0 var(--ld-px-8);\n}\n\n/* ===== Empty state ===== */\n.lv-empty {\n text-align: center;\n padding: var(--ld-px-48) var(--ld-px-16);\n color: var(--lv-text-muted);\n font-size: var(--ld-px-14);\n}\n\n.lv-empty-icon {\n font-size: var(--ld-px-48);\n margin-bottom: var(--ld-px-12);\n opacity: 0.3;\n}\n";
|
|
288
|
+
function h(e, t = 20) {
|
|
289
|
+
const n = Number(e);
|
|
290
|
+
return !Number.isFinite(n) || n <= 0 ? t : Math.min(1e3, Math.max(1, Math.floor(n)));
|
|
291
|
+
}
|
|
292
|
+
function u(e) {
|
|
293
|
+
const t = Number(e);
|
|
294
|
+
return !Number.isFinite(t) || t < 1 ? 1 : Math.floor(t);
|
|
295
|
+
}
|
|
296
|
+
function g(e) {
|
|
297
|
+
if (void 0 === e) return [];
|
|
298
|
+
if (!Array.isArray(e)) throw new TypeError("[LogViewer] data must be an array");
|
|
299
|
+
return e.map((e, t) => {
|
|
300
|
+
if (!e || "object" != typeof e) throw new TypeError(`[LogViewer] data[${t}] must be an object`);
|
|
301
|
+
const n = i(e), r = String(n.id ?? "").trim();
|
|
302
|
+
if (!r) throw new TypeError(`[LogViewer] data[${t}].id must be a non-empty string`);
|
|
303
|
+
return {
|
|
304
|
+
...n,
|
|
305
|
+
id: r,
|
|
306
|
+
user: String(n.user ?? ""),
|
|
307
|
+
module: String(n.module ?? ""),
|
|
308
|
+
changes: Array.isArray(n.changes) ? n.changes : void 0
|
|
309
|
+
};
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
var b = class {
|
|
313
|
+
constructor(e) {
|
|
314
|
+
if (this.emitter = new t(), this.filterEngine = new n(), this.timelineRenderer = new p(), this.diffRenderer = new l(), this.data = [], this.filteredData = [], this.filter = {}, this.pagination = {
|
|
315
|
+
page: 1,
|
|
316
|
+
pageSize: 20
|
|
317
|
+
}, this.theme = "light", this.expandedIds = /* @__PURE__ */ new Set(), this.filterEl = null, this.timelineEl = null, this.paginationEl = null, this.destroyed = !1, this.requestId = 0, this.abortController = null, "undefined" == typeof document) throw new Error("[LogViewer] A browser document is required");
|
|
318
|
+
const r = "string" == typeof e.container ? document.querySelector(e.container) : e.container;
|
|
319
|
+
if (!r) throw new Error(`[LogViewer] Container not found: ${String(e.container)}`);
|
|
320
|
+
this.config = { ...e }, this.theme = "dark" === e.theme ? "dark" : "light", this.pagination = {
|
|
321
|
+
page: u(e.pagination?.page),
|
|
322
|
+
pageSize: h(e.pageSize ?? e.pagination?.pageSize)
|
|
323
|
+
}, this.filter = i(e.filter || {}), this.data = g(e.data), m(), this.root = document.createElement("div"), this.root.className = "lv-root", this.root.setAttribute("data-theme", this.theme), r.appendChild(this.root), this.bindEvents(), this.applyFilterAndRender(), e.dataSource && this.loadData().catch(() => {});
|
|
324
|
+
}
|
|
325
|
+
getData() {
|
|
326
|
+
return i(this.data);
|
|
327
|
+
}
|
|
328
|
+
setData(e) {
|
|
329
|
+
this.destroyed || (this.data = g(e), this.pagination.page = 1, this.expandedIds.clear(), this.applyFilterAndRender(), this.emitter.emit("data:change", this.getData()));
|
|
330
|
+
}
|
|
331
|
+
appendData(e) {
|
|
332
|
+
this.destroyed || (this.data.push(...g(e)), this.applyFilterAndRender(), this.emitter.emit("data:change", this.getData()));
|
|
333
|
+
}
|
|
334
|
+
getFilter() {
|
|
335
|
+
return i(this.filter);
|
|
336
|
+
}
|
|
337
|
+
setFilter(e) {
|
|
338
|
+
if (this.destroyed) return;
|
|
339
|
+
this.filter = i(e || {}), this.pagination.page = 1, this.applyFilterAndRender();
|
|
340
|
+
const t = this.getFilter();
|
|
341
|
+
this.emitter.emit("filter:change", t), this.config.onFilterChange?.(t);
|
|
342
|
+
}
|
|
343
|
+
resetFilter() {
|
|
344
|
+
this.destroyed || this.setFilter({});
|
|
345
|
+
}
|
|
346
|
+
getTheme() {
|
|
347
|
+
return this.theme;
|
|
348
|
+
}
|
|
349
|
+
setTheme(e) {
|
|
350
|
+
if (!this.destroyed) {
|
|
351
|
+
if ("light" !== e && "dark" !== e) throw new TypeError("[LogViewer] theme must be light or dark");
|
|
352
|
+
this.theme !== e && (this.theme = e, this.root.setAttribute("data-theme", e), this.emitter.emit("theme:change", e), this.config.onThemeChange?.(e));
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
expandEntry(e) {
|
|
356
|
+
if (this.destroyed || this.expandedIds.has(e)) return;
|
|
357
|
+
this.expandedIds.add(e);
|
|
358
|
+
const t = this.data.find((t) => t.id === e);
|
|
359
|
+
t && this.emitter.emit("entry:expand", i(t)), this.renderTimeline();
|
|
360
|
+
}
|
|
361
|
+
collapseEntry(e) {
|
|
362
|
+
if (this.destroyed || !this.expandedIds.has(e)) return;
|
|
363
|
+
this.expandedIds.delete(e);
|
|
364
|
+
const t = this.data.find((t) => t.id === e);
|
|
365
|
+
t && this.emitter.emit("entry:collapse", i(t)), this.renderTimeline();
|
|
366
|
+
}
|
|
367
|
+
collapseAll() {
|
|
368
|
+
this.destroyed || 0 === this.expandedIds.size || (this.expandedIds.clear(), this.renderTimeline());
|
|
369
|
+
}
|
|
370
|
+
goToPage(e) {
|
|
371
|
+
if (this.destroyed) return;
|
|
372
|
+
const t = Math.max(1, Math.ceil(this.filteredData.length / this.pagination.pageSize)), n = Math.min(t, Math.max(1, u(e)));
|
|
373
|
+
n !== this.pagination.page && (this.pagination.page = n, this.emitter.emit("page:change", n), this.renderTimeline());
|
|
374
|
+
}
|
|
375
|
+
getPagination() {
|
|
376
|
+
return {
|
|
377
|
+
...this.pagination,
|
|
378
|
+
total: this.filteredData.length
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
on(e, t) {
|
|
382
|
+
return this.emitter.on(e, t);
|
|
383
|
+
}
|
|
384
|
+
refresh() {
|
|
385
|
+
this.destroyed || this.applyFilterAndRender();
|
|
386
|
+
}
|
|
387
|
+
async loadData() {
|
|
388
|
+
if (this.destroyed || !this.config.dataSource) return;
|
|
389
|
+
const e = ++this.requestId;
|
|
390
|
+
this.abortController?.abort(), this.abortController = "undefined" != typeof AbortController ? new AbortController() : null;
|
|
391
|
+
try {
|
|
392
|
+
let t;
|
|
393
|
+
if ("function" == typeof this.config.dataSource) t = await this.config.dataSource();
|
|
394
|
+
else {
|
|
395
|
+
if ("function" != typeof fetch) throw new Error("[LogViewer] fetch is unavailable");
|
|
396
|
+
const e = await fetch(this.config.dataSource, { signal: this.abortController?.signal });
|
|
397
|
+
if (!e.ok) throw new Error(`[LogViewer] dataSource request failed: ${e.status}`);
|
|
398
|
+
t = await e.json();
|
|
399
|
+
}
|
|
400
|
+
if (this.destroyed || e !== this.requestId) return;
|
|
401
|
+
this.setData(function(e) {
|
|
402
|
+
if (Array.isArray(e)) return e;
|
|
403
|
+
if (!e || "object" != typeof e) throw new TypeError("[LogViewer] dataSource response must be an array");
|
|
404
|
+
const t = e;
|
|
405
|
+
for (const e of [
|
|
406
|
+
"data",
|
|
407
|
+
"logs",
|
|
408
|
+
"items",
|
|
409
|
+
"entries"
|
|
410
|
+
]) if (Array.isArray(t[e])) return t[e];
|
|
411
|
+
throw new TypeError("[LogViewer] dataSource response must contain an array");
|
|
412
|
+
}(t));
|
|
413
|
+
} catch (e) {
|
|
414
|
+
if (this.destroyed || "undefined" != typeof DOMException && e instanceof DOMException && "AbortError" === e.name) return;
|
|
415
|
+
throw this.config.onError?.(e), this.emitter.emit("error", e), e;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
destroy() {
|
|
419
|
+
this.destroyed || (this.destroyed = !0, this.requestId++, this.abortController?.abort(), this.abortController = null, this.emitter.removeAllListeners(), this.expandedIds.clear(), this.root.replaceChildren(), this.root.remove());
|
|
420
|
+
}
|
|
421
|
+
applyFilterAndRender() {
|
|
422
|
+
this.filteredData = this.filterEngine.filter(this.data, this.filter), this.filteredData = this.filteredData.map((e, t) => ({
|
|
423
|
+
entry: e,
|
|
424
|
+
index: t
|
|
425
|
+
})).sort((e, t) => {
|
|
426
|
+
const n = new Date(e.entry.timestamp).getTime(), r = new Date(t.entry.timestamp).getTime(), a = Number.isFinite(n) ? n : Number.MIN_SAFE_INTEGER;
|
|
427
|
+
return (Number.isFinite(r) ? r : Number.MIN_SAFE_INTEGER) - a || e.index - t.index;
|
|
428
|
+
}).map(({ entry: e }) => e);
|
|
429
|
+
const e = Math.max(1, Math.ceil(this.filteredData.length / this.pagination.pageSize));
|
|
430
|
+
this.pagination.page = Math.min(this.pagination.page, e), this.pagination.total = this.filteredData.length, this.renderTimeline();
|
|
431
|
+
}
|
|
432
|
+
getPageData() {
|
|
433
|
+
const { page: e, pageSize: t } = this.pagination, n = (e - 1) * t;
|
|
434
|
+
return this.filteredData.slice(n, n + t);
|
|
435
|
+
}
|
|
436
|
+
renderFilterBar() {
|
|
437
|
+
if (!1 === this.config.showFilter) return null;
|
|
438
|
+
const t = document.createElement("div");
|
|
439
|
+
t.className = "lv-filter", t.setAttribute("role", "search");
|
|
440
|
+
const n = (e, n) => {
|
|
441
|
+
const r = document.createElement("label");
|
|
442
|
+
r.className = "lv-filter-item";
|
|
443
|
+
const a = document.createElement("span");
|
|
444
|
+
a.className = "lv-filter-label", a.textContent = n;
|
|
445
|
+
const i = document.createElement("input");
|
|
446
|
+
i.className = "lv-filter-input", i.type = "search", i.value = String(this.filter[e] || ""), i.setAttribute("data-filter-field", e), i.setAttribute("aria-label", n), r.append(a, i), t.appendChild(r);
|
|
447
|
+
};
|
|
448
|
+
n("keyword", "en-US" === this.config.locale ? "Keyword" : "关键词"), n("user", "en-US" === this.config.locale ? "User" : "用户"), n("module", "en-US" === this.config.locale ? "Module" : "模块"), n("ip", "IP");
|
|
449
|
+
const r = document.createElement("label");
|
|
450
|
+
r.className = "lv-filter-item";
|
|
451
|
+
const a = document.createElement("span");
|
|
452
|
+
a.className = "lv-filter-label", a.textContent = "en-US" === this.config.locale ? "Action" : "操作";
|
|
453
|
+
const i = document.createElement("select");
|
|
454
|
+
i.className = "lv-filter-select", i.setAttribute("data-filter-field", "action"), i.setAttribute("aria-label", a.textContent);
|
|
455
|
+
const o = document.createElement("option");
|
|
456
|
+
o.value = "", o.textContent = "en-US" === this.config.locale ? "All" : "全部", i.appendChild(o);
|
|
457
|
+
for (const [t, n] of Object.entries(e)) {
|
|
458
|
+
const e = document.createElement("option");
|
|
459
|
+
e.value = t, e.textContent = n, i.appendChild(e);
|
|
460
|
+
}
|
|
461
|
+
i.value = Array.isArray(this.filter.action) ? "" : this.filter.action || "", r.append(a, i), t.appendChild(r);
|
|
462
|
+
const l = document.createElement("button");
|
|
463
|
+
return l.type = "button", l.className = "lv-filter-btn", l.setAttribute("data-filter-reset", "true"), l.textContent = "en-US" === this.config.locale ? "Reset" : "重置", t.appendChild(l), t;
|
|
464
|
+
}
|
|
465
|
+
updateFilterField(t, n) {
|
|
466
|
+
const r = this.getFilter();
|
|
467
|
+
n.trim() ? "action" === t && function(t) {
|
|
468
|
+
return Object.keys(e).includes(t);
|
|
469
|
+
}(n) ? r.action = n : "action" !== t && (r[t] = n) : delete r[t], this.setFilter(r);
|
|
470
|
+
}
|
|
471
|
+
renderTimeline() {
|
|
472
|
+
if (this.destroyed) return;
|
|
473
|
+
const e = this.getPageData(), t = this.timelineRenderer.render(e, this.expandedIds, {
|
|
474
|
+
showDiff: this.config.showDiff,
|
|
475
|
+
showAnomalyBadge: this.config.showAnomalyBadge,
|
|
476
|
+
locale: this.config.locale
|
|
477
|
+
});
|
|
478
|
+
if (!1 !== this.config.showDiff) for (const n of e) this.expandedIds.has(n.id) && ([...t.children].find((e) => e.getAttribute("data-id") === n.id)?.querySelector(".lv-timeline-card"))?.appendChild(this.diffRenderer.renderDetail(n));
|
|
479
|
+
this.filterEl?.remove(), this.timelineEl?.remove(), this.paginationEl?.remove(), this.filterEl = this.renderFilterBar(), this.timelineEl = t, this.paginationEl = this.timelineRenderer.renderPagination(this.pagination.page, this.pagination.pageSize, this.filteredData.length), this.filterEl && this.root.appendChild(this.filterEl), this.root.append(this.timelineEl, this.paginationEl);
|
|
480
|
+
}
|
|
481
|
+
bindEvents() {
|
|
482
|
+
this.root.addEventListener("change", (e) => {
|
|
483
|
+
const t = e.target;
|
|
484
|
+
if (!(t instanceof HTMLInputElement || t instanceof HTMLSelectElement)) return;
|
|
485
|
+
const n = t.getAttribute("data-filter-field");
|
|
486
|
+
n && this.updateFilterField(n, t.value);
|
|
487
|
+
}), this.root.addEventListener("click", (e) => {
|
|
488
|
+
const t = e.target;
|
|
489
|
+
if (!(t instanceof Element)) return;
|
|
490
|
+
if (t.closest("[data-filter-reset]")) return void this.resetFilter();
|
|
491
|
+
const n = t.closest(".lv-timeline-expand-hint"), r = (n?.closest(".lv-timeline-item") || t.closest(".lv-timeline-item"))?.getAttribute("data-id");
|
|
492
|
+
if (n && r) return void (this.expandedIds.has(r) ? this.collapseEntry(r) : this.expandEntry(r));
|
|
493
|
+
const a = t.closest(".lv-pagination-btn");
|
|
494
|
+
if (a && !a.disabled) {
|
|
495
|
+
const e = Number(a.getAttribute("data-page"));
|
|
496
|
+
Number.isFinite(e) && this.goToPage(e);
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
if (!t.closest(".lv-timeline-card") || !r) return;
|
|
500
|
+
const o = this.filteredData.find((e) => e.id === r);
|
|
501
|
+
if (o) {
|
|
502
|
+
const e = i(o);
|
|
503
|
+
this.emitter.emit("entry:click", e), this.config.onEntryClick?.(e);
|
|
504
|
+
}
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
exports.ACTION_COLORS = {
|
|
509
|
+
create: "#52c41a",
|
|
510
|
+
update: "#1890ff",
|
|
511
|
+
delete: "#ff4d4f",
|
|
512
|
+
query: "#8c8c8c",
|
|
513
|
+
login: "#722ed1",
|
|
514
|
+
logout: "#eb2f96",
|
|
515
|
+
export: "#fa8c16",
|
|
516
|
+
import: "#13c2c2",
|
|
517
|
+
approve: "#52c41a",
|
|
518
|
+
reject: "#ff4d4f",
|
|
519
|
+
other: "#d9d9d9"
|
|
520
|
+
}, exports.ACTION_LABELS = e, exports.DiffRenderer = l, exports.EventEmitter = t, exports.FilterEngine = n, exports.LogViewer = b, exports.TimelineRenderer = p, exports.VERSION = "0.1.0", exports.createLogViewer = function(e) {
|
|
521
|
+
return new b(e);
|
|
522
|
+
}, exports.debounce = function(e, t) {
|
|
523
|
+
let n = null;
|
|
524
|
+
const r = Number.isFinite(t) && t > 0 ? t : 0;
|
|
525
|
+
return (...t) => {
|
|
526
|
+
n && clearTimeout(n), n = setTimeout(() => {
|
|
527
|
+
n = null, e(...t);
|
|
528
|
+
}, r);
|
|
529
|
+
};
|
|
530
|
+
}, exports.deepClone = i, exports.escapeHtml = function(e) {
|
|
531
|
+
const t = {
|
|
532
|
+
"&": "&",
|
|
533
|
+
"<": "<",
|
|
534
|
+
">": ">",
|
|
535
|
+
"\"": """,
|
|
536
|
+
"'": "'"
|
|
537
|
+
};
|
|
538
|
+
return e.replace(/[&<>"']/g, (e) => t[e] || e);
|
|
539
|
+
}, exports.formatDate = a, exports.formatRelativeTime = function(e) {
|
|
540
|
+
const t = e instanceof Date ? e : new Date(e), n = Date.now() - t.getTime();
|
|
541
|
+
return !Number.isFinite(n) || n < 0 ? a(t) : n < 6e4 ? "刚刚" : n < 36e5 ? `${Math.floor(n / 6e4)} 分钟前` : n < 864e5 ? `${Math.floor(n / 36e5)} 小时前` : n < 6048e5 ? `${Math.floor(n / 864e5)} 天前` : a(t);
|
|
542
|
+
}, exports.formatValue = o, exports.injectStyles = m, exports.uid = function(e = "lv") {
|
|
543
|
+
const t = "undefined" != typeof crypto && "function" == typeof crypto.randomUUID ? crypto.randomUUID().slice(0, 8) : Math.random().toString(36).slice(2, 8);
|
|
544
|
+
return `${e}-${++r}-${t}`;
|
|
545
|
+
};
|
|
546
|
+
});
|
|
547
|
+
/*! End of @composy/log-viewer-core | Powered by @composy/builder */
|
|
548
|
+
//# sourceMappingURL=index.min.js.map
|