refactor(front-end): switch to web-based map navigation and update layouts
- Update `openMapDirection` to `openWebMapRaw` in `utils/front.ts` to open maps in a new browser tab (`_blank`) using raw WGS-84 coordinates. - Adjust URL schemes for Google, AMap, Baidu, Tencent, and Apple Maps to support direct web navigation with `coordinate` parameters. - Update `ClubSummary.tsx` to use `openWebMapRaw` and change the map button label from "Navigation" to "View Location" with a new icon. - Add `padding-bottom` to `AppBar.tsx` in `AppBarLayout.tsx` to accommodate the increased bottom padding required by the updated AppBar styling. - Remove the "Home" FloatButton from `ProfilePage.tsx` and update `routes.tsx` to remove the unused `ActionButtonLayout` import. - Update navigation handlers in `AppBar.tsx` to use `replace: true` for smoother state management without creating new browser history entries.
This commit is contained in:
parent
2f8ce1711f
commit
4b69ed3b84
@ -1,4 +1,4 @@
|
|||||||
import { CalendarOutlined, HeartOutlined, ScheduleOutlined, SearchOutlined, UserOutlined } from "@ant-design/icons";
|
import { HeartOutlined, ScheduleOutlined, SearchOutlined, UserOutlined } from "@ant-design/icons";
|
||||||
import { Button, Flex } from "antd";
|
import { Button, Flex } from "antd";
|
||||||
import { useNavigate } from "react-router";
|
import { useNavigate } from "react-router";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
@ -12,6 +12,7 @@ const StyledContainer = styled.div`
|
|||||||
background: #181818;
|
background: #181818;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
|
padding-bottom: 34px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
.ant-btn {
|
.ant-btn {
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
@ -25,22 +26,22 @@ export const AppBar = () => {
|
|||||||
<Button
|
<Button
|
||||||
type="text"
|
type="text"
|
||||||
icon={<ScheduleOutlined size={64} />}
|
icon={<ScheduleOutlined size={64} />}
|
||||||
onClick={() => navigate('/')}
|
onClick={() => navigate('/', { replace: true })}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
type="text"
|
type="text"
|
||||||
icon={<HeartOutlined />}
|
icon={<HeartOutlined />}
|
||||||
onClick={() => navigate('/fav-players')}
|
onClick={() => navigate('/fav-players', { replace: true })}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
type="text"
|
type="text"
|
||||||
icon={<SearchOutlined />}
|
icon={<SearchOutlined />}
|
||||||
onClick={() => navigate('/find')}
|
onClick={() => navigate('/find', { replace: true })}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
type="text"
|
type="text"
|
||||||
icon={<UserOutlined />}
|
icon={<UserOutlined />}
|
||||||
onClick={() => navigate('/user-center')}
|
onClick={() => navigate('/user-center', { replace: true })}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
</StyledContainer>
|
</StyledContainer>
|
||||||
|
|||||||
@ -3,9 +3,9 @@ import { ChangeBackground } from "./ChangeBackground";
|
|||||||
import { useMemo, useState } from "react";
|
import { useMemo, useState } from "react";
|
||||||
import { useRequest } from "ahooks";
|
import { useRequest } from "ahooks";
|
||||||
import type { ClubDetail } from "../types";
|
import type { ClubDetail } from "../types";
|
||||||
import { isMobile, MapType, openMapDirection } from "../utils/front";
|
import { isMobile, MapType, openWebMapRaw } from "../utils/front";
|
||||||
import type { ItemType } from "antd/es/menu/interface";
|
import type { ItemType } from "antd/es/menu/interface";
|
||||||
import { CarOutlined, NotificationOutlined } from "@ant-design/icons";
|
import { NotificationOutlined, PushpinOutlined } from "@ant-design/icons";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
clubId: string;
|
clubId: string;
|
||||||
@ -33,17 +33,14 @@ export const ClubSummary = (props: Props) => {
|
|||||||
{
|
{
|
||||||
label: '高德地图',
|
label: '高德地图',
|
||||||
key: MapType.AMAP,
|
key: MapType.AMAP,
|
||||||
disabled: !isMobileDevice,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '腾讯地图',
|
label: '腾讯地图',
|
||||||
key: MapType.TENCENT,
|
key: MapType.TENCENT,
|
||||||
disabled: !isMobileDevice,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '百度地图',
|
label: '百度地图',
|
||||||
key: MapType.BAIDU,
|
key: MapType.BAIDU,
|
||||||
disabled: !isMobileDevice,
|
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}, [info]);
|
}, [info]);
|
||||||
@ -65,11 +62,11 @@ export const ClubSummary = (props: Props) => {
|
|||||||
trigger={['click']}
|
trigger={['click']}
|
||||||
menu={{
|
menu={{
|
||||||
items: mapMenu,
|
items: mapMenu,
|
||||||
onClick: (e) => openMapDirection(e.key as MapType, info.geo!),
|
onClick: (e) => openWebMapRaw(e.key as MapType, info.geo!),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Button icon={<CarOutlined />}>
|
<Button icon={<PushpinOutlined />}>
|
||||||
导航
|
查看位置
|
||||||
</Button>
|
</Button>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import { AppBar } from "../AppBar";
|
|||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
|
|
||||||
const StyledContainer = styled.div`
|
const StyledContainer = styled.div`
|
||||||
padding-bottom: 54px;
|
padding-bottom: 90px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
`;
|
`;
|
||||||
export const AppBarLayout = () => {
|
export const AppBarLayout = () => {
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import React, { useEffect, useMemo } from "react";
|
import React, { useEffect, useMemo } from "react";
|
||||||
import { Link, useLoaderData, useNavigate } from "react-router";
|
import { Link, useLoaderData, useNavigate } from "react-router";
|
||||||
import { Avatar, Divider, Flex, FloatButton, Image, Typography } from "antd";
|
import { Avatar, Divider, Flex, Image, Typography } from "antd";
|
||||||
import { HomeOutlined } from "@ant-design/icons";
|
|
||||||
import { useTitle } from "ahooks";
|
import { useTitle } from "ahooks";
|
||||||
import type { XCXProfile } from "../types/profile";
|
import type { XCXProfile } from "../types/profile";
|
||||||
import User from "../components/User";
|
import User from "../components/User";
|
||||||
@ -108,7 +107,6 @@ export default function ProfilePage() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ChangeBackground url={profile?.realpic} />
|
<ChangeBackground url={profile?.realpic} />
|
||||||
<FloatButton icon={<HomeOutlined />} onClick={() => navigate('/')} />
|
|
||||||
<Flex vertical align="center" style={{ padding: 24 }}>
|
<Flex vertical align="center" style={{ padding: 24 }}>
|
||||||
<Avatar src={profile?.realpic} size={128} />
|
<Avatar src={profile?.realpic} size={128} />
|
||||||
<Typography.Title level={2}>
|
<Typography.Title level={2}>
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import { CallbackPage } from "./page/Logto/Callback";
|
|||||||
import App from "./App";
|
import App from "./App";
|
||||||
import { ClubEventsPage } from "./page/ClubEvents";
|
import { ClubEventsPage } from "./page/ClubEvents";
|
||||||
import { HydrateFallback } from "./components/HydrateFallback";
|
import { HydrateFallback } from "./components/HydrateFallback";
|
||||||
import { ActionButtonLayout, AppBarLayout } from './components/Layout';
|
import { AppBarLayout } from './components/Layout';
|
||||||
|
|
||||||
export const route = createBrowserRouter([
|
export const route = createBrowserRouter([
|
||||||
{
|
{
|
||||||
|
|||||||
@ -23,44 +23,59 @@ export const isMobile = (): boolean => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 唤起地图导航
|
* 调起 Web 版地图 (直接使用原始坐标,不进行纠偏转换)
|
||||||
* @param type 地图类型
|
|
||||||
* @param location 坐标点 (默认输入为 GCJ-02 坐标)
|
|
||||||
*/
|
*/
|
||||||
export const openMapDirection = (type: MapType, location: MapLocation): void => {
|
export const openWebMapRaw = (type: MapType, location: MapLocation): void => {
|
||||||
const { lat, lng, name = '目的地' } = location;
|
const { lat, lng, name = 'Location' } = location;
|
||||||
const encodedName = encodeURIComponent(name);
|
const encodedName = encodeURIComponent(name);
|
||||||
|
|
||||||
let url = '';
|
let url = '';
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case MapType.GOOGLE:
|
case MapType.GOOGLE:
|
||||||
/**
|
/**
|
||||||
* Google Maps Scheme
|
* Google Maps Web API
|
||||||
* saddr: 起点 (为空则默认为当前位置)
|
* 使用 query 参数标注位置
|
||||||
* daddr: 终点经纬度
|
|
||||||
* directionsmode: 导航模式 (driving, walking, bicycling, transit)
|
|
||||||
*/
|
*/
|
||||||
url = `https://www.google.com/maps/dir/?api=1&destination=${lat},${lng}`;
|
url = `https://www.google.com/maps/search/?api=1&query=${lat},${lng}`;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MapType.AMAP:
|
case MapType.AMAP:
|
||||||
url = `iosamap://path?sourceApplication=appName&dlat=${lat}&dlon=${lng}&dname=${encodedName}&dev=0&t=0`;
|
/**
|
||||||
|
* 高德 Web 标注
|
||||||
|
* coordinate=wgs84 声明输入的是原始 GPS 坐标
|
||||||
|
* position 参数顺序为 [经度, 纬度]
|
||||||
|
*/
|
||||||
|
url = `https://uri.amap.com/marker?position=${lng},${lat}&name=${encodedName}&coordinate=wgs84&callnative=0`;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MapType.BAIDU:
|
case MapType.BAIDU:
|
||||||
url = `baidumap://map/direction?destination=name:${encodedName}|latlng:${lat},${lng}&mode=driving&coord_type=gcj02`;
|
/**
|
||||||
|
* 百度 Web 标注
|
||||||
|
* coord_type=wgs84 声明输入的是原始 GPS 坐标
|
||||||
|
*/
|
||||||
|
url = `http://api.map.baidu.com/marker?location=${lat},${lng}&title=${encodedName}&content=${encodedName}&coord_type=wgs84&output=html&src=webapp`;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MapType.TENCENT:
|
case MapType.TENCENT:
|
||||||
url = `qqmap://map/routeplan?type=drive&to=${encodedName}&tocoord=${lat},${lng}&referer=myapp`;
|
/**
|
||||||
|
* 腾讯 Web 标注
|
||||||
|
* 腾讯 Web API 目前对 wgs84 直接支持较弱,通常默认 gcj02,
|
||||||
|
* 但在 Web 端直接传坐标,系统会尝试解析。
|
||||||
|
*/
|
||||||
|
url = `https://apis.map.qq.com/uri/v1/marker?marker=coord:${lat},${lng};title:${encodedName}&referer=myapp`;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MapType.APPLE:
|
case MapType.APPLE:
|
||||||
url = `http://maps.apple.com/?daddr=${lat},${lng}&q=${encodedName}`;
|
/**
|
||||||
|
* Apple Maps Web
|
||||||
|
* ll: 经纬度, q: 标注点名称
|
||||||
|
*/
|
||||||
|
url = `https://maps.apple.com/?ll=${lat},${lng}&q=${encodedName}`;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (url) {
|
if (url) {
|
||||||
window.open(url);
|
window.open(url, '_blank');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
23
src/utils/mapUtils.ts
Normal file
23
src/utils/mapUtils.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import gcoord, { CRSTypes } from 'gcoord';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断并转换坐标
|
||||||
|
*/
|
||||||
|
export const getCorrectCoords = (lng: number, lat: number) => {
|
||||||
|
// gcoord.transform 会自动判断:
|
||||||
|
// 如果输入坐标在海外,它通常会返回原值(或根据算法处理)
|
||||||
|
// 但我们可以手动配合它的边界逻辑
|
||||||
|
|
||||||
|
// 更加显式的判断方法:
|
||||||
|
// gcoord 内部其实维护了一个边界多边形,你可以直接进行转换测试
|
||||||
|
const result = gcoord.transform(
|
||||||
|
[lng, lat], // 目标坐标
|
||||||
|
gcoord.WGS84, // 当前坐标系
|
||||||
|
gcoord.GCJ02 // 目标坐标系
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
lng: result[0],
|
||||||
|
lat: result[1]
|
||||||
|
};
|
||||||
|
};
|
||||||
Loading…
Reference in New Issue
Block a user