refactor: update case API to use camelCase for parameters and add import/export functionality for case templates

This commit is contained in:
qiaoxinjiu
2026-04-29 17:14:31 +08:00
parent f3476a8eec
commit 916248483c
3 changed files with 217 additions and 33 deletions

View File

@@ -33,10 +33,14 @@ export function deleteModule(data) {
} }
export function getCaseList(projectId, params) { export function getCaseList(projectId, params) {
const query = Object.assign({}, params || {})
if (projectId !== undefined && projectId !== null && projectId !== '') {
query.projectId = projectId
}
return request({ return request({
url: '/case/list', url: '/case/list',
method: 'get', method: 'get',
params: Object.assign({ project_id: projectId }, params || {}) params: query
}) })
} }
@@ -45,8 +49,8 @@ export function getCaseDetail(projectId, caseId) {
url: '/case/detail', url: '/case/detail',
method: 'get', method: 'get',
params: { params: {
project_id: projectId, projectId,
id: caseId caseId
} }
}) })
} }
@@ -55,7 +59,7 @@ export function createCase(projectId, data) {
return request({ return request({
url: '/case/create', url: '/case/create',
method: 'post', method: 'post',
data: Object.assign({ project_id: projectId }, data) data: Object.assign({ projectId }, data)
}) })
} }
@@ -63,7 +67,7 @@ export function updateCase(projectId, caseId, data) {
return request({ return request({
url: '/case/update', url: '/case/update',
method: 'post', method: 'post',
data: Object.assign({ project_id: projectId, id: caseId }, data) data: Object.assign({ projectId, caseId }, data)
}) })
} }
@@ -72,8 +76,8 @@ export function deleteCase(projectId, caseId) {
url: '/case/delete', url: '/case/delete',
method: 'post', method: 'post',
data: { data: {
project_id: projectId, projectId,
id: caseId caseId
} }
}) })
} }

View File

@@ -2,8 +2,44 @@
<div class="page-wrap"> <div class="page-wrap">
<page-section :title="form.id ? '编辑用例' : '新建用例'"> <page-section :title="form.id ? '编辑用例' : '新建用例'">
<el-form ref="form" :model="form" :rules="rules" label-width="120px" size="small"> <el-form ref="form" :model="form" :rules="rules" label-width="120px" size="small">
<el-form-item label="项目ID"> <el-form-item label="产品" prop="productId">
<el-input v-model="projectId" style="width: 200px;"></el-input> <el-select
v-model="form.productId"
filterable
clearable
placeholder="请选择产品"
style="width: 360px;"
@change="handleProductChange"
@focus="loadProductOptions">
<el-option v-for="item in productOptions" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item label="项目" prop="projectId">
<el-select
v-model="form.projectId"
filterable
clearable
placeholder="请选择项目"
style="width: 360px;"
:disabled="!form.productId"
@change="handleProjectChange">
<el-option v-for="item in projectOptions" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item label="模块" prop="moduleId">
<el-select
v-model="form.moduleId"
filterable
clearable
placeholder="请选择模块"
style="width: 360px;"
:disabled="!form.projectId"
@focus="loadModuleOptions">
<el-option v-for="item in moduleOptions" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item label="用例编号">
<el-input v-model="form.caseKey" placeholder="不填则由后端自动生成"></el-input>
</el-form-item> </el-form-item>
<el-form-item label="标题" prop="title"> <el-form-item label="标题" prop="title">
<el-input v-model="form.title"></el-input> <el-input v-model="form.title"></el-input>
@@ -23,19 +59,32 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="类型"> <el-form-item label="类型">
<el-select v-model="form.case_type"> <el-select v-model="form.caseType">
<el-option label="功能" :value="1"></el-option> <el-option label="功能" :value="1"></el-option>
<el-option label="性能" :value="2"></el-option> <el-option label="性能" :value="2"></el-option>
<el-option label="安全" :value="3"></el-option> <el-option label="安全" :value="3"></el-option>
<el-option label="接口" :value="4"></el-option> <el-option label="接口" :value="4"></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="状态">
<el-select v-model="form.status">
<el-option label="正常" :value="1"></el-option>
<el-option label="已废弃" :value="2"></el-option>
<el-option label="评审中" :value="3"></el-option>
</el-select>
</el-form-item>
<el-form-item label="是否自动化">
<el-select v-model="form.isAuto">
<el-option label="未实现" :value="0"></el-option>
<el-option label="已实现" :value="1"></el-option>
</el-select>
</el-form-item>
<el-form-item label="标签"> <el-form-item label="标签">
<el-input v-model="tagsText" placeholder="多个标签用逗号分隔"></el-input> <el-input v-model="tagsText" placeholder="多个标签用逗号分隔"></el-input>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" :loading="saving" @click="submitForm">保存</el-button> <el-button type="primary" :loading="saving" @click="submitForm">保存</el-button>
<el-button @click="$router.push('/test-platform/cases')">返回</el-button> <el-button @click="backList">返回</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
</page-section> </page-section>
@@ -44,7 +93,9 @@
<script> <script>
import PageSection from '@/components/TestPlatform/common/PageSection' import PageSection from '@/components/TestPlatform/common/PageSection'
import { createCase, getCaseDetail, updateCase } from '@/api/caseApi' import { createCase, getCaseDetail, getModuleTree, updateCase } from '@/api/caseApi'
import { getProductList } from '@/api/productApi'
import { getProjectDetail, getProjectList } from '@/api/projectApi'
export default { export default {
name: 'CaseEditor', name: 'CaseEditor',
@@ -52,59 +103,175 @@ export default {
data() { data() {
return { return {
saving: false, saving: false,
projectId: this.$route.query.projectId || 1, productOptions: [],
projectOptions: [],
moduleOptions: [],
form: { form: {
productId: this.$route.query.productId ? Number(this.$route.query.productId) : '',
projectId: this.$route.query.projectId ? Number(this.$route.query.projectId) : '',
id: '', id: '',
moduleId: '',
caseKey: '',
title: '', title: '',
preconditions: '', preconditions: '',
steps: [], steps: [],
priority: 2, priority: 2,
case_type: 1, caseType: 1,
tags: [] tags: [],
status: 1,
isAuto: 0
}, },
stepsText: '[]', stepsText: '[]',
tagsText: '', tagsText: '',
rules: { rules: {
productId: [{ required: true, message: '请选择产品', trigger: 'change' }],
projectId: [{ required: true, message: '请选择项目', trigger: 'change' }],
moduleId: [{ required: true, message: '请选择模块', trigger: 'change' }],
title: [{ required: true, message: '请输入标题', trigger: 'blur' }], title: [{ required: true, message: '请输入标题', trigger: 'blur' }],
steps: [{ required: true, message: '请输入步骤', trigger: 'change' }] steps: [{ required: true, message: '请输入步骤', trigger: 'change' }]
} }
} }
}, },
methods: { methods: {
loadProductOptions() {
if (this.productOptions && this.productOptions.length > 0) {
return Promise.resolve()
}
return getProductList({ pageNo: 1, pageSize: 1000, status: 1 }).then(res => {
const data = res && res.data ? res.data : res || {}
this.productOptions = data.items || data.list || data.data || []
}).catch(() => {
this.productOptions = []
})
},
loadProjectOptions() {
if (!this.form.productId) {
this.projectOptions = []
return Promise.resolve()
}
return getProjectList({ pageNo: 1, pageSize: 1000, status: 1, productId: this.form.productId }).then(res => {
const data = res && res.data ? res.data : res || {}
this.projectOptions = data.items || data.list || data.data || []
}).catch(() => {
this.projectOptions = []
})
},
loadModuleOptions() {
if (!this.form.projectId) {
this.moduleOptions = []
return Promise.resolve()
}
return getModuleTree({ projectId: this.form.projectId }).then(res => {
const data = (res && res.data) || res || {}
const list = data.list || data.items || []
this.moduleOptions = Array.isArray(list) ? list : []
}).catch(() => {
this.moduleOptions = []
})
},
handleProductChange() {
this.form.projectId = ''
this.form.moduleId = ''
this.projectOptions = []
this.moduleOptions = []
this.loadProjectOptions()
},
handleProjectChange() {
this.form.moduleId = ''
this.moduleOptions = []
this.loadModuleOptions()
},
fetchDetail() { fetchDetail() {
const caseId = this.$route.query.caseId const caseId = this.$route.query.caseId
if (!caseId) { if (!caseId) {
return return
} }
getCaseDetail(this.projectId, caseId).then(res => { getCaseDetail(this.form.projectId, caseId).then(res => {
const data = (res && res.data) || res || {} const data = (res && res.data) || res || {}
this.form = Object.assign({}, this.form, data) this.form = Object.assign({}, this.form, {
this.stepsText = JSON.stringify(data.steps || [], null, 2) id: data.id || caseId,
this.tagsText = (data.tags || []).join(',') moduleId: data.moduleId || data.module_id || '',
caseKey: data.caseKey || data.case_key || '',
title: data.title || '',
preconditions: data.preconditions || '',
steps: data.steps || [],
priority: data.priority !== undefined ? data.priority : 2,
caseType: data.caseType || data.case_type || 1,
tags: data.tags || [],
status: data.status || 1,
isAuto: data.isAuto !== undefined ? data.isAuto : (data.is_auto !== undefined ? data.is_auto : 0)
})
this.stepsText = JSON.stringify(this.form.steps || [], null, 2)
this.tagsText = Array.isArray(this.form.tags) ? this.form.tags.join(',') : (this.form.tags || '')
if (this.form.projectId) {
this.loadModuleOptions()
}
}) })
}, },
submitForm() { submitForm() {
let steps = []
try { try {
this.form.steps = JSON.parse(this.stepsText || '[]') steps = JSON.parse(this.stepsText || '[]')
} catch (e) { } catch (e) {
this.$message({ type: 'error', message: '步骤 JSON 格式错误' }) this.$message({ type: 'error', message: '步骤 JSON 格式错误' })
return return
} }
this.form.tags = this.tagsText ? this.tagsText.split(',').map(item => item.trim()).filter(Boolean) : [] const tags = this.tagsText ? this.tagsText.split(',').map(item => item.trim()).filter(Boolean) : []
const payload = this.cleanParams({
projectId: this.form.projectId,
moduleId: this.form.moduleId,
caseKey: this.form.caseKey,
title: this.form.title,
preconditions: this.form.preconditions,
steps,
priority: this.form.priority,
caseType: this.form.caseType,
tags,
status: this.form.status,
isAuto: this.form.isAuto
})
this.saving = true this.saving = true
const request = this.form.id || this.$route.query.caseId const caseId = this.form.id || this.$route.query.caseId
? updateCase(this.projectId, this.form.id || this.$route.query.caseId, this.form) const request = caseId ? updateCase(this.form.projectId, caseId, payload) : createCase(this.form.projectId, payload)
: createCase(this.projectId, this.form)
request.then(() => { request.then(() => {
this.$message({ type: 'success', message: '保存成功' }) this.$message({ type: 'success', message: '保存成功' })
this.$router.push({ path: '/test-platform/cases', query: { projectId: this.projectId } }) this.backList()
}).finally(() => { }).finally(() => {
this.saving = false this.saving = false
}) })
},
cleanParams(params) {
return Object.keys(params).reduce((result, key) => {
if (params[key] !== '' && params[key] !== undefined && params[key] !== null) {
result[key] = params[key]
}
return result
}, {})
},
backList() {
this.$router.push({ path: '/test-platform/case', query: { productId: this.form.productId || undefined, projectId: this.form.projectId || undefined } })
} }
}, },
created() { created() {
this.loadProductOptions().then(() => {
if (this.form.projectId && !this.form.productId) {
return getProjectDetail(this.form.projectId).then(res => {
const data = res && res.data ? res.data : res || {}
const pid = data.productId || data.product_id || ''
if (pid) {
this.form.productId = pid
}
}).catch(() => {})
}
}).then(() => {
return this.loadProjectOptions()
}).then(() => {
if (this.form.projectId) {
return this.loadModuleOptions()
}
}).finally(() => {
this.fetchDetail() this.fetchDetail()
})
} }
} }
</script> </script>

View File

@@ -26,17 +26,30 @@ service.interceptors.request.use(
// 响应拦截 401 token过期处理 // 响应拦截 401 token过期处理
service.interceptors.response.use( service.interceptors.response.use(
response => { response => {
if(response.data.code===500){ const data = response && response.data ? response.data : {}
Message.error("服务异常") // 兼容后端返回结构:{ success, code, message, data }
}else if(response.data.code===451){ if (data && data.code === 500) {
Message.error('服务异常')
} else if (data && data.code === 451) {
router.push({ name: 'login' }) router.push({ name: 'login' })
} } else if (data && data.success === false) {
else { Message.error(data.message || '请求失败')
} else {
return response.data return response.data
} }
}, },
error => { error => {
// 错误提醒 // 非 2xx 时会进入这里(如 40009/40012后端通常会带 JSON body
const data = error && error.response && error.response.data ? error.response.data : null
if (data && typeof data === 'object') {
if (data.success === false) {
Message.error(data.message || '请求失败')
} else if (data.code && data.code !== 20000 && data.message) {
Message.error(data.message || '请求失败')
}
} else if (error && error.message) {
Message.error(error.message)
}
return Promise.reject(error) return Promise.reject(error)
} }
) )