十一、【核心功能篇】测试用例管理:设计用例新增编辑界面
【核心功能篇】测试用例管理:设计用例新增&编辑界面
- 前言
- 准备工作
- 第一步:创建测试用例相关的 API 服务 (`src/api/testcase.ts`)
- 第二步:创建测试用例编辑页面组件 (`src/views/testcase/TestCaseEditView.vue`)
- 第三步:配置测试用例编辑页的路由
- 第四步:测试用例新增&编辑功能
- 总结
前言
一个好的测试用例编辑界面应该具备以下特点:
- 清晰直观: 用户能够快速理解各个字段的含义和作用。
- 高效易用: 能够方便地输入和修改信息,特别是对于重复性的测试步骤。
- 结构化: 能够清晰地展示和管理测试步骤等复杂结构。
- 可扩展性: (虽然本篇可能不完全覆盖)未来可以方便地增加对参数化、数据驱动、自定义关键字等高级功能的支持。
在我们的 Django 后端模型 (api/models.py
中的 TestCase
) 中,我们暂时将 steps_text
定义为一个 TextField
,用于存储文本描述的测试步骤。为了实现一个更结构化的步骤编辑界面,前端在提交时需要将这些步骤组织成一种格式(例如 JSON 数组或特定分隔符的文本),而后端在保存或解析时需要能处理这种格式。
这篇文章将带你:
- 规划测试用例编辑页面的整体布局和交互。
- 设计并实现一个能够动态添加、删除和编辑测试步骤的表单区域。
- 将测试用例的创建和编辑功能与后端 API 进行联调。
我们将重点放在前端界面的设计与实现,以及与后端 API 的数据交互上。
准备工作
- 前端项目就绪:
test-platform/frontend
项目可以正常运行 (npm run dev
)。 - 后端 API 运行中: Django 后端服务运行(
python manage.py runserver
),测试用例的 API (/api/testcases/
) 可用。 - Axios 和 API 服务已封装:
utils/request.ts
和api/project.ts
,api/module.ts
已配置好。 - 项目和模块管理功能可用: 我们需要先有项目和模块,才能创建测试用例。
- 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
调用createTestCase
或updateTestCase
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
的相应调整:
确保 ModuleViewSet
的 get_queryset
方法在 project_id
未提供时返回所有模块。目前的实现(在上一篇文章中修改的)已经是这样了,如果 project_id
为 None
,则不过滤。
# 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()
,但这是一个好习惯。
第四步:测试用例新增&编辑功能
- 确保前后端服务运行,CORS 和 API 正常。
- 测试新建用例:
-
访问
http://127.0.0.1:5173/testcase/create
。 -
填写表单,包括选择所属模块,添加几个测试步骤。
-
点击“创建用例”。
-
观察 Network 面板的 API 请求 (
POST /api/testcases/
),查看请求体中的steps_text
是否是合并后的字符串。
-
看是否成功创建并跳转 (跳转目标页
TestCaseListView.vue
尚不存在,会显示空白,但 API 调用是会成功的)。
-
去 Django Admin 或通过 API 确认用例已创建,
steps_text
已保存。
-
- 测试编辑用例 (需要先通过 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
),让用户能够方便地查看、筛选和查找已创建的测试用例,并从列表跳转到编辑页面。
相关文章:

十一、【核心功能篇】测试用例管理:设计用例新增编辑界面
【核心功能篇】测试用例管理:设计用例新增&编辑界面 前言准备工作第一步:创建测试用例相关的 API 服务 (src/api/testcase.ts)第二步:创建测试用例编辑页面组件 (src/views/testcase/TestCaseEditView.vue)第三步:配置测试用例…...
react-native的token认证流程
在 React Native 中实现 Token 认证是移动应用开发中的常见需求,它用于验证用户的身份并授权其访问受保护的 API 资源。 Token 认证的核心流程: 用户登录 (Login): 用户在前端输入用户名和密码。前端将这些凭据发送到后端 API。后端验证凭据。如果验证成…...
ERP系统中商品定价功能设计:支持渠道、会员与批发场景的灵活定价机制
在现代零售、批发与电商环境下,商品的定价策略日益复杂。一个优秀的ERP系统不仅需要管理商品基础信息、库存与订单,还必须提供一套灵活且可扩展的商品定价机制,以满足: 不同销售渠道(如线上平台、线下门店、分销商&…...

Spring是如何实现属性占位符解析
Spring属性占位符解析 核心实现思路1️⃣ 定义占位符处理器类2️⃣ 处理 BeanDefinition 中的属性3️⃣ 替换具体的占位符4️⃣ 加载配置文件5️⃣ Getter / Setter 方法 源码见:mini-spring 在使用 Spring 框架开发过程中,为了实现配置的灵活性…...
数据结构之ArrayList
系列文章目录 目录 系列文章目录 前言 一、数据结构的前置语法 1. 时空复杂度 2. 包装类 3. 泛型 二、ArrayList 和顺序表 1. 顺序表的模拟实现 2. 源码 3. ArrayList 的优缺点 前言 本文介绍数据结构的前置算法,以及 ArrayList 的模拟实现,部…...

DDR4读写压力测试
1.1测试环境 1.1.1整体环境介绍 板卡: pcie-403板卡 主控芯片: Xilinx xcvu13p-fhgb2104-2 调试软件: Vivado 2018.3 代码环境: Vscode utf-8 测试工程: pcie403_user_top 1.1.2硬件介绍 UD PCIe-403…...
uniapp 开发企业微信小程序时,如何在当前页面真正销毁前或者关闭小程序前调用一个api接口
在 UniApp 开发企业微信小程序时,若需在页面销毁或小程序关闭前调用 API 接口,需结合页面生命周期和应用生命周期实现。以下是具体实现方案及注意事项: 一、在页面销毁前调用 API(页面级) 通过页面生命周期钩子 onUnl…...
WPF 按钮点击音效实现
WPF 按钮点击音效实现 下面我将为您提供一个完整的 WPF 按钮点击音效实现方案,包含多种实现方式和高级功能: 完整实现方案 MainWindow.xaml <Window x:Class"ButtonClickSound.MainWindow"xmlns"http://schemas.microsoft.com/win…...

编写测试用例
测试用例(Test Case)是用于测试系统的要素集合 目录 编写测试用例作用 编写测试用例要包含七大元素 测试用例的设计方法 1、等价类法 2、边界值法 3、正交表法 4、判定表法 5、错误推测法 6、场景法 编写测试用例作用 1、确保功能全面覆盖…...
解释程序(Python)不需要生成机器码 逐行解析 逐行执行
在计算机组成原理中,解释程序(Interpreter)通常不会生成独立的机器码,但具体情况取决于解释器的实现方式。以下是详细分析: 1. 传统解释程序:不生成机器码 直接逐行执行: 经典的解释器ÿ…...

每日Prompt:隐形人
提示词 黑色棒球帽,白色抹胸、粉色低腰短裙、白色襪子,黑色鞋子,粉紅色背包,衣服悬浮在空中呈现动态姿势,虚幻引擎渲染风格,高清晰游戏CG质感,户外山林背景,画面聚焦在漂浮的衣服上…...

TensorFlow深度学习实战(19)——受限玻尔兹曼机
TensorFlow深度学习实战(19)——受限玻尔兹曼机 0. 前言1. 受限玻尔兹曼机1.1 受限玻尔兹曼机架构1.2 受限玻尔兹曼机的数学原理 2. 使用受限玻尔兹曼机重建图像3. 深度信念网络小结系列链接 0. 前言 受限玻尔兹曼机 (Restricted Boltzmann Machine, RB…...

告别手动绘图!基于AI的Smart Mermaid自动可视化图表工具搭建与使用指南
以下是对Smart Mermaid的简单介绍: 一款基于 AI 技术的 Web 应用程序,可将文本内容智能转换为 Mermaid 格式的代码,并将其渲染成可视化图表可以智能制作流程图、序列图、甘特图、状态图等等,并且支持在线调整、图片导出可以Docke…...

【Oracle】安装单实例
个人主页:Guiat 归属专栏:Oracle 文章目录 1. 安装前的准备工作1.1 硬件和系统要求1.2 检查系统环境1.3 下载Oracle软件 2. 系统配置2.1 创建Oracle用户和组2.2 配置内核参数2.3 配置用户资源限制2.4 安装必要的软件包 3. 目录结构和环境变量3.1 创建Ora…...
C++测开,自动化测试,业务(第一段实习)
目录 🌼前言 一,实习经历怎么写简历 🌹业务理解 🎂结构化表达 二,实习 🦂技术和流程卡点 🔑实习收获 / 代码风格 三,测试理论,用例设计,工具链 &…...

QT中更新或添加组件时出现“”qt操作至少需要一个处于启用状态的有效资料档案库“解决方法”
在MaintenanceTool.exe中点击下一步 第一个: 第二个: 第三个: 以上任意一个放入资料库中...

论文速读《UAV-Flow Colosseo: 自然语言控制无人机系统》
论文链接:https://arxiv.org/abs/2505.15725项目主页:https://prince687028.github.io/UAV-Flow/ 0. 简介 近年来,无人机技术蓬勃发展,但如何让无人机像智能助手一样理解并执行人类语言指令,仍是一个前沿挑战。现有研…...

ES6+中Promise 中错误捕捉详解——链式调用catch()或者async/await+try/catch
通过 unhandledrejection 捕捉未处理的 Promise 异常,手动将其抛出,最终让 window.onerror 捕捉,从而统一所有异常的处理逻辑 规范代码:catch(onRejected)、async...awaittry...catch 在 JavaScript 的 Pro…...
CDN安全加速:HTTPS加密最佳配置方案
CDN安全加速的HTTPS加密最佳配置方案需从证书管理、协议优化、安全策略到性能调优进行全链路设计,以下是核心实施步骤与注意事项: 一、证书配置与管理 证书选择与格式 证书类型:优先使用受信任CA机构颁发的DV/OV/EV证…...

解常微分方程组
Euler法 function euler_method % 参数设置 v_missile 450; % 导弹速度 km/h v_enemy 90; % 敌艇速度 km/h % 初始条件 x0 0; % 导弹初始位置 x y0 0; % 导弹初始位置 y xe0 120; % 敌艇初始位置 y t0 0; % 初始时间 % 时间步长和总时间 dt 0.01; % 时间步长 t_final …...

C++实现汉诺塔游戏自动完成
目录 一、汉诺塔的规则二、数学递归推导式三、步骤实现(一)汉诺塔模型(二)递归实现(三)显示1.命令行显示2.SDL图形显示 四、处理用户输入及SDL环境配置五、总结六、源码下载 一、汉诺塔的规则 游戏由3根柱子和若干大小不一的圆盘组成,初始状态下,所有的…...
在 ABP VNext 中集成 Serilog:打造可观测、结构化日志系统
🚀 在 ABP VNext 中集成 Serilog:打造可观测、结构化日志系统 📚 目录 🚀 在 ABP VNext 中集成 Serilog:打造可观测、结构化日志系统1. 为什么要使用结构化日志? 🤔2. 核心集成步骤 Ὦ…...

pikachu靶场通关笔记07 XSS关卡03-存储型XSS
目录 一、XSS 二、存储型XSS 三、源码分析 四、渗透实战 1、输入mooyuan试一试 2、注入Payload 3、查看数据库 4、再次进入留言板页面 本系列为通过《pikachu靶场通关笔记》的XSS关卡(共10关)渗透集合,通过对XSS关卡源码的代码审计找到XSS风险的…...
GitLab CI、GitHub Actions和Jenkins进行比较
特性/工具JenkinsGitLab CIGitHub Actions架构设计哲学Master/Agent分布式架构,通过插件扩展功能代码与CI/CD强耦合,内置Git仓库,基于Runner注册机制事件驱动,与GitHub深度集成,基于虚拟机的Job执行单元核心运行机制支…...
strcat及其模拟实现
#define _CRT_SECURE_NO_WARNINGS strcat 追加字符串 str "string"(字符串) cat "concatenate"(连接 / 追加) char* strcat(char* destination, const char* source); strcat的应用 方法一ÿ…...

OpenCV CUDA模块直方图计算------用于在 GPU 上执行对比度受限的自适应直方图均衡类cv::cuda::CLAHE
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 cv::cuda::CLAHE 是 OpenCV 的 CUDA 模块中提供的一个类,用于在 GPU 上执行对比度受限的自适应直方图均衡(Contrast Limi…...

华为OD机试真题——矩形绘制(2025A卷:200分)Java/python/JavaScript/C/C++/GO最佳实现
2025 A卷 200分 题型 本专栏内全部题目均提供Java、python、JavaScript、C、C++、GO六种语言的最佳实现方式; 并且每种语言均涵盖详细的问题分析、解题思路、代码实现、代码详解、3个测试用例以及综合分析; 本文收录于专栏:《2025华为OD真题目录+全流程解析+备考攻略+经验分…...
通义开源视觉感知多模态 RAG 推理框架 VRAG-RL:开启多模态推理新时代
通义实验室的自然语言智能团队,凭借深厚的技术积累与创新精神,成功研发并开源了视觉感知多模态 RAG 推理框架 VRAG-RL,为 AI 在复杂视觉信息处理领域带来了重大突破。 传统 RAG 方法的局限 传统的检索增强型生成(RAG࿰…...
爬虫入门:从基础到实战全攻略
🧠 一、爬虫基础概念 1.1 爬虫定义 爬虫(Web Crawler)是模拟浏览器行为,自动向服务器发送请求并获取响应数据的一种程序。主要用于从网页中提取结构化数据,供后续分析、展示或存储使用。 1.2 爬虫特点 数据碎片化&…...
qemu安装risc-V 64
参考这篇文章https://developer.aliyun.com/article/1323996,其中在wsl下面安装可能会报错环境变量中有空格。 # clean_path.sh#!/bin/bash# 备份旧 PATH OLD_PATH"$PATH"# 过滤掉包含空格、制表符、换行的路径 CLEAN_PATH"" IFS: read -ra PA…...