| import * as React from 'react' | 
| import * as PIXI from 'pixi.js'; | 
| import { FormattedMessage, useIntl, useModel } from '@umijs/max'; | 
| import { Layout, Button, Flex, Row, Col, FloatButton, Select, Spin, AutoComplete } from 'antd'; | 
| const { Header, Content } = Layout; | 
| import { | 
|     AppstoreAddOutlined, | 
|     FileAddOutlined, | 
|     CompressOutlined, | 
|     SettingOutlined, | 
|     CloseOutlined | 
| } from '@ant-design/icons'; | 
| import './index.css' | 
| import { createStyles } from 'antd-style'; | 
| import Edit from './components/device'; | 
| import Settings from './components/settings' | 
| import * as Utils from './utils' | 
| import Player from './player'; | 
| import MapSearch from './header/search'; | 
| import MapDrawer from './drawer'; | 
|   | 
| const useStyles = createStyles(({ token }) => { | 
|     let dark = token.colorBgBase === '#000'; | 
|     return { | 
|         dark: dark, | 
|         layout: { | 
|             overflow: 'hidden', | 
|         }, | 
|         header: { | 
|             height: 64, | 
|             paddingInline: 48, | 
|             lineHeight: '64px', | 
|             padding: 0, | 
|         }, | 
|         flex: { | 
|             width: '100%', | 
|             height: '100%', | 
|             padding: '0 30px', | 
|         }, | 
|         content: { | 
|             backgroundColor: '#F8FAFB', | 
|             height: 'calc(100vh - 120px)' | 
|         }, | 
|         select: { | 
|             color: 'red', | 
|             fontWeight: 'bold', | 
|         }, | 
|         headerCol: { | 
|             paddingLeft: '50px' | 
|         } | 
|     }; | 
| }); | 
|   | 
| export const MapModel = Object.freeze({ | 
|     OBSERVER_MODEL: "1", | 
|     MOVABLE_MODEL: "2", | 
|     SETTINGS_MODEL: "3", | 
| }) | 
|   | 
| let player; | 
|   | 
| const Map = () => { | 
|     const intl = useIntl(); | 
|     const { initialState, setInitialState } = useModel('@@initialState'); | 
|     const { styles } = useStyles(); | 
|     const mapRef = React.useRef(); | 
|     const contentRef = React.useRef(); | 
|   | 
|     const [model, setModel] = React.useState(null); | 
|     const [deviceVisible, setDeviceVisible] = React.useState(false); | 
|     const [settingsVisible, setSettingsVisible] = React.useState(false); | 
|     const [windowSize, setWindowSize] = React.useState({ | 
|         width: window.innerWidth, | 
|         height: window.innerHeight, | 
|     }); | 
|     const [app, setApp] = React.useState(null); | 
|     const [mapContainer, setMapContainer] = React.useState(null); | 
|     const [didClickSprite, setDidClickSprite] = React.useState(false); | 
|     const [spriteBySettings, setSpriteBySettings] = React.useState(null); | 
|     const prevSpriteBySettingsRef = React.useRef(); | 
|     const [drawerVisible, setDrawerVisible] = React.useState(false); | 
|     const [dataFetched, setDataFetched] = React.useState(false); | 
|     const [curSprite, setCurSPrite] = React.useState(null); | 
|     const prevCurSpriteRef = React.useRef(); | 
|   | 
|     // init func | 
|     React.useEffect(() => { | 
|         const initialize = async () => { | 
|             player = new Player(mapRef.current, styles.dark, didClickSprite); | 
|             setApp(player.app); | 
|             setMapContainer(player.mapContainer); | 
|             Utils.syncApp(player.app); | 
|             Utils.syncMapContainer(player.mapContainer); | 
|   | 
|             const handleResize = () => { | 
|                 setWindowSize({ | 
|                     width: window.innerWidth, | 
|                     height: window.innerHeight, | 
|                 }); | 
|             }; | 
|             window.addEventListener('resize', handleResize); | 
|             await Utils.fetchMapData(intl); | 
|             setDataFetched(true); | 
|             setModel(MapModel.OBSERVER_MODEL) | 
|             setTimeout(() => { | 
|                 player.adaptScreen(); | 
|             }, 200) | 
|         } | 
|         initialize(); | 
|     }, []); | 
|   | 
|     // resize | 
|     React.useEffect(() => { | 
|         if (!app) { | 
|             return; | 
|         } | 
|         const width = contentRef.current.offsetWidth; | 
|         const height = contentRef.current.offsetHeight; | 
|         app.renderer.resize(width, height); | 
|         if (model !== MapModel.OBSERVER_MODEL) { | 
|             player.showGridlines(); | 
|         } | 
|     }, [app, mapContainer, windowSize]) | 
|   | 
|     // model | 
|     React.useEffect(() => { | 
|         if (!mapContainer && !dataFetched) { | 
|             return; | 
|         } | 
|         switch (model) { | 
|             case MapModel.OBSERVER_MODEL: | 
|   | 
|                 player.hideGridlines(); | 
|                 player.hideStarryBackground(); | 
|   | 
|                 player.activateMapEvent(null); | 
|   | 
|                 Utils.removeSelectedEffect(); | 
|                 setCurSPrite(null); | 
|                 setDeviceVisible(false); | 
|                 setSettingsVisible(false); | 
|   | 
|                 mapContainer.children.forEach(child => { | 
|                     Utils.viewFeature(child, setCurSPrite); | 
|                 }) | 
|                 break | 
|             case MapModel.MOVABLE_MODEL: | 
|   | 
|                 player.showGridlines(); | 
|                 player.hideStarryBackground(); | 
|   | 
|                 player.activateMapEvent(Utils.MapEvent.SELECTION_BOX); | 
|   | 
|                 Utils.removeSelectedEffect(); | 
|                 setSpriteBySettings(null); | 
|                 setSettingsVisible(false); | 
|                 setDrawerVisible(false); | 
|   | 
|                 mapContainer.children.forEach(child => { | 
|                     Utils.beMovable(child, setDidClickSprite); | 
|                 }) | 
|                 break | 
|             case MapModel.SETTINGS_MODEL: | 
|   | 
|                 player.showGridlines(); | 
|                 player.showStarryBackground(); | 
|   | 
|                 player.activateMapEvent(null); | 
|   | 
|                 setDeviceVisible(false); | 
|                 setDrawerVisible(false); | 
|   | 
|                 mapContainer.children.forEach(child => { | 
|                     Utils.beSettings(child, setSpriteBySettings, setDidClickSprite); | 
|                 }) | 
|                 break | 
|             default: | 
|                 break | 
|         } | 
|     }, [model]); | 
|   | 
|     // Add New Device | 
|     const onDrop = (sprite, type, x, y) => { | 
|         const { mapX, mapY } = Utils.getRealPosition(x, y, mapContainer); | 
|         sprite.x = mapX; | 
|         sprite.y = mapY; | 
|   | 
|         Utils.initSprite(sprite, type); | 
|         mapContainer.addChild(sprite); | 
|         Utils.beMovable(sprite, setDidClickSprite); | 
|     }; | 
|   | 
|     // watch curSprite | 
|     React.useEffect(() => { | 
|         if (!mapContainer) { | 
|             return; | 
|         } | 
|         prevCurSpriteRef.current = curSprite; | 
|         if (curSprite && prevCurSprite !== curSprite) { | 
|             Utils.removeSelectedEffect(); | 
|         } | 
|         if (curSprite) { | 
|             if (model === MapModel.OBSERVER_MODEL) { | 
|                 Utils.showSelectedEffect(curSprite) | 
|                 setDrawerVisible(true) | 
|             } | 
|         } else { | 
|             Utils.removeSelectedEffect(); | 
|         } | 
|     }, [curSprite]); | 
|     const prevCurSprite = prevCurSpriteRef.current; | 
|   | 
|     // didClickSprite, stop triggers both sprite click and play's selection boxs | 
|     React.useEffect(() => { | 
|         player.updateDidClickSprite(didClickSprite); | 
|     }, [didClickSprite]) | 
|   | 
|     // watch spriteBySettings | 
|     React.useEffect(() => { | 
|         if (!mapContainer) { | 
|             return; | 
|         } | 
|         prevSpriteBySettingsRef.current = spriteBySettings; | 
|         if (spriteBySettings && prevSpriteBySettings !== spriteBySettings) { | 
|             Utils.removeSelectedEffect(); | 
|         } | 
|         if (spriteBySettings) { | 
|             Utils.showSelectedEffect(spriteBySettings) | 
|             setSettingsVisible(true); | 
|         } else { | 
|             Utils.removeSelectedEffect(); | 
|         } | 
|     }, [spriteBySettings]) | 
|     const prevSpriteBySettings = prevSpriteBySettingsRef.current; | 
|   | 
|     const settingsFinish = (values, fn) => { | 
|         fn(); | 
|         // setSettingsVisible(false); | 
|         // setSpriteBySettings(null); | 
|     } | 
|   | 
|     return ( | 
|         <> | 
|             <Layout className={styles.layout}> | 
|                 <Header className={styles.header}> | 
|                     <Row style={{ height: '100%' }}> | 
|                         <Col className={styles.headerCol} span={12} style={{}}> | 
|                             {dataFetched && ( | 
|                                 <MapSearch | 
|                                     model={model} | 
|                                     setModel={setModel} | 
|                                     ModelEnum={MapModel} | 
|                                     curSprite={curSprite} | 
|                                     setCurSPrite={setCurSPrite} | 
|                                     setSpriteBySettings={setSpriteBySettings} | 
|                                 /> | 
|                             )} | 
|                         </Col> | 
|                         <Col span={12} style={{ backgroundColor: styles.dark ? '#2C3A47' : '#4a69bd' }}> | 
|                             <Flex className={styles.flex} gap={'large'} justify={'flex-end'} align={'center'}> | 
|   | 
|                                 {model === MapModel.OBSERVER_MODEL && ( | 
|                                     <> | 
|                                         <Button | 
|                                             className='map-header-button' | 
|                                             size={'large'} | 
|                                             onClick={async () => { | 
|                                                 await Utils.fetchMapData(intl); | 
|   | 
|                                                 player.hideGridlines(); | 
|                                                 player.hideStarryBackground(); | 
|   | 
|                                                 player.activateMapEvent(null); | 
|   | 
|                                                 Utils.removeSelectedEffect(); | 
|                                                 setCurSPrite(null); | 
|                                                 setDeviceVisible(false); | 
|                                                 setSettingsVisible(false); | 
|                                                 setDrawerVisible(false); | 
|   | 
|                                                 mapContainer.children.forEach(child => { | 
|                                                     Utils.viewFeature(child, setCurSPrite); | 
|                                                 }) | 
|   | 
|                                             }} | 
|                                         > | 
|                                             <FormattedMessage id='map.load' defaultMessage='加载地图' /> | 
|                                         </Button> | 
|                                     </> | 
|                                 )} | 
|   | 
|                                 {model !== MapModel.OBSERVER_MODEL && ( | 
|                                     <> | 
|                                         <Button | 
|                                             className='map-header-button' | 
|                                             size={'large'} | 
|                                             onClick={() => { | 
|                                                 Utils.clearMapData(intl); | 
|                                             }} | 
|                                         > | 
|                                             <FormattedMessage id='map.clear' defaultMessage='清除地图' /> | 
|                                         </Button> | 
|                                         <Button | 
|                                             className='map-header-button' | 
|                                             size={'large'} | 
|                                             onClick={() => { | 
|                                                 Utils.saveMapData(intl); | 
|                                             }} | 
|                                         > | 
|                                             <FormattedMessage id='map.save' defaultMessage='保存地图' /> | 
|                                         </Button> | 
|                                     </> | 
|                                 )} | 
|   | 
|                                 <Select | 
|                                     className='map-header-select' | 
|                                     size={'large'} | 
|                                     defaultValue={MapModel.OBSERVER_MODEL} | 
|                                     style={{ | 
|                                         width: 180, | 
|                                     }} | 
|                                     onChange={setModel} | 
|                                     options={[ | 
|                                         { | 
|                                             value: MapModel.OBSERVER_MODEL, | 
|                                             label: intl.formatMessage({ id: 'map.model.observer', defaultMessage: '观察者模式' }), | 
|                                         }, | 
|                                         { | 
|                                             value: MapModel.MOVABLE_MODEL, | 
|                                             label: intl.formatMessage({ id: 'map.model.editor', defaultMessage: '编辑者模式' }), | 
|                                         }, | 
|                                     ]} | 
|                                 /> | 
|                             </Flex> | 
|                         </Col> | 
|                     </Row> | 
|                 </Header> | 
|                 <Content ref={contentRef} className={styles.content}> | 
|                     <div ref={mapRef} style={{ position: "relative" }} > | 
|                         <FloatButton.Group | 
|                             shape="square" | 
|                             style={{ | 
|                                 left: 35, | 
|                                 bottom: 35 | 
|                             }} | 
|                         > | 
|                             <FloatButton | 
|                                 icon={<CompressOutlined />} | 
|                                 onClick={() => { | 
|                                     player.adaptScreen(); | 
|                                 }} | 
|                             /> | 
|                         </FloatButton.Group> | 
|   | 
|                         <FloatButton.Group | 
|                             hidden={model === MapModel.OBSERVER_MODEL} | 
|                             style={{ | 
|                                 left: 35, | 
|                                 bottom: window.innerHeight / 2 | 
|                             }} | 
|                             icon={<AppstoreAddOutlined />} | 
|                         > | 
|                             <FloatButton | 
|                                 hidden={model === MapModel.OBSERVER_MODEL} | 
|                                 type={deviceVisible ? 'primary' : 'default'} | 
|                                 tooltip={<div><FormattedMessage id='map.device.add' defaultMessage='添加设备' /></div>} | 
|                                 icon={<FileAddOutlined />} | 
|                                 onClick={() => { | 
|                                     if (deviceVisible) { | 
|                                         setDeviceVisible(false); | 
|                                     } else { | 
|                                         setDeviceVisible(true); | 
|                                         setModel(MapModel.MOVABLE_MODEL); | 
|                                     } | 
|                                 }} | 
|                             /> | 
|                             <FloatButton | 
|                                 hidden={model === MapModel.OBSERVER_MODEL} | 
|                                 type={model === MapModel.SETTINGS_MODEL ? 'primary' : 'default'} | 
|                                 tooltip={<div><FormattedMessage id='map.device.oper' defaultMessage='参数设置' /></div>} | 
|                                 icon={<SettingOutlined />} | 
|                                 onClick={() => { | 
|                                     setModel(model === MapModel.SETTINGS_MODEL ? MapModel.MOVABLE_MODEL : MapModel.SETTINGS_MODEL) | 
|                                 }} | 
|                             /> | 
|                         </FloatButton.Group> | 
|                     </div> | 
|                 </Content> | 
|             </Layout > | 
|   | 
|             <MapDrawer | 
|                 open={drawerVisible} | 
|                 curSprite={curSprite} | 
|                 refCurr={mapRef.current} | 
|                 onCancel={() => { | 
|                     setCurSPrite(null); | 
|                     setDrawerVisible(false); | 
|                 }} | 
|             /> | 
|   | 
|             <Edit | 
|                 open={deviceVisible} | 
|                 onCancel={() => { | 
|                     setDeviceVisible(false); | 
|                 }} | 
|                 refCurr={mapRef.current} | 
|                 onDrop={onDrop} | 
|             /> | 
|   | 
|             <Settings | 
|                 open={settingsVisible} | 
|                 curSprite={spriteBySettings} | 
|                 onCancel={() => { | 
|                     setSettingsVisible(false); | 
|                     setSpriteBySettings(null); | 
|                 }} | 
|                 setSpriteBySettings={setSpriteBySettings} | 
|                 setDidClickSprite={setDidClickSprite} | 
|                 refCurr={mapRef.current} | 
|                 onSubmit={settingsFinish} | 
|             /> | 
|         </> | 
|     ) | 
| } | 
|   | 
| export default Map; |