diff --git a/assets/script/battle/ai/runtime/AiHeadlessBagHost.ts b/assets/script/battle/ai/runtime/AiHeadlessBagHost.ts index 361de19..270ae94 100644 --- a/assets/script/battle/ai/runtime/AiHeadlessBagHost.ts +++ b/assets/script/battle/ai/runtime/AiHeadlessBagHost.ts @@ -154,7 +154,7 @@ export class AiHeadlessBagHost { } } - /** 下次召唤费用(Const 4/5) */ + /** 下次召唤费用(按当前空开启格相对满额 5 格折算) */ get nextRefreshCost(): number { return this._bagSession?.normalRefreshCost ?? -1; } diff --git a/assets/script/battle/bag/BagPrepConfig.ts b/assets/script/battle/bag/BagPrepConfig.ts index c22f772..9d51bcb 100644 --- a/assets/script/battle/bag/BagPrepConfig.ts +++ b/assets/script/battle/bag/BagPrepConfig.ts @@ -175,3 +175,21 @@ export function applyWanReduceToRefreshCost(tierCost: number, reduceWan: number) const reduced = cost - Math.floor(cost * wan / 10000); return reduced > 0 ? Math.max(1, reduced) : 0; } + +/** 召唤按钮一次最多补的空开启格数量(满额对应 Const 刷新消耗) */ +export const BAG_SUMMON_SLOT_MAX = 5; + +/** + * 按实际补格数折算召唤消耗(满额 5 格付满费;3 格 → floor(满费×3/5))。 + * @param fullCost 满额消耗(已含省金等减免) + * @param fillCount 本次实际补入空格数 + * @param maxCount 满额格数,默认 {@link BAG_SUMMON_SLOT_MAX} + */ +export function scaleBagRefreshCostByFillCount(fullCost: number, fillCount: number, maxCount: number = BAG_SUMMON_SLOT_MAX): number { + const full = Math.max(0, Math.floor(fullCost)); + const max = Math.max(1, Math.floor(maxCount)); + const fill = Math.max(0, Math.min(max, Math.floor(fillCount))); + if (full <= 0 || fill <= 0) return 0; + if (fill >= max) return full; + return Math.floor((full * fill) / max); +} diff --git a/assets/script/battle/bag/BattleBagSession.ts b/assets/script/battle/bag/BattleBagSession.ts index e6b0bed..c736340 100644 --- a/assets/script/battle/bag/BattleBagSession.ts +++ b/assets/script/battle/bag/BattleBagSession.ts @@ -27,7 +27,7 @@ import { logBagRefreshDebug, logBagRefreshSection, } from '../debug/BagRefreshDebugLog'; -import { getDiscountedBagNormalRefreshCost } from './BagPrepConfig'; +import { BAG_SUMMON_SLOT_MAX, getDiscountedBagNormalRefreshCost, scaleBagRefreshCostByFillCount } from './BagPrepConfig'; import { MapPlaceValidator } from './MapPlaceValidator'; import { MapDragGuideHelper } from '../view/MapDragGuideHelper'; import { TextListCellState } from '../view/TextListLandHost'; @@ -59,8 +59,8 @@ const BATTLE_CARD_MULTIPLIER_WEAPON2_ID = 910001; const BATTLE_CARD_ATTR_WEAPON2_ID = 910002; import { getTowerLevelDownHost } from '../combat/TowerLevelDownHost'; import { getTowerRuinHost } from '../combat/TowerRuinHost'; -/** 召唤按钮一次最多补的空开启格数量 */ -const REFRESH_EMPTY_MAX = 5; +/** 召唤按钮一次最多补的空开启格数量(与 BagPrepConfig.BAG_SUMMON_SLOT_MAX 一致) */ +const REFRESH_EMPTY_MAX = BAG_SUMMON_SLOT_MAX; /** 项目层背包会话:桥接 MergeSession 与 MapManager */ export class BattleBagSession { @@ -185,7 +185,19 @@ export class BattleBagSession { return this.merge.compositions; } + /** + * 本次召唤消耗:满额按 Const(含省金),再按当前空开启格数 / 5 折算。 + * 无空格时为 0;按钮预览与扣费共用。 + */ get normalRefreshCost(): number { + const full = this._fullSummonCost(); + if (!this.landStateOf) return full; + const fill = Math.min(REFRESH_EMPTY_MAX, this._emptyOpenSlots().length); + return scaleBagRefreshCostByFillCount(full, fill); + } + + /** 满 5 格召唤档位消耗(Const + 省金,未按空位折算) */ + private _fullSummonCost(): number { const reduceWan = resolveCarriedSummonCostReduceWan(this._currentCarryPassives()).reduceWan; return getDiscountedBagNormalRefreshCost(this._normalRefreshUsed, reduceWan); } @@ -462,7 +474,7 @@ export class BattleBagSession { if (!this.canNormalRefreshFill()) { return 'pool_empty'; } - const cost = this.normalRefreshCost; + const cost = this._fullSummonCost(); if (slot.gold < cost) return 'not_enough'; if (!slot.spendGold(cost)) return 'not_enough'; if (this._ownerPlayerId === 0) { @@ -540,7 +552,7 @@ export class BattleBagSession { console.log('[BattleBagSession] tryFillEmptyOpenSlots: player slot missing', this._ownerPlayerId); return 'failed'; } - const cost = this.normalRefreshCost; + const cost = scaleBagRefreshCostByFillCount(this._fullSummonCost(), count); if (slot.gold < cost) return 'not_enough'; if (!slot.spendGold(cost)) return 'not_enough'; if (this._ownerPlayerId === 0) { diff --git a/assets/script/ui/PopDailyShopLottery/DailyShopLotteryIcon.ts b/assets/script/ui/PopDailyShopLottery/DailyShopLotteryIcon.ts index 2e1b39e..ba5b7b7 100644 --- a/assets/script/ui/PopDailyShopLottery/DailyShopLotteryIcon.ts +++ b/assets/script/ui/PopDailyShopLottery/DailyShopLotteryIcon.ts @@ -380,7 +380,7 @@ export class DailyShopOwnedDesc { const nameNode = descBg.getChildByName('name'); this._setLabel(nameNode, parsed.name); DailyShopLotteryIcon.applyNameOutline(nameNode?.getComponent(Label), parsed.quality); - this._setLabel(descBg.getChildByName('desc'), DailyShopLotteryIcon.battleBuffDesc(parsed)); + this._setDescLabel(descBg, DailyShopLotteryIcon.battleBuffDesc(parsed)); this._raiseDescLayer(descRoot); descRoot.active = true; } @@ -471,4 +471,36 @@ export class DailyShopOwnedDesc { const lb = node.getComponent(Label); if (lb) lb.string = text; } + + /** + * 描述文案:固定宽度、按行增高,避免预制体 CLAMP+矮框裁切多行。 + * @param descBg 描述底板 + * @param text 描述全文 + */ + private _setDescLabel(descBg: Node, text: string): void { + const descNode = descBg.getChildByName('desc'); + if (!descNode?.isValid) { + console.log('[DailyShop] descBg 缺少 desc'); + return; + } + const lb = descNode.getComponent(Label); + const ut = descNode.getComponent(UITransform); + if (!lb || !ut) { + console.log('[DailyShop] desc 缺少 Label/UITransform'); + return; + } + const width = ut.contentSize.width; + if (!(width > 0)) { + console.log('[DailyShop] desc 宽度无效', width); + return; + } + lb.overflow = Label.Overflow.RESIZE_HEIGHT; + lb.enableWrapText = true; + if (!(lb.lineHeight > lb.fontSize)) { + lb.lineHeight = lb.fontSize + 6; + } + ut.setContentSize(width, ut.contentSize.height); + lb.string = text; + lb.updateRenderData(true); + } } diff --git a/preview-template/workbench/css/workbench.css b/preview-template/workbench/css/workbench.css index 717ed4f..c941aea 100644 --- a/preview-template/workbench/css/workbench.css +++ b/preview-template/workbench/css/workbench.css @@ -1080,7 +1080,7 @@ body { .pw-assets-row { display: grid; - grid-template-columns: 1fr 110px 72px; + grid-template-columns: 1fr 90px 72px 36px 72px; gap: 6px; align-items: center; padding: 4px 10px; @@ -1127,6 +1127,89 @@ body { font-variant-numeric: tabular-nums; } +.pw-assets-ref { + font-family: var(--pw-font-mono); + font-size: 11px; + text-align: right; + color: var(--pw-info); + font-variant-numeric: tabular-nums; +} + +.pw-assets-meta .pw-asset-kv { + display: grid; + grid-template-columns: 56px 1fr; + gap: 6px; + margin-bottom: 4px; + font-size: 11px; + line-height: 1.45; +} + +.pw-assets-meta .pw-asset-kv span { + color: var(--pw-muted); +} + +.pw-assets-meta .pw-asset-kv b { + font-weight: 500; + word-break: break-all; + color: var(--pw-text-2); +} + +.pw-mono { + font-family: var(--pw-font-mono); + font-size: 10px; +} + +.pw-assets-refs { + border-top: 1px solid var(--pw-border); + padding: 6px 8px 8px; + max-height: 160px; + overflow: auto; +} + +.pw-assets-refs h5 { + margin: 0 0 4px; + font-size: 10px; + color: var(--pw-muted); + font-weight: 600; +} + +.pw-asset-ref-item { + padding: 3px 4px; + border-bottom: 1px dashed rgba(255, 255, 255, 0.06); + font-size: 11px; + cursor: pointer; + border-radius: 3px; +} + +.pw-asset-ref-item:hover { + background: rgba(47, 111, 237, 0.15); +} + +.pw-asset-ref-item::after { + content: '双击定位'; + float: right; + font-size: 9px; + color: var(--pw-muted); + opacity: 0; +} + +.pw-asset-ref-item:hover::after { + opacity: 1; +} + +.pw-asset-ref-item b { + display: block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.pw-btn-mini.copied { + background: var(--pw-ok); + border-color: var(--pw-ok); + color: #fff; +} + .pw-assets-tag { display: inline-block; margin-left: 4px; diff --git a/preview-template/workbench/js/agent-assets.js b/preview-template/workbench/js/agent-assets.js index 467a1eb..568f557 100644 --- a/preview-template/workbench/js/agent-assets.js +++ b/preview-template/workbench/js/agent-assets.js @@ -455,44 +455,213 @@ }; } + /** 更稳的位图预览:HTMLImage / ImageBitmap / ImageData / nativeUrl */ + function imageFromAsset(asset, type) { + if (type === 'SpriteFrame') { + var tex = asset.texture || (asset.getTexture && asset.getTexture()); + return { + img: (tex && tex.image) || (tex && tex._image) || null, + rect: asset.rect || asset._rect || null, + nativeUrl: (tex && tex.image && tex.image.nativeUrl) || (tex && tex.nativeUrl) || asset.nativeUrl || '', + }; + } + if (type === 'ImageAsset') { + return { img: asset, rect: null, nativeUrl: asset.nativeUrl || '' }; + } + var im = asset.image || asset._image || null; + return { + img: im, + rect: null, + nativeUrl: (im && im.nativeUrl) || asset.nativeUrl || asset._nativeUrl || '', + }; + } + function buildPreview(asset, type) { try { - if (type === 'SpriteFrame') { - var tex = asset.texture || (asset.getTexture && asset.getTexture()); - var img = tex && tex.image; - var data = img && (img._data || img.data); - var rect = asset.rect || asset._rect || { x: 0, y: 0, width: asset.width, height: asset.height }; - if (data && typeof HTMLImageElement !== 'undefined' && data instanceof HTMLImageElement) { + var pack = imageFromAsset(asset, type); + var img = pack.img; + var data = img && (img._data || img.data); + var rect = pack.rect; + + // HTMLImageElement + if (data && typeof HTMLImageElement !== 'undefined' && data instanceof HTMLImageElement) { + if (rect && type === 'SpriteFrame') { var ih = data.height || img.height || 0; var sw = Math.max(1, rect.width | 0); var sh = Math.max(1, rect.height | 0); var cv = document.createElement('canvas'); cv.width = sw; cv.height = sh; - cv.getContext('2d').drawImage(data, rect.x | 0, Math.max(0, ih - (rect.y | 0) - sh) | 0, sw, sh, 0, 0, sw, sh); + cv.getContext('2d').drawImage( + data, + rect.x | 0, + Math.max(0, ih - (rect.y | 0) - sh) | 0, + sw, sh, 0, 0, sw, sh + ); return { kind: 'image', w: sw, h: sh, url: cv.toDataURL('image/png') }; } - return { kind: 'none' }; + return { kind: 'image', w: data.width, h: data.height, url: data.src || '' }; + } + + // ImageBitmap:画到 canvas + if (data && typeof ImageBitmap !== 'undefined' && data instanceof ImageBitmap) { + var c2 = document.createElement('canvas'); + c2.width = data.width; + c2.height = data.height; + c2.getContext('2d').drawImage(data, 0, 0); + return { kind: 'image', w: data.width, h: data.height, url: c2.toDataURL('image/png') }; + } + + // ImageData + if (data && data.data && data.width && typeof ImageData !== 'undefined') { + var c3 = document.createElement('canvas'); + c3.width = data.width; + c3.height = data.height; + c3.getContext('2d').putImageData(new ImageData(data.data, data.width, data.height), 0, 0); + return { kind: 'image', w: data.width, h: data.height, url: c3.toDataURL('image/png') }; + } + + // TypedArray / ArrayBuffer 位图 + if (data && data.buffer && data.width && data.height && typeof Uint8Array !== 'undefined') { + var c4 = document.createElement('canvas'); + c4.width = data.width; + c4.height = data.height; + var ctx4 = c4.getContext('2d'); + try { + ctx4.putImageData(new ImageData(new Uint8ClampedArray(data.buffer, data.byteOffset || 0, data.width * data.height * 4), data.width, data.height), 0, 0); + return { kind: 'image', w: data.width, h: data.height, url: c4.toDataURL('image/png') }; + } catch (e1) { /* */ } } - var im = type === 'ImageAsset' ? asset : (asset.image || asset._image); - var d2 = im && (im._data || im.data); - if (d2 && typeof HTMLImageElement !== 'undefined' && d2 instanceof HTMLImageElement) { - return { kind: 'image', w: d2.width, h: d2.height, url: d2.src || '' }; + + // nativeUrl + if (pack.nativeUrl) { + return { + kind: 'url', + url: pack.nativeUrl, + w: (img && img.width) || asset.width || 0, + h: (img && img.height) || asset.height || 0, + }; + } + if (asset.nativeUrl) { + return { kind: 'url', url: asset.nativeUrl, w: asset.width | 0, h: asset.height | 0 }; } - if (im && im.nativeUrl) return { kind: 'url', url: im.nativeUrl, w: im.width | 0, h: im.height | 0 }; - if (asset.nativeUrl) return { kind: 'url', url: asset.nativeUrl, w: asset.width | 0, h: asset.height | 0 }; } catch (e) { /* */ } return { kind: 'none' }; } - function previewAsset(uuid) { + /** 反向引用:谁依赖了 uuid(资源/节点名) */ + function reverseRefs(uuid, cc) { + var deps = []; + var depUtil = cc && cc.assetManager && cc.assetManager.dependUtil; + var assets = cc && cc.assetManager && cc.assetManager.assets; + if (!assets || !assets.forEach) return deps; + try { + assets.forEach(function (asset, id) { + if (!id || String(id) === String(uuid)) return; + var list = null; + try { + if (depUtil && depUtil.getDeps) list = depUtil.getDeps(id); + } catch (e) { list = null; } + if (!list || !list.length) return; + for (var i = 0; i < list.length; i++) { + if (String(list[i]) === String(uuid)) { + deps.push({ + uuid: String(id), + name: asset && asset.name ? asset.name : String(id), + type: fmtType(asset), + }); + break; + } + } + }); + } catch (e2) { /* */ } + // 场景节点名:遍历当前场景找引用(spriteFrame/texture 等) + try { + if (cc.director && cc.director.getScene && cc.UITransform) { + var scene = cc.director.getScene(); + var walkN = function (node) { + if (!node) return; + var comps = node.components || []; + for (var c = 0; c < comps.length; c++) { + var comp = comps[c]; + if (!comp) continue; + var vals = ['spriteFrame', 'texture', 'spriteAtlas', 'skeletonData', 'clip', 'audioClip']; + for (var v = 0; v < vals.length; v++) { + var a = comp[vals[v]]; + var au = a && (a._uuid || a.uuid); + if (au && String(au) === String(uuid)) { + deps.push({ + uuid: node.uuid || '', + name: (node.name || '?') + ' / ' + (comp.constructor && comp.constructor.name || ''), + type: 'Node', + }); + break; + } + } + } + var ch = node.children || []; + for (var k = 0; k < ch.length; k++) walkN(ch[k]); + }; + walkN(scene); + } + } catch (e3) { /* */ } + return deps.slice(0, 40); + } + + /** 详情:预览 + 路径 + 反向引用 */ + function assetDetail(uuid) { var cc = global.cc; var asset = null; try { asset = cc.assetManager.assets.get(uuid); } catch (e) { /* */ } - if (!asset) return { kind: 'none' }; - return buildPreview(asset, fmtType(asset)); + if (!asset) { + return { kind: 'none', name: '', bundle: '', path: '', uuid: uuid, ref: 0, usedBy: [], refs: [] }; + } + var type = fmtType(asset); + var preview = buildPreview(asset, type); + var path = ''; + try { + cc.assetManager.bundles.forEach(function (bundle) { + if (path) return; + if (typeof bundle.getAssetInfo === 'function') { + var inf = bundle.getAssetInfo(uuid); + if (inf && inf.path) path = inf.path; + } + }); + } catch (e2) { /* */ } + var bundleName = ''; + try { + cc.assetManager.bundles.forEach(function (bundle, name) { + if (bundleName) return; + if (bundle && bundle.cache && bundle.cache.has && bundle.cache.has(uuid)) { + bundleName = name || bundle.name || ''; + } + }); + } catch (e3) { /* */ } + return { + kind: preview.kind, + url: preview.url || '', + w: preview.w || 0, + h: preview.h || 0, + name: asset.name || uuid, + type: type, + bundle: bundleName, + path: path, + uuid: uuid, + ref: asset.refCount != null ? asset.refCount : 0, + refs: reverseRefs(uuid, cc), + }; + } + + function previewAsset(uuid) { + var d = assetDetail(uuid); + return { + kind: d.kind, + url: d.url, + w: d.w, + h: d.h, + }; } if (global.PWAgent) { @@ -508,12 +677,14 @@ } }; global.PWAgent.prototype._previewAsset = function (uuid) { - return previewAsset(uuid); + return assetDetail(uuid); }; } global.PWAssetsUtil = { collectAssets: collectAssets, previewAsset: previewAsset, + assetDetail: assetDetail, + buildPreview: buildPreview, }; })(window); diff --git a/preview-template/workbench/js/panels/assets.js b/preview-template/workbench/js/panels/assets.js index 59ada5f..c6633dd 100644 --- a/preview-template/workbench/js/panels/assets.js +++ b/preview-template/workbench/js/panels/assets.js @@ -47,6 +47,7 @@ '
' + '
选择资源预览
' + '
' + + '
引用来源
选择资源后显示
' + '
'; this.el = root; @@ -194,7 +195,7 @@ return; } var out = []; - out.push('
名称类型内存
'); + out.push('
名称类型内存引用操作
'); var maxShow = Math.min(rows.length, 400); for (var j = 0; j < maxShow; j++) { var r = rows[j]; @@ -214,7 +215,11 @@ '' + escapeHtml(r.name) + tag + '' + '' + escapeHtml(r.type) + '' + '' + escapeHtml(memText) + '' + - '' + '' + (r.ref || 0) + '' + + '' + + '' + + '' + + '' ); } if (rows.length > maxShow) { @@ -222,53 +227,147 @@ } listEl.innerHTML = out.join(''); var self = this; - var items = listEl.querySelectorAll('.pw-assets-row[data-uuid]'); - for (var k = 0; k < items.length; k++) { - items[k].addEventListener('click', function (ev) { - var uuid = ev.currentTarget.getAttribute('data-uuid'); - self._selectedUuid = uuid; - self.renderList(); - self.loadPreview(uuid); - }); - } + 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 row = null; - for (var i = 0; i < this._rows.length; i++) { - if (this._rows[i].uuid === uuid) { row = this._rows[i]; break; } - } - if (meta && row) { - meta.innerHTML = - '
' + escapeHtml(row.name) + '
' + - '
' + escapeHtml(row.type) + ' · ' + escapeHtml(row.bundle) + '
' + - '
' + - (row.type === 'SpriteFrame' || row.type === 'ImageAsset' - ? ((row.w && row.h) ? (row.w + '×' + row.h) : '') - : fmtMB(row.mem)) + - ' · 引用 ' + (row.ref || 0) + '
'; - } - if (!box || !this._bridge) return; - box.innerHTML = '加载预览…'; - this._bridge.call('assets.preview', { uuid: uuid }).then(function (p) { - if (!box) return; - if (p && p.kind === 'image' && p.url) { - box.innerHTML = ''; - } else if (p && p.kind === 'bitmap') { - box.innerHTML = 'Bitmap ' + (p.w || 0) + '×' + (p.h || 0) + ''; - } else if (p && p.kind === 'url' && p.url) { - box.innerHTML = ''; - } else { - box.innerHTML = '无预览'; + 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'; diff --git a/配置表/excelList/常量表.xlsx b/配置表/excelList/常量表.xlsx index 7ec075c..876f17f 100644 Binary files a/配置表/excelList/常量表.xlsx and b/配置表/excelList/常量表.xlsx differ