forked from drone/command-center-dashboard

罗广辉
2025-03-30 f02122892ee506d6955eba4aac8ddee53a5194fc
feat: 整体交互调整
5 files renamed
12 files modified
9 files added
10 files deleted
3211 ■■■■ changed files
src/assets/images/home/footer/3D.png patch | view | raw | blame | history
src/assets/images/home/footer/3D1.png patch | view | raw | blame | history
src/assets/images/home/footer/event.png patch | view | raw | blame | history
src/assets/images/home/footer/event1.png patch | view | raw | blame | history
src/assets/images/home/footer/machine-nest.png patch | view | raw | blame | history
src/assets/images/home/footer/machine-nest1.png patch | view | raw | blame | history
src/assets/images/home/footer/orthophoto.png patch | view | raw | blame | history
src/assets/images/home/footer/orthophoto1.png patch | view | raw | blame | history
src/assets/images/home/footer/panorama.png patch | view | raw | blame | history
src/assets/images/home/footer/panorama1.png patch | view | raw | blame | history
src/directive/drag.js 42 ●●●●● patch | view | raw | blame | history
src/layout/Footer.vue 58 ●●●● patch | view | raw | blame | history
src/layout/Header.vue 1 ●●●● patch | view | raw | blame | history
src/layout/RSide.vue 46 ●●●●● patch | view | raw | blame | history
src/layout/index.vue 8 ●●●●● patch | view | raw | blame | history
src/mac/index.vue 388 ●●●●● patch | view | raw | blame | history
src/mac/lock.vue 62 ●●●●● patch | view | raw | blame | history
src/mac/login.scss 134 ●●●●● patch | view | raw | blame | history
src/mac/login.vue 72 ●●●●● patch | view | raw | blame | history
src/mac/mode/index.js 18 ●●●●● patch | view | raw | blame | history
src/mac/mode/index.vue 483 ●●●●● patch | view | raw | blame | history
src/router/ext/index.js 1 ●●●● patch | view | raw | blame | history
src/router/index.js 3 ●●●● patch | view | raw | blame | history
src/router/views/index.js 70 ●●●●● patch | view | raw | blame | history
src/store/getters.js 1 ●●●● patch | view | raw | blame | history
src/store/index.js 2 ●●●●● patch | view | raw | blame | history
src/store/modules/home.js 18 ●●●●● patch | view | raw | blame | history
src/utils/cesium-tsa.js 2 ●●● patch | view | raw | blame | history
src/views/Home/Home.vue 35 ●●●● patch | view | raw | blame | history
src/views/Home/components/HomeLeft/OverviewNext.vue 14 ●●●●● patch | view | raw | blame | history
src/views/Home/useAggregation.js 51 ●●●●● patch | view | raw | blame | history
src/views/System/Userinfo.vue 113 ●●●●● patch | view | raw | blame | history
src/views/Test.vue 19 ●●●●● patch | view | raw | blame | history
src/views/TestTemplate.vue 27 ●●●●● patch | view | raw | blame | history
src/views/wel/dashboard.vue 196 ●●●●● patch | view | raw | blame | history
src/views/wel/index.vue 1347 ●●●●● patch | view | raw | blame | history
src/assets/images/home/footer/3D.png

src/assets/images/home/footer/3D1.png
src/assets/images/home/footer/event.png

src/assets/images/home/footer/event1.png
src/assets/images/home/footer/machine-nest.png
src/assets/images/home/footer/machine-nest1.png

src/assets/images/home/footer/orthophoto.png

src/assets/images/home/footer/orthophoto1.png
src/assets/images/home/footer/panorama.png

