(function (global) { 'use strict'; /** * 资源面板(壳侧):Bundle 优先 + 去重内存 + 可预览。 * @constructor */ function AssetsPanel() { this.el = null; this._bridge = null; this._rows = []; this._bundles = []; this._totals = null; this._bundleFilter = ''; this._typeFilter = ''; this._query = ''; this._sort = 'mem'; this._selectedUuid = ''; } AssetsPanel.prototype.setBridge = function (bridge) { this._bridge = bridge; }; AssetsPanel.prototype.mount = function () { var self = this; var root = document.createElement('div'); root.className = 'pw-panel-inner pw-assets'; root.style.height = '100%'; root.style.display = 'flex'; root.style.flexDirection = 'column'; root.innerHTML = '
' + '' + '' + '' + '' + '
' + '
' + '
' + '
' + '
' + '
' + '
' + '
选择资源预览
' + '
' + '
引用来源
选择资源后显示
' + '
'; this.el = root; root.querySelector('#pw-asset-q').addEventListener('input', function (ev) { self._query = (ev.target.value || '').toLowerCase(); self.renderList(); }); root.querySelector('#pw-asset-type').addEventListener('change', function (ev) { self._typeFilter = ev.target.value; self.renderList(); }); root.querySelector('#pw-asset-sort').addEventListener('change', function (ev) { self._sort = ev.target.value; self.renderList(); }); root.querySelector('#pw-asset-refresh').addEventListener('click', function () { if (!self._bridge) return; self._bridge.call('assets.list').then(function (d) { self.applyData(d); }); }); return root; }; AssetsPanel.prototype.applyData = function (data) { this._rows = (data && data.rows) || []; this._bundles = (data && data.bundles) || []; this._totals = (data && data.totals) || null; this.renderSummary(); this.renderBundles(); this.renderList(); }; AssetsPanel.prototype.renderSummary = function () { var el = this.el && this.el.querySelector('#pw-asset-summary'); if (!el) return; var t = this._totals || {}; var f = function (n) { n = Number(n) || 0; return n >= 100 ? Math.round(n) : Math.round(n * 10) / 10; }; el.innerHTML = '
' + (t.bundleCount != null ? t.bundleCount : this._bundles.length) + 'Bundle
' + '
' + (t.assetCount != null ? t.assetCount : this._rows.length) + '资源
' + '
' + f(t.estimateMB) + '估算 MB
' + '
' + f(t.texMB) + '纹理
' + '
' + f(t.gfxTextureMB) + 'GFX纹理
' + '
' + f(t.jsHeapMB) + 'JS堆
' + '
估算=去重纹理+音频+其它;GFX/JS堆为实测
'; }; AssetsPanel.prototype.renderBundles = function () { var el = this.el && this.el.querySelector('#pw-asset-bundles'); if (!el) return; var q = this._query; var list = this._bundles.slice(); if (q) { list = list.filter(function (b) { return String(b.name).toLowerCase().indexOf(q) >= 0; }); } var maxMem = 1; for (var i = 0; i < this._bundles.length; i++) { maxMem = Math.max(maxMem, this._bundles[i].memMB || 0); } var html = ['
Bundle · ' + this._bundles.length + '
']; var allActive = !this._bundleFilter; html.push( '' ); for (var j = 0; j < list.length; j++) { var b = list[j]; var mem = Number(b.memMB) || 0; var pct = maxMem > 0 ? Math.min(100, (mem / maxMem) * 100) : 0; var act = this._bundleFilter === b.name ? ' active' : ''; var st = b.status || (b.assetCount > 0 ? 'loaded' : 'unloaded'); var stCls = st === 'loaded' ? 'st-loaded' : (st === 'loaded-empty' ? 'st-empty' : 'st-unloaded'); var stText = st === 'loaded' ? '已加载' : (st === 'loaded-empty' ? '空缓存' : '未加载'); html.push( '' ); } el.innerHTML = html.join(''); var self = this; var btns = el.querySelectorAll('.pw-bundle-item'); for (var k = 0; k < btns.length; k++) { btns[k].addEventListener('click', function (ev) { var name = ev.currentTarget.getAttribute('data-bundle') || ''; self._bundleFilter = name; self.renderBundles(); self.renderList(); }); } }; AssetsPanel.prototype.renderList = function () { if (!this.el) return; var rows = this._rows.slice(); var q = this._query; var bf = this._bundleFilter; var tf = this._typeFilter; rows = rows.filter(function (r) { if (bf) { var inBundle = r.bundle === bf; var used = r.usedBy && r.usedBy.indexOf(bf) >= 0; if (!inBundle && !used) return false; } if (tf && r.type !== tf && r.kind !== tf) return false; if (q && String(r.name).toLowerCase().indexOf(q) < 0 && String(r.type).toLowerCase().indexOf(q) < 0) return false; return true; }); var sort = this._sort; rows.sort(function (a, b) { if (sort === 'name') return String(a.name).localeCompare(String(b.name)); if (sort === 'type') return String(a.type).localeCompare(String(b.type)) || String(a.name).localeCompare(String(b.name)); if (sort === 'ref') return (b.ref || 0) - (a.ref || 0); return (b.mem || 0) - (a.mem || 0); }); // 类型选项 var typeSel = this.el.querySelector('#pw-asset-type'); if (typeSel) { var types = {}; for (var i = 0; i < this._rows.length; i++) types[this._rows[i].type] = 1; var cur = this._typeFilter; var html = ''; var keys = Object.keys(types).sort(); for (var t = 0; t < keys.length; t++) { html += ''; } typeSel.innerHTML = html; } var listEl = this.el.querySelector('#pw-asset-list'); if (!listEl) return; if (!rows.length) { listEl.innerHTML = '
无匹配资源
'; return; } var out = []; out.push('
名称类型内存引用操作
'); var maxShow = Math.min(rows.length, 400); for (var j = 0; j < maxShow; j++) { var r = rows[j]; var sel = r.uuid === this._selectedUuid ? ' active' : ''; var memText = fmtMB(r.mem); if (r.type === 'SpriteFrame') { memText = (r.w && r.h) ? (r.w + '×' + r.h) : '共享纹理'; } else if (r.type === 'ImageAsset') { memText = (r.w && r.h) ? (r.w + '×' + r.h) : '源像素'; } var tag = ''; if (r.pack) tag += ' 图集'; if (r.type === 'Texture2D' && r.pack) tag += ' 合包'; if (r.via === 'dep') tag += ' 依赖'; out.push( '
' + '' + escapeHtml(r.name) + tag + '' + '' + escapeHtml(r.type) + '' + '' + escapeHtml(memText) + '' + '' + (r.ref || 0) + '' + '' + '' + '' + '
' ); } if (rows.length > maxShow) { out.push('
… 共 ' + rows.length + ' 项,已显示前 ' + maxShow + '
'); } listEl.innerHTML = out.join(''); var self = this; listEl.onclick = function (ev) { var btn = ev.target.closest ? ev.target.closest('button[data-copy]') : null; var item = (ev.target.closest && ev.target.closest('.pw-assets-row[data-uuid]')) || null; if (btn && item) { ev.stopPropagation(); var uuid = item.getAttribute('data-uuid') || ''; var name = (item.querySelector('.pw-assets-name') && item.querySelector('.pw-assets-name').textContent) || ''; if (btn.getAttribute('data-copy') === 'name') copyText(name.trim()); else copyText(uuid); btn.classList.add('copied'); setTimeout(function () { btn.classList.remove('copied'); }, 600); return; } if (!item) return; var u = item.getAttribute('data-uuid'); self._selectedUuid = u; self.renderList(); self.loadPreview(u); }; }; AssetsPanel.prototype.loadPreview = function (uuid) { var self = this; var meta = this.el && this.el.querySelector('#pw-asset-meta'); var box = this.el && this.el.querySelector('#pw-asset-preview'); var refsEl = this.el && this.el.querySelector('#pw-asset-refs'); if (!this._bridge) return; if (box) box.innerHTML = '加载预览…'; if (meta) meta.innerHTML = ''; if (refsEl) refsEl.innerHTML = '…'; this._bridge.call('assets.preview', { uuid: uuid }).then(function (d) { if (!d) return; if (box) { if (d.kind === 'image' && d.url) { box.innerHTML = ''; } else if (d.kind === 'url' && d.url) { box.innerHTML = ''; } else { box.innerHTML = '无预览
' + escapeHtml(d.type || '') + '
'; } } if (meta) { meta.innerHTML = '
名称' + escapeHtml(d.name || '') + '
' + '
UUID' + escapeHtml(d.uuid || '') + '
' + '
Bundle' + escapeHtml(d.bundle || '—') + '
' + '
路径' + escapeHtml(d.path || '—') + '
' + '
类型' + escapeHtml(d.type || '') + '
' + '
引用计数' + (d.ref || 0) + '
' + (d.w && d.h ? '
尺寸' + d.w + '×' + d.h + '
' : ''); meta.onclick = function (ev) { var b = ev.target.closest ? ev.target.closest('[data-copy-val]') : null; if (!b) return; copyText(b.getAttribute('data-copy-val') || ''); }; } if (refsEl) { var refs = d.refs || []; if (!refs.length) { refsEl.innerHTML = '无反向引用'; } else { var html = []; for (var i = 0; i < refs.length; i++) { html.push( '
' + '' + escapeHtml(refs[i].name) + '' + '' + escapeHtml(refs[i].type || '') + '' + '
' ); } refsEl.innerHTML = html.join(''); } refsEl.ondblclick = function (ev) { var item = ev.target.closest ? ev.target.closest('.pw-asset-ref-item') : null; if (!item) return; self._locateRef(item.getAttribute('data-uuid') || '', item.getAttribute('data-type') || ''); }; } }).catch(function () { if (box) box.innerHTML = '预览失败'; }); }; function copyText(text) { try { if (navigator.clipboard && navigator.clipboard.writeText) { navigator.clipboard.writeText(String(text)); return; } } catch (e) { /* */ } var ta = document.createElement('textarea'); ta.value = String(text); document.body.appendChild(ta); ta.select(); try { document.execCommand('copy'); } catch (e2) { /* */ } document.body.removeChild(ta); } /** 双击引用项:资源→左侧列表定位;节点→层级面板选中 */ AssetsPanel.prototype._locateRef = function (uuid, type) { if (!uuid) return; if (type === 'Node') { // 层级选中 + 高亮(走引擎 hier.select → pick.selected 同步) if (this._bridge) { this._bridge.call('hier.select', { uuid: uuid }).catch(function () { /* */ }); this._bridge.call('pick.select', { uuid: uuid }).catch(function () { /* */ }); } return; } // 资源:列表定位并选中 var known = false; for (var i = 0; i < this._rows.length; i++) { if (this._rows[i].uuid === uuid) { known = true; break; } } if (!known) { // 当前筛选下可能不可见:清筛选后再找 this._bundleFilter = ''; this._typeFilter = ''; this._query = ''; var qEl = this.el && this.el.querySelector('#pw-asset-q'); if (qEl) qEl.value = ''; } this._selectedUuid = uuid; this.renderList(); this.loadPreview(uuid); var listEl = this.el && this.el.querySelector('#pw-asset-list'); if (listEl) { var row = listEl.querySelector('.pw-assets-row[data-uuid="' + cssEscape(uuid) + '"]'); if (row && row.scrollIntoView) row.scrollIntoView({ block: 'center' }); } }; function cssEscape(s) { return String(s).replace(/"/g, '\\"'); } function fmtMB(mb) { var n = Number(mb) || 0; if (n <= 0) return '0'; if (n < 0.01) return '<0.01MB'; if (n < 10) return (Math.round(n * 100) / 100) + 'MB'; return (Math.round(n * 10) / 10) + 'MB'; } function escapeHtml(s) { return String(s == null ? '' : s) .replace(/&/g, '&').replace(//g, '>'); } function escapeAttr(s) { return escapeHtml(s).replace(/"/g, '"'); } global.PWAssetsPanel = AssetsPanel; })(window);