Просмотр исходного кода

[Mod 0000] 调整系统header样式与组件封装

wangkeyi 6 дней назад
Родитель
Сommit
3544596ff9

+ 2 - 2
src/components/AIChat/MiniChat.vue

@@ -1068,8 +1068,8 @@
 
 <style lang="less" scoped>
   .btn-header {
-    width: 40px;
-    height: 40px;
+    width: 30px;
+    height: 30px;
     margin-right: 5px;
     margin-bottom: 5px;
   }

+ 252 - 252
src/layouts/default/header/components/VoiceBroadcast.vue

@@ -6,8 +6,8 @@
       <a href="#" class="head-example"></a>
     </a-badge> -->
       <a-badge :dot="isWarningDot" style="display: flex; flex-direction: row">
-        <BellOutlined style="font-size: 22px; color: #fff; margin-right: 10px" />
-        <WarningOutlined style="font-size: 22px; color: #fff" />
+        <BellOutlined style="font-size: 22px; color: #fff; padding-left: 0" />
+        <WarningOutlined style="font-size: 22px; color: #fff; padding-right: 0" />
       </a-badge>
     </div>
     <div v-if="isShowWarningBroad" class="broadcast" ref="VoiceBroadcastRef" id="VoiceBroadcast">
@@ -47,297 +47,297 @@
   </div>
 </template>
 <script lang="ts">
