Marketplace 排序 + 上架日期控制面板
目前為
// ==UserScript==
// @name FB Marketplace 商品排序
// @namespace http://tampermonkey.net/
// @version 2025-08-29.2
// @description Marketplace 排序 + 上架日期控制面板
// @author Henrik
// @match *://www.facebook.com/*
// @grant none
// ==/UserScript==
(function () {
'use strict';
const PANEL_ID = 'marketplace-control-panel';
let panel = null;
// ---------- 建立控制面板 ----------
function createPanel() {
if (panel) return;
panel = document.createElement('div');
panel.id = PANEL_ID;
panel.style.position = 'fixed';
panel.style.top = '80px';
panel.style.right = '20px';
panel.style.zIndex = '9999';
panel.style.width = '220px';
panel.style.background = 'rgba(255,255,255,0.97)';
panel.style.border = '1px solid #ddd';
panel.style.padding = '10px';
panel.style.borderRadius = '8px';
panel.style.boxShadow = '0 4px 10px rgba(0,0,0,0.2)';
panel.style.fontFamily = 'system-ui, -apple-system, "Segoe UI", Roboto, Arial';
panel.style.fontSize = '14px';
panel.style.color = '#111';
panel.style.display = 'flex';
panel.style.flexDirection = 'column';
panel.style.gap = '8px';
panel.style.cursor = 'move';
// 標題
const title = document.createElement('div');
title.textContent = 'Marketplace 排序';
title.style.fontWeight = '600';
title.style.marginBottom = '4px';
panel.appendChild(title);
// 控制項容器
const container = document.createElement('div');
const controls = [
{
label: '排序方式',
param: 'sortBy',
type: 'select',
options: [
{ text: '推薦', value: 'best_match' },
{ text: '從近到遠', value: 'distance_ascend' },
{ text: '由新到舊', value: 'creation_time_descend' },
{ text: '價格低至高', value: 'price_ascend' },
{ text: '價格高至低', value: 'price_descend' }
]
},
{
label: '上架日期(天)',
param: 'daysSinceListed',
type: 'select',
options: [
{ text: '不限', value: '' },
...Array.from({ length: 30 }, (_, i) => ({ text: `${i + 1} 天內`, value: `${i + 1}` }))
]
}
];
const inputs = {};
controls.forEach(ctrl => {
const label = document.createElement('label');
label.textContent = ctrl.label;
label.style.fontWeight = '500';
container.appendChild(label);
if (ctrl.type === 'select') {
const select = document.createElement('select');
select.style.width = '100%';
select.style.padding = '6px';
select.style.borderRadius = '6px';
select.style.border = '1px solid #ccc';
select.style.fontSize = '13px';
ctrl.options.forEach(opt => {
const o = document.createElement('option');
o.value = opt.value;
o.textContent = opt.text;
select.appendChild(o);
});
container.appendChild(select);
inputs[ctrl.param] = select;
}
});
panel.appendChild(container);
document.body.appendChild(panel);
// 初始化 URL 參數
try {
const url = new URL(window.location.href);
controls.forEach(ctrl => {
const val = url.searchParams.get(ctrl.param);
if (val !== null && inputs[ctrl.param]) inputs[ctrl.param].value = val;
});
} catch (e) {
console.warn('URL 解析失敗', e);
}
// select 改變即更新 URL 並刷新頁面
function updateURL() {
try {
const newUrl = new URL(window.location.href);
Object.entries(inputs).forEach(([param, el]) => {
const value = el.value;
if (value) newUrl.searchParams.set(param, value);
else newUrl.searchParams.delete(param);
});
window.location.href = newUrl.toString();
} catch (e) {
console.error('更新 URL 失敗', e);
}
}
Object.values(inputs).forEach(el => el.addEventListener('change', updateURL));
// 拖曳
dragElement(panel, title);
}
function removePanel() {
const p = document.getElementById(PANEL_ID);
if (p) p.remove();
panel = null;
}
// ---------- 拖曳 ----------
function dragElement(elmnt, handle) {
let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
handle.onmousedown = dragMouseDown;
function dragMouseDown(e) {
e.preventDefault();
pos3 = e.clientX;
pos4 = e.clientY;
document.onmouseup = closeDragElement;
document.onmousemove = elementDrag;
}
function elementDrag(e) {
e.preventDefault();
pos1 = pos3 - e.clientX;
pos2 = pos4 - e.clientY;
pos3 = e.clientX;
pos4 = e.clientY;
elmnt.style.top = Math.max(0, Math.min(window.innerHeight - elmnt.offsetHeight, elmnt.offsetTop - pos2)) + "px";
elmnt.style.left = Math.max(0, Math.min(window.innerWidth - elmnt.offsetWidth, elmnt.offsetLeft - pos1)) + "px";
}
function closeDragElement() {
document.onmouseup = null;
document.onmousemove = null;
}
}
// ---------- 判斷是否顯示 ----------
function checkPanelVisibility() {
const isMarketplacePage = !!window.location.pathname.match(/\/marketplace\//);
const isItemPage = window.location.pathname.includes('/item');
if (isItemPage || !isMarketplacePage) {
if (panel) removePanel();
} else {
if (!panel) createPanel();
}
}
// ---------- SPA 導航支援 ----------
(function () {
const wrapHistory = function (type) {
const orig = history[type];
return function () {
const rv = orig.apply(this, arguments);
setTimeout(checkPanelVisibility, 300);
return rv;
};
};
history.pushState = wrapHistory('pushState');
history.replaceState = wrapHistory('replaceState');
window.addEventListener('popstate', () => setTimeout(checkPanelVisibility, 300));
})();
// 初始檢查
checkPanelVisibility();
})();