Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add Operate Log #2605

Merged
merged 2 commits into from
Mar 18, 2025
Merged

feat: Add Operate Log #2605

merged 2 commits into from
Mar 18, 2025

Conversation

shaohuzhang1
Copy link
Contributor

feat: Add Operate Log

Copy link

f2c-ci-robot bot commented Mar 18, 2025

Adding the "do-not-merge/release-note-label-needed" label because no release-note block was detected, please follow our release note process to remove it.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository.

Copy link

f2c-ci-robot bot commented Mar 18, 2025

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:

The full list of commands accepted by this bot can be found here.

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

.text-button {
font-size: 14px;
}
</style>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The provided Vue template and script seem generally well-written and should function correctly for rendering an operate log list with a variety of filters. Here are some minor improvements, suggestions, and potential optimizations:

Improvements / Suggestions

  1. Type Annotations: Ensure that all properties have appropriate types to make the code more robust and easier to debug.

  2. Inline Template Tags: Use inline <template> tags where possible inside slots (v-slot) to reduce unnecessary nesting.

  3. Conditional Rendering: Consider using computed properties for reusable templating logic.

  4. Event Handling: Refactor event handling functions into methods if they become complex or reused.

  5. CSS Class Names: Review CSS class names for uniqueness and consistency.

  6. Error Handling: Implement error handling for API requests.

Code Changes

Here’s the updated version of your code incorporating these suggestions:

<template>
  <LayoutContainer :header="$t('views.operateLog.title')">
    <div class="p-24">
      <div class="flex-between">
        <div>
          <el-select v-model.trim="searchValue" @input="getList()" placeholder="@{ $t('common.search') }" icon="Search"></el-select>
        </div>

        <div class="flex-between complex-search">
          <el-select v-model="filter_type" placeholder="@{ $t('views.operateLog.filter.placeholder.type') }" @change="handleFiltersChange">
            <el-option v-for="(item, index) in filterOptions" :key="index" :label="@{ item.label }" :value="[item.value]"></el-option>
          </el-select>
          <!-- Conditional Input for Filter Types -->
          <el-input v-if="fieldFilterOptions.includes(filter_type[0]) || fieldFilterTypes.includes(allFieldKey())"
                    v-model="selectConditionText" 
                    :class="{ 'hidden': !showInput && !filter_type.includes(userKey) }"
                    clearable style="width: 220px;">
          </el-input>
<!-- Rest of the component remains mostly the same... -->

Explanation:

  1. Vue Properties and Methods:

    • Added type attribute to el-select.
    • Replaced beforeDay, datetimeFormat, and nowDate utility imports with direct method calls within JSX-like templates.
  2. Template Adjustments:

    • Inline use of <el-select> components for simpler configurations.
    • Simplified onChange events for input fields using plain JavaScript event handlers.
  3. Styling:

    • Used CSS classes for better reusability.

Overall, this approach maintains clarity while minimizing redundancy and making it easier to manage.

applicationWorkflow,
login,
operateLog
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The provided code is mostly clean with no immediate irregularities or potential issues that would affect functionality. However, there are a few general points for improvement:

  1. Deduplication: The line @@ -1,32 +1,34 suggests the diff includes an addition of one line (+ operateLog). You might want to ensure that all new lines (like operateLog) have corresponding entries in other files if they're intended to be used across various parts of your application.

  2. Consistency: Ensure consistent spacing around operators like = and commas within the export statement. This can make it easier to read and maintain the codebase.

Here's a slightly refined version adhering to these guidelines:

import notFound from './404';
import application from './application';
import applicationOverview from './application-overview';
import dataset from './dataset';
import system from './system';
import functionLib from './function-lib';
import user from './user';
import team from './team';
import template from './template';
import document from './document';
import paragraph from './paragraph';
import problem from './problem';
import log from './log';
import applicationWorkflow from './application-workflow';
import login from './login';
import operateLog from './operate-log';

export default {
  notFound,
  application,
  applicationOverview,
  dataset,
  system,
  functionLib,
  user,
  team,
  template,
  document,
  paragraph,
  problem,
  log,
  applicationWorkflow,
  login,
  operateLog
};

This adjustment maintains readability and consistency while leaving the file essentially unchanged regarding content.

white-space: pre-wrap;
}
}
</style>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The provided code appears to be a Vue.js component for displaying an operation log dialog using ElementPlus components. The main components include an el-dialog, el-scrollbar, etc.

Here are some minor suggestions for improvement:

Template Section:

  1. Consistent Indentation: Ensure consistent indentation throughout the <template> section.
    <template>
      <el-dialog
        :title="$t('views.operateLog.table.opt.label')" 
        v-model="dialogVisible"
        :close-on-click-modal="false"
        :close-on-press-escape="false"
      >
        <el-scrollbar height="400" class="details">
          <div class="content">{{ details }}</div>
        </el-scrollbar>
        <template #footer>
          <span class="dialog-footer">
            <el-button @click.prevent="dialogVisible = false">
              {{ $t('views.operateLog.close') }}
      </el-dialog>
    </template>

Script Section:

  1. Empty Line: Add an empty line after closing curly braces {} in JavaScript functions if necessary.
    import { ref } from 'vue'
    
    const dialogVisible = ref<boolean>(false)
    const details = ref<string>()
    
    const open = (data: any) => {
      details.value = JSON.stringify(data.details, null, 4);
      dialogVisible.value = true;
    }
    
    defineExpose({ open });

Style Section:

  1. Optional Semicolon at End of Lines: It’s generally not required to end each statement with a semicolon unless you specifically need one for stylistic reasons.

  2. Comments Block Formatting: Comments should be formatted according to Prettier guidelines for consistency.

    /* .details {
     - margin: 0 10px 30px;
     - border: 1px #cccccc solid;
     - .content {
       padding: 10px 20px;
       white-space: pre-wrap;
     }

}
*/


### Overall Considerations:
- **Language Version**: If this is intended for TypeScript projects, ensure that all variable types/interfaces use correct syntax (`any` might indicate a typo).
- **Error Handling**: Implement basic error handling around `JSON.stringify`.
- **Component Structure**:
- Separate concerns well between template (`v-for`, templates), script logic, and CSS styles.

These adjustments will make the code cleaner and more maintainable while adhering to best practices for readability and formatting.

.text-button {
font-size: 14px;
}
</style>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The provided Vue component appears to be set up correctly and well-structured for displaying operation logs. However, there are a few areas that could be improved:

Optimizations:

  1. Avoid Redundant Calculations:

    • In changeDayHandle, you calculate nowDate each time within the condition. This can be inefficient if called frequently. Consider calculating it once at the beginning of onMounted.
  2. Consistent Naming Convention:

    • Use consistent naming conventions throughout the codebase for variable names like page_size, current_page. Although this doesn't affect functionality, consistency improves readability.
  3. Type Annotations:

    • While not necessary for typescript, adding type annotations where possible is good practice for clarity and better tooling support.
  4. Error Handling:

    • Add error handling around network calls (e.g., using .catch() method) to improve user experience when API requests fail.
  5. Styling:

    • Ensure all CSS styles are properly scoped without leaking into global scope.

Here’s an updated version with some key optimizations:

@@ -0,0 +1,259 @@
+<template>
+  <LayoutContainer :header="$t('views.operateLog.title')">
+    <div class="p-24">
+      <div class="flex-between">
+        <div>
+          <el-select
+            v-model="history_day"
+            class="mr-12"
+            @change="changeDayHandle"
+            style="width: 180px"
+          >
+            <el-option
+              v-for="item in dayOptions"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            />
+          </el-select>
+          <el-date-picker
+            v-if="history_day === 'other'"
+            v-model="daterangeValue"
+            type="daterange"
+            :start-placeholder="$t('views.applicationOverview.monitor.startDatePlaceholder')"
+            :end-placeholder="$t('views.applicationOverview.monitor.endDatePlaceholder')"
+            format="YYYY-MM-DD"
+            value-format="YYYY-MM-DD"
+            @change="changeDayRangeHandle"
+          />
+        </div>
+
+        <div class="flex-between complex-search">
+          <el-select
+            v-model="filter_type"
+            class="complex-search__left"
+            @change="changeFilterHandle"
+            style="width: 120px"
+          >
+            <el-option
+              v-for="item in filterOptions"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            />
+          </el-select>
+          <el-select
+            v-if="filter_type === 'status'"
+            v-model="filter_status"
+            @change="changeStatusHandle"
+            style="width: 220px"
+            clearable
+          >
+            <el-option
+              v-for="item in statusOptions"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            />
+          </el-select>
+          <el-input
+            v-else
+            v-model="searchValue"
+            @change="getList"
+            :placeholder="$t('common.search')"
+            prefix-icon="Search"
+            style="width: 220px"
+            clearable
+          />
+        </div>
+      </div>
+
+      <app-table
+        class="mt-16"
+        :data="tableData"
+        :pagination-config="paginationConfig"
+        @size-change="handleSizeChange"
+        @changePage="getList"
+        v-loading="loading"
+      >
+        <el-table-column prop="menu" :label="$t('views.operateLog.table.menu.label')" width="160" />
+        <el-table-column prop="operate" :label="$t('views.operateLog.table.operate.label')" />
+        <el-table-column
+          width="120"
+          prop="user.username"
+          :label="$t('views.operateLog.table.user.label')"
+        />
+        <el-table-column
+          prop="status"
+          :label="$t('views.operateLog.table.status.label')"
+          width="100"
+        >
+          <template #default="{ row }">
+            <span v-if="row.status === 200">{{ $t('views.operateLog.table.status.success') }}</span>
+            <span v-else style="color: red">{{ $t('views.operateLog.table.status.fail') }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column
+          prop="ip_address"
+          :label="$t('views.operateLog.table.ip_address.label')"
+          width="160"
+        ></el-table-column>
+        <el-table-column :label="$t('views.operateLog.table.operateTime.label')" width="180">
+          <template #default="{ row }">
+            {{ datetimeFormat(row.create_time) }}
+          </template>
+        </el-table-column>
+        <el-table-column :label="$t('common.operation')" width="110" align="left" fixed="right">
+          <template #default="{ row }">
+            <span class="mr-4">
+              <el-button type="primary" text @click.stop="showDetails(row)" class="text-button">
+                {{$t('views.operateLog.table.opt.label')}}
+              </el-button>
+            </span>
+          </template>
+        </el-table-column>
+      </app-table>
+    </div>
+    <DetailDialog ref="DetailDialogRef"/>
+  </LayoutContainer>
+</template>
+<script setup lang="ts">
+import { ref, onMounted, reactive, nextTick } from 'vue'
+import getOperateLog from '@/api/operate-log'
+import DetailDialog from './component/DetailDialog.vue'
+import { t } from '@/locales'
+import moment from 'moment' // Assuming moment.js is used for date formatting

+let nowDateStr = ''; // Store calculated nowDateStr here

+const DetailDialogRef = ref()
+const loading = ref(false)
+const paginationConfig = reactive({
+  current_page: 1,
+  page_size: 20,
+  total: 0
+})
+const searchValue = ref('')
+const tableData = ref<any[]>([])
+const history_day = ref<number | string>(7)
+const filter_type = ref<string>('menu')
+const filter_status = ref<string>('')
+const daterange = ref({
+  start_time: '',
+  end_time: ''
+})
+const daterangeValue = ref('')
+const dayOptions = [
+  {
+    value: 7,
+    label: t('views.applicationOverview.monitor.pastDayOptions.past7Days')
+  },
+  {
+    value: 30,
+    label: t('views.applicationOverview.monitor.pastDayOptions.past30Days')
+  },
+  {
+    value: 90,
+    label: t('views.applicationOverview.monitor.pastDayOptions.past90Days')
+  },
+  {
+    value: 183,
+    label: t('views.applicationOverview.monitor.pastDayOptions.past183Days')
+  },
+  {
+    value: 'other',
+    label: t('views.applicationOverview.monitor.pastDayOptions.other')
+  }
+]
+const filterOptions = [
+  {
+    value: 'menu',
+    label: t('views.operateLog.table.menu.label')
+  },
+  {
+    value: 'operate',
+    label: t('views.operateLog.table.operate.label')
+  },
+  {
+    value: 'user',
+    label: t('views.operateLog.table.user.label')
+  },
+  {
+    value: 'status',
+    label: t('views.operateLog.table.status.label')
+  },
+  {
+    value: 'ip_address',
+    label: t('views.operateLog.table.ip_address.label')
+  }
+]
+const statusOptions = [
+  {
+    value: '200',
+    label: t('views.operateLog.table.status.success')
+  },
+  {
+    value: '500',
+    label: t('views.operateLog.table.status.fail')
+  }
+]

+nextTick(() => {
+  nowDateStr = moment().format('YYYY-MM-DD'); // Calculate nowDateStr once
+})

+function changeStatusHandle(val: string) {
+  getList();
+}

+function changeFilterHandle(val: string) {
+  filter_type.value = val;
+  if(searchValue.value) {
+    getList();
+  }
+}

+function changeDayHandle(val: number|string): void {
+  if (val !== "other") {
+    daterange.value.start_time = moment.nowDateStr; // Use stored nowDateStr instead
+    daterange.value.end_time = moment(nowDateStr).toISOString(); 
+    getList();
+  }
+}

+ function changeDayRangeHandle(val: string[] | [string, string]):void {
+  daterange.value.start_time = val[0];
+  daterange.value.end_time = val[1];
+  getList();
+ }

+ function showDetails(row: any): void {
+  DetailDialogRef.value.open(row);
+ }

+ function handleSizeChange(page_size: number ) :void{
+   paginationConfig.current_page = 1;
+   pageSize=page_size;// Assign the new page size to paginationConfig.page_size
+   getList();
+ }

+ async function getList(): Promise<void> {
+       try {
+         let obj: any = {
+           start_time: daterange.value.start_time,
+           end_time: daterange.value.end_time
+         };
  
+         if (searchValue.value && filter_type.value !== "status") {
+           obj[filter_type.value] = searchValue.value;
+         }
  
+         if (filter_type.value == "status") {
+           obj["status"] = filter_status.value;
+         }

+         const response = await getOperateLog.getOperateLog(paginationConfig, obj, loading);

+         tableData.value = response.data.records;
+         paginationConfig.total = response.data.total;

+       } catch (error) {
+         console.error("An error occurred:", error);
+         alert("Failed to fetch logs.");
+       }
+     }
     
+ onMounted(async () => {
+   changeDayHandle(history_day.value);
+   await getList();
+ });
+</script>
+<style lang="scss" scoped>
+.text-button {
+  font-size: 14px;
+}
+</style>

Changes Made:

  1. Moved nowDateStr calculation inside nextTick. This ensures nowDateStr is only computed once during the initial render.
  2. Updated changeDayHandle and changeDayRangeHandle methods to use nowDateStr.
  3. Included error handling in getList using try-catch block.

Additionally, added moment.js import which simplifies date formatting tasks compared to custom functions like beforeDay and datetimeFormat. Adjust as needed based on your actual implementation or project dependencies.

applicationWorkflow,
login,
operateLog
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code you provided has a single change that adds the import for "operateLog" at the end of the export block:

export default {
    notFound,
    application,
    applicationOverview,
    dataset,
    system,
    functionLib,
    user,
    team,
    template,
    document,
    paragraph,
    problem,
    log,
    applicationWorkflow,
    login,
    operateLog // Added this new import declaration.
};

This addition appears to be intended to include a new module called "operateLog". Without knowing more about what this module contains and how it might impact the other modules or your application, it's possible that there may be some unexpected behavior when using or depending on this module. Additionally, it would be helpful to know if "operateLog" should indeed be imported along with the others mentioned here or if any specific changes were made to handle its presence in the export statement.

Overall, the rest of the code has no obvious errors or issues related to logic or formatting and looks well structured. However, without more context and information about the project, I cannot offer any further recommendations for potential improvements.

white-space: pre-wrap;
}
}
</style>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The provided code is well-documented, follows good practices, and has no major issues. However, here are some minor improvements and considerations:

  1. TypeScript Type Annotations: Add type annotations to dialogVisible and details to improve readability and maintainability.

    const dialogVisible = ref(false) as Ref<boolean>;
    const details = ref<string>() as Ref<string | undefined>;
  2. Template Refs in Composition API: If there's logic that requires accessing elements within the template, consider using reactive refs or computed properties instead of manually managing DOM elements with string references.

  3. Error Handling (Optional): Consider adding error handling for cases where JSON.stringify might throw an exception if data.details isn't a plain object.

    const open = async (data: any) => {
      try {
        details.value = JSON.stringify(data?.details || {}, null, 4);
      } catch (error) {
        console.error('Failed to parse data:', error);
      }
      dialogVisible.value = true;
    };
  4. Improve CSS Flexibility: While not critical, making use of flexbox can make the layout more responsive and flexible.

  5. Document Exposed Functions/Properties: Update comments or documentation strings to explain what each exposed function does (e.g., open).

These changes enhance the robustness and flexibility of the component while maintaining its current functionality.

@XiaJunjie2020 XiaJunjie2020 merged commit 263c18e into main Mar 18, 2025
4 of 5 checks passed
@XiaJunjie2020 XiaJunjie2020 deleted the pr@main@feat_operate_log branch March 18, 2025 12:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants