Browse Source

首页提交

luogang 9 months ago
parent
commit
ec31ebef64

+ 1 - 0
src/assets/images/home/banzu.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1730278436497" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6800" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32"><path d="M512 512m-512 0a512 512 0 1 0 1024 0 512 512 0 1 0-1024 0Z" fill="#1E9FF2" p-id="6801" data-spm-anchor-id="a313x.search_index.0.i18.7dfd3a81Klywrj" class="selected"></path><path d="M665.6 742.4H358.4c-28.16 0-51.2-23.04-51.2-51.2V332.8c0-28.16 23.04-51.2 51.2-51.2h307.2c28.16 0 51.2 23.04 51.2 51.2v358.4c0 28.16-23.04 51.2-51.2 51.2zM371.2 512h115.2c7.68 0 12.8-5.12 12.8-12.8s-5.12-12.8-12.8-12.8h-115.2c-7.68 0-12.8 5.12-12.8 12.8s5.12 12.8 12.8 12.8z m192-115.2H371.2c-7.68 0-12.8 5.12-12.8 12.8s5.12 12.8 12.8 12.8h192c7.68 0 12.8-5.12 12.8-12.8s-5.12-12.8-12.8-12.8z" fill="#FFFFFF" p-id="6802"></path></svg>

+ 1 - 0
src/assets/images/home/person.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1730278436497" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6800" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32"><path d="M512 512m-512 0a512 512 0 1 0 1024 0 512 512 0 1 0-1024 0Z" fill="#2CD9C5" p-id="6801" data-spm-anchor-id="a313x.search_index.0.i18.7dfd3a81Klywrj" class="selected"></path><path d="M665.6 742.4H358.4c-28.16 0-51.2-23.04-51.2-51.2V332.8c0-28.16 23.04-51.2 51.2-51.2h307.2c28.16 0 51.2 23.04 51.2 51.2v358.4c0 28.16-23.04 51.2-51.2 51.2zM371.2 512h115.2c7.68 0 12.8-5.12 12.8-12.8s-5.12-12.8-12.8-12.8h-115.2c-7.68 0-12.8 5.12-12.8 12.8s5.12 12.8 12.8 12.8z m192-115.2H371.2c-7.68 0-12.8 5.12-12.8 12.8s5.12 12.8 12.8 12.8h192c7.68 0 12.8-5.12 12.8-12.8s-5.12-12.8-12.8-12.8z" fill="#FFFFFF" p-id="6802"></path></svg>

+ 156 - 156
src/router/index.ts

