liling 3 nedēļas atpakaļ
vecāks
revīzija
392fac064e

+ 2 - 2
.env.development

@@ -1,8 +1,8 @@
 # 页面标题
-VITE_APP_TITLE = 聚聚通管理系统
+VITE_APP_TITLE = 智眸.睿盾融合感知平台
 
 # 开发环境配置
 VITE_APP_ENV = 'development'
 
-# 聚聚通管理系统/开发环境
+# 智眸.睿盾融合感知平台/开发环境
 VITE_APP_BASE_API = '/dev-api'

+ 2 - 2
.env.production

@@ -1,10 +1,10 @@
 # 页面标题
-VITE_APP_TITLE = 聚聚通管理系统
+VITE_APP_TITLE = 智眸.睿盾融合感知平台
 
 # 生产环境配置
 VITE_APP_ENV = 'production'
 
-# 聚聚通管理系统/生产环境
+# 智眸.睿盾融合感知平台/生产环境
 VITE_APP_BASE_API = '/prod-api'
 
 # 是否在打包时开启压缩,支持 gzip 和 brotli

+ 2 - 2
.env.staging

@@ -1,10 +1,10 @@
 # 页面标题
-VITE_APP_TITLE = 聚聚通管理系统
+VITE_APP_TITLE = 智眸.睿盾融合感知平台
 
 # 生产环境配置
 VITE_APP_ENV = 'staging'
 
-# 聚聚通管理系统/生产环境
+# 系统/生产环境
 VITE_APP_BASE_API = '/stage-api'
 
 # 是否在打包时开启压缩,支持 gzip 和 brotli

+ 1 - 1
index.html

@@ -7,7 +7,7 @@
   <meta name="renderer" content="webkit">
   <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
   <link rel="icon" href="/favicon.ico">
-  <title>聚聚通管理系统</title>
+  <title></title>
   <!--[if lt IE 11]><script>window.location.href='/html/ie.html';</script><![endif]-->
   <style>
     html,

+ 3 - 2
package.json

@@ -1,8 +1,8 @@
 {
   "name": "ruoyi",
   "version": "3.8.9",
-  "description": "聚聚通管理系统",
-  "author": "聚聚通",
+  "description": "智眸.睿盾融合感知平台",
+  "author": "author",
   "license": "MIT",
   "type": "module",
   "scripts": {
@@ -16,6 +16,7 @@
     "url": "https://gitee.com/y_project/RuoYi-Vue.git"
   },
   "dependencies": {
+    "@amap/amap-jsapi-loader": "^1.0.1",
     "@element-plus/icons-vue": "2.3.1",
     "@vueup/vue-quill": "1.2.0",
     "@vueuse/core": "10.11.0",

BIN
public/favicon.ico


BIN
src/assets/logo/logo.png


+ 29 - 5
src/assets/styles/sidebar.scss

@@ -14,10 +14,10 @@
   .sidebar-container {
     transition: width 0.28s;
     width: $base-sidebar-width !important;
-    height: 100%;
+    height: calc(100% - 60px);
     position: fixed;
     font-size: 0px;
-    top: 0;
+    top: 60px;
     bottom: 0;
     left: 0;
     z-index: 1001;
@@ -44,7 +44,7 @@
 
     &.has-logo {
       .el-scrollbar {
-        height: calc(100% - 50px);
+        height: calc(100% - 60px);
       }
     }
 
@@ -72,10 +72,34 @@
       overflow: hidden !important;
       text-overflow: ellipsis !important;
       white-space: nowrap !important;
+      padding: 0 15px !important;
+      img{
+        width: 30px;
+        height: 30px;
+        opacity: 0.4;
+      }
+      img:hover{
+        opacity: 1;
+      }
+    }
+    .el-menu-item.is-active{
+      background-color: #455b6d;
+      img{
+        opacity: 1 !important;
+      }
     }
 
     .el-menu-item .el-menu-tooltip__trigger {
       display: inline-block !important;
+      padding: 0 15px !important;
+      img{
+        width: 30px;
+        height: 30px;
+        opacity: 0.4;
+      }
+      img:hover{
+        opacity: 1;
+      }
     }
 
     // menu hover
@@ -111,11 +135,11 @@
 
   .hideSidebar {
     .sidebar-container {
-      width: 54px !important;
+      width: 60px !important;
     }
 
     .main-container {
-      margin-left: 54px;
+      margin-left: 0px;
     }
 
     .sub-menu-title-noDropdown {

+ 4 - 4
src/layout/components/AppMain.vue

@@ -36,24 +36,24 @@ function addIframe() {
 <style lang="scss" scoped>
 .app-main {
   /* 50= navbar  50  */
-  min-height: calc(100vh - 50px);
+  min-height: calc(100vh - 60px);
   width: 100%;
   position: relative;
   overflow: hidden;
 }
 
 .fixed-header + .app-main {
-  padding-top: 50px;
+  padding-top: 60px;
 }
 
 .hasTagsView {
   .app-main {
     /* 84 = navbar + tags-view = 50 + 34 */
-    min-height: calc(100vh - 84px);
+    min-height: calc(100vh - 60px);
   }
 
   .fixed-header + .app-main {
-    padding-top: 84px;
+    padding-top: 60px;
   }
 }
 </style>

+ 9 - 8
src/layout/components/Navbar.vue

@@ -1,10 +1,9 @@
 <template>
   <div class="navbar">
-    <hamburger id="hamburger-container" :is-active="appStore.sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
-    <breadcrumb id="breadcrumb-container" class="breadcrumb-container" v-if="!settingsStore.topNav" />
+    <logo v-if="showLogo" :collapse="isCollapse" />
     <top-nav id="topmenu-container" class="topmenu-container" v-if="settingsStore.topNav" />
 
-    <div class="right-menu">
+    <div class="right-menu" >
       <template v-if="appStore.device !== 'mobile'">
         <header-search id="header-search" class="right-menu-item" />
 
@@ -55,6 +54,7 @@
 </template>
 
 <script setup>
+import Logo from '@/layout/components/Sidebar/Logo'
 import { ElMessageBox } from 'element-plus'
 import Breadcrumb from '@/components/Breadcrumb'
 import TopNav from '@/components/TopNav'
@@ -68,9 +68,10 @@ import useAppStore from '@/store/modules/app'
 import useUserStore from '@/store/modules/user'
 import useSettingsStore from '@/store/modules/settings'
 
+const settingsStore = useSettingsStore()
+const showLogo = computed(() => settingsStore.sidebarLogo);
 const appStore = useAppStore()
 const userStore = useUserStore()
-const settingsStore = useSettingsStore()
 
 function toggleSideBar() {
   appStore.toggleSideBar()
@@ -113,10 +114,10 @@ function toggleTheme() {
 
 <style lang='scss' scoped>
 .navbar {
-  height: 50px;
+  height: 60px;
   overflow: hidden;
   position: relative;
-  background: var(--navbar-bg);
+  background: #08121c;
   box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
 
   .hamburger-container {
@@ -138,7 +139,7 @@ function toggleTheme() {
 
   .topmenu-container {
     position: absolute;
-    left: 50px;
+    left: 0px;
   }
 
   .errLog-container {
@@ -149,7 +150,7 @@ function toggleTheme() {
   .right-menu {
     float: right;
     height: 100%;
-    line-height: 50px;
+    line-height: 60px;
     display: flex;
 
     &:focus {

+ 7 - 7
src/layout/components/Sidebar/Logo.vue

@@ -60,13 +60,13 @@ const getLogoTextColor = computed(() => {
 
 .sidebar-logo-container {
   position: relative;
-  width: 100%;
-  height: 50px;
-  line-height: 50px;
-  background: v-bind(getLogoBackground);
+  width: 260px;
+  height: 60px;
+  line-height: 60px;
+  background: #08121c;
   text-align: center;
   overflow: hidden;
-
+  float: left;
   & .sidebar-logo-link {
     height: 100%;
     width: 100%;
@@ -83,8 +83,8 @@ const getLogoTextColor = computed(() => {
       margin: 0;
       color: v-bind(getLogoTextColor);
       font-weight: 600;
-      line-height: 50px;
-      font-size: 14px;
+      line-height: 60px;
+      font-size: 18px;
       font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif;
       vertical-align: middle;
     }

+ 9 - 3
src/layout/components/Sidebar/SidebarItem.vue

@@ -2,8 +2,9 @@
   <div v-if="!item.hidden">
     <template v-if="hasOneShowingChild(item.children, item) && (!onlyOneChild.children || onlyOneChild.noShowingChildren) && !item.alwaysShow">
       <app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path, onlyOneChild.query)">
-        <el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{ 'submenu-title-noDropdown': !isNest }">
-          <svg-icon :icon-class="onlyOneChild.meta.icon || (item.meta && item.meta.icon)"/>
+        <el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{ 'submenu-title-noDropdown': !isNest }">          
+          <svg-icon v-if="item.meta!=null && item.meta.icon!=null" :icon-class="onlyOneChild.meta.icon || (item.meta && item.meta.icon)" />
+          <img v-else :src="getImageSrc(onlyOneChild.meta.png||item.meta.png)" />
           <template #title><span class="menu-title" :title="hasTitle(onlyOneChild.meta.title)">{{ onlyOneChild.meta.title }}</span></template>
         </el-menu-item>
       </app-link>
@@ -11,7 +12,8 @@
 
     <el-sub-menu v-else ref="subMenu" :index="resolvePath(item.path)" teleported>
       <template v-if="item.meta" #title>
-        <svg-icon :icon-class="item.meta && item.meta.icon" />
+        <svg-icon v-if="item.meta!=null && item.meta.icon!=null" :icon-class="item.meta && item.meta.icon" />
+        <img v-else :src="getImageSrc(item.meta.png)" />
         <span class="menu-title" :title="hasTitle(item.meta.title)">{{ item.meta.title }}</span>
       </template>
 
@@ -50,6 +52,10 @@ const props = defineProps({
 
 const onlyOneChild = ref({});
 
+function getImageSrc(png) {
+  return new URL('../../../../src/assets/images/'+png,import.meta.url).href;
+}
+
 function hasOneShowingChild(children = [], parent) {
   if (!children) {
     children = [];

+ 9 - 6
src/layout/components/Sidebar/index.vue

@@ -1,6 +1,5 @@
 <template>
   <div :class="{ 'has-logo': showLogo }" class="sidebar-container">
-    <logo v-if="showLogo" :collapse="isCollapse" />
     <el-scrollbar wrap-class="scrollbar-wrapper">
       <el-menu
         :default-active="activeMenu"
@@ -70,26 +69,30 @@ const activeMenu = computed(() => {
 
 <style lang="scss" scoped>
 .sidebar-container {
-  background-color: v-bind(getMenuBackground);
+  background-color: #0b1621;
   
   .scrollbar-wrapper {
-    background-color: v-bind(getMenuBackground);
+    background-color: #0b1621;
   }
 
   .el-menu {
     border: none;
     height: 100%;
     width: 100% !important;
-    
+    background-color:transparent !important;
     .el-menu-item, .el-sub-menu__title {
       &:hover {
-        background-color: var(--menu-hover, rgba(0, 0, 0, 0.06)) !important;
+        background-color: #0b1621 !important;
       }
     }
 
     .el-menu-item {
       color: v-bind(getMenuTextColor);
-      
+      padding: 0 15px !important;
+      img{
+        width: 30px;
+        height: 30px;
+      }
       &.is-active {
         color: var(--menu-active-text, #409eff);
         background-color: var(--menu-hover, rgba(0, 0, 0, 0.06)) !important;

+ 4 - 6
src/layout/index.vue

@@ -1,11 +1,9 @@
 <template>
-  <div :class="classObj" class="app-wrapper" :style="{ '--current-color': theme }">
-    <div v-if="device === 'mobile' && sidebar.opened" class="drawer-bg" @click="handleClickOutside"/>
-    <sidebar v-if="!sidebar.hide" class="sidebar-container" />
-    <div :class="{ hasTagsView: needTagsView, sidebarHide: sidebar.hide }" class="main-container">
+  <div :class="classObj" class="app-wrapper" :style="{ '--current-color': theme }">    
+    <div :class="{ hasTagsView: needTagsView, sidebarHide: true }" class="main-container hideSidebar">
+      <sidebar class="sidebar-container" />
       <div :class="{ 'fixed-header': fixedHeader }">
         <navbar @setLayout="setLayout" />
-        <tags-view v-if="needTagsView" />
       </div>
       <app-main />
       <settings ref="settingRef" />
@@ -101,7 +99,7 @@ function setLayout() {
 }
 
 .hideSidebar .fixed-header {
-  width: calc(100% - 54px);
+  width: calc(100% - 60px);
 }
 
 .sidebarHide .fixed-header {

+ 3 - 0
src/main.js

@@ -43,6 +43,9 @@ import ImagePreview from "@/components/ImagePreview"
 // 字典标签组件
 import DictTag from '@/components/DictTag'
 
+const title = import.meta.env.VITE_APP_TITLE;
+document.title = title;
+
 const app = createApp(App)
 
 // 全局方法挂载

+ 53 - 1
src/router/index.js

@@ -66,7 +66,59 @@ export const constantRoutes = [
         path: '/index',
         component: () => import('@/views/index'),
         name: 'Index',
-        meta: { title: '首页', icon: 'dashboard', affix: true }
+        meta: { title: '首页', png: 'ico_index.png', affix: true }
+      }
+    ]
+  },
+  {
+    path: '',
+    component: Layout,
+    redirect: '/camer',
+    children: [
+      {
+        path: '/camer',
+        component: () => import('@/views/camer/index'),
+        name: 'camer',
+        meta: { title: '设备', png: 'ico_camer.png', affix: true }
+      }
+    ]
+  },
+  {
+    path: '',
+    component: Layout,
+    redirect: '/device',
+    children: [
+      {
+        path: '/device',
+        component: () => import('@/views/device/index'),
+        name: 'device',
+        meta: { title: '感知', png: 'ico_device.png', affix: true }
+      }
+    ]
+  },
+  {
+    path: '',
+    component: Layout,
+    redirect: '/ai',
+    children: [
+      {
+        path: '/ai',
+        component: () => import('@/views/ai/index'),
+        name: 'device',
+        meta: { title: 'AI', png: 'ico_ai.png', affix: true }
+      }
+    ]
+  },
+  {
+    path: '',
+    component: Layout,
+    redirect: '/alarm',
+    children: [
+      {
+        path: '/alarm',
+        component: () => import('@/views/alarm/index'),
+        name: 'alarm',
+        meta: { title: '事件', png: 'ico_alarm.png', affix: true }
       }
     ]
   },

+ 3 - 3
src/settings.js

@@ -10,7 +10,7 @@ export default {
   /**
    * 是否系统布局配置
    */
-  showSettings: true,
+  showSettings: false,
 
   /**
    * 是否显示顶部导航
@@ -20,12 +20,12 @@ export default {
   /**
    * 是否显示 tagsView
    */
-  tagsView: true,
+  tagsView: false,
 
   /**
    * 是否固定头部
    */
-  fixedHeader: false,
+  fixedHeader: true,
 
   /**
    * 是否显示logo

+ 1 - 0
src/store/modules/user.js

@@ -49,6 +49,7 @@ const useUserStore = defineStore(
             this.id = user.userId
             this.name = user.userName
             this.avatar = avatar
+            sessionStorage.setItem("user",JSON.stringify(user))
             resolve(res)
           }).catch(error => {
             reject(error)

+ 218 - 0
src/views/index.vue

@@ -1,2 +1,220 @@
 <template>
+    <div id="containerMap" class="containerMap">
+      <div class="alarm_list">
+        <span class="alarm_list_item" v-for="item in alarm_list"><img :src="alarm_type[item.code].icon"/><span>{{ alarm_type[item.code].name }}</span><span class="alarm_item_brager" :style="{'background-color':alarm_type[item.code].bg}">{{ item.count }}</span></span>
+      </div>
+      <div class="houselist">
+        <span  class="item" v-for="item in houselist" :style="{'background-color':alarm_type[item.alarmtype].bg}" >
+          <el-tooltip :content="item.name" placement="top" class="custom-tooltip">
+            <span class="item">{{ item.code }}</span>
+          </el-tooltip>      
+        </span>
+      </div>
+    </div>
 </template>
+<script setup>
+import AMapLoader from '@amap/amap-jsapi-loader';
+import {shallowRef,onMounted} from "vue";
+
+const map = shallowRef(null);
+const AMap = ref();
+const alarm_type=ref({
+  "none":{
+    name:'行为合规',
+    bg:'#2488fe',
+    icon:'src/assets/images/ico_alarm_none.png'
+  },
+  "smoke":{
+    name:'吸烟告警',
+    bg:'#ef911c',
+    icon:'src/assets/images/ico_alarm_smoke.png'  
+  },
+  "fire":{
+    name:'异常烟火',
+    icon:'src/assets/images/ico_alarm_fire.png',
+    bg:'#de4a4a'
+  },
+  "temp":{
+    name:'环境异常',
+    bg:'#12c9b5',
+    icon:'src/assets/images/ico_alarm_temp.png'
+  },
+  "person":{
+    name:'人员入侵',
+    icon:'src/assets/images/ico_alarm_person.png',
+    bg:'#833dd9'
+  }
+})
+const alarm_list = ref([{
+  code:"none", 
+  count:0
+},{
+  code:"smoke",
+  count:0
+},{
+  code:"fire",
+  count:0
+},{
+  code:"temp",
+  count:0
+},{
+  code:"person",
+  count:0
+}]);
+
+const houselist=ref([{
+  name:"自贡中心仓库",
+  code:"zg",
+  city:"510300",
+  addr:"自贡市沿滩区金川路东段28号高新仓储物流园一期",
+  lnglat:[0,0],  
+  alarmtype:"none",
+},{
+  name:"成都中心仓库",
+  code:"cd",
+  city:"510100",
+  addr:"简阳市石桥镇羊羊小镇大华国际B1栋13楼1322号",
+  lnglat:[0,0],  
+  alarmtype:"smoke",
+}]);
+
+const initHouseLoca=()=>{
+  for (let index = 0; index < houselist.value.length; index++) {
+    const element = houselist.value[index];
+      let geocoder = AMap.value.Geocoder({
+      city: "全国" // 城市范围,默认全国
+      });
+
+      let address = element.addr;
+      geocoder.getLocation(address, function(status, result) {
+        if (status === 'complete' && result.geocodes.length) {
+        var lng = result.geocodes[0].location.getLng();
+        var lat = result.geocodes[0].location.getLat();
+        console.log('地址解析结果的经纬度:', lng, lat);
+        } else {
+        console.error('地址解析失败');
+        }
+      });
+  }
+}
+
+const initWindow=()=>{
+// 信息窗体
+let infoWindow = new AMap.value.InfoWindow({
+offset: new AMap.value.Pixel(0,-10),
+retainwhenClose: true,
+});
+}
+
+const initMap = () => {
+  AMapLoader.load({
+    key: 'cf0ede1d8833ccb51fb6b084e23a7b5e', // 申请好的Web端开发者Key,首次调用 load 时必填
+    version: '2.0', // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
+    plugins: ['AMap.Scale', 'AMap.ToolBar','AMap.Geocoder'], // 需要使用的的插件列表,如比例尺'AMap.Scale'等
+    Loca:{
+      version:'2.0.0'
+    }
+  })
+    .then((res) => {   
+      AMap.value = res
+      // 上来就显示的中心点  北京 116.397, 39.918
+      var lnglat = new res.LngLat(116.397, 39.918);
+      map.value = new res.Map('containerMap', {
+        //设置地图容器id
+        viewMode: '2D', //是否为3D地图模式
+        zoom: 15, //初始化地图级别
+        mapStyle:"amap://styles/dark",//深色模式
+        center: lnglat, //初始化地图中心点位置
+      })
+      map.value.clearMap() // 清除地图覆盖物
+      // 地图是否可拖拽和缩放
+      map.value.setStatus({
+        dragEnable: true, // 是否可拖拽
+        zoomEnable: true, // 是否可缩放
+      })
+      initHouseLoca();
+      initWindow()
+      
+    })
+    .catch((e) => {
+      console.log('error', e)
+    })
+}
+onMounted(() => {
+  const userinfo = JSON.parse(sessionStorage.getItem("user")||'{}');
+  if (userinfo.userName!=null) {
+    let tmplst =[];
+    for (let index = 0; index <  houselist.value.length; index++) {
+      const element =  houselist.value[index];
+      if(userinfo.userName=='admin'|| element.code==userinfo.userName){
+        tmplst.push(element)
+      }
+    }
+    houselist.value = tmplst;
+  }
+  initMap()
+})
+</script>
+<style scoped>
+.custom-tooltip .el-tooltip__popper {
+  background-color: #484e58 !important; /* 修改为你的颜色 */
+}
+</style>
+<style lang='scss' scoped>
+.containerMap{
+    width: 100%;
+    height: calc(100vh - 60px);
+    position: relative;
+    .alarm_list{
+      position: absolute;
+      z-index: 10;
+      display: flex;
+      left: 75px;
+      top: 40px;
+      .alarm_list_item{
+        position: relative;
+        margin: 0 5px;
+        padding: 6px 20px;
+        border-radius: 30px;
+        color: #fff;
+        background-color: #455b6d;     
+        img{
+          width: 24px;
+          height: 24px;
+          vertical-align: middle;
+        }   
+        .alarm_item_brager{
+          position: absolute;
+          font-weight: bold;
+          width: 24px;
+          height: 24px;
+          border-radius: 50%;
+          text-align: center;
+          line-height: 24px;
+          top: -13px;
+          right: 0;
+          font-size: 12px;
+        }
+      }
+    }
+    .houselist{
+      position: absolute;
+      bottom: 20px;
+      left: 75px;
+      display: flex;
+      z-index: 10;
+      color: #fff;
+      .item{
+        cursor: pointer;
+        border-radius: 3px;
+        margin: 3px;
+        padding:5px 5px;
+        text-align: center;
+        font-weight: bold;
+        .item:hover{
+          padding:20px 5px;
+        }
+      }
+    }
+}
+</style>

+ 1 - 1
src/views/login.vue

@@ -59,7 +59,7 @@
     </el-form>
     <!--  底部  -->
     <div class="el-login-footer">
-      <span>Copyright © 2018-2025 ruoyi.vip All Rights Reserved.</span>
+      
     </div>
   </div>
 </template>

+ 4 - 3
src/views/register.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="register">
     <el-form ref="registerRef" :model="registerForm" :rules="registerRules" class="register-form">
-      <h3 class="title">聚聚通后台管理系统</h3>
+      <h3 class="title">{{ title }}</h3>
       <el-form-item prop="username">
         <el-input
           v-model="registerForm.username"
@@ -69,8 +69,7 @@
       </el-form-item>
     </el-form>
     <!--  底部  -->
-    <div class="el-register-footer">
-      <span>Copyright © 2018-2025 ruoyi.vip All Rights Reserved.</span>
+    <div class="el-register-footer">      
     </div>
   </div>
 </template>
@@ -82,6 +81,8 @@ import { getCodeImg, register } from "@/api/login";
 const router = useRouter();
 const { proxy } = getCurrentInstance();
 
+const title = import.meta.env.VITE_APP_TITLE;
+
 const registerForm = ref({
   username: "",
   password: "",

+ 2 - 2
vite.config.js

@@ -25,13 +25,13 @@ export default defineConfig(({ mode, command }) => {
     },
     // vite 相关配置
     server: {
-      port: 80,
+      port: 1880,
       host: true,
       open: true,
       proxy: {
         // https://cn.vitejs.dev/config/#server-proxy
         '/dev-api': {
-          target: 'http://localhost:8080',
+          target: 'http://47.109.90.136:8080',
           changeOrigin: true,
           rewrite: (p) => p.replace(/^\/dev-api/, '')
         }