-import { Tooltip, Badge } from 'ant-design-vue';
-import { SoundOutlined, ClearOutlined, BellOutlined, WarningOutlined, CloseOutlined } from '@ant-design/icons-vue';
-import Icon from '/@/components/Icon';
-import { defineComponent, ref, unref, onMounted, nextTick } from 'vue';
-import { defHttp } from '/@/utils/http/axios';
-import { useRouter } from 'vue-router';
-import { connectWebSocket, onWebSocket } from '/@/hooks/web/useWebSocket';
-import { getToken } from '/@/utils/auth';
-import { useUserStore } from '/@/store/modules/user';
-import { useGlobSetting } from '/@/hooks/setting';
-import SpeakVoice from './notify/speakVoice';
-import { useDrag } from '@/hooks/event/useDrag';
-import dayjs from 'dayjs';
-export default defineComponent({
-  name: 'VoiceBroadcast',
-  components: { Icon, Tooltip, Badge, SoundOutlined, ClearOutlined, BellOutlined, WarningOutlined, CloseOutlined },
+  import { Tooltip, Badge } from 'ant-design-vue';
+  import { SoundOutlined, ClearOutlined, BellOutlined, WarningOutlined, CloseOutlined } from '@ant-design/icons-vue';
+  import Icon from '/@/components/Icon';
+  import { defineComponent, ref, unref, onMounted, nextTick } from 'vue';
+  import { defHttp } from '/@/utils/http/axios';
+  import { useRouter } from 'vue-router';
+  import { connectWebSocket, onWebSocket } from '/@/hooks/web/useWebSocket';
+  import { getToken } from '/@/utils/auth';
+  import { useUserStore } from '/@/store/modules/user';
+  import { useGlobSetting } from '/@/hooks/setting';
+  import SpeakVoice from './notify/speakVoice';
+  import { useDrag } from '@/hooks/event/useDrag';
+  import dayjs from 'dayjs';
+  export default defineComponent({
+    name: 'VoiceBroadcast',
+    components: { Icon, Tooltip, Badge, SoundOutlined, ClearOutlined, BellOutlined, WarningOutlined, CloseOutlined },
 
-  setup() {
-    // debugger;
-    let speakVoice;
-    const userStore = useUserStore();
-    const glob = useGlobSetting();
-    const router = useRouter();
-    const list = (params) => defHttp.get({ url: '/safety/ventanalyAlarmLog/list', params });
-    const cleanAlarm = () => defHttp.get({ url: '/safety/ventanalyAlarmLog/cleanAlarmLog' });
-    const activeKey = ref(0);
-    const isShowWarningBroad = ref(false);
-    const isBroad = ref(true);
-    const isWarningDot = ref(false);
-    const broadcastList = ref([]);
-    function showWarningBroad() {
-      isShowWarningBroad.value = !isShowWarningBroad.value;
-      if (isShowWarningBroad.value) {
-        toSelectList(0);
-        nextTick(() => {
-          const dom = document.getElementById('VoiceBroadcast');
-          if (dom) useDrag(dom);
-        });
+    setup() {
+      // debugger;
+      let speakVoice;
+      const userStore = useUserStore();
+      const glob = useGlobSetting();
+      const router = useRouter();
+      const list = (params) => defHttp.get({ url: '/safety/ventanalyAlarmLog/list', params });
+      const cleanAlarm = () => defHttp.get({ url: '/safety/ventanalyAlarmLog/cleanAlarmLog' });
+      const activeKey = ref(0);
+      const isShowWarningBroad = ref(false);
+      const isBroad = ref(true);
+      const isWarningDot = ref(false);
+      const broadcastList = ref([]);
+      function showWarningBroad() {
+        isShowWarningBroad.value = !isShowWarningBroad.value;
+        if (isShowWarningBroad.value) {
+          toSelectList(0);
+          nextTick(() => {
+            const dom = document.getElementById('VoiceBroadcast');
+            if (dom) useDrag(dom);
+          });
+        }
+      }
+      function handleBroad() {
+        isBroad.value = !isBroad.value;
+      }
+      //点击关闭预警信息弹窗
+      function closeWarningBroad() {
+        isShowWarningBroad.value = false;
       }
-    }
-    function handleBroad() {
-      isBroad.value = !isBroad.value;
-    }
-    //点击关闭预警信息弹窗
-    function closeWarningBroad() {
-      isShowWarningBroad.value = false;
-    }
 
-    async function clearInfo() {
-      await cleanAlarm();
-      const res = await list({ pageSize: 20, devicetype: '', sort: 'createTime', delFlag: 0, nwartype_end: '999' });
-      broadcastList.value = res['records'];
-    }
+      async function clearInfo() {
+        await cleanAlarm();
+        const res = await list({ pageSize: 20, devicetype: '', sort: 'createTime', delFlag: 0, nwartype_end: '999' });
+        broadcastList.value = res['records'];
+      }
 
-    async function toSelectList(key) {
-      activeKey.value = key;
-      const res = await list({
-        pageSize: 20,
-        devicetype: '',
-        isok: key == 1 ? 0 : key == 2 ? 1 : null,
-        sort: 'createTime',
-        delFlag: 0,
-        nwartype_end: '999',
-      });
-      broadcastList.value = res['records'];
-      const isHasWarning = broadcastList.value.findIndex((item) => !item['isok']);
-      if (key !== 2) isWarningDot.value = isHasWarning > -1 ? true : false;
-    }
+      async function toSelectList(key) {
+        activeKey.value = key;
+        const res = await list({
+          pageSize: 20,
+          devicetype: '',
+          isok: key == 1 ? 0 : key == 2 ? 1 : null,
+          sort: 'createTime',
+          delFlag: 0,
+          nwartype_end: '999',
+        });
+        broadcastList.value = res['records'];
+        const isHasWarning = broadcastList.value.findIndex((item) => !item['isok']);
+        if (key !== 2) isWarningDot.value = isHasWarning > -1 ? true : false;
+      }
 
-    async function toMore() {
-      await router.push({ path: '/monitorChannel/device-monitor/warningHistory/home' });
-      showWarningBroad();
-    }
+      async function toMore() {
+        await router.push({ path: '/monitorChannel/device-monitor/warningHistory/home' });
+        showWarningBroad();
+      }
 
-    // 初始化 WebSocket
-    function initWebSocket() {
-      let token = getToken();
-      //将登录token生成一个短的标识
-      // let wsClientId = md5(token);
-      // let userId = unref(userStore.getUserInfo).id + '_' + wsClientId;
-      let userId = unref(userStore.getUserInfo).id + '?token=' + token;
-      console.log(userStore.getUserInfo, 'userId---');
-      // WebSocket与普通的请求所用协议有所不同,ws等同于http,wss等同于https
-      // let url = glob.wsUrl?.replace('https://', 'wss://').replace('http://', 'ws://') + '/websocket/' + userId;
-      let url = 'http://' + window.location.hostname + ':9999'?.replace('https://', 'wss://').replace('http://', 'ws://') + '/websocket/' + userId;
-      console.log(url, 'url------');
-      connectWebSocket(url);
-      onWebSocket(onWebSocketMessage);
-    }
+      // 初始化 WebSocket
+      function initWebSocket() {
+        let token = getToken();
+        //将登录token生成一个短的标识
+        // let wsClientId = md5(token);
+        // let userId = unref(userStore.getUserInfo).id + '_' + wsClientId;
+        let userId = unref(userStore.getUserInfo).id + '?token=' + token;
+        console.log(userStore.getUserInfo, 'userId---');
+        // WebSocket与普通的请求所用协议有所不同,ws等同于http,wss等同于https
+        // let url = glob.wsUrl?.replace('https://', 'wss://').replace('http://', 'ws://') + '/websocket/' + userId;
+        let url = 'http://' + window.location.hostname + ':9999'?.replace('https://', 'wss://').replace('http://', 'ws://') + '/websocket/' + userId;
+        console.log(url, 'url------');
+        connectWebSocket(url);
+        onWebSocket(onWebSocketMessage);
+      }
 
-    async function onWebSocketMessage(data) {
-      console.log('WebSocket 监测消息--------------》', data);
-      if (data.topic === 'warn' || data.cmd === 'user') {
-        if (isBroad.value) {
-          await speakVoice.getSpeechCnVoices();
-          const messageText = data['warndata'];
-          speakVoice.handleReply(messageText);
-          const time = dayjs().format('YYYY-MM-DD HH:mm:ss');
-          console.log(time + '语音播报开始报警------>', data);
-        }
-        if (!isShowWarningBroad.value) {
-          isWarningDot.value = true;
-        } else {
-          isWarningDot.value = false;
-        }
-        setTimeout(() => {
-          if (isShowWarningBroad.value) {
-            toSelectList(0);
+      async function onWebSocketMessage(data) {
+        console.log('WebSocket 监测消息--------------》', data);
+        if (data.topic === 'warn' || data.cmd === 'user') {
+          if (isBroad.value) {
+            await speakVoice.getSpeechCnVoices();
+            const messageText = data['warndata'];
+            speakVoice.handleReply(messageText);
+            const time = dayjs().format('YYYY-MM-DD HH:mm:ss');
+            console.log(time + '语音播报开始报警------>', data);
+          }
+          if (!isShowWarningBroad.value) {
+            isWarningDot.value = true;
+          } else {
+            isWarningDot.value = false;
           }
-        }, 0);
+          setTimeout(() => {
+            if (isShowWarningBroad.value) {
+              toSelectList(0);
+            }
+          }, 0);
+        }
       }
-    }
-    onMounted(() => {
-      speakVoice = new SpeakVoice();
-      nextTick(async () => {
-        initWebSocket();
-        await toSelectList(1);
+      onMounted(() => {
+        speakVoice = new SpeakVoice();
+        nextTick(async () => {
+          initWebSocket();
+          await toSelectList(1);
+        });
+        window.speechSynthesis.onvoiceschanged = () => {
+          console.log('语音列表已更新');
+        };
       });
-      window.speechSynthesis.onvoiceschanged = () => {
-        console.log('语音列表已更新');
-      };
-    });
 
-    return {
-      showWarningBroad,
-      isShowWarningBroad,
-      activeKey,
-      toSelectList,
-      broadcastList,
-      toMore,
-      isBroad,
-      handleBroad,
-      clearInfo,
-      closeWarningBroad,
-      isWarningDot,
-    };
-  },
-});
+      return {
+        showWarningBroad,
+        isShowWarningBroad,
+        activeKey,
+        toSelectList,
+        broadcastList,
+        toMore,
+        isBroad,
+        handleBroad,
+        clearInfo,
+        closeWarningBroad,
+        isWarningDot,
+      };
+    },
+  });
 </script>
 <style lang="less" scoped>
-.btn {
-  line-height: 30px;
-  margin-right: 10px;
-  cursor: pointer;
-  display: flex;
-}
-
-.no-play {
-  position: relative;
-  &::after {
-    position: absolute;
-    width: 70%;
-    height: 100%;
-    content: '';
-    left: 15%;
-    top: 0;
-    background: linear-gradient(
-      to bottom left,
-      transparent 0%,
-      transparent calc(50% - 1px),
-      #ffffff 50%,
-      transparent calc(50% + 1px),
-      transparent 100%
-    );
+  .btn {
+    line-height: 30px;
+    margin-right: 10px;
+    cursor: pointer;
+    display: flex;
   }
-}
-
-.broadcast {
-  width: 500px;
-  height: 350px;
-  border-radius: 4px;
-  position: fixed;
-  top: 57px;
-  right: 2px;
-  background-color: rgb(255, 255, 255);
-  background: url('../../../../assets/images/warn-dialog-bg.png') no-repeat center;
-  background-size: 100% 100%;
-  z-index: 9999999;
-  color: #fff;
 
-  .title {
-    height: 32px;
-    padding: 0 20px;
-
-    :deep(.ant-badge:not(.ant-badge-status)) {
-      margin-right: 40px !important;
+  .no-play {
+    position: relative;
+    &::after {
+      position: absolute;
+      width: 70%;
+      height: 100%;
+      content: '';
+      left: 15%;
+      top: 0;
+      background: linear-gradient(
+        to bottom left,
+        transparent 0%,
+        transparent calc(50% - 1px),
+        #ffffff 50%,
+        transparent calc(50% + 1px),
+        transparent 100%
+      );
     }
+  }
 
-    display: flex;
-    align-items: center;
-    justify-content: space-between;
-    margin-bottom: 5px;
+  .broadcast {
+    width: 500px;
+    height: 350px;
+    border-radius: 4px;
+    position: fixed;
+    top: 57px;
+    right: 2px;
+    background-color: rgb(255, 255, 255);
+    background: url('../../../../assets/images/warn-dialog-bg.png') no-repeat center;
+    background-size: 100% 100%;
+    z-index: 9999999;
+    color: #fff;
 
-    .message-title {
-      font-size: 18px;
-      padding-top: 10px;
-    }
+    .title {
+      height: 32px;
+      padding: 0 20px;
 
-    .badge-box {
-      display: flex;
-      align-items: center;
-      padding-top: 10px;
-      .badge-title {
-        display: inline-block;
-        width: 62px;
-        line-height: 32px;
-        background-color: #2174f0;
-        border-radius: 26px;
-        text-align: center;
-        color: #fff;
-        padding-bottom: 2px;
+      :deep(.ant-badge:not(.ant-badge-status)) {
+        margin-right: 40px !important;
       }
-    }
-  }
 
-  .broadcast-context {
-    .context-tab {
       display: flex;
-      padding: 20px;
-      .context-tab-item {
-        line-height: 24px;
-        background-color: #6b6b6b;
-        border-radius: 24px;
-        text-align: center;
-        padding: 0 10px;
-        color: #fff;
-        margin: 5px;
-        cursor: pointer;
-        font-size: 14px;
+      align-items: center;
+      justify-content: space-between;
+      margin-bottom: 5px;
+
+      .message-title {
+        font-size: 18px;
+        padding-top: 10px;
       }
 
-      .context-tab-item-active {
-        background-color: #2174f0;
+      .badge-box {
+        display: flex;
+        align-items: center;
+        padding-top: 10px;
+        .badge-title {
+          display: inline-block;
+          width: 62px;
+          line-height: 32px;
+          background-color: #2174f0;
+          border-radius: 26px;
+          text-align: center;
+          color: #fff;
+          padding-bottom: 2px;
+        }
       }
     }
 
-    .context-box {
-      flex: 1;
-      padding: 0 10px;
-      height: 200px;
-      overflow-y: auto;
-
-      .no-context {
+    .broadcast-context {
+      .context-tab {
         display: flex;
-        justify-content: center;
-        padding-top: 30px;
-        font-size: 16px;
+        padding: 20px;
+        .context-tab-item {
+          line-height: 24px;
+          background-color: #6b6b6b;
+          border-radius: 24px;
+          text-align: center;
+          padding: 0 10px;
+          color: #fff;
+          margin: 5px;
+          cursor: pointer;
+          font-size: 14px;
+        }
+
+        .context-tab-item-active {
+          background-color: #2174f0;
+        }
       }
 
-      .context-detail {
-        display: flex;
-        justify-content: space-between;
-        line-height: 24px;
-        padding: 0px 16px;
+      .context-box {
+        flex: 1;
+        padding: 0 10px;
+        height: 200px;
+        overflow-y: auto;
 
-        div {
+        .no-context {
           display: flex;
-          justify-content: flex-start;
+          justify-content: center;
+          padding-top: 30px;
+          font-size: 16px;
+        }
 
-          &:nth-child(1) {
-            width: 44%;
-          }
+        .context-detail {
+          display: flex;
+          justify-content: space-between;
+          line-height: 24px;
+          padding: 0px 16px;
 
-          &:nth-child(2) {
-            width: 40%;
-          }
+          div {
+            display: flex;
+            justify-content: flex-start;
 
-          &:nth-child(3) {
-            width: 25%;
-          }
+            &:nth-child(1) {
+              width: 44%;
+            }
 
-          &:nth-child(4) {
-            width: 15%;
+            &:nth-child(2) {
+              width: 40%;
+            }
+
+            &:nth-child(3) {
+              width: 25%;
+            }
+
+            &:nth-child(4) {
+              width: 15%;
+            }
           }
         }
       }
-    }
-    .more {
-      position: absolute;
-      left: 24px;
-      bottom: 10px;
-      cursor: pointer;
+      .more {
+        position: absolute;
+        left: 24px;
+        bottom: 10px;
+        cursor: pointer;
 
-      &:hover {
-        color: #2174f0;
+        &:hover {
+          color: #2174f0;
+        }
       }
     }
   }
-}
-:deep(.zxm-badge-count) {
-  width: 8px;
-  height: 8px;
-}
+  :deep(.zxm-badge-count) {
+    width: 8px;
+    height: 8px;
+  }
 </style>

+ 128 - 0
src/layouts/default/header/components/rightPositionBar.vue

@@ -0,0 +1,128 @@
+<template>
+  <!-- 布局头部右侧操作栏:布局 header 与 AI 首页共用,后续修改只改此一处 -->
+  <div :class="`${prefixCls}-action`" :style="{ position: 'fixed', top: top + 'px', right: '20px', zIndex: 999999 }">
+    <div class="right-position">
+      <div v-if="hasPermission('show:modalChange')" class="mode-switch" @click="changeModalType">
+        <div v-if="programModalType == true"> <span class="icon">⚡</span>快速模式</div>
+        <div v-if="programModalType == false"> <span class="icon">✨</span>特效模式</div>
+      </div>
+      <WeatherBroadcast v-if="sysOrgCode != 'sdmtjtgsd' && isShowQy && portValue != '8062' && hasPermission('show:weather')" />
+      <VoiceBroadcast v-if="sysOrgCode != 'sdmtjtgsd' && portValue != '8062'" />
+      <div v-if="hasPermission('show:AIChat')" class="ai-chat-wrap">
+        <AIChat />
+      </div>
+      <div v-if="hasPermission('show:AIDiAssistant')" class="ai-picker">
+        <img
+          :src="dataPickerHoverEnabled ? AIOpenIcon : AICloseIcon"
+          :title="dataPickerHoverEnabled ? '关闭AI解读' : '开启AI解读'"
+          @click="toggleDataPickerHover"
+        />
+      </div>
+      <VoiceBroadcastGsd v-if="sysOrgCode == 'sdmtjtgsd'" />
+      <div v-if="showUserDropdown" class="user-dropdown-wrap">
+        <UserDropDown :theme="getHeaderTheme" />
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { computed, ref } from 'vue';
+  import { useDesign } from '/@/hooks/web/useDesign';
+  import { useGlobSetting } from '/@/hooks/setting';
+  import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
+  import { useAppStore } from '/@/store/modules/app';
+  import { usePermission } from '/@/hooks/web/usePermission';
+  import { dataPickerHoverEnabled, setDataPickerHoverEnabled } from '/@/views/ventAI/dataPicker';
+  import WeatherBroadcast from './weatherBroadcast.vue';
+  import VoiceBroadcast from './VoiceBroadcast.vue';
+  import VoiceBroadcastGsd from './VoiceBroadcastGsd.vue';
+  import AIChat from '/@/components/AIChat/MiniChat.vue';
+  import UserDropDown from './user-dropdown/index.vue';
+  import AIOpenIcon from '/@/assets/images/ventAI/dataPicker/AI-open.svg';
+  import AICloseIcon from '/@/assets/images/ventAI/dataPicker/AI-close.svg';
+
+  interface Props {
+    // 距顶部偏移(px),默认与其它页面 header 布局一致
+    top?: number;
+  }
+  withDefaults(defineProps<Props>(), {
+    top: 25,
+  });
+
+  const { prefixCls } = useDesign('layout-header');
+  const appStore = useAppStore();
+  const { hasPermission } = usePermission();
+  const { sysOrgCode } = useGlobSetting();
+  const { getHeaderTheme } = useHeaderSetting();
+
+  const programModalType = ref(appStore.getProgramModalType);
+  const portValue = ref('');
+  portValue.value = window.location.port;
+  const isShowQy = VENT_PARAM['isShowQy'];
+  const showUserDropdown = computed(() => window.self === window.top);
+
+  const changeModalType = () => {
+    programModalType.value = !Boolean(programModalType.value);
+    appStore.setModalType(programModalType.value);
+    window.location.reload();
+  };
+
+  const toggleDataPickerHover = () => {
+    setDataPickerHoverEnabled(!dataPickerHoverEnabled.value);
+  };
+</script>
+
+<style lang="less" scoped>
+  .right-position {
+    display: flex;
+    align-items: center;
+    height: 48px;
+
+    // 统一各按钮间距:子项左右各 8px,相邻间距 16px
+    > * {
+      margin: 0 8px !important;
+      padding: 0 !important;
+    }
+
+    // 首尾子项外侧清零,避免操作栏两端出现空白、贴不齐右侧边缘
+    > *:first-child {
+      margin-left: 0 !important;
+    }
+
+    > *:last-child {
+      margin-right: 0 !important;
+    }
+
+    // 子组件内部按钮自带的外边距/高度不一致,统一重置为撑满 48px 并垂直居中
+    :deep(.btn),
+    :deep(.btn-header) {
+      margin: 0;
+      padding: 0;
+      height: 48px;
+      line-height: 48px;
+      display: flex;
+      align-items: center;
+    }
+
+    // UserDropDown 是多根节点组件,包一层容器隔离其弹窗根节点;
+    // 其根节点自带左右 padding,重置后与其它按钮间距保持一致
+    .user-dropdown-wrap {
+      :deep(.jeecg-header-user-dropdown) {
+        padding: 0 16px 0 0;
+      }
+    }
+
+    .mode-switch {
+      color: #fff;
+      cursor: pointer;
+    }
+
+    .ai-picker {
+      width: 24px;
+      height: 24px;
+      cursor: pointer;
+      user-select: none;
+    }
+  }
+</style>

+ 1 - 1
src/layouts/default/header/components/weatherBroadcast.vue

@@ -8,7 +8,7 @@
       <div style="display: flex; flex-direction: row;align-items: center;" class="btn-header">
         <img :src="parseWeatherData(weatherObj.text)" class="weather-icon" />
         <span class="unit">{{ weatherObj.pressure }}&nbsp;hPa</span>
-        <FileSearchOutlined style="font-size: 20px; color: #fff;" />
+        <FileSearchOutlined style="font-size: 20px; color: #fff; padding-right: 0;" />
       </div>
     </div>
     <div v-if="isShowWeatherBroad" class="broadcast" ref="VoiceBroadcastRef" id="VoiceBroadcast">

+ 6 - 77
src/layouts/default/header/index.vue

@@ -55,33 +55,8 @@
       <LoginSelect ref="loginSelectRef" @success="loginSelectOk" /> -->
     </div>
   </Header>
-  <div :class="`${prefixCls}-action`" style="position: fixed; top: 25px; right: 20px; z-index: 999999">
-    <div class="right-position">
-      <!-- 公司端不显示语音播报功能 weatherBroadcast.vue-->
-      <!-- <a-radio-group v-if="hasPermission('show:modalChange')" v-model:value="programModalType" @change="changeModalType" style="display: flex">
-        <a-radio :value="true">快速模式</a-radio>
-        <a-radio :value="false">特效模式</a-radio>
-      </a-radio-group> -->
-
-      <div v-if="hasPermission('show:modalChange')" @click="changeModalType" style="width: 92px; color: #fff; cursor: pointer">
-        <div v-if="programModalType == true"> <span class="icon">⚡</span>快速模式</div>
-        <div v-if="programModalType == false"> <span class="icon">✨</span>特效模式</div>
-      </div>
-      <WeatherBroadcast v-if="sysOrgCode != 'sdmtjtgsd' && isShowQy && portValue != '8062' && hasPermission('show:weather')" />
-      <VoiceBroadcast v-if="sysOrgCode != 'sdmtjtgsd' && portValue != '8062'" />
-      <AIChat v-if="hasPermission('show:AIChat')" />
-      <div v-if="hasPermission('show:AIDiAssistant')" style="width: 24px; height: 24px; cursor: pointer; user-select: none; margin: 0 3px">
-        <img
-          :src="dataPickerHoverEnabled ? AIOpenIcon : AICloseIcon"
-          :title="dataPickerHoverEnabled ? '关闭AI解读' : '开启AI解读'"
-          @click="toggleDataPickerHover"
-        />
-      </div>
-      <VoiceBroadcastGsd v-if="sysOrgCode == 'sdmtjtgsd'" />
-      <UserDropDown v-if="showUserDropdown" :theme="getHeaderTheme" />
-      <LoginSelect ref="loginSelectRef" @success="loginSelectOk" />
-    </div>
-  </div>
+  <RightPositionBar />
+  <LoginSelect ref="loginSelectRef" @success="loginSelectOk" />
 </template>
 <script lang="ts">
   import { defineComponent, unref, computed, ref, onMounted, toRaw } from 'vue';
@@ -103,7 +78,7 @@
   import { SettingButtonPositionEnum } from '/@/enums/appEnum';
   import { AppLocalePicker } from '/@/components/Application';
 
-  import { UserDropDown, LayoutBreadcrumb, FullScreen, Notify, ErrorAction, LockScreen } from './components';
+  import { LayoutBreadcrumb, FullScreen, Notify, ErrorAction, LockScreen } from './components';
   import { useAppInject } from '/@/hooks/web/useAppInject';
   import { useDesign } from '/@/hooks/web/useDesign';
 
@@ -111,22 +86,14 @@
   import { useLocale } from '/@/locales/useLocale';
   import { Persistent } from '/@/utils/cache/persistent';
 
-  import WeatherBroadcast from './components/weatherBroadcast.vue';
-  import VoiceBroadcast from './components/VoiceBroadcast.vue';
-  import VoiceBroadcastGsd from './components/VoiceBroadcastGsd.vue';
-  import AIChat from '/@/components/AIChat/MiniChat.vue';
+  import RightPositionBar from './components/rightPositionBar.vue';
 
   import LoginSelect from '/@/views/sys/login/LoginSelect.vue';
   import { useUserStore } from '/@/store/modules/user';
   import { useRouter } from 'vue-router';
 
   import { noHeadeLink } from '../layout.data';
-  import { usePermission } from '/@/hooks/web/usePermission';
-  import { useAppStore } from '/@/store/modules/app';
   import { router } from '/@/router';
-  import { dataPickerHoverEnabled, setDataPickerHoverEnabled } from '/@/views/ventAI/dataPicker';
-  import AIOpenIcon from '/@/assets/images/ventAI/dataPicker/AI-open.svg';
-  import AICloseIcon from '/@/assets/images/ventAI/dataPicker/AI-close.svg';
 
   export default defineComponent({
     name: 'LayoutHeader',
@@ -136,7 +103,7 @@
       LayoutTrigger,
       LayoutBreadcrumb,
       LayoutMenu,
-      UserDropDown,
+      RightPositionBar,
       AppLocalePicker,
       FullScreen,
       Notify,
@@ -144,10 +111,6 @@
       ErrorAction,
       LockScreen,
       LoginSelect,
-      VoiceBroadcast,
-      AIChat,
-      VoiceBroadcastGsd,
-      WeatherBroadcast,
       SettingDrawer: createAsyncComponent(() => import('/@/layouts/default/setting/index.vue'), {
         loading: true,
       }),
@@ -160,11 +123,9 @@
       const userStore = useUserStore();
       const { currentRoute } = useRouter();
       console.log(currentRoute);
-      const { hasPermission } = usePermission();
-      const isShowQy = VENT_PARAM['isShowQy'];
       const { getShowTopMenu, getShowHeaderTrigger, getSplit, getIsMixMode, getMenuWidth, getIsMixSidebar } = useMenuSetting();
       const { getUseErrorHandle, getShowSettingButton, getSettingButtonPosition } = useRootSetting();
-      const { title, sysOrgCode, homePath } = useGlobSetting();
+      const { title, homePath } = useGlobSetting();
       const {
         getHeaderTheme,
         getShowFullScreen,
@@ -228,15 +189,10 @@
         return unref(getSplit) ? MenuModeEnum.HORIZONTAL : null;
       });
 
-      const appStore = useAppStore();
-      const programModalType = ref(appStore.getProgramModalType);
-
       // /**
       //  * 首页多租户部门弹窗逻辑
       //  */
       const loginSelectRef = ref();
-      const portValue = ref('');
-      portValue.value = window.location.port;
       function showLoginSelect() {
         //update-begin---author:liusq  Date:20220101  for:判断登录进来是否需要弹窗选择租户----
         //判断是否是登陆进来
@@ -251,28 +207,11 @@
         console.log('成功。。。。。');
       }
 
-      // 用户下拉框应该在以下情况中隐藏:
-      // 1. 本页面是由其他页面的 iframe 嵌入的页面
-      const showUserDropdown = computed(() => {
-        return window.self === window.top;
-      });
-
-      const changeModalType = () => {
-        programModalType.value = !Boolean(programModalType.value);
-        appStore.setModalType(programModalType.value);
-        window.location.reload();
-      };
-
-      const toggleDataPickerHover = () => {
-        setDataPickerHoverEnabled(!dataPickerHoverEnabled.value);
-      };
-
       onMounted(() => {
         showLoginSelect();
       });
 
       return {
-        portValue,
         prefixCls,
         getHeaderClass,
         getShowHeaderLogo,
@@ -302,17 +241,7 @@
         title,
         getShowFullHeader,
         noHeadeLink,
-        showUserDropdown,
-        sysOrgCode,
         homePath,
-        hasPermission,
-        isShowQy,
-        programModalType,
-        changeModalType,
-        dataPickerHoverEnabled,
-        toggleDataPickerHover,
-        AIOpenIcon,
-        AICloseIcon,
       };
     },
   });