@@ -26,173 +26,173 @@ import Layout from '@/layout/index.vue';
 
 // 公共路由
 export const constantRoutes: RouteRecordRaw[] = [
-  {
-    path: '/redirect',
-    component: Layout,
-    hidden: true,
-    children: [
-      {
-        path: '/redirect/:path(.*)',
-        component: () => import('@/views/redirect/index.vue')
-      }
-    ]
-  },
-  {
-    path: '/social-callback',
-    hidden: true,
-    component: () => import('@/layout/components/SocialCallback/index.vue')
-  },
-  {
-    path: '/login',
-    component: () => import('@/views/login.vue'),
-    hidden: true
-  },
-  {
-    path: '/register',
-    component: () => import('@/views/register.vue'),
-    hidden: true
-  },
-  {
-    path: '/:pathMatch(.*)*',
-    component: () => import('@/views/error/404.vue'),
-    hidden: true
-  },
-  {
-    path: '/401',
-    component: () => import('@/views/error/401.vue'),
-    hidden: true
-  },
-  {
-    path: '',
-    component: Layout,
-    redirect: '/index',
-    children: [
-      {
-        path: '/index',
-        component: () => import('@/views/index.vue'),
-        name: 'Index',
-        meta: { title: '首页', icon: 'dashboard', affix: true }
-      }
-    ]
-  },
-  {
-    path: '/user',
-    component: Layout,
-    hidden: true,
-    redirect: 'noredirect',
-    children: [
-      {
-        path: 'profile',
-        component: () => import('@/views/system/user/profile/index.vue'),
-        name: 'Profile',
-        meta: { title: '个人中心', icon: 'user' }
-      }
-    ]
-  }
+    {
+        path: '/redirect',
+        component: Layout,
+        hidden: true,
+        children: [
+            {
+                path: '/redirect/:path(.*)',
+                component: () => import('@/views/redirect/index.vue')
+            }
+        ]
+    },
+    {
+        path: '/social-callback',
+        hidden: true,
+        component: () => import('@/layout/components/SocialCallback/index.vue')
+    },
+    {
+        path: '/login',
+        component: () => import('@/views/login.vue'),
+        hidden: true
+    },
+    {
+        path: '/register',
+        component: () => import('@/views/register.vue'),
+        hidden: true
+    },
+    {
+        path: '/:pathMatch(.*)*',
+        component: () => import('@/views/error/404.vue'),
+        hidden: true
+    },
+    {
+        path: '/401',
+        component: () => import('@/views/error/401.vue'),
+        hidden: true
+    },
+    {
+        path: '',
+        component: Layout,
+        redirect: '/index',
+        children: [
+            {
+                path: '/index',
+                component: () => import('@/views/dashboard/index.vue'),
+                name: 'Index',
+                meta: { title: '首页', icon: 'dashboard', affix: true }
+            }
+        ]
+    },
+    {
+        path: '/user',
+        component: Layout,
+        hidden: true,
+        redirect: 'noredirect',
+        children: [
+            {
+                path: 'profile',
+                component: () => import('@/views/system/user/profile/index.vue'),
+                name: 'Profile',
+                meta: { title: '个人中心', icon: 'user' }
+            }
+        ]
+    }
 ];
 
 // 动态路由,基于用户权限动态去加载
 export const dynamicRoutes: RouteRecordRaw[] = [
-  {
-    path: '/system/user-auth',
-    component: Layout,
-    hidden: true,
-    permissions: ['system:user:edit'],
-    children: [
-      {
-        path: 'role/:userId(\\d+)',
-        component: () => import('@/views/system/user/authRole.vue'),
-        name: 'AuthRole',
-        meta: { title: '分配角色', activeMenu: '/system/user', icon: '' }
-      }
-    ]
-  },
-  {
-    path: '/system/role-auth',
-    component: Layout,
-    hidden: true,
-    permissions: ['system:role:edit'],
-    children: [
-      {
-        path: 'user/:roleId(\\d+)',
-        component: () => import('@/views/system/role/authUser.vue'),
-        name: 'AuthUser',
-        meta: { title: '分配用户', activeMenu: '/system/role', icon: '' }
-      }
-    ]
-  },
-  {
-    path: '/system/dict-data',
-    component: Layout,
-    hidden: true,
-    permissions: ['system:dict:list'],
-    children: [
-      {
-        path: 'index/:dictId(\\d+)',
-        component: () => import('@/views/system/dict/data.vue'),
-        name: 'Data',
-        meta: { title: '字典数据', activeMenu: '/system/dict', icon: '' }
-      }
-    ]
-  },
-  {
-    path: '/system/oss-config',
-    component: Layout,
-    hidden: true,
-    permissions: ['system:ossConfig:list'],
-    children: [
-      {
-        path: 'index',
-        component: () => import('@/views/system/oss/config.vue'),
-        name: 'OssConfig',
-        meta: { title: '配置管理', activeMenu: '/system/oss', icon: '' }
-      }
-    ]
-  },
-  {
-    path: '/tool/gen-edit',
-    component: Layout,
-    hidden: true,
-    permissions: ['tool:gen:edit'],
-    children: [
-      {
-        path: 'index/:tableId(\\d+)',
-        component: () => import('@/views/tool/gen/editTable.vue'),
-        name: 'GenEdit',
-        meta: { title: '修改生成配置', activeMenu: '/tool/gen', icon: '', noCache: true }
-      }
-    ]
-  },
-  {
-    path: '/workflow/leaveEdit',
-    component: Layout,
-    hidden: true,
-    permissions: ['workflow:leave:edit'],
-    children: [
-      {
-        path: 'index',
-        component: () => import('@/views/workflow/leave/leaveEdit.vue'),
-        name: 'leaveEdit',
-        meta: { title: '请假申请', activeMenu: '/workflow/leave', noCache: true }
-      }
-    ]
-  }
+    {
+        path: '/system/user-auth',
+        component: Layout,
+        hidden: true,
+        permissions: ['system:user:edit'],
+        children: [
+            {
+                path: 'role/:userId(\\d+)',
+                component: () => import('@/views/system/user/authRole.vue'),
+                name: 'AuthRole',
+                meta: { title: '分配角色', activeMenu: '/system/user', icon: '' }
+            }
+        ]
+    },
+    {
+        path: '/system/role-auth',
+        component: Layout,
+        hidden: true,
+        permissions: ['system:role:edit'],
+        children: [
+            {
+                path: 'user/:roleId(\\d+)',
+                component: () => import('@/views/system/role/authUser.vue'),
+                name: 'AuthUser',
+                meta: { title: '分配用户', activeMenu: '/system/role', icon: '' }
+            }
+        ]
+    },
+    {
+        path: '/system/dict-data',
+        component: Layout,
+        hidden: true,
+        permissions: ['system:dict:list'],
+        children: [
+            {
+                path: 'index/:dictId(\\d+)',
+                component: () => import('@/views/system/dict/data.vue'),
+                name: 'Data',
+                meta: { title: '字典数据', activeMenu: '/system/dict', icon: '' }
+            }
+        ]
+    },
+    {
+        path: '/system/oss-config',
+        component: Layout,
+        hidden: true,
+        permissions: ['system:ossConfig:list'],
+        children: [
+            {
+                path: 'index',
+                component: () => import('@/views/system/oss/config.vue'),
+                name: 'OssConfig',
+                meta: { title: '配置管理', activeMenu: '/system/oss', icon: '' }
+            }
+        ]
+    },
+    {
+        path: '/tool/gen-edit',
+        component: Layout,
+        hidden: true,
+        permissions: ['tool:gen:edit'],
+        children: [
+            {
+                path: 'index/:tableId(\\d+)',
+                component: () => import('@/views/tool/gen/editTable.vue'),
+                name: 'GenEdit',
+                meta: { title: '修改生成配置', activeMenu: '/tool/gen', icon: '', noCache: true }
+            }
+        ]
+    },
+    {
+        path: '/workflow/leaveEdit',
+        component: Layout,
+        hidden: true,
+        permissions: ['workflow:leave:edit'],
+        children: [
+            {
+                path: 'index',
+                component: () => import('@/views/workflow/leave/leaveEdit.vue'),
+                name: 'leaveEdit',
+                meta: { title: '请假申请', activeMenu: '/workflow/leave', noCache: true }
+            }
+        ]
+    }
 ];
 
 /**
  * 创建路由
  */
 const router = createRouter({
-  history: createWebHistory(import.meta.env.VITE_APP_CONTEXT_PATH),
-  routes: constantRoutes,
-  // 刷新时,滚动条位置还原
-  scrollBehavior(to, from, savedPosition) {
-    if (savedPosition) {
-      return savedPosition;
-    } else {
-      return { top: 0 };
+    history: createWebHistory(import.meta.env.VITE_APP_CONTEXT_PATH),
+    routes: constantRoutes,
+    // 刷新时,滚动条位置还原
+    scrollBehavior(to, from, savedPosition) {
+        if (savedPosition) {
+            return savedPosition;
+        } else {
+            return { top: 0 };
+        }
     }
-  }
 });
 
 export default router;

+ 65 - 0
src/views/dashboard/components/CustomTabs.vue

@@ -0,0 +1,65 @@
+<template>
+    <div class="tabs">
+        <div v-for="item in tabs" :key="item.value" :class="{ 'tab-selected': activeTab === item.value }" @click="handleTabClick(item)">
+            {{ item.name }}
+        </div>
+    </div>
+</template>
+
+<script lang="ts" setup>
+interface TabItem {
+    value: string | number;
+    name: string;
+}
+
+const props = defineProps<{
+    tabs: TabItem[];
+    active: string | number;
+}>();
+
+const emit = defineEmits<{
+    (event: 'update:active', value: string | number): void;
+    (event: 'tab-click', item: TabItem): void;
+}>();
+
+const activeTab = ref(props.active);
+
+// 监听外部传入的active变化
+watch(
+    () => props.active,
+    (newVal) => {
+        activeTab.value = newVal;
+    }
+);
+
+function handleTabClick(item: TabItem) {
+    activeTab.value = item.value;
+    emit('update:active', item.value);
+    emit('tab-click', item);
+}
+</script>
+
+<style lang="scss" scoped>
+.tabs {
+    display: flex;
+    border-collapse: collapse;
+    > div {
+        padding: 2px 8px;
+        color: #333;
+        cursor: pointer;
+        border: 1px solid #dedede;
+        font-size: 11px;
+        &:first-of-type {
+            border-radius: 4px 0 0 4px;
+        }
+        &:last-of-type {
+            border-radius: 0 4px 4px 0;
+        }
+    }
+
+    .tab-selected {
+        color: #48a1ff;
+        border-color: #48a1ff;
+    }
+}
+</style>

+ 492 - 0
src/views/dashboard/dashboard.vue

@@ -0,0 +1,492 @@
+<template>
+    <div class="dashbord">
+        <div class="chart-group">
+            <el-card style="margin-right: 2px">
+                <SubTitle title="设备运行" />
+                <div class="chart-content">
+                    <img class="positionImg" src="@/assets/images/position.png" alt="" />
+                </div>
+            </el-card>
+            <div class="chart-two" style="padding-left: 4px">
+                <el-card>
+                    <SubTitle title="设备总览" />
+                    <div class="chart-content">
+                        <BaseChart width="100%" height="100%" :option="equipOption" />
+                    </div>
+                </el-card>
+                <el-card>
+                    <SubTitle title="告警处理" />
+                    <div class="chart-content">
+                        <el-table class="warningTable" :data="warningData" border style="margin-top: 10px" max-height="300">
+                            <el-table-column prop="deviceNo" align="center" label="设备编号" />
+                            <el-table-column prop="warningDate" align="center" width="100" label="告警时间" />
+                            <el-table-column prop="warningType" align="center" label="告警类型" />
+                            <el-table-column label="操作" align="center" fixed="right" width="70">
+                                <template #default="scope">
+                                    <div class="warnBtns">
+                                        <el-button link size="small" type="primary" text>详情</el-button>
+                                        <el-button link size="small" type="danger" text>处理</el-button>
+                                    </div>
+                                </template>
+                            </el-table-column>
+                        </el-table>
+                    </div>
+                </el-card>
+            </div>
+        </div>
+        <div class="chart-group">
+            <div class="chart-two">
+                <el-card>
+                    <SubTitle title="巡检人员" />
+                    <div class="chart-content">
+                        <div class="check-summary">
+                            <div>
+                                <img src="@/assets/images/home/banzu.svg" alt="" />
+                                <div class="check-name">
+                                    <div>8</div>
+                                    <div>班组</div>
+                                </div>
+                            </div>
+                            <div>
+                                <img src="@/assets/images/home/person.svg" alt="" />
+                                <div class="check-name">
+                                    <div>40</div>
+                                    <div>巡检人员</div>
+                                </div>
+                            </div>
+                        </div>
+                        <div class="check-rank">
+                            <div>当月巡检排名</div>
+                            <CustomTabs v-model:active="checkActive" :tabs="checkTabs" />
+                        </div>
+                        <el-table :data="tableData" style="width: 100%; margin-top: 10px" max-height="200">
+                            <el-table-column prop="name" label="巡检员">
+                                <template #default="scope">
+                                    {{ `${scope.$index + 1}、${scope.row.name}` }}
+                                </template>
+                            </el-table-column>
+
+                            <el-table-column prop="group" label="所在班组" />
+                            <el-table-column prop="num" label="数量" />
+                        </el-table>
+                    </div>
+                </el-card>
+                <el-card>
+                    <SubTitle title="售后工程师" />
+                    <div class="chart-content">
+                        <div class="check-summary">
+                            <div>
+                                <img src="@/assets/images/home/banzu.svg" alt="" />
+                                <div class="check-name">
+                                    <div>16</div>
+                                    <div>供应商</div>
+                                </div>
+                            </div>
+                            <div>
+                                <img src="@/assets/images/home/person.svg" alt="" />
+                                <div class="check-name">
+                                    <div>62</div>
+                                    <div>售后工程师</div>
+                                </div>
+                            </div>
+                        </div>
+                        <div class="check-rank">
+                            <div>累计故障处理</div>
+                            <CustomTabs v-model:active="serviceActive" :tabs="serviceTabs" />
+                        </div>
+                        <el-table :data="serviceData" style="width: 100%; margin-top: 10px" max-height="200">
+                            <el-table-column prop="name" label="售后工程师">
+                                <template #default="scope">
+                                    {{ `${scope.$index + 1}、${scope.row.name}` }}
+                                </template>
+                            </el-table-column>
+
+                            <el-table-column prop="group" label="所属供应商" />
+                            <el-table-column prop="num" label="工单完成率" />
+                        </el-table>
+                    </div>
+                </el-card>
+            </div>
+            <el-card>
+                <SubTitle title="运营数据" />
+                <div class="chart-content">
+                    <BaseChart width="100%" height="50%" :option="checkOption" />
+                    <BaseChart width="100%" height="50%" :option="checkOption" />
+                </div>
+            </el-card>
+        </div>
+    </div>
+</template>
+<script setup lang="ts">
+import CustomTabs from './components/CustomTabs.vue';
+const checkTabs = [
+    {
+        name: '按巡检设备数',
+        value: '1'
+    },
+    {
+        name: '按发现故障数',
+        value: '2'
+    }
+];
+const serviceTabs = [
+    {
+        name: '按完成率',
+        value: '1'
+    },
+    {
+        name: '按专业度',
+        value: '2'
+    },
+    {
+        name: '按服务态度',
+        value: '3'
+    }
+];
+const checkActive = ref('1');
+const serviceActive = ref('1');
+const tableData = [
+    {
+        name: '王刚',
+        group: '班组4',
+        num: '123'
+    },
+    {
+        name: '李思',
+        group: '班组2',
+        num: '88'
+    },
+    {
+        name: '张伟',
+        group: '班组3',
+        num: '65'
+    },
+    {
+        name: '张强',
+        group: '班组1',
+        num: '54'
+    }
+];
+const serviceData = [
+    {
+        name: '王伟',
+        group: '供应商3',
+        num: '100%'
+    },
+    {
+        name: '李成',
+        group: '供应商2',
+        num: '88%'
+    },
+    {
+        name: '张雷',
+        group: '供应商1',
+        num: '65%'
+    },
+    {
+        name: '刘飞',
+        group: '供应商4',
+        num: '54%'
+    }
+];
+const warningData = [
+    {
+        deviceNo: 'No12332',
+        warningDate: '2024-10-30 11:45:00',
+        warningType: '故障告警'
+    },
+    {
+        deviceNo: 'No125',
+        warningDate: '2024-10-30 11:25:00',
+        warningType: '离线告警'
+    },
+    {
+        deviceNo: 'No1282',
+        warningDate: '2024-10-30 10:45:00',
+        warningType: '故障告警'
+    },
+    {
+        deviceNo: 'No1236',
+        warningDate: '2024-10-30 09:45:00',
+        warningType: '故障告警'
+    }
+];
+const equipData = ref<any>([
+    {
+        name: '报废',
+        value: 1546
+    },
+    {
+        name: '维修中',
+        value: 189
+    },
+    {
+        name: '故障',
+        value: 1452
+    },
+    {
+        name: '其他',
+        value: 189
+    },
+    {
+        name: '正在运行',
+        value: 600
+    },
+    {
+        name: '停役',
+        value: 200
+    }
+]);
+const color = ['#1990FF', '#8543E0', '#30C25B', '#16C2C2', '#FACC14', '#F04864'];
+equipData.value.forEach((item, index) => {
+    const tag = index % 6;
+    item.itemStyle = {
+        color: color[tag] || ''
+    };
+});
+let equipTotal = 0;
+equipData.value.forEach((v) => {
+    equipTotal += v.value;
+});
+const equipOption = computed(() => {
+    return {
+        legend: {
+            show: true,
+            left: '0%',
+            bottom: '5%',
+            itemGap: 10,
+            borderRadius: 5,
+            itemWidth: 10,
+            icon: 'circle',
+            itemHeight: 10,
+            data: equipData.value,
+            formatter: function (name) {
+                const res = equipData.value.filter((n) => {
+                    return n.name === name;
+                });
+                if (!res.length) return;
+                return `${name}  ${res[0].value ? ((res[0].value / equipTotal) * 100).toFixed(2) : 0}%  ${res[0].value}`;
+            }
+        },
+        tooltip: {
+            trigger: 'item',
+            backgroundColor: 'rgba(13,5,30,.6)',
+            borderWidth: 1,
+            borderColor: '#32A1FF',
+            padding: 5,
+            textStyle: {
+                color: '#fff'
+            },
+            formatter: (params) => {
+                return `${params.name}<br/>${params.marker}${params.value}`;
+            }
+        },
+        title: {
+            show: true,
+            text: '设备总数',
+            itemGap: 5, //主副标题之间的距离
+            left: 'center',
+            top: '28%',
+            textStyle: {
+                fontSize: 13,
+                color: '#9E9E9E',
+                fontWeight: 'normal'
+            },
+            subtext: '4226',
+            subtextStyle: {
+                fontSize: 18,
+                fontWeight: 500,
+                color: '#333'
+            }
+        },
+        series: [
+            {
+                name: '',
+                type: 'pie',
+                radius: ['40%', '60%'],
+                center: ['50%', '35%'],
+                itemStyle: {
+                    borderWidth: 2, //描边线宽
+                    borderColor: '#fff'
+                },
+                label: {
+                    show: false
+                },
+                labelLine: {},
+                data: equipData.value
+            }
+        ]
+    };
+});
+const getRecentWeekDates = () => {
+    const today = new Date();
+    const dates = [];
+    for (let i = 0; i < 7; i++) {
+        // 创建一个新的日期对象,将今天的日期减去i天
+        const date = new Date(today);
+        date.setDate(date.getDate() - i);
+
+        // 将日期格式化为 YYYY/MM/DD 或者其他你想要的格式
+        const formattedDate = `${date.getFullYear()}-${('0' + (date.getMonth() + 1)).slice(-2)}-${('0' + date.getDate()).slice(-2)}`;
+        dates.push(formattedDate);
+    }
+    // 由于我们是从今天开始向过去推算的,所以需要反转数组以使日期递增
+    dates.reverse();
+    return dates;
+};
+const checkData = getRecentWeekDates().map((item) => ({
+    xData: item.substr(5),
+    yData1: Math.floor(Math.random() * 100),
+    yData2: Math.floor(Math.random() * 100)
+}));
+const checkOption = computed(() => {
+    return {
+        tooltip: {
+            trigger: 'axis',
+            axisPointer: {
+                type: 'shadow'
+            }
+        },
+        legend: {
+            show: true
+        },
+        grid: {
+            left: '3%',
+            right: '4%',
+            bottom: '3%',
+            containLabel: true
+        },
+        xAxis: [
+            {
+                type: 'category',
+                data: checkData.map((item) => item.xData),
+                axisTick: {
+                    alignWithLabel: true
+                }
+            }
+        ],
+        yAxis: [
+            {
+                type: 'value'
+            }
+        ],
+        series: [
+            {
+                name: '巡检设备数',
+                type: 'bar',
+                barWidth: '12',
+                data: checkData.map((item) => item.yData1)
+            },
+            {
+                name: '维修设备数',
+                type: 'bar',
+                barWidth: '12',
+                data: checkData.map((item) => item.yData2)
+            }
+        ]
+    };
+});
+</script>
+<style lang="scss" scoped>
+.positionImg {
+    padding: 10px 0;
+    width: 100%;
+    height: 100%;
+}
+
+.chart-group {
+    display: flex;
+    margin-top: 5px;
+
+    > div {
+        width: 50%;
+    }
+
+    .chart-two {
+        display: flex;
+
+        > div {
+            flex: 1;
+            flex-shrink: 0;
+        }
+    }
+
+    :deep(.el-card__body) {
+        height: 100%;
+        padding: 15px 10px !important;
+    }
+
+    .el-card {
+        height: 350px;
+        background: #fff;
+        &:not(:first-child) {
+            margin-left: 5px;
+        }
+    }
+
+    .chart-content {
+        height: calc(100% - 10px);
+        margin-top: 5px;
+        border-top: 1px solid #eaebee;
+    }
+}
+
+.check-summary {
+    display: flex;
+    margin-top: 10px;
+
+    > div {
+        flex: 1;
+        padding: 5px 10px;
+        display: flex;
+        background: #fafbfc;
+        border: 1px solid #e4e5e9;
+        border-radius: 4px;
+
+        &:not(:first-child) {
+            margin-left: 10px;
+        }
+
+        img {
+            height: 40px;
+        }
+
+        .check-name {
+            margin-left: 10px;
+
+            div:first-child {
+                font-size: 16px;
+                font-weight: 500;
+            }
+
+            div:last-child {
+                font-size: 14px;
+            }
+        }
+    }
+}
+
+.check-rank {
+    margin-top: 10px;
+    display: flex;
+    justify-content: space-between;
+
+    > div:first-child {
+        font-size: 14px;
+    }
+}
+
+.warnBtns {
+    display: flex;
+
+    .el-button + .el-button {
+        margin-left: 5px;
+    }
+}
+
+.warningTable {
+    :deep(.cell) {
+        padding: 0 3px !important;
+    }
+}
+</style>

+ 780 - 0
src/views/dashboard/dashboardAdmin.vue

@@ -0,0 +1,780 @@
+<template>
+    <div class="dashbord">
+        <div class="chart-group">
+            <el-card>
+                <SubTitle title="设备运行" />
+                <div class="chart-content">
+                    <img class="positionImg" src="@/assets/images/position.png" alt="" />
+                </div>
+            </el-card>
+        </div>
+        <div class="chart-group">
+            <el-card>
+                <SubTitle title="项目情况" />
+                <div class="chart-content">
+                    <BaseChart width="100%" height="100%" :option="projectOption" />
+                </div>
+            </el-card>
+            <el-card>
+                <SubTitle title="设备总览" />
+                <div class="chart-content">
+                    <BaseChart width="100%" height="100%" :option="equipOption" />
+                </div>
+            </el-card>
+            <el-card>
+                <SubTitle title="巡检人员" />
+                <div class="chart-content">
+                    <div class="check-summary">
+                        <div>
+                            <img src="@/assets/images/home/banzu.svg" alt="" />
+                            <div class="check-name">
+                                <div>8</div>
+                                <div>班组</div>
+                            </div>
+                        </div>
+                        <div>
+                            <img src="@/assets/images/home/person.svg" alt="" />
+                            <div class="check-name">
+                                <div>40</div>
+                                <div>巡检人员</div>
+                            </div>
+                        </div>
+                    </div>
+                    <div class="check-rank">
+                        <div>当月巡检排名</div>
+                        <CustomTabs v-model:active="checkActive" :tabs="checkTabs" />
+                    </div>
+                    <el-table :data="tableData" style="width: 100%; margin-top: 10px" max-height="200">
+                        <el-table-column prop="name" label="巡检员">
+                            <template #default="scope">
+                                {{ `${scope.$index + 1}、${scope.row.name}` }}
+                            </template>
+                        </el-table-column>
+
+                        <el-table-column prop="group" label="所在班组" />
+                        <el-table-column prop="num" label="数量" />
+                    </el-table>
+                </div>
+            </el-card>
+            <el-card>
+                <SubTitle title="设备故障" />
+                <div class="chart-content">
+                    <div style="display: flex; justify-content: flex-end; margin-top: 10px">
+                        <CustomTabs v-model:active="faultActive" :tabs="faultTabs" />
+                    </div>
+                    <BaseChart width="100%" height="100%" :option="rankOption" />
+                </div>
+            </el-card>
+        </div>
+    </div>
+</template>
+<script setup lang="ts">
+import CustomTabs from './components/CustomTabs.vue';
+const checkTabs = [
+    {
+        name: '按巡检设备数',
+        value: '1'
+    },
+    {
+        name: '按发现故障数',
+        value: '2'
+    }
+];
+const faultTabs = [
+    {
+        name: '按设备类型',
+        value: '1'
+    },
+    {
+        name: '按故障类型',
+        value: '2'
+    }
+];
+const checkActive = ref('1');
+const faultActive = ref('1');
+const tableData = [
+    {
+        name: '王刚',
+        group: '班组4',
+        num: '123'
+    },
+    {
+        name: '李思',
+        group: '班组2',
+        num: '88'
+    },
+    {
+        name: '张伟',
+        group: '班组3',
+        num: '65'
+    },
+    {
+        name: '张强',
+        group: '班组1',
+        num: '54'
+    }
+];
+function getParametricEquation(startRatio, endRatio, isSelected, isHovered, k, height) {
+    // 计算
+    let midRatio = (startRatio + endRatio) / 2;
+
+    let startRadian = startRatio * Math.PI * 2;
+    let endRadian = endRatio * Math.PI * 2;
+    let midRadian = midRatio * Math.PI * 2;
+
+    // 如果只有一个扇形,则不实现选中效果。
+    if (startRatio === 0 && endRatio === 1) {
+        isSelected = false;
+    }
+
+    // 通过扇形内径/外径的值,换算出辅助参数 k(默认值 1/3)
+    k = typeof k !== 'undefined' ? k : 1 / 3;
+
+    // 计算选中效果分别在 x 轴、y 轴方向上的位移(未选中,则位移均为 0)
+    let offsetX = isSelected ? Math.cos(midRadian) * 0.1 : 0;
+    let offsetY = isSelected ? Math.sin(midRadian) * 0.1 : 0;
+
+    // 计算高亮效果的放大比例(未高亮,则比例为 1)
+    let hoverRate = isHovered ? 1.05 : 1;
+
+    // 返回曲面参数方程
+    return {
+        u: {
+            min: -Math.PI,
+            max: Math.PI * 3,
+            step: Math.PI / 32
+        },
+
+        v: {
+            min: 0,
+            max: Math.PI * 2,
+            step: Math.PI / 20
+        },
+
+        x: function (u, v) {
+            if (u < startRadian) {
+                return offsetX + Math.cos(startRadian) * (1 + Math.cos(v) * k) * hoverRate;
+            }
+            if (u > endRadian) {
+                return offsetX + Math.cos(endRadian) * (1 + Math.cos(v) * k) * hoverRate;
+            }
+            return offsetX + Math.cos(u) * (1 + Math.cos(v) * k) * hoverRate;
+        },
+
+        y: function (u, v) {
+            if (u < startRadian) {
+                return offsetY + Math.sin(startRadian) * (1 + Math.cos(v) * k) * hoverRate;
+            }
+            if (u > endRadian) {
+                return offsetY + Math.sin(endRadian) * (1 + Math.cos(v) * k) * hoverRate;
+            }
+            return offsetY + Math.sin(u) * (1 + Math.cos(v) * k) * hoverRate;
+        },
+
+        z: function (u, v) {
+            if (u < -Math.PI * 0.5) {
+                return Math.sin(u);
+            }
+            if (u > Math.PI * 2.5) {
+                return Math.sin(u);
+            }
+            return Math.sin(v) > 0 ? 0.2 * height : -1;
+        }
+    };
+}
+
+// 生成模拟 3D 饼图的配置项
+function getPie3D(pieData, internalDiameterRatio) {
+    let series = [];
+    let sumValue = 0;
+    let startValue = 0;
+    let endValue = 0;
+    let legendData = [];
+    let k = typeof internalDiameterRatio !== 'undefined' ? (1 - internalDiameterRatio) / (1 + internalDiameterRatio) : 1 / 3;
+
+    // 为每一个饼图数据,生成一个 series-surface 配置
+    for (let i = 0; i < pieData.length; i++) {
+        sumValue += pieData[i].value;
+
+        let seriesItem = {
+            name: typeof pieData[i].name === 'undefined' ? `series${i}` : pieData[i].name,
+            type: 'surface',
+            parametric: true,
+            wireframe: {
+                show: false
+            },
+            pieData: pieData[i],
+            pieStatus: {
+                selected: false,
+                hovered: false,
+                k: k
+            }
+        };
+
+        if (typeof pieData[i].itemStyle != 'undefined') {
+            let itemStyle = {};
+
+            typeof pieData[i].itemStyle.color != 'undefined' ? (itemStyle.color = pieData[i].itemStyle.color) : null;
+            typeof pieData[i].itemStyle.opacity != 'undefined' ? (itemStyle.opacity = pieData[i].itemStyle.opacity) : null;
+
+            seriesItem.itemStyle = itemStyle;
+        }
+        series.push(seriesItem);
+    }
+
+    // 使用上一次遍历时,计算出的数据和 sumValue,调用 getParametricEquation 函数,
+    // 向每个 series-surface 传入不同的参数方程 series-surface.parametricEquation,也就是实现每一个扇形。
+    for (let i = 0; i < series.length; i++) {
+        endValue = startValue + series[i].pieData.value;
+        console.log(series[i]);
+        series[i].pieData.startRatio = startValue / sumValue;
+        series[i].pieData.endRatio = endValue / sumValue;
+        series[i].parametricEquation = getParametricEquation(
+            series[i].pieData.startRatio,
+            series[i].pieData.endRatio,
+            false,
+            false,
+            k,
+            series[i].pieData.value
+        );
+
+        startValue = endValue;
+
+        legendData.push(series[i].name);
+    }
+
+    // // 补充一个透明的圆环,用于支撑高亮功能的近似实现。
+    series.push({
+        name: 'mouseoutSeries',
+        type: 'surface',
+        parametric: true,
+        wireframe: {
+            show: false
+        },
+        itemStyle: {
+            opacity: 0.1,
+            color: '#8997DE'
+        },
+        parametricEquation: {
+            u: {
+                min: 0,
+                max: Math.PI * 2,
+                step: Math.PI / 20
+            },
+            v: {
+                min: 0,
+                max: Math.PI,
+                step: Math.PI / 20
+            },
+            x: function (u, v) {
+                return ((Math.sin(v) * Math.sin(u) + Math.sin(u)) / Math.PI) * 2;
+            },
+            y: function (u, v) {
+                return ((Math.sin(v) * Math.cos(u) + Math.cos(u)) / Math.PI) * 2;
+            },
+            z: function (u, v) {
+                return Math.cos(v) > 0 ? -0.5 : -5;
+            }
+        }
+    });
+
+    // // 补充一个透明的圆环,用于支撑高亮功能的近似实现。
+    series.push({
+        name: 'mouseoutSeries',
+        type: 'surface',
+        parametric: true,
+        wireframe: {
+            show: false
+        },
+        itemStyle: {
+            opacity: 0.1,
+            color: '#8997DE'
+        },
+        parametricEquation: {
+            u: {
+                min: 0,
+                max: Math.PI * 2,
+                step: Math.PI / 20
+            },
+            v: {
+                min: 0,
+                max: Math.PI,
+                step: Math.PI / 20
+            },
+            x: function (u, v) {
+                return ((Math.sin(v) * Math.sin(u) + Math.sin(u)) / Math.PI) * 2;
+            },
+            y: function (u, v) {
+                return ((Math.sin(v) * Math.cos(u) + Math.cos(u)) / Math.PI) * 2;
+            },
+            z: function (u, v) {
+                return Math.cos(v) > 0 ? -5 : -7;
+            }
+        }
+    });
+    series.push({
+        name: 'mouseoutSeries',
+        type: 'surface',
+        parametric: true,
+        wireframe: {
+            show: false
+        },
+        itemStyle: {
+            opacity: 0.1,
+            color: '#8997DE'
+        },
+
+        parametricEquation: {
+            u: {
+                min: 0,
+                max: Math.PI * 2,
+                step: Math.PI / 20
+            },
+            v: {
+                min: 0,
+                max: Math.PI,
+                step: Math.PI / 20
+            },
+            x: function (u, v) {
+                return ((Math.sin(v) * Math.sin(u) + Math.sin(u)) / Math.PI) * 2.2;
+            },
+            y: function (u, v) {
+                return ((Math.sin(v) * Math.cos(u) + Math.cos(u)) / Math.PI) * 2.2;
+            },
+            z: function (u, v) {
+                return Math.cos(v) > 0 ? -7 : -7;
+            }
+        }
+    });
+    return series;
+}
+
+let colors = ['#085AC7', '#24525E', '#C3972E'];
+let xData = ['运营', '施工', '结束'];
+let yData = [568, 175, 396];
+// 传入数据生成 option
+let optionsData = [];
+let total = 0;
+yData.forEach((v) => {
+    total += v;
+});
+for (let i = 0; i < xData.length; i++) {
+    optionsData.push({
+        name: xData[i],
+        value: yData[i],
+        itemStyle: {
+            color: colors[i],
+            opacity: 0.7
+        }
+    });
+}
+
+const series = getPie3D(optionsData, 0.8);
+const projectOption = computed(() => {
+    return {
+        legend: {
+            tooltip: {
+                show: true
+            },
+            data: xData,
+            orient: 'vertial',
+            bottom: '5%',
+            left: 'center',
+            itemGap: 14,
+            itemHeight: 10,
+            itemWidth: 15,
+            formatter: (name) => {
+                const res = optionsData.filter((n) => {
+                    return n.name === name;
+                });
+                if (!res.length) return;
+                return `${name}  ${res[0].value}个  ${res[0].value ? ((res[0].value / total) * 100).toFixed(2) : 0}%`;
+            }
+        },
+        animation: true,
+        tooltip: {
+            formatter: (params) => {
+                if (params.seriesName !== 'mouseoutSeries' && params.seriesName !== 'pie2d') {
+                    return `${params.seriesName}<br/><span style="display:inline-block;margin-right:5px;border-radius:10px;width:10px;height:10px;background-color:${params.color};"></span>${projectOption.value.series[params.seriesIndex].pieData.value}个`;
+                }
+            },
+            textStyle: {
+                fontSize: 12
+            }
+        },
+        xAxis3D: {
+            min: -1,
+            max: 1
+        },
+        yAxis3D: {
+            min: -1,
+            max: 1
+        },
+        zAxis3D: {
+            min: -1,
+            max: 1
+        },
+        grid3D: {
+            show: false,
+            boxHeight: 0.5,
+            top: '-10%',
+            viewControl: {
+                distance: 240,
+                alpha: 30,
+                beta: 10,
+                autoRotate: true // 自动旋转
+            }
+        },
+
+        series: series
+    };
+});
+const equipData = ref<any>([
+    {
+        name: '报废',
+        value: 1546
+    },
+    {
+        name: '维修中',
+        value: 189
+    },
+    {
+        name: '故障',
+        value: 1452
+    },
+    {
+        name: '其他',
+        value: 189
+    },
+    {
+        name: '正在运行',
+        value: 600
+    },
+    {
+        name: '停役',
+        value: 200
+    }
+]);
+const color = ['#1990FF', '#8543E0', '#30C25B', '#16C2C2', '#FACC14', '#F04864'];
+equipData.value.forEach((item, index) => {
+    const tag = index % 6;
+    item.itemStyle = {
+        color: color[tag] || ''
+    };
+});
+let equipTotal = 0;
+equipData.value.forEach((v) => {
+    equipTotal += v.value;
+});
+const equipOption = computed(() => {
+    return {
+        legend: {
+            show: true,
+            left: '0%',
+            bottom: '5%',
+            itemGap: 10,
+            borderRadius: 5,
+            itemWidth: 10,
+            icon: 'circle',
+            itemHeight: 10,
+            data: equipData.value,
+            formatter: function (name) {
+                const res = equipData.value.filter((n) => {
+                    return n.name === name;
+                });
+                if (!res.length) return;
+                return `${name}  ${res[0].value ? ((res[0].value / equipTotal) * 100).toFixed(2) : 0}%  ${res[0].value}`;
+            }
+        },
+        tooltip: {
+            trigger: 'item',
+            backgroundColor: 'rgba(13,5,30,.6)',
+            borderWidth: 1,
+            borderColor: '#32A1FF',
+            padding: 5,
+            textStyle: {
+                color: '#fff'
+            },
+            formatter: (params) => {
+                return `${params.name}<br/>${params.marker}${params.value}`;
+            }
+        },
+        title: {
+            show: true,
+            text: '设备总数',
+            itemGap: 5, //主副标题之间的距离
+            left: 'center',
+            top: '28%',
+            textStyle: {
+                fontSize: 13,
+                color: '#9E9E9E',
+                fontWeight: 'normal'
+            },
+            subtext: '4226',
+            subtextStyle: {
+                fontSize: 18,
+                fontWeight: 500,
+                color: '#333'
+            }
+        },
+        series: [
+            {
+                name: '',
+                type: 'pie',
+                radius: ['40%', '60%'],
+                center: ['50%', '35%'],
+                itemStyle: {
+                    borderWidth: 2, //描边线宽
+                    borderColor: '#fff'
+                },
+                label: {
+                    show: false
+                },
+                labelLine: {},
+                data: equipData.value
+            }
+        ]
+    };
+});
+
+const rankData = [
+    { name: '设备类型一', value: 323 },
+    { name: '设备类型二', value: 108 },
+    { name: '设备类型三', value: 95 },
+    { name: '设备类型四', value: 43 },
+    { name: '设备类型五', value: 10 }
+]; // 类别
+let rankTotal = 0; // 数据总数
+rankData.forEach((v) => {
+    rankTotal += v.value;
+});
+const rankOption = computed(() => {
+    return {
+        grid: {
+            left: '5%',
+            top: '3%', // 设置条形图的边距
+            right: '12%',
+            bottom: '15%'
+        },
+        xAxis: {
+            splitLine: {
+                show: false,
+                lineStyle: {
+                    color: 'rgba(255,255,255,0.2)',
+                    type: 'dashed'
+                }
+            },
+            axisLine: {
+                show: false
+            },
+            axisLabel: {
+                show: false,
+                color: '#ABBFE3'
+            },
+            axisTick: {
+                show: false
+            }
+        },
+        yAxis: [
+            {
+                type: 'category',
+                inverse: true,
+                data: rankData.map((item) => item.name),
+                axisLine: {
+                    show: false
+                },
+                axisTick: {
+                    show: false
+                },
+                axisLabel: {
+                    show: true,
+                    textStyle: {
+                        verticalAlign: 'bottom',
+                        color: '#000',
+                        fontSize: 12,
+                        fontFamily: 'Microsoft YaHei',
+                        align: 'left',
+                        padding: [0, 0, 9, 5]
+                    },
+                    formatter: (name, index) => {
+                        const _index = index + 1;
+                        return `NO${_index}. ${name}`;
+                    }
+                },
+                offset: 0
+            }
+        ],
+        series: [
+            {
+                // 内
+                type: 'bar',
+                barWidth: 10,
+                barCateGoryGap: 20,
+                legendHoverLink: false,
+                silent: true,
+                itemStyle: {
+                    normal: {
+                        barBorderRadius: 10,
+                        color: {
+                            type: 'linear',
+                            x: 0,
+                            y: 0,
+                            x2: 1,
+                            y2: 0,
+                            colorStops: [
+                                {
+                                    offset: 0,
+                                    color: '#FFFFFF' // 0% 处的颜色
+                                },
+                                {
+                                    offset: 1,
+                                    color: '#0768FF' // 100% 处的颜色
+                                }
+                            ]
+                        }
+                    }
+                },
+                label: {
+                    normal: {
+                        show: false,
+                        position: '[0, 0, 15, 10]',
+                        formatter: '{b}',
+                        textStyle: {
+                            color: '#fff',
+                            fontSize: 14
+                        }
+                    }
+                },
+                data: rankData,
+                z: 2,
+                animationEasing: 'elasticOut'
+            },
+            {
+                // 外边框
+                type: 'pictorialBar',
+                symbol: 'rect',
+                symbolBoundingData: rankTotal,
+                itemStyle: {
+                    barBorderRadius: 10,
+                    normal: {
+                        color: 'none'
+                    }
+                },
+                label: {
+                    normal: {
+                        padding: [0, 10, 0, 14],
+                        formatter: (params) => {
+                            return params.data;
+                        },
+                        color: '#03FF00',
+                        fontWeight: 'bold',
+                        position: 'right',
+                        distance: 1, // 向右偏移位置
+                        show: true
+                    }
+                },
+                data: rankData.map((item) => item.value),
+                z: 0,
+                animationEasing: 'elasticOut'
+            },
+            {
+                name: '外框',
+                type: 'bar',
+                barCateGoryGap: 20,
+                barGap: '-100%', // 设置外框粗细
+                data: new Array(rankData.length).fill(rankTotal),
+                barWidth: 10,
+                itemStyle: {
+                    normal: {
+                        barBorderRadius: [0, 6, 6, 0],
+                        color: '#F2F2F2'
+                    },
+                    emphasis: {
+                        barBorderRadius: [0, 6, 6, 0],
+                        color: '#F2F2F2'
+                    }
+                },
+                z: 0
+            }
+        ]
+    };
+});
+</script>
+<style lang="scss" scoped>
+.positionImg {
+    width: 100%;
+    height: 100%;
+    padding: 10px 0;
+}
+
+.chart-group {
+    display: flex;
+    margin-top: 5px;
+
+    :deep(.el-card__body) {
+        height: 100%;
+        padding: 15px 10px !important;
+    }
+
+    .el-card {
+        flex: 1;
+        height: 350px;
+        background: #fff;
+
+        &:not(:first-child) {
+            margin-left: 5px;
+        }
+    }
+
+    .chart-content {
+        height: calc(100% - 10px);
+        margin-top: 5px;
+        border-top: 1px solid #eaebee;
+    }
+}
+
+.check-summary {
+    display: flex;
+    margin-top: 10px;
+
+    > div {
+        flex: 1;
+        padding: 5px 10px;
+        display: flex;
+        background: #fafbfc;
+        border: 1px solid #e4e5e9;
+        border-radius: 4px;
+
+        &:not(:first-child) {
+            margin-left: 10px;
+        }
+
+        img {
+            height: 40px;
+        }
+
+        .check-name {
+            margin-left: 10px;
+
+            div:first-child {
+                font-size: 16px;
+                font-weight: 500;
+            }
+
+            div:last-child {
+                font-size: 14px;
+            }
+        }
+    }
+}
+
+.check-rank {
+    margin-top: 10px;
+    display: flex;
+    justify-content: space-between;
+
+    > div:first-child {
+        font-size: 14px;
+    }
+}
+</style>

+ 21 - 0
src/views/dashboard/index.vue

@@ -0,0 +1,21 @@
+<template>
+    <div class="home">
+        <component :is="comp"></component>
+    </div>
+</template>
+
+<script setup name="Index" lang="ts">
+import useUserStore from '@/store/modules/user';
+import DashbordAdmin from './dashboardAdmin.vue';
+import Dashbord from './dashboard.vue';
+console.log(useUserStore().roles);
+const comp = !useUserStore().roles.includes('superadmin') ? DashbordAdmin : Dashbord;
+</script>
+
+<style scoped lang="scss">
+.home {
+    height: 100%;
+    padding: 10px 20px;
+    background: #fcfcfc;
+}
+</style>

+ 0 - 5
src/views/dashbord.vue

@@ -1,5 +0,0 @@
-<template>
-  <div class=""></div>
-</template>
-<script setup lang="ts"></script>
-<style lang="scss" scoped></style>

+ 0 - 380
src/views/dashbordAdmin.vue

@@ -1,380 +0,0 @@
-<template>
-    <div class="dashbord">
-        <el-card>
-            <SubTitle title="设备运行" />
-            <img class="positionImg" src="@/assets/images/position.png" alt="" />
-        </el-card>
-        <div class="chart-group">
-            <el-card>
-                <SubTitle title="项目情况" />
-                <div class="chart-content">
-                    <BaseChart width="100%" height="100%" :option="option" />
-                </div>
-            </el-card>
-            <el-card>
-                <SubTitle title="设备总览" />
-                <div class="chart-content">
-                    <BaseChart width="100%" height="100%" :option="option" />
-                </div>
-            </el-card>
-            <el-card>
-                <SubTitle title="巡检人员" />
-            </el-card>
-            <el-card>
-                <SubTitle title="设备故障" />
-            </el-card>
-        </div>
-    </div>
-</template>
-<script setup lang="ts">
-function getParametricEquation(startRatio, endRatio, isSelected, isHovered, k, height) {
-    // 计算
-    let midRatio = (startRatio + endRatio) / 2;
-
-    let startRadian = startRatio * Math.PI * 2;
-    let endRadian = endRatio * Math.PI * 2;
-    let midRadian = midRatio * Math.PI * 2;
-
-    // 如果只有一个扇形,则不实现选中效果。
-    if (startRatio === 0 && endRatio === 1) {
-        isSelected = false;
-    }
-
-    // 通过扇形内径/外径的值,换算出辅助参数 k(默认值 1/3)
-    k = typeof k !== 'undefined' ? k : 1 / 3;
-
-    // 计算选中效果分别在 x 轴、y 轴方向上的位移(未选中,则位移均为 0)
-    let offsetX = isSelected ? Math.cos(midRadian) * 0.1 : 0;
-    let offsetY = isSelected ? Math.sin(midRadian) * 0.1 : 0;
-
-    // 计算高亮效果的放大比例(未高亮,则比例为 1)
-    let hoverRate = isHovered ? 1.05 : 1;
-
-    // 返回曲面参数方程
-    return {
-        u: {
-            min: -Math.PI,
-            max: Math.PI * 3,
-            step: Math.PI / 32
-        },
-
-        v: {
-            min: 0,
-            max: Math.PI * 2,
-            step: Math.PI / 20
-        },
-
-        x: function (u, v) {
-            if (u < startRadian) {
-                return offsetX + Math.cos(startRadian) * (1 + Math.cos(v) * k) * hoverRate;
-            }
-            if (u > endRadian) {
-                return offsetX + Math.cos(endRadian) * (1 + Math.cos(v) * k) * hoverRate;
-            }
-            return offsetX + Math.cos(u) * (1 + Math.cos(v) * k) * hoverRate;
-        },
-
-        y: function (u, v) {
-            if (u < startRadian) {
-                return offsetY + Math.sin(startRadian) * (1 + Math.cos(v) * k) * hoverRate;
-            }
-            if (u > endRadian) {
-                return offsetY + Math.sin(endRadian) * (1 + Math.cos(v) * k) * hoverRate;
-            }
-            return offsetY + Math.sin(u) * (1 + Math.cos(v) * k) * hoverRate;
-        },
-
-        z: function (u, v) {
-            if (u < -Math.PI * 0.5) {
-                return Math.sin(u);
-            }
-            if (u > Math.PI * 2.5) {
-                return Math.sin(u);
-            }
-            return Math.sin(v) > 0 ? 0.2 * height : -1;
-        }
-    };
-}
-
-// 生成模拟 3D 饼图的配置项
-function getPie3D(pieData, internalDiameterRatio) {
-    let series = [];
-    let sumValue = 0;
-    let startValue = 0;
-    let endValue = 0;
-    let legendData = [];
-    let k = typeof internalDiameterRatio !== 'undefined' ? (1 - internalDiameterRatio) / (1 + internalDiameterRatio) : 1 / 3;
-
-    // 为每一个饼图数据,生成一个 series-surface 配置
-    for (let i = 0; i < pieData.length; i++) {
-        sumValue += pieData[i].value;
-
-        let seriesItem = {
-            name: typeof pieData[i].name === 'undefined' ? `series${i}` : pieData[i].name,
-            type: 'surface',
-            parametric: true,
-            wireframe: {
-                show: false
-            },
-            pieData: pieData[i],
-            pieStatus: {
-                selected: false,
-                hovered: false,
-                k: k
-            }
-        };
-
-        if (typeof pieData[i].itemStyle != 'undefined') {
-            let itemStyle = {};
-
-            typeof pieData[i].itemStyle.color != 'undefined' ? (itemStyle.color = pieData[i].itemStyle.color) : null;
-            typeof pieData[i].itemStyle.opacity != 'undefined' ? (itemStyle.opacity = pieData[i].itemStyle.opacity) : null;
-
-            seriesItem.itemStyle = itemStyle;
-        }
-        series.push(seriesItem);
-    }
-
-    // 使用上一次遍历时,计算出的数据和 sumValue,调用 getParametricEquation 函数,
-    // 向每个 series-surface 传入不同的参数方程 series-surface.parametricEquation,也就是实现每一个扇形。
-    for (let i = 0; i < series.length; i++) {
-        endValue = startValue + series[i].pieData.value;
-        console.log(series[i]);
-        series[i].pieData.startRatio = startValue / sumValue;
-        series[i].pieData.endRatio = endValue / sumValue;
-        series[i].parametricEquation = getParametricEquation(
-            series[i].pieData.startRatio,
-            series[i].pieData.endRatio,
-            false,
-            false,
-            k,
-            series[i].pieData.value
-        );
-
-        startValue = endValue;
-
-        legendData.push(series[i].name);
-    }
-
-    // // 补充一个透明的圆环,用于支撑高亮功能的近似实现。
-    series.push({
-        name: 'mouseoutSeries',
-        type: 'surface',
-        parametric: true,
-        wireframe: {
-            show: false
-        },
-        itemStyle: {
-            opacity: 0.1,
-            color: '#8997DE'
-        },
-        parametricEquation: {
-            u: {
-                min: 0,
-                max: Math.PI * 2,
-                step: Math.PI / 20
-            },
-            v: {
-                min: 0,
-                max: Math.PI,
-                step: Math.PI / 20
-            },
-            x: function (u, v) {
-                return ((Math.sin(v) * Math.sin(u) + Math.sin(u)) / Math.PI) * 2;
-            },
-            y: function (u, v) {
-                return ((Math.sin(v) * Math.cos(u) + Math.cos(u)) / Math.PI) * 2;
-            },
-            z: function (u, v) {
-                return Math.cos(v) > 0 ? -0.5 : -5;
-            }
-        }
-    });
-
-    // // 补充一个透明的圆环,用于支撑高亮功能的近似实现。
-    series.push({
-        name: 'mouseoutSeries',
-        type: 'surface',
-        parametric: true,
-        wireframe: {
-            show: false
-        },
-        itemStyle: {
-            opacity: 0.1,
-            color: '#8997DE'
-        },
-        parametricEquation: {
-            u: {
-                min: 0,
-                max: Math.PI * 2,
-                step: Math.PI / 20
-            },
-            v: {
-                min: 0,
-                max: Math.PI,
-                step: Math.PI / 20
-            },
-            x: function (u, v) {
-                return ((Math.sin(v) * Math.sin(u) + Math.sin(u)) / Math.PI) * 2;
-            },
-            y: function (u, v) {
-                return ((Math.sin(v) * Math.cos(u) + Math.cos(u)) / Math.PI) * 2;
-            },
-            z: function (u, v) {
-                return Math.cos(v) > 0 ? -5 : -7;
-            }
-        }
-    });
-    series.push({
-        name: 'mouseoutSeries',
-        type: 'surface',
-        parametric: true,
-        wireframe: {
-            show: false
-        },
-        itemStyle: {
-            opacity: 0.1,
-            color: '#8997DE'
-        },
-
-        parametricEquation: {
-            u: {
-                min: 0,
-                max: Math.PI * 2,
-                step: Math.PI / 20
-            },
-            v: {
-                min: 0,
-                max: Math.PI,
-                step: Math.PI / 20
-            },
-            x: function (u, v) {
-                return ((Math.sin(v) * Math.sin(u) + Math.sin(u)) / Math.PI) * 2.2;
-            },
-            y: function (u, v) {
-                return ((Math.sin(v) * Math.cos(u) + Math.cos(u)) / Math.PI) * 2.2;
-            },
-            z: function (u, v) {
-                return Math.cos(v) > 0 ? -7 : -7;
-            }
-        }
-    });
-    return series;
-}
-
-let colors = ['#085AC7', '#24525E', '#C3972E'];
-let xData = ['运营', '施工', '结束'];
-let yData = [568, 175, 396];
-// 传入数据生成 option
-let optionsData = [];
-let total = 0;
-yData.forEach((v) => {
-    total += v;
-});
-for (let i = 0; i < xData.length; i++) {
-    optionsData.push({
-        name: xData[i],
-        value: yData[i],
-        itemStyle: {
-            color: colors[i],
-            opacity: 0.7
-        }
-    });
-}
-
-const series = getPie3D(optionsData, 0.8);
-const option = computed(() => {
-    return {
-        grid: {
-            top: '0%',
-            containLabel: true
-        },
-        legend: {
-            tooltip: {
-                show: true
-            },
-            data: xData,
-            orient: 'vertial',
-            bottom: '10%',
-            left: 'center',
-            itemGap: 14,
-            itemHeight: 10,
-            itemWidth: 15,
-            formatter: (name) => {
-                const res = optionsData.filter((n) => {
-                    return n.name === name;
-                });
-                if (!res.length) return;
-                return `${name}  ${res[0].value}个  ${res[0].value ? ((res[0].value / total) * 100).toFixed(2) : 0}%`;
-            }
-        },
-        animation: true,
-        tooltip: {
-            formatter: (params) => {
-                if (params.seriesName !== 'mouseoutSeries' && params.seriesName !== 'pie2d') {
-                    return `${params.seriesName}<br/><span style="display:inline-block;margin-right:5px;border-radius:10px;width:10px;height:10px;background-color:${params.color};"></span>${option.value.series[params.seriesIndex].pieData.value}个`;
-                }
-            },
-            textStyle: {
-                fontSize: 12
-            }
-        },
-        xAxis3D: {
-            min: -1,
-            max: 1
-        },
-        yAxis3D: {
-            min: -1,
-            max: 1
-        },
-        zAxis3D: {
-            min: -1,
-            max: 1
-        },
-        grid3D: {
-            show: false,
-            boxHeight: 0.6,
-            bottom: '20%',
-            viewControl: {
-                distance: 280,
-                alpha: 25,
-                beta: 60,
-                autoRotate: true // 自动旋转
-            }
-        },
-
-        series: series
-    };
-});
-</script>
-<style lang="scss" scoped>
-.positionImg {
-    width: 100%;
-    height: 250px;
-}
-
-.chart-group {
-    display: flex;
-    margin-top: 5px;
-
-    :deep(.el-card__body) {
-        height: 100%;
-    }
-
-    .el-card {
-        flex: 1;
-        height: 350px;
-        background: #fff;
-
-        &:not(:first-child) {
-            margin-left: 5px;
-        }
-    }
-
-    .chart-content {
-        height: 100%;
-        margin-top: 5px;
-        border-top: 1px solid #eaebee;
-    }
-}
-</style>

+ 567 - 545
src/views/deviceManage/deviceType/index.vue

@@ -1,293 +1,313 @@
 <template>
-  <div class="p-2">
-    <el-row :gutter="10">
-      <el-col :span="5">
-        <el-card>
-          <template #header>
-            <div class="card-header">
-              <span>分项系统</span>
-              <el-icon color="#409EFF" @click="addSystem">
-                <Plus />
-              </el-icon>
-            </div>
-          </template>
-          <div>
-            <el-input v-model="filterText" style="width: 200px" placeholder="请输入关键字" />
-            <el-tree
-              ref="treeRef"
-              style="max-width: 600px; margin-top: 5px"
-              class="filter-tree"
-              :props="defaultProps"
-              :data="treeData"
-              node-key="id"
-              default-expand-all
-              highlight-current
-              :expand-on-click-node="false"
-              :filter-node-method="filterNode"
-              @node-click="handleNodeClick"
-            >
-              <template #default="{ node, data }">
-                <div class="custom-tree-node">
-                  <span>{{ node.label }}</span>
-                  <el-popover placement="bottom" :popper-style="{ 'width': '70px', 'min-width': '70px' }" trigger="click">
-                    <div class="node-operateBtns">
-                      <div @click="addSystem(data)">新增</div>
-                      <div @click="editSystem(node, data)">编辑</div>
-                      <div @click="delSystem(node, data)">删除</div>
+    <div class="p-2">
+        <el-row :gutter="10">
+            <el-col :span="5">
+                <el-card>
+                    <template #header>
+                        <div class="card-header">
+                            <span>分项系统</span>
+                            <el-icon color="#409EFF" @click="addSystem">
+                                <Plus />
+                            </el-icon>
+                        </div>
+                    </template>
+                    <div>
+                        <el-input v-model="filterText" style="width: 200px" placeholder="请输入关键字" />
+                        <el-tree
+                            ref="treeRef"
+                            style="max-width: 600px; margin-top: 5px"
+                            class="filter-tree"
+                            :props="defaultProps"
+                            :data="treeData"
+                            node-key="id"
+                            default-expand-all
+                            highlight-current
+                            :expand-on-click-node="false"
+                            :filter-node-method="filterNode"
+                            @node-click="handleNodeClick"
+                        >
+                            <template #default="{ node, data }">
+                                <div class="custom-tree-node">
+                                    <span>{{ node.label }}</span>
+                                    <el-popover placement="bottom" :popper-style="{ 'width': '70px', 'min-width': '70px' }" trigger="click">
+                                        <div class="node-operateBtns">
+                                            <div @click="addSystem(data)">新增</div>
+                                            <div @click="editSystem(node, data)">编辑</div>
+                                            <div @click="delSystem(node, data)">删除</div>
+                                        </div>
+                                        <template #reference>
+                                            <el-icon>
+                                                <Setting />
+                                            </el-icon>
+                                        </template>
+                                    </el-popover>
+                                </div>
+                            </template>
+                        </el-tree>
+                    </div>
+                </el-card>
+            </el-col>
+            <el-col :span="19">
+                <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+                    <div v-show="showSearch" class="mb-[10px]">
+                        <el-card shadow="hover">
+                            <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="100px">
+                                <el-form-item label="设备类型名称" prop="name">
+                                    <el-input v-model="queryParams.name" placeholder="请输入设备类型名称" clearable @keyup.enter="handleQuery" />
+                                </el-form-item>
+                                <el-form-item>
+                                    <el-button type="primary" icon="Search" @click="handleQuery"> 搜索 </el-button>
+                                    <el-button icon="Refresh" @click="resetQuery"> 重置 </el-button>
+                                </el-form-item>
+                            </el-form>
+                        </el-card>
                     </div>
-                    <template #reference>
-                      <el-icon>
-                        <Setting />
-                      </el-icon>
+                </transition>
+
+                <el-card shadow="never">
+                    <template #header>
+                        <el-row :gutter="10" class="mb8">
+                            <el-col :span="1.5">
+                                <el-button v-hasPermi="['jdyw:deviceType:add']" type="primary" plain icon="Plus" @click="handleAdd"> 新增 </el-button>
+                            </el-col>
+                            <el-col :span="1.5">
+                                <el-button
+                                    v-hasPermi="['jdyw:deviceType:edit']"
+                                    type="success"
+                                    plain
+                                    icon="Edit"
+                                    :disabled="single"
+                                    @click="handleUpdate"
+                                >
+                                    修改
+                                </el-button>
+                            </el-col>
+                            <el-col :span="1.5">
+                                <el-button
+                                    v-hasPermi="['jdyw:deviceType:remove']"
+                                    type="danger"
+                                    plain
+                                    icon="Delete"
+                                    :disabled="multiple"
+                                    @click="handleDelete"
+                                >
+                                    删除
+                                </el-button>
+                            </el-col>
+                            <right-toolbar v-model:showSearch="showSearch" @query-table="getList" />
+                        </el-row>
                     </template>
-                  </el-popover>
-                </div>
-              </template>
-            </el-tree>
-          </div>
-        </el-card>
-      </el-col>
-      <el-col :span="19">
-        <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
-          <div v-show="showSearch" class="mb-[10px]">
-            <el-card shadow="hover">
-              <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="100px">
+
+                    <el-table v-loading="loading" :data="deviceTypeList" @selection-change="handleSelectionChange">
+                        <el-table-column type="selection" width="55" align="center" />
+                        <el-table-column label="设备类型名称" align="center" prop="name" />
+                        <el-table-column label="设备等级" align="center" prop="deviceLevel" />
+                        <el-table-column label="分项系统" align="center" show-overflow-tooltip prop="systemId">
+                            <template #default="scope">
+                                {{ findNameWithParents(treeData, scope.row.systemId, '') }}
+                            </template>
+                        </el-table-column>
+                        <el-table-column label="备注" align="center" prop="remark" />
+                        <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+                            <template #default="scope">
+                                <el-button v-hasPermi="['jdyw:deviceType:edit']" size="small" link type="primary" @click="handleUpdate(scope.row)"
+                                    >修改</el-button
+                                >
+                                <el-button v-hasPermi="['jdyw:deviceType:edit']" size="small" link type="primary" @click="handleModel(scope.row)"
+                                    >物模型</el-button
+                                >
+                                <el-button v-hasPermi="['jdyw:deviceType:remove']" size="small" link type="danger" @click="handleDelete(scope.row)"
+                                    >删除</el-button
+                                >
+                            </template>
+                        </el-table-column>
+                    </el-table>
+                    <pagination
+                        v-show="total > 0"
+                        v-model:page="queryParams.pageNum"
+                        v-model:limit="queryParams.pageSize"
+                        :total="total"
+                        @pagination="getList"
+                    />
+                </el-card>
+            </el-col>
+        </el-row>
+
+        <!-- 添加或修改设备类型信息对话框 -->
+        <el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
+            <el-form ref="deviceTypeFormRef" :model="form" :rules="rules" label-width="110px">
                 <el-form-item label="设备类型名称" prop="name">
-                  <el-input v-model="queryParams.name" placeholder="请输入设备类型名称" clearable @keyup.enter="handleQuery" />
+                    <el-input v-model="form.name" placeholder="请输入设备类型名称" />
                 </el-form-item>
-                <el-form-item>
-                  <el-button type="primary" icon="Search" @click="handleQuery"> 搜索 </el-button>
-                  <el-button icon="Refresh" @click="resetQuery"> 重置 </el-button>
+                <el-form-item label="设备等级" prop="deviceLevel">
+                    <el-select v-model="form.deviceLevel" placeholder="请选择设备等级">
+                        <el-option v-for="dict in device_level" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
+                    </el-select>
                 </el-form-item>
-              </el-form>
-            </el-card>
-          </div>
-        </transition>
-
-        <el-card shadow="never">
-          <template #header>
-            <el-row :gutter="10" class="mb8">
-              <el-col :span="1.5">
-                <el-button v-hasPermi="['jdyw:deviceType:add']" type="primary" plain icon="Plus" @click="handleAdd"> 新增 </el-button>
-              </el-col>
-              <el-col :span="1.5">
-                <el-button v-hasPermi="['jdyw:deviceType:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate">
-                  修改
-                </el-button>
-              </el-col>
-              <el-col :span="1.5">
-                <el-button v-hasPermi="['jdyw:deviceType:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete">
-                  删除
-                </el-button>
-              </el-col>
-              <right-toolbar v-model:showSearch="showSearch" @query-table="getList" />
-            </el-row>
-          </template>
-
-          <el-table v-loading="loading" :data="deviceTypeList" @selection-change="handleSelectionChange">
-            <el-table-column type="selection" width="55" align="center" />
-            <el-table-column label="设备类型名称" align="center" prop="name" />
-            <el-table-column label="设备等级" align="center" prop="deviceLevel" />
-            <el-table-column label="分项系统" align="center" show-overflow-tooltip prop="systemId">
-              <template #default="scope">
-                {{ findNameWithParents(treeData, scope.row.systemId, '') }}
-              </template>
-            </el-table-column>
-            <el-table-column label="备注" align="center" prop="remark" />
-            <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
-              <template #default="scope">
-                <el-button v-hasPermi="['jdyw:deviceType:edit']" size="small" link type="primary" @click="handleUpdate(scope.row)">修改</el-button>
-                <el-button v-hasPermi="['jdyw:deviceType:edit']" size="small" link type="primary" @click="handleModel(scope.row)">物模型</el-button>
-                <el-button v-hasPermi="['jdyw:deviceType:remove']" size="small" link type="danger" @click="handleDelete(scope.row)">删除</el-button>
-              </template>
-            </el-table-column>
-          </el-table>
-          <pagination
-            v-show="total > 0"
-            v-model:page="queryParams.pageNum"
-            v-model:limit="queryParams.pageSize"
-            :total="total"
-            @pagination="getList"
-          />
-        </el-card>
-      </el-col>
-    </el-row>
-
-    <!-- 添加或修改设备类型信息对话框 -->
-    <el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
-      <el-form ref="deviceTypeFormRef" :model="form" :rules="rules" label-width="110px">
-        <el-form-item label="设备类型名称" prop="name">
-          <el-input v-model="form.name" placeholder="请输入设备类型名称" />
-        </el-form-item>
-        <el-form-item label="设备等级" prop="deviceLevel">
-          <el-select v-model="form.deviceLevel" placeholder="请选择设备等级">
-            <el-option v-for="dict in device_level" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
-          </el-select>
-        </el-form-item>
-        <el-form-item label="分项系统" prop="systemId">
-          <el-tree-select
-            v-model="form.systemId"
-            :props="defaultProps"
-            node-key="id"
-            :data="treeData"
-            default-expand-all
-            :check-strictly="true"
-            :render-after-expand="false"
-          />
-        </el-form-item>
-        <el-form-item label="备注" prop="remark">
-          <el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
-        </el-form-item>
-      </el-form>
-      <template #footer>
-        <div class="dialog-footer">
-          <el-button :loading="buttonLoading" type="primary" @click="submitForm"> 确 定 </el-button>
-          <el-button @click="cancel"> 取 消 </el-button>
-        </div>
-      </template>
-    </el-dialog>
-    <el-dialog v-model="systemDialog.visible" :title="systemDialog.title" width="500px" append-to-body>
-      <el-form ref="deviceSystemFormRef" :model="systemForm" label-width="120px">
-        <el-form-item label="上级分项系统" prop="parentName" required>
-          <el-input v-model="systemForm.parentName" disabled placeholder="请选择上级分项系统" />
-        </el-form-item>
-        <el-form-item label="系统名称" prop="name" required :rules="[{ required: true, message: '请输入系统名称' }]">
-          <el-input v-model="systemForm.name" placeholder="请输入系统名称" />
-        </el-form-item>
-      </el-form>
-      <template #footer>
-        <div class="dialog-footer">
-          <el-button :loading="buttonLoading" type="primary" @click="submitSystemForm"> 确 定 </el-button>
-          <el-button @click="systemDialog.visible = false"> 取 消 </el-button>
-        </div>
-      </template>
-    </el-dialog>
-    <el-drawer v-model="rightDrawer" size="70%" class="customDrawer">
-      <template #header>
-        <div class="drawer-title">
-          <div class="title-name">物模型</div>
-          <el-descriptions style="margin-top: 10px" direction="vertical" title="" :column="4" border>
-            <el-descriptions-item label="设备名称">{{ curRow.name }}</el-descriptions-item>
-            <el-descriptions-item label="分项系统">{{ findNameWithParents(treeData, curRow.systemId, '') }}</el-descriptions-item>
-            <el-descriptions-item label="设备等级">{{ curRow.deviceLevel }}</el-descriptions-item>
-          </el-descriptions>
-        </div>
-      </template>
-      <el-tabs v-model="activeName" class="demo-tabs">
-        <el-tab-pane label="属性定义" name="dataPoints">
-          <el-button size="small" type="primary" @click="addModel">新增</el-button>
-          <el-table :data="modelData.dataPoints" style="margin-top: 10px">
-            <el-table-column label="属性" align="center" prop="name" />
-            <el-table-column label="属性值" align="center" prop="value" />
-            <el-table-column label="单位" align="center" prop="unit" />
-            <el-table-column label="描述" align="center" prop="remark" />
-            <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
-              <template #default="scope">
-                <el-button size="small" link type="primary" @click="editModel(scope.$index)">修改</el-button>
-                <el-button size="small" link type="danger" @click="delModel(scope.$index)">删除</el-button>
-              </template>
-            </el-table-column>
-          </el-table>
-        </el-tab-pane>
-        <el-tab-pane label="能力定义" name="controlFuncs">
-          <el-button size="small" type="primary" @click="addModel">新增</el-button>
-          <el-table :data="modelData.controlFuncs" style="margin-top: 10px">
-            <el-table-column label="能力名称" align="center" prop="name" />
-            <el-table-column label="下发参数" align="center" prop="value" />
-            <el-table-column label="描述" align="center" prop="remark" />
-            <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
-              <template #default="scope">
-                <el-button size="small" link type="primary" @click="editModel(scope.$index)">修改</el-button>
-                <el-button size="small" link type="danger" @click="delModel(scope.$index)">删除</el-button>
-              </template>
-            </el-table-column>
-          </el-table>
-        </el-tab-pane>
-        <el-tab-pane label="事件定义" name="events">
-          <el-button size="small" type="primary" @click="addModel">新增</el-button>
-          <el-table :data="modelData.events" style="margin-top: 10px">
-            <el-table-column label="事件名称" align="center" prop="name" />
-            <el-table-column label="事件类型" align="center" prop="type" />
-            <el-table-column label="事件代码" align="center" prop="code" />
-            <el-table-column label="外部事件代码" align="center" prop="outerCode" />
-            <el-table-column label="描述" align="center" prop="remark" />
-            <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
-              <template #default="scope">
-                <el-button size="small" link type="primary" @click="editModel(scope.$index)">修改</el-button>
-                <el-button size="small" link type="danger" @click="delModel(scope.$index)">删除</el-button>
-              </template>
-            </el-table-column>
-          </el-table>
-        </el-tab-pane>
-      </el-tabs>
-    </el-drawer>
-    <el-dialog v-model="modelDialog.visible" :title="modelDialog.title" width="500px" append-to-body>
-      <el-form v-if="activeName == 'dataPoints'" ref="modelFormRef" :model="modelData.dataPointsForm" label-width="120px">
-        <el-form-item label="属性名称" prop="name" :rules="[{ required: true, message: '请输入属性名称' }]">
-          <el-input v-model="modelData.dataPointsForm.name" placeholder="请输入属性名称" />
-        </el-form-item>
-        <el-form-item label="属性值" prop="value" :rules="[{ required: true, message: '请输入属性值' }]">
-          <el-input v-model="modelData.dataPointsForm.value" placeholder="请输入属性值" />
-        </el-form-item>
-        <el-form-item label="单位" prop="unit" :rules="[{ required: true, message: '请输入单位' }]">
-          <el-input v-model="modelData.dataPointsForm.unit" placeholder="请输入单位" />
-        </el-form-item>
-        <el-form-item label="描述" prop="remark">
-          <el-input v-model="modelData.dataPointsForm.remark" placeholder="请输入描述" />
-        </el-form-item>
-      </el-form>
-      <el-form v-else-if="activeName == 'controlFuncs'" ref="modelFormRef" :model="modelData.controlFuncsForm" label-width="120px">
-        <el-form-item label="能力名称" prop="name" :rules="[{ required: true, message: '请输入能力名称' }]">
-          <el-input v-model="modelData.controlFuncsForm.name" placeholder="请输入能力名称" />
-        </el-form-item>
-        <el-form-item label="下发参数" prop="value" :rules="[{ required: true, message: '请输入下发参数' }]">
-          <el-input v-model="modelData.controlFuncsForm.value" placeholder="请输入下发参数" />
-        </el-form-item>
-        <el-form-item label="描述" prop="remark">
-          <el-input v-model="modelData.controlFuncsForm.unit" placeholder="请输入描述" />
-        </el-form-item>
-      </el-form>
-      <el-form v-else-if="activeName == 'events'" ref="modelFormRef" :model="modelData.eventsForm" label-width="120px">
-        <el-form-item label="事件名称" prop="name" :rules="[{ required: true, message: '请输入事件名称' }]">
-          <el-input v-model="modelData.eventsForm.name" placeholder="请输入事件名称" />
-        </el-form-item>
-        <el-form-item label="事件类型" prop="type" :rules="[{ required: true, message: '请输入事件类型' }]">
-          <el-input v-model="modelData.eventsForm.type" placeholder="请输入事件类型" />
-        </el-form-item>
-        <el-form-item label="事件代码" prop="code" :rules="[{ required: true, message: '请输入事件代码' }]">
-          <el-input v-model="modelData.eventsForm.code" placeholder="请输入事件代码" />
-        </el-form-item>
-        <el-form-item label="外部事件代码" prop="outerCode" :rules="[{ required: true, message: '请输入外部事件代码' }]">
-          <el-input v-model="modelData.eventsForm.outerCode" placeholder="请输入外部事件代码" />
-        </el-form-item>
-        <el-form-item label="描述" prop="remark">
-          <el-input v-model="modelData.eventsForm.remark" placeholder="请输入描述" />
-        </el-form-item>
-      </el-form>
-      <template #footer>
-        <div class="dialog-footer">
-          <el-button :loading="buttonLoading" type="primary" @click="submitModelForm"> 确 定 </el-button>
-          <el-button @click="modelDialog.visible = false"> 取 消 </el-button>
-        </div>
-      </template>
-    </el-dialog>
-  </div>
+                <el-form-item label="分项系统" prop="systemId">
+                    <el-tree-select
+                        v-model="form.systemId"
+                        :props="defaultProps"
+                        node-key="id"
+                        :data="treeData"
+                        default-expand-all
+                        :check-strictly="true"
+                        :render-after-expand="false"
+                    />
+                </el-form-item>
+                <el-form-item label="备注" prop="remark">
+                    <el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
+                </el-form-item>
+            </el-form>
+            <template #footer>
+                <div class="dialog-footer">
+                    <el-button :loading="buttonLoading" type="primary" @click="submitForm"> 确 定 </el-button>
+                    <el-button @click="cancel"> 取 消 </el-button>
+                </div>
+            </template>
+        </el-dialog>
+        <el-dialog v-model="systemDialog.visible" :title="systemDialog.title" width="500px" append-to-body>
+            <el-form ref="deviceSystemFormRef" :model="systemForm" label-width="120px">
+                <el-form-item label="上级分项系统" prop="parentName" required>
+                    <el-input v-model="systemForm.parentName" disabled placeholder="请选择上级分项系统" />
+                </el-form-item>
+                <el-form-item label="系统名称" prop="name" required :rules="[{ required: true, message: '请输入系统名称' }]">
+                    <el-input v-model="systemForm.name" placeholder="请输入系统名称" />
+                </el-form-item>
+            </el-form>
+            <template #footer>
+                <div class="dialog-footer">
+                    <el-button :loading="buttonLoading" type="primary" @click="submitSystemForm"> 确 定 </el-button>
+                    <el-button @click="systemDialog.visible = false"> 取 消 </el-button>
+                </div>
+            </template>
+        </el-dialog>
+        <el-drawer v-model="rightDrawer" size="70%" class="customDrawer">
+            <template #header>
+                <div class="drawer-title">
+                    <div class="title-name">物模型</div>
+                    <el-descriptions style="margin-top: 10px" direction="vertical" title="" :column="4" border>
+                        <el-descriptions-item label="设备名称">{{ curRow.name }}</el-descriptions-item>
+                        <el-descriptions-item label="分项系统">{{ findNameWithParents(treeData, curRow.systemId, '') }}</el-descriptions-item>
+                        <el-descriptions-item label="设备等级">{{ curRow.deviceLevel }}</el-descriptions-item>
+                    </el-descriptions>
+                </div>
+            </template>
+            <el-tabs v-model="activeName" class="demo-tabs">
+                <el-tab-pane label="属性定义" name="dataPoints">
+                    <el-button size="small" type="primary" @click="addModel">新增</el-button>
+                    <el-table :data="modelData.dataPoints" style="margin-top: 10px">
+                        <el-table-column label="属性" align="center" prop="name" />
+                        <el-table-column label="属性值" align="center" prop="value" />
+                        <el-table-column label="单位" align="center" prop="unit" />
+                        <el-table-column label="描述" align="center" prop="remark" />
+                        <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+                            <template #default="scope">
+                                <el-button size="small" link type="primary" @click="editModel(scope.$index)">修改</el-button>
+                                <el-button size="small" link type="danger" @click="delModel(scope.$index)">删除</el-button>
+                            </template>
+                        </el-table-column>
+                    </el-table>
+                </el-tab-pane>
+                <el-tab-pane label="能力定义" name="controlFuncs">
+                    <el-button size="small" type="primary" @click="addModel">新增</el-button>
+                    <el-table :data="modelData.controlFuncs" style="margin-top: 10px">
+                        <el-table-column label="能力名称" align="center" prop="name" />
+                        <el-table-column label="下发参数" align="center" prop="value" />
+                        <el-table-column label="描述" align="center" prop="remark" />
+                        <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+                            <template #default="scope">
+                                <el-button size="small" link type="primary" @click="editModel(scope.$index)">修改</el-button>
+                                <el-button size="small" link type="danger" @click="delModel(scope.$index)">删除</el-button>
+                            </template>
+                        </el-table-column>
+                    </el-table>
+                </el-tab-pane>
+                <el-tab-pane label="事件定义" name="events">
+                    <el-button size="small" type="primary" @click="addModel">新增</el-button>
+                    <el-table :data="modelData.events" style="margin-top: 10px">
+                        <el-table-column label="事件名称" align="center" prop="name" />
+                        <el-table-column label="事件类型" align="center" prop="type" />
+                        <el-table-column label="事件代码" align="center" prop="code" />
+                        <el-table-column label="外部事件代码" align="center" prop="outerCode" />
+                        <el-table-column label="描述" align="center" prop="remark" />
+                        <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+                            <template #default="scope">
+                                <el-button size="small" link type="primary" @click="editModel(scope.$index)">修改</el-button>
+                                <el-button size="small" link type="danger" @click="delModel(scope.$index)">删除</el-button>
+                            </template>
+                        </el-table-column>
+                    </el-table>
+                </el-tab-pane>
+            </el-tabs>
+        </el-drawer>
+        <el-dialog v-model="modelDialog.visible" :title="modelDialog.title" width="500px" append-to-body>
+            <el-form v-if="activeName == 'dataPoints'" ref="modelFormRef" :model="modelData.dataPointsForm" label-width="120px">
+                <el-form-item label="属性名称" prop="name" :rules="[{ required: true, message: '请输入属性名称' }]">
+                    <el-input v-model="modelData.dataPointsForm.name" placeholder="请输入属性名称" />
+                </el-form-item>
+                <el-form-item label="属性值" prop="value" :rules="[{ required: true, message: '请输入属性值' }]">
+                    <el-input v-model="modelData.dataPointsForm.value" placeholder="请输入属性值" />
+                </el-form-item>
+                <el-form-item label="单位" prop="unit" :rules="[{ required: true, message: '请输入单位' }]">
+                    <el-input v-model="modelData.dataPointsForm.unit" placeholder="请输入单位" />
+                </el-form-item>
+                <el-form-item label="描述" prop="remark">
+                    <el-input v-model="modelData.dataPointsForm.remark" placeholder="请输入描述" />
+                </el-form-item>
+            </el-form>
+            <el-form v-else-if="activeName == 'controlFuncs'" ref="modelFormRef" :model="modelData.controlFuncsForm" label-width="120px">
+                <el-form-item label="能力名称" prop="name" :rules="[{ required: true, message: '请输入能力名称' }]">
+                    <el-input v-model="modelData.controlFuncsForm.name" placeholder="请输入能力名称" />
+                </el-form-item>
+                <el-form-item label="下发参数" prop="value" :rules="[{ required: true, message: '请输入下发参数' }]">
+                    <el-input v-model="modelData.controlFuncsForm.value" placeholder="请输入下发参数" />
+                </el-form-item>
+                <el-form-item label="描述" prop="remark">
+                    <el-input v-model="modelData.controlFuncsForm.unit" placeholder="请输入描述" />
+                </el-form-item>
+            </el-form>
+            <el-form v-else-if="activeName == 'events'" ref="modelFormRef" :model="modelData.eventsForm" label-width="120px">
+                <el-form-item label="事件名称" prop="name" :rules="[{ required: true, message: '请输入事件名称' }]">
+                    <el-input v-model="modelData.eventsForm.name" placeholder="请输入事件名称" />
+                </el-form-item>
+                <el-form-item label="事件类型" prop="type" :rules="[{ required: true, message: '请输入事件类型' }]">
+                    <el-input v-model="modelData.eventsForm.type" placeholder="请输入事件类型" />
+                </el-form-item>
+                <el-form-item label="事件代码" prop="code" :rules="[{ required: true, message: '请输入事件代码' }]">
+                    <el-input v-model="modelData.eventsForm.code" placeholder="请输入事件代码" />
+                </el-form-item>
+                <el-form-item label="外部事件代码" prop="outerCode" :rules="[{ required: true, message: '请输入外部事件代码' }]">
+                    <el-input v-model="modelData.eventsForm.outerCode" placeholder="请输入外部事件代码" />
+                </el-form-item>
+                <el-form-item label="描述" prop="remark">
+                    <el-input v-model="modelData.eventsForm.remark" placeholder="请输入描述" />
+                </el-form-item>
+            </el-form>
+            <template #footer>
+                <div class="dialog-footer">
+                    <el-button :loading="buttonLoading" type="primary" @click="submitModelForm"> 确 定 </el-button>
+                    <el-button @click="modelDialog.visible = false"> 取 消 </el-button>
+                </div>
+            </template>
+        </el-dialog>
+    </div>
 </template>
 
 <script setup name="DeviceType" lang="ts">
 import {
-  listDeviceType,
-  getDeviceType,
-  delDeviceType,
-  addDeviceType,
-  updateDeviceType,
-  getDeviceSystemTree,
-  addDeviceSystem,
-  editDeviceSystem,
-  delDeviceSystem,
-  addModels,
-  editModels,
-  getModels
+    listDeviceType,
+    getDeviceType,
+    delDeviceType,
+    addDeviceType,
+    updateDeviceType,
+    getDeviceSystemTree,
+    addDeviceSystem,
+    editDeviceSystem,
+    delDeviceSystem,
+    addModels,
+    editModels,
+    getModels
 } from '@/api/deviceManage/deviceType';
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 
@@ -306,387 +326,389 @@ const modelFormRef = ref<ElFormInstance>();
 const filterText = ref('');
 const treeRef = ref<ElTreeInstance>();
 const filterNode = (value: string, data) => {
-  if (!value) return true;
-  return data.name.includes(value);
+    if (!value) return true;
+    return data.name.includes(value);
 };
 watch(filterText, (val) => {
-  treeRef.value!.filter(val);
+    treeRef.value!.filter(val);
 });
 const defaultProps = {
-  children: 'children',
-  label: 'name'
+    children: 'children',
+    label: 'name'
 };
 const treeData = ref([]);
 const dialog = reactive<DialogOption>({
-  visible: false,
-  title: ''
+    visible: false,
+    title: ''
 });
 const systemDialog = reactive<DialogOption>({
-  visible: false,
-  title: ''
+    visible: false,
+    title: ''
 });
 const { device_level } = toRefs<any>(proxy?.useDict('device_level'));
 const rightDrawer = ref(false);
 const curRow = ref<any>({});
 const activeName = ref('dataPoints');
 const systemForm = reactive({
-  id: '',
-  name: '',
-  parentId: 0,
-  parentName: ''
+    id: '',
+    name: '',
+    parentId: 0,
+    parentName: ''
 });
 
 const modelData = reactive<any>({
-  id: '',
-  name: '',
-  dataPoints: [],
-  controlFuncs: [],
-  events: [],
-  dataPointsForm: {
-    name: '',
-    value: '',
-    unit: '',
-    remark: ''
-  },
-  controlFuncsForm: {
+    id: '',
     name: '',
-    value: '',
-    remark: ''
-  },
-  eventsForm: {
-    name: '',
-    type: '',
-    code: '',
-    outerCode: '',
-    remark: ''
-  }
+    dataPoints: [],
+    controlFuncs: [],
+    events: [],
+    dataPointsForm: {
+        name: '',
+        value: '',
+        unit: '',
+        remark: ''
+    },
+    controlFuncsForm: {
+        name: '',
+        value: '',
+        remark: ''
+    },
+    eventsForm: {
+        name: '',
+        type: '',
+        code: '',
+        outerCode: '',
+        remark: ''
+    }
 });
 const editIndex = ref(null);
 const modelDialog = reactive<DialogOption>({
-  visible: false,
-  title: ''
+    visible: false,
+    title: ''
 });
 const initFormData = {
-  id: undefined,
-  name: undefined,
-  deviceLevel: undefined,
-  systemId: null,
-  modeId: undefined,
-  remark: undefined,
-  ext1: undefined,
-  ext2: undefined
+    id: undefined,
+    name: undefined,
+    deviceLevel: undefined,
+    systemId: null,
+    modeId: undefined,
+    remark: undefined,
+    ext1: undefined,
+    ext2: undefined
 };
 const formData = reactive({
-  form: { ...initFormData },
-  queryParams: {
-    pageNum: 1,
-    pageSize: 10,
-    name: undefined,
-    systemId: undefined,
-    params: {}
-  },
-  rules: {
-    name: [{ required: true, message: '设备类型名称不能为空', trigger: 'blur' }],
-    deviceLevel: [{ required: true, message: '设备等级不能为空', trigger: 'change' }],
-    systemId: [{ required: true, message: '分项系统不能为空', trigger: 'change' }]
-  }
+    form: { ...initFormData },
+    queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        name: undefined,
+        systemId: undefined,
+        params: {}
+    },
+    rules: {
+        name: [{ required: true, message: '设备类型名称不能为空', trigger: 'blur' }],
+        deviceLevel: [{ required: true, message: '设备等级不能为空', trigger: 'change' }],
+        systemId: [{ required: true, message: '分项系统不能为空', trigger: 'change' }]
+    }
 });
 
 const { queryParams, form, rules } = toRefs(formData);
 
 /** 查询设备类型信息列表 */
 const getList = async () => {
-  loading.value = true;
-  const res = await listDeviceType(queryParams.value);
-  deviceTypeList.value = res.rows;
-  total.value = res.total;
-  loading.value = false;
+    loading.value = true;
+    const res = await listDeviceType(queryParams.value);
+    deviceTypeList.value = res.rows;
+    total.value = res.total;
+    loading.value = false;
 };
 
 /** 取消按钮 */
 const cancel = () => {
-  reset();
-  dialog.visible = false;
+    reset();
+    dialog.visible = false;
 };
 
 /** 表单重置 */
 const reset = () => {
-  form.value = { ...initFormData };
-  deviceTypeFormRef.value?.resetFields();
+    form.value = { ...initFormData };
+    deviceTypeFormRef.value?.resetFields();
 };
 
 /** 搜索按钮操作 */
 const handleQuery = () => {
-  queryParams.value.pageNum = 1;
-  getList();
+    queryParams.value.pageNum = 1;
+    getList();
 };
 
 /** 重置按钮操作 */
 const resetQuery = () => {
-  queryFormRef.value?.resetFields();
-  handleQuery();
+    queryFormRef.value?.resetFields();
+    handleQuery();
 };
 
 /** 多选框选中数据 */
 const handleSelectionChange = (selection) => {
-  ids.value = selection.map((item) => item.id);
-  single.value = selection.length != 1;
-  multiple.value = !selection.length;
+    ids.value = selection.map((item) => item.id);
+    single.value = selection.length != 1;
+    multiple.value = !selection.length;
 };
 
 /** 新增按钮操作 */
 const handleAdd = () => {
-  reset();
-  dialog.visible = true;
-  dialog.title = '新增设备类型信息';
+    reset();
+    dialog.visible = true;
+    dialog.title = '新增设备类型信息';
 };
 
 /** 修改按钮操作 */
 const handleUpdate = async (row) => {
-  reset();
-  const _id = row?.id || ids.value[0];
-  const res = await getDeviceType(_id);
-  Object.assign(form.value, res.data);
-  dialog.visible = true;
-  dialog.title = '修改设备类型信息';
+    reset();
+    const _id = row?.id || ids.value[0];
+    const res = await getDeviceType(_id);
+    Object.assign(form.value, res.data);
+    dialog.visible = true;
+    dialog.title = '修改设备类型信息';
 };
 
 /** 提交按钮 */
 const submitForm = () => {
-  deviceTypeFormRef.value?.validate(async (valid: boolean) => {
-    if (valid) {
-      buttonLoading.value = true;
-      if (form.value.id) {
-        await updateDeviceType(form.value).finally(() => (buttonLoading.value = false));
-      } else {
-        await addDeviceType(form.value).finally(() => (buttonLoading.value = false));
-      }
-      proxy?.$modal.msgSuccess('操作成功');
-      dialog.visible = false;
-      await getList();
-    }
-  });
+    deviceTypeFormRef.value?.validate(async (valid: boolean) => {
+        if (valid) {
+            buttonLoading.value = true;
+            if (form.value.id) {
+                await updateDeviceType(form.value).finally(() => (buttonLoading.value = false));
+            } else {
+                await addDeviceType(form.value).finally(() => (buttonLoading.value = false));
+            }
+            proxy?.$modal.msgSuccess('操作成功');
+            dialog.visible = false;
+            await getList();
+        }
+    });
 };
 
 /** 删除按钮操作 */
 const handleDelete = async (row) => {
-  const _ids = row?.id || ids.value;
-  await proxy?.$modal.confirm('是否确认删除设备类型信息编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
-  await delDeviceType(_ids);
-  proxy?.$modal.msgSuccess('删除成功');
-  await getList();
+    const _ids = row?.id || ids.value;
+    await proxy?.$modal.confirm('是否确认删除设备类型信息编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
+    await delDeviceType(_ids);
+    proxy?.$modal.msgSuccess('删除成功');
+    await getList();
 };
 
 const getTreeData = async () => {
-  getDeviceSystemTree({}).then(({ code, rows }) => {
-    if (code === 200) {
-      treeData.value = convertToTree(rows);
-      if (treeData.value.length === 0) {
-        treeData.value = [];
-      }
-    }
-  });
+    getDeviceSystemTree({}).then(({ code, rows }) => {
+        if (code === 200) {
+            treeData.value = convertToTree(rows);
+            if (treeData.value.length === 0) {
+                treeData.value = [];
+            }
+        }
+    });
 };
 const convertToTree = (list) => {
-  let map = new Map();
-  let root = [];
-  for (let item of list) {
-    map.set(item.id, { ...item });
-  }
-  for (let item of list) {
-    if (item.parentId !== 0) {
-      let parent = map.get(item.parentId);
-      if (!parent.children) {
-        parent.children = [];
-      }
-      parent.children.push(map.get(item.id));
-    } else {
-      root.push(map.get(item.id));
+    let map = new Map();
+    let root = [];
+    for (let item of list) {
+        map.set(item.id, { ...item });
     }
-  }
-  return root;
+    for (let item of list) {
+        if (item.parentId !== 0) {
+            let parent = map.get(item.parentId);
+            if (!parent.children) {
+                parent.children = [];
+            }
+            parent.children.push(map.get(item.id));
+        } else {
+            root.push(map.get(item.id));
+        }
+    }
+    return root;
 };
 const findNameWithParents = (data, idToFind, parentName = '') => {
-  for (let item of data) {
-    let fullName = parentName ? `${parentName}/${item.name}` : item.name;
-    if (item.id === idToFind) {
-      return fullName;
-    }
-    if (item.children && Array.isArray(item.children)) {
-      let result = findNameWithParents(item.children, idToFind, fullName);
-      if (result) {
-        return result;
-      }
+    for (let item of data) {
+        let fullName = parentName ? `${parentName}/${item.name}` : item.name;
+        if (item.id === idToFind) {
+            return fullName;
+        }
+        if (item.children && Array.isArray(item.children)) {
+            let result = findNameWithParents(item.children, idToFind, fullName);
+            if (result) {
+                return result;
+            }
+        }
     }
-  }
-  return null; // 如果没有找到对应的 ID,则返回 null
+    return null; // 如果没有找到对应的 ID,则返回 null
 };
 const addSystem = (data) => {
-  systemDialog.visible = true;
-  systemDialog.title = '新增分项系统';
-  deviceSystemFormRef.value?.resetFields();
-  Object.keys(systemForm).forEach((key) => {
-    systemForm[key] = '';
-  });
-  if (data.name) {
-    systemForm.parentName = data.name;
-    systemForm.parentId = data.id;
-  } else {
-    systemForm.parentName = '根节点';
-    systemForm.parentId = 0;
-  }
+    systemDialog.visible = true;
+    systemDialog.title = '新增分项系统';
+    deviceSystemFormRef.value?.resetFields();
+    Object.keys(systemForm).forEach((key) => {
+        systemForm[key] = '';
+    });
+    if (data.name) {
+        systemForm.parentName = data.name;
+        systemForm.parentId = data.id;
+    } else {
+        systemForm.parentName = '根节点';
+        systemForm.parentId = 0;
+    }
 };
 const editSystem = (node, data) => {
-  systemDialog.visible = true;
-  systemDialog.title = '修改分项系统';
-  deviceSystemFormRef.value?.resetFields();
-  Object.keys(systemForm).forEach((key) => {
-    systemForm[key] = data[key];
-  });
-  if (data.parentId === 0) {
-    systemForm.parentName = '根节点';
-  } else {
-    systemForm.parentName = node.parent.data.name;
-  }
+    systemDialog.visible = true;
+    systemDialog.title = '修改分项系统';
+    deviceSystemFormRef.value?.resetFields();
+    Object.keys(systemForm).forEach((key) => {
+        systemForm[key] = data[key];
+    });
+    if (data.parentId === 0) {
+        systemForm.parentName = '根节点';
+    } else {
+        systemForm.parentName = node.parent.data.name;
+    }
 };
 const delSystem = async (node, data) => {
-  if (node.childNodes.length) return proxy?.$modal.msgWarning('存在子节点,请先删除子节点');
-  await delDeviceSystem(data.id);
-  proxy?.$modal.msgSuccess('删除成功');
-  await getTreeData();
+    if (node.childNodes.length) return proxy?.$modal.msgWarning('存在子节点,请先删除子节点');
+    await delDeviceSystem(data.id);
+    proxy?.$modal.msgSuccess('删除成功');
+    await getTreeData();
 };
 const handleNodeClick = (data, node) => {
-  queryParams.value.systemId = data.id;
-  handleQuery();
+    queryParams.value.systemId = data.id;
+    handleQuery();
 };
 const submitSystemForm = () => {
-  deviceSystemFormRef.value?.validate(async (valid: boolean) => {
-    if (valid) {
-      buttonLoading.value = true;
-      if (systemForm.id) {
-        await editDeviceSystem(systemForm).finally(() => (buttonLoading.value = false));
-      } else {
-        await addDeviceSystem(systemForm).finally(() => (buttonLoading.value = false));
-      }
-      proxy?.$modal.msgSuccess('操作成功');
-      systemDialog.visible = false;
-      await getTreeData();
-    }
-  });
+    deviceSystemFormRef.value?.validate(async (valid: boolean) => {
+        if (valid) {
+            buttonLoading.value = true;
+            if (systemForm.id) {
+                await editDeviceSystem(systemForm).finally(() => (buttonLoading.value = false));
+            } else {
+                await addDeviceSystem(systemForm).finally(() => (buttonLoading.value = false));
+            }
+            proxy?.$modal.msgSuccess('操作成功');
+            systemDialog.visible = false;
+            await getTreeData();
+        }
+    });
 };
 const handleModel = (row) => {
-  rightDrawer.value = true;
-  curRow.value = row;
-  if (row.modeId) {
-    getModels(row.modeId).then(({ code, data }) => {
-      if (code === 200) {
-        modelData.id = data.id;
-        modelData.name = data.name;
-        modelData.dataPoints = JSON.parse(data.dataPoints || '[]');
-        modelData.controlFuncs = JSON.parse(data.controlFuncs || '[]');
-        modelData.events = JSON.parse(data.events || '[]');
-      }
-    });
-  }
+    rightDrawer.value = true;
+    curRow.value = row;
+    if (row.modeId) {
+        getModels(row.modeId).then(({ code, data }) => {
+            if (code === 200) {
+                modelData.id = data.id;
+                modelData.name = data.name;
+                modelData.dataPoints = JSON.parse(data.dataPoints || '[]');
+                modelData.controlFuncs = JSON.parse(data.controlFuncs || '[]');
+                modelData.events = JSON.parse(data.events || '[]');
+            }
+        });
+    }
 };
 const addModel = () => {
-  modelDialog.visible = true;
-  modelDialog.title = activeName.value === 'dataPoints' ? '新增属性' : activeName.value === 'controlFuncs' ? '新增能力' : '新增事件';
-  editIndex.value = null;
-  modelFormRef.value?.resetFields();
-  Object.keys(modelData[`${activeName.value}Form`]).forEach((key) => {
-    modelData[`${activeName.value}Form`][key] = '';
-  });
+    modelDialog.visible = true;
+    modelDialog.title = activeName.value === 'dataPoints' ? '新增属性' : activeName.value === 'controlFuncs' ? '新增能力' : '新增事件';
+    editIndex.value = null;
+    modelFormRef.value?.resetFields();
+    Object.keys(modelData[`${activeName.value}Form`]).forEach((key) => {
+        modelData[`${activeName.value}Form`][key] = '';
+    });
 };
 const editModel = (index) => {
-  modelDialog.visible = true;
-  modelDialog.title = activeName.value === 'dataPoints' ? '修改属性' : activeName.value === 'controlFuncs' ? '修改能力' : '修改事件';
-  editIndex.value = index;
-  modelFormRef.value?.resetFields();
-  Object.assign(modelData[`${activeName.value}Form`], modelData[`${activeName.value}`][index]);
+    modelDialog.visible = true;
+    modelDialog.title = activeName.value === 'dataPoints' ? '修改属性' : activeName.value === 'controlFuncs' ? '修改能力' : '修改事件';
+    editIndex.value = index;
+    modelFormRef.value?.resetFields();
+    Object.assign(modelData[`${activeName.value}Form`], modelData[`${activeName.value}`][index]);
 };
 const delModel = async (index) => {
-  modelData[activeName.value].splice(index, 1);
-  const params = {};
-  params[activeName.value] = JSON.stringify(modelData[activeName.value]);
-  await editModels({ id: modelData.id, ...params });
-  proxy?.$modal.msgSuccess('删除成功');
+    modelData[activeName.value].splice(index, 1);
+    const params = {};
+    params[activeName.value] = JSON.stringify(modelData[activeName.value]);
+    await editModels({ id: modelData.id, ...params });
+    proxy?.$modal.msgSuccess('删除成功');
 };
 const submitModelForm = () => {
-  modelFormRef.value?.validate(async (valid: boolean) => {
-    if (valid) {
-      if (editIndex.value !== null) {
-        modelData[activeName.value].splice(editIndex.value, 1, modelData[`${activeName.value}Form`]);
-      } else {
-        modelData[activeName.value].push(modelData[`${activeName.value}Form`]);
-      }
-      const params = { name: `${curRow.value.name}物模型` };
-      params[activeName.value] = JSON.stringify(modelData[activeName.value]);
-      buttonLoading.value = true;
-      if (modelData.id) {
-        await editModels({ id: modelData.id, ...params });
-        proxy?.$modal.msgSuccess('修改成功');
-      } else {
-        await addModels(params).then(({ code, data }) => {
-          if (code === 200) {
-            updateDeviceType({ id: curRow.value.id, modeId: data.id });
-          }
-        });
-        proxy?.$modal.msgSuccess('新增成功');
-      }
-      modelDialog.visible = false;
-      buttonLoading.value = false;
-    }
-  });
+    modelFormRef.value?.validate(async (valid: boolean) => {
+        if (valid) {
+            if (editIndex.value !== null) {
+                modelData[activeName.value].splice(editIndex.value, 1, modelData[`${activeName.value}Form`]);
+            } else {
+                modelData[activeName.value].push(modelData[`${activeName.value}Form`]);
+            }
+            const params = { name: `${curRow.value.name}物模型` };
+            params[activeName.value] = JSON.stringify(modelData[activeName.value]);
+            buttonLoading.value = true;
+            if (modelData.id) {
+                await editModels({ id: modelData.id, ...params });
+                proxy?.$modal.msgSuccess('修改成功');
+            } else {
+                await addModels(params).then(({ code, data }) => {
+                    if (code === 200) {
+                        updateDeviceType({ id: curRow.value.id, modeId: data.id });
+                    }
+                });
+                proxy?.$modal.msgSuccess('新增成功');
+            }
+            modelDialog.visible = false;
+            buttonLoading.value = false;
+        }
+    });
 };
 onMounted(() => {
-  getList();
-  getTreeData();
+    getList();
+    getTreeData();
 });
 </script>
 <style lang="scss" scoped>
 .card-header {
-  display: flex;
-  justify-content: space-between;
+    display: flex;
+    justify-content: space-between;
 
-  .el-icon {
-    cursor: pointer;
-  }
+    .el-icon {
+        cursor: pointer;
+    }
 }
 
 .custom-tree-node {
-  flex: 1;
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  padding-right: 5px;
+    flex: 1;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding-right: 5px;
 }
 
 .node-operateBtns {
-  div {
-    margin-top: 3px;
-    text-align: center;
-    cursor: pointer;
+    div {
+        margin-top: 3px;
+        text-align: center;
+        cursor: pointer;
 
-    &:hover {
-      color: #409eff;
+        &:hover {
+            color: #409eff;
+        }
     }
-  }
 }
 </style>
 <style lang="scss">
 .customDrawer {
-  .el-drawer__header {
-    align-items: flex-start !important;
-    margin-bottom: 10px;
-    .drawer-title {
-      .title-name {
-        color: #000;
-      }
+    .el-drawer__header {
+        align-items: flex-start !important;
+        margin-bottom: 10px;
+
+        .drawer-title {
+            .title-name {
+                color: #000;
+            }
+        }
+    }
+
+    .el-drawer__body {
+        padding-top: 0;
     }
-  }
-  .el-drawer__body {
-    padding-top: 0;
-  }
 }
 </style>

+ 0 - 21
src/views/index.vue

@@ -1,21 +0,0 @@
-<template>
-  <div class="home">
-    <component :is="comp"></component>
-  </div>
-</template>
-
-<script setup name="Index" lang="ts">
-import useUserStore from '@/store/modules/user';
-import DashbordAdmin from './dashbordAdmin.vue';
-import Dashbord from './dashbord.vue';
-console.log(useUserStore().roles);
-const comp = useUserStore().roles.includes('superadmin') ? DashbordAdmin : Dashbord;
-</script>
-
-<style scoped lang="scss">
-.home {
-  height: 100%;
-  padding: 10px 20px;
-  background: #fcfcfc;
-}
-</style>