// Panel Content Script —— 商品页右下角悬浮按钮 + 页内悬浮面板 // // 面板 = iframe 加载插件内置 sidepanel.html(扩展页面在 iframe 里仍有 chrome.* 权限, // 采集/生成/导出逻辑零改动);按钮与面板容器渲染在独立 Shadow DOM 中, // 不受商品页全局 CSS 影响,面板悬浮覆盖页面、不挤压原页面布局。 import { matchProfile } from '../src/profiles/index'; /** 面板内 App.tsx → 宿主页的收起消息 */ const PANEL_CLOSE_MSG = 'sc-panel-close'; const STYLES = ` :host { all: initial; } .fab { position: fixed; right: 24px; bottom: 24px; width: 48px; height: 48px; border-radius: 50%; border: none; cursor: pointer; display: flex; align-items: center; justify-content: center; font: 700 20px/1 -apple-system, 'PingFang SC', 'Microsoft YaHei', sans-serif; color: #fff; background: linear-gradient(135deg, #8b5cf6, #6d28d9); box-shadow: 0 4px 16px rgba(109, 40, 217, 0.45); z-index: 3; transition: transform 0.15s ease; } .fab:hover { transform: scale(1.08); } .fab.hidden { display: none; } .panel { position: fixed; top: 0; bottom: 0; right: 0; width: min(880px, 100vw); border-radius: 14px 0 0 14px; overflow: hidden; background: #fff; box-shadow: -8px 0 32px rgba(0, 0, 0, 0.25), 0 0 0 1px rgba(0, 0, 0, 0.06); transform: translateX(100%); transition: transform 0.25s ease; z-index: 2; pointer-events: none; } .panel.open { transform: translateX(0); pointer-events: auto; } .panel iframe { width: 100%; height: 100%; border: 0; display: block; background: #fff; } `; export default defineContentScript({ matches: [ // Ozon 'https://*.ozon.ru/*', 'https://*.ozon.kz/*', 'https://*.ozon.by/*', // 1688 'https://detail.1688.com/*', // 淘宝 / 天猫 'https://item.taobao.com/*', 'https://detail.tmall.com/*', ], async main(ctx) { const ui = await createShadowRootUi(ctx, { name: 'suite-studio-panel', position: 'overlay', anchor: 'body', alignment: 'bottom-right', zIndex: 2147483646, css: STYLES, isolateEvents: true, onMount(container) { const fab = document.createElement('button'); fab.className = 'fab hidden'; fab.title = '电商套图工作台'; fab.textContent = '套'; const panel = document.createElement('div'); panel.className = 'panel'; // iframe 懒加载:首次展开才设 src,避免每个商品页都加载整个面板应用 const iframe = document.createElement('iframe'); iframe.title = '电商套图工作台'; panel.append(iframe); container.append(fab, panel); const open = () => { if (!iframe.src) iframe.src = chrome.runtime.getURL('/sidepanel.html'); panel.classList.add('open'); fab.classList.add('hidden'); // 聚焦进面板,键盘操作(Esc 关闭 / 预览翻页)直接可用 iframe.focus(); }; const close = () => { panel.classList.remove('open'); if (isProductPage()) fab.classList.remove('hidden'); }; fab.addEventListener('click', open); // 面板内 App(✕ / Esc)→ 收起 window.addEventListener('message', (e) => { if (e.source === iframe.contentWindow && (e.data as any)?.type === PANEL_CLOSE_MSG) close(); }); // 工具栏图标点击 → 开关面板 chrome.runtime.onMessage.addListener((msg: any) => { if (msg?.action === 'toggle-suite-panel') { panel.classList.contains('open') ? close() : open(); } }); // 仅商品详情页显示按钮;站内软导航后重判(WXT 内置事件,自动拦截 pushState/replaceState/popState) const refresh = () => { if (isProductPage()) { if (!panel.classList.contains('open')) fab.classList.remove('hidden'); } else { fab.classList.add('hidden'); close(); } }; ctx.addEventListener(window, 'wxt:locationchange', refresh); refresh(); return { open, close }; }, }); ui.mount(); }, }); /** 是否为四站点支持的商品详情页(与采集 profile 一致) */ function isProductPage(): boolean { return matchProfile(location.href) !== null; }