泥潭日报 uscardforum · 每日精选

[AS]里程票批量搜索和结果增强显示 — AS Enhancer + Batch Search 【4/15 更新V1.2版本:UI大改,增加大量过滤】

内容摘要

AS搜索增强与批量搜索脚本使用指南及最新修复

1. 关键信息

  • 两个脚本互不干涉:AS Enhancer(结果增强)+ Batch Search(批量搜索)。
  • AS 不需登录;登录后不建议使用脚本。
  • 需开启 allow user scripts 并打开 Dev Mode 安装。
  • 增强功能:Booking Code 显示、机型/中转/里程显示、排序、过滤、日历热力图。
  • 批量搜索:多出发地×目的地、日期范围、舱位/税费/人数/直飞过滤、按里程排序。
  • 已知 Bug:单航班点不开;已修复(#106)。

2. 羊毛/优惠信息

3. 最新动态

  • v1.2 UI 大改,增加大量过滤与日历视图(#1)。
  • 已修复单航班无法展开问题(#104-#106)。
  • 更新后需重新安装并开启用户脚本(#98)。

4. 争议或不同意见

  • 有人反映批量搜索突然停止(#99);建议手动搜一张票再试。
  • 日历视图在现金与里程票切换后曾显示错误(#85);已修复。

5. 行动建议

  • 安装前确认开启 allow user scripts 与 Dev Mode。
  • 先用 Incognito 测试,确认脚本生效再登录。
  • 遇到点不开航班先刷新或重新安装脚本。
原始内容
--- 第 1 楼来自 zpahai 的回复 (2026-03-20 21:35:45 PDT) ---

相信大家都知道AS官网搜里程票有多么操蛋,并且得益于其完全不设防的官网 ,遂以CC辅助我搓了两个油猴脚本:

这两个脚本互不干涉,可以同时安装也可以只使用一个

以下部分说明内容由AI生成:
工具一:AS Enhancer(搜索结果增强)

装上之后,AS的里程票搜索结果页会自动增强:

功能:

Booking Code显示 — 每个票价格子直接显示舱位代码,比如 X(Y) I(J),不用再点进去看
航班信息增强 — 机型、中转时间、总里程一目了然
排序 — 类似AA官网的排序逻辑,点击任意票价列头,按里程数升序/降序排列
过滤 — 勾选"Available only"只显示对应cabin有票的航班
日历视图 — 替换AS原生的日期选择器,改为显示两个月的里程价格热力图

预览:

Screenshot 2026-03-21 0004502880×1462 326 KB
工具二:AS Batch Award Search(批量里程票搜索)

这个会在AS搜索界面增加一个浮动面板,支持批量搜索多条航线的里程票。

核心功能:

多出发地 × 多目的地 — 输入 KIX, NRT, ICN → SAN, LAX, SFO,自动搜索所有9条航线组合
日期范围 — 选择起止月份,最远支持330天
分舱位里程上限 — 默认 Main 50K、Business 95K、First 110K
税费上限 — 设置最大现金部分,过滤掉税费太高的票
人数 — 支持1-9人,过滤座位不够的航班
Nonstop筛选 — 只看直飞

搜索流程:

日历扫描(Phase 1)— 并行请求所有航线×月份的日历数据,找出里程数低于上限的日期
自动拉取(Phase 2)— 对符合条件的日期,自动拉取详细航班信息
过滤 — 日历显示有票但实际没有对应价格的,自动标记并过滤。(并不能处理真的幽灵票,只能筛掉日历视图中的假票)

显示效果(类似国泰神器风格):

按航线分组,可折叠
每个航班显示样式:航司Logo + 航班号 + 中转机场 + 飞行时间 + 舱位徽章(J 95K / F 110K)
点击展开看详细信息:每段航班时间、机型、实际承运、剩余座位、Booking Code
不同机场换乘(如NRT→HND)红色警告
混合舱位(如一段J一段Y)标记 * 并提示

其他:

保存最近5次搜索记录,一键重搜
搜索参数本地化存储
Codeshare自动合并

预览:

Screenshot 2026-03-21 0006212880×1460 223 KB

Screenshot 2026-03-21 0008072880×1462 275 KB

Disclaimer:

不建议在登录账户的情况下使用此脚本,不登录不影响任何功能,本人不对使用此脚本造成任何后果及损失负责

本来准备放Github,想了下还是先放这,太长拆开放在楼下:

--- 第 2 楼来自 dreambig 的回复 (2026-03-20 21:38:33 PDT) ---

先赞后看

--- 第 3 楼来自 zpahai 的回复 (2026-03-20 21:42:15 PDT) ---

第二个太长放不下,直接放pastebin了:

pastebin.com

https://pastebin.com/3JuJrjQN

密码 uscardno1

--- 第 4 楼来自 Tiangua 的回复 (2026-03-20 21:44:13 PDT) ---

前排前排

--- 第 5 楼来自 CubeOvO 的回复 (2026-03-20 21:46:07 PDT) ---

厉害了!

--- 第 6 楼来自 zpahai 的回复 (2026-03-20 21:46:57 PDT) ---

AS Enhancer
// ==UserScript==
// @name AS Enhancer
// @namespace http://tampermonkey.net/
// @version 8.1
// @description Sort, filter, and enrich Alaska Airlines award search results + calendar
// @author zpahai
// @match https://www.alaskaair.com/search/results*
// @grant none
// @run-at document-idle
// @updateURL https://raw.githubusercontent.com/AhaiMk01/as_enhanced/main/as.js
// @downloadURL https://raw.githubusercontent.com/AhaiMk01/as_enhanced/main/as.js
// @license MIT
// ==/UserScript==

(function () {
'use strict';

/* ─── Constants ─── */
const CABIN_LETTER = { COACH:'Y', BUSINESS:'J', FIRST:'F', PREMIUM:'W', PREMIUM_ECONOMY:'W' };
const SOLUTION_PREFIX = 'REFUNDABLE_';
const POLL_INTERVAL = 800;
const POLL_TIMEOUT = 20000;
const CAL_MAX_DAYS = 330;

/* ─── State ─── */
let sortCol = null, sortDir = 0;
let activeFilters = new Set();
let flightData = null;
let initRunning = false;
let calDateCache = {};
let calDisplayedMonths = [];
let calSelectedDate = null;

/* ─── CSS ─── */
function injectStyles() {
if (document.getElementById('as-enhancer-css')) return;
const style = document.createElement('style');
style.id = 'as-enhancer-css';
style.textContent = `
.fare-header-container button.header { pointer-events: auto !important; }
.as-sort-arrow { display: inline; margin-left: 3px; font-size: 13px; }
.as-filter-row {
display: flex; align-items: center; justify-content: center;
gap: 4px; margin-top: 2px; font-size: 11px; font-weight: 500;
line-height: 1; pointer-events: auto !important; position: relative; z-index: 10;
}
.as-filter-cb {
width: 14px; height: 14px; cursor: pointer; accent-color: #0074c8;
margin: 0; pointer-events: auto !important; position: relative; z-index: 11;
}
.as-filter-label {
cursor: pointer; user-select: none; opacity: 0.75; font-size: 11px;
pointer-events: auto !important;
}
.as-clear-filters {
display: inline-flex; align-items: center; gap: 3px;
padding: 2px 10px; border: 1px solid rgba(255,255,255,0.6); border-radius: 12px;
background: rgba(255,255,255,0.15); color: white; font-size: 11px; font-weight: 600;
cursor: pointer; pointer-events: auto !important; transition: background 0.15s;
position: relative; z-index: 10;
}
.as-clear-filters:hover { background: rgba(255,255,255,0.3); }
.as-clear-filters.hidden { display: none; }

.as-enrich {
display: flex; flex-direction: column; gap: 1px;
margin-top: 2px; padding-top: 2px;
font-size: 10px; line-height: 1.3; color: #444;
}
.as-enrich-row { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.as-tag {
display: inline-block; padding: 1px 5px; border-radius: 3px;
font-size: 10px; font-weight: 600; font-family: monospace;
}
.as-tag-booking { background: #e0f0ff; color: #0055a5; }
.as-tag-aircraft { background: #e8f5e9; color: #2e7d32; }

.as-seg-info { font-size: 9px; color: #666; padding: 2px 12px 4px; }
.as-seg-line-compact {
display: flex; align-items: center; gap: 3px; flex-wrap: wrap; line-height: 1.4;
}
.as-seg-line-compact .as-tag-aircraft { font-size: 9px; padding: 0 4px; }
.as-dot { color: #999; }
.as-layover { color: #c55; font-weight: 600; }
.as-distance { color: #888; }
.as-low-fare { color: #2e7d32; font-weight: 600; font-size: 9px; }

.flight-card-badges { display: none !important; }
.flight-card-details { padding: 8px 12px !important; }
.flight-header { padding: 2px 0 !important; }
.flight-info { gap: 0 !important; }
.disclosure-container { padding: 0 !important; margin: 0 !important; }
.disclosure-text { font-size: 11px !important; line-height: 1.3 !important; margin: 0 !important; }
.full-width-divider { margin: 4px 0 !important; }
.action-buttons { padding: 2px 0 !important; margin: 0 !important; gap: 4px !important; }
.action-buttons button, .action-buttons [type="button"] {
padding: 2px 10px !important; font-size: 11px !important;
min-height: unset !important; height: auto !important;
}
.flight-card-content { gap: 0 !important; }
.flight-card-details-section { gap: 0 !important; }

.shoulder-dates-wrapper { display: none !important; }
#as-cal-wrap { margin: 0 auto 8px; max-width: 820px; }
#as-cal-nav {
display: flex; justify-content: space-between; align-items: center; margin-bottom: 4px;
}
#as-cal-nav button {
background: none; border: 1px solid #ccc; border-radius: 4px;
padding: 2px 10px; cursor: pointer; font-size: 16px;
}
#as-cal-nav button:hover { background: #f0f0f0; }
#as-cal-nav span { font-weight: 600; font-size: 14px; }
#as-cal { transition: opacity .2s; }
.as-cal-cell {
border-radius: 4px; padding: 2px 1px; text-align: center;
min-height: 32px; display: flex; flex-direction: column; justify-content: center;
font-size: 10px; line-height: 1.2; cursor: default;
}
.as-cal-cell.clickable { cursor: pointer; }
.as-cal-cell.clickable:hover { outline: 2px solid #003366; outline-offset: -1px; }
`;
document.head.appendChild(style);
}

/* ─── SvelteKit devalue decoder ─── */
function devalueRevive(data) {
const c = new Map();
function r(i) {
if (i < 0) return i === -1 ? undefined : i === -2 ? NaN : i === -3 ? Infinity
: i === -4 ? -Infinity : undefined;
if (c.has(i)) return c.get(i);
const v = data[i];
if (v === null || typeof v !== 'object') { c.set(i, v); return v; }
if (Array.isArray(v)) { const a = v.map(r); c.set(i, a); return a; }
const o = {}; c.set(i, o);
for (const [k, j] of Object.entries(v)) o[k] = r(j);
return o;
}
return r(0);
}

/* ─── Inline resolve script parser ─── */
function parseResolveData(resolveId) {
const scripts = document.querySelectorAll('script:not([src])');
const pattern = new RegExp('\\.resolve\\(\\s*' + resolveId + '\\s*,\\s*\\(\\)\\s*=>\\s*');
for (const s of scripts) {
const text = s.textContent;
const m = text.match(pattern);
if (!m) continue;
// Extract data using balanced paren tracking to find the closing )
const start = m.index + m[0].length;
let depth = 1, i = m.index + m[0].length - 1; // start after the outer (
// Actually, the outer ( is from resolve(2, () => DATA) — we need to find the matching )
// Reset: find the resolve( opening paren, track depth from there
const resolveStart = text.indexOf('(', m.index);
depth = 1;
for (i = resolveStart + 1; i < text.length; i++) {
if (text[i] === '(') depth++;
else if (text[i] === ')') { depth--; if (depth === 0) break; }
}
if (depth !== 0) continue;
const dataStr = text.substring(start, i);
try {
const data = (new Function('return ' + dataStr))();
return data;
} catch (e) { console.warn('AS Enhancer: resolve(' + resolveId + ') eval failed', e); }
}
return null;
}

/* ─── Data extraction ─── */
async function extractFlightData() {
// Method 1: parse the inline resolve(2) script from the DOM (instant)
try {
const data = parseResolveData(2);
if (data && Array.isArray(data) && data[0]?.rows) return data;
} catch (e) { console.warn('AS Enhancer: inline resolve parse failed', e); }

// Method 2: fallback network fetch (slow, triggers full server search)
try {
const params = new URLSearchParams(window.location.search);
const resp = await fetch('/search/results/__data.json?' + params.toString());
const text = await resp.text();
const lines = text.split('\n').filter(l => l.trim());
if (lines[2]) {
const chunk2 = JSON.parse(lines[2]);
const decoded = devalueRevive(chunk2.data);
if (decoded && decoded.rows) return [decoded];
}
} catch (e) { console.warn('AS Enhancer: __data.json failed', e); }

return null;
}

/* ─── Matching helpers ─── */
function parseDuration(text) {
const m = (text || '').match(/(\d+)h\s*(\d*)m?/);
return m ? parseInt(m[1]) * 60 + (parseInt(m[2]) || 0) : 0;
}

function getLocalTimeFromISO(isoStr) {
// Extract local time directly from ISO string, ignoring timezone conversion
const m = isoStr.match(/T(\d{2}):(\d{2})/);
return m ? parseInt(m[1]) * 60 + parseInt(m[2]) : -1;
}

function parseTimeToMinutes(timeStr) {
const m = timeStr.match(/(\d{1,2}):(\d{2})\s*(am|pm)/i);
if (!m) return -1;
let h = parseInt(m[1]);
const min = parseInt(m[2]);
const ap = m[3].toLowerCase();
if (ap === 'pm' && h !== 12) h += 12;
if (ap === 'am' && h === 12) h = 0;
return h * 60 + min;
}

/* ─── Match displayed rows to data ─── */
function matchRowToData(tr, dataRows) {
const card = tr.querySelector('[data-testid*="flight-card"]');
if (!card) return null;
const texts = card.textContent;

// Method 1: all flight numbers in text + duration
for (const dr of dataRows) {
const flights = dr.segments.map(s =>
s.publishingCarrier.carrierCode + ' ' + s.publishingCarrier.flightNumber
).join(', ');
if (texts.includes(flights) &&
Math.abs(dr.duration - parseDuration(texts.match(/(\d+h\s*\d*m)/)?.[1] || '')) < 5) {
return dr;
}
}

// Method 2: first segment flight number + duration
for (const dr of dataRows) {
const first = dr.segments[0].publishingCarrier;
const flightStr = first.carrierCode + ' ' + first.flightNumber;
if (texts.includes(flightStr)) {
const durMatch = texts.match(/(\d+h\s*\d*m)/);
if (durMatch && Math.abs(dr.duration - parseDuration(durMatch[1])) < 5) return dr;
}
}

// Method 3: departure time + duration (timezone-safe, for cards without flight numbers)
const depEl = card.querySelector('.departure-time');
const durMatch = texts.match(/(\d+h\s*\d*m)/);
if (depEl && durMatch) {
const cardDepMin = parseTimeToMinutes(depEl.textContent.trim());
const cardDur = parseDuration(durMatch[1]);
if (cardDepMin >= 0 && cardDur > 0) {
for (const dr of dataRows) {
const dataDepMin = getLocalTimeFromISO(dr.segments[0].departureTime);
if (Math.abs(cardDepMin - dataDepMin) < 2 &&
Math.abs(cardDur - dr.duration) < 5) {
return dr;
}
}
}
}

return null;
}

/* ─── Enrich fare tiles ─── */
function enrichTile(tile, sol, segments) {
if (tile.querySelector('.as-enrich')) return;
if (!sol) return;
if (tile.classList.contains('sold-out-button') || tile.textContent.includes('Unavailable')) return;
let html = '<div class="as-enrich"><div class="as-enrich-row">';
sol.bookingCodes.forEach((code, i) => {
const cabin = sol.cabins[i] || '';
const cabLetter = CABIN_LETTER[cabin] || cabin.charAt(0);
const seg = segments[i];
const route = seg ? seg.departureStation + '→' + seg.arrivalStation : '';
const cabinShort = cabin.charAt(0) + cabin.slice(1).toLowerCase();
html += `<span class="as-tag as-tag-booking" title="${code} (${cabinShort}) • ${route}">${code}(${cabLetter})</span>`;
});
html += '</div></div>';
tile.insertAdjacentHTML('beforeend', html);
}

/* ─── Enrich segment info ─── */
function enrichSegments(card, dataRow) {
if (card.querySelector('.as-seg-info')) return;
const segments = dataRow.segments;
let parts = [];

segments.forEach((seg, i) => {
const route = seg.departureStation + '→' + seg.arrivalStation;
const equip = seg.aircraftCode || seg.equipment?.code || seg.legs?.[0]?.equipment?.code || '';
parts.push(`<span style="color:#666">${route}</span>`);
if (equip) parts.push(`<span class="as-tag as-tag-aircraft">${equip}</span>`);

if (i < segments.length - 1) {
const arrTime = seg.arrivalTime || seg.legs?.[seg.legs.length - 1]?.arrivalTime;
const nextDep = segments[i + 1].departureTime || segments[i + 1].legs?.[0]?.departureTime;
if (arrTime && nextDep) {
const diff = (new Date(nextDep) - new Date(arrTime)) / 60000;
if (diff > 0) {
const h = Math.floor(diff / 60), m = Math.round(diff % 60);
parts.push(`<span class="as-dot">·</span>`);
parts.push(`<span class="as-layover">${h}h ${m}m</span>`);
}
}
parts.push(`<span class="as-dot">·</span>`);
}
});

if (dataRow.totalDistance) {
const dist = dataRow.totalDistance;
const len = typeof dist === 'object' ? dist.length : dist;
const unit = typeof dist === 'object' ? dist.unit : 'MI';
if (len) {
parts.push(`<span class="as-dot">·</span>`);
parts.push(`<span class="as-distance">${Number(len).toLocaleString()} ${unit}</span>`);
}
}

const badge = card.querySelector('.flight-card-badges .badge-text');
if (badge && badge.textContent.trim()) {
parts.push(`<span class="as-dot">·</span>`);
parts.push(`<span class="as-low-fare">🟢 ${badge.textContent.trim()}</span>`);
}

const div = document.createElement('div');
div.className = 'as-seg-info';
div.innerHTML = `<div class="as-seg-line-compact">${parts.join(' ')}</div>`;

const details = card.querySelector('.flight-card-details');
if (details) details.parentElement.insertBefore(div, details.nextSibling);
}

/* ─── Column header setup (sort + filter) ─── */
function setupHeaders() {
const containers = document.querySelectorAll('.fare-header-container');
if (!containers.length) return;

const lastContainer = containers[containers.length - 1];

containers.forEach(container => {
const btn = container.querySelector('button.header');
if (!btn || btn.dataset.asReady) return;
btn.dataset.asReady = '1';

const nameEl = btn.querySelector('.heading, p.heading');
const colId = container.id;

let arrow = btn.querySelector('.as-sort-arrow');
if (!arrow) {
arrow = document.createElement('span');
arrow.className = 'as-sort-arrow';
if (nameEl) nameEl.appendChild(arrow);
}

container.addEventListener('click', function (e) {
if (e.target.closest('.as-filter-row') || e.target.closest('.as-clear-filters')) return;
e.stopImmediatePropagation();
e.preventDefault();

if (sortCol === colId) {
sortDir = (sortDir % 3) + 1;
if (sortDir === 3) { sortCol = null; sortDir = 0; }
} else {
sortCol = colId; sortDir = 1;
}
updateSortArrows();
applySort();
}, true);

if (!btn.querySelector('.as-filter-row')) {
const filterRow = document.createElement('div');
filterRow.className = 'as-filter-row';
filterRow.addEventListener('click', e => e.stopImmediatePropagation(), true);

const cb = document.createElement('input');
cb.type = 'checkbox';
cb.className = 'as-filter-cb';
cb.addEventListener('change', () => {
setTimeout(() => {
if (cb.checked) activeFilters.add(colId);
else activeFilters.delete(colId);
applyFilter();
}, 0);
});

const label = document.createElement('span');
label.className = 'as-filter-label';
label.textContent = 'Available only';
label.addEventListener('click', (e) => {
e.preventDefault();
cb.checked = !cb.checked;
cb.dispatchEvent(new Event('change'));
});

filterRow.appendChild(cb);
filterRow.appendChild(label);
btn.appendChild(filterRow);
}

if (container === lastContainer && !btn.querySelector('.as-clear-filters')) {
const clearBtn = document.createElement('button');
clearBtn.className = 'as-clear-filters hidden';
clearBtn.textContent = '✕ Clear filters';
clearBtn.addEventListener('click', e => {
e.stopImmediatePropagation();
e.preventDefault();
activeFilters.clear();
document.querySelectorAll('.as-filter-cb').forEach(c => c.checked = false);
applyFilter();
}, true);
btn.appendChild(clearBtn);
}
});
}

function updateSortArrows() {
document.querySelectorAll('.fare-header-container').forEach(c => {
const arrow = c.querySelector('.as-sort-arrow');
if (!arrow) return;
if (c.id === sortCol) {
arrow.textContent = sortDir === 1 ? ' ↑' : sortDir === 2 ? ' ↓' : '';
} else {
arrow.textContent = '';
}
});
}

/* ─── Sort logic ─── */
function getPoints(row, colId) {
const tiles = row.querySelectorAll('button[data-testid*="valuetile"]');
const headers = document.querySelectorAll('.fare-header-container');
let colIdx = -1;
headers.forEach((h, i) => { if (h.id === colId) colIdx = i; });
if (colIdx < 0 || colIdx >= tiles.length) return Infinity;
const tile = tiles[colIdx];
if (tile.classList.contains('sold-out-button') || tile.textContent.includes('Unavailable')) return Infinity;
const span = tile.querySelector('.price-line span:first-child, [data-testid="award-price"] span:first-child');
if (!span) return Infinity;
const txt = span.textContent.trim().replace(/,/g, '');
const m = txt.match(/([\d.]+)\s*k?/i);
return m ? parseFloat(m[1]) * (txt.toLowerCase().includes('k') ? 1000 : 1) : Infinity;
}

function applySort() {
const tbody = document.querySelector('table.resultsTable tbody') ||
document.querySelector('tr[data-testid*="matrix-row"]')?.parentElement;
if (!tbody) return;
const rows = [...tbody.querySelectorAll('tr[data-testid*="matrix-row"]')];
if (!sortCol || sortDir === 0) {
rows.sort((a, b) => {
const ai = parseInt(a.dataset.testid?.match(/\d+/)?.[0] || '0');
const bi = parseInt(b.dataset.testid?.match(/\d+/)?.[0] || '0');
return ai - bi;
});
} else {
rows.sort((a, b) => {
const pa = getPoints(a, sortCol), pb = getPoints(b, sortCol);
return sortDir === 1 ? pa - pb : pb - pa;
});
}
rows.forEach(r => tbody.appendChild(r));
}

/* ─── Filter logic ─── */
function applyFilter() {
const rows = document.querySelectorAll('tr[data-testid*="matrix-row"]');
const headers = document.querySelectorAll('.fare-header-container');
const colIds = [...headers].map(h => h.id);
let visible = 0, total = rows.length;

rows.forEach(tr => {
if (activeFilters.size === 0) {
tr.style.display = '';
visible++;
return;
}
const tiles = tr.querySelectorAll('button[data-testid*="valuetile"]');
let show = true;
activeFilters.forEach(colId => {
const idx = colIds.indexOf(colId);
if (idx < 0 || idx >= tiles.length) return;
const tile = tiles[idx];
if (tile.classList.contains('sold-out-button') || tile.textContent.includes('Unavailable')) {
show = false;
}
});
tr.style.display = show ? '' : 'none';
if (show) visible++;
});

const countEl = document.querySelector('p.display-count-text, [class*="display-count"]');
if (countEl) {
countEl.textContent = activeFilters.size > 0
? `Showing ${visible} of ${total} (filtered)`
: `Showing ${total} of ${total}`;
}

const clearBtn = document.querySelector('.as-clear-filters');
if (clearBtn) clearBtn.classList.toggle('hidden', activeFilters.size === 0);
}

/* ═══════════════════════════════════════
─── Calendar ───
═══════════════════════════════════════ */

function cacheShoulderDates(dates) {
dates.forEach(d => {
const ym = d.date.substring(0, 7);
if (!calDateCache[ym]) calDateCache[ym] = {};
calDateCache[ym][d.date] = d;
});
}

async function fetchMonthDates(yearMonth) {
const existing = calDateCache[yearMonth];
if (existing) {
const [y, m] = yearMonth.split('-').map(Number);
const daysInMonth = new Date(y, m, 0).getDate();
if (Object.keys(existing).length >= daysInMonth) return;
}

// Try inline resolve(3) script first (shoulder/calendar dates)
try {
const data = parseResolveData(3);
if (data) {
const dates = data.shoulderDates || data.calendarDates || (Array.isArray(data) ? data : []);
if (dates.length) { cacheShoulderDates(dates); return; }
}
} catch (e) {}

// Fallback: network fetch for the target month
const params = new URLSearchParams(window.location.search);
params.set('OD', yearMonth + '-15');
try {
const resp = await fetch('/search/results/__data.json?' + params.toString());
const text = await resp.text();
const lines = text.split('\n').filter(l => l.trim());
if (!lines[3]) return;
const chunk3 = JSON.parse(lines[3]);
const decoded = devalueRevive(chunk3.data);
const dates = decoded.shoulderDates || decoded.calendarDates || [];
cacheShoulderDates(dates);
} catch (e) {
console.error('AS Enhancer: fetchMonthDates error', e);
}
}

function seedCalCache() {
try {
const sd = document.querySelector('shoulder-dates');
const dates = JSON.parse(sd.getAttribute('dates'));
cacheShoulderDates(dates);
} catch {}
}

function shiftMonth(ym, delta) {
const [y, m] = ym.split('-').map(Number);
const total = (y * 12 + (m - 1)) + delta;
const ny = Math.floor(total / 12);
const nm = (total % 12) + 1;
return ny + '-' + String(nm).padStart(2, '0');
}

function monthLabel(ym) {
const [y, m] = ym.split('-').map(Number);
return new Date(y, m - 1, 1).toLocaleString('en-US', { month: 'long', year: 'numeric' });
}

function priceColor(pts, min, max) {
if (pts == null || pts === 0) return '#e8e8e8';
if (min === max) return '#c6efce';
const ratio = (pts - min) / (max - min);
const r = Math.round(ratio < 0.5 ? ratio * 2 * 255 : 255);
const g = Math.round(ratio < 0.5 ? 255 : (1 - (ratio - 0.5) * 2) * 255);
return 'rgb(' + r + ',' + g + ',60)';
}

function buildMonthGrid(ym, minPts, maxPts) {
const [year, month] = ym.split('-').map(Number);
const daysInMonth = new Date(year, month, 0).getDate();
let startDay = new Date(year, month - 1, 1).getDay();
startDay = startDay === 0 ? 6 : startDay - 1;
const monthData = calDateCache[ym] || {};
const today = new Date().toISOString().split('T')[0];

let html = '<div style="text-align:center;font-weight:600;font-size:13px;margin-bottom:4px">' +
monthLabel(ym) + '</div>';
html += '<div style="display:grid;grid-template-columns:repeat(7,1fr);gap:1px;font-size:11px">';
['Mon','Tue','Wed','Thu','Fri','Sat','Sun'].forEach(d => {
html += '<div style="text-align:center;font-weight:600;padding:2px 0;font-size:10px;color:#666">' + d + '</div>';
});
for (let i = 0; i < startDay; i++) html += '<div></div>';

for (let d = 1; d <= daysInMonth; d++) {
const ds = ym + '-' + String(d).padStart(2, '0');
const info = monthData[ds];
const pts = info ? info.awardPoints : null;
const isPast = ds < today;
const isSel = ds === calSelectedDate;
let bg = '#f5f5f5', fg = '#999', cls = 'as-cal-cell';

if (!isPast && pts != null && pts > 0) {
bg = priceColor(pts, minPts, maxPts); fg = '#000'; cls += ' clickable';
} else if (!isPast && info) {
bg = '#e8e8e8'; fg = '#666'; cls += ' clickable';
} else if (!isPast) {
cls += ' clickable';
}
if (isSel) { bg = '#003366'; fg = '#fff'; }
const ptsLabel = (!isPast && pts > 0) ? (pts >= 1000 ? (pts / 1000) + 'k' : pts) : '';

html += '<div class="' + cls + '" data-date="' + ds + '" style="background:' + bg +
';color:' + fg + ';' + (isPast ? 'opacity:.4;' : '') + '">';
html += '<div style="font-weight:600;font-size:11px">' + d + '</div>';
if (ptsLabel) html += '<div style="font-size:9px">' + ptsLabel + '</div>';
html += '</div>';
}
html += '</div>';
return html;
}

function navigateToDate(dateStr) {
const sd = document.querySelector('shoulder-dates');
if (sd && sd.shadowRoot) {
const btn = sd.shadowRoot.querySelector('button[data-date="' + dateStr + '"]');
if (btn) { btn.click(); return; }
}
const params = new URLSearchParams(window.location.search);
params.set('OD', dateStr);
window.location.href = '/search/results?' + params.toString();
}

function paintCalendar(leftM, rightM) {
const cal = document.getElementById('as-cal');
if (!cal) return;

let allPts = [];
[leftM, rightM].forEach(ym => {
if (calDateCache[ym])
Object.values(calDateCache[ym]).forEach(d => {
if (d.awardPoints > 0) allPts.push(d.awardPoints);
});
});
const minPts = allPts.length ? Math.min(...allPts) : 0;
const maxPts = allPts.length ? Math.max(...allPts) : 0;

cal.innerHTML =
'<div style="display:grid;grid-template-columns:1fr 1fr;gap:16px"><div>' +
buildMonthGrid(leftM, minPts, maxPts) + '</div><div>' +
buildMonthGrid(rightM, minPts, maxPts) + '</div></div>';
cal.style.opacity = '1';

const lbl = document.getElementById('as-cal-range');
if (lbl) {
const lName = monthLabel(leftM).split(' ')[0];
const rFull = monthLabel(rightM);
lbl.textContent = leftM.split('-')[0] === rightM.split('-')[0]
? lName + ' – ' + rFull
: monthLabel(leftM) + ' – ' + rFull;
}

cal.querySelectorAll('.as-cal-cell[data-date]').forEach(cell => {
cell.addEventListener('click', () => {
const dt = cell.dataset.date;
const today = new Date().toISOString().split('T')[0];
if (dt < today) return;
calSelectedDate = dt;
paintCalendar(calDisplayedMonths[0], calDisplayedMonths[1]);
navigateToDate(dt);
});
});
}

function renderCalendar(leftM, rightM, loading) {
const cal = document.getElementById('as-cal');
if (!cal) return;
calDisplayedMonths = [leftM, rightM];

// Render immediately from cache
paintCalendar(leftM, rightM);
if (loading) cal.style.opacity = '0.5';

// Defer fetches to a macrotask so the browser paints the cached grid first
setTimeout(async () => {
await Promise.all([fetchMonthDates(leftM), fetchMonthDates(rightM)]);
if (calDisplayedMonths[0] === leftM && calDisplayedMonths[1] === rightM) {
paintCalendar(leftM, rightM);
}
}, 0);
}

function buildCalendar() {
calSelectedDate = new URLSearchParams(window.location.search).get('OD') ||
new Date().toISOString().split('T')[0];
seedCalCache();

const old = document.getElementById('as-cal-wrap');
if (old) old.remove();

const wrap = document.createElement('div');
wrap.id = 'as-cal-wrap';
wrap.innerHTML =
'<div id="as-cal-nav">' +
'<button id="as-cal-prev">◀</button>' +
'<span id="as-cal-range"></span>' +
'<button id="as-cal-next">▶</button>' +
'</div>' +
'<div id="as-cal"></div>';

const baggage = document.querySelector('a[href*="baggage"], [class*="baggage"]');
const heading = document.querySelector('h1, h2, [class*="Depart"]');
const insertAfter = baggage?.closest('p, div, span') || heading;
if (insertAfter && insertAfter.parentElement) {
insertAfter.parentElement.insertBefore(wrap, insertAfter.nextSibling);
}

const now = new Date();
const currentYM = now.getFullYear() + '-' + String(now.getMonth() + 1).padStart(2, '0');
const maxDate = new Date(now.getTime() + CAL_MAX_DAYS * 86400000);
const maxYM = maxDate.getFullYear() + '-' + String(maxDate.getMonth() + 1).padStart(2, '0');

document.getElementById('as-cal-prev').addEventListener('click', () => {
const newLeft = shiftMonth(calDisplayedMonths[0], -1);
if (newLeft < currentYM) return;
renderCalendar(newLeft, calDisplayedMonths[0], true);
});
document.getElementById('as-cal-next').addEventListener('click', () => {
const newRight = shiftMonth(calDisplayedMonths[1], 1);
if (newRight > maxYM) return;
renderCalendar(calDisplayedMonths[1], newRight, true);
});

const selMonth = calSelectedDate.substring(0, 7);
renderCalendar(selMonth, shiftMonth(selMonth, 1), true);
}

/* ─── Cleanup (when switching away from award mode) ─── */
function cleanup() {
document.querySelectorAll('.as-enrich, .as-seg-info, .as-sort-arrow, .as-filter-row, .as-clear-filters').forEach(el => el.remove());
document.querySelectorAll('[data-as-ready]').forEach(el => delete el.dataset.asReady);
const css = document.getElementById('as-enhancer-css');
if (css) css.remove();
const cal = document.getElementById('as-cal-wrap');
if (cal) cal.remove();
const banner = document.getElementById('as-profile-banner');
if (banner) banner.remove();
sortCol = null; sortDir = 0;
activeFilters.clear();
flightData = null;
// Restore hidden rows
document.querySelectorAll('tr[data-testid*="matrix-row"]').forEach(tr => tr.style.display = '');
}

/* ─── Main init ─── */
async function init() {
if (initRunning) return false;
if (!window.location.search.includes('ShoppingMethod=onlineaward')) {
cleanup();
return true; // stop polling — watchNavigation will re-trigger if user switches back
}
const rows = document.querySelectorAll('tr[data-testid*="matrix-row"]');
if (!rows.length) return false;
initRunning = true;

injectStyles();

flightData = await extractFlightData();
const dataRows = flightData?.[0]?.rows || [];

const headerEls = document.querySelectorAll('.fare-header-container');
const colMap = {};
headerEls.forEach((h, i) => { colMap[i] = SOLUTION_PREFIX + h.id; });

setupHeaders();

rows.forEach(tr => {
const card = tr.querySelector('[data-testid*="flight-card"]');
if (!card) return;
const dataRow = matchRowToData(tr, dataRows);
if (!dataRow) return;

enrichSegments(card, dataRow);

const tiles = tr.querySelectorAll('button[data-testid*="valuetile"]');
tiles.forEach((tile, colIdx) => {
const solKey = colMap[colIdx];
const sol = solKey ? dataRow.solutions[solKey] : null;
enrichTile(tile, sol, dataRow.segments);
});
});

sortCol = null; sortDir = 0;
activeFilters.clear();

buildCalendar();

initRunning = false;
return true;
}

/* ─── Poll for results + SPA navigation detection ─── */
function pollForResults() {
const start = Date.now();
const interval = setInterval(async () => {
if ((await init()) || Date.now() - start > POLL_TIMEOUT) clearInterval(interval);
}, POLL_INTERVAL);
}

function watchNavigation() {
let currentUrl = location.href;
const check = () => {
if (location.href !== currentUrl) {
currentUrl = location.href;
setTimeout(() => pollForResults(), 500);
}
};

const origPush = history.pushState;
history.pushState = function () {
origPush.apply(this, arguments);
check();
};
const origReplace = history.replaceState;
history.replaceState = function () {
origReplace.apply(this, arguments);
check();
};
window.addEventListener('popstate', check);

const observer = new MutationObserver((mutations) => {
for (const m of mutations) {
if (m.addedNodes.length > 0) {
const hasNewRows = [...m.addedNodes].some(n =>
n.nodeType === 1 && (n.matches?.('tr[data-testid*="matrix-row"]') ||
n.querySelector?.('tr[data-testid*="matrix-row"]'))
);
if (hasNewRows) {
setTimeout(() => init(), 300);
break;
}
}
}
});
const table = document.querySelector('table.resultsTable') ||
document.querySelector('tr[data-testid*="matrix-row"]')?.closest('table');
if (table) observer.observe(table, { childList: true, subtree: true });
}

/* ─── Start ─── */
pollForResults();
watchNavigation();
})();

--- 第 7 楼来自 xuezhai 的回复 (2026-03-20 21:49:22 PDT) ---

厉害了 zszs

--- 第 8 楼来自 xuezhai 的回复 (2026-03-20 21:54:40 PDT) ---

这个怎么提取呢?还要注册帐号?

--- 第 9 楼来自 zpahai 的回复 (2026-03-20 21:59:02 PDT) ---

不用 密码在上面啊

--- 第 10 楼来自 windywinter 的回复 (2026-03-20 22:03:47 PDT) ---

【引用自 zpahai】:
会在AS搜索界面增加一个浮动面板
这个浮动面板在哪里?找不到啊。

--- 第 11 楼来自 zpahai 的回复 (2026-03-20 22:08:14 PDT) ---

随便搜一个里程票 右下角会显示

--- 第 12 楼来自 dreambig 的回复 (2026-03-20 22:13:29 PDT) ---

好用!感谢!

这两个tool是怎么work的,会用到账户的信息去fetch data吗?频繁使用AS会block account或ip吗?

--- 第 13 楼来自 zpahai 的回复 (2026-03-20 22:16:48 PDT) ---

首先AS不需要登录就可以搜票,所以不建议在登录账户的情况使用这个插件

第一个只是修改了显示,并不涉及fetch当前搜索额外的数据

第二个是批量发request,目前AS并不存在rate limit和其他风控

--- 第 14 楼来自 dreambig 的回复 (2026-03-20 22:24:25 PDT) ---

got it thanks

--- 第 15 楼来自 windywinter 的回复 (2026-03-20 22:25:30 PDT) ---

右下角什么也没有的样子……

--- 第 16 楼来自 pisomnia 的回复 (2026-03-20 22:32:14 PDT) ---

这个工具看起来很有用啊 不过大佬能简单说明下怎么安装运行这两个脚本文件啊

--- 第 17 楼来自 Th3ret0go 的回复 (2026-03-20 22:47:02 PDT) ---

原来大家都是用脚本的,怪不得等我看到JL的时候黄花菜都凉了

--- 第 18 楼来自 Acoolalien 的回复 (2026-03-20 22:50:40 PDT) ---

感谢楼主喂饭,试了一下工具一完美运行,工具二原来是需要点这个小飞机图标。再次感谢楼主!

image3338×1428 255 KB

--- 第 19 楼来自 JPMorgan 的回复 (2026-03-20 23:31:06 PDT) ---

厉害啊厉害

--- 第 21 楼来自 Nongnong 的回复 (2026-03-21 00:06:49 PDT) ---

厉害,这是又有大佬要冲钛了

--- 第 22 楼来自 Nongnong 的回复 (2026-03-21 00:09:17 PDT) ---

工具二成功了,工具一怎么好像没有运行

--- 第 23 楼来自 Yidegu 的回复 (2026-03-21 00:13:30 PDT) ---

好活儿 早日lounge相见

是不是把两个工具分开发表就能有两个升钛贴了该死的PM思维

--- 第 24 楼来自 舒米zwc 的回复 (2026-03-21 00:23:06 PDT) ---

太好用了大佬

--- 第 25 楼来自 舒米zwc 的回复 (2026-03-21 00:23:49 PDT) ---

image1512×458 54.4 KB

就是这种票点进去根本没有唉

--- 第 26 楼来自 Psych0 的回复 (2026-03-21 01:36:23 PDT) ---

好用 但好像搜出来的票时区未换算

--- 第 27 楼来自 smlee 的回复 (2026-03-21 05:29:34 PDT) ---

赞一个! 多谢大牛!

--- 第 28 楼来自 james.an0706 的回复 (2026-03-21 06:28:49 PDT) ---

感谢!只有第二个tool的link,第一个的呢 AS enhancer

--- 第 29 楼来自 ChatPPT 的回复 (2026-03-21 06:38:44 PDT) ---

昨晚要定的AA执飞的欧美商务舱也是幽灵

--- 第 30 楼来自 zpahai 的回复 (2026-03-21 06:48:18 PDT) ---

第一个在楼里 在第二个下面两层

--- 第 31 楼来自 zpahai 的回复 (2026-03-21 06:49:15 PDT) ---

6个的一般都是真幽灵,这个如果不用ba或者点进去看没法验证的

--- 第 32 楼来自 ytc 的回复 (2026-03-21 06:52:34 PDT) ---

感谢楼主,已点赞。有个小问题,每次用它会从你github仓库更新最新代码是吗?但是我在你git主页没看到这个项目

--- 第 33 楼来自 zpahai 的回复 (2026-03-21 06:55:40 PDT) ---

暂时是private的

--- 第 34 楼来自 Ava.太太太后 的回复 (2026-03-21 08:03:09 PDT) ---

装上试了一下,批量搜索那个真的好用!以前找AS里程票要一条一条手搜,现在一下子出来一堆,省了不少时间

--- 第 35 楼来自 Shamyer 的回复 (2026-03-21 08:12:59 PDT) ---

太强了大佬,感谢分享!火钳刘明

--- 第 36 楼来自 舒米zwc 的回复 (2026-03-21 08:15:53 PDT) ---

谢谢,其他几条线路显示一张,点view on Alaska之后也找不到,可能也是幽灵

--- 第 37 楼来自 Shamyer 的回复 (2026-03-21 08:30:20 PDT) ---

这个神器会不会很快出现在某星球上

--- 第 38 楼来自 zpahai 的回复 (2026-03-21 08:36:31 PDT) ---

一张说明是搜到到显示结果之间已经被人hold了

--- 第 39 楼来自 Shamyer 的回复 (2026-03-21 08:37:54 PDT) ---

【引用自 Ava.太太太后】:
程票要一条一条手搜,现在一下子出来一堆,省了不少时间
确实是,我刚刚试着用,发现一张JL 75k,我还点进去验证了。过了差不过5分钟再看已经被人拿走了

--- 第 40 楼来自 94027 的回复 (2026-03-21 08:38:06 PDT) ---

某星球现在已经饥不择食了

连听课房、付费买会员都要强力推荐

--- 第 41 楼来自 WilliamOne 的回复 (2026-03-21 08:38:13 PDT) ---

感谢分享。

--- 第 42 楼来自 舒米zwc 的回复 (2026-03-21 08:40:49 PDT) ---

嗯嗯,谢谢大佬

--- 第 43 楼来自 JPMorgan 的回复 (2026-03-21 09:10:05 PDT) ---

是我安装姿势不对吗,我装了后没有反应

image2606×1406 346 KB

--- 第 44 楼来自 zpahai 的回复 (2026-03-21 09:20:41 PDT) ---

我大概懂了,是不是tampermonkey设置没有开allow user script?我后面会改成github/gist应该就没问题了,chrome浏览器插件developer mode也开一下,就是右上角的

image2879×1462 219 KB

--- 第 45 楼来自 Renya_Kojima 的回复 (2026-03-21 09:28:35 PDT) ---

给大佬冲完钛之后有一点点想挪到白金了… 这个工具给某星球和黄牛免费拿走之后一般人更是抢不到票了…

--- 第 46 楼来自 zpahai 的回复 (2026-03-21 09:33:29 PDT) ---

顺便送一些availability吧

2026-04-08 | AS Business · 95K · $5.60 · 1s · HA823

2026-08-16 | AS Business · 95K · $24.80 · 7s · AS823

2026-08-23 | AS Business · 95K · $24.80 · 7s · AS823

2026-08-29 | AS Business · 95K · $24.80 · 4s · AS823

2026-03-25 | AS Business · 75K · $55.63 · 6s · JL2

2026-03-31 | AS Business · 75K · $55.63 · 6s · JL2

2027-01-31 | AS Business · 75K · $55.63 · 6s · JL2

2026-03-26 | AS Business · 95K · $48.73 · 1s · AA170

2027-02-11 | AS Business · 75K · $55.63 · 6s · JL16

2026-08-01 | AS Business · 95K · $27.40 · 9s · JX11

2026-10-19 | AS Business · 95K · $27.40 · 9s · JX11

2026-07-17 | AS Business · 95K · $58.53 · 9s · JX2

2026-09-16 | AS Business · 75K · $46.43 · 1s · JX2

2026-04-17 | AS Business · 60K · $56.93 · 1s · HA824

2026-07-26 | AS Business · 60K · $56.93 · 3s · JL68, AS824

2026-06-01 | AS Business · 75K · $55.63 · 6s · JL12

2026-09-16 | AS Business · 85K · $52.03 · 9s · JX32

2026-09-16 | AS Business · 75K · $52.03 · 9s · JX12

2026-04-27 | AS Business · 110K · $55.63

--- 第 47 楼来自 Nu1l 的回复 (2026-03-21 09:47:22 PDT) ---

同意zszs

--- 第 48 楼来自 Nu1l 的回复 (2026-03-21 09:48:10 PDT) ---

给大牛点赞。感觉以后票更难抢了

--- 第 49 楼来自 doudoudou 的回复 (2026-03-21 10:22:57 PDT) ---

我怎么看不到小飞机了 就看到过一次

--- 第 50 楼来自 doudoudou 的回复 (2026-03-21 10:27:19 PDT) ---

等稳定了再回来看看

--- 第 51 楼来自 JPMorgan 的回复 (2026-03-21 11:27:13 PDT) ---

谢谢!开了allow user scripts后就能看到小飞机了!

--- 第 52 楼来自 Nu1l 的回复 (2026-03-21 11:28:27 PDT) ---

这个搜索界面怎么弄出来?还是自动会跳出来?

--- 第 53 楼来自 Nu1l 的回复 (2026-03-21 11:30:40 PDT) ---

看到了。感谢!

--- 第 55 楼来自 lenzjay 的回复 (2026-03-21 11:39:28 PDT) ---

手动点赞先

--- 第 56 楼来自 james.an0706 的回复 (2026-03-21 13:51:35 PDT) ---

是不是改的时候把第一个tool AS enhancer不小心删掉了,还是没找到link啊

--- 第 57 楼来自 FTW 的回复 (2026-03-21 13:54:03 PDT) ---

点赞慢慢看

--- 第 58 楼来自 murasaki 的回复 (2026-03-21 15:03:54 PDT) ---

厉害了

--- 第 59 楼来自 zpahai 的回复 (2026-03-21 15:32:51 PDT) ---

【引用自 未知】:
[AS]里程票搜索工具和结果增强显示 — AS Enhancer + Batch Search 航空常旅客
AS Enhancer

--- 第 60 楼来自 牛油果修勾 的回复 (2026-03-21 17:15:43 PDT) ---

lz 太厉害了!支持冲钛

要不要在code里加一些验证泥潭登陆等级的东西?现在这样确实太容易被票贩子/xhs/一些别有用心的人转载出去了。不过还是感谢lz,搓出这样一个工具方便谭友太伟大了

--- 第 61 楼来自 unclewang 的回复 (2026-03-21 17:19:35 PDT) ---

Screenshot 2026-03-21 at 5.19.18 PM1452×372 32.4 KB

好像很厉害

--- 第 62 楼来自 LindaW 的回复 (2026-03-21 20:35:29 PDT) ---

感谢大佬!

--- 第 63 楼来自 Kcus 的回复 (2026-03-21 20:44:55 PDT) ---

nb 了

--- 第 64 楼来自 Kcus 的回复 (2026-03-21 20:54:02 PDT) ---

怎么样apply code?

--- 第 65 楼来自 shuaibi666 的回复 (2026-03-21 22:52:25 PDT) ---

感谢大佬分享! 请教一下,AS Enhancer日历貌似只显示经济舱的价格?

--- 第 66 楼来自 ALousaBao 的回复 (2026-03-22 07:45:30 PDT) ---

卧槽我刚想vibe一个这个冲钛 钛金还是太快了

--- 第 67 楼来自 Nongnong 的回复 (2026-03-22 09:05:58 PDT) ---

楼主也是刚变钛

--- 第 68 楼来自 ALousaBao 的回复 (2026-03-22 09:07:46 PDT) ---

我前两天刚vibe了一个 懒得登出AS账号就没试

今天发现lz已经冲钛了

--- 第 69 楼来自 Nongnong 的回复 (2026-03-22 09:11:53 PDT) ---

泥潭高手云集

--- 第 70 楼来自 zpahai 的回复 (2026-03-22 09:17:17 PDT) ---

AS应该是最简单的了

--- 第 71 楼来自 zpahai 的回复 (2026-03-22 09:20:45 PDT) ---

直接新建脚本 粘贴到油猴插件里面就行

--- 第 72 楼来自 zpahai 的回复 (2026-03-22 09:38:50 PDT) ---

这个是match原来的AS平铺日期显示逻辑的显示的是当天最低价,我可以看看有没有所有数据,有的话可以加一个开关

--- 第 73 楼来自 shuaibi666 的回复 (2026-03-22 11:02:29 PDT) ---

感谢大佬!!

--- 第 74 楼来自 jackalsin 的回复 (2026-03-22 14:15:11 PDT) ---

各位群友能给个日期,from 和 to么

我开了incognito,只enable Tamper Monkey和两个script,还是看不见小飞机啊

或者chrome版本给一个?

我用的 sea tpe points Apr 19

--- 第 75 楼来自 zpahai 的回复 (2026-03-22 14:19:44 PDT) ---

【引用自 未知】:
[AS]里程票搜索工具和结果增强显示 — AS Enhancer + Batch Search 航空常旅客
我大概懂了,是不是tampermonkey设置没有开allow user script?我后面会改成github/gist应该就没问题了,chrome浏览器插件developer mode也开一下,就是右上角的
[image]
大概率是这个问题?因为不是从链接直接安装而是手动所以算作user script?

--- 第 76 楼来自 jackalsin 的回复 (2026-03-22 14:23:38 PDT) ---

确实是我的问题,为了防止中奖,特意新注册的Profile

--- 第 77 楼来自 jackalsin 的回复 (2026-03-22 14:24:49 PDT) ---

顺带再提个建议吧,我记得response里有 多少EQM和Earned miles,都显示舱位了,其实这个也可以扔扔上去

--- 第 78 楼来自 李同学 的回复 (2026-03-22 20:08:05 PDT) ---

image1222×518 30 KB

笑死,全美没票

--- 第 79 楼来自 zpahai 的回复 (2026-03-22 21:04:46 PDT) ---

这个我看了并没有这俩field,可能需要到下一步才会有

--- 第 80 楼来自 ChatPPT 的回复 (2026-03-22 23:39:25 PDT) ---

牛逼~

不过当然商务舱都被抢没了。。看到的QR(原是我痴心妄想)和AA商务舱都是幽灵

--- 第 81 楼来自 zpahai 的回复 (2026-03-23 00:56:30 PDT) ---

已严肃修复

--- 第 82 楼来自 zpahai 的回复 (2026-03-23 00:57:34 PDT) ---

目前加了登录验证和混淆,只要不让外面用就行

--- 第 83 楼来自 doudoudou 的回复 (2026-03-23 01:08:09 PDT) ---

已经钛金了

--- 第 84 楼来自 CubeOvO 的回复 (2026-03-23 16:40:47 PDT) ---

点赞,各仓位都剩6的票可以过滤掉或者标一下,大概率是幽灵

--- 第 85 楼来自 Sebzhuc 的回复 (2026-03-23 16:48:31 PDT) ---

提个issue,第一个脚本貌似搜索的时候在现金和里程票之间切换后日历视图会显示错误的数值

--- 第 86 楼来自 zpahai 的回复 (2026-03-24 07:42:27 PDT) ---

我有空看看吧

--- 第 87 楼来自 zpahai 的回复 (2026-03-24 07:43:27 PDT) ---

OneworldSaver Pro — 3/24/2026

2027-01-13 | AA Business · 60K · $44.43

2027-01-14 | AA Business · 60K · $44.43

2027-01-15 | AA Business · 60K · $44.43

2027-01-17 | AA Business · 60K · $44.43

2027-01-18 | AA Business · 60K · $44.43

2026-07-21 | AA Business · 60K · $44.43

2027-01-13 | AA Business · 60K · $44.43

2027-01-14 | AA Business · 60K · $44.43

2027-01-15 | AA Business · 60K · $44.43

2027-01-18 | AA Business · 60K · $44.43

2027-01-13 | AA Business · 60K · $44.43

2027-01-14 | AA Business · 60K · $44.43

2027-01-15 | AA Business · 60K · $44.43

2027-01-18 | AA Business · 60K · $44.43

2026-03-24 | AA Business · 60K · $11.20

2026-03-25 | AA Business · 60K · $11.20

2026-04-17 | AA Business · 60K · $11.20

2026-08-25 | AA First · 80K · $5.60

2026-08-25 | AA First · 80K · $5.60

2026-04-04 | AA First · 80K · $5.60

2026-04-04 | AA First · 80K · $5.60

2026-08-26 | AA Business · 60K · $5.60

2026-08-26 | AA Business · 60K · $5.60

2026-08-26 | AA Business · 60K · $5.60

2026-08-26 | AA Business · 60K · $5.60

2026-08-26 | AA Business · 60K · $5.60

--- 第 88 楼来自 jwkill1 的回复 (2026-03-24 21:19:30 PDT) ---

小飞机那个标突然没了,请问batch search如何debug?

--- 第 89 楼来自 zpahai 的回复 (2026-03-24 22:46:44 PDT) ---

2026-11-08 | AS Business · 75K · $51.93 · 1s · JX32, JX32, JX32, JX32, JX32, JX32, JX32, JX32, JX32, JX32, JX32, JX32, JX32, JX32, JX32, JX32

2026-12-19 | AS Business · 75K · $51.93 · 1s · JX12

2026-11-08 | AS Business · 75K · $51.93 · 1s · JX32, JX32, JX32, JX32, JX32, JX32

2026-12-19 | AS Business · 75K · $46.33 · 1s · JX12

2026-11-08 | AS Business · 75K · $51.93 · 1s · JX32, JX32, JX32, JX32, JX32, JX32, JX32, JX32, JX32

2026-12-19 | AS Business · 75K · $51.93 · 1s · JX12

2027-02-11 | AS Business · 95K · $18.10 · 6s · AS626, AS247, AS731, AS732, AS743

2027-02-12 | AS Business · 95K · $18.10 · 6s · AS1489, AS726

2026-10-16 | AS Business · 110K · $62.53 · 1s · JL18, JL18, JL18

2026-09-08 | AS Business · 75K · $55.53 · 6s · JL6

2026-11-08 | AS Business · 85K · $51.93 · 1s · JX32, JX32, JX32

2026-11-08 | AS Business · 75K · $46.33 · 1s · JX32

2026-11-08 | AS Business · 85K · $51.93 · 1s · JX32, JX32, JX32, JX32

2026-11-08 | AS Business · 85K · $51.93 · 1s · JX32, JX32, JX32

2026-11-08 | AS Business · 85K · $51.93 · 1s · JX32, JX32, JX32, JX32, JX32, JX32, JX32, JX32

--- 第 90 楼来自 zpahai 的回复 (2026-03-25 22:34:50 PDT) ---

2026-05-19 | AA Business · 60K · $44.43 · 1s · JL242 → AS824, JL240 → AS824

2026-05-20 | AA Business · 60K · $44.43 · 1s · JL236 → AS824, JL234 → AS824, JL232 → AS824

2026-06-10 | AA Business · 60K · $44.43 · 1s · JL236 → AS824, JL234 → AS824, JL232 → AS824

2026-06-25 | AA Business · 60K · $44.43 · 1s · JL242 → JL68, JL240 → JL68

2026-06-26 | AA Business · 60K · $44.43 · 1s · JL236 → JL68, JL234 → JL68, JL232 → JL68

2027-01-13 | AA Business · 60K · $44.43 · 1s · JL242 → JL68, JL240 → JL68

2027-01-14 | AA Business · 60K · $44.43 · 2s · JL236 → JL68, JL234 → JL68, JL232 → JL68, JL242 → JL68, JL240 → JL68

2027-01-17 | AA Business · 60K · $44.43 · 1s · JL240 → JL68

2027-01-18 | AA Business · 60K · $44.43 · 1s · JL236 → JL68, JL234 → JL68, JL232 → JL68

2026-05-19 | AA Business · 60K · $44.43 · 1s · JL464 → AS824, JL462 → AS824

2026-05-20 | AA Business · 60K · $44.43 · 1s · JL456 → AS824, JL454 → AS824

2026-06-09 | AA Business · 60K · $44.43 · 1s · JL464 → AS824

2026-06-10 | AA Business · 60K · $44.43 · 1s · JL456 → AS824, JL454 → AS824

2026-06-25 | AA Business · 60K · $44.43 · 1s · JL464 → JL68

2026-06-26 | AA Business · 60K · $44.43 · 1s · JL454 → JL68

2027-01-13 | AA Business · 60K · $44.43 · 1s · JL464 → JL68

2027-01-14 | AA Business · 60K · $44.43 · 1s · JL456 → JL68, JL454 → JL68

2027-01-18 | AA Business · 60K · $44.43 · 1s · JL456 → JL68, JL454 → JL68

2026-05-19 | AA Business · 60K · $44.43 · 1s · JL486 → AS824

2026-05-20 | AA Business · 60K · $44.43 · 1s · JL478 → AS824, JL476 → AS824, JL474 → AS824

2026-06-25 | AA Business · 60K · $44.43 · 1s · JL486 → JL68, JL484 → JL68

2026-06-26 | AA Business · 60K · $44.43 · 1s · JL480 → JL68, JL478 → JL68, JL476 → JL68, JL474 → JL68

2026-07-21 | AA Business · 60K · $44.43 · 1s · JL474 → JL68

2027-01-14 | AA Business · 60K · $44.43 · 1s · JL480 → JL68, JL478 → JL68, JL476 → JL68, JL474 → JL68

2027-01-18 | AA Business · 60K · $44.43 · 1s · JL478 → JL68, JL476 → JL68, JL474 → JL68

2026-05-19 | AA Business · 60K · $44.43 · 1s · JL332 → AS824, JL330 → AS824

2026-05-20 | AA Business · 60K · $44.43 · 1s · JL312 → AS824, JL310 → AS824, JL304 → AS824, JL302 → AS824, JL300 → AS824

2026-06-09 | AA Business · 60K · $44.43 · 1s · JL332 → AS824, JL330 → AS824, JL328 → AS824, JL322 → AS824

2026-06-10 | AA Business · 60K · $44.43 · 1s · JL312 → AS824, JL310 → AS824, JL304 → AS824, JL302 → AS824, JL300 → AS824

2026-04-05 | AA Business · 60K · $44.43 · 1s · JL922 → JL68, JL920 → JL68, JL916 → JL68, JL914 → JL68

2026-05-19 | AA Business · 60K · $44.43 · 1s · JL922 → AS824, JL920 → AS824, JL918 → AS824, JL914 → AS824

2026-05-20 | AA Business · 60K · $44.43 · 1s · JL906 → AS824, JL904 → AS824, JL902 → AS824, JL900 → AS824

2026-04-29 | AA Business · 60K · $11.20 · 2s · AA1996 → JL69, AA3317 → JL69, AA1999 → JL69, AA711 → JL69

2027-01-08 | AA Business · 60K · $11.20 · 2s · AA1247 → JL69, AA2900 → JL69, AA2793 → JL69

2026-05-19 | AA Business · 60K · $44.43 · 1s · JL530 → AS824, JL528 → AS824, JL526 → AS824

2026-05-20 | AA Business · 60K · $44.43 · 1s · JL508 → AS824, JL500 → AS824

2026-04-21 | AA Business · 60K · $11.20 · 1s · AS489 → JL69

2026-03-26 | AA Business · 60K · $53.03 · 1s · JL60 → AA3176

2026-03-26 | AA Business · 60K · $47.43 · 2s · JL60

2026-04-17 | AA Business · 60K · $11.20 · 2s · AA300 → JL69

2026-03-26 | AA Business · 60K · $44.43 · 1s · JL62

2026-04-12 | AA Business · 60K · $5.60 · 2s · JL67 → JL241, JL67 → JL237

2026-05-12 | AA Business · 60K · $5.60 · 1s · AS823 → JL101

2026-03-27 | AA Business · 60K · $43.13 · 1s · JL300 → JL2

2026-10-18 | AS Business · 60K · $56.93 · 1s · JL18, JL18, JL18

2026-04-10 | AS Business · 60K · $18.10 · 2s · AS2110

--- 第 91 楼来自 zpahai 的回复 (2026-03-25 22:35:14 PDT) ---

OneworldSaver Pro — 3/26/2026

2026-06-04 | AA First · 80K · $43.13 · 1s · JL332 → JL10, JL330 → JL10, JL328 → JL10, JL326 → JL10, JL322 → JL10, JL320 → JL10, JL318 → JL10, JL316 → JL10, JL314 → JL10, JL312 → JL10

2026-10-15 | AA First · 80K · $43.13 · 1s · JL332 → JL10, JL330 → JL10, JL328 → JL10, JL326 → JL10, JL322 → JL10, JL320 → JL10, JL318 → JL10, JL316 → JL10, JL314 → JL10, JL312 → JL10

2027-02-18 | AA First · 80K · $43.13 · 2s · JL332 → JL10, JL330 → JL10, JL328 → JL10, JL326 → JL10, JL324 → JL10, JL322 → JL10, JL320 → JL10, JL318 → JL10, JL316 → JL10, JL314 → JL10, JL312 → JL10, JL310 → JL10, JL308 → JL10, JL306 → JL10

2027-02-20 | AA First · 80K · $43.13 · 2s · JL302 → JL10, JL300 → JL10, JL312 → JL56, JL310 → JL56, JL308 → JL56, JL306 → JL56, JL304 → JL56, JL302 → JL56, JL300 → JL56

2027-02-18 | AA First · 80K · $43.13 · 1s · JL228 → JL10, JL224 → JL10

2027-02-19 | AA First · 80K · $44.43 · 2s · JL228 → JL56

2027-02-20 | AA First · 80K · $43.13 · 2s · JL220 → JL10, JL220 → JL56

2026-06-04 | AA First · 80K · $43.13 · 1s · JL138 → JL10, JL130 → JL10, JL128 → JL10, JL126 → JL10, JL124 → JL10, JL118 → JL10, JL116 → JL10

2026-10-15 | AA First · 80K · $43.13 · 1s · JL116 → JL10

2027-02-18 | AA First · 80K · $43.13 · 2s · JL138 → JL10, JL130 → JL10, JL128 → JL10, JL126 → JL10, JL124 → JL10, JL3006 → JL10, JL118 → JL10, JL116 → JL10, JL114 → JL10

2026-05-25 | AA First · 80K · $5.60 · 1s · JL9

2027-01-21 | AA First · 80K · $5.60 · 1s · JL9

2027-02-04 | AA First · 80K · $5.60 · 1s · JL9

2026-04-07 | AA First · 80K · $5.60 · 1s · JL5 → JL901, JL5 → JL903, JL5 → JL905, JL5 → JL907, JL5 → JL909

2027-02-18 | AA First · 80K · $5.60 · 2s · JL5 → JL915, JL5 → JL919, JL5 → JL921

2027-02-19 | AA First · 80K · $5.60 · 2s · JL5 → JL925, JL3 → JL915, JL3 → JL919, JL3 → JL921, JL3 → JL925

2026-06-18 | AA First · 80K · $43.13 · 1s · JL524 → JL2, JL522 → JL2, JL518 → JL2, JL516 → JL2

2026-10-15 | AA First · 80K · $43.13 · 1s · JL530 → JL2, JL528 → JL2, JL526 → JL2, JL524 → JL2, JL522 → JL2, JL518 → JL2

2027-02-18 | AA First · 80K · $5.60 · 2s · JL9 → JL303, JL9 → JL305, JL9 → JL307, JL9 → JL311, JL9 → JL313, JL9 → JL315, JL9 → JL317, JL55 → JL303, JL9 → JL321, JL55 → JL305, JL55 → JL307, JL9 → JL325, JL55 → JL311, JL9 → JL327, JL55 → JL313, JL9 → JL329, JL55 → JL315, JL9 → JL331, JL55 → JL317, JL9 → JL333, JL9 → JL335, JL55 → JL321

2027-02-19 | AA First · 80K · $5.60 · 2s · JL55 → JL329, JL55 → JL331, JL55 → JL333, JL55 → JL335

2026-10-15 | AA First · 80K · $43.13 · 1s · JL918 → JL10, JL916 → JL10, JL904 → JL10

2027-02-18 | AA First · 80K · $43.13 · 2s · JL922 → JL10, JL920 → JL10, JL918 → JL10, JL916 → JL10, JL914 → JL10, JL988 → JL10, JL912 → JL10, JL910 → JL10, JL908 → JL10, JL906 → JL10, JL904 → JL10, JL902 → JL10

2027-02-18 | AA First · 80K · $5.60 · 2s · JL9 → JL915, JL9 → JL919, JL9 → JL921, JL55 → JL915, JL9 → JL925

2027-02-19 | AA First · 80K · $5.60 · 2s · JL55 → JL925

2026-06-04 | AA First · 80K · $43.13 · 1s · JL530 → JL10, JL528 → JL10, JL526 → JL10, JL524 → JL10, JL522 → JL10, JL518 → JL10, JL516 → JL10, JL514 → JL10, JL512 → JL10, JL510 → JL10, JL508 → JL10

2027-02-18 | AA First · 80K · $43.13 · 1s · JL530 → JL10, JL528 → JL10, JL526 → JL10, JL524 → JL10, JL522 → JL10, JL520 → JL10, JL518 → JL10, JL516 → JL10, JL514 → JL10, JL512 → JL10, JL510 → JL10, JL508 → JL10, JL506 → JL10, JL504 → JL10

2027-02-18 | AA First · 80K · $5.60 · 2s · JL9 → JL101, JL9 → JL103, JL9 → JL111, JL9 → JL113, JL9 → JL115, JL9 → JL119, JL55 → JL101, JL9 → JL121, JL55 → JL103, JL9 → JL125, JL9 → JL127, JL55 → JL111, JL55 → JL113, JL9 → JL133, JL9 → JL3009, JL55 → JL115, JL9 → JL137, JL9 → JL139, JL55 → JL119, JL55 → JL121, JL55 → JL125

2027-02-19 | AA First · 80K · $5.60 · 2s · JL55 → JL133, JL55 → JL3009, JL55 → JL137, JL55 → JL139

2027-02-18 | AA First · 80K · $5.60 · 2s · JL15 → JL303, JL15 → JL305, JL15 → JL307, JL15 → JL311, JL15 → JL313, JL15 → JL315, JL15 → JL317, JL15 → JL321, JL15 → JL325, JL15 → JL327

2027-02-19 | AA First · 80K · $5.60 · 1s · JL15 → JL333, JL15 → JL335

2026-04-29 | AA Business · 80K · $11.20 · 2s · AA1996 → JL69, AA3317 → JL69, AA1999 → JL69, AA711 → JL69

2027-01-08 | AA Business · 80K · $11.20 · 2s · AA1247 → JL69, AA2900 → JL69, AA2793 → JL69

2027-02-18 | AA First · 80K · $5.60 · 2s · JL15 → JL915, JL15 → JL919, JL15 → JL921

2027-02-19 | AA First · 80K · $5.60 · 1s · JL15 → JL925

2027-02-18 | AA First · 80K · $5.60 · 2s · JL11 → JL501, JL11 → JL505, JL11 → JL515, JL11 → JL517

2027-02-19 | AA First · 80K · $5.60 · 2s · JL11 → JL525, JL11 → JL527, JL11 → JL529, JL11 → JL531

2027-02-19 | AA First · 80K · $43.13 · 2s · JL138 → JL12, JL130 → JL12, JL128 → JL12, JL126 → JL12, JL124 → JL12, JL3006 → JL12, JL118 → JL12, JL116 → JL12, JL114 → JL12

2027-02-20 | AA First · 80K · $43.13 · 2s · JL104 → JL12, JL102 → JL12

2027-02-19 | AA First · 80K · $43.13 · 2s · JL902 → JL6

2027-02-20 | AA First · 80K · $43.13 · 2s · JL910 → JL4, JL908 → JL4, JL906 → JL4, JL904 → JL4, JL902 → JL4

2027-02-18 | AA First · 80K · $5.60 · 2s · JL5 → JL101, JL5 → JL103, JL5 → JL111, JL5 → JL113, JL5 → JL115, JL5 → JL119, JL5 → JL121, JL5 → JL125, JL5 → JL127

2027-02-19 | AA First · 80K · $5.60 · 2s · JL3 → JL101, JL5 → JL137, JL5 → JL139, JL3 → JL103, JL3 → JL111, JL3 → JL113, JL3 → JL115, JL3 → JL119, JL3 → JL121, JL3 → JL125, JL3 → JL127, JL3 → JL133, JL3 → JL3009, JL3 → JL137, JL3 → JL139

2026-05-19 | AA Business · 80K · $44.43 · 1s · JL524 → AS824, JL522 → AS824

2026-05-20 | AA Business · 80K · $44.43 · 1s · JL506 → AS824, JL504 → AS824, JL502 → AS824

2026-06-18 | AA First · 80K · $43.13 · 1s · JL138 → JL2, JL130 → JL2, JL128 → JL2, JL126 → JL2, JL124 → JL2

2027-02-18 | AA First · 80K · $43.13 · 2s · JL464 → JL10, JL462 → JL10, JL460 → JL10, JL456 → JL10

2026-03-26 | AA First · 80K · $53.03 · 3s · JL60 → AA12, JL60 → AA394

2026-04-21 | AA Business · 80K · $11.20 · 1s · AS489 → JL69

2027-02-18 | AA First · 80K · $5.60 · 2s · JL9 → JL501, JL9 → JL505, JL9 → JL515, JL9 → JL517, JL55 → JL501, JL55 → JL505, JL9 → JL525, JL9 → JL527, JL9 → JL529

2026-04-17 | AA Business · 80K · $11.20 · 2s · AA300 → JL69

2027-02-20 | AA First · 80K · $43.13 · 2s · JL910 → JL16, JL908 → JL16, JL906 → JL16, JL904 → JL16, JL902 → JL16

2027-02-18 | AA First · 80K · $5.60 · 2s · JL15 → JL101, JL15 → JL103, JL15 → JL111, JL15 → JL113, JL15 → JL121, JL15 → JL125

2027-02-20 | AA First · 80K · $43.13 · 2s · JL302 → JL12, JL300 → JL12

2027-02-18 | AA First · 80K · $5.60 · 2s · JL11 → JL303, JL11 → JL305, JL11 → JL307, JL11 → JL311, JL11 → JL313, JL11 → JL315

2027-02-18 | AA First · 80K · $5.60 · 2s · JL11 → JL915, JL11 → JL919

2027-02-20 | AA First · 80K · $43.13 · 2s · JL500 → JL12

2027-02-18 | AA First · 80K · $5.60 · 2s · JL11 → JL111, JL11 → JL113, JL11 → JL115, JL11 → JL119, JL11 → JL121, JL11 → JL125

2027-02-20 | AA First · 80K · $43.13 · 2s · JL322 → JL4, JL302 → JL6, JL320 → JL4, JL300 → JL6, JL318 → JL4, JL316 → JL4, JL314 → JL4, JL312 → JL4, JL310 → JL4, JL308 → JL4, JL306 → JL4, JL304 → JL4, JL300 → JL4

2027-02-19 | AA First · 80K · $5.60 · 2s · JL3 → JL311, JL3 → JL313, JL3 → JL315, JL3 → JL317, JL3 → JL321, JL3 → JL325, JL3 → JL327, JL3 → JL329, JL3 → JL331, JL3 → JL333, JL3 → JL335

2026-04-07 | AA First · 80K · $5.60 · 1s · JL5 → JL531, JL5 → JL501, JL5 → JL503, JL5 → JL507

2027-02-19 | AA First · 80K · $43.13 · 2s · JL138 → JL6, JL130 → JL6, JL128 → JL6, JL126 → JL6, JL124 → JL6, JL3006 → JL6, JL118 → JL6, JL116 → JL6, JL114 → JL6, JL138 → JL4

2026-06-18 | AA First · 80K · $43.13 · 1s · JL332 → JL2, JL330 → JL2, JL328 → JL2, JL326 → JL2, JL322 → JL2, JL320 → JL2, JL318 → JL2

--- 第 92 楼来自 olaf 的回复 (2026-03-25 22:40:52 PDT) ---

感覺不少幽靈啊

--- 第 93 楼来自 zpahai 的回复 (2026-03-25 22:42:49 PDT) ---

已经简单filter过一次了,不能保证远期没幽灵,但是我测试下来一半一半吧,至少能到填信息的一步

--- 第 94 楼来自 zpahai 的回复 (2026-03-25 22:45:13 PDT) ---

最近JL抽风太多 昨天差点都炸了 每个日期都有幽灵票

--- 第 95 楼来自 Shamyer 的回复 (2026-03-31 15:16:26 PDT) ---

大佬,请问填完信息点确定显示无法confirm flight就是所谓的幽灵票吗

--- 第 96 楼来自 zpahai 的回复 (2026-03-31 20:44:16 PDT) ---

最后一步付款/出票时报错就是

--- 第 97 楼来自 zpahai 的回复 (2026-04-04 22:31:28 PDT) ---
--- 第 98 楼来自 Alzuhod 的回复 (2026-04-07 21:05:13 PDT) ---

太赞了!确实我的allow user scripts就能看到了!

--- 第 99 楼来自 savvygogh 的回复 (2026-04-14 11:01:06 PDT) ---

不知道为什么batch searcher突然不进行搜索了,已经登陆泥潭

--- 第 100 楼来自 bbqurhero 的回复 (2026-04-14 11:28:06 PDT) ---

AS Batch award search, 鼠标hover 要是能显示还剩下几个位置就更好了

--- 第 101 楼来自 zpahai 的回复 (2026-04-15 19:29:04 PDT) ---

可能主页不一定能获取到cookie,手动搜一张票再试试

--- 第 102 楼来自 zpahai 的回复 (2026-04-15 19:32:19 PDT) ---

更新了,有的没的都全加上了

--- 第 103 楼来自 fdorakon 的回复 (2026-04-17 15:17:33 PDT) ---

改版后好像有bug了, 点不开航班 如果只有一班, 只能手动再搜

--- 第 104 楼来自 zpahai 的回复 (2026-04-17 16:33:18 PDT) ---

确实最后一行有bug,在修了

--- 第 105 楼来自 fdorakon 的回复 (2026-04-17 16:39:37 PDT) ---

大佬辛苦

--- 第 106 楼来自 zpahai 的回复 (2026-04-17 16:40:45 PDT) ---

修好了,应该更新就好了

--- 第 107 楼来自 l6239 的回复 (2026-05-01 21:29:32 PDT) ---

感谢lz让我我感受到了来自泥潭的温暖