14 changed files with 9767 additions and 0 deletions
-
2src/assets/fengmap/fengmap.analyser.min.js
-
2src/assets/fengmap/fengmap.effect.min.js
-
8649src/assets/fengmap/fengmap.map.min.js
-
35src/assets/fengmap/fengmap.plugin.ui.min.js
-
1src/assets/fengmap/toolBarStyle.css
-
46src/views/deviceManage/accessControlLog/index.vue
-
152src/views/deviceManage/accessControlLog/logList.vue
-
136src/views/deviceManage/map/index copy.vue
-
428src/views/deviceManage/map/index.vue
-
82src/views/deviceManage/safetyDoorLog/alarmLog/index.vue
-
52src/views/deviceManage/safetyDoorLog/index.vue
-
116src/views/deviceManage/safetyDoorLog/search.vue
-
61src/views/deviceManage/safetyDoorLog/visitorLog/index.vue
-
5vue.config.js
2
src/assets/fengmap/fengmap.analyser.min.js
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
2
src/assets/fengmap/fengmap.effect.min.js
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
8649
src/assets/fengmap/fengmap.map.min.js
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
35
src/assets/fengmap/fengmap.plugin.ui.min.js
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
1
src/assets/fengmap/toolBarStyle.css
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,46 @@ |
|||||
|
<template> |
||||
|
<div class="app-container tab-container"> |
||||
|
<div class="tab-content"> |
||||
|
<span class="right-top-line" /> |
||||
|
<span class="left-bottom-line" /> |
||||
|
<span class="right-bottom-line" /> |
||||
|
<ul class="tab-nav"> |
||||
|
<li :class="{ 'active-tab-nav': activeIndex == 0 }" @click="changeActiveTab(0)">人流日志<i /></li> |
||||
|
<span class="tab-right-img" /> |
||||
|
</ul> |
||||
|
<component :is="comName" /> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import logList from './logList' |
||||
|
|
||||
|
export default { |
||||
|
name: 'LogManage', |
||||
|
components: { |
||||
|
logList |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
activeIndex: 0 |
||||
|
} |
||||
|
}, |
||||
|
computed: { |
||||
|
comName: function() { |
||||
|
if (this.activeIndex === 0) { |
||||
|
return 'logList' |
||||
|
} |
||||
|
return 'logList' |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
changeActiveTab(data) { |
||||
|
this.activeIndex = data |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
</style> |
||||
@ -0,0 +1,152 @@ |
|||||
|
<template> |
||||
|
<div style="height: calc(100vh - 232px);"> |
||||
|
<div class="head-container"> |
||||
|
<div class="head-search"> |
||||
|
<el-select v-model="query.personType" clearable size="small" placeholder="门禁列表选择" class="filter-item" style="width: 140px;" @change="crud.toQuery"> |
||||
|
<i slot="prefix" class="iconfont icon-zhuangtai" /> |
||||
|
<el-option v-for="item in userTypeOptions" :key="item.key" :label="item.display_name" :value="item.key" /> |
||||
|
</el-select> |
||||
|
<date-range-picker v-model="blurryTime" class="date-item" /> |
||||
|
<rrOperation> |
||||
|
<template v-slot:right> |
||||
|
<el-button class="filter-item filter-refresh" size="mini" type="warning" icon="el-icon-refresh-left" @click="resetQuery()">重置</el-button> |
||||
|
</template> |
||||
|
</rrOperation> |
||||
|
</div> |
||||
|
<crudOperation :permission="permission"> |
||||
|
<template v-slot:right> |
||||
|
<el-button :loading="crud.downloadLoading" size="mini" @click="doExport"> |
||||
|
<i class="iconfont icon-daochu" /> |
||||
|
导出 |
||||
|
</el-button> |
||||
|
</template> |
||||
|
</crudOperation> |
||||
|
</div> |
||||
|
<div class="container-wrap"> |
||||
|
<span class="right-top-line" /> |
||||
|
<span class="left-bottom-line" /> |
||||
|
<el-table ref="table" v-loading="crud.loading" highlight-current-row style="width: 100%;" height="calc(100vh - 330px)" :data="crud.data" @selection-change="crud.selectionChangeHandler"> |
||||
|
<el-table-column type="index" label="序号" width="55" align="center" /> |
||||
|
<el-table-column prop="personName" label="门禁设备" /> |
||||
|
<el-table-column prop="personType" label="进馆人次" /> |
||||
|
<el-table-column prop="deviceName" label="出馆人次" /> |
||||
|
<el-table-column :show-overflow-tooltip="true" prop="snapshotTime" label="操作时间"> |
||||
|
<template slot-scope="scope"> |
||||
|
<div>{{ scope.row.snapshotTime | parseTime }}</div> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
</el-table> |
||||
|
<!--分页组件--> |
||||
|
<pagination v-if="crud.data.length !== 0" /> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import crudFace from '@/api/faceRecognition/index' |
||||
|
import CRUD, { presenter, header, crud } from '@crud/crud' |
||||
|
import rrOperation from '@crud/RR.operation' |
||||
|
import crudOperation from '@crud/CRUD.operation' |
||||
|
import pagination from '@crud/Pagination' |
||||
|
import DateRangePicker from '@/components/DateRangePicker' |
||||
|
import { exportFile } from '@/utils/index' |
||||
|
import qs from 'qs' |
||||
|
import { mapGetters } from 'vuex' |
||||
|
|
||||
|
export default { |
||||
|
name: 'PersonInfoManage', |
||||
|
components: { pagination, crudOperation, rrOperation, DateRangePicker }, |
||||
|
cruds() { |
||||
|
return CRUD({ title: '门禁日志', url: 'api/person/initFaceRecognizeLog', crudMethod: { ...crudFace }, optShow: { |
||||
|
add: false, |
||||
|
edit: false, |
||||
|
del: false, |
||||
|
reset: false, |
||||
|
download: false, |
||||
|
group: false |
||||
|
}}) |
||||
|
}, |
||||
|
mixins: [presenter(), header(), crud()], |
||||
|
data() { |
||||
|
return { |
||||
|
permission: { |
||||
|
add: ['admin', 'column:add'], |
||||
|
edit: ['admin', 'column:edit'], |
||||
|
del: ['admin', 'column:del'] |
||||
|
}, |
||||
|
userTypeOptions: [ |
||||
|
{ key: 0, display_name: '普通用户' }, |
||||
|
{ key: 1, display_name: 'VIP用户' }, |
||||
|
{ key: 2, display_name: '黑名单用户' } |
||||
|
], |
||||
|
blurryTime: [] |
||||
|
} |
||||
|
}, |
||||
|
computed: { |
||||
|
...mapGetters([ |
||||
|
'baseApi', |
||||
|
'user' |
||||
|
]) |
||||
|
}, |
||||
|
created() { |
||||
|
}, |
||||
|
mounted() { |
||||
|
}, |
||||
|
methods: { |
||||
|
[CRUD.HOOK.beforeRefresh]() { |
||||
|
this.crud.query.libcode = this.user.fonds.fondsNo |
||||
|
if (this.blurryTime) { |
||||
|
this.crud.query.startTime = this.blurryTime[0] |
||||
|
this.crud.query.endTime = this.blurryTime[1] |
||||
|
} else { |
||||
|
this.crud.query.startTime = null |
||||
|
this.crud.query.endTime = null |
||||
|
} |
||||
|
}, |
||||
|
[CRUD.HOOK.afterRefresh]() { |
||||
|
}, |
||||
|
resetQuery() { |
||||
|
this.blurryTime = null |
||||
|
this.crud.query.startTime = null |
||||
|
this.crud.query.endTime = null |
||||
|
this.crud.query.personType = null |
||||
|
this.crud.toQuery() |
||||
|
}, |
||||
|
doExport() { |
||||
|
console.log('doExport', this.crud.page.total) |
||||
|
if (this.crud.page.total > 10000) { |
||||
|
this.handleExport('导出数据大于10000条,时间可能较长') |
||||
|
} else { |
||||
|
this.handleExport('此操作将导出所有数据') |
||||
|
} |
||||
|
}, |
||||
|
handleExport(message) { |
||||
|
this.crud.downloadLoading = true |
||||
|
this.$confirm(message + '<span>你是否还要继续?</span>', '提示', { |
||||
|
confirmButtonText: '继续', |
||||
|
cancelButtonText: '取消', |
||||
|
type: 'warning', |
||||
|
dangerouslyUseHTMLString: true |
||||
|
}).then(() => { |
||||
|
const params = { |
||||
|
'libcode': this.user.fonds.fondsNo, |
||||
|
'personType': this.crud.query.personType, |
||||
|
'startTime': this.blurryTime.length !== 0 ? this.blurryTime[0].split(' ')[0] : null, |
||||
|
'endTime': this.blurryTime.length !== 0 ? this.blurryTime[1].split(' ')[0] : null |
||||
|
} |
||||
|
console.log('exportFile', params) |
||||
|
exportFile(this.baseApi + '/api/person/downloadFaceRecognizeLog?' + qs.stringify(params, { indices: false, allowDots: true, skipNulls: false })) |
||||
|
this.crud.downloadLoading = false |
||||
|
}).catch(() => { |
||||
|
console.log('取消') |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
::v-deep .el-pagination{ |
||||
|
margin: 24px 0 10px 0 !important |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,136 @@ |
|||||
|
<template> |
||||
|
<div class="map-container"> |
||||
|
<div class="toolBarDiv" /> |
||||
|
<div id="fengmap" /> |
||||
|
|
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
// import fengmap from '@/assets/fengmapSDK/fengmap.map.min.js' |
||||
|
|
||||
|
import { FMMap } from '@/assets/fengmap/fengmap.map.min.js' |
||||
|
import { |
||||
|
FMControlPosition, |
||||
|
FMCompass, |
||||
|
FMZoomBar, |
||||
|
FMToolbar, |
||||
|
FMScaleBar |
||||
|
} from '@/assets/fengmap/fengmap.plugin.ui.min.js' |
||||
|
|
||||
|
// import '@/assets/fengmap/fengmap.plugin.ui.min' // UI控件包 |
||||
|
// import '@/assets/fengmap/fengmap.analyser.min' // 分析器包 |
||||
|
// import '@/assets/fengmapSDK/fengmap.plugin.navi.min' // 导航包 |
||||
|
// import '@/assets/fengmapSDK/fengmap.effect.min' // 特效包 |
||||
|
// import '@/assets/fengmapSDK/fengmap.plugin.markers.min' // 特殊标注包 |
||||
|
// import '@/assets/fengmapSDK/fengmap.plugin.draw.min' // 绘图包 |
||||
|
// import '@/assets/fengmapSDK/fengmap.plugin.location.min' // 位置服务相关功能包 |
||||
|
// import '@/assets/fengmapSDK/fengmap.plugin.export.min' // 打印/出图包 |
||||
|
// import '@/assets/fengmapSDK/fengmap.plugin.layers.min' // 附加图层包 |
||||
|
// import '@/assets/fengmapSDK/fengmap.plugin.debug.min' // 性能监控包 |
||||
|
// import '@/assets/fengmapSDK/fengmap.plugin.fusion.min' // 数据融合信息工具包 |
||||
|
// import '@/assets/fengmapSDK/fengmap.plugin.loader.min' // JS外部模型/FBX动态模型加载器插件包 |
||||
|
export default { |
||||
|
name: 'FengmapComponent', |
||||
|
data() { |
||||
|
return { |
||||
|
map: null, // 地图实例 |
||||
|
marker: null // 标记点实例 |
||||
|
} |
||||
|
}, |
||||
|
mounted() { |
||||
|
this.initMap() |
||||
|
}, |
||||
|
beforeDestroy() { |
||||
|
if (this.map) { |
||||
|
this.map.destroy() |
||||
|
this.map = null |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
// 初始化地图 |
||||
|
initMap() { |
||||
|
const mapOptions = { |
||||
|
container: document.getElementById('fengmap'), |
||||
|
appName: '武汉东西湖图书馆', |
||||
|
key: 'd0ebc0507c72e6fb8598a3e58517f9d3', |
||||
|
mapID: '1572162706543931393', |
||||
|
level: 3, |
||||
|
mapZoom: 19.5 |
||||
|
|
||||
|
} |
||||
|
|
||||
|
// 创建地图实例 |
||||
|
this.map = new FMMap(mapOptions) |
||||
|
|
||||
|
this.map.on('loaded', function() { |
||||
|
// 指北针控件 |
||||
|
const scrollCompassCtlOpt = { |
||||
|
position: FMControlPosition.RIGHT_TOP, |
||||
|
offset: { |
||||
|
x: -20, |
||||
|
y: 80 |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
const compass = new FMCompass(scrollCompassCtlOpt) |
||||
|
compass.addTo(this.map) |
||||
|
|
||||
|
compass.on('click', function() { |
||||
|
this.map.setRotation({ |
||||
|
rotation: 0, |
||||
|
animate: true, |
||||
|
duration: 0.3, |
||||
|
finish: function() { console.log('setRotation') } |
||||
|
}) |
||||
|
}) |
||||
|
|
||||
|
// 缩放控件 |
||||
|
const scrollZoomCtlOpt = { |
||||
|
position: FMControlPosition.RIGHT_TOP, |
||||
|
offset: { |
||||
|
x: -20, |
||||
|
y: 600 |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
const toolbar = new FMZoomBar(scrollZoomCtlOpt) |
||||
|
toolbar.addTo(this.map) |
||||
|
|
||||
|
// 楼层控件 |
||||
|
const scrollFloorCtlOpt = { |
||||
|
position: FMControlPosition.RIGHT_TOP, |
||||
|
floorButtonCount: 5, |
||||
|
offset: { |
||||
|
x: -20, |
||||
|
y: 150 |
||||
|
}, |
||||
|
viewModeControl: true, |
||||
|
floorModeControl: true, |
||||
|
needAllLayerBtn: true |
||||
|
|
||||
|
} |
||||
|
const scrollFloorControl = new FMToolbar(scrollFloorCtlOpt) |
||||
|
scrollFloorControl.addTo(this.map) |
||||
|
|
||||
|
// 比例尺控件 |
||||
|
const scrollScaleBarCtlOpt = { |
||||
|
fontSize: 18, |
||||
|
height: 30, |
||||
|
position: FMControlPosition.LEFT_BOTTOM, |
||||
|
offset: { |
||||
|
x: 20, |
||||
|
y: -20 |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
const scaleBar = new FMScaleBar(scrollScaleBarCtlOpt) |
||||
|
scaleBar.addTo(this.map) |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
</style> |
||||
@ -0,0 +1,428 @@ |
|||||
|
<template> |
||||
|
<div class="app-container"> |
||||
|
<div id="fengmap" /> |
||||
|
<div class="toolBarDiv" /> |
||||
|
<div class="rightMask" /> |
||||
|
<!-- <div v-if="debugInfo" class="debug-info"> |
||||
|
<pre>{{ debugInfo }}</pre> |
||||
|
</div> --> |
||||
|
<!-- <div class="control-panel"> |
||||
|
<div class="button-group"> |
||||
|
<el-button type="primary" @click="startTracking(0)">定位点1</el-button> |
||||
|
<el-button type="success" @click="startTracking(1)">定位点2</el-button> |
||||
|
<el-button type="warning" @click="startTracking(2)">定位点3</el-button> |
||||
|
<el-button type="danger" @click="stopAllTracking">停止所有</el-button> |
||||
|
</div> |
||||
|
<div class="tracking-info"> |
||||
|
<div v-for="(marker, index) in markers" :key="index" class="marker-status"> |
||||
|
<span :style="{ color: getMarkerColor(index) }"> |
||||
|
定位点{{ index + 1 }}: {{ marker.active ? '追踪中' : '已停止' }} |
||||
|
<template v-if="marker.remainTime > 0">({{ marker.remainTime }}s)</template> |
||||
|
</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> --> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import fengmap from '@/assets/fengmap/fengmap.map.min' |
||||
|
import { |
||||
|
FMControlPosition, |
||||
|
FMCompass, |
||||
|
FMZoomBar, |
||||
|
FMToolbar, |
||||
|
FMScaleBar |
||||
|
} from '@/assets/fengmap/fengmap.plugin.ui.min.js' |
||||
|
import '@/assets/fengmap/fengmap.analyser.min' |
||||
|
import '@/assets/fengmap/fengmap.effect.min' |
||||
|
|
||||
|
export default { |
||||
|
name: 'FengNiaoYun', |
||||
|
data() { |
||||
|
return { |
||||
|
map: null, |
||||
|
markers: [ |
||||
|
{ marker: null, timer: null, remainTimer: null, remainTime: 0, active: false, positions: [] }, |
||||
|
{ marker: null, timer: null, remainTimer: null, remainTime: 0, active: false, positions: [] }, |
||||
|
{ marker: null, timer: null, remainTimer: null, remainTime: 0, active: false, positions: [] } |
||||
|
], |
||||
|
level: null, |
||||
|
levels: null, |
||||
|
floorObj: {}, |
||||
|
activeFloor: null, |
||||
|
debugInfo: null, |
||||
|
markerColors: ['#409EFF', '#67C23A', '#E6A23C'] // 三个点的颜色 |
||||
|
} |
||||
|
}, |
||||
|
mounted() { |
||||
|
console.log('组件已挂载') |
||||
|
const container = document.getElementById('fengmap') |
||||
|
console.log('容器尺寸:', container.offsetWidth, container.offsetHeight) |
||||
|
this.$nextTick(() => { |
||||
|
this.initMap() |
||||
|
}) |
||||
|
}, |
||||
|
methods: { |
||||
|
initMap() { |
||||
|
try { |
||||
|
console.log('开始初始化地图') |
||||
|
const options = { |
||||
|
container: document.getElementById('fengmap'), |
||||
|
appName: '武汉东西湖图书馆', |
||||
|
key: 'd0ebc0507c72e6fb8598a3e58517f9d3', |
||||
|
mapID: '1572162706543931393', |
||||
|
// themeID: '1572162706543931393', |
||||
|
// level: 3, |
||||
|
mapZoom: 19 |
||||
|
// defaultViewMode: fengmap.FMViewMode.MODE_3D, |
||||
|
// defaultControlsPose: 0, |
||||
|
// defaultTiltAngle: 60, |
||||
|
// defaultMapScaleLevel: 18, |
||||
|
// defaultFocusGroup: 1, |
||||
|
// forceWebGL: true |
||||
|
} |
||||
|
console.log('地图配置:', options) |
||||
|
|
||||
|
this.map = new fengmap.FMMap(options) |
||||
|
console.log('地图实例已创建') |
||||
|
|
||||
|
this.map.on('loadingProcess', (event) => { |
||||
|
console.log('加载进度:', event.progress) |
||||
|
this.debugInfo = `加载进度: ${event.progress}%` |
||||
|
}) |
||||
|
|
||||
|
this.map.on('loaded', () => { |
||||
|
console.log('地图加载完成') |
||||
|
this.debugInfo = '地图加载完成' |
||||
|
this.map.setViewMode(fengmap.FMViewMode.MODE_3D) |
||||
|
|
||||
|
// 指北针控件 |
||||
|
const scrollCompassCtlOpt = { |
||||
|
position: FMControlPosition.RIGHT_TOP, |
||||
|
offset: { |
||||
|
x: -20, |
||||
|
y: 80 |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
const compass = new FMCompass(scrollCompassCtlOpt) |
||||
|
compass.addTo(this.map) |
||||
|
|
||||
|
compass.on('click', function() { |
||||
|
this.map.setRotation({ |
||||
|
rotation: 0, |
||||
|
animate: true, |
||||
|
duration: 0.3, |
||||
|
finish: function() { console.log('setRotation') } |
||||
|
}) |
||||
|
}) |
||||
|
|
||||
|
// 缩放控件 |
||||
|
const scrollZoomCtlOpt = { |
||||
|
position: FMControlPosition.RIGHT_TOP, |
||||
|
offset: { |
||||
|
x: -20, |
||||
|
y: 600 |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
const toolbar = new FMZoomBar(scrollZoomCtlOpt) |
||||
|
toolbar.addTo(this.map) |
||||
|
|
||||
|
// 楼层控件 |
||||
|
const scrollFloorCtlOpt = { |
||||
|
position: FMControlPosition.RIGHT_TOP, |
||||
|
floorButtonCount: 5, |
||||
|
offset: { |
||||
|
x: -20, |
||||
|
y: 150 |
||||
|
}, |
||||
|
viewModeControl: true, |
||||
|
floorModeControl: true, |
||||
|
needAllLayerBtn: true |
||||
|
} |
||||
|
const scrollFloorControl = new FMToolbar(scrollFloorCtlOpt) |
||||
|
console.log('scrollFloorControl', scrollFloorControl.p) |
||||
|
scrollFloorControl.addTo(this.map) |
||||
|
|
||||
|
// 比例尺控件 |
||||
|
const scrollScaleBarCtlOpt = { |
||||
|
fontSize: 18, |
||||
|
height: 30, |
||||
|
position: FMControlPosition.LEFT_BOTTOM, |
||||
|
offset: { |
||||
|
x: 20, |
||||
|
y: -20 |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
const scaleBar = new FMScaleBar(scrollScaleBarCtlOpt) |
||||
|
scaleBar.addTo(this.map) |
||||
|
console.log('当前楼层', this.map.getLevel()) |
||||
|
console.log('所有楼层', this.map.getLevels()) |
||||
|
|
||||
|
// 获取楼层信息 |
||||
|
this.level = this.map.getLevel() |
||||
|
this.levels = this.map.getLevels() |
||||
|
|
||||
|
// console.log('所有楼层', this.map.getVisibleLevels()) |
||||
|
const floorInfos = this.map.getFloorInfos().reverse() |
||||
|
console.log(floorInfos) |
||||
|
|
||||
|
// 设置聚焦楼层 |
||||
|
// this.map.setLevel({ |
||||
|
// level: item |
||||
|
// }) |
||||
|
// this.addFloorsDom() |
||||
|
}, { passive: true }) |
||||
|
|
||||
|
this.map.on('click', (e) => { |
||||
|
console.log('地图点击事件:', e) |
||||
|
this.debugInfo = `点击坐标: x=${e.x}, y=${e.y}` |
||||
|
}, { passive: true }) |
||||
|
|
||||
|
this.map.on('mapInitError', (err) => { |
||||
|
console.error('地图初始化错误:', err) |
||||
|
this.debugInfo = `地图初始化错误: ${err.message}` |
||||
|
}) |
||||
|
} catch (error) { |
||||
|
console.error('初始化地图失败:', error) |
||||
|
this.debugInfo = `初始化失败: ${error.message}` |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
addFloorsDom() { |
||||
|
// 获取所有楼层信息 |
||||
|
const floorInfos = this.map.getFloorInfos().reverse() |
||||
|
console.log(floorInfos) |
||||
|
|
||||
|
floorInfos.forEach(item => { |
||||
|
this.floorObj[item.level] = item.name |
||||
|
}) |
||||
|
const dom = document.getElementsByClassName('fm-floor-list')[0] |
||||
|
|
||||
|
this.levels.reverse().forEach((item) => { |
||||
|
const labelEle = document.createElement('label') |
||||
|
const textEle = document.createTextNode(this.floorObj[item]) |
||||
|
|
||||
|
// 增加楼层Dom点击事件 |
||||
|
labelEle.onclick = function setLevelClick() { |
||||
|
console.log(item, this.floorObj) |
||||
|
this.level = item |
||||
|
// 设置聚焦楼层 |
||||
|
this.map.setLevel({ |
||||
|
level: item |
||||
|
}) |
||||
|
// 更改样式 |
||||
|
if (this.activeFloor) { |
||||
|
const activeDom = document.getElementById(this.activeFloor) |
||||
|
activeDom.classList.remove('fm-floor-name-active') |
||||
|
activeDom.classList.add('fm-floor-name-normal') |
||||
|
} |
||||
|
const dom = document.getElementById(this.floorObj[item]) |
||||
|
dom.classList.remove('fm-floor-name-normal') |
||||
|
dom.classList.add('fm-floor-name-active') |
||||
|
this.activeFloor = this.floorObj[item] |
||||
|
} |
||||
|
|
||||
|
labelEle.id = this.floorObj[item] |
||||
|
labelEle.classList.add('fm-floor-name') |
||||
|
labelEle.classList.add('fm-floor-name-normal') |
||||
|
labelEle.appendChild(textEle) |
||||
|
dom.appendChild(labelEle) |
||||
|
|
||||
|
const hrEle = document.createElement('hr') |
||||
|
hrEle.classList.add('fm-floor-line') |
||||
|
dom.appendChild(hrEle) |
||||
|
}) |
||||
|
dom.removeChild(dom.childNodes[dom.childNodes.length - 1]) |
||||
|
}, |
||||
|
|
||||
|
getMarkerColor(index) { |
||||
|
return this.markerColors[index] |
||||
|
}, |
||||
|
async addLocationMarker(index, ops = {}) { |
||||
|
try { |
||||
|
if (this.markers[index].marker) { |
||||
|
this.markers[index].marker.remove() |
||||
|
this.markers[index].marker = null |
||||
|
} |
||||
|
|
||||
|
const markerConfig = { |
||||
|
height: 0.2, |
||||
|
size: 23, |
||||
|
level: 5, |
||||
|
url: 'https://developer.fengmap.com/fmAPI/images/location.png', |
||||
|
x: 11791504, |
||||
|
y: 3418833.5, |
||||
|
color: this.markerColors[index], // 设置不同颜色 |
||||
|
...ops |
||||
|
} |
||||
|
|
||||
|
this.markers[index].marker = new fengmap.FMLocationMarker(markerConfig) |
||||
|
this.markers[index].marker.addTo(this.map) |
||||
|
} catch (error) { |
||||
|
console.error(`添加定位标记${index + 1}失败:`, error) |
||||
|
this.debugInfo = `添加标记${index + 1}失败: ${error.message}` |
||||
|
} |
||||
|
}, |
||||
|
async getRealtimePosition(index) { |
||||
|
// 模拟不同定位点的位置 |
||||
|
const baseX = 11791504 + (index * 50) // 不同定位点基础位置间隔50 |
||||
|
const baseY = 3418833.5 + (index * 50) |
||||
|
return { |
||||
|
x: baseX + Math.random() * 20 - 10, |
||||
|
y: baseY + Math.random() * 20 - 10, |
||||
|
level: 5 |
||||
|
} |
||||
|
}, |
||||
|
async updateMarkerPosition(index) { |
||||
|
try { |
||||
|
const position = await this.getRealtimePosition(index) |
||||
|
if (this.markers[index].marker) { |
||||
|
this.markers[index].marker.moveTo({ |
||||
|
x: position.x, |
||||
|
y: position.y, |
||||
|
level: position.level, |
||||
|
animate: true, |
||||
|
duration: 0.5 |
||||
|
}) |
||||
|
} else { |
||||
|
this.addLocationMarker(index, position) |
||||
|
} |
||||
|
|
||||
|
// 记录位置信息 |
||||
|
this.markers[index].positions.push({ |
||||
|
x: position.x, |
||||
|
y: position.y, |
||||
|
timestamp: new Date().toLocaleString() |
||||
|
}) |
||||
|
|
||||
|
this.debugInfo = `定位点${index + 1}更新: x=${position.x.toFixed(2)}, y=${position.y.toFixed(2)}` |
||||
|
} catch (error) { |
||||
|
console.error(`更新位置${index + 1}失败:`, error) |
||||
|
this.debugInfo = `更新位置${index + 1}失败: ${error.message}` |
||||
|
} |
||||
|
}, |
||||
|
startTracking(index) { |
||||
|
if (this.markers[index].timer) { |
||||
|
this.$message.warning(`定位点${index + 1}已在追踪中`) |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
this.markers[index].active = true |
||||
|
this.markers[index].remainTime = 60 |
||||
|
this.markers[index].positions = [] // 清空历史位置记录 |
||||
|
|
||||
|
// 倒计时定时器 |
||||
|
this.markers[index].remainTimer = setInterval(() => { |
||||
|
this.markers[index].remainTime-- |
||||
|
if (this.markers[index].remainTime <= 0) { |
||||
|
this.stopTracking(index) |
||||
|
} |
||||
|
}, 1000) |
||||
|
|
||||
|
// 位置更新定时器 |
||||
|
this.updateMarkerPosition(index) |
||||
|
this.markers[index].timer = setInterval(() => { |
||||
|
this.updateMarkerPosition(index) |
||||
|
}, 3000) |
||||
|
|
||||
|
this.$message.success(`定位点${index + 1}开始追踪`) |
||||
|
}, |
||||
|
stopTracking(index) { |
||||
|
if (this.markers[index].timer) { |
||||
|
clearInterval(this.markers[index].timer) |
||||
|
clearInterval(this.markers[index].remainTimer) |
||||
|
this.markers[index].timer = null |
||||
|
this.markers[index].remainTimer = null |
||||
|
this.markers[index].remainTime = 0 |
||||
|
this.markers[index].active = false |
||||
|
|
||||
|
// 输出位置记录 |
||||
|
console.log(`定位点${index + 1}位置记录:`, this.markers[index].positions) |
||||
|
this.$message.info(`定位点${index + 1}追踪已停止`) |
||||
|
} |
||||
|
}, |
||||
|
stopAllTracking() { |
||||
|
this.markers.forEach((_, index) => { |
||||
|
this.stopTracking(index) |
||||
|
}) |
||||
|
this.$message.info('所有定位点追踪已停止') |
||||
|
}, |
||||
|
beforeDestroy() { |
||||
|
this.stopAllTracking() |
||||
|
this.markers.forEach(markerInfo => { |
||||
|
if (markerInfo.marker) { |
||||
|
markerInfo.marker.remove() |
||||
|
} |
||||
|
}) |
||||
|
if (this.map) { |
||||
|
this.map.dispose() |
||||
|
this.map = null |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped lang="scss"> |
||||
|
@import "~@/assets/fengmap/toolBarStyle.css"; |
||||
|
#fengmap { |
||||
|
position: absolute; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
background: #f5f5f5; |
||||
|
} |
||||
|
.rightMask{ |
||||
|
width: 162px; |
||||
|
height: 42px; |
||||
|
position: absolute; |
||||
|
bottom: 0; |
||||
|
right: 0; |
||||
|
background: #fff; |
||||
|
} |
||||
|
.debug-info { |
||||
|
position: absolute; |
||||
|
top: 10px; |
||||
|
left: 10px; |
||||
|
background: rgba(0, 0, 0, 0.7); |
||||
|
color: white; |
||||
|
padding: 10px; |
||||
|
border-radius: 4px; |
||||
|
font-size: 12px; |
||||
|
max-width: 80%; |
||||
|
z-index: 99999999; |
||||
|
} |
||||
|
|
||||
|
/*.control-panel { |
||||
|
position: fixed; |
||||
|
top: 20px; |
||||
|
right: 20px; |
||||
|
background: rgba(255, 255, 255, 0.9); |
||||
|
padding: 15px; |
||||
|
border-radius: 4px; |
||||
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); |
||||
|
z-index: 1000; |
||||
|
} |
||||
|
|
||||
|
.button-group { |
||||
|
display: flex; |
||||
|
gap: 10px; |
||||
|
margin-bottom: 10px; |
||||
|
} |
||||
|
|
||||
|
.tracking-info { |
||||
|
margin-top: 10px; |
||||
|
padding-top: 10px; |
||||
|
border-top: 1px solid #eee; |
||||
|
} |
||||
|
|
||||
|
.marker-status { |
||||
|
margin: 5px 0; |
||||
|
font-size: 14px; |
||||
|
} */ |
||||
|
</style> |
||||
@ -0,0 +1,82 @@ |
|||||
|
<template> |
||||
|
<div style="height: calc(100vh - 236px);"> |
||||
|
<Search :is-log-type="isLogType" /> |
||||
|
<el-table |
||||
|
ref="table" |
||||
|
:data="crud.data" |
||||
|
style="width: 100%;" |
||||
|
height="calc(100vh - 330px)" |
||||
|
> |
||||
|
<el-table-column type="index" label="序号" align="center" width="55" /> |
||||
|
<el-table-column prop="username" label="书名" /> |
||||
|
<el-table-column prop="nickName" label="ISBN" /> |
||||
|
<el-table-column prop="fondsName" label="索书号" /> |
||||
|
<el-table-column prop="deptsName" label="UID" /> |
||||
|
<el-table-column prop="requestIp" label="安全门设备" /> |
||||
|
<el-table-column prop="address" label="报警类型" /> |
||||
|
<el-table-column prop="createTime" label="时间"> |
||||
|
<template slot-scope="scope"> |
||||
|
<div>{{ scope.row.createTime | parseTime }}</div> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
</el-table> |
||||
|
<pagination v-if="crud.data.length !== 0" /> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import Search from '../search.vue' |
||||
|
import CRUD, { presenter, crud } from '@crud/crud' |
||||
|
import { mapGetters } from 'vuex' |
||||
|
import pagination from '@crud/Pagination' |
||||
|
|
||||
|
export default { |
||||
|
name: 'OperateLog', |
||||
|
components: { pagination, Search }, |
||||
|
mixins: [presenter(), crud()], |
||||
|
cruds() { |
||||
|
return CRUD({ |
||||
|
url: 'api/log/initLog', |
||||
|
title: '操作日志', |
||||
|
optShow: { |
||||
|
add: false, |
||||
|
edit: false, |
||||
|
del: false, |
||||
|
download: false, |
||||
|
reset: false, |
||||
|
group: false |
||||
|
} |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
props: { |
||||
|
isCenter: { |
||||
|
type: Boolean, |
||||
|
default: false |
||||
|
} |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
isLogType: 'alarm', |
||||
|
selections: [] |
||||
|
} |
||||
|
}, |
||||
|
computed: { |
||||
|
...mapGetters([ |
||||
|
'user' |
||||
|
]) |
||||
|
}, |
||||
|
mounted() { |
||||
|
}, |
||||
|
methods: { |
||||
|
[CRUD.HOOK.beforeRefresh]() { |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
::v-deep .el-pagination{ |
||||
|
margin: 24px 0 10px 0 !important |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,52 @@ |
|||||
|
<template> |
||||
|
<div class="app-container tab-container"> |
||||
|
<div class="tab-content"> |
||||
|
<span class="right-top-line" /> |
||||
|
<span class="left-bottom-line" /> |
||||
|
<span class="right-bottom-line" /> |
||||
|
<ul class="tab-nav"> |
||||
|
<li :class="{ 'active-tab-nav': activeIndex == 0 }" @click="changeActiveTab(0)">人流日志<i /></li> |
||||
|
<li :class="{ 'active-tab-nav': activeIndex == 1 }" @click="changeActiveTab(1)">报警日志<i /></li> |
||||
|
<!-- 最右侧装饰img --> |
||||
|
<span class="tab-right-img" /> |
||||
|
</ul> |
||||
|
<component :is="comName" /> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import visitorLog from './visitorLog/index' |
||||
|
import alarmLog from './alarmLog/index' |
||||
|
|
||||
|
export default { |
||||
|
name: 'LogManage', |
||||
|
components: { |
||||
|
visitorLog, |
||||
|
alarmLog |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
activeIndex: 0 |
||||
|
} |
||||
|
}, |
||||
|
computed: { |
||||
|
comName: function() { |
||||
|
if (this.activeIndex === 0) { |
||||
|
return 'visitorLog' |
||||
|
} else if (this.activeIndex === 1) { |
||||
|
return 'alarmLog' |
||||
|
} |
||||
|
return 'visitorLog' |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
changeActiveTab(data) { |
||||
|
this.activeIndex = data |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
</style> |
||||
@ -0,0 +1,116 @@ |
|||||
|
<template> |
||||
|
<div class="head-container"> |
||||
|
<div class="head-search"> |
||||
|
<el-select v-if="isLogType === 'alarm'" v-model="query.snapshotOperation" clearable size="small" placeholder="报警类型" class="filter-item" style="width: 150px;" @change="crud.toQuery"> |
||||
|
<i slot="prefix" class="iconfont icon-zhuangtai" /> |
||||
|
<el-option v-for="item in alarmOptions" :key="item.key" :label="item.display_name" :value="item.key" /> |
||||
|
</el-select> |
||||
|
<el-select v-model="query.personType" clearable size="small" placeholder="安全门列表选择" class="filter-item" style="width: 140px;" @change="crud.toQuery"> |
||||
|
<i slot="prefix" class="iconfont icon-zhuangtai" /> |
||||
|
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" /> |
||||
|
</el-select> |
||||
|
<date-range-picker v-model="blurryTime" class="date-item" /> |
||||
|
<el-button class="filter-item filter-search" size="mini" type="success" icon="el-icon-search" @click="crud.toQuery">搜索</el-button> |
||||
|
<el-button class="filter-item filter-refresh" size="mini" type="warning" icon="el-icon-refresh-left" @click="resetQuery()">重置</el-button> |
||||
|
</div> |
||||
|
<crudOperation> |
||||
|
<template v-slot:right> |
||||
|
<el-button :loading="crud.downloadLoading" size="mini" :disabled="crud.selections.length === 0" @click="doExport"> |
||||
|
<i class="iconfont icon-daochu" /> |
||||
|
导出 |
||||
|
</el-button> |
||||
|
</template> |
||||
|
</crudOperation> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import CRUD, { header, crud } from '@crud/crud' |
||||
|
import crudOperation from '@crud/CRUD.operation' |
||||
|
import DateRangePicker from '@/components/DateRangePicker' |
||||
|
import { mapGetters } from 'vuex' |
||||
|
import { exportFile } from '@/utils/index' |
||||
|
import qs from 'qs' |
||||
|
export default { |
||||
|
components: { DateRangePicker, crudOperation }, |
||||
|
mixins: [header(), crud()], |
||||
|
props: { |
||||
|
isLogType: { |
||||
|
type: String, |
||||
|
default: '' |
||||
|
} |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
blurryTime: [], |
||||
|
delVisible: false, |
||||
|
options: [ |
||||
|
{ value: 'username', label: '用户名' }, |
||||
|
{ value: 'account', label: '登录账号' }, |
||||
|
{ value: 'method', label: '请求方法' }, |
||||
|
{ value: 'params', label: '请求参数' } |
||||
|
], |
||||
|
alarmOptions: [] |
||||
|
} |
||||
|
}, |
||||
|
computed: { |
||||
|
...mapGetters([ |
||||
|
'baseApi', |
||||
|
'user' |
||||
|
]) |
||||
|
}, |
||||
|
created() { |
||||
|
}, |
||||
|
methods: { |
||||
|
[CRUD.HOOK.beforeRefresh]() { |
||||
|
if (this.blurryTime) { |
||||
|
this.crud.query.startTime = this.blurryTime[0] |
||||
|
this.crud.query.endTime = this.blurryTime[1] |
||||
|
} else { |
||||
|
this.crud.query.startTime = null |
||||
|
this.crud.query.endTime = null |
||||
|
} |
||||
|
}, |
||||
|
resetQuery() { |
||||
|
this.crud.query.startTime = null |
||||
|
this.crud.query.endTime = null |
||||
|
this.crud.query.personType = null |
||||
|
this.crud.query.snapshotOperation = null |
||||
|
this.crud.toQuery() |
||||
|
}, |
||||
|
doExport() { |
||||
|
console.log('doExport', this.crud.page.total) |
||||
|
if (this.crud.page.total > 10000) { |
||||
|
this.handleExport('导出数据大于10000条,时间可能较长') |
||||
|
} else { |
||||
|
this.handleExport('此操作将导出所有数据') |
||||
|
} |
||||
|
}, |
||||
|
handleExport(message) { |
||||
|
this.crud.downloadLoading = true |
||||
|
this.$confirm(message + '<span>你是否还要继续?</span>', '提示', { |
||||
|
confirmButtonText: '继续', |
||||
|
cancelButtonText: '取消', |
||||
|
type: 'warning', |
||||
|
dangerouslyUseHTMLString: true |
||||
|
}).then(() => { |
||||
|
const params = { |
||||
|
'personType': this.crud.query.personType, |
||||
|
'snapshotOperation': this.crud.query.snapshotOperation, |
||||
|
'startTime': this.blurryTime.length !== 0 ? this.blurryTime[0].split(' ')[0] : null, |
||||
|
'endTime': this.blurryTime.length !== 0 ? this.blurryTime[1].split(' ')[0] : null |
||||
|
} |
||||
|
console.log('exportFile', params) |
||||
|
exportFile(this.baseApi + '/api/person/downloadFaceRecognizeLog?' + qs.stringify(params, { indices: false, allowDots: true, skipNulls: false })) |
||||
|
this.crud.downloadLoading = false |
||||
|
}).catch(() => { |
||||
|
console.log('取消') |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
</style> |
||||
@ -0,0 +1,61 @@ |
|||||
|
<template> |
||||
|
<div style="height: calc(100vh - 232px);"> |
||||
|
<Search :is-log-type="isLogType" /> |
||||
|
<el-table |
||||
|
ref="table" |
||||
|
:data="crud.data" |
||||
|
style="width: 100%;" |
||||
|
height="calc(100vh - 330px)" |
||||
|
> |
||||
|
<el-table-column type="index" label="序号" width="55" align="center" /> |
||||
|
<el-table-column prop="account" label="安全门设备" /> |
||||
|
<el-table-column prop="username" label="进馆人次" /> |
||||
|
<el-table-column prop="fondsName" label="出馆人次" /> |
||||
|
<el-table-column prop="createTime" label="时间"> |
||||
|
<template slot-scope="scope"> |
||||
|
<div>{{ scope.row.createTime | parseTime }}</div> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
</el-table> |
||||
|
<pagination v-if="crud.data.length !== 0" /> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import CRUD, { presenter, crud, header } from '@crud/crud' |
||||
|
import pagination from '@crud/Pagination' |
||||
|
import Search from '../search.vue' |
||||
|
export default { |
||||
|
name: 'LoginLog', |
||||
|
components: { pagination, Search }, |
||||
|
mixins: [presenter(), crud(), header()], |
||||
|
cruds() { |
||||
|
return CRUD({ |
||||
|
url: 'api/log/initLoginLog', |
||||
|
sort: [], |
||||
|
optShow: { |
||||
|
add: false, |
||||
|
edit: false, |
||||
|
del: false, |
||||
|
download: false, |
||||
|
reset: false, |
||||
|
group: false |
||||
|
} |
||||
|
}) |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
isLogType: 'visitor', |
||||
|
selections: [] |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
::v-deep .el-pagination{ |
||||
|
margin: 24px 0 10px 0 !important |
||||
|
} |
||||
|
</style> |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue