功能更新:新增Bug管理模块,完善用户角色分配,优化项目设置
This commit is contained in:
@@ -1,26 +1,100 @@
|
||||
<template>
|
||||
<div class="page-wrap">
|
||||
<page-section title="计划构建">
|
||||
<el-form :model="form" label-width="120px" size="small">
|
||||
<el-form-item label="项目ID">
|
||||
<el-input v-model="projectId" style="width: 200px;"></el-input>
|
||||
<page-section :title="isEditMode ? '编辑计划' : '计划构建'">
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="120px" size="small">
|
||||
<el-form-item label="产品名称" prop="productId">
|
||||
<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-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="计划名称">
|
||||
<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-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="计划名称" prop="name">
|
||||
<el-input v-model="form.name"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="版本">
|
||||
<el-form-item label="版本" prop="version">
|
||||
<el-input v-model="form.version"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="负责人ID">
|
||||
<el-input v-model="form.owner_id"></el-input>
|
||||
<el-form-item label="负责人" prop="owner_id">
|
||||
<el-select
|
||||
v-model="form.owner_id"
|
||||
filterable
|
||||
clearable
|
||||
placeholder="请选择负责人"
|
||||
style="width: 360px;"
|
||||
:disabled="!form.projectId">
|
||||
<el-option
|
||||
v-for="item in ownerOptions"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="环境ID">
|
||||
<el-input v-model="form.environment_id"></el-input>
|
||||
<el-form-item label="环境名称" prop="environment_id">
|
||||
<el-select
|
||||
v-model="form.environment_id"
|
||||
filterable
|
||||
clearable
|
||||
placeholder="请选择环境"
|
||||
style="width: 360px;"
|
||||
:disabled="!form.projectId">
|
||||
<el-option
|
||||
v-for="item in environmentOptions"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="开始时间" prop="start_time">
|
||||
<el-date-picker
|
||||
v-model="form.start_time"
|
||||
type="datetime"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
format="yyyy-MM-dd HH:mm:ss"
|
||||
placeholder="请选择开始时间"
|
||||
style="width: 360px;">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item label="结束时间" prop="end_time">
|
||||
<el-date-picker
|
||||
v-model="form.end_time"
|
||||
type="datetime"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
format="yyyy-MM-dd HH:mm:ss"
|
||||
placeholder="请选择结束时间"
|
||||
style="width: 360px;">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item label="描述">
|
||||
<el-input v-model="form.description" type="textarea" :rows="4"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="goBack">返回</el-button>
|
||||
<el-button type="primary" :loading="saving" @click="submitForm">保存计划</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
@@ -30,34 +104,270 @@
|
||||
|
||||
<script>
|
||||
import PageSection from '@/components/TestPlatform/common/PageSection'
|
||||
import { createPlan } from '@/api/planApi'
|
||||
import { createPlan, getPlanDetail, updatePlan } from '@/api/planApi'
|
||||
import { getProductList } from '@/api/productApi'
|
||||
import { getProjectDetail, getProjectEnvironments, getProjectList, getProjectMembers } from '@/api/projectApi'
|
||||
|
||||
export default {
|
||||
name: 'PlanBuilder',
|
||||
components: { PageSection },
|
||||
components: {PageSection},
|
||||
data() {
|
||||
return {
|
||||
saving: false,
|
||||
projectId: this.$route.query.projectId || 1,
|
||||
planId: this.$route.query.planId || '',
|
||||
productOptions: [],
|
||||
projectOptions: [],
|
||||
ownerOptions: [],
|
||||
environmentOptions: [],
|
||||
form: {
|
||||
productId: '',
|
||||
projectId: this.$route.query.projectId ? Number(this.$route.query.projectId) : '',
|
||||
name: '',
|
||||
version: '',
|
||||
owner_id: '',
|
||||
environment_id: '',
|
||||
start_time: '',
|
||||
end_time: '',
|
||||
description: ''
|
||||
},
|
||||
rules: {
|
||||
productId: [{ required: true, message: '请选择产品', trigger: 'change' }],
|
||||
projectId: [{ required: true, message: '请选择项目', trigger: 'change' }],
|
||||
name: [{ required: true, message: '请输入计划名称', trigger: 'blur' }],
|
||||
version: [{ required: true, message: '请输入版本', trigger: 'blur' }],
|
||||
owner_id: [{ required: true, message: '请选择负责人', trigger: 'change' }],
|
||||
environment_id: [{ required: true, message: '请选择环境', trigger: 'change' }],
|
||||
start_time: [{ required: true, message: '请选择开始时间', trigger: 'change' }],
|
||||
end_time: [{ required: true, message: '请选择结束时间', trigger: 'change' }]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
submitForm() {
|
||||
this.saving = true
|
||||
createPlan(this.projectId, this.form).then(() => {
|
||||
this.$message({ type: 'success', message: '计划创建成功' })
|
||||
this.$router.push({ path: '/test-platform/plans', query: { projectId: this.projectId } })
|
||||
}).finally(() => {
|
||||
this.saving = false
|
||||
})
|
||||
computed: {
|
||||
isEditMode() {
|
||||
return !!this.planId
|
||||
}
|
||||
},
|
||||
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 = []
|
||||
})
|
||||
},
|
||||
loadProjectOptionsByProduct(productId) {
|
||||
if (!productId) {
|
||||
this.projectOptions = []
|
||||
return Promise.resolve()
|
||||
}
|
||||
return getProjectList({ pageNo: 1, pageSize: 1000, status: 1, productId }).then(res => {
|
||||
const data = res && res.data ? res.data : res || {}
|
||||
this.projectOptions = data.items || data.list || data.data || []
|
||||
}).catch(() => {
|
||||
this.projectOptions = []
|
||||
})
|
||||
},
|
||||
loadProjectMeta(projectId) {
|
||||
if (!projectId) {
|
||||
this.ownerOptions = []
|
||||
this.environmentOptions = []
|
||||
return Promise.resolve()
|
||||
}
|
||||
const memberReq = getProjectMembers(projectId, { pageNo: 1, pageSize: 1000 }).then(res => {
|
||||
const data = (res && res.data) || res || {}
|
||||
const list = data.items || data.list || data.data || data || []
|
||||
this.ownerOptions = (Array.isArray(list) ? list : []).map(item => ({
|
||||
id: item.user_id || item.userId || item.id,
|
||||
name:
|
||||
item.real_name ||
|
||||
item.realName ||
|
||||
item.username ||
|
||||
item.name ||
|
||||
item.user_name ||
|
||||
`用户${item.user_id || item.id}`
|
||||
})).filter(item => item.id !== undefined && item.id !== null && item.id !== '')
|
||||
}).catch(() => {
|
||||
this.ownerOptions = []
|
||||
})
|
||||
const envReq = getProjectEnvironments(projectId, { pageNo: 1, pageSize: 1000 }).then(res => {
|
||||
const data = (res && res.data) || res || {}
|
||||
const list = data.items || data.list || data.data || data || []
|
||||
this.environmentOptions = (Array.isArray(list) ? list : []).map(item => ({
|
||||
id: item.id,
|
||||
name: item.name
|
||||
}))
|
||||
}).catch(() => {
|
||||
this.environmentOptions = []
|
||||
})
|
||||
return Promise.all([memberReq, envReq])
|
||||
},
|
||||
handleProductChange() {
|
||||
this.form.projectId = ''
|
||||
this.form.owner_id = ''
|
||||
this.form.environment_id = ''
|
||||
this.projectOptions = []
|
||||
this.ownerOptions = []
|
||||
this.environmentOptions = []
|
||||
this.loadProjectOptionsByProduct(this.form.productId)
|
||||
},
|
||||
handleProjectChange() {
|
||||
this.form.owner_id = ''
|
||||
this.form.environment_id = ''
|
||||
this.ownerOptions = []
|
||||
this.environmentOptions = []
|
||||
this.loadProjectMeta(this.form.projectId)
|
||||
},
|
||||
goBack() {
|
||||
this.$router.push({
|
||||
path: '/test-platform/plan',
|
||||
query: {
|
||||
productId: this.form.productId || undefined,
|
||||
projectId: this.form.projectId || undefined
|
||||
}
|
||||
})
|
||||
},
|
||||
submitForm() {
|
||||
this.$refs.formRef.validate(valid => {
|
||||
if (!valid) {
|
||||
return
|
||||
}
|
||||
const projectId = this.form.projectId
|
||||
const payload = {
|
||||
name: this.form.name,
|
||||
version: this.form.version,
|
||||
owner_id: this.form.owner_id,
|
||||
environment_id: this.form.environment_id,
|
||||
start_time: this.form.start_time,
|
||||
end_time: this.form.end_time,
|
||||
description: this.form.description
|
||||
}
|
||||
this.saving = true
|
||||
const request = this.isEditMode
|
||||
? updatePlan(projectId, this.planId, payload)
|
||||
: createPlan(projectId, payload)
|
||||
request.then(() => {
|
||||
this.$message({ type: 'success', message: this.isEditMode ? '计划更新成功' : '计划创建成功' })
|
||||
this.$router.push({
|
||||
path: '/test-platform/plan',
|
||||
query: {
|
||||
productId: this.form.productId || undefined,
|
||||
projectId: projectId || undefined
|
||||
}
|
||||
})
|
||||
}).finally(() => {
|
||||
this.saving = false
|
||||
})
|
||||
})
|
||||
},
|
||||
/** 与 PlanList 列表行一致,兼容详情接口多种时间字段名 */
|
||||
pickPlanDetailStartRaw(plan) {
|
||||
if (!plan) return ''
|
||||
return (
|
||||
plan.start_date ||
|
||||
plan.startDate ||
|
||||
plan.start_time ||
|
||||
plan.startTime ||
|
||||
plan.begin_time ||
|
||||
plan.beginTime ||
|
||||
plan.planned_start_time ||
|
||||
plan.plannedStartTime ||
|
||||
''
|
||||
)
|
||||
},
|
||||
pickPlanDetailEndRaw(plan) {
|
||||
if (!plan) return ''
|
||||
return (
|
||||
plan.end_date ||
|
||||
plan.endDate ||
|
||||
plan.end_time ||
|
||||
plan.endTime ||
|
||||
plan.finish_time ||
|
||||
plan.finishTime ||
|
||||
plan.planned_end_time ||
|
||||
plan.plannedEndTime ||
|
||||
''
|
||||
)
|
||||
},
|
||||
/** 将接口返回的时间戳 / ISO 串等转为 date-picker 的 value-format 字符串 */
|
||||
toDatePickerValue(value) {
|
||||
if (value === undefined || value === null || value === '') return ''
|
||||
if (typeof value === 'number' || (typeof value === 'string' && /^\d+$/.test(value.trim()))) {
|
||||
const raw = Number(value)
|
||||
if (Number.isNaN(raw) || raw <= 0) return ''
|
||||
const ms = raw < 1000000000000 ? raw * 1000 : raw
|
||||
const d = new Date(ms)
|
||||
if (Number.isNaN(d.getTime())) return ''
|
||||
const pad = n => String(n).padStart(2, '0')
|
||||
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`
|
||||
}
|
||||
const s = String(value).trim()
|
||||
if (!s) return ''
|
||||
let normalized = s
|
||||
if (s.includes('T')) {
|
||||
normalized = s.replace('T', ' ').replace(/\.\d+/, '').replace(/Z$/i, '').trim()
|
||||
}
|
||||
if (normalized.length >= 19) {
|
||||
return normalized.slice(0, 19)
|
||||
}
|
||||
if (/^\d{4}-\d{2}-\d{2}$/.test(normalized)) {
|
||||
return `${normalized} 00:00:00`
|
||||
}
|
||||
const parsed = new Date(s)
|
||||
if (!Number.isNaN(parsed.getTime())) {
|
||||
const pad = n => String(n).padStart(2, '0')
|
||||
return `${parsed.getFullYear()}-${pad(parsed.getMonth() + 1)}-${pad(parsed.getDate())} ${pad(parsed.getHours())}:${pad(parsed.getMinutes())}:${pad(parsed.getSeconds())}`
|
||||
}
|
||||
return normalized
|
||||
},
|
||||
unwrapPlanDetailPayload(res) {
|
||||
const raw = (res && res.data) || res || {}
|
||||
const inner = raw.plan || raw.detail
|
||||
if (inner && typeof inner === 'object') {
|
||||
return Object.assign({}, raw, inner)
|
||||
}
|
||||
return raw
|
||||
},
|
||||
loadPlanDetail() {
|
||||
if (!this.isEditMode || !this.form.projectId) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
return getPlanDetail(this.form.projectId, this.planId).then(res => {
|
||||
const data = this.unwrapPlanDetailPayload(res)
|
||||
this.form.name = data.name || ''
|
||||
this.form.version = data.version || ''
|
||||
this.form.owner_id = data.owner_id || data.ownerId || ''
|
||||
this.form.environment_id = data.environment_id || data.environmentId || ''
|
||||
this.form.start_time = this.toDatePickerValue(this.pickPlanDetailStartRaw(data))
|
||||
this.form.end_time = this.toDatePickerValue(this.pickPlanDetailEndRaw(data))
|
||||
this.form.description = data.description || ''
|
||||
}).catch(() => {})
|
||||
},
|
||||
initByRouteProject() {
|
||||
if (!this.form.projectId) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
return getProjectDetail(this.form.projectId).then(res => {
|
||||
const data = res && res.data ? res.data : res || {}
|
||||
const productId = data.productId || data.product_id || ''
|
||||
if (productId) {
|
||||
this.form.productId = productId
|
||||
return this.loadProjectOptionsByProduct(productId)
|
||||
}
|
||||
}).catch(() => {})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadProductOptions().then(() => this.initByRouteProject()).finally(() => {
|
||||
if (this.form.projectId) {
|
||||
this.loadProjectMeta(this.form.projectId).then(() => {
|
||||
this.loadPlanDetail()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user