| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721 |
- <template>
- <div class="c-camera-detail">
- <div class="l-flex--row center c-camera-detail__header">
- <div class="c-camera-detail__name u-ellipsis"> {{ camera.name }}</div>
- <i
- class="c-camera-detail__close el-icon-close u-pointer"
- @click="close"
- />
- </div>
- <video
- ref="video"
- class="c-camera-detail__video o-simple-video"
- :poster="poster"
- autoplay
- muted
- @play="onVideoPlay"
- @pause="onVideoPause"
- @waiting="onVideoWaiting"
- @playing="onVideoPlaying"
- @error="onVideoError"
- />
- <div class="c-camera-detail__footer has-padding">
- <div class="l-flex--row c-sibling-item--v c-video-controls">
- <div
- v-if="settingsShow"
- class="settingB"
- @click.stop
- >
- <template v-if="settingTab">
- <div
- class="settingT settingsub"
- @click="onCloseSettingTab"
- >
- <i class="el-icon-arrow-left" />{{ settingActive.label }}
- </div>
- <div class="settingHeight">
- <div
- v-for="item in videoSettings[settingActive.value]"
- :key="item.value"
- class="settingT settingsub"
- @click="settingClick(item)"
- >
- <i :class="{ 'el-icon-check': item.active }" />
- {{ item.label }}
- </div>
- </div>
- </template>
- <template v-else>
- <div
- v-for="tab in tabs"
- :key="tab.value"
- class="settingT"
- @click="onClickSettings(tab)"
- >
- {{ tab.label }} <i class="el-icon-arrow-right" />
- </div>
- </template>
- </div>
- <div class="l-flex__auto">
- <i
- v-if="loading"
- class="c-video-controls__btn el-icon-loading"
- />
- <i
- v-else
- class="c-video-controls__btn has-active u-pointer"
- :class="iconClass"
- @click="onPlayOrPause"
- />
- </div>
- <i
- class="c-video-controls__btn settings el-icon-setting has-active u-pointer"
- @click.stop="onSettings"
- />
- <i
- class="c-video-controls__btn el-icon-refresh has-active u-pointer"
- @click="onRefresh"
- />
- </div>
- <div
- v-if="isTraffic"
- class="c-sibling-item--v far c-camera-detail__traffic has-padding"
- >
- <div
- id="main"
- class="o-canvas"
- />
- <div class="c-choose-date">
- <div class="c-sibling-item c-choose-date__type u-pointer">
- <span
- class="c-choose-date__item"
- :class="{ active: active === 'hour' }"
- @click="onTimeTypeChanged('hour')"
- >
- 1小时
- </span>
- <span
- class="c-choose-date__item"
- :class="{ active: active === 'day' }"
- @click="onTimeTypeChanged('day')"
- >
- 1天
- </span>
- </div>
- <el-date-picker
- v-model="dateValue"
- class="c-sibling-item far c-choose-date__date u-pointer"
- type="date"
- value-format="yyyy-MM-dd"
- :editable="false"
- :clearable="false"
- @change="getStatistic"
- />
- <el-time-select
- v-model="timeValue"
- class="c-sibling-item c-choose-date__time u-pointer"
- :picker-options="timePickerOptions"
- :editable="false"
- :clearable="false"
- @change="getStatistic"
- />
- </div>
- </div>
- </div>
- </div>
- </template>
- <script>
- import { mapGetters } from 'vuex'
- import * as echarts from 'echarts'
- import {
- getStatistic,
- getVideoinfo,
- getAvailableParam,
- setCamera
- } from '@/api/camera'
- import {
- GATEWAY_CAMERA,
- Camera
- } from '@/constant'
- import { parseTime } from '@/utils'
- import playerMixin from '../../player'
- export default {
- name: 'CameraDetail',
- mixins: [playerMixin],
- props: {
- camera: {
- type: Object,
- required: true
- }
- },
- data () {
- const now = new Date()
- return {
- active: 'hour', // hour是小时,day是天
- settingActive: 'items',
- tabs: [
- { value: 'items', label: '分辨率' },
- { value: 'fps', label: '帧率' },
- { value: 'bitRates', label: '码率' }
- ],
- settingTab: true,
- settingsShow: false,
- videoSettings: null,
- infoData: null,
- dateValue: parseTime(now, '{y}-{m}-{d}'),
- timeValue: parseTime(now, '{h}:00'),
- timePickerOptions: {
- start: '00:00',
- step: '01:00',
- end: '23:00'
- }
- }
- },
- computed: {
- ...mapGetters(['token']),
- isTraffic () {
- return this.camera.cameraType === Camera.TRAFFIC
- },
- iconClass () {
- return this.paused || this.needReset ? 'el-icon-video-play' : 'el-icon-video-pause'
- }
- },
- created () {
- this.getAvailableParam()
- },
- mounted () {
- this.createPlayer()
- if (this.isTraffic) {
- this.getStatistic()
- window.addEventListener('resize', this.onResize)
- }
- },
- beforeDestroy () {
- if (this.isTraffic) {
- window.removeEventListener('resize', this.onResize)
- }
- },
- methods: {
- close () {
- this.destroyPlayer()
- this.$emit('close')
- },
- createPlayer () {
- this.playUrl(`${GATEWAY_CAMERA}/${this.camera.identifier}?authorization=${this.token}`)
- },
- setCamera () {
- this.hideSettingsMenu()
- setCamera({
- deviceId: this.camera.identifier,
- ...this.infoData
- }).then(
- () => {
- this.dataInit()
- this.destroyPlayer()
- this.createPlayer()
- },
- () => {
- this.infoData = { ...this.$infoData }
- }
- )
- },
- getAvailableParam () {
- if (this.$videoPramsLoading) {
- this.$message({
- type: 'warning',
- message: '配置获取中,请稍后重试'
- })
- return
- }
- this.$videoPramsLoading = true
- getAvailableParam().then(({ data }) => {
- this.videoSettings = {
- items: data.itemList.map(item => {
- // maxBitRateOptions: 20480
- // minBitRateOptions: 3
- // resolutionFpsMax: 20
- // snHight: 1944
- // snWidth: 2592
- return {
- value: `${item.snWidth}*${item.snHight}`,
- label: `${item.snWidth}*${item.snHight}`,
- active: false,
- ...item
- }
- }),
- fps: [],
- bitRates: [],
- streamRateType: data.streamRateTypeList
- }
- this.getVideoinfo()
- }).finally(() => {
- this.$videoPramsLoading = false
- })
- },
- onSettings () {
- if (!this.videoSettings) {
- this.getAvailableParam()
- return
- }
- if (!this.infoData) {
- this.getVideoinfo()
- return
- }
- this.settingTab = false
- if (this.settingsShow) {
- this.hideSettingsMenu()
- } else {
- document.addEventListener('click', this.hideSettingsMenu)
- this.settingsShow = true
- }
- },
- hideSettingsMenu () {
- if (this.settingsShow) {
- this.settingsShow = false
- document.addEventListener('click', this.hideSettingsMenu)
- }
- },
- onRefresh () {
- this.destroyPlayer()
- this.createPlayer()
- },
- onClickSettings (tab) {
- this.settingActive = tab
- this.settingTab = true
- },
- onCloseSettingTab () {
- this.settingTab = false
- },
- getVideoinfo () {
- if (this.$videoInfoLoading) {
- this.$message({
- type: 'warning',
- message: '配置获取中,请稍后重试'
- })
- return
- }
- this.$videoInfoLoading = true
- getVideoinfo(this.camera.identifier).then(({ data }) => {
- const { width, hight, frameRate, bitRate } = data
- this.infoData = { width, hight, frameRate, bitRate }
- this.dataInit()
- }).finally(() => {
- this.$videoInfoLoading = false
- })
- },
- dataInit () {
- const { width, hight, frameRate, bitRate } = this.infoData
- const value = `${width}*${hight}`
- let target = null
- this.videoSettings.items.forEach(item => {
- if (item.value === value) {
- item.active = true
- target = item
- } else {
- item.active = false
- }
- })
- if (target) {
- const fps = []
- const max = target.resolutionFpsMax
- for (let i = 1; i <= max; i++) {
- fps.push({ value: i, label: `${i}fps`, active: i === frameRate })
- }
- this.videoSettings.fps = fps
- const bitRates = []
- const minBitRate = target.minBitRateOptions
- const maxBitRate = target.maxBitRateOptions
- const streamRateType = this.videoSettings.streamRateType
- for (let i = 0; i < streamRateType.length; i++) {
- const rateType = streamRateType[i]
- if (rateType >= minBitRate && rateType <= maxBitRate) {
- bitRates.push({ value: rateType, label: `${rateType}kb/s`, active: rateType === bitRate })
- }
- }
- this.videoSettings.bitRates = bitRates
- } else {
- this.videoSettings.items = [
- { label: `${width}*${hight}`, active: true }
- ]
- this.videoSettings.fps = [
- { value: frameRate, label: `${frameRate}fps`, active: true }
- ]
- this.videoSettings.bitRates = [
- { value: bitRate, label: `${bitRate}kb/s`, active: true }
- ]
- }
- },
- settingClick (item) {
- let value = null
- switch (this.settingActive.value) {
- case 'items':
- value = `${this.infoData.width}*${this.infoData.hight}`
- break
- case 'fps':
- value = this.infoData.frameRate
- break
- case 'bitRates':
- value = this.infoData.bitRate
- break
- default:
- return
- }
- if (value === item.value) {
- return
- }
- this.$infoData = { ...this.infoData }
- switch (this.settingActive.value) {
- case 'items':
- this.infoData.width = item.snWidth
- this.infoData.hight = item.snHight
- this.infoData.frameRate = Math.min(item.resolutionFpsMax, this.infoData.frameRate)
- this.infoData.bitRate = Math.max(item.minBitRateOptions, Math.min(item.maxBitRateOptions, this.infoData.bitRate))
- break
- case 'fps':
- this.infoData.frameRate = item.value
- break
- case 'bitRates':
- this.infoData.bitRate = item.value
- break
- default:
- break
- }
- this.setCamera()
- },
- onTimeTypeChanged (type) {
- if (this.active === type) {
- return
- }
- this.active = type
- this.getStatistic()
- },
- getStatistic () {
- const endTime = `${this.dateValue} ${this.timeValue}:00`
- const date = new Date(endTime)
- switch (this.active) {
- case 'hour':
- date.setHours(date.getHours() - 1)
- break
- default:
- date.setDate(date.getDate() - 1)
- break
- }
- const startTime = parseTime(date, '{y}-{m}-{d} {h}:{i}:{s}')
- this.refreshEchart([])
- getStatistic({
- deviceId: this.camera.identifier,
- startTime,
- endTime,
- pageIndex: 1,
- pageSize: 10000
- }).then(({ data }) => {
- this.refreshEchart(data)
- })
- },
- getEchartData (data) {
- const date = new Date(`${this.dateValue} ${this.timeValue}:00`)
- const xdata = []
- const ydata = []
- if (this.active === 'hour') {
- date.setHours(date.getHours() - 1)
- for (let i = 0; i <= 60; i++) {
- const value = parseTime(date, '{y}-{m}-{d} {h}:{i}')
- xdata.push(value.split(' ')[1])
- ydata.push(data[value] || 0)
- date.setMinutes(date.getMinutes() + 1)
- }
- } else {
- date.setDate(date.getDate() - 1)
- for (let i = 0; i <= 24; i++) {
- const value = parseTime(date, '{y}-{m}-{d} {h}')
- xdata.push(`${value.split(' ')[1]}:00`)
- ydata.push(data[value] || 0)
- date.setHours(date.getHours() + 1)
- }
- }
- return { xdata, ydata }
- },
- transformEchartData (data) {
- const ydata = {}
- const isHour = this.active === 'hour'
- data.forEach(({ eventTime, insidePeopleNum }) => {
- const key = eventTime.slice(0, isHour ? 16 : 13)
- if (ydata[key]) {
- ydata[key] = Math.max(ydata[key], insidePeopleNum)
- } else {
- ydata[key] = insidePeopleNum
- }
- })
- return this.getEchartData(ydata)
- },
- onResize () {
- this.$echarts?.resize()
- },
- refreshEchart (echartsData) {
- const { xdata, ydata } = this.transformEchartData(echartsData)
- if (!this.$echarts) {
- this.$echarts = echarts.init(document.getElementById('main'))
- }
- this.$echarts?.setOption({
- title: {
- text: '区域内人数',
- textStyle: {
- color: '#fff',
- fontWeight: 'bold'
- }
- },
- xAxis: {
- type: 'category',
- data: xdata,
- axisLine: {
- lineStyle: {
- color: '#4779BC'
- }
- },
- axisLabel: {
- color: '#A9CEFF'
- }
- },
- yAxis: {
- type: 'value',
- minInterval: 1,
- splitLine: {
- lineStyle: {
- color: '#4779BC',
- type: 'dashed'
- }
- },
- axisLine: {
- lineStyle: {
- color: '#4779BC'
- }
- },
- axisLabel: {
- color: '#A9CEFF'
- }
- },
- grid: {
- left: '30',
- right: '20',
- top: '40',
- bottom: '20'
- },
- series: [
- {
- data: ydata,
- type: 'bar',
- showBackground: true,
- backgroundStyle: {
- color: 'transparent'
- },
- itemStyle: {
- color: 'rgba(0, 191, 208, 0.5)'
- },
- select: {
- itemStyle: {
- color: 'rgb(0, 234, 255)'
- }
- }
- }
- ],
- tooltip: {
- formatter: '时间:{b}<br />人流量:{c}'
- }
- })
- }
- }
- }
- </script>
- <style lang="scss" scoped>
- $theme-blue: #003e90;
- .c-camera-detail {
- position: relative;
- width: 100%;
- height: 100%;
- overflow: hidden;
- &__video {
- width: 100%;
- height: 100%;
- }
- &__header {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 42px;
- color: #fff;
- font-size: 24px;
- text-align: center;
- background-color: rgba($theme-blue, 0.8);
- z-index: 9;
- &::after {
- content: "";
- position: absolute;
- top: 100%;
- left: 50%;
- width: 700px;
- height: 0;
- border-top: 16px solid rgba($theme-blue, 0.8);
- border-right: 16px solid transparent;
- border-bottom: 16px solid transparent;
- border-left: 16px solid transparent;
- transform: translateX(-50%);
- }
- }
- &__name {
- display: inline-block;
- width: 600px;
- }
- &__close {
- position: absolute;
- top: 50%;
- right: 0;
- padding: $spacing;
- color: #fff;
- font-size: 32px;
- transform: translateY(-50%);
- }
- &__footer {
- position: absolute;
- left: 0;
- bottom: 0;
- width: 100%;
- }
- &__traffic {
- position: relative;
- line-height: 1;
- border-radius: $radius--mini;
- background-color: rgba($theme-blue, 0.8);
- }
- }
- .c-video-controls {
- position: relative;
- height: 48px;
- padding: 0 32px 0 16px;
- border-radius: $radius--mini;
- background-color: rgba(#00060d, 0.75);
- &__btn {
- color: #fff;
- font-size: 32px;
- &.settings {
- margin-right: 32px;
- }
- }
- }
- .settingB {
- position: absolute;
- right: 134px;
- bottom: 48px;
- padding: 10px 0px;
- color: #fff;
- background-color: rgba(#000, 0.85);
- transform: translateX(50%);
- .settingT {
- position: relative;
- padding: 0px 16px;
- width: 180px;
- height: 40px;
- line-height: 40px;
- cursor: pointer;
- &:hover {
- background-color: rgba(#454545, 0.85);
- }
- i {
- position: absolute;
- top: 13px;
- right: 16px;
- }
- }
- .settingHeight {
- max-height: 250px;
- overflow: auto;
- }
- .settingsub {
- width: 140px;
- padding-left: 42px;
- i {
- position: absolute;
- left: 16px;
- }
- }
- }
- .c-choose-date {
- position: absolute;
- right: $spacing;
- top: $spacing;
- color: #fff;
- &__type {
- display: inline-block;
- height: 24px;
- line-height: 24px;
- border-radius: $radius--mini;
- background-color: #4478bc;
- }
- &__item {
- display: inline-block;
- width: 50px;
- font-size: 14px;
- text-align: center;
- border-radius: $radius--mini;
- background-color: #4478bc;
- &.active {
- background: #0096ff;
- }
- }
- &__date {
- width: 130px;
- }
- &__time {
- width: 80px;
- }
- ::v-deep input {
- height: 24px;
- padding-right: 10px;
- color: #fff;
- font-size: 14px;
- line-height: 24px;
- background-color: transparent;
- }
- ::v-deep .el-input__icon {
- line-height: 24px;
- }
- }
- .o-canvas {
- width: 100%;
- height: 200px;
- }
- </style>
|