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