企微迁移智慧监护(手环)
This commit is contained in:
66
src/apps/AppGuardianship/AppGuardianship.vue
Normal file
66
src/apps/AppGuardianship/AppGuardianship.vue
Normal file
@@ -0,0 +1,66 @@
|
||||
<template>
|
||||
<section class="AppGuardianship">
|
||||
<component ref="currentTab" :is="currentTab.comp"/>
|
||||
<ai-tabbar :active.sync="active" :list="bottomBar"/>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AiLoading from "../../components/AiLoading";
|
||||
import GsLocation from "./gsLocation";
|
||||
import AiTabbar from "../../components/AiTabbar";
|
||||
import WardList from "./wardList";
|
||||
import EarlyWarning from "./earlyWarning";
|
||||
|
||||
export default {
|
||||
name: "AppGuardianship",
|
||||
appName: "智慧监护",
|
||||
components: {AiTabbar, AiLoading},
|
||||
provide() {
|
||||
return {
|
||||
top: this
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
bottomBar() {
|
||||
return [
|
||||
{text: "定位", iconPath: "bardwn", selectedIconPath: "bardwh", comp: GsLocation},
|
||||
{text: "人员", iconPath: "barryn", selectedIconPath: "barryh", comp: WardList},
|
||||
{text: "预警", iconPath: "baryjn", selectedIconPath: "baryjh", comp: EarlyWarning},
|
||||
].map(e => ({
|
||||
...e,
|
||||
iconPath: this.cdn(e.iconPath),
|
||||
selectedIconPath: this.cdn(e.selectedIconPath)
|
||||
}))
|
||||
},
|
||||
currentTab() {
|
||||
return this.bottomBar?.[this.active] || {}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
cdn(icon) {
|
||||
return `${this.$cdn}AppGuardianship/${icon}.png`
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
active: 0
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$dict.load("intelligentGuardianshipItem", 'intelligentGuardianshipItem2', 'intelligentGuardianshipAbnormalStatus')
|
||||
},
|
||||
onReachBottom() {
|
||||
if (typeof this.$refs?.currentTab?.reachBottom == 'function') this.$refs?.currentTab.reachBottom()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.AppGuardianship {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
</style>
|
||||
146
src/apps/AppGuardianship/component/areaSelector.vue
Normal file
146
src/apps/AppGuardianship/component/areaSelector.vue
Normal file
@@ -0,0 +1,146 @@
|
||||
<template>
|
||||
<section class="areaSelector">
|
||||
<ai-search-popup mode="bottom" ref="areaSelector">
|
||||
<template #btn>
|
||||
<div class="areaSelector">
|
||||
<span v-for="area in fullArea" :key="area.id" v-text="area.name"
|
||||
:class="{current:area.id==areaId}" @tap="index=area.id,getChildAreas(area.id)"/>
|
||||
</div>
|
||||
</template>
|
||||
<div class="areaSelector">
|
||||
<span v-for="area in fullArea" :key="area.id" v-text="area.name"
|
||||
:class="{current:area.id==index}"
|
||||
@click="index=area.id,getChildAreas(area.id)"/>
|
||||
</div>
|
||||
<div class="pendingItem" flex v-for="op in list" :key="op.id" @tap="handleSelect(op)">
|
||||
<div class="fill" :class="{self:index==op.id}" v-html="op.name"/>
|
||||
<u-icon name="arrow-right" color="#ddd"/>
|
||||
</div>
|
||||
</ai-search-popup>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AiSearchPopup from "../../../components/AiSearchPopup";
|
||||
import AiCell from "../../../components/AiCell";
|
||||
import {mapState} from "vuex";
|
||||
|
||||
export default {
|
||||
name: "areaSelector",
|
||||
components: {AiCell, AiSearchPopup},
|
||||
props: {
|
||||
areaId: {default: ""}
|
||||
},
|
||||
computed: {
|
||||
...mapState(['user']),
|
||||
dataRange() {
|
||||
let rules = [10, 8, 6, 3, 0], level = 2
|
||||
rules.some((e, i) => {
|
||||
let reg = new RegExp(`0{${e}}`, 'g')
|
||||
if (reg.test(this.user.areaId)) {
|
||||
return level = i
|
||||
}
|
||||
})
|
||||
return level
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
fullArea: [],
|
||||
index: "",
|
||||
list: []
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
areaId(v) {
|
||||
v && this.getFullArea()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getFullArea() {
|
||||
let {areaId} = this
|
||||
return areaId && this.$http.post("/admin/area/getAllParentAreaId", null, {
|
||||
params: {areaId}
|
||||
}).then(res => {
|
||||
if (res?.data) {
|
||||
this.fullArea = res.data.reverse().slice(this.dataRange)
|
||||
}
|
||||
})
|
||||
},
|
||||
getChildAreas(id) {
|
||||
id && this.$http.post("/admin/area/queryAreaByParentId", null, {
|
||||
params: {id}
|
||||
}).then(res => {
|
||||
if (res?.data) {
|
||||
this.list = res.data
|
||||
let self = this.fullArea.find(e => e.id == this.index)
|
||||
this.list.unshift(self)
|
||||
}
|
||||
})
|
||||
},
|
||||
handleSelect(op) {
|
||||
this.$emit('select', op)
|
||||
this.$refs.areaSelector?.handleSelect()
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.index = this.areaId
|
||||
this.getFullArea()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.areaSelector {
|
||||
::v-deep .AiSearchPopup {
|
||||
.areaSelector {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
span {
|
||||
cursor: pointer;
|
||||
|
||||
&:first-of-type:before {
|
||||
content: "";
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&:before {
|
||||
color: #333;
|
||||
content: "/";
|
||||
padding: 0 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.current {
|
||||
color: #3F8DF5;
|
||||
}
|
||||
}
|
||||
|
||||
.u-drawer-content {
|
||||
position: fixed;
|
||||
|
||||
.areaSelector {
|
||||
padding: 0 16px;
|
||||
box-sizing: border-box;
|
||||
border-bottom: 16px solid #f5f5f5;
|
||||
|
||||
span {
|
||||
line-height: 100px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pendingItem {
|
||||
margin-left: 32px;
|
||||
padding-right: 32px;
|
||||
height: 104px;
|
||||
border-bottom: 1px solid #ddd;
|
||||
|
||||
.self {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
75
src/apps/AppGuardianship/component/makeCalls.vue
Normal file
75
src/apps/AppGuardianship/component/makeCalls.vue
Normal file
@@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<section class="makeCalls">
|
||||
<div v-if="$slots.default" @tap="calls=true">
|
||||
<slot/>
|
||||
</div>
|
||||
<div v-else flex class="column" @tap="calls=true">
|
||||
<img :src="`${$cdn}guardianship/dh.png`"/>
|
||||
<span v-html="label"/>
|
||||
</div>
|
||||
<u-popup v-model="calls" mode="bottom">
|
||||
<div flex class="column option" v-for="item in list" @click="handleCall(item.guardianPhone)">
|
||||
{{ [item.guardianName, item.guardianPhone].join(' ') }}
|
||||
</div>
|
||||
<div class="option" @tap="calls=false">取消</div>
|
||||
</u-popup>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "makeCalls",
|
||||
props: {
|
||||
label: {default: "联系ta"},
|
||||
list: {default: () => []}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
calls: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleCall(phone) {
|
||||
location.href = "tel:" + phone
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.makeCalls {
|
||||
img {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
::v-deep span {
|
||||
margin-left: 0;
|
||||
color: #999;
|
||||
font-size: 20px;
|
||||
|
||||
p {
|
||||
color: #3D94FB;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .u-drawer {
|
||||
text-align: center;
|
||||
|
||||
.uni-scroll-view-content {
|
||||
max-height: 672px;
|
||||
}
|
||||
|
||||
.option {
|
||||
font-size: 32px;
|
||||
cursor: pointer;
|
||||
line-height: 112px;
|
||||
border-bottom: 1px solid #D8DDE6;
|
||||
|
||||
&:first-of-type {
|
||||
border-radius: 16px 16px 0 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
40
src/apps/AppGuardianship/component/openMap.vue
Normal file
40
src/apps/AppGuardianship/component/openMap.vue
Normal file
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<section class="openMap">
|
||||
<div flex class="column" shrink @tap="handleOpenMap">
|
||||
<img :src="`${$cdn}guardianship/seat.png`"/>
|
||||
<span v-text="'地图/导航'"/>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "openMap",
|
||||
props: {
|
||||
data: {default: () => ({})}
|
||||
},
|
||||
methods: {
|
||||
handleOpenMap() {
|
||||
let {lng, lat, gpsDesc} = this.data
|
||||
location.href = `https://uri.amap.com/marker?callnative=1&position=${[lng, lat].toString()}&name=${gpsDesc}`
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.openMap {
|
||||
flex-shrink: 0;
|
||||
|
||||
img {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
span {
|
||||
margin-left: 0;
|
||||
color: #999;
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
169
src/apps/AppGuardianship/earlyWarning.vue
Normal file
169
src/apps/AppGuardianship/earlyWarning.vue
Normal file
@@ -0,0 +1,169 @@
|
||||
<template>
|
||||
<section class="earlyWarning">
|
||||
<ai-top-fixed>
|
||||
<u-search v-model="search.name" placeholder="请输入姓名" :show-action="false"
|
||||
search-icon-color="#ccc" placeholder-color="#999"
|
||||
@change="page.current=1,getList()"/>
|
||||
<div flex>
|
||||
<ai-date class="fill" placeholder="日期选择" mode="range" @change="handleDateSearch"/>
|
||||
<ai-select class="fill" dict="intelligentGuardianshipItem2" @data="handleTypeSearch">
|
||||
<div>{{ $dict.getLabel('intelligentGuardianshipItem2', search.item) || '全部预警' }}</div>
|
||||
<i class="iconfont iconfont-iconArrow_Down"/>
|
||||
</ai-select>
|
||||
</div>
|
||||
</ai-top-fixed>
|
||||
<div class="card" v-for="row in list" :key="row.id" @tap="handleShow(row)">
|
||||
<div class="header" flex>
|
||||
<img :src="top.cdn(typeIcons[row.item])"/>
|
||||
<b v-text="row.desc"/>
|
||||
</div>
|
||||
<div class="wrapper">
|
||||
<div class="start" flex>
|
||||
<span v-text="`上报时间:`"/>
|
||||
<div v-text="row.createTime"/>
|
||||
</div>
|
||||
<div class="start" flex>
|
||||
<span v-text="`上报地点:`"/>
|
||||
<div v-text="row.gpsDesc"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AiTopFixed from "../../components/AiTopFixed";
|
||||
import {mapState} from "vuex";
|
||||
import AiDate from "../../components/AiDate";
|
||||
import AiSelect from "../../components/AiSelect";
|
||||
|
||||
export default {
|
||||
name: "earlyWarning",
|
||||
components: {AiSelect, AiDate, AiTopFixed},
|
||||
inject: ['top'],
|
||||
computed: {
|
||||
...mapState(['user']),
|
||||
typeIcons() {
|
||||
return {
|
||||
0: "icon4",
|
||||
1: "icon2",
|
||||
2: "icon5",
|
||||
3: "icon6",
|
||||
4: "icon3",
|
||||
5: "icon1",
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
search: {name: "", createTimeRange: ",", item: ""},
|
||||
areaId: "",
|
||||
page: {current: 1, size: 10, total: 0},
|
||||
list: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getList() {
|
||||
let {areaId} = this
|
||||
this.$http.post("/app/appintelligentguardianshipalarm/list", null, {
|
||||
params: {areaId, ...this.search, ...this.page}
|
||||
}).then(res => {
|
||||
if (res?.data) {
|
||||
let data = res.data.records.map(e => {
|
||||
return {...e, desc: [e.name, this.$dict.getLabel('intelligentGuardianshipItem2', e.item)].join('的')}
|
||||
})
|
||||
if (this.page.current > 1) {
|
||||
this.list = [...this.list, ...data]
|
||||
} else this.list = data
|
||||
this.page.total = res.data.total
|
||||
}
|
||||
})
|
||||
},
|
||||
reachBottom() {
|
||||
if (this.page.total > this.list.length) {
|
||||
this.page.current++
|
||||
this.getList()
|
||||
}
|
||||
},
|
||||
handleDateSearch(v) {
|
||||
let {startDate: start, endDate: end} = v
|
||||
start = this.$dateFormat(start)
|
||||
end = this.$dateFormat(end)
|
||||
this.search.createTimeRange = [start, end || start].toString()
|
||||
this.page.current = 1
|
||||
this.getList()
|
||||
},
|
||||
handleTypeSearch(v) {
|
||||
this.search.item = v?.[0]?.value
|
||||
this.page.current = 1
|
||||
this.getList()
|
||||
},
|
||||
handleShow(row) {
|
||||
uni.navigateTo({url: `./warningDetail?id=${row.id}`})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.areaId = JSON.parse(JSON.stringify(this.user.areaId))
|
||||
this.getList()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.earlyWarning {
|
||||
padding-bottom: 130px;
|
||||
background: #f5f5f5;
|
||||
|
||||
::v-deep .AiDate > div {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
::v-deep .u-drawer-content {
|
||||
padding-bottom: 100px;
|
||||
}
|
||||
|
||||
::v-deep .display {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
::v-deep .iconfont-iconArrow_Down {
|
||||
margin-left: 4px;
|
||||
font-size: 32px;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
::v-deep .card {
|
||||
margin: 32px 32px 0;
|
||||
background: #FFFFFF;
|
||||
box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.02);
|
||||
border-radius: 8px;
|
||||
|
||||
.header {
|
||||
padding: 0 32px;
|
||||
height: 104px;
|
||||
border-bottom: 2px solid #EFEFF4;
|
||||
font-size: 36px;
|
||||
|
||||
img {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
margin-right: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
color: #343D65;
|
||||
font-size: 30px;
|
||||
margin-bottom: 8px;
|
||||
padding: 18px 32px;
|
||||
|
||||
span {
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
color: #999;
|
||||
margin-right: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
461
src/apps/AppGuardianship/gsLocation.vue
Normal file
461
src/apps/AppGuardianship/gsLocation.vue
Normal file
@@ -0,0 +1,461 @@
|
||||
<template>
|
||||
<section class="gsLocation">
|
||||
<ai-map 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}guardianship/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 AiSearchPopup from "../../components/AiSearchPopup";
|
||||
import {mapState} from "vuex";
|
||||
import UPopup from "../../uview/components/u-popup/u-popup";
|
||||
import MakeCalls from "./component/makeCalls";
|
||||
import OpenMap from "./component/openMap";
|
||||
import AiMap from "../../components/AiMap";
|
||||
|
||||
export default {
|
||||
name: "gsLocation",
|
||||
components: {AiMap, OpenMap, MakeCalls, UPopup, AiSearchPopup},
|
||||
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}guardianship/${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>
|
||||
76
src/apps/AppGuardianship/historyList.vue
Normal file
76
src/apps/AppGuardianship/historyList.vue
Normal file
@@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<section class="historyList">
|
||||
<div flex>
|
||||
<b class="header" v-text="itemLabel"/>
|
||||
</div>
|
||||
<div v-for="row in list" :key="row.id" flex class="spb row">
|
||||
<div :class="{abnormal:row.abnormalStatus==1}" v-text="row.itemValue"/>
|
||||
<span v-text="row.sampleTime"/>
|
||||
</div>
|
||||
<ai-back/>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AiBack from "../../components/AiBack";
|
||||
|
||||
export default {
|
||||
name: "historyList",
|
||||
components: {AiBack},
|
||||
computed: {
|
||||
itemLabel() {
|
||||
return '历史' + this.$dict.getLabel('intelligentGuardianshipItem', this.$route.query.type) + `(${this.$dict.getLabel('intelligentGuardianshipItemUnit', this.$route.query.type)})`
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
list: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getHistory() {
|
||||
let {type: item, id: deviceId} = this.$route.query
|
||||
this.$http.post("/app/appintelligentguardianshipdevice/queryMonitorList", null, {
|
||||
params: {deviceId, size: 999, item}
|
||||
}).then(res => {
|
||||
if (res?.data) {
|
||||
this.list = res.data.records
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$dict.load("intelligentGuardianshipItem", 'intelligentGuardianshipItemUnit')
|
||||
this.getHistory()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.historyList {
|
||||
font-size: 30px;
|
||||
|
||||
& > div {
|
||||
padding: 0 32px;
|
||||
border-bottom: 1px solid #ddd;
|
||||
height: 96px;
|
||||
background: #FFFFFF;
|
||||
}
|
||||
|
||||
.header {
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.row {
|
||||
color: #5AAD6A;
|
||||
|
||||
.abnormal {
|
||||
color: #CD413A;
|
||||
}
|
||||
|
||||
& > span {
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
279
src/apps/AppGuardianship/userDetail.vue
Normal file
279
src/apps/AppGuardianship/userDetail.vue
Normal file
@@ -0,0 +1,279 @@
|
||||
<template>
|
||||
<section class="userDetail">
|
||||
<div class="selectedInfo">
|
||||
<div class="header" flex>
|
||||
<img :src="`${$cdn}guardianship/tx.png`"/>
|
||||
<b v-text="detail.name"/>
|
||||
<div v-if="detail.abnormalStatus==1" class="abnormal">异常</div>
|
||||
<div class="fill"/>
|
||||
<make-calls :list="phoneList" :label="`<p>拨打电话</p>`"/>
|
||||
</div>
|
||||
<div class="content">
|
||||
<ai-cell label="所属地区">{{ detail.areaName }}</ai-cell>
|
||||
<ai-cell label="联系电话">{{ detail.phone }}</ai-cell>
|
||||
<ai-cell label="性别">{{ $dict.getLabel('sex', detail.sex) }}</ai-cell>
|
||||
<ai-cell label="年龄">{{ $calcAge(detail.idNumber) }}</ai-cell>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div flex class="spb header">
|
||||
<b v-text="`设备状况`"/>
|
||||
<span class="onlineStatus" v-html="detail.onlineStatus==1?'设备在线':'<p>设备离线</p>'"/>
|
||||
</div>
|
||||
<div flex class="spb wrap quotas">
|
||||
<div class="quota" v-for="(op,i) in quotas" :key="i" flex @tap="handleShowHistory(op)">
|
||||
<img :src="op.icon"/>
|
||||
<div class="fill" v-text="op.label"/>
|
||||
<div :class="{abnormal:op.abnormal}" v-text="op.value"/>
|
||||
<u-icon name="arrow-right" color="#ddd"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="navigation">
|
||||
<div class="content spb" flex>
|
||||
<div flex class="spb wrap">
|
||||
<div class="fill" v-text="detail.gpsDesc"/>
|
||||
<span>最后更新:{{ detail.lastUpdateTime }}</span>
|
||||
<div class="battery" flex>
|
||||
<img :src="batteryIcon"/>
|
||||
<div v-text="`剩余${detail.electricQuantity||0}%`"/>
|
||||
</div>
|
||||
</div>
|
||||
<open-map :data="detail"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div flex class="spb header">
|
||||
<b v-text="`监护人信息`"/>
|
||||
</div>
|
||||
<div flex class="spb guardian" v-for="row in detail.guardians" :key="row.id">
|
||||
<span v-text="row.guardianName"/>
|
||||
<div v-text="row.guardianPhone"/>
|
||||
</div>
|
||||
</div>
|
||||
<ai-back/>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MakeCalls from "./component/makeCalls";
|
||||
import OpenMap from "./component/openMap";
|
||||
import AiCell from "../../components/AiCell";
|
||||
import AiBack from "../../components/AiBack";
|
||||
|
||||
export default {
|
||||
name: "userDetail",
|
||||
components: {AiBack, AiCell, OpenMap, MakeCalls},
|
||||
computed: {
|
||||
batteryIcon() {
|
||||
return this.cdn(this.detail.electricQuantity == 100 ? 'dcm' : 'dcq')
|
||||
},
|
||||
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.quota?.find(d => d.item == e.key)
|
||||
let label = this.$dict.getLabel('intelligentGuardianshipItem', e.key)
|
||||
return {
|
||||
label, icon: this.cdn(e.icon), type: e.key,
|
||||
value: item?.itemValue || "-",
|
||||
abnormal: item?.abnormalStatus == 1
|
||||
}
|
||||
})
|
||||
},
|
||||
phoneList() {
|
||||
let {name: guardianName, phone: guardianPhone} = this.detail
|
||||
return [{guardianName, guardianPhone}, ...this.detail.guardians]
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
detail: {
|
||||
guardians: []
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
cdn(icon) {
|
||||
return `${this.$cdn}guardianship/${icon}.png`
|
||||
},
|
||||
getDetail(deviceId) {
|
||||
this.$http.post("/app/appintelligentguardianshipdevice/queryMonitorList", null, {
|
||||
params: {type: 1, deviceId}
|
||||
}).then(res => {
|
||||
if (res?.data) {
|
||||
this.$set(this.detail, 'quota', res.data.records)
|
||||
}
|
||||
})
|
||||
this.$http.post("/app/appintelligentguardianshipdevice/queryDetailById", null, {
|
||||
params: {id: deviceId}
|
||||
}).then(res => {
|
||||
if (res?.data) {
|
||||
this.detail = {...this.detail, ...res.data}
|
||||
}
|
||||
})
|
||||
},
|
||||
handleShowHistory(item) {
|
||||
uni.navigateTo({url: `./historyList?type=${item.type}&id=${this.$route.query.id}`})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$dict.load("intelligentGuardianshipItem", 'sex')
|
||||
this.getDetail(this.$route.query.id)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.userDetail {
|
||||
padding-bottom: 60px;
|
||||
|
||||
& > * {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: #fff;
|
||||
font-size: 32px;
|
||||
box-shadow: 0 1px 1px 0 rgba(221, 221, 221, 1);
|
||||
|
||||
.header {
|
||||
height: 96px;
|
||||
background: #FFFFFF;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
& > .spb {
|
||||
padding: 0 32px;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .selectedInfo {
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
|
||||
.header {
|
||||
height: 136px;
|
||||
font-size: 40px;
|
||||
padding: 0 32px;
|
||||
border-bottom: 1px solid #D8DDE6;
|
||||
|
||||
& > img {
|
||||
width: 82px;
|
||||
height: 82px;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.abnormal {
|
||||
color: #FF4466;
|
||||
font-size: 24px;
|
||||
padding: 12px;
|
||||
background: rgba(#EC4461, .1);
|
||||
border-radius: 8px;
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
span {
|
||||
margin-left: 0;
|
||||
color: #999;
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
& > .content {
|
||||
padding: 32px;
|
||||
font-size: 30px;
|
||||
|
||||
::v-deep .AiCell {
|
||||
padding: 0;
|
||||
min-height: 58px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.quotas {
|
||||
margin-top: 24px;
|
||||
|
||||
.quota {
|
||||
cursor: pointer;
|
||||
width: 318px;
|
||||
height: 84px;
|
||||
background: #F4F5F6;
|
||||
border-radius: 8px;
|
||||
padding: 0 8px 0 24px;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 36px;
|
||||
font-size: 28px;
|
||||
|
||||
img {
|
||||
margin-right: 16px;
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
}
|
||||
|
||||
.abnormal {
|
||||
color: #FF4466;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.navigation {
|
||||
.content {
|
||||
padding: 10px 40px 32px;
|
||||
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-left: 32px;
|
||||
margin-right: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .onlineStatus {
|
||||
font-size: 30px;
|
||||
color: #5AAD6A;
|
||||
|
||||
p {
|
||||
color: #F5A319;
|
||||
}
|
||||
}
|
||||
|
||||
.guardian {
|
||||
height: 96px;
|
||||
border-bottom: 1px solid #ddd;
|
||||
font-size: 28px;
|
||||
color: #222;
|
||||
|
||||
&:last-of-type {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #666666;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
106
src/apps/AppGuardianship/wardList.vue
Normal file
106
src/apps/AppGuardianship/wardList.vue
Normal file
@@ -0,0 +1,106 @@
|
||||
<template>
|
||||
<section class="wardList">
|
||||
<ai-top-fixed>
|
||||
<u-search v-model="search" placeholder="请输入姓名" :show-action="false"
|
||||
search-icon-color="#ccc" placeholder-color="#999"
|
||||
@change="page.current=1,getUser()"/>
|
||||
<area-selector :areaId="areaId" @select="handleSelectArea"/>
|
||||
</ai-top-fixed>
|
||||
<div class="userList">
|
||||
<div v-for="row in list" :key="row.id" flex class="row" @tap="handleShowDetail(row)">
|
||||
<img :src="top.cdn(row.onlineStatus==1?'zxtx':'lxtx')"/>
|
||||
<b class="fill" v-text="row.name"/>
|
||||
<div class="status" :style="{color:$dict.getColor('intelligentGuardianshipAbnormalStatus',row.abnormalStatus)}"
|
||||
v-text="$dict.getLabel('intelligentGuardianshipAbnormalStatus',row.abnormalStatus)"/>
|
||||
<u-icon name="arrow-right" color="#ddd"/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AiTopFixed from "../../components/AiTopFixed";
|
||||
import {mapState} from "vuex";
|
||||
import AreaSelector from "./component/areaSelector";
|
||||
|
||||
export default {
|
||||
name: "wardList",
|
||||
components: {AreaSelector, AiTopFixed},
|
||||
computed: {
|
||||
...mapState(['user']),
|
||||
},
|
||||
inject: ['top'],
|
||||
data() {
|
||||
return {
|
||||
search: "",
|
||||
areaId: "",
|
||||
page: {current: 1, size: 20, total: 0},
|
||||
list: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getUser() {
|
||||
let {areaId, search: name} = this
|
||||
this.$http.post("/app/appintelligentguardianshipdevice/list", null, {
|
||||
params: {areaId, name, ...this.page}
|
||||
}).then(res => {
|
||||
if (res?.data) {
|
||||
let data = res.data.records.reverse()
|
||||
if (this.page.current > 1) {
|
||||
this.list = [...this.list, ...data]
|
||||
} else this.list = data
|
||||
this.page.total = res.data.total
|
||||
}
|
||||
})
|
||||
},
|
||||
handleSelectArea({id}) {
|
||||
this.areaId = id
|
||||
this.getUser()
|
||||
},
|
||||
reachBottom() {
|
||||
if (this.page.total > this.list.length) {
|
||||
this.page.current++
|
||||
this.getUser()
|
||||
}
|
||||
},
|
||||
handleShowDetail(user) {
|
||||
uni.navigateTo({url: `./userDetail?id=${user.id}`})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.areaId = JSON.parse(JSON.stringify(this.user.areaId))
|
||||
this.getUser()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.wardList {
|
||||
background: #f5f5f5;
|
||||
padding-bottom: 130px;
|
||||
font-size: 30px;
|
||||
|
||||
::v-deep .userList {
|
||||
margin-top: 16px;
|
||||
background: #fff;
|
||||
|
||||
.row {
|
||||
font-size: 36px;
|
||||
margin-left: 32px;
|
||||
padding-right: 32px;
|
||||
height: 104px;
|
||||
border-bottom: 1px solid #ddd;
|
||||
|
||||
img {
|
||||
width: 72px;
|
||||
height: 72px;
|
||||
margin-right: 38px;
|
||||
}
|
||||
|
||||
.status {
|
||||
margin-right: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
185
src/apps/AppGuardianship/warningDetail.vue
Normal file
185
src/apps/AppGuardianship/warningDetail.vue
Normal file
@@ -0,0 +1,185 @@
|
||||
<template>
|
||||
<section class="warningDetail">
|
||||
<div flex class="header">
|
||||
<b v-text="detail.name"/>
|
||||
<div>
|
||||
{{ $dict.getLabel('intelligentGuardianshipItem2', detail.item) }}
|
||||
{{ detail.itemValue }}
|
||||
</div>
|
||||
</div>
|
||||
<ai-map class="fill" :map.sync="amap" :lib.sync="mapLib"/>
|
||||
<div class="navigation">
|
||||
<div class="content spb" flex>
|
||||
<div flex class="spb wrap">
|
||||
<div class="fill" v-text="detail.gpsDesc"/>
|
||||
<span>最后更新:{{ detail.createTime }}</span>
|
||||
</div>
|
||||
<open-map :data="detail"/>
|
||||
</div>
|
||||
</div>
|
||||
<make-calls :list="phoneList">
|
||||
<div class="bottomBtn">拨打电话</div>
|
||||
</make-calls>
|
||||
<ai-back/>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import OpenMap from "./component/openMap";
|
||||
import MakeCalls from "./component/makeCalls";
|
||||
import AiBack from "../../components/AiBack";
|
||||
import AiMap from "../../components/AiMap";
|
||||
|
||||
export default {
|
||||
name: "warningDetail",
|
||||
components: {AiMap, AiBack, MakeCalls, OpenMap},
|
||||
computed: {
|
||||
phoneList() {
|
||||
let {name: guardianName, phone: guardianPhone} = this.detail
|
||||
return [{guardianName, guardianPhone}, ...this.detail.guardians]
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
detail: {guardians: []},
|
||||
mapLib: null,
|
||||
amap: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getDetail(id) {
|
||||
return this.$http.post("/app/appintelligentguardianshipalarm/queryDetailById", null, {
|
||||
params: {id},
|
||||
withoutToken: true
|
||||
}).then(res => {
|
||||
if (res?.data) {
|
||||
this.detail = res.data
|
||||
}
|
||||
})
|
||||
},
|
||||
initMap() {
|
||||
if (this.mapLib) {
|
||||
let pos = new this.mapLib.LngLat(this.detail.lng, this.detail.lat)
|
||||
this.amap.add(new this.mapLib.Marker({
|
||||
position: pos,
|
||||
anchor: 'bottom-center',
|
||||
content: `<div class="marker">${this.detail.name}</div>`,
|
||||
}))
|
||||
this.amap.setFitView()
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
mapLib(v) {
|
||||
this.detail.id && v && this.initMap()
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$dict.load("intelligentGuardianshipItem", 'intelligentGuardianshipItem2', 'sex')
|
||||
},
|
||||
mounted() {
|
||||
this.getDetail(this.$route.query.id).then(() => this.initMap())
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.warningDetail {
|
||||
padding: 48px 48px 112px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: absolute;
|
||||
|
||||
.header {
|
||||
font-size: 40px;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
justify-content: center;
|
||||
margin-bottom: 48px;
|
||||
|
||||
& > div {
|
||||
color: #EC4461;
|
||||
}
|
||||
}
|
||||
|
||||
.navigation {
|
||||
.content {
|
||||
padding: 10px 0 32px;
|
||||
font-size: 28px;
|
||||
color: #555;
|
||||
|
||||
& > .spb {
|
||||
margin-right: 40px;
|
||||
}
|
||||
|
||||
.fill {
|
||||
min-width: 100%;
|
||||
flex-shrink: 0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
span {
|
||||
margin-left: 0;
|
||||
color: #999;
|
||||
font-size: 22px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bottomBtn {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
background: #1365DD;
|
||||
line-height: 112px;
|
||||
}
|
||||
|
||||
.AiMap {
|
||||
margin-bottom: 72px;
|
||||
}
|
||||
|
||||
|
||||
::v-deep .marker {
|
||||
border-radius: 52px;
|
||||
background: #F46159;
|
||||
color: #fff;
|
||||
font-size: 22px;
|
||||
padding: 4px 16px;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
|
||||
&:before {
|
||||
position: absolute;
|
||||
left: calc(50% - 30px);
|
||||
bottom: -34px;
|
||||
z-index: -1;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 50%;
|
||||
background-color: #F46159;
|
||||
animation: mapWarn 1s ease-out 0s infinite;
|
||||
content: " ";
|
||||
}
|
||||
|
||||
&:after {
|
||||
position: absolute;
|
||||
display: block;
|
||||
content: " ";
|
||||
bottom: -8px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
border: 8px solid transparent;
|
||||
border-bottom: none;
|
||||
border-top-color: #F46159;
|
||||
height: 0;
|
||||
width: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user