src/assets/images/home/footer/panorama1.png
src/directive/drag.js
New file
@@ -0,0 +1,42 @@
const drag = {
  mounted(el, binding) {
    const dragBox = document.querySelector('#' + binding.arg)
    //鼠标点击距离box盒子left的距离
    let spotToBoxLeft = 0
    let spotToBoxTop = 0
    const mouseupFun = (e) => {
      window.removeEventListener('mousemove', mousemoveFun)
      window.removeEventListener('mouseup', mouseupFun)
    }
    const mousemoveFun = (evt) => {
      console.log(66666);
      //获取body宽高,获取拖动盒子的宽高
      const {offsetWidth: bodyWidth, offsetHeight: bodyHeight} = document.body
      const {offsetWidth: boxWidth, offsetHeight: boxHeight} = dragBox
      //max取最大值,min取最小值
      const left = Math.max(evt.clientX - spotToBoxLeft, 0)
      const top = Math.max(evt.clientY - spotToBoxTop, 0)
      dragBox.style.left = Math.min(left, bodyWidth - boxWidth) + 'px'
      dragBox.style.top = Math.min(top, bodyHeight - boxHeight) + 'px'
    }
    el.__mousedownFun = (evt) => {
      //鼠标坐标减去 box盒子相对于父盒子body 的距离
      spotToBoxLeft = evt.clientX - dragBox.offsetLeft
      spotToBoxTop = evt.clientY - dragBox.offsetTop
      window.addEventListener('mousemove', mousemoveFun)
      window.addEventListener('mouseup', mouseupFun)
    }
    el.addEventListener('mousedown', el.__mousedownFun);
  },
  // 绑定元素的父组件卸载前调用
  beforeUnmount(el, binding,) {
    el.removeEventListener('mousedown', el.__mousedownFun)
  },
}
export default drag
src/layout/Footer.vue
@@ -1,18 +1,53 @@
<template>
    <div class="footer">
      <img class="machine-nest" src="@/assets/images/machine-nest.png" alt="">
      <img class="event" src="@/assets/images/event.png" alt="">
      <img class="panorama" src="@/assets/images/panorama.png" alt="">
      <img class="threeD" src="@/assets/images/3D.png" alt="">
      <img class="orthophoto" src="@/assets/images/orthophoto.png" alt="">
    </div>
    <div class="intelligent-introduction-flying">
      <img class="orthophoto" src="@/assets/images/intelligent-introduction-flying.png" alt="">
    </div>
  <div class="footer">
    <img v-for="item in list" :class="item.className" :src="item.active ? item.activeImg : item.img" alt="" @click="imgClick(item)">
  </div>
  <div class="intelligent-introduction-flying">
    <img class="orthophoto" src="@/assets/images/intelligent-introduction-flying.png" alt="">
  </div>
</template>
<script setup>
import { useAggregation } from '@/views/Home/useAggregation';
import img1 from '@/assets/images/home/footer/machine-nest.png'
import activeImg1 from '@/assets/images/home/footer/machine-nest1.png'
import img2 from '@/assets/images/home/footer/event.png'
import activeImg2 from '@/assets/images/home/footer/event1.png'
import img3 from '@/assets/images/home/footer/panorama.png'
import activeImg3 from '@/assets/images/home/footer/panorama1.png'
import img4 from '@/assets/images/home/footer/3D.png'
import activeImg4 from '@/assets/images/home/footer/3D1.png'
import img5 from '@/assets/images/home/footer/orthophoto.png'
import activeImg5 from '@/assets/images/home/footer/orthophoto1.png'
const list = ref([
  {name:'event1',img:img1,activeImg:activeImg1,active:true,className:'machine-nest'},
  {name:'event2',img:img2,activeImg:activeImg2,active:false,className:'event'},
  {name:'event3',img:img3,activeImg:activeImg3,active:false,className:'panorama'},
  {name:'event4',img:img4,activeImg:activeImg4,active:false,className:'threeD'},
  {name:'event5',img:img5,activeImg:activeImg5,active:false,className:'orthophoto'},
])
const {init,removeAll} = useAggregation()
const imgClick = (row) => {
  const find = list.value.find(item => item.active)
  if (find.name === row.name) return
  list.value = list.value.map(item => ({...item,active: item.name === row.name}));
  row.name === 'event1' ? init() :removeAll()
}
// 销毁前钩子
onBeforeUnmount(() => {
  if (!window.$viewer) return
  removeAll();
});
onMounted(() => {
  nextTick(() => {
    init()
  });
});
</script>
<style scoped lang="scss">
@@ -23,6 +58,7 @@
    img {
      width: 103px;
      height: 119px;
      cursor: pointer;
    }
    .event {
      position: relative;
@@ -53,4 +89,4 @@
      height: 54px;
    }
  }
</style>
</style>
src/layout/Header.vue
@@ -60,7 +60,6 @@
  rightList.value.forEach(item => {
    item.active = item.path === path;
  });
  console.log(path);
  // 跳转到指定页面
  router.push(path);
};
src/layout/RSide.vue
@@ -1,6 +1,23 @@
<template>
  <div class="ai-chat">
    <img class="chat" src="@/assets/images/chat.png" alt="">
    <el-popover
      placement="bottom"
      :visible="visible"
      :width="200"
      trigger="click"
    >
      <template #reference>
        <div class="chat" id="chatID" v-drag:chatID  @mousedown="handleMouseDown"
             @mouseup="handleMouseUp"/>
      </template>
      <div>
        快和我对话吧
        <el-input/>
      </div>
    </el-popover>
    <img class="chat-bottom" src="@/assets/images/chat-bottom.png" alt="">
  </div>
  <div class="r-side">
@@ -14,10 +31,25 @@
</template>
<script setup>
import vDrag from '@/directive/drag'
let logIndex = ref(3);
const enterHover = (value) => {
  logIndex.value = value;
}
const visible = ref(false);
let pressStart = 0;
const handleMouseDown = () => {
  pressStart = Date.now(); // 记录按下时间
};
const handleMouseUp = () => {
  const pressDuration = Date.now() - pressStart; // 计算按下时长
  if (pressDuration < 150) {
    // 如果按下时间小于200ms,认为是点击
    visible.value = !visible.value;
  }
};
</script>
<style scoped lang="scss">
@@ -31,10 +63,16 @@
    height: 68px;
    display: block;
  };
  .chat {
    position: relative;
    bottom: -50px;
    position: fixed;
    bottom: 330px;
    height: 124px;
    width: 100px;
    user-select: none;
    z-index: 999;
    background: url('@/assets/images/chat.png') no-repeat center / 100% 100%;
  }
}
.r-side {
@@ -79,4 +117,4 @@
  bottom: 122px;
}
</style>
</style>
src/layout/index.vue
@@ -4,8 +4,6 @@
    <div class="page-index">
      <Header />
      <router-view></router-view>
      <RSide />
      <Footer />
    </div>
  </div>
</template>
@@ -14,19 +12,13 @@
import { onMounted } from 'vue';
import cesiumOperation from '@/utils/cesium-tsa';
import Header from './Header.vue';
import Footer from './Footer.vue';
import RSide from './RSide.vue';
const { _init } = cesiumOperation();
const narrowScreen  = ref(false)
const narrowScreenFun = () => {
  // 监听窗口变化,计算是不是 窄屏幕
  narrowScreen.value = window.innerWidth / window.innerHeight < 16 / 9;
};
onMounted(() => {
  _init('cesium');
  narrowScreenFun()
  window.addEventListener('resize', narrowScreenFun);
});
src/mac/index.vue
File was deleted
src/mac/lock.vue
File was deleted
src/mac/login.scss
File was deleted
src/mac/login.vue
File was deleted
src/mac/mode/index.js
File was deleted
src/mac/mode/index.vue
File was deleted
src/router/ext/index.js
File was deleted
src/router/index.js
@@ -1,5 +1,4 @@
import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router';
import ExtRouter from './ext/';
import PageRouter from './page/';
import ViewsRouter from './views/';
import AvueRouter from './avue-router';
@@ -9,7 +8,7 @@
const Router = createRouter({
  base: import.meta.env.VITE_APP_BASE,
  history: createWebHashHistory(import.meta.env.VITE_APP_BASE),
  routes: [...ExtRouter, ...PageRouter, ...ViewsRouter],
  routes: [...PageRouter, ...ViewsRouter],
});
AvueRouter.install({
  store: Store,
src/router/views/index.js
@@ -1,5 +1,6 @@
import Index from '@/layout/index.vue';
import Store from '@/store/';
import Layout from '@/page/index/layout.vue';
export default [
  {
@@ -33,77 +34,18 @@
        },
        component: () => import(/* webpackChunkName: "TaskManage" */ '@/views/TaskManage/TaskManage.vue'),
      },
      {
        path: 'dashboard',
        name: '控制台',
        meta: {
          i18n: 'dashboard',
          menu: false,
        },
        component: () => import(/* webpackChunkName: "views" */ '@/views/wel/dashboard.vue'),
      },
    ],
  },
  {
    path: '/test',
    component: Index,
    redirect: '/test/index',
    path: '/info',
    component: () => import('@/layout/index.vue'),
    redirect: '/info/index',
    children: [
      {
        path: 'index',
        name: '测试页',
        meta: {
          i18n: 'test',
        },
        component: () => import(/* webpackChunkName: "views" */ '@/views/Test.vue'),
        name: '个人信息',
        component: () => import('@/views/System/Userinfo.vue'),
      },
    ],
  },
  // {
  //   path: '/dict-horizontal',
  //   component: Layout,
  //   redirect: '/dict-horizontal/index',
  //   children: [
  //     {
  //       path: 'index',
  //       name: '字典管理',
  //       meta: {
  //         i18n: 'dict',
  //       },
  //       component: () =>
  //         import(/* webpackChunkName: "views" */ '@/views/util/demo/dict-horizontal.vue'),
  //     },
  //   ],
  // },
  // {
  //   path: '/dict-vertical',
  //   component: Layout,
  //   redirect: '/dict-vertical/index',
  //   children: [
  //     {
  //       path: 'index',
  //       name: '字典管理',
  //       meta: {
  //         i18n: 'dict',
  //       },
  //       component: () =>
  //         import(/* webpackChunkName: "views" */ '@/views/util/demo/dict-vertical.vue'),
  //     },
  //   ],
  // },
  // {
  //   path: '/info',
  //   component: Layout,
  //   redirect: '/info/index',
  //   children: [
  //     {
  //       path: 'index',
  //       name: '个人信息',
  //       meta: {
  //         i18n: 'info',
  //       },
  //       component: () => import(/* webpackChunkName: "views" */ '@/views/system/userinfo.vue'),
  //     },
  //   ],
  // },
];
src/store/getters.js
@@ -33,5 +33,6 @@
  logsLen: state => state.logs.logsList.length || 0,
  logsFlag: (state, getters) => getters.logsLen === 0,
  flowRoutes: state => state.dict.flowRoutes,
  singleUavHome: state => state.home.singleUavHome,
};
export default getters;
src/store/index.js
@@ -5,6 +5,7 @@
import logs from './modules/logs';
import dict from './modules/dict';
import getters from './getters';
import home from '@/store/modules/home';
const store = createStore({
  modules: {
@@ -13,6 +14,7 @@
    logs,
    tags,
    dict,
    home,
  },
  getters,
});
src/store/modules/home.js
New file
@@ -0,0 +1,18 @@
const home = {
  state: {
    singleUavHome: null,
  },
  actions: {},
  mutations: {
    setSingleUavHome: (state, data) => {
      state.singleUavHome = data;
    },
  },
  getters:{
    test(state) {
      return state.singleUavHome.id.toString() + '65'
    }
  }
};
export default home;
src/utils/cesium-tsa.js
@@ -219,7 +219,6 @@
        if (item.mapLayer) item.mapLayer.show = false;
      });
    console.log(store.state.common);
    // 高德影像地图数据图层
    if (store.state.common.mapSetting.mode === 3) {
@@ -991,6 +990,7 @@
  const viewerDestory = () => {
    viewer && viewer.destroy();
    viewer = null;
    window.$viewer = null
  };
  let tilesetArr = [];
src/views/Home/Home.vue
@@ -1,18 +1,37 @@
<template>
  <HomeLeft></HomeLeft>
  <!-- <div>中间搜索</div> -->
  <HomeRight></HomeRight>
  <SearchBox />
  <template v-if="!singleUavHome?.id">
    <SearchBox />
    <HomeLeft />
    <HomeRight />
  </template>
  <template v-else>
    <SignMachineNest />
  </template>
  <RSide />
  <Footer />
</template>
<script setup>
import HomeRight from './components/HomeRight/HomeRight.vue';
import HomeLeft from '@/views/Home/components/HomeLeft/HomeLeft.vue';
import SearchBox from '@/views/Home/SearchBox.vue';
import { useAggregation } from '@/views/Home/useAggregation';
import MapPopUpBox from '@/views/Home/MapPopUpBox.vue';
import { render } from 'vue';
import SignMachineNest from '@/views/SignMachineNest/SignMachineNest.vue';
import { useStore } from 'vuex';
import Footer from '@/layout/Footer.vue';
import RSide from '@/layout/RSide.vue';
import { onMounted } from 'vue';
import cesiumOperation from '@/utils/cesium-tsa';
const store = useStore();
const { _init,viewerDestory } = cesiumOperation();
const singleUavHome = computed(() => store.state.home.singleUavHome);
useAggregation()
onBeforeUnmount(()=>{
  store.commit('setSingleUavHome',null);
  viewerDestory()
})
onMounted(() => {
  _init('cesium');
});
</script>
src/views/Home/components/HomeLeft/OverviewNext.vue
@@ -38,12 +38,11 @@
  </div>
</template>
<script setup>
import { pxToRem } from '@/utils/rem';
import { Search } from '@element-plus/icons-vue';
import CommonTitle from '@/components/CommonTitle.vue';
import { getDeviceInfoNum } from '@/api/home/machineNest.js'
import router from '@/router/';
import { useStore } from 'vuex';
const searchText = ref('')
const list = ref([
  { name: '执行中', value: 89, color: '#FFA768' },
  { name: '在线', value: 100, color: '#8EFFAC' },
@@ -64,13 +63,12 @@
    console.log(res);
  });
}
const store = useStore();
// 单个机巢详情
const signMachineNestClick = () => {
  router.push({
    path: '/signMachineNest',
    query: {
      id: '123'  // 这里可以传递你需要的参数
    }
  store.commit('setSingleUavHome', {
    id: '123'
  });
}
src/views/Home/useAggregation.js
@@ -18,10 +18,10 @@
    { name: '国', value: [1169651, 37962800], gJson: zg },
  ];
  let viewer = null;
  const active = ref('');
  let active = null;
  let handler = null;
  let positionC3 = null;
  const init = () => {
  const listenerHeight = () => {
    determineScaling();
    viewer.camera.moveEnd.addEventListener(() => {
      determineScaling();
@@ -30,14 +30,15 @@
  // 确定缩放比例
  const determineScaling = () => {
    if(!viewer) return
    let height = viewer.camera.positionCartographic.height;
    // 根据高度展示对应的 gJson
    for (let item of scalingJudgment) {
      if (height > item.value[0] && height <= item.value[1]) {
        if (active.value !== item.name) {
        if (active !== item.name) {
          removeEntities();
          removeLabel();
          active.value = item.name;
          active = item.name;
          item.gJson ? aggregation(item) : splashed();
        }
        break;
@@ -126,6 +127,7 @@
    });
  };
  // 获取弹框box
  const getLabelDom = () => {
    const vNode = h(MapPopUpBox, { data: '参数', removeLabel });
    const tooltipContainer = document.createElement('div');
@@ -138,6 +140,7 @@
    return tooltipContainer;
  };
  // 弹框位置刷新
  const labelBox = () => {
    let dom = document.querySelector('#mapPopUpBox');
    if (!dom) {
@@ -151,7 +154,8 @@
    }
  };
  const clickFun = click => {
  // 左键单机事件
  const singleMachineEvent = click => {
    const pickedObject = viewer.scene.pick(click.position);
    if (Cesium.defined(pickedObject) && pickedObject.id) {
      const entity = pickedObject.id;
@@ -161,10 +165,12 @@
    }
  };
  // 移除 点 和 gjson 实体
  const removeEntities = () => {
    viewer.dataSources.removeAll(true);
    viewer.entities.removeAll();
    viewer.dataSources?.removeAll(true);
    viewer.entities?.removeAll();
  };
  // 移除弹框标签
  const removeLabel = () => {
    viewer.scene.postRender.removeEventListener(labelBox);
    const dom = document.querySelector('#mapPopUpBox');
@@ -173,19 +179,30 @@
    }
  };
  onBeforeUnmount(() => {
  const removeAll = () => {
    removeEntities();
    removeLabel();
    handler?.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
    handler?.destroy();
  });
    viewer = null;
    active = null
    handler = null;
    positionC3 = null;
  };
  const init = () => {
    viewer = window.$viewer;
    handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
    handler.setInputAction(singleMachineEvent, Cesium.ScreenSpaceEventType.LEFT_CLICK);
    listenerHeight();
  };
  // onMounted(() => {
  //   nextTick(() => {
  //     viewer = window.$viewer;
  //     handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
  //     handler.setInputAction(singleMachineEvent, Cesium.ScreenSpaceEventType.LEFT_CLICK);
  //     listenerHeight();
  //   });
  // });
  onMounted(() => {
    nextTick(() => {
      viewer = window.$viewer;
      handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
      handler.setInputAction(clickFun, Cesium.ScreenSpaceEventType.LEFT_CLICK);
      init();
    });
  });
  return { init,removeAll };
};
src/views/System/Userinfo.vue
New file
@@ -0,0 +1,113 @@
<template>
  <div>
    <basic-container>
      <avue-form
        :option="option"
        v-model="form"
        @tab-click="handleTabClick"
        @submit="handleSubmit"
      ></avue-form>
    </basic-container>
  </div>
</template>
<script>
import option from '@/option/user/info';
import { getUserInfo, updateInfo, updatePassword } from '@/api/system/user';
import md5 from 'js-md5';
import func from '@/utils/func';
import { validatenull } from '@/utils/validate';
import { sensitive } from '@/utils/sensitive';
export default {
  data() {
    return {
      index: 0,
      option: option,
      form: {},
      sensitiveManager: null,
    };
  },
  created() {
    this.handleWitch();
  },
  methods: {
    handleSubmit(form, done) {
      if (this.index === 0) {
        const submitData = this.sensitiveManager.getSubmitData(form);
        updateInfo(submitData).then(
          res => {
            if (res.data.success) {
              this.$message({
                type: 'success',
                message: '修改信息成功!',
              });
            } else {
              this.$message({
                type: 'error',
                message: res.data.msg,
              });
            }
            done();
          },
          error => {
            window.console.log(error);
            done();
          }
        );
      } else {
        updatePassword(md5(form.oldPassword), md5(form.newPassword), md5(form.newPassword1)).then(
          res => {
            if (res.data.success) {
              this.$message({
                type: 'success',
                message: '修改密码成功!',
              });
            } else {
              this.$message({
                type: 'error',
                message: res.data.msg,
              });
            }
            done();
          },
          error => {
            window.console.log(error);
            done();
          }
        );
      }
    },
    handleWitch() {
      // 创建脱敏工具实例
      this.sensitiveManager = sensitive.create({
        fields: ['phone', 'email'], // 配置需要脱敏的字段
      });
      if (this.index === 0) {
        getUserInfo().then(res => {
          const user = res.data.data;
          this.form = {
            id: user.id,
            avatar: user.avatar,
            name: user.name,
            realName: user.realName,
            phone: user.phone,
            email: user.email,
          };
          // 保存初始脱敏数据
          this.sensitiveManager.saveInitialData(this.form);
        });
      }
    },
    handleTabClick(tabs) {
      if (validatenull(tabs.index)) {
        return;
      }
      this.index = func.toInt(tabs.index, 0);
      this.handleWitch();
    },
  },
};
</script>
<style></style>
src/views/Test.vue
File was deleted
src/views/TestTemplate.vue
New file
@@ -0,0 +1,27 @@
<script setup>
import { useStore } from 'vuex';
const store = useStore();
// state必须带上.home
const singleUavHome = computed(() => store.state.home.singleUavHome);
// getters不需要带.home
const test = computed(() => store.getters.test);
onMounted(() => {
  // commit dispatch 不需要带.home
  store.commit('xxxx', '同步步修改啦');
  store.dispatch('xxxx', '异步修改啦');
});
watch(
  singleUavHome,
  (newValue, oldValue) => {
    console.log(newValue);
  },
  { immediate: true, deep: true }
);
</script>
<template></template>
<style scoped lang="scss"></style>
src/views/wel/dashboard.vue
File was deleted
src/views/wel/index.vue
File was deleted