1
0

9 Revīzijas 04264f3948 ... c2d6e568ba

Autors SHA1 Ziņojums Datums
  wangcong c2d6e568ba feat(views): 优化设备工况冷水主机卡片 1 nedēļu atpakaļ
  wangcong 5288d3422d fix(views): 修复点击工况卡片跳到详情页后不能返回工况页面的问题 1 nedēļu atpakaļ
  wangshun 640a0a16f0 perf(views): 修复“环境监控”运行状态判断选择参数异常问题 1 nedēļu atpakaļ
  wangcong b9b1863639 perf(views): 优化“设备工况”模块参数的历史数据曲线 1 nedēļu atpakaļ
  wangcong f20e14a722 feat(views): 优化设备工况冷水主机卡片 1 nedēļu atpakaļ
  wangcong c8b2a66e03 chore(constants): 更新设备参数编码 1 nedēļu atpakaļ
  wangshun 80d9c602f0 perf(views): 编写"角色管理"页面角色新增,编辑,删除功能 1 nedēļu atpakaļ
  wangshun e4d5a82979 perf(views): 编写"账号管理"页面编辑,删除,查询等功能 1 nedēļu atpakaļ
  wangshun 4db4d00463 chore(api): 添加用户管理模块相关接口 1 nedēļu atpakaļ

+ 40 - 3
src/api/index.ts

