Commit fb9b097c authored by lei's avatar lei

演示大屏使用修改

parent 88a585c6
...@@ -7,7 +7,7 @@ ENV = 'development' ...@@ -7,7 +7,7 @@ ENV = 'development'
# 若依管理系统/开发环境 # 若依管理系统/开发环境
VUE_APP_BASE_API = '/dev-api' VUE_APP_BASE_API = '/dev-api'
VUE_APP_WS_URL = 'ws://192.168.2.16:8081' VUE_APP_WS_URL = 'ws://192.168.2.16:8080'
# 路由懒加载 # 路由懒加载
VUE_CLI_BABEL_TRANSPILE_MODULES = true VUE_CLI_BABEL_TRANSPILE_MODULES = true
...@@ -22,3 +22,4 @@ selenium-debug.log ...@@ -22,3 +22,4 @@ selenium-debug.log
package-lock.json package-lock.json
yarn.lock yarn.lock
public/video/webrtc-streamer.exe public/video/webrtc-streamer.exe
*.zip
...@@ -43,6 +43,7 @@ ...@@ -43,6 +43,7 @@
"echarts": "5.4.0", "echarts": "5.4.0",
"element-ui": "2.15.14", "element-ui": "2.15.14",
"file-saver": "2.0.5", "file-saver": "2.0.5",
"flv.js": "^1.6.2",
"fuse.js": "6.4.3", "fuse.js": "6.4.3",
"highlight.js": "9.18.5", "highlight.js": "9.18.5",
"html2canvas": "^1.4.1", "html2canvas": "^1.4.1",
...@@ -61,6 +62,7 @@ ...@@ -61,6 +62,7 @@
"vue": "2.6.12", "vue": "2.6.12",
"vue-count-to": "1.0.13", "vue-count-to": "1.0.13",
"vue-cropper": "0.5.5", "vue-cropper": "0.5.5",
"vue-flv-player": "^1.0.3",
"vue-meta": "2.4.0", "vue-meta": "2.4.0",
"vue-router": "3.4.9", "vue-router": "3.4.9",
"vuedraggable": "2.24.3", "vuedraggable": "2.24.3",
......
...@@ -73,6 +73,7 @@ ...@@ -73,6 +73,7 @@
v-model="selectVidoe" v-model="selectVidoe"
@change="handleCascaderChange" @change="handleCascaderChange"
:options="videoList" :options="videoList"
placeholder="二楼办公室左后"
:props="{ :props="{
value: 'value', value: 'value',
label: 'label', label: 'label',
...@@ -88,15 +89,16 @@ ...@@ -88,15 +89,16 @@
class="video-box" class="video-box"
></VideoPlay> --> ></VideoPlay> -->
<div class="video-box" ref="videoContainer"> <div class="video-box" ref="videoContainer">
<iframe <video
:src="iframeSrc" id="videoPlayer"
:style="{ ref="videoPlayer"
width: width + 'px', class=""
height: height + 'px', style="width: 100%; height: 100%"
border: 'none', autoplay
}" muted
ref="analysisIframe" controls
></iframe> preload="auto"
></video>
</div> </div>
</div> </div>
<div class="content-bottom"> <div class="content-bottom">
...@@ -176,6 +178,8 @@ import { ...@@ -176,6 +178,8 @@ import {
getVideoPlayUrlWithAlgorithm, getVideoPlayUrlWithAlgorithm,
} from "@/api/business/home.js"; } from "@/api/business/home.js";
import { getAlarmLogList } from "@/api/business/alarmlog.js"; import { getAlarmLogList } from "@/api/business/alarmlog.js";
// 播放FLV视频流
import flvjs from "flv.js";
export default { export default {
dicts: ["algorithm_level"], dicts: ["algorithm_level"],
name: "Screen", name: "Screen",
...@@ -227,22 +231,7 @@ export default { ...@@ -227,22 +231,7 @@ export default {
}, },
mounted() { mounted() {
this.$nextTick(() => { this.$nextTick(() => {
// 初始化观察器 this.videoPlayer();
this.resizeObserver = new ResizeObserver((entries) => {
const { width, height } = entries[0].contentRect;
this.width = width;
this.height = height;
// 传递尺寸到iframe内部(需同源)
this.$refs.analysisIframe.contentWindow.postMessage(
{
type: "resize",
width,
height,
},
"*"
);
});
this.resizeObserver.observe(this.$refs.videoContainer);
}); });
}, },
beforeDestroy() { beforeDestroy() {
...@@ -257,34 +246,6 @@ export default { ...@@ -257,34 +246,6 @@ export default {
await Promise.all([ await Promise.all([
getCameraList().then((res) => { getCameraList().then((res) => {
if (res.code !== 200) return; if (res.code !== 200) return;
// 在数据转换时保留原始字段
this.videoList = res.rows.map((camera) => ({
value: camera.cameraId,
label: camera.cameraName, // 这里保持摄像头名称
children: [
// 新增预览选项
{ value: "preview", label: "原始画面" },
// 原有算法列表
...camera.algorithmVo.map((algorithm) => ({
value: algorithm.algorithmId,
label: algorithm.algorithmName,
})),
],
}));
// 设置默认选中第一个摄像头的第一个算法
if (
this.videoList.length > 0 &&
this.videoList[0].children?.length > 0
) {
this.selectVidoe = [
this.videoList[0].value,
this.videoList[0].children[0].value,
];
this.cameraName = this.videoList[0].label; // 摄像头名称
this.algorithmName = this.videoList[0].children[0].label; // 算法名称
this.iframeSrc = `${this.iframeUrl}/view/${this.videoList[0].value}/${this.videoList[0].children[0].value}`;
}
this.videoShow = true;
}), }),
getCameraOnlineData().then((res) => { getCameraOnlineData().then((res) => {
...@@ -361,6 +322,18 @@ export default { ...@@ -361,6 +322,18 @@ export default {
this.iframeSrc = `${this.iframeUrl}/view/${value[0]}/${value[1]}`; this.iframeSrc = `${this.iframeUrl}/view/${value[0]}/${value[1]}`;
} }
}, },
videoPlayer() {
if (flvjs.isSupported()) {
var videoElement = document.getElementById("videoPlayer");
var flvPlayer = flvjs.createPlayer({
type: "flv",
url: "http://192.168.3.248:2022/live/15_1_.flv", //你的url地址
});
flvPlayer.attachMediaElement(videoElement);
flvPlayer.load();
flvPlayer.play();
}
},
}, },
}; };
</script> </script>
......
...@@ -146,10 +146,10 @@ export default { ...@@ -146,10 +146,10 @@ export default {
}, },
}, },
grid: { grid: {
left: 10, left: 30,
right: 40, right: 30,
bottom: 20, bottom: 20,
top: 30, top: 90,
containLabel: true, containLabel: true,
}, },
tooltip: { tooltip: {
...@@ -166,6 +166,11 @@ export default { ...@@ -166,6 +166,11 @@ export default {
}, },
legend: { legend: {
data: lendata, data: lendata,
//颜色、
textStyle: {
color: "#fff", // 图例文字颜色
},
}, },
series: list, series: list,
}); });
......
...@@ -25,7 +25,10 @@ ...@@ -25,7 +25,10 @@
<h3 class="status-title">视频分析任务启动状态</h3> <h3 class="status-title">视频分析任务启动状态</h3>
<div class="status-group"> <div class="status-group">
<div class="status-item" style="height: 300px"> <div class="status-item" style="height: 300px">
<pie-chart :option="VideoAnalysisOnlineData" v-if="isDataLoaded" /> <pie-chart
:option="VideoAnalysisOnlineData"
v-if="isDataLoaded"
/>
</div> </div>
</div> </div>
</div> </div>
...@@ -39,34 +42,32 @@ ...@@ -39,34 +42,32 @@
</template> </template>
<template v-else> <template v-else>
<span>请选择摄像头和算法</span> <span>请选择摄像头和算法</span>
</template></span> </template></span
<el-cascader v-model="selectVidoe" @change="handleCascaderChange" :options="videoList" :props="{ >
<el-cascader
v-model="selectVidoe"
@change="handleCascaderChange"
:options="videoList"
:props="{
value: 'value', value: 'value',
label: 'label', label: 'label',
children: 'children', children: 'children',
}" clearable></el-cascader> }"
clearable
></el-cascader>
</h3> </h3>
<div class="video-box" ref="videoContainer"> <div class="video-box" ref="videoContainer">
<!-- <iframe
:src="iframeSrc"
:style="{
width: width + 'px',
height: height + 'px',
border: 'none',
}"
ref="analysisIframe"
></iframe> -->
<div class="video-container"> <div class="video-container">
<video id="videoPlayer" ref="videoPlayer" class="video-js vjs-default-skin" autoplay muted controls <video
preload="auto"></video> id="videoPlayer"
<div class="video-placeholder" v-if="!isLoaded"> ref="videoPlayer"
<div class="placeholder-content"> class=""
<i class="el-icon-video-camera"></i> style="width: 100%; height: 100%"
<p>视频加载中...</p> autoplay
<div class="loading-spinner"></div> muted
</div> controls
</div> preload="auto"
></video>
</div> </div>
</div> </div>
</div> </div>
...@@ -77,8 +78,15 @@ ...@@ -77,8 +78,15 @@
<div class="status-container"> <div class="status-container">
<h3 class="status-title both-end"> <h3 class="status-title both-end">
<span>统计分析(条/天)</span> <span>统计分析(条/天)</span>
<el-date-picker v-model="pickerDate" type="daterange" range-separator="至" start-placeholder="开始日期" <el-date-picker
end-placeholder="结束日期" value-format="yyyy-MM-dd" @change="getData"> v-model="pickerDate"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="yyyy-MM-dd"
@change="getData"
>
</el-date-picker> </el-date-picker>
</h3> </h3>
<line-chart :chartData="StatisticsData" v-if="isDataLoaded" /> <line-chart :chartData="StatisticsData" v-if="isDataLoaded" />
...@@ -91,16 +99,28 @@ ...@@ -91,16 +99,28 @@
<h3 class="status-title both-end"> <h3 class="status-title both-end">
<span>实时报警</span> <span>实时报警</span>
</h3> </h3>
<div class="card-box" v-for="item of alarmList" :key="item.logId" @click="lookAlarm(item)"> <div
class="card-box"
v-for="item of alarmList"
:key="item.logId"
@click="lookAlarm(item)"
>
<el-row> <el-row>
<el-col :span="12"> <el-col :span="12">
<dict-tag :options="dict.type.algorithm_level" :value="item.alarmLevel" /> <dict-tag
:options="dict.type.algorithm_level"
:value="item.alarmLevel"
/>
<p>{{ item.algorithmName }}</p> <p>{{ item.algorithmName }}</p>
<p>{{ item.cameraName }}</p> <p>{{ item.cameraName }}</p>
<p>{{ item.alarmTime }}</p> <p>{{ item.alarmTime }}</p>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<image-preview :src="item.alarmImageUrl" :width="170" :height="145" /> <image-preview
:src="item.alarmImageUrl"
:width="170"
:height="145"
/>
</el-col> </el-col>
</el-row> </el-row>
</div> </div>
...@@ -110,23 +130,36 @@ ...@@ -110,23 +130,36 @@
<el-dialog :visible.sync="dialogVisible" width="60%"> <el-dialog :visible.sync="dialogVisible" width="60%">
<div slot="title" class="dialog-title"> <div slot="title" class="dialog-title">
<span> <span>
<dict-tag style="display: inline-block; margin-right: 10px" :options="dict.type.algorithm_level" <dict-tag
:value="currentAlarm.alarmLevel" /> style="display: inline-block; margin-right: 10px"
:options="dict.type.algorithm_level"
:value="currentAlarm.alarmLevel"
/>
<span style="margin-right: 20px">{{ <span style="margin-right: 20px">{{
(currentAlarm && currentAlarm.alarmMessage) || "报警详情" (currentAlarm && currentAlarm.alarmMessage) || "报警详情"
}}</span> }}</span>
<span style="margin-right: 20px"> <span style="margin-right: 20px">
{{ currentAlarm && currentAlarm.cameraPosition }}</span> {{ currentAlarm && currentAlarm.cameraPosition }}</span
>
<span style="margin-right: 20px">{{ <span style="margin-right: 20px">{{
currentAlarm && currentAlarm.alarmTime currentAlarm && currentAlarm.alarmTime
}}</span> }}</span>
</span> </span>
<el-button @click="playBack" :disabled="currentAlarm.videoStatus === 0" type="text" class="right-btn"> <el-button
<i v-if="lookBackVideo === 0" :class="currentAlarm.videoStatus === 1 @click="playBack"
:disabled="currentAlarm.videoStatus === 0"
type="text"
class="right-btn"
>
<i
v-if="lookBackVideo === 0"
:class="
currentAlarm.videoStatus === 1
? 'el-icon-video-play' ? 'el-icon-video-play'
: 'el-icon-loading' : 'el-icon-loading'
"></i> "
></i>
{{ {{
lookBackVideo === 1 lookBackVideo === 1
? "查看图片" ? "查看图片"
...@@ -137,9 +170,18 @@ ...@@ -137,9 +170,18 @@
</el-button> </el-button>
</div> </div>
<div v-if="currentAlarm"> <div v-if="currentAlarm">
<img v-if="lookBackVideo === 0" :src="currentAlarm.alarmImageUrl" style="width: 100%; height: 400px" /> <img
<video v-if="lookBackVideo === 1" :src="currentAlarm.videoBackUrl" style="width: 100%; height: 400px" controls v-if="lookBackVideo === 0"
autoplay></video> :src="currentAlarm.alarmImageUrl"
style="width: 100%; height: 400px"
/>
<video
v-if="lookBackVideo === 1"
:src="currentAlarm.videoBackUrl"
style="width: 100%; height: 400px"
controls
autoplay
></video>
</div> </div>
</el-dialog> </el-dialog>
</div> </div>
...@@ -155,9 +197,8 @@ import { ...@@ -155,9 +197,8 @@ import {
getVideoPlayUrlWithAlgorithm, getVideoPlayUrlWithAlgorithm,
} from "@/api/business/home.js"; } from "@/api/business/home.js";
import { getAlarmLogList } from "@/api/business/alarmlog.js"; import { getAlarmLogList } from "@/api/business/alarmlog.js";
// 播放rtmp视频流 // 播放FLV视频流
import "video.js/dist/video-js.css"; import flvjs from "flv.js";
import videojs from "video.js";
export default { export default {
dicts: ["algorithm_level"], dicts: ["algorithm_level"],
...@@ -210,25 +251,9 @@ export default { ...@@ -210,25 +251,9 @@ export default {
this.getAlarmLog(); this.getAlarmLog();
}, },
mounted() { mounted() {
// this.$nextTick(() => { this.$nextTick(() => {
// // 初始化观察器 this.videoPlayer();
// this.resizeObserver = new ResizeObserver((entries) => { });
// const { width, height } = entries[0].contentRect;
// this.width = width;
// this.height = height;
// // 传递尺寸到iframe内部(需同源)
// this.$refs.analysisIframe.contentWindow.postMessage(
// {
// type: "resize",
// width,
// height,
// },
// "*"
// );
// });
// this.resizeObserver.observe(this.$refs.videoContainer);
// });
this.initVideoPlayer();
}, },
methods: { methods: {
async init() { async init() {
...@@ -340,379 +365,108 @@ export default { ...@@ -340,379 +365,108 @@ export default {
this.iframeSrc = `${this.iframeUrl}/view/${value[0]}/${value[1]}`; this.iframeSrc = `${this.iframeUrl}/view/${value[0]}/${value[1]}`;
} }
}, },
initVideoPlayer() { videoPlayer() {
this.player = videojs(this.$refs.videoPlayer, { if (flvjs.isSupported()) {
controls: true, var videoElement = document.getElementById("videoPlayer");
preload: 'auto', var flvPlayer = flvjs.createPlayer({
fluid: true, type: "flv",
responsive: true, url: "http://192.168.3.248:2022/live/15_1_.flv", //你的url地址
aspectRatio: '16:9',
playbackRates: [0.5, 0.75, 1, 1.25, 1.5, 2],
controlBar: {
children: [
'playToggle',
'volumePanel',
'currentTimeDisplay',
'timeDivider',
'durationDisplay',
'progressControl',
'playbackRateMenuButton',
'qualitySelector',
'fullscreenToggle'
]
},
// 自定义主题
userActions: {
hotkeys: true
}
}); });
flvPlayer.attachMediaElement(videoElement);
// 添加错误处理 flvPlayer.load();
this.player.on('error', (error) => { flvPlayer.play();
console.error('Video player error:', error);
const errorDetails = this.player.error();
console.log('Error details:', errorDetails);
if (errorDetails && errorDetails.code === 4) {
this.$message.error('视频格式不支持或网络连接失败');
} else {
this.$message.error('视频加载失败,请检查网络连接');
} }
});
// 添加加载状态处理
this.player.on('loadstart', () => {
console.log('开始加载视频');
this.isLoaded = false;
this.$refs.videoPlayer.classList.add('loading');
});
this.player.on('loadeddata', () => {
console.log('视频数据加载完成');
this.isLoaded = true;
this.$refs.videoPlayer.classList.remove('loading');
});
// 设置视频源
this.player.src({
src: "proxy-iframe/output.m3u8",
// type: "application/x-mpegURL",
});
}, },
}, },
beforeDestroy() { beforeDestroy() {},
// if (this.resizeObserver) {
// this.resizeObserver.disconnect();
// this.resizeObserver = null; // 重要!解除引用
// }
// 销毁视频播放器
if (this.player) {
this.player.dispose();
this.player = null;
}
},
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.app-container { .app-container {
padding: 24px; padding-right: 0;
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
min-height: 100vh;
.fs30 { .fs30 {
font-size: 38px; font-size: 38px;
} }
.ac { .ac {
text-align: center; text-align: center;
} }
// 响应式设计
@media (max-width: 768px) {
padding: 16px;
.status-container {
padding: 16px;
margin-bottom: 16px;
}
.status-title {
font-size: 16px;
}
.status-item {
padding: 16px;
.count {
font-size: 28px;
}
}
}
.status-container { .status-container {
padding: 24px; padding: 20px;
background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%); background: #f8f9fa;
border-radius: 12px; border-radius: 5px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.08); box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
margin-bottom: 24px; margin-bottom: 20px;
border: 1px solid rgba(255, 255, 255, 0.8);
backdrop-filter: blur(10px);
transition: all 0.3s ease;
&:hover {
transform: translateY(-2px);
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.12);
}
&.h100 { &.h100 {
height: 100vh; height: 100vh;
} }
.status-title { .status-title {
margin: 0 0 24px 0; margin: 0 0 20px 0;
font-size: 18px; font-size: 16px;
font-weight: 600; color: #333;
color: #2c3e50;
position: relative;
padding-left: 16px;
&::before {
content: '';
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 4px;
height: 20px;
background: linear-gradient(135deg, #409eff, #67c23a);
border-radius: 2px;
}
.title-icon { .title-icon {
margin-right: 12px; margin-right: 8px;
font-size: 22px; font-size: 20px;
color: #409eff; color: #409eff;
vertical-align: middle;
} }
} }
.both-end { .both-end {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
} }
.card-box { .card-box {
border-radius: 12px; border-radius: 5px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
width: 100%; width: 100%;
padding: 20px; padding: 15px;
margin-top: 16px; margin-top: 10px;
cursor: pointer; cursor: pointer;
background: rgba(255, 255, 255, 0.9);
border: 1px solid rgba(255, 255, 255, 0.8);
transition: all 0.3s ease;
backdrop-filter: blur(10px);
&:hover {
transform: translateY(-4px);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
background: rgba(255, 255, 255, 1);
} }
p {
margin: 8px 0;
color: #606266;
font-size: 14px;
line-height: 1.5;
}
}
.status-group { .status-group {
display: flex; display: flex;
gap: 20px; gap: 20px;
.status-item { .status-item {
flex: 1; flex: 1;
text-align: center; text-align: center;
padding: 20px;
background: rgba(255, 255, 255, 0.8);
border-radius: 12px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.06);
transition: all 0.3s ease;
border: 1px solid rgba(255, 255, 255, 0.9);
&:hover {
transform: translateY(-4px);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
}
.count { .count {
font-size: 36px; font-size: 32px;
font-weight: 700; font-weight: bold;
display: block; display: block;
margin-bottom: 12px; margin-bottom: 8px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
&.online { &.online {
background: linear-gradient(135deg, #67c23a 0%, #85ce61 100%); color: #67c23a;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
} }
&.offline { &.offline {
background: linear-gradient(135deg, #f56c6c 0%, #f78989 100%); color: #f56c6c;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
} }
} }
.label { .label {
font-size: 15px; font-size: 14px;
color: #606266; color: #666;
font-weight: 500;
.svg-icon { .svg-icon {
vertical-align: middle; vertical-align: middle;
margin-right: 8px; margin-right: 5px;
font-size: 20px; font-size: 18px;
color: #409eff;
} }
} }
} }
} }
.video-box { .video-box {
height: 440px; height: 440px;
width: 100%; width: 100%;
position: relative; position: relative;
flex-shrink: 0; // 禁止弹性收缩 flex-shrink: 0; // 禁止弹性收缩
overflow: hidden; // 添加溢出隐藏 overflow: hidden; // 添加溢出隐藏
border-radius: 8px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
background: #000;
iframe {
width: 100%;
height: 100%;
}
.video-container { .video-container {
position: relative;
width: 100%; width: 100%;
height: 100%; height: 100%;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%);
border-radius: 8px;
overflow: hidden;
// 视频播放器样式
.video-js {
width: 100% !important;
height: 100% !important;
border-radius: 8px;
// 自定义视频播放器主题
.vjs-control-bar {
background: linear-gradient(transparent, rgba(0, 0, 0, 0.7));
border-radius: 0 0 8px 8px;
}
.vjs-progress-control {
.vjs-progress-holder {
height: 4px;
border-radius: 2px;
}
.vjs-play-progress {
background: linear-gradient(90deg, #409eff, #67c23a);
border-radius: 2px;
}
}
.vjs-play-control {
.vjs-icon-placeholder {
color: #409eff;
}
}
.vjs-volume-panel {
.vjs-volume-control {
.vjs-volume-bar {
background: rgba(255, 255, 255, 0.3);
border-radius: 2px;
}
}
}
.vjs-fullscreen-control {
.vjs-icon-placeholder {
color: #409eff;
}
}
}
// 加载状态
&.loading {
.video-js {
opacity: 0.7;
}
} }
} iframe {
.video-placeholder {
position: absolute;
top: 0;
left: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%);
z-index: 10;
border-radius: 8px;
.placeholder-content {
text-align: center;
color: #fff;
i {
font-size: 64px;
margin-bottom: 20px;
display: block;
opacity: 0.8;
animation: pulse 2s infinite;
}
p {
font-size: 16px;
margin: 0 0 20px 0;
font-weight: 300;
letter-spacing: 1px;
}
.loading-spinner {
width: 40px;
height: 40px;
border: 3px solid rgba(255, 255, 255, 0.3);
border-top: 3px solid #409eff;
border-radius: 50%;
animation: spin 1s linear infinite;
margin: 0 auto;
}
} }
}
.loading-overlay, .loading-overlay,
.error-overlay { .error-overlay {
position: absolute; position: absolute;
...@@ -720,56 +474,23 @@ export default { ...@@ -720,56 +474,23 @@ export default {
left: 50%; left: 50%;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
text-align: center; text-align: center;
i { i {
font-size: 40px; font-size: 40px;
} }
&-text { &-text {
font-size: 14px; font-size: 14px;
} }
} }
.error-overlay { .error-overlay {
color: #f56c6c; color: #f56c6c;
} }
} }
// 动画效果
@keyframes pulse {
0% {
transform: scale(1);
opacity: 0.8;
}
50% {
transform: scale(1.1);
opacity: 1;
}
100% {
transform: scale(1);
opacity: 0.8;
} }
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
}
.dialog-title { .dialog-title {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
width: 100%; width: 100%;
.right-btn { .right-btn {
margin-right: 40px; margin-right: 40px;
} }
......
...@@ -40,7 +40,7 @@ module.exports = { ...@@ -40,7 +40,7 @@ module.exports = {
proxy: { proxy: {
// detail: https://cli.vuejs.org/config/#devserver-proxy // detail: https://cli.vuejs.org/config/#devserver-proxy
[process.env.VUE_APP_BASE_API]: { [process.env.VUE_APP_BASE_API]: {
target: `http://192.168.2.16:8081`, target: `http://192.168.2.16:8080`,
changeOrigin: true, changeOrigin: true,
pathRewrite: { pathRewrite: {
['^' + process.env.VUE_APP_BASE_API]: '' ['^' + process.env.VUE_APP_BASE_API]: ''
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment