feat:更新飞渡ac.min.js

main
guangyin 2024-01-25 16:15:56 +08:00
commit ea53cd989c
21 changed files with 2783 additions and 0 deletions

543
hbt-debang/public/ac.min.js vendored 100644

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>进入后台</title>
<defs>
<linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-1">
<stop stop-color="#FFFFFF" offset="0%"></stop>
<stop stop-color="#C4EEFF" offset="100%"></stop>
</linearGradient>
<linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-2">
<stop stop-color="#F7F7F7" offset="0%"></stop>
<stop stop-color="#F7F7F7" offset="100%"></stop>
</linearGradient>
</defs>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="智慧化工园区/封闭园区-首页" transform="translate(-1476.000000, -18.000000)" fill="url(#linearGradient-2)" fill-rule="nonzero">
<g id="进入后台" transform="translate(1476.000000, 18.000000)">
<g id="编组" transform="translate(1.000000, 2.000000)">
<path d="M13.5997167,15.8663833 C13.9078191,15.8732724 14.1539931,16.1250122 14.1539931,16.4331917 C14.1539931,16.7413711 13.9078191,16.993111 13.5997167,17 L3.4,17 C3.09189753,16.993111 2.84572355,16.7413711 2.84572355,16.4331917 C2.84572355,16.1250122 3.09189753,15.8732724 3.4,15.8663833 L13.6,15.8663833 L13.5997167,15.8663833 Z M14.1663833,0 C15.7301,0 17,1.18546666 17,2.6435 L17,12.0889833 C16.9977333,13.5473 15.7278333,14.7324834 14.1663833,14.7324834 L2.83333334,14.7324834 C1.27245,14.7327667 0,13.5475833 0,12.0892667 L0,2.64378334 C0,1.18546666 1.27216666,0 2.83361666,0 L14.1666667,0 L14.1663833,0 Z M14.1663833,1.13361666 L2.83333334,1.13361666 C1.89521668,1.13361666 1.13333334,1.81078332 1.13333334,2.64378334 L1.13333334,12.0889833 C1.13333334,12.9217 1.89521668,13.59915 2.83333334,13.59915 L14.1658167,13.59915 C15.1042167,13.59915 15.8661003,12.9217 15.8661003,12.0889833 L15.8661003,2.64378334 C15.8666667,1.81106666 15.1045,1.13333334 14.1666667,1.13333334 L14.1663833,1.13361666 Z M9.60925,5.15383334 L9.71295,5.21333334 C9.9042,5.32978334 9.97248334,5.58733334 9.86623334,5.79218334 L8.2229,8.8485 C8.12345,9.03975 7.90443334,9.1171 7.71856666,9.03351666 L7.67351666,9.00971666 L7.67465,9.00971666 L7.56981666,8.95021666 C7.37856666,8.83348332 7.31028332,8.57621666 7.41653334,8.37108334 L9.05986666,5.31505 C9.1715,5.11246666 9.41516666,5.04078334 9.60925,5.15355 L9.60925,5.15383334 Z M4.66026666,5.23146666 C4.80638892,5.0697265 5.05550469,5.05582354 5.21871666,5.2003 L5.25668334,5.23713334 C5.40741668,5.40146668 5.41591668,5.65986668 5.28331666,5.83496666 L5.25271666,5.87123334 L4.1225,7.08333334 L5.26065,8.296 C5.4243111,8.4773971 5.42406623,8.75324375 5.26008334,8.93435 C5.10708334,9.09868334 4.8654,9.10945 4.70078334,8.96665 L4.66678334,8.93378334 L3.24076666,7.4103 C3.08768133,7.23724923 3.07485782,6.98124945 3.20988334,6.79376666 L3.24105,6.75608334 L4.66055,5.2309 L4.66026666,5.23146666 Z M11.7402,5.23203334 C11.8861351,5.06967116 12.1356986,5.05537785 12.2992167,5.20001666 L12.3332167,5.23288334 L13.75895,6.76543334 C13.8411167,6.84675 13.88645,6.96263334 13.8827667,7.08276666 C13.8844667,7.18845 13.8516,7.29045 13.7915333,7.37233334 L13.75895,7.41143334 L12.3332167,8.93293334 C12.1793667,9.09981668 11.9402333,9.11086668 11.7739167,8.96636666 L11.7370833,8.9301 C11.5874049,8.76280265 11.575693,8.51342237 11.7090333,8.33283334 L11.7396333,8.29656666 L12.8692833,7.08276666 L11.7396333,5.87066666 C11.575933,5.68917628 11.5761778,5.41323293 11.7402,5.23203334 Z" id="形状"></path>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

@ -0,0 +1,74 @@
<div class="center-right d-flex">
<div class="box-content height-auto">
<title-component :bg-size="'large'" :imgSrc="imgPowerSrc">
企业消防力量分布
</title-component>
<div class="margin-top-30 d-flex flex-wrap">
<div class="d-flex wd-100" v-for="item in powerList">
<div v-for="single in item" class="power-item d-flex">
<div class="icon-style" v-if="single.label">
<img :src="iconImg3" alt="">
</div>
<div v-if="single.label">
<div>
<b>
{{ single.num }}
</b>
</div>
<div class="text-style">{{ single.label }}</div>
</div>
</div>
</div>
</div>
</div>
<div class="box-content flex-0 height-auto">
<title-component :bg-size="'large'" @changeMore="changeMore('large')" :show-more="true" :imgSrc="imgAlertSrc">
全场重点消防监控报警
</title-component>
<div class="d-flex height-auto margin-top-30">
<div class="flex-1">
<div class="charts-box">
<div class="text">
<div class="count">{{ count }}%</div>
设备在线
</div>
<el-progress type="circle" :width="172" define-back-color="rgba(255,255,255,0.16)" :format="textFormat"
:stroke-width="16" :percentage="count" :color="colors"></el-progress>
</div>
<div class="d-flex margin-top-23 padding-0-15">
<div class="flex-1 text-center green-text font-size-14">
在线监测 50
</div>
<div class="flex-1 text-center font-size-14">
总监测点 63
</div>
</div>
</div>
<div class="flex-1 d-flex flex-direction-column">
<div class="static margin-bottom-16">
<div class="border l-t"></div>
<div class="border r-t"></div>
<div class="border r-b"></div>
<div class="border l-b"></div>
<span><i class="green-cycle"></i>已处理报警</span><span class="count">75</span>
</div>
<div class="static margin-bottom-16">
<div class="border l-t"></div>
<div class="border r-t"></div>
<div class="border r-b"></div>
<div class="border l-b"></div>
<span><i class="orange-cycle"></i>未处理报警</span><span class="count">75</span>
</div>
<div class="static margin-bottom-16">
<div class="border l-t"></div>
<div class="border r-t"></div>
<div class="border r-b"></div>
<div class="border l-b"></div>
<span><i class="red-cycle"></i>逾期处理</span><span class="count">75</span>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,104 @@
import {Component, Emit, Vue} from 'vue-property-decorator';
import template from "./centerSide.component.html"
import TitleComponent from "@/components/title.component.vue"
import("@/assets/style/pageCommon.component.scss")
Component.registerHooks([
'beforeRouteLeave',
]);
@Component({
template,
components: {
TitleComponent,
},
})
export default class CenterSideComponent extends Vue {
//标题图片
imgSrc = require("@/assets/icons/png/env/env-title-icon.png");
imgPowerSrc = require("@/assets/icons/png/power-icon.png");
imgAlertSrc = require("@/assets/icons/png/alert-icon.png");
iconImg3 = require("@/assets/img/env/count.png");
count = 0
public timer:any;
public colors = [
{color: '#FF5959', percentage: 20},
{color: '#FF5959', percentage: 40},
{color: '#FFA900', percentage: 60},
{color: '#80ED30', percentage: 80},
{color: '#80ED30', percentage: 100}
];
powerList = [
[
{
label: '消防人员(人)',
num: 34
},{
label: '消防车辆(辆)',
num: 4
},{
label: '消防设施(个)',
num: 54
}
],
[{
label: '火灾报警点(个)',
num: 20
},{
label: '应急物资(套)',
num: 340
},{
label: '重大危险源(个)',
num: 2
}],
[{
label: '储罐(个)',
num: 24
},{
label: '重大危险源(个)',
num: 4
},{
label: '',
num: 0
}]
]
mounted() {
this.timer = setInterval(()=>{
this.getCount(80)
},4000)
}
public textFormat(data){
return ""
}
public getCount(data){
this.count = 0;
setTimeout(()=>{
const timer = setInterval(()=>{
if(this.count<data){
this.count++
}else{
clearInterval(timer)
}
},20)
},400)
}
@Emit('changeMore')
changeMore(e){
}
beforeDestroy() {
}
destroyed() {
clearInterval(this.timer);
this.timer = null;
}
}

View File

@ -0,0 +1,66 @@
<div class="env-left left-part">
<div class="box-content flex-0 height-auto">
<title-component :imgSrc="imgDutySrc">
今日消防值班人员
</title-component>
<div class="plan-card margin-top-10">
<div class="border l-t"></div>
<div class="border r-t"></div>
<div class="border r-b"></div>
<div class="border l-b"></div>
<p class="margin-bottom-13">带班领导陈远航 139308897887</p>
<ScrollBoxComponent :id="'dutyBox'" class="duty-box" :speed="0.15">
<ul class="plan">
<li :key="i" v-for="(item,i) in dutyList">
<div class="d-flex flex-align-center">
<img src="~@/assets/img/default-icon.png" alt="">
<span class="duty-name-box">{{ item.name }}</span>
</div>
<span class="tel-box">{{ item.tel }}</span>
<span>{{ item.job }}</span>
</li>
</ul>
</ScrollBoxComponent>
</div>
</div>
<div class="box-content">
<title-component class="margin-top-30" :imgSrc="imgCarSrc">
车辆信息
</title-component>
<div class="plan-head">
<span class="span-item">车辆类型</span>
<span class="span-item">备战</span>
<span class="span-item">巡检</span>
<span class="span-item">故障</span>
<span class="span-item">保养</span>
<span class="span-item">合计</span>
</div>
<ScrollBoxComponent :id="'carBox'" class="car-box" :speed="0.15">
<div class="plan-head-tbody">
<div class="tr" :key="i" v-for="(item,i) in tableData">
<span class="span-item">{{ item.title }}</span>
<span class="span-item">{{ item.bz }}</span>
<span class="span-item">{{ item.xj }}</span>
<span class="span-item">{{ item.gz }}</span>
<span class="span-item">{{ item.by }}</span>
<span class="span-item">{{ item.total }}</span>
</div>
</div>
</ScrollBoxComponent>
</div>
<div class="box-content flex-0" style="height: 300px">
<title-component class="margin-top-30" @changeMore="changeMore('small')" :show-more="true" :imgSrc="imgPointSrc">
重点区域火灾报警点位
</title-component>
<div class="fire-point d-flex flex-wrap margin-top-16 margin-bottom-20">
<ScrollBoxComponent :id="'eqBox'" class="eqBox" :speed="0.15">
<div>
<div class="fire-point-item d-flex" v-for="item in eqList">
<div class="fire-point-item-item bg-gyay">{{item.label}}</div>
<div class="fire-point-item-item" :class="item.status === 0?'red-text':'green-text'">{{item.status === 0?'报警':'正常'}}</div>
</div>
</div>
</ScrollBoxComponent>
</div>
</div>
</div>

View File