@@ -24,6 +24,7 @@ import type {
   ChangeState,
   CharacterPageItem,
   CharacterPageItemData,
+  CharacterParams,
   CoolingHistoryDataQuery,
   CoolingHistoryDataResult,
   CoolingRealTimeDataQuery,
@@ -114,6 +115,7 @@ import type {
   TempHumidityControlSettings,
   TreeStructure,
   UploadLogo,
+  UserPageItemData,
   UserPageParams,
   VerificationAgreement,
   VerificationEquipment,
@@ -195,6 +197,21 @@ export const getFindRolesByOrgIds = async (params: number[]) => {
 };
 
 // 角色信息表
+
+export const addCharacter = async (params: CharacterParams) => {
+  await request(apiSys('/sysRole/add'), {
+    method: 'POST',
+    body: JSON.stringify(params),
+  });
+};
+
+export const updateCharacter = async (params: CharacterParams) => {
+  await request(apiSys('/sysRole/update'), {
+    method: 'POST',
+    body: JSON.stringify(params),
+  });
+};
+
 export const getCharacterPageList = async (params: PageParams) => {
   const data = await request<CharacterPageItemData>(apiSys('/sysRole/getPageList'), {
     method: 'POST',
@@ -203,6 +220,12 @@ export const getCharacterPageList = async (params: PageParams) => {
   return data;
 };
 
+export const deleteCharacter = async (id: number) => {
+  await request(apiSys(`/sysRole/delete/${id}`), {
+    method: 'POST',
+  });
+};
+
 // 组织表
 export const addOrganization = async (params: OrganizationItem) => {
   const data = await request<number>(apiSys('/sysOrg/add'), {
@@ -213,7 +236,7 @@ export const addOrganization = async (params: OrganizationItem) => {
 };
 
 export const updateOrganization = async (params: OrganizationItem) => {
-  request<number>(apiSys('/sysOrg/update'), {
+  await request<number>(apiSys('/sysOrg/update'), {
     method: 'POST',
     body: JSON.stringify(params),
   });
@@ -236,14 +259,28 @@ export const getSubOrgsByToken = async () => {
 
 // 用户信息表
 export const addAccount = async (params: AccountParams) => {
-  request(apiSys('/sysUser/add'), {
+  await request(apiSys('/sysUser/add'), {
+    method: 'POST',
+    body: JSON.stringify(params),
+  });
+};
+
+export const updateAccount = async (params: AccountParams) => {
+  await request(apiSys('/sysUser/update'), {
+    method: 'POST',
+    body: JSON.stringify(params),
+  });
+};
+
+export const batchDeleteAccount = async (params: number[]) => {
+  await request(apiSys('/sysUser/batchDelete/'), {
     method: 'POST',
     body: JSON.stringify(params),
   });
 };
 
 export const getUserPageList = async (params: UserPageParams) => {
-  const data = await request<number>(apiSys('/sysUser/getPageList'), {
+  const data = await request<UserPageItemData>(apiSys('/sysUser/getPageList'), {
     method: 'POST',
     body: JSON.stringify(params),
   });

+ 6 - 6
src/constants/device-params.ts

@@ -61,12 +61,12 @@ export const enum DevParamChillerUnit {
   本月耗电量 = 'energyMonth',
   冷冻水回水温度 = 'evapWaterTempIn',
   冷冻水出水温度 = 'evapWaterTempOut',
-  蒸发压力 = 'cir_1_evapPre',
-  蒸发温度 = 'cir_1_evapTemp',
+  蒸发压力 = 'cirEvapPre',
+  蒸发温度 = 'cirEvapTemp',
   冷却水回水温度 = 'condWaterTempIn',
   冷却水出水温度 = 'condWaterTempOut',
-  冷凝压力 = 'cir_1_condPre',
-  冷凝温度 = 'cir_1_condTemp',
-  排气温度 = 'cir_1_comp_1_disTemp',
-  吸气温度 = 'cir_1_comp_1_sucTemp',
+  冷凝压力 = 'cirCondPre',
+  冷凝温度 = 'cirCondTemp',
+  排气温度 = 'cirCompDisTemp',
+  吸气温度 = 'cirCompSucTemp',
 }

+ 29 - 2
src/types/index.ts

@@ -1006,6 +1006,7 @@ export interface DevicesListItem {
   controlTypeName: string;
   compressionLevelName: string;
   voltageLevelName: string;
+  enableCopSet: boolean;
 }
 
 export interface DeviceGroup {
@@ -1054,6 +1055,7 @@ export interface DeviceTypeCount {
 export interface DevWorkCardProps<T extends string> {
   realTimeData?: Partial<Record<T, number | string>>;
   deviceDetail: Partial<Record<string, number>>;
+  enableCopSet: boolean;
 }
 
 export interface DevWorkRealTimeData {
@@ -2406,7 +2408,7 @@ export interface UserPageParams {
   startTenancy: string;
   endTenancy: string;
   enabled?: string;
-  orgId: number;
+  orgId?: number;
   roleId?: number;
 }
 
@@ -2428,5 +2430,30 @@ export interface AccountForm {
   password: string;
   enabled: boolean;
   roleId?: number;
-  accountTerm: [Dayjs, Dayjs];
+  accountTerm?: [Dayjs, Dayjs];
+}
+
+export type UserPageItemData = PageData<UserPageItem>;
+export interface UserPageItem {
+  id: number;
+  createTime: string;
+  updateTime: string;
+  createUserId: number;
+  updateUserId: number;
+  userName: string;
+  email: string;
+  mobile: string;
+  avatar: string;
+  password: string;
+  startTenancy: string;
+  endTenancy: string;
+  enabled: number;
+  deleted: string;
+  roleId: number;
+}
+
+export interface CharacterParams {
+  id?: number;
+  roleName: string;
+  orgId?: number;
 }

+ 2 - 2
src/views/device-work-status/DevWorkParamData.vue

@@ -187,9 +187,9 @@ const option = computed<EChartsOption>(() => {
     series,
     grid: {
       top: 10,
-      right: 0,
+      right: 30,
       bottom: 20,
-      left: 0,
+      left: 30,
       containLabel: true,
     },
   };

+ 13 - 3
src/views/device-work-status/DeviceWorkStatus.vue

@@ -49,12 +49,13 @@ onMounted(() => {
     });
 
     if (deviceTypes.value.length) {
-      if (route.query.deviceType && route.query.deviceId) {
+      activeDeviceId.value = route.query.deviceId ? Number(route.query.deviceId) : undefined;
+
+      if (route.query.deviceType) {
         const deviceType = Number(route.query.deviceType);
         const isDeviceTypeExisted = deviceTypes.value.find((item) => item.deviceType === deviceType);
 
         if (isDeviceTypeExisted) {
-          activeDeviceId.value = Number(route.query.deviceId);
           activeDeviceType.value = deviceType;
 
           router.replace({
@@ -206,7 +207,14 @@ const handleDevCardClick = (devId: number, e: Event) => {
     currentDevId.value = devId;
     viewHistoryData(paramCode);
   } else {
-    router.push(`/device-manage/device-list/equipment-details/${devId}`);
+    router.push({
+      path: `/device-manage/device-list/equipment-details/${devId}`,
+      query: {
+        from: 'deviceWorkStatus',
+        groupId: props.deviceGroupId,
+        deviceType: activeDeviceType.value,
+      },
+    });
   }
 };
 </script>
@@ -253,6 +261,7 @@ const handleDevCardClick = (devId: number, e: Event) => {
               </div>
               <template
                 v-if="
+                  item.enableCopSet &&
                   activeDeviceType === DeviceType.冷水主机 &&
                   deviceRealTimeData[item.id]?.[DevParamChillerUnit.COP] !== undefined
                 "
@@ -269,6 +278,7 @@ const handleDevCardClick = (devId: number, e: Event) => {
               :is="deviceCardData[activeDeviceType]?.component"
               :real-time-data="deviceRealTimeData[item.id]"
               :device-detail="JSON.parse(item.deviceDetail || '{}')"
+              :enable-cop-set="item.enableCopSet"
             />
           </div>
         </ACol>

+ 60 - 49
src/views/device-work-status/device-card/ChillerUnit.vue

@@ -47,6 +47,11 @@ ${t('deviceWorkStatus.chillerUnit.activePower')}: ${activePower ?? '-'}kW
 ${t('deviceList.ratedPower')}: ${powerRating ?? '-'}kW`;
 });
 
+const showEvapCondTempPre = computed(() => {
+  const { numberUnitCircuits } = props.deviceDetail;
+  return numberUnitCircuits === 1;
+});
+
 const showDisSucTemp = computed(() => {
   const { numberUnitCircuits, singleLoopCompressor } = props.deviceDetail;
   return numberUnitCircuits === 1 && singleLoopCompressor === 1;
@@ -57,7 +62,7 @@ const showDisSucTemp = computed(() => {
   <div>
     <div class="chiller-unit-top">
       <div class="chiller-unit-left">
-        <div>
+        <div v-if="enableCopSet">
           <div class="device-card-label">{{ $t('deviceWorkStatus.chillerUnit.coolingCapacity') }} (kW)</div>
           <ATooltip overlay-class-name="hvac-tooltip">
             <template #title>{{ coolingCapacityTip }}</template>
@@ -68,6 +73,10 @@ const showDisSucTemp = computed(() => {
             />
           </ATooltip>
         </div>
+        <div v-else>
+          <div class="device-card-label"></div>
+          <div class="device-card-value device-card-no-history"></div>
+        </div>
         <div>
           <div class="device-card-label">{{ $t('deviceWorkStatus.chillerUnit.activePower') }} (kW)</div>
           <ATooltip overlay-class-name="hvac-tooltip">
@@ -142,54 +151,56 @@ const showDisSucTemp = computed(() => {
             {{ realTimeData?.[DevParamChillerUnit.冷却水出水温度] }}°C
           </ATooltip>
         </div>
-        <div
-          v-if="realTimeData?.[DevParamChillerUnit.蒸发压力] !== undefined"
-          class="chiller-unit-img-text evap-pre"
-          :data-param-code="DevParamChillerUnit.蒸发压力"
-        >
-          <ATooltip>
-            <template #title>
-              {{ $t('deviceWorkStatus.chillerUnit.evapPre') }}: {{ realTimeData?.[DevParamChillerUnit.蒸发压力] }}kPa
-            </template>
-            {{ realTimeData?.[DevParamChillerUnit.蒸发压力] }}kPa
-          </ATooltip>
-        </div>
-        <div
-          v-if="realTimeData?.[DevParamChillerUnit.蒸发温度] !== undefined"
-          class="chiller-unit-img-text evap-temp"
-          :data-param-code="DevParamChillerUnit.蒸发温度"
-        >
-          <ATooltip>
-            <template #title>
-              {{ $t('deviceWorkStatus.chillerUnit.evapTemp') }}: {{ realTimeData?.[DevParamChillerUnit.蒸发温度] }}°C
-            </template>
-            {{ realTimeData?.[DevParamChillerUnit.蒸发温度] }}°C
-          </ATooltip>
-        </div>
-        <div
-          v-if="realTimeData?.[DevParamChillerUnit.冷凝压力] !== undefined"
-          class="chiller-unit-img-text cond-pre"
-          :data-param-code="DevParamChillerUnit.冷凝压力"
-        >
-          <ATooltip>
-            <template #title>
-              {{ $t('deviceWorkStatus.chillerUnit.condPre') }}: {{ realTimeData?.[DevParamChillerUnit.冷凝压力] }}kPa
-            </template>
-            {{ realTimeData?.[DevParamChillerUnit.冷凝压力] }}kPa
-          </ATooltip>
-        </div>
-        <div
-          v-if="realTimeData?.[DevParamChillerUnit.冷凝温度] !== undefined"
-          class="chiller-unit-img-text cond-temp"
-          :data-param-code="DevParamChillerUnit.冷凝温度"
-        >
-          <ATooltip>
-            <template #title>
-              {{ $t('deviceWorkStatus.chillerUnit.condTemp') }}: {{ realTimeData?.[DevParamChillerUnit.冷凝温度] }}°C
-            </template>
-            {{ realTimeData?.[DevParamChillerUnit.冷凝温度] }}°C
-          </ATooltip>
-        </div>
+        <template v-if="showEvapCondTempPre">
+          <div
+            v-if="realTimeData?.[DevParamChillerUnit.蒸发压力] !== undefined"
+            class="chiller-unit-img-text evap-pre"
+            :data-param-code="DevParamChillerUnit.蒸发压力"
+          >
+            <ATooltip>
+              <template #title>
+                {{ $t('deviceWorkStatus.chillerUnit.evapPre') }}: {{ realTimeData?.[DevParamChillerUnit.蒸发压力] }}kPa
+              </template>
+              {{ realTimeData?.[DevParamChillerUnit.蒸发压力] }}kPa
+            </ATooltip>
+          </div>
+          <div
+            v-if="realTimeData?.[DevParamChillerUnit.蒸发温度] !== undefined"
+            class="chiller-unit-img-text evap-temp"
+            :data-param-code="DevParamChillerUnit.蒸发温度"
+          >
+            <ATooltip>
+              <template #title>
+                {{ $t('deviceWorkStatus.chillerUnit.evapTemp') }}: {{ realTimeData?.[DevParamChillerUnit.蒸发温度] }}°C
+              </template>
+              {{ realTimeData?.[DevParamChillerUnit.蒸发温度] }}°C
+            </ATooltip>
+          </div>
+          <div
+            v-if="realTimeData?.[DevParamChillerUnit.冷凝压力] !== undefined"
+            class="chiller-unit-img-text cond-pre"
+            :data-param-code="DevParamChillerUnit.冷凝压力"
+          >
+            <ATooltip>
+              <template #title>
+                {{ $t('deviceWorkStatus.chillerUnit.condPre') }}: {{ realTimeData?.[DevParamChillerUnit.冷凝压力] }}kPa
+              </template>
+              {{ realTimeData?.[DevParamChillerUnit.冷凝压力] }}kPa
+            </ATooltip>
+          </div>
+          <div
+            v-if="realTimeData?.[DevParamChillerUnit.冷凝温度] !== undefined"
+            class="chiller-unit-img-text cond-temp"
+            :data-param-code="DevParamChillerUnit.冷凝温度"
+          >
+            <ATooltip>
+              <template #title>
+                {{ $t('deviceWorkStatus.chillerUnit.condTemp') }}: {{ realTimeData?.[DevParamChillerUnit.冷凝温度] }}°C
+              </template>
+              {{ realTimeData?.[DevParamChillerUnit.冷凝温度] }}°C
+            </ATooltip>
+          </div>
+        </template>
         <template v-if="showDisSucTemp">
           <div
             v-if="realTimeData?.[DevParamChillerUnit.排气温度] !== undefined"

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

@@ -448,6 +448,9 @@ const getDeviceListOptionsList = (id: number, show: boolean, runStatusParamCode:
 const adddParametersDevice = (value: SelectValue, option: DefaultOptionType) => {
   runStatusList.value = [];
   inputType.value = option.type;
+  monitoringForm.value.runStatusConditionCode = undefined;
+  monitoringForm.value.runStatusConditionValue = undefined;
+
   if (option.type === 1) {
     option.candidates.forEach((item: string, index: number) => {
       runStatusList.value.push({
@@ -1019,7 +1022,7 @@ onMounted(() => {
     temperatureDifference.value = [];
     await getParamAlgOperator();
     paramAlgOperator.value.forEach((item) => {
-      if (item.dictValueId === 250 || item.dictValueId === 251) {
+      if (item.dictEngValue === 'eq' || item.dictEngValue === 'ne') {
         temperatureDifference.value.push(item);
       }
     });

+ 17 - 1
src/views/equipment-details/EquipmentDetails.vue

@@ -590,6 +590,22 @@ watch(
 onMounted(() => {
   obtainDeviceDetails(true);
 });
+
+const goBack = () => {
+  if (route.query.from === 'deviceWorkStatus' && route.query.groupId) {
+    router.push({
+      path: `/ai-smart-ctrl/device-group/${route.query.groupId}`,
+      query: {
+        tab: 'deviceWorkStatus',
+        deviceType: route.query.deviceType,
+      },
+    });
+
+    return;
+  }
+
+  router.push('/device-manage/device-list');
+};
 </script>
 
 <template>
@@ -597,7 +613,7 @@ onMounted(() => {
     <div>
       <AFlex justify="space-between">
         <AFlex align="center">
-          <div class="return" @click="$router.push('/device-manage/device-list')">
+          <div class="return" @click="goBack">
             <SvgIcon name="left" />
           </div>
 

+ 110 - 37
src/views/role-manage/RoleManage.vue

@@ -1,45 +1,35 @@
 <script setup lang="ts">
-import { nextTick, onMounted, ref } from 'vue';
+import { nextTick, onMounted, ref, useTemplateRef } from 'vue';
 import { message } from 'ant-design-vue';
 
+import ConfirmModal from '@/components/ConfirmModal.vue';
 import OrganizationalStructure from '@/components/OrganizationalStructure.vue';
 import SvgIcon from '@/components/SvgIcon.vue';
 import { useRequest } from '@/hooks/request';
-import { getSubOrgsByToken } from '@/api';
+import { t } from '@/i18n';
+import { addCharacter, deleteCharacter, getFindRolesByOrgIds, getSubOrgsByToken, updateCharacter } from '@/api';
 
 import type { DataNode } from 'ant-design-vue/es/tree';
 
-const characterList = ref([
-  {
-    name: '管理员',
-    id: 1,
-    show: false,
-  },
-  {
-    name: '工程师',
-    id: 2,
-    show: false,
-  },
-  {
-    name: '技术员',
-    id: 3,
-    show: false,
-  },
-  {
-    name: '操作工',
-    id: 4,
-    show: false,
-  },
-]);
+interface HaracterItem {
+  name: string;
+  id?: number;
+  show: boolean;
+}
+
+const characterList = ref<HaracterItem[]>([]);
 const { handleRequest } = useRequest();
-const characterListId = ref(1);
+const characterListId = ref();
 const inputRef = ref<HTMLInputElement[]>([]); // 明确的类型声明
 const permissions = ref<string>('dataPermissions');
 const equipmentChecked = ref<boolean>(false);
 const editorChecked = ref<boolean>(true);
 const valueTime = ref<string>('1');
 const pagePermissionsSelectedKeys = ref<number[]>([]);
-
+const orgId = ref<number>();
+const characterId = ref<number>();
+const enterShow = ref<boolean>(false);
+const modalComponentRef = useTemplateRef('modalComponent');
 const pagePermissionsTree = ref<DataNode[]>([
   {
     title: '父节点 1',
@@ -57,17 +47,17 @@ const pagePermissionsTree = ref<DataNode[]>([
     ],
   },
 ]);
-const addCharacter = async () => {
+const addCharacterName = async () => {
   if (characterList.value.some((item) => item.name === '')) {
     return;
   }
   characterList.value.push({
     name: '',
-    id: 5,
+    id: undefined,
     show: true,
   });
   await nextTick();
-  console.log(inputRef.value);
+
   inputRef.value[0].focus();
 };
 const clickCharacter = (id: number) => {
@@ -85,9 +75,58 @@ const addEditor = async (index: number) => {
   await nextTick();
   inputRef.value[0].focus();
 };
-const editorcharacter = (index: number, name: string) => {
+const addDelete = (id: number | undefined) => {
+  if (!id) {
+    return message.warning(t('deviceList.pleaseSelectItemDelete'));
+  }
+  characterId.value = id;
+  modalComponentRef.value?.showView();
+};
+const confirm = () => {
+  handleRequest(async () => {
+    if (characterId.value) {
+      await deleteCharacter(characterId.value);
+      if (orgId.value) {
+        getFindRolesByOrg(orgId.value);
+      }
+    }
+
+    modalComponentRef.value?.hideView();
+  });
+};
+const editorCharacterBlur = (index: number, name: string) => {
+  if (!enterShow.value) {
+    editorCharacter(index, name);
+  }
+};
+/**
+ * 检查字符串是否包含任何空白字符(包括空格、制表符、换行等)
+ * @param str 待检测的字符串
+ * @returns 是否包含空白字符
+ */
+const hasWhitespace = (str: string): boolean => {
+  return /\s/.test(str);
+};
+const editorCharacter = (index: number, name: string) => {
   if (name) {
-    characterList.value[index].show = false;
+    if (!hasWhitespace(name)) {
+      enterShow.value = true;
+      characterList.value[index].show = false;
+      const id = characterList.value[index].id;
+      handleRequest(async () => {
+        if (id) {
+          await updateCharacter({ roleName: name, orgId: orgId.value, id });
+        } else {
+          await addCharacter({ roleName: name, orgId: orgId.value });
+        }
+        enterShow.value = false;
+        if (orgId.value) {
+          getFindRolesByOrg(orgId.value);
+        }
+      });
+    } else {
+      return message.warning('名称中不能包含空格!');
+    }
   } else {
     return message.warning('名称不能为空!');
   }
@@ -103,6 +142,32 @@ const savePermission = () => {
 };
 const clickOrganizationChange = (id: number) => {
   console.log(id);
+  orgId.value = id;
+  getFindRolesByOrg(id);
+};
+
+const getFindRolesByOrg = (id: number) => {
+  handleRequest(async () => {
+    const data = await getFindRolesByOrgIds([id]);
+    characterList.value = [];
+    if (data.length) {
+      data.forEach((item) => {
+        const { roleName, id } = item;
+        characterList.value.push({
+          name: roleName,
+          id,
+          show: false,
+        });
+      });
+      if (!characterListId.value) {
+        characterListId.value = data[0].id;
+      }
+    }
+  });
+};
+
+const addRadioGroup = () => {
+  editorChecked.value = true;
 };
 
 onMounted(() => {
@@ -120,7 +185,7 @@ onMounted(() => {
       <div class="content">
         <AFlex justify="space-between" align="center" class="content-top">
           <div class="content-text">角色</div>
-          <div class="icon-style pointer" @click="addCharacter">
+          <div class="icon-style pointer" @click="addCharacterName">
             <AFlex align="center"
               ><SvgIcon name="plus" />
               <div class="text-left">添加</div>
@@ -135,8 +200,8 @@ onMounted(() => {
                 v-model:value="item.name"
                 :bordered="false"
                 ref="inputRef"
-                @pressEnter="editorcharacter(index, item.name)"
-                @blur="editorcharacter(index, item.name)"
+                @pressEnter="editorCharacter(index, item.name)"
+                @blur="editorCharacterBlur(index, item.name)"
               />
             </div>
             <AFlex
@@ -145,10 +210,10 @@ onMounted(() => {
               align="center"
               :class="item.id === characterListId ? 'character-list character-list-color' : 'character-list'"
             >
-              <div class="pointer text-height" @click="clickCharacter(item.id)">{{ item.name }}</div>
+              <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" name="delete" />
+                <SvgIcon class="pointer icon-left" @click="addDelete(item.id)" name="delete" />
               </div>
             </AFlex>
           </div>
@@ -180,7 +245,7 @@ onMounted(() => {
           </AFlex>
         </AFlex>
 
-        <ARadioGroup v-model:value="permissions" button-style="solid" size="large">
+        <ARadioGroup v-model:value="permissions" button-style="solid" size="large" @change="addRadioGroup">
           <ARadioButton value="dataPermissions">数据权限</ARadioButton>
           <ARadioButton value="functionPermissions">功能权限</ARadioButton>
         </ARadioGroup>
@@ -227,6 +292,14 @@ onMounted(() => {
         </div>
       </div>
     </AFlex>
+    <ConfirmModal
+      ref="modalComponent"
+      :title="t('common.deleteConfirmation')"
+      :description-text="t('common.confirmDeletion')"
+      :icon="{ name: 'delete' }"
+      :icon-bg-color="'#F56C6C'"
+      @confirm="confirm"
+    />
   </div>
 </template>
 

+ 156 - 109
src/views/user-manage/UserManage.vue

@@ -1,5 +1,5 @@
 <script setup lang="ts">
-import { onMounted, ref, useTemplateRef } from 'vue';
+import { ref, useTemplateRef } from 'vue';
 import { message } from 'ant-design-vue';
 import dayjs, { Dayjs } from 'dayjs';
 
@@ -7,19 +7,21 @@ import ConfirmModal from '@/components/ConfirmModal.vue';
 import OrganizationalStructure from '@/components/OrganizationalStructure.vue';
 import { useRequest } from '@/hooks/request';
 import { t } from '@/i18n';
-import { addAccount, getFindRolesByOrgIds } from '@/api';
+import { addAccount, batchDeleteAccount, getFindRolesByOrgIds, getUserPageList, updateAccount } from '@/api';
 
 import type { FormInstance, Rule } from 'ant-design-vue/es/form';
 import type { Key } from 'ant-design-vue/es/table/interface';
-import type { AccountForm, CharacterPageItem, UserPageParams } from '@/types';
+import type { AccountForm, CharacterPageItem, UserPageItem, UserPageParams } from '@/types';
 
 const modalComponentRef = useTemplateRef('modalComponent');
 const { handleRequest } = useRequest();
 const accountKeys = ref<Key[]>([]);
-const searchContent = ref<string>();
+const searchContent = ref<string>('');
 const accountTerm = ref<[Dayjs, Dayjs]>();
 const orgId = ref<number>();
 const formRef = ref<FormInstance>();
+const titleAccount = ref<boolean>(true);
+const accountId = ref<number>(0);
 const accountPageParam = ref<UserPageParams>({
   pageIndex: 1,
   pageSize: 10,
@@ -28,12 +30,11 @@ const accountPageParam = ref<UserPageParams>({
   startTenancy: '',
   endTenancy: '',
   enabled: undefined,
-  orgId: 1,
+  orgId: undefined,
   roleId: undefined,
 });
 const accountTotal = ref<number>();
 const accountOpen = ref<boolean>(false);
-const checkOpen = ref<boolean>(false);
 const characterPageList = ref<CharacterPageItem[]>([]);
 const accountForm = ref<AccountForm>({
   userName: '',
@@ -41,77 +42,76 @@ const accountForm = ref<AccountForm>({
   password: '',
   enabled: true,
   roleId: undefined,
-  accountTerm: [dayjs(), dayjs()],
+  accountTerm: undefined,
 });
-const accountList = ref([
-  {
-    deviceName: '15972030052',
-    deviceName1: '王某某',
-    deviceName2: '管理员',
-    deviceName3: '2024-12-31 23:12:00',
-    deviceName4: '2024-12-31 23:12:00',
-    deviceName5: 1,
-  },
-  {
-    deviceName: '15872040051',
-    deviceName1: '李某某',
-    deviceName2: '操作工',
-    deviceName3: '2024-12-31 23:12:00',
-    deviceName4: '2024-12-31 23:12:00',
-    deviceName5: 0,
-  },
-]);
+const accountList = ref<UserPageItem[]>([]);
 const accountColumns = [
   {
     title: '手机号',
-    dataIndex: 'deviceName',
-    key: 'deviceName',
+    dataIndex: 'mobile',
+    key: 'mobile',
     ellipsis: true,
   },
   {
     title: '姓名',
-    dataIndex: 'deviceName1',
-    key: 'deviceName1',
+    dataIndex: 'userName',
+    key: 'userName',
     ellipsis: true,
   },
   {
     title: '角色',
-    dataIndex: 'deviceName2',
-    key: 'deviceName2',
+    dataIndex: 'roleName',
+    key: 'roleName',
     ellipsis: true,
   },
   {
-    title: '创建时间',
-    dataIndex: 'deviceName3',
-    key: 'deviceName3',
+    title: '创建日期',
+    dataIndex: 'startTenancy',
+    key: 'startTenancy',
     ellipsis: true,
   },
   {
-    title: '到期时间',
-    dataIndex: 'deviceName4',
-    key: 'deviceName4',
+    title: '到期日期',
+    dataIndex: 'endTenancy',
+    key: 'endTenancy',
     ellipsis: true,
   },
   {
     title: '状态',
-    dataIndex: 'deviceName5',
-    key: 'deviceName5',
+    dataIndex: 'enabled',
+    key: 'enabled',
     ellipsis: true,
   },
 ];
 const rules: Record<string, Rule[]> = {
   userName: [{ required: true, message: t('common.cannotEmpty'), trigger: 'change' }],
-  mobile: [{ required: true, message: t('common.cannotEmpty'), trigger: 'change' }],
+  mobile: [
+    { required: true, message: t('common.cannotEmpty'), trigger: 'change' },
+    {
+      validator: (_rule: unknown, value: string) => {
+        if (!isValidPhone(value)) {
+          return Promise.reject('手机号格式错误');
+        }
+        return Promise.resolve();
+      },
+    },
+  ],
   password: [{ required: true, message: t('common.cannotEmpty'), trigger: 'change' }],
   enabled: [{ required: true, message: t('common.cannotEmpty'), trigger: 'change' }],
   roleId: [{ required: true, message: t('common.cannotEmpty'), trigger: 'change' }],
   accountTerm: [{ required: true, message: t('common.cannotEmpty'), trigger: 'change' }],
 };
+// 手机号校验函数
+const isValidPhone = (phone: string): boolean => {
+  return /^1[3-9]\d{9}$/.test(phone);
+};
 const accountChange = (selectedRowKeys: Key[]) => {
   accountKeys.value = selectedRowKeys;
 };
 const confirm = () => {
   handleRequest(async () => {
+    await batchDeleteAccount(accountKeys.value as number[]);
+    getUserList();
     modalComponentRef.value?.hideView();
   });
 };
@@ -123,12 +123,62 @@ const addDelete = () => {
 };
 const addOpenAccount = () => {
   accountOpen.value = true;
+  titleAccount.value = true;
+};
+const addCheck = (item: UserPageItem) => {
+  accountOpen.value = true;
+  titleAccount.value = false;
+
+  const { userName, mobile, roleId, enabled, startTenancy, endTenancy, id } = item;
+  accountId.value = id;
+  Object.assign(accountForm.value, {
+    userName,
+    mobile,
+    roleId,
+    password: '',
+    enabled: enabled === 1 ? true : false,
+    accountTerm: [dayjs(startTenancy, 'YYYY-MM-DD'), dayjs(endTenancy, 'YYYY-MM-DD')],
+  });
+};
+const addQuery = () => {
+  if (accountTerm.value) {
+    accountPageParam.value.startTenancy = accountTerm.value[0].format('YYYY-MM-DD');
+    accountPageParam.value.endTenancy = accountTerm.value[1].format('YYYY-MM-DD');
+  } else {
+    accountPageParam.value.startTenancy = '';
+    accountPageParam.value.endTenancy = '';
+  }
+  console.log(searchContent.value);
+  accountPageParam.value.mobile = searchContent.value;
+  accountPageParam.value.userName = searchContent.value;
+  accountPageParam.value.pageIndex = 1;
+  getUserList();
 };
-const addCheck = () => {
-  checkOpen.value = true;
+
+const resetItem = () => {
+  searchContent.value = '';
+  accountTerm.value = undefined;
+  accountPageParam.value.startTenancy = '';
+  accountPageParam.value.endTenancy = '';
+  accountPageParam.value.pageIndex = 1;
+  accountPageParam.value.enabled = undefined;
+  accountPageParam.value.roleId = undefined;
+  getUserList();
+};
+
+const handleClose = () => {
+  accountForm.value = {
+    userName: '',
+    mobile: '',
+    password: '',
+    enabled: true,
+    roleId: undefined,
+    accountTerm: undefined,
+  };
+};
+const addReset = () => {
+  resetItem();
 };
-const addQuery = () => {};
-const addReset = () => {};
 const switchPages = () => {};
 const bindingAccount = () => {
   formRef.value
@@ -136,29 +186,60 @@ const bindingAccount = () => {
     .then(() => {
       handleRequest(async () => {
         const { userName, mobile, password, enabled, roleId, accountTerm } = accountForm.value;
-        await addAccount({
-          userName,
-          mobile,
-          password,
-          roleId: roleId as number,
-          enabled: enabled ? '1' : '0',
-          startTenancy: accountTerm[0].format('YYYY-MM-DD'),
-          endTenancy: accountTerm[1].format('YYYY-MM-DD'),
-          orgId: orgId.value as number,
-        });
+        if (titleAccount.value) {
+          await addAccount({
+            userName,
+            mobile,
+            password,
+            roleId: roleId as number,
+            enabled: enabled ? '1' : '0',
+            startTenancy: accountTerm![0].format('YYYY-MM-DD'),
+            endTenancy: accountTerm![1].format('YYYY-MM-DD'),
+            orgId: orgId.value as number,
+          });
+        } else {
+          await updateAccount({
+            id: accountId.value,
+            userName,
+            mobile,
+            password,
+            roleId: roleId as number,
+            enabled: enabled ? '1' : '0',
+            startTenancy: accountTerm![0].format('YYYY-MM-DD'),
+            endTenancy: accountTerm![1].format('YYYY-MM-DD'),
+            orgId: orgId.value as number,
+          });
+        }
+        getUserList();
+
         accountOpen.value = false;
       });
     })
     .catch(() => {});
 };
-const bindingCheck = () => {};
+
 const clickOrganizationChange = (id: number) => {
   orgId.value = id;
+  searchContent.value = '';
+  accountTerm.value = undefined;
+  accountPageParam.value.startTenancy = '';
+  accountPageParam.value.endTenancy = '';
   handleRequest(async () => {
     characterPageList.value = await getFindRolesByOrgIds([id]);
+    getUserList();
+  });
+};
+
+const getUserList = () => {
+  handleRequest(async () => {
+    accountPageParam.value.orgId = orgId.value;
+    accountPageParam.value.mobile = searchContent.value;
+    accountPageParam.value.userName = searchContent.value;
+    const { records, total } = await getUserPageList(accountPageParam.value);
+    accountList.value = records;
+    accountTotal.value = total;
   });
 };
-onMounted(() => {});
 </script>
 
 <template>
@@ -197,6 +278,7 @@ onMounted(() => {});
               :options="characterPageList"
               :field-names="{ label: 'roleName', value: 'id' }"
               :placeholder="t('common.plzSelect')"
+              :allow-clear="true"
             />
           </AFlex>
           <AFlex align="center" class="margin-bottom">
@@ -204,22 +286,27 @@ onMounted(() => {});
             <ARangePicker
               v-model:value="accountTerm"
               class="input-width"
-              :allow-clear="false"
+              :allow-clear="true"
               :separator="$t('common.to')"
             />
           </AFlex>
 
           <AFlex align="center" class="margin-bottom">
             <div class="account-content-text">状态</div>
-            <ASelect class="input-width" v-model:value="accountPageParam.enabled" :placeholder="$t('common.plzSelect')">
+            <ASelect
+              class="input-width"
+              v-model:value="accountPageParam.enabled"
+              :placeholder="$t('common.plzSelect')"
+              :allow-clear="true"
+            >
               <ASelectOption value="1">正常</ASelectOption>
               <ASelectOption value="0">停用</ASelectOption>
             </ASelect>
           </AFlex>
-          <AFlex class="margin-bottom">
-            <AButton type="primary" @click="addQuery"> 查询 </AButton>
-            <AButton class="default-button margin-left" @click="addReset"> 重置 </AButton>
-          </AFlex>
+        </AFlex>
+        <AFlex class="margin-bottom" justify="flex-end">
+          <AButton type="primary" @click="addQuery"> 查询 </AButton>
+          <AButton class="default-button margin-left" @click="addReset"> 重置 </AButton>
         </AFlex>
         <ATable
           :row-selection="{
@@ -233,11 +320,11 @@ onMounted(() => {});
           :pagination="false"
         >
           <template #bodyCell="{ column, record }">
-            <template v-if="column.key === 'deviceName'">
-              <div @click="addCheck" class="mobile-phone">{{ record.deviceName }}</div>
+            <template v-if="column.key === 'mobile'">
+              <div @click="addCheck(record as UserPageItem)" class="mobile-phone">{{ record.mobile }}</div>
             </template>
-            <template v-if="column.key === 'deviceName5'">
-              <div v-if="record.deviceName5 == 1" class="tag-style success">启用中</div>
+            <template v-if="column.key === 'enabled'">
+              <div v-if="record.enabled == 1" class="tag-style success">启用中</div>
               <div v-else class="tag-style default">停用</div>
             </template>
           </template>
@@ -259,11 +346,12 @@ onMounted(() => {});
     </AFlex>
     <AModal
       v-model:open="accountOpen"
-      title="添加"
+      :title="titleAccount ? '添加' : '编辑'"
       width="460px"
       :footer="null"
       :mask-closable="false"
       :keyboard="false"
+      :after-close="handleClose"
     >
       <AForm
         ref="formRef"
@@ -279,7 +367,7 @@ onMounted(() => {});
             <AInput class="input-width" v-model:value="accountForm.userName" placeholder="请输入" />
           </AFormItem>
           <AFormItem label="手机号" name="mobile">
-            <AInput class="input-width" v-model:value="accountForm.mobile" placeholder="请输入" />
+            <AInputNumber class="input-width" v-model:value="accountForm.mobile" placeholder="请输入" />
           </AFormItem>
           <AFormItem label="角色" name="roleId">
             <ASelect
@@ -314,47 +402,6 @@ onMounted(() => {});
         <AButton class="button-style" type="primary" @click="bindingAccount">{{ $t('common.save') }}</AButton>
       </AFlex>
     </AModal>
-    <AModal
-      v-model:open="checkOpen"
-      title="用户详情"
-      width="460px"
-      :footer="null"
-      :mask-closable="false"
-      :keyboard="false"
-    >
-      <div class="check-div">
-        <AFlex>
-          <div>姓名:</div>
-          <div class="check-text">王某某</div>
-        </AFlex>
-        <AFlex>
-          <div>手机号:</div>
-          <div class="check-text">15622321170</div>
-        </AFlex>
-        <AFlex>
-          <div>角色:</div>
-          <div class="check-text">管理员</div>
-        </AFlex>
-        <AFlex>
-          <div>到期时间:</div>
-          <div class="check-text">2026-10-24</div>
-        </AFlex>
-        <AFlex>
-          <div>密码:</div>
-          <div class="check-text">00000000</div>
-        </AFlex>
-        <AFlex class="margin-top">
-          <div>启用:</div>
-          <ASwitch />
-        </AFlex>
-      </div>
-      <AFlex justify="flex-end">
-        <AButton class="button-style" type="primary" ghost @click="checkOpen = false">{{
-          $t('common.cancel')
-        }}</AButton>
-        <AButton class="button-style" type="primary" @click="bindingCheck">{{ $t('common.save') }}</AButton>
-      </AFlex>
-    </AModal>
     <ConfirmModal
       ref="modalComponent"
       :title="t('common.deleteConfirmation')"