Merge pull request 'dev' (#186) from dev into master

Reviewed-on: http://git.feashow.cn/clay/tunnel-cloud-web/pulls/186
This commit is contained in:
odjbin
2023-12-24 10:09:36 +00:00
39 changed files with 22112 additions and 876 deletions

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@@ -21,10 +21,10 @@ export const getTunnelBySiteId = (siteId) => {
method: 'get'
})
}
// 风压echarts数据
// 传感器echarts数据
export const getEchartsInfo = (equipmentId) => {
return request({
url: `/tunnel/large/screen/echarts/wind/pressure/${equipmentId}`,
url: `/tunnel/large/screen/echarts/sensor/${equipmentId}`,
method: 'get'
})
}
@@ -34,4 +34,4 @@ export const getBadGasEchartsInfo = (tunnelId) => {
url: `/tunnel/large/screen/echarts/gas/sensor/${tunnelId}`,
method: 'get'
})
}
}

View File

@@ -2,7 +2,7 @@ import request from '@/utils/request.js'
export const getSiteList = (params) => {
return request({
url: '/tunnel/site/screenlist',
url: '/tunnel/site/screen/list',
method: 'get',
params
})

8
src/api/tunnel.js Normal file
View File

@@ -0,0 +1,8 @@
import request from '@/utils/request.js'
export const getTunnelOption = (siteId) => {
return request({
url: `/tunnel/tunnel/option/${siteId}`,
method: 'get'
})
}

44
src/api/tunnelScene.js Normal file
View File

@@ -0,0 +1,44 @@
import request from "@/utils/request.js";
// 设备类型
export const getEquipmentType = () => {
return request({
url: "/tunnel/model/equipment/type/option",
method: "get",
});
};
// 获取设备
export const getEquipment = (tunnelId, typeKey, equipmentIds = "1") => {
return request({
url: "/tunnel/model/equipment/option",
method: "post",
params: {
tunnelId,
typeKey,
equipmentIds,
},
});
};
// 初始化数据
export const initSceneData = (tunnelId) => {
return request({
url: `/tunnel/model/${tunnelId}`,
method: "get",
});
};
// 添加设备
export const saveEquipment = (
constructionLength,
tunnelId,
tunnelThreeConfig
) => {
return request({
url: "/tunnel/model",
method: "put",
data: {
constructionLength,
tunnelId,
tunnelThreeConfig,
},
});
};

42
src/api/user.js Normal file
View File

@@ -0,0 +1,42 @@
import request from '@/utils/request.js'
export const getUser = (params) => {
return request({
url: '/admin/user/tunnel',
method: 'get',
params
})
}
export const getUserDetail = (userId) => {
return request({
url: `/admin/user/info/${userId}`,
method: 'get'
})
}
export const editUser = (data) => {
return request({
url: '/admin/user',
method: 'put',
data
})
}
export const addUser = (data) => {
return request({
url: '/admin/user',
method: 'post',
data
})
}
export const deleteUser = (userId) => {
return request({
url: `/admin/user/${userId}`,
method: 'delete'
})
}
export const getRoleOption = () => {
return request({
url: '/admin/role/option',
method: 'get'
})
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

View File

@@ -12,7 +12,7 @@
-khtml-user-select: none; /*早期浏览器*/
user-select: none;
font-family: "MicrosoftYaHei", "微软雅黑", "Helvetica Neue", Helvetica, Arial,
sans-serif;
sans-serif;
}
body {
@@ -97,6 +97,7 @@ body,
.current-site {
display: flex;
align-items: center;
> span:first-child {
color: #f7b500;
margin-right: 20px;
@@ -486,43 +487,72 @@ body,
background: #072247 !important;
border: 1px solid #0F82AF !important;
}
.el-popper {
max-width: 202px !important;
max-width: 200px !important;
box-sizing: border-box;
}
.el-popper__arrow::before {
display: none;
}
.el-select-dropdown {
width: 200px !important;
.el-select-dropdown{
width: 200px!important;
}
.el-select-dropdown__item {
color: #fff !important;
}
.el-select-dropdown__item.hover {
background-color: #072247 !important;
}
.el-select-dropdown__item:hover {
background-color: #072247 !important;
color: #08B7B8 !important;
}
.el-select-dropdown__item.selected {
color: #08B7B8 !important;
}
.el-select-dropdown__list {
.el-select-dropdown__item:first-child {
.el-select-dropdown__item{
>span{
color: #FFFFFF;
}
}
.el-select .el-input .el-select__caret {
font-size: 35px!important;
}
.el-select-dropdown__item.hover,.el-select-dropdown__item:hover{
background-color: #064B66!important;
width:198px!important;
}
.el-select-dropdown.is-multiple .el-select-dropdown__item.selected{
background-color: #064B66!important;
width:200px!important;
}
.el-select-tags-wrapper{
.el-tag{
padding: 28px 20px!important;
font-size: 35px;
.el-icon{
width: 40px;
height: 40px;
font-size: 40px!important;
}
}
}
.current-site {
.el-select-dropdown {
width: 200px !important;
}
&:hover {
color: #08B7B8 !important;
.el-select-dropdown__item {
color: #fff !important;
}
.el-select-dropdown__item.hover {
background-color: #072247 !important;
}
.el-select-dropdown__item:hover {
background-color: #072247 !important;
color: #08B7B8 !important;
}
.el-select-dropdown__item.selected {
color: #08B7B8 !important;
}
.el-select-dropdown__list {
.el-select-dropdown__item:first-child {
color: #FFFFFF;
&:hover {
color: #08B7B8 !important;
}
}
}
}
@@ -542,8 +572,8 @@ input[type="number"] {
width: 40px !important;
height: 40px !important;
border-radius: 25px !important;
border: 4px solid #05FEFF!important;
background-color: transparent!important;
border: 4px solid #05FEFF !important;
background-color: transparent !important;
}
.el-checkbox__input.is-checked .el-checkbox__inner {
@@ -559,11 +589,11 @@ input[type="number"] {
//border-top: 0;
border-radius: 25px;
top: 6px;
left:6px;
left: 6px;
//left: 12px;
font-size: 30px;
background: #05FEFF;
border-color: transparent!important;
border-color: transparent !important;
}
//取消 确定按钮
@@ -599,3 +629,13 @@ input[type="number"] {
font-size: 38px;
}
}
.el-pagination.is-background .btn-prev:disabled {
background-color: transparent !important;
color: #60DDDE !important;
}
.el-pagination.is-background .btn-next:disabled {
background-color: transparent !important;
color: #60DDDE !important;
}

View File

@@ -3,8 +3,7 @@
<div class="fan-speed">
<img src="@/assets/images/airInfo/fan-v-icon.png" alt=""/>
<div class="fan-info" @click="handleOpenChart">
<div class="input-fan"><span>风速</span><span>进风{{ windSpeed }}m/s</span></div>
<div class="output-fan"><span>出风{{ windSpeed }}m/s</span></div>
<div class="input-fan"><span>风速</span>{{ windSpeed }}m/s</div>
</div>
</div>
<item-info
@@ -17,7 +16,7 @@
@click="handleOpenAirChart(item)"
/>
<div class="digital-tunnel">
<el-dialog v-model="isWindSpeedVisited" :title="'风速监控数据'" width="2175px" :modal="false">
<el-dialog :close-on-click-modal="false" v-model="isWindSpeedVisited" :title="'风速监控数据'" width="2175px" :modal="false">
<div class="left-top-icon"></div>
<div class="right-top-icon"></div>
<div class="chat-dialog">
@@ -36,7 +35,7 @@
</el-dialog>
</div>
<div class="digital-tunnel">
<el-dialog v-model="isAirVisited" :title="airTitle+'监控数据'" width="2175px" :modal="false">
<el-dialog :close-on-click-modal="false" v-model="isAirVisited" :title="airTitle+'监控数据'" width="2175px" :modal="false">
<div class="left-top-icon"></div>
<div class="right-top-icon"></div>
<div class="chat-dialog">
@@ -62,6 +61,7 @@
import ItemInfo from "./childComps/ItemInfo.vue";
import TimeRangeBtn from "@/components/timeRangeBtn/index.vue"
import * as echarts from 'echarts';
import {getEchartsInfo} from "../../../api/largeScreen";
let myEcharts = reactive({});
let myAirEcharts = reactive({});
@@ -70,6 +70,7 @@ const props = defineProps({
airData: Array
});
const windSpeed = ref('')
const windSpeedId = ref(0)
const airTitle = ref('')
const airList = ref([])
const timeList = ref(["年", "月", "日"]);
@@ -92,19 +93,39 @@ watch(() => props.airData, (now) => {
getAirInfo(now.sensorList)
}
}, {deep: true});
const handleOpenChart = () => {
isWindSpeedVisited.value = true
nextTick(() => {
initChart()
const getChartInfo = (equipmentId) => {
getEchartsInfo(equipmentId).then(res => {
if (res?.code === 1000) {
isWindSpeedVisited.value = true
nextTick(() => {
initChart(res.data.dates, res.data.values)
})
}
})
}
const handleOpenAirChart = (item) => {
isAirVisited.value = true
airTitle.value = item.name
nextTick(() => {
initAirChart()
const getGasChartInfo = (equipmentId) => {
getEchartsInfo(equipmentId).then(res => {
if (res?.code === 1000) {
isAirVisited.value = true
nextTick(() => {
initAirChart(res.data.dates, res.data.values)
})
}
})
}
const handleOpenChart = () => {
// isWindSpeedVisited.value = true
getChartInfo(windSpeedId.value)
}
const handleOpenAirChart = (item) => {
// isAirVisited.value = true
airTitle.value = item.name
getGasChartInfo(item.equipmentId)
// nextTick(() => {
// initAirChart()
// })
}
const timeSelect = (index) => {
console.log('选择时间', index)
if (index === 0) {
@@ -146,14 +167,15 @@ const getAirInfo = (now) => {
if (item.equipmentType === "dust" || item.equipmentType === "oxygen" || item.equipmentType === "temperature" || item.equipmentType === "humidness") {
airObj = changeData(item)
airArr.push(airObj)
} else if (item.equipmentType === "windDirection") {
} else if (item.equipmentType === "windSpeed") {
windSpeedId.value = item.equipmentId
windSpeed.value = item.value
}
})
airArr.push(airArr.shift())
airList.value = airArr
}
const initChart = () => {
const initChart = (type, values) => {
//3.初始化container容器
myEcharts = echarts.init(document.getElementById('containerWind'));
//5.传入数据
@@ -191,8 +213,8 @@ const initChart = () => {
let content = `
<div style="background: linear-gradient(180deg, #254062 0%, rgba(20,36,51,0.3) 100%);;border: 2px solid #6087BA;border-radius: 4px;padding: 8px 16px;">
<div style="font-size: 52px;font-family: PingFang SC-Regular, PingFang SC;font-weight: 400;color: #EFEEEE;margin-bottom: 8px;">${params[0].name}</div>
<div style="font-size: 48px;line-height: 48px;font-family: Bebas Neue-Regular, Bebas Neue;font-weight: 400;"><span style="color: #FFFFFF">进风口: </span><span style="background: linear-gradient(180deg, #F5B85F 0%, #FFFFFF 100%);background-clip: text;-webkit-background-clip: text;-webkit-text-fill-color: transparent;">${params[0].value}</span></div>
<div style="font-size: 48px;line-height: 48px;font-family: Bebas Neue-Regular, Bebas Neue;font-weight: 400;margin-top: 30px"><span style="color: #FFFFFF">出风口: </span><span style="background: linear-gradient(180deg, #F5B85F 0%, #FFFFFF 100%);background-clip: text;-webkit-background-clip: text;-webkit-text-fill-color: transparent;">${params[1].value}</span></div>
<div style="font-size: 48px;line-height: 48px;font-family: Bebas Neue-Regular, Bebas Neue;font-weight: 400;"><span style="color: #FFFFFF">进风口: </span><span style="background: linear-gradient(180deg, #F5B85F 0%, #FFFFFF 100%);-webkit-background-clip: text;-webkit-text-fill-color: transparent;">${params[0].value}</span></div>
<div style="font-size: 48px;line-height: 48px;font-family: Bebas Neue-Regular, Bebas Neue;font-weight: 400;margin-top: 30px"><span style="color: #FFFFFF">出风口: </span><span style="background: linear-gradient(180deg, #F5B85F 0%, #FFFFFF 100%);-webkit-background-clip: text;-webkit-text-fill-color: transparent;">${params[1].value}</span></div>
</div>`;
return content;
},
@@ -212,7 +234,8 @@ const initChart = () => {
//X轴
xAxis: {
type: 'category',
data: ['00:00', '01:00', '02:00', '03:00', '04:00', '05:00', '06:00', '07:00', '08:00', '09:00', '10:00', '11:00', '12:00', '13:00', '14:00', '15:00', '16:00', '17:00', '18:00', '19:00', '20:00', '21:00', '22:00', '23:00', '24:00'],
// data: ['00:00', '01:00', '02:00', '03:00', '04:00', '05:00', '06:00', '07:00', '08:00', '09:00', '10:00', '11:00', '12:00', '13:00', '14:00', '15:00', '16:00', '17:00', '18:00', '19:00', '20:00', '21:00', '22:00', '23:00', '24:00'],
data:type,
axisLabel: {
textStyle: {
fontSize: 45,
@@ -233,18 +256,8 @@ const initChart = () => {
//配置项
series: [
{
name: '进风口',
data: [56, 12, 89, 34, 71, 43, 67, 20, 98, 72, 19, 61, 3, 85, 47, 92, 17, 76, 69, 25, 31, 49, 81, 63],
type: 'line',
smooth: true,
symbolSize: 24,
lineStyle: {
width: 5
}
},
{
name: '出风口',
data: [3, 85, 47, 92, 17, 76, 69, 25, 56, 12, 89, 34, 71, 43, 67, 20, 98, 72, 19, 61, 31, 49, 81, 63],
// data: [3, 85, 47, 92, 17, 76, 69, 25, 56, 12, 89, 34, 71, 43, 67, 20, 98, 72, 19, 61, 31, 49, 81, 63],
data:values,
type: 'line',
smooth: true,
symbolSize: 24,
@@ -254,13 +267,14 @@ const initChart = () => {
}
]
}
myEcharts.setOption(option);
//图表大小自适应窗口大小变化
window.onresize = () => {
myEcharts.resize();
}
}
const initAirChart = () => {
const initAirChart = (type, values) => {
//3.初始化container容器
myAirEcharts = echarts.init(document.getElementById('containerAir'));
//5.传入数据
@@ -295,7 +309,7 @@ const initAirChart = () => {
let content = `
<div style="background: linear-gradient(180deg, #254062 0%, rgba(20,36,51,0.3) 100%);;border: 2px solid #6087BA;border-radius: 4px;padding: 8px 16px;">
<div style="font-size: 52px;font-family: PingFang SC-Regular, PingFang SC;font-weight: 400;color: #EFEEEE;margin-bottom: 8px;">${params[0].name}</div>
<div style="font-size: 52px;line-height: 48px;font-family: Bebas Neue-Regular, Bebas Neue;font-weight: 400;"><span style="background: linear-gradient(180deg, #F5B85F 0%, #FFFFFF 100%);background-clip: text;-webkit-background-clip: text;-webkit-text-fill-color: transparent;">${params[0].value}</span></div>
<div style="font-size: 52px;line-height: 48px;font-family: Bebas Neue-Regular, Bebas Neue;font-weight: 400;"><span style="background: linear-gradient(180deg, #F5B85F 0%, #FFFFFF 100%);-webkit-background-clip: text;-webkit-text-fill-color: transparent;">${params[0].value}</span></div>
</div>`;
return content;
},
@@ -315,7 +329,8 @@ const initAirChart = () => {
//X轴
xAxis: {
type: 'category',
data: ['00:00', '01:00', '02:00', '03:00', '04:00', '05:00', '06:00', '07:00', '08:00', '09:00', '10:00', '11:00', '12:00', '13:00', '14:00', '15:00', '16:00', '17:00', '18:00', '19:00', '20:00', '21:00', '22:00', '23:00', '24:00'],
// data: ['00:00', '01:00', '02:00', '03:00', '04:00', '05:00', '06:00', '07:00', '08:00', '09:00', '10:00', '11:00', '12:00', '13:00', '14:00', '15:00', '16:00', '17:00', '18:00', '19:00', '20:00', '21:00', '22:00', '23:00', '24:00'],
data:type,
axisLabel: {
textStyle: {
fontSize: 45,
@@ -336,7 +351,8 @@ const initAirChart = () => {
//配置项
series: [
{
data: [56, 12, 89, 34, 71, 43, 67, 20, 98, 72, 19, 61, 3, 85, 47, 92, 17, 76, 69, 25, 31, 49, 81, 63],
// data: [56, 12, 89, 34, 71, 43, 67, 20, 98, 72, 19, 61, 3, 85, 47, 92, 17, 76, 69, 25, 31, 49, 81, 63],
data:values,
type: 'line',
smooth: true,
symbolSize: 24,
@@ -391,13 +407,14 @@ const initAirChart = () => {
.fan-info {
flex: 1;
display: flex;
justify-content: space-between;
justify-content: flex-start;
.input-fan {
margin: 0 26px;
//margin: 0 26px;
> span:last-child {
margin-left: 30px;
> span:first-child {
margin-left: 25px;
margin-right: 25px;
}
}
}

View File

@@ -1,23 +1,24 @@
<template>
<div
id="bad-gas-info"
:style="{ backgroundImage: 'url(' + getImageUrl(bgImage) + ')' }">
id="bad-gas-info"
:style="{ backgroundImage: 'url(' + getImageUrl(bgImage) + ')' }">
<div class="title">有害气体</div>
<div class="info-list">
<gas-info-item
v-for="item in badGasList"
:key="item.equipmentId"
:gasInfo="item"
@click="handleOpenChart"
v-for="item in badGasList"
:key="item.equipmentId"
:gasInfo="item"
@click="handleOpenChart"
/>
</div>
<div class="digital-tunnel">
<el-dialog
v-model="isBadGasVisited"
title="有害气体监控数据"
width="2175px"
:modal="false"
:close-on-click-modal="false"
v-model="isBadGasVisited"
title="有害气体监控数据"
width="2175px"
:modal="false"
>
<div class="left-top-icon"></div>
<div class="right-top-icon"></div>
@@ -27,9 +28,9 @@
</div>
<div class="time-select">
<time-range-btn
:buttonList="timeList"
v-model="selectTimeButton"
@select="timeSelect"
:buttonList="timeList"
v-model="selectTimeButton"
@select="timeSelect"
/>
</div>
<div class="left-bottom-icon"></div>
@@ -48,7 +49,7 @@ import {getBadGasEchartsInfo} from "@/api/largeScreen";
const props = defineProps({
list: Array,
badGasData: Array,
tunnelId:Number
tunnelId: Number
});
const timeList = ref(["年", "月", "日"]);
const selectTimeButton = ref(2);
@@ -57,37 +58,37 @@ const badGasList = ref([]);
let myEcharts = reactive({});
const bgImage = computed(() => (isBadGasVisited.value ? "sp_active.png" : "bg.png"));
watch(
() => props.list,
(now) => {
badGasList.value?.forEach((item) => {
now.forEach((newItem) => {
if (item.equipmentId === newItem.equipmentId) {
item.value = newItem.value;
}
() => props.list,
(now) => {
badGasList.value?.forEach((item) => {
now.forEach((newItem) => {
if (item.equipmentId === newItem.equipmentId) {
item.value = newItem.value;
}
});
});
});
},
{ deep: true }
},
{deep: true}
);
watch(
() => props.badGasData,
(now) => {
if(now.sensorList===null){
badGasList.value=[]
}else {
getBadGasInfo(now.sensorList);
}
},
{ deep: true }
() => props.badGasData,
(now) => {
if (now.sensorList === null) {
badGasList.value = []
} else {
getBadGasInfo(now.sensorList);
}
},
{deep: true}
);
watch(
() => props.tunnelId,
(now) => {
props.tunnelId=now
props.tunnelId = now
},
{ deep: true }
{deep: true}
);
const timeSelect = (index) => {
console.log("选择时间", index);
@@ -100,17 +101,17 @@ const timeSelect = (index) => {
}
};
const getBadGasInfo = (now) => {
if(now===null)return;
if (now === null) return;
let windPressureObj = {};
let windPressureArr = [];
now?.map((item) => {
if (
item.equipmentType === "carbonDioxide" ||
item.equipmentType === "carbonMonoxide" ||
item.equipmentType === "hydrogenSulfide" ||
item.equipmentType === "sulfurDioxide" ||
item.equipmentType === "sulfurMonoxide" ||
item.equipmentType === "nitrogenDioxide"
item.equipmentType === "carbonDioxide" ||
item.equipmentType === "carbonMonoxide" ||
item.equipmentType === "hydrogenSulfide" ||
item.equipmentType === "sulfurDioxide" ||
item.equipmentType === "sulfurMonoxide" ||
item.equipmentType === "nitrogenDioxide"
) {
windPressureObj = changeData(item);
windPressureArr.push(windPressureObj);
@@ -129,7 +130,7 @@ const changeData = (item) => {
};
};
const getBadGasChartInfo = () => {
let id =props.tunnelId
let id = props.tunnelId
getBadGasEchartsInfo(id).then(res => {
if (res?.code === 1000) {
isBadGasVisited.value = true
@@ -144,7 +145,7 @@ const handleOpenChart = () => {
};
const getImageUrl = (name) => {
return new URL(`../../../assets/images/badGasInfo/${name}`, import.meta.url)
.href;
.href;
};
/**
* 初始化echarts实例方法
@@ -180,14 +181,14 @@ const initChart = (type, values) => {
backgroundColor: "rgba(20,36,51,0.7)", // 设置背景颜色为透明
borderColor: "#6087BA", // 设置边框颜色为透明
padding: 0, // 设置内边距为0
borderWidth:2,
borderWidth: 2,
textStyle: {
fontSize: 40,
},
formatter: (params)=> {
let res = ` <div style="font-size: 52px;font-family: PingFang SC-Regular, PingFang SC;font-weight: 400;color: #EFEEEE;margin-bottom: -30px">${params[0].name}</div>` + '<br/>'
formatter: (params) => {
let res = ` <div style="font-size: 52px;font-family: PingFang SC-Regular, PingFang SC;font-weight: 400;color: #EFEEEE;margin-bottom: -30px">${params[0].name}</div>` + '<br/>'
for (let i = 0; i < params.length; i++) {
res += `
res += `
<div style="font-size: 52px;line-height: 48px;font-family: Bebas Neue-Regular, Bebas Neue;font-weight: 400;margin:0 20px 30px 20px;"><span style="color: #FFFFFF">${params[i].seriesName}: </span><span style="background: linear-gradient(180deg, #F5B85F 0%, #FFFFFF 100%);background-clip: text;-webkit-background-clip: text;-webkit-text-fill-color: transparent;">${params[i].value}</span></div>
`;
}
@@ -238,7 +239,7 @@ const initChart = (type, values) => {
// "23:00",
// "24:00",
// ],
data:type,
data: type,
axisLabel: {
textStyle: {
fontSize: 45,
@@ -264,7 +265,7 @@ const initChart = (type, values) => {
// 56, 12, 89, 34, 71, 43, 67, 20, 98, 72, 19, 61, 3, 85, 47, 92, 17, 76,
// 69, 25, 31, 49, 81, 63,
// ],
data:values.carbonDioxideValues,
data: values.carbonDioxideValues,
type: "line",
smooth: true,
symbolSize: 24,
@@ -278,7 +279,7 @@ const initChart = (type, values) => {
// 96, 38, 75, 10, 62, 81, 41, 27, 69, 16, 33, 57, 73, 87, 22, 46, 11,
// 79, 51, 29, 60, 48, 83, 15,
// ],
data:values.carbonMonoxideValues,
data: values.carbonMonoxideValues,
type: "line",
smooth: true,
symbolSize: 24,
@@ -292,7 +293,7 @@ const initChart = (type, values) => {
// 53, 88, 10, 67, 92, 31, 74, 46, 20, 37, 85, 16, 63, 70, 41, 99, 25,
// 77, 82, 13, 68, 49, 55, 7, 39,
// ],
data:values.nitrogenDioxideValues,
data: values.nitrogenDioxideValues,
type: "line",
smooth: true,
symbolSize: 24,
@@ -306,7 +307,7 @@ const initChart = (type, values) => {
// 37, 19, 85, 62, 28, 91, 43, 76, 14, 68, 33, 52, 71, 26, 97, 49, 82,
// 17, 66, 30, 78, 55, 23, 47,
// ],
data:values.sulfurMonoxideValues,
data: values.sulfurMonoxideValues,
type: "line",
smooth: true,
symbolSize: 24,
@@ -320,7 +321,7 @@ const initChart = (type, values) => {
// 6, 30, 91, 77, 28, 51, 42, 83, 16, 65, 35, 72, 18, 96, 47, 79, 100,
// 23, 80, 44, 62, 59, 13, 86,
// ],
data:values.sulfurDioxideValues,
data: values.sulfurDioxideValues,
type: "line",
smooth: true,
symbolSize: 24,
@@ -334,7 +335,7 @@ const initChart = (type, values) => {
// 13, 27, 39, 51, 62, 73, 84, 95, 10, 20, 30, 40, 50, 60, 70, 80, 90,
// 100, 11, 21, 31, 41, 51,
// ],
data:values.hydrogenSulfideValues,
data: values.hydrogenSulfideValues,
type: "line",
smooth: true,
symbolSize: 24,
@@ -344,7 +345,7 @@ const initChart = (type, values) => {
},
],
};
myEcharts.setOption(option,true);
myEcharts.setOption(option, true);
//图表大小自适应窗口大小变化
window.onresize = () => {
myEcharts.resize();

View File

@@ -3,53 +3,70 @@
<div class="title">风机</div>
<div class="fans">
<div class="fan-item" v-for="(item,index) in socketData" :key="item.equipmentId">
<!-- echarts -->
<div class="echart"></div>
<!-- 风机名 -->
<div class="fan-name">{{ changeNum(index + 1) }}号风机</div>
<!-- 功能 -->
<div class="option-nav">
<div class="state " :class="{ 'blue-state': !item.breakdown }">
<div :class="{ stopColor: item.breakdown }">
<div :style="{ backgroundImage: 'url(' +getImageUrl(item.breakdown)+')' }" class="state-icon"></div>
状态{{ item.breakdown ? '故障' : '运行' }}
</div>
<div class="switch">
<div
id="auto"
:class="{ active: item.running }"
@click="item.running = true"
>
启动
<div>
<!-- echarts -->
<div class="echart"></div>
<!-- 风机名 -->
<div class="fan-name">{{ changeNum(index + 1) }}号风机</div>
<!-- 功能 -->
<div class="option-nav">
<div>
<div class="state " :class="{ 'blue-state': !item.breakdown }">
<div :class="{ stopColor: item.breakdown }">
<div :style="{ backgroundImage: 'url(' +getImageUrl(item.breakdown)+')' }" class="state-icon"></div>
状态{{ item.breakdown ? '故障' : '运行' }}
</div>
<div class="switch">
<div
id="auto"
:class="{ active: item.running }"
@click="item.running = true"
>
启动
</div>
<div
id="stop"
:class="{ active: !item.running }"
@click="item.running = false"
>
停止
</div>
</div>
</div>
<div
id="stop"
:class="{ active: !item.running }"
@click="item.running = false"
>
停止
<div class="power">
<div class="check-box">
<el-radio-group v-model="item.autoMode" class="radio-group">
<el-radio :label="true">自动</el-radio>
<el-radio :label="false">手动</el-radio>
</el-radio-group>
</div>
<div class="edit-power">
<span style="color: white">当前功率</span>
<span class="units"
><input
type="number"
min="0"
v-model="item.frequencySetting"
:disabled="item.autoMode"
/></span>
</div>
</div>
</div>
</div>
<div class="power">
<div class="check-box">
<el-radio-group v-model="item.autoMode" class="radio-group">
<el-radio :label="true">自动</el-radio>
<el-radio :label="false">手动</el-radio>
</el-radio-group>
</div>
<div class="edit-power">
<span style="color: white">当前功率</span>
<div class="shuntThreshold">
<div class="fenliu-icon"></div>
分流阀值
<span class="units"
><input
type="number"
min="0"
v-model="item.frequencyFeedback"
:disabled="item.autoMode"
v-model="shuntThreshold"
/></span>
</div>
</div>
</div>
<div class="current">
<fan-info-item :wp="transducerData[index]"/>
</div>
</div>
</div>
</div>
@@ -57,15 +74,24 @@
<script setup>
import * as echarts from "echarts";
import FanInfoItem from "./FanInfoItem.vue";
const props = defineProps({
list: Array,
fanData: Array
fanData: Array,
transducerData: Array
});
const socketData = ref()
let Echarts_info1 = null;
let fan01_option = reactive();
const shuntThreshold = ref(23)
const transducerData = ref([])
const stateA = ref(false)
const stateB = ref(false)
const stateC = ref(false)
watch(() => props.transducerData, (now) => {
getTransData(now.frequencyChangerList)
}, {deep: true});
watch(() => props.fanData, (now) => {
getBasicData(now.frequencyChangerList)
}, {deep: true});
@@ -74,14 +100,52 @@ watch(() => props.list, (now) => {
now.forEach(newItem => {
if (item.equipmentId === newItem.equipmentId) {
if (newItem.frequencyFeedback) {
item.frequencySetting = newItem.frequencySetting
item.frequencyFeedback = newItem.frequencyFeedback
}
}
})
})
transducerData.value.forEach(item => {
now.forEach(newItem => {
if (item.equipmentId === newItem.equipmentId) {
if (newItem.frequencyFeedback) {
item.frequencySetting = newItem.frequencySetting
item.frequencyFeedback = newItem.frequencyFeedback
} else if (newItem.phaseCurrentA) {
item.valueA = newItem.phaseCurrentA
item.valueB = newItem.phaseCurrentB
item.valueC = newItem.phaseCurrentC
}
}
})
})
handleOnMounted()
}, {deep: true});
const getTransData = (data) => {
let tranObj = {}
let tranArr = []
data?.map(item => {
tranObj = {
equipmentId: item.equipmentId,
max: 200,
valueA: item.phaseCurrentA,
pointA: item.phaseCurrentAThreshold,
valueB: item.phaseCurrentB,
pointB: item.phaseCurrentBThreshold,
valueC: item.phaseCurrentC,
pointC: item.phaseCurrentCThreshold,
frequencySetting: item.frequencySetting,
frequencyFeedback: item.frequencyFeedback,
}
stateA.value = item.phaseCurrentA > item.phaseCurrentAThreshold
stateB.value = item.phaseCurrentB > item.phaseCurrentBThreshold
stateC.value = item.phaseCurrentC > item.phaseCurrentCThreshold
tranArr.push(tranObj)
})
transducerData.value = tranArr
}
const getImage = (type) => {
switch (type) {
case false:
@@ -109,7 +173,8 @@ const getBasicData = (data) => {
autoMode: item.autoMode,//自动模式
breakdown: item.breakdown,//故障
running: item.running,//启动,
frequencyFeedback: item.frequencyFeedback//当前功率
frequencyFeedback: item.frequencyFeedback,//当前功率
frequencySetting: item.frequencySetting//当前功率
}
tranArr.push(tranObj)
})
@@ -289,9 +354,8 @@ input[type="number"] {
}
#fan_info {
min-height: 547px;
max-height: 795px;
height: 547px;
//height: 1465px;
height: 1000px;
width: 830px;
position: absolute;
z-index: 100;
@@ -319,125 +383,194 @@ input[type="number"] {
height: calc(100% - 30px);
.fan-item {
//height: 33.5%;
height: 50%;
display: flex;
flex-direction: column;
font-size: 14px;
border-bottom: 2px solid rgba(107, 163, 237, 0.4);
.echart {
height: 100%;
width: 30%;
margin: 0px 0px 0px 10px;
position: relative;
}
.echart::after {
content: "Hz";
font-size: 24px;
font-family: MicrosoftYaHei, MicrosoftYaHei;
font-weight: bold;
color: #ffffff;
line-height: 31px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, 60%);
}
.fan-name {
width: 39px;
height: 140px;
font-size: 26px;
font-family: MicrosoftYaHei;
color: #38cafb;
line-height: 35px;
text-align: center;
margin-right: 42px;
transform: translateY(33%);
}
.option-nav {
> div:first-child {
display: flex;
width: 70%;
.blue-state {
> div:first-child:hover {
color: #fff;
background-color: #127399;
width: 180px;
padding-left: 5px;
border-radius: 8px;
> div:first-child {
background-image: url('../../../assets/images/fanInfo/white-state-icon.png') !important;
}
}
.echart {
height: 100%;
width: 30%;
margin: 0px 0px 0px 10px;
position: relative;
}
.state {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
.echart::after {
content: "Hz";
font-size: 24px;
font-family: MicrosoftYaHei, MicrosoftYaHei;
font-weight: bold;
color: #ffffff;
line-height: 31px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, 60%);
}
.fan-name {
width: 39px;
height: 140px;
font-size: 26px;
font-family: MicrosoftYaHei;
color: #38cafb;
line-height: 35px;
gap: 40px;
> div:first-child {
display: flex;
align-items: center;
padding-left: 5px;
> div {
margin-right: 10px;
}
}
img {
margin-right: 4px;
transform: translateY(15%);
}
.state-icon {
width: 26px;
height: 26px;
}
.switch {
display: flex;
width: 160px;
height: 40px;
border-radius: 22px;
border: 2px solid #0f82af;
overflow: hidden;
font-family: MicrosoftYaHei, MicrosoftYaHei;
font-weight: bold;
color: #127399;
line-height: 35px;
& > div {
flex: 1;
text-align: center;
cursor: pointer;
}
}
text-align: center;
margin-right: 42px;
transform: translateY(33%);
}
.power {
flex: 1.3;
.option-nav {
display: flex;
flex-direction: column;
justify-content: center;
padding: 0px 20px;
font-size: 28px;
font-family: MicrosoftYaHei;
color: #38cafb;
line-height: 37px;
gap: 33px;
width: 70%;
.edit-power {
margin-top: 10px;
> div:first-child {
margin-top: 48px;
display: flex;
align-items: center;
.state {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
font-size: 26px;
font-family: MicrosoftYaHei;
color: #38cafb;
line-height: 35px;
gap: 40px;
.blue-state {
> div:first-child:hover {
color: #fff;
background-color: #127399;
width: 180px;
padding-left: 5px;
border-radius: 8px;
> div:first-child {
background-image: url('../../../assets/images/fanInfo/white-state-icon.png') !important;
}
}
}
> div:first-child {
display: flex;
align-items: center;
padding-left: 5px;
> div {
margin-right: 10px;
}
}
img {
margin-right: 4px;
transform: translateY(15%);
}
.state-icon {
width: 26px;
height: 26px;
}
.switch {
display: flex;
width: 160px;
height: 40px;
border-radius: 22px;
border: 2px solid #0f82af;
overflow: hidden;
font-family: MicrosoftYaHei, MicrosoftYaHei;
font-weight: bold;
color: #127399;
line-height: 35px;
& > div {
flex: 1;
text-align: center;
cursor: pointer;
}
}
}
.power {
flex: 1.3;
display: flex;
flex-direction: column;
justify-content: center;
padding: 0px 20px;
font-size: 28px;
font-family: MicrosoftYaHei;
color: #38cafb;
line-height: 37px;
gap: 33px;
.edit-power {
margin-top: 10px;
.units {
position: relative;
}
.units::after {
content: "Hz";
position: absolute;
right: 6px;
top: 50%;
transform: translateY(-50%);
font-size: 28px;
font-family: MicrosoftYaHei;
color: #38cafb;
line-height: 37px;
}
input {
width: 130px;
height: 44px;
border: 2px solid #0f82af;
background: transparent;
margin-left: 14px;
outline: none;
font-size: 28px;
font-family: MicrosoftYaHei, MicrosoftYaHei;
font-weight: bold;
color: #38cafb;
line-height: 37px;
}
}
}
}
.shuntThreshold {
display: flex;
align-items: center;
justify-content: flex-end;
//align-items: center;
//width: 112px;
padding-top: 30px;
padding-right: 25px;
font-size: 28px;
font-family: MicrosoftYaHei;
color: #FFFFFF;
.fenliu-icon {
margin-right: 9px;
width: 31px;
height: 28px;
background-image: url('../../../assets/images/fanInfo/fsp_icon_flf.png');
}
.units {
z-index: 22;
position: relative;
}
input {
width: 130px;
@@ -445,35 +578,42 @@ input[type="number"] {
border: 2px solid #0f82af;
background: transparent;
margin-left: 14px;
outline:none;
outline: none;
font-size: 28px;
font-family: MicrosoftYaHei, MicrosoftYaHei;
font-weight: bold;
color: #38cafb;
line-height: 37px;
}
.units::after {
content: "%";
position: absolute;
right: 6px;
top: 40%;
bottom: 0;
transform: translateY(-50%);
font-size: 28px;
font-family: MicrosoftYaHei;
color: #38cafb;
line-height: 37px;
}
}
}
}
.current {
margin-top: 30px;
margin-left: 26px;
height: 37px;
font-size: 28px;
color: #FFFFFF;
}
}
}
}
.units {
position: relative;
}
.units::after {
content: "Hz";
position: absolute;
right: 6px;
top: 50%;
transform: translateY(-50%);
font-size: 28px;
font-family: MicrosoftYaHei;
color: #38cafb;
line-height: 37px;
}
.active {
color: white;

View File

@@ -0,0 +1,188 @@
<template>
<div class="wind-pressure-item" :class="{ abnormal:isWaringA }">
<div class="label">
A相电流:{{params.wp.valueA}}A
</div>
<div class="container" ref="length">
<div class="value" ref="valueA"></div>
<div class="point" ref="pointA"></div>
</div>
</div>
<div class="wind-pressure-item" :class="{ abnormal:isWaringB }">
<div class="label">
B相电流:{{params.wp.valueB}}A
</div>
<div class="container" ref="length">
<div class="value" ref="valueB"></div>
<div class="point" ref="pointB"></div>
</div>
</div>
<div class="wind-pressure-item" :class="{ abnormal:isWaringC }">
<div class="label">
C相电流:{{params.wp.valueC}}A
</div>
<div class="container" ref="length">
<div class="value" ref="valueC"></div>
<div class="point" ref="pointC"></div>
</div>
</div>
</template>
<script setup>
const params = defineProps({
wp: Object
});
const length = ref(null);
const valueA = ref(null);
const valueB = ref(null);
const valueC= ref(null);
const pointA = ref(null);
const pointB = ref(null);
const pointC = ref(null);
onMounted(()=>{
setValueA();
setValueB();
setValueC();
})
watch(() => params.wp.value, () => {
setValueA();
setValueB();
setValueC();
});
const setValueA=()=> {
let width = (params.wp.valueA* length.value.offsetWidth) / params.wp.max;
valueA.value.style.width = `${width}px`;
let flag = (params.wp.pointA * length.value.offsetWidth) / params.wp.max;
pointA.value.style.left = `${flag}px`;
if (width >= flag) {
valueA.value.style.background = "#F53839";
} else {
valueA.value.style.background = "#60DDDE";
}
}
const setValueB=()=> {
let width = (params.wp.valueB* length.value.offsetWidth) / params.wp.max;
valueB.value.style.width = `${width}px`;
let flag = (params.wp.pointB * length.value.offsetWidth) / params.wp.max;
pointB.value.style.left = `${flag}px`;
if (width >= flag) {
valueB.value.style.background = "#F53839";
} else {
valueB.value.style.background = "#60DDDE";
}
}
const setValueC=()=> {
let width = (params.wp.valueC* length.value.offsetWidth) / params.wp.max;
valueC.value.style.width = `${width}px`;
let flag = (params.wp.pointC * length.value.offsetWidth) / params.wp.max;
pointC.value.style.left = `${flag}px`;
if (width >= flag) {
valueC.value.style.background = "#F53839";
} else {
valueC.value.style.background = "#60DDDE";
}
}
let isWaringA = computed(() => {
return params.wp.valueA >= params.wp.pointA;
});
let isWaringB = computed(() => {
return params.wp.valueB >= params.wp.pointB;
});
let isWaringC = computed(() => {
return params.wp.valueC >= params.wp.pointC;
});
</script>
<style lang="scss" scoped>
.abnormal:hover {
background: #9C5252 !important;
border-radius: 6px;
.point {
background: #fff !important;
}
}
.wind-pressure-item {
display: flex;
align-items: center;
width: 100%;
padding: 6px 38px 7px 10px;
margin-bottom: 17px;
&:hover {
background: #2E5589;
border-radius: 6px;
}
&:last-child {
.label {
span {
margin: 0 5px 0 14px;
}
}
}
.label {
display: flex;
white-space: nowrap;
align-items: center;
margin-right: 20px;
img {
height: 26px;
width: 26px;
}
span {
//width: 101px;
white-space: nowrap;
height: 37px;
font-size: 28px;
color: #d6f1fa;
line-height: 37px;
margin: 0 20px 0 14px;
}
}
.container {
width: 500px;
height: 24px;
border-radius: 12px;
border: 1px solid #0f82af;
position: relative;
.value {
height: inherit;
width: 0px;
background: #60ddde;
border-radius: 10px;
transition: width 0.5s linear 0s;
}
.point {
position: absolute;
height: 20px;
background: #92D18F;
width: 3px;
border-radius: 4px;
box-shadow: 0 0 10px #92D18F;
top: 1px;
left: 1px;
}
}
.value-num {
height: 37px;
font-size: 28px;
font-family: MicrosoftYaHei;
color: #d6f1fa;
line-height: 37px;
margin-left: 20px;
}
}
</style>

View File

@@ -0,0 +1,326 @@
<template>
<div id="scene">
<div id="cvs" ref="content"></div>
<dev-info ref="info" :devInfo="devInfo" />
<pre-dialog ref="edit" @addEquipment="handleAddEqu" @removeEquipment="handleRemoveEqu" @cancel="handleCancel"
:hasDev="hasDevice" :pointNum="pointNum" pointGap="500" :equipmentType="equipmentType"
:equipmentName="equipmentName" :equipmentValue="equipmentValue" />
<el-dialog v-model="centerDialogVisible" width="30%" destroy-on-close center :show-close="false" style="
margin: 20% auto;
width: 569px;
height: 330px;
background: rgba(7, 35, 72, 0.79);
border-radius: 20px;
border: 2px solid #0f82af;
">
<p id="remove-title">是否确定删除该设备</p>
<div class="btn">
<button @click="centerDialogVisible = false">取消</button>
<button @click="handleConfirmAddEqu">确定</button>
</div>
</el-dialog>
</div>
</template>
<script setup>
import * as three from "three";
import ThreeDScene from "./sceneClass/demo.js";
import DevInfo from "./displayInfoComp/DevInfo.vue";
import PreDialog from "./preEquComp/preDialog.vue"
// 导入模模型加载器
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
// import { DRACOLoader } from "three/examples/jsm/loaders/dracoloader";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import * as TWEEN from "three/examples/jsm/libs/tween.module";
import {
CSS3DRenderer,
CSS3DObject,
CSS3DSprite,
} from "three/addons/renderers/CSS3DRenderer.js";
// 引入CSS2渲染器CSS2DRenderer和CSS2模型对象CSS2DObject
import {
CSS2DRenderer,
CSS2DObject,
} from "three/addons/renderers/CSS2DRenderer.js";
import { OBJLoader } from "three/examples/jsm/loaders/OBJLoader";
import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader";
import { onMounted, reactive, ref } from "vue";
import { ElMessage } from "element-plus";
import { useModelSceneStore } from "@/store/modelSceneStore";
import { toRaw } from "vue"
// 获取html标签跟随组件dom
const content = ref(null);
const info = ref(null);
const edit = ref(null);
const store = useModelSceneStore();
let demo; //定义demo全局变量
// const loader = new OBJLoader();
const loader = new OBJLoader();
let hdrLoader = new RGBELoader();
let backColorSet = three.sRGBEncoding;
const params = defineProps(["isedit", "tunnelId"]); //接收参数看是不是编辑模式,如果是编辑模式,则需要做一些处理
let isedit = ref(params.isedit)
onMounted(handleMounted);
// 挂载后回调
async function handleMounted() {
const doms = [info.value.$el, edit.value.$el];
demo = new ThreeDScene(three, content.value);
//看是不是预览模式,然后继续相关的操作(会在demo中的初始化中进行)
demo.isedit = params.isedit;
let tunnelasync = await demo.loadModel(GLTFLoader, "./assets/tunnelModel/chanel-have-wall.gltf");
demo.addOrbitControls(OrbitControls);
demo.addTween(TWEEN);
demo.addCSS3Renderer(CSS3DRenderer, CSS3DSprite, doms);
demo.setDistance(10);
lClickCallback(demo); //绑定左键回调
rClickCallback(demo); //绑定右键回调
//加载HDR背景图片
demo.loadBackground(hdrLoader, backColorSet);
watch(
() => params.tunnelId,
async () => {
const text = await store.initData(params.tunnelId);
}
);
// 初始化设备模型
try {
//在这加载隧道
const map = new Map();
map.set("equ_fan", await loadModel("/devicesModel/model2.obj"));
map.set("equ_sensors", await loadModel("/devicesModel/sensors.obj"));
// 给对象初加载设备模型
demo.initDevicesModel(map);
const equipmentList = await store.getEquipmentList();
demo.previewtunnelModeInit(equipmentList);
// previewEquInfProcess(equipmentList)
} catch (err) {
console.log(err);
}
}
// 每个模型加载回调
function loadModel(path) {
return new Promise((resolve, reject) => {
loader.load(
path,
(obj) => {
resolve(obj);
},
(xhr) => { },
(err) => {
reject(err);
}
);
});
}
let hasDevice = ref(true);
let devInfo = reactive({
meshId: null,
name: "无",
state: "无",
position: "无",
});
// 对象建构赋值功能
function editDevInfo(
value = {
meshId: null,
name: "无",
state: "无",
position: "无",
}
) {
devInfo.meshId = value.meshId;
devInfo.name = value.name;
devInfo.state = value.state;
devInfo.position = value.position;
}
//左键/双击左键回调函数
function lClickCallback(demo) {
//demo动态添加函数为操作组件内部
function displayDevInfo(targetPoint = null) {
hasDevice.value = targetPoint.hasDevice;
if (!targetPoint.info) {
editDevInfo();
return;
}
editDevInfo(targetPoint.info);
}
// 传给内部使用
demo.addFunction("displayDevInfo", displayDevInfo);
}
let pointNum = ref(0);
let targetP;
// 右键点击附着点后调函数
function rClickCallback(demo) {
function editDev(targetPoint = null) {
hasDevice = targetPoint.hasDevice;
targetP = targetPoint;
//点击之后马上调用这个函数变成回调然后进行处理处理在传给preview表单
const equipmentList = store.getEquipmentList().then((result) => {
previewEquInfProcess(result, targetP)
});
pointNum.value = Number(
targetPoint.name.substring(
targetPoint.name.indexOf("_") + 1,
targetPoint.name.lastIndexOf("_")
) - 1
);
if (!targetPoint.info) return;
editDevInfo(targetPoint.info);
}
demo.addFunction("editDev", editDev);
}
// 添加设备
function handleAddEqu(formInfo) {
if (!formInfo.equipmentType) {
ElMessage({
message: "请选择传感器!",
type: "warning",
});
return;
}
//表单信息
//这里利用处理请求
demo.addEquipment(targetP, formInfo);
ElMessage({
message: "添加成功!",
type: "success",
});
}
const centerDialogVisible = ref(false);
// 删除设备
function handleRemoveEqu() {
if (!targetP.hasDevice) {
ElMessage({
message: "该点位不存在设备!",
type: "warning",
});
return;
}
centerDialogVisible.value = true;
}
// 对话框确认删除
function handleConfirmAddEqu() {
demo.removeEquipment(targetP);
centerDialogVisible.value = false;
ElMessage({
message: "删除成功!",
type: "success",
});
}
// 处理取消关闭编辑框事件
function handleCancel() {
if (!demo) return;
// 关闭标签
demo.isControlOrbit(true);
demo._resetState();
demo.clearTagsObj();
}
let equipmentType = ref(0)
let equipmentName = ref(0)
let equipmentValue = ref(0)
function previewEquInfProcess(equipmentList, targetP) {
for (const equipment of equipmentList) {
// console.log(equipment);
if (equipment.position == targetP.name) {
equipmentType.value = equipment.equipmentType
//需要将获取到的设备列表进行序列化
equipmentName.value = toRaw(equipment.chooseEquipment).label
equipmentValue.value = equipment.threshold
if (equipmentType.value == 'frequency') {
equipmentType.value = '风机'
}
if (equipmentType.value == 'windPressure') {
equipmentType.value = '风压传感器'
}
if (equipmentType.value == 'sensor') {
equipmentType.value = '其他传感器'
}
// console.log(equipmentType.value);
// console.log(equipmentName.value);
// console.log(equipmentValue.value);
}
}
}
</script>
<style lang="scss" scoped>
#scene {
position: relative;
height: 100%;
width: 100%;
#cvs {
height: 100%;
}
}
#remove-title {
height: 42px;
font-size: 32px;
font-family: MicrosoftYaHei, MicrosoftYaHei;
font-weight: bold;
color: #08b7b8;
line-height: 42px;
letter-spacing: 3px;
text-align: center;
margin: 65px 0px 70px 0px;
}
.btn {
display: flex;
justify-content: center;
gap: 80px;
:nth-child(1) {
width: 190px;
height: 60px;
border-radius: 11px;
border: 2px solid #0f82af;
font-size: 28px;
font-family: MicrosoftYaHei;
color: #08b7b8;
line-height: 37px;
background: transparent;
}
:nth-child(2) {
width: 190px;
height: 60px;
background: #08b7b8;
border-radius: 11px;
font-size: 28px;
font-family: MicrosoftYaHei, MicrosoftYaHei;
font-weight: bold;
color: #ffffff;
line-height: 37px;
}
}
</style>

View File

@@ -2,8 +2,10 @@
<div id="scene">
<div id="cvs" ref="content"></div>
<dev-info ref="info" :devInfo="devInfo" />
<!-- 这里的预览模式需要做成不能修改的模式 -->
<edit-dialog ref="edit" @addEquipment="handleAddEqu" @removeEquipment="handleRemoveEqu" @cancel="handleCancel"
:hasDev="hasDevice" :pointNum="pointNum" pointGap="500" />
:hasDev="hasDevice" :pointNum="pointNum" :tunnelId="tunnelId" :position="targetP?.name" :hasEquipment="hasDevice"
pointGap="500" />
<el-dialog v-model="centerDialogVisible" width="30%" destroy-on-close center :show-close="false" style="
margin: 20% auto;
width: 569px;
@@ -47,22 +49,35 @@ import { OBJLoader } from "three/examples/jsm/loaders/OBJLoader";
import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader";
import { onMounted, reactive, ref } from "vue";
import { ElMessage } from "element-plus";
import { useModelSceneStore } from "@/store/modelSceneStore";
// 获取html标签跟随组件dom
const content = ref(null);
const info = ref(null);
const edit = ref(null);
let demo; //定义demo全局变量
// const loader = new OBJLoader();
const store = useModelSceneStore();
let demo; //定义demo对象
const loader = new OBJLoader();
let hdrLoader = new RGBELoader();
let backColorSet = three.sRGBEncoding;
const params = defineProps(["isedit", "tunnelId"]); //接收参数看是不是编辑模式,如果是编辑模式,则需要做一些处理
let isedit = ref(params.isedit);
onMounted(handleMounted);
// 挂载后回调
async function handleMounted() {
const doms = [info.value.$el, edit.value.$el];
demo = new ThreeDScene(three, content.value);
demo.loadModel(GLTFLoader, "./assets/tunnelModel/chanel.gltf");
//看是不是预览模式,然后继续相关的操作(会在demo中的初始化中进行)
demo.isedit = params.isedit;
const loaded = await demo.loadModel(
GLTFLoader,
"./assets/tunnelModel/chanel-have-wall.gltf"
);
demo.addOrbitControls(OrbitControls);
demo.addTween(TWEEN);
demo.addCSS3Renderer(CSS3DRenderer, CSS3DSprite, doms);
@@ -72,21 +87,27 @@ async function handleMounted() {
//加载HDR背景图片
demo.loadBackground(hdrLoader, backColorSet);
watch(
() => params.tunnelId,
async () => {
const text = await store.initData(params.tunnelId);
}
);
// 初始化设备模型
try {
const map = new Map();
map.set("equ_fan", await loadModel("/devicesModel/model2.obj"));
map.set("equ_sensors", await loadModel("/devicesModel/sensors.obj"));
// 给对象初加载设备模型
demo.initDevicesModel(map);
// 初始化
const equipmentList = await store.getEquipmentList();
demo.editTunnelInit(equipmentList);
} catch (err) {
console.log(err);
ElMessage({
message: "场景加载异常!",
type: "warning",
});
}
demo.tunnelModeInit();
}
// 每个模型加载回调
@@ -139,54 +160,39 @@ function lClickCallback(demo) {
}
editDevInfo(targetPoint.info);
}
// 传给内部使用
demo.addFunction("displayDevInfo", displayDevInfo);
}
let pointNum = ref(0);
let targetP;
let targetP = ref({});
// 右键点击附着点后调函数
function rClickCallback(demo) {
function editDev(targetPoint = null) {
hasDevice = targetPoint.hasDevice;
targetP = targetPoint;
hasDevice.value = targetPoint.hasDevice;
targetP.value = targetPoint;
pointNum.value = Number(
targetPoint.name.substring(
targetPoint.name.indexOf("_") + 1,
targetPoint.name.lastIndexOf("_")
) - 1
);
if (!targetPoint.info) return;
editDevInfo(targetPoint.info);
}
demo.addFunction("editDev", editDev);
}
// 添加设备
// 场景添加设备
function handleAddEqu(formInfo) {
if (!formInfo.equipmentType) {
ElMessage({
message: "请选择传感器!",
type: "warning",
});
return;
}
//表单信息
//这里利用处理请求
demo.addEquipment(targetP, formInfo);
ElMessage({
message: "添加成功!",
type: "success",
});
demo.addEquipment(targetP.value, formInfo);
}
const centerDialogVisible = ref(false);
// 删除设备
function handleRemoveEqu() {
if (!targetP.hasDevice) {
if (!targetP.value.hasDevice) {
ElMessage({
message: "该点位不存在设备!",
type: "warning",
@@ -197,12 +203,23 @@ function handleRemoveEqu() {
}
// 对话框确认删除
function handleConfirmAddEqu() {
demo.removeEquipment(targetP);
centerDialogVisible.value = false;
ElMessage({
message: "删除成功!",
type: "success",
});
console.log("target:", targetP.value.info);
store
.deleteEquipment(10000, params.tunnelId, targetP.value.info.equipmentId)
.then((res) => {
demo.removeEquipment(targetP.value);
centerDialogVisible.value = false;
ElMessage({
message: "删除成功!",
type: "success",
});
})
.catch((err) => {
ElMessage({
message: "请求异常!",
type: "warning",
});
});
}
// 处理取消关闭编辑框事件
function handleCancel() {
@@ -212,6 +229,7 @@ function handleCancel() {
demo._resetState();
demo.clearTagsObj();
}
//现在首先有二种方案是写在TunnelScene.vue中还是demo.js中呢
//我认为可能看数据在哪获取到时候看在哪里导入首先我们放在TunnelScene里面吧
//因为没获取到接口,我们先写死!!!
@@ -219,33 +237,25 @@ function handleCancel() {
const ThreeConfig = {
code: 0,
data: {
tunnelThreeConfig: [{
equipmentId: 'fan_01',//传感器id
equipmentName: '01',//设备名称
pointName: 'point_005_tl',//附着点名称(定位)
equipmentType: 'fan',//设备类型(类型可根据后端
equipmentValue: 23, //设备存的值
}, {
equipmentId: 'sensors_01',//传感器id
equipmentName: '01',//设备名称
pointName: 'point_009_bl',//附着点名称(定位)
equipmentType: 'sensors',//设备类型(类型可根据后端
equipmentValue: 67, //设备存的值
}],
tunnelThreeConfig: [
{
equipmentId: "fan_01", //传感器id
equipmentName: "01", //设备名称
pointName: "point_005_tl", //附着点名称(定位)
equipmentType: "fan", //设备类型(类型可根据后端
equipmentValue: 23, //设备存的值
},
{
equipmentId: "sensors_01", //传感器id
equipmentName: "01", //设备名称
pointName: "point_009_bl", //附着点名称(定位)
equipmentType: "sensors", //设备类型(类型可根据后端
equipmentValue: 67, //设备存的值
},
],
},
msg: "dda"
}
//取值方便操作
// const tunnelConfigEquipment = ThreeConfig.data.tunnelThreeConfig
// function tunnelModeInit() {
// for (const item of tunnelConfigEquipment) {
// let pointmodel = demo.scene.getobjectByName(item.pointName)
// console.log(pointmodel);
// }
// }
// tunnelModeInit()
msg: "dda",
};
</script>
<style lang="scss" scoped>

View File

@@ -0,0 +1,113 @@
<template>
<div id="select-input">
<div class="select">
<div class="value" @click="isShowList = !isShowList">
<span>{{ selectedVal }}</span>
<img src="/images/htmlEditDialog/select-icon.png" alt="" />
</div>
<ul v-show="isShowList">
<li
v-for="(item, index) of params.options"
:key="index"
@click="handleClickItem(item)"
>
{{ item.label }}
</li>
</ul>
</div>
</div>
</template>
<script setup>
import { ref, defineEmits, defineProps, defineExpose } from "vue";
const params = defineProps({
options: Array,
placeholder: String,
});
const emit = defineEmits(["selectChange"]);
let isShowList = ref(false);
let selectedVal = ref(params.placeholder);
function handleClickItem(item) {
if (!item.value) return;
selectedVal.value = item.label;
isShowList.value = false;
emit("selectChange", item);
}
function reset() {
isShowList.value = false;
selectedVal.value = params.placeholder;
}
defineExpose({
reset,
});
</script>
<style lang="scss" scoped>
#select-input {
P {
width: 130px;
height: 35px;
font-size: 26px;
font-family: MicrosoftYaHei;
color: #ffffff;
line-height: 35px;
}
.select {
position: relative;
.value {
width: 284px;
height: 51px;
border: 1px solid #0f82af;
font-size: 28px;
font-family: MicrosoftYaHei;
color: #08b7b8;
line-height: 51px;
margin-top: 20px;
padding: 0px 0px 0px 10px;
position: relative;
img {
position: absolute;
right: 20px;
bottom: 18px;
cursor: pointer;
}
img:active {
transform: scale(0.9);
}
}
ul {
position: absolute;
width: 284px;
max-height: 300px;
background: #072247;
border-radius: 0px 0px 20px 20px;
border: 1px solid #0f82af;
z-index: 10000;
overflow-y: scroll;
li {
// height: 37px;
font-size: 28px;
font-family: MicrosoftYaHei;
color: #ffffff;
line-height: 37px;
padding: 10px 10px;
cursor: pointer;
}
li:hover {
color: #08b7b8;
background: #0f82af8f;
}
}
ul::-webkit-scrollbar {
height: 20px;
width: 20;
}
ul::-webkit-scrollbar-thumb {
border-radius: 10px;
-webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
background: #53535380;
}
}
}
</style>

View File

@@ -2,25 +2,39 @@
<div id="edit-dialog">
<div class="distance-back">
<p>当前距离洞口{{ pointDistance_str }}</p>
<img src="/images/htmlEditDialog/back-icon.png" alt="" @click="handleCancel" />
<img
src="/images/htmlEditDialog/back-icon.png"
alt=""
@click="handleCancel"
/>
</div>
<div class="equ-info">当前风压:{{ p }}Pa</div>
<div class="setting">
<div class="setting-item">
<p>传感器类型</p>
<el-select v-model="equipmentSetting.equipmentType" :fit-input-width="true" filterable clearable
placeholder="请选择设备类型">
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<select-input
:options="options"
@selectChange="handleTypeChange"
placeholder="请选择传感器类型"
ref="equipmentType"
/>
</div>
<div class="setting-item">
<p>设备选择</p>
<el-select v-model="equipmentSetting.chooseEquipment" :fit-input-width="true" filterable clearable
placeholder="请选择设备类型">
<el-option v-for="item in options2" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<select-input
:options="options2"
@selectChange="handleEquipmentChange"
placeholder="请选择设备"
ref="equipment"
/>
</div>
<input-num name="阈值" placeholder="请输入阈值" @inputValue="handelInput" :disabled="isDisabledInputNum" />
<input-num
name="阈值"
placeholder="请输入阈值"
@inputValue="handelInput"
:disabled="isDisabledInputNum"
/>
</div>
<div class="btn">
<button @click="removeEquipment">删除</button>
@@ -31,83 +45,81 @@
<script setup>
import InputNum from "./childComps/InputNum.vue";
import SelectInput from "./childComps/SelectInput.vue";
import { getEquipmentType, getEquipment } from "@/api/tunnelScene";
import { reactive, computed, defineEmits, defineProps, watch, ref } from "vue";
import { useModelSceneStore } from "@/store/modelSceneStore";
import { ElMessage } from "element-plus";
import {
reactive,
computed,
defineEmits,
defineProps,
watch,
nextTick,
} from "vue";
// 定义事件发射器,父组件监听
const emit = defineEmits(["cancel", "removeEquipment", "addEquipment"]);
const params = defineProps(["pointNum", "pointGap"]); //隧道第几个锚点
const params = defineProps([
"pointNum",
"pointGap",
"tunnelId",
"position",
"hasEquipment",
]);
// 当前风压
let p = ref(57);
const equipment = ref(null);
const equipmentType = ref(null);
const store = useModelSceneStore();
//计算锚点之间距离
const pointDistance_str = computed(
() =>
`${params.pointGap}米*${params.pointNum}=${params.pointGap * params.pointNum
`${params.pointGap}米*${params.pointNum}=${
params.pointGap * params.pointNum
}`
);
// 请求数据模型
const equipmentSetting = reactive({
equipmentType: "", //设备类型
chooseEquipment: "", //设备选择(设备名称)
chooseEquipment: null, //设备选择(设备名称)
threshold: "", //阈值
});
// 绑定选择的设备类型
function handleTypeChange(optionItem) {
equipmentSetting.equipmentType = optionItem.value;
}
function handleEquipmentChange(equipmentItem) {
equipmentSetting.chooseEquipment = equipmentItem;
}
// 设备类型选项参数
const options = reactive([
{ label: "风机", value: "fan" },
{ label: "风压传感器", value: "sensors" },
{ label: "普通传感器", value: "sensors_2" },
]);
let options = reactive([{ label: "暂无数据", value: "" }]);
// 设备编号参数
let options2 = reactive([]);
let options2 = reactive([{ equipmentId: 0, label: "暂无数据", value: "" }]);
let isDisabledInputNum = ref(false);
// 模拟生成option2
watch(
() => equipmentSetting.equipmentType,
() => {
let label;
options2 = [];
equipmentSetting.chooseEquipment = "";
isDisabledInputNum = false;
if (equipmentSetting.equipmentType === "fan") {
label = "风机";
isDisabledInputNum = true;
}
if (equipmentSetting.equipmentType === "windPressure") label = "风压传感器";
if (equipmentSetting.equipmentType === "sensors") label = "普通传感器";
for (let i = 1; i < 11; i++) {
options2.push({ label: `${i}${label}`, value: `${label}_${i}` });
}
(newVal) => {
console.log(params.tunnelId, newVal);
equipment.value.reset();
getEquipment(params.tunnelId, newVal, 1).then((res) => {
options2.splice(0, options2.length);
options2.push(
...res.data.map((item) => {
return {
equipmentId: item.equipmentId,
label: item.equipmentName,
value: item.equipmentType,
};
})
);
});
}
);
// const obj = {
// equipmentId: String, //传感器id
// equipmentName: String, //设备名称
// pointName: String, //附着点名称(定位)
// equipmentType: String, //设备类型(类型可根据后端)
// equipmentOnline: Boolean, //是否在线
// isFan: Boolean, //是不是风机
// fanSpeed: Number, //F
// };
// const tunnel = {
// tunnelId: "",
// };
// 显示锚点距离
let point_num = ref(0);
// 设备类型请求
getEquipmentType()
.then((res) => {
options.splice(0, options.length);
options.push(...res.data);
})
.catch((err) => console.log(err));
function handelInput(e) {
equipmentSetting.threshold = e;
@@ -115,6 +127,11 @@ function handelInput(e) {
// 处理取消事件
function handleCancel() {
equipmentSetting.equipmentType = "";
equipmentSetting.chooseEquipment = null;
equipmentSetting.threshold = "";
equipment.value.reset();
equipmentType.value.reset();
// 发射事件给tunnel父组件
emit("cancel");
}
@@ -126,20 +143,44 @@ function removeEquipment() {
// 添加设备
function addEquipment() {
emit("addEquipment", equipmentSetting);
equipmentSetting.chooseEquipment = "";
equipmentSetting.equipmentType = "";
equipmentSetting.threshold = "";
if (params.hasEquipment) {
ElMessage({
message: "该点位已存在设备!",
type: "warning",
});
return;
}
if (!equipmentSetting.equipmentType || !equipmentSetting.chooseEquipment) {
ElMessage({
message: "选项不能为空!",
type: "warning",
});
return;
}
equipmentSetting.position = params.position;
// 合法请求
store
.saveSceneData(10000, 1, equipmentSetting)
.then((res) => {
emit("addEquipment", equipmentSetting);
equipmentSetting.chooseEquipment = null;
equipmentSetting.equipmentType = "";
equipmentSetting.threshold = "";
equipment.value.reset();
equipmentType.value.reset();
ElMessage({
message: "添加成功!",
type: "success",
});
})
.catch((err) => {
ElMessage({
message: "请求异常!",
type: "warning",
});
});
}
const equipmentType = {
label: String, //传感器类型
typeId: String, //传感器类型ID(方便查找该类型的所有设备)
};
// 通过以上选项请求以下具体哪个设备
const equipment = {
label: String, //传感器编号加名称
equId: String, // 传感器Id
};
</script>
<style lang="scss" scoped>
@@ -202,40 +243,6 @@ const equipment = {
margin-bottom: 20px;
}
}
:deep(.el-select) {
width: 284px;
//height: 51px;
border: transparent;
}
:deep(.el-input--suffix) {
width: 284px;
height: 51px;
background: rgba(7, 35, 72, 0.79);
border: 2px solid #0f82af;
}
:deep(.el-input__wrapper) {
background: transparent;
border: none !important;
padding: 0px 12px 0px 10px;
box-shadow: none !important;
}
:deep(.el-input__wrapper input) {
background: transparent;
height: 100%;
font-size: 28px;
font-family: MicrosoftYaHei;
color: #08b7b8;
line-height: 37px;
}
:deep(.el-icon) {
color: #05feff;
font-size: 34px;
}
}
.btn {

View File

@@ -0,0 +1,61 @@
<template>
<div id="input-num">
<p>{{ params.name }}</p>
<input type="text" :placeholder="params.placeholder" @input="handleChange" :disabled="params.disabled" readonly
unselectable="on" οnfοcus="this.blur();" />
</div>
</template>
<script setup>
import { ref, defineProps, defineEmits, onUnmounted } from "vue";
const params = defineProps({
name: String,
placeholder: String,
disabled: Boolean,
});
const emit = defineEmits(["inputValue"]);
let timer = null;
function handleChange(e) {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
emit("inputValue", e.target.value);
}, 200);
}
onUnmounted(handleUnmounted);
function handleUnmounted() {
clearInterval(timer);
}
</script>
<style lang="scss" scoped>
#input-num {
margin: 40px 30px 0px 30px;
P {
width: 130px;
height: 35px;
font-size: 26px;
font-family: MicrosoftYaHei;
color: #ffffff;
line-height: 35px;
}
input {
width: 284px;
height: 51px;
border: 2px solid #0f82af;
background: transparent;
margin-top: 20px;
font-size: 26px;
font-family: MicrosoftYaHei;
color: #ffffff;
line-height: 35px;
padding: 0px 10px;
overflow: hidden;
}
::placeholder {
color: #0f82af;
}
}
</style>

View File

@@ -0,0 +1,306 @@
<template>
<div id="edit-dialog">
<div class="distance-back">
<p>当前距离洞口{{ pointDistance_str }}</p>
<img src="/images/htmlEditDialog/back-icon.png" alt="" @click="handleCancel" />
</div>
<div class="equ-info">当前风压:{{ p }}Pa</div>
<div class="setting">
<div class="setting-item">
<p>传感器类型</p>
<el-select v-model="equipmentSetting.equipmentType" :fit-input-width="true" filterable clearable
:placeholder="params.equipmentType" disabled>
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</div>
<div class="setting-item">
<p>设备选择</p>
<el-select v-model="equipmentSetting.chooseEquipment" :fit-input-width="true" filterable clearable
:placeholder="params.equipmentName" disabled>
<el-option v-for="item in options2" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</div>
<input-num name="阈值" :placeholder="params.equipmentValue" @inputValue="handelInput"
:disabled="isDisabledInputNum" />
</div>
<div class="btn">
<button @click="removeEquipment" disabled>删除</button>
<button @click="addEquipment" disabled>添加</button>
</div>
</div>
</template>
<script setup>
import InputNum from "./childComps/InputNum.vue";
import {
reactive,
computed,
defineEmits,
defineProps,
watch,
nextTick,
ref,
} from "vue";
// 定义事件发射器,父组件监听
const emit = defineEmits(["cancel", "removeEquipment", "addEquipment", "EquInf"]);
const params = defineProps(["pointNum", "pointGap", "equipmentType", "equipmentName", "equipmentValue"]); //隧道第几个锚点
// 当前风压
let p = ref(57);
// function EquInf() {
// // 发射事件给tunnel父组件
// emit("EquInf");
// }
console.log(params.equipmentType);
console.log(params.equipmentName);
console.log(params.equipmentValue);
// let equipmentTypeInf = ref(params.equipmentType)
// let equipmentNameInf = ref(params.equipmentName)
// let equipmentValueInf = ref(params.equipmentValue)
//计算锚点之间距离
const pointDistance_str = computed(
() =>
`${params.pointGap}米*${params.pointNum}=${params.pointGap * params.pointNum
}`
);
// 请求数据模型
const equipmentSetting = reactive({
equipmentType: "", //设备类型
chooseEquipment: "", //设备选择(设备名称)
threshold: "", //阈值
});
// 设备类型选项参数
const options = reactive([
{ label: "风机", value: "fan" },
{ label: "风压传感器", value: "sensors" },
{ label: "普通传感器", value: "sensors_2" },
]);
// 设备编号参数
let options2 = reactive([]);
let isDisabledInputNum = ref(false);
watch(
() => equipmentSetting.equipmentType,
() => {
let label;
options2 = [];
equipmentSetting.chooseEquipment = "";
isDisabledInputNum = false;
if (equipmentSetting.equipmentType === "fan") {
label = "风机";
isDisabledInputNum = true;
}
if (equipmentSetting.equipmentType === "windPressure") label = "风压传感器";
if (equipmentSetting.equipmentType === "sensors") label = "普通传感器";
for (let i = 1; i < 11; i++) {
options2.push({ label: `${i}${label}`, value: `${label}_${i}` });
}
}
);
// const obj = {
// equipmentId: String, //传感器id
// equipmentName: String, //设备名称
// pointName: String, //附着点名称(定位)
// equipmentType: String, //设备类型(类型可根据后端)
// equipmentOnline: Boolean, //是否在线
// isFan: Boolean, //是不是风机
// fanSpeed: Number, //F
// };
// const tunnel = {
// tunnelId: "",
// };
// 显示锚点距离
let point_num = ref(0);
function handelInput(e) {
equipmentSetting.threshold = e;
}
// 处理取消事件
function handleCancel() {
// 发射事件给tunnel父组件
emit("cancel");
}
// 删除模型
function removeEquipment() {
emit("removeEquipment");
}
// 添加设备
function addEquipment() {
emit("addEquipment", equipmentSetting);
equipmentSetting.chooseEquipment = "";
equipmentSetting.equipmentType = "";
equipmentSetting.threshold = "";
}
const equipmentType = {
label: String, //传感器类型
typeId: String, //传感器类型ID(方便查找该类型的所有设备)
};
// 通过以上选项请求以下具体哪个设备
const equipment = {
label: String, //传感器编号加名称
equId: String, // 传感器Id
};
</script>
<style lang="scss" scoped>
#edit-dialog {
width: 540px;
// min-height: 683px;
background: rgba(7, 35, 72, 0.79);
border-radius: 20px;
border: 2px solid #0f82af;
position: absolute;
z-index: 999;
display: block;
opacity: 0;
.distance-back {
height: 30px;
display: flex;
justify-content: space-between;
margin: 20px 20px 0px 30px;
p {
width: 388px;
height: 35px;
font-size: 24px;
font-family: MicrosoftYaHei;
color: #ffffff;
line-height: 35px;
}
img {
cursor: pointer;
}
img:active {
transform: scale(0.9);
}
}
.equ-info {
width: 190px;
height: 35px;
font-size: 26px;
font-family: MicrosoftYaHei;
color: #f7b500;
line-height: 35px;
margin: 20px 30px 0px 30px;
}
.setting {
.setting-item {
padding: 30px 30px 10px 30px;
p {
width: 130px;
height: 35px;
font-size: 26px;
font-family: MicrosoftYaHei;
color: #ffffff;
line-height: 35px;
margin-bottom: 20px;
}
}
:deep(.el-select) {
width: 284px;
//height: 51px;
border: transparent;
}
:deep(.el-input--suffix) {
width: 284px;
height: 51px;
background: rgba(7, 35, 72, 0.79);
border: 2px solid #0f82af;
}
:deep(.el-input__wrapper) {
background: transparent;
border: none !important;
padding: 0px 12px 0px 10px;
box-shadow: none !important;
}
:deep(.el-input__wrapper input) {
background: transparent;
height: 100%;
font-size: 28px;
font-family: MicrosoftYaHei;
color: #08b7b8;
line-height: 37px;
}
:deep(.el-icon) {
color: #05feff;
font-size: 34px;
}
}
.btn {
margin: 100px 40px 40px 40px;
display: flex;
justify-content: space-between;
button[disabled] {
color: grey !important;
opacity: 1
}
button {
cursor: pointer;
transition: transform 0.1s linear 0s;
}
& :nth-child(1) {
width: 190px;
height: 60px;
border-radius: 11px;
border: 2px solid #0f82af;
background: transparent;
font-size: 28px;
font-family: MicrosoftYaHei;
color: #08b7b8;
line-height: 60px;
}
& :nth-child(2) {
width: 190px;
height: 60px;
background: #08b7b8;
border-radius: 11px;
font-size: 28px;
font-family: MicrosoftYaHei, MicrosoftYaHei;
font-weight: bold;
color: #ffffff;
line-height: 60px;
}
button:active {
transform: scale(0.9);
}
}
}
</style>

View File

@@ -8,8 +8,9 @@ import {
} from "./handleOrbitControlsChange";
import { saveState, resetState } from "./viewBack";
import addFunction from "./addEvent";
import { editTunnelInit } from "./editTunnelInit";
import { addEquipment, removeEquipment } from "./editEquipment";
import { Group } from "three/examples/jsm/libs/tween.module";
import previewtunnelModeInit from "./previewTunnelInit"
export default class Demo {
// 摄像机看向位置
origin = null;
@@ -26,6 +27,8 @@ export default class Demo {
this._resetState = resetState;
this.addEquipment = addEquipment;
this.removeEquipment = removeEquipment;
this.previewtunnelModeInit = previewtunnelModeInit;
this.editTunnelInit = editTunnelInit;
// 外部可添加函数
this.addFunction = addFunction;
@@ -133,7 +136,7 @@ export default class Demo {
return new Promise((resolve, reject) => {
this.gltfloader = new GLTFLoader();
this.gltfloader.load(
"/tunnelModel/chanel.gltf",
"/tunnelModel/chanel-have-wall.gltf",
(gltf) => {
gltf.scene.traverse((child) => {
this._forModels(child);
@@ -161,6 +164,8 @@ export default class Demo {
this._hoverModel(this.points);
// 可以进行点击
this._ClickModel(this.points);
//将墙壁进行隐藏
this.WallInit()
}
// 在此方法中对模型批量操作,这里遍历附着点
@@ -171,6 +176,7 @@ export default class Demo {
// 改变为基础材质
child.material = new this.THREE.MeshBasicMaterial();
child.scale.set(0.01, 0.01, 0.01);
if (/b[rl]$/.test(child.name)) child.translateZ(2);
// 遍历一个属性是否存在设备
child.hasDevice = false; //初始化
}
@@ -378,10 +384,10 @@ export default class Demo {
// console.log(v);
}
});
const axesHelper = new this.THREE.AxesHelper(100);
// const axesHelper = new this.THREE.AxesHelper(100);
// 改变叶子旋转中心
let x = 0,
y = -0.2,
y = -0.2099,
z = 0;
const wrapper = new this.THREE.Object3D();
wrapper.position.set(x, y, z);
@@ -393,24 +399,23 @@ export default class Demo {
this.equMap.get("equ_fan").add(wrapper);
// 初其他传感器机颜色
this.equMap.get("equ_sensors").scale.set(0.1, 0.1, 0.1);
this.equMap.get("equ_sensors").scale.set(0.2, 0.2, 0.2);
this.equMap.get("equ_sensors").traverse((v) => {
v.material = new this.THREE.MeshBasicMaterial();
v.material.color = new this.THREE.Color("#7e7b7b");
v.material.color = new this.THREE.Color("white");
});
// 初始标签面板
const tag = new this.THREE.Mesh(
new this.THREE.PlaneGeometry(10, 4),
new this.THREE.PlaneGeometry(7, 5),
new this.THREE.MeshBasicMaterial({ color: "white" })
);
this.equMap.get("equ_sensors").rotation.x += Math.PI / 2;
this.equMap.get("equ_sensors").add(tag);
// const axesHelper = new this.THREE.AxesHelper(100);
// this.equMap.get("equ_sensors").add(axesHelper);
tag.name = "tag";
tag.rotation.x = Math.PI / 2;
tag.rotation.z = Math.PI / 2; //旋转这里改变文字顺序
tag.rotation.y = Math.PI;
tag.rotation.z -= Math.PI / 2;
tag.translateZ(2.0);
}
@@ -428,11 +433,6 @@ export default class Demo {
this.distance = distance;
}
loadBackground(hdrLoader, backColorSet) {
// hdrLoader.load("/images/background/background.hdr", (texture) => {
// texture.mapping = this.THREE.EquirectangularReflectionMapping;
// this.scene.background = texture;
// this.scene.environment = texture;
// })
this.scene.background = new this.THREE.TextureLoader().load(
"/images/background/background.png",
function (texture) {
@@ -487,39 +487,16 @@ export default class Demo {
opacityTween(0.1, 0.5, true);
}
}
ThreeConfig = {
code: 0,
data: {
tunnelThreeConfig: [{
equipmentId: 'fan_01',//传感器id
equipmentName: '01',//设备名称
pointName: 'point_005_tl',//附着点名称(定位)
equipmentType: 'fan',//设备类型(类型可根据后端
equipmentValue: 23, //设备存的值
}, {
equipmentId: 'sensors_01',//传感器id
equipmentName: '01',//设备名称
pointName: 'point_009_bl',//附着点名称(定位)
equipmentType: 'sensors',//设备类型(类型可根据后端
equipmentValue: 67, //设备存的值
}],
},
msg: "dda"
}
tunnelConfigEquipment = this.ThreeConfig.data.tunnelThreeConfig
tunnelModeInit() {
for (const item of this.tunnelConfigEquipment) {
//使用api取拿到附着点
let pointmodel = this.scene.getObjectByName(item.pointName)
// pointmodel.hasDevice = true;
let formInfo = {
equipmentType: item.equipmentType, //设备类型
chooseEquipment: item.equipmentName, //设备选择(设备名称)
threshold: item.equipmentValue, //阈值
//初始将墙壁进行隐藏
WallInit() {
for (let line = 1; line <= 20; line++) {
if (line < 10) {
let wall = 'wall_' + '0' + line
this.scene.getObjectByName(wall).visible = false
} else if (line >= 10) {
let wall = 'wall_' + line
this.scene.getObjectByName(wall).visible = false
}
this.addEquipment(pointmodel, formInfo);
}
}
}

View File

@@ -1,3 +1,4 @@
import Demo from "../demo";
import EquipmentTag from "../utils/EquipmentTag";
/**
*
@@ -5,20 +6,24 @@ import EquipmentTag from "../utils/EquipmentTag";
* @param {String} equType "fan" "sensors"
*/
//formInfo需要的信息这里包括了
//equipmentType
//equipmentType
function addEquipment(targetPoint, formInfo) {
if (targetPoint.hasDevice) {
alert("已添加设备");
return;
}
switch (formInfo.equipmentType) {
case "fan":
handleFanEqu.call(this, targetPoint);
case "frequency":
handleFanEqu.call(this, targetPoint, formInfo.chooseEquipment);
break;
case "sensors":
case "sensors_2":
handleOtherEqu.call(this, targetPoint, formInfo.equipmentType);
// 还没有风压阀的模型,所以先用其他设备传感器实现
// case "valve":
// handleValveEqu.call(this, targetPoint, formInfo.chooseEquipment);
// break;
case "windPressure":
case "sensor":
handleOtherEqu.call(this, targetPoint, formInfo.chooseEquipment);
break;
default:
break;
@@ -27,48 +32,100 @@ function addEquipment(targetPoint, formInfo) {
// 标识设备信息
this.clearTagsObj();
}
// function handleValveEqu(targetPoint, equipmentInfo) {
// }
// 其他传感器
function handleOtherEqu(targetPoint, EqeName) {
function handleOtherEqu(targetPoint, equipmentInfo) {
const equMesh = this.equMap.get("equ_sensors").clone();
const worldP = targetPoint.getWorldPosition(new this.THREE.Vector3());
equMesh.position.copy(worldP);
//设备添加标签
const tag = EquipmentTag(EqeName === "sensors" ? "风压传感器" : "普通传感器");
const tag = EquipmentTag(equipmentInfo.label);
console.log(equipmentInfo);
equMesh.getObjectByName("tag").material = tag;
if (/tr$/.test(targetPoint.name)) {
equMesh.rotation.z = -(3 * Math.PI) / 4;
targetPoint.scale.set(0.03, 0.03, 0.03);
equMesh.getObjectByName("tag").rotation.z += Math.PI; ///注意在原来的基础上加
equMesh.rotation.z = -Math.PI / 2;
equMesh.rotation.y -= Math.PI / 4;
equMesh.getObjectByName("tag").rotation.z += Math.PI; // 注意在原来的基础上加
} else if (/tl$/.test(targetPoint.name)) {
equMesh.rotation.z = -(5 * Math.PI) / 4;
targetPoint.scale.set(0.03, 0.03, 0.03);
equMesh.rotation.z = Math.PI / 2;
equMesh.rotation.y += Math.PI / 4;
equMesh.getObjectByName("tag").rotation.z += Math.PI;
} else if (/tc$/.test(targetPoint.name)) {
equMesh.rotation.z = Math.PI;
targetPoint.scale.set(0.03, 0.03, 0.03);
equMesh.getObjectByName("tag").rotation.z += Math.PI;
} else if (/br$/.test(targetPoint.name)) {
equMesh.rotation.z = -Math.PI / 2;
equMesh.translateX(-1);
equMesh.getObjectByName("tag").rotation.z += Math.PI; ///注意在原来的基础上加
targetPoint.scale.set(0.03, 0.03, 0.075);
equMesh.getObjectByName("tag").rotation.z += Math.PI;
} else if (/bl$/.test(targetPoint.name)) {
equMesh.rotation.z = Math.PI / 2;
equMesh.translateX(1);
targetPoint.scale.set(0.03, 0.03, 0.075);
equMesh.getObjectByName("tag").rotation.z += Math.PI;
}
targetPoint.scale.set(0.03, 0.06, 0.05);
this.scene.add(equMesh);
targetPoint.visible = false;
// 保存该设备id后期直接从附附着点进行删除
targetPoint.hasWall = true
//处理风压阀的代码片段
let lineIndex = targetPoint.name.slice(7, 9)
for (let line = Number(lineIndex); line <= 20; line++) {
if (line < 10) {
let wall = 'wall_' + '0' + line
this.scene.getObjectByName(wall).visible = true
let point = 'point_' + '00' + line + '_tc'
console.log(this.scene.getObjectByName(point));
this.scene.getObjectByName(point).visible = false
this.scene.getObjectByName(point).layers.set(-2)
if (this.scene.getObjectByName(point).hasDevice == true) {
let EquID = this.scene.getObjectByName(point).info.id
this.scene.getObjectById(EquID).visible = false
}
} else if (line >= 10) {
let wall = 'wall_' + line
this.scene.getObjectByName(wall).visible = true
let point = 'point_' + '0' + line + '_tc'
this.scene.getObjectByName(point).visible = false
this.scene.getObjectByName(point).layers.set(-2)
if (this.scene.getObjectByName(point).hasDevice == true) {
let EquID = this.scene.getObjectByName(point).info.id
this.scene.getObjectById(EquID).visible = false
}
}
//处理21行的设备和附着点
let point = 'point_' + '0' + 21 + '_tc'
this.scene.getObjectByName(point).visible = false
this.scene.getObjectByName(point).layers.set(-2)
if (this.scene.getObjectByName(point).hasDevice == true) {
let EquID = this.scene.getObjectByName(point).info.id
this.scene.getObjectById(EquID).visible = false
}
}
// this.scene.getObjectByName('wall_01').visible = true
// console.log(this.scene);
// 保存该设备模型id后期直接从附附着点进行删除
targetPoint.info = {
id: equMesh.id,
...equipmentInfo,
};
//下面进行模拟分压阀的添加效果
// 1.先取出当前点击添加设备锚点的行数
// 2.再进行字符串拼接出墙的名字使用scene.getObjectByName(墙名去获取)
// 3.加上循环固定到20结束就可以了将点击的函数到20的函数的墙全部设为visable设为可见,还需要将中间的那行设备和附着点全部设为隐藏,且层级变低,无法选中
}
// 风机
function handleFanEqu(targetPoint, speed = Math.random().toFixed(1) * 1000) {
function handleFanEqu(
targetPoint,
equipmentInfo,
speed = Math.random().toFixed(1) * 1000
) {
// 由于风机比较多,每个风机转速不一直,保存在一个数中遍历
if (!this.fanSpinArray) {
this.fanSpinArray = [];
@@ -84,6 +141,7 @@ function handleFanEqu(targetPoint, speed = Math.random().toFixed(1) * 1000) {
// 保存该设备id后期直接从附附着点进行删除
targetPoint.info = {
id: equMesh.id, //模型在场景id
...equipmentInfo,
};
// 定义风机旋转
@@ -101,11 +159,50 @@ function handleFanEqu(targetPoint, speed = Math.random().toFixed(1) * 1000) {
// 删除传感器
function removeEquipment(targetPoint) {
//删除设备这里需要再进行处理,就是根据附着点(附着点一定保存了当前添加设备的信息了)判断是不是分压阀,然后继续一样的隐藏
if (!targetPoint.hasDevice) return;
if (targetPoint.hasWall == true) {
let lineIndex = targetPoint.name.slice(7, 9)
for (let line = Number(lineIndex); line <= 20; line++) {
if (line < 10) {
let wall = 'wall_' + '0' + line
this.scene.getObjectByName(wall).visible = false
let point = 'point_' + '00' + line + '_tc'
console.log(this.scene.getObjectByName(point));
this.scene.getObjectByName(point).visible = true
this.scene.getObjectByName(point).layers.set(0)
if (this.scene.getObjectByName(point).hasDevice == true) {
this.scene.getObjectByName(point).visible = false
let EquID = this.scene.getObjectByName(point).info.id
this.scene.getObjectById(EquID).visible = true
}
} else if (line >= 10) {
let wall = 'wall_' + line
this.scene.getObjectByName(wall).visible = false
let point = 'point_' + '0' + line + '_tc'
this.scene.getObjectByName(point).visible = true
this.scene.getObjectByName(point).layers.set(0)
if (this.scene.getObjectByName(point).hasDevice == true) {
this.scene.getObjectByName(point).visible = false
let EquID = this.scene.getObjectByName(point).info.id
this.scene.getObjectById(EquID).visible = true
}
}
//处理21行的设备和附着点
let point = 'point_' + '0' + 21 + '_tc'
this.scene.getObjectByName(point).visible = true
this.scene.getObjectByName(point).layers.set(0)
if (this.scene.getObjectByName(point).hasDevice == true) {
let EquID = this.scene.getObjectByName(point).info.id
this.scene.getObjectById(EquID).visible = true
}
}
}
const mesh = this.scene.getObjectById(targetPoint.info.id);
this.scene.remove(mesh);
targetPoint.visible = true;
targetPoint.hasDevice = false;
targetPoint.scale.set(0.01, 0.01, 0.01);
delete targetPoint.info; // 清空设备信息
}
export { addEquipment, removeEquipment };

View File

@@ -0,0 +1,21 @@
export function editTunnelInit(equipmentList) {
//初始化将墙壁隐藏起来
// for (let line = 1; line <= 20; line++) {
// if (line < 10) {
// let wall = 'wall_' + '0' + line
// this.scene.getObjectByName(wall).visible = false
// } else if (line >= 10) {
// let wall = 'wall_' + line
// this.scene.getObjectByName(wall).visible = false
// }
// }
equipmentList.forEach((item) => {
const formInfo = {
equipmentType: item.equipmentType, //设备类型
chooseEquipment: item.chooseEquipment, //设备选择(设备名称)
threshold: item.threshold,
};
this.addEquipment(this.scene.getObjectByName(item.position), formInfo);
});
}

View File

@@ -0,0 +1,32 @@
export default function previewtunnelModeInit(equipmentList) {
//初始化将墙壁隐藏起来
// for (let line = 1; line <= 20; line++) {
// if (line < 10) {
// let wall = 'wall_' + '0' + line
// this.scene.getObjectByName(wall).visible = false
// } else if (line >= 10) {
// let wall = 'wall_' + line
// this.scene.getObjectByName(wall).visible = false
// }
// }
equipmentList.forEach((item) => {
const formInfo = {
equipmentType: item.equipmentType, //设备类型
chooseEquipment: item.chooseEquipment, //设备选择(设备名称)
threshold: item.threshold,
};
this.addEquipment(this.scene.getObjectByName(item.position), formInfo);
});
//进行预览和编辑模式的一些操作
if (this.isedit == false) {
this.scene.traverse(function (item) {
if (item.name.includes('point')) {
if (item.hasDevice == false) {
item.visible = false
item.layers.set(-1)
}
}
})
}
}

View File

@@ -1,18 +1,25 @@
import * as THREE from "three";
/**
* 返回一个带有文字的几何平面
* @param {String} text 把输入的文字转化为base64的img图片
* 返回一个带有文字的的#3D材质
* 把输入的文字转化为base64的img图片
* @param {String} text
* @param {String} param 传感器实时检测参数
*/
export default function (text, width = 100, height = 50) {
export default function (text = "", param = "50", width = 80, height = 50) {
const canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext("2d");
ctx.fillStyle = "white";
ctx.fillStyle = "#00b8ec";
ctx.fillRect(0, 0, width, height);
ctx.fillStyle = "black";
ctx.font = "16px serif";
ctx.fillText(text, (100 - text.length * 16.6) / 2, 30);
ctx.fillStyle = "white";
ctx.font = "12px serif";
ctx.fillText(text, (100 - text.length * 15) / 2, 20);
ctx.fillText(param, (100 - param.length * 10) / 2, 40);
ctx.strokeStyle = "white";
ctx.moveTo(0, 26);
ctx.lineTo(100, 26);
ctx.stroke();
const base64 = canvas.toDataURL();
return new THREE.MeshBasicMaterial({
map: new THREE.TextureLoader().load(base64),

View File

@@ -1,17 +1,27 @@
<template>
<div id="used-ele" :style="{ backgroundImage: 'url(' +getImageUrl(bgImage)+')' }" @click="handleOpenChart">
<div class="content" >
<div class="content">
<div class="item">
<div></div>
<span>当月用电量{{ electricityConsumptionMonthly }}v</span>
<div class="container" ref="length">
<div class="value" ref="valueA"></div>
</div>
<div>
<div class="icon"></div>
<span>当月用电量{{ electricityConsumptionMonthly }}v</span>
</div>
</div>
<div class="item">
<div></div>
<span>当月节省量{{ monthlySavings }}v</span>
<div class="container" ref="length">
<div class="value" ref="valueB"></div>
</div>
<div>
<div class="icon"></div>
<span>当月节省量{{ monthlySavings }}v</span>
</div>
</div>
</div>
<div class="digital-tunnel">
<el-dialog v-model="isVisited" width="2175px" :modal="false">
<el-dialog :close-on-click-modal="false" v-model="isVisited" width="2175px" :modal="false">
<div class="left-top-icon"></div>
<div class="right-top-icon"></div>
<div class="top-tag">
@@ -28,10 +38,10 @@
<div class="value">345v</div>
</div>
</div>
<div class="chat-dialog">
<div id="containerEle"></div>
<div style="width: 1px;"></div>
</div>
<div class="chat-dialog">
<div id="containerEle"></div>
<div style="width: 1px;"></div>
</div>
<div class="left-bottom-icon"></div>
<div class="right-bottom-icon"></div>
</el-dialog>
@@ -50,7 +60,10 @@ let myEcharts = reactive({});
const isVisited = ref(false);
const eleData = ref([])
const electricityConsumptionMonthly = ref()
const monthlySavings = ref(55555)
const monthlySavings = ref(4000)
const length = ref(null);
const valueA = ref();
const valueB = ref(monthlySavings.value);
const bgImage = computed(() =>
isVisited.value
? "bpq_active.png"
@@ -58,6 +71,7 @@ const bgImage = computed(() =>
);
watch(() => props.eleData, (now) => {
getBasicData(now.frequencyChangerList)
setValueA()
}, {deep: true});
watch(() => props.list, (now) => {
eleData.value.forEach(item => {
@@ -72,7 +86,20 @@ watch(() => props.list, (now) => {
}
})
})
setValueA()
}, {deep: true});
onMounted(()=>{
setValueA()
setValueB()
})
const setValueA=()=> {
let width = (electricityConsumptionMonthly.value * length.value.offsetHeight) / 10000;
valueA.value.style.height = `${width}px`;
}
const setValueB=()=> {
let width = (4000 * length.value.offsetHeight) / 10000;
valueB.value.style.height = `${width}px`;
}
const getBasicData = (data) => {
let tranObj = {}
let tranArr = []
@@ -112,7 +139,7 @@ const initChart = () => {
fontSize: 45
},
itemWidth: 30,
itemHeight:30,
itemHeight: 30,
icon: "circle",
},
//离容器四侧的距离
@@ -179,13 +206,13 @@ const initChart = () => {
//配置项
series: [
{
name:'日用电量',
name: '日用电量',
data: [120, 200, 150, 80, 70, 110, 130, 100, 140, 130, 160, 150, 90, 130, 110, 120, 150, 140, 130, 120, 110, 100, 90, 80, 100, 140, 130, 160, 150, 24, 50],
type: 'bar'
},
{
name:'日节省量',
data: [140, 130, 160, 150, 90, 130, 110, 120, 150, 140, 130, 120, 110, 100, 90, 80, 100, 140, 130, 160, 150, 24, 50,120, 200, 150, 80, 70, 110, 130, 100, ],
name: '日节省量',
data: [140, 130, 160, 150, 90, 130, 110, 120, 150, 140, 130, 120, 110, 100, 90, 80, 100, 140, 130, 160, 150, 24, 50, 120, 200, 150, 80, 70, 110, 130, 100,],
type: 'bar'
},
]
@@ -203,16 +230,17 @@ const getImageUrl = (name) => {
<style lang="scss" scoped>
:deep(.el-dialog__header) {
padding: 0!important;
padding: 0 !important;
}
#used-ele {
cursor: pointer;
width: 830px;
height: 90px;
height: 373px;
color: aliceblue;
position: absolute;
//top: 1980px;
top: 1420px;
top: 1220px;
//top: 1680px;
left: 68px;
background-image: url(../../../assets/images/usedEle/bg.png);
padding: 21px 62px 35px 62px;
@@ -233,94 +261,43 @@ const getImageUrl = (name) => {
align-items: center;
justify-content: space-between;
cursor: pointer;
.item {
height: 37px;
margin-top: 44px;
font-size: 28px;
font-family: MicrosoftYaHei;
color: #38CAFB;
line-height: 37px;
display: flex;
flex-direction: column;
align-items: center;
.container {
width: 60px;
height: 210px;
border-radius: 31px;
border: 1px solid #0f82af;
position: relative;
margin-bottom: 20px;
> div {
margin-right: 10px;
width: 29px;
height: 33px;
background-image: url(../../../assets/images/usedEle/white-ele.png);
.value {
position: absolute;
bottom: 0;
width: 60px;
height: inherit;
background: #60ddde;
border-radius: 31px;
transition: width 0.5s linear 0s;
}
}
> div:nth-child(2) {
display: flex;
//ul {
// font-size: 14px;
//
// li {
// display: flex;
// align-items: center;
// //width: 270px;
// height: 37px;
// font-size: 28px;
// color: #38CAFB;
// line-height: 37px;
// margin-bottom: 25px;
// padding-left: 5px;
//
// &:hover {
// cursor: pointer;
// width: 280px;
// //height: 46px;
// background: #2E5589;
// border-radius: 7px;
// color: #FBF7F4;
//
// > div {
// background-image: url(../../../assets/images/usedEle/white-ele.png);
// }
// }
//
// > div {
// width: 29px;
// height: 33px;
// background-image: url(../../../assets/images/usedEle/white-ele.png);
// margin-right: 14px;
// }
//
// &:nth-child(2) {
// > div {
// background-image: url(../../../assets/images/usedEle/icon-month.png);
// }
//
// &:hover {
// > div {
// background-image: url(../../../assets/images/usedEle/white-ele.png);
// }
// }
// }
//
// &:nth-child(3) {
// > div {
// background-image: url(../../../assets/images/usedEle/icon-day.png);
// }
//
// &:hover {
// > div {
// background-image: url(../../../assets/images/usedEle/white-ele.png);
// }
// }
// }
// }
//
// & > :nth-child(2) {
// color: #4bcb6b;
// }
//
// & > :nth-child(3) {
// color: #4bcbbc;
// }
//}
.icon {
margin-right: 10px;
width: 29px;
height: 33px;
background-image: url(../../../assets/images/usedEle/white-ele.png);
}
}
}
//& > :nth-child(2) {
// transform: translateX(25px);
//}
}
}
</style>

View File

@@ -6,7 +6,7 @@
@click="handleOpenChart(item,index+1)"/>
</div>
<div class="digital-tunnel">
<el-dialog v-model="isVisited" :title="windSort+'号风压监控数据'" width="2175px" :modal="false">
<el-dialog :close-on-click-modal="false" v-model="isVisited" :title="windSort+'号风压监控数据'" width="2175px" :modal="false">
<div class="left-top-icon"></div>
<div class="right-top-icon"></div>
<div class="chat-dialog">

View File

@@ -21,16 +21,16 @@ const routes = [
meta: {
title: '首页',
breadcrumb: true
}
},
},
{
path: '/preview',
name: 'tunnelpreview',
component: () => import('@/views/tunnel/preview.vue'),
path: '/edit',
name: 'tunneledit',
component: () => import('@/views/edit/edit.vue'),
meta: {
title: '预览首页',
title: '编辑首页',
breadcrumb: true
}
},
},
{
path: '/debug',
@@ -51,7 +51,7 @@ const routes = [
}
},
{
path: '/user',
path: '/user/:siteId(\\d+)',
name: 'user',
component: () => import('@/views/user/index.vue'),
meta: {

View File

@@ -0,0 +1,54 @@
import { defineStore } from "pinia";
import { ref } from "vue";
import { saveEquipment, initSceneData } from "../api/tunnelScene";
import { ElMessage } from "element-plus";
export const useModelSceneStore = defineStore("modelSceneData", () => {
let equipmentList = ref("[]");
function initData(tunnelId = 1) {
initSceneData(tunnelId)
.then((res) => {
equipmentList.value = res.data.tunnelThreeConfig;
})
.catch((Error) => {
ElMessage.error("场景初始化异常!");
});
}
//默认初始化1号隧道
function saveSceneData(constructionLength, tunnelId, modelData) {
const temp = JSON.parse(equipmentList.value);
temp.push(modelData);
equipmentList.value = JSON.stringify(temp);
console.log("save:", equipmentList.value);
return saveEquipment(constructionLength, tunnelId, equipmentList.value);
}
// 删除设备
function deleteEquipment(constructionLength, tunnelId, EquipmentId) {
let temp = JSON.parse(equipmentList.value);
temp = temp.filter(
(item) => item.chooseEquipment.equipmentId != EquipmentId
);
equipmentList.value = JSON.stringify(temp);
return saveEquipment(constructionLength, tunnelId, equipmentList.value);
}
function getEquipmentList(tunnelId = 1) {
return new Promise((resolve, reject) => {
initSceneData(tunnelId)
.then((res) => {
equipmentList.value = res.data.tunnelThreeConfig;
resolve(JSON.parse(equipmentList.value));
})
.catch((Error) => {
ElMessage.error("场景初始化异常!");
reject();
});
});
}
return {
equipmentList,
initData,
saveSceneData,
deleteEquipment,
getEquipmentList,
};
});

View File

@@ -26,6 +26,9 @@
<el-menu-item index="3">
<span>其他传感器</span>
</el-menu-item>
<el-menu-item index="4">
<span>分流器</span>
</el-menu-item>
</el-menu>
</div>
<div class="box-right" v-if="changeIndex==1">
@@ -208,6 +211,61 @@
</div>
</div>
</div>
<div class="box-right" v-if="changeIndex==4">
<div class="device-title">
<span>分流器设备管理</span>
<div class="collection-frequency">
<span>采集频率</span>
<span class="units">
<input
type="number"
min="0"
v-model="collectionFrequency"
/>
</span>
</div>
</div>
<div class="device-table">
<el-table stripe
style="border-bottom: 1px #06e5e5 solid;background-color: #011c29;--el-table-border-color: none;"
:header-cell-style="{backgroundColor: '#064B66',color: '#fff',fontSize: '40px',borderBottom: 'none' }"
:cell-style="{textAlign: 'center',borderBottom: 'none'}" :data="fenliuData">
<el-table-column prop="name" label="设备名称" align="center"/>
<el-table-column prop="offset" label="偏移量" align="center"/>
<el-table-column prop="thresholdValue" label="阈值" align="center">
<template #default="scope">
<el-input placeholder="请输入阈值" v-model="scope.row.thresholdValue"></el-input>
</template>
</el-table-column>
<el-table-column prop="state" label="状态" align="center">
<template #default="scope">
<div class="switch">
<div
:class="{ active: scope.row.state }"
@click=" scope.row.state = true"
>
启动
</div>
<div
:class="{ active: ! scope.row.state }"
@click=" scope.row.state = false"
>
停止
</div>
</div>
</template>
</el-table-column>
</el-table>
</div>
<div class="btns">
<div class="cancel-btn" @click="isVisited=false">
取消
</div>
<div class="sure-btn" @click="router.back(-1)">
确定
</div>
</div>
</div>
</div>
</div>
</div>
@@ -381,6 +439,24 @@ const otherData = [
state: false,
}
]
const fenliuData = [
{
name: '1号分流器',
offset: '2342',
thresholdValue: '2342',
state: false,
}, {
name: '2号分流器',
offset: '2342',
thresholdValue: '2342',
state: false,
}, {
name: '3号分流器',
offset: '2342',
thresholdValue: '2342',
state: false,
}
]
const handleChangeMenu = (e) => {
console.log('切换', e)
changeIndex.value = e
@@ -476,6 +552,9 @@ const handleChangeMenu = (e) => {
&:nth-child(2) {
letter-spacing: 120px;
}
&:last-child{
letter-spacing: 40px;
}
}
}
}

View File

@@ -7,7 +7,15 @@
<div class="top-right">
<div class="current-site">
当前站点<span>{{ currentSite }}</span>
<div class="toggle"></div>
<el-dropdown trigger="click" @command="handleCommand">
<div class="toggle"></div>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item v-for="item in siteList" :key="item.value" :command="item">{{ item.label
}}</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
<div class="current-user">
上午好<span>{{ currentUser }}</span>
@@ -16,7 +24,8 @@
</div>
</div>
</div>
<tunnel-scene id="tunnel-box" />
<!-- 这里就导入正常的编辑模式就是我们之前写的部分 -->
<tunnel-scene id="tunnel-box" :isedit="true" :tunnelId="tunnelId" />
</div>
</template>
<script setup>
@@ -24,11 +33,11 @@ import TunnelScene from "@/components/content/tunnelScene/TunnelScene.vue";
import ManageBtn from "@/components/manageBtn/index.vue";
import ManageLength from "@/components/manageLength/index.vue";
import { dateFormat } from "@/utils/date.js";
import { onMounted } from "vue";
import { getToken } from "@/utils/auth";
import { useAuthStore } from "@/store/userstore.js";
import { getLargeScreen, getLargeScreenInfo } from "@/api/largeScreen";
import { getLargeScreen, getLargeScreenInfo, getTunnelBySiteId } from "@/api/largeScreen";
import { ElMessageBox } from "element-plus";
import { getUserInfo } from "@/api/login";
const authStore = useAuthStore();
const router = useRouter();
@@ -36,11 +45,13 @@ const selectIndex = ref(-1);
const showFan = ref(false);
const drawerLeft = ref(true);
const drawerRight = ref(true);
const currentSite = ref("松江站");
const currentUser = ref("admin");
const currentSite = ref("");
const siteList = ref([])
const currentUser = ref("");
const currentDate = ref(dateFormat());
const tunnelBtn = ref();
const tunnelList = ref([]);
const tunnelId = ref(0);
const routeList = ref([]);
let socket = reactive("");
const serialNumber = ref("SC00DY00GH00ELBT");
@@ -59,34 +70,72 @@ onMounted(() => {
nextTick(() => {
showFan.value = true;
});
getUser()
getOtherInfo()
getScreenInfo();
});
const getOtherInfo = async () => {
await getLargeScreenInfo().then((res) => {
const getUser = () => {
getUserInfo().then(res => {
currentUser.value = res.data.user.userName
})
}
const getOtherInfo = () => {
getLargeScreenInfo().then((res) => {
if (res?.code === 1000) {
routeList.value = res.data.routeList
currentSite.value = res.data.siteOption[0].label
tunnelList.value = res.data.tunnelOption
siteList.value = res.data.siteOption
tunnelId.value = res.data.tunnelOption[0].value
getTunnel(res.data.siteOption[0].value)
getScreenInfo(tunnelId.value);
}
});
};
const getScreenInfo = async () => {
await getLargeScreen(1).then((res) => {
const getScreenInfo = async (id) => {
await getLargeScreen(id).then((res) => {
if (res?.code === 1000) {
largeScreenData.value = res.data;
}
});
};
//id
const getTunnel = (id) => {
getTunnelBySiteId(id).then((res) => {
if (res?.code === 1000) {
tunnelList.value = res.data
getScreenInfo(res.data[0].value)
}
});
}
const changeTunnel = (e) => {
tunnelId.value = e
let newObj = {}
tunnelList.value.forEach((item, index) => {
if (index === e) {
newObj = item
}
})
getScreenInfo(newObj.value)
nextTick(() => {
showFan.value = true;
});
}
const manageSelect = (index) => {
console.log("首页点击-", index);
if (index === 0) {
router.push("/site");
} else if (index === 1) {
router.push("/tunnel/1");
} else if (index === 2) {
router.push("/user");
}
};
const handleCommand = (item) => {
console.log('commads', item)
currentSite.value = item.label
getTunnel(item.value)
}
const closeLeft = () => {
drawerLeft.value = !drawerLeft.value;
};
@@ -154,7 +203,53 @@ const closeSocket = () => {
};
initWebSocket();
</script>
<style lang="scss">
.el-dropdown__popper.el-popper {
background: transparent;
//border: none;
border: 1px solid #0E7DAA;
border-radius: 10px;
}
.is-light {
background: rgba(7, 35, 72, 0.9);
}
.el-popper {
padding: 20px;
margin-left: 50px;
width: 150px;
background: rgba(7, 35, 72, 0.9);
.el-scrollbar__wrap {
.el-dropdown__list {
.el-dropdown-menu {
background-color: rgba(7, 35, 72, 0.9);
border-radius: 10px;
padding: 5px;
.el-dropdown-menu__item {
color: #FFFFFF;
//border:none;
padding: 5px;
border-bottom: 1px solid #05FEFF;
&:last-child {
border-bottom: none;
}
}
.el-dropdown-menu__item.hover,
.el-dropdown-menu__item:hover {
background-color: transparent !important;
color: #F7B500;
}
}
}
}
}
</style>
<style lang="scss" scoped>
#main {
height: 100%;

View File

@@ -27,14 +27,14 @@
<div class="box-center">
<div class="left-img"></div>
<div class="right-tunnel">
<div>隧道数量{{ item.info.tunnelNum }}
<div>隧道数量{{ item.tunnelList.length }}
</div>
<div>
<div class="tunnel" v-if="item.info.tunnelName">
<div>{{ item.info.tunnelName }}</div>
<div class="tunnel-icon"></div>
<div>施工长度{{ item.info.constructionLength }}</div>
<div>实现长度{{ item.info.implementationLength }}公里
<div>实现长度{{ item.info.totalLength }}公里
</div>
</div>
<div class="tunnel-add" @click="goToAddTunnel(item.siteId)">
@@ -57,7 +57,7 @@
<div style="cursor: pointer">添加站点</div>
</div>
</div>
<el-dialog v-model="isVisited" width="1958px">
<el-dialog :close-on-click-modal="false" v-model="isVisited" width="1958px">
<div class="siteId">
<span>{{ title }}</span>
</div>
@@ -101,41 +101,40 @@ import {ElMessage, ElMessageBox} from "element-plus";
const router = useRouter()
const siteList = ref([
{
siteName: '松江站',
info:{
tunnelNum: 1,
tunnelName: '一号隧道',
constructionLength: 500,
implementationLength: 10
}
},
{
siteName: '松江站',
info:{
tunnelNum: 1,
tunnelName: '二号隧道',
constructionLength: 500,
implementationLength: 10
}
},
{
siteName: '松江站',
info:{
tunnelNum: 1,
tunnelName: '三号隧道',
constructionLength: 500,
implementationLength: 10
}
}
// {
// siteName: '松江站',
// info:{
// tunnelNum: 1,
// tunnelName: '一号隧道',
// constructionLength: 500,
// implementationLength: 10
// }
// },
// {
// siteName: '松江站',
// info:{
// tunnelNum: 1,
// tunnelName: '二号隧道',
// constructionLength: 500,
// implementationLength: 10
// }
// },
// {
// siteName: '松江站',
// info:{
// tunnelNum: 1,
// tunnelName: '三号隧道',
// constructionLength: 500,
// implementationLength: 10
// }
// }
])
const siteIds = ref([])
const siteNameList = ref([])
const info = ref({
tunnelNum: 0,
tunnelName: '',
constructionLength: 0,
implementationLength: 0
totalLength: 0
})
const title = ref('新增站点')
const isClick = ref(false);
@@ -160,16 +159,18 @@ const getList = () => {
...pageInfo
}).then((res) => {
total.value = res.data.total;
showAddIcon.value = total.value !== 6;
showAddIcon.value = total.value % 6!==0;
res.data.rows.map(item => {
if (item.tunnelList === null || item.tunnelList === []) {
if (item.tunnelList === null || item.tunnelList.length === 0) {
item.info = info.value
}else{
item.info=item.tunnelList[0]
}
item.checked = false
})
// siteList.value = res.data.rows;
siteList.value = res.data.rows;
console.log('res',siteList.value)
});
}
getList()
@@ -191,8 +192,8 @@ const handleClickSite = (type) => {
}
}
const goToAddTunnel = (siteId) => {
// router.push('/tunnel/' + siteId)
router.push('/tunnel/1')
router.push('/tunnel/' + siteId)
// router.push('/tunnel/1')
}
//重置from表单
const restFrom = () => {
@@ -205,11 +206,11 @@ const restFrom = () => {
const handleEdit = (item) => {
title.value = '编辑站点'
restFrom()
// getSiteDetail(item.siteId).then((res) => {
// form.value = res.data;
getSiteDetail(item.siteId).then((res) => {
form.value = res.data;
form.value = item;
isVisited.value = true
// });
});
}
const handleAdd = () => {
@@ -218,21 +219,21 @@ const handleAdd = () => {
isVisited.value = true
}
const handleSubmit = (instance) => {
// if (!instance) return
// instance.validate(async (valid) => {
// if (!valid) return
// if (title.value === '编辑站点') {
// editSite(form.value).then(() => {
// isVisited.value = false
// getList()
// });
// } else {
// addSite(form.value).then(() => {
if (!instance) return
instance.validate(async (valid) => {
if (!valid) return
if (title.value === '编辑站点') {
editSite(form.value).then(() => {
isVisited.value = false
// getList()
// });
// }
// })
getList()
});
} else {
addSite(form.value).then(() => {
isVisited.value = false
getList()
});
}
})
}
const handleChooseAll=()=>{
siteList.value.map(item=>{
@@ -249,16 +250,16 @@ const handleMoreDelete = () => {
type: 'warning',
customClass: 'delBox'
}).then(() => {
// deleteSite(siteIds.value).then(res => {
// if (res.code === 1000) {
// ElMessage.success(res.msg)
// getList()
// siteIds.value = []
// siteNameList.value = []
// } else {
// ElMessage.error(res.msg)
// }
// })
deleteSite(siteIds.value).then(res => {
if (res.code === 1000) {
ElMessage.success(res.msg)
getList()
siteIds.value = []
siteNameList.value = []
} else {
ElMessage.error(res.msg)
}
})
})
}
}

View File

@@ -19,7 +19,8 @@
</div>
</div>
<div class="box-content">
<div class="site-box" v-for="item in tunnelList" :key="item.tunnelId">
<div class="site-box" v-for="item in tunnelList" :key="item.tunnelId"
@click="handleGoToEditTunnel(item.tunnelId)">
<div class="top">
<span>{{ item.tunnelName }}</span>
<span>施工长度500米 隧道长度10公里</span>
@@ -29,11 +30,11 @@
<div>
<div class="left-img"></div>
<div>
<div class="edit-btn" @click="handleEdit">
<div class="edit-btn" @click.stop="handleEdit">
<div class="edit-icon"></div>
<div>隧道编辑</div>
</div>
<div class="edit-btn" @click="handleEditDevice">
<div class="edit-btn" @click.stop="handleEditDevice">
<div class="edit-icon-two"></div>
<div>设备管理</div>
</div>
@@ -58,7 +59,7 @@
<div style="cursor: pointer">添加隧道</div>
</div>
</div>
<el-dialog v-model="isVisited" width="1958px">
<el-dialog :close-on-click-modal="false" v-model="isVisited" width="1958px">
<div class="siteId">
<span>{{ title }}</span>
</div>
@@ -87,7 +88,7 @@
</el-dialog>
<div class="pagination">
<span>首页</span>
<el-pagination background :page-size="6" :total="50" prev-text="上一页" next-text="下一页" layout="prev, pager, next" />
<el-pagination background :page-size="6" :total="50" prev-text="上一页" next-text="下一页" layout="prev, pager, next"/>
<span>尾页</span>
</div>
</div>
@@ -219,9 +220,16 @@ const form = ref({
tunnelLength: '',
remarks: ''
});
const handleChooseAll=()=>{
tunnelList.value.map(item=>{
item.checked=!item.checked
const isEdit = ref(false)
const bgImage = computed(() => (isEdit.value ? "zdgl_bj.png" : "sdgl_bjtq.png"));
const handleGoToEditTunnel = (tunnelId) => {
isEdit.value = true
router.push('/edit')
}
const handleChooseAll = () => {
tunnelList.value.map(item => {
item.checked = !item.checked
})
}
const handleEdit = () => {
@@ -403,6 +411,7 @@ const handleMoreDelete = () => {
justify-content: space-between;
box-sizing: border-box;
overflow: hidden;
.add-box {
cursor: pointer;
font-weight: bold;
@@ -422,15 +431,20 @@ const handleMoreDelete = () => {
}
.site-box {
cursor: pointer;
margin-top: 50px;
margin-right: 1.5%;
padding: 40px 30px;
width: 925px;
height: 550px;
background-image: url('@/assets/images/site/zdgl_bj.png');
background-image: url('@/assets/images/tunnel/zdgl_bj.png');
//box-sizing: border-box;
position: relative;
&:hover {
background-image: url('@/assets/images/tunnel/sdgl_bjtq.png');
}
&:nth-child(4n) {
margin-right: 0;
}
@@ -588,16 +602,15 @@ const handleMoreDelete = () => {
:deep(.btn-prev) {
background-color: transparent;
font-size: 38px;
font-family: MicrosoftYaHei, MicrosoftYaHei;
font-weight: bold;
color: #60DDDE;
margin-right: 20px;
}
:deep(.btn-next) {
background-color: transparent;
font-size: 38px;
font-family: MicrosoftYaHei, MicrosoftYaHei;
font-weight: bold;
color: #60DDDE;
margin-left: 30px;
@@ -610,7 +623,6 @@ const handleMoreDelete = () => {
border-radius: 50%;
color: #071F40;
font-size: 38px;
font-family: MicrosoftYaHei, MicrosoftYaHei;
font-weight: bold;
}
@@ -625,7 +637,6 @@ const handleMoreDelete = () => {
border-radius: 50%;
background-color: transparent;
font-size: 38px;
font-family: MicrosoftYaHei, MicrosoftYaHei;
font-weight: bold;
color: #60DDDE;
}

View File

@@ -1,7 +1,7 @@
<template>
<div id="main">
<div class="box-top">
<manage-btn v-model="selectIndex" @select="manageSelect" :list="routeList"/>
<manage-btn v-model="selectIndex" @select="manageSelect" :list="routeList" />
<div class="tunnel-title"></div>
<manage-length class="tunnel-length"></manage-length>
<div class="top-right">
@@ -11,7 +11,8 @@
<div class="toggle"></div>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item v-for="item in siteList" :key="item.value" :command="item">{{ item.label }}</el-dropdown-item>
<el-dropdown-item v-for="item in siteList" :key="item.value" :command="item">{{ item.label
}}</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
@@ -23,23 +24,26 @@
</div>
</div>
</div>
<tunnel-scene id="tunnel-box"/>
<!-- <tunnel-scene id="tunnel-box" :isedit="false" /> -->\
<!-- 一进去的话应该是预览模式所以引入这个组件 -->
<preview-scene id="tunnel-box" :isedit="false" :tunnelId="tunnelId"></preview-scene>
<div class="left">
<el-drawer v-model="drawerLeft" direction="ltr" modal-class="modal-box" :modal="false" :show-close="false"
:close-on-click-modal="false" :close-on-press-escape="false">
<fan-info v-if="showFan" :list="socketData.leftData" :fan-data="largeScreenData"/>
<transducer-list v-if="showFan" :list="socketData.leftData" :transducer-data="largeScreenData"/>
<used-ele v-if="showFan" :list="socketData.leftData" :ele-data="largeScreenData"/>
:close-on-click-modal="false" :close-on-press-escape="false">
<fan-info v-if="showFan" :list="socketData.leftData" :fan-data="largeScreenData"
:transducer-data="largeScreenData" />
<!-- <transducer-list v-if="showFan" :list="socketData.leftData" :transducer-data="largeScreenData" />-->
<used-ele v-if="showFan" :list="socketData.leftData" :ele-data="largeScreenData" />
</el-drawer>
<div v-if="drawerLeft" class="left-arrow" @click="closeLeft"></div>
<div v-else class="shrink-left" @click="closeLeft"></div>
</div>
<div class="right">
<el-drawer v-model="drawerRight" direction="rtl" modal-class="modal-box" :modal="false" :show-close="false"
:close-on-click-modal="false" :close-on-press-escape="false">
<wind-pressure-list v-if="showFan" :list="socketData.windPressure" :win-data="largeScreenData"/>
<air-info v-if="showFan" :list="socketData.sensor" :air-data="largeScreenData"/>
<bad-gas-info v-if="showFan" :list="socketData.sensor" :bad-gas-data="largeScreenData" :tunnelId="tunnelId"/>
:close-on-click-modal="false" :close-on-press-escape="false">
<wind-pressure-list v-if="showFan" :list="socketData.windPressure" :win-data="largeScreenData" />
<air-info v-if="showFan" :list="socketData.sensor" :air-data="largeScreenData" />
<bad-gas-info v-if="showFan" :list="socketData.sensor" :bad-gas-data="largeScreenData" :tunnelId="tunnelId" />
</el-drawer>
<div v-if="drawerRight" class="right-arrow" @click="closeRight"></div>
<div v-else class="shrink-right" @click="closeRight"></div>
@@ -60,6 +64,7 @@
<script setup>
import TunnelScene from "@/components/content/tunnelScene/TunnelScene.vue";
import PreviewScene from "@/components/content/tunnelScene/PreviewScene.vue"
import FanInfo from "@/components/content/fanInfo/FanInfo.vue";
import TransducerList from "@/components/content/transducerList/TransducerList.vue";
import UsedEle from "@/components/content/usedEle/UsedEle.vue";
@@ -68,12 +73,12 @@ import AirInfo from "@/components/content/airInfo/AirInfo.vue";
import BadGasInfo from "@/components/content/badGasInfo/BadGasInfo.vue";
import ManageBtn from "@/components/manageBtn/index.vue";
import ManageLength from "@/components/manageLength/index.vue";
import {dateFormat} from "@/utils/date.js";
import {getToken} from "@/utils/auth";
import {useAuthStore} from "@/store/userstore.js";
import {getLargeScreen, getLargeScreenInfo, getTunnelBySiteId} from "@/api/largeScreen";
import {ElMessageBox} from "element-plus";
import {getUserInfo} from "@/api/login";
import { dateFormat } from "@/utils/date.js";
import { getToken } from "@/utils/auth";
import { useAuthStore } from "@/store/userstore.js";
import { getLargeScreen, getLargeScreenInfo, getTunnelBySiteId } from "@/api/largeScreen";
import { ElMessageBox } from "element-plus";
import { getUserInfo } from "@/api/login";
const authStore = useAuthStore();
const router = useRouter();
@@ -81,6 +86,7 @@ const selectIndex = ref(-1);
const showFan = ref(false);
const drawerLeft = ref(true);
const drawerRight = ref(true);
const currentSiteId = ref(0);
const currentSite = ref("");
const siteList = ref([])
const currentUser = ref("");
@@ -110,7 +116,6 @@ onMounted(() => {
getOtherInfo()
});
const getUser = () => {
getUserInfo().then(res => {
currentUser.value = res.data.user.userName
@@ -120,6 +125,7 @@ const getOtherInfo = () => {
getLargeScreenInfo().then((res) => {
if (res?.code === 1000) {
routeList.value = res.data.routeList
currentSiteId.value = res.data.siteOption[0].value
currentSite.value = res.data.siteOption[0].label
siteList.value = res.data.siteOption
tunnelId.value = res.data.tunnelOption[0].value
@@ -162,14 +168,13 @@ const manageSelect = (index) => {
if (index === 0) {
router.push("/site");
} else if (index === 1) {
router.push("/tunnel/1");
}else if (index === 2) {
router.push("/user");
router.push("/tunnel/"+tunnelId.value);
} else if (index === 2) {
router.push("/user/"+currentSiteId.value);
}
};
const handleCommand=(item)=>{
console.log('commads',item)
currentSite.value=item.label
const handleCommand = (item) => {
currentSite.value = item.label
getTunnel(item.value)
}
const closeLeft = () => {
@@ -246,40 +251,45 @@ initWebSocket();
border: 1px solid #0E7DAA;
border-radius: 10px;
}
.is-light{
background:rgba(7,35,72,0.9);
.is-light {
background: rgba(7, 35, 72, 0.9);
}
.el-popper{
.el-popper {
padding: 20px;
margin-left: 50px;
width: 150px;
background:rgba(7,35,72,0.9);
background: rgba(7, 35, 72, 0.9);
.el-scrollbar__wrap {
.el-dropdown__list {
.el-dropdown-menu {
background-color:rgba(7,35,72,0.9);
background-color: rgba(7, 35, 72, 0.9);
border-radius: 10px;
padding: 5px;
.el-dropdown-menu__item{
.el-dropdown-menu__item {
color: #FFFFFF;
//border:none;
padding: 5px;
border-bottom: 1px solid #05FEFF;
&:last-child{
&:last-child {
border-bottom: none;
}
}
.el-dropdown-menu__item.hover,.el-dropdown-menu__item:hover{
background-color:transparent!important;
.el-dropdown-menu__item.hover,
.el-dropdown-menu__item:hover {
background-color: transparent !important;
color: #F7B500;
}
}
}
}
}
</style>
<style lang="scss" scoped>
#main {

View File

@@ -7,10 +7,10 @@
</div>
<div class="tunnel-title"></div>
<div class="all-del-btn">
<div class="all-btn" @click="handleAll">
全选
</div>
<div class="all-btn del-btn" @click="handleMoreDelete">
<!-- <div class="all-btn" @click="handleAll">-->
<!-- 全选-->
<!-- </div>-->
<div class="all-btn del-btn" @click="handleDelete">
删除
</div>
</div>
@@ -22,26 +22,29 @@
<div>
<el-input
v-model="username"
clearable
placeholder="输入搜索内容"
/>
</div>
<el-button type="primary" :icon="Search" style="margin-left: 20px">搜索</el-button>
<el-button type="primary" :icon="Search" style="margin-left: 20px" @click="getInfo">搜索</el-button>
</div>
<div>用户管理</div>
<div class="add-btn" @click="addUser">添加用户</div>
<div class="add-btn" @click="handleAddUser">添加用户</div>
</div>
<div class="device-table">
<el-table stripe ref="multipleTable"
<el-table stripe ref="multipleTable" @select="handleSelect"
style="border-bottom: 1px #06e5e5 solid;background-color: #011c29;--el-table-border-color: none;margin-top: 80px;"
:header-cell-style="{backgroundColor: '#064B66',color: '#fff',fontSize: '40px',borderBottom: 'none' }"
:cell-style="{textAlign: 'center',borderBottom: 'none'}" :data="userData">
<el-table-column type="selection" width="80"/>
<el-table-column prop="account" label="账户" align="center"/>
<el-table-column prop="password" label="密码" align="center"/>
<el-table-column prop="identity" label="身份" align="center"/>
<el-table-column prop="userName" label="账户" align="center"/>
<el-table-column prop="nickName" label="昵称" align="center"/>
<el-table-column prop="roleName" label="身份" align="center"/>
<el-table-column prop="phoneNumber" label="电话号码" align="center"/>
<el-table-column label="操作" align="center">
<template #default>
<el-button link type="primary" size="large" style="font-size: 40px;color: #17E1E2;" @click="editUser">
<template #default="scope">
<el-button link type="primary" size="large" style="font-size: 40px;color: #17E1E2;"
@click="handleEditUser(scope.row)">
修改
</el-button>
</template>
@@ -50,24 +53,50 @@
</div>
</div>
</div>
<el-dialog v-model="isVisited" width="1658px">
<div class="siteId">
<el-dialog :close-on-click-modal="false" v-model="isVisited" width="1658px">
<div class="user-title">
<span>{{ title }}</span>
</div>
<el-form :model="form" :label-position="right" :rules="formRules" ref="formInstance" label-width="168px">
<el-form-item label="输入账户">
<el-input v-model="form.account" placeholder="请输入账户"/>
<el-form :model="form" :label-position="right" :rules="formRules" ref="formInstance" label-width="240px">
<el-form-item label="输入用户名称">
<el-input v-model="form.userName" placeholder="请输入用户名称"/>
</el-form-item>
<el-form-item label="输入密码">
<el-form-item label="输入用户昵称">
<el-input v-model="form.nickName" placeholder="请输入用户昵称"/>
</el-form-item>
<el-form-item label="输入电话号码">
<el-input v-model="form.phoneNumber" placeholder="输入电话号码"/>
</el-form-item>
<el-form-item label="输入密码" v-if="title==='添加用户'">
<el-input v-model="form.password" placeholder="请输入密码"/>
</el-form-item>
<el-form-item label="选择身份">
<el-checkbox-group v-model="form.identity" @change="handleChangeCheckbox">
<el-checkbox v-for="item in checkList" :label="item.label" :key="item.value"/>
</el-checkbox-group>
<el-radio-group v-model="form.roleId" @change="(val) => {
console.log('vaaa',val)
if (val >3){
getTunnel()
}}">
<el-radio v-for="item in radioList" :label="item.value" :key="item.value">{{ item.label }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="选择隧道" v-if="checkList.length!==0&&form.roleId>3">
<el-select
v-model="tunnelIndex"
multiple
filterable
clearable
placeholder="请选择隧道"
:fit-input-width="true"
>
<el-option
v-for="item in checkList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<div class="btns">
<div class="cancel-btn" @click="isVisited=false">
@@ -91,66 +120,19 @@
<script setup>
import {Search} from '@element-plus/icons-vue'
import {addUser, editUser, getUser, getRoleOption} from "@/api/user";
import {ElMessage, ElMessageBox} from "element-plus";
import {getTunnelOption} from "@/api/tunnel";
import {deleteUser, getUserDetail} from "../../api/user";
const router = useRouter()
const title = ref('添加用户')
const siteId = reactive(router.currentRoute.value.params.siteId)
const tunnelIndex = ref()
const username = ref()
const userData = ref([
{
account: '123343546',
password: '243',
identity: '超级管理员'
},
{
account: '123343546',
password: '243',
identity: '超级管理员'
},
{
account: '123343546',
password: '243',
identity: '超级管理员'
},
{
account: '123343546',
password: '243',
identity: '超级管理员'
},
{
account: '123343546',
password: '243',
identity: '超级管理员'
},
{
account: '123343546',
password: '243',
identity: '超级管理员'
},
{
account: '123343546',
password: '243',
identity: '超级管理员'
},
{
account: '123343546',
password: '243',
identity: '超级管理员'
},
{
account: '123343546',
password: '243',
identity: '超级管理员'
},
{
account: '123343546',
password: '243',
identity: '超级管理员'
},
{
account: '123343546',
password: '243',
identity: '超级管理员'
}
])
const userIds = ref()
const formInstance = ref()
const userData = ref([])
const pageInfo = reactive({
pageNum: 1,
pageSize: 6
@@ -159,32 +141,190 @@ const total = ref(10);
const isVisited = ref(false);
const form = ref({
password: '',
account: '',
identity: []
userName: '',
nickName: '',
phoneNumber: '',
roleId: [],
tunnelIds: []
});
const formRules = ref({
account: [{required: true, message: '请输入站点名称', trigger: 'blur'}],
userName: [{required: true, message: '请输入用户名称', trigger: 'blur'}],
password: [{required: true, message: '请输入密码', trigger: 'blur'}]
})
const checkList = ref([
{label: "系统管理员", value: 111},
{label: "站点管理员", value: 222},
{label: "隧道管理员", value: 333},
{label: "巡查员", value: 444}
])
const radioList = ref([])
const checkList = ref([])
const multipleTable = ref()
const addUser = () => {
const getTunnel = () => {
getTunnelOption(siteId).then(res => {
if (res.code === 1000) {
checkList.value = res.data
}
})
}
const handleAddUser = () => {
reset()
title.value = '添加用户'
isVisited.value = true
}
const editUser = () => {
const handleEditUser = (row) => {
reset()
getUserDetail(row.userId).then(res => {
if (res.code === 1000) {
form.value = res.data.user
form.value.roleId = res.data.roleIds[0]
}
})
title.value = '修改用户'
isVisited.value = true
}
const handleAll = () => {
multipleTable.value.toggleAllSelection()
}
const handleDelete = () => {
ElMessageBox.confirm(`是否确认删除该用户吗?`, '系统提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
deleteUser(userIds.value).then(res => {
if (res.code === 1000) {
ElMessage.success(res.msg)
getInfo()
}
})
})
}
const reset = () => {
form.value = {
password: '',
userName: '',
nickName: '',
phoneNumber: '',
roleId: [],
tunnelIds: []
}
}
const getRoleOptionInfo = () => {
getRoleOption().then(res => {
if (res.code === 1000) {
radioList.value = res.data
}
})
}
getRoleOptionInfo()
const getInfo = () => {
getUser({
siteId: siteId,
userName: username.value
}).then(res => {
if (res.code === 1000) {
console.log('res', res)
userData.value = res.data.rows
total.value = res.data.total
}
})
}
getInfo()
const handleSubmit = async (formInstance) => {
if (!formInstance) return;
formInstance.validate(async (valid) => {
if (!valid) return;
const data = {
siteId: siteId,
roleIds: [form.value.roleId],
...form.value
}
if (title.value === '添加用户') {
await addUser(data).then(res => {
if (res.code === 1000) {
getInfo()
isVisited.value = false
} else {
ElMessage.warning(res.msg)
}
})
} else {
await editUser(data).then(res => {
if (res.code === 1000) {
getInfo()
isVisited.value = false
} else {
ElMessage.warning(res.msg)
}
})
}
})
}
const handleSelect = async (selection, row) => {
if (selection.length !== 0) {
// disabled.value = false
userIds.value = row.userId
if (selection.length > 1) {
const del_row = selection.shift();
multipleTable.value.toggleRowSelection(del_row, false);
}
} else {
// disabled.value = true
}
}
</script>
<style scoped lang="scss">
:deep(.el-table__header-wrapper .el-checkbox) {
display: none
}
:deep(.el-select-dropdown__item:hover) {
background-color: #064B66 !important;
width: 137px !important;
}
:deep(.el-radio-group) {
margin-top: 10px;
}
:deep(.el-radio__label) {
color: #FFFFFF;
font-size: 38px;
}
:deep(.el-radio__inner) {
width: 40px;
height: 40px;
border-radius: 25px;
border: 4px solid #05FEFF;
background-color: transparent;
}
:deep(.el-radio__input.is-checked+.el-radio__label) {
color: #FFFFFF;
}
:deep(.el-radio__input.is-checked .el-radio__inner ) {
background: #064B66;
border-color: #05FEFF !important;
}
:deep(.el-radio__inner::after) {
width: 18px;
height: 18px;
background: #05FEFF;
}
.user-title {
display: flex;
justify-content: center;
margin: 0 0 60px 0;
font-size: 50px;
color: #FFFFFF;
}
:deep(.el-table__empty-block) {
background: #064B66;
font-size: 30px;
min-height: 200px;
}
.tunnel-bgc {
padding: 85px 0 0 0;
width: 100%;
@@ -393,14 +533,6 @@ const handleAll = () => {
}
}
:deep(.el-checkbox-group) {
margin-top: 10px;
}
:deep(.el-checkbox__label) {
color: #FFFFFF;
font-size: 38px;
}
.pagination {
position: absolute;

View File

@@ -1,18 +1,19 @@
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import {fileURLToPath, URL} from 'node:url'
import {defineConfig} from 'vite'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import {ElementPlusResolver} from 'unplugin-vue-components/resolvers'
import Icons from 'unplugin-icons/vite'
import IconsResolver from 'unplugin-icons/resolver'
import vue from '@vitejs/plugin-vue'
import Inspect from 'vite-plugin-inspect'
export default defineConfig({
plugins: [
vue(),
AutoImport({
//自动导入vue相关函数
imports: ['vue','vue-router'],
imports: ['vue', 'vue-router'],
resolvers: [
ElementPlusResolver(),
@@ -57,6 +58,18 @@ export default defineConfig({
strictPort: false,
open: true,
proxy: {
// '/api/tunnel': {
// // target: 'http://gateway.feashow.cn',
// target: 'http://192.168.31.175:8000',
// changeOrigin: true,
// rewrite: (path) => path.replace(/^\/api/, ''),
// },
// '/api/admin': {
// // target: 'http://gateway.feashow.cn',
// target: 'http://192.168.31.175:8000',
// changeOrigin: true,
// rewrite: (path) => path.replace(/^\/api/, ''),
// },
'/api': {
target: 'http://web-tunnel.feashow.com/api',
// target: 'http://192.168.31.175:8000',