- 新增小红书前端模块(启动服务、内容分析、爆款创作、发布记录、技能卡片、拟人设置、AI 助手) - 新增 xhs-agent-desktop 独立桌面 Agent,对接 OMS 任务调度 - 登录页重构,整合微信登录与 OMS JWT 认证 - 订单、渠道、用户中心等页面与 API 增强 Co-authored-by: Cursor <cursoragent@cursor.com>
687 lines
28 KiB
JavaScript
687 lines
28 KiB
JavaScript
window.NANAFA_MAP_EMBED = true;
|
||
window.NANAFA_MAP_EL_ID = 'services-route-map';
|
||
window.NANAFA_MAP_THEME = 'light';
|
||
(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);
|
||
})(); |