Ver Fonte

feat(views): 编写智控日志和操作日志页面

wangcong há 1 semana atrás
pai
commit
6691565c06

+ 26 - 0
src/api/index.ts

@@ -84,6 +84,8 @@ import type {
   MonitorPointInfo,
   MonitorPointItem,
   NoticePageItemData,
+  OperateLogData,
+  OperateLogQuery,
   Organization,
   OrganizationItem,
   OutdooForm,
@@ -114,6 +116,8 @@ import type {
   RegionsPointsItem,
   SerialNumberItem,
   SerialNumberItemData,
+  SmartCtrlLogData,
+  SmartCtrlLogQuery,
   SubmitDeviceGroup,
   SubmitSorting,
   TempHumidityControlSettings,
@@ -1261,3 +1265,25 @@ export const getAIAdvancedSetting = async (groupId: number) => {
 
   return data;
 };
+
+// 智控日志
+
+export const getSmartCtrlLog = async (params: SmartCtrlLogQuery) => {
+  const data = await request<SmartCtrlLogData>(apiBiz('/algLogInfo/getPageList'), {
+    method: 'POST',
+    body: JSON.stringify(params),
+  });
+
+  return data;
+};
+
+// 操作日志
+
+export const getOperateLog = async (params: OperateLogQuery) => {
+  const data = await request<OperateLogData>(apiBiz('/controlLogInfo/getPageList'), {
+    method: 'POST',
+    body: JSON.stringify(params),
+  });
+
+  return data;
+};

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

@@ -168,6 +168,7 @@
     "operator": "操作人员",
     "opsCenter": "运维中心",
     "pageTotal": "共 {total} 条",
+    "platform": "平台",
     "pleaseEnter": "请输入",
     "plzEnter": "请输入{name}",
     "plzSelect": "请选择{name}",

+ 46 - 0
src/types/index.ts

@@ -2334,6 +2334,52 @@ export interface AIAdvancedParamItem {
   oneValue: string | null;
 }
 
+export type SmartCtrlLogData = PageData<SmartCtrlLogItem>;
+
+export interface SmartCtrlLogItem {
+  content: string;
+  createTime: string;
+  createUserId: number | null;
+  devGroupId: number;
+  devGroupName: string;
+  devParentGroupId: number;
+  deviceId: number;
+  deviceName: string;
+  id: number;
+  orderType: number;
+  orgId: number;
+  result: number;
+  updateTime: string;
+  updateUserId: number | null;
+}
+
+export interface SmartCtrlLogQuery extends PageParams {
+  deviceIds: string;
+  searchContent: string;
+  startTime?: string;
+  endTime?: string;
+}
+
+export type OperateLogData = PageData<OperateLogItem>;
+
+export interface OperateLogItem {
+  deviceName: string;
+  content: string;
+  result: number;
+  userName: string;
+  createUserId: number;
+  createTime: string;
+}
+
+export interface OperateLogQuery extends PageParams {
+  groupIds: number[];
+  content: string;
+  result: number | null;
+  userId: number | null;
+  startTime?: string;
+  endTime?: string;
+}
+
 export interface UploadLogo {
   etag: string;
   versionId: string;

+ 44 - 5
src/views/log-center/LogCenter.vue

@@ -1,12 +1,51 @@
 <script setup lang="ts">
-import { ref } from 'vue';
+import { computed, onMounted, ref } from 'vue';
 
-const activeKey = ref('smartControlLogs');
+import { useRefreshView } from '@/hooks/refresh-view';
+import { t } from '@/i18n';
+
+import OperateLog from './OperateLog.vue';
+import SmartCtrlLog from './SmartCtrlLog.vue';
+
+import type { TabComponent } from '@/types';
+
+const { renderView, refreshView } = useRefreshView();
+const activeKey = ref('');
+
+const logTabs = computed<TabComponent[]>(() => {
+  return [
+    {
+      key: 'smartCtrlLog',
+      name: t('logCenter.smartControlLogs'),
+      component: SmartCtrlLog,
+    },
+    {
+      key: 'operateLog',
+      name: t('logCenter.operationLogs'),
+      component: OperateLog,
+    },
+  ];
+});
+
+onMounted(() => {
+  activeKey.value = logTabs.value[0].key;
+  refreshView();
+});
 </script>
 
 <template>
-  <ATabs class="button-tabs-compact" v-model:active-key="activeKey" type="card">
-    <ATabPane key="smartControlLogs" :tab="$t('logCenter.smartControlLogs')"> 智控日志 </ATabPane>
-    <ATabPane key="operationLogs" :tab="$t('logCenter.operationLogs')"> 操作日志 </ATabPane>
+  <ATabs class="button-tabs-compact" v-model:active-key="activeKey" type="card" @tab-click="refreshView">
+    <ATabPane v-for="item in logTabs" :key="item.key" :tab="item.name">
+      <component v-if="activeKey === item.key && renderView" :is="item.component" />
+    </ATabPane>
   </ATabs>
 </template>
+
+<style lang="scss" scoped>
+:deep(.ant-tabs-content-holder) {
+  .ant-card {
+    border-radius: 16px;
+    box-shadow: none;
+  }
+}
+</style>

+ 301 - 0
src/views/log-center/OperateLog.vue

@@ -0,0 +1,301 @@
+<script setup lang="ts">
+import { computed, onMounted, ref } from 'vue';
+import { Cascader } from 'ant-design-vue';
+
+import { useRequest } from '@/hooks/request';
+import { t } from '@/i18n';
+import { getOperateLog, getPageList } from '@/api';
+import { disabledDate } from '@/utils';
+
+import type { ColumnType, TableProps } from 'ant-design-vue/es/table';
+import type { DeviceGroupItem, OperateLogItem, OptionItem, PageParams, RangeValue, UserPageItem } from '@/types';
+
+const enum OperateResult {
+  All = -1,
+  Success,
+  Failure,
+}
+
+const enum OperatorType {
+  All = -2,
+  Platform = -1,
+}
+
+const timeRange = ref<RangeValue>();
+const selectedGroups = ref<number[][]>([]);
+const deviceGroup = ref<DeviceGroupItem[]>([]);
+const searchContent = ref('');
+const selectedResult = ref<number>(OperateResult.All);
+const selectedOperator = ref<number>(OperatorType.All);
+const userList = ref<UserPageItem[]>([]);
+
+const operatorList = computed<OptionItem<number>[]>(() => {
+  return [
+    { value: OperatorType.All, label: t('common.entire') },
+    { value: OperatorType.Platform, label: t('common.platform') },
+    ...userList.value.map((item) => ({
+      value: item.id,
+      label: item.userName,
+    })),
+  ];
+});
+
+const logList = ref<OperateLogItem[]>([]);
+const logTotal = ref(0);
+const pageParams = ref<PageParams>({
+  pageIndex: 1,
+  pageSize: 10,
+});
+
+const columns = computed<ColumnType<OperateLogItem>[]>(() => {
+  return [
+    {
+      title: t('common.device'),
+      dataIndex: 'deviceName',
+      key: 'deviceName',
+    },
+    {
+      title: t('logCenter.deviceParameterChange'),
+      dataIndex: 'content',
+      key: 'content',
+    },
+    {
+      title: t('common.time'),
+      dataIndex: 'createTime',
+      key: 'createTime',
+    },
+    {
+      title: t('common.result'),
+      dataIndex: 'result',
+      key: 'result',
+    },
+    {
+      title: t('common.operator'),
+      dataIndex: 'userName',
+      key: 'userName',
+    },
+  ];
+});
+
+onMounted(() => {
+  getDeviceGroups();
+  getUserList();
+  getLogList();
+});
+
+const { handleRequest } = useRequest();
+
+const getDeviceGroups = () => {
+  handleRequest(async () => {
+    deviceGroup.value = await getPageList();
+  });
+};
+
+const getUserList = () => {
+  handleRequest(async () => {
+    //
+  });
+};
+
+const { isLoading: isLogLoading, handleRequest: handleLogRequest } = useRequest();
+
+const getLogList = () => {
+  handleLogRequest(async () => {
+    const { records, total } = await getOperateLog({
+      ...pageParams.value,
+      groupIds: selectedGroups.value.map((item) => item[1]),
+      content: searchContent.value,
+      startTime: timeRange.value?.[0].format('YYYY-MM-DD HH:mm:ss'),
+      endTime: timeRange.value?.[1].format('YYYY-MM-DD HH:mm:ss'),
+      result: selectedResult.value === OperateResult.All ? null : selectedResult.value,
+      userId: selectedOperator.value === OperatorType.All ? null : selectedOperator.value,
+    });
+
+    logList.value = records;
+    logTotal.value = total;
+  });
+};
+
+const filterOperator = (input: string, option: OptionItem<number>) => {
+  return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
+};
+
+const handleSearch = () => {
+  pageParams.value.pageIndex = 1;
+  getLogList();
+};
+
+const handleReset = () => {
+  timeRange.value = undefined;
+  selectedGroups.value = [];
+  searchContent.value = '';
+  selectedResult.value = OperateResult.All;
+  selectedOperator.value = OperatorType.All;
+  handleSearch();
+};
+
+const handleLogTableChange: TableProps['onChange'] = (pagination) => {
+  pageParams.value.pageIndex = pagination.current || 1;
+  pageParams.value.pageSize = pagination.pageSize || 10;
+  getLogList();
+};
+</script>
+
+<template>
+  <ACard :bordered="false">
+    <ARow class="operate-log-query" :gutter="[80, 16]">
+      <ACol :span="8">
+        <div class="operate-log-label">{{ $t('logCenter.selectTimeRange') }}</div>
+        <ARangePicker
+          class="operate-log-time"
+          v-model:value="timeRange"
+          :placeholder="[$t('common.startTime'), $t('common.endTime')]"
+          :separator="$t('common.to')"
+          :disabled-date="disabledDate"
+          show-time
+        >
+          <template #suffixIcon>
+            <SvgIcon name="calendar" color="#333" />
+          </template>
+        </ARangePicker>
+      </ACol>
+      <ACol :span="8">
+        <div class="operate-log-label">{{ $t('logCenter.selectDeviceGroup') }}</div>
+        <ACascader
+          v-model:value="selectedGroups"
+          class="operate-log-input"
+          :placeholder="$t('logCenter.plzSelectDeviceGroup')"
+          :field-names="{ label: 'groupName', value: 'id', children: 'deviceGroupChilds' }"
+          :options="deviceGroup"
+          
+          
+          :max-tag-count="2"
+          :max-tag-text-length="10"
+          :show-checked-strategy="Cascader.SHOW_CHILD"
+           change-on-select show-search multiple 
+        />
+      </ACol>
+      <ACol :span="8">
+        <div class="operate-log-label">{{ $t('common.operationContent') }}</div>
+        <AInput
+          v-model:value="searchContent"
+          class="operate-log-input"
+          :placeholder="$t('common.plzEnter')"
+          allow-clear
+        />
+      </ACol>
+      <ACol :span="8">
+        <div class="operate-log-label">{{ $t('common.operationResult') }}</div>
+        <ASelect v-model:value="selectedResult" class="operate-log-input">
+          <ASelectOption :value="OperateResult.All">{{ $t('common.entire') }} </ASelectOption>
+          <ASelectOption :value="OperateResult.Success">{{ $t('common.success') }} </ASelectOption>
+          <ASelectOption :value="OperateResult.Failure">{{ $t('common.failure') }} </ASelectOption>
+        </ASelect>
+      </ACol>
+      <ACol :span="8">
+        <div class="operate-log-label">{{ $t('common.operator') }}</div>
+        <ASelect
+          v-model:value="selectedOperator"
+          class="operate-log-input"
+          :options="operatorList"
+          show-search
+          :filter-option="filterOperator"
+        />
+      </ACol>
+      <ACol :span="8" class="operate-log-button">
+        <AButton @click="handleReset">{{ $t('common.reset') }}</AButton>
+        <AButton type="primary" @click="handleSearch">{{ $t('common.query') }}</AButton>
+      </ACol>
+    </ARow>
+    <ATable
+      class="hvac-table"
+      :data-source="logList"
+      :columns="columns"
+      row-key="id"
+      :loading="isLogLoading"
+      :pagination="{
+        current: pageParams.pageIndex,
+        pageSize: pageParams.pageSize,
+        total: logTotal,
+        showSizeChanger: true,
+        showQuickJumper: true,
+        hideOnSinglePage: false,
+        showTotal: (total) => $t('common.pageTotal', { total }),
+      }"
+      @change="handleLogTableChange"
+    >
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'result'">
+          <span class="operate-log-result operate-result-success" v-if="record.result === OperateResult.Success">
+            {{ $t('common.success') }}
+          </span>
+          <span class="operate-log-result operate-result-failure" v-else-if="record.result === OperateResult.Failure">
+            {{ $t('common.failure') }}
+          </span>
+        </template>
+      </template>
+    </ATable>
+  </ACard>
+</template>
+
+<style lang="scss" scoped>
+.operate-log-query {
+  margin-bottom: 16px;
+
+  > .ant-col {
+    display: flex;
+    align-items: center;
+  }
+}
+
+.operate-log-label {
+  width: 70px;
+  margin-right: 12px;
+  font-size: 14px;
+  line-height: 22px;
+  color: var(--antd-color-text);
+  text-align: right;
+}
+
+.operate-log-time {
+  :deep(input) {
+    text-align: center;
+  }
+}
+
+.operate-log-time,
+.operate-log-input {
+  flex: 1;
+}
+
+.operate-log-button {
+  display: flex;
+  flex-direction: row-reverse;
+
+  button + button {
+    margin-right: 12px;
+  }
+}
+
+.operate-log-result {
+  height: 24px;
+  padding: 2px 8px;
+  font-size: 12px;
+  line-height: 20px;
+  text-align: center;
+  border: 1px solid var(--antd-color-border);
+  border-radius: 4px;
+
+  &.operate-result-success {
+    color: #52c41a;
+    background: #f6ffed;
+    border-color: #b7eb8f;
+  }
+
+  &.operate-result-failure {
+    color: #f56c6c;
+    background: #fff1f0;
+    border-color: #ffa39e;
+  }
+}
+</style>

+ 209 - 0
src/views/log-center/SmartCtrlLog.vue

@@ -0,0 +1,209 @@
+<script setup lang="ts">
+import { computed, onMounted, ref } from 'vue';
+
+import { useRequest } from '@/hooks/request';
+import { t } from '@/i18n';
+import { getSmartCtrlLog, noPaginationDevicesList } from '@/api';
+import { disabledDate } from '@/utils';
+
+import type { ColumnType, TableProps } from 'ant-design-vue/es/table';
+import type { AllDevicesList, PageParams, RangeValue, SmartCtrlLogItem } from '@/types';
+
+const timeRange = ref<RangeValue>();
+const selectedDevices = ref<number[]>([]);
+const deviceList = ref<AllDevicesList[]>([]);
+const searchContent = ref('');
+
+const logList = ref<SmartCtrlLogItem[]>([]);
+const logTotal = ref(0);
+const pageParams = ref<PageParams>({
+  pageIndex: 1,
+  pageSize: 10,
+  pageSorts: [
+    {
+      column: 'id',
+      asc: false,
+    },
+  ],
+});
+
+const columns = computed<ColumnType<SmartCtrlLogItem>[]>(() => {
+  return [
+    {
+      title: t('common.device'),
+      dataIndex: 'deviceName',
+      key: 'deviceName',
+    },
+    {
+      title: t('common.content'),
+      dataIndex: 'content',
+      key: 'content',
+    },
+    {
+      title: t('common.time'),
+      dataIndex: 'updateTime',
+      key: 'updateTime',
+    },
+  ];
+});
+
+onMounted(() => {
+  getDeviceList();
+  getLogList();
+});
+
+const { handleRequest } = useRequest();
+
+const getDeviceList = () => {
+  handleRequest(async () => {
+    deviceList.value = await noPaginationDevicesList();
+  });
+};
+
+const { isLoading: isLogLoading, handleRequest: handleLogRequest } = useRequest();
+
+const getLogList = () => {
+  handleLogRequest(async () => {
+    const { records, total } = await getSmartCtrlLog({
+      ...pageParams.value,
+      deviceIds: selectedDevices.value.join(','),
+      searchContent: searchContent.value,
+      startTime: timeRange.value?.[0].format('YYYY-MM-DD HH:mm:ss'),
+      endTime: timeRange.value?.[1].format('YYYY-MM-DD HH:mm:ss'),
+    });
+
+    logList.value = records;
+    logTotal.value = total;
+  });
+};
+
+const filterDevice = (input: string, option: AllDevicesList) => {
+  return option.deviceName.toLowerCase().indexOf(input.toLowerCase()) >= 0;
+};
+
+const handleSearch = () => {
+  pageParams.value.pageIndex = 1;
+  getLogList();
+};
+
+const handleReset = () => {
+  timeRange.value = undefined;
+  selectedDevices.value = [];
+  searchContent.value = '';
+  handleSearch();
+};
+
+const handleLogTableChange: TableProps['onChange'] = (pagination) => {
+  pageParams.value.pageIndex = pagination.current || 1;
+  pageParams.value.pageSize = pagination.pageSize || 10;
+  getLogList();
+};
+</script>
+
+<template>
+  <ACard :bordered="false">
+    <ARow class="smart-ctrl-log-query" :gutter="[80, 16]">
+      <ACol :span="8">
+        <div class="smart-ctrl-log-label">{{ $t('logCenter.selectTimeRange') }}</div>
+        <ARangePicker
+          class="smart-ctrl-log-time"
+          v-model:value="timeRange"
+          :placeholder="[$t('common.startTime'), $t('common.endTime')]"
+          :separator="$t('common.to')"
+          :disabled-date="disabledDate"
+          show-time
+        >
+          <template #suffixIcon>
+            <SvgIcon name="calendar" color="#333" />
+          </template>
+        </ARangePicker>
+      </ACol>
+      <ACol :span="8">
+        <div class="smart-ctrl-log-label">{{ $t('logCenter.selectDevice') }}</div>
+        <ASelect
+          v-model:value="selectedDevices"
+          class="smart-ctrl-log-input"
+          :options="deviceList"
+          :field-names="{ label: 'deviceName', value: 'id' }"
+          mode="multiple"
+          :max-tag-count="2"
+          :max-tag-text-length="10"
+          :filter-option="filterDevice"
+          :placeholder="$t('logCenter.plzSelectDevice')"
+          allow-clear
+        />
+      </ACol>
+      <ACol :span="8">
+        <div class="smart-ctrl-log-label">{{ $t('common.inputContent') }}</div>
+        <AInput
+          v-model:value="searchContent"
+          class="smart-ctrl-log-input"
+          :placeholder="$t('logCenter.plzEnterContent')"
+          allow-clear
+        />
+      </ACol>
+    </ARow>
+    <div :span="8" class="smart-ctrl-log-button">
+      <AButton @click="handleReset">{{ $t('common.reset') }}</AButton>
+      <AButton type="primary" @click="handleSearch">{{ $t('common.query') }}</AButton>
+    </div>
+    <ATable
+      class="hvac-table"
+      :data-source="logList"
+      :columns="columns"
+      row-key="id"
+      :loading="isLogLoading"
+      :pagination="{
+        current: pageParams.pageIndex,
+        pageSize: pageParams.pageSize,
+        total: logTotal,
+        showSizeChanger: true,
+        showQuickJumper: true,
+        hideOnSinglePage: false,
+        showTotal: (total) => $t('common.pageTotal', { total }),
+      }"
+      @change="handleLogTableChange"
+    />
+  </ACard>
+</template>
+
+<style lang="scss" scoped>
+.smart-ctrl-log-query {
+  margin-bottom: 16px;
+
+  > .ant-col {
+    display: flex;
+    align-items: center;
+  }
+}
+
+.smart-ctrl-log-label {
+  width: 70px;
+  margin-right: 12px;
+  font-size: 14px;
+  line-height: 22px;
+  color: var(--antd-color-text);
+  text-align: right;
+}
+
+.smart-ctrl-log-time {
+  :deep(input) {
+    text-align: center;
+  }
+}
+
+.smart-ctrl-log-time,
+.smart-ctrl-log-input {
+  flex: 1;
+}
+
+.smart-ctrl-log-button {
+  display: flex;
+  flex-direction: row-reverse;
+  margin-block: 16px;
+
+  button + button {
+    margin-right: 12px;
+  }
+}
+</style>