// ==UserScript==
// @name Shopee 悬浮按钮
// @namespace http://tampermonkey.net/
// @version 2.1.3
// @license Rayu
// @description 悬浮快捷入口
// @author Rayu
// @match https://seller.shopee.tw/*
// @exclude https://seller.shopee.tw/webchat/conversations
// @match *://shopee.tw/*
// @match *://shopee.ph/*
// @match *://shopee.sg/*
// @match *://shopee.com.my/*
// @icon https://www.wikimedia.org/static/favicon/wikipedia.ico
// @grant GM_addStyle
// @run-at document-end
// @downloadURL none
// ==/UserScript==
(function () {
'use strict';
// 配置中心
const CONFIG = {
BUTTONS: [
{
id: 'shipment-btn',
content: '待
出货',
path: '/portal/sale/shipment?type=toship',
title: '待处理订单'
},
{
id: 'products-btn',
content: '我的
商品',
path: '/portal/product/list/all?page=1&size=48',
title: '商品管理'
},
{
id: 'analytics-btn',
content: '商品
分析',
path: '/datacenter/product/overview',
title: '数据分析'
},
{
id: 'ads-btn',
content: '我的
广告',
path: '/portal/marketing/pas/assembly',
title: '广告管理'
},
{
id: 'campaign-btn',
content: '行销
活动',
path: '/portal/marketing',
title: '营销活动'
},
{
id: 'returns-btn',
content: '退貨
退款',
path: '/portal/sale/return',
title: '退货退款'
},
{
id: 'roi-edit-btn',
content: '修改ROI',
path: 'javascript:void(0);',
title: '一键把本页ROI值全部设为7'
}
],
TRACKING_PATTERN: /-i\.(\d+)\.(\d+)/,
STYLES: `
:root {
--primary-color: #ee4d2d;
--button-size: 52px;
--hover-scale: 1.08;
--gap: 8px;
}
#floating-buttons-container {
position: fixed;
top: 50vh;
right: 50px;
transform: translateY(-50%);
display: flex;
flex-direction: column;
gap: var(--gap);
z-index: 9999;
filter: drop-shadow(0 2px 6px rgba(0,0,0,0.16));
}
.floating-button {
display: flex;
align-items: center;
justify-content: center;
width: var(--button-size);
height: var(--button-size);
background: var(--primary-color);
color: white !important;
border-radius: 8px;
font-size: 15px;
font-weight: 500;
line-height: 1.3;
text-align: center;
text-decoration: none !important;
cursor: pointer;
transition:
transform 0.2s cubic-bezier(0.18, 0.89, 0.32, 1.28),
opacity 0.2s,
background 0.2s;
user-select: none;
}
.floating-button:hover {
transform: scale(var(--hover-scale));
background: #d14327;
opacity: 0.95;
}
.floating-button:active {
transition-duration: 0.1s;
transform: scale(0.96);
}
#roi-left-bottom-btn-container {
position: fixed !important;
left: 30px !important;
bottom: 30px !important;
z-index: 9999 !important;
}
#roi-edit-btn {
width: 110px !important;
height: 38px !important;
border-radius: 8px !important;
font-size: 16px !important;
line-height: 38px !important;
text-align: center !important;
padding: 0 !important;
}
@media (max-width: 768px) {
#floating-buttons-container {
right: 4px;
transform: translateY(-50%) scale(0.82);
gap: 6px;
}
.floating-button {
width: 46px;
height: 46px;
font-size: 14px;
}
}
`
};
// 核心功能模块
class ShopeeEnhancer {
constructor() {
this.observer = null;
this.reg = CONFIG.TRACKING_PATTERN;
this.init();
}
// 判断是否为 ROI 按钮需要出现的页面
isRoiPage() {
// 只要当前路径以 /portal/marketing/pas/product/ 开头
return /^\/portal\/marketing\/pas\/product\//.test(location.pathname);
}
init() {
this.injectStyles();
this.createFloatingButtons();
this.hijackHistoryMethods();
this.sanitizeLinks();
}
injectStyles() {
GM_addStyle(CONFIG.STYLES);
}
createFloatingButtons() {
// 清理老容器
let container = document.getElementById('floating-buttons-container');
if (container) container.remove();
// 新建右侧容器
container = document.createElement('div');
container.id = 'floating-buttons-container';
document.body.appendChild(container);
// 右侧按钮(不含roi-edit-btn)
CONFIG.BUTTONS.filter(btn => btn.id !== 'roi-edit-btn').forEach(btn => {
let a = document.createElement('a');
a.className = 'floating-button';
a.id = btn.id;
a.href = btn.path.startsWith('javascript:') ? '#' : new URL(btn.path, window.location.origin);
a.target = '_blank';
a.rel = 'noopener noreferrer';
a.title = btn.title;
a.innerHTML = btn.content;
container.appendChild(a);
});
// 清理老的左下角ROI容器
let roiContainer = document.getElementById('roi-left-bottom-btn-container');
if (roiContainer) roiContainer.remove();
// 新建左下ROI容器
roiContainer = document.createElement('div');
roiContainer.id = 'roi-left-bottom-btn-container';
document.body.appendChild(roiContainer);
// 只在指定页面插入ROI按钮
if (this.isRoiPage()) {
let roiBtnConf = CONFIG.BUTTONS.find(btn => btn.id === 'roi-edit-btn');
if (roiBtnConf) {
let roiBtn = document.createElement('a');
roiBtn.className = 'floating-button';
roiBtn.id = 'roi-edit-btn';
roiBtn.href = '#';
roiBtn.title = roiBtnConf.title;
roiBtn.innerHTML = roiBtnConf.content;
roiContainer.appendChild(roiBtn);
roiBtn.addEventListener('click', function (e) {
e.preventDefault();
// 选出所有 .eds-switch
let switches = document.querySelectorAll('.eds-switch');
switches.forEach(sw => {
// 如果是开着的则点一下,让它变成关闭。“关”的就不用管它
if (sw.classList.contains('eds-switch--open')) {
sw.click();
}
});
const els = document.querySelectorAll('.roi-edit-popover-container .roi-value');
if (els.length === 0) {
showAutoTip('未发现ROI内容元素!');
return;
}
els.forEach(el => el.innerText = '7');
showAutoTip('全部ROI值已修改为7!');
// 勾选radio
let yesLimitRadio = document.querySelector('.yes-limit input[type="radio"]');
if (yesLimitRadio) {
yesLimitRadio.checked = true;
yesLimitRadio.dispatchEvent(new Event('change', { bubbles: true }));
}
// 填写预算输入框,加延时和原生setter双保险
setTimeout(function () {
let input = document.querySelector('.budget-input .eds-input__input');
if (input) {
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set;
nativeInputValueSetter.call(input, '40');
input.dispatchEvent(new Event('input', { bubbles: true }));
input.dispatchEvent(new Event('change', { bubbles: true }));
} else {
// showAutoTip('未找到预算输入框!'); // 可以开启
}
}, 300);
});
// 自动消失的浮动提示函数
function showAutoTip(msg) {
let tip = document.createElement('div');
tip.innerText = msg;
tip.style.position = 'fixed';
tip.style.left = '50%';
tip.style.top = '30%';
tip.style.transform = 'translate(-50%, -50%)';
tip.style.background = '#23272E';
tip.style.color = '#fff';
tip.style.padding = '14px 38px';
tip.style.borderRadius = '8px';
tip.style.fontSize = '18px';
tip.style.boxShadow = '0 2px 16px rgba(0,0,0,0.17)';
tip.style.zIndex = 999999;
tip.style.opacity = 1;
tip.style.transition = 'opacity 0.6s';
document.body.appendChild(tip);
setTimeout(() => {
tip.style.opacity = 0;
setTimeout(() => tip.remove(), 600);
}, 2000);
}
}
}
}
cleanURL(url) {
const match = url.match(this.reg);
if (!match) return url;
return `${window.location.origin}/product/${match[1]}/${match[2]}`;
}
hijackHistoryMethods() {
const self = this;
const originalPushState = history.pushState;
const originalReplaceState = history.replaceState;
history.pushState = function (state, title, url) {
if (url) url = self.cleanURL(url);
return originalPushState.call(this, state, title, url);
};
history.replaceState = function (state, title, url) {
if (url) url = self.cleanURL(url);
return originalReplaceState.call(this, state, title, url);
};
}
sanitizeLinks() {
// 当前页面处理
if (this.reg.test(window.location.href)) {
window.location.replace(this.cleanURL(window.location.href));
return;
}
// 初始化清理
this.processLinks(document);
// 动态内容监控
this.observer = new MutationObserver(mutations => {
mutations.forEach(({ addedNodes }) => {
addedNodes.forEach(node => {
if (node.nodeType === Node.ELEMENT_NODE) {
this.processLinks(node);
}
});
});
});
this.observer.observe(document, {
subtree: true,
childList: true
});
}
processLinks(root) {
const links = root.querySelectorAll('a[href*="-i."]');
links.forEach(link => {
link.href = this.cleanURL(link.href);
});
}
}
// 启动
const enhancer = new ShopeeEnhancer();
// 单页应用路由变更时保持悬浮按钮
let lastUrl = location.href;
setInterval(() => {
if (location.href !== lastUrl) {
lastUrl = location.href;
enhancer.createFloatingButtons();
}
}, 1000);
})();