6 Commity 0ec93c47c8 ... cdb938d11e

Autor SHA1 Wiadomość Data
  wangcong cdb938d11e chore(api): 更新实时监测相关接口 2 dni temu
  wangcong 45ef0a3f29 chore(constants): 更新设备参数编码 2 dni temu
  wangshun 1baaa7e9dd perf(views): 优化"创建客户"模块多语言 2 dni temu
  wangshun 83496b3c03 perf(views): 修复导航菜单折叠报警图标未显示问题 2 dni temu
  wangshun 52761f2945 perf(views): 优化页面操作按钮支持权限控制功能 2 dni temu
  wangshun f27d3be671 perf(views): 优化“登陆页”密码框显示 3 dni temu

+ 16 - 1
src/api/index.ts

@@ -44,6 +44,7 @@ import type {
   DevicesList,
   DevicesListItemData,
   DeviceTypeCount,
+  DevStartStopStatus,
   DevWorkHisDataQuery,
   DevWorkHistoryData,
   DevWorkRealTimeData,
@@ -1264,12 +1265,13 @@ export const updateGroupModuleInfo = async (params: Partial<GroupModuleInfo>) =>
   });
 };
 
-export const getGroupModuleDevData = async (deviceId: number, deviceType: number) => {
+export const getGroupModuleDevData = async (deviceId: number, deviceType: number, valveType?: number) => {
   const data = await request<GroupModuleDevData>(apiBiz('/moduleInfo/info/device/detail'), {
     method: 'POST',
     body: JSON.stringify({
       deviceId,
       deviceType,
+      valveType,
     }),
   });
 
@@ -1287,6 +1289,19 @@ export const updateGroupModuleDevData = async (deviceId: number, deviceParamCode
   });
 };
 
+export const getDevStartStopStatus = async (deviceId: number, deviceType: number, valveType?: number) => {
+  const data = await request<DevStartStopStatus>(apiBiz('/moduleInfo/info/device/determine'), {
+    method: 'POST',
+    body: JSON.stringify({
+      deviceId,
+      deviceType,
+      valveType,
+    }),
+  });
+
+  return data;
+};
+
 export const getGroupModuleDevStatus = async (groupId: number) => {
   const data = await request<GroupModuleBatchItem[]>(apiBiz('/moduleInfo/info/batch/status'), {
     method: 'POST',

+ 10 - 2
src/components/LineChart.vue

@@ -14,6 +14,9 @@ import {
 import { use } from 'echarts/core';
 import { CanvasRenderer } from 'echarts/renderers';
 
+import { useUserInfoStore } from '@/stores/user-info';
+import { OperatePermission } from '@/utils/permission-type';
+
 import SvgIcon from './SvgIcon.vue';
 
 import type { MonitoringPointData } from '@/types';
@@ -32,7 +35,7 @@ use([
 ]);
 
 const list = ref<number[]>([]);
-
+const { booleanPermission } = useUserInfoStore();
 interface Props {
   data: MonitoringPointData;
   monitoringId?: number;
@@ -199,7 +202,12 @@ onUnmounted(() => {
         <div ref="textContainer" class="line-chart-header-text">{{ data.name }}</div>
       </APopover>
       <div v-else ref="textContainer" class="line-chart-header-text">{{ data.name }}</div>
-      <SvgIcon v-if="iconShow" class="line-chart-header-icon" @click="editorMonitoring(data.id)" name="adjustment" />
+      <SvgIcon
+        v-if="iconShow && booleanPermission(OperatePermission.编辑检测点)"
+        class="line-chart-header-icon"
+        @click="editorMonitoring(data.id)"
+        name="adjustment"
+      />
     </AFlex>
     <div @click="addHistoricalData(data)" class="chart-container" style="cursor: pointer">
       <VChart class="chart chart-bgc" :option="option" />

+ 69 - 0
src/constants/device-params.ts

@@ -1,3 +1,12 @@
+/**
+ * 公共设备参数
+ */
+export const enum DevParamCommon {
+  禁启用状态 = 'disableEnableStatus',
+  手自动状态 = 'manualAutoStatus',
+  手动启停 = 'startStopOrder',
+}
+
 /**
  * 控制柜设备参数
  */
@@ -55,6 +64,9 @@ export const enum DevParamChillerUnit {
   有功功率 = 'outputActivePower',
   负载率 = 'loadRatio',
   负载率限制设定值 = 'loadRatioLimitSet',
+  本地远程状态 = 'localRemoteStatus',
+  运行时间 = 'runHours',
+  自动启停指令 = 'semiAutoStartStopOrder',
   冷冻水出水温度设定值 = 'evapWaterTempOutSet',
   冷冻水出水温度设定值反馈 = 'evapWaterTempOutSetFb',
   今日耗电量 = 'energyToday',
@@ -70,3 +82,60 @@ export const enum DevParamChillerUnit {
   排气温度 = 'cirCompDisTemp',
   吸气温度 = 'cirCompSucTemp',
 }
+
+/**
+ * 冷冻阀设备参数
+ */
+export const enum DevParamChilledValve {
+  冷冻侧阀开到位信号 = 'chilledValveOpenSignal',
+  冷冻侧阀关到位信号 = 'chilledValveCloseSignal',
+  冷冻侧阀本地远程状态 = 'chilledValveLocalRemoteStatus',
+  冷冻侧阀开关指令 = 'chilledValveStartStopOrder',
+  冷冻侧阀手自动状态 = 'chilledValveManualAutoStatus',
+}
+
+/**
+ * 冷却阀设备参数
+ */
+export const enum DevParamCoolingValve {
+  冷却侧阀开到位信号 = 'coolingValveOpenSignal',
+  冷却侧阀关到位信号 = 'coolingValveCloseSignal',
+  冷却侧阀本地远程状态 = 'coolingValveLocalRemoteStatus',
+  冷却侧阀开关指令 = 'coolingValveStartStopOrder',
+  冷却侧阀手自动状态 = 'coolingValveManualAutoStatus',
+}
+
+/**
+ * 进口阀设备参数
+ */
+export const enum DevParamInletValve {
+  进口阀开到位信号 = 'inletValveOpenSignal',
+  进口阀关到位信号 = 'inletValveCloseSignal',
+  进口阀本地远程状态 = 'inletValveLocalRemoteStatus',
+  进口阀开关指令 = 'inletValveStartStopOrder',
+  进口阀手自动状态 = 'inletValveManualAutoStatus',
+}
+
+/**
+ * 出口阀设备参数
+ */
+export const enum DevParamOutletValve {
+  出口阀开到位信号 = 'outletValveOpenSignal',
+  出口阀关到位信号 = 'outletValveCloseSignal',
+  出口阀本地远程状态 = 'outletValveLocalRemoteStatus',
+  出口阀开关指令 = 'outletValveStartStopOrder',
+  出口阀手自动状态 = 'outletValveManualAutoStatus',
+}
+
+/**
+ * 旁通阀设备参数
+ */
+export const enum DevParamBypassValve {
+  旁通阀开度反馈 = 'bypassValveOpeningFb',
+  旁通阀压差设定 = 'bypassValvePreDiffSet',
+  旁通阀开度控制模式 = 'bypassValveOpeningControlMode',
+  旁通阀开度手动设定 = 'bypassValveOpeningManualSet',
+  旁通阀开度上限设定 = 'bypassValveOpeningUpperLimitSet',
+  旁通阀开度下限设定 = 'bypassValveOpeningLowerLimitSet',
+  旁通阀开度最终设定 = 'bypassValveOpeningFinalSet',
+}

+ 25 - 1
src/i18n/locales/zh.json

@@ -202,6 +202,7 @@
     "rzpeat": "重复",
     "save": "保存",
     "search": "搜索",
+    "selectAll": "全选",
     "selectTime": "选择时间",
     "selected": "已选",
     "sequenceNumber": "序列号",
@@ -218,6 +219,7 @@
     "status": "状态",
     "submit": "提交",
     "success": "成功",
+    "taiwan": "台",
     "templateImport": "模板导入",
     "tenThousand": "万",
     "time": "时间",
@@ -603,10 +605,19 @@
     "userPermission": "账号管理"
   },
   "organizationManage": {
+    "custom": "可以自定义二级域名、Logo及主题色,以后可随时修改",
+    "dataRetentionPeriod": "数据存储时长",
+    "imageFormat": "请上传JPG/PNG/JPEG格式的图片!",
+    "leaseTerm": "租期",
+    "numberStations": "站房数(设备组)",
     "organizationDescription": "组织描述",
     "organizationName": "组织名称",
     "parentOrganization": "上级组织",
-    "pleaseEnterOrganizationName": "请输入组织名称"
+    "pleaseEnterOrganizationName": "请输入组织名称",
+    "quantityLimit": "设置数量上限",
+    "supportedImageFormats": "支持JPG/PNG/JPEG格式的图片",
+    "themeColor": "主题色",
+    "uploadImage": "上传图片"
   },
   "realTimeMonitor": {
     "activePower": "有功功率",
@@ -772,13 +783,24 @@
     "verificationSuccessful": "验证成功"
   },
   "roleManage": {
+    "addCharacters": "添加角色",
+    "administrator": "管理员",
+    "characterDescription": "角色描述",
+    "characterName": "角色名称",
+    "customRole": "可以直接使用默认角色,也可以自定义角色,权限后续可随时编辑",
     "dataPermission": "数据权限",
+    "defaultRole": "默认角色",
     "deviceGroupPermission": "设备组权限",
+    "editorialRole": "编辑角色",
     "functionalPermission": "功能权限",
+    "increaseCharacter": "增加角色",
+    "menuPermissionConfig": "菜单权限配置",
     "nameCannotBeEmpty": "名称不能为空!",
     "nameCannotContainSpaces": "名称不能包含空格!",
     "operationPermission": "操作权限",
+    "operationsStaff": "运维人员",
     "permission": "权限",
+    "pleaseEnterRoleName": "请输入角色名称",
     "viewPermission": "查看权限"
   },
   "setupProtocol": {
@@ -906,6 +928,7 @@
   "standardProtocolLibrary": {},
   "userManage": {
     "accountManagement": "账号管理",
+    "addAccount": "增加账号",
     "createDate": "创建日期",
     "defaultPassword": "默认密码8个0",
     "expiryDate": "到期日期",
@@ -913,6 +936,7 @@
     "mobileNumberFormatError": "手机号格式错误",
     "mobilePhoneNumber": "手机号",
     "password": "密码",
+    "pleaseAddAccount": "请添加账号!",
     "pleaseEnterMobileNumber": "请输入手机号、姓名",
     "role": "角色"
   }

+ 16 - 1
src/layout/HvacAside.vue

@@ -344,7 +344,10 @@ const bindingPassword = () => {
       </AMenu>
     </Simplebar>
     <div v-show="collapsed" class="aside-collapsed-icons">
-      <SvgIcon name="information" @click="addInformation" />
+      <ABadge :count="countNumber">
+        <SvgIcon name="information" @click="addInformation" />
+      </ABadge>
+
       <SvgIcon name="setting" />
       <SvgIcon name="unfold" @click="toggleCollapsed" />
     </div>
@@ -544,6 +547,18 @@ const bindingPassword = () => {
   cursor: pointer;
 }
 
+:deep(.aside-collapsed-icons) {
+  .ant-badge .ant-badge-count {
+    min-width: 15px;
+    height: 15px;
+    margin-top: 10px;
+    margin-right: 14px;
+    font-size: 10px;
+    line-height: 15px;
+    cursor: pointer;
+  }
+}
+
 :deep(.aside-footer) {
   .ant-badge .ant-badge-count {
     min-width: 15px;

+ 18 - 0
src/types/index.ts

@@ -2230,6 +2230,24 @@ export interface GroupModuleDevData {
     dataCode: string;
     value?: number | string;
   }[];
+  dataListRO: GroupModuleDevParam[];
+  dataListRW: GroupModuleDevParam[];
+}
+
+export interface GroupModuleDevParam {
+  dataName: string;
+  dataCode: string;
+  value: string;
+  unit: string | null;
+  bitAddress0Status: string | null;
+  bitAddress0Detail: string | null;
+  bitAddress1Status: string | null;
+  bitAddress1Detail: string | null;
+}
+
+export interface DevStartStopStatus {
+  status: boolean;
+  message: string[];
 }
 
 export interface GroupModuleBatchItem {

+ 1 - 1
src/utils/permission-type.ts

@@ -16,7 +16,7 @@ export const enum ViewPermission {
   日志中心 = 110,
   智控日志 = 11101,
   操作日志 = 11102,
-  实时监 = 111,
+  实时监 = 111,
   设备工况 = 112,
 }
 

+ 21 - 4
src/views/alarm-manage/AlarmManage.vue

@@ -7,6 +7,7 @@ import ConfirmModal from '@/components/ConfirmModal.vue';
 import SvgIcon from '@/components/SvgIcon.vue';
 import { useDictData } from '@/hooks/dict-data';
 import { useRequest } from '@/hooks/request';
+import { useUserInfoStore } from '@/stores/user-info';
 import { t } from '@/i18n';
 import {
   addAlarmEvent,
@@ -18,6 +19,7 @@ import {
   getOrgUsers,
   updateAlarmEvent,
 } from '@/api';
+import { OperatePermission } from '@/utils/permission-type';
 import { DictCode } from '@/constants';
 
 import AlarmConditions from './AlarmConditions.vue';
@@ -44,7 +46,7 @@ const { dictData: alarmRepeatTime, getDictData: getAlarmRepeatTime } = useDictDa
 const { dictData: alarmAlgOperator, getDictData: getAlarmAlgOperator } = useDictData(DictCode.AlarmAlgOperator);
 const { dictData: alarmDeviceState, getDictData: getAlarmDeviceState } = useDictData(DictCode.AlarmDeviceState);
 const { dictData: alarmNotifyMethod, getDictData: getAlarmNotifyMethod } = useDictData(DictCode.AlarmNotifyMethod);
-
+const { booleanPermission } = useUserInfoStore();
 const modalComponentRef = useTemplateRef('modalComponent');
 const formRef = ref<FormInstance>();
 const triggerConditionRefs = ref<InstanceType<typeof AlarmConditions>[]>([]);
@@ -630,7 +632,12 @@ onMounted(() => {
     <AFlex justify="space-between">
       <div class="text-top">{{ $t('navigation.alarmManage') }}</div>
       <div>
-        <AButton type="primary" class="icon-button" @click="addAlarm">
+        <AButton
+          v-if="booleanPermission(OperatePermission.新增事件响应)"
+          type="primary"
+          class="icon-button"
+          @click="addAlarm"
+        >
           <AFlex align="center">
             <SvgIcon name="plus" />
             <span> {{ $t('common.addNew') }} </span>
@@ -660,9 +667,19 @@ onMounted(() => {
             <ASwitch @change="switchalarm(record as EventTrigger)" v-model:checked="record.enabled" />
           </template>
           <template v-else-if="column.key === 'action'">
-            <SvgIcon @click="alarmEditor(record.id)" class="icon-style" name="edit-o" />
+            <SvgIcon
+              v-if="booleanPermission(OperatePermission.编辑事件响应)"
+              @click="alarmEditor(record.id)"
+              class="icon-style"
+              name="edit-o"
+            />
             <SvgIcon @click="alarmHistory(record.id)" class="icon-style" name="clock-circle-o" />
-            <SvgIcon @click="alarmDelete(record.id)" class="icon-style" name="delete" />
+            <SvgIcon
+              v-if="booleanPermission(OperatePermission.删除事件响应)"
+              @click="alarmDelete(record.id)"
+              class="icon-style"
+              name="delete"
+            />
           </template>
         </template>
       </ATable>

+ 4 - 1
src/views/algorithm-manage/AlgorithmManage.vue

@@ -3,13 +3,16 @@ import { ref, useTemplateRef } from 'vue';
 
 import DeviceGroupSelect from '@/components/DeviceGroupSelect.vue';
 import { useRequest } from '@/hooks/request';
+import { useUserInfoStore } from '@/stores/user-info';
 import { t } from '@/i18n';
 import { getAlgorithmConfigInfo } from '@/api';
+import { OperatePermission } from '@/utils/permission-type';
 
 import AlgorithmEditing from './AlgorithmEditing.vue';
 
 import type { AlgorithmConfigInfo } from '@/types';
 
+const { booleanPermission } = useUserInfoStore();
 const algorithmConfigInfo = ref<AlgorithmConfigInfo>();
 const algorithmEditingRef = useTemplateRef('algorithmEditing');
 const activeKey = ref<string[]>(['intelligentControl', 'functionSettings']);
@@ -51,7 +54,7 @@ const cancelClick = () => {
         <div class="title-text">{{ $t('algorithmManage.algorithmConfiguration') }}</div>
         <DeviceGroupSelect @change="handleDevGroupChange" />
       </AFlex>
-      <AButton type="primary" @click="editorAlgorithm">
+      <AButton type="primary" @click="editorAlgorithm" v-if="booleanPermission(OperatePermission.算法配置编辑)">
         <AFlex align="center">
           <SvgIcon style="margin-right: 4px" name="edit-o" />
           <span> {{ t('common.editor') }} </span>

+ 8 - 8
src/views/create-customer/AddAccount.vue

@@ -22,7 +22,7 @@ const rules: Record<string, Rule[]> = {
     {
       validator: (_rule: unknown, value: string) => {
         if (!isValidPhone(value)) {
-          return Promise.reject('手机号格式错误');
+          return Promise.reject(t('userManage.mobileNumberFormatError'));
         }
         return Promise.resolve();
       },
@@ -61,13 +61,13 @@ defineExpose({
           <SvgIcon @click="deleteAccount" class="icon-delete" name="close-circle" />
         </template>
         <AFlex class="account-bgc" :vertical="true">
-          <AFormItem label="姓名" name="userName">
-            <AInput class="input-width" v-model:value="form.userName" placeholder="请输入" />
+          <AFormItem :label="t('userManage.fullName')" name="userName">
+            <AInput class="input-width" v-model:value="form.userName" :placeholder="t('common.pleaseEnter')" />
           </AFormItem>
-          <AFormItem label="手机号" name="mobile">
-            <AInput class="input-width" v-model:value="form.mobile" placeholder="请输入" />
+          <AFormItem :label="t('userManage.mobilePhoneNumber')" name="mobile">
+            <AInput class="input-width" v-model:value="form.mobile" :placeholder="t('common.pleaseEnter')" />
           </AFormItem>
-          <AFormItem label="角色" name="roleId">
+          <AFormItem :label="t('userManage.role')" name="roleId">
             <ASelect
               v-model:value="form.roleId"
               class="input-width"
@@ -76,7 +76,7 @@ defineExpose({
               :placeholder="$t('common.plzSelect')"
             />
           </AFormItem>
-          <AFormItem label="到期时间" name="accountTerm">
+          <AFormItem :label="t('userManage.expiryDate')" name="accountTerm">
             <ARangePicker
               v-model:value="form.accountTerm"
               class="input-width"
@@ -84,7 +84,7 @@ defineExpose({
               :separator="$t('common.to')"
             />
           </AFormItem>
-          <AFormItem label="启用" name="enabled">
+          <AFormItem :label="t('alarmManage.enable')" name="enabled">
             <ASwitch v-model:checked="form.enabled" />
           </AFormItem>
         </AFlex>

+ 3 - 3
src/views/create-customer/CreateAccount.vue

@@ -45,7 +45,7 @@ const confirm = () => {
 
 const finish = async () => {
   if (!accountList.value.length) {
-    throw new Error('请添加账号!');
+    throw new Error(t('userManage.pleaseAddAccount'));
   }
   // eslint-disable-next-line no-useless-catch
   try {
@@ -84,7 +84,7 @@ onMounted(() => {
 
 <template>
   <div>
-    <div class="account"><span class="account-text">*</span>创建账号</div>
+    <div class="account"><span class="account-text">*</span>{{ t('createCustomer.createAccount') }}</div>
     <AFlex wrap="wrap">
       <div v-for="(item, index) in accountList" :key="index" class="account-list">
         <AddAccount
@@ -100,7 +100,7 @@ onMounted(() => {
     <AButton type="primary" ghost class="icon-button button-style" @click="addAccountForm">
       <AFlex align="center">
         <SvgIcon name="plus" />
-        <span> 增加账号 </span>
+        <span> {{ t('userManage.addAccount') }} </span>
       </AFlex>
     </AButton>
     <ConfirmModal

+ 23 - 15
src/views/create-customer/CreateCharacter.vue

@@ -161,7 +161,7 @@ const getFindRolesByOrg = () => {
       characterList.value = [];
       const list = await getFindRolesByOrgIds([props.form.id]);
       list.forEach((item) => {
-        if (item.roleName !== '管理员' && item.roleName !== '工程师') {
+        if (item.roleName !== '管理员' && item.roleName !== '运维人员') {
           characterList.value.push(item);
         }
       });
@@ -192,15 +192,15 @@ onMounted(() => {
 
 <template>
   <div>
-    <div class="character"><span class="character-text">*</span>创建角色</div>
+    <div class="character"><span class="character-text">*</span>{{ t('createCustomer.createCharacter') }}</div>
     <AFlex :vertical="true" :gap="16">
       <AFlex align="center">
-        <AFlex class="input-style" align="center"> 管理员 </AFlex>
-        <div class="input-style-text">默认角色</div>
+        <AFlex class="input-style" align="center"> {{ t('roleManage.administrator') }} </AFlex>
+        <div class="input-style-text">{{ t('roleManage.defaultRole') }}</div>
       </AFlex>
       <AFlex align="center">
-        <AFlex class="input-style" align="center"> 运维人员 </AFlex>
-        <div class="input-style-text">默认角色</div>
+        <AFlex class="input-style" align="center"> {{ t('roleManage.operationsStaff') }} </AFlex>
+        <div class="input-style-text">{{ t('roleManage.defaultRole') }}</div>
       </AFlex>
       <AFlex align="center" v-for="(item, index) in characterList" :key="index">
         <AFlex class="input-style input-background" align="center"> {{ item.roleName }} </AFlex>
@@ -219,12 +219,12 @@ onMounted(() => {
     <AButton type="primary" ghost class="icon-button button-style" @click="addCharacter">
       <AFlex align="center">
         <SvgIcon name="plus" />
-        <span> 增加角色 </span>
+        <span> {{ t('roleManage.increaseCharacter') }} </span>
       </AFlex>
     </AButton>
     <AModal
       v-model:open="characterOpen"
-      :title="characterTitle ? '添加角色' : '编辑角色'"
+      :title="characterTitle ? t('roleManage.addCharacters') : t('roleManage.editorialRole')"
       width="920px"
       :mask-closable="false"
       :footer="null"
@@ -238,10 +238,14 @@ onMounted(() => {
         :rules="rules"
         :label-col="{ span: 3 }"
       >
-        <AFormItem label="角色名称" name="roleName">
-          <AInput class="input-width" v-model:value="characterForm.roleName" placeholder="请输入角色名称" />
+        <AFormItem :label="t('roleManage.characterName')" name="roleName">
+          <AInput
+            class="input-width"
+            v-model:value="characterForm.roleName"
+            :placeholder="t('roleManage.pleaseEnterRoleName')"
+          />
         </AFormItem>
-        <AFormItem label="角色描述">
+        <AFormItem :label="t('roleManage.characterDescription')">
           <ATextarea
             class="input-width"
             v-model:value="characterForm.remark"
@@ -249,10 +253,14 @@ onMounted(() => {
             :auto-size="{ minRows: 4 }"
           />
         </AFormItem>
-        <AFormItem label="菜单权限配置">
+        <AFormItem :label="t('roleManage.menuPermissionConfig')">
           <div class="permission-configuration">
-            <ACheckbox class="select-all" :indeterminate="indeterminate" v-model:checked="checked" @change="selectAll"
-              >全选</ACheckbox
+            <ACheckbox
+              class="select-all"
+              :indeterminate="indeterminate"
+              v-model:checked="checked"
+              @change="selectAll"
+              >{{ t('common.selectAll') }}</ACheckbox
             >
             <ATree
               v-model:expanded-keys="expandedKeys"
@@ -267,7 +275,7 @@ onMounted(() => {
       </AForm>
       <AFlex justify="flex-end" class="footer">
         <AButton class="button-right" type="primary" ghost @click="cancelSave">{{ $t('common.cancel') }}</AButton>
-        <AButton type="primary" @click="characterSave">保存</AButton>
+        <AButton type="primary" @click="characterSave">{{ t('common.save') }}</AButton>
       </AFlex>
     </AModal>
     <ConfirmModal

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

@@ -27,28 +27,28 @@ const rules = computed<FormRules<RegisterGatewayForm>>(() => {
     orgName: [
       {
         required: true,
-        message: '不能为空',
+        message: t('common.cannotEmpty'),
         trigger: 'change',
       },
     ],
     leaseTerm: [
       {
         required: true,
-        message: '不能为空',
+        message: t('common.cannotEmpty'),
         trigger: 'change',
       },
     ],
     stationsNumber: [
       {
         required: true,
-        message: '不能为空',
+        message: t('common.cannotEmpty'),
         trigger: 'change',
       },
     ],
     dataValidityPeriod: [
       {
         required: true,
-        message: '不能为空',
+        message: t('common.cannotEmpty'),
         trigger: 'change',
       },
     ],
@@ -59,14 +59,14 @@ const steps = ref<UseGuideStepItem[]>([
   {
     title: t('createCustomer.establishOrganization'),
     component: shallowRef(EstablishOrganization),
-    stepDescription: '可以自定义二级域名、Logo及主题色,以后可随时修改',
+    stepDescription: t('organizationManage.custom'),
     labelAlign: 'left',
     labelCol: { span: 2 },
   },
   {
     title: t('createCustomer.createCharacter'),
     component: shallowRef(CreateCharacter),
-    stepDescription: '可以直接使用默认角色,也可以自定义角色,权限后续可随时编辑',
+    stepDescription: t('roleManage.customRole'),
   },
   {
     title: t('createCustomer.createAccount'),

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

@@ -52,12 +52,12 @@ defineExpose({
               />
             </AFormItem>
 
-            <AFormItem label="数量" name="upperLimit">
+            <AFormItem :label="t('setupProtocol.protocolParamFields.quantity')" name="upperLimit">
               <AInputNumber
                 :placeholder="t('common.pleaseEnter')"
                 v-model:value="form.upperLimit"
                 class="time-width"
-                addon-after="台"
+                :addon-after="t('common.taiwan')"
               />
             </AFormItem>
           </AFlex>

+ 9 - 9
src/views/create-customer/EstablishOrganization.vue

@@ -166,7 +166,7 @@ const beforeUpload = (file: Blob) => {
   const isJpgOrPng = file.type === 'image/jpg' || file.type === 'image/png' || file.type === 'image/jpeg';
   console.log(isJpgOrPng);
   if (!isJpgOrPng) {
-    message.error('请上传JPG/PNG/JPEG格式的图片!');
+    message.error(t('organizationManage.imageFormat'));
     return false;
   }
 };
@@ -239,10 +239,10 @@ onMounted(() => {
 
 <template>
   <div class="organization text">
-    <AFormItem label="组织名称" name="orgName">
+    <AFormItem :label="t('organizationManage.organizationName')" name="orgName">
       <AInput v-model:value="form.orgName" class="organization-input" :placeholder="$t('common.pleaseEnter')" />
     </AFormItem>
-    <AFormItem label="租期" name="leaseTerm">
+    <AFormItem :label="t('organizationManage.leaseTerm')" name="leaseTerm">
       <ARangePicker
         class="organization-input"
         v-model:value="form.leaseTerm"
@@ -250,8 +250,8 @@ onMounted(() => {
         :separator="$t('common.to')"
       />
     </AFormItem>
-    <div class="upper-limit">设置数量上限</div>
-    <AFormItem label="站房数(设备组)" name="stationsNumber">
+    <div class="upper-limit">{{ t('organizationManage.quantityLimit') }}</div>
+    <AFormItem :label="t('organizationManage.numberStations')" name="stationsNumber">
       <AInputNumber
         :placeholder="t('common.pleaseEnter')"
         v-model:value="form.stationsNumber"
@@ -270,7 +270,7 @@ onMounted(() => {
       />
     </div>
 
-    <AFormItem label="数据存储时长" name="dataValidityPeriod">
+    <AFormItem :label="t('organizationManage.dataRetentionPeriod')" name="dataValidityPeriod">
       <ASelect
         class="organization-input"
         v-model:value="form.dataValidityPeriod"
@@ -301,16 +301,16 @@ onMounted(() => {
 
           <div v-else>
             <SvgIcon class="icon-size" name="upload-pictures" />
-            <div class="text">上传图片</div>
+            <div class="text">{{ t('organizationManage.uploadImage') }}</div>
           </div>
         </AUpload>
-        <div class="format-text">支持JPG/PNG/JPEG格式的图片</div>
+        <div class="format-text">{{ t('organizationManage.supportedImageFormats') }}</div>
       </div>
     </AFormItem>
 
     <AFlex>
       <AFlex align="center">
-        <div class="text">主题色</div>
+        <div class="text">{{ t('organizationManage.themeColor') }}</div>
         <AFlex class="color-list">
           <div v-for="(item, index) in colorStyle" :key="index">
             <div @click="colorClick(item.color)">

+ 23 - 2
src/views/device-group/DeviceGroup.vue

@@ -5,12 +5,15 @@ import { useRoute, useRouter } from 'vue-router';
 import DeviceWorkStatus from '@/views/device-work-status/DeviceWorkStatus.vue';
 import RealTimeMonitor from '@/views/real-time-monitor/RealTimeMonitor.vue';
 import { useRefreshView } from '@/hooks/refresh-view';
+import { useUserInfoStore } from '@/stores/user-info';
 import { t } from '@/i18n';
+import { ViewPermission } from '@/utils/permission-type';
 
 import type { TabComponent } from '@/types';
 
 const router = useRouter();
 const route = useRoute();
+const { booleanPermission } = useUserInfoStore();
 const { renderView, refreshView } = useRefreshView();
 const activeKey = ref('');
 
@@ -19,7 +22,10 @@ const deviceGroupId = computed(() => {
 });
 
 const aiSmartCtrlTabs = computed<TabComponent[]>(() => {
-  return [
+  const realTimeMonitor = booleanPermission(ViewPermission.实时监测);
+  const deviceWorkStatus = booleanPermission(ViewPermission.设备工况);
+  const result: TabComponent[] = [];
+  const data = [
     {
       key: 'realTimeMonitor',
       name: t('navigation.realTimeMonitor'),
@@ -31,6 +37,16 @@ const aiSmartCtrlTabs = computed<TabComponent[]>(() => {
       component: DeviceWorkStatus,
     },
   ];
+
+  if (realTimeMonitor) {
+    result.push(data.find((item) => item.key === 'realTimeMonitor') as TabComponent);
+  }
+
+  if (deviceWorkStatus) {
+    result.push(data.find((item) => item.key === 'deviceWorkStatus') as TabComponent);
+  }
+
+  return result;
 });
 
 watch(deviceGroupId, () => {
@@ -69,7 +85,12 @@ const handleTabClick = (activeKey: string | number) => {
 
 <template>
   <div class="device-group-container">
-    <ATabs class="device-group-tab" v-model:active-key="activeKey" @tab-click="handleTabClick">
+    <ATabs
+      class="device-group-tab"
+      v-model:active-key="activeKey"
+      @tab-click="handleTabClick"
+      v-if="aiSmartCtrlTabs.length"
+    >
       <ATabPane v-for="item in aiSmartCtrlTabs" :key="item.key" :tab="item.name">
         <component
           v-if="activeKey === item.key && renderView"

+ 18 - 8
src/views/device-list/DeviceList.vue

@@ -6,8 +6,10 @@ import { message } from 'ant-design-vue';
 import ModalGuidance from '@/layout/ModalGuidance.vue';
 import ConfirmModal from '@/components/ConfirmModal.vue';
 import { useRequest } from '@/hooks/request';
+import { useUserInfoStore } from '@/stores/user-info';
 import { t } from '@/i18n';
 import { deviceDeletion, getPageList, groupList, queryDevicesList } from '@/api';
+import { OperatePermission } from '@/utils/permission-type';
 
 import CreateDevice from '../create-device/CreateDevice.vue';
 
@@ -64,7 +66,7 @@ const devicesColumns = [
     ellipsis: true,
   },
 ];
-
+const { booleanPermission } = useUserInfoStore();
 const router = useRouter();
 const modalGuidanceRef = useTemplateRef('modalGuidance');
 const devicesList = ref<DevicesListItem[]>([]);
@@ -198,14 +200,18 @@ onMounted(() => {
       <AFlex justify="space-between">
         <div class="text-top">{{ $t('navigation.deviceManage') }}</div>
         <div>
-          <AButton class="deletion-button default-button" @click="postDeviceDeletion">
+          <AButton
+            class="deletion-button default-button"
+            @click="postDeviceDeletion"
+            v-if="booleanPermission(OperatePermission.删除设备)"
+          >
             <AFlex align="center">
               <SvgIcon style="margin-right: 4px" name="delete" />
 
               <span> {{ $t('common.delete') }} </span>
             </AFlex>
           </AButton>
-          <AButton type="primary" @click="addEquipment">
+          <AButton type="primary" @click="addEquipment" v-if="booleanPermission(OperatePermission.新增设备)">
             <AFlex align="center">
               <SvgIcon style="margin-right: 4px" name="plus" />
               <span> {{ $t('common.add') }} </span>
@@ -288,11 +294,15 @@ onMounted(() => {
           <AButton class="default-button" @click="addQueryReset">{{ $t('common.reset') }}</AButton>
         </AFlex>
         <ATable
-          :row-selection="{
-            type: 'checkbox',
-            selectedRowKeys: devicesKeys,
-            onChange: devicesChange,
-          }"
+          :row-selection="
+            booleanPermission(OperatePermission.删除设备)
+              ? {
+                  type: 'checkbox',
+                  selectedRowKeys: devicesKeys,
+                  onChange: devicesChange,
+                }
+              : undefined
+          "
           row-key="id"
           :columns="devicesColumns"
           :data-source="devicesList"

+ 16 - 4
src/views/env-monitor/EnvMonitor.vue

@@ -10,6 +10,7 @@ import LineChart from '@/components/LineChart.vue';
 import SvgIcon from '@/components/SvgIcon.vue';
 import { useDictData } from '@/hooks/dict-data';
 import { useRequest } from '@/hooks/request';
+import { useUserInfoStore } from '@/stores/user-info';
 import router from '@/router';
 import { t } from '@/i18n';
 import {
@@ -31,6 +32,7 @@ import {
   updateMonitorPoint,
   updateRegionMonitorPoint,
 } from '@/api';
+import { OperatePermission } from '@/utils/permission-type';
 import { DictCode, HumitureType } from '@/constants';
 
 import AreaEditor from './AreaEditor.vue';
@@ -90,7 +92,7 @@ const outdoorOpen = ref<boolean>(false);
 const regionNameOpen = ref<boolean>(false);
 const monitoringDataOpen = ref<boolean>(false);
 const regionCopyOpen = ref<boolean>(false);
-
+const { booleanPermission } = useUserInfoStore();
 const regionName = ref<string>('');
 const regionNameEditor = ref<string>('');
 const groupRegions = ref<GroupRegions[]>([]);
@@ -1136,7 +1138,12 @@ const copyAreaCanvas = () => {
               <span>{{ listDisplay ? $t('envMonitor.listDisplay') : $t('envMonitor.cardDisplay') }} </span>
             </AFlex>
           </AButton>
-          <AButton type="primary" class="icon-button button-monitoring" @click="addMonitoringPoint">
+          <AButton
+            type="primary"
+            class="icon-button button-monitoring"
+            @click="addMonitoringPoint"
+            v-if="booleanPermission(OperatePermission.新增监测点)"
+          >
             <AFlex align="center">
               <SvgIcon name="plus" />
               <span> {{ $t('envMonitor.addInspectionPoints') }} </span>
@@ -1218,7 +1225,7 @@ const copyAreaCanvas = () => {
                   {{ currentAreaData?.outSideTemperature || '-' }}℃|{{ currentAreaData?.outSideHumidity || '-' }}%
                 </AButton>
                 <AFlex>
-                  <div @click="areaEditorRef?.showView">
+                  <div @click="areaEditorRef?.showView" v-if="booleanPermission(OperatePermission.编辑平面图)">
                     <AFlex justify="center" align="center" class="button-icon">
                       <SvgIcon name="edit-o" />
                     </AFlex>
@@ -1634,7 +1641,12 @@ const copyAreaCanvas = () => {
         </AForm>
         <template #footer>
           <AFlex justify="flex-end" :gap="16">
-            <AButton class="default-button" @click="deleteMonitoringPoint">{{ $t('common.delete') }}</AButton>
+            <AButton
+              v-if="booleanPermission(OperatePermission.删除监测点)"
+              class="default-button"
+              @click="deleteMonitoringPoint"
+              >{{ $t('common.delete') }}</AButton
+            >
             <AButton type="primary" @click="saveMonitoringPoint">{{ $t('common.save') }}</AButton>
           </AFlex>
         </template>

+ 5 - 2
src/views/equipment-details/EquipmentDetails.vue

@@ -5,8 +5,10 @@ import { message } from 'ant-design-vue';
 import dayjs from 'dayjs';
 
 import { useRequest } from '@/hooks/request';
+import { useUserInfoStore } from '@/stores/user-info';
 import { t } from '@/i18n';
 import { equipmentDetails, getDeviceParams, noPaginationDevicesList } from '@/api';
+import { OperatePermission } from '@/utils/permission-type';
 
 import BasicInformation from '../create-device/BasicInformation.vue';
 import DetailedInformation from '../create-device/DetailedInformation.vue';
@@ -190,6 +192,7 @@ const customizationColumns = [
   },
 ];
 
+const { booleanPermission } = useUserInfoStore();
 const route = useRoute();
 const { isLoading, handleRequest } = useRequest();
 const router = useRouter();
@@ -628,7 +631,7 @@ const goBack = () => {
             :placeholder="$t('common.plzSelect')"
             @change="selectDeviceId"
           />
-          <AButton class="previous default-button" @click="switchDevices(1)">{{ $t('common.previous') }}</AButton>
+          <AButton class="default-button previous" @click="switchDevices(1)">{{ $t('common.previous') }}</AButton>
           <AButton class="default-button" @click="switchDevices(2)">{{ $t('common.next') }}</AButton>
         </div>
       </AFlex>
@@ -665,7 +668,7 @@ const goBack = () => {
           <div :class="switchTabs(1)" @click="addSwitchTabs">{{ $t('deviceList.realTimeMonitoring') }}</div>
           <div :class="switchTabs(2)" @click="addSwitchTabs">{{ $t('deviceList.basicInformation') }}</div>
         </AFlex>
-        <div @click="editorEquipment">
+        <div @click="editorEquipment" v-if="booleanPermission(OperatePermission.编辑设备)">
           <AFlex v-if="showTabs" align="center" class="pointer">
             <SvgIcon name="edit-o" />
             <div class="editor-style">{{ $t('common.editor') }}</div>

+ 10 - 2
src/views/gateway-list/GatewayList.vue

@@ -5,6 +5,7 @@ import ModalGuidance from '@/layout/ModalGuidance.vue';
 import ConfirmModal from '@/components/ConfirmModal.vue';
 import SvgIcon from '@/components/SvgIcon.vue';
 import { useRequest } from '@/hooks/request';
+import { useUserInfoStore } from '@/stores/user-info';
 import { t } from '@/i18n';
 import {
   addGatewayLinkBatchUpdate,
@@ -14,6 +15,7 @@ import {
   obtainListPhysicalInterfaces,
   orgGatewayUnregister,
 } from '@/api';
+import { OperatePermission } from '@/utils/permission-type';
 
 import RegisterGateway from '../register-gateway/RegisterGateway.vue';
 
@@ -81,6 +83,7 @@ const agreementColumns = [
   },
 ];
 
+const { booleanPermission } = useUserInfoStore();
 const gatewayData = ref<GatewayListItem[]>([]);
 const interfaceList = ref<InterfaceLsit[]>([]);
 const interfaceOriginalData = ref<InterfaceLsit[]>([]);
@@ -288,7 +291,12 @@ onMounted(() => {
     <AFlex justify="space-between">
       <div class="text-top">{{ $t('navigation.gatewayManage') }}</div>
       <div>
-        <AButton type="primary" class="icon-button" @click="addGateway">
+        <AButton
+          type="primary"
+          class="icon-button"
+          @click="addGateway"
+          v-if="booleanPermission(OperatePermission.新增网关)"
+        >
           <AFlex align="center">
             <SvgIcon name="plus" />
             <span> {{ $t('common.add') }} </span>
@@ -346,7 +354,7 @@ onMounted(() => {
                 <div v-if="record.state === 0" class="tag-style default">{{ $t('common.offline') }}</div>
                 <div v-else class="tag-style success">{{ $t('common.online') }}</div>
               </template>
-              <template v-else-if="column.key === 'action'">
+              <template v-else-if="column.key === 'action' && booleanPermission(OperatePermission.删除网关)">
                 <SvgIcon @click="addGatewayDelete(record.id)" class="icon-delete" name="delete" />
               </template>
             </template>

+ 21 - 3
src/views/gateway-protocol/GatewayProtocol.vue

@@ -6,9 +6,11 @@ import ModalGuidance from '@/layout/ModalGuidance.vue';
 import SvgIcon from '@/components/SvgIcon.vue';
 import { useRequest } from '@/hooks/request';
 import { useViewVisible } from '@/hooks/view-visible';
+import { useUserInfoStore } from '@/stores/user-info';
 import { t } from '@/i18n';
 import { deleteProtocolBaseInfo, getProtocolList, updateProtocolBaseInfo } from '@/api';
 import { getTablePageSorts } from '@/utils';
+import { OperatePermission } from '@/utils/permission-type';
 
 import ProtocolContent from '../setup-protocol/ProtocolContent.vue';
 import SetupProtocol from '../setup-protocol/SetupProtocol.vue';
@@ -16,6 +18,7 @@ import SetupProtocol from '../setup-protocol/SetupProtocol.vue';
 import type { ColumnType, TableProps } from 'ant-design-vue/es/table';
 import type { PageParams, ProtocolBaseInfo } from '@/types';
 
+const { booleanPermission } = useUserInfoStore();
 const protocolInitialInfo: Partial<ProtocolBaseInfo> = {
   id: undefined,
   protocolName: '',
@@ -174,7 +177,12 @@ const handleOk = () => {
 <template>
   <AFlex justify="space-between">
     <div class="hvac-layout-main-title">{{ $t('navigation.protocolManage') }}</div>
-    <AButton class="icon-button add-button" type="primary" @click="handleAdd">
+    <AButton
+      class="icon-button add-button"
+      type="primary"
+      @click="handleAdd"
+      v-if="booleanPermission(OperatePermission.新增协议)"
+    >
       <SvgIcon name="plus" />
       {{ $t('common.addNew') }}
     </AButton>
@@ -214,8 +222,18 @@ const handleOk = () => {
     >
       <template #bodyCell="{ column, record }">
         <span v-if="column.key === 'action'">
-          <SvgIcon class="action-icon" name="edit-o" @click="handleEdit(record as ProtocolBaseInfo)" />
-          <SvgIcon class="action-icon" name="delete" @click="handleDelete(record as ProtocolBaseInfo)" />
+          <SvgIcon
+            v-if="booleanPermission(OperatePermission.编辑协议)"
+            class="action-icon"
+            name="edit-o"
+            @click="handleEdit(record as ProtocolBaseInfo)"
+          />
+          <SvgIcon
+            v-if="booleanPermission(OperatePermission.删除协议)"
+            class="action-icon"
+            name="delete"
+            @click="handleDelete(record as ProtocolBaseInfo)"
+          />
         </span>
       </template>
     </ATable>

+ 24 - 2
src/views/log-center/LogCenter.vue

@@ -2,18 +2,24 @@
 import { computed, onMounted, ref } from 'vue';
 
 import { useRefreshView } from '@/hooks/refresh-view';
+import { useUserInfoStore } from '@/stores/user-info';
 import { t } from '@/i18n';
+import { ViewPermission } from '@/utils/permission-type';
 
 import OperateLog from './OperateLog.vue';
 import SmartCtrlLog from './SmartCtrlLog.vue';
 
 import type { TabComponent } from '@/types';
 
+const { booleanPermission } = useUserInfoStore();
 const { renderView, refreshView } = useRefreshView();
 const activeKey = ref('');
 
 const logTabs = computed<TabComponent[]>(() => {
-  return [
+  const smartCtrlLog = booleanPermission(ViewPermission.智控日志);
+  const operateLog = booleanPermission(ViewPermission.操作日志);
+  const result: TabComponent[] = [];
+  const data = [
     {
       key: 'smartCtrlLog',
       name: t('logCenter.smartControlLogs'),
@@ -25,6 +31,16 @@ const logTabs = computed<TabComponent[]>(() => {
       component: OperateLog,
     },
   ];
+
+  if (smartCtrlLog) {
+    result.push(data.find((item) => item.key === 'smartCtrlLog') as TabComponent);
+  }
+
+  if (operateLog) {
+    result.push(data.find((item) => item.key === 'operateLog') as TabComponent);
+  }
+
+  return result;
 });
 
 onMounted(() => {
@@ -34,7 +50,13 @@ onMounted(() => {
 </script>
 
 <template>
-  <ATabs class="button-tabs-compact" v-model:active-key="activeKey" type="card" @tab-click="refreshView">
+  <ATabs
+    class="button-tabs-compact"
+    v-model:active-key="activeKey"
+    type="card"
+    @tab-click="refreshView"
+    v-if="logTabs.length"
+  >
     <ATabPane v-for="item in logTabs" :key="item.key" :tab="item.name">
       <component v-if="activeKey === item.key && renderView" :is="item.component" />
     </ATabPane>

+ 2 - 2
src/views/login-component/LoginView.vue

@@ -101,7 +101,7 @@ const clickReturn = () => {
               </AInput>
             </AFormItem>
             <AFormItem :label="t('userManage.password')" name="password">
-              <AInput
+              <AInputPassword
                 class="input-width"
                 v-model:value="loginForm.password"
                 :placeholder="t('registerGateway.pleasePassword')"
@@ -109,7 +109,7 @@ const clickReturn = () => {
                 <template #prefix>
                   <SvgIcon name="password" class="icon-style" />
                 </template>
-              </AInput>
+              </AInputPassword>
             </AFormItem>
           </AForm>
           <AButton type="primary" class="button-style" @click="addLog">{{ t('logView.login') }}</AButton>

+ 15 - 2
src/views/organization-manage/OrganizationManage.vue

@@ -6,14 +6,17 @@ import dayjs from 'dayjs';
 import ConfirmModal from '@/components/ConfirmModal.vue';
 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 { OperatePermission } from '@/utils/permission-type';
 
 import EstablishOrganization from '../create-customer/EstablishOrganization.vue';
 
 import type { FormInstance } from 'ant-design-vue';
 import type { CreateCustomer, FormRules, OrganizationListItem, RegisterGatewayForm } from '@/types';
 
+const { booleanPermission } = useUserInfoStore();
 const organizationOpen = ref<boolean>(false);
 const establishOrganizationRef = useTemplateRef('establishOrganization');
 const organizationRefs = ref<FormInstance>();
@@ -202,7 +205,12 @@ onMounted(() => {
       <div class="text-top">{{ t('navigation.organizationManage') }}</div>
       <AFlex align="center">
         <!--<div class="text-restriction">还可创建1个</div> -->
-        <AButton type="primary" class="icon-button" @click="addOrganization">
+        <AButton
+          type="primary"
+          class="icon-button"
+          @click="addOrganization"
+          v-if="booleanPermission(OperatePermission.新增组织)"
+        >
           <AFlex align="center">
             <SvgIcon name="plus" />
             <span> {{ t('common.add') }} </span>
@@ -231,7 +239,12 @@ onMounted(() => {
           <ATable :columns="organizationColumns" :data-source="organizationList" :pagination="false">
             <template #bodyCell="{ column, record }">
               <template v-if="column.key === 'action'">
-                <SvgIcon @click="organizationEditor(record.id)" class="icon-style" name="edit-o" />
+                <SvgIcon
+                  v-if="booleanPermission(OperatePermission.编辑组织)"
+                  @click="organizationEditor(record.id)"
+                  class="icon-style"
+                  name="edit-o"
+                />
                 <!-- <SvgIcon @click="organizationDelete(record.id)" class="icon-style" name="delete" /> -->
               </template>
             </template>

+ 26 - 5
src/views/role-manage/RoleManage.vue

@@ -6,6 +6,7 @@ import ConfirmModal from '@/components/ConfirmModal.vue';
 import OrganizationalStructure from '@/components/OrganizationalStructure.vue';
 import SvgIcon from '@/components/SvgIcon.vue';
 import { useRequest } from '@/hooks/request';
+import { useUserInfoStore } from '@/stores/user-info';
 import { t } from '@/i18n';
 import {
   addCharacter,
@@ -18,6 +19,7 @@ import {
   getSubOrgsByToken,
   updateCharacter,
 } from '@/api';
+import { OperatePermission } from '@/utils/permission-type';
 
 import type { DataNode, TreeProps } from 'ant-design-vue/es/tree';
 import type { Key } from 'ant-design-vue/es/vc-tree/interface';
@@ -30,6 +32,7 @@ interface HaracterItem {
   show: boolean;
 }
 
+const { booleanPermission } = useUserInfoStore();
 const characterList = ref<HaracterItem[]>([]);
 const { handleRequest } = useRequest();
 const characterListId = ref();
@@ -372,7 +375,11 @@ onMounted(() => {
       <div class="content">
         <AFlex justify="space-between" align="center" class="content-top">
           <div class="content-text">{{ t('userManage.role') }}</div>
-          <div class="icon-style pointer" @click="addCharacterName">
+          <div
+            class="icon-style pointer"
+            @click="addCharacterName"
+            v-if="booleanPermission(OperatePermission.新增角色)"
+          >
             <AFlex align="center"
               ><SvgIcon name="plus" />
               <div class="text-left">{{ t('common.add') }}</div>
@@ -399,8 +406,18 @@ onMounted(() => {
             >
               <div class="pointer text-height" @click="clickCharacter(item.id!)">{{ item.name }}</div>
               <div v-if="item.id === characterListId">
-                <SvgIcon class="pointer" name="edit-o" @click="addEditor(index)" />
-                <SvgIcon class="pointer icon-left" @click="addDelete(item.id)" name="delete" />
+                <SvgIcon
+                  v-if="booleanPermission(OperatePermission.编辑角色)"
+                  class="pointer"
+                  name="edit-o"
+                  @click="addEditor(index)"
+                />
+                <SvgIcon
+                  v-if="booleanPermission(OperatePermission.删除角色)"
+                  class="pointer icon-left"
+                  @click="addDelete(item.id)"
+                  name="delete"
+                />
               </div>
             </AFlex>
           </div>
@@ -410,13 +427,17 @@ onMounted(() => {
       <div class="permission-management">
         <AFlex justify="space-between" align="center" class="content-top">
           <div class="content-text">{{ t('roleManage.permission') }}</div>
-          <div class="pointer" @click="editorPermission" v-if="editorChecked">
+          <div
+            class="pointer"
+            @click="editorPermission"
+            v-if="editorChecked && booleanPermission(OperatePermission.编辑角色)"
+          >
             <AFlex align="center"
               ><SvgIcon name="edit-o" />
               <div class="text-left">{{ t('common.editor') }}</div>
             </AFlex>
           </div>
-          <AFlex v-else>
+          <AFlex v-if="!editorChecked">
             <div class="pointer" @click="cancelPermission">
               <AFlex align="center"
                 ><SvgIcon name="close" />

+ 25 - 8
src/views/user-manage/UserManage.vue

@@ -6,13 +6,16 @@ import dayjs, { Dayjs } from 'dayjs';
 import ConfirmModal from '@/components/ConfirmModal.vue';
 import OrganizationalStructure from '@/components/OrganizationalStructure.vue';
 import { useRequest } from '@/hooks/request';
+import { useUserInfoStore } from '@/stores/user-info';
 import { t } from '@/i18n';
 import { addAccount, batchDeleteAccount, getFindRolesByOrgIds, getUserPageList, updateAccount } from '@/api';
+import { OperatePermission } from '@/utils/permission-type';
 
 import type { FormInstance, Rule } from 'ant-design-vue/es/form';
 import type { Key } from 'ant-design-vue/es/table/interface';
 import type { AccountForm, CharacterPageItem, UserPageItem, UserPageParams } from '@/types';
 
+const { booleanPermission } = useUserInfoStore();
 const modalComponentRef = useTemplateRef('modalComponent');
 const { handleRequest } = useRequest();
 const accountKeys = ref<Key[]>([]);
@@ -250,13 +253,22 @@ const getUserList = () => {
     <AFlex justify="space-between" class="account-header">
       <div class="text-top">{{ t('userManage.accountManagement') }}</div>
       <div>
-        <AButton class="icon-button default-button" @click="addDelete">
+        <AButton
+          v-if="booleanPermission(OperatePermission.删除账户)"
+          class="icon-button default-button"
+          @click="addDelete"
+        >
           <AFlex align="center">
             <SvgIcon name="delete" />
             <span>{{ t('common.delete') }}</span>
           </AFlex>
         </AButton>
-        <AButton type="primary" class="icon-button button-monitoring" @click="addOpenAccount">
+        <AButton
+          v-if="booleanPermission(OperatePermission.新增账户)"
+          type="primary"
+          class="icon-button button-monitoring"
+          @click="addOpenAccount"
+        >
           <AFlex align="center">
             <SvgIcon name="plus" />
             <span> {{ t('common.add') }} </span>
@@ -316,11 +328,15 @@ const getUserList = () => {
           <AButton class="default-button margin-left" @click="addReset"> {{ t('common.reset') }} </AButton>
         </AFlex>
         <ATable
-          :row-selection="{
-            type: 'checkbox',
-            selectedRowKeys: accountKeys,
-            onChange: accountChange,
-          }"
+          :row-selection="
+            booleanPermission(OperatePermission.删除账户)
+              ? {
+                  type: 'checkbox',
+                  selectedRowKeys: accountKeys,
+                  onChange: accountChange,
+                }
+              : undefined
+          "
           row-key="id"
           :columns="accountColumns"
           :data-source="accountList"
@@ -328,7 +344,8 @@ const getUserList = () => {
         >
           <template #bodyCell="{ column, record }">
             <template v-if="column.key === 'mobile'">
-              <div @click="addCheck(record as UserPageItem)" class="mobile-phone">{{ record.mobile }}</div>
+              <div v-if="!booleanPermission(OperatePermission.编辑账户)">{{ record.mobile }}</div>
+              <div v-else @click="addCheck(record as UserPageItem)" class="mobile-phone">{{ record.mobile }}</div>
             </template>
             <template v-if="column.key === 'enabled'">
               <div v-if="record.enabled == 1" class="tag-style success">{{ t('common.activating') }}</div>