@ -0,0 +1,481 @@
import {Component, Emit, Prop, Vue} from 'vue-property-decorator';
import template from "./leftSide.component.html"
import TitleComponent from "@/components/title.component.vue"
import WarningListComponent from "@/components/warningList.component.vue"
import ScrollBoxComponent from "@/components/scrollBox.component.vue"
import * as turf from '@turf/turf'
import Mock from "mockjs"
import moment from "moment"
import("@/assets/style/pageCommon.component.scss")
Component.registerHooks([
'beforeRouteLeave',
]);
@Component({
template,
components: {
TitleComponent,
WarningListComponent,
ScrollBoxComponent
},
})
export default class LeftSideComponent extends Vue {
//传入的报警信息详情
@Prop({
default: ()=> {
return {
"typeName": "火灾",
"content": "北京汉邦唐化工厂区发生火灾",
"levelName": "二级",
"levelClass": "level-2-text",
"levelClassIcon": "level-2",
"time": "2023-05-15 15:21:35",
"address": "工厂厂房",
"reporter": "蒋丽",
"tableHeader": [
{
"prop": "name",
"label": "摄像头名称"
},
{
"prop": "type",
"label": "类型"
},
{
"prop": "distance",
"label": "距离 (m)"
}
],
"tableData": [
{
"type": "简型摄像头",
"distance": "10",
"name": "1号摄像头"
},
{
"type": "简型摄像头",
"distance": "20",
"name": "2号摄像头"
},
{
"type": "简型摄像头",
"distance": "22",
"name": "3号摄像头"
},
{
"type": "简型摄像头",
"distance": "10",
"name": "4号摄像头"
},
{
"type": "简型摄像头",
"distance": "20",
"name": "5号摄像头"
},
{
"type": "简型摄像头",
"distance": "22",
"name": "6号摄像头"
}
],
"title": "火灾爆炸",
"info": "北京汉邦唐化工厂区发生火灾",
"level": "1",
"type": "fire",
"tel": 13231475537,
"id": "110000197911038403",
"position": [
13312990.72,
4098613.12,
0.51
]
}
}
}) info!: any;
//标题左侧图标
imgSrc = require("@/assets/icons/png/env/env-title-icon.png");
imgDutySrc = require("@/assets/icons/png/duty-icon.png");
imgCarSrc = require("@/assets/icons/png/car-icon.png");
imgPointSrc = require("@/assets/icons/png/point-icon.png");
currentCompany = false
popObj: any = {
popTitle: '报警记录',
type: '',
title: '风险点异常',
info: '',
time: '',
address: '',
level: '',
typeName: "",
reporter: "",
tel: "",
position: [],
content: "",
typeId: "",
id: ''
}
typeList = [
{
label: '气体泄露',
value: 'normal',
level: 2,
id: 1
}, {
label: '火灾爆炸',
value: 'fire',
level: 1,
id: 2
}, {
label: '气液泄露',
value: 'normal',
level: 1,
id: 3
}, {
label: '安全事故',
value: 'normal',
level: 2,
id: 4
}, {
label: '自然灾害',
value: 'normal',
level: 3,
id: 5
},
]
eqList =[
{
label:'乙烯装置01-Y-001',
status:1,
},{
label:'乙烯装置02-W-098',
status:0,
},{
label:'芳烃装置03-R-012',
status:1,
},{
label:'硝基苯胺04-Q-034',
status:0,
},{
label:'硝基苯胺04-T-001',
status:1,
},{
label:'邻苯甲酚09-Y-129',
status:0,
},{
label:'二氯苯胺10-Y-09',
status:0,
},
]
levelClass = 'level-1';
levelName = '一级';
level = 1;
cardText = 'level-1-text';
dutyList = [
{name:'刘月明',tel:'139308897887',job:'指挥员'},
{name:'王建民',tel:'139308897887',job:'指挥员'},
{name:'李璇',tel:'139308897887',job:'指挥员'},
{name:'刘月明',tel:'139308897887',job:'指挥员'},
{name:'王建民',tel:'139308897887',job:'指挥员'},
{name:'李璇',tel:'139308897887',job:'指挥员'}
]
tableData = [
{
title: '水罐消防车',
bz: '1',
xj: '2',
gz: '0',
by: '0',
total: '3',
}, {
title: '抢险救援车',
bz: '2',
xj: '0',
gz: '1',
by: '1',
total: '4',
}, {
title: '干粉泡沫联用车',
bz: '1',
xj: '2',
gz: '0',
by: '0',
total: '3',
}, {
title: '泡沫运输车',
bz: '2',
xj: '0',
gz: '1',
by: '1',
total: '4',
},{
title: '重型泡沫消防车',
bz: '2',
xj: '0',
gz: '1',
by: '1',
total: '4',
},{
title: '举高喷射消防车',
bz: '1',
xj: '1',
gz: '1',
by: '1',
total: '4',
},{
title: '化学洗消消防车',
bz: '3',
xj: '0',
gz: '1',
by: '1',
total: '5',
},{
title: '大跨度消防车',
bz: '0',
xj: '0',
gz: '1',
by: '1',
total: '2',
},{
title: '干粉消防车',
bz: '0',
xj: '0',
gz: '1',
by: '1',
total: '2',
},{
title: '云梯消防车',
bz: '1',
xj: '0',
gz: '1',
by: '1',
total: '3',
},{
title: '槽罐车',
bz: '1',
xj: '0',
gz: '2',
by: '1',
total: '4',
},{
title: '多功能救护车',
bz: '1',
xj: '0',
gz: '2',
by: '1',
total: '4',
},{
title: '后勤保障车',
bz: '1',
xj: '0',
gz: '2',
by: '1',
total: '4',
}
]
tableDataExpert = [
{
person: '黄勇军, 程宇航,蔡宝惠',
area: '火灾救援',
checked: false,
}, {
person: '林子涵,吴琳芳,梁博雅',
area: '医疗救助',
checked: false,
}, {
person: '陶春华,邓思琪,张锐毅',
area: '医疗组',
checked: false,
},
]
tableDataVicinity = [
{
position: '人民广场东北方向…',
distance: '10',
name: '一号站',
checked: false,
}, {
position: '人民广场西南方向…',
distance: '20',
name: '四号站',
checked: false,
}, {
position: '人民广场西北方向…',
distance: '30',
name: '二号站',
checked: false,
},
]
// 当前进度条
active = 1
get dataList() {
return this.$store.state.warningList.filter(item => item.model === 'eme')
}
//下一步操作
nextStep() {
if (this.active < 3) {
this.active++
}
this.gotoNext()
}
@Emit('gotoNext')
gotoNext() {
//
}
changeLevel(type) {
if (type === 'up') {
if (this.level === 1) {
return
}
this.level--
} else if (type === 'down') {
if (this.level === 4) {
return
}
this.level++
}
const nameList = {
1: '一级',
2: '二级',
3: '三级',
4: '四级',
}
this.levelName = nameList[this.level]
this.levelClass = `level-${this.level}`
this.cardText = `level-${this.level}-text`
}
setData(val, type) {
if (type === 'typeName') {
const find = this.typeList.find(item => item.id == val) as any
this.popObj.level = find.level
this.popObj.title = find.label
this.popObj.type = find.value
}
this.popObj[type] = val
}
//展示告警信息
showWaring(item) {
const levelMap = {
1:'一级',
2:'二级',
3:'三级',
4:'四级',
}
const levelClassMap = {
4:'blue',
3:'yellow',
2:'orange',
1:'red',
}
this.getItem(Object.assign(item,{
level:item.level,
levelClass:levelClassMap[item.level],
levelName:levelMap[item.level],
equipment:'园区2号监测点',
tableHeader:[
{
'prop': 'alarmSubject',
'label': '告警主体',
'width': '90px',
}, {
'prop': 'COD',
'label': 'COD',
}, {
'prop': 'standard',
'label': '标准值',
},
],
tableData: [{
'alarmSubject': '排放量',
'COD': '456m<sup>3</sup>',
'standard': '466m<sup>3</sup>',
}, {
'alarmSubject': '排放浓度',
'COD': '2.6mg/L',
'standard': '2.5mg/L',
}, {
'alarmSubject': '排放时间',
'COD': '2023.3.31 05:03:45',
'standard': '8:00-22:00',
}, {
'alarmSubject': '排放浓度',
'COD': '2.6mg/L',
'standard': '2.5mg/L',
}, {
'alarmSubject': '排放时间',
'COD': '2023.3.31 05:03:45',
'standard': '8:00-22:00',
}]
}))
}
//点击告警信息
@Emit()
getItem(item) {
// console.log('item', item)
}
count = 1
mounted() {
}
destroyed() {
}
changeClose() {
console.log(1)
}
addManual() {
this.currentCompany = true
}
submitWaring() {
this.popObj.id = Mock.mock("@id");
if (this.popObj.type === 'normal') {
this.popObj.position = turf.randomPosition([
13312995.84,
4098177.2800000003,
13313096.96,
4098754.88,
]);
} else if (this.popObj.type === 'fire') {
this.popObj.position = [13312990.72, 4098613.12, 0.51]
}
this.popObj.time = moment().subtract(0, "days").format("YYYY-MM-DD HH:mm:ss")
this.$store.commit("upDateWaringList", [this.popObj])
this.currentCompany = false
}
//点击点位
@Emit('changePoint')
changePoint(item) {
console.log(item)
//
}
//点击点位
@Emit('changeMore')
changeMore(item) {
console.log(item)
//
}
}

View File

