蝦皮票券管理

取出所有蝦皮票券,支援匯出,方便管理

目前為 2023-09-05 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         蝦皮票券管理
// @namespace
// @version      1.0.1
// @description  取出所有蝦皮票券,支援匯出,方便管理
// @author       Danny H.
// @match        https://shopee.tw/*
// @icon         https://freepngimg.com/save/109004-shopee-logo-free-transparent-image-hq/128x128
// @grant        GM_addStyle
// @license      MIT
// @namespace https://greasyfork.org/users/1166167
// ==/UserScript==
(async function() {
    'use strict';

    // Create UI elements
    const btn = createButton();
    const modal = createModal();

    document.body.appendChild(btn);
    document.body.appendChild(modal);

    btn.addEventListener("click", async () => {
        modal.style.display = "block";
        const tickets = await fetchTickets();
        updateModalContent(modal, tickets);
    });

    function createButton() {
        const btn = document.createElement("BUTTON");
        btn.innerHTML = "顯示所有票券";
        btn.style.position = "fixed";
        btn.style.top = "20px";
        btn.style.right = "20px";
        btn.style.zIndex = "9999";

        // 添加自定義樣式
        btn.style.backgroundColor = "#FF5700";
        btn.style.color = "white";
        btn.style.padding = "10px 20px";
        btn.style.border = "none";
        btn.style.borderRadius = "4px";
        btn.style.fontSize = "16px";
        btn.style.boxShadow = "0 4px 8px rgba(0, 0, 0, 0.1)";
        btn.style.cursor = "pointer";

        // 添加 hover 效果
        btn.addEventListener("mouseover", function() {
            this.style.backgroundColor = "#FF4500";
        });

        btn.addEventListener("mouseout", function() {
            this.style.backgroundColor = "#FF5700";
        });

        return btn;
    }

    function createModal() {
        const modal = document.createElement("DIV");
        const modalContent = document.createElement("DIV");
        modal.id = "myModal";
        modalContent.id = "modalContent";
        Object.assign(modal.style, {
            position: "fixed",
            left: "0",
            top: "0",
            width: "100%",
            height: "100%",
            backgroundColor: "rgba(0,0,0,0.5)",
            display: "none",
            zIndex: "10000"
        });
        Object.assign(modalContent.style, {
            backgroundColor: "#fff",
            margin: "5% auto",
            padding: "20px",
            width: "50%",
            maxHeight: "80%",
            overflowY: "scroll"
        });
        modal.appendChild(modalContent);
        return modal;
    }

    async function fetchTickets() {
        const ticketNum = await fetch("/digital-product/api/my-ticket/list?page_size=15&state=1")
            .then(r => r.json())
            .then(data => data['data']['total']);
        const ticketData = await fetch(`/digital-product/api/my-ticket/list?page_size=${ticketNum}&state=1`)
            .then(r => r.json());
        const ticketInfoList = ticketData['data']['ticket_info_list'];

        const fetchPromises = ticketInfoList.map(ticket =>
            fetch(`/digital-product/api/my-ticket/detail?ticket_id=${ticket['ticket_id']}`)
            .then(r => r.json())
        );
        const ticketDetails = await Promise.all(fetchPromises);
        return ticketDetails;
    }

    function addBOM(csvContent) {
        return "\uFEFF" + csvContent;
    }

    function convertToCSV(ticketDetails) {
        const header = ["Expire Date", "Item Name", "URL"];
        const rows = ticketDetails.map(detail => {
            const {
                expire_time,
                item_name
            } = detail['data']['ticket_detail'];
            const url = detail['data']['ticket_detail']['evoucher_ticket_detail']['code_url'];
            const formattedDate = formatDate(new Date(expire_time * 1000));
            return [formattedDate, item_name, url];
        });

        let csvContent = header.join(",") + "\n";
        csvContent += rows.map(row => row.join(",")).join("\n");
        return addBOM(csvContent);
    }

    function updateModalContent(modal, ticketDetails) {
        const modalContent = modal.querySelector("#modalContent");
        const modalClose = createModalCloseButton();

        // 添加匯出CSV按鈕
        const exportCSVButton = document.createElement("BUTTON");
        exportCSVButton.innerHTML = "匯出CSV";
        exportCSVButton.style.marginBottom = "10px";
        exportCSVButton.addEventListener("click", function() {
            const csvContent = convertToCSV(ticketDetails);
            const blob = new Blob([csvContent], {
                type: 'text/csv;charset=utf-8;'
            });
            const url = URL.createObjectURL(blob);
            const link = document.createElement("a");
            link.href = url;
            link.download = 'ticket_details.csv';
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
        });

        modalContent.innerHTML = "";
        modalContent.appendChild(modalClose);
        modalContent.appendChild(exportCSVButton);
        modalContent.insertAdjacentHTML("beforeend", generateTicketListHTML(ticketDetails));

        modalClose.addEventListener("click", function() {
            modal.style.display = "none";
        });
    }


    function createModalCloseButton() {
        const modalClose = document.createElement("SPAN");
        modalClose.innerHTML = "×";
        Object.assign(modalClose.style, {
            float: "right",
            cursor: "pointer",
            fontSize: "24px",
            fontWeight: "bold",
            color: "#333"
        });
        return modalClose;
    }

    function formatDate(shopeeDateStr) {
        const date = new Date(shopeeDateStr);
        const year = date.getFullYear();
        const month = String(date.getMonth() + 1).padStart(2, '0');
        const day = String(date.getDate()).padStart(2, '0');
        return `${year}-${month}-${day}`;
    }

    function generateTicketListHTML(ticketDetails) {
        const ticketListHtml = `<div style="font-weight: bold; margin-bottom: 10px;">總共有 ${ticketDetails.length} 張票券尚未使用:</div><ul style="list-style-type: none; padding-left: 0;">`;
        const ticketItems = ticketDetails.map(detail => {
            const {
                expire_time,
                item_name
            } = detail['data']['ticket_detail'];
            const url = detail['data']['ticket_detail']['evoucher_ticket_detail']['code_url'];

            // 使用 formatDate 函數將日期轉換為 yyyy-MM-dd 格式
            const formattedDate = formatDate(new Date(expire_time * 1000));

            return `
        <li style="border: 1px solid #eaeaea; margin: 8px 0; padding: 10px; border-radius: 4px;">
            <span style="font-weight: bold;">${formattedDate}</span>,
            <span>${item_name}</span>,
            <a href="${url}" target="_blank" style="color: #FF5700;">點我開啟</a>
        </li>`;
        }).join("");
        return ticketListHtml + ticketItems + "</ul>";
    }

})();