front/nanafa/assets/map/route-map.html
zzh 5a26ae0048 feat: 小红书模块迁移、登录重构与 xhs-agent-desktop 桌面 Agent
- 新增小红书前端模块(启动服务、内容分析、爆款创作、发布记录、技能卡片、拟人设置、AI 助手)

- 新增 xhs-agent-desktop 独立桌面 Agent,对接 OMS 任务调度

- 登录页重构,整合微信登录与 OMS JWT 认证

- 订单、渠道、用户中心等页面与 API 增强

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-30 16:13:13 +08:00

722 lines
29 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>香港全球货运航线网络(目的国黄色背景版)</title>
<!-- 自托管:使用项目内静态资源,避免 CDN 请求 -->
<script src="./echarts.min.js"></script>
<script src="./login-map-minimal-data.js"></script>
<script src="./geo/world.js"></script>
<style>
body,html { margin:0; padding:0; width:100%; height:100%; overflow:hidden; background:#0a0e17; transition: background-color 0.35s ease; }
#map { width:100%; height:100%; }
/* 自定义tooltip样式,更美观 */
.echarts-tooltip {
border-radius: 6px !important;
padding: 8px 12px !important;
font-size: 13px !important;
border: none !important;
box-shadow: 0 2px 8px rgba(0,0,0,0.3) !important;
}
html.login-map-light .echarts-tooltip {
box-shadow: 0 2px 12px rgba(30,60,120,0.15) !important;
}
html.login-map-embed,
html.login-map-embed body {
background: transparent !important;
}
</style>
</head>
<body>
<div id="map"></div>
<script>
(function() {
var MAP_MSG_TYPE = 'login-map-theme';
function getEmbedMode() {
if (typeof window !== 'undefined' && window.NANAFA_MAP_EMBED) return true;
try {
return new URLSearchParams(window.location.search).get('embed') === '1';
} catch (e) {
return false;
}
}
var EMBED = getEmbedMode();
if (EMBED) {
document.documentElement.classList.add('login-map-embed');
}
function getInitialTheme() {
if (EMBED) {
if (typeof window.NANAFA_MAP_THEME === 'string') {
return window.NANAFA_MAP_THEME === 'light' ? 'light' : 'dark';
}
try {
var embedParams = new URLSearchParams(window.location.search);
if (embedParams.get('theme') === 'light') return 'light';
if (embedParams.get('theme') === 'dark') return 'dark';
} catch (e) {
/* ignore */
}
return 'dark';
}
try {
var p = new URLSearchParams(window.location.search);
return p.get('theme') === 'light' ? 'light' : 'dark';
} catch (e) {
return 'dark';
}
}
var THEME = {
dark: {
pageBg: '#142637',
chartBg: '#142637',
areaHighlight: '#f9d506',
areaHighlightEm: '#fff380',
areaDim: '#142952',
areaDimEm: '#0f2149',
border: '#2b82f6',
borderEm: '#4096ff',
line: '#1e6ff7',
lineOpacity: 0.7,
effect: '#fff',
tooltipBg: 'rgba(10,14,23,0.9)',
tooltipText: '#fff',
tooltipBorder: '#409eff',
tooltipMuted: '#ffffcc',
tooltipAccent: '#409eff',
labelCountryBg: 'yellow',
labelAirportBg: '#ffffcc'
}
};
/**
* 日间地图风格(?lightStyle= / postMessage.lightStyle):
* minimal — 极简浅底低饱和 + 大洲分区色(默认)
* fresh / warm / pastel — 原业务高亮黄目的国方案
*/
var LIGHT_PALETTES = {
fresh: {
pageBg: '#e8f4fc',
chartBg: '#e8f4fc',
areaHighlight: '#ffd740',
areaHighlightEm: '#ffecb3',
areaDim: '#c5dff2',
areaDimEm: '#b0d4ee',
border: '#4a9fd6',
borderEm: '#1565c0',
line: '#8eb4ff',
lineOpacity: 0.58,
effect: '#fff',
tooltipBg: 'rgba(255,255,255,0.97)',
tooltipText: '#1a2d4a',
tooltipBorder: '#42a5f5',
tooltipMuted: '#546e7a',
tooltipAccent: '#0277bd',
labelCountryBg: '#fff9c4',
labelAirportBg: '#fffde7'
},
warm: {
pageBg: '#faf6f0',
chartBg: '#f8f3eb',
areaHighlight: '#ffca28',
areaHighlightEm: '#ffe082',
areaDim: '#ebe0d4',
areaDimEm: '#ddd1c2',
border: '#8d6e63',
borderEm: '#5d4037',
line: '#8eb4ff',
lineOpacity: 0.58,
effect: '#fff',
tooltipBg: 'rgba(255,255,255,0.97)',
tooltipText: '#3e2723',
tooltipBorder: '#8d6e63',
tooltipMuted: '#6d4c41',
tooltipAccent: '#0277bd',
labelCountryBg: '#ffecb3',
labelAirportBg: '#fff8e1'
},
pastel: {
pageBg: '#f3f5ff',
chartBg: '#f0f3ff',
areaHighlight: '#fff176',
areaHighlightEm: '#ffff8d',
areaDim: '#dde2f5',
areaDimEm: '#cfd6ef',
border: '#7e8ccf',
borderEm: '#5c6bc0',
line: '#8eb4ff',
lineOpacity: 0.58,
effect: '#fff',
tooltipBg: 'rgba(255,255,255,0.97)',
tooltipText: '#283593',
tooltipBorder: '#7986cb',
tooltipMuted: '#5c6bc0',
tooltipAccent: '#303f9f',
labelCountryBg: '#e8eaf6',
labelAirportBg: '#f3e5f5'
},
minimal: {
pageBg: '#f8fafc',
chartBg: 'transparent',
minimalDefault: '#e8e8e8',
minimalBorder: '#f8fafc',
minimalHover: '#a8b8c8',
colorAsia: '#d6b8b8',
colorEurope: '#b8c8d6',
colorAfrica: '#c1d6b8',
colorNorthAmerica: '#e2d6b8',
colorSouthAmerica: '#d4b8d6',
colorOceania: '#e0b8d6',
areaHighlight: '#ffd740',
areaHighlightEm: '#ffecb3',
areaDim: '#e8e8e8',
areaDimEm: '#dde3e8',
border: '#f8fafc',
borderEm: '#cfd8dc',
line: '#8eb4ff',
lineOpacity: 0.58,
effect: '#fff',
tooltipBg: 'rgba(255,255,255,0.9)',
tooltipText: '#333333',
tooltipBorder: '#e0e0e0',
tooltipMuted: '#666666',
tooltipAccent: '#455a64',
labelCountryBg: '#eceff1',
labelAirportBg: '#f5f5f5'
}
};
function getInitialLightStyle() {
if (EMBED) return 'minimal';
try {
var p = new URLSearchParams(window.location.search);
var s = (p.get('lightStyle') || 'minimal').toLowerCase();
if (s === 'warm' || s === 'pastel' || s === 'fresh' || s === 'minimal') return s;
return 'minimal';
} catch (e) {
return 'minimal';
}
}
function mapZoom() {
return EMBED ? 1.05 : 1.2;
}
function mapCenter() {
return EMBED ? [20, 24] : [50, 25];
}
function mapScaleLimit() {
return EMBED ? { min: 0.75, max: 3.5 } : undefined;
}
function getMapElement() {
var id = (typeof window !== 'undefined' && window.NANAFA_MAP_EL_ID) || 'map';
return document.getElementById(id);
}
var currentLightStyle = getInitialLightStyle();
function colorsFor(theme) {
if (theme === 'dark') return THEME.dark;
return LIGHT_PALETTES[currentLightStyle] || LIGHT_PALETTES.fresh;
}
var myChart = null;
// 航线:香港 HKG → 所有指定机场
var lines = [
{ name: 'HKG→LAX', coords: [[114.17,22.28], [-118.24,34.05]] },
{ name: 'HKG→JFK', coords: [[114.17,22.28], [-74.00,40.71]] },
{ name: 'HKG→ORD', coords: [[114.17,22.28], [-87.68,41.98]] },
{ name: 'HKG→DFW', coords: [[114.17,22.28], [-97.04,32.90]] },
{ name: 'HKG→MIA', coords: [[114.17,22.28], [-80.28,25.79]] },
{ name: 'HKG→MEM', coords: [[114.17,22.28], [-89.98,35.05]] },
{ name: 'HKG→ANC', coords: [[114.17,22.28], [-149.89,61.17]] },
{ name: 'HKG→YYZ', coords: [[114.17,22.28], [-79.38,43.65]] },
{ name: 'HKG→YVR', coords: [[114.17,22.28], [-123.12,49.28]] },
{ name: 'HKG→YUL', coords: [[114.17,22.28], [-73.75,45.47]] },
{ name: 'HKG→YYC', coords: [[114.17,22.28], [-114.07,51.10]] },
{ name: 'HKG→MEX', coords: [[114.17,22.28], [-99.13,19.43]] },
{ name: 'HKG→GDL', coords: [[114.17,22.28], [-103.41,20.67]] },
{ name: 'HKG→MTY', coords: [[114.17,22.28], [-100.39,25.67]] },
{ name: 'HKG→CUN', coords: [[114.17,22.28], [-86.83,21.08]] },
{ name: 'HKG→GRU', coords: [[114.17,22.28], [-46.63,-23.55]] },
{ name: 'HKG→VCP', coords: [[114.17,22.28], [-47.07,-22.91]] },
{ name: 'HKG→BSB', coords: [[114.17,22.28], [-47.86,-15.83]] },
{ name: 'HKG→FRA', coords: [[114.17,22.28], [8.68,50.11]] },
{ name: 'HKG→MUC', coords: [[114.17,22.28], [11.78,48.35]] },
{ name: 'HKG→CDG', coords: [[114.17,22.28], [2.55,49.01]] },
{ name: 'HKG→AMS', coords: [[114.17,22.28], [4.76,52.30]] },
{ name: 'HKG→MST', coords: [[114.17,22.28], [5.77,50.90]] },
{ name: 'HKG→LGG', coords: [[114.17,22.28], [5.68,50.64]] },
{ name: 'HKG→BRU', coords: [[114.17,22.28], [4.49,50.90]] },
{ name: 'HKG→MXP', coords: [[114.17,22.28], [8.72,45.63]] },
{ name: 'HKG→FCO', coords: [[114.17,22.28], [12.24,41.80]] },
{ name: 'HKG→MAD', coords: [[114.17,22.28], [-3.57,40.47]] },
{ name: 'HKG→OSL', coords: [[114.17,22.28], [11.08,60.20]] },
{ name: 'HKG→ZRH', coords: [[114.17,22.28], [8.55,47.45]] },
{ name: 'HKG→DUB', coords: [[114.17,22.28], [-6.27,53.42]] },
{ name: 'HKG→LHR', coords: [[114.17,22.28], [-0.12,51.51]] },
{ name: 'HKG→STN', coords: [[114.17,22.28], [0.28,51.89]] },
{ name: 'HKG→MAN', coords: [[114.17,22.28], [-2.28,53.35]] },
{ name: 'HKG→BHX', coords: [[114.17,22.28], [-1.86,52.45]] },
{ name: 'HKG→SYD', coords: [[114.17,22.28], [151.18,-33.94]] },
{ name: 'HKG→MEL', coords: [[114.17,22.28], [144.84,-37.67]] },
{ name: 'HKG→BNE', coords: [[114.17,22.28], [153.13,-27.39]] },
{ name: 'HKG→PER', coords: [[114.17,22.28], [115.96,-31.94]] },
{ name: 'HKG→AKL', coords: [[114.17,22.28], [174.79,-37.01]] }
];
var countryLangMap = {
'中国': { en: 'China', zh: '中国' },
'美国': { en: 'United States', zh: '美国' },
'加拿大': { en: 'Canada', zh: '加拿大' },
'墨西哥': { en: 'Mexico', zh: '墨西哥' },
'巴西': { en: 'Brazil', zh: '巴西' },
'德国': { en: 'Germany', zh: '德国' },
'法国': { en: 'France', zh: '法国' },
'荷兰': { en: 'Netherlands', zh: '荷兰' },
'比利时': { en: 'Belgium', zh: '比利时' },
'意大利': { en: 'Italy', zh: '意大利' },
'西班牙': { en: 'Spain', zh: '西班牙' },
'挪威': { en: 'Norway', zh: '挪威' },
'瑞士': { en: 'Switzerland', zh: '瑞士' },
'爱尔兰': { en: 'Ireland', zh: '爱尔兰' },
'英国': { en: 'United Kingdom', zh: '英国' },
'澳大利亚': { en: 'Australia', zh: '澳大利亚' },
'新西兰': { en: 'New Zealand', zh: '新西兰' }
};
var countries = [
{ name: '中国', value: [114.17,22.28], en: 'China' },
{ name: '美国', value: [-98.58,37.88], en: 'United States' },
{ name: '加拿大', value: [-95.94,51.13], en: 'Canada' },
{ name: '墨西哥', value: [-102.55,23.63], en: 'Mexico' },
{ name: '巴西', value: [-55.04,-14.24], en: 'Brazil' },
{ name: '德国', value: [10.45,51.17], en: 'Germany' },
{ name: '法国', value: [2.21,46.23], en: 'France' },
{ name: '荷兰', value: [5.29,52.13], en: 'Netherlands' },
{ name: '比利时', value: [4.42,50.85], en: 'Belgium' },
{ name: '意大利', value: [12.57,41.87], en: 'Italy' },
{ name: '西班牙', value: [-3.70,40.42], en: 'Spain' },
{ name: '挪威', value: [8.46,60.47], en: 'Norway' },
{ name: '瑞士', value: [8.23,46.82], en: 'Switzerland' },
{ name: '爱尔兰', value: [-7.69,53.14], en: 'Ireland' },
{ name: '英国', value: [-3.43,55.38], en: 'United Kingdom' },
{ name: '澳大利亚', value: [133.77,-24.78], en: 'Australia' },
{ name: '新西兰', value: [172.64,-41.40], en: 'New Zealand' }
];
var airportData = {
HKG: { name: '香港国际机场', code: 'HKG', country: { zh: '中国', en: 'China' } },
LAX: { name: '洛杉矶国际机场', code: 'LAX', country: { zh: '美国', en: 'United States' } },
JFK: { name: '纽约肯尼迪国际机场', code: 'JFK', country: { zh: '美国', en: 'United States' } },
ORD: { name: '芝加哥奥黑尔国际机场', code: 'ORD', country: { zh: '美国', en: 'United States' } },
DFW: { name: '达拉斯沃斯堡国际机场', code: 'DFW', country: { zh: '美国', en: 'United States' } },
MIA: { name: '迈阿密国际机场', code: 'MIA', country: { zh: '美国', en: 'United States' } },
MEM: { name: '孟菲斯国际机场', code: 'MEM', country: { zh: '美国', en: 'United States' } },
ANC: { name: '安克雷奇国际机场', code: 'ANC', country: { zh: '美国', en: 'United States' } },
YYZ: { name: '多伦多皮尔逊机场', code: 'YYZ', country: { zh: '加拿大', en: 'Canada' } },
YVR: { name: '温哥华国际机场', code: 'YVR', country: { zh: '加拿大', en: 'Canada' } },
YUL: { name: '蒙特利尔特鲁多机场', code: 'YUL', country: { zh: '加拿大', en: 'Canada' } },
YYC: { name: '卡尔加里机场', code: 'YYC', country: { zh: '加拿大', en: 'Canada' } },
MEX: { name: '墨西哥城机场', code: 'MEX', country: { zh: '墨西哥', en: 'Mexico' } },
GDL: { name: '瓜达拉哈拉机场', code: 'GDL', country: { zh: '墨西哥', en: 'Mexico' } },
MTY: { name: '蒙特雷机场', code: 'MTY', country: { zh: '墨西哥', en: 'Mexico' } },
CUN: { name: '坎昆机场', code: 'CUN', country: { zh: '墨西哥', en: 'Mexico' } },
GRU: { name: '圣保罗瓜鲁柳斯机场', code: 'GRU', country: { zh: '巴西', en: 'Brazil' } },
VCP: { name: '坎皮纳斯机场', code: 'VCP', country: { zh: '巴西', en: 'Brazil' } },
BSB: { name: '巴西利亚机场', code: 'BSB', country: { zh: '巴西', en: 'Brazil' } },
FRA: { name: '法兰克福国际机场', code: 'FRA', country: { zh: '德国', en: 'Germany' } },
MUC: { name: '慕尼黑国际机场', code: 'MUC', country: { zh: '德国', en: 'Germany' } },
CDG: { name: '巴黎戴高乐国际机场', code: 'CDG', country: { zh: '法国', en: 'France' } },
AMS: { name: '阿姆斯特丹史基浦机场', code: 'AMS', country: { zh: '荷兰', en: 'Netherlands' } },
MST: { name: '马斯特里赫特机场', code: 'MST', country: { zh: '荷兰', en: 'Netherlands' } },
LGG: { name: '列日机场', code: 'LGG', country: { zh: '比利时', en: 'Belgium' } },
BRU: { name: '布鲁塞尔机场', code: 'BRU', country: { zh: '比利时', en: 'Belgium' } },
MXP: { name: '米兰马尔彭萨机场', code: 'MXP', country: { zh: '意大利', en: 'Italy' } },
FCO: { name: '罗马菲乌米奇诺机场', code: 'FCO', country: { zh: '意大利', en: 'Italy' } },
MAD: { name: '马德里机场', code: 'MAD', country: { zh: '西班牙', en: 'Spain' } },
OSL: { name: '奥斯陆加勒穆恩机场', code: 'OSL', country: { zh: '挪威', en: 'Norway' } },
ZRH: { name: '苏黎世机场', code: 'ZRH', country: { zh: '瑞士', en: 'Switzerland' } },
DUB: { name: '都柏林机场', code: 'DUB', country: { zh: '爱尔兰', en: 'Ireland' } },
LHR: { name: '伦敦希思罗机场', code: 'LHR', country: { zh: '英国', en: 'United Kingdom' } },
STN: { name: '伦敦斯坦斯特德机场', code: 'STN', country: { zh: '英国', en: 'United Kingdom' } },
MAN: { name: '曼彻斯特机场', code: 'MAN', country: { zh: '英国', en: 'United Kingdom' } },
BHX: { name: '伯明翰机场', code: 'BHX', country: { zh: '英国', en: 'United Kingdom' } },
SYD: { name: '悉尼机场', code: 'SYD', country: { zh: '澳大利亚', en: 'Australia' } },
MEL: { name: '墨尔本机场', code: 'MEL', country: { zh: '澳大利亚', en: 'Australia' } },
BNE: { name: '布里斯班机场', code: 'BNE', country: { zh: '澳大利亚', en: 'Australia' } },
PER: { name: '珀斯机场', code: 'PER', country: { zh: '澳大利亚', en: 'Australia' } },
AKL: { name: '奥克兰机场', code: 'AKL', country: { zh: '新西兰', en: 'New Zealand' } }
};
var airports = Object.keys(airportData).map(function(code) {
return {
name: code,
value: [
code === 'HKG' ? 114.17 : code === 'LAX' ? -118.24 : code === 'JFK' ? -74.00 : code === 'ORD' ? -87.68 : code === 'DFW' ? -97.04 : code === 'MIA' ? -80.28 : code === 'MEM' ? -89.98 : code === 'ANC' ? -149.89 : code === 'YYZ' ? -79.38 : code === 'YVR' ? -123.12 : code === 'YUL' ? -73.75 : code === 'YYC' ? -114.07 : code === 'MEX' ? -99.13 : code === 'GDL' ? -103.41 : code === 'MTY' ? -100.39 : code === 'CUN' ? -86.83 : code === 'GRU' ? -46.63 : code === 'VCP' ? -47.07 : code === 'BSB' ? -47.86 : code === 'FRA' ? 8.68 : code === 'MUC' ? 11.78 : code === 'CDG' ? 2.55 : code === 'AMS' ? 4.76 : code === 'MST' ? 5.77 : code === 'LGG' ? 5.68 : code === 'BRU' ? 4.49 : code === 'MXP' ? 8.72 : code === 'FCO' ? 12.24 : code === 'MAD' ? -3.57 : code === 'OSL' ? 11.08 : code === 'ZRH' ? 8.55 : code === 'DUB' ? -6.27 : code === 'LHR' ? -0.12 : code === 'STN' ? 0.28 : code === 'MAN' ? -2.28 : code === 'BHX' ? -1.86 : code === 'SYD' ? 151.18 : code === 'MEL' ? 144.84 : code === 'BNE' ? 153.13 : code === 'PER' ? 115.96 : code === 'AKL' ? 174.79 : 0,
code === 'HKG' ? 22.28 : code === 'LAX' ? 34.05 : code === 'JFK' ? 40.71 : code === 'ORD' ? 41.98 : code === 'DFW' ? 32.90 : code === 'MIA' ? 25.79 : code === 'MEM' ? 35.05 : code === 'ANC' ? 61.17 : code === 'YYZ' ? 43.65 : code === 'YVR' ? 49.28 : code === 'YUL' ? 45.47 : code === 'YYC' ? 51.10 : code === 'MEX' ? 19.43 : code === 'GDL' ? 20.67 : code === 'MTY' ? 25.67 : code === 'CUN' ? 21.08 : code === 'GRU' ? -23.55 : code === 'VCP' ? -22.91 : code === 'BSB' ? -15.83 : code === 'FRA' ? 50.11 : code === 'MUC' ? 48.35 : code === 'CDG' ? 49.01 : code === 'AMS' ? 52.30 : code === 'MST' ? 50.90 : code === 'LGG' ? 50.64 : code === 'BRU' ? 50.90 : code === 'MXP' ? 45.63 : code === 'FCO' ? 41.80 : code === 'MAD' ? 40.47 : code === 'OSL' ? 60.20 : code === 'ZRH' ? 47.45 : code === 'DUB' ? 53.42 : code === 'LHR' ? 51.51 : code === 'STN' ? 51.89 : code === 'MAN' ? 53.35 : code === 'BHX' ? 52.45 : code === 'SYD' ? -33.94 : code === 'MEL' ? -37.67 : code === 'BNE' ? -27.39 : code === 'PER' ? -31.94 : code === 'AKL' ? -37.01 : 0
],
airportName: airportData[code].name,
country: airportData[code].country
};
});
var highlightCountries = [
'美国', '加拿大', '墨西哥', '巴西', '德国', '法国', '荷兰',
'比利时', '意大利', '西班牙', '挪威', '瑞士', '爱尔兰',
'英国', '澳大利亚', '新西兰'
];
/** world.json 的 properties.name 为英文,必须与中文并列判断,否则日间会像「全图一块深色」 */
var highlightCountriesEn = [
'United States', 'Canada', 'Mexico', 'Brazil', 'Germany', 'France', 'Netherlands',
'Belgium', 'Italy', 'Spain', 'Norway', 'Switzerland', 'Ireland',
'United Kingdom', 'Australia', 'New Zealand'
];
function isDestinationGeo(name) {
return highlightCountries.indexOf(name) !== -1 || highlightCountriesEn.indexOf(name) !== -1;
}
/**
* 按国家单独覆盖版图填充色(可改「每一个国家」)。
* - 键必须与 GeoJSON 的 properties.name 完全一致,一般为英文(如 China、Russia、India)。
* - 查名称:在 public/assets/geo/world.json 里搜索,或日间/夜间下将鼠标移到国家上看 tooltip 标题旁的英文名。
* - dark / light 可分别配置;未写的国家仍走默认:目的国 → areaHighlight,其它 → areaDim。
*/
var COUNTRY_AREA_COLORS = {
dark: {
// China: '#1e3a5f',
// Russia: '#152238',
},
light: {
// China: '#d8f0e8',
// Russia: '#e8edf5',
// India: '#fff8e1',
}
};
function getCountryAreaOverride(theme, geoName) {
var bucket = theme === 'light' ? COUNTRY_AREA_COLORS.light : COUNTRY_AREA_COLORS.dark;
if (!bucket || geoName == null || geoName === '') return null;
var c = bucket[geoName];
return c != null && c !== '' ? c : null;
}
/** emphasis:有自定义色则沿用同色(避免悬浮时乱跳);否则用主题里的高亮/底色强调色 */
function resolveGeoAreaColor(theme, geoName, T, emphasis) {
var custom = getCountryAreaOverride(theme, geoName);
if (custom != null) return custom;
if (isDestinationGeo(geoName)) return emphasis ? T.areaHighlightEm : T.areaHighlight;
return emphasis ? T.areaDimEm : T.areaDim;
}
function buildMinimalGeoRegions(T) {
if (typeof MINIMAL_BY_CONTINENT === 'undefined') return [];
var key = {
asia: T.colorAsia,
europe: T.colorEurope,
africa: T.colorAfrica,
na: T.colorNorthAmerica,
sa: T.colorSouthAmerica,
oceania: T.colorOceania
};
var fix = typeof MINIMAL_GEO_NAME_FIX !== 'undefined' ? MINIMAL_GEO_NAME_FIX : {};
var out = [];
Object.keys(MINIMAL_BY_CONTINENT).forEach(function(cont) {
var col = key[cont];
if (!col) return;
MINIMAL_BY_CONTINENT[cont].forEach(function(raw) {
var name = fix[raw] || raw;
out.push({ name: name, itemStyle: { areaColor: col } });
});
});
return out;
}
function buildTooltipConfig(T) {
return {
trigger: 'item',
triggerOn: 'mousemove',
formatter: function(params) {
var tc = T.tooltipText;
var tm = T.tooltipMuted;
var ta = T.tooltipAccent;
if (params.seriesType === 'scatter') {
if (airportData[params.name]) {
var airport = airportData[params.name];
return '<div style="text-align:center;">' +
'<strong style="font-size:14px;color:' + tc + ';">' + airport.name + '</strong><br>' +
'<span style="color:' + tm + ';">三字码:' + params.name + '</span><br>' +
'<span style="color:' + ta + ';">所属国家:' + airport.country.en + ' / ' + airport.country.zh + '</span>' +
'</div>';
}
var country = countryLangMap[params.name] || { en: params.name, zh: params.name };
return '<div style="text-align:center;color:' + tc + ';"><strong>' + country.en + ' / ' + country.zh + '</strong></div>';
}
if (params.componentType === 'geo') {
var c2 = countryLangMap[params.name] || { en: params.name, zh: params.name };
return '<div style="text-align:center;color:' + tc + ';"><strong>' + c2.en + ' / ' + c2.zh + '</strong></div>';
}
if (params.seriesType === 'lines') {
var parts = params.name.split('→');
var fromCode = parts[0];
var toCode = parts[1];
var fromAirport = airportData[fromCode];
var toAirport = airportData[toCode];
return '<div style="text-align:center;color:' + tc + ';">' +
'<strong>货运航线</strong><br>' +
fromAirport.name + ' → ' + toAirport.name + '<br>' +
'<span style="color:' + tm + ';">' + fromCode + ' → ' + toCode + '</span><br>' +
'<span style="color:' + ta + ';">' + fromAirport.country.en + ' → ' + toAirport.country.en + '</span>' +
'</div>';
}
return params.name || '';
},
textStyle: { fontSize: 12, color: T.tooltipText },
backgroundColor: T.tooltipBg,
borderColor: T.tooltipBorder,
borderWidth: 1
};
}
function buildFlightSeries(T) {
var countryLabelSize = EMBED ? 11 : 13;
var airportLabelSize = EMBED ? 9 : 10;
var effectSize = EMBED ? 4 : 6;
var effectPeriod = EMBED ? 10 : 8;
return [
{
type: 'lines',
coordinateSystem: 'geo',
zlevel: 2,
data: lines,
lineStyle: {
color: T.line,
width: EMBED ? 0.6 : 0.8,
curveness: 0.2,
opacity: T.lineOpacity
},
effect: {
show: true,
symbol: 'pin',
symbolSize: effectSize,
color: T.effect,
trailLength: 0.6,
period: effectPeriod
}
},
{
type: 'scatter',
coordinateSystem: 'geo',
zlevel: 4,
data: countries,
symbolSize: 0,
label: {
show: true,
formatter: '{b}',
color: '#000',
fontSize: countryLabelSize,
fontWeight: 'bold',
backgroundColor: T.labelCountryBg,
padding: EMBED ? [2, 4] : [3, 6],
borderRadius: 3
}
},
{
type: 'scatter',
coordinateSystem: 'geo',
zlevel: 3,
data: airports,
symbolSize: 0,
label: {
show: true,
formatter: '{b}',
color: '#000',
fontSize: airportLabelSize,
fontWeight: 'bold',
backgroundColor: T.labelAirportBg,
padding: [1, 4],
borderRadius: 2
}
}
];
}
function applyPageChrome(theme) {
if (EMBED) {
var mapEl = getMapElement();
if (mapEl) {
var embedColors = colorsFor(theme);
mapEl.style.background = theme === 'light' ? 'transparent' : embedColors.chartBg;
}
document.documentElement.style.background = '';
document.body.style.background = '';
document.documentElement.classList.toggle('login-map-light', theme === 'light');
return;
}
var T = colorsFor(theme);
document.documentElement.style.background = T.pageBg;
document.body.style.background = T.pageBg;
document.documentElement.classList.toggle('login-map-light', theme === 'light');
}
function buildOption(theme) {
var T = colorsFor(theme);
if (theme === 'light' && currentLightStyle === 'minimal') {
return {
backgroundColor: T.chartBg,
tooltip: buildTooltipConfig(T),
geo: {
map: 'world',
center: mapCenter(),
zoom: mapZoom(),
roam: true,
scaleLimit: mapScaleLimit(),
itemStyle: {
areaColor: T.minimalDefault,
borderColor: T.minimalBorder,
borderWidth: 0.8,
shadowBlur: 0
},
emphasis: {
itemStyle: {
areaColor: T.minimalHover,
shadowBlur: 2,
shadowColor: 'rgba(0,0,0,0.1)'
},
label: { show: false }
},
regions: buildMinimalGeoRegions(T)
},
series: buildFlightSeries(T)
};
}
return {
backgroundColor: T.chartBg,
tooltip: buildTooltipConfig(T),
geo: {
map: 'world',
center: mapCenter(),
zoom: mapZoom(),
roam: true,
scaleLimit: mapScaleLimit(),
itemStyle: {
areaColor: function(params) {
return resolveGeoAreaColor(theme, params.name, T, false);
},
borderColor: T.border,
borderWidth: 1
},
emphasis: {
itemStyle: {
areaColor: function(params) {
return resolveGeoAreaColor(theme, params.name, T, true);
},
borderColor: T.borderEm,
borderWidth: 1.5
},
label: {
show: true,
color: '#000',
fontSize: 10,
fontWeight: 'bold'
}
},
label: { show: false }
},
series: buildFlightSeries(T)
};
}
function setMapTheme(theme, lightStyleOpt) {
var t = theme === 'light' ? 'light' : 'dark';
if (t === 'light' && lightStyleOpt && LIGHT_PALETTES[lightStyleOpt]) {
currentLightStyle = lightStyleOpt;
}
applyPageChrome(t);
if (myChart) {
myChart.setOption(buildOption(t), { notMerge: true });
}
}
function runMap(worldJson) {
var mapEl = getMapElement();
if (!mapEl) return;
if (!worldJson) {
showMapLoadError(mapEl);
return;
}
echarts.registerMap('world', worldJson);
myChart = echarts.init(mapEl);
setMapTheme(getInitialTheme());
scheduleEmbedResize();
window.addEventListener('resize', function() {
if (myChart) myChart.resize();
});
if (!EMBED) {
window.addEventListener('message', function(e) {
if (e.origin !== window.location.origin) return;
if (!e.data || e.data.type !== MAP_MSG_TYPE) return;
var ls = e.data.lightStyle;
if (ls !== 'warm' && ls !== 'pastel' && ls !== 'fresh' && ls !== 'minimal') ls = undefined;
setMapTheme(e.data.theme === 'light' ? 'light' : 'dark', ls);
});
}
}
function showMapLoadError(mapEl) {
if (!mapEl || !EMBED) return;
mapEl.innerHTML =
'<div style="display:flex;align-items:center;justify-content:center;height:100%;min-height:200px;color:#64748b;font-size:14px;padding:16px;text-align:center;line-height:1.6;">' +
'Global route map is loading…<br><span style="font-size:12px;opacity:.85;">If this persists, open the site via HTTP (not file://).</span></div>';
}
function scheduleEmbedResize() {
if (!EMBED || !myChart) return;
var resize = function() {
if (myChart) myChart.resize();
};
resize();
[100, 400, 1000, 2000].forEach(function(ms) {
setTimeout(resize, ms);
});
if (typeof ResizeObserver !== 'undefined') {
var mapEl = getMapElement();
if (mapEl) new ResizeObserver(resize).observe(mapEl);
}
}
function loadWorldGeo(callback) {
if (typeof window.__NANFA_WORLD_GEO__ !== 'undefined') {
callback(window.__NANFA_WORLD_GEO__);
return;
}
var geoUrl = EMBED ? './geo/world.json' : '/assets/geo/world.json';
fetch(geoUrl)
.then(function(r) {
if (!r.ok) throw new Error('geo fetch failed');
return r.json();
})
.then(callback)
.catch(function() {
callback(null);
});
}
loadWorldGeo(runMap);
})();
</script>
</body>
</html>