// ==UserScript==
// @name Nidin(你訂) 訂單一鍵複製 & 下載
// @namespace https://aliyang.gf.qytechs.cn
// @version 1.1
// @description 於 Nidin(你訂) 訂單頁面加入「複製」與「匯出 CSV」按鈕,方便貼到 Excel/Google Sheets 或下載保存
// @author aliyang
// @match https://order.nidin.shop/orderListInfo/*
// @grant none
// @license MIT © 2025 Ali Yang
// ==/UserScript==
(() => {
'use strict';
/** 等待商品列表與小計列渲染完成後插入按鈕 */
const wait = setInterval(() => {
const rows = document.querySelectorAll('.prod-list');
const subtotal = document.querySelector('.subtotal_price');
if (rows.length && subtotal) {
clearInterval(wait);
addButtons(rows, subtotal);
}
}, 350);
/** 新增「複製」與「匯出」按鈕 */
function addButtons(rows, subtotal) {
const wrapper = document.createElement('div');
wrapper.style.cssText =
'display:inline-flex;gap:8px;margin-left:8px;vertical-align:middle;';
wrapper.appendChild(createButton('複製', () => copyToClipboard(buildTable(rows, subtotal))));
wrapper.appendChild(
createButton('匯出 CSV', () => downloadCsv(arrayToCsv(buildTable(rows, subtotal)), 'order.csv'))
);
(subtotal.parentElement || document.body).appendChild(wrapper);
}
/** 建立按鈕元素 */
const createButton = (text, handler) =>
Object.assign(document.createElement('button'), {
textContent: text,
style:
'padding:4px 12px;border:1px solid #888;border-radius:4px;'+
'background:#1976d2;color:#fff;cursor:pointer;font-size:14px;',
onclick: handler
});
/* ---------------- 資料處理 ---------------- */
/** 將畫面中的訂單資訊轉成二維陣列 */
function buildTable(rows, subtotal) {
const table = [['品項', '額外資訊(冰/糖/加料)', '價格', '訂購者', '數量']];
rows.forEach(row => {
const name = row.querySelector('.text-bold')?.textContent.trim() || '';
const detail = row.querySelector('.prod-detail-font span')?.textContent.trim() || '';
const extra = detail.replace(/\s*\$[\d.]+.*$/, '').replace(/\/\s*$/, '').trim();
const price = (detail.match(/\$([\d.]+)/) || [])[1] || '';
const qty = (detail.match(/\/\s*(\d+)\s*份/) || [])[1] || '';
const buyer = row
.querySelector('.text-gray-2.font-size-caption i.material-icons')
?.parentElement.textContent.replace('account_circle', '').trim() || '';
table.push([name, extra, price, buyer, qty]);
});
const sum = subtotal.textContent.replace(/\s+/g, '');
table.push([
'合計',
'',
(sum.match(/\$([\d.]+)/) || [])[1] || '',
'',
(sum.match(/\/(\d+)份/) || [])[1] || ''
]);
return table;
}
/** 轉成含 UTF‑8 BOM 的 CSV 字串 */
const arrayToCsv = data =>
'\uFEFF' + data.map(r => r.map(csvEscape).join(',')).join('\n');
const csvEscape = s => /[",\n]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s;
/* ---------------- 複製至剪貼簿 ---------------- */
async function copyToClipboard(table) {
const tsv = table.map(r => r.join('\t')).join('\n');
const html = '<table>' + table.map(r =>
'<tr>' + r.map(c => `<td>${htmlEscape(c)}</td>`).join('') + '</tr>'
).join('') + '</table>';
if (navigator.clipboard?.write && window.ClipboardItem) {
try {
await navigator.clipboard.write([
new ClipboardItem({
'text/plain': new Blob([tsv], { type: 'text/plain' }),
'text/html' : new Blob([html], { type: 'text/html' })
})
]);
alert('已複製!直接貼到 Excel/Google Sheets 即可。');
return;
} catch { /* 若失敗則 fallback */ }
}
navigator.clipboard.writeText(tsv).then(() =>
alert('已複製 (僅純文字)。若貼上仍合併在同欄,請更新瀏覽器版本。')
);
}
/* ---------------- 下載 CSV 檔 ---------------- */
function downloadCsv(text, filename) {
const url = URL.createObjectURL(new Blob([text], { type: 'text/csv;charset=utf-8;' }));
Object.assign(document.createElement('a'), { href: url, download: filename }).click();
URL.revokeObjectURL(url);
}
/* ---------------- 小工具 ---------------- */
const htmlEscape = s =>
s.replace(/[&<>"']/g, m => ({ '&':'&', '<':'<', '>':'>', '"':'"', "'":''' }[m]));
})();