十一、【核心功能篇】測試用例管理:設計用例新增編輯界面

【核心功能篇】測試用例管理:設計用例新增&編輯界面

    • 前言
      • 準備工作
      • 第一步:創建測試用例相關的 API 服務 (`src/api/testcase.ts`)
      • 第二步:創建測試用例編輯頁面組件 (`src/views/testcase/TestCaseEditView.vue`)
      • 第三步:配置測試用例編輯頁的路由
      • 第四步:測試用例新增&編輯功能
    • 總結

前言

一個好的測試用例編輯界面應該具備以下特點:

  • 清晰直觀: 用戶能夠快速理解各個字段的含義和作用。
  • 高效易用: 能夠方便地輸入和修改信息,特別是對于重復性的測試步驟。
  • 結構化: 能夠清晰地展示和管理測試步驟等復雜結構。
  • 可擴展性: (雖然本篇可能不完全覆蓋)未來可以方便地增加對參數化、數據驅動、自定義關鍵字等高級功能的支持。

在我們的 Django 后端模型 (api/models.py中的 TestCase) 中,我們暫時將 steps_text 定義為一個 TextField,用于存儲文本描述的測試步驟。為了實現一個更結構化的步驟編輯界面,前端在提交時需要將這些步驟組織成一種格式(例如 JSON 數組或特定分隔符的文本),而后端在保存或解析時需要能處理這種格式。

這篇文章將帶你

  1. 規劃測試用例編輯頁面的整體布局和交互。
  2. 設計并實現一個能夠動態添加、刪除和編輯測試步驟的表單區域。
  3. 將測試用例的創建和編輯功能與后端 API 進行聯調。

我們將重點放在前端界面的設計與實現,以及與后端 API 的數據交互上。

準備工作

  1. 前端項目就緒: test-platform/frontend 項目可以正常運行 (npm run dev)。
  2. 后端 API 運行中: Django 后端服務運行(python manage.py runserver),測試用例的 API (/api/testcases/) 可用。
  3. Axios 和 API 服務已封裝: utils/request.tsapi/project.ts, api/module.ts 已配置好。
  4. 項目和模塊管理功能可用: 我們需要先有項目和模塊,才能創建測試用例。
  5. Element Plus 集成完畢。

第一步:創建測試用例相關的 API 服務 (src/api/testcase.ts)

與項目和模塊類似,我們先為測試用例創建 API 服務文件。
在這里插入圖片描述

