8 커밋 b848fad8f6 ... ac0c3d9520

작성자 SHA1 메시지 날짜
  wangcong ac0c3d9520 feat(views): 优化“实时监测”页面的设备配置对话框 1 일 전
  wangshun 333235d66a perf(views): 优化"用户管理"模块添加主题色异常问题 1 일 전
  wangshun c1dc4e2c45 perf(views): 优化页面主题色异常问题 1 일 전
  wangshun ee0ccb7113 perf(views): 添加无权限页面 1 일 전
  wangshun 0eb7c48883 perf(views): 优化主题色显示异常问题 1 일 전
  wangshun 0a0467d30a perf(views): 优化“角色管理”操作权限显示异常问题 1 일 전
  wangshun 8fba423774 perf(views): 优化“登陆页”后跳转接口查询支持名称与图片地址 1 일 전
  wangshun f33fdfd560 perf(views): 新增“暂无权限”页面 1 일 전

+ 6 - 0
src/api/index.ts

@@ -82,6 +82,7 @@ import type {
   ListInfo,
   ListInterfaces,
   ListPhysicalInterfaces,
+  LoginData,
   loginForm,
   LoginUser,
   MonitoringForm,
@@ -389,6 +390,11 @@ export const addRevisePassword = async (params: PasswordParams) => {
   });
 };
 