@ -0,0 +1,23 @@
<div class="env-right">
<div class="box-content">
<title-component :imgSrc="imgMsgSrc">
报警信息
</title-component>
<div class="margin-top-30 waring-list-con">
<warning-list-component @getItem="showWaring" :data-list="dataList"></warning-list-component>
</div>
</div>
<div class="box-content margin-top-20 flex-0 height-auto">
<title-component @changeMore="changeMore('video')" :show-more="true" :imgSrc="imgVideoSrc">
全场重点消防监控
</title-component>
<div class="margin-top-12">2#氧化反应器R-2222</div>
<div class="img-box margin-top-12" @click="openPopVideo">
<img width="100%" src="~@/assets/img/index_video_1.png" alt="" />
</div>
<div class="margin-top-12">4#氧化反应器R-2224</div>
<div @click="openPopVideo" class="img-box margin-top-12">
<img width="100%" src="~@/assets/img/index_video_2.png" alt="" />
</div>
</div>
</div>

View File

@ -0,0 +1,102 @@
import {Component, Emit, Vue} from 'vue-property-decorator';
import template from "./rightSide.component.html"
import TitleComponent from "@/components/title.component.vue"
import WarningListComponent from "@/components/warningList.component.vue"
import {getPreviousDay} from "@/utils/utils";
import("@/assets/style/pageCommon.component.scss")
Component.registerHooks([
'beforeRouteLeave',
]);
@Component({
template,
components: {
TitleComponent,
WarningListComponent
},
})
export default class RightSideComponent extends Vue {
//标题图片
imgSrc = require("@/assets/icons/png/env/env-title-icon.png");
imgMsgSrc = require("@/assets/icons/png/message-icon.png");
imgVideoSrc = require("@/assets/icons/png/video-icon.png");
mounted() {
}
get dataList() {
// return this.$store.state.warningList.filter(item => item.model === 'env')
return this.$store.state.eventList
}
//展示告警信息
showWaring(item) {
const levelMap = {
1:'一级',
2:'二级',
3:'三级',
4:'四级',
}
const levelClassMap = {
4:'blue',
3:'yellow',
2:'orange',
1:'red',
}
console.log(item)
this.getItem(Object.assign(item, {
level: item.level,
levelClass: levelClassMap[item.level],
levelName: levelMap[item.level],
equipment: '北区广场10号监测仪',
tableHeader: [
{
'prop': 'alarmSubject',
'label': '告警主体',
'width': '90px',
}, {
'prop': 'COD',
'label': 'COD',
}, {
'prop': 'standard',
'label': '标准值',
},
],
tableData: [{
'alarmSubject': '排放量',
'COD': '456m<sup>3</sup>',
'standard': '466m<sup>3</sup>',
}, {
'alarmSubject': '排放浓度',
'COD': '2.6mg/L',
'standard': '2.5mg/L',
}, {
'alarmSubject': '排放时间',
'COD': getPreviousDay(item.time),
'standard': '8:00-22:00',
}]
}))
}
//点击告警信息
@Emit('getItem')
getItem(item) {
// console.log('item', item)
}
@Emit('changeMore')
changeMore(e){
}
@Emit('openPopVideo')
openPopVideo(e){
}
beforeDestroy() {
}
destroyed() {
}
}

View File

@ -0,0 +1,134 @@
<template>
<!-- :class="!isScroll?'scroll':''"-->
<div class="listScroll" :id="id" ref="box">
<slot></slot>
<slot></slot>
</div>
</template>
<script lang="ts">
import {Component, Emit, Prop, Vue, Watch} from 'vue-property-decorator';
@Component
export default class ScrollBoxComponent extends Vue {
//
@Prop({default: 999}) value!: any;
@Prop({default: 1}) speed!: any;
@Prop({default: 'scrollBox'}) id!: any;
height = 0
isScroll = true
get ele0() {
return (this.$refs.box as any).children[0];
}
//slot
get ele1() {
return (this.$refs.box as any).children[1];
}
//
get boxHeight() {
return (this.$refs.box as any).clientHeight;
}
//
setEvent() {
(this.$refs.box as any).onmouseenter = () => {
this.isScroll = false;
};
(this.$refs.box as any).onmouseleave = () => {
this.isScroll = true;
this.$nextTick(() => {
this.start(this.height);
});
};
}
setHeight(){
const scrollDom = document.querySelector("#listScroll") as any
scrollDom.addEventListener('scroll',(e)=>{
console.log(e.target.scrollTop)
let height = e.target.scrollTop
if (height >= this.ele0.clientHeight) {
this.height = 0;
}else{
this.height = height
}
this.start(this.height);
})
}
removeListener(){
const scrollDom = document.querySelector("#listScroll") as any
scrollDom.removeEventListener('scroll',(e)=>{
console.log(e)
})
}
//
start(height) {
this.ele0.style = `transform:translateY(-${height}px);`;
this.ele1.style = `height:${this.boxHeight}px;transform:translateY(-${height}px);overflow: hidden;`;
// console.log(height,'height')
// console.log(this.boxHeight,'boxHeight')
if (height >= this.ele0.clientHeight) {
this.height = 0;
} else {
this.height += this.speed;
}
if (!this.isScroll) return;
window.requestAnimationFrame(() => {
this.start(this.height);
})
}
mounted() {
//
setTimeout(()=>{
if (this.boxHeight < this.ele0.clientHeight) {
this.start(this.height);
this.setEvent();
} else {
this.isScroll = false;
}
},1000)
}
}
</script>
<style lang="scss" scoped>
//
@mixin scrollStyle($width) {
&::-webkit-scrollbar {
width: $width !important;
height: 100% !important;
}
&::-webkit-scrollbar-thumb {
border-radius: 2px;
box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
background: rgba(255, 255, 255, 0.68);
}
&::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
-webkit-border-radius: 2px;
background: transparent;
}
}
.listScroll {
overflow: hidden;
}
.listScroll.scroll{
overflow-y: auto;
@include scrollStyle(4px);
}
.hover {
overflow-y: auto;
}
.hide {
display: none;
}
</style>

View File

@ -0,0 +1,110 @@
<template>
<el-container>
<el-header class="admin-header d-flex align-items-center is-justify-space-between">
<div class="logo-box d-flex align-items-center">
<img src="~@/assets/img/logo.png" width="36" height="31" alt="">
<span class="logo-text">智慧消防预警系统</span>
</div>
<div class="d-flex align-items-center">
<el-link @click="gotoView"></el-link>
<img style="margin-left: 20px" width="28" height="28" src="~@/assets/img/default-icon.png" alt="">
</div>
</el-header>
<el-container>
<el-aside width="200px" class="nav-side">
<el-menu
router
default-active="/tjfx"
class="el-menu-vertical-demo"
@open="handleOpen"
@close="handleClose"
background-color="#545c64"
text-color="#fff"
active-text-color="#FFFFFF">
<el-menu-item v-for="item in menuList" :index="item.path">
<i class="el-icon-menu"></i>
<span slot="title">{{item.label}}</span>
</el-menu-item>
</el-menu>
</el-aside>
<el-main class="main-box">
<router-view></router-view>
</el-main>
</el-container>
</el-container>
</template>
<script lang="ts">
import {Component, Emit, Prop, PropSync, Vue} from 'vue-property-decorator';
@Component({
components: {
},
})
export default class AdminComponent extends Vue {
menuList = [
{label:'报警统计与分析',path:'/tjfx'},
{label:'监控设备信息',path:'/jk'},
{label:'值班计划',path:'/duty'},
{label:'消防车辆管理',path:'/car'},
{label:'消防物资管理',path:'/goods'},
{label:'消防设备与设施',path:'/eq'},
]
created() {
//
}
handleOpen(key, keyPath) {
console.log(key, keyPath);
}
handleClose(key, keyPath) {
console.log(key, keyPath);
}
gotoView(){
this.$router.push('/home')
}
mounted() {
}
destroyed() {
}
}
</script>
<style lang="scss" scoped>
.admin-header{
width: 100%;
height: 52px;
background: #FFFFFF;
box-shadow: 0px -5px 20px -10px rgba(51,73,94,0.3);
color: #000000;
box-sizing: border-box;
padding-left: 30px;
padding-right: 30px;
.logo-text{
color: #000000;
font-size: 20px;
font-weight: bold;
margin-left: 11px;
}
}
.nav-side{
height: calc(100vh - 60px);
background-color: rgb(84, 92, 100);
}
.main-box{
padding: 8px;
background: #ECEDEE;
}
::v-deep{
.el-menu-item{
width: 200px;
}
.el-menu-item.is-active{
background: #409EFF !important;
color: #FFFFFF;
}
}
</style>

View File

@ -0,0 +1,125 @@
<template>
<div>
<div class="admin-card-box search-area d-flex align-items-center is-justify-space-between">
<el-form :inline="true" :model="formInline">
<el-form-item label="计划名称">
<el-input v-model="formInline.cameraName" placeholder="请输入计划名称"></el-input>
</el-form-item>
<el-form-item label="计划时间">
<el-date-picker
v-model="formInline.name"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期">
</el-date-picker>
</el-form-item>
<el-form-item label="计划类型">
<el-select v-model="formInline.type">
<el-option value="日计划" label="日计划"></el-option>
<el-option value="周计划" label="周计划"></el-option>
<el-option value="月度计划" label="月度计划"></el-option>
</el-select>
</el-form-item>
</el-form>
<div class="d-flex align-items-center" style="margin-bottom: 22px">
<el-button type="primary" icon="el-icon-search">查询</el-button>
<el-button class="margin-left-20" icon="el-icon-refresh">清空</el-button>
</div>
</div>
<div class="admin-card-box margin-top-16">
<div class="d-flex align-items-center flex-end">
<el-button type="primary" icon="el-icon-plus">添加</el-button>
<el-button type="danger" plain icon="el-icon-delete">批量删除</el-button>
<el-button icon="el-icon-edit-outline">批量修改</el-button>
<el-button icon="el-icon-upload2">导入</el-button>
<el-button icon="el-icon-download">导出</el-button>
</div>
<el-table
class="margin-top-20"
:data="tableData"
border
style="width: 100%">
<el-table-column
type="index"
label="序号"
width="60"
>
</el-table-column>
<el-table-column
prop="name"
label="计划名称"
>
</el-table-column>
<el-table-column
prop="startTime"
label="开始时间"
>
</el-table-column>
<el-table-column
prop="endTime"
label="结束时间">
</el-table-column>
<el-table-column
prop="type"
label="计划类型">
</el-table-column>
<el-table-column
prop="area"
label="人员范围">
</el-table-column>
<el-table-column
prop="position"
label="值守地点">
</el-table-column>
<el-table-column
prop="leader"
label="带班领导">
</el-table-column>
<el-table-column
fixed="right"
label="操作"
>
<template slot-scope="scope">
<el-button type="text" size="small">查看</el-button>
<el-button type="text" size="small">编辑</el-button>
<el-button type="text" size="small">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="margin-top-16" style="text-align: center">
<el-pagination
background
layout="total, prev, pager, next"
:total="9">
</el-pagination>
</div>
</div>
</div>
</template>
<script lang="ts">
import {Component, Emit, Prop, PropSync, Vue} from 'vue-property-decorator';
import {dutyDataList} from '../morkData'
@Component({
components: {
},
})
export default class DutyComponent extends Vue {
formInline:any = {}
tableData = dutyDataList
mounted() {
}
destroyed() {
}
}
</script>
<style lang="scss" scoped>
.search-area{
padding-bottom: 0;
}
</style>

View File

@ -0,0 +1,128 @@
<template>
<div>
<div class="admin-card-box search-area d-flex align-items-center is-justify-space-between">
<el-form :inline="true" :model="formInline">
<el-form-item label="摄像头名称">
<el-input v-model="formInline.cameraName" placeholder="请输入摄像头名称"></el-input>
</el-form-item>
<el-form-item label="摄像头编码">
<el-input v-model="formInline.cameraCode" placeholder="请输入摄像头编码"></el-input>
</el-form-item>
<el-form-item label="企业名称">
<el-input v-model="formInline.companyName" placeholder="请输入企业名称"></el-input>
</el-form-item>
<el-form-item label="摄像头类型">
<el-select v-model="formInline.cameraType">
<el-option value="简型摄像头" label="简型摄像头"></el-option>
<el-option value="枪机" label="枪机"></el-option>
<el-option value="云台" label="云台"></el-option>
<el-option value="球机" label="球机"></el-option>
</el-select>
</el-form-item>
</el-form>
<div class="d-flex align-items-center" style="margin-bottom: 22px">
<el-button type="primary" icon="el-icon-search">查询</el-button>
<el-button class="margin-left-20" icon="el-icon-refresh">清空</el-button>
</div>
</div>
<div class="admin-card-box margin-top-16">
<div class="d-flex align-items-center flex-end">
<el-button type="primary" icon="el-icon-plus">添加</el-button>
<el-button type="danger" plain icon="el-icon-delete">批量删除</el-button>
<el-button icon="el-icon-edit-outline">批量修改</el-button>
<el-button icon="el-icon-upload2">导入</el-button>
<el-button icon="el-icon-download">导出</el-button>
</div>
<el-table
class="margin-top-20"
:data="tableData"
border
style="width: 100%">
<el-table-column
type="index"
label="序号"
width="60"
>
</el-table-column>
<el-table-column
prop="cameraName"
label="摄像头名称"
>
</el-table-column>
<el-table-column
prop="companyName"
label="企业名称"
>
</el-table-column>
<el-table-column
prop="cameraCode"
label="摄像头编码">
</el-table-column>
<el-table-column
prop="position"
label="安装位置">
</el-table-column>
<el-table-column
prop="monitorObject"
label="监控对象">
</el-table-column>
<el-table-column
prop="cameraType"
label="摄像头类型">
</el-table-column>
<el-table-column
prop="status"
label="状态">
<template slot-scope="scope">
<span :class="scope.row.status?'green-text':'red-text'">
{{scope.row.status?'在线':'离线'}}
</span>
</template>
</el-table-column>
<el-table-column
fixed="right"
label="操作"
>
<template slot-scope="scope">
<el-button type="text" size="small">查看</el-button>
<el-button type="text" size="small">编辑</el-button>
<el-button type="text" size="small">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="margin-top-16" style="text-align: center">
<el-pagination
background
layout="total, prev, pager, next"
:total="8">
</el-pagination>
</div>
</div>
</div>
</template>
<script lang="ts">
import {Component, Emit, Prop, PropSync, Vue} from 'vue-property-decorator';
import {jkDataList} from '../morkData'
@Component({
components: {
},
})
export default class JkComponent extends Vue {
formInline:any = {}
tableData = jkDataList
mounted() {
}
destroyed() {
}
}
</script>
<style lang="scss" scoped>
.search-area{
padding-bottom: 0;
}
</style>

View File

@ -0,0 +1,160 @@
<template>
<div class="d-flex flex-direction-column">
<div class="d-flex flex-0 height-auto">
<div class="admin-card-box flex-1">
<div class="title d-flex align-items-center">
<div class="d-flex align-items-center">
<img width="20" height="20" src="~@/assets/icons/png/admin-title-icon.png" alt="">
<span class="admin-title-text">报警类型分析</span>
</div>
</div>
<div class="margin-top-16">
<el-date-picker
v-model="boxTime1"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期">
</el-date-picker>
</div>
<hbt-echarts className="charts-box" :options="pieOpt" @onChartInit="getEcharts($event,'pie')"></hbt-echarts>
</div>
<div class="admin-card-box flex-1" style="margin-left: 8px;margin-right: 8px">
<div class="title d-flex align-items-center is-justify-space-between">
<div class="d-flex align-items-center">
<img width="20" height="20" src="~@/assets/icons/png/admin-title-icon.png" alt="">
<span class="admin-title-text">处置分析</span>
</div>
<div>
<el-select v-model="fxVal">
<el-option label="烟雾分析" value="烟雾分析"></el-option>
</el-select>
</div>
</div>
<hbt-echarts class="margin-top-16" className="charts-box" :options="lineOpt" @onChartInit="getEcharts($event,'line')"></hbt-echarts>
</div>
<div class="admin-card-box flex-1">
<div class="title d-flex align-items-center is-justify-space-between">
<div class="d-flex align-items-center">
<img width="20" height="20" src="~@/assets/icons/png/admin-title-icon.png" alt="">
<span class="admin-title-text">环比对比分析</span>
</div>
<div>
<el-select v-model="hbVal">
<el-option label="月" value="月"></el-option>
<el-option label="周" value="周"></el-option>
<el-option label="日" value="日"></el-option>
</el-select>
</div>
</div>
<hbt-echarts class="margin-top-16" className="charts-box" :options="barOpt" @onChartInit="getEcharts($event,'bar')"></hbt-echarts>
</div>
</div>
<div class="admin-card-box d-flex flex-direction-column margin-top-16 flex-1" style="min-height: calc(100vh - 530px)">
<div class="title d-flex align-items-center is-justify-space-between">
<div class="d-flex align-items-center">
<img width="20" height="20" src="~@/assets/icons/png/admin-title-icon.png" alt="">
<span class="admin-title-text">报警历史趋势分析</span>
</div>
</div>
<hbt-echarts class="margin-top-16 flex-1" style="min-height: calc(100vh - 630px)" className="charts-box" :options="linesOpt" @onChartInit="getEcharts($event,'lines')"></hbt-echarts>
</div>
</div>
</template>
<script lang="ts">
import {Component, Emit, Prop, PropSync, Vue} from 'vue-property-decorator';
import {pieData,lineData,barData,linesData} from './chartData.js'
@Component({
components: {
},
})
export default class TjfxComponent extends Vue {
//
pie:any = {}
line:any = {}
boxTime1:any = ''
fxVal:any = '烟雾分析'
hbVal:any = '月'
created() {
//
}
handleOpen(key, keyPath) {
console.log(key, keyPath);
}
handleClose(key, keyPath) {
console.log(key, keyPath);
}
pieOpt = {}
lineOpt = {}
barOpt = {}
linesOpt = {}
getEcharts(e,type){
this[type] = e
switch (type) {
case 'pie':
this.pieOpt = pieData;
break;
case 'line':
this.lineOpt = lineData;
break;
case 'bar':
this.barOpt = barData;
break;
case 'lines':
this.linesOpt = linesData;
break;
}
}
mounted() {
}
destroyed() {
}
}
</script>
<style lang="scss" scoped>
.admin-header{
width: 100%;
height: 52px;
background: #FFFFFF;
box-shadow: 0px -5px 20px -10px rgba(51,73,94,0.3);
color: #000000;
box-sizing: border-box;
padding-left: 30px;
padding-right: 30px;
.logo-text{
color: #000000;
font-size: 20px;
font-weight: bold;
margin-left: 11px;
}
}
.nav-side{
height: calc(100vh - 60px);
background-color: rgb(84, 92, 100);
}
.main-box{
padding: 8px;
background: #ECEDEE;
}
.charts-box{
height: 300px;
}
::v-deep{
.el-menu-item{
width: 200px;
}
.el-menu-item.is-active{
background: #409EFF !important;
color: #FFFFFF;
}
}
</style>

View File

