消耗优化

master
humingxing 2 days ago
parent 47e32b3449
commit d67df295bd
  1. 2
      assets/script/battle/ai/runtime/AiHeadlessBagHost.ts
  2. 18
      assets/script/battle/bag/BagPrepConfig.ts
  3. 22
      assets/script/battle/bag/BattleBagSession.ts
  4. 34
      assets/script/ui/PopDailyShopLottery/DailyShopLotteryIcon.ts
  5. 85
      preview-template/workbench/css/workbench.css
  6. 203
      preview-template/workbench/js/agent-assets.js
  7. 163
      preview-template/workbench/js/panels/assets.js
  8. BIN
      配置表/excelList/常量表.xlsx

@ -154,7 +154,7 @@ export class AiHeadlessBagHost {
}
}
/** 下次召唤费用(Const 4/5) */
/** 下次召唤费用(按当前空开启格相对满额 5 格折算) */
get nextRefreshCost(): number {
return this._bagSession?.normalRefreshCost ?? -1;
}

@ -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);
}

@ -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) {

@ -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);
}
}

@ -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;

@ -455,44 +455,213 @@
};
}
function buildPreview(asset, type) {
try {
/** 更稳的位图预览:HTMLImage / ImageBitmap / ImageData / nativeUrl */
function imageFromAsset(asset, type) {
if (type === 'SpriteFrame') {
var tex = asset.texture || (asset.getTexture && asset.getTexture());
var img = tex && tex.image;
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 {
var pack = imageFromAsset(asset, type);
var img = pack.img;
var data = img && (img._data || img.data);
var rect = asset.rect || asset._rect || { x: 0, y: 0, width: asset.width, height: asset.height };
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') };
}
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 || '' };
// 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) { /* */ }
}
// 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);

@ -47,6 +47,7 @@
'<div class="pw-assets-side">' +
'<div class="pw-assets-preview" id="pw-asset-preview"><span class="pw-muted">选择资源预览</span></div>' +
'<div id="pw-asset-meta" class="pw-assets-meta"></div>' +
'<div class="pw-assets-refs"><h5>引用来源</h5><div id="pw-asset-refs"><span class="pw-muted">选择资源后显示</span></div></div>' +
'</div></div></div>';
this.el = root;
@ -194,7 +195,7 @@
return;
}
var out = [];
out.push('<div class="pw-assets-row pw-assets-row-head"><span>名称</span><span>类型</span><span>内存</span></div>');
out.push('<div class="pw-assets-row pw-assets-row-head"><span>名称</span><span>类型</span><span>内存</span><span>引用</span><span>操作</span></div>');
var maxShow = Math.min(rows.length, 400);
for (var j = 0; j < maxShow; j++) {
var r = rows[j];
@ -214,7 +215,11 @@
'<span class="pw-assets-name">' + escapeHtml(r.name) + tag + '</span>' +
'<span class="pw-assets-type">' + escapeHtml(r.type) + '</span>' +
'<span class="pw-assets-mem">' + escapeHtml(memText) + '</span>' +
'</div>'
'<span class="pw-assets-ref">' + (r.ref || 0) + '</span>' +
'<span class="pw-res-ops">' +
'<button type="button" class="pw-btn pw-btn-mini" data-copy="name" title="复制名称">名</button>' +
'<button type="button" class="pw-btn pw-btn-mini" data-copy="uuid" title="复制 UUID">ID</button>' +
'</span></div>'
);
}
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; }
var refsEl = this.el && this.el.querySelector('#pw-asset-refs');
if (!this._bridge) return;
if (box) box.innerHTML = '<span class="pw-muted">加载预览…</span>';
if (meta) meta.innerHTML = '';
if (refsEl) refsEl.innerHTML = '<span class="pw-muted">…</span>';
this._bridge.call('assets.preview', { uuid: uuid }).then(function (d) {
if (!d) return;
if (box) {
if (d.kind === 'image' && d.url) {
box.innerHTML = '<img alt="" src="' + d.url + '" />';
} else if (d.kind === 'url' && d.url) {
box.innerHTML = '<img alt="" src="' + d.url + '" />';
} else {
box.innerHTML = '<span class="pw-muted">无预览<br><i>' + escapeHtml(d.type || '') + '</i></span>';
}
if (meta && row) {
}
if (meta) {
meta.innerHTML =
'<div><b>' + escapeHtml(row.name) + '</b></div>' +
'<div class="pw-muted">' + escapeHtml(row.type) + ' · ' + escapeHtml(row.bundle) + '</div>' +
'<div class="pw-muted">' +
(row.type === 'SpriteFrame' || row.type === 'ImageAsset'
? ((row.w && row.h) ? (row.w + '×' + row.h) : '')
: fmtMB(row.mem)) +
' · 引用 ' + (row.ref || 0) + '</div>';
'<div class="pw-asset-kv"><span>名称</span><b>' + escapeHtml(d.name || '') +
' <button type="button" class="pw-btn pw-btn-mini" data-copy-val="' + escapeAttr(d.name || '') + '">复制</button></b></div>' +
'<div class="pw-asset-kv"><span>UUID</span><b class="pw-mono">' + escapeHtml(d.uuid || '') +
' <button type="button" class="pw-btn pw-btn-mini" data-copy-val="' + escapeAttr(d.uuid || '') + '">复制</button></b></div>' +
'<div class="pw-asset-kv"><span>Bundle</span><b>' + escapeHtml(d.bundle || '—') + '</b></div>' +
'<div class="pw-asset-kv"><span>路径</span><b>' + escapeHtml(d.path || '—') + '</b></div>' +
'<div class="pw-asset-kv"><span>类型</span><b>' + escapeHtml(d.type || '') + '</b></div>' +
'<div class="pw-asset-kv"><span>引用计数</span><b>' + (d.ref || 0) + '</b></div>' +
(d.w && d.h ? '<div class="pw-asset-kv"><span>尺寸</span><b>' + d.w + '×' + d.h + '</b></div>' : '');
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 (!box || !this._bridge) return;
box.innerHTML = '<span class="pw-muted">加载预览…</span>';
this._bridge.call('assets.preview', { uuid: uuid }).then(function (p) {
if (!box) return;
if (p && p.kind === 'image' && p.url) {
box.innerHTML = '<img alt="" src="' + p.url + '" />';
} else if (p && p.kind === 'bitmap') {
box.innerHTML = '<span class="pw-muted">Bitmap ' + (p.w || 0) + '×' + (p.h || 0) + '</span>';
} else if (p && p.kind === 'url' && p.url) {
box.innerHTML = '<img alt="" src="' + p.url + '" />';
if (refsEl) {
var refs = d.refs || [];
if (!refs.length) {
refsEl.innerHTML = '<span class="pw-muted">无反向引用</span>';
} else {
box.innerHTML = '<span class="pw-muted">无预览</span>';
var html = [];
for (var i = 0; i < refs.length; i++) {
html.push(
'<div class="pw-asset-ref-item" data-uuid="' + escapeAttr(refs[i].uuid || '') +
'" data-type="' + escapeAttr(refs[i].type || '') +
'" title="双击定位 · ' + escapeAttr(refs[i].uuid || '') + '">' +
'<b>' + escapeHtml(refs[i].name) + '</b>' +
'<i class="pw-muted">' + escapeHtml(refs[i].type || '') + '</i>' +
'</div>'
);
}
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 = '<span class="pw-muted">预览失败</span>';
});
};
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';

Loading…
Cancel
Save