功能更新:新增Bug管理模块,完善用户角色分配,优化项目设置

This commit is contained in:
qiaoxinjiu
2026-05-07 19:43:05 +08:00
parent 916248483c
commit f8211673ee
44 changed files with 10410 additions and 437 deletions

View File

@@ -1,24 +1,61 @@
<template>
<div class="effekt-home">
<el-card shadow="never">
<div class="home-content">
<p class="home-desc">这里汇总各项目常用环境地址和文档链接方便快速进入</p>
<div
v-for="project in projectLinks"
:key="project.name"
class="project-block">
<div class="project-title">{{ project.name }}</div>
<div class="project-links">
<div v-for="item in project.links" :key="item.name" class="link-item">
<span class="link-label">{{ item.name }}</span>
<el-link
:href="item.url"
target="_blank"
type="primary"
class="doc-link">
{{ item.url }}
</el-link>
<el-row :gutter="20" class="top-row">
<el-col :xs="24" :md="10">
<el-card shadow="never" class="greet-card">
<div class="greet-line">{{ greetingPrefix }}{{ greetingTime }}</div>
<div class="greet-date">{{ todayText }}</div>
<div v-if="currentUser" class="greet-progress">
<span class="greet-progress-label">待处理进度</span>
<el-progress :percentage="100" :stroke-width="10" status="success" />
<span class="greet-progress-tip">已完成 100%</span>
</div>
<div v-else class="greet-login-tip">
<el-link type="primary" @click="goLogin">登录后查看个人工作台</el-link>
</div>
</el-card>
</el-col>
<el-col :xs="24" :md="14">
<el-card shadow="never" class="work-card">
<div class="work-card-title">今天剩余工作总计</div>
<div class="work-stats">
<div class="work-stat">
<div class="work-stat-value">{{ formatCount(workCountOpportunity) }}</div>
<div class="work-stat-label">我的机会</div>
</div>
<div class="work-stat work-stat--click" @click="goMyBugs">
<div class="work-stat-value">{{ formatCount(workCountBug) }}</div>
<div class="work-stat-label">我的 BUG</div>
<div class="work-stat-hint">点击查看指派给我</div>
</div>
<div class="work-stat work-stat--click" @click="goMyPlans">
<div class="work-stat-value">{{ formatCount(workCountPlan) }}</div>
<div class="work-stat-label">我的计划</div>
<div class="work-stat-hint">点击查看我负责的</div>
</div>
</div>
</el-card>
</el-col>
</el-row>
<el-card shadow="never" class="links-card">
<div class="links-card-title">环境与文档</div>
<p class="home-desc">这里汇总各项目常用环境地址和文档链接方便快速进入</p>
<div
v-for="project in projectLinks"
:key="project.name"
class="project-block">
<div class="project-title">{{ project.name }}</div>
<div class="project-links">
<div v-for="item in project.links" :key="item.name" class="link-item">
<span class="link-label">{{ item.name }}</span>
<el-link
:href="item.url"
target="_blank"
type="primary"
class="doc-link">
{{ item.url }}
</el-link>
</div>
</div>
</div>
@@ -27,10 +64,17 @@
</template>
<script>
import { getBugList } from '@/api/bugApi'
import { getPlanList } from '@/api/planApi'
import { readLastProductProjectCache } from '@/utils/lastProductProjectCache'
export default {
name: 'EffektHome',
data() {
return {
workCountOpportunity: null,
workCountBug: null,
workCountPlan: null,
projectLinks: [
{
name: '智慧运营',
@@ -40,7 +84,7 @@ export default {
{ name: '需求文档', url: 'https://vcncbabzm4lv.feishu.cn/wiki/UsvzwMzV0i7Lrgk9VIhc2XgJn6e?fromScene=spaceOverview' },
{ name: '资源连接地址(包含数据库连接jenkins配置git仓库日志查询xxjob等)', url: 'https://vcncbabzm4lv.feishu.cn/wiki/ZKmown7QuiXtwTkONhUcagpQnWh' },
{ name: '领星地址', url: 'https://envision.lingxing.com/erp/home' },
{ name: '禅道地址', url: 'http://39.170.26.156:8888/my.html' },
{ name: '禅道地址', url: 'http://39.170.26.156:8888/my.html' }
]
},
{
@@ -50,11 +94,116 @@ export default {
{ name: 'web的C端测试环境地址', url: 'https://joyhub-website-frontend-test.best-envision.com/' },
{ name: '接口开发地址', url: 'https://joyhub-website-manager-api-test.best-envision.com/doc.html#/home' },
{ name: '资源连接地址', url: 'https://vcncbabzm4lv.feishu.cn/wiki/PXTmw6BBviMjNDkKCxCcewD7nMd' },
{ name: '禅道地址', url: 'http://192.168.16.4:8956/index.php?m=my&f=index' },
{ name: '禅道地址', url: 'http://192.168.16.4:8956/index.php?m=my&f=index' }
]
}
]
}
},
computed: {
currentUser() {
return this.$store.state.currentUser
},
greetingPrefix() {
const u = this.currentUser
if (!u) return ''
const name = u.realName || u.username || ''
return name ? `${name}` : ''
},
greetingTime() {
const h = new Date().getHours()
if (h < 12) return '上午好!'
if (h < 18) return '下午好!'
return '晚上好!'
},
todayText() {
const d = new Date()
const y = d.getFullYear()
const m = String(d.getMonth() + 1).padStart(2, '0')
const day = String(d.getDate()).padStart(2, '0')
return `${y}${m}${day}`
}
},
mounted() {
this.refreshWorkCounts()
},
methods: {
formatCount(n) {
if (n === null || n === undefined || Number.isNaN(Number(n))) return '—'
return String(n)
},
goLogin() {
this.$router.push({ name: 'login' })
},
goMyBugs() {
if (!this.currentUser) {
this.$message.warning('请先登录')
this.goLogin()
return
}
const c = readLastProductProjectCache()
const q = { assignToMe: '1' }
if (c && c.productId !== undefined && c.productId !== null && String(c.productId).trim() !== '') {
q.productId = String(c.productId)
}
if (c && c.projectId !== undefined && c.projectId !== null && String(c.projectId).trim() !== '') {
q.projectId = String(c.projectId)
}
this.$router.push({ path: '/bug/list', query: q })
},
goMyPlans() {
if (!this.currentUser) {
this.$message.warning('请先登录')
this.goLogin()
return
}
const c = readLastProductProjectCache()
const q = { planOwnerSelf: '1' }
if (c && c.productId !== undefined && c.productId !== null && String(c.productId).trim() !== '') {
q.productId = String(c.productId)
}
if (c && c.projectId !== undefined && c.projectId !== null && String(c.projectId).trim() !== '') {
q.projectId = String(c.projectId)
}
this.$router.push({ path: '/test-platform/plan', query: q })
},
refreshWorkCounts() {
const u = this.currentUser
const c = readLastProductProjectCache()
this.workCountOpportunity = null
this.workCountBug = null
this.workCountPlan = null
if (!u || u.id == null || u.id === '' || !c || !c.projectId) {
return
}
getBugList({
productId: c.productId,
projectId: c.projectId,
assigneeId: u.id,
pageNo: 1,
pageSize: 1
})
.then(res => {
const data = (res && res.data) || res || {}
this.workCountBug = Number(data.total != null ? data.total : 0)
})
.catch(() => {
this.workCountBug = null
})
getPlanList(c.projectId, {
owner_id: u.id,
owner: u.id,
pageNo: 1,
pageSize: 1
})
.then(res => {
const data = (res && res.data) || res || {}
this.workCountPlan = Number(data.total != null ? data.total : 0)
})
.catch(() => {
this.workCountPlan = null
})
}
}
}
</script>
@@ -62,6 +211,117 @@ export default {
<style scoped>
.effekt-home {
padding: 20px;
max-width: 1200px;
margin: 0 auto;
}
.top-row {
margin-bottom: 20px;
}
.greet-card,
.work-card {
border-radius: 10px;
border: 1px solid #ebeef5;
min-height: 160px;
}
.greet-line {
font-size: 20px;
font-weight: 600;
color: #303133;
margin-bottom: 8px;
}
.greet-date {
color: #909399;
font-size: 13px;
margin-bottom: 16px;
}
.greet-progress-label {
font-size: 12px;
color: #606266;
display: block;
margin-bottom: 6px;
}
.greet-progress-tip {
font-size: 12px;
color: #67c23a;
margin-top: 6px;
display: block;
}
.greet-login-tip {
margin-top: 8px;
}
.work-card-title {
font-size: 15px;
font-weight: 600;
color: #303133;
margin-bottom: 16px;
padding-bottom: 10px;
border-bottom: 1px solid #ebeef5;
}
.work-stats {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
gap: 12px;
}
.work-stat {
flex: 1;
min-width: 120px;
text-align: center;
padding: 12px 8px;
border-radius: 8px;
background: #f5f9ff;
border: 1px solid #e4ecfb;
cursor: default;
transition: box-shadow 0.2s, border-color 0.2s;
}
.work-stat--click {
cursor: pointer;
}
.work-stat--click:hover {
border-color: #409eff;
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.15);
}
.work-stat-value {
font-size: 28px;
font-weight: 600;
color: #409eff;
line-height: 1.2;
}
.work-stat-label {
margin-top: 8px;
font-size: 14px;
color: #606266;
}
.work-stat-hint {
margin-top: 4px;
font-size: 11px;
color: #909399;
}
.links-card {
border-radius: 10px;
}
.links-card-title {
font-size: 15px;
font-weight: 600;
color: #303133;
margin-bottom: 8px;
}
.home-content {
@@ -72,6 +332,7 @@ export default {
.home-desc {
margin: 0 0 16px;
color: #606266;
font-size: 13px;
}
.project-block {