// test-platform/frontend/src/api/testcase.ts
import request from '@/utils/request'
import type { AxiosPromise } from 'axios'// 與后端 TestCase model 和 TestCaseSerializer 對應
export interface TestCase {id: number;name: string;description: string | null;module: number; // 所屬模塊 IDmodule_name?: string; // 可選,如果 API 返回project_id?: number; // 可選,如果 API 返回project_name?: string; // 可選,如果 API 返回priority: 'P0' | 'P1' | 'P2' | 'P3';priority_display?: string;precondition: string | null;steps_text: string; // 后端存儲的是合并后的文本expected_result: string;case_type: 'functional' | 'api' | 'ui';case_type_display?: string;maintainer: string | null;create_time: string;update_time: string;
}export type TestCaseListResponse = TestCase[] // 假設列表直接返回數組// 創建或更新測試用例時發送的數據類型
export interface UpsertTestCaseData {name: string;description?: string | null;module: number; // 必須priority?: 'P0' | 'P1' | 'P2' | 'P3';precondition?: string | null;steps_text: string; // 前端會將步驟數組合并為這個文本expected_result?: string;case_type?: 'functional' | 'api' | 'ui';maintainer?: string | null;
}// 1. 獲取測試用例列表 (支持按模塊或項目過濾)
export function getTestCaseList(params?: { module_id?: number, project_id?: number, search?: string }): AxiosPromise<TestCaseListResponse> {return request({url: '/testcases/',method: 'get',params})
}// 2. 創建測試用例
export function createTestCase(data: UpsertTestCaseData): AxiosPromise<TestCase> {return request({url: '/testcases/',method: 'post',data})
}// 3. 獲取單個測試用例詳情
export function getTestCaseDetail(testCaseId: number): AxiosPromise<TestCase> {return request({url: `/testcases/${testCaseId}/`,method: 'get'})
}// 4. 更新測試用例
export function updateTestCase(testCaseId: number, data: Partial<UpsertTestCaseData>): AxiosPromise<TestCase> {return request({url: `/testcases/${testCaseId}/`,method: 'put', // 或者 patchdata})
}// 5. 刪除測試用例
export function deleteTestCase(testCaseId: number): AxiosPromise<void> {return request({url: `/testcases/${testCaseId}/`,method: 'delete'})
}

關鍵點:

  • UpsertTestCaseData 中的 steps_text 字段,前端會將動態編輯的多個步驟描述合并成一個字符串傳遞給它。
  • 類型定義應盡量與后端 DRF Serializer 的輸入輸出保持一致。

第二步:創建測試用例編輯頁面組件 (src/views/testcase/TestCaseEditView.vue)

我們將創建一個新的路由頁面專門用于新建和編輯測試用例。

a. 創建文件:
src/views/ 目錄下創建 testcase 文件夾,并在其中創建 TestCaseEditView.vue
在這里插入圖片描述

b. 編寫 TestCaseEditView.vue 的基本結構和表單:
在這里插入圖片描述

<!-- test-platform/frontend/src/views/testcase/TestCaseEditView.vue -->
<template><div class="testcase-edit-view" v-loading="pageLoading"><el-page-header @back="goBack" :content="pageTitle" class="page-header-custom" /><el-card class="form-card"><el-formref="testCaseFormRef":model="formData":rules="formRules"label-width="120px"label-position="right"><el-row :gutter="20"><el-col :span="12"><el-form-item label="用例名稱" prop="name"><el-input v-model="formData.name" placeholder="請輸入用例名稱" /></el-form-item></el-col><el-col :span="12"><el-form-item label="所屬模塊" prop="module"><!-- 這里需要一個模塊選擇器,先用 Input 占位,后續改進 --><el-select v-model="formData.module" placeholder="請選擇所屬模塊" filterablestyle="width: 100%;"@focus="fetchModulesForSelect" :loading="moduleSelectLoading"><el-optionv-for="item in moduleOptions":key="item.id":label="`${item.project_name} - ${item.name}`":value="item.id"/></el-select></el-form-item></el-col></el-row><el-form-item label="用例描述" prop="description"><el-input v-model="formData.description" type="textarea" placeholder="請輸入用例描述" /></el-form-item><el-row :gutter="20"><el-col :span="12"><el-form-item label="優先級" prop="priority"><el-select v-model="formData.priority" placeholder="請選擇優先級" style="width: 100%;"><el-option label="P0 - 最高" value="P0" /><el-option label="P1 - 高" value="P1" /><el-option label="P2 - 中" value="P2" /><el-option label="P3 - 低" value="P3" /></el-select></el-form-item></el-col><el-col :span="12"><el-form-item label="用例類型" prop="case_type"><el-select v-model="formData.case_type" placeholder="請選擇用例類型" style="width: 100%;"><el-option label="功能測試" value="functional" /><el-option label="接口測試" value="api" /><el-option label="UI測試" value="ui" /></el-select></el-form-item></el-col></el-row><el-form-item label="維護人" prop="maintainer"><el-input v-model="formData.maintainer" placeholder="請輸入維護人名稱" /></el-form-item><el-form-item label="前置條件" prop="precondition"><el-input v-model="formData.precondition" type="textarea" :rows="2" placeholder="請輸入前置條件" /></el-form-item><!-- 測試步驟區域 --><el-form-item label="測試步驟" prop="steps_text_ignored"> <!-- steps_text_ignored 僅用于觸發表單項樣式 --><div class="steps-editor"><div v-for="(step, index) in formData.steps" :key="index" class="step-item"><el-inputv-model="step.description"type="textarea":autosize="{ minRows: 1, maxRows: 4 }"placeholder="請輸入步驟描述"class="step-input"/><el-buttontype="danger":icon="Delete"circlesize="small"@click="removeStep(index)"class="step-action-btn"v-if="formData.steps.length > 1"/></div><el-button type="primary" :icon="Plus" @click="addStep" plain size="small">添加步驟</el-button></div><!-- 隱藏的表單項,用于實際提交 steps_text,由 steps 數組生成 --><el-input v-model="computedStepsText" style="display: none;"></el-input></el-form-item><el-form-item label="預期結果" prop="expected_result"><el-input v-model="formData.expected_result" type="textarea" :rows="3" placeholder="請輸入預期結果" /></el-form-item><el-form-item><el-button type="primary" @click="handleSubmit" :loading="submitLoading">{{ isEditMode ? '更新用例' : '創建用例' }}</el-button><el-button @click="goBack">取消</el-button></el-form-item></el-form></el-card></div>
</template><script setup lang="ts">
import { ref, reactive, computed, onMounted, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { ElMessage, ElPageHeader } from 'element-plus' // 確保導入 ElPageHeader
import type { FormInstance, FormRules } from 'element-plus'
import { Plus, Delete } from '@element-plus/icons-vue'
import { createTestCase, getTestCaseDetail, updateTestCase, type UpsertTestCaseData,type TestCase
} from '@/api/testcase'
import { getModuleList, type Module as ApiModule } from '@/api/module' // 獲取所有模塊用于選擇器interface FormStep {description: string;
}interface TestCaseFormData extends Omit<UpsertTestCaseData, 'steps_text'> {steps: FormStep[]; // 前端用步驟數組來編輯
}const route = useRoute()
const router = useRouter()const pageLoading = ref(false)
const submitLoading = ref(false)
const testCaseFormRef = ref<FormInstance>()const testCaseId = computed(() => route.params.id ? Number(route.params.id) : null)
const isEditMode = computed(() => !!testCaseId.value)
const pageTitle = computed(() => (isEditMode.value ? '編輯測試用例' : '新建測試用例'))// 所屬模塊選擇器的數據
const moduleOptions = ref<ApiModule[]>([])
const moduleSelectLoading = ref(false)const initialFormData: TestCaseFormData = {name: '',description: null,module: undefined as number | undefined, // 確保初始為 undefined 以便 placeholder 顯示priority: 'P1',precondition: null,steps: [{ description: '' }], // 至少有一個空步驟expected_result: '',case_type: 'functional',maintainer: null,
}
const formData = reactive<TestCaseFormData>({ ...initialFormData })const formRules = reactive<FormRules>({name: [{ required: true, message: '用例名稱不能為空', trigger: 'blur' }],module: [{ required: true, message: '請選擇所屬模塊', trigger: 'change' }],priority: [{ required: true, message: '請選擇優先級', trigger: 'change' }],steps: [{ // 對 steps 數組的校驗 (雖然我們主要校驗合并后的 steps_text)type: 'array', required: true, validator: (rule, value, callback) => {if (!value || value.length === 0 || value.every((step: FormStep) => !step.description.trim())) {callback(new Error('測試步驟不能為空'));} else {callback();}}, trigger: 'change' }],expected_result: [{ required: true, message: '預期結果不能為空', trigger: 'blur' }],
})// 將步驟數組轉換為提交給后端的 steps_text 字符串
const computedStepsText = computed(() => {return formData.steps.map(step => step.description.trim()).filter(desc => desc).join('\n');
})// 加載模塊列表給選擇器
const fetchModulesForSelect = async () => {if (moduleOptions.value.length > 0 && !isEditMode.value) return; // 避免重復加載 (新建時如果已有數據則不重載)// 編輯時可能需要強制重載,或當模塊列表不常變時緩存moduleSelectLoading.value = true;try {// 這里獲取所有模塊,實際項目中可能需要分頁或搜索// 如果模塊非常多,這里需要優化,例如使用遠程搜索的 Selectconst response = await getModuleList(); // 這個 getModuleList 需要支持不傳 projectId 獲取所有moduleOptions.value = response.data;} catch (error) {console.error('獲取模塊列表失敗:', error);ElMessage.error('獲取模塊列表失敗');} finally {moduleSelectLoading.value = false;}
}// 加載用例詳情 (編輯模式)
const loadTestCaseDetail = async () => {if (!isEditMode.value || !testCaseId.value) returnpageLoading.value = truetry {const response = await getTestCaseDetail(testCaseId.value)const dataFromServer = response.data// 回填表單數據formData.name = dataFromServer.nameformData.description = dataFromServer.descriptionformData.module = dataFromServer.moduleformData.priority = dataFromServer.priorityformData.precondition = dataFromServer.preconditionformData.expected_result = dataFromServer.expected_resultformData.case_type = dataFromServer.case_typeformData.maintainer = dataFromServer.maintainer// 將 steps_text 解析回步驟數組if (dataFromServer.steps_text) {formData.steps = dataFromServer.steps_text.split('\n').map(desc => ({ description: desc }))if (formData.steps.length === 0) { // 保證至少有一個空步驟輸入框formData.steps.push({ description: '' });}} else {formData.steps = [{ description: '' }]}// 確保模塊選擇器中有當前模塊的選項,如果沒有,需要手動獲取一次模塊列表(或者在 fetchModulesForSelect 中處理)if (formData.module && !moduleOptions.value.find(m => m.id === formData.module)) {await fetchModulesForSelect(); // 重新獲取模塊列表以確保包含當前模塊}} catch (error) {ElMessage.error('獲取用例詳情失敗')console.error(error)} finally {pageLoading.value = false}
}onMounted(async () => {await fetchModulesForSelect(); // 先加載模塊選項if (isEditMode.value) {await loadTestCaseDetail()}
})// 動態步驟管理
const addStep = () => {formData.steps.push({ description: '' })
}
const removeStep = (index: number) => {if (formData.steps.length > 1) {formData.steps.splice(index, 1)} else {ElMessage.warning('至少需要一個測試步驟')}
}const handleSubmit = async () => {if (!testCaseFormRef.value) returnawait testCaseFormRef.value.validate(async (valid) => {if (valid) {// 再次校驗 steps 是否真的有內容(因為 formRules 對數組的校驗可能不夠精細)if (!computedStepsText.value.trim()) {ElMessage.error('測試步驟描述不能為空');return;}submitLoading.value = trueconst dataToSubmit: UpsertTestCaseData = {name: formData.name,description: formData.description,module: formData.module!, // module 是必填的,這里可以用 ! 斷言priority: formData.priority,precondition: formData.precondition,steps_text: computedStepsText.value, // 使用合并后的文本expected_result: formData.expected_result,case_type: formData.case_type,maintainer: formData.maintainer,}try {if (isEditMode.value && testCaseId.value) {await updateTestCase(testCaseId.value, dataToSubmit)ElMessage.success('測試用例更新成功!')} else {await createTestCase(dataToSubmit)ElMessage.success('測試用例創建成功!')}// 成功后可以跳轉到用例列表頁或詳情頁// router.push(`/testcases/list?moduleId=${formData.module}`) // 假設有列表頁router.push({ name: 'testcases', query: { moduleId: formData.module } }) // 假設列表頁路由名為 'testcases'} catch (error) {console.error('用例操作失敗:', error)// 全局錯誤提示已處理} finally {submitLoading.value = false}} else {ElMessage.error('請檢查表單填寫是否正確!')return false}})
}const goBack = () => {router.back()
}
</script><style scoped lang="scss">
.testcase-edit-view {padding: 20px;
}
.page-header-custom {margin-bottom: 20px;background-color: #fff;padding: 16px 24px;border-radius: 4px;box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
}
.form-card {padding: 20px;
}.steps-editor {width: 100%;.step-item {display: flex;align-items: center;margin-bottom: 10px;.step-input {flex-grow: 1;margin-right: 10px;}.step-action-btn {// flex-shrink: 0; // 防止按鈕被壓縮}}
}
</style>

代碼解釋與關鍵點:

  • 整體布局: 使用 ElPageHeader 提供返回和標題,ElCard 包裹表單。
  • 表單字段: 根據 TestCase 模型定義了各個輸入項。
    • 所屬模塊 (formData.module):
      • 使用 el-select
      • moduleOptions 通過調用 getModuleList() API (在 api/module.ts 中) 來獲取所有模塊(這里暫時獲取所有,實際項目可能需要優化為按項目篩選或遠程搜索)。
      • fetchModulesForSelect 在組件掛載時以及 Select 獲得焦點時(如果選項為空)被調用。
      • 重要: api/module.ts 中的 getModuleList 函數需要能夠不傳 projectId 時返回所有模塊,或者你需要一個新的 API 來獲取所有模塊。這里假設 getModuleList() 可以無參數調用以獲取所有模塊。
  • 動態測試步驟 (formData.steps):
    • formData.steps 是一個響應式數組,每個元素是 { description: string } 對象。
    • 使用 v-for 渲染每個步驟的輸入框和刪除按鈕。
    • addStep() 方法向數組中添加一個新的空步驟。
    • removeStep(index) 方法從數組中刪除指定索引的步驟(至少保留一個)。
    • computedStepsText 這是一個計算屬性,它將 formData.steps 數組中的 description 合并成一個用換行符 \n 分隔的字符串。這個計算屬性的值將用于賦值給提交給后端的 steps_text 字段。我們在模板中添加了一個隱藏的 el-input 綁定到它,主要是為了方便調試時查看,實際提交時我們直接用這個計算屬性的值。
    • 表單校驗 (formRules.steps): 我們為 steps 數組添加了一個自定義校驗器,確保至少有一個步驟并且步驟描述不全為空。
  • 編輯模式 (isEditMode):
    • 通過 route.params.id 判斷當前是新建還是編輯模式。
    • pageTitle 動態顯示。
    • onMounted 中,如果是編輯模式,則調用 loadTestCaseDetail()
  • loadTestCaseDetail()
    • 調用 getTestCaseDetail API 獲取用例數據。
    • 回填表單各個字段。
    • 解析 steps_text 將從后端獲取的 steps_text 字符串按換行符分割,轉換回 formData.steps 數組。
    • 模塊選擇器回顯: 確保在編輯時,如果 formData.module 有值,moduleOptions 中包含該選項,否則 Select 可能無法正確顯示已選模塊。如果 moduleOptions 中沒有,則重新調用 fetchModulesForSelect
  • handleSubmit()
    • 表單校驗。
    • 特別校驗 computedStepsText 確保合并后的步驟文本不為空。
    • 構造提交給后端的數據 dataToSubmit,其中 steps_text 使用 computedStepsText.value
    • 根據 isEditMode 調用 createTestCaseupdateTestCase API。
    • 成功后跳轉到用例列表頁 (我們暫時假設用例列表頁的路由名為 testcases,并且可以通過 moduleId 查詢參數篩選)。

修改 frontend/src/api/module.ts 中的 getModuleList

為了讓模塊選擇器能獲取所有模塊,我們需要修改 api/module.ts 中的 getModuleList 函數,使其在不傳遞 projectId 時能獲取所有模塊。

假設后端 /api/modules/ 在沒有 project_id 參數時返回所有模塊,那么前端 getModuleList 可以這樣:
在這里插入圖片描述

// test-platform/frontend/src/api/module.ts
// ...
// 1. 獲取模塊列表 (支持按項目ID過濾,不傳則獲取所有)
export function getModuleList(projectId?: number): AxiosPromise<ModuleListResponse> {const params: { project_id?: number } = {};if (projectId) {params.project_id = projectId;}return request({url: '/modules/',method: 'get',params // 如果 projectId 未定義,則 params 為空對象,不傳 project_id 參數})
}
// ...

后端 DRF ModuleViewSet 的相應調整:

確保 ModuleViewSetget_queryset 方法在 project_id 未提供時返回所有模塊。目前的實現(在上一篇文章中修改的)已經是這樣了,如果 project_idNone,則不過濾。
在這里插入圖片描述

# api/views.py -> ModuleViewSet
class ModuleViewSet(viewsets.ModelViewSet):# ...def get_queryset(self):queryset = super().get_queryset()project_id = self.request.query_params.get('project_id', None) # 修改這里,如果沒傳,project_id 為 Noneif project_id is not None: # 只有當 project_id 實際傳遞了才過濾try:queryset = queryset.filter(project_id=int(project_id))except ValueError:pass return queryset.order_by('-create_time')

第三步:配置測試用例編輯頁的路由

打開 frontend/src/router/index.ts,添加新建和編輯測試用例的路由。
在這里插入圖片描述

// test-platform/frontend/src/router/index.ts
// ... (在 Layout 的 children 中添加){path: '/testcases', // 用例列表頁 (我們將在下一篇創建)name: 'testcases',component: () => import('../views/project/TestCaseListView.vue'),meta: { title: '用例管理', requiresAuth: true }},{path: '/testcase/create', // 新建用例name: 'testcaseCreate',component: () => import('../views/testcase/TestCaseEditView.vue'),meta: { title: '新建測試用例', requiresAuth: true }},{path: '/testcase/edit/:id', // 編輯用例,:id 是用例IDname: 'testcaseEdit',component: () => import('../views/testcase/TestCaseEditView.vue'),meta: { title: '編輯測試用例', requiresAuth: true },props: true // 將路由參數 id 作為 props 傳遞給組件 (雖然我們組件內主要用 route.params)},
// ...

說明:

  • 我們為新建 (/testcase/create) 和編輯 (/testcase/edit/:id) 都指向了同一個 TestCaseEditView.vue 組件。組件內部通過 route.params.id 是否存在來區分模式。
  • 為編輯路由啟用了 props: true,雖然我們當前組件實現主要依賴 useRoute(),但這是一個好習慣。

第四步:測試用例新增&編輯功能

  1. 確保前后端服務運行,CORS 和 API 正常。
  2. 測試新建用例:
    • 訪問 http://127.0.0.1:5173/testcase/create

    • 填寫表單,包括選擇所屬模塊,添加幾個測試步驟。

    • 點擊“創建用例”。
      在這里插入圖片描述

    • 觀察 Network 面板的 API 請求 (POST /api/testcases/),查看請求體中的 steps_text 是否是合并后的字符串。
      在這里插入圖片描述

    • 看是否成功創建并跳轉 (跳轉目標頁 TestCaseListView.vue 尚不存在,會顯示空白,但 API 調用是會成功的)。
      在這里插入圖片描述

    • 去 Django Admin 或通過 API 確認用例已創建,steps_text 已保存。

  3. 測試編輯用例 (需要先通過 API 或 Django Admin 創建一個用例):
    • 在上面我創建了一個 ID 為 3 的用例。

    • 訪問 http://127.0.0.1:5173/testcase/edit/3

    • 頁面應加載該用例的數據,表單應被回填,測試步驟應被正確解析并顯示。
      在這里插入圖片描述

    • 修改數據,例如增刪步驟,修改其他字段。

    • 點擊“更新用例”。

    • 觀察 Network 面板的 API 請求 (PUT /api/testcases/3/)。

    • 確認更新成功。
      在這里插入圖片描述

總結

在這篇文章中,我們攻克了測試用例管理中復雜的編輯界面設計與實現:

  • ? 為測試用例創建了相應的 API 服務函數 (api/testcase.ts) 和 TypeScript 類型。
  • ? 設計并實現了 TestCaseEditView.vue 組件,用于新建和編輯測試用例,其核心特性包括:
    • 一個包含用例名稱、描述、所屬模塊選擇器、優先級、類型、前置條件、預期結果等字段的綜合表單。
    • 一個動態的測試步驟編輯區域,用戶可以方便地添加、刪除和編輯多個步驟描述。
    • 將前端編輯的步驟數組通過計算屬性 computedStepsText 合并為后端 steps_text 字段所需的單一字符串。
    • 在編輯模式下,能夠從后端 API 獲取用例詳情,并將 steps_text 解析回步驟數組以供編輯。
  • ? 配置了新建和編輯測試用例的路由。
  • ? 指導了如何測試新建和編輯用例的完整流程,并與后端 API 進行了聯調。
  • ? 解決了模塊選擇器的數據加載和編輯時選項回顯的問題。

測試用例的創建和編輯是測試平臺的核心功能,一個良好設計的界面能極大地提升測試人員的效率。

在下一篇文章中,我們將實現測試用例的列表展示與搜索功能 (TestCaseListView.vue),讓用戶能夠方便地查看、篩選和查找已創建的測試用例,并從列表跳轉到編輯頁面。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/83161.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/83161.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/83161.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

三、web安全-信息收集

1、信息搜集的重要性 &#xff08;1&#xff09;明確攻擊面 信息搜集能讓滲透測試人員清晰地勾勒出目標系統的邊界&#xff0c;包括其網絡拓撲結構、開放的服務端口、運行的軟件系統等。例如&#xff0c;通過信息搜集發現目標企業除了對外提供官網服務外&#xff0c;還有一個…

生活小記啊

最近生活上的事情還是蠻多的&#xff0c;想到哪寫到哪。 工作 三月的某個周六&#xff0c;正在加班寫技術方案&#xff0c;大晚上寫完了聽到調動通知&#xff0c;要去新的團隊了。 還是蠻不舍的&#xff0c;看著產品從無到有&#xff0c;一路走過來&#xff0c;傾注了不少感…

vue-08(使用slot進行靈活的組件渲染)

使用slot進行靈活的組件渲染 作用域slot是 Vue.js 中的一種強大機制&#xff0c;它允許父組件自定義子組件內容的呈現。與僅向下傳遞數據的常規 props 不同&#xff0c;作用域 slot 為父級提供了一個模板&#xff0c;然后子級可以填充數據。這提供了高度的靈活性和可重用性&am…

MySQL索引與性能優化入門:讓查詢提速的秘密武器【MySQL系列】

本文將深入講解 MySQL 索引的底層原理、常見類型、使用技巧&#xff0c;并結合 EXPLAIN 工具分析查詢執行計劃&#xff0c;配合慢查詢日志識別瓶頸&#xff0c;逐步建立起系統的 MySQL 查詢優化知識體系。適合有一定基礎、希望在數據量增長或面試中脫穎而出的開發者閱讀。 一、…

C 語言開發中常見的開發環境

目錄 1.Dev-C 2.Visual Studio Code 3.虛擬機 Linux 環境 4.嵌入式 MCU 專用開發環境 1.Dev-C 使用集成的 C/C 開發環境&#xff08;適合基礎學習&#xff09;,下載鏈接Dev-C下載 - 官方正版 - 極客應用 2.Visual Studio Code 結合 C/C 擴展 GCC/MinGW 編譯器&#xff0c…

STM32G4 電機外設篇(二) VOFA + ADC + OPAMP

目錄 一、STM32G4 電機外設篇&#xff08;二&#xff09; VOFA ADC OPAMP1 VOFA1.1 VOFA上位機顯示波形 2 ADC2.1 用ADC規則組對板載電壓和電位器進行采樣 3 OPAMP&#xff08;運放&#xff09;3.1 結合STM32內部運放和ADC來完成對三相電流的采樣3.2 運放電路分析 附學習參考…

再見Notepad++,你好Notepad--

Notepad-- 是一款國產開源的輕量級、跨平臺文本編輯器&#xff0c;支持 Window、Linux、macOS 以及國產 UOS、麒麟等操作系統。 除了具有常用編輯器的功能之外&#xff0c;Notepad-- 還內置了專業級的代碼對比功能&#xff0c;支持文件、文件夾、二進制文件的比對&#xff0c;支…

跳動的愛心

跳動的心形圖案&#xff0c;通過字符打印和延時效果模擬跳動&#xff0c;心形在兩種大小間交替跳動。 通過數學公式生成心形曲線 #include <stdio.h> #include <windows.h> // Windows 系統頭文件&#xff08;用于延時和清屏&#xff09; void printHeart(int …

2.2HarmonyOS NEXT高性能開發技術:編譯優化、內存管理與并發編程實踐

HarmonyOS NEXT高性能開發技術&#xff1a;編譯優化、內存管理與并發編程實踐 在HarmonyOS NEXT全場景設備開發中&#xff0c;高性能是跨端應用體驗的核心保障。本章節聚焦ArkCompiler編譯優化、內存管理工具及多線程并發編程三大技術模塊&#xff0c;結合實戰案例解析底層實現…

C# 類和繼承(使用基類的引用)

使用基類的引用 派生類的實例由基類的實例和派生類新增的成員組成。派生類的引用指向整個類對象&#xff0c;包括 基類部分。 如果有一個派生類對象的引用&#xff0c;就可以獲取該對象基類部分的引用&#xff08;使用類型轉換運算符把 該引用轉換為基類類型&#xff09;。類…

如何在騰訊云 OpenCloudOS 上安裝 Docker 和 Docker Compose

從你提供的 /etc/os-release 文件內容來看&#xff0c;你的服務器運行的是 OpenCloudOS 9.2。這是一個基于 CentOS 和 RHEL 的開源操作系統&#xff0c;因此它屬于 CentOS/RHEL 系列。 關鍵信息總結 操作系統名稱&#xff1a;OpenCloudOS版本&#xff1a;9.2ID&#xff1a;op…

趨勢直線指標

趨勢直線副圖和主圖指標&#xff0c;旨在通過技術分析工具幫助交易者識別市場趨勢和潛在的買賣點。 副圖指標&#xff1a;基于KDJ指標的交易策略 1. RSV值計算&#xff1a; - RSV&#xff08;未成熟隨機值&#xff09;反映了當前收盤價在過去一段時間內的相對位置。通過計算當前…

FEMFAT許可分析的數據可視化方法

隨著企業對FEMFAT軟件使用的增加&#xff0c;如何有效地管理和分析許可數據成為了關鍵。數據可視化作為一種強大的工具&#xff0c;能夠幫助企業直觀地理解FEMFAT許可的使用情況&#xff0c;從而做出更明智的決策。本文將介紹FEMFAT許可分析的數據可視化方法&#xff0c;并探討…

AMBER軟件介紹

AMBER軟件介紹 AMBER&#xff08;Assisted Model Building with Energy Refinement&#xff09;是一套廣泛應用于分子動力學&#xff08;MD&#xff09;模擬和生物分子結構分析的軟件工具集&#xff0c;尤其在蛋白質、核酸、多糖等生物大分子的模擬中表現突出。以下是關于AMBE…

GoogLeNet網絡模型

GoogLeNet網絡模型 誕生背景 在2014年的ImageNet圖像識別挑戰賽中&#xff0c;一個GoogLeNet的網絡架構大放異彩&#xff0c;與VGG不同的是&#xff0c;VGG用的是3*3的卷積&#xff0c;而GoogLeNet從1*1到7*7的卷積核都用&#xff0c;也就是使用不同大小的卷積核組合。 網絡…

Free2AI:企業智能化轉型的加速器

隨著數字化與智能化的深度交融&#xff0c;企業的競爭舞臺已悄然轉變為數據處理能力和智能服務水平的競技場。Free2AI以其三大核心功能——智能數據采集、多格式文檔解析、智能FAQ構建&#xff0c;為企業鋪設了一條從數據洞察到智能服務的全鏈路升級之路&#xff0c;成為推動企…

Vue 核心技術與實戰day07

1. vuex概述 2. 構建 vuex [多組件數據共享] 環境 <template><div id"app"><h1>根組件- {{ title }}- {{ count }}</h1><input :value"count" input"handleInput" type"text"><Son1></Son1>…

【原神 × 插入排序】刷圣遺物也講算法:圣遺物評分系統背后的排序邏輯你真的懂嗎?

?? 改編自:王爭《數據結構與算法之美》 ?? 游戲演繹:米哈游《原神》 ?? 核心關鍵詞:插入排序、排序算法、評分系統、屬性評價、強化圣遺物、冒泡排序對比 ?? 引言:原神刷本=刷排序? 玩《原神》的玩家每天日常是啥?體力用來刷圣遺物、精通頭、暴擊頭、攻充沙………

quasar electron mode如何打包無邊框桌面應用程序

預覽 開源項目Tokei Kun 一款簡潔的周年紀念app&#xff0c;現已發布APK&#xff08;安卓&#xff09;和 EXE&#xff08;Windows&#xff09; 項目倉庫地址&#xff1a;Github Repo 應用下載鏈接&#xff1a;Github Releases Preparation for Electron quasar dev -m elect…

微信小程序真機調試時如何實現與本地開發環境服務器交互

最近在開發微信小程序項目,真機調試時需要在手機上運行小程序,為了實現本地開發服務器與手機小程序的交互,需要以下步驟 1.將手機連到和本地一樣的局域網 2.Visual Studio中將IIS Express服務器的localhost端口地址修改為本機的IP自定義的端口: 1&#xff09;找到web api項目…