提交 ee1b67e7 作者: ZhangLingKun

功能:用户定位展示(IP位置)

上级 328e197f
流水线 #7302 已通过 于阶段
in 5 分 2 秒
......@@ -20,6 +20,7 @@
"@amap/amap-jsapi-loader": "^1.0.1",
"@ant-design/cssinjs": "^1.17.2",
"@ant-design/icons": "^5.2.6",
"@reduxjs/toolkit": "^1.9.7",
"@types/styled-components": "^5.1.29",
"antd": "^5.11.0",
"axios": "^1.6.0",
......@@ -30,9 +31,13 @@
"js-base64": "^3.7.5",
"js-cookie": "^3.0.5",
"next": "14.0.1",
"next-redux-wrapper": "^8.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-infinite-scroll-component": "^6.1.0",
"react-redux": "^8.1.3",
"redux": "^4.2.1",
"redux-persist": "^6.0.0",
"styled-components": "^6.1.0"
},
"devDependencies": {
......
......@@ -2,7 +2,11 @@ import React, { useEffect } from 'react';
import { EnvironmentFilled } from '@ant-design/icons';
import { Button } from 'antd';
import { useRouter } from 'next/router';
import { useDispatch, useSelector } from 'react-redux';
import { HeaderWrap } from '@/components/layout/header/styled';
import { RootState } from '@/store';
import { AddressState, setAddress } from '@/store/address';
import getLocationByIP from '@/utils/getLocationByIP';
const HeaderView: React.FC<{
placeholder?: boolean;
......@@ -12,9 +16,19 @@ const HeaderView: React.FC<{
};
// 当前的路由数据
const router = useRouter();
// store
const dispatch = useDispatch();
// address
const address = useSelector(
(state: RootState) => state.address,
) as AddressState;
// 组件挂载
useEffect(() => {
console.log('HeaderView --->', router);
// console.log('HeaderView --->', router);
getLocationByIP().then((res) => {
dispatch(setAddress(res));
});
console.log('address --->', address);
}, [router]);
return (
<HeaderWrap>
......@@ -26,9 +40,9 @@ const HeaderView: React.FC<{
/>
</div>
<Button type={'link'} className="location-address">
杭州
{address?.city || '定位中...'}
</Button>
<div className="location-hello">Hi,欢迎来云享飞</div>
<div className="location-hello">Hi,欢迎来云享飞</div>
</div>
<div className="header-nav">
<div className="nav-tab">
......
......@@ -2,7 +2,8 @@ import { FC, useEffect } from 'react';
const MapContainer: FC<{
list: { lat: number; lon: number; name: string }[];
}> = ({ list }) => {
center?: [number, number];
}> = ({ list, center }) => {
// 地图实例
let map: any = null;
// 高德地图
......@@ -58,7 +59,7 @@ const MapContainer: FC<{
// 设置地图容器id
viewMode: '3D', // 是否为3D地图模式
zoom: 10, // 初始化地图级别
center: [119.96043, 30.04885], // 初始化地图中心点位置
center: center || [119.96043, 30.04885], // 初始化地图中心点位置
});
// 用户定位
map.plugin('AMap.Geolocation', () => {
......@@ -89,7 +90,7 @@ const MapContainer: FC<{
return () => {
map?.destroy();
};
}, [list]);
}, [list, center]);
return (
<div
id="container"
......
import React from 'react';
import { ConfigProvider, theme } from 'antd';
// eslint-disable-next-line import/order
import type { AppProps } from 'next/app';
import '../styles/animate.css';
import '../styles/globals.css';
import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/integration/react';
import { wrapper } from '@/store';
import themeConfig from '../theme/themeConfig';
const App = ({ Component, pageProps }: AppProps) => (
const App = ({ Component, pageProps, ...rest }: AppProps) => {
const { store } = wrapper.useWrappedStore(rest);
return (
<Provider store={store}>
{/* eslint-disable-next-line no-underscore-dangle */}
<PersistGate persistor={(store as any)?.__persistor} loading={null}>
<ConfigProvider
theme={{
algorithm: theme.compactAlgorithm,
......@@ -15,6 +23,9 @@ const App = ({ Component, pageProps }: AppProps) => (
>
<Component {...pageProps} />
</ConfigProvider>
);
</PersistGate>
</Provider>
);
};
export default App;
import React, { useEffect, useState } from 'react';
import { useSelector } from 'react-redux';
import styled from 'styled-components';
import { HomeAPI } from '@/api';
import MapContainer from '@/components/map';
import { RootState } from '@/store';
import { AddressState } from '@/store/address';
const HomeMapView = () => {
// 选择索引
......@@ -18,6 +21,10 @@ const HomeMapView = () => {
setCurrentIndex(index);
getServiceBitmap().then();
};
// address
const address = useSelector(
(state: RootState) => state.address,
) as AddressState;
// 地图网点列表
const [mapMarkerList, setMapMarkerList] = useState<
{ lat: number; lon: number; name: string }[]
......@@ -46,7 +53,10 @@ const HomeMapView = () => {
}, []);
return (
<HomeMapWrap>
<MapContainer list={mapMarkerList} />
<MapContainer
list={mapMarkerList}
center={[address?.longitude, address?.latitude]}
/>
<div className="map-wrap flex-around">
{networkTypeList?.map((i, j) => (
<div
......
// user.ts
import { createSlice } from '@reduxjs/toolkit';
import { HYDRATE } from 'next-redux-wrapper';
import { LocationType } from '@/utils/getLocationByIP';
export type AddressState = LocationType;
const initialState: AddressState | {} = {};
const addressSlice = createSlice({
name: 'address',
initialState,
reducers: {
setAddress: (state, action) => {
return action.payload;
},
},
extraReducers: {
// hydrated 用于获取服务端注入的state并选择更新
[HYDRATE]: (state, action) => {
return {
...action.payload.address,
};
},
},
});
export const { setAddress } = addressSlice.actions;
export default addressSlice.reducer;
// index.ts
import { combineReducers, configureStore } from '@reduxjs/toolkit';
import { createWrapper } from 'next-redux-wrapper';
import { persistReducer, persistStore } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import addressReducer from './address';
import userReducer from './user';
// 单独创建rootReducer供服务端和客户端创建store使用;
const rootReducer = combineReducers({
user: userReducer,
address: addressReducer,
});
const makeStore = () => {
const isServer = typeof window === 'undefined';
// 区分客户端和服务端,服务端不需要持久存储,客户端存在在localStorage中;
if (isServer) {
return configureStore({
reducer: rootReducer,
devTools: true,
});
}
const persistConfig = {
key: 'SHAREFLY-WEB-STORAGE',
whiteList: ['user', 'address'],
storage,
};
const persistedReducer = persistReducer(persistConfig, rootReducer);
const store = configureStore({
reducer: persistedReducer,
devTools: process.env.NODE_ENV !== 'production',
});
// @ts-ignore 只使用客户端渲染不需要此种做法,只需导出persistor即可;
// eslint-disable-next-line no-underscore-dangle
(store as any).__persistor = persistStore(store);
return store;
};
export type AppStore = ReturnType<typeof makeStore>;
export type RootState = ReturnType<AppStore['getState']>;
export const wrapper = createWrapper(makeStore);
// user.ts
import { createSlice } from '@reduxjs/toolkit';
import { HYDRATE } from 'next-redux-wrapper';
// @ts-ignore
export type UserState = API.User.UserInfo;
const initialState: UserState = {
username: '', // 示例
avatar: '',
};
const userSlice = createSlice({
name: 'user',
initialState,
reducers: {
setUser: (state, action) => {
return action.payload;
},
},
extraReducers: {
// hydrated 用于获取服务端注入的state并选择更新
[HYDRATE]: (state, action) => {
return {
...action.payload.user,
};
},
},
});
export const { setUser } = userSlice.actions;
export default userSlice.reducer;
import { message } from 'antd';
import Axios from 'axios';
// 位置的类型
export interface LocationType {
ip: string;
network: string;
version: string;
city: string;
region: string;
region_code: string;
country: string;
country_name: string;
country_code: string;
country_code_iso3: string;
country_capital: string;
country_tld: string;
continent_code: string;
in_eu: boolean;
postal: null;
latitude: number;
longitude: number;
timezone: string;
utc_offset: string;
country_calling_code: string;
currency: string;
currency_name: string;
languages: string;
country_area: number;
country_population: number;
asn: string;
org: string;
status: string;
info: string;
infocode: string;
province: string;
adcode: string;
rectangle: string;
}
// 根据用户的ip地址获取当前位置
export default function getLocationByIP(): Promise<LocationType> {
return new Promise((resolve, reject) => {
// 用户的ip信息
let ipInfo: LocationType;
// 获取用户的ip信息
const getIPInfo = async () => {
const res = await Axios.get('https://ipapi.co/json/');
if (res && res?.status === 200) {
ipInfo = res.data;
// console.log('获取位置1 --->', ipInfo);
return;
}
message.warning('获取位置失败').then();
};
// 根据ip信息获取位置信息
const getLocationByIPInfo = async () => {
const res = await Axios.get(
`https://restapi.amap.com/v3/ip?key=4f98d03f611b80f95a0ad56b044ffb59&ip=${ipInfo.ip}`,
);
if (res && res?.status === 200) {
const { province, city, adcode, rectangle } = res.data;
ipInfo.province = province;
ipInfo.city = city;
ipInfo.adcode = adcode;
ipInfo.rectangle = rectangle;
// console.log('获取位置 --->', ipInfo);
resolve(ipInfo);
return;
}
message.warning('获取位置失败').then();
reject(ipInfo);
};
// 自执行
(async () => {
await getIPInfo();
await getLocationByIPInfo();
})();
});
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论