+export const getLoginData = async () => {
+  const data = await request<LoginData>(apiSys('/sysUser/loginData'));
+  return data;
+};
+
 // 内部用户表
 export const getOrgUsers = async () => {
   const data = await request<UserPageItem[]>(apiSys('/inner/sysUser/orgUsers'));

BIN
src/assets/img/not-allow.png


+ 65 - 1
src/components/BitSwitchConfirm.vue

@@ -2,12 +2,16 @@
 import { computed } from 'vue';
 import { Modal } from 'ant-design-vue';
 
+import { DeviceType, ValveType } from '@/views/device-work-status/device-card';
 import { t } from '@/i18n';
 
 import type { DevStartStopStatus, GroupModuleDevParam } from '@/types';
 
 interface Props {
   info: GroupModuleDevParam;
+  deviceName?: string;
+  deviceType?: number;
+  valveType?: number | string;
   startStopStatus?: DevStartStopStatus;
 }
 
@@ -35,9 +39,11 @@ const disableStartStopTip = computed(() => {
 
 const handleSwitchClick = (checked: string) => {
   const value = checked === props.info.bitAddress0Detail ? props.info.bitAddress0Status : props.info.bitAddress1Status;
+  const { title, content } = getConfirmText(checked);
 
   Modal.confirm({
-    title: t('realTimeMonitor.confirmSwitchToMode', { mode: checked }),
+    title,
+    content,
     closable: true,
     centered: true,
     onOk() {
@@ -46,6 +52,64 @@ const handleSwitchClick = (checked: string) => {
     },
   });
 };
+
+const getConfirmText = (checked: string) => {
+  if (!isStartStopParam.value) {
+    return {
+      title: t('realTimeMonitor.confirmSwitchToMode', { mode: checked }),
+    };
+  }
+
+  const isStart = checked === props.info.bitAddress1Detail;
+  const title = isStart
+    ? t('realTimeMonitor.confirmStart', { name: props.deviceName })
+    : t('realTimeMonitor.confirmStop', { name: props.deviceName });
+  const [startText, stopText] = getStartStopText();
+  const content = isStart ? startText : stopText;
+
+  return {
+    title,
+    content,
+  };
+};
+
+const getStartStopText = (): string[] => {
+  if (props.valveType) {
+    switch (props.valveType) {
+      case ValveType.冷冻侧阀:
+      case ValveType.冷却侧阀:
+        return ['注意避免已开启主机触发缺水报警', '关闭阀门前,请确认相关主机与水泵已关闭'];
+      case ValveType.进口阀:
+        return ['', '关闭阀门前,请确保出口阀同步关闭,同时确认相关主机与冷却泵均已关闭'];
+      case ValveType.出口阀:
+        return ['', '关闭阀门前,请确保进口阀同步关闭,同时确认相关主机与冷却泵均已关闭'];
+      default:
+        return [];
+    }
+  }
+
+  switch (props.deviceType) {
+    case DeviceType.冷水主机:
+      return ['开启主机前请确认配套冷却泵、冷冻泵、侧阀以及冷却塔已开启', ''];
+    case DeviceType.冷却塔:
+      return [
+        '',
+        '冷却塔关闭后,请注意避免冷却水出水温度大幅度升高,也请及时调整冷却塔进出口阀门状态,避免冷却水旁通造成能源浪费',
+      ];
+    case DeviceType.冷却泵:
+      return [
+        '开启水泵前,请确认相关主机冷却侧阀与冷却塔侧阀已开启',
+        '关闭水泵前,请确认相关主机已关闭,同时确保运行中的主机不会触发缺水报警',
+      ];
+    case DeviceType.冷冻泵:
+      return [
+        '开启水泵前,请确认相关主机冷冻侧阀已开启',
+        '关闭水泵前,请确认相关主机已关闭,同时确保运行中的主机不会触发缺水报警',
+      ];
+    default:
+      return [];
+  }
+};
 </script>
 
 <template>

+ 3 - 0
src/i18n/locales/zh.json

@@ -163,6 +163,7 @@
     "nextStep": "下一步",
     "no": "否",
     "noData": "暂无数据",
+    "noPermission": "暂无权限",
     "noSave": "不保存",
     "note": "备注",
     "off": "关",
@@ -634,6 +635,8 @@
     "confirmFullPowerOffTip": "所有设备将有序停机",
     "confirmFullPowerOn": "确定整体开机",
     "confirmFullPowerOnTip": "所有设备将有序开机",
+    "confirmStart": "确认开启{name}?",
+    "confirmStop": "确认关闭{name}?",
     "confirmSwitchStartStop": "确定将{name}切换至{status}状态?",
     "confirmSwitchToAutoTip": "调整为自动后,设备将由系统自动控制,无法手动控制",
     "confirmSwitchToManualTip": "调整为手动后,设备将不参与系统自动控制",

+ 30 - 8
src/layout/HvacAside.vue

@@ -1,5 +1,5 @@
 <script setup lang="ts">
-import { computed, onBeforeUnmount, onMounted, ref, useTemplateRef } from 'vue';
+import { computed, inject, onBeforeUnmount, onMounted, ref, useTemplateRef } from 'vue';
 import { useRoute, useRouter } from 'vue-router';
 import { message } from 'ant-design-vue';
 import Simplebar from 'simplebar-vue';
@@ -10,9 +10,17 @@ import { useRequest } from '@/hooks/request';
 import { useUserInfoStore } from '@/stores/user-info';
 import { dataCenterRoutes, opsCenterRoutes } from '@/router';
 import { t } from '@/i18n';
-import { addRevisePassword, getNoticePageList, getPageList, getUnreadNotifications } from '@/api';
+import {
+  addRevisePassword,
+  getDownloadLogo,
+  getLoginData,
+  getNoticePageList,
+  getPageList,
+  getUnreadNotifications,
+} from '@/api';
 import { translateNavigation } from '@/utils';
 import { removeToken } from '@/utils/auth';
+import { SET_COLOR_PRIMARY } from '@/constants/inject-key';
 
 import type { RouteRecordRaw } from 'vue-router';
 import type { FormInstance, Rule } from 'ant-design-vue/es/form';
@@ -22,7 +30,7 @@ import type { ChangePasswordForm, DeviceGroupItem, NoticePageItem, PageParams }
 const router = useRouter();
 const route = useRoute();
 const { permission, resetToken } = useUserInfoStore();
-
+const setColorPrimary = inject(SET_COLOR_PRIMARY, undefined);
 const menuRef = useTemplateRef('menu');
 const selectedKeys = ref<string[]>([route.path]);
 const openKeys = ref<string[]>([]);
@@ -31,7 +39,9 @@ const messageOpen = ref<boolean>(false);
 const changePasswordOpen = ref<boolean>(false);
 const messageList = ref<NoticePageItem[]>([]);
 const formRef = ref<FormInstance>();
+const imageUrl = ref<string>();
 const modalComponentRef = useTemplateRef('modalComponent');
+const userName = ref<string>('');
 const changePasswordForm = ref<ChangePasswordForm>({
   rawPassword: '',
   oldRawPassword: '',
@@ -146,6 +156,16 @@ const startTimer = () => {
 onMounted(async () => {
   try {
     const data = await getPageList();
+    const { userName: name, orgLogoPath, orgThemeColor } = await getLoginData();
+    if (orgThemeColor) {
+      setColorPrimary?.(orgThemeColor);
+    }
+
+    if (orgLogoPath) {
+      const data = await getDownloadLogo(orgLogoPath);
+      imageUrl.value = URL.createObjectURL(data as Blob);
+    }
+    userName.value = name[0];
     deviceGroupList.value = data.filter((item) => item.deviceGroupChilds.length > 0);
   } catch (err) {
     if (err instanceof Error) {
@@ -239,6 +259,7 @@ const confirm = () => {
   removeToken();
   resetToken();
   router.push('/login');
+  setColorPrimary?.('#32BAC0');
   modalComponentRef.value?.hideView();
 };
 
@@ -279,7 +300,8 @@ const bindingPassword = () => {
     :width="246"
   >
     <div class="aside-header">
-      <img class="aside-header-logo" src="@/assets/img/logo.png" />
+      <img v-if="imageUrl" class="aside-header-logo" :src="imageUrl" />
+      <img v-else class="aside-header-logo" src="@/assets/img/logo.png" />
       <span v-show="!collapsed" class="aside-header-title">{{ $t('common.unimatIoT') }}</span>
     </div>
     <Simplebar class="aside-scroll">
@@ -358,7 +380,9 @@ const bindingPassword = () => {
         :align="{ offset: [0, -30] }"
         :trigger="['click']"
       >
-        <div class="aside-footer-avatar" @click.prevent></div>
+        <div class="aside-footer-avatar" @click.prevent>
+          <span class="aside-footer-text">{{ userName }}</span>
+        </div>
         <template #overlay>
           <AMenu>
             <AMenuItem>
@@ -769,13 +793,11 @@ const bindingPassword = () => {
   background: var(--antd-color-primary);
   border-radius: 50%;
 
-  // 临时使用
-  &::before {
+  .aside-footer-text {
     font-size: 14px;
     font-weight: 500;
     line-height: 22px;
     color: #fff;
-    content: '贾';
   }
 }
 

+ 9 - 0
src/router/index.ts

@@ -446,6 +446,15 @@ const routes: Readonly<RouteRecordRaw[]> = [
       },
     ],
   },
+  {
+    path: '/exception',
+    name: 'exception',
+    component: () => import('@/views/NotAllow.vue'),
+    meta: {
+      hideInMenu: true,
+      requiresAuth: false,
+    },
+  },
   ...dataCenterRoutes,
   ...opsCenterRoutes,
 ];

+ 7 - 0
src/types/index.ts

@@ -137,6 +137,7 @@ export type UseGuideStepItemExpose = {
    */
   goBack?: () => boolean | Promise<boolean>;
   finish?: () => void | Promise<void>;
+  getInfoListByOrg?: () => void | Promise<void>;
 };
 
 export type UseGuideStepItemInstance = ComponentPublicInstance<unknown, UseGuideStepItemExpose>;
@@ -2704,3 +2705,9 @@ export interface PasswordParams {
   rawPassword: string;
   oldRawPassword: string;
 }
+export interface LoginData {
+  userName: string;
+  orgLogoPath: string;
+  orgThemeColor: string;
+  needFirstWizard: boolean;
+}

+ 32 - 0
src/views/NotAllow.vue

@@ -0,0 +1,32 @@
+<template>
+  <AFlex class="content-flex" justify="center" align="center">
+    <div class="content-div">
+      <div>
+        <img class="content-img" src="@/assets/img/not-allow.png" alt="" />
+      </div>
+      {{ $t('common.noPermission') }}
+    </div>
+  </AFlex>
+</template>
+
+<style lang="scss" scoped>
+.content-div {
+  font-size: 14px;
+  font-style: normal;
+  font-weight: 500;
+  line-height: 22px;
+  color: rgb(0 0 0 / 65%);
+  text-align: center;
+}
+
+.content-img {
+  width: 184px;
+  height: 132px;
+  margin-bottom: 8px;
+}
+
+.content-flex {
+  width: 100%;
+  height: 100%;
+}
+</style>

+ 1 - 1
src/views/algorithm-manage/AlgorithmEditing.vue

@@ -724,7 +724,7 @@ onMounted(() => {
 }
 
 .editor-style {
-  color: #32bac0;
+  color: var(--antd-color-primary-hover);
   text-decoration: underline;
   cursor: pointer;
 }

+ 2 - 2
src/views/create-customer/CreateCustomer.vue

@@ -13,10 +13,10 @@ import type { CreateCustomer, FormRules, RegisterGatewayForm, UseGuideStepItem }
 const useForm = reactive<CreateCustomer>({
   orgName: '',
   logo: '',
-  themeColor: '#32bac0',
+  themeColor: '#32BAC0',
   id: undefined,
   imageUrl: '',
-  selectedColor: '#e2550d',
+  selectedColor: '#E2550D',
   leaseTerm: [dayjs(), dayjs()],
   stationsNumber: 0,
   dataValidityPeriod: '',

+ 30 - 27
src/views/create-customer/EstablishOrganization.vue

@@ -1,5 +1,5 @@
 <script setup lang="ts">
-import { inject, onMounted, ref, watch } from 'vue';
+import { inject, onMounted, ref } from 'vue';
 import { ColorPicker } from 'vue-color-kit';
 import { message } from 'ant-design-vue';
 
@@ -55,8 +55,8 @@ const equipmentType = ref<EquipmentTypeItem[]>([]);
 const props = defineProps<UseGuideStepItemProps<CreateCustomer>>();
 const colorStyle: ColorStyle[] = [
   {
-    background: 'background:#32bac0',
-    border: 'border:1px solid #32bac0',
+    background: 'background:#32BAC0',
+    border: 'border:1px solid #32BAC0',
     color: '#32BAC0',
     index: 0,
   },
@@ -193,38 +193,41 @@ const handleChange = () => {
   }
 };
 
-defineExpose<UseGuideStepItemExpose>({
-  finish,
-});
-
 const getInfoListByOrg = () => {
   handleRequest(async () => {
     if (props.form.id) {
       equipmentLimitationsList.value = [];
       const data = await getInfoListByOrgId(props.form.id);
-      data.forEach((item) => {
-        if (item.deviceGlobalId === -1) {
-          props.form.stationsNumber = item.upperLimit;
-        }
-        if (item.deviceGlobalId !== -1) {
-          equipmentLimitationsList.value.push({
-            deviceGlobalId: item.deviceGlobalId,
-            upperLimit: item.upperLimit,
-          });
+      setTimeout(() => {
+        if (props.form.themeColor) {
+          setColorPrimary?.(props.form.themeColor);
         }
-      });
+      }, 500);
+      if (data.length) {
+        data.forEach((item) => {
+          if (item.deviceGlobalId === -1) {
+            props.form.stationsNumber = item.upperLimit;
+          }
+          if (item.deviceGlobalId !== -1) {
+            equipmentLimitationsList.value.push({
+              deviceGlobalId: item.deviceGlobalId,
+              upperLimit: item.upperLimit,
+            });
+          }
+        });
+      } else {
+        equipmentLimitationsList.value.push({
+          deviceGlobalId: undefined,
+          upperLimit: 0,
+        });
+      }
     }
   });
 };
-
-watch(
-  () => props.form.id,
-  (count) => {
-    if (count) {
-      getInfoListByOrg();
-    }
-  },
-);
+defineExpose<UseGuideStepItemExpose>({
+  finish,
+  getInfoListByOrg,
+});
 
 onMounted(() => {
   handleRequest(async () => {
@@ -232,8 +235,8 @@ onMounted(() => {
     equipmentType.value = await groupList({
       dataType: 1,
     });
-    getInfoListByOrg();
   });
+  getInfoListByOrg();
 });
 </script>
 

+ 3 - 0
src/views/device-group/DeviceGroup.vue

@@ -9,6 +9,8 @@ import { useUserInfoStore } from '@/stores/user-info';
 import { t } from '@/i18n';
 import { ViewPermission } from '@/utils/permission-type';
 
+import NotAllow from '../NotAllow.vue';
+
 import type { TabComponent } from '@/types';
 
 const router = useRouter();
@@ -99,6 +101,7 @@ const handleTabClick = (activeKey: string | number) => {
         />
       </ATabPane>
     </ATabs>
+    <NotAllow v-else />
   </div>
 </template>
 

+ 1 - 1
src/views/env-monitor/EnvMonitor.vue

@@ -1832,7 +1832,7 @@ const copyAreaCanvas = () => {
 
 .return-currently {
   margin-left: 10px;
-  color: #32bac0;
+  color: var(--antd-color-primary-hover);
 }
 
 .timeline-confirm {

+ 4 - 0
src/views/log-center/LogCenter.vue

@@ -6,6 +6,8 @@ import { useUserInfoStore } from '@/stores/user-info';
 import { t } from '@/i18n';
 import { ViewPermission } from '@/utils/permission-type';
 
+import NotAllow from '../NotAllow.vue';
+
 import OperateLog from './OperateLog.vue';
 import SmartCtrlLog from './SmartCtrlLog.vue';
 
@@ -18,6 +20,7 @@ const activeKey = ref('');
 const logTabs = computed<TabComponent[]>(() => {
   const smartCtrlLog = booleanPermission(ViewPermission.智控日志);
   const operateLog = booleanPermission(ViewPermission.操作日志);
+
   const result: TabComponent[] = [];
   const data = [
     {
@@ -61,6 +64,7 @@ onMounted(() => {
       <component v-if="activeKey === item.key && renderView" :is="item.component" />
     </ATabPane>
   </ATabs>
+  <NotAllow v-else />
 </template>
 
 <style lang="scss" scoped>

+ 9 - 4
src/views/login-component/LoginView.vue

@@ -1,18 +1,20 @@
 <script setup lang="ts">
-import { ref } from 'vue';
+import { inject, ref } from 'vue';
 import { useRouter } from 'vue-router';
 
 import SvgIcon from '@/components/SvgIcon.vue';
 import { useRequest } from '@/hooks/request';
 import { useUserInfoStore } from '@/stores/user-info';
 import { t } from '@/i18n';
-import { getNeedFirstWizard, getPermIdsByCurrentUser, loginUser } from '@/api';
+import { getLoginData, getPermIdsByCurrentUser, loginUser } from '@/api';
+import { SET_COLOR_PRIMARY } from '@/constants/inject-key';
 
 import { setPermission, setToken } from '../../utils/auth';
 
 import type { FormInstance, Rule } from 'ant-design-vue/es/form';
 import type { LoginUser } from '@/types';
 
+const setColorPrimary = inject(SET_COLOR_PRIMARY, undefined);
 const formRef = ref<FormInstance>();
 const router = useRouter();
 const { saveToken, savePermission } = useUserInfoStore();
@@ -53,8 +55,11 @@ const addLog = () => {
           setPermission(data);
           savePermission(data);
         }
-        const value = await getNeedFirstWizard();
-        if (value) {
+        const { needFirstWizard, orgThemeColor } = await getLoginData();
+        if (orgThemeColor) {
+          setColorPrimary?.(orgThemeColor);
+        }
+        if (needFirstWizard) {
           router.push('/first-usage');
         } else {
           router.push('/env-monitor/index');

+ 37 - 7
src/views/organization-manage/OrganizationManage.vue

@@ -1,5 +1,5 @@
 <script setup lang="ts">
-import { computed, onMounted, reactive, ref, useTemplateRef } from 'vue';
+import { computed, inject, onMounted, reactive, ref, useTemplateRef } from 'vue';
 import { message } from 'ant-design-vue';
 import dayjs from 'dayjs';
 
@@ -8,14 +8,16 @@ import OrganizationalStructure from '@/components/OrganizationalStructure.vue';
 import { useRequest } from '@/hooks/request';
 import { useUserInfoStore } from '@/stores/user-info';
 import { t } from '@/i18n';
-import { getDownloadLogo, getOrganizationAllList, getOrgInfo } from '@/api';
+import { getDownloadLogo, getLoginData, getOrganizationAllList, getOrgInfo } from '@/api';
 import { OperatePermission } from '@/utils/permission-type';
+import { SET_COLOR_PRIMARY } from '@/constants/inject-key';
 
 import EstablishOrganization from '../create-customer/EstablishOrganization.vue';
 
 import type { FormInstance } from 'ant-design-vue';
 import type { CreateCustomer, FormRules, OrganizationListItem, RegisterGatewayForm } from '@/types';
 
+const setColorPrimary = inject(SET_COLOR_PRIMARY, undefined);
 const { booleanPermission } = useUserInfoStore();
 const organizationOpen = ref<boolean>(false);
 const establishOrganizationRef = useTemplateRef('establishOrganization');
@@ -29,10 +31,10 @@ const modalComponentRef = useTemplateRef('modalComponent');
 const useForm = reactive<CreateCustomer>({
   orgName: '',
   logo: '',
-  themeColor: '#32bac0',
+  themeColor: '#32BAC0',
   id: undefined,
   imageUrl: '',
-  selectedColor: '#e2550d',
+  selectedColor: '#E2550D',
   leaseTerm: [dayjs(), dayjs()],
   stationsNumber: 0,
   dataValidityPeriod: '',
@@ -111,6 +113,7 @@ const rules = computed<FormRules<RegisterGatewayForm>>(() => {
 const addOrganization = () => {
   organizationOpen.value = true;
   organizationTitle.value = true;
+  setColorPrimary?.('#32BAC0');
   useForm.id = undefined;
   useForm.orgName = '';
   useForm.themeColor = '#32BAC0';
@@ -118,7 +121,7 @@ const addOrganization = () => {
   useForm.leaseTerm = [dayjs(), dayjs()];
   useForm.dataValidityPeriod = '';
   useForm.stationsNumber = 0;
-  useForm.selectedColor = '#e2550d';
+  useForm.selectedColor = '#E2550D';
   useForm.imageUrl = '';
 };
 const addQuery = () => {
@@ -129,6 +132,19 @@ const addReset = () => {
   getOrganizationAll();
 };
 
+const getFormattedDate = () => {
+  const date = new Date(); // 获取当前时间
+  const year = date.getFullYear(); // 年(四位数)
+
+  // 月(补零处理:1-12 → 01-12)
+  const month = String(date.getMonth() + 1).padStart(2, '0');
+
+  // 日(补零处理:1-31 → 01-31)
+  const day = String(date.getDate()).padStart(2, '0');
+
+  return `${year}-${month}-${day}`; // 拼接成指定格式
+};
+
 const organizationEditor = (id: number) => {
   organizationOpen.value = true;
   organizationTitle.value = false;
@@ -141,7 +157,7 @@ const organizationEditor = (id: number) => {
     const colorList = ['#32BAC0', '#256AFE', '#00A94D', '#7866FF'];
     if (colorList.includes(themeColor)) {
       useForm.themeColor = themeColor;
-      useForm.selectedColor = '#e2550d';
+      useForm.selectedColor = '#E2550D';
     } else {
       useForm.selectedColor = themeColor;
       useForm.themeColor = themeColor;
@@ -150,8 +166,12 @@ const organizationEditor = (id: number) => {
     useForm.orgName = orgName;
 
     useForm.logo = logo;
-    useForm.leaseTerm = [dayjs(startTenancy, 'YYYY-MM-DD'), dayjs(endTenancy, 'YYYY-MM-DD')];
+    useForm.leaseTerm = [
+      dayjs(startTenancy ? startTenancy : getFormattedDate(), 'YYYY-MM-DD'),
+      dayjs(endTenancy ? endTenancy : getFormattedDate(), 'YYYY-MM-DD'),
+    ];
     useForm.dataValidityPeriod = dataValidityPeriod;
+    establishOrganizationRef.value?.getInfoListByOrg?.();
   });
 };
 // const organizationDelete = (id: number) => {
@@ -164,6 +184,15 @@ const clickOrganizationChange = () => {
 const cancelOrganization = () => {
   organizationOpen.value = false;
 };
+
+const handleClose = () => {
+  handleRequest(async () => {
+    const { orgThemeColor } = await getLoginData();
+    setTimeout(() => {
+      setColorPrimary?.(orgThemeColor);
+    }, 500);
+  });
+};
 const saveOrganization = async () => {
   try {
     await Promise.all([organizationRefs.value?.validate() || Promise.resolve()]);
@@ -259,6 +288,7 @@ onMounted(() => {
       width="920px"
       :mask-closable="false"
       :footer="null"
+      :after-close="handleClose"
     >
       <AForm ref="organizationRefs" class="organization-form" :model="useForm" label-align="left" :rules="rules">
         <EstablishOrganization

+ 2 - 0
src/views/real-time-monitor/DeviceCtrlModal.vue

@@ -359,7 +359,9 @@ defineExpose({
       <BitSwitchConfirm
         v-if="isParamSwitch(item)"
         :info="item"
+        :device-name="deviceData?.deviceName"
         :device-type="deviceType"
+        :valve-type="valveType"
         :start-stop-status="devStartStopStatus"
         @ok="updateDeviceData(item.dataCode, $event)"
       />

+ 7 - 2
src/views/role-manage/RoleManage.vue

@@ -87,6 +87,7 @@ const clickCharacter = (id: number) => {
   if (characterList.value.some((item) => !isValidString(item.name))) {
     return;
   }
+
   characterListId.value = id;
   getDeviceGroupList();
   getFunctionPermList();
@@ -306,7 +307,6 @@ const transformData = (data: TreeStructure[]): OperationPermissions[] => {
 
 const getFunctionPermList = () => {
   handleRequest(async () => {
-    // const data = await getSubPermList(0);
     const dataCheck = await getPermissionCheckTree(characterListId.value);
     checkedKeys.value = [];
     expandedKeys.value = [];
@@ -516,7 +516,12 @@ onMounted(() => {
           </AFlex>
           <div class="operation">
             <AFlex align="center" v-for="(item, index) in operationpermissions" :key="index" class="operation-div">
-              <ACheckboxGroup v-model:value="item.list" :options="item.subPermissions" :disabled="editorChecked" />
+              <ACheckboxGroup
+                v-if="item.subPermissions"
+                v-model:value="item.list"
+                :options="item.subPermissions"
+                :disabled="editorChecked"
+              />
             </AFlex>
           </div>
         </div>