Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gk
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
micro_app
gk
Commits
510bf5f4
Commit
510bf5f4
authored
Jul 09, 2025
by
daniel
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix(demo): mapbox接入
parent
465069bf
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
1511 additions
and
32 deletions
+1511
-32
demo.jsp
src/main/webapp/WEB-INF/views/cmpt/demo.jsp
+1243
-32
base.css
src/main/webapp/static/css/base.css
+268
-0
No files found.
src/main/webapp/WEB-INF/views/cmpt/demo.jsp
View file @
510bf5f4
...
...
@@ -37,6 +37,10 @@
<!-- 添加天地图API -->
<script
src=
"https://api.tianditu.gov.cn/api?v=4.0&tk=16a595e56ea0cec57ad8c07368d7f1ac"
></script>
<!-- 引入togeojson -->
<script
src=
"https://unpkg.com/@tmcw/togeojson@7.1.2/dist/togeojson.umd.js"
></script>
<!-- 引入turf -->
<script
src=
"https://cdn.jsdelivr.net/npm/@turf/turf@7/turf.min.js"
></script>
<style>
...
...
@@ -62,8 +66,8 @@
<body>
<div
class=
"page-content d-flex align-items-stretch main-body"
>
<div
id=
"app"
v-cloak
>
<n-config-provider>
<div
id=
"app"
class=
"flex-1"
v-cloak
>
<n-config-provider
:locale=
"locale"
:date-locale=
"dateLocale"
>
<n-modal-provider>
<n-message-provider>
<n-notification-provider>
...
...
@@ -76,14 +80,472 @@
</div>
<script
type=
"text/x-template"
id=
"my-app"
>
<
div
style
=
"position: fixed; top: 0; left: 0; right: 0; bottom: 0; margin: 0; padding: 0;"
>
<
div
ref
=
"mapContainer"
style
=
"width: 100%; height: 100%;"
><
/div
>
<
div
id
=
"map"
style
=
"width: 100%; height: 100%;z-index: 0;"
><
/div
>
<
div
class
=
"fixed left-0 top-0 z-10 flex flex-row justify-center items-center p-3"
>
<
input
type
=
"file"
accept
=
".gpx,.kml"
@
change
=
"handleFileChange"
style
=
"display: none"
ref
=
"fileInputRef"
/>
<
n
-
card
>
<
div
class
=
"flex flex-col items-start gap-y-2"
>
<
n
-
button
@
click
=
"fileInputRef?.click()"
type
=
"primary"
>
导入轨迹
<
/n-button
>
<
span
class
=
"text-gray text-sm"
>
只能上传
GPX
或
KML
文件,且不超过
10
M
。
<
/span
>
<
span
class
=
"text-gray text-blue-600"
v
-
if
=
"fileName"
>
{{
fileName
}}
<
/span
>
<
/div
>
<
/n-card
>
<
/div
>
<
div
class
=
"fixed right-3 top-0 z-10 p-3 flex flex-col gap-y-1"
>
<
div
class
=
"flex flex-row items-center mb-2 w-full gap-x-1 bg-white p-2 rounded-sm"
>
<
span
>
点位拾取
<
/span
>
<
n
-
switch
v
-
model
:
value
=
"isAddMode"
>
<
template
#
checked
>
开启
<
/template
>
<
template
#
unchecked
>
关闭
<
/template
>
<
/n-switch
>
<
div
class
=
"ml-auto"
>
<
n
-
button
@
click
=
"editCategory(null,null)"
>
新增分类
<
/n-button
>
<
/div
>
<
/div
>
<
n
-
collapse
accordion
class
=
"bg-white p-2 rounded-sm"
style
=
"min-width:250px;"
>
<
n
-
collapse
-
item
:
title
=
"category.name"
:
name
=
"category.value"
v
-
for
=
"category,categoryIndex in categories"
:
key
=
"categoryIndex"
>
<
template
#
header
-
extra
>
<
ion
-
icon
@
click
=
"(e)=>editCategory(e,category)"
class
=
"cursor-pointer"
name
=
"create-outline"
><
/ion-icon
>
<
/template
>
<
n
-
space
vertical
style
=
"width:100%;"
>
<
div
v
-
for
=
"(marker, index) in markers.filter(v=>v.categoryId===category.id)"
:
key
=
"index"
class
=
"flex flex-row items-center gap-x-2 py-1"
>
<
span
class
=
"flex flex-1 flex-row items-center gap-x-1 text-xs"
>
<
span
class
=
"line-clamp-1 flex-1"
>
{{
marker
.
name
}}
<
/span
>
<
n
-
tag
size
=
"small"
:
bordered
=
"false"
:
type
=
"CheckPointTypeEnum[marker.type].color"
>
{{
CheckPointTypeEnum
[
marker
.
type
].
name
}}
<
/n-tag
>
<
n
-
tag
size
=
"small"
:
bordered
=
"false"
:
type
=
"StatusEnum[marker.status].color"
>
{{
StatusEnum
[
marker
.
status
].
name
}}
<
/n-tag
>
<
/span
>
<
ion
-
icon
@
click
=
"editCheckPoint(marker)"
class
=
"cursor-pointer"
name
=
"create-outline"
><
/ion-icon
>
<
n
-
popconfirm
@
positive
-
click
=
"deleteCheckPoint(index)"
>
<
template
#
trigger
>
<
ion
-
icon
class
=
"text-red cursor-pointer"
name
=
"trash-outline"
><
/ion-icon
>
<
/template
>
是否删除该标注点
<
/n-popconfirm
>
<
/div
>
<
/n-space
>
<
/n-collapse-item
>
<
n
-
collapse
-
item
title
=
"未分类"
name
=
"unknown"
>
<
n
-
space
vertical
style
=
"width:100%;"
>
<
div
v
-
for
=
"(marker, index) in markers.filter(v=>!v.categoryId)"
:
key
=
"index"
class
=
"flex flex-row items-center gap-x-2 py-1"
>
<
span
class
=
"flex flex-1 flex-row items-center gap-x-1 text-xs"
>
<
span
class
=
"line-clamp-1 flex-1"
>
{{
marker
.
name
}}
<
/span
>
<
n
-
tag
size
=
"small"
:
bordered
=
"false"
:
type
=
"CheckPointTypeEnum[marker.type].color"
>
{{
CheckPointTypeEnum
[
marker
.
type
].
name
}}
<
/n-tag
>
<
n
-
tag
size
=
"small"
:
bordered
=
"false"
:
type
=
"StatusEnum[marker.status].color"
>
{{
StatusEnum
[
marker
.
status
].
name
}}
<
/n-tag
>
<
/span
>
<
ion
-
icon
@
click
=
"editCheckPoint(marker)"
class
=
"cursor-pointer"
name
=
"create-outline"
><
/ion-icon
>
<
n
-
popconfirm
@
positive
-
click
=
"deleteCheckPoint(index)"
>
<
template
#
trigger
>
<
ion
-
icon
class
=
"text-red cursor-pointer"
name
=
"trash-outline"
><
/ion-icon
>
<
/template
>
是否删除该标注点
<
/n-popconfirm
>
<
/div
>
<
/n-space
>
<
/n-collapse-item
>
<
/n-collapse
>
<
/div
>
<
n
-
modal
:
mask
-
closable
=
"false"
:
loading
=
"loading"
style
=
"width: 400px"
:
title
=
"isEditMarker?'编辑标记点':'新增标记点'"
preset
=
"card"
draggable
v
-
model
:
show
=
"editMarkerModal"
>
<
n
-
form
ref
=
"markerFormRef"
:
model
=
"markerForm"
:
rules
=
"markerRules"
label
-
placement
=
"left"
label
-
width
=
"auto"
>
<
n
-
form
-
item
label
=
"点位名称"
path
=
"name"
>
<
n
-
input
v
-
model
:
value
=
"markerForm.name"
placeholder
=
"请输入点位名称"
/>
<
/n-form-item
>
<
n
-
form
-
item
label
=
"排序"
path
=
"sort"
>
<
n
-
input
-
number
v
-
model
:
value
=
"markerForm.sort"
placeholder
=
"请输入排序"
/>
<
/n-form-item
>
<
n
-
form
-
item
label
=
"经度"
path
=
"longitude"
>
<
n
-
input
v
-
model
:
value
=
"markerForm.longitude"
placeholder
=
"请输入经度"
/>
<
/n-form-item
>
<
n
-
form
-
item
label
=
"纬度"
path
=
"latitude"
>
<
n
-
input
v
-
model
:
value
=
"markerForm.latitude"
placeholder
=
"请输入纬度"
/>
<
/n-form-item
>
<
n
-
form
-
item
label
=
"海拔"
path
=
"altitude"
>
<
n
-
input
v
-
model
:
value
=
"markerForm.altitude"
placeholder
=
"请输入海拔"
/>
<
/n-form-item
>
<
n
-
form
-
item
label
=
"类型"
path
=
"type"
>
<
n
-
select
v
-
model
:
value
=
"markerForm.type"
:
options
=
"CheckPointTypeEnum.data.map(v=>({
label:v.name,
value:v.value
}))"
/>
<
/n-form-item
>
<
n
-
form
-
item
label
=
"分类"
path
=
"categoryId"
>
<
n
-
select
v
-
model
:
value
=
"markerForm.categoryId"
:
options
=
"categories.map(v=>({
label:v.name,
value:v.id
}))"
/>
<
/n-form-item
>
<
n
-
form
-
item
label
=
"状态"
path
=
"status"
>
<
n
-
switch
:
checked
-
value
=
"1"
:
unchecked
-
value
=
"0"
v
-
model
:
value
=
"markerForm.status"
>
<
template
#
checked
>
开启
<
/template
>
<
template
#
unchecked
>
关闭
<
/template
>
<
/n-switch
>
<
/n-form-item
>
<
/n-form
>
<
template
#
footer
>
<
div
class
=
"flex gap-x-2 flex-row items-center"
>
<
n
-
button
class
=
"flex-1"
:
loading
=
"loading"
@
click
=
"onMarkerCancel"
>
取消
<
/n-button
>
<
n
-
button
class
=
"flex-1"
:
loading
=
"loading"
type
=
'primary'
@
click
=
"onMarkerSubmit"
>
保存
<
/n-button
>
<
/div
>
<
/template
>
<
/n-modal
>
<!--
分类的弹框
-->
<
n
-
modal
:
mask
-
closable
=
"false"
:
loading
=
"loading"
preset
=
"card"
style
=
"width: 400px"
:
title
=
"isEditCategory?'编辑分类':'新增分类'"
draggable
v
-
model
:
show
=
"categoryModal"
>
<
n
-
form
ref
=
"categoryFormRef"
:
model
=
"categoryForm"
:
rules
=
"categoryRules"
label
-
placement
=
"left"
label
-
width
=
"auto"
>
<
n
-
form
-
item
label
=
"分类名称"
path
=
"name"
>
<
n
-
input
v
-
model
:
value
=
"categoryForm.name"
placeholder
=
"请输入分类名称"
/>
<
/n-form-item
>
<
n
-
form
-
item
label
=
"排序"
path
=
"sort"
>
<
n
-
input
-
number
v
-
model
:
value
=
"categoryForm.sort"
placeholder
=
"请输入排序"
/>
<
/n-form-item
>
<
n
-
form
-
item
label
=
"状态"
path
=
"status"
>
<
n
-
switch
:
checked
-
value
=
"1"
:
unchecked
-
value
=
"0"
v
-
model
:
value
=
"categoryForm.status"
>
<
template
#
checked
>
开启
<
/template
>
<
template
#
unchecked
>
关闭
<
/template
>
<
/n-switch
>
<
/n-form-item
>
<
/n-form
>
<
template
#
footer
>
<
div
class
=
"flex gap-x-2 flex-row items-center"
>
<
n
-
button
class
=
"flex-1"
:
loading
=
"loading"
@
click
=
"onCategoryCancel"
>
取消
<
/n-button
>
<
n
-
button
class
=
"flex-1"
:
loading
=
"loading"
type
=
'primary'
@
click
=
"onCategorySubmit"
>
保存
<
/n-button
>
<
/div
>
<
/template
>
<
/n-modal
>
<
/div
>
</script>
<script>
const
{
createApp
,
defineComponent
,
ref
,
onMounted
}
=
Vue
;
const
{
useMessage
,
useNotification
}
=
naive
// 枚举class
class
SuperEnum
{
constructor
(
param
)
{
const
keyObj
=
{};
const
valueObj
=
{};
const
nameObj
=
{};
if
(
!
Array
.
isArray
(
param
))
{
throw
new
TypeError
(
'param is not an array!'
);
}
param
.
forEach
((
element
)
=>
{
if
(
!
element
.
key
||
!
element
.
name
)
{
return
;
}
// 使用对象存储键值对,方便快速查找
keyObj
[
element
.
key
]
=
element
;
valueObj
[
element
.
value
]
=
element
;
nameObj
[
element
.
name
]
=
element
;
// 动态添加属性
this
[
element
.
key
]
=
element
;
this
[
element
.
name
]
=
element
;
if
(
element
.
key
!==
element
.
value
)
{
this
[
element
.
value
]
=
element
;
}
});
// 保存源数组
this
.
data
=
param
;
this
.
keyObj
=
keyObj
;
this
.
valueObj
=
valueObj
;
this
.
nameObj
=
nameObj
;
// 防止被修改
Object
.
freeze
(
this
);
}
getColorByKey
(
key
)
{
const
prop
=
this
.
keyOf
(
key
);
if
(
!
prop
)
{
const
error
=
new
Error
(
'No enum constant'
);
error
.
code
=
'ENUM_NOT_FOUND'
;
throw
error
;
}
return
prop
.
color
;
}
getData
()
{
return
this
.
data
;
}
getKeyByName
(
name
)
{
const
prop
=
this
.
nameOf
(
name
);
if
(
!
prop
)
{
const
error
=
new
Error
(
'No enum constant'
);
error
.
code
=
'ENUM_NOT_FOUND'
;
throw
error
;
}
return
prop
.
key
;
}
getNameByKey
(
key
)
{
const
prop
=
this
.
keyOf
(
key
);
if
(
!
prop
)
{
const
error
=
new
Error
(
'No enum constant'
);
error
.
code
=
'ENUM_NOT_FOUND'
;
throw
error
;
}
return
prop
.
name
;
}
getNameByValue
(
value
)
{
const
prop
=
this
.
valueOf
(
value
);
if
(
!
prop
)
{
const
error
=
new
Error
(
'No enum constant'
);
error
.
code
=
'ENUM_NOT_FOUND'
;
throw
error
;
}
return
prop
.
name
;
}
getValueByKey
(
key
)
{
const
prop
=
this
.
keyOf
(
key
);
if
(
!
prop
)
{
const
error
=
new
Error
(
'No enum constant'
);
error
.
code
=
'ENUM_NOT_FOUND'
;
throw
error
;
}
return
prop
.
value
;
}
getValueByName
(
name
)
{
const
prop
=
this
.
nameOf
(
name
);
if
(
!
prop
)
{
const
error
=
new
Error
(
'No enum constant'
);
error
.
code
=
'ENUM_NOT_FOUND'
;
throw
error
;
}
return
prop
.
value
;
}
keyOf
(
key
)
{
return
this
.
keyObj
[
key
];
}
nameOf
(
name
)
{
return
this
.
nameObj
[
name
];
}
valueOf
(
value
)
{
return
this
.
valueObj
[
value
];
}
}
</script>
<script>
const
{
createApp
,
defineComponent
,
ref
,
onMounted
,
reactive
}
=
Vue
;
const
{
useMessage
,
useNotification
,
dateZhCN
,
zhCN
}
=
naive
const
tk
=
"16a595e56ea0cec57ad8c07368d7f1ac"
const
mapBoxToken
=
'pk.eyJ1IjoieGluZ3NhaSIsImEiOiJjbTc0ZXJ4ZjgwOWF3MmtyMjFxN2E2MHo0In0.pknVy3Lq6fLcXiwvrVLcFg'
// mapKey
const
MapKeyEnum
=
{
// cp点标注相关
CP_ID
:
'cp_id'
,
CP_KEY
:
'cp_'
,
CP_SOURCE_ID
:
'cp_source_id'
,
KM_CIRCLE_ID
:
'km_circle_id'
,
// 公里数相关
KM_KEY
:
'km_'
,
KM_SOURCE_ID
:
'km_source_id'
,
KM_TEXT_ID
:
'km_text_id'
,
// 轨迹相关
LINE_ID
:
'line_id'
,
LINE_KEY
:
'line_'
,
LINE_SOURCE_ID
:
'line_source_id'
,
// 定义的字段
NAME_KEY
:
'name'
,
}
// 点位类型
const
pointTypes
=
[
{
id
:
0
,
name
:
'计时点'
,
},
{
id
:
1
,
name
:
'起点'
,
},
{
id
:
2
,
name
:
'终点'
,
}
]
// 地图用到的图标
const
mapIcons
=
{
0
:
'https://geexekimgoss-sxk.oss-cn-hangzhou.aliyuncs.com/gk/common/map-icon/icon-cp.png'
,
1
:
'https://geexekimgoss-sxk.oss-cn-hangzhou.aliyuncs.com/gk/common/map-icon/icon-start.png'
,
2
:
'https://geexekimgoss-sxk.oss-cn-hangzhou.aliyuncs.com/gk/common/map-icon/icon-end.png'
}
// 地图图标枚举
const
MapIconEnum
=
{
'ICON_CP'
:
mapIcons
[
0
],
'ICON_START_POINT'
:
mapIcons
[
1
],
'ICON_END_POINT'
:
mapIcons
[
2
],
}
// 打卡点类型
const
CheckPointTypeEnum
=
new
SuperEnum
([
{
key
:
'MARKER'
,
name
:
'标记点'
,
color
:
'warning'
,
value
:
0
},
{
key
:
'START'
,
name
:
'起点'
,
color
:
'primary'
,
value
:
1
},
{
key
:
'END'
,
name
:
'终点'
,
color
:
'success'
,
value
:
2
},
{
key
:
'CHECK_POINT'
,
name
:
'打卡点'
,
color
:
'info'
,
value
:
3
},
])
// 状态枚举
const
StatusEnum
=
new
SuperEnum
([
{
key
:
'CLOSE'
,
name
:
'关闭'
,
color
:
'error'
,
value
:
0
},
{
key
:
'OPEN'
,
name
:
'开启'
,
color
:
'success'
,
value
:
1
},
])
/**
* GPS公共配置信息
*/
const
GpsConfig
=
{
// 地图配置
map
:
{
zoom
:
8
,
center
:
[
106.67573046471102
,
26.411047728805663
],
centerPadding
:
[
100
,
100
,
100
,
100
],
maxZoom
:
20
,
minZoom
:
3
,
centerZoom
:
10
,
speed
:
0.5
,
curve
:
1.42
,
threePitch
:
45
,
defaultPitch
:
0
,
minTileCacheSize
:
20
*
1024
*
1024
,
// 20MB
maxTileCacheSize
:
100
*
1024
*
1024
,
// 100MB
duration
:
1000
,
},
// 文字配置
text
:
{
textColor
:
'#ffffff'
,
padding
:
2
,
textAlign
:
'center'
,
textBaseline
:
'middle'
,
lineWidth
:
1
,
lineHeight
:
1.5
,
backgroundRadius
:
5
,
haloWidth
:
25
,
haloPadding
:
8
,
},
km
:
{
circleOpacity
:
0.5
,
circleRadius
:
12
,
font
:
[
'Arial Unicode MS Regular'
],
fontSize
:
14
,
circleBorderWidth
:
4
,
circleBorderColor
:
'#94e697'
,
textColor
:
'#6defde'
,
circleColor
:
'#ffffff'
,
textOffset
:
[
0
,
0.1
],
},
// cp点的配置信息
cp
:
{
anchor
:
[
0.5
,
1
],
scale
:
0.15
,
fontSize
:
14
,
iconSize
:
1
,
iconCommonSize
:
0.2
,
textColor
:
'#eba236'
,
haloWidth
:
2
,
font
:
[
'Arial Unicode MS Regular'
],
textOffset
:
[
0
,
-
0.2
],
},
// 轨迹配置
path
:
{
width
:
5
,
color
:
"#7367F0"
},
};
// 获取天地图的地址
const
getTDTUrls
=
()
=>
{
const
services
=
Array
.
from
({
length
:
7
},
(
_
,
i
)
=>
i
+
1
);
...
...
@@ -151,46 +613,795 @@
name
:
'MyApp'
,
template
:
'#my-app'
,
setup
()
{
const
loading
=
ref
(
false
)
const
message
=
useMessage
()
const
notification
=
useNotification
()
const
mapContainer
=
ref
(
null
);
onMounted
(()
=>
{
fetchData
()
});
const
fetchData
=
async
()
=>
{
// 初始化地图
initMap
();
}
/**------------------------ 地图初始化 start ------------------------**/
// 地图对象
let
map
=
ref
(
null
);
// 初始化地图
const
initMap
=
()
=>
{
// 确保容器已完全渲染
setTimeout
(()
=>
{
mapboxgl
.
config
.
MAX_PARALLEL_IMAGE_REQUESTS
=
256
;
map
.
value
=
new
mapboxgl
.
Map
({
container
:
'map'
,
style
:
defaultStyle
,
container
:
'map'
,
center
:
GpsConfig
.
map
.
center
,
zoom
:
GpsConfig
.
map
.
zoom
,
accessToken
:
mapBoxToken
,
minZoom
:
GpsConfig
.
map
.
minZoom
,
maxZoom
:
GpsConfig
.
map
.
maxZoom
,
pitch
:
GpsConfig
.
map
.
defaultPitch
,
// 初始倾斜角度,0 表示 2D 模式
antialias
:
true
,
// 抗锯齿
bearing
:
0
,
// 初始旋转角度
interactive
:
true
,
// 可交互
attributionControl
:
false
,
minTileCacheSize
:
GpsConfig
.
map
.
minTileCacheSize
,
maxTileCacheSize
:
GpsConfig
.
map
.
maxTileCacheSize
,
// 调整瓦片缓存大小
});
onMounted
(()
=>
{
if
(
mapContainer
.
value
)
{
// 确保容器已完全渲染
setTimeout
(()
=>
{
map
.
value
=
new
mapboxgl
.
Map
({
container
:
mapContainer
.
value
,
style
:
defaultStyle
,
center
:
[
116.404
,
39.915
],
zoom
:
11
,
minZoom
:
5
,
maxZoom
:
18
,
pitch
:
0
,
accessToken
:
mapBoxToken
,
antialias
:
true
,
bearing
:
0
,
interactive
:
true
,
attributionControl
:
false
,
// 添加resize事件监听器
window
.
addEventListener
(
'resize'
,
()
=>
{
map
.
value
.
resize
();
});
// 监听 pitchChange 事件
map
.
value
.
on
(
'load'
,
()
=>
{
loadMapIcons
().
then
(()
=>
{
refreshLiveMap
();
destroyEvent
();
initMapEvent
();
initMapControls
();
});
// 添加resize事件监听器
window
.
addEventListener
(
'resize'
,
()
=>
{
map
.
value
.
resize
();
});
},
0
);
}
/**
* 刷新地图
*/
const
refreshLiveMap
=
()
=>
{
clearMap
();
initPolyline
();
initMarkers
();
initCenter
();
};
/**
* 清除地图
*/
const
clearMap
=
()
=>
{
if
(
!
map
.
value
)
return
;
const
layers
=
map
.
value
.
getStyle
()?.
layers
;
layers
.
forEach
((
layer
)
=>
{
if
(
layer
.
id
.
includes
(
MapKeyEnum
.
KM_KEY
)
||
layer
.
id
.
includes
(
MapKeyEnum
.
CP_KEY
)
||
layer
.
id
.
includes
(
MapKeyEnum
.
LINE_KEY
)
)
{
// 删除图层
map
.
value
?.
removeLayer
(
layer
.
id
);
// 检查并删除对应的数据源
if
(
map
.
value
?.
getSource
(
layer
.
id
))
{
map
.
value
.
removeSource
(
layer
.
id
);
}
}
});
};
/**
* 初始化中心
* */
const
initCenter
=
()
=>
{
const
allCoordinates
=
trackCoordinates
.
value
.
map
(
(
v
)
=>
[
v
[
0
],
v
[
1
]],
);
if
(
allCoordinates
.
length
>
0
)
{
const
bounds
=
allCoordinates
?.
reduce
(
(
bounds
,
coord
)
=>
{
return
bounds
.
extend
(
coord
);
},
new
mapboxgl
.
LngLatBounds
(
allCoordinates
[
0
],
allCoordinates
[
0
]),
);
map
.
value
?.
fitBounds
(
bounds
,
{
padding
:
GpsConfig
.
map
.
centerPadding
,
duration
:
GpsConfig
.
map
.
duration
,
essential
:
true
,
});
}
};
/**
* 初始化轨迹
*/
const
initPolyline
=
()
=>
{
if
(
!
map
.
value
)
return
;
if
(
!
trackCoordinates
.
value
.
length
)
return
// 添加 polyline 到地图
const
coordinates
=
trackCoordinates
.
value
.
map
((
v
)
=>
[
v
[
0
],
v
[
1
]]);
const
sourceId
=
MapKeyEnum
.
LINE_SOURCE_ID
;
const
layerId
=
MapKeyEnum
.
LINE_ID
// 移除旧的轨迹图层和数据源
if
(
map
.
value
?.
getLayer
(
layerId
))
{
map
.
value
?.
removeLayer
(
layerId
);
}
if
(
map
.
value
?.
getSource
(
sourceId
))
{
map
.
value
?.
removeSource
(
sourceId
);
}
console
.
log
({
coordinates
})
if
(
!
map
.
value
?.
getSource
(
sourceId
))
{
map
.
value
?.
addSource
(
sourceId
,
{
type
:
'geojson'
,
data
:
{
type
:
'Feature'
,
geometry
:
{
type
:
'LineString'
,
coordinates
,
},
},
});
map
.
value
?.
addLayer
({
id
:
layerId
,
type
:
'line'
,
source
:
sourceId
,
paint
:
{
'line-color'
:
GpsConfig
.
path
.
color
,
'line-width'
:
GpsConfig
.
path
.
width
,
},
});
}
};
/**
* 初始化标记点
*/
const
initMarkers
=
()
=>
{
const
kms
=
trackCoordinates
.
value
?.
length
?
calculateDistanceWithTurf
(
trackCoordinates
.
value
.
map
((
v
)
=>
[
v
.
longitude
,
v
.
latitude
]),
)
:
[];
// 构建cp点
const
cpFeatures
=
markers
.
value
.
map
((
cp
)
=>
{
const
{
latitude
,
longitude
,
name
,
ID
,
type
}
=
cp
;
let
icon
=
cp
.
icon
;
if
(
!
icon
)
{
switch
(
type
)
{
case
pointTypes
[
0
].
value
:
{
icon
=
MapIconEnum
.
ICON_CP
;
break
;
}
case
pointTypes
[
2
].
value
:
{
icon
=
MapIconEnum
.
ICON_END_POINT
;
break
;
}
case
pointTypes
[
1
].
value
:
{
icon
=
MapIconEnum
.
ICON_START_POINT
;
break
;
}
default
:
{
icon
=
MapIconEnum
.
ICON_CP
;
}
}
}
return
{
type
:
'Feature'
,
geometry
:
{
type
:
'Point'
,
coordinates
:
[
longitude
,
latitude
],
},
properties
:
{
text
:
name
,
color
:
GpsConfig
.
cp
.
textColor
,
[
MapKeyEnum
.
NAME_KEY
]:
ID
,
icon
,
},
};
});
// 公里数的展示
const
kmFeatures
=
kms
.
map
((
km
,
kmIndex
)
=>
{
const
kmId
=
MapKeyEnum
.
KM_KEY
+
kmIndex
;
return
{
type
:
'Feature'
,
geometry
:
{
type
:
'Point'
,
coordinates
:
km
,
},
properties
:
{
text
:
kmIndex
+
1
,
color
:
GpsConfig
.
text
.
textColor
,
id
:
kmId
,
circleColor
:
GpsConfig
.
km
.
circleColor
,
},
};
});
// 异步添加/更新 数据
if
(
!
map
.
value
)
return
;
// 强制移除旧的数据源和图层
if
(
map
.
value
.
getLayer
(
MapKeyEnum
.
KM_CIRCLE_ID
))
{
map
.
value
.
removeLayer
(
MapKeyEnum
.
KM_CIRCLE_ID
);
}
if
(
map
.
value
.
getLayer
(
MapKeyEnum
.
KM_TEXT_ID
))
{
map
.
value
.
removeLayer
(
MapKeyEnum
.
KM_TEXT_ID
);
}
if
(
map
.
value
.
getLayer
(
MapKeyEnum
.
CP_ID
))
{
map
.
value
.
removeLayer
(
MapKeyEnum
.
CP_ID
);
}
if
(
map
.
value
.
getSource
(
MapKeyEnum
.
KM_SOURCE_ID
))
{
map
.
value
.
removeSource
(
MapKeyEnum
.
KM_SOURCE_ID
);
}
if
(
map
.
value
.
getSource
(
MapKeyEnum
.
CP_SOURCE_ID
))
{
map
.
value
.
removeSource
(
MapKeyEnum
.
CP_SOURCE_ID
);
}
// 添加公里数图层
if
(
map
.
value
.
getSource
(
MapKeyEnum
.
KM_SOURCE_ID
))
{
// 如果 sourceId 存在,设置数据
(
map
.
value
.
getSource
(
MapKeyEnum
.
KM_SOURCE_ID
)).
setData
({
type
:
'FeatureCollection'
,
features
:
kmFeatures
,
});
}
else
{
// 如果 sourceId 不存在,添加 source
map
.
value
.
addSource
(
MapKeyEnum
.
KM_SOURCE_ID
,
{
type
:
'geojson'
,
data
:
{
type
:
'FeatureCollection'
,
features
:
kmFeatures
,
},
});
// 添加图层
map
.
value
.
addLayer
({
id
:
MapKeyEnum
.
KM_CIRCLE_ID
,
type
:
'circle'
,
source
:
MapKeyEnum
.
KM_SOURCE_ID
,
paint
:
{
'circle-radius'
:
GpsConfig
.
km
.
circleRadius
,
// 允许文本相互重叠
'circle-color'
:
[
'get'
,
'circleColor'
],
'circle-stroke-width'
:
GpsConfig
.
km
.
circleBorderWidth
,
// 边框宽度
'circle-stroke-color'
:
GpsConfig
.
km
.
circleBorderColor
,
// 边框颜色,这里设置为白色
},
});
map
.
value
.
addLayer
({
id
:
MapKeyEnum
.
KM_TEXT_ID
,
type
:
'symbol'
,
source
:
MapKeyEnum
.
KM_SOURCE_ID
,
minzoom
:
0
,
maxzoom
:
24
,
layout
:
{
'text-field'
:
[
'get'
,
'text'
],
'text-anchor'
:
'center'
,
'text-size'
:
GpsConfig
.
km
.
fontSize
,
'text-font'
:
GpsConfig
.
km
.
font
,
'text-padding'
:
GpsConfig
.
text
.
padding
,
// 允许图标相互重叠
'icon-allow-overlap'
:
true
,
// 允许文本相互重叠
'text-allow-overlap'
:
true
,
},
paint
:
{
'text-color'
:
GpsConfig
.
km
.
textColor
,
},
});
}
// 添加cp点图层
if
(
map
.
value
.
getSource
(
MapKeyEnum
.
CP_SOURCE_ID
))
{
// 如果 sourceId 存在,设置数据
(
map
.
value
.
getSource
(
MapKeyEnum
.
CP_SOURCE_ID
)).
setData
({
type
:
'FeatureCollection'
,
features
:
cpFeatures
,
});
}
else
{
// 如果 sourceId 不存在,添加 source
map
.
value
.
addSource
(
MapKeyEnum
.
CP_SOURCE_ID
,
{
type
:
'geojson'
,
data
:
{
type
:
'FeatureCollection'
,
features
:
cpFeatures
,
},
});
map
.
value
.
addLayer
({
id
:
MapKeyEnum
.
CP_ID
,
type
:
'symbol'
,
source
:
MapKeyEnum
.
CP_SOURCE_ID
,
// 设置图层在所有缩放级别显示
layout
:
{
'icon-image'
:
[
'get'
,
'icon'
],
'icon-anchor'
:
'bottom'
,
'text-anchor'
:
'top'
,
'text-size'
:
GpsConfig
.
cp
.
fontSize
,
'text-padding'
:
GpsConfig
.
text
.
padding
,
'text-font'
:
GpsConfig
.
cp
.
font
,
'text-field'
:
[
'get'
,
'text'
],
'text-justify'
:
'center'
,
'text-pitch-alignment'
:
'viewport'
,
'text-line-height'
:
GpsConfig
.
text
.
lineHeight
,
'icon-size'
:
GpsConfig
.
cp
.
iconSize
,
'text-offset'
:
GpsConfig
.
cp
.
textOffset
,
// 允许图标相互重叠
'icon-allow-overlap'
:
true
,
// 允许文本相互重叠
'text-allow-overlap'
:
true
,
},
paint
:
{
'text-color'
:
GpsConfig
.
cp
.
textColor
,
// 添加字体描边颜色
'text-halo-color'
:
GpsConfig
.
text
.
textColor
,
// 可根据需要修改颜色
// 添加字体描边宽度
'text-halo-width'
:
GpsConfig
.
cp
.
haloWidth
,
// 可根据需要修改宽度
},
});
}
};
/**
* 计算总距离和公里点
*/
function
calculateDistanceWithTurf
(
coordinates
,
)
{
// 1. 创建LineString
const
line
=
turf
.
lineString
(
coordinates
);
// 2. 计算总长度(公里)
const
totalDistance
=
turf
.
length
(
line
,
{
units
:
'kilometers'
});
// 3. 计算每公里的点位
const
kmPoints
=
[];
for
(
let
km
=
1
;
km
<=
Math
.
floor
(
totalDistance
);
km
++
)
{
const
along
=
turf
.
along
(
line
,
km
,
{
units
:
'kilometers'
});
kmPoints
.
push
(
along
.
geometry
.
coordinates
);
}
return
kmPoints
;
}
/**
* 初始化地图单位
*/
const
initMapControls
=
()
=>
{
if
(
!
map
.
value
)
return
;
// 添加比例尺控件到地图
map
.
value
.
addControl
(
new
mapboxgl
.
ScaleControl
(),
'bottom-right'
);
};
/**
* 加载静态资源
*/
const
loadMapIcons
=
()
=>
{
// 加载图标图片
const
icons
=
Object
.
values
(
mapIcons
);
const
loadIcons
=
icons
.
map
((
icon
)
=>
{
// 检查图片是否已经存在
if
(
map
.
value
?.
hasImage
(
icon
))
{
return
Promise
.
resolve
(
true
);
}
return
new
Promise
((
resolve
,
reject
)
=>
{
map
.
value
?.
loadImage
(
icon
,
(
error
,
image
)
=>
{
if
(
error
)
{
// console.error(`Failed to load image: ${icon}`, error);
reject
(
error
);
}
else
{
if
(
map
.
value
?.
hasImage
(
icon
))
{
return
resolve
(
true
);
}
map
.
value
?.
addImage
(
icon
,
image
);
resolve
(
true
);
}
});
});
});
return
Promise
.
all
(
loadIcons
);
};
// 销毁之前的事件监听器
const
destroyEvent
=
()
=>
{
if
(
!
map
.
value
)
return
;
// 移除地图事件
map
.
value
.
off
(
'click'
,
MapKeyEnum
.
CP_ID
,
onCp
);
map
.
value
.
off
(
'click'
,
onMapClick
);
map
.
value
.
off
(
'mouseenter'
);
map
.
value
.
off
(
'mouseleave'
);
map
.
value
?.
off
(
'mousemove'
,
handleMapMouseMove
);
};
/**
* 添加地图点击事件处理函数
* @param event 地图点击事件
*/
const
onCp
=
(
event
)
=>
{
event
.
preventDefault
()
try
{
if
(
!
map
.
value
)
return
;
// 点击标记点·
// 筛选出 players-icons 图层的要素
const
features
=
map
.
value
.
queryRenderedFeatures
(
event
.
point
,
{
layers
:
[
MapKeyEnum
.
CP_ID
],
});
if
(
features
.
length
>
0
)
{
const
feature
=
features
[
0
];
if
(
!
feature
?.
properties
)
return
;
const
id
=
feature
.
properties
[
MapKeyEnum
.
NAME_KEY
];
// emits('onMarker', id);
console
.
log
({
markId
:
id
})
}
}
catch
(
error
)
{
console
.
error
(
'地图点击事件处理出错:'
,
error
);
}
};
// 点位拾取
const
onMapClick
=
(
e
)
=>
{
if
(
!
map
.
value
||
!
isAddMode
.
value
)
return
;
const
features
=
map
.
value
.
queryRenderedFeatures
(
e
.
point
,
{
layers
:
[
MapKeyEnum
.
CP_ID
]
});
if
(
features
.
length
)
return
// 获取点击位置的坐标
const
coordinates
=
e
.
lngLat
;
// console.log('拾取的点位坐标:', coordinates);
markerForm
.
latitude
=
coordinates
.
lat
.
toString
();
markerForm
.
longitude
=
coordinates
.
lng
.
toString
();
markerForm
.
name
=
'新增标记点'
editCheckPoint
(
null
)
// 添加地图控件
map
.
value
.
addControl
(
new
mapboxgl
.
NavigationControl
());
},
0
);
// 可以在这里添加自定义逻辑,如:
// 1. 显示坐标信息
// 2. 在地图上添加标记
// 3. 触发其他业务逻辑
}
/**
* 提取 mousemove 事件处理函数
* @param e
*/
const
handleMapMouseMove
=
(
e
)
=>
{
// 否则光标是十字准线。
if
(
map
.
value
?.
getCanvas
())
{
map
.
value
.
getCanvas
().
style
.
cursor
=
!
isAddMode
.
value
?
'pointer'
:
'crosshair'
;
}
});
};
// 初始化 地图事件
const
initMapEvent
=
()
=>
{
if
(
!
map
.
value
)
return
;
map
.
value
.
on
(
'click'
,
[
MapKeyEnum
.
CP_ID
],
onCp
);
// 鼠标悬停在聚合点上时改变鼠标样式
map
.
value
.
on
(
'mouseenter'
,
[
MapKeyEnum
.
CP_ID
],
()
=>
{
if
(
!
map
.
value
)
return
;
map
.
value
.
getCanvas
().
style
.
cursor
=
'pointer'
;
});
// 鼠标离开聚合点时恢复鼠标样式
map
.
value
.
on
(
'mouseleave'
,
[
MapKeyEnum
.
CP_ID
],
()
=>
{
if
(
!
map
.
value
)
return
;
map
.
value
.
getCanvas
().
style
.
cursor
=
''
;
});
// 测距的时候监听鼠标悬浮事件
map
.
value
?.
on
(
'mousemove'
,
handleMapMouseMove
);
// 添加地图点击拾取点位事件
map
.
value
.
on
(
'click'
,
onMapClick
);
};
/**------------------------ 地图初始化 end ------------------------**/
/**------------------------ 轨迹导入 start ------------------------**/
// 轨迹的名称
const
fileName
=
ref
(
''
)
// 轨迹上传的input
const
fileInputRef
=
ref
(
null
);
// 确保是二维数组类型
const
trackCoordinates
=
ref
([]);
const
markers
=
ref
([]);
// 处理文件选择
const
handleFileChange
=
async
(
event
)
=>
{
const
input
=
event
.
target
;
if
(
input
.
files
&&
input
.
files
.
length
>
0
)
{
const
file
=
input
.
files
[
0
];
const
fileType
=
file
.
name
.
split
(
'.'
).
pop
()?.
toLowerCase
();
if
(
fileType
===
'gpx'
||
fileType
===
'kml'
)
{
if
(
file
.
size
<=
10
*
1024
*
1024
)
{
// 检查文件大小不超过 10M
message
.
success
(
`
${
file
.
name
}
文件选择成功`
);
}
else
{
message
.
error
(
`
${
file
.
name
}
文件大小超过 10M,请重新选择`
);
}
}
else
{
message
.
error
(
`只能上传 GPX 或 KML 文件,请重新选择`
);
}
if
(
file
)
{
fileName
.
value
=
file
.
name
;
const
text
=
await
file
.
text
();
if
(
file
.
name
.
endsWith
(
'.gpx'
))
{
parseGPX
(
text
);
}
else
if
(
file
.
name
.
endsWith
(
'.kml'
))
{
parseKML
(
text
);
}
}
}
};
// 解析gpx
async
function
parseGPX
(
gpxData
)
{
try
{
const
goxDom
=
new
DOMParser
().
parseFromString
(
gpxData
,
"text/xml"
);
const
geoJSON
=
toGeoJSON
.
gpx
(
goxDom
);
trackCoordinates
.
value
=
geoJSON
.
features
.
filter
((
f
)
=>
f
.
geometry
?.
type
===
'LineString'
)
.
flatMap
((
f
)
=>
f
.
geometry
?.
coordinates
?.
map
((
coord
)
=>
[
coord
[
0
],
coord
[
1
],
coord
[
2
]
||
0
,
]),
)
??
[];
markers
.
value
=
geoJSON
.
features
.
filter
((
f
)
=>
f
.
geometry
?.
type
===
'Point'
&&
f
.
properties
?.
name
)
.
map
(
(
p
,
index
)
=>
({
name
:
p
.
properties
?.
name
||
'未命名'
,
longitude
:
p
.
geometry
.
coordinates
[
0
],
latitude
:
p
.
geometry
.
coordinates
[
1
],
altitude
:
p
.
geometry
.
coordinates
[
2
]
||
0
,
status
:
StatusEnum
.
OPEN
.
value
,
type
:
CheckPointTypeEnum
.
MARKER
.
value
,
id
:
index
+
1
}),
);
if
(
trackCoordinates
.
value
.
length
>
0
)
{
importMarkersAndTrackers
();
}
else
{
message
.
error
(
'GPX 文件中未找到有效的轨迹数据'
);
}
importMarkersAndTrackers
();
}
catch
(
error
)
{
console
.
error
(
'Error parsing GPX:'
,
error
);
}
}
// 解析kml
async
function
parseKML
(
kmlData
)
{
try
{
const
kmlDom
=
new
DOMParser
().
parseFromString
(
kmlData
,
"text/xml"
);
const
geoJSON
=
toGeoJSON
.
kml
(
kmlDom
);
// 安全提取并转换轨迹坐标
trackCoordinates
.
value
=
geoJSON
.
features
.
filter
((
f
)
=>
f
.
geometry
?.
type
===
'LineString'
)
.
flatMap
((
f
)
=>
f
.
geometry
?.
coordinates
?.
map
((
coord
)
=>
[
coord
[
0
],
coord
[
1
],
coord
[
2
]
||
0
,
]),
)
??
[];
markers
.
value
=
geoJSON
.
features
.
filter
((
f
)
=>
f
.
geometry
?.
type
===
'Point'
&&
f
.
properties
?.
name
)
.
map
(
(
p
,
index
)
=>
({
name
:
p
.
properties
?.
name
||
'未命名'
,
longitude
:
p
.
geometry
.
coordinates
[
0
],
latitude
:
p
.
geometry
.
coordinates
[
1
],
altitude
:
p
.
geometry
.
coordinates
[
2
]
||
0
,
status
:
StatusEnum
.
OPEN
.
value
,
type
:
CheckPointTypeEnum
.
MARKER
.
value
,
id
:
index
+
1
}),
)
.
filter
(
Boolean
);
if
(
trackCoordinates
.
value
.
length
>
0
)
{
importMarkersAndTrackers
();
}
else
{
message
.
error
(
'KML 文件中未找到有效的轨迹数据'
);
}
}
catch
(
error
)
{
console
.
error
(
'Error parsing KML:'
,
error
);
}
}
// 保存标记点
const
importMarkersAndTrackers
=
async
()
=>
{
// TODO 保存标记点
refreshLiveMap
();
}
/**------------------------ 轨迹导入 end ------------------------**/
/**------------------------ marker start ------------------------**/
const
isEditMarker
=
ref
(
false
)
const
defaultMarkerForm
=
{
id
:
undefined
,
name
:
''
,
longitude
:
''
,
latitude
:
''
,
altitude
:
''
,
status
:
1
,
sort
:
0
,
categoryId
:
undefined
,
type
:
CheckPointTypeEnum
.
CHECK_POINT
.
value
,
}
const
markerForm
=
reactive
({
id
:
undefined
,
name
:
''
,
longitude
:
''
,
latitude
:
''
,
altitude
:
''
,
status
:
1
,
sort
:
0
,
categoryId
:
undefined
,
type
:
CheckPointTypeEnum
.
CHECK_POINT
.
value
,
})
// 点位表单
const
markerFormRef
=
ref
()
// 是否显示弹框
const
editMarkerModal
=
ref
(
false
)
// 是否是点位添加模式
const
isAddMode
=
ref
(
false
)
// 表单规则
const
markerRules
=
{
name
:
{
required
:
true
,
message
:
'请输入点位名称'
,
trigger
:
[
'blur'
,
'input'
],
},
longitude
:
{
required
:
true
,
message
:
'请输入经度'
,
trigger
:
[
'blur'
,
'input'
],
},
latitude
:
{
required
:
true
,
message
:
'请输入纬度'
,
trigger
:
[
'blur'
,
'input'
],
},
}
// 重置
const
resetMarkerForm
=
()
=>
{
Object
.
assign
(
markerForm
,
defaultMarkerForm
)
}
// 编辑点位
const
editCheckPoint
=
(
record
)
=>
{
isEditMarker
.
value
=
!!
record
if
(
record
)
{
Object
.
assign
(
markerForm
,
{
...
record
})
}
editMarkerModal
.
value
=
true
}
// 删除点位
const
deleteCheckPoint
=
(
index
)
=>
{
markers
.
value
.
splice
(
index
,
1
);
message
.
success
(
`删除点位:
${
marker
.
name
}
`
);
refreshLiveMap
()
}
// 取消编辑点
const
onMarkerCancel
=
()
=>
{
editMarkerModal
.
value
=
false
resetMarkerForm
()
}
// 保存编辑点
const
onMarkerSubmit
=
(
e
)
=>
{
e
.
preventDefault
()
markerFormRef
.
value
?.
validate
(
async
(
errors
)
=>
{
if
(
errors
)
return
message
.
error
(
'请检查表单是否填写完整'
)
if
(
isEditMarker
.
value
)
{
const
index
=
markers
.
value
.
findIndex
(
v
=>
v
.
id
===
markerForm
.
id
)
// 编辑点位
Object
.
assign
(
markers
.
value
[
index
],
{
...
markerForm
})
}
else
{
// 新增点位
markers
.
value
.
push
({
...
markerForm
,
id
:
markers
.
value
.
length
+
1
})
}
message
.
success
(
'操作成功'
)
editMarkerModal
.
value
=
false
resetMarkerForm
()
refreshLiveMap
()
})
}
/**------------------------ marker end ------------------------**/
/**------------------------ 分类 start ------------------------**/
const
defaultCategoryForm
=
{
name
:
''
,
status
:
StatusEnum
.
OPEN
.
value
,
sort
:
100
,
}
const
categoryForm
=
reactive
({
...
defaultCategoryForm
})
const
categories
=
ref
([])
const
isEditCategory
=
ref
(
false
)
// 分类表单
const
categoryFormRef
=
ref
()
const
categoryModal
=
ref
(
false
)
// 分类规则
const
categoryRules
=
{
name
:
{
required
:
true
,
message
:
'请输入分类名称'
,
trigger
:
[
'blur'
,
'input'
],
},
}
// 初始化分类
const
resetCategoryForm
=
async
()
=>
{
Object
.
assign
(
categoryForm
,
defaultCategoryForm
)
}
// 编辑分类
const
editCategory
=
(
e
,
record
)
=>
{
e
?.
preventDefault
()
isEditCategory
.
value
=
!!
record
if
(
record
)
{
Object
.
assign
(
categoryForm
,
{
...
record
})
}
categoryModal
.
value
=
true
}
// 保存分类
const
onCategorySubmit
=
(
e
)
=>
{
e
.
preventDefault
()
categoryFormRef
.
value
?.
validate
(
async
(
errors
)
=>
{
if
(
errors
)
return
message
.
error
(
'请检查表单是否填写完整'
)
if
(
isEditCategory
.
value
)
{
const
index
=
categories
.
value
.
findIndex
(
v
=>
v
.
id
===
categoryForm
.
id
)
// 编辑分类
Object
.
assign
(
categories
.
value
[
index
],
{
...
categoryForm
})
}
else
{
// 新增分类
categories
.
value
.
unshift
({
...
categoryForm
,
id
:
categories
.
value
.
length
+
1
})
}
message
.
success
(
'操作成功'
)
categoryModal
.
value
=
false
resetCategoryForm
()
})
}
const
onCategoryCancel
=
()
=>
{
resetCategoryForm
()
categoryModal
.
value
=
false
}
/**------------------------ 分类 end ------------------------**/
// 对外返回的信息
return
{
// 轨迹导入模块
fileInputRef
,
fileName
,
handleFileChange
,
// 公共
CheckPointTypeEnum
,
StatusEnum
,
// 点位编辑模块
markers
,
markerForm
,
markerFormRef
,
isEditMarker
,
editMarkerModal
,
editCheckPoint
,
deleteCheckPoint
,
onMarkerCancel
,
onMarkerSubmit
,
markerRules
,
isAddMode
,
// 分类板块
categories
,
editCategory
,
onCategorySubmit
,
onCategoryCancel
,
categoryRules
,
isEditCategory
,
categoryModal
,
categoryForm
,
categoryFormRef
}
}
})
const
app
=
Vue
.
createApp
({
setup
()
{
return
{
mapContainer
locale
:
zhCN
,
dateLocale
:
dateZhCN
}
}
})
const
app
=
Vue
.
createApp
({})
app
.
component
(
'my-app'
,
MyApp
)
app
.
use
(
naive
)
app
.
mount
(
'#app'
)
...
...
src/main/webapp/static/css/base.css
View file @
510bf5f4
...
...
@@ -116,6 +116,22 @@
margin-top
:
3rem
;
}
.ml-auto
{
margin-left
:
auto
;
}
.mt-auto
{
margin-top
:
auto
;
}
.mb-auto
{
margin-bottom
:
auto
;
}
.mr-auto
{
margin-right
:
auto
;
}
/* 其他方向margin (mr, mb, ml) 和 padding (p, pt, pr, pb, pl) 同理 */
/* 这里省略类似代码,实际使用时建议补全 */
...
...
@@ -1154,4 +1170,256 @@
.bg-opacity-90
{
opacity
:
0.9
;
}
/* 定位相关 */
.relative
{
position
:
relative
;
}
.absolute
{
position
:
absolute
;
}
.fixed
{
position
:
fixed
;
}
.sticky
{
position
:
sticky
;
}
.static
{
position
:
static
;
}
/* 定位偏移 - 基于rem单位 */
.top-0
{
top
:
0
;
}
.top-1
{
top
:
0.25rem
;
}
.top-2
{
top
:
0.5rem
;
}
.top-3
{
top
:
1rem
;
}
.top-4
{
top
:
1.5rem
;
}
.top-5
{
top
:
3rem
;
}
.right-0
{
right
:
0
;
}
.right-1
{
right
:
0.25rem
;
}
.right-2
{
right
:
0.5rem
;
}
.right-3
{
right
:
1rem
;
}
.right-4
{
right
:
1.5rem
;
}
.right-5
{
right
:
3rem
;
}
.bottom-0
{
bottom
:
0
;
}
.bottom-1
{
bottom
:
0.25rem
;
}
.bottom-2
{
bottom
:
0.5rem
;
}
.bottom-3
{
bottom
:
1rem
;
}
.bottom-4
{
bottom
:
1.5rem
;
}
.bottom-5
{
bottom
:
3rem
;
}
.left-0
{
left
:
0
;
}
.left-1
{
left
:
0.25rem
;
}
.left-2
{
left
:
0.5rem
;
}
.left-3
{
left
:
1rem
;
}
.left-4
{
left
:
1.5rem
;
}
.left-5
{
left
:
3rem
;
}
/* 百分比偏移 */
.top-1
\
/
2
{
top
:
50%
;
}
.top-1
\
/
3
{
top
:
33.333333%
;
}
.top-2
\
/
3
{
top
:
66.666667%
;
}
.right-1
\
/
2
{
right
:
50%
;
}
.right-1
\
/
3
{
right
:
33.333333%
;
}
.right-2
\
/
3
{
right
:
66.666667%
;
}
.bottom-1
\
/
2
{
bottom
:
50%
;
}
.bottom-1
\
/
3
{
bottom
:
33.333333%
;
}
.bottom-2
\
/
3
{
bottom
:
66.666667%
;
}
.left-1
\
/
2
{
left
:
50%
;
}
.left-1
\
/
3
{
left
:
33.333333%
;
}
.left-2
\
/
3
{
left
:
66.666667%
;
}
/* 全屏定位 */
.inset-0
{
top
:
0
;
right
:
0
;
bottom
:
0
;
left
:
0
;
}
.inset-x-0
{
left
:
0
;
right
:
0
;
}
.inset-y-0
{
top
:
0
;
bottom
:
0
;
}
/* 固定像素值偏移 */
.top-4px
{
top
:
4px
;
}
.top-8px
{
top
:
8px
;
}
.top-12px
{
top
:
12px
;
}
.top-16px
{
top
:
16px
;
}
/* z-index 相关 */
.z-0
{
z-index
:
0
;
}
.z-1
{
z-index
:
1
;
}
.z-2
{
z-index
:
2
;
}
.z-3
{
z-index
:
3
;
}
.z-4
{
z-index
:
4
;
}
.z-5
{
z-index
:
5
;
}
.z-10
{
z-index
:
10
;
}
.z-20
{
z-index
:
20
;
}
.z-30
{
z-index
:
30
;
}
.z-40
{
z-index
:
40
;
}
.z-50
{
z-index
:
50
;
}
.z-auto
{
z-index
:
auto
;
}
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment