查询轨迹页面edit icon

创建者:
Kitty
Fork(复制)
下载
嵌入
BUG反馈
index.html
md
README.md
index.html
            
            <!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
    <title>运单智能工作台 · 批量查询与轨迹解析</title>
    <!-- Google Font & Font Awesome -->
    <link href="https://fonts.googleapis.com/css2?family=Inter:opsz,[email protected],400;14..32,500;14..32,600;14..32,700&display=swap" rel="stylesheet">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            background: #f3f6fc;
            font-family: 'Inter', system-ui, -apple-system, sans-serif;
            color: #1a2c3e;
            padding: 32px 24px;
        }

        /* 主容器 */
        .dashboard {
            max-width: 1600px;
            margin: 0 auto;
        }

        /* 头部 */
        .header {
            margin-bottom: 28px;
        }
        .header h1 {
            font-size: 1.9rem;
            font-weight: 700;
            letter-spacing: -0.02em;
            background: linear-gradient(135deg, #1f3a6b, #2b4c7c);
            background-clip: text;
            -webkit-background-clip: text;
            color: transparent;
            display: inline-flex;
            align-items: center;
            gap: 12px;
        }
        .header p {
            color: #5a6e8a;
            margin-top: 8px;
            font-size: 0.9rem;
        }

        /* 卡片样式 */
        .card {
            background: #ffffff;
            border-radius: 28px;
            box-shadow: 0 4px 14px rgba(0, 0, 0, 0.02), 0 1px 2px rgba(0, 0, 0, 0.03);
            border: 1px solid #e9eff5;
            overflow: hidden;
            transition: all 0.2s;
        }

        /* 平台切换条 */
        .platform-switch {
            display: flex;
            gap: 12px;
            background: #ffffff;
            border-radius: 48px;
            padding: 6px;
            width: fit-content;
            border: 1px solid #e2ecf5;
            margin-bottom: 28px;
            background: #f9fbfe;
        }
        .platform-btn {
            display: flex;
            align-items: center;
            gap: 10px;
            padding: 10px 28px;
            border-radius: 40px;
            font-weight: 600;
            font-size: 0.9rem;
            border: none;
            background: transparent;
            cursor: pointer;
            transition: 0.2s;
            color: #4c6280;
        }
        .platform-btn i {
            font-size: 1.1rem;
        }
        .platform-btn.active {
            background: #1e4a76;
            color: white;
            box-shadow: 0 2px 8px rgba(30,74,118,0.2);
        }

        /* 双栏布局 */
        .two-col {
            display: flex;
            gap: 28px;
            flex-wrap: wrap;
        }
        .col-left {
            flex: 2.2;
            min-width: 320px;
        }
        .col-right {
            flex: 1.3;
            min-width: 300px;
        }

        /* 表格区域 */
        .table-wrapper {
            overflow-x: auto;
            border-radius: 24px;
        }
        .track-table {
            width: 100%;
            border-collapse: collapse;
            font-size: 0.85rem;
        }
        .track-table th {
            text-align: left;
            padding: 16px 18px;
            background: #fbfdff;
            border-bottom: 1px solid #eef2f8;
            font-weight: 600;
            color: #2c3f66;
        }
        .track-table td {
            padding: 14px 18px;
            border-bottom: 1px solid #f0f4fa;
            vertical-align: middle;
        }
        .track-table tr:last-child td {
            border-bottom: none;
        }
        .checkbox-cell {
            width: 42px;
            text-align: center;
        }
        .track-code {
            font-family: 'SF Mono', 'Menlo', monospace;
            font-weight: 500;
            font-size: 0.8rem;
            letter-spacing: -0.2px;
            color: #1e3a5f;
            word-break: break-all;
        }
        .status-badge {
            display: inline-flex;
            align-items: center;
            gap: 6px;
            background: #f0f4f9;
            padding: 4px 12px;
            border-radius: 30px;
            font-size: 0.7rem;
            font-weight: 500;
            color: #4c5a72;
        }
        .status-valid {
            background: #e6f4ea;
            color: #1f7840;
        }
        .status-invalid {
            background: #fee9e6;
            color: #bc3f2e;
        }
        .action-icons i {
            margin: 0 6px;
            cursor: pointer;
            color: #8c9bb0;
            transition: 0.1s;
        }
        .action-icons i:hover {
            color: #1e4a76;
        }

        /* 右侧卡片 */
        .right-card {
            padding: 22px;
        }
        .section-title {
            font-weight: 600;
            font-size: 0.9rem;
            margin-bottom: 16px;
            display: flex;
            align-items: center;
            gap: 8px;
            color: #1f3a6b;
        }
        textarea {
            width: 100%;
            border: 1px solid #dfe7ef;
            border-radius: 20px;
            padding: 14px;
            font-family: 'SF Mono', monospace;
            font-size: 0.8rem;
            resize: vertical;
            background: #fefefe;
            transition: 0.2s;
        }
        textarea:focus {
            outline: none;
            border-color: #3b82f6;
            box-shadow: 0 0 0 3px rgba(59,130,246,0.1);
        }
        .preview-link {
            background: #f8fafd;
            border-radius: 20px;
            padding: 14px;
            margin: 16px 0;
            font-family: monospace;
            font-size: 0.7rem;
            word-break: break-all;
            border: 1px solid #eaf0f6;
            color: #2c6280;
        }
        .btn-group {
            display: flex;
            gap: 12px;
            flex-wrap: wrap;
            margin-top: 16px;
        }
        .btn {
            border: none;
            padding: 8px 20px;
            border-radius: 40px;
            font-weight: 500;
            font-size: 0.8rem;
            cursor: pointer;
            transition: 0.2s;
            display: inline-flex;
            align-items: center;
            gap: 8px;
            background: #f0f4f9;
            color: #2c4b6e;
        }
        .btn-primary {
            background: #1e4a76;
            color: white;
        }
        .btn-primary:hover {
            background: #0f3860;
            transform: translateY(-1px);
        }
        .btn-outline {
            border: 1px solid #cddfef;
            background: white;
        }
        .btn-outline:hover {
            background: #f8fafd;
        }
        .badge-count {
            background: #eef2fa;
            padding: 4px 12px;
            border-radius: 40px;
            font-size: 0.7rem;
        }
        .footer-note {
            margin-top: 28px;
            text-align: center;
            font-size: 0.7rem;
            color: #7b8ea8;
        }
        i.fa, i.far, i.fas {
            pointer-events: none;
        }
        .limit-hint {
            font-size: 0.7rem;
            color: #7085a3;
            margin-left: 8px;
        }
        .clear-invalid-area {
            margin-top: 24px;
            border-top: 1px solid #ecf3fa;
            padding-top: 20px;
        }
        .list-toolbar {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 14px 20px;
            background: linear-gradient(180deg, #fafdff 0%, #f4f8fd 100%);
            border-bottom: 1px solid #e8eef6;
        }
        .list-toolbar-actions {
            display: inline-flex;
            align-items: center;
            gap: 10px;
        }
        .btn-ghost {
            background: #ffffff;
            border: 1px solid #d6e3f0;
            color: #385675;
        }
        .btn-ghost:hover {
            background: #f5f9fe;
        }
        .source-switch {
            display: inline-flex;
            gap: 8px;
            background: #f6f9fe;
            border: 1px solid #dfe8f4;
            border-radius: 999px;
            padding: 4px;
            margin-bottom: 12px;
        }
        .source-btn {
            border: none;
            background: transparent;
            color: #5d7391;
            border-radius: 999px;
            padding: 7px 14px;
            font-size: 0.75rem;
            font-weight: 600;
            display: inline-flex;
            align-items: center;
            gap: 6px;
            cursor: pointer;
            transition: all 0.2s ease;
        }
        .source-btn.active {
            background: #1e4a76;
            color: #fff;
            box-shadow: 0 2px 8px rgba(30,74,118,0.2);
        }
    </style>
</head>
<body>
<div class="dashboard">
    <div class="header">
        <h1><i class="fas fa-chalkboard-user"></i> 运单智能工作台</h1>
        <p>批量勾选 · 自动拼接链接 · 粘贴轨迹结果自动识别无效单号</p>
    </div>

    <!-- 平台切换 -->
    <div class="platform-switch">
        <button id="aftershipBtn" class="platform-btn active"><i class="fas fa-ship"></i> AfterShip <span class="limit-hint">≤20个/次</span></button>
        <button id="track17Btn" class="platform-btn"><i class="fas fa-globe"></i> 17TRACK <span class="limit-hint">≤40个/次</span></button>
    </div>

    <div class="two-col">
        <!-- 左侧:运单表格 -->
        <div class="col-left">
            <div class="card">
                <div class="list-toolbar">
                    <div id="selectionStats" class="badge-count">已选中 0 个运单</div>
                    <div class="list-toolbar-actions">
                        <button id="clearSelectionBtn" class="btn btn-ghost"><i class="fas fa-times"></i> 清空勾选</button>
                    </div>
                </div>
                <div class="table-wrapper">
                    <table class="track-table" id="trackTable">
                        <thead>
                        <tr><th class="checkbox-cell"><input type="checkbox" id="selectAllChk"></th>
                            <th>运单号</th>
                            <th>状态</th>
                        </tr>
                        </thead>
                        <tbody id="tableBody">
                        <!-- 动态加载用户提供的跟踪号 -->
                        </tbody>
                    </table>
                </div>
            </div>
        </div>

        <!-- 右侧:操作面板 -->
        <div class="col-right">
            <div class="card right-card">
                <div class="section-title"><i class="fas fa-link"></i> 实时拼接链接预览</div>
                <div id="linkPreview" class="preview-link">未勾选任何运单号</div>
                <div class="btn-group">
                    <button id="copyLinkBtn" class="btn btn-outline"><i class="fas fa-copy"></i> 复制链接</button>
                    <button id="openQueryBtn" class="btn btn-primary"><i class="fas fa-external-link-alt"></i> 打开查询页面</button>
                </div>

                <div class="section-title" style="margin-top: 28px;"><i class="fas fa-keyboard"></i> 自定义导入/查询运单</div>
                <textarea id="customInput" rows="3" placeholder="每行一个运单号,支持批量粘贴&#10;例如:&#10;00340434624219076481&#10;9C9089I947047"></textarea>
                <div class="btn-group">
                    <button id="customAftershipBtn" class="btn btn-outline"><i class="fas fa-ship"></i> AfterShip查询</button>
                    <button id="custom17trackBtn" class="btn btn-outline"><i class="fas fa-globe"></i> 17TRACK查询</button>
                    <button id="importListBtn" class="btn btn-outline"><i class="fas fa-upload"></i> 导入列表</button>
                    <button id="clearCustomBtn" class="btn">清空输入框</button>
                </div>

                <!-- 重点:粘贴轨迹结果自动识别无效单号 -->
                <div class="clear-invalid-area">
                    <div class="section-title"><i class="fas fa-clipboard-list"></i> 粘贴轨迹结果信息 · 自动更新订单跟进数据</div>
                    <div class="source-switch">
                        <button id="parseAftershipBtn" class="source-btn active"><i class="fas fa-ship"></i> AfterShip</button>
                        <button id="parse17trackBtn" class="source-btn"><i class="fas fa-globe"></i> 17TRACK</button>
                    </div>
                    <textarea id="trackResultInput" rows="4" placeholder="从 AfterShip / 17TRACK 等网站复制轨迹内容粘贴至此&#10;系统将自动识别未出现或查询失败的运单号,标记为「无轨迹」"></textarea>
                    <div class="btn-group">
                        <button id="parseInvalidBtn" class="btn btn-primary"><i class="fas fa-filter"></i> 自动更新订单跟进数据</button>
                        <button id="removeInvalidBtn" class="btn btn-outline"><i class="fas fa-trash-alt"></i> 一键清除所有无效单号</button>
                    </div>
                    <p style="font-size:0.7rem; margin-top:12px; color:#6c819e;"><i class="fas fa-info-circle"></i> 系统会检测粘贴文本中是否包含每个运单号,未出现则标记为“无轨迹”。支持中英文“未找到/not found”等关键词增强识别。</p>
                </div>
            </div>
        </div>
    </div>
    <div class="footer-note">
        <i class="fas fa-mouse-pointer"></i> 勾选运单 → 自动生成链接 → 点击打开即可批量查询 (AfterShip上限20,17track上限40)
        · 已查看状态会保留,支持单独查询/重新查询
    </div>
</div>

<script>
    // ---------- 用户提供的跟踪号列表(去重保留,共106个)----------
    const rawTrackingNumbers = [
        "00340434624219076481", "9C9089I947047", "36CNN5006486", "00340434624219163280", "Q10206795102B7020",
        "00340434727198173995", "00340434630522121473", "00340434624219163730", "367314227280", "00340434624219075835",
        "0082800082809767093642", "GFUS01039547921728", "00340434624219077723", "9261290278833819876414", "IW933354425GB",
        "00340434624219163105", "9C1824J119920", "9261290306531784191724", "JJD0002210781528399", "00340434624219163242",
        "GFUS01039382823363", "JT20260685125566", "LM130599395CN", "0082800082809767061685", "00340434624219164751",
        "IW933353699GB", "00340435060715475509", "00340435060715475370", "9C1823J654129", "9C1823J683855",
        "ND699220730BR", "ND703686851BR", "ND699838385BR", "ND697216613BR", "ND697057355BR",
        "JT20260635016625", "JJD0002210781642874", "JJD0002210781662221", "IW933736407GB", "9C1823J781829",
        "9C1823J538123", "JJD0002210781756807", "Q19350032772L4843", "1041106541518610193005", "9C1823J744993",
        "1041106541518510110904", "9C1823J800746", "9261290278833819699655", "CNB29753480", "0082800082809766765042",
        "CNB29760473", "9C1823J962203", "CNB29760451", "0079350082809753939782", "0082800082809767127564",
        "9C1824J170573", "9C1824J136137", "00340435060716616826", "00340435060716619896", "00340435060716620090",
        "00340435060716648032", "0079350082809754879087", "0079350082809755047937", "6031126660807", "0079350082809754251383",
        "9C1824J171034", "0079350082809755559827", "9C1824J002369", "RELRLYTVR4WK", "6031126118552",
        "420332069261290376144360060346", "6A05435128978", "9C1824J121504", "00340434727199596298", "9C1824J163995",
        "9C1824J186786", "9C1824J223527", "00340433911300245118", "889863095941", "0079350082809755165515",
        "H1022620542143701024", "0079350082809755438536", "H1022620542041401044", "0082800082809767197424", "0079350082809754995181"
    ];
    // 去重保留顺序
    const uniqueNumbers = [];
    const seen = new Set();
    for (let num of rawTrackingNumbers) {
        if (!seen.has(num)) {
            seen.add(num);
            uniqueNumbers.push(num);
        }
    }
    // 构建数据模型:status: 'pending' / 'viewed' / 'invalid'
    let trackingItems = uniqueNumbers.map((code, idx) => ({
        id: idx,
        trackingNumber: code,
        status: 'pending',    // pending, viewed, invalid
        lastViewTime: null
    }));

    // 平台与限制
    let currentPlatform = 'aftership';   // 'aftership' or 'seventrack'
    const LIMIT = { aftership: 20, seventrack: 40 };

    // DOM 元素
    const tbody = document.getElementById('tableBody');
    const selectAllChk = document.getElementById('selectAllChk');
    const selectionStatsSpan = document.getElementById('selectionStats');
    const linkPreviewDiv = document.getElementById('linkPreview');
    const copyLinkBtn = document.getElementById('copyLinkBtn');
    const openQueryBtn = document.getElementById('openQueryBtn');
    const aftershipBtn = document.getElementById('aftershipBtn');
    const track17Btn = document.getElementById('track17Btn');
    const clearSelectionBtn = document.getElementById('clearSelectionBtn');
    const customInput = document.getElementById('customInput');
    const customAftershipBtn = document.getElementById('customAftershipBtn');
    const custom17trackBtn = document.getElementById('custom17trackBtn');
    const importListBtn = document.getElementById('importListBtn');
    const clearCustomBtn = document.getElementById('clearCustomBtn');
    const parseInvalidBtn = document.getElementById('parseInvalidBtn');
    const removeInvalidBtn = document.getElementById('removeInvalidBtn');
    const trackResultInput = document.getElementById('trackResultInput');
    const parseAftershipBtn = document.getElementById('parseAftershipBtn');
    const parse17trackBtn = document.getElementById('parse17trackBtn');
    let parseSource = 'aftership';

    // 辅助: 获取当前勾选的运单号数组
    function getSelectedNumbers() {
        const checkboxes = document.querySelectorAll('.row-checkbox:checked');
        return Array.from(checkboxes).map(cb => cb.getAttribute('data-tn'));
    }

    // 生成查询URL
    function buildQueryUrl(selectedNumbers) {
        if (!selectedNumbers.length) return null;
        const limit = LIMIT[currentPlatform];
        const limited = selectedNumbers.slice(0, limit);
        if (currentPlatform === 'aftership') {
            return "https://www.aftership.com/track?t=" + limited.join(',');
        } else {
            return "https://www.17track.net/en/track?nums=" + limited.join(',');
        }
    }

    // 更新预览和统计
    function updatePreviewAndStats() {
        const selected = getSelectedNumbers();
        const total = selected.length;
        const limit = LIMIT[currentPlatform];
        if (total === 0) {
            linkPreviewDiv.innerHTML = '<span style="color:#99aec9;">未勾选任何运单号</span>';
            selectionStatsSpan.innerText = `已选中 0 个运单`;
            return;
        }
        const used = Math.min(total, limit);
        const overflowMsg = total > limit ? ` (仅前${limit}个生效)` : '';
        const url = buildQueryUrl(selected);
        if (url) {
            linkPreviewDiv.innerHTML = `<span style="word-break:break-all;">${escapeHtml(url)}</span>`;
        } else {
            linkPreviewDiv.innerHTML = '生成链接失败';
        }
        selectionStatsSpan.innerText = `已选中 ${total} 个运单${overflowMsg}`;
    }

    function escapeHtml(str) {
        return str.replace(/[&<>]/g, function(m) {
            if (m === '&') return '&amp;';
            if (m === '<') return '&lt;';
            if (m === '>') return '&gt;';
            return m;
        });
    }

    // 渲染表格 (保留当前勾选)
    function renderTable() {
        const beforeSelected = getSelectedNumbers();
        tbody.innerHTML = '';
        trackingItems.forEach(item => {
            const row = tbody.insertRow();
            // 复选框
            const chkTd = row.insertCell(0);
            chkTd.className = 'checkbox-cell';
            const chk = document.createElement('input');
            chk.type = 'checkbox';
            chk.className = 'row-checkbox';
            chk.setAttribute('data-tn', item.trackingNumber);
            if (beforeSelected.includes(item.trackingNumber)) chk.checked = true;
            chk.addEventListener('change', () => {
                updatePreviewAndStats();
                updateSelectAllState();
            });
            chkTd.appendChild(chk);
            // 运单号
            const codeCell = row.insertCell(1);
            codeCell.className = 'track-code';
            codeCell.innerText = item.trackingNumber;
            // 状态
            const statusCell = row.insertCell(2);
            const statusSpan = document.createElement('span');
            statusSpan.className = `status-badge ${item.status === 'invalid' ? 'status-invalid' : (item.status === 'viewed' ? 'status-valid' : '')}`;
            if (item.status === 'pending') {
                statusSpan.innerHTML = '<i class="far fa-clock"></i> 未查看';
            } else if (item.status === 'viewed') {
                statusSpan.innerHTML = '<i class="fas fa-check-circle"></i> 已查看';
            } else {
                statusSpan.innerHTML = '<i class="fas fa-ban"></i> 无轨迹';
            }
            statusCell.appendChild(statusSpan);
        });
        updatePreviewAndStats();
        updateSelectAllState();
    }

    // 打开单个运单并标记为已查看
    function openSingle(trackingNumber) {
        const url = buildQueryUrl([trackingNumber]);
        if (url) {
            window.open(url, '_blank', 'noopener,noreferrer');
            const item = trackingItems.find(i => i.trackingNumber === trackingNumber);
            if (item && item.status !== 'invalid') {
                item.status = 'viewed';
                item.lastViewTime = new Date().toLocaleString();
                renderTable();
            }
        } else {
            alert('链接无效');
        }
    }

    // 批量打开(使用当前勾选,自动遵守上限)
    function batchOpen() {
        let selected = getSelectedNumbers();
        if (selected.length === 0) {
            alert('请至少勾选一个运单号');
            return;
        }
        const limit = LIMIT[currentPlatform];
        if (selected.length > limit) {
            if (!confirm(`当前平台最多同时查询 ${limit} 个单号,你勾选了 ${selected.length} 个,将只查询前 ${limit} 个,是否继续?`)) {
                return;
            }
            selected = selected.slice(0, limit);
        }
        const url = buildQueryUrl(selected);
        if (url) {
            window.open(url, '_blank', 'noopener,noreferrer');
            // 标记这些单号为已查看
            selected.forEach(tn => {
                const item = trackingItems.find(i => i.trackingNumber === tn);
                if (item && item.status !== 'invalid') {
                    item.status = 'viewed';
                    item.lastViewTime = new Date().toLocaleString();
                }
            });
            renderTable();
        } else {
            alert('生成链接失败');
        }
    }

    // 全选逻辑
    function updateSelectAllState() {
        const all = document.querySelectorAll('.row-checkbox');
        const checked = document.querySelectorAll('.row-checkbox:checked');
        if (all.length === 0) return;
        const targetCount = Math.min(all.length, LIMIT[currentPlatform]);
        selectAllChk.checked = checked.length === targetCount;
        selectAllChk.indeterminate = checked.length > 0 && checked.length < targetCount;
    }
    function handleSelectAll() {
        const isChecked = selectAllChk.checked;
        const all = Array.from(document.querySelectorAll('.row-checkbox'));
        const targetCount = Math.min(all.length, LIMIT[currentPlatform]);
        all.forEach((cb, idx) => {
            cb.checked = isChecked ? idx < targetCount : false;
        });
        updatePreviewAndStats();
        updateSelectAllState();
    }
    function clearSelection() {
        document.querySelectorAll('.row-checkbox').forEach(cb => cb.checked = false);
        updatePreviewAndStats();
        updateSelectAllState();
    }

    function queryFromText(platform) {
        const raw = customInput.value;
        if (!raw.trim()) return alert('请在文本框输入运单号');
        const numbers = raw.split(/[,\n\r\s]+/).map(s => s.trim()).filter(Boolean);
        const dedup = [];
        const seenInInput = new Set();
        for (let n of numbers) {
            if (!seenInInput.has(n)) {
                seenInInput.add(n);
                dedup.push(n);
            }
        }
        if (dedup.length === 0) return alert('未识别到有效运单号');
        const limit = LIMIT[platform];
        let finalNums = dedup;
        if (dedup.length > limit) {
            if (!confirm(`当前平台最多同时查询 ${limit} 个单号,你输入了 ${dedup.length} 个,将只查询前 ${limit} 个,是否继续?`)) {
                return;
            }
            finalNums = dedup.slice(0, limit);
        }
        const url = platform === 'aftership'
            ? "https://www.aftership.com/track?t=" + finalNums.join(',')
            : "https://www.17track.net/en/track?nums=" + finalNums.join(',');
        window.open(url, '_blank', 'noopener,noreferrer');
    }

    function importToListFromText() {
        const raw = customInput.value;
        if (!raw.trim()) return alert('请在文本框输入运单号');
        const numbers = raw.split(/[,\n\r\s]+/).map(s => s.trim()).filter(Boolean);
        const dedup = [];
        const seenInInput = new Set();
        for (let n of numbers) {
            if (!seenInInput.has(n)) {
                seenInInput.add(n);
                dedup.push(n);
            }
        }
        const existingSet = new Set(trackingItems.map(i => i.trackingNumber));
        const appendList = [];
        for (let n of dedup) {
            if (!existingSet.has(n)) {
                existingSet.add(n);
                appendList.push(n);
            }
        }
        if (appendList.length === 0) {
            alert('没有新增运单号可导入');
            return;
        }
        appendList.forEach((code, idx) => {
            trackingItems.push({
                id: trackingItems.length + idx,
                trackingNumber: code,
                status: 'pending',
                lastViewTime: null
            });
        });
        renderTable();
        alert(`已导入 ${appendList.length} 个运单号,当前共 ${trackingItems.length} 个`);
    }

    function setParseSource(source) {
        parseSource = source;
        if (source === 'aftership') {
            parseAftershipBtn.classList.add('active');
            parse17trackBtn.classList.remove('active');
            trackResultInput.placeholder = "从 AfterShip 复制轨迹内容粘贴至此\n系统将自动识别未出现或查询失败的运单号,标记为「无轨迹」";
        } else {
            parse17trackBtn.classList.add('active');
            parseAftershipBtn.classList.remove('active');
            trackResultInput.placeholder = "从 17TRACK 复制轨迹内容粘贴至此\n系统将自动识别未出现或查询失败的运单号,标记为「无轨迹」";
        }
    }

    // 重点:解析粘贴的轨迹结果,自动标记无效单号
    function parseAndMarkInvalid() {
        const text = trackResultInput.value;
        if (!text.trim()) {
            alert('请粘贴轨迹查询结果文本');
            return;
        }
        const lowerText = text.toLowerCase();
        // 将当前所有未被标记为无效的单号取出来
        const activeItems = trackingItems.filter(item => item.status !== 'invalid');
        if (activeItems.length === 0) {
            alert('当前没有活跃单号(全部已无效)');
            return;
        }
        const notFound = [];
        for (let item of activeItems) {
            const tn = item.trackingNumber;
            // 检查原始文本是否包含完整运单号(不区分大小写)
            if (!lowerText.includes(tn.toLowerCase())) {
                notFound.push(tn);
            }
        }
        if (notFound.length === 0) {
            alert('所有活跃单号均在粘贴文本中出现,无需标记无效');
            return;
        }
        // 标记为 invalid
        let changed = 0;
        trackingItems.forEach(item => {
            if (notFound.includes(item.trackingNumber) && item.status !== 'invalid') {
                item.status = 'invalid';
                item.lastViewTime = null;
                changed++;
            }
        });
        if (changed > 0) {
            renderTable();
            const sourceName = parseSource === 'aftership' ? 'AfterShip' : '17TRACK';
            alert(`已按 ${sourceName} 结果标记 ${changed} 个未出现轨迹的单号为「无轨迹」`);
        } else {
            alert('没有新的无效单号可标记');
        }
    }

    function removeAllInvalid() {
        const newList = trackingItems.filter(item => item.status !== 'invalid');
        if (newList.length === trackingItems.length) {
            alert('没有无效单号需要清除');
            return;
        }
        const removedCount = trackingItems.length - newList.length;
        trackingItems = newList;
        renderTable();
        alert(`已清除 ${removedCount} 个无效单号,当前剩余 ${trackingItems.length} 个`);
    }

    // 复制链接
    function copyLink() {
        const selected = getSelectedNumbers();
        if (selected.length === 0) return alert('请勾选运单号');
        const url = buildQueryUrl(selected);
        if (url) {
            navigator.clipboard.writeText(url).then(() => {
                const original = copyLinkBtn.innerHTML;
                copyLinkBtn.innerHTML = '<i class="fas fa-check"></i> 已复制';
                setTimeout(() => copyLinkBtn.innerHTML = original, 1500);
            }).catch(() => alert('复制失败'));
        } else {
            alert('链接无效');
        }
    }

    // 切换平台
    function setPlatform(platform) {
        currentPlatform = platform;
        if (platform === 'aftership') {
            aftershipBtn.classList.add('active');
            track17Btn.classList.remove('active');
        } else {
            track17Btn.classList.add('active');
            aftershipBtn.classList.remove('active');
        }
        updatePreviewAndStats();
    }

    // 事件绑定
    selectAllChk.addEventListener('change', handleSelectAll);
    clearSelectionBtn.addEventListener('click', clearSelection);
    copyLinkBtn.addEventListener('click', copyLink);
    openQueryBtn.addEventListener('click', batchOpen);
    aftershipBtn.addEventListener('click', () => setPlatform('aftership'));
    track17Btn.addEventListener('click', () => setPlatform('seventrack'));
    customAftershipBtn.addEventListener('click', () => queryFromText('aftership'));
    custom17trackBtn.addEventListener('click', () => queryFromText('seventrack'));
    importListBtn.addEventListener('click', importToListFromText);
    clearCustomBtn.addEventListener('click', () => { customInput.value = ''; });
    parseAftershipBtn.addEventListener('click', () => setParseSource('aftership'));
    parse17trackBtn.addEventListener('click', () => setParseSource('seventrack'));
    parseInvalidBtn.addEventListener('click', parseAndMarkInvalid);
    removeInvalidBtn.addEventListener('click', removeAllInvalid);

    // 监听复选框变化 (动态)
    document.addEventListener('change', (e) => {
        if (e.target.classList && e.target.classList.contains('row-checkbox')) {
            updatePreviewAndStats();
            updateSelectAllState();
        }
    });

    // 初始化渲染
    renderTable();
    setPlatform('aftership');
    setParseSource('aftership');
</script>
</body>
</html>

        
编辑器加载中
预览
控制台