定位记录 demo
This commit is contained in:
parent
c61f6fcc7e
commit
fc26de7744
5
app/wails/frontend/components.d.ts
vendored
5
app/wails/frontend/components.d.ts
vendored
@ -10,12 +10,17 @@ declare module 'vue' {
|
||||
CPUUsage: typeof import('./src/components/system/cpu/CPUUsage.vue')['default']
|
||||
CPUUsageChart: typeof import('./src/components/system/cpu/CPUUsageChart.vue')['default']
|
||||
ElButton: typeof import('element-plus/es')['ElButton']
|
||||
ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
|
||||
ElCol: typeof import('element-plus/es')['ElCol']
|
||||
ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
|
||||
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
|
||||
ElIcon: typeof import('element-plus/es')['ElIcon']
|
||||
ElInput: typeof import('element-plus/es')['ElInput']
|
||||
ElProgress: typeof import('element-plus/es')['ElProgress']
|
||||
ElRow: typeof import('element-plus/es')['ElRow']
|
||||
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
|
||||
ElStatistic: typeof import('element-plus/es')['ElStatistic']
|
||||
ElSwitch: typeof import('element-plus/es')['ElSwitch']
|
||||
ElTableV2: typeof import('element-plus/es')['ElTableV2']
|
||||
ElTabPane: typeof import('element-plus/es')['ElTabPane']
|
||||
ElTabs: typeof import('element-plus/es')['ElTabs']
|
||||
|
@ -4,6 +4,14 @@
|
||||
<meta charset="UTF-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||
<title>wails</title>
|
||||
|
||||
<script type="text/javascript">
|
||||
window._AMapSecurityConfig = {
|
||||
// serviceHost:'您的代理服务器域名或地址/_AMapService',
|
||||
// 例如 :serviceHost:'http://1.1.1.1:80/_AMapService',
|
||||
securityJsCode: "5d02234aa9de86567268e33be3c22aef"
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
@ -19,7 +19,9 @@
|
||||
"vite": "^4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@amap/amap-jsapi-loader": "^1.0.1",
|
||||
"@vueuse/core": "^10.2.1",
|
||||
"axios": "^1.4.0",
|
||||
"echarts": "^5.4.2",
|
||||
"element-plus": "^2.3.7",
|
||||
"eruda": "^3.0.0",
|
||||
|
@ -1 +1 @@
|
||||
d09bd561bfdf7ff46efd368fe8aa4abe
|
||||
56a069904657d2e7c850e467b8924bd9
|
@ -31,6 +31,14 @@ const routes = [
|
||||
groups: "/",
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/location-record",
|
||||
component: ()=>import("src/views/tabs/location-record/index.vue"),
|
||||
meta: {
|
||||
title: "定位记录",
|
||||
groups: "/"
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/test",
|
||||
component: ()=>import("src/views/tabs/home/Test.vue"),
|
||||
|
62
app/wails/frontend/src/utils/axios/index.ts
Normal file
62
app/wails/frontend/src/utils/axios/index.ts
Normal file
@ -0,0 +1,62 @@
|
||||
import axios, {AxiosInstance, AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig} from "axios";
|
||||
import {ElNotification} from "element-plus";
|
||||
|
||||
export function getAxiosInstance(axiosRequestConfig: AxiosRequestConfig): AxiosInstance {
|
||||
let instance = axios.create(axiosRequestConfig);
|
||||
// 全局 请求拦截器
|
||||
instance.interceptors.request.use(async (config: InternalAxiosRequestConfig) => {
|
||||
let headers = config.headers || {}
|
||||
return config;
|
||||
})
|
||||
// 全局 响应拦截器
|
||||
instance.interceptors.response.use(successCB, rejectCB);
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
||||
// 请求成功回调
|
||||
export function successCB(response: AxiosResponse | Promise<AxiosResponse>): AxiosResponse | Promise<AxiosResponse> {
|
||||
function processResponse(response: AxiosResponse) {
|
||||
if (response.status === 200) { // 响应成功
|
||||
if (response.data.code === 401) {
|
||||
ElNotification.warning({
|
||||
message: "登录状态失效, 请重新登录",
|
||||
})
|
||||
console.log(response,response.data.msg)
|
||||
} else if (response.data.code === 403) {
|
||||
setTimeout(() => {
|
||||
console.log(response,response.data.msg)
|
||||
}, 0)
|
||||
}
|
||||
return response;
|
||||
} else {
|
||||
return Promise.reject(response);
|
||||
}
|
||||
}
|
||||
|
||||
if (response instanceof Promise) {
|
||||
let resp: AxiosResponse
|
||||
return new Promise((resolve)=>{
|
||||
response.then((r: AxiosResponse) => {
|
||||
resp = r;
|
||||
resolve(processResponse(resp))
|
||||
});
|
||||
})
|
||||
} else {
|
||||
return processResponse(response);
|
||||
}
|
||||
}
|
||||
|
||||
// 请求失败回调
|
||||
export function rejectCB(error: any): any {
|
||||
let response = error.response
|
||||
if(response.status === 401){
|
||||
return response;
|
||||
} else if(response.status === 403){
|
||||
setTimeout(() => {
|
||||
console.log(response.data.msg)
|
||||
}, 0)
|
||||
return response;
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
@ -151,6 +151,7 @@ const cpuInfo = useCpuInfo()
|
||||
|
||||
<el-button @click="addTab()">添加tab</el-button>
|
||||
<el-button @click="router.push('/environment')">环境变量</el-button>
|
||||
<el-button @click="router.push('/location-record')">定位记录</el-button>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
@ -0,0 +1,32 @@
|
||||
import {getAxiosInstance} from "src/utils/axios";
|
||||
|
||||
export const http = getAxiosInstance({
|
||||
timeout: 30 * 1000
|
||||
})
|
||||
|
||||
export default http;
|
||||
|
||||
export interface locationRecordListDTO {
|
||||
startTime?:string,
|
||||
endTime?:string
|
||||
}
|
||||
|
||||
export const locationRecord = {
|
||||
list: (dto:locationRecordListDTO, server: string)=>{
|
||||
return http.post(`${server}/location/record/list`, dto,{
|
||||
headers: {
|
||||
token: localStorage.getItem("token")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export interface LoginDTO {
|
||||
account: string,
|
||||
password: string
|
||||
}
|
||||
export const auth = {
|
||||
login: (dto:LoginDTO, server: string)=>{
|
||||
return http.post(`${server}/auth/login`, dto)
|
||||
}
|
||||
}
|
498
app/wails/frontend/src/views/tabs/location-record/index.vue
Normal file
498
app/wails/frontend/src/views/tabs/location-record/index.vue
Normal file
@ -0,0 +1,498 @@
|
||||
<template>
|
||||
<div class=" w-full h-full">
|
||||
|
||||
|
||||
<div class="flex justify-start flex-wrap items-center">
|
||||
<div class="flex" v-if="!view.loginStatus">
|
||||
<el-input v-model="authModel.account" class="mx-1" label="账号" dense clearable clear-icon="close" filled />
|
||||
<el-input v-model="authModel.password" class="mx-1" label="密码" type="password" dense clearable clear-icon="close" filled />
|
||||
<el-button @click="login">登录</el-button>
|
||||
</div>
|
||||
<div class="flex" v-else>
|
||||
<el-button @click="logout">切换账号</el-button>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center">
|
||||
<!-- <DateTimeSelector v-model="date" range></DateTimeSelector>-->
|
||||
<el-date-picker
|
||||
v-model="date"
|
||||
type="datetimerange"
|
||||
/>
|
||||
<el-button @click="search">查询</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-end flex-wrap items-center p-1">
|
||||
显示所有路径点
|
||||
<el-switch
|
||||
v-model="options.showMarker"
|
||||
color="primary"
|
||||
@update:model-value="toggleShowMarker"
|
||||
keep-color
|
||||
/>
|
||||
|
||||
仅使用高精度定位
|
||||
<el-switch
|
||||
v-model="options.useHighAccuracyLocation"
|
||||
color="primary"
|
||||
@update:model-value="toggleUseHighAccuracyLocation"
|
||||
keep-color
|
||||
/>
|
||||
|
||||
自动缩放
|
||||
<el-switch
|
||||
v-model="options.autoScaling"
|
||||
color="primary"
|
||||
@update:model-value="toggleAutoView"
|
||||
keep-color
|
||||
/>
|
||||
|
||||
卫星地图
|
||||
<el-switch
|
||||
v-model="layers.satellite.show"
|
||||
color="primary"
|
||||
@update:model-value="toggleSatellite"
|
||||
keep-color
|
||||
/>
|
||||
|
||||
路网
|
||||
<el-switch
|
||||
v-model="layers.roadNet.show"
|
||||
color="primary"
|
||||
@update:model-value="toggleRoadNet"
|
||||
keep-color
|
||||
/>
|
||||
|
||||
实时交通
|
||||
<el-switch
|
||||
v-model="layers.traffic.show"
|
||||
color="primary"
|
||||
@update:model-value="toggleTraffic"
|
||||
keep-color
|
||||
/>
|
||||
|
||||
纠偏路线
|
||||
<el-switch
|
||||
v-model="feature.showFixed"
|
||||
color="primary"
|
||||
@update:model-value="fixedLocation"
|
||||
keep-color
|
||||
/>
|
||||
</div>
|
||||
<div ref="containerRef" id="map-container" style="height: 80vh">
|
||||
<div class="absolute bg-amber-50 rounded-br z-max text-center" style="width: 300px" v-if="view.filterData.length > 0">
|
||||
<div class="flex">
|
||||
<div class="w-1/4">最高速度</div>
|
||||
<div>{{view.speed.max}} km/h</div>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<div class="w-1/4">平均速度</div>
|
||||
<div>{{view.speed.avg}} km/h</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="absolute bg-amber-50 rounded-tl z-max text-left absolute-bottom-right pl-1.5" style="width: 350px" v-if="view.lines.length > 0">
|
||||
<div class="flex justify-between">
|
||||
<div>
|
||||
路径数量: {{view.lines.length}}
|
||||
</div>
|
||||
<div>
|
||||
<el-button dense @click="changeAnimateRate">动画速率: {{view.animateRate}}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-scrollbar :style="{height: view.lines.length * 22 > 250 ?'250px':view.lines.length * 22 + 'px'}" class="overflow-auto">
|
||||
<!-- <div v-for="(item,i) in view.lines" :key="i" >-->
|
||||
<!-- {{item[0].locationTime}} ~ {{item[item.length-1].locationTime}}-->
|
||||
<!-- </div>-->
|
||||
<el-checkbox-group v-model="options.showLines" @update:model-value="changeShowLines" :options="options.lines" type="checkbox" dense>
|
||||
<!-- <template>-->
|
||||
<el-row v-for="opt in options.lines">
|
||||
<span>{{ opt.label }}</span>
|
||||
<el-icon v-if="view.animating === opt.value"
|
||||
@click.stop="stop"
|
||||
name="fas fa-stop" color="negative" size="0.625em" class="q-ml-sm"></el-icon>
|
||||
<el-icon v-else @click.stop="play(opt)" name="fas fa-play" color="teal" size="0.625em" class="q-ml-sm" />
|
||||
</el-row>
|
||||
<!-- </template>-->
|
||||
</el-checkbox-group>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import AMapLoader from '@amap/amap-jsapi-loader';
|
||||
import {onActivated, onMounted, onUnmounted, reactive, ref} from "vue";
|
||||
import moment from "moment";
|
||||
import {locationRecord} from "src/views/tabs/location-record/api";
|
||||
import {getAvgSpeed, getMaxSpeed} from "src/views/tabs/location-record/logic/speed";
|
||||
import {
|
||||
checkLoginStatus,
|
||||
loginRequest,
|
||||
logoutHandler
|
||||
} from "src/views/tabs/location-record/logic/login";
|
||||
import {fixedLocationHandler} from "src/views/tabs/location-record/logic/location";
|
||||
import {drawMarkers} from "src/views/tabs/location-record/logic/markers";
|
||||
import {drawLines} from "src/views/tabs/location-record/logic/lines";
|
||||
import {pauseAnimation, playAnimation, stopAnimation} from "src/views/tabs/location-record/logic/play";
|
||||
import {ElMessage} from "element-plus";
|
||||
function login(){
|
||||
loginRequest(authModel, options.server).finally(()=>{
|
||||
view.loginStatus = checkLoginStatus()
|
||||
})
|
||||
}
|
||||
function logout(){
|
||||
logoutHandler()
|
||||
view.loginStatus = checkLoginStatus()
|
||||
}
|
||||
const date = ref([
|
||||
moment().startOf('d').toDate(),
|
||||
moment().toDate()
|
||||
])
|
||||
const feature = reactive({
|
||||
showFixed: false
|
||||
})
|
||||
function changeAnimateRate(){
|
||||
const animateRate = [1,2,5,10,20,30,60,120]
|
||||
let curIndex = animateRate.findIndex(item=>item === view.animateRate)
|
||||
let nextIndex = curIndex + 1
|
||||
view.animateRate = nextIndex >= animateRate.length ? animateRate[0] : animateRate[nextIndex]
|
||||
|
||||
if(view.animating !== -1){
|
||||
let index = view.animating
|
||||
pauseAnimation(data)
|
||||
setTimeout(()=>{
|
||||
play(options.lines[index])
|
||||
},100)
|
||||
}
|
||||
}
|
||||
function stop(){
|
||||
stopAnimation(map,data,view)
|
||||
}
|
||||
function play(item){
|
||||
if(view.animating !== item.value){
|
||||
stop()
|
||||
}
|
||||
console.log(item,view.lines,data.polylines)
|
||||
options.showLines = [item.value]
|
||||
changeShowLines()
|
||||
view.animating = item.value
|
||||
playAnimation(map,data,view.lines[item.value],view.animateRate,view,item.value)
|
||||
}
|
||||
// 地图实例 和 数据不要交给 vue 管理 否则性能大打折扣 且 会有各种奇葩问题
|
||||
let map = {}
|
||||
const data = {
|
||||
data: [],
|
||||
polylines: [],
|
||||
fixedPolyline: {},
|
||||
labelsLayer: null,
|
||||
markers: [],
|
||||
tmpMarker: null,
|
||||
infoWindow: null,
|
||||
animateMarker: null,
|
||||
}
|
||||
const view = reactive({
|
||||
filterData: [],
|
||||
speed: {
|
||||
max: 0,
|
||||
avg: 0
|
||||
},
|
||||
lines: [],
|
||||
loginStatus: false,
|
||||
animateRate: 1,
|
||||
animating: -1
|
||||
})
|
||||
onMounted(()=>{
|
||||
view.loginStatus = checkLoginStatus()
|
||||
})
|
||||
onUnmounted(()=>{
|
||||
clearMap()
|
||||
map.remove(data.labelsLayer || {})
|
||||
map.remove(data.tmpMarker || {})
|
||||
map.remove(data.animateMarker || {})
|
||||
map.remove(data.infoWindow || {})
|
||||
})
|
||||
const layers = reactive({
|
||||
satellite: {
|
||||
inst: {},
|
||||
show: true,
|
||||
},
|
||||
roadNet: {
|
||||
inst: {},
|
||||
show: false,
|
||||
},
|
||||
traffic: {
|
||||
inst: {},
|
||||
show: false,
|
||||
}
|
||||
})
|
||||
const options = reactive({
|
||||
server: "http://10.10.10.200:45680",
|
||||
useHighAccuracyLocation: true,
|
||||
showMarker: false,
|
||||
autoScaling: true,
|
||||
lines: [],
|
||||
showLines: [],
|
||||
})
|
||||
|
||||
const authModel = reactive({
|
||||
account: "",
|
||||
password: "",
|
||||
})
|
||||
|
||||
function toggleAutoView(v){
|
||||
if(v){
|
||||
map.setFitView()
|
||||
}
|
||||
}
|
||||
|
||||
function toggleShowMarker(v){
|
||||
if(v){
|
||||
options.autoScaling = false
|
||||
}
|
||||
redrawMap()
|
||||
}
|
||||
|
||||
function redrawMap(){
|
||||
clearMap()
|
||||
drawMap()
|
||||
}
|
||||
|
||||
function toggleUseHighAccuracyLocation(){
|
||||
redrawMap()
|
||||
}
|
||||
|
||||
function toggleSatellite(){
|
||||
if (layers.satellite.show) {
|
||||
layers.satellite.inst.show()
|
||||
} else {
|
||||
layers.satellite.inst.hide()
|
||||
}
|
||||
}
|
||||
function toggleRoadNet() {
|
||||
if (layers.roadNet.show) {
|
||||
layers.roadNet.inst.show()
|
||||
} else {
|
||||
layers.roadNet.inst.hide()
|
||||
}
|
||||
}
|
||||
function toggleTraffic() {
|
||||
if (layers.traffic.show) {
|
||||
layers.traffic.inst.show()
|
||||
} else {
|
||||
layers.traffic.inst.hide()
|
||||
}
|
||||
}
|
||||
|
||||
function changeShowLines(){
|
||||
console.log("options.showLines",options.showLines)
|
||||
console.log("view.lines",view.lines)
|
||||
|
||||
let showLines = view.lines.filter((_,index)=>options.showLines.includes(index))
|
||||
drawMarkers(map,data,options,showLines)
|
||||
drawLines(map,data,showLines)
|
||||
|
||||
view.speed.max = (getMaxSpeed(showLines.flat()) * 3.6).toFixed(2);
|
||||
view.speed.avg = (getAvgSpeed(showLines.flat()) * 3.6).toFixed(2);
|
||||
}
|
||||
|
||||
const containerRef = ref()
|
||||
|
||||
// onActivated(()=>{
|
||||
|
||||
onMounted(()=>{
|
||||
console.log("初始化")
|
||||
AMapLoader.load({
|
||||
"key": "e01d7df3a4c3a1b8f06fa4544ddbbe9c", // 申请好的Web端开发者Key,首次调用 load 时必填
|
||||
"version": "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
|
||||
"plugins": [
|
||||
"AMap.Scale",
|
||||
"AMap.HawkEye",
|
||||
"AMap.ToolBar",
|
||||
"AMap.ControlBar",
|
||||
"AMap.MoveAnimation"
|
||||
],// 需要使用的的插件列表,如比例尺'AMap.Scale'等
|
||||
"lang": "zh_cn"
|
||||
}).then((AMap) => {
|
||||
let scale = new AMap.Scale({
|
||||
visible: true
|
||||
})
|
||||
let toolBar = new AMap.ToolBar({
|
||||
visible: true,
|
||||
position: {
|
||||
top: '110px',
|
||||
right: '40px'
|
||||
}
|
||||
})
|
||||
let controlBar = new AMap.ControlBar({
|
||||
visible: true,
|
||||
position: {
|
||||
top: '10px',
|
||||
right: '10px'
|
||||
}
|
||||
})
|
||||
|
||||
map = new AMap.Map(containerRef.value,{
|
||||
resizeEnable: true,
|
||||
viewMode:'3D', // 地图模式
|
||||
center: [116.397428, 39.90923],
|
||||
zoom: 14,
|
||||
});
|
||||
console.log("地图对象", map)
|
||||
map.on("moveend",()=>{
|
||||
let bounds = map.getBounds()
|
||||
data.markers.forEach(marker=>{
|
||||
if(bounds.contains(marker.getPosition())) {
|
||||
marker.show()
|
||||
} else {
|
||||
marker.hide()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
map.addControl(scale)
|
||||
map.addControl(toolBar)
|
||||
map.addControl(controlBar)
|
||||
|
||||
// 卫星
|
||||
layers.satellite.inst = new AMap.TileLayer.Satellite({
|
||||
visible: layers.satellite.show,
|
||||
})
|
||||
// 路网
|
||||
layers.roadNet.inst = new AMap.TileLayer.RoadNet({
|
||||
visible: layers.roadNet.show,
|
||||
index: 10
|
||||
})
|
||||
// 实时交通
|
||||
layers.traffic.inst = new AMap.TileLayer.Traffic({
|
||||
zIndex: 11,
|
||||
visible: layers.traffic.show,
|
||||
});
|
||||
|
||||
map.addLayer(layers.satellite.inst)
|
||||
map.addLayer(layers.roadNet.inst)
|
||||
map.addLayer(layers.traffic.inst)
|
||||
|
||||
}).catch(e => {
|
||||
ElMessage.error({
|
||||
message: "地图组件初始化失败",
|
||||
})
|
||||
console.log(e);
|
||||
})
|
||||
// })
|
||||
})
|
||||
|
||||
|
||||
function clearMap(){
|
||||
view.lines = []
|
||||
map.remove(data.polylines)
|
||||
map.remove(data.markers)
|
||||
}
|
||||
|
||||
|
||||
function drawMap(autoSelect=false){
|
||||
if(options.useHighAccuracyLocation){
|
||||
console.log("[仅使用高精度定位数据]")
|
||||
data.data = data.data.filter(item=>item.locationType === 1)
|
||||
}
|
||||
|
||||
data.data = data.data.map(item=>{
|
||||
return {
|
||||
time: moment(item.locationTime),
|
||||
...item,
|
||||
}
|
||||
})
|
||||
data.data.sort((a,b)=>{
|
||||
return a.time.isBefore(b.time)?-1:1
|
||||
})
|
||||
|
||||
view.filterData = data.data;
|
||||
|
||||
view.speed.max = (getMaxSpeed(view.filterData) * 3.6).toFixed(2);
|
||||
view.speed.avg = (getAvgSpeed(view.filterData) * 3.6).toFixed(2);
|
||||
|
||||
console.log("data", data.data)
|
||||
|
||||
view.lines = []
|
||||
let prev = moment(0)
|
||||
for (let i = 0; i < data.data.length; i++) {
|
||||
let datum = data.data[i]
|
||||
|
||||
if (prev.year() === datum.time.year() && prev.month() === datum.time.month() && prev.date() === datum.time.date() && datum.time.diff(prev,'m') <= 5 ) {
|
||||
prev = datum.time
|
||||
} else {
|
||||
prev = datum.time
|
||||
view.lines.push([])
|
||||
}
|
||||
view.lines[view.lines.length -1].push(datum)
|
||||
}
|
||||
view.lines = view.lines.reverse()
|
||||
options.lines = view.lines.map((item,index)=>{
|
||||
if(autoSelect){
|
||||
// 默认选中
|
||||
options.showLines.push(index)
|
||||
}
|
||||
|
||||
return {
|
||||
label: `${item[0].locationTime} ~ ${item[item.length-1].locationTime}`,
|
||||
value: index,
|
||||
}
|
||||
})
|
||||
console.log("lines", view.lines)
|
||||
|
||||
drawMarkers(map,data,options,view.lines.filter((_,index)=>options.showLines.includes(index)))
|
||||
drawLines(map,data,view.lines.filter((_,index)=>options.showLines.includes(index)))
|
||||
|
||||
setTimeout(()=>{
|
||||
if(!options.autoScaling){
|
||||
return
|
||||
}
|
||||
if (data.polylines.length > 0) {
|
||||
map.setFitView()
|
||||
}
|
||||
})
|
||||
fixedLocation()
|
||||
}
|
||||
|
||||
function search() {
|
||||
console.log(date.value)
|
||||
view.animating = -1
|
||||
let params = {
|
||||
startTime: !date.value[0] ? undefined : moment(date.value[0]).format("YYYY-MM-DD HH:mm:ss"),
|
||||
endTime: !date.value[1] ? undefined : moment(date.value[1]).format("YYYY-MM-DD HH:mm:ss"),
|
||||
}
|
||||
|
||||
clearMap();
|
||||
locationRecord.list(params, options.server).then(resp => {
|
||||
if(resp.status !== 200){
|
||||
console.log(resp)
|
||||
let res = resp.data || {}
|
||||
ElMessage.error({
|
||||
message: res.data || res.msg || "请求失败",
|
||||
})
|
||||
throw new Error("请求失败")
|
||||
}
|
||||
return resp.data;
|
||||
}).then(res => {
|
||||
data.data = res.data || []
|
||||
options.showLines = []
|
||||
drawMap(true)
|
||||
})
|
||||
}
|
||||
|
||||
function fixedLocation() {
|
||||
map.remove(data.fixedPolyline)
|
||||
|
||||
if (!feature.showFixed) {
|
||||
return
|
||||
}
|
||||
fixedLocationHandler(map,data)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
#container {
|
||||
width: 100%;
|
||||
height: 80vh;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,10 @@
|
||||
export const COLOR = [
|
||||
'#6090e0', '#30c030', '#d070d0', '#80c0f0', '#f07050', '#ffb900',
|
||||
'#37a2da', '#ffdb5c', '#8378ea', '#e7bcf3', '#32c5e9', '#67e0e3',
|
||||
'#dd6b66', '#759aa0', '#e69d87', '#8dc1a9', '#ea7e53', '#eedd78',
|
||||
]
|
||||
|
||||
export function randomColorList(len:number){
|
||||
return Array.from({ length: len })
|
||||
.map(() => COLOR[Math.floor(Math.random() * COLOR.length)]);
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
export function createCanvasDir(){
|
||||
let canvasDir = document.createElement('canvas')
|
||||
let width = 24;
|
||||
let height = 24;
|
||||
canvasDir.width = width;
|
||||
canvasDir.height = height;
|
||||
|
||||
let context = canvasDir.getContext('2d')!;
|
||||
context.strokeStyle = 'white';
|
||||
context.lineJoin = 'round';
|
||||
context.lineWidth = 8;
|
||||
context.moveTo(-4, width - 4);
|
||||
context.lineTo(width / 2, 6);
|
||||
context.lineTo(width + 4, width - 4);
|
||||
context.stroke();
|
||||
|
||||
return canvasDir;
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
import {randomColorList} from "src/views/tabs/location-record/logic/color";
|
||||
import {createCanvasDir} from "src/views/tabs/location-record/logic/icon";
|
||||
import {getAvgSpeed, getMaxSpeed} from "src/views/tabs/location-record/logic/speed";
|
||||
|
||||
const canvasDir = createCanvasDir();
|
||||
const SLOW = "#ff0036"
|
||||
const NORMAL = "#ff8119"
|
||||
const FAST = "#AF5"
|
||||
export function drawLines(map,data,lines){
|
||||
map.remove(data.polylines)
|
||||
data.polylines = []
|
||||
let colorList = randomColorList(lines.length)
|
||||
|
||||
lines.forEach((_data,index) => {
|
||||
let max = getMaxSpeed(_data)
|
||||
let avg = getAvgSpeed(_data)
|
||||
|
||||
let p:any = []
|
||||
for (let i = 0; i < _data.length; i++) {
|
||||
let item = _data[i]
|
||||
let speed = item.speed || 0
|
||||
let type
|
||||
if(speed < avg / 2){
|
||||
type= SLOW
|
||||
} else if(speed >= avg/2 && speed < avg){
|
||||
type = NORMAL
|
||||
} else {
|
||||
type = FAST
|
||||
}
|
||||
|
||||
if(p.length === 0){
|
||||
p.push({
|
||||
path: [new AMap.LngLat(item.longitude, item.latitude)],
|
||||
strokeColor: type,
|
||||
})
|
||||
} else {
|
||||
if(p[p.length - 1].strokeColor === type){
|
||||
p[p.length - 1].path.push(new AMap.LngLat(item.longitude, item.latitude))
|
||||
} else {
|
||||
let prevPath = p[p.length - 1].path
|
||||
let prev = prevPath[prevPath.length - 1]
|
||||
p.push({
|
||||
path: [prev, new AMap.LngLat(item.longitude, item.latitude)],
|
||||
strokeColor: type,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log("p",p)
|
||||
|
||||
let polylineArr:any[] = []
|
||||
p.forEach((item)=>{
|
||||
let polyline = new AMap.Polyline({
|
||||
path: item.path,
|
||||
showDir: true,
|
||||
dirImg: canvasDir,
|
||||
borderWeight: 6, // 线条宽度,默认为 1
|
||||
strokeWeight: 6,
|
||||
strokeOpacity: 0.9, // 透明度
|
||||
strokeColor: item.strokeColor, // 线条颜色
|
||||
dirColor: 'white',
|
||||
lineJoin: 'round' // 折线拐点连接处样式})
|
||||
})
|
||||
polylineArr.push(polyline)
|
||||
})
|
||||
data.polylines.push(polylineArr)
|
||||
})
|
||||
|
||||
data.polylines = data.polylines.flat()
|
||||
console.log("polylines",data.polylines)
|
||||
map.add(data.polylines)
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
export function fixedLocationHandler(map,data) {
|
||||
console.log(map,data)
|
||||
let task:Promise<any>[] = []
|
||||
let startTime
|
||||
let preFixedData = (data.data || []).map((item, index) => {
|
||||
if (index % 500 === 0) {
|
||||
startTime = data.data[0].time
|
||||
}
|
||||
|
||||
let time = item.time
|
||||
return {
|
||||
x: item.longitude,
|
||||
y: item.latitude,
|
||||
sp: item.speed || 0,
|
||||
ag: item.bearing || 0,
|
||||
tm: index % 500 === 0 ? time.unix() : time.diff(startTime, 's')
|
||||
}
|
||||
})
|
||||
console.log("纠偏路线 预处理数据: ", preFixedData)
|
||||
|
||||
let group:any[] = []
|
||||
let len = preFixedData.length
|
||||
for (let i = 0; i < Math.ceil(len / 500); i++) {
|
||||
group.push(preFixedData.splice(0, 500))
|
||||
}
|
||||
console.log("分组: ", group)
|
||||
|
||||
task = group.map(item => {
|
||||
return new Promise(resolve => {
|
||||
AMap.plugin('AMap.GraspRoad', function () {
|
||||
let grasp = new AMap.GraspRoad();
|
||||
grasp.driving(item, function (error, result) {
|
||||
if (error) {
|
||||
resolve([])
|
||||
}
|
||||
if (!error) {
|
||||
let newPath = result.data.points;//纠偏后的轨迹
|
||||
let distance = result.data.distance;//里程
|
||||
|
||||
console.log("返回值 => ", "路径: ", newPath, "里程: ", distance)
|
||||
resolve(newPath)
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Promise.all(task).then(paths => {
|
||||
let fixedData = paths.flat(1)
|
||||
console.log("纠偏路线数据: ", fixedData)
|
||||
|
||||
data.fixedPolyline = new AMap.Polyline({
|
||||
path: fixedData.map(item => {
|
||||
return new AMap.LngLat(item.x, item.y)
|
||||
}),
|
||||
showDir: true,
|
||||
strokeColor: 'orange', // 线条颜色
|
||||
borderWeight: 6, // 线条宽度,默认为 1
|
||||
strokeWeight: 6,
|
||||
})
|
||||
map.add(data.fixedPolyline)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,54 @@
|
||||
import {auth, LoginDTO} from "src/views/tabs/location-record/api";
|
||||
import moment from "moment";
|
||||
import {ElMessage, ElNotification} from "element-plus";
|
||||
|
||||
export function checkLoginStatus(){
|
||||
let user = localStorage.getItem("user")
|
||||
let lastLogin = localStorage.getItem("lastLogin")
|
||||
let token = localStorage.getItem("token")
|
||||
|
||||
return !!user &&
|
||||
!!lastLogin &&
|
||||
!!token &&
|
||||
moment().diff(moment(lastLogin),"h") < 24
|
||||
}
|
||||
|
||||
export function logoutHandler(){
|
||||
localStorage.removeItem("user")
|
||||
localStorage.removeItem("lastLogin")
|
||||
localStorage.removeItem("token")
|
||||
}
|
||||
|
||||
export function loginRequest(authModel:LoginDTO, server:string){
|
||||
logoutHandler()
|
||||
|
||||
return new Promise((resolve)=>{
|
||||
auth.login(authModel,server).then((resp)=>{
|
||||
let res = resp.data
|
||||
console.log("login", res)
|
||||
if(res.code !== 200){
|
||||
ElMessage.warning({
|
||||
message: res.data || res.msg,
|
||||
duration: 500
|
||||
})
|
||||
resolve(false)
|
||||
} else {
|
||||
ElMessage.success({
|
||||
message: "登录成功",
|
||||
duration: 500
|
||||
})
|
||||
localStorage.setItem("user", authModel.account)
|
||||
localStorage.setItem("lastLogin", moment().format("YYYY-MM-DD HH:mm:ss"))
|
||||
localStorage.setItem("token",res.data.token)
|
||||
resolve(true)
|
||||
}
|
||||
}).catch((e)=>{
|
||||
console.error(e)
|
||||
ElMessage.error({
|
||||
message: "登录失败",
|
||||
duration: 500,
|
||||
})
|
||||
resolve(false)
|
||||
})
|
||||
})
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
export function getMarkers(map,data,line){
|
||||
if(!data.tmpMarker){
|
||||
data.tmpMarker = new AMap.Marker({
|
||||
anchor: 'bottom-center',
|
||||
offset: [0, -15],
|
||||
});
|
||||
}
|
||||
return line.filter((_, index) => index % 1 === 0).map((item) => {
|
||||
let text = {
|
||||
fontSize: 10,
|
||||
content: `时间:${item.locationTime}<br>速度: ${((item.speed || 0) * 3.6).toFixed(2)}km/h 方向角: ${(item.bearing || 0).toFixed(2)}°`
|
||||
}
|
||||
|
||||
let marker = new AMap.LabelMarker({
|
||||
name: item.id,
|
||||
position: [item.longitude, item.latitude],
|
||||
zIndex: 10,
|
||||
icon: {
|
||||
// 图标类型,现阶段只支持 image 类型
|
||||
type: 'image',
|
||||
// 图片 url
|
||||
image: 'https://webapi.amap.com/theme/v1.3/markers/n/mark_b.png',
|
||||
// 图片尺寸
|
||||
size: [6, 9],
|
||||
// 图片相对 position 的锚点,默认为 bottom-center
|
||||
anchor: 'bottom-center'
|
||||
},
|
||||
// text,
|
||||
})
|
||||
|
||||
function show(e) {
|
||||
let position = e.data.data && e.data.data.position;
|
||||
|
||||
if (position) {
|
||||
data.tmpMarker.setContent(
|
||||
`<div class="amap-info-window"
|
||||
style="padding: .75rem 1.25rem;font-size:12px;
|
||||
margin-bottom: 1rem;
|
||||
border-radius: .25rem;
|
||||
position: fixed;
|
||||
top: 1rem;
|
||||
background-color: white;
|
||||
width: auto;
|
||||
min-width: 22rem;
|
||||
border-width: 0;
|
||||
right: 1rem;
|
||||
box-shadow: 0 2px 6px 0 rgba(114, 124, 245, .5);">`
|
||||
+ text.content +
|
||||
'<div class="amap-info-sharp"></div>' +
|
||||
'</div>');
|
||||
data.tmpMarker.setPosition(position);
|
||||
map.add(data.tmpMarker);
|
||||
}
|
||||
}
|
||||
|
||||
function hide() {
|
||||
map.remove(data.tmpMarker);
|
||||
}
|
||||
|
||||
marker.on('mouseover', (e) => show(e));
|
||||
marker.on('touchstart', (e) => show(e));
|
||||
marker.on('mouseout', () => hide());
|
||||
marker.on('touchend', () => hide());
|
||||
|
||||
return marker;
|
||||
});
|
||||
}
|
||||
|
||||
export function drawMarkers(map,data,options,lines){
|
||||
if(!data.labelsLayer){
|
||||
data.labelsLayer = new AMap.LabelsLayer({
|
||||
zooms: [3, 20],
|
||||
zIndex: 1000,
|
||||
// 该层内标注是否避让
|
||||
collision: true,
|
||||
// 设置 allowCollision:true,可以让标注避让用户的标注
|
||||
allowCollision: false,
|
||||
});
|
||||
map.add(data.labelsLayer)
|
||||
}
|
||||
data.labelsLayer.remove(data.markers)
|
||||
data.markers = []
|
||||
|
||||
if(!options.showMarker){
|
||||
return
|
||||
}
|
||||
data.markers = lines.map(line=>{
|
||||
return getMarkers(map,data,line)
|
||||
}).flat()
|
||||
data.labelsLayer.add(data.markers)
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
let index = 0
|
||||
|
||||
export function playAnimation(map,data,line,animateRate,view,itemIndex){
|
||||
console.log("playAnimation",line)
|
||||
if(data.animateMarker){
|
||||
map.remove(data.animateMarker)
|
||||
}
|
||||
data.animateMarker = new AMap.Marker({
|
||||
map: map,
|
||||
position: [0,0],
|
||||
icon: "https://a.amap.com/jsapi_demos/static/demo-center-v2/car.png",
|
||||
offset: new AMap.Pixel(-13, -26),
|
||||
autoRotation: true
|
||||
})
|
||||
|
||||
if(!data.infoWindow){
|
||||
data.infoWindow = new AMap.InfoWindow({
|
||||
offset: new AMap.Pixel(6, -25),
|
||||
content: "",
|
||||
isCustom: true
|
||||
});
|
||||
}
|
||||
|
||||
if(itemIndex !== view.animating){
|
||||
index = 0
|
||||
}
|
||||
|
||||
let path = line.slice(index).map((item) => {
|
||||
return new AMap.LngLat(item.longitude, item.latitude)
|
||||
})
|
||||
|
||||
data.animateMarker.stopMove();
|
||||
data.animateMarker.on("movealong",()=>{})
|
||||
let lastMovingIndex = 0
|
||||
data.animateMarker.on("moving",(e)=>{
|
||||
if(lastMovingIndex != e.index){
|
||||
index++
|
||||
}
|
||||
lastMovingIndex = e.index
|
||||
let lastLocation = e.passedPath[e.passedPath.length - 1];
|
||||
data.infoWindow.setPosition(lastLocation);
|
||||
let speed = ((line[index].speed||0) * 3.6).toFixed(2)
|
||||
data.infoWindow.setContent(`<div style="width:180px;background: #fff;border-radius: 5px">
|
||||
<div>${line[index].locationTime}</div>
|
||||
<div>${speed} km/h</div>
|
||||
</div>`);
|
||||
map.setCenter(e.target.getPosition(),true)
|
||||
})
|
||||
data.animateMarker.on("moveend",()=>{
|
||||
if(index >= line.length-2){
|
||||
setTimeout(()=>{
|
||||
view.animating = -1
|
||||
index = 0
|
||||
data.animateMarker.stopMove()
|
||||
map.remove(data.animateMarker)
|
||||
data.infoWindow.close()
|
||||
},100)
|
||||
}
|
||||
})
|
||||
let polyline = new AMap.Polyline({
|
||||
path: path,
|
||||
})
|
||||
data.animateMarker.moveAlong(polyline.getPath(),{
|
||||
duration: 1000/animateRate,
|
||||
autoRotation: true
|
||||
});
|
||||
data.infoWindow.open(map, data.animateMarker.getPosition())
|
||||
}
|
||||
|
||||
export function stopAnimation(map,data,view){
|
||||
if(!data.animateMarker){
|
||||
return
|
||||
}
|
||||
pauseAnimation(data)
|
||||
view.animating = -1
|
||||
index = 0
|
||||
}
|
||||
|
||||
export function pauseAnimation(data){
|
||||
if(!data.animateMarker){
|
||||
return
|
||||
}
|
||||
data.animateMarker.stopMove()
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
export function getMaxSpeed(data:any[]){
|
||||
let sortSpeedData = JSON.parse(JSON.stringify(data)).sort((a,b)=>{return (b.speed || 0) - (a.speed||0)})
|
||||
return ((sortSpeedData[0]||{}).speed || 0)
|
||||
}
|
||||
|
||||
export function getAvgSpeed(data:any[]){
|
||||
if(data.length === 0){
|
||||
return 0
|
||||
} else {
|
||||
return ((data.map(item=>item.speed || 0).reduce((prev,cur)=>{
|
||||
return prev + cur
|
||||
},0) / data.length))
|
||||
}
|
||||
}
|
@ -10,6 +10,7 @@
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": false,
|
||||
"esModuleInterop": true,
|
||||
"noImplicitAny": false,
|
||||
"lib": [
|
||||
"ESNext",
|
||||
"DOM"
|
||||
@ -24,7 +25,8 @@
|
||||
"src/**/*.ts",
|
||||
"src/**/*.d.ts",
|
||||
"src/**/*.tsx",
|
||||
"src/**/*.vue"
|
||||
"src/**/*.vue",
|
||||
"node_modules/@amap/amap-jsapi-loader/src/global.d.ts"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
|
@ -7,6 +7,11 @@
|
||||
resolved "https://registry.npmmirror.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz#7bf68b20c0a350f936915fcae06f58e32007ce30"
|
||||
integrity sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==
|
||||
|
||||
"@amap/amap-jsapi-loader@^1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmmirror.com/@amap/amap-jsapi-loader/-/amap-jsapi-loader-1.0.1.tgz#9ec4b4d5d2467eac451f6c852e35db69e9f9f0c0"
|
||||
integrity sha512-nPyLKt7Ow/ThHLkSvn2etQlUzqxmTVgK7bIgwdBRTg2HK5668oN7xVxkaiRe3YZEzGzfV2XgH5Jmu2T73ljejw==
|
||||
|
||||
"@ampproject/remapping@^2.2.0":
|
||||
version "2.2.1"
|
||||
resolved "https://registry.npmmirror.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630"
|
||||
@ -728,6 +733,11 @@ async-validator@^4.2.5:
|
||||
resolved "https://registry.npmmirror.com/async-validator/-/async-validator-4.2.5.tgz#c96ea3332a521699d0afaaceed510a54656c6339"
|
||||
integrity sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==
|
||||
|
||||
asynckit@^0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
|
||||
integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
|
||||
|
||||
autoprefixer@^10.4.14:
|
||||
version "10.4.14"
|
||||
resolved "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.4.14.tgz#e28d49902f8e759dd25b153264e862df2705f79d"
|
||||
@ -740,6 +750,15 @@ autoprefixer@^10.4.14:
|
||||
picocolors "^1.0.0"
|
||||
postcss-value-parser "^4.2.0"
|
||||
|
||||
axios@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.npmmirror.com/axios/-/axios-1.4.0.tgz#38a7bf1224cd308de271146038b551d725f0be1f"
|
||||
integrity sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==
|
||||
dependencies:
|
||||
follow-redirects "^1.15.0"
|
||||
form-data "^4.0.0"
|
||||
proxy-from-env "^1.1.0"
|
||||
|
||||
balanced-match@^1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
|
||||
@ -833,6 +852,13 @@ color-name@1.1.3:
|
||||
resolved "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
|
||||
integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==
|
||||
|
||||
combined-stream@^1.0.8:
|
||||
version "1.0.8"
|
||||
resolved "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
|
||||
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
|
||||
dependencies:
|
||||
delayed-stream "~1.0.0"
|
||||
|
||||
commander@^4.0.0:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.npmmirror.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068"
|
||||
@ -870,6 +896,11 @@ debug@^4.1.0, debug@^4.3.4:
|
||||
dependencies:
|
||||
ms "2.1.2"
|
||||
|
||||
delayed-stream@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
|
||||
integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
|
||||
|
||||
didyoumean@^1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.npmmirror.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037"
|
||||
@ -992,6 +1023,20 @@ fill-range@^7.0.1:
|
||||
dependencies:
|
||||
to-regex-range "^5.0.1"
|
||||
|
||||
follow-redirects@^1.15.0:
|
||||
version "1.15.2"
|
||||
resolved "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13"
|
||||
integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==
|
||||
|
||||
form-data@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.npmmirror.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
|
||||
integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
|
||||
dependencies:
|
||||
asynckit "^0.4.0"
|
||||
combined-stream "^1.0.8"
|
||||
mime-types "^2.1.12"
|
||||
|
||||
fraction.js@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.npmmirror.com/fraction.js/-/fraction.js-4.2.0.tgz#448e5109a313a3527f5a3ab2119ec4cf0e0e2950"
|
||||
@ -1196,6 +1241,18 @@ micromatch@^4.0.4, micromatch@^4.0.5:
|
||||
braces "^3.0.2"
|
||||
picomatch "^2.3.1"
|
||||
|
||||
mime-db@1.52.0:
|
||||
version "1.52.0"
|
||||
resolved "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
|
||||
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
|
||||
|
||||
mime-types@^2.1.12:
|
||||
version "2.1.35"
|
||||
resolved "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
|
||||
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
|
||||
dependencies:
|
||||
mime-db "1.52.0"
|
||||
|
||||
minimatch@^3.0.4:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
|
||||
@ -1362,6 +1419,11 @@ postcss@^8.1.10, postcss@^8.4.23, postcss@^8.4.24:
|
||||
picocolors "^1.0.0"
|
||||
source-map-js "^1.0.2"
|
||||
|
||||
proxy-from-env@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
|
||||
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
|
||||
|
||||
queue-microtask@^1.2.2:
|
||||
version "1.2.3"
|
||||
resolved "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
|
||||
|
Loading…
Reference in New Issue
Block a user