fix/环境配置
This commit is contained in:
parent
1f77b00c58
commit
8a605b91fc
|
|
@ -0,0 +1,24 @@
|
||||||
|
###
|
||||||
|
# @Author: 季万俊
|
||||||
|
# @Date: 2025-09-04 19:58:43
|
||||||
|
# @Description:
|
||||||
|
###
|
||||||
|
# 页面标题
|
||||||
|
VUE_APP_TITLE = 十堰运维
|
||||||
|
|
||||||
|
# 开发环境配置
|
||||||
|
ENV = 'development'
|
||||||
|
|
||||||
|
# 若依管理系统/开发环境
|
||||||
|
VITE_APP_BASE_API = '/'
|
||||||
|
# VITE_APP_BASE_API = '/dev-api'
|
||||||
|
# VUE_APP_CONTROL_BASE_API = '/control-api'
|
||||||
|
VITE_VUE_APP_CONTROL_BASE_API = 'http://172.16.1.253:12310/api'
|
||||||
|
VITE_VUE_APP_SERVER_BASE_API = '/control-api/Server'
|
||||||
|
#VUE_APP_MONITOR_URL='http://172.16.1.147:8083/cn/demo.html'
|
||||||
|
VITE_VUE_APP_MONITOR_URL='http://172.16.1.138:8666/cn/camera.html'
|
||||||
|
VITE_VUE_LOGIN_USERNAME = 'admin'
|
||||||
|
VITE_VUE_LOGIN_PASSWORD = 'admin123'
|
||||||
|
|
||||||
|
# 路由懒加载
|
||||||
|
VUE_CLI_BABEL_TRANSPILE_MODULES = true
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
###
|
||||||
|
# @Author: 季万俊
|
||||||
|
# @Date: 2025-09-04 19:58:43
|
||||||
|
# @Description:
|
||||||
|
###
|
||||||
|
# 页面标题
|
||||||
|
VUE_APP_TITLE = 十堰运维
|
||||||
|
|
||||||
|
# 开发环境配置
|
||||||
|
ENV = 'development'
|
||||||
|
|
||||||
|
# 若依管理系统/开发环境
|
||||||
|
VITE_APP_BASE_API = '/dev-api'
|
||||||
|
# VUE_APP_CONTROL_BASE_API = '/control-api'
|
||||||
|
VITE_VUE_APP_CONTROL_BASE_API = 'http://172.16.1.253:12310/api'
|
||||||
|
VITE_VUE_APP_SERVER_BASE_API = '/control-api/Server'
|
||||||
|
#VUE_APP_MONITOR_URL='http://172.16.1.147:8083/cn/demo.html'
|
||||||
|
VITE_VUE_APP_MONITOR_URL='http://172.16.1.138:8666/cn/camera.html'
|
||||||
|
VITE_VUE_LOGIN_USERNAME = 'admin'
|
||||||
|
VITE_VUE_LOGIN_PASSWORD = 'admin123'
|
||||||
|
|
||||||
|
# 路由懒加载
|
||||||
|
VUE_CLI_BABEL_TRANSPILE_MODULES = true
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
###
|
||||||
|
# @Author: 季万俊
|
||||||
|
# @Date: 2025-09-04 19:58:43
|
||||||
|
# @Description:
|
||||||
|
###
|
||||||
|
# 页面标题
|
||||||
|
VUE_APP_TITLE = 十堰运维
|
||||||
|
|
||||||
|
# 开发环境配置
|
||||||
|
ENV = 'development'
|
||||||
|
|
||||||
|
# 若依管理系统/开发环境
|
||||||
|
VITE_APP_BASE_API = '/dev-api'
|
||||||
|
# VUE_APP_CONTROL_BASE_API = '/control-api'
|
||||||
|
VITE_VUE_APP_CONTROL_BASE_API = 'http://172.16.1.253:12310/api'
|
||||||
|
VITE_VUE_APP_SERVER_BASE_API = '/control-api/Server'
|
||||||
|
#VUE_APP_MONITOR_URL='http://172.16.1.147:8083/cn/demo.html'
|
||||||
|
VITE_VUE_APP_MONITOR_URL='http://172.16.1.138:8666/cn/camera.html'
|
||||||
|
VITE_VUE_LOGIN_USERNAME = 'admin'
|
||||||
|
VITE_VUE_LOGIN_PASSWORD = 'admin123'
|
||||||
|
|
||||||
|
# 路由懒加载
|
||||||
|
VUE_CLI_BABEL_TRANSPILE_MODULES = true
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
<!--
|
||||||
|
* @Author: 季万俊
|
||||||
|
* @Date: 2025-09-26 11:23:42
|
||||||
|
* @Description:
|
||||||
|
-->
|
||||||
<template>
|
<template>
|
||||||
<section class="app-main">
|
<section class="app-main">
|
||||||
<router-view v-slot="{ Component, route }">
|
<router-view v-slot="{ Component, route }">
|
||||||
|
|
@ -38,10 +43,10 @@ function addIframe() {
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.app-main {
|
.app-main {
|
||||||
/* 50= navbar 50 */
|
/* 50= navbar 50 */
|
||||||
min-height: calc(100vh - 50px);
|
height: calc(100vh - 84px);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fixed-header + .app-main {
|
.fixed-header + .app-main {
|
||||||
|
|
|
||||||
|
|
@ -234,7 +234,7 @@ export const constantRoutes = [
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "linkConfig",
|
name: "linkConfig",
|
||||||
alwaysShow: true,
|
alwaysShow: true,
|
||||||
component: Layout,
|
component: Layout,
|
||||||
|
|
@ -504,6 +504,31 @@ export const constantRoutes = [
|
||||||
title: "角色管理",
|
title: "角色管理",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
component: () => import('@/views/system/dict/index'),
|
||||||
|
hidden: false,
|
||||||
|
name: "dict",
|
||||||
|
path: "dict",
|
||||||
|
meta: {
|
||||||
|
icon: "dict",
|
||||||
|
link: null,
|
||||||
|
noCache: false,
|
||||||
|
title: "字典管理",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// component: () => import('@/views/system/menu/index'),
|
||||||
|
// hidden: false,
|
||||||
|
// name: "menu",
|
||||||
|
// path: "menu",
|
||||||
|
// meta: {
|
||||||
|
// icon: "menu",
|
||||||
|
// link: null,
|
||||||
|
// noCache: false,
|
||||||
|
// title: "菜单管理",
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
|
||||||
{
|
{
|
||||||
component: () => import('@/views/system/log/index'),
|
component: () => import('@/views/system/log/index'),
|
||||||
hidden: false,
|
hidden: false,
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ export const useUserStore = defineStore('user', {
|
||||||
const avatar =
|
const avatar =
|
||||||
user.avatar == "" || user.avatar == null
|
user.avatar == "" || user.avatar == null
|
||||||
? require("@/assets/images/profile.jpg")
|
? require("@/assets/images/profile.jpg")
|
||||||
: process.env.VITE_VUE_APP_BASE_API + user.avatar
|
: process.env.VITE_VITE_VUE_APP_BASE_API + user.avatar
|
||||||
if (res.roles && res.roles.length > 0) {
|
if (res.roles && res.roles.length > 0) {
|
||||||
console.log("获取用户信息")
|
console.log("获取用户信息")
|
||||||
// 验证返回的roles是否是一个非空数组
|
// 验证返回的roles是否是一个非空数组
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,10 @@ export function useDict(...args) {
|
||||||
// res.value[dictType] = dicts
|
// res.value[dictType] = dicts
|
||||||
// } else {
|
// } else {
|
||||||
getDicts(dictType).then(resp => {
|
getDicts(dictType).then(resp => {
|
||||||
res.value[dictType] = resp.data.map(p => ({ label: p.dictLabel, value: p.dictValue, elTagType: p.listClass, elTagClass: p.cssClass }))
|
if(resp && resp.data) {
|
||||||
useDictStore().setDict(dictType, res.value[dictType])
|
res.value[dictType] = resp.data.map(p => ({ label: p.dictLabel, value: p.dictValue, elTagType: p.listClass, elTagClass: p.cssClass }))
|
||||||
|
useDictStore().setDict(dictType, res.value[dictType])
|
||||||
|
}
|
||||||
})
|
})
|
||||||
// }
|
// }
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -293,7 +293,6 @@ const cancel = () => {
|
||||||
.meter-monitoring-page {
|
.meter-monitoring-page {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
background-color: #f8fafc;
|
background-color: #f8fafc;
|
||||||
min-height: calc(100vh - 64px); /* 适配顶部导航高度 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 卡片样式:圆角 + 悬浮阴影,增强层次感 */
|
/* 卡片样式:圆角 + 悬浮阴影,增强层次感 */
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
<div class="device-tree-panel">
|
<div class="device-tree-panel">
|
||||||
<div class="tree-header">
|
<div class="tree-header">
|
||||||
<div class="search-add">
|
<div class="search-add">
|
||||||
<!-- 修复:确保 ElInput 标签闭合,图标组件正确引用 -->
|
|
||||||
<ElInput
|
<ElInput
|
||||||
v-model="searchDevice"
|
v-model="searchDevice"
|
||||||
placeholder="请输入设备"
|
placeholder="请输入设备"
|
||||||
|
|
@ -14,29 +13,44 @@
|
||||||
></ElInput>
|
></ElInput>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 修复:ElTree 标签闭合,避免自闭合语法不兼容 -->
|
|
||||||
<ElTree
|
<!-- 自定义设备列表 -->
|
||||||
ref="deviceTreeRef"
|
<div class="custom-tree">
|
||||||
:data="deviceTreeData"
|
<div
|
||||||
:props="treeProps"
|
v-for="group in filteredTreeData"
|
||||||
:filter-node-method="filterNode"
|
:key="group.label"
|
||||||
:current-node-key="currentNodeKey"
|
class="tree-group"
|
||||||
:current-node-id="currentSelectedNode"
|
>
|
||||||
:disabled="isDisabled"
|
<div class="group-label">{{ group.label }}</div>
|
||||||
default-expand-all
|
<div
|
||||||
highlight-current
|
v-for="item in group.children"
|
||||||
@node-click="handleNodeClick"
|
:key="item.label"
|
||||||
class="device-tree"
|
class="tree-item"
|
||||||
></ElTree>
|
:class="{
|
||||||
|
'selected': isItemSelected(item.label),
|
||||||
|
'disabled': isItemDisabled && !isItemSelected(item.label)
|
||||||
|
}"
|
||||||
|
@click="handleItemClick(item)"
|
||||||
|
>
|
||||||
|
<div class="item-content">
|
||||||
|
<Camera class="item-icon"></Camera>
|
||||||
|
<span class="item-label">{{ item.label }}</span>
|
||||||
|
</div>
|
||||||
|
<div v-if="isItemSelected(item.label)" class="selected-indicator">
|
||||||
|
<el-icon><Check /></el-icon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 右侧视频墙面板 -->
|
<!-- 右侧视频墙面板 -->
|
||||||
<div class="video-wall-panel">
|
<div class="video-wall-panel">
|
||||||
<div class="video-header">
|
<div class="video-header">
|
||||||
<!-- 修复:图标组件闭合,添加类名语法正确 -->
|
<Camera class="icon"></Camera>
|
||||||
<span>监控视频</span>
|
<span>监控视频</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- 修复:style 绑定语法完整,英文逗号,闭合大括号 -->
|
|
||||||
<div
|
<div
|
||||||
class="video-container"
|
class="video-container"
|
||||||
:style="{
|
:style="{
|
||||||
|
|
@ -44,7 +58,6 @@
|
||||||
gridTemplateRows: gridRows
|
gridTemplateRows: gridRows
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<!-- 修复:v-for key 唯一(暂用index,实际建议用设备ID),标签闭合 -->
|
|
||||||
<div
|
<div
|
||||||
v-for="(video, index) in videoList"
|
v-for="(video, index) in videoList"
|
||||||
:key="`video-${index}`"
|
:key="`video-${index}`"
|
||||||
|
|
@ -58,7 +71,6 @@
|
||||||
<div class="video-placeholder">
|
<div class="video-placeholder">
|
||||||
<Camera class="icon"></Camera>
|
<Camera class="icon"></Camera>
|
||||||
<span>{{ video.name }}</span>
|
<span>{{ video.name }}</span>
|
||||||
<!-- 修复:Close 图标标签闭合,@click.stop 语法正确 -->
|
|
||||||
<Close
|
<Close
|
||||||
v-if="video.name !== '待选择区域'"
|
v-if="video.name !== '待选择区域'"
|
||||||
class="close-icon"
|
class="close-icon"
|
||||||
|
|
@ -70,7 +82,6 @@
|
||||||
|
|
||||||
<!-- 布局切换按钮 -->
|
<!-- 布局切换按钮 -->
|
||||||
<div class="layout-switch">
|
<div class="layout-switch">
|
||||||
<!-- 修复:v-for key 唯一,ElButton 标签闭合 -->
|
|
||||||
<ElButton
|
<ElButton
|
||||||
v-for="item in buttonType"
|
v-for="item in buttonType"
|
||||||
:key="`layout-${item.type}`"
|
:key="`layout-${item.type}`"
|
||||||
|
|
@ -88,14 +99,11 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
// 修复:确保所有导入正确,无拼写错误,逗号分隔规范
|
import { ref, computed, watch, onMounted } from 'vue';
|
||||||
import { ref, computed, watch, onMounted, nextTick } from 'vue';
|
import { ElInput, ElButton, ElMessage } from 'element-plus';
|
||||||
import { ElInput, ElTree, ElButton, ElMessage } from 'element-plus';
|
import { Search, Camera, Close, Check } from '@element-plus/icons-vue';
|
||||||
import { Search, Camera, Close } from '@element-plus/icons-vue';
|
|
||||||
|
|
||||||
// 修复:响应式变量定义完整,无遗漏赋值
|
|
||||||
const searchDevice = ref('');
|
const searchDevice = ref('');
|
||||||
const deviceTreeRef = ref(null);
|
|
||||||
const deviceTreeData = ref([
|
const deviceTreeData = ref([
|
||||||
{
|
{
|
||||||
label: "公共区域",
|
label: "公共区域",
|
||||||
|
|
@ -110,32 +118,19 @@ const deviceTreeData = ref([
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
const currentLayout = ref(1);
|
|
||||||
const videoList = ref([
|
|
||||||
{ name: "待选择区域", bgColor: "#e6f7ff", isSelected: false }
|
|
||||||
]);
|
|
||||||
const selectedNodes = ref([]);
|
|
||||||
const currentSelectedNode = ref(null);
|
|
||||||
|
|
||||||
// 修复:常量定义无语法错误,对象逗号规范
|
const currentLayout = ref(1);
|
||||||
|
// 重构:使用对象来记录每个位置的设备,key为位置索引,value为设备信息
|
||||||
|
const videoPositions = ref({});
|
||||||
|
const selectedNodes = ref([]);
|
||||||
|
|
||||||
const buttonType = [
|
const buttonType = [
|
||||||
{ label: "1x1", type: 1 },
|
{ label: "1x1", type: 1 },
|
||||||
{ label: "2x2", type: 2 },
|
{ label: "2x2", type: 2 },
|
||||||
{ label: "3x3", type: 3 }
|
{ label: "3x3", type: 3 }
|
||||||
];
|
];
|
||||||
const treeProps = {
|
|
||||||
children: "children",
|
|
||||||
label: "label"
|
|
||||||
};
|
|
||||||
const currentNodeKey = "label";
|
|
||||||
|
|
||||||
// 修复:计算属性函数闭合完整,逻辑无语法错误
|
|
||||||
const isDisabled = computed(() => {
|
|
||||||
if (currentLayout.value === 1) return false;
|
|
||||||
if (currentLayout.value === 2) return selectedNodes.value.length === 4;
|
|
||||||
return selectedNodes.value.length === 9;
|
|
||||||
});
|
|
||||||
|
|
||||||
|
// 计算属性
|
||||||
const gridColumns = computed(() => {
|
const gridColumns = computed(() => {
|
||||||
switch (currentLayout.value) {
|
switch (currentLayout.value) {
|
||||||
case 1: return "1fr";
|
case 1: return "1fr";
|
||||||
|
|
@ -158,107 +153,138 @@ const maxCapacity = computed(() => {
|
||||||
return currentLayout.value === 1 ? 1 : (currentLayout.value === 2 ? 4 : 9);
|
return currentLayout.value === 1 ? 1 : (currentLayout.value === 2 ? 4 : 9);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 修复:函数定义完整,括号/大括号闭合,无非法字符
|
const isItemDisabled = computed(() => {
|
||||||
const filterNode = (value, data) => {
|
return selectedNodes.value.length >= maxCapacity.value;
|
||||||
if (!value) return true;
|
|
||||||
return data.label.toLowerCase().includes(value.toLowerCase());
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateVideoList = () => {
|
|
||||||
const capacity = maxCapacity.value;
|
|
||||||
videoList.value = Array.from({ length: capacity }, (_, index) => {
|
|
||||||
const deviceName = selectedNodes.value[index];
|
|
||||||
return {
|
|
||||||
name: deviceName || "待选择区域",
|
|
||||||
bgColor: deviceName ? `rgba(64, 158, 255, ${0.1 + (index % 5) * 0.15})` : "#e6f7ff",
|
|
||||||
isSelected: !!deviceName
|
|
||||||
};
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleNodeClick = (data) => {
|
|
||||||
if (currentLayout.value === 1) {
|
|
||||||
selectedNodes.value = [data.label];
|
|
||||||
updateVideoList();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const isExist = selectedNodes.value.includes(data.label);
|
|
||||||
if (isExist) {
|
|
||||||
selectedNodes.value = selectedNodes.value.filter(item => item !== data.label);
|
|
||||||
} else {
|
|
||||||
if (selectedNodes.value.length >= maxCapacity.value) {
|
|
||||||
const layoutLabel = buttonType.find(item => item.type === currentLayout.value).label;
|
|
||||||
ElMessage.warning(`当前${layoutLabel}模式最多选择${maxCapacity.value}个视频,请先移除一个`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
selectedNodes.value.push(data.label);
|
|
||||||
}
|
|
||||||
updateVideoList();
|
|
||||||
};
|
|
||||||
|
|
||||||
const syncTreeSelection = () => {
|
|
||||||
if (currentLayout.value === 1) {
|
|
||||||
currentSelectedNode.value = selectedNodes.value[0] || null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
nextTick(() => {
|
|
||||||
const treeNodes = document.querySelectorAll(".device-tree .el-tree-node");
|
|
||||||
treeNodes.forEach(node => {
|
|
||||||
const labelElem = node.querySelector(".el-tree-node__label");
|
|
||||||
if (!labelElem) return;
|
|
||||||
const label = labelElem.textContent.trim();
|
|
||||||
if (selectedNodes.value.includes(label)) {
|
|
||||||
node.classList.add("is-selected");
|
|
||||||
} else {
|
|
||||||
node.classList.remove("is-selected");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const changeLayout = (layoutType) => {
|
|
||||||
currentLayout.value = layoutType;
|
|
||||||
selectedNodes.value = [];
|
|
||||||
updateVideoList();
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleVideoClick = (index) => {
|
|
||||||
const video = videoList.value[index];
|
|
||||||
if (video.name !== "待选择区域") {
|
|
||||||
currentSelectedNode.value = video.name;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const removeVideo = (index) => {
|
|
||||||
const removedItem = selectedNodes.value[index];
|
|
||||||
if (!removedItem) return;
|
|
||||||
selectedNodes.value = selectedNodes.value.filter((_, i) => i !== index);
|
|
||||||
updateVideoList();
|
|
||||||
ElMessage.info(`已移除${removedItem}`);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 修复:watch 监听语法正确,回调函数闭合
|
|
||||||
watch(searchDevice, (value) => {
|
|
||||||
if (deviceTreeRef.value) {
|
|
||||||
deviceTreeRef.value.filter(value);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(selectedNodes, (newVal) => {
|
// 视频列表,根据位置信息生成
|
||||||
syncTreeSelection();
|
const videoList = computed(() => {
|
||||||
}, { immediate: true }); // 新增immediate,初始化时同步选中状态
|
const capacity = maxCapacity.value;
|
||||||
|
const list = [];
|
||||||
// 修复:onMounted 钩子函数闭合,样式添加无语法错误
|
|
||||||
onMounted(() => {
|
for (let i = 0; i < capacity; i++) {
|
||||||
const style = document.createElement("style");
|
if (videoPositions.value[i]) {
|
||||||
// 修复:样式字符串无换行错误,引号闭合
|
// 该位置有设备
|
||||||
style.textContent = `
|
const device = videoPositions.value[i];
|
||||||
.device-tree .el-tree-node.is-selected .el-tree-node__content {
|
list.push({
|
||||||
background-color: #e6f7ff;
|
name: device.label,
|
||||||
color: #409eff;
|
bgColor: `rgba(64, 158, 255, ${0.1 + (i % 5) * 0.15})`,
|
||||||
|
isSelected: false
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 该位置为空
|
||||||
|
list.push({
|
||||||
|
name: "待选择区域",
|
||||||
|
bgColor: "#e6f7ff",
|
||||||
|
isSelected: false
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 过滤树数据
|
||||||
|
const filteredTreeData = computed(() => {
|
||||||
|
if (!searchDevice.value) return deviceTreeData.value;
|
||||||
|
|
||||||
|
return deviceTreeData.value.map(group => ({
|
||||||
|
...group,
|
||||||
|
children: group.children.filter(item =>
|
||||||
|
item.label.toLowerCase().includes(searchDevice.value.toLowerCase())
|
||||||
|
)
|
||||||
|
})).filter(group => group.children.length > 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 判断项目是否被选中
|
||||||
|
const isItemSelected = (label) => {
|
||||||
|
return selectedNodes.value.includes(label);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 查找第一个可用的位置
|
||||||
|
const findAvailablePosition = () => {
|
||||||
|
const capacity = maxCapacity.value;
|
||||||
|
for (let i = 0; i < capacity; i++) {
|
||||||
|
if (!videoPositions.value[i]) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 查找设备所在的位置
|
||||||
|
const findDevicePosition = (label) => {
|
||||||
|
for (const [position, device] of Object.entries(videoPositions.value)) {
|
||||||
|
if (device.label === label) {
|
||||||
|
return parseInt(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理项目点击
|
||||||
|
const handleItemClick = (item) => {
|
||||||
|
const itemLabel = item.label;
|
||||||
|
const position = findDevicePosition(itemLabel);
|
||||||
|
|
||||||
|
if (position !== -1) {
|
||||||
|
// 设备已存在,取消选择
|
||||||
|
delete videoPositions.value[position];
|
||||||
|
selectedNodes.value = selectedNodes.value.filter(label => label !== itemLabel);
|
||||||
|
ElMessage.info(`已移除${itemLabel}`);
|
||||||
|
} else {
|
||||||
|
// 设备不存在,尝试添加
|
||||||
|
if (selectedNodes.value.length >= maxCapacity.value) {
|
||||||
|
ElMessage.warning(`当前${buttonType.find(b => b.type === currentLayout.value)?.label}模式最多选择${maxCapacity.value}个视频`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const availablePosition = findAvailablePosition();
|
||||||
|
if (availablePosition !== -1) {
|
||||||
|
videoPositions.value[availablePosition] = item;
|
||||||
|
selectedNodes.value.push(itemLabel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 切换布局
|
||||||
|
const changeLayout = (layoutType) => {
|
||||||
|
currentLayout.value = layoutType;
|
||||||
|
videoPositions.value = {};
|
||||||
|
selectedNodes.value = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理视频点击
|
||||||
|
const handleVideoClick = (index) => {
|
||||||
|
// 重置所有视频的选中状态
|
||||||
|
const updatedList = [...videoList.value];
|
||||||
|
updatedList.forEach((video, i) => {
|
||||||
|
video.isSelected = i === index && video.name !== "待选择区域";
|
||||||
|
});
|
||||||
|
|
||||||
|
// 注意:由于videoList是计算属性,这里直接修改不会持久化
|
||||||
|
// 我们可以通过其他方式处理选中状态,或者如果需要持久化选中状态,可以添加一个响应式变量
|
||||||
|
};
|
||||||
|
|
||||||
|
// 移除视频
|
||||||
|
const removeVideo = (index) => {
|
||||||
|
if (videoPositions.value[index]) {
|
||||||
|
const removedDevice = videoPositions.value[index];
|
||||||
|
delete videoPositions.value[index];
|
||||||
|
selectedNodes.value = selectedNodes.value.filter(label => label !== removedDevice.label);
|
||||||
|
ElMessage.info(`已移除${removedDevice.label}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 监听搜索
|
||||||
|
watch(searchDevice, () => {
|
||||||
|
// 搜索功能已在 computed 中处理
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 初始化样式
|
||||||
|
const style = document.createElement("style");
|
||||||
|
style.textContent = `
|
||||||
.video-item .close-icon {
|
.video-item .close-icon {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 5px;
|
top: 5px;
|
||||||
|
|
@ -274,10 +300,6 @@ onMounted(() => {
|
||||||
.video-item:hover .close-icon {
|
.video-item:hover .close-icon {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
.el-tree.is-disabled .el-tree-node__content {
|
|
||||||
cursor: not-allowed;
|
|
||||||
opacity: 0.7;
|
|
||||||
}
|
|
||||||
.video-header .icon,
|
.video-header .icon,
|
||||||
.video-placeholder .icon {
|
.video-placeholder .icon {
|
||||||
color: #409eff;
|
color: #409eff;
|
||||||
|
|
@ -295,7 +317,6 @@ onMounted(() => {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
/* 修复:样式选择器无语法错误,分号闭合 */
|
|
||||||
.monitor-page {
|
.monitor-page {
|
||||||
height: calc(100vh - 84px);
|
height: calc(100vh - 84px);
|
||||||
background-color: #f5f7fa;
|
background-color: #f5f7fa;
|
||||||
|
|
@ -310,7 +331,7 @@ onMounted(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
.device-tree-panel {
|
.device-tree-panel {
|
||||||
flex: 0 0 220px;
|
flex: 0 0 280px;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||||
|
|
@ -333,12 +354,74 @@ onMounted(() => {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.device-tree {
|
.custom-tree {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tree-group {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-label {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
padding: 8px 0;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 8px 12px;
|
||||||
|
margin: 4px 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-item:hover {
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-item.selected {
|
||||||
|
background-color: #e6f7ff;
|
||||||
|
color: #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-item.disabled:not(.selected) {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-icon {
|
||||||
|
color: #909399;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-item.selected .item-icon {
|
||||||
|
color: #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-label {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected-indicator {
|
||||||
|
color: #409eff;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
.video-wall-panel {
|
.video-wall-panel {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
|
|
@ -376,8 +459,14 @@ onMounted(() => {
|
||||||
position: relative;
|
position: relative;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
|
min-height: 120px;
|
||||||
|
}
|
||||||
|
.item-icon {
|
||||||
|
width: 18px;
|
||||||
|
}
|
||||||
|
.icon {
|
||||||
|
width: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.video-item:hover {
|
.video-item:hover {
|
||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -254,7 +254,6 @@ const handleCurrentChange = (val) => {
|
||||||
.area-management-page {
|
.area-management-page {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
background-color: #f8fafc;
|
background-color: #f8fafc;
|
||||||
min-height: calc(100vh - 64px); /* 适配顶部导航高度 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 卡片样式:圆角 + 悬浮阴影增强层次感 */
|
/* 卡片样式:圆角 + 悬浮阴影增强层次感 */
|
||||||
|
|
|
||||||
|
|
@ -294,7 +294,6 @@ const handleCurrentChange = (val) => {
|
||||||
.cabin-management-page {
|
.cabin-management-page {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
background-color: #f8fafc;
|
background-color: #f8fafc;
|
||||||
min-height: calc(100vh - 64px); /* 适配顶部导航高度 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 卡片样式:圆角 + 悬浮阴影增强层次感 */
|
/* 卡片样式:圆角 + 悬浮阴影增强层次感 */
|
||||||
|
|
|
||||||
|
|
@ -237,7 +237,6 @@ const padZero = (num) => {
|
||||||
.meter-monitoring-page {
|
.meter-monitoring-page {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
background-color: #f8fafc;
|
background-color: #f8fafc;
|
||||||
min-height: calc(100vh - 64px); /* 适配顶部导航高度 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 卡片样式:圆角 + 悬浮阴影,增强层次感 */
|
/* 卡片样式:圆角 + 悬浮阴影,增强层次感 */
|
||||||
|
|
|
||||||
|
|
@ -346,7 +346,6 @@ const getList = () => {
|
||||||
.alarm-management-page {
|
.alarm-management-page {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
background-color: #f8fafc;
|
background-color: #f8fafc;
|
||||||
min-height: calc(100vh - 64px);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-card {
|
.page-card {
|
||||||
|
|
|
||||||
|
|
@ -311,7 +311,7 @@
|
||||||
import { ref, reactive, toRefs, onUnmounted } from 'vue';
|
import { ref, reactive, toRefs, onUnmounted } from 'vue';
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||||
import { User, Edit, Upload, Plus } from '@element-plus/icons-vue';
|
import { User, Edit, Upload, Plus } from '@element-plus/icons-vue';
|
||||||
import ExcelExporter from "./../../utils/ExcelExporter"
|
import ExcelExporter from "@/utils/ExcelExporter"
|
||||||
|
|
||||||
// 用户权限
|
// 用户权限
|
||||||
const isAdmin = ref(true);
|
const isAdmin = ref(true);
|
||||||
|
|
@ -721,7 +721,6 @@ onUnmounted(() => {
|
||||||
.maintenance-page {
|
.maintenance-page {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
background-color: #f8fafc;
|
background-color: #f8fafc;
|
||||||
min-height: calc(100vh - 64px);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-card {
|
.page-card {
|
||||||
|
|
|
||||||
|
|
@ -627,7 +627,6 @@ onUnmounted(() => {
|
||||||
.inspection-page {
|
.inspection-page {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
background-color: #f8fafc;
|
background-color: #f8fafc;
|
||||||
min-height: calc(100vh - 64px);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-card {
|
.page-card {
|
||||||
|
|
|
||||||
|
|
@ -356,7 +356,6 @@ onMounted(() => {
|
||||||
.report-management-page {
|
.report-management-page {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
background-color: #f8fafc;
|
background-color: #f8fafc;
|
||||||
min-height: calc(100vh - 64px); /* 适配顶部导航高度 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-card {
|
.page-card {
|
||||||
|
|
|
||||||
|
|
@ -247,7 +247,6 @@ const MOUSE_CURRENT_Y = ref(0);
|
||||||
|
|
||||||
//悬浮展示设备 摄像头基本信息相关(保持不变)
|
//悬浮展示设备 摄像头基本信息相关(保持不变)
|
||||||
const activeMarker = ref({ target: "" });
|
const activeMarker = ref({ target: "" });
|
||||||
const Shrink = ref(false);
|
|
||||||
|
|
||||||
const SELECT_ACTION_TYPE = ref("");
|
const SELECT_ACTION_TYPE = ref("");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -185,7 +185,6 @@ const handleCurrentChange = (val) => {
|
||||||
.operation-record-page {
|
.operation-record-page {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
background-color: #f8fafc;
|
background-color: #f8fafc;
|
||||||
min-height: calc(100vh - 64px); /* 适配顶部导航高度 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 卡片样式:圆角 + 悬浮阴影增强层次感 */
|
/* 卡片样式:圆角 + 悬浮阴影增强层次感 */
|
||||||
|
|
|
||||||
|
|
@ -444,7 +444,6 @@ const getList = () => {};
|
||||||
.meter-monitoring-page {
|
.meter-monitoring-page {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
background-color: #f8fafc;
|
background-color: #f8fafc;
|
||||||
min-height: calc(100vh - 64px); /* 适配顶部导航高度 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 卡片样式:圆角 + 悬浮阴影,增强层次感 */
|
/* 卡片样式:圆角 + 悬浮阴影,增强层次感 */
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue