修改问题

main
刘政 3 weeks ago
parent 19025c6442
commit d5ec93ded6

@ -2,7 +2,7 @@ import { createApp } from 'vue';
import pinia from '@/stores'; import pinia from '@/stores';
import '@/styles/index.scss'; import '@/styles/index.scss';
import 'uno.css'
// element plus // element plus
import ElementPlus from 'element-plus'; import ElementPlus from 'element-plus';
// element css // element css

@ -124,7 +124,11 @@
</template> </template>
</el-dialog> </el-dialog>
<!-- 注册人脸的弹框 --> <!-- 注册人脸的弹框 -->
<el-dialog v-model="registerFaceDialog.visible" :title="registerFaceDialog.title" :destroy-on-close="true"> <el-dialog v-model="registerFaceDialog.visible"
:title="registerFaceDialog.title"
:destroy-on-close="true"
draggable
@closed="handelCancelRegisterFace">
<el-form <el-form
:model="registerFaceForm" :model="registerFaceForm"
ref="registerFaceFormRef" ref="registerFaceFormRef"
@ -152,7 +156,9 @@
:http-request="BeforeHandleHttpUpload" :http-request="BeforeHandleHttpUpload"
> >
<template v-if="registerFaceForm.imageUrl"> <template v-if="registerFaceForm.imageUrl">
<img :src="'http://127.0.0.1:9991/api/admin'+registerFaceForm.imageUrl" class="avatar" width="146px" height="146px" alt=""/> <img :src="'http://127.0.0.1:9991/api/admin'+registerFaceForm.imageUrl"
style="width: 100%; height: 100%; object-fit: cover"
alt=""/>
</template> </template>
<template v-else> <template v-else>
<el-icon><Avatar /></el-icon> <el-icon><Avatar /></el-icon>
@ -162,7 +168,7 @@
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<el-button @click="registerFaceDialog.visible = false">取消</el-button> <el-button @click="handelCancelRegisterFace"></el-button>
<el-button type="primary" @click="handleRegisterFace"></el-button> <el-button type="primary" @click="handleRegisterFace"></el-button>
</template> </template>
</el-dialog> </el-dialog>
@ -242,7 +248,13 @@ const queryParams = reactive({
pageSize: 3 // pageSize: 3 //
}) })
const total = ref(0) const total = ref(0)
//
const handelCancelRegisterFace=()=>{
registerFaceDialog.visible = false;
if(registerFaceFormRef.value) {
registerFaceFormRef.value.resetFields();
}
}
// //
const handleNodeClick = (data: Tree) => { const handleNodeClick = (data: Tree) => {
queryParams.libraryId = data.id; queryParams.libraryId = data.id;

@ -197,8 +197,10 @@
append-to-body append-to-body
class="sentinel-dialog" class="sentinel-dialog"
> >
<el-form label-width="100px" label-position="left"> <el-form label-width="100px"
<el-form-item label="自动守望"> label-position="left"
:model="sentinelConfig">
<el-form-item label="自动守望" >
<el-switch <el-switch
v-model="sentinelConfig.enabled" v-model="sentinelConfig.enabled"
active-text="开启" active-text="开启"
@ -208,7 +210,7 @@
</el-form-item> </el-form-item>
<template v-if="sentinelConfig.enabled"> <template v-if="sentinelConfig.enabled">
<el-form-item label="等待时间"> <el-form-item label="等待时间" prop="time">
<el-input-number <el-input-number
v-model="sentinelConfig.time" v-model="sentinelConfig.time"
:min="5" :min="5"
@ -218,8 +220,11 @@
/> />
<span class="unit-text"></span> <span class="unit-text"></span>
</el-form-item> </el-form-item>
<el-form-item label="预置点"> <el-form-item label="预置点" prop="presetIndex">
<el-select v-model="sentinelConfig.presetIndex" size="small" placeholder="请选择预置点"> <el-select v-model="sentinelConfig.presetIndex"
size="small"
clearable
placeholder="请选择预置点">
<el-option <el-option
v-for="item in existPresetList" v-for="item in existPresetList"
:key="item.presetIndex" :key="item.presetIndex"
@ -338,7 +343,7 @@ const isSaving = ref(false);
const sentinelConfig = reactive({ const sentinelConfig = reactive({
enabled: false, enabled: false,
time: 15, time: 15,
presetIndex: 1 presetIndex: undefined
}); });
// id // id
const handleChange=async (value:any)=>{ const handleChange=async (value:any)=>{
@ -385,19 +390,17 @@ const saveSentinelConfig = async () => {
id: cameraId, id: cameraId,
openOrNo: sentinelConfig.enabled ? 1 : 0, openOrNo: sentinelConfig.enabled ? 1 : 0,
watchTime: sentinelConfig.time, watchTime: sentinelConfig.time,
presetIndex: sentinelConfig.presetIndex presetIndex: sentinelConfig.presetIndex as unknown as number
}); });
if(res.data) if(res.data)
{ {
ElMessage.success("硬件参数设置成功!"); ElMessage.success("硬件参数设置成功!");
// //
await loadPresets(); await loadPresets();
}else {
ElMessage.error("写入失败,请检查设备连接");
} }
sentinelVisible.value = false; sentinelVisible.value = false;
} catch (e) { } catch (e) {
ElMessage.error("写入失败,请检查设备连接"); console.log("写入失败,请检查设备连接")
} finally { } finally {
isSaving.value = false; isSaving.value = false;
} }

@ -8,6 +8,7 @@
@on-save-click="onSaveClick" @on-save-click="onSaveClick"
@on-thumbnail-click="onThumbnailClick" @on-thumbnail-click="onThumbnailClick"
></mt-edit> ></mt-edit>
</div> </div>
</template> </template>

@ -3,5 +3,5 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import doBottomMenuIndex from '@/components/menu/doBottomMenuIndex.vue'; import doBottomMenuIndex from '@/views/teacher/teacherStatistics/components/menu/doBottomMenuIndex.vue';
</script> </script>

@ -5,13 +5,12 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import doCreateMenu from '@/components/menu/doCreateMenu.vue'; import doCreateMenu from '@/views/teacher/teacherStatistics/components/menu/doCreateMenu.vue';
import { constantRoutes } from '@/router/newIndex';
import { computed } from 'vue'; import { computed } from 'vue';
// //
const filteredRoutes = computed(() => { const filteredRoutes = computed(() => {
return constantRoutes.filter((route) => route.meta && !route.meta.hidden); // return constantRoutes.filter((route) => route.meta && !route.meta.hidden);
}); });
</script> </script>

@ -97,8 +97,6 @@ export default {
<script lang="ts" setup> <script lang="ts" setup>
import { Document, Menu as IconMenu, Location } from '@element-plus/icons-vue'; import { Document, Menu as IconMenu, Location } from '@element-plus/icons-vue';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { stairRouteNames, stairRouteChildrenMap } from '@/router/newIndex';
import emitter from '@/utils/emitter';
// src\router\newIndex.ts // src\router\newIndex.ts
const props = withDefaults( const props = withDefaults(
@ -112,27 +110,27 @@ const props = withDefaults(
); );
function isStairRoute(routeName: string) { function isStairRoute(routeName: string) {
if (stairRouteNames.includes(routeName)) { // if (stairRouteNames.includes(routeName)) {
emitter.emit('getStairRoute', routeName); // emitter.emit('getStairRoute', routeName);
} // }
} }
const router = useRouter(); const router = useRouter();
function headerCliek(val: any) { function headerCliek(val: any) {
if (stairRouteNames.includes(val.index)) { // if (stairRouteNames.includes(val.index)) {
isStairRoute(val.index); // isStairRoute(val.index);
} else { // } else {
stairRouteChildrenMap.forEach((children: string[], parent: string) => { // stairRouteChildrenMap.forEach((children: string[], parent: string) => {
if (children.includes(val.index)) { // if (children.includes(val.index)) {
isStairRoute(parent); // isStairRoute(parent);
} // }
}); // });
} // }
router.push({ // router.push({
name: val.index, // name: val.index,
query: { screen: val.index } // query: { screen: val.index }
}); // });
} }
function handleSubMenuTitleClick(menu: any) { function handleSubMenuTitleClick(menu: any) {
@ -146,14 +144,14 @@ function handleSubMenuTitleClick(menu: any) {
// //
function handleSubMenuClick(menu: any) { function handleSubMenuClick(menu: any) {
if (stairRouteNames.includes(menu.name)) { // if (stairRouteNames.includes(menu.name)) {
isStairRoute(menu.name); // isStairRoute(menu.name);
// // //
router.push({ // router.push({
name: menu.name, // name: menu.name,
query: { screen: menu.name } // query: { screen: menu.name }
}); // });
} // }
} }
</script> </script>

@ -6,8 +6,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import menuDoCreateMenu from '@/components/menu/menuDoCreateMenu.vue'; import menuDoCreateMenu from '@/views/teacher/teacherStatistics/components/menu/menuDoCreateMenu.vue';
import constantRoutes from '@/router/newIndex';
import { type RouteRecordRaw } from 'vue-router'; import { type RouteRecordRaw } from 'vue-router';
import { onMounted, ref } from 'vue'; import { onMounted, ref } from 'vue';
@ -56,8 +55,8 @@ function loadMenuData(constantRoutes: Readonly<RouteRecordRaw[]>, treeData: any[
const emit = defineEmits(['getMenuList']); const emit = defineEmits(['getMenuList']);
onMounted(() => { onMounted(() => {
loadMenuData(constantRoutes.options.routes, treeData); // loadMenuData(constantRoutes.options.routes, treeData);
menuRoutes.value = Array.from(constantRoutes.options.routes); // menuRoutes.value = Array.from(constantRoutes.options.routes);
emit('getMenuList', treeData); emit('getMenuList', treeData);
}); });
</script> </script>

@ -7,7 +7,6 @@
size="small" size="small"
@click="emits('update:leftAside', !headerPanelProps.leftAside)" @click="emits('update:leftAside', !headerPanelProps.leftAside)"
> >
<el-icon :size="20"> <el-icon :size="20">
<svg-analysis v-if="headerPanelProps.leftAside" name="menu-fold"></svg-analysis> <svg-analysis v-if="headerPanelProps.leftAside" name="menu-fold"></svg-analysis>
<svg-analysis v-else name="menu-unfold"></svg-analysis> <svg-analysis v-else name="menu-unfold"></svg-analysis>
@ -17,7 +16,7 @@
<el-divider direction="vertical"></el-divider> <el-divider direction="vertical"></el-divider>
</div> </div>
<div class="flex justify-between" style="width: calc(100% - 20px)"> <div class="flex justify-between" style="width: calc(100% - 20px);" >
<div class="flex items-center"> <div class="flex items-center">
<el-button-group> <el-button-group>
<el-button <el-button
@ -92,11 +91,7 @@
}}</el-tag> }}</el-tag>
</div> </div>
<div class="flex items-center mr-20px"> <div class="flex items-center mr-20px">
<!-- <el-button text circle size="small" @click="emits('onReturnClick')">
<el-icon title="返回" :size="20">
<svg-analysis name="return"></svg-analysis>
</el-icon>
</el-button> -->
<el-button type="primary" size="small" @click="emits('onImageDialog')"> <el-button type="primary" size="small" @click="emits('onImageDialog')">
图片模型 图片模型
@ -104,23 +99,12 @@
<el-divider v-if="headerPanelProps.useThumbnail" direction="vertical"></el-divider> <el-divider v-if="headerPanelProps.useThumbnail" direction="vertical"></el-divider>
<!-- <el-upload
accept="image/*"
:action="BASE_URL + '/fileStorage/saveFile'"
:on-success="saveImageSuccess"
:show-file-list="false"
style="width: 100%; padding: 0 10px"
>
<el-button type="primary">图片存储</el-button>
</el-upload> -->
<!-- 数据文件存储 --> <!-- 数据文件存储 -->
<el-button type="primary" size="small" @click="emits('onRightDrawer')"> <el-button type="primary" size="small" @click="emits('onRightDrawer')">
模型文件 模型文件
</el-button> </el-button>
<el-divider direction="vertical"></el-divider> <el-divider direction="vertical"></el-divider>
<el-button <el-button
text text
circle circle
@ -132,83 +116,9 @@
<svg-analysis v-else name="menu-fold"></svg-analysis> <svg-analysis v-else name="menu-fold"></svg-analysis>
</el-icon> </el-icon>
</el-button> </el-button>
<!-- <el-button text circle size="small" @click="emits('onSaveClick')">
<el-icon title="保存" :size="20">
<svg-analysis name="save"></svg-analysis>
</el-icon>
</el-button> -->
<!-- <el-divider v-if="headerPanelProps.useThumbnail" direction="vertical"></el-divider> -->
<!-- <el-button
v-if="headerPanelProps.useThumbnail"
text
circle
size="small"
@click="emits('onThumbnailClick')"
>
<el-icon title="生成缩略图" :size="20">
<svg-analysis name="thumbnail"></svg-analysis>
</el-icon>
</el-button> -->
<!-- <el-divider direction="vertical"></el-divider> -->
<!-- <el-button text circle size="small" @click="emits('onPreviewClick')">
<el-icon title="预览" :size="20">
<svg-analysis name="preview"></svg-analysis>
</el-icon>
</el-button> -->
</div> </div>
</div> </div>
<div class="flex items-center">
<!-- <el-button
text
circle
size="small"
@click="emits('update:rightAside', !headerPanelProps.rightAside)"
>
<el-icon :size="20" style="cursor: pointer">
<svg-analysis v-if="headerPanelProps.rightAside" name="menu-unfold"></svg-analysis>
<svg-analysis v-else name="menu-fold"></svg-analysis>
</el-icon>
</el-button> -->
<div class="flex items-center">
<!-- <el-button text circle size="small" @click="onHelpClick">
<el-icon title="帮助" :size="20">
<svg-analysis name="help"></svg-analysis>
</el-icon>
</el-button> -->
<!-- <el-button text circle size="small" @click="toggle">
<el-icon title="全屏" :size="20">
<svg-analysis :name="isFullscreen ? 'exit-full-screen' : 'full-screen'"></svg-analysis>
</el-icon>
</el-button> -->
<!-- <el-divider direction="vertical"></el-divider> -->
<!-- <el-button text circle size="small" @click="changeLockState">
<el-icon :title="headerPanelProps.lockState ? '已锁定' : '已解锁'" :size="20">
<svg-analysis :name="headerPanelProps.lockState ? 'lock' : 'unlock'"></svg-analysis>
</el-icon>
</el-button> -->
<!-- <el-divider direction="vertical"></el-divider> -->
<!-- <el-button text circle size="small" class="mr-10px">
<el-icon
:title="isDark ? '切换到日间模式' : '切换到夜间模式'"
:size="20"
@click="toggleDark()"
>
<svg-analysis :name="isDark ? 'light' : 'dark'"></svg-analysis>
</el-icon>
</el-button> -->
</div>
</div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">

@ -6,8 +6,7 @@
height="45px" height="45px"
class="dark:bg-myDarkBgColor cb-border p-0 select-none" class="dark:bg-myDarkBgColor cb-border p-0 select-none"
@mousedown="mainPanelRef?.stopListenerKeyDown()" @mousedown="mainPanelRef?.stopListenerKeyDown()"
> > <!-- 头部2 -->
<!-- 头部2 -->
<header-panel <header-panel
v-model:leftAside="aside_state.left_show" v-model:leftAside="aside_state.left_show"
v-model:rightAside="aside_state.right_show" v-model:rightAside="aside_state.right_show"
@ -39,7 +38,6 @@
@on-right-drawer="onRightDrawer" @on-right-drawer="onRightDrawer"
@on-image-dialog="onImageDialog" @on-image-dialog="onImageDialog"
> >
</header-panel> </header-panel>
</el-header> </el-header>

@ -40,13 +40,13 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'; import { ref, reactive, onMounted } from 'vue';
import RenderCore from '@/components/mt-edit/components/render-core/index.vue'; import RenderCore from '@/views/teacher/teacherStatistics/components/mt-edit/components/render-core/index.vue';
import type { IExportJson } from '../mt-edit/components/types'; import type { IExportJson } from '../mt-edit/components/types';
import { useExportJsonToDoneJson } from '../mt-edit/composables'; import { useExportJsonToDoneJson } from '../mt-edit/composables';
import type { IDoneJson } from '../mt-edit/store/types'; import type { IDoneJson } from '../mt-edit/store/types';
import { getItemAttr, previewCompareVal, setItemAttr } from '../mt-edit/utils'; import { getItemAttr, previewCompareVal, setItemAttr } from '../mt-edit/utils';
import { ElScrollbar, ElMessage, ElMessageBox } from 'element-plus'; import { ElScrollbar, ElMessage, ElMessageBox } from 'element-plus';
import DragCanvas from '@/components/mt-edit/components/drag-canvas/index.vue'; import DragCanvas from '@/views/teacher/teacherStatistics/components/mt-edit/components/drag-canvas/index.vue';
type MtPreviewProps = { type MtPreviewProps = {
exportJson?: IExportJson; exportJson?: IExportJson;
canZoom?: boolean; canZoom?: boolean;

@ -1,187 +0,0 @@
<template>
<div class="div3D" ref="div3D"></div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue';
//threejs
import {
AxesHelper,
Color,
PerspectiveCamera,
Scene,
WebGLRenderer,
EquirectangularReflectionMapping,
Group
} from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
const div3D = ref<HTMLDivElement>();
//
const scene = new Scene();
scene.background = new Color(0xd1d1d1);
//
const camera = new PerspectiveCamera(
25,
div3D.value?.clientWidth! / div3D.value?.clientHeight!,
0.1,
1000
); // 1
camera.position.set(50, 30, 0);
camera.lookAt(0, 25, 0);
scene.add(camera);
//
const render = new WebGLRenderer({ antialias: true }); // 齿
//
const controls = new OrbitControls(camera, render.domElement);
//
controls.enableZoom = false;
controls.autoRotate = false;
controls.enablePan = false; //
controls.autoRotateSpeed = 1;
controls.dampingFactor = 0.2;
controls.rotateSpeed = 0.3; // 1.0
controls.enableDamping = true;
controls.enableRotate = true;
controls.minDistance = 10;
// 4545
// 4545
controls.minPolarAngle = Math.PI / 4; // 45 (π/4)
controls.maxPolarAngle = (Math.PI * 3) / 4; // 45 (3π/4)
const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
loader.setDRACOLoader(dracoLoader);
let meshArr: Group[] = [];
loader.load('/models/smokeSiren/threeObject.glb', (gltf: any) => {
// gltf.scene.visible = false;
scene.add(gltf.scene);
gltf.scene.children.forEach((child: any) => {
meshArr.push(child);
child.visible = false;
});
// moduleType
updateMeshVisibility();
});
// console.log('meshArr:', meshArr);
// Props
const props = defineProps({
moduleType: {
type: String,
default: 'smokeDetector'
}
});
// moduleType
function updateMeshVisibility() {
meshArr.forEach((mesh: Group) => {
if (props.moduleType && props.moduleType.includes(mesh.name)) {
mesh.visible = true;
} else {
mesh.visible = false;
}
});
}
// props
import { watch } from 'vue';
import { debug } from 'console';
watch(
() => props.moduleType,
() => {
updateMeshVisibility();
}
);
meshArr.forEach((mesh: Group) => {
if (props.moduleType.includes(mesh.name as string)) {
mesh.visible = true;
} else {
mesh.visible = false;
}
});
//
const rgbELoader = new RGBELoader().load(
'/models/texture/Alex_Hart-Nature_Lab_Bones_2k.hdr',
(envMap: any) => {
envMap.mapping = EquirectangularReflectionMapping;
scene.environment = envMap;
}
);
//
// const axes = new AxesHelper(20);
// scene.add(axes);
//
function handleResize() {
if (!div3D.value) return;
const container = div3D.value;
const width = container.clientWidth;
const height = container.clientHeight;
//
camera.aspect = width / height;
camera.updateProjectionMatrix();
//
render.setSize(width, height);
}
function init() {
requestAnimationFrame(init);
controls.update();
render.render(scene, camera);
}
onMounted(() => {
//
handleResize();
//
init();
//
div3D.value?.appendChild(render.domElement);
//
const resizeObserver = new ResizeObserver(handleResize);
if (div3D.value) {
resizeObserver.observe(div3D.value);
}
// 便
(window as any)._resizeObserver = resizeObserver;
});
onUnmounted(() => {
//
if ((window as any)._resizeObserver) {
(window as any)._resizeObserver.disconnect();
delete (window as any)._resizeObserver;
}
});
</script>
<style scoped>
.div3D {
margin: 0;
padding: 0;
/* width: 500px;
height: 500px; */
width: 100%;
height: 100%;
position: relative;
}
</style>

@ -1,140 +0,0 @@
<template>
<div class="div3D" ref="div3D"></div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue';
//threejs
import {
AxesHelper,
Color,
PerspectiveCamera,
Scene,
WebGLRenderer,
EquirectangularReflectionMapping,
Group
} from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
const div3D = ref<HTMLDivElement>();
//
const scene = new Scene();
//
const camera = new PerspectiveCamera(
25,
div3D.value?.clientWidth! / div3D.value?.clientHeight!,
0.05,
1000
); // 1
camera.position.set(0, -1.8, 5);
camera.lookAt(0, 0, 0);
scene.add(camera);
//
const render = new WebGLRenderer({ antialias: true }); // 齿
render.setClearColor(new Color('#131519'));
//
const controls = new OrbitControls(camera, render.domElement);
//
// controls.enableZoom = false;
// controls.autoRotate = false;
// controls.enablePan = false; //
controls.autoRotateSpeed = 1;
controls.dampingFactor = 0.2;
controls.rotateSpeed = 0.3; // 1.0
controls.enableDamping = true;
controls.enableRotate = true;
controls.minDistance = 1; //
// 4545
// 4545
controls.minPolarAngle = Math.PI / 4; // 45 (π/4)
controls.maxPolarAngle = (Math.PI * 3) / 4; // 45 (3π/4)
const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
loader.setDRACOLoader(dracoLoader);
let meshArr: Group[] = [];
loader.load('/models/fireAlarmControl/fire_alarm_control.glb', (gltf: any) => {
scene.add(gltf.scene);
});
//
const rgbELoader = new RGBELoader().load(
'/models/texture/Alex_Hart-Nature_Lab_Bones_2k.hdr',
(envMap: any) => {
envMap.mapping = EquirectangularReflectionMapping;
scene.environment = envMap;
}
);
//
// const axes = new AxesHelper(20);
// scene.add(axes);
//
function handleResize() {
if (!div3D.value) return;
const container = div3D.value;
const width = container.clientWidth;
const height = container.clientHeight;
//
camera.aspect = width / height;
camera.updateProjectionMatrix();
//
render.setSize(width, height);
}
function init() {
requestAnimationFrame(init);
controls.update();
render.render(scene, camera);
}
onMounted(() => {
//
handleResize();
//
init();
//
div3D.value?.appendChild(render.domElement);
//
const resizeObserver = new ResizeObserver(handleResize);
if (div3D.value) {
resizeObserver.observe(div3D.value);
}
// 便
(window as any)._resizeObserver = resizeObserver;
});
onUnmounted(() => {
//
if ((window as any)._resizeObserver) {
(window as any)._resizeObserver.disconnect();
delete (window as any)._resizeObserver;
}
});
</script>
<style scoped>
.div3D {
margin: 0;
padding: 0;
/* width: 500px;
height: 500px; */
width: 100%;
height: 100%;
position: relative;
}
</style>

@ -1,223 +0,0 @@
<template>
<div class="div3D" ref="div3D" v-loading="loading" element-loading-text="渲染模型..."></div>
<!-- 固定位置的控制按钮 -->
<div class="control-buttons">
<el-button
type="warning"
@click="tranlate(new Vector3(28, 10, -10), new Vector3(0, 0, 0))"
:icon="Refresh"
/>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue';
//threejs
import {
AxesHelper,
Color,
PerspectiveCamera,
Scene,
WebGLRenderer,
EquirectangularReflectionMapping,
Group,
Vector3,
AmbientLight,
DirectionalLight,
PointLight
} from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
import gsap from 'gsap';
import { Star, Bell, MagicStick, Refresh, Compass } from '@element-plus/icons-vue';
const loading = ref(true); // loading
const div3D = ref<HTMLDivElement>();
//
const scene = new Scene();
//
const camera = new PerspectiveCamera(
45,
div3D.value?.clientWidth! / div3D.value?.clientHeight!,
0.05,
200
); // 1
camera.position.set(30, 15, -15);
camera.lookAt(0, 0, 0);
scene.add(camera);
//
const render = new WebGLRenderer({
antialias: true, // 齿
powerPreference: 'high-performance', // 使GPU
stencil: false, //
depth: true,
logarithmicDepthBuffer: true //
});
render.setClearColor(new Color('#131519'));
//
render.shadowMap.enabled = false; //
//
const controls = new OrbitControls(camera, render.domElement);
//
// controls.enableZoom = false;
// controls.autoRotate = false;
// controls.enablePan = false; //
controls.autoRotateSpeed = 1;
controls.dampingFactor = 0.2;
controls.rotateSpeed = 0.3; // 1.0
controls.enableDamping = true;
controls.enableRotate = true;
controls.minDistance = 1; //
// 4545
// 4545
controls.minPolarAngle = Math.PI / 4; // 45 (π/4)
controls.maxPolarAngle = (Math.PI * 3) / 4; // 45 (3π/4)
//
const startTime = Date.now();
const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
loader.setDRACOLoader(dracoLoader);
let meshArr: Group[] = [];
loader.load('/models/substation/substation.gltf', (gltf: any) => {
scene.add(gltf.scene);
const loadTime = Date.now() - startTime;
//
controls.update();
render.render(scene, camera);
console.log(`大场景模型已加载并显示,总耗时:${loadTime}ms`);
loading.value = false;
});
//
const ambientLight = new AmbientLight('#ffffff', 1.2); // 0.6 1.0
scene.add(ambientLight);
//
const directionalLight = new DirectionalLight('#ffffff', 1.4); // 0.2 0.8
scene.add(directionalLight);
directionalLight.position.set(20, 20, 10);
// //
const pointLight = new PointLight('#ffffff', 1.4, 1800); // 0.5 1.0
scene.add(pointLight);
pointLight.position.set(0, 40, 0);
// //
// const rgbELoader = new RGBELoader().load(
// '/models/texture/Alex_Hart-Nature_Lab_Bones_2k.hdr',
// (envMap: any) => {
// envMap.mapping = EquirectangularReflectionMapping;
// scene.environment = envMap;
// }
// );
//
const timeline1 = gsap.timeline();
const timeline2 = gsap.timeline();
//
function tranlate(position: Vector3, target: Vector3) {
timeline1.to(camera.position, {
x: position.x,
y: position.y,
z: position.z,
duration: 1,
ease: 'power2.inOut'
});
timeline2.to(controls.target, {
x: target.x,
y: target.y,
z: target.z,
duration: 1,
ease: 'power2.inOut'
});
}
//
// const axes = new AxesHelper(20);
// scene.add(axes);
//
function handleResize() {
if (!div3D.value) return;
const container = div3D.value;
const width = container.clientWidth;
const height = container.clientHeight;
//
camera.aspect = width / height;
camera.updateProjectionMatrix();
//
render.setSize(width, height);
}
function init() {
requestAnimationFrame(init);
controls.update();
render.render(scene, camera);
}
onMounted(() => {
//
handleResize();
//
init();
//
div3D.value?.appendChild(render.domElement);
//
const resizeObserver = new ResizeObserver(handleResize);
if (div3D.value) {
resizeObserver.observe(div3D.value);
}
// 便
(window as any)._resizeObserver = resizeObserver;
});
onUnmounted(() => {
//
if ((window as any)._resizeObserver) {
(window as any)._resizeObserver.disconnect();
delete (window as any)._resizeObserver;
}
});
</script>
<style scoped>
.div3D {
margin: 0;
padding: 0;
/* width: 500px;
height: 500px; */
width: 100%;
height: 100%;
position: relative;
}
.control-buttons {
position: absolute;
top: 50%;
left: 2%;
transform: translateY(-50%); /* 垂直居中 */
display: flex;
flex-direction: column;
gap: 20px;
z-index: 100;
align-items: flex-start; /* 确保所有按钮左对齐 */
}
</style>

@ -1,341 +0,0 @@
<template>
<div class="div3D" ref="div3D">
<!-- 加载状态显示 -->
<div v-if="isLoading" class="loading-overlay">
<div class="loading-content">
<div class="progress-bar">
<div class="progress-fill" :style="{ width: loadProgress + '%' }"></div>
</div>
<p>正在加载资源... {{ loadProgress }}%</p>
<p class="hint">资源加载完成后还需短暂处理</p>
</div>
</div>
</div>
<!-- 固定位置的控制按钮 -->
<div class="control-buttons">
<el-button
type="warning"
@click="tranlate(new Vector3(80, 35, -50), new Vector3(0, 0, 0))"
:icon="Refresh"
/>
<!-- <el-button type="primary" @click="displayModel('area_oneFloor')" :icon="Bell" />
<el-button type="warning" @click="displayModel('smokeDetector')" :icon="Compass" />
<el-button type="warning" @click="displayModel('acoustoOptic')" :icon="MagicStick" />
<el-button type="warning" @click="displayModel('manual')" :icon="Star" /> -->
<!-- 1:左右 2:上下 3:前后 -->
<!-- <el-button type="warning" @click="displayModel('refresh')" :icon="Refresh" />
<el-button
type="warning"
@click="meshControllerArr[0].controller.startMalfunction()"
:icon="Refresh"
/>
<el-button
type="warning"
@click="meshControllerArr[0].controller.restore()"
:icon="Refresh"
/> -->
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted, nextTick } from 'vue';
//threejs
import {
AxesHelper,
Color,
PerspectiveCamera,
Scene,
WebGLRenderer,
EquirectangularReflectionMapping,
Group,
Vector3
} from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
import gsap from 'gsap';
import { Star, Bell, MagicStick, Refresh, Compass } from '@element-plus/icons-vue';
const div3D = ref<HTMLDivElement>();
const isLoading = ref(true); //
const loadProgress = ref(0); //
//
const scene = new Scene();
//
const camera = new PerspectiveCamera(
45,
div3D.value?.clientWidth! / div3D.value?.clientHeight!,
0.05,
1000
); // 1
camera.position.set(80, 35, -50);
camera.lookAt(0, 0, 0);
scene.add(camera);
//
const render = new WebGLRenderer({
antialias: false, // 齿
powerPreference: 'high-performance', // 使GPU
stencil: false, //
depth: true,
logarithmicDepthBuffer: true //
});
render.setClearColor(new Color('#131519'));
//
render.shadowMap.enabled = false; //
//
const controls = new OrbitControls(camera, render.domElement);
//
// controls.enableZoom = false;
// controls.autoRotate = false;
// controls.enablePan = false; //
controls.autoRotateSpeed = 1;
controls.dampingFactor = 0.2;
controls.rotateSpeed = 0.3; // 1.0
controls.enableDamping = true;
controls.enableRotate = true;
controls.minDistance = 1; //
// 4545
// 4545
controls.minPolarAngle = Math.PI / 4; // 45 (π/4)
controls.maxPolarAngle = (Math.PI * 3) / 4; // 45 (3π/4)
const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
loader.setDRACOLoader(dracoLoader);
let meshArr: Group[] = [];
//
const startTime = Date.now();
//
loader.setRequestHeader({
'Cache-Control': 'max-age=31536000' //
});
//
const loadingIndicator = document.createElement('div');
loadingIndicator.style.cssText = `
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: white;
font-size: 18px;
z-index: 1000;
background: rgba(0,0,0,0.7);
padding: 20px;
border-radius: 10px;
`;
loader.load(
'/models/substation/substation.gltf',
async (gltf: any) => {
console.log('文件下载完成,开始解析模型...');
//
gltf.scene.traverse((child: any) => {
if (child.isMesh) {
//
child.frustumCulled = true; //
if (child.material) {
//
child.material.side = 0; // FrontSide -
}
}
});
//
scene.add(gltf.scene);
isLoading.value = false;
loadProgress.value = 100;
//
if (div3D.value && div3D.value.contains(loadingIndicator)) {
div3D.value.removeChild(loadingIndicator);
}
const loadTime = Date.now() - startTime;
console.log(`大场景模型完全加载并显示,总耗时: ${loadTime}ms`);
},
//
(xhr) => {
const percentComplete = (xhr.loaded / xhr.total) * 100;
loadProgress.value = Math.round(percentComplete);
//
loadingIndicator.textContent = `加载中... ${loadProgress.value}%`;
console.log(`大场景加载进度: ${loadProgress.value}%`);
},
//
(error) => {
console.error('模型加载失败:', error);
loadingIndicator.textContent = '加载失败,请刷新重试';
loadingIndicator.style.color = 'red';
}
);
//
nextTick(() => {
if (div3D.value) {
loadingIndicator.textContent = '加载中... 0%';
div3D.value.appendChild(loadingIndicator);
}
});
//
const rgbELoader = new RGBELoader().load(
'/models/texture/Alex_Hart-Nature_Lab_Bones_2k.hdr',
(envMap: any) => {
envMap.mapping = EquirectangularReflectionMapping;
scene.environment = envMap;
}
);
//
const timeline1 = gsap.timeline();
const timeline2 = gsap.timeline();
//
function tranlate(position: Vector3, target: Vector3) {
timeline1.to(camera.position, {
x: position.x,
y: position.y,
z: position.z,
duration: 1,
ease: 'power2.inOut'
});
timeline2.to(controls.target, {
x: target.x,
y: target.y,
z: target.z,
duration: 1,
ease: 'power2.inOut'
});
}
//
// const axes = new AxesHelper(20);
// scene.add(axes);
//
function handleResize() {
if (!div3D.value) return;
const container = div3D.value;
const width = container.clientWidth;
const height = container.clientHeight;
//
camera.aspect = width / height;
camera.updateProjectionMatrix();
//
render.setSize(width, height);
}
function init() {
requestAnimationFrame(init);
controls.update();
render.render(scene, camera);
}
onMounted(() => {
//
handleResize();
//
init();
//
div3D.value?.appendChild(render.domElement);
//
const resizeObserver = new ResizeObserver(handleResize);
if (div3D.value) {
resizeObserver.observe(div3D.value);
}
// 便
(window as any)._resizeObserver = resizeObserver;
});
onUnmounted(() => {
//
if ((window as any)._resizeObserver) {
(window as any)._resizeObserver.disconnect();
delete (window as any)._resizeObserver;
}
});
</script>
<style scoped>
.div3D {
margin: 0;
padding: 0;
/* width: 500px;
height: 500px; */
width: 100%;
height: 100%;
position: relative;
}
.control-buttons {
position: absolute;
top: 50%;
left: 2%;
transform: translateY(-50%); /* 垂直居中 */
display: flex;
flex-direction: column;
gap: 20px;
z-index: 100;
align-items: flex-start; /* 确保所有按钮左对齐 */
}
/* 加载状态样式 */
.loading-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.8);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.loading-content {
text-align: center;
color: white;
}
.progress-bar {
width: 300px;
height: 20px;
background: #333;
border-radius: 10px;
overflow: hidden;
margin-bottom: 20px;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #409eff, #67c23a);
transition: width 0.3s ease;
}
.hint {
font-size: 14px;
color: #ccc;
margin-top: 10px;
font-style: italic;
}
</style>

@ -1,197 +0,0 @@
<template>
<div class="bar-chart-page">
<div ref="chartRef" class="bar-chart"></div>
<!-- 控制按钮 -->
<div class="controls">
<el-button @click="refreshData"></el-button>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue';
import * as echarts from 'echarts';
import type { EChartsOption } from 'echarts';
const chartRef = ref<HTMLElement>();
let chartInstance: echarts.ECharts | null = null;
let isHorizontal = false;
//
const baseData = [120, 200, 150, 80, 70, 110, 130];
const categories = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
//
const generateRandomData = () => {
return baseData.map((val) => Math.floor(val * (0.8 + Math.random() * 0.4)));
};
//
const initChart = () => {
if (!chartRef.value) return;
chartInstance = echarts.init(chartRef.value);
const option: EChartsOption = {
title: {
text: '周数据统计',
left: 'center',
top: 10
},
tooltip: {
trigger: 'axis',
formatter: '{b}: {c}'
},
xAxis: {
type: 'category',
data: categories,
axisLine: {
lineStyle: {
color: '#ddd'
}
},
axisLabel: {
color: '#666'
}
},
yAxis: {
type: 'value',
axisLine: {
lineStyle: {
color: '#ddd'
}
},
axisLabel: {
color: '#666'
},
splitLine: {
lineStyle: {
color: '#f0f0f0'
}
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
top: '60px',
containLabel: true
},
series: [
{
name: '数据',
type: 'bar',
data: baseData,
barWidth: '40%',
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: '#83bff6'
},
{
offset: 0.5,
color: '#188df0'
},
{
offset: 1,
color: '#188df0'
}
]),
borderRadius: [4, 4, 0, 0]
},
emphasis: {
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: '#2378f7'
},
{
offset: 0.7,
color: '#2378f7'
},
{
offset: 1,
color: '#83bff6'
}
])
}
}
}
]
};
chartInstance.setOption(option);
// resize
setTimeout(() => {
chartInstance?.resize();
}, 10);
};
//
const refreshData = () => {
if (!chartInstance) return;
const newData = generateRandomData();
chartInstance.setOption({
series: [
{
data: newData,
type: 'bar'
}
]
});
};
//
let resizeObserver: ResizeObserver | null = null;
const handleResize = () => {
chartInstance?.resize();
};
onMounted(() => {
initChart();
// window resize
window.addEventListener('resize', handleResize);
// 使 ResizeObserver
if (chartRef.value && typeof ResizeObserver !== 'undefined') {
resizeObserver = new ResizeObserver(() => {
handleResize();
});
resizeObserver.observe(chartRef.value.parentElement!);
}
});
onUnmounted(() => {
resizeObserver?.disconnect();
window.removeEventListener('resize', handleResize);
chartInstance?.dispose();
});
</script>
<style scoped lang="scss">
.bar-chart-page {
width: 100%;
height: 100%;
.bar-chart {
width: 100%;
height: 100%;
}
.controls {
margin-top: 20px;
text-align: center;
.el-button {
margin-right: 10px;
}
}
}
</style>

@ -1,406 +0,0 @@
<template>
<div class="echarts-gauge-page">
<div class="gauge-container">
<div ref="chartRef" class="gauge-chart"></div>
分数
<el-input-number v-model="score" :min="-10" :max="100" step="10" />
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, watch, nextTick, onUnmounted } from 'vue';
import * as echarts from 'echarts';
// src\components\mt-edit\store\types.ts
import { type DataItem } from '@/components/mt-edit/store/types';
import { debug } from 'console';
const chartRef = ref<HTMLElement>();
let score = ref(0);
let chartInstance: echarts.ECharts | null = null;
let resizeObserver: ResizeObserver | null = null;
// Rec
interface RecServiceType {
service: {
node: {
runtimes: Record<string, DataItem>;
};
};
}
interface ExtendedParentWindow extends Window {
Rec?: RecServiceType;
}
const props = defineProps({
fontSize: {
type: Number,
default: 14
},
moduleId: {
type: String,
default: ''
},
maxNum: {
type: Number,
default: 100
},
minNum: {
type: Number,
default: -10
}
});
console.log('绑定:', props.moduleId);
let node = (window.parent as ExtendedParentWindow).Rec?.service.node.runtimes[props.moduleId];
// score.value = node?.double || 0;
const gradeColors = {
D: ['#E6A23C', '#E6A23C'], //
C: ['#409EFF', '#409EFF'], //
B: ['#F56C6C', '#F56C6C'], //
A: ['#909399', '#909399'] //
};
//
function getGrade(value: number): string {
if (!node) return 'A';
//
let highNormal = node.nodeCfg.highNormal;
//
let highAct = node.nodeCfg.highAct;
//
let highAlm = node.nodeCfg.highAlm;
console.log(highNormal, highAct, highAlm);
if (value >= highAct && value < highAlm) return 'D';
if (value >= highAlm) return 'B';
return 'C'; // -50 -26: D
}
function getGradeText(value: number): string {
if (!node) return '未绑定';
let sig = node.sig;
// ISign
switch (sig) {
case 20: // N_G_HIGH_ALM
return '高限告警';
case -20: // N_G_LOW_ALM
return '低限告警';
case 10: // N_G_HIGH_ACT
return '预警/高限联动';
case -10: // N_G_LOW_ACT
return '预警/低限联动';
case 5: // N_G_ACT_OFF
return '联动关';
case 1: // N_G_ON
return '合/告警';
case 0: // N_G_OFF
return '分/复归';
case -2000: // N_G_UKNOW
return '未初始化';
default:
return '未知状态';
}
}
//
function getGradeColor(grade: string): string {
const colors = gradeColors[grade as keyof typeof gradeColors];
return colors[0];
}
//
function initChart() {
if (!chartRef.value) return;
chartInstance = echarts.init(chartRef.value);
updateChart();
resizeObserver = new ResizeObserver(() => {
chartInstance?.resize();
});
resizeObserver.observe(chartRef.value.parentElement!);
window.addEventListener('resize', () => {
chartInstance?.resize();
});
}
const colors: string[] = [
'#409EFF', //
'#42e9ff', //
'#E6A23C', //
'#F56C6C' //
];
function getIndex(index: number) {
if (index < colors.length) return index;
else return colors.length - 1;
}
function getColorArr() {
if (!node)
return [
[
1,
new echarts.graphic.LinearGradient(0, 0, 1, 0, [
{ offset: 0, color: '#909399' }, //
{ offset: 1, color: '#909399' } // rgb(133, 64, 64)
])
]
];
//
let highNormal = node.nodeCfg.highNormal;
//
let highAct = node.nodeCfg.highAct;
//
let highAlm = node.nodeCfg.highAlm;
let range = props.maxNum - props.minNum;
let colorArr = [];
let index = 0;
if (highNormal <= props.maxNum) {
colorArr.push([
(highNormal - props.minNum) / range,
new echarts.graphic.LinearGradient(0, 0, 1, 0, [
{ offset: 0, color: '#409EFF' }, //
{ offset: 1, color: '#409EFF' } //
])
]);
}
if (highAct <= props.maxNum) {
colorArr.push([
(highAct - props.minNum) / range,
new echarts.graphic.LinearGradient(0, 0, 1, 0, [
{ offset: 0, color: '#42e9ff' }, //
{ offset: 1, color: '#42e9ff' } //
])
]);
}
if (highAlm < props.maxNum) {
colorArr.push([
(highAlm - props.minNum) / range,
new echarts.graphic.LinearGradient(0, 0, 1, 0, [
{ offset: 0, color: '#E6A23C' }, //
{ offset: 1, color: '#E6A23C' } //
])
]);
colorArr.push([
1,
new echarts.graphic.LinearGradient(0, 0, 1, 0, [
{ offset: 0, color: '#F56C6C' }, //
{ offset: 1, color: '#F56C6C' } //
])
]);
} else if (highAlm == props.maxNum) {
colorArr.push([
1,
new echarts.graphic.LinearGradient(0, 0, 1, 0, [
{ offset: 0, color: '#E6A23C' }, //
{ offset: 1, color: '#E6A23C' } //
])
]);
}
if (colorArr.length == 0) {
colorArr.push([
1,
new echarts.graphic.LinearGradient(0, 0, 1, 0, [
{ offset: 0, color: colors[colors.length - 1] },
{ offset: 1, color: colors[colors.length - 1] }
])
]);
}
return colorArr;
}
function updateChart() {
if (!chartInstance) return;
const currentValue = score.value;
const currentGrade = getGrade(currentValue); // 使
const currentText = getGradeText(currentValue);
const gradeColor = getGradeColor(currentGrade);
const option = {
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b}: {c}'
},
//
series: [
{
name: 'Grade Rating',
type: 'gauge',
startAngle: 180, //
endAngle: 0, //
center: ['50%', '75%'], //
radius: '90%', //
min: props.minNum,
max: props.maxNum,
splitNumber: (props.maxNum - props.minNum) / 10, //props.maxNum - props.minNum //
axisLine: {
lineStyle: {
width: 6,
color: getColorArr()
// [
// [
// 0.818,
// new echarts.graphic.LinearGradient(0, 0, 1, 0, [
// { offset: 0, color: '#409EFF' }, //
// { offset: 1, color: '#67C23A' } // 绿
// ])
// ],
// [
// 0.909,
// new echarts.graphic.LinearGradient(0, 0, 1, 0, [
// { offset: 0, color: '#67C23A' }, // 绿
// { offset: 1, color: '#F56C6C' } //
// ])
// ],
// [
// 1,
// new echarts.graphic.LinearGradient(0, 0, 1, 0, [
// { offset: 0, color: '#F56C6C' }, //
// { offset: 1, color: '#854040' } // rgb(133, 64, 64)
// ])
// ]
// ]
}
},
pointer: {
icon: 'path://M12.8,0.7l12,40.1H0.7L12.8,0.7z',
length: '12%',
width: '8%',
offsetCenter: [0, '-55%'],
itemStyle: {
color: gradeColor
}
},
//
axisTick: {
show: true,
splitNumber: 4,
length: '5%',
lineStyle: {
color: 'auto',
width: 1
}
},
//
splitLine: {
show: true,
length: '8%',
lineStyle: {
color: 'auto',
width: 2
}
},
// axisLabel: {
// show: false,
// color: '#E5EAF3',
// fontSize: 13,
// distance: -45,
// rotate: 'tangential',
// formatter: function (value: number) {
// if(!node)
// return '';
// //
// let highNormal = node.nodeCfg.highNormal;
// //
// let highAct = node.nodeCfg.highAct;
// //
// let highAlm = node.nodeCfg.highAlm;
// if (value === highNormal) {
// return highNormal+'\n';
// } else if (value === highAct) {
// return highAct+'\n';
// } else if (value === highAlm) {
// return highAlm+'\n';
// }
// //
// return '';
// }
// },
//
title: {
offsetCenter: [0, '-10%'],
fontSize: props.fontSize,
color: gradeColor
},
//
detail: {
fontSize: props.fontSize + 5,
offsetCenter: [0, '-35%'],
valueAnimation: true,
formatter: function (value: number) {
return Math.round(value) + '';
},
color: gradeColor //
},
data: [
{
value: currentValue, // -50 50 0 1
name: currentText
}
]
}
]
};
chartInstance.setOption(option);
}
//
watch(
[score, () => props.fontSize, () => props.moduleId], // 使 props
() => {
node = (window.parent as ExtendedParentWindow).Rec?.service.node.runtimes[props.moduleId];
// score.value = node?.double || 0;
nextTick(() => {
updateChart();
});
}
);
nextTick(() => {
initChart();
});
//
onUnmounted(() => {
if (chartInstance) {
chartInstance.dispose();
}
if (resizeObserver) {
resizeObserver.disconnect();
}
});
</script>
<style scoped>
.echarts-gauge-page {
width: 100%;
height: 100%;
display: flex;
/* justify-content: center;
align-items: center; */
/* position: relative; */
}
.gauge-container {
width: 100%;
height: 100%;
/* position: relative; */
}
.gauge-chart {
width: 100%;
height: 100%;
}
</style>

@ -1,198 +0,0 @@
<template>
<div class="line-chart-page">
<div ref="chartRef" class="line-chart"></div>
<!-- 控制按钮 -->
<div class="controls">
<el-button @click="refreshData"></el-button>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue';
import * as echarts from 'echarts';
import type { EChartsOption } from 'echarts';
const chartRef = ref<HTMLElement>();
let chartInstance: echarts.ECharts | null = null;
let isSmooth = false;
//
const baseData = [160, 230, 224, 218, 135, 147, 260];
const categories = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
//
const generateRandomData = () => {
return baseData.map((val) => Math.floor(val * (0.8 + Math.random() * 0.4)));
};
//
const initChart = () => {
if (!chartRef.value) return;
chartInstance = echarts.init(chartRef.value);
const option: EChartsOption = {
title: {
text: '周数据统计',
left: 'center',
top: 10
},
tooltip: {
trigger: 'axis',
formatter: '{b}: {c}'
},
xAxis: {
type: 'category',
data: categories,
boundaryGap: false,
axisLine: {
lineStyle: {
color: '#ddd'
}
},
axisLabel: {
color: '#666'
}
},
yAxis: {
type: 'value',
axisLine: {
lineStyle: {
color: '#ddd'
}
},
axisLabel: {
color: '#666'
},
splitLine: {
lineStyle: {
color: '#f0f0f0'
}
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
top: '60px',
containLabel: true
},
series: [
{
name: '数据',
type: 'line',
data: baseData,
smooth: true, //线
symbol: 'circle',
symbolSize: 8,
itemStyle: {
color: '#409EFF',
borderColor: '#fff',
borderWidth: 2
},
lineStyle: {
width: 3,
color: '#409EFF'
},
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: 'rgba(64, 158, 255, 0.3)'
},
{
offset: 1,
color: 'rgba(64, 158, 255, 0.05)'
}
])
},
emphasis: {
focus: 'series'
}
}
]
};
chartInstance.setOption(option);
// resize
setTimeout(() => {
chartInstance?.resize();
}, 10);
};
//
const refreshData = () => {
if (!chartInstance) return;
const newData = generateRandomData();
chartInstance.setOption({
series: [
{
data: newData,
type: 'line',
smooth: true
}
]
});
};
// - 使 ResizeObserver
let resizeObserver: ResizeObserver | null = null;
const handleResize = () => {
chartInstance?.resize();
};
onMounted(() => {
initChart();
// 1: window resize
window.addEventListener('resize', handleResize);
// 2: 使 ResizeObserver
if (chartRef.value && typeof ResizeObserver !== 'undefined') {
resizeObserver = new ResizeObserver(() => {
handleResize();
});
resizeObserver.observe(chartRef.value.parentElement!);
}
});
onUnmounted(() => {
resizeObserver?.disconnect();
window.removeEventListener('resize', handleResize);
chartInstance?.dispose();
});
</script>
<style scoped lang="scss">
.line-chart-page {
width: 100%;
height: 100%;
h2 {
text-align: center;
color: #333;
margin-bottom: 20px;
}
.line-chart {
width: 100%;
height: 100%;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}
.controls {
margin-top: 20px;
text-align: center;
.el-button {
margin-right: 10px;
}
}
}
</style>

