目录代码整合
This commit is contained in:
233
packages/device/monitor/components/deviceSlider.vue
Normal file
233
packages/device/monitor/components/deviceSlider.vue
Normal file
@@ -0,0 +1,233 @@
|
||||
<template>
|
||||
<section class="deviceSlider">
|
||||
<div class="mainPane" v-if="show">
|
||||
<div flex overview>
|
||||
<b>监控设备</b>
|
||||
<div>
|
||||
<div>设备总数:{{ overview.total }}</div>
|
||||
<div flex>在线设备:<p v-text="overview.online"/></div>
|
||||
</div>
|
||||
<el-progress type="circle" :width="40" :percentage="overview.percent" color="#19D286" :stroke-width="4"/>
|
||||
</div>
|
||||
<div flex search>
|
||||
<el-select v-model="search.bind" size="mini" placeholder="全部" clearable @change="onChange">
|
||||
<el-option v-for="(op,i) in dict.getDict('deviceStatus')" :key="i" :value="op.dictValue"
|
||||
:label="op.dictName"/>
|
||||
</el-select>
|
||||
<el-input v-model="search.name" size="mini" placeholder="设备名称" prefix-icon="el-icon-search"
|
||||
@change="handleTreeFilter" clearable/>
|
||||
</div>
|
||||
<div title>设备列表</div>
|
||||
<div fill class="deviceList">
|
||||
<el-scrollbar>
|
||||
<el-tree ref="deviceTree" :data="treeData" :props="propsConfig" @node-click="handleNodeClick"
|
||||
:render-content="renderItem" :filter-node-method="handleFilter"/>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rightBtn" :class="{show}" @click="handleShow">
|
||||
<i class="iconfont iconArrow_Right"/>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "deviceSlider",
|
||||
props: {
|
||||
show: Boolean,
|
||||
ins: Function,
|
||||
dict: Object,
|
||||
renderItem: Function
|
||||
},
|
||||
computed: {
|
||||
overview() {
|
||||
let total = this.list?.length || 0,
|
||||
online = this.list?.filter(e => e.deviceStatus == 1)?.length || 0
|
||||
return {
|
||||
total, online,
|
||||
percent: Math.ceil(online / total * 100) || 0
|
||||
}
|
||||
},
|
||||
propsConfig() {
|
||||
return {
|
||||
label: 'name',
|
||||
children: 'children'
|
||||
}
|
||||
},
|
||||
treeData() {
|
||||
let {list, noArea, staData} = this
|
||||
let meta = [staData?.reduce((t, e) => {
|
||||
return t.type <= e.type ? t : e
|
||||
}, {name: '读取中...'})]
|
||||
meta.map(p => this.addChild(p, [...staData, ...list].map(s => ({
|
||||
...s,
|
||||
parentId: s.areaId || s.parent_id
|
||||
}))))
|
||||
return [...meta, {
|
||||
id: 'no_area',
|
||||
name: '未知区划',
|
||||
children: noArea
|
||||
}]
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
list: [],
|
||||
noArea: [],
|
||||
staData: [],
|
||||
name: '',
|
||||
search: {
|
||||
bind: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleShow() {
|
||||
this.$emit('update:show', !this.show)
|
||||
},
|
||||
getDevices() {
|
||||
this.ins.post("/app/appzyvideoequipment/tree", null, {
|
||||
params: {size: 999}
|
||||
}).then(res => {
|
||||
if (res?.data) {
|
||||
this.staData = res.data.count
|
||||
this.list = res.data.list
|
||||
this.noArea = res.data.noArea
|
||||
this.$emit('list', this.list)
|
||||
}
|
||||
})
|
||||
},
|
||||
handleNodeClick(data) {
|
||||
this.$emit('select', data)
|
||||
},
|
||||
handleFilter(v, data) {
|
||||
if (!v) {
|
||||
return !this.search.bind ? true : data.deviceStatus === this.search.bind
|
||||
}
|
||||
|
||||
return data?.name?.indexOf(v) > -1 && (!this.search.bind ? true : data.deviceStatus === this.search.bind)
|
||||
},
|
||||
handleTreeFilter(v) {
|
||||
this.$refs.deviceTree?.filter(v)
|
||||
},
|
||||
|
||||
onChange () {
|
||||
this.$refs.deviceTree?.filter(this.search.name)
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.dict.load("deviceStatus")
|
||||
this.getDevices()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.deviceSlider {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
color: #fff;
|
||||
|
||||
div[flex] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
div[fill] {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.mainPane {
|
||||
width: 280px;
|
||||
height: 100%;
|
||||
background: #333C53;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-top: 16px;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
|
||||
b {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
div[overview], div[search] {
|
||||
box-sizing: border-box;
|
||||
font-size: 12px;
|
||||
justify-content: space-between;
|
||||
padding: 0 16px;
|
||||
gap: 4px;
|
||||
margin-bottom: 16px;
|
||||
|
||||
::v-deep.el-input__inner {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
div[title] {
|
||||
height: 28px;
|
||||
background: #3E4A69;
|
||||
padding: 0 16px;
|
||||
line-height: 28px;
|
||||
}
|
||||
|
||||
::v-deep.deviceList {
|
||||
padding: 0 8px;
|
||||
|
||||
.el-scrollbar {
|
||||
height: 100%;
|
||||
|
||||
.el-scrollbar__wrap {
|
||||
box-sizing: content-box;
|
||||
padding-bottom: 17px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .el-progress__text, p {
|
||||
color: #19D286;
|
||||
}
|
||||
|
||||
::v-deep .el-input__inner {
|
||||
background: #282F45;
|
||||
border: none;
|
||||
}
|
||||
|
||||
::v-deep .el-tree {
|
||||
background: transparent;
|
||||
color: #fff;
|
||||
|
||||
.el-tree-node:focus > .el-tree-node__content, .el-tree-node__content:hover {
|
||||
background: rgba(#fff, .1);
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .el-input__icon {
|
||||
color: #89b;
|
||||
}
|
||||
}
|
||||
|
||||
.rightBtn {
|
||||
width: 16px;
|
||||
height: 80px;
|
||||
background: url("https://cdn.cunwuyun.cn/monitor/drawerBtn.png");
|
||||
color: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.iconfont {
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
|
||||
&.show > .iconfont {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
181
packages/device/monitor/components/locateDialog.vue
Normal file
181
packages/device/monitor/components/locateDialog.vue
Normal file
@@ -0,0 +1,181 @@
|
||||
<template>
|
||||
<section class="locateDialog">
|
||||
<ai-dialog :visible.sync="dialog" title="标绘" @closed="$emit('visible',false),selected={}"
|
||||
@opened="$nextTick(()=>initMap())"
|
||||
@onConfirm="handleConfirm">
|
||||
<div id="amap" v-if="dialog"/>
|
||||
<div class="poi">
|
||||
<el-input ref="poiInput" v-model="search" size="small" clearable @change="handleSearch" placeholder="请输入地点"/>
|
||||
</div>
|
||||
<el-form class="selected" v-if="!!selected.location" id="result" size="mini" label-suffix=":"
|
||||
label-position="left">
|
||||
<div class="header">
|
||||
<i class="iconfont iconLocation"/>
|
||||
<span v-html="[selected.location.lng, selected.location.lat].join(',')"/>
|
||||
</div>
|
||||
<el-form-item label="地点">{{ selected.name || "未知地名" }}</el-form-item>
|
||||
<el-form-item label="类型" v-if="!!selected.type">{{ selected.type }}</el-form-item>
|
||||
<el-form-item label="地址" v-if="!!selected.address">{{ selected.address }}</el-form-item>
|
||||
</el-form>
|
||||
</ai-dialog>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AMapLoader from '@amap/amap-jsapi-loader'
|
||||
|
||||
export default {
|
||||
name: "locateDialog",
|
||||
model: {
|
||||
prop: "visible",
|
||||
event: "visible",
|
||||
},
|
||||
props: ['latlng', 'visible'],
|
||||
data() {
|
||||
return {
|
||||
dialog: false,
|
||||
search: "",
|
||||
poi: null,
|
||||
map: null,
|
||||
AMap: null,
|
||||
selected: {},
|
||||
geo: null
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
visible(v) {
|
||||
this.dialog = v
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
initMap() {
|
||||
AMapLoader.load({
|
||||
key: "b553334ba34f7ac3cd09df9bc8b539dc",
|
||||
version: '2.0',
|
||||
plugins: ['AMap.PlaceSearch', 'AMap.Marker', 'AMap.Geolocation'],
|
||||
}).then(AMap => {
|
||||
this.AMap = AMap
|
||||
this.map = new AMap.Map('amap', {
|
||||
zoom: 14,
|
||||
center: this.latlng ? [this.latlng.lng, this.latlng.lat] : ''
|
||||
}).on('click', res => {
|
||||
this.map.clearMap()
|
||||
this.selected = {location: res.lnglat}
|
||||
this.poi?.searchNearBy('', res.lnglat, 100)
|
||||
});
|
||||
if (this.latlng) {
|
||||
let marker = new AMap.Marker({
|
||||
position: [this.latlng.lng, this.latlng.lat]
|
||||
})
|
||||
this.map.add(marker)
|
||||
}
|
||||
this.poi = new AMap.PlaceSearch().on('complete', ({poiList}) => {
|
||||
this.map.clearMap()
|
||||
if (poiList?.length > 0) {
|
||||
poiList?.pois?.map(e => {
|
||||
let marker = new AMap.Marker({
|
||||
position: e.location,
|
||||
}).on('click', () => this.selected = e)
|
||||
this.map.add(marker)
|
||||
})
|
||||
} else {
|
||||
let marker = new AMap.Marker({
|
||||
position: this.selected.location,
|
||||
})
|
||||
this.map.add(marker)
|
||||
}
|
||||
})
|
||||
this.geo = new AMap.Geolocation({
|
||||
enableHighAccuracy: true,//是否使用高精度定位
|
||||
zoomToAccuracy: true//定位成功后是否自动调整地图视野到定位点
|
||||
})
|
||||
this.map.addControl(this.geo)
|
||||
})
|
||||
},
|
||||
handleSearch(v) {
|
||||
if (v) {
|
||||
this.poi.searchNearBy(v, this.map.getCenter(), 50000)
|
||||
}
|
||||
},
|
||||
handleConfirm() {
|
||||
if (this.selected?.location) {
|
||||
this.$emit('confirm', this.selected)
|
||||
} else {
|
||||
this.$message.error('请先选择坐标位置')
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.dialog = this.visible
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.locateDialog {
|
||||
::v-deep .el-dialog__body {
|
||||
padding: 0;
|
||||
height: 480px;
|
||||
position: relative;
|
||||
|
||||
.ai-dialog__content--wrapper {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
#amap {
|
||||
width: 100%;
|
||||
height: 480px;
|
||||
|
||||
.amap-logo, .amap-copyright {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.amap-marker-label {
|
||||
border-color: transparent;
|
||||
box-shadow: 1px 1px 0 0 rgba(#999, .2);
|
||||
}
|
||||
}
|
||||
|
||||
.poi {
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
top: 10px;
|
||||
display: flex;
|
||||
height: 32px;
|
||||
flex-direction: column;
|
||||
|
||||
div {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.selected {
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
top: 16px;
|
||||
background: #fff;
|
||||
min-width: 200px;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
|
||||
.header {
|
||||
color: #fff;
|
||||
background: #26f;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 32px;
|
||||
font-size: 14px;
|
||||
gap: 4px;
|
||||
padding: 0 8px;
|
||||
}
|
||||
|
||||
.el-form-item {
|
||||
padding: 0 8px;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
91
packages/device/monitor/components/settingDialog.vue
Normal file
91
packages/device/monitor/components/settingDialog.vue
Normal file
@@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<section class="settingDialog">
|
||||
<ai-dialog :visible.sync="dialog" title="基础设置" @close="$emit('visible',false)">
|
||||
<el-form ref="deviceForm" size="small" label-width="140px">
|
||||
<el-form-item label="设备名称" class="full">
|
||||
<el-input v-model="form.name" clearable placeholder="设备名称"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="摄像头状态">
|
||||
<el-radio v-model="form.status" label="开启"/>
|
||||
<el-radio v-model="form.status" label="关闭"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="高清视频">
|
||||
<el-radio v-model="form.status" label="开启"/>
|
||||
<el-radio v-model="form.status" label="关闭"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="摄像头麦克风">
|
||||
<el-radio v-model="form.status" label="开启"/>
|
||||
<el-radio v-model="form.status" label="关闭"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态指示灯">
|
||||
<el-radio v-model="form.status" label="开启"/>
|
||||
<el-radio v-model="form.status" label="关闭"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="夜视">
|
||||
<el-radio v-model="form.status" label="自动"/>
|
||||
<el-radio v-model="form.status" label="开启"/>
|
||||
<el-radio v-model="form.status" label="关闭"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="旋转180°">
|
||||
<el-radio v-model="form.status" label="开启"/>
|
||||
<el-radio v-model="form.status" label="关闭"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="WIFI网络" class="full">-</el-form-item>
|
||||
<el-form-item label="MAC地址">(34:75:6b:c9:10)</el-form-item>
|
||||
<el-form-item label="摄像头型号">C71</el-form-item>
|
||||
<el-form-item label="固件">20.0326.251.2486</el-form-item>
|
||||
<el-form-item label="嵌入式应用">2.3.37.8954</el-form-item>
|
||||
<el-form-item label="IMEI">110003953100302</el-form-item>
|
||||
</el-form>
|
||||
</ai-dialog>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "settingDialog",
|
||||
model: {
|
||||
prop: "visible",
|
||||
event: "visible",
|
||||
},
|
||||
props: {
|
||||
visible: Boolean,
|
||||
detail: {default: () => ({})}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dialog: false,
|
||||
form: {}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
visible(v) {
|
||||
this.dialog = v
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.form = JSON.parse(JSON.stringify(this.form))
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.settingDialog {
|
||||
.el-form {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
::v-deep .el-form-item {
|
||||
width: 50%;
|
||||
|
||||
.el-form-item__label {
|
||||
padding-right: 40px;
|
||||
}
|
||||
|
||||
&.full {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user