功能更新:新增Bug管理模块,完善用户角色分配,优化项目设置
This commit is contained in:
721
src/components/Bug/BugList.vue
Normal file
721
src/components/Bug/BugList.vue
Normal file
@@ -0,0 +1,721 @@
|
||||
<template>
|
||||
<div class="page-wrap">
|
||||
<page-section title="Bug 列表">
|
||||
<template slot="extra">
|
||||
<el-button type="primary" size="small" @click="goCreate">新建 Bug</el-button>
|
||||
</template>
|
||||
<el-form :inline="true" size="small" class="filter-form" @submit.native.prevent>
|
||||
<el-form-item label="产品">
|
||||
<el-select
|
||||
v-model="queryForm.productId"
|
||||
filterable
|
||||
clearable
|
||||
placeholder="产品"
|
||||
style="width: 200px;"
|
||||
@change="onProductChange"
|
||||
@focus="loadProductOptions">
|
||||
<el-option v-for="p in productOptions" :key="p.id" :label="p.name" :value="p.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="项目">
|
||||
<el-select
|
||||
v-model="queryForm.projectId"
|
||||
filterable
|
||||
clearable
|
||||
placeholder="项目"
|
||||
style="width: 200px;"
|
||||
:disabled="!queryForm.productId"
|
||||
@change="onProjectChange">
|
||||
<el-option v-for="p in projectOptions" :key="p.id" :label="p.name" :value="p.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="模块">
|
||||
<el-select v-model="queryForm.moduleId" filterable clearable placeholder="模块" style="width: 180px;" :disabled="!queryForm.projectId">
|
||||
<el-option v-for="m in flatModules" :key="m.id" :label="m.name" :value="m.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="当前指派">
|
||||
<el-select v-model="queryForm.assigneeId" filterable clearable placeholder="当前指派" style="width: 140px;" :disabled="!queryForm.projectId">
|
||||
<el-option v-for="u in memberOptions" :key="'a-' + u.id" :label="u.name" :value="u.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="queryForm.status" clearable placeholder="全部" style="width: 120px;">
|
||||
<el-option v-for="(label, key) in statusOptions" :key="key" :label="label" :value="Number(key)" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建人">
|
||||
<el-select v-model="queryForm.reporterId" filterable clearable placeholder="创建人" style="width: 140px;" :disabled="!queryForm.projectId">
|
||||
<el-option v-for="u in memberOptions" :key="'r-' + u.id" :label="u.name" :value="u.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item class="more-filter-item">
|
||||
<el-popover v-model="moreFilterVisible" placement="bottom-start" width="560" trigger="click">
|
||||
<div class="more-filter-wrap">
|
||||
<el-form :inline="true" :model="queryForm" size="small" @submit.native.prevent>
|
||||
<el-form-item label="类型">
|
||||
<el-select v-model="queryForm.bugType" clearable placeholder="全部" style="width: 180px;">
|
||||
<el-option v-for="(label, key) in bugTypeOptions" :key="key" :label="label" :value="Number(key)" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="严重程度">
|
||||
<el-select v-model="queryForm.severity" clearable placeholder="全部" style="width: 180px;">
|
||||
<el-option v-for="(label, key) in severityOptions" :key="key" :label="label" :value="Number(key)" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="优先级">
|
||||
<el-select v-model="queryForm.priority" clearable placeholder="全部" style="width: 180px;">
|
||||
<el-option v-for="(label, key) in priorityOptions" :key="key" :label="label" :value="Number(key)" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="解决人">
|
||||
<el-select
|
||||
v-model="queryForm.resolvedBy"
|
||||
filterable
|
||||
clearable
|
||||
placeholder="全部"
|
||||
style="width: 180px;"
|
||||
:disabled="!queryForm.projectId">
|
||||
<el-option v-for="u in memberOptions" :key="'rb-' + u.id" :label="u.name" :value="u.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="复现率">
|
||||
<el-select v-model="queryForm.reproduceRate" clearable placeholder="全部" style="width: 180px;">
|
||||
<el-option v-for="(label, key) in reproduceRateOptions" :key="'rr-' + key" :label="label" :value="Number(key)" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="关键词">
|
||||
<el-input v-model.trim="queryForm.keyword" clearable placeholder="标题/描述" style="width: 180px;" @keyup.enter.native="applyMoreFilters" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div class="more-filter-footer">
|
||||
<el-button size="small" @click="moreFilterVisible = false">取消</el-button>
|
||||
<el-button type="primary" size="small" @click="applyMoreFilters">搜索</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<el-button slot="reference" size="small">更多筛选</el-button>
|
||||
</el-popover>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSearch">查询</el-button>
|
||||
<el-button @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item class="bug-column-setting-item">
|
||||
<div class="bug-table-toolbar-actions">
|
||||
<el-button size="small" @click="fillCreatedByMe">由我创建的</el-button>
|
||||
<el-button size="small" @click="fillAssignedToMe">指派给我的</el-button>
|
||||
<el-popover v-model="columnSettingVisible" placement="bottom-end" width="300" trigger="click">
|
||||
<div class="column-setting-wrap">
|
||||
<div class="column-setting-title">自定义列表展示字段</div>
|
||||
<el-checkbox-group v-model="selectedBugColumnKeys" @change="handleBugColumnSelectionChange">
|
||||
<el-checkbox v-for="item in allBugColumns" :key="item.key" :label="item.key">{{ item.label }}</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
<el-button slot="reference" size="small">自定义列表展示字段</el-button>
|
||||
</el-popover>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table v-loading="loading" :data="tableData" border class="bug-table">
|
||||
<el-table-column
|
||||
v-for="column in visibleBugColumns"
|
||||
:key="column.key"
|
||||
:label="column.label"
|
||||
:min-width="column.minWidth"
|
||||
:width="column.width"
|
||||
:show-overflow-tooltip="column.key === 'title' || column.key === 'solution' || column.key === 'bugKey' || column.key === 'creator' || column.key === 'assignee' || column.key === 'resolvedBy' || column.key === 'reproduceRate'">
|
||||
<template slot-scope="scope">
|
||||
<template v-if="column.key === 'bugType'">
|
||||
<el-tag size="mini" :type="bugTypeTagType(scope.row.bug_type || scope.row.bugType)">{{ formatBugType(scope.row.bug_type || scope.row.bugType) }}</el-tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'severity'">
|
||||
<el-tag size="mini" :type="severityTagType(scope.row.severity)">{{ formatSeverity(scope.row.severity) }}</el-tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'priority'">
|
||||
<el-tag size="mini" :type="priorityTagType(scope.row.priority)">{{ formatPriority(scope.row.priority) }}</el-tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'status'">
|
||||
<el-tag size="mini" :type="statusTagType(scope.row.status)">{{ formatStatus(scope.row.status) }}</el-tag>
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ formatBugListCell(column.key, scope.row) }}
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="260" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" @click="goDetail(scope.row)">详情</el-button>
|
||||
<el-button type="text" @click="goEdit(scope.row)">编辑</el-button>
|
||||
<el-button type="text" @click="copyBug(scope.row)">复制</el-button>
|
||||
<el-button type="text" style="color: #F56C6C;" @click="handleDelete(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="pager">
|
||||
<el-pagination
|
||||
:current-page="pageNo"
|
||||
:page-size="pageSize"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
:total="total"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange" />
|
||||
</div>
|
||||
</page-section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PageSection from '@/components/TestPlatform/common/PageSection'
|
||||
import { getBugList, deleteBug } from '@/api/bugApi'
|
||||
import { recordBugHistory } from '@/utils/bugHistory'
|
||||
import { getProductList } from '@/api/productApi'
|
||||
import { getProjectList, getProjectMembers } from '@/api/projectApi'
|
||||
import { getModuleTree } from '@/api/caseApi'
|
||||
import {
|
||||
BUG_TYPE_MAP,
|
||||
SEVERITY_MAP,
|
||||
PRIORITY_MAP,
|
||||
STATUS_MAP,
|
||||
REPRODUCE_RATE_MAP,
|
||||
formatBugType,
|
||||
formatSeverity,
|
||||
formatPriority,
|
||||
formatStatus,
|
||||
formatReproduceRate,
|
||||
statusTagType,
|
||||
severityTagType,
|
||||
priorityTagType,
|
||||
bugTypeTagType
|
||||
} from '@/utils/bugMaps'
|
||||
import {
|
||||
readLastProductProjectCache,
|
||||
saveLastProductProjectCache,
|
||||
pickIdFromOptions
|
||||
} from '@/utils/lastProductProjectCache'
|
||||
|
||||
/** 与 BugDetail 解决弹窗 solutionOptions 的 value 一致,用于列表展示 */
|
||||
const BUG_SOLUTION_LABEL_MAP = {
|
||||
by_design: '设计如此',
|
||||
duplicate_bug: '重复Bug',
|
||||
external_reason: '外部原因',
|
||||
solution_resolved: '已解决',
|
||||
cannot_reproduce: '无法重现',
|
||||
deferred: '延期处理',
|
||||
wont_fix: '不予解决'
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'BugList',
|
||||
components: { PageSection },
|
||||
data() {
|
||||
return {
|
||||
columnSettingVisible: false,
|
||||
moreFilterVisible: false,
|
||||
loading: false,
|
||||
productOptions: [],
|
||||
projectOptions: [],
|
||||
moduleTree: [],
|
||||
memberOptions: [],
|
||||
assigneeMap: {},
|
||||
queryForm: {
|
||||
productId: '',
|
||||
projectId: '',
|
||||
moduleId: '',
|
||||
bugType: '',
|
||||
severity: '',
|
||||
priority: '',
|
||||
status: '',
|
||||
assigneeId: '',
|
||||
reporterId: '',
|
||||
resolvedBy: '',
|
||||
reproduceRate: '',
|
||||
keyword: ''
|
||||
},
|
||||
allBugColumns: [
|
||||
{ key: 'bugKey', label: '编号', width: 120 },
|
||||
{ key: 'title', label: '标题', minWidth: 200 },
|
||||
{ key: 'status', label: '状态', width: 100 },
|
||||
{ key: 'assignee', label: '当前指派', width: 110 },
|
||||
{ key: 'creator', label: '创建人', width: 110 },
|
||||
{ key: 'resolvedBy', label: '解决人', width: 110 },
|
||||
{ key: 'reproduceRate', label: '复现率', width: 100 },
|
||||
{ key: 'solution', label: '解决方案', minWidth: 120 },
|
||||
{ key: 'createdTime', label: '创建时间', width: 170 },
|
||||
{ key: 'bugType', label: '类型', width: 100 },
|
||||
{ key: 'severity', label: '严重程度', width: 100 },
|
||||
{ key: 'priority', label: '优先级', width: 80 }
|
||||
],
|
||||
selectedBugColumnKeys: ['bugKey', 'title', 'status', 'assignee', 'creator', 'solution', 'createdTime'],
|
||||
pageNo: 1,
|
||||
pageSize: 20,
|
||||
total: 0,
|
||||
tableData: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
bugTypeOptions() {
|
||||
return BUG_TYPE_MAP
|
||||
},
|
||||
severityOptions() {
|
||||
return SEVERITY_MAP
|
||||
},
|
||||
priorityOptions() {
|
||||
return PRIORITY_MAP
|
||||
},
|
||||
statusOptions() {
|
||||
return STATUS_MAP
|
||||
},
|
||||
flatModules() {
|
||||
const out = []
|
||||
const walk = (nodes, prefix) => {
|
||||
;(nodes || []).forEach(n => {
|
||||
const name = prefix ? `${prefix} / ${n.name}` : n.name
|
||||
out.push({ id: n.id, name })
|
||||
const ch = n.children || n.child_list || n.childList || []
|
||||
if (ch.length) walk(ch, name)
|
||||
})
|
||||
}
|
||||
walk(this.moduleTree, '')
|
||||
return out
|
||||
},
|
||||
currentUser() {
|
||||
return this.$store.state.currentUser
|
||||
},
|
||||
visibleBugColumns() {
|
||||
return this.allBugColumns.filter(item => this.selectedBugColumnKeys.includes(item.key))
|
||||
},
|
||||
reproduceRateOptions() {
|
||||
return REPRODUCE_RATE_MAP
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
formatBugType,
|
||||
formatSeverity,
|
||||
formatPriority,
|
||||
formatStatus,
|
||||
formatReproduceRate,
|
||||
statusTagType,
|
||||
severityTagType,
|
||||
priorityTagType,
|
||||
bugTypeTagType,
|
||||
loadProductOptions() {
|
||||
if (this.productOptions.length) return Promise.resolve()
|
||||
return getProductList({ pageNo: 1, pageSize: 1000, status: 1 }).then(res => {
|
||||
const data = (res && res.data) || res || {}
|
||||
this.productOptions = data.items || data.list || data.data || []
|
||||
}).catch(() => { this.productOptions = [] })
|
||||
},
|
||||
restoreBugListFromCache() {
|
||||
const cached = readLastProductProjectCache()
|
||||
const q = this.$route.query || {}
|
||||
const fromAssignDeepLink = q.assignToMe === '1' || q.assignToMe === 'true'
|
||||
let pid = cached && cached.productId
|
||||
let projId = cached && cached.projectId
|
||||
if (fromAssignDeepLink) {
|
||||
if (q.productId !== undefined && q.productId !== null && String(q.productId).trim() !== '') {
|
||||
pid = q.productId
|
||||
}
|
||||
if (q.projectId !== undefined && q.projectId !== null && String(q.projectId).trim() !== '') {
|
||||
projId = q.projectId
|
||||
}
|
||||
}
|
||||
if (pid === '' || pid === undefined || pid === null || projId === '' || projId === undefined || projId === null) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
const hasProduct = (this.productOptions || []).some(p => String(p.id) === String(pid))
|
||||
if (!hasProduct) return Promise.resolve()
|
||||
this.queryForm.productId = pickIdFromOptions(this.productOptions, pid)
|
||||
return this.loadProjects(this.queryForm.productId).then(() => {
|
||||
const hasProject = (this.projectOptions || []).some(p => String(p.id) === String(projId))
|
||||
if (!hasProject) return
|
||||
this.queryForm.projectId = pickIdFromOptions(this.projectOptions, projId)
|
||||
return Promise.all([
|
||||
this.loadModules(this.queryForm.projectId),
|
||||
this.loadMembers(this.queryForm.projectId)
|
||||
])
|
||||
})
|
||||
},
|
||||
loadProjects(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 || {}
|
||||
this.projectOptions = data.items || data.list || data.data || []
|
||||
}).catch(() => { this.projectOptions = [] })
|
||||
},
|
||||
loadModules(projectId) {
|
||||
if (!projectId) {
|
||||
this.moduleTree = []
|
||||
return Promise.resolve()
|
||||
}
|
||||
return getModuleTree({ projectId, pageNo: 1, pageSize: 1000 }).then(res => {
|
||||
const data = (res && res.data) || res || {}
|
||||
this.moduleTree = data.list || data.items || []
|
||||
}).catch(() => { this.moduleTree = [] })
|
||||
},
|
||||
loadMembers(projectId) {
|
||||
if (!projectId) {
|
||||
this.memberOptions = []
|
||||
this.assigneeMap = {}
|
||||
return Promise.resolve()
|
||||
}
|
||||
return getProjectMembers(projectId, { pageNo: 1, pageSize: 1000 }).then(res => {
|
||||
const data = (res && res.data) || res || {}
|
||||
const list = data.items || data.list || data.data || data || []
|
||||
const arr = Array.isArray(list) ? list : []
|
||||
this.memberOptions = arr.map(item => ({
|
||||
id: item.user_id || item.userId || item.id,
|
||||
name:
|
||||
item.real_name ||
|
||||
item.realName ||
|
||||
item.username ||
|
||||
item.name ||
|
||||
item.user_name ||
|
||||
String(item.user_id || item.id)
|
||||
})).filter(u => u.id !== undefined && u.id !== null)
|
||||
this.assigneeMap = this.memberOptions.reduce((m, u) => { m[u.id] = u.name; return m }, {})
|
||||
}).catch(() => {
|
||||
this.memberOptions = []
|
||||
this.assigneeMap = {}
|
||||
})
|
||||
},
|
||||
rebuildAssigneeMap() {
|
||||
this.assigneeMap = (this.memberOptions || []).reduce((m, u) => {
|
||||
m[u.id] = u.name
|
||||
return m
|
||||
}, {})
|
||||
},
|
||||
mergeCurrentUserIntoMemberOptionsIfNeeded() {
|
||||
const u = this.currentUser
|
||||
if (!u || u.id == null || u.id === '') return
|
||||
const id = u.id
|
||||
if ((this.memberOptions || []).some(m => String(m.id) === String(id))) return
|
||||
const name = u.realName || u.username || '当前用户'
|
||||
this.memberOptions = [{ id, name }, ...(this.memberOptions || [])]
|
||||
this.rebuildAssigneeMap()
|
||||
},
|
||||
fillCreatedByMe() {
|
||||
const u = this.currentUser
|
||||
const uid = u && u.id != null && u.id !== '' ? u.id : null
|
||||
if (uid == null) {
|
||||
this.$message.warning('请先登录')
|
||||
return
|
||||
}
|
||||
if (!this.queryForm.projectId) {
|
||||
this.$message.warning('请先选择项目')
|
||||
return
|
||||
}
|
||||
this.mergeCurrentUserIntoMemberOptionsIfNeeded()
|
||||
this.queryForm.reporterId = uid
|
||||
this.pageNo = 1
|
||||
this.fetchList()
|
||||
},
|
||||
fillAssignedToMe() {
|
||||
const u = this.currentUser
|
||||
const uid = u && u.id != null && u.id !== '' ? u.id : null
|
||||
if (uid == null) {
|
||||
this.$message.warning('请先登录')
|
||||
return
|
||||
}
|
||||
if (!this.queryForm.projectId) {
|
||||
this.$message.warning('请先选择项目')
|
||||
return
|
||||
}
|
||||
this.mergeCurrentUserIntoMemberOptionsIfNeeded()
|
||||
this.queryForm.assigneeId = uid
|
||||
this.pageNo = 1
|
||||
this.fetchList()
|
||||
},
|
||||
onProductChange(val) {
|
||||
this.queryForm.projectId = ''
|
||||
this.queryForm.moduleId = ''
|
||||
this.queryForm.assigneeId = ''
|
||||
this.queryForm.reporterId = ''
|
||||
this.moduleTree = []
|
||||
this.memberOptions = []
|
||||
this.loadProjects(val)
|
||||
},
|
||||
onProjectChange(val) {
|
||||
this.queryForm.moduleId = ''
|
||||
this.queryForm.assigneeId = ''
|
||||
this.queryForm.reporterId = ''
|
||||
this.queryForm.resolvedBy = ''
|
||||
this.loadModules(val)
|
||||
this.loadMembers(val)
|
||||
if (val) {
|
||||
saveLastProductProjectCache(this.queryForm.productId, val)
|
||||
}
|
||||
},
|
||||
cleanParams(obj) {
|
||||
return Object.keys(obj).reduce((acc, k) => {
|
||||
const v = obj[k]
|
||||
if (v !== '' && v !== undefined && v !== null) acc[k] = v
|
||||
return acc
|
||||
}, {})
|
||||
},
|
||||
fetchList() {
|
||||
this.loading = true
|
||||
const params = this.cleanParams({
|
||||
productId: this.queryForm.productId,
|
||||
projectId: this.queryForm.projectId,
|
||||
moduleId: this.queryForm.moduleId,
|
||||
bugType: this.queryForm.bugType,
|
||||
severity: this.queryForm.severity,
|
||||
priority: this.queryForm.priority,
|
||||
status: this.queryForm.status,
|
||||
assigneeId: this.queryForm.assigneeId,
|
||||
reporterId: this.queryForm.reporterId,
|
||||
resolvedBy: this.queryForm.resolvedBy,
|
||||
reproduceRate: this.queryForm.reproduceRate,
|
||||
keyword: this.queryForm.keyword,
|
||||
pageNo: this.pageNo,
|
||||
pageSize: this.pageSize
|
||||
})
|
||||
getBugList(params).then(res => {
|
||||
const data = (res && res.data) || res || {}
|
||||
this.tableData = data.list || data.items || []
|
||||
this.total = Number(data.total || 0)
|
||||
}).catch(() => {
|
||||
this.tableData = []
|
||||
this.total = 0
|
||||
}).finally(() => { this.loading = false })
|
||||
},
|
||||
handleSearch() {
|
||||
this.pageNo = 1
|
||||
saveLastProductProjectCache(this.queryForm.productId, this.queryForm.projectId)
|
||||
this.fetchList()
|
||||
},
|
||||
applyMoreFilters() {
|
||||
this.moreFilterVisible = false
|
||||
this.pageNo = 1
|
||||
saveLastProductProjectCache(this.queryForm.productId, this.queryForm.projectId)
|
||||
this.fetchList()
|
||||
},
|
||||
resetQuery() {
|
||||
this.moreFilterVisible = false
|
||||
this.queryForm = {
|
||||
productId: '',
|
||||
projectId: '',
|
||||
moduleId: '',
|
||||
bugType: '',
|
||||
severity: '',
|
||||
priority: '',
|
||||
status: '',
|
||||
assigneeId: '',
|
||||
reporterId: '',
|
||||
resolvedBy: '',
|
||||
reproduceRate: '',
|
||||
keyword: ''
|
||||
}
|
||||
this.projectOptions = []
|
||||
this.moduleTree = []
|
||||
this.memberOptions = []
|
||||
this.pageNo = 1
|
||||
this.fetchList()
|
||||
},
|
||||
handleSizeChange(s) {
|
||||
this.pageSize = s
|
||||
this.pageNo = 1
|
||||
this.fetchList()
|
||||
},
|
||||
handleCurrentChange(p) {
|
||||
this.pageNo = p
|
||||
this.fetchList()
|
||||
},
|
||||
assigneeLabel(row) {
|
||||
const name = row.assignee_name || row.assigneeName
|
||||
if (name) return name
|
||||
const id = row.assignee_id || row.assigneeId
|
||||
if (this.assigneeMap[id]) return this.assigneeMap[id]
|
||||
if (id !== undefined && id !== null && id !== '') return String(id)
|
||||
return '-'
|
||||
},
|
||||
solutionLabel(row) {
|
||||
const d = row || {}
|
||||
const name = d.solution_name || d.solutionName || d.solution_label || d.solutionLabel
|
||||
if (name) return name
|
||||
const code = String(d.solution_type || d.solutionType || d.solution_code || d.solutionCode || '').trim()
|
||||
if (code && BUG_SOLUTION_LABEL_MAP[code]) return BUG_SOLUTION_LABEL_MAP[code]
|
||||
const sol = d.solution
|
||||
if (sol != null && sol !== '') {
|
||||
const s = String(sol).trim()
|
||||
if (BUG_SOLUTION_LABEL_MAP[s]) return BUG_SOLUTION_LABEL_MAP[s]
|
||||
return s
|
||||
}
|
||||
if (code) return code
|
||||
return '-'
|
||||
},
|
||||
formatBugListCell(key, row) {
|
||||
if (key === 'bugKey') return row.bug_key || row.bugKey || ''
|
||||
if (key === 'title') return row.title || ''
|
||||
if (key === 'assignee') return this.assigneeLabel(row)
|
||||
if (key === 'creator') return this.creatorLabel(row)
|
||||
if (key === 'solution') return this.solutionLabel(row)
|
||||
if (key === 'createdTime') return this.formatTime(row.created_time || row.createdTime)
|
||||
if (key === 'resolvedBy') return this.resolvedByLabel(row)
|
||||
if (key === 'reproduceRate') {
|
||||
return this.formatReproduceRate(row.reproduce_rate != null ? row.reproduce_rate : row.reproduceRate)
|
||||
}
|
||||
return ''
|
||||
},
|
||||
resolvedByLabel(row) {
|
||||
const d = row || {}
|
||||
const name =
|
||||
d.resolved_by_name ||
|
||||
d.resolvedByName ||
|
||||
d.resolver_name ||
|
||||
d.resolverName ||
|
||||
''
|
||||
const id = d.resolved_by != null && d.resolved_by !== '' ? d.resolved_by : d.resolvedBy
|
||||
if (name) return name
|
||||
if (id !== undefined && id !== null && id !== '') {
|
||||
if (this.assigneeMap[id]) return this.assigneeMap[id]
|
||||
return String(id)
|
||||
}
|
||||
return '-'
|
||||
},
|
||||
handleBugColumnSelectionChange(value) {
|
||||
if (!value || value.length === 0) {
|
||||
this.$message.warning('至少保留一个展示字段')
|
||||
this.selectedBugColumnKeys = ['bugKey', 'title']
|
||||
}
|
||||
},
|
||||
creatorLabel(row) {
|
||||
const d = row || {}
|
||||
const name =
|
||||
d.reporter_real_name ||
|
||||
d.reporterRealName ||
|
||||
d.reporter_name ||
|
||||
d.reporterName ||
|
||||
d.creator_real_name ||
|
||||
d.creatorRealName ||
|
||||
d.creator_name ||
|
||||
d.creatorName ||
|
||||
''
|
||||
const id = d.reporter_id || d.reporterId || d.creator_id || d.creatorId || d.created_by || d.createdBy
|
||||
if (name) return name
|
||||
if (id !== undefined && id !== null && id !== '') return String(id)
|
||||
return '-'
|
||||
},
|
||||
formatTime(v) {
|
||||
if (!v) return '-'
|
||||
return String(v).replace('T', ' ').slice(0, 19)
|
||||
},
|
||||
goCreate() {
|
||||
this.$router.push({ path: '/bug/create' })
|
||||
},
|
||||
goDetail(row) {
|
||||
this.$router.push({ path: '/bug/detail', query: { bugId: row.id } })
|
||||
},
|
||||
goEdit(row) {
|
||||
this.$router.push({ path: '/bug/edit', query: { bugId: row.id } })
|
||||
},
|
||||
copyBug(row) {
|
||||
const id = row && (row.id != null ? row.id : row.bugId)
|
||||
if (id === undefined || id === null || id === '') {
|
||||
this.$message.warning('无法复制:缺少 Bug ID')
|
||||
return
|
||||
}
|
||||
this.$router.push({ path: '/bug/create', query: { copyFrom: String(id) } })
|
||||
},
|
||||
handleDelete(row) {
|
||||
this.$confirm('确认删除该 Bug?', '提示', { type: 'warning' }).then(() => {
|
||||
const bid = row.id
|
||||
recordBugHistory(this.$store, {
|
||||
bugId: bid,
|
||||
fieldName: 'delete',
|
||||
oldValue: '0',
|
||||
newValue: '1'
|
||||
})
|
||||
.then(() => deleteBug({ bugId: bid, id: bid }))
|
||||
.then(() => {
|
||||
this.$message.success('已删除')
|
||||
this.fetchList()
|
||||
})
|
||||
.catch(() => {})
|
||||
}).catch(() => {})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadProductOptions()
|
||||
.then(() => this.restoreBugListFromCache())
|
||||
.then(() => {
|
||||
const q = this.$route.query || {}
|
||||
if (q.assignToMe !== '1' && q.assignToMe !== 'true') return
|
||||
const u = this.currentUser
|
||||
const uid = u && u.id != null && u.id !== '' ? u.id : null
|
||||
if (uid == null) {
|
||||
this.$message.warning('请先登录')
|
||||
return
|
||||
}
|
||||
if (!this.queryForm.projectId) {
|
||||
this.$message.warning('请先在列表中选择产品、项目,或从首页在已选过产品/项目时再次进入')
|
||||
return
|
||||
}
|
||||
this.mergeCurrentUserIntoMemberOptionsIfNeeded()
|
||||
this.queryForm.assigneeId = uid
|
||||
this.pageNo = 1
|
||||
})
|
||||
.finally(() => this.fetchList())
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-wrap {
|
||||
padding: 20px;
|
||||
}
|
||||
.filter-form {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.filter-form::after {
|
||||
content: '';
|
||||
display: table;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.bug-table-toolbar-actions {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.bug-table {
|
||||
margin-top: 8px;
|
||||
}
|
||||
.pager {
|
||||
margin-top: 16px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.more-filter-wrap {
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.more-filter-footer {
|
||||
border-top: 1px solid #ebeef5;
|
||||
text-align: right;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.bug-column-setting-item {
|
||||
float: right;
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
|
||||
.column-setting-wrap {
|
||||
max-height: 320px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.column-setting-title {
|
||||
color: #606266;
|
||||
font-weight: 500;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user