Files
dvcp_v2_wxcp_app/library/apps/AppGuardianship/gsLocation.vue
2024-10-31 14:34:57 +08:00

459 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<section class="gsLocation">
<AiMap class="fill" :map.sync="amap" :lib.sync="mapLib"/>
<div class="searchZone">
<u-search v-model="search" placeholder="请输入姓名" shape="square" bg-color="#fff"
:show-action="false" @search="getList()"/>
<div class="searchResult" v-if="searchResult">
<div class="item" v-for="row in list" :key="row.id" @tap="handleSelect(row)">
<img :src="cdn(row.onlineStatus==1?'zxtx':'lxtx')"/>
<div flex class="column fill">
<b v-html="searchName(row.name)"/>
<div v-text="row.gpsDesc"/>
</div>
</div>
</div>
<u-popup v-model="popup" mode="bottom" :mask="false">
<div class="headerIcon" flex @touchstart="handleTouchStart" @touchmove="handleTouchmoveClose"/>
<div class="selectedInfo">
<div class="header" flex>
<img :src="`${$cdn}AppGuardianship/tx.png`" @tap="handleShowDetail(selected)"/>
<b v-text="selected.name" @tap="handleShowDetail(selected)"/>
<div v-if="selected.abnormalStatus==1" class="abnormal" @tap="handleShowDetail(selected)">异常</div>
<u-icon name="arrow-right" color="#ddd" class="fill" @tap="handleShowDetail(selected)"/>
<make-calls :list="phoneList"/>
</div>
<div flex class="spb wrap">
<div class="detail" v-for="(op,i) in quotas" :key="i" flex>
<img :src="op.icon"/>
<div class="fill" v-text="op.label"/>
<div :class="{abnormal:op.abnormal}" v-text="op.value"/>
</div>
</div>
</div>
<div class="navigation">
<div class="content" flex>
<div flex class="spb wrap">
<div class="fill" v-text="selected.gpsDesc"/>
<span>最后更新{{ selected.lastUpdateTime }}</span>
<div class="battery" flex>
<img :src="batteryIcon"/>
<div v-text="`剩余${selected.electricQuantity}%`"/>
</div>
</div>
<open-map :data="selected"/>
</div>
</div>
</u-popup>
</div>
</section>
</template>
<script>
import {mapState} from "vuex";
import MakeCalls from "./component/makeCalls";
import OpenMap from "./component/openMap";
export default {
name: "gsLocation",
components: {OpenMap, MakeCalls},
computed: {
...mapState(['user']),
markers() {
return this.list.filter(e => e.lng).map(e => {
let abnormal = 'offline'
if (e.onlineStatus == 1) {
switch (e.abnormalStatus) {
case '1':
abnormal = 'warning';
break;
case '2':
abnormal = 'abnormal';
break;
default:
abnormal = ''
}
}
return new this.mapLib.Marker({
position: new this.mapLib.LngLat(e.lng, e.lat),
anchor: 'bottom-center',
content: `<div class="marker ${abnormal}">${e.name}</div>`,
extData: e,
topWhenClick: true
}).on('click', () => {
this.handleSelect(e)
})
})
},
quotas() {
let quota = [
{key: "0", icon: "1"},
{key: "1", icon: "2"},
{key: "2", icon: "3"},
{key: "3", icon: "4"},
]
return quota.map(e => {
let item = this.detail.find(d => d.item == e.key)
let label = this.$dict.getLabel('intelligentGuardianshipItem', e.key)
return {
label, icon: this.cdn(e.icon),
value: item?.itemValue || "-",
abnormal: item?.abnormalStatus == 1
}
})
},
batteryIcon() {
return this.cdn(this.selected.electricQuantity == 100 ? 'dcm' : 'dcq')
},
phoneList() {
let {name: guardianName, phone: guardianPhone} = this.selected
return [{guardianName, guardianPhone}, ...(this.selected.guardians || [])]
},
},
data() {
return {
mapLib: null,
amap: null,
search: "",
selected: {},
list: [],
popup: false,//被监护人信息弹窗
detail: [],
moveDistance: 0,
searchResult: false //搜索下拉弹窗
}
},
watch: {
amap(v) {
v && this.getList().then(() => {
this.amap?.add(this.markers)
this.getMapArea()
})
}
},
methods: {
cdn(icon) {
return `${this.$cdn}AppGuardianship/${icon}.png`
},
getMapArea() {
if (this.mapLib) {
new this.mapLib.DistrictSearch({
subdistrict: 0, //获取边界不需要返回下级行政区
extensions: 'all', //返回行政区边界坐标组等具体信息
level: 'district' //查询行政级别为 市
}).search(this.user.areaId.substring(0, 6), (status, result) => {
let bounds = result?.districtList?.[0]?.boundaries;
let polygons = []
bounds?.forEach(path => polygons.push(new this.mapLib.Polygon({
strokeWeight: 1,
path,
strokeStyle: 'dashed',
fillOpacity: 0.1,
fillColor: '#80d8ff',
strokeColor: '#0091ea'
})))
this.amap.add(polygons)
this.amap.setFitView();//视口自适应
})
}
},
getList() {
this.searchResult = !!this.search
return this.$http.post("/app/appintelligentguardianshipdevice/list", null, {
params: {name: this.search, size: 999, areaId: this.user.areaId}
}).then(res => {
if (res?.data) {
this.list = res.data.records
}
})
},
getDetail(deviceId) {
this.$http.post("/app/appintelligentguardianshipdevice/queryMonitorList", null, {
params: {type: 1, deviceId}
}).then(res => {
if (res?.data) {
this.detail = res.data.records
}
})
this.$http.post("/app/appintelligentguardianshipdevice/queryDetailById", null, {
params: {id: deviceId}
}).then(res => {
if (res?.data) {
this.selected = {...this.selected, ...res.data}
}
})
},
searchName(name) {
return name?.replace(this.search, `<span>${this.search}</span>`)
},
handleSelect(e) {
this.amap.setCenter(new this.mapLib.LngLat(e.lng, e.lat))
this.selected = e
this.popup = true
this.searchResult = false
this.getDetail(e.id)
},
handleShowDetail(user) {
uni.navigateTo({url: `./userDetail?id=${user.id}`})
},
handleTouchmoveClose(e) {
if (e.touches?.[0]?.clientY > this.moveDistance + 10) {
this.popup = false
}
},
handleTouchStart(e) {
this.moveDistance = e.touches?.[0]?.clientY
}
}
}
</script>
<style lang="scss" scoped>
.gsLocation {
height: 100%;
a {
color: inherit;
text-decoration: none;
display: flex;
align-items: center;
justify-content: center;
height: 112px;
border-top: 1px solid #d8dde6;
&:first-of-type {
border-top: none;
}
}
.headerIcon {
width: 100%;
height: 60px;
justify-content: center;
&:before {
content: " ";
display: block;
width: 64px;
height: 10px;
background: #CCCCCC;
border-radius: 5px;
}
}
::v-deep .makeCalls {
.option:last-of-type {
margin-bottom: 120px;
}
}
::v-deep .selectedInfo {
width: 100%;
border-radius: 20px 20px 0 0;
padding: 0 32px;
background: #fff;
overflow: hidden;
box-sizing: border-box;
.header {
margin-bottom: 40px;
font-size: 40px;
& > img {
width: 82px;
height: 82px;
margin-right: 16px;
}
.abnormal {
color: #FF4466;
font-size: 24px;
padding: 12px;
background: rgba(#EC4461, .1);
border-radius: 8px;
margin: 0 16px;
}
span {
margin-left: 0;
color: #999;
font-size: 20px;
}
}
.detail {
width: 318px;
height: 84px;
background: #F4F5F6;
border-radius: 8px;
padding: 0 24px;
box-sizing: border-box;
margin-bottom: 36px;
img {
margin-right: 16px;
width: 56px;
height: 56px;
}
.abnormal {
color: #FF4466;
}
}
}
.navigation {
padding-bottom: 100px;
.content {
padding: 32px 40px;
font-size: 26px;
& > .spb {
margin-right: 40px;
}
.fill {
min-width: 100%;
flex-shrink: 0;
margin-bottom: 10px;
}
span {
margin-left: 0;
color: #999;
font-size: 22px;
}
.battery > img {
margin-right: 14px;
}
}
&:before {
content: " ";
display: block;
width: 100%;
height: 8px;
background: #F4F5F6;
}
}
::v-deep .searchZone {
position: absolute;
top: 0;
z-index: 2;
width: 100%;
background: transparent;
padding: 24px 16px;
box-sizing: border-box;
.u-search {
box-shadow: 0 4px 8px 0 rgba(192, 185, 185, 0.5);
}
.searchResult {
margin-top: 16px;
padding: 0 28px;
background: #fff;
.item {
font-size: 24px;
display: flex;
line-height: 36px;
padding: 24px 0;
border-bottom: 3px solid #DEDFE1;
&:last-of-type {
border-bottom: none;
}
img {
width: 36px;
height: 36px;
margin-right: 16px;
}
& > .fill {
align-items: unset;
b > span {
color: #1365DD;
}
& > div {
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
}
}
::v-deep .marker {
color: #fff;
font-size: 30px;
display: flex;
justify-content: center;
align-items: center;
padding: 0 32px;
height: 56px;
white-space: nowrap;
background: #5088FF;
border-color: #5088FF;
border-radius: 52px;
position: relative;
&:after {
position: absolute;
display: block;
content: " ";
bottom: -12px;
left: 50%;
transform: translateX(-50%);
border: 12px solid transparent;
border-bottom: none;
height: 0;
width: 0;
border-top-color: inherit;
}
&.offline {
background: #C4CAD4;
border-color: #C4CAD4;
}
&.warning {
background: #FFAA44;
border-color: #FFAA44;
}
&.abnormal {
background: #F46159;
border-color: #F46159;
&:before {
position: absolute;
z-index: -1;
bottom: -40px;
width: 80px;
height: 80px;
border-radius: 50%;
background-color: #F46159;
transform: translate(-50%, -50%);
animation: mapWarn 1s ease-out 0s infinite;
content: " ";
}
}
}
.AiMap {
width: 100%;
height: 100%;
}
}
</style>