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 '