@ -1,153 +0,0 @@
<template>
<div class="pie-chart-page">
<div ref="chartRef" class="pie-chart"></div>
<!-- 控制按钮 -->
<div class="controls">
<el-button @click="refreshData"></el-button>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue';
import * as echarts from 'echarts';
import type { EChartsOption } from 'echarts';
const chartRef = ref<HTMLElement>();
let chartInstance: echarts.ECharts | null = null;
//
const baseData = [
{ value: 1048, name: 'Search Engine' },
{ value: 735, name: 'Direct' },
{ value: 580, name: 'Email' },
{ value: 484, name: 'Union Ads' },
{ value: 300, name: 'Video Ads' }
];
//
const generateRandomData = () => {
return baseData.map((item) => ({
name: item.name,
value: Math.floor(item.value * (0.8 + Math.random() * 0.4))
}));
};
//
const initChart = () => {
if (!chartRef.value) return;
chartInstance = echarts.init(chartRef.value);
const option: EChartsOption = {
//
title: {
text: '饼图',
// subtext: '',
left: 'center'
},
//
tooltip: {
trigger: 'item',
formatter: '{b}: {c} ({d}%)'
},
//
legend: {
orient: 'vertical', // vertical horizontal
left: 'left', // left right center
bottom: '0' // bottom top middle
},
//
series: [
{
name: 'Access From',
type: 'pie',
radius: '50%',
data: baseData,
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
},
label: {
color: '#666'
},
labelLine: {
lineStyle: {
color: '#999'
}
}
}
]
};
chartInstance.setOption(option);
// resize
setTimeout(() => {
chartInstance?.resize();
}, 10);
};
//
const refreshData = () => {
if (!chartInstance) return;
const newData = generateRandomData();
chartInstance.setOption({
series: [
{
data: newData,
type: 'pie'
}
]
});
};
//
let resizeObserver: ResizeObserver | null = null;
const handleResize = () => {
chartInstance?.resize();
};
onMounted(() => {
initChart();
// window resize
window.addEventListener('resize', handleResize);
// 使 ResizeObserver
if (chartRef.value && typeof ResizeObserver !== 'undefined') {
resizeObserver = new ResizeObserver(() => {
handleResize();
});
resizeObserver.observe(chartRef.value.parentElement!);
}
});
onUnmounted(() => {
resizeObserver?.disconnect();
window.removeEventListener('resize', handleResize);
chartInstance?.dispose();
});
</script>
<style scoped lang="scss">
.pie-chart-page {
width: 100%;
height: 100%;
.pie-chart {
width: 100%;
height: 100%;
}
.controls {
text-align: center;
}
}
</style>