@ -0,0 +1,691 @@
<template>
<div class="app">
<h2>分片上传示例</h2>
<el-upload
ref="upload"
class="upload-demo"
action="http://127.0.0.1:8088/file/upload"
:on-remove="handleRemove"
:on-change="handleFileChange"
:file-list="uploadFileList"
:show-file-list="false"
:auto-upload="false"
multiple
>
<template #trigger>
<el-button type="primary" plain>选择文件</el-button>
</template>
<el-button style="margin-left: 10px;" type="success" plain @click="handler"></el-button>
<el-button type="danger" plain @click="clearFileHandler"></el-button>
</el-upload>
<!-- 文件列表 -->
<div class="file-list-wrapper">
<el-collapse>
<el-collapse-item v-for="(item, index) in uploadFileList" :key="index">
<template #title>
<div class="upload-file-item">
<div class="file-info-item file-name" :title="item.name">{{ item.name }}</div>
<div class="file-info-item file-size">{{ transformByte(item.size) }}</div>
<div class="file-info-item file-progress">
<span class="file-progress-label"/>
<el-progress :percentage="item.uploadProgress" class="file-progress-value"/>
</div>
<div class="file-info-item file-size"><span/>
<el-tag v-if="item.status === '等待上传'" size="small" type="info"></el-tag>
<el-tag v-else-if="item.status === 'md5'" size="small" type="warning">校验MD5</el-tag>
<el-tag v-else-if="item.status === ''" size="small">正在上传</el-tag>
<el-tag v-else-if="item.status === ''" size="small" type="success">上传完成</el-tag>
<el-tag v-else size="small" type="danger">上传错误</el-tag>
</div>
</div>
</template>
<div class="file-chunk-list-wrapper">
<!-- 分片列表 -->
<el-table :data="item.chunkList" max-height="400" style="width: 100%">
<el-table-column prop="chunkNumber" label="分片序号" width="180"/>
<el-table-column prop="progress" label="上传进度">
<template #default="{ row }">
<el-progress
v-if="!row.status || row.progressStatus === 'normal'"
:percentage="row.progress"
/>
<el-progress
v-else
:percentage="row.progress"
:status="row.progressStatus"
:text-inside="true"
:stroke-width="16"
/>
</template>
</el-table-column>
<el-table-column prop="status" label="状态" width="180"/>
</el-table>
</div>
</el-collapse-item>
</el-collapse>
</div>
</div>
</template>
<script>
import SparkMD5 from 'spark-md5'
import { check, init, merge, getId } from '@/api/upload'
import axios from 'axios'
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
export default {
name: 'MultipartUpload',
setup() {
// file_upload_id
const FILE_UPLOAD_ID_KEY = 'file_upload_id'
//
const chunkSize = 5 * 1024 * 1024
//
let currentFileIndex = 0
//
const FileStatus = {
wait: '等待上传',
getMd5: '校验md5',
chip: '正在创建序列',
uploading: '正在上传',
success: '上传成功',
error: '上传错误'
}
//
const simultaneousUploads = ref(3)
const uploadIdInfo = ref(null)
const uploadFileList = ref([])
const imgDataUrl = ref('')
const transformByte = (size) => {
if (!size) {
return '0B'
}
const unitSize = 1024
if (size < unitSize) {
return size + ' B'
}
// KB
if (size < Math.pow(unitSize, 2)) {
return (size / unitSize).toFixed(2) + ' K'
}
// MB
if (size < Math.pow(unitSize, 3)) {
return (size / Math.pow(unitSize, 2)).toFixed(2) + ' MB'
}
// GB
if (size < Math.pow(unitSize, 4)) {
return (size / Math.pow(unitSize, 3)).toFixed(2) + ' GB'
}
// TB
return (size / Math.pow(unitSize, 4)).toFixed(2) + ' TB'
}
/**
* 开始上传文件
*/
const handler = () => {
//
if (uploadFileList.value.length === 0) {
ElMessage.error('请先选择文件')
return
}
//
const currentFile = uploadFileList.value[currentFileIndex]
//
if (currentFile) {
currentFile.status = FileStatus.getMd5
}
//
// ScreenshotVideo(currentFile.raw);
// MD5
if (!currentFile) return
getFileMd5(currentFile.raw, async(md5, totalChunks) => {
// upLoadId
const uploadResult = await getupLoadId({})
//
currentFile.status = FileStatus.chip
//
const fileChunks = createFileChunk(currentFile.raw, chunkSize)
//
const fileName = currentFile.name
//
const type = currentFile.name.substring(currentFile.name.lastIndexOf('.') + 1)
// let type = fileSuffixTypeUtil(currentFile.name)
const param = {
id: uploadResult.data,
originalName: fileName,
applicationName: fileName,
fileSize: currentFile.size,
chunkSize: chunkSize,
size: chunkSize,
partCount: totalChunks,
contentType: 'application/octet-stream',
fileType: type
}
// url
uploadIdInfo.value = await getFileUploadUrls(param)
saveFileUploadId(uploadIdInfo.value.data.uploadId)
const uploadUrls = uploadIdInfo.value.data.urlList
currentFile.chunkList = []
// $set(currentFile, 'chunkList', [])
if (uploadUrls !== undefined) {
if (fileChunks.length !== uploadUrls.length) {
ElMessage.error('文件分片上传地址获取错误')
return
}
}
fileChunks.map((chunkItem, index) => {
currentFile.chunkList.push({
chunkNumber: index + 1,
chunk: chunkItem,
uploadUrl: uploadUrls[index],
progress: 0,
status: '—'
})
})
let tempFileChunks = []
currentFile.chunkList.forEach((item) => {
tempFileChunks.push(item)
})
//
currentFile.status = FileStatus.uploading
//
await uploadChunkBase(tempFileChunks)
//
tempFileChunks = processUploadChunkList(tempFileChunks)
//
if (uploadIdInfo.value.uploadId === 'SingleFileUpload') {
//
currentFile.status = FileStatus.success
} else {
getVideoTime(currentFile.raw, async(duration) => {
//
const mergeResult = await mergeFile({
uploadId: uploadIdInfo.value.data.uploadId,
extractName: uploadIdInfo.value.data.extractName,
originalName: fileName,
name: fileName,
id: uploadResult.data,
type: type,
duration
})
//
if (!mergeResult) {
currentFile.status = FileStatus.error
ElMessage.error(mergeResult.error)
} else {
currentFile.status = FileStatus.success
console.log('文件访问地址:' + mergeResult)
ElMessage.success(`上传成功,文件地址:${mergeResult}`)
//
currentFileIndex++
//
handler()
}
})
}
// TODO
})
}
//
const getVideoTime = (file, fn) => {
const url = URL.createObjectURL(file)
const video = document.createElement('video')
video.src = url
video.addEventListener('loadedmetadata', function() {
console.log('视频长度(秒): ', this.duration)
fn(secToHHMMSS(this.duration))
URL.revokeObjectURL(url)
video.remove()
})
}
const secToHHMMSS = (time) => {
let hours = Math.floor(time / 3600)
let minutes = Math.floor((time - (hours * 3600)) / 60)
let seconds = Math.floor(time - (hours * 3600) - (minutes * 60))
hours = (hours < 10) ? '0' + hours : hours
minutes = (minutes < 10) ? '0' + minutes : minutes
seconds = (seconds < 10) ? '0' + seconds : seconds
return hours + ':' + minutes + ':' + seconds
}
/**
* 清空列表
*/
const clearFileHandler = () => {
uploadFileList.value = []
uploadIdInfo.value = null
currentFileIndex = 0
}
/**
* 上传文件列表
* @param {*} file
* @param {*} fileList
*/
const handleFileChange = (file, fileList) => {
// if (!beforeUploadVideo(file)) return
uploadFileList.value = fileList
uploadFileList.value.forEach((item) => {
//
initFileProperties(item)
})
}
/**
* 初始化文件属性
* @param file
*/
const initFileProperties = (file) => {
file.chunkList = []
file.status = FileStatus.wait
file.progressStatus = 'warning'
file.uploadProgress = 0
}
/**
* 移除文件列表
* @param {*} file
* @param {*} fileList
*/
const handleRemove = (file, fileList) => {
uploadFileList.value = fileList
}
/**
* 检查上传文件格式
* @param {*} file
*/
const beforeUploadVideo = (file) => {
const type = file.name.substring(file.name.lastIndexOf('.') + 1)
if (['mp4', 'ogg', 'flv', 'avi', 'wmv', 'rmvb'].indexOf(type) === -1) {
ElMessage.error('请上传正确的视频格式')
return false
}
}
/**
* 获取文件新名称
* @param file
* @param md5
* @returns {*}
*/
const getNewFileName = (file, md5) => {
return new Date().getTime() + file.name
// return md5 + '-' + file.name
}
/**
* 分片读取文件 MD5
*/
const getFileMd5 = (file, callback) => {
const blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice
const fileReader = new FileReader()
//
const totalChunks = Math.ceil(file.size / chunkSize)
console.log('总分片数:' + totalChunks)
let currentChunk = 0
const spark = new SparkMD5.ArrayBuffer()
loadNext()
fileReader.onload = function(e) {
try {
spark.append(e.target.result)
} catch (error) {
console.log('获取Md5错误' + currentChunk)
}
if (currentChunk < totalChunks) {
currentChunk++
loadNext()
} else {
callback(spark.end(), totalChunks)
}
}
fileReader.onerror = function() {
console.warn('读取Md5失败文件读取错误')
}
function loadNext() {
const start = currentChunk * chunkSize
const end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize
// fileRaw
fileReader.readAsArrayBuffer(blobSlice.call(file, start, end))
}
}
/**
* 文件分片
* @param file
* @param size
* @returns {*[]}
*/
const createFileChunk = (file, size = chunkSize) => {
const fileChunkList = []
let count = 0
while (count < file.size) {
fileChunkList.push({
file: file.slice(count, count + size)
})
count += size
}
return fileChunkList
}
/**
* 处理即将上传的分片列表判断是否有已上传的分片有则从列表中删除
* @param chunkList
* @returns {*}
*/
const processUploadChunkList = (chunkList) => {
const currentFile = uploadFileList.value[currentFileIndex]
const chunkUploadedList = currentFile.chunkUploadedList
if (chunkUploadedList === undefined || chunkUploadedList === null || chunkUploadedList.length === 0) {
return chunkList
}
for (let i = chunkList.length - 1; i >= 0; i--) {
const chunkItem = chunkList[currentFileIndex]
for (let j = 0; j < chunkUploadedList.length; j++) {
if (chunkItem.chunkNumber === chunkUploadedList[j]) {
chunkList.splice(i, 1)
break
}
}
}
return chunkList
}
/**
* 上传分片文件
* @param chunkList
* @returns {Promise<unknown>}
*/
const uploadChunkBase = (chunkList) => {
let successCount = 0
const totalChunks = chunkList.length
return new Promise((resolve, reject) => {
const handler = () => {
if (chunkList.length) {
const chunkItem = chunkList.shift()
chunkItem.uploadUrl = chunkItem.uploadUrl.replace('http://146.56.192.100:10000', 'https://minio.hanbangtang.com')
// FormData
axios.put(chunkItem.uploadUrl, chunkItem.chunk.file, {
//
onUploadProgress: checkChunkUploadProgress(chunkItem),
headers: {
'Content-Type': 'application/octet-stream'
}
}).then(response => {
if (response.status === 200) {
console.log('分片:' + chunkItem.chunkNumber + ' 上传成功')
// 1退
// if (chunkList.length === 1) {
// return;
// }
successCount++
//
handler()
} else {
console.log('上传失败:' + response.status + '' + response.statusText)
}
}).catch(error => {
//
console.log('分片:' + chunkItem.chunkNumber + ' 上传失败,' + error)
//
chunkList.push(chunkItem)
handler()
})
}
if (successCount >= totalChunks) {
resolve()
}
}
//
for (let i = 0; i < simultaneousUploads.value; i++) {
handler()
}
})
}
/**
* 获取文件上传地址
* @param fileParam
* @returns {Promise<axios.AxiosResponse<any>>}
*/
const getFileUploadUrls = (fileParam) => {
return init(fileParam)
}
/**
* 保存文件上传id
* @param data
*/
const saveFileUploadId = (data) => {
localStorage.setItem(FILE_UPLOAD_ID_KEY, data)
}
/**
* 上传校验
* @param md5
* @returns {Promise<unknown>}
*/
const checkFileUploadedByMd5 = (md5) => {
return new Promise((resolve, reject) => {
check(md5).then(response => {
console.log(response)
resolve(response)
}).catch(error => {
reject(error)
})
})
}
/**
* 上传校验
* @param md5
* @returns {Promise<unknown>}
*/
const getupLoadId = (data) => {
return new Promise((resolve, reject) => {
getId({}).then(response => {
console.log(response)
resolve(response)
}).catch(error => {
reject(error)
})
})
}
/**
* 合并分片数据
* @param fileParam
* @returns {Promise<unknown>}
*/
const mergeFile = (fileParam) => {
return new Promise((resolve, reject) => {
const formData = new FormData()
formData.append('id', fileParam.id)
formData.append('extractName', fileParam.extractName)
formData.append('uploadId', fileParam.uploadId)
formData.append('duration', fileParam.duration)
merge(formData).then(response => {
console.log(response)
const data = response
if (!data) {
// data.msg = FileStatus.error
resolve(data)
} else {
// data.msg = FileStatus.success
resolve(data)
}
})
// .catch(error => {
// $message.error('' + error)
// file.status = FileStatus.error
// reject()
// })
})
}
/**
* 检查分片上传进度
* @param item
* @returns {(function(*): void)|*}
*/
const checkChunkUploadProgress = (item) => {
return p => {
item.progress = parseInt(String((p.loaded / p.total) * 100))
updateChunkUploadStatus(item)
}
}
/**
* 更新上传状态
* @param item
*/
const updateChunkUploadStatus = (item) => {
let status = FileStatus.uploading
let progressStatus = 'normal'
if (item.progress >= 100) {
status = FileStatus.success
progressStatus = 'success'
}
const chunkIndex = item.chunkNumber - 1
const currentChunk = uploadFileList.value[currentFileIndex].chunkList[chunkIndex]
//
currentChunk.status = status
currentChunk.progressStatus = progressStatus
//
// $set(uploadFileList[currentFileIndex].chunkList, chunkIndex, currentChunk)
uploadFileList.value[currentFileIndex].chunkList.splice(chunkIndex, 1, currentChunk)
//
getCurrentFileProgress()
}
/**
* 获取文件上传进度
*/
const getCurrentFileProgress = () => {
const currentFile = uploadFileList.value[currentFileIndex]
if (!currentFile || !currentFile.chunkList) {
return
}
const chunkList = currentFile.chunkList
const uploadedSize = chunkList.map((item) => item.chunk.file.size * item.progress).reduce((acc, cur) => acc + cur)
// /
currentFile.uploadProgress = parseInt((uploadedSize / currentFile.size).toFixed(2))
// $set(uploadFileList.value, currentFile)
}
return {
uploadIdInfo,
uploadFileList,
imgDataUrl,
transformByte,
handler,
clearFileHandler,
handleFileChange,
initFileProperties,
handleRemove,
beforeUploadVideo,
getNewFileName,
getFileMd5,
createFileChunk,
processUploadChunkList,
uploadChunkBase,
getFileUploadUrls,
saveFileUploadId,
checkFileUploadedByMd5,
mergeFile,
checkChunkUploadProgress,
updateChunkUploadStatus,
getCurrentFileProgress
}
}
}
</script>
<style scoped lang="less">
.container {
width: 600px;
margin: 0 auto;
}
.file-list-wrapper {
margin-top: 20px;
}
h2 {
text-align: center;
}
.file-info-item {
margin: 0 10px;
}
.upload-file-item {
display: flex;
}
.file-progress {
display: flex;
align-items: center;
}
.file-progress-value {
width: 150px;
}
.file-name {
width: 190px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.file-size {
width: 100px;
}
.uploader-example {
width: 880px;
padding: 15px;
margin: 40px auto 0;
font-size: 12px;
box-shadow: 0 0 10px rgba(0, 0, 0, .4);
}
.uploader-example .uploader-btn {
margin-right: 4px;
}
.uploader-example .uploader-list {
max-height: 440px;
overflow: auto;
overflow-x: hidden;
overflow-y: auto;
}
</style>