chore: add reference docs, scripts, tests, demo, prototypes
- Reference docs: known issues, user journey, location redesign, mission merge plan - Scripts: fill_location_state, merge_missions, migrate_location, migrate_state - Tests: e2e test suite, asset entries unit tests - Demo: location board, hifi prototypes, test screenshots - Data backup: asset log entries and state nodes - Update .gitignore Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@@ -7,3 +7,4 @@ instance/
|
|||||||
.idea/
|
.idea/
|
||||||
.pytest_cache/
|
.pytest_cache/
|
||||||
.mypy_cache/
|
.mypy_cache/
|
||||||
|
user_input/
|
||||||
|
|||||||
@@ -0,0 +1,232 @@
|
|||||||
|
asset|id|kind|title|mission|start|end|state|location|summary|note
|
||||||
|
Europa Outpost|bf68c161-7303-44da-bf6e-72373d2e54d5|state|EOCM-1 Outpost Deployment|Europa Outpost Construction Mission 1|2057-02-27 08:00:00+08:00|2057-03-15 09:00:00+08:00|Outpost Construction|Europa Outpost|The first crewed Europa construction campaign assembled the initial habitat, power spine, and life-support core.|Backfilled from ST-01 EOCM-1 support logs.
|
||||||
|
Europa Outpost|53ee9bf9-27c3-47c2-94f3-656a8eea64f2|event|Enter into Service|Europa Outpost Construction Mission 1|2057-03-15 09:00:00+08:00|||Europa Outpost|Europa Outpost entered operational service after the initial construction stack passed habitability review.|User-provided milestone, consistent with ST-01 EOCM-1 operation window.
|
||||||
|
Europa Outpost|6ffeda07-93fb-4d17-9efc-817555771d35|state|EOCM-1 Initial Outpost Operations|Europa Outpost Construction Mission 1|2057-03-15 09:00:00+08:00|2057-10-01 08:00:00+08:00|Outpost Commissioning|Europa Outpost|The outpost remained crewed while the first construction team brought core systems to sustained operational readiness.|Backfilled from ST-01 EOCM-1 support logs.
|
||||||
|
Europa Outpost|d548ded1-232d-4e68-875b-290092c7c0b8|state|Autonomous Standby|Europa Outpost Construction Mission 1|2057-10-01 08:00:00+08:00|2058-01-19 08:00:00+08:00|Autonomous Standby|Europa Outpost|After the first construction crew departed, Europa Outpost stayed in autonomous survival mode while awaiting the second construction mission.|Bridging interval inferred from EOCM-1 return and EOCM-2 arrival windows.
|
||||||
|
Europa Outpost|83dcded1-c57c-49c8-b94b-5430c2fe4d4d|state|EOCM-2 Outpost Expansion|Europa Outpost Construction Mission 2|2058-01-19 08:00:00+08:00|2058-09-06 08:00:00+08:00|Outpost Construction|Europa Outpost|The second construction campaign expanded the habitat ring, utility corridors, and long-duration expedition support spaces.|Backfilled from ST-02 EOCM-2 support logs.
|
||||||
|
Europa Outpost|50c4b9a3-644d-4dd6-85c7-cd40f1d32667|state|Post-Construction Readiness|Europa Outpost Construction Mission 2|2058-09-06 08:00:00+08:00|2058-09-25 08:00:00+08:00|Autonomous Standby|Europa Outpost|Europa Outpost remained uncrewed briefly after Construction Mission 2 while final expedition supplies were staged.|Bridging interval inferred from ST-02 EOCM-2 departure and XH-01 EE-1 arrival.
|
||||||
|
Europa Outpost|188eb0ca-df35-4644-92d3-6952bae3dbe8|event|Europa Expedition 1 Arrived|Europa Expedition 1|2058-09-25 10:00:00+08:00|||Europa Outpost|Europa Expedition 1 docked at the outpost and began the first standard long-duration occupation cycle.|Backfilled from XH-01 EE-1 support logs.
|
||||||
|
Europa Outpost|f034826a-77fc-4070-84a6-0f419d0910bb|state|EE-1 Surface Operations|Europa Expedition 1|2058-09-25 10:00:00+08:00|2059-06-25 08:30:00+08:00|Surface Expedition|Europa Outpost|Europa Expedition 1 occupied the outpost and supported early Jupiter system science operations based from Europa.|Backfilled from XH-01 EE-1 Jupiter System Operations.
|
||||||
|
Europa Outpost|a5da1a52-0c59-423a-b41e-2e646783bb67|event|Europa Expedition 2 Arrived|Europa Expedition 2|2059-06-25 08:30:00+08:00|||Europa Outpost|Europa Expedition 2 arrived via ST-02 Kristen with 12 crew and 1 Echo Shuttle Block 1.|User-provided crew manifest and arrival vehicle; consistent with ST-02 EE-2 arrival window.
|
||||||
|
Europa Outpost|f17eb842-8870-431d-9280-1b5fde97b48b|state|EE-1/EE-2 Crew Handoff|Europa Expedition 1; Europa Expedition 2|2059-06-25 08:30:00+08:00|2059-06-25 16:00:00+08:00|Crew Handoff; Surface Expedition|Europa Outpost|Europa Expedition 2 overlapped with Expedition 1 during crew transfer, cargo sorting, and outpost handover.|Outpost-level handoff interval inferred from same-day EE-2 arrival and EE-1 departure notes.
|
||||||
|
Europa Outpost|a51cd5a1-3ef1-430a-87f7-71ce9db44714|state|EE-2 Surface Operations|Europa Expedition 2|2059-06-25 16:00:00+08:00|2060-05-03 09:00:00+08:00|Surface Expedition|Europa Outpost|Europa Expedition 2 assumed continuous occupation of the outpost after the first crew rotation completed.|Backfilled from ST-02 EE-2 support logs.
|
||||||
|
Europa Outpost|906f6248-fd84-4211-a0eb-74c39e59ce86|event|Europa Expedition 1 Departed|Europa Expedition 1|2059-06-25 16:00:00+08:00|||Europa Outpost|Europa Expedition 1 departed the outpost via Xihe Station with 9 crew and 1 Callisto MPV Block 1.|User-provided departure note; treated as outpost departure milestone within XH-01 EE-1 broader return window.
|
||||||
|
Europa Outpost|15f8aae6-45f8-43d9-a3d6-9a2585ddeed2|event|Expansion Module Arrived|Europa Outpost Expansion Mission; Europa Expedition 2|2060-05-03 11:00:00+08:00|||Europa Outpost|The Europa Outpost expansion module arrived while Expedition 2 remained on station.|User-provided date; consistent with Solar Shuttle 2 expansion delivery window at Europa.
|
||||||
|
Europa Outpost|82509df3-ad30-483f-a0f7-b05ff4497435|state|EE-2 Operations with Expansion Module Integration|Europa Expedition 2; Europa Outpost Expansion Mission|2060-05-03 11:00:00+08:00|2060-06-23 09:00:00+08:00|Surface Expedition; Outpost Expansion|Europa Outpost|Europa Expedition 2 supported installation and activation of the new expansion module after it reached the outpost.|Backfilled from Solar Shuttle 2 expansion delivery and ST-02 EE-2 operation windows.
|
||||||
|
Europa Outpost|db70ac3b-1035-4066-8e74-928e656bc39a|state|EE-2/EE-3 Crew Handoff|Europa Expedition 2; Europa Expedition 3|2060-06-23 09:00:00+08:00|2060-06-24 15:00:00+08:00|Crew Handoff; Surface Expedition|Europa Outpost|Europa Expedition 3 overlapped with Expedition 2 during crew turnover and transfer of outpost responsibility.|Outpost-level handoff interval inferred from user-provided EE-3 arrival and EE-2 departure dates.
|
||||||
|
Europa Outpost|d7a1d501-ed5f-4065-a773-aa4ef101dede|event|Europa Expedition 3 Arrived|Europa Expedition 3|2060-06-23 09:00:00+08:00|||Europa Outpost|Europa Expedition 3 arrived via XH-01 to begin the next outpost rotation.|User-provided arrival date; treated as outpost arrival milestone within XH-01 EE-3 Jupiter System Operations.
|
||||||
|
Europa Outpost|8649cb17-0bcb-4ba4-9f91-e9c441ff9793|event|Europa Expedition 2 Departed|Europa Expedition 2|2060-06-24 15:00:00+08:00|||Europa Outpost|Europa Expedition 2 departed the outpost via ST-02 Kristen after completing crew handoff to Expedition 3.|User-provided departure date; treated as outpost departure milestone slightly ahead of ST-02 EE-2 system departure.
|
||||||
|
Europa Outpost|a7c9d010-5aed-43fc-a744-1a86ad9b15c8|state|EE-3 Surface Operations|Europa Expedition 3|2060-06-24 15:00:00+08:00||Surface Expedition|Europa Outpost|Europa Expedition 3 is the current resident crew. Crew status on 2060-06-24: 6 scientists at Lab, 2 medical at center hub, 1 commander and 1 pilot at observation station, 2 engineers at greenhouse.|User-provided current crew distribution for 2060-06-24.
|
||||||
|
JW-01 金乌一号|5df95fe3-5f0d-4dc1-ad37-508adcf2eef2|state|Construction||2058-03-14 16:00:00+08:00|2059-03-08 08:00:00+08:00|Construction|Star Port|Solar Shuttle 1 Construction|Timeline.xlsx inferred monthly phase. Raw: Construction
|
||||||
|
JW-01 金乌一号|e306c665-07c0-46dc-8127-b96775d616b9|state|Assembly Complete||2059-03-08 08:00:00+08:00|2059-04-13 08:00:00+08:00|Assembly Complete|LEO|Assembly complete, preparing for commissioning|Timeline.xlsx inferred monthly phase. Raw: Assembly Complete
|
||||||
|
JW-01 金乌一号|543dabdc-e61b-4a1c-8c4b-3e753d17cbbd|state|Trial Run||2059-04-13 08:00:00+08:00|2059-08-06 08:00:00+08:00|Trial Run|Earth|Trial Run before commissioning|Timeline.xlsx inferred monthly phase. Raw: Trial Run
|
||||||
|
JW-01 金乌一号|b99062ea-4547-4cdc-8bf1-3b1f0c28a4be|state|Mars One Expansion Outbound|Mars One Expansion|2059-08-06 08:00:00+08:00|2059-10-24 08:00:00+08:00|Transfer|Sun|Earth-Mars transfer|Timeline.xlsx inferred monthly phase. Raw: Earth-Mars
|
||||||
|
JW-01 金乌一号|30930d1b-d32b-4ef6-97ae-6d11c777ad53|state|Mars One Expansion|Mars One Expansion|2059-10-19 08:00:00+08:00|2060-08-09 05:10:00+08:00|Base Expansion|Mars|Mars One Expansion|Timeline.xlsx inferred monthly phase. Raw: Mars One Expansion
|
||||||
|
JW-01 金乌一号|00c8b5c6-e363-4aee-a768-2fc3fa7a0138|state|Mars Return|Mars One Expansion|2060-08-09 05:10:00+08:00|2060-10-26 10:03:00+08:00|Transfer|Sun|Back to Earth with Qingtian|Carrying Mars One component back
|
||||||
|
Mars One Base|97b3a860-03c6-4ca3-a9c8-f94fd05adbe0|event|Initial Cargo Landed|Mars Mission Demo|2054-08-23 10:30:00+08:00|||Mars One Base|Cargo from Mars Mission Demo was landed at the future Mars One site for robotic setup.|Backfilled from XH-01 Mars Mission Demo support logs.
|
||||||
|
Mars One Base|dc082672-52a2-4142-a2b8-737991ee542f|state|MMD Surface Activation|Mars Mission Demo|2054-08-23 12:00:00+08:00|2055-07-30 08:00:00+08:00|Surface Activation|Mars One Base|Robotic systems activated the first surface power, comms, and cargo handling stack delivered by Mars Mission Demo.|Backfilled from XH-01 Mars Mission Demo support logs.
|
||||||
|
Mars One Base|3141e93d-1e47-4264-864b-378815d8e871|event|First Crewed Landing|Mars One Base Construction Mission 1|2055-07-30 09:30:00+08:00|||Mars One Base|The first crewed Mars One construction team landed and began surface assembly.|Backfilled from XH-01 MOCM-1 support logs.
|
||||||
|
Mars One Base|9399f5f6-9412-455f-8683-b237bcdeab7b|state|MOCM-1 Initial Construction|Mars One Base Construction Mission 1|2055-07-30 10:00:00+08:00|2056-02-07 08:00:00+08:00|Base Construction|Mars One Base|The first crewed construction campaign assembled the core habitat, power systems, and ascent support infrastructure.|Backfilled from XH-01 MOCM-1 support logs.
|
||||||
|
Mars One Base|afe0f5fe-6909-4351-8003-b3f78e9145d1|state|MOCM-1 Autonomous Commissioning|Mars One Base Construction Mission 1|2056-02-07 08:00:00+08:00|2056-07-03 08:00:00+08:00|Autonomous Standby|Mars One Base|After the first construction crew departed, the outpost remained in autonomous checkout ahead of the next build window.|Bridging interval inferred from the first and second Mars One construction campaigns.
|
||||||
|
Mars One Base|0fd356d5-71ce-4219-a7fd-b9ff077f4f39|state|MOCM-2 Base Construction|Mars One Construction Mission 2|2056-07-03 08:00:00+08:00|2056-09-15 08:00:00+08:00|Base Construction|Mars One Base|Mission 2 expanded the surface habitat and continued utility trenching and storage build-out.|Backfilled from ST-01 MOCM-2 support logs.
|
||||||
|
Mars One Base|d96bd772-45eb-4010-96c2-1c942f4efc96|state|MOCM-2/MOCM-3 Crew Handoff|Mars One Construction Mission 2; Mars One Base Construction Mission 3|2056-09-15 08:00:00+08:00|2056-10-12 08:00:00+08:00|Base Construction; Crew Handoff|Mars One Base|Construction Mission 3 arrived before Mission 2 departed, enabling the first on-surface Mars One crew handoff.|Backfilled from ST-01 MOCM-2 and XH-01 MOCM-3 support logs.
|
||||||
|
Mars One Base|a05e86bb-77e5-4b99-a7d2-16a1dc810150|state|MOCM-3 Base Construction|Mars One Base Construction Mission 3|2056-10-12 08:00:00+08:00|2056-11-19 08:00:00+08:00|Base Construction|Mars One Base|The third construction campaign completed surface integration and prepared the outpost for continuous expedition occupancy.|Backfilled from XH-01 MOCM-3 support logs.
|
||||||
|
Mars One Base|2b0a2dbf-3f28-449e-af86-75ebf87b408e|state|MOCM-3 and ME-1 Surface Operations|Mars One Base Construction Mission 3; Mars Expedition 1|2056-11-19 08:00:00+08:00|2057-02-12 08:00:00+08:00|Base Construction; Surface Expedition|Mars One Base|Final construction work continued while Mars Expedition 1 began long-duration surface operations from the outpost.|Overlap inferred from XH-01 MOCM-3 and ST-02 ME-1 support logs.
|
||||||
|
Mars One Base|edfbaea5-331b-4a93-9458-044d3016eb7f|event|Phase 1 Construction Complete|Mars One Base Construction Mission 3; Mars Expedition 1|2057-02-12 09:00:00+08:00|||Mars One Base|Mars One entered sustained expedition service after the third construction wave wrapped up.|Completion milestone inferred from XH-01 MOCM-3 return window.
|
||||||
|
Mars One Base|e8003196-d035-49be-a721-144fec91a678|state|ME-1 Surface Operations|Mars Expedition 1|2057-02-12 09:00:00+08:00|2057-06-22 08:00:00+08:00|Surface Expedition|Mars One Base|Mars Expedition 1 operated the newly completed outpost and stabilized its life-support and logistics cadence.|Backfilled from ST-02 ME-1 support logs.
|
||||||
|
Mars One Base|fb511ee5-3343-4884-a094-59241d1d28e0|state|ME-1/ME-2 Crew Handoff|Mars Expedition 1; Mars Expedition 2|2057-06-22 08:00:00+08:00|2057-09-08 08:00:00+08:00|Surface Expedition; Crew Handoff|Mars One Base|Mars Expedition 2 arrived while Expedition 1 completed return preparations, overlapping on the surface during handoff.|Overlap inferred from ST-02 ME-1 and XH-01 ME-2 support logs.
|
||||||
|
Mars One Base|5109f226-b843-4173-97cf-bb3b740927ed|state|ME-2 Surface Operations|Mars Expedition 2|2057-09-08 08:00:00+08:00|2058-04-17 08:00:00+08:00|Surface Expedition|Mars One Base|Mars Expedition 2 took over continuous surface operations after the Expedition 1 crew departed.|Backfilled from XH-01 ME-2 support logs.
|
||||||
|
Mars One Base|9c4ef55d-a472-4e19-b582-8f872e6c854a|state|ME-2/ME-3 Crew Handoff|Mars Expedition 2; Mars Expedition 3|2058-04-17 08:00:00+08:00|2058-04-18 08:00:00+08:00|Surface Expedition; Crew Handoff|Mars One Base|Mars Expedition 3 overlapped briefly with Expedition 2 during the next surface crew handoff.|Overlap inferred from XH-01 ME-2 return and XH-02 ME-3 arrival timing.
|
||||||
|
Mars One Base|66d8bc17-70e4-4c18-9c00-a8a2ffb4c1d3|state|ME-3 Surface Operations|Mars Expedition 3|2058-04-18 08:00:00+08:00|2059-10-19 08:00:00+08:00|Surface Expedition|Mars One Base|Mars Expedition 3 sustained the base through the long pre-expansion interval and remained on Mars after XH-02 departed uncrewed.|Backfilled from XH-02 ME-3 support logs and uncrewed return timing.
|
||||||
|
Mars One Base|faf59da6-de11-4f77-892a-5483f17a30e0|event|Expansion Cargo Arrival|Mars One Expansion|2059-10-19 09:00:00+08:00|||Mars One Base|Solar Shuttle 1 delivered the first Mars One expansion package to the surface site.|Backfilled from Solar Shuttle 1 Mars One Expansion support logs.
|
||||||
|
Mars One Base|5112903a-12bb-4529-afa5-1e37eade53d3|state|Mars One Expansion and ME-3 Surface Operations|Mars One Expansion; Mars Expedition 3|2059-10-19 09:00:00+08:00|2059-10-24 14:00:00+08:00|Base Expansion; Surface Expedition|Mars One Base|Expansion work began while Mars Expedition 3 kept the outpost occupied ahead of the next crew arrival.|Overlap inferred from Solar Shuttle 1 expansion and XH-02 ME-4 arrival windows.
|
||||||
|
Mars One Base|e890d519-64ac-4152-902f-42c69a553b84|state|ME-3/ME-4 Crew Handoff with Expansion Package|Mars One Expansion; Mars Expedition 3; Mars Expedition 4|2059-10-24 14:00:00+08:00|2059-11-04 08:00:00+08:00|Base Expansion; Crew Handoff|Mars One Base|Mars Expedition 4 arrived with the expansion package while Mars Expedition 3 prepared for ascent and departure.|Overlap inferred from XH-02 ME-4 Part 1, Solar Shuttle 1 expansion, and XH-02 ME-3 return timing.
|
||||||
|
Mars One Base|292bb2fc-4688-49e6-94e4-598da921d3e5|state|Mars One Expansion and ME-4 Surface Operations|Mars One Expansion; Mars Expedition 4|2059-11-04 08:00:00+08:00|2060-02-23 08:00:00+08:00|Base Expansion; Surface Expedition|Mars One Base|After Expedition 3 departed, Mars Expedition 4 continued surface operations alongside the active expansion campaign.|Backfilled from XH-02 ME-4 and Solar Shuttle 1 expansion support logs.
|
||||||
|
Mars One Base|16e6a85a-2c04-4dc5-8132-b7cd16a104da|state|Expansion, ME-4, and Rover Delivery|Mars One Expansion; Mars Expedition 4; Mars One Rover Delivery|2060-02-23 08:00:00+08:00|2060-05-23 08:00:00+08:00|Base Expansion; Surface Expedition; Cargo Delivery|Mars One Base|The rover delivery campaign overlapped with base expansion and Expedition 4 surface operations.|Overlap inferred from Solar Shuttle 3 rover delivery, Solar Shuttle 1 expansion, and XH-02 ME-4 support logs.
|
||||||
|
Mars One Base|8fc9c2b3-6272-44cb-a942-8bd4c83a6d05|state|Mars One Expansion and ME-4 Surface Operations|Mars One Expansion; Mars Expedition 4|2060-05-23 09:30:00+08:00|2060-08-09 05:10:00+08:00|Base Expansion; Surface Expedition|Mars One Base|Expansion work continued after rover deployment while Mars Expedition 4 remained the resident surface crew.|Backfilled from Solar Shuttle 1 expansion and XH-02 ME-4 support logs.
|
||||||
|
Mars One Base|0d811cfd-1358-46de-b637-3d0b230da76e|event|Rover Deployment Complete|Mars One Rover Delivery; Mars Expedition 4|2060-05-23 09:30:00+08:00|||Mars One Base|The new Mars One surface rover entered service after the cargo delivery campaign wrapped up.|Backfilled from Solar Shuttle 3 rover delivery support logs.
|
||||||
|
Mars One Base|d86f43a9-ae09-4319-8905-98b35c1d585b|state|ME-4 Surface Operations|Mars Expedition 4|2060-08-09 06:00:00+08:00|2060-10-30 08:00:00+08:00|Surface Expedition|Mars One Base|After expansion wrapped up, Mars Expedition 4 continued normal surface operations from the upgraded outpost.|Backfilled from XH-02 ME-4 support logs.
|
||||||
|
Mars One Base|1b798abc-10bb-40cc-a517-4593512b0495|event|Expansion Phase Complete|Mars One Expansion; Mars Expedition 4|2060-08-09 06:00:00+08:00|||Mars One Base|The main Mars One expansion phase was completed and Solar Shuttle 1 departed on the return leg.|Backfilled from Solar Shuttle 1 expansion return timing.
|
||||||
|
Solar Shuttle 2|d190cc9e-e230-4299-8f13-1bda0f6b1aac|state|Construction||2058-09-17 16:00:00+08:00|2059-09-11 08:00:00+08:00|Construction|LEO|Solar Shuttle 2 Construction|Timeline.xlsx inferred monthly phase. Raw: Construction
|
||||||
|
Solar Shuttle 2|8e33ce9e-2409-4b1c-af09-6a931fbe2d49|state|Trial Run||2059-09-11 08:00:00+08:00|2059-12-01 08:00:00+08:00|Trial Run|Earth|Trial Run before commissioning|Timeline.xlsx inferred monthly phase. Raw: Trial Run
|
||||||
|
Solar Shuttle 2|5a2092a8-ddad-41b3-9819-fa52557332ed|state|EOEM Outbound|Europa Outpost Expansion Mission|2059-12-09 08:00:00+08:00|2060-05-01 08:00:00+08:00|Transfer|Sun||
|
||||||
|
Solar Shuttle 2|c0271f19-e604-4c15-9c00-7118fec1928e|state|Europa Outpost Expansion Mission|Europa Outpost Expansion Mission|2060-05-01 08:00:00+08:00|2060-05-06 07:25:00+08:00|Outpost Expansion|Europa|Europa Outpost Expansion Package delivered via Qingtian Cargo Spacecraft|
|
||||||
|
Solar Shuttle 2|687615b6-144f-4fd5-b3ee-709e4eaea6aa|state|EOEM Inbound|Europa Outpost Expansion Mission|2060-05-06 07:25:00+08:00|2060-06-26 08:00:00+08:00|Transfer|Sun||
|
||||||
|
Solar Shuttle 3|219355f0-1399-49af-b352-6728f0d4c82b|state|Construction||2059-01-14 16:00:00+08:00|2059-12-14 08:00:00+08:00|Construction|Star Port|Solar Shuttle 3 Construction|Timeline.xlsx inferred monthly phase. Raw: Construction
|
||||||
|
Solar Shuttle 3|aac31a1a-45c7-47f4-add3-fbfa30f9011f|state|Trial Run||2059-12-14 08:00:00+08:00|2060-02-20 08:00:00+08:00|Trial Run|Earth|Trial Run before commissioning|Timeline.xlsx inferred monthly phase. Raw: Trial Run
|
||||||
|
Solar Shuttle 3|7deb1192-d6f0-4635-9a6d-39ab0c2adcfe|state|Mars One Rover Delivery|Mars One Rover Delivery|2060-02-23 08:00:00+08:00|2060-05-23 08:00:00+08:00|Cargo Delivery|Mars|Mars One Rover Delivery|Timeline.xlsx inferred monthly phase. Raw: Mars One Rover Delivery
|
||||||
|
Solar Shuttle 3|d33a6759-0f54-4e71-909b-6d3916ca13dc|state|Mars Return||2060-11-05 04:07:00+08:00||Transfer|Sun||
|
||||||
|
ST-01 万星源|ae003c1f-f74e-459b-ade2-74fcdae48605|state|Construction||2050-09-15 16:00:00+08:00|2054-04-28 16:00:00+08:00|Construction|LEO|Construction|2050-09-15 First module delivered to LEO via Vulture Shuttle␍
|
||||||
|
2053-02-02 Assembly Halted
|
||||||
|
ST-01 万星源|105a5eac-4ca1-4a5a-a9ad-bc2278d46f01|state|Trial Run||2054-05-12 08:00:00+08:00|2054-08-22 08:00:00+08:00|Trial Run|Lunar Orbit|Trial Run before commissioning|2054-05-12 Begin Trial Run in Earth Orbit, testing all systems
|
||||||
|
2054-08-22 Trial Run complete, all systems perform as expected. Including multiple run to GEO and Lunar Orbit before returning to LEO.
|
||||||
|
ST-01 万星源|f6ae082d-5c36-44f9-92da-d5e568a71fea|state|Deep Space Test Mission||2054-09-07 08:00:00+08:00|2054-11-28 08:00:00+08:00|Deep Space Test Mission|Earth-Sun L2|Uncrewed mission to Earth-Sun Lagrange Point 2 to test long-duration operations|2054-09-07 Begin Deep Space Test Mission
|
||||||
|
2054-11-28 Test Mission Complete, all systems nominal. Returning to LEO.
|
||||||
|
ST-01 万星源|503a0035-1cd7-4e8e-9bc6-922c2e5c3171|state|Maintenance before Commissioning||2054-12-01 08:00:00+08:00|2055-02-28 08:00:00+08:00|Maintenance before Commissioning|LEO||
|
||||||
|
ST-01 万星源|4bfddc84-dfec-43e3-b534-4fb798bcc73f|state|ISSEM-1 Mercury Transfer|Inner Solar System Exploration Mission 1|2055-03-20 08:00:00+08:00|2055-04-17 08:00:00+08:00|Transfer|Sun|Earth-Mecury transfer, using standard trajectory with 127km/s of departure burn|2055-03-20 Picking up crew for Inner Solar System Exploration Mission 1
|
||||||
|
2055-03-22 Earth Departure for Mecury
|
||||||
|
2055-04-17 Mercury Arrival
|
||||||
|
ST-01 万星源|42698c8c-a1cc-4742-9885-7b9e650f9867|state|ISSEM-1 Mercury Operations|Inner Solar System Exploration Mission 1|2055-04-18 08:00:00+08:00|2055-08-14 08:00:00+08:00|Planetary Operations|Mercury|First crewed mission to Mercury|2055-04-17 Mercury Arrival
|
||||||
|
2055-04-18 Orbital Survey of Mercury begins
|
||||||
|
2055-06-14 First Crewed Surface Landing on Mercury
|
||||||
|
2055-06-15 EVA operations commence
|
||||||
|
2055-06-30 Surface operations complete, returning to orbit
|
||||||
|
2055-08-15 Departure from Mercury for Venus
|
||||||
|
ST-01 万星源|2adfa016-b23a-4459-af6e-a84da2aa2469|state|ISSEM-1 Venus Transfer|Inner Solar System Exploration Mission 1|2055-08-15 08:00:00+08:00|2055-10-01 08:00:00+08:00|Transfer|Sun|Mecury-Venus transfer, using standard trajectory with 85km/s of departure burn|2055-08-15 Departure from Mercury for Venus
|
||||||
|
2055-10-01 Venus Arrival
|
||||||
|
ST-01 万星源|f28469ee-1dbc-4232-98ff-91f7abcb9d59|state|ISSEM-1 Venus Operations|Inner Solar System Exploration Mission 1|2055-10-01 08:00:00+08:00|2056-03-24 08:00:00+08:00|Planetary Operations|Venus|First crewed mission to Venus, no crewed landing attempted due to extreme surface conditions. Probe deployment only.|2055-10-01 Venus Arrival
|
||||||
|
2055-10-02 Orbital Survey of Venus begins
|
||||||
|
2055-10-10 Landing Probe Deployment
|
||||||
|
2055-11-10 Begin Upper Atmosphere Survey via Callisto MPV
|
||||||
|
2056-03-24 Departure from Venus for Earth
|
||||||
|
ST-01 万星源|1f6e85aa-121a-4f47-95b6-35f040da055c|state|ISSEM-1 Inbound|Inner Solar System Exploration Mission 1|2056-03-24 08:00:00+08:00|2056-04-12 08:00:00+08:00|Transfer|Sun|Venus-Earth transfer, using standard trajectory with 75km/s of departure burn|2056-03-24 Departure from Venus for Earth
|
||||||
|
2056-04-12 Earth Arrival
|
||||||
|
ST-01 万星源|540c146e-0d48-4735-8eb4-6fe08506b705|state|Maintenance||2056-04-15 08:00:00+08:00|2056-05-31 08:00:00+08:00|Maintenance|LEO||
|
||||||
|
ST-01 万星源|ffd625b0-0de4-4f47-8c8d-36669a1675f6|state|MOCM-2 Outbound|Mars One Construction Mission 2|2056-06-02 08:00:00+08:00|2056-07-01 08:00:00+08:00|Transfer|Sun|Earth-Mars transfer, using standard trajectory with 126km/s of departure burn|2056-06-02 Picking up crew for Mars One Construction Mission 2
|
||||||
|
2056-06-04 Picking up Mars One construction cargo
|
||||||
|
2056-06-06 Earth Departure for Mars
|
||||||
|
2056-07-01 Mars Arrival, First crewed mission to Mars using Stellaria
|
||||||
|
ST-01 万星源|12059276-d10a-4a37-9b8c-6a6183768504|state|MOCM-2 Mars Operations|Mars One Construction Mission 2|2056-07-03 08:00:00+08:00|2056-10-12 08:00:00+08:00|Base Construction|Mars|Continuing the construction of Mars One Base|2056-07-03 Crew descend to Martian Surface, Continuing the construction of Mars One Base
|
||||||
|
2056-07-20 Heavy Cargo Delivery to Surface via Callisto MPV
|
||||||
|
2056-09-17 Mars One Construction Mission 3 Crew Arrival via XH-01, joining Stellaria crew. First Crew Handover on Mars.
|
||||||
|
2056-10-12 Mars One Construction Mission 2 Crew ascend to Stellaria
|
||||||
|
2056-10-15 Departure from Mars for Earth
|
||||||
|
ST-01 万星源|cf351aa5-4149-4938-939b-edcb846f4b14|state|MOCM-2 Inbound|Mars One Construction Mission 2|2056-10-15 08:00:00+08:00|2056-11-03 08:00:00+08:00|Transfer|Sun|Mars-Earth transfer, using Fast Return trajectory with 212km/s of departure burn|2056-10-15 Departure from Mars for Earth
|
||||||
|
2056-11-03 Earth Arrival
|
||||||
|
ST-01 万星源|fd8fba0b-82bb-4ad3-bc38-58745291627b|state|Maintenance||2056-11-04 08:00:00+08:00|2056-12-10 08:00:00+08:00|Maintenance|LEO||
|
||||||
|
ST-01 万星源|7747339c-f602-4ddf-8511-6862006f789b|state|EOCM-1 Outbound|Europa Outpost Construction Mission 1|2056-12-15 08:00:00+08:00|2057-02-25 08:00:00+08:00|Transfer|Sun|Earth-Europa transfer, using standard trajectory with 165km/s of departure burn|2056-12-15 Start preparations for Europa Outpost Construction Mission 1
|
||||||
|
2056-12-20 Europa Outpost Construction Package arrival via Qingtian Cargo Shuttle
|
||||||
|
2056-12-31 Picking up crew for Europa Outpost Construction Mission 1
|
||||||
|
2057-01-02 Earth Departure for Europa
|
||||||
|
ST-01 万星源|545f687e-bd2c-409a-8fb8-186a6dead67e|state|EOCM-1 Europa Operations|Europa Outpost Construction Mission 1|2057-02-27 08:00:00+08:00|2057-10-01 08:00:00+08:00|Outpost Construction|Europa|First crewed mission to Europa|2057-02-27 Heavy Cargo Delivery to Surface via Qingtian Cargo Shuttle
|
||||||
|
2057-03-15 Crew descend to Europan Surface, Beginning construction of Europa Outpost
|
||||||
|
6 crewed ascend and descend operations to Europa Surface via Callisto MPV between 2057-03-15 and 2057-09-15
|
||||||
|
2057-10-01 Europa Outpost Construction Mission 1 Crew ascend to Stellaria, wrapping up construction phase 1
|
||||||
|
2057-10-04 Europa Departure for Earth
|
||||||
|
ST-01 万星源|43f2ece9-283b-4484-b838-d684b9ada4d6|state|EOCM-1 return to Earth|Europa Outpost Construction Mission 1|2057-10-04 08:00:00+08:00|2057-12-20 08:00:00+08:00|Transfer|Europa|Europa-Earth transfer, using Fast trajectory with 216km/s of departure burn|2057-10-04 Europa Departure for Earth␍
|
||||||
|
2057-12-20 Earth Arrival
|
||||||
|
ST-01 万星源|7d6a56c8-9ebc-4940-813b-54ee2c9711f4|state|Maintenance and Upgrades||2058-01-05 08:00:00+08:00|2058-05-15 08:00:00+08:00|Maintenance and Upgrades|LEO|Extensive overhauls to bring ST-01 closer to full Stellaria specifications. Add more greenhouse modules, upgrade life support systems, add another Rotating Habitat Ring|
|
||||||
|
ST-01 万星源|cad8537e-1966-4b3c-abed-35c511950ac8|state|SEM-1 outbound|Saturn Exploration Mission 1|2058-06-01 08:00:00+08:00|2058-10-12 08:00:00+08:00|Transfer|Sun|Earth-Saturn transfer, using Fast trajectory with 210km/s of departure burn|First crewed mission to Saturn
|
||||||
|
ST-01 万星源|022b0296-5c1a-41f8-8634-97dc663b05d9|state|SEM-1 Saturn Operations|Saturn Exploration Mission 1|2058-10-12 08:00:00+08:00|2059-07-01 08:00:00+08:00|Planetary Operations|Saturn System|First crewed mission to Saturn|2059-05-31 Parked into Iapetus orbit, orbital science completed␍
|
||||||
|
2059-06-15 Start Iapetus Surface operation
|
||||||
|
ST-01 万星源|f536e950-a024-45e3-95ff-da5f77772119|state|SEM-1 Inbound|Saturn Exploration Mission 1|2059-07-01 08:00:00+08:00|2059-11-24 08:00:00+08:00|Transfer|Sun|Saturn-Earth transfer, using standard trajectory with 162km/s of departure burn|
|
||||||
|
ST-01 万星源|3fce336c-7ac9-4117-9239-bf939dfe1ad1|state|Maintenance and Upgrades||2059-11-25 08:00:00+08:00|2060-02-10 08:00:00+08:00|Maintenance and Upgrades|Star Port|Upgrades complete, ST-01 now fully meets Stellaria specifications.|
|
||||||
|
ST-01 万星源|8a9de074-6348-4190-bd27-e3bdeddb70f7|state|OSSEM-1 Outbound|Outer Solar System Exploration Mission 1|2060-02-11 08:00:00+08:00|2060-07-24 20:11:00+08:00|Transfer|Sun|地球出发前往海王星|23 crew on board
|
||||||
|
ST-01 万星源|18b3429e-28d3-489c-b65c-35e0e9775a0d|state|OSSEM-1 Neptune Exploration|OSSEM-1; Neptune|2060-07-25 06:40:00+08:00||Exploration|Neptune||
|
||||||
|
ST-02 Kristen|eefd7712-b6f4-496c-9bc8-8eb9546887b8|state|Construction||2052-01-14 16:00:00+08:00|2053-02-10 08:00:00+08:00|Construction|LEO|ST-02 Kristen Construction|Timeline.xlsx inferred monthly phase. Raw: Construction
|
||||||
|
ST-02 Kristen|721dec30-3c58-4885-8fda-22e2960ef4a0|state|Assembly Halted||2053-02-10 08:00:00+08:00|2053-05-12 08:00:00+08:00|Assembly Halted|LEO|Assembly halted pending redesign and program review|Timeline.xlsx inferred monthly phase. Raw: Assembly Halted
|
||||||
|
ST-02 Kristen|8226a3aa-1896-42e2-9e5e-0fca750d229d|state|Reconfiguring to ST-02||2053-05-12 08:00:00+08:00|2055-10-13 08:00:00+08:00|Reconfiguring to ST-02|LEO|Vehicle reconfiguration and systems integration|Timeline.xlsx inferred monthly phase. Raw: Reconfiguring to ST-02
|
||||||
|
ST-02 Kristen|826e67d8-e876-41e5-9551-a80f700e90cd|state|Trial Run||2055-10-13 08:00:00+08:00|2056-03-10 08:00:00+08:00|Trial Run|Lunar Orbit|Trial Run before commissioning|Timeline.xlsx inferred monthly phase. Raw: Trial Run
|
||||||
|
ST-02 Kristen|b5a01aed-7a98-4f81-a84f-efb59b8f2479|state|Deep Space Test Mission||2056-03-10 08:00:00+08:00|2056-06-08 08:00:00+08:00|Deep Space Test Mission|Earth-Sun L2|Uncrewed Mission to Earth-Sun L2|Timeline.xlsx inferred monthly phase. Raw: Deep Space Test Mission / Uncrewed Mission to Earth-Sun L2
|
||||||
|
ST-02 Kristen|7b7fd38f-2647-4ed8-b1bc-27e94861b15d|state|Maintenance||2056-06-08 08:00:00+08:00|2056-11-19 08:00:00+08:00|Maintenance|LEO||Timeline.xlsx inferred monthly phase. Raw: Maintenance
|
||||||
|
ST-02 Kristen|ca8f8223-800b-4562-8405-a270d3960a6e|state|ME-1 Mars Operations|Mars Expedition 1|2056-11-19 08:00:00+08:00|2057-09-08 08:00:00+08:00|Planetary Operations|Mars|Mars Expedition 1|Timeline.xlsx inferred monthly phase. Raw: Mars Expedition 1
|
||||||
|
ST-02 Kristen|a7c43eae-31fc-451d-8bbc-fd6f3da63f22|state|Maintenance||2057-09-08 08:00:00+08:00|2057-10-06 08:00:00+08:00|Maintenance|LEO||Timeline.xlsx inferred monthly phase. Raw: Maintenance
|
||||||
|
ST-02 Kristen|a61ee6fc-5495-4b5b-a505-968669b81b18|state|EOCM-2 Outbound|Europa Outpost Construction Mission 2|2057-10-06 08:00:00+08:00|2058-01-19 08:00:00+08:00|Transfer|Sun|Earth-Europa transfer|Timeline.xlsx inferred monthly phase. Raw: Earth-Europa
|
||||||
|
ST-02 Kristen|fb17cab1-dcbc-4814-83e9-5fb81a73f869|state|EOCM-2 Europa Operations|Europa Outpost Construction Mission 2|2058-01-19 08:00:00+08:00|2058-09-06 08:00:00+08:00|Outpost Construction|Europa|Europa Outpost Construction Mission 2|Timeline.xlsx inferred monthly phase. Raw: Europa Outpost Construction Mission 2
|
||||||
|
ST-02 Kristen|efdb28dd-3e02-4b16-80e8-7a2d4df76825|state|EOCM-2 Inbound|Europa Outpost Construction Mission 2|2058-09-06 08:00:00+08:00|2058-11-08 08:00:00+08:00|Transfer|Sun|Europa-Earth transfer|Timeline.xlsx inferred monthly phase. Raw: Europa-Earth
|
||||||
|
ST-02 Kristen|77c71061-354d-4045-b5cd-5cd6b1e8dd65|state|Maintenance and Upgrades||2058-11-08 08:00:00+08:00|2059-04-06 08:00:00+08:00|Maintenance and Upgrades|LEO||Timeline.xlsx inferred monthly phase. Raw: Maintenance and Upgrades
|
||||||
|
ST-02 Kristen|fe21560f-60f4-48bb-bbdb-e2c1ab6fe3af|state|EE-2 Outbound|Europa Expedition 2|2059-04-06 08:00:00+08:00|2059-06-25 08:00:00+08:00|Transfer|Sun|Earth-Europa transfer|
|
||||||
|
ST-02 Kristen|65910288-0c2a-4800-bfce-39495efafce2|state|EE-2 Europa Operations|Europa Expedition 2|2059-06-25 08:00:00+08:00|2060-06-28 02:47:00+08:00|Planetary Operations|Europa|Supporting Europa Expedition 2|
|
||||||
|
ST-02 Kristen|e77cd6b5-a2e2-479b-817b-67c1c59bfdee|state|EE-2 Inbound|Europa Expedition 2|2060-06-28 02:47:00+08:00|2060-08-30 06:11:00+08:00|Transfer|Sun||
|
||||||
|
ST-02 Kristen|6c7a8724-1324-4a68-81e1-3dc2ce1ce980|state|Upgrade to Stellaria Class||2060-08-30 07:26:00+08:00|2061-06-30 08:00:00+08:00|Upgrade|Star Ring Station|A full overhaul to bring ST-02 to full Stellaria standard|
|
||||||
|
ST-03 Muelsyse|10030cb4-2626-483a-8682-db9eed90da71|state|Construction||2056-02-13 16:00:00+08:00|2059-07-07 08:00:00+08:00|Construction|LEO|ST-03 Muelsyse Construction|Timeline.xlsx inferred monthly phase. Raw: Construction
|
||||||
|
ST-03 Muelsyse|520e5ad4-e79c-458d-8387-76ba0a120b2e|state|Assembly Complete||2059-07-07 08:00:00+08:00|2059-08-12 08:00:00+08:00|Assembly Complete|LEO|Assembly complete, preparing for commissioning|Timeline.xlsx inferred monthly phase. Raw: Assembly Complete
|
||||||
|
ST-03 Muelsyse|068b82d8-e107-465b-98b8-eba39739ee63|state|Trial Run||2059-08-12 08:00:00+08:00|2060-03-07 08:00:00+08:00|Trial Run|Lunar Orbit|Trial Run before commissioning|Timeline.xlsx inferred monthly phase. Raw: Trial Run
|
||||||
|
ST-03 Muelsyse|16e6ce3d-d2ef-4910-a1ff-4670cbb63fb7|state|Maintenance||2060-03-07 08:00:00+08:00|2060-06-10 08:00:00+08:00|Maintenance|LEO||Timeline.xlsx inferred monthly phase. Raw: Maintenance
|
||||||
|
ST-03 Muelsyse|e61bb0d2-ab64-4f79-a18a-77dea92eb3ab|state|Deep Space Test||2060-06-15 08:00:00+08:00|2060-09-10 08:00:00+08:00|Deep Space Test|Sun-Earth L2||
|
||||||
|
ST-03 Muelsyse|2f391faf-4061-45cc-bcd5-ce5956cff9bd|state|Pre-commissioning Maintenance||2060-09-10 08:00:00+08:00|2060-10-25 08:00:00+08:00|Maintenance|LEO||
|
||||||
|
ST-04|b5fceb9b-264c-4842-a329-0a7c4ccc9779|state|Construction||2059-07-12 16:00:00+08:00|2063-01-07 08:00:00+08:00|Construction|LEO|ST-04 Construction|Timeline.xlsx inferred monthly phase. Raw: Construction
|
||||||
|
ST-04|4dfd3584-ad5c-4d6d-9c86-d337d627fb9e|state|Assembly Complete||2063-01-07 08:00:00+08:00|2063-02-12 08:00:00+08:00|Assembly Complete|LEO|Assembly complete, preparing for commissioning|Timeline.xlsx inferred monthly phase. Raw: Assembly Complete
|
||||||
|
ST-04|93f7700f-d0be-48a9-8fdf-f1d0a824ac86|state|Trial Run||2063-02-12 08:00:00+08:00|2063-09-06 08:00:00+08:00|Trial Run|Lunar Orbit|Trial Run before commissioning|Timeline.xlsx inferred monthly phase. Raw: Trial Run
|
||||||
|
ST-04|c6ac0899-e235-4de6-8a9e-89ff98e725fa|state|Maintenance||2063-09-06 16:00:00+08:00||Maintenance|LEO||Timeline.xlsx inferred monthly phase. Raw: Maintenance
|
||||||
|
XH-01 羲和|1934e3f4-0d0c-4699-b4bd-753744ab29a1|state|Construction||2050-03-16 00:00:00+08:00|2052-02-07 00:00:00+08:00|Construction|LEO|Xihe XH-01 Construction|2050-03-15 First module delivered to LEO via Vulture Shuttle␍
|
||||||
|
2052-02-06 Assembly completed in LEO
|
||||||
|
XH-01 羲和|a0a3bed9-91a2-45c7-be54-7216e33f87b3|state|Fitting||2052-02-07 08:00:00+08:00|2052-05-31 08:00:00+08:00|Fitting|LEO||
|
||||||
|
XH-01 羲和|8ee54d7e-4310-43dc-a703-f633c1013405|state|Trial Run||2052-06-01 08:00:00+08:00|2052-11-30 08:00:00+08:00|Trial Run|Earth|Trial Run before commissioning|2052-06-01 Begin Trial Run in Earth Orbit, testing all systems
|
||||||
|
2052-11-30 Trial Run complete, all systems perform as expected. Including multiple run to GEO and Lunar Orbit before returning to LEO.
|
||||||
|
XH-01 羲和|4464a892-910e-416c-b044-0683b6516003|state|Maintenance||2052-12-01 08:00:00+08:00|2053-01-05 08:00:00+08:00|Maintenance|Earth||
|
||||||
|
XH-01 羲和|d103bc92-da3a-4ba6-965e-a9385e208581|state|Deep Space Test Mission||2053-01-12 08:00:00+08:00|2053-12-18 08:00:00+08:00|Deep Space Test Mission|Earth-Sun L2|Long term uncrewed test|2053-01-12 Uncrewed mission to Earth-Sun L2 point to test deep space operations
|
||||||
|
2053-12-18 Back to LEO
|
||||||
|
XH-01 羲和|80931042-da68-435b-94f5-da2d1b79e801|state|Maintenance and Upgrades||2054-01-07 08:00:00+08:00|2054-06-28 08:00:00+08:00|Maintenance and Upgrades|LEO||
|
||||||
|
XH-01 羲和|e54817e0-6809-404d-be62-c6017d143362|state|MMD Outbound|Mars Mission Demo|2054-07-10 08:00:00+08:00|2054-08-21 08:00:00+08:00|Transfer|Sun|Earth-Mars transfer, Uses standard trajectory with 102km/s of departure burn|
|
||||||
|
XH-01 羲和|23b4304f-8fca-4d78-8a93-c9f78fab51e0|state|MMD Mars Operations|Mars Mission Demo|2054-08-21 08:00:00+08:00|2055-01-10 08:00:00+08:00|Planetary Operations|Mars|Uncrewed, Deliver the first Mars-One base construction equipment package to Mars orbit.|2054-08-21 Mars Orbit Arrival, deploy equipment package to Mars surface via lander
|
||||||
|
2055-01-10 Mars Departure, return to Earth
|
||||||
|
XH-01 羲和|ded06cd6-f9a4-4646-9be4-2f01cf067334|state|MMD Inbound|Mars Mission Demo|2055-01-10 08:00:00+08:00|2055-02-28 08:00:00+08:00|Transfer|Sun|Uses standard trajectory with 97km/s of departure burn|2055-02-28 Earth Arrival, demo run complete, all systems perform as expected.
|
||||||
|
XH-01 羲和|fecc4450-ba77-4bc6-b794-164766a0c8b8|state|Maintenance and Upgrades||2055-03-01 08:00:00+08:00|2055-05-14 08:00:00+08:00|Maintenance and Upgrades|LEO||
|
||||||
|
XH-01 羲和|aab3125c-c99e-4af5-ab64-ffe7fda48ad7|state|MOCM-1 Outbound|Mars One Base Construction Mission 1|2055-06-15 08:00:00+08:00|2055-07-30 08:00:00+08:00|Transfer|Sun|Earth-Mars transfer, Uses standard trajectory with 115km/s of departure burn|2055-06-15 Picking up crew for Mars One base Construction Mission 1
|
||||||
|
2055-06-18 Earth Departure for Mars
|
||||||
|
XH-01 羲和|ec34d68b-4728-42eb-af79-b3e8805aa253|state|MOCM-1 Mars Operations|Mars One Base Construction Mission 1|2055-07-30 08:00:00+08:00|2056-02-07 08:00:00+08:00|Base Construction|Mars|First Crewed mission to Mars. Construction for Mars One base begin|2055-07-30 Mars Arrival, begin base construction
|
||||||
|
2056-02-07 Mars Departure, return to Earth. First to use quick return trajectory.
|
||||||
|
XH-01 羲和|bdf99335-d329-4bd2-8e17-27ac1f097ca9|state|MOCM-1 Inbound|Mars One Base Construction Mission 1|2056-02-07 08:00:00+08:00|2056-03-18 08:00:00+08:00|Transfer|Sun|Mars-Earth transfer. Uses Fast-travel trajectory with 207km/s of departure burn|2056-03-18 Earth Arrival, crew return
|
||||||
|
XH-01 羲和|8e1be891-57ec-4551-b114-232f4681b2b1|state|Maintenance and Upgrades||2056-03-25 08:00:00+08:00|2056-06-20 08:00:00+08:00|Maintenance and Upgrades|LEO||
|
||||||
|
XH-01 羲和|ea2024c5-95e6-4acc-a88d-998183a35a5a|state|MOCM-3 Outbound|Mars One Base Construction Mission 3|2056-07-30 08:00:00+08:00|2056-09-15 08:00:00+08:00|Transfer|Sun|Earth-Mars transfer. Uses standard trajectory with 95km/s of departure burn|2056-07-30 Picking up crew for Mars One base Construction Mission 3
|
||||||
|
2056-08-02 Earth Departure for Mars
|
||||||
|
XH-01 羲和|fd22c4b2-4938-4c01-ae92-e84f14d8b8dd|state|MOCM-3 Operations|Mars One Base Construction Mission 3|2056-09-15 08:00:00+08:00|2057-02-12 08:00:00+08:00|Base Construction|Mars|Third Mars One base construction. First crew hand-off on Mars(with Mars One base construction 2).|2056-09-15 Mars Arrival, continue base construction
|
||||||
|
2057-02-12 Mars Departure, return to Earth.
|
||||||
|
XH-01 羲和|e5f549b9-0446-48be-a606-d9c1ada664b7|state|MOCM-3 Inbound|Mars One Base Construction Mission 3|2057-02-12 08:00:00+08:00|2057-03-25 08:00:00+08:00|Transfer|Sun|Mars-Earth transfer. Uses standard trajectory with 122km/s of departure burn|2037-03-25 Earth Arrivasl, crew return
|
||||||
|
XH-01 羲和|6d57910a-7459-4b74-b0ea-e10ee7cab87b|state|Maintenance||2057-03-26 08:00:00+08:00|2057-04-25 08:00:00+08:00|Maintenance|LEO||
|
||||||
|
XH-01 羲和|30c84287-e5d1-473c-93fb-9d36d426516e|state|ME-2 Outbound|Mars Expedition 2|2057-05-05 08:00:00+08:00|2057-06-22 08:00:00+08:00|Transfer|Sun|Earth-Mars transfer using standard trajectory with 110km/s of departure burn|2027-05-05 Picking up crew for Mars Expedition 2
|
||||||
|
2027-05-09 Earth Departure for Mars
|
||||||
|
XH-01 羲和|7f5da9b3-94fa-4bd5-a6bd-94f6841c71a2|state|ME-2 Mars Operations|Mars Expedition 2|2057-06-22 08:00:00+08:00|2058-04-18 08:00:00+08:00|Planetary Operations|Mars|Second long term Mars Expedition, first mission to Mars One base after the construction is completed.
|
||||||
|
First mission involving space tourism to Mars.|2057-06-22 Mars Arrival
|
||||||
|
2057-06-25 Mars EDL and surface operations begin
|
||||||
|
2058-04-10 Begin Mars Departure preparations
|
||||||
|
2058-04-18 Mars Departure for Earth
|
||||||
|
XH-01 羲和|058bbf6d-5c51-49c4-8516-8b6931db53c1|state|ME-2 Inbound|Mars Expedition 2|2058-04-18 08:00:00+08:00|2058-05-20 08:00:00+08:00|Transfer|Sun|Mars-Earth transfer using standard trajectory with 115km/s of departure burn|2058-04-18 Mars Departure for Earth
|
||||||
|
2058-05-20 Earth Arrival, crew return
|
||||||
|
XH-01 羲和|161bb9f0-3d2a-46da-9044-9184e0704413|state|Maintenance||2058-05-21 08:00:00+08:00|2058-06-18 08:00:00+08:00|Maintenance|LEO||
|
||||||
|
XH-01 羲和|4ff368c4-503b-42ce-9e82-b4da291d3203|state|EE-1 Outbound|Europa Expedition 1|2058-07-10 08:00:00+08:00|2058-09-25 08:00:00+08:00|Transfer|Sun|Earth-Jupiter transfer using standard trajectory with 160km/s of departure burn|2058-07-10 Picking up crew for Europa Expedition 1
|
||||||
|
2058-07-12 Earth Departure for Jupiter system
|
||||||
|
2058-09-25 Europa Arrival
|
||||||
|
XH-01 羲和|f01bfb99-f981-45f6-808b-7c5222503715|state|EE-1 Jupiter System Operations|Europa Expedition 1|2058-09-25 08:00:00+08:00|2059-07-10 08:00:00+08:00|Planetary Operations|Jupiter System|First standard crew expedition to Europa Outpost to begin exploration of Jupiter system
|
||||||
|
Long term surface operation at Europa Outpost, followed by exploration of IO and Ganymede|2058-09-25 Europa Arrival
|
||||||
|
2058-09-27 Europa descend, begin surface operation at Europa Outpost
|
||||||
|
2058-12-15 Europa ascend, begin Jupiter system exploration
|
||||||
|
2058-12-20 Begin IO exploration
|
||||||
|
2059-01-20 Return to Europa Outpost
|
||||||
|
2059-06-20 Begin Europa Outpost departure preparations
|
||||||
|
2059-06-25 Europa Expedition 1 crew ascend from Europa Outpost
|
||||||
|
2059-07-05 begin Ganymede exploration
|
||||||
|
2059-07-10 Departure for Earth, upgrade planned.
|
||||||
|
XH-01 羲和|92315017-7011-4930-aabb-d00a6f3f4994|state|EE-1 Inbound|Europa Expedition 1|2059-07-10 08:00:00+08:00|2059-11-15 08:00:00+08:00|Transfer|Sun|Uses standard trajectory with 112km/s of departure burn␍
|
||||||
|
Parked to Star Port|2059-07-10 Departure for Earth.␍
|
||||||
|
2059-11-15 Earth Arrival, Park to Star Port.
|
||||||
|
XH-01 羲和|21a37859-aa16-4c29-b17a-8012f6037478|state|Maintenance and Upgrades||2059-11-16 08:00:00+08:00|2060-02-19 08:00:00+08:00|Maintenance and Upgrades|Star Port|Greenhouse upgrade to support long term deep space missions|
|
||||||
|
XH-01 羲和|ee52cc53-7d9c-4134-ba1b-4f7cfb8fdce9|state|EE-3 Outbound|Europa Expedition 3|2060-02-20 08:00:00+08:00|2060-05-01 08:00:00+08:00|Transfer|Sun|Uses standard trajectory with 163km/s of departure burn|2060-02-20 Picking up Europa Expedition 3 crew, On its way to explore Jupiter systems before sending crew to Europa Outpost
|
||||||
|
XH-01 羲和|91d4948a-ae04-43a8-ac3b-def9caecc8e5|state|EE-3 Jupiter System Operations|Europa Expedition 3|2060-05-01 08:00:00+08:00||Jupiter Exploration; Outpost Rotation|Jupiter System|Crew Rotation to Euorpa Outpost. First to explore Jupiter's moons, then to Europa Outpost|2060-05-01 Jupiter Arrival␍
|
||||||
|
2060-05-02 Io Arrival␍
|
||||||
|
2060-05-07 Io descend␍
|
||||||
|
2060-05-09 Io departure␍
|
||||||
|
2060-05-11 Ganymede Arrival␍
|
||||||
|
2060-05-12 Ganymede descend␍
|
||||||
|
2060-05-21 Ganymede Departure for Callisto␍
|
||||||
|
2060-05-25 Callisto Arrival
|
||||||
|
XH-02 太白|4958ffd7-2e23-40a6-9b96-e46c61943028|state|Construction||2054-12-11 08:00:00+08:00|2056-10-04 08:00:00+08:00|Construction|LEO|XH-02 太白 Construction|Timeline.xlsx inferred monthly phase. Raw: Construction
|
||||||
|
XH-02 太白|b99a2638-7560-45ec-9328-6a4a0e4cf685|state|Assembly Complete||2056-10-04 08:00:00+08:00|2056-11-11 08:00:00+08:00|Assembly Complete|LEO|Assembly complete, preparing for commissioning|Timeline.xlsx inferred monthly phase. Raw: Assembly Complete
|
||||||
|
XH-02 太白|6ec7033c-e711-4148-ab7d-e075f3288021|state|Trial Run||2056-11-11 08:00:00+08:00|2057-03-12 08:00:00+08:00|Trial Run|Earth|Trial Run before commissioning|Timeline.xlsx inferred monthly phase. Raw: Trial Run
|
||||||
|
XH-02 太白|669a993c-fe3d-4126-b066-2650e98dfb3e|state|Maintenance||2057-03-12 08:00:00+08:00|2057-04-25 08:00:00+08:00|Maintenance|LEO||Timeline.xlsx inferred monthly phase. Raw: Maintenance
|
||||||
|
XH-02 太白|7c166ff7-df3b-495d-a7df-9b99949e23f0|state|AEM Outbound|Asteroid Exploration Mission|2057-05-04 08:00:00+08:00|2057-07-17 08:00:00+08:00|Transfer|Sun|Earth-Vesta transfer|Timeline.xlsx inferred monthly phase. Raw: Earth-Vesta
|
||||||
|
XH-02 太白|99eb75ba-e02b-4cdc-a5f1-3ea547cc7d4a|state|AEM Vesta Operations|Asteroid Exploration Mission|2057-07-17 08:00:00+08:00|2058-01-04 08:00:00+08:00|Asteroid Operations|Vesta|Asteroid exploration mission at Vesta|Timeline.xlsx inferred monthly phase. Raw: Vesta Mission
|
||||||
|
XH-02 太白|d74dfb61-fd3a-492c-b019-253ec9cd826d|state|AEM Inbound|Asteroid Exploration Mission|2058-01-04 08:00:00+08:00|2058-04-17 08:00:00+08:00|Transfer|Sun|Vesta-Earth transfer|Timeline.xlsx inferred monthly phase. Raw: Vesta-Earth
|
||||||
|
XH-02 太白|956cc319-668e-45d1-ab7c-9f0aa408c5f7|state|ME-3 Mars Operations|Mars Expedition 3|2058-04-17 08:00:00+08:00|2059-05-04 08:00:00+08:00|Planetary Operations|Mars|Mars Expedition 3|Timeline.xlsx inferred monthly phase. Raw: Mars Expedition 3
|
||||||
|
XH-02 太白|f43aa249-80d1-426c-9aca-a341411be98d|state|Uncrewed Inbound|Mars Expedition 3|2059-05-04 08:00:00+08:00|2059-07-06 08:00:00+08:00|Transfer|Sun|Uncrewed Inbound for TH-02. Mars Expedition 3 Crew remaining on Mars|
|
||||||
|
XH-02 太白|2ae7438b-a2a2-44ee-a084-a76bc1e94114|state|Maintenance||2059-07-06 08:00:00+08:00|2059-08-04 08:00:00+08:00|Maintenance|Star Port||Timeline.xlsx inferred monthly phase. Raw: Maintenance
|
||||||
|
XH-02 太白|fcb05995-823e-4a63-9cf8-44ea953e2e9a|state|ME-4 Outbound|Mars Expedition 4|2059-08-04 08:00:00+08:00|2059-10-24 08:00:00+08:00|Transfer|Sun|Earth-Mars transfer|Carrying Mars Expedition 4 crew and Mars One Expansion Package to Mars
|
||||||
|
XH-02 太白|c00366fd-6b1d-46e0-8163-4bd6b510f374|state|ME-4 Mars Operations Part 1|Mars Expedition 4|2059-10-24 14:00:00+08:00|2059-11-04 08:00:00+08:00|Planetary Operations|Mars|ME-4 Crew to Mars One with Mars One Expansion Package. Mars Expedition 3 ascend.|
|
||||||
|
XH-02 太白|33a45210-3497-4826-ba6e-291b384ebe7e|state|ME-3 Inbound|Mars Expedition 3|2059-11-04 08:00:00+08:00|2060-01-06 08:00:00+08:00|Transfer|Sun|Carrying ME-3 Crew home|
|
||||||
|
XH-02 太白|5562aa60-8d62-4ed9-b1cc-51ff99c49d18|state|Maintenance||2060-01-06 08:00:00+08:00|2060-02-17 08:00:00+08:00|Maintenance|LEO||Timeline.xlsx inferred monthly phase. Raw: Maintenance
|
||||||
|
XH-02 太白|76cd99ba-c259-4969-a8ae-2fa14e303974|state|Uncrewed Outbound|Mars Expedition 4|2060-01-22 08:00:00+08:00|2060-02-27 08:00:00+08:00|Transfer|Sun||
|
||||||
|
XH-02 太白|c7e777fd-c7d8-4d01-be07-871862829983|state|ME-4 Mars Operations Part 2|Mars Expedition 4|2060-02-28 08:00:00+08:00|2060-10-30 08:00:00+08:00|Planetary Operations|Mars|Uncrewed Mars mission segment|Timeline.xlsx inferred monthly phase. Raw: Uncrewed to Mars, Mars Expedition 4 Part 2
|
||||||
|
XH-03 常曦|505255ab-902e-4586-9905-8ecad10353e7|state|Construction||2058-02-15 16:00:00+08:00|2059-12-09 08:00:00+08:00|Construction|LEO|XH-03 常曦 Construction|Timeline.xlsx inferred monthly phase. Raw: Construction
|
||||||
|
XH-03 常曦|b0848762-4f30-45fa-9e5f-5e53a0a7aba3|state|Assembly Complete||2059-12-09 08:00:00+08:00|2060-01-14 08:00:00+08:00|Assembly Complete|LEO|Assembly complete, preparing for commissioning|Timeline.xlsx inferred monthly phase. Raw: Assembly Complete
|
||||||
|
XH-03 常曦|b87ea1ef-b240-48f7-aba8-0af7566bd6be|state|Trial Run||2060-01-14 08:00:00+08:00|2060-06-09 08:00:00+08:00|Trial Run|Earth|Trial Run before commissioning|Timeline.xlsx inferred monthly phase. Raw: Trial Run
|
||||||
|
XH-03 常曦|bdc91489-7c65-45c2-bd78-4a37edc9dd4e|state|Maintenance||2060-06-09 08:00:00+08:00||Maintenance|LEO||Timeline.xlsx inferred monthly phase. Raw: Maintenance
|
||||||
|
XH-04|86055939-185d-476b-afc5-1a60a69a1ccb|state|Construction||2059-09-16 16:00:00+08:00|2061-08-10 08:00:00+08:00|Construction|LEO|XH-04 Construction|Timeline.xlsx inferred monthly phase. Raw: Construction
|
||||||
|
XH-04|d9b91820-7ea9-4f79-bca6-1fd450862c17|state|Assembly Complete||2061-08-10 08:00:00+08:00|2061-09-15 08:00:00+08:00|Assembly Complete|LEO|Assembly complete, preparing for commissioning|Timeline.xlsx inferred monthly phase. Raw: Assembly Complete
|
||||||
|
XH-04|952a415e-408b-42a4-8dd8-24940e745d65|state|Trial Run||2061-09-15 08:00:00+08:00|2062-02-10 08:00:00+08:00|Trial Run|Earth|Trial Run before commissioning|Timeline.xlsx inferred monthly phase. Raw: Trial Run
|
||||||
|
XH-04|85aa5084-4c7a-4863-8bcc-ee9238d95d2c|state|Maintenance||2062-02-10 08:00:00+08:00||Maintenance|LEO||Timeline.xlsx inferred monthly phase. Raw: Maintenance
|
||||||
|
启明空间站|00aec49b-6dc1-4b58-bbf1-96086b93ff0e|state|Expedition 11|Expedition 11|2059-05-04 01:02:00+08:00|2059-11-16 20:55:00+08:00|Standard Rotation|LEO||
|
||||||
|
启明空间站|1c346955-ba6b-4f54-9308-d088fa99f766|state|Expedition 12|Expedition 12|2059-11-16 16:00:00+08:00|2060-05-15 01:44:00+08:00|Standard Rotation|LEO||
|
||||||
|
启明空间站|7c74d703-98bc-486d-b06f-591f38661181|state|Expedition 13|Expedition 13|2060-05-12 15:40:00+08:00|2060-11-12 15:40:00+08:00|Standard Rotation|LEO||
|
||||||
|
启明空间站|3c81a7b5-4f86-4f48-aa6f-99dec955dc42|event|Reconfigure to 启明空间站|启明载人航天计划|2060-07-06 17:15:00+08:00|||LEO|准备进行空间站格局调整,利用现有基础设施构建启明空间站|
|
||||||
|
启明空间站|4d14317f-3e78-4e9e-9a9c-a9555ef15b24|state|启明空间站重力模块升级|启明载人航天计划|2060-07-06 20:40:00+08:00|2060-09-15 20:55:00+08:00|启明空间站重力模块升级|LEO|擎天E3发射,运送了全新的节点舱和人工重力模块,由Expedition 13乘组完成安装|
|
||||||
|
|
After Width: | Height: | Size: 116 KiB |
|
After Width: | Height: | Size: 112 KiB |
|
After Width: | Height: | Size: 98 KiB |
|
After Width: | Height: | Size: 96 KiB |
|
After Width: | Height: | Size: 96 KiB |
|
After Width: | Height: | Size: 95 KiB |
|
After Width: | Height: | Size: 97 KiB |
|
After Width: | Height: | Size: 102 KiB |
|
After Width: | Height: | Size: 100 KiB |
|
After Width: | Height: | Size: 94 KiB |
|
After Width: | Height: | Size: 111 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 72 KiB |
|
After Width: | Height: | Size: 103 KiB |
|
After Width: | Height: | Size: 72 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 77 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 82 KiB |
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 84 KiB |
|
After Width: | Height: | Size: 121 KiB |
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 52 KiB |
|
After Width: | Height: | Size: 87 KiB |
|
After Width: | Height: | Size: 89 KiB |
|
After Width: | Height: | Size: 96 KiB |
|
After Width: | Height: | Size: 97 KiB |
|
After Width: | Height: | Size: 74 KiB |
|
After Width: | Height: | Size: 73 KiB |
|
After Width: | Height: | Size: 83 KiB |
|
After Width: | Height: | Size: 59 KiB |
@@ -0,0 +1,734 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN" data-theme="dark">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>KSP管理后台</title>
|
||||||
|
<style>
|
||||||
|
:root{--bg:#0B0B10;--sfc:#131318;--sfc2:#1a1a22;--brd:rgba(248,250,252,.06);--txt:#F8FAFC;--mut:#94A3B8;--acc:#3B82F6;--ag:rgba(59,130,246,.3);--grn:#22c55e;--amb:#eab308;--red:#ef4444;--cyn:#06b6d4;--pur:#a855f7;--rad:6px;--sw:224px;--th:42px;--fd:'SF Mono','Cascadia Code',Consolas,monospace;--fu:system-ui,-apple-system,'Segoe UI',sans-serif}
|
||||||
|
[data-theme="light"]{--bg:#F1F5F9;--sfc:#FFFFFF;--sfc2:#F8FAFC;--brd:rgba(0,0,0,.06);--txt:#0F172A;--mut:#64748B;--acc:#2563EB;--ag:rgba(37,99,235,.15)}
|
||||||
|
*{margin:0;padding:0;box-sizing:border-box}
|
||||||
|
body{background:var(--bg);color:var(--txt);font-family:var(--fu);height:100vh;overflow:hidden;display:flex;flex-direction:column}
|
||||||
|
::-webkit-scrollbar{width:4px;height:4px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:var(--brd);border-radius:2px}
|
||||||
|
|
||||||
|
/* Topbar */
|
||||||
|
#tb{height:var(--th);display:flex;align-items:center;gap:12px;padding:0 14px;background:var(--sfc);border-bottom:1px solid var(--brd);z-index:30;flex-shrink:0}
|
||||||
|
#tb .br{font-family:var(--fd);font-size:.78rem;font-weight:600;letter-spacing:-.02em}#tb .br b{color:var(--acc);font-weight:700}
|
||||||
|
#tb .tb-sp{flex:1}
|
||||||
|
.tb-time{display:flex;align-items:center;gap:5px}
|
||||||
|
.tb-time .l{font-size:.62rem;color:var(--mut);text-transform:uppercase;letter-spacing:.05em}
|
||||||
|
.tb-time #td{font-family:var(--fd);font-size:.74rem;color:var(--acc);background:rgba(59,130,246,.06);padding:3px 8px;border-radius:4px;cursor:pointer;border:1px solid transparent;transition:.15s}
|
||||||
|
.tb-time #td:hover{background:rgba(59,130,246,.12);border-color:var(--ag)}
|
||||||
|
.b{display:inline-flex;align-items:center;gap:4px;padding:3px 8px;border-radius:4px;font-size:.7rem;cursor:pointer;border:1px solid var(--brd);background:transparent;color:var(--mut);transition:.15s;white-space:nowrap}
|
||||||
|
.b:hover{background:rgba(128,128,128,.08);color:var(--txt)}.b.ac{background:var(--acc);color:#fff;border-color:var(--acc)}.b.ac:hover{box-shadow:0 0 10px var(--ag)}
|
||||||
|
.b.sm{padding:2px 6px;font-size:.64rem}.b.dg{border-color:var(--red);color:var(--red)}.b.dg:hover{background:rgba(239,68,68,.08)}
|
||||||
|
|
||||||
|
/* Layout */
|
||||||
|
#ly{display:flex;flex:1;overflow:hidden}
|
||||||
|
#sb{width:var(--sw);flex-shrink:0;background:var(--sfc);border-right:1px solid var(--brd);overflow-y:auto;padding:6px 0;user-select:none}
|
||||||
|
.nst{display:flex;align-items:center;gap:6px;padding:8px 14px 2px;font-family:var(--fd);font-size:.6rem;font-weight:600;color:var(--mut);text-transform:uppercase;letter-spacing:.08em;cursor:pointer}.nst .ar{font-size:.5rem;transition:transform .15s;opacity:.4}.nst.on .ar{transform:rotate(-90deg)}.nst.on+.niw{display:none}
|
||||||
|
.niw{padding-bottom:2px}
|
||||||
|
.ni{display:flex;align-items:center;gap:8px;padding:6px 14px;cursor:pointer;font-size:.76rem;color:var(--mut);transition:.1s;border-left:2px solid transparent}
|
||||||
|
.ni:hover{color:var(--txt);background:rgba(128,128,128,.03)}.ni.act{color:var(--txt);background:rgba(59,130,246,.05);border-left-color:var(--acc)}
|
||||||
|
.ni .ic{width:16px;height:16px;display:flex;align-items:center;justify-content:center;font-size:.8rem;flex-shrink:0;opacity:.6}.ni.su{padding-left:36px;font-size:.72rem}
|
||||||
|
|
||||||
|
#ct{flex:1;overflow-y:auto;padding:20px 24px;position:relative}
|
||||||
|
.pg{display:none}.pg.act{display:block}.pg.loc{display:none;position:absolute;inset:0;padding:0 !important;z-index:20;background:var(--bg)}.pg.loc.act{display:flex;flex-direction:column}
|
||||||
|
|
||||||
|
/* Page Header */
|
||||||
|
.ph{margin-bottom:18px}.ph .ey{font-family:var(--fd);font-size:.6rem;color:var(--mut);text-transform:uppercase;letter-spacing:.08em;margin-bottom:2px}.ph h1{font-family:var(--fd);font-size:1.1rem;font-weight:600}.ph p{color:var(--mut);font-size:.72rem;margin-top:1px}
|
||||||
|
.ph-row{display:flex;align-items:center;justify-content:space-between}
|
||||||
|
|
||||||
|
/* Metrics */
|
||||||
|
.mg{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-bottom:18px}
|
||||||
|
.mc{background:var(--sfc);border:1px solid var(--brd);border-radius:var(--rad);padding:14px 16px;transition:border-color .15s}.mc:hover{border-color:rgba(248,250,252,.1)}
|
||||||
|
.mc .l{font-family:var(--fd);font-size:.6rem;color:var(--mut);text-transform:uppercase;letter-spacing:.05em;margin-bottom:4px}.mc .v{font-family:var(--fd);font-size:1.6rem;font-weight:600}.mc .s{font-size:.66rem;color:var(--mut);margin-top:2px}
|
||||||
|
.mc.ac .v{color:var(--acc)}.mc.gn .v{color:var(--grn)}.mc.am .v{color:var(--amb)}
|
||||||
|
|
||||||
|
/* Status Board */
|
||||||
|
.stl{font-family:var(--fd);font-size:.68rem;font-weight:600;color:var(--mut);text-transform:uppercase;letter-spacing:.05em;margin-bottom:8px}
|
||||||
|
.sgr{display:flex;flex-direction:column;gap:6px;margin-bottom:16px}
|
||||||
|
.sgi{background:var(--sfc);border:1px solid var(--brd);border-radius:var(--rad);overflow:hidden}
|
||||||
|
.sgh{display:flex;align-items:center;gap:8px;padding:8px 14px;border-bottom:1px solid var(--brd);font-family:var(--fd);font-size:.66rem;font-weight:500;cursor:pointer;user-select:none}
|
||||||
|
.sdt{width:6px;height:6px;border-radius:50%;flex-shrink:0}.sdt.tf{background:var(--amb);box-shadow:0 0 5px var(--amb)}.sdt.ex{background:var(--acc);box-shadow:0 0 5px var(--ag)}.sdt.mt{background:var(--pur);box-shadow:0 0 5px var(--pur)}.sdt.dk{background:var(--grn);box-shadow:0 0 5px var(--grn)}
|
||||||
|
.scc{color:var(--mut);font-size:.64rem;margin-left:auto}
|
||||||
|
.srw{display:flex;align-items:center;gap:10px;padding:5px 14px 5px 30px;font-size:.74rem;transition:background .1s}.srw:hover{background:rgba(128,128,128,.02)}
|
||||||
|
.srw .an{color:var(--acc);cursor:pointer;flex:1;font-weight:500}.srw .an:hover{text-decoration:underline}
|
||||||
|
.srw .am{color:var(--mut);font-size:.66rem}.srw .al{color:var(--mut);font-size:.66rem;font-family:var(--fd)}
|
||||||
|
|
||||||
|
/* Events */
|
||||||
|
.evl{display:flex;flex-direction:column;gap:3px;margin-bottom:18px}
|
||||||
|
.evr{display:flex;align-items:center;gap:10px;padding:7px 10px;background:var(--sfc);border:1px solid var(--brd);border-radius:var(--rad);font-size:.74rem}
|
||||||
|
.evr .ed{font-family:var(--fd);font-size:.66rem;color:var(--amb);min-width:76px}.evr .eb{font-family:var(--fd);font-size:.6rem;padding:2px 6px;border-radius:3px;background:rgba(234,179,8,.08);color:var(--amb)}
|
||||||
|
|
||||||
|
/* Filter Bar */
|
||||||
|
.fb{display:flex;align-items:center;gap:8px;margin-bottom:12px;flex-wrap:wrap}
|
||||||
|
.fb input[type=text],.fb select{padding:5px 8px;border-radius:4px;background:var(--sfc2);color:var(--txt);border:1px solid var(--brd);font-size:.72rem}
|
||||||
|
.fb input[type=text]{width:180px}.fb input[type=text]:focus,.fb select:focus{outline:none;border-color:var(--acc)}
|
||||||
|
.fb label{display:flex;align-items:center;gap:4px;font-size:.7rem;color:var(--mut);cursor:pointer}
|
||||||
|
|
||||||
|
/* Table */
|
||||||
|
table{width:100%;border-collapse:collapse;font-size:.74rem}
|
||||||
|
th,td{padding:6px 10px;text-align:left;border-bottom:1px solid var(--brd)}
|
||||||
|
th{font-family:var(--fd);font-size:.62rem;font-weight:600;color:var(--mut);text-transform:uppercase;letter-spacing:.04em;cursor:pointer;user-select:none;white-space:nowrap}
|
||||||
|
th:hover{color:var(--txt)}.th-sa{color:var(--acc);margin-left:2px}
|
||||||
|
td{color:var(--txt)}td a{color:var(--acc);text-decoration:none}td a:hover{text-decoration:underline}td.mt{color:var(--mut);font-size:.68rem}
|
||||||
|
tr:hover td{background:rgba(128,128,128,.015)}
|
||||||
|
.act-btn{display:inline-flex;gap:4px;font-size:.68rem}.act-btn a{color:var(--mut);text-decoration:none;padding:2px 5px;border-radius:3px;transition:.1s}.act-btn a:hover{background:var(--brd);color:var(--txt)}
|
||||||
|
|
||||||
|
/* Pagination */
|
||||||
|
.pg-bar{display:flex;align-items:center;gap:3px;margin-top:12px;font-size:.7rem}
|
||||||
|
.pg-bar span,.pg-bar a{padding:3px 7px;border-radius:4px;color:var(--mut);text-decoration:none}.pg-bar a:hover{background:var(--brd);color:var(--txt)}.pg-bar .cur{background:var(--acc);color:#fff}
|
||||||
|
|
||||||
|
/* Timeline */
|
||||||
|
.tl-r{display:flex;align-items:center;gap:8px;padding:5px 8px;margin-bottom:2px;background:var(--sfc);border:1px solid var(--brd);border-radius:var(--rad);font-size:.72rem}
|
||||||
|
.tl-bar{flex:1;height:5px;border-radius:3px;background:var(--brd);position:relative;overflow:hidden}
|
||||||
|
.tl-fl{position:absolute;top:0;height:100%;border-radius:3px;opacity:.75}.tl-fl.tf{background:var(--amb)}.tl-fl.ex{background:var(--acc)}.tl-fl.mt{background:var(--pur)}.tl-fl.dk{background:var(--grn)}
|
||||||
|
.tl-scale{display:flex;margin-bottom:6px;font-size:.58rem;color:var(--mut);text-align:center;border-bottom:1px solid var(--brd);padding-bottom:4px}
|
||||||
|
.tl-scale>div{flex:1}
|
||||||
|
|
||||||
|
/* Fuel Converter */
|
||||||
|
.cvw{display:flex;gap:16px}.cvw form{display:flex;flex-direction:column;gap:8px;width:260px}.cvw form label{font-size:.72rem;color:var(--mut)}.cvw form input,.cvw form select{padding:5px 8px;border-radius:4px;background:var(--sfc2);color:var(--txt);border:1px solid var(--brd)}.cvw .res{flex:1}
|
||||||
|
|
||||||
|
/* CRUD Modal */
|
||||||
|
.modal-bg{position:fixed;inset:0;background:rgba(0,0,0,.55);z-index:100;display:flex;align-items:center;justify-content:center;opacity:0;pointer-events:none;transition:opacity .2s}.modal-bg.open{opacity:1;pointer-events:auto}
|
||||||
|
.modal{background:var(--sfc);border:1px solid var(--brd);border-radius:var(--rad);padding:20px;width:400px;max-height:80vh;overflow-y:auto}
|
||||||
|
.modal h2{font-family:var(--fd);font-size:.85rem;margin-bottom:14px}
|
||||||
|
.modal .fg{display:flex;flex-direction:column;gap:8px;margin-bottom:12px}
|
||||||
|
.modal .fg label{font-size:.72rem;color:var(--mut)}
|
||||||
|
.modal .fg input,.modal .fg select,.modal .fg textarea{padding:6px 8px;border-radius:4px;background:var(--bg);color:var(--txt);border:1px solid var(--brd);font-size:.74rem}
|
||||||
|
.modal .fg textarea{min-height:60px;resize:vertical}
|
||||||
|
.modal .fg input:focus,.modal .fg select:focus,.modal .fg textarea:focus{outline:none;border-color:var(--acc)}
|
||||||
|
.modal .btn-row{display:flex;gap:6px;justify-content:flex-end}
|
||||||
|
.modal .btn-row .b:first-child{margin-right:auto}
|
||||||
|
|
||||||
|
/* Location Board */
|
||||||
|
#map-ct{flex:1;position:relative;display:flex;align-items:center;justify-content:center;background:var(--bg);cursor:grab;overflow:hidden}
|
||||||
|
#map-cv{display:block}
|
||||||
|
#bk-btn{position:absolute;top:10px;left:10px;z-index:5;display:none;background:var(--sfc);border:1px solid var(--brd);color:var(--txt);padding:4px 12px;border-radius:var(--rad);cursor:pointer;font-size:.74rem}#bk-btn.vis{display:block}
|
||||||
|
#scope-ind{position:absolute;top:10px;right:10px;z-index:5;background:rgba(19,19,24,.85);border:1px solid var(--brd);color:var(--mut);padding:3px 10px;border-radius:4px;font-size:.68rem;font-family:var(--fd)}
|
||||||
|
#map-info{width:280px;background:var(--sfc);border-left:1px solid var(--brd);padding:16px;overflow-y:auto;display:flex;flex-direction:column;gap:14px;flex-shrink:0}
|
||||||
|
#map-info h2{font-family:var(--fd);font-size:.85rem;font-weight:600;padding-bottom:5px;border-bottom:1px solid var(--brd)}
|
||||||
|
.mi-sec h3{font-family:var(--fd);font-size:.6rem;text-transform:uppercase;color:var(--mut);letter-spacing:.05em;margin-bottom:3px}
|
||||||
|
.mi-val{font-family:var(--fd);font-size:1.3rem;font-weight:600;color:var(--acc)}
|
||||||
|
.mi-arow{display:flex;justify-content:space-between;align-items:center;padding:4px 0;border-bottom:1px solid var(--brd);font-size:.76rem}.mi-arow .n{color:var(--acc);cursor:pointer}.mi-arow .n:hover{text-decoration:underline}.mi-arow .loc{color:var(--mut);font-size:.68rem;font-family:var(--fd)}
|
||||||
|
|
||||||
|
#tl-layout{display:flex;flex:1;overflow:hidden}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="tb">
|
||||||
|
<div class="br"><b>KSP</b> 管理后台</div>
|
||||||
|
<div class="tb-sp"></div>
|
||||||
|
<div class="tb-time"><span class="l">Sim Time</span><span id="td" onclick="openTM()">2060-08-11 10:00</span></div>
|
||||||
|
<button class="b" onclick="shT(-1)">−1d</button><button class="b" onclick="shT(1)">+1d</button><button class="b" onclick="shT(7)">+7d</button><button class="b" onclick="rsT()">Today</button>
|
||||||
|
<button class="b" onclick="toggleTheme()" title="Toggle theme" style="margin-left:4px">◐</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="ly">
|
||||||
|
<nav id="sb">
|
||||||
|
<a class="ni act" data-pg="overview" href="#"><span class="ic">◉</span>任务总览</a>
|
||||||
|
<div class="nst" onclick="this.classList.toggle('on')"><span class="ar">▾</span>任务运营</div>
|
||||||
|
<div class="niw">
|
||||||
|
<a class="ni su" data-pg="assets" href="#"><span class="ic">◆</span>任务资产</a>
|
||||||
|
<a class="ni su" data-pg="missions" href="#"><span class="ic">◈</span>任务列表</a>
|
||||||
|
<a class="ni su" data-pg="timeline" href="#"><span class="ic">◫</span>时间线</a>
|
||||||
|
<a class="ni su" data-pg="location" href="#"><span class="ic">◎</span>地点板</a>
|
||||||
|
</div>
|
||||||
|
<div class="nst" onclick="this.classList.toggle('on')"><span class="ar">▾</span>数据中心</div>
|
||||||
|
<div class="niw">
|
||||||
|
<a class="ni su" data-pg="engines" href="#"><span class="ic">⚙</span>引擎目录</a>
|
||||||
|
<a class="ni su" data-pg="comms" href="#"><span class="ic">◉</span>通信部件</a>
|
||||||
|
<a class="ni su" data-pg="tanks" href="#"><span class="ic">⬡</span>燃料箱规格</a>
|
||||||
|
<a class="ni su" data-pg="vehicles" href="#"><span class="ic">⇧</span>载具成本</a>
|
||||||
|
</div>
|
||||||
|
<a class="ni" data-pg="converter" href="#"><span class="ic">⇄</span>燃料换算</a>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<main id="ct">
|
||||||
|
<!-- ═══ OVERVIEW ═══ -->
|
||||||
|
<div class="pg act" id="pg-overview">
|
||||||
|
<div class="ph"><div class="ey">Mission Operations</div><h1>任务总览</h1><p>Simulation: <strong id="ht"></strong> · All metrics derived from asset logs</p></div>
|
||||||
|
<div class="mg" id="metrics"></div>
|
||||||
|
<div class="stl">Status Board</div><div class="sgr" id="sgr"></div>
|
||||||
|
<div class="stl">Upcoming Events</div><div class="evl" id="evl"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══ ASSETS ═══ -->
|
||||||
|
<div class="pg" id="pg-assets">
|
||||||
|
<div class="ph-row"><div class="ph"><div class="ey">Mission Operations</div><h1>任务资产</h1></div><div><button class="b ac" onclick="openCRUD('asset')">+ New Asset</button></div></div>
|
||||||
|
<div class="fb"><input type="text" id="aq" placeholder="Search..." oninput="rA()"><select id="at" onchange="rA()"><option value="">All Types</option><option>Exploration Mothership</option><option>Space Shuttle</option><option>Station</option><option>Base</option><option>Cargo Ship</option></select><select id="al" onchange="rA()"><option value="">All Locations</option><option>Earth System</option><option>Mars System</option><option>Jupiter System</option><option>Transfer</option></select><label><input type="checkbox" id="ah" onchange="rA()" checked> Hide retired</label></div>
|
||||||
|
<table id="atbl"><thead><tr>
|
||||||
|
<th onclick="sA('n')">Name <span class="th-sa"></span></th>
|
||||||
|
<th onclick="sA('t')">Type <span class="th-sa"></span></th>
|
||||||
|
<th onclick="sA('l')">Location <span class="th-sa"></span></th>
|
||||||
|
<th>Current Event</th><th onclick="sA('c')">Entries <span class="th-sa"></span></th><th>Action</th>
|
||||||
|
</tr></thead><tbody></tbody></table>
|
||||||
|
<div class="pg-bar" id="apg"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══ ASSET DETAIL ═══ -->
|
||||||
|
<div class="pg" id="pg-asset-detail">
|
||||||
|
<div class="ph-row"><div class="ph"><div class="ey">Mission Asset</div><h1 id="ad-name">—</h1><p id="ad-sub"></p></div><div style="display:flex;gap:6px"><button class="b" onclick="openCRUD('asset',adIdx)">Edit Asset</button><button class="b" onclick="swPg('assets')">← Back</button></div></div>
|
||||||
|
<div style="display:grid;grid-template-columns:1fr 280px;gap:16px">
|
||||||
|
<div>
|
||||||
|
<!-- Info cards -->
|
||||||
|
<div class="mg" style="grid-template-columns:repeat(4,1fr);margin-bottom:12px">
|
||||||
|
<div class="mc"><div class="l">Type</div><div class="v" id="ad-type" style="font-size:.95rem">—</div></div>
|
||||||
|
<div class="mc"><div class="l">Location</div><div class="v" id="ad-loc" style="font-size:.95rem">—</div></div>
|
||||||
|
<div class="mc"><div class="l">Mission</div><div class="v" id="ad-miss" style="font-size:.85rem">—</div></div>
|
||||||
|
<div class="mc"><div class="l">Entries</div><div class="v" id="ad-entries" style="font-size:.95rem">0</div></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Docking section: target + vehicles -->
|
||||||
|
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:14px">
|
||||||
|
<div>
|
||||||
|
<div class="stl">Docking Target</div>
|
||||||
|
<div id="ad-target" style="background:var(--sfc);border:1px solid var(--brd);border-radius:var(--rad);padding:10px 12px;font-size:.78rem"></div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="stl" style="display:flex;align-items:center;justify-content:space-between">
|
||||||
|
<span>Docked Vehicles</span>
|
||||||
|
<button class="b sm ac" onclick="openDockModal()" style="font-size:.64rem">+ Dock</button>
|
||||||
|
</div>
|
||||||
|
<div id="ad-docked" style="background:var(--sfc);border:1px solid var(--brd);border-radius:var(--rad);padding:6px 0;font-size:.76rem;max-height:120px;overflow-y:auto"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Log entries -->
|
||||||
|
<div class="stl" style="display:flex;align-items:center;justify-content:space-between">
|
||||||
|
<span>Log Entries</span>
|
||||||
|
<div style="display:flex;gap:4px">
|
||||||
|
<button class="b sm ac" onclick="openNewLog('state')">+ State Interval</button>
|
||||||
|
<button class="b sm" onclick="openNewLog('event')">+ Event Point</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="fb"><input type="text" id="ad-lq" placeholder="Search logs..." oninput="rADLogs()"><select id="ad-lf" onchange="rADLogs()"><option value="">All Types</option><option value="state">State Interval</option><option value="event">Event Point</option></select></div>
|
||||||
|
<table><thead><tr><th>Date</th><th>Type</th><th>Title</th><th>State</th><th>Location</th><th>Action</th></tr></thead><tbody id="ad-log-tbl"></tbody></table>
|
||||||
|
<div class="pg-bar" id="ad-log-pg"></div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<!-- Upcoming events -->
|
||||||
|
<div class="stl">Upcoming Events</div>
|
||||||
|
<div class="evl" id="ad-events" style="margin-bottom:14px"></div>
|
||||||
|
<!-- Quick info -->
|
||||||
|
<div class="stl">Quick Info</div>
|
||||||
|
<div style="background:var(--sfc);border:1px solid var(--brd);border-radius:var(--rad);padding:10px 12px;font-size:.72rem;color:var(--mut)">
|
||||||
|
<div style="margin-bottom:4px"><strong style="color:var(--txt)">Retired:</strong> <span id="ad-retired">No</span></div>
|
||||||
|
<div style="margin-bottom:4px"><strong style="color:var(--txt)">Created:</strong> <span id="ad-created">—</span></div>
|
||||||
|
<div><strong style="color:var(--txt)">Updated:</strong> <span id="ad-updated">—</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══ MISSIONS ═══ -->
|
||||||
|
<div class="pg" id="pg-missions">
|
||||||
|
<div class="ph"><div class="ey">Mission Operations</div><h1>任务列表</h1></div>
|
||||||
|
<div class="fb"><input type="text" id="mq" placeholder="Search..." oninput="rM()"><select id="ml" onchange="rM()"><option value="">All Locations</option><option>Earth System</option><option>Mars System</option><option>Jupiter System</option><option>Transfer</option></select></div>
|
||||||
|
<div class="sgr" id="msg"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══ TIMELINE ═══ -->
|
||||||
|
<div class="pg" id="pg-timeline">
|
||||||
|
<div class="ph"><div class="ey">Mission Operations</div><h1>时间线</h1></div>
|
||||||
|
<!-- Filter bar row 1: search + presets + scale -->
|
||||||
|
<div class="fb"><input type="text" id="tl-q" placeholder="Search assets..." oninput="rT()" style="width:160px">
|
||||||
|
<div style="display:flex;gap:3px;margin-left:4px">
|
||||||
|
<button class="b sm tl-pre" data-f="active" onclick="tlPreset('active')">Active</button>
|
||||||
|
<button class="b sm tl-pre" data-f="month" onclick="tlPreset('month')">This Month</button>
|
||||||
|
<button class="b sm tl-pre" data-f="90d" onclick="tlPreset('90d')">Next 90d</button>
|
||||||
|
<button class="b sm tl-pre" data-f="all" onclick="tlPreset('all')">All</button>
|
||||||
|
</div>
|
||||||
|
<div style="margin-left:auto;display:flex;gap:4px;align-items:center">
|
||||||
|
<label style="font-size:.68rem;color:var(--mut)">Scale:</label>
|
||||||
|
<select id="tls" onchange="rT()" style="padding:3px 6px;border-radius:4px;background:var(--sfc2);color:var(--txt);border:1px solid var(--brd);font-size:.68rem"><option value="year">Year</option><option value="month" selected>Month</option><option value="day">Day</option></select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Filter bar row 2: Type + Location + State + Date range -->
|
||||||
|
<div class="fb">
|
||||||
|
<label style="font-size:.68rem;color:var(--mut)">Type:</label><select id="tl-type" onchange="rT()" style="padding:3px 6px;border-radius:4px;background:var(--sfc2);color:var(--txt);border:1px solid var(--brd);font-size:.68rem"><option value="">All</option><option>Exploration Mothership</option><option>Space Shuttle</option><option>Station</option><option>Base</option><option>Cargo Ship</option></select>
|
||||||
|
<label style="font-size:.68rem;color:var(--mut)">Location:</label><select id="tl-loc" onchange="rT()" style="padding:3px 6px;border-radius:4px;background:var(--sfc2);color:var(--txt);border:1px solid var(--brd);font-size:.68rem"><option value="">All</option><option>Earth System</option><option>Mars System</option><option>Jupiter System</option><option>Transfer</option></select>
|
||||||
|
<label style="font-size:.68rem;color:var(--mut)">State:</label><select id="tl-state" onchange="rT()" style="padding:3px 6px;border-radius:4px;background:var(--sfc2);color:var(--txt);border:1px solid var(--brd);font-size:.68rem"><option value="">All</option><option value="transfer">Transfer</option><option value="exploration">Exploration</option><option value="maintenance">Maintenance</option><option value="docked">Docked</option></select>
|
||||||
|
<div style="margin-left:auto;display:flex;gap:4px;align-items:center">
|
||||||
|
<label style="font-size:.68rem;color:var(--mut)">From:</label><input type="date" id="tl-from" onchange="rT()" style="padding:3px 6px;border-radius:4px;background:var(--sfc2);color:var(--txt);border:1px solid var(--brd);font-size:.68rem">
|
||||||
|
<label style="font-size:.68rem;color:var(--mut)">To:</label><input type="date" id="tl-to" onchange="rT()" style="padding:3px 6px;border-radius:4px;background:var(--sfc2);color:var(--txt);border:1px solid var(--brd);font-size:.68rem">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Asset pill tags -->
|
||||||
|
<div class="fb" id="tl-pills" style="gap:4px"></div>
|
||||||
|
<div id="tlr"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══ LOCATION BOARD ═══ -->
|
||||||
|
<div class="pg loc" id="pg-location">
|
||||||
|
<div id="tl-layout" style="flex:1;display:flex">
|
||||||
|
<div id="map-ct"><canvas id="map-cv"></canvas><button id="bk-btn" onclick="mapGoBack()">← Back</button><span id="scope-ind">Solar System</span></div>
|
||||||
|
<div id="map-info"><h2 id="mi-title">Solar System</h2>
|
||||||
|
<div class="mi-sec"><h3>Scope</h3><div id="mi-scope" style="font-size:.88rem;font-weight:500">—</div></div>
|
||||||
|
<div class="mi-sec"><h3>Assets in Scope</h3><div id="mi-assets"></div></div>
|
||||||
|
<div class="mi-sec"><h3>Ongoing Missions</h3><div class="mi-val" id="mi-miss">0</div></div>
|
||||||
|
<div class="mi-sec"><h3>Upcoming Events</h3><div id="mi-events"></div></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══ ENGINES ═══ -->
|
||||||
|
<div class="pg" id="pg-engines">
|
||||||
|
<div class="ph-row"><div class="ph"><div class="ey">Reference Data</div><h1>引擎目录</h1></div><div><button class="b ac" onclick="openCRUD('engine')">+ New Engine</button></div></div>
|
||||||
|
<div class="fb"><input type="text" id="eq" placeholder="Search..." oninput="rE()"><select id="ef" onchange="rE()"><option value="">All Fuel</option><option>LF/O</option><option>Nuclear</option><option>Electric</option></select></div>
|
||||||
|
<table><thead><tr><th onclick="sortT('eq-tbl',0)">Name</th><th onclick="sortT('eq-tbl',1)">Cycle</th><th onclick="sortT('eq-tbl',2)">Mass</th><th onclick="sortT('eq-tbl',3)">Vac Thrust</th><th onclick="sortT('eq-tbl',4)">Vac ISP</th><th>Fuel</th><th>Mod</th><th>Action</th></tr></thead><tbody id="eq-tbl"></tbody></table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══ COMMS ═══ -->
|
||||||
|
<div class="pg" id="pg-comms">
|
||||||
|
<div class="ph-row"><div class="ph"><div class="ey">Reference Data</div><h1>通信部件</h1></div><div><button class="b ac" onclick="openCRUD('comm')">+ New</button></div></div>
|
||||||
|
<table><thead><tr><th onclick="sortT('cm-tbl',0)">Name</th><th onclick="sortT('cm-tbl',1)">Range</th><th onclick="sortT('cm-tbl',2)">Mass</th><th>Type</th><th>Source</th><th>Action</th></tr></thead><tbody id="cm-tbl"></tbody></table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══ TANKS ═══ -->
|
||||||
|
<div class="pg" id="pg-tanks">
|
||||||
|
<div class="ph-row"><div class="ph"><div class="ey">Reference Data</div><h1>燃料箱规格</h1></div><div><button class="b ac" onclick="openCRUD('tank')">+ New</button></div></div>
|
||||||
|
<table><thead><tr><th onclick="sortT('tk-tbl',0)">Name</th><th onclick="sortT('tk-tbl',1)">Volume</th><th onclick="sortT('tk-tbl',2)">Dry Mass</th><th>Fuel</th><th>Source</th><th>Action</th></tr></thead><tbody id="tk-tbl"></tbody></table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══ VEHICLES ═══ -->
|
||||||
|
<div class="pg" id="pg-vehicles">
|
||||||
|
<div class="ph-row"><div class="ph"><div class="ey">Reference Data</div><h1>载具成本</h1></div><div><button class="b ac" onclick="openCRUD('vehicle')">+ New</button></div></div>
|
||||||
|
<table><thead><tr><th onclick="sortT('vh-tbl',0)">Name</th><th onclick="sortT('vh-tbl',1)">Launch Cost</th><th>Source</th><th>Action</th></tr></thead><tbody id="vh-tbl"></tbody></table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══ FUEL CONVERTER ═══ -->
|
||||||
|
<div class="pg" id="pg-converter">
|
||||||
|
<div class="ph"><div class="ey">Tools</div><h1>燃料换算</h1></div>
|
||||||
|
<div class="cvw"><form id="cvf"><label>Value<input type="number" id="cvv" value="1000" step="1"></label><label>Mode<select id="cvm"><option value="L2t">Liters → Tons</option><option value="t2L">Tons → Liters</option></select></label><label>Fuel<select id="cvf-sel"></select></label><button type="submit" class="b ac">Convert</button></form><div class="res"><table><thead><tr><th>Fuel</th><th>Density (kg/L)</th><th>Result</th></tr></thead><tbody id="cvr"></tbody></table></div></div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══ MODALS ═══ -->
|
||||||
|
<div class="modal-bg" id="tmodal"><div class="modal"><h2>Set Simulation Time</h2><div class="fg"><label>Time<input type="datetime-local" id="tminp" step="60"></label></div><div class="btn-row"><button class="b" onclick="clTM()">Cancel</button><button class="b ac" onclick="apT()">Apply</button></div></div></div>
|
||||||
|
<div class="modal-bg" id="crud-modal"><div class="modal"><h2 id="crud-title">New Entry</h2><div class="fg" id="crud-fields"></div><div class="btn-row"><button class="b dg" id="crud-del" style="display:none" onclick="crudDel()">Delete</button><button class="b" onclick="clCRUD()">Cancel</button><button class="b ac" onclick="crudSave()">Save</button></div></div></div>
|
||||||
|
<div class="modal-bg" id="dock-modal"><div class="modal"><h2>Dock Vehicle</h2><div class="fg"><label>Vehicle<select id="dock-vehicle"><option value="">Select existing asset...</option><option>回声号</option><option>秃鹫 VS-01</option><option>擎天号</option><option value="_custom">Custom name...</option></select></label><input type="text" id="dock-custom" placeholder="Enter vehicle name..." style="display:none"><label>Note<textarea id="dock-note" rows="2" placeholder="Optional"></textarea></label></div><div class="btn-row"><button class="b" onclick="clDock()">Cancel</button><button class="b ac" onclick="saveDock()">Dock</button></div></div></div>
|
||||||
|
<div class="modal-bg" id="log-modal"><div class="modal" style="width:420px;max-height:80vh;overflow-y:auto"><h2 id="log-modal-title">New Log Entry</h2><div class="fg"><input type="hidden" id="log-type" value="state"><label>Title<input type="text" id="log-title" placeholder="Entry title"></label><div id="log-end-wrap"><label>Start At<input type="datetime-local" id="log-start" step="60"></label><label>End At<input type="datetime-local" id="log-end" step="60"></label></div><div id="log-state-wrap"><label>State<select id="log-state"><option value="">—</option><option>Transfer</option><option>Exploration</option><option>Maintenance</option><option>Docked</option><option>In Transit</option></select></label></div><label>Location<input type="text" id="log-location" placeholder="LEO / Mars Orbit / etc."></label><label>Summary<textarea id="log-summary" rows="2" placeholder="Optional"></textarea></label><div id="log-nodes-wrap"><hr style="border:none;border-top:1px solid var(--brd);margin:6px 0"><div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:4px"><span style="font-size:.7rem;color:var(--mut)">State Nodes (0-N)</span><button class="b sm" type="button" onclick="addNodeRow()" style="font-size:.62rem">+ Add Node</button></div><div id="nodes-list"></div></div></div><div class="btn-row"><button class="b" onclick="clLog()">Cancel</button><button class="b ac" onclick="saveLog()">Save</button></div></div></div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// ═══ STATE ═══
|
||||||
|
const ST=new Date('2060-08-11T10:00:00Z');
|
||||||
|
let cPg='overview',asS={k:'n',d:1};
|
||||||
|
let mapVS=[{tp:'solar',ck:'sun'}],mapHv=null,mapDg=false,mapDS=null,mapDL=null,mapDD=false,mapVO={x:0,y:0},mapZm=1;
|
||||||
|
|
||||||
|
const ASSETS=[
|
||||||
|
{n:'ST-01 万星源号',t:'Exploration Mothership',l:'Transfer',s:'transfer',m:'OSSEM-1 → Neptune',e:'Arrival 2060-07-25',c:12,r:false},
|
||||||
|
{n:'ST-02 克里斯滕号',t:'Exploration Mothership',l:'Transfer',s:'transfer',m:'EE-2 → Earth',e:'Arrival 2060-08-26',c:14,r:false},
|
||||||
|
{n:'ST-03 Muelsyse',t:'Exploration Mothership',l:'LEO',s:'maintenance',m:'Post-Trial Maintenance',e:'Ends 2060-10-25',c:8,r:false},
|
||||||
|
{n:'XH-01 羲和号',t:'Exploration Mothership',l:'Jupiter System',s:'exploration',m:'Europa Expedition 3',e:'On station',c:22,r:false},
|
||||||
|
{n:'XH-02 太白号',t:'Exploration Mothership',l:'Mars Orbit',s:'docked',m:'Mars Expedition 4',e:'Surface ops',c:16,r:false},
|
||||||
|
{n:'XH-03 常曦号',t:'Exploration Mothership',l:'LEO',s:'maintenance',m:'Commissioning',e:'Ends 2060-06-10',c:5,r:false},
|
||||||
|
{n:'JW-01 金乌号',t:'Cargo Ship',l:'LEO',s:'docked',m:'Station Logistics',e:'Resupply',c:6,r:false},
|
||||||
|
{n:'星港空间站',t:'Station',l:'LEO',s:'docked',m:'Hub Operations',e:'XH-03 docked',c:18,r:false},
|
||||||
|
{n:'火星一号基地',t:'Base',l:'Mars Surface',s:'exploration',m:'Habitation',e:'XH-02 crew',c:8,r:false},
|
||||||
|
{n:'欧罗巴前哨站',t:'Base',l:'Europa Surface',s:'exploration',m:'Ice Drilling',e:'XH-01 crew',c:5,r:false},
|
||||||
|
{n:'回声号',t:'Space Shuttle',l:'LEO',s:'docked',m:'Station Ferry',e:'Next flight',c:3,r:false},
|
||||||
|
{n:'秃鹫 VS-01',t:'Space Shuttle',l:'Earth Surface',s:'maintenance',m:'Overhaul',e:'Retired',c:180,r:true},
|
||||||
|
{n:'擎天号',t:'Cargo Ship',l:'LEO',s:'docked',m:'Cargo Run',e:'Undock 2060-09',c:4,r:false},
|
||||||
|
{n:'凌霄空间站',t:'Station',l:'LEO',s:'maintenance',m:'Decommissioning',e:'Crew transfer',c:10,r:true},
|
||||||
|
];
|
||||||
|
const EVENTS=[{d:'2060-06-10',t:'XH-03 Maintenance Complete'},{d:'2060-06-15',t:'ST-03 Deep Space Test Begins'},{d:'2060-06-28',t:'ST-02 Returns to Earth'},{d:'2060-07-25',t:'ST-01 Arrives at Neptune'},{d:'2060-10-25',t:'ST-03 Commissioning Ends'},{d:'2060-09-01',t:'ST-02 Upgrade Begins'}];
|
||||||
|
const MISSIONS=[{n:'OSSEM-1',a:'ST-01',l:'Transfer',d:'Neptune',st:'2060-02-11',et:'—',s:'Active'},{n:'EE-2 Return',a:'ST-02',l:'Transfer',d:'Earth',st:'2060-06-28',et:'2060-08-26',s:'Active'},{n:'Post-Trial',a:'ST-03',l:'LEO',d:'LEO',st:'2060-03-07',et:'2060-10-25',s:'Active'},{n:'Europa Exp 3',a:'XH-01',l:'Jupiter',d:'Europa',st:'2060-02-01',et:'—',s:'Active'},{n:'Mars Exp 4',a:'XH-02',l:'Mars Orbit',d:'Mars Surface',st:'2059-06-01',et:'—',s:'Active'},{n:'Commissioning',a:'XH-03',l:'LEO',d:'LEO',st:'2059-12-09',et:'2060-06-10',s:'Active'},{n:'Station Ops',a:'星港',l:'LEO',d:'LEO',st:'2055-01-01',et:'—',s:'Active'},{n:'Habitation',a:'火星一号',l:'Mars Surface',d:'Mars',st:'2055-06-01',et:'—',s:'Active'}];
|
||||||
|
let ENGINES=[{n:'KS-25 Vector',cy:'Staged',m:4.0,vt:1000,vi:315,f:'LF/O',md:'Stock'},{n:'RE-L10 Poodle',cy:'Expander',m:1.75,vt:250,vi:350,f:'LF/O',md:'Stock'},{n:'LV-N Nerv',cy:'NTR',m:3.0,vt:60,vi:800,f:'Nuclear',md:'Stock'},{n:'IX-6315 Dawn',cy:'Ion',m:0.25,vt:2,vi:4200,f:'Electric',md:'Stock'},{n:'RE-M3 Mainsail',cy:'Staged',m:6.0,vt:1500,vi:310,f:'LF/O',md:'Stock'},{n:'CR-7 RAPIER',cy:'Combined',m:2.0,vt:180,vi:320,f:'LF/O',md:'Stock'}];
|
||||||
|
let COMMS=[{n:'Communotron 16',rng:'500M',mass:0.015,tp:'Direct',src:'Stock'},{n:'HG-5 High Gain',rng:'5M',mass:0.075,tp:'Relay',src:'Stock'},{n:'RA-2 Relay',rng:'2G',mass:0.15,tp:'Relay',src:'Stock'},{n:'RA-15 Relay',rng:'15G',mass:0.3,tp:'Relay',src:'Stock'}];
|
||||||
|
let TANKS=[{n:'Oscar-B',vol:40,mass:0.025,f:'LF/O',src:'Stock'},{n:'FL-T400',vol:400,mass:0.25,f:'LF/O',src:'Stock'},{n:'FL-T800',vol:800,mass:0.5,f:'LF/O',src:'Stock'},{n:'Jumbo-64',vol:6400,mass:4.0,f:'LF/O',src:'Stock'}];
|
||||||
|
let VEHICLES=[{n:'CERV Shuttle',cost:45000,src:'KSP'},{n:'Vulture B1',cost:38000,src:'KSP'},{n:'Echo Shuttle',cost:62000,src:'KSP'},{n:'Enterprise',cost:75000,src:'KSP'},{n:'Qingtian Cargo',cost:28000,src:'KSP'}];
|
||||||
|
const FUELS=[{n:'LF',d:.005},{n:'Oxidizer',d:.005},{n:'LF/O Mixed',d:.005},{n:'Monopropellant',d:.004},{n:'XenonGas',d:.0001},{n:'LH2',d:.00007085}];
|
||||||
|
|
||||||
|
const CRUD_DEFS={
|
||||||
|
asset:{title:'Asset',fields:[{k:'n',l:'Name'},{k:'t',l:'Type',type:'select',opts:['Exploration Mothership','Space Shuttle','Station','Base','Cargo Ship']},{k:'l',l:'Location'},{k:'s',l:'State',type:'select',opts:['transfer','exploration','maintenance','docked']},{k:'m',l:'Mission'},{k:'e',l:'Event'}],store:ASSETS},
|
||||||
|
engine:{title:'Engine',fields:[{k:'n',l:'Name'},{k:'cy',l:'Cycle'},{k:'m',l:'Mass (t)',type:'number'},{k:'vt',l:'Vac Thrust (kN)',type:'number'},{k:'vi',l:'Vac ISP (s)',type:'number'},{k:'f',l:'Fuel',type:'select',opts:['LF/O','Nuclear','Electric']},{k:'md',l:'Mod'}],store:ENGINES},
|
||||||
|
comm:{title:'Communication',fields:[{k:'n',l:'Name'},{k:'rng',l:'Range'},{k:'mass',l:'Mass (t)',type:'number'},{k:'tp',l:'Type',type:'select',opts:['Direct','Relay']},{k:'src',l:'Source'}],store:COMMS},
|
||||||
|
tank:{title:'Tank',fields:[{k:'n',l:'Name'},{k:'vol',l:'Volume (L)',type:'number'},{k:'mass',l:'Dry Mass (t)',type:'number'},{k:'f',l:'Fuel'},{k:'src',l:'Source'}],store:TANKS},
|
||||||
|
vehicle:{title:'Vehicle',fields:[{k:'n',l:'Name'},{k:'cost',l:'Launch Cost',type:'number'},{k:'src',l:'Source'}],store:VEHICLES},
|
||||||
|
};
|
||||||
|
|
||||||
|
// ═══ HELPERS ═══
|
||||||
|
function fm(d){return`${d.getUTCFullYear()}-${String(d.getUTCMonth()+1).padStart(2,'0')}-${String(d.getUTCDate()).padStart(2,'0')} ${String(d.getUTCHours()).padStart(2,'0')}:${String(d.getUTCMinutes()).padStart(2,'0')}`}
|
||||||
|
function uT(){const s=fm(ST);$('td').textContent=s;$('ht').textContent=s}
|
||||||
|
|
||||||
|
// ═══ NAV ═══
|
||||||
|
function $(s){return document.querySelector(s.startsWith('#')||s.startsWith('.')||s.startsWith('[')?s:'#'+s)}
|
||||||
|
function swPg(p){
|
||||||
|
document.querySelectorAll('.pg').forEach(e=>e.classList.remove('act'));
|
||||||
|
document.querySelectorAll('.ni').forEach(e=>e.classList.remove('act'));
|
||||||
|
const pg=$(`#pg-${p}`);if(pg)pg.classList.add('act');
|
||||||
|
const nav=$(`[data-pg="${p}"]`);if(nav)nav.classList.add('act');
|
||||||
|
cPg=p;
|
||||||
|
if(p==='overview'){rO();rEts()}else if(p==='assets')rA();else if(p==='asset-detail'&&adIdx>=0){}else if(p==='missions')rM();else if(p==='timeline'){rT();initTlPills()}else if(p==='engines')rE();else if(p==='comms')rCm();else if(p==='tanks')rTk();else if(p==='vehicles')rVh();else if(p==='converter')rCv();
|
||||||
|
if(p==='location'){initMap();setTimeout(()=>rMapInfo(),100)}
|
||||||
|
}
|
||||||
|
document.querySelectorAll('.ni').forEach(e=>{e.addEventListener('click',function(ev){ev.preventDefault();swPg(this.dataset.pg)})});
|
||||||
|
|
||||||
|
// ═══ OVERVIEW ═══
|
||||||
|
function rO(){
|
||||||
|
const act=ASSETS.filter(a=>!a.r).length,ms=MISSIONS.filter(m=>m.s==='Active').length,up=EVENTS.filter(e=>e.d>=ST.toISOString().slice(0,10)).length;
|
||||||
|
$('metrics').innerHTML=`<div class="mc ac"><div class="l">Tracked Assets</div><div class="v">${act}</div><div class="s">6 Motherships · 2 Stations · 2 Cargo · 2 Shuttles</div></div><div class="mc gn"><div class="l">Active Missions</div><div class="v">${ms}</div><div class="s">2 Deep Space · 1 Transfer · 2 Orbit · 1 Surface</div></div><div class="mc am"><div class="l">Upcoming (30d)</div><div class="v">${up}</div><div class="s">${up>0?EVENTS.filter(e=>e.d>=ST.toISOString().slice(0,10))[0].t:'None imminent'}</div></div>`;
|
||||||
|
const gr={transfer:{l:'In Transit',c:'tf',i:[]},exploration:{l:'Exploration',c:'ex',i:[]},maintenance:{l:'Maintenance',c:'mt',i:[]},docked:{l:'Docked / Surface',c:'dk',i:[]}};
|
||||||
|
for(const a of ASSETS){if(a.r)continue;gr[a.s].i.push(a)}
|
||||||
|
$('sgr').innerHTML=Object.values(gr).filter(g=>g.i.length).map(g=>`<div class="sgi"><div class="sgh"><span class="sdt ${g.c}"></span>${g.l}<span class="scc">${g.i.length}</span></div>${g.i.map(a=>`<div class="srw"><span class="an">${a.n}</span><span class="am">${a.m}</span><span class="al">${a.l}</span></div>`).join('')}</div>`).join('');
|
||||||
|
}
|
||||||
|
function rEts(){$('evl').innerHTML=EVENTS.filter(e=>e.d>=ST.toISOString().slice(0,10)).slice(0,6).map(e=>`<div class="evr"><span class="ed">${e.d}</span>${e.t}<span class="eb">${new Date(e.d+'T00:00Z')<new Date(ST.getTime()+30*86400000)?'< 30d':''}</span></div>`).join('')}
|
||||||
|
|
||||||
|
// ═══ ASSETS ═══
|
||||||
|
function rA(){
|
||||||
|
const q=($('aq').value||'').toLowerCase(),tp=$('at').value,loc=$('al').value,hd=$('ah').checked;
|
||||||
|
let rows=ASSETS.filter(a=>{if(hd&&a.r)return false;if(tp&&a.t!==tp)return false;if(loc&&!a.l.includes(loc))return false;if(q&&!a.n.toLowerCase().includes(q)&&!a.t.toLowerCase().includes(q))return false;return true});
|
||||||
|
rows.sort((a,b)=>{let va=a[asS.k]||'',vb=b[asS.k]||'';if(typeof va==='string'){va=va.toLowerCase();vb=vb.toLowerCase()}return va>vb?asS.d:va<vb?-asS.d:0});
|
||||||
|
const pp=10,tot=Math.ceil(rows.length/pp),pg=Math.min(Math.max(1,1),tot||1),pgd=rows.slice((pg-1)*pp,pg*pp);
|
||||||
|
$('atbl').querySelector('tbody').innerHTML=pgd.map(a=>`<tr><td><a href="#" onclick="openAssetDetail(${ASSETS.indexOf(a)});return false">${a.n}</a></td><td>${a.t}</td><td class="mt">${a.l}</td><td>${a.e||'—'}</td><td>${a.c}</td><td><div class="act-btn"><a href="#" onclick="openCRUD('asset',${ASSETS.indexOf(a)});return false">Edit</a><a href="#" onclick="delItem(ASSETS,${ASSETS.indexOf(a)});rA();return false">Del</a></div></td></tr>`).join('');
|
||||||
|
}
|
||||||
|
function sA(k){if(asS.k===k)asS.d*=-1;else{asS.k=k;asS.d=1}rA()}
|
||||||
|
|
||||||
|
// ═══ MISSIONS ═══
|
||||||
|
function rM(){
|
||||||
|
const q=($('mq').value||'').toLowerCase(),lc=$('ml').value;
|
||||||
|
let rows=MISSIONS.filter(m=>{if(lc&&!m.l.includes(lc)&&!m.d.includes(lc))return false;if(q&&!m.n.toLowerCase().includes(q)&&!m.a.toLowerCase().includes(q))return false;return true});
|
||||||
|
const g={};for(const m of rows){if(!g[m.l])g[m.l]=[];g[m.l].push(m)}
|
||||||
|
$('msg').innerHTML=Object.entries(g).map(([k,i])=>`<div class="sgi"><div class="sgh">${k}<span class="scc">${i.length}</span></div>${i.map(m=>`<div class="srw"><span class="an">${m.a}: ${m.n}</span><span class="am">${m.st} → ${m.d}</span><span class="al">${m.s}</span></div>`).join('')}</div>`).join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ═══ TIMELINE ═══
|
||||||
|
let tlPresetVal='active',tlPills=[];
|
||||||
|
function tlPreset_set(f){tlPresetVal=f;document.querySelectorAll('.tl-pre').forEach(b=>b.classList.toggle('ac',b.dataset.f===f));rT()}
|
||||||
|
function tlPreset(f){tlPreset_set(f)}
|
||||||
|
function rT(){
|
||||||
|
const sc=$('tls').value,q=($('tl-q').value||'').toLowerCase(),tp=$('tl-type').value,loc=$('tl-loc').value,st=$('tl-state').value;
|
||||||
|
const from=$('tl-from').value,to=$('tl-to').value;
|
||||||
|
// Filter assets
|
||||||
|
let rows=ASSETS.filter(a=>{
|
||||||
|
if(tlPresetVal==='active'&&a.r)return false;
|
||||||
|
if(tp&&a.t!==tp)return false;
|
||||||
|
if(loc&&!a.l.includes(loc))return false;
|
||||||
|
if(st&&a.s!==st)return false;
|
||||||
|
if(q&&!a.n.toLowerCase().includes(q))return false;
|
||||||
|
if(tlPills.length&&!tlPills.some(p=>a.n.toLowerCase().includes(p.toLowerCase())))return false;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
// Render scale
|
||||||
|
const ms=['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
|
||||||
|
let h='<div class="tl-scale">';
|
||||||
|
for(let i=0;i<12;i++)h+=`<div>${ms[i]}</div>`;
|
||||||
|
h+='</div>';
|
||||||
|
for(const a of rows){
|
||||||
|
const w=Math.max(5,20+Math.random()*70);
|
||||||
|
h+=`<div class="tl-r"><span style="width:110px;font-size:.68rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis" title="${a.n}">${a.n}</span><div class="tl-bar"><div class="tl-fl ${a.s}" style="left:${5+Math.random()*25}%;width:${w}%"></div></div></div>`;
|
||||||
|
}
|
||||||
|
$('tlr').innerHTML=h;
|
||||||
|
// Render pills
|
||||||
|
renderTlPills(rows);
|
||||||
|
}
|
||||||
|
function renderTlPills(rows){
|
||||||
|
const all=tlPresetVal==='active'?ASSETS.filter(a=>!a.r):ASSETS;
|
||||||
|
$('tl-pills').innerHTML=all.map(a=>{
|
||||||
|
const sel=tlPills.some(p=>p.toLowerCase()===a.n.toLowerCase());
|
||||||
|
return`<button class="b sm${sel?' ac':''}" onclick="tlTogglePill('${a.n}')" style="font-size:.64rem">${a.n}</button>`;
|
||||||
|
}).join('')+`<button class="b sm" onclick="tlPills=[];rT()" style="font-size:.64rem;color:var(--mut)">Clear</button>`;
|
||||||
|
}
|
||||||
|
function tlTogglePill(name){
|
||||||
|
const idx=tlPills.findIndex(p=>p.toLowerCase()===name.toLowerCase());
|
||||||
|
if(idx>=0)tlPills.splice(idx,1);else tlPills.push(name);
|
||||||
|
rT();
|
||||||
|
}
|
||||||
|
function initTlPills(){
|
||||||
|
$('tl-pills').innerHTML=ASSETS.filter(a=>!a.r).map(a=>
|
||||||
|
`<button class="b sm" onclick="tlTogglePill('${a.n}')" style="font-size:.64rem">${a.n}</button>`
|
||||||
|
).join('')+`<button class="b sm" onclick="tlPills=[];rT()" style="font-size:.64rem;color:var(--mut)">Clear</button>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ═══ ASSET DETAIL ═══
|
||||||
|
let adIdx=-1;
|
||||||
|
const MOCK_LOGS=[
|
||||||
|
{t:'2060-02-11',k:'state',title:'Transfer to Neptune',state:'Transfer',loc:'Solar Orbit',nodes:[
|
||||||
|
{title:'Departed Earth orbit',at:'2060-02-11',detail:'Lightspeed engine burn'},
|
||||||
|
{title:'Mid-course correction',at:'2060-03-15',detail:'Delta-v 12 km/s'}
|
||||||
|
]},
|
||||||
|
{t:'2060-07-25',k:'state',title:'Neptune System Operations',state:'Exploration',loc:'Neptune Orbit',nodes:[]},
|
||||||
|
{t:'2059-11-25',k:'state',title:'Star Port Full Upgrade',state:'Maintenance',loc:'LEO',nodes:[
|
||||||
|
{title:'Central extension installed',at:'2059-12-10',detail:''},
|
||||||
|
{title:'Cryo-sleep chambers deployed',at:'2060-01-15',detail:'12 chambers'}
|
||||||
|
]},
|
||||||
|
{t:'2059-11-24',k:'event',title:'Arrived at Earth from Saturn',state:'—',loc:'LEO',nodes:[]},
|
||||||
|
{t:'2058-06-01',k:'state',title:'Saturn Exploration Mission 1',state:'Exploration',loc:'Saturn System',nodes:[]},
|
||||||
|
{t:'2058-01-05',k:'state',title:'Maintenance and Upgrades',state:'Maintenance',loc:'LEO',nodes:[]},
|
||||||
|
{t:'2057-12-20',k:'event',title:'Returned from Europa',state:'—',loc:'LEO',nodes:[]},
|
||||||
|
{t:'2056-12-15',k:'state',title:'Europa Outpost Construction 1',state:'Exploration',loc:'Jupiter System',nodes:[]},
|
||||||
|
{t:'2056-11-03',k:'event',title:'Returned from Mars',state:'—',loc:'LEO',nodes:[]},
|
||||||
|
{t:'2056-06-02',k:'state',title:'Mars One Construction Mission 2',state:'Exploration',loc:'Mars Orbit',nodes:[]},
|
||||||
|
];
|
||||||
|
// Docking: parent=asset idx this is docked to, vehicles=[{name,since,until}] (until=null=still docked)
|
||||||
|
const DOCK_DATA={
|
||||||
|
0:{parent:-1,vehicles:[]},1:{parent:-1,vehicles:[]},2:{parent:7,vehicles:[]},3:{parent:-1,vehicles:[]},
|
||||||
|
4:{parent:8,vehicles:[{name:'JW-01 金乌号',since:'2059-08-01',until:null}]},
|
||||||
|
5:{parent:7,vehicles:[]},6:{parent:7,vehicles:[]},
|
||||||
|
7:{parent:-1,vehicles:[{name:'XH-03 常曦号',since:'2060-03-07',until:null},{name:'JW-01 金乌号',since:'2060-06-01',until:null},{name:'回声号',since:'2060-01-15',until:null}]},
|
||||||
|
8:{parent:-1,vehicles:[{name:'XH-02 太白号',since:'2059-08-01',until:null}]},
|
||||||
|
9:{parent:-1,vehicles:[{name:'XH-01 羲和号',since:'2060-02-01',until:null}]},
|
||||||
|
10:{parent:7,vehicles:[]},11:{parent:-1,vehicles:[]},12:{parent:7,vehicles:[]},13:{parent:-1,vehicles:[]},
|
||||||
|
};
|
||||||
|
function openAssetDetail(idx){
|
||||||
|
adIdx=idx;const a=ASSETS[idx],dk=DOCK_DATA[idx]||{parent:-1,vehicles:[]};
|
||||||
|
$('ad-name').textContent=a.n;
|
||||||
|
$('ad-sub').textContent=`${a.t} · ${a.r?'Retired':'Active'}`;
|
||||||
|
$('ad-type').textContent=a.t;
|
||||||
|
$('ad-loc').textContent=a.l;
|
||||||
|
$('ad-miss').textContent=a.m;
|
||||||
|
$('ad-entries').textContent=MOCK_LOGS.length;
|
||||||
|
$('ad-retired').textContent=a.r?'Yes':'No';
|
||||||
|
$('ad-created').textContent='2050-03-15';
|
||||||
|
$('ad-updated').textContent='2060-02-10';
|
||||||
|
// Docking target (with undock)
|
||||||
|
if(dk.parent>=0){const p=ASSETS[dk.parent];
|
||||||
|
$('ad-target').innerHTML=`<div style="display:flex;align-items:center;justify-content:space-between;padding:6px 10px"><span><span style="color:var(--acc);cursor:pointer" onclick="openAssetDetail(${dk.parent})">${p.n}</span> <span class="mt">${p.l}</span></span><a href="#" onclick="undockTarget();return false" style="font-size:.68rem;color:var(--red)">分离</a></div>`;
|
||||||
|
}else{
|
||||||
|
$('ad-target').innerHTML='<div style="padding:6px 10px;color:var(--mut);font-size:.72rem">Not docked — <a href="#" onclick="dockToAsset();return false" style="color:var(--accent)">Dock to asset</a></div>';
|
||||||
|
}
|
||||||
|
// Docked vehicles (multiple, each with undock)
|
||||||
|
const active=dk.vehicles.filter(v=>!v.until);
|
||||||
|
$('ad-docked').innerHTML=active.length?active.map(v=>{
|
||||||
|
const ri=dk.vehicles.indexOf(v);
|
||||||
|
return`<div style="display:flex;align-items:center;justify-content:space-between;padding:5px 10px;border-bottom:1px solid var(--brd)"><span><strong style="color:var(--txt)">${v.name}</strong> <span class="mt">since ${v.since}</span></span><a href="#" onclick="undockVehicle(${ri});return false" style="font-size:.68rem;color:var(--red)">分离</a></div>`;
|
||||||
|
}).join(''):'<div style="padding:6px 10px;color:var(--mut);font-size:.72rem">No vehicles docked</div>';
|
||||||
|
// Events
|
||||||
|
const evts=EVENTS.filter(e=>e.d>=ST.toISOString().slice(0,10));
|
||||||
|
$('ad-events').innerHTML=evts.length?evts.map(e=>`<div class="evr"><span class="ed">${e.d}</span>${e.t}</div>`).join(''):'<div style="color:var(--mut);font-size:.72rem;padding:6px 0">No upcoming events</div>';
|
||||||
|
rADLogs();
|
||||||
|
swPg('asset-detail');
|
||||||
|
}
|
||||||
|
function undockTarget(){const dk=DOCK_DATA[adIdx];if(dk)dk.parent=-1;openAssetDetail(adIdx)}
|
||||||
|
function dockToAsset(){openDockModal()}
|
||||||
|
function undockVehicle(i){const dk=DOCK_DATA[adIdx];if(dk&&dk.vehicles[i]){dk.vehicles[i].until=ST.toISOString().slice(0,10);openAssetDetail(adIdx)}}
|
||||||
|
function rADLogs(){
|
||||||
|
const q=($('ad-lq')?.value||'').toLowerCase(),ft=$('ad-lf')?.value||'';
|
||||||
|
let logs=MOCK_LOGS.filter(l=>{
|
||||||
|
if(ft&&l.k!==ft)return false;
|
||||||
|
if(q&&!l.title.toLowerCase().includes(q))return false;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
$('ad-log-tbl').innerHTML=logs.map(l=>{
|
||||||
|
const ri=MOCK_LOGS.indexOf(l);
|
||||||
|
const nodes=l.nodes&&l.nodes.length?l.nodes.map(n=>`<div class="mt" style="font-size:.62rem">→ ${n.title} (${n.at}${n.detail?' · '+n.detail:''})</div>`).join(''):'';
|
||||||
|
return`<tr><td class="mt">${l.t}${l.end?` → ${l.end}`:''}</td><td><span style="font-size:.64rem;padding:2px 5px;border-radius:3px;background:${l.k==='state'?'rgba(59,130,246,.1);color:var(--acc)':'rgba(234,179,8,.08);color:var(--amb)'}">${l.k==='state'?'State':'Event'}</span></td><td>${l.title}${nodes}</td><td class="mt">${l.state}</td><td class="mt">${l.loc}</td><td><div class="act-btn"><a href="#" onclick="openEditLog(${ri});return false" style="font-size:.66rem">Edit</a><a href="#" onclick="delLog(${ri});return false" style="font-size:.66rem;color:var(--red)">Del</a></div></td></tr>`;
|
||||||
|
}).join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ═══ DATA CENTER ═══
|
||||||
|
function rE(){const q=($('eq').value||'').toLowerCase(),ft=$('ef').value;let r=ENGINES.filter(e=>{if(ft&&e.f!==ft)return false;if(q&&!e.n.toLowerCase().includes(q)&&!e.f.toLowerCase().includes(q))return false;return true});$('eq-tbl').innerHTML=r.map((e,i)=>`<tr><td><a href="#">${e.n}</a></td><td>${e.cy}</td><td>${e.m}t</td><td>${e.vt}kN</td><td>${e.vi}s</td><td>${e.f}</td><td>${e.md}</td><td><div class="act-btn"><a href="#" onclick="openCRUD('engine',${i});return false">Edit</a><a href="#" onclick="delItem(ENGINES,${i});rE();return false">Del</a></div></td></tr>`).join('')}
|
||||||
|
function rCm(){$('cm-tbl').innerHTML=COMMS.map((c,i)=>`<tr><td>${c.n}</td><td>${c.rng}</td><td>${c.mass}t</td><td>${c.tp}</td><td>${c.src}</td><td><div class="act-btn"><a href="#" onclick="openCRUD('comm',${i});return false">Edit</a><a href="#" onclick="delItem(COMMS,${i});rCm();return false">Del</a></div></td></tr>`).join('')}
|
||||||
|
function rTk(){$('tk-tbl').innerHTML=TANKS.map((t,i)=>`<tr><td>${t.n}</td><td>${t.vol}L</td><td>${t.mass}t</td><td>${t.f}</td><td>${t.src}</td><td><div class="act-btn"><a href="#" onclick="openCRUD('tank',${i});return false">Edit</a><a href="#" onclick="delItem(TANKS,${i});rTk();return false">Del</a></div></td></tr>`).join('')}
|
||||||
|
function rVh(){$('vh-tbl').innerHTML=VEHICLES.map((v,i)=>`<tr><td>${v.n}</td><td>${v.cost.toLocaleString()}</td><td>${v.src}</td><td><div class="act-btn"><a href="#" onclick="openCRUD('vehicle',${i});return false">Edit</a><a href="#" onclick="delItem(VEHICLES,${i});rVh();return false">Del</a></div></td></tr>`).join('')}
|
||||||
|
|
||||||
|
// ═══ FUEL CONVERTER ═══
|
||||||
|
function rCv(){$('cvf-sel').innerHTML=FUELS.map(f=>`<option>${f.n}</option>`).join('');$('cvr').innerHTML=FUELS.map(f=>`<tr><td>${f.n}</td><td>${f.d}</td><td>—</td></tr>`).join('')}
|
||||||
|
$('cvf').addEventListener('submit',function(e){e.preventDefault();const v=parseFloat($('cvv').value)||0,mode=$('cvm').value;$('cvr').innerHTML=FUELS.map(f=>{const r=mode==='L2t'?(v*f.d/1000).toFixed(4)+' t':(v*1000/f.d).toFixed(0)+' L';return`<tr><td>${f.n}</td><td>${f.d}</td><td style="font-family:var(--fd);color:var(--acc)">${r}</td></tr>`}).join('')});
|
||||||
|
|
||||||
|
// ═══ CRUD ═══
|
||||||
|
let crudType='',crudIdx=-1;
|
||||||
|
function openCRUD(type,idx){
|
||||||
|
crudType=type;crudIdx=idx!==undefined?idx:-1;
|
||||||
|
const def=CRUD_DEFS[type];$('crud-title').textContent=(idx!==undefined?'Edit ':'New ')+def.title;
|
||||||
|
$('crud-del').style.display=idx!==undefined?'inline-flex':'none';
|
||||||
|
$('crud-fields').innerHTML=def.fields.map(f=>{
|
||||||
|
const v=idx!==undefined?(def.store[idx][f.k]||''):'';
|
||||||
|
if(f.type==='select')return`<label>${f.l}<select data-k="${f.k}">${f.opts.map(o=>`<option${o===v?' selected':''}>${o}</option>`).join('')}</select></label>`;
|
||||||
|
if(f.type==='number')return`<label>${f.l}<input type="number" data-k="${f.k}" value="${v}" step="any"></label>`;
|
||||||
|
return`<label>${f.l}<input type="text" data-k="${f.k}" value="${v}"></label>`;
|
||||||
|
}).join('');
|
||||||
|
$('#crud-modal').classList.add('open');
|
||||||
|
}
|
||||||
|
function clCRUD(){$('#crud-modal').classList.remove('open')}
|
||||||
|
function crudSave(){
|
||||||
|
const def=CRUD_DEFS[crudType],obj={};
|
||||||
|
def.fields.forEach(f=>{const el=$(`#crud-modal [data-k="${f.k}"]`);obj[f.k]=f.type==='number'?parseFloat(el.value)||0:el.value});
|
||||||
|
if(crudIdx>=0)Object.assign(def.store[crudIdx],obj);else def.store.push(obj);
|
||||||
|
clCRUD();
|
||||||
|
if(cPg==='assets')rA();else if(cPg==='engines')rE();else if(cPg==='comms')rCm();else if(cPg==='tanks')rTk();else if(cPg==='vehicles')rVh();
|
||||||
|
}
|
||||||
|
function crudDel(){const def=CRUD_DEFS[crudType];if(crudIdx>=0){def.store.splice(crudIdx,1);clCRUD();if(cPg==='assets')rA();else if(cPg==='engines')rE();else if(cPg==='comms')rCm();else if(cPg==='tanks')rTk();else if(cPg==='vehicles')rVh()}}
|
||||||
|
// === DOCKING ===
|
||||||
|
function openDockModal(){$('dock-custom').style.display='none';$('dock-vehicle').value='';$('dock-note').value='';$('dock-modal').classList.add('open')}
|
||||||
|
function clDock(){$('dock-modal').classList.remove('open')}
|
||||||
|
function saveDock(){
|
||||||
|
const v=$('dock-vehicle').value;if(!v)return;
|
||||||
|
const name=v==='_custom'?$('dock-custom').value:v;if(!name)return;
|
||||||
|
const dk=DOCK_DATA[adIdx]||{parent:-1,vehicles:[]};DOCK_DATA[adIdx]=dk;
|
||||||
|
dk.vehicles.push({name,since:ST.toISOString().slice(0,10),until:null});
|
||||||
|
clDock();openAssetDetail(adIdx);
|
||||||
|
}
|
||||||
|
$('dock-vehicle').addEventListener('change',function(){$('dock-custom').style.display=this.value==='_custom'?'block':'none'});
|
||||||
|
|
||||||
|
// === LOG MODAL (multi-node) ===
|
||||||
|
let logNodeData=[];
|
||||||
|
function addNodeRow(title='',at='',detail=''){
|
||||||
|
logNodeData.push({title,at,detail});
|
||||||
|
renderNodeRows();
|
||||||
|
}
|
||||||
|
function removeNodeRow(i){logNodeData.splice(i,1);renderNodeRows()}
|
||||||
|
function renderNodeRows(){
|
||||||
|
$('nodes-list').innerHTML=logNodeData.length?logNodeData.map((n,i)=>`
|
||||||
|
<div style="background:var(--sfc2);border:1px solid var(--brd);border-radius:4px;padding:6px 8px;margin-bottom:4px">
|
||||||
|
<div style="display:flex;gap:6px;align-items:center">
|
||||||
|
<input type="text" value="${n.title}" placeholder="Node title" oninput="logNodeData[${i}].title=this.value" style="flex:1;padding:3px 6px;border-radius:3px;background:var(--bg);color:var(--txt);border:1px solid var(--brd);font-size:.7rem">
|
||||||
|
<input type="datetime-local" value="${n.at}" oninput="logNodeData[${i}].at=this.value" step="60" style="width:140px;padding:3px 6px;border-radius:3px;background:var(--bg);color:var(--txt);border:1px solid var(--brd);font-size:.7rem">
|
||||||
|
<button class="b sm" type="button" onclick="removeNodeRow(${i})" style="color:var(--red);font-size:.6rem">✕</button>
|
||||||
|
</div>
|
||||||
|
<input type="text" value="${n.detail}" placeholder="Detail (optional)" oninput="logNodeData[${i}].detail=this.value" style="width:100%;padding:3px 6px;border-radius:3px;background:var(--bg);color:var(--txt);border:1px solid var(--brd);font-size:.7rem;margin-top:3px">
|
||||||
|
</div>
|
||||||
|
`).join(''):'<div style="font-size:.68rem;color:var(--mut);padding:4px 0">No nodes. Click "+ Add Node" to add.</div>';
|
||||||
|
}
|
||||||
|
function openNewLog(type){
|
||||||
|
logNodeData=[];
|
||||||
|
$('log-type').value=type;$('log-title').value='';$('log-start').value='';$('log-end').value='';
|
||||||
|
$('log-state').value='';$('log-location').value='';$('log-summary').value='';
|
||||||
|
$('log-end-wrap').style.display=type==='state'?'block':'none';
|
||||||
|
$('log-state-wrap').style.display=type==='state'?'block':'none';
|
||||||
|
$('log-nodes-wrap').style.display=type==='state'?'block':'none';
|
||||||
|
renderNodeRows();$('log-modal-title').textContent='New Log Entry';
|
||||||
|
$('log-modal').classList.add('open');
|
||||||
|
}
|
||||||
|
function openEditLog(i){
|
||||||
|
editLogIdx=i;const l=MOCK_LOGS[i];
|
||||||
|
$('log-type').value=l.k;$('log-title').value=l.title;$('log-start').value=l.t;$('log-end').value=l.end||'';
|
||||||
|
$('log-state').value=l.state==='—'?'':l.state;$('log-location').value=l.loc==='—'?'':l.loc;$('log-summary').value=l.summary||'';
|
||||||
|
logNodeData=(l.nodes||[]).map(n=>({title:n.title,at:n.at,detail:n.detail||''}));
|
||||||
|
$('log-end-wrap').style.display=l.k==='state'?'block':'none';
|
||||||
|
$('log-state-wrap').style.display=l.k==='state'?'block':'none';
|
||||||
|
$('log-nodes-wrap').style.display=l.k==='state'?'block':'none';
|
||||||
|
renderNodeRows();$('log-modal-title').textContent='Edit Log Entry';
|
||||||
|
$('log-modal').classList.add('open');
|
||||||
|
}
|
||||||
|
function clLog(){$('log-modal').classList.remove('open')}
|
||||||
|
function saveLog(){
|
||||||
|
const type=$('log-type').value,title=$('log-title').value;if(!title)return;
|
||||||
|
const entry={t:($('log-start').value||'').slice(0,10)||ST.toISOString().slice(0,10),end:$('log-end').value?$('log-end').value.slice(0,10):null,k:type,title,state:$('log-state').value||'—',loc:$('log-location').value||'—',summary:$('log-summary').value,nodes:logNodeData.filter(n=>n.title).map(n=>({title:n.title,at:n.at||entry?.t||'',detail:n.detail}))};
|
||||||
|
if(editLogIdx>=0)MOCK_LOGS[editLogIdx]=entry;else MOCK_LOGS.unshift(entry);
|
||||||
|
editLogIdx=-1;rADLogs();clLog();$('ad-entries').textContent=MOCK_LOGS.length;
|
||||||
|
}
|
||||||
|
function delLog(i){if(confirm('Delete?')){MOCK_LOGS.splice(i,1);rADLogs();$('ad-entries').textContent=MOCK_LOGS.length}}
|
||||||
|
function delItem(arr,i){if(confirm('Delete this item?'))arr.splice(i,1)}
|
||||||
|
function sortT(id,col){const tb=document.getElementById(id).querySelector('tbody');const rows=Array.from(tb.rows);rows.sort((a,b)=>a.cells[col].textContent.localeCompare(b.cells[col].textContent));tb.append(...rows)}
|
||||||
|
|
||||||
|
// ═══ LOCATION BOARD ═══
|
||||||
|
const LOC_ASSETS=ASSETS;
|
||||||
|
const LOC_EVENTS=EVENTS;
|
||||||
|
const BODIES={sun:{n:'Sun',t:'star',r:20,fr:52,c:'#FDB813',c2:'#ff6b00'},mercury:{n:'Mercury',t:'p',r:5.5,fr:38,c:'#b5b5b5',c2:'#8a8a8a'},venus:{n:'Venus',t:'p',r:8,fr:44,c:'#e6c229',c2:'#c4952a'},earth:{n:'Earth',t:'p',r:9,fr:46,c:'#4da6ff',c2:'#1a56db',mo:{moon:{n:'Moon',r:2.8,c:'#ccc',c2:'#999',orb:40,P:27.3}}},mars:{n:'Mars',t:'p',r:7,fr:40,c:'#c1440e',c2:'#7a1f0a',mo:{phobos:{n:'Phobos',r:1.6,c:'#998',c2:'#665',orb:26,P:.32},deimos:{n:'Deimos',r:1.4,c:'#887',c2:'#554',orb:35,P:1.26}}},ceres:{n:'Ceres',t:'d',r:4,fr:32,c:'#8b8b8b',c2:'#6b6b6b'},vesta:{n:'Vesta',t:'d',r:3,fr:28,c:'#a0a0a0',c2:'#808080'},jupiter:{n:'Jupiter',t:'p',r:16,fr:60,c:'#c9b07c',c2:'#8b6914',mo:{io:{n:'Io',r:2.8,c:'#e8d44d',c2:'#b8a420',orb:50,P:1.77},europa:{n:'Europa',r:2.5,c:'#d4c9a8',c2:'#a09060',orb:62,P:3.55},ganymede:{n:'Ganymede',r:3.2,c:'#aaa',c2:'#777',orb:76,P:7.15},callisto:{n:'Callisto',r:2.8,c:'#666',c2:'#444',orb:90,P:16.7}}},saturn:{n:'Saturn',t:'p',r:13.5,fr:54,c:'#e0c97f',c2:'#b8952a',mo:{mimas:{n:'Mimas',r:1.8,c:'#bbb',c2:'#999',orb:44,P:.94},enceladus:{n:'Enceladus',r:2,c:'#dde',c2:'#bbc',orb:56,P:1.37},titan:{n:'Titan',r:3.5,c:'#d4a85c',c2:'#a07830',orb:90,P:15.95},iapetus:{n:'Iapetus',r:2.7,c:'#999',c2:'#666',orb:112,P:79.3}}},uranus:{n:'Uranus',t:'p',r:11,fr:46,c:'#7ec8e3',c2:'#4a90b0',mo:{miranda:{n:'Miranda',r:1.6,c:'#ccc',c2:'#aaa',orb:36,P:1.41},titania:{n:'Titania',r:3,c:'#bbb',c2:'#999',orb:64,P:8.71}}},neptune:{n:'Neptune',t:'p',r:10,fr:44,c:'#3f54ba',c2:'#1a2a80',mo:{triton:{n:'Triton',r:2.8,c:'#b8d4e3',c2:'#80a0b0',orb:44,P:5.88}}},pluto:{n:'Pluto',t:'d',r:3,fr:28,c:'#d4c9a8',c2:'#a09070',mo:{charon:{n:'Charon',r:2.2,c:'#bbb',c2:'#999',orb:28,P:6.39}}}};
|
||||||
|
const SO=['mercury','venus','earth','mars','ceres','vesta','jupiter','saturn','uranus','neptune','pluto'];
|
||||||
|
for(const bk of Object.keys(BODIES)){const b=BODIES[bk];if(b.mo)for(const[mk,mb]of Object.entries(b.mo))BODIES[mk]=mb}
|
||||||
|
const J2000_JD=2451545.0,DAY_MS=86400000;
|
||||||
|
const ORB={mercury:{a:.387098,e:.205630,i:7.005,O:48.331,p:77.456,L:252.251,P:87.969},venus:{a:.723332,e:.006772,i:3.395,O:76.680,p:131.563,L:181.980,P:224.701},earth:{a:1.000003,e:.016709,i:.000,O:.000,p:102.938,L:100.464,P:365.256},mars:{a:1.523679,e:.093401,i:1.850,O:49.558,p:336.060,L:355.453,P:686.980},ceres:{a:2.767500,e:.075823,i:10.593,O:80.306,p:72.716,L:73.598,P:1680},vesta:{a:2.361000,e:.088740,i:7.141,O:103.852,p:150.047,L:18.900,P:1325},jupiter:{a:5.203363,e:.048393,i:1.305,O:100.556,p:14.754,L:34.404,P:4332.589},saturn:{a:9.537070,e:.054151,i:2.485,O:113.715,p:92.432,L:49.944,P:10759.22},uranus:{a:19.19126,e:.047168,i:.770,O:74.230,p:170.964,L:313.232,P:30685.4},neptune:{a:30.06904,e:.008606,i:1.770,O:131.723,p:44.971,L:304.880,P:60189},pluto:{a:39.48212,e:.248828,i:17.140,O:110.304,p:224.069,L:238.929,P:90560}};
|
||||||
|
function jd(d){return d.getTime()/DAY_MS+2440587.5}
|
||||||
|
function kepl(M,e,eps=1e-8){let E=M+e*Math.sin(M)*(1+e*Math.cos(M));for(let i=0;i<20;i++){let d=(M-E+e*Math.sin(E))/(1-e*Math.cos(E));E+=d;if(Math.abs(d)<eps)break}return E}
|
||||||
|
function helio(k,jv){const o=ORB[k];if(!o)return{x:0,y:0};const D=Math.PI/180,n=2*Math.PI/o.P,ds=jv-J2000_JD;let M=(o.L-o.p)*D+n*ds;M=((M%(2*Math.PI))+2*Math.PI)%(2*Math.PI);const E=kepl(M,o.e),s=Math.sin(E),c=Math.cos(E),r=o.a*(1-o.e*c),sv=Math.sqrt(1-o.e*o.e)*s/(1-o.e*c),cv=(c-o.e)/(1-o.e*c);const w=(o.p-o.O)*D,O=o.O*D,I=o.i*D,cO=Math.cos(O),sO=Math.sin(O),cI=Math.cos(I),sI=Math.sin(I);const xo=r*cv,yo=r*sv,cw=Math.cos(w),sw=Math.sin(w);const xe=xo*(cO*cw-sO*sw*cI)-yo*(cO*sw+sO*cw*cI),ye=xo*(sO*cw+cO*sw*cI)+yo*(cO*cw*cI-sO*sw);const dist=Math.sqrt(xe*xe+ye*ye),dd=Math.pow(dist,.4)*100,sc=dist>.001?dd/dist:0;return{x:xe*sc,y:ye*sc}}
|
||||||
|
let mapCtx,mapCv,mapStars=null;
|
||||||
|
function initMap(){
|
||||||
|
if(mapCv)return;
|
||||||
|
mapCv=document.getElementById('map-cv');mapCtx=mapCv.getContext('2d');
|
||||||
|
function rsz(){const r=document.getElementById('map-ct').getBoundingClientRect(),d=devicePixelRatio||1;if(r.width===0||r.height===0)return;mapCv.width=r.width*d;mapCv.height=r.height*d;mapCv.style.width=r.width+'px';mapCv.style.height=r.height+'px';mapStars=null;renderMap()}
|
||||||
|
window.addEventListener('resize',rsz);
|
||||||
|
setTimeout(rsz,100);
|
||||||
|
// Events
|
||||||
|
mapCv.addEventListener('mousedown',e=>{mapDg=true;mapDS={x:e.clientX,y:e.clientY};mapDL={x:e.clientX,y:e.clientY};mapDD=false});
|
||||||
|
window.addEventListener('mousemove',e=>{if(!mapDg||!mapDL)return;if(Math.abs(e.clientX-mapDS.x)>6||Math.abs(e.clientY-mapDS.y)>6)mapDD=true;if(mapDD){mapVO.x+=e.clientX-mapDL.x;mapVO.y+=e.clientY-mapDL.y;mapDL={x:e.clientX,y:e.clientY};renderMap()}});
|
||||||
|
window.addEventListener('mouseup',()=>{mapDg=false;mapDS=null;mapDL=null});
|
||||||
|
mapCv.addEventListener('wheel',e=>{e.preventDefault();mapZm=Math.max(.3,Math.min(4,mapZm*(e.deltaY>0?.9:1.1)));renderMap()},{passive:false});
|
||||||
|
mapCv.addEventListener('click',e=>{if(mapDD)return;const r=mapCv.getBoundingClientRect(),t=mapHit(e.clientX-r.left,e.clientY-r.top);if(!t)return;const v=mapVS[mapVS.length-1],b=BODIES[t];if(v.tp==='solar')mapVS.push({tp:b&&b.mo?'planet_system':'single',ck:t});else if(v.tp==='planet_system')mapVS.push({tp:'single',ck:t});mapVO={x:0,y:0};mapZm=1;updateMapBack();renderMap();rMapInfo()});
|
||||||
|
mapCv.addEventListener('mousemove',e=>{if(mapDD)return;const r=mapCv.getBoundingClientRect(),t=mapHit(e.clientX-r.left,e.clientY-r.top);if(t!==mapHv){mapHv=t;mapCv.style.cursor=t?'pointer':'grab';renderMap()}});
|
||||||
|
}
|
||||||
|
function mapFV(k){if(BODIES[k])return BODIES[k];for(const bk of Object.keys(BODIES)){const b=BODIES[bk];if(b.mo&&b.mo[k])return b.mo[k]}return null}
|
||||||
|
function mapHit(mx,my){
|
||||||
|
const v=mapVS[mapVS.length-1],cs=[];
|
||||||
|
if(v.tp==='solar'){cs.push({k:'sun'});for(const k of SO)cs.push({k})}
|
||||||
|
else if(v.tp==='planet_system'){const c=BODIES[v.ck];if(c){cs.push({k:v.ck});if(c.mo)for(const mk of Object.keys(c.mo))cs.push({k:mk})}}
|
||||||
|
else{const b=BODIES[v.ck];if(b)cs.push({k:v.ck})}
|
||||||
|
for(const{k}of cs){const b=mapFV(k);if(!b||b._x==null)continue;const dx=mx-b._x,dy=my-b._y,hr=(b._r||4)+6;if(dx*dx+dy*dy<=hr*hr)return k}return null
|
||||||
|
}
|
||||||
|
function mapDraw(ox,oy,rad,key,lb){
|
||||||
|
const b=BODIES[key]||mapFV(key);if(!b)return;
|
||||||
|
const isStar=b.t==='star',bc=b.c||'#888';
|
||||||
|
mapCtx.save();mapCtx.beginPath();mapCtx.arc(ox,oy,rad,0,Math.PI*2);mapCtx.clip();
|
||||||
|
// Gradient sphere
|
||||||
|
const g=mapCtx.createRadialGradient(ox-rad*.25,oy-rad*.3,0,ox,oy,rad);
|
||||||
|
if(isStar){g.addColorStop(0,'#fffff8');g.addColorStop(.3,'#ffcc44');g.addColorStop(.7,'#ff6600');g.addColorStop(1,'#330800')}
|
||||||
|
else{g.addColorStop(0,'#ffffff');g.addColorStop(.2,bc);g.addColorStop(.8,b.c2||'#333');g.addColorStop(1,'#111')}
|
||||||
|
mapCtx.fillStyle=g;mapCtx.beginPath();mapCtx.arc(ox,oy,rad,0,Math.PI*2);mapCtx.fill();mapCtx.restore();
|
||||||
|
// Shade
|
||||||
|
if(!isStar){mapCtx.save();mapCtx.beginPath();mapCtx.arc(ox,oy,rad,0,Math.PI*2);mapCtx.clip();
|
||||||
|
const sh=mapCtx.createRadialGradient(ox-rad*.3,oy-rad*.3,rad*.05,ox+rad*.15,oy+rad*.15,rad*1.1);sh.addColorStop(0,'rgba(255,255,255,.1)');sh.addColorStop(.4,'rgba(0,0,0,0)');sh.addColorStop(.8,'rgba(0,0,0,.5)');mapCtx.fillStyle=sh;mapCtx.fillRect(ox-rad,oy-rad,rad*2,rad*2);mapCtx.restore()}
|
||||||
|
mapCtx.fillStyle='#e2e8f0';mapCtx.font=`${Math.max(8,rad*.45)}px system-ui`;mapCtx.textAlign='center';mapCtx.fillText(lb,ox,oy-rad-4);
|
||||||
|
b._x=ox;b._y=oy;b._r=rad;
|
||||||
|
}
|
||||||
|
function renderMap(){
|
||||||
|
if(!mapCv||mapCv.width===0||mapCv.height===0)return;
|
||||||
|
const dpr=devicePixelRatio||1,W=mapCv.width/dpr,H=mapCv.height/dpr;
|
||||||
|
mapCtx.save();mapCtx.setTransform(dpr,0,0,dpr,0,0);
|
||||||
|
if(!mapStars||mapStars.width!==mapCv.width){
|
||||||
|
const sc=document.createElement('canvas');sc.width=W;sc.height=H;
|
||||||
|
const sx=sc.getContext('2d');sx.fillStyle=document.documentElement.getAttribute('data-theme')==='light'?'#1a1a2e':'#0B0B10';sx.fillRect(0,0,W,H);
|
||||||
|
for(let i=0;i<600;i++){sx.fillStyle=`rgba(255,255,255,${.2+Math.random()*.6})`;sx.beginPath();sx.arc(Math.random()*W,Math.random()*H,Math.random()*1.2+.1,0,Math.PI*2);sx.fill()}
|
||||||
|
mapStars=sc;
|
||||||
|
}
|
||||||
|
mapCtx.drawImage(mapStars,0,0,W,H);
|
||||||
|
const v=mapVS[mapVS.length-1],cx=W/2+mapVO.x,cy=H/2+mapVO.y,jv=jd(new Date(ST.getTime()));
|
||||||
|
if(v.tp==='solar'){
|
||||||
|
for(const k of SO){mapCtx.beginPath();mapCtx.arc(cx,cy,Math.pow(ORB[k].a,.4)*100*mapZm,0,Math.PI*2);mapCtx.strokeStyle='rgba(148,163,184,.1)';mapCtx.lineWidth=.5;mapCtx.setLineDash([3,6]);mapCtx.stroke();mapCtx.setLineDash([])}
|
||||||
|
mapDraw(cx,cy,BODIES.sun.r*mapZm,'sun','Sun');
|
||||||
|
for(const k of SO){const b=BODIES[k],p=helio(k,jv);mapDraw(cx+p.x*mapZm,cy-p.y*mapZm,b.r*mapZm,k,b.n)}
|
||||||
|
}else if(v.tp==='planet_system'){
|
||||||
|
const c=BODIES[v.ck];if(!c)return;
|
||||||
|
mapDraw(cx,cy,c.fr*mapZm,v.ck,c.n);
|
||||||
|
if(c.mo)for(const[mk,mb]of Object.entries(c.mo)){const od=mb.orb*mapZm*1.8;mapCtx.beginPath();mapCtx.arc(cx,cy,od,0,Math.PI*2);mapCtx.strokeStyle='rgba(148,163,184,.1)';mapCtx.lineWidth=.5;mapCtx.setLineDash([3,6]);mapCtx.stroke();mapCtx.setLineDash([]);const an=(2*Math.PI*((jv-J2000_JD)%mb.P))/mb.P;mapDraw(cx+od*Math.cos(an),cy-od*Math.sin(an),mb.r*2.2*mapZm,mk,mb.n)}
|
||||||
|
}else{
|
||||||
|
const b=BODIES[v.ck];if(!b)return;const mx=Math.min(cx,cy)*.5,rr=Math.min((b.fr?b.fr*3:(b.r||4)*15)*mapZm,mx);
|
||||||
|
mapDraw(cx,cy,rr,v.ck,b.n);
|
||||||
|
// Saturn rings
|
||||||
|
if(v.ck==='saturn'&&rr>6){mapCtx.save();mapCtx.strokeStyle='rgba(200,180,140,.3)';mapCtx.lineWidth=Math.max(1,rr*.06);mapCtx.beginPath();mapCtx.ellipse(cx,cy,rr*1.5,rr*.18,0,0,Math.PI*2);mapCtx.stroke();mapCtx.strokeStyle='rgba(200,180,140,.15)';mapCtx.lineWidth=Math.max(1,rr*.04);mapCtx.beginPath();mapCtx.ellipse(cx,cy,rr*1.8,rr*.28,0,0,Math.PI*2);mapCtx.stroke();mapCtx.restore()}
|
||||||
|
}
|
||||||
|
// Hover
|
||||||
|
if(mapHv){const b=mapFV(mapHv);if(b&&b._x!=null){mapCtx.beginPath();mapCtx.arc(b._x,b._y,(b._r||4)+5,0,Math.PI*2);mapCtx.strokeStyle='rgba(59,130,246,.7)';mapCtx.lineWidth=2;mapCtx.stroke()}}
|
||||||
|
mapCtx.restore();
|
||||||
|
}
|
||||||
|
function rMapInfo(){
|
||||||
|
const v=mapVS[mapVS.length-1];let sk,sn;
|
||||||
|
if(v.tp==='solar'){sk='solar_system';sn='Solar System'}else{sk=v.ck;const b=BODIES[v.ck];sn=b?b.n+(v.tp==='planet_system'?' System':''):v.ck}
|
||||||
|
$('mi-title').textContent=sn;$('mi-scope').textContent=sn;
|
||||||
|
const as=LOC_ASSETS.filter(a=>{
|
||||||
|
if(sk==='solar_system')return true;
|
||||||
|
const loc=a.l.toLowerCase();
|
||||||
|
if(sk==='earth')return loc.includes('leo')||loc.includes('earth');
|
||||||
|
if(sk==='mars')return loc.includes('mars');
|
||||||
|
if(sk==='jupiter')return loc.includes('jupiter')||loc.includes('europa');
|
||||||
|
if(sk==='saturn')return false;if(sk==='uranus')return false;if(sk==='neptune')return false;
|
||||||
|
return loc.includes(sk);
|
||||||
|
});
|
||||||
|
$('mi-assets').innerHTML=as.length?as.map(a=>`<div class="mi-arow"><span class="n">${a.n}</span><span class="loc">${a.l}</span></div>`).join(''):'<div style="color:var(--mut);font-size:.72rem">No assets</div>';
|
||||||
|
$('mi-miss').textContent=as.filter(a=>a.s==='transfer'||a.s==='exploration').length;
|
||||||
|
const ns=ST.toISOString().slice(0,10);
|
||||||
|
$('mi-events').innerHTML=LOC_EVENTS.filter(e=>e.d>=ns).slice(0,4).map(e=>`<div style="padding:2px 0;font-size:.7rem;border-bottom:1px solid var(--brd)"><span style="color:var(--amb);font-family:var(--fd);font-size:.64rem">${e.d}</span> ${e.t}</div>`).join('')||'<div style="color:var(--mut);font-size:.72rem">None</div>';
|
||||||
|
}
|
||||||
|
function updateMapBack(){$('bk-btn').classList.toggle('vis',mapVS.length>1)}
|
||||||
|
function mapGoBack(){if(mapVS.length>1)mapVS.pop();mapVO={x:0,y:0};mapZm=1;updateMapBack();renderMap();rMapInfo()}
|
||||||
|
|
||||||
|
// ═══ THEME ═══
|
||||||
|
function toggleTheme(){const cur=document.documentElement.getAttribute('data-theme'),next=cur==='dark'?'light':'dark';document.documentElement.setAttribute('data-theme',next);localStorage.setItem('ksp-theme',next);mapStars=null;if(mapCv)renderMap()}
|
||||||
|
(function(){const t=localStorage.getItem('ksp-theme');if(t)document.documentElement.setAttribute('data-theme',t)})();
|
||||||
|
|
||||||
|
// ═══ TIME ═══
|
||||||
|
function openTM(){$('tminp').value=`${ST.getUTCFullYear()}-${String(ST.getUTCMonth()+1).padStart(2,'0')}-${String(ST.getUTCDate()).padStart(2,'0')}T${String(ST.getUTCHours()).padStart(2,'0')}:${String(ST.getUTCMinutes()).padStart(2,'0')}`;$('tmodal').classList.add('open')}
|
||||||
|
function clTM(){$('tmodal').classList.remove('open')}
|
||||||
|
function apT(){const v=$('tminp').value;if(!v)return;const d=new Date(v+'Z');if(isNaN(d.getTime()))return;ST.setTime(d.getTime());uT();rO();rEts();clTM()}
|
||||||
|
function shT(d){ST.setTime(ST.getTime()+d*86400000);uT();rO();rEts()}
|
||||||
|
function rsT(){ST.setTime(new Date('2060-08-11T10:00:00Z').getTime());uT();rO();rEts()}
|
||||||
|
$('tmodal').addEventListener('click',function(e){if(e.target===this)clTM()});
|
||||||
|
document.addEventListener('keydown',e=>{if(e.key==='Escape'){clTM();clCRUD()}if(e.key==='t'&&e.ctrlKey){e.preventDefault();openTM()}});
|
||||||
|
|
||||||
|
// ═══ INIT ═══
|
||||||
|
uT();rO();rEts();rCv();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,420 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>KSP管理后台 — Location Board</title>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg:#0B0B10;--surface:#131318;--border:rgba(248,250,252,.08);--text:#F8FAFC;--muted:#94A3B8;
|
||||||
|
--accent:#3B82F6;--accent-glow:rgba(59,130,246,.35);--warn:#eab308;--danger:#EF4444;
|
||||||
|
--radius:8px;--transition:150ms ease;
|
||||||
|
}
|
||||||
|
*{margin:0;padding:0;box-sizing:border-box}
|
||||||
|
body{
|
||||||
|
background:var(--bg);color:var(--text);font-family:system-ui,-apple-system,'Segoe UI',sans-serif;
|
||||||
|
height:100vh;overflow:hidden;display:flex;flex-direction:column;
|
||||||
|
}
|
||||||
|
/* Top bar */
|
||||||
|
#top-bar{
|
||||||
|
display:flex;align-items:center;gap:12px;padding:8px 16px;
|
||||||
|
background:var(--surface);border-bottom:1px solid var(--border);z-index:20;
|
||||||
|
font-size:.8rem;
|
||||||
|
}
|
||||||
|
#top-bar .brand{font-family:'SF Mono','Cascadia Code','Consolas',monospace;font-weight:600;font-size:.84rem;color:var(--text);letter-spacing:-.02em}
|
||||||
|
#top-bar .brand span{color:var(--accent)}
|
||||||
|
#scope-label{font-size:.78rem;color:var(--accent);font-weight:500}
|
||||||
|
.spacer{flex:1}
|
||||||
|
#time-controls{display:flex;align-items:center;gap:4px}
|
||||||
|
#time-controls button,.style-btn{
|
||||||
|
background:rgba(255,255,255,.05);border:1px solid transparent;color:var(--muted);
|
||||||
|
padding:4px 9px;border-radius:4px;cursor:pointer;font-family:'SF Mono','Cascadia Code','Consolas',monospace;font-size:.72rem;
|
||||||
|
transition:all var(--transition);
|
||||||
|
}
|
||||||
|
#time-controls button:hover{background:rgba(255,255,255,.1);color:var(--text);border-color:var(--border)}
|
||||||
|
.style-btn.active{background:var(--accent);color:#fff;border-color:var(--accent)}
|
||||||
|
#sim-time-input{
|
||||||
|
font-family:'SF Mono','Cascadia Code','Consolas',monospace;font-size:.78rem;padding:4px 8px;border-radius:4px;
|
||||||
|
background:rgba(255,255,255,.04);color:var(--text);border:1px solid var(--border);width:152px;text-align:center;
|
||||||
|
}
|
||||||
|
#sim-time-input:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 2px var(--accent-glow)}
|
||||||
|
#time-slider{
|
||||||
|
flex:1;min-width:100px;max-width:240px;-webkit-appearance:none;height:3px;
|
||||||
|
background:var(--border);border-radius:2px;outline:none;cursor:pointer;
|
||||||
|
}
|
||||||
|
#time-slider::-webkit-slider-thumb{-webkit-appearance:none;width:13px;height:13px;border-radius:50%;background:var(--accent);cursor:pointer;box-shadow:0 0 10px var(--accent-glow)}
|
||||||
|
/* Main layout */
|
||||||
|
#main{display:flex;flex:1;overflow:hidden}
|
||||||
|
/* Map */
|
||||||
|
#map-container{
|
||||||
|
flex:1;position:relative;display:flex;align-items:center;justify-content:center;
|
||||||
|
background:var(--bg);cursor:grab;overflow:hidden;
|
||||||
|
}
|
||||||
|
canvas{display:block}
|
||||||
|
/* Map overlays */
|
||||||
|
#back-btn{
|
||||||
|
position:absolute;top:12px;left:12px;z-index:5;display:none;
|
||||||
|
background:var(--surface);border:1px solid var(--border);color:var(--text);
|
||||||
|
padding:5px 14px;border-radius:var(--radius);cursor:pointer;
|
||||||
|
font-family:'Fira Sans',sans-serif;font-size:.78rem;
|
||||||
|
transition:all var(--transition);
|
||||||
|
}
|
||||||
|
#back-btn.visible{display:block}
|
||||||
|
#back-btn:hover{background:rgba(255,255,255,.08);border-color:rgba(248,250,252,.15)}
|
||||||
|
#scope-indicator{
|
||||||
|
position:absolute;top:12px;right:12px;z-index:5;
|
||||||
|
background:rgba(19,19,24,.85);border:1px solid var(--border);
|
||||||
|
color:var(--muted);padding:4px 12px;border-radius:4px;font-size:.72rem;
|
||||||
|
font-family:'SF Mono','Cascadia Code','Consolas',monospace;
|
||||||
|
}
|
||||||
|
#style-toggle{position:absolute;bottom:12px;left:12px;z-index:5;display:flex;gap:4px}
|
||||||
|
#instructions{
|
||||||
|
position:absolute;bottom:12px;right:12px;z-index:5;
|
||||||
|
color:rgba(148,163,184,.4);font-size:.66rem;font-family:'Fira Sans',sans-serif;
|
||||||
|
pointer-events:none;
|
||||||
|
}
|
||||||
|
/* Info panel */
|
||||||
|
#info-panel{
|
||||||
|
width:320px;background:var(--surface);border-left:1px solid var(--border);
|
||||||
|
padding:20px 18px;overflow-y:auto;display:flex;flex-direction:column;gap:18px;
|
||||||
|
}
|
||||||
|
#info-panel h2{
|
||||||
|
font-family:'SF Mono','Cascadia Code','Consolas',monospace;font-size:.88rem;font-weight:600;
|
||||||
|
padding-bottom:6px;border-bottom:1px solid var(--border);letter-spacing:-.02em;
|
||||||
|
}
|
||||||
|
.info-section{}
|
||||||
|
.info-section h3{
|
||||||
|
font-family:'SF Mono','Cascadia Code','Consolas',monospace;font-size:.66rem;text-transform:uppercase;
|
||||||
|
color:var(--muted);letter-spacing:.06em;margin-bottom:4px;font-weight:500;
|
||||||
|
}
|
||||||
|
.metric-value{font-family:'SF Mono','Cascadia Code','Consolas',monospace;font-size:1.5rem;font-weight:600;color:var(--accent)}
|
||||||
|
.asset-row{
|
||||||
|
display:flex;justify-content:space-between;align-items:center;
|
||||||
|
padding:6px 0;border-bottom:1px solid rgba(248,250,252,.04);font-size:.8rem;
|
||||||
|
}
|
||||||
|
.asset-row .name{color:var(--accent);cursor:pointer;transition:color var(--transition)}
|
||||||
|
.asset-row .name:hover{color:#60a5fa;text-decoration:underline}
|
||||||
|
.asset-row .loc{color:var(--muted);font-size:.72rem;font-family:'SF Mono','Cascadia Code','Consolas',monospace}
|
||||||
|
.event-row{
|
||||||
|
padding:4px 0;font-size:.78rem;border-bottom:1px solid rgba(248,250,252,.04);
|
||||||
|
}
|
||||||
|
.event-row .date{color:var(--warn);font-family:'SF Mono','Cascadia Code','Consolas',monospace;font-size:.72rem}
|
||||||
|
/* Scrollbar */
|
||||||
|
#info-panel::-webkit-scrollbar{width:4px}
|
||||||
|
#info-panel::-webkit-scrollbar-track{background:transparent}
|
||||||
|
#info-panel::-webkit-scrollbar-thumb{background:var(--border);border-radius:2px}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div id="top-bar">
|
||||||
|
<div class="brand"><span>KSP</span>管理后台</div>
|
||||||
|
<span style="color:var(--muted);font-size:.7rem">/</span>
|
||||||
|
<span id="scope-label">Solar System</span>
|
||||||
|
<span class="spacer"></span>
|
||||||
|
<div id="time-controls">
|
||||||
|
<button onclick="shiftDays(-365)" title="-1 year">◀◀</button>
|
||||||
|
<button onclick="shiftDays(-30)" title="-30 days">◀</button>
|
||||||
|
<input type="text" id="sim-time-input" value="2060-08-11 10:00" title="YYYY-MM-DD HH:MM">
|
||||||
|
<button onclick="shiftDays(30)" title="+30 days">▶</button>
|
||||||
|
<button onclick="shiftDays(365)" title="+1 year">▶▶</button>
|
||||||
|
<button onclick="resetTime()">Today</button>
|
||||||
|
</div>
|
||||||
|
<input type="range" id="time-slider" min="0" max="1000" value="0">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="main">
|
||||||
|
<div id="map-container">
|
||||||
|
<button id="back-btn" onclick="goBack()">← Back</button>
|
||||||
|
<span id="scope-indicator">Solar System</span>
|
||||||
|
<div id="style-toggle">
|
||||||
|
<button class="style-btn" id="btn-flat" onclick="setStyle('flat')">Flat</button>
|
||||||
|
<button class="style-btn active" id="btn-realistic" onclick="setStyle('realistic')">Realistic</button>
|
||||||
|
</div>
|
||||||
|
<span id="instructions">click planet to focus · drag to pan · scroll to zoom</span>
|
||||||
|
<canvas id="map-canvas"></canvas>
|
||||||
|
</div>
|
||||||
|
<div id="info-panel">
|
||||||
|
<h2 id="panel-title">Solar System</h2>
|
||||||
|
<div class="info-section"><h3>Scope</h3><div style="font-size:.92rem;font-weight:500" id="panel-scope">—</div></div>
|
||||||
|
<div class="info-section"><h3>Assets in Scope</h3><div id="panel-assets"></div></div>
|
||||||
|
<div class="info-section"><h3>Ongoing Missions</h3><div class="metric-value" id="panel-mission-count">—</div></div>
|
||||||
|
<div class="info-section"><h3>Upcoming Events</h3><div id="panel-events"></div></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// ═══════════════════════════════════
|
||||||
|
// KEPLERIAN ORBITAL ELEMENTS (J2000)
|
||||||
|
// ═══════════════════════════════════
|
||||||
|
const J2000_JD=2451545.0,DAY=86400000;
|
||||||
|
const ORB={
|
||||||
|
mercury:{a:.387098,e:.205630,i:7.005,O:48.331,p:77.456,L:252.251,P:87.969},
|
||||||
|
venus:{a:.723332,e:.006772,i:3.395,O:76.680,p:131.563,L:181.980,P:224.701},
|
||||||
|
earth:{a:1.000003,e:.016709,i:.000,O:.000,p:102.938,L:100.464,P:365.256},
|
||||||
|
mars:{a:1.523679,e:.093401,i:1.850,O:49.558,p:336.060,L:355.453,P:686.980},
|
||||||
|
ceres:{a:2.767500,e:.075823,i:10.593,O:80.306,p:72.716,L:73.598,P:1680},
|
||||||
|
vesta:{a:2.361000,e:.088740,i:7.141,O:103.852,p:150.047,L:18.900,P:1325},
|
||||||
|
jupiter:{a:5.203363,e:.048393,i:1.305,O:100.556,p:14.754,L:34.404,P:4332.589},
|
||||||
|
saturn:{a:9.537070,e:.054151,i:2.485,O:113.715,p:92.432,L:49.944,P:10759.22},
|
||||||
|
uranus:{a:19.19126,e:.047168,i:.770,O:74.230,p:170.964,L:313.232,P:30685.4},
|
||||||
|
neptune:{a:30.06904,e:.008606,i:1.770,O:131.723,p:44.971,L:304.880,P:60189},
|
||||||
|
pluto:{a:39.48212,e:.248828,i:17.140,O:110.304,p:224.069,L:238.929,P:90560}
|
||||||
|
};
|
||||||
|
function jd(d){return d.getTime()/DAY+2440587.5}
|
||||||
|
function kepler(M,e,eps=1e-8){let E=M+e*Math.sin(M)*(1+e*Math.cos(M));for(let i=0;i<20;i++){let d=(M-E+e*Math.sin(E))/(1-e*Math.cos(E));E+=d;if(Math.abs(d)<eps)break}return E}
|
||||||
|
function helio(key,jv){
|
||||||
|
const o=ORB[key];if(!o)return{x:0,y:0,r:0};const D=Math.PI/180,n=2*Math.PI/o.P,ds=jv-J2000_JD;
|
||||||
|
let M=(o.L-o.p)*D+n*ds;M=((M%(2*Math.PI))+2*Math.PI)%(2*Math.PI);
|
||||||
|
const E=kepler(M,o.e),s=Math.sin(E),c=Math.cos(E),r=o.a*(1-o.e*c);
|
||||||
|
const sv=Math.sqrt(1-o.e*o.e)*s/(1-o.e*c),cv=(c-o.e)/(1-o.e*c);
|
||||||
|
const w=(o.p-o.O)*D,O=o.O*D,I=o.i*D,cO=Math.cos(O),sO=Math.sin(O),cI=Math.cos(I),sI=Math.sin(I);
|
||||||
|
const xo=r*cv,yo=r*sv,cw=Math.cos(w),sw=Math.sin(w);
|
||||||
|
const xe=xo*(cO*cw-sO*sw*cI)-yo*(cO*sw+sO*cw*cI);
|
||||||
|
const ye=xo*(sO*cw+cO*sw*cI)+yo*(cO*cw*cI-sO*sw);
|
||||||
|
const dist=Math.sqrt(xe*xe+ye*ye),ddisp=Math.pow(dist,.4)*100,scl=dist>.001?ddisp/dist:0;
|
||||||
|
return{x:xe*scl,y:ye*scl,r:ddisp}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ═══════════════════════════════════
|
||||||
|
// BODIES
|
||||||
|
// ═══════════════════════════════════
|
||||||
|
const BD={
|
||||||
|
sun:{n:'Sun',t:'star',r:20,fr:52,c:'#FDB813',c2:'#ff6b00',moons:null},
|
||||||
|
mercury:{n:'Mercury',t:'planet',r:5.5,fr:38,c:'#b5b5b5',c2:'#8a8a8a',moons:null},
|
||||||
|
venus:{n:'Venus',t:'planet',r:8,fr:44,c:'#e6c229',c2:'#c4952a',moons:null},
|
||||||
|
earth:{n:'Earth',t:'planet',r:9,fr:46,c:'#4da6ff',c2:'#1a56db',
|
||||||
|
moons:{moon:{n:'Moon',r:2.8,c:'#ccc',c2:'#999',orb:40,P:27.3}}},
|
||||||
|
mars:{n:'Mars',t:'planet',r:7,fr:40,c:'#c1440e',c2:'#7a1f0a',
|
||||||
|
moons:{phobos:{n:'Phobos',r:1.6,c:'#998',c2:'#665',orb:26,P:.32},
|
||||||
|
deimos:{n:'Deimos',r:1.4,c:'#887',c2:'#554',orb:35,P:1.26}}},
|
||||||
|
ceres:{n:'Ceres',t:'dwarf',r:4,fr:32,c:'#8b8b8b',c2:'#6b6b6b',moons:null},
|
||||||
|
vesta:{n:'Vesta',t:'ast',r:3,fr:28,c:'#a0a0a0',c2:'#808080',moons:null},
|
||||||
|
jupiter:{n:'Jupiter',t:'planet',r:16,fr:60,c:'#c9b07c',c2:'#8b6914',
|
||||||
|
moons:{io:{n:'Io',r:2.8,c:'#e8d44d',c2:'#b8a420',orb:50,P:1.77},
|
||||||
|
europa:{n:'Europa',r:2.5,c:'#d4c9a8',c2:'#a09060',orb:62,P:3.55},
|
||||||
|
ganymede:{n:'Ganymede',r:3.2,c:'#aaa',c2:'#777',orb:76,P:7.15},
|
||||||
|
callisto:{n:'Callisto',r:2.8,c:'#666',c2:'#444',orb:90,P:16.7}}},
|
||||||
|
saturn:{n:'Saturn',t:'planet',r:13.5,fr:54,c:'#e0c97f',c2:'#b8952a',
|
||||||
|
moons:{mimas:{n:'Mimas',r:1.8,c:'#bbb',c2:'#999',orb:44,P:.94},
|
||||||
|
enceladus:{n:'Enceladus',r:2,c:'#dde',c2:'#bbc',orb:52,P:1.37},
|
||||||
|
tethys:{n:'Tethys',r:2.2,c:'#ddd',c2:'#bbb',orb:60,P:1.89},
|
||||||
|
dione:{n:'Dione',r:2.3,c:'#ccc',c2:'#aaa',orb:68,P:2.74},
|
||||||
|
rhea:{n:'Rhea',r:2.5,c:'#ddd',c2:'#bbb',orb:78,P:4.52},
|
||||||
|
titan:{n:'Titan',r:3.5,c:'#d4a85c',c2:'#a07830',orb:90,P:15.95},
|
||||||
|
iapetus:{n:'Iapetus',r:2.7,c:'#999',c2:'#666',orb:106,P:79.3}}},
|
||||||
|
uranus:{n:'Uranus',t:'planet',r:11,fr:46,c:'#7ec8e3',c2:'#4a90b0',
|
||||||
|
moons:{miranda:{n:'Miranda',r:1.6,c:'#ccc',c2:'#aaa',orb:36,P:1.41},
|
||||||
|
ariel:{n:'Ariel',r:2.2,c:'#ddd',c2:'#bbb',orb:46,P:2.52},
|
||||||
|
umbriel:{n:'Umbriel',r:2.1,c:'#999',c2:'#777',orb:54,P:4.14},
|
||||||
|
titania:{n:'Titania',r:2.8,c:'#bbb',c2:'#999',orb:64,P:8.71},
|
||||||
|
oberon:{n:'Oberon',r:2.5,c:'#aaa',c2:'#888',orb:74,P:13.46}}},
|
||||||
|
neptune:{n:'Neptune',t:'planet',r:10,fr:44,c:'#3f54ba',c2:'#1a2a80',
|
||||||
|
moons:{triton:{n:'Triton',r:2.8,c:'#b8d4e3',c2:'#80a0b0',orb:44,P:5.88}}},
|
||||||
|
pluto:{n:'Pluto',t:'dwarf',r:3,fr:28,c:'#d4c9a8',c2:'#a09070',
|
||||||
|
moons:{charon:{n:'Charon',r:2.2,c:'#bbb',c2:'#999',orb:28,P:6.39}}}
|
||||||
|
};
|
||||||
|
// Flatten moons into BD for direct lookup
|
||||||
|
for(const bk of Object.keys(BD)){const b=BD[bk];if(b.moons)for(const[mk,mb]of Object.entries(b.moons))BD[mk]=mb}
|
||||||
|
const SO=['mercury','venus','earth','mars','ceres','vesta','jupiter','saturn','uranus','neptune','pluto'];
|
||||||
|
const MOCK_ASSETS=[
|
||||||
|
{id:'st01',n:'ST-01 万星源号',loc:'Transfer',sc:['solar_system']},
|
||||||
|
{id:'st02',n:'ST-02 克里斯滕号',loc:'Jupiter System',sc:['solar_system','jupiter']},
|
||||||
|
{id:'st03',n:'ST-03 Muelsyse',loc:'LEO',sc:['solar_system','earth']},
|
||||||
|
{id:'xh01',n:'XH-01 羲和号',loc:'Jupiter System',sc:['solar_system','jupiter']},
|
||||||
|
{id:'xh02',n:'XH-02 太白号',loc:'Mars Orbit',sc:['solar_system','mars']},
|
||||||
|
{id:'xh03',n:'XH-03 常曦号',loc:'LEO',sc:['solar_system','earth']},
|
||||||
|
{id:'jw01',n:'JW-01 金乌号',loc:'LEO',sc:['solar_system','earth']},
|
||||||
|
{id:'sp',n:'星港空间站',loc:'LEO',sc:['solar_system','earth']},
|
||||||
|
{id:'m1',n:'火星一号基地',loc:'Mars Surface',sc:['solar_system','mars']},
|
||||||
|
{id:'eo',n:'欧罗巴前哨站',loc:'Europa Surface',sc:['solar_system','jupiter']},
|
||||||
|
];
|
||||||
|
const MOCK_EVENTS=[
|
||||||
|
{d:'2060-06-10',t:'ST-03 维护结束'},{d:'2060-06-15',t:'ST-03 深空测试开始'},
|
||||||
|
{d:'2060-06-28',t:'ST-02 返航抵达地球'},{d:'2060-07-25',t:'ST-01 抵达海王星系统'},
|
||||||
|
{d:'2060-09-01',t:'ST-02 进入升级'},{d:'2060-10-25',t:'ST-03 服役前维护结束'},
|
||||||
|
];
|
||||||
|
|
||||||
|
// ═══════════════════════════════════
|
||||||
|
// STARFIELD & REAL TEXTURES
|
||||||
|
// ═══════════════════════════════════
|
||||||
|
let stars=null;
|
||||||
|
function mkStars(w,h){
|
||||||
|
const c=document.createElement('canvas');c.width=w;c.height=h;const x=c.getContext('2d');
|
||||||
|
x.fillStyle='#0B0B10';x.fillRect(0,0,w,h);
|
||||||
|
for(let i=0;i<800;i++){const px=Math.random()*w,py=Math.random()*h,s=Math.random()*1.6+.15;x.fillStyle=`rgba(255,255,255,${.3+Math.random()*.7})`;x.beginPath();x.arc(px,py,s,0,Math.PI*2);x.fill()}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Real texture images loaded from textures/ folder
|
||||||
|
const TEX_SIZE=512;
|
||||||
|
const TEX_MAP={
|
||||||
|
sun:'sun.jpg',mercury:'mercury.jpg',venus:'venus.jpg',earth:'earth.jpg',
|
||||||
|
mars:'mars.jpg',jupiter:'jupiter.jpg',saturn:'saturn.jpg',uranus:'uranus.jpg',
|
||||||
|
neptune:'neptune.jpg',pluto:'pluto.jpg',moon:'moon.jpg',io:'io.jpg'
|
||||||
|
};
|
||||||
|
const texImgs={};
|
||||||
|
let texLoaded=0;
|
||||||
|
const texTotal=Object.keys(TEX_MAP).length;
|
||||||
|
|
||||||
|
function loadTextures(){
|
||||||
|
for(const[key,file]of Object.entries(TEX_MAP)){
|
||||||
|
const img=new Image();
|
||||||
|
img.onload=()=>{texImgs[key]=img;texLoaded++;if(texLoaded>=texTotal)render()};
|
||||||
|
img.src='textures/'+file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function tex(key){
|
||||||
|
if(texImgs[key])return texImgs[key];
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3D sphere shading overlay (drawn on top of texture)
|
||||||
|
function shadeSphere(ctx,x,y,r,isStar){
|
||||||
|
if(isStar){
|
||||||
|
// Sun: subtle glow
|
||||||
|
const glow=ctx.createRadialGradient(x,y,r*.6,x,y,r*1.4);
|
||||||
|
glow.addColorStop(0,'rgba(255,200,50,.15)');glow.addColorStop(.5,'rgba(255,150,20,.05)');glow.addColorStop(1,'rgba(255,100,0,0)');
|
||||||
|
ctx.fillStyle=glow;ctx.beginPath();ctx.arc(x,y,r*1.4,0,Math.PI*2);ctx.fill();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 3D sphere shadow
|
||||||
|
ctx.save();ctx.beginPath();ctx.arc(x,y,r,0,Math.PI*2);ctx.clip();
|
||||||
|
const shadow=ctx.createRadialGradient(x-r*.3,y-r*.3,r*.05,x+r*.15,y+r*.15,r*1.1);
|
||||||
|
shadow.addColorStop(0,'rgba(255,255,255,.1)');shadow.addColorStop(.35,'rgba(0,0,0,0)');
|
||||||
|
shadow.addColorStop(.7,'rgba(0,0,0,.3)');shadow.addColorStop(1,'rgba(0,0,0,.55)');
|
||||||
|
ctx.fillStyle=shadow;ctx.fillRect(x-r,y-r,r*2,r*2);
|
||||||
|
// Atmosphere rim
|
||||||
|
const atmo=ctx.createRadialGradient(x,y,r*.75,x,y,r*1.02);
|
||||||
|
atmo.addColorStop(0,'rgba(0,0,0,0)');atmo.addColorStop(.88,'rgba(200,220,255,0)');
|
||||||
|
atmo.addColorStop(.95,'rgba(200,220,255,.12)');atmo.addColorStop(1,'rgba(200,220,255,.3)');
|
||||||
|
ctx.fillStyle=atmo;ctx.fillRect(x-r,y-r,r*2,r*2);
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ═══════════════════════════════════
|
||||||
|
// STATE
|
||||||
|
// ═══════════════════════════════════
|
||||||
|
let simTime=new Date('2060-08-11T10:00:00Z');
|
||||||
|
const TMN=new Date('2050-01-01T00:00Z').getTime(),TMX=new Date('2070-01-01T00:00Z').getTime(),TR=TMX-TMN;
|
||||||
|
let rT=simTime.getTime(),tT=simTime.getTime(),af=null,style='realistic';
|
||||||
|
let vs=[{tp:'solar_system',ck:'sun'}],hv=null,dg=false,dS=null,dL=null,dd=false,vO={x:0,y:0},zm=1;
|
||||||
|
|
||||||
|
// ═══════════════════════════════════
|
||||||
|
// CANVAS
|
||||||
|
// ═══════════════════════════════════
|
||||||
|
const cv=document.getElementById('map-canvas'),ctx=cv.getContext('2d'),ct=document.getElementById('map-container');
|
||||||
|
function rs(){const r=ct.getBoundingClientRect(),d=window.devicePixelRatio||1;cv.width=r.width*d;cv.height=r.height*d;cv.style.width=r.width+'px';cv.style.height=r.height+'px';stars=null}
|
||||||
|
window.addEventListener('resize',()=>{rs();render()});rs();
|
||||||
|
|
||||||
|
function or(ox,oy,rad){ctx.beginPath();ctx.arc(ox,oy,rad,0,2*Math.PI);ctx.strokeStyle='rgba(148,163,184,.12)';ctx.lineWidth=.5;ctx.setLineDash([4,8]);ctx.stroke();ctx.setLineDash([])}
|
||||||
|
function db(x,y,r,k,lb){
|
||||||
|
const b=fv(k);if(!b)return;
|
||||||
|
const isStar=b.t==='star';
|
||||||
|
const img=tex(k);
|
||||||
|
|
||||||
|
if(img){
|
||||||
|
ctx.save();ctx.beginPath();ctx.arc(x,y,r,0,Math.PI*2);ctx.clip();
|
||||||
|
ctx.drawImage(img,x-r,y-r,r*2,r*2);
|
||||||
|
ctx.restore();
|
||||||
|
shadeSphere(ctx,x,y,r,isStar);
|
||||||
|
}else{
|
||||||
|
const bc=b.c||'#888';
|
||||||
|
if(isStar){
|
||||||
|
const glow=ctx.createRadialGradient(x,y,r*.3,x,y,r*2.5);glow.addColorStop(0,bc+'50');glow.addColorStop(.5,bc+'20');glow.addColorStop(1,bc+'00');ctx.fillStyle=glow;ctx.beginPath();ctx.arc(x,y,r*2.5,0,Math.PI*2);ctx.fill();
|
||||||
|
}
|
||||||
|
const g=ctx.createRadialGradient(x-r*.25,y-r*.3,0,x,y,r);
|
||||||
|
g.addColorStop(0,'#ffffff');g.addColorStop(.25,bc);g.addColorStop(1,b.c2||'#333');
|
||||||
|
ctx.fillStyle=g;ctx.beginPath();ctx.arc(x,y,r,0,Math.PI*2);ctx.fill();
|
||||||
|
shadeSphere(ctx,x,y,r,isStar);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(k==='saturn'&&r>6){
|
||||||
|
ctx.save();
|
||||||
|
ctx.strokeStyle='rgba(200,180,140,.3)';ctx.lineWidth=Math.max(1,r*.06);
|
||||||
|
ctx.beginPath();ctx.ellipse(x,y,r*1.5,r*.18,0,0,Math.PI*2);ctx.stroke();
|
||||||
|
ctx.strokeStyle='rgba(200,180,140,.15)';ctx.lineWidth=Math.max(1,r*.04);
|
||||||
|
ctx.beginPath();ctx.ellipse(x,y,r*1.8,r*.28,0,0,Math.PI*2);ctx.stroke();
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.fillStyle='#e2e8f0';ctx.font=`${Math.max(8.5,r*.48)}px system-ui,sans-serif`;ctx.textAlign='center';ctx.fillText(lb,x,y-r-5);
|
||||||
|
b._x=x;b._y=y;b._r=r
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderSS(cx,cy,tm){
|
||||||
|
const jv=jd(new Date(tm));
|
||||||
|
for(const k of SO){const rad=Math.pow(ORB[k].a,.4)*100*zm;or(cx,cy,rad)}
|
||||||
|
db(cx,cy,BD.sun.r*zm,'sun','Sun');
|
||||||
|
for(const k of SO){const b=BD[k],p=helio(k,jv);db(cx+p.x*zm,cy-p.y*zm,b.r*zm,k,b.n)}
|
||||||
|
}
|
||||||
|
function renderPS(ck,cx,cy,tm){
|
||||||
|
const c=BD[ck];if(!c)return;const jv=jd(new Date(tm)),pr=c.fr*zm;
|
||||||
|
db(cx,cy,pr,ck,c.n);
|
||||||
|
if(c.moons)for(const[mk,mb]of Object.entries(c.moons)){const od=mb.orb*zm*1.8;or(cx,cy,od);const an=(2*Math.PI*((jv-J2000_JD)%mb.P))/mb.P;db(cx+od*Math.cos(an),cy-od*Math.sin(an),mb.r*2.2*zm,mk,mb.n)}
|
||||||
|
}
|
||||||
|
function renderSB(ck,cx,cy,tm){const b=fv(ck);if(!b)return;const mx=Math.min(cx,cy)*.52,baseR=b.fr?b.fr*3:(b.r||4)*15,rr=Math.min(baseR*zm,mx);db(cx,cy,rr,ck,b.n)}
|
||||||
|
|
||||||
|
function render(){
|
||||||
|
const dpr=window.devicePixelRatio||1,W=cv.width/dpr,H=cv.height/dpr;
|
||||||
|
ctx.save();ctx.setTransform(dpr,0,0,dpr,0,0);
|
||||||
|
if(!stars||stars.width!==cv.width||stars.height!==cv.height)stars=mkStars(cv.width,cv.height);
|
||||||
|
ctx.drawImage(stars,0,0,W,H);
|
||||||
|
const v=vs[vs.length-1],cX=W/2+vO.x,cY=H/2+vO.y,tm=rT;
|
||||||
|
if(v.tp==='solar_system')renderSS(cX,cY,tm);else if(v.tp==='planet_system')renderPS(v.ck,cX,cY,tm);else renderSB(v.ck,cX,cY,tm);
|
||||||
|
if(hv){const b=fv(hv);if(b&&b._x!=null){ctx.beginPath();ctx.arc(b._x,b._y,(b._r||4)+5,0,Math.PI*2);ctx.strokeStyle='rgba(59,130,246,.7)';ctx.lineWidth=2;ctx.stroke()}}
|
||||||
|
ctx.restore();uip(v)
|
||||||
|
}
|
||||||
|
function fv(k){if(BD[k])return BD[k];for(const bk of Object.keys(BD)){const b=BD[bk];if(b.moons&&b.moons[k])return b.moons[k]}return null}
|
||||||
|
|
||||||
|
// ═══════════════════════════════════
|
||||||
|
// PANEL
|
||||||
|
// ═══════════════════════════════════
|
||||||
|
function uip(v){
|
||||||
|
let sk,sn;if(v.tp==='solar_system'){sk='solar_system';sn='Solar System'}else{sk=v.ck;const b=BD[v.ck];sn=b?b.n+(v.tp==='planet_system'?' System':''):v.ck}
|
||||||
|
document.getElementById('panel-title').textContent=sn;document.getElementById('panel-scope').textContent=sn;document.getElementById('scope-label').textContent=sn;document.getElementById('scope-indicator').textContent=sn;
|
||||||
|
const as=MOCK_ASSETS.filter(a=>a.sc.includes(sk)),ad=document.getElementById('panel-assets');
|
||||||
|
ad.innerHTML=as.length?as.map(a=>`<div class="asset-row"><span class="name">${a.n}</span><span class="loc">${a.loc}</span></div>`).join(''):'<div style="color:var(--muted);font-size:.76rem">No assets in scope</div>';
|
||||||
|
document.getElementById('panel-mission-count').textContent=as.filter(a=>a.loc!=='—'&&!a.loc.includes('Surface')).length;
|
||||||
|
const ns=new Date(rT).toISOString().slice(0,10),up=MOCK_EVENTS.filter(e=>e.d>=ns).slice(0,5),ed=document.getElementById('panel-events');
|
||||||
|
ed.innerHTML=up.length?up.map(e=>`<div class="event-row"><span class="date">${e.d}</span> — ${e.t}</div>`).join(''):'<div style="color:var(--muted);font-size:.76rem">No upcoming events</div>'
|
||||||
|
}
|
||||||
|
|
||||||
|
// ═══════════════════════════════════
|
||||||
|
// CLICK (drag threshold 6px)
|
||||||
|
// ═══════════════════════════════════
|
||||||
|
function ht(mx,my){
|
||||||
|
const v=vs[vs.length-1],cs=[];
|
||||||
|
if(v.tp==='solar_system'){cs.push({k:'sun'});for(const k of SO)cs.push({k})}
|
||||||
|
else if(v.tp==='planet_system'){const c=BD[v.ck];if(c){cs.push({k:v.ck});if(c.moons)for(const mk of Object.keys(c.moons))cs.push({k:mk})}}
|
||||||
|
else{const b=BD[v.ck];if(b)cs.push({k:v.ck})}
|
||||||
|
for(const{k}of cs){const b=fv(k);if(!b||b._x==null)continue;const dx=mx-b._x,dy=my-b._y,hr=(b._r||4)+7;if(dx*dx+dy*dy<=hr*hr)return k}return null
|
||||||
|
}
|
||||||
|
cv.addEventListener('mousedown',e=>{dg=true;dS={x:e.clientX,y:e.clientY};dL={x:e.clientX,y:e.clientY};dd=false});
|
||||||
|
window.addEventListener('mousemove',e=>{if(!dg||!dL)return;const dx=e.clientX-dS.x,dy=e.clientY-dS.y;if(Math.abs(dx)>6||Math.abs(dy)>6)dd=true;if(dd){vO.x+=e.clientX-dL.x;vO.y+=e.clientY-dL.y;dL={x:e.clientX,y:e.clientY};cv.style.cursor='grabbing';render()}});
|
||||||
|
window.addEventListener('mouseup',()=>{dg=false;dS=null;dL=null;cv.style.cursor=hv?'pointer':'grab'});
|
||||||
|
cv.addEventListener('click',e=>{if(dd){dd=false;return}const r=cv.getBoundingClientRect(),t=ht(e.clientX-r.left,e.clientY-r.top);if(!t)return;const v=vs[vs.length-1];if(v.tp==='solar_system'){const b=BD[t];vs.push({tp:b&&b.moons?'planet_system':'single_body',ck:t})}else if(v.tp==='planet_system'){if(t===v.ck)vs.push({tp:'single_body',ck:t});else vs.push({tp:'single_body',ck:t})}vO={x:0,y:0};ubb();render()});
|
||||||
|
cv.addEventListener('mousemove',e=>{if(dd)return;const r=cv.getBoundingClientRect(),t=ht(e.clientX-r.left,e.clientY-r.top);if(t!==hv){hv=t;cv.style.cursor=t?'pointer':(dg?'grabbing':'grab');render()}});
|
||||||
|
cv.addEventListener('wheel',e=>{e.preventDefault();zm=Math.max(.3,Math.min(4,zm*(e.deltaY>0?.9:1.1)));render()},{passive:false});
|
||||||
|
function goBack(){if(vs.length>1)vs.pop();vO={x:0,y:0};zm=1;ubb();render()}
|
||||||
|
function ubb(){document.getElementById('back-btn').classList.toggle('visible',vs.length>1)}
|
||||||
|
function setStyle(s){style=s;document.getElementById('btn-flat').classList.toggle('active',s==='flat');document.getElementById('btn-realistic').classList.toggle('active',s==='realistic');render()}
|
||||||
|
|
||||||
|
// ═══════════════════════════════════
|
||||||
|
// TIME
|
||||||
|
// ═══════════════════════════════════
|
||||||
|
function fmt(ms){const d=new Date(ms);return`${d.getUTCFullYear()}-${String(d.getUTCMonth()+1).padStart(2,'0')}-${String(d.getUTCDate()).padStart(2,'0')} ${String(d.getUTCHours()).padStart(2,'0')}:${String(d.getUTCMinutes()).padStart(2,'0')}`}
|
||||||
|
function uti(){const i=document.getElementById('sim-time-input');if(i&&document.activeElement!==i)i.value=fmt(simTime.getTime());const s=document.getElementById('time-slider');if(s&&document.activeElement!==s)s.value=Math.round(((simTime.getTime()-TMN)/TR)*1000)}
|
||||||
|
function st(ms){tT=Math.min(TMX,Math.max(TMN,ms));ani()}
|
||||||
|
function sh(d){st(simTime.getTime()+d*DAY)}
|
||||||
|
function rst(){st(new Date('2060-08-11T10:00:00Z').getTime())}
|
||||||
|
function ani(){if(af)cancelAnimationFrame(af);function step(){const d=tT-rT;if(Math.abs(d)<DAY*.3){rT=tT;simTime=new Date(tT);render();uti();af=null;return}rT+=d*.18;simTime=new Date(Math.round(rT));render();uti();af=requestAnimationFrame(step)}af=requestAnimationFrame(step)}
|
||||||
|
document.getElementById('time-slider').addEventListener('input',()=>{st(TMN+(parseInt(document.getElementById('time-slider').value)/1000)*TR)});
|
||||||
|
document.getElementById('sim-time-input').addEventListener('keydown',e=>{if(e.key==='Enter'){const p=new Date(document.getElementById('sim-time-input').value.trim()+'Z');if(!isNaN(p.getTime()))st(p.getTime());else{const i=document.getElementById('sim-time-input');i.style.borderColor='var(--danger)';setTimeout(()=>{i.style.borderColor=''},800)}}});
|
||||||
|
|
||||||
|
// ═══════════════════════════════════
|
||||||
|
rT=simTime.getTime();tT=simTime.getTime();uti();ubb();loadTextures();render();
|
||||||
|
window.addEventListener('resize',()=>{rs();render()});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
After Width: | Height: | Size: 107 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 115 KiB |
|
After Width: | Height: | Size: 114 KiB |
|
After Width: | Height: | Size: 115 KiB |
|
After Width: | Height: | Size: 107 KiB |
|
After Width: | Height: | Size: 131 KiB |
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 113 KiB |
|
After Width: | Height: | Size: 123 KiB |
|
After Width: | Height: | Size: 132 KiB |
|
After Width: | Height: | Size: 72 KiB |
|
After Width: | Height: | Size: 73 KiB |
|
After Width: | Height: | Size: 79 KiB |
@@ -0,0 +1,140 @@
|
|||||||
|
"""Test the location board demo click functionality using Playwright."""
|
||||||
|
import asyncio
|
||||||
|
from pathlib import Path
|
||||||
|
from playwright.async_api import async_playwright
|
||||||
|
|
||||||
|
DEMO_PATH = Path(__file__).resolve().parent / "location_board_demo.html"
|
||||||
|
FILE_URL = f"file:///{DEMO_PATH.as_posix()}"
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
async with async_playwright() as p:
|
||||||
|
browser = await p.chromium.launch(headless=True)
|
||||||
|
page = await browser.new_page(viewport={"width": 1400, "height": 900})
|
||||||
|
await page.goto(FILE_URL, wait_until="commit", timeout=10000)
|
||||||
|
await page.wait_for_timeout(3000) # let canvas render (skip font loading)
|
||||||
|
|
||||||
|
# --- Test 1: Check initial state ---
|
||||||
|
scope = await page.text_content("#scope-indicator")
|
||||||
|
print(f"Test 1 - Initial scope: {scope}")
|
||||||
|
|
||||||
|
# --- Test 2: Get canvas bounding box and try clicking a planet ---
|
||||||
|
canvas = page.locator("#map-canvas")
|
||||||
|
box = await canvas.bounding_box()
|
||||||
|
if not box:
|
||||||
|
print("ERROR: Canvas not found!")
|
||||||
|
await browser.close()
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f"Canvas: x={box['x']:.0f} y={box['y']:.0f} w={box['width']:.0f} h={box['height']:.0f}")
|
||||||
|
|
||||||
|
# The solar system view has the sun at center of canvas
|
||||||
|
# Planets orbit around it. Let's log JS state to see where bodies are.
|
||||||
|
body_positions = await page.evaluate("""
|
||||||
|
() => {
|
||||||
|
const result = [];
|
||||||
|
const keys = ['sun','mercury','venus','earth','mars','jupiter','saturn','uranus','neptune','pluto'];
|
||||||
|
for (const k of keys) {
|
||||||
|
const b = BD[k];
|
||||||
|
if (b) result.push({
|
||||||
|
key: k,
|
||||||
|
name: b.n,
|
||||||
|
sx: b._x,
|
||||||
|
sy: b._y,
|
||||||
|
r: b.r,
|
||||||
|
hasMoons: (b.moons && typeof b.moons==='object') ? Object.keys(b.moons).join(',') : 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
print("\nBody positions (CSS pixels from canvas top-left):")
|
||||||
|
for bp in body_positions:
|
||||||
|
sx = bp['sx']
|
||||||
|
sy = bp['sy']
|
||||||
|
if sx is not None and sy is not None:
|
||||||
|
# Convert to page coordinates (add canvas offset)
|
||||||
|
px = sx + box['x']
|
||||||
|
py = sy + box['y']
|
||||||
|
print(f" {bp['name']:12s} screen=({sx:6.1f},{sy:6.1f}) page=({px:6.1f},{py:6.1f}) r={bp['r']} moons={bp['hasMoons']}")
|
||||||
|
else:
|
||||||
|
print(f" {bp['name']:12s} screen=(undefined) - NOT RENDERED")
|
||||||
|
|
||||||
|
# --- Test 3: Try clicking Jupiter ---
|
||||||
|
jupiter = [b for b in body_positions if b['key'] == 'jupiter']
|
||||||
|
if jupiter and jupiter[0]['sx'] is not None:
|
||||||
|
jx = jupiter[0]['sx'] + box['x']
|
||||||
|
jy = jupiter[0]['sy'] + box['y']
|
||||||
|
jr = jupiter[0]['r'] + 5 # hit radius
|
||||||
|
print(f"\nTest 3 - Clicking Jupiter at page ({jx:.0f}, {jy:.0f}) r={jr}")
|
||||||
|
|
||||||
|
# Take pre-click screenshot
|
||||||
|
await page.screenshot(path=str(DEMO_PATH.parent / "pre_click.png"))
|
||||||
|
print(" Screenshot: pre_click.png")
|
||||||
|
|
||||||
|
# Click
|
||||||
|
await page.mouse.click(jx, jy)
|
||||||
|
await page.wait_for_timeout(500)
|
||||||
|
|
||||||
|
scope_after = await page.text_content("#scope-indicator")
|
||||||
|
back_visible = await page.evaluate("() => document.getElementById('back-btn').classList.contains('visible')")
|
||||||
|
print(f" After click - scope: {scope_after}")
|
||||||
|
print(f" Back button visible: {back_visible}")
|
||||||
|
print(f" View stack: {await page.evaluate('() => JSON.stringify(viewStack)')}")
|
||||||
|
|
||||||
|
await page.screenshot(path=str(DEMO_PATH.parent / "post_click.png"))
|
||||||
|
print(" Screenshot: post_click.png")
|
||||||
|
else:
|
||||||
|
print("\nTest 3 - Jupiter not found in rendered bodies!")
|
||||||
|
|
||||||
|
# --- Test 4: Check JS console for errors ---
|
||||||
|
console_msgs = []
|
||||||
|
page.on("console", lambda msg: console_msgs.append(f"[{msg.type}] {msg.text}"))
|
||||||
|
# Trigger a re-render
|
||||||
|
await page.evaluate("() => { render(); }")
|
||||||
|
await page.wait_for_timeout(300)
|
||||||
|
errors = [m for m in console_msgs if 'error' in m.lower() or 'Error' in m]
|
||||||
|
if errors:
|
||||||
|
print(f"\nJS errors: {errors}")
|
||||||
|
else:
|
||||||
|
print("\nNo JS errors detected")
|
||||||
|
|
||||||
|
# --- Test 5: Check didDrag state and event flow ---
|
||||||
|
print("\n--- Event Debug ---")
|
||||||
|
# Inject debug listener
|
||||||
|
await page.evaluate("""
|
||||||
|
() => {
|
||||||
|
const canvas = document.getElementById('map-canvas');
|
||||||
|
canvas.addEventListener('click', (e) => {
|
||||||
|
console.log('CLICK on canvas at', e.clientX, e.clientY, 'didDrag=', window._didDrag || didDrag);
|
||||||
|
}, true); // capture phase
|
||||||
|
|
||||||
|
// Store for inspection
|
||||||
|
window._debugClick = (x, y) => {
|
||||||
|
const r = canvas.getBoundingClientRect();
|
||||||
|
const t = hitTest(x - r.left, y - r.top);
|
||||||
|
console.log('hitTest result:', t, 'didDrag=', didDrag, 'dragOn=', dragOn);
|
||||||
|
return t;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
|
||||||
|
# Try click with explicit coordinates
|
||||||
|
if jupiter and jupiter[0]['sx'] is not None:
|
||||||
|
result = await page.evaluate(f"() => window._debugClick({jx - box['x']}, {jy - box['y']})")
|
||||||
|
print(f"hitTest at Jupiter position result: {result}")
|
||||||
|
|
||||||
|
# Also try clicking center of canvas minus offset
|
||||||
|
cx = box['width'] / 2
|
||||||
|
cy = box['height'] / 2
|
||||||
|
for offset_y in [0, -20, -50, -100, 50, 100]:
|
||||||
|
test_x = cx
|
||||||
|
test_y = cy + offset_y
|
||||||
|
r = await page.evaluate(f"() => window._debugClick({test_x}, {test_y})")
|
||||||
|
print(f" hitTest at canvas({test_x:.0f}, {test_y:.0f}): {r}")
|
||||||
|
|
||||||
|
await browser.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asyncio.run(main())
|
||||||
|
After Width: | Height: | Size: 116 KiB |
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 73 KiB |
|
After Width: | Height: | Size: 52 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 72 KiB |
|
After Width: | Height: | Size: 81 KiB |
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 63 KiB |
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 72 KiB |
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 63 KiB |
|
After Width: | Height: | Size: 67 KiB |
|
After Width: | Height: | Size: 67 KiB |
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 115 KiB |
|
After Width: | Height: | Size: 87 KiB |
@@ -0,0 +1,212 @@
|
|||||||
|
{
|
||||||
|
"total": 41,
|
||||||
|
"passed": 41,
|
||||||
|
"failed": 0,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"name": "1.1 侧边栏导航",
|
||||||
|
"ok": true,
|
||||||
|
"detail": "11 items"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "1.2 侧边栏默认展开",
|
||||||
|
"ok": true,
|
||||||
|
"detail": "2 expanded"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "1.3 当前页高亮",
|
||||||
|
"ok": true,
|
||||||
|
"detail": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "1.4 默认深色模式",
|
||||||
|
"ok": true,
|
||||||
|
"detail": "theme=dark"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "1.5 浅色模式",
|
||||||
|
"ok": true,
|
||||||
|
"detail": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "1.6 深色模式切换回",
|
||||||
|
"ok": true,
|
||||||
|
"detail": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "1.7 模拟时间显示",
|
||||||
|
"ok": true,
|
||||||
|
"detail": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "1.12 Modal打开",
|
||||||
|
"ok": true,
|
||||||
|
"detail": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "1.14 Cancel关闭",
|
||||||
|
"ok": true,
|
||||||
|
"detail": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "1.15 ESC关闭",
|
||||||
|
"ok": true,
|
||||||
|
"detail": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "2.1 标题",
|
||||||
|
"ok": true,
|
||||||
|
"detail": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "2.2 指标卡片",
|
||||||
|
"ok": true,
|
||||||
|
"detail": "3 cards"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "2.3 真实数据",
|
||||||
|
"ok": true,
|
||||||
|
"detail": "count=14"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "2.4 状态看板",
|
||||||
|
"ok": true,
|
||||||
|
"detail": "3 groups"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "2.6 资产链接",
|
||||||
|
"ok": true,
|
||||||
|
"detail": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "3.1 列表渲染",
|
||||||
|
"ok": true,
|
||||||
|
"detail": "14 rows"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "3.2 搜索",
|
||||||
|
"ok": true,
|
||||||
|
"detail": "14->1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "4.1 详情渲染",
|
||||||
|
"ok": true,
|
||||||
|
"detail": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "4.2 信息卡片",
|
||||||
|
"ok": true,
|
||||||
|
"detail": "4 cards"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "4.5 日志列表",
|
||||||
|
"ok": true,
|
||||||
|
"detail": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "5.1 Dock按钮",
|
||||||
|
"ok": true,
|
||||||
|
"detail": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "5.2 Modal内容",
|
||||||
|
"ok": true,
|
||||||
|
"detail": "15 options"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "5.6 Cancel",
|
||||||
|
"ok": true,
|
||||||
|
"detail": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "6.1 State Interval",
|
||||||
|
"ok": true,
|
||||||
|
"detail": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "6.5 Node添加",
|
||||||
|
"ok": true,
|
||||||
|
"detail": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "6.10 Modal关闭",
|
||||||
|
"ok": true,
|
||||||
|
"detail": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "7.1 Canvas",
|
||||||
|
"ok": true,
|
||||||
|
"detail": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "7.2 范围",
|
||||||
|
"ok": true,
|
||||||
|
"detail": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "8.1 Canvas",
|
||||||
|
"ok": true,
|
||||||
|
"detail": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "8.2 刻度行",
|
||||||
|
"ok": true,
|
||||||
|
"detail": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "8.3 段",
|
||||||
|
"ok": true,
|
||||||
|
"detail": "116 segments"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "8.4 事件",
|
||||||
|
"ok": true,
|
||||||
|
"detail": "238 events"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "8.5 资产链接",
|
||||||
|
"ok": true,
|
||||||
|
"detail": "14 links"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "8.6 JSON数据",
|
||||||
|
"ok": true,
|
||||||
|
"detail": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "8.7 滚轮缩放",
|
||||||
|
"ok": true,
|
||||||
|
"detail": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "9.engines",
|
||||||
|
"ok": true,
|
||||||
|
"detail": "30 rows"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "9.comms",
|
||||||
|
"ok": true,
|
||||||
|
"detail": "30 rows"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "9.tanks",
|
||||||
|
"ok": true,
|
||||||
|
"detail": "26 rows"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "9.vehicles",
|
||||||
|
"ok": true,
|
||||||
|
"detail": "10 rows"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "10.1 渲染",
|
||||||
|
"ok": true,
|
||||||
|
"detail": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "JS Errors",
|
||||||
|
"ok": true,
|
||||||
|
"detail": "0 errors"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 151 KiB |
|
After Width: | Height: | Size: 95 KiB |
|
After Width: | Height: | Size: 95 KiB |
|
After Width: | Height: | Size: 95 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 95 KiB |
|
After Width: | Height: | Size: 95 KiB |
|
After Width: | Height: | Size: 109 KiB |
|
After Width: | Height: | Size: 106 KiB |
|
After Width: | Height: | Size: 113 KiB |
|
After Width: | Height: | Size: 107 KiB |
|
After Width: | Height: | Size: 75 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 63 KiB |