@ -1,82 +0,0 @@
<template>
<div class="main-img">
<div v-if="!machine" class="no-img">
<el-icon :size="200" class="no-img-icon">
<Picture />
</el-icon>
</div>
<!-- style="max-width: 300px" -->
<div class="img-container" v-if="machine">
<!-- <img v-if="imgUrl" draggable="false" class="display-img" :src="imgUrl" /> -->
<img v-if="machine" draggable="false" class="display-img" :src="machine" />
<!-- 等比缩放 -->
<!-- <el-image :src="imgUrl" draggable="false" :preview-src-list="[]" /> -->
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue';
import { useImgStore } from '@/components/mt-edit/store/imgStore';
import { Picture } from '@element-plus/icons-vue';
import machine from '/public/imgs/machine.png';
// const displayImageUrl = ref<string | null>(null);
const displayImageUrl = ref<string>();
const imgStore = useImgStore();
const props = defineProps({
definitionItemJson: {
type: Object,
default: () => ({})
},
moduleId: {
type: String,
default: ''
}
});
onUnmounted(() => {
console.log('组件卸载');
});
//
onMounted(() => {
});
</script>
<style scoped>
.main-img {
width: 100%;
height: 100%;
}
.no-img {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
}
.no-img-icon {
width: 100%;
height: 100%;
color: #409efc;
}
.img-container {
width: 100%;
height: 100%;
}
.display-img {
width: 100%;
height: 100%;
/* max-width: 300px; */
}
</style>

@ -1,303 +0,0 @@
<template>
<!-- <button @click="console.log('data:', nodeByModelsStore.nodeMap)">点击</button> -->
<el-row v-if="location === 'top'">
<el-col :span="24" class="flex-center">
<el-tag type="primary" size="small" style="opacity: 0.9">
<el-text type="primary" style="max-width: 65px" size="small" truncated>
{{ modeName }}
</el-text>
</el-tag>
</el-col>
</el-row>
<el-row justify="center">
<el-col :span="12" class="flex-center" v-if="location === 'left'">
<el-tag type="primary" size="small" style="opacity: 0.9">
<el-text type="primary" style="max-width: 65px" size="small" truncated>
{{ modeName }}
</el-text>
</el-tag>
</el-col>
<el-col :span="12" class="flex-center">
<div style="display: flex; gap: 10px; align-items: center">
<el-popover
:width="300"
:disabled="false"
popper-style="box-shadow: rgb(14 18 22 / 35%) 0px 10px 38px -10px, rgb(14 18 22 / 20%) 0px 10px 20px -15px; padding: 20px;"
>
<!-- :style="{ color: isBool ? 'rgb(146, 252, 40)' : 'rgb(252, 40, 40)' }" -->
<!-- rgb(196, 86, 86) -->
<template #reference>
<el-icon
:class="['iconStyle', isAalarm ? 'blink-animation' : '']"
:size="18"
:style="{ color: isBool ? 'rgb(166, 169, 173)' : 'rgb(247, 137, 137)' }"
>
<WarningFilled />
</el-icon>
</template>
<template #default>
<div class="demo-rich-conent" style="display: flex; gap: 20px; flex-direction: column">
<el-icon
class="iconStyle"
:size="60"
:style="{ color: isBool ? 'rgb(146, 252, 40)' : 'rgb(252, 40, 40)' }"
>
<WarningFilled />
</el-icon>
<el-row :gutter="6">
<el-col :span="12" :xs="24" :sm="12" :md="6" class="elColInfo">
<el-statistic title="绑定ID" :value="null">
<template #suffix>
<el-tag size="default" type="primary">{{ moduleId }}</el-tag>
</template>
</el-statistic>
</el-col>
<el-col :span="12" :xs="24" :sm="12" :md="12" class="elColInfo">
<el-statistic title="节点名称" :value="null">
<template #suffix>
<el-tag size="default" type="success">{{ modeName }}</el-tag>
</template>
</el-statistic>
</el-col>
<el-col :span="12" :xs="24" :sm="12" :md="6" class="elColInfo">
<el-statistic title="类型" :value="null">
<template #suffix>
<el-tag size="default" type="primary">遥信</el-tag>
</template>
</el-statistic>
</el-col>
</el-row>
<el-row :gutter="6">
<el-col :span="12" :xs="24" :sm="12" :md="6" class="elColInfo">
<el-statistic title="开关情况" :value="null">
<template #suffix>
<el-tag size="default" type="primary">{{ isBool }}</el-tag>
</template>
</el-statistic>
</el-col>
<el-col :span="12" :xs="24" :sm="12" :md="12" class="elColInfo">
<el-statistic title="告警" :value="null">
<template #suffix>
<el-tag size="default" type="primary">{{ isAalarm }}</el-tag>
</template>
</el-statistic>
</el-col>
<el-col :span="12" :xs="24" :sm="12" :md="6" class="elColInfo">
<el-statistic title="是否可告警" :value="null">
<template #suffix>
<el-tag size="default" type="primary">{{ isAlarmable }}</el-tag>
</template>
</el-statistic>
</el-col>
</el-row>
</div>
</template>
</el-popover>
</div>
</el-col>
<el-col :span="12" class="flex-center" v-if="location === 'right'">
<el-tag type="primary" size="small" style="opacity: 0.9">
<el-text type="primary" style="max-width: 65px" size="small" truncated>
{{ modeName }}
</el-text>
</el-tag>
</el-col>
</el-row>
<el-row v-if="location === 'bottom'">
<el-col :span="24" class="flex-center">
<el-tag type="primary" size="small" style="opacity: 0.9">
<el-text type="primary" style="max-width: 65px" size="small" truncated>
{{ modeName }}
</el-text>
</el-tag>
</el-col>
</el-row>
</template>
<script lang="ts" setup>
import { ref, onUnmounted, onMounted, watch } from 'vue';
import { WarningFilled } from '@element-plus/icons-vue';
import { useNodeByModelsStore } from '../mt-edit/store/nodeByModels';
import emitter from '../../utils/emitter';
const nodeByModelsStore = useNodeByModelsStore();
onMounted(() => {
loadingModuleById();
saveodeByModels();
});
onUnmounted(() => {
deleteByModels();
console.log('组件卸载');
emitter.off(props.definitionItemJson.id);
});
// const props = defineProps(['definitionItemJson']);
const props = defineProps({
moduleType: {
type: String,
default: '--'
},
moduleId: {
type: String,
default: '--'
},
definitionItemJson: {
type: Object,
default: () => ({})
},
location: {
type: String,
default: 'bottom'
}
});
let modeName = ref<string>();
//
let isBool = ref<boolean>(false);
//
let isAalarm = ref<boolean>();
//
let isAlarmable = ref<boolean>();
function getModuleById(moduleId: string) {
const globalData = (window as any).globalData;
if (!globalData || moduleId == undefined || moduleId == '' || props.moduleId == '--') {
console.warn('globalData 未初始化');
return null;
}
// 访
if (globalData instanceof Map) {
// Map
return globalData.get(moduleId);
} else {
//
return globalData[moduleId];
}
}
function loadingModuleById() {
let module = getModuleById(props.moduleId);
if (props.moduleId !== '' && props.moduleId !== undefined && props.moduleId !== '--' && module) {
if (module) {
modeName.value = module.node.name;
isBool.value = module.double == 1;
isAalarm.value = module.alarm;
isAlarmable.value = module.alarmable;
}
}
}
// function loadingModuleById() {
// const globalData = (window as any).globalData as Map<string, any>;
// if (
// props.moduleId !== '' &&
// props.moduleId !== undefined &&
// props.moduleId !== '--' &&
// globalData?.has(props.moduleId)
// ) {
// let module = globalData.get(props.moduleId);
// if (module) {
// modeName.value = module.node.name;
// isBool.value = module.double == 1;
// isAalarm.value = module.alarm;
// isAlarmable.value = module.alarmable;
// }
// }
// }
//
emitter.on(props.definitionItemJson.id, (value) => {
console.log('触发事件', value);
loadingModuleById();
});
watch(
() => props.moduleId,
(newVal, oldVal) => {
loadingModuleById();
nodeByModelsStore.change(newVal, oldVal, props.definitionItemJson.id);
}
);
function deleteByModels() {
if (props.moduleId == '' || props.moduleId == undefined || props.moduleId == '--') return;
nodeByModelsStore.delete(props.moduleId, props.definitionItemJson.id);
}
function saveodeByModels() {
if (props.moduleId == '' || props.moduleId == undefined || props.moduleId == '--') return;
nodeByModelsStore.saveOrUpdate(props.moduleId, props.definitionItemJson.id);
}
</script>
<style scoped>
/* 设置固定宽度和文本省略 */
.ellipsis-tag {
width: 65px; /* 设置固定宽度 */
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: inline-block;
align-items: center; /* 水平居中 */
}
.flex-center {
display: flex;
flex-direction: column; /* 垂直排列 */
justify-content: center; /* 垂直居中 */
align-items: center; /* 水平居中 */
}
.iconStyle {
size: 30;
transition: opacity 0.5s ease-in-out; /* 添加平滑过渡 */
/* color: v-bind('isBool ? "rgb(146, 252, 40)" : "rgb(252, 40, 40)"'); */
/* color: rgb(146, 252, 40); */
/* rgb(252, 40, 40); */
}
/* 闪烁动画 */
/* .blink-animation {
animation: blink-animation 1s infinite;
} */
/* 平滑闪烁动画 */
.blink-animation {
animation: smooth-blink 1s infinite ease-in-out;
}
@keyframes smooth-blink {
0%,
100% {
opacity: 1;
}
30% {
opacity: 0.3;
}
}
.el-statistic {
/* 控制文字大小 */
--el-statistic-content-font-size: 16px;
}
.elColInfo {
text-align: center;
}
</style>

@ -22,6 +22,14 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
root, root,
plugins: [ plugins: [
vue(), vue(),
// 只在库模式下生成声明文件
...(mode === 'lib' || mode === 'npm'
? [
dts({
tsconfigPath: 'tsconfig.app.json'
})
]
: []),
vueJsx(), vueJsx(),
vueDevTools(), vueDevTools(),
// 使用 svg 图标 // 使用 svg 图标

Loading…
Cancel
Save